@voltro/cli 0.11.3 → 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 +436 -0
- package/THIRD-PARTY-NOTICES.md +4 -4
- package/dist/{apiBuild-zFDv0u8y.js → apiBuild-GefjNI5d.js} +10 -9
- package/dist/apiBuild-s7swuEU5.js +2 -0
- package/dist/appModuleLoader-C9r9mxZt.js +19 -0
- package/dist/bin.js +24 -6
- package/dist/cliArgs-qdZSElM3.js +28 -0
- package/dist/{commands-BDGYrBhK.js → commands-BmNi1RgZ.js} +2580 -2493
- package/dist/dbCommand-Bt6BCG7g.js +2 -0
- package/dist/dbCommand-DNrkb58g.js +862 -0
- package/dist/dev-YHbHmeXt.js +3 -0
- package/dist/{dev-CGt0PP1f.js → dev-d0LjACxE.js} +1591 -2332
- package/dist/{devActivity-1WtIVyHc.js → devActivity-BhIu6ncs.js} +7 -1
- package/dist/devActivity.js +1 -1
- package/dist/index.d.ts +22 -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-Crl39M38.js → start-DKcQd52a.js} +348 -337
- 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/_manifest.json +1 -1
- package/templates/agent-docs/cli.md +266 -16
- package/templates/agent-docs/data.md +143 -1
- package/templates/agent-docs/database/misc.md +32 -0
- package/templates/agent-docs/database/querying.md +14 -1
- package/templates/agent-docs/internationalization.md +3 -8
- package/templates/agent-docs/reference.md +35 -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/agent-docs/workflows.md +31 -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/README.md +3 -1
- 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-BmPdmhgi.js +0 -2
- package/dist/dev-D_PGP9Kx.js +0 -2
- package/dist/inspectMetrics-SRtv8KDy.js +0 -4247
- package/dist/serveCommand-p6e5Ahgx.js +0 -1070
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
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";
|
|
8
|
+
import { snapshotMetricsSync as me } from "@voltro/runtime";
|
|
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
|
-
let t = e.channel ?? "framework_changes", n =
|
|
28
|
+
}, ge = (e) => {
|
|
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,
|
|
@@ -79,9 +80,9 @@ CREATE TABLE IF NOT EXISTS ${e} (
|
|
|
79
80
|
stale_while_revalidate_ms BIGINT NOT NULL DEFAULT 0
|
|
80
81
|
);
|
|
81
82
|
CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
82
|
-
`,
|
|
83
|
-
let t = e.tableName ??
|
|
84
|
-
i || (i = !0, await n.query(
|
|
83
|
+
`, ve = (e) => {
|
|
84
|
+
let t = e.tableName ?? K, { client: n } = e, r = /* @__PURE__ */ new Map(), i = !1, a = (async () => {
|
|
85
|
+
i || (i = !0, await n.query(_e(t)));
|
|
85
86
|
})().then(async () => {
|
|
86
87
|
let e = await n.query(`SELECT * FROM ${t}`);
|
|
87
88
|
for (let t of e.rows) r.set(t.cache_key, {
|
|
@@ -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 = I(
|
|
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
|
-
},
|
|
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("/");
|
|
@@ -181,8 +182,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
181
182
|
paramNames: t,
|
|
182
183
|
priority: n
|
|
183
184
|
};
|
|
184
|
-
},
|
|
185
|
-
let { regex: t, paramNames: n, priority: r } =
|
|
185
|
+
}, Se = (e) => e.map((e) => {
|
|
186
|
+
let { regex: t, paramNames: n, priority: r } = Q(e.pattern);
|
|
186
187
|
return {
|
|
187
188
|
pattern: e.pattern,
|
|
188
189
|
file: e.file,
|
|
@@ -191,7 +192,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
191
192
|
paramNames: n,
|
|
192
193
|
priority: r
|
|
193
194
|
};
|
|
194
|
-
}).slice().sort((e, t) => e.priority - t.priority),
|
|
195
|
+
}).slice().sort((e, t) => e.priority - t.priority), Ce = (e, t) => {
|
|
195
196
|
for (let n of t) {
|
|
196
197
|
let t = n.regex.exec(e);
|
|
197
198
|
if (!t) continue;
|
|
@@ -203,81 +204,81 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
203
204
|
};
|
|
204
205
|
}
|
|
205
206
|
return null;
|
|
206
|
-
},
|
|
207
|
-
let { provider: t, route: n, params: r, pathname:
|
|
208
|
-
if (
|
|
209
|
-
let
|
|
207
|
+
}, we = async (e) => {
|
|
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:
|
|
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:
|
|
220
|
-
headers:
|
|
221
|
-
query:
|
|
219
|
+
pathname: i,
|
|
220
|
+
signal: x.signal,
|
|
221
|
+
headers: c,
|
|
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:
|
|
227
|
-
headers:
|
|
228
|
-
query:
|
|
226
|
+
pathname: i,
|
|
227
|
+
signal: x.signal,
|
|
228
|
+
headers: c,
|
|
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
|
|
235
236
|
};
|
|
236
|
-
if (
|
|
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:
|
|
247
|
-
chain:
|
|
248
|
-
},
|
|
249
|
-
cookies:
|
|
250
|
-
headers:
|
|
251
|
-
url:
|
|
252
|
-
},
|
|
253
|
-
if (
|
|
254
|
-
let e =
|
|
255
|
-
helpers:
|
|
242
|
+
Component: f.default,
|
|
243
|
+
meta: f.meta,
|
|
244
|
+
loader: f.loader,
|
|
245
|
+
ErrorBoundary: f.ErrorBoundary,
|
|
246
|
+
Pending: f.Pending,
|
|
247
|
+
renderMode: p,
|
|
248
|
+
chain: te
|
|
249
|
+
}, E = {
|
|
250
|
+
cookies: w.parseCookieHeader(c.cookie),
|
|
251
|
+
headers: c,
|
|
252
|
+
url: l
|
|
253
|
+
}, D = w.i18n?.resolve(c.cookie, c["accept-language"]), O = f.interactive ?? "full";
|
|
254
|
+
if (g) {
|
|
255
|
+
let e = M({
|
|
256
|
+
helpers: w,
|
|
256
257
|
page: n.pattern,
|
|
257
|
-
interactive:
|
|
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
|
-
interactive:
|
|
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
300
|
let o = N({
|
|
300
|
-
helpers:
|
|
301
|
+
helpers: w,
|
|
301
302
|
prepared: e
|
|
302
303
|
});
|
|
303
304
|
return {
|
|
304
|
-
html:
|
|
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
|
|
310
|
-
helpers:
|
|
310
|
+
let k = m({
|
|
311
|
+
helpers: w,
|
|
311
312
|
page: n.pattern,
|
|
312
|
-
pathname:
|
|
313
|
-
renderMode:
|
|
314
|
-
interactive:
|
|
315
|
-
loaderData:
|
|
316
|
-
segmentLoaderData:
|
|
317
|
-
pageLoaderRan: typeof
|
|
318
|
-
}),
|
|
319
|
-
descriptor:
|
|
313
|
+
pathname: i,
|
|
314
|
+
renderMode: p,
|
|
315
|
+
interactive: O,
|
|
316
|
+
loaderData: b,
|
|
317
|
+
segmentLoaderData: y,
|
|
318
|
+
pageLoaderRan: typeof f.loader == "function"
|
|
319
|
+
}), A = {
|
|
320
|
+
descriptor: ne,
|
|
320
321
|
params: r,
|
|
321
|
-
pathname:
|
|
322
|
-
loaderData:
|
|
323
|
-
segmentLoaderData:
|
|
324
|
-
requestContext:
|
|
325
|
-
...
|
|
326
|
-
outerWrap:
|
|
327
|
-
locale:
|
|
322
|
+
pathname: i,
|
|
323
|
+
loaderData: k.loaderData,
|
|
324
|
+
segmentLoaderData: k.segmentLoaderData,
|
|
325
|
+
requestContext: E,
|
|
326
|
+
...D ? {
|
|
327
|
+
outerWrap: D.outerWrap,
|
|
328
|
+
locale: D.locale
|
|
328
329
|
} : {}
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
`<meta name="voltro-interactive" content="${
|
|
332
|
-
...
|
|
333
|
-
|
|
330
|
+
}, j = (e) => [
|
|
331
|
+
w.renderMetaToHtml(e),
|
|
332
|
+
`<meta name="voltro-interactive" content="${O}" />`,
|
|
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,
|
|
@@ -344,27 +345,27 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
344
345
|
"content-type": "text/html; charset=utf-8",
|
|
345
346
|
"x-voltro-rendered-by": "ssr"
|
|
346
347
|
},
|
|
347
|
-
headInjection:
|
|
348
|
-
render: (e) =>
|
|
349
|
-
...
|
|
348
|
+
headInjection: j,
|
|
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:
|
|
359
|
-
|
|
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
|
-
html:
|
|
363
|
-
renderMode:
|
|
364
|
-
revalidateMs:
|
|
363
|
+
html: P,
|
|
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() {
|
|
@@ -393,8 +394,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
393
394
|
},
|
|
394
395
|
async close() {}
|
|
395
396
|
};
|
|
396
|
-
},
|
|
397
|
-
let n = await
|
|
397
|
+
}, Ee = async (e, t) => {
|
|
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 = I(e, "src", "pages"),
|
|
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
|
-
},
|
|
432
|
-
let s =
|
|
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
|
+
}), _ = h.publicValues;
|
|
448
449
|
s.mark("config");
|
|
449
|
-
let y =
|
|
450
|
-
|
|
451
|
-
let
|
|
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
|
|
465
|
-
id:
|
|
466
|
-
name:
|
|
464
|
+
let D = m.name ?? "unnamed", M = l("web", D, S), N = r(D);
|
|
465
|
+
await w({
|
|
466
|
+
id: M,
|
|
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
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:
|
|
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);
|
|
482
483
|
s.mark("scan");
|
|
483
|
-
let
|
|
484
|
-
if (!
|
|
485
|
-
let
|
|
486
|
-
|
|
487
|
-
kind:
|
|
488
|
-
...
|
|
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" } : {}
|
|
489
490
|
}), s.mark("provider");
|
|
490
|
-
let
|
|
491
|
-
for (let e of
|
|
492
|
-
let t =
|
|
491
|
+
let be = J.getPageMeta?.(), Y = /* @__PURE__ */ new Map(), X = 0;
|
|
492
|
+
for (let e of U) {
|
|
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:
|
|
505
|
+
q.info(`registered ${Y.size} route(s)`, {
|
|
506
|
+
count: Y.size,
|
|
507
|
+
fromManifest: Y.size - X,
|
|
508
|
+
fromModules: X
|
|
508
509
|
}), s.mark("routes");
|
|
509
|
-
let
|
|
510
|
-
for (let e of
|
|
511
|
-
|
|
512
|
-
let
|
|
513
|
-
for (let [e, t] of
|
|
514
|
-
let
|
|
515
|
-
for (let e of
|
|
516
|
-
let
|
|
517
|
-
cache:
|
|
518
|
-
tableSubscriptionsByPattern:
|
|
519
|
-
regexByPattern:
|
|
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);
|
|
515
|
+
let De = /* @__PURE__ */ new Map();
|
|
516
|
+
for (let e of G) De.set(e.pattern, e.regex);
|
|
517
|
+
let Oe = ge({
|
|
518
|
+
cache: B,
|
|
519
|
+
tableSubscriptionsByPattern: $,
|
|
520
|
+
regexByPattern: De
|
|
520
521
|
});
|
|
521
|
-
await
|
|
522
|
-
let
|
|
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
|
-
}),
|
|
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
|
-
})),
|
|
545
|
+
})), Me = H ? "postgres" : "memory", Ne = {
|
|
545
546
|
kind: "web",
|
|
546
547
|
meta: {
|
|
547
|
-
name:
|
|
548
|
-
voltroVersion:
|
|
549
|
-
port:
|
|
548
|
+
name: m.name ?? "(unnamed)",
|
|
549
|
+
voltroVersion: ye,
|
|
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
|
|
@@ -556,10 +557,10 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
556
557
|
return t.url !== void 0 && (n.url = t.url), n;
|
|
557
558
|
})
|
|
558
559
|
},
|
|
559
|
-
routes:
|
|
560
|
-
dirs:
|
|
560
|
+
routes: Ae,
|
|
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,
|
|
@@ -570,16 +571,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
570
571
|
staleWhileRevalidateMs: n.staleWhileRevalidateMs
|
|
571
572
|
}));
|
|
572
573
|
return {
|
|
573
|
-
backend:
|
|
574
|
-
size:
|
|
574
|
+
backend: Me,
|
|
575
|
+
size: B.size(),
|
|
575
576
|
entries: t
|
|
576
577
|
};
|
|
577
578
|
},
|
|
578
|
-
metrics: () =>
|
|
579
|
-
},
|
|
580
|
-
let r =
|
|
581
|
-
if (
|
|
582
|
-
let e = await
|
|
579
|
+
metrics: () => me()
|
|
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;
|
|
@@ -622,55 +623,55 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
622
623
|
};
|
|
623
624
|
}
|
|
624
625
|
if (r === "/_voltro/inspect/logs") {
|
|
625
|
-
let
|
|
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
636
|
if (r !== "/_voltro/inspect/stream") {
|
|
636
|
-
let
|
|
637
|
-
if (
|
|
638
|
-
status:
|
|
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
653
|
if (/\.[a-z0-9]+$/i.test(r)) {
|
|
653
654
|
let e = I(T, r.replace(/^\//, ""));
|
|
654
|
-
if (await
|
|
655
|
-
let t = await
|
|
655
|
+
if (await Z(e)) {
|
|
656
|
+
let t = await V.readFile(e);
|
|
656
657
|
return {
|
|
657
658
|
status: 200,
|
|
658
|
-
headers: { "content-type":
|
|
659
|
+
headers: { "content-type": xe(e) },
|
|
659
660
|
body: t
|
|
660
661
|
};
|
|
661
662
|
}
|
|
662
663
|
}
|
|
663
|
-
let d =
|
|
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
|
|
670
|
-
...
|
|
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, {
|
|
671
|
+
...f?.tenantAware ? { tenant: p } : {},
|
|
671
672
|
...h ? { locale: h } : {}
|
|
672
|
-
}), _ =
|
|
673
|
-
if (_ &&
|
|
673
|
+
}), _ = B.get(g), v = Date.now();
|
|
674
|
+
if (_ && e(_, v)) return y.emit("cache", {
|
|
674
675
|
kind: "HIT",
|
|
675
676
|
key: g,
|
|
676
677
|
ageMs: v - _.renderedAt
|
|
@@ -685,39 +686,39 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
685
686
|
},
|
|
686
687
|
body: _.html
|
|
687
688
|
};
|
|
688
|
-
if (_ &&
|
|
689
|
+
if (_ && b(_, v)) return y.emit("cache", {
|
|
689
690
|
kind: "STALE",
|
|
690
691
|
key: g,
|
|
691
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
700
|
pathname: r,
|
|
700
|
-
dirs:
|
|
701
|
+
dirs: W,
|
|
701
702
|
shellTemplate: F,
|
|
702
|
-
requestHeaders:
|
|
703
|
+
requestHeaders: n,
|
|
703
704
|
apiOrigin: R,
|
|
704
|
-
apiName:
|
|
705
|
-
requestUrl:
|
|
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,
|
|
@@ -731,20 +732,20 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
731
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
740
|
pathname: r,
|
|
740
|
-
dirs:
|
|
741
|
+
dirs: W,
|
|
741
742
|
shellTemplate: F,
|
|
742
|
-
requestHeaders:
|
|
743
|
+
requestHeaders: n,
|
|
743
744
|
apiOrigin: R,
|
|
744
|
-
apiName:
|
|
745
|
-
requestUrl:
|
|
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",
|
|
@@ -752,16 +753,16 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
752
753
|
},
|
|
753
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
792
|
key: g,
|
|
792
|
-
revalidateMs:
|
|
793
|
-
swrMs:
|
|
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
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",
|
|
@@ -815,9 +816,9 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
815
816
|
body: "server error"
|
|
816
817
|
};
|
|
817
818
|
}
|
|
818
|
-
},
|
|
819
|
-
let n = e.url ?? "/", r = Date.now(),
|
|
820
|
-
for (let [t, n] of Object.entries(e.headers)) n !== void 0 && (
|
|
819
|
+
}, Fe = !1, Ie = he(async (e, t) => {
|
|
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);
|
|
821
822
|
let o = n.split("?")[0] ?? "/", s = (e.method ?? "GET").toUpperCase();
|
|
822
823
|
if (o === "/internal/liveness") {
|
|
823
824
|
t.writeHead(200, {
|
|
@@ -827,36 +828,41 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
827
828
|
return;
|
|
828
829
|
}
|
|
829
830
|
if (o === "/internal/readiness") {
|
|
830
|
-
let e =
|
|
831
|
+
let e = Fe ? 200 : 503;
|
|
831
832
|
t.writeHead(e, {
|
|
832
833
|
"content-type": "text/plain",
|
|
833
834
|
"cache-control": "no-store"
|
|
834
|
-
}), t.end(
|
|
835
|
+
}), t.end(Fe ? "ready" : "not-ready");
|
|
835
836
|
return;
|
|
836
837
|
}
|
|
837
838
|
if (s === "OPTIONS" && o.startsWith("/_voltro/inspect")) {
|
|
838
|
-
let e =
|
|
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
|
}
|
|
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");
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
848
853
|
try {
|
|
849
|
-
let n = { accept:
|
|
850
|
-
|
|
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}`);
|
|
851
856
|
let o;
|
|
852
857
|
if (s && s !== "GET" && s !== "HEAD") {
|
|
853
858
|
let t = [];
|
|
854
859
|
for await (let n of e) t.push(n);
|
|
855
860
|
o = Buffer.concat(t);
|
|
856
|
-
let r =
|
|
861
|
+
let r = a["content-type"];
|
|
857
862
|
r && (n["content-type"] = Array.isArray(r) ? r[0] : r);
|
|
858
863
|
}
|
|
859
864
|
let c = await fetch(r, {
|
|
865
|
+
redirect: "manual",
|
|
860
866
|
method: s,
|
|
861
867
|
headers: n,
|
|
862
868
|
...o ? { body: new Uint8Array(o.buffer, o.byteOffset, o.byteLength) } : {}
|
|
@@ -870,22 +876,27 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
870
876
|
}
|
|
871
877
|
return;
|
|
872
878
|
}
|
|
873
|
-
if (
|
|
879
|
+
if (m.name === "voltroDashboard" && o === "/api/dashboard/proxy/stream") {
|
|
874
880
|
let r = new URL(n, "http://x").searchParams.get("target");
|
|
875
881
|
if (!r) {
|
|
876
882
|
t.writeHead(400, { "content-type": "text/plain" }), t.end("missing ?target=");
|
|
877
883
|
return;
|
|
878
884
|
}
|
|
879
|
-
|
|
880
|
-
|
|
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");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
let i = new AbortController();
|
|
890
|
+
e.on("close", () => i.abort()), t.on("close", () => i.abort());
|
|
881
891
|
try {
|
|
882
|
-
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",
|
|
883
894
|
method: "GET",
|
|
884
895
|
headers: {
|
|
885
896
|
accept: "text/event-stream",
|
|
886
897
|
...e ? { authorization: Array.isArray(e) ? e[0] : e } : {}
|
|
887
898
|
},
|
|
888
|
-
signal:
|
|
899
|
+
signal: i.signal
|
|
889
900
|
});
|
|
890
901
|
if (!n.ok || !n.body) {
|
|
891
902
|
t.writeHead(n.status, { "content-type": "text/plain" }), t.end(`upstream ${n.status}`);
|
|
@@ -913,15 +924,15 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
913
924
|
return;
|
|
914
925
|
}
|
|
915
926
|
if (o === "/_voltro/inspect/stream") {
|
|
916
|
-
let n =
|
|
917
|
-
if (
|
|
927
|
+
let n = ue(a.origin);
|
|
928
|
+
if (se()) {
|
|
918
929
|
t.writeHead(503, {
|
|
919
930
|
"content-type": "application/json",
|
|
920
931
|
...n
|
|
921
932
|
}), t.end(JSON.stringify({ error: "inspect disabled (VOLTRO_INSPECT=off)" }));
|
|
922
933
|
return;
|
|
923
934
|
}
|
|
924
|
-
let r =
|
|
935
|
+
let r = P(a);
|
|
925
936
|
if (!r.ok) {
|
|
926
937
|
t.writeHead(401, {
|
|
927
938
|
"content-type": "application/json",
|
|
@@ -939,11 +950,11 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
939
950
|
"x-accel-buffering": "no",
|
|
940
951
|
...n
|
|
941
952
|
}), t.flushHeaders(), t.write("retry: 5000\n\n");
|
|
942
|
-
for (let e of y.buffered()) t.write(
|
|
953
|
+
for (let e of y.buffered()) t.write(d(e));
|
|
943
954
|
let o = y.subscribe((e) => {
|
|
944
|
-
t.write(
|
|
955
|
+
t.write(d(e));
|
|
945
956
|
}), s = setInterval(() => {
|
|
946
|
-
t.write(
|
|
957
|
+
t.write(i());
|
|
947
958
|
}, 15e3), c = () => {
|
|
948
959
|
clearInterval(s), o();
|
|
949
960
|
};
|
|
@@ -951,7 +962,7 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
951
962
|
return;
|
|
952
963
|
}
|
|
953
964
|
try {
|
|
954
|
-
let e = await
|
|
965
|
+
let e = await Pe(n, a);
|
|
955
966
|
if (e.stream ? e.stream(t) : (t.writeHead(e.status, e.headers), t.end(e.body)), !o.startsWith("/_voltro/inspect")) {
|
|
956
967
|
let t = Date.now() - r;
|
|
957
968
|
y.emit("request", {
|
|
@@ -961,8 +972,8 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
961
972
|
renderedBy: e.headers["x-voltro-rendered-by"],
|
|
962
973
|
cache: e.headers["x-voltro-cache"]
|
|
963
974
|
});
|
|
964
|
-
let n =
|
|
965
|
-
|
|
975
|
+
let n = Ce(o, G)?.route.pattern ?? (e.status === 404 ? "<unmatched>" : "<asset>");
|
|
976
|
+
ke.record({
|
|
966
977
|
kind: "request",
|
|
967
978
|
tag: n,
|
|
968
979
|
durationMs: t,
|
|
@@ -973,71 +984,71 @@ CREATE INDEX IF NOT EXISTS ${e}_servable_until_idx ON ${e} (servable_until);
|
|
|
973
984
|
t.writeHead(500, { "content-type": "text/plain" }), t.end(`internal error: ${e.message}`);
|
|
974
985
|
}
|
|
975
986
|
});
|
|
976
|
-
await new Promise((e) =>
|
|
977
|
-
port:
|
|
987
|
+
await new Promise((e) => Ie.listen(S, "::", e)), Fe = !0, s.mark("ready"), q.info(`listening on http://localhost:${S}`, {
|
|
988
|
+
port: S,
|
|
978
989
|
distDir: T
|
|
979
|
-
}),
|
|
990
|
+
}), q.debug("dynamic + ssr/isr routes resolve via SSR loader");
|
|
980
991
|
try {
|
|
981
|
-
await
|
|
982
|
-
id:
|
|
983
|
-
name:
|
|
992
|
+
await w({
|
|
993
|
+
id: M,
|
|
994
|
+
name: D,
|
|
984
995
|
kind: "web",
|
|
985
|
-
url: `http://localhost:${
|
|
996
|
+
url: `http://localhost:${S}`,
|
|
986
997
|
pid: process.pid,
|
|
987
998
|
status: "live",
|
|
988
999
|
logFile: N.path
|
|
989
1000
|
});
|
|
990
|
-
let e =
|
|
1001
|
+
let e = ae(o);
|
|
991
1002
|
if (e) {
|
|
992
|
-
let
|
|
993
|
-
|
|
1003
|
+
let t = await n({ dashboardAppPath: e });
|
|
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 });
|
|
994
1005
|
}
|
|
995
1006
|
setInterval(() => {
|
|
996
|
-
|
|
997
|
-
id:
|
|
998
|
-
name:
|
|
1007
|
+
w({
|
|
1008
|
+
id: M,
|
|
1009
|
+
name: D,
|
|
999
1010
|
kind: "web",
|
|
1000
|
-
url: `http://localhost:${
|
|
1011
|
+
url: `http://localhost:${S}`,
|
|
1001
1012
|
pid: process.pid,
|
|
1002
1013
|
status: "live",
|
|
1003
1014
|
logFile: N.path
|
|
1004
1015
|
}).catch(() => {});
|
|
1005
1016
|
}, 1e4).unref();
|
|
1006
1017
|
} catch (e) {
|
|
1007
|
-
|
|
1018
|
+
q.warn("runtime-registry write failed", {}, e);
|
|
1008
1019
|
}
|
|
1009
|
-
|
|
1010
|
-
app:
|
|
1020
|
+
pe({
|
|
1021
|
+
app: m.name ?? "unnamed",
|
|
1011
1022
|
kind: "web",
|
|
1012
|
-
url: `http://localhost:${
|
|
1023
|
+
url: `http://localhost:${S}`,
|
|
1013
1024
|
bootMs: s.total()
|
|
1014
1025
|
});
|
|
1015
|
-
let
|
|
1016
|
-
|
|
1026
|
+
let Le = s.phaseBreakdown();
|
|
1027
|
+
q.info(`start: ready in ${s.total()}ms`, {
|
|
1017
1028
|
bootMs: s.total(),
|
|
1018
|
-
...
|
|
1029
|
+
...Le ? { phases: Le } : {}
|
|
1019
1030
|
});
|
|
1020
|
-
let
|
|
1021
|
-
if (!
|
|
1022
|
-
if (
|
|
1023
|
-
await
|
|
1031
|
+
let Re = !1, ze = (e) => async () => {
|
|
1032
|
+
if (!Re) {
|
|
1033
|
+
if (Re = !0, q.info(`received ${e}, shutting down`, { signal: e }), Ie.close(), await Oe.stop(), H) try {
|
|
1034
|
+
await H.end();
|
|
1024
1035
|
} catch {}
|
|
1025
|
-
await
|
|
1036
|
+
await J.close(), await t(M), await N.dispose(), process.exit(0);
|
|
1026
1037
|
}
|
|
1027
1038
|
};
|
|
1028
|
-
process.on("SIGINT",
|
|
1029
|
-
let
|
|
1030
|
-
if (
|
|
1039
|
+
process.on("SIGINT", ze("SIGINT")), process.on("SIGTERM", ze("SIGTERM"));
|
|
1040
|
+
let Be = process.ppid;
|
|
1041
|
+
if (Be > 1) {
|
|
1031
1042
|
let e = setInterval(() => {
|
|
1032
|
-
(process.ppid !==
|
|
1043
|
+
(process.ppid !== Be || process.ppid === 1) && (clearInterval(e), ze("SIGTERM")());
|
|
1033
1044
|
}, 2e3);
|
|
1034
1045
|
e.unref();
|
|
1035
1046
|
}
|
|
1036
1047
|
return new Promise(() => {});
|
|
1037
|
-
},
|
|
1048
|
+
}, Oe = async (e) => {
|
|
1038
1049
|
process.env.NODE_ENV || (process.env.NODE_ENV = "production");
|
|
1039
|
-
let t = await
|
|
1040
|
-
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);
|
|
1041
1052
|
};
|
|
1042
1053
|
//#endregion
|
|
1043
|
-
export {
|
|
1054
|
+
export { Oe as t };
|