@vinext/cloudflare 1.0.0-beta.9 → 1.0.0
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/README.md +165 -37
- package/dist/cache/cdn-adapter-config.d.ts +2 -1
- package/dist/cache/cdn-adapter-config.js +13 -5
- package/dist/cache/cdn-adapter.d.ts +5 -56
- package/dist/cache/cdn-adapter.js +3 -61
- package/dist/cache/cdn-adapter.runtime.d.ts +1 -0
- package/dist/cache/cdn-adapter.runtime.js +17 -3
- package/dist/cache/cdn-build-id.d.ts +1 -1
- package/dist/cache/cdn-build-id.js +1 -1
- package/dist/cache/config.d.ts +137 -0
- package/dist/cache/config.js +87 -0
- package/dist/cache/kv-data-adapter.d.ts +1 -1
- package/dist/cache/kv-data-adapter.js +1 -1
- package/dist/cache/kv-data-adapter.runtime.js +13 -9
- package/dist/cache/kv-key.d.ts +7 -4
- package/dist/cache/kv-key.js +16 -5
- package/dist/cache/response-store-adapter.d.ts +9 -6
- package/dist/cache/response-store-adapter.js +20 -1
- package/dist/cache/response-store-adapter.worker.js +100 -43
- package/dist/cache/response-store-cdn.runtime.js +1 -0
- package/dist/cache/response-store-data.runtime.js +53 -31
- package/dist/cache/static-assets-adapter.build.d.ts +6 -0
- package/dist/cache/static-assets-adapter.build.js +65 -0
- package/dist/cache/static-assets-adapter.d.ts +27 -0
- package/dist/cache/static-assets-adapter.js +26 -0
- package/dist/cache/static-assets-adapter.runtime.d.ts +26 -0
- package/dist/cache/static-assets-adapter.runtime.js +77 -0
- package/dist/cache/static-assets-adapter.shared.d.ts +12 -0
- package/dist/cache/static-assets-adapter.shared.js +4 -0
- package/dist/cache/workers-cache-cdn-adapter.d.ts +58 -0
- package/dist/cache/workers-cache-cdn-adapter.js +65 -0
- package/dist/cacheability-artifact.d.ts +7 -1
- package/dist/cacheability-artifact.js +60 -21
- package/dist/cacheability-probe.d.ts +2 -0
- package/dist/cacheability-probe.js +107 -13
- package/dist/cdn-warm.d.ts +1 -0
- package/dist/cdn-warm.js +2 -1
- package/dist/deploy-config.d.ts +3 -8
- package/dist/deploy-config.js +5 -23
- package/dist/deploy-help.js +47 -42
- package/dist/deploy.d.ts +18 -16
- package/dist/deploy.js +355 -216
- package/dist/tpr.d.ts +4 -0
- package/dist/tpr.js +16 -7
- package/dist/version-deploy.d.ts +27 -0
- package/dist/version-deploy.js +121 -3
- package/dist/wrangler-version-metadata.js +1 -1
- package/package.json +16 -5
- package/dist/prerender-kv-populate.d.ts +0 -24
- package/dist/prerender-kv-populate.js +0 -153
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { CloudflareConfig, WorkerConfig } from "cf/config";
|
|
2
|
+
//#region src/cache/config.d.ts
|
|
3
|
+
type ResponseStoreStorageOptions = {
|
|
4
|
+
worker: string;
|
|
5
|
+
bucket: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function createWorkersResponseStoreSelfContainedConfig(options: ResponseStoreStorageOptions): Promise<{
|
|
8
|
+
cache: {
|
|
9
|
+
enabled: true;
|
|
10
|
+
};
|
|
11
|
+
exports: {
|
|
12
|
+
CacheMetadata: {
|
|
13
|
+
state?: "created";
|
|
14
|
+
type: "durable-object";
|
|
15
|
+
storage: "sqlite";
|
|
16
|
+
container: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
state?: "created";
|
|
19
|
+
type: "durable-object";
|
|
20
|
+
storage: "sqlite";
|
|
21
|
+
container: undefined;
|
|
22
|
+
};
|
|
23
|
+
ResponseStoreBinding: import("cf/config").WorkerEntrypointExport;
|
|
24
|
+
};
|
|
25
|
+
env: {
|
|
26
|
+
CACHE_BODIES: import("cf/config").R2Binding;
|
|
27
|
+
CACHE_METADATA: {
|
|
28
|
+
type: "durable-object";
|
|
29
|
+
worker: string;
|
|
30
|
+
exportName: string;
|
|
31
|
+
};
|
|
32
|
+
CF_VERSION_METADATA: import("cf/config").VersionMetadataBinding;
|
|
33
|
+
};
|
|
34
|
+
}>;
|
|
35
|
+
export declare function createWorkersResponseStoreServiceBindingConfig(options: {
|
|
36
|
+
accountId?: CloudflareConfig["accountId"];
|
|
37
|
+
worker: {
|
|
38
|
+
name: string;
|
|
39
|
+
compatibilityDate: string;
|
|
40
|
+
compatibilityFlags?: string[];
|
|
41
|
+
observability?: WorkerConfig["observability"];
|
|
42
|
+
};
|
|
43
|
+
bucket: string;
|
|
44
|
+
}): Promise<{
|
|
45
|
+
accountId: string | undefined;
|
|
46
|
+
serviceBindingWorker: {
|
|
47
|
+
cache: {
|
|
48
|
+
enabled: true;
|
|
49
|
+
};
|
|
50
|
+
exports: {
|
|
51
|
+
CacheMetadata: {
|
|
52
|
+
state?: "created";
|
|
53
|
+
type: "durable-object";
|
|
54
|
+
storage: "sqlite";
|
|
55
|
+
container: undefined;
|
|
56
|
+
} | {
|
|
57
|
+
state?: "created";
|
|
58
|
+
type: "durable-object";
|
|
59
|
+
storage: "sqlite";
|
|
60
|
+
container: undefined;
|
|
61
|
+
};
|
|
62
|
+
ResponseStoreBinding: import("cf/config").WorkerEntrypointExport;
|
|
63
|
+
};
|
|
64
|
+
env: {
|
|
65
|
+
CACHE_BODIES: import("cf/config").R2Binding;
|
|
66
|
+
CACHE_METADATA: {
|
|
67
|
+
type: "durable-object";
|
|
68
|
+
worker: string;
|
|
69
|
+
exportName: string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
name: string;
|
|
73
|
+
compatibilityDate: string;
|
|
74
|
+
compatibilityFlags?: string[];
|
|
75
|
+
observability?: WorkerConfig["observability"];
|
|
76
|
+
entrypoint: string;
|
|
77
|
+
workersDev: false;
|
|
78
|
+
previewUrls: false;
|
|
79
|
+
};
|
|
80
|
+
applicationWorker: {
|
|
81
|
+
cache: {
|
|
82
|
+
enabled: false;
|
|
83
|
+
};
|
|
84
|
+
env: {
|
|
85
|
+
RESPONSE_STORE: import("cf/config").WorkerBinding<{
|
|
86
|
+
cache: {
|
|
87
|
+
enabled: true;
|
|
88
|
+
};
|
|
89
|
+
exports: {
|
|
90
|
+
CacheMetadata: {
|
|
91
|
+
state?: "created";
|
|
92
|
+
type: "durable-object";
|
|
93
|
+
storage: "sqlite";
|
|
94
|
+
container: undefined;
|
|
95
|
+
} | {
|
|
96
|
+
state?: "created";
|
|
97
|
+
type: "durable-object";
|
|
98
|
+
storage: "sqlite";
|
|
99
|
+
container: undefined;
|
|
100
|
+
};
|
|
101
|
+
ResponseStoreBinding: import("cf/config").WorkerEntrypointExport;
|
|
102
|
+
};
|
|
103
|
+
env: {
|
|
104
|
+
CACHE_BODIES: import("cf/config").R2Binding;
|
|
105
|
+
CACHE_METADATA: {
|
|
106
|
+
type: "durable-object";
|
|
107
|
+
worker: string;
|
|
108
|
+
exportName: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
name: string;
|
|
112
|
+
compatibilityDate: string;
|
|
113
|
+
compatibilityFlags?: string[];
|
|
114
|
+
observability?: WorkerConfig["observability"];
|
|
115
|
+
entrypoint: string;
|
|
116
|
+
workersDev: false;
|
|
117
|
+
previewUrls: false;
|
|
118
|
+
}, "ResponseStoreService">;
|
|
119
|
+
CF_VERSION_METADATA: import("cf/config").VersionMetadataBinding;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
}>;
|
|
123
|
+
export declare function createWorkersCacheConfig({ versionMetadataBinding }?: {
|
|
124
|
+
versionMetadataBinding?: string;
|
|
125
|
+
}): Promise<{
|
|
126
|
+
cache: {
|
|
127
|
+
enabled: false;
|
|
128
|
+
};
|
|
129
|
+
env: {
|
|
130
|
+
[versionMetadataBinding]: import("cf/config").VersionMetadataBinding;
|
|
131
|
+
};
|
|
132
|
+
exports: {
|
|
133
|
+
VinextCachedResponse: import("cf/config").WorkerEntrypointExport;
|
|
134
|
+
VinextUncachedResponse: import("cf/config").WorkerEntrypointExport;
|
|
135
|
+
};
|
|
136
|
+
}>;
|
|
137
|
+
//#endregion
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/cache/config.ts
|
|
2
|
+
const RESPONSE_STORE_BINDING = "RESPONSE_STORE";
|
|
3
|
+
const RESPONSE_STORE_ENTRYPOINT = "ResponseStoreService";
|
|
4
|
+
const DEFAULT_VERSION_METADATA_BINDING = "CF_VERSION_METADATA";
|
|
5
|
+
const CACHE_BODIES_BINDING = "CACHE_BODIES";
|
|
6
|
+
const CACHE_METADATA_BINDING = "CACHE_METADATA";
|
|
7
|
+
const CACHE_METADATA_CLASS = "CacheMetadata";
|
|
8
|
+
const RESPONSE_STORE_CACHE_ENTRYPOINT = "ResponseStoreBinding";
|
|
9
|
+
const RESPONSE_STORE_SERVICE_ENTRYPOINT = "@cloudflare/workers-response-store/service";
|
|
10
|
+
async function createResponseStoreWorkerConfig(options) {
|
|
11
|
+
const { bindings, exports } = await import("cf/config");
|
|
12
|
+
return {
|
|
13
|
+
cache: { enabled: true },
|
|
14
|
+
exports: {
|
|
15
|
+
[CACHE_METADATA_CLASS]: {
|
|
16
|
+
...exports.durableObject({ storage: "sqlite" }),
|
|
17
|
+
storage: "sqlite",
|
|
18
|
+
container: void 0
|
|
19
|
+
},
|
|
20
|
+
[RESPONSE_STORE_CACHE_ENTRYPOINT]: exports.worker({ cache: { enabled: true } })
|
|
21
|
+
},
|
|
22
|
+
env: {
|
|
23
|
+
[CACHE_BODIES_BINDING]: bindings.r2({ name: options.bucket }),
|
|
24
|
+
[CACHE_METADATA_BINDING]: {
|
|
25
|
+
...bindings.durableObject({
|
|
26
|
+
worker: options.worker,
|
|
27
|
+
exportName: CACHE_METADATA_CLASS
|
|
28
|
+
}),
|
|
29
|
+
worker: options.worker,
|
|
30
|
+
exportName: CACHE_METADATA_CLASS
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function createWorkersResponseStoreSelfContainedConfig(options) {
|
|
36
|
+
const { bindings } = await import("cf/config");
|
|
37
|
+
const storage = await createResponseStoreWorkerConfig(options);
|
|
38
|
+
return {
|
|
39
|
+
...storage,
|
|
40
|
+
env: {
|
|
41
|
+
...storage.env,
|
|
42
|
+
[DEFAULT_VERSION_METADATA_BINDING]: bindings.versionMetadata()
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function createWorkersResponseStoreServiceBindingConfig(options) {
|
|
47
|
+
const { bindings } = await import("cf/config");
|
|
48
|
+
const storage = await createResponseStoreWorkerConfig({
|
|
49
|
+
worker: options.worker.name,
|
|
50
|
+
bucket: options.bucket
|
|
51
|
+
});
|
|
52
|
+
const serviceBindingWorker = {
|
|
53
|
+
...options.worker,
|
|
54
|
+
entrypoint: RESPONSE_STORE_SERVICE_ENTRYPOINT,
|
|
55
|
+
workersDev: false,
|
|
56
|
+
previewUrls: false,
|
|
57
|
+
...storage
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
accountId: options.accountId,
|
|
61
|
+
serviceBindingWorker,
|
|
62
|
+
applicationWorker: {
|
|
63
|
+
cache: { enabled: false },
|
|
64
|
+
env: {
|
|
65
|
+
[RESPONSE_STORE_BINDING]: bindings.worker({
|
|
66
|
+
worker: serviceBindingWorker,
|
|
67
|
+
exportName: RESPONSE_STORE_ENTRYPOINT
|
|
68
|
+
}),
|
|
69
|
+
[DEFAULT_VERSION_METADATA_BINDING]: bindings.versionMetadata()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function createWorkersCacheConfig({ versionMetadataBinding = DEFAULT_VERSION_METADATA_BINDING } = {}) {
|
|
75
|
+
if (versionMetadataBinding.length === 0) throw new TypeError("versionMetadataBinding must be a non-empty string");
|
|
76
|
+
const { bindings, exports } = await import("cf/config");
|
|
77
|
+
return {
|
|
78
|
+
cache: { enabled: false },
|
|
79
|
+
env: { [versionMetadataBinding]: bindings.versionMetadata() },
|
|
80
|
+
exports: {
|
|
81
|
+
VinextCachedResponse: exports.worker({ cache: { enabled: true } }),
|
|
82
|
+
VinextUncachedResponse: exports.worker({ cache: { enabled: false } })
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { createWorkersCacheConfig, createWorkersResponseStoreSelfContainedConfig, createWorkersResponseStoreServiceBindingConfig };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ENTRY_PREFIX, createKvKeySpace } from "./kv-key.js";
|
|
2
1
|
import { isUnknownRecord, readCacheControlNumberField } from "../utils/cache-control-metadata.js";
|
|
2
|
+
import { ENTRY_PREFIX, createKvKeySpace, resolveKvExpirationTtlSeconds } from "./kv-key.js";
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
4
|
import { getRequestExecutionContext } from "vinext/shims/request-context";
|
|
5
5
|
//#region src/cache/kv-data-adapter.runtime.ts
|
|
@@ -20,7 +20,7 @@ import { getRequestExecutionContext } from "vinext/shims/request-context";
|
|
|
20
20
|
*
|
|
21
21
|
* Wrangler config (wrangler.jsonc):
|
|
22
22
|
*
|
|
23
|
-
* { "kv_namespaces": [{ "binding": "VINEXT_KV_CACHE"
|
|
23
|
+
* { "kv_namespaces": [{ "binding": "VINEXT_KV_CACHE" }] }
|
|
24
24
|
*/
|
|
25
25
|
/** Default KV namespace binding name read from the Worker `env`. */
|
|
26
26
|
const DEFAULT_BINDING = "VINEXT_KV_CACHE";
|
|
@@ -112,7 +112,7 @@ var KVCacheHandler = class {
|
|
|
112
112
|
this.kv = kvNamespace;
|
|
113
113
|
this.keySpace = createKvKeySpace(options?.appPrefix);
|
|
114
114
|
this.ctx = options?.ctx;
|
|
115
|
-
this.ttlSeconds = options?.ttlSeconds
|
|
115
|
+
this.ttlSeconds = resolveKvExpirationTtlSeconds(options?.ttlSeconds);
|
|
116
116
|
this._tagCacheTtl = options?.tagCacheTtlMs ?? 5e3;
|
|
117
117
|
const entryCacheTtl = options?.entryCacheTtlSeconds;
|
|
118
118
|
this._entryReadOptions = typeof entryCacheTtl === "number" && Number.isFinite(entryCacheTtl) ? { cacheTtl: Math.max(MIN_KV_CACHE_TTL_SECONDS, Math.floor(entryCacheTtl)) } : void 0;
|
|
@@ -280,10 +280,10 @@ var KVCacheHandler = class {
|
|
|
280
280
|
if (data && "revalidate" in data && typeof data.revalidate === "number") effectiveRevalidate = data.revalidate;
|
|
281
281
|
if (effectiveRevalidate === 0) return Promise.resolve();
|
|
282
282
|
const now = Date.now();
|
|
283
|
-
const revalidateAt = typeof effectiveRevalidate === "number" && effectiveRevalidate > 0 ? now + effectiveRevalidate * 1e3 : null;
|
|
283
|
+
const revalidateAt = typeof effectiveRevalidate === "number" && effectiveRevalidate > 0 && Number.isFinite(effectiveRevalidate) ? now + effectiveRevalidate * 1e3 : null;
|
|
284
284
|
const expireAt = typeof effectiveExpire === "number" && effectiveExpire > 0 ? now + effectiveExpire * 1e3 : null;
|
|
285
285
|
const cacheControl = typeof effectiveRevalidate === "number" ? {
|
|
286
|
-
revalidate: effectiveRevalidate,
|
|
286
|
+
revalidate: Number.isFinite(effectiveRevalidate) ? effectiveRevalidate : false,
|
|
287
287
|
...effectiveExpire === void 0 ? {} : { expire: effectiveExpire },
|
|
288
288
|
...effectiveStale === void 0 ? {} : { stale: effectiveStale }
|
|
289
289
|
} : void 0;
|
|
@@ -295,7 +295,7 @@ var KVCacheHandler = class {
|
|
|
295
295
|
expireAt,
|
|
296
296
|
cacheControl
|
|
297
297
|
};
|
|
298
|
-
const expirationTtl =
|
|
298
|
+
const expirationTtl = this.ttlSeconds;
|
|
299
299
|
const metadata = JSON.stringify({ tags }).length <= 1024 ? { tags } : void 0;
|
|
300
300
|
return this._put(this._entryKey(key), JSON.stringify(entry), {
|
|
301
301
|
expirationTtl,
|
|
@@ -306,7 +306,7 @@ var KVCacheHandler = class {
|
|
|
306
306
|
const tagList = Array.isArray(tags) ? tags : [tags];
|
|
307
307
|
const now = Date.now();
|
|
308
308
|
const validTags = tagList.filter((t) => validateTag(t) !== null);
|
|
309
|
-
await Promise.all(validTags.map((tag) => this.kv.put(this._tagKey(tag), String(now)
|
|
309
|
+
await Promise.all(validTags.map((tag) => this.kv.put(this._tagKey(tag), String(now))));
|
|
310
310
|
const order = ++this._tagCacheOrder;
|
|
311
311
|
for (const tag of validTags) this._tagCache.set(tag, {
|
|
312
312
|
timestamp: now,
|
|
@@ -423,7 +423,7 @@ function validateCacheEntry(raw) {
|
|
|
423
423
|
if (obj.expireAt !== void 0 && obj.expireAt !== null && typeof obj.expireAt !== "number") return null;
|
|
424
424
|
if (obj.cacheControl !== void 0) {
|
|
425
425
|
if (!isUnknownRecord(obj.cacheControl)) return null;
|
|
426
|
-
if (typeof obj.cacheControl.revalidate !== "number") return null;
|
|
426
|
+
if (typeof obj.cacheControl.revalidate !== "number" && obj.cacheControl.revalidate !== false) return null;
|
|
427
427
|
if (obj.cacheControl.expire !== void 0 && typeof obj.cacheControl.expire !== "number") return null;
|
|
428
428
|
if (obj.cacheControl.stale !== void 0 && typeof obj.cacheControl.stale !== "number") return null;
|
|
429
429
|
}
|
|
@@ -432,6 +432,10 @@ function validateCacheEntry(raw) {
|
|
|
432
432
|
const value = obj.value;
|
|
433
433
|
if (typeof value.kind !== "string" || !VALID_KINDS.has(value.kind)) return null;
|
|
434
434
|
}
|
|
435
|
+
if (obj.cacheControl?.revalidate === false) obj.cacheControl = {
|
|
436
|
+
...obj.cacheControl,
|
|
437
|
+
revalidate: Infinity
|
|
438
|
+
};
|
|
435
439
|
return raw;
|
|
436
440
|
}
|
|
437
441
|
/**
|
|
@@ -522,7 +526,7 @@ function safeBase64ToArrayBuffer(base64) {
|
|
|
522
526
|
const createKvDataCacheAdapter = ({ env, options }) => {
|
|
523
527
|
const binding = options?.binding ?? DEFAULT_BINDING;
|
|
524
528
|
const namespace = env?.[binding];
|
|
525
|
-
if (!namespace) throw new Error(`[vinext] The KV data cache adapter requires a \`${binding}\` KV namespace binding.\n Add it to wrangler.jsonc:\n "kv_namespaces": [{ "binding": "${binding}"
|
|
529
|
+
if (!namespace) throw new Error(`[vinext] The KV data cache adapter requires a \`${binding}\` KV namespace binding.\n Add it to wrangler.jsonc:\n "kv_namespaces": [{ "binding": "${binding}" }]`);
|
|
526
530
|
return new KVCacheHandler(namespace, {
|
|
527
531
|
appPrefix: options?.appPrefix,
|
|
528
532
|
ttlSeconds: options?.ttlSeconds,
|
package/dist/cache/kv-key.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
//#region src/cache/kv-key.d.ts
|
|
2
2
|
/** Key prefix for cache entries. */
|
|
3
3
|
export declare const ENTRY_PREFIX = "cache:";
|
|
4
|
+
/**
|
|
5
|
+
* The KV TTL for a configured `ttlSeconds`. A missing, non-finite or
|
|
6
|
+
* nonpositive value falls back to 30 days. Any other value is truncated to
|
|
7
|
+
* whole seconds, as the binding does, and kept within the range KV accepts.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveKvExpirationTtlSeconds(ttlSeconds: number | undefined): number;
|
|
4
10
|
export type KvKeySpace = {
|
|
5
11
|
/** Prefix shared by every cache entry, including entries with hashed logical keys. */
|
|
6
12
|
entryPrefix: string;
|
|
7
13
|
entryKey(logicalKey: string): string;
|
|
8
14
|
tagKey(tag: string): string;
|
|
9
15
|
};
|
|
10
|
-
/**
|
|
11
|
-
* Create the deterministic key namespace shared by runtime cache operations
|
|
12
|
-
* and deploy-time prerender population.
|
|
13
|
-
*/
|
|
16
|
+
/** Create the deterministic key namespace for runtime cache operations. */
|
|
14
17
|
export declare function createKvKeySpace(appPrefix: string | undefined): KvKeySpace;
|
|
15
18
|
//#endregion
|
package/dist/cache/kv-key.js
CHANGED
|
@@ -9,6 +9,20 @@ const KV_KEY_MAX_BYTES = 512;
|
|
|
9
9
|
/** Marker for logical keys that must be hashed to fit in KV. */
|
|
10
10
|
const HASHED_KEY_PREFIX = "__hash:";
|
|
11
11
|
const KV_KEY_ENCODER = new TextEncoder();
|
|
12
|
+
/** KV rejects an expiration TTL below 60 seconds. */
|
|
13
|
+
const MIN_KV_EXPIRATION_TTL_SECONDS = 60;
|
|
14
|
+
/** The Workers KV binding types `expirationTtl` as a signed 32-bit integer. */
|
|
15
|
+
const MAX_KV_EXPIRATION_TTL_SECONDS = 2147483647;
|
|
16
|
+
/** Default KV TTL for cache entries. */
|
|
17
|
+
const DEFAULT_KV_EXPIRATION_TTL_SECONDS = 2592e3;
|
|
18
|
+
/**
|
|
19
|
+
* The KV TTL for a configured `ttlSeconds`. A missing, non-finite or
|
|
20
|
+
* nonpositive value falls back to 30 days. Any other value is truncated to
|
|
21
|
+
* whole seconds, as the binding does, and kept within the range KV accepts.
|
|
22
|
+
*/
|
|
23
|
+
function resolveKvExpirationTtlSeconds(ttlSeconds) {
|
|
24
|
+
return Math.min(MAX_KV_EXPIRATION_TTL_SECONDS, Math.max(MIN_KV_EXPIRATION_TTL_SECONDS, Math.trunc(typeof ttlSeconds === "number" && Number.isFinite(ttlSeconds) && ttlSeconds > 0 ? ttlSeconds : DEFAULT_KV_EXPIRATION_TTL_SECONDS)));
|
|
25
|
+
}
|
|
12
26
|
function kvKeyByteLength(key) {
|
|
13
27
|
return KV_KEY_ENCODER.encode(key).length;
|
|
14
28
|
}
|
|
@@ -27,10 +41,7 @@ function buildStorageKey(prefix, categoryPrefix, logicalKey) {
|
|
|
27
41
|
if (kvKeyByteLength(key) <= KV_KEY_MAX_BYTES) return key;
|
|
28
42
|
return `${prefix}${categoryPrefix}${HASHED_KEY_PREFIX}${fnv1a64(logicalKey)}`;
|
|
29
43
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Create the deterministic key namespace shared by runtime cache operations
|
|
32
|
-
* and deploy-time prerender population.
|
|
33
|
-
*/
|
|
44
|
+
/** Create the deterministic key namespace for runtime cache operations. */
|
|
34
45
|
function createKvKeySpace(appPrefix) {
|
|
35
46
|
const prefix = normalizeAppPrefix(appPrefix);
|
|
36
47
|
return {
|
|
@@ -40,4 +51,4 @@ function createKvKeySpace(appPrefix) {
|
|
|
40
51
|
};
|
|
41
52
|
}
|
|
42
53
|
//#endregion
|
|
43
|
-
export { ENTRY_PREFIX, createKvKeySpace };
|
|
54
|
+
export { ENTRY_PREFIX, createKvKeySpace, resolveKvExpirationTtlSeconds };
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { ResponseStoreLocationHint } from "@cloudflare/workers-response-store";
|
|
1
2
|
//#region src/cache/response-store-adapter.d.ts
|
|
2
3
|
export type ResponseStoreAdapterOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Best-effort location for metadata Durable Objects on first creation.
|
|
6
|
+
* Changing this does not move existing objects and should be treated as a
|
|
7
|
+
* cache-cold deployment change.
|
|
8
|
+
*/
|
|
9
|
+
locationHint?: ResponseStoreLocationHint;
|
|
3
10
|
mode?: "self-contained" | "service-binding";
|
|
4
11
|
/** Split version-scoped metadata across this many Durable Objects. */
|
|
5
12
|
shards?: number;
|
|
@@ -33,9 +40,7 @@ export declare function responseStoreAdapter(options?: ResponseStoreAdapterOptio
|
|
|
33
40
|
warmup: "response-store";
|
|
34
41
|
};
|
|
35
42
|
} | {
|
|
36
|
-
options:
|
|
37
|
-
shards: number;
|
|
38
|
-
};
|
|
43
|
+
options: Pick<ResponseStoreAdapterOptions, "locationHint" | "shards">;
|
|
39
44
|
adapter: string;
|
|
40
45
|
output: {
|
|
41
46
|
entry: string;
|
|
@@ -61,9 +66,7 @@ export declare function responseStoreAdapter(options?: ResponseStoreAdapterOptio
|
|
|
61
66
|
options?: undefined;
|
|
62
67
|
adapter: string;
|
|
63
68
|
} | {
|
|
64
|
-
options:
|
|
65
|
-
shards: number;
|
|
66
|
-
};
|
|
69
|
+
options: Pick<ResponseStoreAdapterOptions, "locationHint" | "shards">;
|
|
67
70
|
adapter: string;
|
|
68
71
|
};
|
|
69
72
|
};
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
//#region src/cache/response-store-adapter.ts
|
|
3
3
|
const CLOUDFLARE_WORKER_ENTRY_ID = "virtual:cloudflare/worker-entry";
|
|
4
|
+
const RESPONSE_STORE_LOCATION_HINTS = {
|
|
5
|
+
afr: true,
|
|
6
|
+
apac: true,
|
|
7
|
+
"apac-ne": true,
|
|
8
|
+
"apac-se": true,
|
|
9
|
+
eeur: true,
|
|
10
|
+
enam: true,
|
|
11
|
+
me: true,
|
|
12
|
+
oc: true,
|
|
13
|
+
sam: true,
|
|
14
|
+
weur: true,
|
|
15
|
+
wnam: true
|
|
16
|
+
};
|
|
4
17
|
/**
|
|
5
18
|
* Use Workers Response Store for both vinext response-stage and data caching.
|
|
6
19
|
* Service-binding mode keeps storage in a separate cache Worker. Self-contained
|
|
7
20
|
* mode keeps the same API and loopback in the application Worker.
|
|
8
21
|
*/
|
|
9
22
|
function responseStoreAdapter(options = {}) {
|
|
23
|
+
const locationHint = options.locationHint;
|
|
24
|
+
if (locationHint !== void 0 && (typeof locationHint !== "string" || !Object.hasOwn(RESPONSE_STORE_LOCATION_HINTS, locationHint))) throw new TypeError("Workers Response Store locationHint is not supported by Cloudflare");
|
|
10
25
|
const mode = options.mode ?? "service-binding";
|
|
11
26
|
if (mode !== "service-binding" && mode !== "self-contained") throw new Error(`Unknown Workers Response Store mode: ${String(mode)}`);
|
|
12
27
|
if (options.shards !== void 0 && (!Number.isSafeInteger(options.shards) || options.shards <= 1)) throw new TypeError("Workers Response Store shards must be an integer greater than 1");
|
|
13
|
-
const
|
|
28
|
+
const configuredOptions = {
|
|
29
|
+
...locationHint === void 0 ? {} : { locationHint },
|
|
30
|
+
...options.shards === void 0 ? {} : { shards: options.shards }
|
|
31
|
+
};
|
|
32
|
+
const runtimeOptions = Object.keys(configuredOptions).length === 0 ? {} : { options: configuredOptions };
|
|
14
33
|
const workerEntry = fileURLToPath(import.meta.resolve(mode === "self-contained" ? "./response-store-adapter.self-contained.worker.js" : "./response-store-adapter.service-binding.worker.js"));
|
|
15
34
|
const entrypoints = mode === "self-contained" ? "CacheMetadata, ResponseStoreBinding, ResponseStoreRevalidator" : "ResponseStoreClient, ResponseStoreRevalidator";
|
|
16
35
|
return {
|