@vercel/kv2 0.0.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.
Files changed (166) hide show
  1. package/README.md +87 -0
  2. package/SKILL.md +65 -0
  3. package/dist/blob-format.d.ts +35 -0
  4. package/dist/blob-format.d.ts.map +1 -0
  5. package/dist/blob-format.js +91 -0
  6. package/dist/blob-format.js.map +1 -0
  7. package/dist/blob-store.d.ts +11 -0
  8. package/dist/blob-store.d.ts.map +1 -0
  9. package/dist/blob-store.js +32 -0
  10. package/dist/blob-store.js.map +1 -0
  11. package/dist/cache.d.ts +33 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +146 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/cached-kv.d.ts +63 -0
  16. package/dist/cached-kv.d.ts.map +1 -0
  17. package/dist/cached-kv.js +891 -0
  18. package/dist/cached-kv.js.map +1 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +342 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/create-kv.d.ts +86 -0
  24. package/dist/create-kv.d.ts.map +1 -0
  25. package/dist/create-kv.js +125 -0
  26. package/dist/create-kv.js.map +1 -0
  27. package/dist/disk-cache.d.ts.map +1 -0
  28. package/dist/disk-cache.js.map +1 -0
  29. package/dist/index.d.ts +16 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +13 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/indexed-kv.d.ts +44 -0
  34. package/dist/indexed-kv.d.ts.map +1 -0
  35. package/dist/indexed-kv.js +373 -0
  36. package/dist/indexed-kv.js.map +1 -0
  37. package/dist/manifest-log.d.ts +57 -0
  38. package/dist/manifest-log.d.ts.map +1 -0
  39. package/dist/manifest-log.js +128 -0
  40. package/dist/manifest-log.js.map +1 -0
  41. package/dist/memory-cache.d.ts +22 -0
  42. package/dist/memory-cache.d.ts.map +1 -0
  43. package/dist/memory-cache.js +90 -0
  44. package/dist/memory-cache.js.map +1 -0
  45. package/dist/proxy-cache.d.ts +40 -0
  46. package/dist/proxy-cache.d.ts.map +1 -0
  47. package/dist/proxy-cache.js +124 -0
  48. package/dist/proxy-cache.js.map +1 -0
  49. package/dist/readme.test.d.ts +9 -0
  50. package/dist/readme.test.d.ts.map +1 -0
  51. package/dist/readme.test.js +285 -0
  52. package/dist/readme.test.js.map +1 -0
  53. package/dist/schema/define-schema.d.ts +35 -0
  54. package/dist/schema/define-schema.d.ts.map +1 -0
  55. package/dist/schema/define-schema.js +70 -0
  56. package/dist/schema/define-schema.js.map +1 -0
  57. package/dist/schema/index.d.ts +4 -0
  58. package/dist/schema/index.d.ts.map +1 -0
  59. package/dist/schema/index.js +5 -0
  60. package/dist/schema/index.js.map +1 -0
  61. package/dist/schema/key-builders.d.ts +40 -0
  62. package/dist/schema/key-builders.d.ts.map +1 -0
  63. package/dist/schema/key-builders.js +124 -0
  64. package/dist/schema/key-builders.js.map +1 -0
  65. package/dist/schema/schema-kv.d.ts +48 -0
  66. package/dist/schema/schema-kv.d.ts.map +1 -0
  67. package/dist/schema/schema-kv.js +96 -0
  68. package/dist/schema/schema-kv.js.map +1 -0
  69. package/dist/schema/tree.d.ts +14 -0
  70. package/dist/schema/tree.d.ts.map +1 -0
  71. package/dist/schema/tree.js +135 -0
  72. package/dist/schema/tree.js.map +1 -0
  73. package/dist/schema/types.d.ts +135 -0
  74. package/dist/schema/types.d.ts.map +1 -0
  75. package/dist/schema/types.js +2 -0
  76. package/dist/schema/types.js.map +1 -0
  77. package/dist/testing/core-tests.d.ts +30 -0
  78. package/dist/testing/core-tests.d.ts.map +1 -0
  79. package/dist/testing/core-tests.js +383 -0
  80. package/dist/testing/core-tests.js.map +1 -0
  81. package/dist/testing/create-kv-test-setup.d.ts +21 -0
  82. package/dist/testing/create-kv-test-setup.d.ts.map +1 -0
  83. package/dist/testing/create-kv-test-setup.js +25 -0
  84. package/dist/testing/create-kv-test-setup.js.map +1 -0
  85. package/dist/testing/debug-manifest.d.ts +2 -0
  86. package/dist/testing/debug-manifest.d.ts.map +1 -0
  87. package/dist/testing/debug-manifest.js +14 -0
  88. package/dist/testing/debug-manifest.js.map +1 -0
  89. package/dist/testing/fake-blob-store.d.ts +23 -0
  90. package/dist/testing/fake-blob-store.d.ts.map +1 -0
  91. package/dist/testing/fake-blob-store.js +158 -0
  92. package/dist/testing/fake-blob-store.js.map +1 -0
  93. package/dist/testing/fake-cache.d.ts +54 -0
  94. package/dist/testing/fake-cache.d.ts.map +1 -0
  95. package/dist/testing/fake-cache.js +137 -0
  96. package/dist/testing/fake-cache.js.map +1 -0
  97. package/dist/testing/index.d.ts +34 -0
  98. package/dist/testing/index.d.ts.map +1 -0
  99. package/dist/testing/index.js +101 -0
  100. package/dist/testing/index.js.map +1 -0
  101. package/dist/testing/manifest-test-setup.d.ts +22 -0
  102. package/dist/testing/manifest-test-setup.d.ts.map +1 -0
  103. package/dist/testing/manifest-test-setup.js +43 -0
  104. package/dist/testing/manifest-test-setup.js.map +1 -0
  105. package/dist/testing/perf-test.d.ts +13 -0
  106. package/dist/testing/perf-test.d.ts.map +1 -0
  107. package/dist/testing/perf-test.js +101 -0
  108. package/dist/testing/perf-test.js.map +1 -0
  109. package/dist/testing/run-tests.d.ts +2 -0
  110. package/dist/testing/run-tests.d.ts.map +1 -0
  111. package/dist/testing/run-tests.js +141 -0
  112. package/dist/testing/run-tests.js.map +1 -0
  113. package/dist/testing/setup.d.ts +2 -0
  114. package/dist/testing/setup.d.ts.map +1 -0
  115. package/dist/testing/setup.js +3 -0
  116. package/dist/testing/setup.js.map +1 -0
  117. package/dist/testing/test-index.d.ts +28 -0
  118. package/dist/testing/test-index.d.ts.map +1 -0
  119. package/dist/testing/test-index.js +35 -0
  120. package/dist/testing/test-index.js.map +1 -0
  121. package/dist/testing/test-setup.d.ts +32 -0
  122. package/dist/testing/test-setup.d.ts.map +1 -0
  123. package/dist/testing/test-setup.js +72 -0
  124. package/dist/testing/test-setup.js.map +1 -0
  125. package/dist/testing/upstream-kv-test-setup.d.ts +30 -0
  126. package/dist/testing/upstream-kv-test-setup.d.ts.map +1 -0
  127. package/dist/testing/upstream-kv-test-setup.js +66 -0
  128. package/dist/testing/upstream-kv-test-setup.js.map +1 -0
  129. package/dist/testing/vitest-compat.d.ts +92 -0
  130. package/dist/testing/vitest-compat.d.ts.map +1 -0
  131. package/dist/testing/vitest-compat.js +601 -0
  132. package/dist/testing/vitest-compat.js.map +1 -0
  133. package/dist/tracing.d.ts +71 -0
  134. package/dist/tracing.d.ts.map +1 -0
  135. package/dist/tracing.js +232 -0
  136. package/dist/tracing.js.map +1 -0
  137. package/dist/typed-kv.d.ts +120 -0
  138. package/dist/typed-kv.d.ts.map +1 -0
  139. package/dist/typed-kv.js +565 -0
  140. package/dist/typed-kv.js.map +1 -0
  141. package/dist/typed-upstream-kv.d.ts +17 -0
  142. package/dist/typed-upstream-kv.d.ts.map +1 -0
  143. package/dist/typed-upstream-kv.js +38 -0
  144. package/dist/typed-upstream-kv.js.map +1 -0
  145. package/dist/types.d.ts +199 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +23 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/upstream-kv.d.ts +84 -0
  150. package/dist/upstream-kv.d.ts.map +1 -0
  151. package/dist/upstream-kv.js +375 -0
  152. package/dist/upstream-kv.js.map +1 -0
  153. package/docs/api-reference.md +222 -0
  154. package/docs/caching.md +60 -0
  155. package/docs/cli.md +123 -0
  156. package/docs/copy-on-write-branches.md +98 -0
  157. package/docs/getting-started.md +61 -0
  158. package/docs/indexes.md +122 -0
  159. package/docs/iterating-and-pagination.md +93 -0
  160. package/docs/metadata.md +82 -0
  161. package/docs/optimistic-locking.md +72 -0
  162. package/docs/schema-and-trees.md +222 -0
  163. package/docs/streaming.md +61 -0
  164. package/docs/testing-and-tracing.md +141 -0
  165. package/docs/typed-stores.md +68 -0
  166. package/package.json +63 -0
@@ -0,0 +1,375 @@
1
+ import { TypedKV } from "./typed-kv.js";
2
+ /**
3
+ * Wrapper that adds copy-on-write (CoW) behavior to KV2.
4
+ *
5
+ * - Reads check manifest to route to local or upstream (avoids unnecessary blob lookups)
6
+ * - Writes go to local only
7
+ * - Deletes create tombstones to prevent upstream fallthrough
8
+ * - keys() returns keys from the manifest (discovered + local)
9
+ */
10
+ export class UpstreamKV {
11
+ local;
12
+ upstream;
13
+ manifest;
14
+ constructor(local, upstream, manifest) {
15
+ this.local = local;
16
+ this.upstream = upstream;
17
+ this.manifest = manifest;
18
+ }
19
+ /**
20
+ * Wrap an upstream entry to redirect update() calls through UpstreamKV.set().
21
+ * This ensures updates write to local storage, not upstream.
22
+ *
23
+ * Note: We don't use expectedVersion here because we're creating a new local
24
+ * shadow of the upstream entry, not updating an existing local entry.
25
+ */
26
+ wrapUpstreamEntry(key, entry) {
27
+ const self = this;
28
+ return {
29
+ exists: true,
30
+ metadata: entry.metadata,
31
+ version: entry.version,
32
+ get value() {
33
+ return entry.value;
34
+ },
35
+ get stream() {
36
+ return entry.stream;
37
+ },
38
+ async update(value, metadata) {
39
+ const meta = metadata ?? entry.metadata;
40
+ // Write to local without expectedVersion - creates a new local shadow
41
+ return self.set(key, value, meta);
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * Get a value. Uses manifest to route efficiently:
47
+ * - Keys marked "local" → check local only
48
+ * - Keys marked "upstream" → check upstream only (skip local blob lookup)
49
+ * - Keys marked "tombstone" → return not found
50
+ * - Unknown keys → check local, then upstream
51
+ */
52
+ async get(key) {
53
+ // Check manifest for this specific key (cached as single entry)
54
+ const keyMeta = await this.manifest.getKeySource(key);
55
+ // Tombstoned keys don't exist
56
+ if (keyMeta?.source === "tombstone") {
57
+ return {
58
+ exists: false,
59
+ metadata: undefined,
60
+ value: undefined,
61
+ stream: undefined,
62
+ };
63
+ }
64
+ // Key known to be local - check local only
65
+ // Local entries' update() already writes to local, so no wrapping needed
66
+ if (keyMeta?.source === "local") {
67
+ return this.local.get(key);
68
+ }
69
+ // Key known to be upstream - skip local blob lookup, go straight to upstream
70
+ // Wrap the entry so update() writes to local instead of upstream
71
+ if (keyMeta?.source === "upstream") {
72
+ const result = await this.upstream.get(key);
73
+ if (result.exists) {
74
+ return this.wrapUpstreamEntry(key, result);
75
+ }
76
+ return result;
77
+ }
78
+ // Unknown key - check local first, then upstream
79
+ const localResult = await this.local.get(key);
80
+ if (localResult.exists) {
81
+ // Record in manifest for future lookups (only happens once per key)
82
+ await this.manifest.recordAdd(key, "local");
83
+ return localResult;
84
+ }
85
+ // Try upstream
86
+ const upstreamResult = await this.upstream.get(key);
87
+ if (upstreamResult.exists) {
88
+ // Record discovery for future lookups (only happens once per key)
89
+ await this.manifest.recordAdd(key, "upstream");
90
+ // Wrap so update() writes to local
91
+ return this.wrapUpstreamEntry(key, upstreamResult);
92
+ }
93
+ return upstreamResult;
94
+ }
95
+ /**
96
+ * Set a value. Writes to local only.
97
+ */
98
+ async set(key, value, ...[metadata, options]) {
99
+ // Write to local - use type assertion for the complex conditional type
100
+ const result = await this.local.set(key, value, metadata, options);
101
+ // Record in manifest - await for consistency
102
+ await this.manifest.recordAdd(key, "local");
103
+ return result;
104
+ }
105
+ /**
106
+ * Delete a key. Creates a tombstone to prevent upstream fallthrough.
107
+ */
108
+ async delete(key) {
109
+ // Delete from local
110
+ await this.local.delete(key);
111
+ // Record tombstone - await for consistency (next get must see it)
112
+ await this.manifest.recordDelete(key);
113
+ }
114
+ /**
115
+ * Iterate over keys. Returns merged keys from local, manifest, and upstream,
116
+ * excluding tombstones.
117
+ */
118
+ keys(prefix) {
119
+ const self = this;
120
+ return {
121
+ async *[Symbol.asyncIterator]() {
122
+ const manifestEntries = await self.manifest.getAll();
123
+ const seen = new Set();
124
+ // First yield local keys
125
+ for await (const key of self.local.keys(prefix)) {
126
+ seen.add(key);
127
+ yield key;
128
+ }
129
+ // Then yield manifest keys (discovered from upstream) not yet seen
130
+ for (const [key, meta] of manifestEntries) {
131
+ if (meta.source === "tombstone")
132
+ continue;
133
+ if (prefix && !key.startsWith(prefix))
134
+ continue;
135
+ if (seen.has(key))
136
+ continue;
137
+ seen.add(key);
138
+ yield key;
139
+ }
140
+ // Finally yield upstream keys not yet seen or tombstoned
141
+ for await (const key of self.upstream.keys(prefix)) {
142
+ if (seen.has(key))
143
+ continue;
144
+ if (manifestEntries.get(key)?.source === "tombstone")
145
+ continue;
146
+ seen.add(key);
147
+ yield key;
148
+ }
149
+ },
150
+ async page(limit, cursor) {
151
+ // Cursor format: "local:cursor" | "upstream:cursor" | undefined
152
+ // Local keys and manifest are collected fully (typically small)
153
+ // Upstream uses cursor-based pagination
154
+ const manifestEntries = await self.manifest.getAll();
155
+ const tombstones = new Set();
156
+ for (const [key, meta] of manifestEntries) {
157
+ if (meta.source === "tombstone")
158
+ tombstones.add(key);
159
+ }
160
+ const keys = [];
161
+ let phase = "local";
162
+ let phaseCursor;
163
+ // Parse cursor
164
+ if (cursor) {
165
+ const colonIndex = cursor.indexOf(":");
166
+ if (colonIndex !== -1) {
167
+ phase = cursor.slice(0, colonIndex);
168
+ phaseCursor = cursor.slice(colonIndex + 1) || undefined;
169
+ }
170
+ }
171
+ const seen = new Set();
172
+ // Local phase
173
+ if (phase === "local") {
174
+ const { keys: localKeys, cursor: localCursor } = await self.local
175
+ .keys(prefix)
176
+ .page(limit, phaseCursor);
177
+ for (const key of localKeys) {
178
+ seen.add(key);
179
+ keys.push(key);
180
+ if (keys.length >= limit) {
181
+ return {
182
+ keys,
183
+ cursor: localCursor ? `local:${localCursor}` : "manifest:",
184
+ };
185
+ }
186
+ }
187
+ // If local has more, continue there next time
188
+ if (localCursor) {
189
+ return { keys, cursor: `local:${localCursor}` };
190
+ }
191
+ // Move to manifest phase
192
+ phase = "manifest";
193
+ phaseCursor = undefined;
194
+ }
195
+ // Manifest phase - collect all matching manifest keys not seen
196
+ if (phase === "manifest") {
197
+ for (const [key, meta] of manifestEntries) {
198
+ if (meta.source === "tombstone")
199
+ continue;
200
+ if (prefix && !key.startsWith(prefix))
201
+ continue;
202
+ if (seen.has(key))
203
+ continue;
204
+ seen.add(key);
205
+ keys.push(key);
206
+ if (keys.length >= limit) {
207
+ // Can't cursor into manifest easily, move to upstream next
208
+ return { keys, cursor: "upstream:" };
209
+ }
210
+ }
211
+ // Move to upstream phase
212
+ phase = "upstream";
213
+ phaseCursor = undefined;
214
+ }
215
+ // Upstream phase
216
+ if (phase === "upstream") {
217
+ const remaining = limit - keys.length;
218
+ if (remaining > 0) {
219
+ // Need to fetch more from upstream, filtering duplicates and tombstones
220
+ let upstreamCursor = phaseCursor;
221
+ while (keys.length < limit) {
222
+ const { keys: upstreamKeys, cursor: nextCursor } = await self.upstream
223
+ .keys(prefix)
224
+ .page(remaining * 2, upstreamCursor);
225
+ for (const key of upstreamKeys) {
226
+ if (seen.has(key) || tombstones.has(key))
227
+ continue;
228
+ seen.add(key);
229
+ keys.push(key);
230
+ if (keys.length >= limit)
231
+ break;
232
+ }
233
+ upstreamCursor = nextCursor;
234
+ if (!upstreamCursor)
235
+ break;
236
+ }
237
+ if (upstreamCursor) {
238
+ return { keys, cursor: `upstream:${upstreamCursor}` };
239
+ }
240
+ }
241
+ }
242
+ // No more data
243
+ return { keys, cursor: undefined };
244
+ },
245
+ };
246
+ }
247
+ /**
248
+ * Fetch multiple keys concurrently with bounded concurrency.
249
+ * Returns a Map of key -> entry for all existing keys.
250
+ * Uses this.get() which handles local/upstream routing and tombstones.
251
+ *
252
+ * @param keys - Array of keys to fetch
253
+ * @param concurrency - Number of concurrent get operations (default: 20)
254
+ */
255
+ async getMany(keys, concurrency = 20) {
256
+ const results = new Map();
257
+ // Process keys in batches
258
+ for (let i = 0; i < keys.length; i += concurrency) {
259
+ const batch = keys.slice(i, i + concurrency);
260
+ const batchResults = await Promise.all(batch.map(async (key) => {
261
+ const result = await this.get(key);
262
+ return { key, result };
263
+ }));
264
+ for (const { key, result } of batchResults) {
265
+ if (result.exists) {
266
+ results.set(key, result);
267
+ }
268
+ }
269
+ }
270
+ return results;
271
+ }
272
+ /**
273
+ * Iterate over key-value entries with concurrent fetching.
274
+ * Yields entries as soon as each fetch completes.
275
+ * Returns merged entries from local and upstream, excluding tombstones.
276
+ *
277
+ * @param prefix - Optional prefix to filter keys
278
+ * @param concurrency - Number of concurrent get operations (default: 20)
279
+ */
280
+ entries(prefix, concurrency = 20) {
281
+ const self = this;
282
+ return {
283
+ async *[Symbol.asyncIterator]() {
284
+ const keyIterator = self.keys(prefix)[Symbol.asyncIterator]();
285
+ // Pool of in-flight fetches
286
+ const inFlight = new Map();
287
+ let keysDone = false;
288
+ // Start initial batch of fetches
289
+ while (inFlight.size < concurrency && !keysDone) {
290
+ const { done, value: key } = await keyIterator.next();
291
+ if (done) {
292
+ keysDone = true;
293
+ break;
294
+ }
295
+ inFlight.set(key, self.get(key).then((result) => ({ key, result })));
296
+ }
297
+ // Process results as they complete, refilling the pool
298
+ while (inFlight.size > 0) {
299
+ const { key, result } = await Promise.race(inFlight.values());
300
+ inFlight.delete(key);
301
+ if (result.exists) {
302
+ yield [key, result];
303
+ }
304
+ if (!keysDone) {
305
+ const { done, value: nextKey } = await keyIterator.next();
306
+ if (done) {
307
+ keysDone = true;
308
+ }
309
+ else {
310
+ inFlight.set(nextKey, self.get(nextKey).then((r) => ({ key: nextKey, result: r })));
311
+ }
312
+ }
313
+ }
314
+ },
315
+ async page(limit, cursor) {
316
+ // Get a page of keys first
317
+ const { keys, cursor: nextCursor } = await self
318
+ .keys(prefix)
319
+ .page(limit, cursor);
320
+ // Fetch all values concurrently
321
+ const entriesMap = await self.getMany(keys, concurrency);
322
+ // Build entries array in key order
323
+ const entries = [];
324
+ for (const key of keys) {
325
+ const entry = entriesMap.get(key);
326
+ if (entry) {
327
+ entries.push([key, entry]);
328
+ }
329
+ }
330
+ return { entries, cursor: nextCursor };
331
+ },
332
+ };
333
+ }
334
+ /**
335
+ * Discover all keys from upstream and add to manifest.
336
+ * Use when you need complete keys() immediately.
337
+ */
338
+ async discoverAllKeys() {
339
+ const state = await this.manifest.getManifest();
340
+ const known = new Set(state.keys.keys());
341
+ let discovered = 0;
342
+ for await (const key of this.upstream.keys()) {
343
+ if (!known.has(key)) {
344
+ await this.manifest.recordAdd(key, "upstream");
345
+ discovered++;
346
+ }
347
+ }
348
+ return discovered;
349
+ }
350
+ /**
351
+ * Delete all local data and manifest (for environment cleanup).
352
+ */
353
+ async destroy() {
354
+ // Delete all local KV data
355
+ for await (const key of this.local.keys()) {
356
+ await this.local.delete(key);
357
+ }
358
+ // Delete manifest
359
+ await this.manifest.destroy();
360
+ }
361
+ /**
362
+ * Get the underlying local KV (for advanced use cases).
363
+ */
364
+ getLocalKV() {
365
+ return this.local;
366
+ }
367
+ /**
368
+ * Get a typed sub-store with CoW behavior.
369
+ * Reads fall back to upstream, writes go to local.
370
+ */
371
+ getStore(subPrefix, indexes) {
372
+ return new TypedKV(this, subPrefix, indexes);
373
+ }
374
+ }
375
+ //# sourceMappingURL=upstream-kv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-kv.js","sourceRoot":"","sources":["../src/upstream-kv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ [Home](../README.md) | [Previous: CLI](cli.md)
2
+
3
+ # API Reference
4
+
5
+ ## `createKV<M>(options)`
6
+
7
+ Creates a KV store with automatic environment detection and copy-on-write fallback.
8
+
9
+ | Option | Type | Default | Description |
10
+ |--------|------|---------|-------------|
11
+ | `prefix` | `string` | `""` | Key prefix for namespacing |
12
+ | `upstream` | `UpstreamConfig \| null` | `{ branch: "main" }` | Fallback config, or `null` to disable |
13
+ | `env` | `string` | `VERCEL_ENV` | Environment name |
14
+ | `branch` | `string` | `VERCEL_GIT_COMMIT_REF` | Branch name |
15
+ | `token` | `string` | `BLOB_READ_WRITE_TOKEN` | Blob access token |
16
+ | `blobStore` | `BlobStore` | `VercelBlobStore` | Custom blob store |
17
+ | `cache` | `CacheLike` | auto-detected | Custom cache implementation |
18
+ | `cacheTtl` | `number` | `3600` | Cache TTL in seconds |
19
+ | `largeValueThreshold` | `number` | `1048576` | Byte threshold for binary format |
20
+ | `tracer` | `Tracer` | `noopTracer` | Custom tracer |
21
+
22
+ Returns `KV2<M>` (on production/main) or `UpstreamKV<M>` (on preview branches).
23
+
24
+ ## `KVLike<M>` Interface
25
+
26
+ All stores (`KV2`, `UpstreamKV`, `TypedKV`) implement this interface:
27
+
28
+ ```typescript
29
+ interface KVLike<M> {
30
+ get<V>(key: string): Promise<KVGetResult<V, M>>;
31
+ set<V>(key: string, value: V | ReadableStream<Uint8Array>, metadata?: M, options?: SetOptions): Promise<KVSetResult>;
32
+ delete(key: string): Promise<void>;
33
+ keys(prefix?: string): KeysIterable;
34
+ entries<V>(prefix?: string): EntriesIterable<V, M>;
35
+ getMany<V>(keys: string[], concurrency?: number): Promise<Map<string, KVEntry<V, M>>>;
36
+ }
37
+ ```
38
+
39
+ ## `KVGetResult<V, M>`
40
+
41
+ A discriminated union — check `exists` before accessing properties:
42
+
43
+ ```typescript
44
+ const result = await kv.get<User>("key");
45
+ if (result.exists) {
46
+ result.metadata; // M — immediately available
47
+ result.version; // string — etag for optimistic locking
48
+ await result.value; // V — lazy-loaded
49
+ await result.stream; // ReadableStream<Uint8Array>
50
+ await result.update(newValue); // conditional write
51
+ }
52
+ ```
53
+
54
+ ### Properties (when `exists: true`)
55
+
56
+ | Property | Type | Description |
57
+ |----------|------|-------------|
58
+ | `exists` | `true` | Entry found |
59
+ | `metadata` | `M` | Entry metadata (immediate) |
60
+ | `version` | `string` | Etag for optimistic locking |
61
+ | `value` | `Promise<V>` | Lazily parsed value |
62
+ | `stream` | `Promise<ReadableStream<Uint8Array>>` | Raw byte stream |
63
+ | `update(value, metadata?)` | `Promise<KVSetResult>` | Conditional update using captured version |
64
+
65
+ ### Properties (when `exists: false`)
66
+
67
+ | Property | Type | Description |
68
+ |----------|------|-------------|
69
+ | `exists` | `false` | Entry not found |
70
+ | `metadata` | `undefined` | |
71
+ | `value` | `undefined` | |
72
+ | `stream` | `undefined` | |
73
+
74
+ ## `SetOptions`
75
+
76
+ ```typescript
77
+ interface SetOptions {
78
+ expectedVersion?: string; // Only succeed if current version matches
79
+ override?: boolean; // Allow overwriting (default: true)
80
+ }
81
+ ```
82
+
83
+ ## `KVSetResult`
84
+
85
+ ```typescript
86
+ interface KVSetResult {
87
+ version: string; // etag of the written blob
88
+ }
89
+ ```
90
+
91
+ ## `KeysIterable`
92
+
93
+ ```typescript
94
+ interface KeysIterable extends AsyncIterable<string> {
95
+ page(limit: number, cursor?: string): Promise<KeysPage>;
96
+ }
97
+
98
+ interface KeysPage {
99
+ keys: string[];
100
+ cursor?: string;
101
+ }
102
+ ```
103
+
104
+ ## `EntriesIterable<V, M>`
105
+
106
+ ```typescript
107
+ interface EntriesIterable<V, M> extends AsyncIterable<[string, KVEntry<V, M>]> {
108
+ page(limit: number, cursor?: string): Promise<EntriesPage<V, M>>;
109
+ }
110
+
111
+ interface EntriesPage<V, M> {
112
+ entries: [string, KVEntry<V, M>][];
113
+ cursor?: string;
114
+ }
115
+ ```
116
+
117
+ ## `TypedKV<V, M, I>`
118
+
119
+ A typed sub-store with automatic key prefixing and optional secondary indexes.
120
+
121
+ ```typescript
122
+ // Create from any KV
123
+ const users = kv.getStore<User>("users/");
124
+
125
+ // With indexes
126
+ const docs = kv.getStore<Doc, undefined, "bySlug">("docs/", {
127
+ bySlug: { key: (doc) => doc.slug, unique: true },
128
+ });
129
+ ```
130
+
131
+ ### Methods
132
+
133
+ All `KVLike<M>` methods plus:
134
+
135
+ | Method | Description |
136
+ |--------|-------------|
137
+ | `getStore<V, M, I>(prefix, indexes?)` | Create a nested sub-store |
138
+ | `reindex(indexName?)` | Rebuild one or all secondary indexes |
139
+ | `get(indexQuery)` | Look up by index (unique indexes) |
140
+ | `keys(indexQuery)` | List keys matching an index query |
141
+ | `entries(indexQuery)` | List entries matching an index query |
142
+
143
+ ## `KV2<M>`
144
+
145
+ The core cached KV implementation. Implements `KVLike<M>` plus:
146
+
147
+ | Method | Description |
148
+ |--------|-------------|
149
+ | `getStore<V, M, I>(prefix, indexes?)` | Create a typed sub-store |
150
+
151
+ ### Constructor Options (`KV2Options`)
152
+
153
+ | Option | Type | Default | Description |
154
+ |--------|------|---------|-------------|
155
+ | `prefix` | `PrefixString` | `""` | Global key prefix |
156
+ | `token` | `string` | `BLOB_READ_WRITE_TOKEN` | Blob access token |
157
+ | `largeValueThreshold` | `number` | `1048576` | Binary format threshold (bytes) |
158
+ | `cacheTtl` | `number` | `3600` | Cache TTL (seconds) |
159
+ | `blobStore` | `BlobStore` | `VercelBlobStore` | Blob storage backend |
160
+ | `cache` | `CacheLike` | auto-detected | Cache backend |
161
+ | `tracer` | `Tracer` | `noopTracer` | Tracing backend |
162
+
163
+ ## `UpstreamKV<M>`
164
+
165
+ Copy-on-write wrapper. Implements `KVLike<M>` with fallback reads to an upstream KV. Created automatically by `createKV()` on preview branches.
166
+
167
+ ## `SchemaKV<P, E>`
168
+
169
+ Schema-aware KV with type-safe key builders and tree loading:
170
+
171
+ | Property/Method | Description |
172
+ |----------------|-------------|
173
+ | `raw` | The underlying `KVLike` store |
174
+ | `key` | Type-safe key builders for all entity types |
175
+ | `tree(type, ...ids)` | Fetch an entity and its descendants as a lazy tree |
176
+ | `get(type, ...ids)` | Get a single entity |
177
+ | `set(type, value, metadata, ...ids)` | Set an entity |
178
+ | `delete(type, ...ids)` | Delete an entity |
179
+
180
+ ## Error Classes
181
+
182
+ ### `KVVersionConflictError`
183
+
184
+ Thrown when a conditional update fails because the entry was modified by another process.
185
+
186
+ ```typescript
187
+ import { KVVersionConflictError } from "@vercel/kv2";
188
+
189
+ try {
190
+ await entry.update(newValue);
191
+ } catch (error) {
192
+ if (error instanceof KVVersionConflictError) {
193
+ // Retry the read-modify-write cycle
194
+ }
195
+ }
196
+ ```
197
+
198
+ ### `KVIndexConflictError`
199
+
200
+ Thrown when a unique index constraint is violated.
201
+
202
+ ```typescript
203
+ import { KVIndexConflictError } from "@vercel/kv2";
204
+
205
+ try {
206
+ await docs.set("doc-2", docData);
207
+ } catch (error) {
208
+ if (error instanceof KVIndexConflictError) {
209
+ console.log(error.indexName); // e.g. "bySlug"
210
+ console.log(error.indexKey); // the conflicting key
211
+ }
212
+ }
213
+ ```
214
+
215
+ ## Environment Variables
216
+
217
+ | Variable | Required | Description |
218
+ |----------|----------|-------------|
219
+ | `BLOB_READ_WRITE_TOKEN` | Yes | Vercel Blob access token |
220
+ | `VERCEL_ENV` | Auto | `production`, `preview`, or `development` |
221
+ | `VERCEL_GIT_COMMIT_REF` | Auto | Current git branch |
222
+ | `PROTECTION_BYPASS` | Integration tests | Protection bypass token |
@@ -0,0 +1,60 @@
1
+ [Home](../README.md) | [Previous: Indexes](indexes.md) | [Next: Streaming](streaming.md)
2
+
3
+ # Caching
4
+
5
+ ## How Caching Works
6
+
7
+ `@vercel/kv2` uses write-through caching with tag-based invalidation:
8
+
9
+ 1. **On write** — the value is written to Vercel Blob and the cache is invalidated by tag
10
+ 2. **On read** — the cache is checked first; on cache miss, the value is loaded from Blob and cached
11
+
12
+ This gives you read-your-writes consistency within a deployment while providing low-latency cached reads across regions.
13
+
14
+ ## Cache Hierarchy
15
+
16
+ The cache implementation is selected automatically based on the environment:
17
+
18
+ | Environment | Cache | Notes |
19
+ |-------------|-------|-------|
20
+ | Vercel Production/Preview | Vercel Runtime Cache | Edge-distributed, tag invalidation |
21
+ | Local Development | MemoryCache | In-memory, persists across HMR |
22
+ | Integration Tests | ProxyCache | Connects to Vercel cache proxy |
23
+
24
+ ## Configuration
25
+
26
+ ### Cache TTL
27
+
28
+ Set the cache TTL in seconds (default: 3600):
29
+
30
+ ```typescript
31
+ import { createKV } from "@vercel/kv2";
32
+
33
+ const kv = createKV({
34
+ prefix: "app/",
35
+ cacheTtl: 600, // 10 minutes
36
+ });
37
+ ```
38
+
39
+ ### Custom Cache
40
+
41
+ Provide a custom `CacheLike` implementation:
42
+
43
+ ```typescript
44
+ import { createKV } from "@vercel/kv2";
45
+
46
+ const kv = createKV({
47
+ prefix: "app/",
48
+ cache: {
49
+ async get(key: string) {
50
+ return null; // custom get
51
+ },
52
+ async set(key: string, value: unknown, options?: { tags?: string[]; ttl?: number }) {
53
+ // custom set
54
+ },
55
+ async expireTag(tag: string) {
56
+ // custom tag invalidation
57
+ },
58
+ },
59
+ });
60
+ ```