@vinext/cloudflare 1.0.0-beta.1 → 1.0.0-beta.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 (66) hide show
  1. package/README.md +193 -1
  2. package/dist/cache/cdn-adapter-config.d.ts +31 -0
  3. package/dist/cache/cdn-adapter-config.js +95 -0
  4. package/dist/cache/cdn-adapter.d.ts +48 -15
  5. package/dist/cache/cdn-adapter.js +50 -13
  6. package/dist/cache/cdn-adapter.runtime.d.ts +26 -4
  7. package/dist/cache/cdn-adapter.runtime.js +150 -37
  8. package/dist/cache/cdn-adapter.worker.d.ts +31 -0
  9. package/dist/cache/cdn-adapter.worker.js +373 -0
  10. package/dist/cache/cdn-build-id.d.ts +6 -0
  11. package/dist/cache/cdn-build-id.js +9 -0
  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 +32 -8
  15. package/dist/cache/kv-data-adapter.js +5 -1
  16. package/dist/cache/kv-data-adapter.runtime.d.ts +45 -13
  17. package/dist/cache/kv-data-adapter.runtime.js +132 -52
  18. package/dist/cache/kv-key.d.ts +15 -0
  19. package/dist/cache/kv-key.js +43 -0
  20. package/dist/cache/response-store-adapter.d.ts +73 -0
  21. package/dist/cache/response-store-adapter.js +64 -0
  22. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +18 -0
  23. package/dist/cache/response-store-adapter.self-contained.worker.js +12 -0
  24. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +16 -0
  25. package/dist/cache/response-store-adapter.service-binding.worker.js +11 -0
  26. package/dist/cache/response-store-adapter.worker.d.ts +14 -0
  27. package/dist/cache/response-store-adapter.worker.js +278 -0
  28. package/dist/cache/response-store-cdn.runtime.d.ts +5 -0
  29. package/dist/cache/response-store-cdn.runtime.js +41 -0
  30. package/dist/cache/response-store-data.runtime.d.ts +34 -0
  31. package/dist/cache/response-store-data.runtime.js +300 -0
  32. package/dist/cacheability-artifact.d.ts +10 -0
  33. package/dist/cacheability-artifact.js +215 -0
  34. package/dist/cacheability-manifest-limits.d.ts +4 -0
  35. package/dist/cacheability-manifest-limits.js +7 -0
  36. package/dist/cacheability-probe.d.ts +49 -0
  37. package/dist/cacheability-probe.js +660 -0
  38. package/dist/cdn-warm.d.ts +110 -12
  39. package/dist/cdn-warm.js +880 -82
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +15 -1
  42. package/dist/deploy-config.d.ts +14 -11
  43. package/dist/deploy-config.js +24 -5
  44. package/dist/deploy-help.d.ts +3 -4
  45. package/dist/deploy-help.js +65 -22
  46. package/dist/deploy.d.ts +139 -42
  47. package/dist/deploy.js +946 -132
  48. package/dist/images/images-optimizer.d.ts +5 -5
  49. package/dist/images/images-optimizer.runtime.d.ts +1 -5
  50. package/dist/prerender-kv-populate.d.ts +3 -4
  51. package/dist/prerender-kv-populate.js +51 -18
  52. package/dist/tpr.d.ts +34 -70
  53. package/dist/tpr.js +50 -603
  54. package/dist/utils/cache-control-metadata.d.ts +3 -4
  55. package/dist/version-deploy.d.ts +46 -17
  56. package/dist/version-deploy.js +151 -14
  57. package/dist/version-headers.d.ts +2 -0
  58. package/dist/version-headers.js +2 -0
  59. package/dist/worker-deployment-url.d.ts +9 -0
  60. package/dist/worker-deployment-url.js +45 -0
  61. package/dist/workers-dev-url.d.ts +2 -3
  62. package/dist/wrangler-config.d.ts +19 -0
  63. package/dist/wrangler-config.js +294 -0
  64. package/dist/wrangler-version-metadata.d.ts +7 -0
  65. package/dist/wrangler-version-metadata.js +9 -0
  66. package/package.json +21 -11
@@ -0,0 +1,137 @@
1
+ import { CloudflareConfig, WorkerConfig } from "@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
24
+ };
25
+ env: {
26
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-config").R2Binding;
27
+ CACHE_METADATA: {
28
+ type: "durable-object";
29
+ worker: string;
30
+ exportName: string;
31
+ };
32
+ CF_VERSION_METADATA: import("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
63
+ };
64
+ env: {
65
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
102
+ };
103
+ env: {
104
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-config").VersionMetadataBinding;
131
+ };
132
+ exports: {
133
+ VinextCachedResponse: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
134
+ VinextUncachedResponse: import("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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("@cloudflare/vite-plugin/experimental-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,11 +1,32 @@
1
1
  //#region src/cache/kv-data-adapter.d.ts
2
- declare const DEFAULT_KV_DATA_CACHE_BINDING = "VINEXT_KV_CACHE";
2
+ export declare const DEFAULT_KV_DATA_CACHE_BINDING = "VINEXT_KV_CACHE";
3
3
  /** Options accepted by {@link kvDataAdapter}, forwarded to the runtime factory. */
4
- type KvDataAdapterOptions = {
5
- /** KV namespace binding name on the Worker `env`. @default "VINEXT_KV_CACHE" */binding?: string; /** Namespace prefix for cache keys (isolates multiple apps in one namespace). */
6
- appPrefix?: string; /** Default KV `expirationTtl` in seconds. @default 2592000 (30 days) */
7
- ttlSeconds?: number; /** TTL in milliseconds for the in-memory tag-invalidation cache. @default 5000 */
4
+ export type KvDataAdapterOptions = {
5
+ /** KV namespace binding name on the Worker `env`. @default "VINEXT_KV_CACHE" */
6
+ binding?: string;
7
+ /** Namespace prefix for cache keys (isolates multiple apps in one namespace). */
8
+ appPrefix?: string;
9
+ /** Default KV `expirationTtl` in seconds. @default 2592000 (30 days) */
10
+ ttlSeconds?: number;
11
+ /** TTL in milliseconds for the in-memory tag-invalidation cache. @default 5000 */
8
12
  tagCacheTtlMs?: number;
13
+ /**
14
+ * KV `cacheTtl` in seconds for entry reads, letting a colo answer a repeat
15
+ * read from its own cache instead of the central store. The runtime rejects
16
+ * a value below 30, so lower values are raised to 30.
17
+ *
18
+ * Trade-off: after a `set()`, a colo that already cached the key can serve
19
+ * the superseded value for up to this long.
20
+ *
21
+ * It applies to entry reads only. Tag markers, which `revalidateTag()` and
22
+ * `revalidatePath()` write, keep KV's own default cacheTtl of 60 s rather
23
+ * than this longer one, so this option never widens the window in which a
24
+ * colo can miss a publish. That window stays `tagCacheTtlMs` plus whatever
25
+ * the KV default cache holds, with or without this option.
26
+ *
27
+ * @default undefined (entry reads keep KV's default cacheTtl of 60 s)
28
+ */
29
+ entryCacheTtlSeconds?: number;
9
30
  };
10
31
  /**
11
32
  * Cloudflare KV data cache.
@@ -20,9 +41,12 @@ type KvDataAdapterOptions = {
20
41
  * }
21
42
  * ```
22
43
  */
23
- declare function kvDataAdapter(options?: KvDataAdapterOptions): {
44
+ export declare function kvDataAdapter(options?: KvDataAdapterOptions): {
24
45
  adapter: string;
25
46
  options: KvDataAdapterOptions | undefined;
47
+ capabilities: {
48
+ buildIdentity: "response-header";
49
+ warmup: "data-cache";
50
+ };
26
51
  };
27
- //#endregion
28
- export { DEFAULT_KV_DATA_CACHE_BINDING, KvDataAdapterOptions, kvDataAdapter };
52
+ //#endregion
@@ -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,15 +1,21 @@
1
1
  import { KvDataAdapterOptions } from "./kv-data-adapter.js";
2
+ import { ENTRY_PREFIX } from "./kv-key.js";
2
3
  import { ExecutionContextLike } from "vinext/shims/request-context";
3
4
  import { CacheHandler, CacheHandlerValue, IncrementalCacheValue } from "vinext/shims/cache";
4
-
5
5
  //#region src/cache/kv-data-adapter.runtime.d.ts
6
6
  type KVNamespace = {
7
7
  get(key: string, options?: {
8
8
  type?: string;
9
+ cacheTtl?: number;
9
10
  }): Promise<string | null>;
10
11
  get(key: string, options: {
11
12
  type: "arrayBuffer";
12
13
  }): Promise<ArrayBuffer | null>;
14
+ /** Bulk read, capped at 100 keys per call. */
15
+ get(keys: string[], options?: {
16
+ type?: string;
17
+ cacheTtl?: number;
18
+ }): Promise<Map<string, string | null>>;
13
19
  put(key: string, value: string | ArrayBuffer | ReadableStream, options?: {
14
20
  expirationTtl?: number;
15
21
  metadata?: Record<string, unknown>;
@@ -28,8 +34,6 @@ type KVNamespace = {
28
34
  cursor?: string;
29
35
  }>;
30
36
  };
31
- /** Key prefix for cache entries. */
32
- declare const ENTRY_PREFIX = "cache:";
33
37
  /**
34
38
  * Cloudflare KV data cache handler.
35
39
  *
@@ -51,25 +55,50 @@ declare const ENTRY_PREFIX = "cache:";
51
55
  * the first request. This class stays exported for the runtime factory and for
52
56
  * backwards compatibility, but is not the recommended consumer API.
53
57
  */
54
- declare class KVCacheHandler implements CacheHandler {
58
+ export declare class KVCacheHandler implements CacheHandler {
55
59
  private kv;
56
- private prefix;
60
+ private keySpace;
57
61
  private ctx;
58
62
  private ttlSeconds;
59
63
  /** Local in-memory cache for tag invalidation timestamps. Avoids redundant KV reads. */
60
64
  private _tagCache;
65
+ /** Monotonic ordering for concurrent tag-cache fills and local invalidations. */
66
+ private _tagCacheOrder;
61
67
  /** TTL (ms) for local tag cache entries. After this, re-fetch from KV. */
62
68
  private _tagCacheTtl;
69
+ /** Read options for entry keys only. `undefined` keeps the KV default cacheTtl. */
70
+ private _entryReadOptions;
63
71
  constructor(kvNamespace: KVNamespace, options?: {
64
72
  appPrefix?: string;
65
73
  ctx?: ExecutionContextLike;
66
- ttlSeconds?: number; /** TTL in milliseconds for the local tag cache. Defaults to 5000ms. */
74
+ ttlSeconds?: number;
75
+ /** TTL in milliseconds for the local tag cache. Defaults to 5000ms. */
67
76
  tagCacheTtlMs?: number;
77
+ /** KV `cacheTtl` in seconds for entry reads. Off by default, never used for tag markers. */
78
+ entryCacheTtlSeconds?: number;
68
79
  });
80
+ private _entryKey;
81
+ private _tagKey;
69
82
  get(key: string, _ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
70
83
  /**
71
- * Check tag invalidation markers for stored tags or read-time soft tags.
72
- * Uses a local in-memory cache to avoid redundant KV reads for recently-seen tags.
84
+ * Load the invalidation markers these tags still need into the local cache.
85
+ * Tags a recent read already cached cost nothing, so the entry-tag batch only
86
+ * pays for what the soft-tag batch did not already fetch.
87
+ */
88
+ private _primeTagCache;
89
+ /**
90
+ * Read every marker key, one round trip per 100-key chunk.
91
+ *
92
+ * Markers never take the entry `cacheTtl`: `revalidateTag()` writes them, so
93
+ * a longer colo cache would hide a publish from that colo for its span. They
94
+ * keep KV's own default cacheTtl of 60 s.
95
+ */
96
+ private _readTagMarkers;
97
+ /**
98
+ * Report whether any tag has an invalidation marker at or after `lastModified`.
99
+ * Call `_primeTagCache` for the same tags first — a tag with no cache entry
100
+ * counts as never invalidated. Pass `requireFresh` to call it before a prime,
101
+ * so an entry past `tagCacheTtlMs` does not answer for a tag it never re-read.
73
102
  */
74
103
  private _hasRevalidatedTag;
75
104
  set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
@@ -108,6 +137,12 @@ declare class KVCacheHandler implements CacheHandler {
108
137
  * fresh KVCacheHandler per request or invoke this method explicitly.
109
138
  */
110
139
  resetRequestCache(): void;
140
+ /**
141
+ * Clean up a bad entry only when this read cannot be an explicitly cached
142
+ * older version of the shared KV value. KV has no conditional delete, so a
143
+ * cached read must remain a non-destructive miss.
144
+ */
145
+ private _deleteEntryReadInBackground;
111
146
  /**
112
147
  * Fire a KV delete in the background.
113
148
  * Prefers the per-request ExecutionContext from ALS (set by
@@ -124,12 +159,9 @@ declare class KVCacheHandler implements CacheHandler {
124
159
  */
125
160
  private _put;
126
161
  }
127
- declare const createKvDataCacheAdapter: ({
128
- env,
129
- options
130
- }: {
162
+ declare const createKvDataCacheAdapter: ({ env, options }: {
131
163
  env?: Record<string, unknown>;
132
164
  options?: KvDataAdapterOptions;
133
165
  }) => CacheHandler;
134
166
  //#endregion
135
- export { ENTRY_PREFIX, KVCacheHandler, createKvDataCacheAdapter as default };
167
+ export { ENTRY_PREFIX, createKvDataCacheAdapter as default };