@voltro/cli 0.59.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/dist/apiBuild-5U7PxjLa.js +2 -0
- package/dist/{apiBuild-BU5cd-Fn.js → apiBuild-C5HhMAEm.js} +2 -2
- package/dist/bin.js +1 -1
- package/dist/{build-uRMZrT8z.js → build-DQr7SsKK.js} +21 -17
- package/dist/{checkCommand-C1CQc8lD.js → checkCommand-ByaOhdc8.js} +1 -1
- package/dist/{checkCommand-DC7k1_hP.js → checkCommand-CvSheSdb.js} +3 -3
- package/dist/{codegenCommand-kS3Twuc9.js → codegenCommand-CI0mpjxo.js} +1 -1
- package/dist/{codemodRunner-kNnh1I7_.js → codemodRunner-Drb-H-jr.js} +258 -159
- package/dist/{commands-cu34ENUT.js → commands-pCItjdHG.js} +21 -21
- package/dist/{dashboardCommand-gG1y4PFG.js → dashboardCommand-O-ToEgiH.js} +3 -3
- package/dist/{dataCommand-hNOGKz4K.js → dataCommand-mn1Zc88W.js} +1 -1
- package/dist/{dev-Ce2gEziv.js → dev-dSH-Pkb2.js} +1 -1
- package/dist/{dev-Dff_pdik.js → dev-kn9-7RXZ.js} +1 -1
- package/dist/doctorCommand-BVat2ejT.js +2 -0
- package/dist/{doctorCommand-CGjefPmk.js → doctorCommand-ClB7Ucz5.js} +13 -13
- package/dist/{dormancyCommand-DYgI0Kf-.js → dormancyCommand-DZupfuq-.js} +1 -1
- package/dist/{embeddingsCommand-yJivQSrI.js → embeddingsCommand-D8_GquVX.js} +1 -1
- package/dist/{envCommand-ORJUf8r_.js → envCommand-DRjVSXRp.js} +2 -2
- package/dist/{evolveCommand-8CuFBlbQ.js → evolveCommand-Cb0C2b3V.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-D02A4WI2.js → infoCommand-BlKnoY2D.js} +1 -1
- package/dist/{probeCommand-BUYzc0CT.js → probeCommand-DYsOFm62.js} +1 -1
- package/dist/{runtimeTrace-DDuKJgVi.js → runtimeTrace-BI3kdicc.js} +1 -1
- package/dist/{sdkgen-vq1fF9D_.js → sdkgen-DZ4zCtV4.js} +1 -1
- package/dist/serveCommand-CwK_6Wxt.js +2 -0
- package/dist/{serveCommand-Dr4KXmTS.js → serveCommand-DnxqCQ16.js} +3 -3
- package/dist/serveEntry.js +1 -1
- package/dist/{start-C75w6ftA.js → start-DE4ixCTi.js} +1 -1
- package/dist/{start-DNtIlBcn.js → start-bqT4GRH0.js} +286 -226
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-Bw3mCUDT.js → updateCommand-B18ZQNFg.js} +1 -1
- package/dist/updateCommand-B4EgDD-m.js +2 -0
- package/dist/{webDev-CBN9KkyQ.js → webDev-CYjhmOTZ.js} +1 -1
- package/dist/{webDev-Bzb8ZFus.js → webDev-CuG3Ged-.js} +1 -1
- package/dist/{webhooksCommand-BPJQOGTe.js → webhooksCommand-Bs-HZ4wY.js} +1 -1
- package/package.json +37 -19
- package/templates/AGENTS.md +2 -2
- package/templates/agent-docs/_index.md +2 -2
- package/templates/agent-docs/_manifest.json +1 -1
- package/templates/agent-docs/data.md +10 -2
- package/templates/agent-docs/observability.md +7 -1
- package/templates/agent-docs/plugins/sentry.md +121 -17
- package/templates/agent-docs/schema-driven-ui.md +38 -10
- package/templates/agent-docs/whats-new.md +11 -41
- package/templates/apps/api-ai/package.json +6 -6
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -9
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-row-history/package.json +8 -8
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +7 -7
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +7 -7
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-auth/package.json +7 -7
- package/templates/apps/frontend-blank/package.json +6 -6
- package/templates/apps/frontend-cms/package.json +8 -8
- package/templates/apps/frontend-collab/package.json +9 -9
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +6 -6
- package/templates/apps/frontend-docs/package.json +8 -8
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +7 -7
- package/templates/apps/frontend-saas/package.json +7 -7
- package/templates/apps/frontend-spa/package.json +6 -6
- package/templates/apps/frontend-ssr/package.json +6 -6
- package/templates/apps/frontend-ssr-api/package.json +7 -7
- package/templates/apps/frontend-static-blog/package.json +8 -8
- package/templates/apps/frontend-status/package.json +7 -7
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-B9OvhITV.js +0 -2
- package/dist/doctorCommand-DXnNpKQT.js +0 -2
- package/dist/serveCommand-1R52mo6F.js +0 -2
- package/dist/updateCommand-inInBIAh.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as e, t } from "./nodeEnvironment-cGFAj1J8.js";
|
|
2
|
-
import { $ as n, $t as r, A as i, B as a, D as o, E as s, Et as c, F as l, Ft as u, G as d, H as ee, I as f, It as p, J as m, K as te, L as h, Lt as g, M as _, N as v, O as y, P as b, Pt as x, Q as
|
|
2
|
+
import { $ as n, $t as r, A as i, B as a, D as o, E as s, Et as c, F as l, Ft as u, G as d, H as ee, I as f, It as p, J as m, K as te, L as h, Lt as g, M as _, N as v, O as y, P as b, Pt as x, Q as S, Qt as C, R as ne, Rt as w, T as re, U as ie, W as ae, X as T, Y as E, Yt as oe, Z as se, a as ce, bt as le, c as D, en as ue, et as de, ft as fe, h as O, it as pe, j as me, jt as k, k as he, lt as A, m as j, mt as ge, n as _e, nt as ve, ot as ye, q as M, rt as be, s as xe, st as Se, tn as Ce, tt as N, u as we, xt as Te, z as P } from "./webDev-CYjhmOTZ.js";
|
|
3
3
|
import { A as F, I as Ee, M as De, O as Oe, g as ke, j as Ae, o as je } from "./inspect-PcC5J-Dl.js";
|
|
4
4
|
import { r as Me } from "./appPort-B_HpJ_ck.js";
|
|
5
5
|
import { o as Ne, t as Pe } from "./connectionConfig-Bk9IC7D0.js";
|
|
@@ -16,13 +16,14 @@ import { createLogger as H, printReadyBlock as et } from "@voltro/logger";
|
|
|
16
16
|
import { appMode as tt, frameworkVersion as nt, setProcessBootPath as rt } from "@voltro/protocol/identity";
|
|
17
17
|
import { applyKeepAlive as it, maybeCompress as U, negotiateEncoding as at, resolveKeepAlive as ot, snapshotMetricsSync as st } from "@voltro/runtime";
|
|
18
18
|
import { Effect as ct } from "effect";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
19
|
+
import { createRequire as W } from "node:module";
|
|
20
|
+
import { createHash as lt } from "node:crypto";
|
|
21
|
+
import { resolveBroadcastProvider as ut } from "@voltro/plugin-broadcast";
|
|
22
|
+
import { createServer as dt } from "node:http";
|
|
22
23
|
//#region src/inertPublicEnv.ts
|
|
23
|
-
var
|
|
24
|
+
var ft = "VOLTRO_PUBLIC_", pt = (e, t) => {
|
|
24
25
|
let n = [];
|
|
25
|
-
for (let [r, i] of Object.entries(e)) if (r.startsWith(
|
|
26
|
+
for (let [r, i] of Object.entries(e)) if (r.startsWith(ft) && i !== void 0 && i !== "") {
|
|
26
27
|
if (t === void 0 || !(r in t)) {
|
|
27
28
|
n.push({
|
|
28
29
|
key: r,
|
|
@@ -36,15 +37,15 @@ var dt = "VOLTRO_PUBLIC_", ft = (e, t) => {
|
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
return n.sort((e, t) => e.key.localeCompare(t.key));
|
|
39
|
-
},
|
|
40
|
+
}, mt = (e) => {
|
|
40
41
|
let t = e.map((e) => e.baked === "absent" ? ` • ${e.key} — not in the bundle at all` : ` • ${e.key} — the bundle carries a DIFFERENT value; the built one wins`);
|
|
41
42
|
return `${e.length} VOLTRO_PUBLIC_* variable${e.length === 1 ? "" : "s"} set in this process's environment cannot reach the browser:\n${t.join("\n")}\n Public values are BAKED into the bundle by \`voltro build\` — that is what makes them
|
|
42
43
|
readable in a browser — so they must exist when the bundle is BUILT, not when it runs.
|
|
43
44
|
→ pass them to the build (a Docker build-arg / CI build step), not as deployment env.`;
|
|
44
|
-
},
|
|
45
|
+
}, ht = (e) => `W/"${lt("md5").update(e).digest("hex")}"`, gt = (e) => {
|
|
45
46
|
let { response: t, status: n, headers: r, body: i, requestHeaders: a } = e, o = r["cache-control"], s = (typeof o == "string" ? o : "").toLowerCase();
|
|
46
47
|
if (i === void 0 || n !== 200 || s.includes("no-store")) return !1;
|
|
47
|
-
let c = r["content-type"], l = typeof c == "string" ? c : void 0, u = r.etag, d = typeof u == "string" ? u :
|
|
48
|
+
let c = r["content-type"], l = typeof c == "string" ? c : void 0, u = r.etag, d = typeof u == "string" ? u : ht(i), ee = a["if-none-match"];
|
|
48
49
|
if (ee !== void 0 && ee.split(",").some((e) => e.trim() === d)) return t.writeHead(304, {
|
|
49
50
|
...r,
|
|
50
51
|
etag: d
|
|
@@ -64,7 +65,7 @@ var dt = "VOLTRO_PUBLIC_", ft = (e, t) => {
|
|
|
64
65
|
etag: d
|
|
65
66
|
};
|
|
66
67
|
return f.compressed && delete p["content-length"], t.writeHead(n, p), t.end(Buffer.from(f.bytes)), !0;
|
|
67
|
-
}, G = H({ scope: "voltro:start:cdc" }),
|
|
68
|
+
}, G = H({ scope: "voltro:start:cdc" }), _t = (e) => (t) => {
|
|
68
69
|
let n = 0;
|
|
69
70
|
for (let [r, i] of e.tableSubscriptionsByPattern) {
|
|
70
71
|
if (!i.includes(t)) continue;
|
|
@@ -75,13 +76,13 @@ var dt = "VOLTRO_PUBLIC_", ft = (e, t) => {
|
|
|
75
76
|
}));
|
|
76
77
|
}
|
|
77
78
|
return n;
|
|
78
|
-
},
|
|
79
|
-
let t = e.channel ?? "framework_changes", n = e.revalidateChannel ?? "voltro_revalidate", r =
|
|
79
|
+
}, vt = () => Pe() ? Ne() : null, yt = (e) => {
|
|
80
|
+
let t = e.channel ?? "framework_changes", n = e.revalidateChannel ?? "voltro_revalidate", r = _t(e), i = null, a = null;
|
|
80
81
|
return {
|
|
81
82
|
start: async () => {
|
|
82
83
|
let o = e.tableSubscriptionsByPattern.size > 0, s = e.onRevalidate !== void 0;
|
|
83
84
|
if (!o && !s) return G.debug("skipped — no pages declare cacheInvalidatesOn and no revalidation handler is wired"), !1;
|
|
84
|
-
let c =
|
|
85
|
+
let c = vt();
|
|
85
86
|
if (!c && !o) return G.debug("no database configured — the revalidation LISTEN line is unavailable"), !1;
|
|
86
87
|
if (!c) return G.warn(`${e.tableSubscriptionsByPattern.size} route(s) declare \`cacheInvalidatesOn\` and will NOT be invalidated live — no database is configured for this web process. Set DB_URL (or DB_HOST / PG_HOST) so the CDC listener can connect; until then those routes go stale until their \`revalidate\` window expires.`, { routes: [...e.tableSubscriptionsByPattern.keys()] }), !1;
|
|
87
88
|
i = new (await (import("pg"))).default.Client(c), await i.connect(), a = (i) => {
|
|
@@ -123,7 +124,7 @@ var dt = "VOLTRO_PUBLIC_", ft = (e, t) => {
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
};
|
|
126
|
-
},
|
|
127
|
+
}, K = H({ scope: "voltro:start:isr-cache" }), bt = "voltro_isr_cache", xt = (e) => `
|
|
127
128
|
CREATE TABLE IF NOT EXISTS ${e} (
|
|
128
129
|
cache_key TEXT PRIMARY KEY,
|
|
129
130
|
html TEXT NOT NULL,
|
|
@@ -134,9 +135,9 @@ CREATE TABLE IF NOT EXISTS ${e} (
|
|
|
134
135
|
stale_while_revalidate_ms BIGINT NOT NULL DEFAULT 0
|
|
135
136
|
);
|
|
136
137
|
CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
137
|
-
`,
|
|
138
|
-
let t = e.tableName ??
|
|
139
|
-
i || (i = !0, await n.query(
|
|
138
|
+
`, St = (e) => {
|
|
139
|
+
let t = e.tableName ?? bt, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
|
|
140
|
+
i || (i = !0, await n.query(xt(t)));
|
|
140
141
|
})().then(async () => {
|
|
141
142
|
let e = await n.query(`SELECT * FROM ${t}`);
|
|
142
143
|
for (let t of e.rows) r.set(t.cache_key, {
|
|
@@ -148,10 +149,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
148
149
|
staleWhileRevalidateMs: Number(t.stale_while_revalidate_ms)
|
|
149
150
|
});
|
|
150
151
|
}).catch((e) => {
|
|
151
|
-
|
|
152
|
+
K.warn("postgres bootstrap failed", {}, e);
|
|
152
153
|
}), o = (e, t) => {
|
|
153
154
|
a.then(() => n.query(e, t)).catch((e) => {
|
|
154
|
-
|
|
155
|
+
K.warn("postgres write failed", {}, e);
|
|
155
156
|
});
|
|
156
157
|
}, s = /* @__PURE__ */ new Map(), c = (e) => {
|
|
157
158
|
s.set(e, (s.get(e) ?? 0) + 1);
|
|
@@ -202,21 +203,21 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
202
203
|
return Array.from(r.entries());
|
|
203
204
|
}
|
|
204
205
|
};
|
|
205
|
-
},
|
|
206
|
+
}, q = H({ scope: "voltro:start:revalidate" }), Ct = (e) => {
|
|
206
207
|
try {
|
|
207
208
|
let t = JSON.parse(e);
|
|
208
209
|
return (t.kind === "path" || t.kind === "table" || t.kind === "tag") && typeof t.value == "string" && t.value.length > 0 && (t.tenant === void 0 || typeof t.tenant == "string") ? {
|
|
209
210
|
kind: t.kind,
|
|
210
211
|
value: t.value,
|
|
211
212
|
...t.tenant === void 0 ? {} : { tenant: t.tenant }
|
|
212
|
-
} : (
|
|
213
|
+
} : (q.warn("bad revalidation payload (shape)", { payload: e }), null);
|
|
213
214
|
} catch {
|
|
214
|
-
return
|
|
215
|
+
return q.warn("bad revalidation payload (not JSON)", { payload: e }), null;
|
|
215
216
|
}
|
|
216
|
-
},
|
|
217
|
+
}, wt = (e, t) => {
|
|
217
218
|
if (t.kind === "table" || t.kind === "tag") {
|
|
218
219
|
let n = e.tableSink(t.value);
|
|
219
|
-
return
|
|
220
|
+
return q.info(`revalidate ${t.kind} '${t.value}' → dropped ${n} cache entries`, {
|
|
220
221
|
kind: t.kind,
|
|
221
222
|
value: t.value,
|
|
222
223
|
dropped: n
|
|
@@ -225,7 +226,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
225
226
|
let n = t.value, r = (e) => t.tenant === void 0 || e.split("|").includes(`tenant=${t.tenant}`), i = e.regexByPattern.get(n);
|
|
226
227
|
if (i !== void 0) {
|
|
227
228
|
let a = e.renderModeByPattern.get(n);
|
|
228
|
-
if (a !== "isr") return
|
|
229
|
+
if (a !== "isr") return q.error(`revalidatePath('${n}') targets a '${a ?? "unknown"}' route — only \`renderMode: 'isr'\` routes have cache entries to drop. Static HTML is a build artifact this process never re-renders; rebuild to change it.`, {
|
|
229
230
|
path: n,
|
|
230
231
|
renderMode: a ?? "unknown"
|
|
231
232
|
}), 0;
|
|
@@ -233,27 +234,67 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
233
234
|
let t = e.split("|")[0] ?? e;
|
|
234
235
|
return i.test(t) && r(e);
|
|
235
236
|
});
|
|
236
|
-
return
|
|
237
|
+
return q.info(`revalidate path pattern '${n}' → dropped ${o} cache entries`, {
|
|
237
238
|
path: n,
|
|
238
239
|
dropped: o,
|
|
239
240
|
...t.tenant === void 0 ? {} : { tenant: t.tenant }
|
|
240
241
|
}), o;
|
|
241
242
|
}
|
|
242
243
|
let a = [...e.regexByPattern.entries()].find(([, e]) => e.test(n));
|
|
243
|
-
if (a === void 0) return
|
|
244
|
+
if (a === void 0) return q.error(`revalidatePath('${n}') matches no declared route — nothing was dropped. Pass a declared route pattern (e.g. /blog/[slug]) or a concrete path of one.`, { path: n }), 0;
|
|
244
245
|
let o = e.renderModeByPattern.get(a[0]);
|
|
245
|
-
if (o !== "isr") return
|
|
246
|
+
if (o !== "isr") return q.error(`revalidatePath('${n}') targets a '${o ?? "unknown"}' route (${a[0]}) — only \`renderMode: 'isr'\` routes have cache entries to drop.`, {
|
|
246
247
|
path: n,
|
|
247
248
|
route: a[0],
|
|
248
249
|
renderMode: o ?? "unknown"
|
|
249
250
|
}), 0;
|
|
250
251
|
let s = e.cache.invalidateBy((e) => (e.split("|")[0] ?? e) === n && r(e));
|
|
251
|
-
return
|
|
252
|
+
return q.info(`revalidate path '${n}' → dropped ${s} cache entries`, {
|
|
252
253
|
path: n,
|
|
253
254
|
dropped: s,
|
|
254
255
|
...t.tenant === void 0 ? {} : { tenant: t.tenant }
|
|
255
256
|
}), s;
|
|
256
|
-
},
|
|
257
|
+
}, Tt = async (e) => {
|
|
258
|
+
let t = W(R(e, "package.json")), n;
|
|
259
|
+
try {
|
|
260
|
+
n = t.resolve("@voltro/plugin-sentry/server");
|
|
261
|
+
} catch {
|
|
262
|
+
return { reason: "SENTRY_DSN is set but `@voltro/plugin-sentry` is not installed in this app — the web server cannot report SSR errors. → `pnpm add @voltro/plugin-sentry` in the web app." };
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
return await import(V(n).href);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
return { reason: `@voltro/plugin-sentry/server resolved but could not be loaded: ${e instanceof Error ? e.message : String(e)}` };
|
|
268
|
+
}
|
|
269
|
+
}, Et = async (e, t, n, r = {}) => {
|
|
270
|
+
let i = t.SENTRY_DSN;
|
|
271
|
+
if (i === void 0 || i === "") return;
|
|
272
|
+
let a = await (r.load ?? Tt)(e);
|
|
273
|
+
if ("reason" in a) {
|
|
274
|
+
n.warn(a.reason);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
let o = {
|
|
278
|
+
dsn: i,
|
|
279
|
+
environment: t.SENTRY_ENVIRONMENT,
|
|
280
|
+
release: t.SENTRY_RELEASE,
|
|
281
|
+
traces: !1
|
|
282
|
+
}, s = await a.initSentryServer(o, n);
|
|
283
|
+
if (s !== void 0) return n.info("sentry active", {
|
|
284
|
+
...a.sentryActiveFields(s, o),
|
|
285
|
+
process: "web"
|
|
286
|
+
}), (e, t) => {
|
|
287
|
+
try {
|
|
288
|
+
s.sdk.captureException(e, {
|
|
289
|
+
tags: {
|
|
290
|
+
"voltro.process": "web",
|
|
291
|
+
"voltro.stage": t.stage
|
|
292
|
+
},
|
|
293
|
+
...t.pathname === void 0 ? {} : { extra: { pathname: t.pathname } }
|
|
294
|
+
});
|
|
295
|
+
} catch {}
|
|
296
|
+
};
|
|
297
|
+
}, J = H({ scope: "voltro:start" }), Dt = nt() ?? "unknown", Ot = (e) => e instanceof Error && e._voltroControl === "redirect" && typeof e.location == "string", kt = (e) => e instanceof Error && e._voltroControl === "not-found", At = {
|
|
257
298
|
".html": "text/html; charset=utf-8",
|
|
258
299
|
".js": "application/javascript",
|
|
259
300
|
".mjs": "application/javascript",
|
|
@@ -271,13 +312,13 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
271
312
|
".woff": "font/woff",
|
|
272
313
|
".woff2": "font/woff2",
|
|
273
314
|
".txt": "text/plain; charset=utf-8"
|
|
274
|
-
},
|
|
315
|
+
}, jt = (e) => At[L(e).toLowerCase()] ?? "application/octet-stream", Mt = (e) => e.replace(/<script\b(?=[^>]*\btype="module")(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<script\b(?=[^>]*\bsrc=)(?:(?!<\/script>).)*<\/script>/gi, "").replace(/<link\b[^>]*\brel="modulepreload"[^>]*>/gi, ""), Y = async (e) => {
|
|
275
316
|
try {
|
|
276
317
|
return await B.stat(e), !0;
|
|
277
318
|
} catch {
|
|
278
319
|
return !1;
|
|
279
320
|
}
|
|
280
|
-
},
|
|
321
|
+
}, Nt = (e) => {
|
|
281
322
|
let t = [], n = 0, r = e.split("/").filter(Boolean), i = [];
|
|
282
323
|
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, "\\$&"));
|
|
283
324
|
let a = i.join("/");
|
|
@@ -286,8 +327,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
286
327
|
paramNames: t,
|
|
287
328
|
priority: n
|
|
288
329
|
};
|
|
289
|
-
},
|
|
290
|
-
let { regex: t, paramNames: n, priority: r } =
|
|
330
|
+
}, Pt = (e) => e.map((e) => {
|
|
331
|
+
let { regex: t, paramNames: n, priority: r } = Nt(e.pattern);
|
|
291
332
|
return {
|
|
292
333
|
pattern: e.pattern,
|
|
293
334
|
file: e.file,
|
|
@@ -296,7 +337,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
296
337
|
paramNames: n,
|
|
297
338
|
priority: r
|
|
298
339
|
};
|
|
299
|
-
}).slice().sort((e, t) => e.priority - t.priority),
|
|
340
|
+
}).slice().sort((e, t) => e.priority - t.priority), Ft = (e, t) => {
|
|
300
341
|
for (let n of t) {
|
|
301
342
|
let t = n.regex.exec(e);
|
|
302
343
|
if (!t) continue;
|
|
@@ -308,7 +349,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
308
349
|
};
|
|
309
350
|
}
|
|
310
351
|
return null;
|
|
311
|
-
},
|
|
352
|
+
}, X = null, It, Lt = (e, t) => t === void 0 ? Promise.resolve() : (It ??= (async () => {
|
|
312
353
|
try {
|
|
313
354
|
let n = await e.getHelpers(), r = n.apiDescriptors?.[t];
|
|
314
355
|
if (r === void 0) return;
|
|
@@ -316,9 +357,9 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
316
357
|
if (i === void 0) return;
|
|
317
358
|
i(t, await r());
|
|
318
359
|
} catch {}
|
|
319
|
-
})(),
|
|
360
|
+
})(), It), Rt = async (e) => {
|
|
320
361
|
let { provider: t, route: r, params: i, pathname: a, dirs: o, shellTemplate: s, requestHeaders: c, requestUrl: l = "", apiOrigin: u, apiName: d } = e;
|
|
321
|
-
await
|
|
362
|
+
await Lt(t, d);
|
|
322
363
|
let { search: p } = be(l), g = await ie(ae(e.middlewares ?? [], {
|
|
323
364
|
route: r.pattern,
|
|
324
365
|
pathname: a
|
|
@@ -328,9 +369,9 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
328
369
|
headers: c,
|
|
329
370
|
cookies: ee(c.cookie),
|
|
330
371
|
route: r.pattern
|
|
331
|
-
}, e.onCookieWarning), y = await t.getPage(r.pattern, r.file), x = e.forceSsrRender === !0 ? "ssr" : ze(y, r.file),
|
|
372
|
+
}, e.onCookieWarning), y = await t.getPage(r.pattern, r.file), x = e.forceSsrRender === !0 ? "ssr" : ze(y, r.file), S = x === "isr" && y.ppr === !0, C = x === "isr" && e.holesOnly !== !0 ? h(g.headers) : g.headers, ne = xe(u, C.cookie, {
|
|
332
373
|
apiName: d,
|
|
333
|
-
headers:
|
|
374
|
+
headers: C
|
|
334
375
|
});
|
|
335
376
|
if (e.holesOnly === !0) {
|
|
336
377
|
if (typeof y.loader != "function") return null;
|
|
@@ -343,8 +384,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
343
384
|
pathname: a,
|
|
344
385
|
search: p,
|
|
345
386
|
signal: e.signal,
|
|
346
|
-
headers:
|
|
347
|
-
query:
|
|
387
|
+
headers: C,
|
|
388
|
+
query: ne,
|
|
348
389
|
isServer: !0
|
|
349
390
|
},
|
|
350
391
|
helpers: await t.getHelpers(),
|
|
@@ -355,7 +396,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
355
396
|
}
|
|
356
397
|
let w = x === "spa" && we(o, r.dirChain).some((e) => e.hasLayout);
|
|
357
398
|
if (x !== "ssr" && x !== "isr" && !w) return null;
|
|
358
|
-
let re = we(o, r.dirChain), oe = {}, ce = null, le, ue = new AbortController(), fe =
|
|
399
|
+
let re = we(o, r.dirChain), oe = {}, ce = null, le, ue = new AbortController(), fe = S ? b(C, r.pattern) : C;
|
|
359
400
|
try {
|
|
360
401
|
(await t.getHelpers()).beginStoreSeeds(), {chain: le, segmentLoaderData: oe} = await D({
|
|
361
402
|
entries: re,
|
|
@@ -368,7 +409,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
368
409
|
search: p,
|
|
369
410
|
signal: ue.signal,
|
|
370
411
|
headers: fe,
|
|
371
|
-
query:
|
|
412
|
+
query: ne,
|
|
372
413
|
isServer: !0
|
|
373
414
|
}
|
|
374
415
|
}), typeof y.loader == "function" && !w && (ce = await y.loader({
|
|
@@ -377,18 +418,18 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
377
418
|
search: p,
|
|
378
419
|
signal: ue.signal,
|
|
379
420
|
headers: fe,
|
|
380
|
-
query:
|
|
421
|
+
query: ne,
|
|
381
422
|
isServer: !0
|
|
382
423
|
}));
|
|
383
424
|
} catch (e) {
|
|
384
|
-
if (
|
|
425
|
+
if (Ot(e)) return {
|
|
385
426
|
control: "redirect",
|
|
386
427
|
status: e.status,
|
|
387
428
|
location: e.location,
|
|
388
429
|
setCookies: g.setCookies,
|
|
389
430
|
responseHeaders: g.responseHeaders
|
|
390
431
|
};
|
|
391
|
-
if (
|
|
432
|
+
if (kt(e)) return {
|
|
392
433
|
control: "not-found",
|
|
393
434
|
setCookies: g.setCookies,
|
|
394
435
|
responseHeaders: g.responseHeaders
|
|
@@ -399,11 +440,11 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
399
440
|
w || await te({
|
|
400
441
|
pageMod: y,
|
|
401
442
|
params: i,
|
|
402
|
-
query:
|
|
443
|
+
query: ne,
|
|
403
444
|
apiName: d,
|
|
404
445
|
seed: O.seedPreloadedSubscription,
|
|
405
446
|
seedFailure: O.seedPreloadedSubscriptionFailure,
|
|
406
|
-
onError: ({ tag: e, apiName: t }, n) =>
|
|
447
|
+
onError: ({ tag: e, apiName: t }, n) => J.warn("preload seed failed", {
|
|
407
448
|
tag: e,
|
|
408
449
|
apiName: t,
|
|
409
450
|
pathname: a
|
|
@@ -421,11 +462,11 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
421
462
|
};
|
|
422
463
|
if (x === "isr" && g.cspNonce !== void 0) throw Error(`page '${r.pattern}' is renderMode 'isr' while middleware.ts mints a cspNonce — an isr render is CACHED and a cached nonce is a lie the browser enforces. Use renderMode 'ssr' for nonce'd pages, or a hash-based CSP for isr.`);
|
|
423
464
|
let me = {
|
|
424
|
-
cookies:
|
|
465
|
+
cookies: S ? v(f(g.cookies), r.pattern) : x === "isr" ? f(g.cookies) : g.cookies,
|
|
425
466
|
headers: fe,
|
|
426
467
|
url: l,
|
|
427
468
|
...e.formFlash === void 0 ? {} : { formFlash: e.formFlash }
|
|
428
|
-
}, k = O.i18n?.resolve(
|
|
469
|
+
}, k = O.i18n?.resolve(C.cookie, C["accept-language"]), he = {
|
|
429
470
|
lang: k?.locale ?? me.cookies["voltro:locale"] ?? "en",
|
|
430
471
|
...k?.timeZone === void 0 ? {} : { timeZone: k.timeZone },
|
|
431
472
|
renderedAt: Date.now()
|
|
@@ -478,7 +519,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
478
519
|
...g.cspNonce === void 0 ? {} : { nonce: g.cspNonce }
|
|
479
520
|
}),
|
|
480
521
|
onShellError: (e) => {
|
|
481
|
-
|
|
522
|
+
J.error("spa layout shell render failed", { pathname: a }, e), Q?.(e, {
|
|
523
|
+
stage: "spa-layout-shell",
|
|
524
|
+
pathname: a
|
|
525
|
+
}), r.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), r.end("server error");
|
|
482
526
|
}
|
|
483
527
|
})
|
|
484
528
|
};
|
|
@@ -504,9 +548,9 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
504
548
|
loaderData: ce,
|
|
505
549
|
segmentLoaderData: oe,
|
|
506
550
|
pageLoaderRan: typeof y.loader == "function",
|
|
507
|
-
...
|
|
551
|
+
...S ? { ppr: !0 } : {}
|
|
508
552
|
});
|
|
509
|
-
|
|
553
|
+
S && _(j.stateInput, r.pattern);
|
|
510
554
|
let ge = {
|
|
511
555
|
descriptor: pe,
|
|
512
556
|
params: i,
|
|
@@ -520,8 +564,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
520
564
|
locale: k.locale
|
|
521
565
|
} : {}
|
|
522
566
|
}, _e = (t) => {
|
|
523
|
-
if (
|
|
524
|
-
let n =
|
|
567
|
+
if (X === null || x !== "ssr" && x !== "isr" || typeof y.ogImage != "function" || t.includes("og:image")) return "";
|
|
568
|
+
let n = C["x-tenant"], i = X.signOgUrl({
|
|
525
569
|
pattern: r.pattern,
|
|
526
570
|
params: e.params,
|
|
527
571
|
...n === void 0 ? {} : { tenant: n }
|
|
@@ -556,13 +600,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
556
600
|
...g.cspNonce === void 0 ? {} : { nonce: g.cspNonce }
|
|
557
601
|
}),
|
|
558
602
|
onShellError: (e) => {
|
|
559
|
-
|
|
603
|
+
J.error("ssr shell render failed", { pathname: a }, e), Q?.(e, {
|
|
604
|
+
stage: "ssr-shell",
|
|
605
|
+
pathname: a
|
|
606
|
+
}), t.writeHead(500, { "content-type": "text/plain; charset=utf-8" }), t.end("server error");
|
|
560
607
|
}
|
|
561
608
|
})
|
|
562
609
|
};
|
|
563
610
|
}
|
|
564
611
|
let { html: Se, meta: Ce } = O.renderPageToHtml(ge), Te = ye(Ce), F = M(m(s.replace("<div id=\"root\"></div>", `<div id="root">${Se}</div>`), Te), he);
|
|
565
|
-
A === "none" && (F =
|
|
612
|
+
A === "none" && (F = Mt(F));
|
|
566
613
|
let Ee = x === "isr" ? I(y.revalidate) : null;
|
|
567
614
|
return {
|
|
568
615
|
html: g.cspNonce === void 0 ? F : T(F, g.cspNonce),
|
|
@@ -571,7 +618,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
571
618
|
setCookies: g.setCookies,
|
|
572
619
|
responseHeaders: g.responseHeaders
|
|
573
620
|
};
|
|
574
|
-
},
|
|
621
|
+
}, Z = (e) => e === "" ? "/" : e, zt = async (e) => {
|
|
575
622
|
let t = await import(V(e).href);
|
|
576
623
|
return {
|
|
577
624
|
kind: "bundle",
|
|
@@ -582,18 +629,18 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
582
629
|
return typeof r == "function" ? await r() : r;
|
|
583
630
|
},
|
|
584
631
|
async getLayout(e) {
|
|
585
|
-
let n = t.layouts[
|
|
586
|
-
if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${
|
|
632
|
+
let n = t.layouts[Z(e)];
|
|
633
|
+
if (!n) throw Error(`[voltro start] ssr bundle missing layout for dir ${Z(e)}`);
|
|
587
634
|
return typeof n == "function" ? await n() : n;
|
|
588
635
|
},
|
|
589
636
|
async getError(e) {
|
|
590
|
-
let n = t.errors[
|
|
591
|
-
if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${
|
|
637
|
+
let n = t.errors[Z(e)];
|
|
638
|
+
if (!n) throw Error(`[voltro start] ssr bundle missing error for dir ${Z(e)}`);
|
|
592
639
|
return typeof n == "function" ? await n() : n;
|
|
593
640
|
},
|
|
594
641
|
async getPending(e) {
|
|
595
|
-
let n = t.pendings[
|
|
596
|
-
if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${
|
|
642
|
+
let n = t.pendings[Z(e)];
|
|
643
|
+
if (!n) throw Error(`[voltro start] ssr bundle missing pending for dir ${Z(e)}`);
|
|
597
644
|
return typeof n == "function" ? await n() : n;
|
|
598
645
|
},
|
|
599
646
|
async getHelpers() {
|
|
@@ -601,8 +648,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
601
648
|
},
|
|
602
649
|
async close() {}
|
|
603
650
|
};
|
|
604
|
-
},
|
|
605
|
-
let n = await
|
|
651
|
+
}, Bt = async (e, t) => {
|
|
652
|
+
let n = await C("vite", () => import("vite")), r = (await import("@vitejs/plugin-react")).default, i = (await import("@tailwindcss/vite")).default, a = await n.createServer({
|
|
606
653
|
root: t,
|
|
607
654
|
envPrefix: "__VOLTRO_VITE_ENV_DISABLED__",
|
|
608
655
|
plugins: [r(), i()],
|
|
@@ -621,8 +668,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
621
668
|
logLevel: "warn"
|
|
622
669
|
}), o = R(e, "src", "pages"), s = (e) => e === "" ? o : R(o, e), c = g({
|
|
623
670
|
limit: w(),
|
|
624
|
-
onColdStart: (e) =>
|
|
625
|
-
onColdEnd: (e, { durationMs: t, failed: n }) =>
|
|
671
|
+
onColdStart: (e) => J.debug("ssr cold-compile start", { id: e }),
|
|
672
|
+
onColdEnd: (e, { durationMs: t, failed: n }) => J.debug(`ssr cold-compile ${n ? "FAILED" : "end"} ${t}ms`, {
|
|
626
673
|
id: e,
|
|
627
674
|
ms: t,
|
|
628
675
|
...n ? { failed: !0 } : {}
|
|
@@ -649,19 +696,20 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
649
696
|
await a.close();
|
|
650
697
|
}
|
|
651
698
|
};
|
|
652
|
-
},
|
|
699
|
+
}, Q, $ = async (t) => {
|
|
653
700
|
let n = Ye(), f = await j(t);
|
|
654
701
|
if (!f) return {
|
|
655
702
|
ran: !1,
|
|
656
703
|
exitCode: 0
|
|
657
704
|
};
|
|
658
|
-
if (f.type !== "web") return
|
|
705
|
+
if (f.type !== "web") return J.error("only web apps are supported by `voltro start`"), {
|
|
659
706
|
ran: !0,
|
|
660
707
|
exitCode: 1
|
|
661
708
|
};
|
|
709
|
+
Q = await Et(t, process.env, J);
|
|
662
710
|
{
|
|
663
711
|
let e = s(f);
|
|
664
|
-
if (e !== void 0) return
|
|
712
|
+
if (e !== void 0) return J.error(`voltro start: refusing to boot — ${e}`), {
|
|
665
713
|
ran: !0,
|
|
666
714
|
exitCode: 1
|
|
667
715
|
};
|
|
@@ -670,19 +718,19 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
670
718
|
rawEnv: f.env,
|
|
671
719
|
appType: "web",
|
|
672
720
|
root: t,
|
|
673
|
-
log:
|
|
721
|
+
log: J,
|
|
674
722
|
generateTypes: !1
|
|
675
723
|
}), te = m.publicValues;
|
|
676
724
|
n.mark("config");
|
|
677
725
|
let h = ue();
|
|
678
|
-
fe(h),
|
|
726
|
+
fe(h), J.info("booting voltro start", { app: f.name ?? "(unnamed)" });
|
|
679
727
|
let g = Me({
|
|
680
728
|
kind: "web",
|
|
681
729
|
configPort: f.port,
|
|
682
730
|
env: process.env,
|
|
683
731
|
dashboardPin: process.env[pe] === "1"
|
|
684
732
|
});
|
|
685
|
-
for (let e of g.warnings)
|
|
733
|
+
for (let e of g.warnings) J.warn(e);
|
|
686
734
|
let _ = g.port, v = R(t, ".framework");
|
|
687
735
|
{
|
|
688
736
|
let e;
|
|
@@ -691,11 +739,11 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
691
739
|
} catch {
|
|
692
740
|
e = void 0;
|
|
693
741
|
}
|
|
694
|
-
let t =
|
|
695
|
-
t.length > 0 &&
|
|
742
|
+
let t = pt(process.env, e);
|
|
743
|
+
t.length > 0 && J.warn(mt(t));
|
|
696
744
|
}
|
|
697
|
-
let b = R(v, "dist"),
|
|
698
|
-
if (!await
|
|
745
|
+
let b = R(v, "dist"), C = R(b, "index.html");
|
|
746
|
+
if (!await Y(C)) return J.error("no built dist found — run `voltro build` first", { distDir: b }), {
|
|
699
747
|
ran: !0,
|
|
700
748
|
exitCode: 1
|
|
701
749
|
};
|
|
@@ -709,78 +757,78 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
709
757
|
status: "booting",
|
|
710
758
|
logFile: E.path
|
|
711
759
|
}).catch(() => {});
|
|
712
|
-
let se = R(b, "_voltro", "shell.html"), D = await
|
|
713
|
-
ze.has(e.name) || (ze.add(e.name),
|
|
760
|
+
let se = R(b, "_voltro", "shell.html"), D = await Y(se) ? await B.readFile(se, "utf8") : await B.readFile(C, "utf8"), de = await O(t, f), ve = Fe(f), M = Xe(f), N = _e(de[0]), P = de[0]?.name, ze = /* @__PURE__ */ new Set(), I = (e) => {
|
|
761
|
+
ze.has(e.name) || (ze.add(e.name), J.warn(`middleware.ts wrote the cookie "${e.name}" with the HttpOnly default — a browser SDK cannot read it`, {
|
|
714
762
|
cookie: e.name,
|
|
715
763
|
hint: "If your IdP client reads this from `document.cookie` (Supabase's `createBrowserClient` does), pass `httpOnly: false`. Otherwise the SSR render succeeds and the user is signed out at the first client-side call. Set `httpOnly: true` explicitly to silence this."
|
|
716
764
|
}));
|
|
717
765
|
}, L = await a(t);
|
|
718
|
-
L.length > 0 &&
|
|
766
|
+
L.length > 0 && J.info(`middleware.ts loaded — ${L.length} middleware(s): ` + L.map((e) => e.exportName).join(", "));
|
|
719
767
|
let z, V = null, H = process.env.SSR_CACHE === "postgres";
|
|
720
|
-
if (H && Pe()) V = new (await (import("pg"))).default.Client(Ne()), await V.connect(), z =
|
|
768
|
+
if (H && Pe()) V = new (await (import("pg"))).default.Client(Ne()), await V.connect(), z = St({ client: V }), J.info("isr cache backend: postgres", { table: "voltro_isr_cache" });
|
|
721
769
|
else if (H) {
|
|
722
770
|
let t = "SSR_CACHE=postgres, but nothing in the environment names a database (looked for DB_URL, DB_PRIMARY_URL, DB_DIRECT_URL, DB_MIGRATE_URL, DB_HOST, PG_HOST).";
|
|
723
|
-
if (e()) throw
|
|
724
|
-
z = Ve(),
|
|
725
|
-
} else z = Ve(),
|
|
771
|
+
if (e()) throw J.fatal(`${t} Refusing to fall back to the per-process memory cache: it is not shared between instances and does not survive a restart, so the pages this process serves would differ from its replicas' with nothing to indicate it.`), Error("[voltro start] SSR_CACHE=postgres with no database configured");
|
|
772
|
+
z = Ve(), J.warn(`${t} Falling back to the per-process memory cache — this would be a hard failure in production.`);
|
|
773
|
+
} else z = Ve(), J.info("isr cache backend: memory (per-process)", { hint: "set SSR_CACHE=postgres for persistent storage" });
|
|
726
774
|
await o(t, f, de, te);
|
|
727
|
-
let nt = R(t, "src", "pages"), { pages: rt, dirs: U } = await re(nt), W =
|
|
728
|
-
|
|
729
|
-
let
|
|
730
|
-
if (!
|
|
731
|
-
let
|
|
732
|
-
|
|
733
|
-
kind:
|
|
734
|
-
...
|
|
775
|
+
let nt = R(t, "src", "pages"), { pages: rt, dirs: U } = await re(nt), W = Pt(rt);
|
|
776
|
+
ne(L, W.map((e) => e.pattern)), n.mark("scan");
|
|
777
|
+
let lt = R(b, "server", "ssrEntry.js"), ft = await Y(lt);
|
|
778
|
+
if (!ft && process.env.NODE_ENV === "production") throw J.fatal(`production \`voltro start\` requires a precompiled SSR bundle at ${lt} 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 ${lt}`);
|
|
779
|
+
let G = ft ? await zt(lt) : await Bt(t, v);
|
|
780
|
+
J.info(`ssr loader: ${G.kind}`, {
|
|
781
|
+
kind: G.kind,
|
|
782
|
+
...G.kind === "middleware" ? { hint: "no pre-built SSR bundle found; run `voltro build` to enable bundle mode" } : {}
|
|
735
783
|
}), n.mark("provider");
|
|
736
|
-
let vt =
|
|
784
|
+
let vt = G.getPageMeta?.();
|
|
737
785
|
c(t);
|
|
738
|
-
let
|
|
786
|
+
let K = /* @__PURE__ */ new Map(), bt = 0;
|
|
739
787
|
for (let e of rt) {
|
|
740
788
|
let t = vt?.[e.pattern];
|
|
741
789
|
if (t) {
|
|
742
|
-
|
|
790
|
+
K.set(e.pattern, t);
|
|
743
791
|
continue;
|
|
744
792
|
}
|
|
745
793
|
try {
|
|
746
|
-
let t = await
|
|
747
|
-
|
|
794
|
+
let t = await G.getPage(e.pattern, e.file);
|
|
795
|
+
K.set(e.pattern, Re(t, e.file)), bt++;
|
|
748
796
|
} catch (t) {
|
|
749
|
-
|
|
797
|
+
J.warn("failed to load page metadata", { pattern: e.pattern }, t);
|
|
750
798
|
}
|
|
751
799
|
}
|
|
752
|
-
let
|
|
753
|
-
for (let [e, t] of
|
|
800
|
+
let xt = /* @__PURE__ */ new Map();
|
|
801
|
+
for (let [e, t] of K) {
|
|
754
802
|
if (t.interactive !== "islands") continue;
|
|
755
803
|
let n = R(b, "island-shells", `${k(e)}.html`);
|
|
756
804
|
try {
|
|
757
|
-
|
|
805
|
+
xt.set(e, await B.readFile(n, "utf8"));
|
|
758
806
|
} catch {
|
|
759
|
-
|
|
807
|
+
J.warn("islands page has no built per-page shell — serving the full app shell", {
|
|
760
808
|
pattern: e,
|
|
761
809
|
expected: n
|
|
762
810
|
});
|
|
763
811
|
}
|
|
764
812
|
}
|
|
765
|
-
|
|
766
|
-
count:
|
|
767
|
-
fromManifest:
|
|
768
|
-
fromModules:
|
|
813
|
+
J.info(`registered ${K.size} route(s)`, {
|
|
814
|
+
count: K.size,
|
|
815
|
+
fromManifest: K.size - bt,
|
|
816
|
+
fromModules: bt
|
|
769
817
|
}), n.mark("routes");
|
|
770
|
-
let
|
|
771
|
-
for (let e of W)
|
|
772
|
-
|
|
773
|
-
let wt = /* @__PURE__ */ new Map();
|
|
774
|
-
for (let [e, t] of q) t.cacheInvalidatesOn.length > 0 && wt.set(e, t.cacheInvalidatesOn);
|
|
818
|
+
let q = /* @__PURE__ */ new Set();
|
|
819
|
+
for (let e of W) K.get(e.pattern)?.renderMode === "spa" && we(U, e.dirChain).some((e) => e.hasLayout) && q.add(e.pattern);
|
|
820
|
+
q.size > 0 && J.info(`${q.size} spa route(s) render an SSR layout shell`, { patterns: [...q] });
|
|
775
821
|
let Tt = /* @__PURE__ */ new Map();
|
|
776
|
-
for (let e of
|
|
822
|
+
for (let [e, t] of K) t.cacheInvalidatesOn.length > 0 && Tt.set(e, t.cacheInvalidatesOn);
|
|
823
|
+
let Ot = /* @__PURE__ */ new Map();
|
|
824
|
+
for (let e of W) Ot.set(e.pattern, e.regex);
|
|
777
825
|
{
|
|
778
|
-
let n = new Set([...
|
|
826
|
+
let n = new Set([...K].filter(([, e]) => (e.renderMode === "ssr" || e.renderMode === "isr") && e.hasOgImage).map(([e]) => e));
|
|
779
827
|
if (n.size > 0) {
|
|
780
828
|
let { secret: r, fromEnv: i } = Te();
|
|
781
|
-
if (!i && e()) throw
|
|
829
|
+
if (!i && e()) throw J.fatal(`${n.size} ssr route(s) export ogImage, but VOLTRO_OG_SECRET is unset. Behind a load balancer the signing replica and the fetching replica differ, so a per-boot secret would 403 every cross-replica image fetch. Set VOLTRO_OG_SECRET (any long random value, same on every replica).`), Error("[voltro start] ssr ogImage routes need VOLTRO_OG_SECRET in a deploy environment");
|
|
782
830
|
let a = new Map(rt.map((e) => [e.pattern, e.file]));
|
|
783
|
-
|
|
831
|
+
X = le({
|
|
784
832
|
root: t,
|
|
785
833
|
fonts: f.fonts,
|
|
786
834
|
cache: z,
|
|
@@ -788,45 +836,45 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
788
836
|
isOgSsrRoute: (e) => n.has(e),
|
|
789
837
|
getPageModule: async (e) => {
|
|
790
838
|
let t = a.get(e);
|
|
791
|
-
return t === void 0 ? null : await
|
|
839
|
+
return t === void 0 ? null : await G.getPage(e, t);
|
|
792
840
|
}
|
|
793
|
-
}),
|
|
841
|
+
}), J.info(`og route active for ${n.size} ssr route(s)`, { secret: i ? "env" : "per-boot" });
|
|
794
842
|
}
|
|
795
843
|
}
|
|
796
|
-
let
|
|
797
|
-
for (let [e, t] of
|
|
798
|
-
let
|
|
844
|
+
let kt = /* @__PURE__ */ new Map();
|
|
845
|
+
for (let [e, t] of K) kt.set(e, t.renderMode);
|
|
846
|
+
let At = [...K.values()].some((e) => e.renderMode === "isr"), Mt = {
|
|
799
847
|
cache: z,
|
|
800
|
-
tableSink:
|
|
848
|
+
tableSink: _t({
|
|
801
849
|
cache: z,
|
|
802
|
-
tableSubscriptionsByPattern:
|
|
803
|
-
regexByPattern:
|
|
850
|
+
tableSubscriptionsByPattern: Tt,
|
|
851
|
+
regexByPattern: Ot
|
|
804
852
|
}),
|
|
805
|
-
regexByPattern:
|
|
806
|
-
renderModeByPattern:
|
|
853
|
+
regexByPattern: Ot,
|
|
854
|
+
renderModeByPattern: kt
|
|
807
855
|
}, Nt = (e) => {
|
|
808
|
-
let t =
|
|
809
|
-
t &&
|
|
810
|
-
},
|
|
856
|
+
let t = Ct(e);
|
|
857
|
+
t && wt(Mt, t);
|
|
858
|
+
}, It = yt({
|
|
811
859
|
cache: z,
|
|
812
|
-
tableSubscriptionsByPattern:
|
|
813
|
-
regexByPattern:
|
|
814
|
-
...
|
|
815
|
-
}), Lt = await
|
|
816
|
-
if (
|
|
817
|
-
let e =
|
|
860
|
+
tableSubscriptionsByPattern: Tt,
|
|
861
|
+
regexByPattern: Ot,
|
|
862
|
+
...At ? { onRevalidate: Nt } : {}
|
|
863
|
+
}), Lt = await It.start(), Z = null, $ = null;
|
|
864
|
+
if (At) {
|
|
865
|
+
let e = ut({}, process.env);
|
|
818
866
|
if (e.crossReplica) try {
|
|
819
|
-
|
|
867
|
+
$ = e.provider;
|
|
820
868
|
let t = Ie(process.env);
|
|
821
|
-
|
|
869
|
+
Z = await ct.runPromise(e.provider.subscribe(t, Nt)), J.info(`revalidation subscriber: broker (${e.provider.name})`, { channel: t });
|
|
822
870
|
} catch (e) {
|
|
823
|
-
|
|
871
|
+
J.warn("revalidation broker subscribe failed — broker leg disabled", {}, e), $ = null;
|
|
824
872
|
}
|
|
825
|
-
Lt &&
|
|
873
|
+
Lt && Z === null && J.info("revalidation subscriber: postgres LISTEN (voltro_revalidate)"), !Lt && Z === null && J.warn("isr routes exist, but this web process has NO revalidation transport: no database is configured for the LISTEN line and no broker is configured. `revalidatePath` / `revalidateTable` / `revalidateTag` from the api will not reach this process — cached pages stay until their own `revalidate` window expires. Set DB_URL (postgres) or BROADCAST_URL (redis:// or nats://) on BOTH the api and this web deployment.");
|
|
826
874
|
}
|
|
827
875
|
n.mark("cdc");
|
|
828
|
-
let
|
|
829
|
-
let t =
|
|
876
|
+
let Vt = Le(), Ht = rt.map((e) => {
|
|
877
|
+
let t = K.get(e.pattern);
|
|
830
878
|
return {
|
|
831
879
|
pattern: e.pattern,
|
|
832
880
|
file: e.file,
|
|
@@ -841,17 +889,17 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
841
889
|
hasMeta: t?.hasMeta ?? !1,
|
|
842
890
|
hasGetStaticPaths: t?.hasGetStaticPaths ?? !1
|
|
843
891
|
};
|
|
844
|
-
}),
|
|
892
|
+
}), Ut = U.map((e) => ({
|
|
845
893
|
dir: e.dir,
|
|
846
894
|
hasLayout: e.hasLayout,
|
|
847
895
|
hasError: e.hasError,
|
|
848
896
|
hasPending: e.hasPending,
|
|
849
897
|
hasNotFound: e.hasNotFound
|
|
850
|
-
})),
|
|
898
|
+
})), Wt = V ? "postgres" : "memory", Gt = {
|
|
851
899
|
kind: "web",
|
|
852
900
|
meta: {
|
|
853
901
|
name: f.name ?? "(unnamed)",
|
|
854
|
-
voltroVersion:
|
|
902
|
+
voltroVersion: Dt,
|
|
855
903
|
port: _,
|
|
856
904
|
mode: tt(),
|
|
857
905
|
apis: Object.entries(f.apis ?? {}).map(([e, t]) => {
|
|
@@ -862,8 +910,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
862
910
|
return t.url !== void 0 && (n.url = t.url), n;
|
|
863
911
|
})
|
|
864
912
|
},
|
|
865
|
-
routes:
|
|
866
|
-
dirs:
|
|
913
|
+
routes: Ht,
|
|
914
|
+
dirs: Ut,
|
|
867
915
|
cache: () => {
|
|
868
916
|
let e = Date.now(), t = z.snapshot().map(([t, n]) => ({
|
|
869
917
|
key: t,
|
|
@@ -876,13 +924,13 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
876
924
|
staleWhileRevalidateMs: n.staleWhileRevalidateMs
|
|
877
925
|
}));
|
|
878
926
|
return {
|
|
879
|
-
backend:
|
|
927
|
+
backend: Wt,
|
|
880
928
|
size: z.size(),
|
|
881
929
|
entries: t
|
|
882
930
|
};
|
|
883
931
|
},
|
|
884
932
|
metrics: () => st()
|
|
885
|
-
},
|
|
933
|
+
}, Kt = async (e, n) => {
|
|
886
934
|
let r = e.split("?")[0] ?? "/";
|
|
887
935
|
if (f.name === "voltroDashboard" && r === "/api/dashboard/registry") try {
|
|
888
936
|
let e = await Ge();
|
|
@@ -927,8 +975,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
927
975
|
body: JSON.stringify({ apps: t })
|
|
928
976
|
};
|
|
929
977
|
}
|
|
930
|
-
if (
|
|
931
|
-
let t = await
|
|
978
|
+
if (X !== null && r === "/_voltro/og") {
|
|
979
|
+
let t = await X.handle(new URL(e, "http://localhost"));
|
|
932
980
|
if (t !== null) return {
|
|
933
981
|
status: t.status,
|
|
934
982
|
headers: { ...t.headers },
|
|
@@ -947,7 +995,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
947
995
|
};
|
|
948
996
|
}
|
|
949
997
|
if (r !== "/_voltro/inspect/stream") {
|
|
950
|
-
let t = ke(e,
|
|
998
|
+
let t = ke(e, Gt, n);
|
|
951
999
|
if (t !== null) return {
|
|
952
1000
|
status: t.status,
|
|
953
1001
|
headers: {
|
|
@@ -957,7 +1005,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
957
1005
|
body: t.body
|
|
958
1006
|
};
|
|
959
1007
|
}
|
|
960
|
-
let i =
|
|
1008
|
+
let i = Ft(r, W), a = i ? K.get(i.route.pattern)?.renderMode : void 0, o = a === "ssr" || a === "isr" || (i ? q.has(i.route.pattern) : !1), s = [];
|
|
961
1009
|
if (!o && L.length > 0) {
|
|
962
1010
|
let t = ae(L, {
|
|
963
1011
|
route: void 0,
|
|
@@ -978,7 +1026,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
978
1026
|
...e,
|
|
979
1027
|
"set-cookie": s.map(d)
|
|
980
1028
|
}, u = r === "/" ? R(b, "index.html") : R(b, r.replace(/^\//, ""), "index.html");
|
|
981
|
-
if (!o && await
|
|
1029
|
+
if (!o && await Y(u)) {
|
|
982
1030
|
let e = await B.readFile(u);
|
|
983
1031
|
return {
|
|
984
1032
|
status: 200,
|
|
@@ -996,18 +1044,18 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
996
1044
|
};
|
|
997
1045
|
if (/\.[a-z0-9]+$/i.test(r)) {
|
|
998
1046
|
let e = R(b, r.replace(/^\//, ""));
|
|
999
|
-
if (await
|
|
1047
|
+
if (await Y(e)) {
|
|
1000
1048
|
let t = $e(r, M), i = at(n["accept-encoding"]), a = i === "br" ? `${e}.br` : i === "gzip" ? `${e}.gz` : void 0;
|
|
1001
|
-
if (a !== void 0 && await
|
|
1049
|
+
if (a !== void 0 && await Y(a)) {
|
|
1002
1050
|
let n = await B.readFile(e);
|
|
1003
1051
|
return {
|
|
1004
1052
|
status: 200,
|
|
1005
1053
|
headers: c({
|
|
1006
|
-
"content-type":
|
|
1054
|
+
"content-type": jt(e),
|
|
1007
1055
|
"cache-control": t,
|
|
1008
1056
|
"content-encoding": i === "br" ? "br" : "gzip",
|
|
1009
1057
|
vary: "Accept-Encoding",
|
|
1010
|
-
etag:
|
|
1058
|
+
etag: ht(n)
|
|
1011
1059
|
}),
|
|
1012
1060
|
body: await B.readFile(a)
|
|
1013
1061
|
};
|
|
@@ -1016,19 +1064,19 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1016
1064
|
return {
|
|
1017
1065
|
status: 200,
|
|
1018
1066
|
headers: c({
|
|
1019
|
-
"content-type":
|
|
1067
|
+
"content-type": jt(e),
|
|
1020
1068
|
"cache-control": t
|
|
1021
1069
|
}),
|
|
1022
1070
|
body: o
|
|
1023
1071
|
};
|
|
1024
1072
|
}
|
|
1025
1073
|
}
|
|
1026
|
-
let p =
|
|
1074
|
+
let p = Ft(r, W);
|
|
1027
1075
|
if (!p) {
|
|
1028
1076
|
let i = ce(U, r);
|
|
1029
1077
|
if (i !== void 0) try {
|
|
1030
|
-
let a = await
|
|
1031
|
-
provider:
|
|
1078
|
+
let a = await Rt({
|
|
1079
|
+
provider: G,
|
|
1032
1080
|
root: t,
|
|
1033
1081
|
route: {
|
|
1034
1082
|
pattern: "<not-found>",
|
|
@@ -1060,7 +1108,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1060
1108
|
body: a.html
|
|
1061
1109
|
};
|
|
1062
1110
|
} catch (e) {
|
|
1063
|
-
|
|
1111
|
+
J.warn("not-found.tsx failed to render — serving the plain 404", { pathname: r }, e instanceof Error ? e : Error(String(e))), Q?.(e, {
|
|
1112
|
+
stage: "not-found-render",
|
|
1113
|
+
pathname: r
|
|
1114
|
+
});
|
|
1064
1115
|
}
|
|
1065
1116
|
return {
|
|
1066
1117
|
status: 404,
|
|
@@ -1071,7 +1122,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1071
1122
|
body: "not found"
|
|
1072
1123
|
};
|
|
1073
1124
|
}
|
|
1074
|
-
let m =
|
|
1125
|
+
let m = K.get(p.route.pattern), te = n["x-tenant"] ?? "anonymous", g = (await G.getHelpers()).i18n?.resolve(n.cookie, n["accept-language"])?.locale, _ = He(r, {
|
|
1075
1126
|
...m?.tenantAware ? { tenant: te } : {},
|
|
1076
1127
|
...g ? { locale: g } : {}
|
|
1077
1128
|
}), v = m?.ppr === !0 && m.renderMode === "isr", y = (i, a) => ({
|
|
@@ -1086,14 +1137,14 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1086
1137
|
...a
|
|
1087
1138
|
}), o.write(i), (async () => {
|
|
1088
1139
|
try {
|
|
1089
|
-
let i = await
|
|
1090
|
-
provider:
|
|
1140
|
+
let i = await Rt({
|
|
1141
|
+
provider: G,
|
|
1091
1142
|
root: t,
|
|
1092
1143
|
route: p.route,
|
|
1093
1144
|
params: p.params,
|
|
1094
1145
|
pathname: r,
|
|
1095
1146
|
dirs: U,
|
|
1096
|
-
shellTemplate:
|
|
1147
|
+
shellTemplate: xt.get(p.route.pattern) ?? D,
|
|
1097
1148
|
requestHeaders: n,
|
|
1098
1149
|
apiOrigin: N,
|
|
1099
1150
|
apiName: P,
|
|
@@ -1108,23 +1159,26 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1108
1159
|
res: o,
|
|
1109
1160
|
helpers: i.holes.helpers,
|
|
1110
1161
|
page: i.holes.page,
|
|
1111
|
-
log:
|
|
1162
|
+
log: J
|
|
1112
1163
|
}) : o.writableEnded || o.end();
|
|
1113
1164
|
} catch (e) {
|
|
1114
|
-
|
|
1165
|
+
J.warn("ppr hole pass failed", { url: r }, e), Q?.(e, {
|
|
1166
|
+
stage: "ppr-holes",
|
|
1167
|
+
pathname: r
|
|
1168
|
+
}), o.writableEnded || o.end();
|
|
1115
1169
|
}
|
|
1116
1170
|
})();
|
|
1117
1171
|
}
|
|
1118
|
-
}), x = z.get(_),
|
|
1119
|
-
if (x && Ue(x,
|
|
1172
|
+
}), x = z.get(_), C = Date.now();
|
|
1173
|
+
if (x && Ue(x, C)) return h.emit("cache", {
|
|
1120
1174
|
kind: "HIT",
|
|
1121
1175
|
key: _,
|
|
1122
|
-
ageMs:
|
|
1176
|
+
ageMs: C - x.renderedAt
|
|
1123
1177
|
}), v ? y(x.html, {
|
|
1124
1178
|
"cache-control": Ze(M, x.revalidateMs, x.staleWhileRevalidateMs),
|
|
1125
1179
|
"x-voltro-cache": "HIT",
|
|
1126
1180
|
"x-voltro-cache-key": _,
|
|
1127
|
-
"x-voltro-cache-age-ms": String(
|
|
1181
|
+
"x-voltro-cache-age-ms": String(C - x.renderedAt)
|
|
1128
1182
|
}) : {
|
|
1129
1183
|
status: 200,
|
|
1130
1184
|
headers: {
|
|
@@ -1133,27 +1187,27 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1133
1187
|
"x-voltro-rendered-by": "isr",
|
|
1134
1188
|
"x-voltro-cache": "HIT",
|
|
1135
1189
|
"x-voltro-cache-key": _,
|
|
1136
|
-
"x-voltro-cache-age-ms": String(
|
|
1190
|
+
"x-voltro-cache-age-ms": String(C - x.renderedAt)
|
|
1137
1191
|
},
|
|
1138
1192
|
body: x.html
|
|
1139
1193
|
};
|
|
1140
|
-
if (x && Be(x,
|
|
1194
|
+
if (x && Be(x, C)) {
|
|
1141
1195
|
h.emit("cache", {
|
|
1142
1196
|
kind: "STALE",
|
|
1143
1197
|
key: _,
|
|
1144
|
-
ageMs:
|
|
1198
|
+
ageMs: C - x.renderedAt
|
|
1145
1199
|
});
|
|
1146
1200
|
let i = z.generation(_);
|
|
1147
1201
|
return (async () => {
|
|
1148
1202
|
try {
|
|
1149
|
-
let a = await
|
|
1150
|
-
provider:
|
|
1203
|
+
let a = await Rt({
|
|
1204
|
+
provider: G,
|
|
1151
1205
|
root: t,
|
|
1152
1206
|
route: p.route,
|
|
1153
1207
|
params: p.params,
|
|
1154
1208
|
pathname: r,
|
|
1155
1209
|
dirs: U,
|
|
1156
|
-
shellTemplate:
|
|
1210
|
+
shellTemplate: xt.get(p.route.pattern) ?? D,
|
|
1157
1211
|
requestHeaders: n,
|
|
1158
1212
|
apiOrigin: N,
|
|
1159
1213
|
apiName: P,
|
|
@@ -1163,7 +1217,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1163
1217
|
buffered: !0
|
|
1164
1218
|
});
|
|
1165
1219
|
if (a && "html" in a && a.renderMode === "isr" && a.revalidateMs !== null) {
|
|
1166
|
-
let e =
|
|
1220
|
+
let e = K.get(p.route.pattern)?.staleWhileRevalidateMs ?? 0, t = Date.now();
|
|
1167
1221
|
z.set(_, {
|
|
1168
1222
|
html: a.html,
|
|
1169
1223
|
renderedAt: t,
|
|
@@ -1171,16 +1225,19 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1171
1225
|
servableUntil: t + a.revalidateMs + e,
|
|
1172
1226
|
revalidateMs: a.revalidateMs,
|
|
1173
1227
|
staleWhileRevalidateMs: e
|
|
1174
|
-
}, { ifGeneration: i }) ||
|
|
1228
|
+
}, { ifGeneration: i }) || J.info("SWR refresh discarded — the entry was purged while it rendered", { key: _ });
|
|
1175
1229
|
}
|
|
1176
1230
|
} catch (e) {
|
|
1177
|
-
|
|
1231
|
+
J.warn("background SWR refresh failed", { url: r }, e), Q?.(e, {
|
|
1232
|
+
stage: "swr-refresh",
|
|
1233
|
+
pathname: r
|
|
1234
|
+
});
|
|
1178
1235
|
}
|
|
1179
1236
|
})(), v ? y(x.html, {
|
|
1180
1237
|
"cache-control": Ze(M, x.revalidateMs, x.staleWhileRevalidateMs),
|
|
1181
1238
|
"x-voltro-cache": "STALE",
|
|
1182
1239
|
"x-voltro-cache-key": _,
|
|
1183
|
-
"x-voltro-cache-age-ms": String(
|
|
1240
|
+
"x-voltro-cache-age-ms": String(C - x.renderedAt)
|
|
1184
1241
|
}) : {
|
|
1185
1242
|
status: 200,
|
|
1186
1243
|
headers: {
|
|
@@ -1189,20 +1246,20 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1189
1246
|
"x-voltro-rendered-by": "isr",
|
|
1190
1247
|
"x-voltro-cache": "STALE",
|
|
1191
1248
|
"x-voltro-cache-key": _,
|
|
1192
|
-
"x-voltro-cache-age-ms": String(
|
|
1249
|
+
"x-voltro-cache-age-ms": String(C - x.renderedAt)
|
|
1193
1250
|
},
|
|
1194
1251
|
body: x.html
|
|
1195
1252
|
};
|
|
1196
1253
|
}
|
|
1197
1254
|
try {
|
|
1198
|
-
let i = z.generation(_), a = await
|
|
1199
|
-
provider:
|
|
1255
|
+
let i = z.generation(_), a = await Rt({
|
|
1256
|
+
provider: G,
|
|
1200
1257
|
root: t,
|
|
1201
1258
|
route: p.route,
|
|
1202
1259
|
params: p.params,
|
|
1203
1260
|
pathname: r,
|
|
1204
1261
|
dirs: U,
|
|
1205
|
-
shellTemplate:
|
|
1262
|
+
shellTemplate: xt.get(p.route.pattern) ?? D,
|
|
1206
1263
|
requestHeaders: n,
|
|
1207
1264
|
apiOrigin: N,
|
|
1208
1265
|
apiName: P,
|
|
@@ -1250,7 +1307,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1250
1307
|
"x-voltro-rendered-by": a.renderMode
|
|
1251
1308
|
});
|
|
1252
1309
|
if (a.renderMode === "isr" && a.revalidateMs !== null) {
|
|
1253
|
-
let e = Date.now(), t =
|
|
1310
|
+
let e = Date.now(), t = K.get(p.route.pattern)?.staleWhileRevalidateMs ?? 0;
|
|
1254
1311
|
z.set(_, {
|
|
1255
1312
|
html: a.html,
|
|
1256
1313
|
renderedAt: e,
|
|
@@ -1258,7 +1315,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1258
1315
|
servableUntil: e + a.revalidateMs + t,
|
|
1259
1316
|
revalidateMs: a.revalidateMs,
|
|
1260
1317
|
staleWhileRevalidateMs: t
|
|
1261
|
-
}, { ifGeneration: i }) ||
|
|
1318
|
+
}, { ifGeneration: i }) || J.info("miss fill discarded — the entry was purged while it rendered", { key: _ }), l["x-voltro-cache"] = "MISS", l["x-voltro-cache-key"] = _, l["x-voltro-cache-revalidate-ms"] = String(a.revalidateMs), t > 0 && (l["x-voltro-cache-swr-ms"] = String(t)), h.emit("cache", {
|
|
1262
1319
|
kind: "MISS",
|
|
1263
1320
|
key: _,
|
|
1264
1321
|
revalidateMs: a.revalidateMs,
|
|
@@ -1277,7 +1334,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1277
1334
|
body: a.html
|
|
1278
1335
|
};
|
|
1279
1336
|
} catch (e) {
|
|
1280
|
-
return
|
|
1337
|
+
return J.error("render error", { url: r }, e), Q?.(e, {
|
|
1338
|
+
stage: "render",
|
|
1339
|
+
pathname: r
|
|
1340
|
+
}), S(e) ? {
|
|
1281
1341
|
status: 500,
|
|
1282
1342
|
headers: {
|
|
1283
1343
|
"content-type": "text/plain; charset=utf-8",
|
|
@@ -1297,12 +1357,12 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1297
1357
|
body: "server error"
|
|
1298
1358
|
};
|
|
1299
1359
|
}
|
|
1300
|
-
},
|
|
1360
|
+
}, qt = !1, Jt = i(f.health, () => qt), Yt = p({
|
|
1301
1361
|
apiName: P,
|
|
1302
1362
|
apiOrigin: N,
|
|
1303
1363
|
loadDescriptors: async () => {
|
|
1304
1364
|
if (P === void 0) return;
|
|
1305
|
-
let e = (await
|
|
1365
|
+
let e = (await G.getHelpers()).apiDescriptors?.[P];
|
|
1306
1366
|
return e === void 0 ? void 0 : await e();
|
|
1307
1367
|
},
|
|
1308
1368
|
rpcQuery: (e, t) => xe(N, e, {
|
|
@@ -1310,14 +1370,14 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1310
1370
|
headers: t
|
|
1311
1371
|
}),
|
|
1312
1372
|
resolveLocale: async (e, t) => {
|
|
1313
|
-
let n = await
|
|
1373
|
+
let n = await G.getHelpers(), r = u(t, n.i18n?.locales);
|
|
1314
1374
|
return r === void 0 ? n.i18n?.resolve(e.cookie, e["accept-language"])?.locale ?? "en" : r;
|
|
1315
1375
|
},
|
|
1316
1376
|
renderErrorPage: async ({ path: e, headers: n, flash: r }) => {
|
|
1317
|
-
let { pathname: i } = be(e), a =
|
|
1377
|
+
let { pathname: i } = be(e), a = Ft(i, W);
|
|
1318
1378
|
if (!a) return;
|
|
1319
|
-
let o = await
|
|
1320
|
-
provider:
|
|
1379
|
+
let o = await Rt({
|
|
1380
|
+
provider: G,
|
|
1321
1381
|
root: t,
|
|
1322
1382
|
route: a.route,
|
|
1323
1383
|
params: a.params,
|
|
@@ -1335,19 +1395,19 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1335
1395
|
});
|
|
1336
1396
|
return o !== null && "html" in o ? o.html : void 0;
|
|
1337
1397
|
}
|
|
1338
|
-
}),
|
|
1398
|
+
}), Xt = ot(f), Zt = dt(async (e, t) => {
|
|
1339
1399
|
let n = e.url ?? "/", i = Date.now(), a = {};
|
|
1340
1400
|
for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (a[t.toLowerCase()] = Array.isArray(n) ? n.join(", ") : n);
|
|
1341
|
-
let o = n.split("?")[0] ?? "/", s = (e.method ?? "GET").toUpperCase(), c = he(
|
|
1401
|
+
let o = n.split("?")[0] ?? "/", s = (e.method ?? "GET").toUpperCase(), c = he(Jt, o);
|
|
1342
1402
|
if (c !== void 0) {
|
|
1343
|
-
let e = await y(
|
|
1403
|
+
let e = await y(Jt, c);
|
|
1344
1404
|
t.writeHead(e.status, {
|
|
1345
1405
|
"content-type": "text/plain",
|
|
1346
1406
|
"cache-control": "no-store"
|
|
1347
1407
|
}), t.end(e.body);
|
|
1348
1408
|
return;
|
|
1349
1409
|
}
|
|
1350
|
-
if (!(o.startsWith(x) && await
|
|
1410
|
+
if (!(o.startsWith(x) && await Yt(e, t))) {
|
|
1351
1411
|
if (s === "OPTIONS" && o.startsWith("/_voltro/inspect")) {
|
|
1352
1412
|
let e = Oe(a.origin);
|
|
1353
1413
|
t.writeHead(204, e), t.end();
|
|
@@ -1475,8 +1535,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1475
1535
|
return;
|
|
1476
1536
|
}
|
|
1477
1537
|
try {
|
|
1478
|
-
let e = await
|
|
1479
|
-
if (e.stream ? e.stream(t) :
|
|
1538
|
+
let e = await Kt(n, a);
|
|
1539
|
+
if (e.stream ? e.stream(t) : gt({
|
|
1480
1540
|
response: t,
|
|
1481
1541
|
status: e.status,
|
|
1482
1542
|
headers: e.headers,
|
|
@@ -1492,8 +1552,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1492
1552
|
renderedBy: e.headers["x-voltro-rendered-by"],
|
|
1493
1553
|
cache: e.headers["x-voltro-cache"]
|
|
1494
1554
|
});
|
|
1495
|
-
let n =
|
|
1496
|
-
|
|
1555
|
+
let n = Ft(o, W)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
|
|
1556
|
+
Vt.record({
|
|
1497
1557
|
kind: "request",
|
|
1498
1558
|
tag: n,
|
|
1499
1559
|
durationMs: t,
|
|
@@ -1505,10 +1565,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1505
1565
|
}
|
|
1506
1566
|
}
|
|
1507
1567
|
});
|
|
1508
|
-
await new Promise((e) =>
|
|
1568
|
+
await new Promise((e) => Zt.listen(_, "::", e)), qt = !0, n.mark("ready"), it(Zt, Xt), J.info(`listening on http://localhost:${_}`, {
|
|
1509
1569
|
port: _,
|
|
1510
1570
|
distDir: b
|
|
1511
|
-
}),
|
|
1571
|
+
}), J.debug("dynamic + ssr/isr routes resolve via SSR loader");
|
|
1512
1572
|
try {
|
|
1513
1573
|
await We({
|
|
1514
1574
|
id: T,
|
|
@@ -1522,7 +1582,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1522
1582
|
let e = Se(t);
|
|
1523
1583
|
if (e) {
|
|
1524
1584
|
let t = await ye({ dashboardAppPath: e });
|
|
1525
|
-
t.status === "launched" ?
|
|
1585
|
+
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 });
|
|
1526
1586
|
}
|
|
1527
1587
|
setInterval(() => {
|
|
1528
1588
|
We({
|
|
@@ -1536,7 +1596,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1536
1596
|
}).catch(() => {});
|
|
1537
1597
|
}, 1e4).unref();
|
|
1538
1598
|
} catch (e) {
|
|
1539
|
-
|
|
1599
|
+
J.warn("runtime-registry write failed", {}, e);
|
|
1540
1600
|
}
|
|
1541
1601
|
et({
|
|
1542
1602
|
app: f.name ?? "unnamed",
|
|
@@ -1544,38 +1604,38 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
1544
1604
|
url: `http://localhost:${_}`,
|
|
1545
1605
|
bootMs: n.total()
|
|
1546
1606
|
});
|
|
1547
|
-
let
|
|
1548
|
-
|
|
1607
|
+
let Qt = n.phaseBreakdown();
|
|
1608
|
+
J.info(`start: ready in ${n.total()}ms`, {
|
|
1549
1609
|
bootMs: n.total(),
|
|
1550
|
-
...
|
|
1610
|
+
...Qt ? { phases: Qt } : {}
|
|
1551
1611
|
});
|
|
1552
|
-
let
|
|
1553
|
-
if (
|
|
1554
|
-
if (
|
|
1555
|
-
|
|
1612
|
+
let $t = !1, en = (e) => async () => {
|
|
1613
|
+
if (!$t) {
|
|
1614
|
+
if ($t = !0, J.info(`received ${e}, shutting down`, { signal: e }), Zt.close(), await It.stop(), Z) try {
|
|
1615
|
+
Z();
|
|
1556
1616
|
} catch {}
|
|
1557
|
-
if (
|
|
1558
|
-
await ct.runPromise(
|
|
1617
|
+
if ($) try {
|
|
1618
|
+
await ct.runPromise($.close());
|
|
1559
1619
|
} catch {}
|
|
1560
1620
|
if (V) try {
|
|
1561
1621
|
await V.end();
|
|
1562
1622
|
} catch {}
|
|
1563
|
-
await
|
|
1623
|
+
await G.close(), await Ke(T), await E.dispose(), process.exit(0);
|
|
1564
1624
|
}
|
|
1565
1625
|
};
|
|
1566
|
-
process.on("SIGINT",
|
|
1567
|
-
let
|
|
1568
|
-
if (
|
|
1626
|
+
process.on("SIGINT", en("SIGINT")), process.on("SIGTERM", en("SIGTERM"));
|
|
1627
|
+
let tn = process.ppid;
|
|
1628
|
+
if (tn > 1) {
|
|
1569
1629
|
let e = setInterval(() => {
|
|
1570
|
-
(process.ppid !==
|
|
1630
|
+
(process.ppid !== tn || process.ppid === 1) && (clearInterval(e), en("SIGTERM")());
|
|
1571
1631
|
}, 2e3);
|
|
1572
1632
|
e.unref();
|
|
1573
1633
|
}
|
|
1574
1634
|
return new Promise(() => {});
|
|
1575
|
-
},
|
|
1635
|
+
}, Vt = async (e) => {
|
|
1576
1636
|
rt("start"), t("start");
|
|
1577
|
-
let n = e.find((e) => !e.startsWith("-")) ?? process.cwd(), r = await
|
|
1578
|
-
return r.ran ? r.exitCode : (
|
|
1637
|
+
let n = e.find((e) => !e.startsWith("-")) ?? process.cwd(), r = await $(z(n));
|
|
1638
|
+
return r.ran ? r.exitCode : (J.error("no web app found at this path"), 1);
|
|
1579
1639
|
};
|
|
1580
1640
|
//#endregion
|
|
1581
|
-
export {
|
|
1641
|
+
export { $ as n, Vt as t };
|