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