@voltro/cli 0.38.0 → 0.39.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 +224 -0
- package/THIRD-PARTY-NOTICES.md +80 -45
- package/dist/{addCommand-BNeoeSxe.js → addCommand-C05L9tZh.js} +2 -2
- package/dist/addCommand-C3MdJG-a.js +2 -0
- package/dist/{agentsMd-mhQMF1bx.js → agentsMd-7zI2h5l9.js} +3 -3
- package/dist/agentsMd-BFCXh2gl.js +2 -0
- package/dist/apiBuild-2GvK8CUB.js +2 -0
- package/dist/{apiBuild-CvacV4zA.js → apiBuild-DGalUk9v.js} +3 -3
- package/dist/baselineCommand-CXv680Dc.js +2 -0
- package/dist/{baselineCommand-C6NMt-oa.js → baselineCommand-Ck2Xm8M7.js} +18 -18
- package/dist/bin.js +1 -1
- package/dist/{build-_LoTC1s0.js → build-CfF6t0UM.js} +107 -100
- package/dist/{capabilitiesCommand-nq_pz5xd.js → capabilitiesCommand-D5gucwYa.js} +8 -8
- package/dist/{checkCommand-wibtQx-N.js → checkCommand-BV56Pcc1.js} +1 -1
- package/dist/{checkCommand-Pjk5sBl2.js → checkCommand-CBtynBKW.js} +34 -34
- package/dist/{cloudCmd-NQSwe_Qk.js → cloudCmd-C42gaO8s.js} +16 -16
- package/dist/{clusterCmd-D5wsCmA_.js → clusterCmd-DrVFCzSj.js} +8 -4
- package/dist/{codegen-GYYcdpCg.js → codegen-CbpGCWLG.js} +1 -1
- package/dist/codegen-i8QGcHsi.js +2 -0
- package/dist/codegenCommand-W7SDdiAQ.js +129 -0
- package/dist/{codemodRunner-CXgQ-ecJ.js → codemodRunner-D-jTyvWo.js} +622 -551
- package/dist/{commands-DaZVi9wi.js → commands-BD9eBRY3.js} +48 -48
- package/dist/{connectionConfig-UFlIEiys.js → connectionConfig-Bk9IC7D0.js} +1 -1
- package/dist/{dashboardCommand-Dxy_kmoW.js → dashboardCommand-7qGylm0F.js} +3 -3
- package/dist/{dataCommand-D6-Ha2lf.js → dataCommand-C_F2DYxe.js} +14 -11
- package/dist/{dbCommand-C4UW8cVN.js → dbCommand-DSwGv9wS.js} +50 -49
- package/dist/dbCommand-DcqEyxju.js +2 -0
- package/dist/dev-alhkKoEX.js +3 -0
- package/dist/{dev-E7Eo5GHc.js → dev-gqpnzVhI.js} +2260 -2184
- package/dist/{dialectDriver-CgXnDfec.js → dialectDriver-czCHYpeH.js} +2 -1
- package/dist/{doctorCommand-gmDyALFL.js → doctorCommand-Bvs-BQrM.js} +242 -238
- package/dist/doctorCommand-CVXfRrng.js +2 -0
- package/dist/{dormancyCommand-CE6wG8lj.js → dormancyCommand-B-PPHc9Q.js} +1 -1
- package/dist/{e2eCmd-BRabZww-.js → e2eCmd-uFHig1hV.js} +8 -2
- package/dist/{embeddingsCommand-C3uAuf6N.js → embeddingsCommand-OKY6XjUf.js} +6 -6
- package/dist/{envCommand-DzuWh8ya.js → envCommand-B1-Zm85H.js} +19 -19
- package/dist/{evolveCommand-DPLqcYb2.js → evolveCommand-C4-NlFbd.js} +6 -8
- package/dist/{generateCommand-DbgcUpGw.js → generateCommand-CcyvH2ve.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-CmczGj_l.js → infoCommand-CTo8Jnhq.js} +7 -7
- package/dist/inspect-B7U7Cl_Z.js +1192 -0
- package/dist/inspect-DUze25t0.js +2 -0
- package/dist/{inspectCmd-EHFZ9yYu.js → inspectCmd-niF97fAq.js} +5 -5
- package/dist/inspectGateHint-BjnFubmH.js +7 -0
- package/dist/{logFileSink-C_D2wRN1.js → logFileSink-B4uP8pvf.js} +1 -1
- package/dist/{logsCmd-D36xK7Zu.js → logsCmd-B6oNsfaZ.js} +10 -7
- package/dist/{manifestBuild-hpPLaGxV.js → manifestBuild-BK42hu0k.js} +1 -1
- package/dist/manifestBuild-j0n109tt.js +2 -0
- package/dist/{metaCommands-CIoQNlaQ.js → metaCommands-DUYR--Ts.js} +1 -1
- package/dist/{migrate-RwgUWXfc.js → migrate-BnPw2zC8.js} +7 -4
- package/dist/{packageCommand-Cug_3Ogl.js → packageCommand-9SVyvsSo.js} +3 -2
- package/dist/{probeCommand-BPEpwT32.js → probeCommand-C9gazU0H.js} +35 -24
- package/dist/{projectScaffold-CxgtJvlb.js → projectScaffold-BEhhHjPr.js} +1 -1
- package/dist/{projectScaffold-DDgWbNLe.js → projectScaffold-BvhLOrLq.js} +17 -15
- package/dist/{runtimeTrace-6jfznSzr.js → runtimeTrace-DzZOCd94.js} +1 -1
- package/dist/{scheduleManifestCmd-D2x0CTTY.js → scheduleManifestCmd-kmWzrO_w.js} +6 -9
- package/dist/{sdkgen-CEJZZQUA.js → sdkgen-C4roLErM.js} +4 -4
- package/dist/{seedRunner-ZmLSqNe2.js → seedRunner-IdHEprqf.js} +1 -4
- package/dist/serveCommand-B6TATyCj.js +1770 -0
- package/dist/serveCommand-CNR0gI9V.js +2 -0
- package/dist/serveEntry.js +2 -2
- package/dist/{serverlessCommand-CfJZy6dS.js → serverlessCommand-DbJ7Plg9.js} +3 -3
- package/dist/{start-CidwCcjl.js → start-BNuTWdRd.js} +1 -1
- package/dist/{start-RCh6qKFe.js → start-ft_KzFTd.js} +257 -254
- package/dist/startEntry.js +1 -1
- package/dist/{test-DIQ0jlkQ.js → test-CLYXrZ2F.js} +1 -1
- package/dist/{tracesCmd-DStmCJPi.js → tracesCmd-DgtgOUdi.js} +7 -4
- package/dist/{typecheckCommand-BlsWiCNq.js → typecheckCommand-BuCtT92o.js} +7 -7
- package/dist/updateCommand-6FMU2klq.js +2 -0
- package/dist/{updateCommand-CmksX7m_.js → updateCommand-CHBmCB17.js} +5 -3
- package/dist/{webDev-BKdD7c_a.js → webDev-CTpSY-e_.js} +813 -775
- package/dist/webDev-id3I5PvG.js +2 -0
- package/dist/{webhookDiscovery-D7VaeMlz.js → webhookDiscovery-CphsQe59.js} +4 -6
- package/dist/{webhookDiscovery-CrGAfhIG.js → webhookDiscovery-il9ti-HE.js} +1 -1
- package/dist/{webhooksCommand-BHeaF1ZJ.js → webhooksCommand-BVvOtZ1B.js} +1 -1
- package/package.json +27 -21
- package/templates/AGENTS.md +2 -2
- package/templates/agent-docs/_index.md +2 -2
- package/templates/agent-docs/_manifest.json +2 -2
- package/templates/agent-docs/authentication.md +30 -0
- package/templates/agent-docs/cli.md +17 -13
- package/templates/agent-docs/data.md +44 -1
- package/templates/agent-docs/database/migrations.md +2 -2
- package/templates/agent-docs/database/seedsdialects.md +2 -2
- package/templates/agent-docs/deployment.md +21 -7
- package/templates/agent-docs/internationalization.md +10 -3
- package/templates/agent-docs/local-first-mobile.md +141 -20
- package/templates/agent-docs/plugins/mail.md +1 -1
- package/templates/agent-docs/plugins/ratelimit.md +1 -1
- package/templates/agent-docs/plugins.md +3 -1
- package/templates/agent-docs/releases.md +130 -1
- package/templates/agent-docs/routing.md +34 -2
- package/templates/agent-docs/templates/apibackends.md +1 -1
- package/templates/agent-docs/templates/mobile.md +1 -1
- package/templates/agent-docs/whats-new.md +181 -49
- package/templates/apps/api-ai/package.json +7 -7
- 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 +10 -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/authz.ts +16 -1
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- 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-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +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/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/README.md +41 -17
- package/templates/apps/mobile-app/app.config.ts +1 -1
- package/templates/apps/mobile-app/package.json +15 -11
- package/templates/apps/mobile-app/src/app/_layout.tsx +44 -13
- package/templates/apps/mobile-app/src/app/index.tsx +1 -1
- package/templates/apps/mobile-app/src/app/settings.tsx +4 -1
- package/templates/apps/mobile-app/src/client.ts +74 -57
- package/templates/apps/mobile-app/src/lib/api.ts +4 -4
- package/templates/apps/mobile-app/src/lib/deeplinks.ts +6 -6
- package/templates/apps/mobile-app/src/persistence.ts +24 -3
- package/templates/apps/mobile-app/tsconfig.json +17 -3
- package/dist/addCommand-aXSQveak.js +0 -2
- package/dist/agentsMd-BTchIZku.js +0 -2
- package/dist/apiBuild-ttSDDOGp.js +0 -2
- package/dist/baselineCommand-Cfw2Afwm.js +0 -2
- package/dist/codegen-uYrrDzQv.js +0 -2
- package/dist/codegenCommand-C60m_LCp.js +0 -30
- package/dist/dbCommand-CC-qNzpz.js +0 -2
- package/dist/dev-CAOoXOkc.js +0 -3
- package/dist/doctorCommand-Cjl8Yii8.js +0 -2
- package/dist/inspect-CjTYzAs_.js +0 -1190
- package/dist/inspect-P4pxoMaV.js +0 -2
- package/dist/inspectGateHint-BF6608UT.js +0 -4
- package/dist/manifestBuild-COkJoyAr.js +0 -2
- package/dist/serveCommand-DNRF2nDI.js +0 -2
- package/dist/serveCommand-DnY4VS3D.js +0 -1767
- package/dist/updateCommand-B0J_T9Vs.js +0 -2
- package/dist/webDev-CcJfdSjl2.js +0 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Bt as e,
|
|
2
|
-
import { $ as r, Q as i, X as a, Z as o, s, t as c, y as l } from "./webDev-
|
|
1
|
+
import { Bt as e, Ht as t, Vt as n } from "./dev-gqpnzVhI.js";
|
|
2
|
+
import { $ as r, Q as i, X as a, Z as o, s, t as c, y as l } from "./webDev-CTpSY-e_.js";
|
|
3
3
|
import { w as u } from "./frameworkTableAssembly-oMPBKqlE.js";
|
|
4
4
|
import { n as d } from "./pageConvention-cEiRxdab.js";
|
|
5
|
-
import { r as f, u as p } from "./apiBuild-
|
|
5
|
+
import { r as f, u as p } from "./apiBuild-DGalUk9v.js";
|
|
6
6
|
import { n as m, r as h, t as g } from "./cliOutput-D1tSBoRM.js";
|
|
7
|
-
import { n as
|
|
8
|
-
import { n as
|
|
9
|
-
import { basename as
|
|
10
|
-
import { existsSync as
|
|
11
|
-
import { pathToFileURL as
|
|
12
|
-
import { createLogger as
|
|
13
|
-
import { Node as
|
|
7
|
+
import { n as _, t as ee } from "./tsconfigPaths-BWXBWgcl.js";
|
|
8
|
+
import { n as te, r as ne } from "./fileTaxonomy-CJfgOllU.js";
|
|
9
|
+
import { basename as re, dirname as ie, join as v, relative as y, resolve as b, sep as x } from "node:path";
|
|
10
|
+
import { existsSync as S, readFileSync as C, readdirSync as w, statSync as T, writeFileSync as ae } from "node:fs";
|
|
11
|
+
import { pathToFileURL as oe } from "node:url";
|
|
12
|
+
import { createLogger as se } from "@voltro/logger";
|
|
13
|
+
import { Node as E, Project as D, SyntaxKind as O } from "ts-morph";
|
|
14
14
|
//#region src/predicateColumnAudit.ts
|
|
15
|
-
var
|
|
15
|
+
var ce = /* @__PURE__ */ new Set([
|
|
16
16
|
"eq",
|
|
17
17
|
"ne",
|
|
18
18
|
"gt",
|
|
@@ -30,43 +30,43 @@ var le = /* @__PURE__ */ new Set([
|
|
|
30
30
|
"endsWith",
|
|
31
31
|
"between",
|
|
32
32
|
"jsonField"
|
|
33
|
-
]),
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
36
|
-
},
|
|
33
|
+
]), le = (e) => {
|
|
34
|
+
if (E.isPropertyAccessExpression(e)) return e.getName();
|
|
35
|
+
if (E.isIdentifier(e)) return e.getText();
|
|
36
|
+
}, ue = (e) => {
|
|
37
37
|
let t = e;
|
|
38
|
-
for (;
|
|
38
|
+
for (; E.isCallExpression(t);) {
|
|
39
39
|
let e = t.getExpression();
|
|
40
|
-
if (!
|
|
40
|
+
if (!E.isPropertyAccessExpression(e)) break;
|
|
41
41
|
t = e.getExpression();
|
|
42
42
|
}
|
|
43
43
|
return t;
|
|
44
|
-
},
|
|
45
|
-
let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new
|
|
44
|
+
}, de = (e, t, n) => {
|
|
45
|
+
let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new D({
|
|
46
46
|
useInMemoryFileSystem: !0,
|
|
47
47
|
skipAddingFilesFromTsConfig: !0
|
|
48
48
|
});
|
|
49
49
|
for (let t of e) {
|
|
50
50
|
let e;
|
|
51
51
|
try {
|
|
52
|
-
let n =
|
|
52
|
+
let n = C(t, "utf8");
|
|
53
53
|
if (!n.includes(".where(") && !n.includes(".select(")) continue;
|
|
54
54
|
e = s.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
|
|
55
55
|
} catch {
|
|
56
56
|
continue;
|
|
57
57
|
}
|
|
58
|
-
let c =
|
|
59
|
-
for (let t of e.getDescendantsOfKind(
|
|
58
|
+
let c = y(n, t);
|
|
59
|
+
for (let t of e.getDescendantsOfKind(O.CallExpression)) {
|
|
60
60
|
let e = t.getExpression();
|
|
61
|
-
if (!
|
|
62
|
-
let n = ue(
|
|
61
|
+
if (!E.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
|
|
62
|
+
let n = le(ue(e.getExpression()));
|
|
63
63
|
if (n === void 0 || !r.has(n)) {
|
|
64
64
|
let t = e.getExpression();
|
|
65
|
-
if (
|
|
65
|
+
if (E.isCallExpression(t)) {
|
|
66
66
|
let e = t.getExpression();
|
|
67
|
-
if (
|
|
67
|
+
if (E.isPropertyAccessExpression(e) && e.getName() === "select") {
|
|
68
68
|
let e = t.getArguments()[0];
|
|
69
|
-
e !== void 0 &&
|
|
69
|
+
e !== void 0 && E.isStringLiteral(e) && (n = e.getLiteralValue());
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -75,16 +75,16 @@ var le = /* @__PURE__ */ new Set([
|
|
|
75
75
|
if (s === void 0) continue;
|
|
76
76
|
let l = [], [u] = t.getArguments();
|
|
77
77
|
if (u !== void 0) {
|
|
78
|
-
if (
|
|
78
|
+
if (E.isStringLiteral(u)) l.push({
|
|
79
79
|
column: u.getLiteralValue(),
|
|
80
80
|
line: u.getStartLineNumber()
|
|
81
81
|
});
|
|
82
|
-
else for (let e of [u, ...u.getDescendantsOfKind(
|
|
83
|
-
if (!
|
|
84
|
-
let t = e.getExpression(), n =
|
|
85
|
-
if (n === void 0 || !
|
|
82
|
+
else for (let e of [u, ...u.getDescendantsOfKind(O.CallExpression)]) {
|
|
83
|
+
if (!E.isCallExpression(e)) continue;
|
|
84
|
+
let t = e.getExpression(), n = E.isIdentifier(t) ? t.getText() : E.isPropertyAccessExpression(t) ? t.getName() : void 0;
|
|
85
|
+
if (n === void 0 || !ce.has(n)) continue;
|
|
86
86
|
let r = e.getArguments()[0];
|
|
87
|
-
r !== void 0 &&
|
|
87
|
+
r !== void 0 && E.isStringLiteral(r) && l.push({
|
|
88
88
|
column: r.getLiteralValue(),
|
|
89
89
|
line: r.getStartLineNumber()
|
|
90
90
|
});
|
|
@@ -103,16 +103,16 @@ var le = /* @__PURE__ */ new Set([
|
|
|
103
103
|
issues: a,
|
|
104
104
|
checked: o
|
|
105
105
|
};
|
|
106
|
-
},
|
|
106
|
+
}, k = 12, fe = (e) => {
|
|
107
107
|
if (e.checked === 0) return null;
|
|
108
108
|
if (e.issues.length === 0) return `predicate columns: ${e.checked} checked — every filtered column exists on its table`;
|
|
109
109
|
let t = [`predicate columns: ${e.issues.length} filter${e.issues.length, "s"} on a column that does not exist (${e.checked} checked)`];
|
|
110
110
|
for (let n of e.issues) {
|
|
111
|
-
let e = n.available.slice(0,
|
|
111
|
+
let e = n.available.slice(0, k).join(", "), r = n.available.length > k ? `, … (${n.available.length - k} more)` : "";
|
|
112
112
|
t.push(` ${n.file}:${n.line} '${n.table}' has no column '${n.column}'`), t.push(` columns: ${e}${r}`);
|
|
113
113
|
}
|
|
114
114
|
return t.push(" This type-checks today and fails at runtime as a bare SQL error."), t.join("\n");
|
|
115
|
-
},
|
|
115
|
+
}, pe = /* @__PURE__ */ new Set([
|
|
116
116
|
"id",
|
|
117
117
|
"tenantId",
|
|
118
118
|
"tenant_id",
|
|
@@ -132,7 +132,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
132
132
|
"idempotency_id",
|
|
133
133
|
"idempotencyKey",
|
|
134
134
|
"idempotency_key"
|
|
135
|
-
]),
|
|
135
|
+
]), me = /* @__PURE__ */ new Set([
|
|
136
136
|
"id",
|
|
137
137
|
"createdAt",
|
|
138
138
|
"created_at",
|
|
@@ -142,10 +142,10 @@ var le = /* @__PURE__ */ new Set([
|
|
|
142
142
|
"deleted_at",
|
|
143
143
|
"tenantId",
|
|
144
144
|
"tenant_id"
|
|
145
|
-
]),
|
|
145
|
+
]), he = (e) => pe.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), ge = (e) => e.type === "reference", _e = (e) => {
|
|
146
146
|
let t = [];
|
|
147
147
|
for (let n of e) {
|
|
148
|
-
let e = n.columns.filter((e) =>
|
|
148
|
+
let e = n.columns.filter((e) => he(e.name)), r = e.filter((e) => !ge(e)).map((e) => e.name);
|
|
149
149
|
if (r.length === 0) continue;
|
|
150
150
|
let i = new Set(n.primaryKey), a = r.filter((e) => i.has(e));
|
|
151
151
|
if (n.primaryKey.length >= 2 && a.length > 0) {
|
|
@@ -156,7 +156,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
156
156
|
});
|
|
157
157
|
continue;
|
|
158
158
|
}
|
|
159
|
-
if (e.some(
|
|
159
|
+
if (e.some(ge) && e.length >= 2) {
|
|
160
160
|
t.push({
|
|
161
161
|
table: n.name,
|
|
162
162
|
columns: r,
|
|
@@ -164,7 +164,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
164
164
|
});
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
|
-
e.length >= 2 && n.columns.filter((e) => !
|
|
167
|
+
e.length >= 2 && n.columns.filter((e) => !he(e.name) && !me.has(e.name)).length === 0 && t.push({
|
|
168
168
|
table: n.name,
|
|
169
169
|
columns: r,
|
|
170
170
|
signal: "link-only"
|
|
@@ -174,20 +174,20 @@ var le = /* @__PURE__ */ new Set([
|
|
|
174
174
|
issues: t,
|
|
175
175
|
tablesChecked: e.length
|
|
176
176
|
};
|
|
177
|
-
},
|
|
177
|
+
}, ve = {
|
|
178
178
|
"composite-pk": "part of a composite primary key",
|
|
179
179
|
"reference-sibling": "sits beside a wired reference() on this table",
|
|
180
180
|
"link-only": "this table is nothing but link columns"
|
|
181
|
-
},
|
|
181
|
+
}, ye = (e) => {
|
|
182
182
|
if (e.tablesChecked === 0) return null;
|
|
183
183
|
if (e.issues.length === 0) return `junction FKs: ${e.tablesChecked} table${e.tablesChecked === 1 ? "" : "s"} checked — every link column is a wired reference()`;
|
|
184
184
|
let t = e.issues.length, n = [`junction FKs: ${t} junction table${t === 1 ? "" : "s"} with an id column that is a plain text() and not a reference()`];
|
|
185
185
|
for (let t of e.issues) {
|
|
186
186
|
let e = t.columns.map((e) => `'${e}'`).join(", ");
|
|
187
|
-
n.push(` '${t.table}': ${e} (${
|
|
187
|
+
n.push(` '${t.table}': ${e} (${ve[t.signal]})`);
|
|
188
188
|
}
|
|
189
189
|
return n.push(" Declare each as reference(() => <table>): the FK is enforced, the column is auto-indexed,"), n.push(" and the relationship becomes walkable (a plain text() id column is an invisible edge)."), n.join("\n");
|
|
190
|
-
},
|
|
190
|
+
}, be = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), Se = (e) => xe(e).some((e) => be.has(e)), Ce = (e) => {
|
|
191
191
|
let t = [], n = [];
|
|
192
192
|
for (let r of e) t.push({
|
|
193
193
|
event: r.name,
|
|
@@ -196,7 +196,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
196
196
|
event: r.name,
|
|
197
197
|
kind: "wide-key",
|
|
198
198
|
keyFieldCount: r.keyFieldCount
|
|
199
|
-
}), r.hasWebhook &&
|
|
199
|
+
}), r.hasWebhook && Se(r.name) && n.push({
|
|
200
200
|
event: r.name,
|
|
201
201
|
kind: "webhook-high-rate"
|
|
202
202
|
});
|
|
@@ -205,45 +205,45 @@ var le = /* @__PURE__ */ new Set([
|
|
|
205
205
|
modes: t,
|
|
206
206
|
warnings: n
|
|
207
207
|
};
|
|
208
|
-
},
|
|
208
|
+
}, we = (e) => {
|
|
209
209
|
if (e.eventsChecked === 0) return null;
|
|
210
210
|
let t = e.eventsChecked, n = [`event delivery: ${t} declared event${t === 1 ? "" : "s"} — the mode decides what a MISSING message means`];
|
|
211
211
|
for (let t of e.modes) n.push(` '${t.event}': ${t.delivery}`);
|
|
212
212
|
return n.push(" each — every delivery matters; a slow subscriber loses the oldest and is TOLD how many (the default)."), n.push(" latest — a newer delivery supersedes a pending one; a slow subscriber gets the current value, told nothing."), n.push(" Two events with identical route/subscriber/buffer numbers mean OPPOSITE things about a drop — the mode is the difference."), n.join("\n");
|
|
213
|
-
},
|
|
213
|
+
}, Te = (e) => {
|
|
214
214
|
if (e.warnings.length === 0) return null;
|
|
215
215
|
let t = e.warnings.length, n = [`events that may not scale as declared: ${t} finding${t === 1 ? "" : "s"}`];
|
|
216
216
|
for (let t of e.warnings) t.kind === "wide-key" ? (n.push(` '${t.event}': routing key has ${t.keyFieldCount ?? 0} fields — every field FRAGMENTS the subscriber set (distinct routes are the PRODUCT of the fields’ value spaces).`), n.push(" Check each is an ADDRESS the delivery is decided by (arenaId), not a discriminator the handler reads (gameType) — the latter is payload, not key.")) : (n.push(` '${t.event}': a webhook block on a per-frame-looking event — every publish is N HTTP deliveries per second per subscribed target.`), n.push(" The webhook rate limit DEFERS the excess as pending rows rather than failing, so the symptom is a growing table. Publish a coarser event (a summary / state change) for the outside world."));
|
|
217
217
|
return n.push(" WARNING, not a fault — a producer may intend the fan-out. `voltro doctor --json` prints every finding."), n.join("\n");
|
|
218
|
-
},
|
|
218
|
+
}, Ee = /* @__PURE__ */ new Set([
|
|
219
219
|
"start",
|
|
220
220
|
"run",
|
|
221
221
|
"child"
|
|
222
|
-
]),
|
|
222
|
+
]), De = (e) => E.isCallExpression(e) && (() => {
|
|
223
223
|
let t = e.getExpression();
|
|
224
|
-
if (!
|
|
224
|
+
if (!E.isPropertyAccessExpression(t) || !Ee.has(t.getName())) return !1;
|
|
225
225
|
let n = t.getExpression();
|
|
226
|
-
return
|
|
227
|
-
})(),
|
|
228
|
-
let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new
|
|
226
|
+
return E.isPropertyAccessExpression(n) ? n.getName() === "workflows" : E.isIdentifier(n) && n.getText() === "workflows";
|
|
227
|
+
})(), Oe = (e, t, n) => {
|
|
228
|
+
let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new D({
|
|
229
229
|
useInMemoryFileSystem: !0,
|
|
230
230
|
skipAddingFilesFromTsConfig: !0
|
|
231
231
|
});
|
|
232
232
|
for (let t of e) {
|
|
233
233
|
let e;
|
|
234
234
|
try {
|
|
235
|
-
let n =
|
|
235
|
+
let n = C(t, "utf8");
|
|
236
236
|
if (!n.includes("workflows.")) continue;
|
|
237
237
|
e = l.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
|
|
238
238
|
} catch {
|
|
239
239
|
continue;
|
|
240
240
|
}
|
|
241
|
-
let u =
|
|
242
|
-
for (let t of e.getDescendantsOfKind(
|
|
243
|
-
if (!
|
|
241
|
+
let u = y(n, t);
|
|
242
|
+
for (let t of e.getDescendantsOfKind(O.CallExpression)) {
|
|
243
|
+
if (!De(t)) continue;
|
|
244
244
|
c++;
|
|
245
245
|
let e = t.getStartLineNumber(), [n, l] = t.getArguments();
|
|
246
|
-
if (n === void 0 || !
|
|
246
|
+
if (n === void 0 || !E.isStringLiteral(n)) {
|
|
247
247
|
o.push({
|
|
248
248
|
file: u,
|
|
249
249
|
line: e,
|
|
@@ -262,7 +262,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
262
262
|
});
|
|
263
263
|
continue;
|
|
264
264
|
}
|
|
265
|
-
if (l === void 0 || !
|
|
265
|
+
if (l === void 0 || !E.isObjectLiteralExpression(l)) {
|
|
266
266
|
o.push({
|
|
267
267
|
file: u,
|
|
268
268
|
line: e,
|
|
@@ -270,7 +270,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
270
270
|
});
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
|
-
if (l.getProperties().some((e) =>
|
|
273
|
+
if (l.getProperties().some((e) => E.isSpreadAssignment(e))) {
|
|
274
274
|
o.push({
|
|
275
275
|
file: u,
|
|
276
276
|
line: e,
|
|
@@ -279,14 +279,14 @@ var le = /* @__PURE__ */ new Set([
|
|
|
279
279
|
continue;
|
|
280
280
|
}
|
|
281
281
|
let p = /* @__PURE__ */ new Set(), m = !1;
|
|
282
|
-
for (let e of l.getProperties()) if (
|
|
283
|
-
let t = (
|
|
284
|
-
if (
|
|
282
|
+
for (let e of l.getProperties()) if (E.isPropertyAssignment(e) || E.isShorthandPropertyAssignment(e)) {
|
|
283
|
+
let t = (E.isPropertyAssignment(e), e.getNameNode());
|
|
284
|
+
if (E.isComputedPropertyName(t)) {
|
|
285
285
|
m = !0;
|
|
286
286
|
break;
|
|
287
287
|
}
|
|
288
288
|
p.add(e.getName().replace(/^['"]|['"]$/g, ""));
|
|
289
|
-
} else (
|
|
289
|
+
} else (E.isMethodDeclaration(e) || E.isGetAccessorDeclaration(e)) && p.add(e.getName());
|
|
290
290
|
if (m) {
|
|
291
291
|
o.push({
|
|
292
292
|
file: u,
|
|
@@ -324,7 +324,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
324
324
|
checked: s,
|
|
325
325
|
callSites: c
|
|
326
326
|
};
|
|
327
|
-
},
|
|
327
|
+
}, ke = (e) => {
|
|
328
328
|
if (e.callSites === 0) return null;
|
|
329
329
|
let t = [];
|
|
330
330
|
t.push(`workflow starts: ${e.checked}/${e.callSites} call sites checked`);
|
|
@@ -337,7 +337,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
337
337
|
for (let n of e.unchecked) t.push(` ${n.file}:${n.line} — ${n.reason}`);
|
|
338
338
|
}
|
|
339
339
|
return e.issues.length === 0 && e.unchecked.length === 0 && t.push(" every call site matches its workflow"), t.join("\n");
|
|
340
|
-
},
|
|
340
|
+
}, Ae = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, A = "VOLTRO_FIELD_ENCRYPTION_KEY", je = (e) => {
|
|
341
341
|
let t;
|
|
342
342
|
for (let [n, r] of e.sources) if (t === void 0 && /\bfieldEncryption\b/.test(r) && (t = {
|
|
343
343
|
reason: "fieldEncryption",
|
|
@@ -345,13 +345,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
345
345
|
}), t === void 0 && /\.encrypted\s*\(/.test(r) && (t = {
|
|
346
346
|
reason: "encrypted-column",
|
|
347
347
|
file: n
|
|
348
|
-
}), r.includes(
|
|
348
|
+
}), r.includes(A)) return;
|
|
349
349
|
if (t !== void 0) {
|
|
350
|
-
for (let [, t] of e.envFiles) if (t.includes(
|
|
350
|
+
for (let [, t] of e.envFiles) if (t.includes(A)) return;
|
|
351
351
|
return t;
|
|
352
352
|
}
|
|
353
|
-
},
|
|
354
|
-
`✗ ${
|
|
353
|
+
}, Me = (e) => [
|
|
354
|
+
`✗ ${A} is named nowhere under this app root, and the app encrypts columns at rest`,
|
|
355
355
|
e.reason === "fieldEncryption" ? ` (\`fieldEncryption\` in ${e.file})` : ` (an \`.encrypted()\` column in ${e.file})`,
|
|
356
356
|
"",
|
|
357
357
|
" A deploy without it does not degrade — it refuses to boot at `onActivate`,",
|
|
@@ -367,14 +367,14 @@ 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"), Ne = (e) => {
|
|
371
371
|
if (e.catalogCodes.length === 0) return;
|
|
372
372
|
let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
|
|
373
373
|
if (n.length !== 0) return {
|
|
374
374
|
orphaned: n,
|
|
375
375
|
noneDeclared: t.size === 0
|
|
376
376
|
};
|
|
377
|
-
},
|
|
377
|
+
}, Pe = (e, t) => {
|
|
378
378
|
let n = e.orphaned.join(", ");
|
|
379
379
|
return e.noneDeclared ? [
|
|
380
380
|
`⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
|
|
@@ -386,33 +386,33 @@ var le = /* @__PURE__ */ new Set([
|
|
|
386
386
|
" …or delete the files. A catalog that is never loaded looks from the inside",
|
|
387
387
|
" exactly like one that works, which is why this is worth a line."
|
|
388
388
|
] : [`⚠ ${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
|
-
},
|
|
389
|
+
}, j = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), M = /* @__PURE__ */ new Set([
|
|
390
390
|
"requireScope",
|
|
391
391
|
"requireAnyScope",
|
|
392
392
|
"requireAuth",
|
|
393
393
|
"assertScope",
|
|
394
394
|
"assertCan",
|
|
395
395
|
"can"
|
|
396
|
-
]),
|
|
396
|
+
]), Fe = (e) => {
|
|
397
397
|
let t = /* @__PURE__ */ new Set();
|
|
398
|
-
for (let n of e) for (let e of
|
|
398
|
+
for (let n of e) for (let e of j(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
|
|
399
399
|
return t;
|
|
400
|
-
},
|
|
400
|
+
}, Ie = (e, t) => {
|
|
401
401
|
for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
|
|
402
|
-
},
|
|
402
|
+
}, Le = (e) => {
|
|
403
403
|
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);
|
|
404
404
|
return t && n;
|
|
405
|
-
},
|
|
405
|
+
}, Re = (e, t, n) => {
|
|
406
406
|
if (e.hasDeclaredGuards) return { kind: "declared-guard" };
|
|
407
|
-
let r =
|
|
408
|
-
return i === void 0 ?
|
|
407
|
+
let r = j(e.src), i = Ie(r, t);
|
|
408
|
+
return i === void 0 ? Le(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
|
|
409
409
|
kind: "vocabulary-guard",
|
|
410
410
|
name: i
|
|
411
411
|
};
|
|
412
|
-
},
|
|
412
|
+
}, ze = (e, t, n) => {
|
|
413
413
|
let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
|
|
414
414
|
return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
|
|
415
|
-
},
|
|
415
|
+
}, Be = (e, t, n) => {
|
|
416
416
|
switch (t.kind) {
|
|
417
417
|
case "declared-guard": return "guards: on the descriptor";
|
|
418
418
|
case "vocabulary-guard": return `calls ${t.name}()`;
|
|
@@ -420,15 +420,15 @@ var le = /* @__PURE__ */ new Set([
|
|
|
420
420
|
case "allowlisted": return "accepted by a previous run";
|
|
421
421
|
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`;
|
|
422
422
|
}
|
|
423
|
-
},
|
|
423
|
+
}, Ve = (e) => {
|
|
424
424
|
let t = e.executors.map((t) => {
|
|
425
|
-
let n =
|
|
425
|
+
let n = Re(t, e.guardVocabulary, e.allowlist);
|
|
426
426
|
return {
|
|
427
427
|
tag: t.tag,
|
|
428
428
|
path: t.path,
|
|
429
429
|
verdict: n,
|
|
430
|
-
severity:
|
|
431
|
-
why:
|
|
430
|
+
severity: ze(t, e.tenantScopedTables, e.referencedTables),
|
|
431
|
+
why: Be(t, n, e.rowFilterRegistered === !0)
|
|
432
432
|
};
|
|
433
433
|
}), n = (e) => t.filter((t) => t.verdict.kind === e).length;
|
|
434
434
|
return {
|
|
@@ -442,7 +442,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
442
442
|
},
|
|
443
443
|
unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
|
|
444
444
|
};
|
|
445
|
-
},
|
|
445
|
+
}, He = [
|
|
446
446
|
"# voltro doctor — authz allowlist",
|
|
447
447
|
"#",
|
|
448
448
|
"# Two KINDS of line, and the difference is the point:",
|
|
@@ -467,19 +467,19 @@ var le = /* @__PURE__ */ new Set([
|
|
|
467
467
|
"# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
|
|
468
468
|
"# lines for executors that are newly unchecked.",
|
|
469
469
|
""
|
|
470
|
-
],
|
|
470
|
+
], Ue = (e) => {
|
|
471
471
|
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));
|
|
472
472
|
return [
|
|
473
|
-
...
|
|
473
|
+
...He,
|
|
474
474
|
...n,
|
|
475
475
|
...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
|
|
476
476
|
...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
|
|
477
477
|
""
|
|
478
478
|
].join("\n");
|
|
479
|
-
},
|
|
479
|
+
}, We = (e, t) => {
|
|
480
480
|
let n = [...new Set(t)].sort();
|
|
481
|
-
if (e === void 0) return
|
|
482
|
-
let r =
|
|
481
|
+
if (e === void 0) return Ue(n.map((e) => ({ tag: e })));
|
|
482
|
+
let r = N(e).tags, i = n.filter((e) => !r.has(e));
|
|
483
483
|
return i.length === 0 ? e : [
|
|
484
484
|
e.replace(/\n+$/, ""),
|
|
485
485
|
"",
|
|
@@ -488,25 +488,25 @@ var le = /* @__PURE__ */ new Set([
|
|
|
488
488
|
...i,
|
|
489
489
|
""
|
|
490
490
|
].join("\n");
|
|
491
|
-
},
|
|
491
|
+
}, Ge = (e, t) => {
|
|
492
492
|
let n = [...new Set(t)];
|
|
493
493
|
if (e === void 0) return {
|
|
494
494
|
added: n.length,
|
|
495
495
|
kept: 0,
|
|
496
496
|
removed: 0
|
|
497
497
|
};
|
|
498
|
-
let r =
|
|
498
|
+
let r = N(e).tags;
|
|
499
499
|
return {
|
|
500
500
|
added: n.filter((e) => !r.has(e)).length,
|
|
501
501
|
kept: r.size,
|
|
502
502
|
removed: 0
|
|
503
503
|
};
|
|
504
|
-
},
|
|
504
|
+
}, Ke = class extends Error {
|
|
505
505
|
tag;
|
|
506
506
|
constructor(e) {
|
|
507
507
|
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";
|
|
508
508
|
}
|
|
509
|
-
},
|
|
509
|
+
}, N = (e) => {
|
|
510
510
|
let t = [];
|
|
511
511
|
for (let n of e.split("\n")) {
|
|
512
512
|
let e = n.trim();
|
|
@@ -519,7 +519,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
519
519
|
continue;
|
|
520
520
|
}
|
|
521
521
|
let a = r[2].trim();
|
|
522
|
-
if (a.length === 0) throw new
|
|
522
|
+
if (a.length === 0) throw new Ke(i);
|
|
523
523
|
t.push({
|
|
524
524
|
tag: i,
|
|
525
525
|
reviewed: a
|
|
@@ -529,13 +529,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
529
529
|
tags: new Set(t.map((e) => e.tag)),
|
|
530
530
|
entries: t
|
|
531
531
|
};
|
|
532
|
-
},
|
|
532
|
+
}, P = (e) => {
|
|
533
533
|
let t = e.indexOf(".");
|
|
534
534
|
return t <= 0 ? void 0 : e.slice(0, t);
|
|
535
|
-
},
|
|
535
|
+
}, qe = (e, t) => {
|
|
536
536
|
let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
|
|
537
537
|
for (let e of t.rpcTags) {
|
|
538
|
-
let t =
|
|
538
|
+
let t = P(e);
|
|
539
539
|
if (t === void 0) continue;
|
|
540
540
|
let n = i.get(t);
|
|
541
541
|
n ? n.push(e) : i.set(t, [e]);
|
|
@@ -564,7 +564,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
564
564
|
});
|
|
565
565
|
continue;
|
|
566
566
|
}
|
|
567
|
-
let s = new Set(o.rpcTags.map(
|
|
567
|
+
let s = new Set(o.rpcTags.map(P).filter((e) => e !== void 0));
|
|
568
568
|
for (let e of s) {
|
|
569
569
|
let t = i.get(e);
|
|
570
570
|
t !== void 0 && a.push({
|
|
@@ -577,12 +577,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
577
577
|
}
|
|
578
578
|
}
|
|
579
579
|
return a;
|
|
580
|
-
},
|
|
580
|
+
}, Je = (e, t) => {
|
|
581
581
|
let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
|
|
582
582
|
if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
|
|
583
583
|
for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
|
|
584
584
|
return r;
|
|
585
|
-
},
|
|
585
|
+
}, Ye = async (e) => {
|
|
586
586
|
if (!await e.hasWorkflows().catch(() => !1)) return { kind: "not-applicable" };
|
|
587
587
|
let t;
|
|
588
588
|
try {
|
|
@@ -606,12 +606,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
606
606
|
} finally {
|
|
607
607
|
await t.close().catch(() => {});
|
|
608
608
|
}
|
|
609
|
-
},
|
|
609
|
+
}, Xe = (e) => {
|
|
610
610
|
let t = Math.round(e / 6e4);
|
|
611
611
|
if (t < 90) return `${t}m`;
|
|
612
612
|
let n = Math.round(t / 60);
|
|
613
613
|
return n < 48 ? `${n}h` : `${Math.round(n / 24)}d`;
|
|
614
|
-
},
|
|
614
|
+
}, Ze = (e) => {
|
|
615
615
|
if (e.kind === "not-applicable") return null;
|
|
616
616
|
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.
|
|
617
617
|
Run it where the app's DB env vars are set, or ignore this line locally.`;
|
|
@@ -619,41 +619,41 @@ var le = /* @__PURE__ */ new Set([
|
|
|
619
619
|
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` : ""})`);
|
|
620
620
|
else {
|
|
621
621
|
n.push(`✗ stuck runs: ${t.stalled.length} run(s) have made no progress`);
|
|
622
|
-
for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${
|
|
622
|
+
for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${Xe(e.idleMs)} ${e.reason}`);
|
|
623
623
|
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.");
|
|
624
624
|
}
|
|
625
625
|
t.sawFullRunPage && n.push(" (the examined page filled up — more old runs exist than were read this pass)");
|
|
626
626
|
for (let e of t.failures) n.push(` ! could not examine ${e.subject}: ${e.detail}`);
|
|
627
627
|
return n.join("\n");
|
|
628
|
-
},
|
|
628
|
+
}, Qe = (e) => S(b(e, "app.config.ts")) || S(b(e, "app.config.js")), $e = (e) => {
|
|
629
629
|
let t = p(e), n = f(e);
|
|
630
630
|
return {
|
|
631
631
|
root: e,
|
|
632
632
|
serveBundle: t,
|
|
633
|
-
serveBundlePresent:
|
|
633
|
+
serveBundlePresent: S(t),
|
|
634
634
|
apiBundle: n,
|
|
635
|
-
apiBundlePresent:
|
|
635
|
+
apiBundlePresent: S(n)
|
|
636
636
|
};
|
|
637
637
|
}, et = [
|
|
638
638
|
"Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
|
|
639
639
|
"Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
|
|
640
640
|
"(Generated Dockerfiles from `voltro create-project` already do this.)"
|
|
641
641
|
].join("\n "), tt = (e, t = !1) => {
|
|
642
|
-
let n =
|
|
643
|
-
if (console.log(`voltro doctor — serve preflight for ${n.root}`),
|
|
642
|
+
let n = $e(e);
|
|
643
|
+
if (console.log(`voltro doctor — serve preflight for ${n.root}`), !Qe(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
|
|
644
644
|
console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
|
|
645
645
|
let r = process.env.NODE_ENV === "production";
|
|
646
646
|
if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
|
|
647
647
|
let i = t || r;
|
|
648
648
|
return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${et}`), +!!i;
|
|
649
649
|
}, nt = (e, t, n) => {
|
|
650
|
-
let r = new Set(e), i =
|
|
650
|
+
let r = new Set(e), i = _(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
|
|
651
651
|
`${e}.ts`,
|
|
652
652
|
`${e}.tsx`,
|
|
653
653
|
`${e}.js`,
|
|
654
654
|
`${e}.jsx`,
|
|
655
|
-
|
|
656
|
-
|
|
655
|
+
v(e, "index.ts"),
|
|
656
|
+
v(e, "index.tsx")
|
|
657
657
|
]).find((e) => r.has(e)), o = (e) => {
|
|
658
658
|
let n = (t.get(e) ?? "").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), r = [];
|
|
659
659
|
for (let e of [
|
|
@@ -666,13 +666,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
666
666
|
}
|
|
667
667
|
let o = [];
|
|
668
668
|
for (let t of r) {
|
|
669
|
-
let n = t.startsWith(".") ?
|
|
669
|
+
let n = t.startsWith(".") ? b(ie(e), t) : ee(t, i);
|
|
670
670
|
if (n === void 0) continue;
|
|
671
671
|
let r = a(n);
|
|
672
672
|
r !== void 0 && o.push(r);
|
|
673
673
|
}
|
|
674
674
|
return o;
|
|
675
|
-
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) => ct.test(
|
|
675
|
+
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) => ct.test(y(n, e))), l = new Set(c);
|
|
676
676
|
for (; c.length > 0;) {
|
|
677
677
|
let e = c.shift();
|
|
678
678
|
if (!s.has(e)) {
|
|
@@ -685,7 +685,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
685
685
|
let d = /* @__PURE__ */ new Set();
|
|
686
686
|
for (let e of s) l.has(e) || u.has(e) || d.add(e);
|
|
687
687
|
return d;
|
|
688
|
-
}, rt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), it = (e, t) => (e.match(t) ?? []).length, F = (e, t) => e.getDescendantsOfKind(
|
|
688
|
+
}, rt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), it = (e, t) => (e.match(t) ?? []).length, F = (e, t) => e.getDescendantsOfKind(O.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, at = (e, t) => {
|
|
689
689
|
if (e.src.includes(t)) return !0;
|
|
690
690
|
let n = t.replace(/^@voltro\/plugin-/, "");
|
|
691
691
|
return n !== t && F(e.file, RegExp(`^${n}Plugin$`)) > 0;
|
|
@@ -699,7 +699,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
699
699
|
return n;
|
|
700
700
|
}, st = (e, t) => {
|
|
701
701
|
let n = t.getSymbol()?.getDeclarations()?.[0];
|
|
702
|
-
return n === void 0 ? [] : e.getDescendantsOfKind(
|
|
702
|
+
return n === void 0 ? [] : e.getDescendantsOfKind(O.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
|
|
703
703
|
}, ct = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, lt = /\.(query|mutation|action|stream)\.server\.tsx?$/, ut = [
|
|
704
704
|
"Id",
|
|
705
705
|
"ID",
|
|
@@ -814,16 +814,16 @@ var le = /* @__PURE__ */ new Set([
|
|
|
814
814
|
scope: "client",
|
|
815
815
|
smell: "checking a subscription's `data === undefined` (or `!data`) instead of its `loading` / `idle` flag",
|
|
816
816
|
use: "branch on `loading` (and `idle` when you pass `skip`) — `loading` narrows `data`, so the settled branch needs no `?? []` and no `!`",
|
|
817
|
-
match: ({ file: e }) => e.getDescendantsOfKind(
|
|
818
|
-
let t = e.getFirstAncestorByKind(
|
|
819
|
-
return t === void 0 ? [] :
|
|
817
|
+
match: ({ file: e }) => e.getDescendantsOfKind(O.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
|
|
818
|
+
let t = e.getFirstAncestorByKind(O.VariableDeclaration)?.getNameNode();
|
|
819
|
+
return t === void 0 ? [] : E.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
|
|
820
820
|
}).some((t) => st(e, t).some((e) => {
|
|
821
821
|
let t = e.getParent();
|
|
822
|
-
if (
|
|
822
|
+
if (E.isBinaryExpression(t)) {
|
|
823
823
|
let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
|
|
824
824
|
return /^[!=]==?$/.test(n) && /^(undefined|null)$/.test(r.getText());
|
|
825
825
|
}
|
|
826
|
-
return
|
|
826
|
+
return E.isPrefixUnaryExpression(t) && t.getOperatorToken() === O.ExclamationToken;
|
|
827
827
|
}))
|
|
828
828
|
},
|
|
829
829
|
{
|
|
@@ -957,7 +957,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
957
957
|
], pt = (e) => R(e), mt = (e, t) => {
|
|
958
958
|
let n = /* @__PURE__ */ new Set();
|
|
959
959
|
for (let r of t) {
|
|
960
|
-
let t =
|
|
960
|
+
let t = y(e, r), i = t.split(/[\\/]/)[0];
|
|
961
961
|
n.add(i !== void 0 && i !== t ? i : ".");
|
|
962
962
|
}
|
|
963
963
|
return [...n].sort();
|
|
@@ -965,30 +965,30 @@ var le = /* @__PURE__ */ new Set([
|
|
|
965
965
|
if (n > 12 || L.test(e)) return t;
|
|
966
966
|
let r;
|
|
967
967
|
try {
|
|
968
|
-
r =
|
|
968
|
+
r = w(e, { withFileTypes: !0 });
|
|
969
969
|
} catch {
|
|
970
970
|
return t;
|
|
971
971
|
}
|
|
972
972
|
for (let i of r) {
|
|
973
|
-
let r =
|
|
973
|
+
let r = v(e, i.name);
|
|
974
974
|
if (i.isDirectory()) {
|
|
975
975
|
if (i.name.startsWith(".")) continue;
|
|
976
976
|
gt(r, t, n + 1);
|
|
977
|
-
} else
|
|
977
|
+
} else Ae.test(i.name) && t.push(r);
|
|
978
978
|
}
|
|
979
979
|
return t;
|
|
980
980
|
}, R = (e, t = [], n = 0) => {
|
|
981
981
|
if (n > 12 || L.test(e)) return t;
|
|
982
982
|
let r;
|
|
983
983
|
try {
|
|
984
|
-
r =
|
|
984
|
+
r = w(e);
|
|
985
985
|
} catch {
|
|
986
986
|
return t;
|
|
987
987
|
}
|
|
988
988
|
for (let i of r) {
|
|
989
|
-
let r =
|
|
989
|
+
let r = v(e, i), a;
|
|
990
990
|
try {
|
|
991
|
-
a =
|
|
991
|
+
a = T(r);
|
|
992
992
|
} catch {
|
|
993
993
|
continue;
|
|
994
994
|
}
|
|
@@ -999,14 +999,14 @@ var le = /* @__PURE__ */ new Set([
|
|
|
999
999
|
if (n > 12 || L.test(e)) return t;
|
|
1000
1000
|
let r;
|
|
1001
1001
|
try {
|
|
1002
|
-
r =
|
|
1002
|
+
r = w(e);
|
|
1003
1003
|
} catch {
|
|
1004
1004
|
return t;
|
|
1005
1005
|
}
|
|
1006
1006
|
for (let i of r) {
|
|
1007
|
-
let r =
|
|
1007
|
+
let r = v(e, i), a;
|
|
1008
1008
|
try {
|
|
1009
|
-
a =
|
|
1009
|
+
a = T(r);
|
|
1010
1010
|
} catch {
|
|
1011
1011
|
continue;
|
|
1012
1012
|
}
|
|
@@ -1022,16 +1022,16 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1022
1022
|
}, yt = (e) => {
|
|
1023
1023
|
let t = pt(e);
|
|
1024
1024
|
if (t.length === 0) return [];
|
|
1025
|
-
let n = new
|
|
1025
|
+
let n = new D({
|
|
1026
1026
|
useInMemoryFileSystem: !0,
|
|
1027
1027
|
skipAddingFilesFromTsConfig: !0
|
|
1028
1028
|
}), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
|
|
1029
1029
|
for (let e of t) try {
|
|
1030
|
-
i.set(e,
|
|
1030
|
+
i.set(e, C(e, "utf8"));
|
|
1031
1031
|
} catch {}
|
|
1032
1032
|
let a = nt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
|
|
1033
1033
|
for (let t of _t(e)) try {
|
|
1034
|
-
o.set(t,
|
|
1034
|
+
o.set(t, C(t, "utf8"));
|
|
1035
1035
|
} catch {}
|
|
1036
1036
|
for (let s of t) {
|
|
1037
1037
|
let t = i.get(s);
|
|
@@ -1041,7 +1041,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1041
1041
|
c = {
|
|
1042
1042
|
src: t,
|
|
1043
1043
|
file: n.createSourceFile(`/scan${s.endsWith("x") ? ".tsx" : ".ts"}`, t, { overwrite: !0 }),
|
|
1044
|
-
path:
|
|
1044
|
+
path: y(e, s),
|
|
1045
1045
|
serverOnly: a.has(s),
|
|
1046
1046
|
testSources: o
|
|
1047
1047
|
};
|
|
@@ -1051,7 +1051,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1051
1051
|
for (let t of ft) {
|
|
1052
1052
|
if (t.plugin !== void 0 && at(c, t.plugin) || !t.match(c)) continue;
|
|
1053
1053
|
let n = r.get(t.id) ?? [];
|
|
1054
|
-
n.push(
|
|
1054
|
+
n.push(y(e, s)), r.set(t.id, n);
|
|
1055
1055
|
}
|
|
1056
1056
|
}
|
|
1057
1057
|
return ft.filter((e) => r.has(e.id)).map((e) => ({
|
|
@@ -1075,17 +1075,17 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1075
1075
|
}, bt = 3, xt = .2, St = 50, Ct = (e, t) => e >= St && t > 0 && e / t >= xt, wt = (e) => {
|
|
1076
1076
|
let t = /* @__PURE__ */ new Map();
|
|
1077
1077
|
for (let n of R(e)) try {
|
|
1078
|
-
t.set(
|
|
1078
|
+
t.set(y(e, n), C(n, "utf8"));
|
|
1079
1079
|
} catch {}
|
|
1080
1080
|
let n = /* @__PURE__ */ new Map();
|
|
1081
1081
|
for (let t of gt(e)) try {
|
|
1082
|
-
n.set(
|
|
1082
|
+
n.set(y(e, t), C(t, "utf8"));
|
|
1083
1083
|
} catch {}
|
|
1084
|
-
let r =
|
|
1084
|
+
let r = je({
|
|
1085
1085
|
sources: t,
|
|
1086
1086
|
envFiles: n
|
|
1087
1087
|
});
|
|
1088
|
-
r !== void 0 && (console.log(""), console.log(
|
|
1088
|
+
r !== void 0 && (console.log(""), console.log(Me(r)));
|
|
1089
1089
|
}, Tt = (e) => {
|
|
1090
1090
|
let t = z(e);
|
|
1091
1091
|
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;
|
|
@@ -1116,37 +1116,39 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1116
1116
|
let n = Ot.exec(e)?.[1];
|
|
1117
1117
|
return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
|
|
1118
1118
|
}, B = async (e) => {
|
|
1119
|
-
let t =
|
|
1120
|
-
if (!
|
|
1119
|
+
let t = v(e, "src", "pages");
|
|
1120
|
+
if (!S(t)) return [];
|
|
1121
1121
|
let { pages: n, dirs: r } = await l(t), i = [];
|
|
1122
1122
|
for (let a of n) {
|
|
1123
1123
|
if (!s(r, a.dirChain).some((e) => e.hasLayout)) continue;
|
|
1124
1124
|
let n;
|
|
1125
1125
|
try {
|
|
1126
|
-
n =
|
|
1126
|
+
n = C(v(t, a.file), "utf8");
|
|
1127
1127
|
} catch {
|
|
1128
1128
|
continue;
|
|
1129
1129
|
}
|
|
1130
1130
|
if (c.test(n)) continue;
|
|
1131
1131
|
let o = kt(n);
|
|
1132
1132
|
o !== null && i.push({
|
|
1133
|
-
file:
|
|
1133
|
+
file: y(e, v(t, a.file)),
|
|
1134
1134
|
pattern: a.pattern,
|
|
1135
1135
|
currentMode: o
|
|
1136
1136
|
});
|
|
1137
1137
|
}
|
|
1138
1138
|
return i;
|
|
1139
1139
|
}, At = 10, jt = async (e) => {
|
|
1140
|
-
|
|
1141
|
-
let t =
|
|
1142
|
-
if (t
|
|
1143
|
-
|
|
1144
|
-
|
|
1140
|
+
console.log("");
|
|
1141
|
+
let t = v(e, "src", "pages");
|
|
1142
|
+
if (!S(t)) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
1143
|
+
let n = await B(e);
|
|
1144
|
+
if (n.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
|
|
1145
|
+
console.log(`• renderMode:'spa' candidates (${n.length} page${n.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
|
|
1146
|
+
for (let e of n.slice(0, At)) {
|
|
1145
1147
|
let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
|
|
1146
1148
|
console.log(` ${e.file} (${e.pattern}) — ${t}`);
|
|
1147
1149
|
}
|
|
1148
|
-
let
|
|
1149
|
-
return
|
|
1150
|
+
let r = n.length - At;
|
|
1151
|
+
return r > 0 && console.log(` (+${r} more page${r === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${Et}`), n.length;
|
|
1150
1152
|
}, Mt = /* @__PURE__ */ new Set([
|
|
1151
1153
|
"node_modules",
|
|
1152
1154
|
"dist",
|
|
@@ -1158,60 +1160,60 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1158
1160
|
]), Nt = (e, t, n) => {
|
|
1159
1161
|
let r;
|
|
1160
1162
|
try {
|
|
1161
|
-
r =
|
|
1163
|
+
r = w(e, { withFileTypes: !0 });
|
|
1162
1164
|
} catch {
|
|
1163
1165
|
return;
|
|
1164
1166
|
}
|
|
1165
1167
|
for (let i of r) {
|
|
1166
1168
|
if (Mt.has(i.name)) continue;
|
|
1167
|
-
let r =
|
|
1168
|
-
i.isDirectory() ? Nt(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(
|
|
1169
|
+
let r = v(e, i.name);
|
|
1170
|
+
i.isDirectory() ? Nt(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(y(t, r));
|
|
1169
1171
|
}
|
|
1170
1172
|
}, Pt = (e, t) => {
|
|
1171
1173
|
let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
|
|
1172
1174
|
for (let i of t) {
|
|
1173
1175
|
let t;
|
|
1174
1176
|
try {
|
|
1175
|
-
t =
|
|
1177
|
+
t = C(v(e, i), "utf8");
|
|
1176
1178
|
} catch {
|
|
1177
1179
|
continue;
|
|
1178
1180
|
}
|
|
1179
|
-
let a =
|
|
1181
|
+
let a = ie(v(e, i));
|
|
1180
1182
|
for (let i of t.matchAll(r)) {
|
|
1181
|
-
let t =
|
|
1183
|
+
let t = b(a, i[1]);
|
|
1182
1184
|
for (let r of [
|
|
1183
1185
|
t,
|
|
1184
1186
|
`${t}.tsx`,
|
|
1185
1187
|
`${t}.ts`,
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
]) if (
|
|
1189
|
-
n.add(
|
|
1188
|
+
v(t, "index.tsx"),
|
|
1189
|
+
v(t, "index.ts")
|
|
1190
|
+
]) if (S(r) && T(r).isFile()) {
|
|
1191
|
+
n.add(y(e, r));
|
|
1190
1192
|
break;
|
|
1191
1193
|
}
|
|
1192
1194
|
}
|
|
1193
1195
|
}
|
|
1194
1196
|
return n;
|
|
1195
1197
|
}, Ft = (e, t) => {
|
|
1196
|
-
let n = e.slice(t.length).split(
|
|
1198
|
+
let n = e.slice(t.length).split(x).slice(0, -1)[0];
|
|
1197
1199
|
return n !== void 0 && n.startsWith("[");
|
|
1198
1200
|
}, V = (e) => {
|
|
1199
|
-
let t = [], n =
|
|
1200
|
-
if (!
|
|
1201
|
+
let t = [], n = v(e, "src", "pages"), r = v(e, "src");
|
|
1202
|
+
if (!S(r)) return t;
|
|
1201
1203
|
let i = [];
|
|
1202
1204
|
Nt(r, e, i), i.sort();
|
|
1203
|
-
let a = `${
|
|
1205
|
+
let a = `${y(e, n)}${x}`;
|
|
1204
1206
|
Pt(e, i);
|
|
1205
1207
|
for (let n of i) {
|
|
1206
|
-
let r = n.slice(n.lastIndexOf(
|
|
1208
|
+
let r = n.slice(n.lastIndexOf(x) + 1);
|
|
1207
1209
|
if (n.startsWith(a)) {
|
|
1208
1210
|
if (r !== "page.tsx") continue;
|
|
1209
|
-
|
|
1211
|
+
S(v(e, d(n))) || t.push({
|
|
1210
1212
|
kind: "page-without-test",
|
|
1211
1213
|
file: n
|
|
1212
1214
|
});
|
|
1213
1215
|
try {
|
|
1214
|
-
let r =
|
|
1216
|
+
let r = C(v(e, n), "utf8");
|
|
1215
1217
|
!/\bexport\s+(?:const|let|var|function)\s+meta\b/.test(r) && !/\bexport\s*\{[^}]*\bmeta\b/.test(r) && t.push({
|
|
1216
1218
|
kind: "page-without-meta",
|
|
1217
1219
|
file: n
|
|
@@ -1236,16 +1238,18 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1236
1238
|
let r = t.length - It;
|
|
1237
1239
|
r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
|
|
1238
1240
|
}, Lt = (e) => {
|
|
1239
|
-
|
|
1240
|
-
let t =
|
|
1241
|
-
if (t
|
|
1242
|
-
let n = (e)
|
|
1243
|
-
|
|
1241
|
+
console.log("");
|
|
1242
|
+
let t = v(e, "src", "pages");
|
|
1243
|
+
if (!S(t)) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
1244
|
+
let n = V(e);
|
|
1245
|
+
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;
|
|
1246
|
+
let r = (e) => n.filter((t) => t.kind === e).map((e) => e.file);
|
|
1247
|
+
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;
|
|
1244
1248
|
}, Rt = "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.", zt = /* @__PURE__ */ new Map(), U = (e) => {
|
|
1245
1249
|
let t = zt.get(e);
|
|
1246
1250
|
if (t !== void 0) return t;
|
|
1247
1251
|
let n = (async () => {
|
|
1248
|
-
let { walk: t, loadDiscovered: n } = await import("./dev-
|
|
1252
|
+
let { walk: t, loadDiscovered: n } = await import("./dev-alhkKoEX.js"), r = await t(e);
|
|
1249
1253
|
return {
|
|
1250
1254
|
files: r,
|
|
1251
1255
|
discovered: await n(r)
|
|
@@ -1263,7 +1267,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1263
1267
|
};
|
|
1264
1268
|
});
|
|
1265
1269
|
if (i.length === 0) return;
|
|
1266
|
-
t =
|
|
1270
|
+
t = fe(de(n, i, e));
|
|
1267
1271
|
} catch {
|
|
1268
1272
|
return;
|
|
1269
1273
|
}
|
|
@@ -1281,19 +1285,19 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1281
1285
|
}), W = async (e) => {
|
|
1282
1286
|
try {
|
|
1283
1287
|
let { discovered: t } = await U(e);
|
|
1284
|
-
return
|
|
1288
|
+
return _e(Vt(t.tables));
|
|
1285
1289
|
} catch {
|
|
1286
1290
|
return null;
|
|
1287
1291
|
}
|
|
1288
1292
|
}, Ht = async (e) => {
|
|
1289
1293
|
let t = await W(e);
|
|
1290
1294
|
if (t === null || t.tablesChecked === 0) return;
|
|
1291
|
-
let n =
|
|
1295
|
+
let n = ye(t);
|
|
1292
1296
|
n !== null && (console.log(""), console.log(`${t.issues.length > 0 ? "✗" : "✓"} ${n}`));
|
|
1293
1297
|
}, G = async (e) => {
|
|
1294
1298
|
try {
|
|
1295
1299
|
let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await U(e);
|
|
1296
|
-
return
|
|
1300
|
+
return Ce(n.events.map((e) => {
|
|
1297
1301
|
let n = e.descriptor, r = t(n.key);
|
|
1298
1302
|
return {
|
|
1299
1303
|
name: n.name,
|
|
@@ -1308,9 +1312,9 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1308
1312
|
}, Ut = async (e) => {
|
|
1309
1313
|
let t = await G(e);
|
|
1310
1314
|
if (t === null || t.eventsChecked === 0) return;
|
|
1311
|
-
let n =
|
|
1315
|
+
let n = we(t);
|
|
1312
1316
|
n !== null && (console.log(""), console.log(`• ${n}`));
|
|
1313
|
-
let r =
|
|
1317
|
+
let r = Te(t);
|
|
1314
1318
|
r !== null && (console.log(""), console.log(`⚠️ ${r}`));
|
|
1315
1319
|
}, K = (e) => {
|
|
1316
1320
|
let t = e, n = typeof t?.message == "string" && t.message !== "" ? t.message : String(e), r = typeof t?.code == "string" ? t.code : void 0;
|
|
@@ -1333,7 +1337,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1333
1337
|
}
|
|
1334
1338
|
}, J = async (r) => {
|
|
1335
1339
|
try {
|
|
1336
|
-
let { loadApiConfig: i } = await import("./dev-
|
|
1340
|
+
let { loadApiConfig: i } = await import("./dev-alhkKoEX.js"), { discovered: a } = await U(r), o = n(await i(r)), s = {
|
|
1337
1341
|
queries: a.queries,
|
|
1338
1342
|
mutations: a.mutations,
|
|
1339
1343
|
actions: a.actions,
|
|
@@ -1343,8 +1347,8 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1343
1347
|
return {
|
|
1344
1348
|
checked: !0,
|
|
1345
1349
|
defaultDeny: o,
|
|
1346
|
-
undecided:
|
|
1347
|
-
openAccess:
|
|
1350
|
+
undecided: t(s),
|
|
1351
|
+
openAccess: e(s)
|
|
1348
1352
|
};
|
|
1349
1353
|
} catch (e) {
|
|
1350
1354
|
return {
|
|
@@ -1368,14 +1372,14 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1368
1372
|
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");
|
|
1369
1373
|
}, Y = async (e) => {
|
|
1370
1374
|
try {
|
|
1371
|
-
let { files: t } = await U(e), { WEBHOOK_PATTERN: n, loadWebhookFiles: r } = await import("./webhookDiscovery-
|
|
1375
|
+
let { files: t } = await U(e), { WEBHOOK_PATTERN: n, loadWebhookFiles: r } = await import("./webhookDiscovery-il9ti-HE.js"), { incomingWebhookVerification: i } = await import("@voltro/plugin-webhooks"), a = t.filter((e) => n.test(re(e)));
|
|
1372
1376
|
if (a.length === 0) return {
|
|
1373
1377
|
checked: !0,
|
|
1374
1378
|
unverified: [],
|
|
1375
1379
|
open: [],
|
|
1376
1380
|
verified: 0
|
|
1377
1381
|
};
|
|
1378
|
-
let o = await r(a,
|
|
1382
|
+
let o = await r(a, se({ scope: "voltro:doctor" })), s = [], c = [], l = 0;
|
|
1379
1383
|
for (let e of o.incoming) {
|
|
1380
1384
|
let t = i(e.descriptor);
|
|
1381
1385
|
t === null ? s.push({
|
|
@@ -1413,11 +1417,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1413
1417
|
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;
|
|
1414
1418
|
}, qt = async (e) => {
|
|
1415
1419
|
let { status: t, message: n } = await q(e);
|
|
1416
|
-
return t.checked ? n
|
|
1417
|
-
}, Jt = async (e) =>
|
|
1420
|
+
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);
|
|
1421
|
+
}, Jt = async (e) => Ye({
|
|
1418
1422
|
hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
|
|
1419
1423
|
openStore: async () => {
|
|
1420
|
-
let { loadApiConfig: t, buildStore: n } = await import("./dev-
|
|
1424
|
+
let { loadApiConfig: t, buildStore: n } = await import("./dev-alhkKoEX.js"), r = await t(e);
|
|
1421
1425
|
if (r === null) throw Error("no app.config.ts in this directory");
|
|
1422
1426
|
let i = await n(r, void 0);
|
|
1423
1427
|
return {
|
|
@@ -1432,13 +1436,13 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1432
1436
|
return t({ store: e });
|
|
1433
1437
|
}
|
|
1434
1438
|
}), Yt = async (e) => {
|
|
1435
|
-
let t =
|
|
1439
|
+
let t = Ze(await Jt(e));
|
|
1436
1440
|
t !== null && (console.log(""), console.log(t));
|
|
1437
1441
|
}, Xt = async (e) => {
|
|
1438
1442
|
let t;
|
|
1439
1443
|
try {
|
|
1440
1444
|
let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await U(e);
|
|
1441
|
-
t =
|
|
1445
|
+
t = Oe(r, i.workflows.map((e) => {
|
|
1442
1446
|
let t = e.definition, r = t.payloadSchema === void 0 ? {
|
|
1443
1447
|
required: [],
|
|
1444
1448
|
optional: []
|
|
@@ -1452,18 +1456,18 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1452
1456
|
} catch {
|
|
1453
1457
|
return;
|
|
1454
1458
|
}
|
|
1455
|
-
let n =
|
|
1459
|
+
let n = ke(t);
|
|
1456
1460
|
if (n === null) return;
|
|
1457
1461
|
console.log("");
|
|
1458
1462
|
let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
|
|
1459
1463
|
console.log(`${r} ${n}`);
|
|
1460
1464
|
}, X = "voltro-authz-allowlist.txt", Zt = (e, t = (e) => {
|
|
1461
1465
|
try {
|
|
1462
|
-
return
|
|
1466
|
+
return C(e, "utf8");
|
|
1463
1467
|
} catch {
|
|
1464
1468
|
return "";
|
|
1465
1469
|
}
|
|
1466
|
-
}) => /* @__PURE__ */ new Set([...
|
|
1470
|
+
}) => /* @__PURE__ */ new Set([...M, ...Fe(R(e).map(t))]), Qt = (e, t) => {
|
|
1467
1471
|
let n = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1468
1472
|
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];
|
|
1469
1473
|
}, Z = async (e) => {
|
|
@@ -1475,7 +1479,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1475
1479
|
if (n.length === 0) return [];
|
|
1476
1480
|
let r = new Set(t.events.map((e) => e.file)), i = (e) => {
|
|
1477
1481
|
try {
|
|
1478
|
-
return
|
|
1482
|
+
return C(e, "utf8");
|
|
1479
1483
|
} catch {
|
|
1480
1484
|
return "";
|
|
1481
1485
|
}
|
|
@@ -1497,11 +1501,11 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1497
1501
|
}
|
|
1498
1502
|
}, en = async (e) => {
|
|
1499
1503
|
try {
|
|
1500
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1504
|
+
let { loadApiConfig: t } = await import("./dev-alhkKoEX.js"), n = (await t(e))?.doctor?.scopeVocabulary;
|
|
1501
1505
|
if (typeof n != "string") return [];
|
|
1502
1506
|
let [r, i] = n.split("#");
|
|
1503
1507
|
if (r === void 0 || i === void 0) return [];
|
|
1504
|
-
let a = (await import(
|
|
1508
|
+
let a = (await import(oe(b(e, r)).href))[i];
|
|
1505
1509
|
return Array.isArray(a) ? a.filter((e) => typeof e == "string") : [];
|
|
1506
1510
|
} catch {
|
|
1507
1511
|
return [];
|
|
@@ -1513,7 +1517,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1513
1517
|
findings: []
|
|
1514
1518
|
};
|
|
1515
1519
|
try {
|
|
1516
|
-
let { offlineManifest: n, toInput: r } = await import("./checkCommand-
|
|
1520
|
+
let { offlineManifest: n, toInput: r } = await import("./checkCommand-BV56Pcc1.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-zuMGKVYX.js"), o = await n(e), s = r(o.manifest, o.tables), c = await en(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
|
|
1517
1521
|
if (l.length === 0) return t;
|
|
1518
1522
|
let u = i({
|
|
1519
1523
|
...s,
|
|
@@ -1546,32 +1550,32 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1546
1550
|
}, nn = async (e) => {
|
|
1547
1551
|
let t = ["src/locales", "locales"], n, r = [];
|
|
1548
1552
|
for (let i of t) {
|
|
1549
|
-
let t =
|
|
1550
|
-
if (
|
|
1551
|
-
r =
|
|
1553
|
+
let t = v(e, i);
|
|
1554
|
+
if (S(t)) try {
|
|
1555
|
+
r = w(t).filter((e) => /\.tsx?$/.test(e) && !e.endsWith(".d.ts")).map((e) => e.replace(/\.tsx?$/, "")), n = i;
|
|
1552
1556
|
break;
|
|
1553
1557
|
} catch {}
|
|
1554
1558
|
}
|
|
1555
1559
|
if (n === void 0 || r.length === 0) return;
|
|
1556
1560
|
let i = [];
|
|
1557
1561
|
try {
|
|
1558
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1562
|
+
let { loadApiConfig: t } = await import("./dev-alhkKoEX.js");
|
|
1559
1563
|
i = (await t(e))?.locales ?? [];
|
|
1560
1564
|
} catch {
|
|
1561
1565
|
return;
|
|
1562
1566
|
}
|
|
1563
|
-
let a =
|
|
1567
|
+
let a = Ne({
|
|
1564
1568
|
catalogCodes: r,
|
|
1565
1569
|
declaredLocales: i
|
|
1566
1570
|
});
|
|
1567
1571
|
if (a !== void 0) {
|
|
1568
1572
|
console.log("");
|
|
1569
|
-
for (let e of
|
|
1573
|
+
for (let e of Pe(a, n)) console.log(e);
|
|
1570
1574
|
}
|
|
1571
1575
|
}, rn = async (e) => {
|
|
1572
1576
|
let { discovered: t } = await U(e), n = [];
|
|
1573
1577
|
try {
|
|
1574
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1578
|
+
let { loadApiConfig: t } = await import("./dev-alhkKoEX.js");
|
|
1575
1579
|
n = ((await t(e))?.plugins ?? []).map((e) => {
|
|
1576
1580
|
let t = (e.extendSchema?.tables ?? []).map((e) => {
|
|
1577
1581
|
let t = e;
|
|
@@ -1597,12 +1601,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1597
1601
|
...t.mutations,
|
|
1598
1602
|
...t.actions,
|
|
1599
1603
|
...t.streams ?? []
|
|
1600
|
-
].map((e) => String(e.descriptor.name)), i =
|
|
1604
|
+
].map((e) => String(e.descriptor.name)), i = qe(n, {
|
|
1601
1605
|
tables: t.tables.map((e) => e.tableName),
|
|
1602
1606
|
rpcTags: r
|
|
1603
1607
|
});
|
|
1604
1608
|
console.log();
|
|
1605
|
-
for (let e of
|
|
1609
|
+
for (let e of Je(n, i)) console.log(e);
|
|
1606
1610
|
}, an = /* @__PURE__ */ new Set([
|
|
1607
1611
|
"@voltro/plugin-rbac",
|
|
1608
1612
|
"@voltro/plugin-audit",
|
|
@@ -1641,7 +1645,7 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1641
1645
|
}, cn = async (e) => {
|
|
1642
1646
|
let { discovered: t } = await U(e), n = /* @__PURE__ */ new Map(), r = (e) => {
|
|
1643
1647
|
if (!n.has(e)) try {
|
|
1644
|
-
n.set(e,
|
|
1648
|
+
n.set(e, C(e, "utf8"));
|
|
1645
1649
|
} catch {
|
|
1646
1650
|
n.set(e, "");
|
|
1647
1651
|
}
|
|
@@ -1678,12 +1682,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1678
1682
|
op: a?.op === "delete" || a?.op === "insert" ? a.op : t,
|
|
1679
1683
|
...a?.table === void 0 ? {} : { table: a.table }
|
|
1680
1684
|
};
|
|
1681
|
-
})), s =
|
|
1685
|
+
})), s = v(e, X), c = S(s) ? N(C(s, "utf8")) : void 0, l = !1;
|
|
1682
1686
|
for (let t of R(e)) if (/\bsetRowFilter\s*[<(]/.test(r(t))) {
|
|
1683
1687
|
l = !0;
|
|
1684
1688
|
break;
|
|
1685
1689
|
}
|
|
1686
|
-
let u =
|
|
1690
|
+
let u = Ve({
|
|
1687
1691
|
executors: o,
|
|
1688
1692
|
guardVocabulary: i,
|
|
1689
1693
|
tenantScopedTables: t.tenantScopedTables,
|
|
@@ -1703,12 +1707,12 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1703
1707
|
}, ln = async (e, t) => {
|
|
1704
1708
|
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await cn(e), l = n.counts;
|
|
1705
1709
|
console.log(`\nauthz scan · ${o} executor(s)`);
|
|
1706
|
-
let u = [...r].filter((e) => !
|
|
1710
|
+
let u = [...r].filter((e) => !M.has(e)).sort();
|
|
1707
1711
|
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)}`);
|
|
1708
1712
|
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;
|
|
1709
1713
|
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) {
|
|
1710
|
-
let e =
|
|
1711
|
-
return
|
|
1714
|
+
let e = S(s) ? C(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = Ge(e, t);
|
|
1715
|
+
return ae(s, We(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;
|
|
1712
1716
|
}
|
|
1713
1717
|
if (n.unchecked.length === 0) return !1;
|
|
1714
1718
|
console.log("");
|
|
@@ -1716,30 +1720,30 @@ var le = /* @__PURE__ */ new Set([
|
|
|
1716
1720
|
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);
|
|
1717
1721
|
}, un = async (e) => {
|
|
1718
1722
|
if (e.includes("--help") || e.includes("-h")) return console.log(Rt), 0;
|
|
1719
|
-
let t = e.includes("--strict"), n =
|
|
1723
|
+
let t = e.includes("--strict"), n = e.find((e) => !e.startsWith("-")), s = b(n ?? process.cwd());
|
|
1720
1724
|
if (h(e)) return g(), m({
|
|
1721
|
-
...z(
|
|
1722
|
-
spaCandidates: await B(
|
|
1723
|
-
pageConvention: V(
|
|
1724
|
-
fileTaxonomy:
|
|
1725
|
-
junctionFks: (await W(
|
|
1726
|
-
eventDelivery: await G(
|
|
1727
|
-
duplicateInstances: a(
|
|
1728
|
-
versionSkew: o(
|
|
1729
|
-
serverOnly: (await q(
|
|
1730
|
-
accessDecisions: await J(
|
|
1731
|
-
webhookVerification: await Y(
|
|
1732
|
-
authz: await sn(
|
|
1733
|
-
ungrantedScopes: await Q(
|
|
1734
|
-
neverEmittedEvents: await Z(
|
|
1735
|
-
stalledRuns: await Jt(
|
|
1725
|
+
...z(s),
|
|
1726
|
+
spaCandidates: await B(s),
|
|
1727
|
+
pageConvention: V(s),
|
|
1728
|
+
fileTaxonomy: ne(s).findings,
|
|
1729
|
+
junctionFks: (await W(s))?.issues ?? [],
|
|
1730
|
+
eventDelivery: await G(s),
|
|
1731
|
+
duplicateInstances: a(s),
|
|
1732
|
+
versionSkew: o(s) ?? null,
|
|
1733
|
+
serverOnly: (await q(s)).status,
|
|
1734
|
+
accessDecisions: await J(s),
|
|
1735
|
+
webhookVerification: await Y(s),
|
|
1736
|
+
authz: await sn(s),
|
|
1737
|
+
ungrantedScopes: await Q(s),
|
|
1738
|
+
neverEmittedEvents: await Z(s),
|
|
1739
|
+
stalledRuns: await Jt(s)
|
|
1736
1740
|
}), 0;
|
|
1737
|
-
let
|
|
1738
|
-
Tt(
|
|
1739
|
-
let
|
|
1740
|
-
|
|
1741
|
-
let
|
|
1742
|
-
return
|
|
1741
|
+
let c = tt(s, t);
|
|
1742
|
+
Tt(s), wt(s), Lt(s), te(s), await jt(s), await Xt(s), await Yt(s), await Bt(s), await Ht(s), await Ut(s), await rn(s), await tn(s), await $t(s), await nn(s);
|
|
1743
|
+
let l = await qt(s), u = await ln(s, e.includes("--write-authz-allowlist")), d = await Wt(s), f = await Kt(s), p = r(o(s));
|
|
1744
|
+
p !== void 0 && (console.log(""), console.log(p));
|
|
1745
|
+
let _ = i(a(s));
|
|
1746
|
+
return _ !== void 0 && console.log(`\n${_}`), l || u || d || f ? Math.max(c, 1) : c;
|
|
1743
1747
|
};
|
|
1744
1748
|
//#endregion
|
|
1745
|
-
export { q as C, B as S, Y as T, jt as _, sn as a, yt as b, G as c, vt as d,
|
|
1749
|
+
export { q as C, B as S, Y as T, jt as _, sn as a, yt as b, G as c, vt as d, $e as f, Lt as g, Tt as h, J as i, Zt as l, wt as m, et as n, Z as o, W as p, Et as r, Qt as s, Rt as t, z as u, un as v, Q as w, V as x, tt as y };
|