@vercel/kv2 0.0.10 → 0.0.12
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/SKILL.md +92 -22
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/indexes.md +83 -0
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -8,40 +8,110 @@ Read the README for a quick overview and code examples:
|
|
|
8
8
|
node_modules/@vercel/kv2/README.md
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Usage
|
|
11
|
+
## Usage patterns
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Use typed stores, not raw KV
|
|
14
|
+
|
|
15
|
+
Always use `kv.getStore<V>(prefix)` rather than `kv.get`/`kv.set` directly. Typed stores enforce value types, scope keys by prefix, and support indexes.
|
|
14
16
|
|
|
15
17
|
```typescript
|
|
16
18
|
const kv = createKV({ prefix: "myapp/" });
|
|
17
|
-
|
|
18
|
-
// Good: typed store with enforced value type
|
|
19
19
|
const users = kv.getStore<User>("users/");
|
|
20
20
|
await users.set("alice", { name: "Alice", email: "alice@example.com" });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Reading values
|
|
24
|
+
|
|
25
|
+
`get()` returns a discriminated union — always check `exists` before accessing `value`. Note that `value` is a `Promise` (lazy-parsed), so it needs `await`.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
const result = await users.get("alice");
|
|
29
|
+
if (result.exists) {
|
|
30
|
+
const user = await result.value; // Promise<User>, not User
|
|
31
|
+
console.log(user.name, result.metadata);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Indexes
|
|
36
|
+
|
|
37
|
+
Define indexes in `getStore()` to enable lookups by secondary keys. The third type parameter is a union of index names.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const users = kv.getStore<User, undefined, "byEmail" | "byRole">("users/", {
|
|
41
|
+
byEmail: { key: (u) => u.email, unique: true },
|
|
42
|
+
byRole: { key: (u) => u.role },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Exact match on unique index — returns single result
|
|
46
|
+
const result = await users.get({ byEmail: "alice@example.com" });
|
|
47
|
+
|
|
48
|
+
// Exact match on non-unique index — iterate multiple results
|
|
49
|
+
for await (const key of users.keys({ byRole: "admin" })) { ... }
|
|
50
|
+
|
|
51
|
+
// Prefix scan — sorted iteration over a range
|
|
52
|
+
for await (const [key, entry] of users.entries({ byRole: { prefix: "admin" } })) { ... }
|
|
21
53
|
|
|
22
|
-
//
|
|
23
|
-
await
|
|
54
|
+
// Empty prefix — all entries sorted by index key
|
|
55
|
+
for await (const key of users.keys({ byRole: { prefix: "" } })) { ... }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Composite index keys enable "group by X, sort by Y" patterns. Use `/` separators between fields and invert values for descending order (see `docs/indexes.md` for details).
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const MAX_TS = 8_640_000_000_000;
|
|
62
|
+
const sessions = kv.getStore<Session, undefined, "byUserNewest">("sessions/", {
|
|
63
|
+
byUserNewest: {
|
|
64
|
+
key: (s) => `${s.userId}/${String(MAX_TS - Date.parse(s.createdAt)).padStart(14, "0")}`,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
// user-42's most recent sessions first
|
|
68
|
+
sessions.entries({ byUserNewest: { prefix: "user-42/" } })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Iteration and pagination
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Async iteration
|
|
75
|
+
for await (const [key, entry] of users.entries()) {
|
|
76
|
+
console.log(key, await entry.value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Cursor-based pagination
|
|
80
|
+
const page1 = await users.keys().page(20);
|
|
81
|
+
const page2 = await users.keys().page(20, page1.cursor);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Safe updates with optimistic locking
|
|
85
|
+
|
|
86
|
+
Use `entry.update()` to do read-modify-write with automatic version checking. Throws `KVVersionConflictError` on conflict.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const result = await users.get("alice");
|
|
90
|
+
if (result.exists) {
|
|
91
|
+
const user = await result.value;
|
|
92
|
+
await result.update({ ...user, role: "admin" });
|
|
93
|
+
}
|
|
24
94
|
```
|
|
25
95
|
|
|
26
96
|
## Docs
|
|
27
97
|
|
|
28
98
|
Detailed documentation is available in the package's `docs/` directory. Read files as needed from `node_modules/@vercel/kv2/docs/`:
|
|
29
99
|
|
|
30
|
-
| File |
|
|
31
|
-
|
|
32
|
-
| `getting-started.md` | Installation,
|
|
33
|
-
| `typed-stores.md` |
|
|
34
|
-
| `iterating-and-pagination.md` | keys
|
|
35
|
-
| `optimistic-locking.md` | Versions,
|
|
36
|
-
| `metadata.md` | Typed per-entry metadata |
|
|
37
|
-
| `schema-and-trees.md` | defineSchema
|
|
38
|
-
| `indexes.md` |
|
|
39
|
-
| `caching.md` |
|
|
40
|
-
| `streaming.md` |
|
|
41
|
-
| `copy-on-write-branches.md` |
|
|
42
|
-
| `testing-and-tracing.md` |
|
|
43
|
-
| `cli.md` | CLI
|
|
44
|
-
| `api-reference.md` | Full
|
|
100
|
+
| File | Topics |
|
|
101
|
+
|------|--------|
|
|
102
|
+
| `getting-started.md` | Installation, prerequisites, environment variables (`BLOB_READ_WRITE_TOKEN`), how edge caching and CoW branching work |
|
|
103
|
+
| `typed-stores.md` | `getStore<V>()`, type-safe values, automatic key prefixing, nested/hierarchical stores |
|
|
104
|
+
| `iterating-and-pagination.md` | `keys()`, `entries()`, `getMany()`, prefix filtering, cursor-based `page()` pagination, Next.js API route example |
|
|
105
|
+
| `optimistic-locking.md` | Versions/etags, `entry.update()`, read-modify-write retry loops, `expectedVersion`, create-only writes (`override: false`) |
|
|
106
|
+
| `metadata.md` | Typed per-entry metadata via `createKV<M>()`, metadata inheritance in sub-stores, filtering by metadata without loading values |
|
|
107
|
+
| `schema-and-trees.md` | `defineSchema()`, `createSchemaKV()`, hierarchical entity models, type-safe key builders, `tree()` loading with lazy children |
|
|
108
|
+
| `indexes.md` | Defining indexes, unique constraints (`KVIndexConflictError`), multi-value indexes (arrays), prefix queries for sorted iteration, composite index keys (group + sort), key design (padding, DESC via inverted keys, separators), `reindex()`, orphan self-healing |
|
|
109
|
+
| `caching.md` | Write-through caching, tag-based invalidation, cache hierarchy (Runtime Cache, MemoryCache, ProxyCache), TTL configuration |
|
|
110
|
+
| `streaming.md` | `result.stream` for large value reads, `ReadableStream` writes, binary format, `largeValueThreshold` (default 1MB) |
|
|
111
|
+
| `copy-on-write-branches.md` | Preview branch data isolation, virtual forking with upstream fallback, tombstones, automatic environment detection, ManifestLog, branch name encoding |
|
|
112
|
+
| `testing-and-tracing.md` | `FakeBlobStore` for unit tests, integration tests with `INTEGRATION_TEST=1`, tracers: no-op, console, OpenTelemetry (`createOtelTracer`), stats (`createStatsTracer`) |
|
|
113
|
+
| `cli.md` | `kv2` CLI: `keys`, `get`, `set`, `del` commands, `--prefix`/`--env`/`--branch` options, interactive REPL, stdout/stderr contract for piping |
|
|
114
|
+
| `api-reference.md` | Full API: `createKV()`, `KVLike`, `KVGetResult`, `SetOptions`, `TypedKV`, `KV2`, `UpstreamKV`, `SchemaKV`, `KeysIterable`, `EntriesIterable`, error classes, environment variables |
|
|
45
115
|
|
|
46
116
|
## Types
|
|
47
117
|
|
|
@@ -57,7 +127,7 @@ Key type definition files in `node_modules/@vercel/kv2/dist/`:
|
|
|
57
127
|
|------|-------|
|
|
58
128
|
| `types.d.ts` | `KVLike`, `KVEntry`, `KVGetResult`, `KVSetResult`, `KeysIterable`, `KeysPage`, `EntriesIterable`, `SetOptions`, `BlobStore`, `Tracer` |
|
|
59
129
|
| `cached-kv.d.ts` | `KV2` class |
|
|
60
|
-
| `typed-kv.d.ts` | `TypedKV` class, `IndexDef`, `IndexQuery` |
|
|
130
|
+
| `typed-kv.d.ts` | `TypedKV` class, `IndexDef`, `IndexQuery`, `IndexQueryValue` |
|
|
61
131
|
| `create-kv.d.ts` | `createKV()`, `CreateKVOptions`, `UpstreamConfig` |
|
|
62
132
|
| `upstream-kv.d.ts` | `UpstreamKV` class |
|
|
63
133
|
| `manifest-log.d.ts` | `ManifestLog`, `KeyMeta` |
|
package/dist/types.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface KVLike<M> {
|
|
|
96
96
|
keys(prefix?: string): KeysIterable;
|
|
97
97
|
entries<V = unknown>(prefix?: string): EntriesIterable<V, M>;
|
|
98
98
|
getMany<V = unknown>(keys: string[], concurrency?: number): Promise<Map<string, KVEntry<V, M>>>;
|
|
99
|
+
getStore<V, SubM = M, I extends string = never>(subPrefix: string, indexes?: Record<I, import("./typed-kv.js").IndexDef<V>>): import("./typed-kv.js").TypedKV<V, SubM, I>;
|
|
99
100
|
}
|
|
100
101
|
/** A string that ends with a forward slash */
|
|
101
102
|
export type PrefixString = `${string}/`;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/docs/indexes.md
CHANGED
|
@@ -169,6 +169,89 @@ sessions.entries({ byOwnerDate: { prefix: "user-42/2024-01" } })
|
|
|
169
169
|
|
|
170
170
|
**Important:** Include a separator (like `/`) after each field to prevent partial matches — without it, `"user-4"` would also match `"user-42"`.
|
|
171
171
|
|
|
172
|
+
## Key Design
|
|
173
|
+
|
|
174
|
+
Index keys are scanned in **lexicographic (ascending) order**. This section covers patterns for getting the most out of your index key design.
|
|
175
|
+
|
|
176
|
+
### Pad numbers with leading zeros
|
|
177
|
+
|
|
178
|
+
Lexicographic sort treats numbers as strings, so `"9"` sorts after `"10"`. Pad numeric values to a fixed width:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Bad: "9" > "10" lexicographically
|
|
182
|
+
key: (o) => `${o.priority}`
|
|
183
|
+
|
|
184
|
+
// Good: "009" < "010"
|
|
185
|
+
key: (o) => String(o.priority).padStart(3, "0")
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Use ISO dates for chronological order
|
|
189
|
+
|
|
190
|
+
ISO 8601 strings (`"2024-01-15T10:30:00Z"`) sort correctly as-is — no padding needed.
|
|
191
|
+
|
|
192
|
+
### Descending order
|
|
193
|
+
|
|
194
|
+
There is no built-in DESC option. Instead, invert the sort key so that lexicographic ascending order produces the desired descending result.
|
|
195
|
+
|
|
196
|
+
**For timestamps** — subtract from a far-future epoch and pad to fixed width:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const MAX_TS = 8_640_000_000_000; // year 2243
|
|
200
|
+
|
|
201
|
+
const messages = kv.getStore<Message, undefined, "byNewest">("messages/", {
|
|
202
|
+
byNewest: {
|
|
203
|
+
key: (m) => String(MAX_TS - new Date(m.createdAt).getTime()).padStart(14, "0"),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Most recent messages first
|
|
208
|
+
for await (const [key, entry] of messages.entries({ byNewest: { prefix: "" } })) {
|
|
209
|
+
console.log((await entry.value).createdAt);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**For scores/priorities** — subtract from a known max:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const MAX_SCORE = 1_000_000;
|
|
217
|
+
|
|
218
|
+
const leaderboard = kv.getStore<Player, undefined, "byTopScore">("players/", {
|
|
219
|
+
byTopScore: {
|
|
220
|
+
key: (p) => String(MAX_SCORE - p.score).padStart(7, "0"),
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Combined with grouping** — composite key with an inverted sort field:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const MAX_TS = 8_640_000_000_000;
|
|
229
|
+
|
|
230
|
+
const sessions = kv.getStore<Session, undefined, "byOwnerNewest">("sessions/", {
|
|
231
|
+
byOwnerNewest: {
|
|
232
|
+
key: (s) => {
|
|
233
|
+
const inv = String(MAX_TS - new Date(s.createdAt).getTime()).padStart(14, "0");
|
|
234
|
+
return `${s.ownerId}/${inv}`;
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// user-42's most recent sessions first
|
|
240
|
+
sessions.entries({ byOwnerNewest: { prefix: "user-42/" } })
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Use separators to prevent prefix collisions
|
|
244
|
+
|
|
245
|
+
Always include a delimiter (typically `/`) between fields in composite keys:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// Bad: prefix "user-4" matches "user-4", "user-42", "user-400"
|
|
249
|
+
key: (s) => `${s.ownerId}${s.createdAt}`
|
|
250
|
+
|
|
251
|
+
// Good: prefix "user-4/" only matches user-4
|
|
252
|
+
key: (s) => `${s.ownerId}/${s.createdAt}`
|
|
253
|
+
```
|
|
254
|
+
|
|
172
255
|
## Index Maintenance
|
|
173
256
|
|
|
174
257
|
Indexes are maintained automatically on `set()` and `delete()`. When you update a value, old index entries are removed and new ones are created.
|