@voltro/cli 0.11.4 → 0.13.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 (99) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/dist/{apiBuild-DHHuiwXF.js → apiBuild-B6_ehbem.js} +9 -8
  3. package/dist/apiBuild-Bzpb383i.js +2 -0
  4. package/dist/appModuleLoader-C9r9mxZt.js +19 -0
  5. package/dist/bin.js +21 -10
  6. package/dist/cliArgs-qdZSElM3.js +28 -0
  7. package/dist/{commands-BwbMqR-1.js → commands-DYOuGlEC.js} +2903 -2647
  8. package/dist/dbCommand-ADD4mrTI.js +2 -0
  9. package/dist/dbCommand-DIOJQwsq.js +876 -0
  10. package/dist/{dev-Cvojvb_J.js → dev-Fb9BmavX.js} +1697 -2411
  11. package/dist/dev-xdijqojc.js +3 -0
  12. package/dist/frameworkTableAssembly-CYlrafoK.js +600 -0
  13. package/dist/frameworkTableAssembly-XvKO7wQt.js +2 -0
  14. package/dist/index.d.ts +19 -0
  15. package/dist/index.js +3 -3
  16. package/dist/inspect-DcZ04OME.js +2 -0
  17. package/dist/inspect-Dwx0_tUj.js +921 -0
  18. package/dist/inspectMetrics-DOiiII34.js +3408 -0
  19. package/dist/manifestBuild-D1MzJAiQ.js +2 -0
  20. package/dist/manifestBuild-P9yuCY2d.js +39 -0
  21. package/dist/seedRunner-D6eu-u5U.js +229 -0
  22. package/dist/serveCommand-CqYxzBZR.js +1113 -0
  23. package/dist/serveEntry.js +4 -4
  24. package/dist/{start-DyJE6w1R.js → start-CjG_0xhR.js} +346 -343
  25. package/dist/startEntry.js +2 -2
  26. package/dist/startup.js +1 -1
  27. package/dist/{startupRunner-DhlX9nqd.js → startupRunner-DPGFchOa.js} +19 -33
  28. package/package.json +17 -17
  29. package/templates/AGENTS.md +1 -0
  30. package/templates/agent-docs/_index.md +1 -0
  31. package/templates/agent-docs/authentication.md +36 -0
  32. package/templates/agent-docs/cli.md +254 -14
  33. package/templates/agent-docs/configuration.md +48 -0
  34. package/templates/agent-docs/data.md +39 -0
  35. package/templates/agent-docs/database/migrations.md +32 -0
  36. package/templates/agent-docs/database/transactions.md +31 -0
  37. package/templates/agent-docs/scheduling.md +35 -1
  38. package/templates/agent-docs/security.md +48 -0
  39. package/templates/agent-docs/templates/apibackends.md +90 -17
  40. package/templates/agent-docs/testing.md +24 -0
  41. package/templates/agent-docs/whats-new.md +81 -0
  42. package/templates/apps/api-ai/package.json +7 -7
  43. package/templates/apps/api-auth/package.json +8 -8
  44. package/templates/apps/api-backend/package.json +7 -7
  45. package/templates/apps/api-backend-deactivation/package.json +7 -7
  46. package/templates/apps/api-backend-deactivation/tests/users.deactivate.test.ts +1 -1
  47. package/templates/apps/api-backend-mail/package.json +8 -8
  48. package/templates/apps/api-backend-mariadb/package.json +9 -9
  49. package/templates/apps/api-backend-storage/package.json +8 -8
  50. package/templates/apps/api-data-advanced/package.json +8 -8
  51. package/templates/apps/api-data-advanced/tests/queries.test.ts +6 -1
  52. package/templates/apps/api-durable/package.json +8 -8
  53. package/templates/apps/api-feature-flags/package.json +9 -9
  54. package/templates/apps/api-governance/app.config.ts +0 -1
  55. package/templates/apps/api-governance/package.json +8 -8
  56. package/templates/apps/api-kv/package.json +8 -8
  57. package/templates/apps/api-kv/tests/sync.test.ts +2 -2
  58. package/templates/apps/api-moderation/package.json +8 -8
  59. package/templates/apps/api-observability/package.json +8 -8
  60. package/templates/apps/api-ratelimit/package.json +8 -8
  61. package/templates/apps/api-rbac/README.md +53 -14
  62. package/templates/apps/api-rbac/app.config.ts +24 -26
  63. package/templates/apps/api-rbac/authz.ts +76 -0
  64. package/templates/apps/api-rbac/database/schema.ts +23 -6
  65. package/templates/apps/api-rbac/mutations/notes.create.mutation.server.ts +7 -5
  66. package/templates/apps/api-rbac/mutations/notes.create.mutation.ts +15 -8
  67. package/templates/apps/api-rbac/mutations/notes.delete.mutation.server.ts +54 -0
  68. package/templates/apps/api-rbac/mutations/notes.delete.mutation.ts +43 -0
  69. package/templates/apps/api-rbac/mutations/teams.rename.mutation.server.ts +19 -0
  70. package/templates/apps/api-rbac/mutations/teams.rename.mutation.ts +37 -0
  71. package/templates/apps/api-rbac/package.json +8 -8
  72. package/templates/apps/api-rbac/queries/notes.list.query.server.ts +12 -0
  73. package/templates/apps/api-rbac/queries/notes.list.query.ts +26 -0
  74. package/templates/apps/api-rbac/template.json +2 -2
  75. package/templates/apps/api-rbac/tests/authz.test.ts +233 -0
  76. package/templates/apps/api-rest/package.json +7 -7
  77. package/templates/apps/api-saas/package.json +11 -11
  78. package/templates/apps/api-search/package.json +8 -8
  79. package/templates/apps/api-versioning/package.json +8 -8
  80. package/templates/apps/api-webhooks/package.json +9 -8
  81. package/templates/apps/changelog/package.json +6 -6
  82. package/templates/apps/edge-functions/package.json +2 -2
  83. package/templates/apps/frontend-admin/package.json +8 -8
  84. package/templates/apps/frontend-app/package.json +8 -8
  85. package/templates/apps/frontend-blank/package.json +7 -7
  86. package/templates/apps/frontend-contact/package.json +7 -7
  87. package/templates/apps/frontend-dashboard/package.json +7 -7
  88. package/templates/apps/frontend-docs/package.json +7 -7
  89. package/templates/apps/frontend-i18n/package.json +6 -6
  90. package/templates/apps/frontend-landing/package.json +7 -7
  91. package/templates/apps/frontend-spa/package.json +7 -7
  92. package/templates/apps/frontend-ssr/package.json +7 -7
  93. package/templates/apps/frontend-ssr-api/package.json +8 -8
  94. package/templates/apps/frontend-static-blog/package.json +6 -6
  95. package/dist/apiBuild-D9MtlD02.js +0 -2
  96. package/dist/dev-BsBsIv93.js +0 -2
  97. package/dist/inspectMetrics-D4EJ8LTi.js +0 -4255
  98. package/dist/serveCommand-ARqSs73o.js +0 -1117
  99. package/templates/apps/api-rbac/tests/notes.create.test.ts +0 -80
@@ -1,13 +1,14 @@
1
- import { A as e, At as t, C as n, Ct as r, Dt as i, E as a, Et as o, G as s, H as c, I as l, J as u, N as d, Nt as f, O as p, R as m, Tt as h, U as g, V as _, W as v, Z as y, _ as ee, _t as te, a as b, at as ne, b as x, c as S, f as C, g as w, gt as T, h as E, i as D, j as O, k, m as A, o as re, p as ie, q as j, r as ae, s as oe, t as se, v as M, vt as N, w as P, wt as ce, y as F } from "./inspectMetrics-D4EJ8LTi.js";
2
- import { t as le } from "./bootTiming-BdyP9nYw.js";
3
- import { dirname as ue, extname as I, join as L, resolve as R } from "node:path";
4
- import { fileURLToPath as de, pathToFileURL as fe } from "node:url";
5
- import { createLogger as z, printReadyBlock as pe } from "@voltro/logger";
6
- import { promises as B } from "node:fs";
1
+ import { $ as e, A as t, C as n, E as r, G as i, H as a, I as o, J as s, N as c, O as l, Q as u, R as ee, U as d, V as f, W as p, _ as m, a as h, at as g, b as _, c as v, ct as y, et as b, f as te, g as x, h as S, i as C, j as w, k as ne, m as T, nt as re, o as ie, p as E, q as D, r as O, s as k, t as A, tt as j, v as M, w as ae, y as N } from "./inspectMetrics-DOiiII34.js";
2
+ import { D as oe, E as se, T as ce, a as P, p as le, w as ue } from "./inspect-Dwx0_tUj.js";
3
+ import { t as de } from "./bootTiming-BdyP9nYw.js";
4
+ import { dirname as fe, extname as F, join as I, resolve as L } from "node:path";
5
+ import { fileURLToPath as R, pathToFileURL as z } from "node:url";
6
+ import { createLogger as B, printReadyBlock as pe } from "@voltro/logger";
7
+ import { promises as V } from "node:fs";
7
8
  import { snapshotMetricsSync as me } from "@voltro/runtime";
8
9
  import { createServer as he } from "node:http";
9
10
  //#region src/isrCdcInvalidator.ts
10
- var V = z({ scope: "voltro:start:cdc" }), H = (e) => (t) => {
11
+ var H = B({ scope: "voltro:start:cdc" }), U = (e) => (t) => {
11
12
  let n = 0;
12
13
  for (let [r, i] of e.tableSubscriptionsByPattern) {
13
14
  if (!i.includes(t)) continue;
@@ -18,23 +19,23 @@ var V = z({ scope: "voltro:start:cdc" }), H = (e) => (t) => {
18
19
  }));
19
20
  }
20
21
  return n;
21
- }, U = () => !process.env.PG_HOST && !process.env.PG_DATABASE ? null : {
22
+ }, W = () => !process.env.PG_HOST && !process.env.PG_DATABASE ? null : {
22
23
  host: process.env.PG_HOST ?? "localhost",
23
24
  port: Number(process.env.PG_PORT ?? 5432),
24
25
  user: process.env.PG_USER ?? "app",
25
26
  password: process.env.PG_PASSWORD ?? "app",
26
27
  database: process.env.PG_DATABASE ?? "app"
27
28
  }, ge = (e) => {
28
- let t = e.channel ?? "framework_changes", n = H(e), r = null, i = null;
29
+ let t = e.channel ?? "framework_changes", n = U(e), r = null, i = null;
29
30
  return {
30
31
  start: async () => {
31
- let a = U();
32
+ let a = W();
32
33
  if (!a) {
33
- V.debug("skipped — no PG_* env vars set");
34
+ H.debug("skipped — no PG_* env vars set");
34
35
  return;
35
36
  }
36
37
  if (e.tableSubscriptionsByPattern.size === 0) {
37
- V.debug("skipped — no pages declare cacheInvalidatesOn");
38
+ H.debug("skipped — no pages declare cacheInvalidatesOn");
38
39
  return;
39
40
  }
40
41
  r = new (await (import("pg"))).default.Client(a), await r.connect(), i = (e) => {
@@ -43,16 +44,16 @@ var V = z({ scope: "voltro:start:cdc" }), H = (e) => (t) => {
43
44
  try {
44
45
  r = JSON.parse(e.payload);
45
46
  } catch (e) {
46
- V.warn("bad CDC payload", {}, e);
47
+ H.warn("bad CDC payload", {}, e);
47
48
  return;
48
49
  }
49
50
  let i = n(r.table);
50
- i > 0 && V.info(`${r.op} on '${r.table}' → dropped ${i} cache entries`, {
51
+ i > 0 && H.info(`${r.op} on '${r.table}' → dropped ${i} cache entries`, {
51
52
  op: r.op,
52
53
  table: r.table,
53
54
  dropped: i
54
55
  });
55
- }, r.on("notification", i), await r.query(`LISTEN ${t}`), V.info(`LISTEN ${t}`, {
56
+ }, r.on("notification", i), await r.query(`LISTEN ${t}`), H.info(`LISTEN ${t}`, {
56
57
  channel: t,
57
58
  host: a.host,
58
59
  port: a.port,
@@ -68,7 +69,7 @@ var V = z({ scope: "voltro:start:cdc" }), H = (e) => (t) => {
68
69
  }
69
70
  }
70
71
  };
71
- }, W = z({ scope: "voltro:start:isr-cache" }), G = "voltro_isr_cache", _e = (e) => `
72
+ }, G = B({ scope: "voltro:start:isr-cache" }), K = "voltro_isr_cache", _e = (e) => `
72
73
  CREATE TABLE IF NOT EXISTS ${e} (
73
74
  cache_key TEXT PRIMARY KEY,
74
75
  html TEXT NOT NULL,
@@ -80,7 +81,7 @@ CREATE TABLE IF NOT EXISTS ${e} (
80
81
  );
81
82
  CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
82
83
  `, ve = (e) => {
83
- let t = e.tableName ?? G, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
84
+ let t = e.tableName ?? K, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
84
85
  i || (i = !0, await n.query(_e(t)));
85
86
  })().then(async () => {
86
87
  let e = await n.query(`SELECT * FROM ${t}`);
@@ -93,10 +94,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
93
94
  staleWhileRevalidateMs: Number(t.stale_while_revalidate_ms)
94
95
  });
95
96
  }).catch((e) => {
96
- W.warn("postgres bootstrap failed", {}, e);
97
+ G.warn("postgres bootstrap failed", {}, e);
97
98
  }), o = (e, t) => {
98
99
  a.then(() => n.query(e, t)).catch((e) => {
99
- W.warn("postgres write failed", {}, e);
100
+ G.warn("postgres write failed", {}, e);
100
101
  });
101
102
  };
102
103
  return {
@@ -141,14 +142,14 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
141
142
  return Array.from(r.entries());
142
143
  }
143
144
  };
144
- }, K = z({ scope: "voltro:start" }), ye = await (async () => {
145
+ }, q = B({ scope: "voltro:start" }), ye = await (async () => {
145
146
  try {
146
- let e = L(ue(de(import.meta.url)), "..", "package.json"), t = await B.readFile(e, "utf8");
147
+ let e = I(fe(R(import.meta.url)), "..", "package.json"), t = await V.readFile(e, "utf8");
147
148
  return JSON.parse(t).version ?? "0.0.0";
148
149
  } catch {
149
150
  return "0.0.0";
150
151
  }
151
- })(), q = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", be = (e) => e instanceof Error && e._voltroControl === "not-found", J = {
152
+ })(), J = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", be = (e) => e instanceof Error && e._voltroControl === "not-found", Y = {
152
153
  ".html": "text/html; charset=utf-8",
153
154
  ".js": "application/javascript",
154
155
  ".mjs": "application/javascript",
@@ -166,13 +167,13 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
166
167
  ".woff": "font/woff",
167
168
  ".woff2": "font/woff2",
168
169
  ".txt": "text/plain; charset=utf-8"
169
- }, xe = (e) => J[I(e).toLowerCase()] ?? "application/octet-stream", Y = (e) => e.replace(/<script\b(?=[^>]*\btype="module")(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<script\b(?=[^>]*\bsrc=)(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<link\b[^>]*\brel="modulepreload"[^>]*>/gi, ""), X = async (e) => {
170
+ }, xe = (e) => Y[F(e).toLowerCase()] ?? "application/octet-stream", X = (e) => e.replace(/<script\b(?=[^>]*\btype="module")(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<script\b(?=[^>]*\bsrc=)(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<link\b[^>]*\brel="modulepreload"[^>]*>/gi, ""), Z = async (e) => {
170
171
  try {
171
- return await B.stat(e), !0;
172
+ return await V.stat(e), !0;
172
173
  } catch {
173
174
  return !1;
174
175
  }
175
- }, Z = (e) => {
176
+ }, Q = (e) => {
176
177
  let t = [], n = 0, r = e.split("/").filter(Boolean), i = [];
177
178
  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, "\\$&"));
178
179
  let a = i.join("/");
@@ -182,7 +183,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
182
183
  priority: n
183
184
  };
184
185
  }, Se = (e) => e.map((e) => {
185
- let { regex: t, paramNames: n, priority: r } = Z(e.pattern);
186
+ let { regex: t, paramNames: n, priority: r } = Q(e.pattern);
186
187
  return {
187
188
  pattern: e.pattern,
188
189
  file: e.file,
@@ -204,31 +205,31 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
204
205
  }
205
206
  return null;
206
207
  }, we = async (e) => {
207
- let { provider: t, route: n, params: r, pathname: a, dirs: o, shellTemplate: s, requestHeaders: c, requestUrl: u = "", apiOrigin: d, apiName: f } = e, p = D(d, c.cookie, { apiName: f }), m = await t.getPage(n.pattern, n.file), h = l(m, n.file), g = h === "spa" && re(o, n.dirChain).some((e) => e.hasLayout);
208
- if (h !== "ssr" && h !== "isr" && !g) return null;
209
- let _ = re(o, n.dirChain), v = {}, y = null, te, ne = new AbortController();
208
+ let { provider: t, route: n, params: r, pathname: i, dirs: a, shellTemplate: s, requestHeaders: c, requestUrl: l = "", apiOrigin: u, apiName: ee } = e, d = C(u, c.cookie, { apiName: ee }), f = await t.getPage(n.pattern, n.file), p = o(f, n.file), g = p === "spa" && ie(a, n.dirChain).some((e) => e.hasLayout);
209
+ if (p !== "ssr" && p !== "isr" && !g) return null;
210
+ let v = ie(a, n.dirChain), y = {}, b = null, te, x = new AbortController();
210
211
  try {
211
- ({chain: te, segmentLoaderData: v} = await b({
212
- entries: _,
212
+ ({chain: te, segmentLoaderData: y} = await h({
213
+ entries: v,
213
214
  loadLayout: (e) => t.getLayout(e.dir),
214
215
  loadError: (e) => t.getError(e.dir),
215
216
  loadPending: (e) => t.getPending(e.dir),
216
217
  ctx: {
217
218
  params: r,
218
- pathname: a,
219
- signal: ne.signal,
219
+ pathname: i,
220
+ signal: x.signal,
220
221
  headers: c,
221
- query: p
222
+ query: d
222
223
  }
223
- })), typeof m.loader == "function" && !g && (y = await m.loader({
224
+ })), typeof f.loader == "function" && !g && (b = await f.loader({
224
225
  params: r,
225
- pathname: a,
226
- signal: ne.signal,
226
+ pathname: i,
227
+ signal: x.signal,
227
228
  headers: c,
228
- query: p
229
+ query: d
229
230
  }));
230
231
  } catch (e) {
231
- if (q(e)) return {
232
+ if (J(e)) return {
232
233
  control: "redirect",
233
234
  status: e.status,
234
235
  location: e.location
@@ -236,48 +237,48 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
236
237
  if (be(e)) return { control: "not-found" };
237
238
  throw e;
238
239
  }
239
- let S = await t.getHelpers(), C = {
240
+ let w = await t.getHelpers(), ne = {
240
241
  pattern: n.pattern,
241
- Component: m.default,
242
- meta: m.meta,
243
- loader: m.loader,
244
- ErrorBoundary: m.ErrorBoundary,
245
- Pending: m.Pending,
246
- renderMode: h,
242
+ Component: f.default,
243
+ meta: f.meta,
244
+ loader: f.loader,
245
+ ErrorBoundary: f.ErrorBoundary,
246
+ Pending: f.Pending,
247
+ renderMode: p,
247
248
  chain: te
248
- }, w = {
249
- cookies: S.parseCookieHeader(c.cookie),
249
+ }, E = {
250
+ cookies: w.parseCookieHeader(c.cookie),
250
251
  headers: c,
251
- url: u
252
- }, T = S.i18n?.resolve(c.cookie, c["accept-language"]), O = m.interactive ?? "full";
252
+ url: l
253
+ }, D = w.i18n?.resolve(c.cookie, c["accept-language"]), O = f.interactive ?? "full";
253
254
  if (g) {
254
255
  let e = M({
255
- helpers: S,
256
+ helpers: w,
256
257
  page: n.pattern,
257
258
  interactive: O,
258
259
  delivery: "stream",
259
260
  renderInput: {
260
- descriptor: C,
261
+ descriptor: ne,
261
262
  params: r,
262
- pathname: a,
263
- segmentLoaderData: v,
264
- requestContext: w,
265
- ...T ? {
266
- outerWrap: T.outerWrap,
267
- locale: T.locale
263
+ pathname: i,
264
+ segmentLoaderData: y,
265
+ requestContext: E,
266
+ ...D ? {
267
+ outerWrap: D.outerWrap,
268
+ locale: D.locale
268
269
  } : {}
269
270
  }
270
- }), t = T?.locale ?? w.cookies["voltro:lang"] ?? "en", i = (t) => x({
271
- helpers: S,
271
+ }), t = D?.locale ?? E.cookies["voltro:lang"] ?? "en", a = (t) => _({
272
+ helpers: w,
272
273
  prepared: e,
273
274
  meta: t,
274
275
  interactive: O
275
276
  });
276
277
  if (e.hasDeferred) {
277
- let n = A({ shell: s });
278
+ let n = T({ shell: s });
278
279
  return {
279
280
  renderMode: "spa",
280
- stream: (r) => E({
281
+ stream: (r) => S({
281
282
  res: r,
282
283
  shell: n,
283
284
  htmlLang: t,
@@ -285,58 +286,58 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
285
286
  "content-type": "text/html; charset=utf-8",
286
287
  "x-voltro-rendered-by": "spa"
287
288
  },
288
- headInjection: i,
289
- render: (t) => S.renderPageToStream({
289
+ headInjection: a,
290
+ render: (t) => w.renderPageToStream({
290
291
  ...e.renderInput,
291
292
  ...t
292
293
  }),
293
294
  onShellError: (e) => {
294
- K.error("spa layout shell render failed", { pathname: a }, e), r.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), r.end("server error");
295
+ q.error("spa layout shell render failed", { pathname: i }, e), r.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), r.end("server error");
295
296
  }
296
297
  })
297
298
  };
298
299
  }
299
- let o = F({
300
- helpers: S,
300
+ let o = N({
301
+ helpers: w,
301
302
  prepared: e
302
303
  });
303
304
  return {
304
- html: s.replace("<div id=\"root\"></div>", `<div id="root">${o.body}</div>`).replace("</head>", ` ${i(o.meta)}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${t}"`),
305
+ html: s.replace("<div id=\"root\"></div>", `<div id="root">${o.body}</div>`).replace("</head>", ` ${a(o.meta)}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${t}"`),
305
306
  renderMode: "spa",
306
307
  revalidateMs: null
307
308
  };
308
309
  }
309
- let k = ee({
310
- helpers: S,
310
+ let k = m({
311
+ helpers: w,
311
312
  page: n.pattern,
312
- pathname: a,
313
- renderMode: h,
313
+ pathname: i,
314
+ renderMode: p,
314
315
  interactive: O,
315
- loaderData: y,
316
- segmentLoaderData: v,
317
- pageLoaderRan: typeof m.loader == "function"
318
- }), ie = {
319
- descriptor: C,
316
+ loaderData: b,
317
+ segmentLoaderData: y,
318
+ pageLoaderRan: typeof f.loader == "function"
319
+ }), A = {
320
+ descriptor: ne,
320
321
  params: r,
321
- pathname: a,
322
+ pathname: i,
322
323
  loaderData: k.loaderData,
323
324
  segmentLoaderData: k.segmentLoaderData,
324
- requestContext: w,
325
- ...T ? {
326
- outerWrap: T.outerWrap,
327
- locale: T.locale
325
+ requestContext: E,
326
+ ...D ? {
327
+ outerWrap: D.outerWrap,
328
+ locale: D.locale
328
329
  } : {}
329
330
  }, j = (e) => [
330
- S.renderMetaToHtml(e),
331
+ w.renderMetaToHtml(e),
331
332
  `<meta name="voltro-interactive" content="${O}" />`,
332
- ...k.hasDeferred ? [S.renderDeferredRegistryScript()] : [],
333
- S.renderRouterStateScript(k.stateInput)
333
+ ...k.hasDeferred ? [w.renderDeferredRegistryScript()] : [],
334
+ w.renderRouterStateScript(k.stateInput)
334
335
  ].filter((e) => e.length > 0).join("\n ");
335
- if (h === "ssr" && O === "full" && e.buffered !== !0) {
336
- let e = T?.locale ?? w.cookies["voltro:lang"] ?? "en", t = A({ shell: s });
336
+ if (p === "ssr" && O === "full" && e.buffered !== !0) {
337
+ let e = D?.locale ?? E.cookies["voltro:lang"] ?? "en", t = T({ shell: s });
337
338
  return {
338
339
  renderMode: "ssr",
339
- stream: (n) => E({
340
+ stream: (n) => S({
340
341
  res: n,
341
342
  shell: t,
342
343
  htmlLang: e,
@@ -345,26 +346,26 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
345
346
  "x-voltro-rendered-by": "ssr"
346
347
  },
347
348
  headInjection: j,
348
- render: (e) => S.renderPageToStream({
349
- ...ie,
349
+ render: (e) => w.renderPageToStream({
350
+ ...A,
350
351
  ...e
351
352
  }),
352
353
  onShellError: (e) => {
353
- K.error("ssr shell render failed", { pathname: a }, e), n.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), n.end("server error");
354
+ q.error("ssr shell render failed", { pathname: i }, e), n.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), n.end("server error");
354
355
  }
355
356
  })
356
357
  };
357
358
  }
358
- let { html: ae, meta: oe } = S.renderPageToHtml(ie), se = j(oe), N = T?.locale ?? w.cookies["voltro:lang"] ?? "en", P = s.replace("<div id=\"root\"></div>", `<div id="root">${ae}</div>`).replace("</head>", ` ${se}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${N}"`);
359
- O === "none" && (P = Y(P));
360
- let ce = h === "isr" ? i(m.revalidate) : null;
359
+ let { html: ae, meta: oe } = w.renderPageToHtml(A), se = j(oe), ce = D?.locale ?? E.cookies["voltro:lang"] ?? "en", P = s.replace("<div id=\"root\"></div>", `<div id="root">${ae}</div>`).replace("</head>", ` ${se}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${ce}"`);
360
+ O === "none" && (P = X(P));
361
+ let le = p === "isr" ? re(f.revalidate) : null;
361
362
  return {
362
363
  html: P,
363
- renderMode: h,
364
- revalidateMs: ce
364
+ renderMode: p,
365
+ revalidateMs: le
365
366
  };
366
- }, Q = (e) => e === "" ? "/" : e, Te = async (e) => {
367
- let t = await import(fe(e).href);
367
+ }, $ = (e) => e === "" ? "/" : e, Te = async (e) => {
368
+ let t = await import(z(e).href);
368
369
  return {
369
370
  kind: "bundle",
370
371
  getPageMeta: () => t.pageMeta,
@@ -374,18 +375,18 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
374
375
  return typeof r == "function" ? await r() : r;
375
376
  },
376
377
  async getLayout(e) {
377
- let n = t.layouts[Q(e)];
378
- if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${Q(e)}`);
378
+ let n = t.layouts[$(e)];
379
+ if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${$(e)}`);
379
380
  return typeof n == "function" ? await n() : n;
380
381
  },
381
382
  async getError(e) {
382
- let n = t.errors[Q(e)];
383
- if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${Q(e)}`);
383
+ let n = t.errors[$(e)];
384
+ if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${$(e)}`);
384
385
  return typeof n == "function" ? await n() : n;
385
386
  },
386
387
  async getPending(e) {
387
- let n = t.pendings[Q(e)];
388
- if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${Q(e)}`);
388
+ let n = t.pendings[$(e)];
389
+ if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${$(e)}`);
389
390
  return typeof n == "function" ? await n() : n;
390
391
  },
391
392
  async getHelpers() {
@@ -394,7 +395,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
394
395
  async close() {}
395
396
  };
396
397
  }, Ee = async (e, t) => {
397
- let n = await f("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, a = await n.createServer({
398
+ let n = await y("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, a = await n.createServer({
398
399
  root: t,
399
400
  envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
400
401
  plugins: [r(), i()],
@@ -406,121 +407,121 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
406
407
  },
407
408
  appType: "custom",
408
409
  logLevel: "warn"
409
- }), o = L(e, "src", "pages"), s = (e) => e === "" ? o : L(o, e), c = j({ limit: u() }), l = (e) => c.load(e, async () => await a.ssrLoadModule(e));
410
+ }), o = I(e, "src", "pages"), c = (e) => e === "" ? o : I(o, e), l = D({ limit: s() }), u = (e) => l.load(e, async () => await a.ssrLoadModule(e));
410
411
  return {
411
412
  kind: "middleware",
412
413
  async getPage(e, t) {
413
- return l(L(o, t));
414
+ return u(I(o, t));
414
415
  },
415
416
  async getLayout(e) {
416
- return l(L(s(e), "layout.tsx"));
417
+ return u(I(c(e), "layout.tsx"));
417
418
  },
418
419
  async getError(e) {
419
- return l(L(s(e), "error.tsx"));
420
+ return u(I(c(e), "error.tsx"));
420
421
  },
421
422
  async getPending(e) {
422
- return l(L(s(e), "loading.tsx"));
423
+ return u(I(c(e), "loading.tsx"));
423
424
  },
424
425
  async getHelpers() {
425
- return await l("@voltro/web/ssr");
426
+ return await u("@voltro/web/ssr");
426
427
  },
427
428
  async close() {
428
429
  await a.close();
429
430
  }
430
431
  };
431
- }, De = async (i) => {
432
- let l = le(), u = await oe(i);
433
- if (!u) return {
432
+ }, De = async (o) => {
433
+ let s = de(), m = await k(o);
434
+ if (!m) return {
434
435
  ran: !1,
435
436
  exitCode: 0
436
437
  };
437
- if (u.type !== "web") return K.error("only web apps are supported by `voltro start`"), {
438
+ if (m.type !== "web") return q.error("only web apps are supported by `voltro start`"), {
438
439
  ran: !0,
439
440
  exitCode: 1
440
441
  };
441
- let { resolved: f } = await t({
442
- rawEnv: u.env,
442
+ let { resolved: h } = await g({
443
+ rawEnv: m.env,
443
444
  appType: "web",
444
- root: i,
445
- log: K,
445
+ root: o,
446
+ log: q,
446
447
  generateTypes: !1
447
- }), ee = f.publicValues;
448
- l.mark("config");
449
- let b = v();
450
- _(b), K.info("booting voltro start", { app: u.name ?? "(unnamed)" });
451
- let x = (() => {
448
+ }), _ = h.publicValues;
449
+ s.mark("config");
450
+ let y = p();
451
+ f(y), q.info("booting voltro start", { app: m.name ?? "(unnamed)" });
452
+ let S = (() => {
452
453
  if (process.env.VOLTRO_IS_DASHBOARD === "1" && process.env.VOLTRO_DASHBOARD_PORT) return Number(process.env.VOLTRO_DASHBOARD_PORT);
453
454
  if (process.env.PORT) {
454
455
  let e = Number(process.env.PORT);
455
456
  if (Number.isFinite(e) && e > 0) return e;
456
457
  }
457
- return u.port ?? 5173;
458
- })(), E = L(i, ".framework"), D = L(E, "dist"), A = L(D, "index.html");
459
- if (!await X(A)) return K.error("no built dist found — run `voltro build` first", { distDir: D }), {
458
+ return m.port ?? 5173;
459
+ })(), C = I(o, ".framework"), T = I(C, "dist"), re = I(T, "index.html");
460
+ if (!await Z(re)) return q.error("no built dist found — run `voltro build` first", { distDir: T }), {
460
461
  ran: !0,
461
462
  exitCode: 1
462
463
  };
463
- let j = u.name ?? "unnamed", M = p("web", j, x), F = a(j);
464
- await O({
464
+ let D = m.name ?? "unnamed", M = l("web", D, S), N = r(D);
465
+ await w({
465
466
  id: M,
466
- name: j,
467
+ name: D,
467
468
  kind: "web",
468
- url: `http://localhost:${x}`,
469
+ url: `http://localhost:${S}`,
469
470
  pid: process.pid,
470
471
  status: "booting",
471
- logFile: F.path
472
+ logFile: N.path
472
473
  }).catch(() => {});
473
- let ue = L(D, "_voltro", "shell.html"), I = await X(ue) ? await B.readFile(ue, "utf8") : await B.readFile(A, "utf8"), R = await S(i, u), de = ae(R[0]), fe = R[0]?.name, z, V = null;
474
- process.env.SSR_CACHE === "postgres" && process.env.PG_HOST ? (V = new (await (import("pg"))).default.Client({
474
+ let fe = I(T, "_voltro", "shell.html"), F = await Z(fe) ? await V.readFile(fe, "utf8") : await V.readFile(re, "utf8"), L = await v(o, m), R = O(L[0]), z = L[0]?.name, B, H = null;
475
+ process.env.SSR_CACHE === "postgres" && process.env.PG_HOST ? (H = new (await (import("pg"))).default.Client({
475
476
  host: process.env.PG_HOST,
476
477
  port: Number(process.env.PG_PORT ?? 5432),
477
478
  user: process.env.PG_USER ?? "app",
478
479
  password: process.env.PG_PASSWORD ?? "app",
479
480
  database: process.env.PG_DATABASE ?? "app"
480
- }), await V.connect(), z = ve({ client: V }), K.info("isr cache backend: postgres", { table: "voltro_isr_cache" })) : (z = o(), K.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" })), await ie(i, u, R, ee);
481
- let { pages: H, dirs: U } = await C(L(i, "src", "pages")), W = Se(H);
482
- l.mark("scan");
483
- let G = L(D, "server", "ssrEntry.js"), _e = await X(G);
484
- if (!_e && process.env.NODE_ENV === "production") throw K.fatal(`production \`voltro start\` requires a precompiled SSR bundle at ${G} 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 ${G}`);
485
- let q = _e ? await Te(G) : await Ee(i, E);
486
- K.info(`ssr loader: ${q.kind}`, {
487
- kind: q.kind,
488
- ...q.kind === "middleware" ? { hint: "no pre-built SSR bundle found; run `voltro build` to enable bundle mode" } : {}
489
- }), l.mark("provider");
490
- let be = q.getPageMeta?.(), J = /* @__PURE__ */ new Map(), Y = 0;
491
- for (let e of H) {
481
+ }), await H.connect(), B = ve({ client: H }), q.info("isr cache backend: postgres", { table: "voltro_isr_cache" })) : (B = j(), q.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" })), await E(o, m, L, _);
482
+ let { pages: U, dirs: W } = await te(I(o, "src", "pages")), G = Se(U);
483
+ s.mark("scan");
484
+ let K = I(T, "server", "ssrEntry.js"), _e = await Z(K);
485
+ if (!_e && process.env.NODE_ENV === "production") throw q.fatal(`production \`voltro start\` requires a precompiled SSR bundle at ${K} 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 ${K}`);
486
+ let J = _e ? await Te(K) : await Ee(o, C);
487
+ q.info(`ssr loader: ${J.kind}`, {
488
+ kind: J.kind,
489
+ ...J.kind === "middleware" ? { hint: "no pre-built SSR bundle found; run `voltro build` to enable bundle mode" } : {}
490
+ }), s.mark("provider");
491
+ let be = J.getPageMeta?.(), Y = /* @__PURE__ */ new Map(), X = 0;
492
+ for (let e of U) {
492
493
  let t = be?.[e.pattern];
493
494
  if (t) {
494
- J.set(e.pattern, t);
495
+ Y.set(e.pattern, t);
495
496
  continue;
496
497
  }
497
498
  try {
498
- let t = await q.getPage(e.pattern, e.file);
499
- J.set(e.pattern, d(t, e.file)), Y++;
499
+ let t = await J.getPage(e.pattern, e.file);
500
+ Y.set(e.pattern, c(t, e.file)), X++;
500
501
  } catch (t) {
501
- K.warn("failed to load page metadata", { pattern: e.pattern }, t);
502
+ q.warn("failed to load page metadata", { pattern: e.pattern }, t);
502
503
  }
503
504
  }
504
- K.info(`registered ${J.size} route(s)`, {
505
- count: J.size,
506
- fromManifest: J.size - Y,
507
- fromModules: Y
508
- }), l.mark("routes");
509
- let Z = /* @__PURE__ */ new Set();
510
- for (let e of W) J.get(e.pattern)?.renderMode === "spa" && re(U, e.dirChain).some((e) => e.hasLayout) && Z.add(e.pattern);
511
- Z.size > 0 && K.info(`${Z.size} spa route(s) render an SSR layout shell`, { patterns: [...Z] });
512
- let Q = /* @__PURE__ */ new Map();
513
- for (let [e, t] of J) t.cacheInvalidatesOn.length > 0 && Q.set(e, t.cacheInvalidatesOn);
505
+ q.info(`registered ${Y.size} route(s)`, {
506
+ count: Y.size,
507
+ fromManifest: Y.size - X,
508
+ fromModules: X
509
+ }), s.mark("routes");
510
+ let Q = /* @__PURE__ */ new Set();
511
+ for (let e of G) Y.get(e.pattern)?.renderMode === "spa" && ie(W, e.dirChain).some((e) => e.hasLayout) && Q.add(e.pattern);
512
+ Q.size > 0 && q.info(`${Q.size} spa route(s) render an SSR layout shell`, { patterns: [...Q] });
513
+ let $ = /* @__PURE__ */ new Map();
514
+ for (let [e, t] of Y) t.cacheInvalidatesOn.length > 0 && $.set(e, t.cacheInvalidatesOn);
514
515
  let De = /* @__PURE__ */ new Map();
515
- for (let e of W) De.set(e.pattern, e.regex);
516
+ for (let e of G) De.set(e.pattern, e.regex);
516
517
  let Oe = ge({
517
- cache: z,
518
- tableSubscriptionsByPattern: Q,
518
+ cache: B,
519
+ tableSubscriptionsByPattern: $,
519
520
  regexByPattern: De
520
521
  });
521
- await Oe.start(), l.mark("cdc");
522
- let ke = se(), Ae = H.map((e) => {
523
- let t = J.get(e.pattern);
522
+ await Oe.start(), s.mark("cdc");
523
+ let ke = A(), Ae = U.map((e) => {
524
+ let t = Y.get(e.pattern);
524
525
  return {
525
526
  pattern: e.pattern,
526
527
  file: e.file,
@@ -535,20 +536,20 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
535
536
  hasMeta: t?.hasMeta ?? !1,
536
537
  hasGetStaticPaths: t?.hasGetStaticPaths ?? !1
537
538
  };
538
- }), je = U.map((e) => ({
539
+ }), je = W.map((e) => ({
539
540
  dir: e.dir,
540
541
  hasLayout: e.hasLayout,
541
542
  hasError: e.hasError,
542
543
  hasPending: e.hasPending,
543
544
  hasNotFound: e.hasNotFound
544
- })), Me = V ? "postgres" : "memory", Ne = {
545
+ })), Me = H ? "postgres" : "memory", Ne = {
545
546
  kind: "web",
546
547
  meta: {
547
- name: u.name ?? "(unnamed)",
548
+ name: m.name ?? "(unnamed)",
548
549
  voltroVersion: ye,
549
- port: x,
550
+ port: S,
550
551
  mode: "start",
551
- apis: Object.entries(u.apis ?? {}).map(([e, t]) => {
552
+ apis: Object.entries(m.apis ?? {}).map(([e, t]) => {
552
553
  let n = {
553
554
  name: e,
554
555
  package: t.package
@@ -559,7 +560,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
559
560
  routes: Ae,
560
561
  dirs: je,
561
562
  cache: () => {
562
- let e = Date.now(), t = z.snapshot().map(([t, n]) => ({
563
+ let e = Date.now(), t = B.snapshot().map(([t, n]) => ({
563
564
  key: t,
564
565
  renderedAt: n.renderedAt,
565
566
  expiresAt: n.expiresAt,
@@ -571,15 +572,15 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
571
572
  }));
572
573
  return {
573
574
  backend: Me,
574
- size: z.size(),
575
+ size: B.size(),
575
576
  entries: t
576
577
  };
577
578
  },
578
579
  metrics: () => me()
579
- }, Pe = async (e, t) => {
580
- let n = e.split("?")[0] ?? "/";
581
- if (u.name === "voltroDashboard" && n === "/api/dashboard/registry") try {
582
- let e = await k();
580
+ }, Pe = async (t, n) => {
581
+ let r = t.split("?")[0] ?? "/";
582
+ if (m.name === "voltroDashboard" && r === "/api/dashboard/registry") try {
583
+ let e = await ne();
583
584
  return {
584
585
  status: 200,
585
586
  headers: { "content-type": "application/json; charset=utf-8" },
@@ -592,7 +593,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
592
593
  body: `registry read error: ${e.message}`
593
594
  };
594
595
  }
595
- if (u.name === "voltroDashboard" && n === "/api/dashboard/config") {
596
+ if (m.name === "voltroDashboard" && r === "/api/dashboard/config") {
596
597
  let e = process.env.VOLTRO_DASHBOARD_APPS, t = [];
597
598
  if (e && e.trim().length > 0) {
598
599
  let n;
@@ -621,38 +622,38 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
621
622
  body: JSON.stringify({ apps: t })
622
623
  };
623
624
  }
624
- if (n === "/_voltro/inspect/logs") {
625
- let n = m(e, c(), t);
625
+ if (r === "/_voltro/inspect/logs") {
626
+ let e = ee(t, a(), n);
626
627
  return {
627
- status: n.status,
628
+ status: e.status,
628
629
  headers: {
629
- "content-type": n.contentType,
630
- ...T(t.origin)
630
+ "content-type": e.contentType,
631
+ ...ue(n.origin)
631
632
  },
632
- body: n.body
633
+ body: e.body
633
634
  };
634
635
  }
635
- if (n !== "/_voltro/inspect/stream") {
636
- let n = ne(e, Ne, t);
637
- if (n !== null) return {
638
- status: n.status,
636
+ if (r !== "/_voltro/inspect/stream") {
637
+ let e = le(t, Ne, n);
638
+ if (e !== null) return {
639
+ status: e.status,
639
640
  headers: {
640
- "content-type": n.contentType,
641
- ...n.headers ?? {}
641
+ "content-type": e.contentType,
642
+ ...e.headers ?? {}
642
643
  },
643
- body: n.body
644
+ body: e.body
644
645
  };
645
646
  }
646
- let a = Ce(n, W), o = a ? J.get(a.route.pattern)?.renderMode : void 0, s = o === "ssr" || o === "isr" || (a ? Z.has(a.route.pattern) : !1), l = n === "/" ? L(D, "index.html") : L(D, n.replace(/^\//, ""), "index.html");
647
- if (!s && await X(l)) return {
647
+ let i = Ce(r, G), s = i ? Y.get(i.route.pattern)?.renderMode : void 0, c = s === "ssr" || s === "isr" || (i ? Q.has(i.route.pattern) : !1), l = r === "/" ? I(T, "index.html") : I(T, r.replace(/^\//, ""), "index.html");
648
+ if (!c && await Z(l)) return {
648
649
  status: 200,
649
650
  headers: { "content-type": "text/html; charset=utf-8" },
650
- body: await B.readFile(l)
651
+ body: await V.readFile(l)
651
652
  };
652
- if (/\.[a-z0-9]+$/i.test(n)) {
653
- let e = L(D, n.replace(/^\//, ""));
654
- if (await X(e)) {
655
- let t = await B.readFile(e);
653
+ if (/\.[a-z0-9]+$/i.test(r)) {
654
+ let e = I(T, r.replace(/^\//, ""));
655
+ if (await Z(e)) {
656
+ let t = await V.readFile(e);
656
657
  return {
657
658
  status: 200,
658
659
  headers: { "content-type": xe(e) },
@@ -660,64 +661,64 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
660
661
  };
661
662
  }
662
663
  }
663
- let d = Ce(n, W);
664
+ let d = Ce(r, G);
664
665
  if (!d) return {
665
666
  status: 404,
666
667
  headers: { "content-type": "text/plain" },
667
668
  body: "not found"
668
669
  };
669
- let f = J.get(d.route.pattern), p = t["x-tenant"] ?? "anonymous", g = (await q.getHelpers()).i18n?.resolve(t.cookie, t["accept-language"])?.locale, _ = r(n, {
670
+ let f = Y.get(d.route.pattern), p = n["x-tenant"] ?? "anonymous", h = (await J.getHelpers()).i18n?.resolve(n.cookie, n["accept-language"])?.locale, g = u(r, {
670
671
  ...f?.tenantAware ? { tenant: p } : {},
671
- ...g ? { locale: g } : {}
672
- }), v = z.get(_), y = Date.now();
673
- if (v && ce(v, y)) return b.emit("cache", {
672
+ ...h ? { locale: h } : {}
673
+ }), _ = B.get(g), v = Date.now();
674
+ if (_ && e(_, v)) return y.emit("cache", {
674
675
  kind: "HIT",
675
- key: _,
676
- ageMs: y - v.renderedAt
676
+ key: g,
677
+ ageMs: v - _.renderedAt
677
678
  }), {
678
679
  status: 200,
679
680
  headers: {
680
681
  "content-type": "text/html; charset=utf-8",
681
682
  "x-voltro-rendered-by": "isr",
682
683
  "x-voltro-cache": "HIT",
683
- "x-voltro-cache-key": _,
684
- "x-voltro-cache-age-ms": String(y - v.renderedAt)
684
+ "x-voltro-cache-key": g,
685
+ "x-voltro-cache-age-ms": String(v - _.renderedAt)
685
686
  },
686
- body: v.html
687
+ body: _.html
687
688
  };
688
- if (v && h(v, y)) return b.emit("cache", {
689
+ if (_ && b(_, v)) return y.emit("cache", {
689
690
  kind: "STALE",
690
- key: _,
691
- ageMs: y - v.renderedAt
691
+ key: g,
692
+ ageMs: v - _.renderedAt
692
693
  }), (async () => {
693
694
  try {
694
- let r = await we({
695
- provider: q,
696
- root: i,
695
+ let e = await we({
696
+ provider: J,
697
+ root: o,
697
698
  route: d.route,
698
699
  params: d.params,
699
- pathname: n,
700
- dirs: U,
701
- shellTemplate: I,
702
- requestHeaders: t,
703
- apiOrigin: de,
704
- apiName: fe,
705
- requestUrl: e,
700
+ pathname: r,
701
+ dirs: W,
702
+ shellTemplate: F,
703
+ requestHeaders: n,
704
+ apiOrigin: R,
705
+ apiName: z,
706
+ requestUrl: t,
706
707
  buffered: !0
707
708
  });
708
- if (r && "html" in r && r.renderMode === "isr" && r.revalidateMs !== null) {
709
- let e = J.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0, t = Date.now();
710
- z.set(_, {
711
- html: r.html,
712
- renderedAt: t,
713
- expiresAt: t + r.revalidateMs,
714
- servableUntil: t + r.revalidateMs + e,
715
- revalidateMs: r.revalidateMs,
716
- staleWhileRevalidateMs: e
709
+ if (e && "html" in e && e.renderMode === "isr" && e.revalidateMs !== null) {
710
+ let t = Y.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0, n = Date.now();
711
+ B.set(g, {
712
+ html: e.html,
713
+ renderedAt: n,
714
+ expiresAt: n + e.revalidateMs,
715
+ servableUntil: n + e.revalidateMs + t,
716
+ revalidateMs: e.revalidateMs,
717
+ staleWhileRevalidateMs: t
717
718
  });
718
719
  }
719
720
  } catch (e) {
720
- K.warn("background SWR refresh failed", { url: n }, e);
721
+ q.warn("background SWR refresh failed", { url: r }, e);
721
722
  }
722
723
  })(), {
723
724
  status: 200,
@@ -725,43 +726,43 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
725
726
  "content-type": "text/html; charset=utf-8",
726
727
  "x-voltro-rendered-by": "isr",
727
728
  "x-voltro-cache": "STALE",
728
- "x-voltro-cache-key": _,
729
- "x-voltro-cache-age-ms": String(y - v.renderedAt)
729
+ "x-voltro-cache-key": g,
730
+ "x-voltro-cache-age-ms": String(v - _.renderedAt)
730
731
  },
731
- body: v.html
732
+ body: _.html
732
733
  };
733
734
  try {
734
- let r = await we({
735
- provider: q,
736
- root: i,
735
+ let e = await we({
736
+ provider: J,
737
+ root: o,
737
738
  route: d.route,
738
739
  params: d.params,
739
- pathname: n,
740
- dirs: U,
741
- shellTemplate: I,
742
- requestHeaders: t,
743
- apiOrigin: de,
744
- apiName: fe,
745
- requestUrl: e
740
+ pathname: r,
741
+ dirs: W,
742
+ shellTemplate: F,
743
+ requestHeaders: n,
744
+ apiOrigin: R,
745
+ apiName: z,
746
+ requestUrl: t
746
747
  });
747
- if (!r) return {
748
+ if (!e) return {
748
749
  status: 200,
749
750
  headers: {
750
751
  "content-type": "text/html; charset=utf-8",
751
752
  "x-voltro-rendered-by": "spa-fallback"
752
753
  },
753
- body: I
754
+ body: F
754
755
  };
755
- if ("stream" in r) return {
756
+ if ("stream" in e) return {
756
757
  status: 200,
757
- headers: { "x-voltro-rendered-by": r.renderMode },
758
+ headers: { "x-voltro-rendered-by": e.renderMode },
758
759
  body: "",
759
- stream: r.stream
760
+ stream: e.stream
760
761
  };
761
- if ("control" in r) return r.control === "redirect" ? {
762
- status: r.status,
762
+ if ("control" in e) return e.control === "redirect" ? {
763
+ status: e.status,
763
764
  headers: {
764
- location: r.location,
765
+ location: e.location,
765
766
  "x-voltro-rendered-by": "loader-redirect"
766
767
  },
767
768
  body: ""
@@ -773,36 +774,36 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
773
774
  },
774
775
  body: "not found"
775
776
  };
776
- let a = {
777
+ let i = {
777
778
  "content-type": "text/html; charset=utf-8",
778
- "x-voltro-rendered-by": r.renderMode
779
+ "x-voltro-rendered-by": e.renderMode
779
780
  };
780
- if (r.renderMode === "isr" && r.revalidateMs !== null) {
781
- let e = Date.now(), t = J.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0;
782
- z.set(_, {
783
- html: r.html,
784
- renderedAt: e,
785
- expiresAt: e + r.revalidateMs,
786
- servableUntil: e + r.revalidateMs + t,
787
- revalidateMs: r.revalidateMs,
788
- staleWhileRevalidateMs: t
789
- }), a["x-voltro-cache"] = "MISS", a["x-voltro-cache-key"] = _, a["x-voltro-cache-revalidate-ms"] = String(r.revalidateMs), t > 0 && (a["x-voltro-cache-swr-ms"] = String(t)), b.emit("cache", {
781
+ if (e.renderMode === "isr" && e.revalidateMs !== null) {
782
+ let t = Date.now(), n = Y.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0;
783
+ B.set(g, {
784
+ html: e.html,
785
+ renderedAt: t,
786
+ expiresAt: t + e.revalidateMs,
787
+ servableUntil: t + e.revalidateMs + n,
788
+ revalidateMs: e.revalidateMs,
789
+ staleWhileRevalidateMs: n
790
+ }), i["x-voltro-cache"] = "MISS", i["x-voltro-cache-key"] = g, i["x-voltro-cache-revalidate-ms"] = String(e.revalidateMs), n > 0 && (i["x-voltro-cache-swr-ms"] = String(n)), y.emit("cache", {
790
791
  kind: "MISS",
791
- key: _,
792
- revalidateMs: r.revalidateMs,
793
- swrMs: t
792
+ key: g,
793
+ revalidateMs: e.revalidateMs,
794
+ swrMs: n
794
795
  });
795
- } else r.renderMode === "isr" && (a["x-voltro-cache"] = "BYPASS", b.emit("cache", {
796
+ } else e.renderMode === "isr" && (i["x-voltro-cache"] = "BYPASS", y.emit("cache", {
796
797
  kind: "BYPASS",
797
- key: _
798
+ key: g
798
799
  }));
799
800
  return {
800
801
  status: 200,
801
- headers: a,
802
- body: r.html
802
+ headers: i,
803
+ body: e.html
803
804
  };
804
805
  } catch (e) {
805
- return K.error("render error", { url: n }, e), w(e) ? {
806
+ return q.error("render error", { url: r }, e), x(e) ? {
806
807
  status: 500,
807
808
  headers: {
808
809
  "content-type": "text/plain; charset=utf-8",
@@ -816,17 +817,17 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
816
817
  };
817
818
  }
818
819
  }, Fe = !1, Ie = he(async (e, t) => {
819
- let n = e.url ?? "/", r = Date.now(), i = {};
820
- for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (i[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
821
- let a = n.split("?")[0] ?? "/", o = (e.method ?? "GET").toUpperCase();
822
- if (a === "/internal/liveness") {
820
+ let n = e.url ?? "/", r = Date.now(), a = {};
821
+ for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (a[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
822
+ let o = n.split("?")[0] ?? "/", s = (e.method ?? "GET").toUpperCase();
823
+ if (o === "/internal/liveness") {
823
824
  t.writeHead(200, {
824
825
  "content-type": "text/plain",
825
826
  "cache-control": "no-store"
826
827
  }), t.end("ok");
827
828
  return;
828
829
  }
829
- if (a === "/internal/readiness") {
830
+ if (o === "/internal/readiness") {
830
831
  let e = Fe ? 200 : 503;
831
832
  t.writeHead(e, {
832
833
  "content-type": "text/plain",
@@ -834,36 +835,37 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
834
835
  }), t.end(Fe ? "ready" : "not-ready");
835
836
  return;
836
837
  }
837
- if (o === "OPTIONS" && a.startsWith("/_voltro/inspect")) {
838
- let e = T(i.origin);
838
+ if (s === "OPTIONS" && o.startsWith("/_voltro/inspect")) {
839
+ let e = ue(a.origin);
839
840
  t.writeHead(204, e), t.end();
840
841
  return;
841
842
  }
842
- if (u.name === "voltroDashboard" && a === "/api/dashboard/proxy") {
843
+ if (m.name === "voltroDashboard" && o === "/api/dashboard/proxy") {
843
844
  let r = new URL(n, "http://x").searchParams.get("target");
844
845
  if (!r) {
845
846
  t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
846
847
  return;
847
848
  }
848
- if (!N(r)) {
849
- t.writeHead(400, { "content-type": "text/plain" }), t.end("target must be a loopback address");
849
+ if (!ce(r)) {
850
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("target not allowed: must be loopback or an origin configured in VOLTRO_DASHBOARD_APPS");
850
851
  return;
851
852
  }
852
853
  try {
853
- let n = { accept: i.accept ?? "application/json" }, a = i.authorization;
854
- a && (n.authorization = Array.isArray(a) ? a[0] : a);
855
- let s;
856
- if (o && o !== "GET" && o !== "HEAD") {
854
+ let n = { accept: a.accept ?? "application/json" }, i = a.authorization;
855
+ i ? n.authorization = Array.isArray(i) ? i[0] : i : oe(r) && process.env.VOLTRO_INSPECT_TOKEN && (n.authorization = `Bearer ${process.env.VOLTRO_INSPECT_TOKEN}`);
856
+ let o;
857
+ if (s && s !== "GET" && s !== "HEAD") {
857
858
  let t = [];
858
859
  for await (let n of e) t.push(n);
859
- s = Buffer.concat(t);
860
- let r = i["content-type"];
860
+ o = Buffer.concat(t);
861
+ let r = a["content-type"];
861
862
  r && (n["content-type"] = Array.isArray(r) ? r[0] : r);
862
863
  }
863
864
  let c = await fetch(r, {
864
- method: o,
865
+ redirect: "manual",
866
+ method: s,
865
867
  headers: n,
866
- ...s ? { body: new Uint8Array(s.buffer, s.byteOffset, s.byteLength) } : {}
868
+ ...o ? { body: new Uint8Array(o.buffer, o.byteOffset, o.byteLength) } : {}
867
869
  }), l = Buffer.from(await c.arrayBuffer());
868
870
  t.writeHead(c.status, {
869
871
  "content-type": c.headers.get("content-type") ?? "application/octet-stream",
@@ -874,26 +876,27 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
874
876
  }
875
877
  return;
876
878
  }
877
- if (u.name === "voltroDashboard" && a === "/api/dashboard/proxy/stream") {
879
+ if (m.name === "voltroDashboard" && o === "/api/dashboard/proxy/stream") {
878
880
  let r = new URL(n, "http://x").searchParams.get("target");
879
881
  if (!r) {
880
882
  t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
881
883
  return;
882
884
  }
883
- if (!N(r)) {
884
- t.writeHead(400, { "content-type": "text/plain" }), t.end("target must be a loopback address");
885
+ if (!ce(r)) {
886
+ t.writeHead(400, { "content-type": "text/plain" }), t.end("target not allowed: must be loopback or an origin configured in VOLTRO_DASHBOARD_APPS");
885
887
  return;
886
888
  }
887
- let a = new AbortController();
888
- e.on("close", () => a.abort()), t.on("close", () => a.abort());
889
+ let i = new AbortController();
890
+ e.on("close", () => i.abort()), t.on("close", () => i.abort());
889
891
  try {
890
- let e = i.authorization, n = await fetch(r, {
892
+ let e = a.authorization ?? (oe(r) && process.env.VOLTRO_INSPECT_TOKEN ? `Bearer ${process.env.VOLTRO_INSPECT_TOKEN}` : void 0), n = await fetch(r, {
893
+ redirect: "manual",
891
894
  method: "GET",
892
895
  headers: {
893
896
  accept: "text/event-stream",
894
897
  ...e ? { authorization: Array.isArray(e) ? e[0] : e } : {}
895
898
  },
896
- signal: a.signal
899
+ signal: i.signal
897
900
  });
898
901
  if (!n.ok || !n.body) {
899
902
  t.writeHead(n.status, { "content-type": "text/plain" }), t.end(`upstream ${n.status}`);
@@ -920,16 +923,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
920
923
  }
921
924
  return;
922
925
  }
923
- if (a === "/_voltro/inspect/stream") {
924
- let n = T(i.origin);
925
- if (te()) {
926
+ if (o === "/_voltro/inspect/stream") {
927
+ let n = ue(a.origin);
928
+ if (se()) {
926
929
  t.writeHead(503, {
927
930
  "content-type": "application/json",
928
931
  ...n
929
932
  }), t.end(JSON.stringify({ error: "inspect disabled (VOLTRO_INSPECT=off)" }));
930
933
  return;
931
934
  }
932
- let r = y(i);
935
+ let r = P(a);
933
936
  if (!r.ok) {
934
937
  t.writeHead(401, {
935
938
  "content-type": "application/json",
@@ -947,29 +950,29 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
947
950
  "x-accel-buffering": "no",
948
951
  ...n
949
952
  }), t.flushHeaders(), t.write("retry: 5000\n\n");
950
- for (let e of b.buffered()) t.write(g(e));
951
- let a = b.subscribe((e) => {
952
- t.write(g(e));
953
- }), o = setInterval(() => {
954
- t.write(s());
953
+ for (let e of y.buffered()) t.write(d(e));
954
+ let o = y.subscribe((e) => {
955
+ t.write(d(e));
956
+ }), s = setInterval(() => {
957
+ t.write(i());
955
958
  }, 15e3), c = () => {
956
- clearInterval(o), a();
959
+ clearInterval(s), o();
957
960
  };
958
961
  e.on("close", c), t.on("close", c);
959
962
  return;
960
963
  }
961
964
  try {
962
- let e = await Pe(n, i);
963
- if (e.stream ? e.stream(t) : (t.writeHead(e.status, e.headers), t.end(e.body)), !a.startsWith("/_voltro/inspect")) {
965
+ let e = await Pe(n, a);
966
+ if (e.stream ? e.stream(t) : (t.writeHead(e.status, e.headers), t.end(e.body)), !o.startsWith("/_voltro/inspect")) {
964
967
  let t = Date.now() - r;
965
- b.emit("request", {
966
- url: a,
968
+ y.emit("request", {
969
+ url: o,
967
970
  status: e.status,
968
971
  durationMs: t,
969
972
  renderedBy: e.headers["x-voltro-rendered-by"],
970
973
  cache: e.headers["x-voltro-cache"]
971
974
  });
972
- let n = Ce(a, W)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
975
+ let n = Ce(o, G)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
973
976
  ke.record({
974
977
  kind: "request",
975
978
  tag: n,
@@ -981,71 +984,71 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
981
984
  t.writeHead(500, { "content-type": "text/plain" }), t.end(`internal error: ${e.message}`);
982
985
  }
983
986
  });
984
- await new Promise((e) => Ie.listen(x, "::", e)), Fe = !0, l.mark("ready"), K.info(`listening on http://localhost:${x}`, {
985
- port: x,
986
- distDir: D
987
- }), K.debug("dynamic + ssr/isr routes resolve via SSR loader");
987
+ await new Promise((e) => Ie.listen(S, "::", e)), Fe = !0, s.mark("ready"), q.info(`listening on http://localhost:${S}`, {
988
+ port: S,
989
+ distDir: T
990
+ }), q.debug("dynamic + ssr/isr routes resolve via SSR loader");
988
991
  try {
989
- await O({
992
+ await w({
990
993
  id: M,
991
- name: j,
994
+ name: D,
992
995
  kind: "web",
993
- url: `http://localhost:${x}`,
996
+ url: `http://localhost:${S}`,
994
997
  pid: process.pid,
995
998
  status: "live",
996
- logFile: F.path
999
+ logFile: N.path
997
1000
  });
998
- let e = P(i);
1001
+ let e = ae(o);
999
1002
  if (e) {
1000
1003
  let t = await n({ dashboardAppPath: e });
1001
- t.status === "launched" ? K.info(`dashboard launched at http://localhost:${t.port}`, { port: t.port }) : t.status === "already-running" && K.debug(`dashboard already running on :${t.port}`, { port: t.port });
1004
+ t.status === "launched" ? q.info(`dashboard launched at http://localhost:${t.port}`, { port: t.port }) : t.status === "already-running" && q.debug(`dashboard already running on :${t.port}`, { port: t.port });
1002
1005
  }
1003
1006
  setInterval(() => {
1004
- O({
1007
+ w({
1005
1008
  id: M,
1006
- name: j,
1009
+ name: D,
1007
1010
  kind: "web",
1008
- url: `http://localhost:${x}`,
1011
+ url: `http://localhost:${S}`,
1009
1012
  pid: process.pid,
1010
1013
  status: "live",
1011
- logFile: F.path
1014
+ logFile: N.path
1012
1015
  }).catch(() => {});
1013
1016
  }, 1e4).unref();
1014
1017
  } catch (e) {
1015
- K.warn("runtime-registry write failed", {}, e);
1018
+ q.warn("runtime-registry write failed", {}, e);
1016
1019
  }
1017
1020
  pe({
1018
- app: u.name ?? "unnamed",
1021
+ app: m.name ?? "unnamed",
1019
1022
  kind: "web",
1020
- url: `http://localhost:${x}`,
1021
- bootMs: l.total()
1023
+ url: `http://localhost:${S}`,
1024
+ bootMs: s.total()
1022
1025
  });
1023
- let Le = l.phaseBreakdown();
1024
- K.info(`start: ready in ${l.total()}ms`, {
1025
- bootMs: l.total(),
1026
+ let Le = s.phaseBreakdown();
1027
+ q.info(`start: ready in ${s.total()}ms`, {
1028
+ bootMs: s.total(),
1026
1029
  ...Le ? { phases: Le } : {}
1027
1030
  });
1028
- let Re = !1, $ = (t) => async () => {
1031
+ let Re = !1, ze = (e) => async () => {
1029
1032
  if (!Re) {
1030
- if (Re = !0, K.info(`received ${t}, shutting down`, { signal: t }), Ie.close(), await Oe.stop(), V) try {
1031
- await V.end();
1033
+ if (Re = !0, q.info(`received ${e}, shutting down`, { signal: e }), Ie.close(), await Oe.stop(), H) try {
1034
+ await H.end();
1032
1035
  } catch {}
1033
- await q.close(), await e(M), await F.dispose(), process.exit(0);
1036
+ await J.close(), await t(M), await N.dispose(), process.exit(0);
1034
1037
  }
1035
1038
  };
1036
- process.on("SIGINT", $("SIGINT")), process.on("SIGTERM", $("SIGTERM"));
1037
- let ze = process.ppid;
1038
- if (ze > 1) {
1039
+ process.on("SIGINT", ze("SIGINT")), process.on("SIGTERM", ze("SIGTERM"));
1040
+ let Be = process.ppid;
1041
+ if (Be > 1) {
1039
1042
  let e = setInterval(() => {
1040
- (process.ppid !== ze || process.ppid === 1) && (clearInterval(e), $("SIGTERM")());
1043
+ (process.ppid !== Be || process.ppid === 1) && (clearInterval(e), ze("SIGTERM")());
1041
1044
  }, 2e3);
1042
1045
  e.unref();
1043
1046
  }
1044
1047
  return new Promise(() => {});
1045
1048
  }, Oe = async (e) => {
1046
1049
  process.env.NODE_ENV || (process.env.NODE_ENV = "production");
1047
- let t = await De(R(e.find((e) => !e.startsWith("-")) ?? process.cwd()));
1048
- return t.ran ? t.exitCode : (K.error("no web app found at this path"), 1);
1050
+ let t = await De(L(e.find((e) => !e.startsWith("-")) ?? process.cwd()));
1051
+ return t.ran ? t.exitCode : (q.error("no web app found at this path"), 1);
1049
1052
  };
1050
1053
  //#endregion
1051
1054
  export { Oe as t };