@elurjs/kit 2.4.10 → 2.5.1
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 +313 -0
- package/README.md +248 -15
- package/dist/lib/action/index.cjs +1 -1
- package/dist/lib/action/index.js +1 -1
- package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
- package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
- package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
- package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
- package/dist/lib/adapter-DNsjXvq5.js +160 -0
- package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
- package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
- package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
- package/dist/lib/adapters/bun.cjs +2 -2
- package/dist/lib/adapters/bun.cjs.map +1 -1
- package/dist/lib/adapters/bun.js +17 -9
- package/dist/lib/adapters/bun.js.map +1 -1
- package/dist/lib/adapters/index.d.cts +34 -0
- package/dist/lib/adapters/index.d.ts +34 -0
- package/dist/lib/adapters/netlify.cjs +1 -1
- package/dist/lib/adapters/netlify.js +7 -7
- package/dist/lib/adapters/node.cjs +5 -5
- package/dist/lib/adapters/node.cjs.map +1 -1
- package/dist/lib/adapters/node.js +20 -12
- package/dist/lib/adapters/node.js.map +1 -1
- package/dist/lib/adapters/vercel.cjs +1 -1
- package/dist/lib/adapters/vercel.js +8 -8
- package/dist/lib/build/build.d.cts +40 -0
- package/dist/lib/build/build.d.ts +40 -0
- package/dist/lib/build/document-shell.d.cts +47 -1
- package/dist/lib/build/document-shell.d.ts +47 -1
- package/dist/lib/build/vite-build.d.cts +25 -2
- package/dist/lib/build/vite-build.d.ts +25 -2
- package/dist/lib/cache/adapter.d.cts +41 -0
- package/dist/lib/cache/adapter.d.ts +41 -0
- package/dist/lib/cache/index.cjs +2 -0
- package/dist/lib/cache/index.cjs.map +1 -0
- package/dist/lib/cache/index.d.cts +5 -0
- package/dist/lib/cache/index.d.ts +5 -0
- package/dist/lib/cache/index.js +113 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/invalidation.d.cts +36 -0
- package/dist/lib/cache/invalidation.d.ts +36 -0
- package/dist/lib/cache/redis-adapter.d.cts +49 -0
- package/dist/lib/cache/redis-adapter.d.ts +49 -0
- package/dist/lib/cache-DGhTYyqh.js +50 -0
- package/dist/lib/cache-DGhTYyqh.js.map +1 -0
- package/dist/lib/cache-X1n4Ossu.cjs +2 -0
- package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
- package/dist/lib/cli/output.d.cts +58 -0
- package/dist/lib/cli/output.d.ts +58 -0
- package/dist/lib/cli/ports.d.cts +20 -0
- package/dist/lib/cli/ports.d.ts +20 -0
- package/dist/lib/cli.cjs +2559 -1488
- package/dist/lib/cli.cjs.map +1 -1
- package/dist/lib/cli.d.cts +21 -0
- package/dist/lib/cli.d.ts +21 -0
- package/dist/lib/cli.js +2558 -1488
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/config/index.cjs +1 -1
- package/dist/lib/config/index.cjs.map +1 -1
- package/dist/lib/config/index.d.cts +79 -0
- package/dist/lib/config/index.d.ts +79 -0
- package/dist/lib/config/index.js +21 -14
- package/dist/lib/config/index.js.map +1 -1
- package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
- package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
- package/dist/lib/errors-kSl9yLkg.cjs +2 -0
- package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
- package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
- package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
- package/dist/lib/hydrate-BaS7-d8C.js +133 -0
- package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +8 -6
- package/dist/lib/index.d.ts +8 -6
- package/dist/lib/index.js +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
- package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
- package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
- package/dist/lib/invalidation-DXhdE9wj.js +64 -0
- package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
- package/dist/lib/island/generate-entry.d.cts +40 -2
- package/dist/lib/island/generate-entry.d.ts +40 -2
- package/dist/lib/island/hydrate.d.cts +19 -6
- package/dist/lib/island/hydrate.d.ts +19 -6
- package/dist/lib/island/index.cjs +1 -1
- package/dist/lib/island/index.d.cts +2 -1
- package/dist/lib/island/index.d.ts +2 -1
- package/dist/lib/island/index.js +1 -1
- package/dist/lib/island/island.d.cts +15 -0
- package/dist/lib/island/island.d.ts +15 -0
- package/dist/lib/island-CTBnsOhI.js +59 -0
- package/dist/lib/island-CTBnsOhI.js.map +1 -0
- package/dist/lib/island-kfQEKRe_.cjs +2 -0
- package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
- package/dist/lib/node-http-DVFcYeBS.js +723 -0
- package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
- package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
- package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
- package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
- package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/lib/router/client.cjs +1 -1
- package/dist/lib/router/client.cjs.map +1 -1
- package/dist/lib/router/client.d.cts +55 -8
- package/dist/lib/router/client.d.ts +55 -8
- package/dist/lib/router/client.js +298 -97
- package/dist/lib/router/client.js.map +1 -1
- package/dist/lib/router/redirects.d.cts +34 -0
- package/dist/lib/router/redirects.d.ts +34 -0
- package/dist/lib/runtime/handler.d.cts +59 -0
- package/dist/lib/runtime/handler.d.ts +59 -0
- package/dist/lib/runtime/index.cjs +1 -1
- package/dist/lib/runtime/index.d.cts +2 -1
- package/dist/lib/runtime/index.d.ts +2 -1
- package/dist/lib/runtime/index.js +4 -4
- package/dist/lib/runtime/logger.d.cts +47 -0
- package/dist/lib/runtime/logger.d.ts +47 -0
- package/dist/lib/runtime/node-http.d.cts +11 -1
- package/dist/lib/runtime/node-http.d.ts +11 -1
- package/dist/lib/runtime-ChE4flX3.cjs +2 -0
- package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
- package/dist/lib/runtime-OMyz5yE-.js +905 -0
- package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
- package/dist/lib/seo/index.cjs +1 -10
- package/dist/lib/seo/index.d.cts +1 -0
- package/dist/lib/seo/index.d.ts +1 -0
- package/dist/lib/seo/index.js +2 -41
- package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
- package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
- package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
- package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
- package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
- package/dist/lib/shared-D8w-neq2.js.map +1 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
- package/dist/lib/ssr/render.d.cts +8 -2
- package/dist/lib/ssr/render.d.ts +8 -2
- package/dist/lib/ssr/server.d.cts +7 -0
- package/dist/lib/ssr/server.d.ts +7 -0
- package/dist/lib/ssr/stream-response.d.cts +30 -0
- package/dist/lib/ssr/stream-response.d.ts +30 -0
- package/dist/lib/ssr/stream.d.cts +16 -1
- package/dist/lib/ssr/stream.d.ts +16 -1
- package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
- package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
- package/dist/lib/stream-D-ytogdu.js +106 -0
- package/dist/lib/stream-D-ytogdu.js.map +1 -0
- package/dist/lib/vite/index.cjs +1 -1
- package/dist/lib/vite/index.cjs.map +1 -1
- package/dist/lib/vite/index.d.cts +11 -0
- package/dist/lib/vite/index.d.ts +11 -0
- package/dist/lib/vite/index.js +101 -94
- package/dist/lib/vite/index.js.map +1 -1
- package/package.json +25 -8
- package/dist/lib/errors-0X7pCFNU.cjs +0 -2
- package/dist/lib/hydrate-CJbUWdXu.js +0 -126
- package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
- package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
- package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
- package/dist/lib/node-http-DRAUhO0c.js +0 -617
- package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
- package/dist/lib/node-http-DhxguYyz.cjs +0 -13
- package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
- package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
- package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
- package/dist/lib/runtime-Ds2-wH6P.js +0 -520
- package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
- package/dist/lib/seo/index.cjs.map +0 -1
- package/dist/lib/seo/index.js.map +0 -1
- package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
- package/dist/lib/shared-DU3iQgr7.js.map +0 -1
- package/dist/lib/stream-CpOG052V.js +0 -104
- package/dist/lib/stream-CpOG052V.js.map +0 -1
- package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
|
@@ -1,32 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirname as r, relative as i, sep as a } from "node:path";
|
|
1
|
+
import { mkdir as e, writeFile as t } from "node:fs/promises";
|
|
2
|
+
import { dirname as n, join as r, relative as i, sep as a } from "node:path";
|
|
4
3
|
import { createRequire as o } from "node:module";
|
|
5
4
|
//#region src/island/generate-entry.ts
|
|
6
5
|
function s(e, t) {
|
|
7
6
|
let n = e.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
8
7
|
return /^[a-zA-Z_$]/.test(n) ? `${n}_${t}` : `_${n}_${t}`;
|
|
9
8
|
}
|
|
10
|
-
function c(e
|
|
11
|
-
let
|
|
9
|
+
function c(e = "@elurjs/kit/router", t = {}) {
|
|
10
|
+
let n = {};
|
|
11
|
+
t.prefetch === !1 && (n.prefetch = !1), t.morph === !0 && (n.morph = !0), t.loadingIndicator === !0 && (n.loadingIndicator = !0);
|
|
12
|
+
let r = Object.keys(n).length > 0 ? JSON.stringify(n) : "";
|
|
13
|
+
return `// AUTO-GENERATED by @elurjs/kit. Do not edit.
|
|
14
|
+
import { startClientRouter } from ${JSON.stringify(e)};
|
|
15
|
+
|
|
16
|
+
startClientRouter(${r});
|
|
17
|
+
`;
|
|
18
|
+
}
|
|
19
|
+
function l(e) {
|
|
20
|
+
let t = {};
|
|
21
|
+
return e?.prefetch === !1 && (t.prefetch = !1), e?.morph === !0 && (t.morph = !0), e?.loadingIndicator === !0 && (t.loadingIndicator = !0), Object.keys(t).length > 0 ? JSON.stringify(t) : "";
|
|
22
|
+
}
|
|
23
|
+
function u(e, t, n = "@elurjs/kit/island", r = "@elurjs/kit/router", i) {
|
|
24
|
+
let a = e.map((e, n) => ({
|
|
12
25
|
ident: s(e.name, n),
|
|
13
26
|
name: e.name,
|
|
14
|
-
spec:
|
|
15
|
-
})).map((e) => ` ${JSON.stringify(e.name)}: { load: () => import(${JSON.stringify(e.spec)}).then(m => m.default) },`).join("\n"),
|
|
16
|
-
${
|
|
27
|
+
spec: d(t, e.filePath)
|
|
28
|
+
})).map((e) => ` ${JSON.stringify(e.name)}: { load: () => import(${JSON.stringify(e.spec)}).then(m => m.default) },`).join("\n"), o = a ? `const registry = {
|
|
29
|
+
${a}
|
|
17
30
|
};
|
|
18
|
-
// Defer hydration until the browser is idle so the first paint is not
|
|
19
|
-
// blocked. Islands with directive "load" already have SSR-rendered DOM,
|
|
20
|
-
// so the user sees content immediately — hydration only adds interactivity.
|
|
21
31
|
const hydrate = () => hydrateIslands(registry);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
// Islands inside persisted nodes must not be disposed across navigations.
|
|
33
|
+
// The router announces the survivors in the elur:before-render detail; the
|
|
34
|
+
// DOM query fallback covers hosts that dispatch it without detail.
|
|
35
|
+
const cleanup = (persisted) =>
|
|
36
|
+
cleanupHydratedIslands({
|
|
37
|
+
except: persisted ?? document.querySelectorAll("[data-elur-persist]"),
|
|
38
|
+
});
|
|
39
|
+
let sawBeforeRender = false;
|
|
40
|
+
|
|
41
|
+
// Hydrate right after parse: module scripts are deferred, so this already
|
|
42
|
+
// runs once the DOM is ready. Directives schedule themselves inside
|
|
43
|
+
// hydrateIslands — "load"/"only" run immediately (real Astro semantics),
|
|
44
|
+
// "idle"/"visible" keep their deferred scheduling.
|
|
45
|
+
hydrate();
|
|
46
|
+
|
|
47
|
+
// The router dispatches elur:before-render BEFORE swapping #app: islands are
|
|
48
|
+
// disposed while still attached (Fase A2), except persisted subtrees.
|
|
49
|
+
document.addEventListener("elur:before-render", (event) => {
|
|
50
|
+
sawBeforeRender = true;
|
|
51
|
+
cleanup(event.detail?.persisted);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Re-hydrate after SPA navigations.
|
|
27
55
|
document.addEventListener("elur:rendered", () => {
|
|
28
|
-
|
|
29
|
-
|
|
56
|
+
// Compat fallback: hosts that only dispatch elur:rendered (streaming swap
|
|
57
|
+
// script, hand-rolled integrations) still need the cleanup pass.
|
|
58
|
+
if (!sawBeforeRender) cleanup();
|
|
59
|
+
sawBeforeRender = false;
|
|
60
|
+
hydrate();
|
|
30
61
|
});
|
|
31
62
|
|
|
32
63
|
// Vite HMR: when an island module (or the entry itself) updates, dispose the
|
|
@@ -34,116 +65,54 @@ document.addEventListener("elur:rendered", () => {
|
|
|
34
65
|
// dynamic import() resolves to the fresh modules, so no full page reload is
|
|
35
66
|
// needed (progressive enhancement, audit §10.2 / §12.2).
|
|
36
67
|
if (import.meta.hot) {
|
|
37
|
-
import.meta.hot.accept((
|
|
68
|
+
import.meta.hot.accept(() => {
|
|
38
69
|
cleanupHydratedIslands();
|
|
39
70
|
hydrateIslands(registry);
|
|
40
|
-
if (newModule) {
|
|
41
|
-
// Re-run the module so its side effects (router, listeners) apply.
|
|
42
|
-
}
|
|
43
71
|
});
|
|
44
|
-
}` : "";
|
|
72
|
+
}` : "", c = !i || i.enabled !== !1 && !i.separate;
|
|
45
73
|
return `// AUTO-GENERATED by @elurjs/kit. Do not edit.
|
|
46
|
-
import { startClientRouter } from ${JSON.stringify(r)};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
startClientRouter();
|
|
50
|
-
${a}
|
|
74
|
+
${c ? `import { startClientRouter } from ${JSON.stringify(r)};\n` : ""}import { hydrateIslands, cleanupHydratedIslands } from ${JSON.stringify(n)};
|
|
75
|
+
${c ? `\nstartClientRouter(${l(i)});\n` : ""}${o}
|
|
51
76
|
`;
|
|
52
77
|
}
|
|
53
|
-
function
|
|
54
|
-
let
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
async function u(e) {
|
|
58
|
-
let i = c(e.islands, e.outFile, e.hydrateImport, e.routerImport);
|
|
59
|
-
return await t(r(e.outFile), { recursive: !0 }), await n(e.outFile, i, "utf8"), e.outFile;
|
|
78
|
+
function d(e, t) {
|
|
79
|
+
let r = i(n(e), t).split(a).join("/");
|
|
80
|
+
return r.startsWith(".") || (r = `./${r}`), r;
|
|
60
81
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
let t = await import(e), n = t.default ?? t.middleware;
|
|
67
|
-
if (typeof n != "function") continue;
|
|
68
|
-
return {
|
|
69
|
-
handler: n,
|
|
70
|
-
config: t.config ?? {}
|
|
71
|
-
};
|
|
72
|
-
} catch (e) {
|
|
73
|
-
let t = typeof e == "object" && e && "message" in e ? String(e.message) : String(e);
|
|
74
|
-
if (t.includes("Cannot find module") || t.includes("Cannot find package") || t.includes("ENOENT") || t.includes("Module not found")) continue;
|
|
75
|
-
throw Error(`[elur-kit] Error loading middleware: ${t}`, { cause: e });
|
|
76
|
-
}
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
function f(t, n) {
|
|
80
|
-
if (!n.matcher || n.matcher.length === 0) return !0;
|
|
81
|
-
let r = t.split("?")[0];
|
|
82
|
-
for (let t of n.matcher) {
|
|
83
|
-
if (t === r) return !0;
|
|
84
|
-
let n = t.match(/^(.*)\/:[\w]+\*$/);
|
|
85
|
-
if (n && r === n[1] || e(r, [{
|
|
86
|
-
path: t,
|
|
87
|
-
pagePath: "",
|
|
88
|
-
params: [],
|
|
89
|
-
layouts: []
|
|
90
|
-
}])) return !0;
|
|
91
|
-
}
|
|
92
|
-
return !1;
|
|
93
|
-
}
|
|
94
|
-
async function p(e, t, n) {
|
|
95
|
-
let r, i, a, o = [], s = {
|
|
96
|
-
next(e) {
|
|
97
|
-
e?.headers && (r = e.headers), e?.params && (i = e.params), e?.locals && (a = e.locals);
|
|
98
|
-
},
|
|
99
|
-
params: n,
|
|
100
|
-
locals: {}
|
|
101
|
-
};
|
|
102
|
-
try {
|
|
103
|
-
let o = await e.handler(t, s);
|
|
104
|
-
return o instanceof Response ? {
|
|
105
|
-
kind: "response",
|
|
106
|
-
response: o
|
|
107
|
-
} : {
|
|
108
|
-
kind: "continue",
|
|
109
|
-
headers: r,
|
|
110
|
-
params: i ?? n,
|
|
111
|
-
locals: a
|
|
112
|
-
};
|
|
113
|
-
} finally {
|
|
114
|
-
for (let e of o) try {
|
|
115
|
-
await e();
|
|
116
|
-
} catch (e) {
|
|
117
|
-
console.error("[elur-kit] middleware cleanup error:", e);
|
|
118
|
-
}
|
|
82
|
+
async function f(i) {
|
|
83
|
+
let a = u(i.islands, i.outFile, i.hydrateImport, i.routerImport, i.router);
|
|
84
|
+
if (await e(n(i.outFile), { recursive: !0 }), await t(i.outFile, a, "utf8"), i.router) {
|
|
85
|
+
let e = i.router.outFile ?? r(n(i.outFile), "router.ts"), a = i.router.enabled === !1 ? "// AUTO-GENERATED by @elurjs/kit. Do not edit.\n// router.enabled: false — intentionally empty.\nexport {};\n" : c(i.routerImport ?? "@elurjs/kit/router", i.router);
|
|
86
|
+
await t(e, a, "utf8");
|
|
119
87
|
}
|
|
88
|
+
return i.outFile;
|
|
120
89
|
}
|
|
121
90
|
//#endregion
|
|
122
91
|
//#region src/vite/interpolation-plugin.ts
|
|
123
|
-
var
|
|
124
|
-
function
|
|
125
|
-
|
|
92
|
+
var p = o(import.meta.url), m = !1;
|
|
93
|
+
function h() {
|
|
94
|
+
m || (m = !0, console.warn("[elur-kit] The legacy interpolation transform is deprecated. Install @elurjs/vite-plugin-elur >= 1.1.0 for compile-time partial attribute interpolation. Remove `interpolation: \"legacy\"` once migration is complete."));
|
|
126
95
|
}
|
|
127
|
-
function
|
|
96
|
+
function g() {
|
|
128
97
|
try {
|
|
129
|
-
let [e, t] = (
|
|
98
|
+
let [e, t] = (p("@elurjs/vite-plugin-elur/package.json").version ?? "0.0.0").split(".").map(Number);
|
|
130
99
|
return e > 1 || e === 1 && t >= 1;
|
|
131
100
|
} catch {
|
|
132
101
|
return !1;
|
|
133
102
|
}
|
|
134
103
|
}
|
|
135
|
-
function
|
|
104
|
+
function _() {
|
|
136
105
|
try {
|
|
137
|
-
return
|
|
106
|
+
return p("@elurjs/core")?.templateFeatures?.partialAttributeInterpolation === !0;
|
|
138
107
|
} catch {
|
|
139
108
|
return !1;
|
|
140
109
|
}
|
|
141
110
|
}
|
|
142
|
-
function
|
|
143
|
-
return e === "off" ? !1 : e === "legacy" ? (
|
|
111
|
+
function v(e) {
|
|
112
|
+
return e === "off" ? !1 : e === "legacy" ? (h(), !0) : !g() && !_();
|
|
144
113
|
}
|
|
145
|
-
var
|
|
146
|
-
function
|
|
114
|
+
var y = "html", b = "`";
|
|
115
|
+
function x(e, t) {
|
|
147
116
|
let n = 1, r = t + 2;
|
|
148
117
|
for (; r < e.length && n > 0;) {
|
|
149
118
|
let t = e[r];
|
|
@@ -168,7 +137,7 @@ function S(e, t) {
|
|
|
168
137
|
}
|
|
169
138
|
return r;
|
|
170
139
|
}
|
|
171
|
-
function
|
|
140
|
+
function S(e, t, n) {
|
|
172
141
|
let r = t + 1, i = "", a = !1;
|
|
173
142
|
for (; r < e.length;) {
|
|
174
143
|
let t = e[r];
|
|
@@ -181,7 +150,7 @@ function C(e, t, n) {
|
|
|
181
150
|
break;
|
|
182
151
|
}
|
|
183
152
|
if (t === "$" && e[r + 1] === "{") {
|
|
184
|
-
let t =
|
|
153
|
+
let t = x(e, r);
|
|
185
154
|
i += e.slice(r, t), r = t, a = !0;
|
|
186
155
|
continue;
|
|
187
156
|
}
|
|
@@ -193,9 +162,9 @@ function C(e, t, n) {
|
|
|
193
162
|
hasInterp: a
|
|
194
163
|
};
|
|
195
164
|
}
|
|
196
|
-
function
|
|
165
|
+
function C(e) {
|
|
197
166
|
let t = [], n = 0, r = "", i = () => {
|
|
198
|
-
r &&= (t.push(JSON.stringify(
|
|
167
|
+
r &&= (t.push(JSON.stringify(w(r))), "");
|
|
199
168
|
};
|
|
200
169
|
for (; n < e.length;) {
|
|
201
170
|
if (e[n] === "\\") {
|
|
@@ -204,7 +173,7 @@ function w(e) {
|
|
|
204
173
|
}
|
|
205
174
|
if (e[n] === "$" && e[n + 1] === "{") {
|
|
206
175
|
i();
|
|
207
|
-
let r =
|
|
176
|
+
let r = x(e, n), a = e.slice(n + 2, r - 1).trim();
|
|
208
177
|
a && t.push(`(${a})`), n = r;
|
|
209
178
|
continue;
|
|
210
179
|
}
|
|
@@ -212,7 +181,7 @@ function w(e) {
|
|
|
212
181
|
}
|
|
213
182
|
return i(), t.length === 0 ? "\"\"" : t.length === 1 ? t[0] : t.join(" + ");
|
|
214
183
|
}
|
|
215
|
-
function
|
|
184
|
+
function w(e) {
|
|
216
185
|
let t = {
|
|
217
186
|
n: "\n",
|
|
218
187
|
t: " ",
|
|
@@ -233,7 +202,7 @@ function T(e) {
|
|
|
233
202
|
}
|
|
234
203
|
return n;
|
|
235
204
|
}
|
|
236
|
-
function
|
|
205
|
+
function T(e) {
|
|
237
206
|
let t = "", n = 0, r = e.length;
|
|
238
207
|
for (; n < r;) {
|
|
239
208
|
let i = e.indexOf("<", n);
|
|
@@ -277,7 +246,7 @@ function E(e) {
|
|
|
277
246
|
break;
|
|
278
247
|
}
|
|
279
248
|
if (e[n] === "$" && e[n + 1] === "{") {
|
|
280
|
-
let r =
|
|
249
|
+
let r = x(e, n);
|
|
281
250
|
t += i + e.slice(n, r), n = r;
|
|
282
251
|
continue;
|
|
283
252
|
}
|
|
@@ -299,11 +268,11 @@ function E(e) {
|
|
|
299
268
|
for (; n < r && /\s/.test(e[n]);) c += e[n], n++;
|
|
300
269
|
let l = e[n];
|
|
301
270
|
if (l === "\"" || l === "'") {
|
|
302
|
-
let { end: r, inside: a, hasInterp: u } =
|
|
271
|
+
let { end: r, inside: a, hasInterp: u } = S(e, n, l);
|
|
303
272
|
if (u) {
|
|
304
|
-
let l =
|
|
273
|
+
let l = x(a, 0);
|
|
305
274
|
if (!(a.startsWith("${") && l === a.length && !a.slice(2, l - 1).includes("${"))) {
|
|
306
|
-
t += i + o + s + "=${" +
|
|
275
|
+
t += i + o + s + "=${" + C(a) + "}", n = r;
|
|
307
276
|
continue;
|
|
308
277
|
}
|
|
309
278
|
t += i + o + s + "=" + c + e.slice(n, r);
|
|
@@ -318,16 +287,16 @@ function E(e) {
|
|
|
318
287
|
}
|
|
319
288
|
return t;
|
|
320
289
|
}
|
|
321
|
-
function
|
|
290
|
+
function E(e) {
|
|
322
291
|
let t = "", n = 0;
|
|
323
292
|
for (; n < e.length;) {
|
|
324
|
-
let r = e.indexOf(
|
|
293
|
+
let r = e.indexOf(y, n);
|
|
325
294
|
if (r === -1) {
|
|
326
295
|
t += e.slice(n);
|
|
327
296
|
break;
|
|
328
297
|
}
|
|
329
298
|
for (t += e.slice(n, r + 4), n = r + 4; n < e.length && /\s/.test(e[n]);) t += e[n], n++;
|
|
330
|
-
if (n >= e.length || e[n] !==
|
|
299
|
+
if (n >= e.length || e[n] !== b) continue;
|
|
331
300
|
t += e[n], n++;
|
|
332
301
|
let i = 1, a = "";
|
|
333
302
|
for (; n < e.length && i > 0;) {
|
|
@@ -336,30 +305,30 @@ function D(e) {
|
|
|
336
305
|
a += t + e[n + 1], n += 2;
|
|
337
306
|
continue;
|
|
338
307
|
}
|
|
339
|
-
if (t ===
|
|
308
|
+
if (t === b && (i--, i === 0)) {
|
|
340
309
|
n++;
|
|
341
310
|
break;
|
|
342
311
|
}
|
|
343
312
|
if (t === "$" && e[n + 1] === "{") {
|
|
344
|
-
let t =
|
|
313
|
+
let t = x(e, n);
|
|
345
314
|
a += e.slice(n, t), n = t;
|
|
346
315
|
continue;
|
|
347
316
|
}
|
|
348
317
|
a += t, n++;
|
|
349
318
|
}
|
|
350
|
-
let o =
|
|
351
|
-
t += o, t +=
|
|
319
|
+
let o = T(a);
|
|
320
|
+
t += o, t += b;
|
|
352
321
|
}
|
|
353
322
|
return t;
|
|
354
323
|
}
|
|
355
|
-
function
|
|
324
|
+
function D(e = {}) {
|
|
356
325
|
let t = e.appDir ?? "src/app", n = e.islandsDir ?? "src/islands";
|
|
357
326
|
return {
|
|
358
327
|
name: "elur-kit-interpolation",
|
|
359
328
|
enforce: "pre",
|
|
360
329
|
transform(e, r) {
|
|
361
330
|
if (!r.endsWith(".ts") && !r.endsWith(".js") || !r.includes(t) && !r.includes(n) || !e.includes("html`")) return;
|
|
362
|
-
let i =
|
|
331
|
+
let i = E(e);
|
|
363
332
|
if (i !== e) return {
|
|
364
333
|
code: i,
|
|
365
334
|
map: null
|
|
@@ -368,6 +337,6 @@ function O(e = {}) {
|
|
|
368
337
|
};
|
|
369
338
|
}
|
|
370
339
|
//#endregion
|
|
371
|
-
export {
|
|
340
|
+
export { c as a, u as i, D as n, f as o, v as r, _ as t };
|
|
372
341
|
|
|
373
|
-
//# sourceMappingURL=interpolation-plugin-
|
|
342
|
+
//# sourceMappingURL=interpolation-plugin-K2qE69tF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interpolation-plugin-K2qE69tF.js","names":[],"sources":["../../src/island/generate-entry.ts","../../src/vite/interpolation-plugin.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport type { IslandModule } from \"./scan.js\";\n\n// --- Client entry generator ---\n//\n// Turns a list of scanned islands into a client entry module that imports each\n// island and registers it with `hydrateIslands`. This removes the need to hand-\n// maintain `entry-client.ts` as islands are added or removed.\n//\n// The generated file imports island default exports and passes them to\n// `hydrateIslands` keyed by their registry name.\n\n/**\n * Router options for the generated client entry (Fase 8.3).\n */\nexport interface RouterEntryOptions {\n /**\n * Include SPA navigation code. When `false` the generated entry is\n * hydrate-only and no router file is emitted. Default: `true`.\n */\n enabled?: boolean;\n /** Forwarded to `startClientRouter({ prefetch })`. Default: `true`. */\n prefetch?: boolean;\n /** Forwarded to `startClientRouter({ morph })` (idiomorph swap). */\n morph?: boolean;\n /** Forwarded to `startClientRouter({ loadingIndicator })`. */\n loadingIndicator?: boolean;\n /**\n * Split mode: the entry hydrates islands only and the router lives in a\n * separate generated module (`router.ts` next to the entry, emitted as its\n * own chunk). This is what lets pages without islands load only the router.\n */\n separate?: boolean;\n /** Absolute path of the generated router module when `separate` is set. */\n outFile?: string;\n}\n\n/** Options for generating the client entry module. */\nexport interface GenerateEntryOptions {\n /** Islands to register, from `scanIslands`. */\n islands: IslandModule[];\n /** Absolute path of the entry file to write (e.g. \".elur/entry-client.ts\"). */\n outFile: string;\n /**\n * Import specifier for the kit's client island helpers.\n * Defaults to the published subpath `@elurjs/kit/island`.\n */\n hydrateImport?: string;\n /**\n * Import specifier for the kit's client router.\n * Defaults to the published subpath `@elurjs/kit/router`.\n */\n routerImport?: string;\n /**\n * Router inclusion. Omit for the legacy combined entry (router embedded).\n * With `separate: true`, a standalone router module is also generated at\n * `router.outFile`.\n */\n router?: RouterEntryOptions;\n}\n\n/** Turns a registry name into a safe JS identifier for the import binding. */\nfunction toIdentifier(name: string, index: number): string {\n const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, \"_\");\n return /^[a-zA-Z_$]/.test(cleaned) ? `${cleaned}_${index}` : `_${cleaned}_${index}`;\n}\n\n/**\n * Source for the standalone router module emitted in split builds. Router\n * options are baked in so the public module takes no arguments.\n */\nexport function buildRouterEntrySource(\n routerImport = \"@elurjs/kit/router\",\n options: Omit<RouterEntryOptions, \"enabled\" | \"separate\" | \"outFile\"> = {},\n): string {\n const opts: Record<string, boolean> = {};\n if (options.prefetch === false) opts.prefetch = false;\n if (options.morph === true) opts.morph = true;\n if (options.loadingIndicator === true) opts.loadingIndicator = true;\n const args = Object.keys(opts).length > 0 ? JSON.stringify(opts) : \"\";\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { startClientRouter } from ${JSON.stringify(routerImport)};\n\nstartClientRouter(${args});\n`;\n}\n\n/**\n * Serializes the startClientRouter options embedded in a combined entry.\n * Only non-default flags are emitted to keep the generated code minimal.\n */\nfunction routerCallArgs(router?: RouterEntryOptions): string {\n const opts: Record<string, boolean> = {};\n if (router?.prefetch === false) opts.prefetch = false;\n if (router?.morph === true) opts.morph = true;\n if (router?.loadingIndicator === true) opts.loadingIndicator = true;\n return Object.keys(opts).length > 0 ? JSON.stringify(opts) : \"\";\n}\n\n/** Builds the source code of the client entry module. */\nexport function buildEntrySource(\n islands: IslandModule[],\n outFile: string,\n hydrateImport = \"@elurjs/kit/island\",\n routerImport = \"@elurjs/kit/router\",\n router?: RouterEntryOptions,\n): string {\n const bindings = islands.map((island, i) => ({\n ident: toIdentifier(island.name, i),\n name: island.name,\n // Relative import specifier from the entry file to the island module.\n spec: toImportSpecifier(outFile, island.filePath),\n }));\n\n // Lazy registry: each island is loaded on-demand via dynamic import().\n // This enables code-splitting — islands not on the current page (or not yet\n // triggered by their directive) stay out of the initial bundle.\n //\n // The registry maps island name → discriminated lazy loader `{ load }`.\n // hydrateIslands() awaits `entry.load()` before hydrating, so the first\n // paint only needs the small entry chunk + the islands on the page. The\n // discriminated form lets the hydrator tell eager components from lazy\n // loaders without executing a probe.\n const registryLines = bindings\n .map((b) => ` ${JSON.stringify(b.name)}: { load: () => import(${JSON.stringify(b.spec)}).then(m => m.default) },`)\n .join(\"\\n\");\n\n const islandHydration = registryLines\n ? `const registry = {\n${registryLines}\n};\nconst hydrate = () => hydrateIslands(registry);\n// Islands inside persisted nodes must not be disposed across navigations.\n// The router announces the survivors in the elur:before-render detail; the\n// DOM query fallback covers hosts that dispatch it without detail.\nconst cleanup = (persisted) =>\n cleanupHydratedIslands({\n except: persisted ?? document.querySelectorAll(\"[data-elur-persist]\"),\n });\nlet sawBeforeRender = false;\n\n// Hydrate right after parse: module scripts are deferred, so this already\n// runs once the DOM is ready. Directives schedule themselves inside\n// hydrateIslands — \"load\"/\"only\" run immediately (real Astro semantics),\n// \"idle\"/\"visible\" keep their deferred scheduling.\nhydrate();\n\n// The router dispatches elur:before-render BEFORE swapping #app: islands are\n// disposed while still attached (Fase A2), except persisted subtrees.\ndocument.addEventListener(\"elur:before-render\", (event) => {\n sawBeforeRender = true;\n cleanup(event.detail?.persisted);\n});\n\n// Re-hydrate after SPA navigations.\ndocument.addEventListener(\"elur:rendered\", () => {\n // Compat fallback: hosts that only dispatch elur:rendered (streaming swap\n // script, hand-rolled integrations) still need the cleanup pass.\n if (!sawBeforeRender) cleanup();\n sawBeforeRender = false;\n hydrate();\n});\n\n// Vite HMR: when an island module (or the entry itself) updates, dispose the\n// current islands and re-hydrate from the updated modules — the registry's\n// dynamic import() resolves to the fresh modules, so no full page reload is\n// needed (progressive enhancement, audit §10.2 / §12.2).\nif (import.meta.hot) {\n import.meta.hot.accept(() => {\n cleanupHydratedIslands();\n hydrateIslands(registry);\n });\n}`\n : \"\";\n\n // The router is embedded in the entry unless a separate chunk was\n // requested (split builds) or the router is disabled outright.\n const embedRouter = router ? router.enabled !== false && !router.separate : true;\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\n${embedRouter ? `import { startClientRouter } from ${JSON.stringify(routerImport)};\\n` : \"\"}import { hydrateIslands, cleanupHydratedIslands } from ${JSON.stringify(hydrateImport)};\n${embedRouter ? `\\nstartClientRouter(${routerCallArgs(router)});\\n` : \"\"}${islandHydration}\n`;\n}\n\n/** Computes a POSIX-style relative import specifier between two files. */\nfunction toImportSpecifier(fromFile: string, toFile: string): string {\n let spec = relative(dirname(fromFile), toFile).split(sep).join(\"/\");\n if (!spec.startsWith(\".\")) spec = `./${spec}`;\n return spec;\n}\n\n/**\n * Generates and writes the client entry module for the given islands. When\n * `options.router.separate` is set (and the router is enabled), a standalone\n * router module is written alongside it (default: `router.ts` next to the\n * entry — bundle it as a second input to emit `/_elur/router.js`).\n *\n * @param options Generation options.\n * @returns The absolute path of the written entry file.\n */\nexport async function generateClientEntry(\n options: GenerateEntryOptions,\n): Promise<string> {\n const source = buildEntrySource(\n options.islands,\n options.outFile,\n options.hydrateImport,\n options.routerImport,\n options.router,\n );\n await mkdir(dirname(options.outFile), { recursive: true });\n await writeFile(options.outFile, source, \"utf8\");\n\n // Always emit the router module whenever router options were provided —\n // even in combined mode or with the router disabled — so a two-input user\n // config (`.elur/router.ts` as second input) never breaks on a missing\n // file. In combined mode the file is simply never emitted as a page script.\n if (options.router) {\n const routerFile =\n options.router.outFile ?? join(dirname(options.outFile), \"router.ts\");\n const routerSource =\n options.router.enabled === false\n ? `// AUTO-GENERATED by @elurjs/kit. Do not edit.\\n// router.enabled: false — intentionally empty.\\nexport {};\\n`\n : buildRouterEntrySource(options.routerImport ?? \"@elurjs/kit/router\", options.router);\n await writeFile(routerFile, routerSource, \"utf8\");\n }\n return options.outFile;\n}\n","import { createRequire } from \"node:module\";\nimport type { Plugin } from \"vite\";\n\n/**\n * How the legacy interpolation transform is handled relative to the installed\n * Elur core and Vite plugin:\n *\n * - `\"auto\"` (default): the kit's legacy transform is only applied when the\n * Vite plugin (`@elurjs/vite-plugin-elur` >= 1.1.0) is NOT installed.\n * The plugin has a more powerful state-machine lexer and takes precedence.\n * - `\"legacy\"`: always apply the kit's transform (for migrations), with a\n * one-time deprecation warning.\n * - `\"off\"`: never apply the kit's transform. Recommended when the Vite\n * plugin is installed.\n */\nexport type InterpolationMode = \"auto\" | \"legacy\" | \"off\";\n\nconst require = createRequire(import.meta.url);\n\nlet _warnedLegacy = false;\n\nfunction warnLegacyOnce(): void {\n if (_warnedLegacy) return;\n _warnedLegacy = true;\n console.warn(\n \"[elur-kit] The legacy interpolation transform is deprecated. \" +\n \"Install @elurjs/vite-plugin-elur >= 1.1.0 for compile-time \" +\n \"partial attribute interpolation. Remove `interpolation: \\\"legacy\\\"` \" +\n \"once migration is complete.\",\n );\n}\n\n/**\n * Detects whether the Vite plugin (`@elurjs/vite-plugin-elur`) is\n * installed and provides compile-time partial attribute interpolation.\n */\nexport function pluginSupportsPartialInterpolation(): boolean {\n try {\n const pkg = require(\"@elurjs/vite-plugin-elur/package.json\") as {\n version?: string;\n };\n // >= 1.1.0 has the interpolation lexer\n const [major, minor] = (pkg.version ?? \"0.0.0\").split(\".\").map(Number);\n return major > 1 || (major === 1 && minor >= 1);\n } catch {\n return false;\n }\n}\n\n/**\n * Detects whether the installed Elur core supports partial attribute\n * interpolation natively (via the public `templateFeatures` capability).\n * Note: as of core v3.4.0, this is always false — the lexer moved to the\n * Vite plugin.\n */\nexport function coreSupportsPartialInterpolation(): boolean {\n try {\n const core = require(\"@elurjs/core\") as {\n templateFeatures?: { partialAttributeInterpolation?: boolean };\n };\n return core?.templateFeatures?.partialAttributeInterpolation === true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolves whether the kit's legacy transform should be applied.\n *\n * In `\"auto\"` mode, the kit's transform runs only when neither the Vite\n * plugin nor the core provides partial interpolation. When the Vite plugin\n * is installed (>= 1.1.0), it takes precedence and the kit's transform is\n * skipped to avoid double-processing.\n */\nexport function shouldUseLegacyInterpolation(mode: InterpolationMode): boolean {\n if (mode === \"off\") return false;\n if (mode === \"legacy\") {\n warnLegacyOnce();\n return true;\n }\n // auto: skip if the Vite plugin handles it\n if (pluginSupportsPartialInterpolation()) return false;\n // fallback: use legacy if core doesn't support it natively\n return !coreSupportsPartialInterpolation();\n}\n\n/**\n * Transforms Elur `html\\`\\`` templates so that attributes with partial\n * interpolation become a single interpolation expression.\n *\n * Elur requires every dynamic attribute to be a single interpolation covering\n * the whole value. This plugin rewrites patterns such as:\n *\n * html\\`<a href=\"/blog/${slug}\">...</a>\\`\n *\n * into:\n *\n * html\\`<a href=${\"/blog/\" + slug}>...</a>\\`\n *\n * Only files inside the app and islands directories are processed.\n *\n * @deprecated Elur core supports partial attribute interpolation natively.\n * Keep this transform only for migrations against older cores\n * (`interpolation: \"legacy\"`).\n */\nexport interface InterpolationPluginOptions {\n appDir?: string;\n islandsDir?: string;\n}\n\nconst HTML_TAG = \"html\";\nconst TEMPLATE_START = \"`\";\n\n/**\n * Scans a `${...}` interpolation starting at `start` (where content[start] is\n * `$` and content[start + 1] is `{`), honoring nested braces, strings and\n * escape sequences. Returns the index just past the closing `}`.\n */\nfunction scanInterpolation(content: string, start: number): number {\n let depth = 1;\n let i = start + 2;\n while (i < content.length && depth > 0) {\n const c = content[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n const q = c;\n i++;\n while (i < content.length) {\n if (content[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (content[i] === q) break;\n i++;\n }\n i++;\n continue;\n }\n if (c === \"{\") depth++;\n else if (c === \"}\") depth--;\n i++;\n }\n return i;\n}\n\n/**\n * Scans a quoted attribute value starting at `start` (where content[start] is\n * the quote character). Handles escapes, `${...}` interpolations with nested\n * braces, and nested quotes. Returns the index just past the closing quote,\n * the raw inner text (escapes preserved as in the source) and whether the\n * value contains at least one interpolation.\n */\nfunction scanQuotedValue(\n content: string,\n start: number,\n quote: string,\n): { end: number; inside: string; hasInterp: boolean } {\n let i = start + 1;\n let inside = \"\";\n let hasInterp = false;\n while (i < content.length) {\n const c = content[i];\n if (c === \"\\\\\") {\n inside += c + (content[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (c === quote) {\n i++;\n break;\n }\n if (c === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n inside += content.slice(i, end);\n i = end;\n hasInterp = true;\n continue;\n }\n inside += c;\n i++;\n }\n return { end: i, inside, hasInterp };\n}\n\n/**\n * Converts the inner text of a quoted attribute value (which may contain\n * `${...}` interpolations) into a JS expression. Literal parts are JSON\n * encoded; interpolations keep their raw expression text.\n *\n * Examples:\n * /blog/${slug} -> \"/blog/\" + (slug)\n * ${slug} -> (slug)\n * tag ${cls({a:1})} -> \"tag \" + (cls({a:1}))\n */\nfunction valueToExpression(value: string): string {\n const parts: string[] = [];\n let i = 0;\n let literal = \"\";\n const flush = () => {\n if (literal) {\n parts.push(JSON.stringify(unescapeAttributeLiteral(literal)));\n literal = \"\";\n }\n };\n\n while (i < value.length) {\n if (value[i] === \"\\\\\") {\n literal += value[i] + (value[i + 1] ?? \"\");\n i += 2;\n continue;\n }\n if (value[i] === \"$\" && value[i + 1] === \"{\") {\n flush();\n const end = scanInterpolation(value, i);\n const expr = value.slice(i + 2, end - 1).trim();\n if (expr) parts.push(`(${expr})`);\n i = end;\n continue;\n }\n literal += value[i];\n i++;\n }\n flush();\n\n if (parts.length === 0) return '\"\"';\n if (parts.length === 1) return parts[0] as string;\n return parts.join(\" + \");\n}\n\n/**\n * Unescapes escape sequences that appear inside a JS template literal so the\n * JSON.stringify output matches the runtime string value.\n */\nfunction unescapeAttributeLiteral(literal: string): string {\n const escapes: Record<string, string> = {\n n: \"\\n\",\n t: \"\\t\",\n r: \"\\r\",\n };\n let out = \"\";\n let i = 0;\n while (i < literal.length) {\n const c = literal[i];\n if (c === \"\\\\\" && i + 1 < literal.length) {\n const next = literal[i + 1];\n if (next in escapes) {\n out += escapes[next];\n i += 2;\n continue;\n }\n out += next;\n i += 2;\n continue;\n }\n out += c;\n i++;\n }\n return out;\n}\n\n/**\n * Rewrites quoted attribute values that contain interpolations inside html``\n * templates, leaving everything else untouched.\n */\nfunction transformTemplateContent(content: string): string {\n let out = \"\";\n let i = 0;\n const n = content.length;\n\n while (i < n) {\n const lt = content.indexOf(\"<\", i);\n if (lt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, lt);\n i = lt;\n\n // HTML comments: copy verbatim.\n if (content.startsWith(\"<!--\", i)) {\n const end = content.indexOf(\"-->\", i + 4);\n if (end === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, end + 3);\n i = end + 3;\n continue;\n }\n\n // Closing tags, doctype, CDATA, processing instructions: copy verbatim.\n if (content[i + 1] === \"/\" || content[i + 1] === \"!\" || content[i + 1] === \"?\") {\n const gt = content.indexOf(\">\", i + 1);\n if (gt === -1) {\n out += content.slice(i);\n break;\n }\n out += content.slice(i, gt + 1);\n i = gt + 1;\n continue;\n }\n\n // Opening tag. Copy the tag name, then walk its attributes.\n let j = i + 1;\n while (j < n && /[a-zA-Z0-9-]/.test(content[j])) j++;\n out += content.slice(i, j);\n i = j;\n\n while (i < n) {\n let ws = \"\";\n while (i < n && /\\s/.test(content[i])) {\n ws += content[i];\n i++;\n }\n if (i >= n) {\n out += ws;\n break;\n }\n if (content[i] === \">\") {\n out += ws + \">\";\n i++;\n break;\n }\n if (content[i] === \"/\" && content[i + 1] === \">\") {\n out += ws + \"/>\";\n i += 2;\n break;\n }\n // Interpolation in the tag body (dynamic attrs/spread): copy verbatim.\n if (content[i] === \"$\" && content[i + 1] === \"{\") {\n const end = scanInterpolation(content, i);\n out += ws + content.slice(i, end);\n i = end;\n continue;\n }\n\n // Attribute name.\n let nameStart = i;\n while (i < n && !/[\\s=/>\"'$]/.test(content[i])) i++;\n const name = content.slice(nameStart, i);\n if (!name) {\n out += ws + content[i];\n i++;\n continue;\n }\n\n let eqWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n eqWs += content[i];\n i++;\n }\n\n if (content[i] !== \"=\") {\n out += ws + name + eqWs;\n continue;\n }\n\n i++; // consume \"=\"\n let valWs = \"\";\n while (i < n && /\\s/.test(content[i])) {\n valWs += content[i];\n i++;\n }\n\n const quote = content[i];\n if (quote === '\"' || quote === \"'\") {\n const { end, inside, hasInterp } = scanQuotedValue(content, i, quote);\n if (hasInterp) {\n // Skip values that are a single full interpolation: Elur handles\n // `attr=\"${expr}\"` natively, so only partial interpolations need the\n // rewrite.\n const first = scanInterpolation(inside, 0);\n const fullValue =\n inside.startsWith(\"${\") &&\n first === inside.length &&\n !inside.slice(2, first - 1).includes(\"${\");\n if (!fullValue) {\n // Elur needs the interpolation to start right after \"=\" (no space),\n // so the whitespace before the original value is dropped.\n out += ws + name + eqWs + \"=\" + \"${\" + valueToExpression(inside) + \"}\";\n i = end;\n continue;\n }\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n } else {\n out += ws + name + eqWs + \"=\" + valWs + content.slice(i, end);\n }\n i = end;\n continue;\n }\n\n // Unquoted value: copy up to whitespace, \">\" or \"/>\".\n let v = \"\";\n while (\n i < n &&\n !/\\s/.test(content[i]) &&\n content[i] !== \">\" &&\n !(content[i] === \"/\" && content[i + 1] === \">\")\n ) {\n v += content[i];\n i++;\n }\n out += ws + name + eqWs + \"=\" + valWs + v;\n }\n }\n\n return out;\n}\n\n/**\n * @deprecated Use the native partial attribute interpolation of Elur core\n * (core >= 3.3). Kept for legacy migrations and direct consumers.\n */\nexport function transformPartialInterpolations(source: string): string {\n let result = \"\";\n let i = 0;\n while (i < source.length) {\n // Find the next html` sequence.\n const htmlIndex = source.indexOf(HTML_TAG, i);\n if (htmlIndex === -1) {\n result += source.slice(i);\n break;\n }\n result += source.slice(i, htmlIndex + HTML_TAG.length);\n i = htmlIndex + HTML_TAG.length;\n\n // Skip whitespace before the backtick.\n while (i < source.length && /\\s/.test(source[i])) {\n result += source[i];\n i++;\n }\n if (i >= source.length || source[i] !== TEMPLATE_START) {\n continue;\n }\n result += source[i];\n i++;\n\n // Parse the template literal until the matching backtick.\n let depth = 1;\n let templateContent = \"\";\n while (i < source.length && depth > 0) {\n const char = source[i];\n if (char === \"\\\\\") {\n templateContent += char + source[i + 1];\n i += 2;\n continue;\n }\n if (char === TEMPLATE_START) {\n depth--;\n if (depth === 0) {\n i++;\n break;\n }\n }\n if (char === \"$\") {\n // Look ahead for ${...}\n if (source[i + 1] === \"{\") {\n const end = scanInterpolation(source, i);\n templateContent += source.slice(i, end);\n i = end;\n continue;\n }\n }\n templateContent += char;\n i++;\n }\n\n const transformed = transformTemplateContent(templateContent);\n result += transformed;\n result += TEMPLATE_START;\n }\n return result;\n}\n\nexport function elurJsInterpolationPlugin(options: InterpolationPluginOptions = {}): Plugin {\n const appDir = options.appDir ?? \"src/app\";\n const islandsDir = options.islandsDir ?? \"src/islands\";\n return {\n name: \"elur-kit-interpolation\",\n enforce: \"pre\",\n transform(code, id) {\n if (!id.endsWith(\".ts\") && !id.endsWith(\".js\")) return;\n if (!id.includes(appDir) && !id.includes(islandsDir)) return;\n if (!code.includes(\"html`\")) return;\n const transformed = transformPartialInterpolations(code);\n if (transformed === code) return;\n return { code: transformed, map: null };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={mode:`dynamic`,revalidate:0};function t(t){if(!t||typeof t!=`object`)return e;let n=t,r=n.mode;return r!==`public`&&r!==`private`&&r!==`dynamic`?e:{mode:r,revalidate:typeof n.revalidate==`number`?n.revalidate:0,tags:Array.isArray(n.tags)?n.tags.filter(e=>typeof e==`string`):void 0}}function n(e,t){return!(e.mode!==`public`||e.revalidate<=0||t.headers.get(`Cookie`)||t.headers.get(`Authorization`))}var r=class{listeners=new Set;on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}async emit(e){let t=[];for(let n of this.listeners)try{let r=n(e);r instanceof Promise&&t.push(r.catch(e=>{console.error(`[elur-kit] invalidation listener error:`,e)}))}catch(e){console.error(`[elur-kit] invalidation listener error:`,e)}await Promise.all(t)}async invalidateTags(e,t){e.length!==0&&await this.emit({tags:e,source:t})}async invalidatePaths(e,t){e.length!==0&&await this.emit({paths:e,source:t})}clear(){this.listeners.clear()}},i=new r;function a(e,t=i){return t.on(async t=>{if(t.tags&&t.tags.length>0&&await e.invalidateTags(t.tags),t.paths&&t.paths.length>0&&e.delete){let{cacheKey:n}=await Promise.resolve().then(()=>require("./adapter-Dz2hzR36.cjs")).then(e=>e.t);await Promise.all(t.paths.map(t=>e.delete(n(t))))}})}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return r}});
|
|
2
|
+
//# sourceMappingURL=invalidation-CFpNQUv6.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalidation-CFpNQUv6.cjs","names":[],"sources":["../../src/cache/policy.ts","../../src/cache/invalidation.ts"],"sourcesContent":["// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","// --- Cache invalidation hooks (runtime-security §9.4) ---\n//\n// Actions can emit tags/paths to invalidate via a generic context. The cache\n// server listens to these hooks and invalidates the appropriate entries.\n// Integrations like elur-query can also listen, but they are NOT a dependency\n// of the cache server.\n//\n// Design:\n// - `CacheInvalidator` is a simple pub/sub for invalidation events.\n// - The runtime registers an invalidator with the cache adapter.\n// - Actions call `invalidateTags()` / `invalidatePaths()` from their context.\n// - The invalidator dispatches to all registered listeners.\n\nexport interface InvalidationEvent {\n tags?: readonly string[];\n paths?: readonly string[];\n /** Source of the invalidation (e.g. action name). */\n source?: string;\n}\n\nexport type InvalidationListener = (event: InvalidationEvent) => void | Promise<void>;\n\n/**\n * A pub/sub hub for cache invalidation events. Actions emit events;\n * the cache adapter (and optionally elur-query or other integrations) listen.\n */\nexport class CacheInvalidator {\n private listeners = new Set<InvalidationListener>();\n\n /** Registers a listener for invalidation events. Returns an unsubscribe function. */\n on(listener: InvalidationListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** Emits an invalidation event to all listeners. */\n async emit(event: InvalidationEvent): Promise<void> {\n const promises: Array<Promise<void>> = [];\n for (const listener of this.listeners) {\n try {\n const result = listener(event);\n if (result instanceof Promise) {\n // Wrap to prevent unhandled rejection from failing the whole emit.\n promises.push(result.catch((err) => {\n console.error(\"[elur-kit] invalidation listener error:\", err);\n }));\n }\n } catch (err) {\n console.error(\"[elur-kit] invalidation listener error:\", err);\n }\n }\n await Promise.all(promises);\n }\n\n /** Convenience: invalidate by tags. */\n async invalidateTags(tags: readonly string[], source?: string): Promise<void> {\n if (tags.length === 0) return;\n await this.emit({ tags, source });\n }\n\n /** Convenience: invalidate by paths. */\n async invalidatePaths(paths: readonly string[], source?: string): Promise<void> {\n if (paths.length === 0) return;\n await this.emit({ paths, source });\n }\n\n /** Removes all listeners. */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/** Global default invalidator. The runtime registers the cache adapter here. */\nexport const defaultInvalidator = new CacheInvalidator();\n\n/**\n * Connects a CacheAdapter to the default invalidator so that tag/path\n * invalidation events from actions are dispatched to the cache.\n *\n * Returns an unsubscribe function.\n */\nexport function connectCacheAdapter(\n adapter: {\n invalidateTags: (tags: readonly string[]) => Promise<void>;\n delete?: (key: string) => Promise<void>;\n },\n invalidator: CacheInvalidator = defaultInvalidator,\n): () => void {\n return invalidator.on(async (event) => {\n if (event.tags && event.tags.length > 0) {\n await adapter.invalidateTags(event.tags);\n }\n // Path-based invalidation: the adapter needs to know which cache keys\n // correspond to which paths. This is handled by the runtime mapping\n // paths to cache keys before calling delete().\n if (event.paths && event.paths.length > 0 && adapter.delete) {\n // The runtime should register a path-to-key mapper.\n // For now, we use the path as the cache key directly (SHA-256 of path).\n const { cacheKey } = await import(\"./adapter.js\");\n await Promise.all(event.paths.map((p) => adapter.delete!(cacheKey(p))));\n }\n });\n}\n"],"mappings":"AAyBA,IAAa,EAAoC,CAC/C,KAAM,UACN,WAAY,CACd,EAMA,SAAgB,EAAqB,EAA2B,CAC9D,GAAI,CAAC,GAAO,OAAO,GAAQ,SAAU,OAAO,EAC5C,IAAM,EAAM,EACN,EAAO,EAAI,KAMjB,OALI,IAAS,UAAY,IAAS,WAAa,IAAS,UAC/C,EAIF,CAAE,OAAM,WAFI,OAAO,EAAI,YAAe,SAAW,EAAI,WAAa,EAE9C,KADd,MAAM,QAAQ,EAAI,IAAI,EAAI,EAAI,KAAK,OAAQ,GAAM,OAAO,GAAM,QAAQ,EAAI,IAAA,EACvD,CAClC,CAWA,SAAgB,EACd,EACA,EACS,CAKT,MADA,EAHI,EAAO,OAAS,UAChB,EAAO,YAAc,GACrB,EAAQ,QAAQ,IAAI,QAAQ,GAC5B,EAAQ,QAAQ,IAAI,eAAe,EAEzC,CCtCA,IAAa,EAAb,KAA8B,CAC5B,UAAoB,IAAI,IAGxB,GAAG,EAA4C,CAE7C,OADA,KAAK,UAAU,IAAI,CAAQ,MACd,KAAK,UAAU,OAAO,CAAQ,CAC7C,CAGA,MAAM,KAAK,EAAyC,CAClD,IAAM,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAY,KAAK,UAC1B,GAAI,CACF,IAAM,EAAS,EAAS,CAAK,EACzB,aAAkB,SAEpB,EAAS,KAAK,EAAO,MAAO,GAAQ,CAClC,QAAQ,MAAM,0CAA2C,CAAG,CAC9D,CAAC,CAAC,CAEN,OAAS,EAAK,CACZ,QAAQ,MAAM,0CAA2C,CAAG,CAC9D,CAEF,MAAM,QAAQ,IAAI,CAAQ,CAC5B,CAGA,MAAM,eAAe,EAAyB,EAAgC,CACxE,EAAK,SAAW,GACpB,MAAM,KAAK,KAAK,CAAE,OAAM,QAAO,CAAC,CAClC,CAGA,MAAM,gBAAgB,EAA0B,EAAgC,CAC1E,EAAM,SAAW,GACrB,MAAM,KAAK,KAAK,CAAE,QAAO,QAAO,CAAC,CACnC,CAGA,OAAc,CACZ,KAAK,UAAU,MAAM,CACvB,CACF,EAGa,EAAqB,IAAI,EAQtC,SAAgB,EACd,EAIA,EAAgC,EACpB,CACZ,OAAO,EAAY,GAAG,KAAO,IAAU,CAOrC,GANI,EAAM,MAAQ,EAAM,KAAK,OAAS,GACpC,MAAM,EAAQ,eAAe,EAAM,IAAI,EAKrC,EAAM,OAAS,EAAM,MAAM,OAAS,GAAK,EAAQ,OAAQ,CAG3D,GAAM,CAAE,YAAa,MAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAM,wBAAA,CAAA,CAAA,CAAA,KAAA,GAAA,EAAA,CAAA,EAC3B,MAAM,QAAQ,IAAI,EAAM,MAAM,IAAK,GAAM,EAAQ,OAAQ,EAAS,CAAC,CAAC,CAAC,CAAC,CACxE,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//#region src/cache/policy.ts
|
|
2
|
+
var e = {
|
|
3
|
+
mode: "dynamic",
|
|
4
|
+
revalidate: 0
|
|
5
|
+
};
|
|
6
|
+
function t(t) {
|
|
7
|
+
if (!t || typeof t != "object") return e;
|
|
8
|
+
let n = t, r = n.mode;
|
|
9
|
+
return r !== "public" && r !== "private" && r !== "dynamic" ? e : {
|
|
10
|
+
mode: r,
|
|
11
|
+
revalidate: typeof n.revalidate == "number" ? n.revalidate : 0,
|
|
12
|
+
tags: Array.isArray(n.tags) ? n.tags.filter((e) => typeof e == "string") : void 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function n(e, t) {
|
|
16
|
+
return !(e.mode !== "public" || e.revalidate <= 0 || t.headers.get("Cookie") || t.headers.get("Authorization"));
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/cache/invalidation.ts
|
|
20
|
+
var r = class {
|
|
21
|
+
listeners = /* @__PURE__ */ new Set();
|
|
22
|
+
on(e) {
|
|
23
|
+
return this.listeners.add(e), () => this.listeners.delete(e);
|
|
24
|
+
}
|
|
25
|
+
async emit(e) {
|
|
26
|
+
let t = [];
|
|
27
|
+
for (let n of this.listeners) try {
|
|
28
|
+
let r = n(e);
|
|
29
|
+
r instanceof Promise && t.push(r.catch((e) => {
|
|
30
|
+
console.error("[elur-kit] invalidation listener error:", e);
|
|
31
|
+
}));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error("[elur-kit] invalidation listener error:", e);
|
|
34
|
+
}
|
|
35
|
+
await Promise.all(t);
|
|
36
|
+
}
|
|
37
|
+
async invalidateTags(e, t) {
|
|
38
|
+
e.length !== 0 && await this.emit({
|
|
39
|
+
tags: e,
|
|
40
|
+
source: t
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async invalidatePaths(e, t) {
|
|
44
|
+
e.length !== 0 && await this.emit({
|
|
45
|
+
paths: e,
|
|
46
|
+
source: t
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
clear() {
|
|
50
|
+
this.listeners.clear();
|
|
51
|
+
}
|
|
52
|
+
}, i = new r();
|
|
53
|
+
function a(e, t = i) {
|
|
54
|
+
return t.on(async (t) => {
|
|
55
|
+
if (t.tags && t.tags.length > 0 && await e.invalidateTags(t.tags), t.paths && t.paths.length > 0 && e.delete) {
|
|
56
|
+
let { cacheKey: n } = await import("./adapter-DNsjXvq5.js").then((e) => e.t);
|
|
57
|
+
await Promise.all(t.paths.map((t) => e.delete(n(t))));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { t as a, e as i, a as n, n as o, i as r, r as t };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=invalidation-DXhdE9wj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalidation-DXhdE9wj.js","names":[],"sources":["../../src/cache/policy.ts","../../src/cache/invalidation.ts"],"sourcesContent":["// --- Cache policy per route (runtime-security §9.1) ---\n//\n// Authors can declare a cache policy in their page.data.ts:\n//\n// export const cache = {\n// mode: \"public\", // \"public\" | \"private\" | \"dynamic\"\n// revalidate: 60, // seconds\n// tags: [\"products\"], // for tag-based invalidation\n// };\n//\n// Default policy: \"dynamic\" (no public ISR caching).\n// Requests with Cookie/Authorization are never cached publicly.\n// Responses with Set-Cookie/private/no-store are never cached publicly.\n\n/** Cache mode for a route. */\nexport type CacheMode = \"public\" | \"private\" | \"dynamic\";\n\n/** Cache policy declared by the route's data module. */\nexport interface CachePolicy {\n mode: CacheMode;\n revalidate: number;\n tags?: string[];\n}\n\n/** Default cache policy when none is declared. */\nexport const DEFAULT_CACHE_POLICY: CachePolicy = {\n mode: \"dynamic\",\n revalidate: 0,\n};\n\n/**\n * Normalizes a raw cache export from a data module into a CachePolicy.\n * Returns the default policy if the input is invalid or missing.\n */\nexport function normalizeCachePolicy(raw: unknown): CachePolicy {\n if (!raw || typeof raw !== \"object\") return DEFAULT_CACHE_POLICY;\n const obj = raw as Record<string, unknown>;\n const mode = obj.mode;\n if (mode !== \"public\" && mode !== \"private\" && mode !== \"dynamic\") {\n return DEFAULT_CACHE_POLICY;\n }\n const revalidate = typeof obj.revalidate === \"number\" ? obj.revalidate : 0;\n const tags = Array.isArray(obj.tags) ? obj.tags.filter((t) => typeof t === \"string\") : undefined;\n return { mode, revalidate, tags };\n}\n\n/**\n * Determines whether a route's cache policy allows public caching for the\n * given request.\n *\n * Per §9.1:\n * - \"dynamic\" → never cache\n * - \"private\" → never cache publicly (requires private adapter)\n * - \"public\" → cache only if request has no Cookie/Authorization\n */\nexport function shouldCachePublic(\n policy: CachePolicy,\n request: Request,\n): boolean {\n if (policy.mode !== \"public\") return false;\n if (policy.revalidate <= 0) return false;\n if (request.headers.get(\"Cookie\")) return false;\n if (request.headers.get(\"Authorization\")) return false;\n return true;\n}\n","// --- Cache invalidation hooks (runtime-security §9.4) ---\n//\n// Actions can emit tags/paths to invalidate via a generic context. The cache\n// server listens to these hooks and invalidates the appropriate entries.\n// Integrations like elur-query can also listen, but they are NOT a dependency\n// of the cache server.\n//\n// Design:\n// - `CacheInvalidator` is a simple pub/sub for invalidation events.\n// - The runtime registers an invalidator with the cache adapter.\n// - Actions call `invalidateTags()` / `invalidatePaths()` from their context.\n// - The invalidator dispatches to all registered listeners.\n\nexport interface InvalidationEvent {\n tags?: readonly string[];\n paths?: readonly string[];\n /** Source of the invalidation (e.g. action name). */\n source?: string;\n}\n\nexport type InvalidationListener = (event: InvalidationEvent) => void | Promise<void>;\n\n/**\n * A pub/sub hub for cache invalidation events. Actions emit events;\n * the cache adapter (and optionally elur-query or other integrations) listen.\n */\nexport class CacheInvalidator {\n private listeners = new Set<InvalidationListener>();\n\n /** Registers a listener for invalidation events. Returns an unsubscribe function. */\n on(listener: InvalidationListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** Emits an invalidation event to all listeners. */\n async emit(event: InvalidationEvent): Promise<void> {\n const promises: Array<Promise<void>> = [];\n for (const listener of this.listeners) {\n try {\n const result = listener(event);\n if (result instanceof Promise) {\n // Wrap to prevent unhandled rejection from failing the whole emit.\n promises.push(result.catch((err) => {\n console.error(\"[elur-kit] invalidation listener error:\", err);\n }));\n }\n } catch (err) {\n console.error(\"[elur-kit] invalidation listener error:\", err);\n }\n }\n await Promise.all(promises);\n }\n\n /** Convenience: invalidate by tags. */\n async invalidateTags(tags: readonly string[], source?: string): Promise<void> {\n if (tags.length === 0) return;\n await this.emit({ tags, source });\n }\n\n /** Convenience: invalidate by paths. */\n async invalidatePaths(paths: readonly string[], source?: string): Promise<void> {\n if (paths.length === 0) return;\n await this.emit({ paths, source });\n }\n\n /** Removes all listeners. */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/** Global default invalidator. The runtime registers the cache adapter here. */\nexport const defaultInvalidator = new CacheInvalidator();\n\n/**\n * Connects a CacheAdapter to the default invalidator so that tag/path\n * invalidation events from actions are dispatched to the cache.\n *\n * Returns an unsubscribe function.\n */\nexport function connectCacheAdapter(\n adapter: {\n invalidateTags: (tags: readonly string[]) => Promise<void>;\n delete?: (key: string) => Promise<void>;\n },\n invalidator: CacheInvalidator = defaultInvalidator,\n): () => void {\n return invalidator.on(async (event) => {\n if (event.tags && event.tags.length > 0) {\n await adapter.invalidateTags(event.tags);\n }\n // Path-based invalidation: the adapter needs to know which cache keys\n // correspond to which paths. This is handled by the runtime mapping\n // paths to cache keys before calling delete().\n if (event.paths && event.paths.length > 0 && adapter.delete) {\n // The runtime should register a path-to-key mapper.\n // For now, we use the path as the cache key directly (SHA-256 of path).\n const { cacheKey } = await import(\"./adapter.js\");\n await Promise.all(event.paths.map((p) => adapter.delete!(cacheKey(p))));\n }\n });\n}\n"],"mappings":";AAyBA,IAAa,IAAoC;CAC/C,MAAM;CACN,YAAY;AACd;AAMA,SAAgB,EAAqB,GAA2B;CAC9D,IAAI,CAAC,KAAO,OAAO,KAAQ,UAAU,OAAO;CAC5C,IAAM,IAAM,GACN,IAAO,EAAI;CAMjB,OALI,MAAS,YAAY,MAAS,aAAa,MAAS,YAC/C,IAIF;EAAE;EAAM,YAFI,OAAO,EAAI,cAAe,WAAW,EAAI,aAAa;EAE9C,MADd,MAAM,QAAQ,EAAI,IAAI,IAAI,EAAI,KAAK,QAAQ,MAAM,OAAO,KAAM,QAAQ,IAAI,KAAA;CACvD;AAClC;AAWA,SAAgB,EACd,GACA,GACS;CAKT,OADA,EAHI,EAAO,SAAS,YAChB,EAAO,cAAc,KACrB,EAAQ,QAAQ,IAAI,QAAQ,KAC5B,EAAQ,QAAQ,IAAI,eAAe;AAEzC;;;ACtCA,IAAa,IAAb,MAA8B;CAC5B,4BAAoB,IAAI,IAA0B;CAGlD,GAAG,GAA4C;EAE7C,OADA,KAAK,UAAU,IAAI,CAAQ,SACd,KAAK,UAAU,OAAO,CAAQ;CAC7C;CAGA,MAAM,KAAK,GAAyC;EAClD,IAAM,IAAiC,CAAC;EACxC,KAAK,IAAM,KAAY,KAAK,WAC1B,IAAI;GACF,IAAM,IAAS,EAAS,CAAK;GAC7B,AAAI,aAAkB,WAEpB,EAAS,KAAK,EAAO,OAAO,MAAQ;IAClC,QAAQ,MAAM,2CAA2C,CAAG;GAC9D,CAAC,CAAC;EAEN,SAAS,GAAK;GACZ,QAAQ,MAAM,2CAA2C,CAAG;EAC9D;EAEF,MAAM,QAAQ,IAAI,CAAQ;CAC5B;CAGA,MAAM,eAAe,GAAyB,GAAgC;EACxE,EAAK,WAAW,KACpB,MAAM,KAAK,KAAK;GAAE;GAAM;EAAO,CAAC;CAClC;CAGA,MAAM,gBAAgB,GAA0B,GAAgC;EAC1E,EAAM,WAAW,KACrB,MAAM,KAAK,KAAK;GAAE;GAAO;EAAO,CAAC;CACnC;CAGA,QAAc;EACZ,KAAK,UAAU,MAAM;CACvB;AACF,GAGa,IAAqB,IAAI,EAAiB;AAQvD,SAAgB,EACd,GAIA,IAAgC,GACpB;CACZ,OAAO,EAAY,GAAG,OAAO,MAAU;EAOrC,IANI,EAAM,QAAQ,EAAM,KAAK,SAAS,KACpC,MAAM,EAAQ,eAAe,EAAM,IAAI,GAKrC,EAAM,SAAS,EAAM,MAAM,SAAS,KAAK,EAAQ,QAAQ;GAG3D,IAAM,EAAE,gBAAa,MAAM,OAAO,wBAAA,CAAA,MAAA,MAAA,EAAA,CAAA;GAClC,MAAM,QAAQ,IAAI,EAAM,MAAM,KAAK,MAAM,EAAQ,OAAQ,EAAS,CAAC,CAAC,CAAC,CAAC;EACxE;CACF,CAAC;AACH"}
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { IslandModule } from "./scan.js";
|
|
2
|
+
/**
|
|
3
|
+
* Router options for the generated client entry (Fase 8.3).
|
|
4
|
+
*/
|
|
5
|
+
export interface RouterEntryOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Include SPA navigation code. When `false` the generated entry is
|
|
8
|
+
* hydrate-only and no router file is emitted. Default: `true`.
|
|
9
|
+
*/
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
/** Forwarded to `startClientRouter({ prefetch })`. Default: `true`. */
|
|
12
|
+
prefetch?: boolean;
|
|
13
|
+
/** Forwarded to `startClientRouter({ morph })` (idiomorph swap). */
|
|
14
|
+
morph?: boolean;
|
|
15
|
+
/** Forwarded to `startClientRouter({ loadingIndicator })`. */
|
|
16
|
+
loadingIndicator?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Split mode: the entry hydrates islands only and the router lives in a
|
|
19
|
+
* separate generated module (`router.ts` next to the entry, emitted as its
|
|
20
|
+
* own chunk). This is what lets pages without islands load only the router.
|
|
21
|
+
*/
|
|
22
|
+
separate?: boolean;
|
|
23
|
+
/** Absolute path of the generated router module when `separate` is set. */
|
|
24
|
+
outFile?: string;
|
|
25
|
+
}
|
|
2
26
|
/** Options for generating the client entry module. */
|
|
3
27
|
export interface GenerateEntryOptions {
|
|
4
28
|
/** Islands to register, from `scanIslands`. */
|
|
@@ -15,11 +39,25 @@ export interface GenerateEntryOptions {
|
|
|
15
39
|
* Defaults to the published subpath `@elurjs/kit/router`.
|
|
16
40
|
*/
|
|
17
41
|
routerImport?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Router inclusion. Omit for the legacy combined entry (router embedded).
|
|
44
|
+
* With `separate: true`, a standalone router module is also generated at
|
|
45
|
+
* `router.outFile`.
|
|
46
|
+
*/
|
|
47
|
+
router?: RouterEntryOptions;
|
|
18
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Source for the standalone router module emitted in split builds. Router
|
|
51
|
+
* options are baked in so the public module takes no arguments.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildRouterEntrySource(routerImport?: string, options?: Omit<RouterEntryOptions, "enabled" | "separate" | "outFile">): string;
|
|
19
54
|
/** Builds the source code of the client entry module. */
|
|
20
|
-
export declare function buildEntrySource(islands: IslandModule[], outFile: string, hydrateImport?: string, routerImport?: string): string;
|
|
55
|
+
export declare function buildEntrySource(islands: IslandModule[], outFile: string, hydrateImport?: string, routerImport?: string, router?: RouterEntryOptions): string;
|
|
21
56
|
/**
|
|
22
|
-
* Generates and writes the client entry module for the given islands.
|
|
57
|
+
* Generates and writes the client entry module for the given islands. When
|
|
58
|
+
* `options.router.separate` is set (and the router is enabled), a standalone
|
|
59
|
+
* router module is written alongside it (default: `router.ts` next to the
|
|
60
|
+
* entry — bundle it as a second input to emit `/_elur/router.js`).
|
|
23
61
|
*
|
|
24
62
|
* @param options Generation options.
|
|
25
63
|
* @returns The absolute path of the written entry file.
|