@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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "../invalidation-DXhdE9wj.js";
|
|
2
|
+
import { i as o, n as s, r as c, t as l } from "../cache-DGhTYyqh.js";
|
|
3
|
+
import { i as u, n as d, r as f } from "../adapter-DNsjXvq5.js";
|
|
4
|
+
//#region src/cache/redis-adapter.ts
|
|
5
|
+
function p(e) {
|
|
6
|
+
let t = e.client, n = e.prefix ?? "elur-kit:", r = (e) => `${n}data:${e}`, i = (e) => `${n}tag:${e}`;
|
|
7
|
+
async function a(e) {
|
|
8
|
+
let n = await t.get(r(e));
|
|
9
|
+
if (!n) return null;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(n);
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function o(e, n, a) {
|
|
17
|
+
let o = {
|
|
18
|
+
html: n.html,
|
|
19
|
+
generatedAt: n.generatedAt ?? Date.now(),
|
|
20
|
+
revalidate: a.revalidate,
|
|
21
|
+
tags: a.tags,
|
|
22
|
+
version: a.version
|
|
23
|
+
}, s = r(e), c = JSON.stringify(o);
|
|
24
|
+
if (a.revalidate > 0 ? await t.set(s, c, { PX: a.revalidate * 1e3 }) : await t.set(s, c), a.tags && a.tags.length > 0 && t.sadd && t.srem) for (let e of a.tags) await t.sadd(i(e), s);
|
|
25
|
+
}
|
|
26
|
+
async function s(e) {
|
|
27
|
+
await t.del(r(e));
|
|
28
|
+
}
|
|
29
|
+
async function c(e) {
|
|
30
|
+
if (e.length !== 0) {
|
|
31
|
+
if (t.smembers && t.srem) {
|
|
32
|
+
let n = /* @__PURE__ */ new Set();
|
|
33
|
+
for (let r of e) {
|
|
34
|
+
let e = await t.smembers(i(r));
|
|
35
|
+
for (let t of e) n.add(t);
|
|
36
|
+
await t.del(i(r));
|
|
37
|
+
}
|
|
38
|
+
n.size > 0 && await t.del([...n]);
|
|
39
|
+
} else {
|
|
40
|
+
let r = await t.keys(`${n}data:*`);
|
|
41
|
+
for (let n of r) {
|
|
42
|
+
let r = await t.get(n);
|
|
43
|
+
if (r) try {
|
|
44
|
+
JSON.parse(r).tags?.some((t) => e.includes(t)) && await t.del(n);
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
get: a,
|
|
52
|
+
set: o,
|
|
53
|
+
delete: s,
|
|
54
|
+
invalidateTags: c
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function m(e) {
|
|
58
|
+
let t = e.namespace, n = e.prefix ?? "elur-kit:", r = (e) => `${n}data:${e}`, i = (e) => `${n}tag:${e}`;
|
|
59
|
+
async function a(e) {
|
|
60
|
+
let n = await t.get(i(e));
|
|
61
|
+
if (!n) return [];
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(n);
|
|
64
|
+
} catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function o(e, n) {
|
|
69
|
+
await t.put(i(e), JSON.stringify(n));
|
|
70
|
+
}
|
|
71
|
+
async function s(e) {
|
|
72
|
+
let n = await t.get(r(e));
|
|
73
|
+
if (!n) return null;
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(n);
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function c(e, n, i) {
|
|
81
|
+
let s = {
|
|
82
|
+
html: n.html,
|
|
83
|
+
generatedAt: n.generatedAt ?? Date.now(),
|
|
84
|
+
revalidate: i.revalidate,
|
|
85
|
+
tags: i.tags,
|
|
86
|
+
version: i.version
|
|
87
|
+
}, c = r(e), l = JSON.stringify(s);
|
|
88
|
+
if (i.revalidate > 0 ? await t.put(c, l, { expirationTtl: i.revalidate }) : await t.put(c, l), i.tags && i.tags.length > 0) for (let e of i.tags) {
|
|
89
|
+
let t = await a(e);
|
|
90
|
+
t.includes(c) || (t.push(c), await o(e, t));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function l(e) {
|
|
94
|
+
await t.delete(r(e));
|
|
95
|
+
}
|
|
96
|
+
async function u(e) {
|
|
97
|
+
if (e.length !== 0) for (let n of e) {
|
|
98
|
+
let e = await a(n);
|
|
99
|
+
for (let n of e) await t.delete(n);
|
|
100
|
+
await t.delete(i(n));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
get: s,
|
|
105
|
+
set: c,
|
|
106
|
+
delete: l,
|
|
107
|
+
invalidateTags: u
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { a as CacheInvalidator, t as DEFAULT_CACHE_POLICY, d as cacheKey, l as clearCache, n as connectCacheAdapter, m as createCloudflareKVCacheAdapter, f as createFsCacheAdapter, p as createRedisCacheAdapter, i as defaultInvalidator, s as getCachedHtml, u as getWithSWR, c as isStale, e as normalizeCachePolicy, o as setCachedHtml, r as shouldCachePublic };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cache/redis-adapter.ts"],"sourcesContent":["// --- Redis / Upstash / Cloudflare KV Cache Adapter (v2.1 — Fix #3) ---\n//\n// Pluggable cache adapter for serverless and distributed deployments.\n// Implements the same CacheAdapter interface as the filesystem adapter, so\n// it can be dropped in via config without changing application code.\n//\n// Supports any Redis-compatible client (node-redis, ioredis, Upstash REST,\n// Cloudflare KV via @upstash/redis or direct API).\n\nimport type { CacheAdapter, CacheEntry, CacheWriteOptions } from \"./adapter.js\";\n\n// ─── Redis Cache Adapter ────────────────────────────────────────────────────\n\nexport interface RedisCacheAdapterOptions {\n /**\n * Redis client with get/set/del commands.\n * Compatible with node-redis, ioredis, and Upstash.\n */\n client: RedisClient;\n /** Key prefix to namespace cache entries. Default: \"elur-kit:\". */\n prefix?: string;\n}\n\nexport interface RedisClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, opts?: { EX?: number; PX?: number; NX?: boolean }): Promise<unknown>;\n del(key: string | string[]): Promise<number>;\n keys(pattern: string): Promise<string[]>;\n srem?(key: string, ...members: string[]): Promise<number>;\n smembers?(key: string): Promise<string[]>;\n sadd?(key: string, ...members: string[]): Promise<number>;\n}\n\nexport function createRedisCacheAdapter(options: RedisCacheAdapterOptions): CacheAdapter {\n const client = options.client;\n const prefix = options.prefix ?? \"elur-kit:\";\n\n const dataKey = (key: string) => `${prefix}data:${key}`;\n const tagKey = (tag: string) => `${prefix}tag:${tag}`;\n\n async function get(key: string): Promise<CacheEntry | null> {\n const raw = await client.get(dataKey(key));\n if (!raw) return null;\n try {\n return JSON.parse(raw) as CacheEntry;\n } catch {\n return null;\n }\n }\n\n async function set(key: string, value: CacheEntry, opts: CacheWriteOptions): Promise<void> {\n const entry: CacheEntry = {\n html: value.html,\n generatedAt: value.generatedAt ?? Date.now(),\n revalidate: opts.revalidate,\n tags: opts.tags,\n version: opts.version,\n };\n const fullKey = dataKey(key);\n const serialized = JSON.stringify(entry);\n\n // Set TTL on the Redis key if revalidate is set (convert seconds to ms for PX).\n if (opts.revalidate > 0) {\n await client.set(fullKey, serialized, { PX: opts.revalidate * 1000 });\n } else {\n await client.set(fullKey, serialized);\n }\n\n // Update tag index using Redis sets if available.\n if (opts.tags && opts.tags.length > 0 && client.sadd && client.srem) {\n for (const tag of opts.tags) {\n await client.sadd(tagKey(tag), fullKey);\n }\n }\n }\n\n async function del(key: string): Promise<void> {\n await client.del(dataKey(key));\n }\n\n async function invalidateTags(tags: readonly string[]): Promise<void> {\n if (tags.length === 0) return;\n\n if (client.smembers && client.srem) {\n // Use Redis sets for O(1) tag lookup.\n const keysToDelete = new Set<string>();\n for (const tag of tags) {\n const members = await client.smembers(tagKey(tag));\n for (const m of members) keysToDelete.add(m);\n await client.del(tagKey(tag));\n }\n if (keysToDelete.size > 0) {\n await client.del([...keysToDelete]);\n }\n } else {\n // Fallback: scan all data keys and check tags in the entry.\n const allKeys = await client.keys(`${prefix}data:*`);\n for (const fullKey of allKeys) {\n const raw = await client.get(fullKey);\n if (!raw) continue;\n try {\n const entry = JSON.parse(raw) as CacheEntry;\n if (entry.tags?.some((t) => tags.includes(t))) {\n await client.del(fullKey);\n }\n } catch {\n // skip corrupt entries\n }\n }\n }\n }\n\n return { get, set, delete: del, invalidateTags };\n}\n\n// ─── Cloudflare KV Cache Adapter ────────────────────────────────────────────\n\nexport interface CloudflareKVAdapterOptions {\n /**\n * Cloudflare KV namespace binding (from wrangler.toml env binding).\n * Must implement `get`, `put`, and `delete` methods.\n */\n namespace: CloudflareKVNamespace;\n /** Key prefix to namespace cache entries. Default: \"elur-kit:\". */\n prefix?: string;\n}\n\nexport interface CloudflareKVNamespace {\n get(key: string): Promise<string | null>;\n put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: { prefix?: string }): Promise<{ keys: { name: string; metadata?: unknown }[] }>;\n}\n\nexport function createCloudflareKVCacheAdapter(options: CloudflareKVAdapterOptions): CacheAdapter {\n const kv = options.namespace;\n const prefix = options.prefix ?? \"elur-kit:\";\n\n const dataKey = (key: string) => `${prefix}data:${key}`;\n const tagKey = (tag: string) => `${prefix}tag:${tag}`;\n\n // KV doesn't have sets, so we store tag → keys as a JSON string.\n async function loadTagKeys(tag: string): Promise<string[]> {\n const raw = await kv.get(tagKey(tag));\n if (!raw) return [];\n try {\n return JSON.parse(raw) as string[];\n } catch {\n return [];\n }\n }\n\n async function saveTagKeys(tag: string, keys: string[]): Promise<void> {\n await kv.put(tagKey(tag), JSON.stringify(keys));\n }\n\n async function get(key: string): Promise<CacheEntry | null> {\n const raw = await kv.get(dataKey(key));\n if (!raw) return null;\n try {\n return JSON.parse(raw) as CacheEntry;\n } catch {\n return null;\n }\n }\n\n async function set(key: string, value: CacheEntry, opts: CacheWriteOptions): Promise<void> {\n const entry: CacheEntry = {\n html: value.html,\n generatedAt: value.generatedAt ?? Date.now(),\n revalidate: opts.revalidate,\n tags: opts.tags,\n version: opts.version,\n };\n const fullKey = dataKey(key);\n const serialized = JSON.stringify(entry);\n\n // KV supports expirationTtl in seconds.\n if (opts.revalidate > 0) {\n await kv.put(fullKey, serialized, { expirationTtl: opts.revalidate });\n } else {\n await kv.put(fullKey, serialized);\n }\n\n // Update tag index.\n if (opts.tags && opts.tags.length > 0) {\n for (const tag of opts.tags) {\n const existing = await loadTagKeys(tag);\n if (!existing.includes(fullKey)) {\n existing.push(fullKey);\n await saveTagKeys(tag, existing);\n }\n }\n }\n }\n\n async function del(key: string): Promise<void> {\n await kv.delete(dataKey(key));\n }\n\n async function invalidateTags(tags: readonly string[]): Promise<void> {\n if (tags.length === 0) return;\n for (const tag of tags) {\n const keys = await loadTagKeys(tag);\n for (const fullKey of keys) {\n await kv.delete(fullKey);\n }\n await kv.delete(tagKey(tag));\n }\n }\n\n return { get, set, delete: del, invalidateTags };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface InvalidationEvent {
|
|
2
|
+
tags?: readonly string[];
|
|
3
|
+
paths?: readonly string[];
|
|
4
|
+
/** Source of the invalidation (e.g. action name). */
|
|
5
|
+
source?: string;
|
|
6
|
+
}
|
|
7
|
+
export type InvalidationListener = (event: InvalidationEvent) => void | Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* A pub/sub hub for cache invalidation events. Actions emit events;
|
|
10
|
+
* the cache adapter (and optionally elur-query or other integrations) listen.
|
|
11
|
+
*/
|
|
12
|
+
export declare class CacheInvalidator {
|
|
13
|
+
private listeners;
|
|
14
|
+
/** Registers a listener for invalidation events. Returns an unsubscribe function. */
|
|
15
|
+
on(listener: InvalidationListener): () => void;
|
|
16
|
+
/** Emits an invalidation event to all listeners. */
|
|
17
|
+
emit(event: InvalidationEvent): Promise<void>;
|
|
18
|
+
/** Convenience: invalidate by tags. */
|
|
19
|
+
invalidateTags(tags: readonly string[], source?: string): Promise<void>;
|
|
20
|
+
/** Convenience: invalidate by paths. */
|
|
21
|
+
invalidatePaths(paths: readonly string[], source?: string): Promise<void>;
|
|
22
|
+
/** Removes all listeners. */
|
|
23
|
+
clear(): void;
|
|
24
|
+
}
|
|
25
|
+
/** Global default invalidator. The runtime registers the cache adapter here. */
|
|
26
|
+
export declare const defaultInvalidator: CacheInvalidator;
|
|
27
|
+
/**
|
|
28
|
+
* Connects a CacheAdapter to the default invalidator so that tag/path
|
|
29
|
+
* invalidation events from actions are dispatched to the cache.
|
|
30
|
+
*
|
|
31
|
+
* Returns an unsubscribe function.
|
|
32
|
+
*/
|
|
33
|
+
export declare function connectCacheAdapter(adapter: {
|
|
34
|
+
invalidateTags: (tags: readonly string[]) => Promise<void>;
|
|
35
|
+
delete?: (key: string) => Promise<void>;
|
|
36
|
+
}, invalidator?: CacheInvalidator): () => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface InvalidationEvent {
|
|
2
|
+
tags?: readonly string[];
|
|
3
|
+
paths?: readonly string[];
|
|
4
|
+
/** Source of the invalidation (e.g. action name). */
|
|
5
|
+
source?: string;
|
|
6
|
+
}
|
|
7
|
+
export type InvalidationListener = (event: InvalidationEvent) => void | Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* A pub/sub hub for cache invalidation events. Actions emit events;
|
|
10
|
+
* the cache adapter (and optionally elur-query or other integrations) listen.
|
|
11
|
+
*/
|
|
12
|
+
export declare class CacheInvalidator {
|
|
13
|
+
private listeners;
|
|
14
|
+
/** Registers a listener for invalidation events. Returns an unsubscribe function. */
|
|
15
|
+
on(listener: InvalidationListener): () => void;
|
|
16
|
+
/** Emits an invalidation event to all listeners. */
|
|
17
|
+
emit(event: InvalidationEvent): Promise<void>;
|
|
18
|
+
/** Convenience: invalidate by tags. */
|
|
19
|
+
invalidateTags(tags: readonly string[], source?: string): Promise<void>;
|
|
20
|
+
/** Convenience: invalidate by paths. */
|
|
21
|
+
invalidatePaths(paths: readonly string[], source?: string): Promise<void>;
|
|
22
|
+
/** Removes all listeners. */
|
|
23
|
+
clear(): void;
|
|
24
|
+
}
|
|
25
|
+
/** Global default invalidator. The runtime registers the cache adapter here. */
|
|
26
|
+
export declare const defaultInvalidator: CacheInvalidator;
|
|
27
|
+
/**
|
|
28
|
+
* Connects a CacheAdapter to the default invalidator so that tag/path
|
|
29
|
+
* invalidation events from actions are dispatched to the cache.
|
|
30
|
+
*
|
|
31
|
+
* Returns an unsubscribe function.
|
|
32
|
+
*/
|
|
33
|
+
export declare function connectCacheAdapter(adapter: {
|
|
34
|
+
invalidateTags: (tags: readonly string[]) => Promise<void>;
|
|
35
|
+
delete?: (key: string) => Promise<void>;
|
|
36
|
+
}, invalidator?: CacheInvalidator): () => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CacheAdapter } from "./adapter.js";
|
|
2
|
+
export interface RedisCacheAdapterOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Redis client with get/set/del commands.
|
|
5
|
+
* Compatible with node-redis, ioredis, and Upstash.
|
|
6
|
+
*/
|
|
7
|
+
client: RedisClient;
|
|
8
|
+
/** Key prefix to namespace cache entries. Default: "elur-kit:". */
|
|
9
|
+
prefix?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RedisClient {
|
|
12
|
+
get(key: string): Promise<string | null>;
|
|
13
|
+
set(key: string, value: string, opts?: {
|
|
14
|
+
EX?: number;
|
|
15
|
+
PX?: number;
|
|
16
|
+
NX?: boolean;
|
|
17
|
+
}): Promise<unknown>;
|
|
18
|
+
del(key: string | string[]): Promise<number>;
|
|
19
|
+
keys(pattern: string): Promise<string[]>;
|
|
20
|
+
srem?(key: string, ...members: string[]): Promise<number>;
|
|
21
|
+
smembers?(key: string): Promise<string[]>;
|
|
22
|
+
sadd?(key: string, ...members: string[]): Promise<number>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createRedisCacheAdapter(options: RedisCacheAdapterOptions): CacheAdapter;
|
|
25
|
+
export interface CloudflareKVAdapterOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Cloudflare KV namespace binding (from wrangler.toml env binding).
|
|
28
|
+
* Must implement `get`, `put`, and `delete` methods.
|
|
29
|
+
*/
|
|
30
|
+
namespace: CloudflareKVNamespace;
|
|
31
|
+
/** Key prefix to namespace cache entries. Default: "elur-kit:". */
|
|
32
|
+
prefix?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface CloudflareKVNamespace {
|
|
35
|
+
get(key: string): Promise<string | null>;
|
|
36
|
+
put(key: string, value: string, options?: {
|
|
37
|
+
expirationTtl?: number;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
delete(key: string): Promise<void>;
|
|
40
|
+
list(options?: {
|
|
41
|
+
prefix?: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
keys: {
|
|
44
|
+
name: string;
|
|
45
|
+
metadata?: unknown;
|
|
46
|
+
}[];
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare function createCloudflareKVCacheAdapter(options: CloudflareKVAdapterOptions): CacheAdapter;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CacheAdapter } from "./adapter.js";
|
|
2
|
+
export interface RedisCacheAdapterOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Redis client with get/set/del commands.
|
|
5
|
+
* Compatible with node-redis, ioredis, and Upstash.
|
|
6
|
+
*/
|
|
7
|
+
client: RedisClient;
|
|
8
|
+
/** Key prefix to namespace cache entries. Default: "elur-kit:". */
|
|
9
|
+
prefix?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RedisClient {
|
|
12
|
+
get(key: string): Promise<string | null>;
|
|
13
|
+
set(key: string, value: string, opts?: {
|
|
14
|
+
EX?: number;
|
|
15
|
+
PX?: number;
|
|
16
|
+
NX?: boolean;
|
|
17
|
+
}): Promise<unknown>;
|
|
18
|
+
del(key: string | string[]): Promise<number>;
|
|
19
|
+
keys(pattern: string): Promise<string[]>;
|
|
20
|
+
srem?(key: string, ...members: string[]): Promise<number>;
|
|
21
|
+
smembers?(key: string): Promise<string[]>;
|
|
22
|
+
sadd?(key: string, ...members: string[]): Promise<number>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createRedisCacheAdapter(options: RedisCacheAdapterOptions): CacheAdapter;
|
|
25
|
+
export interface CloudflareKVAdapterOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Cloudflare KV namespace binding (from wrangler.toml env binding).
|
|
28
|
+
* Must implement `get`, `put`, and `delete` methods.
|
|
29
|
+
*/
|
|
30
|
+
namespace: CloudflareKVNamespace;
|
|
31
|
+
/** Key prefix to namespace cache entries. Default: "elur-kit:". */
|
|
32
|
+
prefix?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface CloudflareKVNamespace {
|
|
35
|
+
get(key: string): Promise<string | null>;
|
|
36
|
+
put(key: string, value: string, options?: {
|
|
37
|
+
expirationTtl?: number;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
delete(key: string): Promise<void>;
|
|
40
|
+
list(options?: {
|
|
41
|
+
prefix?: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
keys: {
|
|
44
|
+
name: string;
|
|
45
|
+
metadata?: unknown;
|
|
46
|
+
}[];
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare function createCloudflareKVCacheAdapter(options: CloudflareKVAdapterOptions): CacheAdapter;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mkdir as e, readFile as t, rename as n, rm as r, writeFile as i } from "node:fs/promises";
|
|
2
|
+
import { dirname as a, join as o } from "node:path";
|
|
3
|
+
import { createHash as s, randomUUID as c } from "node:crypto";
|
|
4
|
+
//#region src/cache.ts
|
|
5
|
+
function l(e, t) {
|
|
6
|
+
let n = s("sha256").update(t).digest("hex");
|
|
7
|
+
return o(e, `${n}.html.json`);
|
|
8
|
+
}
|
|
9
|
+
async function u(e, n) {
|
|
10
|
+
let r = l(e, n);
|
|
11
|
+
try {
|
|
12
|
+
let e = await t(r, "utf8"), n = JSON.parse(e);
|
|
13
|
+
if (Date.now() - n.generatedAt < n.revalidate * 1e3) return n;
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
async function d(t, o, s, u) {
|
|
17
|
+
let d = l(t, o);
|
|
18
|
+
await e(a(d), { recursive: !0 });
|
|
19
|
+
let f = {
|
|
20
|
+
html: s,
|
|
21
|
+
generatedAt: Date.now(),
|
|
22
|
+
revalidate: u
|
|
23
|
+
}, p = `${d}.${process.pid}.${c()}.tmp`;
|
|
24
|
+
try {
|
|
25
|
+
await i(p, JSON.stringify(f), "utf8"), await n(p, d);
|
|
26
|
+
} finally {
|
|
27
|
+
await r(p, { force: !0 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function f(e, n) {
|
|
31
|
+
let r = l(e, n);
|
|
32
|
+
try {
|
|
33
|
+
let e = await t(r, "utf8"), n = JSON.parse(e);
|
|
34
|
+
return Date.now() - n.generatedAt >= n.revalidate * 1e3;
|
|
35
|
+
} catch {
|
|
36
|
+
return !0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function p(e) {
|
|
40
|
+
try {
|
|
41
|
+
await r(e, {
|
|
42
|
+
recursive: !0,
|
|
43
|
+
force: !0
|
|
44
|
+
});
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { d as i, u as n, f as r, p as t };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=cache-DGhTYyqh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-DGhTYyqh.js","names":[],"sources":["../../src/cache.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { dirname, join } from \"node:path\";\n\nexport interface CacheEntry {\n html: string;\n generatedAt: number;\n revalidate: number;\n}\n\nexport interface CacheOptions {\n cacheDir: string;\n defaultRevalidate?: number;\n}\n\nfunction cachePath(cacheDir: string, pathname: string): string {\n const key = createHash(\"sha256\").update(pathname).digest(\"hex\");\n return join(cacheDir, `${key}.html.json`);\n}\n\nexport async function getCachedHtml(\n cacheDir: string,\n pathname: string,\n): Promise<CacheEntry | undefined> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n if (Date.now() - entry.generatedAt < entry.revalidate * 1000) {\n return entry;\n }\n } catch {\n // cache miss or invalid\n }\n return undefined;\n}\n\nexport async function setCachedHtml(\n cacheDir: string,\n pathname: string,\n html: string,\n revalidate: number,\n): Promise<void> {\n const path = cachePath(cacheDir, pathname);\n await mkdir(dirname(path), { recursive: true });\n const entry: CacheEntry = { html, generatedAt: Date.now(), revalidate };\n const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporaryPath, JSON.stringify(entry), \"utf8\");\n await rename(temporaryPath, path);\n } finally {\n await rm(temporaryPath, { force: true });\n }\n}\n\nexport async function isStale(cacheDir: string, pathname: string): Promise<boolean> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n return Date.now() - entry.generatedAt >= entry.revalidate * 1000;\n } catch {\n return true;\n }\n}\n\nexport async function clearCache(cacheDir: string): Promise<void> {\n try {\n await rm(cacheDir, { recursive: true, force: true });\n } catch {\n // ignore\n }\n}\n"],"mappings":";;;;AAeA,SAAS,EAAU,GAAkB,GAA0B;CAC7D,IAAM,IAAM,EAAW,QAAQ,CAAC,CAAC,OAAO,CAAQ,CAAC,CAAC,OAAO,KAAK;CAC9D,OAAO,EAAK,GAAU,GAAG,EAAI,WAAW;AAC1C;AAEA,eAAsB,EACpB,GACA,GACiC;CACjC,IAAM,IAAO,EAAU,GAAU,CAAQ;CACzC,IAAI;EACF,IAAM,IAAM,MAAM,EAAS,GAAM,MAAM,GACjC,IAAQ,KAAK,MAAM,CAAG;EAC5B,IAAI,KAAK,IAAI,IAAI,EAAM,cAAc,EAAM,aAAa,KACtD,OAAO;CAEX,QAAQ,CAER;AAEF;AAEA,eAAsB,EACpB,GACA,GACA,GACA,GACe;CACf,IAAM,IAAO,EAAU,GAAU,CAAQ;CACzC,MAAM,EAAM,EAAQ,CAAI,GAAG,EAAE,WAAW,GAAK,CAAC;CAC9C,IAAM,IAAoB;EAAE;EAAM,aAAa,KAAK,IAAI;EAAG;CAAW,GAChE,IAAgB,GAAG,EAAK,GAAG,QAAQ,IAAI,GAAG,EAAW,EAAE;CAC7D,IAAI;EAEF,AADA,MAAM,EAAU,GAAe,KAAK,UAAU,CAAK,GAAG,MAAM,GAC5D,MAAM,EAAO,GAAe,CAAI;CAClC,UAAU;EACR,MAAM,EAAG,GAAe,EAAE,OAAO,GAAK,CAAC;CACzC;AACF;AAEA,eAAsB,EAAQ,GAAkB,GAAoC;CAClF,IAAM,IAAO,EAAU,GAAU,CAAQ;CACzC,IAAI;EACF,IAAM,IAAM,MAAM,EAAS,GAAM,MAAM,GACjC,IAAQ,KAAK,MAAM,CAAG;EAC5B,OAAO,KAAK,IAAI,IAAI,EAAM,eAAe,EAAM,aAAa;CAC9D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,EAAW,GAAiC;CAChE,IAAI;EACF,MAAM,EAAG,GAAU;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACrD,QAAQ,CAER;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("node:fs/promises"),t=require("node:path"),n=require("node:crypto");function r(e,r){let i=(0,n.createHash)(`sha256`).update(r).digest(`hex`);return(0,t.join)(e,`${i}.html.json`)}async function i(t,n){let i=r(t,n);try{let t=await(0,e.readFile)(i,`utf8`),n=JSON.parse(t);if(Date.now()-n.generatedAt<n.revalidate*1e3)return n}catch{}}async function a(i,a,o,s){let c=r(i,a);await(0,e.mkdir)((0,t.dirname)(c),{recursive:!0});let l={html:o,generatedAt:Date.now(),revalidate:s},u=`${c}.${process.pid}.${(0,n.randomUUID)()}.tmp`;try{await(0,e.writeFile)(u,JSON.stringify(l),`utf8`),await(0,e.rename)(u,c)}finally{await(0,e.rm)(u,{force:!0})}}async function o(t,n){let i=r(t,n);try{let t=await(0,e.readFile)(i,`utf8`),n=JSON.parse(t);return Date.now()-n.generatedAt>=n.revalidate*1e3}catch{return!0}}async function s(t){try{await(0,e.rm)(t,{recursive:!0,force:!0})}catch{}}Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
|
|
2
|
+
//# sourceMappingURL=cache-X1n4Ossu.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-X1n4Ossu.cjs","names":[],"sources":["../../src/cache.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { dirname, join } from \"node:path\";\n\nexport interface CacheEntry {\n html: string;\n generatedAt: number;\n revalidate: number;\n}\n\nexport interface CacheOptions {\n cacheDir: string;\n defaultRevalidate?: number;\n}\n\nfunction cachePath(cacheDir: string, pathname: string): string {\n const key = createHash(\"sha256\").update(pathname).digest(\"hex\");\n return join(cacheDir, `${key}.html.json`);\n}\n\nexport async function getCachedHtml(\n cacheDir: string,\n pathname: string,\n): Promise<CacheEntry | undefined> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n if (Date.now() - entry.generatedAt < entry.revalidate * 1000) {\n return entry;\n }\n } catch {\n // cache miss or invalid\n }\n return undefined;\n}\n\nexport async function setCachedHtml(\n cacheDir: string,\n pathname: string,\n html: string,\n revalidate: number,\n): Promise<void> {\n const path = cachePath(cacheDir, pathname);\n await mkdir(dirname(path), { recursive: true });\n const entry: CacheEntry = { html, generatedAt: Date.now(), revalidate };\n const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporaryPath, JSON.stringify(entry), \"utf8\");\n await rename(temporaryPath, path);\n } finally {\n await rm(temporaryPath, { force: true });\n }\n}\n\nexport async function isStale(cacheDir: string, pathname: string): Promise<boolean> {\n const path = cachePath(cacheDir, pathname);\n try {\n const raw = await readFile(path, \"utf8\");\n const entry = JSON.parse(raw) as CacheEntry;\n return Date.now() - entry.generatedAt >= entry.revalidate * 1000;\n } catch {\n return true;\n }\n}\n\nexport async function clearCache(cacheDir: string): Promise<void> {\n try {\n await rm(cacheDir, { recursive: true, force: true });\n } catch {\n // ignore\n }\n}\n"],"mappings":"kFAeA,SAAS,EAAU,EAAkB,EAA0B,CAC7D,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,CAAQ,CAAC,CAAC,OAAO,KAAK,EAC9D,OAAA,EAAO,EAAA,KAAA,CAAK,EAAU,GAAG,EAAI,WAAW,CAC1C,CAEA,eAAsB,EACpB,EACA,EACiC,CACjC,IAAM,EAAO,EAAU,EAAU,CAAQ,EACzC,GAAI,CACF,IAAM,EAAM,MAAA,EAAM,EAAA,SAAA,CAAS,EAAM,MAAM,EACjC,EAAQ,KAAK,MAAM,CAAG,EAC5B,GAAI,KAAK,IAAI,EAAI,EAAM,YAAc,EAAM,WAAa,IACtD,OAAO,CAEX,MAAQ,CAER,CAEF,CAEA,eAAsB,EACpB,EACA,EACA,EACA,EACe,CACf,IAAM,EAAO,EAAU,EAAU,CAAQ,EACzC,MAAA,EAAM,EAAA,MAAA,EAAA,EAAM,EAAA,QAAA,CAAQ,CAAI,EAAG,CAAE,UAAW,EAAK,CAAC,EAC9C,IAAM,EAAoB,CAAE,OAAM,YAAa,KAAK,IAAI,EAAG,YAAW,EAChE,EAAgB,GAAG,EAAK,GAAG,QAAQ,IAAI,IAAA,EAAG,EAAA,WAAA,CAAW,EAAE,MAC7D,GAAI,CACF,MAAA,EAAM,EAAA,UAAA,CAAU,EAAe,KAAK,UAAU,CAAK,EAAG,MAAM,EAC5D,MAAA,EAAM,EAAA,OAAA,CAAO,EAAe,CAAI,CAClC,QAAU,CACR,MAAA,EAAM,EAAA,GAAA,CAAG,EAAe,CAAE,MAAO,EAAK,CAAC,CACzC,CACF,CAEA,eAAsB,EAAQ,EAAkB,EAAoC,CAClF,IAAM,EAAO,EAAU,EAAU,CAAQ,EACzC,GAAI,CACF,IAAM,EAAM,MAAA,EAAM,EAAA,SAAA,CAAS,EAAM,MAAM,EACjC,EAAQ,KAAK,MAAM,CAAG,EAC5B,OAAO,KAAK,IAAI,EAAI,EAAM,aAAe,EAAM,WAAa,GAC9D,MAAQ,CACN,MAAO,EACT,CACF,CAEA,eAAsB,EAAW,EAAiC,CAChE,GAAI,CACF,MAAA,EAAM,EAAA,GAAA,CAAG,EAAU,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACrD,MAAQ,CAER,CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const bold: (text: string) => string;
|
|
2
|
+
export declare const dim: (text: string) => string;
|
|
3
|
+
export declare const red: (text: string) => string;
|
|
4
|
+
export declare const green: (text: string) => string;
|
|
5
|
+
export declare const yellow: (text: string) => string;
|
|
6
|
+
export declare const cyan: (text: string) => string;
|
|
7
|
+
/** Enables quiet mode: only errors are printed. */
|
|
8
|
+
export declare function setQuiet(value: boolean): void;
|
|
9
|
+
/** Success message with a green check. */
|
|
10
|
+
export declare function success(message: string): void;
|
|
11
|
+
/** Indented info line with a cyan arrow. */
|
|
12
|
+
export declare function info(message: string): void;
|
|
13
|
+
/** Indented detail line (file lists, sub-items). */
|
|
14
|
+
export declare function detail(message: string): void;
|
|
15
|
+
/** Lifecycle event with a dim bracket tag, e.g. [dev], [change]. */
|
|
16
|
+
export declare function event(tag: string, message: string): void;
|
|
17
|
+
/** Warning; suppressed in quiet mode. */
|
|
18
|
+
export declare function warn(message: string): void;
|
|
19
|
+
/** Error; always printed, even in quiet mode. */
|
|
20
|
+
export declare function error(message: string): void;
|
|
21
|
+
/** Formats a byte count as "640 B", "1.5 kB", "2.3 MB". */
|
|
22
|
+
export declare function formatBytes(bytes: number): string;
|
|
23
|
+
/** Formats a duration as "45ms" or "1.23s". */
|
|
24
|
+
export declare function formatDuration(ms: number): string;
|
|
25
|
+
/** Build phase line: checkmark + label + dim duration. */
|
|
26
|
+
export declare function phase(label: string, durationMs: number): void;
|
|
27
|
+
export interface FileEntry {
|
|
28
|
+
path: string;
|
|
29
|
+
bytes: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Renders the generated-file list as aligned "path size" rows (plain text,
|
|
33
|
+
* no color). With more than MAX_FILE_ROWS entries, shows the SHOWN_FILE_ROWS
|
|
34
|
+
* largest plus a "… and N more" line.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fileRows(files: FileEntry[], max?: number, shown?: number): string[];
|
|
37
|
+
/** Prints the generated-file list: dim paths, aligned sizes. */
|
|
38
|
+
export declare function fileList(files: FileEntry[]): void;
|
|
39
|
+
export interface ServerBannerOptions {
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
/** Command label, e.g. "dev" or "preview". */
|
|
43
|
+
command: string;
|
|
44
|
+
localUrl: string;
|
|
45
|
+
networkUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Plain-text lines of the server startup banner (Astro-style, no box):
|
|
49
|
+
*
|
|
50
|
+
* elur-kit v2.4.10 dev server running at:
|
|
51
|
+
* → Local: http://localhost:3000/
|
|
52
|
+
* → Network: http://192.168.1.20:3000/
|
|
53
|
+
*/
|
|
54
|
+
export declare function serverBannerLines(options: ServerBannerOptions): string[];
|
|
55
|
+
/** Prints the server startup banner with brand colors. */
|
|
56
|
+
export declare function serverBanner(options: ServerBannerOptions): void;
|
|
57
|
+
/** First external (LAN) IPv4 address, for the Network URL. */
|
|
58
|
+
export declare function getNetworkAddress(): string | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const bold: (text: string) => string;
|
|
2
|
+
export declare const dim: (text: string) => string;
|
|
3
|
+
export declare const red: (text: string) => string;
|
|
4
|
+
export declare const green: (text: string) => string;
|
|
5
|
+
export declare const yellow: (text: string) => string;
|
|
6
|
+
export declare const cyan: (text: string) => string;
|
|
7
|
+
/** Enables quiet mode: only errors are printed. */
|
|
8
|
+
export declare function setQuiet(value: boolean): void;
|
|
9
|
+
/** Success message with a green check. */
|
|
10
|
+
export declare function success(message: string): void;
|
|
11
|
+
/** Indented info line with a cyan arrow. */
|
|
12
|
+
export declare function info(message: string): void;
|
|
13
|
+
/** Indented detail line (file lists, sub-items). */
|
|
14
|
+
export declare function detail(message: string): void;
|
|
15
|
+
/** Lifecycle event with a dim bracket tag, e.g. [dev], [change]. */
|
|
16
|
+
export declare function event(tag: string, message: string): void;
|
|
17
|
+
/** Warning; suppressed in quiet mode. */
|
|
18
|
+
export declare function warn(message: string): void;
|
|
19
|
+
/** Error; always printed, even in quiet mode. */
|
|
20
|
+
export declare function error(message: string): void;
|
|
21
|
+
/** Formats a byte count as "640 B", "1.5 kB", "2.3 MB". */
|
|
22
|
+
export declare function formatBytes(bytes: number): string;
|
|
23
|
+
/** Formats a duration as "45ms" or "1.23s". */
|
|
24
|
+
export declare function formatDuration(ms: number): string;
|
|
25
|
+
/** Build phase line: checkmark + label + dim duration. */
|
|
26
|
+
export declare function phase(label: string, durationMs: number): void;
|
|
27
|
+
export interface FileEntry {
|
|
28
|
+
path: string;
|
|
29
|
+
bytes: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Renders the generated-file list as aligned "path size" rows (plain text,
|
|
33
|
+
* no color). With more than MAX_FILE_ROWS entries, shows the SHOWN_FILE_ROWS
|
|
34
|
+
* largest plus a "… and N more" line.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fileRows(files: FileEntry[], max?: number, shown?: number): string[];
|
|
37
|
+
/** Prints the generated-file list: dim paths, aligned sizes. */
|
|
38
|
+
export declare function fileList(files: FileEntry[]): void;
|
|
39
|
+
export interface ServerBannerOptions {
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
/** Command label, e.g. "dev" or "preview". */
|
|
43
|
+
command: string;
|
|
44
|
+
localUrl: string;
|
|
45
|
+
networkUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Plain-text lines of the server startup banner (Astro-style, no box):
|
|
49
|
+
*
|
|
50
|
+
* elur-kit v2.4.10 dev server running at:
|
|
51
|
+
* → Local: http://localhost:3000/
|
|
52
|
+
* → Network: http://192.168.1.20:3000/
|
|
53
|
+
*/
|
|
54
|
+
export declare function serverBannerLines(options: ServerBannerOptions): string[];
|
|
55
|
+
/** Prints the server startup banner with brand colors. */
|
|
56
|
+
export declare function serverBanner(options: ServerBannerOptions): void;
|
|
57
|
+
/** First external (LAN) IPv4 address, for the Network URL. */
|
|
58
|
+
export declare function getNetworkAddress(): string | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
/** Max ports tried after the requested one before giving up. */
|
|
3
|
+
export declare const MAX_PORT_FALLBACK_TRIES = 20;
|
|
4
|
+
/**
|
|
5
|
+
* Exit code used when no port in the fallback range is available. The dev
|
|
6
|
+
* supervisor treats it as fatal and does not restart the worker.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PORT_UNAVAILABLE_EXIT_CODE = 78;
|
|
9
|
+
export interface ListenFallbackOptions {
|
|
10
|
+
/** Max fallbacks after the requested port. Default: MAX_PORT_FALLBACK_TRIES. */
|
|
11
|
+
maxTries?: number;
|
|
12
|
+
/** Called when a busy port is skipped: (busyPort, nextPort). */
|
|
13
|
+
onFallback?: (busyPort: number, nextPort: number) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Listens on host:port, falling back to the next port while the failure is
|
|
17
|
+
* EADDRINUSE. Resolves with the port actually bound. Rejects with the
|
|
18
|
+
* original error for non-port failures or when the range is exhausted.
|
|
19
|
+
*/
|
|
20
|
+
export declare function listenWithFallback(server: Server, host: string, port: number, options?: ListenFallbackOptions): Promise<number>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
/** Max ports tried after the requested one before giving up. */
|
|
3
|
+
export declare const MAX_PORT_FALLBACK_TRIES = 20;
|
|
4
|
+
/**
|
|
5
|
+
* Exit code used when no port in the fallback range is available. The dev
|
|
6
|
+
* supervisor treats it as fatal and does not restart the worker.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PORT_UNAVAILABLE_EXIT_CODE = 78;
|
|
9
|
+
export interface ListenFallbackOptions {
|
|
10
|
+
/** Max fallbacks after the requested port. Default: MAX_PORT_FALLBACK_TRIES. */
|
|
11
|
+
maxTries?: number;
|
|
12
|
+
/** Called when a busy port is skipped: (busyPort, nextPort). */
|
|
13
|
+
onFallback?: (busyPort: number, nextPort: number) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Listens on host:port, falling back to the next port while the failure is
|
|
17
|
+
* EADDRINUSE. Resolves with the port actually bound. Rejects with the
|
|
18
|
+
* original error for non-port failures or when the range is exhausted.
|
|
19
|
+
*/
|
|
20
|
+
export declare function listenWithFallback(server: Server, host: string, port: number, options?: ListenFallbackOptions): Promise<number>;
|