@voltro/cli 0.38.0 → 0.39.1

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 (173) hide show
  1. package/CHANGELOG.md +276 -0
  2. package/THIRD-PARTY-NOTICES.md +80 -45
  3. package/dist/{addCommand-BNeoeSxe.js → addCommand-C05L9tZh.js} +2 -2
  4. package/dist/addCommand-C3MdJG-a.js +2 -0
  5. package/dist/{agentsMd-mhQMF1bx.js → agentsMd-7zI2h5l9.js} +3 -3
  6. package/dist/agentsMd-BFCXh2gl.js +2 -0
  7. package/dist/apiBuild-BmR_bIxN.js +2 -0
  8. package/dist/{apiBuild-CvacV4zA.js → apiBuild-D9FB13_2.js} +3 -3
  9. package/dist/baselineCommand-CXv680Dc.js +2 -0
  10. package/dist/{baselineCommand-C6NMt-oa.js → baselineCommand-Ck2Xm8M7.js} +18 -18
  11. package/dist/bin.js +1 -1
  12. package/dist/{build-_LoTC1s0.js → build-DORV5mLM.js} +107 -100
  13. package/dist/{capabilitiesCommand-nq_pz5xd.js → capabilitiesCommand-D5gucwYa.js} +8 -8
  14. package/dist/{checkCommand-Pjk5sBl2.js → checkCommand-Bf6oMao4.js} +35 -35
  15. package/dist/{checkCommand-wibtQx-N.js → checkCommand-D9qK_gtA.js} +1 -1
  16. package/dist/{cloudCmd-NQSwe_Qk.js → cloudCmd-C42gaO8s.js} +16 -16
  17. package/dist/{clusterCmd-D5wsCmA_.js → clusterCmd-DrVFCzSj.js} +8 -4
  18. package/dist/{codegen-GYYcdpCg.js → codegen-CbpGCWLG.js} +1 -1
  19. package/dist/codegen-i8QGcHsi.js +2 -0
  20. package/dist/codegenCommand-CxOyKmc1.js +129 -0
  21. package/dist/{codemodRunner-CXgQ-ecJ.js → codemodRunner-D-jTyvWo.js} +622 -551
  22. package/dist/{commands-DaZVi9wi.js → commands-D6aW_6AR.js} +48 -48
  23. package/dist/{connectionConfig-UFlIEiys.js → connectionConfig-Bk9IC7D0.js} +1 -1
  24. package/dist/{dashboardCommand-Dxy_kmoW.js → dashboardCommand-CwTZh-XZ.js} +3 -3
  25. package/dist/{dataCommand-D6-Ha2lf.js → dataCommand-Cc9RiLQZ.js} +14 -11
  26. package/dist/{dbCommand-C4UW8cVN.js → dbCommand-DSwGv9wS.js} +50 -49
  27. package/dist/dbCommand-DcqEyxju.js +2 -0
  28. package/dist/{dev-E7Eo5GHc.js → dev-3mgc8c-H.js} +2260 -2184
  29. package/dist/dev-CjJAasDs.js +3 -0
  30. package/dist/{dialectDriver-CgXnDfec.js → dialectDriver-czCHYpeH.js} +2 -1
  31. package/dist/doctorCommand-Cghssndo.js +2 -0
  32. package/dist/{doctorCommand-gmDyALFL.js → doctorCommand-oLuzc-aC.js} +261 -244
  33. package/dist/{dormancyCommand-CE6wG8lj.js → dormancyCommand-Bxe4WtHU.js} +1 -1
  34. package/dist/{e2eCmd-BRabZww-.js → e2eCmd-uFHig1hV.js} +8 -2
  35. package/dist/{embeddingsCommand-C3uAuf6N.js → embeddingsCommand-CyNQkFIc.js} +6 -6
  36. package/dist/{envCommand-DzuWh8ya.js → envCommand-CAkSgBbh.js} +19 -19
  37. package/dist/{evolveCommand-DPLqcYb2.js → evolveCommand-CWhBoe_P.js} +6 -8
  38. package/dist/frameworkTableAssembly-DgylYh9_.js +2 -0
  39. package/dist/{generateCommand-DbgcUpGw.js → generateCommand-CcyvH2ve.js} +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/{infoCommand-CmczGj_l.js → infoCommand-CTo8Jnhq.js} +7 -7
  42. package/dist/inspect-B7U7Cl_Z.js +1192 -0
  43. package/dist/inspect-DUze25t0.js +2 -0
  44. package/dist/{inspectCmd-EHFZ9yYu.js → inspectCmd-niF97fAq.js} +5 -5
  45. package/dist/inspectGateHint-BjnFubmH.js +7 -0
  46. package/dist/{logFileSink-C_D2wRN1.js → logFileSink-B4uP8pvf.js} +1 -1
  47. package/dist/{logsCmd-D36xK7Zu.js → logsCmd-B6oNsfaZ.js} +10 -7
  48. package/dist/{manifestBuild-hpPLaGxV.js → manifestBuild-BK42hu0k.js} +1 -1
  49. package/dist/manifestBuild-j0n109tt.js +2 -0
  50. package/dist/{metaCommands-CIoQNlaQ.js → metaCommands-DUYR--Ts.js} +1 -1
  51. package/dist/{migrate-RwgUWXfc.js → migrate-BnPw2zC8.js} +7 -4
  52. package/dist/{packageCommand-Cug_3Ogl.js → packageCommand-9SVyvsSo.js} +3 -2
  53. package/dist/{probeCommand-BPEpwT32.js → probeCommand-C9gazU0H.js} +35 -24
  54. package/dist/{projectScaffold-CxgtJvlb.js → projectScaffold-BEhhHjPr.js} +1 -1
  55. package/dist/{projectScaffold-DDgWbNLe.js → projectScaffold-BvhLOrLq.js} +17 -15
  56. package/dist/{runtimeTrace-6jfznSzr.js → runtimeTrace-gNXeLf6H.js} +1 -1
  57. package/dist/{scheduleManifestCmd-D2x0CTTY.js → scheduleManifestCmd-kmWzrO_w.js} +6 -9
  58. package/dist/{sdkgen-CEJZZQUA.js → sdkgen-C8zz10-X.js} +4 -4
  59. package/dist/{seedRunner-ZmLSqNe2.js → seedRunner-IdHEprqf.js} +1 -4
  60. package/dist/serveCommand-BpK8-m1N.js +1770 -0
  61. package/dist/serveCommand-CW-goXvh.js +2 -0
  62. package/dist/serveEntry.js +2 -2
  63. package/dist/{serverlessCommand-CfJZy6dS.js → serverlessCommand-DbJ7Plg9.js} +3 -3
  64. package/dist/{start-CidwCcjl.js → start-BNuTWdRd.js} +1 -1
  65. package/dist/{start-RCh6qKFe.js → start-ft_KzFTd.js} +257 -254
  66. package/dist/startEntry.js +1 -1
  67. package/dist/{test-DIQ0jlkQ.js → test-CLYXrZ2F.js} +1 -1
  68. package/dist/{tracesCmd-DStmCJPi.js → tracesCmd-DgtgOUdi.js} +7 -4
  69. package/dist/{typecheckCommand-BlsWiCNq.js → typecheckCommand-BuCtT92o.js} +7 -7
  70. package/dist/updateCommand-6FMU2klq.js +2 -0
  71. package/dist/{updateCommand-CmksX7m_.js → updateCommand-CHBmCB17.js} +5 -3
  72. package/dist/{webDev-BKdD7c_a.js → webDev-CTpSY-e_.js} +813 -775
  73. package/dist/webDev-id3I5PvG.js +2 -0
  74. package/dist/{webhookDiscovery-D7VaeMlz.js → webhookDiscovery-CphsQe59.js} +4 -6
  75. package/dist/{webhookDiscovery-CrGAfhIG.js → webhookDiscovery-il9ti-HE.js} +1 -1
  76. package/dist/{webhooksCommand-BHeaF1ZJ.js → webhooksCommand-EccPZQZ4.js} +1 -1
  77. package/package.json +27 -21
  78. package/templates/AGENTS.md +2 -2
  79. package/templates/agent-docs/_index.md +2 -2
  80. package/templates/agent-docs/_manifest.json +2 -2
  81. package/templates/agent-docs/authentication.md +43 -4
  82. package/templates/agent-docs/cli.md +17 -13
  83. package/templates/agent-docs/data.md +44 -1
  84. package/templates/agent-docs/database/migrations.md +2 -2
  85. package/templates/agent-docs/database/seedsdialects.md +2 -2
  86. package/templates/agent-docs/deployment.md +21 -7
  87. package/templates/agent-docs/internationalization.md +10 -3
  88. package/templates/agent-docs/local-first-mobile.md +141 -20
  89. package/templates/agent-docs/plugins/mail.md +1 -1
  90. package/templates/agent-docs/plugins/ratelimit.md +1 -1
  91. package/templates/agent-docs/plugins.md +3 -1
  92. package/templates/agent-docs/releases.md +130 -1
  93. package/templates/agent-docs/routing.md +34 -2
  94. package/templates/agent-docs/templates/apibackends.md +1 -1
  95. package/templates/agent-docs/templates/mobile.md +1 -1
  96. package/templates/agent-docs/whats-new.md +26 -66
  97. package/templates/apps/api-ai/package.json +7 -7
  98. package/templates/apps/api-auth/package.json +8 -8
  99. package/templates/apps/api-backend/package.json +7 -7
  100. package/templates/apps/api-backend-deactivation/package.json +7 -7
  101. package/templates/apps/api-backend-mail/package.json +8 -8
  102. package/templates/apps/api-backend-mariadb/package.json +9 -9
  103. package/templates/apps/api-backend-sqlite/package.json +8 -8
  104. package/templates/apps/api-backend-storage/package.json +8 -8
  105. package/templates/apps/api-cms/package.json +10 -10
  106. package/templates/apps/api-collab/package.json +8 -8
  107. package/templates/apps/api-data-advanced/package.json +8 -8
  108. package/templates/apps/api-durable/package.json +8 -8
  109. package/templates/apps/api-durable/startup/warm.startup.tsx +16 -2
  110. package/templates/apps/api-feature-flags/package.json +9 -9
  111. package/templates/apps/api-governance/package.json +8 -8
  112. package/templates/apps/api-kv/package.json +8 -8
  113. package/templates/apps/api-moderation/package.json +8 -8
  114. package/templates/apps/api-observability/package.json +8 -8
  115. package/templates/apps/api-ratelimit/package.json +8 -8
  116. package/templates/apps/api-rbac/authz.ts +16 -1
  117. package/templates/apps/api-rbac/package.json +8 -8
  118. package/templates/apps/api-rest/package.json +7 -7
  119. package/templates/apps/api-saas/package.json +11 -11
  120. package/templates/apps/api-saas-starter/package.json +10 -10
  121. package/templates/apps/api-search/package.json +8 -8
  122. package/templates/apps/api-status/package.json +8 -8
  123. package/templates/apps/api-versioning/package.json +8 -8
  124. package/templates/apps/api-webhooks/package.json +9 -9
  125. package/templates/apps/changelog/package.json +6 -6
  126. package/templates/apps/edge-functions/package.json +2 -2
  127. package/templates/apps/frontend-admin/package.json +8 -8
  128. package/templates/apps/frontend-app/package.json +9 -9
  129. package/templates/apps/frontend-auth/package.json +8 -8
  130. package/templates/apps/frontend-blank/package.json +7 -7
  131. package/templates/apps/frontend-cms/package.json +9 -9
  132. package/templates/apps/frontend-collab/package.json +10 -10
  133. package/templates/apps/frontend-contact/package.json +7 -7
  134. package/templates/apps/frontend-dashboard/package.json +7 -7
  135. package/templates/apps/frontend-docs/package.json +7 -7
  136. package/templates/apps/frontend-i18n/package.json +6 -6
  137. package/templates/apps/frontend-landing/package.json +7 -7
  138. package/templates/apps/frontend-portal/package.json +8 -8
  139. package/templates/apps/frontend-saas/package.json +8 -8
  140. package/templates/apps/frontend-spa/package.json +7 -7
  141. package/templates/apps/frontend-ssr/package.json +7 -7
  142. package/templates/apps/frontend-ssr-api/package.json +8 -8
  143. package/templates/apps/frontend-static-blog/package.json +6 -6
  144. package/templates/apps/frontend-status/package.json +8 -8
  145. package/templates/apps/mobile-app/README.md +41 -17
  146. package/templates/apps/mobile-app/app.config.ts +1 -1
  147. package/templates/apps/mobile-app/package.json +15 -11
  148. package/templates/apps/mobile-app/src/app/_layout.tsx +44 -13
  149. package/templates/apps/mobile-app/src/app/index.tsx +1 -1
  150. package/templates/apps/mobile-app/src/app/settings.tsx +4 -1
  151. package/templates/apps/mobile-app/src/client.ts +74 -57
  152. package/templates/apps/mobile-app/src/lib/api.ts +4 -4
  153. package/templates/apps/mobile-app/src/lib/deeplinks.ts +6 -6
  154. package/templates/apps/mobile-app/src/persistence.ts +24 -3
  155. package/templates/apps/mobile-app/tsconfig.json +17 -3
  156. package/dist/addCommand-aXSQveak.js +0 -2
  157. package/dist/agentsMd-BTchIZku.js +0 -2
  158. package/dist/apiBuild-ttSDDOGp.js +0 -2
  159. package/dist/baselineCommand-Cfw2Afwm.js +0 -2
  160. package/dist/codegen-uYrrDzQv.js +0 -2
  161. package/dist/codegenCommand-C60m_LCp.js +0 -30
  162. package/dist/dbCommand-CC-qNzpz.js +0 -2
  163. package/dist/dev-CAOoXOkc.js +0 -3
  164. package/dist/doctorCommand-Cjl8Yii8.js +0 -2
  165. package/dist/frameworkTableAssembly-YyVe32Cb.js +0 -2
  166. package/dist/inspect-CjTYzAs_.js +0 -1190
  167. package/dist/inspect-P4pxoMaV.js +0 -2
  168. package/dist/inspectGateHint-BF6608UT.js +0 -4
  169. package/dist/manifestBuild-COkJoyAr.js +0 -2
  170. package/dist/serveCommand-DNRF2nDI.js +0 -2
  171. package/dist/serveCommand-DnY4VS3D.js +0 -1767
  172. package/dist/updateCommand-B0J_T9Vs.js +0 -2
  173. package/dist/webDev-CcJfdSjl2.js +0 -2
@@ -2,13 +2,13 @@ import { E as e, N as t, O as n, T as r, d as i, f as a, h as o, i as s, l as c,
2
2
  import { t as d } from "./pageConvention-cEiRxdab.js";
3
3
  import { r as f } from "./tsconfigPaths-BWXBWgcl.js";
4
4
  import { a as p, i as ee, t as te } from "./fileTaxonomy-CJfgOllU.js";
5
- import { basename as m, dirname as h, join as g, relative as _ } from "node:path";
6
- import { existsSync as v, readFileSync as ne } from "node:fs";
7
- import { execFileSync as re } from "node:child_process";
8
- import { Node as y, Project as ie, QuoteKind as ae, SyntaxKind as b } from "ts-morph";
9
- import { globSync as oe } from "tinyglobby";
5
+ import { basename as m, dirname as h, join as g, relative as ne } from "node:path";
6
+ import { existsSync as _, readFileSync as re } from "node:fs";
7
+ import { execFileSync as ie } from "node:child_process";
8
+ import { Node as v, Project as ae, QuoteKind as oe, SyntaxKind as y } from "ts-morph";
9
+ import { globSync as se } from "tinyglobby";
10
10
  //#region src/codemodHelpers.ts
11
- var x = (e, t, n) => {
11
+ var b = (e, t, n) => {
12
12
  let r = [];
13
13
  for (let i of e.getImportDeclarations()) if (i.getModuleSpecifierValue() === t) for (let e of i.getNamedImports()) {
14
14
  if (e.getName() !== n) continue;
@@ -16,70 +16,70 @@ var x = (e, t, n) => {
16
16
  r.push(t ? t.getText() : e.getName());
17
17
  }
18
18
  return r;
19
- }, S = (e, t) => {
19
+ }, x = (e, t) => {
20
20
  let n = e.getExpression();
21
- return y.isIdentifier(n) && t.includes(n.getText());
22
- }, se = (e, { module: t, from: n, to: r }) => {
21
+ return v.isIdentifier(n) && t.includes(n.getText());
22
+ }, ce = (e, { module: t, from: n, to: r }) => {
23
23
  let i = 0;
24
24
  for (let a of e.getSourceFiles()) for (let e of a.getImportDeclarations()) if (e.getModuleSpecifierValue() === t) for (let t of e.getNamedImports()) {
25
25
  if (t.getName() !== n) continue;
26
26
  let e = t.getNameNode();
27
- !t.getAliasNode() && y.isIdentifier(e) ? e.rename(r) : e.replaceWithText(r), i++;
27
+ !t.getAliasNode() && v.isIdentifier(e) ? e.rename(r) : e.replaceWithText(r), i++;
28
28
  }
29
29
  return i;
30
- }, ce = (e, { from: t, to: n }) => {
30
+ }, le = (e, { from: t, to: n }) => {
31
31
  let r = 0;
32
32
  for (let i of e.getSourceFiles()) {
33
33
  for (let e of i.getImportDeclarations()) e.getModuleSpecifierValue() === t && (e.setModuleSpecifier(n), r++);
34
34
  for (let e of i.getExportDeclarations()) e.getModuleSpecifierValue() === t && (e.setModuleSpecifier(n), r++);
35
- for (let e of i.getDescendantsOfKind(b.CallExpression)) {
36
- if (e.getExpression().getKind() !== b.ImportKeyword) continue;
35
+ for (let e of i.getDescendantsOfKind(y.CallExpression)) {
36
+ if (e.getExpression().getKind() !== y.ImportKeyword) continue;
37
37
  let i = e.getArguments()[0];
38
- i && y.isStringLiteral(i) && i.getLiteralValue() === t && (i.replaceWithText(JSON.stringify(n)), r++);
38
+ i && v.isStringLiteral(i) && i.getLiteralValue() === t && (i.replaceWithText(JSON.stringify(n)), r++);
39
39
  }
40
40
  }
41
41
  return r;
42
- }, le = (e, { component: t, from: n, to: r }) => {
42
+ }, ue = (e, { component: t, from: n, to: r }) => {
43
43
  let i = 0;
44
44
  for (let a of e.getSourceFiles()) {
45
- let e = [...a.getDescendantsOfKind(b.JsxOpeningElement), ...a.getDescendantsOfKind(b.JsxSelfClosingElement)];
46
- for (let a of e) if (a.getTagNameNode().getText() === t) for (let e of a.getAttributes()) y.isJsxAttribute(e) && e.getNameNode().getText() === n && (e.getNameNode().replaceWithText(r), i++);
45
+ let e = [...a.getDescendantsOfKind(y.JsxOpeningElement), ...a.getDescendantsOfKind(y.JsxSelfClosingElement)];
46
+ for (let a of e) if (a.getTagNameNode().getText() === t) for (let e of a.getAttributes()) v.isJsxAttribute(e) && e.getNameNode().getText() === n && (e.getNameNode().replaceWithText(r), i++);
47
47
  }
48
48
  return i;
49
- }, ue = (e, { inCallTo: t, module: n, from: r, to: i }) => {
49
+ }, de = (e, { inCallTo: t, module: n, from: r, to: i }) => {
50
50
  let a = 0;
51
51
  for (let o of e.getSourceFiles()) {
52
- let e = x(o, n, t);
53
- if (e.length !== 0) for (let t of o.getDescendantsOfKind(b.CallExpression).reverse()) {
54
- if (!S(t, e)) continue;
52
+ let e = b(o, n, t);
53
+ if (e.length !== 0) for (let t of o.getDescendantsOfKind(y.CallExpression).reverse()) {
54
+ if (!x(t, e)) continue;
55
55
  let n = t.getArguments()[0];
56
- if (!(!n || !y.isObjectLiteralExpression(n))) for (let e of n.getProperties()) {
57
- if (!y.isPropertyAssignment(e) && !y.isShorthandPropertyAssignment(e)) continue;
56
+ if (!(!n || !v.isObjectLiteralExpression(n))) for (let e of n.getProperties()) {
57
+ if (!v.isPropertyAssignment(e) && !v.isShorthandPropertyAssignment(e)) continue;
58
58
  let t = e.getNameNode();
59
- t.getText() === r && (y.isShorthandPropertyAssignment(e) ? e.replaceWithText(`${i}: ${r}`) : t.replaceWithText(i), a++);
59
+ t.getText() === r && (v.isShorthandPropertyAssignment(e) ? e.replaceWithText(`${i}: ${r}`) : t.replaceWithText(i), a++);
60
60
  }
61
61
  }
62
62
  }
63
63
  return a;
64
- }, de = (e, { module: t, name: n }) => {
64
+ }, fe = (e, { module: t, name: n }) => {
65
65
  let r = 0;
66
66
  for (let i of e.getSourceFiles()) {
67
67
  let e = i.getImportDeclaration((e) => e.getModuleSpecifierValue() === t);
68
68
  e && (e.getNamedImports().some((e) => e.getName() === n) || (e.addNamedImport(n), r++));
69
69
  }
70
70
  return r;
71
- }, fe = (e, t, n) => {
71
+ }, pe = (e, t, n) => {
72
72
  let r = e.getImportDeclaration((e) => e.getModuleSpecifierValue() === t);
73
73
  r ? r.getNamedImports().some((e) => e.getName() === n) || r.addNamedImport(n) : e.addImportDeclaration({
74
74
  moduleSpecifier: t,
75
75
  namedImports: [n]
76
76
  });
77
- }, pe = {
78
- renameImport: se,
79
- renameModuleSpecifier: ce,
80
- renameJsxProp: le,
81
- renameObjectKey: ue,
82
- addImport: de,
77
+ }, me = {
78
+ renameImport: ce,
79
+ renameModuleSpecifier: le,
80
+ renameJsxProp: ue,
81
+ renameObjectKey: de,
82
+ addImport: fe,
83
83
  removeImport: (e, { module: t, name: n }) => {
84
84
  let r = 0;
85
85
  for (let i of e.getSourceFiles()) for (let e of i.getImportDeclarations()) {
@@ -92,9 +92,9 @@ var x = (e, t, n) => {
92
92
  changeCallArgs: (e, { module: t, callee: n, reorder: r, drop: i, toObject: a }) => {
93
93
  let o = 0;
94
94
  for (let s of e.getSourceFiles()) {
95
- let e = x(s, t, n);
96
- if (e.length !== 0) for (let t of s.getDescendantsOfKind(b.CallExpression).reverse()) {
97
- if (!S(t, e)) continue;
95
+ let e = b(s, t, n);
96
+ if (e.length !== 0) for (let t of s.getDescendantsOfKind(y.CallExpression).reverse()) {
97
+ if (!x(t, e)) continue;
98
98
  let n = t.getArguments().map((e) => e.getText());
99
99
  if (r && (n = r.map((e) => n[e]).filter((e) => e !== void 0)), i) {
100
100
  let e = new Set(i);
@@ -113,31 +113,31 @@ var x = (e, t, n) => {
113
113
  wrapCall: (e, { module: t, callee: n, wrapper: r, wrapperModule: i }) => {
114
114
  let a = 0;
115
115
  for (let o of e.getSourceFiles()) {
116
- let e = x(o, t, n);
116
+ let e = b(o, t, n);
117
117
  if (e.length === 0) continue;
118
118
  let s = !1;
119
- for (let t of o.getDescendantsOfKind(b.CallExpression).reverse()) S(t, e) && (t.replaceWithText(`${r}(${t.getText()})`), s = !0, a++);
120
- s && fe(o, i, r);
119
+ for (let t of o.getDescendantsOfKind(y.CallExpression).reverse()) x(t, e) && (t.replaceWithText(`${r}(${t.getText()})`), s = !0, a++);
120
+ s && pe(o, i, r);
121
121
  }
122
122
  return a;
123
123
  },
124
124
  annotate: (e, { module: t, symbol: n, comment: r }) => {
125
125
  let i = 0;
126
126
  for (let a of e.getSourceFiles()) {
127
- let e = x(a, t, n);
127
+ let e = b(a, t, n);
128
128
  if (e.length === 0) continue;
129
129
  let o = /* @__PURE__ */ new Map();
130
- for (let t of a.getDescendantsOfKind(b.Identifier)) {
130
+ for (let t of a.getDescendantsOfKind(y.Identifier)) {
131
131
  if (!e.includes(t.getText())) continue;
132
- let n = t.getFirstAncestorByKind(b.ExpressionStatement) ?? t.getFirstAncestorByKind(b.VariableStatement) ?? t.getFirstAncestorByKind(b.ReturnStatement);
132
+ let n = t.getFirstAncestorByKind(y.ExpressionStatement) ?? t.getFirstAncestorByKind(y.VariableStatement) ?? t.getFirstAncestorByKind(y.ReturnStatement);
133
133
  n && o.set(n.getStart(), n);
134
134
  }
135
135
  for (let e of [...o.values()].sort((e, t) => t.getStart() - e.getStart())) e.replaceWithText(`// TODO(voltro-migration): ${r}\n${e.getText()}`), i++;
136
136
  }
137
137
  return i;
138
138
  }
139
- }, me = /^\d+\.\d+\.\d+$/, C = (e) => {
140
- if (!me.test(e.version)) throw Error(`codemod "${e.id}": version must be x.y.z (got "${e.version}")`);
139
+ }, he = /^\d+\.\d+\.\d+$/, S = (e) => {
140
+ if (!he.test(e.version)) throw Error(`codemod "${e.id}": version must be x.y.z (got "${e.version}")`);
141
141
  if (!e.id.startsWith(`${e.version}/`)) throw Error(`codemod "${e.id}": id must start with "${e.version}/" (the version it migrates to)`);
142
142
  if (e.kind === "transform" && typeof e.transform != "function") throw Error(`codemod "${e.id}": kind:'transform' requires a transform() function`);
143
143
  if (e.kind === "manual" && (e.note === void 0 || e.note.trim() === "")) throw Error(`codemod "${e.id}": kind:'manual' requires a non-empty note`);
@@ -147,14 +147,14 @@ var x = (e, t, n) => {
147
147
  __codemod: !0,
148
148
  seq: n
149
149
  };
150
- }, w = (e) => RegExp(`\\b${e}\\s*(?:<[^()]*>)?\\s*\\(`), he = (e, t) => {
150
+ }, C = (e) => RegExp(`\\b${e}\\s*(?:<[^()]*>)?\\s*\\(`), ge = (e, t) => {
151
151
  let n = e.version.split(".").map(Number), r = t.version.split(".").map(Number);
152
152
  for (let e = 0; e < 3; e++) {
153
153
  let t = (n[e] ?? 0) - (r[e] ?? 0);
154
154
  if (t !== 0) return t;
155
155
  }
156
156
  return e.seq === t.seq ? e.id < t.id ? -1 : +(e.id > t.id) : e.seq - t.seq;
157
- }, ge = C({
157
+ }, _e = S({
158
158
  version: "0.3.0",
159
159
  id: "0.3.0/01_prod-serve-requires-build",
160
160
  title: "Production `voltro serve` requires a precompiled bundle (build before serve)",
@@ -174,7 +174,7 @@ var x = (e, t, n) => {
174
174
  "",
175
175
  "`voltro dev` and a non-production local `voltro serve` are unchanged (still tsx)."
176
176
  ].join("\n")
177
- }), _e = C({
177
+ }), ve = S({
178
178
  version: "0.4.0",
179
179
  id: "0.4.0/01_rbac-forbidden-to-scopeerror",
180
180
  title: "@voltro/plugin-rbac: `Forbidden` → protocol `ScopeError` (one denial tag)",
@@ -200,36 +200,36 @@ var x = (e, t, n) => {
200
200
  let a = r + i;
201
201
  a > 0 && n(`rbac Forbidden → ScopeError: rewrote ${a} import site(s). Check any \`new Forbidden({ reason })\` (→ \`new ScopeError({ message })\`) and \`catchTag('Forbidden')\` / \`_tag === 'Forbidden'\` (→ 'ScopeError') by hand.`);
202
202
  }
203
- }), T = "@voltro/plugin-rbac/web", ve = "@voltro/client", ye = C({
203
+ }), w = "@voltro/plugin-rbac/web", ye = "@voltro/client", be = S({
204
204
  version: "0.4.0",
205
205
  id: "0.4.0/02_rbac-web-to-client-permissions",
206
206
  title: "@voltro/plugin-rbac/web removed — the UI gate lives in @voltro/client",
207
207
  kind: "transform",
208
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === T)),
208
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === w)),
209
209
  transform: ({ project: e, helpers: t, log: n }) => {
210
210
  let r = t.annotate(e, {
211
- module: T,
211
+ module: w,
212
212
  symbol: "RbacScopeContext",
213
213
  comment: "voltro 0.4.0: RbacScopeContext is gone. Read scopes with `usePermissions()` from '@voltro/client' — it returns { scopes }, not the bare array."
214
214
  }), i = t.renameImport(e, {
215
- module: T,
215
+ module: w,
216
216
  from: "RbacScopeProvider",
217
217
  to: "PermissionProvider"
218
218
  }) + t.renameImport(e, {
219
- module: T,
219
+ module: w,
220
220
  from: "canFromScopes",
221
221
  to: "canCall"
222
222
  }) + t.renameImport(e, {
223
- module: T,
223
+ module: w,
224
224
  from: "canAnyFromScopes",
225
225
  to: "canCallAny"
226
226
  }), a = t.renameModuleSpecifier(e, {
227
- from: T,
228
- to: ve
227
+ from: w,
228
+ to: ye
229
229
  });
230
230
  a > 0 && n(`@voltro/plugin-rbac/web → @voltro/client: repointed ${a} import(s)` + (i > 0 ? `, renamed ${i} symbol(s)` : "") + ". Mount <PermissionProvider scopes={…}> where <RbacScopeProvider> was — same prop, and it now feeds useCan across the whole app." + (r > 0 ? ` ${r} RbacScopeContext site(s) were annotated: switch them to usePermissions().` : ""));
231
231
  }
232
- }), be = C({
232
+ }), xe = S({
233
233
  version: "0.5.0",
234
234
  id: "0.5.0/01_one-terminal-exactly-one",
235
235
  title: ".one() now fails on MORE than one row; NoRowFound is a declarable typed error",
@@ -262,7 +262,7 @@ var x = (e, t, n) => {
262
262
  " new OptimisticLockError({ table: 'notes', expected: 3 })",
263
263
  " • `instanceof` and `err._tag === 'NoRowFound'` checks are unaffected."
264
264
  ].join("\n")
265
- }), xe = C({
265
+ }), Se = S({
266
266
  version: "0.5.0",
267
267
  id: "0.5.0/02_typed-store-rows",
268
268
  title: "store.query() now returns the row type — casts on row fields may stop compiling",
@@ -301,14 +301,14 @@ var x = (e, t, n) => {
301
301
  "A hand-built descriptor still resolves to `Row`, so untyped call sites are",
302
302
  "unaffected."
303
303
  ].join("\n")
304
- }), Se = /VOLTRO_DEV_SESSION_SECRET|voltro-dev-session-secret/, Ce = /VOLTRO_SESSION_SECRET\s*(?:\?\?|\|\|)\s*['"`]/, we = C({
304
+ }), Ce = /VOLTRO_DEV_SESSION_SECRET|voltro-dev-session-secret/, we = /VOLTRO_SESSION_SECRET\s*(?:\?\?|\|\|)\s*['"`]/, Te = S({
305
305
  version: "0.6.0",
306
306
  id: "0.6.0/01_no-dev-session-secret",
307
307
  title: "The built-in dev session secret is removed; VOLTRO_SESSION_SECRET is required",
308
308
  kind: "manual",
309
309
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
310
310
  let t = e.getFullText();
311
- return Se.test(t) || Ce.test(t);
311
+ return Ce.test(t) || we.test(t);
312
312
  }),
313
313
  note: [
314
314
  "The framework no longer has a built-in fallback session secret.",
@@ -352,14 +352,14 @@ var x = (e, t, n) => {
352
352
  " voltro secret generate session",
353
353
  " `voltro dev` minting one for you locally does NOT cover your deployment."
354
354
  ].join("\n")
355
- }), Te = /computeProration|roundHalfAwayFromZero|ProrationInput|ProrationResult|DunningState|DunningCycleResult|DunningSchedule|DEFAULT_DUNNING_SCHEDULE|validateDunningSchedule|retryTimeFor|openDunning|recoverDunning|advanceDunning|runDunningCycle|recordPaymentFailure|recordPaymentSuccess|dunningSchedule|DunningStore|memoryDunningStore|dataStoreDunningStore/, Ee = C({
355
+ }), Ee = /computeProration|roundHalfAwayFromZero|ProrationInput|ProrationResult|DunningState|DunningCycleResult|DunningSchedule|DEFAULT_DUNNING_SCHEDULE|validateDunningSchedule|retryTimeFor|openDunning|recoverDunning|advanceDunning|runDunningCycle|recordPaymentFailure|recordPaymentSuccess|dunningSchedule|DunningStore|memoryDunningStore|dataStoreDunningStore/, De = S({
356
356
  version: "0.6.0",
357
357
  id: "0.6.0/03_billing-on-stripe-sdk",
358
358
  title: "Billing moved onto the Stripe SDK; proration + dunning are now Stripe’s",
359
359
  kind: "manual",
360
360
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
361
361
  let t = e.getFullText();
362
- return Te.test(t) || /@voltro\/plugin-billing/.test(t);
362
+ return Ee.test(t) || /@voltro\/plugin-billing/.test(t);
363
363
  }),
364
364
  note: [
365
365
  "`@voltro/plugin-billing` now talks to Stripe through the official SDK, and two",
@@ -415,7 +415,7 @@ var x = (e, t, n) => {
415
415
  " Set a `priceId` on every paid plan. A subscription created in the Stripe",
416
416
  " Dashboard is now recognised via its price id, which requires the mapping."
417
417
  ].join("\n")
418
- }), De = C({
418
+ }), Oe = S({
419
419
  version: "0.6.0",
420
420
  id: "0.6.0/02_subscription-state-union",
421
421
  title: "SubscriptionState is a discriminated union — `loading` now narrows `data`",
@@ -462,7 +462,7 @@ var x = (e, t, n) => {
462
462
  "`error` set. If you branch only on `loading`, check `error` too, or that",
463
463
  "case renders a skeleton forever."
464
464
  ].join("\n")
465
- }), Oe = C({
465
+ }), ke = S({
466
466
  version: "0.6.0",
467
467
  id: "0.6.0/04_relation-key-validation",
468
468
  title: "relation key options are validated at boot; `one` now fails on more than one match",
@@ -503,28 +503,28 @@ var x = (e, t, n) => {
503
503
  "read it now rather than waiting: `sourceKey` is almost always what a",
504
504
  "\"parent\" relation means."
505
505
  ].join("\n")
506
- }), ke = "@voltro/plugin-atlassian", Ae = "atlassianPlugin", E = (e) => {
506
+ }), Ae = "@voltro/plugin-atlassian", je = "atlassianPlugin", T = (e) => {
507
507
  let t = [];
508
- for (let n of e.getImportDeclarations()) if (n.getModuleSpecifierValue() === ke) for (let e of n.getNamedImports()) e.getName() === Ae && t.push(e.getAliasNode()?.getText() ?? e.getName());
508
+ for (let n of e.getImportDeclarations()) if (n.getModuleSpecifierValue() === Ae) for (let e of n.getNamedImports()) e.getName() === je && t.push(e.getAliasNode()?.getText() ?? e.getName());
509
509
  return t;
510
- }, je = C({
510
+ }, Me = S({
511
511
  version: "0.6.0",
512
512
  id: "0.6.0/05_atlassian-avatar-mode",
513
513
  title: "atlassianPlugin's `avatar` config requires an explicit `mode: 'service' | 'perUser'`",
514
514
  kind: "transform",
515
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => E(e).length > 0),
515
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => T(e).length > 0),
516
516
  transform: ({ project: e, log: t }) => {
517
517
  let n = 0;
518
518
  for (let t of e.getSourceFiles()) {
519
- let e = E(t);
520
- if (e.length !== 0) for (let r of t.getDescendantsOfKind(b.CallExpression)) {
519
+ let e = T(t);
520
+ if (e.length !== 0) for (let r of t.getDescendantsOfKind(y.CallExpression)) {
521
521
  if (!e.includes(r.getExpression().getText())) continue;
522
522
  let t = r.getArguments()[0];
523
- if (!t || !y.isObjectLiteralExpression(t)) continue;
523
+ if (!t || !v.isObjectLiteralExpression(t)) continue;
524
524
  let i = t.getProperty("avatar");
525
- if (!i || !y.isPropertyAssignment(i)) continue;
525
+ if (!i || !v.isPropertyAssignment(i)) continue;
526
526
  let a = i.getInitializer();
527
- !a || !y.isObjectLiteralExpression(a) || a.getProperty("mode") || (a.insertPropertyAssignment(0, {
527
+ !a || !v.isObjectLiteralExpression(a) || a.getProperty("mode") || (a.insertPropertyAssignment(0, {
528
528
  name: "mode",
529
529
  initializer: "'service'"
530
530
  }), n++);
@@ -532,7 +532,7 @@ var x = (e, t, n) => {
532
532
  }
533
533
  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"));
534
534
  }
535
- }), Me = C({
535
+ }), Ne = S({
536
536
  version: "0.6.0",
537
537
  id: "0.6.0/06_ui-strings-connect-account",
538
538
  title: "UiStrings gained a required `connectAccount` section",
@@ -576,14 +576,14 @@ var x = (e, t, n) => {
576
576
  "Alternatively, switch the annotation to `PartialUiStrings` if the object",
577
577
  "was only ever an override bag."
578
578
  ].join("\n")
579
- }), Ne = w("setRowFilter"), Pe = /\bresolveRowFilterScope\b/, Fe = C({
579
+ }), Pe = C("setRowFilter"), Fe = /\bresolveRowFilterScope\b/, Ie = S({
580
580
  version: "0.7.0",
581
581
  id: "0.7.0/01_row-filter-load-failure",
582
582
  title: "A row filter that fails to load now errors instead of returning zero rows",
583
583
  kind: "manual",
584
584
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
585
585
  let t = e.getFullText();
586
- return Ne.test(t) || Pe.test(t);
586
+ return Pe.test(t) || Fe.test(t);
587
587
  }),
588
588
  note: [
589
589
  "Row-level security: a `load` failure is now an ERROR, and is retried first.",
@@ -654,14 +654,14 @@ var x = (e, t, n) => {
654
654
  " \"every row you could see was just deleted\". Make sure your subscription",
655
655
  " error handling surfaces this rather than swallowing it."
656
656
  ].join("\n")
657
- }), Ie = C({
657
+ }), Le = S({
658
658
  version: "0.7.0",
659
659
  id: "0.7.0/02_testing-invoke-effect-handlers",
660
660
  title: "@voltro/testing: invoke() runs Effect handlers; executor types widened",
661
661
  kind: "manual",
662
662
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
663
663
  let t = e.getFullText();
664
- return t.includes("@voltro/testing") ? w("invoke").test(t) || /\bProcedureExecutor\b/.test(t) : !1;
664
+ return t.includes("@voltro/testing") ? C("invoke").test(t) || /\bProcedureExecutor\b/.test(t) : !1;
665
665
  }),
666
666
  note: [
667
667
  "`invoke(descriptor, executor, input, ctx)` now DETECTS an Effect-returning",
@@ -709,14 +709,14 @@ var x = (e, t, n) => {
709
709
  "transaction, the deadlock replay, `afterCommit` and the plugin interceptor",
710
710
  "chain wrap an Effect handler exactly as they wrap an async one."
711
711
  ].join("\n")
712
- }), Le = /\bpasswordHash\b/, Re = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, ze = C({
712
+ }), Re = /\bpasswordHash\b/, ze = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, Be = S({
713
713
  version: "0.8.0",
714
714
  id: "0.8.0/01_optional-password-hash",
715
715
  title: "`UserRecord.passwordHash` is optional — password-less identities are now first-class",
716
716
  kind: "manual",
717
717
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
718
718
  let t = e.getFullText();
719
- return Le.test(t) || Re.test(t);
719
+ return Re.test(t) || ze.test(t);
720
720
  }),
721
721
  note: [
722
722
  "`UserRecord.passwordHash` is now `string | null | undefined`, not `string`.",
@@ -787,7 +787,7 @@ var x = (e, t, n) => {
787
787
  " to match and make sure your read path maps a NULL column to `null`/",
788
788
  " `undefined` rather than to `''`-by-coercion."
789
789
  ].join("\n")
790
- }), Be = C({
790
+ }), Ve = S({
791
791
  version: "0.8.0",
792
792
  id: "0.8.0/02_ssr-streams-and-loaderdata",
793
793
  title: "`ssr` pages stream; `useLoaderData<T>()` returns `LoaderData<T>`",
@@ -839,13 +839,13 @@ var x = (e, t, n) => {
839
839
  "docs. `defer()` requires `renderMode: 'ssr'` and `interactive: 'full'`;",
840
840
  "every other combination is a hard error naming the page."
841
841
  ].join("\n")
842
- }), Ve = /renderMode\s*[=:]\s*['"](spa|client)['"]/, He = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, Ue = C({
842
+ }), He = /renderMode\s*[=:]\s*['"](spa|client)['"]/, Ue = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, We = S({
843
843
  version: "0.9.0",
844
844
  id: "0.9.0/01_ssr-layout-shell-for-spa",
845
845
  title: "A `renderMode:'spa'` page under a layout now server-renders that layout",
846
846
  kind: "manual",
847
847
  appliesTo: ({ project: e }) => {
848
- let t = e.getSourceFiles(), n = t.some((e) => Ve.test(e.getFullText())), r = t.some((e) => He.test(e.getFilePath()));
848
+ let t = e.getSourceFiles(), n = t.some((e) => He.test(e.getFullText())), r = t.some((e) => Ue.test(e.getFilePath()));
849
849
  return n && r;
850
850
  },
851
851
  note: [
@@ -884,35 +884,35 @@ var x = (e, t, n) => {
884
884
  "Nothing changes for `static` / `ssr` / `isr` pages, or for a spa page that",
885
885
  "has no layout (still a pure client mount)."
886
886
  ].join("\n")
887
- }), D = "client", O = "spa", k = (e) => {
887
+ }), E = "client", D = "spa", Ge = (e) => {
888
888
  if (!e) return null;
889
- let t = y.isAsExpression(e) ? e.getExpression() : e;
890
- return y.isStringLiteral(t) ? t : null;
891
- }, A = (e) => {
889
+ let t = v.isAsExpression(e) ? e.getExpression() : e;
890
+ return v.isStringLiteral(t) ? t : null;
891
+ }, O = (e) => {
892
892
  let t = [];
893
- for (let n of e.getDescendantsOfKind(b.VariableDeclaration)) {
893
+ for (let n of e.getDescendantsOfKind(y.VariableDeclaration)) {
894
894
  if (n.getName() !== "renderMode") continue;
895
- let e = k(n.getInitializer());
895
+ let e = Ge(n.getInitializer());
896
896
  e && t.push(e);
897
897
  }
898
- for (let n of e.getDescendantsOfKind(b.PropertyAssignment)) {
898
+ for (let n of e.getDescendantsOfKind(y.PropertyAssignment)) {
899
899
  if (n.getName() !== "renderMode") continue;
900
- let e = k(n.getInitializer());
900
+ let e = Ge(n.getInitializer());
901
901
  e && t.push(e);
902
902
  }
903
903
  return t;
904
- }, We = C({
904
+ }, Ke = S({
905
905
  version: "0.10.0",
906
906
  id: "0.10.0/01_render-mode-client-to-spa",
907
907
  title: "renderMode is a closed set — `'client'` becomes `'spa'`",
908
908
  kind: "transform",
909
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => A(e).some((e) => e.getLiteralValue() === D)),
909
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => O(e).some((e) => e.getLiteralValue() === E)),
910
910
  transform: ({ project: e, log: t }) => {
911
911
  let n = 0, r = /* @__PURE__ */ new Set();
912
- for (let t of e.getSourceFiles()) for (let e of A(t)) e.getLiteralValue() === D && (e.setLiteralValue(O), n++, r.add(t.getFilePath()));
913
- n > 0 && (t(`renderMode: rewrote ${n} \`'${D}'\` → \`'${O}'\` 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)"));
912
+ for (let t of e.getSourceFiles()) for (let e of O(t)) e.getLiteralValue() === E && (e.setLiteralValue(D), n++, r.add(t.getFilePath()));
913
+ n > 0 && (t(`renderMode: rewrote ${n} \`'${E}'\` → \`'${D}'\` 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)"));
914
914
  }
915
- }), Ge = C({
915
+ }), qe = S({
916
916
  version: "0.10.0",
917
917
  id: "0.10.0/02_router-state-carries-its-path",
918
918
  title: "`renderRouterStateScript` now requires the `path` it rendered",
@@ -949,7 +949,7 @@ var x = (e, t, n) => {
949
949
  "Trailing slashes, a trailing `/index.html`, and percent-encoding are normalised",
950
950
  "before the comparison, so the usual static-host shapes still hydrate."
951
951
  ].join("\n")
952
- }), Ke = C({
952
+ }), Je = S({
953
953
  version: "0.10.0",
954
954
  id: "0.10.0/03_workflow-caller-context-resolved",
955
955
  title: "workflow `buildContext` receives a RESOLVED caller context",
@@ -995,7 +995,7 @@ var x = (e, t, n) => {
995
995
  "it receives the resolved context too, so `callerContext?.subject ?? …` there",
996
996
  "becomes `callerContext.subject`."
997
997
  ].join("\n")
998
- }), qe = C({
998
+ }), Ye = S({
999
999
  version: "0.10.0",
1000
1000
  id: "0.10.0/04_seed-run-status-skipped",
1001
1001
  title: "seed run status gained `skipped`",
@@ -1036,7 +1036,7 @@ var x = (e, t, n) => {
1036
1036
  "`Schema.Literal`, a zod enum, a DB check constraint), widen it there too — an",
1037
1037
  "unlisted third state is a DECODE failure, not a rendering oddity."
1038
1038
  ].join("\n")
1039
- }), Je = C({
1039
+ }), Xe = S({
1040
1040
  version: "0.10.0",
1041
1041
  id: "0.10.0/05_subscription-idle-state",
1042
1042
  title: "`useSubscription` gained an idle state for `skip`",
@@ -1088,7 +1088,7 @@ var x = (e, t, n) => {
1088
1088
  "time's data), hold it in your own state — the subscription no longer claims to",
1089
1089
  "have data it was told to stop fetching."
1090
1090
  ].join("\n")
1091
- }), Ye = C({
1091
+ }), Ze = S({
1092
1092
  version: "0.11.0",
1093
1093
  id: "0.11.0/01_step-options-type-params",
1094
1094
  title: "`StepOptions` takes the context type first",
@@ -1116,7 +1116,7 @@ var x = (e, t, n) => {
1116
1116
  "If the step reads nothing from earlier steps, `StepOptions<Record<never, never>,",
1117
1117
  "Result>` is the honest spelling."
1118
1118
  ].join("\n")
1119
- }), Xe = C({
1119
+ }), Qe = S({
1120
1120
  version: "0.12.0",
1121
1121
  id: "0.12.0/01_outbound-http-ssrf-guard",
1122
1122
  title: "Outbound HttpClient blocks internal targets by default (add `http.allowHosts` if you call one)",
@@ -1156,7 +1156,7 @@ var x = (e, t, n) => {
1156
1156
  "private address (DNS rebinding) still passes. Pair with network-layer egress",
1157
1157
  "control if that is in your threat model."
1158
1158
  ].join("\n")
1159
- }), Ze = C({
1159
+ }), $e = S({
1160
1160
  version: "0.12.0",
1161
1161
  id: "0.12.0/02_api-key-revoke-tenant-scope",
1162
1162
  title: "apiKeyService.revoke/rotate now require the caller's tenantId",
@@ -1185,7 +1185,7 @@ var x = (e, t, n) => {
1185
1185
  "The framework's own `/v1/api-keys/revoke` route already passes the caller's",
1186
1186
  "tenant — no change needed if you only use the shipped routes."
1187
1187
  ].join("\n")
1188
- }), Qe = C({
1188
+ }), et = S({
1189
1189
  version: "0.12.0",
1190
1190
  id: "0.12.0/03_api-key-on-behalf-of",
1191
1191
  title: "API keys: createdBy is provenance, onBehalfOf is ownership",
@@ -1219,7 +1219,7 @@ var x = (e, t, n) => {
1219
1219
  "specific one against what the body asks for. Grant them in your role map. A key",
1220
1220
  "can never carry scopes its issuer does not hold."
1221
1221
  ].join("\n")
1222
- }), $e = C({
1222
+ }), tt = S({
1223
1223
  version: "0.12.0",
1224
1224
  id: "0.12.0/04_inspect-fail-closed",
1225
1225
  title: "The inspect surface requires VOLTRO_INSPECT_TOKEN (previously open when unset)",
@@ -1248,7 +1248,7 @@ var x = (e, t, n) => {
1248
1248
  "",
1249
1249
  "To turn the surface off entirely instead, set `VOLTRO_INSPECT=off`."
1250
1250
  ].join("\n")
1251
- }), et = C({
1251
+ }), nt = S({
1252
1252
  version: "0.13.0",
1253
1253
  id: "0.13.0/01_bearer-gates-fail-closed",
1254
1254
  title: "checkBearer is fail-closed; scimPlugin refuses an empty token",
@@ -1285,7 +1285,7 @@ var x = (e, t, n) => {
1285
1285
  " • `@voltro/plugin-prometheus` already passes `{ openWhenUnset: true }` — its",
1286
1286
  " `PROMETHEUS_TOKEN` is documented as optional. Nothing to do there."
1287
1287
  ].join("\n")
1288
- }), tt = C({
1288
+ }), rt = S({
1289
1289
  version: "0.13.0",
1290
1290
  id: "0.13.0/02_storage-grants-owner-only",
1291
1291
  title: "storage.share / revoke / listGrants require ownership",
@@ -1316,7 +1316,7 @@ var x = (e, t, n) => {
1316
1316
  " returning the grant or null. `storage.revoke` is handed a grant id, so it has",
1317
1317
  " to resolve the object before it can check the owner."
1318
1318
  ].join("\n")
1319
- }), nt = C({
1319
+ }), it = S({
1320
1320
  version: "0.13.0",
1321
1321
  id: "0.13.0/03_apply-plan-convergence-proof",
1322
1322
  title: "applyPlan requires a `replan` convergence proof",
@@ -1349,7 +1349,7 @@ var x = (e, t, n) => {
1349
1349
  " • `AppliedMigration` gained `appliedOps` (operations actually EXECUTED, from",
1350
1350
  " the per-op results). If you construct that value in a test fixture, add it."
1351
1351
  ].join("\n")
1352
- }), rt = C({
1352
+ }), at = S({
1353
1353
  version: "0.14.0",
1354
1354
  id: "0.14.0/01_resolve-scopes-store",
1355
1355
  title: "auth.resolveScopes receives the app DataStore",
@@ -1387,19 +1387,19 @@ var x = (e, t, n) => {
1387
1387
  " deliberately does not, because only you know how fast a role change must",
1388
1388
  " take effect."
1389
1389
  ].join("\n")
1390
- }), j = (e) => {
1391
- if (!y.isCallExpression(e)) return !1;
1390
+ }), k = (e) => {
1391
+ if (!v.isCallExpression(e)) return !1;
1392
1392
  let t = e.getExpression();
1393
- return y.isPropertyAccessExpression(t) && t.getName() === "reactive" && e.getArguments().length === 0;
1394
- }, it = (e) => e.getDescendantsOfKind(b.CallExpression).some(j), at = (e) => {
1395
- let t = 0, n = e.getDescendantsOfKind(b.CallExpression);
1393
+ return v.isPropertyAccessExpression(t) && t.getName() === "reactive" && e.getArguments().length === 0;
1394
+ }, ot = (e) => e.getDescendantsOfKind(y.CallExpression).some(k), st = (e) => {
1395
+ let t = 0, n = e.getDescendantsOfKind(y.CallExpression);
1396
1396
  for (let e of n) {
1397
- if (e.wasForgotten() || !j(e)) continue;
1397
+ if (e.wasForgotten() || !k(e)) continue;
1398
1398
  let n = e.getExpression();
1399
- y.isPropertyAccessExpression(n) && (e.replaceWithText(n.getExpression().getText()), t++);
1399
+ v.isPropertyAccessExpression(n) && (e.replaceWithText(n.getExpression().getText()), t++);
1400
1400
  }
1401
1401
  return t;
1402
- }, ot = C({
1402
+ }, ct = S({
1403
1403
  version: "0.14.0",
1404
1404
  id: "0.14.0/02_reactive-by-default",
1405
1405
  title: "Reactivity is the default; .reactive() is removed",
@@ -1417,16 +1417,16 @@ var x = (e, t, n) => {
1417
1417
  "real saving: REPLICA IDENTITY FULL widens every UPDATE/DELETE in the WAL.",
1418
1418
  "`voltro dev` warns if you opt out a table a query still reads."
1419
1419
  ].join("\n"),
1420
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => it(e)),
1420
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ot(e)),
1421
1421
  transform: ({ project: e, log: t }) => {
1422
1422
  let n = 0, r = 0;
1423
1423
  for (let t of e.getSourceFiles()) {
1424
- let e = at(t);
1424
+ let e = st(t);
1425
1425
  e !== 0 && (n += e, r++);
1426
1426
  }
1427
1427
  return n > 0 && t(`removed ${n} .reactive() call(s) across ${r} file(s) — reactivity is the default`), n > 0;
1428
1428
  }
1429
- }), st = C({
1429
+ }), lt = S({
1430
1430
  version: "0.14.0",
1431
1431
  id: "0.14.0/07_dev-remote-file-migrations",
1432
1432
  title: "voltro dev refuses unattended file migrations against a remote database",
@@ -1469,19 +1469,19 @@ var x = (e, t, n) => {
1469
1469
  "Unchanged: `voltro db files`, `voltro db apply` and `voltro serve`. This gates the",
1470
1470
  "AUTOMATIC boot path only; an explicit command is already an explicit decision."
1471
1471
  ].join("\n")
1472
- }), ct = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()], M = (e) => m(e).replace(/\.tsx?$/, ""), lt = (e, t, n) => {
1473
- let r = M(t), i = M(n);
1472
+ }), ut = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()], A = (e) => m(e).replace(/\.tsx?$/, ""), dt = (e, t, n) => {
1473
+ let r = A(t), i = A(n);
1474
1474
  if (r === i) return;
1475
1475
  let a = /\.tsx?$/.exec(e)?.[0] ?? "", o = a === "" ? e : e.slice(0, -a.length);
1476
1476
  if (!o.endsWith(`/${r}`) && o !== r) return;
1477
1477
  let s = a === "" ? "" : /\.tsx$/.test(n) ? ".tsx" : ".ts";
1478
1478
  return `${o.slice(0, o.length - r.length)}${i}${s}`;
1479
- }, N = (e, t) => {
1479
+ }, j = (e, t) => {
1480
1480
  let n = e.getFilePath(), r = [];
1481
- for (let i of e.getProject().getSourceFiles()) for (let e of ct(i)) {
1481
+ for (let i of e.getProject().getSourceFiles()) for (let e of ut(i)) {
1482
1482
  let i = e.getModuleSpecifierValue();
1483
1483
  if (i === void 0 || i.startsWith(".") || e.getModuleSpecifierSourceFile()?.getFilePath() !== n) continue;
1484
- let a = lt(i, n, t);
1484
+ let a = dt(i, n, t);
1485
1485
  a !== void 0 && r.push({
1486
1486
  decl: e,
1487
1487
  spec: a
@@ -1490,19 +1490,19 @@ var x = (e, t, n) => {
1490
1490
  e.move(t);
1491
1491
  for (let e of r) e.decl.setModuleSpecifier(e.spec);
1492
1492
  return r.length;
1493
- }, ut = (e) => {
1493
+ }, ft = (e) => {
1494
1494
  let t = e.getProject().getCompilerOptions().paths;
1495
1495
  if (t === void 0) return;
1496
- let n = M(e.getFilePath()), r = e.getFilePath().slice(0, e.getFilePath().lastIndexOf("/"));
1496
+ let n = A(e.getFilePath()), r = e.getFilePath().slice(0, e.getFilePath().lastIndexOf("/"));
1497
1497
  for (let [e, i] of Object.entries(t)) if (!e.endsWith("*")) for (let t of i ?? []) {
1498
1498
  if (!/\.tsx?$/.test(t)) continue;
1499
- let i = M(t), a = t.slice(0, t.lastIndexOf("/"));
1499
+ let i = A(t), a = t.slice(0, t.lastIndexOf("/"));
1500
1500
  if (i === n && r.endsWith(a.replace(/^\.\//, ""))) return e;
1501
1501
  }
1502
- }, dt = /\.page\.tsx$/, P = (e) => {
1502
+ }, pt = /\.page\.tsx$/, M = (e) => {
1503
1503
  let t = e.includes("\\src\\pages\\") ? "\\src\\pages\\" : "/src/pages/", n = e.indexOf(t);
1504
1504
  return n === -1 ? null : e.slice(0, n);
1505
- }, ft = (e) => e.endsWith(".tsx") && !o.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !dt.test(e), pt = C({
1505
+ }, mt = (e) => e.endsWith(".tsx") && !o.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !pt.test(e), ht = S({
1506
1506
  version: "0.14.0",
1507
1507
  id: "0.14.0/03_pages-suffix",
1508
1508
  title: "Pages are declared with a .page.tsx suffix",
@@ -1517,8 +1517,8 @@ var x = (e, t, n) => {
1517
1517
  transform: ({ project: e, log: t }) => {
1518
1518
  let n = /* @__PURE__ */ new Map(), r = e.getSourceFiles().filter((t) => {
1519
1519
  let r = t.getFilePath();
1520
- if (!ft(m(r))) return !1;
1521
- let i = P(r);
1520
+ if (!mt(m(r))) return !1;
1521
+ let i = M(r);
1522
1522
  if (i === null) return !1;
1523
1523
  let a = n.get(i);
1524
1524
  return a === void 0 && (a = e.getSourceFile(g(i, "app.config.ts")) !== void 0 || e.getSourceFile(g(i, "app.config.tsx")) !== void 0, n.set(i, a)), a;
@@ -1536,13 +1536,13 @@ var x = (e, t, n) => {
1536
1536
  let n = t.getFilePath(), r = m(n), s = h(n), c = r.slice(0, -4);
1537
1537
  for (let t of [".test.tsx", ".spec.tsx"]) {
1538
1538
  let n = g(s, `${c}${t}`), r = e.getSourceFile(n);
1539
- r !== void 0 && (N(r, g(s, `${c}.page${t}`)), o += 1);
1539
+ r !== void 0 && (j(r, g(s, `${c}.page${t}`)), o += 1);
1540
1540
  }
1541
- N(t, g(s, `${c}.page.tsx`));
1541
+ j(t, g(s, `${c}.page.tsx`));
1542
1542
  let l = i.get(n);
1543
1543
  if (l !== void 0 && l.length > 0 && !l.every((e) => d(m(e)))) {
1544
- let e = P(n) ?? h(n), t = [...new Set(l)].map((t) => _(e, t));
1545
- a.push(`${_(e, n)} ← imported by ${t.join(", ")}`);
1544
+ let e = M(n) ?? h(n), t = [...new Set(l)].map((t) => ne(e, t));
1545
+ a.push(`${ne(e, n)} ← imported by ${t.join(", ")}`);
1546
1546
  }
1547
1547
  }
1548
1548
  if (t(`renamed ${r.length} route file(s) to *.page.tsx${o > 0 ? ` (+${o} co-located test file(s))` : ""}`), a.length > 0) {
@@ -1550,7 +1550,7 @@ var x = (e, t, n) => {
1550
1550
  for (let e of a) t(` ${e}`);
1551
1551
  }
1552
1552
  }
1553
- }), mt = [
1553
+ }), gt = [
1554
1554
  /\.page\.tsx$/,
1555
1555
  a,
1556
1556
  s,
@@ -1561,24 +1561,24 @@ var x = (e, t, n) => {
1561
1561
  c,
1562
1562
  e,
1563
1563
  r
1564
- ], ht = (e) => {
1564
+ ], _t = (e) => {
1565
1565
  let n = m(e);
1566
- return !/\.tsx?$/.test(n) || o.has(n) || t(n) || n === "index.ts" || n === "index.tsx" || n === "app.config.ts" ? !1 : !mt.some((e) => e.test(n));
1567
- }, gt = (e) => {
1566
+ return !/\.tsx?$/.test(n) || o.has(n) || t(n) || n === "index.ts" || n === "index.tsx" || n === "app.config.ts" ? !1 : !gt.some((e) => e.test(n));
1567
+ }, vt = (e) => {
1568
1568
  let t = [], n = [], r = [], i = [];
1569
1569
  for (let [a, o] of e.getExportedDeclarations()) {
1570
1570
  let e = o[0];
1571
1571
  if (e === void 0) continue;
1572
- if (y.isInterfaceDeclaration(e) || y.isTypeAliasDeclaration(e)) {
1572
+ if (v.isInterfaceDeclaration(e) || v.isTypeAliasDeclaration(e)) {
1573
1573
  i.push(a);
1574
1574
  continue;
1575
1575
  }
1576
1576
  if (a === "default") {
1577
- if (!(y.isFunctionDeclaration(e) || y.isClassDeclaration(e) || y.isArrowFunction(e) || y.isFunctionExpression(e))) {
1577
+ if (!(v.isFunctionDeclaration(e) || v.isClassDeclaration(e) || v.isArrowFunction(e) || v.isFunctionExpression(e))) {
1578
1578
  r.push("default");
1579
1579
  continue;
1580
1580
  }
1581
- let n = y.isFunctionDeclaration(e) || y.isClassDeclaration(e) ? e.getName() : void 0;
1581
+ let n = v.isFunctionDeclaration(e) || v.isClassDeclaration(e) ? e.getName() : void 0;
1582
1582
  t.push(n ?? "Default");
1583
1583
  continue;
1584
1584
  }
@@ -1591,10 +1591,10 @@ var x = (e, t, n) => {
1591
1591
  others: r,
1592
1592
  types: i
1593
1593
  };
1594
- }, _t = (e, t) => {
1594
+ }, yt = (e, t) => {
1595
1595
  let { components: n, hooks: r, others: i, types: a } = e;
1596
1596
  return n.length + r.length + i.length === 0 ? a.length > 0 ? ".types" : null : t && te(n) && r.length === 0 && i.length === 0 ? ".component.tsx" : n.length === 0 && r.length === 1 && i.length === 0 ? ".hook" : null;
1597
- }, vt = C({
1597
+ }, bt = S({
1598
1598
  version: "0.14.0",
1599
1599
  id: "0.14.0/04_file-taxonomy",
1600
1600
  title: "Web files declare their contract (*.component.tsx / *.hook.ts / *.types.ts)",
@@ -1607,19 +1607,19 @@ var x = (e, t, n) => {
1607
1607
  if (s.length > 0) for (let e of s) t(` exempt (not ours): ${e.dir} — ${e.source}`);
1608
1608
  for (let t of e.getSourceFiles()) {
1609
1609
  let e = t.getFilePath();
1610
- if (!ht(e) || p(e, s)) continue;
1611
- let o = ut(t);
1610
+ if (!_t(e) || p(e, s)) continue;
1611
+ let o = ft(t);
1612
1612
  if (o !== void 0) {
1613
1613
  a.push(`${m(e)} (tsconfig paths: "${o}")`);
1614
1614
  continue;
1615
1615
  }
1616
- let c = e.endsWith(".tsx"), l = gt(t), u = _t(l, c);
1616
+ let c = e.endsWith(".tsx"), l = vt(t), u = yt(l, c);
1617
1617
  if (u === null) {
1618
1618
  l.components.length > 0 && l.hooks.length > 0 && r.push(`${m(e)} (component ${l.components.join("/")} + hook ${l.hooks.join("/")})`);
1619
1619
  continue;
1620
1620
  }
1621
1621
  let d = m(e).replace(/\.tsx?$/, ""), f = u === ".component.tsx" ? ".component.tsx" : `${u}${c ? ".tsx" : ".ts"}`;
1622
- i += N(t, g(h(e), `${d}${f}`)), n.push(`${d}${f}`);
1622
+ i += j(t, g(h(e), `${d}${f}`)), n.push(`${d}${f}`);
1623
1623
  }
1624
1624
  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) {
1625
1625
  t(""), t(`left alone — a tsconfig \`paths\` entry names ${a.length} of these files directly:`);
@@ -1632,7 +1632,7 @@ var x = (e, t, n) => {
1632
1632
  r.length > 20 && t(` (+${r.length - 20} more)`);
1633
1633
  }
1634
1634
  }
1635
- }), yt = C({
1635
+ }), xt = S({
1636
1636
  version: "0.14.0",
1637
1637
  id: "0.14.0/05_typed-tracking",
1638
1638
  title: "Tracking catalogues are typed against the component props",
@@ -1669,7 +1669,7 @@ var x = (e, t, n) => {
1669
1669
  "Read each one before changing it: a cast that no longer compiles is sometimes",
1670
1670
  "a real type disagreement the old signature was hiding."
1671
1671
  ].join("\n")
1672
- }), bt = C({
1672
+ }), St = S({
1673
1673
  version: "0.14.0",
1674
1674
  id: "0.14.0/06_voltro-test-skips-e2e",
1675
1675
  title: "`voltro test` leaves `e2e/` to `voltro e2e`",
@@ -1691,13 +1691,13 @@ var x = (e, t, n) => {
1691
1691
  " code it tests, or",
1692
1692
  " • pass your own `--exclude`, which overrides the framework default outright."
1693
1693
  ].join("\n")
1694
- }), xt = (e) => {
1694
+ }), Ct = (e) => {
1695
1695
  let n = e.replace(/\.component(\.ui)?(\.tsx?)$/, "$2");
1696
1696
  return n === e ? null : t(n) ? n : null;
1697
- }, St = (e) => {
1697
+ }, wt = (e) => {
1698
1698
  for (let [, t] of e.getExportedDeclarations()) if (t[0] !== void 0) return !1;
1699
1699
  return e.getExportDeclarations().length === 0 && e.getExportAssignments().length === 0;
1700
- }, Ct = C({
1700
+ }, Tt = S({
1701
1701
  version: "0.15.0",
1702
1702
  id: "0.15.0/01_undo-mistaken-taxonomy-renames",
1703
1703
  title: "Undo the two file renames 0.14.0 got wrong (framework primitives, side-effect modules)",
@@ -1714,14 +1714,14 @@ var x = (e, t, n) => {
1714
1714
  transform: ({ project: e, log: t }) => {
1715
1715
  let n = [], r = [];
1716
1716
  for (let t of e.getSourceFiles()) {
1717
- let e = t.getFilePath(), i = m(e), a = xt(i);
1717
+ let e = t.getFilePath(), i = m(e), a = Ct(i);
1718
1718
  if (a !== null) {
1719
- N(t, g(h(e), a)), n.push(`${i} → ${a}`);
1719
+ j(t, g(h(e), a)), n.push(`${i} → ${a}`);
1720
1720
  continue;
1721
1721
  }
1722
- if (/\.types\.tsx?$/.test(i) && St(t)) {
1722
+ if (/\.types\.tsx?$/.test(i) && wt(t)) {
1723
1723
  let n = i.replace(/\.types(\.tsx?)$/, "$1");
1724
- N(t, g(h(e), n)), r.push(`${i} → ${n}`);
1724
+ j(t, g(h(e), n)), r.push(`${i} → ${n}`);
1725
1725
  }
1726
1726
  }
1727
1727
  if (n.length > 0) {
@@ -1735,54 +1735,54 @@ var x = (e, t, n) => {
1735
1735
  }
1736
1736
  n.length === 0 && r.length === 0 && t("nothing to undo — 0.14.0 renamed neither a framework file nor a side-effect module here.");
1737
1737
  }
1738
- }), F = ["@voltro/client", "@voltro/web"], I = "@voltro/protocol", wt = C({
1738
+ }), N = ["@voltro/client", "@voltro/web"], P = "@voltro/protocol", Et = S({
1739
1739
  version: "0.15.0",
1740
1740
  id: "0.15.0/02_error-tag-in-protocol",
1741
1741
  title: "`errorTag` is imported from @voltro/protocol",
1742
1742
  kind: "transform",
1743
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => F.includes(e.getModuleSpecifierValue()) && e.getNamedImports().some((e) => e.getName() === "errorTag"))),
1743
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => N.includes(e.getModuleSpecifierValue()) && e.getNamedImports().some((e) => e.getName() === "errorTag"))),
1744
1744
  transform: ({ project: e, log: t }) => {
1745
1745
  let n = 0;
1746
1746
  for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
1747
- if (!F.includes(e.getModuleSpecifierValue())) continue;
1747
+ if (!N.includes(e.getModuleSpecifierValue())) continue;
1748
1748
  let r = e.getNamedImports().find((e) => e.getName() === "errorTag");
1749
1749
  if (r === void 0) continue;
1750
1750
  let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
1751
1751
  r.remove();
1752
- let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === I && e.isTypeOnly() === a), s = {
1752
+ let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === P && e.isTypeOnly() === a), s = {
1753
1753
  name: "errorTag",
1754
1754
  ...i === void 0 ? {} : { alias: i }
1755
1755
  };
1756
1756
  o === void 0 ? t.addImportDeclaration({
1757
- moduleSpecifier: I,
1757
+ moduleSpecifier: P,
1758
1758
  isTypeOnly: a,
1759
1759
  namedImports: [s]
1760
1760
  }) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
1761
1761
  }
1762
- n > 0 && t(`errorTag: ${n} import(s) repointed to ${I}`);
1762
+ n > 0 && t(`errorTag: ${n} import(s) repointed to ${P}`);
1763
1763
  }
1764
- }), L = /\.page\.tsx$/, R = /\.page\.(test|spec)\.tsx$/, z = (e) => /(^|\/)src\/pages\//.test(e), Tt = (e) => {
1765
- let t = h(e), n = m(e).replace(L, "");
1764
+ }), F = /\.page\.tsx$/, Dt = /\.page\.(test|spec)\.tsx$/, I = (e) => /(^|\/)src\/pages\//.test(e), Ot = (e) => {
1765
+ let t = h(e), n = m(e).replace(F, "");
1766
1766
  return n === "index" ? g(t, "page.tsx") : g(t, n, "page.tsx");
1767
- }, Et = (e) => {
1768
- let t = h(e), n = m(e), r = /\.page\.spec\.tsx$/.test(n) ? "spec" : "test", i = n.replace(R, "");
1767
+ }, kt = (e) => {
1768
+ let t = h(e), n = m(e), r = /\.page\.spec\.tsx$/.test(n) ? "spec" : "test", i = n.replace(Dt, "");
1769
1769
  return i === "index" ? g(t, `page.${r}.tsx`) : g(t, i, `page.${r}.tsx`);
1770
- }, Dt = C({
1770
+ }, At = S({
1771
1771
  version: "0.17.0",
1772
1772
  id: "0.17.0/01_pages-are-directories",
1773
1773
  title: "A directory is a route segment; its route is page.tsx",
1774
1774
  kind: "transform",
1775
1775
  scope: ["**/src/pages/**/*.ts", "**/src/pages/**/*.tsx"],
1776
1776
  needsImporters: !0,
1777
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => z(e.getFilePath()) && L.test(m(e.getFilePath()))),
1777
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => I(e.getFilePath()) && F.test(m(e.getFilePath()))),
1778
1778
  transform: ({ project: e, log: t }) => {
1779
1779
  let n = e.getSourceFiles(), r = [];
1780
1780
  for (let e of n) {
1781
1781
  let t = String(e.getFilePath());
1782
- if (!z(t)) continue;
1783
- let n = m(t), i = R.test(n);
1784
- if (!i && !L.test(n)) continue;
1785
- let a = i ? Et(t) : Tt(t);
1782
+ if (!I(t)) continue;
1783
+ let n = m(t), i = Dt.test(n);
1784
+ if (!i && !F.test(n)) continue;
1785
+ let a = i ? kt(t) : Ot(t);
1786
1786
  a !== t && r.push({
1787
1787
  sf: e,
1788
1788
  from: t,
@@ -1801,7 +1801,7 @@ var x = (e, t, n) => {
1801
1801
  }
1802
1802
  }
1803
1803
  let l = [], u = 0;
1804
- for (let e of r) s.has(e.from) || (u += N(e.sf, e.to), e.isTest || l.push(o(e.to)));
1804
+ for (let e of r) s.has(e.from) || (u += j(e.sf, e.to), e.isTest || l.push(o(e.to)));
1805
1805
  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) {
1806
1806
  t(""), t(`LEFT ALONE — ${c.length} case(s) where two files claim one route:`);
1807
1807
  for (let e of c) t(` ${e}`);
@@ -1809,12 +1809,12 @@ var x = (e, t, n) => {
1809
1809
  }
1810
1810
  l.length === 0 && c.length === 0 && t("every route already lives in its own directory — nothing to move.");
1811
1811
  }
1812
- }), Ot = /,\s*(['"])like\1\s*,/, kt = C({
1812
+ }), jt = /,\s*(['"])like\1\s*,/, Mt = S({
1813
1813
  version: "0.18.0",
1814
1814
  id: "0.18.0/01_where-like-removed",
1815
1815
  title: "the `'like'` where-operator is removed — pick `contains` or `startsWith`",
1816
1816
  kind: "manual",
1817
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ot.test(e.getFullText())),
1817
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => jt.test(e.getFullText())),
1818
1818
  note: [
1819
1819
  "The `'like'` operator is removed from the ergonomic `.where(col, op, value)` form.",
1820
1820
  "",
@@ -1852,12 +1852,12 @@ var x = (e, t, n) => {
1852
1852
  "in a raw query (`ctx.store.raw`); the DSL has no arbitrary-pattern operator, and",
1853
1853
  "adding one that cannot use an index is not on the roadmap."
1854
1854
  ].join("\n")
1855
- }), At = /\.serverOnly\s*\(\s*\)/, jt = C({
1855
+ }), Nt = /\.serverOnly\s*\(\s*\)/, Pt = S({
1856
1856
  version: "0.19.0",
1857
1857
  id: "0.19.0/01_server-only-gates-the-boot",
1858
1858
  title: "`.serverOnly()` now FAILS the boot under `voltro serve` — it used to only warn",
1859
1859
  kind: "manual",
1860
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => At.test(e.getFullText())),
1860
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Nt.test(e.getFullText())),
1861
1861
  note: [
1862
1862
  "`.serverOnly()` now GATES. Before 0.19.0 it emitted one `log.warn` and let the",
1863
1863
  "boot continue — in every command, including `voltro serve`.",
@@ -1904,12 +1904,12 @@ var x = (e, t, n) => {
1904
1904
  "hatch is that people delete the marker instead — and a check whose only way out",
1905
1905
  "is to disable it gets disabled."
1906
1906
  ].join("\n")
1907
- }), Mt = /\bauditPlugin\s*\(/, Nt = C({
1907
+ }), Ft = /\bauditPlugin\s*\(/, It = S({
1908
1908
  version: "0.19.0",
1909
1909
  id: "0.19.0/02_audit-redact-input-default",
1910
1910
  title: "`auditPlugin` now redacts mutation input by default (`redactInput: 'all'`)",
1911
1911
  kind: "manual",
1912
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Mt.test(e.getFullText())),
1912
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ft.test(e.getFullText())),
1913
1913
  note: [
1914
1914
  "`auditPlugin`'s `redactInput` now defaults to `'all'`. Audit rows written from",
1915
1915
  "now on carry `input: { __redacted: 'all' }` — which still proves a payload",
@@ -1945,12 +1945,12 @@ var x = (e, t, n) => {
1945
1945
  "those markers live on TABLE COLUMNS, and this is a mutation's INPUT.",
1946
1946
  "`changePassword({ oldPassword, newPassword })` has no column to consult."
1947
1947
  ].join("\n")
1948
- }), Pt = w("registerWriteRecorder"), Ft = C({
1948
+ }), Lt = C("registerWriteRecorder"), Rt = S({
1949
1949
  version: "0.20.0",
1950
1950
  id: "0.20.0/01_write-recorder-port",
1951
1951
  title: "`WriteRecorder` takes a port (`{ append, maxOf }`) instead of a bare `append`",
1952
1952
  kind: "manual",
1953
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Pt.test(e.getFullText())),
1953
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Lt.test(e.getFullText())),
1954
1954
  note: [
1955
1955
  "If you wrote your own in-transaction write recorder, its first parameter is",
1956
1956
  "now an object rather than a function:",
@@ -1989,12 +1989,12 @@ var x = (e, t, n) => {
1989
1989
  "nested transaction, and a throw from either operation still rolls the",
1990
1990
  "caller's write back. That is the guarantee the timing exists for."
1991
1991
  ].join("\n")
1992
- }), It = /\.maxLength\s*\(/, Lt = C({
1992
+ }), zt = /\.maxLength\s*\(/, Bt = S({
1993
1993
  version: "0.21.0",
1994
1994
  id: "0.21.0/01_maxlength-now-migrates",
1995
1995
  title: "`text().maxLength(n)` now migrates EXISTING columns — plan before you apply",
1996
1996
  kind: "manual",
1997
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => It.test(e.getFullText())),
1997
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => zt.test(e.getFullText())),
1998
1998
  note: [
1999
1999
  "Your source does not change. `text().maxLength(n)` compiles exactly as before",
2000
2000
  "and every bound you already declared stays as written.",
@@ -2036,12 +2036,12 @@ var x = (e, t, n) => {
2036
2036
  "framework guarantees, and an upgrade-time ALTER that fails on your data is a",
2037
2037
  "worse outcome than the index-size concern it would fix."
2038
2038
  ].join("\n")
2039
- }), Rt = /\bMigrationOperation\b|\bPlannedOperation\b|\bInspectMigrationOperationKind\b|\bOperationKind\b/, zt = C({
2039
+ }), Vt = /\bMigrationOperation\b|\bPlannedOperation\b|\bInspectMigrationOperationKind\b|\bOperationKind\b/, Ht = S({
2040
2040
  version: "0.22.0",
2041
2041
  id: "0.22.0/01_rename-index-op",
2042
2042
  title: "`MigrationOperation` gained a `rename-index` kind",
2043
2043
  kind: "manual",
2044
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Rt.test(e.getFullText())),
2044
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Vt.test(e.getFullText())),
2045
2045
  note: [
2046
2046
  "The migration planner can now emit a `rename-index` operation:",
2047
2047
  "",
@@ -2073,12 +2073,12 @@ var x = (e, t, n) => {
2073
2073
  "match where two same-shaped indexes were renamed at once. Those still plan",
2074
2074
  "as drop + create, exactly as before."
2075
2075
  ].join("\n")
2076
- }), Bt = /@voltro\/plugin-(notifications|scim|ai-flows)/, Vt = C({
2076
+ }), Ut = /@voltro\/plugin-(notifications|scim|ai-flows)/, Wt = S({
2077
2077
  version: "0.22.0",
2078
2078
  id: "0.22.0/02_plugin-tables-voltro-prefixed",
2079
2079
  title: "plugin-owned tables are `_voltro_`-prefixed (the rows move themselves)",
2080
2080
  kind: "manual",
2081
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Bt.test(e.getFullText())),
2081
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ut.test(e.getFullText())),
2082
2082
  note: [
2083
2083
  "Ten tables owned by @voltro/plugin-notifications, @voltro/plugin-scim and",
2084
2084
  "@voltro/plugin-ai-flows moved out of your table namespace and into the",
@@ -2128,12 +2128,12 @@ var x = (e, t, n) => {
2128
2128
  "gained a `rename-table` kind. An exhaustive `switch` needs an arm; don't add",
2129
2129
  "a `default:`, which would silence this and every future kind."
2130
2130
  ].join("\n")
2131
- }), Ht = /QueryCacheScope|QueryCacheConfig|scope\s*:\s*['"](?:subject|global)['"]/, Ut = C({
2131
+ }), Gt = /QueryCacheScope|QueryCacheConfig|scope\s*:\s*['"](?:subject|global)['"]/, Kt = S({
2132
2132
  version: "0.22.0",
2133
2133
  id: "0.22.0/03_cache-scope-gained-tenant",
2134
2134
  title: "`cache.scope` gained 'tenant' — an exhaustive switch over it needs a branch",
2135
2135
  kind: "manual",
2136
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ht.test(e.getFullText())),
2136
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Gt.test(e.getFullText())),
2137
2137
  note: [
2138
2138
  "`cache: { scope }` now accepts a third value, `'tenant'`: one cache entry per",
2139
2139
  "org, none shared across orgs.",
@@ -2162,12 +2162,12 @@ var x = (e, t, n) => {
2162
2162
  "Rubric: does the resolved predicate depend on the caller? On the PERSON →",
2163
2163
  "`subject`; on their ORG only → `tenant`; on neither → `global`."
2164
2164
  ].join("\n")
2165
- }), Wt = /envTokenAuthResolver|InspectAuthResolver|authResolver/, Gt = C({
2165
+ }), qt = /envTokenAuthResolver|InspectAuthResolver|authResolver/, Jt = S({
2166
2166
  version: "0.22.0",
2167
2167
  id: "0.22.0/04_inspect-write-credential",
2168
2168
  title: "Mutating `/_voltro/inspect/*` endpoints need VOLTRO_INSPECT_WRITE_TOKEN",
2169
2169
  kind: "manual",
2170
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Wt.test(e.getFullText())),
2170
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => qt.test(e.getFullText())),
2171
2171
  note: [
2172
2172
  "A POST / PATCH / PUT / DELETE to `/_voltro/inspect/*` now needs a SECOND",
2173
2173
  "credential: `VOLTRO_INSPECT_WRITE_TOKEN`, sent as the `x-voltro-inspect-write`",
@@ -2205,12 +2205,12 @@ var x = (e, t, n) => {
2205
2205
  "IMPLEMENTING a resolver is unaffected — `(headers) => …` still satisfies the",
2206
2206
  "type. Only callers are obliged."
2207
2207
  ].join("\n")
2208
- }), Kt = /versioningPlugin\s*\(/, qt = C({
2208
+ }), Yt = /versioningPlugin\s*\(/, Xt = S({
2209
2209
  version: "0.23.0",
2210
2210
  id: "0.23.0/01_versioning-is-opt-out",
2211
2211
  title: "versioningPlugin: `tables: string[]` → opt-out by default, `include` / `exclude` by value",
2212
2212
  kind: "manual",
2213
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Kt.test(e.getFullText())),
2213
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Yt.test(e.getFullText())),
2214
2214
  note: [
2215
2215
  "`versioningPlugin({ tables: [...] })` no longer compiles. Row history is now",
2216
2216
  "ON by default for every table your app declares.",
@@ -2250,12 +2250,12 @@ var x = (e, t, n) => {
2250
2250
  "surprises you, `exclude` is the knob, and the retention sweep",
2251
2251
  "(`VOLTRO_ROW_HISTORY_TTL_HOURS`) still bounds age."
2252
2252
  ].join("\n")
2253
- }), Jt = /@voltro\/ai|\.agent\.tsx|agent_(threads|messages)/, Yt = C({
2253
+ }), Zt = /@voltro\/ai|\.agent\.tsx|agent_(threads|messages)/, Qt = S({
2254
2254
  version: "0.24.0",
2255
2255
  id: "0.24.0/01_agent-tables-voltro-prefixed",
2256
2256
  title: "the two agent tables are `_voltro_`-prefixed (the rows move themselves)",
2257
2257
  kind: "manual",
2258
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Jt.test(e.getFullText())),
2258
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Zt.test(e.getFullText())),
2259
2259
  note: [
2260
2260
  "The two tables owned by @voltro/ai moved out of your table namespace and",
2261
2261
  "into the framework's:",
@@ -2308,12 +2308,12 @@ var x = (e, t, n) => {
2308
2308
  "declared by something. You get a fresh empty `_voltro_…` table alongside it,",
2309
2309
  "which is the collision this ends."
2310
2310
  ].join("\n")
2311
- }), Xt = /\.events\s*\.\s*emit\s*\(/, Zt = C({
2311
+ }), $t = /\.events\s*\.\s*emit\s*\(/, en = S({
2312
2312
  version: "0.25.0",
2313
2313
  id: "0.25.0/01_events-emit-to-publish",
2314
2314
  title: "`ctx.events.emit(name, data)` → `ctx.events.publish(descriptor, key, payload)`",
2315
2315
  kind: "manual",
2316
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Xt.test(e.getFullText())),
2316
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => $t.test(e.getFullText())),
2317
2317
  note: [
2318
2318
  "`ctx.events.emit('name', data)` is gone. Declare the event and publish it:",
2319
2319
  "",
@@ -2357,53 +2357,53 @@ var x = (e, t, n) => {
2357
2357
  "",
2358
2358
  "Docs: https://docs.voltro.dev/docs/data/events"
2359
2359
  ].join("\n")
2360
- }), Qt = /* @__PURE__ */ new Set([
2360
+ }), tn = /* @__PURE__ */ new Set([
2361
2361
  "description",
2362
2362
  "version",
2363
2363
  "globalRateLimit"
2364
- ]), $t = ["defaultRetry", "defaultSigning"], B = (e) => e.getDescendantsOfKind(b.CallExpression).filter((e) => e.getExpression().getText() === "defineOutgoingEvent"), en = C({
2364
+ ]), nn = ["defaultRetry", "defaultSigning"], L = (e) => e.getDescendantsOfKind(y.CallExpression).filter((e) => e.getExpression().getText() === "defineOutgoingEvent"), rn = S({
2365
2365
  version: "0.25.0",
2366
2366
  id: "0.25.0/02_outgoing-event-to-define-event",
2367
2367
  title: "`defineOutgoingEvent` → `defineEvent({ …, webhook: { … } })`",
2368
2368
  kind: "transform",
2369
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => B(e).length > 0),
2369
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => L(e).length > 0),
2370
2370
  transform: ({ project: e, log: t }) => {
2371
2371
  let n = 0, r = /* @__PURE__ */ new Set(), i = [];
2372
- for (let t of e.getSourceFiles()) for (let e of B(t)) {
2372
+ for (let t of e.getSourceFiles()) for (let e of L(t)) {
2373
2373
  let a = e.getArguments()[0];
2374
- if (a === void 0 || !a.isKind(b.ObjectLiteralExpression)) continue;
2375
- let o = a.getProperties().filter((e) => e.isKind(b.PropertyAssignment)), s = (e) => o.find((t) => t.asKindOrThrow(b.PropertyAssignment).getName() === e)?.asKindOrThrow(b.PropertyAssignment).getInitializer()?.getText(), c = s("id"), l = s("payload");
2374
+ if (a === void 0 || !a.isKind(y.ObjectLiteralExpression)) continue;
2375
+ let o = a.getProperties().filter((e) => e.isKind(y.PropertyAssignment)), s = (e) => o.find((t) => t.asKindOrThrow(y.PropertyAssignment).getName() === e)?.asKindOrThrow(y.PropertyAssignment).getInitializer()?.getText(), c = s("id"), l = s("payload");
2376
2376
  if (c === void 0 || l === void 0) continue;
2377
- let u = [...Qt].map((e) => {
2377
+ let u = [...tn].map((e) => {
2378
2378
  let t = s(e);
2379
2379
  if (t !== void 0) return `${e === "globalRateLimit" ? "rateLimit" : e}: ${t}`;
2380
2380
  }).filter((e) => e !== void 0);
2381
- for (let e of $t) s(e) !== void 0 && i.push(`${t.getBaseName()}: ${e}`);
2381
+ for (let e of nn) s(e) !== void 0 && i.push(`${t.getBaseName()}: ${e}`);
2382
2382
  let d = u.length > 0 ? `, webhook: { ${u.join(", ")} }` : ", webhook: {}";
2383
2383
  e.replaceWithText(`defineEvent({ name: ${c}, key: Schema.Struct({}), payload: ${l}${d} })`), n++, r.add(t.getFilePath());
2384
2384
  }
2385
2385
  n !== 0 && (t(`webhooks: rewrote ${n} \`defineOutgoingEvent\` → \`defineEvent\` in ${r.size} file${r.size === 1 ? "" : "s"}`), t(" → `key: Schema.Struct({})` is CORRECT, not a placeholder: a webhook event goes to"), t(" subscribed targets, not to a routing key. Give it a key only if you also want"), t(" connected clients to subscribe per-something with `useEvent`."), t(" → import `defineEvent` from `@voltro/protocol` and `Schema` from `effect`;"), t(" the file is now a declaration, so `*.event.ts` is its home."), t(" → `ctx.webhooks.emit(descriptor, payload)` still works, and"), t(" `ctx.events.publish(descriptor, key, payload)` now reaches the HTTP targets too."), i.length > 0 && (t(` → NOT carried across (${i.length}): ${[...new Set(i)].join(", ")}`), t(" The plugin's retry/signing shapes are richer than the declared event can hold,"), t(" so they are left in place as a compile error rather than dropped silently."), t(" Configure them at subscribe time, where the full shape is typed.")));
2386
2386
  }
2387
- }), V = (e) => e.getDescendantsOfKind(b.CallExpression).filter((e) => {
2387
+ }), R = (e) => e.getDescendantsOfKind(y.CallExpression).filter((e) => {
2388
2388
  let t = e.getExpression().getText();
2389
2389
  return t === "broadcast" || t === "broadcastPlugin";
2390
- }), H = (e) => {
2390
+ }), z = (e) => {
2391
2391
  let t = e.getArguments()[0];
2392
- if (!(t === void 0 || !t.isKind(b.ObjectLiteralExpression))) return t.getProperties().filter((e) => e.isKind(b.PropertyAssignment)).map((e) => e.asKindOrThrow(b.PropertyAssignment)).find((e) => e.getName() === "channel");
2393
- }, tn = C({
2392
+ if (!(t === void 0 || !t.isKind(y.ObjectLiteralExpression))) return t.getProperties().filter((e) => e.isKind(y.PropertyAssignment)).map((e) => e.asKindOrThrow(y.PropertyAssignment)).find((e) => e.getName() === "channel");
2393
+ }, an = S({
2394
2394
  version: "0.25.0",
2395
2395
  id: "0.25.0/03_broadcast-channel-to-namespace",
2396
2396
  title: "`broadcast({ channel })` → `broadcast({ namespace })`",
2397
2397
  kind: "transform",
2398
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => V(e).some((e) => H(e) !== void 0)),
2398
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => R(e).some((e) => z(e) !== void 0)),
2399
2399
  transform: ({ project: e, log: t }) => {
2400
2400
  let n = 0, r = [], i = [], a = /* @__PURE__ */ new Set();
2401
- for (let t of e.getSourceFiles()) for (let e of V(t)) {
2402
- let o = H(e);
2401
+ for (let t of e.getSourceFiles()) for (let e of R(t)) {
2402
+ let o = z(e);
2403
2403
  if (o === void 0) continue;
2404
2404
  let s = o.getInitializer();
2405
2405
  if (s === void 0) continue;
2406
- let c = s.getText(), l = s.isKind(b.StringLiteral) ? s.getLiteralValue() : void 0;
2406
+ let c = s.getText(), l = s.isKind(y.StringLiteral) ? s.getLiteralValue() : void 0;
2407
2407
  if (l !== void 0 && l.endsWith(":changes")) {
2408
2408
  let e = l.slice(0, -8);
2409
2409
  o.replaceWithText(`namespace: '${e}'`), r.push(`${t.getBaseName()}: '${l}' → '${e}'`);
@@ -2416,7 +2416,7 @@ var x = (e, t, n) => {
2416
2416
  for (let e of i) t(` ! ${e} — carried verbatim; CHECK IT: the value is now a PREFIX the framework`), t(" appends ':changes' / ':events:<name>' / ':members' / ':presence' to.");
2417
2417
  }
2418
2418
  }
2419
- }), nn = C({
2419
+ }), on = S({
2420
2420
  version: "0.26.0",
2421
2421
  id: "0.26.0/01_event-webhook-retry-removed",
2422
2422
  title: "`defineEvent({ webhook: { retry } })` is gone — it never did anything",
@@ -2450,7 +2450,7 @@ var x = (e, t, n) => {
2450
2450
  "If you were relying on the declared value to take effect, note that it never",
2451
2451
  "did — your deliveries have been using the target default all along."
2452
2452
  ].join("\n")
2453
- }), rn = C({
2453
+ }), sn = S({
2454
2454
  version: "0.26.0",
2455
2455
  id: "0.26.0/02_step-retry-enforced",
2456
2456
  title: "workflow step `retry:` is now ENFORCED (was metadata) — drop any redundant manual retry",
@@ -2503,12 +2503,12 @@ var x = (e, t, n) => {
2503
2503
  "(retry transient, fail permanent), `maxElapsed: '5 minutes'` (a total time budget,",
2504
2504
  "not just a count), `jitter` (on by default), and `respectRetryAfter` (honor a 429)."
2505
2505
  ].join("\n")
2506
- }), an = /\bmakeSubjectId\b/, on = C({
2506
+ }), cn = /\bmakeSubjectId\b/, ln = S({
2507
2507
  version: "0.28.0",
2508
2508
  id: "0.28.0/01_notifications-resolve-subject-id-async",
2509
2509
  title: "`makeSubjectId` returns a Promise; `resolveSubjectId` may be async",
2510
2510
  kind: "manual",
2511
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => an.test(e.getFullText())),
2511
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => cn.test(e.getFullText())),
2512
2512
  note: [
2513
2513
  "`makeSubjectId` from @voltro/plugin-notifications now returns a Promise.",
2514
2514
  "",
@@ -2540,12 +2540,12 @@ var x = (e, t, n) => {
2540
2540
  " reconnect. Memoise inside your own resolver if that trade suits your app —",
2541
2541
  " you know your invalidation and we do not."
2542
2542
  ].join("\n")
2543
- }), sn = /\bPluginRefChange\b|\bisSoftDelete\s*\(|\bapplyPluginRefRules\s*\(/, cn = C({
2543
+ }), un = /\bPluginRefChange\b|\bisSoftDelete\s*\(|\bapplyPluginRefRules\s*\(/, dn = S({
2544
2544
  version: "0.28.0",
2545
2545
  id: "0.28.0/02_plugin-ref-change-event",
2546
2546
  title: "`PluginRefChange` is removed — the change channel's `ChangeEvent` is the one shape",
2547
2547
  kind: "manual",
2548
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => sn.test(e.getFullText())),
2548
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => un.test(e.getFullText())),
2549
2549
  note: [
2550
2550
  "`PluginRefChange` no longer exists. `isSoftDelete` and `applyPluginRefRules`",
2551
2551
  "take `ChangeEvent` from @voltro/database — the type the change channel actually",
@@ -2587,12 +2587,12 @@ var x = (e, t, n) => {
2587
2587
  " not know that writes a delete, and the test passes against a stream the",
2588
2588
  " framework never emits. That is the defect this change removes."
2589
2589
  ].join("\n")
2590
- }), ln = /credentialsResolver\s*:/, un = C({
2590
+ }), fn = /credentialsResolver\s*:/, pn = S({
2591
2591
  version: "0.28.0",
2592
2592
  id: "0.28.0/03_atlassian-credentials-context",
2593
2593
  title: "`credentialsResolver` receives `{ subject, store }` instead of a bare Subject",
2594
2594
  kind: "manual",
2595
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ln.test(e.getFullText())),
2595
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => fn.test(e.getFullText())),
2596
2596
  note: [
2597
2597
  "@voltro/plugin-atlassian: `credentialsResolver` now takes a context object.",
2598
2598
  "",
@@ -2662,12 +2662,12 @@ var x = (e, t, n) => {
2662
2662
  "",
2663
2663
  "and rotate anything you find — a redacted future does not un-leak a past row."
2664
2664
  ].join("\n")
2665
- }), dn = /auditPlugin\s*\(/, fn = C({
2665
+ }), mn = /auditPlugin\s*\(/, hn = S({
2666
2666
  version: "0.28.0",
2667
2667
  id: "0.28.0/04_audit-redacts-subject-metadata",
2668
2668
  title: "`auditPlugin` redacts `subject.metadata` by default",
2669
2669
  kind: "manual",
2670
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => dn.test(e.getFullText())),
2670
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => mn.test(e.getFullText())),
2671
2671
  note: [
2672
2672
  "`auditPlugin` now redacts `subject.metadata` before the sink sees the event.",
2673
2673
  "",
@@ -2723,12 +2723,12 @@ var x = (e, t, n) => {
2723
2723
  " keeps the token in the framework vault, and the resolver's new `store`",
2724
2724
  " handle covers an app with its own token table."
2725
2725
  ].join("\n")
2726
- }), pn = w("useLoaderData"), mn = C({
2726
+ }), gn = C("useLoaderData"), _n = S({
2727
2727
  version: "0.29.0",
2728
2728
  id: "0.29.0/01_loader-data-absence",
2729
2729
  title: "`useLoaderData()` throws at a level that declares no `loader`",
2730
2730
  kind: "manual",
2731
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => pn.test(e.getFullText())),
2731
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => gn.test(e.getFullText())),
2732
2732
  note: [
2733
2733
  "`useLoaderData()` used to return `undefined` at a level with no `loader`,",
2734
2734
  "which then died at `Cannot destructure property 'x' of undefined` — a",
@@ -2764,38 +2764,38 @@ var x = (e, t, n) => {
2764
2764
  "that legitimately resolves to `undefined` does not throw — only a missing",
2765
2765
  "`loader` does."
2766
2766
  ].join("\n")
2767
- }), U = "@voltro/ui-shadcn", W = "voltro:lang", hn = "voltro:locale", gn = "parsePreferenceCookies", _n = (e, t) => {
2767
+ }), B = "@voltro/ui-shadcn", V = "voltro:lang", vn = "voltro:locale", H = "parsePreferenceCookies", yn = (e, t) => {
2768
2768
  let n = [];
2769
- for (let r of e.getImportDeclarations()) if (r.getModuleSpecifierValue() === U) for (let e of r.getNamedImports()) e.getName() === t && n.push(e.getAliasNode()?.getText() ?? e.getName());
2769
+ for (let r of e.getImportDeclarations()) if (r.getModuleSpecifierValue() === B) for (let e of r.getNamedImports()) e.getName() === t && n.push(e.getAliasNode()?.getText() ?? e.getName());
2770
2770
  return n;
2771
- }, vn = (e) => {
2771
+ }, bn = (e) => {
2772
2772
  let t = 0;
2773
- for (let n of e.getSourceFiles()) n.getFullText().includes(W) && n.forEachDescendant((e) => {
2774
- if (y.isStringLiteral(e) || y.isNoSubstitutionTemplateLiteral(e) || y.isTemplateHead(e) || y.isTemplateMiddle(e) || y.isTemplateTail(e) || y.isRegularExpressionLiteral(e)) {
2773
+ for (let n of e.getSourceFiles()) n.getFullText().includes(V) && n.forEachDescendant((e) => {
2774
+ if (v.isStringLiteral(e) || v.isNoSubstitutionTemplateLiteral(e) || v.isTemplateHead(e) || v.isTemplateMiddle(e) || v.isTemplateTail(e) || v.isRegularExpressionLiteral(e)) {
2775
2775
  let n = e.getText();
2776
- if (!n.includes(W)) return;
2777
- e.replaceWithText(n.split(W).join(hn)), t++;
2776
+ if (!n.includes(V)) return;
2777
+ e.replaceWithText(n.split(V).join(vn)), t++;
2778
2778
  }
2779
2779
  });
2780
2780
  return t;
2781
- }, yn = (e) => {
2781
+ }, xn = (e) => {
2782
2782
  let t = 0, n = 0;
2783
2783
  for (let r of e.getSourceFiles()) {
2784
- let e = _n(r, gn);
2784
+ let e = yn(r, H);
2785
2785
  e.length !== 0 && r.forEachDescendant((r) => {
2786
- if (!y.isCallExpression(r) || !e.includes(r.getExpression().getText())) return;
2786
+ if (!v.isCallExpression(r) || !e.includes(r.getExpression().getText())) return;
2787
2787
  let i = r.getParent();
2788
- if (y.isPropertyAccessExpression(i) && i.getName() === "lang") {
2788
+ if (v.isPropertyAccessExpression(i) && i.getName() === "lang") {
2789
2789
  i.getNameNode().replaceWithText("locale"), t++;
2790
2790
  return;
2791
2791
  }
2792
- if (y.isVariableDeclaration(i)) {
2792
+ if (v.isVariableDeclaration(i)) {
2793
2793
  let e = i.getNameNode();
2794
- if (!y.isObjectBindingPattern(e)) return;
2794
+ if (!v.isObjectBindingPattern(e)) return;
2795
2795
  for (let t of e.getElements()) {
2796
2796
  if (t.getPropertyNameNode() !== void 0) continue;
2797
2797
  let e = t.getNameNode();
2798
- !y.isIdentifier(e) || e.getText() !== "lang" || (t.replaceWithText(`locale: ${t.getText()}`), n++);
2798
+ !v.isIdentifier(e) || e.getText() !== "lang" || (t.replaceWithText(`locale: ${t.getText()}`), n++);
2799
2799
  }
2800
2800
  }
2801
2801
  });
@@ -2804,29 +2804,29 @@ var x = (e, t, n) => {
2804
2804
  access: t,
2805
2805
  destructured: n
2806
2806
  };
2807
- }, bn = C({
2807
+ }, Sn = S({
2808
2808
  version: "0.30.0",
2809
2809
  id: "0.30.0/01_locale-cookie-rename",
2810
2810
  title: "the language-preference cookie is `voltro:locale` (was `voltro:lang`)",
2811
2811
  kind: "transform",
2812
2812
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
2813
2813
  let t = e.getFullText();
2814
- return t.includes(W) || t.includes(U) && (t.includes("LANG_COOKIE") || t.includes(gn));
2814
+ return t.includes(V) || t.includes(B) && (t.includes("LANG_COOKIE") || t.includes(H));
2815
2815
  }),
2816
2816
  transform: ({ project: e, helpers: t, log: n }) => {
2817
2817
  let r = t.renameImport(e, {
2818
- module: U,
2818
+ module: B,
2819
2819
  from: "LANG_COOKIE",
2820
2820
  to: "LOCALE_COOKIE"
2821
- }), i = vn(e), { access: a, destructured: o } = yn(e), s = [];
2821
+ }), i = bn(e), { access: a, destructured: o } = xn(e), s = [];
2822
2822
  r > 0 && s.push(`LANG_COOKIE → LOCALE_COOKIE (${r} import(s))`), i > 0 && s.push(`'voltro:lang' → 'voltro:locale' (${i} literal(s))`), a + o > 0 && s.push(`parsePreferenceCookies().lang → .locale (${a + o} site(s))`), s.length !== 0 && n(`locale cookie rename: ${s.join("; ")}. Your SOURCE is migrated. Your USERS' cookies are not — the old \`voltro:lang\` cookie in an already-visited browser is no longer read, so each user falls back to Accept-Language / defaultLocale once and re-picks their language. Nothing errors. If that reset is unacceptable, copy the old value forward at your own boot — the codemod file has the three-line bridge.` + (o > 0 ? ` ${o} destructure(s) were rewritten as \`{ locale: lang }\` to keep the local name safe from a scope collision — rename the binding yourself if you want the vocabulary to match.` : ""));
2823
2823
  }
2824
- }), xn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, Sn = C({
2824
+ }), Cn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, wn = S({
2825
2825
  version: "0.30.0",
2826
2826
  id: "0.30.0/02_audit-purge-query-recheck",
2827
2827
  title: "re-run the credential-purge query — the 0.28.0 spelling under-reported on MySQL/MariaDB",
2828
2828
  kind: "manual",
2829
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => xn.test(e.getFullText())),
2829
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Cn.test(e.getFullText())),
2830
2830
  note: [
2831
2831
  "IF YOU ALREADY RAN THE 0.28.0 PURGE CHECK ON MySQL OR MariaDB, RUN IT AGAIN.",
2832
2832
  "The query that note shipped under-reported, and it under-reported by",
@@ -2873,14 +2873,14 @@ var x = (e, t, n) => {
2873
2873
  " `connectionCredentials(...)` keeps it in the framework vault",
2874
2874
  " (see `0.28.0/03_atlassian-credentials-context`)."
2875
2875
  ].join("\n")
2876
- }), Cn = /\.webhooks\s*\.\s*emit\s*\(|useWebhooks\s*\(/, wn = C({
2876
+ }), Tn = /\.webhooks\s*\.\s*emit\s*\(|useWebhooks\s*\(/, En = S({
2877
2877
  version: "0.30.0",
2878
2878
  id: "0.30.0/03_webhook-emit-durable-deferral",
2879
2879
  title: "a deferred `webhooks.emit` rides the transactional outbox; `{ immediate: true }` opts out",
2880
2880
  kind: "manual",
2881
2881
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
2882
2882
  let t = e.getFullText();
2883
- return Cn.test(t) || w("emit").test(t);
2883
+ return Tn.test(t) || C("emit").test(t);
2884
2884
  }),
2885
2885
  note: [
2886
2886
  "A `ctx.webhooks.emit(...)` inside a MUTATION now enqueues a transactional",
@@ -2930,14 +2930,14 @@ var x = (e, t, n) => {
2930
2930
  "created by the declarative differ on the next `voltro dev` boot or",
2931
2931
  "`voltro db apply` — there is nothing to run by hand."
2932
2932
  ].join("\n")
2933
- }), Tn = /\.executionId\b|workflows\s*\.\s*start\s*\(|workflows\s*\.\s*child\s*\(/, En = C({
2933
+ }), Dn = /\.executionId\b|workflows\s*\.\s*start\s*\(|workflows\s*\.\s*child\s*\(/, On = S({
2934
2934
  version: "0.30.0",
2935
2935
  id: "0.30.0/04_workflow-run-handle-nullable-execution",
2936
2936
  title: "`WorkflowRunHandle.executionId` is `string | null`; `status` gains queued/dropped/skipped",
2937
2937
  kind: "manual",
2938
2938
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
2939
2939
  let t = e.getFullText();
2940
- return Tn.test(t) || w("start").test(t);
2940
+ return Dn.test(t) || C("start").test(t);
2941
2941
  }),
2942
2942
  note: [
2943
2943
  "A workflow start no longer always becomes a run. With declarative flow",
@@ -3002,12 +3002,12 @@ var x = (e, t, n) => {
3002
3002
  " and `singleton: { mode: 'skip' }` throws `WorkflowSingletonHeldError`",
3003
3003
  " carrying the incumbent's execution id, so you can wait on THAT run."
3004
3004
  ].join("\n")
3005
- }), Dn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, On = C({
3005
+ }), kn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, An = S({
3006
3006
  version: "0.31.0",
3007
3007
  id: "0.31.0/01_rerun-credential-scan",
3008
3008
  title: "re-run `voltro db scan-credentials` — on 0.30.0–0.30.2 it skipped the column credentials land in",
3009
3009
  kind: "manual",
3010
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Dn.test(e.getFullText())),
3010
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => kn.test(e.getFullText())),
3011
3011
  note: [
3012
3012
  "IF YOU RAN `voltro db scan-credentials` ON 0.30.0, 0.30.1 OR 0.30.2, RUN IT",
3013
3013
  "AGAIN. Its answer did not cover the column a leaked credential is most",
@@ -3041,7 +3041,7 @@ var x = (e, t, n) => {
3041
3041
  "credential on the Subject — `connectionCredentials(...)` keeps it in the",
3042
3042
  "framework vault."
3043
3043
  ].join("\n")
3044
- }), kn = C({
3044
+ }), jn = S({
3045
3045
  version: "0.32.0",
3046
3046
  id: "0.32.0/01_audit-redacts-outcome",
3047
3047
  title: "`auditPlugin` now redacts `outcome` by default — opt out if your trail reads it",
@@ -3082,7 +3082,7 @@ var x = (e, t, n) => {
3082
3082
  "purge the rows AND rotate the credentials — assume anything written to a",
3083
3083
  "log table has been read."
3084
3084
  ].join("\n")
3085
- }), An = C({
3085
+ }), Mn = S({
3086
3086
  version: "0.33.0",
3087
3087
  id: "0.33.0/01_opentelemetry-optional-peer",
3088
3088
  title: "`@effect/opentelemetry` is now an optional peer — install it if you use tracing",
@@ -3114,7 +3114,7 @@ var x = (e, t, n) => {
3114
3114
  "never wrote down. It is a startup failure, not a silent loss of traces —",
3115
3115
  "losing telemetry quietly would be the worse outcome."
3116
3116
  ].join("\n")
3117
- }), jn = C({
3117
+ }), Nn = S({
3118
3118
  version: "0.33.0",
3119
3119
  id: "0.33.0/02_coordinated-handle-gained-wake",
3120
3120
  title: "`CoordinatedScheduleHandle` gained `wake()`, `currentIntervalMs()` and `isArmed()` — update hand-written test doubles",
@@ -3164,7 +3164,7 @@ var x = (e, t, n) => {
3164
3164
  "Reporting nothing is the conservative default on purpose — a task that",
3165
3165
  "cannot tell whether it was idle must not be slowed down on a guess."
3166
3166
  ].join("\n")
3167
- }), Mn = C({
3167
+ }), Pn = S({
3168
3168
  version: "0.34.0",
3169
3169
  id: "0.34.0/01_serve-does-mount-inspect",
3170
3170
  title: "Correction: `voltro serve` DOES mount an inspect surface (the 0.12.0 note said it mounts none)",
@@ -3205,12 +3205,12 @@ var x = (e, t, n) => {
3205
3205
  "place, everyone already past 0.12.0 — which is everyone the wrong sentence",
3206
3206
  "could have misled — would never have seen it."
3207
3207
  ].join("\n")
3208
- }), Nn = (e) => /\.with\(\s*[\w.]*tenant\(/.test(e) || /\.serverOnly\(\s*\)/.test(e), Pn = C({
3208
+ }), Fn = (e) => /\.with\(\s*[\w.]*tenant\(/.test(e) || /\.serverOnly\(\s*\)/.test(e), In = S({
3209
3209
  version: "0.34.0",
3210
3210
  id: "0.34.0/02_keyed-writes-are-tenant-scoped",
3211
3211
  title: "Keyed-by-pk writes are tenant-enforced, and `crud` refuses `.serverOnly()` input",
3212
3212
  kind: "manual",
3213
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Nn(e.getFullText())),
3213
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Fn(e.getFullText())),
3214
3214
  note: [
3215
3215
  "TWO WRITE-SIDE REFUSALS ARE NEW. Neither changes a type, so `tsc` will not",
3216
3216
  "point at the call sites — these are runtime behaviour changes.",
@@ -3274,30 +3274,30 @@ var x = (e, t, n) => {
3274
3274
  " an attack look like a no-op and leaves an honest caller debugging a",
3275
3275
  " value that quietly did not land."
3276
3276
  ].join("\n")
3277
- }), Fn = /* @__PURE__ */ new Set([
3277
+ }), Ln = /* @__PURE__ */ new Set([
3278
3278
  "defineQuery",
3279
3279
  "defineMutation",
3280
3280
  "defineAction",
3281
3281
  "defineStream"
3282
- ]), In = (e) => {
3282
+ ]), Rn = (e) => {
3283
3283
  let t = e.getArguments()[0];
3284
- if (t === void 0 || !t.isKind(b.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
3284
+ if (t === void 0 || !t.isKind(y.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
3285
3285
  let n = /* @__PURE__ */ new Set();
3286
3286
  for (let e of t.getProperties()) {
3287
- let t = (e.asKind(b.PropertyAssignment) ?? e.asKind(b.ShorthandPropertyAssignment))?.getName?.();
3287
+ let t = (e.asKind(y.PropertyAssignment) ?? e.asKind(y.ShorthandPropertyAssignment))?.getName?.();
3288
3288
  t !== void 0 && n.add(t);
3289
3289
  }
3290
3290
  return n;
3291
- }, Ln = (e, t) => /define(Query|Mutation|Action|Stream)\s*\(/.test(e) ? t.some((e) => {
3292
- if (!Fn.has(e.getExpression().getText())) return !1;
3293
- let t = In(e);
3291
+ }, zn = (e, t) => /define(Query|Mutation|Action|Stream)\s*\(/.test(e) ? t.some((e) => {
3292
+ if (!Ln.has(e.getExpression().getText())) return !1;
3293
+ let t = Rn(e);
3294
3294
  return !t.has("internal") && !t.has("guards") && !t.has("openAccess");
3295
- }) : !1, Rn = C({
3295
+ }) : !1, Bn = S({
3296
3296
  version: "0.34.0",
3297
3297
  id: "0.34.0/03_procedure-access-decision",
3298
3298
  title: "Every wire-exposed procedure declares an access decision (`guards:` or `openAccess:`)",
3299
3299
  kind: "manual",
3300
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ln(e.getFullText(), e.getDescendantsOfKind(b.CallExpression))),
3300
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => zn(e.getFullText(), e.getDescendantsOfKind(y.CallExpression))),
3301
3301
  note: [
3302
3302
  "YOUR APP WILL NOT BOOT UNTIL EVERY WIRE-EXPOSED PROCEDURE DECIDES WHO MAY",
3303
3303
  "CALL IT. This is the single largest behaviour change in this release, and it",
@@ -3342,18 +3342,18 @@ var x = (e, t, n) => {
3342
3342
  "procedures your PLUGINS declare — the gate reads your app's own discovered",
3343
3343
  "files only, so adopting this does not turn into a plugin bug report."
3344
3344
  ].join("\n")
3345
- }), zn = [
3345
+ }), Vn = [
3346
3346
  "workosAuthorizationUrl",
3347
3347
  "workosAuthenticateWithCode",
3348
3348
  "workosBeginLogin"
3349
- ], Bn = C({
3349
+ ], Hn = S({
3350
3350
  version: "0.34.0",
3351
3351
  id: "0.34.0/04_workos-oauth-state-pkce",
3352
3352
  title: "WorkOS hosted login: `workosAuthorizationUrl` → `workosBeginLogin`, with mandatory `state` + PKCE",
3353
3353
  kind: "manual",
3354
3354
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
3355
3355
  let t = e.getFullText();
3356
- return t.includes("@voltro/plugin-auth-workos") && zn.some((e) => t.includes(e));
3356
+ return t.includes("@voltro/plugin-auth-workos") && Vn.some((e) => t.includes(e));
3357
3357
  }),
3358
3358
  note: [
3359
3359
  "YOUR WORKOS HOSTED-LOGIN FLOW HAD NO CSRF PROTECTION BY DEFAULT, AND NO PKCE.",
@@ -3411,12 +3411,12 @@ var x = (e, t, n) => {
3411
3411
  "are accepted on the existing endpoints; your registered redirect URI is",
3412
3412
  "unchanged."
3413
3413
  ].join("\n")
3414
- }), Vn = /mirrorTables|_?voltro_mirror_/, Hn = C({
3414
+ }), Un = /mirrorTables|_?voltro_mirror_/, Wn = S({
3415
3415
  version: "0.34.0",
3416
3416
  id: "0.34.0/05_analytics-mirror-versioned-writes",
3417
3417
  title: "Analytics CDC-mirror: version-ordered writes, tombstoned deletes, retry + repair",
3418
3418
  kind: "manual",
3419
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Vn.test(e.getFullText())),
3419
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Un.test(e.getFullText())),
3420
3420
  note: [
3421
3421
  "The analytics CDC-mirror (`mirrorTables`) now orders every write and",
3422
3422
  "retries the ones that fail. Three things to do.",
@@ -3471,7 +3471,7 @@ var x = (e, t, n) => {
3471
3471
  " VOLTRO_ANALYTICS_MIRROR_REPAIR_INTERVAL_MS (60000, 0 disables)",
3472
3472
  " VOLTRO_ANALYTICS_MIRROR_REPAIR_QUEUE_LIMIT (10000)"
3473
3473
  ].join("\n")
3474
- }), Un = C({
3474
+ }), Gn = S({
3475
3475
  version: "0.34.0",
3476
3476
  id: "0.34.0/06_origin-guard-and-trusted-proxies",
3477
3477
  title: "Cross-site protection, trusted proxies and security headers are ON by default on the api listener",
@@ -3595,7 +3595,7 @@ var x = (e, t, n) => {
3595
3595
  " a `Transfer-Encoding: chunked` body skipped it and was buffered without",
3596
3596
  " bound. No action needed; VOLTRO_MAX_RPC_BODY_BYTES still tunes it."
3597
3597
  ].join("\n")
3598
- }), Wn = C({
3598
+ }), Kn = S({
3599
3599
  version: "0.34.0",
3600
3600
  id: "0.34.0/07_incoming-webhook-declares-verification",
3601
3601
  title: "An incoming webhook with no signature scheme now refuses to boot",
@@ -3644,7 +3644,7 @@ var x = (e, t, n) => {
3644
3644
  "value — the sender holds the other half of it, so a generated one would",
3645
3645
  "authenticate nobody."
3646
3646
  ].join("\n")
3647
- }), Gn = C({
3647
+ }), qn = S({
3648
3648
  version: "0.34.0",
3649
3649
  id: "0.34.0/08_init-initialises-the-workspace",
3650
3650
  title: "`voltro init` initialises the workspace root; scaffolding is `create-project` only",
@@ -3696,20 +3696,20 @@ var x = (e, t, n) => {
3696
3696
  "all 18 of its scripts — including the `pnpm db:up` the CLI then printed as a",
3697
3697
  "next step. Run `voltro init` in the root first."
3698
3698
  ].join("\n")
3699
- }), Kn = (e, t) => (e.getProperty(t)?.asKind(b.PropertyAssignment))?.getInitializer()?.asKind(b.ObjectLiteralExpression), qn = (e) => {
3699
+ }), Jn = (e, t) => (e.getProperty(t)?.asKind(y.PropertyAssignment))?.getInitializer()?.asKind(y.ObjectLiteralExpression), Yn = (e) => {
3700
3700
  if (e.getExpression().getText() !== "workflow") return !1;
3701
- let t = e.getArguments()[0]?.asKind(b.ObjectLiteralExpression);
3701
+ let t = e.getArguments()[0]?.asKind(y.ObjectLiteralExpression);
3702
3702
  if (t === void 0) return !1;
3703
- let n = Kn(t, "messages");
3703
+ let n = Jn(t, "messages");
3704
3704
  return n !== void 0 && n.getProperty("queries") !== void 0;
3705
- }, Jn = C({
3705
+ }, Xn = S({
3706
3706
  version: "0.34.0",
3707
3707
  id: "0.34.0/09_workflow-messages-queries-removed",
3708
3708
  title: "`workflow({ messages: { queries } })` is removed — it never had a send path",
3709
3709
  kind: "manual",
3710
3710
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
3711
3711
  let t = e.getFullText();
3712
- return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(b.CallExpression).some(qn);
3712
+ return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(y.CallExpression).some(Yn);
3713
3713
  }),
3714
3714
  note: [
3715
3715
  "YOU DECLARED `messages: { queries: { … } }` ON A WORKFLOW. That channel is",
@@ -3752,7 +3752,7 @@ var x = (e, t, n) => {
3752
3752
  "in-flight runs finish on the old path after a deploy. See the Versioning",
3753
3753
  "page in the docs."
3754
3754
  ].join("\n")
3755
- }), Yn = C({
3755
+ }), Zn = S({
3756
3756
  version: "0.34.0",
3757
3757
  id: "0.34.0/11_ai-flows-per-step-human-signal",
3758
3758
  title: "`HUMAN_RESPONSE_SIGNAL` removed — a human step's signal name is now per-step",
@@ -3796,23 +3796,23 @@ var x = (e, t, n) => {
3796
3796
  "per flow (`humanTimeoutMs`), per app (`aiFlowsPlugin({ humanReviewTimeoutMs })`)",
3797
3797
  "or per deploy (`VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS`). `0` = wait forever."
3798
3798
  ].join("\n")
3799
- }), Xn = /* @__PURE__ */ new Set([
3799
+ }), Qn = /* @__PURE__ */ new Set([
3800
3800
  "@voltro/plugin-auth",
3801
3801
  "@voltro/protocol/session",
3802
3802
  "@voltro/plugin-sso-saml"
3803
- ]), Zn = /* @__PURE__ */ new Set(["issueSession", "signSession"]), Qn = (e) => {
3803
+ ]), $n = /* @__PURE__ */ new Set(["issueSession", "signSession"]), er = (e) => {
3804
3804
  let t = e.getFullText();
3805
3805
  if (!/issueSession|signSession|resolveScopes|@voltro\/(plugin-auth|protocol\/session|plugin-sso-saml)/.test(t)) return !1;
3806
- for (let t of e.getImportDeclarations()) if (Xn.has(t.getModuleSpecifierValue())) return !0;
3807
- for (let t of e.getDescendantsOfKind(b.CallExpression)) if (Zn.has(t.getExpression().getText())) return !0;
3808
- for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
3806
+ for (let t of e.getImportDeclarations()) if (Qn.has(t.getModuleSpecifierValue())) return !0;
3807
+ for (let t of e.getDescendantsOfKind(y.CallExpression)) if ($n.has(t.getExpression().getText())) return !0;
3808
+ for (let t of e.getDescendantsOfKind(y.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
3809
3809
  return !1;
3810
- }, $n = C({
3810
+ }, tr = S({
3811
3811
  version: "0.34.0",
3812
3812
  id: "0.34.0/12_session-cookie-carries-identity",
3813
3813
  title: "The session cookie carries identity only — authority resolves per request",
3814
3814
  kind: "manual",
3815
- appliesTo: ({ project: e }) => e.getSourceFiles().some(Qn),
3815
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(er),
3816
3816
  note: [
3817
3817
  "YOUR USERS WILL SIGN IN ONCE MORE, AND A SESSION COOKIE CAN NO LONGER",
3818
3818
  "CARRY PERMISSIONS.",
@@ -3903,12 +3903,12 @@ var x = (e, t, n) => {
3903
3903
  "not have: an authoritative resolver now overrides a token's own scope",
3904
3904
  "claim, which union-only could never do."
3905
3905
  ].join("\n")
3906
- }), er = /\bMockEmail\b|\.email\.(sent|lastTo|clear)\b|\bSentEmail\b/, tr = C({
3906
+ }), nr = /\bMockEmail\b|\.email\.(sent|lastTo|clear)\b|\bSentEmail\b/, rr = S({
3907
3907
  version: "0.34.0",
3908
3908
  id: "0.34.0/10_testing-mail-is-a-service",
3909
3909
  title: "`ctx.email` / `MockEmail` removed — assert mail through the mail plugin",
3910
3910
  kind: "manual",
3911
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => er.test(e.getFullText())),
3911
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => nr.test(e.getFullText())),
3912
3912
  note: [
3913
3913
  "`@voltro/testing` no longer ships `MockEmail`, and `makeTestContext()` no",
3914
3914
  "longer returns `ctx.email`. Your tests that touch it will not compile.",
@@ -3977,12 +3977,12 @@ var x = (e, t, n) => {
3977
3977
  " • `makeTestContext({ layers })` — your app's own `app.config.ts`",
3978
3978
  " `layers:` now resolve for an Effect-mode handler under test."
3979
3979
  ].join("\n")
3980
- }), nr = /from\s+['"]@voltro\/plugin-billing(?:\/[\w-]+)?['"]/, rr = C({
3980
+ }), ir = /from\s+['"]@voltro\/plugin-billing(?:\/[\w-]+)?['"]/, ar = S({
3981
3981
  version: "0.34.0",
3982
3982
  id: "0.34.0/13_billing-provider-dunning",
3983
3983
  title: "Billing dunning — `BillingProvider.fetchSubscription` is required, and `plan()` now honours a grace period",
3984
3984
  kind: "manual",
3985
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => nr.test(e.getFullText())),
3985
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ir.test(e.getFullText())),
3986
3986
  note: [
3987
3987
  "BILLING GAINED A DUNNING SEQUENCE. TWO THINGS CHANGED UNDER YOU.",
3988
3988
  "",
@@ -4058,12 +4058,12 @@ var x = (e, t, n) => {
4058
4058
  "`pastDueSince` / `statusEventAt` columns ride the declarative differ — they",
4059
4059
  "are applied by `voltro db apply` and by a `voltro dev` boot, on every dialect."
4060
4060
  ].join("\n")
4061
- }), ir = /from\s+['"]@voltro\/plugin-search(?:\/[\w-]+)?['"]/, ar = C({
4061
+ }), or = /from\s+['"]@voltro\/plugin-search(?:\/[\w-]+)?['"]/, sr = S({
4062
4062
  version: "0.34.0",
4063
4063
  id: "0.34.0/14_search-memory-backend-refuses-production",
4064
4064
  title: "`plugin-search` on the in-memory backend now refuses to boot in production",
4065
4065
  kind: "manual",
4066
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ir.test(e.getFullText())),
4066
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => or.test(e.getFullText())),
4067
4067
  note: [
4068
4068
  "THE ZERO-CONFIG SEARCH BACKEND NO LONGER BOOTS IN PRODUCTION.",
4069
4069
  "",
@@ -4132,7 +4132,7 @@ var x = (e, t, n) => {
4132
4132
  "(`ts_headline` / `snippet()` / …) that `SearchQuery` promises. Degrading",
4133
4133
  "those silently would replace one lie with another."
4134
4134
  ].join("\n")
4135
- }), or = C({
4135
+ }), cr = S({
4136
4136
  version: "0.34.0",
4137
4137
  id: "0.34.0/15_mysql-mssql-tls-honoured",
4138
4138
  title: "mysql/mariadb and mssql connections honour ?ssl= / ?sslmode= — or refuse to boot",
@@ -4204,7 +4204,7 @@ var x = (e, t, n) => {
4204
4204
  "spins in-tick and starves the event loop. Set it only if you know nothing in",
4205
4205
  "your process does that, and set it well above their peak concurrency."
4206
4206
  ].join("\n")
4207
- }), sr = C({
4207
+ }), lr = S({
4208
4208
  version: "0.34.0",
4209
4209
  id: "0.34.0/16_declare-node-env-for-migration-commands",
4210
4210
  title: "`voltro db apply` / `voltro migrate` now treat an unset NODE_ENV as production",
@@ -4268,12 +4268,12 @@ var x = (e, t, n) => {
4268
4268
  "process environment if you want a non-production `voltro serve` without a",
4269
4269
  "built bundle. Everything downstream of the launcher does honour `.env`."
4270
4270
  ].join("\n")
4271
- }), cr = /\/(?:src\/)?pages\//, lr = /\bcacheInvalidatesOn\b/, ur = C({
4271
+ }), ur = /\/(?:src\/)?pages\//, dr = /\bcacheInvalidatesOn\b/, fr = S({
4272
4272
  version: "0.34.0",
4273
4273
  id: "0.34.0/17_isr-postgres-refuses-without-a-database",
4274
4274
  title: "The postgres ISR cache + CDC invalidator read DB_URL, and no longer fall back silently",
4275
4275
  kind: "manual",
4276
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => lr.test(e.getFullText()) || cr.test(e.getFilePath())),
4276
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => dr.test(e.getFullText()) || ur.test(e.getFilePath())),
4277
4277
  note: [
4278
4278
  "YOUR WEB PROCESS MAY HAVE BEEN USING THE MEMORY ISR CACHE ALL ALONG.",
4279
4279
  "",
@@ -4318,12 +4318,12 @@ var x = (e, t, n) => {
4318
4318
  " until their `revalidate` window expires. Give the web process a `DB_URL`",
4319
4319
  " or remove the declaration."
4320
4320
  ].join("\n")
4321
- }), dr = /\.migration\.ts$/, fr = C({
4321
+ }), pr = /\.migration\.ts$/, mr = S({
4322
4322
  version: "0.34.0",
4323
4323
  id: "0.34.0/18_serve-refuses-pending-file-migrations",
4324
4324
  title: "`voltro serve` refuses to boot with pending file-based migrations",
4325
4325
  kind: "manual",
4326
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => dr.test(e.getFilePath())),
4326
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => pr.test(e.getFilePath())),
4327
4327
  note: [
4328
4328
  "YOUR DEPLOY MUST RUN `migrations/*.migration.ts` BEFORE `voltro serve`.",
4329
4329
  "",
@@ -4366,11 +4366,11 @@ var x = (e, t, n) => {
4366
4366
  "fingerprint check — it is the one switch for \"no boot-time schema checks\",",
4367
4367
  "and it now covers both."
4368
4368
  ].join("\n")
4369
- }), pr = /* @__PURE__ */ new Set([
4369
+ }), hr = /* @__PURE__ */ new Set([
4370
4370
  "@voltro/plugin-auth",
4371
4371
  "@voltro/plugin-auth/schema",
4372
4372
  "@voltro/plugin-auth/plugin"
4373
- ]), mr = /* @__PURE__ */ new Set([
4373
+ ]), gr = /* @__PURE__ */ new Set([
4374
4374
  "UserStore",
4375
4375
  "authTables",
4376
4376
  "SendEmail",
@@ -4378,20 +4378,20 @@ var x = (e, t, n) => {
4378
4378
  "memoryUserStore",
4379
4379
  "postgresUserStore",
4380
4380
  "authRoutesPlugin"
4381
- ]), hr = (e) => {
4381
+ ]), _r = (e) => {
4382
4382
  if (!e.getFullText().includes("@voltro/plugin-auth")) return !1;
4383
- for (let t of e.getImportDeclarations()) if (pr.has(t.getModuleSpecifierValue())) {
4384
- for (let e of t.getNamedImports()) if (mr.has(e.getName())) return !0;
4383
+ for (let t of e.getImportDeclarations()) if (hr.has(t.getModuleSpecifierValue())) {
4384
+ for (let e of t.getNamedImports()) if (gr.has(e.getName())) return !0;
4385
4385
  if (t.getNamespaceImport() !== void 0) return !0;
4386
4386
  }
4387
- for (let t of e.getDescendantsOfKind(b.TypeReference)) if (t.getTypeName().getText() === "UserStore") return !0;
4387
+ for (let t of e.getDescendantsOfKind(y.TypeReference)) if (t.getTypeName().getText() === "UserStore") return !0;
4388
4388
  return !1;
4389
- }, gr = C({
4389
+ }, vr = S({
4390
4390
  version: "0.34.0",
4391
4391
  id: "0.34.0/20_user-store-auth-feature-methods",
4392
4392
  title: "UserStore gained the email-verification, invitation and impersonation methods",
4393
4393
  kind: "manual",
4394
- appliesTo: ({ project: e }) => e.getSourceFiles().some(hr),
4394
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(_r),
4395
4395
  note: [
4396
4396
  "IF YOU USE memoryUserStore OR postgresUserStore, THERE IS NOTHING TO DO.",
4397
4397
  "Both already implement everything below. Read on only if you wrote your",
@@ -4471,26 +4471,26 @@ var x = (e, t, n) => {
4471
4471
  " is the difference between \"new signups must confirm\" and \"everyone is",
4472
4472
  " locked out until they check their mail\"."
4473
4473
  ].join("\n")
4474
- }), _r = /* @__PURE__ */ new Set([
4474
+ }), U = /* @__PURE__ */ new Set([
4475
4475
  "bindConnectionSubject",
4476
4476
  "getConnectionSubject",
4477
4477
  "unbindConnectionSubject",
4478
4478
  "onBindConnectionSubject",
4479
4479
  "connectionSubjectsSnapshot"
4480
- ]), vr = (e) => {
4480
+ ]), yr = (e) => {
4481
4481
  let t = e.getFullText();
4482
4482
  if (!/ConnectionSubject|connectionSubjectsSnapshot|rebind/.test(t)) return !1;
4483
- for (let t of e.getDescendantsOfKind(b.CallExpression)) if (_r.has(t.getExpression().getText())) return !0;
4484
- for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (_r.has(e.getName())) return !0;
4485
- for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "rebind") return !0;
4486
- for (let t of e.getDescendantsOfKind(b.ShorthandPropertyAssignment)) if (t.getName() === "rebind") return !0;
4483
+ for (let t of e.getDescendantsOfKind(y.CallExpression)) if (U.has(t.getExpression().getText())) return !0;
4484
+ for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (U.has(e.getName())) return !0;
4485
+ for (let t of e.getDescendantsOfKind(y.PropertyAssignment)) if (t.getName() === "rebind") return !0;
4486
+ for (let t of e.getDescendantsOfKind(y.ShorthandPropertyAssignment)) if (t.getName() === "rebind") return !0;
4487
4487
  return !1;
4488
- }, yr = C({
4488
+ }, br = S({
4489
4489
  version: "0.34.0",
4490
4490
  id: "0.34.0/19_connection-override-carries-a-credential",
4491
4491
  title: "Soft re-auth presents a credential — it no longer hands the server a Subject",
4492
4492
  kind: "manual",
4493
- appliesTo: ({ project: e }) => e.getSourceFiles().some(vr),
4493
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(yr),
4494
4494
  note: [
4495
4495
  "A LIVE CONNECTION CAN NO LONGER BE HANDED A SUBJECT. IT PRESENTS A",
4496
4496
  "CREDENTIAL, AND THE SERVER RESOLVES IT LIKE ANY OTHER REQUEST.",
@@ -4575,19 +4575,19 @@ var x = (e, t, n) => {
4575
4575
  "it behaves identically under `voltro dev` and `voltro serve`, because both",
4576
4576
  "now build the middleware from one function (`makeAuthMiddlewareLayers`)."
4577
4577
  ].join("\n")
4578
- }), br = (e) => {
4578
+ }), xr = (e) => {
4579
4579
  if (/\.workflow(\.server)?\.tsx?$/.test(e.getBaseName())) return !0;
4580
4580
  let t = e.getFullText();
4581
4581
  if (!/resolveScopes|defineWorkflow|ctx\.workflows/.test(t)) return !1;
4582
- for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
4583
- for (let t of e.getDescendantsOfKind(b.CallExpression)) if (t.getExpression().getText() === "defineWorkflow") return !0;
4582
+ for (let t of e.getDescendantsOfKind(y.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
4583
+ for (let t of e.getDescendantsOfKind(y.CallExpression)) if (t.getExpression().getText() === "defineWorkflow") return !0;
4584
4584
  return !1;
4585
- }, xr = C({
4585
+ }, Sr = S({
4586
4586
  version: "0.34.0",
4587
4587
  id: "0.34.0/20_workflow-authority-resolves-at-resume",
4588
4588
  title: "A resumed workflow re-resolves its caller's authority instead of restoring it",
4589
4589
  kind: "manual",
4590
- appliesTo: ({ project: e }) => e.getSourceFiles().some(br),
4590
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(xr),
4591
4591
  note: [
4592
4592
  "A WORKFLOW NO LONGER CARRIES ITS CALLER'S PERMISSIONS FORWARD. IT CARRIES",
4593
4593
  "THEIR IDENTITY, AND ASKS YOUR APP WHAT THEY MAY DO WHEN IT RUNS.",
@@ -4652,7 +4652,7 @@ var x = (e, t, n) => {
4652
4652
  " contain scopes and are stripped on READ — a resumed run cannot re-assert",
4653
4653
  " authority the old build persisted."
4654
4654
  ].join("\n")
4655
- }), Sr = /* @__PURE__ */ new Set([
4655
+ }), Cr = /* @__PURE__ */ new Set([
4656
4656
  "listTag",
4657
4657
  "createTag",
4658
4658
  "updateTag",
@@ -4660,21 +4660,21 @@ var x = (e, t, n) => {
4660
4660
  "createScope",
4661
4661
  "writeScope",
4662
4662
  "deleteScope"
4663
- ]), Cr = (e) => {
4663
+ ]), wr = (e) => {
4664
4664
  let t = e.getFullText();
4665
4665
  if (!/EntityAdminSpec|deriveEntityAdmins|Tag\b|Scope\b/.test(t)) return !1;
4666
4666
  for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) {
4667
4667
  let t = e.getName();
4668
4668
  if (t === "EntityAdminSpec" || t === "deriveEntityAdmins") return !0;
4669
4669
  }
4670
- for (let t of e.getDescendantsOfKind(b.PropertyAccessExpression)) if (Sr.has(t.getName())) return !0;
4670
+ for (let t of e.getDescendantsOfKind(y.PropertyAccessExpression)) if (Cr.has(t.getName())) return !0;
4671
4671
  return !1;
4672
- }, wr = C({
4672
+ }, Tr = S({
4673
4673
  version: "0.34.0",
4674
4674
  id: "0.34.0/21_admin-gates-on-declared-access",
4675
4675
  title: "The derived admin gates on the api's DECLARED access — the invented `<table>:create` scopes are gone",
4676
4676
  kind: "manual",
4677
- appliesTo: ({ project: e }) => e.getSourceFiles().some(Cr),
4677
+ appliesTo: ({ project: e }) => e.getSourceFiles().some(wr),
4678
4678
  note: [
4679
4679
  "YOUR ADMIN WAS GATING ON SCOPE STRINGS NOTHING PRODUCED.",
4680
4680
  "",
@@ -4764,14 +4764,14 @@ var x = (e, t, n) => {
4764
4764
  "from \"undeclared\", so an intentionally public action rendered as forbidden",
4765
4765
  "to everybody."
4766
4766
  ].join("\n")
4767
- }), Tr = /\b(useMutation|useAction|useWorkflow|useWorkflowSignal|useWorkflowUpdate|useUpload)\b/, Er = /FiberFailure|Cause\.squash|squashWith/, Dr = C({
4767
+ }), Er = /\b(useMutation|useAction|useWorkflow|useWorkflowSignal|useWorkflowUpdate|useUpload)\b/, Dr = /FiberFailure|Cause\.squash|squashWith/, Or = S({
4768
4768
  version: "0.34.0",
4769
4769
  id: "0.34.0/22_write-rejections-carry-the-typed-error",
4770
4770
  title: "A rejected mutate/run/signal/upload carries the typed error, not a FiberFailure",
4771
4771
  kind: "manual",
4772
4772
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
4773
4773
  let t = e.getFullText();
4774
- return Tr.test(t) || Er.test(t);
4774
+ return Er.test(t) || Dr.test(t);
4775
4775
  }),
4776
4776
  note: [
4777
4777
  "WHAT CHANGED. A write hook that rejects now rejects with the typed error",
@@ -4824,12 +4824,12 @@ var x = (e, t, n) => {
4824
4824
  "contract, reject from a real rpc client, or assert on the value your",
4825
4825
  "component branches on rather than on the literal the test wrote."
4826
4826
  ].join("\n")
4827
- }), Or = /@voltro\/plugin-sso-saml|samlSsoPlugin/, kr = C({
4827
+ }), kr = /@voltro\/plugin-sso-saml|samlSsoPlugin/, Ar = S({
4828
4828
  version: "0.34.0",
4829
4829
  id: "0.34.0/23_saml-signature-and-replay-defaults",
4830
4830
  title: "SAML: response-signature requirement relaxed + surfaced; replay protection ON by default",
4831
4831
  kind: "manual",
4832
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Or.test(e.getFullText())),
4832
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => kr.test(e.getFullText())),
4833
4833
  note: [
4834
4834
  "Two defaults in `samlSsoPlugin` moved. Read both — they interact.",
4835
4835
  "",
@@ -4920,58 +4920,58 @@ var x = (e, t, n) => {
4920
4920
  "request, and the envelope is unauthenticated. If you must run unsolicited,",
4921
4921
  "set `wantAuthnResponseSigned: true`."
4922
4922
  ].join("\n")
4923
- }), Ar = /\b(signPayload|verifySignature)\s*\(/, jr = C({
4923
+ }), jr = /\b(signPayload|verifySignature)\s*\(/, Mr = S({
4924
4924
  version: "0.34.0",
4925
4925
  id: "0.34.0/24_webhook-signing-renders-headers",
4926
4926
  title: "Webhook signing: `signPayload`/`verifySignature` → `signRequest`/`verifyRequest`",
4927
4927
  kind: "manual",
4928
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ar.test(e.getFullText())),
4928
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => jr.test(e.getFullText())),
4929
4929
  note: "A webhook signature scheme now renders a header MAP rather than one header,\nand verifies against the request's whole header map.\n\n - signPayload(scheme, rawBody, secret)\n → signRequest(scheme, { rawBody, secret, messageId })\n returns { headers } instead of { headerName, headerValue }\n\n - verifySignature(scheme, rawBody, secret, headerValue)\n → verifyRequest(scheme, { rawBody, secret, headers })\n\nRewrite each call site:\n\n // before\n const { headerName, headerValue } = signPayload(scheme, body, secret)\n await fetch(url, { headers: { [headerName]: headerValue }, body })\n\n // after\n const { headers } = signRequest(scheme, { rawBody: body, secret, messageId: deliveryId })\n await fetch(url, { headers, body })\n\nWHY `messageId` IS REQUIRED, AND WHY THIS IS NOT AUTOMATED:\n\n Standard Webhooks signs over `<messageId>.<timestamp>.<body>` and sends the\n id as `webhook-id` — which is also the value the spec tells consumers to\n use as their IDEMPOTENCY KEY. Pass the id that identifies the message to\n THAT receiver, stable across retries (the framework passes its delivery\n id). A fresh `randomUUID()` per attempt compiles and reads fine, and\n quietly makes every retry look like a new message to the receiver.\n\nTHE DEFAULT OUTGOING SCHEME CHANGED:\n\n `defaultOutgoingSignature()` now returns Standard Webhooks v1.0.0 —\n `webhook-id` / `webhook-timestamp` / `webhook-signature: v1,<base64>` — so\n a NEW subscription signs to an interoperable spec every conformant\n consumer library already verifies. Existing target rows are untouched:\n each stores the scheme it was created with.\n\n Its key serialization differs too: `whsec_` + base64 of 24..64 random\n bytes, HMAC-ing the DECODED bytes. `subscribe()` mints the right shape\n automatically; passing your own hex secret for a standard-webhooks target\n is REFUSED at subscribe rather than producing signatures no consumer\n accepts.\n\n To keep the previous house format on a new subscription, pass it\n explicitly: `webhooks.subscribe({ …, signing: genericHmacSignature() })`\n — the same shape as before, under a name that says it is a choice.\n\nALSO IN THIS RELEASE, no code change needed:\n\n - a 3xx response is a failure and redirects are no longer FOLLOWED (they\n were, by fetch's default — which also walked past the SSRF guard);\n - a `410 Gone` disables the target immediately, whatever the\n consecutive-failure threshold says;\n - each attempt has a 30s wire timeout (`VOLTRO_WEBHOOK_TIMEOUT_MS`, or\n `timeoutMs`). There was none, so a receiver that accepted the connection\n and never answered held the delivery workflow open indefinitely.\n\nAnd `slackSignature()` is now a REAL verifier. It shipped as a stub that\nreturned false unconditionally, workable only through a header-packing\nconvention in the route mounter. A scheme now receives the whole header map\nand reads `x-slack-request-timestamp` itself."
4930
- }), Mr = /from\s+['"]@voltro\/plugin-presence(?:\/[a-z]+)?['"]/, Nr = /\b(lastSeen|presenceTable|memoryPresenceStore|isOnline|filterOnline|staleKeys|PresenceStore|PresenceEntry|PresenceTracker)\b/, Pr = /_voltro_presence/, Fr = C({
4930
+ }), Nr = /from\s+['"]@voltro\/plugin-presence(?:\/[a-z]+)?['"]/, Pr = /\b(lastSeen|presenceTable|memoryPresenceStore|isOnline|filterOnline|staleKeys|PresenceStore|PresenceEntry|PresenceTracker)\b/, Fr = /_voltro_presence/, Ir = S({
4931
4931
  version: "0.34.0",
4932
4932
  id: "0.34.0/25_presence-has-no-table",
4933
4933
  title: "Presence: `_voltro_presence` is gone, and so is `lastSeen` on the roster",
4934
4934
  kind: "manual",
4935
4935
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
4936
4936
  let t = e.getFullText();
4937
- return Pr.test(t) || Mr.test(t) && Nr.test(t);
4937
+ return Fr.test(t) || Nr.test(t) && Pr.test(t);
4938
4938
  }),
4939
4939
  note: "@voltro/plugin-presence no longer declares a table. Four things moved.\n\n1. A roster member is now { key, meta } — there is NO `lastSeen`.\n\n It was the OWNING replica's clock, so on more than one replica \"active 3\n minutes ago\" rendered from it was wrong by the skew between two pods, with\n nothing to say so. It was also the only field a plain heartbeat changed,\n which is what made the roster push on every beat instead of on every change.\n\n // before\n members.map((m) => `${m.key} — ${ago(m.lastSeen)}`)\n // after: put anything time-shaped you need into meta\n usePresence(channel, { meta: { joinedAt: Date.now() } })\n members.map((m) => `${m.key} — ${ago(m.meta?.joinedAt)}`)\n\n2. `PresenceTracker.track()` and `.untrack()` return { delta, changed }.\n\n // before\n bus.announce(tracker.track({ channel, tenantId, key }))\n // after — announce ALWAYS (a peer's sweep needs the liveness signal),\n // push only on a change\n const write = tracker.track({ channel, tenantId, key })\n bus.announce(write.delta)\n if (write.changed) publishReactivity(store, presenceRoster)\n\n Do not blind-append `.delta` and move on: dropping `changed` restores the\n per-heartbeat push this release removed, and nothing will report it.\n\n `attachPresenceBus` also takes an optional `onRosterChanged` now — call it\n if you drive the bus yourself, or a peer's join and a peer's death will\n update your tracker and push nobody.\n\n3. These exports are DELETED, with no replacement:\n\n presenceTable, PresenceStore, PresenceEntry,\n memoryPresenceStore, isOnline, filterOnline, staleKeys\n\n They were the table era's read model — an abstraction over rows plus the\n staleness filter a row needed because it outlived the client that wrote it.\n Presence has neither. Reading the roster is `presence.list` / `usePresence`;\n the staleness sweep is the plugin's own and needs nothing from you.\n\n4. If your `source:` names '_voltro_presence' as a string, point it at the\n channel object instead:\n\n import { presenceRoster } from '@voltro/plugin-presence'\n defineQuery({ name: 'room.summary', source: presenceRoster, ... })\n\n And to push it yourself:\n\n import { publishReactivity } from '@voltro/protocol'\n publishReactivity(ctx.store, presenceRoster)\n\nTHE TABLE IS NOT DROPPED FOR YOU. The declarative differ never plans a drop for\na framework table no app declares — deliberately, so a live `_voltro_*` table is\nnever destroyed by an app that simply does not know about it. So an existing\n`_voltro_presence` stays, empty, until you remove it:\n\n DROP TABLE IF EXISTS _voltro_presence;\n\nIt holds no data anyone can use: nothing has written a row to it since presence\nmoved into memory."
4940
- }), Ir = (e) => {
4940
+ }), Lr = (e) => {
4941
4941
  let t = e.getArguments()[0];
4942
- if (t === void 0 || !t.isKind(b.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
4942
+ if (t === void 0 || !t.isKind(y.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
4943
4943
  let n = /* @__PURE__ */ new Set();
4944
4944
  for (let e of t.getProperties()) {
4945
- let t = (e.asKind(b.PropertyAssignment) ?? e.asKind(b.ShorthandPropertyAssignment))?.getName?.();
4945
+ let t = (e.asKind(y.PropertyAssignment) ?? e.asKind(y.ShorthandPropertyAssignment))?.getName?.();
4946
4946
  t !== void 0 && n.add(t);
4947
4947
  }
4948
4948
  return n;
4949
- }, Lr = (e, t) => /defineEvent\s*\(/.test(e) ? t.some((e) => {
4949
+ }, Rr = (e, t) => /defineEvent\s*\(/.test(e) ? t.some((e) => {
4950
4950
  if (e.getExpression().getText() !== "defineEvent") return !1;
4951
- let t = Ir(e);
4951
+ let t = Lr(e);
4952
4952
  return !t.has("guards") && !t.has("openAccess");
4953
- }) : !1, Rr = C({
4953
+ }) : !1, zr = S({
4954
4954
  version: "0.35.0",
4955
4955
  id: "0.35.0/01_event-access-decision",
4956
4956
  title: "Every declared event decides who may listen (`guards:` or `openAccess:`)",
4957
4957
  kind: "manual",
4958
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Lr(e.getFullText(), e.getDescendantsOfKind(b.CallExpression))),
4958
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Rr(e.getFullText(), e.getDescendantsOfKind(y.CallExpression))),
4959
4959
  note: (/* @__PURE__ */ "YOUR APP WILL NOT BOOT UNTIL EVERY DECLARED EVENT DECIDES WHO MAY LISTEN.(This closes the sibling of 0.34.0's procedure gate: `defineEvent`'s `guards:`(was optional and enforcement skipped an empty list, so an event with no(decision was silently subscribable by ANY authenticated session that could(open the socket.((WHAT TO DO, IN THIS ORDER:((1) LIST THEM. `voltro doctor` prints every undecided event with its tag and( file — the same set, from the same function, that the boot refuses on.( `voltro doctor --json` carries it as `accessDecisions.undecided` for CI.((2) DECIDE EACH ONE. There are exactly two answers and they are not the same( claim:(( guards: [{ scope: 'display:read' }] a subscriber must hold a scope( openAccess: 'public scoreboard' anyone may listen — and why(( `openAccess` takes a REASON, not a boolean. It is what a reviewer reads( later, and it is what makes \"we decided this is open\" distinguishable( from \"nobody looked\".((3) DO NOT RUBBER-STAMP. Reaching for a scope every caller already holds( satisfies the gate, reads as protection, and enforces nothing. If the( event is open, say `openAccess`. If it should not be open, this gate just( found a real hole — an event often carries exactly the data its table( guards behind a scoped query.((IF YOU NEED TO SHIP FIRST. The same field that covers procedures covers(events, in `app.config.ts`:(( export default { security: { defaultDeny: false } }((That restores the old default-allow for the WHOLE app, in one place a(reviewer can see. There is no env var for it, on purpose. `voltro doctor`(keeps listing the undecided events while it is off, marked advisory.((NOT AFFECTED: apps already running `security: { defaultDeny: false }`, and(events your PLUGINS declare — the gate reads your app's own discovered(`*.event.ts` files only.".split("(")).join("\n")
4960
- }), zr = /\brouteHttp\s*\(/, Br = C({
4960
+ }), Br = /\brouteHttp\s*\(/, Vr = S({
4961
4961
  version: "0.34.0",
4962
4962
  id: "0.34.0/26_route-http-is-async",
4963
4963
  title: "`routeHttp` returns a Promise",
4964
4964
  kind: "manual",
4965
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => zr.test(e.getFullText())),
4965
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Br.test(e.getFullText())),
4966
4966
  note: "`routeHttp` (@voltro/mcp) is async now:\n\n routeHttp(options): HttpOutcome // before\n routeHttp(options): Promise<HttpOutcome> // after\n\nAwait it, and make the enclosing function async if it is not already:\n\n // before\n const outcome = routeHttp({ method, url, body, tools })\n if (outcome.kind === 'json') respond(outcome)\n\n // after\n const outcome = await routeHttp({ method, url, body, tools })\n if (outcome.kind === 'json') respond(outcome)\n\nWhy it moved: `tools/list` now folds in the app's agent tools and `tools/call`\nexecutes one, so two of the methods reach the app. The rest still resolve without\ntouching anything, and the function is still pure in the sense that matters — it\nbinds no socket, and a test supplies a `live` double.\n\nIf you use the shipped `serveHttp` transport there is nothing to do; it already\nawaits."
4967
- }), Vr = /@voltro\/plugin-(billing|governance|storage)/, Hr = /* @__PURE__ */ RegExp("\\b(billing\\.(startCheckout|portalUrl|previewChange|changePlan|changeSeats|invoices|reportUsage)|governance\\.(export|erase)|storage\\.(mintUploadUrl|ingestUrl|listRefs))\\b"), Ur = /\bdefinePluginRoute\s*\(|\broutes\s*:\s*\[/, Wr = C({
4967
+ }), Hr = /@voltro\/plugin-(billing|governance|storage)/, Ur = /* @__PURE__ */ RegExp("\\b(billing\\.(startCheckout|portalUrl|previewChange|changePlan|changeSeats|invoices|reportUsage)|governance\\.(export|erase)|storage\\.(mintUploadUrl|ingestUrl|listRefs))\\b"), Wr = /\bdefinePluginRoute\s*\(|\broutes\s*:\s*\[/, Gr = S({
4968
4968
  version: "0.35.0",
4969
4969
  id: "0.35.0/02_plugin-procedures-carry-access-decisions",
4970
4970
  title: "First-party plugin procedures carry access decisions; twelve now require a scope",
4971
4971
  kind: "manual",
4972
4972
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
4973
4973
  let t = e.getFullText();
4974
- return Vr.test(t) || Hr.test(t) || Ur.test(t) && /VoltroPlugin|definePlugin/.test(t);
4974
+ return Hr.test(t) || Ur.test(t) || Wr.test(t) && /VoltroPlugin|definePlugin/.test(t);
4975
4975
  }),
4976
4976
  note: [
4977
4977
  "Every first-party plugin rpc route now declares an access decision, and the",
@@ -5036,12 +5036,12 @@ var x = (e, t, n) => {
5036
5036
  "`openAccess` with the reason each is safe (self-scoped rows, signed",
5037
5037
  "subject-bound tickets, per-object grants), and behave exactly as before."
5038
5038
  ].join("\n")
5039
- }), Gr = /AnalyticsMirrorImpl/, Kr = C({
5039
+ }), Kr = /AnalyticsMirrorImpl/, qr = S({
5040
5040
  version: "0.35.0",
5041
5041
  id: "0.35.0/03_analytics-mirror-max-version",
5042
5042
  title: "Custom analytics mirror sinks answer `maxVersion` (fleet-scope seed read)",
5043
5043
  kind: "manual",
5044
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Gr.test(e.getFullText())),
5044
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Kr.test(e.getFullText())),
5045
5045
  note: [
5046
5046
  "The analytics CDC-mirror now derives its version from the CHANGE under",
5047
5047
  "`changeScope: 'fleet'` (postgres CDC, mysql binlog): warehouse baseline +",
@@ -5066,44 +5066,44 @@ var x = (e, t, n) => {
5066
5066
  "",
5067
5067
  "The read is issued at most once per key per process, off the commit path."
5068
5068
  ].join("\n")
5069
- }), qr = (e) => /RunStepStatus/.test(e) || /['"]succeeded['"]/.test(e) && /['"]waiting['"]/.test(e) && /RunStep|runStep|flow/i.test(e), Jr = C({
5069
+ }), Jr = (e) => /RunStepStatus/.test(e) || /['"]succeeded['"]/.test(e) && /['"]waiting['"]/.test(e) && /RunStep|runStep|flow/i.test(e), Yr = S({
5070
5070
  version: "0.35.0",
5071
5071
  id: "0.35.0/04_flow-run-step-skipped",
5072
5072
  title: "A flow run step can now be 'skipped' — handle it where you render a run timeline",
5073
5073
  kind: "manual",
5074
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => qr(e.getFullText())),
5074
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Jr(e.getFullText())),
5075
5075
  note: (/* @__PURE__ */ "A FLOW RUN STEP CAN NOW BE 'skipped'.((`when:` on a flow step gates it on a prior value, and a step whose condition(is false is SKIPPED rather than run. That is a sixth member of(`RunStepStatus`, so an exhaustive switch over it no longer compiles, and a(lookup keyed by status now has a hole.((WHAT TO DO:((1) FIND IT. Anywhere you render or branch on a run step status — a timeline( component, a status badge map, a progress calculation.((2) DECIDE WHAT A SKIPPED STEP LOOKS LIKE. It is NOT a failure and NOT a( success: it did not run, on purpose, and it produced no output. The( step carries `skipReason` — the rendered condition, e.g.( `{{mode}} equals \"full\"` — so the timeline can say WHY.((3) DO NOT DEFAULT IT TO INVISIBLE. Dropping the row compiles and reads fine( in review, and it reproduces exactly the problem `skipReason` exists to( prevent: a step that vanished is indistinguishable from a step nobody( declared, which is the first thing a user reports as a bug.((A PROGRESS COUNT probably needs a second look too: `succeeded / total` now(under-reports a completed run that skipped a step. Counting(`succeeded + skipped` against the total is usually what was meant.((If your app does not render a flow timeline, there is nothing to do — this(note fires on any file that names the status type or its literals.".split("(")).join("\n")
5076
- }), Yr = C({
5076
+ }), Xr = S({
5077
5077
  version: "0.35.0",
5078
5078
  id: "0.35.0/05_declared-schema-drops-runtime-flags",
5079
5079
  title: "The declared schema no longer reads `CDC` / `VOLTRO_UNDO` / `VOLTRO_TRACING_PERSIST`",
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
- }), G = "session_expired", Xr = C({
5083
+ }), W = "session_expired", Zr = S({
5084
5084
  version: "0.35.1",
5085
5085
  id: "0.35.1/01_unauthorized-replaces-session-expired",
5086
5086
  title: "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
5087
5087
  kind: "transform",
5088
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(G)),
5088
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(W)),
5089
5089
  transform: ({ project: e, log: t }) => {
5090
5090
  let n = [];
5091
5091
  for (let t of e.getSourceFiles()) {
5092
- if (!t.getFullText().includes(G)) continue;
5092
+ if (!t.getFullText().includes(W)) continue;
5093
5093
  let e = !1;
5094
- for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === G && (n.setLiteralValue("unauthorized"), e = !0);
5094
+ for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === W && (n.setLiteralValue("unauthorized"), e = !0);
5095
5095
  e && n.push(t.getFilePath());
5096
5096
  }
5097
- n.length !== 0 && t(`rewrote '${G}' 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"));
5097
+ n.length !== 0 && t(`rewrote '${W}' 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
5098
  }
5099
- }), Zr = /\(\s*\{[^}]*\.\.\./, Qr = /\bquery\??\.?\(\s*['"`]/, $r = C({
5099
+ }), Qr = /\(\s*\{[^}]*\.\.\./, $r = /\bquery\??\.?\(\s*['"`]/, ei = S({
5100
5100
  version: "0.37.0",
5101
5101
  id: "0.37.0/01_procedure-input-rejects-undeclared-fields",
5102
5102
  title: "a procedure input rejects a field it does not declare (it used to drop it)",
5103
5103
  kind: "manual",
5104
5104
  appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
5105
5105
  let t = e.getFullText();
5106
- return Zr.test(t) || Qr.test(t) || w("query").test(t);
5106
+ return Qr.test(t) || $r.test(t) || C("query").test(t);
5107
5107
  }),
5108
5108
  note: [
5109
5109
  "A procedure call whose input carries a field the schema does not declare",
@@ -5140,7 +5140,18 @@ var x = (e, t, n) => {
5140
5140
  "WHAT DOES NOT CHANGE",
5141
5141
  "",
5142
5142
  " • A DECLARED field, including every optional one, decodes exactly as",
5143
- " before. An empty input to a procedure that declares none is still fine.",
5143
+ " before. Sending `{}` to a procedure that declares none is still fine.",
5144
+ "",
5145
+ " ! BUT `input: Schema.Struct({})` IS THE EXCEPTION, AND THE WORST-HIT ONE.",
5146
+ " An empty struct has no expected keys, so excess-property checking had",
5147
+ " nothing to compare against and DISCARDED EVERY FIELD silently. Those",
5148
+ " procedures do not move from \"drops the extra field\" to \"rejects it\" —",
5149
+ " they move from accepting ANYTHING to accepting NOTHING. Start there:",
5150
+ "",
5151
+ " grep -rn \"Struct({})\" --include=*.ts src/",
5152
+ "",
5153
+ " and check each one's untyped callers (wrapper hooks, table components,",
5154
+ " `preload` entries) for a field they always send.",
5144
5155
  " • Nested objects and union members follow the same rule — that is the",
5145
5156
  " point, since a filter object one level down is the commoner spelling.",
5146
5157
  " • A non-object input (a scalar, a void input) is unaffected; there is no",
@@ -5154,25 +5165,134 @@ var x = (e, t, n) => {
5154
5165
  "two, the only safe direction is to refuse — and a warning would have to be",
5155
5166
  "read by someone, in a log, after the wrong rows were already served."
5156
5167
  ].join("\n")
5157
- }), K = "@voltro/plugin-auth", q = "@voltro/protocol", J = "IMPERSONATION_METADATA_KEY", ei = [
5158
- ge,
5168
+ }), G = "@voltro/plugin-auth", K = "@voltro/protocol", q = "IMPERSONATION_METADATA_KEY", ti = S({
5169
+ version: "0.38.0",
5170
+ id: "0.38.0/01_impersonation-key-in-protocol",
5171
+ title: `\`${q}\` is imported from @voltro/protocol`,
5172
+ kind: "transform",
5173
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === G && e.getNamedImports().some((e) => e.getName() === q))),
5174
+ transform: ({ project: e, log: t }) => {
5175
+ let n = 0;
5176
+ for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
5177
+ if (e.getModuleSpecifierValue() !== G) continue;
5178
+ let r = e.getNamedImports().find((e) => e.getName() === q);
5179
+ if (r === void 0) continue;
5180
+ let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
5181
+ r.remove();
5182
+ let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === K && e.isTypeOnly() === a), s = {
5183
+ name: q,
5184
+ ...i === void 0 ? {} : { alias: i }
5185
+ };
5186
+ o === void 0 ? t.addImportDeclaration({
5187
+ moduleSpecifier: K,
5188
+ isTypeOnly: a,
5189
+ namedImports: [s]
5190
+ }) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
5191
+ }
5192
+ n > 0 && t(`${q}: ${n} import(s) repointed to ${K}`);
5193
+ }
5194
+ }), ni = S({
5195
+ version: "0.38.0",
5196
+ id: "0.38.0/02_startup-failure-refuses-boot",
5197
+ title: "a `*.startup.ts` that fails refuses the boot (it used to warn and continue)",
5198
+ kind: "manual",
5199
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => /\.startup\.tsx?$/.test(e.getFilePath())),
5200
+ note: [
5201
+ "A `*.startup.ts` whose function throws or rejects now ABORTS the boot,",
5202
+ "naming the file and the cause. Before this release it logged a warning and",
5203
+ "the server came up without whatever the startup was there to install.",
5204
+ "",
5205
+ "WHY IT CHANGED",
5206
+ "",
5207
+ "A startup is the documented place to REGISTER things the request path",
5208
+ "depends on — `setRowFilter` (row-level security) above all. We measured a",
5209
+ "startup that threw on its first line and produced this:",
5210
+ "",
5211
+ " warn startup: function rejected",
5212
+ " info startup: registered <- next line, same file",
5213
+ "",
5214
+ "and then a server that answered every request with no row filter",
5215
+ "registered. The two lines contradict each other and the second asserts",
5216
+ "exactly the thing that had just failed.",
5217
+ "",
5218
+ "The runner cannot tell a registration from a background loop, and the two",
5219
+ "outcomes are not symmetric: \"the app refuses to boot\" is fixed in seconds",
5220
+ "and everyone sees it; \"the app serves without its access control\" is seen",
5221
+ "by nobody.",
5222
+ "",
5223
+ "WHAT TO DO",
5224
+ "",
5225
+ "1. Boot each app once. A startup that was failing silently now says so, and",
5226
+ " the message names the file. This is the whole migration for most apps —",
5227
+ " a startup that worked keeps working.",
5228
+ "",
5229
+ "2. If a startup CAN fail and that is acceptable — a best-effort cache warm,",
5230
+ " an optional metrics bridge — catch it yourself:",
5231
+ "",
5232
+ " export default async (ctx) => {",
5233
+ " try {",
5234
+ " await warmTheCache(ctx.store)",
5235
+ " } catch (err) {",
5236
+ " ctx.log.warn('cache warm failed; serving cold', {}, err)",
5237
+ " }",
5238
+ " }",
5239
+ "",
5240
+ " The catch belongs at the site where somebody decided the failure was",
5241
+ " acceptable, where a reviewer can see the decision. That is the ONLY",
5242
+ " reason this is not a flag: a flag would move the decision away from the",
5243
+ " startup it applies to and make it one setting for all of them.",
5244
+ "",
5245
+ "TWO MORE WAYS A STARTUP USED TO VANISH, now also refusals",
5246
+ "",
5247
+ " • A startup file that cannot be IMPORTED (a bad import path, a syntax",
5248
+ " error) used to warn and skip the file.",
5249
+ " • A startup file with no DEFAULT-exported function used to warn and skip.",
5250
+ " The convention is `export default async (ctx) => {…}` — a named export",
5251
+ " is discovered and never runs, which is indistinguishable from a startup",
5252
+ " that ran and did nothing.",
5253
+ "",
5254
+ "THE OTHER HALF: A STARTUP MUST RETURN",
5255
+ "",
5256
+ "A startup function that never returns now refuses the boot too, after",
5257
+ "`VOLTRO_STARTUP_TIMEOUT_MS` (60s default). Awaiting forever used to be",
5258
+ "allowed and documented, and it is what made a slow-then-failing startup",
5259
+ "unreportable: the boot moved on after 2 seconds, so a rejection arriving",
5260
+ "later had nothing left to refuse.",
5261
+ "",
5262
+ "Start the work and RETURN; hand the teardown to `onShutdown`:",
5263
+ "",
5264
+ " export default async ({ store, log, onShutdown }) => {",
5265
+ " const timer = setInterval(() => sync(store), 60_000)",
5266
+ " onShutdown(() => clearInterval(timer))",
5267
+ " }",
5268
+ "",
5269
+ "Every startup this framework ships already has that shape, so most apps",
5270
+ "need no change. If yours awaits a fiber until shutdown, move the await",
5271
+ "behind the return.",
5272
+ "",
5273
+ "A startup that is legitimately SLOW is unaffected — it is waited for, and",
5274
+ "it registers when it finishes. Raise VOLTRO_STARTUP_TIMEOUT_MS if a real",
5275
+ "backfill needs more than a minute, or (better) register first and do the",
5276
+ "long work after, so a failure in the long half cannot hide a missing",
5277
+ "registration."
5278
+ ].join("\n")
5279
+ }), ri = /\bStruct\(\s*\{\s*\}\s*\)/, J = [
5159
5280
  _e,
5160
- ye,
5281
+ ve,
5161
5282
  be,
5162
5283
  xe,
5163
- we,
5164
- Ee,
5284
+ Se,
5285
+ Te,
5165
5286
  De,
5166
5287
  Oe,
5167
- je,
5288
+ ke,
5168
5289
  Me,
5169
- Fe,
5290
+ Ne,
5170
5291
  Ie,
5171
- ze,
5292
+ Le,
5172
5293
  Be,
5173
- Ue,
5294
+ Ve,
5174
5295
  We,
5175
- Ge,
5176
5296
  Ke,
5177
5297
  qe,
5178
5298
  Je,
@@ -5184,190 +5304,141 @@ var x = (e, t, n) => {
5184
5304
  et,
5185
5305
  tt,
5186
5306
  nt,
5187
- st,
5188
5307
  rt,
5189
- ot,
5190
- pt,
5191
- vt,
5192
- yt,
5308
+ it,
5309
+ lt,
5310
+ at,
5311
+ ct,
5312
+ ht,
5193
5313
  bt,
5194
- Ct,
5195
- wt,
5196
- Dt,
5197
- kt,
5198
- jt,
5199
- Nt,
5200
- Ft,
5201
- Lt,
5202
- zt,
5203
- Vt,
5204
- Ut,
5205
- Gt,
5206
- qt,
5207
- Yt,
5208
- Zt,
5314
+ xt,
5315
+ St,
5316
+ Tt,
5317
+ Et,
5318
+ At,
5319
+ Mt,
5320
+ Pt,
5321
+ It,
5322
+ Rt,
5323
+ Bt,
5324
+ Ht,
5325
+ Wt,
5326
+ Kt,
5327
+ Jt,
5328
+ Xt,
5329
+ Qt,
5209
5330
  en,
5210
- tn,
5211
- nn,
5212
5331
  rn,
5332
+ an,
5213
5333
  on,
5214
- cn,
5215
- un,
5216
- fn,
5217
- mn,
5218
- bn,
5334
+ sn,
5335
+ ln,
5336
+ dn,
5337
+ pn,
5338
+ hn,
5339
+ _n,
5219
5340
  Sn,
5220
5341
  wn,
5221
5342
  En,
5222
5343
  On,
5223
- kn,
5224
5344
  An,
5225
5345
  jn,
5226
5346
  Mn,
5347
+ Nn,
5227
5348
  Pn,
5228
- Rn,
5349
+ In,
5229
5350
  Bn,
5230
5351
  Hn,
5231
- Un,
5232
5352
  Wn,
5233
5353
  Gn,
5234
- Jn,
5235
- tr,
5236
- Yn,
5237
- $n,
5354
+ Kn,
5355
+ qn,
5356
+ Xn,
5238
5357
  rr,
5358
+ Zn,
5359
+ tr,
5239
5360
  ar,
5240
- or,
5241
5361
  sr,
5242
- ur,
5362
+ cr,
5363
+ lr,
5243
5364
  fr,
5244
- yr,
5245
- xr,
5246
- gr,
5247
- wr,
5248
- Dr,
5249
- kr,
5250
- jr,
5251
- Fr,
5252
- Br,
5253
- Rr,
5254
- Wr,
5255
- Kr,
5256
- Jr,
5365
+ mr,
5366
+ br,
5367
+ Sr,
5368
+ vr,
5369
+ Tr,
5370
+ Or,
5371
+ Ar,
5372
+ Mr,
5373
+ Ir,
5374
+ Vr,
5375
+ zr,
5376
+ Gr,
5377
+ qr,
5257
5378
  Yr,
5258
5379
  Xr,
5259
- $r,
5260
- C({
5261
- version: "0.38.0",
5262
- id: "0.38.0/01_impersonation-key-in-protocol",
5263
- title: `\`${J}\` is imported from @voltro/protocol`,
5264
- kind: "transform",
5265
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === K && e.getNamedImports().some((e) => e.getName() === J))),
5266
- transform: ({ project: e, log: t }) => {
5267
- let n = 0;
5268
- for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
5269
- if (e.getModuleSpecifierValue() !== K) continue;
5270
- let r = e.getNamedImports().find((e) => e.getName() === J);
5271
- if (r === void 0) continue;
5272
- let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
5273
- r.remove();
5274
- let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === q && e.isTypeOnly() === a), s = {
5275
- name: J,
5276
- ...i === void 0 ? {} : { alias: i }
5277
- };
5278
- o === void 0 ? t.addImportDeclaration({
5279
- moduleSpecifier: q,
5280
- isTypeOnly: a,
5281
- namedImports: [s]
5282
- }) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
5283
- }
5284
- n > 0 && t(`${J}: ${n} import(s) repointed to ${q}`);
5285
- }
5286
- }),
5287
- C({
5288
- version: "0.38.0",
5289
- id: "0.38.0/02_startup-failure-refuses-boot",
5290
- title: "a `*.startup.ts` that fails refuses the boot (it used to warn and continue)",
5380
+ Zr,
5381
+ ei,
5382
+ ti,
5383
+ ni,
5384
+ S({
5385
+ version: "0.39.0",
5386
+ id: "0.39.0/01_empty-input-schema-rejects-every-field",
5387
+ title: "a procedure declaring `Schema.Struct({})` used to accept every field and now accepts none",
5291
5388
  kind: "manual",
5292
- appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => /\.startup\.tsx?$/.test(e.getFilePath())),
5389
+ appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ri.test(e.getFullText())),
5293
5390
  note: [
5294
- "A `*.startup.ts` whose function throws or rejects now ABORTS the boot,",
5295
- "naming the file and the cause. Before this release it logged a warning and",
5296
- "the server came up without whatever the startup was there to install.",
5297
- "",
5298
- "WHY IT CHANGED",
5391
+ "CHECK YOUR `Schema.Struct({})` PROCEDURES FIRST. They have been accepting",
5392
+ "every field anyone sent them, and they now accept none the one jump in",
5393
+ "this change that goes from \"accepts anything\" straight to \"accepts",
5394
+ "nothing\" rather than from \"silently drops\" to \"rejects\".",
5299
5395
  "",
5300
- "A startup is the documented place to REGISTER things the request path",
5301
- "depends on `setRowFilter` (row-level security) above all. We measured a",
5302
- "startup that threw on its first line and produced this:",
5396
+ "This is a correction to the 0.37.0 note, re-issued rather than edited: a",
5397
+ "codemod note is only ever shown to users crossing its version, so anyone",
5398
+ "already past 0.37.0 the users most likely to be holding a broken call —",
5399
+ "could not have been reached by fixing it in place.",
5303
5400
  "",
5304
- " warn startup: function rejected",
5305
- " info startup: registered <- next line, same file",
5401
+ "WHY THAT SHAPE AND NOT THE OTHERS",
5306
5402
  "",
5307
- "and then a server that answered every request with no row filter",
5308
- "registered. The two lines contradict each other and the second asserts",
5309
- "exactly the thing that had just failed.",
5403
+ "Excess-property checking compares a payload against the schema's expected",
5404
+ "keys. An empty struct has none, so there was nothing to compare and every",
5405
+ "field decoded away silently. The declaration that looks strictest was the",
5406
+ "only one enforcing nothing.",
5310
5407
  "",
5311
- "The runner cannot tell a registration from a background loop, and the two",
5312
- "outcomes are not symmetric: \"the app refuses to boot\" is fixed in seconds",
5313
- "and everyone sees it; \"the app serves without its access control\" is seen",
5314
- "by nobody.",
5408
+ "THE LIST, IN ABOUT A MINUTE",
5315
5409
  "",
5316
- "WHAT TO DO",
5410
+ " grep -rn \"Struct({})\" --include=*.ts src/",
5317
5411
  "",
5318
- "1. Boot each app once. A startup that was failing silently now says so, and",
5319
- " the message names the file. This is the whole migration for most apps —",
5320
- " a startup that worked keeps working.",
5412
+ "For each hit, find its callers. The break is invisible at the call site",
5413
+ "whenever the caller is NOT typed against the descriptor a wrapper hook",
5414
+ "that takes `Record<string, unknown>`, a table component passing its own",
5415
+ "`{ limit, offset }`, a `preload` entry, a `parentKey`. A caller that IS",
5416
+ "typed against the descriptor was already a compile error and is not your",
5417
+ "problem here.",
5321
5418
  "",
5322
- "2. If a startup CAN fail and that is acceptable a best-effort cache warm,",
5323
- " an optional metrics bridge — catch it yourself:",
5419
+ "The reported case, in full, because it is the shape to look for:",
5324
5420
  "",
5325
- " export default async (ctx) => {",
5326
- " try {",
5327
- " await warmTheCache(ctx.store)",
5328
- " } catch (err) {",
5329
- " ctx.log.warn('cache warm failed; serving cold', {}, err)",
5330
- " }",
5331
- " }",
5421
+ " // the procedure",
5422
+ " input: Schema.Struct({})",
5423
+ " // the caller — a shared table hook, every page, always",
5424
+ " { limit: pageSize }",
5332
5425
  "",
5333
- " The catch belongs at the site where somebody decided the failure was",
5334
- " acceptable, where a reviewer can see the decision. That is the ONLY",
5335
- " reason this is not a flag: a flag would move the decision away from the",
5336
- " startup it applies to and make it one setting for all of them.",
5426
+ "Before: `limit` was dropped and the query returned everything. After: the",
5427
+ "call fails. Both the live subscription and the SSR seed of that page go,",
5428
+ "because they are the same procedure call made twice.",
5337
5429
  "",
5338
- "TWO MORE WAYS A STARTUP USED TO VANISH, now also refusals",
5430
+ "WHAT TO DO WITH EACH ONE",
5339
5431
  "",
5340
- " • A startup file that cannot be IMPORTED (a bad import path, a syntax",
5341
- " error) used to warn and skip the file.",
5342
- " A startup file with no DEFAULT-exported function used to warn and skip.",
5343
- " The convention is `export default async (ctx) => {…}` — a named export",
5344
- " is discovered and never runs, which is indistinguishable from a startup",
5345
- " that ran and did nothing.",
5432
+ " • The field is REAL and the schema was wrong declare it. This is the",
5433
+ " common case for a `limit` that was silently ignored: the procedure was",
5434
+ " not honouring it before, so declaring it is a behaviour change you want",
5435
+ " to make deliberately, not a mechanical fix.",
5436
+ " • The field is JUNK a caller sends out of habit drop it at the call",
5437
+ " site. Do NOT widen the input schema to absorb it; that re-creates the",
5438
+ " silent discard under a name that looks intentional.",
5346
5439
  "",
5347
- "THE OTHER HALF: A STARTUP MUST RETURN",
5348
- "",
5349
- "A startup function that never returns now refuses the boot too, after",
5350
- "`VOLTRO_STARTUP_TIMEOUT_MS` (60s default). Awaiting forever used to be",
5351
- "allowed and documented, and it is what made a slow-then-failing startup",
5352
- "unreportable: the boot moved on after 2 seconds, so a rejection arriving",
5353
- "later had nothing left to refuse.",
5354
- "",
5355
- "Start the work and RETURN; hand the teardown to `onShutdown`:",
5356
- "",
5357
- " export default async ({ store, log, onShutdown }) => {",
5358
- " const timer = setInterval(() => sync(store), 60_000)",
5359
- " onShutdown(() => clearInterval(timer))",
5360
- " }",
5361
- "",
5362
- "Every startup this framework ships already has that shape, so most apps",
5363
- "need no change. If yours awaits a fiber until shutdown, move the await",
5364
- "behind the return.",
5365
- "",
5366
- "A startup that is legitimately SLOW is unaffected — it is waited for, and",
5367
- "it registers when it finishes. Raise VOLTRO_STARTUP_TIMEOUT_MS if a real",
5368
- "backfill needs more than a minute, or (better) register first and do the",
5369
- "long work after, so a failure in the long half cannot hide a missing",
5370
- "registration."
5440
+ "A procedure that genuinely takes no input keeps `Schema.Struct({})` and is",
5441
+ "correct as it stands — callers must send `{}` or nothing."
5371
5442
  ].join("\n")
5372
5443
  })
5373
5444
  ], Y = /* @__PURE__ */ new Set([
@@ -5382,73 +5453,73 @@ var x = (e, t, n) => {
5382
5453
  ".turbo",
5383
5454
  ".cache",
5384
5455
  ".output"
5385
- ]), ti = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, ni = /\.(?:tsx?|jsx?|mjs|cjs)$/, ri = (e) => m(e).replace(ni, ""), ii = (e) => {
5456
+ ]), ii = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Z = /\.(?:tsx?|jsx?|mjs|cjs)$/, ai = (e) => m(e).replace(Z, ""), oi = (e) => {
5386
5457
  let t = e.split("/").filter((e) => e.length > 0).pop();
5387
5458
  if (t === void 0 || t === "." || t === "..") return;
5388
- let n = t.replace(ni, "");
5459
+ let n = t.replace(Z, "");
5389
5460
  return n.length > 0 ? n : void 0;
5390
- }, ai = (e) => {
5461
+ }, si = (e) => {
5391
5462
  let t = /* @__PURE__ */ new Map();
5392
5463
  for (let n of e) {
5393
5464
  let e;
5394
5465
  try {
5395
- e = ne(n, "utf8");
5466
+ e = re(n, "utf8");
5396
5467
  } catch {
5397
5468
  continue;
5398
5469
  }
5399
5470
  X.lastIndex = 0;
5400
5471
  for (let r = X.exec(e); r !== null; r = X.exec(e)) {
5401
- let e = ii(r[1] ?? r[2] ?? "");
5472
+ let e = oi(r[1] ?? r[2] ?? "");
5402
5473
  if (e === void 0) continue;
5403
5474
  let i = t.get(e);
5404
5475
  i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
5405
5476
  }
5406
5477
  }
5407
5478
  return t;
5408
- }, oi = (e, t) => {
5479
+ }, ci = (e, t) => {
5409
5480
  let n = new Set(e);
5410
- for (let r of e) for (let e of [ri(r), m(h(r))]) {
5481
+ for (let r of e) for (let e of [ai(r), m(h(r))]) {
5411
5482
  let r = t.get(e);
5412
5483
  if (r !== void 0) for (let e of r) n.add(e);
5413
5484
  }
5414
5485
  return n;
5415
- }, si = [
5486
+ }, li = [
5416
5487
  "**/*.ts",
5417
5488
  "**/*.tsx",
5418
5489
  "!**/node_modules/**",
5419
5490
  "!**/dist/**",
5420
5491
  "!**/.framework/**",
5421
5492
  "!**/*.d.ts"
5422
- ], Z = (e, t, n = ui) => {
5493
+ ], Q = (e, t, n = fi) => {
5423
5494
  let r = /* @__PURE__ */ new Set();
5424
- for (let e of t) for (let t of e.scope ?? si) r.add(t);
5495
+ for (let e of t) for (let t of e.scope ?? li) r.add(t);
5425
5496
  let i = n(e);
5426
5497
  if (i !== void 0) {
5427
- let t = [...r].filter((e) => !e.startsWith("!")).map((e) => li(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => li(e.slice(1))), a = [...Y], o = [];
5498
+ let t = [...r].filter((e) => !e.startsWith("!")).map((e) => di(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => di(e.slice(1))), a = [...Y], o = [];
5428
5499
  for (let r of i) {
5429
5500
  if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
5430
5501
  let i = g(e, r);
5431
- v(i) && o.push(i);
5502
+ _(i) && o.push(i);
5432
5503
  }
5433
- return ci(o.length, e), o;
5504
+ return ui(o.length, e), o;
5434
5505
  }
5435
- let a = oe([...r], {
5506
+ let a = se([...r], {
5436
5507
  cwd: e,
5437
5508
  absolute: !0,
5438
5509
  expandDirectories: !1,
5439
5510
  followSymbolicLinks: !1,
5440
- ignore: [...ti()]
5511
+ ignore: [...ii()]
5441
5512
  });
5442
- return ci(a.length, e), a;
5443
- }, ci = (e, t) => {
5444
- let n = di();
5513
+ return ui(a.length, e), a;
5514
+ }, ui = (e, t) => {
5515
+ let n = pi();
5445
5516
  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.`);
5446
- }, li = (e) => {
5517
+ }, di = (e) => {
5447
5518
  let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
5448
5519
  return (e) => RegExp(`^${t}$`).test(e);
5449
- }, ui = (e) => {
5520
+ }, fi = (e) => {
5450
5521
  try {
5451
- let t = re("git", [
5522
+ let t = ie("git", [
5452
5523
  "ls-files",
5453
5524
  "--cached",
5454
5525
  "--others",
@@ -5456,7 +5527,7 @@ var x = (e, t, n) => {
5456
5527
  ], {
5457
5528
  cwd: e,
5458
5529
  encoding: "utf8",
5459
- maxBuffer: 256 * 1024 * 1024,
5530
+ maxBuffer: 268435456,
5460
5531
  stdio: [
5461
5532
  "ignore",
5462
5533
  "pipe",
@@ -5467,17 +5538,17 @@ var x = (e, t, n) => {
5467
5538
  } catch {
5468
5539
  return;
5469
5540
  }
5470
- }, di = () => {
5541
+ }, pi = () => {
5471
5542
  let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
5472
5543
  return Number.isInteger(e) && e > 0 ? e : 2e4;
5473
- }, Q = (e, t) => {
5544
+ }, $ = (e, t) => {
5474
5545
  let n = e.split(".").map(Number), r = t.split(".").map(Number);
5475
5546
  for (let e = 0; e < 3; e++) {
5476
5547
  let t = (n[e] ?? 0) - (r[e] ?? 0);
5477
5548
  if (t !== 0) return t;
5478
5549
  }
5479
5550
  return 0;
5480
- }, fi = (e, t, n = ei) => n.filter((n) => Q(e, n.version) < 0 && Q(n.version, t) <= 0).slice().sort(he), $ = (e, t, n = () => {}) => {
5551
+ }, mi = (e, t, n = J) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(ge), hi = (e, t, n = () => {}) => {
5481
5552
  let r = [], i = [], a = [];
5482
5553
  for (let o of t) {
5483
5554
  if (!(!o.appliesTo || o.appliesTo({ project: e }))) {
@@ -5495,7 +5566,7 @@ var x = (e, t, n) => {
5495
5566
  let t = new Map(e.getSourceFiles().map((e) => [e.getFilePath(), e.getFullText()]));
5496
5567
  o.transform?.({
5497
5568
  project: e,
5498
- helpers: pe,
5569
+ helpers: me,
5499
5570
  log: n
5500
5571
  });
5501
5572
  let s = new Map(e.getSourceFiles().map((e) => [e.getFilePath(), e.getFullText()])), c = /* @__PURE__ */ new Map();
@@ -5523,8 +5594,8 @@ var x = (e, t, n) => {
5523
5594
  manual: i,
5524
5595
  skipped: a
5525
5596
  };
5526
- }, pi = async (e) => {
5527
- let t = e.log ?? (() => {}), n = fi(e.from, e.to, e.codemods ?? ei), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
5597
+ }, gi = async (e) => {
5598
+ let t = e.log ?? (() => {}), n = mi(e.from, e.to, e.codemods ?? J), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
5528
5599
  let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
5529
5600
  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")}`);
5530
5601
  return t;
@@ -5536,31 +5607,31 @@ var x = (e, t, n) => {
5536
5607
  };
5537
5608
  let i = f(e.root);
5538
5609
  i !== void 0 && t(`path aliases: ${Object.keys(i.paths).join(", ")}`);
5539
- let a = () => new ie({
5610
+ let a = () => new ae({
5540
5611
  skipAddingFilesFromTsConfig: !0,
5541
- manipulationSettings: { quoteKind: ae.Single },
5612
+ manipulationSettings: { quoteKind: oe.Single },
5542
5613
  ...i === void 0 ? {} : { compilerOptions: {
5543
5614
  baseUrl: i.baseUrl,
5544
5615
  paths: i.paths
5545
5616
  } }
5546
- }), o, s = Z(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p = c.length > 0 ? ai(Z(e.root, [{
5617
+ }), o, s = Q(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p = c.length > 0 ? si(Q(e.root, [{
5547
5618
  ...c[0],
5548
- scope: si
5619
+ scope: li
5549
5620
  }])) : void 0;
5550
5621
  for (let n of r) {
5551
5622
  if (c.includes(n)) {
5552
- let r = a(), i = Z(e.root, [n]);
5553
- for (let e of oi(i, p)) v(e) && r.addSourceFileAtPath(e);
5554
- let s = $(r, [n], t);
5623
+ let r = a(), i = Q(e.root, [n]);
5624
+ for (let e of ci(i, p)) _(e) && r.addSourceFileAtPath(e);
5625
+ let s = hi(r, [n], t);
5555
5626
  e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
5556
5627
  continue;
5557
5628
  }
5558
5629
  o ??= (() => {
5559
5630
  let e = a();
5560
- for (let t of s) v(t) && e.addSourceFileAtPath(t);
5631
+ for (let t of s) _(t) && e.addSourceFileAtPath(t);
5561
5632
  return e;
5562
5633
  })();
5563
- let r = $(o, [n], t);
5634
+ let r = hi(o, [n], t);
5564
5635
  e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
5565
5636
  }
5566
5637
  return {
@@ -5570,4 +5641,4 @@ var x = (e, t, n) => {
5570
5641
  };
5571
5642
  };
5572
5643
  //#endregion
5573
- export { C as i, pi as n, Y as r, Q as t };
5644
+ export { S as i, gi as n, Y as r, $ as t };