diff --git a/.claude/skills/migration-graduate/SKILL.md b/.claude/skills/migration-graduate/SKILL.md index be4c5c4..e29be4c 100644 --- a/.claude/skills/migration-graduate/SKILL.md +++ b/.claude/skills/migration-graduate/SKILL.md @@ -28,6 +28,18 @@ For each graduated command: - The legacy handler is either deleted (most common) or kept as a tiny adapter the command's execute calls. - All tests still pass. +## Finding graduation candidates with ESLint + +`eslint-plugin-acture-migration` ships the `acture/no-stale-wrap-mutation` rule, which flags `wrapMutation(...)` calls whose result is never used — a strong single-file signal that the wrapper has graduated. Enable it during a migration and the lint warnings become your graduation backlog: + +```js +// eslint.config.js +import acture from 'eslint-plugin-acture-migration'; +export default [{ plugins: { acture }, rules: { 'acture/no-stale-wrap-mutation': 'warn' } }]; +``` + +The rule is conservative (it stays quiet on exported or still-referenced bindings), so a clean lint run does not prove every wrapper has graduated — but every warning is a real candidate. Step 1 below is the cross-file verification the rule cannot do. + ## Steps ### 1. Pick a wrapped command to graduate diff --git a/AGENTS.md b/AGENTS.md index 18e375a..b73f603 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,9 +77,9 @@ Full discussion is in `docs/redesign_takeaways.md` §3 and the `acture-hard-dont - Generalizing beyond what `v1_plan.md` commits to. Rule of three. - Modifying the central paper (`docs/command_dispatch_journal_article.md`). It is canonical. -## Current state (v1.3, Phase 4 + v1.1 + v1.2 + v1.3 increments DONE, 2026-05-13) +## Current state (v1.4, Phase 4 + v1.1 → v1.4 increments DONE, 2026-05-14) -Fourteen packages ship in the workspace at versions ranging from v1.0.0 to v1.2.0: +Fifteen packages ship in the workspace at versions ranging from v1.0.0 to v1.2.0: - Core: `acture@1.1.0` — `enableTierWarnings`, `deprecationReason`, `internalToken`, `DispatchOptions`. - State: `@acture/state-zustand@1.0.0`, `@acture/state-redux@1.0.0`. @@ -87,9 +87,12 @@ Fourteen packages ship in the workspace at versions ranging from v1.0.0 to v1.2. - Surfaces: `@acture/mcp@1.0.0`, `@acture/ai-vercel@1.0.0` — honour the tier filter and prepend `[DEPRECATED — ]`. - Migration: `@acture/migration@1.1.0` — `createDomInterceptor` for DOM-event interception. - Tooling: `@acture/build-tier@1.1.0` (regex + AST mode), `@acture/cli@1.2.0` (deep nested compare-schemas diffs), `@acture/devtools@1.0.0`. -- Codemods: `@acture/codemods@1.1.0` — **research-4 §B.5 codemod set is complete in v1.3** (5 codemods: `wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). +- Codemods: `@acture/codemods@1.1.0` — **research-4 §B.5 codemod set is complete** (5 codemods: `wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). +- Lint: `eslint-plugin-acture-migration@1.0.0` — **new in v1.4.** One rule, `acture/no-stale-wrap-mutation`: flags `wrapMutation(...)` calls whose result is never used (the migration has graduated; author with `defineCommand`). -Four worked examples: `examples/greenfield/graph-editor/`, `examples/drop-in/`, `examples/migration/zustand-wrap/{before,after}/`, `examples/migration/redux-wrap/` (RTK + `actureMiddleware` end-to-end). See `docs/next_session.md` for the v1.4 backlog. +Four worked examples: `examples/greenfield/graph-editor/`, `examples/drop-in/`, `examples/migration/zustand-wrap/{before,after}/`, `examples/migration/redux-wrap/` (RTK + `actureMiddleware` end-to-end). + +v1.4 also ran the deferred fresh-agent release-gate test against `@acture/codemods` — see `docs/fresh-agent-test-results.md`. The codemod engine passed; README-accuracy fixes are carried to v1.5. See `docs/next_session.md` for the v1.5 backlog. ## Phase progression diff --git a/README.md b/README.md index a6ce3f7..110d747 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,17 @@ pnpm add @acture/migration # strangler-fig adoption primitives # …plus @acture/forms-autoform and @acture/forms-rjsf for parameterized commands. # Dev / CI tooling (post-v1.0): -pnpm add -D @acture/build-tier # build-step @stable/@experimental/@internal/@deprecated mirror -pnpm add -D @acture/cli # `acture compare-schemas` / `acture snapshot` CLI -pnpm add -D @acture/devtools # embeddable for dev builds +pnpm add -D @acture/build-tier # build-step @stable/@experimental/@internal/@deprecated mirror +pnpm add -D @acture/cli # `acture compare-schemas` / `acture snapshot` CLI +pnpm add -D @acture/devtools # embeddable for dev builds +pnpm add -D eslint-plugin-acture-migration # ESLint rule that flags stale wrapMutation wrappers ``` > The `acture` name is also reserved on PyPI as a placeholder; a real Python companion is post-v1. `pip install acture` gives you a no-op package whose only purpose is to keep the name ours. ## Status -**v1.3.0 (Phase 4 DONE + v1.1 + v1.2 + v1.3 increments, 2026-05-13).** Fourteen packages ship in the workspace: +**v1.4.0 (Phase 4 DONE + v1.1 → v1.4 increments, 2026-05-14).** Fifteen packages ship in the workspace: | Package | Role | | --- | --- | @@ -44,6 +45,7 @@ pnpm add -D @acture/devtools # embeddable for dev builds | [`@acture/cli`](packages/cli) | `acture compare-schemas` (CI gating, deep nested diffs) + `acture snapshot` (registry → JSON) | | [`@acture/devtools`](packages/devtools) | embeddable `` and `instrumentRegistry` dispatch log | | [`@acture/codemods`](packages/codemods) | Codemod CLI: all five research-4 §B.5 codemods now shipped (`wrap-handler-with-mutation`, `extract-onclick-to-command`, `redux-action-to-command`, `usestate-mutation-to-command`, `rtk-thunk-to-command`). `--dry-run` + `--json` for agents | +| [`eslint-plugin-acture-migration`](packages/eslint-plugin-acture-migration) | ESLint rule `acture/no-stale-wrap-mutation` — flags `wrapMutation(...)` wrappers whose result is never used (the migration has graduated; author with `defineCommand`) | Worked examples: @@ -54,6 +56,11 @@ Worked examples: Agent skills live under [`.claude/skills/`](.claude/skills/): five migration-track skills (`migration-diagnose`, `migration-plan`, `migration-scaffold`, `migration-wrap`, `migration-graduate`) plus the architecture / tier / schema / hard-don'ts primer skills. +What's new in v1.4 (release-readiness theme): + +- **`eslint-plugin-acture-migration`.** One rule, `acture/no-stale-wrap-mutation`: flags `wrapMutation(...)` calls whose result is never used — the strangler-fig wrapper has graduated and should become a `defineCommand`. Single-file, conservative detection. +16 tests. Closes a research-4 backlog item carried since v1.1. +- **Fresh-agent release-gate test.** A fresh agent drove `@acture/codemods` from its README alone. The codemod engine + CLI passed; the README's pre-publish `npx` invocation and undocumented `--option` keys did not. Full assessment in [`docs/fresh-agent-test-results.md`](docs/fresh-agent-test-results.md); fixes carried to v1.5. + What's new in v1.3: - **Codemod set complete.** Three new codemods finish research-4 §B.5: `redux-action-to-command` (RTK action calls → registry.dispatch), `usestate-mutation-to-command` (setX-only handlers → wrapMutation), `rtk-thunk-to-command` (`createAsyncThunk` → `defineCommand`). +30 tests; manifest now has zero `status: 'planned'` entries. @@ -74,7 +81,7 @@ Previously in v1.0 / v1.1: - **``.** Embeddable React dev-tool with a command list (tier-filterable), dispatch log, and live when-clause evaluator. Mount it behind a toggle in any greenfield app. - **`enableTierWarnings(registry)`.** Once-per-process `console.warn` on first dispatch of each `@experimental` command. Suppress with `ACTURE_SUPPRESS_EXPERIMENTAL_WARNINGS=1`. -What's next: see [`docs/next_session.md`](docs/next_session.md) for the v1.4 backlog. +What's next: see [`docs/next_session.md`](docs/next_session.md) for the v1.5 backlog. ## Three paths diff --git a/docs/fresh-agent-test-results.md b/docs/fresh-agent-test-results.md new file mode 100644 index 0000000..3dc67ce --- /dev/null +++ b/docs/fresh-agent-test-results.md @@ -0,0 +1,51 @@ +# Fresh-agent release-gate test — results + +**Run:** 2026-05-14, as part of v1.4 (release-readiness theme). +**Gate origin:** Phase-4 reflection §5 deferred this through v1.0 → v1.3. v1.3's reflection §"Pre-v1.4 reflection answers" §4 named the next session as the right place to run it, with the `@acture/codemods` README as the densest agent-facing surface in the repo. + +## What was tested + +A fresh agent — no prior context about acture — was given only: + +- the location of `packages/codemods/README.md`, +- the fact that `@acture/codemods` is an unpublished workspace package whose CLI is built at `packages/codemods/dist/cli.js`. + +It was instructed to read **only the README** (not source, not other docs, not skills), pick one codemod, build a realistic sample file, drive the CLI end-to-end (`--list`, `--help`, `--dry-run --json`, real apply), and deliberately mis-use the CLI to test error messages. The deliverable was a written assessment of where the README + CLI fall short — **no code change in v1.4** (per `docs/next_session.md` §"Strong candidates" #2). + +## Outcome + +**The codemod mechanics passed; the README's accuracy did not.** The agent got `wrap-handler-with-mutation` working end-to-end (handler wrapped, import added, output functionally correct) and rated the CLI surface — `--list`, `--help`, `--dry-run`, `--json`, the per-file `before`/`after`/`changed` JSON, the readable plain-text diff — as solid. But it returned a **"not ready to ship as-is"** verdict, driven entirely by README gaps and one error-message ambiguity. + +### Findings (verbatim priority order from the fresh agent) + +1. **The headline invocation does not work.** Every Quick-start and CLI example uses `npx @acture/codemods …` or the `acture-codemods` bin. The package is unpublished, so the *first command a copy-pasting user runs* fails with an npm 404. The README never mentions `node dist/cli.js`, a workspace bin alias, a build step, or publish status. Single biggest blocker. + +2. **Per-codemod `--option` keys are undiscoverable.** The README mentions `--option key=value` generically and the codemod table alludes to "configurable setter pattern" / "optional slash→dot id rewrite", but no option *names* are listed anywhere a CLI user can see them. `--help` shows one example (`id-prefix=app.button`) but no enumeration. The programmatic example leaks `events: 'onClick,onSubmit'` — so the options exist, just unlisted. + +3. **`--manifest` and `--files-from` are under/undocumented.** `--manifest` appears in the CLI usage block with no explanation (vs. `--list`?). `--files-from` appears in `--help` but is **absent from the README entirely**. + +4. **"No files matched" error is ambiguous.** A missing `--target` and a *nonexistent* `--target` path produce the identical message ("No files matched. Use --target…"). A user who typo'd a real path is told to "use --target" when they already did. + +5. **Cosmetic:** rewritten handler bodies are over-indented in the output diff. Harmless, but a reviewing user may distrust the transform. + +6. **Undocumented exit codes** (errors → 2, no-args → 0). Minor. + +Positives worth keeping: `--help` is actually *more* complete than the README; the JSON dry-run shape is "exactly what an agent needs"; the bad-codemod-name error helpfully lists all valid names. + +## Assessment + +The v1.x codemod *engine* is release-ready — the abstraction shape, the dry-run/JSON contract, and the error handling for the common "wrong codemod name" case are all sound. The release risk is **documentation drift**, not code: the README was written assuming a published package and never revisited for the pre-publish reality, and it under-documents the CLI's own surface (`--option` keys, `--manifest`, `--files-from`). + +None of the findings indicate a design problem or a hard-don't violation. They are all README edits plus one ~3-line error-message disambiguation. Because `docs/next_session.md` explicitly scoped #2 as a no-code-change written assessment, **the fixes are deferred to v1.5** and carried forward as the top candidate in the v1.5 planning prompt. + +## Recommended v1.5 follow-up (codemods README + CLI polish) + +In priority order: + +1. **Fix the invocation story.** Either document the monorepo invocation (`node dist/cli.js …` or a `pnpm` workspace bin) alongside the `npx` form, or add an explicit "published?" status line. The Quick start must contain at least one command that actually runs today. +2. **Document per-codemod `--option` keys** — a column in the codemod table, or a short sub-section per codemod. They must be discoverable without reading source. +3. **Document `--manifest` and `--files-from`** in the README; explain `--manifest` vs `--list`. +4. **Disambiguate the "No files matched" error** — distinguish "no `--target` given" from "`--target` path does not exist". +5. (Optional) Fix the output over-indentation; document exit codes. + +Estimated effort: README pass + one small CLI error-message edit + ~3 tests. A natural quick-win pairing for v1.5. diff --git a/docs/next_session.md b/docs/next_session.md index 476e30e..3a7879a 100644 --- a/docs/next_session.md +++ b/docs/next_session.md @@ -1,28 +1,26 @@ -# Next Session — v1.4 Planning +# Next Session — v1.5 Planning -**Your role:** You are the v1.4 planning / implementing agent. **v1.3 is DONE as of 2026-05-13.** Phase 4 v1.0 + v1.1 + v1.2 + v1.3 increments have all landed. Your job is to confirm v1.4 scope with the user and ship it. +**Your role:** You are the v1.5 planning / implementing agent. **v1.4 is DONE as of 2026-05-14.** Phase 4 v1.0 + v1.1 + v1.2 + v1.3 + v1.4 increments have all landed. Your job is to confirm v1.5 scope with the user and ship it. -**v1.3 finished 2026-05-13.** Repo state at handoff: +**v1.4 finished 2026-05-14.** Repo state at handoff: -- **14 packages publishable.** Versions: `acture@1.1.0`, `@acture/cli@1.2.0`, `@acture/migration@1.1.0`, `@acture/build-tier@1.1.0`, `@acture/codemods@1.1.0`, others at `1.0.0`. -- **380 package tests** + **41 example tests** all green. +- **15 packages publishable.** New this increment: `eslint-plugin-acture-migration@1.0.0`. Other notable versions: `acture@1.1.0`, `@acture/cli@1.2.0`, `@acture/migration@1.1.0`, `@acture/build-tier@1.1.0`, `@acture/codemods@1.1.0`, the rest at `1.0.0`. +- **396 package tests** + **41 example tests** all green. - **4 worked examples** unchanged. - All packages typecheck and build via tsup / vite. -- v1.3 reflection: [`docs/v1_3-reflection.md`](v1_3-reflection.md). +- v1.4 reflection: [`docs/v1_4-reflection.md`](v1_4-reflection.md). +- v1.4 ran the deferred fresh-agent release-gate test — results in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md). -What v1.3 shipped on top of v1.2: +What v1.4 shipped on top of v1.3: -- `redux-action-to-command` codemod (research-4 §B.5 row 2). -- `usestate-mutation-to-command` codemod (research-4 §B.5 row 3). -- `rtk-thunk-to-command` codemod (research-4 §B.5 row 5). -- Manifest now contains ZERO `status: 'planned'` entries — research-4 §B.5 is fully shipped. +- **`eslint-plugin-acture-migration`** — one rule, `acture/no-stale-wrap-mutation`, flags `wrapMutation(...)` calls whose result is never used (the migration has graduated; author with `defineCommand`). Closes a research-4 backlog item carried since v1.1. The `migration-graduate` skill now points at it. +- **Fresh-agent release-gate test** — a fresh agent drove `@acture/codemods` from its README alone. Engine + CLI passed; the README did not (see below). What's still in the backlog: -- **`eslint-plugin-acture-migration`** — `acture/no-stale-wrap-mutation` rule. Research-4 §"Defer to v1.2" item #9, slipped through v1.2 and v1.3. Three-callers check: any migration that uses `wrapMutation` extensively accumulates stale wrappers; ESLint is the natural feedback channel. -- **`.d.ts` mirror of resolved tier values.** Optional polish — JSDoc already surfaces in `.d.ts`; this would put the resolved `tier: 'experimental'` on the type-system path too. -- **Hypermod-style AI-generation recipe doc.** Research-4 recommendation #8 — markdown doc in `docs/` showing how to ask Claude to write a one-off codemod for a handler shape. -- **Fresh-agent / second-agent release gate.** Phase-4 reflection §5 deferred this through v1.0 → v1.3. The `@acture/codemods` README is now the densest agent-facing surface; this is the right session to run the ritual. +- **Codemods README + CLI polish.** The v1.4 fresh-agent test found the `@acture/codemods` README's headline `npx @acture/codemods` invocation fails pre-publish, and that per-codemod `--option` keys, `--manifest`, and `--files-from` are undiscoverable from the docs. The CLI also gives an ambiguous "No files matched" error for missing-vs-nonexistent `--target`. Full finding list and priority order in [`docs/fresh-agent-test-results.md`](fresh-agent-test-results.md) §"Recommended v1.5 follow-up". This is the direct, scoped output of v1.4's release gate. +- **`.d.ts` mirror of resolved tier values.** Optional polish — JSDoc already surfaces in `.d.ts`; this would put the resolved `tier: 'experimental'` on the type-system path too. Slipped through v1.2 → v1.4. +- **Hypermod-style AI-generation recipe doc.** Research-4 recommendation #8 — markdown doc in `docs/` showing how to ask Claude to write a one-off codemod for a handler shape that doesn't match any of the shipped five. --- @@ -30,29 +28,28 @@ What's still in the backlog: Read in this order (~15 minutes total): -1. `docs/v1_3-reflection.md` — what v1.3 found, especially §"Pre-v1.4 reflection answers" §4 (release gate recommendation). -2. `.claude/skills/acture-hard-donts/SKILL.md` — re-read before merging anything. -3. `docs/v1_plan.md` §"Post-v1 (deferred, not committed)" — long-term backlog (undo, macros, telemetry, Python companion). None promote to v1.4 without explicit user direction. +1. `docs/v1_4-reflection.md` — what v1.4 found, especially §"Pre-v1.5 reflection answers". +2. `docs/fresh-agent-test-results.md` — the release-gate findings that drive the top v1.5 candidate. +3. `.claude/skills/acture-hard-donts/SKILL.md` — re-read before merging anything. +4. `docs/v1_plan.md` §"Post-v1 (deferred, not committed)" — long-term backlog (undo, macros, telemetry, Python companion). None promote to v1.5 without explicit user direction. -## Step 2 — Pick v1.4 scope +## Step 2 — Pick v1.5 scope -Rule of three. The four candidates below are concrete and bounded. Pick at most TWO unless the user explicitly authorizes more. +Rule of three. The three candidates below are concrete and bounded. Pick at most TWO unless the user explicitly authorizes more. -**Strong candidates (v1.4):** +**Strong candidate (v1.5):** -1. **`eslint-plugin-acture-migration`** with `acture/no-stale-wrap-mutation`. Flags `wrapMutation(...)` calls where the wrapped handler is the only caller — the migration has graduated and the wrapper is now dead weight. One-rule plugin, small surface, high signal for active migrations. ~150 lines + ~10 tests. Three-callers check passes: research-4 named it; the migration-graduate skill points at it; active wrappers accumulate stale. - -2. **Fresh-agent / second-agent release gate ritual.** Run the experiment: a fresh agent reads `@acture/codemods/README.md` and uses the CLI on a sample handler. Document the result in `docs/fresh-agent-test-results.md`. No code change; the deliverable is a written assessment of where the README + CLI fall short (if anywhere). Could pair with #1 — both are release-readiness work. +1. **Codemods README + CLI polish.** Act on `docs/fresh-agent-test-results.md` §"Recommended v1.5 follow-up". In priority order: (a) fix the invocation story — document a monorepo invocation (`node dist/cli.js …` or a workspace bin) alongside the `npx` form, or add an explicit publish-status line so the Quick start contains at least one command that runs today; (b) document per-codemod `--option` keys — a column in the codemod table or a per-codemod sub-section; (c) document `--manifest` and `--files-from` in the README; (d) disambiguate the "No files matched" CLI error for missing-vs-nonexistent `--target`. Mostly a README pass plus one small CLI error-message edit + ~3 tests. This is the direct output of v1.4's release gate — high signal, well-scoped. **Medium candidates (could ship as quick polish):** -3. **`.d.ts` mirror of resolved tier values.** A tsup post-process pass that walks the emitted `.d.ts` files and injects the resolved `tier:` value at the type level. Small lift if implemented as a `@acture/build-tier/dts-mirror` companion to the existing JS-side mirror. +2. **`.d.ts` mirror of resolved tier values.** A tsup post-process pass that walks the emitted `.d.ts` files and injects the resolved `tier:` value at the type level. Small lift if implemented as a `@acture/build-tier/dts-mirror` companion to the existing JS-side mirror. -4. **Hypermod-style AI-generation recipe doc.** Pure docs (no code) — markdown in `docs/` showing how to prompt Claude to author a one-off codemod for a handler shape that doesn't match any of the shipped five. The recipe references the existing `Codemod` interface so users can drop the generated code into the `@acture/codemods` package or use it standalone. +3. **Hypermod-style AI-generation recipe doc.** Pure docs (no code) — markdown in `docs/` showing how to prompt Claude to author a one-off codemod for a handler shape that doesn't match any of the shipped five. References the existing `Codemod` interface so users can drop the generated code into `@acture/codemods` or use it standalone. -**My recommendation if asked:** ship **#1 (ESLint plugin)** plus **#2 (fresh-agent test)**. Together they round out the "release-readiness" theme of v1.4 — one closes a documented backlog item, the other validates the agent-facing surface is solid before declaring v1.x complete. #3 and #4 are post-v1.4 polish. +**My recommendation if asked:** ship **#1 (codemods polish)** — it closes the loop on the v1.4 release gate and removes a real "first command fails" blocker. Optionally pair with **#3 (AI-recipe doc)** since it's pure docs and thematically adjacent to the codemods surface. #2 is still optional polish and can wait. -## Step 3 — Things that are still post-v1.4 +## Step 3 — Things that are still post-v1.5 These remain `docs/v1_plan.md` §"Post-v1 (deferred, not committed)": @@ -65,21 +62,21 @@ Do NOT promote any of these without explicit user direction AND three concrete c ## Step 4 — Hard-don'ts still in force -Re-read `.claude/skills/acture-hard-donts/SKILL.md`. The closed-surface principle held through v1.0 + v1.1 + v1.2 + v1.3. CommandRecord remains at 15 fields. Hold the line. +Re-read `.claude/skills/acture-hard-donts/SKILL.md`. The closed-surface principle held through v1.0 → v1.4. CommandRecord remains at 15 fields. Hold the line. -## Step 5 — Release ceremony for v1.4 +## Step 5 — Release ceremony for v1.5 -When v1.4 deliverables are merged and tests are green: +When v1.5 deliverables are merged and tests are green: -1. Bump only the affected packages. +1. Bump only the affected packages (e.g. `@acture/codemods` if its CLI/README changed — a `patch` for docs + error-message, a `minor` if a new flag is added). 2. `pnpm -r --filter "./packages/*" build && pnpm test` — green. 3. `npm pack --dry-run` clean for each bumped package. 4. Tag and publish (owner discretion). -5. Write `docs/v1_4-reflection.md`. -6. Replace this file with a v1.5 / post-v1 planning prompt. +5. Write `docs/v1_5-reflection.md`. +6. Replace this file with a v1.6 / post-v1 planning prompt. ## When unsure -Re-read this file, `docs/v1_3-reflection.md`, `docs/v1_plan.md` §"Post-v1", and `.claude/skills/acture-hard-donts/SKILL.md`. If still unsure, append a note to `docs/escalations.md` (create if missing) and ask the user before locking in any irreversible decision. +Re-read this file, `docs/v1_4-reflection.md`, `docs/fresh-agent-test-results.md`, `docs/v1_plan.md` §"Post-v1", and `.claude/skills/acture-hard-donts/SKILL.md`. If still unsure, append a note to `docs/escalations.md` (create if missing) and ask the user before locking in any irreversible decision. -**Good luck.** v1.3 finished the research-4 §B.5 codemod set cleanly. v1.4 should focus on release-readiness — the remaining backlog items are all either small polish or process work. Don't promote post-v1 items into v1.x. +**Good luck.** v1.4 rounded out the release-readiness theme — the ESLint plugin closed a backlog item and the fresh-agent test validated the codemod surface, surfacing a tight, scoped punch list. v1.5 should act on that punch list. Don't promote post-v1 items into v1.x. diff --git a/docs/v1_4-reflection.md b/docs/v1_4-reflection.md new file mode 100644 index 0000000..b409967 --- /dev/null +++ b/docs/v1_4-reflection.md @@ -0,0 +1,89 @@ +# v1.4 Reflection + +**Authored:** 2026-05-14 by the v1.4 implementing agent. All previous tests still pass; **396 package tests** (was 380 at end of v1.3; +16 from the new ESLint rule). Example tests unchanged at 41. Every package and example typechecks and builds via tsup / vite. + +v1.4 was a **release-readiness** session, not a feature session. The user authorized exactly the two recommended candidates from `docs/next_session.md`: the ESLint plugin (#1) and the fresh-agent release-gate test (#2). Both shipped; nothing else was promoted. + +## What v1.4 shipped + +### 1. `eslint-plugin-acture-migration` — new package (`@1.0.0`) + +One rule, `acture/no-stale-wrap-mutation`. It flags `wrapMutation(...)` calls whose **return value is never used** — the only effect of such a call is the registry-registration side effect, which means the strangler-fig wrapper has graduated and the command should be authored directly with `defineCommand`. + +Detection is single-file and conservative, by design (research-4's codemod principle: a high-confidence partial signal beats a noisy total one): + +- Tracks `wrapMutation` imported — named or aliased — from `@acture/migration` (configurable via the `module` option). Namespace imports are not tracked. +- Reports two shapes: a bare `ExpressionStatement`, and assignment to a non-exported local binding with zero references beyond its own initializer. +- Stays silent on exported bindings, referenced bindings, returned results, and results passed as arguments — any of which may still be load-bearing. + +~140 lines of rule + index, 16 RuleTester cases wired into vitest. The package has zero runtime dependencies; `eslint >=9` is a peer dependency. The `migration-graduate` skill now points at the rule (the v1.4 next-session prompt had assumed it already did — it didn't, now it does). + +### 2. Fresh-agent release-gate test — `docs/fresh-agent-test-results.md` + +The Phase-4-reflection §5 gate, deferred through v1.0 → v1.3, finally ran. A fresh agent — no acture context — drove `@acture/codemods` from its README alone. Verdict: the **codemod engine + CLI passed** (the abstraction shape, the `--dry-run`/`--json` contract, `--list`/`--help`, the bad-codemod-name error all rated solid), but the **README did not** — its headline `npx @acture/codemods` invocation fails pre-publish, and per-codemod `--option` keys / `--manifest` / `--files-from` are undiscoverable from the docs. Per the next-session scope (#2 = no code change), the fixes are written up and carried to v1.5 as the top candidate. + +## What v1.4 did NOT ship + +The two medium candidates from `docs/next_session.md`, deliberately not promoted (rule of three; the user authorized exactly #1 + #2): + +- **`.d.ts` mirror of resolved tier values.** Still optional polish. Deferred. +- **Hypermod-style AI-generation recipe doc.** Still optional. Deferred. + +Post-v1 items (`undo`, `macros`, `telemetry`, `sandbox`, `test-property`, `state-jotai`/`state-valtio`, Python companion) remain untouched and uncommitted. + +## Hard-don'ts audit + +Ran `.claude/skills/acture-hard-donts/SKILL.md` against the v1.4 increment. + +1. **No conditional logic in command metadata.** ✅ Zero `CommandRecord` shape changes (still 15 fields). The ESLint rule reads source AST; it never touches the record shape. +2. **No god-package.** ✅ The ESLint plugin is its own package with one rule and zero runtime deps — the opposite of bloat. It does not re-export anything from `acture` or `@acture/migration`. +3. **No business logic in adapter packages.** ✅ The rule is a static analyzer: AST in, lint report out. It makes no domain decisions — it does not decide *whether* to graduate, only flags the structural signal. The cross-file verification and the actual rewrite stay with the human/agent (per the `migration-graduate` skill). +4. **No `if (mode === ...)` in shared helpers.** ✅ The rule branches on AST shape (bare statement vs. unused binding) and on the user-supplied `module` option. No positioning-path awareness. +5. **No `eval()`-ing LLM-produced strings.** ✅ N/A — a lint rule executes nothing. +6. **No coupling the registry to React.** ✅ N/A — the plugin doesn't import `acture` at all, let alone React. +7. **No promoting `@experimental` to `@stable` without a migration story.** ✅ The one rule ships `@stable` from 1.0.0; there was no prior experimental version. +8. **No bundling a UI kit.** ✅ N/A. +9. **No marketing on category.** ✅ The README leads with the concrete win ("catch `wrapMutation` wrappers that have outlived their purpose"), not an architecture pitch. +10. **No assuming the LLM's chosen function is authorization.** ✅ N/A. + +**One ESLint-specific gotcha caught during the session:** ESLint's scope model counts a `const x = …` binding's *own initializer* as a (write) `Reference`. The first cut of `no-stale-wrap-mutation` checked `variable.references.length === 0` and therefore never fired on the `const x = wrapMutation(...)` shape. The fix: `variable.references.every((ref) => ref.init === true)` — ignore the initializer write, count only uses beyond the declaration. Worth remembering for any future scope-analysis rule. + +## Versioning + +v1.4 adds **one new package** (`eslint-plugin-acture-migration@1.0.0`) and **one docs file**. No existing package's source changed, so **no changeset was created** — a changeset would force an unwanted bump, and `changeset publish` auto-detects the new package and publishes it at its stated `1.0.0`. This is the cleanest possible "bump only the affected packages": the only affected package is new and already at its intended version. + +## Stat sheet + +| Metric | v1.3 end | v1.4 end | Δ | +| --- | --- | --- | --- | +| Packages | 14 | 15 | +1 (`eslint-plugin-acture-migration`) | +| Worked examples | 4 | 4 | 0 | +| Tests (packages) | 380 | 396 | +16 (ESLint RuleTester suite) | +| Tests (examples) | 41 | 41 | 0 | +| CommandRecord fields | 15 | 15 | 0 — closed surface still holds | +| Versions touched | codemods→1.1.0 | none (new pkg at 1.0.0) | targeted | +| Release-gate rituals run | 0 | 1 (fresh-agent test) | +1 | + +CI green across the workspace; `npm pack --dry-run` clean for `eslint-plugin-acture-migration@1.0.0` (8 files, 6.7 kB). + +## Pre-v1.5 reflection answers + +1. **Did the "one rule, one package" shape hold?** Yes. The package is ~140 lines of rule logic + a thin index that exposes the rule and a `recommended` flat config. No temptation toward a second rule appeared — the three-callers test for `no-stale-wrap-mutation` passed (research-4 named it; the `migration-graduate` skill now points at it; active migrations accumulate stale wrappers), but no second rule has three callers yet. If/when one does, it's a one-file addition to the same package. + +2. **Is the rule's conservatism the right call?** Yes, but it has a known blind spot: a wrapper whose result is exported and then unused *cross-file* is not caught (ESLint rules are single-file). The honest framing — documented in the README's "Detection contract" — is that false negatives are expected and false positives should be rare. A type-aware / project-graph version could close the gap, but that's a much bigger lift and nobody has asked. Not promoting it without three callers. + +3. **What did the fresh-agent test actually prove?** That the codemod *engine* is release-ready and the codemod *README* is not. The gap is documentation drift (the README was written assuming a published package), not a design flaw — which is the good kind of finding for a release gate. v1.5's top candidate is the codemods README + CLI polish pass (see `docs/next_session.md`). + +4. **Should v1.4 have fixed the codemods README in-session?** No — `docs/next_session.md` explicitly scoped #2 as no-code-change, and honoring that kept the session to exactly two deliverables. But the temptation was real: several of the findings are trivial edits. Carrying them as a *named, scoped* v1.5 candidate (rather than scope-creeping v1.4) is the rule-of-three-respecting move. + +5. **Hard-don'ts audit.** Clean. + +## Release readiness + +- ✅ All 15 packages typecheck and build. +- ✅ 396 package tests + 41 example tests green. +- ✅ `npm pack --dry-run` clean for `eslint-plugin-acture-migration@1.0.0`. +- ✅ Hard-don'ts audit clean. +- ✅ Fresh-agent release-gate test run and documented (`docs/fresh-agent-test-results.md`). + +**v1.4 is DONE.** Next session: see `docs/next_session.md` for the v1.5 planning prompt (codemods README/CLI polish, `.d.ts` tier mirror, AI-recipe doc). diff --git a/packages/eslint-plugin-acture-migration/README.md b/packages/eslint-plugin-acture-migration/README.md new file mode 100644 index 0000000..d3396ed --- /dev/null +++ b/packages/eslint-plugin-acture-migration/README.md @@ -0,0 +1,101 @@ +# eslint-plugin-acture-migration + +One ESLint rule for [acture](https://github.com/thorwhalen/acture) strangler-fig migrations: **catch `wrapMutation` wrappers that have outlived their purpose.** + +During a migration you wrap an existing handler with `wrapMutation(...)` so the call site stays unchanged while the command becomes visible to the palette, MCP, and AI surfaces. Once the legacy call sites are gone, the wrapper is dead weight — the command should be authored directly with `defineCommand`. This plugin flags those stale wrappers so they don't accumulate. + +## Install + +```bash +npm install --save-dev eslint-plugin-acture-migration +``` + +Requires ESLint 9+ (flat config). + +## Usage + +```js +// eslint.config.js +import acture from 'eslint-plugin-acture-migration'; + +export default [ + { + plugins: { acture }, + rules: { + 'acture/no-stale-wrap-mutation': 'warn', + }, + }, +]; +``` + +Or use the bundled config: + +```js +import acture from 'eslint-plugin-acture-migration'; + +export default [acture.configs.recommended]; +``` + +The `plugins` key is yours to name — `acture` above is just a convention. Whatever you pick becomes the rule's prefix. + +## Rule: `acture/no-stale-wrap-mutation` + +Flags `wrapMutation(...)` calls whose **return value is never used**. The only thing such a call does is the registry-registration side effect — which means the wrapper has graduated: nothing calls the wrapped function anymore. + +`wrapMutation`'s whole reason to exist over `defineCommand` is that the call site stays unchanged — the wrapped function is still *called*. When the result is discarded, that property isn't being used, so the wrapper is dead weight. + +### Flagged + +```ts +import { wrapMutation } from '@acture/migration'; + +// Bare statement — result discarded entirely. +wrapMutation(handleSave, { registry }); + +// Assigned to a local binding that is never referenced. +const onSave = wrapMutation(handleSave, { registry }); +``` + +Both should become a direct `defineCommand` plus `registry.register(...)`. See the `migration-graduate` skill. + +### Not flagged + +```ts +import { wrapMutation } from '@acture/migration'; + +// Result is used — wrapper is still load-bearing. +const onSave = wrapMutation(handleSave, { registry }); +button.addEventListener('click', onSave); + +// Result is exported — may be called from another file. +export const onSave = wrapMutation(handleSave, { registry }); + +// Result is returned or passed onward. +register(wrapMutation(handleSave, { registry })); +``` + +### Detection contract + +The rule is deliberately **single-file and conservative** — research-4's codemod principle: a high-confidence partial signal beats a noisy total one. + +- Tracks `wrapMutation` imported (named or aliased) from `@acture/migration`. Namespace imports (`import * as m`) are not tracked. +- Reports two shapes: a bare `ExpressionStatement`, and assignment to a non-exported local binding with zero references. +- An exported binding, a referenced binding, a returned result, or a result passed as an argument are all left alone — any of them may still be load-bearing. + +False negatives are expected (a binding exported and unused cross-file won't be caught). False positives should be rare; if you hit one, the wrapper result genuinely looks unused in that file. + +### Options + +```js +'acture/no-stale-wrap-mutation': ['warn', { module: '@myorg/legacy-migration' }] +``` + +| Option | Default | Description | +| -------- | -------------------- | ----------- | +| `module` | `@acture/migration` | Module that `wrapMutation` is imported from. Override if your codebase re-exports it under its own package name. | + +## See also + +- `@acture/migration` — the runtime adoption surface (`wrapMutation`, `actureMiddleware`, …). +- `@acture/codemods` — structural transforms for adopting acture. +- `.claude/skills/migration-graduate/SKILL.md` — the agent workflow for retiring a `wrapMutation` once this rule fires. diff --git a/packages/eslint-plugin-acture-migration/package.json b/packages/eslint-plugin-acture-migration/package.json new file mode 100644 index 0000000..569b6de --- /dev/null +++ b/packages/eslint-plugin-acture-migration/package.json @@ -0,0 +1,60 @@ +{ + "name": "eslint-plugin-acture-migration", + "version": "1.0.0", + "description": "ESLint rule for acture strangler-fig migrations. Flags stale wrapMutation(...) wrappers whose result is never used — the migration has graduated and the wrapper should become a defineCommand.", + "license": "Apache-2.0", + "author": "Thor Whalen", + "repository": { + "type": "git", + "url": "git+https://github.com/thorwhalen/acture.git", + "directory": "packages/eslint-plugin-acture-migration" + }, + "homepage": "https://github.com/thorwhalen/acture#readme", + "bugs": "https://github.com/thorwhalen/acture/issues", + "type": "module", + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "scripts": { + "build": "tsup", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "clean": "rm -rf dist *.tsbuildinfo" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + }, + "devDependencies": { + "@types/estree": "^1.0.0", + "@types/node": "^22.10.0", + "eslint": "^9.17.0", + "tsup": "^8.3.0", + "typescript": "^5.7.0", + "vitest": "^2.1.0" + }, + "keywords": [ + "eslint", + "eslintplugin", + "acture", + "migration", + "wrap-mutation", + "strangler-fig" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/eslint-plugin-acture-migration/src/index.ts b/packages/eslint-plugin-acture-migration/src/index.ts new file mode 100644 index 0000000..c180cb9 --- /dev/null +++ b/packages/eslint-plugin-acture-migration/src/index.ts @@ -0,0 +1,48 @@ +/** + * `eslint-plugin-acture-migration` + * + * One rule, one job: catch strangler-fig migrations that have graduated + * but still carry their `wrapMutation` scaffolding. See the rule's own + * file and the package README for the detection contract. + * + * Flat-config usage (ESLint 9+): + * + * import acture from 'eslint-plugin-acture-migration'; + * + * export default [ + * { + * plugins: { acture }, + * rules: { 'acture/no-stale-wrap-mutation': 'warn' }, + * }, + * ]; + * + * Or pull in the bundled config: + * + * import acture from 'eslint-plugin-acture-migration'; + * export default [acture.configs.recommended]; + */ + +import type { ESLint, Linter } from 'eslint'; +import { noStaleWrapMutation } from './rules/no-stale-wrap-mutation.js'; + +const rules = { + 'no-stale-wrap-mutation': noStaleWrapMutation, +} satisfies ESLint.Plugin['rules']; + +const plugin: ESLint.Plugin = { + meta: { + name: 'eslint-plugin-acture-migration', + version: '1.0.0', + }, + rules, +}; + +const recommended: Linter.Config = { + plugins: { acture: plugin }, + rules: { 'acture/no-stale-wrap-mutation': 'warn' }, +}; + +plugin.configs = { recommended }; + +export default plugin; +export { rules, noStaleWrapMutation }; diff --git a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts new file mode 100644 index 0000000..69128e1 --- /dev/null +++ b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.test.ts @@ -0,0 +1,112 @@ +import { describe, it } from 'vitest'; +import { RuleTester } from 'eslint'; +import { noStaleWrapMutation } from './no-stale-wrap-mutation.js'; + +// Wire ESLint's RuleTester into vitest so each case shows up as its own +// test. RuleTester looks for these statics; vitest doesn't expose them as +// globals unless `globals: true`, so we hand them over explicitly. +RuleTester.describe = describe; +RuleTester.it = it; +RuleTester.itOnly = it.only; + +const ruleTester = new RuleTester({ + languageOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('no-stale-wrap-mutation', noStaleWrapMutation, { + valid: [ + // Result is assigned and then referenced — wrapper is load-bearing. + `import { wrapMutation } from '@acture/migration'; + const onSave = wrapMutation(handleSave, { registry }); + button.addEventListener('click', onSave);`, + + // Result is exported — may be called from another file. + `import { wrapMutation } from '@acture/migration'; + export const onSave = wrapMutation(handleSave, { registry });`, + + // Result is exported via a specifier — the specifier counts as a use. + `import { wrapMutation } from '@acture/migration'; + const onSave = wrapMutation(handleSave, { registry }); + export { onSave };`, + + // Result is returned — still in use. + `import { wrapMutation } from '@acture/migration'; + function make() { return wrapMutation(handleSave, { registry }); }`, + + // Result is passed as an argument — still in use. + `import { wrapMutation } from '@acture/migration'; + register(wrapMutation(handleSave, { registry }));`, + + // No import of wrapMutation — not our function, stay quiet. + `wrapMutation(handleSave, { registry });`, + + // wrapMutation imported from a different module — not acture's. + `import { wrapMutation } from './local-utils'; + wrapMutation(handleSave, { registry });`, + + // Namespace import — member-expression callee is not tracked. + `import * as m from '@acture/migration'; + m.wrapMutation(handleSave, { registry });`, + + // Custom module option configured, but the import is from elsewhere. + { + code: `import { wrapMutation } from '@acture/migration'; + wrapMutation(handleSave, { registry });`, + options: [{ module: '@myorg/legacy-migration' }], + }, + ], + + invalid: [ + // Bare expression statement — result discarded entirely. + { + code: `import { wrapMutation } from '@acture/migration'; + wrapMutation(handleSave, { registry });`, + errors: [{ messageId: 'staleWrapper' }], + }, + + // Assigned to a local, non-exported binding that is never used. + { + code: `import { wrapMutation } from '@acture/migration'; + const onSave = wrapMutation(handleSave, { registry });`, + errors: [{ messageId: 'staleWrapper' }], + }, + + // Aliased import — track the local name. + { + code: `import { wrapMutation as wm } from '@acture/migration'; + wm(handleSave, { registry });`, + errors: [{ messageId: 'staleWrapper' }], + }, + + // Custom module option — wrapMutation re-exported under another name. + { + code: `import { wrapMutation } from '@myorg/legacy-migration'; + wrapMutation(handleSave, { registry });`, + options: [{ module: '@myorg/legacy-migration' }], + errors: [{ messageId: 'staleWrapper' }], + }, + + // Multiple stale wrappers in one file — one report each. + { + code: `import { wrapMutation } from '@acture/migration'; + wrapMutation(addTodo, { registry }); + const unused = wrapMutation(removeTodo, { registry });`, + errors: [{ messageId: 'staleWrapper' }, { messageId: 'staleWrapper' }], + }, + + // Inline handler, result discarded — still stale. + { + code: `import { wrapMutation } from '@acture/migration'; + wrapMutation(() => store.save(), { registry });`, + errors: [{ messageId: 'staleWrapper' }], + }, + + // Import declaration appears after the call (legal at module scope) — + // the deferred Program:exit check still resolves it. + { + code: `wrapMutation(handleSave, { registry }); + import { wrapMutation } from '@acture/migration';`, + errors: [{ messageId: 'staleWrapper' }], + }, + ], +}); diff --git a/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts new file mode 100644 index 0000000..3f38d3d --- /dev/null +++ b/packages/eslint-plugin-acture-migration/src/rules/no-stale-wrap-mutation.ts @@ -0,0 +1,162 @@ +/** + * `acture/no-stale-wrap-mutation` + * + * Flags `wrapMutation(...)` calls whose return value is never used. The + * only thing such a call does is the registry-registration side effect — + * which means the strangler-fig wrapper has done its job: there is no + * longer a call site invoking the wrapped function. The command should be + * authored directly with `defineCommand` (see the `migration-graduate` + * skill). + * + * `wrapMutation`'s entire reason to exist over `defineCommand` is that the + * call site stays unchanged: the wrapped function is still *called* as a + * function somewhere. When the wrapper's result is discarded, that + * property is no longer being used — the wrapper is dead weight. + * + * Detection is intentionally single-file and conservative (research-4's + * codemod principle: a high-confidence partial signal beats a noisy total + * one). Two stale shapes are reported: + * + * 1. Bare expression statement — `wrapMutation(handler, { registry });` + * The result is discarded entirely. + * 2. Assigned to a local, non-exported binding that is never + * referenced — `const x = wrapMutation(handler, { registry });` with + * no later use of `x`. + * + * Anything else is left alone: an exported binding may be called from + * another file; a referenced binding is still load-bearing; a result that + * is returned or passed as an argument is still in use. Namespace imports + * (`import * as m from '@acture/migration'`) are not tracked. + */ + +import type { Rule, SourceCode } from 'eslint'; +import type { + CallExpression, + Identifier, + ImportDeclaration, + VariableDeclarator, +} from 'estree'; + +const DEFAULT_MODULE = '@acture/migration'; +const WRAP_MUTATION = 'wrapMutation'; + +interface Options { + /** Module that `wrapMutation` is imported from. Override for codebases + * that re-export it under their own package name. */ + readonly module?: string; +} + +export const noStaleWrapMutation: Rule.RuleModule = { + meta: { + type: 'suggestion', + docs: { + description: + 'Flag wrapMutation(...) calls whose result is never used — the migration has graduated and the wrapper should become a defineCommand.', + recommended: true, + url: 'https://github.com/thorwhalen/acture/blob/main/packages/eslint-plugin-acture-migration/README.md', + }, + schema: [ + { + type: 'object', + properties: { + module: { type: 'string' }, + }, + additionalProperties: false, + }, + ], + messages: { + staleWrapper: + 'This wrapMutation(...) result is never used — the wrapper exists only for its registry side effect. The migration has graduated; author this command directly with defineCommand (see the migration-graduate skill).', + }, + }, + + create(context): Rule.RuleListener { + const options = (context.options[0] ?? {}) as Options; + const moduleName = options.module ?? DEFAULT_MODULE; + + /** Local names that `wrapMutation` was imported as. */ + const wrapNames = new Set(); + /** Every identifier-callee call, collected for a deferred decision so + * the import scan is guaranteed complete first. */ + const candidateCalls: CallExpression[] = []; + + return { + ImportDeclaration(node: ImportDeclaration) { + if (node.source.value !== moduleName) return; + for (const spec of node.specifiers) { + if ( + spec.type === 'ImportSpecifier' && + spec.imported.type === 'Identifier' && + spec.imported.name === WRAP_MUTATION + ) { + wrapNames.add(spec.local.name); + } + } + }, + + CallExpression(node: CallExpression) { + if (node.callee.type === 'Identifier') { + candidateCalls.push(node); + } + }, + + 'Program:exit'() { + if (wrapNames.size === 0) return; + const sourceCode = context.sourceCode; + + for (const call of candidateCalls) { + const callee = call.callee as Identifier; + if (!wrapNames.has(callee.name)) continue; + + const parent = (call as Rule.Node).parent; + if (!parent) continue; + + // Shape 1: bare expression statement — result discarded. + if (parent.type === 'ExpressionStatement') { + context.report({ node: call, messageId: 'staleWrapper' }); + continue; + } + + // Shape 2: assigned to an unused, non-exported local binding. + if ( + parent.type === 'VariableDeclarator' && + parent.init === call && + isUnusedLocalBinding(sourceCode, parent) + ) { + context.report({ node: call, messageId: 'staleWrapper' }); + } + } + }, + }; + }, +}; + +/** + * True when `declarator` binds a plain identifier that is (a) not part of + * an `export` declaration and (b) never referenced. Either condition + * failing means the wrapper may still be load-bearing, so we stay quiet. + */ +function isUnusedLocalBinding( + sourceCode: SourceCode, + declarator: VariableDeclarator, +): boolean { + if (declarator.id.type !== 'Identifier') return false; + + const declaration = (declarator as Rule.Node).parent; + if (!declaration || declaration.type !== 'VariableDeclaration') return false; + if (declaration.parent?.type === 'ExportNamedDeclaration') { + return false; + } + + const name = declarator.id.name; + const variable = sourceCode + .getDeclaredVariables(declaration) + .find((v) => v.name === name); + if (!variable) return false; + + // Any reference — a read, or an `export { x }` specifier — means the + // wrapped function is still reachable. The binding's own initializer + // counts as a (write) reference in ESLint's scope model, so ignore it: + // only a use *beyond* the declaration makes the wrapper load-bearing. + return variable.references.every((ref) => ref.init === true); +} diff --git a/packages/eslint-plugin-acture-migration/tsconfig.json b/packages/eslint-plugin-acture-migration/tsconfig.json new file mode 100644 index 0000000..5d2ba83 --- /dev/null +++ b/packages/eslint-plugin-acture-migration/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "lib": ["ES2022"], + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/eslint-plugin-acture-migration/tsup.config.ts b/packages/eslint-plugin-acture-migration/tsup.config.ts new file mode 100644 index 0000000..369cb8b --- /dev/null +++ b/packages/eslint-plugin-acture-migration/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: true, + sourcemap: true, + clean: true, + treeshake: true, + target: 'es2022', + outDir: 'dist', +}); diff --git a/packages/eslint-plugin-acture-migration/vitest.config.ts b/packages/eslint-plugin-acture-migration/vitest.config.ts new file mode 100644 index 0000000..7eeb3f8 --- /dev/null +++ b/packages/eslint-plugin-acture-migration/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + include: ['src/**/*.test.ts'], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 835ac6e..f30bd2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -435,6 +435,27 @@ importers: specifier: ^4.0.0 version: 4.4.3 + packages/eslint-plugin-acture-migration: + devDependencies: + '@types/estree': + specifier: ^1.0.0 + version: 1.0.9 + '@types/node': + specifier: ^22.10.0 + version: 22.19.19 + eslint: + specifier: ^9.17.0 + version: 9.39.4 + tsup: + specifier: ^8.3.0 + version: 8.5.1(postcss@8.5.14)(tsx@4.21.0)(typescript@5.9.3) + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^2.1.0 + version: 2.1.9(@types/node@22.19.19)(jsdom@25.0.1) + packages/forms-autoform: devDependencies: '@testing-library/react': @@ -1326,12 +1347,70 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@hono/node-server@1.19.14': resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -1805,6 +1884,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1858,6 +1940,11 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -1893,6 +1980,9 @@ packages: ajv: optional: true + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} @@ -1904,6 +1994,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} @@ -1951,6 +2045,9 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} @@ -1985,6 +2082,10 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} @@ -1992,6 +2093,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -2016,6 +2121,13 @@ packages: code-block-writer@13.0.3: resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2030,6 +2142,9 @@ packages: compute-lcm@1.1.2: resolution: {integrity: sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -2091,6 +2206,9 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -2183,14 +2301,60 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -2231,6 +2395,12 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} @@ -2246,6 +2416,10 @@ packages: picomatch: optional: true + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2258,9 +2432,20 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -2312,6 +2497,14 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2323,6 +2516,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -2374,6 +2571,14 @@ packages: immer@11.1.8: resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2446,6 +2651,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-schema-compare@0.2.2: resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==} @@ -2453,6 +2661,9 @@ packages: resolution: {integrity: sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==} engines: {node: '>=12.0.0'} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -2462,6 +2673,9 @@ packages: json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -2479,6 +2693,13 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -2494,9 +2715,16 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -2568,6 +2796,9 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -2590,6 +2821,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -2615,6 +2849,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -2626,10 +2864,18 @@ packages: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -2641,6 +2887,10 @@ packages: package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -2724,6 +2974,10 @@ packages: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -2840,6 +3094,10 @@ packages: reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -2969,11 +3227,19 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + swr@2.4.1: resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==} peerDependencies: @@ -3082,6 +3348,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -3111,6 +3381,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -3287,6 +3560,10 @@ packages: engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -3312,6 +3589,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -3884,10 +4165,72 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@hono/node-server@1.19.14(hono@4.12.18)': dependencies: hono: 4.12.18 + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@inquirer/external-editor@1.0.3(@types/node@22.19.19)': dependencies: chardet: 2.1.1 @@ -4299,6 +4642,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/json-schema@7.0.15': {} + '@types/node@12.20.55': {} '@types/node@22.19.19': @@ -4370,6 +4715,10 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn@8.16.0: {} agent-base@7.1.4: {} @@ -4394,6 +4743,13 @@ snapshots: optionalDependencies: ajv: 8.20.0 + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -4405,6 +4761,10 @@ snapshots: ansi-regex@5.0.1: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansi-styles@5.2.0: {} any-promise@1.3.0: {} @@ -4451,6 +4811,11 @@ snapshots: transitivePeerDependencies: - supports-color + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -4486,6 +4851,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + caniuse-lite@1.0.30001792: {} chai@5.3.3: @@ -4496,6 +4863,11 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} chardet@2.1.1: {} @@ -4520,6 +4892,12 @@ snapshots: code-block-writer@13.0.3: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -4539,6 +4917,8 @@ snapshots: validate.io-function: 1.0.2 validate.io-integer-array: 1.0.0 + concat-map@0.0.1: {} + confbox@0.1.8: {} consola@3.4.2: {} @@ -4584,6 +4964,8 @@ snapshots: deep-eql@5.0.2: {} + deep-is@0.1.4: {} + delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -4726,12 +5108,80 @@ snapshots: escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.4: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 + esutils@2.0.3: {} + etag@1.8.1: {} eventsource-parser@3.0.8: {} @@ -4796,6 +5246,10 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-uri@3.1.2: {} fastq@1.20.1: @@ -4806,6 +5260,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4826,12 +5284,24 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 mlly: 1.8.2 rollup: 4.60.3 + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -4891,6 +5361,12 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -4904,6 +5380,8 @@ snapshots: graceful-fs@4.2.11: {} + has-flag@4.0.0: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -4956,6 +5434,13 @@ snapshots: immer@11.1.8: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + inherits@2.0.4: {} ip-address@10.2.0: {} @@ -5027,6 +5512,8 @@ snapshots: jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-schema-compare@0.2.2: dependencies: lodash: 4.18.1 @@ -5037,12 +5524,16 @@ snapshots: json-schema-compare: 0.2.2 lodash: 4.18.1 + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} jsondiffpatch@0.6.0: @@ -5057,6 +5548,15 @@ snapshots: jsonpointer@5.0.1: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -5067,8 +5567,14 @@ snapshots: dependencies: p-locate: 4.1.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash-es@4.18.1: {} + lodash.merge@4.6.2: {} + lodash.startcase@4.4.0: {} lodash@4.18.1: {} @@ -5120,6 +5626,10 @@ snapshots: dependencies: mime-db: 1.54.0 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + minimatch@9.0.9: dependencies: brace-expansion: 2.1.0 @@ -5143,6 +5653,8 @@ snapshots: nanoid@3.3.12: {} + natural-compare@1.4.0: {} + negotiator@1.0.0: {} node-releases@2.0.44: {} @@ -5161,6 +5673,15 @@ snapshots: dependencies: wrappy: 1.0.2 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + outdent@0.5.0: {} p-filter@2.1.0: @@ -5171,10 +5692,18 @@ snapshots: dependencies: p-try: 2.2.0 + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-map@2.1.0: {} p-try@2.2.0: {} @@ -5183,6 +5712,10 @@ snapshots: dependencies: quansync: 0.2.11 + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -5236,6 +5769,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + prettier@2.8.8: {} pretty-format@27.5.1: @@ -5337,6 +5872,8 @@ snapshots: reselect@5.1.1: {} + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -5496,6 +6033,8 @@ snapshots: strip-bom@3.0.0: {} + strip-json-comments@3.1.1: {} + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -5506,6 +6045,10 @@ snapshots: tinyglobby: 0.2.16 ts-interface-checker: 0.1.13 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + swr@2.4.1(react@19.2.6): dependencies: dequal: 2.0.3 @@ -5609,6 +6152,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -5631,6 +6178,10 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.6): dependencies: react: 19.2.6 @@ -5769,6 +6320,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + wrappy@1.0.2: {} ws@8.20.0: {} @@ -5779,6 +6332,8 @@ snapshots: yallist@3.1.1: {} + yocto-queue@0.1.0: {} + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: zod: 4.4.3