@voltro/cli 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +395 -0
  2. package/dist/apiBuild-CDE4NXNi.js +2 -0
  3. package/dist/{apiBuild-Bdaetr37.js → apiBuild-CkpsCqIu.js} +2 -2
  4. package/dist/bin.js +1 -1
  5. package/dist/build-51qBWfYY.js +981 -0
  6. package/dist/{checkCommand-D0QV_zM_.js → checkCommand-C1EbNmwl.js} +1 -1
  7. package/dist/{checkCommand-CAwFXrxA.js → checkCommand-C30J2F18.js} +4 -4
  8. package/dist/{codegenCommand-CidbQzbv.js → codegenCommand-kS3Twuc9.js} +12 -12
  9. package/dist/{codemodRunner-BlQPfjzA.js → codemodRunner-BKjCwGiN.js} +1127 -895
  10. package/dist/{commands-CWjfThXv.js → commands-DowWkYtd.js} +25 -25
  11. package/dist/{dashboardCommand-BekcY5Ls.js → dashboardCommand-gG1y4PFG.js} +3 -3
  12. package/dist/{dataCommand-2pccgbIy.js → dataCommand-hNOGKz4K.js} +7 -7
  13. package/dist/{dbCommand-DpK_vQET.js → dbCommand-CMAIz-Bf.js} +3 -3
  14. package/dist/dbCommand-DHi_RuDl.js +2 -0
  15. package/dist/{dev-Dw263KPu.js → dev-Ce2gEziv.js} +2411 -2391
  16. package/dist/dev-Dff_pdik.js +3 -0
  17. package/dist/{doctorCommand-aR_bFmIi.js → doctorCommand-5ULMpL10.js} +652 -289
  18. package/dist/doctorCommand-B8H2MYCE.js +2 -0
  19. package/dist/{dormancyCommand-eXTQMbHU.js → dormancyCommand-DYgI0Kf-.js} +3 -3
  20. package/dist/{embeddingsCommand-CTmiQvwa.js → embeddingsCommand-yJivQSrI.js} +2 -2
  21. package/dist/emptyResultHeadline-Csa5fZOF.js +18 -0
  22. package/dist/{envCommand-BDUgV7EM.js → envCommand-ORJUf8r_.js} +2 -2
  23. package/dist/{evolveCommand-YV8qW1LU.js → evolveCommand-DTawtTxf.js} +2 -2
  24. package/dist/frameworkTableAssembly-B96WCNJA.js +2 -0
  25. package/dist/{frameworkTableAssembly-DNOFXfEQ.js → frameworkTableAssembly-vfkzuzEo.js} +104 -85
  26. package/dist/index.js +1 -1
  27. package/dist/{infoCommand-BjVXpMlP.js → infoCommand-BFksHunl.js} +1 -1
  28. package/dist/inspect-CZvsmp8t.js +2 -0
  29. package/dist/{inspect-CNYvNXPU.js → inspect-PcC5J-Dl.js} +74 -59
  30. package/dist/{logsCmd-BU8uCdys.js → logsCmd-C5y9HyrG.js} +58 -53
  31. package/dist/manifestBuild-AUh31bFU.js +2 -0
  32. package/dist/{manifestBuild-CEkjfpwc.js → manifestBuild-BwiS4iYm.js} +1 -1
  33. package/dist/{migrate-SICulyz1.js → migrate-DtC3lu7H.js} +4 -4
  34. package/dist/{probeCommand-6HxEkNDG.js → probeCommand-BUYzc0CT.js} +1 -1
  35. package/dist/{runtimeTrace-DgYMc09E.js → runtimeTrace-BUk5zMXY.js} +1 -1
  36. package/dist/{sdkgen-PY-umd6O.js → sdkgen-CAhmmbjx.js} +1 -1
  37. package/dist/serveCommand-BUXUtSF5.js +2 -0
  38. package/dist/serveCommand-DhtC7WBq.js +2545 -0
  39. package/dist/serveEntry.js +1 -1
  40. package/dist/{start-BFQQkL1i.js → start-C75w6ftA.js} +1 -1
  41. package/dist/{start-B9NGB8gn.js → start-DNtIlBcn.js} +527 -502
  42. package/dist/startEntry.js +1 -1
  43. package/dist/{tracesCmd-BWYDqMy6.js → tracesCmd-C1qKa94g.js} +68 -63
  44. package/dist/{updateCommand-C9n_Z_oG.js → updateCommand-CtuD5r1t.js} +1 -1
  45. package/dist/updateCommand-eVKVHj3N.js +2 -0
  46. package/dist/webDev-Bzb8ZFus.js +2 -0
  47. package/dist/{webDev-C53hJdcL.js → webDev-CBN9KkyQ.js} +949 -834
  48. package/dist/{webhooksCommand-uuPu8qQX.js → webhooksCommand-BPJQOGTe.js} +2 -2
  49. package/package.json +61 -19
  50. package/templates/AGENTS.md +1 -1
  51. package/templates/agent-docs/_index.md +1 -1
  52. package/templates/agent-docs/_manifest.json +2 -2
  53. package/templates/agent-docs/ai.md +175 -1
  54. package/templates/agent-docs/authentication.md +68 -19
  55. package/templates/agent-docs/cli.md +48 -0
  56. package/templates/agent-docs/configuration.md +29 -4
  57. package/templates/agent-docs/data.md +30 -0
  58. package/templates/agent-docs/database/scaling.md +10 -1
  59. package/templates/agent-docs/database/transactions.md +48 -0
  60. package/templates/agent-docs/deployment.md +22 -0
  61. package/templates/agent-docs/observability.md +13 -2
  62. package/templates/agent-docs/plugins/audit.md +21 -5
  63. package/templates/agent-docs/plugins/auth-supabase.md +21 -0
  64. package/templates/agent-docs/plugins/billing.md +2 -0
  65. package/templates/agent-docs/plugins/sentry.md +91 -8
  66. package/templates/agent-docs/reference.md +1 -0
  67. package/templates/agent-docs/scheduling.md +23 -0
  68. package/templates/agent-docs/schema-driven-ui.md +110 -0
  69. package/templates/agent-docs/whats-new.md +103 -399
  70. package/templates/apps/api-ai/package.json +6 -6
  71. package/templates/apps/api-auth/package.json +8 -8
  72. package/templates/apps/api-backend/package.json +7 -7
  73. package/templates/apps/api-backend-deactivation/package.json +7 -7
  74. package/templates/apps/api-backend-mail/package.json +8 -8
  75. package/templates/apps/api-backend-mariadb/package.json +9 -9
  76. package/templates/apps/api-backend-sqlite/package.json +8 -8
  77. package/templates/apps/api-backend-storage/package.json +8 -8
  78. package/templates/apps/api-cms/package.json +9 -9
  79. package/templates/apps/api-collab/package.json +8 -8
  80. package/templates/apps/api-data-advanced/package.json +8 -8
  81. package/templates/apps/api-durable/package.json +8 -8
  82. package/templates/apps/api-feature-flags/package.json +9 -9
  83. package/templates/apps/api-governance/package.json +8 -8
  84. package/templates/apps/api-kv/package.json +8 -8
  85. package/templates/apps/api-moderation/package.json +8 -8
  86. package/templates/apps/api-observability/package.json +8 -8
  87. package/templates/apps/api-ratelimit/package.json +8 -8
  88. package/templates/apps/api-rbac/package.json +8 -8
  89. package/templates/apps/api-rest/package.json +7 -7
  90. package/templates/apps/api-row-history/package.json +8 -8
  91. package/templates/apps/api-saas/package.json +11 -11
  92. package/templates/apps/api-saas-starter/package.json +10 -10
  93. package/templates/apps/api-search/package.json +8 -8
  94. package/templates/apps/api-status/package.json +8 -8
  95. package/templates/apps/api-webhooks/package.json +9 -9
  96. package/templates/apps/changelog/package.json +7 -7
  97. package/templates/apps/edge-functions/package.json +2 -2
  98. package/templates/apps/frontend-admin/package.json +7 -7
  99. package/templates/apps/frontend-app/package.json +8 -8
  100. package/templates/apps/frontend-auth/package.json +7 -7
  101. package/templates/apps/frontend-blank/package.json +6 -6
  102. package/templates/apps/frontend-cms/package.json +8 -8
  103. package/templates/apps/frontend-collab/package.json +9 -9
  104. package/templates/apps/frontend-contact/package.json +7 -7
  105. package/templates/apps/frontend-dashboard/package.json +6 -6
  106. package/templates/apps/frontend-docs/package.json +8 -8
  107. package/templates/apps/frontend-i18n/package.json +6 -6
  108. package/templates/apps/frontend-landing/package.json +7 -7
  109. package/templates/apps/frontend-portal/package.json +7 -7
  110. package/templates/apps/frontend-saas/package.json +7 -7
  111. package/templates/apps/frontend-spa/package.json +6 -6
  112. package/templates/apps/frontend-ssr/package.json +6 -6
  113. package/templates/apps/frontend-ssr-api/package.json +7 -7
  114. package/templates/apps/frontend-static-blog/package.json +8 -8
  115. package/templates/apps/frontend-status/package.json +7 -7
  116. package/templates/apps/mobile-app/package.json +4 -4
  117. package/dist/apiBuild-Vw1figjO.js +0 -2
  118. package/dist/build-CI36wL4R.js +0 -868
  119. package/dist/dbCommand-DrycGWWt.js +0 -2
  120. package/dist/dev-B9Gz0k85.js +0 -3
  121. package/dist/doctorCommand-BMWs6aVm.js +0 -2
  122. package/dist/frameworkTableAssembly-CGNC0qr7.js +0 -2
  123. package/dist/inspect-S2rWy1Ys.js +0 -2
  124. package/dist/manifestBuild-DIa_s6u0.js +0 -2
  125. package/dist/serveCommand-BITS8Hpj.js +0 -2
  126. package/dist/serveCommand-DIJ3ma76.js +0 -2485
  127. package/dist/updateCommand-DsXEAHbd.js +0 -2
  128. package/dist/webDev-C2dRz9s5.js +0 -2
@@ -1,7 +1,7 @@
1
- import { Ht as e, Ut as t, Wt as n } from "./dev-Dw263KPu.js";
2
- import { F as r, Ft as i, Mt as a, Nt as o, Pt as s, S as c, t as l, u } from "./webDev-C53hJdcL.js";
3
- import { r as d, u as f } from "./apiBuild-Bdaetr37.js";
4
- import { x as p } from "./frameworkTableAssembly-DNOFXfEQ.js";
1
+ import { Xt as e, Yt as t, Zt as n } from "./dev-Ce2gEziv.js";
2
+ import { Bt as r, Ht as i, T as a, Ut as o, V as s, Vt as c, t as l, u } from "./webDev-CBN9KkyQ.js";
3
+ import { r as d, u as f } from "./apiBuild-CkpsCqIu.js";
4
+ import { S as p } from "./frameworkTableAssembly-vfkzuzEo.js";
5
5
  import { n as m } from "./pageConvention-CMpfDN6r.js";
6
6
  import { n as h, r as g, t as ee } from "./cliOutput-D1tSBoRM.js";
7
7
  import { n as _, t as te } from "./tsconfigPaths-BWXBWgcl.js";
@@ -11,8 +11,227 @@ import { existsSync as S, readFileSync as C, readdirSync as w, statSync as T, wr
11
11
  import { pathToFileURL as se } from "node:url";
12
12
  import { createLogger as ce } from "@voltro/logger";
13
13
  import { Node as E, Project as D, SyntaxKind as O } from "ts-morph";
14
- //#region src/predicateColumnAudit.ts
15
- var le = /* @__PURE__ */ new Set([
14
+ //#region src/frameworkTableNullability.generated.ts
15
+ var le = {
16
+ _voltro_ai_flow_runs: [
17
+ "cancelledAt",
18
+ "chainPending",
19
+ "chainRefusal",
20
+ "completedAt",
21
+ "durationMs",
22
+ "error",
23
+ "errorMessage",
24
+ "flowName",
25
+ "humanResponse",
26
+ "input",
27
+ "metadata",
28
+ "output",
29
+ "ownerId",
30
+ "sessionId",
31
+ "sessionName",
32
+ "source",
33
+ "startedAt",
34
+ "steps"
35
+ ],
36
+ _voltro_ai_flows: [
37
+ "avatarUrl",
38
+ "cadence",
39
+ "chainTo",
40
+ "color",
41
+ "description",
42
+ "humanTimeoutMs",
43
+ "icon",
44
+ "inputSchema",
45
+ "metadata",
46
+ "orchestratorInstructions",
47
+ "orchestratorModel",
48
+ "ownerId",
49
+ "steps"
50
+ ],
51
+ _voltro_api_keys: [
52
+ "createdBy",
53
+ "expiresAt",
54
+ "lastUsedAt",
55
+ "metadata",
56
+ "onBehalfOf",
57
+ "revokedAt",
58
+ "scopes",
59
+ "tenantId"
60
+ ],
61
+ _voltro_change_claims: [],
62
+ _voltro_comment_reactions: [],
63
+ _voltro_comment_reads: [],
64
+ _voltro_comment_threads: ["tenantId"],
65
+ _voltro_comments: ["editedAt", "tenantId"],
66
+ _voltro_events: ["subjectId", "tenantId"],
67
+ _voltro_idempotency: ["response"],
68
+ _voltro_kv: ["expiresAt", "value"],
69
+ _voltro_migration_plans: [
70
+ "liveFingerprint",
71
+ "notes",
72
+ "onlineStrategy",
73
+ "rollbackPlan",
74
+ "squashedAt"
75
+ ],
76
+ _voltro_migrations: [
77
+ "appliedAt",
78
+ "durationMs",
79
+ "errorMessage",
80
+ "errorStack",
81
+ "workflowRunId"
82
+ ],
83
+ _voltro_notification_deliveries: [
84
+ "clickToken",
85
+ "clickedAt",
86
+ "endpoint",
87
+ "error"
88
+ ],
89
+ _voltro_notification_held: [],
90
+ _voltro_notification_inbox: [
91
+ "archivedAt",
92
+ "readAt",
93
+ "tenantId"
94
+ ],
95
+ _voltro_notification_preferences: [],
96
+ _voltro_notification_push_subscriptions: ["tenantId", "ua"],
97
+ _voltro_notification_quiet_hours: [],
98
+ _voltro_notification_topic_subscriptions: ["tenantId"],
99
+ _voltro_rebac_tuples: ["tenantId"],
100
+ _voltro_replica_observations: [
101
+ "payload",
102
+ "reachableAt",
103
+ "version"
104
+ ],
105
+ _voltro_schedule_claims: [],
106
+ _voltro_schedule_runs: [
107
+ "completedAt",
108
+ "coordinationOutcome",
109
+ "durationMs",
110
+ "errorMessage",
111
+ "errorTag",
112
+ "firedAt",
113
+ "heartbeatAt",
114
+ "replicaId",
115
+ "result"
116
+ ],
117
+ _voltro_scim_groups: ["externalId", "members"],
118
+ _voltro_scim_users: [
119
+ "email",
120
+ "externalId",
121
+ "familyName",
122
+ "givenName"
123
+ ],
124
+ _voltro_seeds: [
125
+ "cronSchedule",
126
+ "lastError",
127
+ "lastRunAt",
128
+ "lastRunDurationMs",
129
+ "lastRunRowsTouched",
130
+ "lastRunStatus",
131
+ "nextRunAt",
132
+ "watchedTables",
133
+ "workflowRunId"
134
+ ],
135
+ _voltro_traces: [
136
+ "attributes",
137
+ "events",
138
+ "parentSpanId",
139
+ "statusMessage"
140
+ ],
141
+ _voltro_undo_log: [
142
+ "label",
143
+ "subjectId",
144
+ "tenantId",
145
+ "traceId"
146
+ ],
147
+ _voltro_wakeups: [],
148
+ _voltro_webhook_deliveries: [
149
+ "errorMessage",
150
+ "eventId",
151
+ "latencyMs",
152
+ "nextAttemptAt",
153
+ "responseBody",
154
+ "responseStatus"
155
+ ],
156
+ _voltro_webhook_event_stats: [],
157
+ _voltro_webhook_rate_windows: [],
158
+ _voltro_webhook_targets: [
159
+ "autoDisableAfter",
160
+ "autoDisableReason",
161
+ "autoDisabledAt",
162
+ "description",
163
+ "filter",
164
+ "headers",
165
+ "rateLimitPerMinute",
166
+ "scope"
167
+ ],
168
+ _voltro_workflow_admissions: [
169
+ "concurrencyKey",
170
+ "executionId",
171
+ "expiresAt",
172
+ "mode",
173
+ "rateKey",
174
+ "reason",
175
+ "releasedAt",
176
+ "runId",
177
+ "singletonKey",
178
+ "tenantId",
179
+ "waitedMs"
180
+ ],
181
+ _voltro_workflow_event_deliveries: [
182
+ "completedAt",
183
+ "errorMessage",
184
+ "executionId"
185
+ ],
186
+ _voltro_workflow_events: ["subject", "traceId"],
187
+ _voltro_workflow_pauses: ["pausedBy", "reason"],
188
+ _voltro_workflow_pending: [
189
+ "callerContext",
190
+ "deadlineAt",
191
+ "lastError",
192
+ "tenantId"
193
+ ],
194
+ _voltro_workflow_run_events: [
195
+ "attempt",
196
+ "payload",
197
+ "stepName"
198
+ ],
199
+ _voltro_workflow_run_steps: [
200
+ "completedAt",
201
+ "durationMs",
202
+ "errorCause",
203
+ "errorMessage",
204
+ "errorTag",
205
+ "input",
206
+ "output",
207
+ "retryPolicy"
208
+ ],
209
+ _voltro_workflow_runs: [
210
+ "completedAt",
211
+ "discardedAt",
212
+ "discardedBy",
213
+ "durationMs",
214
+ "errorMessage",
215
+ "errorTag",
216
+ "output",
217
+ "parentClosePolicy",
218
+ "parentExecutionId",
219
+ "runnerEnteredAt",
220
+ "source",
221
+ "subject",
222
+ "traceId",
223
+ "workflowPatches",
224
+ "workflowVersion"
225
+ ],
226
+ _voltro_workflow_start_contexts: [
227
+ "parentClosePolicy",
228
+ "parentExecutionId",
229
+ "source",
230
+ "subject",
231
+ "traceId"
232
+ ],
233
+ _voltro_workflow_watermarks: []
234
+ }, ue = /* @__PURE__ */ new Set([
16
235
  "eq",
17
236
  "ne",
18
237
  "gt",
@@ -30,10 +249,10 @@ var le = /* @__PURE__ */ new Set([
30
249
  "endsWith",
31
250
  "between",
32
251
  "jsonField"
33
- ]), ue = (e) => {
252
+ ]), de = (e) => {
34
253
  if (E.isPropertyAccessExpression(e)) return e.getName();
35
254
  if (E.isIdentifier(e)) return e.getText();
36
- }, de = (e) => {
255
+ }, fe = (e) => {
37
256
  let t = e;
38
257
  for (; E.isCallExpression(t);) {
39
258
  let e = t.getExpression();
@@ -41,7 +260,7 @@ var le = /* @__PURE__ */ new Set([
41
260
  t = e.getExpression();
42
261
  }
43
262
  return t;
44
- }, fe = (e, t, n) => {
263
+ }, pe = (e, t, n) => {
45
264
  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 D({
46
265
  useInMemoryFileSystem: !0,
47
266
  skipAddingFilesFromTsConfig: !0
@@ -59,7 +278,7 @@ var le = /* @__PURE__ */ new Set([
59
278
  for (let t of e.getDescendantsOfKind(O.CallExpression)) {
60
279
  let e = t.getExpression();
61
280
  if (!E.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
62
- let n = ue(de(e.getExpression()));
281
+ let n = de(fe(e.getExpression()));
63
282
  if (n === void 0 || !r.has(n)) {
64
283
  let t = e.getExpression();
65
284
  if (E.isCallExpression(t)) {
@@ -82,7 +301,7 @@ var le = /* @__PURE__ */ new Set([
82
301
  else for (let e of [u, ...u.getDescendantsOfKind(O.CallExpression)]) {
83
302
  if (!E.isCallExpression(e)) continue;
84
303
  let t = e.getExpression(), n = E.isIdentifier(t) ? t.getText() : E.isPropertyAccessExpression(t) ? t.getName() : void 0;
85
- if (n === void 0 || !le.has(n)) continue;
304
+ if (n === void 0 || !ue.has(n)) continue;
86
305
  let r = e.getArguments()[0];
87
306
  r !== void 0 && E.isStringLiteral(r) && l.push({
88
307
  column: r.getLiteralValue(),
@@ -103,7 +322,7 @@ var le = /* @__PURE__ */ new Set([
103
322
  issues: a,
104
323
  checked: o
105
324
  };
106
- }, k = 12, pe = (e) => {
325
+ }, k = 12, me = (e) => {
107
326
  if (e.checked === 0) return null;
108
327
  if (e.issues.length === 0) return `predicate columns: ${e.checked} checked — every filtered column exists on its table`;
109
328
  let t = [`predicate columns: ${e.issues.length} filter${e.issues.length, "s"} on a column that does not exist (${e.checked} checked)`];
@@ -112,7 +331,7 @@ var le = /* @__PURE__ */ new Set([
112
331
  t.push(` ${n.file}:${n.line} '${n.table}' has no column '${n.column}'`), t.push(` columns: ${e}${r}`);
113
332
  }
114
333
  return t.push(" This type-checks today and fails at runtime as a bare SQL error."), t.join("\n");
115
- }, me = /* @__PURE__ */ new Set([
334
+ }, he = /* @__PURE__ */ new Set([
116
335
  "id",
117
336
  "tenantId",
118
337
  "tenant_id",
@@ -132,7 +351,7 @@ var le = /* @__PURE__ */ new Set([
132
351
  "idempotency_id",
133
352
  "idempotencyKey",
134
353
  "idempotency_key"
135
- ]), he = /* @__PURE__ */ new Set([
354
+ ]), ge = /* @__PURE__ */ new Set([
136
355
  "id",
137
356
  "createdAt",
138
357
  "created_at",
@@ -142,10 +361,10 @@ var le = /* @__PURE__ */ new Set([
142
361
  "deleted_at",
143
362
  "tenantId",
144
363
  "tenant_id"
145
- ]), ge = (e) => me.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), _e = (e) => e.type === "reference", ve = (e) => {
364
+ ]), _e = (e) => he.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), ve = (e) => e.type === "reference", ye = (e) => {
146
365
  let t = [];
147
366
  for (let n of e) {
148
- let e = n.columns.filter((e) => ge(e.name)), r = e.filter((e) => !_e(e)).map((e) => e.name);
367
+ let e = n.columns.filter((e) => _e(e.name)), r = e.filter((e) => !ve(e)).map((e) => e.name);
149
368
  if (r.length === 0) continue;
150
369
  let i = new Set(n.primaryKey), a = r.filter((e) => i.has(e));
151
370
  if (n.primaryKey.length >= 2 && a.length > 0) {
@@ -156,7 +375,7 @@ var le = /* @__PURE__ */ new Set([
156
375
  });
157
376
  continue;
158
377
  }
159
- if (e.some(_e) && e.length >= 2) {
378
+ if (e.some(ve) && e.length >= 2) {
160
379
  t.push({
161
380
  table: n.name,
162
381
  columns: r,
@@ -164,7 +383,7 @@ var le = /* @__PURE__ */ new Set([
164
383
  });
165
384
  continue;
166
385
  }
167
- e.length >= 2 && n.columns.filter((e) => !ge(e.name) && !he.has(e.name)).length === 0 && t.push({
386
+ e.length >= 2 && n.columns.filter((e) => !_e(e.name) && !ge.has(e.name)).length === 0 && t.push({
168
387
  table: n.name,
169
388
  columns: r,
170
389
  signal: "link-only"
@@ -174,20 +393,20 @@ var le = /* @__PURE__ */ new Set([
174
393
  issues: t,
175
394
  tablesChecked: e.length
176
395
  };
177
- }, ye = {
396
+ }, be = {
178
397
  "composite-pk": "part of a composite primary key",
179
398
  "reference-sibling": "sits beside a wired reference() on this table",
180
399
  "link-only": "this table is nothing but link columns"
181
- }, be = (e) => {
400
+ }, xe = (e) => {
182
401
  if (e.tablesChecked === 0) return null;
183
402
  if (e.issues.length === 0) return `junction FKs: ${e.tablesChecked} table${e.tablesChecked === 1 ? "" : "s"} checked — every link column is a wired reference()`;
184
403
  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()`];
185
404
  for (let t of e.issues) {
186
405
  let e = t.columns.map((e) => `'${e}'`).join(", ");
187
- n.push(` '${t.table}': ${e} (${ye[t.signal]})`);
406
+ n.push(` '${t.table}': ${e} (${be[t.signal]})`);
188
407
  }
189
408
  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");
190
- }, xe = /* @__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(".")), Se = (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()), Ce = (e) => Se(e).some((e) => xe.has(e)), we = (e) => {
409
+ }, Se = /* @__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(".")), Ce = (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()), we = (e) => Ce(e).some((e) => Se.has(e)), Te = (e) => {
191
410
  let t = [], n = [];
192
411
  for (let r of e) t.push({
193
412
  event: r.name,
@@ -196,7 +415,7 @@ var le = /* @__PURE__ */ new Set([
196
415
  event: r.name,
197
416
  kind: "wide-key",
198
417
  keyFieldCount: r.keyFieldCount
199
- }), r.hasWebhook && Ce(r.name) && n.push({
418
+ }), r.hasWebhook && we(r.name) && n.push({
200
419
  event: r.name,
201
420
  kind: "webhook-high-rate"
202
421
  });
@@ -205,26 +424,26 @@ var le = /* @__PURE__ */ new Set([
205
424
  modes: t,
206
425
  warnings: n
207
426
  };
208
- }, Te = (e) => {
427
+ }, Ee = (e) => {
209
428
  if (e.eventsChecked === 0) return null;
210
429
  let t = e.eventsChecked, n = [`event delivery: ${t} declared event${t === 1 ? "" : "s"} — the mode decides what a MISSING message means`];
211
430
  for (let t of e.modes) n.push(` '${t.event}': ${t.delivery}`);
212
431
  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");
213
- }, Ee = (e) => {
432
+ }, De = (e) => {
214
433
  if (e.warnings.length === 0) return null;
215
434
  let t = e.warnings.length, n = [`events that may not scale as declared: ${t} finding${t === 1 ? "" : "s"}`];
216
435
  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."));
217
436
  return n.push(" WARNING, not a fault — a producer may intend the fan-out. `voltro doctor --json` prints every finding."), n.join("\n");
218
- }, De = /* @__PURE__ */ new Set([
437
+ }, Oe = /* @__PURE__ */ new Set([
219
438
  "start",
220
439
  "run",
221
440
  "child"
222
- ]), Oe = (e) => E.isCallExpression(e) && (() => {
441
+ ]), ke = (e) => E.isCallExpression(e) && (() => {
223
442
  let t = e.getExpression();
224
- if (!E.isPropertyAccessExpression(t) || !De.has(t.getName())) return !1;
443
+ if (!E.isPropertyAccessExpression(t) || !Oe.has(t.getName())) return !1;
225
444
  let n = t.getExpression();
226
445
  return E.isPropertyAccessExpression(n) ? n.getName() === "workflows" : E.isIdentifier(n) && n.getText() === "workflows";
227
- })(), ke = (e, t, n) => {
446
+ })(), Ae = (e, t, n) => {
228
447
  let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new D({
229
448
  useInMemoryFileSystem: !0,
230
449
  skipAddingFilesFromTsConfig: !0
@@ -240,7 +459,7 @@ var le = /* @__PURE__ */ new Set([
240
459
  }
241
460
  let u = y(n, t);
242
461
  for (let t of e.getDescendantsOfKind(O.CallExpression)) {
243
- if (!Oe(t)) continue;
462
+ if (!ke(t)) continue;
244
463
  c++;
245
464
  let e = t.getStartLineNumber(), [n, l] = t.getArguments();
246
465
  if (n === void 0 || !E.isStringLiteral(n)) {
@@ -324,7 +543,7 @@ var le = /* @__PURE__ */ new Set([
324
543
  checked: s,
325
544
  callSites: c
326
545
  };
327
- }, Ae = (e) => {
546
+ }, je = (e) => {
328
547
  if (e.callSites === 0) return null;
329
548
  let t = [];
330
549
  t.push(`workflow starts: ${e.checked}/${e.callSites} call sites checked`);
@@ -337,7 +556,7 @@ var le = /* @__PURE__ */ new Set([
337
556
  for (let n of e.unchecked) t.push(` ${n.file}:${n.line} — ${n.reason}`);
338
557
  }
339
558
  return e.issues.length === 0 && e.unchecked.length === 0 && t.push(" every call site matches its workflow"), t.join("\n");
340
- }, je = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, A = "VOLTRO_FIELD_ENCRYPTION_KEY", Me = (e) => {
559
+ }, Me = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, A = "VOLTRO_FIELD_ENCRYPTION_KEY", Ne = (e) => {
341
560
  let t;
342
561
  for (let [n, r] of e.sources) if (t === void 0 && /\bfieldEncryption\b/.test(r) && (t = {
343
562
  reason: "fieldEncryption",
@@ -350,7 +569,7 @@ var le = /* @__PURE__ */ new Set([
350
569
  for (let [, t] of e.envFiles) if (t.includes(A)) return;
351
570
  return t;
352
571
  }
353
- }, Ne = (e) => [
572
+ }, Pe = (e) => [
354
573
  `✗ ${A} is named nowhere under this app root, and the app encrypts columns at rest`,
355
574
  e.reason === "fieldEncryption" ? ` (\`fieldEncryption\` in ${e.file})` : ` (an \`.encrypted()\` column in ${e.file})`,
356
575
  "",
@@ -367,15 +586,15 @@ var le = /* @__PURE__ */ new Set([
367
586
  " reaches your deploy target is not knowable from here — a secret manager",
368
587
  " or a CI variable is not a file. Naming it nowhere is conclusive; naming",
369
588
  " it somewhere is not a guarantee."
370
- ].join("\n"), Pe = {
589
+ ].join("\n"), Fe = {
371
590
  "@voltro/cli": "provides the `voltro` binary; consumed by scripts, never imported",
372
591
  "@voltro/devtools": "mounted by `voltro dev` itself",
373
592
  "@voltro/sql-postgres": "dialect driver, loaded by the framework from config",
374
593
  "@voltro/sql-mysql": "dialect driver, loaded by the framework from config",
375
594
  "@voltro/sql-sqlite": "dialect driver, loaded by the framework from config",
376
595
  "@voltro/sql-mssql": "dialect driver, loaded by the framework from config"
377
- }, Fe = /* @__PURE__ */ new Set(["@voltro/client", "@voltro/web"]), Ie = /(^|\/)(node_modules|dist|build|\.framework|\.next|\.turbo|\.git|coverage|\.publish)(\/|$)/, Le = /\.([cm]?[jt]sx?)$/, Re = /\.generated\.[cm]?[jt]sx?$/, ze = (e, t = [], n = 0) => {
378
- if (n > 12 || Ie.test(e)) return t;
596
+ }, Ie = /* @__PURE__ */ new Set(["@voltro/client", "@voltro/web"]), Le = /(^|\/)(node_modules|dist|build|\.framework|\.next|\.turbo|\.git|coverage|\.publish)(\/|$)/, Re = /\.([cm]?[jt]sx?)$/, ze = /\.generated\.[cm]?[jt]sx?$/, Be = (e, t = [], n = 0) => {
597
+ if (n > 12 || Le.test(e)) return t;
379
598
  let r;
380
599
  try {
381
600
  r = w(e);
@@ -389,10 +608,10 @@ var le = /* @__PURE__ */ new Set([
389
608
  } catch {
390
609
  continue;
391
610
  }
392
- a.isDirectory() ? ze(r, t, n + 1) : Le.test(i) && t.push(r);
611
+ a.isDirectory() ? Be(r, t, n + 1) : Re.test(i) && t.push(r);
393
612
  }
394
613
  return t;
395
- }, Be = /(?:\bfrom\s*|\bimport\s+|\bimport\s*\(\s*|\brequire\s*\(\s*)['"](@voltro\/[a-z0-9-]+)(?:\/[^'"]*)?['"]/g, Ve = (e) => e.split("\n").filter((e) => !/^\s*(\/\/|\*|\/\*)/.test(e)).join("\n"), He = (e) => {
614
+ }, Ve = /(?:\bfrom\s*|\bimport\s+|\bimport\s*\(\s*|\brequire\s*\(\s*)['"](@voltro\/[a-z0-9-]+)(?:\/[^'"]*)?['"]/g, He = (e) => e.split("\n").filter((e) => !/^\s*(\/\/|\*|\/\*)/.test(e)).join("\n"), Ue = (e) => {
396
615
  let t;
397
616
  try {
398
617
  t = JSON.parse(C(v(e, "package.json"), "utf8"));
@@ -402,7 +621,7 @@ var le = /* @__PURE__ */ new Set([
402
621
  let n = (e) => Object.keys(t[e] ?? {}).filter((e) => e.startsWith("@voltro/")).map((t) => ({
403
622
  pkg: t,
404
623
  declaredIn: e
405
- })), r = /* @__PURE__ */ new Set(), i = [...n("dependencies"), ...n("devDependencies")].filter((e) => !r.has(e.pkg) && (r.add(e.pkg), !0)), a = ze(e), o = a.some((e) => Re.test(e)), s = /* @__PURE__ */ new Set();
624
+ })), r = /* @__PURE__ */ new Set(), i = [...n("dependencies"), ...n("devDependencies")].filter((e) => !r.has(e.pkg) && (r.add(e.pkg), !0)), a = Be(e), o = a.some((e) => ze.test(e)), s = /* @__PURE__ */ new Set();
406
625
  for (let e of a) {
407
626
  let t;
408
627
  try {
@@ -410,17 +629,17 @@ var le = /* @__PURE__ */ new Set([
410
629
  } catch {
411
630
  continue;
412
631
  }
413
- for (let e of Ve(t).matchAll(Be)) s.add(e[1]);
632
+ for (let e of He(t).matchAll(Ve)) s.add(e[1]);
414
633
  }
415
- let c = i.filter((e) => !(e.pkg in Pe) && !s.has(e.pkg));
634
+ let c = i.filter((e) => !(e.pkg in Fe) && !s.has(e.pkg));
416
635
  return {
417
636
  declared: i.map((e) => e.pkg).sort(),
418
- unimported: c.filter((e) => o || !Fe.has(e.pkg)).sort((e, t) => e.pkg.localeCompare(t.pkg)),
419
- pendingCodegen: o ? [] : c.map((e) => e.pkg).filter((e) => Fe.has(e)).sort(),
420
- exempt: i.map((e) => e.pkg).filter((e) => e in Pe).sort(),
637
+ unimported: c.filter((e) => o || !Ie.has(e.pkg)).sort((e, t) => e.pkg.localeCompare(t.pkg)),
638
+ pendingCodegen: o ? [] : c.map((e) => e.pkg).filter((e) => Ie.has(e)).sort(),
639
+ exempt: i.map((e) => e.pkg).filter((e) => e in Fe).sort(),
421
640
  scannedFiles: a.length
422
641
  };
423
- }, Ue = (e) => {
642
+ }, We = (e) => {
424
643
  if (e === null) return ["unimported deps · no package.json here — could not check"];
425
644
  let t = `unimported deps · ${e.declared.length} @voltro/* package(s) declared, ${e.scannedFiles} file(s) scanned`;
426
645
  if (e.declared.length === 0) return [t, " no @voltro/* dependencies declared — nothing to check"];
@@ -437,14 +656,14 @@ var le = /* @__PURE__ */ new Set([
437
656
  let i = [t];
438
657
  for (let t of e.unimported) i.push(` ⚠ ${t.pkg} — declared in ${t.declaredIn}, imported nowhere`);
439
658
  return i.push(" a dependency nobody imports still gets installed, updated, and read as"), i.push(" evidence the package is in use — its breaking-change notes included."), i.push(" Remove it, or if it IS imported through an assembled specifier the scan"), i.push(" cannot see, keep it and ignore this line — the rule is advisory."), i.push(...r), n !== "" && i.push(` ·${n.slice(1)}`), i;
440
- }, We = (e) => {
659
+ }, Ge = (e) => {
441
660
  if (e.catalogCodes.length === 0) return;
442
661
  let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
443
662
  if (n.length !== 0) return {
444
663
  orphaned: n,
445
664
  noneDeclared: t.size === 0
446
665
  };
447
- }, Ge = (e, t) => {
666
+ }, Ke = (e, t) => {
448
667
  let n = e.orphaned.join(", ");
449
668
  return e.noneDeclared ? [
450
669
  `⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
@@ -456,7 +675,7 @@ var le = /* @__PURE__ */ new Set([
456
675
  " …or delete the files. A catalog that is never loaded looks from the inside",
457
676
  " exactly like one that works, which is why this is worth a line."
458
677
  ] : [`⚠ ${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)."];
459
- }, Ke = (e) => {
678
+ }, qe = (e) => {
460
679
  let t = e.getProperty("match");
461
680
  if (t === void 0) return null;
462
681
  let n = (t.asKind(O.PropertyAssignment)?.getInitializer())?.asKind(O.ObjectLiteralExpression);
@@ -493,15 +712,15 @@ var le = /* @__PURE__ */ new Set([
493
712
  else return;
494
713
  }
495
714
  return r;
496
- }, qe = (e, t) => {
715
+ }, Je = (e, t) => {
497
716
  let n = ["middleware.ts", "middleware.tsx"].find((t) => S(v(e, t)));
498
717
  if (n === void 0) return;
499
- let i = new D({
718
+ let r = new D({
500
719
  useInMemoryFileSystem: !0,
501
720
  skipAddingFilesFromTsConfig: !0
502
- }), a;
721
+ }), i;
503
722
  try {
504
- a = i.createSourceFile(n, C(v(e, n), "utf8"));
723
+ i = r.createSourceFile(n, C(v(e, n), "utf8"));
505
724
  } catch {
506
725
  return {
507
726
  problems: [],
@@ -510,22 +729,22 @@ var le = /* @__PURE__ */ new Set([
510
729
  file: n
511
730
  };
512
731
  }
513
- let o = [], s = [], c = 0;
514
- for (let e of a.getVariableDeclarations()) {
732
+ let a = [], o = [], c = 0;
733
+ for (let e of i.getVariableDeclarations()) {
515
734
  let t = e.getInitializerIfKind(O.CallExpression);
516
735
  if (t?.getExpression().getText() !== "defineMiddleware") continue;
517
736
  c += 1;
518
737
  let n = e.getName(), r = t.getArguments()[0]?.asKind(O.ObjectLiteralExpression);
519
738
  if (r === void 0) {
520
- s.push(n);
739
+ o.push(n);
521
740
  continue;
522
741
  }
523
- let i = Ke(r);
742
+ let i = qe(r);
524
743
  if (i === void 0) {
525
- s.push(n);
744
+ o.push(n);
526
745
  continue;
527
746
  }
528
- o.push({
747
+ a.push({
529
748
  exportName: n,
530
749
  middleware: {
531
750
  _tag: "VoltroMiddleware",
@@ -535,45 +754,45 @@ var le = /* @__PURE__ */ new Set([
535
754
  });
536
755
  }
537
756
  return {
538
- problems: r(o, t).map((e) => e.message),
539
- unreadable: s,
757
+ problems: s(a, t).map((e) => e.message),
758
+ unreadable: o,
540
759
  seen: c,
541
760
  file: n
542
761
  };
543
- }, Je = (e) => {
762
+ }, Ye = (e) => {
544
763
  let t = [];
545
764
  if (e.problems.length > 0) {
546
765
  t.push(`✗ ${e.file}: ${e.problems.length} matcher problem(s) — this REFUSES the boot`);
547
766
  for (let n of e.problems) t.push(` • ${n}`);
548
767
  }
549
768
  return e.unreadable.length > 0 && t.push(`… ${e.file}: ${e.unreadable.length} matcher(s) not statically readable (${e.unreadable.join(", ")}) — doctor did NOT check them. The boot still does.`), e.seen === 0 && t.push(`… ${e.file}: no \`defineMiddleware(...)\` found — doctor checked nothing here. A middleware assigned through a variable or re-exported from another module is invisible to this rule.`), t.length === 0 && e.seen > 0 && t.push(`✓ ${e.file}: ${e.seen} middleware(s), matchers resolve and do not overlap`), t;
550
- }, Ye = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), Xe = /* @__PURE__ */ new Set([
769
+ }, Xe = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), Ze = /* @__PURE__ */ new Set([
551
770
  "requireScope",
552
771
  "requireAnyScope",
553
772
  "requireAuth",
554
773
  "assertScope",
555
774
  "assertCan",
556
775
  "can"
557
- ]), Ze = (e) => {
776
+ ]), Qe = (e) => {
558
777
  let t = /* @__PURE__ */ new Set();
559
- for (let n of e) for (let e of Ye(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
778
+ for (let n of e) for (let e of Xe(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
560
779
  return t;
561
- }, Qe = (e, t) => {
780
+ }, $e = (e, t) => {
562
781
  for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
563
- }, $e = (e) => {
782
+ }, et = (e) => {
564
783
  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);
565
784
  return t && n;
566
- }, et = (e, t, n) => {
785
+ }, tt = (e, t, n) => {
567
786
  if (e.hasDeclaredGuards) return { kind: "declared-guard" };
568
- let r = Ye(e.src), i = Qe(r, t);
569
- return i === void 0 ? $e(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
787
+ let r = Xe(e.src), i = $e(r, t);
788
+ return i === void 0 ? et(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
570
789
  kind: "vocabulary-guard",
571
790
  name: i
572
791
  };
573
- }, tt = (e, t, n) => {
792
+ }, nt = (e, t, n) => {
574
793
  let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
575
794
  return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
576
- }, nt = (e, t, n) => {
795
+ }, rt = (e, t, n) => {
577
796
  switch (t.kind) {
578
797
  case "declared-guard": return "guards: on the descriptor";
579
798
  case "vocabulary-guard": return `calls ${t.name}()`;
@@ -581,15 +800,15 @@ var le = /* @__PURE__ */ new Set([
581
800
  case "allowlisted": return "accepted by a previous run";
582
801
  case "unchecked": return n ? e.op === "read" ? "no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt" : `${e.op}s${e.table ? ` ${e.table}` : ""} with no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt` : 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`;
583
802
  }
584
- }, rt = (e) => {
803
+ }, it = (e) => {
585
804
  let t = e.executors.map((t) => {
586
- let n = et(t, e.guardVocabulary, e.allowlist);
805
+ let n = tt(t, e.guardVocabulary, e.allowlist);
587
806
  return {
588
807
  tag: t.tag,
589
808
  path: t.path,
590
809
  verdict: n,
591
- severity: tt(t, e.tenantScopedTables, e.referencedTables),
592
- why: nt(t, n, e.rowFilterRegistered === !0)
810
+ severity: nt(t, e.tenantScopedTables, e.referencedTables),
811
+ why: rt(t, n, e.rowFilterRegistered === !0)
593
812
  };
594
813
  }), n = (e) => t.filter((t) => t.verdict.kind === e).length;
595
814
  return {
@@ -603,7 +822,7 @@ var le = /* @__PURE__ */ new Set([
603
822
  },
604
823
  unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
605
824
  };
606
- }, it = [
825
+ }, at = [
607
826
  "# voltro doctor — authz allowlist",
608
827
  "#",
609
828
  "# Two KINDS of line, and the difference is the point:",
@@ -628,18 +847,18 @@ var le = /* @__PURE__ */ new Set([
628
847
  "# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
629
848
  "# lines for executors that are newly unchecked.",
630
849
  ""
631
- ], at = (e) => {
850
+ ], ot = (e) => {
632
851
  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));
633
852
  return [
634
- ...it,
853
+ ...at,
635
854
  ...n,
636
855
  ...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
637
856
  ...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
638
857
  ""
639
858
  ].join("\n");
640
- }, ot = (e, t) => {
859
+ }, st = (e, t) => {
641
860
  let n = [...new Set(t)].sort();
642
- if (e === void 0) return at(n.map((e) => ({ tag: e })));
861
+ if (e === void 0) return ot(n.map((e) => ({ tag: e })));
643
862
  let r = j(e).tags, i = n.filter((e) => !r.has(e));
644
863
  return i.length === 0 ? e : [
645
864
  e.replace(/\n+$/, ""),
@@ -649,7 +868,7 @@ var le = /* @__PURE__ */ new Set([
649
868
  ...i,
650
869
  ""
651
870
  ].join("\n");
652
- }, st = (e, t) => {
871
+ }, ct = (e, t) => {
653
872
  let n = [...new Set(t)];
654
873
  if (e === void 0) return {
655
874
  added: n.length,
@@ -662,7 +881,7 @@ var le = /* @__PURE__ */ new Set([
662
881
  kept: r.size,
663
882
  removed: 0
664
883
  };
665
- }, ct = class extends Error {
884
+ }, lt = class extends Error {
666
885
  tag;
667
886
  constructor(e) {
668
887
  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";
@@ -680,7 +899,7 @@ var le = /* @__PURE__ */ new Set([
680
899
  continue;
681
900
  }
682
901
  let a = r[2].trim();
683
- if (a.length === 0) throw new ct(i);
902
+ if (a.length === 0) throw new lt(i);
684
903
  t.push({
685
904
  tag: i,
686
905
  reviewed: a
@@ -690,13 +909,13 @@ var le = /* @__PURE__ */ new Set([
690
909
  tags: new Set(t.map((e) => e.tag)),
691
910
  entries: t
692
911
  };
693
- }, lt = (e) => {
912
+ }, ut = (e) => {
694
913
  let t = e.indexOf(".");
695
914
  return t <= 0 ? void 0 : e.slice(0, t);
696
- }, ut = (e, t) => {
915
+ }, dt = (e, t) => {
697
916
  let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
698
917
  for (let e of t.rpcTags) {
699
- let t = lt(e);
918
+ let t = ut(e);
700
919
  if (t === void 0) continue;
701
920
  let n = i.get(t);
702
921
  n ? n.push(e) : i.set(t, [e]);
@@ -725,7 +944,7 @@ var le = /* @__PURE__ */ new Set([
725
944
  });
726
945
  continue;
727
946
  }
728
- let s = new Set(o.rpcTags.map(lt).filter((e) => e !== void 0));
947
+ let s = new Set(o.rpcTags.map(ut).filter((e) => e !== void 0));
729
948
  for (let e of s) {
730
949
  let t = i.get(e);
731
950
  t !== void 0 && a.push({
@@ -738,12 +957,12 @@ var le = /* @__PURE__ */ new Set([
738
957
  }
739
958
  }
740
959
  return a;
741
- }, dt = (e, t) => {
960
+ }, ft = (e, t) => {
742
961
  let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
743
962
  if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
744
963
  for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
745
964
  return r;
746
- }, ft = async (e) => {
965
+ }, pt = async (e) => {
747
966
  if (!await e.hasWorkflows().catch(() => !1)) return { kind: "not-applicable" };
748
967
  let t;
749
968
  try {
@@ -767,12 +986,12 @@ var le = /* @__PURE__ */ new Set([
767
986
  } finally {
768
987
  await t.close().catch(() => {});
769
988
  }
770
- }, pt = (e) => {
989
+ }, mt = (e) => {
771
990
  let t = Math.round(e / 6e4);
772
991
  if (t < 90) return `${t}m`;
773
992
  let n = Math.round(t / 60);
774
993
  return n < 48 ? `${n}h` : `${Math.round(n / 24)}d`;
775
- }, mt = (e) => {
994
+ }, ht = (e) => {
776
995
  if (e.kind === "not-applicable") return null;
777
996
  if (e.kind === "skipped") return `• stuck runs: NOT CHECKED — ${e.reason}\n This is the one doctor rule that reads the database rather than your source.
778
997
  Run it where the app's DB env vars are set, or ignore this line locally.`;
@@ -780,21 +999,21 @@ var le = /* @__PURE__ */ new Set([
780
999
  if (t.stalled.length === 0) n.push(`✓ stuck runs: none (${t.examined} live run(s) old enough to examine${t.waitingOnTimer > 0 ? `, ${t.waitingOnTimer} waiting on a durable timer` : ""}${t.alreadyReported > 0 ? `, ${t.alreadyReported} already reported` : ""})`);
781
1000
  else {
782
1001
  n.push(`✗ stuck runs: ${t.stalled.length} run(s) have made no progress`);
783
- for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${pt(e.idleMs)} ${e.reason}`);
1002
+ for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${mt(e.idleMs)} ${e.reason}`);
784
1003
  t.stalled.length > 20 && n.push(` … and ${t.stalled.length - 20} more`), n.push(" Nothing was changed — this is a SUSPICION, not a verdict. `awaiting-signal` usually means the sender never came.");
785
1004
  }
786
1005
  t.sawFullRunPage && n.push(" (the examined page filled up — more old runs exist than were read this pass)");
787
1006
  for (let e of t.failures) n.push(` ! could not examine ${e.subject}: ${e.detail}`);
788
1007
  return n.join("\n");
789
- }, ht = /(?:^|\n)\s*import\s+(?!type\s)[^;'"]*?from\s+['"]@voltro\/ui-shadcn(?:\/(?!tokens\.css)[^'"]+)?['"]/, gt = /@import\s+["']@voltro\/ui-shadcn\/tokens\.css["']|@import\s+["']tailwindcss["']|from\s+['"]@voltro\/ui-shadcn\/tokens\.css['"]|import\s+['"][^'"]*@voltro\/ui-shadcn\/tokens\.css['"]/, _t = (e) => {
1008
+ }, gt = /(?:^|\n)\s*import\s+(?!type\s)[^;'"]*?from\s+['"]@voltro\/ui-shadcn(?:\/(?!tokens\.css)[^'"]+)?['"]/, _t = /@import\s+["']@voltro\/ui-shadcn\/tokens\.css["']|@import\s+["']tailwindcss["']|from\s+['"]@voltro\/ui-shadcn\/tokens\.css['"]|import\s+['"][^'"]*@voltro\/ui-shadcn\/tokens\.css['"]/, vt = (e) => {
790
1009
  let t = [], n = [];
791
- for (let r of e) gt.test(r.source) ? n.push(r.path) : !r.path.endsWith(".css") && ht.test(r.source) && t.push(r.path);
1010
+ for (let r of e) _t.test(r.source) ? n.push(r.path) : !r.path.endsWith(".css") && gt.test(r.source) && t.push(r.path);
792
1011
  return {
793
1012
  kitImporters: t,
794
1013
  styleEntries: n
795
1014
  };
796
- }, vt = (e) => e.kitImporters.length > 0 && e.styleEntries.length === 0, yt = (e) => {
797
- if (!vt(e)) return [];
1015
+ }, yt = (e) => e.kitImporters.length > 0 && e.styleEntries.length === 0, bt = (e) => {
1016
+ if (!yt(e)) return [];
798
1017
  let t = e.kitImporters.slice(0, 5);
799
1018
  return [
800
1019
  `⚠ ui-shadcn: ${e.kitImporters.length} file(s) render kit components, and no CSS entry imports the kit's stylesheet.`,
@@ -813,7 +1032,7 @@ var le = /* @__PURE__ */ new Set([
813
1032
  " Or, if this app is not a kit app, import the unstyled control instead:",
814
1033
  " import { LocaleSwitcher } from '@voltro/i18n'"
815
1034
  ];
816
- }, bt = (e) => S(b(e, "app.config.ts")) || S(b(e, "app.config.js")), xt = (e) => {
1035
+ }, xt = (e) => S(b(e, "app.config.ts")) || S(b(e, "app.config.js")), St = (e) => {
817
1036
  let t = f(e), n = d(e);
818
1037
  return {
819
1038
  root: e,
@@ -822,22 +1041,22 @@ var le = /* @__PURE__ */ new Set([
822
1041
  apiBundle: n,
823
1042
  apiBundlePresent: S(n)
824
1043
  };
825
- }, St = [
1044
+ }, Ct = [
826
1045
  "Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
827
1046
  "Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
828
1047
  "(Generated Dockerfiles from `voltro create-project` already do this.)"
829
- ].join("\n "), Ct = (e, t = !1) => {
830
- let n = xt(e);
831
- if (console.log(`voltro doctor — serve preflight for ${n.root}`), !bt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
1048
+ ].join("\n "), wt = (e, t = !1) => {
1049
+ let n = St(e);
1050
+ if (console.log(`voltro doctor — serve preflight for ${n.root}`), !xt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
832
1051
  console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
833
1052
  let r = process.env.NODE_ENV === "production";
834
1053
  if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
835
1054
  let i = t || r;
836
- return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${St}`), +!!i;
837
- }, wt = (e) => {
1055
+ return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${Ct}`), +!!i;
1056
+ }, Tt = (e) => {
838
1057
  let t = e.absPath.replace(/\.server\.(tsx?)$/, ".$1");
839
1058
  if (t !== e.absPath) return e.sources.get(t) ?? e.sources.get(t.replace(/\.ts$/, ".tsx"));
840
- }, Tt = (e, t, n) => {
1059
+ }, Et = (e, t, n) => {
841
1060
  let r = new Set(e), i = _(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
842
1061
  `${e}.ts`,
843
1062
  `${e}.tsx`,
@@ -863,7 +1082,7 @@ var le = /* @__PURE__ */ new Set([
863
1082
  r !== void 0 && o.push(r);
864
1083
  }
865
1084
  return o;
866
- }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => Ft.test(y(n, e))), l = new Set(c);
1085
+ }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => Bt.test(y(n, e))), l = new Set(c);
867
1086
  for (; c.length > 0;) {
868
1087
  let e = c.shift();
869
1088
  if (!s.has(e)) {
@@ -876,11 +1095,11 @@ var le = /* @__PURE__ */ new Set([
876
1095
  let d = /* @__PURE__ */ new Set();
877
1096
  for (let e of s) l.has(e) || u.has(e) || d.add(e);
878
1097
  return d;
879
- }, M = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), Et = (e, t) => (e.match(t) ?? []).length, N = (e, t) => e.getDescendantsOfKind(O.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, Dt = (e, t) => {
1098
+ }, M = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), Dt = (e, t) => (e.match(t) ?? []).length, N = (e, t) => e.getDescendantsOfKind(O.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, Ot = (e, t) => {
880
1099
  if (e.src.includes(t)) return !0;
881
1100
  let n = t.replace(/^@voltro\/plugin-/, "");
882
1101
  return n !== t && N(e.file, RegExp(`^${n}Plugin$`)) > 0;
883
- }, Ot = (e, t) => {
1102
+ }, kt = (e, t) => {
884
1103
  let n = [], r = RegExp(`\\b${t}\\s*(?:<[^()]*>)?\\s*\\(`, "g"), i;
885
1104
  for (; (i = r.exec(e)) !== null;) {
886
1105
  let t = i.index + i[0].length, r = 1, a = t;
@@ -888,10 +1107,10 @@ var le = /* @__PURE__ */ new Set([
888
1107
  n.push(e.slice(t, a - 1));
889
1108
  }
890
1109
  return n;
891
- }, kt = (e, t) => {
1110
+ }, At = (e, t) => {
892
1111
  let n = t.getSymbol()?.getDeclarations()?.[0];
893
1112
  return n === void 0 ? [] : e.getDescendantsOfKind(O.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
894
- }, At = /^(insert|insertMany|insertIgnore|update|updateMany|upsert|delete|deleteMany|hardDelete|patchJson)$/, jt = /^(notify|sendNotification|sendWebhook|dispatchWebhook|sendMail|trigger[A-Z]\w*)$/, Mt = (e, t) => {
1113
+ }, jt = /^(insert|insertMany|insertIgnore|update|updateMany|upsert|delete|deleteMany|hardDelete|patchJson)$/, Mt = /^(notify|sendNotification|sendWebhook|dispatchWebhook|sendMail|trigger)\w*$/, Nt = (e, t) => {
895
1114
  if (!E.isObjectLiteralExpression(e)) return;
896
1115
  let n = e.getProperty("handler");
897
1116
  if (n === void 0 || !E.isPropertyAssignment(n)) return;
@@ -903,27 +1122,109 @@ var le = /* @__PURE__ */ new Set([
903
1122
  if (i !== void 0) return i;
904
1123
  let a = t.getVariableDeclaration(r.getText())?.getInitializer();
905
1124
  if (a !== void 0 && (E.isArrowFunction(a) || E.isFunctionExpression(a))) return a;
906
- }, Nt = (e) => {
907
- let t = [jt], n = (E.isArrowFunction(e) || E.isFunctionExpression(e) || E.isFunctionDeclaration(e) ? e.getParameters() : [])[1];
1125
+ }, Pt = (e) => {
1126
+ let t = [Mt], n = (E.isArrowFunction(e) || E.isFunctionExpression(e) || E.isFunctionDeclaration(e) ? e.getParameters() : [])[1];
908
1127
  if (n === void 0) return t;
909
1128
  let r = n.getNameNode();
910
- if (E.isIdentifier(r)) return t.push(RegExp(`^${r.getText()}\\.publish$`)), t.push(RegExp(`^${r.getText()}\\.store\\.${At.source.replace(/^\^|\$$/g, "")}$`)), t;
1129
+ if (E.isIdentifier(r)) return t.push(RegExp(`^${r.getText()}\\.publish$`)), t.push(RegExp(`^${r.getText()}\\.store\\.${jt.source.replace(/^\^|\$$/g, "")}$`)), t;
911
1130
  if (E.isObjectBindingPattern(r)) for (let e of r.getElements()) {
912
1131
  let n = e.getPropertyNameNode()?.getText() ?? e.getName(), r = e.getName();
913
- n === "publish" && t.push(RegExp(`^${r}$`)), n === "store" && t.push(RegExp(`^${r}\\.${At.source.replace(/^\^|\$$/g, "")}$`));
1132
+ n === "publish" && t.push(RegExp(`^${r}$`)), n === "store" && t.push(RegExp(`^${r}\\.${jt.source.replace(/^\^|\$$/g, "")}$`));
914
1133
  }
915
1134
  return t;
916
- }, Pt = (e) => e.getDescendantsOfKind(O.CallExpression).filter((e) => e.getExpression().getText() === "defineSubscriber").some((t) => {
1135
+ }, Ft = (e, t, n, r = 0, i = /* @__PURE__ */ new Set()) => {
1136
+ let a = e.getDescendantsOfKind(O.CallExpression);
1137
+ for (let e of a) {
1138
+ let t = e.getExpression().getText();
1139
+ if (n.some((e) => e.test(t))) return !0;
1140
+ }
1141
+ if (r >= 2) return !1;
1142
+ for (let e of a) {
1143
+ let a = e.getExpression().getText();
1144
+ if (!/^[A-Za-z_$][\w$]*$/.test(a) || i.has(a)) continue;
1145
+ i.add(a);
1146
+ let o = t.getFunction(a) ?? (() => {
1147
+ let e = t.getVariableDeclaration(a)?.getInitializer();
1148
+ return e !== void 0 && (E.isArrowFunction(e) || E.isFunctionExpression(e)) ? e : void 0;
1149
+ })();
1150
+ if (o !== void 0 && Ft(o, t, [...n, ...Pt(o)], r + 1, i)) return !0;
1151
+ }
1152
+ return !1;
1153
+ }, It = (e) => {
1154
+ let t = /* @__PURE__ */ new Set();
1155
+ for (let n of e.values()) {
1156
+ if (!/\bsetRowFilter\s*[<(]/.test(n)) continue;
1157
+ let e = /\btables\s*:\s*\[([^\]]*)\]/.exec(n);
1158
+ if (e !== null) for (let n of e[1].matchAll(/['"`]([A-Za-z_][\w]*)['"`]/g)) t.add(n[1]);
1159
+ }
1160
+ return t;
1161
+ }, Lt = (e) => {
1162
+ if (!/\.(query|stream|mutation|action)\.server\.tsx?$/.test(e.path)) return !1;
1163
+ let t = It(e.sources);
1164
+ return t.size === 0 || ![...e.src.matchAll(/\btable\s*:\s*['"`]([A-Za-z_][\w]*)['"`]/g)].map((e) => e[1]).some((e) => t.has(e)) ? !1 : /\b(eq|and|inSet)\s*\(/.test(e.src) || /\bwhere\s*:/.test(e.src);
1165
+ }, Rt = (e) => e.getDescendantsOfKind(O.CallExpression).filter((e) => e.getExpression().getText() === "defineSubscriber").some((t) => {
917
1166
  let n = t.getArguments()[0];
918
1167
  if (n === void 0 || !E.isObjectLiteralExpression(n) || n.getProperty("once") !== void 0) return !1;
919
- let r = Mt(n, e);
920
- if (r === void 0) return !1;
921
- let i = Nt(r);
922
- return r.getDescendantsOfKind(O.CallExpression).some((e) => {
923
- let t = e.getExpression().getText();
924
- return i.some((e) => e.test(t));
925
- });
926
- }), Ft = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, It = /\.(query|mutation|action|stream)\.server\.tsx?$/, Lt = [
1168
+ let r = Nt(n, e);
1169
+ return r !== void 0 && Ft(r, e, Pt(r));
1170
+ }), zt = (e) => e.getDescendantsOfKind(O.CallExpression).filter((e) => e.getExpression().getText() === "defineSubscriber").filter((e) => {
1171
+ let t = e.getArguments()[0];
1172
+ return t !== void 0 && E.isObjectLiteralExpression(t) && t.getProperty("once") === void 0;
1173
+ }).length, Bt = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, Vt = (e, t) => {
1174
+ let n;
1175
+ try {
1176
+ n = t.createSourceFile("/descriptor-scan.ts", e, { overwrite: !0 });
1177
+ } catch {
1178
+ return;
1179
+ }
1180
+ for (let e of n.getDescendantsOfKind(O.PropertyAssignment)) {
1181
+ if (e.getName() !== "output") continue;
1182
+ let t = e.getInitializer();
1183
+ if (t !== void 0 && E.isCallExpression(t) && /\.Array$/.test(t.getExpression().getText()) && (t = t.getArguments()[0]), t === void 0 || !E.isCallExpression(t) || !/\.Struct$/.test(t.getExpression().getText())) return;
1184
+ let n = t.getArguments()[0];
1185
+ if (n === void 0 || !E.isObjectLiteralExpression(n)) return;
1186
+ let r = [];
1187
+ for (let e of [n, ...n.getDescendantsOfKind(O.ObjectLiteralExpression)]) for (let t of e.getProperties()) if (E.isPropertyAssignment(t) || E.isShorthandPropertyAssignment(t)) r.push(t.getName());
1188
+ else if (E.isSpreadAssignment(t)) return;
1189
+ return r;
1190
+ }
1191
+ }, Ht = (e, t) => {
1192
+ let n = [], r = (e) => {
1193
+ e !== void 0 && (E.isObjectLiteralExpression(e) ? n.push(e) : E.isParenthesizedExpression(e) && r(e.getExpression()));
1194
+ };
1195
+ for (let t of e.getDescendantsOfKind(O.ReturnStatement)) r(t.getExpression());
1196
+ for (let t of e.getDescendantsOfKind(O.ArrowFunction)) r(t.getBody());
1197
+ let i = /* @__PURE__ */ new Set();
1198
+ for (let e of n) {
1199
+ let n = [];
1200
+ for (let t of e.getProperties()) if (E.isPropertyAssignment(t) || E.isShorthandPropertyAssignment(t)) n.push(t.getName());
1201
+ else if (E.isSpreadAssignment(t)) return;
1202
+ if (n.some((e) => t.has(e))) for (let e of n) i.add(e);
1203
+ }
1204
+ return [...i];
1205
+ }, Ut = (e, t, n) => {
1206
+ let r = RegExp(`\\btable\\(\\s*['"\`]${e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"\`]`);
1207
+ for (let [, i] of t) {
1208
+ if (!r.test(i)) continue;
1209
+ let t;
1210
+ try {
1211
+ t = n.createSourceFile("/entity-scan.ts", i, { overwrite: !0 });
1212
+ } catch {
1213
+ continue;
1214
+ }
1215
+ for (let n of t.getDescendantsOfKind(O.CallExpression)) {
1216
+ if (n.getExpression().getText() !== "table") continue;
1217
+ let [t, r] = n.getArguments();
1218
+ if (t === void 0 || !E.isStringLiteral(t) || t.getLiteralValue() !== e) continue;
1219
+ if (r === void 0 || !E.isObjectLiteralExpression(r)) return;
1220
+ let i = /* @__PURE__ */ new Set();
1221
+ for (let e of r.getProperties()) E.isPropertyAssignment(e) && /\.nullable\s*\(/.test(e.getText()) && i.add(e.getName());
1222
+ return i;
1223
+ }
1224
+ }
1225
+ let i = le[e];
1226
+ return i === void 0 ? void 0 : new Set(i);
1227
+ }, Wt = (e) => /\bNullOr\b|\bSchema\.Null\b|\bnullable\s*\(/.test(e), P = /\.(query|mutation|action|stream)\.server\.tsx?$/, Gt = [
927
1228
  "Id",
928
1229
  "ID",
929
1230
  "Hash",
@@ -949,7 +1250,7 @@ var le = /* @__PURE__ */ new Set([
949
1250
  "Comment",
950
1251
  "Provider",
951
1252
  "Format"
952
- ], Rt = RegExp(`(?:${Lt.join("|")})$|_(?:${Lt.map((e) => e.toLowerCase()).join("|")})$`), zt = [
1253
+ ], Kt = RegExp(`(?:${Gt.join("|")})$|_(?:${Gt.map((e) => e.toLowerCase()).join("|")})$`), qt = [
953
1254
  {
954
1255
  id: "hardcoded-framework-cookie",
955
1256
  scope: "client",
@@ -962,7 +1263,7 @@ var le = /* @__PURE__ */ new Set([
962
1263
  scope: "server",
963
1264
  smell: "an executor that never names its own descriptor — its input type is hand-written, so it can drift from the contract it implements",
964
1265
  use: "import the descriptor and type the input as `ExecutorInput<typeof descriptor>`, so the wire contract and the handler cannot disagree",
965
- match: ({ src: e, path: t }) => It.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
1266
+ match: ({ src: e, path: t }) => P.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
966
1267
  },
967
1268
  {
968
1269
  id: "subject-write-no-guard",
@@ -971,10 +1272,10 @@ var le = /* @__PURE__ */ new Set([
971
1272
  use: "declare `guards:` on the descriptor, or — if the procedure really is open — compare the loaded row's owner against `ctx.request.subject.id` and fail with a typed error",
972
1273
  match: (e) => {
973
1274
  let { src: t, path: n } = e;
974
- if (!It.test(n)) return !1;
1275
+ if (!P.test(n)) return !1;
975
1276
  let r = M(t);
976
1277
  if (!(/\bsubject\s*\.\s*id\b/.test(r) && /\b(?:insert|update|upsert|insertMany|updateMany|insertRow|upsertRow|updateManyRow)\s*\(/.test(r)) || /\b(?:guard|guards|requireScope|requireAnyScope|assertCan|assertScope|requireAuth|Unauthenticated)\b/.test(r) || /[=!]==?\s*[^;\n]*\bsubject\s*\.\s*id\b|\bsubject\s*\.\s*id\b\s*[=!]==/.test(r)) return !1;
977
- let i = wt(e);
1278
+ let i = Tt(e);
978
1279
  if (i === void 0) return !1;
979
1280
  let a = M(i);
980
1281
  return /\binternal\s*:\s*true\b/.test(a) || /\bguards\s*:\s*\[/.test(a) || /\.guard\s*\(/.test(a) ? !1 : /\bopenAccess\s*:/.test(a);
@@ -985,7 +1286,7 @@ var le = /* @__PURE__ */ new Set([
985
1286
  scope: "server",
986
1287
  smell: "raw fetch() for outbound HTTP on the server (no traceparent, no retry, and no SSRF guard where the URL is not a constant)",
987
1288
  use: "yield* HttpClient — the framework client. It injects traceparent, retries, and honours `http.allowHosts`. Where the URL is built from anything a caller supplied it ALSO blocks loopback / RFC-1918 / link-local targets (incl. 169.254.169.254) on the initial request AND every redirect hop — for a hard-coded host that half is redundant, and the first three are still the reason to switch",
988
- match: ({ file: e, path: t, serverOnly: n }) => (Ft.test(t) || n) && N(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
1289
+ match: ({ file: e, path: t, serverOnly: n }) => (Bt.test(t) || n) && N(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
989
1290
  },
990
1291
  {
991
1292
  id: "form",
@@ -1020,7 +1321,7 @@ var le = /* @__PURE__ */ new Set([
1020
1321
  scope: "client",
1021
1322
  smell: "useMemo fanning in multiple subscriptions (hand-maintained dep array)",
1022
1323
  use: "useDerived — dependency-tracked, referentially stable",
1023
- match: ({ src: e }) => Et(e, /\buseSubscription\s*[(<]/g) >= 3 && Et(e, /\buseMemo\s*\(/g) >= 3
1324
+ match: ({ src: e }) => Dt(e, /\buseSubscription\s*[(<]/g) >= 3 && Dt(e, /\buseMemo\s*\(/g) >= 3
1024
1325
  },
1025
1326
  {
1026
1327
  id: "next-shim",
@@ -1052,7 +1353,7 @@ var le = /* @__PURE__ */ new Set([
1052
1353
  match: ({ file: e }) => e.getDescendantsOfKind(O.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
1053
1354
  let t = e.getFirstAncestorByKind(O.VariableDeclaration)?.getNameNode();
1054
1355
  return t === void 0 ? [] : E.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
1055
- }).some((t) => kt(e, t).some((e) => {
1356
+ }).some((t) => At(e, t).some((e) => {
1056
1357
  let t = e.getParent();
1057
1358
  if (E.isBinaryExpression(t)) {
1058
1359
  let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
@@ -1103,7 +1404,7 @@ var le = /* @__PURE__ */ new Set([
1103
1404
  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)",
1104
1405
  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) => {
1105
1406
  let n = t[1];
1106
- return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Rt.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
1407
+ return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Kt.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
1107
1408
  }))
1108
1409
  },
1109
1410
  {
@@ -1113,15 +1414,60 @@ var le = /* @__PURE__ */ new Set([
1113
1414
  use: "you cannot look a value up by equality AND encrypt it non-deterministically — `.encrypted()` would make this column unfindable. Store the HASH as the unique lookup key and keep the secret itself either `.encrypted()` in a second column or nowhere (the framework's own `_voltro_api_keys.hashedKey` is the shape: the presented value is hashed and compared, never read back). A deterministic cipher is the other option and buys equality lookup at the cost of leaking which rows share a value",
1114
1415
  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) => {
1115
1416
  let n = t[1];
1116
- return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Rt.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
1417
+ return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Kt.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
1117
1418
  }))
1118
1419
  },
1420
+ {
1421
+ id: "nullable-column-a-mutation-cannot-clear",
1422
+ scope: "server",
1423
+ smell: "a mutation writes a NULLABLE column through an input field that does not accept `null` — the value can be set once and never cleared",
1424
+ use: "declare the field as `Schema.optional(Schema.NullOr(...))` — a column the schema lets be empty needs a wire shape that can say so",
1425
+ match: (e) => {
1426
+ if (!/\.mutation\.tsx?$/.test(e.path)) return !1;
1427
+ let t = /target\s*:\s*\{[^}]*\btable\s*:\s*['"`]([^'"`]+)['"`]/.exec(e.src)?.[1];
1428
+ if (t === void 0) return !1;
1429
+ let n = Ut(t, e.sources, e.file.getProject());
1430
+ if (n === void 0 || n.size === 0) return !1;
1431
+ for (let t of e.file.getDescendantsOfKind(O.PropertyAssignment)) {
1432
+ if (t.getName() !== "input") continue;
1433
+ let e = t.getInitializer();
1434
+ if (e === void 0 || !E.isCallExpression(e) || !/\.Struct$/.test(e.getExpression().getText())) continue;
1435
+ let r = e.getArguments()[0];
1436
+ if (!(r === void 0 || !E.isObjectLiteralExpression(r))) {
1437
+ for (let e of r.getProperties()) if (E.isPropertyAssignment(e) && n.has(e.getName()) && !Wt(e.getText())) return !0;
1438
+ }
1439
+ }
1440
+ return !1;
1441
+ }
1442
+ },
1443
+ {
1444
+ id: "executor-builds-an-undeclared-field",
1445
+ scope: "server",
1446
+ smell: "an executor builds a field its descriptor's `output` does not declare (silently stripped on the way out)",
1447
+ use: "add the field to the descriptor's `output` struct, or stop building it — `output` IS the serializer, so an undeclared key never reaches the caller",
1448
+ match: (e) => {
1449
+ if (!P.test(e.path)) return !1;
1450
+ let t = Tt(e);
1451
+ if (t === void 0) return !1;
1452
+ let n = Vt(t, e.file.getProject());
1453
+ if (n === void 0 || n.length === 0) return !1;
1454
+ let r = Ht(e.file, new Set(n));
1455
+ return r === void 0 || r.length === 0 ? !1 : r.some((e) => !n.includes(e));
1456
+ }
1457
+ },
1119
1458
  {
1120
1459
  id: "subscriber-effect-without-once",
1121
1460
  scope: "server",
1122
1461
  smell: "a *.subscribe.ts handler that WRITES or publishes with no `once:` (it runs on every replica, so the effect repeats once per replica)",
1123
1462
  use: "once: true on defineSubscriber — one INSERT-wins claim per change, fleet-wide (or `once: (event) => key` for a coarser key). A hand-rolled `insertIgnore` dedupe is this, written out.",
1124
- match: ({ file: e }) => Pt(e)
1463
+ match: ({ file: e }) => Rt(e)
1464
+ },
1465
+ {
1466
+ id: "row-filter-duplicated-by-hand",
1467
+ scope: "server",
1468
+ smell: "a handler that constrains a table your registered `setRowFilter` already constrains",
1469
+ use: "drop the hand-written predicate — the filter covers the query, the mutation, the stream AND the subscription delivery. Keep it only if this handler must be NARROWER than the filter, and say so in a comment (two identical predicates ANDed are as tight as one, so this is maintenance, not a leak)",
1470
+ match: (e) => Lt(e)
1125
1471
  },
1126
1472
  {
1127
1473
  id: "mutation-tail-effect",
@@ -1186,7 +1532,7 @@ var le = /* @__PURE__ */ new Set([
1186
1532
  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",
1187
1533
  match: ({ file: e, testSources: t }) => N(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
1188
1534
  let t = M(e);
1189
- return Ot(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
1535
+ return kt(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
1190
1536
  })
1191
1537
  },
1192
1538
  {
@@ -1196,14 +1542,14 @@ var le = /* @__PURE__ */ new Set([
1196
1542
  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",
1197
1543
  match: ({ src: e }) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
1198
1544
  }
1199
- ], P = (e) => L(e), Bt = (e, t) => {
1545
+ ], F = (e) => L(e), Jt = (e, t) => {
1200
1546
  let n = /* @__PURE__ */ new Set();
1201
1547
  for (let r of t) {
1202
1548
  let t = y(e, r), i = t.split(/[\\/]/)[0];
1203
1549
  n.add(i !== void 0 && i !== t ? i : ".");
1204
1550
  }
1205
1551
  return [...n].sort();
1206
- }, F = /\.(tsx?|jsx?)$/, I = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, Vt = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, Ht = (e, t = [], n = 0) => {
1552
+ }, Yt = /\.(tsx?|jsx?)$/, I = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, Xt = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, Zt = (e, t = [], n = 0) => {
1207
1553
  if (n > 12 || I.test(e)) return t;
1208
1554
  let r;
1209
1555
  try {
@@ -1215,8 +1561,8 @@ var le = /* @__PURE__ */ new Set([
1215
1561
  let r = v(e, i.name);
1216
1562
  if (i.isDirectory()) {
1217
1563
  if (i.name.startsWith(".")) continue;
1218
- Ht(r, t, n + 1);
1219
- } else je.test(i.name) && t.push(r);
1564
+ Zt(r, t, n + 1);
1565
+ } else Me.test(i.name) && t.push(r);
1220
1566
  }
1221
1567
  return t;
1222
1568
  }, L = (e, t = [], n = 0) => {
@@ -1234,10 +1580,10 @@ var le = /* @__PURE__ */ new Set([
1234
1580
  } catch {
1235
1581
  continue;
1236
1582
  }
1237
- a.isDirectory() ? L(r, t, n + 1) : F.test(i) && !/\.(test|spec)\./.test(i) && !Vt.test(i) && t.push(r);
1583
+ a.isDirectory() ? L(r, t, n + 1) : Yt.test(i) && !/\.(test|spec)\./.test(i) && !Xt.test(i) && t.push(r);
1238
1584
  }
1239
1585
  return t;
1240
- }, Ut = (e, t = [], n = 0) => {
1586
+ }, Qt = (e, t = [], n = 0) => {
1241
1587
  if (n > 12 || I.test(e)) return t;
1242
1588
  let r;
1243
1589
  try {
@@ -1252,17 +1598,17 @@ var le = /* @__PURE__ */ new Set([
1252
1598
  } catch {
1253
1599
  continue;
1254
1600
  }
1255
- a.isDirectory() ? Ut(r, t, n + 1) : F.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1601
+ a.isDirectory() ? Qt(r, t, n + 1) : Yt.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1256
1602
  }
1257
1603
  return t;
1258
- }, Wt = (e) => {
1259
- let t = P(e);
1604
+ }, $t = (e) => {
1605
+ let t = F(e);
1260
1606
  return {
1261
- dirs: Bt(e, t),
1607
+ dirs: Jt(e, t),
1262
1608
  fileCount: t.length
1263
1609
  };
1264
- }, Gt = (e) => {
1265
- let t = P(e);
1610
+ }, en = (e) => {
1611
+ let t = F(e);
1266
1612
  if (t.length === 0) return [];
1267
1613
  let n = new D({
1268
1614
  useInMemoryFileSystem: !0,
@@ -1271,8 +1617,8 @@ var le = /* @__PURE__ */ new Set([
1271
1617
  for (let e of t) try {
1272
1618
  i.set(e, C(e, "utf8"));
1273
1619
  } catch {}
1274
- let a = Tt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1275
- for (let t of Ut(e)) try {
1620
+ let a = Et([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1621
+ for (let t of Qt(e)) try {
1276
1622
  o.set(t, C(t, "utf8"));
1277
1623
  } catch {}
1278
1624
  for (let s of t) {
@@ -1292,23 +1638,34 @@ var le = /* @__PURE__ */ new Set([
1292
1638
  } catch {
1293
1639
  continue;
1294
1640
  }
1295
- for (let t of zt) {
1296
- if (t.plugin !== void 0 && Dt(c, t.plugin) || !t.match(c)) continue;
1641
+ for (let t of qt) {
1642
+ if (t.plugin !== void 0 && Ot(c, t.plugin) || !t.match(c)) continue;
1297
1643
  let n = r.get(t.id) ?? [];
1298
1644
  n.push(y(e, s)), r.set(t.id, n);
1299
1645
  }
1300
1646
  }
1301
- return zt.filter((e) => r.has(e.id)).map((e) => ({
1647
+ return qt.filter((e) => r.has(e.id)).map((e) => ({
1302
1648
  rule: e,
1303
1649
  files: r.get(e.id)
1304
1650
  })).sort((e, t) => t.files.length - e.files.length || e.rule.id.localeCompare(t.rule.id));
1651
+ }, tn = (e) => {
1652
+ let t = new D({
1653
+ useInMemoryFileSystem: !0,
1654
+ skipAddingFilesFromTsConfig: !0
1655
+ }), n = 0;
1656
+ for (let r of F(e)) if (/\.subscribe\.tsx?$/.test(r)) try {
1657
+ let e = t.createSourceFile(`/count${r.endsWith("x") ? ".tsx" : ".ts"}`, C(r, "utf8"), { overwrite: !0 });
1658
+ n += zt(e);
1659
+ } catch {}
1660
+ return n;
1305
1661
  }, R = (e) => {
1306
- let t = Wt(e);
1662
+ let t = $t(e);
1307
1663
  return {
1308
1664
  root: e,
1309
1665
  scannedFiles: t.fileCount,
1310
1666
  scannedDirs: t.dirs,
1311
- findings: Gt(e).map(({ rule: e, files: t }) => ({
1667
+ subscribersWithoutOnce: tn(e),
1668
+ findings: en(e).map(({ rule: e, files: t }) => ({
1312
1669
  id: e.id,
1313
1670
  scope: e.scope,
1314
1671
  smell: e.smell,
@@ -1316,21 +1673,21 @@ var le = /* @__PURE__ */ new Set([
1316
1673
  files: t
1317
1674
  }))
1318
1675
  };
1319
- }, Kt = 3, qt = .2, Jt = 50, Yt = (e, t) => e >= Jt && t > 0 && e / t >= qt, Xt = (e) => {
1676
+ }, nn = 3, rn = .2, an = 50, on = (e, t) => e >= an && t > 0 && e / t >= rn, sn = (e) => {
1320
1677
  let t = /* @__PURE__ */ new Map();
1321
1678
  for (let n of L(e)) try {
1322
1679
  t.set(y(e, n), C(n, "utf8"));
1323
1680
  } catch {}
1324
1681
  let n = /* @__PURE__ */ new Map();
1325
- for (let t of Ht(e)) try {
1682
+ for (let t of Zt(e)) try {
1326
1683
  n.set(y(e, t), C(t, "utf8"));
1327
1684
  } catch {}
1328
- let r = Me({
1685
+ let r = Ne({
1329
1686
  sources: t,
1330
1687
  envFiles: n
1331
1688
  });
1332
- r !== void 0 && (console.log(""), console.log(Ne(r)));
1333
- }, Zt = (e) => {
1689
+ r !== void 0 && (console.log(""), console.log(Pe(r)));
1690
+ }, cn = (e) => {
1334
1691
  let t = R(e);
1335
1692
  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;
1336
1693
  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;
@@ -1342,27 +1699,33 @@ var le = /* @__PURE__ */ new Set([
1342
1699
  console.log(` [${e}]`);
1343
1700
  let i = [...r].sort((e, t) => e.files.length - t.files.length);
1344
1701
  for (let e of i) {
1345
- if (Yt(e.files.length, t.scannedFiles)) {
1702
+ if (on(e.files.length, t.scannedFiles)) {
1346
1703
  let n = Math.round(e.files.length / t.scannedFiles * 100);
1347
1704
  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.)");
1348
1705
  continue;
1349
1706
  }
1350
- let r = e.files.slice(0, Kt).join(", "), i = e.files.length - Kt;
1707
+ let r = e.files.slice(0, nn).join(", "), i = e.files.length - nn;
1351
1708
  i > 0 && (n += i);
1352
1709
  let a = i > 0 ? `, +${i} more` : "";
1353
1710
  console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${r}${a}`), console.log(` \u2192 ${e.use}`);
1354
1711
  }
1355
1712
  }
1356
- 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;
1357
- }, Qt = "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.", $t = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, en = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, tn = (e) => {
1358
- let t = $t.exec(e)?.[1];
1713
+ n > 0 && console.log(` (${n} more file path${n === 1 ? "" : "s"} not shown \u2014 \`voltro doctor --json\` prints every one.)`);
1714
+ let r = t.findings.find((e) => e.id === "subscriber-effect-without-once");
1715
+ if (t.subscribersWithoutOnce > 0) {
1716
+ let e = r?.files.length ?? 0;
1717
+ console.log(` \u24d8 ${t.subscribersWithoutOnce} subscriber(s) declare no \`once:\`; an effect was found in ${e}. The rule reads each subscriber's OWN file (its handler and the functions that file declares) and does not resolve imports — a write in another module is unexamined, not absent.`);
1718
+ }
1719
+ return console.log(" (See the \"Pick the SERVER primitive\" / \"Pick the CLIENT primitive\" rubrics in AGENTS.md / CLAUDE.md.)"), t.findings.length;
1720
+ }, ln = "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.", un = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, dn = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, fn = (e) => {
1721
+ let t = un.exec(e)?.[1];
1359
1722
  if (t !== void 0) return t === "ssr" ? "ssr" : null;
1360
- let n = en.exec(e)?.[1];
1723
+ let n = dn.exec(e)?.[1];
1361
1724
  return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
1362
1725
  }, z = async (e) => {
1363
1726
  let t = v(e, "src", "pages");
1364
1727
  if (!S(t)) return [];
1365
- let { pages: n, dirs: r } = await c(t), i = [];
1728
+ let { pages: n, dirs: r } = await a(t), i = [];
1366
1729
  for (let a of n) {
1367
1730
  if (!u(r, a.dirChain).some((e) => e.hasLayout)) continue;
1368
1731
  let n;
@@ -1372,7 +1735,7 @@ var le = /* @__PURE__ */ new Set([
1372
1735
  continue;
1373
1736
  }
1374
1737
  if (l.test(n)) continue;
1375
- let o = tn(n);
1738
+ let o = fn(n);
1376
1739
  o !== null && i.push({
1377
1740
  file: y(e, v(t, a.file)),
1378
1741
  pattern: a.pattern,
@@ -1380,20 +1743,20 @@ var le = /* @__PURE__ */ new Set([
1380
1743
  });
1381
1744
  }
1382
1745
  return i;
1383
- }, nn = 10, rn = async (e) => {
1746
+ }, pn = 10, B = async (e) => {
1384
1747
  console.log("");
1385
1748
  let t = v(e, "src", "pages");
1386
1749
  if (!S(t)) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
1387
1750
  let n = await z(e);
1388
1751
  if (n.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
1389
1752
  console.log(`• renderMode:'spa' candidates (${n.length} page${n.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
1390
- for (let e of n.slice(0, nn)) {
1753
+ for (let e of n.slice(0, pn)) {
1391
1754
  let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
1392
1755
  console.log(` ${e.file} (${e.pattern}) — ${t}`);
1393
1756
  }
1394
- let r = n.length - nn;
1395
- return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${Qt}`), n.length;
1396
- }, an = /* @__PURE__ */ new Set([
1757
+ let r = n.length - pn;
1758
+ return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${ln}`), n.length;
1759
+ }, mn = /* @__PURE__ */ new Set([
1397
1760
  "node_modules",
1398
1761
  "dist",
1399
1762
  "build",
@@ -1401,7 +1764,7 @@ var le = /* @__PURE__ */ new Set([
1401
1764
  ".vite",
1402
1765
  ".framework",
1403
1766
  ".git"
1404
- ]), on = (e, t, n) => {
1767
+ ]), hn = (e, t, n) => {
1405
1768
  let r;
1406
1769
  try {
1407
1770
  r = w(e, { withFileTypes: !0 });
@@ -1409,11 +1772,11 @@ var le = /* @__PURE__ */ new Set([
1409
1772
  return;
1410
1773
  }
1411
1774
  for (let i of r) {
1412
- if (an.has(i.name)) continue;
1775
+ if (mn.has(i.name)) continue;
1413
1776
  let r = v(e, i.name);
1414
- i.isDirectory() ? on(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(y(t, r));
1777
+ i.isDirectory() ? hn(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(y(t, r));
1415
1778
  }
1416
- }, sn = (e, t) => {
1779
+ }, gn = (e, t) => {
1417
1780
  let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
1418
1781
  for (let i of t) {
1419
1782
  let t;
@@ -1438,16 +1801,16 @@ var le = /* @__PURE__ */ new Set([
1438
1801
  }
1439
1802
  }
1440
1803
  return n;
1441
- }, cn = (e, t) => {
1804
+ }, _n = (e, t) => {
1442
1805
  let n = e.slice(t.length).split(x).slice(0, -1)[0];
1443
1806
  return n !== void 0 && n.startsWith("[");
1444
- }, B = (e) => {
1807
+ }, V = (e) => {
1445
1808
  let t = [], n = v(e, "src", "pages"), r = v(e, "src");
1446
1809
  if (!S(r)) return t;
1447
1810
  let i = [];
1448
- on(r, e, i), i.sort();
1811
+ hn(r, e, i), i.sort();
1449
1812
  let a = `${y(e, n)}${x}`;
1450
- sn(e, i);
1813
+ gn(e, i);
1451
1814
  for (let n of i) {
1452
1815
  let r = n.slice(n.lastIndexOf(x) + 1);
1453
1816
  if (n.startsWith(a)) {
@@ -1463,7 +1826,7 @@ var le = /* @__PURE__ */ new Set([
1463
1826
  file: n
1464
1827
  });
1465
1828
  } catch {}
1466
- cn(n, a) && t.push({
1829
+ _n(n, a) && t.push({
1467
1830
  kind: "top-level-dynamic-route",
1468
1831
  file: n
1469
1832
  });
@@ -1475,32 +1838,32 @@ var le = /* @__PURE__ */ new Set([
1475
1838
  });
1476
1839
  }
1477
1840
  return t;
1478
- }, V = 10, H = (e, t, n) => {
1841
+ }, vn = 10, H = (e, t, n) => {
1479
1842
  if (t.length === 0) return;
1480
1843
  console.log(`• ${e} (${t.length}):`);
1481
- for (let e of t.slice(0, V)) console.log(` ${e}`);
1482
- let r = t.length - V;
1844
+ for (let e of t.slice(0, vn)) console.log(` ${e}`);
1845
+ let r = t.length - vn;
1483
1846
  r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
1484
- }, ln = (e) => {
1847
+ }, yn = (e) => {
1485
1848
  console.log("");
1486
1849
  let t = v(e, "src", "pages");
1487
1850
  if (!S(t)) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
1488
- let n = B(e);
1851
+ let n = V(e);
1489
1852
  if (n.length === 0) return console.log("✓ page-convention scan: every route is a page.tsx in its own directory, with a test beside it."), 0;
1490
1853
  let r = (e) => n.filter((t) => t.kind === e).map((e) => e.file);
1491
1854
  return H("will NEVER route — page.tsx outside src/pages/", r("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."), H("pages with no test beside them", r("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."), H("routes whose FIRST segment is dynamic — they claim every URL of that depth", r("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."), H("pages with no `meta` export — they serve the APP name as their title", r("page-without-meta"), "a page with no `meta` sets no title, so the response carries `<title>{your app.config name}</title>` — the same string on every such page, to every reader that does not run JavaScript: link previews, crawlers, a bookmark taken before hydration, a screen reader announcing the document. The client router fixes the tab after hydration, which is exactly why this is easy to miss. Add `export const meta = { title: \"…\" }` (or a function of params/loaderData). If such a page also has no real body — it redirects from an effect, say — the missing title is the symptom rather than the bug: on an `ssr` route the server answers 200 with an empty document and the redirect happens only once JavaScript runs. Move it into the loader (`redirect(...)`, a real 303). Advisory."), n.length;
1492
- }, un = "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.", dn = /* @__PURE__ */ new Map(), U = (e) => {
1493
- let t = dn.get(e);
1855
+ }, bn = "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.", xn = /* @__PURE__ */ new Map(), U = (e) => {
1856
+ let t = xn.get(e);
1494
1857
  if (t !== void 0) return t;
1495
1858
  let n = (async () => {
1496
- let { walk: t, loadDiscovered: n } = await import("./dev-B9Gz0k85.js"), r = await t(e);
1859
+ let { walk: t, loadDiscovered: n } = await import("./dev-Dff_pdik.js"), r = await t(e);
1497
1860
  return {
1498
1861
  files: r,
1499
- discovered: await n(r)
1862
+ discovered: await n(r, { root: e })
1500
1863
  };
1501
1864
  })();
1502
- return dn.set(e, n), n;
1503
- }, fn = async (e) => {
1865
+ return xn.set(e, n), n;
1866
+ }, Sn = async (e) => {
1504
1867
  let t;
1505
1868
  try {
1506
1869
  let { files: n, discovered: r } = await U(e), i = r.tables.map((e) => {
@@ -1511,12 +1874,12 @@ var le = /* @__PURE__ */ new Set([
1511
1874
  };
1512
1875
  });
1513
1876
  if (i.length === 0) return;
1514
- t = pe(fe(n, i, e));
1877
+ t = me(pe(n, i, e));
1515
1878
  } catch {
1516
1879
  return;
1517
1880
  }
1518
1881
  t !== null && (console.log(""), console.log(`${t.includes("does not exist") ? "✗" : "✓"} ${t}`));
1519
- }, pn = (e) => e.map((e) => {
1882
+ }, Cn = (e) => e.map((e) => {
1520
1883
  let t = e, n = Object.entries(t.fields ?? {}).map(([e, t]) => ({
1521
1884
  name: e,
1522
1885
  type: typeof t?.type == "string" ? t.type : ""
@@ -1526,13 +1889,13 @@ var le = /* @__PURE__ */ new Set([
1526
1889
  columns: n,
1527
1890
  primaryKey: t.appliedPrimaryKey ?? []
1528
1891
  };
1529
- }), mn = async (e) => {
1892
+ }), wn = async (e) => {
1530
1893
  try {
1531
1894
  let { missingRelationSources: t } = await import("./doctorSourceRelations-c5mlN5HI.js"), { sourceKeys: n } = await import("@voltro/protocol"), { readFile: r } = await import("node:fs/promises"), { discovered: i } = await U(e), a = [], o = [];
1532
1895
  for (let e of i.queries) {
1533
1896
  let i = n(e.descriptor.source);
1534
1897
  if (i.length === 0) continue;
1535
- let s = hn(e.file);
1898
+ let s = Tn(e.file);
1536
1899
  if (s === void 0) continue;
1537
1900
  let c = await r(s, "utf8").catch(() => null);
1538
1901
  if (c === null) continue;
@@ -1549,8 +1912,8 @@ var le = /* @__PURE__ */ new Set([
1549
1912
  } catch {
1550
1913
  return null;
1551
1914
  }
1552
- }, hn = (e) => e.endsWith(".ts") && !e.endsWith(".server.ts") ? `${e.slice(0, -3)}.server.ts` : void 0, gn = async (e) => {
1553
- let t = await mn(e);
1915
+ }, Tn = (e) => e.endsWith(".ts") && !e.endsWith(".server.ts") ? `${e.slice(0, -3)}.server.ts` : void 0, En = async (e) => {
1916
+ let t = await wn(e);
1554
1917
  if (t === null) return;
1555
1918
  let n = t.findings;
1556
1919
  if (n.length > 0) {
@@ -1566,19 +1929,19 @@ var le = /* @__PURE__ */ new Set([
1566
1929
  }, W = async (e) => {
1567
1930
  try {
1568
1931
  let { discovered: t } = await U(e);
1569
- return ve(pn(t.tables));
1932
+ return ye(Cn(t.tables));
1570
1933
  } catch {
1571
1934
  return null;
1572
1935
  }
1573
- }, _n = async (e) => {
1936
+ }, Dn = async (e) => {
1574
1937
  let t = await W(e);
1575
1938
  if (t === null || t.tablesChecked === 0) return;
1576
- let n = be(t);
1939
+ let n = xe(t);
1577
1940
  n !== null && (console.log(""), console.log(`${t.issues.length > 0 ? "✗" : "✓"} ${n}`));
1578
1941
  }, G = async (e) => {
1579
1942
  try {
1580
1943
  let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await U(e);
1581
- return we(n.events.map((e) => {
1944
+ return Te(n.events.map((e) => {
1582
1945
  let n = e.descriptor, r = t(n.key);
1583
1946
  return {
1584
1947
  name: n.name,
@@ -1590,12 +1953,12 @@ var le = /* @__PURE__ */ new Set([
1590
1953
  } catch {
1591
1954
  return null;
1592
1955
  }
1593
- }, vn = async (e) => {
1956
+ }, On = async (e) => {
1594
1957
  let t = await G(e);
1595
1958
  if (t === null || t.eventsChecked === 0) return;
1596
- let n = Te(t);
1959
+ let n = Ee(t);
1597
1960
  n !== null && (console.log(""), console.log(`• ${n}`));
1598
- let r = Ee(t);
1961
+ let r = De(t);
1599
1962
  r !== null && (console.log(""), console.log(`⚠️ ${r}`));
1600
1963
  }, K = (e) => {
1601
1964
  let t = e, n = typeof t?.message == "string" && t.message !== "" ? t.message : String(e), r = typeof t?.code == "string" ? t.code : void 0;
@@ -1618,7 +1981,7 @@ var le = /* @__PURE__ */ new Set([
1618
1981
  }
1619
1982
  }, J = async (r) => {
1620
1983
  try {
1621
- let { loadApiConfig: i } = await import("./dev-B9Gz0k85.js"), { discovered: a } = await U(r), o = t(await i(r)), s = {
1984
+ let { loadApiConfig: i } = await import("./dev-Dff_pdik.js"), { discovered: a } = await U(r), o = e(await i(r)), s = {
1622
1985
  queries: a.queries,
1623
1986
  mutations: a.mutations,
1624
1987
  actions: a.actions,
@@ -1629,7 +1992,7 @@ var le = /* @__PURE__ */ new Set([
1629
1992
  checked: !0,
1630
1993
  defaultDeny: o,
1631
1994
  undecided: n(s),
1632
- openAccess: e(s)
1995
+ openAccess: t(s)
1633
1996
  };
1634
1997
  } catch (e) {
1635
1998
  return {
@@ -1640,7 +2003,7 @@ var le = /* @__PURE__ */ new Set([
1640
2003
  openAccess: []
1641
2004
  };
1642
2005
  }
1643
- }, yn = async (e) => {
2006
+ }, kn = async (e) => {
1644
2007
  let t = await J(e);
1645
2008
  if (!t.checked) return console.log(""), console.log("• access decisions: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
1646
2009
  console.log(""), console.log(`access decisions · security.defaultDeny ${t.defaultDeny ? "ON" : "OFF"}`), console.log(` ✗ no access decision ${String(t.undecided.length).padStart(4)}`), console.log(` ✓ openAccess, declared on purpose ${String(t.openAccess.length).padStart(4)}`);
@@ -1648,8 +2011,8 @@ var le = /* @__PURE__ */ new Set([
1648
2011
  if (t.undecided.length === 0) return !1;
1649
2012
  console.log("");
1650
2013
  for (let e of t.undecided) console.log(` ✗ ${e.tag} (${e.kind})${e.file === void 0 ? "" : `\n ${e.file}`}`);
1651
- return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"), bn(), !0) : (console.log(" This app sets `security: { defaultDeny: false }`, so these BOOT — and each one is"), console.log(" callable by any authenticated session. Advisory here for that reason only."), bn(), !1);
1652
- }, bn = () => {
2014
+ return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"), An(), !0) : (console.log(" This app sets `security: { defaultDeny: false }`, so these BOOT — and each one is"), console.log(" callable by any authenticated session. Advisory here for that reason only."), An(), !1);
2015
+ }, An = () => {
1653
2016
  console.log(" `guards: [{ scope: '…' }]` authority the SUBJECT holds globally"), console.log(" `guards: [{ action, resourceType, resource }]` authority per ROW / per resource"), console.log(" — backed by `defineResourcePolicy` + a tuple source you register. If your"), console.log(" relationships already live in your own tables (a `teamMembers` row), register"), console.log(" a tuple source over them; nothing is copied. Fail-closed without a resolver."), console.log(" Docs: Authentication → Authorization."), console.log(" `openAccess: '<why anyone may call it>'` deliberately unauthenticated");
1654
2017
  }, Y = async (e) => {
1655
2018
  try {
@@ -1686,7 +2049,7 @@ var le = /* @__PURE__ */ new Set([
1686
2049
  verified: 0
1687
2050
  };
1688
2051
  }
1689
- }, xn = async (e) => {
2052
+ }, jn = async (e) => {
1690
2053
  let t = await Y(e);
1691
2054
  if (!t.checked) return console.log(""), console.log("• webhook verification: NOT CHECKED — the app's webhooks could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
1692
2055
  if (t.verified + t.open.length + t.unverified.length === 0) return !1;
@@ -1696,13 +2059,13 @@ var le = /* @__PURE__ */ new Set([
1696
2059
  console.log("");
1697
2060
  for (let e of t.unverified) console.log(` ✗ ${e.id}\n ${e.file}`);
1698
2061
  return console.log(""), console.log(" These are public POSTs that run your application code, and `voltro dev` /"), console.log(" `voltro serve` REFUSE to mount them. Declare one of: `provider:` (a preset),"), console.log(" `signature:` (your own HMAC scheme), `verification: 'provider'` (your handler"), console.log(" verifies), or `verification: 'none'` (deliberately public)."), !0;
1699
- }, Sn = async (e) => {
2062
+ }, Mn = async (e) => {
1700
2063
  let { status: t, message: n } = await q(e);
1701
2064
  return t.checked ? n !== void 0 && (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);
1702
- }, Cn = async (e) => ft({
2065
+ }, Nn = async (e) => pt({
1703
2066
  hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
1704
2067
  openStore: async () => {
1705
- let { loadApiConfig: t, buildStore: n } = await import("./dev-B9Gz0k85.js"), r = await t(e);
2068
+ let { loadApiConfig: t, buildStore: n } = await import("./dev-Dff_pdik.js"), r = await t(e);
1706
2069
  if (r === null) throw Error("no app.config.ts in this directory");
1707
2070
  let i = await n(r, void 0);
1708
2071
  return {
@@ -1716,14 +2079,14 @@ var le = /* @__PURE__ */ new Set([
1716
2079
  let { sweepStalledRuns: t } = await import("@voltro/workflow");
1717
2080
  return t({ store: e });
1718
2081
  }
1719
- }), wn = async (e) => {
1720
- let t = mt(await Cn(e));
2082
+ }), Pn = async (e) => {
2083
+ let t = ht(await Nn(e));
1721
2084
  t !== null && (console.log(""), console.log(t));
1722
- }, Tn = async (e) => {
2085
+ }, Fn = async (e) => {
1723
2086
  let t;
1724
2087
  try {
1725
2088
  let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await U(e);
1726
- t = ke(r, i.workflows.map((e) => {
2089
+ t = Ae(r, i.workflows.map((e) => {
1727
2090
  let t = e.definition, r = t.payloadSchema === void 0 ? {
1728
2091
  required: [],
1729
2092
  optional: []
@@ -1737,18 +2100,18 @@ var le = /* @__PURE__ */ new Set([
1737
2100
  } catch {
1738
2101
  return;
1739
2102
  }
1740
- let n = Ae(t);
2103
+ let n = je(t);
1741
2104
  if (n === null) return;
1742
2105
  console.log("");
1743
2106
  let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
1744
2107
  console.log(`${r} ${n}`);
1745
- }, X = "voltro-authz-allowlist.txt", En = (e, t = (e) => {
2108
+ }, X = "voltro-authz-allowlist.txt", In = (e, t = (e) => {
1746
2109
  try {
1747
2110
  return C(e, "utf8");
1748
2111
  } catch {
1749
2112
  return "";
1750
2113
  }
1751
- }) => /* @__PURE__ */ new Set([...Xe, ...Ze(L(e).map(t))]), Dn = (e, t) => {
2114
+ }) => /* @__PURE__ */ new Set([...Ze, ...Qe(L(e).map(t))]), Ln = (e, t) => {
1752
2115
  let n = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1753
2116
  return new RegExp(String.raw`(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*=\s*define[A-Za-z]*Event\s*\(\s*\{[^}]*?name\s*:\s*['"\`]${n}['"\`]`, "s").exec(e)?.[1];
1754
2117
  }, Z = async (e) => {
@@ -1767,22 +2130,22 @@ var le = /* @__PURE__ */ new Set([
1767
2130
  }, a = L(e).filter((e) => !r.has(e)).map(i).join("\n");
1768
2131
  return n.filter(({ name: e, file: t }) => {
1769
2132
  if (a.includes(e)) return !1;
1770
- let n = Dn(i(t), e);
2133
+ let n = Ln(i(t), e);
1771
2134
  return n === void 0 || !new RegExp(String.raw`\b${n}\b`).test(a);
1772
2135
  }).map((e) => e.name).sort();
1773
2136
  } catch {
1774
2137
  return [];
1775
2138
  }
1776
- }, On = async (e) => {
2139
+ }, Rn = async (e) => {
1777
2140
  let t = await Z(e);
1778
2141
  if (t.length !== 0) {
1779
2142
  console.log(""), console.log(`• ${t.length} declared event(s) with no emit call site found:`);
1780
2143
  for (let e of t) console.log(` ${e}`);
1781
2144
  console.log(""), console.log(" An event you declare is an event partners can subscribe to. One that"), console.log(" never fires is indistinguishable, from their side, from a broken"), console.log(" endpoint of their own — so they debug your contract instead of"), console.log(" reading it."), console.log(" ADVISORY: this looks for the event name AND the identifier its"), console.log(" declaration binds it to, so `emit(ctx, apiKeyCreated, …)` counts. An"), console.log(" emit through an ASSEMBLED name still reads as missing here.");
1782
2145
  }
1783
- }, kn = async (e) => {
2146
+ }, zn = async (e) => {
1784
2147
  try {
1785
- let { loadApiConfig: t } = await import("./dev-B9Gz0k85.js"), n = (await t(e))?.doctor?.scopeVocabulary;
2148
+ let { loadApiConfig: t } = await import("./dev-Dff_pdik.js"), n = (await t(e))?.doctor?.scopeVocabulary;
1786
2149
  if (typeof n != "string") return [];
1787
2150
  let [r, i] = n.split("#");
1788
2151
  if (r === void 0 || i === void 0) return [];
@@ -1799,10 +2162,10 @@ var le = /* @__PURE__ */ new Set([
1799
2162
  findings: []
1800
2163
  });
1801
2164
  try {
1802
- let { offlineManifest: n, toInput: r } = await import("./checkCommand-D0QV_zM_.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-CFmFKHb2.js"), o = await n(e), s = r(o.manifest, o.tables), c = await kn(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
2165
+ let { offlineManifest: n, toInput: r } = await import("./checkCommand-C1EbNmwl.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-CFmFKHb2.js"), o = await n(e), s = r(o.manifest, o.tables), c = await zn(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
1803
2166
  if (l.length === 0) {
1804
2167
  try {
1805
- let { loadAppSchemaInputs: t } = await import("./frameworkTableAssembly-CGNC0qr7.js");
2168
+ let { loadAppSchemaInputs: t } = await import("./frameworkTableAssembly-B96WCNJA.js");
1806
2169
  await t(e);
1807
2170
  } catch {
1808
2171
  return t("app-not-importable");
@@ -1826,7 +2189,7 @@ var le = /* @__PURE__ */ new Set([
1826
2189
  } catch {
1827
2190
  return t("app-not-importable");
1828
2191
  }
1829
- }, An = async (e) => {
2192
+ }, Bn = async (e) => {
1830
2193
  let { findings: t, evaluated: n, notEvaluated: r } = await Q(e);
1831
2194
  if (!n) {
1832
2195
  if (console.log(""), r === "app-not-importable") {
@@ -1841,7 +2204,7 @@ var le = /* @__PURE__ */ new Set([
1841
2204
  for (let e of t) console.log(` ${e.node}${e.file === void 0 ? "" : ` ${e.file}`}`), console.log(` ${e.detail}`);
1842
2205
  console.log(""), console.log(" Each of these is uncallable by every user in every role — not"), console.log(" misconfigured, DEAD — and nothing fails at boot to say so. The"), console.log(" usual cause is a typo or a rename that moved one side only."), console.log(` ${t[0].fix}`);
1843
2206
  }
1844
- }, jn = (e) => {
2207
+ }, Vn = (e) => {
1845
2208
  let t = v(e, "src");
1846
2209
  if (!S(t)) return;
1847
2210
  let n = [], r = (t, i = 0) => {
@@ -1872,12 +2235,12 @@ var le = /* @__PURE__ */ new Set([
1872
2235
  }
1873
2236
  };
1874
2237
  r(t);
1875
- let i = yt(_t(n));
2238
+ let i = bt(vt(n));
1876
2239
  if (i.length !== 0) {
1877
2240
  console.log("");
1878
2241
  for (let e of i) console.log(e);
1879
2242
  }
1880
- }, Mn = async (e) => {
2243
+ }, Hn = async (e) => {
1881
2244
  let t = ["src/locales", "locales"], n, r = [];
1882
2245
  for (let i of t) {
1883
2246
  let t = v(e, i);
@@ -1889,20 +2252,20 @@ var le = /* @__PURE__ */ new Set([
1889
2252
  if (n === void 0 || r.length === 0) return;
1890
2253
  let i = [];
1891
2254
  try {
1892
- let { loadApiConfig: t } = await import("./dev-B9Gz0k85.js");
2255
+ let { loadApiConfig: t } = await import("./dev-Dff_pdik.js");
1893
2256
  i = (await t(e))?.locales ?? [];
1894
2257
  } catch {
1895
2258
  return;
1896
2259
  }
1897
- let a = We({
2260
+ let a = Ge({
1898
2261
  catalogCodes: r,
1899
2262
  declaredLocales: i
1900
2263
  });
1901
2264
  if (a !== void 0) {
1902
2265
  console.log("");
1903
- for (let e of Ge(a, n)) console.log(e);
2266
+ for (let e of Ke(a, n)) console.log(e);
1904
2267
  }
1905
- }, Nn = async (e) => {
2268
+ }, Un = async (e) => {
1906
2269
  let t = v(e, "src");
1907
2270
  if (!S(t)) return;
1908
2271
  let n = !1, r = (e) => {
@@ -1921,9 +2284,9 @@ var le = /* @__PURE__ */ new Set([
1921
2284
  return;
1922
2285
  }
1923
2286
  a()?.reason === "binary-missing" ? (console.log("image pipeline: sharp is INSTALLED but its platform binary is missing —"), console.log(" an install that omitted optional dependencies drops the @img/* prebuilds."), console.log(" Fix: reinstall without omitting optional deps, or `pnpm add -D sharp` in this app.")) : (console.log("image pipeline: sharp is NOT installed — ?image imports serve originals (no srcSet, no blur)."), console.log(" Fix: `pnpm add -D sharp` in this app (it is an optional dependency of @voltro/cli;"), console.log(" your installer skipped optional dependencies)."));
1924
- }, Pn = async (e) => {
2287
+ }, Wn = async (e) => {
1925
2288
  try {
1926
- let { loadConfig: t } = await import("./webDev-C2dRz9s5.js"), n = (await t(e, { preferSource: !0 }))?.fonts;
2289
+ let { loadConfig: t } = await import("./webDev-Bzb8ZFus.js"), n = (await t(e, { preferSource: !0 }))?.fonts;
1927
2290
  if (n === void 0 || n.length === 0) return;
1928
2291
  let { loadFontkit: r, loadSubsetFont: i } = await import("./fontPipeline-LxIHa1vo.js"), a = [];
1929
2292
  if (await r() === null && a.push("fontkit (size-adjust fallback metrics — the CLS guard — are skipped)"), await i() === null && a.push("subset-font (declared `subsets:` ship the full files)"), console.log(""), a.length === 0) {
@@ -1933,25 +2296,25 @@ var le = /* @__PURE__ */ new Set([
1933
2296
  for (let e of a) console.log(`font pipeline: ${e}`);
1934
2297
  console.log(" Fix: reinstall without omitting optional deps, or `pnpm add -D fontkit subset-font` in this app.");
1935
2298
  } catch {}
1936
- }, Fn = async (e) => {
2299
+ }, Gn = async (e) => {
1937
2300
  let t = v(e, "src", "pages");
1938
2301
  if (!S(t)) return !1;
1939
2302
  let n = [];
1940
2303
  try {
1941
- let { walkPagesTree: e } = await import("./webDev-C2dRz9s5.js");
2304
+ let { walkPagesTree: e } = await import("./webDev-Bzb8ZFus.js");
1942
2305
  n = (await e(t)).pages.map((e) => e.pattern);
1943
2306
  } catch {
1944
2307
  return !1;
1945
2308
  }
1946
- let r = qe(e, n);
2309
+ let r = Je(e, n);
1947
2310
  if (r === void 0) return !1;
1948
2311
  console.log("");
1949
- for (let e of Je(r)) console.log(e);
2312
+ for (let e of Ye(r)) console.log(e);
1950
2313
  return r.problems.length > 0;
1951
- }, In = async (e) => {
2314
+ }, Kn = async (e) => {
1952
2315
  let { discovered: t } = await U(e), n = [];
1953
2316
  try {
1954
- let { loadApiConfig: t } = await import("./dev-B9Gz0k85.js");
2317
+ let { loadApiConfig: t } = await import("./dev-Dff_pdik.js");
1955
2318
  n = ((await t(e))?.plugins ?? []).map((e) => {
1956
2319
  let t = (e.extendSchema?.tables ?? []).map((e) => {
1957
2320
  let t = e;
@@ -1964,8 +2327,8 @@ var le = /* @__PURE__ */ new Set([
1964
2327
  plugin: e.name,
1965
2328
  tables: t,
1966
2329
  rpcTags: n,
1967
- canDisableTables: Ln.has(r),
1968
- canAlias: Rn.has(r)
2330
+ canDisableTables: qn.has(r),
2331
+ canAlias: Jn.has(r)
1969
2332
  };
1970
2333
  });
1971
2334
  } catch {
@@ -1977,18 +2340,18 @@ var le = /* @__PURE__ */ new Set([
1977
2340
  ...t.mutations,
1978
2341
  ...t.actions,
1979
2342
  ...t.streams ?? []
1980
- ].map((e) => String(e.descriptor.name)), i = ut(n, {
2343
+ ].map((e) => String(e.descriptor.name)), i = dt(n, {
1981
2344
  tables: t.tables.map((e) => e.tableName),
1982
2345
  rpcTags: r
1983
2346
  });
1984
2347
  console.log();
1985
- for (let e of dt(n, i)) console.log(e);
1986
- }, Ln = /* @__PURE__ */ new Set([
2348
+ for (let e of ft(n, i)) console.log(e);
2349
+ }, qn = /* @__PURE__ */ new Set([
1987
2350
  "@voltro/plugin-rbac",
1988
2351
  "@voltro/plugin-audit",
1989
2352
  "@voltro/plugin-notifications",
1990
2353
  "aiFlows"
1991
- ]), Rn = /* @__PURE__ */ new Set([
2354
+ ]), Jn = /* @__PURE__ */ new Set([
1992
2355
  "aiFlows",
1993
2356
  "@voltro/plugin-audit",
1994
2357
  "@voltro/plugin-billing",
@@ -2006,8 +2369,8 @@ var le = /* @__PURE__ */ new Set([
2006
2369
  "@voltro/plugin-sso-saml",
2007
2370
  "@voltro/plugin-storage",
2008
2371
  "@voltro/plugin-row-history"
2009
- ]), $ = 20, zn = async (e) => {
2010
- let t = await Bn(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;
2372
+ ]), $ = 20, Yn = async (e) => {
2373
+ let t = await Xn(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;
2011
2374
  return {
2012
2375
  executors: t.executorCount,
2013
2376
  guardVocabulary: [...t.vocabulary].sort(),
@@ -2019,7 +2382,7 @@ var le = /* @__PURE__ */ new Set([
2019
2382
  },
2020
2383
  unchecked: t.result.unchecked
2021
2384
  };
2022
- }, Bn = async (e) => {
2385
+ }, Xn = async (e) => {
2023
2386
  let { discovered: t } = await U(e), n = /* @__PURE__ */ new Map(), r = (e) => {
2024
2387
  if (!n.has(e)) try {
2025
2388
  n.set(e, C(e, "utf8"));
@@ -2027,7 +2390,7 @@ var le = /* @__PURE__ */ new Set([
2027
2390
  n.set(e, "");
2028
2391
  }
2029
2392
  return n.get(e);
2030
- }, i = En(e, r), a = /* @__PURE__ */ new Set();
2393
+ }, i = In(e, r), a = /* @__PURE__ */ new Set();
2031
2394
  for (let e of t.tables) for (let t of Object.values(e.fields ?? {})) try {
2032
2395
  let e = t.references?.();
2033
2396
  e && a.add(e.tableName);
@@ -2064,7 +2427,7 @@ var le = /* @__PURE__ */ new Set([
2064
2427
  l = !0;
2065
2428
  break;
2066
2429
  }
2067
- let u = rt({
2430
+ let u = it({
2068
2431
  executors: o,
2069
2432
  guardVocabulary: i,
2070
2433
  tenantScopedTables: t.tenantScopedTables,
@@ -2081,23 +2444,23 @@ var le = /* @__PURE__ */ new Set([
2081
2444
  executorCount: o.length,
2082
2445
  allowPath: s
2083
2446
  };
2084
- }, Vn = async (e, t) => {
2085
- let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await Bn(e), l = n.counts;
2447
+ }, Zn = async (e, t) => {
2448
+ let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await Xn(e), l = n.counts;
2086
2449
  console.log(`\nauthz scan · ${o} executor(s)`);
2087
- let u = [...r].filter((e) => !Xe.has(e)).sort();
2450
+ let u = [...r].filter((e) => !Ze.has(e)).sort();
2088
2451
  console.log(u.length === 0 ? " guard vocabulary: framework names only — no exported require*/assert* found in this app" : ` guard vocabulary: ${u.length} from your source (${u.slice(0, 6).join(", ")}${u.length > 6 ? ", …" : ""})`), console.log(` ✗ no access check ${String(l.unchecked).padStart(4)}`), console.log(` ⚠ inline ownership check, no named guard${String(l.inlineOwnership).padStart(4)} (informational)`), console.log(` ✓ guards: on the descriptor ${String(l.declaredGuard).padStart(4)}`), console.log(` ✓ calls a guard from the vocabulary ${String(l.vocabularyGuard).padStart(4)}`);
2089
2452
  let d = new Set(a.map((e) => e.tag)), f = n.findings.filter((e) => e.verdict.kind === "allowlisted" && !d.has(e.tag)).length, p = l.allowlisted - f;
2090
2453
  if (console.log(` – accepted as recorded debt ${String(f).padStart(4)} (${X})`), console.log(` ✓ reviewed, open on purpose ${String(p).padStart(4)} (\`reviewed=<why>\` in that file)`), c && console.log(" ⓘ this app registers setRowFilter — the scan CANNOT see through it. A finding below may\n already be constrained; verify the filter covers that table and record it as `reviewed=`."), t) {
2091
- let e = S(s) ? C(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = st(e, t);
2092
- return oe(s, ot(e, t), "utf8"), console.log(`\n${X}: +${r.added} new, ${r.kept} kept, ${r.removed} removed. Doctor now fails on ADDITIONS only.`), r.added === 0 && console.log(" (nothing new was unchecked — the file is unchanged.)"), !1;
2454
+ let e = S(s) ? C(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = ct(e, t);
2455
+ return oe(s, st(e, t), "utf8"), console.log(`\n${X}: +${r.added} new, ${r.kept} kept, ${r.removed} removed. Doctor now fails on ADDITIONS only.`), r.added === 0 && console.log(" (nothing new was unchecked — the file is unchanged.)"), !1;
2093
2456
  }
2094
2457
  if (n.unchecked.length === 0) return !1;
2095
2458
  console.log("");
2096
2459
  for (let e of n.unchecked.slice(0, $)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
2097
2460
  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 ${X} 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 ${X}.`), 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);
2098
- }, Hn = async (e) => {
2461
+ }, Qn = async (e) => {
2099
2462
  try {
2100
- let { loadApiConfig: t } = await import("./dev-B9Gz0k85.js"), { decidedSchemaReport: n } = await import("./doctorDecidedSchema-BlbWgCD_.js"), r = await t(e);
2463
+ let { loadApiConfig: t } = await import("./dev-Dff_pdik.js"), { decidedSchemaReport: n } = await import("./doctorDecidedSchema-BlbWgCD_.js"), r = await t(e);
2101
2464
  return r == null ? null : n({
2102
2465
  ...r.schema ? { declared: r.schema } : {},
2103
2466
  ...r.store === void 0 ? {} : { store: r.store },
@@ -2106,42 +2469,42 @@ var le = /* @__PURE__ */ new Set([
2106
2469
  } catch {
2107
2470
  return null;
2108
2471
  }
2109
- }, Un = async (e) => {
2110
- let t = await Hn(e);
2472
+ }, $n = async (e) => {
2473
+ let t = await Qn(e);
2111
2474
  if (t !== null) {
2112
2475
  console.log(""), console.log("• framework tables whose presence is DECIDED rather than implied by your source tree:");
2113
2476
  for (let e of t.text.split("\n")) console.log(` ${e}`);
2114
2477
  }
2115
- }, Wn = async (e) => {
2116
- if (e.includes("--help") || e.includes("-h")) return console.log(un), 0;
2117
- let t = e.includes("--strict"), n = e.find((e) => !e.startsWith("-")), r = b(n ?? process.cwd());
2478
+ }, er = async (e) => {
2479
+ if (e.includes("--help") || e.includes("-h")) return console.log(bn), 0;
2480
+ let t = e.includes("--strict"), n = e.find((e) => !e.startsWith("-")), a = b(n ?? process.cwd());
2118
2481
  if (g(e)) return ee(), h({
2119
- ...R(r),
2120
- spaCandidates: await z(r),
2121
- pageConvention: B(r),
2122
- fileTaxonomy: re(r).findings,
2123
- junctionFks: (await W(r))?.issues ?? [],
2124
- eventDelivery: await G(r),
2125
- duplicateInstances: a(r),
2126
- versionSkew: o(r) ?? null,
2127
- serverOnly: (await q(r)).status,
2128
- accessDecisions: await J(r),
2129
- webhookVerification: await Y(r),
2130
- authz: await zn(r),
2131
- ungrantedScopes: await Q(r),
2132
- neverEmittedEvents: await Z(r),
2133
- unimportedDeps: He(r),
2134
- stalledRuns: await Cn(r),
2135
- decidedSchema: await Hn(r)
2482
+ ...R(a),
2483
+ spaCandidates: await z(a),
2484
+ pageConvention: V(a),
2485
+ fileTaxonomy: re(a).findings,
2486
+ junctionFks: (await W(a))?.issues ?? [],
2487
+ eventDelivery: await G(a),
2488
+ duplicateInstances: r(a),
2489
+ versionSkew: c(a) ?? null,
2490
+ serverOnly: (await q(a)).status,
2491
+ accessDecisions: await J(a),
2492
+ webhookVerification: await Y(a),
2493
+ authz: await Yn(a),
2494
+ ungrantedScopes: await Q(a),
2495
+ neverEmittedEvents: await Z(a),
2496
+ unimportedDeps: Ue(a),
2497
+ stalledRuns: await Nn(a),
2498
+ decidedSchema: await Qn(a)
2136
2499
  }), 0;
2137
- let c = Ct(r, t);
2138
- Zt(r), Xt(r), ln(r), ne(r), await rn(r), await Tn(r), await wn(r), await fn(r), await _n(r), await gn(r), await Un(r), await vn(r), await In(r), await An(r), await On(r), console.log("");
2139
- for (let e of Ue(He(r))) console.log(e);
2140
- await Mn(r), jn(r), await Nn(r), await Pn(r);
2141
- let l = await Sn(r), u = await Vn(r, e.includes("--write-authz-allowlist")), d = await yn(r), f = await xn(r), p = await Fn(r), m = i(o(r));
2500
+ let s = wt(a, t);
2501
+ cn(a), sn(a), yn(a), ne(a), await B(a), await Fn(a), await Pn(a), await Sn(a), await Dn(a), await En(a), await $n(a), await On(a), await Kn(a), await Bn(a), await Rn(a), console.log("");
2502
+ for (let e of We(Ue(a))) console.log(e);
2503
+ await Hn(a), Vn(a), await Un(a), await Wn(a);
2504
+ let l = await Mn(a), u = await Zn(a, e.includes("--write-authz-allowlist")), d = await kn(a), f = await jn(a), p = await Gn(a), m = o(c(a));
2142
2505
  m !== void 0 && (console.log(""), console.log(m));
2143
- let _ = s(a(r));
2144
- return _ !== void 0 && console.log(`\n${_}`), l || u || d || p || f ? Math.max(c, 1) : c;
2506
+ let _ = i(r(a));
2507
+ return _ !== void 0 && console.log(`\n${_}`), l || u || d || p || f ? Math.max(s, 1) : s;
2145
2508
  };
2146
2509
  //#endregion
2147
- export { z as C, Y as E, B as S, Q as T, ln as _, zn as a, Ct as b, G as c, Wt as d, xt as f, Zt as g, Xt as h, J as i, En as l, mn as m, St as n, Z as o, W as p, Qt as r, Dn as s, un as t, R as u, rn as v, q as w, Gt as x, Wn as y };
2510
+ export { z as C, Y as D, Q as E, V as S, zt as T, yn as _, Yn as a, wt as b, G as c, $t as d, St as f, cn as g, sn as h, J as i, In as l, wn as m, Ct as n, Z as o, W as p, ln as r, Ln as s, bn as t, R as u, B as v, q as w, en as x, er as y };