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