@voltro/cli 0.59.0 → 0.61.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 (96) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/apiBuild-5U7PxjLa.js +2 -0
  3. package/dist/{apiBuild-BU5cd-Fn.js → apiBuild-C5HhMAEm.js} +2 -2
  4. package/dist/bin.js +1 -1
  5. package/dist/{build-uRMZrT8z.js → build-DQr7SsKK.js} +21 -17
  6. package/dist/{checkCommand-C1CQc8lD.js → checkCommand-ByaOhdc8.js} +1 -1
  7. package/dist/{checkCommand-DC7k1_hP.js → checkCommand-CvSheSdb.js} +3 -3
  8. package/dist/{codegenCommand-kS3Twuc9.js → codegenCommand-CI0mpjxo.js} +1 -1
  9. package/dist/{codemodRunner-kNnh1I7_.js → codemodRunner-Drb-H-jr.js} +258 -159
  10. package/dist/{commands-cu34ENUT.js → commands-pCItjdHG.js} +21 -21
  11. package/dist/{dashboardCommand-gG1y4PFG.js → dashboardCommand-O-ToEgiH.js} +3 -3
  12. package/dist/{dataCommand-hNOGKz4K.js → dataCommand-mn1Zc88W.js} +1 -1
  13. package/dist/{dev-Ce2gEziv.js → dev-dSH-Pkb2.js} +1 -1
  14. package/dist/{dev-Dff_pdik.js → dev-kn9-7RXZ.js} +1 -1
  15. package/dist/doctorCommand-BVat2ejT.js +2 -0
  16. package/dist/{doctorCommand-CGjefPmk.js → doctorCommand-ClB7Ucz5.js} +13 -13
  17. package/dist/{dormancyCommand-DYgI0Kf-.js → dormancyCommand-DZupfuq-.js} +1 -1
  18. package/dist/{embeddingsCommand-yJivQSrI.js → embeddingsCommand-D8_GquVX.js} +1 -1
  19. package/dist/{envCommand-ORJUf8r_.js → envCommand-DRjVSXRp.js} +2 -2
  20. package/dist/{evolveCommand-8CuFBlbQ.js → evolveCommand-Cb0C2b3V.js} +2 -2
  21. package/dist/index.js +1 -1
  22. package/dist/{infoCommand-D02A4WI2.js → infoCommand-BlKnoY2D.js} +1 -1
  23. package/dist/{probeCommand-BUYzc0CT.js → probeCommand-DYsOFm62.js} +1 -1
  24. package/dist/{runtimeTrace-DDuKJgVi.js → runtimeTrace-BI3kdicc.js} +1 -1
  25. package/dist/{sdkgen-vq1fF9D_.js → sdkgen-DZ4zCtV4.js} +1 -1
  26. package/dist/serveCommand-CwK_6Wxt.js +2 -0
  27. package/dist/{serveCommand-Dr4KXmTS.js → serveCommand-DnxqCQ16.js} +3 -3
  28. package/dist/serveEntry.js +1 -1
  29. package/dist/{start-C75w6ftA.js → start-DE4ixCTi.js} +1 -1
  30. package/dist/{start-DNtIlBcn.js → start-bqT4GRH0.js} +286 -226
  31. package/dist/startEntry.js +1 -1
  32. package/dist/{updateCommand-Bw3mCUDT.js → updateCommand-B18ZQNFg.js} +1 -1
  33. package/dist/updateCommand-B4EgDD-m.js +2 -0
  34. package/dist/{webDev-CBN9KkyQ.js → webDev-CYjhmOTZ.js} +1 -1
  35. package/dist/{webDev-Bzb8ZFus.js → webDev-CuG3Ged-.js} +1 -1
  36. package/dist/{webhooksCommand-BPJQOGTe.js → webhooksCommand-Bs-HZ4wY.js} +1 -1
  37. package/package.json +37 -19
  38. package/templates/AGENTS.md +2 -2
  39. package/templates/agent-docs/_index.md +2 -2
  40. package/templates/agent-docs/_manifest.json +1 -1
  41. package/templates/agent-docs/data.md +10 -2
  42. package/templates/agent-docs/observability.md +7 -1
  43. package/templates/agent-docs/plugins/sentry.md +121 -17
  44. package/templates/agent-docs/schema-driven-ui.md +38 -10
  45. package/templates/agent-docs/whats-new.md +11 -41
  46. package/templates/apps/api-ai/package.json +6 -6
  47. package/templates/apps/api-auth/package.json +8 -8
  48. package/templates/apps/api-backend/package.json +7 -7
  49. package/templates/apps/api-backend-deactivation/package.json +7 -7
  50. package/templates/apps/api-backend-mail/package.json +8 -8
  51. package/templates/apps/api-backend-mariadb/package.json +9 -9
  52. package/templates/apps/api-backend-sqlite/package.json +8 -8
  53. package/templates/apps/api-backend-storage/package.json +8 -8
  54. package/templates/apps/api-cms/package.json +9 -9
  55. package/templates/apps/api-collab/package.json +8 -8
  56. package/templates/apps/api-data-advanced/package.json +8 -8
  57. package/templates/apps/api-durable/package.json +8 -8
  58. package/templates/apps/api-feature-flags/package.json +9 -9
  59. package/templates/apps/api-governance/package.json +8 -8
  60. package/templates/apps/api-kv/package.json +8 -8
  61. package/templates/apps/api-moderation/package.json +8 -8
  62. package/templates/apps/api-observability/package.json +8 -8
  63. package/templates/apps/api-ratelimit/package.json +8 -8
  64. package/templates/apps/api-rbac/package.json +8 -8
  65. package/templates/apps/api-rest/package.json +7 -7
  66. package/templates/apps/api-row-history/package.json +8 -8
  67. package/templates/apps/api-saas/package.json +11 -11
  68. package/templates/apps/api-saas-starter/package.json +10 -10
  69. package/templates/apps/api-search/package.json +8 -8
  70. package/templates/apps/api-status/package.json +8 -8
  71. package/templates/apps/api-webhooks/package.json +9 -9
  72. package/templates/apps/changelog/package.json +7 -7
  73. package/templates/apps/edge-functions/package.json +2 -2
  74. package/templates/apps/frontend-admin/package.json +7 -7
  75. package/templates/apps/frontend-app/package.json +8 -8
  76. package/templates/apps/frontend-auth/package.json +7 -7
  77. package/templates/apps/frontend-blank/package.json +6 -6
  78. package/templates/apps/frontend-cms/package.json +8 -8
  79. package/templates/apps/frontend-collab/package.json +9 -9
  80. package/templates/apps/frontend-contact/package.json +7 -7
  81. package/templates/apps/frontend-dashboard/package.json +6 -6
  82. package/templates/apps/frontend-docs/package.json +8 -8
  83. package/templates/apps/frontend-i18n/package.json +6 -6
  84. package/templates/apps/frontend-landing/package.json +7 -7
  85. package/templates/apps/frontend-portal/package.json +7 -7
  86. package/templates/apps/frontend-saas/package.json +7 -7
  87. package/templates/apps/frontend-spa/package.json +6 -6
  88. package/templates/apps/frontend-ssr/package.json +6 -6
  89. package/templates/apps/frontend-ssr-api/package.json +7 -7
  90. package/templates/apps/frontend-static-blog/package.json +8 -8
  91. package/templates/apps/frontend-status/package.json +7 -7
  92. package/templates/apps/mobile-app/package.json +4 -4
  93. package/dist/apiBuild-B9OvhITV.js +0 -2
  94. package/dist/doctorCommand-DXnNpKQT.js +0 -2
  95. package/dist/serveCommand-1R52mo6F.js +0 -2
  96. package/dist/updateCommand-inInBIAh.js +0 -2
@@ -3,8 +3,8 @@ import { t as d } from "./pageConvention-CMpfDN6r.js";
3
3
  import { r as f } from "./tsconfigPaths-BWXBWgcl.js";
4
4
  import { a as p, i as m, t as h } from "./fileTaxonomy-CbyMQYx_.js";
5
5
  import { basename as g, dirname as _, join as v, relative as ee } from "node:path";
6
- import { existsSync as y, readFileSync as te, statSync as ne } from "node:fs";
7
- import { execFileSync as b } from "node:child_process";
6
+ import { existsSync as y, readFileSync as b, statSync as te } from "node:fs";
7
+ import { execFileSync as ne } from "node:child_process";
8
8
  import { Node as x, Project as re, QuoteKind as ie, SyntaxKind as S } from "ts-morph";
9
9
  import { globSync as ae } from "tinyglobby";
10
10
  //#region src/codemodHelpers.ts
@@ -504,20 +504,20 @@ var C = (e, t, n) => {
504
504
  "read it now rather than waiting: `sourceKey` is almost always what a",
505
505
  "\"parent\" relation means."
506
506
  ].join("\n")
507
- }), Oe = "@voltro/plugin-atlassian", ke = "atlassianPlugin", O = (e) => {
507
+ }), Oe = "@voltro/plugin-atlassian", ke = "atlassianPlugin", Ae = (e) => {
508
508
  let t = [];
509
509
  for (let n of e.getImportDeclarations()) if (n.getModuleSpecifierValue() === Oe) for (let e of n.getNamedImports()) e.getName() === ke && t.push(e.getAliasNode()?.getText() ?? e.getName());
510
510
  return t;
511
- }, Ae = T({
511
+ }, je = T({
512
512
  version: "0.6.0",
513
513
  id: "0.6.0/05_atlassian-avatar-mode",
514
514
  title: "atlassianPlugin's `avatar` config requires an explicit `mode: 'service' | 'perUser'`",
515
515
  kind: "transform",
516
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => O(e).length > 0),
516
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ae(e).length > 0),
517
517
  transform: ({ project: e, log: t }) => {
518
518
  let n = 0;
519
519
  for (let t of e.getSourceFiles()) {
520
- let e = O(t);
520
+ let e = Ae(t);
521
521
  if (e.length !== 0) for (let r of t.getDescendantsOfKind(S.CallExpression)) {
522
522
  if (!e.includes(r.getExpression().getText())) continue;
523
523
  let t = r.getArguments()[0];
@@ -533,7 +533,7 @@ var C = (e, t, n) => {
533
533
  }
534
534
  n > 0 && (t(`atlassian avatar: added mode:'service' to ${n} config${n === 1 ? "" : "s"}`), t(" → to use the viewing user's own PAT instead, switch to mode:'perUser' and add resolveSubject"));
535
535
  }
536
- }), je = T({
536
+ }), Me = T({
537
537
  version: "0.6.0",
538
538
  id: "0.6.0/06_ui-strings-connect-account",
539
539
  title: "UiStrings gained a required `connectAccount` section",
@@ -577,14 +577,14 @@ var C = (e, t, n) => {
577
577
  "Alternatively, switch the annotation to `PartialUiStrings` if the object",
578
578
  "was only ever an override bag."
579
579
  ].join("\n")
580
- }), Me = E("setRowFilter"), Ne = /\bresolveRowFilterScope\b/, Pe = T({
580
+ }), Ne = E("setRowFilter"), Pe = /\bresolveRowFilterScope\b/, Fe = T({
581
581
  version: "0.7.0",
582
582
  id: "0.7.0/01_row-filter-load-failure",
583
583
  title: "A row filter that fails to load now errors instead of returning zero rows",
584
584
  kind: "manual",
585
585
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
586
586
  let t = e.getFullText();
587
- return Me.test(t) || Ne.test(t);
587
+ return Ne.test(t) || Pe.test(t);
588
588
  }),
589
589
  note: [
590
590
  "Row-level security: a `load` failure is now an ERROR, and is retried first.",
@@ -655,7 +655,7 @@ var C = (e, t, n) => {
655
655
  " \"every row you could see was just deleted\". Make sure your subscription",
656
656
  " error handling surfaces this rather than swallowing it."
657
657
  ].join("\n")
658
- }), Fe = T({
658
+ }), Ie = T({
659
659
  version: "0.7.0",
660
660
  id: "0.7.0/02_testing-invoke-effect-handlers",
661
661
  title: "@voltro/testing: invoke() runs Effect handlers; executor types widened",
@@ -710,14 +710,14 @@ var C = (e, t, n) => {
710
710
  "transaction, the deadlock replay, `afterCommit` and the plugin interceptor",
711
711
  "chain wrap an Effect handler exactly as they wrap an async one."
712
712
  ].join("\n")
713
- }), Ie = /\bpasswordHash\b/, Le = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, Re = T({
713
+ }), Le = /\bpasswordHash\b/, Re = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, ze = T({
714
714
  version: "0.8.0",
715
715
  id: "0.8.0/01_optional-password-hash",
716
716
  title: "`UserRecord.passwordHash` is optional — password-less identities are now first-class",
717
717
  kind: "manual",
718
718
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
719
719
  let t = e.getFullText();
720
- return Ie.test(t) || Le.test(t);
720
+ return Le.test(t) || Re.test(t);
721
721
  }),
722
722
  note: [
723
723
  "`UserRecord.passwordHash` is now `string | null | undefined`, not `string`.",
@@ -788,7 +788,7 @@ var C = (e, t, n) => {
788
788
  " to match and make sure your read path maps a NULL column to `null`/",
789
789
  " `undefined` rather than to `''`-by-coercion."
790
790
  ].join("\n")
791
- }), ze = T({
791
+ }), Be = T({
792
792
  version: "0.8.0",
793
793
  id: "0.8.0/02_ssr-streams-and-loaderdata",
794
794
  title: "`ssr` pages stream; `useLoaderData<T>()` returns `LoaderData<T>`",
@@ -840,13 +840,13 @@ var C = (e, t, n) => {
840
840
  "docs. `defer()` requires `renderMode: 'ssr'` and `interactive: 'full'`;",
841
841
  "every other combination is a hard error naming the page."
842
842
  ].join("\n")
843
- }), Be = /renderMode\s*[=:]\s*['"](spa|client)['"]/, Ve = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, He = T({
843
+ }), Ve = /renderMode\s*[=:]\s*['"](spa|client)['"]/, He = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, Ue = T({
844
844
  version: "0.9.0",
845
845
  id: "0.9.0/01_ssr-layout-shell-for-spa",
846
846
  title: "A `renderMode:'spa'` page under a layout now server-renders that layout",
847
847
  kind: "manual",
848
848
  appliesTo: ({ project: e }) => {
849
- let t = e.getSourceFiles(), n = t.some((e) => Be.test(e.getFullText())), r = t.some((e) => Ve.test(e.getFilePath()));
849
+ let t = e.getSourceFiles(), n = t.some((e) => Ve.test(e.getFullText())), r = t.some((e) => He.test(e.getFilePath()));
850
850
  return n && r;
851
851
  },
852
852
  note: [
@@ -885,35 +885,35 @@ var C = (e, t, n) => {
885
885
  "Nothing changes for `static` / `ssr` / `isr` pages, or for a spa page that",
886
886
  "has no layout (still a pure client mount)."
887
887
  ].join("\n")
888
- }), k = "client", Ue = "spa", We = (e) => {
888
+ }), O = "client", We = "spa", Ge = (e) => {
889
889
  if (!e) return null;
890
890
  let t = x.isAsExpression(e) ? e.getExpression() : e;
891
891
  return x.isStringLiteral(t) ? t : null;
892
- }, A = (e) => {
892
+ }, Ke = (e) => {
893
893
  let t = [];
894
894
  for (let n of e.getDescendantsOfKind(S.VariableDeclaration)) {
895
895
  if (n.getName() !== "renderMode") continue;
896
- let e = We(n.getInitializer());
896
+ let e = Ge(n.getInitializer());
897
897
  e && t.push(e);
898
898
  }
899
899
  for (let n of e.getDescendantsOfKind(S.PropertyAssignment)) {
900
900
  if (n.getName() !== "renderMode") continue;
901
- let e = We(n.getInitializer());
901
+ let e = Ge(n.getInitializer());
902
902
  e && t.push(e);
903
903
  }
904
904
  return t;
905
- }, Ge = T({
905
+ }, qe = T({
906
906
  version: "0.10.0",
907
907
  id: "0.10.0/01_render-mode-client-to-spa",
908
908
  title: "renderMode is a closed set — `'client'` becomes `'spa'`",
909
909
  kind: "transform",
910
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => A(e).some((e) => e.getLiteralValue() === k)),
910
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ke(e).some((e) => e.getLiteralValue() === O)),
911
911
  transform: ({ project: e, log: t }) => {
912
912
  let n = 0, r = /* @__PURE__ */ new Set();
913
- for (let t of e.getSourceFiles()) for (let e of A(t)) e.getLiteralValue() === k && (e.setLiteralValue(Ue), n++, r.add(t.getFilePath()));
914
- n > 0 && (t(`renderMode: rewrote ${n} \`'${k}'\` → \`'${Ue}'\` in ${r.size} file${r.size === 1 ? "" : "s"}`), t(" → 'spa' is the mode those pages already behaved as: no prerender, client-rendered"), t(" → new in 0.9.0: if such a page sits under a layout, that layout now server-renders (see the note above)"));
913
+ for (let t of e.getSourceFiles()) for (let e of Ke(t)) e.getLiteralValue() === O && (e.setLiteralValue(We), n++, r.add(t.getFilePath()));
914
+ n > 0 && (t(`renderMode: rewrote ${n} \`'${O}'\` → \`'${We}'\` in ${r.size} file${r.size === 1 ? "" : "s"}`), t(" → 'spa' is the mode those pages already behaved as: no prerender, client-rendered"), t(" → new in 0.9.0: if such a page sits under a layout, that layout now server-renders (see the note above)"));
915
915
  }
916
- }), Ke = T({
916
+ }), Je = T({
917
917
  version: "0.10.0",
918
918
  id: "0.10.0/02_router-state-carries-its-path",
919
919
  title: "`renderRouterStateScript` now requires the `path` it rendered",
@@ -950,7 +950,7 @@ var C = (e, t, n) => {
950
950
  "Trailing slashes, a trailing `/index.html`, and percent-encoding are normalised",
951
951
  "before the comparison, so the usual static-host shapes still hydrate."
952
952
  ].join("\n")
953
- }), qe = T({
953
+ }), Ye = T({
954
954
  version: "0.10.0",
955
955
  id: "0.10.0/03_workflow-caller-context-resolved",
956
956
  title: "workflow `buildContext` receives a RESOLVED caller context",
@@ -996,7 +996,7 @@ var C = (e, t, n) => {
996
996
  "it receives the resolved context too, so `callerContext?.subject ?? …` there",
997
997
  "becomes `callerContext.subject`."
998
998
  ].join("\n")
999
- }), Je = T({
999
+ }), Xe = T({
1000
1000
  version: "0.10.0",
1001
1001
  id: "0.10.0/04_seed-run-status-skipped",
1002
1002
  title: "seed run status gained `skipped`",
@@ -1037,7 +1037,7 @@ var C = (e, t, n) => {
1037
1037
  "`Schema.Literal`, a zod enum, a DB check constraint), widen it there too — an",
1038
1038
  "unlisted third state is a DECODE failure, not a rendering oddity."
1039
1039
  ].join("\n")
1040
- }), Ye = T({
1040
+ }), Ze = T({
1041
1041
  version: "0.10.0",
1042
1042
  id: "0.10.0/05_subscription-idle-state",
1043
1043
  title: "`useSubscription` gained an idle state for `skip`",
@@ -1089,7 +1089,7 @@ var C = (e, t, n) => {
1089
1089
  "time's data), hold it in your own state — the subscription no longer claims to",
1090
1090
  "have data it was told to stop fetching."
1091
1091
  ].join("\n")
1092
- }), Xe = T({
1092
+ }), Qe = T({
1093
1093
  version: "0.11.0",
1094
1094
  id: "0.11.0/01_step-options-type-params",
1095
1095
  title: "`StepOptions` takes the context type first",
@@ -1117,7 +1117,7 @@ var C = (e, t, n) => {
1117
1117
  "If the step reads nothing from earlier steps, `StepOptions<Record<never, never>,",
1118
1118
  "Result>` is the honest spelling."
1119
1119
  ].join("\n")
1120
- }), Ze = T({
1120
+ }), $e = T({
1121
1121
  version: "0.12.0",
1122
1122
  id: "0.12.0/01_outbound-http-ssrf-guard",
1123
1123
  title: "Outbound HttpClient blocks internal targets by default (add `http.allowHosts` if you call one)",
@@ -1157,7 +1157,7 @@ var C = (e, t, n) => {
1157
1157
  "private address (DNS rebinding) still passes. Pair with network-layer egress",
1158
1158
  "control if that is in your threat model."
1159
1159
  ].join("\n")
1160
- }), Qe = T({
1160
+ }), et = T({
1161
1161
  version: "0.12.0",
1162
1162
  id: "0.12.0/02_api-key-revoke-tenant-scope",
1163
1163
  title: "apiKeyService.revoke/rotate now require the caller's tenantId",
@@ -1186,7 +1186,7 @@ var C = (e, t, n) => {
1186
1186
  "The framework's own `/v1/api-keys/revoke` route already passes the caller's",
1187
1187
  "tenant — no change needed if you only use the shipped routes."
1188
1188
  ].join("\n")
1189
- }), $e = T({
1189
+ }), tt = T({
1190
1190
  version: "0.12.0",
1191
1191
  id: "0.12.0/03_api-key-on-behalf-of",
1192
1192
  title: "API keys: createdBy is provenance, onBehalfOf is ownership",
@@ -1220,7 +1220,7 @@ var C = (e, t, n) => {
1220
1220
  "specific one against what the body asks for. Grant them in your role map. A key",
1221
1221
  "can never carry scopes its issuer does not hold."
1222
1222
  ].join("\n")
1223
- }), et = T({
1223
+ }), nt = T({
1224
1224
  version: "0.12.0",
1225
1225
  id: "0.12.0/04_inspect-fail-closed",
1226
1226
  title: "The inspect surface requires VOLTRO_INSPECT_TOKEN (previously open when unset)",
@@ -1249,7 +1249,7 @@ var C = (e, t, n) => {
1249
1249
  "",
1250
1250
  "To turn the surface off entirely instead, set `VOLTRO_INSPECT=off`."
1251
1251
  ].join("\n")
1252
- }), tt = T({
1252
+ }), rt = T({
1253
1253
  version: "0.13.0",
1254
1254
  id: "0.13.0/01_bearer-gates-fail-closed",
1255
1255
  title: "checkBearer is fail-closed; scimPlugin refuses an empty token",
@@ -1286,7 +1286,7 @@ var C = (e, t, n) => {
1286
1286
  " • `@voltro/plugin-prometheus` already passes `{ openWhenUnset: true }` — its",
1287
1287
  " `PROMETHEUS_TOKEN` is documented as optional. Nothing to do there."
1288
1288
  ].join("\n")
1289
- }), nt = T({
1289
+ }), it = T({
1290
1290
  version: "0.13.0",
1291
1291
  id: "0.13.0/02_storage-grants-owner-only",
1292
1292
  title: "storage.share / revoke / listGrants require ownership",
@@ -1317,7 +1317,7 @@ var C = (e, t, n) => {
1317
1317
  " returning the grant or null. `storage.revoke` is handed a grant id, so it has",
1318
1318
  " to resolve the object before it can check the owner."
1319
1319
  ].join("\n")
1320
- }), rt = T({
1320
+ }), at = T({
1321
1321
  version: "0.13.0",
1322
1322
  id: "0.13.0/03_apply-plan-convergence-proof",
1323
1323
  title: "applyPlan requires a `replan` convergence proof",
@@ -1350,7 +1350,7 @@ var C = (e, t, n) => {
1350
1350
  " • `AppliedMigration` gained `appliedOps` (operations actually EXECUTED, from",
1351
1351
  " the per-op results). If you construct that value in a test fixture, add it."
1352
1352
  ].join("\n")
1353
- }), it = T({
1353
+ }), ot = T({
1354
1354
  version: "0.14.0",
1355
1355
  id: "0.14.0/01_resolve-scopes-store",
1356
1356
  title: "auth.resolveScopes receives the app DataStore",
@@ -1388,19 +1388,19 @@ var C = (e, t, n) => {
1388
1388
  " deliberately does not, because only you know how fast a role change must",
1389
1389
  " take effect."
1390
1390
  ].join("\n")
1391
- }), j = (e) => {
1391
+ }), k = (e) => {
1392
1392
  if (!x.isCallExpression(e)) return !1;
1393
1393
  let t = e.getExpression();
1394
1394
  return x.isPropertyAccessExpression(t) && t.getName() === "reactive" && e.getArguments().length === 0;
1395
- }, at = (e) => e.getDescendantsOfKind(S.CallExpression).some(j), ot = (e) => {
1395
+ }, st = (e) => e.getDescendantsOfKind(S.CallExpression).some(k), ct = (e) => {
1396
1396
  let t = 0, n = e.getDescendantsOfKind(S.CallExpression);
1397
1397
  for (let e of n) {
1398
- if (e.wasForgotten() || !j(e)) continue;
1398
+ if (e.wasForgotten() || !k(e)) continue;
1399
1399
  let n = e.getExpression();
1400
1400
  x.isPropertyAccessExpression(n) && (e.replaceWithText(n.getExpression().getText()), t++);
1401
1401
  }
1402
1402
  return t;
1403
- }, st = T({
1403
+ }, lt = T({
1404
1404
  version: "0.14.0",
1405
1405
  id: "0.14.0/02_reactive-by-default",
1406
1406
  title: "Reactivity is the default; .reactive() is removed",
@@ -1418,16 +1418,16 @@ var C = (e, t, n) => {
1418
1418
  "real saving: REPLICA IDENTITY FULL widens every UPDATE/DELETE in the WAL.",
1419
1419
  "`voltro dev` warns if you opt out a table a query still reads."
1420
1420
  ].join("\n"),
1421
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => at(e)),
1421
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => st(e)),
1422
1422
  transform: ({ project: e, log: t }) => {
1423
1423
  let n = 0, r = 0;
1424
1424
  for (let t of e.getSourceFiles()) {
1425
- let e = ot(t);
1425
+ let e = ct(t);
1426
1426
  e !== 0 && (n += e, r++);
1427
1427
  }
1428
1428
  return n > 0 && t(`removed ${n} .reactive() call(s) across ${r} file(s) — reactivity is the default`), n > 0;
1429
1429
  }
1430
- }), ct = T({
1430
+ }), ut = T({
1431
1431
  version: "0.14.0",
1432
1432
  id: "0.14.0/07_dev-remote-file-migrations",
1433
1433
  title: "voltro dev refuses unattended file migrations against a remote database",
@@ -1470,19 +1470,19 @@ var C = (e, t, n) => {
1470
1470
  "Unchanged: `voltro db files`, `voltro db apply` and `voltro serve`. This gates the",
1471
1471
  "AUTOMATIC boot path only; an explicit command is already an explicit decision."
1472
1472
  ].join("\n")
1473
- }), lt = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()], M = (e) => g(e).replace(/\.tsx?$/, ""), ut = (e, t, n) => {
1474
- let r = M(t), i = M(n);
1473
+ }), dt = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()], A = (e) => g(e).replace(/\.tsx?$/, ""), ft = (e, t, n) => {
1474
+ let r = A(t), i = A(n);
1475
1475
  if (r === i) return;
1476
1476
  let a = /\.tsx?$/.exec(e)?.[0] ?? "", o = a === "" ? e : e.slice(0, -a.length);
1477
1477
  if (!o.endsWith(`/${r}`) && o !== r) return;
1478
1478
  let s = a === "" ? "" : /\.tsx$/.test(n) ? ".tsx" : ".ts";
1479
1479
  return `${o.slice(0, o.length - r.length)}${i}${s}`;
1480
- }, N = (e, t) => {
1480
+ }, j = (e, t) => {
1481
1481
  let n = e.getFilePath(), r = [];
1482
- for (let i of e.getProject().getSourceFiles()) for (let e of lt(i)) {
1482
+ for (let i of e.getProject().getSourceFiles()) for (let e of dt(i)) {
1483
1483
  let i = e.getModuleSpecifierValue();
1484
1484
  if (i === void 0 || i.startsWith(".") || e.getModuleSpecifierSourceFile()?.getFilePath() !== n) continue;
1485
- let a = ut(i, n, t);
1485
+ let a = ft(i, n, t);
1486
1486
  a !== void 0 && r.push({
1487
1487
  decl: e,
1488
1488
  spec: a
@@ -1491,19 +1491,19 @@ var C = (e, t, n) => {
1491
1491
  e.move(t);
1492
1492
  for (let e of r) e.decl.setModuleSpecifier(e.spec);
1493
1493
  return r.length;
1494
- }, dt = (e) => {
1494
+ }, pt = (e) => {
1495
1495
  let t = e.getProject().getCompilerOptions().paths;
1496
1496
  if (t === void 0) return;
1497
- let n = M(e.getFilePath()), r = e.getFilePath().slice(0, e.getFilePath().lastIndexOf("/"));
1497
+ let n = A(e.getFilePath()), r = e.getFilePath().slice(0, e.getFilePath().lastIndexOf("/"));
1498
1498
  for (let [e, i] of Object.entries(t)) if (!e.endsWith("*")) for (let t of i ?? []) {
1499
1499
  if (!/\.tsx?$/.test(t)) continue;
1500
- let i = M(t), a = t.slice(0, t.lastIndexOf("/"));
1500
+ let i = A(t), a = t.slice(0, t.lastIndexOf("/"));
1501
1501
  if (i === n && r.endsWith(a.replace(/^\.\//, ""))) return e;
1502
1502
  }
1503
- }, ft = /\.page\.tsx$/, pt = (e) => {
1503
+ }, mt = /\.page\.tsx$/, M = (e) => {
1504
1504
  let t = e.includes("\\src\\pages\\") ? "\\src\\pages\\" : "/src/pages/", n = e.indexOf(t);
1505
1505
  return n === -1 ? null : e.slice(0, n);
1506
- }, mt = (e) => e.endsWith(".tsx") && !i.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !ft.test(e), ht = T({
1506
+ }, ht = (e) => e.endsWith(".tsx") && !i.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !mt.test(e), gt = T({
1507
1507
  version: "0.14.0",
1508
1508
  id: "0.14.0/03_pages-suffix",
1509
1509
  title: "Pages are declared with a .page.tsx suffix",
@@ -1518,8 +1518,8 @@ var C = (e, t, n) => {
1518
1518
  transform: ({ project: e, log: t }) => {
1519
1519
  let n = /* @__PURE__ */ new Map(), r = e.getSourceFiles().filter((t) => {
1520
1520
  let r = t.getFilePath();
1521
- if (!mt(g(r))) return !1;
1522
- let i = pt(r);
1521
+ if (!ht(g(r))) return !1;
1522
+ let i = M(r);
1523
1523
  if (i === null) return !1;
1524
1524
  let a = n.get(i);
1525
1525
  return a === void 0 && (a = e.getSourceFile(v(i, "app.config.ts")) !== void 0 || e.getSourceFile(v(i, "app.config.tsx")) !== void 0, n.set(i, a)), a;
@@ -1537,12 +1537,12 @@ var C = (e, t, n) => {
1537
1537
  let n = t.getFilePath(), r = g(n), s = _(n), c = r.slice(0, -4);
1538
1538
  for (let t of [".test.tsx", ".spec.tsx"]) {
1539
1539
  let n = v(s, `${c}${t}`), r = e.getSourceFile(n);
1540
- r !== void 0 && (N(r, v(s, `${c}.page${t}`)), o += 1);
1540
+ r !== void 0 && (j(r, v(s, `${c}.page${t}`)), o += 1);
1541
1541
  }
1542
- N(t, v(s, `${c}.page.tsx`));
1542
+ j(t, v(s, `${c}.page.tsx`));
1543
1543
  let l = i.get(n);
1544
1544
  if (l !== void 0 && l.length > 0 && !l.every((e) => d(g(e)))) {
1545
- let e = pt(n) ?? _(n), t = [...new Set(l)].map((t) => ee(e, t));
1545
+ let e = M(n) ?? _(n), t = [...new Set(l)].map((t) => ee(e, t));
1546
1546
  a.push(`${ee(e, n)} ← imported by ${t.join(", ")}`);
1547
1547
  }
1548
1548
  }
@@ -1551,7 +1551,7 @@ var C = (e, t, n) => {
1551
1551
  for (let e of a) t(` ${e}`);
1552
1552
  }
1553
1553
  }
1554
- }), gt = [
1554
+ }), _t = [
1555
1555
  /\.page\.tsx$/,
1556
1556
  c,
1557
1557
  s,
@@ -1562,10 +1562,10 @@ var C = (e, t, n) => {
1562
1562
  a,
1563
1563
  r,
1564
1564
  t
1565
- ], _t = (e) => {
1565
+ ], vt = (e) => {
1566
1566
  let t = g(e);
1567
- return !/\.tsx?$/.test(t) || i.has(t) || n(t) || t === "index.ts" || t === "index.tsx" || t === "app.config.ts" ? !1 : !gt.some((e) => e.test(t));
1568
- }, vt = (e) => {
1567
+ return !/\.tsx?$/.test(t) || i.has(t) || n(t) || t === "index.ts" || t === "index.tsx" || t === "app.config.ts" ? !1 : !_t.some((e) => e.test(t));
1568
+ }, yt = (e) => {
1569
1569
  let t = [], n = [], r = [], i = [];
1570
1570
  for (let [a, o] of e.getExportedDeclarations()) {
1571
1571
  let e = o[0];
@@ -1592,10 +1592,10 @@ var C = (e, t, n) => {
1592
1592
  others: r,
1593
1593
  types: i
1594
1594
  };
1595
- }, yt = (e, t) => {
1595
+ }, bt = (e, t) => {
1596
1596
  let { components: n, hooks: r, others: i, types: a } = e;
1597
1597
  return n.length + r.length + i.length === 0 ? a.length > 0 ? ".types" : null : t && h(n) && r.length === 0 && i.length === 0 ? ".component.tsx" : n.length === 0 && r.length === 1 && i.length === 0 ? ".hook" : null;
1598
- }, bt = T({
1598
+ }, xt = T({
1599
1599
  version: "0.14.0",
1600
1600
  id: "0.14.0/04_file-taxonomy",
1601
1601
  title: "Web files declare their contract (*.component.tsx / *.hook.ts / *.types.ts)",
@@ -1608,19 +1608,19 @@ var C = (e, t, n) => {
1608
1608
  if (s.length > 0) for (let e of s) t(` exempt (not ours): ${e.dir} — ${e.source}`);
1609
1609
  for (let t of e.getSourceFiles()) {
1610
1610
  let e = t.getFilePath();
1611
- if (!_t(e) || p(e, s)) continue;
1612
- let o = dt(t);
1611
+ if (!vt(e) || p(e, s)) continue;
1612
+ let o = pt(t);
1613
1613
  if (o !== void 0) {
1614
1614
  a.push(`${g(e)} (tsconfig paths: "${o}")`);
1615
1615
  continue;
1616
1616
  }
1617
- let c = e.endsWith(".tsx"), l = vt(t), u = yt(l, c);
1617
+ let c = e.endsWith(".tsx"), l = yt(t), u = bt(l, c);
1618
1618
  if (u === null) {
1619
1619
  l.components.length > 0 && l.hooks.length > 0 && r.push(`${g(e)} (component ${l.components.join("/")} + hook ${l.hooks.join("/")})`);
1620
1620
  continue;
1621
1621
  }
1622
1622
  let d = g(e).replace(/\.tsx?$/, ""), f = u === ".component.tsx" ? ".component.tsx" : `${u}${c ? ".tsx" : ".ts"}`;
1623
- i += N(t, v(_(e), `${d}${f}`)), n.push(`${d}${f}`);
1623
+ i += j(t, v(_(e), `${d}${f}`)), n.push(`${d}${f}`);
1624
1624
  }
1625
1625
  if (n.length > 0 && (t(`declared ${n.length} file(s): component / hook / types`), i > 0 && t(` rewrote ${i} aliased import specifier(s) (@/… and friends)`)), a.length > 0) {
1626
1626
  t(""), t(`left alone — a tsconfig \`paths\` entry names ${a.length} of these files directly:`);
@@ -1633,7 +1633,7 @@ var C = (e, t, n) => {
1633
1633
  r.length > 20 && t(` (+${r.length - 20} more)`);
1634
1634
  }
1635
1635
  }
1636
- }), xt = T({
1636
+ }), St = T({
1637
1637
  version: "0.14.0",
1638
1638
  id: "0.14.0/05_typed-tracking",
1639
1639
  title: "Tracking catalogues are typed against the component props",
@@ -1670,7 +1670,7 @@ var C = (e, t, n) => {
1670
1670
  "Read each one before changing it: a cast that no longer compiles is sometimes",
1671
1671
  "a real type disagreement the old signature was hiding."
1672
1672
  ].join("\n")
1673
- }), St = T({
1673
+ }), Ct = T({
1674
1674
  version: "0.14.0",
1675
1675
  id: "0.14.0/06_voltro-test-skips-e2e",
1676
1676
  title: "`voltro test` leaves `e2e/` to `voltro e2e`",
@@ -1692,13 +1692,13 @@ var C = (e, t, n) => {
1692
1692
  " code it tests, or",
1693
1693
  " • pass your own `--exclude`, which overrides the framework default outright."
1694
1694
  ].join("\n")
1695
- }), Ct = (e) => {
1695
+ }), wt = (e) => {
1696
1696
  let t = e.replace(/\.component(\.ui)?(\.tsx?)$/, "$2");
1697
1697
  return t === e ? null : n(t) ? t : null;
1698
- }, wt = (e) => {
1698
+ }, Tt = (e) => {
1699
1699
  for (let [, t] of e.getExportedDeclarations()) if (t[0] !== void 0) return !1;
1700
1700
  return e.getExportDeclarations().length === 0 && e.getExportAssignments().length === 0;
1701
- }, Tt = T({
1701
+ }, Et = T({
1702
1702
  version: "0.15.0",
1703
1703
  id: "0.15.0/01_undo-mistaken-taxonomy-renames",
1704
1704
  title: "Undo the two file renames 0.14.0 got wrong (framework primitives, side-effect modules)",
@@ -1715,14 +1715,14 @@ var C = (e, t, n) => {
1715
1715
  transform: ({ project: e, log: t }) => {
1716
1716
  let n = [], r = [];
1717
1717
  for (let t of e.getSourceFiles()) {
1718
- let e = t.getFilePath(), i = g(e), a = Ct(i);
1718
+ let e = t.getFilePath(), i = g(e), a = wt(i);
1719
1719
  if (a !== null) {
1720
- N(t, v(_(e), a)), n.push(`${i} → ${a}`);
1720
+ j(t, v(_(e), a)), n.push(`${i} → ${a}`);
1721
1721
  continue;
1722
1722
  }
1723
- if (/\.types\.tsx?$/.test(i) && wt(t)) {
1723
+ if (/\.types\.tsx?$/.test(i) && Tt(t)) {
1724
1724
  let n = i.replace(/\.types(\.tsx?)$/, "$1");
1725
- N(t, v(_(e), n)), r.push(`${i} → ${n}`);
1725
+ j(t, v(_(e), n)), r.push(`${i} → ${n}`);
1726
1726
  }
1727
1727
  }
1728
1728
  if (n.length > 0) {
@@ -1736,16 +1736,16 @@ var C = (e, t, n) => {
1736
1736
  }
1737
1737
  n.length === 0 && r.length === 0 && t("nothing to undo — 0.14.0 renamed neither a framework file nor a side-effect module here.");
1738
1738
  }
1739
- }), Et = ["@voltro/client", "@voltro/web"], P = "@voltro/protocol", Dt = T({
1739
+ }), N = ["@voltro/client", "@voltro/web"], P = "@voltro/protocol", Dt = T({
1740
1740
  version: "0.15.0",
1741
1741
  id: "0.15.0/02_error-tag-in-protocol",
1742
1742
  title: "`errorTag` is imported from @voltro/protocol",
1743
1743
  kind: "transform",
1744
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => Et.includes(e.getModuleSpecifierValue()) && e.getNamedImports().some((e) => e.getName() === "errorTag"))),
1744
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => N.includes(e.getModuleSpecifierValue()) && e.getNamedImports().some((e) => e.getName() === "errorTag"))),
1745
1745
  transform: ({ project: e, log: t }) => {
1746
1746
  let n = 0;
1747
1747
  for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
1748
- if (!Et.includes(e.getModuleSpecifierValue())) continue;
1748
+ if (!N.includes(e.getModuleSpecifierValue())) continue;
1749
1749
  let r = e.getNamedImports().find((e) => e.getName() === "errorTag");
1750
1750
  if (r === void 0) continue;
1751
1751
  let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
@@ -1802,7 +1802,7 @@ var C = (e, t, n) => {
1802
1802
  }
1803
1803
  }
1804
1804
  let l = [], u = 0;
1805
- for (let e of r) s.has(e.from) || (u += N(e.sf, e.to), e.isTest || l.push(o(e.to)));
1805
+ for (let e of r) s.has(e.from) || (u += j(e.sf, e.to), e.isTest || l.push(o(e.to)));
1806
1806
  if (l.length > 0 && (t(`moved ${l.length} route(s) into their own directory`), u > 0 && t(` rewrote ${u} aliased import specifier(s)`)), c.length > 0) {
1807
1807
  t(""), t(`LEFT ALONE — ${c.length} case(s) where two files claim one route:`);
1808
1808
  for (let e of c) t(` ${e}`);
@@ -6550,7 +6550,110 @@ 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) => /\bcostSource\b|\bCostBreakdown\b|\bestimateCostUsd\b|\b_voltro_ai_usage\b|\bMeteredUsage\b/.test(e.getFullText()), ma = T({
6554
+ version: "0.59.0",
6555
+ id: "0.59.0/02_costsource_gained_unpriced",
6556
+ title: "`costSource` gained `unpriced`, and AI usage is counted in a unit",
6557
+ kind: "manual",
6558
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(pa),
6559
+ note: [
6560
+ "AI COST NOW HAS A THIRD SOURCE, AND USAGE HAS A UNIT.",
6561
+ "",
6562
+ "WHAT CHANGED. `CostBreakdown[\"costSource\"]` gained `\"unpriced\"`, and",
6563
+ "`_voltro_ai_usage` gained a `unit` column. Speech vendors meter characters",
6564
+ "or seconds, never tokens, so a ledger that only knew tokens could not hold a",
6565
+ "speech call at all. A non-token unit with no supplied price is written with",
6566
+ "the REAL count and a zero cost, rather than a fabricated one.",
6567
+ "",
6568
+ "WHAT TO DO:",
6569
+ "",
6570
+ "1) IF YOU NARROWED `costSource` — a union of two members in your own type,",
6571
+ " or an exhaustive switch — add `\"unpriced\"`. The compiler points at it.",
6572
+ " Decide what it means for you: it is a real usage row whose price we do",
6573
+ " not know, not a free call.",
6574
+ "",
6575
+ "2) IF YOU READ `estimateCostUsd` POSITIONALLY, read it by name instead.",
6576
+ "",
6577
+ "3) CHECK YOUR USAGE QUERIES — this one does not fail, it answers wrongly.",
6578
+ " `sum(input_tokens)` over `_voltro_ai_usage` now adds character counts to",
6579
+ " token counts and returns a number that looks fine. Group by `unit`, and",
6580
+ " never sum across it."
6581
+ ].join("\n")
6582
+ }), ha = (e) => /\bsentryPlugin\b|@voltro\/plugin-sentry/.test(e.getFullText()), ga = T({
6583
+ version: "0.60.0",
6584
+ id: "0.60.0/01_declared_failures_are_not_incidents",
6585
+ title: "Sentry no longer reports declared failures by default",
6586
+ kind: "manual",
6587
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(ha),
6588
+ note: [
6589
+ "A DECLARED FAILURE IS NO LONGER A SENTRY ISSUE BY DEFAULT.",
6590
+ "",
6591
+ "WHAT CHANGED. The rpc interceptor skipped only clean interrupts; every",
6592
+ "other cause went to `captureException`, so a failure declared in a",
6593
+ "procedure's `error:` union arrived as `level: error`. It now skips a cause",
6594
+ "that is failures-ONLY. Defects are reported exactly as before, including a",
6595
+ "defect that travelled beside a failure.",
6596
+ "",
6597
+ "WHY. A declared failure is a contract: somebody wrote the outcome down, the",
6598
+ "client gets it typed and branches on it, and it describes a state of the",
6599
+ "world. `AccessDeniedError` on a team you are not in is the system working.",
6600
+ "A defect is the opposite — nobody foresaw it — and that is what Sentry is",
6601
+ "built for. Effect separates the two, this framework leans on that split",
6602
+ "deliberately, and a descriptor carries it in `error:`; reporting both as an",
6603
+ "incident discarded it one layer up. A deployment measured the result: its",
6604
+ "first server-side issue was a person clicking a team they are not in.",
6605
+ "",
6606
+ "THE BROWSER HALF MOVED WITH IT. A rejected call is an rpc error on the",
6607
+ "client too, so `initSentryBrowser` applies the same rule to `rpc.*` events",
6608
+ "that carry a `_tag`. Route render failures and `reportClientError` calls",
6609
+ "are never filtered — nobody declared those.",
6610
+ "",
6611
+ "WHAT TO DO — one of these, deliberately:",
6612
+ "",
6613
+ "1) NOTHING, if Sentry is where you look for the unforeseen. Your issue list",
6614
+ " gets quieter; the trace still shows the failure.",
6615
+ "",
6616
+ "2) KEEP THEM, if Sentry is where you see errors at all:",
6617
+ "",
6618
+ " sentryPlugin({ captureFailures: true })",
6619
+ " initSentryBrowser({ captureFailures: true })",
6620
+ "",
6621
+ "3) KEEP THE ONES THAT ARE SIGNAL:",
6622
+ "",
6623
+ " sentryPlugin({",
6624
+ " captureFailures: (e) => e._tag === 'PaymentDeclined',",
6625
+ " })",
6626
+ "",
6627
+ "The boot line now names the setting — `sentry active … captureFailures=false`",
6628
+ "— so the choice is visible where the decision takes effect."
6629
+ ].join("\n")
6630
+ }), _a = (e) => /\bRpcError(?:Bus|Listener|Kind)?\b/.test(e.getFullText()), va = T({
6631
+ version: "0.61.0",
6632
+ id: "0.61.0/01_rpc_errors_have_kinds",
6633
+ title: "`RpcError` now carries an explicit failure kind",
6634
+ kind: "manual",
6635
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(_a),
6636
+ note: [
6637
+ "`RpcError` NOW REQUIRES `kind`.",
6638
+ "",
6639
+ "Events received from `useOnRpcError` are already populated. Branch on",
6640
+ "`event.kind === 'transport'` for connection-level UX instead of reading",
6641
+ "`useConnectionStatus` in the same callback or matching an error message.",
6642
+ "",
6643
+ "If your own test/helper calls `RpcErrorBus.emit(...)`, add the origin you",
6644
+ "can actually prove:",
6645
+ "",
6646
+ " bus.emit({ source: 'mutation', tag, error, kind: 'handler' })",
6647
+ "",
6648
+ "The values are 'transport' | 'handler' | 'client' | 'unknown'. Use",
6649
+ "'unknown' when the operation combines several stages and no single origin",
6650
+ "is provable. Do not infer whether the server ran from a transport failure:",
6651
+ "the request may have completed and only its response was lost.",
6652
+ "",
6653
+ "`traceId` identifies the client call span. It joins to server logs only",
6654
+ "when the server accepted the request; its presence is not delivery proof."
6655
+ ].join("\n")
6656
+ }), ya = (e) => /@voltro\/plugin-sentry(?:\/web)?|\bsentry\s*:/.test(e.getFullText()), J = [
6554
6657
  he,
6555
6658
  ge,
6556
6659
  ve,
@@ -6560,15 +6663,13 @@ var C = (e, t, n) => {
6560
6663
  Te,
6561
6664
  Ee,
6562
6665
  De,
6563
- Ae,
6564
6666
  je,
6565
- Pe,
6667
+ Me,
6566
6668
  Fe,
6567
- Re,
6669
+ Ie,
6568
6670
  ze,
6569
- He,
6570
- Ge,
6571
- Ke,
6671
+ Be,
6672
+ Ue,
6572
6673
  qe,
6573
6674
  Je,
6574
6675
  Ye,
@@ -6580,14 +6681,16 @@ var C = (e, t, n) => {
6580
6681
  tt,
6581
6682
  nt,
6582
6683
  rt,
6583
- ct,
6584
6684
  it,
6585
- st,
6586
- ht,
6587
- bt,
6685
+ at,
6686
+ ut,
6687
+ ot,
6688
+ lt,
6689
+ gt,
6588
6690
  xt,
6589
6691
  St,
6590
- Tt,
6692
+ Ct,
6693
+ Et,
6591
6694
  Dt,
6592
6695
  Mt,
6593
6696
  Pt,
@@ -6694,37 +6797,33 @@ var C = (e, t, n) => {
6694
6797
  oa,
6695
6798
  ca,
6696
6799
  fa,
6800
+ ga,
6801
+ ma,
6802
+ va,
6697
6803
  T({
6698
- version: "0.59.0",
6699
- id: "0.59.0/02_costsource_gained_unpriced",
6700
- title: "`costSource` gained `unpriced`, and AI usage is counted in a unit",
6804
+ version: "0.61.0",
6805
+ id: "0.61.0/02_sentry_rpc_spans_join_page",
6806
+ title: "Sentry rpc spans now join the active browser transaction",
6701
6807
  kind: "manual",
6702
- appliesTo: ({ project: e }) => e.getSourceFiles().some(pa),
6808
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(ya),
6703
6809
  note: [
6704
- "AI COST NOW HAS A THIRD SOURCE, AND USAGE HAS A UNIT.",
6705
- "",
6706
- "WHAT CHANGED. `CostBreakdown[\"costSource\"]` gained `\"unpriced\"`, and",
6707
- "`_voltro_ai_usage` gained a `unit` column. Speech vendors meter characters",
6708
- "or seconds, never tokens, so a ledger that only knew tokens could not hold a",
6709
- "speech call at all. A non-token unit with no supplied price is written with",
6710
- "the REAL count and a zero cost, rather than a fabricated one.",
6711
- "",
6712
- "WHAT TO DO:",
6810
+ "Browser rpc markers are now children of the active page/navigation",
6811
+ "transaction, with a span link to the Effect/server trace. They no longer",
6812
+ "create one root Sentry transaction and envelope per rpc call.",
6713
6813
  "",
6714
- "1) IF YOU NARROWED `costSource` a union of two members in your own type,",
6715
- " or an exhaustive switch add `\"unpriced\"`. The compiler points at it.",
6716
- " Decide what it means for you: it is a real usage row whose price we do",
6717
- " not know, not a free call.",
6814
+ "`tracesSampleRate` now controls the page transaction and its rpc children.",
6815
+ "`browserTracing: false` also defaults the rpc bridge to off while browser",
6816
+ "error capture stays active. Set `rpcSpans: true` only if your app creates",
6817
+ "its own active Sentry transactions, or set `rpcSpans: false` to keep page",
6818
+ "performance traces without rpc child spans.",
6718
6819
  "",
6719
- "2) IF YOU READ `estimateCostUsd` POSITIONALLY, read it by name instead.",
6720
- "",
6721
- "3) CHECK YOUR USAGE QUERIES this one does not fail, it answers wrongly.",
6722
- " `sum(input_tokens)` over `_voltro_ai_usage` now adds character counts to",
6723
- " token counts and returns a number that looks fine. Group by `unit`, and",
6724
- " never sum across it."
6820
+ "`sentryTraceHeader` was removed. Delete direct imports and use the normal",
6821
+ "`initSentryBrowser` bridge. Custom instrumentation should create a",
6822
+ "parent-only child of Sentry's active transaction and attach the Effect",
6823
+ "trace context as a span link."
6725
6824
  ].join("\n")
6726
6825
  })
6727
- ], J = /* @__PURE__ */ new Set([
6826
+ ], Y = /* @__PURE__ */ new Set([
6728
6827
  "node_modules",
6729
6828
  ".git",
6730
6829
  "dist",
@@ -6736,42 +6835,42 @@ var C = (e, t, n) => {
6736
6835
  ".turbo",
6737
6836
  ".cache",
6738
6837
  ".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) => {
6838
+ ]), ba = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, xa = /\.(?:tsx?|jsx?|mjs|cjs)$/, Sa = (e) => g(e).replace(xa, ""), Ca = (e) => {
6740
6839
  let t = e.split("/").filter((e) => e.length > 0).pop();
6741
6840
  if (t === void 0 || t === "." || t === "..") return;
6742
- let n = t.replace(X, "");
6841
+ let n = t.replace(xa, "");
6743
6842
  return n.length > 0 ? n : void 0;
6744
- }, va = (e) => {
6843
+ }, wa = (e) => {
6745
6844
  let t = /* @__PURE__ */ new Map();
6746
6845
  for (let n of e) {
6747
6846
  let e;
6748
6847
  try {
6749
- e = te(n, "utf8");
6848
+ e = b(n, "utf8");
6750
6849
  } catch {
6751
6850
  continue;
6752
6851
  }
6753
- Y.lastIndex = 0;
6754
- for (let r = Y.exec(e); r !== null; r = Y.exec(e)) {
6755
- let e = _a(r[1] ?? r[2] ?? "");
6852
+ X.lastIndex = 0;
6853
+ for (let r = X.exec(e); r !== null; r = X.exec(e)) {
6854
+ let e = Ca(r[1] ?? r[2] ?? "");
6756
6855
  if (e === void 0) continue;
6757
6856
  let i = t.get(e);
6758
6857
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
6759
6858
  }
6760
6859
  }
6761
6860
  return t;
6762
- }, ya = (e, t) => {
6861
+ }, Ta = (e, t) => {
6763
6862
  let n = new Set(e);
6764
- for (let r of e) for (let e of [ga(r), g(_(r))]) {
6863
+ for (let r of e) for (let e of [Sa(r), g(_(r))]) {
6765
6864
  let r = t.get(e);
6766
6865
  if (r !== void 0) for (let e of r) n.add(e);
6767
6866
  }
6768
6867
  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) => {
6868
+ }, Ea = /* @__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(","), Da = 2097152, Oa = 4e4, ka = (e) => {
6770
6869
  let t = e.slice(e.lastIndexOf("/") + 1);
6771
- return ba.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6772
- }, wa = (e) => {
6870
+ return Ea.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
6871
+ }, Aa = (e) => {
6773
6872
  try {
6774
- let t = b("git", [
6873
+ let t = ne("git", [
6775
6874
  "ls-files",
6776
6875
  "--cached",
6777
6876
  "--others",
@@ -6790,23 +6889,23 @@ var C = (e, t, n) => {
6790
6889
  } catch {
6791
6890
  return;
6792
6891
  }
6793
- }, Z = (e, t = wa) => {
6892
+ }, Z = (e, t = Aa) => {
6794
6893
  let n = t(e);
6795
- if (n === void 0) return Ta;
6894
+ if (n === void 0) return ja;
6796
6895
  let r = /* @__PURE__ */ new Map(), i = [];
6797
6896
  for (let t of n) {
6798
- if (t.split("/").some((e) => J.has(e)) || !Ca(t)) continue;
6799
- if (r.size >= Sa) {
6897
+ if (t.split("/").some((e) => Y.has(e)) || !ka(t)) continue;
6898
+ if (r.size >= Oa) {
6800
6899
  i.push(t);
6801
6900
  continue;
6802
6901
  }
6803
6902
  let n = v(e, t);
6804
6903
  try {
6805
- if (ne(n).size > xa) {
6904
+ if (te(n).size > Da) {
6806
6905
  i.push(t);
6807
6906
  continue;
6808
6907
  }
6809
- let e = te(n, "utf8");
6908
+ let e = b(n, "utf8");
6810
6909
  if (e.includes("\0")) {
6811
6910
  i.push(t);
6812
6911
  continue;
@@ -6826,47 +6925,47 @@ var C = (e, t, n) => {
6826
6925
  scanned: r.size,
6827
6926
  skipped: i
6828
6927
  };
6829
- }, Ta = {
6928
+ }, ja = {
6830
6929
  find: () => [],
6831
6930
  scanned: 0,
6832
6931
  skipped: []
6833
- }, Ea = [
6932
+ }, Ma = [
6834
6933
  "**/*.ts",
6835
6934
  "**/*.tsx",
6836
6935
  "!**/node_modules/**",
6837
6936
  "!**/dist/**",
6838
6937
  "!**/.framework/**",
6839
6938
  "!**/*.d.ts"
6840
- ], Q = (e, t, n = ka) => {
6939
+ ], Q = (e, t, n = Fa) => {
6841
6940
  let r = /* @__PURE__ */ new Set();
6842
- for (let e of t) for (let t of e.scope ?? Ea) r.add(t);
6941
+ for (let e of t) for (let t of e.scope ?? Ma) r.add(t);
6843
6942
  let i = n(e);
6844
6943
  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 = [];
6944
+ let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Pa(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Pa(e.slice(1))), a = [...Y], o = [];
6846
6945
  for (let r of i) {
6847
6946
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
6848
6947
  let i = v(e, r);
6849
6948
  y(i) && o.push(i);
6850
6949
  }
6851
- return Da(o.length, e), o;
6950
+ return Na(o.length, e), o;
6852
6951
  }
6853
6952
  let a = ae([...r], {
6854
6953
  cwd: e,
6855
6954
  absolute: !0,
6856
6955
  expandDirectories: !1,
6857
6956
  followSymbolicLinks: !1,
6858
- ignore: [...ha()]
6957
+ ignore: [...ba()]
6859
6958
  });
6860
- return Da(a.length, e), a;
6861
- }, Da = (e, t) => {
6862
- let n = Aa();
6959
+ return Na(a.length, e), a;
6960
+ }, Na = (e, t) => {
6961
+ let n = Ia();
6863
6962
  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) => {
6963
+ }, Pa = (e) => {
6865
6964
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
6866
6965
  return (e) => RegExp(`^${t}$`).test(e);
6867
- }, ka = (e) => {
6966
+ }, Fa = (e) => {
6868
6967
  try {
6869
- let t = b("git", [
6968
+ let t = ne("git", [
6870
6969
  "ls-files",
6871
6970
  "--cached",
6872
6971
  "--others",
@@ -6885,7 +6984,7 @@ var C = (e, t, n) => {
6885
6984
  } catch {
6886
6985
  return;
6887
6986
  }
6888
- }, Aa = () => {
6987
+ }, Ia = () => {
6889
6988
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
6890
6989
  return Number.isInteger(e) && e > 0 ? e : 2e4;
6891
6990
  }, $ = (e, t) => {
@@ -6895,7 +6994,7 @@ var C = (e, t, n) => {
6895
6994
  if (t !== 0) return t;
6896
6995
  }
6897
6996
  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) => {
6997
+ }, La = (e, t, n = J) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), Ra = (e, t, n = () => {}, r = ja) => {
6899
6998
  let i = [], a = [], o = [];
6900
6999
  for (let s of t) {
6901
7000
  let t = !s.appliesTo || s.appliesTo({
@@ -6946,8 +7045,8 @@ var C = (e, t, n) => {
6946
7045
  manual: a,
6947
7046
  skipped: o
6948
7047
  };
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 : (() => {
7048
+ }, za = async (e) => {
7049
+ let t = e.log ?? (() => {}), n = La(e.from, e.to, e.codemods ?? J), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
6951
7050
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
6952
7051
  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
7052
  return t;
@@ -6974,15 +7073,15 @@ var C = (e, t, n) => {
6974
7073
  get skipped() {
6975
7074
  return (p ??= Z(e.root)).skipped;
6976
7075
  }
6977
- }, h = c.length > 0 ? va(Q(e.root, [{
7076
+ }, h = c.length > 0 ? wa(Q(e.root, [{
6978
7077
  ...c[0],
6979
- scope: Ea
7078
+ scope: Ma
6980
7079
  }])) : void 0;
6981
7080
  for (let n of r) {
6982
7081
  if (c.includes(n)) {
6983
7082
  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);
7083
+ for (let e of Ta(i, h)) y(e) && r.addSourceFileAtPath(e);
7084
+ let s = Ra(r, [n], t, m);
6986
7085
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
6987
7086
  continue;
6988
7087
  }
@@ -6991,7 +7090,7 @@ var C = (e, t, n) => {
6991
7090
  for (let t of s) y(t) && e.addSourceFileAtPath(t);
6992
7091
  return e;
6993
7092
  })();
6994
- let r = Ma(o, [n], t, m);
7093
+ let r = Ra(o, [n], t, m);
6995
7094
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
6996
7095
  }
6997
7096
  return {
@@ -7001,4 +7100,4 @@ var C = (e, t, n) => {
7001
7100
  };
7002
7101
  };
7003
7102
  //#endregion
7004
- export { T as i, Na as n, J as r, $ as t };
7103
+ export { T as i, za as n, Y as r, $ as t };