@voltro/cli 0.7.0 → 0.9.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 (61) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/apiBuild-Ba4njm9m.js +2 -0
  4. package/dist/{apiBuild-deIjJKJm.js → apiBuild-eInHm_bJ.js} +2 -2
  5. package/dist/bin.js +2 -2
  6. package/dist/{commands-BHdc71DG.js → commands-Cxs97AFp.js} +1897 -1664
  7. package/dist/{dev-Bqyq62Ak.js → dev-63Ehpw2p.js} +475 -475
  8. package/dist/dev-CXsR8Zgo.js +2 -0
  9. package/dist/index.js +1 -1
  10. package/dist/{inspectMetrics-BLUBAWLb.js → inspectMetrics-DVCKKSPk.js} +819 -510
  11. package/dist/{serveCommand-Dn6--0Bv.js → serveCommand-Cq7BhtpB.js} +3 -3
  12. package/dist/serveEntry.js +3 -3
  13. package/dist/{start-CWZW_4Et.js → start-CYe-7ueh.js} +371 -306
  14. package/dist/startEntry.js +2 -2
  15. package/package.json +18 -17
  16. package/templates/AGENTS.core.md +15 -4
  17. package/templates/AGENTS.md +15 -4
  18. package/templates/agent-docs/authentication.md +7 -4
  19. package/templates/agent-docs/cli.md +84 -5
  20. package/templates/agent-docs/data.md +94 -70
  21. package/templates/agent-docs/database/schema.md +1 -1
  22. package/templates/agent-docs/reference.md +20 -0
  23. package/templates/agent-docs/routing.md +183 -25
  24. package/templates/agent-docs/testing.md +24 -0
  25. package/templates/apps/api-ai/package.json +7 -7
  26. package/templates/apps/api-auth/package.json +8 -8
  27. package/templates/apps/api-backend/package.json +7 -7
  28. package/templates/apps/api-backend-deactivation/package.json +7 -7
  29. package/templates/apps/api-backend-mail/package.json +8 -8
  30. package/templates/apps/api-backend-mariadb/package.json +9 -9
  31. package/templates/apps/api-backend-storage/package.json +8 -8
  32. package/templates/apps/api-data-advanced/package.json +8 -8
  33. package/templates/apps/api-durable/package.json +8 -8
  34. package/templates/apps/api-feature-flags/package.json +9 -9
  35. package/templates/apps/api-governance/package.json +8 -8
  36. package/templates/apps/api-kv/package.json +8 -8
  37. package/templates/apps/api-moderation/package.json +8 -8
  38. package/templates/apps/api-observability/package.json +8 -8
  39. package/templates/apps/api-ratelimit/package.json +8 -8
  40. package/templates/apps/api-rbac/package.json +8 -8
  41. package/templates/apps/api-rest/package.json +7 -7
  42. package/templates/apps/api-saas/package.json +11 -11
  43. package/templates/apps/api-search/package.json +8 -8
  44. package/templates/apps/api-versioning/package.json +8 -8
  45. package/templates/apps/api-webhooks/package.json +8 -8
  46. package/templates/apps/changelog/package.json +6 -6
  47. package/templates/apps/edge-functions/package.json +2 -2
  48. package/templates/apps/frontend-admin/package.json +8 -8
  49. package/templates/apps/frontend-app/package.json +8 -8
  50. package/templates/apps/frontend-blank/package.json +7 -7
  51. package/templates/apps/frontend-contact/package.json +7 -7
  52. package/templates/apps/frontend-dashboard/package.json +7 -7
  53. package/templates/apps/frontend-docs/package.json +7 -7
  54. package/templates/apps/frontend-i18n/package.json +6 -6
  55. package/templates/apps/frontend-landing/package.json +7 -7
  56. package/templates/apps/frontend-spa/package.json +7 -7
  57. package/templates/apps/frontend-ssr/package.json +7 -7
  58. package/templates/apps/frontend-ssr-api/package.json +8 -8
  59. package/templates/apps/frontend-static-blog/package.json +6 -6
  60. package/dist/apiBuild-CSM74oEZ.js +0 -2
  61. package/dist/dev-BYfrYwrT.js +0 -2
@@ -1,22 +1,22 @@
1
- import { C as e, D as t, E as n, F as r, H as i, I as a, L as o, M as s, R as c, S as l, T as ee, X as u, _ as d, a as f, b as te, ct as p, d as m, gt as h, h as g, i as _, m as v, n as y, o as b, pt as ne, r as re, st as x, t as S, u as C, w as ie, x as w, y as ae, z as oe } from "./inspectMetrics-BLUBAWLb.js";
2
- import { dirname as se, extname as T, join as E, resolve as D } from "node:path";
3
- import { fileURLToPath as O, pathToFileURL as k } from "node:url";
4
- import { createLogger as A, printReadyBlock as ce } from "@voltro/logger";
5
- import { promises as j } from "node:fs";
6
- import { snapshotMetricsSync as le } from "@voltro/runtime";
7
- import { createServer as ue } from "node:http";
1
+ import { A as e, D as t, E as n, G as r, H as i, K as a, M as o, N as s, O as c, P as l, S as u, St as d, Tt as f, U as p, W as m, Y as h, _ as g, a as _, bt as v, c as y, f as ee, g as b, h as x, ht as S, i as C, j as te, k as w, m as ne, mt as T, o as E, p as D, r as O, rt as k, s as re, t as ie, v as A, w as ae, x as oe, xt as j, z as M } from "./inspectMetrics-DVCKKSPk.js";
2
+ import { dirname as N, extname as P, join as F, resolve as I } from "node:path";
3
+ import { fileURLToPath as L, pathToFileURL as R } from "node:url";
4
+ import { createLogger as z, printReadyBlock as se } from "@voltro/logger";
5
+ import { promises as B } from "node:fs";
6
+ import { snapshotMetricsSync as ce } from "@voltro/runtime";
7
+ import { createServer as le } from "node:http";
8
8
  //#region src/routeMeta.ts
9
- var M = (e) => typeof e.renderMode == "string" ? e.renderMode : e.dynamic === "force-dynamic" ? "ssr" : (e.dynamic, "static"), de = (e) => ({
10
- renderMode: M(e),
9
+ var V = (e) => typeof e.renderMode == "string" ? e.renderMode : e.dynamic === "force-dynamic" ? "ssr" : (e.dynamic, "static"), ue = (e) => ({
10
+ renderMode: V(e),
11
11
  tenantAware: e.tenantAware === !0,
12
- revalidateMs: t(e.revalidate),
13
- staleWhileRevalidateMs: t(e.staleWhileRevalidate) ?? 0,
12
+ revalidateMs: l(e.revalidate),
13
+ staleWhileRevalidateMs: l(e.staleWhileRevalidate) ?? 0,
14
14
  cacheInvalidatesOn: Array.isArray(e.cacheInvalidatesOn) ? e.cacheInvalidatesOn : [],
15
15
  interactive: e.interactive ?? "full",
16
16
  hasLoader: typeof e.loader == "function",
17
17
  hasMeta: e.meta != null,
18
18
  hasGetStaticPaths: typeof e.getStaticPaths == "function"
19
- }), N = A({ scope: "voltro:start:cdc" }), P = (e) => (t) => {
19
+ }), H = z({ scope: "voltro:start:cdc" }), U = (e) => (t) => {
20
20
  let n = 0;
21
21
  for (let [r, i] of e.tableSubscriptionsByPattern) {
22
22
  if (!i.includes(t)) continue;
@@ -27,23 +27,23 @@ var M = (e) => typeof e.renderMode == "string" ? e.renderMode : e.dynamic === "f
27
27
  }));
28
28
  }
29
29
  return n;
30
- }, F = () => !process.env.PG_HOST && !process.env.PG_DATABASE ? null : {
30
+ }, W = () => !process.env.PG_HOST && !process.env.PG_DATABASE ? null : {
31
31
  host: process.env.PG_HOST ?? "localhost",
32
32
  port: Number(process.env.PG_PORT ?? 5432),
33
33
  user: process.env.PG_USER ?? "app",
34
34
  password: process.env.PG_PASSWORD ?? "app",
35
35
  database: process.env.PG_DATABASE ?? "app"
36
- }, fe = (e) => {
37
- let t = e.channel ?? "framework_changes", n = P(e), r = null, i = null;
36
+ }, de = (e) => {
37
+ let t = e.channel ?? "framework_changes", n = U(e), r = null, i = null;
38
38
  return {
39
39
  start: async () => {
40
- let a = F();
40
+ let a = W();
41
41
  if (!a) {
42
- N.debug("skipped — no PG_* env vars set");
42
+ H.debug("skipped — no PG_* env vars set");
43
43
  return;
44
44
  }
45
45
  if (e.tableSubscriptionsByPattern.size === 0) {
46
- N.debug("skipped — no pages declare cacheInvalidatesOn");
46
+ H.debug("skipped — no pages declare cacheInvalidatesOn");
47
47
  return;
48
48
  }
49
49
  r = new (await (import("pg"))).default.Client(a), await r.connect(), i = (e) => {
@@ -52,16 +52,16 @@ var M = (e) => typeof e.renderMode == "string" ? e.renderMode : e.dynamic === "f
52
52
  try {
53
53
  r = JSON.parse(e.payload);
54
54
  } catch (e) {
55
- N.warn("bad CDC payload", {}, e);
55
+ H.warn("bad CDC payload", {}, e);
56
56
  return;
57
57
  }
58
58
  let i = n(r.table);
59
- i > 0 && N.info(`${r.op} on '${r.table}' → dropped ${i} cache entries`, {
59
+ i > 0 && H.info(`${r.op} on '${r.table}' → dropped ${i} cache entries`, {
60
60
  op: r.op,
61
61
  table: r.table,
62
62
  dropped: i
63
63
  });
64
- }, r.on("notification", i), await r.query(`LISTEN ${t}`), N.info(`LISTEN ${t}`, {
64
+ }, r.on("notification", i), await r.query(`LISTEN ${t}`), H.info(`LISTEN ${t}`, {
65
65
  channel: t,
66
66
  host: a.host,
67
67
  port: a.port,
@@ -77,7 +77,7 @@ var M = (e) => typeof e.renderMode == "string" ? e.renderMode : e.dynamic === "f
77
77
  }
78
78
  }
79
79
  };
80
- }, I = A({ scope: "voltro:start:isr-cache" }), L = "voltro_isr_cache", R = (e) => `
80
+ }, G = z({ scope: "voltro:start:isr-cache" }), K = "voltro_isr_cache", q = (e) => `
81
81
  CREATE TABLE IF NOT EXISTS ${e} (
82
82
  cache_key TEXT PRIMARY KEY,
83
83
  html TEXT NOT NULL,
@@ -88,9 +88,9 @@ CREATE TABLE IF NOT EXISTS ${e} (
88
88
  stale_while_revalidate_ms BIGINT NOT NULL DEFAULT 0
89
89
  );
90
90
  CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
91
- `, pe = (e) => {
92
- let t = e.tableName ?? L, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
93
- i || (i = !0, await n.query(R(t)));
91
+ `, fe = (e) => {
92
+ let t = e.tableName ?? K, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
93
+ i || (i = !0, await n.query(q(t)));
94
94
  })().then(async () => {
95
95
  let e = await n.query(`SELECT * FROM ${t}`);
96
96
  for (let t of e.rows) r.set(t.cache_key, {
@@ -102,10 +102,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
102
102
  staleWhileRevalidateMs: Number(t.stale_while_revalidate_ms)
103
103
  });
104
104
  }).catch((e) => {
105
- I.warn("postgres bootstrap failed", {}, e);
105
+ G.warn("postgres bootstrap failed", {}, e);
106
106
  }), o = (e, t) => {
107
107
  a.then(() => n.query(e, t)).catch((e) => {
108
- I.warn("postgres write failed", {}, e);
108
+ G.warn("postgres write failed", {}, e);
109
109
  });
110
110
  };
111
111
  return {
@@ -150,14 +150,14 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
150
150
  return Array.from(r.entries());
151
151
  }
152
152
  };
153
- }, z = A({ scope: "voltro:start" }), me = await (async () => {
153
+ }, J = z({ scope: "voltro:start" }), pe = await (async () => {
154
154
  try {
155
- let e = E(se(O(import.meta.url)), "..", "package.json"), t = await j.readFile(e, "utf8");
155
+ let e = F(N(L(import.meta.url)), "..", "package.json"), t = await B.readFile(e, "utf8");
156
156
  return JSON.parse(t).version ?? "0.0.0";
157
157
  } catch {
158
158
  return "0.0.0";
159
159
  }
160
- })(), B = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", V = (e) => e instanceof Error && e._voltroControl === "not-found", H = {
160
+ })(), me = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", Y = (e) => e instanceof Error && e._voltroControl === "not-found", he = {
161
161
  ".html": "text/html; charset=utf-8",
162
162
  ".js": "application/javascript",
163
163
  ".mjs": "application/javascript",
@@ -175,13 +175,13 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
175
175
  ".woff": "font/woff",
176
176
  ".woff2": "font/woff2",
177
177
  ".txt": "text/plain; charset=utf-8"
178
- }, he = (e) => H[T(e).toLowerCase()] ?? "application/octet-stream", U = (e) => e.replace(/<script\b(?=[^>]*\btype="module")(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<script\b(?=[^>]*\bsrc=)(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<link\b[^>]*\brel="modulepreload"[^>]*>/gi, ""), W = async (e) => {
178
+ }, ge = (e) => he[P(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) => {
179
179
  try {
180
- return await j.stat(e), !0;
180
+ return await B.stat(e), !0;
181
181
  } catch {
182
182
  return !1;
183
183
  }
184
- }, G = (e) => {
184
+ }, Q = (e) => {
185
185
  let t = [], n = 0, r = e.split("/").filter(Boolean), i = [];
186
186
  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, "\\$&"));
187
187
  let a = i.join("/");
@@ -190,8 +190,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
190
190
  paramNames: t,
191
191
  priority: n
192
192
  };
193
- }, ge = (e) => e.map((e) => {
194
- let { regex: t, paramNames: n, priority: r } = G(e.pattern);
193
+ }, _e = (e) => e.map((e) => {
194
+ let { regex: t, paramNames: n, priority: r } = Q(e.pattern);
195
195
  return {
196
196
  pattern: e.pattern,
197
197
  file: e.file,
@@ -200,7 +200,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
200
200
  paramNames: n,
201
201
  priority: r
202
202
  };
203
- }).slice().sort((e, t) => e.priority - t.priority), K = (e, t) => {
203
+ }).slice().sort((e, t) => e.priority - t.priority), ve = (e, t) => {
204
204
  for (let n of t) {
205
205
  let t = n.regex.exec(e);
206
206
  if (!t) continue;
@@ -212,88 +212,136 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
212
212
  };
213
213
  }
214
214
  return null;
215
- }, _e = async (e) => {
216
- let { provider: n, route: r, params: i, pathname: a, dirs: o, shellTemplate: s, requestHeaders: c, requestUrl: l = "", apiOrigin: ee } = e, u = re(ee, c.cookie), d = await n.getPage(r.pattern, r.file), f = M(d);
217
- if (f !== "ssr" && f !== "isr") return null;
218
- let te = _(o, r.dirChain), p = {}, m = null, h;
215
+ }, ye = async (e) => {
216
+ let { provider: t, route: n, params: r, pathname: i, dirs: a, shellTemplate: o, requestHeaders: s, requestUrl: c = "", apiOrigin: u } = e, d = C(u, s.cookie), f = await t.getPage(n.pattern, n.file), p = V(f), m = p === "spa" && E(a, n.dirChain).some((e) => e.hasLayout);
217
+ if (p !== "ssr" && p !== "isr" && !m) return null;
218
+ let h = E(a, n.dirChain), v = {}, y = null, ee, b = new AbortController();
219
219
  try {
220
- if (h = await Promise.all(te.map(async (e, t) => {
221
- let r = {};
222
- if (e.hasLayout) {
223
- let o = await n.getLayout(e.dir);
224
- if (r.Layout = o.default, typeof o.loader == "function") {
225
- r.loader = o.loader;
226
- let e = new AbortController();
227
- p[t] = await o.loader({
228
- params: i,
229
- pathname: a,
230
- signal: e.signal,
231
- headers: c,
232
- query: u
233
- });
234
- }
220
+ ({chain: ee, segmentLoaderData: v} = await _({
221
+ entries: h,
222
+ loadLayout: (e) => t.getLayout(e.dir),
223
+ loadError: (e) => t.getError(e.dir),
224
+ loadPending: (e) => t.getPending(e.dir),
225
+ ctx: {
226
+ params: r,
227
+ pathname: i,
228
+ signal: b.signal,
229
+ headers: s,
230
+ query: d
235
231
  }
236
- return e.hasError && (r.Error = (await n.getError(e.dir)).default), e.hasPending && (r.Pending = (await n.getPending(e.dir)).default), {
237
- id: e.dir || "/",
238
- ...r
239
- };
240
- })), typeof d.loader == "function") {
241
- let e = new AbortController();
242
- m = await d.loader({
243
- params: i,
244
- pathname: a,
245
- signal: e.signal,
246
- headers: c,
247
- query: u
248
- });
249
- }
232
+ })), typeof f.loader == "function" && !m && (y = await f.loader({
233
+ params: r,
234
+ pathname: i,
235
+ signal: b.signal,
236
+ headers: s,
237
+ query: d
238
+ }));
250
239
  } catch (e) {
251
- if (B(e)) return {
240
+ if (me(e)) return {
252
241
  control: "redirect",
253
242
  status: e.status,
254
243
  location: e.location
255
244
  };
256
- if (V(e)) return { control: "not-found" };
245
+ if (Y(e)) return { control: "not-found" };
257
246
  throw e;
258
247
  }
259
- let g = await n.getHelpers(), v = {
260
- pattern: r.pattern,
261
- Component: d.default,
262
- meta: d.meta,
263
- loader: d.loader,
264
- ErrorBoundary: d.ErrorBoundary,
265
- Pending: d.Pending,
266
- renderMode: f,
267
- chain: h
268
- }, y = {
269
- cookies: g.parseCookieHeader(c.cookie),
270
- headers: c,
271
- url: l
272
- }, b = g.i18n?.resolve(c.cookie, c["accept-language"]), { html: ne, meta: x } = g.renderPageToHtml({
273
- descriptor: v,
274
- params: i,
275
- pathname: a,
276
- loaderData: m,
277
- segmentLoaderData: p,
278
- requestContext: y,
279
- ...b ? {
280
- outerWrap: b.outerWrap,
281
- locale: b.locale
248
+ let S = await t.getHelpers(), te = {
249
+ pattern: n.pattern,
250
+ Component: f.default,
251
+ meta: f.meta,
252
+ loader: f.loader,
253
+ ErrorBoundary: f.ErrorBoundary,
254
+ Pending: f.Pending,
255
+ renderMode: p,
256
+ chain: ee
257
+ }, w = {
258
+ cookies: S.parseCookieHeader(s.cookie),
259
+ headers: s,
260
+ url: c
261
+ }, T = S.i18n?.resolve(s.cookie, s["accept-language"]), D = f.interactive ?? "full";
262
+ if (m) {
263
+ let e = A({
264
+ helpers: S,
265
+ renderInput: {
266
+ descriptor: te,
267
+ params: r,
268
+ pathname: i,
269
+ segmentLoaderData: v,
270
+ requestContext: w,
271
+ ...T ? {
272
+ outerWrap: T.outerWrap,
273
+ locale: T.locale
274
+ } : {}
275
+ }
276
+ }), t = [
277
+ S.renderMetaToHtml(e.meta),
278
+ `<meta name="voltro-interactive" content="${D}" />`,
279
+ e.stateScript
280
+ ].filter((e) => e.length > 0).join("\n "), n = T?.locale ?? w.cookies["voltro:lang"] ?? "en";
281
+ return {
282
+ html: o.replace("<div id=\"root\"></div>", `<div id="root">${e.body}</div>`).replace("</head>", ` ${t}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${n}"`),
283
+ renderMode: "spa",
284
+ revalidateMs: null
285
+ };
286
+ }
287
+ let O = g({
288
+ helpers: S,
289
+ page: n.pattern,
290
+ renderMode: p,
291
+ interactive: D,
292
+ loaderData: y,
293
+ segmentLoaderData: v,
294
+ pageLoaderRan: typeof f.loader == "function"
295
+ }), k = {
296
+ descriptor: te,
297
+ params: r,
298
+ pathname: i,
299
+ loaderData: O.loaderData,
300
+ segmentLoaderData: O.segmentLoaderData,
301
+ requestContext: w,
302
+ ...T ? {
303
+ outerWrap: T.outerWrap,
304
+ locale: T.locale
282
305
  } : {}
283
- }), S = d.interactive ?? "full", C = [
284
- g.renderMetaToHtml(x),
285
- `<meta name="voltro-interactive" content="${S}" />`,
286
- `<script type="application/json" id="__voltro_state__">${g.serialiseStateForInlining(m)}<\/script>`
287
- ].filter((e) => e.length > 0).join("\n "), ie = b?.locale ?? y.cookies["voltro:lang"] ?? "en", w = s.replace("<div id=\"root\"></div>", `<div id="root">${ne}</div>`).replace("</head>", ` ${C}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${ie}"`);
288
- S === "none" && (w = U(w));
289
- let ae = f === "isr" ? t(d.revalidate) : null;
306
+ }, re = (e) => [
307
+ S.renderMetaToHtml(e),
308
+ `<meta name="voltro-interactive" content="${D}" />`,
309
+ ...O.hasDeferred ? [S.renderDeferredRegistryScript()] : [],
310
+ S.renderRouterStateScript(O.stateInput)
311
+ ].filter((e) => e.length > 0).join("\n ");
312
+ if (p === "ssr" && D === "full" && e.buffered !== !0) {
313
+ let e = T?.locale ?? w.cookies["voltro:lang"] ?? "en", t = ne({ shell: o });
314
+ return {
315
+ renderMode: "ssr",
316
+ stream: (n) => x({
317
+ res: n,
318
+ shell: t,
319
+ htmlLang: e,
320
+ headers: {
321
+ "content-type": "text/html; charset=utf-8",
322
+ "x-voltro-rendered-by": "ssr"
323
+ },
324
+ headInjection: re,
325
+ render: (e) => S.renderPageToStream({
326
+ ...k,
327
+ ...e
328
+ }),
329
+ onShellError: (e) => {
330
+ J.error("ssr shell render failed", { pathname: i }, e), n.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), n.end("server error");
331
+ }
332
+ })
333
+ };
334
+ }
335
+ let { html: ie, meta: ae } = S.renderPageToHtml(k), oe = re(ae), j = T?.locale ?? w.cookies["voltro:lang"] ?? "en", M = o.replace("<div id=\"root\"></div>", `<div id="root">${ie}</div>`).replace("</head>", ` ${oe}\n </head>`).replace(/<html(\s+lang="[^"]*")?/i, `<html lang="${j}"`);
336
+ D === "none" && (M = X(M));
337
+ let N = p === "isr" ? l(f.revalidate) : null;
290
338
  return {
291
- html: w,
292
- renderMode: f,
293
- revalidateMs: ae
339
+ html: M,
340
+ renderMode: p,
341
+ revalidateMs: N
294
342
  };
295
- }, q = (e) => e === "" ? "/" : e, ve = async (e) => {
296
- let t = await import(k(e).href);
343
+ }, $ = (e) => e === "" ? "/" : e, be = async (e) => {
344
+ let t = await import(R(e).href);
297
345
  return {
298
346
  kind: "bundle",
299
347
  getPageMeta: () => t.pageMeta,
@@ -303,18 +351,18 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
303
351
  return typeof r == "function" ? await r() : r;
304
352
  },
305
353
  async getLayout(e) {
306
- let n = t.layouts[q(e)];
307
- if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${q(e)}`);
354
+ let n = t.layouts[$(e)];
355
+ if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${$(e)}`);
308
356
  return typeof n == "function" ? await n() : n;
309
357
  },
310
358
  async getError(e) {
311
- let n = t.errors[q(e)];
312
- if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${q(e)}`);
359
+ let n = t.errors[$(e)];
360
+ if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${$(e)}`);
313
361
  return typeof n == "function" ? await n() : n;
314
362
  },
315
363
  async getPending(e) {
316
- let n = t.pendings[q(e)];
317
- if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${q(e)}`);
364
+ let n = t.pendings[$(e)];
365
+ if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${$(e)}`);
318
366
  return typeof n == "function" ? await n() : n;
319
367
  },
320
368
  async getHelpers() {
@@ -322,8 +370,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
322
370
  },
323
371
  async close() {}
324
372
  };
325
- }, ye = async (e, t) => {
326
- let n = await h("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, a = await n.createServer({
373
+ }, xe = async (e, t) => {
374
+ let n = await f("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, a = await n.createServer({
327
375
  root: t,
328
376
  envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
329
377
  plugins: [r(), i()],
@@ -335,113 +383,116 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
335
383
  },
336
384
  appType: "custom",
337
385
  logLevel: "warn"
338
- }), o = E(e, "src", "pages"), s = (e) => e === "" ? o : E(o, e);
386
+ }), o = F(e, "src", "pages"), s = (e) => e === "" ? o : F(o, e), c = v({ limit: j() }), l = (e) => c.load(e, async () => await a.ssrLoadModule(e));
339
387
  return {
340
388
  kind: "middleware",
341
389
  async getPage(e, t) {
342
- return a.ssrLoadModule(E(o, t));
390
+ return l(F(o, t));
343
391
  },
344
392
  async getLayout(e) {
345
- return a.ssrLoadModule(E(s(e), "layout.tsx"));
393
+ return l(F(s(e), "layout.tsx"));
346
394
  },
347
395
  async getError(e) {
348
- return a.ssrLoadModule(E(s(e), "error.tsx"));
396
+ return l(F(s(e), "error.tsx"));
349
397
  },
350
398
  async getPending(e) {
351
- return a.ssrLoadModule(E(s(e), "loading.tsx"));
399
+ return l(F(s(e), "loading.tsx"));
352
400
  },
353
401
  async getHelpers() {
354
- return await a.ssrLoadModule("@voltro/web/ssr");
402
+ return await l("@voltro/web/ssr");
355
403
  },
356
404
  async close() {
357
405
  await a.close();
358
406
  }
359
407
  };
360
- }, J = async (t) => {
361
- let h = await f(t);
362
- if (!h) return {
408
+ }, Se = async (l) => {
409
+ let f = await re(l);
410
+ if (!f) return {
363
411
  ran: !1,
364
412
  exitCode: 0
365
413
  };
366
- if (h.type !== "web") return z.error("only web apps are supported by `voltro start`"), {
414
+ if (f.type !== "web") return J.error("only web apps are supported by `voltro start`"), {
367
415
  ran: !0,
368
416
  exitCode: 1
369
417
  };
370
- let { resolved: _ } = await ne({
371
- rawEnv: h.env,
418
+ let { resolved: g } = await d({
419
+ rawEnv: f.env,
372
420
  appType: "web",
373
- root: t,
374
- log: z,
421
+ root: l,
422
+ log: J,
375
423
  generateTypes: !1
376
- }), re = _.publicValues, se = Date.now(), T = c();
377
- r(T), z.info("booting voltro start", { app: h.name ?? "(unnamed)" });
378
- let D = (() => {
424
+ }), _ = g.publicValues, v = Date.now(), x = r();
425
+ i(x), J.info("booting voltro start", { app: f.name ?? "(unnamed)" });
426
+ let C = (() => {
379
427
  if (process.env.VOLTRO_IS_DASHBOARD === "1" && process.env.VOLTRO_DASHBOARD_PORT) return Number(process.env.VOLTRO_DASHBOARD_PORT);
380
428
  if (process.env.PORT) {
381
429
  let e = Number(process.env.PORT);
382
430
  if (Number.isFinite(e) && e > 0) return e;
383
431
  }
384
- return h.port ?? 5173;
385
- })(), O = E(t, ".framework"), k = E(O, "dist"), A = E(k, "index.html");
386
- if (!await W(A)) return z.error("no built dist found — run `voltro build` first", { distDir: k }), {
432
+ return f.port ?? 5173;
433
+ })(), ne = F(l, ".framework"), A = F(ne, "dist"), j = F(A, "index.html");
434
+ if (!await Z(j)) return J.error("no built dist found — run `voltro build` first", { distDir: A }), {
387
435
  ran: !0,
388
436
  exitCode: 1
389
437
  };
390
- let M = h.name ?? "unnamed", N = ae("web", M, D), P = d(M);
391
- await l({
392
- id: N,
393
- name: M,
438
+ let N = f.name ?? "unnamed", P = n("web", N, C), I = ae(N);
439
+ await w({
440
+ id: P,
441
+ name: N,
394
442
  kind: "web",
395
- url: `http://localhost:${D}`,
443
+ url: `http://localhost:${C}`,
396
444
  pid: process.pid,
397
445
  status: "booting",
398
- logFile: P.path
446
+ logFile: I.path
399
447
  }).catch(() => {});
400
- let F = E(k, "_voltro", "shell.html"), I = await W(F) ? await j.readFile(F, "utf8") : await j.readFile(A, "utf8"), L = await b(t, h), R = y(L[0]), B, V = null;
401
- process.env.SSR_CACHE === "postgres" && process.env.PG_HOST ? (V = new (await (import("pg"))).default.Client({
448
+ let L = F(A, "_voltro", "shell.html"), R = await Z(L) ? await B.readFile(L, "utf8") : await B.readFile(j, "utf8"), z = await y(l, f), V = O(z[0]), H, U = null;
449
+ process.env.SSR_CACHE === "postgres" && process.env.PG_HOST ? (U = new (await (import("pg"))).default.Client({
402
450
  host: process.env.PG_HOST,
403
451
  port: Number(process.env.PG_PORT ?? 5432),
404
452
  user: process.env.PG_USER ?? "app",
405
453
  password: process.env.PG_PASSWORD ?? "app",
406
454
  database: process.env.PG_DATABASE ?? "app"
407
- }), await V.connect(), B = pe({ client: V }), z.info("isr cache backend: postgres", { table: "voltro_isr_cache" })) : (B = n(), z.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" })), await m(t, h, L, re);
408
- let { pages: H, dirs: U } = await C(E(t, "src", "pages")), G = ge(H), q = E(k, "server", "ssrEntry.js"), J = await W(q);
409
- if (!J && process.env.NODE_ENV === "production") throw z.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}`);
410
- let Y = J ? await ve(q) : await ye(t, O);
411
- z.info(`ssr loader: ${Y.kind}`, {
455
+ }), await U.connect(), H = fe({ client: U }), J.info("isr cache backend: postgres", { table: "voltro_isr_cache" })) : (H = s(), J.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" })), await D(l, f, z, _);
456
+ let { pages: W, dirs: G } = await ee(F(l, "src", "pages")), K = _e(W), q = F(A, "server", "ssrEntry.js"), me = await Z(q);
457
+ if (!me && process.env.NODE_ENV === "production") throw J.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}`);
458
+ let Y = me ? await be(q) : await xe(l, ne);
459
+ J.info(`ssr loader: ${Y.kind}`, {
412
460
  kind: Y.kind,
413
461
  ...Y.kind === "middleware" ? { hint: "no pre-built SSR bundle found; run `voltro build` to enable bundle mode" } : {}
414
462
  });
415
- let be = Y.getPageMeta?.(), X = /* @__PURE__ */ new Map(), Z = 0;
416
- for (let e of H) {
417
- let t = be?.[e.pattern];
463
+ let he = Y.getPageMeta?.(), X = /* @__PURE__ */ new Map(), Q = 0;
464
+ for (let e of W) {
465
+ let t = he?.[e.pattern];
418
466
  if (t) {
419
467
  X.set(e.pattern, t);
420
468
  continue;
421
469
  }
422
470
  try {
423
471
  let t = await Y.getPage(e.pattern, e.file);
424
- X.set(e.pattern, de(t)), Z++;
472
+ X.set(e.pattern, ue(t)), Q++;
425
473
  } catch (t) {
426
- z.warn("failed to load page metadata", { pattern: e.pattern }, t);
474
+ J.warn("failed to load page metadata", { pattern: e.pattern }, t);
427
475
  }
428
476
  }
429
- z.info(`registered ${X.size} route(s)`, {
477
+ J.info(`registered ${X.size} route(s)`, {
430
478
  count: X.size,
431
- fromManifest: X.size - Z,
432
- fromModules: Z
479
+ fromManifest: X.size - Q,
480
+ fromModules: Q
433
481
  });
434
- let xe = /* @__PURE__ */ new Map();
435
- for (let [e, t] of X) t.cacheInvalidatesOn.length > 0 && xe.set(e, t.cacheInvalidatesOn);
482
+ let $ = /* @__PURE__ */ new Set();
483
+ for (let e of K) X.get(e.pattern)?.renderMode === "spa" && E(G, e.dirChain).some((e) => e.hasLayout) && $.add(e.pattern);
484
+ $.size > 0 && J.info(`${$.size} spa route(s) render an SSR layout shell`, { patterns: [...$] });
436
485
  let Se = /* @__PURE__ */ new Map();
437
- for (let e of G) Se.set(e.pattern, e.regex);
438
- let Ce = fe({
439
- cache: B,
440
- tableSubscriptionsByPattern: xe,
441
- regexByPattern: Se
486
+ for (let [e, t] of X) t.cacheInvalidatesOn.length > 0 && Se.set(e, t.cacheInvalidatesOn);
487
+ let Ce = /* @__PURE__ */ new Map();
488
+ for (let e of K) Ce.set(e.pattern, e.regex);
489
+ let we = de({
490
+ cache: H,
491
+ tableSubscriptionsByPattern: Se,
492
+ regexByPattern: Ce
442
493
  });
443
- await Ce.start();
444
- let we = S(), Te = H.map((e) => {
494
+ await we.start();
495
+ let Te = ie(), Ee = W.map((e) => {
445
496
  let t = X.get(e.pattern);
446
497
  return {
447
498
  pattern: e.pattern,
@@ -457,20 +508,20 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
457
508
  hasMeta: t?.hasMeta ?? !1,
458
509
  hasGetStaticPaths: t?.hasGetStaticPaths ?? !1
459
510
  };
460
- }), Ee = U.map((e) => ({
511
+ }), De = G.map((e) => ({
461
512
  dir: e.dir,
462
513
  hasLayout: e.hasLayout,
463
514
  hasError: e.hasError,
464
515
  hasPending: e.hasPending,
465
516
  hasNotFound: e.hasNotFound
466
- })), De = V ? "postgres" : "memory", Oe = {
517
+ })), Oe = U ? "postgres" : "memory", ke = {
467
518
  kind: "web",
468
519
  meta: {
469
- name: h.name ?? "(unnamed)",
470
- voltroVersion: me,
471
- port: D,
520
+ name: f.name ?? "(unnamed)",
521
+ voltroVersion: pe,
522
+ port: C,
472
523
  mode: "start",
473
- apis: Object.entries(h.apis ?? {}).map(([e, t]) => {
524
+ apis: Object.entries(f.apis ?? {}).map(([e, t]) => {
474
525
  let n = {
475
526
  name: e,
476
527
  package: t.package
@@ -478,10 +529,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
478
529
  return t.url !== void 0 && (n.url = t.url), n;
479
530
  })
480
531
  },
481
- routes: Te,
482
- dirs: Ee,
532
+ routes: Ee,
533
+ dirs: De,
483
534
  cache: () => {
484
- let e = Date.now(), t = B.snapshot().map(([t, n]) => ({
535
+ let e = Date.now(), t = H.snapshot().map(([t, n]) => ({
485
536
  key: t,
486
537
  renderedAt: n.renderedAt,
487
538
  expiresAt: n.expiresAt,
@@ -492,16 +543,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
492
543
  staleWhileRevalidateMs: n.staleWhileRevalidateMs
493
544
  }));
494
545
  return {
495
- backend: De,
496
- size: B.size(),
546
+ backend: Oe,
547
+ size: H.size(),
497
548
  entries: t
498
549
  };
499
550
  },
500
- metrics: () => le()
501
- }, ke = async (n, r) => {
551
+ metrics: () => ce()
552
+ }, Ae = async (n, r) => {
502
553
  let i = n.split("?")[0] ?? "/";
503
- if (h.name === "voltroDashboard" && i === "/api/dashboard/registry") try {
504
- let e = await te();
554
+ if (f.name === "voltroDashboard" && i === "/api/dashboard/registry") try {
555
+ let e = await t();
505
556
  return {
506
557
  status: 200,
507
558
  headers: { "content-type": "application/json; charset=utf-8" },
@@ -514,7 +565,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
514
565
  body: `registry read error: ${e.message}`
515
566
  };
516
567
  }
517
- if (h.name === "voltroDashboard" && i === "/api/dashboard/config") {
568
+ if (f.name === "voltroDashboard" && i === "/api/dashboard/config") {
518
569
  let e = process.env.VOLTRO_DASHBOARD_APPS, t = [];
519
570
  if (e && e.trim().length > 0) {
520
571
  let n;
@@ -544,7 +595,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
544
595
  };
545
596
  }
546
597
  if (i === "/_voltro/inspect/logs") {
547
- let e = s(n, a());
598
+ let e = M(n, p());
548
599
  return {
549
600
  status: e.status,
550
601
  headers: { "content-type": e.contentType },
@@ -552,7 +603,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
552
603
  };
553
604
  }
554
605
  if (i !== "/_voltro/inspect/stream") {
555
- let e = u(n, Oe, r);
606
+ let e = k(n, ke, r);
556
607
  if (e !== null) return {
557
608
  status: e.status,
558
609
  headers: {
@@ -562,34 +613,34 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
562
613
  body: e.body
563
614
  };
564
615
  }
565
- let o = K(i, G), c = o ? X.get(o.route.pattern)?.renderMode : void 0, l = c === "ssr" || c === "isr", d = i === "/" ? E(k, "index.html") : E(k, i.replace(/^\//, ""), "index.html");
566
- if (!l && await W(d)) return {
616
+ let a = ve(i, K), s = a ? X.get(a.route.pattern)?.renderMode : void 0, c = s === "ssr" || s === "isr" || (a ? $.has(a.route.pattern) : !1), u = i === "/" ? F(A, "index.html") : F(A, i.replace(/^\//, ""), "index.html");
617
+ if (!c && await Z(u)) return {
567
618
  status: 200,
568
619
  headers: { "content-type": "text/html; charset=utf-8" },
569
- body: await j.readFile(d)
620
+ body: await B.readFile(u)
570
621
  };
571
622
  if (/\.[a-z0-9]+$/i.test(i)) {
572
- let e = E(k, i.replace(/^\//, ""));
573
- if (await W(e)) {
574
- let t = await j.readFile(e);
623
+ let e = F(A, i.replace(/^\//, ""));
624
+ if (await Z(e)) {
625
+ let t = await B.readFile(e);
575
626
  return {
576
627
  status: 200,
577
- headers: { "content-type": he(e) },
628
+ headers: { "content-type": ge(e) },
578
629
  body: t
579
630
  };
580
631
  }
581
632
  }
582
- let f = K(i, G);
583
- if (!f) return {
633
+ let d = ve(i, K);
634
+ if (!d) return {
584
635
  status: 404,
585
636
  headers: { "content-type": "text/plain" },
586
637
  body: "not found"
587
638
  };
588
- let p = X.get(f.route.pattern), m = r["x-tenant"] ?? "anonymous", g = (await Y.getHelpers()).i18n?.resolve(r.cookie, r["accept-language"])?.locale, _ = e(i, {
589
- ...p?.tenantAware ? { tenant: m } : {},
639
+ let m = X.get(d.route.pattern), h = r["x-tenant"] ?? "anonymous", g = (await Y.getHelpers()).i18n?.resolve(r.cookie, r["accept-language"])?.locale, _ = e(i, {
640
+ ...m?.tenantAware ? { tenant: h } : {},
590
641
  ...g ? { locale: g } : {}
591
- }), v = B.get(_), y = Date.now();
592
- if (v && ie(v, y)) return T.emit("cache", {
642
+ }), v = H.get(_), y = Date.now();
643
+ if (v && te(v, y)) return x.emit("cache", {
593
644
  kind: "HIT",
594
645
  key: _,
595
646
  ageMs: y - v.renderedAt
@@ -604,27 +655,28 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
604
655
  },
605
656
  body: v.html
606
657
  };
607
- if (v && ee(v, y)) return T.emit("cache", {
658
+ if (v && o(v, y)) return x.emit("cache", {
608
659
  kind: "STALE",
609
660
  key: _,
610
661
  ageMs: y - v.renderedAt
611
662
  }), (async () => {
612
663
  try {
613
- let e = await _e({
664
+ let e = await ye({
614
665
  provider: Y,
615
- root: t,
616
- route: f.route,
617
- params: f.params,
666
+ root: l,
667
+ route: d.route,
668
+ params: d.params,
618
669
  pathname: i,
619
- dirs: U,
620
- shellTemplate: I,
670
+ dirs: G,
671
+ shellTemplate: R,
621
672
  requestHeaders: r,
622
- apiOrigin: R,
623
- requestUrl: n
673
+ apiOrigin: V,
674
+ requestUrl: n,
675
+ buffered: !0
624
676
  });
625
- if (e && !("control" in e) && e.renderMode === "isr" && e.revalidateMs !== null) {
626
- let t = X.get(f.route.pattern)?.staleWhileRevalidateMs ?? 0, n = Date.now();
627
- B.set(_, {
677
+ if (e && "html" in e && e.renderMode === "isr" && e.revalidateMs !== null) {
678
+ let t = X.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0, n = Date.now();
679
+ H.set(_, {
628
680
  html: e.html,
629
681
  renderedAt: n,
630
682
  expiresAt: n + e.revalidateMs,
@@ -634,7 +686,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
634
686
  });
635
687
  }
636
688
  } catch (e) {
637
- z.warn("background SWR refresh failed", { url: i }, e);
689
+ J.warn("background SWR refresh failed", { url: i }, e);
638
690
  }
639
691
  })(), {
640
692
  status: 200,
@@ -648,16 +700,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
648
700
  body: v.html
649
701
  };
650
702
  try {
651
- let e = await _e({
703
+ let e = await ye({
652
704
  provider: Y,
653
- root: t,
654
- route: f.route,
655
- params: f.params,
705
+ root: l,
706
+ route: d.route,
707
+ params: d.params,
656
708
  pathname: i,
657
- dirs: U,
658
- shellTemplate: I,
709
+ dirs: G,
710
+ shellTemplate: R,
659
711
  requestHeaders: r,
660
- apiOrigin: R,
712
+ apiOrigin: V,
661
713
  requestUrl: n
662
714
  });
663
715
  if (!e) return {
@@ -666,7 +718,13 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
666
718
  "content-type": "text/html; charset=utf-8",
667
719
  "x-voltro-rendered-by": "spa-fallback"
668
720
  },
669
- body: I
721
+ body: R
722
+ };
723
+ if ("stream" in e) return {
724
+ status: 200,
725
+ headers: { "x-voltro-rendered-by": "ssr" },
726
+ body: "",
727
+ stream: e.stream
670
728
  };
671
729
  if ("control" in e) return e.control === "redirect" ? {
672
730
  status: e.status,
@@ -683,89 +741,96 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
683
741
  },
684
742
  body: "not found"
685
743
  };
686
- let a = {
744
+ let t = {
687
745
  "content-type": "text/html; charset=utf-8",
688
746
  "x-voltro-rendered-by": e.renderMode
689
747
  };
690
748
  if (e.renderMode === "isr" && e.revalidateMs !== null) {
691
- let t = Date.now(), n = X.get(f.route.pattern)?.staleWhileRevalidateMs ?? 0;
692
- B.set(_, {
749
+ let n = Date.now(), r = X.get(d.route.pattern)?.staleWhileRevalidateMs ?? 0;
750
+ H.set(_, {
693
751
  html: e.html,
694
- renderedAt: t,
695
- expiresAt: t + e.revalidateMs,
696
- servableUntil: t + e.revalidateMs + n,
752
+ renderedAt: n,
753
+ expiresAt: n + e.revalidateMs,
754
+ servableUntil: n + e.revalidateMs + r,
697
755
  revalidateMs: e.revalidateMs,
698
- staleWhileRevalidateMs: n
699
- }), a["x-voltro-cache"] = "MISS", a["x-voltro-cache-key"] = _, a["x-voltro-cache-revalidate-ms"] = String(e.revalidateMs), n > 0 && (a["x-voltro-cache-swr-ms"] = String(n)), T.emit("cache", {
756
+ staleWhileRevalidateMs: r
757
+ }), t["x-voltro-cache"] = "MISS", t["x-voltro-cache-key"] = _, t["x-voltro-cache-revalidate-ms"] = String(e.revalidateMs), r > 0 && (t["x-voltro-cache-swr-ms"] = String(r)), x.emit("cache", {
700
758
  kind: "MISS",
701
759
  key: _,
702
760
  revalidateMs: e.revalidateMs,
703
- swrMs: n
761
+ swrMs: r
704
762
  });
705
- } else e.renderMode === "isr" && (a["x-voltro-cache"] = "BYPASS", T.emit("cache", {
763
+ } else e.renderMode === "isr" && (t["x-voltro-cache"] = "BYPASS", x.emit("cache", {
706
764
  kind: "BYPASS",
707
765
  key: _
708
766
  }));
709
767
  return {
710
768
  status: 200,
711
- headers: a,
769
+ headers: t,
712
770
  body: e.html
713
771
  };
714
772
  } catch (e) {
715
- return z.error("render error", { url: i }, e), {
773
+ return J.error("render error", { url: i }, e), b(e) ? {
774
+ status: 500,
775
+ headers: {
776
+ "content-type": "text/plain; charset=utf-8",
777
+ "x-voltro-rendered-by": "defer-unsupported"
778
+ },
779
+ body: e.message
780
+ } : {
716
781
  status: 500,
717
782
  headers: { "content-type": "text/plain" },
718
783
  body: "server error"
719
784
  };
720
785
  }
721
- }, Q = !1, Ae = ue(async (e, t) => {
722
- let n = e.url ?? "/", r = Date.now(), a = {};
723
- for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (a[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
724
- let s = n.split("?")[0] ?? "/", c = (e.method ?? "GET").toUpperCase();
725
- if (s === "/internal/liveness") {
786
+ }, je = !1, Me = le(async (e, t) => {
787
+ let n = e.url ?? "/", r = Date.now(), i = {};
788
+ for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (i[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
789
+ let o = n.split("?")[0] ?? "/", s = (e.method ?? "GET").toUpperCase();
790
+ if (o === "/internal/liveness") {
726
791
  t.writeHead(200, {
727
792
  "content-type": "text/plain",
728
793
  "cache-control": "no-store"
729
794
  }), t.end("ok");
730
795
  return;
731
796
  }
732
- if (s === "/internal/readiness") {
733
- let e = Q ? 200 : 503;
797
+ if (o === "/internal/readiness") {
798
+ let e = je ? 200 : 503;
734
799
  t.writeHead(e, {
735
800
  "content-type": "text/plain",
736
801
  "cache-control": "no-store"
737
- }), t.end(Q ? "ready" : "not-ready");
802
+ }), t.end(je ? "ready" : "not-ready");
738
803
  return;
739
804
  }
740
- if (c === "OPTIONS" && s.startsWith("/_voltro/inspect")) {
741
- let e = x(a.origin);
805
+ if (s === "OPTIONS" && o.startsWith("/_voltro/inspect")) {
806
+ let e = T(i.origin);
742
807
  t.writeHead(204, e), t.end();
743
808
  return;
744
809
  }
745
- if (h.name === "voltroDashboard" && s === "/api/dashboard/proxy") {
810
+ if (f.name === "voltroDashboard" && o === "/api/dashboard/proxy") {
746
811
  let r = new URL(n, "http://x").searchParams.get("target");
747
812
  if (!r) {
748
813
  t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
749
814
  return;
750
815
  }
751
816
  try {
752
- let n = { accept: a.accept ?? "application/json" }, i = a.authorization;
753
- i && (n.authorization = Array.isArray(i) ? i[0] : i);
817
+ let n = { accept: i.accept ?? "application/json" }, a = i.authorization;
818
+ a && (n.authorization = Array.isArray(a) ? a[0] : a);
754
819
  let o;
755
- if (c && c !== "GET" && c !== "HEAD") {
820
+ if (s && s !== "GET" && s !== "HEAD") {
756
821
  let t = [];
757
822
  for await (let n of e) t.push(n);
758
823
  o = Buffer.concat(t);
759
- let r = a["content-type"];
824
+ let r = i["content-type"];
760
825
  r && (n["content-type"] = Array.isArray(r) ? r[0] : r);
761
826
  }
762
- let s = await fetch(r, {
763
- method: c,
827
+ let c = await fetch(r, {
828
+ method: s,
764
829
  headers: n,
765
830
  ...o ? { body: new Uint8Array(o.buffer, o.byteOffset, o.byteLength) } : {}
766
- }), l = Buffer.from(await s.arrayBuffer());
767
- t.writeHead(s.status, {
768
- "content-type": s.headers.get("content-type") ?? "application/octet-stream",
831
+ }), l = Buffer.from(await c.arrayBuffer());
832
+ t.writeHead(c.status, {
833
+ "content-type": c.headers.get("content-type") ?? "application/octet-stream",
769
834
  "content-length": String(l.length)
770
835
  }), t.end(l);
771
836
  } catch (e) {
@@ -773,22 +838,22 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
773
838
  }
774
839
  return;
775
840
  }
776
- if (h.name === "voltroDashboard" && s === "/api/dashboard/proxy/stream") {
841
+ if (f.name === "voltroDashboard" && o === "/api/dashboard/proxy/stream") {
777
842
  let r = new URL(n, "http://x").searchParams.get("target");
778
843
  if (!r) {
779
844
  t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
780
845
  return;
781
846
  }
782
- let i = new AbortController();
783
- e.on("close", () => i.abort()), t.on("close", () => i.abort());
847
+ let a = new AbortController();
848
+ e.on("close", () => a.abort()), t.on("close", () => a.abort());
784
849
  try {
785
- let e = a.authorization, n = await fetch(r, {
850
+ let e = i.authorization, n = await fetch(r, {
786
851
  method: "GET",
787
852
  headers: {
788
853
  accept: "text/event-stream",
789
854
  ...e ? { authorization: Array.isArray(e) ? e[0] : e } : {}
790
855
  },
791
- signal: i.signal
856
+ signal: a.signal
792
857
  });
793
858
  if (!n.ok || !n.body) {
794
859
  t.writeHead(n.status, { "content-type": "text/plain" }), t.end(`upstream ${n.status}`);
@@ -815,16 +880,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
815
880
  }
816
881
  return;
817
882
  }
818
- if (s === "/_voltro/inspect/stream") {
819
- let n = x(a.origin);
820
- if (p()) {
883
+ if (o === "/_voltro/inspect/stream") {
884
+ let n = T(i.origin);
885
+ if (S()) {
821
886
  t.writeHead(503, {
822
887
  "content-type": "application/json",
823
888
  ...n
824
889
  }), t.end(JSON.stringify({ error: "inspect disabled (VOLTRO_INSPECT=off)" }));
825
890
  return;
826
891
  }
827
- let r = i(a);
892
+ let r = h(i);
828
893
  if (!r.ok) {
829
894
  t.writeHead(401, {
830
895
  "content-type": "application/json",
@@ -842,30 +907,30 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
842
907
  "x-accel-buffering": "no",
843
908
  ...n
844
909
  }), t.flushHeaders(), t.write("retry: 5000\n\n");
845
- for (let e of T.buffered()) t.write(o(e));
846
- let s = T.subscribe((e) => {
847
- t.write(o(e));
848
- }), c = setInterval(() => {
849
- t.write(oe());
850
- }, 15e3), l = () => {
851
- clearInterval(c), s();
910
+ for (let e of x.buffered()) t.write(m(e));
911
+ let o = x.subscribe((e) => {
912
+ t.write(m(e));
913
+ }), s = setInterval(() => {
914
+ t.write(a());
915
+ }, 15e3), c = () => {
916
+ clearInterval(s), o();
852
917
  };
853
- e.on("close", l), t.on("close", l);
918
+ e.on("close", c), t.on("close", c);
854
919
  return;
855
920
  }
856
921
  try {
857
- let e = await ke(n, a);
858
- if (t.writeHead(e.status, e.headers), t.end(e.body), !s.startsWith("/_voltro/inspect")) {
922
+ let e = await Ae(n, i);
923
+ if (e.stream ? e.stream(t) : (t.writeHead(e.status, e.headers), t.end(e.body)), !o.startsWith("/_voltro/inspect")) {
859
924
  let t = Date.now() - r;
860
- T.emit("request", {
861
- url: s,
925
+ x.emit("request", {
926
+ url: o,
862
927
  status: e.status,
863
928
  durationMs: t,
864
929
  renderedBy: e.headers["x-voltro-rendered-by"],
865
930
  cache: e.headers["x-voltro-cache"]
866
931
  });
867
- let n = K(s, G)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
868
- we.record({
932
+ let n = ve(o, K)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
933
+ Te.record({
869
934
  kind: "request",
870
935
  tag: n,
871
936
  durationMs: t,
@@ -876,66 +941,66 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
876
941
  t.writeHead(500, { "content-type": "text/plain" }), t.end(`internal error: ${e.message}`);
877
942
  }
878
943
  });
879
- await new Promise((e) => Ae.listen(D, "::", e)), Q = !0, z.info(`listening on http://localhost:${D}`, {
880
- port: D,
881
- distDir: k
882
- }), z.debug("dynamic + ssr/isr routes resolve via SSR loader");
944
+ await new Promise((e) => Me.listen(C, "::", e)), je = !0, J.info(`listening on http://localhost:${C}`, {
945
+ port: C,
946
+ distDir: A
947
+ }), J.debug("dynamic + ssr/isr routes resolve via SSR loader");
883
948
  try {
884
- await l({
885
- id: N,
886
- name: M,
949
+ await w({
950
+ id: P,
951
+ name: N,
887
952
  kind: "web",
888
- url: `http://localhost:${D}`,
953
+ url: `http://localhost:${C}`,
889
954
  pid: process.pid,
890
955
  status: "live",
891
- logFile: P.path
956
+ logFile: I.path
892
957
  });
893
- let e = g(t);
958
+ let e = u(l);
894
959
  if (e) {
895
- let t = await v({ dashboardAppPath: e });
896
- t.status === "launched" ? z.info(`dashboard launched at http://localhost:${t.port}`, { port: t.port }) : t.status === "already-running" && z.debug(`dashboard already running on :${t.port}`, { port: t.port });
960
+ let t = await oe({ dashboardAppPath: e });
961
+ t.status === "launched" ? J.info(`dashboard launched at http://localhost:${t.port}`, { port: t.port }) : t.status === "already-running" && J.debug(`dashboard already running on :${t.port}`, { port: t.port });
897
962
  }
898
963
  setInterval(() => {
899
- l({
900
- id: N,
901
- name: M,
964
+ w({
965
+ id: P,
966
+ name: N,
902
967
  kind: "web",
903
- url: `http://localhost:${D}`,
968
+ url: `http://localhost:${C}`,
904
969
  pid: process.pid,
905
970
  status: "live",
906
- logFile: P.path
971
+ logFile: I.path
907
972
  }).catch(() => {});
908
973
  }, 1e4).unref();
909
974
  } catch (e) {
910
- z.warn("runtime-registry write failed", {}, e);
975
+ J.warn("runtime-registry write failed", {}, e);
911
976
  }
912
- ce({
913
- app: h.name ?? "unnamed",
977
+ se({
978
+ app: f.name ?? "unnamed",
914
979
  kind: "web",
915
- url: `http://localhost:${D}`,
916
- bootMs: Date.now() - se
980
+ url: `http://localhost:${C}`,
981
+ bootMs: Date.now() - v
917
982
  });
918
- let je = !1, $ = (e) => async () => {
919
- if (!je) {
920
- if (je = !0, z.info(`received ${e}, shutting down`, { signal: e }), Ae.close(), await Ce.stop(), V) try {
921
- await V.end();
983
+ let Ne = !1, Pe = (e) => async () => {
984
+ if (!Ne) {
985
+ if (Ne = !0, J.info(`received ${e}, shutting down`, { signal: e }), Me.close(), await we.stop(), U) try {
986
+ await U.end();
922
987
  } catch {}
923
- await Y.close(), await w(N), await P.dispose(), process.exit(0);
988
+ await Y.close(), await c(P), await I.dispose(), process.exit(0);
924
989
  }
925
990
  };
926
- process.on("SIGINT", $("SIGINT")), process.on("SIGTERM", $("SIGTERM"));
927
- let Me = process.ppid;
928
- if (Me > 1) {
991
+ process.on("SIGINT", Pe("SIGINT")), process.on("SIGTERM", Pe("SIGTERM"));
992
+ let Fe = process.ppid;
993
+ if (Fe > 1) {
929
994
  let e = setInterval(() => {
930
- (process.ppid !== Me || process.ppid === 1) && (clearInterval(e), $("SIGTERM")());
995
+ (process.ppid !== Fe || process.ppid === 1) && (clearInterval(e), Pe("SIGTERM")());
931
996
  }, 2e3);
932
997
  e.unref();
933
998
  }
934
999
  return new Promise(() => {});
935
- }, Y = async (e) => {
1000
+ }, Ce = async (e) => {
936
1001
  process.env.NODE_ENV || (process.env.NODE_ENV = "production");
937
- let t = await J(D(e.find((e) => !e.startsWith("-")) ?? process.cwd()));
938
- return t.ran ? t.exitCode : (z.error("no web app found at this path"), 1);
1002
+ let t = await Se(I(e.find((e) => !e.startsWith("-")) ?? process.cwd()));
1003
+ return t.ran ? t.exitCode : (J.error("no web app found at this path"), 1);
939
1004
  };
940
1005
  //#endregion
941
- export { de as n, Y as t };
1006
+ export { ue as n, Ce as t };