void 0.10.8 → 0.10.11

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.
Files changed (116) hide show
  1. package/AGENT_PROMPT.md +4 -0
  2. package/README.md +1 -1
  3. package/dist/{agents-Bmr5tFFb.mjs → agents-CtgBYqld.mjs} +1 -1
  4. package/dist/{auth-cmd-DlgNwByu.mjs → auth-cmd-BqsdZJp5.mjs} +3 -3
  5. package/dist/{better-auth-shared-BvnM9px6.d.mts → better-auth-shared-DealXecJ.d.mts} +1 -1
  6. package/dist/{build-cmd-Br8qL0rA.mjs → build-cmd-Bujrv5q-.mjs} +3 -3
  7. package/dist/{cache-wH-mP8UE.mjs → cache-C11V8Fxq.mjs} +3 -3
  8. package/dist/{cancel-deploy-BEBOEgtu.mjs → cancel-deploy-fwFYF04b.mjs} +2 -2
  9. package/dist/cli/cli.mjs +175 -43
  10. package/dist/cli/env-schema-probe.d.mts +96 -0
  11. package/dist/cli/env-schema-probe.mjs +272 -0
  12. package/dist/{client-Cj96iiBH.mjs → client-Gb71-XkG.mjs} +20 -2
  13. package/dist/{config-BdUctCZD.mjs → config-CutEMNGJ.mjs} +3 -3
  14. package/dist/{config-1twldYCW.mjs → config-E03l1C_h.mjs} +8 -2
  15. package/dist/{create-project-DD9n8Ho-.mjs → create-project-DsYvl3TB.mjs} +3 -3
  16. package/dist/{db-DjKE2-A-.mjs → db-PZBsLSGb.mjs} +27 -27
  17. package/dist/{delete-D2kr3Kmk.mjs → delete-mh6p-zkQ.mjs} +3 -3
  18. package/dist/deploy-DT8wsPZd.mjs +6990 -0
  19. package/dist/{discover-BuVVSAum.mjs → discover-CJHyvYfR.mjs} +2 -2
  20. package/dist/dist-DaKKDf8D.mjs +41 -0
  21. package/dist/{domain-_luIsM_B.mjs → domain-DiaNQbrl.mjs} +2 -2
  22. package/dist/entry-D7yy4xVH.mjs +100 -0
  23. package/dist/{env-CO5XAS9t.mjs → env-AAHU02L6.mjs} +5 -5
  24. package/dist/env-mask-Dd47NbR6.mjs +90 -0
  25. package/dist/env-public-BfiLcMBk.d.mts +140 -0
  26. package/dist/env-raw-Cx8ElDdj.mjs +58 -0
  27. package/dist/{env-types-D51bnR-c.mjs → env-types-QBj-ndax.mjs} +2 -2
  28. package/dist/env-validation-BdDlGhbN.mjs +1069 -0
  29. package/dist/{gen-Cf79J4aw.mjs → gen-oup1xBN0.mjs} +8 -8
  30. package/dist/{github-cmd-B6OX9c7d.mjs → github-cmd-BdNaOVNa.mjs} +102 -3
  31. package/dist/{handler-imD0UVDT.d.mts → handler-Cjh8uM3Y.d.mts} +1 -1
  32. package/dist/{headers-BwvFGhkx.mjs → headers-BQknpzkn.mjs} +2 -2
  33. package/dist/index.d.mts +14 -2
  34. package/dist/index.mjs +102 -55
  35. package/dist/{init-KirOzVDs.mjs → init-Dl2PKuQn.mjs} +13 -14
  36. package/dist/{link-CUmiosyb.mjs → link-CdGHSIy-.mjs} +4 -4
  37. package/dist/{list-3GEw7b6m.mjs → list-CPwFDZ_c.mjs} +3 -3
  38. package/dist/{login-DJReaT_Q.mjs → login-BT3H8PN3.mjs} +2 -2
  39. package/dist/{logs-D-rQ56Lq.mjs → logs-Bt313ax7.mjs} +3 -2
  40. package/dist/{mcp-D7yc0dXY.mjs → mcp-DoM3_nhd.mjs} +7 -2
  41. package/dist/{node-yFFk626c.mjs → node-BDx8pmhq.mjs} +5 -5
  42. package/dist/{package-json-B0NuUWGd.mjs → package-json-Cx1osYo6.mjs} +1 -1
  43. package/dist/pages/client.d.mts +1 -1
  44. package/dist/pages/index.d.mts +1 -23
  45. package/dist/pages/index.mjs +5 -5
  46. package/dist/pages/islands-plugin.mjs +2 -2
  47. package/dist/pages/protocol.d.mts +2 -2
  48. package/dist/{plugin-inference-CJxi_fWI.mjs → plugin-inference-DMeavIJ6.mjs} +4 -98
  49. package/dist/{prepare-C_cVurhP.mjs → prepare-DOBTY0o4.mjs} +10 -10
  50. package/dist/preset-BGrvB4Bl.mjs +539 -0
  51. package/dist/{project-cmd-DnU7u9QF.mjs → project-cmd-D_w-4w5B.mjs} +13 -9
  52. package/dist/{project-paths-tpdR1mJR.mjs → project-paths-BQd7OmIo.mjs} +1 -1
  53. package/dist/project-paths-GpziKeQQ.d.mts +25 -0
  54. package/dist/{project-tsconfig-D9uSVVpA.mjs → project-tsconfig-B-QtXjLQ.mjs} +2 -2
  55. package/dist/{protocol-6hTJ04T1.d.mts → protocol-Bnb0LFp3.d.mts} +1 -1
  56. package/dist/provision-BLrCEBbI.mjs +2557 -0
  57. package/dist/requests-B8sZxaFM.mjs +50 -0
  58. package/dist/{resolve-project-D2HI3TrG.mjs → resolve-project-BBMtLLV9.mjs} +1 -1
  59. package/dist/{rollback-Yh7bCKob.mjs → rollback-CkvTFXx5.mjs} +2 -2
  60. package/dist/{route-types-CfKfhbIg.mjs → route-types-COI2DsZv.mjs} +2 -2
  61. package/dist/{runner-h272wcPj.mjs → runner-kapo9aPs.mjs} +3 -3
  62. package/dist/{runner-pg-waxJOnBb.mjs → runner-pg-CHM76xuC.mjs} +1 -1
  63. package/dist/runtime/ai.mjs +113 -15
  64. package/dist/runtime/better-auth-pg.d.mts +1 -1
  65. package/dist/runtime/better-auth.d.mts +1 -1
  66. package/dist/runtime/env-public.d.mts +1 -139
  67. package/dist/runtime/env-public.mjs +10 -93
  68. package/dist/runtime/env.d.mts +14 -1
  69. package/dist/runtime/env.mjs +24 -10
  70. package/dist/runtime/handler.d.mts +1 -1
  71. package/dist/runtime/isr-cache.d.mts +207 -0
  72. package/dist/runtime/isr-cache.mjs +523 -0
  73. package/dist/runtime/isr.d.mts +4 -3
  74. package/dist/runtime/isr.mjs +44 -5
  75. package/dist/runtime/live.d.mts +1 -1
  76. package/dist/runtime/live.mjs +1 -1
  77. package/dist/runtime/sandbox.mjs +1 -1
  78. package/dist/runtime/validator.d.mts +1 -1
  79. package/dist/runtime/ws-server.d.mts +1 -1
  80. package/dist/runtime/ws.d.mts +2 -2
  81. package/dist/{scan-Dp_Gyzs3.mjs → scan-DEwlM_Xy.mjs} +2 -2
  82. package/dist/{scan-i7Yz54fv.mjs → scan-DYXkrasO.mjs} +4 -4
  83. package/dist/{secret-u7FRvg8d.mjs → secret-Dt32J6RI.mjs} +3 -3
  84. package/dist/{skills-DsdNDtX3.mjs → skills-CLjN0uUO.mjs} +2 -2
  85. package/dist/{subcommand-prompt-DtES-oP6.mjs → subcommand-prompt-BzV8iQZo.mjs} +1 -1
  86. package/dist/sveltekit.mjs +1 -1
  87. package/dist/{validate-DT7nFMlf.mjs → validate-Cw_RLeTj.mjs} +1 -1
  88. package/dist/{yarn-pnp-CW8LB6g_.mjs → yarn-pnp-DJn3SAHF.mjs} +1 -1
  89. package/getting-started-prompt.txt +3 -1
  90. package/package.json +6 -3
  91. package/schema.json +11 -0
  92. package/skills/void/SKILL.md +34 -33
  93. package/skills/void/docs/guide/auth.md +8 -0
  94. package/skills/void/docs/guide/deployment.md +1 -1
  95. package/skills/void/docs/guide/env-vars.md +14 -6
  96. package/skills/void/docs/guide/queues.md +4 -0
  97. package/skills/void/docs/integrations/cloudflare.md +171 -5
  98. package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +4 -0
  99. package/skills/void/docs/node_modules/void/{AGENTS.md → CLAUDE.md} +14 -0
  100. package/skills/void/docs/node_modules/void/README.md +1 -1
  101. package/skills/void/docs/node_modules/void/node_modules/@types/proper-lockfile/README.md +51 -0
  102. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathslash/README.md +64 -0
  103. package/skills/void/docs/node_modules/void/node_modules/pathslash/README.md +64 -0
  104. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/CHANGELOG.md +108 -0
  105. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/README.md +183 -0
  106. package/skills/void/docs/node_modules/void/skills/void/SKILL.md +34 -33
  107. package/skills/void/docs/node_modules/void/test/e2e/README.md +85 -0
  108. package/skills/void/docs/reference/cli.md +97 -14
  109. package/dist/deploy-C4PbkFyE.mjs +0 -3705
  110. package/dist/dotenv-D_UbC_vc.mjs +0 -173
  111. package/dist/env-raw-CoS20LHP.mjs +0 -32
  112. package/dist/env-validation-CeC2FL66.mjs +0 -163
  113. package/dist/pathe.M-eThtNZ-CQzLbt4c.mjs +0 -150
  114. package/dist/preset-CVvwCeIy.mjs +0 -208
  115. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathe/README.md +0 -73
  116. package/skills/void/docs/node_modules/void/node_modules/pathe/README.md +0 -73
@@ -0,0 +1,523 @@
1
+ //#region src/runtime/isr-cache.ts
2
+ /**
3
+ * Build the normalized path segment for an ISR-cached page.
4
+ * Root path `/` maps to `__index__` to avoid trailing-slash ambiguity.
5
+ */
6
+ function normalizeIsrPath(pathname) {
7
+ let normalized = pathname;
8
+ if (normalized === "/") normalized = "__index__";
9
+ else {
10
+ normalized = normalized.startsWith("/") ? normalized.slice(1) : normalized;
11
+ if (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
12
+ }
13
+ return normalized;
14
+ }
15
+ /**
16
+ * Inverse of `normalizeIsrPath`: turn a normalized segment back into a
17
+ * pathname starting with `/`.
18
+ */
19
+ function denormalizeIsrPath(normalized) {
20
+ return normalized === "__index__" ? "/" : `/${normalized}`;
21
+ }
22
+ /**
23
+ * Canonicalize an inbound pathname to the form used inside edge cache URLs.
24
+ * Equivalent to `denormalizeIsrPath(normalizeIsrPath(pathname))` — strips any
25
+ * trailing slash except for root.
26
+ */
27
+ function canonicalIsrPathname(pathname) {
28
+ return denormalizeIsrPath(normalizeIsrPath(pathname));
29
+ }
30
+ /**
31
+ * KV cache key for an ISR-cached page. Deployment- AND host-scoped: cached HTML
32
+ * cannot outlive the hashed assets of the deployment that produced it (a fresh
33
+ * `wrangler deploy` mints a new deployment id → cold cache), and one host's
34
+ * cached content never bleeds into another host served by the same worker
35
+ * (custom domain + workers.dev, apex + www, etc.).
36
+ */
37
+ function isrKey(deploymentId, host, pathname) {
38
+ return `void-isr/${deploymentId}/${host}/${normalizeIsrPath(pathname)}`;
39
+ }
40
+ /**
41
+ * Host-registry key. Every host that has written cached content registers itself
42
+ * under `void-isr-hosts/<id>/<host>` (value `''`) so a purge with no request host
43
+ * (cron / queue `revalidate({ paths })`) can enumerate the hosts to purge.
44
+ * Scoped by deployment id so registrations from an old deploy do not leak.
45
+ */
46
+ function hostRegistryKey(deploymentId, host) {
47
+ return `void-isr-hosts/${deploymentId}/${host}`;
48
+ }
49
+ /**
50
+ * Edge-cache (`caches.default`) key for the HTML variant. The public URL stays
51
+ * clean; this internal key carries the deployment id so a new deploy never
52
+ * reads HTML produced by an older deploy.
53
+ */
54
+ function htmlEdgeKey(host, pathname, id) {
55
+ return `https://${host}${canonicalIsrPathname(pathname)}?__void_isr=${id}`;
56
+ }
57
+ /**
58
+ * Edge-cache key for the JSON (pages-protocol) variant. Separated from HTML by
59
+ * the `&__void=json` marker.
60
+ */
61
+ function jsonEdgeKey(host, pathname, id) {
62
+ return `${htmlEdgeKey(host, pathname, id)}&__void=json`;
63
+ }
64
+ const MAX_CACHE = 256;
65
+ const literalRegexCache = /* @__PURE__ */ new Map();
66
+ /**
67
+ * Match a pattern against a pathname using LITERAL semantics — no named
68
+ * captures. Two wildcards are special: `*` matches any characters INCLUDING `/`
69
+ * (a splat), and a WHOLE-segment `:name` (preceded by `/` or start, followed by
70
+ * `/` or end) matches exactly ONE path segment (`[^/]+`). A mid-segment literal
71
+ * `:` (e.g. `/api/v1:health`) is left alone. This is a strict SUPERSET of the
72
+ * old `*`-only matcher (old `*`-only manifests match identically). Malformed
73
+ * patterns yield `false` rather than throwing.
74
+ */
75
+ function matchLiteralPattern(pattern, pathname) {
76
+ let regex = literalRegexCache.get(pattern);
77
+ if (regex) {
78
+ literalRegexCache.delete(pattern);
79
+ literalRegexCache.set(pattern, regex);
80
+ } else {
81
+ try {
82
+ const escaped = pattern.replace(/(^|\/):[A-Za-z0-9_]+(?=\/|$)/g, "$1").replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*").replaceAll("", "[^/]+");
83
+ regex = new RegExp(`^${escaped}$`);
84
+ } catch {
85
+ return false;
86
+ }
87
+ if (literalRegexCache.size >= MAX_CACHE) {
88
+ const first = literalRegexCache.keys().next();
89
+ if (!first.done) literalRegexCache.delete(first.value);
90
+ }
91
+ literalRegexCache.set(pattern, regex);
92
+ }
93
+ try {
94
+ return regex.test(pathname);
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+ /**
100
+ * Pack response headers + body into a single ArrayBuffer.
101
+ * Format: [4 bytes uint32 LE header length][UTF-8 header JSON][raw body]
102
+ */
103
+ function packIsrValue(headers, body) {
104
+ const headerJson = new TextEncoder().encode(JSON.stringify(headers));
105
+ const buffer = new ArrayBuffer(4 + headerJson.byteLength + body.byteLength);
106
+ new DataView(buffer).setUint32(0, headerJson.byteLength, true);
107
+ new Uint8Array(buffer, 4, headerJson.byteLength).set(headerJson);
108
+ new Uint8Array(buffer, 4 + headerJson.byteLength).set(new Uint8Array(body));
109
+ return buffer;
110
+ }
111
+ /**
112
+ * Unpack headers + body from a packed ArrayBuffer.
113
+ * Returns null if the buffer is too small or malformed.
114
+ */
115
+ function unpackIsrValue(buffer) {
116
+ if (buffer.byteLength < 4) return null;
117
+ const headerLen = new DataView(buffer).getUint32(0, true);
118
+ if (buffer.byteLength < 4 + headerLen) return null;
119
+ try {
120
+ const headerBytes = new Uint8Array(buffer, 4, headerLen);
121
+ return {
122
+ headers: JSON.parse(new TextDecoder().decode(headerBytes)),
123
+ body: buffer.slice(4 + headerLen)
124
+ };
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+ /**
130
+ * Read an ISR cache entry from KV. Returns null on cache miss, malformed
131
+ * metadata, or ANY KV error (fail-open → caller renders live). The KV key is
132
+ * deployment-scoped via `env.__VOID_ISR_DEPLOYMENT_ID` (see module header) and
133
+ * host-scoped via `host` so one host never reads another host's cached bytes.
134
+ */
135
+ async function readIsrCache(env, host, pathname) {
136
+ const kv = env.ISR_CACHE;
137
+ if (!kv) return null;
138
+ const key = isrKey(env.__VOID_ISR_DEPLOYMENT_ID ?? "", host, pathname);
139
+ let value;
140
+ let metadata;
141
+ try {
142
+ const result = await kv.getWithMetadata(key, "arrayBuffer");
143
+ value = result.value;
144
+ metadata = result.metadata;
145
+ } catch {
146
+ return null;
147
+ }
148
+ if (!value || !metadata) return null;
149
+ const unpacked = unpackIsrValue(value);
150
+ if (!unpacked) return null;
151
+ if (!Number.isInteger(metadata.s) || metadata.s < 200 || metadata.s > 599) return null;
152
+ if (!Number.isFinite(metadata.c) || metadata.c <= 0 || !Number.isFinite(metadata.t) || metadata.t <= 0) return null;
153
+ try {
154
+ new Headers(unpacked.headers);
155
+ } catch {
156
+ return null;
157
+ }
158
+ return {
159
+ body: new ReadableStream({ start(controller) {
160
+ controller.enqueue(new Uint8Array(unpacked.body));
161
+ controller.close();
162
+ } }),
163
+ status: metadata.s,
164
+ headers: unpacked.headers,
165
+ createdAt: metadata.c,
166
+ ttl: metadata.t
167
+ };
168
+ }
169
+ /**
170
+ * Write an ISR cache entry to KV. The `response.clone()` is taken SYNCHRONOUSLY
171
+ * before any `await` so a caller that consumes `response.body` for the client
172
+ * response (after kicking us off via `ctx.waitUntil`) cannot race the body
173
+ * away. Any KV error is swallowed (fail-open on the write path).
174
+ */
175
+ async function writeIsrCache(env, host, pathname, response, ttl) {
176
+ const kv = env.ISR_CACHE;
177
+ if (!kv) return;
178
+ const responseClone = response.clone();
179
+ const headers = serializeHeaders(responseClone);
180
+ const status = responseClone.status;
181
+ const id = env.__VOID_ISR_DEPLOYMENT_ID ?? "";
182
+ try {
183
+ const packed = packIsrValue(headers, await responseClone.arrayBuffer());
184
+ await kv.put(isrKey(id, host, pathname), packed, { metadata: {
185
+ s: status,
186
+ c: Date.now(),
187
+ t: ttl
188
+ } });
189
+ } catch {}
190
+ await registerIsrHost(kv, id, host);
191
+ }
192
+ /**
193
+ * In-isolate guard: `${id}/${host}` values whose host-registry key this isolate
194
+ * has already written (or attempted). Bounds host registration to ~one KV write
195
+ * per (isolate, deployment, host) instead of one per page write.
196
+ */
197
+ const registeredHosts = /* @__PURE__ */ new Set();
198
+ async function registerIsrHost(kv, deploymentId, host) {
199
+ const guardKey = `${deploymentId}/${host}`;
200
+ if (registeredHosts.has(guardKey)) return;
201
+ registeredHosts.add(guardKey);
202
+ try {
203
+ await kv.put(hostRegistryKey(deploymentId, host), "");
204
+ } catch {
205
+ registeredHosts.delete(guardKey);
206
+ }
207
+ }
208
+ /**
209
+ * List every host that has registered cached content for a deployment. Used by
210
+ * the on-demand `revalidate({ paths })` purge when there is no request host
211
+ * (cron / queue). Fail-open: a registry read error yields the hosts collected so
212
+ * far (or none) rather than throwing — degraded purge coverage, never a 500.
213
+ */
214
+ async function listIsrHosts(kv, deploymentId) {
215
+ const prefix = `void-isr-hosts/${deploymentId}/`;
216
+ const hosts = [];
217
+ try {
218
+ let cursor;
219
+ do {
220
+ const list = await kv.list({
221
+ prefix,
222
+ cursor
223
+ });
224
+ for (const key of list.keys) hosts.push(key.name.slice(prefix.length));
225
+ cursor = list.list_complete ? void 0 : list.cursor;
226
+ } while (cursor);
227
+ } catch {}
228
+ return hosts;
229
+ }
230
+ /**
231
+ * Delete an ISR cache entry from KV. Used when a worker permanently opts out
232
+ * via `x-revalidate: 0`. Any KV error is swallowed (fail-open).
233
+ */
234
+ async function deleteIsrCache(env, host, pathname) {
235
+ const kv = env.ISR_CACHE;
236
+ if (!kv) return;
237
+ try {
238
+ await kv.delete(isrKey(env.__VOID_ISR_DEPLOYMENT_ID ?? "", host, pathname));
239
+ } catch {}
240
+ }
241
+ /** Check if a cached entry is stale. */
242
+ function isStale(entry) {
243
+ return Date.now() - entry.createdAt > entry.ttl * 1e3;
244
+ }
245
+ /** Build a Response from an ISR cache entry. */
246
+ function isrCacheResponse(entry, stale) {
247
+ const headers = new Headers(entry.headers);
248
+ headers.set("x-isr-cache", stale ? "STALE" : "HIT");
249
+ if (stale) {
250
+ const staleTtl = Math.min(10, entry.ttl);
251
+ headers.set("Cache-Control", `public, s-maxage=${staleTtl}, max-age=0, must-revalidate`);
252
+ } else {
253
+ const remainingSeconds = Math.max(0, Math.floor((entry.createdAt + entry.ttl * 1e3 - Date.now()) / 1e3));
254
+ headers.set("Cache-Control", `public, s-maxage=${remainingSeconds}, max-age=0, must-revalidate`);
255
+ }
256
+ return new Response(entry.body, {
257
+ status: entry.status,
258
+ headers
259
+ });
260
+ }
261
+ /**
262
+ * Read the `x-revalidate` header from a worker response.
263
+ * Returns the override TTL, or the default. A value of 0 means "do not cache".
264
+ */
265
+ function resolveRevalidateTtl(response, defaultTtl) {
266
+ const header = response.headers.get("x-revalidate");
267
+ if (header === null) return defaultTtl;
268
+ const value = parseInt(header, 10);
269
+ if (isNaN(value) || value < 0) return defaultTtl;
270
+ return value;
271
+ }
272
+ function acceptsEventStream(request) {
273
+ return request.headers.get("Accept")?.toLowerCase().split(",").some((part) => part.split(";", 1)[0]?.trim() === "text/event-stream") ?? false;
274
+ }
275
+ /**
276
+ * ISR should be bypassed for requests with credentials and EventSource
277
+ * requests. Pages protocol navigation (X-VoidPages) uses a separate JSON edge
278
+ * cache.
279
+ */
280
+ function shouldBypassIsr(request) {
281
+ return request.headers.has("Cookie") || request.headers.has("Authorization") || acceptsEventStream(request);
282
+ }
283
+ /**
284
+ * Resolve the ISR TTL for a given pathname.
285
+ * Accepts the `revalidate` config (number, object, or undefined). For the
286
+ * object form, the first matching pattern wins.
287
+ */
288
+ function resolvePathTtl(revalidate, pathname) {
289
+ if (revalidate == null) return 0;
290
+ if (typeof revalidate === "number") return revalidate;
291
+ for (const [pattern, ttl] of Object.entries(revalidate)) if (matchLiteralPattern(pattern, pathname)) return ttl;
292
+ return 0;
293
+ }
294
+ /** Extract response headers as a plain object, skipping ISR-managed headers. */
295
+ const SKIP_HEADERS = /* @__PURE__ */ new Set([
296
+ "cache-control",
297
+ "x-isr-cache",
298
+ "x-void-page-data",
299
+ "set-cookie"
300
+ ]);
301
+ function serializeHeaders(response) {
302
+ const result = {};
303
+ response.headers.forEach((value, key) => {
304
+ if (!SKIP_HEADERS.has(key.toLowerCase())) result[key] = value;
305
+ });
306
+ return result;
307
+ }
308
+ const PAGE_DATA_SCRIPT_RE = /<script\b[^>]*\bid=(["'])__VOID_PAGE_DATA__\1[^>]*>([\s\S]*?)<\/script>/i;
309
+ function normalizePageDataForJsonCache(raw) {
310
+ try {
311
+ const parsed = JSON.parse(raw);
312
+ if (parsed.deferred !== void 0 || parsed.deferredKeys !== void 0) return null;
313
+ return raw;
314
+ } catch {
315
+ return null;
316
+ }
317
+ }
318
+ function extractPageDataFromHtml(html) {
319
+ const match = html.match(PAGE_DATA_SCRIPT_RE);
320
+ if (!match) return null;
321
+ return normalizePageDataForJsonCache(match[2]);
322
+ }
323
+ const CLIENT_REVALIDATE_CACHE_CONTROL = "public, max-age=0, must-revalidate";
324
+ function clientRevalidateResponse(response) {
325
+ const res = new Response(response.body, response);
326
+ res.headers.set("Cache-Control", CLIENT_REVALIDATE_CACHE_CONTROL);
327
+ return res;
328
+ }
329
+ function isEventStreamResponse(response) {
330
+ return response.headers.get("content-type")?.toLowerCase().split(";", 1)[0]?.trim() === "text/event-stream";
331
+ }
332
+ function jsonCacheResponse(pageData, ttl, state) {
333
+ return new Response(pageData, { headers: {
334
+ "content-type": "application/json",
335
+ "X-VoidPages": "true",
336
+ Vary: "X-VoidPages",
337
+ "Cache-Control": `public, s-maxage=${ttl}, max-age=0, must-revalidate`,
338
+ "x-isr-cache": state
339
+ } });
340
+ }
341
+ /**
342
+ * Populate the JSON edge cache from a response's embedded page data (the
343
+ * `x-void-page-data` header or the `__VOID_PAGE_DATA__` inline script). No-ops
344
+ * when there is no extractable page data. Any cache error is swallowed
345
+ * (fail-open) so this is always safe to fire inside `ctx.waitUntil`.
346
+ */
347
+ async function populateJsonEdgeCache(cache, cacheKey, response, ttl, state) {
348
+ if (!cache) return;
349
+ try {
350
+ const fromHeader = response.headers.get("x-void-page-data");
351
+ const pageData = fromHeader ? normalizePageDataForJsonCache(fromHeader) : extractPageDataFromHtml(await response.clone().text());
352
+ if (!pageData) return;
353
+ await cache.put(cacheKey, jsonCacheResponse(pageData, ttl, state));
354
+ } catch {}
355
+ }
356
+ /**
357
+ * The per-colo edge cache (`caches.default`), or `undefined` when the Cache API
358
+ * is absent/degraded. Accessed through a cast because this package has both
359
+ * `@types/node` and `@cloudflare/workers-types` in scope, and Node's ambient
360
+ * `CacheStorage` (which lacks `.default`) otherwise shadows the Workers one on
361
+ * the `caches` global.
362
+ *
363
+ * Non-throwing by contract: dereferencing `caches.default` can throw if the
364
+ * Cache API is missing (e.g. a non-worker runtime) or degraded. We swallow that
365
+ * here and return `undefined` so `handleIsr` never rejects before its fail-open
366
+ * guards run — the serving invariant is a degraded cache must render live, never
367
+ * 500. Every call site guards `undefined` (via `if (!cache)` or `cache?.`).
368
+ */
369
+ function edgeCache() {
370
+ try {
371
+ return caches.default;
372
+ } catch {
373
+ return;
374
+ }
375
+ }
376
+ async function safeCachePut(cache, key, response) {
377
+ if (!cache) return;
378
+ try {
379
+ await cache.put(key, response);
380
+ } catch {}
381
+ }
382
+ async function safeCacheDelete(cache, key) {
383
+ if (!cache) return;
384
+ try {
385
+ await cache.delete(key);
386
+ } catch {}
387
+ }
388
+ /**
389
+ * Build a new Request with the `X-VoidPages` header removed. Headers on an
390
+ * existing Request are immutable, so we must clone into a fresh Request. ISR
391
+ * only runs for GET, so there is no body to preserve.
392
+ */
393
+ function cloneWithoutVoidPages(request) {
394
+ const headers = new Headers(request.headers);
395
+ headers.delete("X-VoidPages");
396
+ return new Request(request, { headers });
397
+ }
398
+ /**
399
+ * Revalidate an ISR page in the background. Re-renders as HTML (X-VoidPages
400
+ * stripped), writes fresh HTML to KV + HTML edge cache, and populates the JSON
401
+ * edge cache from embedded page data. Errors are caught and logged — serve
402
+ * stale, don't retry. `ttl === 0` means the worker permanently opted out:
403
+ * delete the KV entry and both edge variants.
404
+ */
405
+ async function revalidateInBackground(env, request, host, pathname, defaultTtl, keys, render) {
406
+ try {
407
+ const response = await render(cloneWithoutVoidPages(request));
408
+ if (isEventStreamResponse(response)) return;
409
+ const ttl = resolveRevalidateTtl(response, defaultTtl);
410
+ const cache = edgeCache();
411
+ if (ttl > 0 && response.status === 200) {
412
+ await writeIsrCache(env, host, pathname, response, ttl);
413
+ const headers = new Headers(response.headers);
414
+ headers.set("x-isr-cache", "HIT");
415
+ headers.set("Cache-Control", `public, s-maxage=${ttl}, max-age=0, must-revalidate`);
416
+ headers.delete("set-cookie");
417
+ headers.delete("x-void-page-data");
418
+ await safeCachePut(cache, keys.htmlKey, new Response(response.clone().body, {
419
+ status: response.status,
420
+ headers
421
+ }));
422
+ await populateJsonEdgeCache(cache, keys.jsonKey, response, ttl, "HIT");
423
+ } else if (ttl === 0) {
424
+ await deleteIsrCache(env, host, pathname);
425
+ await safeCacheDelete(cache, keys.htmlKey);
426
+ await safeCacheDelete(cache, keys.jsonKey);
427
+ }
428
+ } catch (err) {
429
+ console.error("ISR revalidation failed:", err);
430
+ }
431
+ }
432
+ /**
433
+ * Run the ISR caching ladder for a request.
434
+ *
435
+ * Returns a cached/rendered `Response` when ISR applies, or `null` when the
436
+ * caller should render live itself (request not ISR-eligible, or a read/hit
437
+ * cache op failed open).
438
+ *
439
+ * Ladder:
440
+ * - Bail (`null`) if not GET, a Range request, `shouldBypassIsr`, or the path
441
+ * has no ISR TTL. Range requests bail BEFORE any cache read/write so they
442
+ * render live (correct 206) and never touch the shared full-response entry.
443
+ * - JSON (X-VoidPages) path: edge match → hit serves `clientRevalidateResponse`;
444
+ * miss renders live JSON and returns it — the JSON path never writes the edge
445
+ * cache. The JSON edge variant is warmed only as a side-effect of the HTML
446
+ * render path (cold colo re-renders JSON).
447
+ * - HTML path: L1 edge match → L2 KV. Hit serves `isrCacheResponse`
448
+ * (stale hits also queue a background revalidation). Miss renders live and,
449
+ * when cacheable, writes KV + JSON edge + HTML edge in the background.
450
+ */
451
+ async function handleIsr(request, env, ctx, options, render) {
452
+ const { revalidate, deploymentId } = options;
453
+ if (request.method !== "GET") return null;
454
+ if (request.headers.has("range")) return null;
455
+ if (shouldBypassIsr(request)) return null;
456
+ const url = new URL(request.url);
457
+ const pathname = url.pathname;
458
+ const isrTtl = resolvePathTtl(revalidate, pathname);
459
+ if (isrTtl <= 0) return null;
460
+ env.__VOID_ISR_DEPLOYMENT_ID ??= deploymentId;
461
+ const host = url.hostname;
462
+ const cache = edgeCache();
463
+ if (request.headers.has("X-VoidPages")) {
464
+ const jsonKey = jsonEdgeKey(host, pathname, deploymentId);
465
+ let jsonCached;
466
+ try {
467
+ jsonCached = await cache?.match(jsonKey);
468
+ } catch {
469
+ return null;
470
+ }
471
+ if (jsonCached) return clientRevalidateResponse(jsonCached);
472
+ return render(request);
473
+ }
474
+ const htmlKey = htmlEdgeKey(host, pathname, deploymentId);
475
+ const jsonKey = jsonEdgeKey(host, pathname, deploymentId);
476
+ let edgeCached;
477
+ try {
478
+ edgeCached = await cache?.match(htmlKey);
479
+ } catch {
480
+ return null;
481
+ }
482
+ if (edgeCached) return clientRevalidateResponse(edgeCached);
483
+ const cached = await readIsrCache(env, host, pathname);
484
+ if (cached) try {
485
+ const stale = isStale(cached);
486
+ if (stale) ctx.waitUntil(revalidateInBackground(env, request, host, pathname, isrTtl, {
487
+ htmlKey,
488
+ jsonKey
489
+ }, render));
490
+ const cacheResponse = isrCacheResponse(cached, stale);
491
+ ctx.waitUntil(safeCachePut(cache, htmlKey, cacheResponse.clone()));
492
+ return clientRevalidateResponse(cacheResponse);
493
+ } catch {
494
+ return null;
495
+ }
496
+ const response = await render(request);
497
+ if (isEventStreamResponse(response)) return response;
498
+ const ttl = resolveRevalidateTtl(response, isrTtl);
499
+ if (ttl > 0 && response.status === 200) {
500
+ ctx.waitUntil(writeIsrCache(env, host, pathname, response, ttl));
501
+ ctx.waitUntil(populateJsonEdgeCache(cache, jsonKey, response, ttl, "MISS"));
502
+ const cacheHeaders = new Headers(response.headers);
503
+ cacheHeaders.set("Cache-Control", `public, s-maxage=${ttl}, max-age=0, must-revalidate`);
504
+ cacheHeaders.set("x-isr-cache", "MISS");
505
+ cacheHeaders.delete("x-void-page-data");
506
+ cacheHeaders.delete("set-cookie");
507
+ const cacheResponse = new Response(response.clone().body, {
508
+ status: response.status,
509
+ headers: cacheHeaders
510
+ });
511
+ ctx.waitUntil(safeCachePut(cache, htmlKey, cacheResponse));
512
+ const clientHeaders = new Headers(cacheHeaders);
513
+ clientHeaders.set("Cache-Control", CLIENT_REVALIDATE_CACHE_CONTROL);
514
+ for (const cookie of response.headers.getSetCookie()) clientHeaders.append("set-cookie", cookie);
515
+ return new Response(response.body, {
516
+ status: response.status,
517
+ headers: clientHeaders
518
+ });
519
+ }
520
+ return response;
521
+ }
522
+ //#endregion
523
+ export { canonicalIsrPathname, clientRevalidateResponse, cloneWithoutVoidPages, deleteIsrCache, denormalizeIsrPath, extractPageDataFromHtml, handleIsr, hostRegistryKey, htmlEdgeKey, isEventStreamResponse, isStale, isrCacheResponse, isrKey, jsonEdgeKey, listIsrHosts, matchLiteralPattern, normalizeIsrPath, normalizePageDataForJsonCache, packIsrValue, populateJsonEdgeCache, readIsrCache, resolvePathTtl, resolveRevalidateTtl, serializeHeaders, shouldBypassIsr, unpackIsrValue, writeIsrCache };
@@ -6,9 +6,10 @@ type RevalidateOptions = {
6
6
  /**
7
7
  * Revalidate ISR-cached pages on demand.
8
8
  *
9
- * In production, calls the platform proxy via service binding which forwards
10
- * to the API for R2 deletion + edge cache purge. In local dev, this is a
11
- * no-op (no ISR cache to purge).
9
+ * On managed Void, calls the platform proxy via service binding which forwards
10
+ * to the API for R2 deletion + edge cache purge. On self-host, purges this
11
+ * worker's own ISR_CACHE (KV) entries plus the caches.default edge variants.
12
+ * When neither is configured (e.g. local dev with no ISR), this is a no-op.
12
13
  *
13
14
  * @example
14
15
  * ```ts
@@ -1,11 +1,13 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-CoS20LHP.mjs";
1
+ import { a as getRawRuntimeEnv } from "../env-raw-Cx8ElDdj.mjs";
2
+ import { denormalizeIsrPath, htmlEdgeKey, isrKey, jsonEdgeKey, listIsrHosts } from "./isr-cache.mjs";
2
3
  //#region src/runtime/isr.ts
3
4
  /**
4
5
  * Revalidate ISR-cached pages on demand.
5
6
  *
6
- * In production, calls the platform proxy via service binding which forwards
7
- * to the API for R2 deletion + edge cache purge. In local dev, this is a
8
- * no-op (no ISR cache to purge).
7
+ * On managed Void, calls the platform proxy via service binding which forwards
8
+ * to the API for R2 deletion + edge cache purge. On self-host, purges this
9
+ * worker's own ISR_CACHE (KV) entries plus the caches.default edge variants.
10
+ * When neither is configured (e.g. local dev with no ISR), this is a no-op.
9
11
  *
10
12
  * @example
11
13
  * ```ts
@@ -21,7 +23,44 @@ import { i as getRawRuntimeEnv } from "../env-raw-CoS20LHP.mjs";
21
23
  async function revalidate(options) {
22
24
  const rawEnv = getRawRuntimeEnv();
23
25
  const proxy = rawEnv.__VOID_PROXY;
24
- if (!proxy) return;
26
+ if (!proxy) {
27
+ const kv = rawEnv.ISR_CACHE;
28
+ const id = rawEnv.__VOID_ISR_DEPLOYMENT_ID;
29
+ if (!kv || !id) return;
30
+ const requestHost = rawEnv.__VOID_ISR_HOST;
31
+ const purge = async (host, path) => {
32
+ await kv.delete(isrKey(id, host, path));
33
+ try {
34
+ const cache = caches.default;
35
+ await cache.delete(htmlEdgeKey(host, path, id));
36
+ await cache.delete(jsonEdgeKey(host, path, id));
37
+ } catch {}
38
+ };
39
+ if (options.all) {
40
+ const prefix = `void-isr/${id}/`;
41
+ let cursor;
42
+ do {
43
+ const list = await kv.list({
44
+ prefix,
45
+ cursor
46
+ });
47
+ for (const key of list.keys) {
48
+ const tail = key.name.slice(prefix.length);
49
+ const slash = tail.indexOf("/");
50
+ if (slash < 0) continue;
51
+ await purge(tail.slice(0, slash), denormalizeIsrPath(tail.slice(slash + 1)));
52
+ }
53
+ cursor = list.list_complete ? void 0 : list.cursor;
54
+ } while (cursor);
55
+ } else {
56
+ const paths = options.paths ?? [];
57
+ if (paths.length === 0) return;
58
+ const hosts = new Set(await listIsrHosts(kv, id));
59
+ if (requestHost) hosts.add(requestHost);
60
+ for (const host of hosts) for (const path of paths) await purge(host, path);
61
+ }
62
+ return;
63
+ }
25
64
  const projectId = rawEnv.__PROJECT_ID;
26
65
  const token = rawEnv.__VOID_PROXY_TOKEN;
27
66
  if (!projectId || !token) throw new Error("isr: Revalidation requires __PROJECT_ID and __VOID_PROXY_TOKEN bindings.");
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
2
  import { r as AuthUser } from "../auth-qgMlYp7Z.mjs";
3
- import { a as CloudEnv, r as CloudContext } from "../handler-imD0UVDT.mjs";
3
+ import { a as CloudEnv, r as CloudContext } from "../handler-Cjh8uM3Y.mjs";
4
4
 
5
5
  //#region src/runtime/live.d.ts
6
6
  type MaybePromise<T> = T | Promise<T>;
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-CoS20LHP.mjs";
1
+ import { a as getRawRuntimeEnv } from "../env-raw-Cx8ElDdj.mjs";
2
2
  import { getUser } from "./auth.mjs";
3
3
  //#region src/runtime/live.ts
4
4
  let liveBindingName = "VOID_LIVE";
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-CoS20LHP.mjs";
1
+ import { a as getRawRuntimeEnv } from "../env-raw-Cx8ElDdj.mjs";
2
2
  import { requireRuntimeBinding } from "./env.mjs";
3
3
  import { Sandbox, getSandbox as getSandbox$1 } from "@cloudflare/sandbox";
4
4
  //#region src/runtime/sandbox.ts
@@ -1,2 +1,2 @@
1
- import { D as runValidation, E as ValidatorSlots, T as HandlerInput } from "../handler-imD0UVDT.mjs";
1
+ import { D as runValidation, E as ValidatorSlots, T as HandlerInput } from "../handler-Cjh8uM3Y.mjs";
2
2
  export { HandlerInput, ValidatorSlots, runValidation };
@@ -1,5 +1,5 @@
1
1
  import { n as AuthState } from "../auth-qgMlYp7Z.mjs";
2
- import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-BvnM9px6.mjs";
2
+ import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-DealXecJ.mjs";
3
3
  import { AnyWebSocketDefinition } from "./ws.mjs";
4
4
 
5
5
  //#region src/runtime/ws-server.d.ts
@@ -1,6 +1,6 @@
1
- import { r as AuthUser } from "../auth-qgMlYp7Z.mjs";
2
1
  import { t as StandardSchemaV1 } from "../standard-schema-DJ0HW7QP.mjs";
3
- import { a as CloudEnv } from "../handler-imD0UVDT.mjs";
2
+ import { r as AuthUser } from "../auth-qgMlYp7Z.mjs";
3
+ import { a as CloudEnv } from "../handler-Cjh8uM3Y.mjs";
4
4
  import { WebSocketRouteMap } from "void/routes";
5
5
 
6
6
  //#region src/runtime/ws.d.ts
@@ -1,5 +1,5 @@
1
- import { a as join, n as dirname, r as extname, t as basename } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
2
- import { n as readProjectPaths } from "./project-paths-tpdR1mJR.mjs";
1
+ import { a as join, n as dirname, r as extname, t as basename } from "./dist-DaKKDf8D.mjs";
2
+ import { n as readProjectPaths } from "./project-paths-BQd7OmIo.mjs";
3
3
  import { t as glob } from "./dist-BuiRJkTd.mjs";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  //#region src/router/parse-filename.ts
@@ -1,8 +1,8 @@
1
- import { a as join, i as isAbsolute, t as basename } from "./pathe.M-eThtNZ-CQzLbt4c.mjs";
2
- import { n as readProjectPaths } from "./project-paths-tpdR1mJR.mjs";
1
+ import { a as join, i as isAbsolute, t as basename } from "./dist-DaKKDf8D.mjs";
2
+ import { n as readProjectPaths } from "./project-paths-BQd7OmIo.mjs";
3
3
  import { n as globSync, t as glob } from "./dist-BuiRJkTd.mjs";
4
- import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-CJxi_fWI.mjs";
5
- import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-Dp_Gyzs3.mjs";
4
+ import { c as isIdentifier, d as isObjectExpression, l as isLiteral, s as isCallExpression } from "./entry-D7yy4xVH.mjs";
5
+ import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-DEwlM_Xy.mjs";
6
6
  import { existsSync, readFileSync } from "node:fs";
7
7
  import { pathToFileURL } from "node:url";
8
8
  import { parseSync } from "vite";
@@ -1,6 +1,6 @@
1
- import { D as outro, E as note, O as password, S as confirm, T as log, a as cliTitle, j as isCancel, w as intro, x as cancel } from "./agents-Bmr5tFFb.mjs";
2
- import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-Cj96iiBH.mjs";
3
- import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-D2HI3TrG.mjs";
1
+ import { D as outro, E as note, O as password, S as confirm, T as log, a as cliTitle, j as isCancel, w as intro, x as cancel } from "./agents-CtgBYqld.mjs";
2
+ import { b as readProjectConfig, f as getToken, n as PlatformClient } from "./client-Gb71-XkG.mjs";
3
+ import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-BBMtLLV9.mjs";
4
4
  import { readFileSync } from "node:fs";
5
5
  //#region src/cli/secret.ts
6
6
  async function runSecretCommand(root, args) {