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