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