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