@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.
- package/CHANGELOG.md +395 -0
- package/dist/apiBuild-CDE4NXNi.js +2 -0
- package/dist/{apiBuild-Bdaetr37.js → apiBuild-CkpsCqIu.js} +2 -2
- package/dist/bin.js +1 -1
- package/dist/build-51qBWfYY.js +981 -0
- package/dist/{checkCommand-D0QV_zM_.js → checkCommand-C1EbNmwl.js} +1 -1
- package/dist/{checkCommand-CAwFXrxA.js → checkCommand-C30J2F18.js} +4 -4
- package/dist/{codegenCommand-CidbQzbv.js → codegenCommand-kS3Twuc9.js} +12 -12
- package/dist/{codemodRunner-BlQPfjzA.js → codemodRunner-BKjCwGiN.js} +1127 -895
- package/dist/{commands-CWjfThXv.js → commands-DowWkYtd.js} +25 -25
- package/dist/{dashboardCommand-BekcY5Ls.js → dashboardCommand-gG1y4PFG.js} +3 -3
- package/dist/{dataCommand-2pccgbIy.js → dataCommand-hNOGKz4K.js} +7 -7
- package/dist/{dbCommand-DpK_vQET.js → dbCommand-CMAIz-Bf.js} +3 -3
- package/dist/dbCommand-DHi_RuDl.js +2 -0
- package/dist/{dev-Dw263KPu.js → dev-Ce2gEziv.js} +2411 -2391
- package/dist/dev-Dff_pdik.js +3 -0
- package/dist/{doctorCommand-aR_bFmIi.js → doctorCommand-5ULMpL10.js} +652 -289
- package/dist/doctorCommand-B8H2MYCE.js +2 -0
- package/dist/{dormancyCommand-eXTQMbHU.js → dormancyCommand-DYgI0Kf-.js} +3 -3
- package/dist/{embeddingsCommand-CTmiQvwa.js → embeddingsCommand-yJivQSrI.js} +2 -2
- package/dist/emptyResultHeadline-Csa5fZOF.js +18 -0
- package/dist/{envCommand-BDUgV7EM.js → envCommand-ORJUf8r_.js} +2 -2
- package/dist/{evolveCommand-YV8qW1LU.js → evolveCommand-DTawtTxf.js} +2 -2
- package/dist/frameworkTableAssembly-B96WCNJA.js +2 -0
- package/dist/{frameworkTableAssembly-DNOFXfEQ.js → frameworkTableAssembly-vfkzuzEo.js} +104 -85
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BjVXpMlP.js → infoCommand-BFksHunl.js} +1 -1
- package/dist/inspect-CZvsmp8t.js +2 -0
- package/dist/{inspect-CNYvNXPU.js → inspect-PcC5J-Dl.js} +74 -59
- package/dist/{logsCmd-BU8uCdys.js → logsCmd-C5y9HyrG.js} +58 -53
- package/dist/manifestBuild-AUh31bFU.js +2 -0
- package/dist/{manifestBuild-CEkjfpwc.js → manifestBuild-BwiS4iYm.js} +1 -1
- package/dist/{migrate-SICulyz1.js → migrate-DtC3lu7H.js} +4 -4
- package/dist/{probeCommand-6HxEkNDG.js → probeCommand-BUYzc0CT.js} +1 -1
- package/dist/{runtimeTrace-DgYMc09E.js → runtimeTrace-BUk5zMXY.js} +1 -1
- package/dist/{sdkgen-PY-umd6O.js → sdkgen-CAhmmbjx.js} +1 -1
- package/dist/serveCommand-BUXUtSF5.js +2 -0
- package/dist/serveCommand-DhtC7WBq.js +2545 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-BFQQkL1i.js → start-C75w6ftA.js} +1 -1
- package/dist/{start-B9NGB8gn.js → start-DNtIlBcn.js} +527 -502
- package/dist/startEntry.js +1 -1
- package/dist/{tracesCmd-BWYDqMy6.js → tracesCmd-C1qKa94g.js} +68 -63
- package/dist/{updateCommand-C9n_Z_oG.js → updateCommand-CtuD5r1t.js} +1 -1
- package/dist/updateCommand-eVKVHj3N.js +2 -0
- package/dist/webDev-Bzb8ZFus.js +2 -0
- package/dist/{webDev-C53hJdcL.js → webDev-CBN9KkyQ.js} +949 -834
- package/dist/{webhooksCommand-uuPu8qQX.js → webhooksCommand-BPJQOGTe.js} +2 -2
- package/package.json +61 -19
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/_manifest.json +2 -2
- package/templates/agent-docs/ai.md +175 -1
- package/templates/agent-docs/authentication.md +68 -19
- package/templates/agent-docs/cli.md +48 -0
- package/templates/agent-docs/configuration.md +29 -4
- package/templates/agent-docs/data.md +30 -0
- package/templates/agent-docs/database/scaling.md +10 -1
- package/templates/agent-docs/database/transactions.md +48 -0
- package/templates/agent-docs/deployment.md +22 -0
- package/templates/agent-docs/observability.md +13 -2
- package/templates/agent-docs/plugins/audit.md +21 -5
- package/templates/agent-docs/plugins/auth-supabase.md +21 -0
- package/templates/agent-docs/plugins/billing.md +2 -0
- package/templates/agent-docs/plugins/sentry.md +91 -8
- package/templates/agent-docs/reference.md +1 -0
- package/templates/agent-docs/scheduling.md +23 -0
- package/templates/agent-docs/schema-driven-ui.md +110 -0
- package/templates/agent-docs/whats-new.md +103 -399
- package/templates/apps/api-ai/package.json +6 -6
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -9
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-row-history/package.json +8 -8
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +7 -7
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +7 -7
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-auth/package.json +7 -7
- package/templates/apps/frontend-blank/package.json +6 -6
- package/templates/apps/frontend-cms/package.json +8 -8
- package/templates/apps/frontend-collab/package.json +9 -9
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +6 -6
- package/templates/apps/frontend-docs/package.json +8 -8
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +7 -7
- package/templates/apps/frontend-saas/package.json +7 -7
- package/templates/apps/frontend-spa/package.json +6 -6
- package/templates/apps/frontend-ssr/package.json +6 -6
- package/templates/apps/frontend-ssr-api/package.json +7 -7
- package/templates/apps/frontend-static-blog/package.json +8 -8
- package/templates/apps/frontend-status/package.json +7 -7
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-Vw1figjO.js +0 -2
- package/dist/build-CI36wL4R.js +0 -868
- package/dist/dbCommand-DrycGWWt.js +0 -2
- package/dist/dev-B9Gz0k85.js +0 -3
- package/dist/doctorCommand-BMWs6aVm.js +0 -2
- package/dist/frameworkTableAssembly-CGNC0qr7.js +0 -2
- package/dist/inspect-S2rWy1Ys.js +0 -2
- package/dist/manifestBuild-DIa_s6u0.js +0 -2
- package/dist/serveCommand-BITS8Hpj.js +0 -2
- package/dist/serveCommand-DIJ3ma76.js +0 -2485
- package/dist/updateCommand-DsXEAHbd.js +0 -2
- package/dist/webDev-C2dRz9s5.js +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as d, u as f } from "./apiBuild-
|
|
4
|
-
import {
|
|
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/
|
|
15
|
-
var le =
|
|
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
|
-
]),
|
|
252
|
+
]), de = (e) => {
|
|
34
253
|
if (E.isPropertyAccessExpression(e)) return e.getName();
|
|
35
254
|
if (E.isIdentifier(e)) return e.getText();
|
|
36
|
-
},
|
|
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
|
-
},
|
|
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 =
|
|
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 || !
|
|
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,
|
|
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
|
-
},
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
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) =>
|
|
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(
|
|
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) => !
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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} (${
|
|
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
|
-
},
|
|
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 &&
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
437
|
+
}, Oe = /* @__PURE__ */ new Set([
|
|
219
438
|
"start",
|
|
220
439
|
"run",
|
|
221
440
|
"child"
|
|
222
|
-
]),
|
|
441
|
+
]), ke = (e) => E.isCallExpression(e) && (() => {
|
|
223
442
|
let t = e.getExpression();
|
|
224
|
-
if (!E.isPropertyAccessExpression(t) || !
|
|
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
|
-
})(),
|
|
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 (!
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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"),
|
|
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
|
-
},
|
|
378
|
-
if (n > 12 ||
|
|
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() ?
|
|
611
|
+
a.isDirectory() ? Be(r, t, n + 1) : Re.test(i) && t.push(r);
|
|
393
612
|
}
|
|
394
613
|
return t;
|
|
395
|
-
},
|
|
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 =
|
|
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
|
|
632
|
+
for (let e of He(t).matchAll(Ve)) s.add(e[1]);
|
|
414
633
|
}
|
|
415
|
-
let c = i.filter((e) => !(e.pkg in
|
|
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 || !
|
|
419
|
-
pendingCodegen: o ? [] : c.map((e) => e.pkg).filter((e) =>
|
|
420
|
-
exempt: i.map((e) => e.pkg).filter((e) => e in
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
|
718
|
+
let r = new D({
|
|
500
719
|
useInMemoryFileSystem: !0,
|
|
501
720
|
skipAddingFilesFromTsConfig: !0
|
|
502
|
-
}),
|
|
721
|
+
}), i;
|
|
503
722
|
try {
|
|
504
|
-
|
|
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
|
|
514
|
-
for (let e of
|
|
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
|
-
|
|
739
|
+
o.push(n);
|
|
521
740
|
continue;
|
|
522
741
|
}
|
|
523
|
-
let i =
|
|
742
|
+
let i = qe(r);
|
|
524
743
|
if (i === void 0) {
|
|
525
|
-
|
|
744
|
+
o.push(n);
|
|
526
745
|
continue;
|
|
527
746
|
}
|
|
528
|
-
|
|
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:
|
|
539
|
-
unreadable:
|
|
757
|
+
problems: s(a, t).map((e) => e.message),
|
|
758
|
+
unreadable: o,
|
|
540
759
|
seen: c,
|
|
541
760
|
file: n
|
|
542
761
|
};
|
|
543
|
-
},
|
|
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
|
-
},
|
|
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
|
-
]),
|
|
776
|
+
]), Qe = (e) => {
|
|
558
777
|
let t = /* @__PURE__ */ new Set();
|
|
559
|
-
for (let n of e) for (let e of
|
|
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
|
-
},
|
|
780
|
+
}, $e = (e, t) => {
|
|
562
781
|
for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
|
|
563
|
-
},
|
|
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
|
-
},
|
|
785
|
+
}, tt = (e, t, n) => {
|
|
567
786
|
if (e.hasDeclaredGuards) return { kind: "declared-guard" };
|
|
568
|
-
let r =
|
|
569
|
-
return i === void 0 ?
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
803
|
+
}, it = (e) => {
|
|
585
804
|
let t = e.executors.map((t) => {
|
|
586
|
-
let n =
|
|
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:
|
|
592
|
-
why:
|
|
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
|
-
},
|
|
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
|
-
],
|
|
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
|
-
...
|
|
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
|
-
},
|
|
859
|
+
}, st = (e, t) => {
|
|
641
860
|
let n = [...new Set(t)].sort();
|
|
642
|
-
if (e === void 0) return
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
|
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
|
-
},
|
|
912
|
+
}, ut = (e) => {
|
|
694
913
|
let t = e.indexOf(".");
|
|
695
914
|
return t <= 0 ? void 0 : e.slice(0, t);
|
|
696
|
-
},
|
|
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 =
|
|
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(
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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 ${
|
|
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
|
-
},
|
|
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)
|
|
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
|
-
},
|
|
797
|
-
if (!
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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 "),
|
|
830
|
-
let n =
|
|
831
|
-
if (console.log(`voltro doctor — serve preflight for ${n.root}`), !
|
|
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(` ${
|
|
837
|
-
},
|
|
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
|
-
},
|
|
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) =>
|
|
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, "\"\""),
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
907
|
-
let t = [
|
|
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\\.${
|
|
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}\\.${
|
|
1132
|
+
n === "publish" && t.push(RegExp(`^${r}$`)), n === "store" && t.push(RegExp(`^${r}\\.${jt.source.replace(/^\^|\$$/g, "")}$`));
|
|
914
1133
|
}
|
|
915
1134
|
return t;
|
|
916
|
-
},
|
|
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 || ['"`]/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 =
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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
|
-
],
|
|
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 }) =>
|
|
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 (!
|
|
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 =
|
|
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 }) => (
|
|
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 }) =>
|
|
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) =>
|
|
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) ||
|
|
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) ||
|
|
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 }) =>
|
|
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
|
|
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
|
-
],
|
|
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
|
-
},
|
|
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
|
-
|
|
1219
|
-
} else
|
|
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) :
|
|
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
|
-
},
|
|
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() ?
|
|
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
|
-
},
|
|
1259
|
-
let t =
|
|
1604
|
+
}, $t = (e) => {
|
|
1605
|
+
let t = F(e);
|
|
1260
1606
|
return {
|
|
1261
|
-
dirs:
|
|
1607
|
+
dirs: Jt(e, t),
|
|
1262
1608
|
fileCount: t.length
|
|
1263
1609
|
};
|
|
1264
|
-
},
|
|
1265
|
-
let t =
|
|
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 =
|
|
1275
|
-
for (let t of
|
|
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
|
|
1296
|
-
if (t.plugin !== void 0 &&
|
|
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
|
|
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 =
|
|
1662
|
+
let t = $t(e);
|
|
1307
1663
|
return {
|
|
1308
1664
|
root: e,
|
|
1309
1665
|
scannedFiles: t.fileCount,
|
|
1310
1666
|
scannedDirs: t.dirs,
|
|
1311
|
-
|
|
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
|
-
},
|
|
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
|
|
1682
|
+
for (let t of Zt(e)) try {
|
|
1326
1683
|
n.set(y(e, t), C(t, "utf8"));
|
|
1327
1684
|
} catch {}
|
|
1328
|
-
let r =
|
|
1685
|
+
let r = Ne({
|
|
1329
1686
|
sources: t,
|
|
1330
1687
|
envFiles: n
|
|
1331
1688
|
});
|
|
1332
|
-
r !== void 0 && (console.log(""), console.log(
|
|
1333
|
-
},
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
},
|
|
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,
|
|
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 -
|
|
1395
|
-
return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${
|
|
1396
|
-
},
|
|
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
|
-
]),
|
|
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 (
|
|
1775
|
+
if (mn.has(i.name)) continue;
|
|
1413
1776
|
let r = v(e, i.name);
|
|
1414
|
-
i.isDirectory() ?
|
|
1777
|
+
i.isDirectory() ? hn(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(y(t, r));
|
|
1415
1778
|
}
|
|
1416
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
|
|
1811
|
+
hn(r, e, i), i.sort();
|
|
1449
1812
|
let a = `${y(e, n)}${x}`;
|
|
1450
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
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,
|
|
1482
|
-
let r = t.length -
|
|
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
|
-
},
|
|
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 =
|
|
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
|
-
},
|
|
1493
|
-
let t =
|
|
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-
|
|
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
|
|
1503
|
-
},
|
|
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(
|
|
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
|
-
},
|
|
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
|
-
}),
|
|
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 =
|
|
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
|
-
},
|
|
1553
|
-
let t = await
|
|
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
|
|
1932
|
+
return ye(Cn(t.tables));
|
|
1570
1933
|
} catch {
|
|
1571
1934
|
return null;
|
|
1572
1935
|
}
|
|
1573
|
-
},
|
|
1936
|
+
}, Dn = async (e) => {
|
|
1574
1937
|
let t = await W(e);
|
|
1575
1938
|
if (t === null || t.tablesChecked === 0) return;
|
|
1576
|
-
let n =
|
|
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
|
|
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
|
-
},
|
|
1956
|
+
}, On = async (e) => {
|
|
1594
1957
|
let t = await G(e);
|
|
1595
1958
|
if (t === null || t.eventsChecked === 0) return;
|
|
1596
|
-
let n =
|
|
1959
|
+
let n = Ee(t);
|
|
1597
1960
|
n !== null && (console.log(""), console.log(`• ${n}`));
|
|
1598
|
-
let r =
|
|
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-
|
|
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:
|
|
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
|
-
},
|
|
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:"),
|
|
1652
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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-
|
|
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
|
-
}),
|
|
1720
|
-
let t =
|
|
2082
|
+
}), Pn = async (e) => {
|
|
2083
|
+
let t = ht(await Nn(e));
|
|
1721
2084
|
t !== null && (console.log(""), console.log(t));
|
|
1722
|
-
},
|
|
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 =
|
|
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 =
|
|
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",
|
|
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([...
|
|
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 =
|
|
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
|
-
},
|
|
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
|
-
},
|
|
2146
|
+
}, zn = async (e) => {
|
|
1784
2147
|
try {
|
|
1785
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
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-
|
|
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-
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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 =
|
|
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
|
-
},
|
|
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-
|
|
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 =
|
|
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
|
|
2266
|
+
for (let e of Ke(a, n)) console.log(e);
|
|
1904
2267
|
}
|
|
1905
|
-
},
|
|
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
|
-
},
|
|
2287
|
+
}, Wn = async (e) => {
|
|
1925
2288
|
try {
|
|
1926
|
-
let { loadConfig: t } = await import("./webDev-
|
|
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
|
-
},
|
|
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-
|
|
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 =
|
|
2309
|
+
let r = Je(e, n);
|
|
1947
2310
|
if (r === void 0) return !1;
|
|
1948
2311
|
console.log("");
|
|
1949
|
-
for (let e of
|
|
2312
|
+
for (let e of Ye(r)) console.log(e);
|
|
1950
2313
|
return r.problems.length > 0;
|
|
1951
|
-
},
|
|
2314
|
+
}, Kn = async (e) => {
|
|
1952
2315
|
let { discovered: t } = await U(e), n = [];
|
|
1953
2316
|
try {
|
|
1954
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
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:
|
|
1968
|
-
canAlias:
|
|
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 =
|
|
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
|
|
1986
|
-
},
|
|
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
|
-
]),
|
|
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,
|
|
2010
|
-
let t = await
|
|
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
|
-
},
|
|
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 =
|
|
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 =
|
|
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
|
-
},
|
|
2085
|
-
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await
|
|
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) => !
|
|
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 =
|
|
2092
|
-
return oe(s,
|
|
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
|
-
},
|
|
2461
|
+
}, Qn = async (e) => {
|
|
2099
2462
|
try {
|
|
2100
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
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
|
-
},
|
|
2110
|
-
let t = await
|
|
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
|
-
},
|
|
2116
|
-
if (e.includes("--help") || e.includes("-h")) return console.log(
|
|
2117
|
-
let t = e.includes("--strict"), n = e.find((e) => !e.startsWith("-")),
|
|
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(
|
|
2120
|
-
spaCandidates: await z(
|
|
2121
|
-
pageConvention:
|
|
2122
|
-
fileTaxonomy: re(
|
|
2123
|
-
junctionFks: (await W(
|
|
2124
|
-
eventDelivery: await G(
|
|
2125
|
-
duplicateInstances: a
|
|
2126
|
-
versionSkew:
|
|
2127
|
-
serverOnly: (await q(
|
|
2128
|
-
accessDecisions: await J(
|
|
2129
|
-
webhookVerification: await Y(
|
|
2130
|
-
authz: await
|
|
2131
|
-
ungrantedScopes: await Q(
|
|
2132
|
-
neverEmittedEvents: await Z(
|
|
2133
|
-
unimportedDeps:
|
|
2134
|
-
stalledRuns: await
|
|
2135
|
-
decidedSchema: await
|
|
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
|
|
2138
|
-
|
|
2139
|
-
for (let e of Ue(
|
|
2140
|
-
await
|
|
2141
|
-
let l = await
|
|
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 _ =
|
|
2144
|
-
return _ !== void 0 && console.log(`\n${_}`), l || u || d || p || f ? Math.max(
|
|
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,
|
|
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 };
|