@voltro/cli 0.50.1 → 0.52.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 (170) hide show
  1. package/CHANGELOG.md +231 -0
  2. package/dist/{agentsMd-D6yD7IQv.js → agentsMd-0l980yhL.js} +4 -1
  3. package/dist/agentsMd-SDDSkyl4.js +2 -0
  4. package/dist/apiBuild-BYBpL7Pz.js +2 -0
  5. package/dist/{apiBuild-D3s7lx57.js → apiBuild-CSFI8QGq.js} +3 -3
  6. package/dist/appGraph-CFmFKHb2.js +2 -0
  7. package/dist/appGraph-yRuKjOba.js +111 -0
  8. package/dist/bin.js +1 -1
  9. package/dist/build-CPgcMQug.js +793 -0
  10. package/dist/checkCommand-2SbqzukH.js +2 -0
  11. package/dist/{checkCommand-DsKIjL6k.js → checkCommand-COmqc2cB.js} +107 -60
  12. package/dist/{cloudCmd-F4YJeqM3.js → cloudCmd-QUXh-b5w.js} +1 -1
  13. package/dist/codegen-CctkDO-1.js +2 -0
  14. package/dist/{codegen-CrMXs4hb.js → codegen-VF479Cnb.js} +1 -1
  15. package/dist/{codegenCommand-C0S27nbB.js → codegenCommand-DCdG2JN-.js} +12 -12
  16. package/dist/{codemodRunner-DwY8Rjbl.js → codemodRunner-r7J9lIa7.js} +771 -636
  17. package/dist/{commands-Brp4DI8u.js → commands-Cc_nV8WI.js} +37 -36
  18. package/dist/{dashboardCommand-bbLfc9FW.js → dashboardCommand-C-gKvwqh.js} +3 -3
  19. package/dist/{dataCommand-k9mXTXin.js → dataCommand-BgpBHnlB.js} +299 -253
  20. package/dist/dbCommand-DNb6yeOG.js +2 -0
  21. package/dist/{dbCommand-Bf7Cy0mn.js → dbCommand-sHedr-NJ.js} +3 -3
  22. package/dist/dev--A3nsxA3.js +3 -0
  23. package/dist/{dev-C0cbqysu.js → dev-CRHoCEiy.js} +2258 -2219
  24. package/dist/doctorCommand-CqoWA2p5.js +2 -0
  25. package/dist/{doctorCommand-DyViaaCP.js → doctorCommand-DtfJ3FA6.js} +314 -234
  26. package/dist/{dormancyCommand-DMZPA_HW.js → dormancyCommand-Drn7o0No.js} +1 -1
  27. package/dist/{embeddingsCommand-CMxRPkg6.js → embeddingsCommand-Z-jO1fWN.js} +1 -1
  28. package/dist/{envCommand-CDLr-yD4.js → envCommand-D4gCrrTZ.js} +15 -15
  29. package/dist/{evolveCommand-Ct4NesL3.js → evolveCommand-CMROeKeA.js} +2 -2
  30. package/dist/fileConventions-DOqD3lPS.js +34 -0
  31. package/dist/{fileTaxonomy-CJfgOllU.js → fileTaxonomy-DvDUV9wq.js} +1 -1
  32. package/dist/frameworkTableAssembly-C6ETawPR.js +2 -0
  33. package/dist/{frameworkTableAssembly-BwJVEKLr.js → frameworkTableAssembly-w-XnLa3q.js} +1 -1
  34. package/dist/{generateCommand-BDdOxhnv.js → generateCommand-DTNPGjxo.js} +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/{infoCommand-orsKJKUy.js → infoCommand-DXM868o_.js} +1 -1
  37. package/dist/inspectMetrics-CGF94puw.js +143 -0
  38. package/dist/{metaCommands-CfRLra0s.js → metaCommands-C6RFmF1r.js} +2 -2
  39. package/dist/{migrate-D3MK9BpK.js → migrate-D0F-eTlK.js} +2 -2
  40. package/dist/{pageConvention-cEiRxdab.js → pageConvention-CzUiSbtU.js} +1 -1
  41. package/dist/{privacyCommand-XejDMvmu.js → privacyCommand-DGdopOI6.js} +1 -1
  42. package/dist/{projectScaffold-mIX_DpSe.js → projectScaffold-B4dmTlwT.js} +1 -1
  43. package/dist/{projectScaffold-BIl97_E6.js → projectScaffold-EzlErR4E.js} +1 -1
  44. package/dist/{renderModeScan-D7J1B7Kw.js → renderModeScan-CcH2X1_D.js} +25 -10
  45. package/dist/{renderProfile-1OWWAAtx.js → renderProfile-Ck32Fzxr.js} +2 -2
  46. package/dist/{runtimeTrace-Bo8jWsNe.js → runtimeTrace-BPQyCmC5.js} +1 -1
  47. package/dist/{sdkgen-OZhiT2vz.js → sdkgen-Se88ifTd.js} +1 -1
  48. package/dist/serveCommand-DLc-BznW.js +2 -0
  49. package/dist/{serveCommand-YbD5VgfV.js → serveCommand-DkP3OT0W.js} +885 -868
  50. package/dist/serveEntry.js +1 -1
  51. package/dist/start-DfL3fOiN.js +3 -0
  52. package/dist/start-jw89Xbqy.js +1339 -0
  53. package/dist/startEntry.js +1 -1
  54. package/dist/{staticCommand-Dr2M6tpU.js → staticCommand-BwNEDlSU.js} +1 -1
  55. package/dist/{test-rXFq4S76.js → test-f3amja6a.js} +1 -1
  56. package/dist/updateCommand-5gFVfK5q.js +2 -0
  57. package/dist/{updateCommand-BgH6vTtq.js → updateCommand-BMk2e4ky.js} +1 -1
  58. package/dist/{webDev-BbdkzMbd.js → webDev-BgWL9gKV.js} +1233 -853
  59. package/dist/webDev-CZbTsDcH.js +2 -0
  60. package/dist/{webhooksCommand-DjE6u0r4.js → webhooksCommand-CoIO3jbj.js} +1 -1
  61. package/package.json +48 -17
  62. package/templates/AGENTS.core.md +26 -5
  63. package/templates/AGENTS.md +30 -9
  64. package/templates/agent-docs/_index.md +4 -4
  65. package/templates/agent-docs/_manifest.json +11 -11
  66. package/templates/agent-docs/cli.md +125 -14
  67. package/templates/agent-docs/data.md +210 -7
  68. package/templates/agent-docs/database/schema.md +1 -1
  69. package/templates/agent-docs/database/seedsdialects.md +1 -1
  70. package/templates/agent-docs/deployment.md +22 -3
  71. package/templates/agent-docs/internationalization.md +131 -13
  72. package/templates/agent-docs/introduction.md +46 -0
  73. package/templates/agent-docs/local-first-mobile.md +34 -7
  74. package/templates/agent-docs/plugins/ai-flows.md +1 -1
  75. package/templates/agent-docs/plugins/audit.md +5 -5
  76. package/templates/agent-docs/plugins/cdc-out.md +2 -2
  77. package/templates/agent-docs/plugins/{versioning.md → row-history.md} +22 -22
  78. package/templates/agent-docs/plugins/storage.md +2 -2
  79. package/templates/agent-docs/plugins.md +29 -7
  80. package/templates/agent-docs/reference.md +39 -2
  81. package/templates/agent-docs/routing.md +341 -47
  82. package/templates/agent-docs/schema-driven-ui.md +78 -2
  83. package/templates/agent-docs/security.md +125 -8
  84. package/templates/agent-docs/templates/apibackends.md +14 -14
  85. package/templates/agent-docs/templates/overview.md +1 -1
  86. package/templates/agent-docs/whats-new.md +100 -29
  87. package/templates/apps/api-ai/package.json +6 -7
  88. package/templates/apps/api-ai/tests/summarize.test.ts +1 -1
  89. package/templates/apps/api-auth/package.json +8 -8
  90. package/templates/apps/api-backend/package.json +7 -7
  91. package/templates/apps/api-backend-deactivation/package.json +7 -7
  92. package/templates/apps/api-backend-mail/package.json +8 -8
  93. package/templates/apps/api-backend-mariadb/package.json +9 -9
  94. package/templates/apps/api-backend-sqlite/package.json +8 -8
  95. package/templates/apps/api-backend-storage/package.json +8 -8
  96. package/templates/apps/api-cms/package.json +9 -10
  97. package/templates/apps/api-collab/package.json +8 -8
  98. package/templates/apps/api-data-advanced/package.json +8 -8
  99. package/templates/apps/api-durable/package.json +8 -8
  100. package/templates/apps/api-feature-flags/package.json +9 -9
  101. package/templates/apps/api-governance/package.json +8 -8
  102. package/templates/apps/api-kv/package.json +8 -8
  103. package/templates/apps/api-moderation/package.json +8 -8
  104. package/templates/apps/api-observability/package.json +8 -8
  105. package/templates/apps/api-ratelimit/package.json +8 -8
  106. package/templates/apps/api-rbac/package.json +8 -8
  107. package/templates/apps/api-rest/package.json +7 -7
  108. package/templates/apps/{api-versioning → api-row-history}/README.md +3 -3
  109. package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.server.ts +1 -1
  110. package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.server.ts +1 -1
  111. package/templates/apps/{api-versioning → api-row-history}/app.config.ts +3 -3
  112. package/templates/apps/{api-versioning → api-row-history}/database/schema.ts +1 -1
  113. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.server.ts +1 -1
  114. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.ts +1 -1
  115. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.ts +1 -1
  116. package/templates/apps/{api-versioning → api-row-history}/package.json +8 -8
  117. package/templates/apps/api-row-history/template.json +6 -0
  118. package/templates/apps/{api-versioning → api-row-history}/tests/documents.create.test.ts +1 -1
  119. package/templates/apps/api-saas/app.config.ts +1 -0
  120. package/templates/apps/api-saas/package.json +10 -11
  121. package/templates/apps/api-saas-starter/package.json +10 -10
  122. package/templates/apps/api-search/package.json +8 -8
  123. package/templates/apps/api-status/package.json +8 -8
  124. package/templates/apps/api-webhooks/package.json +9 -9
  125. package/templates/apps/changelog/package.json +6 -6
  126. package/templates/apps/edge-functions/package.json +2 -2
  127. package/templates/apps/frontend-admin/package.json +8 -8
  128. package/templates/apps/frontend-app/package.json +9 -9
  129. package/templates/apps/frontend-auth/package.json +8 -8
  130. package/templates/apps/frontend-blank/package.json +7 -7
  131. package/templates/apps/frontend-cms/package.json +9 -9
  132. package/templates/apps/frontend-collab/package.json +10 -10
  133. package/templates/apps/frontend-collab/src/pages/page.test.tsx +10 -9
  134. package/templates/apps/frontend-collab/src/pages/page.tsx +42 -65
  135. package/templates/apps/frontend-contact/package.json +7 -7
  136. package/templates/apps/frontend-dashboard/package.json +7 -7
  137. package/templates/apps/frontend-docs/package.json +6 -7
  138. package/templates/apps/frontend-i18n/package.json +6 -6
  139. package/templates/apps/frontend-landing/package.json +6 -7
  140. package/templates/apps/frontend-portal/package.json +8 -8
  141. package/templates/apps/frontend-saas/package.json +8 -8
  142. package/templates/apps/frontend-spa/package.json +7 -7
  143. package/templates/apps/frontend-ssr/package.json +7 -7
  144. package/templates/apps/frontend-ssr-api/package.json +8 -8
  145. package/templates/apps/frontend-static-blog/package.json +6 -6
  146. package/templates/apps/frontend-status/package.json +8 -8
  147. package/templates/apps/mobile-app/package.json +4 -4
  148. package/dist/agentsMd-Bu_XQgVf.js +0 -2
  149. package/dist/apiBuild-BZ-plaQN.js +0 -2
  150. package/dist/appGraph-CM9wvUmD.js +0 -2
  151. package/dist/appGraph-Cv2W_wO5.js +0 -111
  152. package/dist/build-CFmqF39c.js +0 -721
  153. package/dist/checkCommand-DomvYDi1.js +0 -2
  154. package/dist/codegen-DiMn2KkZ.js +0 -2
  155. package/dist/dbCommand-Bnl7MSGC.js +0 -2
  156. package/dist/dev-Dz9GqA6P.js +0 -3
  157. package/dist/doctorCommand-BlciWUJb.js +0 -2
  158. package/dist/fileConventions-Cof68_BL.js +0 -33
  159. package/dist/frameworkTableAssembly-CVDB2hCq.js +0 -2
  160. package/dist/inspectMetrics-CfdKLh6t.js +0 -72
  161. package/dist/serveCommand-Bbgg7Paq.js +0 -2
  162. package/dist/start-7b_bL8Uc.js +0 -3
  163. package/dist/start-B1nH9ahf.js +0 -1150
  164. package/dist/updateCommand-BweY17z-.js +0 -2
  165. package/dist/webDev-BcRFTsMA.js +0 -2
  166. package/templates/apps/api-versioning/template.json +0 -6
  167. /package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.ts +0 -0
  168. /package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.ts +0 -0
  169. /package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.server.ts +0 -0
  170. /package/templates/apps/{api-versioning → api-row-history}/tsconfig.json +0 -0
@@ -0,0 +1,1339 @@
1
+ import { i as e, t } from "./nodeEnvironment-cGFAj1J8.js";
2
+ import { r as n } from "./appPort-B_HpJ_ck.js";
3
+ import { $ as r, A as i, At as a, B as o, C as s, Dt as c, E as l, F as u, G as d, H as f, I as p, J as m, K as h, L as g, M as _, N as v, O as ee, P as y, S as b, T as x, Y as S, a as C, at as te, b as w, c as ne, ct as T, dt as re, et as E, f as D, ft as ie, it as ae, j as oe, k as se, mt as O, n as ce, nt as k, o as le, p as ue, pt as A, q as de, rt as fe, tt as pe, w as me, x as j, z as M } from "./webDev-BgWL9gKV.js";
4
+ import { o as N, t as P } from "./connectionConfig-Bk9IC7D0.js";
5
+ import { A as he, D as F, a as ge, h as _e, j as ve, k as I } from "./inspect-CtL_xTbu.js";
6
+ import { n as ye, r as be, t as xe } from "./inspectMetrics-CGF94puw.js";
7
+ import { a as Se, c as L, d as Ce, f as we, l as Te, p as R, u as Ee } from "./renderModeScan-CcH2X1_D.js";
8
+ import { i as De, n as Oe, r as ke, t as Ae } from "./runtimeRegistry-DMeKfTHP.js";
9
+ import { n as je } from "./logFileSink-B4uP8pvf.js";
10
+ import { t as Me } from "./bootTiming-BdyP9nYw.js";
11
+ import { dirname as Ne, extname as Pe, join as z, resolve as B } from "node:path";
12
+ import { promises as V } from "node:fs";
13
+ import { fileURLToPath as H, pathToFileURL as U } from "node:url";
14
+ import { createLogger as W, printReadyBlock as Fe } from "@voltro/logger";
15
+ import { Effect as Ie } from "effect";
16
+ import { maybeCompress as Le, snapshotMetricsSync as Re } from "@voltro/runtime";
17
+ import { createHash as ze } from "node:crypto";
18
+ import { resolveBroadcastProvider as Be } from "@voltro/plugin-broadcast";
19
+ import { createServer as Ve } from "node:http";
20
+ //#region src/httpResponseWrite.ts
21
+ var G = (e) => `W/"${ze("md5").update(e).digest("hex")}"`, He = (e) => {
22
+ let { response: t, status: n, headers: r, body: i, requestHeaders: a } = e, o = r["cache-control"], s = (typeof o == "string" ? o : "").toLowerCase();
23
+ if (i === void 0 || n !== 200 || s.includes("no-store")) return !1;
24
+ let c = r["content-type"], l = typeof c == "string" ? c : void 0, u = G(i), d = a["if-none-match"];
25
+ if (d !== void 0 && d.split(",").some((e) => e.trim() === u)) return t.writeHead(304, {
26
+ ...r,
27
+ etag: u
28
+ }), t.end(), !0;
29
+ let f = Le({
30
+ body: typeof i == "string" ? new TextEncoder().encode(i) : new Uint8Array(i),
31
+ contentType: l,
32
+ acceptEncoding: a["accept-encoding"],
33
+ options: e.compression
34
+ }), p = {
35
+ ...r,
36
+ ...f.headers,
37
+ etag: u
38
+ };
39
+ return f.compressed && delete p["content-length"], t.writeHead(n, p), t.end(Buffer.from(f.bytes)), !0;
40
+ }, K = W({ scope: "voltro:start:cdc" }), Ue = (e) => (t) => {
41
+ let n = 0;
42
+ for (let [r, i] of e.tableSubscriptionsByPattern) {
43
+ if (!i.includes(t)) continue;
44
+ let a = e.regexByPattern.get(r);
45
+ a && (n += e.cache.invalidateBy((e) => {
46
+ let t = e.split("|")[0] ?? e;
47
+ return a.test(t);
48
+ }));
49
+ }
50
+ return n;
51
+ }, q = () => P() ? N() : null, We = (e) => {
52
+ let t = e.channel ?? "framework_changes", n = e.revalidateChannel ?? "voltro_revalidate", r = Ue(e), i = null, a = null;
53
+ return {
54
+ start: async () => {
55
+ let o = e.tableSubscriptionsByPattern.size > 0, s = e.onRevalidate !== void 0;
56
+ if (!o && !s) return K.debug("skipped — no pages declare cacheInvalidatesOn and no revalidation handler is wired"), !1;
57
+ let c = q();
58
+ if (!c && !o) return K.debug("no database configured — the revalidation LISTEN line is unavailable"), !1;
59
+ if (!c) return K.warn(`${e.tableSubscriptionsByPattern.size} route(s) declare \`cacheInvalidatesOn\` and will NOT be invalidated live — no database is configured for this web process. Set DB_URL (or DB_HOST / PG_HOST) so the CDC listener can connect; until then those routes go stale until their \`revalidate\` window expires.`, { routes: [...e.tableSubscriptionsByPattern.keys()] }), !1;
60
+ i = new (await (import("pg"))).default.Client(c), await i.connect(), a = (i) => {
61
+ if (i.channel === n && i.payload && e.onRevalidate) {
62
+ e.onRevalidate(i.payload);
63
+ return;
64
+ }
65
+ if (i.channel !== t || !i.payload) return;
66
+ let a;
67
+ try {
68
+ a = JSON.parse(i.payload);
69
+ } catch (e) {
70
+ K.warn("bad CDC payload", {}, e);
71
+ return;
72
+ }
73
+ let o = r(a.table);
74
+ o > 0 && K.info(`${a.op} on '${a.table}' → dropped ${o} cache entries`, {
75
+ op: a.op,
76
+ table: a.table,
77
+ dropped: o
78
+ });
79
+ }, i.on("notification", a), o && await i.query(`LISTEN ${t}`), s && await i.query(`LISTEN ${n}`);
80
+ let l = [...o ? [t] : [], ...s ? [n] : []];
81
+ return K.info(`LISTEN ${l.join(", ")}`, {
82
+ channels: l,
83
+ ..."connectionString" in c ? { target: c.connectionString.replace(/\/\/[^@/]*@/, "//") } : {
84
+ host: c.host,
85
+ port: c.port,
86
+ database: c.database
87
+ }
88
+ }), !0;
89
+ },
90
+ stop: async () => {
91
+ if (i) {
92
+ try {
93
+ a && i.off("notification", a), await i.end();
94
+ } catch {}
95
+ i = null, a = null;
96
+ }
97
+ }
98
+ };
99
+ }, Ge = W({ scope: "voltro:start:isr-cache" }), J = "voltro_isr_cache", Ke = (e) => `
100
+ CREATE TABLE IF NOT EXISTS ${e} (
101
+ cache_key TEXT PRIMARY KEY,
102
+ html TEXT NOT NULL,
103
+ rendered_at BIGINT NOT NULL,
104
+ expires_at BIGINT NOT NULL,
105
+ servable_until BIGINT NOT NULL,
106
+ revalidate_ms BIGINT NOT NULL,
107
+ stale_while_revalidate_ms BIGINT NOT NULL DEFAULT 0
108
+ );
109
+ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
110
+ `, qe = (e) => {
111
+ let t = e.tableName ?? J, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
112
+ i || (i = !0, await n.query(Ke(t)));
113
+ })().then(async () => {
114
+ let e = await n.query(`SELECT * FROM ${t}`);
115
+ for (let t of e.rows) r.set(t.cache_key, {
116
+ html: t.html,
117
+ renderedAt: Number(t.rendered_at),
118
+ expiresAt: Number(t.expires_at),
119
+ servableUntil: Number(t.servable_until),
120
+ revalidateMs: Number(t.revalidate_ms),
121
+ staleWhileRevalidateMs: Number(t.stale_while_revalidate_ms)
122
+ });
123
+ }).catch((e) => {
124
+ Ge.warn("postgres bootstrap failed", {}, e);
125
+ }), o = (e, t) => {
126
+ a.then(() => n.query(e, t)).catch((e) => {
127
+ Ge.warn("postgres write failed", {}, e);
128
+ });
129
+ }, s = /* @__PURE__ */ new Map(), c = (e) => {
130
+ s.set(e, (s.get(e) ?? 0) + 1);
131
+ };
132
+ return {
133
+ get(e) {
134
+ return r.get(e);
135
+ },
136
+ set(e, n, i) {
137
+ return i?.ifGeneration !== void 0 && (s.get(e) ?? 0) !== i.ifGeneration ? !1 : (r.set(e, n), o(`INSERT INTO ${t}
138
+ (cache_key, html, rendered_at, expires_at, servable_until, revalidate_ms, stale_while_revalidate_ms)
139
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
140
+ ON CONFLICT (cache_key) DO UPDATE SET
141
+ html = EXCLUDED.html,
142
+ rendered_at = EXCLUDED.rendered_at,
143
+ expires_at = EXCLUDED.expires_at,
144
+ servable_until = EXCLUDED.servable_until,
145
+ revalidate_ms = EXCLUDED.revalidate_ms,
146
+ stale_while_revalidate_ms = EXCLUDED.stale_while_revalidate_ms`, [
147
+ e,
148
+ n.html,
149
+ n.renderedAt,
150
+ n.expiresAt,
151
+ n.servableUntil,
152
+ n.revalidateMs,
153
+ n.staleWhileRevalidateMs
154
+ ]), !0);
155
+ },
156
+ generation(e) {
157
+ return s.get(e) ?? 0;
158
+ },
159
+ invalidate(e) {
160
+ r.delete(e), c(e), o(`DELETE FROM ${t} WHERE cache_key = $1`, [e]);
161
+ },
162
+ invalidateBy(e) {
163
+ let n = 0, i = [];
164
+ for (let t of r.keys()) e(t) && (r.delete(t), c(t), i.push(t), n++);
165
+ return i.length > 0 && o(`DELETE FROM ${t} WHERE cache_key = ANY($1)`, [i]), n;
166
+ },
167
+ clear() {
168
+ for (let e of r.keys()) c(e);
169
+ r.clear(), o(`DELETE FROM ${t}`, []);
170
+ },
171
+ size() {
172
+ return r.size;
173
+ },
174
+ snapshot() {
175
+ return Array.from(r.entries());
176
+ }
177
+ };
178
+ }, Y = W({ scope: "voltro:start:revalidate" }), Je = (e) => {
179
+ try {
180
+ let t = JSON.parse(e);
181
+ return (t.kind === "path" || t.kind === "table" || t.kind === "tag") && typeof t.value == "string" && t.value.length > 0 && (t.tenant === void 0 || typeof t.tenant == "string") ? {
182
+ kind: t.kind,
183
+ value: t.value,
184
+ ...t.tenant === void 0 ? {} : { tenant: t.tenant }
185
+ } : (Y.warn("bad revalidation payload (shape)", { payload: e }), null);
186
+ } catch {
187
+ return Y.warn("bad revalidation payload (not JSON)", { payload: e }), null;
188
+ }
189
+ }, Ye = (e, t) => {
190
+ if (t.kind === "table" || t.kind === "tag") {
191
+ let n = e.tableSink(t.value);
192
+ return Y.info(`revalidate ${t.kind} '${t.value}' → dropped ${n} cache entries`, {
193
+ kind: t.kind,
194
+ value: t.value,
195
+ dropped: n
196
+ }), n;
197
+ }
198
+ let n = t.value, r = (e) => t.tenant === void 0 || e.split("|").includes(`tenant=${t.tenant}`), i = e.regexByPattern.get(n);
199
+ if (i !== void 0) {
200
+ let a = e.renderModeByPattern.get(n);
201
+ if (a !== "isr") return Y.error(`revalidatePath('${n}') targets a '${a ?? "unknown"}' route — only \`renderMode: 'isr'\` routes have cache entries to drop. Static HTML is a build artifact this process never re-renders; rebuild to change it.`, {
202
+ path: n,
203
+ renderMode: a ?? "unknown"
204
+ }), 0;
205
+ let o = e.cache.invalidateBy((e) => {
206
+ let t = e.split("|")[0] ?? e;
207
+ return i.test(t) && r(e);
208
+ });
209
+ return Y.info(`revalidate path pattern '${n}' → dropped ${o} cache entries`, {
210
+ path: n,
211
+ dropped: o,
212
+ ...t.tenant === void 0 ? {} : { tenant: t.tenant }
213
+ }), o;
214
+ }
215
+ let a = [...e.regexByPattern.entries()].find(([, e]) => e.test(n));
216
+ if (a === void 0) return Y.error(`revalidatePath('${n}') matches no declared route — nothing was dropped. Pass a declared route pattern (e.g. /blog/[slug]) or a concrete path of one.`, { path: n }), 0;
217
+ let o = e.renderModeByPattern.get(a[0]);
218
+ if (o !== "isr") return Y.error(`revalidatePath('${n}') targets a '${o ?? "unknown"}' route (${a[0]}) — only \`renderMode: 'isr'\` routes have cache entries to drop.`, {
219
+ path: n,
220
+ route: a[0],
221
+ renderMode: o ?? "unknown"
222
+ }), 0;
223
+ let s = e.cache.invalidateBy((e) => (e.split("|")[0] ?? e) === n && r(e));
224
+ return Y.info(`revalidate path '${n}' → dropped ${s} cache entries`, {
225
+ path: n,
226
+ dropped: s,
227
+ ...t.tenant === void 0 ? {} : { tenant: t.tenant }
228
+ }), s;
229
+ }, X = W({ scope: "voltro:start" }), Xe = await (async () => {
230
+ try {
231
+ let e = z(Ne(H(import.meta.url)), "..", "package.json"), t = await V.readFile(e, "utf8");
232
+ return JSON.parse(t).version ?? "0.0.0";
233
+ } catch {
234
+ return "0.0.0";
235
+ }
236
+ })(), Ze = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", Qe = (e) => e instanceof Error && e._voltroControl === "not-found", Z = {
237
+ ".html": "text/html; charset=utf-8",
238
+ ".js": "application/javascript",
239
+ ".mjs": "application/javascript",
240
+ ".css": "text/css",
241
+ ".json": "application/json",
242
+ ".xml": "application/xml; charset=utf-8",
243
+ ".rss": "application/rss+xml; charset=utf-8",
244
+ ".atom": "application/atom+xml; charset=utf-8",
245
+ ".svg": "image/svg+xml",
246
+ ".png": "image/png",
247
+ ".jpg": "image/jpeg",
248
+ ".jpeg": "image/jpeg",
249
+ ".webp": "image/webp",
250
+ ".ico": "image/x-icon",
251
+ ".woff": "font/woff",
252
+ ".woff2": "font/woff2",
253
+ ".txt": "text/plain; charset=utf-8"
254
+ }, $e = (e) => Z[Pe(e).toLowerCase()] ?? "application/octet-stream", et = (e) => e.replace(/<script\b(?=[^>]*\btype="module")(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<script\b(?=[^>]*\bsrc=)(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<link\b[^>]*\brel="modulepreload"[^>]*>/gi, ""), Q = async (e) => {
255
+ try {
256
+ return await V.stat(e), !0;
257
+ } catch {
258
+ return !1;
259
+ }
260
+ }, tt = (e) => {
261
+ let t = [], n = 0, r = e.split("/").filter(Boolean), i = [];
262
+ for (let e of r) e.startsWith("[[...") && e.endsWith("]]") ? (i.push("(.*)"), t.push(e.slice(5, -2)), n += 2) : e.startsWith("[...") && e.endsWith("]") ? (i.push("(.+)"), t.push(e.slice(4, -1)), n += 3) : e.startsWith("[") && e.endsWith("]") ? (i.push("([^/]+)"), t.push(e.slice(1, -1)), n += 1) : i.push(e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
263
+ let a = i.join("/");
264
+ return {
265
+ regex: a.length === 0 ? /^\/?$/ : RegExp(`^/${a}/?$`),
266
+ paramNames: t,
267
+ priority: n
268
+ };
269
+ }, nt = (e) => e.map((e) => {
270
+ let { regex: t, paramNames: n, priority: r } = tt(e.pattern);
271
+ return {
272
+ pattern: e.pattern,
273
+ file: e.file,
274
+ dirChain: e.dirChain,
275
+ regex: t,
276
+ paramNames: n,
277
+ priority: r
278
+ };
279
+ }).slice().sort((e, t) => e.priority - t.priority), rt = (e, t) => {
280
+ for (let n of t) {
281
+ let t = n.regex.exec(e);
282
+ if (!t) continue;
283
+ let r = {};
284
+ for (let e = 0; e < n.paramNames.length; e++) r[n.paramNames[e]] = t[e + 1] ?? "";
285
+ return {
286
+ route: n,
287
+ params: r
288
+ };
289
+ }
290
+ return null;
291
+ }, it = async (e) => {
292
+ let { provider: t, route: n, params: r, pathname: a, dirs: o, shellTemplate: c, requestHeaders: l, requestUrl: d = "", apiOrigin: f, apiName: m } = e, { search: h } = ae(d), g = await se(i(e.middlewares ?? [], {
293
+ route: n.pattern,
294
+ pathname: a
295
+ }), {
296
+ pathname: a,
297
+ search: h,
298
+ headers: l,
299
+ cookies: ee(l.cookie),
300
+ route: n.pattern
301
+ }, e.onCookieWarning), S = await t.getPage(n.pattern, n.file), w = L(S, n.file), T = w === "isr" ? s(g.headers) : g.headers, re = C(f, T.cookie, {
302
+ apiName: m,
303
+ headers: T
304
+ }), D = w === "spa" && ne(o, n.dirChain).some((e) => e.hasLayout);
305
+ if (w !== "ssr" && w !== "isr" && !D) return null;
306
+ let ie = ne(o, n.dirChain), oe = {}, O = null, ce, ue = new AbortController();
307
+ try {
308
+ (await t.getHelpers()).beginStoreSeeds(), {chain: ce, segmentLoaderData: oe} = await le({
309
+ entries: ie,
310
+ loadLayout: (e) => t.getLayout(e.dir),
311
+ loadError: (e) => t.getError(e.dir),
312
+ loadPending: (e) => t.getPending(e.dir),
313
+ ctx: {
314
+ params: r,
315
+ pathname: a,
316
+ search: h,
317
+ signal: ue.signal,
318
+ headers: T,
319
+ query: re,
320
+ isServer: !0
321
+ }
322
+ }), typeof S.loader == "function" && !D && (O = await S.loader({
323
+ params: r,
324
+ pathname: a,
325
+ search: h,
326
+ signal: ue.signal,
327
+ headers: T,
328
+ query: re,
329
+ isServer: !0
330
+ }));
331
+ } catch (e) {
332
+ if (Ze(e)) return {
333
+ control: "redirect",
334
+ status: e.status,
335
+ location: e.location,
336
+ setCookies: g.setCookies,
337
+ responseHeaders: g.responseHeaders
338
+ };
339
+ if (Qe(e)) return {
340
+ control: "not-found",
341
+ setCookies: g.setCookies,
342
+ responseHeaders: g.responseHeaders
343
+ };
344
+ throw e;
345
+ }
346
+ let A = await t.getHelpers();
347
+ D || await _({
348
+ pageMod: S,
349
+ params: r,
350
+ query: re,
351
+ apiName: m,
352
+ seed: A.seedPreloadedSubscription,
353
+ seedFailure: A.seedPreloadedSubscriptionFailure,
354
+ onError: ({ tag: e, apiName: t }, n) => X.warn("preload seed failed", {
355
+ tag: e,
356
+ apiName: t,
357
+ pathname: a
358
+ }, n)
359
+ });
360
+ let de = {
361
+ pattern: n.pattern,
362
+ Component: S.default,
363
+ meta: S.meta,
364
+ loader: S.loader,
365
+ ErrorBoundary: S.ErrorBoundary,
366
+ Pending: S.Pending,
367
+ renderMode: w,
368
+ chain: ce
369
+ };
370
+ if (w === "isr" && g.cspNonce !== void 0) throw Error(`page '${n.pattern}' is renderMode 'isr' while middleware.ts mints a cspNonce — an isr render is CACHED and a cached nonce is a lie the browser enforces. Use renderMode 'ssr' for nonce'd pages, or a hash-based CSP for isr.`);
371
+ let me = {
372
+ cookies: w === "isr" ? b(g.cookies) : g.cookies,
373
+ headers: T,
374
+ url: d,
375
+ ...e.formFlash === void 0 ? {} : { formFlash: e.formFlash }
376
+ }, j = A.i18n?.resolve(T.cookie, T["accept-language"]), M = {
377
+ lang: j?.locale ?? me.cookies["voltro:locale"] ?? "en",
378
+ ...j?.timeZone === void 0 ? {} : { timeZone: j.timeZone },
379
+ renderedAt: Date.now()
380
+ }, N = S.interactive ?? "full";
381
+ if (D) {
382
+ let e = pe({
383
+ helpers: A,
384
+ page: n.pattern,
385
+ interactive: N,
386
+ delivery: "stream",
387
+ renderInput: {
388
+ descriptor: de,
389
+ params: r,
390
+ pathname: a,
391
+ search: h,
392
+ segmentLoaderData: oe,
393
+ requestContext: me,
394
+ ...j ? {
395
+ outerWrap: j.outerWrap,
396
+ locale: j.locale
397
+ } : {}
398
+ }
399
+ }), t = (t) => fe({
400
+ helpers: A,
401
+ prepared: e,
402
+ meta: t,
403
+ interactive: N
404
+ });
405
+ if (e.hasDeferred) {
406
+ let n = u({ shell: c });
407
+ return {
408
+ renderMode: "spa",
409
+ setCookies: g.setCookies,
410
+ responseHeaders: g.responseHeaders,
411
+ stream: (r) => p({
412
+ res: r,
413
+ shell: n,
414
+ documentAttrs: M,
415
+ headers: {
416
+ ...g.responseHeaders,
417
+ "content-type": "text/html; charset=utf-8",
418
+ "x-voltro-rendered-by": "spa",
419
+ ...x(g.setCookies)
420
+ },
421
+ headInjection: t,
422
+ render: (t) => A.renderPageToStream({
423
+ ...e.renderInput,
424
+ ...t
425
+ }),
426
+ onShellError: (e) => {
427
+ X.error("spa layout shell render failed", { pathname: a }, e), r.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), r.end("server error");
428
+ }
429
+ })
430
+ };
431
+ }
432
+ let i = k({
433
+ helpers: A,
434
+ prepared: e
435
+ });
436
+ return {
437
+ html: v(y(c.replace("<div id=\"root\"></div>", `<div id="root">${i.body}</div>`), t(i.meta)), M),
438
+ renderMode: "spa",
439
+ revalidateMs: null,
440
+ setCookies: g.setCookies,
441
+ responseHeaders: g.responseHeaders
442
+ };
443
+ }
444
+ let P = E({
445
+ helpers: A,
446
+ page: n.pattern,
447
+ pathname: a,
448
+ renderMode: w,
449
+ interactive: N,
450
+ loaderData: O,
451
+ segmentLoaderData: oe,
452
+ pageLoaderRan: typeof S.loader == "function"
453
+ }), he = {
454
+ descriptor: de,
455
+ params: r,
456
+ pathname: a,
457
+ search: h,
458
+ loaderData: P.loaderData,
459
+ segmentLoaderData: P.segmentLoaderData,
460
+ requestContext: me,
461
+ ...j ? {
462
+ outerWrap: j.outerWrap,
463
+ locale: j.locale
464
+ } : {}
465
+ }, F = (e) => [
466
+ A.renderMetaToHtml(e),
467
+ `<meta name="voltro-interactive" content="${N}" />`,
468
+ ...P.hasDeferred ? [A.renderDeferredRegistryScript()] : [],
469
+ A.renderRouterStateScript(P.stateInput)
470
+ ].filter((e) => e.length > 0).join("\n ");
471
+ if (w === "ssr" && N === "full" && e.buffered !== !0) {
472
+ let e = u({ shell: c });
473
+ return {
474
+ renderMode: "ssr",
475
+ setCookies: g.setCookies,
476
+ stream: (t) => p({
477
+ res: t,
478
+ shell: e,
479
+ documentAttrs: M,
480
+ headers: {
481
+ ...g.responseHeaders,
482
+ "content-type": "text/html; charset=utf-8",
483
+ "x-voltro-rendered-by": "ssr",
484
+ ...x(g.setCookies)
485
+ },
486
+ headInjection: F,
487
+ render: (e) => A.renderPageToStream({
488
+ ...he,
489
+ ...e,
490
+ ...g.cspNonce === void 0 ? {} : { nonce: g.cspNonce }
491
+ }),
492
+ onShellError: (e) => {
493
+ X.error("ssr shell render failed", { pathname: a }, e), t.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), t.end("server error");
494
+ }
495
+ })
496
+ };
497
+ }
498
+ let { html: ge, meta: _e } = A.renderPageToHtml(he), ve = F(_e), I = v(y(c.replace("<div id=\"root\"></div>", `<div id="root">${ge}</div>`), ve), M);
499
+ N === "none" && (I = et(I));
500
+ let ye = w === "isr" ? R(S.revalidate) : null;
501
+ return {
502
+ html: g.cspNonce === void 0 ? I : te(I, g.cspNonce),
503
+ renderMode: w,
504
+ revalidateMs: ye,
505
+ setCookies: g.setCookies,
506
+ responseHeaders: g.responseHeaders
507
+ };
508
+ }, $ = (e) => e === "" ? "/" : e, at = async (e) => {
509
+ let t = await import(U(e).href);
510
+ return {
511
+ kind: "bundle",
512
+ getPageMeta: () => t.pageMeta,
513
+ async getPage(e, n) {
514
+ let r = t.pages[e];
515
+ if (!r) throw Error(`[voltro start] ssr bundle missing page for pattern ${e}`);
516
+ return typeof r == "function" ? await r() : r;
517
+ },
518
+ async getLayout(e) {
519
+ let n = t.layouts[$(e)];
520
+ if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${$(e)}`);
521
+ return typeof n == "function" ? await n() : n;
522
+ },
523
+ async getError(e) {
524
+ let n = t.errors[$(e)];
525
+ if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${$(e)}`);
526
+ return typeof n == "function" ? await n() : n;
527
+ },
528
+ async getPending(e) {
529
+ let n = t.pendings[$(e)];
530
+ if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${$(e)}`);
531
+ return typeof n == "function" ? await n() : n;
532
+ },
533
+ async getHelpers() {
534
+ return t.helpers;
535
+ },
536
+ async close() {}
537
+ };
538
+ }, ot = async (e, t) => {
539
+ let n = await a("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, o = await n.createServer({
540
+ root: t,
541
+ envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
542
+ plugins: [r(), i()],
543
+ resolve: { dedupe: [
544
+ "react",
545
+ "react-dom",
546
+ "react-intl",
547
+ "@voltro/i18n"
548
+ ] },
549
+ ssr: { noExternal: ["react-intl", "@voltro/i18n"] },
550
+ server: {
551
+ middlewareMode: !0,
552
+ hmr: !1
553
+ },
554
+ appType: "custom",
555
+ logLevel: "warn"
556
+ }), s = z(e, "src", "pages"), c = (e) => e === "" ? s : z(s, e), l = A({
557
+ limit: O(),
558
+ onColdStart: (e) => X.debug("ssr cold-compile start", { id: e }),
559
+ onColdEnd: (e, { durationMs: t, failed: n }) => X.debug(`ssr cold-compile ${n ? "FAILED" : "end"} ${t}ms`, {
560
+ id: e,
561
+ ms: t,
562
+ ...n ? { failed: !0 } : {}
563
+ })
564
+ }), u = (e) => l.load(e, async () => await o.ssrLoadModule(e));
565
+ return {
566
+ kind: "middleware",
567
+ async getPage(e, t) {
568
+ return u(z(s, t));
569
+ },
570
+ async getLayout(e) {
571
+ return u(z(c(e), "layout.tsx"));
572
+ },
573
+ async getError(e) {
574
+ return u(z(c(e), "error.tsx"));
575
+ },
576
+ async getPending(e) {
577
+ return u(z(c(e), "loading.tsx"));
578
+ },
579
+ async getHelpers() {
580
+ return await u("@voltro/web/ssr");
581
+ },
582
+ async close() {
583
+ await o.close();
584
+ }
585
+ };
586
+ }, st = async (t) => {
587
+ let a = Me(), s = await D(t);
588
+ if (!s) return {
589
+ ran: !1,
590
+ exitCode: 0
591
+ };
592
+ if (s.type !== "web") return X.error("only web apps are supported by `voltro start`"), {
593
+ ran: !0,
594
+ exitCode: 1
595
+ };
596
+ let { resolved: u } = await c({
597
+ rawEnv: s.env,
598
+ appType: "web",
599
+ root: t,
600
+ log: X,
601
+ generateTypes: !1
602
+ }), p = u.publicValues;
603
+ a.mark("config");
604
+ let _ = m();
605
+ d(_), X.info("booting voltro start", { app: s.name ?? "(unnamed)" });
606
+ let v = n({
607
+ kind: "web",
608
+ configPort: s.port,
609
+ env: process.env,
610
+ dashboardPin: process.env[g] === "1"
611
+ });
612
+ for (let e of v.warnings) X.warn(e);
613
+ let y = v.port, b = z(t, ".framework"), x = z(b, "dist"), te = z(x, "index.html");
614
+ if (!await Q(te)) return X.error("no built dist found — run `voltro build` first", { distDir: x }), {
615
+ ran: !0,
616
+ exitCode: 1
617
+ };
618
+ let E = s.name ?? "unnamed", O = Ae("web", E, y), k = je(E);
619
+ await De({
620
+ id: O,
621
+ name: E,
622
+ kind: "web",
623
+ url: `http://localhost:${y}`,
624
+ pid: process.pid,
625
+ status: "booting",
626
+ logFile: k.path
627
+ }).catch(() => {});
628
+ let le = z(x, "_voltro", "shell.html"), A = await Q(le) ? await V.readFile(le, "utf8") : await V.readFile(te, "utf8"), fe = await ue(t, s), pe = ye(s), L = ce(fe[0]), R = fe[0]?.name, Ne = /* @__PURE__ */ new Set(), Pe = (e) => {
629
+ Ne.has(e.name) || (Ne.add(e.name), X.warn(`middleware.ts wrote the cookie "${e.name}" with the HttpOnly default — a browser SDK cannot read it`, {
630
+ cookie: e.name,
631
+ hint: "If your IdP client reads this from `document.cookie` (Supabase's `createBrowserClient` does), pass `httpOnly: false`. Otherwise the SSR render succeeds and the user is signed out at the first client-side call. Set `httpOnly: true` explicitly to silence this."
632
+ }));
633
+ }, B = await l(t);
634
+ B.length > 0 && X.info(`middleware.ts loaded — ${B.length} middleware(s): ` + B.map((e) => e.exportName).join(", "));
635
+ let H, U = null, W = process.env.SSR_CACHE === "postgres";
636
+ if (W && P()) U = new (await (import("pg"))).default.Client(N()), await U.connect(), H = qe({ client: U }), X.info("isr cache backend: postgres", { table: "voltro_isr_cache" });
637
+ else if (W) {
638
+ let t = "SSR_CACHE=postgres, but nothing in the environment names a database (looked for DB_URL, DB_PRIMARY_URL, DB_DIRECT_URL, DB_MIGRATE_URL, DB_HOST, PG_HOST).";
639
+ if (e()) throw X.fatal(`${t} Refusing to fall back to the per-process memory cache: it is not shared between instances and does not survive a restart, so the pages this process serves would differ from its replicas' with nothing to indicate it.`), Error("[voltro start] SSR_CACHE=postgres with no database configured");
640
+ H = we(), X.warn(`${t} Falling back to the per-process memory cache — this would be a hard failure in production.`);
641
+ } else H = we(), X.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" });
642
+ await j(t, s, fe, p);
643
+ let Le = z(t, "src", "pages"), { pages: ze, dirs: G } = await w(Le), K = nt(ze);
644
+ me(B, K.map((e) => e.pattern)), a.mark("scan");
645
+ let q = z(x, "server", "ssrEntry.js"), Ge = await Q(q);
646
+ if (!Ge && process.env.NODE_ENV === "production") throw X.fatal(`production \`voltro start\` requires a precompiled SSR bundle at ${q} but it is missing. Run \`voltro build\` before serving — production renders from the bundle and never compiles on demand.`), Error(`[voltro start] missing SSR bundle at ${q}`);
647
+ let J = Ge ? await at(q) : await ot(t, b);
648
+ X.info(`ssr loader: ${J.kind}`, {
649
+ kind: J.kind,
650
+ ...J.kind === "middleware" ? { hint: "no pre-built SSR bundle found; run `voltro build` to enable bundle mode" } : {}
651
+ }), a.mark("provider");
652
+ let Ke = J.getPageMeta?.(), Y = /* @__PURE__ */ new Map(), Ze = 0;
653
+ for (let e of ze) {
654
+ let t = Ke?.[e.pattern];
655
+ if (t) {
656
+ Y.set(e.pattern, t);
657
+ continue;
658
+ }
659
+ try {
660
+ let t = await J.getPage(e.pattern, e.file);
661
+ Y.set(e.pattern, Se(t, e.file)), Ze++;
662
+ } catch (t) {
663
+ X.warn("failed to load page metadata", { pattern: e.pattern }, t);
664
+ }
665
+ }
666
+ let Qe = /* @__PURE__ */ new Map();
667
+ for (let [e, t] of Y) {
668
+ if (t.interactive !== "islands") continue;
669
+ let n = z(x, "island-shells", `${T(e)}.html`);
670
+ try {
671
+ Qe.set(e, await V.readFile(n, "utf8"));
672
+ } catch {
673
+ X.warn("islands page has no built per-page shell — serving the full app shell", {
674
+ pattern: e,
675
+ expected: n
676
+ });
677
+ }
678
+ }
679
+ X.info(`registered ${Y.size} route(s)`, {
680
+ count: Y.size,
681
+ fromManifest: Y.size - Ze,
682
+ fromModules: Ze
683
+ }), a.mark("routes");
684
+ let Z = /* @__PURE__ */ new Set();
685
+ for (let e of K) Y.get(e.pattern)?.renderMode === "spa" && ne(G, e.dirChain).some((e) => e.hasLayout) && Z.add(e.pattern);
686
+ Z.size > 0 && X.info(`${Z.size} spa route(s) render an SSR layout shell`, { patterns: [...Z] });
687
+ let et = /* @__PURE__ */ new Map();
688
+ for (let [e, t] of Y) t.cacheInvalidatesOn.length > 0 && et.set(e, t.cacheInvalidatesOn);
689
+ let tt = /* @__PURE__ */ new Map();
690
+ for (let e of K) tt.set(e.pattern, e.regex);
691
+ let $ = /* @__PURE__ */ new Map();
692
+ for (let [e, t] of Y) $.set(e, t.renderMode);
693
+ let st = [...Y.values()].some((e) => e.renderMode === "isr"), ct = {
694
+ cache: H,
695
+ tableSink: Ue({
696
+ cache: H,
697
+ tableSubscriptionsByPattern: et,
698
+ regexByPattern: tt
699
+ }),
700
+ regexByPattern: tt,
701
+ renderModeByPattern: $
702
+ }, lt = (e) => {
703
+ let t = Je(e);
704
+ t && Ye(ct, t);
705
+ }, ut = We({
706
+ cache: H,
707
+ tableSubscriptionsByPattern: et,
708
+ regexByPattern: tt,
709
+ ...st ? { onRevalidate: lt } : {}
710
+ }), dt = await ut.start(), ft = null, pt = null;
711
+ if (st) {
712
+ let e = Be({}, process.env);
713
+ if (e.crossReplica) try {
714
+ pt = e.provider;
715
+ let t = be(process.env);
716
+ ft = await Ie.runPromise(e.provider.subscribe(t, lt)), X.info(`revalidation subscriber: broker (${e.provider.name})`, { channel: t });
717
+ } catch (e) {
718
+ X.warn("revalidation broker subscribe failed — broker leg disabled", {}, e), pt = null;
719
+ }
720
+ dt && ft === null && X.info("revalidation subscriber: postgres LISTEN (voltro_revalidate)"), !dt && ft === null && X.warn("isr routes exist, but this web process has NO revalidation transport: no database is configured for the LISTEN line and no broker is configured. `revalidatePath` / `revalidateTable` / `revalidateTag` from the api will not reach this process — cached pages stay until their own `revalidate` window expires. Set DB_URL (postgres) or BROADCAST_URL (redis:// or nats://) on BOTH the api and this web deployment.");
721
+ }
722
+ a.mark("cdc");
723
+ let mt = xe(), ht = ze.map((e) => {
724
+ let t = Y.get(e.pattern);
725
+ return {
726
+ pattern: e.pattern,
727
+ file: e.file,
728
+ dirChain: e.dirChain,
729
+ renderMode: t?.renderMode ?? "static",
730
+ interactive: t?.interactive ?? "full",
731
+ tenantAware: t?.tenantAware ?? !1,
732
+ revalidateMs: t?.revalidateMs ?? null,
733
+ staleWhileRevalidateMs: t?.staleWhileRevalidateMs ?? 0,
734
+ cacheInvalidatesOn: t?.cacheInvalidatesOn ?? [],
735
+ hasLoader: t?.hasLoader ?? !1,
736
+ hasMeta: t?.hasMeta ?? !1,
737
+ hasGetStaticPaths: t?.hasGetStaticPaths ?? !1
738
+ };
739
+ }), gt = G.map((e) => ({
740
+ dir: e.dir,
741
+ hasLayout: e.hasLayout,
742
+ hasError: e.hasError,
743
+ hasPending: e.hasPending,
744
+ hasNotFound: e.hasNotFound
745
+ })), _t = U ? "postgres" : "memory", vt = {
746
+ kind: "web",
747
+ meta: {
748
+ name: s.name ?? "(unnamed)",
749
+ voltroVersion: Xe,
750
+ port: y,
751
+ mode: "start",
752
+ apis: Object.entries(s.apis ?? {}).map(([e, t]) => {
753
+ let n = {
754
+ name: e,
755
+ package: t.package
756
+ };
757
+ return t.url !== void 0 && (n.url = t.url), n;
758
+ })
759
+ },
760
+ routes: ht,
761
+ dirs: gt,
762
+ cache: () => {
763
+ let e = Date.now(), t = H.snapshot().map(([t, n]) => ({
764
+ key: t,
765
+ renderedAt: n.renderedAt,
766
+ expiresAt: n.expiresAt,
767
+ servableUntil: n.servableUntil,
768
+ ageMs: e - n.renderedAt,
769
+ htmlBytes: Buffer.byteLength(n.html, "utf8"),
770
+ revalidateMs: n.revalidateMs,
771
+ staleWhileRevalidateMs: n.staleWhileRevalidateMs
772
+ }));
773
+ return {
774
+ backend: _t,
775
+ size: H.size(),
776
+ entries: t
777
+ };
778
+ },
779
+ metrics: () => Re()
780
+ }, yt = async (e, n) => {
781
+ let a = e.split("?")[0] ?? "/";
782
+ if (s.name === "voltroDashboard" && a === "/api/dashboard/registry") try {
783
+ let e = await Oe();
784
+ return {
785
+ status: 200,
786
+ headers: { "content-type": "application/json; charset=utf-8" },
787
+ body: JSON.stringify(e)
788
+ };
789
+ } catch (e) {
790
+ return {
791
+ status: 500,
792
+ headers: { "content-type": "text/plain" },
793
+ body: `registry read error: ${e.message}`
794
+ };
795
+ }
796
+ if (s.name === "voltroDashboard" && a === "/api/dashboard/config") {
797
+ let e = process.env.VOLTRO_DASHBOARD_APPS, t = [];
798
+ if (e && e.trim().length > 0) {
799
+ let n;
800
+ try {
801
+ n = JSON.parse(e);
802
+ } catch (e) {
803
+ return {
804
+ status: 500,
805
+ headers: { "content-type": "text/plain" },
806
+ body: `VOLTRO_DASHBOARD_APPS is not valid JSON: ${e.message}`
807
+ };
808
+ }
809
+ for (let e of Array.isArray(n) ? n : []) {
810
+ if (typeof e != "object" || !e) continue;
811
+ let n = e;
812
+ typeof n.url == "string" && n.url.length !== 0 && t.push({
813
+ url: n.url,
814
+ ...typeof n.name == "string" ? { name: n.name } : {},
815
+ ...typeof n.token == "string" ? { token: n.token } : {}
816
+ });
817
+ }
818
+ }
819
+ return {
820
+ status: 200,
821
+ headers: { "content-type": "application/json; charset=utf-8" },
822
+ body: JSON.stringify({ apps: t })
823
+ };
824
+ }
825
+ if (a === "/_voltro/inspect/logs") {
826
+ let t = f(e, h(), n);
827
+ return {
828
+ status: t.status,
829
+ headers: {
830
+ "content-type": t.contentType,
831
+ ...F(n.origin)
832
+ },
833
+ body: t.body
834
+ };
835
+ }
836
+ if (a !== "/_voltro/inspect/stream") {
837
+ let t = _e(e, vt, n);
838
+ if (t !== null) return {
839
+ status: t.status,
840
+ headers: {
841
+ "content-type": t.contentType,
842
+ ...t.headers ?? {}
843
+ },
844
+ body: t.body
845
+ };
846
+ }
847
+ let o = rt(a, K), c = o ? Y.get(o.route.pattern)?.renderMode : void 0, l = c === "ssr" || c === "isr" || (o ? Z.has(o.route.pattern) : !1), u = [];
848
+ if (!l && B.length > 0) {
849
+ let t = i(B, {
850
+ route: void 0,
851
+ pathname: a
852
+ });
853
+ if (t !== void 0) {
854
+ let r = await se(t, {
855
+ pathname: a,
856
+ search: e.slice(a.length),
857
+ headers: n,
858
+ cookies: ee(n.cookie),
859
+ route: void 0
860
+ });
861
+ u.push(...r.setCookies);
862
+ }
863
+ }
864
+ let d = (e) => u.length === 0 ? e : {
865
+ ...e,
866
+ "set-cookie": u.map(oe)
867
+ }, p = a === "/" ? z(x, "index.html") : z(x, a.replace(/^\//, ""), "index.html");
868
+ if (!l && await Q(p)) {
869
+ let e = await V.readFile(p);
870
+ return {
871
+ status: 200,
872
+ headers: d({ "content-type": "text/html; charset=utf-8" }),
873
+ body: e
874
+ };
875
+ }
876
+ if (/\.[a-z0-9]+$/i.test(a)) {
877
+ let e = z(x, a.replace(/^\//, ""));
878
+ if (await Q(e)) {
879
+ let t = await V.readFile(e);
880
+ return {
881
+ status: 200,
882
+ headers: d({ "content-type": $e(e) }),
883
+ body: t
884
+ };
885
+ }
886
+ }
887
+ let m = rt(a, K);
888
+ if (!m) return {
889
+ status: 404,
890
+ headers: d({ "content-type": "text/plain" }),
891
+ body: "not found"
892
+ };
893
+ let g = Y.get(m.route.pattern), v = n["x-tenant"] ?? "anonymous", y = (await J.getHelpers()).i18n?.resolve(n.cookie, n["accept-language"])?.locale, b = Te(a, {
894
+ ...g?.tenantAware ? { tenant: v } : {},
895
+ ...y ? { locale: y } : {}
896
+ }), S = H.get(b), C = Date.now();
897
+ if (S && Ee(S, C)) return _.emit("cache", {
898
+ kind: "HIT",
899
+ key: b,
900
+ ageMs: C - S.renderedAt
901
+ }), {
902
+ status: 200,
903
+ headers: {
904
+ "content-type": "text/html; charset=utf-8",
905
+ "x-voltro-rendered-by": "isr",
906
+ "x-voltro-cache": "HIT",
907
+ "x-voltro-cache-key": b,
908
+ "x-voltro-cache-age-ms": String(C - S.renderedAt)
909
+ },
910
+ body: S.html
911
+ };
912
+ if (S && Ce(S, C)) {
913
+ _.emit("cache", {
914
+ kind: "STALE",
915
+ key: b,
916
+ ageMs: C - S.renderedAt
917
+ });
918
+ let r = H.generation(b);
919
+ return (async () => {
920
+ try {
921
+ let i = await it({
922
+ provider: J,
923
+ root: t,
924
+ route: m.route,
925
+ params: m.params,
926
+ pathname: a,
927
+ dirs: G,
928
+ shellTemplate: Qe.get(m.route.pattern) ?? A,
929
+ requestHeaders: n,
930
+ apiOrigin: L,
931
+ apiName: R,
932
+ middlewares: B,
933
+ onCookieWarning: Pe,
934
+ requestUrl: e,
935
+ buffered: !0
936
+ });
937
+ if (i && "html" in i && i.renderMode === "isr" && i.revalidateMs !== null) {
938
+ let e = Y.get(m.route.pattern)?.staleWhileRevalidateMs ?? 0, t = Date.now();
939
+ H.set(b, {
940
+ html: i.html,
941
+ renderedAt: t,
942
+ expiresAt: t + i.revalidateMs,
943
+ servableUntil: t + i.revalidateMs + e,
944
+ revalidateMs: i.revalidateMs,
945
+ staleWhileRevalidateMs: e
946
+ }, { ifGeneration: r }) || X.info("SWR refresh discarded — the entry was purged while it rendered", { key: b });
947
+ }
948
+ } catch (e) {
949
+ X.warn("background SWR refresh failed", { url: a }, e);
950
+ }
951
+ })(), {
952
+ status: 200,
953
+ headers: {
954
+ "content-type": "text/html; charset=utf-8",
955
+ "x-voltro-rendered-by": "isr",
956
+ "x-voltro-cache": "STALE",
957
+ "x-voltro-cache-key": b,
958
+ "x-voltro-cache-age-ms": String(C - S.renderedAt)
959
+ },
960
+ body: S.html
961
+ };
962
+ }
963
+ try {
964
+ let r = H.generation(b), i = await it({
965
+ provider: J,
966
+ root: t,
967
+ route: m.route,
968
+ params: m.params,
969
+ pathname: a,
970
+ dirs: G,
971
+ shellTemplate: Qe.get(m.route.pattern) ?? A,
972
+ requestHeaders: n,
973
+ apiOrigin: L,
974
+ apiName: R,
975
+ middlewares: B,
976
+ onCookieWarning: Pe,
977
+ requestUrl: e
978
+ });
979
+ if (!i) return {
980
+ status: 200,
981
+ headers: {
982
+ "content-type": "text/html; charset=utf-8",
983
+ "x-voltro-rendered-by": "spa-fallback"
984
+ },
985
+ body: A
986
+ };
987
+ let o = i.setCookies ?? [], s = i.responseHeaders ?? {}, c = (e) => ({
988
+ ...s,
989
+ ...e,
990
+ ...o.length === 0 ? {} : { "set-cookie": o.map(oe) }
991
+ });
992
+ if ("stream" in i) return {
993
+ status: 200,
994
+ headers: { "x-voltro-rendered-by": i.renderMode },
995
+ body: "",
996
+ stream: i.stream
997
+ };
998
+ if ("control" in i) return i.control === "redirect" ? {
999
+ status: i.status,
1000
+ headers: c({
1001
+ location: i.location,
1002
+ "x-voltro-rendered-by": "loader-redirect"
1003
+ }),
1004
+ body: ""
1005
+ } : {
1006
+ status: 404,
1007
+ headers: c({
1008
+ "content-type": "text/plain; charset=utf-8",
1009
+ "x-voltro-rendered-by": "loader-not-found"
1010
+ }),
1011
+ body: "not found"
1012
+ };
1013
+ let l = c({
1014
+ "content-type": "text/html; charset=utf-8",
1015
+ "x-voltro-rendered-by": i.renderMode
1016
+ });
1017
+ if (i.renderMode === "isr" && i.revalidateMs !== null) {
1018
+ let e = Date.now(), t = Y.get(m.route.pattern)?.staleWhileRevalidateMs ?? 0;
1019
+ H.set(b, {
1020
+ html: i.html,
1021
+ renderedAt: e,
1022
+ expiresAt: e + i.revalidateMs,
1023
+ servableUntil: e + i.revalidateMs + t,
1024
+ revalidateMs: i.revalidateMs,
1025
+ staleWhileRevalidateMs: t
1026
+ }, { ifGeneration: r }) || X.info("miss fill discarded — the entry was purged while it rendered", { key: b }), l["x-voltro-cache"] = "MISS", l["x-voltro-cache-key"] = b, l["x-voltro-cache-revalidate-ms"] = String(i.revalidateMs), t > 0 && (l["x-voltro-cache-swr-ms"] = String(t)), _.emit("cache", {
1027
+ kind: "MISS",
1028
+ key: b,
1029
+ revalidateMs: i.revalidateMs,
1030
+ swrMs: t
1031
+ });
1032
+ } else i.renderMode === "isr" && (l["x-voltro-cache"] = "BYPASS", _.emit("cache", {
1033
+ kind: "BYPASS",
1034
+ key: b
1035
+ }));
1036
+ return {
1037
+ status: 200,
1038
+ headers: l,
1039
+ body: i.html
1040
+ };
1041
+ } catch (e) {
1042
+ return X.error("render error", { url: a }, e), r(e) ? {
1043
+ status: 500,
1044
+ headers: {
1045
+ "content-type": "text/plain; charset=utf-8",
1046
+ "x-voltro-rendered-by": "defer-unsupported"
1047
+ },
1048
+ body: e.message
1049
+ } : {
1050
+ status: 500,
1051
+ headers: { "content-type": "text/plain" },
1052
+ body: "server error"
1053
+ };
1054
+ }
1055
+ }, bt = !1, xt = ie({
1056
+ apiName: R,
1057
+ apiOrigin: L,
1058
+ loadDescriptors: async () => {
1059
+ if (R === void 0) return;
1060
+ let e = (await J.getHelpers()).apiDescriptors?.[R];
1061
+ return e === void 0 ? void 0 : await e();
1062
+ },
1063
+ rpcQuery: (e, t) => C(L, e, {
1064
+ apiName: R,
1065
+ headers: t
1066
+ }),
1067
+ renderErrorPage: async ({ path: e, headers: n, flash: r }) => {
1068
+ let { pathname: i } = ae(e), a = rt(i, K);
1069
+ if (!a) return;
1070
+ let o = await it({
1071
+ provider: J,
1072
+ root: t,
1073
+ route: a.route,
1074
+ params: a.params,
1075
+ pathname: i,
1076
+ dirs: G,
1077
+ shellTemplate: A,
1078
+ requestHeaders: n,
1079
+ apiOrigin: L,
1080
+ apiName: R,
1081
+ middlewares: B,
1082
+ onCookieWarning: Pe,
1083
+ requestUrl: e,
1084
+ formFlash: r,
1085
+ buffered: !0
1086
+ });
1087
+ return o !== null && "html" in o ? o.html : void 0;
1088
+ }
1089
+ }), St = Ve(async (e, t) => {
1090
+ let n = e.url ?? "/", r = Date.now(), i = {};
1091
+ for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (i[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
1092
+ let a = n.split("?")[0] ?? "/", o = (e.method ?? "GET").toUpperCase();
1093
+ if (a === "/internal/liveness") {
1094
+ t.writeHead(200, {
1095
+ "content-type": "text/plain",
1096
+ "cache-control": "no-store"
1097
+ }), t.end("ok");
1098
+ return;
1099
+ }
1100
+ if (a === "/internal/readiness") {
1101
+ let e = bt ? 200 : 503;
1102
+ t.writeHead(e, {
1103
+ "content-type": "text/plain",
1104
+ "cache-control": "no-store"
1105
+ }), t.end(bt ? "ready" : "not-ready");
1106
+ return;
1107
+ }
1108
+ if (!(a.startsWith(re) && await xt(e, t))) {
1109
+ if (o === "OPTIONS" && a.startsWith("/_voltro/inspect")) {
1110
+ let e = F(i.origin);
1111
+ t.writeHead(204, e), t.end();
1112
+ return;
1113
+ }
1114
+ if (s.name === "voltroDashboard" && a === "/api/dashboard/proxy") {
1115
+ let r = new URL(n, "http://x").searchParams.get("target");
1116
+ if (!r) {
1117
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
1118
+ return;
1119
+ }
1120
+ if (!I(r)) {
1121
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("target not allowed: must be loopback or an origin configured in VOLTRO_DASHBOARD_APPS");
1122
+ return;
1123
+ }
1124
+ try {
1125
+ let n = { accept: i.accept ?? "application/json" }, a = i.authorization;
1126
+ a ? n.authorization = Array.isArray(a) ? a[0] : a : ve(r) && process.env.VOLTRO_INSPECT_TOKEN && (n.authorization = `Bearer ${process.env.VOLTRO_INSPECT_TOKEN}`);
1127
+ let s;
1128
+ if (o && o !== "GET" && o !== "HEAD") {
1129
+ let t = [];
1130
+ for await (let n of e) t.push(n);
1131
+ s = Buffer.concat(t);
1132
+ let r = i["content-type"];
1133
+ r && (n["content-type"] = Array.isArray(r) ? r[0] : r);
1134
+ }
1135
+ let c = await fetch(r, {
1136
+ redirect: "manual",
1137
+ method: o,
1138
+ headers: n,
1139
+ ...s ? { body: new Uint8Array(s.buffer, s.byteOffset, s.byteLength) } : {}
1140
+ }), l = Buffer.from(await c.arrayBuffer());
1141
+ t.writeHead(c.status, {
1142
+ "content-type": c.headers.get("content-type") ?? "application/octet-stream",
1143
+ "content-length": String(l.length)
1144
+ }), t.end(l);
1145
+ } catch (e) {
1146
+ t.writeHead(502, { "content-type": "text/plain" }), t.end(`proxy error: ${e.message}`);
1147
+ }
1148
+ return;
1149
+ }
1150
+ if (s.name === "voltroDashboard" && a === "/api/dashboard/proxy/stream") {
1151
+ let r = new URL(n, "http://x").searchParams.get("target");
1152
+ if (!r) {
1153
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
1154
+ return;
1155
+ }
1156
+ if (!I(r)) {
1157
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("target not allowed: must be loopback or an origin configured in VOLTRO_DASHBOARD_APPS");
1158
+ return;
1159
+ }
1160
+ let a = new AbortController();
1161
+ e.on("close", () => a.abort()), t.on("close", () => a.abort());
1162
+ try {
1163
+ let e = i.authorization ?? (ve(r) && process.env.VOLTRO_INSPECT_TOKEN ? `Bearer ${process.env.VOLTRO_INSPECT_TOKEN}` : void 0), n = await fetch(r, {
1164
+ redirect: "manual",
1165
+ method: "GET",
1166
+ headers: {
1167
+ accept: "text/event-stream",
1168
+ ...e ? { authorization: Array.isArray(e) ? e[0] : e } : {}
1169
+ },
1170
+ signal: a.signal
1171
+ });
1172
+ if (!n.ok || !n.body) {
1173
+ t.writeHead(n.status, { "content-type": "text/plain" }), t.end(`upstream ${n.status}`);
1174
+ return;
1175
+ }
1176
+ t.writeHead(200, {
1177
+ "content-type": "text/event-stream; charset=utf-8",
1178
+ "cache-control": "no-cache, no-transform",
1179
+ connection: "keep-alive",
1180
+ "x-accel-buffering": "no"
1181
+ }), t.flushHeaders();
1182
+ let o = n.body.getReader();
1183
+ try {
1184
+ for (;;) {
1185
+ let { done: e, value: n } = await o.read();
1186
+ if (e) break;
1187
+ n && t.write(Buffer.from(n));
1188
+ }
1189
+ } finally {
1190
+ o.releaseLock(), t.end();
1191
+ }
1192
+ } catch (e) {
1193
+ e.name !== "AbortError" && (t.writeHead(502, { "content-type": "text/plain" }), t.end(`proxy stream error: ${e.message}`));
1194
+ }
1195
+ return;
1196
+ }
1197
+ if (a === "/_voltro/inspect/stream") {
1198
+ let n = F(i.origin);
1199
+ if (he()) {
1200
+ t.writeHead(503, {
1201
+ "content-type": "application/json",
1202
+ ...n
1203
+ }), t.end(JSON.stringify({ error: "inspect disabled (VOLTRO_INSPECT=off)" }));
1204
+ return;
1205
+ }
1206
+ let r = ge(i, "GET");
1207
+ if (!r.ok) {
1208
+ t.writeHead(401, {
1209
+ "content-type": "application/json",
1210
+ ...n
1211
+ }), t.end(JSON.stringify({
1212
+ error: "unauthorized",
1213
+ reason: r.reason
1214
+ }));
1215
+ return;
1216
+ }
1217
+ t.writeHead(200, {
1218
+ "content-type": "text/event-stream; charset=utf-8",
1219
+ "cache-control": "no-cache, no-transform",
1220
+ connection: "keep-alive",
1221
+ "x-accel-buffering": "no",
1222
+ ...n
1223
+ }), t.flushHeaders(), t.write("retry: 5000\n\n");
1224
+ for (let e of _.buffered()) t.write(de(e));
1225
+ let a = _.subscribe((e) => {
1226
+ t.write(de(e));
1227
+ }), o = setInterval(() => {
1228
+ t.write(S());
1229
+ }, 15e3), s = () => {
1230
+ clearInterval(o), a();
1231
+ };
1232
+ e.on("close", s), t.on("close", s);
1233
+ return;
1234
+ }
1235
+ try {
1236
+ let e = await yt(n, i);
1237
+ if (e.stream ? e.stream(t) : He({
1238
+ response: t,
1239
+ status: e.status,
1240
+ headers: e.headers,
1241
+ body: e.body,
1242
+ requestHeaders: i,
1243
+ compression: pe.compression
1244
+ }) || (t.writeHead(e.status, e.headers), t.end(e.body)), !a.startsWith("/_voltro/inspect")) {
1245
+ let t = Date.now() - r;
1246
+ _.emit("request", {
1247
+ url: a,
1248
+ status: e.status,
1249
+ durationMs: t,
1250
+ renderedBy: e.headers["x-voltro-rendered-by"],
1251
+ cache: e.headers["x-voltro-cache"]
1252
+ });
1253
+ let n = rt(a, K)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
1254
+ mt.record({
1255
+ kind: "request",
1256
+ tag: n,
1257
+ durationMs: t,
1258
+ status: e.status >= 500 ? "error" : "ok"
1259
+ });
1260
+ }
1261
+ } catch (e) {
1262
+ t.writeHead(500, { "content-type": "text/plain" }), t.end(`internal error: ${e.message}`);
1263
+ }
1264
+ }
1265
+ });
1266
+ await new Promise((e) => St.listen(y, "::", e)), bt = !0, a.mark("ready"), X.info(`listening on http://localhost:${y}`, {
1267
+ port: y,
1268
+ distDir: x
1269
+ }), X.debug("dynamic + ssr/isr routes resolve via SSR loader");
1270
+ try {
1271
+ await De({
1272
+ id: O,
1273
+ name: E,
1274
+ kind: "web",
1275
+ url: `http://localhost:${y}`,
1276
+ pid: process.pid,
1277
+ status: "live",
1278
+ logFile: k.path
1279
+ });
1280
+ let e = o(t);
1281
+ if (e) {
1282
+ let t = await M({ dashboardAppPath: e });
1283
+ t.status === "launched" ? X.info(`dashboard launched at http://localhost:${t.port}`, { port: t.port }) : t.status === "already-running" && X.debug(`dashboard already running on :${t.port}`, { port: t.port });
1284
+ }
1285
+ setInterval(() => {
1286
+ De({
1287
+ id: O,
1288
+ name: E,
1289
+ kind: "web",
1290
+ url: `http://localhost:${y}`,
1291
+ pid: process.pid,
1292
+ status: "live",
1293
+ logFile: k.path
1294
+ }).catch(() => {});
1295
+ }, 1e4).unref();
1296
+ } catch (e) {
1297
+ X.warn("runtime-registry write failed", {}, e);
1298
+ }
1299
+ Fe({
1300
+ app: s.name ?? "unnamed",
1301
+ kind: "web",
1302
+ url: `http://localhost:${y}`,
1303
+ bootMs: a.total()
1304
+ });
1305
+ let Ct = a.phaseBreakdown();
1306
+ X.info(`start: ready in ${a.total()}ms`, {
1307
+ bootMs: a.total(),
1308
+ ...Ct ? { phases: Ct } : {}
1309
+ });
1310
+ let wt = !1, Tt = (e) => async () => {
1311
+ if (!wt) {
1312
+ if (wt = !0, X.info(`received ${e}, shutting down`, { signal: e }), St.close(), await ut.stop(), ft) try {
1313
+ ft();
1314
+ } catch {}
1315
+ if (pt) try {
1316
+ await Ie.runPromise(pt.close());
1317
+ } catch {}
1318
+ if (U) try {
1319
+ await U.end();
1320
+ } catch {}
1321
+ await J.close(), await ke(O), await k.dispose(), process.exit(0);
1322
+ }
1323
+ };
1324
+ process.on("SIGINT", Tt("SIGINT")), process.on("SIGTERM", Tt("SIGTERM"));
1325
+ let Et = process.ppid;
1326
+ if (Et > 1) {
1327
+ let e = setInterval(() => {
1328
+ (process.ppid !== Et || process.ppid === 1) && (clearInterval(e), Tt("SIGTERM")());
1329
+ }, 2e3);
1330
+ e.unref();
1331
+ }
1332
+ return new Promise(() => {});
1333
+ }, ct = async (e) => {
1334
+ t("start");
1335
+ let n = e.find((e) => !e.startsWith("-")) ?? process.cwd(), r = await st(B(n));
1336
+ return r.ran ? r.exitCode : (X.error("no web app found at this path"), 1);
1337
+ };
1338
+ //#endregion
1339
+ export { st as n, ct as t };