build: package modules cross-platform

This commit is contained in:
reaper
2026-08-26 02:38:24 -05:00
parent 74008ff85d
commit bc92db6252
6 changed files with 774 additions and 47 deletions
+608
View File
@@ -28,6 +28,7 @@
"@testing-library/jest-dom": "^7.0.0", "@testing-library/jest-dom": "^7.0.0",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/archiver": "^8.0.0",
"@types/node": "^22.19.1", "@types/node": "^22.19.1",
"@types/react": "^19.2.7", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
@@ -35,6 +36,7 @@
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.1.10", "@vitest/coverage-v8": "^4.1.10",
"@vitest/ui": "^4.1.10", "@vitest/ui": "^4.1.10",
"archiver": "^8.0.0",
"electron": "^39.2.6", "electron": "^39.2.6",
"electron-builder": "^26.0.12", "electron-builder": "^26.0.12",
"electron-vite": "^5.0.0", "electron-vite": "^5.0.0",
@@ -2986,6 +2988,17 @@
"@testing-library/dom": ">=7.21.4" "@testing-library/dom": ">=7.21.4"
} }
}, },
"node_modules/@types/archiver": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-8.0.0.tgz",
"integrity": "sha512-YpXPbEuv9+eUIPPQWUPahj3cvs9isWRuF+J4z+KbdYVDO3rWorWQFxUVHnwPu2AgKwvgpki5F2VMX0Xx+mX45A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/readdir-glob": "*"
}
},
"node_modules/@types/aria-query": { "node_modules/@types/aria-query": {
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -3154,6 +3167,16 @@
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
}, },
"node_modules/@types/readdir-glob": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz",
"integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/responselike": { "node_modules/@types/responselike": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
@@ -3636,6 +3659,19 @@
"node": "^20.17.0 || >=22.9.0" "node": "^20.17.0 || >=22.9.0"
} }
}, },
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dev": true,
"license": "MIT",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.18.0", "version": "8.18.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
@@ -3883,6 +3919,85 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/archiver": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz",
"integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==",
"dev": true,
"license": "MIT",
"dependencies": {
"async": "^3.2.4",
"buffer-crc32": "^1.0.0",
"is-stream": "^4.0.0",
"lazystream": "^1.0.0",
"normalize-path": "^3.0.0",
"readable-stream": "^4.0.0",
"readdir-glob": "^3.0.0",
"tar-stream": "^3.0.0",
"zip-stream": "^7.0.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/archiver/node_modules/buffer-crc32": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/archiver/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"dev": true,
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/archiver/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/archiver/node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -4148,6 +4263,21 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/b4a": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
"integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
"dev": true,
"license": "Apache-2.0",
"peerDependencies": {
"react-native-b4a": "*"
},
"peerDependenciesMeta": {
"react-native-b4a": {
"optional": true
}
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@@ -4158,6 +4288,91 @@
"node": "18 || 20 || >=22" "node": "18 || 20 || >=22"
} }
}, },
"node_modules/bare-events": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
"integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
"dev": true,
"license": "Apache-2.0",
"peerDependencies": {
"bare-abort-controller": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
}
}
},
"node_modules/bare-fs": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz",
"integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
"bare-stream": "^2.6.4",
"bare-url": "^2.2.2",
"fast-fifo": "^1.3.2"
},
"engines": {
"bare": ">=1.28.0"
},
"peerDependencies": {
"bare-buffer": "*"
},
"peerDependenciesMeta": {
"bare-buffer": {
"optional": true
}
}
},
"node_modules/bare-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
"integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/bare-stream": {
"version": "2.13.3",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
"integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.8.1",
"streamx": "^2.25.0",
"teex": "^1.0.1"
},
"peerDependencies": {
"bare-abort-controller": "*",
"bare-buffer": "*",
"bare-events": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
},
"bare-buffer": {
"optional": true
},
"bare-events": {
"optional": true
}
}
},
"node_modules/bare-url": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.2.tgz",
"integrity": "sha512-L13PCJzKG8RGvx8V1/DdMi12ERhC3tprr7/8a94BxpmnRsFqxh5XZNdhtMxu5HPkRshYOOWRGY8lDP7ZhpG9Cg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-path": "^3.0.0"
}
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -4264,6 +4479,31 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-crc32": { "node_modules/buffer-crc32": {
"version": "0.2.13", "version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -4634,6 +4874,71 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/compress-commons": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz",
"integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"crc-32": "^1.2.0",
"crc32-stream": "^7.0.1",
"is-stream": "^4.0.0",
"normalize-path": "^3.0.0",
"readable-stream": "^4.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/compress-commons/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"dev": true,
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/compress-commons/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/compress-commons/node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -4655,6 +4960,81 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/crc32-stream": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz",
"integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"crc-32": "^1.2.0",
"readable-stream": "^4.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/crc32-stream/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"dev": true,
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/crc32-stream/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/crc32-stream/node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/cross-dirname": { "node_modules/cross-dirname": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz",
@@ -6123,6 +6503,36 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/events-universal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
"integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.7.0"
}
},
"node_modules/expect-type": { "node_modules/expect-type": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
@@ -6174,6 +6584,13 @@
"dev": true, "dev": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-json-stable-stringify": { "node_modules/fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -6927,6 +7344,27 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -7326,6 +7764,19 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-stream": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
"integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-string": { "node_modules/is-string": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
@@ -7711,6 +8162,19 @@
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lazystream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"readable-stream": "^2.0.5"
},
"engines": {
"node": ">= 0.6.3"
}
},
"node_modules/levn": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -8509,6 +8973,16 @@
"node": "^20.17.0 || >=22.9.0" "node": "^20.17.0 || >=22.9.0"
} }
}, },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-url": { "node_modules/normalize-url": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
@@ -9035,6 +9509,16 @@
"node": "^20.17.0 || >=22.9.0" "node": "^20.17.0 || >=22.9.0"
} }
}, },
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": { "node_modules/process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -9224,6 +9708,22 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/readdir-glob": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz",
"integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^10.2.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/yqnn"
}
},
"node_modules/redent": { "node_modules/redent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -9873,6 +10373,18 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/streamx": {
"version": "2.28.1",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz",
"integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==",
"dev": true,
"license": "MIT",
"dependencies": {
"events-universal": "^1.0.0",
"fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0"
}
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -10145,6 +10657,19 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/tar-stream": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz",
"integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"b4a": "^1.6.4",
"bare-fs": "^4.5.5",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
}
},
"node_modules/tar/node_modules/yallist": { "node_modules/tar/node_modules/yallist": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
@@ -10155,6 +10680,16 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/teex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
"integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"streamx": "^2.12.5"
}
},
"node_modules/temp": { "node_modules/temp": {
"version": "0.9.4", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz",
@@ -10219,6 +10754,16 @@
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
}, },
"node_modules/text-decoder": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
"integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.6.4"
}
},
"node_modules/tiny-async-pool": { "node_modules/tiny-async-pool": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz",
@@ -11651,6 +12196,69 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/zip-stream": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz",
"integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==",
"dev": true,
"license": "MIT",
"dependencies": {
"compress-commons": "^7.0.0",
"normalize-path": "^3.0.0",
"readable-stream": "^4.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/zip-stream/node_modules/readable-stream": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"dev": true,
"license": "MIT",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/zip-stream/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/zip-stream/node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/zod": { "node_modules/zod": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+2
View File
@@ -56,6 +56,7 @@
"@testing-library/jest-dom": "^7.0.0", "@testing-library/jest-dom": "^7.0.0",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/archiver": "^8.0.0",
"@types/node": "^22.19.1", "@types/node": "^22.19.1",
"@types/react": "^19.2.7", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
@@ -63,6 +64,7 @@
"@vitejs/plugin-react": "^5.1.1", "@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.1.10", "@vitest/coverage-v8": "^4.1.10",
"@vitest/ui": "^4.1.10", "@vitest/ui": "^4.1.10",
"archiver": "^8.0.0",
"electron": "^39.2.6", "electron": "^39.2.6",
"electron-builder": "^26.0.12", "electron-builder": "^26.0.12",
"electron-vite": "^5.0.0", "electron-vite": "^5.0.0",
+42
View File
@@ -0,0 +1,42 @@
'use strict'
const { ZipArchive } = require('archiver')
const { createWriteStream } = require('fs')
const { lstat, readlink, readdir } = require('fs/promises')
const { join, relative, sep } = require('path')
function archiveName(root, value) {
return relative(root, value).split(sep).join('/')
}
async function appendDirectory(archive, root, directory) {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const absolute = join(directory, entry.name)
const name = archiveName(root, absolute)
const stats = await lstat(absolute)
if (stats.isSymbolicLink()) {
archive.symlink(name, await readlink(absolute), stats.mode)
} else if (stats.isDirectory()) {
archive.append('', { name: `${name}/` })
await appendDirectory(archive, root, absolute)
} else if (stats.isFile()) {
archive.file(absolute, { name, mode: stats.mode })
}
}
}
async function archiveDirectory(source, output) {
const destination = createWriteStream(output)
const archive = new ZipArchive({ zlib: { level: 9 } })
const completed = new Promise((resolve, reject) => {
destination.once('close', resolve)
destination.once('error', reject)
archive.once('error', reject)
})
archive.pipe(destination)
await appendDirectory(archive, source, source)
await archive.finalize()
await completed
}
module.exports = archiveDirectory
+7 -8
View File
@@ -2,7 +2,7 @@
const { mkdirSync, rmSync } = require('fs') const { mkdirSync, rmSync } = require('fs')
const { join, resolve } = require('path') const { join, resolve } = require('path')
const { spawnSync } = require('child_process') const archiveDirectory = require('./archive-directory.cjs')
const root = resolve(__dirname, '..') const root = resolve(__dirname, '..')
const source = join(root, 'packages', 'aurora-dockside-connector') const source = join(root, 'packages', 'aurora-dockside-connector')
@@ -10,10 +10,9 @@ const outputDir = join(root, 'dist', 'connectors')
const output = join(outputDir, 'aurora-dockside-connector-0.1.0.zip') const output = join(outputDir, 'aurora-dockside-connector-0.1.0.zip')
mkdirSync(outputDir, { recursive: true }) mkdirSync(outputDir, { recursive: true })
rmSync(output, { force: true }) rmSync(output, { force: true })
const result = spawnSync('zip', ['-qr', output, 'aurora-dockside-connector'], { archiveDirectory(source, output)
cwd: join(root, 'packages'), .then(() => process.stdout.write(`${output}\n`))
stdio: 'inherit' .catch((error) => {
}) console.error(error)
if (result.error) throw result.error process.exitCode = 1
if (result.status !== 0) throw new Error(`zip failed with exit code ${result.status}`) })
process.stdout.write(`${output}\n`)
+8 -6
View File
@@ -2,9 +2,9 @@
const { mkdirSync, readFileSync, readdirSync, rmSync } = require('fs') const { mkdirSync, readFileSync, readdirSync, rmSync } = require('fs')
const { join, resolve } = require('path') const { join, resolve } = require('path')
const { spawnSync } = require('child_process') const archiveDirectory = require('./archive-directory.cjs')
function packageModules() { async function packageModules() {
const projectRoot = resolve(__dirname, '..') const projectRoot = resolve(__dirname, '..')
const packagesRoot = join(projectRoot, 'packages') const packagesRoot = join(projectRoot, 'packages')
const catalogRoot = join(projectRoot, 'dist', 'module-catalog') const catalogRoot = join(projectRoot, 'dist', 'module-catalog')
@@ -22,13 +22,15 @@ function packageModules() {
if (!manifest.id || !manifest.version) throw new Error(`Invalid module manifest in ${source}`) if (!manifest.id || !manifest.version) throw new Error(`Invalid module manifest in ${source}`)
const output = join(catalogRoot, `${manifest.id}-${manifest.version}.pac`) const output = join(catalogRoot, `${manifest.id}-${manifest.version}.pac`)
rmSync(output, { force: true }) rmSync(output, { force: true })
const result = spawnSync('zip', ['-qr', output, '.'], { cwd: source, stdio: 'inherit' }) await archiveDirectory(source, output)
if (result.error) throw result.error
if (result.status !== 0) throw new Error(`zip failed for ${manifest.id} with exit code ${result.status}`)
process.stdout.write(` • packaged module ${manifest.id}@${manifest.version}${output}\n`) process.stdout.write(` • packaged module ${manifest.id}@${manifest.version}${output}\n`)
} }
} }
module.exports = packageModules module.exports = packageModules
if (require.main === module) packageModules() if (require.main === module)
packageModules().catch((error) => {
console.error(error)
process.exitCode = 1
})
+107 -33
View File
@@ -1,33 +1,68 @@
import { execFile } from 'child_process' import { createRequire } from 'module'
import { mkdtemp, mkdir, readFile, symlink, writeFile } from 'fs/promises' import { mkdtemp, mkdir, readFile, symlink, writeFile } from 'fs/promises'
import { promisify } from 'util'
import { join, resolve } from 'path' import { join, resolve } from 'path'
import { tmpdir } from 'os' import { tmpdir } from 'os'
import { afterEach, describe, expect, it, vi } from 'vitest' import { afterEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', () => ({ app: { getPath: () => '/unused', getAppPath: () => '/unused/app' } })) vi.mock('electron', () => ({ app: { getPath: () => '/unused', getAppPath: () => '/unused/app' } }))
import { getAvailableModulePackages, getModuleRegistry, installModulePackage, uninstallModulePackage, validateModuleManifest } from './moduleRegistry' import {
getAvailableModulePackages,
getModuleRegistry,
installModulePackage,
uninstallModulePackage,
validateModuleManifest
} from './moduleRegistry'
const roots: string[] = [] const roots: string[] = []
const execFileAsync = promisify(execFile) const archiveDirectory = createRequire(import.meta.url)('../../scripts/archive-directory.cjs') as (
async function temp(prefix: string): Promise<string> { const root = await mkdtemp(join(tmpdir(), prefix)); roots.push(root); return root } source: string,
const manifest = { id: 'sample-app', name: 'Sample', version: '1.0.0', category: 'application', description: 'test', aurora: { core: '2.0.0-alpha.24', moduleApi: '1.0.0' }, dependencies: [], conflicts: [], settings: [] } output: string
async function packageDir(value = manifest): Promise<string> { const root = await temp('aurora-package-'); await writeFile(join(root, 'manifest.json'), JSON.stringify(value)); return root } ) => Promise<void>
async function temp(prefix: string): Promise<string> {
const root = await mkdtemp(join(tmpdir(), prefix))
roots.push(root)
return root
}
const manifest = {
id: 'sample-app',
name: 'Sample',
version: '1.0.0',
category: 'application',
description: 'test',
aurora: { core: '2.0.0-alpha.24', moduleApi: '1.0.0' },
dependencies: [],
conflicts: [],
settings: []
}
async function packageDir(value = manifest): Promise<string> {
const root = await temp('aurora-package-')
await writeFile(join(root, 'manifest.json'), JSON.stringify(value))
return root
}
async function pacFile(value = manifest): Promise<string> { async function pacFile(value = manifest): Promise<string> {
const source = await packageDir(value) const source = await packageDir(value)
const output = join(await temp('aurora-pac-'), `${value.id}.pac`) const output = join(await temp('aurora-pac-'), `${value.id}.pac`)
await execFileAsync('zip', ['-qr', output, '.'], { cwd: source }) await archiveDirectory(source, output)
return output return output
} }
afterEach(async () => { const { rm } = await import('fs/promises'); await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))) }) afterEach(async () => {
const { rm } = await import('fs/promises')
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })))
})
describe('external module registry', () => { describe('external module registry', () => {
it('starts empty', async () => expect(await getModuleRegistry(await temp('aurora-user-'))).toEqual([])) it('starts empty', async () =>
expect(await getModuleRegistry(await temp('aurora-user-'))).toEqual([]))
it('lists discoverable packages for the module library', async () => { it('lists discoverable packages for the module library', async () => {
const catalog = await temp('aurora-catalog-'); const source = join(catalog, 'sample-app'); await mkdir(source); await writeFile(join(source, 'manifest.json'), JSON.stringify(manifest)) const catalog = await temp('aurora-catalog-')
const source = join(catalog, 'sample-app')
await mkdir(source)
await writeFile(join(source, 'manifest.json'), JSON.stringify(manifest))
process.env.AURORA_MODULE_PATH = catalog process.env.AURORA_MODULE_PATH = catalog
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app') expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain(
'sample-app'
)
delete process.env.AURORA_MODULE_PATH delete process.env.AURORA_MODULE_PATH
}) })
it('discovers and installs a compressed .pac package', async () => { it('discovers and installs a compressed .pac package', async () => {
@@ -36,7 +71,9 @@ describe('external module registry', () => {
const catalogPac = join(catalog, 'sample-app.pac') const catalogPac = join(catalog, 'sample-app.pac')
await import('fs/promises').then(({ copyFile }) => copyFile(pac, catalogPac)) await import('fs/promises').then(({ copyFile }) => copyFile(pac, catalogPac))
process.env.AURORA_MODULE_PATH = catalog process.env.AURORA_MODULE_PATH = catalog
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app') expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain(
'sample-app'
)
delete process.env.AURORA_MODULE_PATH delete process.env.AURORA_MODULE_PATH
const userData = await temp('aurora-user-') const userData = await temp('aurora-user-')
const installed = await installModulePackage(catalogPac, userData) const installed = await installModulePackage(catalogPac, userData)
@@ -46,9 +83,13 @@ describe('external module registry', () => {
it('discovers a .pac distributed beside the AppImage', async () => { it('discovers a .pac distributed beside the AppImage', async () => {
const release = await temp('aurora-release-') const release = await temp('aurora-release-')
const pac = await pacFile() const pac = await pacFile()
await import('fs/promises').then(({ copyFile }) => copyFile(pac, join(release, 'sample-app.pac'))) await import('fs/promises').then(({ copyFile }) =>
copyFile(pac, join(release, 'sample-app.pac'))
)
process.env.APPIMAGE = join(release, 'aurora-dockside.AppImage') process.env.APPIMAGE = join(release, 'aurora-dockside.AppImage')
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app') expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain(
'sample-app'
)
delete process.env.APPIMAGE delete process.env.APPIMAGE
}) })
it('discovers a .pac embedded in the packaged application resources', async () => { it('discovers a .pac embedded in the packaged application resources', async () => {
@@ -56,31 +97,49 @@ describe('external module registry', () => {
const catalog = join(resources, 'module-catalog') const catalog = join(resources, 'module-catalog')
await mkdir(catalog) await mkdir(catalog)
const pac = await pacFile() const pac = await pacFile()
await import('fs/promises').then(({ copyFile }) => copyFile(pac, join(catalog, 'sample-app.pac'))) await import('fs/promises').then(({ copyFile }) =>
copyFile(pac, join(catalog, 'sample-app.pac'))
)
const previous = Object.getOwnPropertyDescriptor(process, 'resourcesPath') const previous = Object.getOwnPropertyDescriptor(process, 'resourcesPath')
Object.defineProperty(process, 'resourcesPath', { value: resources, configurable: true }) Object.defineProperty(process, 'resourcesPath', { value: resources, configurable: true })
try { try {
expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain('sample-app') expect((await getAvailableModulePackages()).map((item) => item.manifest.id)).toContain(
'sample-app'
)
} finally { } finally {
if (previous) Object.defineProperty(process, 'resourcesPath', previous) if (previous) Object.defineProperty(process, 'resourcesPath', previous)
else Reflect.deleteProperty(process, 'resourcesPath') else Reflect.deleteProperty(process, 'resourcesPath')
} }
}) })
it('installs a valid local package and refreshes after install', async () => { it('installs a valid local package and refreshes after install', async () => {
const userData = await temp('aurora-user-'); const source = await packageDir() const userData = await temp('aurora-user-')
const source = await packageDir()
await installModulePackage(source, userData) await installModulePackage(source, userData)
expect((await getModuleRegistry(userData)).map((item) => item.id)).toEqual(['sample-app']) expect((await getModuleRegistry(userData)).map((item) => item.id)).toEqual(['sample-app'])
}) })
it('rejects invalid and incompatible manifests', () => { it('rejects invalid and incompatible manifests', () => {
expect(() => validateModuleManifest({ ...manifest, id: '../escape' })).toThrow(/module id/) expect(() => validateModuleManifest({ ...manifest, id: '../escape' })).toThrow(/module id/)
expect(() => validateModuleManifest({ ...manifest, aurora: { core: '9.0.0', moduleApi: '1.0.0' } })).toThrow(/requires Aurora Core/) expect(() =>
expect(() => validateModuleManifest({ ...manifest, main: '../outside.cjs' })).toThrow(/may not leave/) validateModuleManifest({ ...manifest, aurora: { core: '9.0.0', moduleApi: '1.0.0' } })
expect(() => validateModuleManifest({ ...manifest, dependencies: ['../escape'] })).toThrow(/module id array/) ).toThrow(/requires Aurora Core/)
expect(() => validateModuleManifest({ ...manifest, project: { adminPath: 'admin' } })).toThrow(/must start with/) expect(() => validateModuleManifest({ ...manifest, main: '../outside.cjs' })).toThrow(
expect(() => validateModuleManifest({ ...manifest, creation: { phpVersions: ['latest'] } })).toThrow(/PHP minor versions/) /may not leave/
)
expect(() => validateModuleManifest({ ...manifest, dependencies: ['../escape'] })).toThrow(
/module id array/
)
expect(() => validateModuleManifest({ ...manifest, project: { adminPath: 'admin' } })).toThrow(
/must start with/
)
expect(() =>
validateModuleManifest({ ...manifest, creation: { phpVersions: ['latest'] } })
).toThrow(/PHP minor versions/)
}) })
it('rejects symbolic links in package paths', async () => { it('rejects symbolic links in package paths', async () => {
const userData = await temp('aurora-user-'); const source = await packageDir(); await mkdir(join(source, 'main')); await symlink('/tmp', join(source, 'main', 'escape')) const userData = await temp('aurora-user-')
const source = await packageDir()
await mkdir(join(source, 'main'))
await symlink('/tmp', join(source, 'main', 'escape'))
await expect(installModulePackage(source, userData)).rejects.toThrow(/symbolic links/) await expect(installModulePackage(source, userData)).rejects.toThrow(/symbolic links/)
}) })
it('rejects symbolic links and traversal paths inside .pac archives', async () => { it('rejects symbolic links and traversal paths inside .pac archives', async () => {
@@ -88,25 +147,37 @@ describe('external module registry', () => {
const linkedSource = await packageDir() const linkedSource = await packageDir()
await symlink('/tmp', join(linkedSource, 'escape')) await symlink('/tmp', join(linkedSource, 'escape'))
const linkedPac = join(await temp('aurora-pac-'), 'linked.pac') const linkedPac = join(await temp('aurora-pac-'), 'linked.pac')
await execFileAsync('zip', ['-qry', linkedPac, '.'], { cwd: linkedSource }) await archiveDirectory(linkedSource, linkedPac)
await expect(installModulePackage(linkedPac, userData)).rejects.toThrow(/symbolic links/) await expect(installModulePackage(linkedPac, userData)).rejects.toThrow(/symbolic links/)
const traversalSource = await packageDir() const traversalSource = await packageDir()
await mkdir(join(traversalSource, 'xx')) await mkdir(join(traversalSource, 'xx'))
await writeFile(join(traversalSource, 'xx', 'evil'), 'unsafe') await writeFile(join(traversalSource, 'xx', 'evil'), 'unsafe')
const traversalPac = join(await temp('aurora-pac-'), 'traversal.pac') const traversalPac = join(await temp('aurora-pac-'), 'traversal.pac')
await execFileAsync('zip', ['-qr', traversalPac, '.'], { cwd: traversalSource }) await archiveDirectory(traversalSource, traversalPac)
const archive = await readFile(traversalPac) const archive = await readFile(traversalPac)
const original = Buffer.from('xx/evil') const original = Buffer.from('xx/evil')
const unsafe = Buffer.from('../evil') const unsafe = Buffer.from('../evil')
for (let offset = archive.indexOf(original); offset !== -1; offset = archive.indexOf(original, offset + unsafe.length)) unsafe.copy(archive, offset) for (
let offset = archive.indexOf(original);
offset !== -1;
offset = archive.indexOf(original, offset + unsafe.length)
)
unsafe.copy(archive, offset)
await writeFile(traversalPac, archive) await writeFile(traversalPac, archive)
await expect(installModulePackage(traversalPac, userData)).rejects.toThrow(/(?:Unsafe \.pac entry path|invalid relative path)/) await expect(installModulePackage(traversalPac, userData)).rejects.toThrow(
/(?:Unsafe \.pac entry path|invalid relative path)/
)
}) })
it('refreshes after uninstall without touching source', async () => { it('refreshes after uninstall without touching source', async () => {
const userData = await temp('aurora-user-'); const source = await packageDir(); await installModulePackage(source, userData); await uninstallModulePackage('sample-app', userData) const userData = await temp('aurora-user-')
const source = await packageDir()
await installModulePackage(source, userData)
await uninstallModulePackage('sample-app', userData)
expect(await getModuleRegistry(userData)).toEqual([]) expect(await getModuleRegistry(userData)).toEqual([])
expect(await import('fs/promises').then(({ stat }) => stat(join(source, 'manifest.json')))).toBeTruthy() expect(
await import('fs/promises').then(({ stat }) => stat(join(source, 'manifest.json')))
).toBeTruthy()
}) })
it('updates an installed package atomically at the same registry id', async () => { it('updates an installed package atomically at the same registry id', async () => {
@@ -131,7 +202,7 @@ describe('external module registry', () => {
const actual = JSON.parse(await readFile(join(packageRoot, 'manifest.json'), 'utf8')) const actual = JSON.parse(await readFile(join(packageRoot, 'manifest.json'), 'utf8'))
expect(validateModuleManifest(actual).id).toBe('wordpress') expect(validateModuleManifest(actual).id).toBe('wordpress')
const archive = join(await temp('aurora-pac-'), 'wordpress.pac') const archive = join(await temp('aurora-pac-'), 'wordpress.pac')
await execFileAsync('zip', ['-qr', archive, '.'], { cwd: packageRoot }) await archiveDirectory(packageRoot, archive)
const userData = await temp('aurora-user-') const userData = await temp('aurora-user-')
expect((await installModulePackage(archive, userData)).manifest.id).toBe('wordpress') expect((await installModulePackage(archive, userData)).manifest.id).toBe('wordpress')
expect((await getModuleRegistry(userData)).map((item) => item.id)).toEqual(['wordpress']) expect((await getModuleRegistry(userData)).map((item) => item.id)).toEqual(['wordpress'])
@@ -140,9 +211,12 @@ describe('external module registry', () => {
it('accepts and packages the Drupal application module contract', async () => { it('accepts and packages the Drupal application module contract', async () => {
const packageRoot = resolve(process.cwd(), 'packages/aurora-module-drupal') const packageRoot = resolve(process.cwd(), 'packages/aurora-module-drupal')
const actual = JSON.parse(await readFile(join(packageRoot, 'manifest.json'), 'utf8')) const actual = JSON.parse(await readFile(join(packageRoot, 'manifest.json'), 'utf8'))
expect(validateModuleManifest(actual)).toMatchObject({ id: 'drupal', defaults: { docroot: 'web' } }) expect(validateModuleManifest(actual)).toMatchObject({
id: 'drupal',
defaults: { docroot: 'web' }
})
const archive = join(await temp('aurora-pac-'), 'drupal.pac') const archive = join(await temp('aurora-pac-'), 'drupal.pac')
await execFileAsync('zip', ['-qr', archive, '.'], { cwd: packageRoot }) await archiveDirectory(packageRoot, archive)
const userData = await temp('aurora-user-') const userData = await temp('aurora-user-')
expect((await installModulePackage(archive, userData)).manifest.id).toBe('drupal') expect((await installModulePackage(archive, userData)).manifest.id).toBe('drupal')
}) })