@voltro/cli 0.51.0 → 0.53.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 +356 -0
- package/THIRD-PARTY-NOTICES.md +8311 -3318
- package/dist/{agentsMd-D6yD7IQv.js → agentsMd-0l980yhL.js} +4 -1
- package/dist/agentsMd-SDDSkyl4.js +2 -0
- package/dist/{apiBuild-CPDHXF72.js → apiBuild-CaPfoWku.js} +11 -5
- package/dist/apiBuild-DHtLXYx9.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/build-D-OnvNMf.js +843 -0
- package/dist/{checkCommand-DNuPiWMc.js → checkCommand-C5elt0tW.js} +92 -46
- package/dist/checkCommand-D2ZduVlh.js +2 -0
- package/dist/{cloudCmd-F4YJeqM3.js → cloudCmd-QUXh-b5w.js} +1 -1
- package/dist/codegen-BWpt3VgF.js +2 -0
- package/dist/{codegen-CrMXs4hb.js → codegen-FEk8AZHb.js} +2 -2
- package/dist/{codegenCommand-BNBHcNNj.js → codegenCommand-BOiWQ5hz.js} +12 -12
- package/dist/{codemodRunner-BDVixlSw.js → codemodRunner-BjtB2lq6.js} +691 -545
- package/dist/{commands-B1OiS9bX.js → commands-DyxAmhP0.js} +36 -36
- package/dist/{dashboardCommand-C-vvPY1B.js → dashboardCommand-BdKTyT13.js} +3 -3
- package/dist/{dataCommand-C1GxXW5q.js → dataCommand-Bab9X7s8.js} +27 -27
- package/dist/{dbCommand-If4Y1xQ-.js → dbCommand-06O2finM.js} +277 -236
- package/dist/dbCommand-B1EXBC6f.js +2 -0
- package/dist/{dev-kdAg9Q7l.js → dev-C6LGF4iY.js} +2998 -2379
- package/dist/dev-GjJWAYo2.js +3 -0
- package/dist/doctorCommand-B0hX0tdz.js +2 -0
- package/dist/{doctorCommand-nKmeW78u.js → doctorCommand-etMkflRc.js} +332 -220
- package/dist/{dormancyCommand-CY3wa_SW.js → dormancyCommand-UwZ1AZzB.js} +1 -1
- package/dist/{embeddingsCommand-BDLIgje_.js → embeddingsCommand-C70zWHwo.js} +1 -1
- package/dist/{envCommand-C6V_xVlT.js → envCommand-dSyKvRkM.js} +15 -15
- package/dist/{evolveCommand-D3c4DSfN.js → evolveCommand-CG0_ebO5.js} +2 -2
- package/dist/fileConventions-DASGEmj-.js +35 -0
- package/dist/{fileTaxonomy-CJfgOllU.js → fileTaxonomy-B7uxipWS.js} +55 -55
- package/dist/fontPipeline-LxIHa1vo.js +2 -0
- package/dist/fontPipeline-Tsh8kZfA.js +152 -0
- package/dist/frameworkTableAssembly-C_7Z-rMs.js +2 -0
- package/dist/{frameworkTableAssembly-BwJVEKLr.js → frameworkTableAssembly-DKx3ba3S.js} +5 -5
- package/dist/imagePipeline-B_GVJgm6.js +2 -0
- package/dist/imagePipeline-CBZmjT4i.js +127 -0
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BnRFEF1o.js → infoCommand-_53iOc_j.js} +1 -1
- package/dist/{inspect-CtL_xTbu.js → inspect-Bd8-9wsi.js} +1 -1
- package/dist/inspect-CuoDInfZ.js +2 -0
- package/dist/{inspectGateHint-BjnFubmH.js → inspectGateHint-4LxkNtrz.js} +1 -1
- package/dist/inspectMetrics-CGF94puw.js +143 -0
- package/dist/manifestBuild-C4-J1-m_.js +2 -0
- package/dist/{manifestBuild-CuU1VrSm.js → manifestBuild-Cqgsx2bM.js} +1 -1
- package/dist/{metaCommands-CfRLra0s.js → metaCommands-Cn2oboG4.js} +9 -3
- package/dist/{migrate-DehuBakM.js → migrate-Cko9rswM.js} +2 -2
- package/dist/{pageConvention-cEiRxdab.js → pageConvention-C938S8oC.js} +1 -1
- package/dist/{privacyCommand-XejDMvmu.js → privacyCommand-DWTQMC6R.js} +2 -2
- package/dist/{probeCommand-C9gazU0H.js → probeCommand-DkGGLknv.js} +83 -24
- package/dist/{projectScaffold-mIX_DpSe.js → projectScaffold-B4dmTlwT.js} +1 -1
- package/dist/{projectScaffold-BIl97_E6.js → projectScaffold-EzlErR4E.js} +1 -1
- package/dist/{renderModeScan-D7J1B7Kw.js → renderModeScan-CUbOeOAg.js} +28 -11
- package/dist/{renderProfile-1OWWAAtx.js → renderProfile-CskIgAfn.js} +2 -2
- package/dist/{runtimeTrace-ZsBU7Tkx.js → runtimeTrace-c0APJz7E.js} +1 -1
- package/dist/{sdkgen-O4XqWOjM.js → sdkgen-BiQCgIEr.js} +1 -1
- package/dist/serveCommand-CueKQgzl.js +2443 -0
- package/dist/serveCommand-DsnrVN3U.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/start-BJzZLbt8.js +3 -0
- package/dist/start-ekPan8BT.js +1510 -0
- package/dist/startEntry.js +1 -1
- package/dist/{staticCommand-Dr2M6tpU.js → staticCommand-xlSL-IWk.js} +1 -1
- package/dist/{test-rXFq4S76.js → test-BWPQcRoB.js} +1 -1
- package/dist/updateCommand-Bqql_rsQ.js +2 -0
- package/dist/{updateCommand-Bs322Q78.js → updateCommand-C_8I8Rzo.js} +139 -115
- package/dist/webDev-C7jWJ5dX.js +2 -0
- package/dist/{webDev-B-ubQEMX.js → webDev-oczpugbx.js} +1767 -913
- package/dist/{webhooksCommand-FLYY9IXh.js → webhooksCommand-4SVPDjKg.js} +1 -1
- package/package.json +72 -18
- package/templates/AGENTS.core.md +11 -0
- package/templates/AGENTS.md +19 -6
- package/templates/agent-docs/_index.md +8 -6
- package/templates/agent-docs/_manifest.json +31 -15
- package/templates/agent-docs/ai.md +2 -2
- package/templates/agent-docs/authentication.md +1 -1
- package/templates/agent-docs/cli.md +97 -15
- package/templates/agent-docs/configuration.md +17 -0
- package/templates/agent-docs/data.md +680 -33
- package/templates/agent-docs/database/advancedqueries.md +7 -7
- package/templates/agent-docs/database/columntypes.md +2 -2
- package/templates/agent-docs/database/querying.md +1 -1
- package/templates/agent-docs/database/schema.md +2 -2
- package/templates/agent-docs/database/seedsdialects.md +2 -2
- package/templates/agent-docs/database/transactions.md +3 -3
- package/templates/agent-docs/deployment.md +30 -3
- package/templates/agent-docs/internationalization.md +2 -2
- package/templates/agent-docs/introduction.md +52 -0
- package/templates/agent-docs/local-first-mobile.md +132 -7
- package/templates/agent-docs/observability.md +2 -0
- package/templates/agent-docs/plugins/ai-flows.md +1 -1
- package/templates/agent-docs/plugins/audit.md +5 -5
- package/templates/agent-docs/plugins/auth.md +1 -1
- package/templates/agent-docs/plugins/cdc-out.md +2 -2
- package/templates/agent-docs/plugins/comments.md +142 -0
- package/templates/agent-docs/plugins/notifications.md +47 -4
- package/templates/agent-docs/plugins/presence.md +16 -3
- package/templates/agent-docs/plugins/prometheus.md +1 -1
- package/templates/agent-docs/plugins/queue.md +129 -0
- package/templates/agent-docs/plugins/{versioning.md → row-history.md} +22 -22
- package/templates/agent-docs/plugins/storage.md +2 -2
- package/templates/agent-docs/plugins.md +38 -12
- package/templates/agent-docs/reference.md +54 -5
- package/templates/agent-docs/routing.md +868 -50
- package/templates/agent-docs/schema-driven-ui.md +292 -5
- package/templates/agent-docs/security.md +125 -8
- package/templates/agent-docs/templates/apibackends.md +14 -14
- package/templates/agent-docs/templates/overview.md +1 -1
- package/templates/agent-docs/whats-new.md +171 -54
- package/templates/apps/api-ai/package.json +6 -7
- package/templates/apps/api-ai/tests/summarize.test.ts +1 -1
- 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 -10
- 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-versioning → api-row-history}/README.md +3 -3
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/app.config.ts +3 -3
- package/templates/apps/{api-versioning → api-row-history}/database/schema.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/package.json +8 -8
- package/templates/apps/api-row-history/template.json +6 -0
- package/templates/apps/{api-versioning → api-row-history}/tests/documents.create.test.ts +1 -1
- package/templates/apps/api-saas/app.config.ts +1 -0
- package/templates/apps/api-saas/package.json +10 -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/app.config.ts +26 -2
- package/templates/apps/changelog/content/releases/{0.1.0.mdx → v0-1-0.mdx} +0 -1
- package/templates/apps/changelog/content/releases/{0.2.0.mdx → v0-2-0.mdx} +0 -1
- package/templates/apps/changelog/package.json +8 -8
- package/templates/apps/changelog/src/collections/releases.collection.ts +48 -0
- package/templates/apps/changelog/src/globals.d.ts +1 -1
- package/templates/apps/changelog/src/locales/de.ts +1 -1
- package/templates/apps/changelog/src/locales/en.ts +1 -1
- package/templates/apps/changelog/src/pages/[locale]/[slug]/page.tsx +7 -4
- package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +13 -41
- package/templates/apps/changelog/src/pages/[slug]/page.test.tsx +26 -51
- package/templates/apps/changelog/src/pages/[slug]/page.tsx +21 -19
- package/templates/apps/changelog/src/pages/page.test.tsx +14 -37
- package/templates/apps/changelog/src/pages/page.tsx +18 -12
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +10 -9
- package/templates/apps/frontend-collab/src/pages/page.tsx +42 -65
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/content/docs/de/guides/first-page.md +4 -0
- package/templates/apps/frontend-docs/content/docs/de/intro/getting-started.md +4 -0
- package/templates/apps/frontend-docs/content/docs/en/guides/first-page.md +4 -0
- package/templates/apps/frontend-docs/content/docs/en/intro/getting-started.md +4 -0
- package/templates/apps/frontend-docs/package.json +8 -7
- package/templates/apps/frontend-docs/src/collections/docs.collection.ts +21 -0
- package/templates/apps/frontend-docs/src/locales/de.ts +0 -5
- package/templates/apps/frontend-docs/src/locales/en.ts +0 -5
- package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug]/page.tsx +17 -9
- package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +10 -5
- package/templates/apps/frontend-docs/src/pages/[locale]/page.tsx +8 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.test.tsx +53 -15
- package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.tsx +27 -33
- package/templates/apps/frontend-docs/src/pages/page.test.tsx +17 -3
- package/templates/apps/frontend-docs/src/pages/page.tsx +16 -12
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +6 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/content/posts/cms-to-ssg.md +12 -0
- package/templates/apps/frontend-static-blog/content/posts/hello-static.md +12 -0
- package/templates/apps/frontend-static-blog/content/posts/islands-not-hydration.md +14 -0
- package/templates/apps/frontend-static-blog/package.json +8 -6
- package/templates/apps/frontend-static-blog/src/collections/posts.collection.ts +27 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug]/page.tsx +7 -4
- package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +7 -1
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.test.tsx +19 -9
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.tsx +19 -21
- package/templates/apps/frontend-static-blog/src/pages/page.test.tsx +18 -5
- package/templates/apps/frontend-static-blog/src/pages/page.tsx +21 -14
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/agentsMd-Bu_XQgVf.js +0 -2
- package/dist/apiBuild-GDKuGOMV.js +0 -2
- package/dist/build-DETLZAFt.js +0 -752
- package/dist/checkCommand-CWcnDArJ.js +0 -2
- package/dist/codegen-DiMn2KkZ.js +0 -2
- package/dist/dbCommand-C27HIsGE.js +0 -2
- package/dist/dev-CK522MV5.js +0 -3
- package/dist/doctorCommand-BK4l18eG.js +0 -2
- package/dist/fileConventions-Cof68_BL.js +0 -33
- package/dist/frameworkTableAssembly-CVDB2hCq.js +0 -2
- package/dist/inspect-CuGDYES0.js +0 -2
- package/dist/inspectMetrics-CfdKLh6t.js +0 -72
- package/dist/manifestBuild-CPjhvM62.js +0 -2
- package/dist/serveCommand-BRnPCxVd.js +0 -2
- package/dist/serveCommand-DdiYNBBu.js +0 -2362
- package/dist/start-BLNmWkLa.js +0 -1154
- package/dist/start-Dzicuyw8.js +0 -3
- package/dist/updateCommand-eXB35SEv.js +0 -2
- package/dist/webDev-DposiF3j.js +0 -2
- package/templates/apps/api-versioning/template.json +0 -6
- package/templates/apps/changelog/scripts/generate-rss.mjs +0 -38
- package/templates/apps/changelog/src/lib/releases.ts +0 -21
- package/templates/apps/frontend-static-blog/src/content/posts.ts +0 -64
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.server.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/tsconfig.json +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Ht as e, Ut as t,
|
|
2
|
-
import {
|
|
3
|
-
import { x as d } from "./frameworkTableAssembly-
|
|
4
|
-
import { n as f } from "./pageConvention-
|
|
5
|
-
import { r as p, u as m } from "./apiBuild-
|
|
1
|
+
import { Ht as e, Ut as t, Wt as n } from "./dev-C6LGF4iY.js";
|
|
2
|
+
import { Ft as r, It as i, Nt as a, P as o, Pt as s, l as c, t as l, x as u } from "./webDev-oczpugbx.js";
|
|
3
|
+
import { x as d } from "./frameworkTableAssembly-DKx3ba3S.js";
|
|
4
|
+
import { n as f } from "./pageConvention-C938S8oC.js";
|
|
5
|
+
import { r as p, u as m } from "./apiBuild-CaPfoWku.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";
|
|
8
|
-
import { n as ne, r as re } from "./fileTaxonomy-
|
|
8
|
+
import { n as ne, r as re } from "./fileTaxonomy-B7uxipWS.js";
|
|
9
9
|
import { basename as ie, dirname as ae, join as v, relative as y, resolve as b, sep as x } from "node:path";
|
|
10
10
|
import { existsSync as S, readFileSync as C, readdirSync as w, statSync as T, writeFileSync as oe } from "node:fs";
|
|
11
11
|
import { pathToFileURL as se } from "node:url";
|
|
@@ -367,14 +367,84 @@ var le = /* @__PURE__ */ new Set([
|
|
|
367
367
|
" reaches your deploy target is not knowable from here — a secret manager",
|
|
368
368
|
" or a CI variable is not a file. Naming it nowhere is conclusive; naming",
|
|
369
369
|
" it somewhere is not a guarantee."
|
|
370
|
-
].join("\n"),
|
|
370
|
+
].join("\n"), j = {
|
|
371
|
+
"@voltro/cli": "provides the `voltro` binary; consumed by scripts, never imported",
|
|
372
|
+
"@voltro/devtools": "mounted by `voltro dev` itself",
|
|
373
|
+
"@voltro/sql-postgres": "dialect driver, loaded by the framework from config",
|
|
374
|
+
"@voltro/sql-mysql": "dialect driver, loaded by the framework from config",
|
|
375
|
+
"@voltro/sql-sqlite": "dialect driver, loaded by the framework from config",
|
|
376
|
+
"@voltro/sql-mssql": "dialect driver, loaded by the framework from config"
|
|
377
|
+
}, M = /* @__PURE__ */ new Set(["@voltro/client", "@voltro/web"]), Pe = /(^|\/)(node_modules|dist|build|\.framework|\.next|\.turbo|\.git|coverage|\.publish)(\/|$)/, Fe = /\.([cm]?[jt]sx?)$/, Ie = /\.generated\.[cm]?[jt]sx?$/, Le = (e, t = [], n = 0) => {
|
|
378
|
+
if (n > 12 || Pe.test(e)) return t;
|
|
379
|
+
let r;
|
|
380
|
+
try {
|
|
381
|
+
r = w(e);
|
|
382
|
+
} catch {
|
|
383
|
+
return t;
|
|
384
|
+
}
|
|
385
|
+
for (let i of r) {
|
|
386
|
+
let r = v(e, i), a;
|
|
387
|
+
try {
|
|
388
|
+
a = T(r);
|
|
389
|
+
} catch {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
a.isDirectory() ? Le(r, t, n + 1) : Fe.test(i) && t.push(r);
|
|
393
|
+
}
|
|
394
|
+
return t;
|
|
395
|
+
}, Re = /(?:\bfrom\s*|\bimport\s+|\bimport\s*\(\s*|\brequire\s*\(\s*)['"](@voltro\/[a-z0-9-]+)(?:\/[^'"]*)?['"]/g, ze = (e) => e.split("\n").filter((e) => !/^\s*(\/\/|\*|\/\*)/.test(e)).join("\n"), Be = (e) => {
|
|
396
|
+
let t;
|
|
397
|
+
try {
|
|
398
|
+
t = JSON.parse(C(v(e, "package.json"), "utf8"));
|
|
399
|
+
} catch {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
let n = (e) => Object.keys(t[e] ?? {}).filter((e) => e.startsWith("@voltro/")).map((t) => ({
|
|
403
|
+
pkg: t,
|
|
404
|
+
declaredIn: e
|
|
405
|
+
})), r = /* @__PURE__ */ new Set(), i = [...n("dependencies"), ...n("devDependencies")].filter((e) => !r.has(e.pkg) && (r.add(e.pkg), !0)), a = Le(e), o = a.some((e) => Ie.test(e)), s = /* @__PURE__ */ new Set();
|
|
406
|
+
for (let e of a) {
|
|
407
|
+
let t;
|
|
408
|
+
try {
|
|
409
|
+
t = C(e, "utf8");
|
|
410
|
+
} catch {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
for (let e of ze(t).matchAll(Re)) s.add(e[1]);
|
|
414
|
+
}
|
|
415
|
+
let c = i.filter((e) => !(e.pkg in j) && !s.has(e.pkg));
|
|
416
|
+
return {
|
|
417
|
+
declared: i.map((e) => e.pkg).sort(),
|
|
418
|
+
unimported: c.filter((e) => o || !M.has(e.pkg)).sort((e, t) => e.pkg.localeCompare(t.pkg)),
|
|
419
|
+
pendingCodegen: o ? [] : c.map((e) => e.pkg).filter((e) => M.has(e)).sort(),
|
|
420
|
+
exempt: i.map((e) => e.pkg).filter((e) => e in j).sort(),
|
|
421
|
+
scannedFiles: a.length
|
|
422
|
+
};
|
|
423
|
+
}, Ve = (e) => {
|
|
424
|
+
if (e === null) return ["unimported deps · no package.json here — could not check"];
|
|
425
|
+
let t = `unimported deps · ${e.declared.length} @voltro/* package(s) declared, ${e.scannedFiles} file(s) scanned`;
|
|
426
|
+
if (e.declared.length === 0) return [t, " no @voltro/* dependencies declared — nothing to check"];
|
|
427
|
+
let n = e.exempt.length > 0 ? ` (${e.exempt.length} loaded by the framework itself: ${e.exempt.join(", ")})` : "", r = e.pendingCodegen.length > 0 ? [
|
|
428
|
+
` · ${e.pendingCodegen.length} not measurable yet (${e.pendingCodegen.join(", ")}) — their import`,
|
|
429
|
+
" site lives in generated code, and codegen has not run here. Run `voltro dev`",
|
|
430
|
+
" once, then re-check."
|
|
431
|
+
] : [];
|
|
432
|
+
if (e.unimported.length === 0) return [
|
|
433
|
+
t,
|
|
434
|
+
` ✓ all ${e.declared.length - e.exempt.length - e.pendingCodegen.length} measurable package(s) have an import site${n}`,
|
|
435
|
+
...r
|
|
436
|
+
];
|
|
437
|
+
let i = [t];
|
|
438
|
+
for (let t of e.unimported) i.push(` ⚠ ${t.pkg} — declared in ${t.declaredIn}, imported nowhere`);
|
|
439
|
+
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
|
+
}, He = (e) => {
|
|
371
441
|
if (e.catalogCodes.length === 0) return;
|
|
372
442
|
let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
|
|
373
443
|
if (n.length !== 0) return {
|
|
374
444
|
orphaned: n,
|
|
375
445
|
noneDeclared: t.size === 0
|
|
376
446
|
};
|
|
377
|
-
},
|
|
447
|
+
}, Ue = (e, t) => {
|
|
378
448
|
let n = e.orphaned.join(", ");
|
|
379
449
|
return e.noneDeclared ? [
|
|
380
450
|
`⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
|
|
@@ -386,7 +456,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
386
456
|
" …or delete the files. A catalog that is never loaded looks from the inside",
|
|
387
457
|
" exactly like one that works, which is why this is worth a line."
|
|
388
458
|
] : [`⚠ ${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)."];
|
|
389
|
-
},
|
|
459
|
+
}, We = (e) => {
|
|
390
460
|
let t = e.getProperty("match");
|
|
391
461
|
if (t === void 0) return null;
|
|
392
462
|
let n = (t.asKind(O.PropertyAssignment)?.getInitializer())?.asKind(O.ObjectLiteralExpression);
|
|
@@ -423,7 +493,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
423
493
|
else return;
|
|
424
494
|
}
|
|
425
495
|
return r;
|
|
426
|
-
},
|
|
496
|
+
}, Ge = (e, t) => {
|
|
427
497
|
let n = ["middleware.ts", "middleware.tsx"].find((t) => S(v(e, t)));
|
|
428
498
|
if (n === void 0) return;
|
|
429
499
|
let r = new D({
|
|
@@ -440,19 +510,19 @@ var le = /* @__PURE__ */ new Set([
|
|
|
440
510
|
file: n
|
|
441
511
|
};
|
|
442
512
|
}
|
|
443
|
-
let a = [],
|
|
513
|
+
let a = [], s = [], c = 0;
|
|
444
514
|
for (let e of i.getVariableDeclarations()) {
|
|
445
515
|
let t = e.getInitializerIfKind(O.CallExpression);
|
|
446
516
|
if (t?.getExpression().getText() !== "defineMiddleware") continue;
|
|
447
|
-
|
|
517
|
+
c += 1;
|
|
448
518
|
let n = e.getName(), r = t.getArguments()[0]?.asKind(O.ObjectLiteralExpression);
|
|
449
519
|
if (r === void 0) {
|
|
450
|
-
|
|
520
|
+
s.push(n);
|
|
451
521
|
continue;
|
|
452
522
|
}
|
|
453
|
-
let i =
|
|
523
|
+
let i = We(r);
|
|
454
524
|
if (i === void 0) {
|
|
455
|
-
|
|
525
|
+
s.push(n);
|
|
456
526
|
continue;
|
|
457
527
|
}
|
|
458
528
|
a.push({
|
|
@@ -465,45 +535,45 @@ var le = /* @__PURE__ */ new Set([
|
|
|
465
535
|
});
|
|
466
536
|
}
|
|
467
537
|
return {
|
|
468
|
-
problems:
|
|
469
|
-
unreadable:
|
|
470
|
-
seen:
|
|
538
|
+
problems: o(a, t).map((e) => e.message),
|
|
539
|
+
unreadable: s,
|
|
540
|
+
seen: c,
|
|
471
541
|
file: n
|
|
472
542
|
};
|
|
473
|
-
},
|
|
543
|
+
}, Ke = (e) => {
|
|
474
544
|
let t = [];
|
|
475
545
|
if (e.problems.length > 0) {
|
|
476
546
|
t.push(`✗ ${e.file}: ${e.problems.length} matcher problem(s) — this REFUSES the boot`);
|
|
477
547
|
for (let n of e.problems) t.push(` • ${n}`);
|
|
478
548
|
}
|
|
479
549
|
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;
|
|
480
|
-
},
|
|
550
|
+
}, qe = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), Je = /* @__PURE__ */ new Set([
|
|
481
551
|
"requireScope",
|
|
482
552
|
"requireAnyScope",
|
|
483
553
|
"requireAuth",
|
|
484
554
|
"assertScope",
|
|
485
555
|
"assertCan",
|
|
486
556
|
"can"
|
|
487
|
-
]),
|
|
557
|
+
]), Ye = (e) => {
|
|
488
558
|
let t = /* @__PURE__ */ new Set();
|
|
489
|
-
for (let n of e) for (let e of
|
|
559
|
+
for (let n of e) for (let e of qe(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
|
|
490
560
|
return t;
|
|
491
|
-
},
|
|
561
|
+
}, Xe = (e, t) => {
|
|
492
562
|
for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
|
|
493
|
-
},
|
|
563
|
+
}, Ze = (e) => {
|
|
494
564
|
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);
|
|
495
565
|
return t && n;
|
|
496
|
-
},
|
|
566
|
+
}, Qe = (e, t, n) => {
|
|
497
567
|
if (e.hasDeclaredGuards) return { kind: "declared-guard" };
|
|
498
|
-
let r =
|
|
499
|
-
return i === void 0 ?
|
|
568
|
+
let r = qe(e.src), i = Xe(r, t);
|
|
569
|
+
return i === void 0 ? Ze(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
|
|
500
570
|
kind: "vocabulary-guard",
|
|
501
571
|
name: i
|
|
502
572
|
};
|
|
503
|
-
},
|
|
573
|
+
}, $e = (e, t, n) => {
|
|
504
574
|
let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
|
|
505
575
|
return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
|
|
506
|
-
},
|
|
576
|
+
}, et = (e, t, n) => {
|
|
507
577
|
switch (t.kind) {
|
|
508
578
|
case "declared-guard": return "guards: on the descriptor";
|
|
509
579
|
case "vocabulary-guard": return `calls ${t.name}()`;
|
|
@@ -511,15 +581,15 @@ var le = /* @__PURE__ */ new Set([
|
|
|
511
581
|
case "allowlisted": return "accepted by a previous run";
|
|
512
582
|
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`;
|
|
513
583
|
}
|
|
514
|
-
},
|
|
584
|
+
}, tt = (e) => {
|
|
515
585
|
let t = e.executors.map((t) => {
|
|
516
|
-
let n =
|
|
586
|
+
let n = Qe(t, e.guardVocabulary, e.allowlist);
|
|
517
587
|
return {
|
|
518
588
|
tag: t.tag,
|
|
519
589
|
path: t.path,
|
|
520
590
|
verdict: n,
|
|
521
|
-
severity:
|
|
522
|
-
why:
|
|
591
|
+
severity: $e(t, e.tenantScopedTables, e.referencedTables),
|
|
592
|
+
why: et(t, n, e.rowFilterRegistered === !0)
|
|
523
593
|
};
|
|
524
594
|
}), n = (e) => t.filter((t) => t.verdict.kind === e).length;
|
|
525
595
|
return {
|
|
@@ -533,7 +603,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
533
603
|
},
|
|
534
604
|
unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
|
|
535
605
|
};
|
|
536
|
-
},
|
|
606
|
+
}, nt = [
|
|
537
607
|
"# voltro doctor — authz allowlist",
|
|
538
608
|
"#",
|
|
539
609
|
"# Two KINDS of line, and the difference is the point:",
|
|
@@ -558,19 +628,19 @@ var le = /* @__PURE__ */ new Set([
|
|
|
558
628
|
"# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
|
|
559
629
|
"# lines for executors that are newly unchecked.",
|
|
560
630
|
""
|
|
561
|
-
],
|
|
631
|
+
], rt = (e) => {
|
|
562
632
|
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));
|
|
563
633
|
return [
|
|
564
|
-
...
|
|
634
|
+
...nt,
|
|
565
635
|
...n,
|
|
566
636
|
...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
|
|
567
637
|
...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
|
|
568
638
|
""
|
|
569
639
|
].join("\n");
|
|
570
|
-
},
|
|
640
|
+
}, it = (e, t) => {
|
|
571
641
|
let n = [...new Set(t)].sort();
|
|
572
|
-
if (e === void 0) return
|
|
573
|
-
let r =
|
|
642
|
+
if (e === void 0) return rt(n.map((e) => ({ tag: e })));
|
|
643
|
+
let r = N(e).tags, i = n.filter((e) => !r.has(e));
|
|
574
644
|
return i.length === 0 ? e : [
|
|
575
645
|
e.replace(/\n+$/, ""),
|
|
576
646
|
"",
|
|
@@ -579,25 +649,25 @@ var le = /* @__PURE__ */ new Set([
|
|
|
579
649
|
...i,
|
|
580
650
|
""
|
|
581
651
|
].join("\n");
|
|
582
|
-
},
|
|
652
|
+
}, at = (e, t) => {
|
|
583
653
|
let n = [...new Set(t)];
|
|
584
654
|
if (e === void 0) return {
|
|
585
655
|
added: n.length,
|
|
586
656
|
kept: 0,
|
|
587
657
|
removed: 0
|
|
588
658
|
};
|
|
589
|
-
let r =
|
|
659
|
+
let r = N(e).tags;
|
|
590
660
|
return {
|
|
591
661
|
added: n.filter((e) => !r.has(e)).length,
|
|
592
662
|
kept: r.size,
|
|
593
663
|
removed: 0
|
|
594
664
|
};
|
|
595
|
-
},
|
|
665
|
+
}, ot = class extends Error {
|
|
596
666
|
tag;
|
|
597
667
|
constructor(e) {
|
|
598
668
|
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";
|
|
599
669
|
}
|
|
600
|
-
},
|
|
670
|
+
}, N = (e) => {
|
|
601
671
|
let t = [];
|
|
602
672
|
for (let n of e.split("\n")) {
|
|
603
673
|
let e = n.trim();
|
|
@@ -610,7 +680,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
610
680
|
continue;
|
|
611
681
|
}
|
|
612
682
|
let a = r[2].trim();
|
|
613
|
-
if (a.length === 0) throw new
|
|
683
|
+
if (a.length === 0) throw new ot(i);
|
|
614
684
|
t.push({
|
|
615
685
|
tag: i,
|
|
616
686
|
reviewed: a
|
|
@@ -620,13 +690,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
620
690
|
tags: new Set(t.map((e) => e.tag)),
|
|
621
691
|
entries: t
|
|
622
692
|
};
|
|
623
|
-
},
|
|
693
|
+
}, st = (e) => {
|
|
624
694
|
let t = e.indexOf(".");
|
|
625
695
|
return t <= 0 ? void 0 : e.slice(0, t);
|
|
626
|
-
},
|
|
696
|
+
}, ct = (e, t) => {
|
|
627
697
|
let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
|
|
628
698
|
for (let e of t.rpcTags) {
|
|
629
|
-
let t =
|
|
699
|
+
let t = st(e);
|
|
630
700
|
if (t === void 0) continue;
|
|
631
701
|
let n = i.get(t);
|
|
632
702
|
n ? n.push(e) : i.set(t, [e]);
|
|
@@ -655,7 +725,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
655
725
|
});
|
|
656
726
|
continue;
|
|
657
727
|
}
|
|
658
|
-
let s = new Set(o.rpcTags.map(
|
|
728
|
+
let s = new Set(o.rpcTags.map(st).filter((e) => e !== void 0));
|
|
659
729
|
for (let e of s) {
|
|
660
730
|
let t = i.get(e);
|
|
661
731
|
t !== void 0 && a.push({
|
|
@@ -668,12 +738,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
668
738
|
}
|
|
669
739
|
}
|
|
670
740
|
return a;
|
|
671
|
-
},
|
|
741
|
+
}, lt = (e, t) => {
|
|
672
742
|
let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
|
|
673
743
|
if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
|
|
674
744
|
for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
|
|
675
745
|
return r;
|
|
676
|
-
},
|
|
746
|
+
}, ut = async (e) => {
|
|
677
747
|
if (!await e.hasWorkflows().catch(() => !1)) return { kind: "not-applicable" };
|
|
678
748
|
let t;
|
|
679
749
|
try {
|
|
@@ -697,12 +767,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
697
767
|
} finally {
|
|
698
768
|
await t.close().catch(() => {});
|
|
699
769
|
}
|
|
700
|
-
},
|
|
770
|
+
}, dt = (e) => {
|
|
701
771
|
let t = Math.round(e / 6e4);
|
|
702
772
|
if (t < 90) return `${t}m`;
|
|
703
773
|
let n = Math.round(t / 60);
|
|
704
774
|
return n < 48 ? `${n}h` : `${Math.round(n / 24)}d`;
|
|
705
|
-
},
|
|
775
|
+
}, ft = (e) => {
|
|
706
776
|
if (e.kind === "not-applicable") return null;
|
|
707
777
|
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.
|
|
708
778
|
Run it where the app's DB env vars are set, or ignore this line locally.`;
|
|
@@ -710,13 +780,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
710
780
|
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` : ""})`);
|
|
711
781
|
else {
|
|
712
782
|
n.push(`✗ stuck runs: ${t.stalled.length} run(s) have made no progress`);
|
|
713
|
-
for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${
|
|
783
|
+
for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${dt(e.idleMs)} ${e.reason}`);
|
|
714
784
|
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.");
|
|
715
785
|
}
|
|
716
786
|
t.sawFullRunPage && n.push(" (the examined page filled up — more old runs exist than were read this pass)");
|
|
717
787
|
for (let e of t.failures) n.push(` ! could not examine ${e.subject}: ${e.detail}`);
|
|
718
788
|
return n.join("\n");
|
|
719
|
-
},
|
|
789
|
+
}, pt = (e) => S(b(e, "app.config.ts")) || S(b(e, "app.config.js")), mt = (e) => {
|
|
720
790
|
let t = m(e), n = p(e);
|
|
721
791
|
return {
|
|
722
792
|
root: e,
|
|
@@ -725,22 +795,22 @@ var le = /* @__PURE__ */ new Set([
|
|
|
725
795
|
apiBundle: n,
|
|
726
796
|
apiBundlePresent: S(n)
|
|
727
797
|
};
|
|
728
|
-
},
|
|
798
|
+
}, ht = [
|
|
729
799
|
"Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
|
|
730
800
|
"Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
|
|
731
801
|
"(Generated Dockerfiles from `voltro create-project` already do this.)"
|
|
732
|
-
].join("\n "),
|
|
733
|
-
let n =
|
|
734
|
-
if (console.log(`voltro doctor — serve preflight for ${n.root}`), !
|
|
802
|
+
].join("\n "), gt = (e, t = !1) => {
|
|
803
|
+
let n = mt(e);
|
|
804
|
+
if (console.log(`voltro doctor — serve preflight for ${n.root}`), !pt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
|
|
735
805
|
console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
|
|
736
806
|
let r = process.env.NODE_ENV === "production";
|
|
737
807
|
if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
|
|
738
808
|
let i = t || r;
|
|
739
|
-
return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${
|
|
740
|
-
},
|
|
809
|
+
return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${ht}`), +!!i;
|
|
810
|
+
}, _t = (e) => {
|
|
741
811
|
let t = e.absPath.replace(/\.server\.(tsx?)$/, ".$1");
|
|
742
812
|
if (t !== e.absPath) return e.sources.get(t) ?? e.sources.get(t.replace(/\.ts$/, ".tsx"));
|
|
743
|
-
},
|
|
813
|
+
}, vt = (e, t, n) => {
|
|
744
814
|
let r = new Set(e), i = _(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
|
|
745
815
|
`${e}.ts`,
|
|
746
816
|
`${e}.tsx`,
|
|
@@ -766,7 +836,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
766
836
|
r !== void 0 && o.push(r);
|
|
767
837
|
}
|
|
768
838
|
return o;
|
|
769
|
-
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) =>
|
|
839
|
+
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) => St.test(y(n, e))), l = new Set(c);
|
|
770
840
|
for (; c.length > 0;) {
|
|
771
841
|
let e = c.shift();
|
|
772
842
|
if (!s.has(e)) {
|
|
@@ -779,11 +849,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
779
849
|
let d = /* @__PURE__ */ new Set();
|
|
780
850
|
for (let e of s) l.has(e) || u.has(e) || d.add(e);
|
|
781
851
|
return d;
|
|
782
|
-
},
|
|
852
|
+
}, P = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), F = (e, t) => (e.match(t) ?? []).length, I = (e, t) => e.getDescendantsOfKind(O.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, yt = (e, t) => {
|
|
783
853
|
if (e.src.includes(t)) return !0;
|
|
784
854
|
let n = t.replace(/^@voltro\/plugin-/, "");
|
|
785
|
-
return n !== t &&
|
|
786
|
-
},
|
|
855
|
+
return n !== t && I(e.file, RegExp(`^${n}Plugin$`)) > 0;
|
|
856
|
+
}, bt = (e, t) => {
|
|
787
857
|
let n = [], r = RegExp(`\\b${t}\\s*(?:<[^()]*>)?\\s*\\(`, "g"), i;
|
|
788
858
|
for (; (i = r.exec(e)) !== null;) {
|
|
789
859
|
let t = i.index + i[0].length, r = 1, a = t;
|
|
@@ -791,10 +861,10 @@ var le = /* @__PURE__ */ new Set([
|
|
|
791
861
|
n.push(e.slice(t, a - 1));
|
|
792
862
|
}
|
|
793
863
|
return n;
|
|
794
|
-
},
|
|
864
|
+
}, xt = (e, t) => {
|
|
795
865
|
let n = t.getSymbol()?.getDeclarations()?.[0];
|
|
796
866
|
return n === void 0 ? [] : e.getDescendantsOfKind(O.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
|
|
797
|
-
},
|
|
867
|
+
}, St = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, Ct = /\.(query|mutation|action|stream)\.server\.tsx?$/, wt = [
|
|
798
868
|
"Id",
|
|
799
869
|
"ID",
|
|
800
870
|
"Hash",
|
|
@@ -820,7 +890,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
820
890
|
"Comment",
|
|
821
891
|
"Provider",
|
|
822
892
|
"Format"
|
|
823
|
-
],
|
|
893
|
+
], Tt = RegExp(`(?:${wt.join("|")})$|_(?:${wt.map((e) => e.toLowerCase()).join("|")})$`), Et = [
|
|
824
894
|
{
|
|
825
895
|
id: "hardcoded-framework-cookie",
|
|
826
896
|
scope: "client",
|
|
@@ -833,7 +903,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
833
903
|
scope: "server",
|
|
834
904
|
smell: "an executor that never names its own descriptor — its input type is hand-written, so it can drift from the contract it implements",
|
|
835
905
|
use: "import the descriptor and type the input as `ExecutorInput<typeof descriptor>`, so the wire contract and the handler cannot disagree",
|
|
836
|
-
match: ({ src: e, path: t }) =>
|
|
906
|
+
match: ({ src: e, path: t }) => Ct.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
|
|
837
907
|
},
|
|
838
908
|
{
|
|
839
909
|
id: "subject-write-no-guard",
|
|
@@ -842,12 +912,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
842
912
|
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",
|
|
843
913
|
match: (e) => {
|
|
844
914
|
let { src: t, path: n } = e;
|
|
845
|
-
if (!
|
|
846
|
-
let r =
|
|
915
|
+
if (!Ct.test(n)) return !1;
|
|
916
|
+
let r = P(t);
|
|
847
917
|
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;
|
|
848
|
-
let i =
|
|
918
|
+
let i = _t(e);
|
|
849
919
|
if (i === void 0) return !1;
|
|
850
|
-
let a =
|
|
920
|
+
let a = P(i);
|
|
851
921
|
return /\binternal\s*:\s*true\b/.test(a) || /\bguards\s*:\s*\[/.test(a) || /\.guard\s*\(/.test(a) ? !1 : /\bopenAccess\s*:/.test(a);
|
|
852
922
|
}
|
|
853
923
|
},
|
|
@@ -856,14 +926,14 @@ var le = /* @__PURE__ */ new Set([
|
|
|
856
926
|
scope: "server",
|
|
857
927
|
smell: "raw fetch() for outbound HTTP on the server (no traceparent, no retry, and no SSRF guard where the URL is not a constant)",
|
|
858
928
|
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",
|
|
859
|
-
match: ({ file: e, path: t, serverOnly: n }) => (
|
|
929
|
+
match: ({ file: e, path: t, serverOnly: n }) => (St.test(t) || n) && I(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
|
|
860
930
|
},
|
|
861
931
|
{
|
|
862
932
|
id: "form",
|
|
863
933
|
scope: "client",
|
|
864
934
|
smell: "per-field useState + a submit flag (hand-rolled form)",
|
|
865
935
|
use: "useFormBinding — fields + validation from the mutation input Schema; server ValidationError({ field }) routes to the field",
|
|
866
|
-
match: ({ src: e, file: t }) =>
|
|
936
|
+
match: ({ src: e, file: t }) => I(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
|
|
867
937
|
},
|
|
868
938
|
{
|
|
869
939
|
id: "table",
|
|
@@ -891,7 +961,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
891
961
|
scope: "client",
|
|
892
962
|
smell: "useMemo fanning in multiple subscriptions (hand-maintained dep array)",
|
|
893
963
|
use: "useDerived — dependency-tracked, referentially stable",
|
|
894
|
-
match: ({ src: e }) =>
|
|
964
|
+
match: ({ src: e }) => F(e, /\buseSubscription\s*[(<]/g) >= 3 && F(e, /\buseMemo\s*\(/g) >= 3
|
|
895
965
|
},
|
|
896
966
|
{
|
|
897
967
|
id: "next-shim",
|
|
@@ -900,6 +970,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
900
970
|
use: "the native @voltro/web exports (useSearchParams, redirect, notFound, useServerRequest, …)",
|
|
901
971
|
match: ({ src: e }) => /from\s+['"][^'"]*next-(compat|shims)/.test(e)
|
|
902
972
|
},
|
|
973
|
+
{
|
|
974
|
+
id: "untyped-search-params",
|
|
975
|
+
scope: "client",
|
|
976
|
+
smell: "a page exporting a `searchParams` schema but reading the query untyped (zero-arg useSearchParams)",
|
|
977
|
+
use: "useSearchParams(searchParams) — the schema this page already declares, decoded with defaults applied",
|
|
978
|
+
match: (e) => /^export const searchParams\b/m.test(e.src) && e.file.getDescendantsOfKind(O.CallExpression).some((e) => e.getExpression().getText() === "useSearchParams" && e.getArguments().length === 0)
|
|
979
|
+
},
|
|
903
980
|
{
|
|
904
981
|
id: "presence",
|
|
905
982
|
scope: "client",
|
|
@@ -916,7 +993,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
916
993
|
match: ({ file: e }) => e.getDescendantsOfKind(O.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
|
|
917
994
|
let t = e.getFirstAncestorByKind(O.VariableDeclaration)?.getNameNode();
|
|
918
995
|
return t === void 0 ? [] : E.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
|
|
919
|
-
}).some((t) =>
|
|
996
|
+
}).some((t) => xt(e, t).some((e) => {
|
|
920
997
|
let t = e.getParent();
|
|
921
998
|
if (E.isBinaryExpression(t)) {
|
|
922
999
|
let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
|
|
@@ -944,7 +1021,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
944
1021
|
scope: "server",
|
|
945
1022
|
smell: "several sequential store reads assembling related data (an N+1 you are writing by hand)",
|
|
946
1023
|
use: "reads are a parent→child walk on ONE key → relations() in a *.relations.ts + .with({ … }) (ONE JSON-aggregate query, every dialect); reads collect ids from SEVERAL sources → keep the assembly and run the independent leading reads under Effect.all (same queries, same results, only concurrent — zero parity risk)",
|
|
947
|
-
match: (e) =>
|
|
1024
|
+
match: (e) => I(e.file, /\bstore\.query$/) >= 3 || I(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
|
|
948
1025
|
},
|
|
949
1026
|
{
|
|
950
1027
|
id: "store-effect-lift",
|
|
@@ -967,7 +1044,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
967
1044
|
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)",
|
|
968
1045
|
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) => {
|
|
969
1046
|
let n = t[1];
|
|
970
|
-
return !/token|secret|credential|password|apikey|accesskey/i.test(n) ||
|
|
1047
|
+
return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Tt.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
|
|
971
1048
|
}))
|
|
972
1049
|
},
|
|
973
1050
|
{
|
|
@@ -977,7 +1054,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
977
1054
|
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",
|
|
978
1055
|
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) => {
|
|
979
1056
|
let n = t[1];
|
|
980
|
-
return !/token|secret|credential|password|apikey|accesskey/i.test(n) ||
|
|
1057
|
+
return !/token|secret|credential|password|apikey|accesskey/i.test(n) || Tt.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
|
|
981
1058
|
}))
|
|
982
1059
|
},
|
|
983
1060
|
{
|
|
@@ -1041,9 +1118,9 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1041
1118
|
scope: "server",
|
|
1042
1119
|
smell: "setRowFilter is registered, but no test passes `rowFilter:` to makeTestContext",
|
|
1043
1120
|
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",
|
|
1044
|
-
match: ({ file: e, testSources: t }) =>
|
|
1045
|
-
let t =
|
|
1046
|
-
return
|
|
1121
|
+
match: ({ file: e, testSources: t }) => I(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
|
|
1122
|
+
let t = P(e);
|
|
1123
|
+
return bt(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
|
|
1047
1124
|
})
|
|
1048
1125
|
},
|
|
1049
1126
|
{
|
|
@@ -1053,15 +1130,15 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1053
1130
|
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",
|
|
1054
1131
|
match: ({ src: e }) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
|
|
1055
1132
|
}
|
|
1056
|
-
],
|
|
1133
|
+
], Dt = (e) => R(e), Ot = (e, t) => {
|
|
1057
1134
|
let n = /* @__PURE__ */ new Set();
|
|
1058
1135
|
for (let r of t) {
|
|
1059
1136
|
let t = y(e, r), i = t.split(/[\\/]/)[0];
|
|
1060
1137
|
n.add(i !== void 0 && i !== t ? i : ".");
|
|
1061
1138
|
}
|
|
1062
1139
|
return [...n].sort();
|
|
1063
|
-
},
|
|
1064
|
-
if (n > 12 ||
|
|
1140
|
+
}, kt = /\.(tsx?|jsx?)$/, L = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, At = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, jt = (e, t = [], n = 0) => {
|
|
1141
|
+
if (n > 12 || L.test(e)) return t;
|
|
1065
1142
|
let r;
|
|
1066
1143
|
try {
|
|
1067
1144
|
r = w(e, { withFileTypes: !0 });
|
|
@@ -1072,12 +1149,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1072
1149
|
let r = v(e, i.name);
|
|
1073
1150
|
if (i.isDirectory()) {
|
|
1074
1151
|
if (i.name.startsWith(".")) continue;
|
|
1075
|
-
|
|
1152
|
+
jt(r, t, n + 1);
|
|
1076
1153
|
} else je.test(i.name) && t.push(r);
|
|
1077
1154
|
}
|
|
1078
1155
|
return t;
|
|
1079
|
-
},
|
|
1080
|
-
if (n > 12 ||
|
|
1156
|
+
}, R = (e, t = [], n = 0) => {
|
|
1157
|
+
if (n > 12 || L.test(e)) return t;
|
|
1081
1158
|
let r;
|
|
1082
1159
|
try {
|
|
1083
1160
|
r = w(e);
|
|
@@ -1091,11 +1168,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1091
1168
|
} catch {
|
|
1092
1169
|
continue;
|
|
1093
1170
|
}
|
|
1094
|
-
a.isDirectory() ?
|
|
1171
|
+
a.isDirectory() ? R(r, t, n + 1) : kt.test(i) && !/\.(test|spec)\./.test(i) && !At.test(i) && t.push(r);
|
|
1095
1172
|
}
|
|
1096
1173
|
return t;
|
|
1097
|
-
},
|
|
1098
|
-
if (n > 12 ||
|
|
1174
|
+
}, Mt = (e, t = [], n = 0) => {
|
|
1175
|
+
if (n > 12 || L.test(e)) return t;
|
|
1099
1176
|
let r;
|
|
1100
1177
|
try {
|
|
1101
1178
|
r = w(e);
|
|
@@ -1109,17 +1186,17 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1109
1186
|
} catch {
|
|
1110
1187
|
continue;
|
|
1111
1188
|
}
|
|
1112
|
-
a.isDirectory() ?
|
|
1189
|
+
a.isDirectory() ? Mt(r, t, n + 1) : kt.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
|
|
1113
1190
|
}
|
|
1114
1191
|
return t;
|
|
1115
|
-
},
|
|
1116
|
-
let t =
|
|
1192
|
+
}, Nt = (e) => {
|
|
1193
|
+
let t = Dt(e);
|
|
1117
1194
|
return {
|
|
1118
|
-
dirs:
|
|
1195
|
+
dirs: Ot(e, t),
|
|
1119
1196
|
fileCount: t.length
|
|
1120
1197
|
};
|
|
1121
|
-
},
|
|
1122
|
-
let t =
|
|
1198
|
+
}, Pt = (e) => {
|
|
1199
|
+
let t = Dt(e);
|
|
1123
1200
|
if (t.length === 0) return [];
|
|
1124
1201
|
let n = new D({
|
|
1125
1202
|
useInMemoryFileSystem: !0,
|
|
@@ -1128,8 +1205,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1128
1205
|
for (let e of t) try {
|
|
1129
1206
|
i.set(e, C(e, "utf8"));
|
|
1130
1207
|
} catch {}
|
|
1131
|
-
let a =
|
|
1132
|
-
for (let t of
|
|
1208
|
+
let a = vt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
|
|
1209
|
+
for (let t of Mt(e)) try {
|
|
1133
1210
|
o.set(t, C(t, "utf8"));
|
|
1134
1211
|
} catch {}
|
|
1135
1212
|
for (let s of t) {
|
|
@@ -1149,23 +1226,23 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1149
1226
|
} catch {
|
|
1150
1227
|
continue;
|
|
1151
1228
|
}
|
|
1152
|
-
for (let t of
|
|
1153
|
-
if (t.plugin !== void 0 &&
|
|
1229
|
+
for (let t of Et) {
|
|
1230
|
+
if (t.plugin !== void 0 && yt(c, t.plugin) || !t.match(c)) continue;
|
|
1154
1231
|
let n = r.get(t.id) ?? [];
|
|
1155
1232
|
n.push(y(e, s)), r.set(t.id, n);
|
|
1156
1233
|
}
|
|
1157
1234
|
}
|
|
1158
|
-
return
|
|
1235
|
+
return Et.filter((e) => r.has(e.id)).map((e) => ({
|
|
1159
1236
|
rule: e,
|
|
1160
1237
|
files: r.get(e.id)
|
|
1161
1238
|
})).sort((e, t) => t.files.length - e.files.length || e.rule.id.localeCompare(t.rule.id));
|
|
1162
|
-
},
|
|
1163
|
-
let t =
|
|
1239
|
+
}, z = (e) => {
|
|
1240
|
+
let t = Nt(e);
|
|
1164
1241
|
return {
|
|
1165
1242
|
root: e,
|
|
1166
1243
|
scannedFiles: t.fileCount,
|
|
1167
1244
|
scannedDirs: t.dirs,
|
|
1168
|
-
findings:
|
|
1245
|
+
findings: Pt(e).map(({ rule: e, files: t }) => ({
|
|
1169
1246
|
id: e.id,
|
|
1170
1247
|
scope: e.scope,
|
|
1171
1248
|
smell: e.smell,
|
|
@@ -1173,13 +1250,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1173
1250
|
files: t
|
|
1174
1251
|
}))
|
|
1175
1252
|
};
|
|
1176
|
-
},
|
|
1253
|
+
}, Ft = 3, It = .2, Lt = 50, Rt = (e, t) => e >= Lt && t > 0 && e / t >= It, zt = (e) => {
|
|
1177
1254
|
let t = /* @__PURE__ */ new Map();
|
|
1178
|
-
for (let n of
|
|
1255
|
+
for (let n of R(e)) try {
|
|
1179
1256
|
t.set(y(e, n), C(n, "utf8"));
|
|
1180
1257
|
} catch {}
|
|
1181
1258
|
let n = /* @__PURE__ */ new Map();
|
|
1182
|
-
for (let t of
|
|
1259
|
+
for (let t of jt(e)) try {
|
|
1183
1260
|
n.set(y(e, t), C(t, "utf8"));
|
|
1184
1261
|
} catch {}
|
|
1185
1262
|
let r = Me({
|
|
@@ -1187,8 +1264,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1187
1264
|
envFiles: n
|
|
1188
1265
|
});
|
|
1189
1266
|
r !== void 0 && (console.log(""), console.log(Ne(r)));
|
|
1190
|
-
},
|
|
1191
|
-
let t =
|
|
1267
|
+
}, Bt = (e) => {
|
|
1268
|
+
let t = z(e);
|
|
1192
1269
|
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;
|
|
1193
1270
|
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;
|
|
1194
1271
|
console.log(`\u2022 Shipped primitives you may be hand-rolling (scanned ${t.scannedFiles} files across ${t.scannedDirs.join(", ")}):`);
|
|
@@ -1199,58 +1276,58 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1199
1276
|
console.log(` [${e}]`);
|
|
1200
1277
|
let i = [...r].sort((e, t) => e.files.length - t.files.length);
|
|
1201
1278
|
for (let e of i) {
|
|
1202
|
-
if (
|
|
1279
|
+
if (Rt(e.files.length, t.scannedFiles)) {
|
|
1203
1280
|
let n = Math.round(e.files.length / t.scannedFiles * 100);
|
|
1204
1281
|
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.)");
|
|
1205
1282
|
continue;
|
|
1206
1283
|
}
|
|
1207
|
-
let r = e.files.slice(0,
|
|
1284
|
+
let r = e.files.slice(0, Ft).join(", "), i = e.files.length - Ft;
|
|
1208
1285
|
i > 0 && (n += i);
|
|
1209
1286
|
let a = i > 0 ? `, +${i} more` : "";
|
|
1210
1287
|
console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${r}${a}`), console.log(` \u2192 ${e.use}`);
|
|
1211
1288
|
}
|
|
1212
1289
|
}
|
|
1213
1290
|
return n > 0 && console.log(` (${n} more file path${n === 1 ? "" : "s"} not shown \u2014 \`voltro doctor --json\` prints every one.)`), console.log(" (See the \"Pick the SERVER primitive\" / \"Pick the CLIENT primitive\" rubrics in AGENTS.md / CLAUDE.md.)"), t.findings.length;
|
|
1214
|
-
},
|
|
1215
|
-
let t =
|
|
1291
|
+
}, Vt = "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.", Ht = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, Ut = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, Wt = (e) => {
|
|
1292
|
+
let t = Ht.exec(e)?.[1];
|
|
1216
1293
|
if (t !== void 0) return t === "ssr" ? "ssr" : null;
|
|
1217
|
-
let n =
|
|
1294
|
+
let n = Ut.exec(e)?.[1];
|
|
1218
1295
|
return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
|
|
1219
1296
|
}, B = async (e) => {
|
|
1220
1297
|
let t = v(e, "src", "pages");
|
|
1221
1298
|
if (!S(t)) return [];
|
|
1222
1299
|
let { pages: n, dirs: r } = await u(t), i = [];
|
|
1223
1300
|
for (let a of n) {
|
|
1224
|
-
if (!
|
|
1301
|
+
if (!c(r, a.dirChain).some((e) => e.hasLayout)) continue;
|
|
1225
1302
|
let n;
|
|
1226
1303
|
try {
|
|
1227
1304
|
n = C(v(t, a.file), "utf8");
|
|
1228
1305
|
} catch {
|
|
1229
1306
|
continue;
|
|
1230
1307
|
}
|
|
1231
|
-
if (
|
|
1232
|
-
let
|
|
1233
|
-
|
|
1308
|
+
if (l.test(n)) continue;
|
|
1309
|
+
let o = Wt(n);
|
|
1310
|
+
o !== null && i.push({
|
|
1234
1311
|
file: y(e, v(t, a.file)),
|
|
1235
1312
|
pattern: a.pattern,
|
|
1236
|
-
currentMode:
|
|
1313
|
+
currentMode: o
|
|
1237
1314
|
});
|
|
1238
1315
|
}
|
|
1239
1316
|
return i;
|
|
1240
|
-
},
|
|
1317
|
+
}, Gt = 10, Kt = async (e) => {
|
|
1241
1318
|
console.log("");
|
|
1242
1319
|
let t = v(e, "src", "pages");
|
|
1243
1320
|
if (!S(t)) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
1244
1321
|
let n = await B(e);
|
|
1245
1322
|
if (n.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
|
|
1246
1323
|
console.log(`• renderMode:'spa' candidates (${n.length} page${n.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
|
|
1247
|
-
for (let e of n.slice(0,
|
|
1324
|
+
for (let e of n.slice(0, Gt)) {
|
|
1248
1325
|
let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
|
|
1249
1326
|
console.log(` ${e.file} (${e.pattern}) — ${t}`);
|
|
1250
1327
|
}
|
|
1251
|
-
let r = n.length -
|
|
1252
|
-
return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${
|
|
1253
|
-
},
|
|
1328
|
+
let r = n.length - Gt;
|
|
1329
|
+
return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${Vt}`), n.length;
|
|
1330
|
+
}, qt = /* @__PURE__ */ new Set([
|
|
1254
1331
|
"node_modules",
|
|
1255
1332
|
"dist",
|
|
1256
1333
|
"build",
|
|
@@ -1258,7 +1335,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1258
1335
|
".vite",
|
|
1259
1336
|
".framework",
|
|
1260
1337
|
".git"
|
|
1261
|
-
]),
|
|
1338
|
+
]), Jt = (e, t, n) => {
|
|
1262
1339
|
let r;
|
|
1263
1340
|
try {
|
|
1264
1341
|
r = w(e, { withFileTypes: !0 });
|
|
@@ -1266,11 +1343,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1266
1343
|
return;
|
|
1267
1344
|
}
|
|
1268
1345
|
for (let i of r) {
|
|
1269
|
-
if (
|
|
1346
|
+
if (qt.has(i.name)) continue;
|
|
1270
1347
|
let r = v(e, i.name);
|
|
1271
|
-
i.isDirectory() ?
|
|
1348
|
+
i.isDirectory() ? Jt(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(y(t, r));
|
|
1272
1349
|
}
|
|
1273
|
-
},
|
|
1350
|
+
}, Yt = (e, t) => {
|
|
1274
1351
|
let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
|
|
1275
1352
|
for (let i of t) {
|
|
1276
1353
|
let t;
|
|
@@ -1295,16 +1372,16 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1295
1372
|
}
|
|
1296
1373
|
}
|
|
1297
1374
|
return n;
|
|
1298
|
-
},
|
|
1375
|
+
}, Xt = (e, t) => {
|
|
1299
1376
|
let n = e.slice(t.length).split(x).slice(0, -1)[0];
|
|
1300
1377
|
return n !== void 0 && n.startsWith("[");
|
|
1301
1378
|
}, V = (e) => {
|
|
1302
1379
|
let t = [], n = v(e, "src", "pages"), r = v(e, "src");
|
|
1303
1380
|
if (!S(r)) return t;
|
|
1304
1381
|
let i = [];
|
|
1305
|
-
|
|
1382
|
+
Jt(r, e, i), i.sort();
|
|
1306
1383
|
let a = `${y(e, n)}${x}`;
|
|
1307
|
-
|
|
1384
|
+
Yt(e, i);
|
|
1308
1385
|
for (let n of i) {
|
|
1309
1386
|
let r = n.slice(n.lastIndexOf(x) + 1);
|
|
1310
1387
|
if (n.startsWith(a)) {
|
|
@@ -1320,7 +1397,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1320
1397
|
file: n
|
|
1321
1398
|
});
|
|
1322
1399
|
} catch {}
|
|
1323
|
-
|
|
1400
|
+
Xt(n, a) && t.push({
|
|
1324
1401
|
kind: "top-level-dynamic-route",
|
|
1325
1402
|
file: n
|
|
1326
1403
|
});
|
|
@@ -1332,13 +1409,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1332
1409
|
});
|
|
1333
1410
|
}
|
|
1334
1411
|
return t;
|
|
1335
|
-
},
|
|
1412
|
+
}, Zt = 10, H = (e, t, n) => {
|
|
1336
1413
|
if (t.length === 0) return;
|
|
1337
1414
|
console.log(`• ${e} (${t.length}):`);
|
|
1338
|
-
for (let e of t.slice(0,
|
|
1339
|
-
let r = t.length -
|
|
1415
|
+
for (let e of t.slice(0, Zt)) console.log(` ${e}`);
|
|
1416
|
+
let r = t.length - Zt;
|
|
1340
1417
|
r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
|
|
1341
|
-
},
|
|
1418
|
+
}, Qt = (e) => {
|
|
1342
1419
|
console.log("");
|
|
1343
1420
|
let t = v(e, "src", "pages");
|
|
1344
1421
|
if (!S(t)) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
@@ -1346,18 +1423,18 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1346
1423
|
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;
|
|
1347
1424
|
let r = (e) => n.filter((t) => t.kind === e).map((e) => e.file);
|
|
1348
1425
|
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;
|
|
1349
|
-
},
|
|
1350
|
-
let t =
|
|
1426
|
+
}, $t = "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.", en = /* @__PURE__ */ new Map(), U = (e) => {
|
|
1427
|
+
let t = en.get(e);
|
|
1351
1428
|
if (t !== void 0) return t;
|
|
1352
1429
|
let n = (async () => {
|
|
1353
|
-
let { walk: t, loadDiscovered: n } = await import("./dev-
|
|
1430
|
+
let { walk: t, loadDiscovered: n } = await import("./dev-GjJWAYo2.js"), r = await t(e);
|
|
1354
1431
|
return {
|
|
1355
1432
|
files: r,
|
|
1356
1433
|
discovered: await n(r)
|
|
1357
1434
|
};
|
|
1358
1435
|
})();
|
|
1359
|
-
return
|
|
1360
|
-
},
|
|
1436
|
+
return en.set(e, n), n;
|
|
1437
|
+
}, tn = async (e) => {
|
|
1361
1438
|
let t;
|
|
1362
1439
|
try {
|
|
1363
1440
|
let { files: n, discovered: r } = await U(e), i = r.tables.map((e) => {
|
|
@@ -1373,7 +1450,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1373
1450
|
return;
|
|
1374
1451
|
}
|
|
1375
1452
|
t !== null && (console.log(""), console.log(`${t.includes("does not exist") ? "✗" : "✓"} ${t}`));
|
|
1376
|
-
},
|
|
1453
|
+
}, nn = (e) => e.map((e) => {
|
|
1377
1454
|
let t = e, n = Object.entries(t.fields ?? {}).map(([e, t]) => ({
|
|
1378
1455
|
name: e,
|
|
1379
1456
|
type: typeof t?.type == "string" ? t.type : ""
|
|
@@ -1383,13 +1460,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1383
1460
|
columns: n,
|
|
1384
1461
|
primaryKey: t.appliedPrimaryKey ?? []
|
|
1385
1462
|
};
|
|
1386
|
-
}),
|
|
1463
|
+
}), rn = async (e) => {
|
|
1387
1464
|
try {
|
|
1388
1465
|
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 = [];
|
|
1389
1466
|
for (let e of i.queries) {
|
|
1390
1467
|
let i = n(e.descriptor.source);
|
|
1391
1468
|
if (i.length === 0) continue;
|
|
1392
|
-
let s =
|
|
1469
|
+
let s = an(e.file);
|
|
1393
1470
|
if (s === void 0) continue;
|
|
1394
1471
|
let c = await r(s, "utf8").catch(() => null);
|
|
1395
1472
|
if (c === null) continue;
|
|
@@ -1406,8 +1483,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1406
1483
|
} catch {
|
|
1407
1484
|
return null;
|
|
1408
1485
|
}
|
|
1409
|
-
},
|
|
1410
|
-
let t = await
|
|
1486
|
+
}, an = (e) => e.endsWith(".ts") && !e.endsWith(".server.ts") ? `${e.slice(0, -3)}.server.ts` : void 0, on = async (e) => {
|
|
1487
|
+
let t = await rn(e);
|
|
1411
1488
|
if (t === null) return;
|
|
1412
1489
|
let n = t.findings;
|
|
1413
1490
|
if (n.length > 0) {
|
|
@@ -1423,11 +1500,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1423
1500
|
}, W = async (e) => {
|
|
1424
1501
|
try {
|
|
1425
1502
|
let { discovered: t } = await U(e);
|
|
1426
|
-
return ve(
|
|
1503
|
+
return ve(nn(t.tables));
|
|
1427
1504
|
} catch {
|
|
1428
1505
|
return null;
|
|
1429
1506
|
}
|
|
1430
|
-
},
|
|
1507
|
+
}, sn = async (e) => {
|
|
1431
1508
|
let t = await W(e);
|
|
1432
1509
|
if (t === null || t.tablesChecked === 0) return;
|
|
1433
1510
|
let n = be(t);
|
|
@@ -1447,7 +1524,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1447
1524
|
} catch {
|
|
1448
1525
|
return null;
|
|
1449
1526
|
}
|
|
1450
|
-
},
|
|
1527
|
+
}, cn = async (e) => {
|
|
1451
1528
|
let t = await G(e);
|
|
1452
1529
|
if (t === null || t.eventsChecked === 0) return;
|
|
1453
1530
|
let n = Te(t);
|
|
@@ -1475,7 +1552,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1475
1552
|
}
|
|
1476
1553
|
}, J = async (r) => {
|
|
1477
1554
|
try {
|
|
1478
|
-
let { loadApiConfig: i } = await import("./dev-
|
|
1555
|
+
let { loadApiConfig: i } = await import("./dev-GjJWAYo2.js"), { discovered: a } = await U(r), o = t(await i(r)), s = {
|
|
1479
1556
|
queries: a.queries,
|
|
1480
1557
|
mutations: a.mutations,
|
|
1481
1558
|
actions: a.actions,
|
|
@@ -1485,8 +1562,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1485
1562
|
return {
|
|
1486
1563
|
checked: !0,
|
|
1487
1564
|
defaultDeny: o,
|
|
1488
|
-
undecided:
|
|
1489
|
-
openAccess:
|
|
1565
|
+
undecided: n(s),
|
|
1566
|
+
openAccess: e(s)
|
|
1490
1567
|
};
|
|
1491
1568
|
} catch (e) {
|
|
1492
1569
|
return {
|
|
@@ -1497,7 +1574,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1497
1574
|
openAccess: []
|
|
1498
1575
|
};
|
|
1499
1576
|
}
|
|
1500
|
-
},
|
|
1577
|
+
}, ln = async (e) => {
|
|
1501
1578
|
let t = await J(e);
|
|
1502
1579
|
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;
|
|
1503
1580
|
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)}`);
|
|
@@ -1505,8 +1582,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1505
1582
|
if (t.undecided.length === 0) return !1;
|
|
1506
1583
|
console.log("");
|
|
1507
1584
|
for (let e of t.undecided) console.log(` ✗ ${e.tag} (${e.kind})${e.file === void 0 ? "" : `\n ${e.file}`}`);
|
|
1508
|
-
return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"),
|
|
1509
|
-
},
|
|
1585
|
+
return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"), un(), !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."), un(), !1);
|
|
1586
|
+
}, un = () => {
|
|
1510
1587
|
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");
|
|
1511
1588
|
}, Y = async (e) => {
|
|
1512
1589
|
try {
|
|
@@ -1543,7 +1620,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1543
1620
|
verified: 0
|
|
1544
1621
|
};
|
|
1545
1622
|
}
|
|
1546
|
-
},
|
|
1623
|
+
}, dn = async (e) => {
|
|
1547
1624
|
let t = await Y(e);
|
|
1548
1625
|
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;
|
|
1549
1626
|
if (t.verified + t.open.length + t.unverified.length === 0) return !1;
|
|
@@ -1553,13 +1630,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1553
1630
|
console.log("");
|
|
1554
1631
|
for (let e of t.unverified) console.log(` ✗ ${e.id}\n ${e.file}`);
|
|
1555
1632
|
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;
|
|
1556
|
-
},
|
|
1633
|
+
}, fn = async (e) => {
|
|
1557
1634
|
let { status: t, message: n } = await q(e);
|
|
1558
1635
|
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);
|
|
1559
|
-
},
|
|
1636
|
+
}, pn = async (e) => ut({
|
|
1560
1637
|
hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
|
|
1561
1638
|
openStore: async () => {
|
|
1562
|
-
let { loadApiConfig: t, buildStore: n } = await import("./dev-
|
|
1639
|
+
let { loadApiConfig: t, buildStore: n } = await import("./dev-GjJWAYo2.js"), r = await t(e);
|
|
1563
1640
|
if (r === null) throw Error("no app.config.ts in this directory");
|
|
1564
1641
|
let i = await n(r, void 0);
|
|
1565
1642
|
return {
|
|
@@ -1573,10 +1650,10 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1573
1650
|
let { sweepStalledRuns: t } = await import("@voltro/workflow");
|
|
1574
1651
|
return t({ store: e });
|
|
1575
1652
|
}
|
|
1576
|
-
}),
|
|
1577
|
-
let t =
|
|
1653
|
+
}), mn = async (e) => {
|
|
1654
|
+
let t = ft(await pn(e));
|
|
1578
1655
|
t !== null && (console.log(""), console.log(t));
|
|
1579
|
-
},
|
|
1656
|
+
}, hn = async (e) => {
|
|
1580
1657
|
let t;
|
|
1581
1658
|
try {
|
|
1582
1659
|
let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await U(e);
|
|
@@ -1599,13 +1676,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1599
1676
|
console.log("");
|
|
1600
1677
|
let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
|
|
1601
1678
|
console.log(`${r} ${n}`);
|
|
1602
|
-
}, X = "voltro-authz-allowlist.txt",
|
|
1679
|
+
}, X = "voltro-authz-allowlist.txt", gn = (e, t = (e) => {
|
|
1603
1680
|
try {
|
|
1604
1681
|
return C(e, "utf8");
|
|
1605
1682
|
} catch {
|
|
1606
1683
|
return "";
|
|
1607
1684
|
}
|
|
1608
|
-
}) => /* @__PURE__ */ new Set([...
|
|
1685
|
+
}) => /* @__PURE__ */ new Set([...Je, ...Ye(R(e).map(t))]), _n = (e, t) => {
|
|
1609
1686
|
let n = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1610
1687
|
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];
|
|
1611
1688
|
}, Z = async (e) => {
|
|
@@ -1621,25 +1698,25 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1621
1698
|
} catch {
|
|
1622
1699
|
return "";
|
|
1623
1700
|
}
|
|
1624
|
-
}, a =
|
|
1701
|
+
}, a = R(e).filter((e) => !r.has(e)).map(i).join("\n");
|
|
1625
1702
|
return n.filter(({ name: e, file: t }) => {
|
|
1626
1703
|
if (a.includes(e)) return !1;
|
|
1627
|
-
let n =
|
|
1704
|
+
let n = _n(i(t), e);
|
|
1628
1705
|
return n === void 0 || !new RegExp(String.raw`\b${n}\b`).test(a);
|
|
1629
1706
|
}).map((e) => e.name).sort();
|
|
1630
1707
|
} catch {
|
|
1631
1708
|
return [];
|
|
1632
1709
|
}
|
|
1633
|
-
},
|
|
1710
|
+
}, vn = async (e) => {
|
|
1634
1711
|
let t = await Z(e);
|
|
1635
1712
|
if (t.length !== 0) {
|
|
1636
1713
|
console.log(""), console.log(`• ${t.length} declared event(s) with no emit call site found:`);
|
|
1637
1714
|
for (let e of t) console.log(` ${e}`);
|
|
1638
1715
|
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.");
|
|
1639
1716
|
}
|
|
1640
|
-
},
|
|
1717
|
+
}, yn = async (e) => {
|
|
1641
1718
|
try {
|
|
1642
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1719
|
+
let { loadApiConfig: t } = await import("./dev-GjJWAYo2.js"), n = (await t(e))?.doctor?.scopeVocabulary;
|
|
1643
1720
|
if (typeof n != "string") return [];
|
|
1644
1721
|
let [r, i] = n.split("#");
|
|
1645
1722
|
if (r === void 0 || i === void 0) return [];
|
|
@@ -1656,10 +1733,10 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1656
1733
|
findings: []
|
|
1657
1734
|
});
|
|
1658
1735
|
try {
|
|
1659
|
-
let { offlineManifest: n, toInput: r } = await import("./checkCommand-
|
|
1736
|
+
let { offlineManifest: n, toInput: r } = await import("./checkCommand-D2ZduVlh.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-CFmFKHb2.js"), o = await n(e), s = r(o.manifest, o.tables), c = await yn(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
|
|
1660
1737
|
if (l.length === 0) {
|
|
1661
1738
|
try {
|
|
1662
|
-
let { loadAppSchemaInputs: t } = await import("./frameworkTableAssembly-
|
|
1739
|
+
let { loadAppSchemaInputs: t } = await import("./frameworkTableAssembly-C_7Z-rMs.js");
|
|
1663
1740
|
await t(e);
|
|
1664
1741
|
} catch {
|
|
1665
1742
|
return t("app-not-importable");
|
|
@@ -1683,7 +1760,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1683
1760
|
} catch {
|
|
1684
1761
|
return t("app-not-importable");
|
|
1685
1762
|
}
|
|
1686
|
-
},
|
|
1763
|
+
}, bn = async (e) => {
|
|
1687
1764
|
let { findings: t, evaluated: n, notEvaluated: r } = await Q(e);
|
|
1688
1765
|
if (!n) {
|
|
1689
1766
|
if (console.log(""), r === "app-not-importable") {
|
|
@@ -1698,7 +1775,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1698
1775
|
for (let e of t) console.log(` ${e.node}${e.file === void 0 ? "" : ` ${e.file}`}`), console.log(` ${e.detail}`);
|
|
1699
1776
|
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}`);
|
|
1700
1777
|
}
|
|
1701
|
-
},
|
|
1778
|
+
}, xn = async (e) => {
|
|
1702
1779
|
let t = ["src/locales", "locales"], n, r = [];
|
|
1703
1780
|
for (let i of t) {
|
|
1704
1781
|
let t = v(e, i);
|
|
@@ -1710,38 +1787,69 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1710
1787
|
if (n === void 0 || r.length === 0) return;
|
|
1711
1788
|
let i = [];
|
|
1712
1789
|
try {
|
|
1713
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1790
|
+
let { loadApiConfig: t } = await import("./dev-GjJWAYo2.js");
|
|
1714
1791
|
i = (await t(e))?.locales ?? [];
|
|
1715
1792
|
} catch {
|
|
1716
1793
|
return;
|
|
1717
1794
|
}
|
|
1718
|
-
let a =
|
|
1795
|
+
let a = He({
|
|
1719
1796
|
catalogCodes: r,
|
|
1720
1797
|
declaredLocales: i
|
|
1721
1798
|
});
|
|
1722
1799
|
if (a !== void 0) {
|
|
1723
1800
|
console.log("");
|
|
1724
|
-
for (let e of
|
|
1801
|
+
for (let e of Ue(a, n)) console.log(e);
|
|
1802
|
+
}
|
|
1803
|
+
}, Sn = async (e) => {
|
|
1804
|
+
let t = v(e, "src");
|
|
1805
|
+
if (!S(t)) return;
|
|
1806
|
+
let n = !1, r = (e) => {
|
|
1807
|
+
for (let t of w(e, { withFileTypes: !0 })) {
|
|
1808
|
+
if (n) return;
|
|
1809
|
+
if (t.isDirectory()) t.name.startsWith(".") || r(v(e, t.name));
|
|
1810
|
+
else if (/\.(tsx?|jsx?|mdx?)$/.test(t.name)) try {
|
|
1811
|
+
C(v(e, t.name), "utf8").includes("?image") && (n = !0);
|
|
1812
|
+
} catch {}
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
if (r(t), !n) return;
|
|
1816
|
+
let { loadSharp: i, sharpUnavailableReason: a } = await import("./imagePipeline-B_GVJgm6.js"), o = await i();
|
|
1817
|
+
if (console.log(""), o !== null) {
|
|
1818
|
+
console.log("image pipeline: sharp is available — ?image imports get srcSet variants + blur.");
|
|
1819
|
+
return;
|
|
1725
1820
|
}
|
|
1726
|
-
|
|
1821
|
+
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)."));
|
|
1822
|
+
}, Cn = async (e) => {
|
|
1823
|
+
try {
|
|
1824
|
+
let { loadConfig: t } = await import("./webDev-C7jWJ5dX.js"), n = (await t(e, { preferSource: !0 }))?.fonts;
|
|
1825
|
+
if (n === void 0 || n.length === 0) return;
|
|
1826
|
+
let { loadFontkit: r, loadSubsetFont: i } = await import("./fontPipeline-LxIHa1vo.js"), a = [];
|
|
1827
|
+
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) {
|
|
1828
|
+
console.log("font pipeline: fontkit + subset-font available — declared fonts get metrics + subsetting.");
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
for (let e of a) console.log(`font pipeline: ${e}`);
|
|
1832
|
+
console.log(" Fix: reinstall without omitting optional deps, or `pnpm add -D fontkit subset-font` in this app.");
|
|
1833
|
+
} catch {}
|
|
1834
|
+
}, wn = async (e) => {
|
|
1727
1835
|
let t = v(e, "src", "pages");
|
|
1728
1836
|
if (!S(t)) return !1;
|
|
1729
1837
|
let n = [];
|
|
1730
1838
|
try {
|
|
1731
|
-
let { walkPagesTree: e } = await import("./webDev-
|
|
1839
|
+
let { walkPagesTree: e } = await import("./webDev-C7jWJ5dX.js");
|
|
1732
1840
|
n = (await e(t)).pages.map((e) => e.pattern);
|
|
1733
1841
|
} catch {
|
|
1734
1842
|
return !1;
|
|
1735
1843
|
}
|
|
1736
|
-
let r =
|
|
1844
|
+
let r = Ge(e, n);
|
|
1737
1845
|
if (r === void 0) return !1;
|
|
1738
1846
|
console.log("");
|
|
1739
|
-
for (let e of
|
|
1847
|
+
for (let e of Ke(r)) console.log(e);
|
|
1740
1848
|
return r.problems.length > 0;
|
|
1741
|
-
},
|
|
1849
|
+
}, Tn = async (e) => {
|
|
1742
1850
|
let { discovered: t } = await U(e), n = [];
|
|
1743
1851
|
try {
|
|
1744
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1852
|
+
let { loadApiConfig: t } = await import("./dev-GjJWAYo2.js");
|
|
1745
1853
|
n = ((await t(e))?.plugins ?? []).map((e) => {
|
|
1746
1854
|
let t = (e.extendSchema?.tables ?? []).map((e) => {
|
|
1747
1855
|
let t = e;
|
|
@@ -1754,8 +1862,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1754
1862
|
plugin: e.name,
|
|
1755
1863
|
tables: t,
|
|
1756
1864
|
rpcTags: n,
|
|
1757
|
-
canDisableTables:
|
|
1758
|
-
canAlias:
|
|
1865
|
+
canDisableTables: En.has(r),
|
|
1866
|
+
canAlias: Dn.has(r)
|
|
1759
1867
|
};
|
|
1760
1868
|
});
|
|
1761
1869
|
} catch {
|
|
@@ -1767,22 +1875,23 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1767
1875
|
...t.mutations,
|
|
1768
1876
|
...t.actions,
|
|
1769
1877
|
...t.streams ?? []
|
|
1770
|
-
].map((e) => String(e.descriptor.name)), i =
|
|
1878
|
+
].map((e) => String(e.descriptor.name)), i = ct(n, {
|
|
1771
1879
|
tables: t.tables.map((e) => e.tableName),
|
|
1772
1880
|
rpcTags: r
|
|
1773
1881
|
});
|
|
1774
1882
|
console.log();
|
|
1775
|
-
for (let e of
|
|
1776
|
-
},
|
|
1883
|
+
for (let e of lt(n, i)) console.log(e);
|
|
1884
|
+
}, En = /* @__PURE__ */ new Set([
|
|
1777
1885
|
"@voltro/plugin-rbac",
|
|
1778
1886
|
"@voltro/plugin-audit",
|
|
1779
1887
|
"@voltro/plugin-notifications",
|
|
1780
1888
|
"aiFlows"
|
|
1781
|
-
]),
|
|
1889
|
+
]), Dn = /* @__PURE__ */ new Set([
|
|
1782
1890
|
"aiFlows",
|
|
1783
1891
|
"@voltro/plugin-audit",
|
|
1784
1892
|
"@voltro/plugin-billing",
|
|
1785
1893
|
"@voltro/plugin-cdc-out",
|
|
1894
|
+
"@voltro/plugin-comments",
|
|
1786
1895
|
"@voltro/plugin-flags",
|
|
1787
1896
|
"@voltro/plugin-governance",
|
|
1788
1897
|
"@voltro/plugin-mail",
|
|
@@ -1794,9 +1903,9 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1794
1903
|
"@voltro/plugin-search",
|
|
1795
1904
|
"@voltro/plugin-sso-saml",
|
|
1796
1905
|
"@voltro/plugin-storage",
|
|
1797
|
-
"@voltro/plugin-
|
|
1798
|
-
]), $ = 20,
|
|
1799
|
-
let t = await
|
|
1906
|
+
"@voltro/plugin-row-history"
|
|
1907
|
+
]), $ = 20, On = async (e) => {
|
|
1908
|
+
let t = await kn(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;
|
|
1800
1909
|
return {
|
|
1801
1910
|
executors: t.executorCount,
|
|
1802
1911
|
guardVocabulary: [...t.vocabulary].sort(),
|
|
@@ -1808,7 +1917,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1808
1917
|
},
|
|
1809
1918
|
unchecked: t.result.unchecked
|
|
1810
1919
|
};
|
|
1811
|
-
},
|
|
1920
|
+
}, kn = async (e) => {
|
|
1812
1921
|
let { discovered: t } = await U(e), n = /* @__PURE__ */ new Map(), r = (e) => {
|
|
1813
1922
|
if (!n.has(e)) try {
|
|
1814
1923
|
n.set(e, C(e, "utf8"));
|
|
@@ -1816,7 +1925,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1816
1925
|
n.set(e, "");
|
|
1817
1926
|
}
|
|
1818
1927
|
return n.get(e);
|
|
1819
|
-
}, i =
|
|
1928
|
+
}, i = gn(e, r), a = /* @__PURE__ */ new Set();
|
|
1820
1929
|
for (let e of t.tables) for (let t of Object.values(e.fields ?? {})) try {
|
|
1821
1930
|
let e = t.references?.();
|
|
1822
1931
|
e && a.add(e.tableName);
|
|
@@ -1848,12 +1957,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1848
1957
|
op: a?.op === "delete" || a?.op === "insert" ? a.op : t,
|
|
1849
1958
|
...a?.table === void 0 ? {} : { table: a.table }
|
|
1850
1959
|
};
|
|
1851
|
-
})), s = v(e, X), c = S(s) ?
|
|
1852
|
-
for (let t of
|
|
1960
|
+
})), s = v(e, X), c = S(s) ? N(C(s, "utf8")) : void 0, l = !1;
|
|
1961
|
+
for (let t of R(e)) if (/\bsetRowFilter\s*[<(]/.test(r(t))) {
|
|
1853
1962
|
l = !0;
|
|
1854
1963
|
break;
|
|
1855
1964
|
}
|
|
1856
|
-
let u =
|
|
1965
|
+
let u = tt({
|
|
1857
1966
|
executors: o,
|
|
1858
1967
|
guardVocabulary: i,
|
|
1859
1968
|
tenantScopedTables: t.tenantScopedTables,
|
|
@@ -1870,23 +1979,23 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1870
1979
|
executorCount: o.length,
|
|
1871
1980
|
allowPath: s
|
|
1872
1981
|
};
|
|
1873
|
-
},
|
|
1874
|
-
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await
|
|
1982
|
+
}, An = async (e, t) => {
|
|
1983
|
+
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await kn(e), l = n.counts;
|
|
1875
1984
|
console.log(`\nauthz scan · ${o} executor(s)`);
|
|
1876
|
-
let u = [...r].filter((e) => !
|
|
1985
|
+
let u = [...r].filter((e) => !Je.has(e)).sort();
|
|
1877
1986
|
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)}`);
|
|
1878
1987
|
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;
|
|
1879
1988
|
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) {
|
|
1880
|
-
let e = S(s) ? C(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r =
|
|
1881
|
-
return oe(s,
|
|
1989
|
+
let e = S(s) ? C(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = at(e, t);
|
|
1990
|
+
return oe(s, it(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;
|
|
1882
1991
|
}
|
|
1883
1992
|
if (n.unchecked.length === 0) return !1;
|
|
1884
1993
|
console.log("");
|
|
1885
1994
|
for (let e of n.unchecked.slice(0, $)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
|
|
1886
1995
|
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);
|
|
1887
|
-
},
|
|
1996
|
+
}, jn = async (e) => {
|
|
1888
1997
|
try {
|
|
1889
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1998
|
+
let { loadApiConfig: t } = await import("./dev-GjJWAYo2.js"), { decidedSchemaReport: n } = await import("./doctorDecidedSchema-BlbWgCD_.js"), r = await t(e);
|
|
1890
1999
|
return r == null ? null : n({
|
|
1891
2000
|
...r.schema ? { declared: r.schema } : {},
|
|
1892
2001
|
...r.store === void 0 ? {} : { store: r.store },
|
|
@@ -1895,39 +2004,42 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1895
2004
|
} catch {
|
|
1896
2005
|
return null;
|
|
1897
2006
|
}
|
|
1898
|
-
},
|
|
1899
|
-
let t = await
|
|
2007
|
+
}, Mn = async (e) => {
|
|
2008
|
+
let t = await jn(e);
|
|
1900
2009
|
if (t !== null) {
|
|
1901
2010
|
console.log(""), console.log("• framework tables whose presence is DECIDED rather than implied by your source tree:");
|
|
1902
2011
|
for (let e of t.text.split("\n")) console.log(` ${e}`);
|
|
1903
2012
|
}
|
|
1904
|
-
},
|
|
1905
|
-
if (e.includes("--help") || e.includes("-h")) return console.log(
|
|
2013
|
+
}, Nn = async (e) => {
|
|
2014
|
+
if (e.includes("--help") || e.includes("-h")) return console.log($t), 0;
|
|
1906
2015
|
let t = e.includes("--strict"), n = e.find((e) => !e.startsWith("-")), o = b(n ?? process.cwd());
|
|
1907
2016
|
if (g(e)) return ee(), h({
|
|
1908
|
-
...
|
|
2017
|
+
...z(o),
|
|
1909
2018
|
spaCandidates: await B(o),
|
|
1910
2019
|
pageConvention: V(o),
|
|
1911
2020
|
fileTaxonomy: re(o).findings,
|
|
1912
2021
|
junctionFks: (await W(o))?.issues ?? [],
|
|
1913
2022
|
eventDelivery: await G(o),
|
|
1914
|
-
duplicateInstances:
|
|
1915
|
-
versionSkew:
|
|
2023
|
+
duplicateInstances: a(o),
|
|
2024
|
+
versionSkew: s(o) ?? null,
|
|
1916
2025
|
serverOnly: (await q(o)).status,
|
|
1917
2026
|
accessDecisions: await J(o),
|
|
1918
2027
|
webhookVerification: await Y(o),
|
|
1919
|
-
authz: await
|
|
2028
|
+
authz: await On(o),
|
|
1920
2029
|
ungrantedScopes: await Q(o),
|
|
1921
2030
|
neverEmittedEvents: await Z(o),
|
|
1922
|
-
|
|
1923
|
-
|
|
2031
|
+
unimportedDeps: Be(o),
|
|
2032
|
+
stalledRuns: await pn(o),
|
|
2033
|
+
decidedSchema: await jn(o)
|
|
1924
2034
|
}), 0;
|
|
1925
|
-
let c =
|
|
1926
|
-
|
|
1927
|
-
|
|
2035
|
+
let c = gt(o, t);
|
|
2036
|
+
Bt(o), zt(o), Qt(o), ne(o), await Kt(o), await hn(o), await mn(o), await tn(o), await sn(o), await on(o), await Mn(o), await cn(o), await Tn(o), await bn(o), await vn(o), console.log("");
|
|
2037
|
+
for (let e of Ve(Be(o))) console.log(e);
|
|
2038
|
+
await xn(o), await Sn(o), await Cn(o);
|
|
2039
|
+
let l = await fn(o), u = await An(o, e.includes("--write-authz-allowlist")), d = await ln(o), f = await dn(o), p = await wn(o), m = i(s(o));
|
|
1928
2040
|
m !== void 0 && (console.log(""), console.log(m));
|
|
1929
|
-
let _ =
|
|
2041
|
+
let _ = r(a(o));
|
|
1930
2042
|
return _ !== void 0 && console.log(`\n${_}`), l || u || d || p || f ? Math.max(c, 1) : c;
|
|
1931
2043
|
};
|
|
1932
2044
|
//#endregion
|
|
1933
|
-
export { B as C, Y as E, V as S, Q as T,
|
|
2045
|
+
export { B as C, Y as E, V as S, Q as T, Qt as _, On as a, gt as b, G as c, Nt as d, mt as f, Bt as g, zt as h, J as i, gn as l, rn as m, ht as n, Z as o, W as p, Vt as r, _n as s, $t as t, z as u, Kt as v, q as w, Pt as x, Nn as y };
|