diff --git a/.gitignore b/.gitignore index ab4665e..0fb7c90 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,3 @@ node_modules dist coverage/ .jest/ -dist/src/__mocks__/ diff --git a/IMPACT_REPORT.md b/IMPACT_REPORT.md new file mode 100644 index 0000000..e69de29 diff --git a/jest.config.e2e.ts b/jest.config.e2e.ts deleted file mode 100644 index d1963fa..0000000 --- a/jest.config.e2e.ts +++ /dev/null @@ -1,29 +0,0 @@ -export default { - preset: 'ts-jest/presets/default-esm', - displayName: 'e2e', - testEnvironment: 'node', - testEnvironmentOptions: { - env: { - NODE_ENV: 'test', - }, - }, - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - '#(.*)': '/node_modules/$1', - '^chalk$': '/src/__mocks__/chalk.ts', - '^ora$': '/src/__mocks__/ora.ts', - '^globby$': '/src/__mocks__/globby.ts', - '^isbinaryfile$': '/src/__mocks__/isbinaryfile.ts', - '^commander$': '/node_modules/commander', - }, - setupFilesAfterEnv: ['/src/__tests__/setup.ts'], - transform: { - '^.+\\.tsx?$': ['ts-jest', { useESM: true }], - }, - testMatch: ['**/src/__tests__/e2e.test.ts'], - testPathIgnorePatterns: [ - '/dist/', - '/src/__tests__/setup.ts', - ], -}; diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 4021dbf..0000000 --- a/jest.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -export default { - projects: ['/jest.config.unit.ts', '/jest.config.e2e.ts'], - testPathIgnorePatterns: ['/node_modules/', '/dist/', 'setup.ts$'], - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - preset: 'ts-jest/presets/default-esm', - transformIgnorePatterns: [ - 'node_modules/(?!chalk|find-up|p-locate|locate-path|path-exists|globby|@nodelib|p-limit|p-try|yaml)/', - ], -}; diff --git a/jest.config.unit.ts b/jest.config.unit.ts deleted file mode 100644 index f37575f..0000000 --- a/jest.config.unit.ts +++ /dev/null @@ -1,39 +0,0 @@ -export default { - preset: 'ts-jest/presets/default-esm', - displayName: 'unit', - testEnvironment: 'node', - testMatch: ['**/src/__tests__/unit.test.ts'], - testPathIgnorePatterns: [ - '/dist/', - '/src/__tests__/setup.ts', - ], - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - '^chalk$': '/src/__mocks__/chalk.ts', - '^ora$': '/src/__mocks__/ora.ts', - '^globby$': '/src/__mocks__/globby.ts', - '^isbinaryfile$': '/src/__mocks__/isbinaryfile.ts', - '^find-up$': '/src/__mocks__/find-up.ts', // Ensures find-up uses the mock - '^fs/promises$': '/src/__mocks__/fs/promises.ts', - '^node:fs/promises$': '/src/__mocks__/fs/promises.ts', // Ensures node:fs/promises uses the mock - '#(.*)': '/node_modules/$1', - // Add any additional module mappings here - }, - setupFilesAfterEnv: ['/src/__tests__/setup.ts'], - transform: { - '^.+\\.tsx?$': [ - 'ts-jest', - { - useESM: true, - tsconfig: { - module: 'ESNext', - moduleResolution: 'bundler', - }, - }, - ], - }, - transformIgnorePatterns: [ - 'node_modules/(?!chalk|find-up|p-locate|locate-path|path-exists|globby|@nodelib|p-limit|p-try|yaml)/', - ], -}; diff --git a/package-lock.json b/package-lock.json index 499d9f7..003b6cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,18 +18,28 @@ "win32" ], "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/traverse": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "axios": "^1.7.9", "chalk": "^5.4.1", "cli-progress": "^3.12.0", "cli-table3": "^0.6.5", - "commander": "^13.0.0", + "commander": "^13.1.0", + "express": "^4.21.2", "find-up": "^7.0.0", "globby": "^14.0.2", "isbinaryfile": "^5.0.4", + "koa": "^2.15.3", "micromatch": "^4.0.8", + "mongodb": "^6.12.0", + "mongoose": "^8.9.5", + "node-fetch": "^3.3.2", "ora": "^8.1.1", + "react": "^19.0.0", "shell-escape": "^0.2.0", + "sqlite": "^5.1.1", + "tslib": "^2.8.1", + "vue": "^3.5.13", "yaml": "^2.7.0" }, "bin": { @@ -40,11 +50,12 @@ "@types/cli-progress": "^3.11.6", "@types/jest": "^29.5.14", "@types/micromatch": "^4.0.9", - "@types/node": "^22.10.6", + "@types/node": "^22.10.10", "@types/shell-escape": "^0.2.3", "jest": "^29.7.0", "mikey-pro": "^7.5.3", "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", "typescript": "^5.7.3" }, "engines": { @@ -81,9 +92,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, "license": "MIT", "engines": { @@ -186,13 +197,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -319,9 +330,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -872,13 +883,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1292,13 +1303,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1574,13 +1585,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1657,15 +1668,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1679,7 +1690,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1690,7 +1701,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -1699,12 +1710,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1721,7 +1732,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -1830,6 +1841,30 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", @@ -2852,6 +2887,15 @@ "stylelint-config-standard-scss": "^13.1.0" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -2966,6 +3010,276 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@swc/core": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.11.tgz", + "integrity": "sha512-3zGU5y3S20cAwot9ZcsxVFNsSVaptG+dKdmAxORSE3EX7ixe1Xn5kUwLlgIsM4qrwTUWCJDLNhRS+2HLFivcDg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.17" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.10.11", + "@swc/core-darwin-x64": "1.10.11", + "@swc/core-linux-arm-gnueabihf": "1.10.11", + "@swc/core-linux-arm64-gnu": "1.10.11", + "@swc/core-linux-arm64-musl": "1.10.11", + "@swc/core-linux-x64-gnu": "1.10.11", + "@swc/core-linux-x64-musl": "1.10.11", + "@swc/core-win32-arm64-msvc": "1.10.11", + "@swc/core-win32-ia32-msvc": "1.10.11", + "@swc/core-win32-x64-msvc": "1.10.11" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.11.tgz", + "integrity": "sha512-ZpgEaNcx2e5D+Pd0yZGVbpSrEDOEubn7r2JXoNBf0O85lPjUm3HDzGRfLlV/MwxRPAkwm93eLP4l7gYnc50l3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.11.tgz", + "integrity": "sha512-szObinnq2o7spXMDU5pdunmUeLrfV67Q77rV+DyojAiGJI1RSbEQotLOk+ONOLpoapwGUxOijFG4IuX1xiwQ2g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.11.tgz", + "integrity": "sha512-tVE8aXQwd8JUB9fOGLawFJa76nrpvp3dvErjozMmWSKWqtoeO7HV83aOrVtc8G66cj4Vq7FjTE9pOJeV1FbKRw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.11.tgz", + "integrity": "sha512-geFkENU5GMEKO7FqHOaw9HVlpQEW10nICoM6ubFc0hXBv8dwRXU4vQbh9s/isLSFRftw1m4jEEWixAnXSw8bxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.11.tgz", + "integrity": "sha512-2mMscXe/ivq8c4tO3eQSbQDFBvagMJGlalXCspn0DgDImLYTEnt/8KHMUMGVfh0gMJTZ9q4FlGLo7mlnbx99MQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.11.tgz", + "integrity": "sha512-eu2apgDbC4xwsigpl6LS+iyw6a3mL6kB4I+6PZMbFF2nIb1Dh7RGnu70Ai6mMn1o80fTmRSKsCT3CKMfVdeNFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.11.tgz", + "integrity": "sha512-0n+wPWpDigwqRay4IL2JIvAqSKCXv6nKxPig9M7+epAlEQlqX+8Oq/Ap3yHtuhjNPb7HmnqNJLCXT1Wx+BZo0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.11.tgz", + "integrity": "sha512-7+bMSIoqcbXKosIVd314YjckDRPneA4OpG1cb3/GrkQTEDXmWT3pFBBlJf82hzJfw7b6lfv6rDVEFBX7/PJoLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.11.tgz", + "integrity": "sha512-6hkLl4+3KjP/OFTryWxpW7YFN+w4R689TSPwiII4fFgsFNupyEmLWWakKfkGgV2JVA59L4Oi02elHy/O1sbgtw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.11.tgz", + "integrity": "sha512-kKNE2BGu/La2k2WFHovenqZvGQAHRIU+rd2/6a7D6EiQ6EyimtbhUqjCCZ+N1f5fIAnvM+sMdLiQJq4jdd/oOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/@swc/types": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3142,6 +3456,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3442,58 +3771,184 @@ "dev": true, "license": "ISC" }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -3544,6 +3999,13 @@ "node": ">= 6.0.0" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3571,6 +4033,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/array-includes": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", @@ -3710,6 +4178,12 @@ "dev": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3726,6 +4200,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3934,6 +4419,45 @@ "dev": true, "license": "MIT" }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4013,6 +4537,15 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4033,6 +4566,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "license": "MIT", + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -4056,7 +4611,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4070,7 +4624,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4265,7 +4818,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, "license": "MIT", "engines": { "iojs": ">= 1.0.0", @@ -4306,6 +4858,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -4332,6 +4896,27 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4339,6 +4924,34 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -4455,6 +5068,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4507,6 +5127,21 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -4593,6 +5228,12 @@ } } }, + "node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4646,6 +5287,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -4789,7 +5464,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4800,6 +5474,12 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -4842,6 +5522,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", @@ -4860,7 +5549,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -4959,7 +5647,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4969,7 +5656,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4986,7 +5672,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5049,6 +5734,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -6319,6 +7010,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -6329,6 +7026,15 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6379,6 +7085,67 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6469,6 +7236,29 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -6527,6 +7317,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", @@ -6566,6 +7389,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -6576,6 +7419,50 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6602,7 +7489,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6675,7 +7561,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -6710,7 +7595,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -6922,7 +7806,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7001,7 +7884,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7014,7 +7896,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -7030,7 +7911,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7087,6 +7967,69 @@ "entities": "^4.4.0" } }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "license": "MIT", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7097,6 +8040,18 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -7189,7 +8144,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -7214,6 +8168,15 @@ "node": ">= 0.4" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -7442,7 +8405,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -7554,7 +8516,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -9121,6 +10082,27 @@ "node": ">=10" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -9158,6 +10140,102 @@ "dev": true, "license": "MIT" }, + "node_modules/koa": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", + "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", + "license": "MIT", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "license": "MIT" + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "license": "MIT", + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9298,6 +10376,15 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9403,7 +10490,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9434,6 +10520,21 @@ "dev": true, "license": "MIT" }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -9447,6 +10548,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9463,6 +10573,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -9491,6 +10610,39 @@ "stylelint": "^16.6.1" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -9546,6 +10698,105 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mongodb": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.5.tgz", + "integrity": "sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.1", + "kareem": "2.6.3", + "mongodb": "~6.12.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9556,7 +10807,6 @@ "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, "funding": [ { "type": "github", @@ -9578,6 +10828,52 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9642,7 +10938,6 @@ "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9735,6 +11030,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9761,6 +11068,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -9970,6 +11282,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", @@ -10006,6 +11327,12 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, "node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -10149,7 +11476,6 @@ "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10347,11 +11673,29 @@ "node": ">= 6" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -10384,6 +11728,21 @@ ], "license": "MIT" }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -10404,6 +11763,39 @@ ], "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -10899,6 +12291,26 @@ "dev": true, "license": "MIT" }, + "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==", + "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/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -10927,7 +12339,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10941,6 +12352,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -10951,6 +12368,69 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -11000,6 +12480,12 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11033,7 +12519,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -11053,7 +12538,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -11070,7 +12554,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -11089,7 +12572,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -11105,6 +12587,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -11234,7 +12722,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -11251,6 +12738,15 @@ "source-map": "^0.6.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -11294,6 +12790,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/sqlite": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", + "integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==", + "license": "MIT" + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -11324,6 +12826,15 @@ "node": ">=8" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -12008,6 +13519,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/toml-eslint-parser": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.10.0.tgz", @@ -12037,6 +13557,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -12112,6 +13644,50 @@ "node": ">=10" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -12152,9 +13728,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -12191,6 +13775,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -12273,7 +13870,7 @@ "version": "5.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12371,6 +13968,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -12419,6 +14025,22 @@ "dev": true, "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -12445,6 +14067,36 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -12455,6 +14107,37 @@ "makeerror": "1.0.12" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -12720,6 +14403,25 @@ "node": ">=12" } }, + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 9535dd9..2b75050 100644 --- a/package.json +++ b/package.json @@ -13,27 +13,37 @@ "major": "npm version major; cd dist && npm version major", "minor": "npm version minor; cd dist && npm version minor", "ncu": "ncu -u; cd dist && ncu -u;", - "start": "node dist/index.js", + "start": "npm run build && node dist/src/index.js", "pretest": "npm run build", - "test": "jest", + "test": "jest --no-cache", "test:coverage": "jest --coverage", "test:e2e": "jest --config jest.config.e2e.ts", "test:unit": "jest --config jest.config.unit.ts", "test:watch": "jest --watch" }, "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/traverse": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "axios": "^1.7.9", "chalk": "^5.4.1", "cli-progress": "^3.12.0", "cli-table3": "^0.6.5", - "commander": "^13.0.0", + "commander": "^13.1.0", + "express": "^4.21.2", "find-up": "^7.0.0", "globby": "^14.0.2", "isbinaryfile": "^5.0.4", + "koa": "^2.15.3", "micromatch": "^4.0.8", + "mongodb": "^6.12.0", + "mongoose": "^8.9.5", + "node-fetch": "^3.3.2", "ora": "^8.1.1", + "react": "^19.0.0", "shell-escape": "^0.2.0", + "sqlite": "^5.1.1", + "tslib": "^2.8.1", + "vue": "^3.5.13", "yaml": "^2.7.0" }, "devDependencies": { @@ -41,11 +51,12 @@ "@types/cli-progress": "^3.11.6", "@types/jest": "^29.5.14", "@types/micromatch": "^4.0.9", - "@types/node": "^22.10.6", + "@types/node": "^22.10.10", "@types/shell-escape": "^0.2.3", "jest": "^29.7.0", "mikey-pro": "^7.5.3", "ts-jest": "^29.2.5", + "ts-node": "^10.9.2", "typescript": "^5.7.3" }, "files": [ @@ -75,10 +86,20 @@ "node" ], "author": "Mikl Wolfe (https://github.com/chiefmikey)", + "imports": { + "#/*": { + "default": "./dist/*.js", + "types": "./src/*.ts" + } + }, "exports": { ".": { "import": "./dist/index.js", - "require": "./dist/index.cjs" + "types": "./dist/index.d.ts" + }, + "#/*": { + "import": "./dist/*.js", + "types": "./src/*.ts" } }, "prettier": "@mikey-pro/prettier-config", diff --git a/src/__tests__/e2e.test.ts b/src/__tests__/e2e.test.ts deleted file mode 100644 index 0f3defa..0000000 --- a/src/__tests__/e2e.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { execSync } from 'node:child_process'; -import path from 'node:path'; -import { beforeAll, describe, expect, it } from '@jest/globals'; - -describe('e2E Test', () => { - const projectRoot = process.cwd(); - const cliPath = path.resolve(projectRoot, 'dist/index.js'); - - beforeAll(async () => { - // Clean and rebuild - execSync('npm run build', { stdio: 'inherit' }); - // Give the filesystem a moment to finish writing - await new Promise((resolve) => setTimeout(resolve, 1000)); - }); - - it('should show help output when run with --help', () => { - expect.hasAssertions(); - - const output = execSync(`node ${cliPath} --help`, { - env: { ...process.env, NODE_ENV: 'test' }, - encoding: 'utf8', - }); - - expect(output).toContain('Usage: depsweep [options]'); - expect(output).toContain('Options:'); - expect(output).toContain('--help'); - expect(output).toContain('--version'); - }); -}); diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts deleted file mode 100644 index 41f9a0e..0000000 --- a/src/__tests__/setup.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { jest } from '@jest/globals'; - -// Set testEnvironment to node -process.env.NODE_ENV = 'test'; - -// Ensure cleanup after each test -afterEach(() => { - jest.clearAllMocks(); -}); - -// Mock process.exit to prevent exiting during tests -const mockExit = jest - .spyOn(process, 'exit') - .mockImplementation((code?: string | number | null | undefined) => { - return undefined as never; // Type assertion to satisfy 'never' return type - }); - -// Suppress console errors during tests -const originalConsoleError = console.error; -beforeAll(() => { - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalConsoleError; -}); - -// Optionally, you can restore the original process.exit after all tests -afterAll(() => { - mockExit.mockRestore(); -}); diff --git a/src/__tests__/unit.test.ts b/src/__tests__/unit.test.ts deleted file mode 100644 index 042339a..0000000 --- a/src/__tests__/unit.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - jest, -} from '@jest/globals'; - -import { findUp } from 'find-up'; -import ora from 'ora'; - -// Mock modules -jest.mock('chalk'); -jest.mock('globby'); -jest.mock('isbinaryfile'); -jest.mock('ora'); -jest.mock('node:fs/promises'); -jest.mock('find-up'); - -// Mock findUp to return our fake package.json path -jest.mocked(findUp).mockResolvedValue('/fake/path/package.json'); - -import { - getSourceFiles, - findClosestPackageJson, - getDependencies, -} from '../index'; - -const oraMock = ora as jest.MockedFunction; - -describe('getSourceFiles', () => { - beforeAll(() => { - process.env.NODE_ENV = 'test'; - }); - - afterEach(async () => { - await jest.clearAllMocks(); - }); - - afterAll(async () => { - await jest.restoreAllMocks(); - }); - - it('should return an array of file paths', async () => { - expect.hasAssertions(); - - const files = await getSourceFiles(process.cwd()); - - // Wait for all promises to resolve - await new Promise(process.nextTick); - - expect(Array.isArray(files)).toBe(true); - expect(files).toHaveLength(2); - expect(files).toEqual([ - '/fake/path/src/index.ts', - '/fake/path/src/utils.ts', - ]); - }); - - it('should not check for spinner in getDependencies', () => { - getDependencies('/fake/path/package.json'); - }); -}); - -describe('depsweep', () => { - it('exports required functions', () => { - expect(findClosestPackageJson).toBeDefined(); - expect(getDependencies).toBeDefined(); - expect(getSourceFiles).toBeDefined(); - }); -}); diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..1dbd8bb --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,73 @@ +export const MESSAGES = { + title: 'DepSweep 🧹', + noPackageJson: 'No package.json found', + monorepoDetected: '\nMonorepo detected, using root package.json', + monorepoWorkspaceDetected: '\nMonorepo workspace package detected', + analyzingDependencies: 'Analyzing dependencies...', + fatalError: '\nFatal error:', + noUnusedDependencies: 'No unused dependencies found', + unusedFound: 'Detected unused dependencies:', + noChangesMade: 'No changes made', + promptRemove: 'Do you want to remove these unused dependencies? (y/N) ', + dependenciesRemoved: 'Dependencies:', + diskSpace: 'Unpacked Disk Space:', + carbonFootprint: 'Carbon Footprint:', + measuringImpact: 'Impact Analysis', + measureComplete: 'Measurement complete', + installTime: 'Total Install Time:', + signalCleanup: '\n{0} received, cleaning up...', + unexpected: '\nUnexpected error:', +}; + +export const CLI_STRINGS = { + PROGRESS_FORMAT: + 'Analyzing dependencies |{bar}| {currentFiles}/{totalFiles} Files | {currentDeps}/{totalDeps} Dependencies | {percentage}%', + BAR_COMPLETE: '\u2588', + BAR_INCOMPLETE: '\u2591', + CLI_NAME: 'depsweep', + CLI_DESCRIPTION: + 'Automated intelligent dependency cleanup and impact analysis report', + EXAMPLE_TEXT: '\nExample:\n $ depsweep -v --measure-impact', +}; + +export const RAW_CONTENT_PATTERNS = new Map([ + ['webpack', ['webpack.*', 'webpack-*']], + ['babel', ['babel.*', '@babel/*']], + ['eslint', ['eslint.*', '@eslint/*']], + ['jest', ['jest.*', '@jest/*']], + ['typescript', ['ts-*', '@typescript-*']], + ['rollup', ['rollup.*', 'rollup-*']], + ['esbuild', ['esbuild.*', '@esbuild/*']], + ['vite', ['vite.*', '@vitejs/*']], + ['next', ['next.*', '@next/*']], + ['vue', ['vue.*', '@vue/*', '@nuxt/*']], + ['react', ['react.*', '@types/react*']], + ['svelte', ['svelte.*', '@sveltejs/*']], +]); + +export const DEPENDENCY_PATTERNS = { + TYPES_PREFIX: '@types/', + DYNAMIC_IMPORT_BASE: String.raw`import\s*\(\s*['"]`, + DYNAMIC_IMPORT_END: String.raw`['"]\s*\)`, +}; + +export const FILE_PATTERNS = { + PACKAGE_JSON: 'package.json', + YARN_LOCK: 'yarn.lock', + PNPM_LOCK: 'pnpm-lock.yaml', + NODE_MODULES: 'node_modules', + CONFIG_REGEX: /\.(config|rc)(\.|\b)/, + PACKAGE_NAME_REGEX: /^[\w./@-]+$/, +}; + +export const PACKAGE_MANAGERS = { + NPM: 'npm', + YARN: 'yarn', + PNPM: 'pnpm', +}; + +export const COMMANDS = { + INSTALL: 'install', + UNINSTALL: 'uninstall', + REMOVE: 'remove', +}; diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..6d2914e --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,868 @@ +/* eslint-disable unicorn/prefer-json-parse-buffer */ +import { execSync, spawn } from 'node:child_process'; +import { readdirSync, statSync } from 'node:fs'; +import * as fs from 'node:fs/promises'; +import { mkdtemp, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import v8 from 'node:v8'; + +import { parse } from '@babel/parser'; +import traverse, { type NodePath } from '@babel/traverse'; +import type { + ImportDeclaration, + CallExpression, + TSImportType, + TSExternalModuleReference, +} from '@babel/types'; +import chalk from 'chalk'; +import CliTable from 'cli-table3'; +import { glob } from 'glob'; +import { isBinaryFileSync } from 'isbinaryfile'; +import micromatch from 'micromatch'; +import fetch from 'node-fetch'; +import shellEscape from 'shell-escape'; + +import { + FILE_PATTERNS, + DEPENDENCY_PATTERNS, + PACKAGE_MANAGERS, + RAW_CONTENT_PATTERNS, +} from './constants.js'; +import type { + DependencyContext, + ProgressOptions, + DependencyInfo, +} from './interfaces.js'; +import { findSubDependencies } from './utils.js'; + +import { customSort } from './index.js'; + +export function isConfigFile(filePath: string): boolean { + const filename = path.basename(filePath).toLowerCase(); + return ( + filename.includes('config') || + filename.startsWith('.') || + filename === FILE_PATTERNS.PACKAGE_JSON || + FILE_PATTERNS.CONFIG_REGEX.test(filename) + ); +} + +export async function parseConfigFile(filePath: string): Promise { + const extension = path.extname(filePath).toLowerCase(); + const content = await fs.readFile(filePath, 'utf8'); + + try { + switch (extension) { + case '.json': { + return JSON.parse(content); + } + case '.yaml': + case '.yml': { + const yaml = await import('yaml').catch(() => null); + return yaml ? yaml.parse(content) : content; + } + case '.js': + case '.cjs': + case '.mjs': { + return content; + } + default: { + try { + return JSON.parse(content); + } catch { + return content; + } + } + } + } catch { + return content; + } +} + +const traverseFunction = ((traverse as any).default || traverse) as ( + ast: any, + options: any, +) => void; + +export async function isTypePackageUsed( + dependency: string, + installedPackages: string[], + unusedDependencies: string[], + context: DependencyContext, + sourceFiles: string[], +): Promise<{ isUsed: boolean; supportedPackage?: string }> { + if (!dependency.startsWith(DEPENDENCY_PATTERNS.TYPES_PREFIX)) { + return { isUsed: false }; + } + + const correspondingPackage = dependency + .replace(/^@types\//, '') + .replaceAll('__', '/'); + + const normalizedPackage = correspondingPackage.includes('/') + ? `@${correspondingPackage}` + : correspondingPackage; + + const supportedPackage = installedPackages.find( + (package_) => package_ === normalizedPackage, + ); + + if (supportedPackage) { + for (const file of sourceFiles) { + if (await isDependencyUsedInFile(supportedPackage, file, context)) { + return { isUsed: true, supportedPackage }; + } + } + } + + for (const package_ of installedPackages) { + try { + const packageJsonPath = require.resolve(`${package_}/package.json`, { + paths: [process.cwd()], + }); + const packageJsonBuffer = await fs.readFile(packageJsonPath); + const packageJson = JSON.parse(packageJsonBuffer.toString('utf8')) as { + peerDependencies?: Record; + }; + if (packageJson.peerDependencies?.[dependency]) { + return { isUsed: true, supportedPackage: package_ }; + } + } catch { + // Ignore errors + } + } + + return { isUsed: false }; +} + +export function scanForDependency( + object: unknown, + dependency: string, +): boolean { + if (typeof object === 'string') { + const matchers = generatePatternMatcher(dependency); + return matchers.some((pattern) => pattern.test(object)); + } + + if (Array.isArray(object)) { + return object.some((item) => scanForDependency(item, dependency)); + } + + if (object && typeof object === 'object') { + return Object.values(object).some((value) => + scanForDependency(value, dependency), + ); + } + + return false; +} + +export async function isDependencyUsedInFile( + dependency: string, + filePath: string, + context: DependencyContext, +): Promise { + if ( + path.basename(filePath) === FILE_PATTERNS.PACKAGE_JSON && + context.configs?.['package.json'] && + scanForDependency(context.configs['package.json'], dependency) + ) { + return true; + } + + const configKey = path.relative(path.dirname(filePath), filePath); + const config = context.configs?.[configKey]; + if (config) { + if (typeof config === 'string') { + if (config.includes(dependency)) { + return true; + } + } else if (scanForDependency(config, dependency)) { + return true; + } + } + + if (context.scripts) { + for (const script of Object.values(context.scripts)) { + const scriptParts = script.split(' '); + if (scriptParts.includes(dependency)) { + return true; + } + } + } + + try { + if (isBinaryFileSync(filePath)) { + return false; + } + + const content = await fs.readFile(filePath, 'utf8'); + + const dynamicImportRegex = new RegExp( + `${DEPENDENCY_PATTERNS.DYNAMIC_IMPORT_BASE}${dependency.replaceAll(/[/@-]/g, '[/@-]')}${DEPENDENCY_PATTERNS.DYNAMIC_IMPORT_END}`, + 'i', + ); + if (dynamicImportRegex.test(content)) { + return true; + } + + try { + const ast = parse(content, { + sourceType: 'unambiguous', + plugins: [ + 'typescript', + 'jsx', + 'decorators-legacy', + 'classProperties', + 'dynamicImport', + 'exportDefaultFrom', + 'exportNamespaceFrom', + 'importMeta', + ], + }); + + let isUsed = false; + traverseFunction(ast, { + ImportDeclaration(importPath: NodePath) { + const importSource = importPath.node.source.value; + if (matchesDependency(importSource, dependency)) { + isUsed = true; + importPath.stop(); + } + }, + CallExpression(importPath: NodePath) { + if ( + importPath.node.callee.type === 'Identifier' && + importPath.node.callee.name === 'require' && + importPath.node.arguments[0]?.type === 'StringLiteral' && + matchesDependency(importPath.node.arguments[0].value, dependency) + ) { + isUsed = true; + importPath.stop(); + } + }, + TSImportType(importPath: NodePath) { + const importSource = importPath.node.argument.value; + if (matchesDependency(importSource, dependency)) { + isUsed = true; + importPath.stop(); + } + }, + TSExternalModuleReference( + importPath: NodePath, + ) { + const importSource = importPath.node.expression.value; + if (matchesDependency(importSource, dependency)) { + isUsed = true; + importPath.stop(); + } + }, + }); + + if (isUsed) return true; + + for (const [base, patterns] of RAW_CONTENT_PATTERNS.entries()) { + if ( + dependency.startsWith(base) && + patterns.some((pattern: string) => + micromatch.isMatch(dependency, pattern), + ) + ) { + const searchPattern = new RegExp( + `\\b${dependency.replaceAll(/[/@-]/g, '[/@-]')}\\b`, + 'i', + ); + if (searchPattern.test(content)) { + return true; + } + } + } + } catch { + // Ignore parse errors + } + + for (const [base, patterns] of RAW_CONTENT_PATTERNS.entries()) { + if ( + dependency.startsWith(base) && + patterns.some((pattern: string) => + micromatch.isMatch(dependency, pattern), + ) + ) { + const searchPattern = new RegExp( + `\\b${dependency.replaceAll(/[/@-]/g, '[/@-]')}\\b`, + 'i', + ); + if (searchPattern.test(content)) { + return true; + } + } + } + } catch { + // Ignore file read errors + } + + return false; +} + +export function getMemoryUsage(): { used: number; total: number } { + const heapStats = v8.getHeapStatistics(); + return { + used: heapStats.used_heap_size, + total: heapStats.heap_size_limit, + }; +} + +interface DependencyPattern { + type: 'exact' | 'prefix' | 'suffix' | 'combined' | 'regex'; + match: string | RegExp; + variations?: string[]; +} + +const COMMON_PATTERNS: DependencyPattern[] = [ + // Direct matches + { type: 'exact', match: '' }, // Base name + { type: 'prefix', match: '@' }, // Scoped packages + + // Common package organization patterns + { type: 'prefix', match: '@types/' }, + { type: 'prefix', match: '@storybook/' }, + { type: 'prefix', match: '@testing-library/' }, + + // Config patterns + { + type: 'suffix', + match: 'config', + variations: ['rc', 'settings', 'configuration', 'setup', 'options'], + }, + + // Plugin patterns + { + type: 'suffix', + match: 'plugin', + variations: ['plugins', 'extension', 'extensions', 'addon', 'addons'], + }, + + // Preset patterns + { + type: 'suffix', + match: 'preset', + variations: ['presets', 'recommended', 'standard', 'defaults'], + }, + + // Tool patterns + { + type: 'combined', + match: '', + variations: ['cli', 'core', 'utils', 'tools', 'helper', 'helpers'], + }, + + // Framework integration patterns + { + type: 'regex', + match: /[/-](react|vue|svelte|angular|node)$/i, + }, + + // Common package naming patterns + { + type: 'regex', + match: /[/-](loader|parser|transformer|formatter|linter|compiler)s?$/i, + }, +]; + +export function generatePatternMatcher(dependency: string): RegExp[] { + const patterns: RegExp[] = []; + const escapedDep = dependency.replaceAll( + /[$()*+.?[\\\]^{|}]/g, + String.raw`\$&`, + ); + + for (const pattern of COMMON_PATTERNS) { + switch (pattern.type) { + case 'exact': { + patterns.push(new RegExp(`^${escapedDep}$`)); + break; + } + case 'prefix': { + patterns.push(new RegExp(`^${pattern.match}${escapedDep}(/.*)?$`)); + break; + } + case 'suffix': { + const suffixes = [pattern.match, ...(pattern.variations || [])]; + for (const suffix of suffixes) { + patterns.push( + new RegExp(`^${escapedDep}[-./]${suffix}$`), + new RegExp(`^${escapedDep}[-./]${suffix}s$`), + ); + } + break; + } + case 'combined': { + const parts = [pattern.match, ...(pattern.variations || [])]; + for (const part of parts) { + patterns.push( + new RegExp(`^${escapedDep}[-./]${part}$`), + new RegExp(`^${part}[-./]${escapedDep}$`), + ); + } + break; + } + case 'regex': { + if (pattern.match instanceof RegExp) { + patterns.push( + new RegExp( + `^${escapedDep}${pattern.match.source}`, + pattern.match.flags, + ), + ); + } + break; + } + } + } + + return patterns; +} + +export function matchesDependency( + importSource: string, + dependency: string, +): boolean { + const depWithoutScope = dependency.startsWith('@') + ? dependency.split('/')[1] + : dependency; + const sourceWithoutScope = importSource.startsWith('@') + ? importSource.split('/')[1] + : importSource; + + return ( + importSource === dependency || + importSource.startsWith(`${dependency}/`) || + sourceWithoutScope === depWithoutScope || + sourceWithoutScope.startsWith(`${depWithoutScope}/`) || + (dependency.startsWith('@types/') && + (importSource === dependency.replace(/^@types\//, '') || + importSource.startsWith(`${dependency.replace(/^@types\//, '')}/`))) + ); +} + +export function processResults( + batchResults: PromiseSettledResult<{ + result: string | null; + hasError: boolean; + }>[], +): { validResults: string[]; errors: number } { + const validResults: string[] = []; + let errors = 0; + + for (const result of batchResults) { + if (result.status === 'fulfilled') { + if (result.value.hasError) { + errors++; + } else if (result.value.result) { + validResults.push(result.value.result); + } + } + } + + return { validResults, errors }; +} + +interface InstallMetrics { + installTime: number; + diskSpace: number; + errors?: string[]; +} + +function getDirectorySize(directory: string): number { + let total = 0; + const files = readdirSync(directory, { withFileTypes: true }); + for (const f of files) { + const fullPath = path.join(directory, f.name); + total += f.isDirectory() + ? getDirectorySize(fullPath) + : statSync(fullPath).size; + } + return total; +} + +export function formatSize(bytes: number): string { + if (bytes >= 1e12) { + return `${(bytes / 1e12).toFixed(2)} ${chalk.blue('TB')}`; + } else if (bytes >= 1e9) { + return `${(bytes / 1e9).toFixed(2)} ${chalk.blue('GB')}`; + } else if (bytes >= 1e6) { + return `${(bytes / 1e6).toFixed(2)} ${chalk.blue('MB')}`; + } else if (bytes >= 1e3) { + return `${(bytes / 1e3).toFixed(2)} ${chalk.blue('KB')}`; + } + return `${bytes} ${chalk.blue('Bytes')}`; +} + +export function formatTime(seconds: number): string { + if (seconds >= 86_400) { + return `${(seconds / 86_400).toFixed(2)} ${chalk.blue('Days')}`; + } else if (seconds >= 3600) { + return `${(seconds / 3600).toFixed(2)} ${chalk.blue('Hours')}`; + } else if (seconds >= 60) { + return `${(seconds / 60).toFixed(2)} ${chalk.blue('Minutes')}`; + } + return `${seconds.toFixed(2)} ${chalk.blue('Seconds')}`; +} + +export function formatNumber(n: number): string { + return n.toLocaleString(); +} + +export function safeExecSync( + command: string[], + options: { cwd: string; stdio?: 'inherit' | 'ignore'; timeout?: number }, +): void { + if (!Array.isArray(command) || command.length === 0) { + throw new Error('Invalid command array'); + } + + const [packageManager, ...arguments_] = command; + + if (!Object.values(PACKAGE_MANAGERS).includes(packageManager)) { + throw new Error(`Invalid package manager: ${packageManager}`); + } + + // Validate all arguments + if ( + !arguments_.every( + (argument) => typeof argument === 'string' && argument.length > 0, + ) + ) { + throw new Error('Invalid command arguments'); + } + + try { + execSync(shellEscape(command), { + stdio: options.stdio || 'inherit', + cwd: options.cwd, + timeout: options.timeout ?? 300_000, + encoding: 'utf8', + }); + } catch (error) { + throw new Error(`Command execution failed: ${(error as Error).message}`); + } +} + +export async function detectPackageManager( + projectDirectory: string, +): Promise { + if ( + await fs + .access(path.join(projectDirectory, FILE_PATTERNS.YARN_LOCK)) + .then(() => true) + .catch(() => false) + ) { + return PACKAGE_MANAGERS.YARN; + } else if ( + await fs + .access(path.join(projectDirectory, FILE_PATTERNS.PNPM_LOCK)) + .then(() => true) + .catch(() => false) + ) { + return PACKAGE_MANAGERS.PNPM; + } + return PACKAGE_MANAGERS.NPM; +} + +export async function createTemporaryPackageJson( + package_: string, +): Promise { + const minimalPackageJson = { + name: 'depsweep-temp', + version: '1.0.0', + private: true, + dependencies: { [package_]: '*' }, + }; + + const temporaryDirectory = await mkdtemp(path.join(tmpdir(), 'depsweep-')); + const packageJsonPath = path.join(temporaryDirectory, 'package.json'); + await writeFile(packageJsonPath, JSON.stringify(minimalPackageJson, null, 2)); + + return temporaryDirectory; +} + +export async function measurePackageInstallation( + packageName: string, +): Promise { + const metrics: InstallMetrics = { + installTime: 0, + diskSpace: 0, + errors: [], + }; + + try { + // Create temp directory with package.json + const temporaryDirectory = await createTemporaryPackageJson(packageName); + + // Measure install time + const startTime = Date.now(); + try { + await new Promise((resolve, reject) => { + const install = spawn('npm', ['install', '--no-package-lock'], { + cwd: temporaryDirectory, + stdio: 'ignore', + }); + + install.on('close', (code) => { + if (code === 0) resolve(); + else reject(new Error(`npm install failed with code ${code}`)); + }); + install.on('error', reject); + }); + } catch (error) { + metrics.errors?.push(`Install error: ${(error as Error).message}`); + } + + metrics.installTime = (Date.now() - startTime) / 1000; + + // Measure disk space + const nodeModulesPath = path.join(temporaryDirectory, 'node_modules'); + metrics.diskSpace = getDirectorySize(nodeModulesPath); + + // Cleanup + await fs.rm(temporaryDirectory, { recursive: true, force: true }); + } catch (error) { + metrics.errors?.push(`Measurement error: ${(error as Error).message}`); + } + + return metrics; +} + +export async function getDownloadStatsFromNpm( + packageName: string, +): Promise { + try { + const response = await fetch( + `https://api.npmjs.org/downloads/point/last-month/${packageName}`, + ); + if (!response.ok) { + return null; + } + const data = await response.json(); + const downloadData = data as { downloads: number }; + return downloadData.downloads || null; + } catch { + return null; + } +} + +export async function getParentPackageDownloads( + packageJsonPath: string, +): Promise<{ + name: string; + downloads: number; + repository?: { url: string }; + homepage?: string; +} | null> { + try { + const packageJsonString = + (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; + const packageJson = JSON.parse(packageJsonString); + const { name, repository, homepage } = packageJson; + if (!name) return null; + + const downloads = await getDownloadStatsFromNpm(name); + if (!downloads) { + console.log( + chalk.yellow(`\nUnable to find download stats for '${name}'`), + ); + return null; + } + return { name, downloads, repository, homepage }; + } catch { + return null; + } +} + +export async function getYearlyDownloads( + packageName: string, + months = 12, +): Promise<{ total: number; monthsFetched: number; startDate: string } | null> { + const monthlyDownloads: number[] = []; + const currentDate = new Date(); + let startDate = ''; + let monthsFetched = 0; + + for (let index = 0; index < months; index++) { + const start = new Date( + currentDate.getFullYear(), + currentDate.getMonth() - index, + 1, + ); + const end = new Date( + currentDate.getFullYear(), + currentDate.getMonth() - index + 1, + 0, + ); + const [startString] = start.toISOString().split('T'); + const [endString] = end.toISOString().split('T'); + + try { + const response = await fetch( + `https://api.npmjs.org/downloads/range/${startString}:${endString}/${packageName}`, + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = (await response.json()) as { + downloads: { downloads: number; day: string }[]; + }; + + if (data.downloads && Array.isArray(data.downloads)) { + // Sum all daily downloads for that month + const monthTotal = data.downloads.reduce( + (accumulator, dayItem) => accumulator + (dayItem.downloads || 0), + 0, + ); + monthlyDownloads.push(monthTotal); + + // Capture the earliest date containing non-zero data + if (monthTotal > 0 && !startDate) { + startDate = startString; + } + monthsFetched++; + } + } catch (error) { + console.error( + `Failed to fetch downloads for ${startString} to ${endString}:`, + error, + ); + break; + } + } + + // Trim trailing zero months + let lastNonZeroIndex = -1; + for (let index = monthlyDownloads.length - 1; index >= 0; index--) { + if (monthlyDownloads[index] > 0) { + lastNonZeroIndex = index; + break; + } + } + + // If no non-zero data found, return null + if (lastNonZeroIndex === -1) { + return null; + } + + // Recalculate monthsFetched and remove trailing zeros + monthlyDownloads.splice(lastNonZeroIndex + 1); + monthsFetched = monthlyDownloads.length; + + // If the recorded startDate is empty (all leading zero months?), set it to the latest non-zero period + if (!startDate) { + const validMonthsAgo = monthsFetched - 1; + const trimmedStart = new Date( + currentDate.getFullYear(), + currentDate.getMonth() - validMonthsAgo, + 1, + ); + [startDate] = trimmedStart.toISOString().split('T'); + } + + // Sum total + const totalDownloads = monthlyDownloads.reduce((a, b) => a + b, 0); + return { total: totalDownloads, monthsFetched, startDate }; +} + +export function calculateImpactStats( + diskSpace: number, + installTime: number, + monthlyDownloads: number | null, + yearlyData: { + total: number; + monthsFetched: number; + startDate: string; + } | null, +): any { + const stats: any = {}; + + if (!yearlyData) { + return stats; + } + + const { total, monthsFetched } = yearlyData; + const daysCount = monthsFetched * 30; + if (daysCount === 0 || total === 0) { + return stats; + } + + // Compute daily average + const dailyAvg = total / daysCount; + + // Replace with day based on up to 30 days + const relevantDays = Math.min(30, daysCount); + const daySum = dailyAvg * relevantDays; + const dayAverage = daySum / relevantDays; + stats.day = { + downloads: Math.round(dayAverage), + diskSpace: diskSpace * dayAverage, + installTime: installTime * dayAverage, + }; + + // 30-day (Monthly) + stats.monthly = { + downloads: Math.round(dailyAvg * 30), + diskSpace: diskSpace * dailyAvg * 30, + installTime: installTime * dailyAvg * 30, + }; + + // Last X months + stats[`last_${monthsFetched}_months`] = { + downloads: Math.round(dailyAvg * daysCount), + diskSpace: diskSpace * dailyAvg * daysCount, + installTime: installTime * dailyAvg * daysCount, + }; + + // If we have at least 12 months, add yearly + if (monthsFetched >= 12) { + const yearlyDays = 12 * 30; + stats.yearly = { + downloads: Math.round(dailyAvg * yearlyDays), + diskSpace: diskSpace * dailyAvg * yearlyDays, + installTime: installTime * dailyAvg * yearlyDays, + }; + } + + return stats; +} + +export function displayImpactTable( + impactData: Record, + totalInstallTime: number, + totalDiskSpace: number, +) { + const table = new CliTable({ + head: ['Package', 'Install Time', 'Disk Space'], + colWidths: [29, 25, 25], + wordWrap: true, + style: { + head: ['cyan'], + border: ['grey'], + }, + }); + + const sortedImpactData = Object.entries(impactData).sort(([a], [b]) => + customSort(a, b), + ); + + for (const [package_, data] of sortedImpactData) { + const numericTime = Number.parseFloat(data.installTime); + table.push([package_, formatTime(numericTime), data.diskSpace]); + } + + // Add totals row with separator + table.push([ + chalk.bold('Total'), + chalk.bold(formatTime(totalInstallTime)), + chalk.bold(formatSize(totalDiskSpace)), + ]); + + console.log(table.toString()); +} diff --git a/src/index.ts b/src/index.ts index 0b2ab15..eecaafc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,846 +1,51 @@ #!/usr/bin/env node /* eslint-disable unicorn/prefer-json-parse-buffer */ -import { execSync, spawn } from 'node:child_process'; -import { readdirSync, statSync } from 'node:fs'; import * as fs from 'node:fs/promises'; -import { mkdtemp, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; import path from 'node:path'; import { stdin as input, stdout as output } from 'node:process'; import * as readline from 'node:readline/promises'; -import v8 from 'node:v8'; - -import { parse } from '@babel/parser'; -import traverse, { type NodePath } from '@babel/traverse'; -import type { - ImportDeclaration, - CallExpression, - TSImportType, - TSExternalModuleReference, -} from '@babel/types'; + import chalk from 'chalk'; import cliProgress from 'cli-progress'; import CliTable from 'cli-table3'; import { Command } from 'commander'; -import { findUp } from 'find-up'; -import { globby } from 'globby'; import { isBinaryFileSync } from 'isbinaryfile'; -import micromatch from 'micromatch'; import ora, { type Ora } from 'ora'; -import shellEscape from 'shell-escape'; - -/* - * Essential packages that should never be removed automatically, - * unless the `-a, --aggressive` flag is used. - ******************************************************************/ -const PROTECTED_PACKAGES = new Set([ - 'typescript', - '@types/node', - 'tslib', - 'prettier', - 'eslint', -]); -/******************************************************************/ - -// Common string literals -const CLI_STRINGS = { - PROGRESS_FORMAT: - 'Analyzing dependencies |{bar}| {currentFiles}/{totalFiles} Files | {currentDeps}/{totalDeps} Dependencies | {percentage}%', - BAR_COMPLETE: '\u2588', - BAR_INCOMPLETE: '\u2591', - CLI_NAME: 'depsweep', - CLI_DESCRIPTION: - 'Automated intelligent dependency cleanup and impact analysis report', - EXAMPLE_TEXT: '\nExample:\n $ depsweep -v --measure-impact', -} as const; - -const FILE_PATTERNS = { - PACKAGE_JSON: 'package.json', - YARN_LOCK: 'yarn.lock', - PNPM_LOCK: 'pnpm-lock.yaml', - NODE_MODULES: 'node_modules', - CONFIG_REGEX: /\.(config|rc)(\.|\b)/, - PACKAGE_NAME_REGEX: /^[\w./@-]+$/, -} as const; - -const PACKAGE_MANAGERS = { - NPM: 'npm', - YARN: 'yarn', - PNPM: 'pnpm', - COMMANDS: { - INSTALL: 'install', - UNINSTALL: 'uninstall', - REMOVE: 'remove', - }, -} as const; - -const DEPENDENCY_PATTERNS = { - TYPES_PREFIX: '@types/', - DYNAMIC_IMPORT_BASE: String.raw`import\s*\(\s*['"]`, - DYNAMIC_IMPORT_END: String.raw`['"]\s*\)`, -} as const; - -// Replace existing MESSAGES constant -const MESSAGES = { - title: 'DepSweep 🧹', - noPackageJson: 'No package.json found', - monorepoDetected: '\nMonorepo detected, using root package.json', - monorepoWorkspaceDetected: '\nMonorepo workspace package detected', - analyzingDependencies: 'Analyzing dependencies...', - fatalError: '\nFatal error:', - noUnusedDependencies: 'No unused dependencies found', - unusedFound: 'Unused dependencies found:', - noChangesMade: 'No changes made', - promptRemove: 'Do you want to remove these unused dependencies? (y/N) ', - dependenciesRemoved: 'Dependencies:', - diskSpace: 'Unpacked Disk Space:', - carbonFootprint: 'Carbon Footprint:', - measuringImpact: 'Measuring impact...', - measureComplete: 'Measurement complete', - installTime: 'Total Install Time:', - analysisComplete: 'Analysis complete', - signalCleanup: '\n{0} received, cleaning up...', - unexpected: '\nUnexpected error:', -} as const; - -// Update interface for package.json structure -interface PackageJson { - dependencies?: Record; - devDependencies?: Record; - peerDependencies?: Record; - optionalDependencies?: Record; - workspaces?: string[] | { packages: string[] }; - scripts?: Record; -} - -// Add interface for dependency context -interface DependencyContext { - scripts?: Record; - configs?: Record; -} - -// Add interface for workspace info -interface WorkspaceInfo { - root: string; - packages: string[]; -} -const traverseFunction = ((traverse as any).default || traverse) as ( - ast: any, - options: any, -) => void; - -// Add raw content patterns -const RAW_CONTENT_PATTERNS = new Map([ - ['webpack', ['webpack.*', 'webpack-*']], - ['babel', ['babel.*', '@babel/*']], - ['eslint', ['eslint.*', '@eslint/*']], - ['jest', ['jest.*', '@jest/*']], - ['typescript', ['ts-*', '@typescript-*']], - ['rollup', ['rollup.*', 'rollup-*']], - ['esbuild', ['esbuild.*', '@esbuild/*']], - ['vite', ['vite.*', '@vitejs/*']], - ['next', ['next.*', '@next/*']], - ['vue', ['vue.*', '@vue/*', '@nuxt/*']], - ['react', ['react.*', '@types/react*']], - ['svelte', ['svelte.*', '@sveltejs/*']], -]); - -// Add workspace detection -async function getWorkspaceInfo( - packageJsonPath: string, -): Promise { - try { - const content = await fs.readFile(packageJsonPath, 'utf8'); - const package_ = JSON.parse(content); - - if (!package_.workspaces) return undefined; - - const patterns = Array.isArray(package_.workspaces) - ? package_.workspaces - : package_.workspaces.packages || []; - - const packagePaths = await globby(patterns, { - cwd: path.dirname(packageJsonPath), - onlyDirectories: true, - expandDirectories: false, - ignore: ['node_modules'], - }); - - return { - root: packageJsonPath, - packages: packagePaths, - }; - } catch { - return undefined; - } -} - -// Enhanced package.json finder with improved monorepo support -async function findClosestPackageJson(startDirectory: string): Promise { - const packageJsonPath = await findUp(FILE_PATTERNS.PACKAGE_JSON, { - cwd: startDirectory, - }); - if (!packageJsonPath) { - console.error(chalk.red(MESSAGES.noPackageJson)); - process.exit(1); - } - - // Check if this is part of a monorepo - let currentDirectory = path.dirname(packageJsonPath); - while (true) { - const parentDirectory = path.dirname(currentDirectory); - if (parentDirectory === currentDirectory) { - break; - } - const potentialRootPackageJson = path.join( - parentDirectory, - FILE_PATTERNS.PACKAGE_JSON, - ); - try { - const rootPackageString = await fs.readFile( - potentialRootPackageJson, - 'utf8', - ); - const rootPackage = JSON.parse(rootPackageString); - if (rootPackage.workspaces) { - console.log(chalk.yellow(MESSAGES.monorepoDetected)); - return potentialRootPackageJson; - } - } catch { - // No package.json found at this level - } - const workspaceInfo = await getWorkspaceInfo(potentialRootPackageJson); - - if (workspaceInfo) { - const relativePath = path.relative( - path.dirname(workspaceInfo.root), - packageJsonPath, - ); - const isWorkspacePackage = workspaceInfo.packages.some( - (p) => relativePath.startsWith(p) || p.startsWith(relativePath), - ); - - if (isWorkspacePackage) { - console.log(chalk.yellow('\nMonorepo workspace package detected.')); - console.log(chalk.yellow(`Root: ${workspaceInfo.root}`)); - return packageJsonPath; // Analyze the workspace package - } - } - currentDirectory = parentDirectory; - } - - return packageJsonPath; -} - -// Function to read dependencies from package.json -async function getDependencies(packageJsonPath: string): Promise { - const packageJsonString = - (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; - const packageJson = JSON.parse(packageJsonString); - - const dependencies = packageJson.dependencies - ? Object.keys(packageJson.dependencies) - : []; - const devDependencies = packageJson.devDependencies - ? Object.keys(packageJson.devDependencies) - : []; - const peerDependencies = packageJson.peerDependencies - ? Object.keys(packageJson.peerDependencies) - : []; - const optionalDependencies = packageJson.optionalDependencies - ? Object.keys(packageJson.optionalDependencies) - : []; - - return [ - ...dependencies, - ...devDependencies, - ...peerDependencies, - ...optionalDependencies, - ]; -} - -// Add these helper functions -function isConfigFile(filePath: string): boolean { - const filename = path.basename(filePath).toLowerCase(); - return ( - filename.includes('config') || - filename.startsWith('.') || - filename === FILE_PATTERNS.PACKAGE_JSON || - FILE_PATTERNS.CONFIG_REGEX.test(filename) - ); -} - -async function parseConfigFile(filePath: string): Promise { - const extension = path.extname(filePath).toLowerCase(); - const content = await fs.readFile(filePath, 'utf8'); - - try { - switch (extension) { - case '.json': { - return JSON.parse(content); - } - case '.yaml': - case '.yml': { - const yaml = await import('yaml').catch(() => null); - return yaml ? yaml.parse(content) : content; - } - case '.js': - case '.cjs': - case '.mjs': { - // For JS files, return the raw content as we can't safely eval - return content; - } - default: { - // For unknown extensions, try JSON parse first, then return raw content - try { - return JSON.parse(content); - } catch { - return content; - } - } - } - } catch { - // If parsing fails, return the raw content - return content; - } -} - -// Update getSourceFiles function -async function getSourceFiles( - projectDirectory: string, - ignorePatterns: string[] = [], -): Promise { - const files = await globby(['**/*'], { - cwd: projectDirectory, - gitignore: true, - ignore: [ - FILE_PATTERNS.NODE_MODULES, - 'dist', - 'coverage', - 'build', - '.git', - '*.log', - '*.lock', - ...ignorePatterns, - ], - absolute: true, - }); - - // Filter out binary files and return - return files.filter((file) => !isBinaryFileSync(file)); -} - -// Update getPackageContext function -async function getPackageContext( - packageJsonPath: string, -): Promise { - const projectDirectory = path.dirname(packageJsonPath); - const configs: Record = {}; - - // Read all files in the project - const allFiles = await getSourceFiles(projectDirectory); - - // Process config files - for (const file of allFiles) { - if (isConfigFile(file)) { - const relativePath = path.relative(projectDirectory, file); - try { - configs[relativePath] = await parseConfigFile(file); - } catch { - // Ignore parse errors - } - } - } - - // Get package.json content - const packageJsonString = - (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; - const packageJson = JSON.parse(packageJsonString) as PackageJson & { - eslintConfig?: { extends?: string | string[] }; - prettier?: unknown; - stylelint?: { extends?: string | string[] }; - }; - - return { - scripts: packageJson.scripts, - configs: { - 'package.json': packageJson, - ...configs, - }, - }; -} - -// Add a helper function to check if a type package corresponds to an installed package -async function isTypePackageUsed( - dependency: string, - installedPackages: string[], - unusedDependencies: string[], - context: DependencyContext, - sourceFiles: string[], -): Promise<{ isUsed: boolean; supportedPackage?: string }> { - if (!dependency.startsWith(DEPENDENCY_PATTERNS.TYPES_PREFIX)) { - return { isUsed: false }; - } - - // Handle special case for @types/babel__* packages etc - const correspondingPackage = dependency - .replace(/^@types\//, '') - .replaceAll('__', '/'); - - // For scoped packages, add the @ prefix back - const normalizedPackage = correspondingPackage.includes('/') - ? `@${correspondingPackage}` - : correspondingPackage; - - const supportedPackage = installedPackages.find( - (package_) => package_ === normalizedPackage, - ); - - if (supportedPackage) { - // Check if the corresponding package is used in the source files - for (const file of sourceFiles) { - if (await isDependencyUsedInFile(supportedPackage, file, context)) { - return { isUsed: true, supportedPackage }; - } - } - } - - // Check if any installed package has this type package as a peer dependency - for (const package_ of installedPackages) { - try { - const packageJsonPath = require.resolve(`${package_}/package.json`, { - paths: [process.cwd()], - }); - const packageJsonBuffer = await fs.readFile(packageJsonPath, 'utf8'); - const packageJson = JSON.parse(packageJsonBuffer); - if (packageJson.peerDependencies?.[dependency]) { - return { isUsed: true, supportedPackage: package_ }; - } - } catch { - // Ignore errors - } - } - - return { isUsed: false }; -} - -// Add helper function to recursively scan objects for dependency usage -// Add dependency pattern helpers -interface DependencyPattern { - type: 'exact' | 'prefix' | 'suffix' | 'combined' | 'regex'; - match: string | RegExp; - variations?: string[]; -} - -const COMMON_PATTERNS: DependencyPattern[] = [ - // Direct matches - { type: 'exact', match: '' }, // Base name - { type: 'prefix', match: '@' }, // Scoped packages - - // Common package organization patterns - { type: 'prefix', match: '@types/' }, - { type: 'prefix', match: '@storybook/' }, - { type: 'prefix', match: '@testing-library/' }, - - // Config patterns - { - type: 'suffix', - match: 'config', - variations: ['rc', 'settings', 'configuration', 'setup', 'options'], - }, - - // Plugin patterns - { - type: 'suffix', - match: 'plugin', - variations: ['plugins', 'extension', 'extensions', 'addon', 'addons'], - }, - - // Preset patterns - { - type: 'suffix', - match: 'preset', - variations: ['presets', 'recommended', 'standard', 'defaults'], - }, - - // Tool patterns - { - type: 'combined', - match: '', - variations: ['cli', 'core', 'utils', 'tools', 'helper', 'helpers'], - }, - - // Framework integration patterns - { - type: 'regex', - match: /[/-](react|vue|svelte|angular|node)$/i, - }, - - // Common package naming patterns - { - type: 'regex', - match: /[/-](loader|parser|transformer|formatter|linter|compiler)s?$/i, - }, -]; - -function generatePatternMatcher(dependency: string): RegExp[] { - const patterns: RegExp[] = []; - const escapedDep = dependency.replaceAll( - /[$()*+.?[\\\]^{|}]/g, - String.raw`\$&`, - ); - - for (const pattern of COMMON_PATTERNS) { - switch (pattern.type) { - case 'exact': { - patterns.push(new RegExp(`^${escapedDep}$`)); - break; - } - case 'prefix': { - patterns.push(new RegExp(`^${pattern.match}${escapedDep}(/.*)?$`)); - break; - } - case 'suffix': { - const suffixes = [pattern.match, ...(pattern.variations || [])]; - for (const suffix of suffixes) { - patterns.push( - new RegExp(`^${escapedDep}[-./]${suffix}$`), - new RegExp(`^${escapedDep}[-./]${suffix}s$`), - ); - } - break; - } - case 'combined': { - const parts = [pattern.match, ...(pattern.variations || [])]; - for (const part of parts) { - patterns.push( - new RegExp(`^${escapedDep}[-./]${part}$`), - new RegExp(`^${part}[-./]${escapedDep}$`), - ); - } - break; - } - case 'regex': { - if (pattern.match instanceof RegExp) { - patterns.push( - new RegExp( - `^${escapedDep}${pattern.match.source}`, - pattern.match.flags, - ), - ); - } - break; - } - } - } - - return patterns; -} - -// Replace the old scanForDependency function -function scanForDependency(object: unknown, dependency: string): boolean { - if (typeof object === 'string') { - const matchers = generatePatternMatcher(dependency); - return matchers.some((pattern) => pattern.test(object)); - } - - if (Array.isArray(object)) { - return object.some((item) => scanForDependency(item, dependency)); - } - - if (object && typeof object === 'object') { - return Object.values(object).some((value) => - scanForDependency(value, dependency), - ); - } - - return false; -} - -async function isDependencyUsedInFile( - dependency: string, - filePath: string, - context: DependencyContext, -): Promise { - // For package.json, do a deep scan of all configurations - if ( - path.basename(filePath) === FILE_PATTERNS.PACKAGE_JSON && - context.configs?.['package.json'] && // Deep scan all of package.json content - scanForDependency(context.configs['package.json'], dependency) - ) { - return true; - } - - // Check if the file is a config file we've parsed - const configKey = path.relative(path.dirname(filePath), filePath); - const config = context.configs?.[configKey]; - if (config) { - if (typeof config === 'string') { - // If the config is a string, treat it as raw content - if (config.includes(dependency)) { - return true; - } - } else if (scanForDependency(config, dependency)) { - return true; - } - } - - // Check scripts for exact matches - if (context.scripts) { - for (const script of Object.values(context.scripts)) { - const scriptParts = script.split(' '); - if (scriptParts.includes(dependency)) { - return true; - } - } - } - - // Check file imports - try { - if (isBinaryFileSync(filePath)) { - return false; - } - - const content = await fs.readFile(filePath, 'utf8'); - - // Check for dynamic imports in raw content - const dynamicImportRegex = new RegExp( - `${DEPENDENCY_PATTERNS.DYNAMIC_IMPORT_BASE}${dependency.replaceAll(/[/@-]/g, '[/@-]')}${DEPENDENCY_PATTERNS.DYNAMIC_IMPORT_END}`, - 'i', - ); - if (dynamicImportRegex.test(content)) { - return true; - } - - // AST parsing for imports/requires - try { - const ast = parse(content, { - sourceType: 'unambiguous', - plugins: [ - 'typescript', - 'jsx', - 'decorators-legacy', - 'classProperties', - 'dynamicImport', - 'exportDefaultFrom', - 'exportNamespaceFrom', - 'importMeta', - ], - }); - - let isUsed = false; - traverseFunction(ast, { - ImportDeclaration(importPath: NodePath) { - const importSource = importPath.node.source.value; - if (matchesDependency(importSource, dependency)) { - isUsed = true; - importPath.stop(); - } - }, - CallExpression(importPath: NodePath) { - if ( - importPath.node.callee.type === 'Identifier' && - importPath.node.callee.name === 'require' && - importPath.node.arguments[0]?.type === 'StringLiteral' && - matchesDependency(importPath.node.arguments[0].value, dependency) - ) { - isUsed = true; - importPath.stop(); - } - }, - // Add handlers for TypeScript type-only imports - TSImportType(importPath: NodePath) { - const importSource = importPath.node.argument.value; - if (matchesDependency(importSource, dependency)) { - isUsed = true; - importPath.stop(); - } - }, - TSExternalModuleReference( - importPath: NodePath, - ) { - const importSource = importPath.node.expression.value; - if (matchesDependency(importSource, dependency)) { - isUsed = true; - importPath.stop(); - } - }, - }); - - if (isUsed) return true; - - // Only check raw patterns if not found in imports - for (const [base, patterns] of RAW_CONTENT_PATTERNS.entries()) { - if ( - dependency.startsWith(base) && - patterns.some((pattern) => micromatch.isMatch(dependency, pattern)) - ) { - const searchPattern = new RegExp( - `\\b${dependency.replaceAll(/[/@-]/g, '[/@-]')}\\b`, - 'i', - ); - if (searchPattern.test(content)) { - return true; - } - } - } - } catch { - // Ignore parse errors - } - - // Only check raw patterns if not found in imports - for (const [base, patterns] of RAW_CONTENT_PATTERNS.entries()) { - if ( - dependency.startsWith(base) && - patterns.some((pattern) => micromatch.isMatch(dependency, pattern)) - ) { - const searchPattern = new RegExp( - `\\b${dependency.replaceAll(/[/@-]/g, '[/@-]')}\\b`, - 'i', - ); - if (searchPattern.test(content)) { - return true; - } - } - } - } catch { - // Ignore file read errors - } - - return false; -} - -// Add a helper function to match dependencies -function matchesDependency(importSource: string, dependency: string): boolean { - const depWithoutScope = dependency.startsWith('@') - ? dependency.split('/')[1] - : dependency; - const sourceWithoutScope = importSource.startsWith('@') - ? importSource.split('/')[1] - : importSource; - - return ( - importSource === dependency || - importSource.startsWith(`${dependency}/`) || - sourceWithoutScope === depWithoutScope || - sourceWithoutScope.startsWith(`${depWithoutScope}/`) || - (dependency.startsWith('@types/') && - (importSource === dependency.replace(/^@types\//, '') || - importSource.startsWith(`${dependency.replace(/^@types\//, '')}/`))) - ); -} - -// Add memory check function -function getMemoryUsage(): { used: number; total: number } { - const heapStats = v8.getHeapStatistics(); - return { - used: heapStats.used_heap_size, - total: heapStats.heap_size_limit, - }; -} - -function processResults( - batchResults: PromiseSettledResult<{ - result: string | null; - hasError: boolean; - }>[], -): { validResults: string[]; errors: number } { - const validResults: string[] = []; - let errors = 0; - - for (const result of batchResults) { - if (result.status === 'fulfilled') { - if (result.value.hasError) { - errors++; - } else if (result.value.result) { - validResults.push(result.value.result); - } - } - } - - return { validResults, errors }; -} - -// Enhanced parallel processing with memory management -async function processFilesInParallel( - files: string[], - dependency: string, - context: DependencyContext, - onProgress?: (processed: number, total: number) => void, -): Promise { - const { total: maxMemory } = getMemoryUsage(); - const BATCH_SIZE = Math.min( - 100, - Math.max(10, Math.floor(maxMemory / (1024 * 1024 * 50))), - ); - - const results: string[] = []; - let totalErrors = 0; - - const processFile = async ( - file: string, - ): Promise<{ result: string | null; hasError: boolean }> => { - try { - const used = await isDependencyUsedInFile(dependency, file, context); - return { result: used ? file : null, hasError: false }; - } catch (error) { - console.error( - chalk.red(`Error processing ${file}: ${(error as Error).message}`), - ); - return { result: null, hasError: true }; - } - }; - - for (let index = 0; index < files.length; index += BATCH_SIZE) { - const batch = files.slice(index, index + BATCH_SIZE); - const batchResults = await Promise.allSettled( - batch.map(async (file) => processFile(file)), - ); - const { validResults, errors } = processResults(batchResults); - results.push(...validResults); - totalErrors += errors; - - const processed = Math.min(index + batch.length, files.length); - onProgress?.(processed, files.length); - } - - if (totalErrors > 0) { - console.warn( - chalk.yellow(`\nWarning: ${totalErrors} files had processing errors`), - ); - } - - return results; -} - -// Add function to detect the package manager -async function detectPackageManager(projectDirectory: string): Promise { - if ( - await fs - .access(path.join(projectDirectory, FILE_PATTERNS.YARN_LOCK)) - .then(() => true) - .catch(() => false) - ) { - return PACKAGE_MANAGERS.YARN; - } else if ( - await fs - .access(path.join(projectDirectory, FILE_PATTERNS.PNPM_LOCK)) - .then(() => true) - .catch(() => false) - ) { - return PACKAGE_MANAGERS.PNPM; - } - return PACKAGE_MANAGERS.NPM; -} - -// Add these variables before the main function +import { + CLI_STRINGS, + FILE_PATTERNS, + MESSAGES, + PACKAGE_MANAGERS, +} from './constants.js'; +import { + isTypePackageUsed, + safeExecSync, + detectPackageManager, + measurePackageInstallation, + getParentPackageDownloads, + getYearlyDownloads, + calculateImpactStats, + displayImpactTable, + formatSize, + formatTime, + formatNumber, +} from './helpers.js'; +import { + getSourceFiles, + findClosestPackageJson, + getDependencies, + getPackageContext, + processFilesInParallel, + getDependencyInfo, +} from './utils.js'; + +// Variables for active resources let activeSpinner: Ora | null = null; let activeProgressBar: cliProgress.SingleBar | null = null; let activeReadline: readline.Interface | null = null; -// Add this function before the main function function cleanup(): void { if (activeSpinner) { activeSpinner.stop(); @@ -857,412 +62,21 @@ function cleanup(): void { } } -// Add this validation function function isValidPackageName(name: string): boolean { return FILE_PATTERNS.PACKAGE_NAME_REGEX.test(name); } -// Recursively compute dir size for accurate disk usage stats -function getDirectorySize(directory: string): number { - let total = 0; - const files = readdirSync(directory, { withFileTypes: true }); - for (const f of files) { - const fullPath = path.join(directory, f.name); - total += f.isDirectory() - ? getDirectorySize(fullPath) - : statSync(fullPath).size; - } - return total; -} - -// Add a helper function to format bytes into human-readable strings -function formatSize(bytes: number): string { - if (bytes >= 1e12) { - return `${(bytes / 1e12).toFixed(2)} ${chalk.blue('TB')}`; - } else if (bytes >= 1e9) { - return `${(bytes / 1e9).toFixed(2)} ${chalk.blue('GB')}`; - } else if (bytes >= 1e6) { - return `${(bytes / 1e6).toFixed(2)} ${chalk.blue('MB')}`; - } else if (bytes >= 1e3) { - return `${(bytes / 1e3).toFixed(2)} ${chalk.blue('KB')}`; - } - return `${bytes} ${chalk.blue('Bytes')}`; -} - -// Add this validation at the top with other constants -const VALID_PACKAGE_MANAGERS = new Set(['npm', 'yarn', 'pnpm']); - -// Add safe execution wrapper -function safeExecSync( - command: string[], - options: { - cwd: string; - stdio?: 'inherit' | 'ignore'; - timeout?: number; - }, -): void { - if (!Array.isArray(command) || command.length === 0) { - throw new Error('Invalid command array'); - } - - const [packageManager, ...arguments_] = command; - - if (!VALID_PACKAGE_MANAGERS.has(packageManager)) { - throw new Error(`Invalid package manager: ${packageManager}`); - } - - // Validate all arguments - if ( - !arguments_.every( - (argument) => typeof argument === 'string' && argument.length > 0, - ) - ) { - throw new Error('Invalid command arguments'); - } - - try { - execSync(shellEscape(command), { - stdio: options.stdio || 'inherit', - cwd: options.cwd, - timeout: options.timeout ?? 300_000, - encoding: 'utf8', - }); - } catch (error) { - throw new Error(`Command execution failed: ${(error as Error).message}`); - } -} - -interface InstallMetrics { - installTime: number; - diskSpace: number; - errors?: string[]; -} - -async function createTemporaryPackageJson(package_: string): Promise { - const minimalPackageJson = { - name: 'depsweep-temp', - version: '1.0.0', - private: true, - dependencies: { [package_]: '*' }, - }; - - const temporaryDirectory = await mkdtemp(path.join(tmpdir(), 'depsweep-')); - const packageJsonPath = path.join(temporaryDirectory, 'package.json'); - await writeFile(packageJsonPath, JSON.stringify(minimalPackageJson, null, 2)); - - return temporaryDirectory; -} - -async function measurePackageInstallation( - packageName: string, -): Promise { - const metrics: InstallMetrics = { - installTime: 0, - diskSpace: 0, - errors: [], - }; - - try { - // Create temp directory with package.json - const temporaryDirectory = await createTemporaryPackageJson(packageName); - - // Measure install time - const startTime = Date.now(); - try { - await new Promise((resolve, reject) => { - const install = spawn('npm', ['install', '--no-package-lock'], { - cwd: temporaryDirectory, - stdio: 'ignore', - }); - - install.on('close', (code) => { - if (code === 0) resolve(); - else reject(new Error(`npm install failed with code ${code}`)); - }); - install.on('error', reject); - }); - } catch (error) { - metrics.errors?.push(`Install error: ${(error as Error).message}`); - } - - metrics.installTime = (Date.now() - startTime) / 1000; - - // Measure disk space - const nodeModulesPath = path.join(temporaryDirectory, 'node_modules'); - metrics.diskSpace = getDirectorySize(nodeModulesPath); - - // Cleanup - await fs.rm(temporaryDirectory, { recursive: true, force: true }); - } catch (error) { - metrics.errors?.push(`Measurement error: ${(error as Error).message}`); - } - - return metrics; -} - -async function getDownloadStatsFromNpm( - packageName: string, -): Promise { - try { - const response = await fetch( - `https://api.npmjs.org/downloads/point/last-month/${packageName}`, - ); - if (!response.ok) { - return null; - } - const data = await response.json(); - const downloadData = data as { downloads: number }; - return downloadData.downloads || null; - } catch { - return null; - } -} - -function formatTime(seconds: number): string { - if (seconds >= 86_400) { - return `${(seconds / 86_400).toFixed(2)} ${chalk.blue('Days')}`; - } else if (seconds >= 3600) { - return `${(seconds / 3600).toFixed(2)} ${chalk.blue('Hours')}`; - } else if (seconds >= 60) { - return `${(seconds / 60).toFixed(2)} ${chalk.blue('Minutes')}`; - } - return `${seconds.toFixed(2)} ${chalk.blue('Seconds')}`; -} - -function formatNumber(n: number): string { - return n.toLocaleString(); -} - -async function getParentPackageDownloads(packageJsonPath: string): Promise<{ - name: string; - downloads: number; -} | null> { - try { - const packageJsonString = - (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; - const packageJson = JSON.parse(packageJsonString); - const { name } = packageJson; - if (!name) return null; - - const downloads = await getDownloadStatsFromNpm(name); - if (!downloads) { - console.log( - chalk.yellow(`\nUnable to find download stats for '${name}'`), - ); - return null; - } - return { name, downloads }; - } catch { - return null; - } -} - -interface measureImpactStats { - daily?: { - downloads: number; - diskSpace: number; - installTime: number; - }; - monthly?: { - downloads: number; - diskSpace: number; - installTime: number; - }; - yearly?: { - downloads: number; - diskSpace: number; - installTime: number; - }; - [key: string]: - | { - downloads: number; - diskSpace: number; - installTime: number; - } - | undefined; -} - -async function getYearlyDownloads( - packageName: string, - months: number = 12, -): Promise<{ total: number; monthsFetched: number; startDate: string } | null> { - const monthlyDownloads: number[] = []; - const currentDate = new Date(); - let startDate = ''; - let monthsFetched = 0; - - for (let index = 0; index < months; index++) { - const start = new Date( - currentDate.getFullYear(), - currentDate.getMonth() - index, - 1, - ); - const end = new Date( - currentDate.getFullYear(), - currentDate.getMonth() - index + 1, - 0, - ); - const [startString] = start.toISOString().split('T'); - const [endString] = end.toISOString().split('T'); - - try { - const response = await fetch( - `https://api.npmjs.org/downloads/range/${startString}:${endString}/${packageName}`, - ); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = (await response.json()) as { - downloads: { downloads: number; day: string }[]; - }; - - if (data.downloads && Array.isArray(data.downloads)) { - // Sum all daily downloads for that month - const monthTotal = data.downloads.reduce( - (accumulator, dayItem) => accumulator + (dayItem.downloads || 0), - 0, - ); - monthlyDownloads.push(monthTotal); - - // Capture the earliest date containing non-zero data - if (monthTotal > 0 && !startDate) { - startDate = startString; - } - monthsFetched++; - } - } catch (error) { - console.error( - `Failed to fetch downloads for ${startString} to ${endString}:`, - error, - ); - break; - } - } - - // Trim trailing zero months - let lastNonZeroIndex = -1; - for (let index = monthlyDownloads.length - 1; index >= 0; index--) { - if (monthlyDownloads[index] > 0) { - lastNonZeroIndex = index; - break; - } - } - - // If no non-zero data found, return null - if (lastNonZeroIndex === -1) { - return null; - } - - // Recalculate monthsFetched and remove trailing zeros - monthlyDownloads.splice(lastNonZeroIndex + 1); - monthsFetched = monthlyDownloads.length; - - // If the recorded startDate is empty (all leading zero months?), set it to the latest non-zero period - if (!startDate) { - const validMonthsAgo = monthsFetched - 1; - const trimmedStart = new Date( - currentDate.getFullYear(), - currentDate.getMonth() - validMonthsAgo, - 1, - ); - [startDate] = trimmedStart.toISOString().split('T'); - } - - // Sum total - const totalDownloads = monthlyDownloads.reduce((a, b) => a + b, 0); - return { total: totalDownloads, monthsFetched, startDate }; -} - -function calculateImpactStats( - diskSpace: number, - installTime: number, - monthlyDownloads: number | null, - yearlyData: { - total: number; - monthsFetched: number; - startDate: string; - } | null, -): measureImpactStats { - const stats: measureImpactStats = {}; - - if (!yearlyData) { - return stats; +function logNewlines(count = 1): void { + for (let index = 0; index < count; index++) { + console.log(); } - - const { total, monthsFetched } = yearlyData; - const daysCount = monthsFetched * 30; - if (daysCount === 0 || total === 0) { - return stats; - } - - // Compute daily average - const dailyAvg = total / daysCount; - - // Replace with day based on up to 30 days - const relevantDays = Math.min(30, daysCount); - const daySum = dailyAvg * relevantDays; - const dayAverage = daySum / relevantDays; - stats.day = { - downloads: Math.round(dayAverage), - diskSpace: diskSpace * dayAverage, - installTime: installTime * dayAverage, - }; - - // 30-day (Monthly) - stats.monthly = { - downloads: Math.round(dailyAvg * 30), - diskSpace: diskSpace * dailyAvg * 30, - installTime: installTime * dailyAvg * 30, - }; - - // Last X months - stats[`last_${monthsFetched}_months`] = { - downloads: Math.round(dailyAvg * daysCount), - diskSpace: diskSpace * dailyAvg * daysCount, - installTime: installTime * dailyAvg * daysCount, - }; - - // If we have at least 12 months, add yearly - if (monthsFetched >= 12) { - const yearlyDays = 12 * 30; - stats.yearly = { - downloads: Math.round(dailyAvg * yearlyDays), - diskSpace: diskSpace * dailyAvg * yearlyDays, - installTime: installTime * dailyAvg * yearlyDays, - }; - } - - return stats; } -function displayImpactTable( - impactData: Record, - totalInstallTime: number, - totalDiskSpace: number, -) { - const table = new CliTable({ - head: ['Package', 'Install Time', 'Disk Space'], - colWidths: [29, 25, 25], - wordWrap: true, - style: { - head: ['cyan'], - border: ['grey'], - }, - }); - - for (const [package_, data] of Object.entries(impactData)) { - const numericTime = Number.parseFloat(data.installTime); - table.push([package_, formatTime(numericTime), data.diskSpace]); - } - - // Add totals row with separator - table.push([ - chalk.bold('Total'), - chalk.bold(formatTime(totalInstallTime)), - chalk.bold(formatSize(totalDiskSpace)), - ]); - - console.log(table.toString()); +// Custom sort function to handle scoped dependencies +export function customSort(a: string, b: string): number { + const aNormalized = a.replace(/^@/, ''); + const bNormalized = b.replace(/^@/, ''); + return aNormalized.localeCompare(bNormalized, 'en', { sensitivity: 'base' }); } // Main execution @@ -1326,7 +140,7 @@ async function main(): Promise { } console.log(chalk.cyan(MESSAGES.title)); - console.log(chalk.bold('Dependency Analysis\n')); + logNewlines(); console.log(chalk.blue(`Package.json found at: ${packageJsonPath}`)); process.on('uncaughtException', (error: Error): void => { @@ -1340,122 +154,137 @@ async function main(): Promise { }); const dependencies = await getDependencies(packageJsonPath); - dependencies.sort((a, b) => - a - .replace(/^@/, '') - .localeCompare(b.replace(/^@/, ''), 'en', { sensitivity: 'base' }), - ); - const sourceFiles = await getSourceFiles( + dependencies.sort(customSort); + + // Filter out any file you don't want to count (e.g., binaries): + const allFiles = await getSourceFiles( projectDirectory, options.ignore || [], ); + const filteredFiles = []; + for (const file of allFiles) { + // ...check if file is binary or some skip condition... + if (!isBinaryFileSync(file)) { + filteredFiles.push(file); + } + } - let unusedDependencies: string[] = []; - const dependencyUsage: Record = {}; - const typePackageSupport: Record = {}; + // sourceFiles now refers to filteredFiles + const sourceFiles = filteredFiles; + const topLevelDeps = new Set(dependencies); - let processedFiles = 0; - let processedDependencies = 0; - const totalDependencies = dependencies.length; - const totalFiles = sourceFiles.length; + const safeUnused: string[] = []; + + // Update totalAnalysisSteps to include subdependencies and files + const totalAnalysisSteps = dependencies.length * sourceFiles.length; + let analysisStepsProcessed = 0; let progressBar: cliProgress.SingleBar | null = null; if (options.progress) { progressBar = new cliProgress.SingleBar({ - format: CLI_STRINGS.PROGRESS_FORMAT, + format: 'Dependency Analysis |{bar}| {currentDep}', barCompleteChar: CLI_STRINGS.BAR_COMPLETE, barIncompleteChar: CLI_STRINGS.BAR_INCOMPLETE, }); activeProgressBar = progressBar; - progressBar.start(totalFiles, 0, { - currentDeps: 0, - totalDeps: totalDependencies, - currentFiles: 0, - totalFiles, + progressBar.start(100, 0, { + currentDep: '', }); } - for (const dep of dependencies) { - const usageFiles = await processFilesInParallel( - sourceFiles, + // Declare subdepIndex and subdepCount at an appropriate scope + let subdepIndex = 0; + let subdepCount = 0; + + let totalDepsProcessed = 0; + const currentDepFiles = new Set(); // Add this to track unique files per dep + + // Add this before the loop + const subdepsProcessed = new Set(); + + // Adjust progress tracking + const progressCallback = ( + filePath: string, + sIndex?: number, + sCount?: number, + ) => { + analysisStepsProcessed++; + if (progressBar) { + progressBar.update( + (analysisStepsProcessed / totalAnalysisSteps) * 100, + { + currentDep: `[${totalDepsProcessed}/${dependencies.length}] ${currentDependency}`, + }, + ); + } + }; + + // Create a map to store all dependency info + const depInfoMap = new Map< + string, + Awaited> + >(); + + // Create a variable to store the current dependency name + let currentDependency = ''; + + // Analyze all dependencies + for (const [index, dep] of dependencies.entries()) { + currentDependency = dep; // Update the current dependency name + subdepsProcessed.clear(); + totalDepsProcessed++; + currentDepFiles.clear(); // Reset the file tracker for each dependency + subdepIndex = 0; + subdepCount = 0; + const info = await getDependencyInfo( dep, context, - (processed) => { - if (progressBar) { - processedFiles = processed; - progressBar.update(processed, { - currentDeps: processedDependencies, - totalDeps: totalDependencies, - currentFiles: processed, - totalFiles, - }); - } + sourceFiles, + topLevelDeps, + { + onProgress: progressCallback, + totalAnalysisSteps, }, ); - processedDependencies++; - if (progressBar) { - progressBar.update({ - currentDeps: processedDependencies, - currentFiles: processedFiles, - }); - } + depInfoMap.set(dep, info); - if (usageFiles.length === 0) { - unusedDependencies.push(dep); - } - dependencyUsage[dep] = usageFiles; + await new Promise((res) => setImmediate(res)); } if (progressBar) { + // Force final update to ensure 100% is shown + progressBar.update(100, { + currentDep: `[${totalDepsProcessed}/${dependencies.length}] ${chalk.green('✔')}`, + }); progressBar.stop(); } - console.log(chalk.green('✔'), MESSAGES.analysisComplete, '\n'); - // Filter out type packages that correspond to installed packages - const installedPackages = dependencies.filter( - (dep) => !dep.startsWith('@types/'), - ); - const typePackageUsagePromises = unusedDependencies.map(async (dep) => { - const { isUsed, supportedPackage } = await isTypePackageUsed( - dep, - installedPackages, - unusedDependencies, - context, - sourceFiles, + logNewlines(); + + // Determine unused dependencies based on complete analysis + let unusedDependencies = dependencies.filter((dep) => { + const info = depInfoMap.get(dep)!; + return ( + info.usedInFiles.length === 0 && info.requiredByPackages.size === 0 ); - if (isUsed && supportedPackage) { - typePackageSupport[dep] = supportedPackage; - } - return { dep, isUsed }; }); - const typePackageUsageResults = await Promise.all(typePackageUsagePromises); - unusedDependencies = typePackageUsageResults - .filter((result) => !result.isUsed) - .map((result) => result.dep); - - // By default, run in safe mode (filter out protected packages) - // Only include protected packages if aggressive flag is set - let safeUnused: string[] = []; - - // Create a combined set of protected packages - const protectedPackages = new Set([ - ...PROTECTED_PACKAGES, - ...(options.safe || []), - ]); - - // In aggressive mode, only use user-specified safe deps - const safeSet = options.aggressive - ? new Set(options.safe || []) - : protectedPackages; - - if (safeSet.size > 0) { - safeUnused = unusedDependencies.filter((dep) => safeSet.has(dep)); - unusedDependencies = unusedDependencies.filter( - (dep) => !safeSet.has(dep), - ); - } + // Finalize the unused dependencies to account for those + // used only by other unused dependencies + unusedDependencies = finalizeUnusedDependencies( + unusedDependencies, + depInfoMap, + dependencies, + ); + + // Sort unused dependencies alphabetically + unusedDependencies.sort(customSort); + + // Sort safeUnused dependencies alphabetically + safeUnused.sort(customSort); + + // Show results and handle package removal if (unusedDependencies.length === 0 && safeUnused.length === 0) { console.log(chalk.green(MESSAGES.noUnusedDependencies)); } else if (unusedDependencies.length === 0 && safeUnused.length > 0) { @@ -1466,7 +295,8 @@ async function main(): Promise { chalk.blue(`- ${dep} [${isSafeListed ? 'safe' : 'protected'}]`), ); } - console.log('\n\n', chalk.blue(MESSAGES.noChangesMade)); + logNewlines(2); // replaces console.log('\n\n') + console.log(chalk.blue(MESSAGES.noChangesMade)); } else { console.log(chalk.bold(MESSAGES.unusedFound)); for (const dep of unusedDependencies) { @@ -1478,50 +308,56 @@ async function main(): Promise { chalk.blue(`- ${dep} [${isSafeListed ? 'safe' : 'protected'}]`), ); } + logNewlines(); - let totalInstallTime = 0; - let totalDiskSpace = 0; - const installResults: { - dep: string; - time: number; - space: number; - errors?: string[]; - }[] = []; - + // Display verbose output if requested if (options.verbose) { const table = new CliTable({ - head: ['Dependency', 'Usage'], + head: ['Dependency', 'Direct Usage', 'Required By'], wordWrap: true, - colWidths: [30, 50], - style: { - head: ['cyan'], - border: ['grey'], - }, + colWidths: [25, 35, 20], + style: { head: ['cyan'], border: ['grey'] }, }); - for (const dep of dependencies) { - const usage = dependencyUsage[dep]; - const supportInfo = typePackageSupport[dep] - ? ` (supports "${typePackageSupport[dep]}")` - : ''; - let label = ''; - if (safeSet.has(dep)) { - label = options.safe?.includes(dep) ? '[safe]' : '[protected]'; - } - table.push([ - `${dep} ${chalk.blue(label)}`, - usage.length > 0 - ? usage.map((u) => path.relative(projectDirectory, u)).join('\n') - : chalk.yellow(`Not used${supportInfo}`), - ]); + const sortedDependencies = [...dependencies].sort(customSort); + for (const dep of sortedDependencies) { + const info = depInfoMap.get(dep)!; + const fileUsage = + info.usedInFiles.length > 0 + ? info.usedInFiles + .map((f) => path.relative(projectDirectory, f)) + .join('\n') + : chalk.gray('-'); + + const requiredBy = + info.requiredByPackages.size > 0 + ? [...info.requiredByPackages] + .map((requestDep) => + unusedDependencies.includes(requestDep) + ? `${requestDep} ${chalk.blue('(unused)')}` + : requestDep, + ) + .join(', ') + : chalk.gray('-'); + + table.push([dep, fileUsage, requiredBy]); } - console.log(); console.log(table.toString()); + logNewlines(); } + // Measure impact if requested if (options.measureImpact) { - console.log(''); + let totalInstallTime = 0; + let totalDiskSpace = 0; + const installResults: { + dep: string; + time: number; + space: number; + errors?: string[]; + }[] = []; + const measureSpinner = ora({ text: MESSAGES.measuringImpact, spinner: 'dots', @@ -1543,21 +379,23 @@ async function main(): Promise { errors: metrics.errors, }); - const progress = `[${index + 1}/${totalPackages}]`; - measureSpinner.text = `${MESSAGES.measuringImpact} ${chalk.blue(progress)}`; + const progress = `[${index + 1}/${totalPackages}] ${dep}`; + measureSpinner.text = `${MESSAGES.measuringImpact} ${progress}`; } catch (error) { console.error(`Error measuring ${dep}:`, error); } } - measureSpinner.succeed( - `${MESSAGES.measureComplete} ${chalk.blue(`[${totalPackages}/${totalPackages}]`)}`, + measureSpinner.stop(); + console.log( + `${MESSAGES.measuringImpact} [${totalPackages}/${totalPackages}] ${chalk.green('✔')}`, ); const parentInfo = await getParentPackageDownloads(packageJsonPath); + logNewlines(); console.log( - `\n${chalk.bold('Impact Analysis Report:')} ${chalk.yellow(parentInfo?.name)}`, + `${chalk.bold('Unused Dependency Impact Report:')} ${chalk.yellow(parentInfo?.name)} ${chalk.blue(`(${parentInfo?.homepage || parentInfo?.repository?.url || ''})`)}`, ); // Create a table for detailed results @@ -1640,14 +478,16 @@ async function main(): Promise { console.log(impactTable.toString()); + logNewlines(); console.log( - `\n${chalk.yellow( + `${chalk.yellow( 'Note:', )} These results depend on your system's capabilities.\nTry a multi-architecture analysis at ${chalk.bold('https://github.com/chiefmikey/depsweep/analysis')}`, ); } else { + logNewlines(); console.log( - chalk.yellow('\nInsufficient download data to calculate impact'), + chalk.yellow('Insufficient download data to calculate impact'), ); } } @@ -1655,18 +495,19 @@ async function main(): Promise { if (!options.measureImpact) { console.log( chalk.blue( - '\nRun with the -m, --measure-impact flag to output a detailed impact analysis report', + 'Run with the -m, --measure-impact flag to output a detailed impact analysis report', ), ); } - console.log('\n'); - if (options.dryRun) { + logNewlines(2); // Use 2 newlines here console.log(chalk.blue(MESSAGES.noChangesMade)); return; } + logNewlines(2); // Use 2 newlines here + // Prompt to remove dependencies const rl = readline.createInterface({ input, output }); activeReadline = rl; @@ -1758,4 +599,34 @@ init().catch((error) => { process.exit(1); }); -export { getSourceFiles, findClosestPackageJson, getDependencies }; +function finalizeUnusedDependencies( + initialUnusedDeps: string[], + depInfoMap: Map< + string, + { usedInFiles: string[]; requiredByPackages: Set } + >, + allDeps: string[], +): string[] { + const unusedSet = new Set(initialUnusedDeps); + let changed = true; + + while (changed) { + changed = false; + for (const dep of allDeps) { + if (!unusedSet.has(dep)) { + const info = depInfoMap.get(dep); + if (info) { + // If every package requiring this dep is also unused, mark it unused + const allRequirersUnused = [...info.requiredByPackages].every( + (package_) => unusedSet.has(package_), + ); + if (allRequirersUnused && info.usedInFiles.length === 0) { + unusedSet.add(dep); + changed = true; + } + } + } + } + } + return [...unusedSet]; +} diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 0000000..4218787 --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,37 @@ +export interface DependencyContext { + scripts?: Record; + configs?: Record; + projectRoot: string; + dependencyGraph?: Map>; // Added dependencyGraph +} + +export interface PackageJson { + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + workspaces?: string[] | { packages: string[] }; + scripts?: Record; + repository?: { url: string }; + homepage?: string; +} + +export interface WorkspaceInfo { + root: string; + packages: string[]; +} + +export interface ProgressOptions { + onProgress?: ( + filePath: string, + subdepIndex?: number, + subdepCount?: number, + ) => void; + totalAnalysisSteps: number; +} + +export interface DependencyInfo { + usedInFiles: string[]; + requiredByPackages: Set; + hasSubDependencyUsage: boolean; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..97a6237 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,523 @@ +/* eslint-disable unicorn/prefer-json-parse-buffer */ +import { readdirSync } from 'node:fs'; +import * as fs from 'node:fs/promises'; +import path from 'node:path'; + +import chalk from 'chalk'; +import { findUp } from 'find-up'; +import { globby } from 'globby'; +import { isBinaryFileSync } from 'isbinaryfile'; + +import { FILE_PATTERNS, MESSAGES } from './constants.js'; +import { + isConfigFile, + parseConfigFile, + getMemoryUsage, + processResults, + isDependencyUsedInFile, +} from './helpers.js'; +import type { + DependencyContext, + PackageJson, + WorkspaceInfo, +} from './interfaces.js'; + +import { customSort } from './index.js'; + +interface DependencyInfo { + usedInFiles: string[]; + requiredByPackages: Set; + hasSubDependencyUsage: boolean; +} + +const depInfoCache = new Map(); + +function normalizeTypesPackage(typesPackage: string): string { + // Remove @types/ prefix + const basePackage = typesPackage.replace('@types/', ''); + // Convert double underscore to @ + // e.g., babel__traverse -> @babel/traverse + if (basePackage.includes('__')) { + return `@${basePackage.replace('__', '/')}`; + } + // Handle regular packages + return basePackage.includes('/') ? `@${basePackage}` : basePackage; +} + +interface ProgressOptions { + onProgress?: ( + filePath: string, + subdepIndex?: number, + totalSubdeps?: number, + ) => void; + totalAnalysisSteps: number; +} + +export async function getDependencyInfo( + dependency: string, + context: DependencyContext, + sourceFiles: string[], + topLevelDependencies: Set, // Add this parameter + progressOptions?: ProgressOptions, +): Promise { + // Check cache + const cacheKey = `${context.projectRoot}:${dependency}`; + if (depInfoCache.has(cacheKey)) { + return depInfoCache.get(cacheKey)!; + } + + const info: DependencyInfo = { + usedInFiles: [], + requiredByPackages: new Set(), + hasSubDependencyUsage: false, + }; + + // Special handling for @types packages + if (dependency.startsWith('@types/')) { + const basePackage = normalizeTypesPackage(dependency); + const tsConfig = await getTSConfig(context.projectRoot); + + // Check if this is a compiler-required types package + if (basePackage === 'node' && hasTSFiles(sourceFiles)) { + info.requiredByPackages.add('typescript'); + return info; + } + + // Check if the base package is installed + if (topLevelDependencies.has(basePackage)) { + info.requiredByPackages.add(basePackage); + } + + // Check if any TypeScript files use types from this package + let subdepIndex = 0; + for (const file of sourceFiles) { + subdepIndex++; + if ( + (file.endsWith('.ts') || file.endsWith('.tsx')) && + ((await isDependencyUsedInFile(dependency, file, context)) || + (await isDependencyUsedInFile(basePackage, file, context))) + ) { + info.usedInFiles.push(file); + } + progressOptions?.onProgress?.(file, subdepIndex); + await new Promise((res) => setImmediate(res)); // Tiny pause + } + + // If we have TypeScript files and tsconfig includes this in types/typeRoots, mark as used + if (tsConfig && hasTSFiles(sourceFiles)) { + const { types = [], typeRoots = [] } = tsConfig.compilerOptions || {}; + if ( + types.includes(basePackage) || + typeRoots.some((root: string) => root.includes(basePackage)) + ) { + info.requiredByPackages.add('typescript'); + } + } + + return info; + } + + // Count subdependencies from the dependency graph + const subdeps = context.dependencyGraph?.get(dependency) || new Set(); + const subdepsArray = [...subdeps]; // Convert to array for indexed access + const totalSubdeps = subdepsArray.length; + + // Check direct file usage + for (const file of sourceFiles) { + // Check subdependencies first + if (subdepsArray.length > 0) { + for (const [index, subdep] of subdepsArray.entries()) { + if (await isDependencyUsedInFile(subdep, file, context)) { + info.hasSubDependencyUsage = true; + } + // Report each subdep check + progressOptions?.onProgress?.(file, index + 1, totalSubdeps); + } + } else { + // If no subdeps, still call progress + progressOptions?.onProgress?.(file); + } + + if (await isDependencyUsedInFile(dependency, file, context)) { + info.usedInFiles.push(file); + } + } + + // Check package dependencies + const nodeModulesPath = path.join(context.projectRoot, 'node_modules'); + try { + const packages = new Set(); + const entries = readdirSync(nodeModulesPath, { withFileTypes: true }); + + // Get list of package directories once + for (const entry of entries) { + if (!entry.isDirectory()) continue; + if (entry.name.startsWith('@')) { + const scopedDir = path.join(nodeModulesPath, entry.name); + const scopedEntries = readdirSync(scopedDir, { withFileTypes: true }); + for (const sub of scopedEntries) { + if (sub.isDirectory()) { + packages.add(path.join(entry.name, sub.name)); + } + } + } else { + packages.add(entry.name); + } + } + + // Bulk read package.json files + const packageJsonPromises = [...packages].map(async (package_) => { + try { + const data = await fs.readFile( + path.join(nodeModulesPath, package_, 'package.json'), + 'utf8', // Add utf8 encoding to get string instead of Buffer + ); + return { pkg: package_, data: JSON.parse(data) }; + } catch { + return null; + } + }); + + const packageJsonResults = await Promise.all(packageJsonPromises); + + // Build dependency graph to track chains + const dependencyGraph = new Map>(); + + // First pass: build direct dependency relationships + for (const result of packageJsonResults) { + if (!result) continue; + const { pkg, data } = result; + const allDeps = { + ...data.dependencies, + ...data.peerDependencies, + ...data.optionalDependencies, + }; + + // Track what each package requires + dependencyGraph.set(pkg, new Set(Object.keys(allDeps))); + } + + // Find all packages that eventually require our dependency + const findTopLevelDependents = (dep: string): Set => { + const dependents = new Set(); + + for (const [package_, deps] of dependencyGraph.entries()) { + if (deps.has(dep)) { + // If this is a top-level dependency, add it + if (topLevelDependencies.has(package_)) { + dependents.add(package_); + } else { + // Otherwise, find what requires this package + const parentDeps = findTopLevelDependents(package_); + for (const parentDep of parentDeps) { + dependents.add(parentDep); + } + } + } + } + + return dependents; + }; + + // Get all packages that require this dependency (directly or indirectly) + const allRequiringPackages = findTopLevelDependents(dependency); + info.requiredByPackages = allRequiringPackages; + } catch { + // Ignore errors + } + + // Cache the result + depInfoCache.set(cacheKey, info); + return info; +} + +async function getTSConfig(projectRoot: string): Promise { + try { + const tsConfigPath = path.join(projectRoot, 'tsconfig.json'); + const content = await fs.readFile(tsConfigPath, 'utf8'); + return JSON.parse(content); + } catch { + return null; + } +} + +function hasTSFiles(files: string[]): boolean { + return files.some((file) => file.endsWith('.ts') || file.endsWith('.tsx')); +} + +// Add workspace detection +export async function getWorkspaceInfo( + packageJsonPath: string, +): Promise { + try { + const content = await fs.readFile(packageJsonPath); + const package_ = JSON.parse(content.toString('utf8')) as PackageJson; + + if (!package_.workspaces) return undefined; + + const patterns = Array.isArray(package_.workspaces) + ? package_.workspaces + : package_.workspaces.packages || []; + + const packagePaths = await globby(patterns, { + cwd: path.dirname(packageJsonPath), + onlyDirectories: true, + expandDirectories: false, + ignore: ['node_modules'], + }); + + return { + root: packageJsonPath, + packages: packagePaths, + }; + } catch { + return undefined; + } +} + +export async function findClosestPackageJson( + startDirectory: string, +): Promise { + const packageJsonPath = await findUp(FILE_PATTERNS.PACKAGE_JSON, { + cwd: startDirectory, + }); + if (!packageJsonPath) { + console.error(chalk.red(MESSAGES.noPackageJson)); + process.exit(1); + } + + // Check if this is part of a monorepo + let currentDirectory = path.dirname(packageJsonPath); + while (true) { + const parentDirectory = path.dirname(currentDirectory); + if (parentDirectory === currentDirectory) { + break; + } + const potentialRootPackageJson = path.join( + parentDirectory, + FILE_PATTERNS.PACKAGE_JSON, + ); + try { + const rootPackageString = await fs.readFile(potentialRootPackageJson); + const rootPackage = JSON.parse(rootPackageString.toString('utf8')) as { + workspaces?: string[]; + }; + if (rootPackage.workspaces) { + console.log(chalk.yellow(MESSAGES.monorepoDetected)); + return potentialRootPackageJson; + } + } catch { + // No package.json found at this level + } + const workspaceInfo = await getWorkspaceInfo(potentialRootPackageJson); + + if (workspaceInfo) { + const relativePath = path.relative( + path.dirname(workspaceInfo.root), + packageJsonPath, + ); + const isWorkspacePackage = workspaceInfo.packages.some( + (p: string) => relativePath.startsWith(p) || p.startsWith(relativePath), + ); + + if (isWorkspacePackage) { + console.log(chalk.yellow('\nMonorepo workspace package detected.')); + console.log(chalk.yellow(`Root: ${workspaceInfo.root}`)); + return packageJsonPath; // Analyze the workspace package + } + } + currentDirectory = parentDirectory; + } + + return packageJsonPath; +} + +export async function getDependencies( + packageJsonPath: string, +): Promise { + const packageJsonString = + (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; + const packageJson = JSON.parse(packageJsonString); + + const dependencies = packageJson.dependencies + ? Object.keys(packageJson.dependencies) + : []; + const devDependencies = packageJson.devDependencies + ? Object.keys(packageJson.devDependencies) + : []; + const peerDependencies = packageJson.peerDependencies + ? Object.keys(packageJson.peerDependencies) + : []; + const optionalDependencies = packageJson.optionalDependencies + ? Object.keys(packageJson.optionalDependencies) + : []; + + const allDependencies = [ + ...dependencies, + ...devDependencies, + ...peerDependencies, + ...optionalDependencies, + ]; + + // Sort all dependencies using custom sort function + allDependencies.sort(customSort); + + return allDependencies; +} + +export async function getPackageContext( + packageJsonPath: string, +): Promise { + const projectDirectory = path.dirname(packageJsonPath); + const configs: Record = {}; + const dependencyGraph = new Map>(); // Re-added dependencyGraph + + // Populate dependencyGraph as needed + // Example: Populate with existing dependencies + const dependencies = await getDependencies(packageJsonPath); + for (const dep of dependencies) { + // Example: Initialize with empty sets or actual subdependencies + dependencyGraph.set(dep, new Set()); + } + + // Read all files in the project + const allFiles = await getSourceFiles(projectDirectory); + + // Process config files + for (const file of allFiles) { + if (isConfigFile(file)) { + const relativePath = path.relative(projectDirectory, file); + try { + configs[relativePath] = await parseConfigFile(file); + } catch { + // Ignore parse errors + } + } + } + + // Get package.json content + const packageJsonString = + (await fs.readFile(packageJsonPath, 'utf8')) || '{}'; + const packageJson = JSON.parse(packageJsonString) as PackageJson & { + eslintConfig?: { extends?: string | string[] }; + prettier?: unknown; + stylelint?: { extends?: string | string[] }; + }; + + return { + scripts: packageJson.scripts, + configs: { + 'package.json': packageJson, + ...configs, + }, + projectRoot: path.dirname(packageJsonPath), + dependencyGraph, // Included dependencyGraph + }; +} + +export async function getSourceFiles( + projectDirectory: string, + ignorePatterns: string[] = [], +): Promise { + const files = await globby(['**/*'], { + cwd: projectDirectory, + gitignore: true, + ignore: [ + FILE_PATTERNS.NODE_MODULES, + 'dist', + 'coverage', + 'build', + '.git', + '*.log', + '*.lock', + ...ignorePatterns, + ], + absolute: true, + }); + + return files.filter((file) => !isBinaryFileSync(file)); +} + +export function scanForDependency(config: any, dependency: string): boolean { + if (!config || typeof config !== 'object') { + return false; + } + + if (Array.isArray(config)) { + return config.some((item) => scanForDependency(item, dependency)); + } + + for (const [key, value] of Object.entries(config)) { + if (typeof value === 'string' && value.includes(dependency)) { + return true; + } + if (typeof value === 'object' && scanForDependency(value, dependency)) { + return true; + } + } + + return false; +} + +// Enhanced parallel processing with memory management +export async function processFilesInParallel( + files: string[], + dependency: string, + context: DependencyContext, + onProgress?: (processed: number, total: number) => void, +): Promise { + const { total: maxMemory } = getMemoryUsage(); + const BATCH_SIZE = Math.min( + 100, + Math.max(10, Math.floor(maxMemory / (1024 * 1024 * 50))), + ); + + const results: string[] = []; + let totalErrors = 0; + + const processFile = async ( + file: string, + ): Promise<{ result: string | null; hasError: boolean }> => { + try { + const used = await isDependencyUsedInFile(dependency, file, context); + return { result: used ? file : null, hasError: false }; + } catch (error) { + console.error( + chalk.red(`Error processing ${file}: ${(error as Error).message}`), + ); + return { result: null, hasError: true }; + } + }; + + for (let index = 0; index < files.length; index += BATCH_SIZE) { + const batch = files.slice(index, index + BATCH_SIZE); + const batchResults = await Promise.allSettled( + batch.map(async (file) => processFile(file)), + ); + const { validResults, errors } = processResults(batchResults); + + results.push(...validResults); + totalErrors += errors; + + const processed = Math.min(index + batch.length, files.length); + onProgress?.(processed, files.length); + } + + if (totalErrors > 0) { + console.warn( + chalk.yellow(`\nWarning: ${totalErrors} files had processing errors`), + ); + } + + return results; +} + +export function findSubDependencies( + dependency: string, + context: DependencyContext, +): string[] { + // Retrieve sub-dependencies from the dependencyGraph + return context.dependencyGraph?.get(dependency) + ? [...context.dependencyGraph.get(dependency)!] + : []; +} diff --git a/src/__mocks__/chalk.ts b/test/__mocks__/chalk.ts similarity index 100% rename from src/__mocks__/chalk.ts rename to test/__mocks__/chalk.ts diff --git a/src/__mocks__/find-up.ts b/test/__mocks__/find-up.ts similarity index 100% rename from src/__mocks__/find-up.ts rename to test/__mocks__/find-up.ts diff --git a/src/__mocks__/globby.ts b/test/__mocks__/globby.ts similarity index 100% rename from src/__mocks__/globby.ts rename to test/__mocks__/globby.ts diff --git a/src/__mocks__/isbinaryfile.ts b/test/__mocks__/isbinaryfile.ts similarity index 100% rename from src/__mocks__/isbinaryfile.ts rename to test/__mocks__/isbinaryfile.ts diff --git a/test/__mocks__/node-fetch.ts b/test/__mocks__/node-fetch.ts new file mode 100644 index 0000000..435db1e --- /dev/null +++ b/test/__mocks__/node-fetch.ts @@ -0,0 +1,8 @@ +const fetch = jest.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve({ downloads: 1000 }), + }), +); + +export default fetch; diff --git a/src/__mocks__/ora.ts b/test/__mocks__/ora.ts similarity index 100% rename from src/__mocks__/ora.ts rename to test/__mocks__/ora.ts diff --git a/src/__mocks__/fs/promises.ts b/test/__mocks__/promises.ts similarity index 100% rename from src/__mocks__/fs/promises.ts rename to test/__mocks__/promises.ts diff --git a/test/__tests__/e2e.test.ts b/test/__tests__/e2e.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/__tests__/unit.test.ts b/test/__tests__/unit.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/jest.config.ts b/test/jest.config.ts new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/test/jest.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/test/tsconfig.test.json b/test/tsconfig.test.json new file mode 100644 index 0000000..dabb806 --- /dev/null +++ b/test/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "include": [ + "src/**/*", + "test/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ], + "compilerOptions": { + "declaration": false + } +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index 871808c..0000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "src/**/*", - "src/__tests__/**/*", - "src/__mocks__/**/*", - "jest.config.ts", - "jest.config.*.ts" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/tsconfig.json b/tsconfig.json index c68f7f4..9910388 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,7 @@ "noImplicitAny": true, "removeComments": true, "preserveConstEnums": true, - "outDir": "dist", + "outDir": "./dist", "declaration": true, "types": [ "node", @@ -30,18 +30,20 @@ }, "baseUrl": ".", "resolveJsonModule": true, - "allowArbitraryExtensions": true + "allowArbitraryExtensions": true, + "rootDir": "." }, "include": [ "src/**/*", - "jest.config.*.ts" + "test/**/*", + "jest.config.*.ts", + "src" ], "exclude": [ "node_modules", "dist", - "src/__tests__/**/*", - "src/__mocks__/**/*", - "jest.config.*.ts" + "jest.config.*.ts", + "test" ], "ts-node": { "esm": true,