From b44665337a743ec43583146b161541569cec37a7 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 08:33:58 +0000 Subject: [PATCH 01/29] Init framework detection Actions workflow --- .github/workflows/auto.yaml | 33 +++++++++++++++++++++++++++ dist/index/index.js | 45 ++++++++++++++++++++++++++++++++----- src/framework.ts | 26 +++++++++++++++++++++ src/index.ts | 26 ++++++++++++++++----- 4 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/auto.yaml create mode 100644 src/framework.ts diff --git a/.github/workflows/auto.yaml b/.github/workflows/auto.yaml new file mode 100644 index 0000000..f8cd602 --- /dev/null +++ b/.github/workflows/auto.yaml @@ -0,0 +1,33 @@ +name: Test auto-framework caching +on: + push: + branches: + - main + pull_request: + branches: + - "*" + schedule: + - cron: "10 */6 * * *" + workflow_dispatch: + + # Allow check to run on the merge queue so that it is eligible to be a "required" check. + merge_group: + types: [checks_requested] + +permissions: + contents: read + +jobs: + node: + runs-on: + - namespace-profile-e2e-small + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup cache + uses: ./ # Uses an action in the root directory + # with intentionally left blank to use framework detection + + - name: Run npm install + run: npm install diff --git a/dist/index/index.js b/dist/index/index.js index 7519b94..f70e648 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29654,6 +29654,30 @@ function shouldUseSymlinks() { return useSymlinks; } +;// CONCATENATED MODULE: ./src/framework.ts +// TODO: tests +async function detectFrameworks() { + const detectors = [ + detectGo, + detectNode, + ]; + const detected = []; + for (const detector of detectors) { + const result = await detector(); + detected.push(...result); + } + return detected; +} +async function detectGo() { + return null; +} +async function detectNode() { + // TODO: + // - presence of package.json + // - get package manager (npm, pnpm, bun, yarn) + return ["node"]; +} + ;// CONCATENATED MODULE: ./src/index.ts @@ -29662,6 +29686,7 @@ function shouldUseSymlinks() { + const Input_Key = "key"; // unused const Input_Path = "path"; const Input_Cache = "cache"; @@ -29716,7 +29741,12 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi } core.info(`Found Namespace cross-invocation cache at ${localCachePath}.`); const useSymlinks = shouldUseSymlinks(); - const cachePaths = await resolveCachePaths(localCachePath); + const manualPaths = core.getMultilineInput(Input_Path); + let cacheModes = core.getMultilineInput(Input_Cache); + if (manualPaths.length === 0 && cacheModes.length === 0) { + cacheModes = await resolveFrameworks(); + } + const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); const cacheMisses = await restoreLocalCache(cachePaths, useSymlinks); const fullHit = cacheMisses.length === 0; core.setOutput(Output_CacheHit, fullHit.toString()); @@ -29793,11 +29823,17 @@ async function restoreLocalCache(cachePaths, useSymlinks) { } return cacheMisses; } -async function resolveCachePaths(localCachePath) { +async function resolveFrameworks() { + const detected = await detectFrameworks(); + if (detected.length > 0) { + core.info(`Detected frameworks: ${detected.join(", ")}`); + } + return detected; +} +async function resolveCachePaths(localCachePath, manualPaths, cacheModes) { const paths = []; - const manual = core.getMultilineInput(Input_Path); let cachesNodeModules = false; - for (const p of manual) { + for (const p of manualPaths) { paths.push({ mountTarget: p, framework: "custom" }); if (p.endsWith("/node_modules")) { cachesNodeModules = true; @@ -29807,7 +29843,6 @@ async function resolveCachePaths(localCachePath) { core.warning(`Caching node_modules is not always safe. Prefer using cache modes if possible. See also https://namespace.so/docs/reference/github-actions/nscloud-cache-action#cache`); } - const cacheModes = core.getMultilineInput(Input_Cache); let cachesXcode = false; for (const mode of cacheModes) { if (mode === ModeXcode) { diff --git a/src/framework.ts b/src/framework.ts new file mode 100644 index 0000000..5792cd8 --- /dev/null +++ b/src/framework.ts @@ -0,0 +1,26 @@ +// TODO: tests +export async function detectFrameworks(): Promise { + const detectors: Array<() => Promise> = [ + detectGo, + detectNode, + ]; + + const detected: string[] = []; + for (const detector of detectors) { + const result = await detector(); + detected.push(...result); + } + + return detected; +} + +async function detectGo(): Promise { + return null; +} + +async function detectNode(): Promise { + // TODO: + // - presence of package.json + // - get package manager (npm, pnpm, bun, yarn) + return ["node"]; +} diff --git a/src/index.ts b/src/index.ts index ece3a44..eea8bcd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import * as core from "@actions/core"; import * as exec from "@actions/exec"; import * as io from "@actions/io"; import * as utils from "./utils"; +import * as framework from "./framework"; const Input_Key = "key"; // unused const Input_Path = "path"; @@ -67,7 +68,13 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const useSymlinks = utils.shouldUseSymlinks(); - const cachePaths = await resolveCachePaths(localCachePath); + const manualPaths = core.getMultilineInput(Input_Path); + let cacheModes = core.getMultilineInput(Input_Cache); + if (manualPaths.length === 0 && cacheModes.length === 0) { + cacheModes = await resolveFrameworks(); + } + + const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); const cacheMisses = await restoreLocalCache(cachePaths, useSymlinks); const fullHit = cacheMisses.length === 0; @@ -160,15 +167,23 @@ export async function restoreLocalCache( return cacheMisses; } +async function resolveFrameworks(): Promise { + const detected = await framework.detectFrameworks(); + if (detected.length > 0) { + core.info(`Detected frameworks: ${detected.join(", ")}`); + } + return detected; +} + async function resolveCachePaths( - localCachePath: string + localCachePath: string, + manualPaths: string[], + cacheModes: string[], ): Promise { const paths: utils.CachePath[] = []; - const manual: string[] = core.getMultilineInput(Input_Path); - let cachesNodeModules = false; - for (const p of manual) { + for (const p of manualPaths) { paths.push({ mountTarget: p, framework: "custom" }); if (p.endsWith("/node_modules")) { @@ -183,7 +198,6 @@ See also https://namespace.so/docs/reference/github-actions/nscloud-cache-action ); } - const cacheModes: string[] = core.getMultilineInput(Input_Cache); let cachesXcode = false; for (const mode of cacheModes) { if (mode === ModeXcode) { From f2a81eef12b76fdfb0da265a02f2e0debf6cd28a Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:18:34 +0000 Subject: [PATCH 02/29] Initial test framework --- package-lock.json | 1451 ++++++++++++++++++++++++++++++++++++++++- package.json | 12 +- src/framework.test.ts | 7 + src/framework.ts | 2 +- 4 files changed, 1452 insertions(+), 20 deletions(-) create mode 100644 src/framework.test.ts diff --git a/package-lock.json b/package-lock.json index 797d184..bda02d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,10 +15,11 @@ "semver": "^7.6.3" }, "devDependencies": { - "@types/node": "^16.18.3", + "@types/node": "^24.9.1", "@vercel/ncc": "^0.38.1", - "ts-node": "^10.9.1", - "typescript": "^4.9.3" + "ts-node": "^10.9.2", + "typescript": "^4.9.3", + "vitest": "^4.0.3" } }, "node_modules/@actions/core": { @@ -64,6 +65,448 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@fastify/busboy": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", @@ -82,10 +525,11 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", @@ -97,6 +541,321 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -121,11 +880,40 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "16.18.58", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.58.tgz", - "integrity": "sha512-YGncyA25/MaVtQkjWW9r0EFBukZ+JulsLcVZBlGUfIb96OBMjkoRWwQo5IEWJ8Fj06Go3GHw+bjYDitv6BaGsA==", - "dev": true + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", + "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } }, "node_modules/@vercel/ncc": { "version": "0.38.1", @@ -136,6 +924,117 @@ "ncc": "dist/ncc/cli.js" } }, + "node_modules/@vitest/expect": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.3.tgz", + "integrity": "sha512-v3eSDx/bF25pzar6aEJrrdTXJduEBU3uSGXHslIdGIpJVP8tQQHV6x1ZfzbFQ/bLIomLSbR/2ZCfnaEGkWkiVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.3", + "@vitest/utils": "4.0.3", + "chai": "^6.0.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.3.tgz", + "integrity": "sha512-evZcRspIPbbiJEe748zI2BRu94ThCBE+RkjCpVF8yoVYuTV7hMe+4wLF/7K86r8GwJHSmAPnPbZhpXWWrg1qbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.3", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.19" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.3.tgz", + "integrity": "sha512-N7gly/DRXzxa9w9sbDXwD9QNFYP2hw90LLLGDobPNwiWgyW95GMxsCt29/COIKKh3P7XJICR38PSDePenMBtsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.3.tgz", + "integrity": "sha512-1/aK6fPM0lYXWyGKwop2Gbvz1plyTps/HDbIIJXYtJtspHjpXIeB3If07eWpVH4HW7Rmd3Rl+IS/+zEAXrRtXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.3", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.3.tgz", + "integrity": "sha512-amnYmvZ5MTjNCP1HZmdeczAPLRD6iOm9+2nMRUGxbe/6sQ0Ymur0NnR9LIrWS8JA3wKE71X25D6ya/3LN9YytA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.3", + "magic-string": "^0.30.19", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.3.tgz", + "integrity": "sha512-82vVL8Cqz7rbXaNUl35V2G7xeNMAjBdNOVaHbrzznT9BmiCiPOzhf0FhU3eP41nP1bLDm/5wWKZqkG4nyU95DQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.3.tgz", + "integrity": "sha512-qV6KJkq8W3piW6MDIbGOmn1xhvcW4DuA07alqaQ+vdx7YA49J85pnwnxigZVQFQw3tWnQNRKWwhz5wbP6iv/GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.3", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -163,12 +1062,50 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.0.tgz", + "integrity": "sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "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 }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -178,17 +1115,253 @@ "node": ">=0.3.1" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/json-multi-parse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/json-multi-parse/-/json-multi-parse-1.3.1.tgz", "integrity": "sha512-vqI0u3rcNivFN++ZIoAeLtV3pE83phfJ0Dwu+FOGXd4brhlt7CP5ZarnYcAdcIBUQfwq7NbEf9671U+xyhn7MA==" }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -201,11 +1374,84 @@ "node": ">=10" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "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" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "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", @@ -276,6 +1522,13 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -290,6 +1543,176 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "node_modules/vite": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.3.tgz", + "integrity": "sha512-IUSop8jgaT7w0g1yOM/35qVtKjr/8Va4PrjzH1OUb0YH4c3OXB2lCZDkMAB6glA8T5w8S164oJGsbcmAecr4sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.3", + "@vitest/mocker": "4.0.3", + "@vitest/pretty-format": "4.0.3", + "@vitest/runner": "4.0.3", + "@vitest/snapshot": "4.0.3", + "@vitest/spy": "4.0.3", + "@vitest/utils": "4.0.3", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.19", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.3", + "@vitest/browser-preview": "4.0.3", + "@vitest/browser-webdriverio": "4.0.3", + "@vitest/ui": "4.0.3", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 2655998..400e74c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "main": "dist/index.js", "type": "module", "scripts": { - "build": "ncc build -o dist/index src/index.ts && ncc build -o dist/post src/post.ts" + "build": "ncc build -o dist/index src/index.ts && ncc build -o dist/post src/post.ts", + "test": "vitest" }, "author": "Namespace Labs", "dependencies": { @@ -17,9 +18,10 @@ "semver": "^7.6.3" }, "devDependencies": { - "@types/node": "^16.18.3", + "@types/node": "^24.9.1", "@vercel/ncc": "^0.38.1", - "ts-node": "^10.9.1", - "typescript": "^4.9.3" + "ts-node": "^10.9.2", + "typescript": "^4.9.3", + "vitest": "^4.0.3" } -} \ No newline at end of file +} diff --git a/src/framework.test.ts b/src/framework.test.ts new file mode 100644 index 0000000..4b15add --- /dev/null +++ b/src/framework.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from 'vitest' +import { detectFrameworks } from "./framework"; + +test('detects frameworks correctly', async () => { + const detected = await detectFrameworks(); + expect(detected).toContain('node'); +}); diff --git a/src/framework.ts b/src/framework.ts index 5792cd8..be87adf 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -15,7 +15,7 @@ export async function detectFrameworks(): Promise { } async function detectGo(): Promise { - return null; + return []; } async function detectNode(): Promise { From 1c63bf92245d48808686c3040bbc028d1e48903d Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:18:50 +0000 Subject: [PATCH 03/29] Build --- dist/index/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/index/index.js b/dist/index/index.js index f70e648..a2cf51c 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29669,7 +29669,7 @@ async function detectFrameworks() { return detected; } async function detectGo() { - return null; + return []; } async function detectNode() { // TODO: From 0e99b19f7d75d6318aaa984b36ceb27821cf78dc Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:54:32 +0000 Subject: [PATCH 04/29] Use vitest as test runner --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 400e74c..c78677f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "type": "module", "scripts": { "build": "ncc build -o dist/index src/index.ts && ncc build -o dist/post src/post.ts", - "test": "vitest" + "test": "vitest run", + "test:watch": "vitest" }, "author": "Namespace Labs", "dependencies": { From bd2c5453a3303e0dda4b1c72c554ee57ab89b2db Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:55:11 +0000 Subject: [PATCH 05/29] Detect go, pnpm & yarn --- dist/index/index.js | 32 ++++++++++++++++++------- src/framework.test.ts | 21 +++++++++++++--- src/framework.ts | 41 ++++++++++++++++++++++++-------- src/testdata/go/mod/go.mod | 0 src/testdata/go/work/go.work | 0 src/testdata/pnpm/package.json | 0 src/testdata/pnpm/pnpm-lock.yaml | 0 src/testdata/yarn/package.json | 0 src/testdata/yarn/yarn.lock | 0 9 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 src/testdata/go/mod/go.mod create mode 100644 src/testdata/go/work/go.work create mode 100644 src/testdata/pnpm/package.json create mode 100644 src/testdata/pnpm/pnpm-lock.yaml create mode 100644 src/testdata/yarn/package.json create mode 100644 src/testdata/yarn/yarn.lock diff --git a/dist/index/index.js b/dist/index/index.js index a2cf51c..0533bfb 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29655,27 +29655,41 @@ function shouldUseSymlinks() { } ;// CONCATENATED MODULE: ./src/framework.ts -// TODO: tests -async function detectFrameworks() { + + +async function detectFrameworks(rootPath = './') { const detectors = [ detectGo, detectNode, ]; const detected = []; for (const detector of detectors) { - const result = await detector(); + const result = await detector(rootPath); detected.push(...result); } return detected; } -async function detectGo() { +async function detectGo(rootPath) { + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.mod'))) { + return ['go']; + } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.work'))) { + return ['go']; + } return []; } -async function detectNode() { - // TODO: - // - presence of package.json - // - get package manager (npm, pnpm, bun, yarn) - return ["node"]; +async function detectNode(rootPath) { + if (!external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'package.json'))) { + return []; + } + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'pnpm-lock.yaml'))) { + detected.push('pnpm'); + } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'yarn.lock'))) { + detected.push('yarn'); + } + return detected; } ;// CONCATENATED MODULE: ./src/index.ts diff --git a/src/framework.test.ts b/src/framework.test.ts index 4b15add..bf56a7f 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -1,7 +1,22 @@ import { expect, test } from 'vitest' import { detectFrameworks } from "./framework"; -test('detects frameworks correctly', async () => { - const detected = await detectFrameworks(); - expect(detected).toContain('node'); +test('detects go - mod', async () => { + const detected = await detectFrameworks('./src/testdata/go/mod'); + expect(detected).toContain('go'); +}); + +test('detects go - work', async () => { + const detected = await detectFrameworks('./src/testdata/go/work'); + expect(detected).toContain('go'); +}); + +test('detects pnpm', async () => { + const detected = await detectFrameworks('./src/testdata/pnpm'); + expect(detected).toContain('pnpm'); +}); + +test('detects yarn', async () => { + const detected = await detectFrameworks('./src/testdata/yarn'); + expect(detected).toContain('yarn'); }); diff --git a/src/framework.ts b/src/framework.ts index be87adf..afd24c4 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -1,26 +1,47 @@ -// TODO: tests -export async function detectFrameworks(): Promise { - const detectors: Array<() => Promise> = [ +import * as fs from "node:fs"; +import * as path from "node:path"; + +export async function detectFrameworks(rootPath = './'): Promise { + const detectors: Array<(rootPath: string) => Promise> = [ detectGo, detectNode, ]; const detected: string[] = []; for (const detector of detectors) { - const result = await detector(); + const result = await detector(rootPath); detected.push(...result); } return detected; } -async function detectGo(): Promise { +async function detectGo(rootPath: string): Promise { + if (fs.existsSync(path.join(rootPath, 'go.mod'))) { + return ['go']; + } + + if (fs.existsSync(path.join(rootPath, 'go.work'))) { + return ['go']; + } + return []; } -async function detectNode(): Promise { - // TODO: - // - presence of package.json - // - get package manager (npm, pnpm, bun, yarn) - return ["node"]; +async function detectNode(rootPath: string): Promise { + if (!fs.existsSync(path.join(rootPath, 'package.json'))) { + return []; + } + + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'pnpm-lock.yaml'))) { + detected.push('pnpm'); + } + + if (fs.existsSync(path.join(rootPath, 'yarn.lock'))) { + detected.push('yarn'); + } + + return detected; } diff --git a/src/testdata/go/mod/go.mod b/src/testdata/go/mod/go.mod new file mode 100644 index 0000000..e69de29 diff --git a/src/testdata/go/work/go.work b/src/testdata/go/work/go.work new file mode 100644 index 0000000..e69de29 diff --git a/src/testdata/pnpm/package.json b/src/testdata/pnpm/package.json new file mode 100644 index 0000000..e69de29 diff --git a/src/testdata/pnpm/pnpm-lock.yaml b/src/testdata/pnpm/pnpm-lock.yaml new file mode 100644 index 0000000..e69de29 diff --git a/src/testdata/yarn/package.json b/src/testdata/yarn/package.json new file mode 100644 index 0000000..e69de29 diff --git a/src/testdata/yarn/yarn.lock b/src/testdata/yarn/yarn.lock new file mode 100644 index 0000000..e69de29 From 88e0fac641be0c329bc1693f7352632b9b7f5f8b Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:56:44 +0000 Subject: [PATCH 06/29] vitest is a better default --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index c78677f..400e74c 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,7 @@ "type": "module", "scripts": { "build": "ncc build -o dist/index src/index.ts && ncc build -o dist/post src/post.ts", - "test": "vitest run", - "test:watch": "vitest" + "test": "vitest" }, "author": "Namespace Labs", "dependencies": { From 642d8eaa78693f7e805cef2d65a7019b1971672f Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 09:59:24 +0000 Subject: [PATCH 07/29] Not currently used, remove for now --- .github/workflows/auto.yaml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/auto.yaml diff --git a/.github/workflows/auto.yaml b/.github/workflows/auto.yaml deleted file mode 100644 index f8cd602..0000000 --- a/.github/workflows/auto.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Test auto-framework caching -on: - push: - branches: - - main - pull_request: - branches: - - "*" - schedule: - - cron: "10 */6 * * *" - workflow_dispatch: - - # Allow check to run on the merge queue so that it is eligible to be a "required" check. - merge_group: - types: [checks_requested] - -permissions: - contents: read - -jobs: - node: - runs-on: - - namespace-profile-e2e-small - steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Setup cache - uses: ./ # Uses an action in the root directory - # with intentionally left blank to use framework detection - - - name: Run npm install - run: npm install From b958168406748daa499d1c70bf0d1fa96460c4bd Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:06:15 +0000 Subject: [PATCH 08/29] Detect php - composer --- src/framework.test.ts | 9 +++++++-- src/framework.ts | 9 +++++++++ src/testdata/composer/composer.json | 0 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/testdata/composer/composer.json diff --git a/src/framework.test.ts b/src/framework.test.ts index bf56a7f..e1108a9 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -1,12 +1,17 @@ import { expect, test } from 'vitest' import { detectFrameworks } from "./framework"; -test('detects go - mod', async () => { +test('detects composer', async () => { + const detected = await detectFrameworks('./src/testdata/composer'); + expect(detected).toContain('composer'); +}); + +test('detects go.mod', async () => { const detected = await detectFrameworks('./src/testdata/go/mod'); expect(detected).toContain('go'); }); -test('detects go - work', async () => { +test('detects go.work', async () => { const detected = await detectFrameworks('./src/testdata/go/work'); expect(detected).toContain('go'); }); diff --git a/src/framework.ts b/src/framework.ts index afd24c4..46aeb92 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -5,6 +5,7 @@ export async function detectFrameworks(rootPath = './'): Promise { const detectors: Array<(rootPath: string) => Promise> = [ detectGo, detectNode, + detectPhp, ]; const detected: string[] = []; @@ -45,3 +46,11 @@ async function detectNode(rootPath: string): Promise { return detected; } + +async function detectPhp(rootPath: string): Promise { + if (fs.existsSync(path.join(rootPath, 'composer.json'))) { + return ['composer']; + } + + return []; +} diff --git a/src/testdata/composer/composer.json b/src/testdata/composer/composer.json new file mode 100644 index 0000000..e69de29 From 0d67621a97d2f9dcb6935d46dbbce43e08b10fd6 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:17:10 +0000 Subject: [PATCH 09/29] Detect python frameworks poetry & uv --- dist/index/index.js | 7 ++++ src/framework.test.ts | 28 +++++++++------- src/framework.ts | 33 ++++++++++++++----- src/testdata/{pnpm => node}/package.json | 0 src/testdata/{pnpm => node}/pnpm-lock.yaml | 0 src/testdata/{yarn => node}/yarn.lock | 0 src/testdata/{composer => php}/composer.json | 0 .../{yarn/package.json => python/poetry.lock} | 0 src/testdata/python/uv.lock | 0 9 files changed, 48 insertions(+), 20 deletions(-) rename src/testdata/{pnpm => node}/package.json (100%) rename src/testdata/{pnpm => node}/pnpm-lock.yaml (100%) rename src/testdata/{yarn => node}/yarn.lock (100%) rename src/testdata/{composer => php}/composer.json (100%) rename src/testdata/{yarn/package.json => python/poetry.lock} (100%) create mode 100644 src/testdata/python/uv.lock diff --git a/dist/index/index.js b/dist/index/index.js index 0533bfb..6dc8391 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29661,6 +29661,7 @@ async function detectFrameworks(rootPath = './') { const detectors = [ detectGo, detectNode, + detectPhp, ]; const detected = []; for (const detector of detectors) { @@ -29691,6 +29692,12 @@ async function detectNode(rootPath) { } return detected; } +async function detectPhp(rootPath) { + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'composer.json'))) { + return ['composer']; + } + return []; +} ;// CONCATENATED MODULE: ./src/index.ts diff --git a/src/framework.test.ts b/src/framework.test.ts index e1108a9..cf57d85 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -1,27 +1,33 @@ import { expect, test } from 'vitest' import { detectFrameworks } from "./framework"; -test('detects composer', async () => { - const detected = await detectFrameworks('./src/testdata/composer'); - expect(detected).toContain('composer'); -}); - -test('detects go.mod', async () => { +test('detects go - mod', async () => { const detected = await detectFrameworks('./src/testdata/go/mod'); expect(detected).toContain('go'); }); -test('detects go.work', async () => { +test('detects go - work', async () => { const detected = await detectFrameworks('./src/testdata/go/work'); expect(detected).toContain('go'); }); -test('detects pnpm', async () => { - const detected = await detectFrameworks('./src/testdata/pnpm'); +test('detects node', async () => { + const detected = await detectFrameworks('./src/testdata/node'); expect(detected).toContain('pnpm'); }); -test('detects yarn', async () => { - const detected = await detectFrameworks('./src/testdata/yarn'); +test('detects node', async () => { + const detected = await detectFrameworks('./src/testdata/node'); expect(detected).toContain('yarn'); }); + +test('detects php', async () => { + const detected = await detectFrameworks('./src/testdata/php'); + expect(detected).toContain('composer'); +}); + +test('detects python', async () => { + const detected = await detectFrameworks('./src/testdata/python'); + expect(detected).toContain('poetry'); + expect(detected).toContain('uv'); +}); diff --git a/src/framework.ts b/src/framework.ts index 46aeb92..6206a5a 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -6,6 +6,7 @@ export async function detectFrameworks(rootPath = './'): Promise { detectGo, detectNode, detectPhp, + detectPython, ]; const detected: string[] = []; @@ -18,22 +19,20 @@ export async function detectFrameworks(rootPath = './'): Promise { } async function detectGo(rootPath: string): Promise { + let detected: string[] = []; + if (fs.existsSync(path.join(rootPath, 'go.mod'))) { - return ['go']; + detected.push('go'); } if (fs.existsSync(path.join(rootPath, 'go.work'))) { - return ['go']; + detected.push('go'); } - return []; + return detected; } async function detectNode(rootPath: string): Promise { - if (!fs.existsSync(path.join(rootPath, 'package.json'))) { - return []; - } - let detected: string[] = []; if (fs.existsSync(path.join(rootPath, 'pnpm-lock.yaml'))) { @@ -48,9 +47,25 @@ async function detectNode(rootPath: string): Promise { } async function detectPhp(rootPath: string): Promise { + let detected: string[] = []; + if (fs.existsSync(path.join(rootPath, 'composer.json'))) { - return ['composer']; + detected.push('composer'); } - return []; + return detected; +} + +async function detectPython(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'poetry.lock'))) { + detected.push('poetry'); + } + + if (fs.existsSync(path.join(rootPath, 'uv.lock'))) { + detected.push('uv'); + } + + return detected; } diff --git a/src/testdata/pnpm/package.json b/src/testdata/node/package.json similarity index 100% rename from src/testdata/pnpm/package.json rename to src/testdata/node/package.json diff --git a/src/testdata/pnpm/pnpm-lock.yaml b/src/testdata/node/pnpm-lock.yaml similarity index 100% rename from src/testdata/pnpm/pnpm-lock.yaml rename to src/testdata/node/pnpm-lock.yaml diff --git a/src/testdata/yarn/yarn.lock b/src/testdata/node/yarn.lock similarity index 100% rename from src/testdata/yarn/yarn.lock rename to src/testdata/node/yarn.lock diff --git a/src/testdata/composer/composer.json b/src/testdata/php/composer.json similarity index 100% rename from src/testdata/composer/composer.json rename to src/testdata/php/composer.json diff --git a/src/testdata/yarn/package.json b/src/testdata/python/poetry.lock similarity index 100% rename from src/testdata/yarn/package.json rename to src/testdata/python/poetry.lock diff --git a/src/testdata/python/uv.lock b/src/testdata/python/uv.lock new file mode 100644 index 0000000..e69de29 From 95838a98324dd0f572650af47b1ecd82b79bf6db Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:19:28 +0000 Subject: [PATCH 10/29] Detect ruby --- dist/index/index.js | 34 ++++++++++++++++++++++++++-------- src/framework.test.ts | 9 +++++---- src/framework.ts | 11 +++++++++++ src/testdata/ruby/Gemfile | 0 4 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/testdata/ruby/Gemfile diff --git a/dist/index/index.js b/dist/index/index.js index 6dc8391..8233bda 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29662,6 +29662,8 @@ async function detectFrameworks(rootPath = './') { detectGo, detectNode, detectPhp, + detectPython, + detectRuby, ]; const detected = []; for (const detector of detectors) { @@ -29671,18 +29673,16 @@ async function detectFrameworks(rootPath = './') { return detected; } async function detectGo(rootPath) { + let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.mod'))) { - return ['go']; + detected.push('go'); } if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.work'))) { - return ['go']; + detected.push('go'); } - return []; + return detected; } async function detectNode(rootPath) { - if (!external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'package.json'))) { - return []; - } let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'pnpm-lock.yaml'))) { detected.push('pnpm'); @@ -29693,10 +29693,28 @@ async function detectNode(rootPath) { return detected; } async function detectPhp(rootPath) { + let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'composer.json'))) { - return ['composer']; + detected.push('composer'); + } + return detected; +} +async function detectPython(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'poetry.lock'))) { + detected.push('poetry'); + } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'uv.lock'))) { + detected.push('uv'); } - return []; + return detected; +} +async function detectRuby(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Gemfile'))) { + detected.push('ruby'); + } + return detected; } ;// CONCATENATED MODULE: ./src/index.ts diff --git a/src/framework.test.ts b/src/framework.test.ts index cf57d85..47a16ba 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -14,10 +14,6 @@ test('detects go - work', async () => { test('detects node', async () => { const detected = await detectFrameworks('./src/testdata/node'); expect(detected).toContain('pnpm'); -}); - -test('detects node', async () => { - const detected = await detectFrameworks('./src/testdata/node'); expect(detected).toContain('yarn'); }); @@ -31,3 +27,8 @@ test('detects python', async () => { expect(detected).toContain('poetry'); expect(detected).toContain('uv'); }); + +test('detects ruby', async () => { + const detected = await detectFrameworks('./src/testdata/ruby'); + expect(detected).toContain('ruby'); +}); diff --git a/src/framework.ts b/src/framework.ts index 6206a5a..34c5976 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -7,6 +7,7 @@ export async function detectFrameworks(rootPath = './'): Promise { detectNode, detectPhp, detectPython, + detectRuby, ]; const detected: string[] = []; @@ -69,3 +70,13 @@ async function detectPython(rootPath: string): Promise { return detected; } + +async function detectRuby(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'Gemfile'))) { + detected.push('ruby'); + } + + return detected; +} diff --git a/src/testdata/ruby/Gemfile b/src/testdata/ruby/Gemfile new file mode 100644 index 0000000..e69de29 From 15ed90e3fb75f836699f31abdd799ce496ba76f0 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:20:50 +0000 Subject: [PATCH 11/29] Detect rust --- src/framework.test.ts | 5 +++++ src/framework.ts | 11 +++++++++++ src/testdata/rust/Cargo.toml | 0 3 files changed, 16 insertions(+) create mode 100644 src/testdata/rust/Cargo.toml diff --git a/src/framework.test.ts b/src/framework.test.ts index 47a16ba..561390e 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -32,3 +32,8 @@ test('detects ruby', async () => { const detected = await detectFrameworks('./src/testdata/ruby'); expect(detected).toContain('ruby'); }); + +test('detects rust', async () => { + const detected = await detectFrameworks('./src/testdata/rust'); + expect(detected).toContain('rust'); +}); diff --git a/src/framework.ts b/src/framework.ts index 34c5976..cc54517 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -8,6 +8,7 @@ export async function detectFrameworks(rootPath = './'): Promise { detectPhp, detectPython, detectRuby, + detectRust, ]; const detected: string[] = []; @@ -80,3 +81,13 @@ async function detectRuby(rootPath: string): Promise { return detected; } + +async function detectRust(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'Cargo.toml'))) { + detected.push('rust'); + } + + return detected; +} diff --git a/src/testdata/rust/Cargo.toml b/src/testdata/rust/Cargo.toml new file mode 100644 index 0000000..e69de29 From 43bf322d99917afb73c83babd828075e3d140544 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:26:48 +0000 Subject: [PATCH 12/29] Detect gradle --- dist/index/index.js | 16 ++++++++++++++++ src/framework.test.ts | 5 +++++ src/framework.ts | 11 +++++++++++ src/testdata/java/gradlew | 0 4 files changed, 32 insertions(+) create mode 100644 src/testdata/java/gradlew diff --git a/dist/index/index.js b/dist/index/index.js index 8233bda..444c418 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29660,10 +29660,12 @@ function shouldUseSymlinks() { async function detectFrameworks(rootPath = './') { const detectors = [ detectGo, + detectJava, detectNode, detectPhp, detectPython, detectRuby, + detectRust, ]; const detected = []; for (const detector of detectors) { @@ -29682,6 +29684,13 @@ async function detectGo(rootPath) { } return detected; } +async function detectJava(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'gradlew'))) { + detected.push('gradle'); + } + return detected; +} async function detectNode(rootPath) { let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'pnpm-lock.yaml'))) { @@ -29716,6 +29725,13 @@ async function detectRuby(rootPath) { } return detected; } +async function detectRust(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Cargo.toml'))) { + detected.push('rust'); + } + return detected; +} ;// CONCATENATED MODULE: ./src/index.ts diff --git a/src/framework.test.ts b/src/framework.test.ts index 561390e..5e1faac 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -11,6 +11,11 @@ test('detects go - work', async () => { expect(detected).toContain('go'); }); +test('detects java', async () => { + const detected = await detectFrameworks('./src/testdata/java'); + expect(detected).toContain('gradle'); +}); + test('detects node', async () => { const detected = await detectFrameworks('./src/testdata/node'); expect(detected).toContain('pnpm'); diff --git a/src/framework.ts b/src/framework.ts index cc54517..da140aa 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -4,6 +4,7 @@ import * as path from "node:path"; export async function detectFrameworks(rootPath = './'): Promise { const detectors: Array<(rootPath: string) => Promise> = [ detectGo, + detectJava, detectNode, detectPhp, detectPython, @@ -34,6 +35,16 @@ async function detectGo(rootPath: string): Promise { return detected; } +async function detectJava(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'gradlew'))) { + detected.push('gradle'); + } + + return detected; +} + async function detectNode(rootPath: string): Promise { let detected: string[] = []; diff --git a/src/testdata/java/gradlew b/src/testdata/java/gradlew new file mode 100644 index 0000000..e69de29 From b70cff52e35b323f90ba750d9571354353666241 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:31:49 +0000 Subject: [PATCH 13/29] Detect homebrew --- dist/index/index.js | 9 +++++++++ src/framework.test.ts | 5 +++++ src/framework.ts | 13 +++++++++++++ src/testdata/homebrew/Brewfile | 0 4 files changed, 27 insertions(+) create mode 100644 src/testdata/homebrew/Brewfile diff --git a/dist/index/index.js b/dist/index/index.js index 444c418..5a8d1b0 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29660,6 +29660,7 @@ function shouldUseSymlinks() { async function detectFrameworks(rootPath = './') { const detectors = [ detectGo, + detectHomebrew, detectJava, detectNode, detectPhp, @@ -29684,8 +29685,16 @@ async function detectGo(rootPath) { } return detected; } +async function detectHomebrew(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Brewfile'))) { + detected.push('brew'); + } + return detected; +} async function detectJava(rootPath) { let detected = []; + // TODO: support maven if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'gradlew'))) { detected.push('gradle'); } diff --git a/src/framework.test.ts b/src/framework.test.ts index 5e1faac..05e16ea 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -11,6 +11,11 @@ test('detects go - work', async () => { expect(detected).toContain('go'); }); +test('detects homebrew', async () => { + const detected = await detectFrameworks('./src/testdata/homebrew'); + expect(detected).toContain('brew'); +}); + test('detects java', async () => { const detected = await detectFrameworks('./src/testdata/java'); expect(detected).toContain('gradle'); diff --git a/src/framework.ts b/src/framework.ts index da140aa..80cee6b 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -4,6 +4,7 @@ import * as path from "node:path"; export async function detectFrameworks(rootPath = './'): Promise { const detectors: Array<(rootPath: string) => Promise> = [ detectGo, + detectHomebrew, detectJava, detectNode, detectPhp, @@ -35,9 +36,21 @@ async function detectGo(rootPath: string): Promise { return detected; } +async function detectHomebrew(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'Brewfile'))) { + detected.push('brew'); + } + + return detected; +} + async function detectJava(rootPath: string): Promise { let detected: string[] = []; + // TODO: support maven + if (fs.existsSync(path.join(rootPath, 'gradlew'))) { detected.push('gradle'); } diff --git a/src/testdata/homebrew/Brewfile b/src/testdata/homebrew/Brewfile new file mode 100644 index 0000000..e69de29 From 2a539c4d466bb282d20c0fc8a249cca42fdbae98 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:33:20 +0000 Subject: [PATCH 14/29] Detect cocoapods --- dist/index/index.js | 9 +++++++++ src/framework.test.ts | 5 +++++ src/framework.ts | 13 +++++++++++++ src/testdata/xcode/Podfile | 0 4 files changed, 27 insertions(+) create mode 100644 src/testdata/xcode/Podfile diff --git a/dist/index/index.js b/dist/index/index.js index 5a8d1b0..32ffd8f 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29667,6 +29667,7 @@ async function detectFrameworks(rootPath = './') { detectPython, detectRuby, detectRust, + detectXcode, ]; const detected = []; for (const detector of detectors) { @@ -29741,6 +29742,14 @@ async function detectRust(rootPath) { } return detected; } +async function detectXcode(rootPath) { + let detected = []; + // TODO: support xcode & swiftpm + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Podfile'))) { + detected.push('cocoapods'); + } + return detected; +} ;// CONCATENATED MODULE: ./src/index.ts diff --git a/src/framework.test.ts b/src/framework.test.ts index 05e16ea..7a89891 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -47,3 +47,8 @@ test('detects rust', async () => { const detected = await detectFrameworks('./src/testdata/rust'); expect(detected).toContain('rust'); }); + +test('detects xcode', async () => { + const detected = await detectFrameworks('./src/testdata/xcode'); + expect(detected).toContain('cocoapods'); +}); diff --git a/src/framework.ts b/src/framework.ts index 80cee6b..f2f87d0 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -11,6 +11,7 @@ export async function detectFrameworks(rootPath = './'): Promise { detectPython, detectRuby, detectRust, + detectXcode, ]; const detected: string[] = []; @@ -115,3 +116,15 @@ async function detectRust(rootPath: string): Promise { return detected; } + +async function detectXcode(rootPath: string): Promise { + let detected: string[] = []; + + // TODO: support xcode & swiftpm + + if (fs.existsSync(path.join(rootPath, 'Podfile'))) { + detected.push('cocoapods'); + } + + return detected; +} diff --git a/src/testdata/xcode/Podfile b/src/testdata/xcode/Podfile new file mode 100644 index 0000000..e69de29 From 2af6947b82cc430b6d0bfb0463a79acfe8ece729 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:36:11 +0000 Subject: [PATCH 15/29] Able to detect even with paths --- dist/index/index.js | 2 +- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/index/index.js b/dist/index/index.js index 32ffd8f..cfceaa3 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29816,7 +29816,7 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const useSymlinks = shouldUseSymlinks(); const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); - if (manualPaths.length === 0 && cacheModes.length === 0) { + if ((manualPaths.length === 0 && cacheModes.length === 0) || (cacheModes.length === 1 && cacheModes[0] === "auto")) { cacheModes = await resolveFrameworks(); } const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); diff --git a/src/index.ts b/src/index.ts index eea8bcd..2222a92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,7 +70,7 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); - if (manualPaths.length === 0 && cacheModes.length === 0) { + if ((manualPaths.length === 0 && cacheModes.length === 0) || (cacheModes.length === 1 && cacheModes[0] === "auto")) { cacheModes = await resolveFrameworks(); } From 08ddfda52de4843b18b1eea50ff8bc90c90aad4f Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 10:40:23 +0000 Subject: [PATCH 16/29] Renable auto framework workflow --- .github/workflows/auto.yaml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/auto.yaml diff --git a/.github/workflows/auto.yaml b/.github/workflows/auto.yaml new file mode 100644 index 0000000..ff637d6 --- /dev/null +++ b/.github/workflows/auto.yaml @@ -0,0 +1,34 @@ +name: Test auto-framework caching +on: + push: + branches: + - main + pull_request: + branches: + - "*" + schedule: + - cron: "10 */6 * * *" + workflow_dispatch: + + # Allow check to run on the merge queue so that it is eligible to be a "required" check. + merge_group: + types: [checks_requested] + +permissions: + contents: read + +jobs: + detection: + runs-on: + - namespace-profile-e2e-small + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Trigger various framework detections + run: | + touch Cargo.toml go.mod + + - name: Setup cache + uses: ./ # Uses an action in the root directory + # with intentionally left blank to use framework detection From 7f662228049c6c8bd6f084ac21d425414da31573 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 11:12:51 +0000 Subject: [PATCH 17/29] Force auto detection --- .github/workflows/auto.yaml | 41 ++++++++++++++++++++++++++++++++++--- action.yml | 3 +++ dist/index/index.js | 4 +++- src/index.ts | 4 +++- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/auto.yaml b/.github/workflows/auto.yaml index ff637d6..664e331 100644 --- a/.github/workflows/auto.yaml +++ b/.github/workflows/auto.yaml @@ -1,4 +1,4 @@ -name: Test auto-framework caching +name: Test auto-framework detection on: push: branches: @@ -18,7 +18,7 @@ permissions: contents: read jobs: - detection: + skipped: runs-on: - namespace-profile-e2e-small steps: @@ -31,4 +31,39 @@ jobs: - name: Setup cache uses: ./ # Uses an action in the root directory - # with intentionally left blank to use framework detection + with: + path: | + ./detect-skipped-tmp + + force: + runs-on: + - namespace-profile-e2e-small + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Trigger various framework detections + run: | + touch Cargo.toml go.mod + + - name: Setup cache + uses: ./ # Uses an action in the root directory + with: + detect: true + path: | + ./detect-force-tmp + + no-config: + runs-on: + - namespace-profile-e2e-small + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Trigger various framework detections + run: | + touch Cargo.toml go.mod + + - name: Setup cache + uses: ./ # Uses an action in the root directory + # `with` intentionally left blank to use framework detection diff --git a/action.yml b/action.yml index f9c6cc8..cdd5186 100644 --- a/action.yml +++ b/action.yml @@ -2,6 +2,9 @@ name: "Cache" description: "Cache artifacts like dependencies and build outputs to a Namespace Cloud cache volume to improve workflow execution time." author: "Namespace Labs" inputs: + detect: + description: "Automatically detect frameworks to cache based on files in the repository. If 'path' or 'cache' inputs are not provided, this is set to true." + required: false path: description: "A list of files, directories, and wildcard patterns to cache and restore" required: false diff --git a/dist/index/index.js b/dist/index/index.js index cfceaa3..1f3fda9 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29761,6 +29761,7 @@ async function detectXcode(rootPath) { const Input_Key = "key"; // unused +const Input_Detect = "detect"; const Input_Path = "path"; const Input_Cache = "cache"; const Input_FailOnCacheMiss = "fail-on-cache-miss"; @@ -29814,9 +29815,10 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi } core.info(`Found Namespace cross-invocation cache at ${localCachePath}.`); const useSymlinks = shouldUseSymlinks(); + const autoDetect = core.getBooleanInput(Input_Detect); const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); - if ((manualPaths.length === 0 && cacheModes.length === 0) || (cacheModes.length === 1 && cacheModes[0] === "auto")) { + if (autoDetect || (manualPaths.length === 0 && cacheModes.length === 0)) { cacheModes = await resolveFrameworks(); } const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); diff --git a/src/index.ts b/src/index.ts index 2222a92..e2edc48 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import * as utils from "./utils"; import * as framework from "./framework"; const Input_Key = "key"; // unused +const Input_Detect = "detect"; const Input_Path = "path"; const Input_Cache = "cache"; const Input_FailOnCacheMiss = "fail-on-cache-miss"; @@ -68,9 +69,10 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const useSymlinks = utils.shouldUseSymlinks(); + const autoDetect = core.getBooleanInput(Input_Detect); const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); - if ((manualPaths.length === 0 && cacheModes.length === 0) || (cacheModes.length === 1 && cacheModes[0] === "auto")) { + if (autoDetect || (manualPaths.length === 0 && cacheModes.length === 0)) { cacheModes = await resolveFrameworks(); } From 334135b912aa116bce4ab7ab10cb2f0695acb605 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 11:16:40 +0000 Subject: [PATCH 18/29] Fix yaml schema issue --- action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/action.yml b/action.yml index cdd5186..e2710f0 100644 --- a/action.yml +++ b/action.yml @@ -4,6 +4,7 @@ author: "Namespace Labs" inputs: detect: description: "Automatically detect frameworks to cache based on files in the repository. If 'path' or 'cache' inputs are not provided, this is set to true." + default: "false" required: false path: description: "A list of files, directories, and wildcard patterns to cache and restore" From 32c3bd6021e778dfab96fa7491dd905c0e6151fd Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 11:39:40 +0000 Subject: [PATCH 19/29] Run tests --- .github/workflows/test.yaml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..9fa70bb --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,29 @@ +on: + push: + branches: + - main + + pull_request: + branches: + - "*" + + merge_group: + types: [checks_requested] + +name: CI +jobs: + test: + runs-on: + - namespace-profile-e2e-small + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup cache + uses: ./ # Uses an action in the root directory + + - name: Install dependencies + run: npm install + + - name: Run tests + run: npm test From 5156d737860b04b1a08e794cb270bd102bbc056a Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 12:22:36 +0000 Subject: [PATCH 20/29] Mock filesystem --- __mocks__/fs.cjs | 2 + package-lock.json | 208 +++++++++++++++++++++++++++++++ package.json | 1 + src/framework.test.ts | 71 +++++++++-- src/testdata/go/mod/go.mod | 0 src/testdata/go/work/go.work | 0 src/testdata/homebrew/Brewfile | 0 src/testdata/java/gradlew | 0 src/testdata/node/package.json | 0 src/testdata/node/pnpm-lock.yaml | 0 src/testdata/node/yarn.lock | 0 src/testdata/php/composer.json | 0 src/testdata/python/poetry.lock | 0 src/testdata/python/uv.lock | 0 src/testdata/ruby/Gemfile | 0 src/testdata/rust/Cargo.toml | 0 src/testdata/xcode/Podfile | 0 17 files changed, 271 insertions(+), 11 deletions(-) create mode 100644 __mocks__/fs.cjs delete mode 100644 src/testdata/go/mod/go.mod delete mode 100644 src/testdata/go/work/go.work delete mode 100644 src/testdata/homebrew/Brewfile delete mode 100644 src/testdata/java/gradlew delete mode 100644 src/testdata/node/package.json delete mode 100644 src/testdata/node/pnpm-lock.yaml delete mode 100644 src/testdata/node/yarn.lock delete mode 100644 src/testdata/php/composer.json delete mode 100644 src/testdata/python/poetry.lock delete mode 100644 src/testdata/python/uv.lock delete mode 100644 src/testdata/ruby/Gemfile delete mode 100644 src/testdata/rust/Cargo.toml delete mode 100644 src/testdata/xcode/Podfile diff --git a/__mocks__/fs.cjs b/__mocks__/fs.cjs new file mode 100644 index 0000000..f9a27d9 --- /dev/null +++ b/__mocks__/fs.cjs @@ -0,0 +1,2 @@ +const { fs } = require('memfs') +module.exports = fs diff --git a/package-lock.json b/package-lock.json index bda02d0..c5206e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "devDependencies": { "@types/node": "^24.9.1", "@vercel/ncc": "^0.38.1", + "memfs": "^4.49.0", "ts-node": "^10.9.2", "typescript": "^4.9.3", "vitest": "^4.0.3" @@ -541,6 +542,126 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.52.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", @@ -1217,6 +1338,33 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, "node_modules/json-multi-parse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/json-multi-parse/-/json-multi-parse-1.3.1.tgz", @@ -1238,6 +1386,25 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/memfs": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.49.0.tgz", + "integrity": "sha512-L9uC9vGuc4xFybbdOpRLoOAOq1YEBBsocCs5NVW32DfU+CZWWIn3OVF+lB8Gp4ttBVSMazwrTrjv8ussX/e3VQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1405,6 +1572,23 @@ "dev": true, "license": "MIT" }, + "node_modules/thingies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", + "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -1446,6 +1630,23 @@ "node": ">=14.0.0" } }, + "node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -1490,6 +1691,13 @@ } } }, + "node_modules/tslib": { + "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/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/package.json b/package.json index 400e74c..5ce6a9d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "devDependencies": { "@types/node": "^24.9.1", "@vercel/ncc": "^0.38.1", + "memfs": "^4.49.0", "ts-node": "^10.9.2", "typescript": "^4.9.3", "vitest": "^4.0.3" diff --git a/src/framework.test.ts b/src/framework.test.ts index 7a89891..d99ea7a 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -1,54 +1,103 @@ -import { expect, test } from 'vitest' +import { beforeEach, expect, test, vi } from 'vitest'; +import { vol } from 'memfs'; import { detectFrameworks } from "./framework"; +vi.mock('node:fs'); + +beforeEach(() => { + vol.reset(); // reset the state of in-memory fs +}); + test('detects go - mod', async () => { - const detected = await detectFrameworks('./src/testdata/go/mod'); + vol.fromJSON({ + 'mockdata/go.mod': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('go'); }); test('detects go - work', async () => { - const detected = await detectFrameworks('./src/testdata/go/work'); + vol.fromJSON({ + 'mockdata/go.work': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('go'); }); test('detects homebrew', async () => { - const detected = await detectFrameworks('./src/testdata/homebrew'); + vol.fromJSON({ + 'mockdata/Brewfile': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('brew'); }); test('detects java', async () => { - const detected = await detectFrameworks('./src/testdata/java'); + vol.fromJSON({ + 'mockdata/gradlew': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('gradle'); }); test('detects node', async () => { - const detected = await detectFrameworks('./src/testdata/node'); + vol.fromJSON({ + 'mockdata/pnpm-lock.yaml': '', + 'mockdata/yarn.lock': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('pnpm'); expect(detected).toContain('yarn'); }); test('detects php', async () => { - const detected = await detectFrameworks('./src/testdata/php'); + vol.fromJSON({ + 'mockdata/composer.json': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('composer'); }); test('detects python', async () => { - const detected = await detectFrameworks('./src/testdata/python'); + vol.fromJSON({ + 'mockdata/poetry.lock': '', + 'mockdata/uv.lock': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('poetry'); expect(detected).toContain('uv'); }); test('detects ruby', async () => { - const detected = await detectFrameworks('./src/testdata/ruby'); + vol.fromJSON({ + 'mockdata/Gemfile': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('ruby'); }); test('detects rust', async () => { - const detected = await detectFrameworks('./src/testdata/rust'); + vol.fromJSON({ + 'mockdata/Cargo.toml': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('rust'); }); test('detects xcode', async () => { - const detected = await detectFrameworks('./src/testdata/xcode'); + vol.fromJSON({ + 'mockdata/Podfile': '', + }); + + const detected = await detectFrameworks('mockdata'); expect(detected).toContain('cocoapods'); }); diff --git a/src/testdata/go/mod/go.mod b/src/testdata/go/mod/go.mod deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/go/work/go.work b/src/testdata/go/work/go.work deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/homebrew/Brewfile b/src/testdata/homebrew/Brewfile deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/java/gradlew b/src/testdata/java/gradlew deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/node/package.json b/src/testdata/node/package.json deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/node/pnpm-lock.yaml b/src/testdata/node/pnpm-lock.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/node/yarn.lock b/src/testdata/node/yarn.lock deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/php/composer.json b/src/testdata/php/composer.json deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/python/poetry.lock b/src/testdata/python/poetry.lock deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/python/uv.lock b/src/testdata/python/uv.lock deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/ruby/Gemfile b/src/testdata/ruby/Gemfile deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/rust/Cargo.toml b/src/testdata/rust/Cargo.toml deleted file mode 100644 index e69de29..0000000 diff --git a/src/testdata/xcode/Podfile b/src/testdata/xcode/Podfile deleted file mode 100644 index e69de29..0000000 From fa3bf18081b868dd3bdfa24d6fda5371a53486e9 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 12:45:05 +0000 Subject: [PATCH 21/29] Detect maven --- src/framework.test.ts | 2 ++ src/framework.ts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/framework.test.ts b/src/framework.test.ts index d99ea7a..52f679d 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -38,10 +38,12 @@ test('detects homebrew', async () => { test('detects java', async () => { vol.fromJSON({ 'mockdata/gradlew': '', + 'mockdata/pom.xml': '', }); const detected = await detectFrameworks('mockdata'); expect(detected).toContain('gradle'); + expect(detected).toContain('maven'); }); test('detects node', async () => { diff --git a/src/framework.ts b/src/framework.ts index f2f87d0..3c345c2 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -50,12 +50,14 @@ async function detectHomebrew(rootPath: string): Promise { async function detectJava(rootPath: string): Promise { let detected: string[] = []; - // TODO: support maven - if (fs.existsSync(path.join(rootPath, 'gradlew'))) { detected.push('gradle'); } + if (fs.existsSync(path.join(rootPath, 'pom.xml'))) { + detected.push('maven'); + } + return detected; } From 8d87620b3024125739695713fe7d96550f3aba1b Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 15:00:47 +0000 Subject: [PATCH 22/29] Detect swiftpm --- src/framework.test.ts | 2 ++ src/framework.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/framework.test.ts b/src/framework.test.ts index 52f679d..8357c37 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -97,9 +97,11 @@ test('detects rust', async () => { test('detects xcode', async () => { vol.fromJSON({ + 'mockdata/Package.swift': '', 'mockdata/Podfile': '', }); const detected = await detectFrameworks('mockdata'); expect(detected).toContain('cocoapods'); + expect(detected).toContain('swiftpm'); }); diff --git a/src/framework.ts b/src/framework.ts index 3c345c2..7e5b73a 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -122,7 +122,9 @@ async function detectRust(rootPath: string): Promise { async function detectXcode(rootPath: string): Promise { let detected: string[] = []; - // TODO: support xcode & swiftpm + if (fs.existsSync(path.join(rootPath, 'Package.swift'))) { + detected.push('swiftpm'); + } if (fs.existsSync(path.join(rootPath, 'Podfile'))) { detected.push('cocoapods'); From e5e8e67f845e325d9d0dd5771c831f7fc91af22c Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 15:06:57 +0000 Subject: [PATCH 23/29] Detect pip/python --- dist/index/index.js | 12 ++++++++++-- src/framework.test.ts | 2 ++ src/framework.ts | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dist/index/index.js b/dist/index/index.js index 1f3fda9..7c8d9d4 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29695,10 +29695,12 @@ async function detectHomebrew(rootPath) { } async function detectJava(rootPath) { let detected = []; - // TODO: support maven if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'gradlew'))) { detected.push('gradle'); } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'pom.xml'))) { + detected.push('maven'); + } return detected; } async function detectNode(rootPath) { @@ -29723,6 +29725,9 @@ async function detectPython(rootPath) { if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'poetry.lock'))) { detected.push('poetry'); } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'requirements.txt'))) { + detected.push('python'); + } if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'uv.lock'))) { detected.push('uv'); } @@ -29744,10 +29749,13 @@ async function detectRust(rootPath) { } async function detectXcode(rootPath) { let detected = []; - // TODO: support xcode & swiftpm + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Package.swift'))) { + detected.push('swiftpm'); + } if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Podfile'))) { detected.push('cocoapods'); } + // - *.xcodeproj return detected; } diff --git a/src/framework.test.ts b/src/framework.test.ts index 8357c37..fa66367 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -69,11 +69,13 @@ test('detects php', async () => { test('detects python', async () => { vol.fromJSON({ 'mockdata/poetry.lock': '', + 'mockdata/requirements.txt': '', 'mockdata/uv.lock': '', }); const detected = await detectFrameworks('mockdata'); expect(detected).toContain('poetry'); + expect(detected).toContain('python'); expect(detected).toContain('uv'); }); diff --git a/src/framework.ts b/src/framework.ts index 7e5b73a..ec55947 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -92,6 +92,10 @@ async function detectPython(rootPath: string): Promise { detected.push('poetry'); } + if (fs.existsSync(path.join(rootPath, 'requirements.txt'))) { + detected.push('python'); + } + if (fs.existsSync(path.join(rootPath, 'uv.lock'))) { detected.push('uv'); } From 20f6ae87436cf2d762e9d73946e3dc4484ae2513 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 15:42:35 +0000 Subject: [PATCH 24/29] Detect xcode --- dist/index/index.js | 7 ++++++- src/framework.test.ts | 2 ++ src/framework.ts | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dist/index/index.js b/dist/index/index.js index 7c8d9d4..e0b5612 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29755,7 +29755,12 @@ async function detectXcode(rootPath) { if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'Podfile'))) { detected.push('cocoapods'); } - // - *.xcodeproj + for (const entry of external_node_fs_namespaceObject.readdirSync(rootPath)) { + if (entry.endsWith('.xcodeproj')) { + detected.push('xcode'); + break; + } + } return detected; } diff --git a/src/framework.test.ts b/src/framework.test.ts index fa66367..4cd9c16 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -101,9 +101,11 @@ test('detects xcode', async () => { vol.fromJSON({ 'mockdata/Package.swift': '', 'mockdata/Podfile': '', + 'mockdata/SomeProject.xcodeproj/project.pbxproj': '', }); const detected = await detectFrameworks('mockdata'); expect(detected).toContain('cocoapods'); expect(detected).toContain('swiftpm'); + expect(detected).toContain('xcode'); }); diff --git a/src/framework.ts b/src/framework.ts index ec55947..f0af5db 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -134,5 +134,12 @@ async function detectXcode(rootPath: string): Promise { detected.push('cocoapods'); } + for (const entry of fs.readdirSync(rootPath)) { + if (entry.endsWith('.xcodeproj')) { + detected.push('xcode'); + break; + } + } + return detected; } From 4be6031688ca24915159843130941e40c6198dc6 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 15:55:41 +0000 Subject: [PATCH 25/29] Detect playwright --- dist/index/index.js | 11 +++++++++++ src/framework.test.ts | 22 ++++++++++++++++++++-- src/framework.ts | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dist/index/index.js b/dist/index/index.js index e0b5612..2f357e7 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29664,6 +29664,7 @@ async function detectFrameworks(rootPath = './') { detectJava, detectNode, detectPhp, + detectPlaywright, detectPython, detectRuby, detectRust, @@ -29720,6 +29721,16 @@ async function detectPhp(rootPath) { } return detected; } +async function detectPlaywright(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'playwright.config.js'))) { + detected.push('playwright'); + } + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'playwright.config.ts'))) { + detected.push('playwright'); + } + return detected; +} async function detectPython(rootPath) { let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'poetry.lock'))) { diff --git a/src/framework.test.ts b/src/framework.test.ts index 4cd9c16..c1e200e 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -8,7 +8,7 @@ beforeEach(() => { vol.reset(); // reset the state of in-memory fs }); -test('detects go - mod', async () => { +test('detects go mod', async () => { vol.fromJSON({ 'mockdata/go.mod': '', }); @@ -17,7 +17,7 @@ test('detects go - mod', async () => { expect(detected).toContain('go'); }); -test('detects go - work', async () => { +test('detects go work', async () => { vol.fromJSON({ 'mockdata/go.work': '', }); @@ -66,6 +66,24 @@ test('detects php', async () => { expect(detected).toContain('composer'); }); +test('detects playwright js', async () => { + vol.fromJSON({ + 'mockdata/playwright.config.js': '', + }); + + const detected = await detectFrameworks('mockdata'); + expect(detected).toContain('playwright'); +}); + +test('detects playwright ts', async () => { + vol.fromJSON({ + 'mockdata/playwright.config.ts': '', + }); + + const detected = await detectFrameworks('mockdata'); + expect(detected).toContain('playwright'); +}); + test('detects python', async () => { vol.fromJSON({ 'mockdata/poetry.lock': '', diff --git a/src/framework.ts b/src/framework.ts index f0af5db..747dc09 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -8,6 +8,7 @@ export async function detectFrameworks(rootPath = './'): Promise { detectJava, detectNode, detectPhp, + detectPlaywright, detectPython, detectRuby, detectRust, @@ -85,6 +86,20 @@ async function detectPhp(rootPath: string): Promise { return detected; } +async function detectPlaywright(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'playwright.config.js'))) { + detected.push('playwright'); + } + + if (fs.existsSync(path.join(rootPath, 'playwright.config.ts'))) { + detected.push('playwright'); + } + + return detected; +} + async function detectPython(rootPath: string): Promise { let detected: string[] = []; From bbb7919a57c8486760e565acd00f37a705935962 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Mon, 27 Oct 2025 16:52:26 +0000 Subject: [PATCH 26/29] Detect bun & deno --- dist/index/index.js | 16 ++++++++++++++++ src/framework.test.ts | 18 ++++++++++++++++++ src/framework.ts | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/dist/index/index.js b/dist/index/index.js index 2f357e7..0577595 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29659,6 +29659,8 @@ function shouldUseSymlinks() { async function detectFrameworks(rootPath = './') { const detectors = [ + detectBun, + detectDeno, detectGo, detectHomebrew, detectJava, @@ -29677,6 +29679,20 @@ async function detectFrameworks(rootPath = './') { } return detected; } +async function detectBun(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'bun.lock'))) { + detected.push('bun'); + } + return detected; +} +async function detectDeno(rootPath) { + let detected = []; + if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'deno.lock'))) { + detected.push('deno'); + } + return detected; +} async function detectGo(rootPath) { let detected = []; if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.mod'))) { diff --git a/src/framework.test.ts b/src/framework.test.ts index c1e200e..c54a489 100644 --- a/src/framework.test.ts +++ b/src/framework.test.ts @@ -8,6 +8,24 @@ beforeEach(() => { vol.reset(); // reset the state of in-memory fs }); +test('detects bun', async () => { + vol.fromJSON({ + 'mockdata/bun.lock': '', + }); + + const detected = await detectFrameworks('mockdata'); + expect(detected).toContain('bun'); +}); + +test('detects deno', async () => { + vol.fromJSON({ + 'mockdata/deno.lock': '', + }); + + const detected = await detectFrameworks('mockdata'); + expect(detected).toContain('deno'); +}); + test('detects go mod', async () => { vol.fromJSON({ 'mockdata/go.mod': '', diff --git a/src/framework.ts b/src/framework.ts index 747dc09..e1e9b69 100644 --- a/src/framework.ts +++ b/src/framework.ts @@ -3,6 +3,8 @@ import * as path from "node:path"; export async function detectFrameworks(rootPath = './'): Promise { const detectors: Array<(rootPath: string) => Promise> = [ + detectBun, + detectDeno, detectGo, detectHomebrew, detectJava, @@ -24,6 +26,26 @@ export async function detectFrameworks(rootPath = './'): Promise { return detected; } +async function detectBun(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'bun.lock'))) { + detected.push('bun'); + } + + return detected; +} + +async function detectDeno(rootPath: string): Promise { + let detected: string[] = []; + + if (fs.existsSync(path.join(rootPath, 'deno.lock'))) { + detected.push('deno'); + } + + return detected; +} + async function detectGo(rootPath: string): Promise { let detected: string[] = []; From c44b8626804ca993f8f2371660e7e1ffd6f9d3d7 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Wed, 29 Oct 2025 13:31:33 +0000 Subject: [PATCH 27/29] Indicate when no cache modes were detected --- src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index e2edc48..154dca3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -172,7 +172,9 @@ export async function restoreLocalCache( async function resolveFrameworks(): Promise { const detected = await framework.detectFrameworks(); if (detected.length > 0) { - core.info(`Detected frameworks: ${detected.join(", ")}`); + core.info(`Detected cache modes: ${detected.join(", ")}`); + } else { + core.info("No cache modes automatically detected."); } return detected; } From 2dc09164fd4871bd848a707bfa6a99cdc0007e8e Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Wed, 29 Oct 2025 13:31:52 +0000 Subject: [PATCH 28/29] Move away from framework naming --- dist/index/index.js | 15 +++++++----- src/{framework.test.ts => detect.test.ts} | 30 +++++++++++------------ src/{framework.ts => detect.ts} | 2 +- src/index.ts | 8 +++--- 4 files changed, 29 insertions(+), 26 deletions(-) rename src/{framework.test.ts => detect.test.ts} (75%) rename src/{framework.ts => detect.ts} (98%) diff --git a/dist/index/index.js b/dist/index/index.js index 0577595..6a80455 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29654,10 +29654,10 @@ function shouldUseSymlinks() { return useSymlinks; } -;// CONCATENATED MODULE: ./src/framework.ts +;// CONCATENATED MODULE: ./src/detect.ts -async function detectFrameworks(rootPath = './') { +async function cacheModes(rootPath = './') { const detectors = [ detectBun, detectDeno, @@ -29859,7 +29859,7 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); if (autoDetect || (manualPaths.length === 0 && cacheModes.length === 0)) { - cacheModes = await resolveFrameworks(); + cacheModes = await resolveDetectedCacheModes(); } const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); const cacheMisses = await restoreLocalCache(cachePaths, useSymlinks); @@ -29938,10 +29938,13 @@ async function restoreLocalCache(cachePaths, useSymlinks) { } return cacheMisses; } -async function resolveFrameworks() { - const detected = await detectFrameworks(); +async function resolveDetectedCacheModes() { + const detected = await cacheModes(); if (detected.length > 0) { - core.info(`Detected frameworks: ${detected.join(", ")}`); + core.info(`Detected cache modes: ${detected.join(", ")}`); + } + else { + core.info("No cache modes automatically detected."); } return detected; } diff --git a/src/framework.test.ts b/src/detect.test.ts similarity index 75% rename from src/framework.test.ts rename to src/detect.test.ts index c54a489..b572f25 100644 --- a/src/framework.test.ts +++ b/src/detect.test.ts @@ -1,6 +1,6 @@ import { beforeEach, expect, test, vi } from 'vitest'; import { vol } from 'memfs'; -import { detectFrameworks } from "./framework"; +import * as detect from "./detect"; vi.mock('node:fs'); @@ -13,7 +13,7 @@ test('detects bun', async () => { 'mockdata/bun.lock': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('bun'); }); @@ -22,7 +22,7 @@ test('detects deno', async () => { 'mockdata/deno.lock': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('deno'); }); @@ -31,7 +31,7 @@ test('detects go mod', async () => { 'mockdata/go.mod': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('go'); }); @@ -40,7 +40,7 @@ test('detects go work', async () => { 'mockdata/go.work': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('go'); }); @@ -49,7 +49,7 @@ test('detects homebrew', async () => { 'mockdata/Brewfile': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('brew'); }); @@ -59,7 +59,7 @@ test('detects java', async () => { 'mockdata/pom.xml': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('gradle'); expect(detected).toContain('maven'); }); @@ -70,7 +70,7 @@ test('detects node', async () => { 'mockdata/yarn.lock': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('pnpm'); expect(detected).toContain('yarn'); }); @@ -80,7 +80,7 @@ test('detects php', async () => { 'mockdata/composer.json': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('composer'); }); @@ -89,7 +89,7 @@ test('detects playwright js', async () => { 'mockdata/playwright.config.js': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('playwright'); }); @@ -98,7 +98,7 @@ test('detects playwright ts', async () => { 'mockdata/playwright.config.ts': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('playwright'); }); @@ -109,7 +109,7 @@ test('detects python', async () => { 'mockdata/uv.lock': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('poetry'); expect(detected).toContain('python'); expect(detected).toContain('uv'); @@ -120,7 +120,7 @@ test('detects ruby', async () => { 'mockdata/Gemfile': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('ruby'); }); @@ -129,7 +129,7 @@ test('detects rust', async () => { 'mockdata/Cargo.toml': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('rust'); }); @@ -140,7 +140,7 @@ test('detects xcode', async () => { 'mockdata/SomeProject.xcodeproj/project.pbxproj': '', }); - const detected = await detectFrameworks('mockdata'); + const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('cocoapods'); expect(detected).toContain('swiftpm'); expect(detected).toContain('xcode'); diff --git a/src/framework.ts b/src/detect.ts similarity index 98% rename from src/framework.ts rename to src/detect.ts index e1e9b69..4b25378 100644 --- a/src/framework.ts +++ b/src/detect.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; -export async function detectFrameworks(rootPath = './'): Promise { +export async function cacheModes(rootPath = './'): Promise { const detectors: Array<(rootPath: string) => Promise> = [ detectBun, detectDeno, diff --git a/src/index.ts b/src/index.ts index 154dca3..dff3a0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import * as core from "@actions/core"; import * as exec from "@actions/exec"; import * as io from "@actions/io"; import * as utils from "./utils"; -import * as framework from "./framework"; +import * as detect from "./detect"; const Input_Key = "key"; // unused const Input_Detect = "detect"; @@ -73,7 +73,7 @@ Are you running in a container? Check out https://namespace.so/docs/reference/gi const manualPaths = core.getMultilineInput(Input_Path); let cacheModes = core.getMultilineInput(Input_Cache); if (autoDetect || (manualPaths.length === 0 && cacheModes.length === 0)) { - cacheModes = await resolveFrameworks(); + cacheModes = await resolveDetectedCacheModes(); } const cachePaths = await resolveCachePaths(localCachePath, manualPaths, cacheModes); @@ -169,8 +169,8 @@ export async function restoreLocalCache( return cacheMisses; } -async function resolveFrameworks(): Promise { - const detected = await framework.detectFrameworks(); +async function resolveDetectedCacheModes(): Promise { + const detected = await detect.cacheModes(); if (detected.length > 0) { core.info(`Detected cache modes: ${detected.join(", ")}`); } else { From 14af2de56dfc9cc4d8f9a8322cc02b40a8383d11 Mon Sep 17 00:00:00 2001 From: Rob Crowe Date: Wed, 29 Oct 2025 13:45:15 +0000 Subject: [PATCH 29/29] Detect golangci-lint --- dist/index/index.js | 6 ++++++ src/detect.test.ts | 4 +++- src/detect.ts | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dist/index/index.js b/dist/index/index.js index 6a80455..bcfac3e 100644 --- a/dist/index/index.js +++ b/dist/index/index.js @@ -29701,6 +29701,12 @@ async function detectGo(rootPath) { if (external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(rootPath, 'go.work'))) { detected.push('go'); } + for (const entry of external_node_fs_namespaceObject.readdirSync(rootPath)) { + if (entry.endsWith('.golangci.yml') || entry.endsWith('.golangci.yaml')) { + detected.push('golangci-lint'); + break; + } + } return detected; } async function detectHomebrew(rootPath) { diff --git a/src/detect.test.ts b/src/detect.test.ts index b572f25..a32dd84 100644 --- a/src/detect.test.ts +++ b/src/detect.test.ts @@ -26,13 +26,15 @@ test('detects deno', async () => { expect(detected).toContain('deno'); }); -test('detects go mod', async () => { +test('detects go', async () => { vol.fromJSON({ 'mockdata/go.mod': '', + 'mockdata/.golangci.yml': '', }); const detected = await detect.cacheModes('mockdata'); expect(detected).toContain('go'); + expect(detected).toContain('golangci-lint'); }); test('detects go work', async () => { diff --git a/src/detect.ts b/src/detect.ts index 4b25378..3bfd932 100644 --- a/src/detect.ts +++ b/src/detect.ts @@ -57,6 +57,13 @@ async function detectGo(rootPath: string): Promise { detected.push('go'); } + for (const entry of fs.readdirSync(rootPath)) { + if (entry.endsWith('.golangci.yml') || entry.endsWith('.golangci.yaml')) { + detected.push('golangci-lint'); + break; + } + } + return detected; }