@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.
- package/README.md +87 -0
- package/SKILL.md +65 -0
- package/dist/blob-format.d.ts +35 -0
- package/dist/blob-format.d.ts.map +1 -0
- package/dist/blob-format.js +91 -0
- package/dist/blob-format.js.map +1 -0
- package/dist/blob-store.d.ts +11 -0
- package/dist/blob-store.d.ts.map +1 -0
- package/dist/blob-store.js +32 -0
- package/dist/blob-store.js.map +1 -0
- package/dist/cache.d.ts +33 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +146 -0
- package/dist/cache.js.map +1 -0
- package/dist/cached-kv.d.ts +63 -0
- package/dist/cached-kv.d.ts.map +1 -0
- package/dist/cached-kv.js +891 -0
- package/dist/cached-kv.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +342 -0
- package/dist/cli.js.map +1 -0
- package/dist/create-kv.d.ts +86 -0
- package/dist/create-kv.d.ts.map +1 -0
- package/dist/create-kv.js +125 -0
- package/dist/create-kv.js.map +1 -0
- package/dist/disk-cache.d.ts.map +1 -0
- package/dist/disk-cache.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/indexed-kv.d.ts +44 -0
- package/dist/indexed-kv.d.ts.map +1 -0
- package/dist/indexed-kv.js +373 -0
- package/dist/indexed-kv.js.map +1 -0
- package/dist/manifest-log.d.ts +57 -0
- package/dist/manifest-log.d.ts.map +1 -0
- package/dist/manifest-log.js +128 -0
- package/dist/manifest-log.js.map +1 -0
- package/dist/memory-cache.d.ts +22 -0
- package/dist/memory-cache.d.ts.map +1 -0
- package/dist/memory-cache.js +90 -0
- package/dist/memory-cache.js.map +1 -0
- package/dist/proxy-cache.d.ts +40 -0
- package/dist/proxy-cache.d.ts.map +1 -0
- package/dist/proxy-cache.js +124 -0
- package/dist/proxy-cache.js.map +1 -0
- package/dist/readme.test.d.ts +9 -0
- package/dist/readme.test.d.ts.map +1 -0
- package/dist/readme.test.js +285 -0
- package/dist/readme.test.js.map +1 -0
- package/dist/schema/define-schema.d.ts +35 -0
- package/dist/schema/define-schema.d.ts.map +1 -0
- package/dist/schema/define-schema.js +70 -0
- package/dist/schema/define-schema.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/key-builders.d.ts +40 -0
- package/dist/schema/key-builders.d.ts.map +1 -0
- package/dist/schema/key-builders.js +124 -0
- package/dist/schema/key-builders.js.map +1 -0
- package/dist/schema/schema-kv.d.ts +48 -0
- package/dist/schema/schema-kv.d.ts.map +1 -0
- package/dist/schema/schema-kv.js +96 -0
- package/dist/schema/schema-kv.js.map +1 -0
- package/dist/schema/tree.d.ts +14 -0
- package/dist/schema/tree.d.ts.map +1 -0
- package/dist/schema/tree.js +135 -0
- package/dist/schema/tree.js.map +1 -0
- package/dist/schema/types.d.ts +135 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +2 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/testing/core-tests.d.ts +30 -0
- package/dist/testing/core-tests.d.ts.map +1 -0
- package/dist/testing/core-tests.js +383 -0
- package/dist/testing/core-tests.js.map +1 -0
- package/dist/testing/create-kv-test-setup.d.ts +21 -0
- package/dist/testing/create-kv-test-setup.d.ts.map +1 -0
- package/dist/testing/create-kv-test-setup.js +25 -0
- package/dist/testing/create-kv-test-setup.js.map +1 -0
- package/dist/testing/debug-manifest.d.ts +2 -0
- package/dist/testing/debug-manifest.d.ts.map +1 -0
- package/dist/testing/debug-manifest.js +14 -0
- package/dist/testing/debug-manifest.js.map +1 -0
- package/dist/testing/fake-blob-store.d.ts +23 -0
- package/dist/testing/fake-blob-store.d.ts.map +1 -0
- package/dist/testing/fake-blob-store.js +158 -0
- package/dist/testing/fake-blob-store.js.map +1 -0
- package/dist/testing/fake-cache.d.ts +54 -0
- package/dist/testing/fake-cache.d.ts.map +1 -0
- package/dist/testing/fake-cache.js +137 -0
- package/dist/testing/fake-cache.js.map +1 -0
- package/dist/testing/index.d.ts +34 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +101 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/manifest-test-setup.d.ts +22 -0
- package/dist/testing/manifest-test-setup.d.ts.map +1 -0
- package/dist/testing/manifest-test-setup.js +43 -0
- package/dist/testing/manifest-test-setup.js.map +1 -0
- package/dist/testing/perf-test.d.ts +13 -0
- package/dist/testing/perf-test.d.ts.map +1 -0
- package/dist/testing/perf-test.js +101 -0
- package/dist/testing/perf-test.js.map +1 -0
- package/dist/testing/run-tests.d.ts +2 -0
- package/dist/testing/run-tests.d.ts.map +1 -0
- package/dist/testing/run-tests.js +141 -0
- package/dist/testing/run-tests.js.map +1 -0
- package/dist/testing/setup.d.ts +2 -0
- package/dist/testing/setup.d.ts.map +1 -0
- package/dist/testing/setup.js +3 -0
- package/dist/testing/setup.js.map +1 -0
- package/dist/testing/test-index.d.ts +28 -0
- package/dist/testing/test-index.d.ts.map +1 -0
- package/dist/testing/test-index.js +35 -0
- package/dist/testing/test-index.js.map +1 -0
- package/dist/testing/test-setup.d.ts +32 -0
- package/dist/testing/test-setup.d.ts.map +1 -0
- package/dist/testing/test-setup.js +72 -0
- package/dist/testing/test-setup.js.map +1 -0
- package/dist/testing/upstream-kv-test-setup.d.ts +30 -0
- package/dist/testing/upstream-kv-test-setup.d.ts.map +1 -0
- package/dist/testing/upstream-kv-test-setup.js +66 -0
- package/dist/testing/upstream-kv-test-setup.js.map +1 -0
- package/dist/testing/vitest-compat.d.ts +92 -0
- package/dist/testing/vitest-compat.d.ts.map +1 -0
- package/dist/testing/vitest-compat.js +601 -0
- package/dist/testing/vitest-compat.js.map +1 -0
- package/dist/tracing.d.ts +71 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +232 -0
- package/dist/tracing.js.map +1 -0
- package/dist/typed-kv.d.ts +120 -0
- package/dist/typed-kv.d.ts.map +1 -0
- package/dist/typed-kv.js +565 -0
- package/dist/typed-kv.js.map +1 -0
- package/dist/typed-upstream-kv.d.ts +17 -0
- package/dist/typed-upstream-kv.d.ts.map +1 -0
- package/dist/typed-upstream-kv.js +38 -0
- package/dist/typed-upstream-kv.js.map +1 -0
- package/dist/types.d.ts +199 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/upstream-kv.d.ts +84 -0
- package/dist/upstream-kv.d.ts.map +1 -0
- package/dist/upstream-kv.js +375 -0
- package/dist/upstream-kv.js.map +1 -0
- package/docs/api-reference.md +222 -0
- package/docs/caching.md +60 -0
- package/docs/cli.md +123 -0
- package/docs/copy-on-write-branches.md +98 -0
- package/docs/getting-started.md +61 -0
- package/docs/indexes.md +122 -0
- package/docs/iterating-and-pagination.md +93 -0
- package/docs/metadata.md +82 -0
- package/docs/optimistic-locking.md +72 -0
- package/docs/schema-and-trees.md +222 -0
- package/docs/streaming.md +61 -0
- package/docs/testing-and-tracing.md +141 -0
- package/docs/typed-stores.md +68 -0
- 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 |
|
package/docs/caching.md
ADDED
|
@@ -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
|
+
```
|