@vinext/cloudflare 1.2.0 → 1.2.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/dist/cache/cdn-adapter-config.d.ts +2 -1
- package/dist/cache/config.d.ts +2 -1
- package/dist/cache/kv-data-adapter.runtime.d.ts +7 -3
- package/dist/cache/kv-data-adapter.runtime.js +60 -13
- package/dist/cache/response-store-adapter.worker.d.ts +2 -1
- package/dist/cache/response-store-data.runtime.js +12 -8
- package/dist/cache/static-assets-adapter.runtime.d.ts +2 -1
- package/dist/cache/workers-cache-invalidation.d.ts +2 -1
- package/dist/cdn-warm.d.ts +2 -1
- package/dist/deploy.d.ts +2 -1
- package/dist/deploy.js +1 -0
- package/dist/version-headers.js +1 -2
- package/dist/wrangler-config.d.ts +2 -1
- package/package.json +7 -8
package/dist/cache/config.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare class KVCacheHandler implements CacheHandler {
|
|
|
60
60
|
private keySpace;
|
|
61
61
|
private ctx;
|
|
62
62
|
private ttlSeconds;
|
|
63
|
-
/** Local in-memory cache for tag invalidation
|
|
63
|
+
/** Local in-memory cache for tag invalidation markers. Avoids redundant KV reads. */
|
|
64
64
|
private _tagCache;
|
|
65
65
|
/** Monotonic ordering for concurrent tag-cache fills and local invalidations. */
|
|
66
66
|
private _tagCacheOrder;
|
|
@@ -95,14 +95,18 @@ export declare class KVCacheHandler implements CacheHandler {
|
|
|
95
95
|
*/
|
|
96
96
|
private _readTagMarkers;
|
|
97
97
|
/**
|
|
98
|
-
* Report whether any tag
|
|
98
|
+
* Report whether any tag expired an entry last modified at `lastModified`:
|
|
99
|
+
* its marker's expire time is at or after `lastModified` and, for a cacheLife
|
|
100
|
+
* profile's marker, has passed.
|
|
99
101
|
* Call `_primeTagCache` for the same tags first — a tag with no cache entry
|
|
100
102
|
* counts as never invalidated. Pass `requireFresh` to call it before a prime,
|
|
101
103
|
* so an entry past `tagCacheTtlMs` does not answer for a tag it never re-read.
|
|
102
104
|
*/
|
|
103
105
|
private _hasRevalidatedTag;
|
|
106
|
+
/** Report whether any primed tag was marked stale after `lastModified`. */
|
|
107
|
+
private _hasStaleTag;
|
|
104
108
|
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
105
|
-
revalidateTag(tags: string | string[],
|
|
109
|
+
revalidateTag(tags: string | string[], durations?: {
|
|
106
110
|
expire?: number;
|
|
107
111
|
}): Promise<void>;
|
|
108
112
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStaleTagInvalidation } from "./workers-cache-invalidation.js";
|
|
1
2
|
import { isUnknownRecord, readCacheControlNumberField } from "../utils/cache-control-metadata.js";
|
|
2
3
|
import { ENTRY_PREFIX, createKvKeySpace, resolveKvExpirationTtlSeconds } from "./kv-key.js";
|
|
3
4
|
import { Buffer } from "node:buffer";
|
|
@@ -24,6 +25,23 @@ import { getRequestExecutionContext } from "vinext/shims/request-context";
|
|
|
24
25
|
*/
|
|
25
26
|
/** Default KV namespace binding name read from the Worker `env`. */
|
|
26
27
|
const DEFAULT_BINDING = "VINEXT_KV_CACHE";
|
|
28
|
+
function parseTagMarker(raw) {
|
|
29
|
+
if (raw === null || raw === void 0) return {};
|
|
30
|
+
if (!raw.startsWith("{")) return { expired: /^\d+$/.test(raw) ? Number(raw) : NaN };
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(raw);
|
|
34
|
+
} catch {
|
|
35
|
+
return { expired: NaN };
|
|
36
|
+
}
|
|
37
|
+
if (!isUnknownRecord(parsed)) return { expired: NaN };
|
|
38
|
+
const { stale, expired } = parsed;
|
|
39
|
+
if (stale === void 0 && expired === void 0 || stale !== void 0 && typeof stale !== "number" || expired !== void 0 && typeof expired !== "number") return { expired: NaN };
|
|
40
|
+
return {
|
|
41
|
+
...stale === void 0 ? {} : { stale },
|
|
42
|
+
...expired === void 0 ? {} : { expired }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
27
45
|
/** Prefix used by revalidatePath for path-based tags. */
|
|
28
46
|
const PATH_TAG_PREFIX = "_N_T_";
|
|
29
47
|
/** The runtime rejects a lower `cacheTtl` with "Cache TTL must be at least 30". */
|
|
@@ -98,7 +116,7 @@ var KVCacheHandler = class {
|
|
|
98
116
|
keySpace;
|
|
99
117
|
ctx;
|
|
100
118
|
ttlSeconds;
|
|
101
|
-
/** Local in-memory cache for tag invalidation
|
|
119
|
+
/** Local in-memory cache for tag invalidation markers. Avoids redundant KV reads. */
|
|
102
120
|
_tagCache = /* @__PURE__ */ new Map();
|
|
103
121
|
/** Monotonic ordering for concurrent tag-cache fills and local invalidations. */
|
|
104
122
|
_tagCacheOrder = 0;
|
|
@@ -179,6 +197,7 @@ var KVCacheHandler = class {
|
|
|
179
197
|
return null;
|
|
180
198
|
}
|
|
181
199
|
if (this._hasRevalidatedTag(softTags, entry.lastModified)) return null;
|
|
200
|
+
const hasStaleTag = this._hasStaleTag([...entryTags, ...softTags], entry.lastModified);
|
|
182
201
|
if (entry.expireAt !== void 0 && entry.expireAt !== null && Date.now() > entry.expireAt) {
|
|
183
202
|
this._deleteEntryReadInBackground(kvKey);
|
|
184
203
|
return null;
|
|
@@ -186,7 +205,7 @@ var KVCacheHandler = class {
|
|
|
186
205
|
const now = Date.now();
|
|
187
206
|
const requestedRevalidate = readPositiveNumberField(_ctx, "revalidate");
|
|
188
207
|
const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
|
|
189
|
-
if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
208
|
+
if (hasStaleTag || entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
190
209
|
lastModified: entry.lastModified,
|
|
191
210
|
value: restoredValue,
|
|
192
211
|
cacheState: "stale",
|
|
@@ -221,7 +240,7 @@ var KVCacheHandler = class {
|
|
|
221
240
|
if (current && current.order >= order) continue;
|
|
222
241
|
const marker = markers.get(this._tagKey(tag));
|
|
223
242
|
tagCache.set(tag, {
|
|
224
|
-
|
|
243
|
+
marker: parseTagMarker(marker),
|
|
225
244
|
fetchedAt: now,
|
|
226
245
|
order
|
|
227
246
|
});
|
|
@@ -244,18 +263,30 @@ var KVCacheHandler = class {
|
|
|
244
263
|
return markers;
|
|
245
264
|
}
|
|
246
265
|
/**
|
|
247
|
-
* Report whether any tag
|
|
266
|
+
* Report whether any tag expired an entry last modified at `lastModified`:
|
|
267
|
+
* its marker's expire time is at or after `lastModified` and, for a cacheLife
|
|
268
|
+
* profile's marker, has passed.
|
|
248
269
|
* Call `_primeTagCache` for the same tags first — a tag with no cache entry
|
|
249
270
|
* counts as never invalidated. Pass `requireFresh` to call it before a prime,
|
|
250
271
|
* so an entry past `tagCacheTtlMs` does not answer for a tag it never re-read.
|
|
251
272
|
*/
|
|
252
273
|
_hasRevalidatedTag(tags, lastModified, requireFresh = false) {
|
|
253
|
-
const now =
|
|
274
|
+
const now = Date.now();
|
|
254
275
|
for (const tag of tags) {
|
|
255
276
|
const cached = this._tagCache.get(tag);
|
|
256
|
-
if (
|
|
277
|
+
if (cached?.marker.expired === void 0) continue;
|
|
257
278
|
if (requireFresh && now - cached.fetchedAt >= this._tagCacheTtl) continue;
|
|
258
|
-
|
|
279
|
+
const { stale, expired } = cached.marker;
|
|
280
|
+
const pending = stale !== void 0 && expired > now;
|
|
281
|
+
if (Number.isNaN(expired) || expired >= lastModified && !pending) return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
/** Report whether any primed tag was marked stale after `lastModified`. */
|
|
286
|
+
_hasStaleTag(tags, lastModified) {
|
|
287
|
+
for (const tag of tags) {
|
|
288
|
+
const stale = this._tagCache.get(tag)?.marker.stale;
|
|
289
|
+
if (stale !== void 0 && stale > lastModified) return true;
|
|
259
290
|
}
|
|
260
291
|
return false;
|
|
261
292
|
}
|
|
@@ -300,16 +331,32 @@ var KVCacheHandler = class {
|
|
|
300
331
|
metadata
|
|
301
332
|
});
|
|
302
333
|
}
|
|
303
|
-
async revalidateTag(tags,
|
|
334
|
+
async revalidateTag(tags, durations) {
|
|
304
335
|
const tagList = Array.isArray(tags) ? tags : [tags];
|
|
305
336
|
const now = Date.now();
|
|
306
337
|
const validTags = tagList.filter((t) => validateTag(t) !== null);
|
|
307
|
-
await Promise.all(validTags.map((tag) =>
|
|
338
|
+
const markers = await Promise.all(validTags.map(async (tag) => {
|
|
339
|
+
const tagKey = this._tagKey(tag);
|
|
340
|
+
if (!isStaleTagInvalidation(durations)) {
|
|
341
|
+
await this.kv.put(tagKey, String(now));
|
|
342
|
+
return { expired: now };
|
|
343
|
+
}
|
|
344
|
+
const expire = durations?.expire;
|
|
345
|
+
const expired = expire === void 0 ? parseTagMarker(await this.kv.get(tagKey)).expired : now + expire * 1e3;
|
|
346
|
+
const marker = {
|
|
347
|
+
stale: now,
|
|
348
|
+
...expired !== void 0 && Number.isFinite(expired) ? { expired } : {}
|
|
349
|
+
};
|
|
350
|
+
await this.kv.put(tagKey, JSON.stringify(marker));
|
|
351
|
+
return marker;
|
|
352
|
+
}));
|
|
308
353
|
const order = ++this._tagCacheOrder;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
354
|
+
validTags.forEach((tag, index) => {
|
|
355
|
+
this._tagCache.set(tag, {
|
|
356
|
+
marker: markers[index],
|
|
357
|
+
fetchedAt: now,
|
|
358
|
+
order
|
|
359
|
+
});
|
|
313
360
|
});
|
|
314
361
|
}
|
|
315
362
|
/**
|
|
@@ -11,4 +11,5 @@ export declare function createVinextResponseStoreOptions<Env extends VinextRespo
|
|
|
11
11
|
export declare function createVinextResponseStoreHandler(store: WorkersResponseStore): {
|
|
12
12
|
fetch(request: Request, env: VinextResponseStoreEnv, ctx: WorkerExecutionContext): Promise<Response>;
|
|
13
13
|
};
|
|
14
|
-
//#endregion
|
|
14
|
+
//#endregion
|
|
15
|
+
export {};
|
|
@@ -6,10 +6,11 @@ import { cacheForRequest } from "vinext/cache";
|
|
|
6
6
|
//#region src/cache/response-store-data.runtime.ts
|
|
7
7
|
const ARRAY_BUFFER_MARKER = "$vinextArrayBuffer";
|
|
8
8
|
const CACHE_MAX_AGE_SECONDS = 31536e4;
|
|
9
|
-
const CACHE_MAX_AGE = `public, max-age=${CACHE_MAX_AGE_SECONDS}`;
|
|
10
9
|
const DATA_ENTRY_PATH = "__vinext_data";
|
|
11
10
|
const REPLAYABLE_HEADER = "X-Vinext-Response-Store-Replayable";
|
|
12
11
|
const MAX_CACHE_TAG_HEADER_BYTES = 16384;
|
|
12
|
+
/** How long past its revalidate a current Store's hit can still be marked fresh: write latency. */
|
|
13
|
+
const STALE_FRESH_HIT_GRACE_MS = 5e3;
|
|
13
14
|
const DATA_REVALIDATOR_ID = "vinext:data";
|
|
14
15
|
const CACHE_FUNCTION_REVALIDATOR_ID = "vinext:cache-function";
|
|
15
16
|
const invocationStorage = new AsyncLocalStorage();
|
|
@@ -194,9 +195,9 @@ function deserialize(value) {
|
|
|
194
195
|
return entry;
|
|
195
196
|
}
|
|
196
197
|
function cachePolicy(revalidate, expire) {
|
|
197
|
-
if (revalidate === false || revalidate === void 0) return `public, max-age=${CACHE_MAX_AGE_SECONDS}`;
|
|
198
|
-
const
|
|
199
|
-
return `public, max-age=${Math.max(0,
|
|
198
|
+
if (revalidate === false || revalidate === void 0) return `public, max-age=${expire === void 0 ? CACHE_MAX_AGE_SECONDS : Math.max(1, Math.ceil(expire))}`;
|
|
199
|
+
const maxAge = revalidate > 0 ? Math.max(1, Math.floor(revalidate)) : 0;
|
|
200
|
+
return `public, max-age=${maxAge}, stale-while-revalidate=${expire === void 0 ? CACHE_MAX_AGE_SECONDS : Math.max(0, Math.ceil(expire) - maxAge)}`;
|
|
200
201
|
}
|
|
201
202
|
var WorkersResponseStoreCacheHandler = class {
|
|
202
203
|
store;
|
|
@@ -244,12 +245,13 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
244
245
|
}
|
|
245
246
|
const age = Date.now() - entry.lastModified;
|
|
246
247
|
const requestedRevalidate = readCacheControlField(context, "revalidate");
|
|
248
|
+
const storedRevalidate = entry.cacheControl?.revalidate;
|
|
247
249
|
const requestedStale = typeof requestedRevalidate === "number" && requestedRevalidate > 0 && age > requestedRevalidate * 1e3;
|
|
248
250
|
let cacheState;
|
|
249
251
|
if (response.headers.get(REPLAYABLE_HEADER) === "1") {
|
|
250
252
|
if (requestedStale && storeStatus !== "BLOB-STALE") cacheState = "stale";
|
|
251
253
|
} else if (typeof entry.cacheControl?.expire === "number" && age > entry.cacheControl.expire * 1e3) cacheState = "expired";
|
|
252
|
-
else if (
|
|
254
|
+
else if (storeStatus === "BLOB-STALE" || requestedStale && typeof requestedRevalidate === "number" && (typeof storedRevalidate !== "number" || requestedRevalidate < storedRevalidate) || typeof storedRevalidate === "number" && storedRevalidate > 0 && age > storedRevalidate * 1e3 + STALE_FRESH_HIT_GRACE_MS) cacheState = "stale";
|
|
253
255
|
return {
|
|
254
256
|
lastModified: entry.lastModified,
|
|
255
257
|
value: entry.value,
|
|
@@ -274,9 +276,11 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
274
276
|
let revalidate = readCacheControlField(context, "revalidate");
|
|
275
277
|
if (value && "revalidate" in value) revalidate = value.revalidate;
|
|
276
278
|
if (revalidate === 0) return;
|
|
279
|
+
if (typeof revalidate === "number" && !Number.isFinite(revalidate)) revalidate = false;
|
|
277
280
|
const rawExpire = readCacheControlField(context, "expire");
|
|
278
|
-
const expire = typeof rawExpire === "number" ? rawExpire : void 0;
|
|
279
|
-
const
|
|
281
|
+
const expire = typeof rawExpire === "number" && Number.isFinite(rawExpire) ? rawExpire : void 0;
|
|
282
|
+
const rawStale = readCacheControlField(context, "stale");
|
|
283
|
+
const stale = typeof rawStale === "number" && Number.isFinite(rawStale) ? rawStale : void 0;
|
|
280
284
|
const cacheControl = typeof revalidate === "number" || revalidate === false ? {
|
|
281
285
|
revalidate,
|
|
282
286
|
...typeof expire === "number" ? { expire } : {},
|
|
@@ -303,7 +307,7 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
303
307
|
lastModified: Date.now(),
|
|
304
308
|
value
|
|
305
309
|
}), { headers: {
|
|
306
|
-
"Cache-Control":
|
|
310
|
+
"Cache-Control": cachePolicy(revalidate, expire),
|
|
307
311
|
...tagHeader ? { "Cache-Tag": tagHeader } : {},
|
|
308
312
|
"Content-Type": "application/json",
|
|
309
313
|
...revalidator ? { [REPLAYABLE_HEADER]: "1" } : {}
|
|
@@ -27,4 +27,5 @@ export declare function hasCacheMethod<Name extends CacheMethodName>(value: unkn
|
|
|
27
27
|
* `undefined` when the cache supports neither.
|
|
28
28
|
*/
|
|
29
29
|
export declare function invalidateOrPurge(cache: unknown, options: CacheInvalidationOptions): unknown;
|
|
30
|
-
//#endregion
|
|
30
|
+
//#endregion
|
|
31
|
+
export {};
|
package/dist/cdn-warm.d.ts
CHANGED
|
@@ -130,4 +130,5 @@ export declare function waitForCdnWarmTargetReadiness(options: Pick<CdnWarmOptio
|
|
|
130
130
|
}): Promise<CdnWarmReadinessResult>;
|
|
131
131
|
export declare function warmCdnCache(options: CdnWarmOptions): Promise<CdnWarmResult>;
|
|
132
132
|
export declare function warmCdnCacheFromPrerender(options: PrerenderCdnWarmOptions): Promise<CdnWarmResult>;
|
|
133
|
-
//#endregion
|
|
133
|
+
//#endregion
|
|
134
|
+
export {};
|
package/dist/deploy.d.ts
CHANGED
|
@@ -192,4 +192,5 @@ export declare function resolveDeploymentControlPlaneOptions(options: Deployment
|
|
|
192
192
|
export declare function resolveWorkerNameForVersionOverride(config: ParsedWranglerConfig | null, options: Pick<DeployOptions, "preview" | "env" | "name">): string | undefined;
|
|
193
193
|
export declare function buildVersionOverrideHeaders(workerName: string | undefined, versionId: string): HeadersInit | undefined;
|
|
194
194
|
export declare function deploy(options: DeployOptions): Promise<void>;
|
|
195
|
-
//#endregion
|
|
195
|
+
//#endregion
|
|
196
|
+
export {};
|
package/dist/deploy.js
CHANGED
|
@@ -1191,6 +1191,7 @@ async function deploy(options) {
|
|
|
1191
1191
|
if (deploymentTool === "cf" && options.name) throw new Error("--name is not supported for cloudflare.config.ts projects. Set `name` in cloudflare.config.ts instead.");
|
|
1192
1192
|
const viteMode = resolveViteBuildMode(deploymentTool, deployEnv);
|
|
1193
1193
|
const wranglerFallbackEnv = deploymentTool === "cf" ? void 0 : deployEnv;
|
|
1194
|
+
if (!process.env.NODE_ENV) Reflect.set(process.env, "NODE_ENV", "production");
|
|
1194
1195
|
loadDotenv({
|
|
1195
1196
|
root,
|
|
1196
1197
|
mode: viteMode
|
package/dist/version-headers.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { VINEXT_EXPECTED_WORKER_VERSION_HEADER };
|
|
1
|
+
export { VINEXT_EXPECTED_WORKER_VERSION_HEADER } from "vinext/internal/server/headers";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vinext/cloudflare",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Cloudflare cache and image adapters for vinext.",
|
|
5
5
|
"homepage": "https://vinext.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,21 +48,20 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cloudflare/workers-response-store": "1.1.
|
|
51
|
+
"@cloudflare/workers-response-store": "1.1.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.9.2",
|
|
55
55
|
"cf": "1.0.0-beta.1",
|
|
56
56
|
"miniflare": "5.20260730.0-alpha",
|
|
57
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@1.
|
|
58
|
-
"vite-plus": "1.
|
|
59
|
-
"
|
|
60
|
-
"vinext": "1.3.0"
|
|
57
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
|
|
58
|
+
"vite-plus": "1.3.0",
|
|
59
|
+
"vinext": "1.3.1"
|
|
61
60
|
},
|
|
62
61
|
"peerDependencies": {
|
|
63
62
|
"@cloudflare/vite-plugin": "*",
|
|
64
63
|
"cf": "*",
|
|
65
|
-
"vinext": "^1.3.
|
|
64
|
+
"vinext": "^1.3.1"
|
|
66
65
|
},
|
|
67
66
|
"peerDependenciesMeta": {
|
|
68
67
|
"@cloudflare/vite-plugin": {
|
|
@@ -78,6 +77,6 @@
|
|
|
78
77
|
"scripts": {
|
|
79
78
|
"build": "vp pack",
|
|
80
79
|
"dev": "vp pack --watch",
|
|
81
|
-
"test": "vp run vinext#build && vp run @cloudflare/workers-response-store#build && vp run @vinext/cloudflare#build && VINEXT_RESPONSE_STORE_MODE=self-contained VINEXT_RESPONSE_STORE_LOCATION_HINT=weur pnpm --filter response-store-demo run build && VINEXT_RESPONSE_STORE_LOCATION_HINT=wnam pnpm --filter response-store-demo run build &&
|
|
80
|
+
"test": "vp run vinext#build && vp run @cloudflare/workers-response-store#build && vp run @vinext/cloudflare#build && VINEXT_RESPONSE_STORE_MODE=self-contained VINEXT_RESPONSE_STORE_LOCATION_HINT=weur pnpm --filter response-store-demo run build && VINEXT_RESPONSE_STORE_LOCATION_HINT=wnam pnpm --filter response-store-demo run build && vp test run"
|
|
82
81
|
}
|
|
83
82
|
}
|