@voltro/cli 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/apiBuild-D0f6hYbH.js +2 -0
  3. package/dist/{apiBuild-DiE9zcj6.js → apiBuild-DGsShRuF.js} +1 -1
  4. package/dist/{baselineCommand-DIttzO8A.js → baselineCommand-C6NMt-oa.js} +1 -4
  5. package/dist/baselineCommand-Cfw2Afwm.js +2 -0
  6. package/dist/bin.js +1 -1
  7. package/dist/{build-DBe6c7qC.js → build-CiFA-E19.js} +7 -7
  8. package/dist/{checkCommand-BTA6P3Yr.js → checkCommand-CIPYyNyc.js} +1 -1
  9. package/dist/{checkCommand-BhG0gmUD.js → checkCommand-CLPTzYqM.js} +3 -3
  10. package/dist/{cloudCmd-Cvv5HGaZ.js → cloudCmd-NQSwe_Qk.js} +1 -1
  11. package/dist/{codegen-CYM3Zqrf.js → codegen-GYYcdpCg.js} +2 -2
  12. package/dist/codegen-uYrrDzQv.js +2 -0
  13. package/dist/{codegenCommand-DH-8ZXzR.js → codegenCommand-BxS3sCeJ.js} +2 -2
  14. package/dist/{codemodRunner-DjQGrdmg.js → codemodRunner-cyqu5oxr.js} +101 -42
  15. package/dist/{commands-Bp7utV-7.js → commands-BwG9A9Vv.js} +73 -72
  16. package/dist/{dashboardCommand-D5Jwjmr1.js → dashboardCommand-C7U146xQ.js} +1 -1
  17. package/dist/{dataCommand-DnCm0B2C.js → dataCommand-BOwd8_aJ.js} +2 -2
  18. package/dist/{dbCommand-O8HA63s2.js → dbCommand-C4UW8cVN.js} +353 -351
  19. package/dist/dbCommand-CC-qNzpz.js +2 -0
  20. package/dist/{dev-BSFTgCQ5.js → dev-BV6XNYKf.js} +1 -1
  21. package/dist/{dev-Dc4cpmRl.js → dev-bwDgSObH.js} +1 -1
  22. package/dist/{doctorCommand-CNFtt3nR.js → doctorCommand-BXyrtN2l.js} +9 -9
  23. package/dist/doctorCommand-C4L61G_L.js +2 -0
  24. package/dist/{dormancyCommand-6F6NsJKf.js → dormancyCommand-BDdRRLsa.js} +1 -1
  25. package/dist/{embeddingsCommand-DRRdneJD.js → embeddingsCommand-CjWGcAE6.js} +1 -1
  26. package/dist/{envCommand-TZzcyqZ-.js → envCommand-B_TeG297.js} +1 -1
  27. package/dist/{evolveCommand-iU35TPjT.js → evolveCommand-D6N-WfA4.js} +2 -2
  28. package/dist/index.js +2 -2
  29. package/dist/{infoCommand-unqisJGp.js → infoCommand-BHKTprrx.js} +1 -1
  30. package/dist/inspectGateHint-BF6608UT.js +4 -0
  31. package/dist/{metaCommands-7MJfZ5cf.js → metaCommands-CIoQNlaQ.js} +1 -1
  32. package/dist/{migrate-BV7I-ZHZ.js → migrate-RwgUWXfc.js} +1 -1
  33. package/dist/{privacyCommand-C-Df56U_.js → privacyCommand-XejDMvmu.js} +4 -3
  34. package/dist/{probeCommand-CZfaaUOZ.js → probeCommand-BPEpwT32.js} +40 -39
  35. package/dist/{projectScaffold-DmzEKHib.js → projectScaffold-CxgtJvlb.js} +1 -1
  36. package/dist/{projectScaffold-LMMtaavR.js → projectScaffold-DDgWbNLe.js} +2 -2
  37. package/dist/{runtimeTrace-D5LJ_N55.js → runtimeTrace-WYV7qYvz.js} +1 -1
  38. package/dist/{sdkgen-82xVuvbt.js → sdkgen-TM5sSdf7.js} +1 -1
  39. package/dist/{serveCommand-VVGcIMIw.js → serveCommand-CyZsXL1l.js} +2 -2
  40. package/dist/serveCommand-qumm7jdF.js +2 -0
  41. package/dist/serveEntry.js +1 -1
  42. package/dist/subcommandNames-CKG5Dz3a.js +27 -0
  43. package/dist/{templates-De8IR5-c.js → templates-BR-fb4SP.js} +1 -4
  44. package/dist/{test-CI6iDsYc.js → test-DIQ0jlkQ.js} +1 -1
  45. package/dist/{updateCommand-CwnUK_mf.js → updateCommand-9GHHsmDf.js} +1 -1
  46. package/dist/updateCommand-BN0tkl9-.js +2 -0
  47. package/dist/{webhooksCommand-di1CJVzu.js → webhooksCommand-CHWSPk5z.js} +1 -1
  48. package/package.json +23 -17
  49. package/templates/AGENTS.md +1 -1
  50. package/templates/agent-docs/_index.md +1 -1
  51. package/templates/agent-docs/cli.md +16 -0
  52. package/templates/agent-docs/data.md +30 -0
  53. package/templates/agent-docs/plugins/audit.md +46 -0
  54. package/templates/agent-docs/whats-new.md +56 -34
  55. package/templates/apps/api-ai/package.json +7 -7
  56. package/templates/apps/api-auth/package.json +8 -8
  57. package/templates/apps/api-backend/package.json +7 -7
  58. package/templates/apps/api-backend-deactivation/package.json +7 -7
  59. package/templates/apps/api-backend-mail/package.json +8 -8
  60. package/templates/apps/api-backend-mariadb/package.json +9 -9
  61. package/templates/apps/api-backend-sqlite/package.json +8 -8
  62. package/templates/apps/api-backend-storage/package.json +8 -8
  63. package/templates/apps/api-cms/package.json +10 -10
  64. package/templates/apps/api-collab/package.json +8 -8
  65. package/templates/apps/api-data-advanced/package.json +8 -8
  66. package/templates/apps/api-durable/package.json +8 -8
  67. package/templates/apps/api-feature-flags/package.json +9 -9
  68. package/templates/apps/api-governance/package.json +8 -8
  69. package/templates/apps/api-kv/package.json +8 -8
  70. package/templates/apps/api-moderation/package.json +8 -8
  71. package/templates/apps/api-observability/package.json +8 -8
  72. package/templates/apps/api-ratelimit/package.json +8 -8
  73. package/templates/apps/api-rbac/package.json +8 -8
  74. package/templates/apps/api-rest/package.json +7 -7
  75. package/templates/apps/api-saas/package.json +11 -11
  76. package/templates/apps/api-saas-starter/package.json +10 -10
  77. package/templates/apps/api-search/package.json +8 -8
  78. package/templates/apps/api-status/package.json +8 -8
  79. package/templates/apps/api-versioning/package.json +8 -8
  80. package/templates/apps/api-webhooks/package.json +9 -9
  81. package/templates/apps/changelog/package.json +6 -6
  82. package/templates/apps/edge-functions/package.json +2 -2
  83. package/templates/apps/frontend-admin/package.json +8 -8
  84. package/templates/apps/frontend-app/package.json +9 -9
  85. package/templates/apps/frontend-auth/package.json +8 -8
  86. package/templates/apps/frontend-blank/package.json +7 -7
  87. package/templates/apps/frontend-cms/package.json +9 -9
  88. package/templates/apps/frontend-collab/package.json +10 -10
  89. package/templates/apps/frontend-contact/package.json +7 -7
  90. package/templates/apps/frontend-dashboard/package.json +7 -7
  91. package/templates/apps/frontend-docs/package.json +7 -7
  92. package/templates/apps/frontend-i18n/package.json +6 -6
  93. package/templates/apps/frontend-landing/package.json +7 -7
  94. package/templates/apps/frontend-portal/package.json +8 -8
  95. package/templates/apps/frontend-saas/package.json +8 -8
  96. package/templates/apps/frontend-spa/package.json +7 -7
  97. package/templates/apps/frontend-ssr/package.json +7 -7
  98. package/templates/apps/frontend-ssr-api/package.json +8 -8
  99. package/templates/apps/frontend-static-blog/package.json +6 -6
  100. package/templates/apps/frontend-status/package.json +8 -8
  101. package/templates/apps/mobile-app/package.json +3 -3
  102. package/dist/apiBuild-ZEZnCXn2.js +0 -2
  103. package/dist/baselineCommand-C2ClWZN3.js +0 -2
  104. package/dist/codegen-ChBi_hVa.js +0 -2
  105. package/dist/dbCommand-DS4b97Is.js +0 -2
  106. package/dist/doctorCommand-BYffQc0s.js +0 -2
  107. package/dist/serveCommand-CEBVP36A.js +0 -2
  108. package/dist/updateCommand-CSBXVsYq.js +0 -2
package/CHANGELOG.md CHANGED
@@ -39,6 +39,96 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.37.0] — 2026-08-13
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/protocol, @voltro/runtime, @voltro/cli** — A field a procedure's input schema does not declare now REJECTS the call. It used to be discarded and the call ran with what was left.
47
+
48
+ The measurement, from a consumer's root layout:
49
+
50
+ ```ts
51
+ query?.('userSettings.list', { employeeId })
52
+ ```
53
+
54
+ That procedure declares `userId` / `userIdIn`. Effect's default `onExcessProperty: 'ignore'` decoded the payload to `{}` — not reasoned, measured:
55
+
56
+ ```ts
57
+ decodeUnknownSync(Struct({ userId: optional(String) }))({ employeeId: 'e' }) // → {}
58
+ ```
59
+
60
+ An empty input to a LIST query is not a narrower filter, it is the ABSENCE of one. Their admin, signed in as `2d0add2c…`, was served the settings row of `4410c2f8…` — another user's language and theme in the first paint, with nothing in any log to say so.
61
+
62
+ **Why refuse rather than warn.** The decoder cannot tell a projection field from a FILTER field, and that asymmetry is the whole risk: dropping an unknown `include` costs a caller some data, dropping an unknown `tenantId` hands them somebody else's. Nothing at decode time distinguishes the two, so the safe direction is the only one available — the same fail-closed reasoning as the row filter's refusal, one layer up. A warning would have to be read by someone, in a log, after the wrong rows were already served.
63
+
64
+ The typed loader query that shipped in 0.36.0 closes the same hole for callers we compile. This closes it for the ones we do not: a plain `fetch`, a curl, a still-cached bundle after a field rename, and every untyped caller.
65
+
66
+ Three things measured rather than assumed, because none follows from the annotation's name: it propagates into NESTED structs, through every member of a UNION, and leaves a non-struct payload (`Schema.Void`, a scalar) alone.
67
+
68
+ **`Schema.Struct({})` needed a filter, and only a real process showed it.** The fixture's `notes.list` declares an empty input; `POST /rpc` with `{ employeeId }` came back `200` with a snapshot, which for twenty minutes read as the whole change having failed. An empty `TypeLiteral` has no property signatures, so Effect has no expected key set for a key to be excess OF — self-consistent, and the wrong answer here, because `input: Schema.Struct({})` is the STRONGEST declaration a procedure can make and it was the one shape that accepted everything. It gets an explicit predicate now; `Schema.Record` keeps its open key set, because there the openness is declared.
69
+
70
+ **Verified against a running `voltro serve`, not only in units.** A declared input succeeds and inserts its row; an undeclared field is refused naming the key and the accepted set. The refusal arrives on the channel a payload decode failure ALREADY used — a missing required field produces the same `Die` with a `ParseError` message — so this adds no new error shape for a client to handle, it moves one case onto the channel the sibling case was always on.
71
+
72
+ `strictInput` lives in one module and every `Rpc.make` payload in `@voltro/protocol` goes through it — query, mutation, action, stream, event, plus the workflow start on both the server lifter and the browser-loaded rpc group. `strictInput.test.ts` asserts that SET by scanning the source, not the five lifters somebody remembered: a rule applied at the sites you can list is the shape that let `bootStoreCodec` be fixed twice and break a third time.
73
+
74
+ **`voltro update` carries you across this** — codemod `0.37.0/01_procedure-input-rejects-undeclared-fields`, a written note. A transform would have to guess which declared field a stray one meant, which is the same guess that produced the defect.
75
+
76
+ ### Added
77
+
78
+ - **@voltro/plugin-audit** — `redactInput` / `redactOutcome` gained `'shape'`, and `redactSubject` gained `'metadata-shape'`: the payload's STRUCTURE survives, no value from it.
79
+
80
+ ```json
81
+ { "__redacted": { "jiraToken": "string(113)", "attempts": "number" } }
82
+ ```
83
+
84
+ Requested by the consumer who had asked for the redaction one round earlier, and both requests were right. They spent a day on a bug their own audit trail could have ended in seconds — a value arrived as 113 characters where 44 were due, and the row that would have said so read `{"__redacted":"all"}`. `'all'` remains the default on every field; this is opt-in.
85
+
86
+ The rules, and the two that are decisions rather than details:
87
+
88
+ - A string reports its LENGTH. Never a prefix, never a hash — `enc:v1:` is a prefix and so is the first byte of a private key, so there is no prefix length that is safe for every credential format. - A number, boolean or date reports its TYPE only. A number can BE the secret. - **A key can be the value.** An object keyed by user data puts a datum where a schema name belongs, so a key is reproduced only when it looks like a declared field — a short plain identifier. The first version truncated long keys and documented the weakness instead; this module's own test caught 62 characters of a secret surviving on the first run. A leak with a footnote is still a leak. - **A string's length is a real disclosure, and a small one.** Stated in the docs rather than buried: for a fixed-format credential it carries nothing, for a human-chosen password it is a weak hint. `'all'` stays the default for anyone that matters to.
89
+
90
+ The `'shape'` outcome describes the payload it REPLACES — the value on success, the error on failure — rather than the event. Describing the event would report `{ kind, value, durationMs }` and hide the field, which is the failure the option exists to end. An error's `_tag` still survives, as it does under `'all'`.
91
+
92
+ ### Fixed
93
+
94
+ - **@voltro/cli** — `voltro db --help` listed fifteen of eighteen subcommands. `adopt`, `scan-credentials` and `encrypt-column` shipped and never joined the hand-written string.
95
+
96
+ A consumer wrote both halves of that gap into a requirements document, as separate items, neither of them about help text:
97
+
98
+ - **`voltro db encrypt-column` "does not exist"** — filed as a feature request, quoting the fifteen names they saw as evidence. It shipped in 0.33.0, and enabling `.encrypted()` on a populated column by hand is exactly the migration they were about to write themselves. - **`scan-credentials` "no longer exists"** — filed as CLOSED, a credential scanner struck off their list as removed. It had not moved.
99
+
100
+ A quoted enumeration is read as exhaustive, and the more careful the reader the more thoroughly they act on the missing entry. Same lesson a boot refusal in `procedureAccessGate` had already taught us, in a place nobody thought of as a message.
101
+
102
+ The usage line is now GENERATED from the dispatch table's key type (`Record<DbSubcommand, Handler>` in `dbCommand.ts`, names in `subcommandNames.ts`), so a handler with no name or a name with no handler fails to compile. `voltro privacy` is keyed the same way. The prose summary beside it cannot be generated — it carries per-command annotations — so a test asserts it mentions every name, because it carried the identical three omissions and it is what `voltro --help` prints first.
103
+
104
+ `subcommandHelpParity.test.ts` also NAMES the six commands whose subcommand menus have no dispatch table behind them (`webhooks`, `evolve`, `new`, `data`, `storage`, `add`). They dispatch through a switch and are unchecked; a silently-unchecked command reads exactly like a checked one.
105
+ - **@voltro/cli** — A 401 or 403 from the inspect surface now names `VOLTRO_INSPECT_TOKEN` and says which side is missing.
106
+
107
+ `voltro db plan --against <url>` printed `remote returned 403` and stopped. A consumer read that as a DATABASE permission problem — the natural reading of a 403 from a command whose entire subject is a database — and went looking at grants. The cause is one unset environment variable, which the command reads four lines above the message.
108
+
109
+ `voltro probe access` had half of it: it named the variable on 401 and not on 403, while classifying both as `refused`. So the two commands somebody needs during an access migration were the two that would not say what was wrong, and one of them said something misleading instead.
110
+
111
+ `inspectGateHint` is shared by both call sites and distinguishes the two statuses, because they call for different actions: a 401 means no credential was sent (set the variable), a 403 means the one sent was not accepted (the two values differ). Both halves of the sentence name the server AND the calling shell — naming one side produces a second failed attempt.
112
+ - **@voltro/cli** — `VOLTRO_TEMPLATES_DIR` is authoritative when set. It used to be a HINT: if the path it named held no `apps/` (or no `baselines/`), both resolvers fell through to the sibling-checkout walk-up and quietly used a different tree — or none.
113
+
114
+ A pointer that silently isn't followed is worse than a wrong one. A CI job aimed at the wrong path scaffolded from whatever it happened to find, and a job whose checkout had failed reported an empty template catalogue with nothing connecting that emptiness to the variable it was given. `scripts/lib/docsSite.mjs` states the same rule for `VOLTRO_DOCS_DIR`, and arrived at it the same way: you said where it is; it is not there.
115
+
116
+ Behaviourally this only changes the misconfigured case — a correct `VOLTRO_TEMPLATES_DIR` resolved to the same place before and after. What changes is that a wrong one now shows up as "not found, here is the path I was told" at the first thing that reads it, instead of as a different tree three steps later.
117
+
118
+ The unbundled resolution order is otherwise untouched: sibling `voltro-templates` → `.voltro-templates` → the bundled `templates/` a published CLI ships.
119
+
120
+ ### Internal (no consumer-facing effect)
121
+
122
+ - **@voltro/plugin-ai-flows** — Two comments in the flow engine cited task records from a plans tracker that has since been deleted. Comment-only; no behavior, no API, nothing a consumer can observe.
123
+
124
+ Worth writing down because of HOW it surfaced. The tracker was retired in the META repo, and the gate that went red was in THIS one — `check-stale-task-comments.mjs` resolves a comment's `task #NN` against `../plans`, so deleting a plan document in one repo can only be half a change, and the other half is in a repo the deleting commit never touched.
125
+
126
+ Neither comment was WRONG, which is the part that makes the rule earn its keep. The first claims `@voltro/ai` has first-class media generation — true: `generateImage`, `generateSpeech`, `generateVideo` all ship in `packages/ai/src/media.ts`. It now names those three instead of a record number, which is checkable without the deleted document. The second only quoted the retired id inside its own account of a defect (a `"not yet wired (task #35)"` message that outlived the shipped HITL park and misled an audit into filing it as unbuilt); the quote lost the number and kept the whole lesson.
127
+
128
+ The check's own failure text is the reasoning: a plan is retired for exactly two reasons — the work shipped, or it was dropped without shipping — and a comment still citing it asserts the second while usually meaning the first.
129
+
130
+ ---
131
+
42
132
  ## [0.36.0] — 2026-08-13
43
133
 
44
134
  ### ⚠ BREAKING
@@ -0,0 +1,2 @@
1
+ import { c as e, l as t } from "./apiBuild-DGsShRuF.js";
2
+ export { e as runApiBuild, t as runServeBundleBuild };
@@ -1,4 +1,4 @@
1
- import { f as e } from "./dev-Dc4cpmRl.js";
1
+ import { f as e } from "./dev-bwDgSObH.js";
2
2
  import { ut as t } from "./webDev-Dybxew86.js";
3
3
  import { t as n } from "./dialectDriver-CgXnDfec.js";
4
4
  import { dirname as r, isAbsolute as i, join as a, relative as o, resolve as s } from "node:path";
@@ -6,10 +6,7 @@ import { createLogger as d } from "@voltro/logger";
6
6
  //#region src/baselineCommand.ts
7
7
  var f = r(u(import.meta.url)), p = d({ scope: "voltro:baseline" }), m = (() => {
8
8
  let e = process.env.VOLTRO_TEMPLATES_DIR;
9
- if (e) {
10
- let t = i(e, "baselines");
11
- if (s(t)) return t;
12
- }
9
+ if (e) return i(e, "baselines");
13
10
  let t = o(f, "..", "templates", "baselines"), n = f;
14
11
  for (let e = 0; e < 12; e++) {
15
12
  for (let e of ["voltro-templates", ".voltro-templates"]) {
@@ -0,0 +1,2 @@
1
+ import { o as e } from "./baselineCommand-C6NMt-oa.js";
2
+ export { e as runBaselineCommand };
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as e } from "./commands-Bp7utV-7.js";
2
+ import { r as e } from "./commands-BwG9A9Vv.js";
3
3
  import { a as t } from "./nodeEnvironment-cGFAj1J8.js";
4
4
  import { t as n } from "./loadEnv-D9nEOClM.js";
5
5
  import { n as r } from "./cliError-BmdYnghb.js";
@@ -1,8 +1,8 @@
1
- import { pn as e } from "./dev-Dc4cpmRl.js";
1
+ import { pn as e } from "./dev-bwDgSObH.js";
2
2
  import { D as t, G as n, K as r, O as i, S as a, W as o, Y as s, a as c, b as l, d as u, f as d, k as f, s as p, st as m, ut as h, v as g, x as _, y as v } from "./webDev-Dybxew86.js";
3
3
  import { t as y } from "./cliRuntime-Dh7UDinH.js";
4
4
  import { a as b } from "./renderModeScan-D7J1B7Kw.js";
5
- import { i as x, o as S, s as C } from "./apiBuild-DiE9zcj6.js";
5
+ import { i as x, o as S, s as C } from "./apiBuild-DGsShRuF.js";
6
6
  import { n as w } from "./renderProfile-1OWWAAtx.js";
7
7
  import { dirname as T, join as E, resolve as D } from "node:path";
8
8
  import { existsSync as O, promises as k } from "node:fs";
@@ -461,13 +461,13 @@ var N = A({ scope: "voltro:build" }), P = async (e) => {
461
461
  ].join("\n"), b = E(o, "ssrEntry.ts");
462
462
  return await k.writeFile(b, y, "utf8"), b;
463
463
  }, G = async (e) => {
464
- let { checkRpcGroupFreshness: t } = await import("./codegen-ChBi_hVa.js"), n = await t(e);
464
+ let { checkRpcGroupFreshness: t } = await import("./codegen-uYrrDzQv.js"), n = await t(e);
465
465
  if (n.kind === "fresh") return;
466
466
  n.kind === "stale" && F.info("rpcGroup.generated.ts is stale — regenerating", {
467
467
  file: n.file,
468
468
  reason: n.reason
469
469
  });
470
- let { regenerateRpcGroup: r } = await import("./codegenCommand-DH-8ZXzR.js"), i = await r(e);
470
+ let { regenerateRpcGroup: r } = await import("./codegenCommand-BxS3sCeJ.js"), i = await r(e);
471
471
  if (i.kind === "refused") {
472
472
  F.warn("could not regenerate the rpc group — building against what is on disk", { reason: i.reason });
473
473
  return;
@@ -479,10 +479,10 @@ var N = A({ scope: "voltro:build" }), P = async (e) => {
479
479
  }, K = async (e) => {
480
480
  let t = await u(e);
481
481
  if (!t) {
482
- let { loadApiConfig: t } = await import("./dev-BSFTgCQ5.js"), n = await t(e);
482
+ let { loadApiConfig: t } = await import("./dev-BV6XNYKf.js"), n = await t(e);
483
483
  if (n) {
484
484
  F.info("building api app", { app: n.name ?? "(unnamed)" }), await G(e);
485
- let { runApiBuild: t, runServeBundleBuild: r } = await import("./apiBuild-ZEZnCXn2.js");
485
+ let { runApiBuild: t, runServeBundleBuild: r } = await import("./apiBuild-D0f6hYbH.js");
486
486
  await t(e);
487
487
  try {
488
488
  await r(e);
@@ -701,7 +701,7 @@ var N = A({ scope: "voltro:build" }), P = async (e) => {
701
701
  return t.ran ? t.exitCode : (yield* j.logError("no web app found at this path", { hint: "looked for app.config.ts with `type: 'web'`" }), 1);
702
702
  }), J = async (t) => {
703
703
  if (t.includes("--target")) {
704
- let { runSdkBuildCommand: e } = await import("./sdkgen-82xVuvbt.js");
704
+ let { runSdkBuildCommand: e } = await import("./sdkgen-TM5sSdf7.js");
705
705
  return e(t);
706
706
  }
707
707
  let n = t.find((e) => !e.startsWith("-")) ?? process.cwd(), r = await y(q(n), { scope: "voltro:build" }), i = e(n);
@@ -1,2 +1,2 @@
1
- import { n as e, r as t, t as n } from "./checkCommand-BhG0gmUD.js";
1
+ import { n as e, r as t, t as n } from "./checkCommand-CLPTzYqM.js";
2
2
  export { n as offlineManifest, e as runCheckCommand, t as toInput };
@@ -1,10 +1,10 @@
1
1
  import { t as e } from "./cliArgs-D4p8n7EE.js";
2
- import { c as t, i as n } from "./codegen-CYM3Zqrf.js";
2
+ import { c as t, i as n } from "./codegen-GYYcdpCg.js";
3
3
  import { r } from "./appGraph-KGDPTuTy.js";
4
4
  import { n as i } from "./cliOutput-D1tSBoRM.js";
5
5
  import { n as a, o, r as s, t as c } from "./inspectFetch-EMuhTG_9.js";
6
6
  import { r as l } from "./fileTaxonomy-CJfgOllU.js";
7
- import { x as u } from "./doctorCommand-CNFtt3nR.js";
7
+ import { x as u } from "./doctorCommand-BXyrtN2l.js";
8
8
  import { resolve as d } from "node:path";
9
9
  import { readdirSync as f, statSync as p } from "node:fs";
10
10
  //#region src/observedDiff.ts
@@ -141,7 +141,7 @@ var m = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, h = /* @__PUR
141
141
  let t = e.severity === "error" ? "✗ error" : "⚠ warn", n = e.file ? ` (${e.file})` : "";
142
142
  return `${t} ${e.rule} ${e.node}${n}\n ${e.breaks.join("; ")}\n fix: ${e.fix}`;
143
143
  }).join("\n"), C = async (e) => {
144
- let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-BSFTgCQ5.js"), { buildRpcEntry: i } = await import("./manifestBuild-COkJoyAr.js"), { buildCapabilityManifest: a } = await import("./inspect-P4pxoMaV.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
144
+ let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-BV6XNYKf.js"), { buildRpcEntry: i } = await import("./manifestBuild-COkJoyAr.js"), { buildCapabilityManifest: a } = await import("./inspect-P4pxoMaV.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
145
145
  ...s.queries.map((e) => c(e.descriptor, "query", e.file)),
146
146
  ...s.mutations.map((e) => c(e.descriptor, "mutation", e.file)),
147
147
  ...s.actions.map((e) => c(e.descriptor, "action", e.file)),
@@ -1,7 +1,7 @@
1
1
  import { a as e, t } from "./cliArgs-D4p8n7EE.js";
2
2
  import { a as n, i as r, n as i, r as a, t as o } from "./cloudClient-DWL-Hw_T.js";
3
3
  import { n as s } from "./cliOutput-D1tSBoRM.js";
4
- import { d as c, f as l, o as u } from "./projectScaffold-LMMtaavR.js";
4
+ import { d as c, f as l, o as u } from "./projectScaffold-DDgWbNLe.js";
5
5
  import { basename as d, join as f, resolve as p } from "node:path";
6
6
  import { promises as m } from "node:fs";
7
7
  import { ansi as h } from "@voltro/logger";
@@ -323,7 +323,7 @@ ${o.join("\n\n")}\n`);
323
323
  let t = H(e.rpcTag), n = A(e);
324
324
  return e.kind === "query" ? `export const ${t} = queryToRpc(${n}${S})` : e.kind === "mutation" ? `export const ${t} = mutationToRpc(${n}${S})` : e.kind === "action" ? `export const ${t} = actionToRpc(${n}${S})` : e.kind === "stream" ? `export const ${t} = streamToRpc(${n}${S})` : e.kind === "event" ? `export const ${t} = eventToRpc(${n}${S})` : [
325
325
  `export const ${t} = Rpc.make('${e.rpcTag}', {`,
326
- ` payload: ${n}.payloadSchema,`,
326
+ ` payload: strictInput(${n}.payloadSchema),`,
327
327
  " success: WorkflowRunHandleSchema,",
328
328
  ` error: ${n}.errorSchema,`,
329
329
  "})"
@@ -357,7 +357,7 @@ ${o.join("\n\n")}\n`);
357
357
  ...B.map((e) => ` ${e},`),
358
358
  ...D
359
359
  ].join("\n"), U = ["queryToRpc"];
360
- (O.some((e) => e.kind === "mutation") || h.some((e) => e.kind === "mutation")) && U.push("mutationToRpc"), (O.some((e) => e.kind === "action") || F || p || h.some((e) => e.kind === "action")) && U.push("actionToRpc"), O.some((e) => e.kind === "stream") && U.push("streamToRpc"), O.some((e) => e.kind === "event") && U.push("eventToRpc"), F && U.push("WorkflowRunHandleSchema", "workflowCancelDescriptor", "workflowDomainEventsQueryDescriptor", "workflowEventDeliveriesQueryDescriptor", "workflowResumeDescriptor", "workflowRunEventsQueryDescriptor", "workflowRunQueryDescriptor", "workflowRunsQueryDescriptor", "workflowRunStepsQueryDescriptor", "workflowSignalDescriptor", "workflowUpdateDescriptor"), c && U.push("mutationToRpc", "undoLogQueryDescriptor", "undoApplyDescriptor", "undoRedoDescriptor"), R && U.push("mutationToRpc", "approvalsPendingQueryDescriptor", "approvalsDecideDescriptor"), l && U.push("mutationToRpc", "actionToRpc", "connectionsListQueryDescriptor", "connectionStartDescriptor", "connectionSubmitTokenDescriptor", "connectionDisconnectDescriptor"), (O.some((e) => e.kind === "query" || e.kind === "mutation" || e.kind === "action" || e.kind === "stream") || p || F || c || R || l || C) && U.push("normalizeDescriptor");
360
+ (O.some((e) => e.kind === "mutation") || h.some((e) => e.kind === "mutation")) && U.push("mutationToRpc"), (O.some((e) => e.kind === "action") || F || p || h.some((e) => e.kind === "action")) && U.push("actionToRpc"), O.some((e) => e.kind === "stream") && U.push("streamToRpc"), O.some((e) => e.kind === "event") && U.push("eventToRpc"), F && U.push("strictInput", "WorkflowRunHandleSchema", "workflowCancelDescriptor", "workflowDomainEventsQueryDescriptor", "workflowEventDeliveriesQueryDescriptor", "workflowResumeDescriptor", "workflowRunEventsQueryDescriptor", "workflowRunQueryDescriptor", "workflowRunsQueryDescriptor", "workflowRunStepsQueryDescriptor", "workflowSignalDescriptor", "workflowUpdateDescriptor"), c && U.push("mutationToRpc", "undoLogQueryDescriptor", "undoApplyDescriptor", "undoRedoDescriptor"), R && U.push("mutationToRpc", "approvalsPendingQueryDescriptor", "approvalsDecideDescriptor"), l && U.push("mutationToRpc", "actionToRpc", "connectionsListQueryDescriptor", "connectionStartDescriptor", "connectionSubmitTokenDescriptor", "connectionDisconnectDescriptor"), (O.some((e) => e.kind === "query" || e.kind === "mutation" || e.kind === "action" || e.kind === "stream") || p || F || c || R || l || C) && U.push("normalizeDescriptor");
361
361
  let J = [...new Set(U)].sort(), Y = [...O.filter((e) => e.kind === "query" || e.kind === "mutation" || e.kind === "action" || e.kind === "stream").map((e) => [e.rpcTag, A(e)]), ...s.flatMap((e) => [[`${e.name}.send`, `${y(e)}.send`], [`${e.name}.messages`, `${y(e)}.messages`]])], X = [
362
362
  ...F ? [
363
363
  ["__voltro.workflow.run", "workflowRunQueryDescriptor"],
@@ -0,0 +1,2 @@
1
+ import { t as e } from "./codegen-GYYcdpCg.js";
2
+ export { e as checkRpcGroupFreshness };
@@ -1,5 +1,5 @@
1
- import { l as e, z as t } from "./dev-Dc4cpmRl.js";
2
- import { r as n } from "./codegen-CYM3Zqrf.js";
1
+ import { l as e, z as t } from "./dev-bwDgSObH.js";
2
+ import { r as n } from "./codegen-GYYcdpCg.js";
3
3
  import { basename as r, relative as i, resolve as a } from "node:path";
4
4
  //#region src/codegenCommand.ts
5
5
  var o = async (i) => {
@@ -5080,7 +5080,23 @@ var x = (e, t, n) => {
5080
5080
  kind: "manual",
5081
5081
  appliesTo: () => !0,
5082
5082
  note: (/* @__PURE__ */ "The set of framework tables your app DECLARES is hashed into the schema(fingerprint. A pre-deploy `voltro db apply` records that hash; every serving(container recomputes it and refuses to boot if it differs. Until this release(three RUNTIME flags fed into that set:(( CDC=0 removed `_voltro_cdc_offsets`( VOLTRO_UNDO=on added `_voltro_undo_log`( VOLTRO_TRACING_PERSIST=all added `_voltro_traces`((All three read as pure runtime knobs, so they tend to live on the PODS and(not on the pre-upgrade migrate Job — which is a green migrate followed by(every pod in CrashLoopBackOff. This was reported against a Helm deployment(where `CDC: \"0\"` was in all three overlays' pod blocks and none of their(jobs; it had been latent for months and would have fired on the next deploy.((WHAT YOU MUST DO — only if you set one of these to turn a table ON in(production:(( * `VOLTRO_UNDO=on` in production → add to app.config.ts:( export default { schema: { undo: true }, … }( * `VOLTRO_TRACING_PERSIST` keeping trace history in production → add:( export default { schema: { traces: true }, … }(( Without the declaration, `voltro serve` now REFUSES to boot and names the( field. That refusal is the point: before, the same configuration either( wrote to a table nobody created or produced a fingerprint the migrate job( could not reproduce.(( Turning capture OFF still needs no declaration and never will — a declared( table nobody writes to is harmless. Only turning it ON is a schema claim.((WHAT HAPPENS WITHOUT ANY ACTION:(( * On mariadb or mssql you will see ONE additive `create-table` in your next( plan: `_voltro_cdc_offsets` is now declared by the DIALECT rather than by( `CDC`, so it exists whether or not that process drives CDC. It is an( offsets table — a handful of rows — and the alternative was leaving a( runtime flag able to split your fleet.( * On postgres/sqlite nothing changes: those dialects have no offsets table.((AND ONE THING WORTH DOING ANYWAY: if you are upgrading a deployment that has(a separate migrate job, diff that job's `env:` against your pods' once. The(framework can now guarantee its OWN tables do not depend on a runtime flag;(it cannot make the same guarantee for a plugin's `extendSchema.tables`, which(is app code and may read anything. A `prod-mismatch` refusal now prints which(tables this process decided and from which input, so the comparison is quick.".split("(")).join("\n")
5083
- }), q = "session_expired", J = [
5083
+ }), q = "session_expired", Jr = C({
5084
+ version: "0.35.1",
5085
+ id: "0.35.1/01_unauthorized-replaces-session-expired",
5086
+ title: "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
5087
+ kind: "transform",
5088
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(q)),
5089
+ transform: ({ project: e, log: t }) => {
5090
+ let n = [];
5091
+ for (let t of e.getSourceFiles()) {
5092
+ if (!t.getFullText().includes(q)) continue;
5093
+ let e = !1;
5094
+ for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === q && (n.setLiteralValue("unauthorized"), e = !0);
5095
+ e && n.push(t.getFilePath());
5096
+ }
5097
+ n.length !== 0 && t(`rewrote '${q}' to 'unauthorized' in ${n.length} file(s).\n A 401 now means only "the credential was refused" — expired, revoked, wrong\n scope and MALFORMED all produce that status, and the old name asserted the\n first one. If a rewritten branch was meant to catch a credential your\n CONNECTION VAULT could not produce (no grant, revoked grant, failed refresh),\n change it to 'credential_unusable' — that is the code the vault raises, and it\n is the one where re-auth is genuinely the right action.\n A health check that logs the user out on this code should read 'unauthorized'\n as "ask again", not as "the session is gone".\n` + n.map((e) => ` ${e}`).join("\n"));
5098
+ }
5099
+ }), Yr = /\(\s*\{[^}]*\.\.\./, Xr = /\bquery\??\.?\(\s*['"`]/, J = [
5084
5100
  ge,
5085
5101
  _e,
5086
5102
  ye,
@@ -5181,22 +5197,65 @@ var x = (e, t, n) => {
5181
5197
  Wr,
5182
5198
  Kr,
5183
5199
  qr,
5200
+ Jr,
5184
5201
  C({
5185
- version: "0.35.1",
5186
- id: "0.35.1/01_unauthorized-replaces-session-expired",
5187
- title: "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
5188
- kind: "transform",
5189
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(q)),
5190
- transform: ({ project: e, log: t }) => {
5191
- let n = [];
5192
- for (let t of e.getSourceFiles()) {
5193
- if (!t.getFullText().includes(q)) continue;
5194
- let e = !1;
5195
- for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === q && (n.setLiteralValue("unauthorized"), e = !0);
5196
- e && n.push(t.getFilePath());
5197
- }
5198
- n.length !== 0 && t(`rewrote '${q}' to 'unauthorized' in ${n.length} file(s).\n A 401 now means only "the credential was refused" — expired, revoked, wrong\n scope and MALFORMED all produce that status, and the old name asserted the\n first one. If a rewritten branch was meant to catch a credential your\n CONNECTION VAULT could not produce (no grant, revoked grant, failed refresh),\n change it to 'credential_unusable' that is the code the vault raises, and it\n is the one where re-auth is genuinely the right action.\n A health check that logs the user out on this code should read 'unauthorized'\n as "ask again", not as "the session is gone".\n` + n.map((e) => ` ${e}`).join("\n"));
5199
- }
5202
+ version: "0.37.0",
5203
+ id: "0.37.0/01_procedure-input-rejects-undeclared-fields",
5204
+ title: "a procedure input rejects a field it does not declare (it used to drop it)",
5205
+ kind: "manual",
5206
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5207
+ let t = e.getFullText();
5208
+ return Yr.test(t) || Xr.test(t) || w("query").test(t);
5209
+ }),
5210
+ note: [
5211
+ "A procedure call whose input carries a field the schema does not declare",
5212
+ "now fails with a ParseError naming the field. Before this release the",
5213
+ "field was DISCARDED and the call ran with what was left.",
5214
+ "",
5215
+ "The measurement that caused it — a consumer's root layout:",
5216
+ "",
5217
+ " query?.('userSettings.list', { employeeId })",
5218
+ "",
5219
+ "That procedure declares `userId` / `userIdIn`. The payload decoded to `{}`,",
5220
+ "which for a LIST query is not a narrower filter — it is NO filter. An admin",
5221
+ "was served a different user's settings row, and nothing logged anything.",
5222
+ "",
5223
+ "WHAT TO DO",
5224
+ "",
5225
+ "1. Run your test suite and your app against a dev build. A wrong field now",
5226
+ " surfaces as a ParseError that NAMES the key and lists the accepted ones,",
5227
+ " so each one is a one-line read rather than an investigation.",
5228
+ "",
5229
+ "2. Look at every SPREAD into a procedure input:",
5230
+ "",
5231
+ " await api.orders.list({ ...filters, limit })",
5232
+ "",
5233
+ " This is the construct the change is aimed at. The keys come from a value,",
5234
+ " so nobody reading the call site can see what is being sent — which is",
5235
+ " exactly the condition under which a stray key survived unnoticed.",
5236
+ "",
5237
+ "3. If a spread legitimately carries more than the procedure declares, narrow",
5238
+ " it at the call site (`{ status: filters.status, limit }`) rather than",
5239
+ " widening the input schema. Widening restores the silent drop under a",
5240
+ " different name: the field is accepted and still does nothing.",
5241
+ "",
5242
+ "WHAT DOES NOT CHANGE",
5243
+ "",
5244
+ " • A DECLARED field, including every optional one, decodes exactly as",
5245
+ " before. An empty input to a procedure that declares none is still fine.",
5246
+ " • Nested objects and union members follow the same rule — that is the",
5247
+ " point, since a filter object one level down is the commoner spelling.",
5248
+ " • A non-object input (a scalar, a void input) is unaffected; there is no",
5249
+ " excess property for it to have.",
5250
+ "",
5251
+ "WHY IT IS NOT A WARNING",
5252
+ "",
5253
+ "The decoder cannot tell a projection field from a FILTER field. Dropping an",
5254
+ "unknown `include` costs a caller some data; dropping an unknown `tenantId`",
5255
+ "hands them somebody else's. Since nothing at decode time distinguishes the",
5256
+ "two, the only safe direction is to refuse — and a warning would have to be",
5257
+ "read by someone, in a log, after the wrong rows were already served."
5258
+ ].join("\n")
5200
5259
  })
5201
5260
  ], Y = /* @__PURE__ */ new Set([
5202
5261
  "node_modules",
@@ -5210,12 +5269,12 @@ var x = (e, t, n) => {
5210
5269
  ".turbo",
5211
5270
  ".cache",
5212
5271
  ".output"
5213
- ]), Jr = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Z = /\.(?:tsx?|jsx?|mjs|cjs)$/, Yr = (e) => m(e).replace(Z, ""), Xr = (e) => {
5272
+ ]), Zr = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Z = /\.(?:tsx?|jsx?|mjs|cjs)$/, Qr = (e) => m(e).replace(Z, ""), $r = (e) => {
5214
5273
  let t = e.split("/").filter((e) => e.length > 0).pop();
5215
5274
  if (t === void 0 || t === "." || t === "..") return;
5216
5275
  let n = t.replace(Z, "");
5217
5276
  return n.length > 0 ? n : void 0;
5218
- }, Zr = (e) => {
5277
+ }, ei = (e) => {
5219
5278
  let t = /* @__PURE__ */ new Map();
5220
5279
  for (let n of e) {
5221
5280
  let e;
@@ -5226,55 +5285,55 @@ var x = (e, t, n) => {
5226
5285
  }
5227
5286
  X.lastIndex = 0;
5228
5287
  for (let r = X.exec(e); r !== null; r = X.exec(e)) {
5229
- let e = Xr(r[1] ?? r[2] ?? "");
5288
+ let e = $r(r[1] ?? r[2] ?? "");
5230
5289
  if (e === void 0) continue;
5231
5290
  let i = t.get(e);
5232
5291
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
5233
5292
  }
5234
5293
  }
5235
5294
  return t;
5236
- }, Qr = (e, t) => {
5295
+ }, ti = (e, t) => {
5237
5296
  let n = new Set(e);
5238
- for (let r of e) for (let e of [Yr(r), m(h(r))]) {
5297
+ for (let r of e) for (let e of [Qr(r), m(h(r))]) {
5239
5298
  let r = t.get(e);
5240
5299
  if (r !== void 0) for (let e of r) n.add(e);
5241
5300
  }
5242
5301
  return n;
5243
- }, $r = [
5302
+ }, ni = [
5244
5303
  "**/*.ts",
5245
5304
  "**/*.tsx",
5246
5305
  "!**/node_modules/**",
5247
5306
  "!**/dist/**",
5248
5307
  "!**/.framework/**",
5249
5308
  "!**/*.d.ts"
5250
- ], Q = (e, t, n = ni) => {
5309
+ ], Q = (e, t, n = ai) => {
5251
5310
  let r = /* @__PURE__ */ new Set();
5252
- for (let e of t) for (let t of e.scope ?? $r) r.add(t);
5311
+ for (let e of t) for (let t of e.scope ?? ni) r.add(t);
5253
5312
  let i = n(e);
5254
5313
  if (i !== void 0) {
5255
- let t = [...r].filter((e) => !e.startsWith("!")).map((e) => ti(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => ti(e.slice(1))), a = [...Y], o = [];
5314
+ let t = [...r].filter((e) => !e.startsWith("!")).map((e) => ii(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => ii(e.slice(1))), a = [...Y], o = [];
5256
5315
  for (let r of i) {
5257
5316
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
5258
5317
  let i = g(e, r);
5259
5318
  v(i) && o.push(i);
5260
5319
  }
5261
- return ei(o.length, e), o;
5320
+ return ri(o.length, e), o;
5262
5321
  }
5263
5322
  let a = oe([...r], {
5264
5323
  cwd: e,
5265
5324
  absolute: !0,
5266
5325
  expandDirectories: !1,
5267
5326
  followSymbolicLinks: !1,
5268
- ignore: [...Jr()]
5327
+ ignore: [...Zr()]
5269
5328
  });
5270
- return ei(a.length, e), a;
5271
- }, ei = (e, t) => {
5272
- let n = ri();
5329
+ return ri(a.length, e), a;
5330
+ }, ri = (e, t) => {
5331
+ let n = oi();
5273
5332
  if (!(e <= n)) throw Error(`voltro update: the codemod scan matched ${e} files under ${t}, past the ${n} ceiling.\n Every matched file is parsed into one ts-morph project, so the heap needed grows with\n this number — past the ceiling it runs out of memory, and V8 aborts without naming a\n limit, which is why this stops here instead. Three ways out, in order:\n • put non-source directories in .gitignore (inside a git repo the scan then never\n sees them at all — a build cache, a data dump, a virtualenv),\n • run \`voltro update\` from the app directory rather than the workspace root, or\n • take one codemod at a time: \`voltro update --dry-run\` lists the ids, then\n \`voltro update --codemods-only --only <id>\` runs a single one.\n If every one of those files really is source this pass must rewrite, raise the ceiling\n with VOLTRO_CODEMOD_MAX_FILES and give node room: NODE_OPTIONS=--max-old-space-size=8192.`);
5274
- }, ti = (e) => {
5333
+ }, ii = (e) => {
5275
5334
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
5276
5335
  return (e) => RegExp(`^${t}$`).test(e);
5277
- }, ni = (e) => {
5336
+ }, ai = (e) => {
5278
5337
  try {
5279
5338
  let t = re("git", [
5280
5339
  "ls-files",
@@ -5295,7 +5354,7 @@ var x = (e, t, n) => {
5295
5354
  } catch {
5296
5355
  return;
5297
5356
  }
5298
- }, ri = () => {
5357
+ }, oi = () => {
5299
5358
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
5300
5359
  return Number.isInteger(e) && e > 0 ? e : 2e4;
5301
5360
  }, $ = (e, t) => {
@@ -5305,7 +5364,7 @@ var x = (e, t, n) => {
5305
5364
  if (t !== 0) return t;
5306
5365
  }
5307
5366
  return 0;
5308
- }, ii = (e, t, n = J) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(he), ai = (e, t, n = () => {}) => {
5367
+ }, si = (e, t, n = J) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(he), ci = (e, t, n = () => {}) => {
5309
5368
  let r = [], i = [], a = [];
5310
5369
  for (let o of t) {
5311
5370
  if (!(!o.appliesTo || o.appliesTo({ project: e }))) {
@@ -5351,8 +5410,8 @@ var x = (e, t, n) => {
5351
5410
  manual: i,
5352
5411
  skipped: a
5353
5412
  };
5354
- }, oi = async (e) => {
5355
- let t = e.log ?? (() => {}), n = ii(e.from, e.to, e.codemods ?? J), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
5413
+ }, li = async (e) => {
5414
+ let t = e.log ?? (() => {}), n = si(e.from, e.to, e.codemods ?? J), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
5356
5415
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
5357
5416
  if (r.length > 0) throw Error(`voltro update --only: no codemod in ${e.from} → ${e.to} has id ${r.map((e) => `"${e}"`).join(", ")}.\n Available for this jump:\n${n.map((e) => ` ${e.id}`).join("\n")}`);
5358
5417
  return t;
@@ -5371,15 +5430,15 @@ var x = (e, t, n) => {
5371
5430
  baseUrl: i.baseUrl,
5372
5431
  paths: i.paths
5373
5432
  } }
5374
- }), o, s = Q(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p = c.length > 0 ? Zr(Q(e.root, [{
5433
+ }), o, s = Q(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p = c.length > 0 ? ei(Q(e.root, [{
5375
5434
  ...c[0],
5376
- scope: $r
5435
+ scope: ni
5377
5436
  }])) : void 0;
5378
5437
  for (let n of r) {
5379
5438
  if (c.includes(n)) {
5380
5439
  let r = a(), i = Q(e.root, [n]);
5381
- for (let e of Qr(i, p)) v(e) && r.addSourceFileAtPath(e);
5382
- let s = ai(r, [n], t);
5440
+ for (let e of ti(i, p)) v(e) && r.addSourceFileAtPath(e);
5441
+ let s = ci(r, [n], t);
5383
5442
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
5384
5443
  continue;
5385
5444
  }
@@ -5388,7 +5447,7 @@ var x = (e, t, n) => {
5388
5447
  for (let t of s) v(t) && e.addSourceFileAtPath(t);
5389
5448
  return e;
5390
5449
  })();
5391
- let r = ai(o, [n], t);
5450
+ let r = ci(o, [n], t);
5392
5451
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
5393
5452
  }
5394
5453
  return {
@@ -5398,4 +5457,4 @@ var x = (e, t, n) => {
5398
5457
  };
5399
5458
  };
5400
5459
  //#endregion
5401
- export { C as i, oi as n, Y as r, $ as t };
5460
+ export { C as i, li as n, Y as r, $ as t };