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