From 692e789f1dcb7d9faa3260d334521727fca119a2 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:43:43 +0000 Subject: [PATCH 1/3] fix(build): reject persisted checkout credentials Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/actions-ci.yml | 2 + actions/build/action.yml | 4 + .../build/scripts/validate-checkout/index.mjs | 111 +++++++ .../scripts/validate-checkout/index.test.mjs | 296 ++++++++++++++++++ hub/docs/github-actions-builds.md | 10 +- 5 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 actions/build/scripts/validate-checkout/index.mjs create mode 100644 actions/build/scripts/validate-checkout/index.test.mjs diff --git a/.github/workflows/actions-ci.yml b/.github/workflows/actions-ci.yml index 251423cd..9abc0bab 100644 --- a/.github/workflows/actions-ci.yml +++ b/.github/workflows/actions-ci.yml @@ -23,6 +23,8 @@ jobs: node: [24, 26] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node }} diff --git a/actions/build/action.yml b/actions/build/action.yml index 128f4b1a..e1107112 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -9,6 +9,10 @@ runs: with: node-version: 24 + - name: Check checkout credentials + run: node "$GITHUB_ACTION_PATH/scripts/validate-checkout/index.mjs" --root "$GITHUB_WORKSPACE" + shell: bash + - name: Check required npm files and build script run: node "$GITHUB_ACTION_PATH/scripts/validate-repository/index.mjs" --root "$GITHUB_WORKSPACE" shell: bash diff --git a/actions/build/scripts/validate-checkout/index.mjs b/actions/build/scripts/validate-checkout/index.mjs new file mode 100644 index 00000000..0367a499 --- /dev/null +++ b/actions/build/scripts/validate-checkout/index.mjs @@ -0,0 +1,111 @@ +import { execFile } from "node:child_process" +import { readFile, readdir, realpath } from "node:fs/promises" +import path from "node:path" +import { fileURLToPath } from "node:url" +import { promisify } from "node:util" + +import { requiredPathArgument } from "../required-path-argument/index.mjs" + +const run = promisify(execFile) +const unsafeCheckout = + "Checkout must not contain Git authentication settings or Git metadata outside the workspace. " + + "Use actions/checkout with persist-credentials: false, including nested checkouts and submodules." + +/** Rejects persisted Git authentication before repository-controlled scripts run. */ +export async function validateCheckout(root) { + root = await realpath(root) + const pending = [{ directory: root, metadata: false }] + const visited = new Set() + + while (pending.length > 0) { + const { directory, metadata } = pending.pop() + const identity = `${metadata}:${directory}` + + if (visited.has(identity)) continue + visited.add(identity) + + for (const entry of await readdir(directory, { withFileTypes: true })) { + const location = path.join(directory, entry.name) + const gitMetadata = metadata || entry.name === ".git" + const config = metadata && ["config", "config.worktree"].includes(entry.name) + + if ((entry.name === ".git" && entry.isFile()) || (metadata && entry.name === "commondir")) { + if (!entry.isFile()) throw new Error(unsafeCheckout) + const content = await readFile(location, "utf8") + const reference = + entry.name === ".git" ? /^gitdir: (.+)\r?\n?$/.exec(content)?.[1] : content.trimEnd() + + if (!reference) throw new Error(unsafeCheckout) + let target + + try { + target = await realpath(path.resolve(directory, reference)) + } catch { + throw new Error("Cannot resolve checkout Git metadata. " + unsafeCheckout) + } + + const relative = path.relative(root, target) + if ( + relative === ".." || + relative.startsWith(`..${path.sep}`) || + path.isAbsolute(relative) + ) { + throw new Error(unsafeCheckout) + } + + pending.push({ directory: target, metadata: true }) + continue + } + + if ( + (entry.name === ".git" && !entry.isDirectory()) || + (metadata && entry.isSymbolicLink()) || + (config && !entry.isFile()) + ) { + throw new Error(unsafeCheckout) + } + + if (entry.isDirectory()) { + pending.push({ directory: location, metadata: gitMetadata }) + } else if (config) { + let output + + try { + const result = await run("git", [ + "config", + "--file", + location, + "--no-includes", + "--null", + "--list", + ]) + output = result.stdout + } catch { + // Git's parser errors can contain credentials from the config file. + throw new Error("Cannot safely read checkout Git configuration. " + unsafeCheckout) + } + + for (const setting of output.split("\0").filter(Boolean)) { + const separator = setting.indexOf("\n") + const key = (separator < 0 ? setting : setting.slice(0, separator)).toLowerCase() + + if ( + key.startsWith("credential.") || + /^http\.(.*\.)?(extraheader|cookiefile)$/.test(key) || + /^core\.(sshcommand|askpass)$/.test(key) || + key === "include.path" || + /^includeif\..*\.path$/.test(key) || + /[a-z][a-z0-9+.-]*:\/\/[^/\s]*@/i.test(setting) + ) { + throw new Error(unsafeCheckout) + } + } + } + } + } +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + await validateCheckout(requiredPathArgument("root")) + console.log("Checkout contains no persisted Git authentication settings.") +} diff --git a/actions/build/scripts/validate-checkout/index.test.mjs b/actions/build/scripts/validate-checkout/index.test.mjs new file mode 100644 index 00000000..aee5e520 --- /dev/null +++ b/actions/build/scripts/validate-checkout/index.test.mjs @@ -0,0 +1,296 @@ +import assert from "node:assert/strict" +import { spawnSync } from "node:child_process" +import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises" +import path from "node:path" +import { fileURLToPath } from "node:url" +import { describe, it } from "node:test" + +import { temporaryDirectory } from "../../test/temporary-directory/index.mjs" + +import { validateCheckout } from "./index.mjs" + +const script = fileURLToPath(new URL("./index.mjs", import.meta.url)) +const secret = "dummy-checkout-credential-do-not-log" + +async function checkout(t) { + const root = await temporaryDirectory() + t.after(() => rm(root, { recursive: true, force: true })) + const result = spawnSync("git", ["init", "--quiet", root], { encoding: "utf8" }) + assert.equal(result.status, 0, result.stderr) + return root +} + +describe("validateCheckout()", () => { + it("accepts a normal checkout with credential-free remote and repository settings", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + "[core]\nrepositoryformatversion = 0\nbare = false\n" + + '[remote "origin"]\nurl = https://github.com/example/prototype.git\n' + + "fetch = +refs/heads/*:refs/remotes/origin/*\n" + + '[branch "main"]\nremote = origin\nmerge = refs/heads/main\n', + ) + + await assert.doesNotReject(validateCheckout(root)) + }) + + for (const [name, config] of [ + [ + "an HTTP authorization header", + `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, + ], + ["an HTTP cookie file", `[http]\ncookieFile = ${secret}\n`], + ["a credential helper", `[credential]\nhelper = !echo ${secret}\n`], + ["a scoped credential setting", `[credential "https://github.com"]\nusername = ${secret}\n`], + ["a custom SSH command", `[core]\nsshCommand = echo ${secret}\n`], + ["a custom askpass command", `[core]\naskPass = ${secret}\n`], + ["an included config", `[include]\npath = ${secret}\n`], + ["a conditional include", `[includeIf "gitdir:./"]\npath = ${secret}\n`], + [ + "URL userinfo in a remote value", + `[remote "origin"]\nurl = https://user:${secret}@github.com/example/prototype.git\n`, + ], + [ + "URL userinfo in a rewrite section key", + `[url "https://user:${secret}@github.com/"]\ninsteadOf = https://github.com/\n`, + ], + ]) { + it(`rejects ${name} without exposing its value`, async (t) => { + const root = await checkout(t) + await writeFile(path.join(root, ".git", "config"), config) + + await assert.rejects(validateCheckout(root), (error) => { + assert.ok(error instanceof Error) + assert.ok(!String(error).includes(secret)) + return true + }) + }) + } + + it("rejects authentication persisted in a nested repository", async (t) => { + const root = await checkout(t) + const metadata = path.join(root, "packages", "nested", ".git") + await mkdir(metadata, { recursive: true }) + await writeFile( + path.join(metadata, "config"), + `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, + ) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects authentication persisted in worktree-specific config", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + "[core]\nrepositoryformatversion = 1\n[extensions]\nworktreeConfig = true\n", + ) + await writeFile( + path.join(root, ".git", "config.worktree"), + `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, + ) + + await assert.rejects(validateCheckout(root), (error) => { + assert.ok(error instanceof Error) + assert.ok(!String(error).includes(secret)) + return true + }) + }) + + it("rejects authentication persisted in submodule metadata", async (t) => { + const root = await checkout(t) + const metadata = path.join(root, ".git", "modules", "packages", "nested") + const submodule = path.join(root, "packages", "nested") + await mkdir(metadata, { recursive: true }) + await mkdir(submodule, { recursive: true }) + await writeFile(path.join(submodule, ".git"), "gitdir: ../../.git/modules/packages/nested\n") + await writeFile( + path.join(metadata, "config"), + `[remote "origin"]\nurl = https://user:${secret}@github.com/example/submodule.git\n`, + ) + + await assert.rejects(validateCheckout(root)) + }) + + for (const [name, newline] of [ + ["LF", "\n"], + ["CRLF", "\r\n"], + ]) { + it(`accepts ${name} submodule gitfiles pointing to credential-free metadata inside the workspace`, async (t) => { + const root = await checkout(t) + const metadata = path.join(root, ".git", "modules", "packages", "nested") + const submodule = path.join(root, "packages", "nested") + await mkdir(path.dirname(metadata), { recursive: true }) + const initialized = spawnSync( + "git", + ["init", "--quiet", "--separate-git-dir", metadata, submodule], + { encoding: "utf8" }, + ) + assert.equal(initialized.status, 0, initialized.stderr) + await writeFile( + path.join(submodule, ".git"), + `gitdir: ../../.git/modules/packages/nested${newline}`, + ) + const resolved = spawnSync("git", ["-C", submodule, "rev-parse", "--absolute-git-dir"], { + encoding: "utf8", + }) + assert.equal(resolved.status, 0, resolved.stderr) + assert.equal(resolved.stdout.trimEnd(), metadata) + + await assert.doesNotReject(validateCheckout(root)) + }) + } + + it("accepts a commondir reference to credential-free metadata inside the workspace", async (t) => { + const root = await checkout(t) + const common = path.join(root, "common") + await mkdir(common) + await writeFile(path.join(root, ".git", "commondir"), "../common\n") + await writeFile( + path.join(common, "config"), + '[remote "origin"]\nurl = https://github.com/example/prototype.git\n', + ) + + await assert.doesNotReject(validateCheckout(root)) + }) + + it("rejects authentication in metadata reached through an internal commondir reference", async (t) => { + const root = await checkout(t) + const common = path.join(root, "common") + await mkdir(common) + await writeFile(path.join(root, ".git", "commondir"), "../common\n") + await writeFile(path.join(common, "config"), `[credential]\nhelper = !echo ${secret}\n`) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects a commondir reference outside the workspace even without authentication", async (t) => { + const root = await checkout(t) + const external = await checkout(t) + await writeFile(path.join(root, ".git", "commondir"), `${path.join(external, ".git")}\n`) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects malformed config without exposing Git's diagnostic input", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n[broken\n`, + ) + + await assert.rejects(validateCheckout(root), (error) => { + assert.ok(error instanceof Error) + assert.ok(!String(error).includes(secret)) + return true + }) + }) + + it("rejects a gitfile pointing outside the workspace", async (t) => { + const root = await checkout(t) + const external = await checkout(t) + await rm(path.join(root, ".git"), { recursive: true }) + await writeFile(path.join(root, ".git"), `gitdir: ${path.join(external, ".git")}\n`) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects a symbolic link to Git metadata", async (t) => { + const root = await checkout(t) + const metadata = path.join(root, "external-metadata") + await mkdir(metadata) + await writeFile(path.join(metadata, "config"), "[core]\nbare = false\n") + await rm(path.join(root, ".git"), { recursive: true }) + await symlink(metadata, path.join(root, ".git"), "dir") + + await assert.rejects(validateCheckout(root)) + }) + + for (const [name, config] of [ + ["persisted authentication", `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n`], + ["malformed config", `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n[broken\n`], + ]) { + it(`fails the CLI for ${name} without printing credentials`, async (t) => { + const root = await checkout(t) + await writeFile(path.join(root, ".git", "config"), config) + + const result = spawnSync(process.execPath, [script, "--root", root], { + encoding: "utf8", + }) + + assert.equal(result.status, 1) + assert.ok(`${result.stdout}${result.stderr}`.trim().length > 0) + assert.ok(!`${result.stdout}${result.stderr}`.includes(secret)) + }) + } + + it("wires checkout validation before repository npm lifecycle and build execution", async (t) => { + const action = await readFile(new URL("../../action.yml", import.meta.url), "utf8") + const guard = action.match( + /run: (node "\$GITHUB_ACTION_PATH\/scripts\/validate-checkout\/index\.mjs" --root "\$GITHUB_WORKSPACE")/, + ) + const build = action.indexOf("npm ci && npm run build") + + assert.ok(guard, "The action must validate the workspace checkout") + assert.ok(build >= 0, "The action must retain its repository build step") + assert.ok(guard.index < build, "Checkout validation must precede repository execution") + + const root = await checkout(t) + const name = "checkout-guard-fixture" + const version = "1.0.0" + await writeFile( + path.join(root, "package.json"), + JSON.stringify({ + name, + version, + scripts: { preinstall: "node marker.mjs installed", build: "node marker.mjs built" }, + }), + ) + await writeFile( + path.join(root, "package-lock.json"), + JSON.stringify({ + name, + version, + lockfileVersion: 3, + packages: { "": { name, version, hasInstallScript: true } }, + }), + ) + await writeFile( + path.join(root, "marker.mjs"), + 'import { writeFileSync } from "node:fs"; writeFileSync(process.argv[2], "executed")\n', + ) + function run() { + return spawnSync("bash", ["-e", "-c", `${guard[1]}\nnpm ci && npm run build`], { + cwd: root, + encoding: "utf8", + env: { + ...process.env, + GITHUB_ACTION_PATH: fileURLToPath(new URL("../../", import.meta.url)), + GITHUB_WORKSPACE: root, + NODE_COMPILE_CACHE: path.join(root, ".node-cache"), + npm_config_audit: "false", + npm_config_fund: "false", + npm_config_cache: path.join(root, ".npm-cache"), + }, + }) + } + + const clean = run() + assert.equal(clean.status, 0, clean.stderr) + for (const marker of ["installed", "built"]) { + assert.equal(await readFile(path.join(root, marker), "utf8"), "executed") + await rm(path.join(root, marker)) + } + + await writeFile( + path.join(root, ".git", "config"), + `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, + ) + const unsafe = run() + assert.equal(unsafe.status, 1) + assert.ok(!`${unsafe.stdout}${unsafe.stderr}`.includes(secret)) + for (const marker of ["installed", "built"]) { + await assert.rejects(readFile(path.join(root, marker)), { code: "ENOENT" }) + } + }) +}) diff --git a/hub/docs/github-actions-builds.md b/hub/docs/github-actions-builds.md index 65a0d790..452245e7 100644 --- a/hub/docs/github-actions-builds.md +++ b/hub/docs/github-actions-builds.md @@ -28,7 +28,7 @@ Installation and update retries reuse a branch only when GitHub confirms it star The action: -1. Checks for `package.json`, `package-lock.json`, and a build script. +1. Checks for persisted Git authentication settings, then for `package.json`, `package-lock.json`, and a build script. 2. Runs `npm ci` and `npm run build` in a pinned Node container. 3. Checks the generated `dist` files and their sizes. 4. Opens the prototype in Chromium. @@ -37,6 +37,14 @@ The action: The build container receives no Hub, GitHub App, or storage credentials. It receives no credentials for any other repository. +### Checkout credentials + +Use `persist-credentials: false` on every `actions/checkout` step that places files in the build workspace, including nested checkouts and submodules. Give the workflow only `contents: read` unless another step needs more access. Hub-generated workflows already use these settings. + +Before running repository scripts, the action rejects Git authentication settings in the workspace, including submodule configuration. Rejected settings include credential helpers, HTTP extra headers and cookie files, custom SSH and password-prompt commands, and URLs containing user information. Git configuration includes, symbolic links within Git metadata, and metadata references outside the workspace are also rejected so they cannot hide authentication settings from the check. Submodule metadata references within the workspace remain supported. Remove rejected settings or use a credential-free checkout with self-contained Git metadata before building. The action does not remove credentials or print their values. + +This check is not a general secret scanner. Do not place tokens, private keys, credential files, or other secrets in the build workspace. Repository-controlled install and build scripts can read that workspace and access the network. + ## How Hub follows the workflow Hub follows `.github/workflows/replica.yml` for connected repositories and matches each run to its default or prototype branch and exact commit. From 8c8fac2ffe789c987b3d3381bb2c0e8cd9856f62 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 27 Oct 2026 17:07:54 +0000 Subject: [PATCH 2/3] refactor(build): run checkout guard with tsx Convert the checkout preflight to TypeScript and retain seven focused regression cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/build/action.yml | 12 +- actions/build/eslint.config.js | 7 +- actions/build/package-lock.json | 1492 +++++++++++++++-- actions/build/package.json | 13 +- .../scripts/validate-checkout/index.test.mjs | 296 ---- .../scripts/validate-checkout/index.test.ts | 166 ++ .../validate-checkout/{index.mjs => index.ts} | 6 +- actions/build/tsconfig.json | 13 + 8 files changed, 1571 insertions(+), 434 deletions(-) delete mode 100644 actions/build/scripts/validate-checkout/index.test.mjs create mode 100644 actions/build/scripts/validate-checkout/index.test.ts rename actions/build/scripts/validate-checkout/{index.mjs => index.ts} (95%) create mode 100644 actions/build/tsconfig.json diff --git a/actions/build/action.yml b/actions/build/action.yml index e1107112..37e5d378 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -9,19 +9,19 @@ runs: with: node-version: 24 + - name: Install action dependencies + run: npm ci --ignore-scripts --omit=dev + shell: bash + working-directory: ${{ github.action_path }} + - name: Check checkout credentials - run: node "$GITHUB_ACTION_PATH/scripts/validate-checkout/index.mjs" --root "$GITHUB_WORKSPACE" + run: '"$GITHUB_ACTION_PATH/node_modules/.bin/tsx" --tsconfig "$GITHUB_ACTION_PATH/tsconfig.json" "$GITHUB_ACTION_PATH/scripts/validate-checkout/index.ts" --root "$GITHUB_WORKSPACE"' shell: bash - name: Check required npm files and build script run: node "$GITHUB_ACTION_PATH/scripts/validate-repository/index.mjs" --root "$GITHUB_WORKSPACE" shell: bash - - name: Install browser-check dependencies - run: npm ci --ignore-scripts --omit=dev - shell: bash - working-directory: ${{ github.action_path }} - - name: Build prototype as a non-root user in a Node container run: | docker run --rm \ diff --git a/actions/build/eslint.config.js b/actions/build/eslint.config.js index e395365d..cddc4f22 100644 --- a/actions/build/eslint.config.js +++ b/actions/build/eslint.config.js @@ -2,6 +2,7 @@ import js from "@eslint/js" import { defineConfig } from "eslint/config" import prettier from "eslint-config-prettier" import globals from "globals" +import tseslint from "typescript-eslint" export default defineConfig( { @@ -9,7 +10,11 @@ export default defineConfig( }, js.configs.recommended, { - files: ["**/*.mjs"], + files: ["**/*.ts"], + extends: [tseslint.configs.recommended], + }, + { + files: ["**/*.{mjs,ts}"], languageOptions: { globals: { ...globals.browser, ...globals.node }, }, diff --git a/actions/build/package-lock.json b/actions/build/package-lock.json index 12af941c..b2dd3300 100644 --- a/actions/build/package-lock.json +++ b/actions/build/package-lock.json @@ -9,14 +9,19 @@ "version": "1.0.0", "dependencies": { "playwright": "1.64.1", - "pngjs": "7.0.0" + "pngjs": "7.0.0", + "tsx": "4.23.15" }, "devDependencies": { "@eslint/js": "10.0.1", + "@types/node": "26.6.4", + "@typescript/native": "npm:typescript@7.0.2", "eslint": "10.12.0", "eslint-config-prettier": "10.1.8", "globals": "17.13.0", - "prettier": "3.9.10" + "prettier": "3.9.10", + "typescript": "npm:@typescript/typescript6@6.0.2", + "typescript-eslint": "8.71.2" }, "engines": { "node": "^24.15.0 || ^26.0.0" @@ -46,6 +51,422 @@ "keyv": "^5.6.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -100,190 +521,821 @@ "minimatch": "^10.2.4" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", + "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.2.tgz", + "integrity": "sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/type-utils": "8.71.2", + "@typescript-eslint/utils": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.71.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", + "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.2.tgz", + "integrity": "sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.2.tgz", + "integrity": "sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.71.2", + "@typescript-eslint/types": "^8.71.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.2.tgz", + "integrity": "sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.2.tgz", + "integrity": "sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.2.tgz", + "integrity": "sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/utils": "8.71.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.2.tgz", + "integrity": "sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.2.tgz", + "integrity": "sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.71.2", + "@typescript-eslint/tsconfig-utils": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.2.tgz", + "integrity": "sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.2.tgz", + "integrity": "sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.71.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript/native": { + "name": "typescript", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/@typescript/old": { + "name": "typescript", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", - "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=16.20.0" } }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=16.20.0" } }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": ">=16.20.0" } }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=16.20.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", - "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=16.20.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=16.20.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], "dev": true, "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=16.20.0" } }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], "dev": true, "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=16.20.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=16.20.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], "dev": true, "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=16.20.0" } }, - "node_modules/@keyv/bigmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", - "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "hashery": "^1.4.0", - "hookified": "^1.15.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "keyv": "^5.6.0" + "node": ">=16.20.0" } }, - "node_modules/@keyv/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/acorn": { "version": "8.18.0", @@ -402,6 +1454,47 @@ "dev": true, "license": "MIT" }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -607,6 +1700,24 @@ "dev": true, "license": "MIT" }, + "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/file-entry-cache": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", @@ -653,6 +1764,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -903,6 +2028,19 @@ "node": ">=8" } }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/playwright": { "version": "1.64.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz", @@ -995,6 +2133,19 @@ "dev": true, "license": "MIT" }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1018,6 +2169,54 @@ "node": ">=8" } }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1031,6 +2230,51 @@ "node": ">= 0.8.0" } }, + "node_modules/typescript": { + "name": "@typescript/typescript6", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript6/-/typescript6-6.0.2.tgz", + "integrity": "sha512-mbCddXd+jm7hfx7w2YU64/Av4/NqqeG3GoRZgxPcgoTxYjhrcfJRw9ULch71SS4G+Q3bOXFhRvPqjguN0Hyp5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@typescript/old": "npm:typescript@^6" + }, + "bin": { + "tsc6": "bin/tsc6" + } + }, + "node_modules/typescript-eslint": { + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.2.tgz", + "integrity": "sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.71.2", + "@typescript-eslint/parser": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/utils": "8.71.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/actions/build/package.json b/actions/build/package.json index 3c942ff0..d69a165e 100644 --- a/actions/build/package.json +++ b/actions/build/package.json @@ -7,18 +7,23 @@ "node": "^24.15.0 || ^26.0.0" }, "scripts": { - "lint": "eslint . && prettier --check .", - "test": "node --test --test-concurrency=1 scripts/*/index.test.mjs" + "lint": "tsc --noEmit && eslint . && prettier --check .", + "test": "node --import tsx --test --test-concurrency=1 scripts/*/index.test.mjs scripts/*/index.test.ts" }, "dependencies": { "playwright": "1.64.1", - "pngjs": "7.0.0" + "pngjs": "7.0.0", + "tsx": "4.23.15" }, "devDependencies": { "@eslint/js": "10.0.1", + "@types/node": "26.6.4", + "@typescript/native": "npm:typescript@7.0.2", "eslint": "10.12.0", "eslint-config-prettier": "10.1.8", "globals": "17.13.0", - "prettier": "3.9.10" + "prettier": "3.9.10", + "typescript": "npm:@typescript/typescript6@6.0.2", + "typescript-eslint": "8.71.2" } } diff --git a/actions/build/scripts/validate-checkout/index.test.mjs b/actions/build/scripts/validate-checkout/index.test.mjs deleted file mode 100644 index aee5e520..00000000 --- a/actions/build/scripts/validate-checkout/index.test.mjs +++ /dev/null @@ -1,296 +0,0 @@ -import assert from "node:assert/strict" -import { spawnSync } from "node:child_process" -import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises" -import path from "node:path" -import { fileURLToPath } from "node:url" -import { describe, it } from "node:test" - -import { temporaryDirectory } from "../../test/temporary-directory/index.mjs" - -import { validateCheckout } from "./index.mjs" - -const script = fileURLToPath(new URL("./index.mjs", import.meta.url)) -const secret = "dummy-checkout-credential-do-not-log" - -async function checkout(t) { - const root = await temporaryDirectory() - t.after(() => rm(root, { recursive: true, force: true })) - const result = spawnSync("git", ["init", "--quiet", root], { encoding: "utf8" }) - assert.equal(result.status, 0, result.stderr) - return root -} - -describe("validateCheckout()", () => { - it("accepts a normal checkout with credential-free remote and repository settings", async (t) => { - const root = await checkout(t) - await writeFile( - path.join(root, ".git", "config"), - "[core]\nrepositoryformatversion = 0\nbare = false\n" + - '[remote "origin"]\nurl = https://github.com/example/prototype.git\n' + - "fetch = +refs/heads/*:refs/remotes/origin/*\n" + - '[branch "main"]\nremote = origin\nmerge = refs/heads/main\n', - ) - - await assert.doesNotReject(validateCheckout(root)) - }) - - for (const [name, config] of [ - [ - "an HTTP authorization header", - `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, - ], - ["an HTTP cookie file", `[http]\ncookieFile = ${secret}\n`], - ["a credential helper", `[credential]\nhelper = !echo ${secret}\n`], - ["a scoped credential setting", `[credential "https://github.com"]\nusername = ${secret}\n`], - ["a custom SSH command", `[core]\nsshCommand = echo ${secret}\n`], - ["a custom askpass command", `[core]\naskPass = ${secret}\n`], - ["an included config", `[include]\npath = ${secret}\n`], - ["a conditional include", `[includeIf "gitdir:./"]\npath = ${secret}\n`], - [ - "URL userinfo in a remote value", - `[remote "origin"]\nurl = https://user:${secret}@github.com/example/prototype.git\n`, - ], - [ - "URL userinfo in a rewrite section key", - `[url "https://user:${secret}@github.com/"]\ninsteadOf = https://github.com/\n`, - ], - ]) { - it(`rejects ${name} without exposing its value`, async (t) => { - const root = await checkout(t) - await writeFile(path.join(root, ".git", "config"), config) - - await assert.rejects(validateCheckout(root), (error) => { - assert.ok(error instanceof Error) - assert.ok(!String(error).includes(secret)) - return true - }) - }) - } - - it("rejects authentication persisted in a nested repository", async (t) => { - const root = await checkout(t) - const metadata = path.join(root, "packages", "nested", ".git") - await mkdir(metadata, { recursive: true }) - await writeFile( - path.join(metadata, "config"), - `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, - ) - - await assert.rejects(validateCheckout(root)) - }) - - it("rejects authentication persisted in worktree-specific config", async (t) => { - const root = await checkout(t) - await writeFile( - path.join(root, ".git", "config"), - "[core]\nrepositoryformatversion = 1\n[extensions]\nworktreeConfig = true\n", - ) - await writeFile( - path.join(root, ".git", "config.worktree"), - `[http "https://github.com/"]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, - ) - - await assert.rejects(validateCheckout(root), (error) => { - assert.ok(error instanceof Error) - assert.ok(!String(error).includes(secret)) - return true - }) - }) - - it("rejects authentication persisted in submodule metadata", async (t) => { - const root = await checkout(t) - const metadata = path.join(root, ".git", "modules", "packages", "nested") - const submodule = path.join(root, "packages", "nested") - await mkdir(metadata, { recursive: true }) - await mkdir(submodule, { recursive: true }) - await writeFile(path.join(submodule, ".git"), "gitdir: ../../.git/modules/packages/nested\n") - await writeFile( - path.join(metadata, "config"), - `[remote "origin"]\nurl = https://user:${secret}@github.com/example/submodule.git\n`, - ) - - await assert.rejects(validateCheckout(root)) - }) - - for (const [name, newline] of [ - ["LF", "\n"], - ["CRLF", "\r\n"], - ]) { - it(`accepts ${name} submodule gitfiles pointing to credential-free metadata inside the workspace`, async (t) => { - const root = await checkout(t) - const metadata = path.join(root, ".git", "modules", "packages", "nested") - const submodule = path.join(root, "packages", "nested") - await mkdir(path.dirname(metadata), { recursive: true }) - const initialized = spawnSync( - "git", - ["init", "--quiet", "--separate-git-dir", metadata, submodule], - { encoding: "utf8" }, - ) - assert.equal(initialized.status, 0, initialized.stderr) - await writeFile( - path.join(submodule, ".git"), - `gitdir: ../../.git/modules/packages/nested${newline}`, - ) - const resolved = spawnSync("git", ["-C", submodule, "rev-parse", "--absolute-git-dir"], { - encoding: "utf8", - }) - assert.equal(resolved.status, 0, resolved.stderr) - assert.equal(resolved.stdout.trimEnd(), metadata) - - await assert.doesNotReject(validateCheckout(root)) - }) - } - - it("accepts a commondir reference to credential-free metadata inside the workspace", async (t) => { - const root = await checkout(t) - const common = path.join(root, "common") - await mkdir(common) - await writeFile(path.join(root, ".git", "commondir"), "../common\n") - await writeFile( - path.join(common, "config"), - '[remote "origin"]\nurl = https://github.com/example/prototype.git\n', - ) - - await assert.doesNotReject(validateCheckout(root)) - }) - - it("rejects authentication in metadata reached through an internal commondir reference", async (t) => { - const root = await checkout(t) - const common = path.join(root, "common") - await mkdir(common) - await writeFile(path.join(root, ".git", "commondir"), "../common\n") - await writeFile(path.join(common, "config"), `[credential]\nhelper = !echo ${secret}\n`) - - await assert.rejects(validateCheckout(root)) - }) - - it("rejects a commondir reference outside the workspace even without authentication", async (t) => { - const root = await checkout(t) - const external = await checkout(t) - await writeFile(path.join(root, ".git", "commondir"), `${path.join(external, ".git")}\n`) - - await assert.rejects(validateCheckout(root)) - }) - - it("rejects malformed config without exposing Git's diagnostic input", async (t) => { - const root = await checkout(t) - await writeFile( - path.join(root, ".git", "config"), - `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n[broken\n`, - ) - - await assert.rejects(validateCheckout(root), (error) => { - assert.ok(error instanceof Error) - assert.ok(!String(error).includes(secret)) - return true - }) - }) - - it("rejects a gitfile pointing outside the workspace", async (t) => { - const root = await checkout(t) - const external = await checkout(t) - await rm(path.join(root, ".git"), { recursive: true }) - await writeFile(path.join(root, ".git"), `gitdir: ${path.join(external, ".git")}\n`) - - await assert.rejects(validateCheckout(root)) - }) - - it("rejects a symbolic link to Git metadata", async (t) => { - const root = await checkout(t) - const metadata = path.join(root, "external-metadata") - await mkdir(metadata) - await writeFile(path.join(metadata, "config"), "[core]\nbare = false\n") - await rm(path.join(root, ".git"), { recursive: true }) - await symlink(metadata, path.join(root, ".git"), "dir") - - await assert.rejects(validateCheckout(root)) - }) - - for (const [name, config] of [ - ["persisted authentication", `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n`], - ["malformed config", `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n[broken\n`], - ]) { - it(`fails the CLI for ${name} without printing credentials`, async (t) => { - const root = await checkout(t) - await writeFile(path.join(root, ".git", "config"), config) - - const result = spawnSync(process.execPath, [script, "--root", root], { - encoding: "utf8", - }) - - assert.equal(result.status, 1) - assert.ok(`${result.stdout}${result.stderr}`.trim().length > 0) - assert.ok(!`${result.stdout}${result.stderr}`.includes(secret)) - }) - } - - it("wires checkout validation before repository npm lifecycle and build execution", async (t) => { - const action = await readFile(new URL("../../action.yml", import.meta.url), "utf8") - const guard = action.match( - /run: (node "\$GITHUB_ACTION_PATH\/scripts\/validate-checkout\/index\.mjs" --root "\$GITHUB_WORKSPACE")/, - ) - const build = action.indexOf("npm ci && npm run build") - - assert.ok(guard, "The action must validate the workspace checkout") - assert.ok(build >= 0, "The action must retain its repository build step") - assert.ok(guard.index < build, "Checkout validation must precede repository execution") - - const root = await checkout(t) - const name = "checkout-guard-fixture" - const version = "1.0.0" - await writeFile( - path.join(root, "package.json"), - JSON.stringify({ - name, - version, - scripts: { preinstall: "node marker.mjs installed", build: "node marker.mjs built" }, - }), - ) - await writeFile( - path.join(root, "package-lock.json"), - JSON.stringify({ - name, - version, - lockfileVersion: 3, - packages: { "": { name, version, hasInstallScript: true } }, - }), - ) - await writeFile( - path.join(root, "marker.mjs"), - 'import { writeFileSync } from "node:fs"; writeFileSync(process.argv[2], "executed")\n', - ) - function run() { - return spawnSync("bash", ["-e", "-c", `${guard[1]}\nnpm ci && npm run build`], { - cwd: root, - encoding: "utf8", - env: { - ...process.env, - GITHUB_ACTION_PATH: fileURLToPath(new URL("../../", import.meta.url)), - GITHUB_WORKSPACE: root, - NODE_COMPILE_CACHE: path.join(root, ".node-cache"), - npm_config_audit: "false", - npm_config_fund: "false", - npm_config_cache: path.join(root, ".npm-cache"), - }, - }) - } - - const clean = run() - assert.equal(clean.status, 0, clean.stderr) - for (const marker of ["installed", "built"]) { - assert.equal(await readFile(path.join(root, marker), "utf8"), "executed") - await rm(path.join(root, marker)) - } - - await writeFile( - path.join(root, ".git", "config"), - `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n`, - ) - const unsafe = run() - assert.equal(unsafe.status, 1) - assert.ok(!`${unsafe.stdout}${unsafe.stderr}`.includes(secret)) - for (const marker of ["installed", "built"]) { - await assert.rejects(readFile(path.join(root, marker)), { code: "ENOENT" }) - } - }) -}) diff --git a/actions/build/scripts/validate-checkout/index.test.ts b/actions/build/scripts/validate-checkout/index.test.ts new file mode 100644 index 00000000..f8ae85fb --- /dev/null +++ b/actions/build/scripts/validate-checkout/index.test.ts @@ -0,0 +1,166 @@ +import assert from "node:assert/strict" +import { spawnSync } from "node:child_process" +import { mkdir, readFile, rm, writeFile } from "node:fs/promises" +import path from "node:path" +import { describe, it, type TestContext } from "node:test" +import { fileURLToPath } from "node:url" + +import { temporaryDirectory } from "../../test/temporary-directory/index.mjs" + +import { validateCheckout } from "./index.ts" + +const actionRoot = fileURLToPath(new URL("../../", import.meta.url)) +const tsx = path.join(actionRoot, "node_modules", ".bin", "tsx") +const script = fileURLToPath(new URL("./index.ts", import.meta.url)) +const secret = "dummy-checkout-credential-do-not-log" +const authorization = `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n` + +async function checkout(t: TestContext) { + const root = await temporaryDirectory() + t.after(() => rm(root, { recursive: true, force: true })) + const result = spawnSync("git", ["init", "--quiet", root], { encoding: "utf8" }) + assert.ifError(result.error) + assert.equal(result.status, 0, result.stderr) + return root +} + +describe("validateCheckout()", () => { + it("accepts an ordinary checkout with a credential-free internal submodule", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + '[remote "origin"]\nurl = https://github.com/example/prototype.git\n' + + '[branch "main"]\nremote = origin\nmerge = refs/heads/main\n', + ) + const metadata = path.join(root, ".git", "modules", "nested") + const submodule = path.join(root, "packages", "nested") + await mkdir(path.dirname(metadata), { recursive: true }) + const result = spawnSync( + "git", + ["init", "--quiet", "--separate-git-dir", metadata, submodule], + { encoding: "utf8" }, + ) + assert.ifError(result.error) + assert.equal(result.status, 0, result.stderr) + await writeFile(path.join(submodule, ".git"), "gitdir: ../../.git/modules/nested\n") + + await assert.doesNotReject(validateCheckout(root)) + }) + + it("runs the action guard before npm lifecycle and build scripts without leaking authorization", async (t) => { + const action = await readFile(path.join(actionRoot, "action.yml"), "utf8") + const guard = action.match(/run: '([^'\n]+validate-checkout[^'\n]+)'/) + assert.ok(guard, "The action must run checkout validation") + assert.ok(typeof guard.index === "number") + const guardCommand = guard[1] + const build = action.indexOf("npm ci && npm run build") + assert.ok(build > guard.index, "The guard must precede repository execution") + + const root = await checkout(t) + const name = "checkout-guard-fixture" + const version = "1.0.0" + await writeFile( + path.join(root, "package.json"), + JSON.stringify({ + name, + version, + scripts: { preinstall: "node marker.mjs installed", build: "node marker.mjs built" }, + }), + ) + await writeFile( + path.join(root, "package-lock.json"), + JSON.stringify({ + name, + version, + lockfileVersion: 3, + packages: { "": { name, version, hasInstallScript: true } }, + }), + ) + await writeFile( + path.join(root, "marker.mjs"), + 'import { writeFileSync } from "node:fs"; writeFileSync(process.argv[2], "executed")\n', + ) + function run() { + const result = spawnSync("bash", ["-e", "-c", `${guardCommand}\nnpm ci && npm run build`], { + cwd: root, + encoding: "utf8", + env: { + ...process.env, + GITHUB_ACTION_PATH: actionRoot, + GITHUB_WORKSPACE: root, + NODE_COMPILE_CACHE: path.join(root, ".node-cache"), + npm_config_audit: "false", + npm_config_fund: "false", + npm_config_cache: path.join(root, ".npm-cache"), + }, + }) + assert.ifError(result.error) + return result + } + + const clean = run() + assert.equal(clean.status, 0, clean.stderr) + for (const marker of ["installed", "built"]) { + assert.equal(await readFile(path.join(root, marker), "utf8"), "executed") + await rm(path.join(root, marker)) + } + await writeFile(path.join(root, ".git", "config"), authorization) + + const unsafe = run() + assert.equal(unsafe.status, 1) + assert.ok(!`${unsafe.stdout}${unsafe.stderr}`.includes(secret)) + for (const marker of ["installed", "built"]) { + await assert.rejects(readFile(path.join(root, marker)), { code: "ENOENT" }) + } + }) + + it("rejects authentication embedded in a remote URL", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + `[remote "origin"]\nurl = https://user:${secret}@github.com/example/prototype.git\n`, + ) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects authentication in recursive submodule metadata", async (t) => { + const root = await checkout(t) + const metadata = path.join(root, ".git", "modules", "parent", "modules", "child") + await mkdir(metadata, { recursive: true }) + await writeFile(path.join(metadata, "config"), authorization) + + await assert.rejects(validateCheckout(root)) + }) + + it("rejects authentication in worktree-specific config", async (t) => { + const root = await checkout(t) + await writeFile( + path.join(root, ".git", "config"), + "[core]\nrepositoryformatversion = 1\n[extensions]\nworktreeConfig = true\n", + ) + await writeFile(path.join(root, ".git", "config.worktree"), authorization) + + await assert.rejects(validateCheckout(root)) + }) + + it("fails closed when an include can hide additional configuration", async (t) => { + const root = await checkout(t) + await writeFile(path.join(root, ".git", "config"), '[include]\npath = "../hidden-config"\n') + await writeFile(path.join(root, "hidden-config"), "[core]\nbare = false\n") + + await assert.rejects(validateCheckout(root)) + }) + + it("fails the CLI for malformed Git config without exposing its diagnostic input", async (t) => { + const root = await checkout(t) + await writeFile(path.join(root, ".git", "config"), `${authorization}[broken\n`) + + const result = spawnSync(tsx, [script, "--root", root], { encoding: "utf8", cwd: root }) + + assert.ifError(result.error) + assert.equal(result.status, 1) + assert.ok(`${result.stdout}${result.stderr}`.trim().length > 0) + assert.ok(!`${result.stdout}${result.stderr}`.includes(secret)) + }) +}) diff --git a/actions/build/scripts/validate-checkout/index.mjs b/actions/build/scripts/validate-checkout/index.ts similarity index 95% rename from actions/build/scripts/validate-checkout/index.mjs rename to actions/build/scripts/validate-checkout/index.ts index 0367a499..1652aaa6 100644 --- a/actions/build/scripts/validate-checkout/index.mjs +++ b/actions/build/scripts/validate-checkout/index.ts @@ -12,13 +12,13 @@ const unsafeCheckout = "Use actions/checkout with persist-credentials: false, including nested checkouts and submodules." /** Rejects persisted Git authentication before repository-controlled scripts run. */ -export async function validateCheckout(root) { +export async function validateCheckout(root: string): Promise { root = await realpath(root) const pending = [{ directory: root, metadata: false }] const visited = new Set() - while (pending.length > 0) { - const { directory, metadata } = pending.pop() + for (let current = pending.pop(); current; current = pending.pop()) { + const { directory, metadata } = current const identity = `${metadata}:${directory}` if (visited.has(identity)) continue diff --git a/actions/build/tsconfig.json b/actions/build/tsconfig.json new file mode 100644 index 00000000..6715f64f --- /dev/null +++ b/actions/build/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2024", + "module": "NodeNext", + "strict": true, + "allowJs": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "noEmit": true, + "types": ["node"] + }, + "include": ["scripts/validate-checkout/**/*.ts"] +} From 86ad214b50b25f306b49d69532819a20f0927c30 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:03:01 +0000 Subject: [PATCH 3/3] refactor(build): use native TypeScript execution Remove tsx and run checkout validation before dependency installation. Keep syntax compatible with Node's native type stripping. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/build/action.yml | 12 +- actions/build/package-lock.json | 492 +----------------- actions/build/package.json | 5 +- .../scripts/validate-checkout/index.test.ts | 8 +- actions/build/tsconfig.json | 1 + 5 files changed, 15 insertions(+), 503 deletions(-) diff --git a/actions/build/action.yml b/actions/build/action.yml index 37e5d378..b2772a27 100644 --- a/actions/build/action.yml +++ b/actions/build/action.yml @@ -9,19 +9,19 @@ runs: with: node-version: 24 - - name: Install action dependencies - run: npm ci --ignore-scripts --omit=dev - shell: bash - working-directory: ${{ github.action_path }} - - name: Check checkout credentials - run: '"$GITHUB_ACTION_PATH/node_modules/.bin/tsx" --tsconfig "$GITHUB_ACTION_PATH/tsconfig.json" "$GITHUB_ACTION_PATH/scripts/validate-checkout/index.ts" --root "$GITHUB_WORKSPACE"' + run: node "$GITHUB_ACTION_PATH/scripts/validate-checkout/index.ts" --root "$GITHUB_WORKSPACE" shell: bash - name: Check required npm files and build script run: node "$GITHUB_ACTION_PATH/scripts/validate-repository/index.mjs" --root "$GITHUB_WORKSPACE" shell: bash + - name: Install browser-check dependencies + run: npm ci --ignore-scripts --omit=dev + shell: bash + working-directory: ${{ github.action_path }} + - name: Build prototype as a non-root user in a Node container run: | docker run --rm \ diff --git a/actions/build/package-lock.json b/actions/build/package-lock.json index 6d43f2ff..f6bc0cc8 100644 --- a/actions/build/package-lock.json +++ b/actions/build/package-lock.json @@ -9,8 +9,7 @@ "version": "1.0.0", "dependencies": { "playwright": "1.64.1", - "pngjs": "7.0.0", - "tsx": "4.23.15" + "pngjs": "7.0.0" }, "devDependencies": { "@eslint/js": "10.0.1", @@ -51,422 +50,6 @@ "keyv": "^5.6.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -1454,47 +1037,6 @@ "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1764,20 +1306,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2199,24 +1727,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/tsx": { - "version": "4.23.15", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", - "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/actions/build/package.json b/actions/build/package.json index 697041d5..fb92b11a 100644 --- a/actions/build/package.json +++ b/actions/build/package.json @@ -8,12 +8,11 @@ }, "scripts": { "lint": "tsc --noEmit && eslint . && prettier --check .", - "test": "node --import tsx --test --test-concurrency=1 scripts/*/index.test.mjs scripts/*/index.test.ts" + "test": "node --test --test-concurrency=1 scripts/*/index.test.mjs scripts/*/index.test.ts" }, "dependencies": { "playwright": "1.64.1", - "pngjs": "7.0.0", - "tsx": "4.23.15" + "pngjs": "7.0.0" }, "devDependencies": { "@eslint/js": "10.0.1", diff --git a/actions/build/scripts/validate-checkout/index.test.ts b/actions/build/scripts/validate-checkout/index.test.ts index f8ae85fb..fa807ff3 100644 --- a/actions/build/scripts/validate-checkout/index.test.ts +++ b/actions/build/scripts/validate-checkout/index.test.ts @@ -10,7 +10,6 @@ import { temporaryDirectory } from "../../test/temporary-directory/index.mjs" import { validateCheckout } from "./index.ts" const actionRoot = fileURLToPath(new URL("../../", import.meta.url)) -const tsx = path.join(actionRoot, "node_modules", ".bin", "tsx") const script = fileURLToPath(new URL("./index.ts", import.meta.url)) const secret = "dummy-checkout-credential-do-not-log" const authorization = `[http]\nextraHeader = AUTHORIZATION: basic ${secret}\n` @@ -49,7 +48,7 @@ describe("validateCheckout()", () => { it("runs the action guard before npm lifecycle and build scripts without leaking authorization", async (t) => { const action = await readFile(path.join(actionRoot, "action.yml"), "utf8") - const guard = action.match(/run: '([^'\n]+validate-checkout[^'\n]+)'/) + const guard = action.match(/run: (node [^\n]+validate-checkout[^\n]+)/) assert.ok(guard, "The action must run checkout validation") assert.ok(typeof guard.index === "number") const guardCommand = guard[1] @@ -156,7 +155,10 @@ describe("validateCheckout()", () => { const root = await checkout(t) await writeFile(path.join(root, ".git", "config"), `${authorization}[broken\n`) - const result = spawnSync(tsx, [script, "--root", root], { encoding: "utf8", cwd: root }) + const result = spawnSync(process.execPath, [script, "--root", root], { + encoding: "utf8", + cwd: root, + }) assert.ifError(result.error) assert.equal(result.status, 1) diff --git a/actions/build/tsconfig.json b/actions/build/tsconfig.json index 6715f64f..d7225393 100644 --- a/actions/build/tsconfig.json +++ b/actions/build/tsconfig.json @@ -3,6 +3,7 @@ "target": "ES2024", "module": "NodeNext", "strict": true, + "erasableSyntaxOnly": true, "allowJs": true, "allowImportingTsExtensions": true, "skipLibCheck": true,