From cb5b5b29fc72efe8d6ae977a93295570c15796f9 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 12 Oct 2026 15:06:20 -0400 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20repository?= =?UTF-8?q?=20code=20and=20reject=20new=20dead=20code=20in=20lint=20(#903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `deno task lint:dead-code` runs Knip over the repository's import graph, and `lint` chains it after oxlint and oxfmt, so both `deno task lint` and the `pnpm lint` the required CI job runs fail on a source file nothing imports, an exported value nothing uses, or an exported type nothing uses — named by path and symbol. The configuration in `knip.json` models what nothing in the repository imports *by design*: the published package exports, the runtime CLI entrypoints, every test, the standalone scripts, the browser entry, the site's routes and islands, the runnable examples, and the fixtures and components a document names or a test spawns. Everything else is implementation, and no implementation directory is declared an entry point to silence a finding. Vendored snapshots, generated npm and Fresh output, declaration files and the deliberately malformed fixtures stay out. The audited candidates are all gone: the `src/output/mod.ts` barrel whose two callers already import the middleware directly, 31 unreferenced declarations, eight unused aliases and re-exports, and ten unused type exports. None was retained — each had no consumer, and the eight aliases each have an underlying implementation that still does. Removing them revealed two more: `validateOutputPlacement`, whose only reference anywhere was the re-export above it, and `useRecordedEnvironment`, whose only caller was the dead `useEnvironment` beside it. Both are gone too. `gitPlugin`/`default` stays, and duplicate exports are not a removal criterion. Three pieces of prose the removals untrue are corrected: the specification's file inventory no longer lists the deleted barrel, the comment in `generated-xmd.ts` states its reason directly rather than by contrast with a reader that no longer exists, and `XMD_ARTIFACT_IDENTITY_DOMAIN`'s comment no longer promises separation from a second format whose constants were themselves the dead code. `scripts/tests/dead-code.test.ts` proves the gate by mutation in a clone of `HEAD` with this checkout's dependencies linked in: an unreferenced source file, an unused export on a module the graph already reaches, and an unused exported type are each rejected and each named, and removing the mutation restores success. The negative case adds a public package export, a site route and island, a script, a smoke-test module, a package example, and a test fixture and component, and requires silence — which is what distinguishes a configuration that finds dead code from one that reports nothing. --- .github/workflows/ci.yml | 8 +- AGENTS.md | 23 +- deno.lock | 444 ++++++++++++-- knip.json | 105 ++++ package.json | 4 +- packages/cli/src/plan.ts | 9 - packages/cli/src/release-targets.ts | 5 - packages/cli/src/repl/application.ts | 13 +- packages/cli/src/workflow.ts | 5 - packages/cli/tests/support/plan-harness.ts | 38 -- packages/core/src/agent/launch-request.ts | 10 +- packages/core/src/body-structure.ts | 23 - packages/core/src/component-api.ts | 2 - packages/core/src/documentation-index.ts | 23 - packages/core/src/expand.ts | 3 +- packages/core/src/expansion.ts | 16 - packages/core/src/fetch-request.ts | 30 - packages/core/src/generated-xmd.ts | 6 +- packages/core/src/output/mod.ts | 5 - .../git/src/composition/components/Dir.ts | 2 - .../composition/components/IssueTracker.ts | 3 - .../composition/pull-request-read-records.ts | 23 - packages/git/src/composition/selection.ts | 30 - .../src/deno/composition/authentication.ts | 10 - .../deno/composition/pull-request-reads.ts | 4 - .../git/src/deno/run-composition/checkouts.ts | 7 +- packages/git/src/issue/effect.ts | 2 - packages/git/src/issue/records.ts | 3 - packages/git/tests/support/github.ts | 29 - packages/testing/src/test-api.ts | 1 - packages/web/src/json.ts | 6 - packages/web/tests/component-support.ts | 17 - .../workflow/src/deno/artifact/manifest.ts | 10 +- packages/workflow/src/deno/artifact/mod.ts | 7 - packages/workflow/src/deno/artifact/schema.ts | 7 - packages/workflow/src/deno/schema.ts | 23 - packages/workflow/src/deno/transitions.ts | 9 +- .../src/deno/workspace/logical-path.ts | 6 - packages/workflow/src/journal.ts | 19 - packages/workflow/src/storage/record.ts | 25 - pnpm-lock.yaml | 554 ++++++++++++++++++ scripts/tests/ci-workflow.test.ts | 66 +++ scripts/tests/dead-code.test.ts | 254 ++++++++ specs/executable-mdx-spec.md | 1 - 44 files changed, 1414 insertions(+), 476 deletions(-) create mode 100644 knip.json delete mode 100644 packages/core/src/output/mod.ts create mode 100644 scripts/tests/dead-code.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a9deb9bd..49a15f114 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,13 @@ jobs: - run: pnpm install - - name: Lint + Format + # `pnpm lint` is oxlint, oxfmt and the repository-wide dead-code check, + # chained with `&&`, so a finding from any of the three reddens this + # required job. The dead-code check is a step of this job rather than a + # job of its own because it needs exactly what lint already prepared, and + # a second job would pay for another checkout and install to learn the + # same thing. + - name: Lint + Format + dead code run: pnpm lint # The corpus is partitioned by measured per-file weight, so the shards finish diff --git a/AGENTS.md b/AGENTS.md index d2a9bb499..7fb47e00b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -181,7 +181,7 @@ keep their own task names: ```bash deno task verify # the shared-state interference proof deno task verify:clean # the same proof, from a clean checkout, after the offline builds -deno task lint # oxlint + oxfmt --check +deno task lint # oxlint + oxfmt --check + the dead-code check deno task check # typecheck deno task test # the complete Deno suite pnpm test:node # the complete suite under Node @@ -291,8 +291,10 @@ composability runs them. The complete battery consists of: -1. **Lint + Format**: `deno task lint` (runs `oxlint` + `oxfmt --check`) — must - produce 0 errors. Run `deno task fmt` to auto-fix formatting. +1. **Lint + Format + dead code**: `deno task lint` (runs `oxlint`, + `oxfmt --check` and `knip`) — must produce 0 errors. Run `deno task fmt` to + auto-fix formatting, and `deno task lint:dead-code` for the dead-code check + alone. 2. **Typecheck**: `deno task check` — must produce no errors 3. **Tests**: `deno task test` — the full Deno suite; CI also runs the full corpus under Node and Bun @@ -347,6 +349,21 @@ here: lint task's command line instead — the rule tests in `scripts/tests/` lint through the repository config, and an `ignorePatterns` entry there stops them seeing their own fixtures. +- The **dead-code** half of `lint` is Knip, pinned in `package.json` and + configured in `knip.json`, exposed on its own as `deno task lint:dead-code`. + It reads the repository's import graph and fails on a source file nothing + imports, an exported value nothing uses, and an exported type nothing uses, + naming the path and the symbol. What keeps it honest is the entry-point list: + the published package exports, the runtime CLI entrypoints, every test, the + standalone scripts, the browser entry, the site's routes and islands, the + runnable examples, and the fixtures and components a document names or a test + spawns are entry points — nothing in the repository imports them and none of + them is dead. Anything else is implementation, and an implementation module + never becomes an entry point to silence a finding. Duplicate exports are out + of scope: `gitPlugin`/`default` is one value declared two ways, not dead code. + `scripts/tests/dead-code.test.ts` proves the gate by mutating a clone of + `HEAD`, which is what distinguishes a configuration that finds dead code from + one that reports nothing. ### Measuring the corpus diff --git a/deno.lock b/deno.lock index 0135003e9..011bf7f1a 100644 --- a/deno.lock +++ b/deno.lock @@ -38,7 +38,7 @@ "jsr:@std/uuid@^1.0.9": "1.1.1", "jsr:@ts-morph/bootstrap@0.27": "0.27.0", "jsr:@ts-morph/common@0.27": "0.27.0", - "npm:@agentclientprotocol/sdk@1.3.0": "1.3.0_zod@4.4.3", + "npm:@agentclientprotocol/sdk@1.3.0": "1.3.0_zod@4.6.6", "npm:@babel/core@^7.28.0": "7.29.7", "npm:@babel/preset-react@^7.27.1": "7.29.7_@babel+core@7.29.7", "npm:@bomb.sh/tty@0.9.0": "0.9.0", @@ -58,20 +58,20 @@ "npm:@fontsource/montserrat@5.3.0": "5.3.0", "npm:@fontsource/space-mono@5.3.0": "5.3.0", "npm:@opentelemetry/api@^1.9.0": "1.9.1", - "npm:@preact/signals@^2.5.1": "2.10.0_preact@10.29.7__preact-render-to-string@6.7.0_preact-render-to-string@6.7.0__preact@10.29.7", - "npm:@preact/signals@^2.9.0": "2.10.0_preact@10.29.7__preact-render-to-string@6.7.0_preact-render-to-string@6.7.0__preact@10.29.7", - "npm:@prefresh/vite@^2.4.8": "2.4.12_preact@10.29.7__preact-render-to-string@6.7.0_vite@7.3.6__@types+node@24.13.3__tsx@4.23.1_preact-render-to-string@6.7.0__preact@10.29.7", + "npm:@preact/signals@^2.5.1": "2.10.0_preact@10.29.7_preact-render-to-string@6.7.0", + "npm:@preact/signals@^2.9.0": "2.10.0_preact@10.29.7_preact-render-to-string@6.7.0", + "npm:@prefresh/vite@^2.4.8": "2.4.12_preact@10.29.7_vite@7.3.6_preact-render-to-string@6.7.0", "npm:@remix-run/node-fetch-server@0.12": "0.12.0", - "npm:@rjsf/core@6.7.1": "6.7.1_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0", - "npm:@rjsf/shadcn@6.7.1": "6.7.1_@rjsf+core@6.7.1__@rjsf+utils@6.7.1___react@19.2.0__react@19.2.0_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0_react-dom@19.2.0__react@19.2.0_tailwindcss@4.3.3", + "npm:@rjsf/core@6.7.1": "6.7.1_@rjsf+utils@6.7.1_react@19.2.0", + "npm:@rjsf/shadcn@6.7.1": "6.7.1_@rjsf+core@6.7.1_@rjsf+utils@6.7.1_react@19.2.0_react-dom@19.2.0_tailwindcss@4.3.3", "npm:@rjsf/utils@6.7.1": "6.7.1_react@19.2.0", - "npm:@rjsf/validator-ajv8@6.7.1": "6.7.1_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0", + "npm:@rjsf/validator-ajv8@6.7.1": "6.7.1_@rjsf+utils@6.7.1_react@19.2.0", "npm:@secretlint/core@13.0.4": "13.0.4", "npm:@secretlint/profiler@13.0.4": "13.0.4", "npm:@secretlint/secretlint-rule-preset-recommend@13.0.4": "13.0.4", "npm:@secretlint/types@13.0.4": "13.0.4", "npm:@standard-schema/spec@1": "1.1.0", - "npm:@tailwindcss/vite@^4.1.12": "4.3.3_vite@7.3.6__@types+node@24.13.3__tsx@4.23.1_@types+node@24.13.3_tsx@4.23.1", + "npm:@tailwindcss/vite@^4.1.12": "4.3.3_vite@7.3.6_@types+node@24.13.3_tsx@4.23.1", "npm:@types/babel__core@^7.20.5": "7.20.5", "npm:@types/node@22": "22.19.15", "npm:@types/node@^24.5.2": "24.13.3", @@ -87,6 +87,7 @@ "npm:esbuild@~0.25.5": "0.25.12", "npm:expect@30": "30.3.0", "npm:gray-matter@^4.0.3": "4.0.3", + "npm:knip@6.41.0": "6.41.0", "npm:magic-string@~0.30.21": "0.30.21", "npm:marked-terminal@^7.3.0": "7.3.0_marked@17.0.4", "npm:marked@^17.0.4": "17.0.4", @@ -114,8 +115,8 @@ "npm:unist-util-select@5": "5.1.0", "npm:vite@^7.1.3": "7.3.6_@types+node@24.13.3_tsx@4.23.1", "npm:vite@^7.1.4": "7.3.6_@types+node@24.13.3_tsx@4.23.1", - "npm:zod@4": "4.4.3", - "npm:zod@^4.3.6": "4.4.3" + "npm:zod@4": "4.6.6", + "npm:zod@^4.3.6": "4.6.6" }, "jsr": { "@david/code-block-writer@13.0.3": { @@ -281,7 +282,7 @@ } }, "npm": { - "@agentclientprotocol/sdk@1.3.0_zod@4.4.3": { + "@agentclientprotocol/sdk@1.3.0_zod@4.6.6": { "integrity": "sha512-i3h/efaeuMUFAO1HSfo97QZQnnvMd7wWBYtBsdL6UMZg3a78sk3Ffya5Xu7C7tYsXomXoDXJBAzQF2PcFKAhIQ==", "dependencies": [ "zod" @@ -605,6 +606,25 @@ "effection" ] }, + "@emnapi/core@1.11.2": { + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "dependencies": [ + "@emnapi/wasi-threads", + "tslib" + ] + }, + "@emnapi/runtime@1.11.2": { + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "dependencies": [ + "tslib" + ] + }, + "@emnapi/wasi-threads@1.2.2": { + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dependencies": [ + "tslib" + ] + }, "@esbuild/aix-ppc64@0.25.12": { "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "os": ["aix"], @@ -1008,7 +1028,7 @@ "@floating-ui/utils" ] }, - "@floating-ui/react-dom@2.1.9_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@floating-ui/react-dom@2.1.9_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", "dependencies": [ "@floating-ui/dom", @@ -1160,12 +1180,217 @@ "os": ["win32"], "cpu": ["x64"] }, + "@napi-rs/wasm-runtime@1.2.5_@emnapi+core@1.11.2_@emnapi+runtime@1.11.2": { + "integrity": "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util" + ] + }, "@neophi/sieve-cache@1.5.0": { "integrity": "sha512-9T3nD5q51X1d4QYW6vouKW9hBSb2Tb/wB/2XoTr4oP5SCGtp3a7aTHHewQFylred1B21/Bhev6gy4x01FPBcbQ==" }, "@opentelemetry/api@1.9.1": { "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==" }, + "@oxc-parser/binding-android-arm-eabi@0.153.0": { + "integrity": "sha512-BcWu25dMwMjHpaDCEidmpAi7rGg1qZoihJeUUH9bD55pVZ0z4Wa5CQgqSTo0R41H81oozisQKW3WqvAN2m77IQ==", + "os": ["android"], + "cpu": ["arm"] + }, + "@oxc-parser/binding-android-arm64@0.153.0": { + "integrity": "sha512-9l5rdp2DhRqVriBHsNpu5cEf+hizZ4B58rt93HzyQEBUgZiC6qe2oFpaAAnXGCxHGnQExI1s7S2G0QhFacUZ6w==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-darwin-arm64@0.153.0": { + "integrity": "sha512-EcpsyRXHhENDpekpG4QNaSZj2F/BAz4Gh4o89zZwNss86R8KyRSQ6sXbAj74Xyeb4z3j/yBZZ6joQZhraIWyfw==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-darwin-x64@0.153.0": { + "integrity": "sha512-hlj80HUQy07/2lYp6rmxH+3u+H2ik3rjLR52CUdPApxmZCNxASDUf/+ykTzAHS/fehftmVMATKvPaWmuLHuasg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@oxc-parser/binding-freebsd-x64@0.153.0": { + "integrity": "sha512-fWRcv2pMSh1qtez46MOu8vuUJ5fiaQZPHSphbWulbL7SHBMKfRgZl2PzjXZYQJbq7NEXyXbgIuIuUm+/Rmkv8w==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@oxc-parser/binding-linux-arm-gnueabihf@0.153.0": { + "integrity": "sha512-paZeUK1GXShl/bOJQET1/8AQxayFeuN4SIWAkgTRxE7ZQwv9uYSB2n0648smygP22N7jcI3zxAm0Z+ALNiIKxg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@oxc-parser/binding-linux-arm-musleabihf@0.153.0": { + "integrity": "sha512-qFPp5NwMf4K1C0O+DCVGmcbOaYyYAq98Rvv9p4VSfb777iUwlJCqMUxf6QG9N5f5ls4TEX6LitOZHi+6GL6rYQ==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@oxc-parser/binding-linux-arm64-gnu@0.153.0": { + "integrity": "sha512-oufEhZCX7saeo27Logra8cvBR1jQ/yw2zUI2hHmWw3qA/XYqEW31FOvLQBv7DWN32NjVdlcaP11WBBZTJo4k9g==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-linux-arm64-musl@0.153.0": { + "integrity": "sha512-zXYzuAqng85h6+LI3iRm0zeMLX1/K2z41ZAMWxVRgt0D10VvZUaV5q+TEB46KDhoKBpdexoTLC6QoB/EiP2oqQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-linux-ppc64-gnu@0.153.0": { + "integrity": "sha512-J7+NLwLEzdkKM9cKyPHH/AjXXstWOs98KYgLrPPnO4N9MO8EZRBfPNnBcS3S67uMOBkRpUmCl2FhRnWdQ5CbBA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@oxc-parser/binding-linux-riscv64-gnu@0.153.0": { + "integrity": "sha512-sD0K11GQTLWylln54nPMMTtgmBbQrR5It/vMpZpy/fogiw8O2AvIE+9LuWUXddrWk9zvFIHGRcSgFn4IHOfoYg==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@oxc-parser/binding-linux-riscv64-musl@0.153.0": { + "integrity": "sha512-7L66uql2BhvSz51LwfExjOdn6et/ydTiQxl23VApdKzJhTc3TczA1Qt6VR/UcvlQrL2ZbytIJb447vh7p5wmUw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@oxc-parser/binding-linux-s390x-gnu@0.153.0": { + "integrity": "sha512-hXV3T7mNyrPR19rbPYkglkQJtoM/UNo/xwbZeeqdAxEg8PXgb4LXXuxPpGgI4bzRRCWwLUxdDSS58xIyC2u1TA==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@oxc-parser/binding-linux-x64-gnu@0.153.0": { + "integrity": "sha512-Da8Vqs1xdtoDYtEjmozD7wGgJeLBQkMbR0WUBECOSxodyX5DXy8ySlvJWbukyO2Q0+TM9EK+4LT6tbq0jCg4hQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oxc-parser/binding-linux-x64-musl@0.153.0": { + "integrity": "sha512-zGAPxNTIIw6X5WyTRscIFE/oDjpdNtrrl32ing6NOXdGjzRucadnxBilexLdeYQ9YxixF93GujpXyQ0jw7ddSg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oxc-parser/binding-openharmony-arm64@0.153.0": { + "integrity": "sha512-GoHzpqP5XyPIp9DHokoEdCp+K4UPl/h1neFj1uXnUB3pJYyOQ7NOhFVOttNUgCGcj2eywAvu5cRivTOSq4S10g==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-win32-arm64-msvc@0.153.0": { + "integrity": "sha512-xGcy12tO9k7zFbeyQzEuilfWfQpyRq23/fLPfN3wjSWuWv58k46fQT1hsO1KFNykZuVGW9xSJE/z592TmAoSDA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@oxc-parser/binding-win32-ia32-msvc@0.153.0": { + "integrity": "sha512-o1ykUjWncvXX6N5a4KebgLjtD8zRui+f1llMhSGoSC9oP2KOqapRpdwmZ3NcNG0axFouwVKmhGBjd+IhA8BksA==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@oxc-parser/binding-win32-x64-msvc@0.153.0": { + "integrity": "sha512-gxQz7G96Up136fd+2zWrP+o6FYGs9NO4Dyv55IoeJFJUDwLFHcLYY11hhA5wQKs0HoM57Q8esQ3nEVhnCdsjAQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@oxc-project/types@0.153.0": { + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==" + }, + "@oxc-resolver/binding-android-arm-eabi@11.24.2": { + "integrity": "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==", + "os": ["android"], + "cpu": ["arm"] + }, + "@oxc-resolver/binding-android-arm64@11.24.2": { + "integrity": "sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-darwin-arm64@11.24.2": { + "integrity": "sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-darwin-x64@11.24.2": { + "integrity": "sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@oxc-resolver/binding-freebsd-x64@11.24.2": { + "integrity": "sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2": { + "integrity": "sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@oxc-resolver/binding-linux-arm-musleabihf@11.24.2": { + "integrity": "sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@oxc-resolver/binding-linux-arm64-gnu@11.24.2": { + "integrity": "sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-linux-arm64-musl@11.24.2": { + "integrity": "sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-linux-ppc64-gnu@11.24.2": { + "integrity": "sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@oxc-resolver/binding-linux-riscv64-gnu@11.24.2": { + "integrity": "sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@oxc-resolver/binding-linux-riscv64-musl@11.24.2": { + "integrity": "sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@oxc-resolver/binding-linux-s390x-gnu@11.24.2": { + "integrity": "sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@oxc-resolver/binding-linux-x64-gnu@11.24.2": { + "integrity": "sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oxc-resolver/binding-linux-x64-musl@11.24.2": { + "integrity": "sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@oxc-resolver/binding-openharmony-arm64@11.24.2": { + "integrity": "sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-wasm32-wasi@11.24.2": { + "integrity": "sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@napi-rs/wasm-runtime" + ], + "cpu": ["wasm32"] + }, + "@oxc-resolver/binding-win32-arm64-msvc@11.24.2": { + "integrity": "sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@oxc-resolver/binding-win32-x64-msvc@11.24.2": { + "integrity": "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==", + "os": ["win32"], + "cpu": ["x64"] + }, "@oxfmt/binding-android-arm-eabi@0.41.0": { "integrity": "sha512-REfrqeMKGkfMP+m/ScX4f5jJBSmVNYcpoDF8vP8f8eYPDuPGZmzp56NIUsYmx3h7f6NzC6cE3gqh8GDWrJHCKw==", "os": ["android"], @@ -1389,7 +1614,7 @@ "@preact/signals-core@1.14.4": { "integrity": "sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==" }, - "@preact/signals@2.10.0_preact@10.29.7__preact-render-to-string@6.7.0_preact-render-to-string@6.7.0__preact@10.29.7": { + "@preact/signals@2.10.0_preact@10.29.7_preact-render-to-string@6.7.0": { "integrity": "sha512-avH1zQLZR6c0DmH3pT/sn4RIzsNDXAue+MEoyS5RIvApKALSU8m05KyBOL8ua21gxeDYp1DclTMcFd+N1zaL1Q==", "dependencies": [ "@preact/signals-core", @@ -1399,7 +1624,7 @@ "@prefresh/babel-plugin@0.5.3": { "integrity": "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ==" }, - "@prefresh/core@1.5.10_preact@10.29.7__preact-render-to-string@6.7.0_preact-render-to-string@6.7.0__preact@10.29.7": { + "@prefresh/core@1.5.10_preact@10.29.7_preact-render-to-string@6.7.0": { "integrity": "sha512-7yPTFbG56sutaFu8krp3B4a200KOFUvrtlllKWRuLjsYXo9UUucHOZRcer+gtgMkFTpv6ob8TGcTwA32bSwa1w==", "dependencies": [ "preact" @@ -1408,7 +1633,7 @@ "@prefresh/utils@1.2.1": { "integrity": "sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==" }, - "@prefresh/vite@2.4.12_preact@10.29.7__preact-render-to-string@6.7.0_vite@7.3.6__@types+node@24.13.3__tsx@4.23.1_preact-render-to-string@6.7.0__preact@10.29.7": { + "@prefresh/vite@2.4.12_preact@10.29.7_vite@7.3.6_preact-render-to-string@6.7.0": { "integrity": "sha512-FY1fzXpUjiuosznMV0YM7XAOPZjB5FIdWS0W24+XnlxYkt9hNAwwsiKYn+cuTEoMtD/ZVazS5QVssBr9YhpCQA==", "dependencies": [ "@babel/core", @@ -1426,7 +1651,7 @@ "@radix-ui/primitive@1.1.7": { "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==" }, - "@radix-ui/react-arrow@1.1.15_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-arrow@1.1.15_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", "dependencies": [ "@radix-ui/react-primitive", @@ -1434,7 +1659,7 @@ "react-dom" ] }, - "@radix-ui/react-checkbox@1.3.11_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-checkbox@1.3.11_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", "dependencies": [ "@radix-ui/primitive", @@ -1448,7 +1673,7 @@ "react-dom" ] }, - "@radix-ui/react-collection@1.1.15_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-collection@1.1.15_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", "dependencies": [ "@radix-ui/react-compose-refs", @@ -1471,7 +1696,7 @@ "react" ] }, - "@radix-ui/react-dialog@1.1.23_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-dialog@1.1.23_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", "dependencies": [ "@radix-ui/primitive", @@ -1499,7 +1724,7 @@ "react" ] }, - "@radix-ui/react-dismissable-layer@1.1.19_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-dismissable-layer@1.1.19_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", "dependencies": [ "@radix-ui/primitive", @@ -1517,7 +1742,7 @@ "react" ] }, - "@radix-ui/react-focus-scope@1.1.16_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-focus-scope@1.1.16_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", "dependencies": [ "@radix-ui/react-compose-refs", @@ -1540,7 +1765,7 @@ "react" ] }, - "@radix-ui/react-label@2.1.15_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-label@2.1.15_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", "dependencies": [ "@radix-ui/react-primitive", @@ -1548,7 +1773,7 @@ "react-dom" ] }, - "@radix-ui/react-popover@1.1.23_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-popover@1.1.23_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", "dependencies": [ "@radix-ui/primitive", @@ -1570,7 +1795,7 @@ "react-remove-scroll" ] }, - "@radix-ui/react-popper@1.3.7_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-popper@1.3.7_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", "dependencies": [ "@floating-ui/react-dom", @@ -1587,7 +1812,7 @@ "react-dom" ] }, - "@radix-ui/react-portal@1.1.17_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-portal@1.1.17_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", "dependencies": [ "@radix-ui/react-primitive", @@ -1596,7 +1821,7 @@ "react-dom" ] }, - "@radix-ui/react-presence@1.1.10_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-presence@1.1.10_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", "dependencies": [ "@radix-ui/react-use-layout-effect", @@ -1604,7 +1829,7 @@ "react-dom" ] }, - "@radix-ui/react-primitive@2.1.10_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-primitive@2.1.10_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", "dependencies": [ "@radix-ui/react-slot", @@ -1612,7 +1837,7 @@ "react-dom" ] }, - "@radix-ui/react-radio-group@1.4.7_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-radio-group@1.4.7_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==", "dependencies": [ "@radix-ui/primitive", @@ -1628,7 +1853,7 @@ "react-dom" ] }, - "@radix-ui/react-roving-focus@1.1.19_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-roving-focus@1.1.19_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", "dependencies": [ "@radix-ui/primitive", @@ -1646,7 +1871,7 @@ "react-dom" ] }, - "@radix-ui/react-select@2.3.7_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-select@2.3.7_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", "dependencies": [ "@radix-ui/number", @@ -1675,7 +1900,7 @@ "react-remove-scroll" ] }, - "@radix-ui/react-separator@1.1.15_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-separator@1.1.15_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", "dependencies": [ "@radix-ui/react-primitive", @@ -1683,7 +1908,7 @@ "react-dom" ] }, - "@radix-ui/react-slider@1.4.7_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-slider@1.4.7_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==", "dependencies": [ "@radix-ui/number", @@ -1762,7 +1987,7 @@ "react" ] }, - "@radix-ui/react-visually-hidden@1.2.11_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "@radix-ui/react-visually-hidden@1.2.11_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", "dependencies": [ "@radix-ui/react-primitive", @@ -1782,7 +2007,7 @@ "@remix-run/node-fetch-server@0.12.0": { "integrity": "sha512-oeg8w8aJJSuq1fCx85jCkcgTfI6On7sKwWVSO4/OW5AvTBuosAIwnuBd/LYeU/I7lYPOTW2NXhUfyfpyeexs4w==" }, - "@rjsf/core@6.7.1_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0": { + "@rjsf/core@6.7.1_@rjsf+utils@6.7.1_react@19.2.0": { "integrity": "sha512-/CQfIGUzcXceBNRhEH3wsTvxcT8dMrjPLXhYSfcJUTftKxOCdisqi2wFwt7LOyIvFvzHUxag0r0xXs/MXS9jmA==", "dependencies": [ "@rjsf/utils", @@ -1793,7 +2018,7 @@ "react" ] }, - "@rjsf/shadcn@6.7.1_@rjsf+core@6.7.1__@rjsf+utils@6.7.1___react@19.2.0__react@19.2.0_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0_react-dom@19.2.0__react@19.2.0_tailwindcss@4.3.3": { + "@rjsf/shadcn@6.7.1_@rjsf+core@6.7.1_@rjsf+utils@6.7.1_react@19.2.0_react-dom@19.2.0_tailwindcss@4.3.3": { "integrity": "sha512-SAo1PtA8PgrBrixxEzPkpg0KlzYbW+NPeaIJ5Gi9CYv/oUc6w10FAS/4h2yj3JBlKFFGnSMO4uvWPX1er9MRyA==", "dependencies": [ "@radix-ui/react-checkbox", @@ -1834,7 +2059,7 @@ "react-is@18.3.1" ] }, - "@rjsf/validator-ajv8@6.7.1_@rjsf+utils@6.7.1__react@19.2.0_react@19.2.0": { + "@rjsf/validator-ajv8@6.7.1_@rjsf+utils@6.7.1_react@19.2.0": { "integrity": "sha512-oG9reR8VgUUTxfsO8WybZWTjKs6SLUdhmUCp55SXmJvwVbeKZ+Mz4SI+y+T1Mdpbm1kLZWQPRyF2Md97soWXkw==", "dependencies": [ "@rjsf/utils", @@ -2091,7 +2316,7 @@ "@tailwindcss/oxide-win32-x64-msvc" ] }, - "@tailwindcss/vite@4.3.3_vite@7.3.6__@types+node@24.13.3__tsx@4.23.1_@types+node@24.13.3_tsx@4.23.1": { + "@tailwindcss/vite@4.3.3_vite@7.3.6_@types+node@24.13.3_tsx@4.23.1": { "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", "dependencies": [ "@tailwindcss/node", @@ -2100,6 +2325,12 @@ "vite" ] }, + "@tybys/wasm-util@0.10.4": { + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "dependencies": [ + "tslib" + ] + }, "@types/babel__core@7.20.5": { "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dependencies": [ @@ -2409,7 +2640,7 @@ "clsx@2.1.1": { "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" }, - "cmdk@1.1.1_react@19.2.0_react-dom@19.2.0__react@19.2.0": { + "cmdk@1.1.1_react@19.2.0_react-dom@19.2.0": { "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", "dependencies": [ "@radix-ui/react-compose-refs", @@ -2439,7 +2670,8 @@ "integrity": "sha512-4bxK3+L+FHr9Xm/d69Syvvpvkj7lj7a4zz3B+tchuohg5WKeudyBS+4Oob5Zdgoh8I7+n2lj0lfa8I6cUXfcEg==", "dependencies": [ "@standard-schema/spec" - ] + ], + "deprecated": true }, "convert-source-map@2.0.0": { "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" @@ -2685,15 +2917,29 @@ "reusify" ] }, - "fdir@6.5.0_picomatch@4.0.5": { + "fd-package-json@2.0.0": { + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dependencies": [ + "walk-up-path" + ] + }, + "fdir@6.5.0_picomatch@4.0.7": { "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dependencies": [ - "picomatch@4.0.5" + "picomatch@4.0.7" ], "optionalPeers": [ - "picomatch@4.0.5" + "picomatch@4.0.7" ] }, + "formatly@0.11.4": { + "integrity": "sha512-TeCC10lP8KCgoX+Fe5lryILIT0/Dy+nP0OSVnsuB5CwXoLuOdqxCYlPFF1inR8S/zTy1ec7TjhS2TeRKWzzImA==", + "dependencies": [ + "fd-package-json", + "package-manager-detector" + ], + "bin": true + }, "fsevents@2.3.3": { "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "os": ["darwin"], @@ -2708,6 +2954,12 @@ "get-nonce@1.0.1": { "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" }, + "get-tsconfig@4.14.3": { + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, "graceful-fs@4.2.11": { "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, @@ -2800,7 +3052,7 @@ "@types/stack-utils", "chalk@4.1.2", "graceful-fs", - "picomatch@4.0.5", + "picomatch@4.0.7", "pretty-format", "slash", "stack-utils" @@ -2825,7 +3077,7 @@ "chalk@4.1.2", "ci-info", "graceful-fs", - "picomatch@4.0.5" + "picomatch@4.0.7" ] }, "jiti@2.7.0": { @@ -2860,6 +3112,24 @@ "kind-of@6.0.3": { "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "knip@6.41.0": { + "integrity": "sha512-DrhQlIuV6xScd5Q+WTF1znd1w41QzxFGCtAHXCajsH/ndGMotHoNYbdXbuFtzW3mIOmniHtZ7zCtvnQkUmJtQQ==", + "dependencies": [ + "formatly", + "get-tsconfig", + "jiti", + "oxc-parser", + "oxc-resolver", + "picomatch@4.0.7", + "smol-toml", + "strip-json-comments", + "tinyglobby", + "unbash", + "yaml", + "zod" + ], + "bin": true + }, "lightningcss-android-arm64@1.32.0": { "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "os": ["android"], @@ -3314,6 +3584,57 @@ "ordered-binary@1.6.1": { "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==" }, + "oxc-parser@0.153.0": { + "integrity": "sha512-cEnpyyV1OJRXv++SFNw0z8i+2o/cr88rVLaOYJafELDoHM5yHvCAbwGRLvxuany6SNLcbblSMUyZaGzH+quSYg==", + "dependencies": [ + "@oxc-project/types" + ], + "optionalDependencies": [ + "@oxc-parser/binding-android-arm-eabi", + "@oxc-parser/binding-android-arm64", + "@oxc-parser/binding-darwin-arm64", + "@oxc-parser/binding-darwin-x64", + "@oxc-parser/binding-freebsd-x64", + "@oxc-parser/binding-linux-arm-gnueabihf", + "@oxc-parser/binding-linux-arm-musleabihf", + "@oxc-parser/binding-linux-arm64-gnu", + "@oxc-parser/binding-linux-arm64-musl", + "@oxc-parser/binding-linux-ppc64-gnu", + "@oxc-parser/binding-linux-riscv64-gnu", + "@oxc-parser/binding-linux-riscv64-musl", + "@oxc-parser/binding-linux-s390x-gnu", + "@oxc-parser/binding-linux-x64-gnu", + "@oxc-parser/binding-linux-x64-musl", + "@oxc-parser/binding-openharmony-arm64", + "@oxc-parser/binding-win32-arm64-msvc", + "@oxc-parser/binding-win32-ia32-msvc", + "@oxc-parser/binding-win32-x64-msvc" + ] + }, + "oxc-resolver@11.24.2": { + "integrity": "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==", + "optionalDependencies": [ + "@oxc-resolver/binding-android-arm-eabi", + "@oxc-resolver/binding-android-arm64", + "@oxc-resolver/binding-darwin-arm64", + "@oxc-resolver/binding-darwin-x64", + "@oxc-resolver/binding-freebsd-x64", + "@oxc-resolver/binding-linux-arm-gnueabihf", + "@oxc-resolver/binding-linux-arm-musleabihf", + "@oxc-resolver/binding-linux-arm64-gnu", + "@oxc-resolver/binding-linux-arm64-musl", + "@oxc-resolver/binding-linux-ppc64-gnu", + "@oxc-resolver/binding-linux-riscv64-gnu", + "@oxc-resolver/binding-linux-riscv64-musl", + "@oxc-resolver/binding-linux-s390x-gnu", + "@oxc-resolver/binding-linux-x64-gnu", + "@oxc-resolver/binding-linux-x64-musl", + "@oxc-resolver/binding-openharmony-arm64", + "@oxc-resolver/binding-wasm32-wasi", + "@oxc-resolver/binding-win32-arm64-msvc", + "@oxc-resolver/binding-win32-x64-msvc" + ] + }, "oxfmt@0.41.0": { "integrity": "sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg==", "dependencies": [ @@ -3385,6 +3706,9 @@ ], "bin": true }, + "package-manager-detector@1.9.0": { + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==" + }, "parse5-htmlparser2-tree-adapter@6.0.1": { "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dependencies": [ @@ -3406,8 +3730,8 @@ "picomatch@2.3.2": { "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" }, - "picomatch@4.0.5": { - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==" + "picomatch@4.0.7": { + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==" }, "postcss@8.5.23": { "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", @@ -3557,6 +3881,9 @@ "require-from-string@2.0.2": { "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, "reusify@1.1.0": { "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" }, @@ -3645,6 +3972,9 @@ "slash@3.0.0": { "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, + "smol-toml@1.9.1": { + "integrity": "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==" + }, "source-map-js@1.2.1": { "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, @@ -3692,6 +4022,9 @@ "strip-bom-string@1.0.0": { "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" }, + "strip-json-comments@5.0.3": { + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==" + }, "structured-source@4.0.0": { "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", "dependencies": [ @@ -3763,7 +4096,7 @@ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dependencies": [ "fdir", - "picomatch@4.0.5" + "picomatch@4.0.7" ] }, "tinypool@2.1.0": { @@ -3792,6 +4125,9 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "bin": true }, + "unbash@5.0.0": { + "integrity": "sha512-RYNm/ISFtflWpIcXTPHHLJZ0mqzInav+L9ke9PcL+hHsSf9+Fd/syPb614Mlqs/O3AYgjnvxDOFl0a+zVqi1/w==" + }, "undici-types@6.21.0": { "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, @@ -3904,7 +4240,7 @@ "@types/node@24.13.3", "esbuild@0.28.1", "fdir", - "picomatch@4.0.5", + "picomatch@4.0.7", "postcss", "rollup", "tinyglobby", @@ -3919,6 +4255,9 @@ ], "bin": true }, + "walk-up-path@4.0.0": { + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==" + }, "weak-lru-cache@1.2.2": { "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" }, @@ -3943,6 +4282,10 @@ "yallist@3.1.1": { "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, + "yaml@2.9.1": { + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "bin": true + }, "yargs-parser@20.2.9": { "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" }, @@ -3958,8 +4301,8 @@ "yargs-parser" ] }, - "zod@4.4.3": { - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==" + "zod@4.6.6": { + "integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==" }, "zwitch@2.0.4": { "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" @@ -4020,6 +4363,7 @@ "npm:effection@4.1.0", "npm:expect@30", "npm:gray-matter@^4.0.3", + "npm:knip@6.41.0", "npm:magic-string@~0.30.21", "npm:marked-terminal@^7.3.0", "npm:marked@^17.0.4", diff --git a/knip.json b/knip.json new file mode 100644 index 000000000..e7c33dfe7 --- /dev/null +++ b/knip.json @@ -0,0 +1,105 @@ +{ + "$schema": "https://unpkg.com/knip@6/schema.json", + "ignore": [ + "**/vendor/**", + "**/npm/**", + "scripts/tests/fixtures/**", + "**/_fresh/**", + "**/*.d.ts" + ], + "workspaces": { + ".": { + "entry": [ + "scripts/*.ts", + "scripts/tests/**/*.test.ts", + "components/**/*.ts", + "smoke-test/**/*.ts", + "site/main.ts", + "site/client.ts", + "site/vite.config.ts", + "site/routes/**/*.{ts,tsx}", + "site/islands/**/*.{ts,tsx}" + ], + "project": [ + "scripts/**/*.ts", + "components/**/*.ts", + "smoke-test/**/*.ts", + "site/**/*.{ts,tsx}" + ], + "paths": { + "@executablemd/acp": ["packages/acp/mod.ts"], + "@executablemd/acp/embedded-adapters": ["packages/acp/embedded-adapters.ts"], + "@executablemd/cli": ["packages/cli/src/deno.ts"], + "@executablemd/code-review-agent": ["packages/code-review-agent/mod.ts"], + "@executablemd/code-review-agent/review-components": [ + "packages/code-review-agent/src/review-components.ts" + ], + "@executablemd/core": ["packages/core/mod.ts"], + "@executablemd/core/api": ["packages/core/api.ts"], + "@executablemd/core/host": ["packages/core/host.ts"], + "@executablemd/durable-streams": ["packages/durable-streams/mod.ts"], + "@executablemd/git": ["packages/git/mod.ts"], + "@executablemd/git/api": ["packages/git/api.ts"], + "@executablemd/git/credential-helper": ["packages/git/credential-helper.ts"], + "@executablemd/git/deno": ["packages/git/deno.ts"], + "@executablemd/runtime": ["packages/runtime/mod.ts"], + "@executablemd/runtime/api": ["packages/runtime/api.ts"], + "@executablemd/runtime/files": ["packages/runtime/files.ts"], + "@executablemd/runtime/test": ["packages/runtime/test/mod.ts"], + "@executablemd/test-agent": ["packages/test-agent/mod.ts"], + "@executablemd/testing": ["packages/testing/mod.ts"], + "@executablemd/web": ["packages/web/mod.ts"], + "@executablemd/workflow": ["packages/workflow/mod.ts"], + "@executablemd/workflow/deno": ["packages/workflow/deno.ts"], + "@/*": ["site/*"] + } + }, + "packages/*": { + "entry": [ + "mod.ts", + "src/{deno,node,bun,compiled}.ts", + "client/main.tsx", + "tests/**/*.test.ts", + "tests/**/fixtures/**/*.{ts,tsx,js}", + "tests/**/components/**/*.ts", + "components/**/*.ts", + "demo/**/*.{ts,tsx}", + "examples/**/*.{ts,tsx}" + ], + "project": [ + "**/*.{ts,tsx,js,jsx}", + "!vendor/**", + "!src/repl/vendor/**", + "!npm/**" + ], + "paths": { + "@executablemd/acp": ["../../packages/acp/mod.ts"], + "@executablemd/acp/embedded-adapters": ["../../packages/acp/embedded-adapters.ts"], + "@executablemd/cli": ["../../packages/cli/src/deno.ts"], + "@executablemd/code-review-agent": ["../../packages/code-review-agent/mod.ts"], + "@executablemd/code-review-agent/review-components": [ + "../../packages/code-review-agent/src/review-components.ts" + ], + "@executablemd/core": ["../../packages/core/mod.ts"], + "@executablemd/core/api": ["../../packages/core/api.ts"], + "@executablemd/core/host": ["../../packages/core/host.ts"], + "@executablemd/durable-streams": ["../../packages/durable-streams/mod.ts"], + "@executablemd/git": ["../../packages/git/mod.ts"], + "@executablemd/git/api": ["../../packages/git/api.ts"], + "@executablemd/git/credential-helper": ["../../packages/git/credential-helper.ts"], + "@executablemd/git/deno": ["../../packages/git/deno.ts"], + "@executablemd/runtime": ["../../packages/runtime/mod.ts"], + "@executablemd/runtime/api": ["../../packages/runtime/api.ts"], + "@executablemd/runtime/files": ["../../packages/runtime/files.ts"], + "@executablemd/runtime/test": ["../../packages/runtime/test/mod.ts"], + "@executablemd/test-agent": ["../../packages/test-agent/mod.ts"], + "@executablemd/testing": ["../../packages/testing/mod.ts"], + "@executablemd/web": ["../../packages/web/mod.ts"], + "@executablemd/workflow": ["../../packages/workflow/mod.ts"], + "@executablemd/workflow/deno": ["../../packages/workflow/deno.ts"], + "@/*": ["../../site/*"] + } + } + }, + "ignoreExportsUsedInFile": true +} diff --git a/package.json b/package.json index 7ec88e4fc..cd4a9cd5d 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@types/node": "^22.0.0", "@types/semver": "^7.7.0", "expect": "^30.0.0", + "knip": "6.41.0", "oxfmt": "^0.41.0", "oxlint": "1.74.0", "tsx": "^4.19.0", @@ -74,7 +75,8 @@ "test:node": "tsx scripts/runtime-tests.ts node", "test:bun": "bun scripts/runtime-tests.ts bun", "test:deno": "deno task test", - "lint": "oxlint -c .oxlintrc.json --ignore-pattern 'scripts/tests/fixtures/**' --ignore-pattern '**/npm/**' --ignore-pattern 'packages/workflow/vendor/cloudflare-computer-dofs/**' --ignore-pattern 'packages/acp/vendor/acpx/**' --ignore-pattern 'packages/cli/src/repl/vendor/freedom/**' packages scripts && oxfmt --check packages scripts", + "lint": "oxlint -c .oxlintrc.json --ignore-pattern 'scripts/tests/fixtures/**' --ignore-pattern '**/npm/**' --ignore-pattern 'packages/workflow/vendor/cloudflare-computer-dofs/**' --ignore-pattern 'packages/acp/vendor/acpx/**' --ignore-pattern 'packages/cli/src/repl/vendor/freedom/**' packages scripts && oxfmt --check packages scripts && pnpm run lint:dead-code", + "lint:dead-code": "knip --config knip.json --no-progress --no-config-hints --include files,exports,types", "fmt": "oxfmt --write packages scripts" }, "workspaces": [ diff --git a/packages/cli/src/plan.ts b/packages/cli/src/plan.ts index cc1346c34..4c0d82aa1 100644 --- a/packages/cli/src/plan.ts +++ b/packages/cli/src/plan.ts @@ -66,15 +66,6 @@ import type { MachineSessionAssembly } from "./session-coordinator.ts"; import { describeError } from "./props.ts"; import { renderSyntaxMarkdown } from "./syntax.ts"; -/** - * The identity approved text runs under. - * - * Owned by the Component module, because the structural admission inside `` - * reads the same bytes under the same identity: a position reading - * `(:5:1)` means the same thing whichever gate produced it. - */ -export { PLAN_IDENTITY } from "./plan-component.ts"; - /** What one `xmd plan` invocation was asked to do. */ export interface PlanCommand { /** The request, byte for byte, as fixed grammar preserved it. */ diff --git a/packages/cli/src/release-targets.ts b/packages/cli/src/release-targets.ts index 2d278779b..9262e5720 100644 --- a/packages/cli/src/release-targets.ts +++ b/packages/cli/src/release-targets.ts @@ -78,8 +78,3 @@ export function releaseTargetFor( (row) => row.platform === platform && row.architecture === architecture, ); } - -/** The row for one target triple, or `undefined` when it names none. */ -export function releaseTargetNamed(target: string): ReleaseTarget | undefined { - return RELEASE_TARGETS.find((row) => row.target === target); -} diff --git a/packages/cli/src/repl/application.ts b/packages/cli/src/repl/application.ts index cf2e9475b..7a2c444ef 100644 --- a/packages/cli/src/repl/application.ts +++ b/packages/cli/src/repl/application.ts @@ -82,7 +82,7 @@ import type { ReplPresentationRole, ReplRowStyle } from "./presentation-style.ts import { jsonRuns, sourceRuns } from "./presentation-text.ts"; import { NO_NAVIGATION, numbered, pointLabel } from "./navigation.ts"; import type { ReplHistoryNavigation } from "./navigation.ts"; -import { blankBand, EMPTY_BAND, HISTORY_ROWS } from "./history-rail.ts"; +import { blankBand, EMPTY_BAND } from "./history-rail.ts"; import type { ReplPreparedRail } from "./history-rail.ts"; import { entryReading, NO_READING } from "./source-reading.ts"; import type { ReplReading } from "./source-reading.ts"; @@ -96,13 +96,7 @@ import { resolveLocation, surfaceForDrawer, } from "./route.ts"; -import type { - ReplDrawerRef, - ReplLiveAvailability, - ReplRoute, - ReplSelection, - ReplSurface, -} from "./route.ts"; +import type { ReplDrawerRef, ReplLiveAvailability, ReplRoute, ReplSelection } from "./route.ts"; import type { ReplAgentPermission, ReplAgentTurn, @@ -5017,5 +5011,4 @@ function totalOf( return drawerContent(view, widths?.drawer ?? 0, preview)?.content.length ?? 0; } -export { HISTORY_ROWS }; -export type { ReplDrawerRef, ReplSurface }; +export type { ReplDrawerRef }; diff --git a/packages/cli/src/workflow.ts b/packages/cli/src/workflow.ts index b5f5e6a39..70cff4380 100644 --- a/packages/cli/src/workflow.ts +++ b/packages/cli/src/workflow.ts @@ -1478,8 +1478,3 @@ function* suspensionEvent( } return undefined; } - -/** The exit code a status reports, for a caller composing its own outcome. */ -export function workflowExitCode(status: WorkflowRunStatus): number { - return EXIT_BY_STATUS[status]; -} diff --git a/packages/cli/tests/support/plan-harness.ts b/packages/cli/tests/support/plan-harness.ts index 55aa393e1..943e5320c 100644 --- a/packages/cli/tests/support/plan-harness.ts +++ b/packages/cli/tests/support/plan-harness.ts @@ -253,44 +253,6 @@ export function* useWorkingDirectory( }); } -/** - * Environment values this scope answers with, delegating every other name. - * - * Delegation matters: the agent stack reads `DEFAULT_AGENT_NAME` through the - * same Api, and answering `undefined` for everything would make a case about - * `XMD_PROPS` quietly also be a case about the default agent. - */ -export function* useEnvironment(values: Record): Operation { - yield* useRecordedEnvironment([], values); -} - -/** - * The same environment, with every name it was asked recorded in order. - * - * What a resolution costs is visible only as the reads it performs: a value - * settled once and handed to both consumers reads its name once, while two - * consumers each settling their own read it twice and agree only by accident. - */ -export function* useRecordedEnvironment( - reads: string[], - values: Record, -): Operation { - yield* API.Env.around({ - *env([name], next) { - reads.push(name); - if (Object.hasOwn(values, name)) { - return values[name]; - } - return yield* next(name); - }, - }); -} - -/** How many times one name was read. */ -export function timesRead(reads: readonly string[], name: string): number { - return reads.filter((read) => read === name).length; -} - /** * A temporary tree this scope creates, owns and removes whole. * diff --git a/packages/core/src/agent/launch-request.ts b/packages/core/src/agent/launch-request.ts index f9913a525..7de0dd716 100644 --- a/packages/core/src/agent/launch-request.ts +++ b/packages/core/src/agent/launch-request.ts @@ -20,7 +20,7 @@ * value it has already moved past. */ -import type { Agent, LaunchOptions, PermissionMode, Session } from "./agent-api.ts"; +import type { Agent, PermissionMode, Session } from "./agent-api.ts"; /** * What public launch middleware is handed. @@ -241,11 +241,3 @@ export function issueLaunch(facts: LaunchFacts, generation: object): IssuedLaunc }, }; } - -/** The facts a routed request carries, for the provider that accepted it. */ -export function launchOptionsOf(request: AgentLaunchRequest): LaunchOptions { - return { - agent: request.agent, - ...(request.session === undefined ? {} : { session: request.session }), - }; -} diff --git a/packages/core/src/body-structure.ts b/packages/core/src/body-structure.ts index 99e241429..5804b105e 100644 --- a/packages/core/src/body-structure.ts +++ b/packages/core/src/body-structure.ts @@ -185,17 +185,6 @@ export function bodyStructureFacts( }; } -/** Whether these facts describe a body whose structure is refused. */ -export function hasBodyStructureViolation(facts: BodyStructureFacts): boolean { - return ( - facts.misplacedOutputs.length > 0 || - facts.undeclaredReturns.length > 0 || - facts.exclusiveOutputs.length > 0 || - facts.missingReturn || - facts.returnViolations.length > 0 - ); -} - function structureError(source: string, headline: string, violations: string[]): ErrorSegment { const list = violations.map((entry) => ` - ${entry}`).join("\n"); return { type: "error", message: `${headline}\n${list}`, source }; @@ -268,18 +257,6 @@ export function renderBodyStructure(facts: BodyStructureFacts): ErrorSegment | u return outputError ?? returnError; } -/** - * Structural preflight (spec §6.9). Validates `` placement against the - * body's own source AST. Only a direct top-level `` is a valid - * declaration; any `` at depth > 0 — including inside unreachable or - * discarded children — is a placement violation. All violations are combined - * into a single aggregate ErrorSegment. Returns undefined when placement is - * valid. - */ -export function validateOutputPlacement(bodySegments: Segment[]): ErrorSegment | undefined { - return misplacedOutputAggregate(collectOutputs(bodySegments, 1)); -} - /** * Structural preflight for a body's output and return contract (spec §6.9, * §6.10). Runs against the body's own source AST, before `` diff --git a/packages/core/src/component-api.ts b/packages/core/src/component-api.ts index d8fbdbc61..e534b4f13 100644 --- a/packages/core/src/component-api.ts +++ b/packages/core/src/component-api.ts @@ -491,7 +491,6 @@ export function expandThroughTerminal( return owned.operations.expand(request); } -export const expand: Operations["expand"] = Component.operations.expand; export const importComponent: Operations["importComponent"] = Component.operations.importComponent; export const applyModifiers: Operations["applyModifiers"] = @@ -507,7 +506,6 @@ export const content: Operations["content"] = Component.operations export const hasContent: Operations["hasContent"] = Component.operations.hasContent; export const hasBinding: Operations["hasBinding"] = Component.operations.hasBinding; export const retain: Operations["retain"] = Component.operations.retain; -export const registry: Operations["registry"] = Component.operations.registry; export const tryContent: Operations["tryContent"] = Component.operations.tryContent; export const hasCapture: Operations["hasCapture"] = Component.operations.hasCapture; export const capture: Operations["capture"] = Component.operations.capture; diff --git a/packages/core/src/documentation-index.ts b/packages/core/src/documentation-index.ts index a217ab480..63a1cd07a 100644 --- a/packages/core/src/documentation-index.ts +++ b/packages/core/src/documentation-index.ts @@ -50,7 +50,6 @@ */ import { isComponentName } from "./components/registration.ts"; -import { parseSource } from "./definition.ts"; import type { ComponentOrigin } from "./types.ts"; /** One package's documentation for the components it registers. */ @@ -114,28 +113,6 @@ export interface DocumentationIndex { bundleDocumentation(origin: ComponentOrigin): string | undefined; } -/** - * A Markdown component's own documentation, taken from its own document. - * - * A repository component, a bundled one and a host's declared Markdown are all - * *this run's* rather than a package's, so no `components.md` documents them. - * Their long-form documentation, when they have any, is the prose in their own - * file — which is where an author writing one would put it, and the only place - * that stays correct when the file changes. - * - * Read from the source the selection already holds, so this loads nothing: a - * repository component's bytes were read to describe it, and a bundled or - * declared one's were admitted before the run began. - */ -export function markdownDocumentation(path: string, source: string): string | undefined { - // The canonical splitter, not a delimiter search of this module's own: a - // document whose body repeats `---` is ordinary, and a second implementation - // of the rule is one release from disagreeing with the one that decides what - // actually runs. - const body = parseSource(path, source).content.trim(); - return body.length === 0 ? undefined : body; -} - /** What a component with no authored documentation renders instead of prose. */ export const NO_DOCUMENTATION = "No long-form documentation is available for this component."; diff --git a/packages/core/src/expand.ts b/packages/core/src/expand.ts index 29307e0d9..9e2f26860 100644 --- a/packages/core/src/expand.ts +++ b/packages/core/src/expand.ts @@ -36,7 +36,6 @@ import { misplacedReturnMessage, outputPropsViolation, validateBodyStructure, - validateOutputPlacement, } from "./body-structure.ts"; import { allStructure, @@ -4385,7 +4384,7 @@ interface BodyChunk { * from; the walk and the catalog now live beside the facts validation reads, so * the two callers cannot drift. */ -export { bodyHasOutput, validateBodyStructure, validateOutputPlacement }; +export { bodyHasOutput, validateBodyStructure }; /** * Partition a definition body into ordered chunks (spec §6.9). Output error mode diff --git a/packages/core/src/expansion.ts b/packages/core/src/expansion.ts index f0e9a120d..dbe988c69 100644 --- a/packages/core/src/expansion.ts +++ b/packages/core/src/expansion.ts @@ -119,22 +119,6 @@ export function snapshot( }); } -/** - * The path and the snapshot one authored element's expansion runs under. - * - * Derived before the element's body runs and unchanged by anything it does, so - * re-expanding the same element arrives at the same identifier. - */ -export function enterElement( - parent: string, - name: string, - site: string, - position: SourcePosition | undefined, -): { path: string; expansion: Expansion } { - const path = extendPath(parent, elementFrame(name, site)); - return { path, expansion: snapshot(path, name, position) }; -} - /** * Publish an expansion for the operation that runs the element's body. * diff --git a/packages/core/src/fetch-request.ts b/packages/core/src/fetch-request.ts index 2e381ac01..0f084f625 100644 --- a/packages/core/src/fetch-request.ts +++ b/packages/core/src/fetch-request.ts @@ -56,36 +56,6 @@ export function requestRecord(request: FetchRequest): JsonObject { }; } -/** - * Read a request back from what was retained. - * - * A replayed description is durable input, so it is parsed rather than trusted: - * what a journal holds was written by an earlier run of a document that may - * since have changed. - */ -export function parseRequestRecord(value: Json): FetchRequest { - if (!isJsonObject(value)) { - throw new FetchRequestError("a retained Fetch request must be a JSON object"); - } - const { url, method, headers, timeout } = value; - if (typeof url !== "string" || typeof method !== "string" || !isJsonObject(headers)) { - throw new FetchRequestError("a retained Fetch request must carry url, method and headers"); - } - const parsed: Record = {}; - for (const [name, header] of Object.entries(headers)) { - if (typeof header !== "string") { - throw new FetchRequestError(`retained Fetch header "${name}" is not a string`); - } - parsed[name] = header; - } - return { - url, - method, - headers: parsed, - ...(typeof timeout === "number" ? { timeout } : {}), - }; -} - /** * The URL a document wrote, checked but not rewritten. * diff --git a/packages/core/src/generated-xmd.ts b/packages/core/src/generated-xmd.ts index 59de297dc..9a6faee22 100644 --- a/packages/core/src/generated-xmd.ts +++ b/packages/core/src/generated-xmd.ts @@ -1553,9 +1553,9 @@ function readRoots(value: Json | undefined): readonly string[] | undefined { /** * The retained requests, read closed. * - * `parseRequestRecord()` is the wrong reader here: it ignores members it does - * not know and treats a malformed `timeout` as an absent one, so a retained - * ceiling could compare equal to a live one it does not describe. + * A reader that ignored members it does not know, or treated a malformed + * `timeout` as an absent one, would let a retained ceiling compare equal to a + * live one it does not describe, so this admits exactly the declared members. */ function readRequests(value: Json): FetchRequest[] | undefined { if (!Array.isArray(value)) { diff --git a/packages/core/src/output/mod.ts b/packages/core/src/output/mod.ts deleted file mode 100644 index 30bb60b49..000000000 --- a/packages/core/src/output/mod.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Output middleware — barrel export. - */ -export { useNormalizedOutput } from "./normalize.ts"; -export { useTerminalOutput } from "./terminal.ts"; diff --git a/packages/git/src/composition/components/Dir.ts b/packages/git/src/composition/components/Dir.ts index 4e20e5608..7918e94f9 100644 --- a/packages/git/src/composition/components/Dir.ts +++ b/packages/git/src/composition/components/Dir.ts @@ -144,5 +144,3 @@ export const form: FormDeclaration = { "engine issued, so which form it was written as cannot be established.", ), }; - -export default form; diff --git a/packages/git/src/composition/components/IssueTracker.ts b/packages/git/src/composition/components/IssueTracker.ts index 1a140c3f6..205b777dd 100644 --- a/packages/git/src/composition/components/IssueTracker.ts +++ b/packages/git/src/composition/components/IssueTracker.ts @@ -42,9 +42,6 @@ import type { Json } from "@executablemd/durable-streams"; import { IssueTrackerContext } from "../../issue/context.ts"; import type { IssueTracker as Tracker } from "../../issue/tracker.ts"; -/** The component name, as a document writes it and as a refusal names it. */ -export const ISSUE_TRACKER_ELEMENT = ""; - export const props: PropsSchema = { type: "object", properties: { diff --git a/packages/git/src/composition/pull-request-read-records.ts b/packages/git/src/composition/pull-request-read-records.ts index 6325dc29d..6b878cd6f 100644 --- a/packages/git/src/composition/pull-request-read-records.ts +++ b/packages/git/src/composition/pull-request-read-records.ts @@ -230,29 +230,6 @@ export function pullRequestReadRequestJson(request: PullRequestReadRequest): Jso }; } -/** The retained request, read back rather than asserted. */ -export function parsePullRequestReadRequest(value: Json): PullRequestReadRequest | undefined { - if (!isJsonObject(value)) { - return undefined; - } - const names = ["operation", "url", "provider", "kind", "runId", "expansionId"]; - if (!exactMembers(value, names) || value.operation !== "read") { - return undefined; - } - const kind = PULL_REQUEST_READ_KINDS.find((known) => known === value.kind); - const { url, provider, runId, expansionId } = value; - if (kind === undefined || typeof url !== "string" || url === "") { - return undefined; - } - if (provider !== null && typeof provider !== "string") { - return undefined; - } - if (typeof runId !== "string" || typeof expansionId !== "string") { - return undefined; - } - return { operation: "read", url, provider, kind, runId, expansionId }; -} - /** * Exactly these members, and no others. * diff --git a/packages/git/src/composition/selection.ts b/packages/git/src/composition/selection.ts index 12ee2d903..6bf2e71b8 100644 --- a/packages/git/src/composition/selection.ts +++ b/packages/git/src/composition/selection.ts @@ -87,8 +87,6 @@ export interface RepositorySelection { readonly checkoutPath: string; } -const SELECTION_MEMBERS = ["selection", "name", "identity", "checkoutPath"] as const; - /** The retained record, filtered to the identity a selection carries. */ export function filteredRepositoryIdentity(record: RepositoryRecord): RepositoryIdentity { return Object.freeze({ @@ -153,34 +151,6 @@ export function sameRepositoryIdentity( return REPOSITORY_IDENTITY_MEMBERS.every((member) => left[member] === right[member]); } -/** - * The selection this value describes, or `undefined` when it describes none. - * - * Total, and exact about membership, for the reason every parser in this - * package is: a selection may arrive from a caller reaching the Api directly, - * and a value carrying more or fewer members than the contract declares - * describes something other than a selection. - */ -export function parseRepositorySelection(value: unknown): RepositorySelection | undefined { - const record = members(value, SELECTION_MEMBERS); - if (record === undefined) { - return undefined; - } - const selection = text(record.selection); - const name = text(record.name); - const identity = parseRepositoryIdentity(record.identity); - const checkoutPath = text(record.checkoutPath); - if ( - selection === undefined || - name === undefined || - identity === undefined || - checkoutPath === undefined - ) { - return undefined; - } - return Object.freeze({ selection, name, identity, checkoutPath }); -} - /** The selection a provider hands back, frozen so a holder cannot edit one. */ export function repositorySelection( selection: string, diff --git a/packages/git/src/deno/composition/authentication.ts b/packages/git/src/deno/composition/authentication.ts index 39b4971aa..8ed69df40 100644 --- a/packages/git/src/deno/composition/authentication.ts +++ b/packages/git/src/deno/composition/authentication.ts @@ -623,16 +623,6 @@ export function denoGitAuthentication(options: GitAuthenticationOptions = {}): G }; } -/** The authentication of a host that lends none. */ -export function noGitAuthentication(): GitAuthentication { - return { - // deno-lint-ignore require-yield - *open(): Operation { - return UNAUTHENTICATED; - }, - }; -} - /** * Whether a failed transport failed for want of authentication. * diff --git a/packages/git/src/deno/composition/pull-request-reads.ts b/packages/git/src/deno/composition/pull-request-reads.ts index e2d3a475e..5f2a6e82e 100644 --- a/packages/git/src/deno/composition/pull-request-reads.ts +++ b/packages/git/src/deno/composition/pull-request-reads.ts @@ -44,7 +44,6 @@ import type { } from "../../composition/pull-request-read-records.ts"; import { PullRequestAPI } from "../../composition/pull-request-api.ts"; import { PullRequestReadExecution } from "../../composition/pull-request-read-execution.ts"; -import type { PullRequestReadOptions } from "../../composition/pull-request-api.ts"; import type { RepositoryRecord } from "../../composition/records.ts"; import type { SelectionRegistry } from "../selections.ts"; import type { GitHubRepositoryName, GitHubSource } from "./github.ts"; @@ -359,6 +358,3 @@ export function* useGitHubPullRequestReads( }, }); } - -/** The options a read carries, re-exported for a host installing this. */ -export type { PullRequestReadOptions }; diff --git a/packages/git/src/deno/run-composition/checkouts.ts b/packages/git/src/deno/run-composition/checkouts.ts index b9e8052d0..aa4c2c236 100644 --- a/packages/git/src/deno/run-composition/checkouts.ts +++ b/packages/git/src/deno/run-composition/checkouts.ts @@ -53,7 +53,7 @@ import { } from "../composition/git.ts"; import type { GitSession } from "../composition/git.ts"; import { useGitAuthentication, type RepositoryHost } from "../composition/host.ts"; -import { repositoryRefused, worktreeRefused } from "../composition/refusals.ts"; +import { repositoryRefused } from "../composition/refusals.ts"; import { ManagedCheckoutError } from "./errors.ts"; import { METADATA_VERSION, @@ -624,8 +624,3 @@ function* createManagedWorktree( metadata, }; } - -/** The refusal a Worktree request reports when native Git refuses it. */ -export function worktreeRefusal(name: string, reason: string): never { - worktreeRefused(name, reason); -} diff --git a/packages/git/src/issue/effect.ts b/packages/git/src/issue/effect.ts index 00057335e..e042d9af2 100644 --- a/packages/git/src/issue/effect.ts +++ b/packages/git/src/issue/effect.ts @@ -66,8 +66,6 @@ import { type IssueUpsertRequest, } from "./records.ts"; -export { ISSUE_EFFECT } from "./effect-type.ts"; - /** What one read asks the envelope for. */ export interface IssueReadEffectRequest { /** The canonical issue URL. */ diff --git a/packages/git/src/issue/records.ts b/packages/git/src/issue/records.ts index 3ed14b0ce..acffa996d 100644 --- a/packages/git/src/issue/records.ts +++ b/packages/git/src/issue/records.ts @@ -83,9 +83,6 @@ export interface IssueUpsertRequest { */ export type IssueRequest = IssueReadRequest | IssueUpsertRequest; -/** What one settled read retains, and what a document binds. */ -export type IssueReadRecord = IssueDetails; - /** What one settled upsert retains, and what a document binds. */ export type IssueRecord = IssueReference; diff --git a/packages/git/tests/support/github.ts b/packages/git/tests/support/github.ts index 8ecb41251..97e16ca4d 100644 --- a/packages/git/tests/support/github.ts +++ b/packages/git/tests/support/github.ts @@ -144,11 +144,6 @@ export function gitHubStore(options: GitHubStoreOptions = {}): GitHubStore { }; } -/** The locator a document writes for this store's repository. */ -export function gitHubLocator(store: GitHubStore): string { - return `https://github.com/${store.owner}/${store.repository}`; -} - function payloadOf(store: GitHubStore, pullRequest: StoredPullRequest): Record { const home = { full_name: `${store.owner}/${store.repository}` }; return { @@ -588,11 +583,6 @@ export function fakeGitHubAccess( }; } -/** The requests one store received, as method and path. */ -export function calls(store: GitHubStore): string[] { - return store.requests.map((request) => `${request.method} ${new URL(request.url).search}`); -} - export function creations(store: GitHubStore): number { return store.requests.filter( (request) => request.method === "POST" && new URL(request.url).pathname.endsWith("/pulls"), @@ -606,30 +596,11 @@ export function issueCreations(store: GitHubStore): number { ).length; } -/** How many issue field updates this GitHub was asked for. */ -export function issuePatches(store: GitHubStore): number { - return store.requests.filter( - (request) => request.method === "PATCH" && new URL(request.url).pathname.includes("/issues/"), - ).length; -} - -/** Every issue call this GitHub received, in order, as method and path. */ -export function issueCalls(store: GitHubStore): string[] { - return store.requests - .filter((request) => new URL(request.url).pathname.includes("/issues")) - .map((request) => `${request.method} ${new URL(request.url).pathname}`); -} - /** How many REST field updates this GitHub was asked for. */ export function patches(store: GitHubStore): number { return store.requests.filter((request) => request.method === "PATCH").length; } -/** How many draft transitions this GitHub was asked for. */ -export function draftMutations(store: GitHubStore): number { - return store.requests.filter((request) => new URL(request.url).pathname === "/graphql").length; -} - /** Every mutation this GitHub received, in order, as a short word. */ export function mutations(store: GitHubStore): string[] { return store.requests diff --git a/packages/testing/src/test-api.ts b/packages/testing/src/test-api.ts index bda717916..e9a848214 100644 --- a/packages/testing/src/test-api.ts +++ b/packages/testing/src/test-api.ts @@ -85,7 +85,6 @@ export const Test: Api = createApi("Test", { export const testing: Operations["testing"] = Test.operations.testing; export const inTest: Operations["inTest"] = Test.operations.inTest; -export const testScope: Operations["testScope"] = Test.operations.testScope; export const verbose: Operations["verbose"] = Test.operations.verbose; export const sessionActive: Operations["sessionActive"] = Test.operations.sessionActive; export const record: Operations["record"] = Test.operations.record; diff --git a/packages/web/src/json.ts b/packages/web/src/json.ts index 7da8fbd86..5ae2076b1 100644 --- a/packages/web/src/json.ts +++ b/packages/web/src/json.ts @@ -33,12 +33,6 @@ export function parseJsonObject(value: unknown): JsonObject { return parsed; } -export function isJsonObject(value: Json | undefined): value is JsonObject { - return ( - value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value) - ); -} - function parseValue(value: unknown, seen: Set, path: string): Json { if (value === null) { return null; diff --git a/packages/web/tests/component-support.ts b/packages/web/tests/component-support.ts index 5b155228c..0bbf059af 100644 --- a/packages/web/tests/component-support.ts +++ b/packages/web/tests/component-support.ts @@ -164,23 +164,6 @@ export function* runWebFormDoc(source: string, options: RunOptions = {}): Operat }); } -/** Printed URLs are captured by watching stderr, which is where they go. */ -export function* recordingPrints(body: () => Operation): Operation<{ - value: T; - printed: string[]; -}> { - const printed: string[] = []; - const original = console.error; - console.error = (...args: unknown[]) => { - printed.push(args.map((arg) => String(arg)).join(" ")); - }; - try { - return { value: yield* body(), printed }; - } finally { - console.error = original; - } -} - /** * Every durable yield of one type, whatever its result. * diff --git a/packages/workflow/src/deno/artifact/manifest.ts b/packages/workflow/src/deno/artifact/manifest.ts index b39438a59..7bea40c35 100644 --- a/packages/workflow/src/deno/artifact/manifest.ts +++ b/packages/workflow/src/deno/artifact/manifest.ts @@ -37,22 +37,14 @@ import type { /** The artifact manifest version a format-1 artifact produces and reads. */ export const XMD_ARTIFACT_MANIFEST_VERSION = 1; -/** The manifest version a format-2 artifact produces and reads. */ -export const XMD_ARTIFACT_SOURCE_BUNDLE_MANIFEST_VERSION = 2; - /** * What the artifact identity is a hash *of*, beyond the manifest bytes. * * Domain separation, so the same bytes appearing as some other structure's - * canonical encoding cannot be presented as an artifact identity — and so one - * format's manifest can never derive the other's identity, however similar the - * two inventories happen to look. + * canonical encoding cannot be presented as an artifact identity. */ export const XMD_ARTIFACT_IDENTITY_DOMAIN = "xmd-artifact\0v1\0"; -/** The same separation for format 2, which is a different set of records. */ -export const XMD_ARTIFACT_SOURCE_BUNDLE_IDENTITY_DOMAIN = "xmd-artifact\0v2\0"; - const encoder = new TextEncoder(); /** The compact canonical JSON encoding of a value, as UTF-8 bytes. */ diff --git a/packages/workflow/src/deno/artifact/mod.ts b/packages/workflow/src/deno/artifact/mod.ts index 376a58d71..a0143065a 100644 --- a/packages/workflow/src/deno/artifact/mod.ts +++ b/packages/workflow/src/deno/artifact/mod.ts @@ -16,15 +16,8 @@ export type { DetachedXmdArtifact, VerifiedXmdArtifact, XmdArtifactAgentBundle, - XmdArtifactAgentCheckpoint, - XmdArtifactAgentEvidence, XmdArtifactAgentPortability, - XmdArtifactAgentPortabilityBase, - XmdArtifactForkLineage, - XmdArtifactFrontier, - XmdArtifactJournalRow, XmdArtifactPortableAgentSession, - XmdArtifactProviderSessionIdentity, XmdArtifactUnavailableAgentSession, XmdArtifactWriteResult, } from "./types.ts"; diff --git a/packages/workflow/src/deno/artifact/schema.ts b/packages/workflow/src/deno/artifact/schema.ts index 4b4408863..d5aa35ca2 100644 --- a/packages/workflow/src/deno/artifact/schema.ts +++ b/packages/workflow/src/deno/artifact/schema.ts @@ -114,13 +114,6 @@ const OBJECTS: ReadonlyMap = new Map([ ], ]); -/** Every object version 1 declares, with the shape it declares. */ -export const XMD_ARTIFACT_EXPECTED_SCHEMA = Object.freeze( - [...OBJECTS.entries()].map(([name, object]) => - Object.freeze({ name, type: object.type, sql: normalize(object.sql) }), - ), -); - /** Version 1 in full. */ export const XMD_ARTIFACT_SCHEMA_SQL = [...OBJECTS.values()] .map((object) => `${object.sql};`) diff --git a/packages/workflow/src/deno/schema.ts b/packages/workflow/src/deno/schema.ts index 832ff1d6a..d302a5d1c 100644 --- a/packages/workflow/src/deno/schema.ts +++ b/packages/workflow/src/deno/schema.ts @@ -604,29 +604,6 @@ export const EXPECTED_SCHEMA = Object.freeze( ), ); -/** - * Objects a version-1 file must hold, including the pinned Cloudflare - * structure. - * - * The optional ones are absent from this list and only from this list: they are - * created with every new database, compared against their declaration wherever - * they are found, and simply not demanded of a file written before they - * existed. - */ -export const REQUIRED_OBJECTS: readonly string[] = Object.freeze( - [...OBJECTS.entries()].filter(([, object]) => object.optional !== true).map(([name]) => name), -); - -/** Tables a version-1 file must hold. */ -export const REQUIRED_TABLES: readonly string[] = Object.freeze( - [...OBJECTS.entries()] - .filter(([, object]) => object.type === "table" && object.optional !== true) - .map(([name]) => name), -); - -/** Every object version 1 declares, required or not. */ -export const DECLARED_OBJECTS: readonly string[] = Object.freeze([...OBJECTS.keys()]); - /** * The objects a version-1 file may hold or not hold. * diff --git a/packages/workflow/src/deno/transitions.ts b/packages/workflow/src/deno/transitions.ts index fdee42b8c..b5b2fcf2f 100644 --- a/packages/workflow/src/deno/transitions.ts +++ b/packages/workflow/src/deno/transitions.ts @@ -73,7 +73,7 @@ import { } from "./definition-source.ts"; import type { ForkSourceSnapshot } from "./fork-source.ts"; import { readForkLineage, writeForkInheritance, type ForkHeadEvents } from "./fork-write.ts"; -import { readDocumentExecution, readRetrieval, stopReasonColumns } from "./rows.ts"; +import { readDocumentExecution, stopReasonColumns } from "./rows.ts"; import { initializeSchema, isSqliteForeignKeyConstraint, @@ -96,7 +96,6 @@ const UPSERT_RETRIEVAL = `INSERT INTO definition_retrieval (id, metadata, revisi ON CONFLICT(id) DO UPDATE SET metadata = excluded.metadata, revision = excluded.revision, updated_at = excluded.updated_at`; -const SELECT_RETRIEVAL = "SELECT * FROM definition_retrieval WHERE id = 1"; const INSERT_EXECUTION = "INSERT INTO document_executions (execution_id, started_at) VALUES (?, ?)"; const FINISH_EXECUTION = `UPDATE document_executions SET stopped_at = ?, stop_status = ?, stop_reason_kind = ?, stop_reason_code = ?, @@ -1132,12 +1131,6 @@ function readExecution(database: DatabaseSync, executionId: string): DocumentExe return readDocumentExecution(row); } -/** The retrieval metadata a run holds, for a caller that already opened it. */ -export function readRetrievalRow(database: DatabaseSync) { - const row = reading(database, SELECT_RETRIEVAL).get(); - return row === undefined ? undefined : readRetrieval(row); -} - /** * One lifecycle transaction, from `BEGIN IMMEDIATE` to `COMMIT`. * diff --git a/packages/workflow/src/deno/workspace/logical-path.ts b/packages/workflow/src/deno/workspace/logical-path.ts index 1b874ddf3..c57d77b4a 100644 --- a/packages/workflow/src/deno/workspace/logical-path.ts +++ b/packages/workflow/src/deno/workspace/logical-path.ts @@ -115,12 +115,6 @@ export function logicalParent(path: string): string { return posix(segments); } -/** The path of `entry` relative to `directory`, POSIX-separated. */ -export function logicalRelative(directory: string, entry: string): string { - const base = directory === WORKSPACE_ROOT ? "" : directory; - return entry.startsWith(`${base}/`) ? entry.slice(base.length + 1) : entry; -} - /** One more segment beneath an absolute logical directory. */ export function logicalJoin(directory: string, name: string): string { return directory === WORKSPACE_ROOT ? `/${name}` : `${directory}/${name}`; diff --git a/packages/workflow/src/journal.ts b/packages/workflow/src/journal.ts index 389a9934f..026788b46 100644 --- a/packages/workflow/src/journal.ts +++ b/packages/workflow/src/journal.ts @@ -323,22 +323,3 @@ export function retainedRunMismatch(fields: readonly string[]): StaleInputError { coroutineId: ROOT_COROUTINE, description: refusalDescription() }, ); } - -/** - * The recorded run started from a different base than this run supplied. - * - * Both bases are named, because here they are the two things a caller has to - * compare to understand the refusal, and both came from that caller rather than - * from the journal's opaque content. They are named in the sentence only: the - * description this error retains still holds nothing but the effect's type and - * name. - */ -export function baseMismatch(recorded: string, supplied: string): StaleInputError { - return new StaleInputError( - `The journal records this workflow run starting from "${recorded}", but this run ` + - `supplied "${supplied}". A recorded base cannot be replayed onto a run that asked ` + - "for a different one. Re-run the document from the start rather than resuming from " + - "this journal.", - { coroutineId: ROOT_COROUTINE, description: refusalDescription() }, - ); -} diff --git a/packages/workflow/src/storage/record.ts b/packages/workflow/src/storage/record.ts index 1873ede8d..96b0accc4 100644 --- a/packages/workflow/src/storage/record.ts +++ b/packages/workflow/src/storage/record.ts @@ -273,28 +273,3 @@ export function parseDocumentExecutionCompletion( throw error; } } - -/** A run state a caller offered, checked before anything is written. */ -export function parseStoredRunState(value: unknown): Result { - try { - const members = parseMembers(value, "$", requestFailure); - requireMemberNames(members, ["status", "reason"], "$", requestFailure); - - const status = parseWorkflowRunStatus(members.get("status"), "$.status", requestFailure); - - const reason = members.get("reason"); - if (reason === undefined) { - return Ok({ status }); - } - const parsed = parseStopReasonInput(reason); - if (!parsed.ok) { - return parsed; - } - return Ok({ status, reason: parsed.value }); - } catch (error) { - if (error instanceof WorkflowRequestError) { - return Err(error); - } - throw error; - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f3687d27..9b0a09b2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,9 @@ importers: expect: specifier: ^30.0.0 version: 30.3.0 + knip: + specifier: 6.41.0 + version: 6.41.0 oxfmt: specifier: ^0.41.0 version: 0.41.0 @@ -658,6 +661,15 @@ packages: peerDependencies: effection: ^3 || ^4 + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild/aix-ppc64@0.27.4': resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} @@ -1089,10 +1101,229 @@ packages: cpu: [x64] os: [win32] + '@napi-rs/wasm-runtime@1.2.5': + resolution: {integrity: sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@neophi/sieve-cache@1.5.0': resolution: {integrity: sha512-9T3nD5q51X1d4QYW6vouKW9hBSb2Tb/wB/2XoTr4oP5SCGtp3a7aTHHewQFylred1B21/Bhev6gy4x01FPBcbQ==} engines: {node: '>=18'} + '@oxc-parser/binding-android-arm-eabi@0.153.0': + resolution: {integrity: sha512-BcWu25dMwMjHpaDCEidmpAi7rGg1qZoihJeUUH9bD55pVZ0z4Wa5CQgqSTo0R41H81oozisQKW3WqvAN2m77IQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.153.0': + resolution: {integrity: sha512-9l5rdp2DhRqVriBHsNpu5cEf+hizZ4B58rt93HzyQEBUgZiC6qe2oFpaAAnXGCxHGnQExI1s7S2G0QhFacUZ6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.153.0': + resolution: {integrity: sha512-EcpsyRXHhENDpekpG4QNaSZj2F/BAz4Gh4o89zZwNss86R8KyRSQ6sXbAj74Xyeb4z3j/yBZZ6joQZhraIWyfw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.153.0': + resolution: {integrity: sha512-hlj80HUQy07/2lYp6rmxH+3u+H2ik3rjLR52CUdPApxmZCNxASDUf/+ykTzAHS/fehftmVMATKvPaWmuLHuasg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.153.0': + resolution: {integrity: sha512-fWRcv2pMSh1qtez46MOu8vuUJ5fiaQZPHSphbWulbL7SHBMKfRgZl2PzjXZYQJbq7NEXyXbgIuIuUm+/Rmkv8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.153.0': + resolution: {integrity: sha512-paZeUK1GXShl/bOJQET1/8AQxayFeuN4SIWAkgTRxE7ZQwv9uYSB2n0648smygP22N7jcI3zxAm0Z+ALNiIKxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.153.0': + resolution: {integrity: sha512-qFPp5NwMf4K1C0O+DCVGmcbOaYyYAq98Rvv9p4VSfb777iUwlJCqMUxf6QG9N5f5ls4TEX6LitOZHi+6GL6rYQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.153.0': + resolution: {integrity: sha512-oufEhZCX7saeo27Logra8cvBR1jQ/yw2zUI2hHmWw3qA/XYqEW31FOvLQBv7DWN32NjVdlcaP11WBBZTJo4k9g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.153.0': + resolution: {integrity: sha512-zXYzuAqng85h6+LI3iRm0zeMLX1/K2z41ZAMWxVRgt0D10VvZUaV5q+TEB46KDhoKBpdexoTLC6QoB/EiP2oqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-ppc64-gnu@0.153.0': + resolution: {integrity: sha512-J7+NLwLEzdkKM9cKyPHH/AjXXstWOs98KYgLrPPnO4N9MO8EZRBfPNnBcS3S67uMOBkRpUmCl2FhRnWdQ5CbBA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.153.0': + resolution: {integrity: sha512-sD0K11GQTLWylln54nPMMTtgmBbQrR5It/vMpZpy/fogiw8O2AvIE+9LuWUXddrWk9zvFIHGRcSgFn4IHOfoYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-musl@0.153.0': + resolution: {integrity: sha512-7L66uql2BhvSz51LwfExjOdn6et/ydTiQxl23VApdKzJhTc3TczA1Qt6VR/UcvlQrL2ZbytIJb447vh7p5wmUw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.153.0': + resolution: {integrity: sha512-hXV3T7mNyrPR19rbPYkglkQJtoM/UNo/xwbZeeqdAxEg8PXgb4LXXuxPpGgI4bzRRCWwLUxdDSS58xIyC2u1TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.153.0': + resolution: {integrity: sha512-Da8Vqs1xdtoDYtEjmozD7wGgJeLBQkMbR0WUBECOSxodyX5DXy8ySlvJWbukyO2Q0+TM9EK+4LT6tbq0jCg4hQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.153.0': + resolution: {integrity: sha512-zGAPxNTIIw6X5WyTRscIFE/oDjpdNtrrl32ing6NOXdGjzRucadnxBilexLdeYQ9YxixF93GujpXyQ0jw7ddSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-openharmony-arm64@0.153.0': + resolution: {integrity: sha512-GoHzpqP5XyPIp9DHokoEdCp+K4UPl/h1neFj1uXnUB3pJYyOQ7NOhFVOttNUgCGcj2eywAvu5cRivTOSq4S10g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-win32-arm64-msvc@0.153.0': + resolution: {integrity: sha512-xGcy12tO9k7zFbeyQzEuilfWfQpyRq23/fLPfN3wjSWuWv58k46fQT1hsO1KFNykZuVGW9xSJE/z592TmAoSDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.153.0': + resolution: {integrity: sha512-o1ykUjWncvXX6N5a4KebgLjtD8zRui+f1llMhSGoSC9oP2KOqapRpdwmZ3NcNG0axFouwVKmhGBjd+IhA8BksA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.153.0': + resolution: {integrity: sha512-gxQz7G96Up136fd+2zWrP+o6FYGs9NO4Dyv55IoeJFJUDwLFHcLYY11hhA5wQKs0HoM57Q8esQ3nEVhnCdsjAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.153.0': + resolution: {integrity: sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==} + + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.24.2': + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.24.2': + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} + cpu: [ppc64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} + cpu: [x64] + os: [win32] + '@oxfmt/binding-android-arm-eabi@0.41.0': resolution: {integrity: sha512-REfrqeMKGkfMP+m/ScX4f5jJBSmVNYcpoDF8vP8f8eYPDuPGZmzp56NIUsYmx3h7f6NzC6cE3gqh8GDWrJHCKw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1752,6 +1983,9 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -2091,6 +2325,23 @@ packages: fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + formatly@0.11.4: + resolution: {integrity: sha512-TeCC10lP8KCgoX+Fe5lryILIT0/Dy+nP0OSVnsuB5CwXoLuOdqxCYlPFF1inR8S/zTy1ec7TjhS2TeRKWzzImA==} + engines: {node: '>=18.14.0'} + hasBin: true + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2107,6 +2358,9 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -2175,6 +2429,10 @@ packages: resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2193,6 +2451,11 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + knip@6.41.0: + resolution: {integrity: sha512-DrhQlIuV6xScd5Q+WTF1znd1w41QzxFGCtAHXCajsH/ndGMotHoNYbdXbuFtzW3mIOmniHtZ7zCtvnQkUmJtQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + lmdb@3.5.6: resolution: {integrity: sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==} hasBin: true @@ -2359,6 +2622,13 @@ packages: ordered-binary@1.6.1: resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==} + oxc-parser@0.153.0: + resolution: {integrity: sha512-cEnpyyV1OJRXv++SFNw0z8i+2o/cr88rVLaOYJafELDoHM5yHvCAbwGRLvxuany6SNLcbblSMUyZaGzH+quSYg==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.24.2: + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} + oxfmt@0.41.0: resolution: {integrity: sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2377,6 +2647,9 @@ packages: vite-plus: optional: true + package-manager-detector@1.9.0: + resolution: {integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==} + parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -2397,6 +2670,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pretty-format@30.3.0: resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2530,6 +2807,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + smol-toml@1.9.1: + resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} + engines: {node: '>= 18'} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -2558,6 +2839,10 @@ packages: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} @@ -2593,6 +2878,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + tinypool@2.1.0: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} @@ -2621,6 +2910,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + unbash@5.1.0: + resolution: {integrity: sha512-YVGyy4XqoZ2AVgHjVdL9cchMSurlXFHWviORa8RiU/Bnol4ooW5Lpyur/5XmbgDdyCKxymmgsEQsLGBMIJ/Agg==} + engines: {node: '>=14'} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -2679,6 +2972,10 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + weak-lru-cache@1.2.2: resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} @@ -2695,6 +2992,11 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -2709,6 +3011,9 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -2836,6 +3141,22 @@ snapshots: dependencies: effection: 4.1.0 + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.27.4': optional: true @@ -3085,8 +3406,135 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true + '@napi-rs/wasm-runtime@1.2.5(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.4 + optional: true + '@neophi/sieve-cache@1.5.0': {} + '@oxc-parser/binding-android-arm-eabi@0.153.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.153.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.153.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.153.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.153.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.153.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.153.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.153.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.153.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.153.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.153.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.153.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.153.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.153.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.153.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.153.0': + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.153.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.153.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.153.0': + optional: true + + '@oxc-project/types@0.153.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + optional: true + + '@oxc-resolver/binding-android-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.5(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + optional: true + '@oxfmt/binding-android-arm-eabi@0.41.0': optional: true @@ -3573,6 +4021,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -3924,6 +4377,19 @@ snapshots: dependencies: reusify: 1.1.0 + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + formatly@0.11.4: + dependencies: + fd-package-json: 2.0.0 + package-manager-detector: 1.9.0 + fsevents@2.3.3: optional: true @@ -3935,6 +4401,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + graceful-fs@4.2.11: {} gray-matter@4.0.3: @@ -4027,6 +4497,8 @@ snapshots: graceful-fs: 4.2.11 picomatch: 4.0.3 + jiti@2.7.0: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -4040,6 +4512,21 @@ snapshots: kind-of@6.0.3: {} + knip@6.41.0: + dependencies: + formatly: 0.11.4 + get-tsconfig: 4.14.3 + jiti: 2.7.0 + oxc-parser: 0.153.0 + oxc-resolver: 11.24.2 + picomatch: 4.0.7 + smol-toml: 1.9.1 + strip-json-comments: 5.0.3 + tinyglobby: 0.2.17 + unbash: 5.1.0 + yaml: 2.9.1 + zod: 4.6.7 + lmdb@3.5.6: dependencies: '@harperfast/extended-iterable': 1.0.3 @@ -4320,6 +4807,52 @@ snapshots: ordered-binary@1.6.1: {} + oxc-parser@0.153.0: + dependencies: + '@oxc-project/types': 0.153.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.153.0 + '@oxc-parser/binding-android-arm64': 0.153.0 + '@oxc-parser/binding-darwin-arm64': 0.153.0 + '@oxc-parser/binding-darwin-x64': 0.153.0 + '@oxc-parser/binding-freebsd-x64': 0.153.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.153.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.153.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.153.0 + '@oxc-parser/binding-linux-arm64-musl': 0.153.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.153.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.153.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.153.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.153.0 + '@oxc-parser/binding-linux-x64-gnu': 0.153.0 + '@oxc-parser/binding-linux-x64-musl': 0.153.0 + '@oxc-parser/binding-openharmony-arm64': 0.153.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.153.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.153.0 + '@oxc-parser/binding-win32-x64-msvc': 0.153.0 + + oxc-resolver@11.24.2: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.24.2 + '@oxc-resolver/binding-android-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-x64': 11.24.2 + '@oxc-resolver/binding-freebsd-x64': 11.24.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.24.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.24.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-musl': 11.24.2 + '@oxc-resolver/binding-openharmony-arm64': 11.24.2 + '@oxc-resolver/binding-wasm32-wasi': 11.24.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 + oxfmt@0.41.0: dependencies: tinypool: 2.1.0 @@ -4366,6 +4899,8 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.74.0 '@oxlint/binding-win32-x64-msvc': 1.74.0 + package-manager-detector@1.9.0: {} + parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 @@ -4380,6 +4915,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.7: {} + pretty-format@30.3.0: dependencies: '@jest/schemas': 30.0.5 @@ -4515,6 +5052,8 @@ snapshots: slash@3.0.0: {} + smol-toml@1.9.1: {} + space-separated-tokens@2.0.2: {} sprintf-js@1.0.3: {} @@ -4549,6 +5088,8 @@ snapshots: strip-bom-string@1.0.0: {} + strip-json-comments@5.0.3: {} + structured-source@4.0.0: dependencies: boundary: 2.0.0 @@ -4598,6 +5139,11 @@ snapshots: dependencies: any-promise: 1.3.0 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + tinypool@2.1.0: {} trim-lines@3.0.1: {} @@ -4621,6 +5167,8 @@ snapshots: typescript@5.9.3: {} + unbash@5.1.0: {} + undici-types@6.21.0: {} unicode-emoji-modifier-base@1.0.0: {} @@ -4689,6 +5237,8 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + walk-up-path@4.0.0: {} + weak-lru-cache@1.2.2: {} which@2.0.2: @@ -4703,6 +5253,8 @@ snapshots: y18n@5.0.8: {} + yaml@2.9.1: {} + yargs-parser@20.2.9: {} yargs@16.2.0: @@ -4719,4 +5271,6 @@ snapshots: zod@4.4.3: {} + zod@4.6.7: {} + zwitch@2.0.4: {} diff --git a/scripts/tests/ci-workflow.test.ts b/scripts/tests/ci-workflow.test.ts index 8837cf447..a39f53baf 100644 --- a/scripts/tests/ci-workflow.test.ts +++ b/scripts/tests/ci-workflow.test.ts @@ -148,6 +148,15 @@ function* workflow(): Operation> { ); } +/** The scripts `pnpm` and `deno task` both run, read from where they are defined. */ +function* packageScripts(): Operation> { + const manifest = object( + JSON.parse(yield* readTextFile(new URL("package.json", ROOT))), + "package.json", + ); + return stringRecord(manifest.scripts, "package.json.scripts"); +} + function green(workflow: Record): Job { const job = workflow.green; if (job === undefined) { @@ -596,6 +605,63 @@ describe("the conditional CI jobs", () => { }); }); +/** + * The dead-code gate is one of lint's checks rather than a job of its own, so + * what CI runs is the repository's `lint` script — and the chain from the + * workflow to knip is asserted link by link. A `lint` script that stopped + * calling the check, or called it in a way that could not redden the job, would + * leave the required `lint` job green while unreferenced files and exports + * accumulated. + */ +describe("the CI lint job", () => { + it("runs the repository's own lint command", function* () { + const lint = (yield* workflow()).lint; + const commands = (lint?.steps ?? []) + .map((step) => step.run) + .filter((run): run is string => run !== undefined); + + expect(commands).toContain("pnpm lint"); + }); + + it("reaches the dead-code check through that command, chained so a finding fails it", function* () { + const defined = yield* packageScripts(); + + expect(defined.lint).toContain("&& pnpm run lint:dead-code"); + expect(defined["lint:dead-code"]).toContain("knip"); + expect(defined.lint).not.toContain("|| true"); + expect(defined["lint:dead-code"]).not.toContain("|| true"); + }); + + it("asks it for unused files, exports and types, through the checked-in configuration", function* () { + const defined = yield* packageScripts(); + + expect(defined["lint:dead-code"]).toContain("--config knip.json"); + expect(defined["lint:dead-code"]).toContain("--include files,exports,types"); + }); + + /** + * Named per job rather than left to the derived sweep below: the aggregate + * cases assert that *every* dependency can redden `green`, which stays true + * of a workflow that dropped the lint job altogether. + */ + it("cannot satisfy green with anything but success", function* () { + const parsed = yield* workflow(); + const commandText = command(green(parsed)); + + for (const occasion of OCCASIONS) { + for (const result of ["failure", "cancelled", "skipped", "timed_out", "action_required"]) { + const values = required(parsed, occasion); + values.lint = result; + expect({ + occasion, + result, + code: yield* runGreen(commandText, values, occasion), + }).not.toEqual({ occasion, result, code: 0 }); + } + } + }); +}); + describe("the CI workflow aggregate", () => { it("defines green", function* () { expect(green(yield* workflow())).toBeDefined(); diff --git a/scripts/tests/dead-code.test.ts b/scripts/tests/dead-code.test.ts new file mode 100644 index 000000000..c3febdc87 --- /dev/null +++ b/scripts/tests/dead-code.test.ts @@ -0,0 +1,254 @@ +import { describe, it } from "@executablemd/test-support/bdd"; +import { expect } from "@executablemd/test-support/expect"; +import { ensureDir, exists, readdir, readTextFile, rm, writeTextFile } from "@effectionx/fs"; +import { exec } from "@effectionx/process"; +import { scoped, until } from "effection"; +import type { Operation } from "effection"; +import { symlink } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { useTempDirectory } from "@executablemd/test-support/temp"; + +const ROOT = fileURLToPath(new URL("../../", import.meta.url)); + +/** + * The dead-code gate, proved by mutation rather than by reading its report. + * + * A configuration that ignored the first-party source directories, or declared + * an implementation module to be an entry point, would report nothing and look + * exactly like a clean repository. So each case changes the tree and asks + * whether the real command notices: the positive cases add dead code and + * require the offending path or symbol in the report, and the negative cases + * add the kinds of module nothing in the repository imports *by design* — a + * public package export, a runtime entry point, a standalone script, a site + * route and island, a package example, and the fixtures and components a test + * loads by name or spawns as a subprocess — and require silence. + * + * Every case runs in a clone of `HEAD` with this checkout's installed + * dependencies linked in, so a mutation can never reach the worktree another + * check is reading and nothing is installed to make the clone usable. A clone + * carries the committed configuration, so run these against a committed change. + */ +function* clone(): Operation { + const target = yield* useTempDirectory("dead-code-"); + + yield* exec("git", { + arguments: ["clone", "--shared", "--quiet", ROOT, target], + cwd: ROOT, + }).expect(); + + // Knip resolves the import graph, and pnpm gives each workspace member its + // own `node_modules` beside the root one. Linking both layouts is what lets + // the clone resolve what this checkout already resolved; a clone that had to + // install first would move dependency state the rest of the battery is + // reading through. + yield* link(path.join(ROOT, "node_modules"), path.join(target, "node_modules")); + for (const member of yield* readdir(path.join(ROOT, "packages"))) { + const dependencies = path.join(ROOT, "packages", member, "node_modules"); + if (yield* exists(dependencies)) { + yield* link(dependencies, path.join(target, "packages", member, "node_modules")); + } + } + + return target; +} + +function link(from: string, to: string): Operation { + return until(symlink(from, to, "dir")); +} + +/** + * The command the repository checks in, read from where it is defined. + * + * Derived rather than restated: a test carrying its own copy of the arguments + * would keep passing after `lint:dead-code` stopped checking files, exports or + * types, which is the one thing these cases exist to establish. + */ +function* deadCodeArguments(): Operation { + const manifest: unknown = JSON.parse(yield* readTextFile(path.join(ROOT, "package.json"))); + const scripts = + typeof manifest === "object" && manifest !== null && "scripts" in manifest + ? manifest.scripts + : undefined; + const command = + typeof scripts === "object" && scripts !== null && "lint:dead-code" in scripts + ? scripts["lint:dead-code"] + : undefined; + if (typeof command !== "string") { + throw new Error("package.json declares no lint:dead-code script"); + } + + const argv = command.split(/\s+/).filter((token) => token !== ""); + expect(argv[0]).toEqual("knip"); + return argv.slice(1); +} + +interface Report { + code: number | undefined; + output: string; +} + +/** + * One run of the check, with its report captured rather than forwarded. + * + * Intercepted, because the positive cases expect findings: a report that + * reached this process's own stdout would put `Unused files (1)` in the log of + * a passing suite, where it reads as the failure it is proving cannot hide. + */ +function* runDeadCode(target: string): Operation { + return yield* scoped(function* () { + const knip = yield* exec(path.join(ROOT, "node_modules", ".bin", "knip"), { + arguments: yield* deadCodeArguments(), + cwd: target, + }); + + // A streaming decoder holds the bytes of a split multi-byte character until + // the next chunk, so the two pipes cannot share one. + const outDecoder = new TextDecoder(); + const errDecoder = new TextDecoder(); + const report: string[] = []; + yield* knip.around({ + *stdout([bytes]) { + report.push(outDecoder.decode(bytes, { stream: true })); + }, + *stderr([bytes]) { + report.push(errDecoder.decode(bytes, { stream: true })); + }, + }); + + const status = yield* knip.join(); + report.push(outDecoder.decode(), errDecoder.decode()); + + return { code: status.code, output: report.join("") }; + }); +} + +function* write(target: string, file: string, source: string): Operation { + const destination = path.join(target, file); + yield* ensureDir(path.dirname(destination)); + yield* writeTextFile(destination, source); +} + +function* append(target: string, file: string, source: string): Operation { + const destination = path.join(target, file); + const before = yield* readTextFile(destination); + yield* writeTextFile(destination, `${before}\n${source}`); +} + +/** A module under a first-party source directory that nothing imports. */ +const UNREFERENCED_FILE = "packages/core/src/dead-code-probe.ts"; + +/** A module the import graph already reaches, so only the new export is dead. */ +const REACHED_MODULE = "packages/core/src/expansion.ts"; + +/** The same, for a type: a module whose types other modules already import. */ +const REACHED_TYPES = "packages/cli/src/repl/route.ts"; + +const DEAD_FUNCTION = `export function deadCodeProbe(): string { + return "dead"; +} +`; + +const DEAD_TYPE = `export type DeadCodeProbe = { readonly dead: true }; +`; + +/** + * Modules nothing imports on purpose, which the gate must not call dead. + * + * Each one is reached by something the import graph cannot see — a consumer of + * the published package, the runtime that starts a command, the browser, a + * document that names a component, or a subprocess a test spawns — so a gate + * that reported these would be unusable and a gate that found them by widening + * its entry points to whole source directories would report nothing at all. + */ +const REACHED_OTHERWISE: readonly { readonly file: string; readonly source: string }[] = [ + { + file: "site/routes/dead-code-probe.tsx", + source: "export default function Probe() {\n return null;\n}\n", + }, + { + file: "site/islands/DeadCodeProbe.tsx", + source: "export default function Probe() {\n return null;\n}\n", + }, + { file: "scripts/dead-code-probe.ts", source: DEAD_FUNCTION }, + { file: "smoke-test/dead-code-probe.ts", source: DEAD_FUNCTION }, + { file: "packages/core/tests/fixtures/dead-code-probe.ts", source: DEAD_FUNCTION }, + { file: "packages/git/tests/components/DeadCodeProbe.ts", source: DEAD_FUNCTION }, + { file: "packages/web/examples/dead-code-probe.ts", source: DEAD_FUNCTION }, +]; + +describe("the repository-wide dead-code gate", () => { + it("reports nothing on the committed tree", function* () { + const target = yield* clone(); + + const report = yield* runDeadCode(target); + + expect(report.output).toEqual(""); + expect(report.code).toEqual(0); + }); + + it("rejects an unreferenced first-party source file, and passes once it is gone", function* () { + const target = yield* clone(); + + yield* write(target, UNREFERENCED_FILE, DEAD_FUNCTION); + const rejected = yield* runDeadCode(target); + + expect(rejected.code).toEqual(1); + expect(rejected.output).toContain("Unused files"); + expect(rejected.output).toContain(UNREFERENCED_FILE); + + yield* rm(path.join(target, UNREFERENCED_FILE)); + + expect((yield* runDeadCode(target)).code).toEqual(0); + }); + + it("rejects an unused export on a module the graph already reaches", function* () { + const target = yield* clone(); + const before = yield* readTextFile(path.join(target, REACHED_MODULE)); + + yield* append(target, REACHED_MODULE, DEAD_FUNCTION); + const rejected = yield* runDeadCode(target); + + expect(rejected.code).toEqual(1); + expect(rejected.output).toContain("Unused exports"); + expect(rejected.output).toContain("deadCodeProbe"); + expect(rejected.output).toContain(REACHED_MODULE); + + yield* writeTextFile(path.join(target, REACHED_MODULE), before); + + expect((yield* runDeadCode(target)).code).toEqual(0); + }); + + it("rejects an unused exported type", function* () { + const target = yield* clone(); + const before = yield* readTextFile(path.join(target, REACHED_TYPES)); + + yield* append(target, REACHED_TYPES, DEAD_TYPE); + const rejected = yield* runDeadCode(target); + + expect(rejected.code).toEqual(1); + expect(rejected.output).toContain("Unused exported types"); + expect(rejected.output).toContain("DeadCodeProbe"); + expect(rejected.output).toContain(REACHED_TYPES); + + yield* writeTextFile(path.join(target, REACHED_TYPES), before); + + expect((yield* runDeadCode(target)).code).toEqual(0); + }); + + it("keeps public exports, entry points and dynamically loaded modules clean", function* () { + const target = yield* clone(); + + // A published package export has no internal consumer by definition, and + // removing one is a compatibility break rather than a cleanup. + yield* append(target, "packages/core/api.ts", DEAD_FUNCTION); + for (const { file, source } of REACHED_OTHERWISE) { + yield* write(target, file, source); + } + + const report = yield* runDeadCode(target); + + expect(report.output).toEqual(""); + expect(report.code).toEqual(0); + }); +}); diff --git a/specs/executable-mdx-spec.md b/specs/executable-mdx-spec.md index b74096ca9..357ced906 100644 --- a/specs/executable-mdx-spec.md +++ b/specs/executable-mdx-spec.md @@ -1939,7 +1939,6 @@ run but are absent from the diagnostic trace. | `src/document-request.ts` | `DocumentRequest` and `DocumentProtocolError` — the one-use capability one document expansion is asked through, its detached prop snapshot, and the settlement canonical core reconciles | | `src/bound-exec.ts` | `BoundExecRequest` and `BoundExecProtocolError` — the one-use capability one `exec as="name"` block is run through, and the settlement the issuing expansion binds | | `src/collect.ts` | `collect()` — stream consumption helper, returns `Result` | -| `src/output/mod.ts` | Barrel export for output middleware | | `src/output/normalize.ts` | `useNormalizedOutput()` — whitespace normalization middleware (§9.4) | | `src/output/terminal.ts` | `useTerminalOutput()` — terminal ANSI formatting middleware (§9.5) | | `packages/cli/src/cli.ts` | Runtime-neutral CLI (separate `cli` workspace package) with `--verbose`, `--journal`, and `--raw` flags; Output Api stream consumption (§9.6); seeds `Config.verbose` and registers `` (§6.20) | From 39742175cb115e4dc4c3301a4ec5078590da882f Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 12 Oct 2026 15:34:42 -0400 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=85=20Hold=20the=20runtime=20entrypoi?= =?UTF-8?q?nts=20to=20the=20control=20that=20keeps=20them=20clean=20(#903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The negative case added a published package export and required silence, which left the other half of "nothing imports this on purpose" implicit: the entrypoint each runtime starts. `packages/cli/src/deno.ts` is the one that matters, because it is declared in `deno.json`'s exports and **not** in `package.json`'s — so Knip does not find it on its own, and the configuration names the four by hand. A configuration that dropped that pattern would report an unused export on a file a `deno task xmd` run enters, and nothing here would have said so. Checked against the opposite first: before the assertion existed, appending an unused export to `src/deno.ts` and `src/node.ts` in a clone exited 0, which is what the control now encodes. --- scripts/tests/dead-code.test.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/scripts/tests/dead-code.test.ts b/scripts/tests/dead-code.test.ts index c3febdc87..19629fac2 100644 --- a/scripts/tests/dead-code.test.ts +++ b/scripts/tests/dead-code.test.ts @@ -152,6 +152,15 @@ const DEAD_FUNCTION = `export function deadCodeProbe(): string { const DEAD_TYPE = `export type DeadCodeProbe = { readonly dead: true }; `; +/** A published package export, and the entrypoints each runtime starts. */ +const PUBLISHED_OR_STARTED: readonly string[] = [ + "packages/core/api.ts", + "packages/cli/src/deno.ts", + "packages/cli/src/node.ts", + "packages/cli/src/bun.ts", + "packages/cli/src/compiled.ts", +]; + /** * Modules nothing imports on purpose, which the gate must not call dead. * @@ -240,8 +249,14 @@ describe("the repository-wide dead-code gate", () => { const target = yield* clone(); // A published package export has no internal consumer by definition, and - // removing one is a compatibility break rather than a cleanup. - yield* append(target, "packages/core/api.ts", DEAD_FUNCTION); + // removing one is a compatibility break rather than a cleanup. A runtime + // entrypoint is the same shape for a different reason: the runtime starts + // it, so nothing here imports it either — and `packages/cli/src/deno.ts` is + // declared in `deno.json` alone, which is why the configuration names the + // four by hand instead of trusting `package.json`'s `exports`. + for (const entry of PUBLISHED_OR_STARTED) { + yield* append(target, entry, DEAD_FUNCTION); + } for (const { file, source } of REACHED_OTHERWISE) { yield* write(target, file, source); } From c194e0c2aec14ccaec7fa3fc8d43a656be4d47af Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 12 Oct 2026 15:56:16 -0400 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=A5=20Remove=20the=20dead-code=20g?= =?UTF-8?q?ate's=20own=20tests=20(#903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops `scripts/tests/dead-code.test.ts` and the four `the CI lint job` cases added to `scripts/tests/ci-workflow.test.ts`, with the `packageScripts` helper they shared and the AGENTS.md sentence that pointed at the deleted file. What goes with them is the discrimination, and that is worth stating plainly: nothing in the repository now distinguishes a configuration that finds dead code from one that reports nothing, and nothing holds the `lint` script to chaining the check with `&&`. The gate still runs, and still fails on the mutations by hand; what is gone is the proof that it will keep doing so. --- AGENTS.md | 3 - scripts/tests/ci-workflow.test.ts | 66 -------- scripts/tests/dead-code.test.ts | 269 ------------------------------ 3 files changed, 338 deletions(-) delete mode 100644 scripts/tests/dead-code.test.ts diff --git a/AGENTS.md b/AGENTS.md index 7fb47e00b..0547cf27b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -361,9 +361,6 @@ here: them is dead. Anything else is implementation, and an implementation module never becomes an entry point to silence a finding. Duplicate exports are out of scope: `gitPlugin`/`default` is one value declared two ways, not dead code. - `scripts/tests/dead-code.test.ts` proves the gate by mutating a clone of - `HEAD`, which is what distinguishes a configuration that finds dead code from - one that reports nothing. ### Measuring the corpus diff --git a/scripts/tests/ci-workflow.test.ts b/scripts/tests/ci-workflow.test.ts index a39f53baf..8837cf447 100644 --- a/scripts/tests/ci-workflow.test.ts +++ b/scripts/tests/ci-workflow.test.ts @@ -148,15 +148,6 @@ function* workflow(): Operation> { ); } -/** The scripts `pnpm` and `deno task` both run, read from where they are defined. */ -function* packageScripts(): Operation> { - const manifest = object( - JSON.parse(yield* readTextFile(new URL("package.json", ROOT))), - "package.json", - ); - return stringRecord(manifest.scripts, "package.json.scripts"); -} - function green(workflow: Record): Job { const job = workflow.green; if (job === undefined) { @@ -605,63 +596,6 @@ describe("the conditional CI jobs", () => { }); }); -/** - * The dead-code gate is one of lint's checks rather than a job of its own, so - * what CI runs is the repository's `lint` script — and the chain from the - * workflow to knip is asserted link by link. A `lint` script that stopped - * calling the check, or called it in a way that could not redden the job, would - * leave the required `lint` job green while unreferenced files and exports - * accumulated. - */ -describe("the CI lint job", () => { - it("runs the repository's own lint command", function* () { - const lint = (yield* workflow()).lint; - const commands = (lint?.steps ?? []) - .map((step) => step.run) - .filter((run): run is string => run !== undefined); - - expect(commands).toContain("pnpm lint"); - }); - - it("reaches the dead-code check through that command, chained so a finding fails it", function* () { - const defined = yield* packageScripts(); - - expect(defined.lint).toContain("&& pnpm run lint:dead-code"); - expect(defined["lint:dead-code"]).toContain("knip"); - expect(defined.lint).not.toContain("|| true"); - expect(defined["lint:dead-code"]).not.toContain("|| true"); - }); - - it("asks it for unused files, exports and types, through the checked-in configuration", function* () { - const defined = yield* packageScripts(); - - expect(defined["lint:dead-code"]).toContain("--config knip.json"); - expect(defined["lint:dead-code"]).toContain("--include files,exports,types"); - }); - - /** - * Named per job rather than left to the derived sweep below: the aggregate - * cases assert that *every* dependency can redden `green`, which stays true - * of a workflow that dropped the lint job altogether. - */ - it("cannot satisfy green with anything but success", function* () { - const parsed = yield* workflow(); - const commandText = command(green(parsed)); - - for (const occasion of OCCASIONS) { - for (const result of ["failure", "cancelled", "skipped", "timed_out", "action_required"]) { - const values = required(parsed, occasion); - values.lint = result; - expect({ - occasion, - result, - code: yield* runGreen(commandText, values, occasion), - }).not.toEqual({ occasion, result, code: 0 }); - } - } - }); -}); - describe("the CI workflow aggregate", () => { it("defines green", function* () { expect(green(yield* workflow())).toBeDefined(); diff --git a/scripts/tests/dead-code.test.ts b/scripts/tests/dead-code.test.ts deleted file mode 100644 index 19629fac2..000000000 --- a/scripts/tests/dead-code.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { describe, it } from "@executablemd/test-support/bdd"; -import { expect } from "@executablemd/test-support/expect"; -import { ensureDir, exists, readdir, readTextFile, rm, writeTextFile } from "@effectionx/fs"; -import { exec } from "@effectionx/process"; -import { scoped, until } from "effection"; -import type { Operation } from "effection"; -import { symlink } from "node:fs/promises"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { useTempDirectory } from "@executablemd/test-support/temp"; - -const ROOT = fileURLToPath(new URL("../../", import.meta.url)); - -/** - * The dead-code gate, proved by mutation rather than by reading its report. - * - * A configuration that ignored the first-party source directories, or declared - * an implementation module to be an entry point, would report nothing and look - * exactly like a clean repository. So each case changes the tree and asks - * whether the real command notices: the positive cases add dead code and - * require the offending path or symbol in the report, and the negative cases - * add the kinds of module nothing in the repository imports *by design* — a - * public package export, a runtime entry point, a standalone script, a site - * route and island, a package example, and the fixtures and components a test - * loads by name or spawns as a subprocess — and require silence. - * - * Every case runs in a clone of `HEAD` with this checkout's installed - * dependencies linked in, so a mutation can never reach the worktree another - * check is reading and nothing is installed to make the clone usable. A clone - * carries the committed configuration, so run these against a committed change. - */ -function* clone(): Operation { - const target = yield* useTempDirectory("dead-code-"); - - yield* exec("git", { - arguments: ["clone", "--shared", "--quiet", ROOT, target], - cwd: ROOT, - }).expect(); - - // Knip resolves the import graph, and pnpm gives each workspace member its - // own `node_modules` beside the root one. Linking both layouts is what lets - // the clone resolve what this checkout already resolved; a clone that had to - // install first would move dependency state the rest of the battery is - // reading through. - yield* link(path.join(ROOT, "node_modules"), path.join(target, "node_modules")); - for (const member of yield* readdir(path.join(ROOT, "packages"))) { - const dependencies = path.join(ROOT, "packages", member, "node_modules"); - if (yield* exists(dependencies)) { - yield* link(dependencies, path.join(target, "packages", member, "node_modules")); - } - } - - return target; -} - -function link(from: string, to: string): Operation { - return until(symlink(from, to, "dir")); -} - -/** - * The command the repository checks in, read from where it is defined. - * - * Derived rather than restated: a test carrying its own copy of the arguments - * would keep passing after `lint:dead-code` stopped checking files, exports or - * types, which is the one thing these cases exist to establish. - */ -function* deadCodeArguments(): Operation { - const manifest: unknown = JSON.parse(yield* readTextFile(path.join(ROOT, "package.json"))); - const scripts = - typeof manifest === "object" && manifest !== null && "scripts" in manifest - ? manifest.scripts - : undefined; - const command = - typeof scripts === "object" && scripts !== null && "lint:dead-code" in scripts - ? scripts["lint:dead-code"] - : undefined; - if (typeof command !== "string") { - throw new Error("package.json declares no lint:dead-code script"); - } - - const argv = command.split(/\s+/).filter((token) => token !== ""); - expect(argv[0]).toEqual("knip"); - return argv.slice(1); -} - -interface Report { - code: number | undefined; - output: string; -} - -/** - * One run of the check, with its report captured rather than forwarded. - * - * Intercepted, because the positive cases expect findings: a report that - * reached this process's own stdout would put `Unused files (1)` in the log of - * a passing suite, where it reads as the failure it is proving cannot hide. - */ -function* runDeadCode(target: string): Operation { - return yield* scoped(function* () { - const knip = yield* exec(path.join(ROOT, "node_modules", ".bin", "knip"), { - arguments: yield* deadCodeArguments(), - cwd: target, - }); - - // A streaming decoder holds the bytes of a split multi-byte character until - // the next chunk, so the two pipes cannot share one. - const outDecoder = new TextDecoder(); - const errDecoder = new TextDecoder(); - const report: string[] = []; - yield* knip.around({ - *stdout([bytes]) { - report.push(outDecoder.decode(bytes, { stream: true })); - }, - *stderr([bytes]) { - report.push(errDecoder.decode(bytes, { stream: true })); - }, - }); - - const status = yield* knip.join(); - report.push(outDecoder.decode(), errDecoder.decode()); - - return { code: status.code, output: report.join("") }; - }); -} - -function* write(target: string, file: string, source: string): Operation { - const destination = path.join(target, file); - yield* ensureDir(path.dirname(destination)); - yield* writeTextFile(destination, source); -} - -function* append(target: string, file: string, source: string): Operation { - const destination = path.join(target, file); - const before = yield* readTextFile(destination); - yield* writeTextFile(destination, `${before}\n${source}`); -} - -/** A module under a first-party source directory that nothing imports. */ -const UNREFERENCED_FILE = "packages/core/src/dead-code-probe.ts"; - -/** A module the import graph already reaches, so only the new export is dead. */ -const REACHED_MODULE = "packages/core/src/expansion.ts"; - -/** The same, for a type: a module whose types other modules already import. */ -const REACHED_TYPES = "packages/cli/src/repl/route.ts"; - -const DEAD_FUNCTION = `export function deadCodeProbe(): string { - return "dead"; -} -`; - -const DEAD_TYPE = `export type DeadCodeProbe = { readonly dead: true }; -`; - -/** A published package export, and the entrypoints each runtime starts. */ -const PUBLISHED_OR_STARTED: readonly string[] = [ - "packages/core/api.ts", - "packages/cli/src/deno.ts", - "packages/cli/src/node.ts", - "packages/cli/src/bun.ts", - "packages/cli/src/compiled.ts", -]; - -/** - * Modules nothing imports on purpose, which the gate must not call dead. - * - * Each one is reached by something the import graph cannot see — a consumer of - * the published package, the runtime that starts a command, the browser, a - * document that names a component, or a subprocess a test spawns — so a gate - * that reported these would be unusable and a gate that found them by widening - * its entry points to whole source directories would report nothing at all. - */ -const REACHED_OTHERWISE: readonly { readonly file: string; readonly source: string }[] = [ - { - file: "site/routes/dead-code-probe.tsx", - source: "export default function Probe() {\n return null;\n}\n", - }, - { - file: "site/islands/DeadCodeProbe.tsx", - source: "export default function Probe() {\n return null;\n}\n", - }, - { file: "scripts/dead-code-probe.ts", source: DEAD_FUNCTION }, - { file: "smoke-test/dead-code-probe.ts", source: DEAD_FUNCTION }, - { file: "packages/core/tests/fixtures/dead-code-probe.ts", source: DEAD_FUNCTION }, - { file: "packages/git/tests/components/DeadCodeProbe.ts", source: DEAD_FUNCTION }, - { file: "packages/web/examples/dead-code-probe.ts", source: DEAD_FUNCTION }, -]; - -describe("the repository-wide dead-code gate", () => { - it("reports nothing on the committed tree", function* () { - const target = yield* clone(); - - const report = yield* runDeadCode(target); - - expect(report.output).toEqual(""); - expect(report.code).toEqual(0); - }); - - it("rejects an unreferenced first-party source file, and passes once it is gone", function* () { - const target = yield* clone(); - - yield* write(target, UNREFERENCED_FILE, DEAD_FUNCTION); - const rejected = yield* runDeadCode(target); - - expect(rejected.code).toEqual(1); - expect(rejected.output).toContain("Unused files"); - expect(rejected.output).toContain(UNREFERENCED_FILE); - - yield* rm(path.join(target, UNREFERENCED_FILE)); - - expect((yield* runDeadCode(target)).code).toEqual(0); - }); - - it("rejects an unused export on a module the graph already reaches", function* () { - const target = yield* clone(); - const before = yield* readTextFile(path.join(target, REACHED_MODULE)); - - yield* append(target, REACHED_MODULE, DEAD_FUNCTION); - const rejected = yield* runDeadCode(target); - - expect(rejected.code).toEqual(1); - expect(rejected.output).toContain("Unused exports"); - expect(rejected.output).toContain("deadCodeProbe"); - expect(rejected.output).toContain(REACHED_MODULE); - - yield* writeTextFile(path.join(target, REACHED_MODULE), before); - - expect((yield* runDeadCode(target)).code).toEqual(0); - }); - - it("rejects an unused exported type", function* () { - const target = yield* clone(); - const before = yield* readTextFile(path.join(target, REACHED_TYPES)); - - yield* append(target, REACHED_TYPES, DEAD_TYPE); - const rejected = yield* runDeadCode(target); - - expect(rejected.code).toEqual(1); - expect(rejected.output).toContain("Unused exported types"); - expect(rejected.output).toContain("DeadCodeProbe"); - expect(rejected.output).toContain(REACHED_TYPES); - - yield* writeTextFile(path.join(target, REACHED_TYPES), before); - - expect((yield* runDeadCode(target)).code).toEqual(0); - }); - - it("keeps public exports, entry points and dynamically loaded modules clean", function* () { - const target = yield* clone(); - - // A published package export has no internal consumer by definition, and - // removing one is a compatibility break rather than a cleanup. A runtime - // entrypoint is the same shape for a different reason: the runtime starts - // it, so nothing here imports it either — and `packages/cli/src/deno.ts` is - // declared in `deno.json` alone, which is why the configuration names the - // four by hand instead of trusting `package.json`'s `exports`. - for (const entry of PUBLISHED_OR_STARTED) { - yield* append(target, entry, DEAD_FUNCTION); - } - for (const { file, source } of REACHED_OTHERWISE) { - yield* write(target, file, source); - } - - const report = yield* runDeadCode(target); - - expect(report.output).toEqual(""); - expect(report.code).toEqual(0); - }); -});