@voltro/cli 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/dist/apiBuild-B8aoJvuw.js +2 -0
  3. package/dist/{apiBuild-DZF_f0_I.js → apiBuild-DYD_ONLD.js} +2 -2
  4. package/dist/{appGraph-gQ_6GkQQ.js → appGraph-KGDPTuTy.js} +1 -1
  5. package/dist/appGraph-zuMGKVYX.js +2 -0
  6. package/dist/bin.js +1 -1
  7. package/dist/{build-CE7Ca9gm.js → build-CD8K4XOr.js} +7 -7
  8. package/dist/{checkCommand-Dv8r9tGO.js → checkCommand-Ct9xkTrS.js} +8 -7
  9. package/dist/{checkCommand-YZDmkAGd.js → checkCommand-DKpDLlqu.js} +1 -1
  10. package/dist/{clusterCmd-CNXKlcvD.js → clusterCmd-D5wsCmA_.js} +1 -1
  11. package/dist/{codegenCommand-DC6w2tNZ.js → codegenCommand-C4YoQIc2.js} +3 -3
  12. package/dist/{codemodRunner-DRRqXR74.js → codemodRunner-BnFq3Fgu.js} +175 -34
  13. package/dist/{commands-gutsz-Ac.js → commands-BE8E7zF3.js} +37 -29
  14. package/dist/{dashboardCommand-3YG8p-UA.js → dashboardCommand-D7SgZGaN.js} +3 -3
  15. package/dist/{dataCommand-qL0r7fPO.js → dataCommand-BhYwDgg-.js} +56 -54
  16. package/dist/dbCommand-DS4b97Is.js +2 -0
  17. package/dist/{dbCommand-B6X0FZbq.js → dbCommand-O8HA63s2.js} +6 -6
  18. package/dist/{dev-CerMd0mW.js → dev-C7sFZq3m.js} +2696 -2188
  19. package/dist/{dev-CoG-ZPx8.js → dev-D2BikO7a.js} +1 -1
  20. package/dist/doctorCommand-CM4Ch9C7.js +2 -0
  21. package/dist/{doctorCommand-CnDXQxfa.js → doctorCommand-DnimF5IM.js} +76 -73
  22. package/dist/{dormancyCommand-Dszo57d6.js → dormancyCommand-QewYug_s.js} +1 -1
  23. package/dist/{embeddingsCommand-C0sKVRo1.js → embeddingsCommand-BfiLS_QI.js} +1 -1
  24. package/dist/{envCommand-DPgdV1Bq.js → envCommand-CCGPRQY1.js} +2 -2
  25. package/dist/{evolveCommand-DHpkgjgH.js → evolveCommand-CHsLCtDf.js} +2 -2
  26. package/dist/frameworkTableAssembly-YyVe32Cb.js +2 -0
  27. package/dist/frameworkTableAssembly-oMPBKqlE.js +511 -0
  28. package/dist/{generateCommand-oibemh97.js → generateCommand-DbgcUpGw.js} +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/{infoCommand-BJw9nLUR.js → infoCommand-DwOgK1t6.js} +1 -1
  31. package/dist/{inspect-CBqFtAKA.js → inspect-CjTYzAs_.js} +32 -2
  32. package/dist/inspect-P4pxoMaV.js +2 -0
  33. package/dist/{inspectCmd-Bppy-GGw.js → inspectCmd-EHFZ9yYu.js} +1 -1
  34. package/dist/{inspectFetch-Cm8_wVvp.js → inspectFetch-EMuhTG_9.js} +1 -1
  35. package/dist/{logsCmd-CCca3KRZ.js → logsCmd-D36xK7Zu.js} +1 -1
  36. package/dist/manifestBuild-COkJoyAr.js +2 -0
  37. package/dist/{manifestBuild-sxpwdKY1.js → manifestBuild-hpPLaGxV.js} +1 -1
  38. package/dist/{migrate-CBwOt_iV.js → migrate-BV7I-ZHZ.js} +3 -3
  39. package/dist/probeCommand-CZfaaUOZ.js +122 -0
  40. package/dist/{runtimeTrace-CRxalXTs.js → runtimeTrace-CH3eUiMw.js} +1 -1
  41. package/dist/scheduleCmd-DQRu6BZC.js +149 -0
  42. package/dist/{sdkgen-CYJscZC7.js → sdkgen-BLkvGRfX.js} +1 -1
  43. package/dist/{seedRunner-TFHHiToI.js → seedRunner-ZmLSqNe2.js} +74 -70
  44. package/dist/serveCommand-CbDHU6l-.js +2 -0
  45. package/dist/serveCommand-iwlUBNS1.js +1766 -0
  46. package/dist/serveEntry.js +1 -1
  47. package/dist/{start-B-9Nsp-S.js → start-BgN62boB.js} +1 -1
  48. package/dist/{start-9LiUOfES.js → start-T4VesWiM.js} +2 -2
  49. package/dist/startEntry.js +1 -1
  50. package/dist/{tracesCmd-CkEZQrtt.js → tracesCmd-DStmCJPi.js} +1 -1
  51. package/dist/updateCommand-BlMZhWgO.js +2 -0
  52. package/dist/{updateCommand-Bkptutss.js → updateCommand-x0pI_x-B.js} +1 -1
  53. package/dist/webDev-BcykISYQ2.js +2 -0
  54. package/dist/{webDev-CBYvPqQr.js → webDev-Dybxew86.js} +14 -2
  55. package/dist/{webhooksCommand-CID96Rga.js → webhooksCommand-DlAgS2Iw.js} +1 -1
  56. package/dist/{workflowsCmd-D1VTmLMY.js → workflowsCmd-BGF-mRZ5.js} +1 -1
  57. package/package.json +47 -17
  58. package/templates/AGENTS.md +1 -1
  59. package/templates/agent-docs/_index.md +1 -1
  60. package/templates/agent-docs/_manifest.json +1 -1
  61. package/templates/agent-docs/authentication.md +55 -7
  62. package/templates/agent-docs/cli.md +59 -0
  63. package/templates/agent-docs/data.md +59 -2
  64. package/templates/agent-docs/database/migrations.md +50 -1
  65. package/templates/agent-docs/deployment.md +214 -21
  66. package/templates/agent-docs/introduction.md +1 -1
  67. package/templates/agent-docs/multi-tenancy.md +93 -22
  68. package/templates/agent-docs/plugins/ai-flows.md +86 -0
  69. package/templates/agent-docs/plugins/billing.md +1 -1
  70. package/templates/agent-docs/plugins/clickhouse.md +31 -0
  71. package/templates/agent-docs/plugins/search.md +6 -3
  72. package/templates/agent-docs/plugins/storage.md +11 -0
  73. package/templates/agent-docs/plugins.md +3 -1
  74. package/templates/agent-docs/scheduling.md +17 -0
  75. package/templates/agent-docs/security.md +2 -2
  76. package/templates/agent-docs/whats-new.md +95 -1729
  77. package/templates/agent-docs/workflows.md +101 -0
  78. package/templates/apps/api-ai/package.json +7 -7
  79. package/templates/apps/api-auth/package.json +8 -8
  80. package/templates/apps/api-backend/package.json +7 -7
  81. package/templates/apps/api-backend-deactivation/package.json +7 -7
  82. package/templates/apps/api-backend-mail/package.json +8 -8
  83. package/templates/apps/api-backend-mariadb/package.json +9 -9
  84. package/templates/apps/api-backend-sqlite/package.json +8 -8
  85. package/templates/apps/api-backend-storage/package.json +8 -8
  86. package/templates/apps/api-cms/package.json +10 -10
  87. package/templates/apps/api-collab/package.json +8 -8
  88. package/templates/apps/api-data-advanced/package.json +8 -8
  89. package/templates/apps/api-durable/package.json +8 -8
  90. package/templates/apps/api-feature-flags/package.json +9 -9
  91. package/templates/apps/api-governance/package.json +8 -8
  92. package/templates/apps/api-kv/package.json +8 -8
  93. package/templates/apps/api-moderation/package.json +8 -8
  94. package/templates/apps/api-observability/package.json +8 -8
  95. package/templates/apps/api-ratelimit/package.json +8 -8
  96. package/templates/apps/api-rbac/package.json +8 -8
  97. package/templates/apps/api-rest/package.json +7 -7
  98. package/templates/apps/api-saas/package.json +11 -11
  99. package/templates/apps/api-saas-starter/package.json +10 -10
  100. package/templates/apps/api-search/package.json +8 -8
  101. package/templates/apps/api-status/package.json +8 -8
  102. package/templates/apps/api-versioning/package.json +8 -8
  103. package/templates/apps/api-webhooks/package.json +9 -9
  104. package/templates/apps/changelog/package.json +6 -6
  105. package/templates/apps/edge-functions/package.json +2 -2
  106. package/templates/apps/frontend-admin/package.json +8 -8
  107. package/templates/apps/frontend-app/package.json +9 -9
  108. package/templates/apps/frontend-auth/package.json +8 -8
  109. package/templates/apps/frontend-blank/package.json +7 -7
  110. package/templates/apps/frontend-cms/package.json +9 -9
  111. package/templates/apps/frontend-collab/package.json +10 -10
  112. package/templates/apps/frontend-contact/package.json +7 -7
  113. package/templates/apps/frontend-dashboard/package.json +7 -7
  114. package/templates/apps/frontend-docs/package.json +7 -7
  115. package/templates/apps/frontend-i18n/package.json +6 -6
  116. package/templates/apps/frontend-landing/package.json +7 -7
  117. package/templates/apps/frontend-portal/package.json +8 -8
  118. package/templates/apps/frontend-saas/package.json +8 -8
  119. package/templates/apps/frontend-spa/package.json +7 -7
  120. package/templates/apps/frontend-ssr/package.json +7 -7
  121. package/templates/apps/frontend-ssr-api/package.json +8 -8
  122. package/templates/apps/frontend-static-blog/package.json +6 -6
  123. package/templates/apps/frontend-status/package.json +8 -8
  124. package/templates/apps/mobile-app/package.json +3 -3
  125. package/dist/apiBuild-BICVGnEC.js +0 -2
  126. package/dist/appGraph-CvQCte0z.js +0 -2
  127. package/dist/dbCommand-CpdKLeQq.js +0 -2
  128. package/dist/doctorCommand-BvqGBwNG.js +0 -2
  129. package/dist/frameworkTableAssembly-BGHmck-x.js +0 -2
  130. package/dist/frameworkTableAssembly-DkkP6BgC.js +0 -674
  131. package/dist/inspect-C_T_WGvl.js +0 -2
  132. package/dist/manifestBuild-ChsKAhmn.js +0 -2
  133. package/dist/scheduleCmd--jksTrf6.js +0 -69
  134. package/dist/serveCommand-B_isw7q4.js +0 -1647
  135. package/dist/serveCommand-DOvbgRnQ.js +0 -2
  136. package/dist/updateCommand-us1_hdIC.js +0 -2
  137. package/dist/webDev-Cg-fFiyd2.js +0 -2
@@ -0,0 +1,122 @@
1
+ import { n as e } from "./cliOutput-D1tSBoRM.js";
2
+ import { n as t, o as n, r, t as i } from "./inspectFetch-EMuhTG_9.js";
3
+ import { ansi as a } from "@voltro/logger";
4
+ //#region src/probeCommand.ts
5
+ var o = () => {
6
+ console.log(`${a.bold("voltro probe")} — ask a RUNNING app whether its declared access is enforced
7
+
8
+ ${a.bold("access")} call every guarded procedure with NO credentials and
9
+ report any that answer anyway
10
+
11
+ Options
12
+ --process NAME target one app by name (default: every live api)
13
+ --url URL address a process directly instead of via the local registry
14
+ --strict also fail on INCONCLUSIVE (recommended in CI)
15
+ --format pretty|json
16
+
17
+ What it does NOT do, stated because the opposite is the natural assumption:
18
+ it probes ANONYMOUSLY, so it cannot tell one scope from another. It answers
19
+ exactly one question — does a procedure that DECLARES a guard admit a caller
20
+ that presents nothing? A guard declared and never bound is the failure it finds.
21
+
22
+ The target must expose the inspect surface: set ${a.bold("VOLTRO_INSPECT_TOKEN")} on the
23
+ server and in this shell.`);
24
+ }, s = (e) => e.filter((e) => (e.guards ?? []).some((e) => e.kind !== "open")), c = (e) => {
25
+ if (e.kind === "err") return {
26
+ verdict: "inconclusive",
27
+ detail: "transport failure — the app did not answer"
28
+ };
29
+ let t = e.status ?? 0;
30
+ if (t === 401 || t === 403) return {
31
+ verdict: "refused",
32
+ detail: `refused (HTTP ${t})`
33
+ };
34
+ let n = Array.isArray(e.data) ? e.data : [], r = [], i = !1;
35
+ for (let e of n) {
36
+ if (e._tag === "Defect") return {
37
+ verdict: "inconclusive",
38
+ detail: `the app answered a defect (${e.defect?.name ?? "Defect"}) — the call never reached the guard`
39
+ };
40
+ for (let t of e.values ?? []) if (t._tag === "error") {
41
+ let e = t.error?._tag;
42
+ e !== void 0 && r.push(e);
43
+ } else i = !0;
44
+ }
45
+ let a = r.find((e) => e === "ScopeError" || e === "Unauthorized" || e === "AuthError");
46
+ return a === void 0 ? r.length > 0 ? {
47
+ verdict: "inconclusive",
48
+ detail: `answered '${r[0]}' — not an access refusal, so the guard was not reached`
49
+ } : i ? {
50
+ verdict: "admitted",
51
+ detail: "ANSWERED an unauthenticated call"
52
+ } : {
53
+ verdict: "inconclusive",
54
+ detail: n.length === 0 ? `HTTP ${t} with no rpc envelope — the app did not speak the wire protocol` : `HTTP ${t} with no value and no error — nothing to judge`
55
+ } : {
56
+ verdict: "refused",
57
+ detail: `refused (${a})`
58
+ };
59
+ }, l = 0, u = (e) => ({
60
+ _tag: "Request",
61
+ id: String(++l),
62
+ tag: e,
63
+ payload: {},
64
+ headers: []
65
+ }), d = async (e, t) => {
66
+ let { verdict: n, detail: r } = c(await i(e, "/rpc", {
67
+ method: "POST",
68
+ body: u(t.tag),
69
+ anonymous: !0
70
+ }));
71
+ return {
72
+ tag: t.tag,
73
+ kind: t.kind,
74
+ verdict: n,
75
+ detail: r
76
+ };
77
+ }, f = (t, n, r, i) => {
78
+ let o = n.filter((e) => e.verdict === "admitted"), s = n.filter((e) => e.verdict === "inconclusive"), c = n.filter((e) => e.verdict === "refused");
79
+ if (r === "json") e({
80
+ app: t,
81
+ probed: n.length,
82
+ refused: c.length,
83
+ admitted: o.length,
84
+ inconclusive: s.length,
85
+ results: n
86
+ });
87
+ else if (n.length === 0) console.log(`${a.yellow("○")} ${t}: no guarded procedure to probe — every route is openAccess or undecided.`), console.log(" Undecided routes are `voltro check`'s finding, not this one's.");
88
+ else {
89
+ console.log(`${a.bold(t)}: probed ${n.length} guarded procedure(s) with NO credentials`);
90
+ for (let e of o) console.log(` ${a.red("✗")} ${a.bold(e.tag)} — ${e.detail}`);
91
+ for (let e of s) console.log(` ${a.yellow("?")} ${e.tag} — ${e.detail}`);
92
+ console.log(` ${a.green("✓")} ${c.length} refused · ${a.yellow(String(s.length))} inconclusive · ${a.red(String(o.length))} admitted`);
93
+ }
94
+ return o.length > 0 ? (r !== "json" && console.error(`\n${a.red("A declared guard did not refuse an anonymous caller.")}\n The declaration and the behaviour disagree — the guard is on the descriptor and
95
+ nothing enforces it on this path. Check that the procedure is bound through the
96
+ dispatch spine (not a hand-mounted route), and that its guard list is not empty.`), 1) : i && s.length > 0 ? (r !== "json" && console.error(`\n${a.yellow("--strict:")} ${s.length} procedure(s) could not be judged.\n Their calls failed before the guard was reached (usually payload validation).
97
+ That is not evidence of enforcement, so under --strict it is not a pass.`), 1) : 0;
98
+ }, p = async (e) => {
99
+ let o = t(e);
100
+ if ("error" in o) return console.error(`voltro probe access: ${o.error}`), 1;
101
+ let { format: c } = o, { process: l, target: u, rest: p, error: m } = r(o.rest);
102
+ if (m !== void 0) return console.error(`voltro probe access: ${m}`), 1;
103
+ let h = p.includes("--strict"), g = await n(l, u);
104
+ if (g.length === 0) return console.error("voltro probe access: no live api found.\n This command asks a RUNNING app what it does — there is nothing to ask a source tree.\n Start one (`voltro dev` / `voltro serve`), or address it directly with --url."), 1;
105
+ let _ = 0;
106
+ for (let e of g) {
107
+ let t = await i(e, "/_voltro/inspect/manifest");
108
+ if (t.kind === "err" || t.status < 200 || t.status >= 300) {
109
+ console.error(`${a.red("✗")} ${e.name}: could not read the manifest — ${t.kind === "err" ? t.error : `HTTP ${t.status}`}`), t.kind === "ok" && t.status === 401 && console.error(" Set VOLTRO_INSPECT_TOKEN on the server and in this shell."), _ = 1;
110
+ continue;
111
+ }
112
+ let n = s(t.data?.procedures ?? []), r = [];
113
+ for (let t of n) r.push(await d(e, t));
114
+ _ = Math.max(_, f(e.name, r, c, h));
115
+ }
116
+ return _;
117
+ }, m = async (e) => {
118
+ let [t, ...n] = e;
119
+ return t === void 0 || t === "--help" || t === "-h" ? (o(), +(t === void 0)) : t === "access" ? p(n) : (console.error(`voltro probe: unknown subcommand "${t}" (expected: access)`), 1);
120
+ };
121
+ //#endregion
122
+ export { m as runProbeCommand };
@@ -1,4 +1,4 @@
1
- import { n as e } from "./apiBuild-DZF_f0_I.js";
1
+ import { n as e } from "./apiBuild-DYD_ONLD.js";
2
2
  import { join as t, resolve as n, sep as r } from "node:path";
3
3
  import { existsSync as i, promises as a } from "node:fs";
4
4
  import { createLogger as o } from "@voltro/logger";
@@ -0,0 +1,149 @@
1
+ import { t as e } from "./cliArgs-D4p8n7EE.js";
2
+ import { n as t } from "./cliOutput-D1tSBoRM.js";
3
+ import { n, o as r, r as i, t as a } from "./inspectFetch-EMuhTG_9.js";
4
+ import { ansi as o } from "@voltro/logger";
5
+ //#region src/scheduleCmd.ts
6
+ var s = () => {
7
+ console.log(`${o.bold("voltro schedule")} — operate on registered schedules
8
+
9
+ ${o.bold("run <name>")} fire the named schedule once, now
10
+ ${o.bold("backfill <name>")} fire every occurrence in an explicit range
11
+ (boot backfill only walks from the last recorded run, and
12
+ cluster cron caps its own catch-up at one day — a longer
13
+ outage needs this verb)
14
+
15
+ Options
16
+ --process NAME target one app by name (default: every live api)
17
+ --trigger manual|external
18
+ (run) how the run is recorded (default: manual)
19
+ --from ISO --to ISO
20
+ (backfill) the range, half-open (from, to]
21
+ --yes (backfill) confirm a range above the server's threshold
22
+ --limit N (backfill) raise the per-request occurrence cap
23
+ --format pretty|json
24
+ --url URL address a process directly instead of via the local registry
25
+
26
+ Examples
27
+ voltro schedule run nightly-reconcile
28
+ voltro schedule backfill nightly-reconcile --from 2026-08-01T00:00:00Z --to 2026-08-05T00:00:00Z
29
+ voltro schedule backfill hourly-sync --from 2026-08-10T00:00:00Z --to 2026-08-12T00:00:00Z --yes
30
+
31
+ The target must expose the inspect surface: set ${o.bold("VOLTRO_INSPECT_TOKEN")} on the
32
+ server and in this shell. \`voltro dev\` mints one per project; \`serve\` / \`start\`
33
+ never mint — inspect stays closed until an operator opens it.`);
34
+ }, c = (e, n, r, i) => {
35
+ if (i === "json") {
36
+ t({
37
+ app: e,
38
+ schedule: n,
39
+ runId: r
40
+ });
41
+ return;
42
+ }
43
+ if (r === null) {
44
+ console.log(`${o.yellow("○")} ${e}: ${o.bold(n)} not started — no run id.\n Another replica holds the coordination lock, or the previous run is still going and this schedule's onOverlap is 'skip'.`);
45
+ return;
46
+ }
47
+ console.log(`${o.green("✓")} ${e}: ${o.bold(n)} fired — run ${o.bold(r)}`), console.log(` watch it: voltro inspect schedules --process ${e}`);
48
+ }, l = (e, t) => {
49
+ let n = e.indexOf(t);
50
+ if (n === -1) return;
51
+ let r = e[n + 1];
52
+ return r !== void 0 && !r.startsWith("--") ? r : void 0;
53
+ }, u = [
54
+ "--from",
55
+ "--to",
56
+ "--limit",
57
+ "--format",
58
+ "--process",
59
+ "--url",
60
+ "--token"
61
+ ], d = async (s) => {
62
+ let c = e(s, u);
63
+ if (c === void 0) return console.error("voltro schedule backfill: a schedule name is required — `voltro schedule backfill <name> --from <iso> --to <iso>`"), 1;
64
+ let d = n(s);
65
+ if ("error" in d) return console.error(`voltro schedule backfill: ${d.error}`), 1;
66
+ let { format: f } = d, { process: p, target: m, rest: h, error: g } = i(d.rest);
67
+ if (g !== void 0) return console.error(`voltro schedule backfill: ${g}`), 1;
68
+ let _ = l(h, "--from"), v = l(h, "--to");
69
+ if (_ === void 0 || v === void 0) return console.error("voltro schedule backfill: both --from and --to are required (ISO instants, e.g. 2026-08-01T00:00:00Z)"), 1;
70
+ let y = l(h, "--limit"), b = y === void 0 ? void 0 : Number(y);
71
+ if (b !== void 0 && (!Number.isFinite(b) || b <= 0)) return console.error(`voltro schedule backfill: --limit must be a positive number (got ${y})`), 1;
72
+ let x = h.includes("--yes"), S = await r(p, m);
73
+ if (S.length === 0) return console.error("voltro schedule backfill: no live api found.\n Start one (`voltro dev` / `voltro serve`), or address it directly with --url."), 1;
74
+ let C = 0;
75
+ for (let e of S) {
76
+ let n = await a(e, `/_voltro/inspect/schedules/${encodeURIComponent(c)}/backfill`, {
77
+ method: "POST",
78
+ body: {
79
+ from: _,
80
+ to: v,
81
+ confirm: x,
82
+ ...b === void 0 ? {} : { limit: b }
83
+ }
84
+ });
85
+ if (n.kind === "err") {
86
+ C += 1, console.error(`${o.red("✗")} ${e.name}: ${n.error}`);
87
+ continue;
88
+ }
89
+ let r = n.data;
90
+ if (n.status === 409 && r?.kind === "refused") {
91
+ if (C += 1, f === "json") {
92
+ t({
93
+ app: e.name,
94
+ ...r
95
+ });
96
+ continue;
97
+ }
98
+ console.error(`${o.yellow("○")} ${e.name}: refused — ${r.reason}`);
99
+ continue;
100
+ }
101
+ if (n.status < 200 || n.status >= 300) {
102
+ C += 1;
103
+ let t = r?.message === void 0 ? r?.error === void 0 ? `HTTP ${n.status}` : String(r.error) : String(r.message);
104
+ console.error(`${o.red("✗")} ${e.name}: ${t}`), n.status === 401 && console.error(" Set VOLTRO_INSPECT_TOKEN on the server and in this shell.");
105
+ continue;
106
+ }
107
+ if (f === "json") {
108
+ t({
109
+ app: e.name,
110
+ ...r
111
+ });
112
+ continue;
113
+ }
114
+ console.log(`${o.green("✓")} ${e.name}: ${o.bold(c)} backfilled — ${r?.fired ?? 0}/${r?.occurrences ?? 0} firing(s) over ${_} .. ${v}`), console.log(` review the runs: voltro inspect schedules --process ${e.name}`);
115
+ }
116
+ return +(C > 0);
117
+ }, f = async (t) => {
118
+ let [l, ...f] = t;
119
+ if (l === void 0 || l === "--help" || l === "-h") return s(), +(l === void 0);
120
+ if (l === "backfill") return d(f);
121
+ if (l !== "run") return console.error(`voltro schedule: unknown subcommand "${l}" (expected: run | backfill)`), 1;
122
+ let p = e(f, u);
123
+ if (p === void 0) return console.error("voltro schedule run: a schedule name is required — `voltro schedule run <name>`"), 1;
124
+ let m = n(f);
125
+ if ("error" in m) return console.error(`voltro schedule run: ${m.error}`), 1;
126
+ let { format: h } = m, { process: g, target: _, rest: v, error: y } = i(m.rest);
127
+ if (y !== void 0) return console.error(`voltro schedule run: ${y}`), 1;
128
+ let b = v[v.indexOf("--trigger") + 1], x = v.includes("--trigger") && b === "external" ? "external" : "manual", S = await r(g, _);
129
+ if (S.length === 0) return console.error("voltro schedule run: no live api found.\n Start one (`voltro dev` / `voltro serve`), or address it directly with --url."), 1;
130
+ let C = 0;
131
+ for (let e of S) {
132
+ let t = await a(e, `/_voltro/inspect/schedules/${encodeURIComponent(p)}/fire?trigger=${x}`, { method: "POST" });
133
+ if (t.kind === "err") {
134
+ C += 1, console.error(`${o.red("✗")} ${e.name}: ${t.error}`);
135
+ continue;
136
+ }
137
+ if (t.status < 200 || t.status >= 300) {
138
+ C += 1;
139
+ let n = t.data, r = n?.message === void 0 ? n?.error === void 0 ? `HTTP ${t.status}` : String(n.error) : String(n.message);
140
+ console.error(`${o.red("✗")} ${e.name}: ${r}`), t.status === 401 && console.error(" Set VOLTRO_INSPECT_TOKEN on the server and in this shell."), t.status === 404 && console.error(" A 404 here means the app is older than this endpoint, or it is a web app (schedules are api-only).");
141
+ continue;
142
+ }
143
+ let n = t.data?.runId ?? null;
144
+ c(e.name, p, n, h);
145
+ }
146
+ return +(C > 0);
147
+ };
148
+ //#endregion
149
+ export { f as runScheduleCommand };
@@ -422,7 +422,7 @@ let package = Package(
422
422
  await l.mkdir(o(e), { recursive: !0 }), await l.writeFile(e, n.content, "utf8");
423
423
  }
424
424
  }, X = async (e) => {
425
- let { offlineManifest: t } = await import("./checkCommand-YZDmkAGd.js"), { manifest: n } = await t(e);
425
+ let { offlineManifest: t } = await import("./checkCommand-DKpDLlqu.js"), { manifest: n } = await t(e);
426
426
  return n;
427
427
  }, Z = async (e) => {
428
428
  let t = J(await (e.manifestProvider ?? X)(e.root), {
@@ -2,15 +2,15 @@ import { n as e } from "./appModuleLoader-C9r9mxZt.js";
2
2
  import { promises as t } from "node:fs";
3
3
  import { Cause as n, Effect as r } from "effect";
4
4
  import { SqlClient as i } from "@effect/sql";
5
- import { DEFAULT_CDC_CHANNEL as a, VOLTRO_MIGRATION_LOCK_KEY as o, detectReactiveTriggerDrift as s, reactiveTriggerRepairSql as c } from "@voltro/database/sql";
6
- import { _voltroSeedsTable as l, and as u, eq as d, generateId as f, isSeedDefinition as p, setSchemaChangeSeedHook as m, setTenantCreateSeedHook as h } from "@voltro/database";
7
- import { defineSchedule as g } from "@voltro/runtime";
8
- import { createHash as _ } from "node:crypto";
5
+ import { DEFAULT_CDC_CHANNEL as a, detectReactiveTriggerDrift as o, migrationLockKeyForSchema as s, reactiveTriggerRepairSql as c, resolveMigrationLockSchema as l } from "@voltro/database/sql";
6
+ import { _voltroSeedsTable as u, and as d, eq as f, generateId as p, isSeedDefinition as m, setSchemaChangeSeedHook as h, setTenantCreateSeedHook as g } from "@voltro/database";
7
+ import { defineSchedule as _ } from "@voltro/runtime";
8
+ import { createHash as v } from "node:crypto";
9
9
  //#region src/reactiveTriggerBoot.ts
10
- var v = (e, t = process.env) => {
10
+ var y = (e, t = process.env) => {
11
11
  let n = (t.VOLTRO_REACTIVE_TRIGGERS ?? "").trim().toLowerCase(), r = n === "repair" || n === "report" || n === "off" ? n : e ?? "repair";
12
12
  return r === "repair" && t.VOLTRO_AUTO_MIGRATE === "0" ? "report" : r;
13
- }, y = (e, t, n = a) => r.map(s(e, t, n), (e) => ({
13
+ }, b = (e, t, n = a) => r.map(o(e, t, n), (e) => ({
14
14
  drift: e,
15
15
  statements: c({
16
16
  tables: t,
@@ -18,28 +18,31 @@ var v = (e, t = process.env) => {
18
18
  channel: n,
19
19
  ...e
20
20
  })
21
- })), b = (e, t) => r.gen(function* () {
22
- let n = yield* i.SqlClient, { drift: s, statements: c } = yield* y(n, e, t.channel ?? a), l = {
23
- missing: s.missing.length,
24
- stale: s.stale.length,
21
+ })), x = (e, t) => r.gen(function* () {
22
+ let n = yield* i.SqlClient, { drift: o, statements: c } = yield* b(n, e, t.channel ?? a), u = {
23
+ missing: o.missing.length,
24
+ stale: o.stale.length,
25
25
  statements: c.length
26
26
  };
27
- return c.length === 0 ? {
27
+ if (c.length === 0) return {
28
28
  kind: "clean",
29
- ...l
30
- } : t.mode === "report" ? {
29
+ ...u
30
+ };
31
+ if (t.mode === "report") return {
31
32
  kind: "reported",
32
- ...l
33
- } : (yield* n`
34
- SELECT pg_try_advisory_lock(${o.toString()}::bigint) AS got`)[0]?.got === !0 ? yield* r.gen(function* () {
33
+ ...u
34
+ };
35
+ let d = s(l({ schema: t.lockSchema }));
36
+ return (yield* n`
37
+ SELECT pg_try_advisory_lock(${d.toString()}::bigint) AS got`)[0]?.got === !0 ? yield* r.gen(function* () {
35
38
  for (let e of c) yield* n.unsafe(e);
36
39
  return {
37
40
  kind: "repaired",
38
- ...l
41
+ ...u
39
42
  };
40
- }).pipe(r.ensuring(n`SELECT pg_advisory_unlock(${o.toString()}::bigint)`.pipe(r.catchAll(() => r.void)))) : {
43
+ }).pipe(r.ensuring(n`SELECT pg_advisory_unlock(${d.toString()}::bigint)`.pipe(r.catchAll(() => r.void)))) : {
41
44
  kind: "lock-held",
42
- ...l
45
+ ...u
43
46
  };
44
47
  }).pipe(r.catchAllCause((e) => r.succeed({
45
48
  kind: "failed",
@@ -47,7 +50,7 @@ var v = (e, t = process.env) => {
47
50
  stale: 0,
48
51
  statements: 0,
49
52
  detail: n.pretty(e)
50
- }))), x = async (e) => {
53
+ }))), S = async (e) => {
51
54
  let t = {
52
55
  kind: "skipped",
53
56
  missing: 0,
@@ -55,9 +58,10 @@ var v = (e, t = process.env) => {
55
58
  statements: 0
56
59
  };
57
60
  if (e.dialectId !== "postgres" || e.mode === "off" || e.tables.length === 0) return t;
58
- let n = await e.makeLayer(), i = await r.runPromise(b(e.tables, {
61
+ let n = await e.makeLayer(), i = await r.runPromise(x(e.tables, {
59
62
  mode: e.mode,
60
- ...e.channel === void 0 ? {} : { channel: e.channel }
63
+ ...e.channel === void 0 ? {} : { channel: e.channel },
64
+ ...e.lockSchema === void 0 ? {} : { lockSchema: e.lockSchema }
61
65
  }).pipe(r.provide(n))).catch((e) => ({
62
66
  kind: "failed",
63
67
  missing: 0,
@@ -90,7 +94,7 @@ var v = (e, t = process.env) => {
90
94
  default: break;
91
95
  }
92
96
  return i;
93
- }, S = async (n, r) => {
97
+ }, C = async (n, r) => {
94
98
  let i = [];
95
99
  for (let a of n) {
96
100
  let n;
@@ -100,12 +104,12 @@ var v = (e, t = process.env) => {
100
104
  r.warn("seed: failed to import", { file: a }, e);
101
105
  continue;
102
106
  }
103
- let o = (n.default && p(n.default) ? n.default : void 0) ?? Object.values(n).find(p);
107
+ let o = (n.default && m(n.default) ? n.default : void 0) ?? Object.values(n).find(m);
104
108
  if (!o) {
105
109
  r.warn("seed: no defineSeed export", { file: a });
106
110
  continue;
107
111
  }
108
- let s = await t.readFile(a, "utf8").catch(() => ""), c = o.fingerprint ? o.fingerprint({ src: s }) : _("sha256").update(s).digest("hex").slice(0, 16);
112
+ let s = await t.readFile(a, "utf8").catch(() => ""), c = o.fingerprint ? o.fingerprint({ src: s }) : v("sha256").update(s).digest("hex").slice(0, 16);
109
113
  i.push({
110
114
  file: a,
111
115
  definition: o,
@@ -113,7 +117,7 @@ var v = (e, t = process.env) => {
113
117
  });
114
118
  }
115
119
  return i;
116
- }, C = (e) => ({
120
+ }, w = (e) => ({
117
121
  query: (t) => e.query({
118
122
  table: t.table,
119
123
  order: t.order ?? [],
@@ -126,14 +130,14 @@ var v = (e, t = process.env) => {
126
130
  insertIgnore: (t, n, r) => e.insertIgnore(t, n, r),
127
131
  update: (t, n, r) => e.update(t, n, r),
128
132
  delete: (t, n) => e.delete(t, n)
129
- }), w = (e) => async (t, n, r) => {
133
+ }), T = (e) => async (t, n, r) => {
130
134
  let i = Object.keys(n);
131
135
  if (i.length === 0) throw Error("upsertByUnique: matchFields must have at least one key");
132
136
  let a;
133
137
  if (i.length === 1) {
134
138
  let e = i[0];
135
- a = d(e, n[e]);
136
- } else a = u(...i.map((e) => d(e, n[e])));
139
+ a = f(e, n[e]);
140
+ } else a = d(...i.map((e) => f(e, n[e])));
137
141
  let o = (await e.query({
138
142
  table: t,
139
143
  order: [],
@@ -150,30 +154,30 @@ var v = (e, t = process.env) => {
150
154
  row: await e.insert(t, r),
151
155
  created: !0
152
156
  };
153
- }, T = (e, t) => {
154
- let n = w(e);
157
+ }, E = (e, t) => {
158
+ let n = T(e);
155
159
  return {
156
- store: C(e),
160
+ store: w(e),
157
161
  upsertByUnique: n,
158
162
  progress: (e, n) => t.debug("seed progress", {
159
163
  done: e,
160
164
  total: n
161
165
  })
162
166
  };
163
- }, E = "_voltro_seeds", D = () => {
164
- let e = l.fields.id?.idScheme;
165
- return (e ? f(e, E) : null) ?? f({
167
+ }, D = "_voltro_seeds", O = () => {
168
+ let e = u.fields.id?.idScheme;
169
+ return (e ? p(e, D) : null) ?? p({
166
170
  kind: "typeid",
167
171
  prefix: "seed"
168
- }, E);
169
- }, O = {
172
+ }, D);
173
+ }, k = {
170
174
  isUnchanged: () => !1,
171
175
  record: async () => {}
172
- }, k = (e, t) => `${e.definition.id}${t}`, A = async (e, t, n = "") => {
176
+ }, A = (e, t) => `${e.definition.id}${t}`, j = async (e, t, n = "") => {
173
177
  let r;
174
178
  try {
175
179
  r = await e.query({
176
- table: E,
180
+ table: D,
177
181
  order: [],
178
182
  predicate: void 0,
179
183
  projection: void 0,
@@ -182,19 +186,19 @@ var v = (e, t = process.env) => {
182
186
  });
183
187
  } catch (e) {
184
188
  return t.warn("seeds: ledger unreadable — nothing will be skipped and nothing recorded", {
185
- table: E,
189
+ table: D,
186
190
  error: String(e)
187
- }), O;
191
+ }), k;
188
192
  }
189
193
  let i = new Map(r.map((e) => [e.name, e]));
190
194
  return {
191
195
  isUnchanged: (e) => {
192
- let t = i.get(k(e, n));
196
+ let t = i.get(A(e, n));
193
197
  return t?.lastRunStatus === "succeeded" && t.fingerprint === e.fingerprint;
194
198
  },
195
199
  record: async (r, a) => {
196
200
  let o = {
197
- name: k(r, n),
201
+ name: A(r, n),
198
202
  lifecycle: r.definition.lifecycle,
199
203
  fingerprint: r.fingerprint,
200
204
  lastRunStatus: a.status,
@@ -204,22 +208,22 @@ var v = (e, t = process.env) => {
204
208
  lastError: a.error?.message ?? null
205
209
  };
206
210
  try {
207
- let t = i.get(k(r, n));
208
- t ? await e.update(E, t.id, o) : await e.insert(E, {
209
- id: D(),
211
+ let t = i.get(A(r, n));
212
+ t ? await e.update(D, t.id, o) : await e.insert(D, {
213
+ id: O(),
210
214
  ...o
211
215
  });
212
216
  } catch (e) {
213
217
  t.warn("seeds: could not record the ledger row — this seed will re-run on every boot", {
214
218
  seedId: r.definition.id,
215
- table: E,
219
+ table: D,
216
220
  error: String(e)
217
221
  });
218
222
  }
219
223
  }
220
224
  };
221
- }, j = async (e, t, n) => {
222
- let r = Date.now(), i = T(t, n), a = 0;
225
+ }, M = async (e, t, n) => {
226
+ let r = Date.now(), i = E(t, n), a = 0;
223
227
  for (let t of e.definition.steps) try {
224
228
  let e = await t.body(i);
225
229
  e && typeof e.rowsTouched == "number" && (a += e.rowsTouched);
@@ -246,10 +250,10 @@ var v = (e, t = process.env) => {
246
250
  durationMs: Date.now() - r,
247
251
  rowsTouched: a
248
252
  };
249
- }, M = async (e, t, n, r = {}) => {
253
+ }, N = async (e, t, n, r = {}) => {
250
254
  let i = e.filter((e) => e.definition.lifecycle === "boot");
251
255
  if (i.length === 0) return [];
252
- let a = await A(t, n), o = [], s = 0;
256
+ let a = await j(t, n), o = [], s = 0;
253
257
  for (let e of i) {
254
258
  if (!r.force && a.isUnchanged(e)) {
255
259
  s++, n.debug(`seed "${e.definition.id}" unchanged — skipped`, { fingerprint: e.fingerprint }), o.push({
@@ -260,7 +264,7 @@ var v = (e, t = process.env) => {
260
264
  });
261
265
  continue;
262
266
  }
263
- let i = await j(e, t, n);
267
+ let i = await M(e, t, n);
264
268
  i.status === "succeeded" && n.info(`seed "${i.seedId}" applied`, {
265
269
  durationMs: i.durationMs,
266
270
  rowsTouched: i.rowsTouched,
@@ -272,19 +276,19 @@ var v = (e, t = process.env) => {
272
276
  skipped: s,
273
277
  ...r.force ? { forced: !0 } : {}
274
278
  }), o;
275
- }, N = async (e, t, n, r) => {
279
+ }, P = async (e, t, n, r) => {
276
280
  let i = e.find((e) => e.definition.id === t);
277
281
  if (!i) throw Error(`seed not found: ${t}`);
278
- let a = await j(i, n, r);
279
- return await (await A(n, r)).record(i, a), a;
280
- }, P = async (e, t, n, r) => {
282
+ let a = await M(i, n, r);
283
+ return await (await j(n, r)).record(i, a), a;
284
+ }, F = async (e, t, n, r) => {
281
285
  let i = e.filter((e) => e.definition.lifecycle === "onTenantCreate");
282
286
  if (i.length === 0) return [];
283
287
  let a = n.withNamespace?.(t);
284
288
  if (a == null) throw Error(`onTenantCreate seeds (${i.map((e) => e.definition.id).join(", ")}) cannot run for namespace "${t}": this store has no namespace scoping, so the only place to write them would be the SHARED tables — one tenant's fixture in every tenant's data. Refusing. Namespace isolation needs a dialect that has it (postgres schemas, mysql databases, mssql schemas); on a single-namespace deployment declare the seed as \`lifecycle: 'boot'\`.`);
285
- let o = await A(n, r, `@${t}`), s = [];
289
+ let o = await j(n, r, `@${t}`), s = [];
286
290
  for (let e of i) {
287
- let n = await j(e, a, r);
291
+ let n = await M(e, a, r);
288
292
  n.status === "succeeded" && r.info(`seed "${n.seedId}" applied to new tenant namespace`, {
289
293
  namespace: t,
290
294
  durationMs: n.durationMs,
@@ -297,12 +301,12 @@ var v = (e, t = process.env) => {
297
301
  namespace: t,
298
302
  ran: s.length
299
303
  }), s;
300
- }, F = async (e, t, n, r) => {
304
+ }, I = async (e, t, n, r) => {
301
305
  let i = new Set(t), a = e.filter((e) => e.definition.lifecycle === "onSchemaChange" && (e.definition.watchedTables ?? []).some((e) => i.has(e)));
302
306
  if (a.length === 0) return [];
303
- let o = await A(n, r), s = [];
307
+ let o = await j(n, r), s = [];
304
308
  for (let e of a) {
305
- let t = await j(e, n, r);
309
+ let t = await M(e, n, r);
306
310
  t.status === "succeeded" && r.info(`seed "${t.seedId}" applied after a schema change`, {
307
311
  watchedTables: e.definition.watchedTables,
308
312
  rowsTouched: t.rowsTouched
@@ -310,20 +314,20 @@ var v = (e, t = process.env) => {
310
314
  }
311
315
  let c = s.filter((e) => e.status === "failed");
312
316
  return c.length > 0 && r.warn("one or more onSchemaChange seeds failed (the schema apply itself succeeded)", { failedIds: c.map((e) => e.seedId) }), s;
313
- }, I = (e, t, n) => (h(async (r) => {
314
- await P(e, r.namespace, t, n);
315
- }), m(async (r) => {
316
- await F(e, r.changedTables, t, n);
317
+ }, L = (e, t, n) => (g(async (r) => {
318
+ await F(e, r.namespace, t, n);
319
+ }), h(async (r) => {
320
+ await I(e, r.changedTables, t, n);
317
321
  }), () => {
318
- h(null), m(null);
319
- }), L = "seed:", R = (e, t, n) => e.filter((e) => e.definition.lifecycle === "cron").map((r) => g({
320
- name: `${L}${r.definition.id}`,
322
+ g(null), h(null);
323
+ }), R = "seed:", z = (e, t, n) => e.filter((e) => e.definition.lifecycle === "cron").map((r) => _({
324
+ name: `${R}${r.definition.id}`,
321
325
  cron: r.definition.cron ?? "",
322
326
  timezone: r.definition.timezone ?? "UTC",
323
327
  description: `seed "${r.definition.name}" (${r.file})`,
324
328
  handler: async () => {
325
- await N(e, r.definition.id, t, n);
329
+ await P(e, r.definition.id, t, n);
326
330
  }
327
331
  }));
328
332
  //#endregion
329
- export { R as a, v as c, N as i, S as n, x as o, M as r, y as s, I as t };
333
+ export { z as a, y as c, P as i, C as n, S as o, N as r, b as s, L as t };
@@ -0,0 +1,2 @@
1
+ import { n as e } from "./serveCommand-iwlUBNS1.js";
2
+ export { e as runServeDispatch };