@vinext/cloudflare 1.0.0-beta.8 → 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.
Files changed (59) hide show
  1. package/README.md +174 -37
  2. package/dist/cache/cdn-adapter-config.d.ts +2 -1
  3. package/dist/cache/cdn-adapter-config.js +13 -5
  4. package/dist/cache/cdn-adapter.d.ts +5 -56
  5. package/dist/cache/cdn-adapter.js +3 -61
  6. package/dist/cache/cdn-adapter.runtime.d.ts +1 -0
  7. package/dist/cache/cdn-adapter.runtime.js +17 -3
  8. package/dist/cache/cdn-adapter.worker.d.ts +1 -0
  9. package/dist/cache/cdn-adapter.worker.js +6 -4
  10. package/dist/cache/cdn-build-id.d.ts +1 -1
  11. package/dist/cache/cdn-build-id.js +1 -1
  12. package/dist/cache/config.d.ts +137 -0
  13. package/dist/cache/config.js +87 -0
  14. package/dist/cache/kv-data-adapter.d.ts +5 -1
  15. package/dist/cache/kv-data-adapter.js +6 -2
  16. package/dist/cache/kv-data-adapter.runtime.js +13 -9
  17. package/dist/cache/kv-key.d.ts +7 -4
  18. package/dist/cache/kv-key.js +16 -5
  19. package/dist/cache/response-store-adapter.d.ts +36 -0
  20. package/dist/cache/response-store-adapter.js +26 -1
  21. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +1 -0
  22. package/dist/cache/response-store-adapter.self-contained.worker.js +3 -1
  23. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +1 -0
  24. package/dist/cache/response-store-adapter.service-binding.worker.js +3 -1
  25. package/dist/cache/response-store-adapter.worker.d.ts +1 -1
  26. package/dist/cache/response-store-adapter.worker.js +148 -81
  27. package/dist/cache/response-store-cdn.runtime.js +1 -0
  28. package/dist/cache/response-store-data.runtime.js +53 -31
  29. package/dist/cache/static-assets-adapter.build.d.ts +6 -0
  30. package/dist/cache/static-assets-adapter.build.js +65 -0
  31. package/dist/cache/static-assets-adapter.d.ts +27 -0
  32. package/dist/cache/static-assets-adapter.js +26 -0
  33. package/dist/cache/static-assets-adapter.runtime.d.ts +26 -0
  34. package/dist/cache/static-assets-adapter.runtime.js +77 -0
  35. package/dist/cache/static-assets-adapter.shared.d.ts +12 -0
  36. package/dist/cache/static-assets-adapter.shared.js +4 -0
  37. package/dist/cache/workers-cache-cdn-adapter.d.ts +58 -0
  38. package/dist/cache/workers-cache-cdn-adapter.js +65 -0
  39. package/dist/cacheability-artifact.d.ts +7 -1
  40. package/dist/cacheability-artifact.js +60 -21
  41. package/dist/cacheability-probe.d.ts +2 -0
  42. package/dist/cacheability-probe.js +107 -13
  43. package/dist/cdn-warm.d.ts +2 -1
  44. package/dist/cdn-warm.js +25 -8
  45. package/dist/deploy-config.d.ts +3 -8
  46. package/dist/deploy-config.js +5 -23
  47. package/dist/deploy-help.js +58 -47
  48. package/dist/deploy.d.ts +31 -21
  49. package/dist/deploy.js +501 -286
  50. package/dist/tpr.d.ts +29 -72
  51. package/dist/tpr.js +52 -601
  52. package/dist/version-deploy.d.ts +27 -0
  53. package/dist/version-deploy.js +125 -5
  54. package/dist/wrangler-config.d.ts +19 -0
  55. package/dist/wrangler-config.js +294 -0
  56. package/dist/wrangler-version-metadata.js +1 -1
  57. package/package.json +16 -9
  58. package/dist/prerender-kv-populate.d.ts +0 -24
  59. 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 };
@@ -36,7 +36,7 @@ export type KvDataAdapterOptions = {
36
36
  * // wrangler.jsonc
37
37
  * {
38
38
  * "kv_namespaces": [
39
- * { "binding": "VINEXT_KV_CACHE", "id": "<your-kv-namespace-id>" }
39
+ * { "binding": "VINEXT_KV_CACHE" }
40
40
  * ]
41
41
  * }
42
42
  * ```
@@ -44,5 +44,9 @@ export type KvDataAdapterOptions = {
44
44
  export declare function kvDataAdapter(options?: KvDataAdapterOptions): {
45
45
  adapter: string;
46
46
  options: KvDataAdapterOptions | undefined;
47
+ capabilities: {
48
+ buildIdentity: "response-header";
49
+ warmup: "data-cache";
50
+ };
47
51
  };
48
52
  //#endregion
@@ -9,7 +9,7 @@ const DEFAULT_KV_DATA_CACHE_BINDING = "VINEXT_KV_CACHE";
9
9
  * // wrangler.jsonc
10
10
  * {
11
11
  * "kv_namespaces": [
12
- * { "binding": "VINEXT_KV_CACHE", "id": "<your-kv-namespace-id>" }
12
+ * { "binding": "VINEXT_KV_CACHE" }
13
13
  * ]
14
14
  * }
15
15
  * ```
@@ -18,7 +18,11 @@ function kvDataAdapter(options) {
18
18
  if (options?.binding !== void 0 && typeof options.binding !== "string") throw new TypeError("[vinext] kvDataAdapter({ binding }) must be a string KV binding name.");
19
19
  return {
20
20
  adapter: fileURLToPath(import.meta.resolve("./kv-data-adapter.runtime.js")),
21
- options
21
+ options,
22
+ capabilities: {
23
+ buildIdentity: "response-header",
24
+ warmup: "data-cache"
25
+ }
22
26
  };
23
27
  }
24
28
  //#endregion
@@ -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", "id": "<your-kv-namespace-id>" }] }
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 ?? 2592e3;
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 = revalidateAt !== null ? this.ttlSeconds : void 0;
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), { expirationTtl: 2592e3 })));
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}", "id": "<your-kv-namespace-id>" }]`);
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,
@@ -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
@@ -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,6 +1,15 @@
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";
11
+ /** Split version-scoped metadata across this many Durable Objects. */
12
+ shards?: number;
4
13
  };
5
14
  /**
6
15
  * Use Workers Response Store for both vinext response-stage and data caching.
@@ -9,6 +18,29 @@ export type ResponseStoreAdapterOptions = {
9
18
  */
10
19
  export declare function responseStoreAdapter(options?: ResponseStoreAdapterOptions): {
11
20
  cdn: {
21
+ options?: undefined;
22
+ adapter: string;
23
+ output: {
24
+ entry: string;
25
+ matchesBuild({ plugins }: {
26
+ plugins: readonly {
27
+ name?: string;
28
+ }[];
29
+ }): boolean;
30
+ transformHostEntry({ code, id }: {
31
+ code: string;
32
+ id: string;
33
+ }): string | null;
34
+ type: "multi-stage";
35
+ };
36
+ capabilities: {
37
+ buildIdentity: "response-header";
38
+ isResponsePolicyHeader: (name: string) => boolean;
39
+ requestRouting: "uncached-stage";
40
+ warmup: "response-store";
41
+ };
42
+ } | {
43
+ options: Pick<ResponseStoreAdapterOptions, "locationHint" | "shards">;
12
44
  adapter: string;
13
45
  output: {
14
46
  entry: string;
@@ -31,6 +63,10 @@ export declare function responseStoreAdapter(options?: ResponseStoreAdapterOptio
31
63
  };
32
64
  };
33
65
  data: {
66
+ options?: undefined;
67
+ adapter: string;
68
+ } | {
69
+ options: Pick<ResponseStoreAdapterOptions, "locationHint" | "shards">;
34
70
  adapter: string;
35
71
  };
36
72
  };
@@ -1,19 +1,41 @@
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)}`);
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");
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 };
12
33
  const workerEntry = fileURLToPath(import.meta.resolve(mode === "self-contained" ? "./response-store-adapter.self-contained.worker.js" : "./response-store-adapter.service-binding.worker.js"));
13
34
  const entrypoints = mode === "self-contained" ? "CacheMetadata, ResponseStoreBinding, ResponseStoreRevalidator" : "ResponseStoreClient, ResponseStoreRevalidator";
14
35
  return {
15
36
  cdn: {
16
37
  adapter: fileURLToPath(import.meta.resolve("./response-store-cdn.runtime.js")),
38
+ ...runtimeOptions,
17
39
  output: {
18
40
  entry: workerEntry,
19
41
  matchesBuild({ plugins }) {
@@ -32,7 +54,10 @@ function responseStoreAdapter(options = {}) {
32
54
  warmup: "response-store"
33
55
  }
34
56
  },
35
- data: { adapter: fileURLToPath(import.meta.resolve("./response-store-data.runtime.js")) }
57
+ data: {
58
+ adapter: fileURLToPath(import.meta.resolve("./response-store-data.runtime.js")),
59
+ ...runtimeOptions
60
+ }
36
61
  };
37
62
  }
38
63
  //#endregion
@@ -1,4 +1,5 @@
1
1
  import { VinextResponseStoreEnv } from "./response-store-adapter.worker.js";
2
+ import "vinext/internal/server/cloudflare-workers-tracing";
2
3
  import { ResponseStoreRevalidatorEntrypoint, WorkersResponseStoreEnv } from "@cloudflare/workers-response-store";
3
4
  import { CacheMetadata as CacheMetadata$1, ResponseStoreBinding as ResponseStoreBinding$1 } from "@cloudflare/workers-response-store/service";
4
5
  //#region src/cache/response-store-adapter.self-contained.worker.d.ts
@@ -1,7 +1,9 @@
1
1
  import { createVinextResponseStoreHandler, createVinextResponseStoreOptions } from "./response-store-adapter.worker.js";
2
+ import "vinext/internal/server/cloudflare-workers-tracing";
2
3
  import { createWorkersResponseStore } from "@cloudflare/workers-response-store";
4
+ import { configuredCdnCacheAdapterOptions } from "virtual:vinext-cdn-cache-adapter";
3
5
  //#region src/cache/response-store-adapter.self-contained.worker.ts
4
- const responseStore = createWorkersResponseStore(createVinextResponseStoreOptions());
6
+ const responseStore = createWorkersResponseStore(createVinextResponseStoreOptions(configuredCdnCacheAdapterOptions));
5
7
  const CacheMetadata = responseStore.entrypoints.CacheMetadata;
6
8
  const ResponseStoreBinding = responseStore.entrypoints.ResponseStoreBinding;
7
9
  const ResponseStoreRevalidator = responseStore.entrypoints.ResponseStoreRevalidator;
@@ -1,4 +1,5 @@
1
1
  import { VinextResponseStoreEnv } from "./response-store-adapter.worker.js";
2
+ import "vinext/internal/server/cloudflare-workers-tracing";
2
3
  import { ResponseStoreClientEntrypoint, ResponseStoreRevalidatorEntrypoint, WorkersResponseStoreClientEnv } from "@cloudflare/workers-response-store";
3
4
  //#region src/cache/response-store-adapter.service-binding.worker.d.ts
4
5
  export declare const ResponseStoreClient: ResponseStoreClientEntrypoint;
@@ -1,7 +1,9 @@
1
1
  import { createVinextResponseStoreHandler, createVinextResponseStoreOptions } from "./response-store-adapter.worker.js";
2
+ import "vinext/internal/server/cloudflare-workers-tracing";
2
3
  import { createWorkersResponseStoreClient } from "@cloudflare/workers-response-store";
4
+ import { configuredCdnCacheAdapterOptions } from "virtual:vinext-cdn-cache-adapter";
3
5
  //#region src/cache/response-store-adapter.service-binding.worker.ts
4
- const responseStore = createWorkersResponseStoreClient(createVinextResponseStoreOptions());
6
+ const responseStore = createWorkersResponseStoreClient(createVinextResponseStoreOptions(configuredCdnCacheAdapterOptions));
5
7
  const ResponseStoreClient = responseStore.entrypoints.ResponseStoreClient;
6
8
  const ResponseStoreRevalidator = responseStore.entrypoints.ResponseStoreRevalidator;
7
9
  var response_store_adapter_service_binding_worker_default = createVinextResponseStoreHandler(responseStore);
@@ -7,7 +7,7 @@ type WorkerExecutionContext = {
7
7
  waitUntil(promise: Promise<unknown>): void;
8
8
  };
9
9
  export type VinextResponseStoreEnv = WorkersResponseStoreClientEnv | WorkersResponseStoreEnv;
10
- export declare function createVinextResponseStoreOptions<Env extends VinextResponseStoreEnv>(): WorkersResponseStoreOptions<Env>;
10
+ export declare function createVinextResponseStoreOptions<Env extends VinextResponseStoreEnv>(configuration?: Record<string, unknown>): WorkersResponseStoreOptions<Env>;
11
11
  export declare function createVinextResponseStoreHandler(store: WorkersResponseStore): {
12
12
  fetch(request: Request, env: VinextResponseStoreEnv, ctx: WorkerExecutionContext): Promise<Response>;
13
13
  };