@voltro/cli 0.29.0 → 0.30.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 (169) hide show
  1. package/CHANGELOG.md +448 -0
  2. package/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/apiBuild-Ca6Sq5jy.js +2 -0
  4. package/dist/{apiBuild-BESZbTjA.js → apiBuild-DmkzRyv7.js} +2 -2
  5. package/dist/bin.js +3 -3
  6. package/dist/{checkCommand-BPAtyWs8.js → checkCommand-C4konUy9.js} +435 -304
  7. package/dist/checkCommand-DHD0aBeR.js +2 -0
  8. package/dist/{commands-82BDEktg.js → commands-BkJft9K2.js} +2413 -1814
  9. package/dist/{dbCommand-bCBbk6tz.js → dbCommand-qn__kLEt.js} +344 -265
  10. package/dist/dbCommand-wfGi3FgG.js +2 -0
  11. package/dist/{dev-Cg4BKLmi.js → dev-C_NPcLDp.js} +2716 -1782
  12. package/dist/{dev-DeEJj5T4.js → dev-DXhhZ7-f.js} +1 -1
  13. package/dist/frameworkTableAssembly-Bn_c4Mnl.js +2 -0
  14. package/dist/{frameworkTableAssembly-4Db40V23.js → frameworkTableAssembly-Cpgn2e88.js} +155 -141
  15. package/dist/index.d.ts +7 -2
  16. package/dist/index.js +1 -1
  17. package/dist/{inspect-BMvQpwqy.js → inspect-BUUjt773.js} +150 -14
  18. package/dist/inspect-mmBuRXmy.js +2 -0
  19. package/dist/{inspectMetrics-DHYygE4s.js → inspectMetrics-CkB2J0Zn.js} +566 -512
  20. package/dist/{manifestBuild-C4R1EdHn.js → manifestBuild-BLrVuSlM.js} +1 -1
  21. package/dist/manifestBuild-Dj8Jjoto.js +2 -0
  22. package/dist/{sdkgen-CW5NbYF0.js → sdkgen-wsWLrq4l.js} +1 -1
  23. package/dist/serveCommand-HtI4X5kj.js +1577 -0
  24. package/dist/serveEntry.js +2 -2
  25. package/dist/{start-CsCIaW4-.js → start-egkbpugc.js} +177 -175
  26. package/dist/startEntry.js +2 -2
  27. package/package.json +17 -17
  28. package/templates/AGENTS.core.md +50 -0
  29. package/templates/AGENTS.md +53 -2
  30. package/templates/agent-docs/_index.md +3 -2
  31. package/templates/agent-docs/_manifest.json +16 -4
  32. package/templates/agent-docs/ai.md +56 -0
  33. package/templates/agent-docs/cli.md +46 -0
  34. package/templates/agent-docs/internationalization.md +8 -7
  35. package/templates/agent-docs/local-first-mobile.md +18 -6
  36. package/templates/agent-docs/routing.md +15 -1
  37. package/templates/agent-docs/security.md +17 -0
  38. package/templates/agent-docs/templates/apibackends.md +209 -0
  39. package/templates/agent-docs/templates/appshells.md +144 -1
  40. package/templates/agent-docs/templates/mobile.md +60 -0
  41. package/templates/agent-docs/templates/overview.md +17 -7
  42. package/templates/agent-docs/whats-new.md +36 -243
  43. package/templates/agent-docs/workflows.md +367 -0
  44. package/templates/apps/api-ai/package.json +7 -7
  45. package/templates/apps/api-auth/package.json +8 -8
  46. package/templates/apps/api-backend/package.json +7 -7
  47. package/templates/apps/api-backend-deactivation/package.json +7 -7
  48. package/templates/apps/api-backend-mail/package.json +8 -8
  49. package/templates/apps/api-backend-mariadb/package.json +9 -9
  50. package/templates/apps/api-backend-sqlite/package.json +8 -8
  51. package/templates/apps/api-backend-storage/package.json +8 -8
  52. package/templates/apps/api-cms/package.json +10 -10
  53. package/templates/apps/api-collab/README.md +70 -0
  54. package/templates/apps/api-collab/app.config.ts +28 -0
  55. package/templates/apps/api-collab/database/schema.ts +63 -0
  56. package/templates/apps/api-collab/mutations/documents.create.mutation.server.ts +19 -0
  57. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +36 -0
  58. package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +20 -0
  59. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +30 -0
  60. package/templates/apps/api-collab/package.json +30 -0
  61. package/templates/apps/api-collab/queries/documents.query.server.ts +16 -0
  62. package/templates/apps/api-collab/queries/documents.query.ts +23 -0
  63. package/templates/apps/api-collab/template.json +6 -0
  64. package/templates/apps/api-collab/tests/documents.setBody.test.ts +89 -0
  65. package/templates/apps/api-collab/tsconfig.json +5 -0
  66. package/templates/apps/api-data-advanced/package.json +8 -8
  67. package/templates/apps/api-durable/package.json +8 -8
  68. package/templates/apps/api-feature-flags/package.json +9 -9
  69. package/templates/apps/api-governance/package.json +8 -8
  70. package/templates/apps/api-kv/package.json +8 -8
  71. package/templates/apps/api-moderation/package.json +8 -8
  72. package/templates/apps/api-observability/package.json +8 -8
  73. package/templates/apps/api-ratelimit/package.json +8 -8
  74. package/templates/apps/api-rbac/package.json +8 -8
  75. package/templates/apps/api-rest/package.json +7 -7
  76. package/templates/apps/api-saas/package.json +11 -11
  77. package/templates/apps/api-saas-starter/package.json +10 -10
  78. package/templates/apps/api-search/package.json +8 -8
  79. package/templates/apps/api-status/package.json +8 -8
  80. package/templates/apps/api-versioning/package.json +8 -8
  81. package/templates/apps/api-webhooks/package.json +9 -9
  82. package/templates/apps/changelog/package.json +6 -6
  83. package/templates/apps/edge-functions/package.json +2 -2
  84. package/templates/apps/frontend-admin/app.config.ts +1 -1
  85. package/templates/apps/frontend-admin/package.json +8 -8
  86. package/templates/apps/frontend-admin/src/locales/index.ts +1 -1
  87. package/templates/apps/frontend-app/app.config.ts +1 -1
  88. package/templates/apps/frontend-app/package.json +8 -8
  89. package/templates/apps/frontend-app/src/locales/index.ts +1 -1
  90. package/templates/apps/frontend-app/src/pages/layout.tsx +1 -1
  91. package/templates/apps/frontend-app/src/pages/page.tsx +1 -1
  92. package/templates/apps/frontend-auth/package.json +8 -8
  93. package/templates/apps/frontend-auth/src/locales/index.ts +1 -1
  94. package/templates/apps/frontend-blank/app.config.ts +1 -1
  95. package/templates/apps/frontend-blank/package.json +7 -7
  96. package/templates/apps/frontend-blank/src/locales/index.ts +1 -1
  97. package/templates/apps/frontend-blank/src/pages/layout.tsx +1 -1
  98. package/templates/apps/frontend-blank/src/pages/page.tsx +1 -1
  99. package/templates/apps/frontend-cms/package.json +9 -9
  100. package/templates/apps/frontend-cms/src/locales/index.ts +1 -1
  101. package/templates/apps/frontend-collab/README.md +80 -0
  102. package/templates/apps/frontend-collab/app.config.ts +38 -0
  103. package/templates/apps/frontend-collab/package.json +33 -0
  104. package/templates/apps/frontend-collab/src/locales/de.ts +29 -0
  105. package/templates/apps/frontend-collab/src/locales/en.ts +31 -0
  106. package/templates/apps/frontend-collab/src/locales/index.ts +15 -0
  107. package/templates/apps/frontend-collab/src/pages/layout.tsx +39 -0
  108. package/templates/apps/frontend-collab/src/pages/page.test.tsx +150 -0
  109. package/templates/apps/frontend-collab/src/pages/page.tsx +186 -0
  110. package/templates/apps/frontend-collab/template.json +6 -0
  111. package/templates/apps/frontend-collab/tsconfig.json +11 -0
  112. package/templates/apps/frontend-contact/package.json +7 -7
  113. package/templates/apps/frontend-dashboard/app.config.ts +1 -1
  114. package/templates/apps/frontend-dashboard/package.json +7 -7
  115. package/templates/apps/frontend-dashboard/src/locales/index.ts +1 -1
  116. package/templates/apps/frontend-docs/package.json +7 -7
  117. package/templates/apps/frontend-i18n/package.json +6 -6
  118. package/templates/apps/frontend-landing/package.json +7 -7
  119. package/templates/apps/frontend-portal/package.json +8 -8
  120. package/templates/apps/frontend-portal/src/locales/index.ts +1 -1
  121. package/templates/apps/frontend-saas/README.md +1 -1
  122. package/templates/apps/frontend-saas/app.config.ts +1 -1
  123. package/templates/apps/frontend-saas/package.json +8 -8
  124. package/templates/apps/frontend-saas/src/locales/index.ts +1 -1
  125. package/templates/apps/frontend-spa/app.config.ts +1 -1
  126. package/templates/apps/frontend-spa/package.json +7 -7
  127. package/templates/apps/frontend-spa/src/locales/index.ts +1 -1
  128. package/templates/apps/frontend-spa/src/pages/layout.tsx +1 -1
  129. package/templates/apps/frontend-spa/src/pages/page.tsx +1 -1
  130. package/templates/apps/frontend-ssr/app.config.ts +1 -1
  131. package/templates/apps/frontend-ssr/package.json +7 -7
  132. package/templates/apps/frontend-ssr/src/locales/index.ts +1 -1
  133. package/templates/apps/frontend-ssr/src/pages/layout.tsx +1 -1
  134. package/templates/apps/frontend-ssr/src/pages/page.tsx +1 -1
  135. package/templates/apps/frontend-ssr-api/app.config.ts +1 -1
  136. package/templates/apps/frontend-ssr-api/package.json +8 -8
  137. package/templates/apps/frontend-ssr-api/src/locales/index.ts +1 -1
  138. package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +1 -1
  139. package/templates/apps/frontend-static-blog/package.json +6 -6
  140. package/templates/apps/frontend-status/package.json +8 -8
  141. package/templates/apps/frontend-status/src/locales/index.ts +1 -1
  142. package/templates/apps/mobile-app/README.md +58 -0
  143. package/templates/apps/mobile-app/app.config.ts +33 -0
  144. package/templates/apps/mobile-app/babel.config.js +5 -0
  145. package/templates/apps/mobile-app/metro.config.js +20 -0
  146. package/templates/apps/mobile-app/package.json +33 -0
  147. package/templates/apps/mobile-app/src/app/_layout.tsx +61 -0
  148. package/templates/apps/mobile-app/src/app/index.tsx +62 -0
  149. package/templates/apps/mobile-app/src/app/orders/[id].tsx +19 -0
  150. package/templates/apps/mobile-app/src/app/settings.tsx +51 -0
  151. package/templates/apps/mobile-app/src/client.ts +74 -0
  152. package/templates/apps/mobile-app/src/lib/deeplinks.ts +39 -0
  153. package/templates/apps/mobile-app/src/lib/notifications.ts +31 -0
  154. package/templates/apps/mobile-app/src/lib/sync.ts +29 -0
  155. package/templates/apps/mobile-app/src/persistence.ts +38 -0
  156. package/templates/apps/mobile-app/template.json +6 -0
  157. package/templates/apps/mobile-app/tests/deeplinks.test.ts +32 -0
  158. package/templates/apps/mobile-app/tests/notifications.test.ts +26 -0
  159. package/templates/apps/mobile-app/tests/sync.test.ts +34 -0
  160. package/templates/apps/mobile-app/tsconfig.json +10 -0
  161. package/templates/apps/mobile-app/voltro.mobile.ts +11 -0
  162. package/templates/patches/@effect__cluster@0.60.0.patch +6 -6
  163. package/dist/apiBuild-C8d74T6g.js +0 -2
  164. package/dist/checkCommand-DBvZvCh1.js +0 -2
  165. package/dist/dbCommand-CaIVTp-a.js +0 -2
  166. package/dist/frameworkTableAssembly-BTNirAAk.js +0 -2
  167. package/dist/inspect-DHeaMZak.js +0 -2
  168. package/dist/manifestBuild-Ck_90gZy.js +0 -2
  169. package/dist/serveCommand-BXl8mhZD.js +0 -1489
@@ -1,7 +1,7 @@
1
- import { R as e, f as t, ht as n, n as r, o as i } from "./inspectMetrics-DHYygE4s.js";
2
- import { Mt as a, jt as o } from "./dev-Cg4BKLmi.js";
1
+ import { R as e, f as t, ht as n, n as r, o as i } from "./inspectMetrics-CkB2J0Zn.js";
2
+ import { Ut as a, Wt as o } from "./dev-C_NPcLDp.js";
3
3
  import { C as s, E as c, O as l, T as u, d, h as f, i as p, l as m, m as h, r as g, u as _ } from "./fileConventions-Cof68_BL.js";
4
- import { r as v, u as y } from "./apiBuild-BESZbTjA.js";
4
+ import { r as v, u as y } from "./apiBuild-DmkzRyv7.js";
5
5
  import { dirname as b, isAbsolute as ee, join as x, relative as S, resolve as C, sep as w } from "node:path";
6
6
  import { routeDiagnosticsToStderr as te } from "@voltro/logger";
7
7
  import { existsSync as T, readFileSync as E, readdirSync as D, statSync as O, writeFileSync as ne } from "node:fs";
@@ -442,7 +442,7 @@ var re = () => {
442
442
  re: l,
443
443
  needsTest: !1
444
444
  }
445
- ], R = (e) => Ce.find((t) => t.re.test(e))?.id ?? null, z = (e) => u.test(e), we = /* @__PURE__ */ new Set([
445
+ ], R = (e) => Ce.find((t) => t.re.test(e))?.id ?? null, we = (e) => u.test(e), Te = /* @__PURE__ */ new Set([
446
446
  "useMutation",
447
447
  "useAction",
448
448
  "useUpload",
@@ -452,12 +452,12 @@ var re = () => {
452
452
  "useAgentStream",
453
453
  "useResumableAgentStream",
454
454
  "useWorkflow"
455
- ]), Te = /* @__PURE__ */ new Set(["defineTracking"]), Ee = /* @__PURE__ */ new Set([
455
+ ]), Ee = /* @__PURE__ */ new Set(["defineTracking"]), De = /* @__PURE__ */ new Set([
456
456
  "useSubscription",
457
457
  "useWindowedSubscription",
458
458
  "useRecord",
459
459
  "useQuery"
460
- ]), De = /* @__PURE__ */ new Set([
460
+ ]), Oe = /* @__PURE__ */ new Set([
461
461
  "node_modules",
462
462
  "dist",
463
463
  "build",
@@ -468,7 +468,7 @@ var re = () => {
468
468
  ".git",
469
469
  "coverage",
470
470
  ".publish"
471
- ]), Oe = /\.tsx?$/, B = (e, t = [], n = 0) => {
471
+ ]), ke = /\.tsx?$/, z = (e, t = [], n = 0) => {
472
472
  if (n > 12) return t;
473
473
  let r;
474
474
  try {
@@ -477,27 +477,27 @@ var re = () => {
477
477
  return t;
478
478
  }
479
479
  for (let i of r) {
480
- if (De.has(i)) continue;
480
+ if (Oe.has(i)) continue;
481
481
  let r = x(e, i), a;
482
482
  try {
483
483
  a = O(r);
484
484
  } catch {
485
485
  continue;
486
486
  }
487
- a.isDirectory() ? B(r, t, n + 1) : a.isFile() && Oe.test(i) && !i.endsWith(".d.ts") && t.push(r);
487
+ a.isDirectory() ? z(r, t, n + 1) : a.isFile() && ke.test(i) && !i.endsWith(".d.ts") && t.push(r);
488
488
  }
489
489
  return t;
490
- }, ke = /\b(?:FC|FunctionComponent|ComponentType|ForwardRefExoticComponent|MemoExoticComponent)\b/, Ae = /\b(?:memo|forwardRef|observer|styled)\b/, V = (e) => k.isArrowFunction(e) || k.isFunctionExpression(e) || k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isClassExpression(e), H = (e) => {
491
- if (V(e)) return !0;
492
- if (k.isExportAssignment(e) || k.isAsExpression(e) || k.isSatisfiesExpression(e) || k.isParenthesizedExpression(e)) return H(e.getExpression());
490
+ }, Ae = /\b(?:FC|FunctionComponent|ComponentType|ForwardRefExoticComponent|MemoExoticComponent)\b/, je = /\b(?:memo|forwardRef|observer|styled)\b/, B = (e) => k.isArrowFunction(e) || k.isFunctionExpression(e) || k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isClassExpression(e), V = (e) => {
491
+ if (B(e)) return !0;
492
+ if (k.isExportAssignment(e) || k.isAsExpression(e) || k.isSatisfiesExpression(e) || k.isParenthesizedExpression(e)) return V(e.getExpression());
493
493
  if (k.isVariableDeclaration(e)) {
494
494
  let t = e.getTypeNode();
495
- if (t !== void 0 && ke.test(t.getText())) return !0;
495
+ if (t !== void 0 && Ae.test(t.getText())) return !0;
496
496
  let n = e.getInitializer();
497
- return n === void 0 || H(n);
497
+ return n === void 0 || V(n);
498
498
  }
499
- return k.isCallExpression(e) ? Ae.test(e.getExpression().getText()) ? !0 : e.getArguments().some((e) => V(e) || k.isIdentifier(e)) : k.isTaggedTemplateExpression(e) ? !0 : !(k.isObjectLiteralExpression(e) || k.isArrayLiteralExpression(e) || k.isStringLiteral(e) || k.isNoSubstitutionTemplateLiteral(e) || k.isNumericLiteral(e) || k.isTemplateExpression(e) || k.isNewExpression(e) || k.isPropertyAccessExpression(e) || e.getKind() === j.TrueKeyword || e.getKind() === j.FalseKeyword || e.getKind() === j.NullKeyword);
500
- }, je = (e) => {
499
+ return k.isCallExpression(e) ? je.test(e.getExpression().getText()) ? !0 : e.getArguments().some((e) => B(e) || k.isIdentifier(e)) : k.isTaggedTemplateExpression(e) ? !0 : !(k.isObjectLiteralExpression(e) || k.isArrayLiteralExpression(e) || k.isStringLiteral(e) || k.isNoSubstitutionTemplateLiteral(e) || k.isNumericLiteral(e) || k.isTemplateExpression(e) || k.isNewExpression(e) || k.isPropertyAccessExpression(e) || e.getKind() === j.TrueKeyword || e.getKind() === j.FalseKeyword || e.getKind() === j.NullKeyword);
500
+ }, Me = (e) => {
501
501
  let t = [], n = [], r = [], i = /* @__PURE__ */ new Set();
502
502
  for (let [a, o] of e.getExportedDeclarations()) {
503
503
  let e = o[0];
@@ -508,7 +508,7 @@ var re = () => {
508
508
  n.push(s);
509
509
  continue;
510
510
  }
511
- if (/^[A-Z]/.test(s) && H(e)) {
511
+ if (/^[A-Z]/.test(s) && V(e)) {
512
512
  t.push(s);
513
513
  continue;
514
514
  }
@@ -520,7 +520,7 @@ var re = () => {
520
520
  others: r,
521
521
  hasRuntimeExport: t.length + n.length + r.length > 0
522
522
  };
523
- }, Me = [], Ne = (e, t, n) => {
523
+ }, Ne = [], Pe = (e, t, n) => {
524
524
  let r = b(e), i = [];
525
525
  for (let e = r;; e = b(e)) {
526
526
  let r = n.get(e);
@@ -537,9 +537,9 @@ var re = () => {
537
537
  }
538
538
  if (e === t || b(e) === e) break;
539
539
  }
540
- for (let e of i) n.set(e, Me);
541
- return Me;
542
- }, Pe = (e, t, n) => {
540
+ for (let e of i) n.set(e, Ne);
541
+ return Ne;
542
+ }, Fe = (e, t, n) => {
543
543
  let r = t.startsWith(".") ? C(b(e), t) : ye(t, n);
544
544
  if (r === void 0) return null;
545
545
  for (let e of [
@@ -552,9 +552,9 @@ var re = () => {
552
552
  if (O(e).isFile()) return e;
553
553
  } catch {}
554
554
  return null;
555
- }, Fe = (e, t, n) => {
555
+ }, Ie = (e, t, n) => {
556
556
  let r = [], i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Map(), o = (e) => {
557
- let i = Pe(t, e, n);
557
+ let i = Fe(t, e, n);
558
558
  return i !== null && r.push(i), i;
559
559
  };
560
560
  for (let t of e.getImportDeclarations()) if (o(t.getModuleSpecifierValue()), !t.isTypeOnly()) for (let e of t.getNamedImports()) e.isTypeOnly() || i.add(e.getName());
@@ -581,22 +581,22 @@ var re = () => {
581
581
  namedBindings: i,
582
582
  reExports: a
583
583
  };
584
- }, Ie = (e) => {
584
+ }, Le = (e) => {
585
585
  let t = /* @__PURE__ */ new Map();
586
586
  for (let n of e.getDescendantsOfKind(j.CallExpression)) {
587
587
  let e = n.getExpression(), r = k.isPropertyAccessExpression(e) ? e.getName() : e.getText();
588
588
  t.set(r, (t.get(r) ?? 0) + 1);
589
589
  }
590
590
  return t;
591
- }, Le = (e) => {
591
+ }, Re = (e) => {
592
592
  if (e.length <= 1) return e.length === 1;
593
593
  let t = [...e].sort((e, t) => e.length - t.length)[0];
594
594
  return e.every((e) => e.startsWith(t));
595
- }, Re = (e, t) => {
595
+ }, ze = (e, t) => {
596
596
  let n = b(t);
597
597
  return e.startsWith(`${n}${w}`) || b(e) === n;
598
- }, U = (e) => {
599
- let t = B(e);
598
+ }, H = (e) => {
599
+ let t = z(e);
600
600
  if (t.length === 0) return {
601
601
  findings: [],
602
602
  declared: 0,
@@ -627,7 +627,7 @@ var re = () => {
627
627
  } catch {
628
628
  continue;
629
629
  }
630
- s.set(t, je(r)), c.set(t, Fe(r, t, Ne(t, e, u))), l.set(t, Ie(r));
630
+ s.set(t, Me(r)), c.set(t, Ie(r, t, Pe(t, e, u))), l.set(t, Le(r));
631
631
  }
632
632
  let d = /* @__PURE__ */ new Map();
633
633
  for (let [e, t] of c) for (let n of t.localTargets) {
@@ -659,11 +659,11 @@ var re = () => {
659
659
  for (let e of r) {
660
660
  let t = R(e), n = s.get(e), r = c.get(e);
661
661
  if (n === void 0 || r === void 0) continue;
662
- let i = z(e);
662
+ let i = we(e);
663
663
  t !== null && !i && (m += 1);
664
664
  let u = d.get(e) ?? [];
665
665
  if ((t === "component" || t === "component.ui") && !i) {
666
- let t = Le(n.components);
666
+ let t = Re(n.components);
667
667
  (n.components.length === 0 || !t) && a.push({
668
668
  rule: "component/one-per-file",
669
669
  file: o(e),
@@ -701,7 +701,7 @@ var re = () => {
701
701
  ].join(", ")}`,
702
702
  fix: "a *.types.ts has NO runtime export. That is what makes importing it provably free AND makes it impossible for it to sit in a runtime import cycle — the guarantee is the point, so move the value to a sibling module"
703
703
  }), t === "internal") {
704
- let t = u.filter((t) => !Re(t, e));
704
+ let t = u.filter((t) => !ze(t, e));
705
705
  t.length > 0 && a.push({
706
706
  rule: "internal/foreign-import",
707
707
  file: o(e),
@@ -711,7 +711,7 @@ var re = () => {
711
711
  });
712
712
  }
713
713
  if (t === "fixture") {
714
- let t = u.filter((e) => !z(e) && R(e) !== "fixture");
714
+ let t = u.filter((e) => !we(e) && R(e) !== "fixture");
715
715
  t.length > 0 && a.push({
716
716
  rule: "fixture/production-import",
717
717
  file: o(e),
@@ -721,7 +721,7 @@ var re = () => {
721
721
  });
722
722
  }
723
723
  if (t === "component.ui" && !i) {
724
- let t = [...r.namedBindings].filter((e) => we.has(e)).sort();
724
+ let t = [...r.namedBindings].filter((e) => Te.has(e)).sort();
725
725
  t.length > 0 && a.push({
726
726
  rule: "ui/no-write",
727
727
  file: o(e),
@@ -756,7 +756,7 @@ var re = () => {
756
756
  detail: t === 0 ? "declares a store file but calls no defineStore()" : `declares ${t} stores`,
757
757
  fix: "a store file defines exactly ONE store — the SSR seed pass and devtools address stores by name, so two in a file makes both harder to find and neither easier to reason about"
758
758
  });
759
- let n = [...l.get(e)?.keys() ?? []].filter((e) => Ee.has(e)).sort();
759
+ let n = [...l.get(e)?.keys() ?? []].filter((e) => De.has(e)).sort();
760
760
  n.length > 0 && a.push({
761
761
  rule: "store/mirrors-server-state",
762
762
  file: o(e),
@@ -766,7 +766,7 @@ var re = () => {
766
766
  });
767
767
  }
768
768
  if (t !== "tracking" && !i) {
769
- let t = [...l.get(e)?.keys() ?? []].filter((e) => Te.has(e)).sort();
769
+ let t = [...l.get(e)?.keys() ?? []].filter((e) => Ee.has(e)).sort();
770
770
  t.length > 0 && a.push({
771
771
  rule: "tracking/outside-tracking-file",
772
772
  file: o(e),
@@ -798,8 +798,8 @@ var re = () => {
798
798
  scanned: r.length,
799
799
  vendored: n
800
800
  };
801
- }, ze = 10, Be = (e) => {
802
- let { findings: t, declared: n, scanned: r, vendored: i } = U(e);
801
+ }, Be = 10, Ve = (e) => {
802
+ let { findings: t, declared: n, scanned: r, vendored: i } = H(e);
803
803
  console.log("");
804
804
  for (let t of Se(e, i)) console.log(`· not ours, exempt: ${t}`);
805
805
  if (r === 0) return console.log("• file-taxonomy scan: no sources here — nothing to check."), 0;
@@ -813,12 +813,12 @@ var re = () => {
813
813
  console.log(`• file taxonomy: ${t.length} finding(s) — ${o} error, ${t.length - o} advisory (${n} declared / ${r} scanned):`);
814
814
  for (let [e, t] of [...a].sort()) {
815
815
  console.log(` ${t[0].severity === "error" ? "✗" : "·"} ${e} (${t.length})`);
816
- for (let e of t.slice(0, ze)) console.log(` ${e.file} — ${e.detail}`);
817
- let n = t.length - ze;
816
+ for (let e of t.slice(0, Be)) console.log(` ${e.file} — ${e.detail}`);
817
+ let n = t.length - Be;
818
818
  n > 0 && console.log(` (+${n} more — \`voltro doctor --json\` prints every one.)`), console.log(` → ${t[0].fix}`);
819
819
  }
820
820
  return t.length;
821
- }, Ve = /* @__PURE__ */ new Set([
821
+ }, He = /* @__PURE__ */ new Set([
822
822
  "eq",
823
823
  "ne",
824
824
  "gt",
@@ -836,10 +836,10 @@ var re = () => {
836
836
  "endsWith",
837
837
  "between",
838
838
  "jsonField"
839
- ]), He = (e) => {
839
+ ]), Ue = (e) => {
840
840
  if (k.isPropertyAccessExpression(e)) return e.getName();
841
841
  if (k.isIdentifier(e)) return e.getText();
842
- }, Ue = (e) => {
842
+ }, We = (e) => {
843
843
  let t = e;
844
844
  for (; k.isCallExpression(t);) {
845
845
  let e = t.getExpression();
@@ -847,7 +847,7 @@ var re = () => {
847
847
  t = e.getExpression();
848
848
  }
849
849
  return t;
850
- }, We = (e, t, n) => {
850
+ }, Ge = (e, t, n) => {
851
851
  let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new A({
852
852
  useInMemoryFileSystem: !0,
853
853
  skipAddingFilesFromTsConfig: !0
@@ -865,7 +865,7 @@ var re = () => {
865
865
  for (let t of e.getDescendantsOfKind(j.CallExpression)) {
866
866
  let e = t.getExpression();
867
867
  if (!k.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
868
- let n = He(Ue(e.getExpression()));
868
+ let n = Ue(We(e.getExpression()));
869
869
  if (n === void 0 || !r.has(n)) {
870
870
  let t = e.getExpression();
871
871
  if (k.isCallExpression(t)) {
@@ -888,7 +888,7 @@ var re = () => {
888
888
  else for (let e of [u, ...u.getDescendantsOfKind(j.CallExpression)]) {
889
889
  if (!k.isCallExpression(e)) continue;
890
890
  let t = e.getExpression(), n = k.isIdentifier(t) ? t.getText() : k.isPropertyAccessExpression(t) ? t.getName() : void 0;
891
- if (n === void 0 || !Ve.has(n)) continue;
891
+ if (n === void 0 || !He.has(n)) continue;
892
892
  let r = e.getArguments()[0];
893
893
  r !== void 0 && k.isStringLiteral(r) && l.push({
894
894
  column: r.getLiteralValue(),
@@ -909,16 +909,16 @@ var re = () => {
909
909
  issues: a,
910
910
  checked: o
911
911
  };
912
- }, W = 12, Ge = (e) => {
912
+ }, U = 12, Ke = (e) => {
913
913
  if (e.checked === 0) return null;
914
914
  if (e.issues.length === 0) return `predicate columns: ${e.checked} checked — every filtered column exists on its table`;
915
915
  let t = [`predicate columns: ${e.issues.length} filter${e.issues.length, "s"} on a column that does not exist (${e.checked} checked)`];
916
916
  for (let n of e.issues) {
917
- let e = n.available.slice(0, W).join(", "), r = n.available.length > W ? `, … (${n.available.length - W} more)` : "";
917
+ let e = n.available.slice(0, U).join(", "), r = n.available.length > U ? `, … (${n.available.length - U} more)` : "";
918
918
  t.push(` ${n.file}:${n.line} '${n.table}' has no column '${n.column}'`), t.push(` columns: ${e}${r}`);
919
919
  }
920
920
  return t.push(" This type-checks today and fails at runtime as a bare SQL error."), t.join("\n");
921
- }, Ke = /* @__PURE__ */ new Set([
921
+ }, qe = /* @__PURE__ */ new Set([
922
922
  "id",
923
923
  "tenantId",
924
924
  "tenant_id",
@@ -938,7 +938,7 @@ var re = () => {
938
938
  "idempotency_id",
939
939
  "idempotencyKey",
940
940
  "idempotency_key"
941
- ]), qe = /* @__PURE__ */ new Set([
941
+ ]), Je = /* @__PURE__ */ new Set([
942
942
  "id",
943
943
  "createdAt",
944
944
  "created_at",
@@ -948,10 +948,10 @@ var re = () => {
948
948
  "deleted_at",
949
949
  "tenantId",
950
950
  "tenant_id"
951
- ]), Je = (e) => Ke.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), Ye = (e) => e.type === "reference", Xe = (e) => {
951
+ ]), Ye = (e) => qe.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), Xe = (e) => e.type === "reference", Ze = (e) => {
952
952
  let t = [];
953
953
  for (let n of e) {
954
- let e = n.columns.filter((e) => Je(e.name)), r = e.filter((e) => !Ye(e)).map((e) => e.name);
954
+ let e = n.columns.filter((e) => Ye(e.name)), r = e.filter((e) => !Xe(e)).map((e) => e.name);
955
955
  if (r.length === 0) continue;
956
956
  let i = new Set(n.primaryKey), a = r.filter((e) => i.has(e));
957
957
  if (n.primaryKey.length >= 2 && a.length > 0) {
@@ -962,7 +962,7 @@ var re = () => {
962
962
  });
963
963
  continue;
964
964
  }
965
- if (e.some(Ye) && e.length >= 2) {
965
+ if (e.some(Xe) && e.length >= 2) {
966
966
  t.push({
967
967
  table: n.name,
968
968
  columns: r,
@@ -970,7 +970,7 @@ var re = () => {
970
970
  });
971
971
  continue;
972
972
  }
973
- e.length >= 2 && n.columns.filter((e) => !Je(e.name) && !qe.has(e.name)).length === 0 && t.push({
973
+ e.length >= 2 && n.columns.filter((e) => !Ye(e.name) && !Je.has(e.name)).length === 0 && t.push({
974
974
  table: n.name,
975
975
  columns: r,
976
976
  signal: "link-only"
@@ -980,20 +980,20 @@ var re = () => {
980
980
  issues: t,
981
981
  tablesChecked: e.length
982
982
  };
983
- }, Ze = {
983
+ }, Qe = {
984
984
  "composite-pk": "part of a composite primary key",
985
985
  "reference-sibling": "sits beside a wired reference() on this table",
986
986
  "link-only": "this table is nothing but link columns"
987
- }, Qe = (e) => {
987
+ }, $e = (e) => {
988
988
  if (e.tablesChecked === 0) return null;
989
989
  if (e.issues.length === 0) return `junction FKs: ${e.tablesChecked} table${e.tablesChecked === 1 ? "" : "s"} checked — every link column is a wired reference()`;
990
990
  let t = e.issues.length, n = [`junction FKs: ${t} junction table${t === 1 ? "" : "s"} with an id column that is a plain text() and not a reference()`];
991
991
  for (let t of e.issues) {
992
992
  let e = t.columns.map((e) => `'${e}'`).join(", ");
993
- n.push(` '${t.table}': ${e} (${Ze[t.signal]})`);
993
+ n.push(` '${t.table}': ${e} (${Qe[t.signal]})`);
994
994
  }
995
995
  return n.push(" Declare each as reference(() => <table>): the FK is enforced, the column is auto-indexed,"), n.push(" and the relationship becomes walkable (a plain text() id column is an invisible edge)."), n.join("\n");
996
- }, $e = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), et = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), tt = (e) => et(e).some((e) => $e.has(e)), nt = (e) => {
996
+ }, et = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), tt = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), nt = (e) => tt(e).some((e) => et.has(e)), rt = (e) => {
997
997
  let t = [], n = [];
998
998
  for (let r of e) t.push({
999
999
  event: r.name,
@@ -1002,7 +1002,7 @@ var re = () => {
1002
1002
  event: r.name,
1003
1003
  kind: "wide-key",
1004
1004
  keyFieldCount: r.keyFieldCount
1005
- }), r.hasWebhook && tt(r.name) && n.push({
1005
+ }), r.hasWebhook && nt(r.name) && n.push({
1006
1006
  event: r.name,
1007
1007
  kind: "webhook-high-rate"
1008
1008
  });
@@ -1011,26 +1011,26 @@ var re = () => {
1011
1011
  modes: t,
1012
1012
  warnings: n
1013
1013
  };
1014
- }, rt = (e) => {
1014
+ }, it = (e) => {
1015
1015
  if (e.eventsChecked === 0) return null;
1016
1016
  let t = e.eventsChecked, n = [`event delivery: ${t} declared event${t === 1 ? "" : "s"} — the mode decides what a MISSING message means`];
1017
1017
  for (let t of e.modes) n.push(` '${t.event}': ${t.delivery}`);
1018
1018
  return n.push(" each — every delivery matters; a slow subscriber loses the oldest and is TOLD how many (the default)."), n.push(" latest — a newer delivery supersedes a pending one; a slow subscriber gets the current value, told nothing."), n.push(" Two events with identical route/subscriber/buffer numbers mean OPPOSITE things about a drop — the mode is the difference."), n.join("\n");
1019
- }, it = (e) => {
1019
+ }, at = (e) => {
1020
1020
  if (e.warnings.length === 0) return null;
1021
1021
  let t = e.warnings.length, n = [`events that may not scale as declared: ${t} finding${t === 1 ? "" : "s"}`];
1022
1022
  for (let t of e.warnings) t.kind === "wide-key" ? (n.push(` '${t.event}': routing key has ${t.keyFieldCount ?? 0} fields — every field FRAGMENTS the subscriber set (distinct routes are the PRODUCT of the fields’ value spaces).`), n.push(" Check each is an ADDRESS the delivery is decided by (arenaId), not a discriminator the handler reads (gameType) — the latter is payload, not key.")) : (n.push(` '${t.event}': a webhook block on a per-frame-looking event — every publish is N HTTP deliveries per second per subscribed target.`), n.push(" The webhook rate limit DEFERS the excess as pending rows rather than failing, so the symptom is a growing table. Publish a coarser event (a summary / state change) for the outside world."));
1023
1023
  return n.push(" WARNING, not a fault — a producer may intend the fan-out. `voltro doctor --json` prints every finding."), n.join("\n");
1024
- }, at = /* @__PURE__ */ new Set([
1024
+ }, ot = /* @__PURE__ */ new Set([
1025
1025
  "start",
1026
1026
  "run",
1027
1027
  "child"
1028
- ]), ot = (e) => k.isCallExpression(e) && (() => {
1028
+ ]), st = (e) => k.isCallExpression(e) && (() => {
1029
1029
  let t = e.getExpression();
1030
- if (!k.isPropertyAccessExpression(t) || !at.has(t.getName())) return !1;
1030
+ if (!k.isPropertyAccessExpression(t) || !ot.has(t.getName())) return !1;
1031
1031
  let n = t.getExpression();
1032
1032
  return k.isPropertyAccessExpression(n) ? n.getName() === "workflows" : k.isIdentifier(n) && n.getText() === "workflows";
1033
- })(), st = (e, t, n) => {
1033
+ })(), ct = (e, t, n) => {
1034
1034
  let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new A({
1035
1035
  useInMemoryFileSystem: !0,
1036
1036
  skipAddingFilesFromTsConfig: !0
@@ -1046,7 +1046,7 @@ var re = () => {
1046
1046
  }
1047
1047
  let u = S(n, t);
1048
1048
  for (let t of e.getDescendantsOfKind(j.CallExpression)) {
1049
- if (!ot(t)) continue;
1049
+ if (!st(t)) continue;
1050
1050
  c++;
1051
1051
  let e = t.getStartLineNumber(), [n, l] = t.getArguments();
1052
1052
  if (n === void 0 || !k.isStringLiteral(n)) {
@@ -1130,7 +1130,7 @@ var re = () => {
1130
1130
  checked: s,
1131
1131
  callSites: c
1132
1132
  };
1133
- }, ct = (e) => {
1133
+ }, lt = (e) => {
1134
1134
  if (e.callSites === 0) return null;
1135
1135
  let t = [];
1136
1136
  t.push(`workflow starts: ${e.checked}/${e.callSites} call sites checked`);
@@ -1143,7 +1143,7 @@ var re = () => {
1143
1143
  for (let n of e.unchecked) t.push(` ${n.file}:${n.line} — ${n.reason}`);
1144
1144
  }
1145
1145
  return e.issues.length === 0 && e.unchecked.length === 0 && t.push(" every call site matches its workflow"), t.join("\n");
1146
- }, lt = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, G = "VOLTRO_FIELD_ENCRYPTION_KEY", ut = (e) => {
1146
+ }, ut = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, W = "VOLTRO_FIELD_ENCRYPTION_KEY", dt = (e) => {
1147
1147
  let t;
1148
1148
  for (let [n, r] of e.sources) if (t === void 0 && /\bfieldEncryption\b/.test(r) && (t = {
1149
1149
  reason: "fieldEncryption",
@@ -1151,13 +1151,13 @@ var re = () => {
1151
1151
  }), t === void 0 && /\.encrypted\s*\(/.test(r) && (t = {
1152
1152
  reason: "encrypted-column",
1153
1153
  file: n
1154
- }), r.includes(G)) return;
1154
+ }), r.includes(W)) return;
1155
1155
  if (t !== void 0) {
1156
- for (let [, t] of e.envFiles) if (t.includes(G)) return;
1156
+ for (let [, t] of e.envFiles) if (t.includes(W)) return;
1157
1157
  return t;
1158
1158
  }
1159
- }, dt = (e) => [
1160
- `✗ ${G} is named nowhere under this app root, and the app encrypts columns at rest`,
1159
+ }, ft = (e) => [
1160
+ `✗ ${W} is named nowhere under this app root, and the app encrypts columns at rest`,
1161
1161
  e.reason === "fieldEncryption" ? ` (\`fieldEncryption\` in ${e.file})` : ` (an \`.encrypted()\` column in ${e.file})`,
1162
1162
  "",
1163
1163
  " A deploy without it does not degrade — it refuses to boot at `onActivate`,",
@@ -1173,7 +1173,7 @@ var re = () => {
1173
1173
  " reaches your deploy target is not knowable from here — a secret manager",
1174
1174
  " or a CI variable is not a file. Naming it nowhere is conclusive; naming",
1175
1175
  " it somewhere is not a guarantee."
1176
- ].join("\n"), ft = (e) => e === "effect" || e.startsWith("@effect/") || e.startsWith("@voltro/") || e === "react" || e === "react-dom", pt = /(^|\/)(\.bin|\.cache|\.pnpm-store|dist|build)(\/|$)/, mt = (e) => {
1176
+ ].join("\n"), pt = (e) => e === "effect" || e.startsWith("@effect/") || e.startsWith("@voltro/") || e === "react" || e === "react-dom", mt = /(^|\/)(\.bin|\.cache|\.pnpm-store|dist|build)(\/|$)/, ht = (e) => {
1177
1177
  let t = [], n = e;
1178
1178
  for (let e = 0; e < 12; e++) {
1179
1179
  let e = x(n, "node_modules");
@@ -1183,14 +1183,14 @@ var re = () => {
1183
1183
  n = r;
1184
1184
  }
1185
1185
  return t;
1186
- }, ht = (e) => {
1186
+ }, gt = (e) => {
1187
1187
  try {
1188
1188
  let t = JSON.parse(E(x(e, "package.json"), "utf8"));
1189
1189
  return typeof t.version == "string" ? t.version : void 0;
1190
1190
  } catch {
1191
1191
  return;
1192
1192
  }
1193
- }, gt = (e) => {
1193
+ }, _t = (e) => {
1194
1194
  let t = [], n;
1195
1195
  try {
1196
1196
  n = D(e);
@@ -1198,7 +1198,7 @@ var re = () => {
1198
1198
  return t;
1199
1199
  }
1200
1200
  for (let r of n) {
1201
- if (r.startsWith(".") || pt.test(r)) continue;
1201
+ if (r.startsWith(".") || mt.test(r)) continue;
1202
1202
  let n = x(e, r);
1203
1203
  if (r.startsWith("@")) {
1204
1204
  let e;
@@ -1224,11 +1224,11 @@ var re = () => {
1224
1224
  });
1225
1225
  }
1226
1226
  return t;
1227
- }, _t = (e) => {
1227
+ }, vt = (e) => {
1228
1228
  let t = /* @__PURE__ */ new Map();
1229
- for (let n of mt(e)) for (let { pkg: e, dir: r } of gt(n)) {
1230
- if (!ft(e)) continue;
1231
- let n = ht(r);
1229
+ for (let n of ht(e)) for (let { pkg: e, dir: r } of _t(n)) {
1230
+ if (!pt(e)) continue;
1231
+ let n = gt(r);
1232
1232
  if (n === void 0) continue;
1233
1233
  let i = t.get(e) ?? /* @__PURE__ */ new Map();
1234
1234
  i.has(n) || i.set(n, r), t.set(e, i);
@@ -1240,7 +1240,7 @@ var re = () => {
1240
1240
  paths: [...i.values()].map((t) => S(e, t)).sort()
1241
1241
  });
1242
1242
  return n.sort((e, t) => e.pkg.localeCompare(t.pkg));
1243
- }, vt = (e) => {
1243
+ }, yt = (e) => {
1244
1244
  if (e.length !== 0) return [
1245
1245
  `• ${e.length} package(s) resolved at more than one version:`,
1246
1246
  ...e.flatMap((e) => [` ${e.pkg} — ${e.versions.join(", ")}`, ...e.paths.map((e) => ` ${e}`)]),
@@ -1258,33 +1258,52 @@ var re = () => {
1258
1258
  " instrument), then reinstall. Do not silence the generated file — it is where",
1259
1259
  " a genuine mistake in your own descriptors surfaces."
1260
1260
  ].join("\n");
1261
- }, yt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), bt = /* @__PURE__ */ new Set([
1261
+ }, bt = (e) => {
1262
+ if (e.catalogCodes.length === 0) return;
1263
+ let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
1264
+ if (n.length !== 0) return {
1265
+ orphaned: n,
1266
+ noneDeclared: t.size === 0
1267
+ };
1268
+ }, xt = (e, t) => {
1269
+ let n = e.orphaned.join(", ");
1270
+ return e.noneDeclared ? [
1271
+ `⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
1272
+ " Nothing imports them: no provider is generated and `useT()` would not find a catalog.",
1273
+ " Either declare them in your app config:",
1274
+ "",
1275
+ ` locales: [${e.orphaned.map((e) => `'${e}'`).join(", ")}],`,
1276
+ "",
1277
+ " …or delete the files. A catalog that is never loaded looks from the inside",
1278
+ " exactly like one that works, which is why this is worth a line."
1279
+ ] : [`⚠ ${t} has catalog(s) not in \`locales:\` — ${n} — so they are never imported.`, " Add the code(s) to `locales:` in your app config, or delete the file(s)."];
1280
+ }, St = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), Ct = /* @__PURE__ */ new Set([
1262
1281
  "requireScope",
1263
1282
  "requireAnyScope",
1264
1283
  "requireAuth",
1265
1284
  "assertScope",
1266
1285
  "assertCan",
1267
1286
  "can"
1268
- ]), xt = (e) => {
1287
+ ]), wt = (e) => {
1269
1288
  let t = /* @__PURE__ */ new Set();
1270
- for (let n of e) for (let e of yt(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
1289
+ for (let n of e) for (let e of St(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
1271
1290
  return t;
1272
- }, St = (e, t) => {
1291
+ }, Tt = (e, t) => {
1273
1292
  for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
1274
- }, Ct = (e) => {
1293
+ }, Et = (e) => {
1275
1294
  let t = /\bsubject\s*\.\s*id\b[^\n]*?(?:!==|===|!=|==)|(?:!==|===|!=|==)[^\n]*?\bsubject\s*\.\s*id\b/.test(e), n = /(?:Error|Denied|Forbidden|Unauthorized)\b|\b(?:fail|die|throw)\b/.test(e);
1276
1295
  return t && n;
1277
- }, wt = (e, t, n) => {
1296
+ }, Dt = (e, t, n) => {
1278
1297
  if (e.hasDeclaredGuards) return { kind: "declared-guard" };
1279
- let r = yt(e.src), i = St(r, t);
1280
- return i === void 0 ? Ct(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
1298
+ let r = St(e.src), i = Tt(r, t);
1299
+ return i === void 0 ? Et(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
1281
1300
  kind: "vocabulary-guard",
1282
1301
  name: i
1283
1302
  };
1284
- }, Tt = (e, t, n) => {
1303
+ }, Ot = (e, t, n) => {
1285
1304
  let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
1286
1305
  return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
1287
- }, Et = (e, t) => {
1306
+ }, kt = (e, t) => {
1288
1307
  switch (t.kind) {
1289
1308
  case "declared-guard": return "guards: on the descriptor";
1290
1309
  case "vocabulary-guard": return `calls ${t.name}()`;
@@ -1292,15 +1311,15 @@ var re = () => {
1292
1311
  case "allowlisted": return "accepted by a previous run";
1293
1312
  case "unchecked": return e.op === "read" ? "returns a row the client selected, with nothing compared to the caller" : `${e.op}s${e.table ? ` ${e.table}` : ""} with nothing constraining WHICH row`;
1294
1313
  }
1295
- }, Dt = (e) => {
1314
+ }, At = (e) => {
1296
1315
  let t = e.executors.map((t) => {
1297
- let n = wt(t, e.guardVocabulary, e.allowlist);
1316
+ let n = Dt(t, e.guardVocabulary, e.allowlist);
1298
1317
  return {
1299
1318
  tag: t.tag,
1300
1319
  path: t.path,
1301
1320
  verdict: n,
1302
- severity: Tt(t, e.tenantScopedTables, e.referencedTables),
1303
- why: Et(t, n)
1321
+ severity: Ot(t, e.tenantScopedTables, e.referencedTables),
1322
+ why: kt(t, n)
1304
1323
  };
1305
1324
  }), n = (e) => t.filter((t) => t.verdict.kind === e).length;
1306
1325
  return {
@@ -1314,26 +1333,75 @@ var re = () => {
1314
1333
  },
1315
1334
  unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
1316
1335
  };
1317
- }, Ot = (e) => [
1336
+ }, jt = [
1318
1337
  "# voltro doctor — authz allowlist",
1319
1338
  "#",
1320
- "# Executors with no access check that a previous run accepted. This is DEBT,",
1321
- "# not approval: every line is an executor nobody has confirmed is safe.",
1339
+ "# Two KINDS of line, and the difference is the point:",
1340
+ "#",
1341
+ "# <tag> DEBT, not approval. Nobody has confirmed",
1342
+ "# this executor is safe; it was accepted so",
1343
+ "# the ratchet could start. Every one of",
1344
+ "# these is owed work.",
1345
+ "#",
1346
+ "# <tag> reviewed=<why> REVIEWED. A human read it and states why it",
1347
+ "# is genuinely open — constrained by something",
1348
+ "# the scanner cannot see. The reason is",
1349
+ "# REQUIRED: \"reviewed\" with no why is the",
1350
+ "# claim without the evidence, and doctor",
1351
+ "# refuses it.",
1322
1352
  "#",
1323
1353
  "# Doctor fails on any executor NOT listed here, so the list can only shrink",
1324
1354
  "# unless someone adds a line deliberately. Delete a line once its executor",
1325
1355
  "# has a guard — doctor reports it as guarded from then on, whether or not the",
1326
1356
  "# line is still here.",
1327
- "",
1328
- ...[...e].sort(),
1357
+ "#",
1358
+ "# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
1359
+ "# lines for executors that are newly unchecked.",
1329
1360
  ""
1330
- ].join("\n"), kt = (e) => new Set(e.split("\n").map((e) => e.trim()).filter((e) => e.length > 0 && !e.startsWith("#"))), At = (e) => {
1361
+ ], Mt = (e) => {
1362
+ let t = [...e].map((e) => typeof e == "string" ? { tag: e } : e), n = t.filter((e) => e.reviewed === void 0).map((e) => e.tag).sort(), r = t.filter((e) => e.reviewed !== void 0).sort((e, t) => e.tag.localeCompare(t.tag));
1363
+ return [
1364
+ ...jt,
1365
+ ...n,
1366
+ ...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
1367
+ ...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
1368
+ ""
1369
+ ].join("\n");
1370
+ }, Nt = class extends Error {
1371
+ tag;
1372
+ constructor(e) {
1373
+ super(`authz allowlist: '${e}' is marked reviewed with no reason. Write \`${e} reviewed=<why it is genuinely open>\`. A bare tag is the other, honest option — it records the executor as DEBT, which is what an unexplained exemption is.`), this.tag = e, this.name = "AllowlistReasonMissingError";
1374
+ }
1375
+ }, Pt = (e) => {
1376
+ let t = [];
1377
+ for (let n of e.split("\n")) {
1378
+ let e = n.trim();
1379
+ if (e.length === 0 || e.startsWith("#")) continue;
1380
+ let r = /^(\S+)(?:\s+reviewed=(.*))?$/.exec(e);
1381
+ if (!r) continue;
1382
+ let i = r[1];
1383
+ if (r[2] === void 0) {
1384
+ t.push({ tag: i });
1385
+ continue;
1386
+ }
1387
+ let a = r[2].trim();
1388
+ if (a.length === 0) throw new Nt(i);
1389
+ t.push({
1390
+ tag: i,
1391
+ reviewed: a
1392
+ });
1393
+ }
1394
+ return {
1395
+ tags: new Set(t.map((e) => e.tag)),
1396
+ entries: t
1397
+ };
1398
+ }, Ft = (e) => {
1331
1399
  let t = e.indexOf(".");
1332
1400
  return t <= 0 ? void 0 : e.slice(0, t);
1333
- }, jt = (e, t) => {
1401
+ }, It = (e, t) => {
1334
1402
  let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
1335
1403
  for (let e of t.rpcTags) {
1336
- let t = At(e);
1404
+ let t = Ft(e);
1337
1405
  if (t === void 0) continue;
1338
1406
  let n = i.get(t);
1339
1407
  n ? n.push(e) : i.set(t, [e]);
@@ -1362,7 +1430,7 @@ var re = () => {
1362
1430
  });
1363
1431
  continue;
1364
1432
  }
1365
- let s = new Set(o.rpcTags.map(At).filter((e) => e !== void 0));
1433
+ let s = new Set(o.rpcTags.map(Ft).filter((e) => e !== void 0));
1366
1434
  for (let e of s) {
1367
1435
  let t = i.get(e);
1368
1436
  t !== void 0 && a.push({
@@ -1375,12 +1443,12 @@ var re = () => {
1375
1443
  }
1376
1444
  }
1377
1445
  return a;
1378
- }, Mt = (e, t) => {
1446
+ }, Lt = (e, t) => {
1379
1447
  let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
1380
1448
  if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
1381
1449
  for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
1382
1450
  return r;
1383
- }, Nt = (e) => T(C(e, "app.config.ts")) || T(C(e, "app.config.js")), Pt = (e) => {
1451
+ }, Rt = (e) => T(C(e, "app.config.ts")) || T(C(e, "app.config.js")), zt = (e) => {
1384
1452
  let t = y(e), n = v(e);
1385
1453
  return {
1386
1454
  root: e,
@@ -1389,19 +1457,19 @@ var re = () => {
1389
1457
  apiBundle: n,
1390
1458
  apiBundlePresent: T(n)
1391
1459
  };
1392
- }, Ft = [
1460
+ }, Bt = [
1393
1461
  "Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
1394
1462
  "Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
1395
1463
  "(Generated Dockerfiles from `voltro create-project` already do this.)"
1396
- ].join("\n "), It = (e, t = !1) => {
1397
- let n = Pt(e);
1398
- if (console.log(`voltro doctor — serve preflight for ${n.root}`), !Nt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
1464
+ ].join("\n "), Vt = (e, t = !1) => {
1465
+ let n = zt(e);
1466
+ if (console.log(`voltro doctor — serve preflight for ${n.root}`), !Rt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
1399
1467
  console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
1400
1468
  let r = process.env.NODE_ENV === "production";
1401
1469
  if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
1402
1470
  let i = t || r;
1403
- return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${Ft}`), +!!i;
1404
- }, Lt = (e, t, n) => {
1471
+ return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${Bt}`), +!!i;
1472
+ }, Ht = (e, t, n) => {
1405
1473
  let r = new Set(e), i = I(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
1406
1474
  `${e}.ts`,
1407
1475
  `${e}.tsx`,
@@ -1427,7 +1495,7 @@ var re = () => {
1427
1495
  r !== void 0 && o.push(r);
1428
1496
  }
1429
1497
  return o;
1430
- }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => Ut.test(S(n, e))), l = new Set(c);
1498
+ }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => Jt.test(S(n, e))), l = new Set(c);
1431
1499
  for (; c.length > 0;) {
1432
1500
  let e = c.shift();
1433
1501
  if (!s.has(e)) {
@@ -1440,11 +1508,11 @@ var re = () => {
1440
1508
  let d = /* @__PURE__ */ new Set();
1441
1509
  for (let e of s) l.has(e) || u.has(e) || d.add(e);
1442
1510
  return d;
1443
- }, Rt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), zt = (e, t) => (e.match(t) ?? []).length, K = (e, t) => e.getDescendantsOfKind(j.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, Bt = (e, t) => {
1511
+ }, Ut = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), Wt = (e, t) => (e.match(t) ?? []).length, G = (e, t) => e.getDescendantsOfKind(j.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, Gt = (e, t) => {
1444
1512
  if (e.src.includes(t)) return !0;
1445
1513
  let n = t.replace(/^@voltro\/plugin-/, "");
1446
- return n !== t && K(e.file, RegExp(`^${n}Plugin$`)) > 0;
1447
- }, Vt = (e, t) => {
1514
+ return n !== t && G(e.file, RegExp(`^${n}Plugin$`)) > 0;
1515
+ }, Kt = (e, t) => {
1448
1516
  let n = [], r = RegExp(`\\b${t}\\s*(?:<[^()]*>)?\\s*\\(`, "g"), i;
1449
1517
  for (; (i = r.exec(e)) !== null;) {
1450
1518
  let t = i.index + i[0].length, r = 1, a = t;
@@ -1452,10 +1520,10 @@ var re = () => {
1452
1520
  n.push(e.slice(t, a - 1));
1453
1521
  }
1454
1522
  return n;
1455
- }, Ht = (e, t) => {
1523
+ }, qt = (e, t) => {
1456
1524
  let n = t.getSymbol()?.getDeclarations()?.[0];
1457
1525
  return n === void 0 ? [] : e.getDescendantsOfKind(j.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
1458
- }, Ut = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, Wt = /\.(query|mutation|action|stream)\.server\.tsx?$/, Gt = [
1526
+ }, Jt = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, Yt = /\.(query|mutation|action|stream)\.server\.tsx?$/, Xt = [
1459
1527
  "Id",
1460
1528
  "ID",
1461
1529
  "Hash",
@@ -1481,13 +1549,20 @@ var re = () => {
1481
1549
  "Comment",
1482
1550
  "Provider",
1483
1551
  "Format"
1484
- ], Kt = RegExp(`(?:${Gt.join("|")})$|_(?:${Gt.map((e) => e.toLowerCase()).join("|")})$`), qt = [
1552
+ ], Zt = RegExp(`(?:${Xt.join("|")})$|_(?:${Xt.map((e) => e.toLowerCase()).join("|")})$`), Qt = [
1553
+ {
1554
+ id: "hardcoded-framework-cookie",
1555
+ scope: "client",
1556
+ smell: "a framework cookie name written as a string literal instead of imported",
1557
+ use: "import the constant — `LOCALE_COOKIE` / `THEME_COOKIE` from `@voltro/ui-shadcn` — so a rename in the framework is a compile error in your app rather than a preference that quietly stops working",
1558
+ match: ({ src: e }) => /['"`]voltro:(?:locale|theme|lang)['"`]/.test(e)
1559
+ },
1485
1560
  {
1486
1561
  id: "executor-without-descriptor",
1487
1562
  scope: "server",
1488
1563
  smell: "an executor that never names its own descriptor — its input type is hand-written, so it can drift from the contract it implements",
1489
1564
  use: "import the descriptor and type the input as `ExecutorInput<typeof descriptor>`, so the wire contract and the handler cannot disagree",
1490
- match: ({ src: e, path: t }) => Wt.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
1565
+ match: ({ src: e, path: t }) => Yt.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
1491
1566
  },
1492
1567
  {
1493
1568
  id: "subject-write-no-guard",
@@ -1495,8 +1570,8 @@ var re = () => {
1495
1570
  smell: "writes ctx.request.subject.id into the database with no guard on the descriptor — an anonymous caller reaches the write and gets a raw statement failure instead of a typed refusal",
1496
1571
  use: "add a guard (e.g. `.guard(requireScope('…'))`) or an explicit auth check, so an unauthenticated caller is refused BEFORE the write",
1497
1572
  match: ({ src: e, path: t }) => {
1498
- if (!Wt.test(t)) return !1;
1499
- let n = Rt(e);
1573
+ if (!Yt.test(t)) return !1;
1574
+ let n = Ut(e);
1500
1575
  return /\bsubject\s*\.\s*id\b/.test(n) && /\b(?:insert|update|upsert|insertMany|updateMany|insertRow|upsertRow|updateManyRow)\s*\(/.test(n) && !/\b(?:guard|guards|requireScope|requireAnyScope|assertCan|assertScope|requireAuth|Unauthenticated)\b/.test(n);
1501
1576
  }
1502
1577
  },
@@ -1505,14 +1580,14 @@ var re = () => {
1505
1580
  scope: "server",
1506
1581
  smell: "raw fetch() for outbound HTTP on the server (no SSRF guard, no traceparent, no retry)",
1507
1582
  use: "yield* HttpClient — the framework client. It blocks loopback / RFC-1918 / link-local targets (incl. 169.254.169.254) on the initial request AND every redirect hop, injects traceparent, and honours `http.allowHosts` for deliberate internal targets",
1508
- match: ({ file: e, path: t, serverOnly: n }) => (Ut.test(t) || n) && K(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
1583
+ match: ({ file: e, path: t, serverOnly: n }) => (Jt.test(t) || n) && G(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
1509
1584
  },
1510
1585
  {
1511
1586
  id: "form",
1512
1587
  scope: "client",
1513
1588
  smell: "per-field useState + a submit flag (hand-rolled form)",
1514
1589
  use: "useFormBinding — fields + validation from the mutation input Schema; server ValidationError({ field }) routes to the field",
1515
- match: ({ src: e, file: t }) => K(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
1590
+ match: ({ src: e, file: t }) => G(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
1516
1591
  },
1517
1592
  {
1518
1593
  id: "table",
@@ -1540,7 +1615,7 @@ var re = () => {
1540
1615
  scope: "client",
1541
1616
  smell: "useMemo fanning in multiple subscriptions (hand-maintained dep array)",
1542
1617
  use: "useDerived — dependency-tracked, referentially stable",
1543
- match: ({ src: e }) => zt(e, /\buseSubscription\s*[(<]/g) >= 3 && zt(e, /\buseMemo\s*\(/g) >= 3
1618
+ match: ({ src: e }) => Wt(e, /\buseSubscription\s*[(<]/g) >= 3 && Wt(e, /\buseMemo\s*\(/g) >= 3
1544
1619
  },
1545
1620
  {
1546
1621
  id: "next-shim",
@@ -1565,7 +1640,7 @@ var re = () => {
1565
1640
  match: ({ file: e }) => e.getDescendantsOfKind(j.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
1566
1641
  let t = e.getFirstAncestorByKind(j.VariableDeclaration)?.getNameNode();
1567
1642
  return t === void 0 ? [] : k.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
1568
- }).some((t) => Ht(e, t).some((e) => {
1643
+ }).some((t) => qt(e, t).some((e) => {
1569
1644
  let t = e.getParent();
1570
1645
  if (k.isBinaryExpression(t)) {
1571
1646
  let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
@@ -1593,7 +1668,7 @@ var re = () => {
1593
1668
  scope: "server",
1594
1669
  smell: "several sequential store reads assembling related data (an N+1 you are writing by hand)",
1595
1670
  use: "reads are a parent→child walk on ONE key → relations() in a *.relations.ts + .with({ … }) (ONE JSON-aggregate query, every dialect); reads collect ids from SEVERAL sources → keep the assembly and run the independent leading reads under Effect.all (same queries, same results, only concurrent — zero parity risk)",
1596
- match: (e) => K(e.file, /\bstore\.query$/) >= 3 || K(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
1671
+ match: (e) => G(e.file, /\bstore\.query$/) >= 3 || G(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
1597
1672
  },
1598
1673
  {
1599
1674
  id: "store-effect-lift",
@@ -1616,7 +1691,7 @@ var re = () => {
1616
1691
  use: ".encrypted() on the column — boot fails loudly if no cipher is configured, so it can never silently persist plaintext (a *Hash column and a vault* handle are NOT plaintext secrets: the hash IS the protection — often a unique lookup key — and the handle only names a secret held elsewhere, so both are skipped)",
1617
1692
  match: ({ src: e }) => e.split("\n").some((e) => [...e.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*(?:text|varchar|string|longtext)\s*\(/g)].some((t) => {
1618
1693
  let n = t[1];
1619
- return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Kt.test(n) || /^vault/i.test(n) ? !1 : !/\.encrypted\s*\(/.test(e);
1694
+ return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Zt.test(n) || /^vault/i.test(n) ? !1 : !/\.encrypted\s*\(/.test(e);
1620
1695
  }))
1621
1696
  },
1622
1697
  {
@@ -1680,9 +1755,9 @@ var re = () => {
1680
1755
  scope: "server",
1681
1756
  smell: "setRowFilter is registered, but no test passes `rowFilter:` to makeTestContext",
1682
1757
  use: "wire the filter into at least one test (`makeTestContext({ subject, store, rowFilter })`) and sabotage the predicate once to watch it go red — an untested row filter is a visibility rule nothing verifies",
1683
- match: ({ file: e, testSources: t }) => K(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
1684
- let t = Rt(e);
1685
- return Vt(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
1758
+ match: ({ file: e, testSources: t }) => G(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
1759
+ let t = Ut(e);
1760
+ return Kt(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
1686
1761
  })
1687
1762
  },
1688
1763
  {
@@ -1692,15 +1767,15 @@ var re = () => {
1692
1767
  use: "timestampMs / timestampMsOrNull from @voltro/database/wire in the descriptor's output struct — `output` IS the serializer (it is the rpc success schema), so a Date field declared as timestampMs crosses as epoch ms on its own; declaring Schema.Number describes the WIRE type and leaves nothing to convert",
1693
1768
  match: ({ src: e }) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
1694
1769
  }
1695
- ], Jt = (e) => J(e), Yt = (e, t) => {
1770
+ ], $t = (e) => q(e), en = (e, t) => {
1696
1771
  let n = /* @__PURE__ */ new Set();
1697
1772
  for (let r of t) {
1698
1773
  let t = S(e, r), i = t.split(/[\\/]/)[0];
1699
1774
  n.add(i !== void 0 && i !== t ? i : ".");
1700
1775
  }
1701
1776
  return [...n].sort();
1702
- }, Xt = /\.(tsx?|jsx?)$/, q = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, Zt = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, Qt = (e, t = [], n = 0) => {
1703
- if (n > 12 || q.test(e)) return t;
1777
+ }, tn = /\.(tsx?|jsx?)$/, K = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, nn = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, rn = (e, t = [], n = 0) => {
1778
+ if (n > 12 || K.test(e)) return t;
1704
1779
  let r;
1705
1780
  try {
1706
1781
  r = D(e, { withFileTypes: !0 });
@@ -1711,12 +1786,12 @@ var re = () => {
1711
1786
  let r = x(e, i.name);
1712
1787
  if (i.isDirectory()) {
1713
1788
  if (i.name.startsWith(".")) continue;
1714
- Qt(r, t, n + 1);
1715
- } else lt.test(i.name) && t.push(r);
1789
+ rn(r, t, n + 1);
1790
+ } else ut.test(i.name) && t.push(r);
1716
1791
  }
1717
1792
  return t;
1718
- }, J = (e, t = [], n = 0) => {
1719
- if (n > 12 || q.test(e)) return t;
1793
+ }, q = (e, t = [], n = 0) => {
1794
+ if (n > 12 || K.test(e)) return t;
1720
1795
  let r;
1721
1796
  try {
1722
1797
  r = D(e);
@@ -1730,11 +1805,11 @@ var re = () => {
1730
1805
  } catch {
1731
1806
  continue;
1732
1807
  }
1733
- a.isDirectory() ? J(r, t, n + 1) : Xt.test(i) && !/\.(test|spec)\./.test(i) && !Zt.test(i) && t.push(r);
1808
+ a.isDirectory() ? q(r, t, n + 1) : tn.test(i) && !/\.(test|spec)\./.test(i) && !nn.test(i) && t.push(r);
1734
1809
  }
1735
1810
  return t;
1736
- }, $t = (e, t = [], n = 0) => {
1737
- if (n > 12 || q.test(e)) return t;
1811
+ }, an = (e, t = [], n = 0) => {
1812
+ if (n > 12 || K.test(e)) return t;
1738
1813
  let r;
1739
1814
  try {
1740
1815
  r = D(e);
@@ -1748,17 +1823,17 @@ var re = () => {
1748
1823
  } catch {
1749
1824
  continue;
1750
1825
  }
1751
- a.isDirectory() ? $t(r, t, n + 1) : Xt.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1826
+ a.isDirectory() ? an(r, t, n + 1) : tn.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1752
1827
  }
1753
1828
  return t;
1754
- }, en = (e) => {
1755
- let t = Jt(e);
1829
+ }, on = (e) => {
1830
+ let t = $t(e);
1756
1831
  return {
1757
- dirs: Yt(e, t),
1832
+ dirs: en(e, t),
1758
1833
  fileCount: t.length
1759
1834
  };
1760
- }, tn = (e) => {
1761
- let t = Jt(e);
1835
+ }, sn = (e) => {
1836
+ let t = $t(e);
1762
1837
  if (t.length === 0) return [];
1763
1838
  let n = new A({
1764
1839
  useInMemoryFileSystem: !0,
@@ -1767,8 +1842,8 @@ var re = () => {
1767
1842
  for (let e of t) try {
1768
1843
  i.set(e, E(e, "utf8"));
1769
1844
  } catch {}
1770
- let a = Lt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1771
- for (let t of $t(e)) try {
1845
+ let a = Ht([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1846
+ for (let t of an(e)) try {
1772
1847
  o.set(t, E(t, "utf8"));
1773
1848
  } catch {}
1774
1849
  for (let s of t) {
@@ -1786,23 +1861,23 @@ var re = () => {
1786
1861
  } catch {
1787
1862
  continue;
1788
1863
  }
1789
- for (let t of qt) {
1790
- if (t.plugin !== void 0 && Bt(c, t.plugin) || !t.match(c)) continue;
1864
+ for (let t of Qt) {
1865
+ if (t.plugin !== void 0 && Gt(c, t.plugin) || !t.match(c)) continue;
1791
1866
  let n = r.get(t.id) ?? [];
1792
1867
  n.push(S(e, s)), r.set(t.id, n);
1793
1868
  }
1794
1869
  }
1795
- return qt.filter((e) => r.has(e.id)).map((e) => ({
1870
+ return Qt.filter((e) => r.has(e.id)).map((e) => ({
1796
1871
  rule: e,
1797
1872
  files: r.get(e.id)
1798
1873
  })).sort((e, t) => t.files.length - e.files.length || e.rule.id.localeCompare(t.rule.id));
1799
- }, nn = (e) => {
1800
- let t = en(e);
1874
+ }, cn = (e) => {
1875
+ let t = on(e);
1801
1876
  return {
1802
1877
  root: e,
1803
1878
  scannedFiles: t.fileCount,
1804
1879
  scannedDirs: t.dirs,
1805
- findings: tn(e).map(({ rule: e, files: t }) => ({
1880
+ findings: sn(e).map(({ rule: e, files: t }) => ({
1806
1881
  id: e.id,
1807
1882
  scope: e.scope,
1808
1883
  smell: e.smell,
@@ -1810,45 +1885,50 @@ var re = () => {
1810
1885
  files: t
1811
1886
  }))
1812
1887
  };
1813
- }, rn = 3, an = (e) => {
1888
+ }, ln = 3, un = .2, dn = 50, fn = (e, t) => e >= dn && t > 0 && e / t >= un, pn = (e) => {
1814
1889
  let t = /* @__PURE__ */ new Map();
1815
- for (let n of J(e)) try {
1890
+ for (let n of q(e)) try {
1816
1891
  t.set(S(e, n), E(n, "utf8"));
1817
1892
  } catch {}
1818
1893
  let n = /* @__PURE__ */ new Map();
1819
- for (let t of Qt(e)) try {
1894
+ for (let t of rn(e)) try {
1820
1895
  n.set(S(e, t), E(t, "utf8"));
1821
1896
  } catch {}
1822
- let r = ut({
1897
+ let r = dt({
1823
1898
  sources: t,
1824
1899
  envFiles: n
1825
1900
  });
1826
- r !== void 0 && (console.log(""), console.log(dt(r)));
1827
- }, on = (e) => {
1828
- let t = nn(e);
1901
+ r !== void 0 && (console.log(""), console.log(ft(r)));
1902
+ }, mn = (e) => {
1903
+ let t = cn(e);
1829
1904
  if (console.log(""), t.scannedFiles === 0) return console.log(`\u2022 hand-roll scan: no source directories found under ${e}.`), console.log(" Walked the app root recursively (node_modules / dist / .framework pruned). Run `voltro doctor <app>` against an app root."), 0;
1830
1905
  if (t.findings.length === 0) return console.log(`\u2713 hand-roll scan: ${t.scannedFiles} file${t.scannedFiles === 1 ? "" : "s"} across ${t.scannedDirs.join(", ")} \u2014 no hand-rolled primitives found.`), 0;
1831
1906
  console.log(`\u2022 Shipped primitives you may be hand-rolling (scanned ${t.scannedFiles} files across ${t.scannedDirs.join(", ")}):`);
1832
1907
  let n = 0;
1833
1908
  for (let e of ["server", "client"]) {
1834
1909
  let r = t.findings.filter((t) => t.scope === e);
1835
- if (r.length !== 0) {
1836
- console.log(` [${e}]`);
1837
- for (let e of r) {
1838
- let t = e.files.slice(0, rn).join(", "), r = e.files.length - rn;
1839
- r > 0 && (n += r);
1840
- let i = r > 0 ? `, +${r} more` : "";
1841
- console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${t}${i}`), console.log(` \u2192 ${e.use}`);
1910
+ if (r.length === 0) continue;
1911
+ console.log(` [${e}]`);
1912
+ let i = [...r].sort((e, t) => e.files.length - t.files.length);
1913
+ for (let e of i) {
1914
+ if (fn(e.files.length, t.scannedFiles)) {
1915
+ let n = Math.round(e.files.length / t.scannedFiles * 100);
1916
+ console.log(` ${e.smell} \u2014 ${e.files.length} file(s), ${n}% of those scanned: a codebase-wide pattern rather than a work list`), console.log(` \u2192 ${e.use}`), console.log(" (paths omitted on purpose at this scale — `voltro doctor --json` has every one.)");
1917
+ continue;
1842
1918
  }
1919
+ let r = e.files.slice(0, ln).join(", "), i = e.files.length - ln;
1920
+ i > 0 && (n += i);
1921
+ let a = i > 0 ? `, +${i} more` : "";
1922
+ console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${r}${a}`), console.log(` \u2192 ${e.use}`);
1843
1923
  }
1844
1924
  }
1845
1925
  return n > 0 && console.log(` (${n} more file path${n === 1 ? "" : "s"} not shown \u2014 \`voltro doctor --json\` prints every one.)`), console.log(" (See the \"Pick the SERVER primitive\" / \"Pick the CLIENT primitive\" rubrics in AGENTS.md / CLAUDE.md.)"), t.findings.length;
1846
- }, sn = "renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.", cn = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, ln = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, un = (e) => {
1847
- let t = cn.exec(e)?.[1];
1926
+ }, hn = "renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.", gn = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, _n = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, vn = (e) => {
1927
+ let t = gn.exec(e)?.[1];
1848
1928
  if (t !== void 0) return t === "ssr" ? "ssr" : null;
1849
- let n = ln.exec(e)?.[1];
1929
+ let n = _n.exec(e)?.[1];
1850
1930
  return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
1851
- }, dn = async (e) => {
1931
+ }, yn = async (e) => {
1852
1932
  let n = x(e, "src", "pages");
1853
1933
  if (!T(n)) return [];
1854
1934
  let { pages: a, dirs: o } = await t(n), s = [];
@@ -1861,7 +1941,7 @@ var re = () => {
1861
1941
  continue;
1862
1942
  }
1863
1943
  if (r.test(a)) continue;
1864
- let c = un(a);
1944
+ let c = vn(a);
1865
1945
  c !== null && s.push({
1866
1946
  file: S(e, x(n, t.file)),
1867
1947
  pattern: t.pattern,
@@ -1869,18 +1949,18 @@ var re = () => {
1869
1949
  });
1870
1950
  }
1871
1951
  return s;
1872
- }, fn = 10, pn = async (e) => {
1952
+ }, bn = 10, xn = async (e) => {
1873
1953
  if (console.log(""), !T(x(e, "src", "pages"))) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
1874
- let t = await dn(e);
1954
+ let t = await yn(e);
1875
1955
  if (t.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
1876
1956
  console.log(`• renderMode:'spa' candidates (${t.length} page${t.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
1877
- for (let e of t.slice(0, fn)) {
1957
+ for (let e of t.slice(0, bn)) {
1878
1958
  let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
1879
1959
  console.log(` ${e.file} (${e.pattern}) — ${t}`);
1880
1960
  }
1881
- let n = t.length - fn;
1882
- return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${sn}`), t.length;
1883
- }, mn = /* @__PURE__ */ new Set([
1961
+ let n = t.length - bn;
1962
+ return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${hn}`), t.length;
1963
+ }, Sn = /* @__PURE__ */ new Set([
1884
1964
  "node_modules",
1885
1965
  "dist",
1886
1966
  "build",
@@ -1888,7 +1968,7 @@ var re = () => {
1888
1968
  ".vite",
1889
1969
  ".framework",
1890
1970
  ".git"
1891
- ]), hn = (e, t, n) => {
1971
+ ]), Cn = (e, t, n) => {
1892
1972
  let r;
1893
1973
  try {
1894
1974
  r = D(e, { withFileTypes: !0 });
@@ -1896,11 +1976,11 @@ var re = () => {
1896
1976
  return;
1897
1977
  }
1898
1978
  for (let i of r) {
1899
- if (mn.has(i.name)) continue;
1979
+ if (Sn.has(i.name)) continue;
1900
1980
  let r = x(e, i.name);
1901
- i.isDirectory() ? hn(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(S(t, r));
1981
+ i.isDirectory() ? Cn(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(S(t, r));
1902
1982
  }
1903
- }, gn = (e, t) => {
1983
+ }, wn = (e, t) => {
1904
1984
  let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
1905
1985
  for (let i of t) {
1906
1986
  let t;
@@ -1925,16 +2005,16 @@ var re = () => {
1925
2005
  }
1926
2006
  }
1927
2007
  return n;
1928
- }, _n = (e, t) => {
2008
+ }, Tn = (e, t) => {
1929
2009
  let n = e.slice(t.length).split(w).slice(0, -1)[0];
1930
2010
  return n !== void 0 && n.startsWith("[");
1931
- }, Y = (t) => {
2011
+ }, J = (t) => {
1932
2012
  let n = [], r = x(t, "src", "pages"), i = x(t, "src");
1933
2013
  if (!T(i)) return n;
1934
2014
  let a = [];
1935
- hn(i, t, a), a.sort();
2015
+ Cn(i, t, a), a.sort();
1936
2016
  let o = `${S(t, r)}${w}`;
1937
- gn(t, a);
2017
+ wn(t, a);
1938
2018
  for (let r of a) {
1939
2019
  let i = r.slice(r.lastIndexOf(w) + 1);
1940
2020
  if (r.startsWith(o)) {
@@ -1942,7 +2022,7 @@ var re = () => {
1942
2022
  T(x(t, e(r))) || n.push({
1943
2023
  kind: "page-without-test",
1944
2024
  file: r
1945
- }), _n(r, o) && n.push({
2025
+ }), Tn(r, o) && n.push({
1946
2026
  kind: "top-level-dynamic-route",
1947
2027
  file: r
1948
2028
  });
@@ -1954,33 +2034,33 @@ var re = () => {
1954
2034
  });
1955
2035
  }
1956
2036
  return n;
1957
- }, X = 10, Z = (e, t, n) => {
2037
+ }, Y = 10, X = (e, t, n) => {
1958
2038
  if (t.length === 0) return;
1959
2039
  console.log(`• ${e} (${t.length}):`);
1960
- for (let e of t.slice(0, X)) console.log(` ${e}`);
1961
- let r = t.length - X;
2040
+ for (let e of t.slice(0, Y)) console.log(` ${e}`);
2041
+ let r = t.length - Y;
1962
2042
  r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
1963
- }, vn = (e) => {
2043
+ }, En = (e) => {
1964
2044
  if (console.log(""), !T(x(e, "src", "pages"))) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
1965
- let t = Y(e);
2045
+ let t = J(e);
1966
2046
  if (t.length === 0) return console.log("✓ page-convention scan: every route is a page.tsx in its own directory, with a test beside it."), 0;
1967
2047
  let n = (e) => t.filter((t) => t.kind === e).map((e) => e.file);
1968
- return Z("will NEVER route — page.tsx outside src/pages/", n("suffixed-outside-pages"), "the name says \"route\", the location says otherwise, and the location wins: the URL comes from the directory chain under src/pages/. Move it into the directory whose route it serves, or rename it."), Z("pages with no test beside them", n("page-without-test"), "a page and its test belong together — `users/[id].page.tsx` → `users/[id].page.test.tsx`. Advisory only; a missing test is a gap, not a defect."), Z("routes whose FIRST segment is dynamic — they claim every URL of that depth", n("top-level-dynamic-route"), "a route like `[id]/[code]` answers `/api/health`, `/internal/readiness` and anything else with that many segments on this origin. That is what the pattern means, so it is not a bug — but it is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Give the route a literal first segment (`scan/[id]/[code]`) if you did not intend to own the whole origin at that depth. Advisory."), t.length;
1969
- }, yn = "voltro doctor [app] [flags]\n\nPreflight an app root: verify it can boot under production `voltro serve`,\nreport shipped primitives the source looks to be hand-rolling, and flag pages\nthat are safe `renderMode:'spa'` candidates.\n\nArguments:\n [app] App directory to check (default: the current directory).\n\nFlags:\n --strict Treat a missing serve bundle as blocking regardless of NODE_ENV\n (the `voltro serve --preflight` behavior). Without it, a missing\n bundle only fails when NODE_ENV=production.\n --json Print the complete scan as JSON — hand-roll findings AND spa\n candidates, every file path, nothing elided. Skips the preflight output.\n --write-authz-allowlist\n Record today's executors that reference NO access check into\n voltro-authz-allowlist.txt, so every later run fails only on\n ADDITIONS. The list is DEBT, not approval — an entry stops\n mattering as soon as its executor gains a guard.\n -h, --help Print this help.", bn = /* @__PURE__ */ new Map(), Q = (e) => {
1970
- let t = bn.get(e);
2048
+ return X("will NEVER route — page.tsx outside src/pages/", n("suffixed-outside-pages"), "the name says \"route\", the location says otherwise, and the location wins: the URL comes from the directory chain under src/pages/. Move it into the directory whose route it serves, or rename it."), X("pages with no test beside them", n("page-without-test"), "a page and its test belong together — `users/[id].page.tsx` → `users/[id].page.test.tsx`. Advisory only; a missing test is a gap, not a defect."), X("routes whose FIRST segment is dynamic — they claim every URL of that depth", n("top-level-dynamic-route"), "a route like `[id]/[code]` answers `/api/health`, `/internal/readiness` and anything else with that many segments on this origin. That is what the pattern means, so it is not a bug — but it is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Give the route a literal first segment (`scan/[id]/[code]`) if you did not intend to own the whole origin at that depth. Advisory."), t.length;
2049
+ }, Dn = "voltro doctor [app] [flags]\n\nPreflight an app root: verify it can boot under production `voltro serve`,\nreport shipped primitives the source looks to be hand-rolling, and flag pages\nthat are safe `renderMode:'spa'` candidates.\n\nArguments:\n [app] App directory to check (default: the current directory).\n\nFlags:\n --strict Treat a missing serve bundle as blocking regardless of NODE_ENV\n (the `voltro serve --preflight` behavior). Without it, a missing\n bundle only fails when NODE_ENV=production.\n --json Print the complete scan as JSON — hand-roll findings AND spa\n candidates, every file path, nothing elided. Skips the preflight output.\n --write-authz-allowlist\n Record today's executors that reference NO access check into\n voltro-authz-allowlist.txt, so every later run fails only on\n ADDITIONS. The list is DEBT, not approval — an entry stops\n mattering as soon as its executor gains a guard.\n -h, --help Print this help.", On = /* @__PURE__ */ new Map(), Z = (e) => {
2050
+ let t = On.get(e);
1971
2051
  if (t !== void 0) return t;
1972
2052
  let n = (async () => {
1973
- let { walk: t, loadDiscovered: n } = await import("./dev-DeEJj5T4.js"), r = await t(e);
2053
+ let { walk: t, loadDiscovered: n } = await import("./dev-DXhhZ7-f.js"), r = await t(e);
1974
2054
  return {
1975
2055
  files: r,
1976
2056
  discovered: await n(r)
1977
2057
  };
1978
2058
  })();
1979
- return bn.set(e, n), n;
1980
- }, xn = async (e) => {
2059
+ return On.set(e, n), n;
2060
+ }, kn = async (e) => {
1981
2061
  let t;
1982
2062
  try {
1983
- let { files: n, discovered: r } = await Q(e), i = r.tables.map((e) => {
2063
+ let { files: n, discovered: r } = await Z(e), i = r.tables.map((e) => {
1984
2064
  let t = e;
1985
2065
  return {
1986
2066
  name: t.tableName,
@@ -1988,12 +2068,12 @@ var re = () => {
1988
2068
  };
1989
2069
  });
1990
2070
  if (i.length === 0) return;
1991
- t = Ge(We(n, i, e));
2071
+ t = Ke(Ge(n, i, e));
1992
2072
  } catch {
1993
2073
  return;
1994
2074
  }
1995
2075
  t !== null && (console.log(""), console.log(`${t.includes("does not exist") ? "✗" : "✓"} ${t}`));
1996
- }, Sn = (e) => e.map((e) => {
2076
+ }, An = (e) => e.map((e) => {
1997
2077
  let t = e, n = Object.entries(t.fields ?? {}).map(([e, t]) => ({
1998
2078
  name: e,
1999
2079
  type: typeof t?.type == "string" ? t.type : ""
@@ -2003,22 +2083,22 @@ var re = () => {
2003
2083
  columns: n,
2004
2084
  primaryKey: t.appliedPrimaryKey ?? []
2005
2085
  };
2006
- }), Cn = async (e) => {
2086
+ }), jn = async (e) => {
2007
2087
  try {
2008
- let { discovered: t } = await Q(e);
2009
- return Xe(Sn(t.tables));
2088
+ let { discovered: t } = await Z(e);
2089
+ return Ze(An(t.tables));
2010
2090
  } catch {
2011
2091
  return null;
2012
2092
  }
2013
- }, wn = async (e) => {
2014
- let t = await Cn(e);
2093
+ }, Mn = async (e) => {
2094
+ let t = await jn(e);
2015
2095
  if (t === null || t.tablesChecked === 0) return;
2016
- let n = Qe(t);
2096
+ let n = $e(t);
2017
2097
  n !== null && (console.log(""), console.log(`${t.issues.length > 0 ? "✗" : "✓"} ${n}`));
2018
- }, Tn = async (e) => {
2098
+ }, Nn = async (e) => {
2019
2099
  try {
2020
- let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await Q(e);
2021
- return nt(n.events.map((e) => {
2100
+ let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await Z(e);
2101
+ return rt(n.events.map((e) => {
2022
2102
  let n = e.descriptor, r = t(n.key);
2023
2103
  return {
2024
2104
  name: n.name,
@@ -2030,19 +2110,19 @@ var re = () => {
2030
2110
  } catch {
2031
2111
  return null;
2032
2112
  }
2033
- }, En = async (e) => {
2034
- let t = await Tn(e);
2113
+ }, Pn = async (e) => {
2114
+ let t = await Nn(e);
2035
2115
  if (t === null || t.eventsChecked === 0) return;
2036
- let n = rt(t);
2116
+ let n = it(t);
2037
2117
  n !== null && (console.log(""), console.log(`• ${n}`));
2038
- let r = it(t);
2118
+ let r = at(t);
2039
2119
  r !== null && (console.log(""), console.log(`⚠️ ${r}`));
2040
- }, Dn = (e) => {
2120
+ }, Fn = (e) => {
2041
2121
  let t = e, n = typeof t?.message == "string" && t.message !== "" ? t.message : String(e), r = typeof t?.code == "string" ? t.code : void 0;
2042
2122
  return r === void 0 ? n : `${r}: ${n}`;
2043
- }, On = async (e) => {
2123
+ }, In = async (e) => {
2044
2124
  try {
2045
- let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await Q(e)).discovered.serverOnlyLeaks, r = t(n);
2125
+ let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await Z(e)).discovered.serverOnlyLeaks, r = t(n);
2046
2126
  return {
2047
2127
  status: {
2048
2128
  checked: !0,
@@ -2053,17 +2133,17 @@ var re = () => {
2053
2133
  } catch (e) {
2054
2134
  return { status: {
2055
2135
  checked: !1,
2056
- reason: Dn(e)
2136
+ reason: Fn(e)
2057
2137
  } };
2058
2138
  }
2059
- }, kn = async (e) => {
2060
- let { status: t, message: n } = await On(e);
2139
+ }, Ln = async (e) => {
2140
+ let { status: t, message: n } = await In(e);
2061
2141
  return t.checked ? n === void 0 ? !1 : (console.log(""), console.log(`✗ [serverOnly] ${n}`), !0) : (console.log(""), console.log("• serverOnly: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), console.log(" This audit is the automated check that a hash or a token cannot reach the wire, so"), console.log(" an app that leans on `.serverOnly()` should treat a persistent skip as a failure."), console.log(" `voltro doctor --json` reports it as `serverOnly.checked: false` for a CI gate."), !1);
2062
- }, An = async (e) => {
2142
+ }, Rn = async (e) => {
2063
2143
  let t;
2064
2144
  try {
2065
- let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await Q(e);
2066
- t = st(r, i.workflows.map((e) => {
2145
+ let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await Z(e);
2146
+ t = ct(r, i.workflows.map((e) => {
2067
2147
  let t = e.definition, r = t.payloadSchema === void 0 ? {
2068
2148
  required: [],
2069
2149
  optional: []
@@ -2077,21 +2157,46 @@ var re = () => {
2077
2157
  } catch {
2078
2158
  return;
2079
2159
  }
2080
- let n = ct(t);
2160
+ let n = lt(t);
2081
2161
  if (n === null) return;
2082
2162
  console.log("");
2083
2163
  let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
2084
2164
  console.log(`${r} ${n}`);
2085
- }, $ = "voltro-authz-allowlist.txt", jn = (e, t = (e) => {
2165
+ }, Q = "voltro-authz-allowlist.txt", zn = (e, t = (e) => {
2086
2166
  try {
2087
2167
  return E(e, "utf8");
2088
2168
  } catch {
2089
2169
  return "";
2090
2170
  }
2091
- }) => /* @__PURE__ */ new Set([...bt, ...xt(J(e).map(t))]), Mn = async (e) => {
2092
- let { discovered: t } = await Q(e), n = [];
2171
+ }) => /* @__PURE__ */ new Set([...Ct, ...wt(q(e).map(t))]), Bn = async (e) => {
2172
+ let t = ["src/locales", "locales"], n, r = [];
2173
+ for (let i of t) {
2174
+ let t = x(e, i);
2175
+ if (T(t)) try {
2176
+ r = D(t).filter((e) => /\.tsx?$/.test(e) && !e.endsWith(".d.ts")).map((e) => e.replace(/\.tsx?$/, "")), n = i;
2177
+ break;
2178
+ } catch {}
2179
+ }
2180
+ if (n === void 0 || r.length === 0) return;
2181
+ let i = [];
2093
2182
  try {
2094
- let { loadApiConfig: t } = await import("./dev-DeEJj5T4.js");
2183
+ let { loadApiConfig: t } = await import("./dev-DXhhZ7-f.js");
2184
+ i = (await t(e))?.locales ?? [];
2185
+ } catch {
2186
+ return;
2187
+ }
2188
+ let a = bt({
2189
+ catalogCodes: r,
2190
+ declaredLocales: i
2191
+ });
2192
+ if (a !== void 0) {
2193
+ console.log("");
2194
+ for (let e of xt(a, n)) console.log(e);
2195
+ }
2196
+ }, Vn = async (e) => {
2197
+ let { discovered: t } = await Z(e), n = [];
2198
+ try {
2199
+ let { loadApiConfig: t } = await import("./dev-DXhhZ7-f.js");
2095
2200
  n = ((await t(e))?.plugins ?? []).map((e) => {
2096
2201
  let t = (e.extendSchema?.tables ?? []).map((e) => {
2097
2202
  let t = e;
@@ -2107,8 +2212,8 @@ var re = () => {
2107
2212
  plugin: e.name,
2108
2213
  tables: t,
2109
2214
  rpcTags: n,
2110
- canDisableTables: Nn.has(e.name),
2111
- canAlias: Pn.has(e.name)
2215
+ canDisableTables: Hn.has(e.name),
2216
+ canAlias: Un.has(e.name)
2112
2217
  };
2113
2218
  });
2114
2219
  } catch {
@@ -2120,89 +2225,115 @@ var re = () => {
2120
2225
  ...t.mutations,
2121
2226
  ...t.actions,
2122
2227
  ...t.streams ?? []
2123
- ].map((e) => String(e.descriptor.name)), i = jt(n, {
2228
+ ].map((e) => String(e.descriptor.name)), i = It(n, {
2124
2229
  tables: t.tables.map((e) => e.tableName),
2125
2230
  rpcTags: r
2126
2231
  });
2127
2232
  console.log();
2128
- for (let e of Mt(n, i)) console.log(e);
2129
- }, Nn = /* @__PURE__ */ new Set(["@voltro/plugin-rbac"]), Pn = /* @__PURE__ */ new Set(["@voltro/plugin-ai-flows"]), Fn = async (e, t) => {
2130
- let { discovered: n } = await Q(e), r = /* @__PURE__ */ new Map(), i = (e) => {
2131
- if (!r.has(e)) try {
2132
- r.set(e, E(e, "utf8"));
2233
+ for (let e of Lt(n, i)) console.log(e);
2234
+ }, Hn = /* @__PURE__ */ new Set(["@voltro/plugin-rbac"]), Un = /* @__PURE__ */ new Set(["@voltro/plugin-ai-flows"]), $ = 20, Wn = async (e) => {
2235
+ let t = await Gn(e), n = new Set(t.reviewed.map((e) => e.tag)), r = t.result.findings.filter((e) => e.verdict.kind === "allowlisted" && !n.has(e.tag)).length;
2236
+ return {
2237
+ executors: t.executorCount,
2238
+ guardVocabulary: [...t.vocabulary].sort(),
2239
+ counts: t.result.counts,
2240
+ allowlist: {
2241
+ exists: t.existing !== void 0,
2242
+ debt: r,
2243
+ reviewed: t.result.counts.allowlisted - r
2244
+ },
2245
+ unchecked: t.result.unchecked
2246
+ };
2247
+ }, Gn = async (e) => {
2248
+ let { discovered: t } = await Z(e), n = /* @__PURE__ */ new Map(), r = (e) => {
2249
+ if (!n.has(e)) try {
2250
+ n.set(e, E(e, "utf8"));
2133
2251
  } catch {
2134
- r.set(e, "");
2252
+ n.set(e, "");
2135
2253
  }
2136
- return r.get(e);
2137
- }, a = jn(e, i), o = /* @__PURE__ */ new Set();
2138
- for (let e of n.tables) for (let t of Object.values(e.fields ?? {})) try {
2254
+ return n.get(e);
2255
+ }, i = zn(e, r), a = /* @__PURE__ */ new Set();
2256
+ for (let e of t.tables) for (let t of Object.values(e.fields ?? {})) try {
2139
2257
  let e = t.references?.();
2140
- e && o.add(e.tableName);
2258
+ e && a.add(e.tableName);
2141
2259
  } catch {}
2142
- let s = [
2260
+ let o = [
2143
2261
  {
2144
- list: n.queries,
2262
+ list: t.queries,
2145
2263
  op: "read"
2146
2264
  },
2147
2265
  {
2148
- list: n.mutations,
2266
+ list: t.mutations,
2149
2267
  op: "update"
2150
2268
  },
2151
2269
  {
2152
- list: n.actions,
2270
+ list: t.actions,
2153
2271
  op: "update"
2154
2272
  },
2155
2273
  {
2156
- list: n.streams,
2274
+ list: t.streams,
2157
2275
  op: "read"
2158
2276
  }
2159
2277
  ].flatMap(({ list: e, op: t }) => e.map((e) => {
2160
- let n = e.descriptor, r = n.target, a = Array.isArray(r) ? r[0] : r, o = e.file.replace(/\.(tsx?)$/, ".server.$1");
2278
+ let n = e.descriptor, i = n.target, a = Array.isArray(i) ? i[0] : i, o = e.file.replace(/\.(tsx?)$/, ".server.$1");
2161
2279
  return {
2162
2280
  tag: String(n.name ?? e.file),
2163
2281
  path: o,
2164
- src: i(o),
2282
+ src: r(o),
2165
2283
  hasDeclaredGuards: Array.isArray(n.guards) && n.guards.length > 0,
2166
2284
  op: a?.op === "delete" || a?.op === "insert" ? a.op : t,
2167
2285
  ...a?.table === void 0 ? {} : { table: a.table }
2168
2286
  };
2169
- })), c = x(e, $), l = T(c) ? kt(E(c, "utf8")) : void 0, u = Dt({
2170
- executors: s,
2171
- guardVocabulary: a,
2172
- tenantScopedTables: n.tenantScopedTables,
2173
- referencedTables: o,
2174
- allowlist: l ?? /* @__PURE__ */ new Set()
2175
- }), d = u.counts;
2176
- console.log(`\nauthz scan · ${s.length} executor(s)`);
2177
- let f = [...a].filter((e) => !bt.has(e)).sort();
2178
- if (console.log(f.length === 0 ? " guard vocabulary: framework names only — no exported require*/assert* found in this app" : ` guard vocabulary: ${f.length} from your source (${f.slice(0, 6).join(", ")}${f.length > 6 ? ", …" : ""})`), console.log(` ✗ no access check ${String(d.unchecked).padStart(4)}`), console.log(` ⚠ inline ownership check, no named guard${String(d.inlineOwnership).padStart(4)} (informational)`), console.log(` ✓ guards: on the descriptor ${String(d.declaredGuard).padStart(4)}`), console.log(` ✓ calls a guard from the vocabulary ${String(d.vocabularyGuard).padStart(4)}`), console.log(` – accepted as recorded debt ${String(d.allowlisted).padStart(4)} (${$})`), t) return ne(c, Ot(u.unchecked.map((e) => e.tag)), "utf8"), console.log(`\nwrote ${u.unchecked.length} tag(s) to ${$}. Doctor now fails on ADDITIONS only.`), !1;
2179
- if (u.unchecked.length === 0) return !1;
2287
+ })), s = x(e, Q), c = T(s) ? Pt(E(s, "utf8")) : void 0;
2288
+ return {
2289
+ result: At({
2290
+ executors: o,
2291
+ guardVocabulary: i,
2292
+ tenantScopedTables: t.tenantScopedTables,
2293
+ referencedTables: a,
2294
+ allowlist: c?.tags ?? /* @__PURE__ */ new Set()
2295
+ }),
2296
+ vocabulary: i,
2297
+ existing: c,
2298
+ reviewed: (c?.entries ?? []).filter((e) => e.reviewed !== void 0),
2299
+ executorCount: o.length,
2300
+ allowPath: s
2301
+ };
2302
+ }, Kn = async (e, t) => {
2303
+ let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s } = await Gn(e), c = n.counts;
2304
+ console.log(`\nauthz scan · ${o} executor(s)`);
2305
+ let l = [...r].filter((e) => !Ct.has(e)).sort();
2306
+ console.log(l.length === 0 ? " guard vocabulary: framework names only — no exported require*/assert* found in this app" : ` guard vocabulary: ${l.length} from your source (${l.slice(0, 6).join(", ")}${l.length > 6 ? ", …" : ""})`), console.log(` ✗ no access check ${String(c.unchecked).padStart(4)}`), console.log(` ⚠ inline ownership check, no named guard${String(c.inlineOwnership).padStart(4)} (informational)`), console.log(` ✓ guards: on the descriptor ${String(c.declaredGuard).padStart(4)}`), console.log(` ✓ calls a guard from the vocabulary ${String(c.vocabularyGuard).padStart(4)}`);
2307
+ let u = new Set(a.map((e) => e.tag)), d = n.findings.filter((e) => e.verdict.kind === "allowlisted" && !u.has(e.tag)).length, f = c.allowlisted - d;
2308
+ if (console.log(` – accepted as recorded debt ${String(d).padStart(4)} (${Q})`), console.log(` ✓ reviewed, open on purpose ${String(f).padStart(4)} (\`reviewed=<why>\` in that file)`), t) return ne(s, Mt([...a, ...n.unchecked.map((e) => ({ tag: e.tag }))]), "utf8"), console.log(`\nwrote ${n.unchecked.length} debt tag(s) to ${Q}${a.length > 0 ? `, keeping ${a.length} reviewed line(s)` : ""}. Doctor now fails on ADDITIONS only.`), !1;
2309
+ if (n.unchecked.length === 0) return !1;
2180
2310
  console.log("");
2181
- for (let e of u.unchecked.slice(0, 20)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
2182
- return u.unchecked.length > 20 && console.log(` … and ${u.unchecked.length - 20} more`), l === void 0 ? (console.log(`\nNo ${$} yet, so this run is advisory. To start the ratchet:`), console.log("\n voltro doctor --write-authz-allowlist\n"), console.log("That records today's unchecked handlers as DEBT and fails every run after on"), console.log("anything NEW. Fix a handler and its line stops mattering — the scan reports it as"), console.log("guarded whether or not the line is still there."), !1) : (console.log(`\n${u.unchecked.length} executor(s) with no access check are not in ${$}.`), console.log("Add a guard, or add the tag to that file if it is genuinely open."), console.log("\nBefore hand-rolling another check: `guards: [{ action, resourceType, resource }]`"), console.log("answers \"may this subject act on THIS row\" declaratively, and an app whose"), console.log("relationships live in its own tables registers its own tuple source rather than"), console.log("copying data into a framework table. See the authorization docs."), !0);
2183
- }, In = async (e) => {
2184
- if (e.includes("--help") || e.includes("-h")) return console.log(yn), 0;
2311
+ for (let e of n.unchecked.slice(0, $)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
2312
+ return n.unchecked.length > $ && console.log(` … and ${n.unchecked.length - $} more — \`voltro doctor --json\` prints every one (\`authz.unchecked\`).`), i === void 0 ? (console.log(`\nNo ${Q} yet, so this run is advisory. To start the ratchet:`), console.log("\n voltro doctor --write-authz-allowlist\n"), console.log("That records today's unchecked handlers as DEBT and fails every run after on"), console.log("anything NEW. Fix a handler and its line stops mattering — the scan reports it as"), console.log("guarded whether or not the line is still there."), !1) : (console.log(`\n${n.unchecked.length} executor(s) with no access check are not in ${Q}.`), console.log("Add a guard, or add the tag to that file if it is genuinely open."), console.log("\nBefore hand-rolling another check: `guards: [{ action, resourceType, resource }]`"), console.log("answers \"may this subject act on THIS row\" declaratively, and an app whose"), console.log("relationships live in its own tables registers its own tuple source rather than"), console.log("copying data into a framework table. See the authorization docs."), !0);
2313
+ }, qn = async (e) => {
2314
+ if (e.includes("--help") || e.includes("-h")) return console.log(Dn), 0;
2185
2315
  let t = e.includes("--strict"), n = C(e.find((e) => !e.startsWith("-")) ?? process.cwd());
2186
2316
  if (ie(e)) return re(), M({
2187
- ...nn(n),
2188
- spaCandidates: await dn(n),
2189
- pageConvention: Y(n),
2190
- fileTaxonomy: U(n).findings,
2191
- junctionFks: (await Cn(n))?.issues ?? [],
2192
- eventDelivery: await Tn(n),
2193
- duplicateInstances: _t(n),
2194
- serverOnly: (await On(n)).status
2317
+ ...cn(n),
2318
+ spaCandidates: await yn(n),
2319
+ pageConvention: J(n),
2320
+ fileTaxonomy: H(n).findings,
2321
+ junctionFks: (await jn(n))?.issues ?? [],
2322
+ eventDelivery: await Nn(n),
2323
+ duplicateInstances: vt(n),
2324
+ serverOnly: (await In(n)).status,
2325
+ authz: await Wn(n)
2195
2326
  }), 0;
2196
- let r = It(n, t);
2197
- on(n), an(n), vn(n), Be(n), await pn(n), await An(n), await xn(n), await wn(n), await En(n), await Mn(n);
2198
- let i = await kn(n), a = await Fn(n, e.includes("--write-authz-allowlist")), o = vt(_t(n));
2327
+ let r = Vt(n, t);
2328
+ mn(n), pn(n), En(n), Ve(n), await xn(n), await Rn(n), await kn(n), await Mn(n), await Pn(n), await Vn(n), await Bn(n);
2329
+ let i = await Ln(n), a = await Kn(n, e.includes("--write-authz-allowlist")), o = yt(vt(n));
2199
2330
  return o !== void 0 && console.log(`\n${o}`), i || a ? Math.max(r, 1) : r;
2200
- }, Ln = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, Rn = /* @__PURE__ */ new Set([
2331
+ }, Jn = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, Yn = /* @__PURE__ */ new Set([
2201
2332
  "insert",
2202
2333
  "update",
2203
2334
  "delete",
2204
2335
  "upsert"
2205
- ]), zn = (e, t) => {
2336
+ ]), Xn = (e, t) => {
2206
2337
  let n = [], r = new Set(t.exercised), i = /* @__PURE__ */ new Map();
2207
2338
  for (let e of t.edges) {
2208
2339
  let t = i.get(e.tag);
@@ -2214,8 +2345,8 @@ var re = () => {
2214
2345
  a.push(t.tag);
2215
2346
  continue;
2216
2347
  }
2217
- let e = `${t.kind}(${t.tag})`, s = t.file, c = i.get(t.tag) ?? [], l = new Set(Ln(t.source)), u = new Map((t.targets ?? []).map((e) => [e.table, e.op]));
2218
- for (let t of c) if (Rn.has(t.op)) {
2348
+ let e = `${t.kind}(${t.tag})`, s = t.file, c = i.get(t.tag) ?? [], l = new Set(Jn(t.source)), u = new Map((t.targets ?? []).map((e) => [e.table, e.op]));
2349
+ for (let t of c) if (Yn.has(t.op)) {
2219
2350
  let r = u.get(t.table);
2220
2351
  r === void 0 ? n.push({
2221
2352
  rule: "observed/undeclared-write",
@@ -2271,7 +2402,7 @@ var re = () => {
2271
2402
  total: e.length
2272
2403
  }
2273
2404
  };
2274
- }, Bn = (e, t = [], n = 0) => {
2405
+ }, Zn = (e, t = [], n = 0) => {
2275
2406
  if (n > 12) return t;
2276
2407
  let r;
2277
2408
  try {
@@ -2289,30 +2420,30 @@ var re = () => {
2289
2420
  ".git",
2290
2421
  "coverage"
2291
2422
  ].includes(i)) continue;
2292
- let r = `${e}/${i}`, a;
2423
+ let r = `${e}/${i}`, o;
2293
2424
  try {
2294
- a = O(r);
2425
+ o = O(r);
2295
2426
  } catch {
2296
2427
  continue;
2297
2428
  }
2298
- a.isDirectory() ? Bn(r, t, n + 1) : a.isFile() && o.test(i) && t.push(r);
2429
+ o.isDirectory() ? Zn(r, t, n + 1) : o.isFile() && a.test(i) && t.push(r);
2299
2430
  }
2300
2431
  return t;
2301
- }, Vn = /* @__PURE__ */ new Set(["actors", "tenants"]), Hn = async (e) => {
2432
+ }, Qn = /* @__PURE__ */ new Set(["actors", "tenants"]), $n = async (e) => {
2302
2433
  try {
2303
2434
  let { readFile: t } = await import("node:fs/promises"), { join: n } = await import("node:path"), r = await t(n(e, "app.graph.observed.generated.json"), "utf8"), i = JSON.parse(r);
2304
2435
  return i?.version !== 1 || !Array.isArray(i.edges) || !Array.isArray(i.exercised) ? null : i;
2305
2436
  } catch {
2306
2437
  return null;
2307
2438
  }
2308
- }, Un = (e) => {
2439
+ }, er = (e) => {
2309
2440
  let { exercised: t, total: n } = e.coverage, r = `\nobserved: ${t}/${n} procedures exercised (${n === 0 ? 0 : Math.round(t / n * 100)}%)`;
2310
2441
  if (e.diagnostics.length === 0) return `${r}\n no declared/observed mismatches${e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : ""}`;
2311
2442
  let i = e.diagnostics.map((e) => ` ${e.node}${e.file === void 0 ? "" : ` (${e.file})`}\n` + e.breaks.map((e) => ` ${e}`).join("\n") + `\n fix: ${e.fix}`), a = e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : "";
2312
2443
  return `${r}\n${i.join("\n")}${a}`;
2313
- }, Wn = (e, t) => ({
2444
+ }, tr = (e, t) => ({
2314
2445
  tables: (t.tables ?? []).map((e) => e.name),
2315
- frameworkOwnedTables: (t.tables ?? []).filter((e) => e.framework === !0 || e.name.startsWith("_voltro_") || Vn.has(e.name)).map((e) => e.name),
2446
+ frameworkOwnedTables: (t.tables ?? []).filter((e) => e.framework === !0 || e.name.startsWith("_voltro_") || Qn.has(e.name)).map((e) => e.name),
2316
2447
  scopes: e.scopes ?? [],
2317
2448
  procedures: (e.procedures ?? []).map((e) => {
2318
2449
  let t = (e.guards ?? []).flatMap((e) => e.kind === "scope" ? e.scope ?? [] : []);
@@ -2325,16 +2456,16 @@ var re = () => {
2325
2456
  ...e.guards === void 0 ? {} : { hasGuards: e.guards.length > 0 }
2326
2457
  };
2327
2458
  })
2328
- }), Gn = (e) => e.length === 0 ? "✓ voltro check: no issues" : e.map((e) => {
2459
+ }), nr = (e) => e.length === 0 ? "✓ voltro check: no issues" : e.map((e) => {
2329
2460
  let t = e.severity === "error" ? "✗ error" : "⚠ warn", n = e.file ? ` (${e.file})` : "";
2330
2461
  return `${t} ${e.rule} ${e.node}${n}\n ${e.breaks.join("; ")}\n fix: ${e.fix}`;
2331
- }).join("\n"), Kn = async (e) => {
2332
- let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-DeEJj5T4.js"), { buildRpcEntry: i } = await import("./manifestBuild-Ck_90gZy.js"), { buildCapabilityManifest: a } = await import("./inspect-DHeaMZak.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
2462
+ }).join("\n"), rr = async (e) => {
2463
+ let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-DXhhZ7-f.js"), { buildRpcEntry: i } = await import("./manifestBuild-Dj8Jjoto.js"), { buildCapabilityManifest: a } = await import("./inspect-mmBuRXmy.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
2333
2464
  ...s.queries.map((e) => c(e.descriptor, "query", e.file)),
2334
2465
  ...s.mutations.map((e) => c(e.descriptor, "mutation", e.file)),
2335
2466
  ...s.actions.map((e) => c(e.descriptor, "action", e.file)),
2336
2467
  ...s.streams.map((e) => c(e.descriptor, "stream", e.file))
2337
- ], { assembleFrameworkTables: u } = await import("./frameworkTableAssembly-BTNirAAk.js"), d = await u({ root: e }).catch(() => []), f = new Set(s.tables.map((e) => e.tableName)), p = [...[...f].map((e) => ({ name: e })), ...d.map((e) => e.tableName).filter((e) => !f.has(e)).map((e) => ({
2468
+ ], { assembleFrameworkTables: u } = await import("./frameworkTableAssembly-Bn_c4Mnl.js"), d = await u({ root: e }).catch(() => []), f = new Set(s.tables.map((e) => e.tableName)), p = [...[...f].map((e) => ({ name: e })), ...d.map((e) => e.tableName).filter((e) => !f.has(e)).map((e) => ({
2338
2469
  name: e,
2339
2470
  framework: !0
2340
2471
  }))], m = await r(e).then((e) => e?.plugins ?? []).catch(() => []);
@@ -2345,21 +2476,21 @@ var re = () => {
2345
2476
  })), [], m),
2346
2477
  tables: { tables: p }
2347
2478
  };
2348
- }, qn = async (e) => {
2479
+ }, ir = async (e) => {
2349
2480
  let t = ue(e.flatMap((e) => e === "--json" ? ["--format", "json"] : [e]));
2350
2481
  if ("error" in t) return console.error(`voltro check: ${t.error}`), 1;
2351
- let { process: n, target: r, rest: i } = de(t.rest), o;
2482
+ let { process: n, target: r, rest: i } = de(t.rest), a;
2352
2483
  for (let e = 0; e < i.length; e++) if (i[e] === "--diff") {
2353
2484
  let [t, n] = (i[++e] ?? "").split(":");
2354
- if (t === "removeTable" && n) o = {
2485
+ if (t === "removeTable" && n) a = {
2355
2486
  kind: "removeTable",
2356
2487
  name: n
2357
2488
  };
2358
- else if (t === "removeProcedure" && n) o = {
2489
+ else if (t === "removeProcedure" && n) a = {
2359
2490
  kind: "removeProcedure",
2360
2491
  tag: n
2361
2492
  };
2362
- else if (t === "removeScope" && n) o = {
2493
+ else if (t === "removeScope" && n) a = {
2363
2494
  kind: "removeScope",
2364
2495
  name: n
2365
2496
  };
@@ -2369,7 +2500,7 @@ var re = () => {
2369
2500
  if (l.length === 0) {
2370
2501
  if (!c && r !== void 0) return console.error("voltro check: --url given but no api reachable there."), 1;
2371
2502
  try {
2372
- let e = await Kn(C(s));
2503
+ let e = await rr(C(s));
2373
2504
  u = e.manifest, d = e.tables;
2374
2505
  } catch (e) {
2375
2506
  return console.error(`voltro check: could not assemble the graph from source: ${e.message}`), console.error(" Start `voltro dev` to check against a running api instead."), 1;
@@ -2379,21 +2510,21 @@ var re = () => {
2379
2510
  if (t.kind === "err") return console.error(`voltro check: manifest fetch failed: ${t.error}`), 1;
2380
2511
  u = t.data, d = n.kind === "ok" ? n.data : { tables: [] };
2381
2512
  }
2382
- let f = Wn(u, d), p = he(f, o), m = [...U(s).findings.map((e) => ({
2513
+ let f = tr(u, d), p = he(f, a), m = [...H(s).findings.map((e) => ({
2383
2514
  rule: e.rule,
2384
2515
  node: e.file,
2385
2516
  file: e.file,
2386
2517
  breaks: [e.detail],
2387
2518
  fix: e.fix,
2388
2519
  severity: e.severity
2389
- })), ...Y(s).map((e) => ({
2520
+ })), ...J(s).map((e) => ({
2390
2521
  rule: `page/${e.kind}`,
2391
2522
  node: e.file,
2392
2523
  file: e.file,
2393
2524
  breaks: [e.kind === "suffixed-outside-pages" ? "a page.tsx outside src/pages/ never routes" : "no test beside it"],
2394
2525
  fix: e.kind === "suffixed-outside-pages" ? "move it under src/pages/ into the directory whose route it serves, or rename it — page.tsx is reserved" : "write the test with the file — the name is derivable",
2395
2526
  severity: e.kind === "suffixed-outside-pages" ? "error" : "warn"
2396
- }))], h = (await a(s, Bn(s))).map((e) => ({
2527
+ }))], h = (await o(s, Zn(s))).map((e) => ({
2397
2528
  rule: "client/not-browser-safe",
2398
2529
  node: e.file,
2399
2530
  file: e.file,
@@ -2404,7 +2535,7 @@ var re = () => {
2404
2535
  ...p.diagnostics,
2405
2536
  ...m,
2406
2537
  ...h
2407
- ], _ = p.ok && !m.some((e) => e.severity === "error") && h.length === 0, v = await Hn(C(s)), y = v === null ? null : zn(f.procedures, v);
2538
+ ], _ = p.ok && !m.some((e) => e.severity === "error") && h.length === 0, v = await $n(C(s)), y = v === null ? null : Xn(f.procedures, v);
2408
2539
  return t.format === "json" ? M(y === null ? {
2409
2540
  ...p,
2410
2541
  ok: _,
@@ -2414,7 +2545,7 @@ var re = () => {
2414
2545
  ok: _,
2415
2546
  diagnostics: g,
2416
2547
  observed: y
2417
- }) : (console.log(Gn(g)), y !== null && console.log(Un(y)), console.log(_ ? "\nvoltro check: OK" : "\nvoltro check: FAILED (errors above)")), +!_;
2548
+ }) : (console.log(nr(g)), y !== null && console.log(er(y)), console.log(_ ? "\nvoltro check: OK" : "\nvoltro check: FAILED (errors above)")), +!_;
2418
2549
  };
2419
2550
  //#endregion
2420
- export { re as _, Le as a, I as c, N as d, ue as f, le as g, ce as h, It as i, ve as l, se as m, qn as n, be as o, de as p, In as r, xe as s, Kn as t, he as u, M as v, ie as y };
2551
+ export { re as _, Re as a, I as c, N as d, ue as f, le as g, ce as h, Vt as i, ve as l, se as m, ir as n, be as o, de as p, qn as r, xe as s, rr as t, he as u, M as v, ie as y };