@elurjs/kit 2.4.4
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 +874 -0
- package/LICENSE +21 -0
- package/README.md +1330 -0
- package/bin/elur-kit.js +54 -0
- package/dist/lib/action/define.d.cts +72 -0
- package/dist/lib/action/define.d.ts +72 -0
- package/dist/lib/action/error-store.d.cts +29 -0
- package/dist/lib/action/error-store.d.ts +29 -0
- package/dist/lib/action/index.cjs +1 -0
- package/dist/lib/action/index.d.cts +64 -0
- package/dist/lib/action/index.d.ts +64 -0
- package/dist/lib/action/index.js +2 -0
- package/dist/lib/action/origin.d.cts +20 -0
- package/dist/lib/action/origin.d.ts +20 -0
- package/dist/lib/action/scan.d.cts +28 -0
- package/dist/lib/action/scan.d.ts +28 -0
- package/dist/lib/action/server.d.cts +30 -0
- package/dist/lib/action/server.d.ts +30 -0
- package/dist/lib/action-BB6N5yD-.js +111 -0
- package/dist/lib/action-BB6N5yD-.js.map +1 -0
- package/dist/lib/action-DZHihU3Z.cjs +2 -0
- package/dist/lib/action-DZHihU3Z.cjs.map +1 -0
- package/dist/lib/adapters/bun.cjs +30 -0
- package/dist/lib/adapters/bun.cjs.map +1 -0
- package/dist/lib/adapters/bun.d.cts +13 -0
- package/dist/lib/adapters/bun.d.ts +13 -0
- package/dist/lib/adapters/bun.js +65 -0
- package/dist/lib/adapters/bun.js.map +1 -0
- package/dist/lib/adapters/index.cjs +0 -0
- package/dist/lib/adapters/index.d.cts +30 -0
- package/dist/lib/adapters/index.d.ts +30 -0
- package/dist/lib/adapters/index.js +0 -0
- package/dist/lib/adapters/netlify.cjs +10 -0
- package/dist/lib/adapters/netlify.cjs.map +1 -0
- package/dist/lib/adapters/netlify.d.cts +13 -0
- package/dist/lib/adapters/netlify.d.ts +13 -0
- package/dist/lib/adapters/netlify.js +51 -0
- package/dist/lib/adapters/netlify.js.map +1 -0
- package/dist/lib/adapters/node.cjs +46 -0
- package/dist/lib/adapters/node.cjs.map +1 -0
- package/dist/lib/adapters/node.d.cts +13 -0
- package/dist/lib/adapters/node.d.ts +13 -0
- package/dist/lib/adapters/node.js +105 -0
- package/dist/lib/adapters/node.js.map +1 -0
- package/dist/lib/adapters/shared.d.cts +17 -0
- package/dist/lib/adapters/shared.d.ts +17 -0
- package/dist/lib/adapters/vercel.cjs +2 -0
- package/dist/lib/adapters/vercel.cjs.map +1 -0
- package/dist/lib/adapters/vercel.d.cts +9 -0
- package/dist/lib/adapters/vercel.d.ts +9 -0
- package/dist/lib/adapters/vercel.js +62 -0
- package/dist/lib/adapters/vercel.js.map +1 -0
- package/dist/lib/build/build.d.cts +83 -0
- package/dist/lib/build/build.d.ts +83 -0
- package/dist/lib/build/document-shell.d.cts +48 -0
- package/dist/lib/build/document-shell.d.ts +48 -0
- package/dist/lib/build/transform-source.d.cts +26 -0
- package/dist/lib/build/transform-source.d.ts +26 -0
- package/dist/lib/build/vite-build.d.cts +78 -0
- package/dist/lib/build/vite-build.d.ts +78 -0
- package/dist/lib/cache/policy.d.cts +25 -0
- package/dist/lib/cache/policy.d.ts +25 -0
- package/dist/lib/cache.d.cts +13 -0
- package/dist/lib/cache.d.ts +13 -0
- package/dist/lib/capabilities-C0TDQoMY.cjs +2 -0
- package/dist/lib/capabilities-C0TDQoMY.cjs.map +1 -0
- package/dist/lib/capabilities-C7txofmY.js +45 -0
- package/dist/lib/capabilities-C7txofmY.js.map +1 -0
- package/dist/lib/cli/commands.d.cts +15 -0
- package/dist/lib/cli/commands.d.ts +15 -0
- package/dist/lib/cli.cjs +5132 -0
- package/dist/lib/cli.cjs.map +1 -0
- package/dist/lib/cli.d.cts +30 -0
- package/dist/lib/cli.d.ts +30 -0
- package/dist/lib/cli.js +5131 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/collections-Dbc8aRLf.js +233 -0
- package/dist/lib/collections-Dbc8aRLf.js.map +1 -0
- package/dist/lib/collections-VDy5g9p8.cjs +13 -0
- package/dist/lib/collections-VDy5g9p8.cjs.map +1 -0
- package/dist/lib/config/index.cjs +2 -0
- package/dist/lib/config/index.cjs.map +1 -0
- package/dist/lib/config/index.d.cts +100 -0
- package/dist/lib/config/index.d.ts +100 -0
- package/dist/lib/config/index.js +122 -0
- package/dist/lib/config/index.js.map +1 -0
- package/dist/lib/content/collections.d.cts +69 -0
- package/dist/lib/content/collections.d.ts +69 -0
- package/dist/lib/content/frontmatter.d.cts +20 -0
- package/dist/lib/content/frontmatter.d.ts +20 -0
- package/dist/lib/content/index.cjs +2 -0
- package/dist/lib/content/index.cjs.map +1 -0
- package/dist/lib/content/index.d.cts +5 -0
- package/dist/lib/content/index.d.ts +5 -0
- package/dist/lib/content/index.js +28 -0
- package/dist/lib/content/index.js.map +1 -0
- package/dist/lib/content/markdown.d.cts +6 -0
- package/dist/lib/content/markdown.d.ts +6 -0
- package/dist/lib/content/raw.d.cts +9 -0
- package/dist/lib/content/raw.d.ts +9 -0
- package/dist/lib/content/schema.d.cts +14 -0
- package/dist/lib/content/schema.d.ts +14 -0
- package/dist/lib/errors-0X7pCFNU.cjs +2 -0
- package/dist/lib/errors-0X7pCFNU.cjs.map +1 -0
- package/dist/lib/errors-ePYxDlhU.js +70 -0
- package/dist/lib/errors-ePYxDlhU.js.map +1 -0
- package/dist/lib/errors.d.cts +84 -0
- package/dist/lib/errors.d.ts +84 -0
- package/dist/lib/hydrate-8a3TbQbs.cjs +2 -0
- package/dist/lib/hydrate-8a3TbQbs.cjs.map +1 -0
- package/dist/lib/hydrate-CgZGrTxF.js +126 -0
- package/dist/lib/hydrate-CgZGrTxF.js.map +1 -0
- package/dist/lib/image/index.cjs +2 -0
- package/dist/lib/image/index.cjs.map +1 -0
- package/dist/lib/image/index.d.cts +37 -0
- package/dist/lib/image/index.d.ts +37 -0
- package/dist/lib/image/index.js +49 -0
- package/dist/lib/image/index.js.map +1 -0
- package/dist/lib/image/pipeline.d.cts +37 -0
- package/dist/lib/image/pipeline.d.ts +37 -0
- package/dist/lib/image/registry.cjs +2 -0
- package/dist/lib/image/registry.cjs.map +1 -0
- package/dist/lib/image/registry.d.cts +26 -0
- package/dist/lib/image/registry.d.ts +26 -0
- package/dist/lib/image/registry.js +23 -0
- package/dist/lib/image/registry.js.map +1 -0
- package/dist/lib/image/service.cjs +2 -0
- package/dist/lib/image/service.cjs.map +1 -0
- package/dist/lib/image/service.d.cts +164 -0
- package/dist/lib/image/service.d.ts +164 -0
- package/dist/lib/image/service.js +308 -0
- package/dist/lib/image/service.js.map +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +38 -0
- package/dist/lib/index.d.ts +38 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/integrations/hooks.d.cts +58 -0
- package/dist/lib/integrations/hooks.d.ts +58 -0
- package/dist/lib/integrations/index.cjs +1 -0
- package/dist/lib/integrations/index.d.cts +18 -0
- package/dist/lib/integrations/index.d.ts +18 -0
- package/dist/lib/integrations/index.js +2 -0
- package/dist/lib/integrations-7dpN1RYB.js +35 -0
- package/dist/lib/integrations-7dpN1RYB.js.map +1 -0
- package/dist/lib/integrations-DDqfWjvH.cjs +2 -0
- package/dist/lib/integrations-DDqfWjvH.cjs.map +1 -0
- package/dist/lib/interpolation-plugin-Bt-KRKVY.js +365 -0
- package/dist/lib/interpolation-plugin-Bt-KRKVY.js.map +1 -0
- package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs +31 -0
- package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs.map +1 -0
- package/dist/lib/island/generate-entry.d.cts +27 -0
- package/dist/lib/island/generate-entry.d.ts +27 -0
- package/dist/lib/island/hydrate.d.cts +33 -0
- package/dist/lib/island/hydrate.d.ts +33 -0
- package/dist/lib/island/index.cjs +1 -0
- package/dist/lib/island/index.d.cts +2 -0
- package/dist/lib/island/index.d.ts +2 -0
- package/dist/lib/island/index.js +1 -0
- package/dist/lib/island/island.d.cts +32 -0
- package/dist/lib/island/island.d.ts +32 -0
- package/dist/lib/island/scan.d.cts +14 -0
- package/dist/lib/island/scan.d.ts +14 -0
- package/dist/lib/manifest/index.cjs +3 -0
- package/dist/lib/manifest/index.cjs.map +1 -0
- package/dist/lib/manifest/index.d.cts +18 -0
- package/dist/lib/manifest/index.d.ts +18 -0
- package/dist/lib/manifest/index.js +110 -0
- package/dist/lib/manifest/index.js.map +1 -0
- package/dist/lib/middleware/index.d.cts +56 -0
- package/dist/lib/middleware/index.d.ts +56 -0
- package/dist/lib/middleware/stream-boundary.d.cts +53 -0
- package/dist/lib/middleware/stream-boundary.d.ts +53 -0
- package/dist/lib/node-http-DRAUhO0c.js +617 -0
- package/dist/lib/node-http-DRAUhO0c.js.map +1 -0
- package/dist/lib/node-http-DhxguYyz.cjs +13 -0
- package/dist/lib/node-http-DhxguYyz.cjs.map +1 -0
- package/dist/lib/render/render-to-string.d.cts +12 -0
- package/dist/lib/render/render-to-string.d.ts +12 -0
- package/dist/lib/render/ssr-flag.d.cts +4 -0
- package/dist/lib/render/ssr-flag.d.ts +4 -0
- package/dist/lib/route-scanner-JY6YrNH5.cjs +2 -0
- package/dist/lib/route-scanner-JY6YrNH5.cjs.map +1 -0
- package/dist/lib/route-scanner-XcLId7lK.js +100 -0
- package/dist/lib/route-scanner-XcLId7lK.js.map +1 -0
- package/dist/lib/router/client.cjs +2 -0
- package/dist/lib/router/client.cjs.map +1 -0
- package/dist/lib/router/client.d.cts +47 -0
- package/dist/lib/router/client.d.ts +47 -0
- package/dist/lib/router/client.js +298 -0
- package/dist/lib/router/client.js.map +1 -0
- package/dist/lib/router/route-scanner.d.cts +50 -0
- package/dist/lib/router/route-scanner.d.ts +50 -0
- package/dist/lib/runtime/capabilities.d.cts +47 -0
- package/dist/lib/runtime/capabilities.d.ts +47 -0
- package/dist/lib/runtime/context.d.cts +140 -0
- package/dist/lib/runtime/context.d.ts +140 -0
- package/dist/lib/runtime/handler.d.cts +39 -0
- package/dist/lib/runtime/handler.d.ts +39 -0
- package/dist/lib/runtime/index.cjs +1 -0
- package/dist/lib/runtime/index.d.cts +6 -0
- package/dist/lib/runtime/index.d.ts +6 -0
- package/dist/lib/runtime/index.js +4 -0
- package/dist/lib/runtime/node-http.d.cts +2 -0
- package/dist/lib/runtime/node-http.d.ts +2 -0
- package/dist/lib/runtime/security-headers.d.cts +13 -0
- package/dist/lib/runtime/security-headers.d.ts +13 -0
- package/dist/lib/runtime/static.d.cts +1 -0
- package/dist/lib/runtime/static.d.ts +1 -0
- package/dist/lib/runtime-CrH5X-Es.cjs +2 -0
- package/dist/lib/runtime-CrH5X-Es.cjs.map +1 -0
- package/dist/lib/runtime-Ds2-wH6P.js +520 -0
- package/dist/lib/runtime-Ds2-wH6P.js.map +1 -0
- package/dist/lib/scan-CY2O9ty6.cjs +2 -0
- package/dist/lib/scan-CY2O9ty6.cjs.map +1 -0
- package/dist/lib/scan-CjMUgnsd.js +22 -0
- package/dist/lib/scan-CjMUgnsd.js.map +1 -0
- package/dist/lib/scan-DXzYtmRQ.cjs +2 -0
- package/dist/lib/scan-DXzYtmRQ.cjs.map +1 -0
- package/dist/lib/scan-Dn93ycXG.js +33 -0
- package/dist/lib/scan-Dn93ycXG.js.map +1 -0
- package/dist/lib/seo/index.cjs +10 -0
- package/dist/lib/seo/index.cjs.map +1 -0
- package/dist/lib/seo/index.d.cts +99 -0
- package/dist/lib/seo/index.d.ts +99 -0
- package/dist/lib/seo/index.js +41 -0
- package/dist/lib/seo/index.js.map +1 -0
- package/dist/lib/shared-CfqOLsKj.cjs +183 -0
- package/dist/lib/shared-CfqOLsKj.cjs.map +1 -0
- package/dist/lib/shared-DU3iQgr7.js +236 -0
- package/dist/lib/shared-DU3iQgr7.js.map +1 -0
- package/dist/lib/ssr/match.d.cts +25 -0
- package/dist/lib/ssr/match.d.ts +25 -0
- package/dist/lib/ssr/render.d.cts +62 -0
- package/dist/lib/ssr/render.d.ts +62 -0
- package/dist/lib/ssr/server.d.cts +41 -0
- package/dist/lib/ssr/server.d.ts +41 -0
- package/dist/lib/ssr/stream.d.cts +48 -0
- package/dist/lib/ssr/stream.d.ts +48 -0
- package/dist/lib/stream-CpOG052V.js +104 -0
- package/dist/lib/stream-CpOG052V.js.map +1 -0
- package/dist/lib/stream-DmZjYqHm.cjs +17 -0
- package/dist/lib/stream-DmZjYqHm.cjs.map +1 -0
- package/dist/lib/types.d.cts +97 -0
- package/dist/lib/types.d.ts +97 -0
- package/dist/lib/vite/index.cjs +2 -0
- package/dist/lib/vite/index.cjs.map +1 -0
- package/dist/lib/vite/index.d.cts +41 -0
- package/dist/lib/vite/index.d.ts +41 -0
- package/dist/lib/vite/index.js +229 -0
- package/dist/lib/vite/index.js.map +1 -0
- package/dist/lib/vite/interpolation-plugin.d.cts +64 -0
- package/dist/lib/vite/interpolation-plugin.d.ts +64 -0
- package/package.json +292 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { copyFile as e, mkdir as t, readdir as n, writeFile as r } from "node:fs/promises";
|
|
2
|
+
import { dirname as i, join as a, relative as o } from "node:path";
|
|
3
|
+
import { existsSync as s } from "node:fs";
|
|
4
|
+
//#region src/adapters/shared.ts
|
|
5
|
+
async function c(r, i) {
|
|
6
|
+
await t(i, { recursive: !0 });
|
|
7
|
+
let o = await n(r, { withFileTypes: !0 });
|
|
8
|
+
for (let t of o) {
|
|
9
|
+
let n = a(r, t.name), o = a(i, t.name);
|
|
10
|
+
t.isDirectory() ? await c(n, o) : await e(n, o);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function l(e, t, n) {
|
|
14
|
+
t.add(e.pagePath), e.dataPath && t.add(e.dataPath), e.loadingPath && t.add(e.loadingPath);
|
|
15
|
+
for (let n of e.layouts) {
|
|
16
|
+
t.add(n);
|
|
17
|
+
let e = n.replace(/layout\.ts$/, "layout.data.ts");
|
|
18
|
+
e !== n && s(e) && t.add(e);
|
|
19
|
+
}
|
|
20
|
+
if (e.actionPath) {
|
|
21
|
+
t.add(e.actionPath);
|
|
22
|
+
let r = n.get(e.path);
|
|
23
|
+
r || (r = /* @__PURE__ */ new Set(), n.set(e.path, r)), r.add(e.actionPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function u(e, t, n) {
|
|
27
|
+
let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Map();
|
|
28
|
+
for (let t of e.pages) l(t, r, i);
|
|
29
|
+
e.error404 && l(e.error404, r, i), e.error500 && l(e.error500, r, i);
|
|
30
|
+
for (let t of e.api) r.add(t.routePath);
|
|
31
|
+
let a = Array.from(r), o = new Map(a.map((e, t) => [e, t])), s = a.map((e, t) => {
|
|
32
|
+
let r = d(n, e);
|
|
33
|
+
return `import * as m_${t} from ${JSON.stringify(r)};`;
|
|
34
|
+
}).join("\n"), c = (e) => `{
|
|
35
|
+
path: ${JSON.stringify(e.path)},
|
|
36
|
+
pagePath: ${JSON.stringify(e.pagePath)},
|
|
37
|
+
dataPath: ${JSON.stringify(e.dataPath ?? null)},
|
|
38
|
+
actionPath: ${JSON.stringify(e.actionPath ?? null)},
|
|
39
|
+
loadingPath: ${JSON.stringify(e.loadingPath ?? null)},
|
|
40
|
+
layouts: ${JSON.stringify(e.layouts)},
|
|
41
|
+
params: ${JSON.stringify(e.params)},
|
|
42
|
+
}`, u = e.pages.map(c).join(",\n"), f = e.api.map((e) => {
|
|
43
|
+
let t = o.get(e.routePath);
|
|
44
|
+
return ` { path: ${JSON.stringify(e.path)}, routePath: m_${t} },`;
|
|
45
|
+
}).join("\n"), p = Array.from(i.entries()).map(([e, t]) => {
|
|
46
|
+
let n = Array.from(t).map((e) => {
|
|
47
|
+
let t = o.get(e);
|
|
48
|
+
return ` [${JSON.stringify(e)}, m_${t}],`;
|
|
49
|
+
}).join("\n");
|
|
50
|
+
return ` [${JSON.stringify(e)}, new Map([\n${n}\n ])],`;
|
|
51
|
+
}).join("\n"), m = {};
|
|
52
|
+
for (let t of e.pages) {
|
|
53
|
+
if (!t.actionPath) continue;
|
|
54
|
+
let e = await import(t.actionPath), n = [];
|
|
55
|
+
for (let [t, r] of Object.entries(e)) t !== "default" && typeof r == "function" && n.push(t);
|
|
56
|
+
n.length > 0 && (m[t.path] = n);
|
|
57
|
+
}
|
|
58
|
+
return `// AUTO-GENERATED by @elurjs/kit. Do not edit.
|
|
59
|
+
import { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from "@elurjs/kit";
|
|
60
|
+
${s}
|
|
61
|
+
|
|
62
|
+
const registry = new Map<string, unknown>([
|
|
63
|
+
${a.map((e, t) => ` [${JSON.stringify(e)}, m_${t}],`).join("\n")}
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const pages = [
|
|
67
|
+
${u},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const apiRoutes = [
|
|
71
|
+
${f}
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const actionModules = new Map<string, Map<string, unknown>>([
|
|
75
|
+
${p}
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
const actions = ${JSON.stringify(m)};
|
|
79
|
+
|
|
80
|
+
const routes = {
|
|
81
|
+
pages,
|
|
82
|
+
api: apiRoutes,
|
|
83
|
+
error404: ${e.error404 ? c(e.error404) : "undefined"},
|
|
84
|
+
error500: ${e.error500 ? c(e.error500) : "undefined"},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const clientEntry = ${JSON.stringify(t.clientEntry)};
|
|
88
|
+
const lang = ${JSON.stringify(t.lang)};
|
|
89
|
+
|
|
90
|
+
function loadModule(path: string) {
|
|
91
|
+
const mod = registry.get(path);
|
|
92
|
+
if (mod) return mod;
|
|
93
|
+
throw new Error(\`Module not found in registry: \${path}\`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function resolveAction(name: string, page?: string) {
|
|
97
|
+
// Match concrete page paths (e.g. /movies/inception) to their route pattern
|
|
98
|
+
// (/movies/:slug) so actions on dynamic routes resolve by scope.
|
|
99
|
+
let pageKey: string | undefined;
|
|
100
|
+
if (page) {
|
|
101
|
+
pageKey = routes.pages.some((route) => route.path === page)
|
|
102
|
+
? page
|
|
103
|
+
: (matchRoute(page, routes.pages)?.route.path ?? page);
|
|
104
|
+
}
|
|
105
|
+
const pageModules = pageKey ? actionModules.get(pageKey) : undefined;
|
|
106
|
+
const candidates = pageModules ? [...pageModules.values()] : [];
|
|
107
|
+
if (!pageModules) {
|
|
108
|
+
for (const mods of actionModules.values()) {
|
|
109
|
+
for (const mod of mods.values()) {
|
|
110
|
+
const action = (mod as Record<string, unknown>)[name];
|
|
111
|
+
if (typeof action === "function") return action;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const mod of candidates) {
|
|
116
|
+
const action = (mod as Record<string, unknown>)[name];
|
|
117
|
+
if (typeof action === "function") {
|
|
118
|
+
return action as (...args: unknown[]) => unknown;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default async function handler(request: Request): Promise<Response> {
|
|
125
|
+
const url = new URL(request.url);
|
|
126
|
+
|
|
127
|
+
if (url.pathname === "/__elur-js/actions") {
|
|
128
|
+
return handleActionRequest(request, resolveAction);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Render endpoint used by the SPA router and streaming boundaries.
|
|
132
|
+
if (url.pathname === "/__elur-js/render") {
|
|
133
|
+
const page = url.searchParams.get("page") ?? "/";
|
|
134
|
+
const search = url.searchParams.get("search") ?? "";
|
|
135
|
+
const wantsJson = (request.headers.get("Accept") ?? "").includes("application/json");
|
|
136
|
+
try {
|
|
137
|
+
const match = matchRoute(page, routes.pages);
|
|
138
|
+
if (!match) throw new Error(\`No route found for \${page}\`);
|
|
139
|
+
const { html } = await renderPage({
|
|
140
|
+
route: match.route,
|
|
141
|
+
params: match.params,
|
|
142
|
+
searchParams: new URLSearchParams(search),
|
|
143
|
+
config: { lang, clientEntry },
|
|
144
|
+
importer: loadModule,
|
|
145
|
+
actions,
|
|
146
|
+
request,
|
|
147
|
+
});
|
|
148
|
+
const bodyStart = html.indexOf('<div id="app">');
|
|
149
|
+
const bodyEnd = html.lastIndexOf("</div>");
|
|
150
|
+
const body = bodyStart >= 0 && bodyEnd > bodyStart
|
|
151
|
+
? html.slice(bodyStart + 13, bodyEnd).trim()
|
|
152
|
+
: html;
|
|
153
|
+
const titleStart = html.indexOf("<title>");
|
|
154
|
+
const titleEnd = html.indexOf("</title>");
|
|
155
|
+
const title = titleStart >= 0 && titleEnd > titleStart
|
|
156
|
+
? html.slice(titleStart + 7, titleEnd)
|
|
157
|
+
: "";
|
|
158
|
+
if (wantsJson) {
|
|
159
|
+
return new Response(JSON.stringify({ title, body }), {
|
|
160
|
+
status: 200,
|
|
161
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return new Response(body, {
|
|
165
|
+
status: 200,
|
|
166
|
+
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
167
|
+
});
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if ((err as { name?: string }).name === "RouteNotFoundError") {
|
|
170
|
+
return new Response("Not Found", {
|
|
171
|
+
status: 404,
|
|
172
|
+
headers: { "Content-Type": "text/plain" },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
console.error("[elur-kit] render endpoint error:", err);
|
|
176
|
+
return new Response("Internal Server Error", {
|
|
177
|
+
status: 500,
|
|
178
|
+
headers: { "Content-Type": "text/plain" },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const apiMatch = matchApiRoute(url.pathname, apiRoutes);
|
|
184
|
+
if (apiMatch) {
|
|
185
|
+
const mod = apiMatch.route.routePath as Record<
|
|
186
|
+
string,
|
|
187
|
+
(request: Request, context?: { params: Record<string, string | string[]> }) => unknown
|
|
188
|
+
>;
|
|
189
|
+
const handler = mod[request.method ?? "GET"];
|
|
190
|
+
if (typeof handler !== "function") {
|
|
191
|
+
return new Response("Method not allowed: " + request.method, { status: 405, headers: { "Content-Type": "text/plain" } });
|
|
192
|
+
}
|
|
193
|
+
return (await handler(request, { params: apiMatch.params })) as Response;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const match = matchRoute(url.pathname, routes.pages);
|
|
197
|
+
if (!match) {
|
|
198
|
+
const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });
|
|
199
|
+
if (errorResult) {
|
|
200
|
+
return new Response(errorResult.html, { status: errorResult.status, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
201
|
+
}
|
|
202
|
+
return new Response("Not Found", { status: 404, headers: { "Content-Type": "text/plain" } });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const { html } = await renderPage({
|
|
207
|
+
route: match.route,
|
|
208
|
+
params: match.params,
|
|
209
|
+
searchParams: new URLSearchParams(url.search),
|
|
210
|
+
config: { lang, clientEntry },
|
|
211
|
+
importer: loadModule,
|
|
212
|
+
actions,
|
|
213
|
+
request,
|
|
214
|
+
});
|
|
215
|
+
return new Response(html, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
216
|
+
} catch (err) {
|
|
217
|
+
console.error("[elur-kit] SSR render error:", err);
|
|
218
|
+
const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });
|
|
219
|
+
if (errorResult) {
|
|
220
|
+
return new Response(errorResult.html, { status: errorResult.status, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
221
|
+
}
|
|
222
|
+
return new Response("Internal Server Error", { status: 500, headers: { "Content-Type": "text/plain; charset=utf-8" } });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
function d(e, t) {
|
|
228
|
+
return o(e, t).split("\\").join("/");
|
|
229
|
+
}
|
|
230
|
+
async function f(e, t, n) {
|
|
231
|
+
await r(e, await u(t, n, i(e)), "utf8");
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
export { f as n, c as t };
|
|
235
|
+
|
|
236
|
+
//# sourceMappingURL=shared-DU3iQgr7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-DU3iQgr7.js","names":[],"sources":["../../src/adapters/shared.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, copyFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\nimport type { AdapterOptions } from \"./index.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/**\n * Shared helper: copy a directory recursively.\n */\nexport async function copyStatic(from: string, to: string): Promise<void> {\n await mkdir(to, { recursive: true });\n const entries = await readdir(from, { withFileTypes: true });\n for (const entry of entries) {\n const src = join(from, entry.name);\n const dest = join(to, entry.name);\n if (entry.isDirectory()) {\n await copyStatic(src, dest);\n } else {\n await copyFile(src, dest);\n }\n }\n}\n\n/** Adds every module the SSR runtime may import for a page to the registry. */\nfunction collectPageModules(\n page: PageRoute,\n moduleSet: Set<string>,\n actionPathsByPage: Map<string, Set<string>>,\n): void {\n moduleSet.add(page.pagePath);\n if (page.dataPath) moduleSet.add(page.dataPath);\n if (page.loadingPath) moduleSet.add(page.loadingPath);\n for (const layout of page.layouts) {\n moduleSet.add(layout);\n const layoutDataPath = layout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (layoutDataPath !== layout && existsSync(layoutDataPath)) {\n moduleSet.add(layoutDataPath);\n }\n }\n if (page.actionPath) {\n moduleSet.add(page.actionPath);\n let set = actionPathsByPage.get(page.path);\n if (!set) {\n set = new Set<string>();\n actionPathsByPage.set(page.path, set);\n }\n set.add(page.actionPath);\n }\n}\n\n/**\n * Build a self-contained SSR entry file for a platform adapter.\n * The generated module exports a default `handler(request: Request): Response`\n * and embeds the full route table plus a registry of all page/layout/data/\n * action modules so the runtime never touches the file system.\n */\nexport async function buildSsrEntry(\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n entryDir: string,\n): Promise<string> {\n // Collect all module paths that the SSR runtime may need to import.\n const moduleSet = new Set<string>();\n const actionPathsByPage = new Map<string, Set<string>>();\n for (const page of routes.pages) {\n collectPageModules(page, moduleSet, actionPathsByPage);\n }\n if (routes.error404) collectPageModules(routes.error404, moduleSet, actionPathsByPage);\n if (routes.error500) collectPageModules(routes.error500, moduleSet, actionPathsByPage);\n for (const api of routes.api) {\n moduleSet.add(api.routePath);\n }\n const modules = Array.from(moduleSet);\n const moduleIndex = new Map(modules.map((path, index) => [path, index]));\n\n const imports = modules\n .map((path, index) => {\n const rel = relativeToPosix(entryDir, path);\n return `import * as m_${index} from ${JSON.stringify(rel)};`;\n })\n .join(\"\\n\");\n\n const renderPageRecord = (page: PageRoute): string => `{\n path: ${JSON.stringify(page.path)},\n pagePath: ${JSON.stringify(page.pagePath)},\n dataPath: ${JSON.stringify(page.dataPath ?? null)},\n actionPath: ${JSON.stringify(page.actionPath ?? null)},\n loadingPath: ${JSON.stringify(page.loadingPath ?? null)},\n layouts: ${JSON.stringify(page.layouts)},\n params: ${JSON.stringify(page.params)},\n }`;\n\n const pages = routes.pages.map(renderPageRecord).join(\",\\n\");\n\n const apiRoutes = routes.api\n .map((api) => {\n const index = moduleIndex.get(api.routePath);\n return ` { path: ${JSON.stringify(api.path)}, routePath: m_${index} },`;\n })\n .join(\"\\n\");\n\n const actionModules = Array.from(actionPathsByPage.entries())\n .map(([pagePath, paths]) => {\n const entries = Array.from(paths)\n .map((path) => {\n const index = moduleIndex.get(path);\n return ` [${JSON.stringify(path)}, m_${index}],`;\n })\n .join(\"\\n\");\n return ` [${JSON.stringify(pagePath)}, new Map([\\n${entries}\\n ])],`;\n })\n .join(\"\\n\");\n\n const actionsRegistry: Record<string, string[]> = {};\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const mod = (await import(page.actionPath)) as Record<string, unknown>;\n const names: string[] = [];\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n names.push(name);\n }\n }\n if (names.length > 0) {\n actionsRegistry[page.path] = names;\n }\n }\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from \"@elurjs/kit\";\n${imports}\n\nconst registry = new Map<string, unknown>([\n${modules.map((path, index) => ` [${JSON.stringify(path)}, m_${index}],`).join(\"\\n\")}\n]);\n\nconst pages = [\n${pages},\n];\n\nconst apiRoutes = [\n${apiRoutes}\n];\n\nconst actionModules = new Map<string, Map<string, unknown>>([\n${actionModules}\n]);\n\nconst actions = ${JSON.stringify(actionsRegistry)};\n\nconst routes = {\n pages,\n api: apiRoutes,\n error404: ${routes.error404 ? renderPageRecord(routes.error404) : \"undefined\"},\n error500: ${routes.error500 ? renderPageRecord(routes.error500) : \"undefined\"},\n};\n\nconst clientEntry = ${JSON.stringify(options.clientEntry)};\nconst lang = ${JSON.stringify(options.lang)};\n\nfunction loadModule(path: string) {\n const mod = registry.get(path);\n if (mod) return mod;\n throw new Error(\\`Module not found in registry: \\${path}\\`);\n}\n\nasync function resolveAction(name: string, page?: string) {\n // Match concrete page paths (e.g. /movies/inception) to their route pattern\n // (/movies/:slug) so actions on dynamic routes resolve by scope.\n let pageKey: string | undefined;\n if (page) {\n pageKey = routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page);\n }\n const pageModules = pageKey ? actionModules.get(pageKey) : undefined;\n const candidates = pageModules ? [...pageModules.values()] : [];\n if (!pageModules) {\n for (const mods of actionModules.values()) {\n for (const mod of mods.values()) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") return action;\n }\n }\n }\n for (const mod of candidates) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") {\n return action as (...args: unknown[]) => unknown;\n }\n }\n return undefined;\n}\n\nexport default async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (url.pathname === \"/__elur-js/actions\") {\n return handleActionRequest(request, resolveAction);\n }\n\n // Render endpoint used by the SPA router and streaming boundaries.\n if (url.pathname === \"/__elur-js/render\") {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n try {\n const match = matchRoute(page, routes.pages);\n if (!match) throw new Error(\\`No route found for \\${page}\\`);\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n const bodyStart = html.indexOf('<div id=\"app\">');\n const bodyEnd = html.lastIndexOf(\"</div>\");\n const body = bodyStart >= 0 && bodyEnd > bodyStart\n ? html.slice(bodyStart + 13, bodyEnd).trim()\n : html;\n const titleStart = html.indexOf(\"<title>\");\n const titleEnd = html.indexOf(\"</title>\");\n const title = titleStart >= 0 && titleEnd > titleStart\n ? html.slice(titleStart + 7, titleEnd)\n : \"\";\n if (wantsJson) {\n return new Response(JSON.stringify({ title, body }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n });\n }\n return new Response(body, {\n status: 200,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (err) {\n if ((err as { name?: string }).name === \"RouteNotFoundError\") {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n console.error(\"[elur-kit] render endpoint error:\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n }\n\n const apiMatch = matchApiRoute(url.pathname, apiRoutes);\n if (apiMatch) {\n const mod = apiMatch.route.routePath as Record<\n string,\n (request: Request, context?: { params: Record<string, string | string[]> }) => unknown\n >;\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") {\n return new Response(\"Method not allowed: \" + request.method, { status: 405, headers: { \"Content-Type\": \"text/plain\" } });\n }\n return (await handler(request, { params: apiMatch.params })) as Response;\n }\n\n const match = matchRoute(url.pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Not Found\", { status: 404, headers: { \"Content-Type\": \"text/plain\" } });\n }\n\n try {\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(url.search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n return new Response(html, { status: 200, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n } catch (err) {\n console.error(\"[elur-kit] SSR render error:\", err);\n const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Internal Server Error\", { status: 500, headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n}\n`;\n}\n\nfunction relativeToPosix(from: string, to: string): string {\n return relative(from, to).split(\"\\\\\").join(\"/\");\n}\n\n/**\n * Write a generated SSR entry file for an adapter.\n */\nexport async function writeSsrEntry(\n entryPath: string,\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n): Promise<void> {\n await writeFile(\n entryPath,\n await buildSsrEntry(routes, options, dirname(entryPath)),\n \"utf8\",\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ApiRoute, PageRoute } from "../router/route-scanner.js";
|
|
2
|
+
export interface MatchResult {
|
|
3
|
+
route: PageRoute;
|
|
4
|
+
params: Record<string, string | string[]>;
|
|
5
|
+
searchParams: URLSearchParams;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Match a request pathname against a list of page routes.
|
|
9
|
+
*
|
|
10
|
+
* Routes are sorted by specificity (static > dynamic > catch-all) before
|
|
11
|
+
* matching, so `/about` wins over `/:slug` even if the catch-all appears first.
|
|
12
|
+
*
|
|
13
|
+
* URL segments are safely decoded (plan §11.1, runtime-security §10).
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchRoute(pathname: string, routes: PageRoute[]): MatchResult | undefined;
|
|
16
|
+
export interface ApiMatchResult<T = ApiRoute> {
|
|
17
|
+
route: T;
|
|
18
|
+
params: Record<string, string | string[]>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Match a request pathname against a list of API routes.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchApiRoute<T extends {
|
|
24
|
+
path: string;
|
|
25
|
+
}>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ApiRoute, PageRoute } from "../router/route-scanner.js";
|
|
2
|
+
export interface MatchResult {
|
|
3
|
+
route: PageRoute;
|
|
4
|
+
params: Record<string, string | string[]>;
|
|
5
|
+
searchParams: URLSearchParams;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Match a request pathname against a list of page routes.
|
|
9
|
+
*
|
|
10
|
+
* Routes are sorted by specificity (static > dynamic > catch-all) before
|
|
11
|
+
* matching, so `/about` wins over `/:slug` even if the catch-all appears first.
|
|
12
|
+
*
|
|
13
|
+
* URL segments are safely decoded (plan §11.1, runtime-security §10).
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchRoute(pathname: string, routes: PageRoute[]): MatchResult | undefined;
|
|
16
|
+
export interface ApiMatchResult<T = ApiRoute> {
|
|
17
|
+
route: T;
|
|
18
|
+
params: Record<string, string | string[]>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Match a request pathname against a list of API routes.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchApiRoute<T extends {
|
|
24
|
+
path: string;
|
|
25
|
+
}>(pathname: string, routes: T[]): ApiMatchResult<T> | undefined;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { PageRoute, ScannedRoutes } from "../router/route-scanner.js";
|
|
2
|
+
import type { BuildConfig } from "../build/build.js";
|
|
3
|
+
import type { RouteParams } from "../types.js";
|
|
4
|
+
import { type CachePolicy } from "../cache/policy.js";
|
|
5
|
+
export interface RenderPageOptions {
|
|
6
|
+
route: PageRoute;
|
|
7
|
+
params?: RouteParams;
|
|
8
|
+
searchParams?: URLSearchParams;
|
|
9
|
+
config: Pick<BuildConfig, "lang" | "clientEntry" | "renderEndpoint">;
|
|
10
|
+
/** Custom module loader. Defaults to native dynamic import. */
|
|
11
|
+
importer?: (path: string) => Promise<unknown>;
|
|
12
|
+
/** Per-page action names exposed in the HTML shell. */
|
|
13
|
+
actions?: Record<string, string[]>;
|
|
14
|
+
/** Current request, used to hydrate data loaders that need cookies/headers. */
|
|
15
|
+
request?: Request;
|
|
16
|
+
}
|
|
17
|
+
export interface RenderPageResult {
|
|
18
|
+
html: string;
|
|
19
|
+
revalidate?: number;
|
|
20
|
+
/**
|
|
21
|
+
* `Set-Cookie` header value that clears the action error cookie, when the
|
|
22
|
+
* page consumed a relayed action failure. The SSR server should append it to
|
|
23
|
+
* the outgoing response so the cookie does not persist.
|
|
24
|
+
*/
|
|
25
|
+
clearActionErrorCookie?: string;
|
|
26
|
+
/** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */
|
|
27
|
+
head?: string;
|
|
28
|
+
/** Resolved page title (from metadata or fallback). */
|
|
29
|
+
resolvedTitle?: string;
|
|
30
|
+
/**
|
|
31
|
+
* When a loader or layout throws a `Response` (e.g. `throw new Response(...,
|
|
32
|
+
* { status: 404 })`), it is captured here as a first-class response instead
|
|
33
|
+
* of being treated as an internal error (A-22).
|
|
34
|
+
*/
|
|
35
|
+
response?: Response;
|
|
36
|
+
/** HTTP status code for the rendered page (e.g. 404 for not-found pages). */
|
|
37
|
+
status?: number;
|
|
38
|
+
/** Cache policy declared by the route (§9.1). */
|
|
39
|
+
cachePolicy?: CachePolicy;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Collects `<html>` attributes and head scripts declared by data loaders
|
|
43
|
+
* (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.
|
|
44
|
+
*/
|
|
45
|
+
export declare function collectShellExtras(pageData: unknown, layoutDataList: unknown[]): {
|
|
46
|
+
htmlAttributes: Record<string, string>;
|
|
47
|
+
headScripts: string[];
|
|
48
|
+
headLinks: string[];
|
|
49
|
+
};
|
|
50
|
+
export declare function renderPage(options: RenderPageOptions): Promise<RenderPageResult>;
|
|
51
|
+
export interface RenderErrorPageOptions {
|
|
52
|
+
routes: ScannedRoutes;
|
|
53
|
+
status: 404 | 500;
|
|
54
|
+
error?: unknown;
|
|
55
|
+
config: Pick<BuildConfig, "lang" | "clientEntry" | "renderEndpoint">;
|
|
56
|
+
actions?: Record<string, string[]>;
|
|
57
|
+
importer?: (path: string) => Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
export declare function renderErrorPage(options: RenderErrorPageOptions): Promise<{
|
|
60
|
+
html: string;
|
|
61
|
+
status: number;
|
|
62
|
+
} | undefined>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { PageRoute, ScannedRoutes } from "../router/route-scanner.js";
|
|
2
|
+
import type { BuildConfig } from "../build/build.js";
|
|
3
|
+
import type { RouteParams } from "../types.js";
|
|
4
|
+
import { type CachePolicy } from "../cache/policy.js";
|
|
5
|
+
export interface RenderPageOptions {
|
|
6
|
+
route: PageRoute;
|
|
7
|
+
params?: RouteParams;
|
|
8
|
+
searchParams?: URLSearchParams;
|
|
9
|
+
config: Pick<BuildConfig, "lang" | "clientEntry" | "renderEndpoint">;
|
|
10
|
+
/** Custom module loader. Defaults to native dynamic import. */
|
|
11
|
+
importer?: (path: string) => Promise<unknown>;
|
|
12
|
+
/** Per-page action names exposed in the HTML shell. */
|
|
13
|
+
actions?: Record<string, string[]>;
|
|
14
|
+
/** Current request, used to hydrate data loaders that need cookies/headers. */
|
|
15
|
+
request?: Request;
|
|
16
|
+
}
|
|
17
|
+
export interface RenderPageResult {
|
|
18
|
+
html: string;
|
|
19
|
+
revalidate?: number;
|
|
20
|
+
/**
|
|
21
|
+
* `Set-Cookie` header value that clears the action error cookie, when the
|
|
22
|
+
* page consumed a relayed action failure. The SSR server should append it to
|
|
23
|
+
* the outgoing response so the cookie does not persist.
|
|
24
|
+
*/
|
|
25
|
+
clearActionErrorCookie?: string;
|
|
26
|
+
/** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */
|
|
27
|
+
head?: string;
|
|
28
|
+
/** Resolved page title (from metadata or fallback). */
|
|
29
|
+
resolvedTitle?: string;
|
|
30
|
+
/**
|
|
31
|
+
* When a loader or layout throws a `Response` (e.g. `throw new Response(...,
|
|
32
|
+
* { status: 404 })`), it is captured here as a first-class response instead
|
|
33
|
+
* of being treated as an internal error (A-22).
|
|
34
|
+
*/
|
|
35
|
+
response?: Response;
|
|
36
|
+
/** HTTP status code for the rendered page (e.g. 404 for not-found pages). */
|
|
37
|
+
status?: number;
|
|
38
|
+
/** Cache policy declared by the route (§9.1). */
|
|
39
|
+
cachePolicy?: CachePolicy;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Collects `<html>` attributes and head scripts declared by data loaders
|
|
43
|
+
* (page and layouts) via top-level `htmlAttributes` / `headScripts` fields.
|
|
44
|
+
*/
|
|
45
|
+
export declare function collectShellExtras(pageData: unknown, layoutDataList: unknown[]): {
|
|
46
|
+
htmlAttributes: Record<string, string>;
|
|
47
|
+
headScripts: string[];
|
|
48
|
+
headLinks: string[];
|
|
49
|
+
};
|
|
50
|
+
export declare function renderPage(options: RenderPageOptions): Promise<RenderPageResult>;
|
|
51
|
+
export interface RenderErrorPageOptions {
|
|
52
|
+
routes: ScannedRoutes;
|
|
53
|
+
status: 404 | 500;
|
|
54
|
+
error?: unknown;
|
|
55
|
+
config: Pick<BuildConfig, "lang" | "clientEntry" | "renderEndpoint">;
|
|
56
|
+
actions?: Record<string, string[]>;
|
|
57
|
+
importer?: (path: string) => Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
export declare function renderErrorPage(options: RenderErrorPageOptions): Promise<{
|
|
60
|
+
html: string;
|
|
61
|
+
status: number;
|
|
62
|
+
} | undefined>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import { scanRoutes } from "../router/route-scanner.js";
|
|
3
|
+
import { type ActionSecurityOptions } from "../action/server.js";
|
|
4
|
+
export interface SsrServerOptions {
|
|
5
|
+
/** Absolute path to the app directory (e.g. /project/src/app). */
|
|
6
|
+
appDir: string;
|
|
7
|
+
/** Absolute path to the project root. When provided, action paths in the
|
|
8
|
+
* serialized HTML shell are made relative to this root. */
|
|
9
|
+
root?: string;
|
|
10
|
+
/** Absolute path to the public directory for static files (optional). */
|
|
11
|
+
publicDir?: string;
|
|
12
|
+
/** Base path for the client entry module, e.g. "/_elur/entry-client.js". */
|
|
13
|
+
clientEntry?: string;
|
|
14
|
+
/** Default language for the HTML shell. */
|
|
15
|
+
lang?: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
host?: string;
|
|
18
|
+
/** Absolute path to the ISR cache directory (optional). */
|
|
19
|
+
cacheDir?: string;
|
|
20
|
+
/** Default revalidate interval in seconds when a page does not export one. */
|
|
21
|
+
defaultRevalidate?: number;
|
|
22
|
+
/** If true, render pages with loading.ts boundaries using streaming. */
|
|
23
|
+
streaming?: boolean;
|
|
24
|
+
/** CSRF / origin policy applied to the server actions endpoint. */
|
|
25
|
+
actionSecurity?: ActionSecurityOptions;
|
|
26
|
+
}
|
|
27
|
+
export interface SsrServer {
|
|
28
|
+
server: Server;
|
|
29
|
+
listen(): Promise<void>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create an SSR server that renders pages on demand and serves static files.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createSsrServer(options: SsrServerOptions): Promise<SsrServer>;
|
|
36
|
+
/**
|
|
37
|
+
* Maps a concrete page path (e.g. `/movies/inception`) to the route pattern
|
|
38
|
+
* key used by the action registry (e.g. `/movies/:slug`). Falls back to the
|
|
39
|
+
* path itself when it matches an exact registry key.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveActionPageKey(page: string | undefined, routes: Awaited<ReturnType<typeof scanRoutes>>): string | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import { scanRoutes } from "../router/route-scanner.js";
|
|
3
|
+
import { type ActionSecurityOptions } from "../action/server.js";
|
|
4
|
+
export interface SsrServerOptions {
|
|
5
|
+
/** Absolute path to the app directory (e.g. /project/src/app). */
|
|
6
|
+
appDir: string;
|
|
7
|
+
/** Absolute path to the project root. When provided, action paths in the
|
|
8
|
+
* serialized HTML shell are made relative to this root. */
|
|
9
|
+
root?: string;
|
|
10
|
+
/** Absolute path to the public directory for static files (optional). */
|
|
11
|
+
publicDir?: string;
|
|
12
|
+
/** Base path for the client entry module, e.g. "/_elur/entry-client.js". */
|
|
13
|
+
clientEntry?: string;
|
|
14
|
+
/** Default language for the HTML shell. */
|
|
15
|
+
lang?: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
host?: string;
|
|
18
|
+
/** Absolute path to the ISR cache directory (optional). */
|
|
19
|
+
cacheDir?: string;
|
|
20
|
+
/** Default revalidate interval in seconds when a page does not export one. */
|
|
21
|
+
defaultRevalidate?: number;
|
|
22
|
+
/** If true, render pages with loading.ts boundaries using streaming. */
|
|
23
|
+
streaming?: boolean;
|
|
24
|
+
/** CSRF / origin policy applied to the server actions endpoint. */
|
|
25
|
+
actionSecurity?: ActionSecurityOptions;
|
|
26
|
+
}
|
|
27
|
+
export interface SsrServer {
|
|
28
|
+
server: Server;
|
|
29
|
+
listen(): Promise<void>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create an SSR server that renders pages on demand and serves static files.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createSsrServer(options: SsrServerOptions): Promise<SsrServer>;
|
|
36
|
+
/**
|
|
37
|
+
* Maps a concrete page path (e.g. `/movies/inception`) to the route pattern
|
|
38
|
+
* key used by the action registry (e.g. `/movies/:slug`). Falls back to the
|
|
39
|
+
* path itself when it matches an exact registry key.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveActionPageKey(page: string | undefined, routes: Awaited<ReturnType<typeof scanRoutes>>): string | undefined;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PageRoute, ScannedRoutes } from "../router/route-scanner.js";
|
|
2
|
+
import type { BuildConfig } from "../build/build.js";
|
|
3
|
+
export interface StreamingPageOptions {
|
|
4
|
+
route: PageRoute;
|
|
5
|
+
params: Record<string, string | string[]>;
|
|
6
|
+
searchParams: URLSearchParams;
|
|
7
|
+
config: Pick<BuildConfig, "lang" | "clientEntry">;
|
|
8
|
+
importer?: (path: string) => Promise<unknown>;
|
|
9
|
+
actions?: Record<string, string[]>;
|
|
10
|
+
request?: Request;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render a page shell that shows the loading boundary while the real content
|
|
14
|
+
* is fetched and injected by the client.
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderStreamingPage(options: StreamingPageOptions): Promise<string>;
|
|
17
|
+
export interface RenderPageBodyOptions {
|
|
18
|
+
routes: ScannedRoutes;
|
|
19
|
+
pathname: string;
|
|
20
|
+
searchParams: URLSearchParams;
|
|
21
|
+
config: Pick<BuildConfig, "lang" | "clientEntry">;
|
|
22
|
+
actions?: Record<string, string[]>;
|
|
23
|
+
importer?: (path: string) => Promise<unknown>;
|
|
24
|
+
request?: Request;
|
|
25
|
+
}
|
|
26
|
+
export interface RenderPageBodyResult {
|
|
27
|
+
/** Inner HTML body for the page (without the document shell). */
|
|
28
|
+
body: string;
|
|
29
|
+
/** Page title extracted from the rendered shell. */
|
|
30
|
+
title: string;
|
|
31
|
+
/** Full rendered document shell (used for ISR caching). */
|
|
32
|
+
fullHtml?: string;
|
|
33
|
+
/** `Set-Cookie` value that clears a consumed action error cookie. */
|
|
34
|
+
clearActionErrorCookie?: string;
|
|
35
|
+
/** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */
|
|
36
|
+
head?: string;
|
|
37
|
+
/** First-class Response when a loader threw one (A-22). */
|
|
38
|
+
response?: Response;
|
|
39
|
+
}
|
|
40
|
+
/** Thrown by `renderPageBody` when the requested path has no matching route. */
|
|
41
|
+
export declare class RouteNotFoundError extends Error {
|
|
42
|
+
constructor(pathname: string);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render only the inner HTML body for a page. Used by the streaming endpoint
|
|
46
|
+
* to inject the real content into the shell.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderPageBody(options: RenderPageBodyOptions): Promise<RenderPageBodyResult>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PageRoute, ScannedRoutes } from "../router/route-scanner.js";
|
|
2
|
+
import type { BuildConfig } from "../build/build.js";
|
|
3
|
+
export interface StreamingPageOptions {
|
|
4
|
+
route: PageRoute;
|
|
5
|
+
params: Record<string, string | string[]>;
|
|
6
|
+
searchParams: URLSearchParams;
|
|
7
|
+
config: Pick<BuildConfig, "lang" | "clientEntry">;
|
|
8
|
+
importer?: (path: string) => Promise<unknown>;
|
|
9
|
+
actions?: Record<string, string[]>;
|
|
10
|
+
request?: Request;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render a page shell that shows the loading boundary while the real content
|
|
14
|
+
* is fetched and injected by the client.
|
|
15
|
+
*/
|
|
16
|
+
export declare function renderStreamingPage(options: StreamingPageOptions): Promise<string>;
|
|
17
|
+
export interface RenderPageBodyOptions {
|
|
18
|
+
routes: ScannedRoutes;
|
|
19
|
+
pathname: string;
|
|
20
|
+
searchParams: URLSearchParams;
|
|
21
|
+
config: Pick<BuildConfig, "lang" | "clientEntry">;
|
|
22
|
+
actions?: Record<string, string[]>;
|
|
23
|
+
importer?: (path: string) => Promise<unknown>;
|
|
24
|
+
request?: Request;
|
|
25
|
+
}
|
|
26
|
+
export interface RenderPageBodyResult {
|
|
27
|
+
/** Inner HTML body for the page (without the document shell). */
|
|
28
|
+
body: string;
|
|
29
|
+
/** Page title extracted from the rendered shell. */
|
|
30
|
+
title: string;
|
|
31
|
+
/** Full rendered document shell (used for ISR caching). */
|
|
32
|
+
fullHtml?: string;
|
|
33
|
+
/** `Set-Cookie` value that clears a consumed action error cookie. */
|
|
34
|
+
clearActionErrorCookie?: string;
|
|
35
|
+
/** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */
|
|
36
|
+
head?: string;
|
|
37
|
+
/** First-class Response when a loader threw one (A-22). */
|
|
38
|
+
response?: Response;
|
|
39
|
+
}
|
|
40
|
+
/** Thrown by `renderPageBody` when the requested path has no matching route. */
|
|
41
|
+
export declare class RouteNotFoundError extends Error {
|
|
42
|
+
constructor(pathname: string);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render only the inner HTML body for a page. Used by the streaming endpoint
|
|
46
|
+
* to inject the real content into the shell.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderPageBody(options: RenderPageBodyOptions): Promise<RenderPageBodyResult>;
|