@voltro/cli 0.59.0 → 0.60.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 (67) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/bin.js +1 -1
  3. package/dist/{build-uRMZrT8z.js → build-BxAsLzyU.js} +14 -10
  4. package/dist/{codemodRunner-kNnh1I7_.js → codemodRunner-ClUqkhvR.js} +86 -37
  5. package/dist/{commands-cu34ENUT.js → commands-Ro4hYBHD.js} +7 -7
  6. package/dist/{dashboardCommand-gG1y4PFG.js → dashboardCommand-t8Qsebjr.js} +1 -1
  7. package/dist/{evolveCommand-8CuFBlbQ.js → evolveCommand-CThb0EZZ.js} +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/{infoCommand-D02A4WI2.js → infoCommand-a0WkKcUY.js} +1 -1
  10. package/dist/{start-C75w6ftA.js → start-BhoDQ25C.js} +1 -1
  11. package/dist/{start-DNtIlBcn.js → start-CcdPRG8L.js} +418 -367
  12. package/dist/startEntry.js +1 -1
  13. package/dist/{updateCommand-Bw3mCUDT.js → updateCommand-D9avpQFT.js} +1 -1
  14. package/dist/updateCommand-QMOirvXC.js +2 -0
  15. package/package.json +25 -19
  16. package/templates/AGENTS.md +1 -1
  17. package/templates/agent-docs/_index.md +1 -1
  18. package/templates/agent-docs/plugins/sentry.md +93 -6
  19. package/templates/agent-docs/whats-new.md +29 -26
  20. package/templates/apps/api-ai/package.json +6 -6
  21. package/templates/apps/api-auth/package.json +8 -8
  22. package/templates/apps/api-backend/package.json +7 -7
  23. package/templates/apps/api-backend-deactivation/package.json +7 -7
  24. package/templates/apps/api-backend-mail/package.json +8 -8
  25. package/templates/apps/api-backend-mariadb/package.json +9 -9
  26. package/templates/apps/api-backend-sqlite/package.json +8 -8
  27. package/templates/apps/api-backend-storage/package.json +8 -8
  28. package/templates/apps/api-cms/package.json +9 -9
  29. package/templates/apps/api-collab/package.json +8 -8
  30. package/templates/apps/api-data-advanced/package.json +8 -8
  31. package/templates/apps/api-durable/package.json +8 -8
  32. package/templates/apps/api-feature-flags/package.json +9 -9
  33. package/templates/apps/api-governance/package.json +8 -8
  34. package/templates/apps/api-kv/package.json +8 -8
  35. package/templates/apps/api-moderation/package.json +8 -8
  36. package/templates/apps/api-observability/package.json +8 -8
  37. package/templates/apps/api-ratelimit/package.json +8 -8
  38. package/templates/apps/api-rbac/package.json +8 -8
  39. package/templates/apps/api-rest/package.json +7 -7
  40. package/templates/apps/api-row-history/package.json +8 -8
  41. package/templates/apps/api-saas/package.json +11 -11
  42. package/templates/apps/api-saas-starter/package.json +10 -10
  43. package/templates/apps/api-search/package.json +8 -8
  44. package/templates/apps/api-status/package.json +8 -8
  45. package/templates/apps/api-webhooks/package.json +9 -9
  46. package/templates/apps/changelog/package.json +7 -7
  47. package/templates/apps/edge-functions/package.json +2 -2
  48. package/templates/apps/frontend-admin/package.json +7 -7
  49. package/templates/apps/frontend-app/package.json +8 -8
  50. package/templates/apps/frontend-auth/package.json +7 -7
  51. package/templates/apps/frontend-blank/package.json +6 -6
  52. package/templates/apps/frontend-cms/package.json +8 -8
  53. package/templates/apps/frontend-collab/package.json +9 -9
  54. package/templates/apps/frontend-contact/package.json +7 -7
  55. package/templates/apps/frontend-dashboard/package.json +6 -6
  56. package/templates/apps/frontend-docs/package.json +8 -8
  57. package/templates/apps/frontend-i18n/package.json +6 -6
  58. package/templates/apps/frontend-landing/package.json +7 -7
  59. package/templates/apps/frontend-portal/package.json +7 -7
  60. package/templates/apps/frontend-saas/package.json +7 -7
  61. package/templates/apps/frontend-spa/package.json +6 -6
  62. package/templates/apps/frontend-ssr/package.json +6 -6
  63. package/templates/apps/frontend-ssr-api/package.json +7 -7
  64. package/templates/apps/frontend-static-blog/package.json +8 -8
  65. package/templates/apps/frontend-status/package.json +7 -7
  66. package/templates/apps/mobile-app/package.json +4 -4
  67. package/dist/updateCommand-inInBIAh.js +0 -2
package/CHANGELOG.md CHANGED
@@ -39,6 +39,64 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.60.0] — 2026-08-31
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/plugin-sentry** — **A declared failure no longer reaches Sentry by default, and the source-map upload now injects debug ids.** Two separate defects, both found by the same deployment on its first day of real server-side events.
47
+
48
+ **1. The contract was being reported as an incident.** The rpc interceptor skipped only clean interrupts; everything else went to `captureException`. So a failure declared in a procedure's `error:` union — the thing the client gets typed and branches on — arrived as `level: error`, `handled: yes`. The first server-side issue a deployment ever received was a person clicking a team they are not a member of.
49
+
50
+ Effect separates a failure from a defect, this framework leans on that split deliberately (a store refusal was made typed so an app could branch on it; an unlookupable conflict key was deliberately left a defect, because it is a broken call rather than a condition in the data), and a descriptor carries it in `error:`. Reporting both as an incident discarded that one layer up.
51
+
52
+ `shouldCapture` now skips a cause that is failures-ONLY. A defect is reported as before, including a defect that travelled beside a failure — the rule is failures-only rather than "any failure present" precisely so one cannot hide the other. `captureFailures: true` restores the old behaviour; a predicate keeps the ones that are signal.
53
+
54
+ **The browser half moved with it**, or the option would have been half-wired: a rejected call is an rpc error on the client too, published to the client error bus and captured by the browser bridge. `initSentryBrowser` takes the same option and applies it to `rpc.*` events carrying a `_tag`. Route render failures and `reportClientError` calls are never filtered — nobody declared those.
55
+
56
+ **2. `sentry-cli sourcemaps upload` does NOT write debug ids.** `inject` is a separate subcommand; `upload` only uses ids that are already present, and falls back to matching on the artifact NAME when they are not. That fallback cannot work for a server bundle: the artifact is named from `--url-prefix` (`~/chunk- ABC.js`) while the frame carries the absolute path the node process loaded, and nothing rewrites either side.
57
+
58
+ Measured downstream: 4300 artifacts uploaded, release finalised, every frame still minified. Nothing was red — the exact shape this code's own header warns about, an upload that matched nothing looking like one that worked. The comment above the uploader asserted the injection happened, which made it a description standing where a check belonged.
59
+
60
+ `inject` now runs first, over the same directories, and `sourcemapDebugIds.test.ts` drives the real binary to assert an id lands in both the JS and the map. `--url-prefix` stays as the fallback for the browser bundle, whose frames really are URLs.
61
+
62
+ The boot line names the new setting (`sentry active … captureFailures=false`), because a default the framework picks for you is one nobody finds again.
63
+
64
+ **`voltro update` carries you across this** — codemod `0.60.0/01_declared_failures_are_not_incidents`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.60.0).
65
+
66
+ ### Added
67
+
68
+ - **@voltro/cli, @voltro/plugin-sentry** — **The web server process now initialises Sentry and reports its own errors.**
69
+
70
+ A framework app runs two server processes. `voltro serve` is the api, where `sentryPlugin()` initialises the SDK through the plugin lifecycle. `voltro start` is the web server — SSR, loaders, ISR, the revalidation legs — and it has no plugin lifecycle, so it had none.
71
+
72
+ That was not a cosmetic difference in boot output. A web pod legitimately logs less than an api pod, because it has no store, no rpc, no scheduler, no workflows and no plugins. What it also had was **no error reporting**: an SSR shell throw is caught, logged and answered with a 500, so no browser ever renders it and the client-side ErrorBoundary bridge cannot see it either. Both ends of the integration worked and the middle was dark — while the docs' "React render error ✅ auto" row, true of the client path, read as covering all of them.
73
+
74
+ Set `SENTRY_DSN` on the web deployment and the boot says `sentry active` with the same message and the same field names as the api half, from the same function — `initSentryServer` in `@voltro/plugin-sentry/server`, which the api plugin now calls too. One init, because a copy of `skipOpenTelemetrySetup`, the traces default, the overrides-first spread and the degrade-on-missing-SDK path would have drifted the moment either half gained a case.
75
+
76
+ Three deliberate asymmetries, each because the two processes are not the same thing:
77
+
78
+ - **`traces: false` on the web side.** The api contributes a span processor to the framework's tracer; the web server has no tracer at all, so tracing on would put `traces: true` in a boot line while nothing produces a span. - **No DSN is SILENT here.** On the api side `sentryPlugin()` is a declaration and an inert one contradicts it. There is no declaration here. - **`@voltro/plugin-sentry` must be installed in the WEB app.** With a DSN set and the package missing, the boot names the command rather than failing — monitoring must never be what stops a deploy.
79
+
80
+ Measured against a real production `voltro start`, both branches. Note that production `voltro start` loads the app's precompiled start bundle, so a web deployment picks this up when that bundle is rebuilt — the framework version alone is not enough.
81
+
82
+ ### Fixed
83
+
84
+ - **@voltro/workflow, @voltro/runtime, @voltro/database, @voltro/cli, @voltro/plugin-sentry** — **Six raw writes removed from production log streams — and the guard that was supposed to catch them rewritten, because it was green for two independent reasons.**
85
+
86
+ A pod tail showed `[voltro:workflow] shard-lock coordination: row-based (dialect=mariadb, mode=row)` sitting between JSON records. `@voltro/logger` is what makes a line JSON in a pod and pretty on a TTY; a `process.stderr.write` bypasses that decision at exactly the place nobody looks, because a dev terminal renders both the same.
87
+
88
+ Fixed at the source: the workflow cluster layer (2), `rpcServer`'s computed-cache warning, the subscription outbox's and the RYW store's `warn`/`onError` defaults — those two are not fallbacks, the callers pass nothing, so the default IS the production path — and the migration file discovery's skip notice, which lands in the migrate job's stream.
89
+
90
+ **The guard is the part worth reading.** `prodLogDiscipline.test.ts` existed for this exact class and reported clean, for two reasons that had to be fixed separately:
91
+
92
+ - Its file set was a hand-written list of five. A guard that opts files IN says nothing about any file added after it was written. - Its matcher was LINE-LOCAL, so `process.stderr.write(` on one line and the `` `[tag] `` on the next never matched — 4 of the 9 call sites in the repo are written that way, including one in a file that WAS on the list. The guard had been pointed straight at an offender and called it clean.
93
+
94
+ It is opt-OUT now: every server-side package's source is scanned, exceptions carry a reason, and a second test fails if an exception's call site disappears — an allowlist entry for code that is gone reads as a rule with a hole in it.
95
+
96
+ Verified against a real `voltro serve` under `NODE_ENV=production`: 35 records, zero framework lines that are not JSON.
97
+
98
+ ---
99
+
42
100
  ## [0.59.0] — 2026-08-30
43
101
 
44
102
  ### ⚠ BREAKING
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as e } from "./commands-cu34ENUT.js";
2
+ import { r as e } from "./commands-Ro4hYBHD.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";
@@ -118,21 +118,25 @@ var X = W({ scope: "voltro:build" }), ie = async (e, t = {}) => {
118
118
  n.set(e, [...n.get(e) ?? [], r.dir]);
119
119
  }
120
120
  return [...n];
121
- }, ce = (e) => async (t, n, r) => {
122
- let i = new (await (ue(e)))(null, {
123
- authToken: t.authToken,
124
- org: t.org,
125
- project: t.project,
126
- ...t.url === void 0 ? {} : { url: t.url },
121
+ }, ce = (e, t = ue) => async (n, r, i) => {
122
+ let a = new (await (t(e)))(null, {
123
+ authToken: n.authToken,
124
+ org: n.org,
125
+ project: n.project,
126
+ ...n.url === void 0 ? {} : { url: n.url },
127
127
  silent: !1
128
128
  });
129
- await i.releases.new(t.release);
130
- for (let [e, n] of Q(r, t.urlPrefix)) await i.releases.uploadSourceMaps(t.release, {
131
- include: n,
129
+ await a.execute([
130
+ "sourcemaps",
131
+ "inject",
132
+ ...i.map((e) => e.dir)
133
+ ], !0), await a.releases.new(n.release);
134
+ for (let [e, t] of Q(i, n.urlPrefix)) await a.releases.uploadSourceMaps(n.release, {
135
+ include: t,
132
136
  urlPrefix: e,
133
137
  rewrite: !0
134
138
  });
135
- await i.releases.finalize(t.release);
139
+ await a.releases.finalize(n.release);
136
140
  }, le = (e) => {
137
141
  let t = e, n = [
138
142
  t?.SentryCli,
@@ -6550,7 +6550,55 @@ var C = (e, t, n) => {
6550
6550
  }
6551
6551
  n > 0 && (t?.(`sourcemaps: moved out of \`web:\` in ${n} config file(s)`), t?.(" → it now covers the SSR, start and serve bundles too, not only the browser bundle")), r > 0 && t?.(`sourcemaps: ${r} case(s) left for you — listed above`), n === 0 && r === 0 && t?.("sourcemaps: no `web.sourcemaps` found — nothing to move");
6552
6552
  }
6553
- }), pa = (e) => /\bcostSource\b|\bCostBreakdown\b|\bestimateCostUsd\b|\b_voltro_ai_usage\b|\bMeteredUsage\b/.test(e.getFullText()), ma = [
6553
+ }), pa = (e) => /\bsentryPlugin\b|@voltro\/plugin-sentry/.test(e.getFullText()), ma = T({
6554
+ version: "0.60.0",
6555
+ id: "0.60.0/01_declared_failures_are_not_incidents",
6556
+ title: "Sentry no longer reports declared failures by default",
6557
+ kind: "manual",
6558
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(pa),
6559
+ note: [
6560
+ "A DECLARED FAILURE IS NO LONGER A SENTRY ISSUE BY DEFAULT.",
6561
+ "",
6562
+ "WHAT CHANGED. The rpc interceptor skipped only clean interrupts; every",
6563
+ "other cause went to `captureException`, so a failure declared in a",
6564
+ "procedure's `error:` union arrived as `level: error`. It now skips a cause",
6565
+ "that is failures-ONLY. Defects are reported exactly as before, including a",
6566
+ "defect that travelled beside a failure.",
6567
+ "",
6568
+ "WHY. A declared failure is a contract: somebody wrote the outcome down, the",
6569
+ "client gets it typed and branches on it, and it describes a state of the",
6570
+ "world. `AccessDeniedError` on a team you are not in is the system working.",
6571
+ "A defect is the opposite — nobody foresaw it — and that is what Sentry is",
6572
+ "built for. Effect separates the two, this framework leans on that split",
6573
+ "deliberately, and a descriptor carries it in `error:`; reporting both as an",
6574
+ "incident discarded it one layer up. A deployment measured the result: its",
6575
+ "first server-side issue was a person clicking a team they are not in.",
6576
+ "",
6577
+ "THE BROWSER HALF MOVED WITH IT. A rejected call is an rpc error on the",
6578
+ "client too, so `initSentryBrowser` applies the same rule to `rpc.*` events",
6579
+ "that carry a `_tag`. Route render failures and `reportClientError` calls",
6580
+ "are never filtered — nobody declared those.",
6581
+ "",
6582
+ "WHAT TO DO — one of these, deliberately:",
6583
+ "",
6584
+ "1) NOTHING, if Sentry is where you look for the unforeseen. Your issue list",
6585
+ " gets quieter; the trace still shows the failure.",
6586
+ "",
6587
+ "2) KEEP THEM, if Sentry is where you see errors at all:",
6588
+ "",
6589
+ " sentryPlugin({ captureFailures: true })",
6590
+ " initSentryBrowser({ captureFailures: true })",
6591
+ "",
6592
+ "3) KEEP THE ONES THAT ARE SIGNAL:",
6593
+ "",
6594
+ " sentryPlugin({",
6595
+ " captureFailures: (e) => e._tag === 'PaymentDeclined',",
6596
+ " })",
6597
+ "",
6598
+ "The boot line now names the setting — `sentry active … captureFailures=false`",
6599
+ "— so the choice is visible where the decision takes effect."
6600
+ ].join("\n")
6601
+ }), ha = (e) => /\bcostSource\b|\bCostBreakdown\b|\bestimateCostUsd\b|\b_voltro_ai_usage\b|\bMeteredUsage\b/.test(e.getFullText()), ga = [
6554
6602
  he,
6555
6603
  ge,
6556
6604
  ve,
@@ -6694,12 +6742,13 @@ var C = (e, t, n) => {
6694
6742
  oa,
6695
6743
  ca,
6696
6744
  fa,
6745
+ ma,
6697
6746
  T({
6698
6747
  version: "0.59.0",
6699
6748
  id: "0.59.0/02_costsource_gained_unpriced",
6700
6749
  title: "`costSource` gained `unpriced`, and AI usage is counted in a unit",
6701
6750
  kind: "manual",
6702
- appliesTo: ({ project: e }) => e.getSourceFiles().some(pa),
6751
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(ha),
6703
6752
  note: [
6704
6753
  "AI COST NOW HAS A THIRD SOURCE, AND USAGE HAS A UNIT.",
6705
6754
  "",
@@ -6736,12 +6785,12 @@ var C = (e, t, n) => {
6736
6785
  ".turbo",
6737
6786
  ".cache",
6738
6787
  ".output"
6739
- ]), ha = () => [...[...J].map((e) => `**/${e}/**`), "**/.voltro-*/**"], Y = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, X = /\.(?:tsx?|jsx?|mjs|cjs)$/, ga = (e) => g(e).replace(X, ""), _a = (e) => {
6788
+ ]), _a = () => [...[...J].map((e) => `**/${e}/**`), "**/.voltro-*/**"], Y = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, X = /\.(?:tsx?|jsx?|mjs|cjs)$/, va = (e) => g(e).replace(X, ""), ya = (e) => {
6740
6789
  let t = e.split("/").filter((e) => e.length > 0).pop();
6741
6790
  if (t === void 0 || t === "." || t === "..") return;
6742
6791
  let n = t.replace(X, "");
6743
6792
  return n.length > 0 ? n : void 0;
6744
- }, va = (e) => {
6793
+ }, ba = (e) => {
6745
6794
  let t = /* @__PURE__ */ new Map();
6746
6795
  for (let n of e) {
6747
6796
  let e;
@@ -6752,24 +6801,24 @@ var C = (e, t, n) => {
6752
6801
  }
6753
6802
  Y.lastIndex = 0;
6754
6803
  for (let r = Y.exec(e); r !== null; r = Y.exec(e)) {
6755
- let e = _a(r[1] ?? r[2] ?? "");
6804
+ let e = ya(r[1] ?? r[2] ?? "");
6756
6805
  if (e === void 0) continue;
6757
6806
  let i = t.get(e);
6758
6807
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
6759
6808
  }
6760
6809
  }
6761
6810
  return t;
6762
- }, ya = (e, t) => {
6811
+ }, xa = (e, t) => {
6763
6812
  let n = new Set(e);
6764
- for (let r of e) for (let e of [ga(r), g(_(r))]) {
6813
+ for (let r of e) for (let e of [va(r), g(_(r))]) {
6765
6814
  let r = t.get(e);
6766
6815
  if (r !== void 0) for (let e of r) n.add(e);
6767
6816
  }
6768
6817
  return n;
6769
- }, ba = /* @__PURE__ */ ".ts,.tsx,.mts,.cts,.js,.jsx,.mjs,.cjs,.yml,.yaml,.toml,.json,.jsonc,.ini,.cfg,.conf,.sh,.bash,.zsh,.fish,.ps1,.bat,.cmd,.md,.mdx,.rst,.txt,.adoc,Makefile,makefile,GNUmakefile,Justfile,justfile,Dockerfile,Containerfile,Procfile,Jenkinsfile,Rakefile,Taskfile".split(","), xa = 2097152, Sa = 4e4, Ca = (e) => {
6818
+ }, Sa = /* @__PURE__ */ ".ts,.tsx,.mts,.cts,.js,.jsx,.mjs,.cjs,.yml,.yaml,.toml,.json,.jsonc,.ini,.cfg,.conf,.sh,.bash,.zsh,.fish,.ps1,.bat,.cmd,.md,.mdx,.rst,.txt,.adoc,Makefile,makefile,GNUmakefile,Justfile,justfile,Dockerfile,Containerfile,Procfile,Jenkinsfile,Rakefile,Taskfile".split(","), Ca = 2097152, wa = 4e4, Ta = (e) => {
6770
6819
  let t = e.slice(e.lastIndexOf("/") + 1);
6771
- return ba.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6772
- }, wa = (e) => {
6820
+ return Sa.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6821
+ }, Ea = (e) => {
6773
6822
  try {
6774
6823
  let t = b("git", [
6775
6824
  "ls-files",
@@ -6790,19 +6839,19 @@ var C = (e, t, n) => {
6790
6839
  } catch {
6791
6840
  return;
6792
6841
  }
6793
- }, Z = (e, t = wa) => {
6842
+ }, Z = (e, t = Ea) => {
6794
6843
  let n = t(e);
6795
- if (n === void 0) return Ta;
6844
+ if (n === void 0) return Da;
6796
6845
  let r = /* @__PURE__ */ new Map(), i = [];
6797
6846
  for (let t of n) {
6798
- if (t.split("/").some((e) => J.has(e)) || !Ca(t)) continue;
6799
- if (r.size >= Sa) {
6847
+ if (t.split("/").some((e) => J.has(e)) || !Ta(t)) continue;
6848
+ if (r.size >= wa) {
6800
6849
  i.push(t);
6801
6850
  continue;
6802
6851
  }
6803
6852
  let n = v(e, t);
6804
6853
  try {
6805
- if (ne(n).size > xa) {
6854
+ if (ne(n).size > Ca) {
6806
6855
  i.push(t);
6807
6856
  continue;
6808
6857
  }
@@ -6826,45 +6875,45 @@ var C = (e, t, n) => {
6826
6875
  scanned: r.size,
6827
6876
  skipped: i
6828
6877
  };
6829
- }, Ta = {
6878
+ }, Da = {
6830
6879
  find: () => [],
6831
6880
  scanned: 0,
6832
6881
  skipped: []
6833
- }, Ea = [
6882
+ }, Oa = [
6834
6883
  "**/*.ts",
6835
6884
  "**/*.tsx",
6836
6885
  "!**/node_modules/**",
6837
6886
  "!**/dist/**",
6838
6887
  "!**/.framework/**",
6839
6888
  "!**/*.d.ts"
6840
- ], Q = (e, t, n = ka) => {
6889
+ ], Q = (e, t, n = ja) => {
6841
6890
  let r = /* @__PURE__ */ new Set();
6842
- for (let e of t) for (let t of e.scope ?? Ea) r.add(t);
6891
+ for (let e of t) for (let t of e.scope ?? Oa) r.add(t);
6843
6892
  let i = n(e);
6844
6893
  if (i !== void 0) {
6845
- let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Oa(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Oa(e.slice(1))), a = [...J], o = [];
6894
+ let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Aa(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Aa(e.slice(1))), a = [...J], o = [];
6846
6895
  for (let r of i) {
6847
6896
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
6848
6897
  let i = v(e, r);
6849
6898
  y(i) && o.push(i);
6850
6899
  }
6851
- return Da(o.length, e), o;
6900
+ return ka(o.length, e), o;
6852
6901
  }
6853
6902
  let a = ae([...r], {
6854
6903
  cwd: e,
6855
6904
  absolute: !0,
6856
6905
  expandDirectories: !1,
6857
6906
  followSymbolicLinks: !1,
6858
- ignore: [...ha()]
6907
+ ignore: [..._a()]
6859
6908
  });
6860
- return Da(a.length, e), a;
6861
- }, Da = (e, t) => {
6862
- let n = Aa();
6909
+ return ka(a.length, e), a;
6910
+ }, ka = (e, t) => {
6911
+ let n = Ma();
6863
6912
  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.`);
6864
- }, Oa = (e) => {
6913
+ }, Aa = (e) => {
6865
6914
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
6866
6915
  return (e) => RegExp(`^${t}$`).test(e);
6867
- }, ka = (e) => {
6916
+ }, ja = (e) => {
6868
6917
  try {
6869
6918
  let t = b("git", [
6870
6919
  "ls-files",
@@ -6885,7 +6934,7 @@ var C = (e, t, n) => {
6885
6934
  } catch {
6886
6935
  return;
6887
6936
  }
6888
- }, Aa = () => {
6937
+ }, Ma = () => {
6889
6938
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
6890
6939
  return Number.isInteger(e) && e > 0 ? e : 2e4;
6891
6940
  }, $ = (e, t) => {
@@ -6895,7 +6944,7 @@ var C = (e, t, n) => {
6895
6944
  if (t !== 0) return t;
6896
6945
  }
6897
6946
  return 0;
6898
- }, ja = (e, t, n = ma) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), Ma = (e, t, n = () => {}, r = Ta) => {
6947
+ }, Na = (e, t, n = ga) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), Pa = (e, t, n = () => {}, r = Da) => {
6899
6948
  let i = [], a = [], o = [];
6900
6949
  for (let s of t) {
6901
6950
  let t = !s.appliesTo || s.appliesTo({
@@ -6946,8 +6995,8 @@ var C = (e, t, n) => {
6946
6995
  manual: a,
6947
6996
  skipped: o
6948
6997
  };
6949
- }, Na = async (e) => {
6950
- let t = e.log ?? (() => {}), n = ja(e.from, e.to, e.codemods ?? ma), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
6998
+ }, Fa = async (e) => {
6999
+ let t = e.log ?? (() => {}), n = Na(e.from, e.to, e.codemods ?? ga), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
6951
7000
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
6952
7001
  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")}`);
6953
7002
  return t;
@@ -6974,15 +7023,15 @@ var C = (e, t, n) => {
6974
7023
  get skipped() {
6975
7024
  return (p ??= Z(e.root)).skipped;
6976
7025
  }
6977
- }, h = c.length > 0 ? va(Q(e.root, [{
7026
+ }, h = c.length > 0 ? ba(Q(e.root, [{
6978
7027
  ...c[0],
6979
- scope: Ea
7028
+ scope: Oa
6980
7029
  }])) : void 0;
6981
7030
  for (let n of r) {
6982
7031
  if (c.includes(n)) {
6983
7032
  let r = a(), i = Q(e.root, [n]);
6984
- for (let e of ya(i, h)) y(e) && r.addSourceFileAtPath(e);
6985
- let s = Ma(r, [n], t, m);
7033
+ for (let e of xa(i, h)) y(e) && r.addSourceFileAtPath(e);
7034
+ let s = Pa(r, [n], t, m);
6986
7035
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
6987
7036
  continue;
6988
7037
  }
@@ -6991,7 +7040,7 @@ var C = (e, t, n) => {
6991
7040
  for (let t of s) y(t) && e.addSourceFileAtPath(t);
6992
7041
  return e;
6993
7042
  })();
6994
- let r = Ma(o, [n], t, m);
7043
+ let r = Pa(o, [n], t, m);
6995
7044
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
6996
7045
  }
6997
7046
  return {
@@ -7001,4 +7050,4 @@ var C = (e, t, n) => {
7001
7050
  };
7002
7051
  };
7003
7052
  //#endregion
7004
- export { T as i, Na as n, J as r, $ as t };
7053
+ export { T as i, Fa as n, J as r, $ as t };
@@ -109,7 +109,7 @@ var u = () => {
109
109
  name: "dashboard",
110
110
  summary: "Serve the DevTools dashboard standalone (from @voltro/dashboard). Targets via VOLTRO_DASHBOARD_APPS; --port to override.",
111
111
  status: "wired",
112
- run: (e) => import("./dashboardCommand-gG1y4PFG.js").then((t) => t.runDashboardCommand(e)),
112
+ run: (e) => import("./dashboardCommand-t8Qsebjr.js").then((t) => t.runDashboardCommand(e)),
113
113
  help: {
114
114
  usage: "[--port <n>]",
115
115
  flags: [["--port <n>", "port to serve the dashboard on"]],
@@ -171,7 +171,7 @@ var u = () => {
171
171
  name: "evolve",
172
172
  summary: "Schema-evolution copilot: propose an observed-graph-driven codemod + a branch-verified backfill for a schema change (rename/retype/split/drop a column, rename a table). Dry-run by default; --write applies. Usage: voltro evolve rename-column <table>.<col> --to <newName> [--write] [--json].",
173
173
  status: "wired",
174
- run: (e) => import("./evolveCommand-8CuFBlbQ.js").then((t) => t.runEvolveCommand(e)),
174
+ run: (e) => import("./evolveCommand-CThb0EZZ.js").then((t) => t.runEvolveCommand(e)),
175
175
  help: {
176
176
  usage: "<rename-column|rename-table|retype-column|split-column|drop-column> <target> [--to <new>] [--write]",
177
177
  flags: [
@@ -281,7 +281,7 @@ var u = () => {
281
281
  name: "info",
282
282
  summary: "Report versions (CLI, node, every installed @voltro/* — flags lockstep skew), package manager, and dialect. --json for CI. Exits 1 on version skew.",
283
283
  status: "wired",
284
- run: (e) => import("./infoCommand-D02A4WI2.js").then((t) => t.runInfoCommand(e)),
284
+ run: (e) => import("./infoCommand-a0WkKcUY.js").then((t) => t.runInfoCommand(e)),
285
285
  help: {
286
286
  usage: "[--json]",
287
287
  flags: [["--json", "machine-readable report"]],
@@ -370,7 +370,7 @@ var u = () => {
370
370
  name: "build",
371
371
  summary: "Production build for a web app (vite build + per-page SSG pre-render).",
372
372
  status: "wired",
373
- run: (e) => import("./build-uRMZrT8z.js").then((t) => t.runBuildCommand(e)),
373
+ run: (e) => import("./build-BxAsLzyU.js").then((t) => t.runBuildCommand(e)),
374
374
  help: {
375
375
  usage: "[app] [--target <t>]",
376
376
  flags: [["--target <t>", "build target"], ["--report", "print what a visitor downloads: first load, chunks, server artefacts"]],
@@ -421,7 +421,7 @@ var u = () => {
421
421
  name: "start",
422
422
  summary: "Production runtime: serves dist + SSR-on-demand for ssr/isr pages.",
423
423
  status: "wired",
424
- run: (e) => import("./start-C75w6ftA.js").then((t) => t.runStartCommand(e)),
424
+ run: (e) => import("./start-BhoDQ25C.js").then((t) => t.runStartCommand(e)),
425
425
  help: {
426
426
  usage: "[app]",
427
427
  examples: [["voltro start", "serve a built web app (dist + SSR-on-demand)"]],
@@ -555,14 +555,14 @@ var u = () => {
555
555
  handlesHelp: !0,
556
556
  summary: "Upgrade the app to the latest framework: bump every @voltro/* dependency (across ALL workspace members, install once at the workspace root), then run the codemods shipped with the target version. Flags: --to <version>, --from <version>, --codemods-only, --dry-run, --force, --exact, --help. After: run `voltro db apply`.",
557
557
  status: "wired",
558
- run: (e) => import("./updateCommand-inInBIAh.js").then((t) => t.runUpdateCommand(e))
558
+ run: (e) => import("./updateCommand-QMOirvXC.js").then((t) => t.runUpdateCommand(e))
559
559
  },
560
560
  {
561
561
  name: "_apply-codemods",
562
562
  summary: "Internal: apply shipped codemods for a framework version jump (re-exec target of `voltro update`).",
563
563
  status: "wired",
564
564
  hidden: !0,
565
- run: (e) => import("./updateCommand-inInBIAh.js").then((t) => t.runApplyCodemods(e))
565
+ run: (e) => import("./updateCommand-QMOirvXC.js").then((t) => t.runApplyCodemods(e))
566
566
  },
567
567
  {
568
568
  name: "embeddings",
@@ -1,6 +1,6 @@
1
1
  import { p as e } from "./dev-Ce2gEziv.js";
2
2
  import { it as t, st as n } from "./webDev-CBN9KkyQ.js";
3
- import { t as r } from "./start-DNtIlBcn.js";
3
+ import { t as r } from "./start-CcdPRG8L.js";
4
4
  import { join as i } from "node:path";
5
5
  import { existsSync as a } from "node:fs";
6
6
  import { createLogger as o } from "@voltro/logger";
@@ -1,6 +1,6 @@
1
1
  import { n as e } from "./cliOutput-D1tSBoRM.js";
2
2
  import { i as t } from "./checkCommand-DC7k1_hP.js";
3
- import { i as n, n as r } from "./codemodRunner-kNnh1I7_.js";
3
+ import { i as n, n as r } from "./codemodRunner-ClUqkhvR.js";
4
4
  import { basename as i, join as a, resolve as o } from "node:path";
5
5
  import { branchNamespaceName as s, planBranchProvision as c } from "@voltro/database";
6
6
  import { readFile as l } from "node:fs/promises";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./commands-cu34ENUT.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./commands-Ro4hYBHD.js";
2
2
  import { r as o } from "./templates-BTWZkJJT.js";
3
3
  //#region src/index.ts
4
4
  var s = "framework";
@@ -1,5 +1,5 @@
1
1
  import { n as e, r as t, t as n } from "./cliOutput-D1tSBoRM.js";
2
- import { o as r } from "./updateCommand-Bw3mCUDT.js";
2
+ import { o as r } from "./updateCommand-D9avpQFT.js";
3
3
  import { join as i, resolve as a } from "node:path";
4
4
  import { readFileSync as o, readdirSync as s, statSync as c } from "node:fs";
5
5
  //#region src/infoCommand.ts
@@ -1,3 +1,3 @@
1
1
  import "./renderModeScan-43yQ2opo.js";
2
- import { t as e } from "./start-DNtIlBcn.js";
2
+ import { t as e } from "./start-CcdPRG8L.js";
3
3
  export { e as runStartCommand };