@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 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 pattern
11
+ ## Usage patterns
12
12
 
13
- Always use typed stores via `kv.getStore<V>(prefix)` rather than calling `kv.get`/`kv.set` directly. Typed stores enforce value types, scope keys by prefix, and support secondary indexes. Raw KV access should only be used for advanced scenarios like cross-store operations or dynamic key patterns.
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
- // Avoid: raw access loses type safety and prefix scoping
23
- await kv.set("users/alice", someValue);
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 | Topic |
31
- |------|-------|
32
- | `getting-started.md` | Installation, quick start, environment setup |
33
- | `typed-stores.md` | Type-safe sub-stores, key prefixing, nesting |
34
- | `iterating-and-pagination.md` | keys, entries, getMany, cursor pagination |
35
- | `optimistic-locking.md` | Versions, conflict detection, retry patterns |
36
- | `metadata.md` | Typed per-entry metadata |
37
- | `schema-and-trees.md` | defineSchema, tree loading, key builders |
38
- | `indexes.md` | Secondary indexes, unique constraints, reindexing |
39
- | `caching.md` | Cache hierarchy, TTL, custom cache |
40
- | `streaming.md` | Binary format, large values, streaming reads/writes |
41
- | `copy-on-write-branches.md` | Branch isolation, upstream config |
42
- | `testing-and-tracing.md` | Unit testing, tracers, stats |
43
- | `cli.md` | CLI explorer reference |
44
- | `api-reference.md` | Full interface and options documentation |
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}/`;
@@ -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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/kv2",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "KV2 - A type-safe KV store backed by Vercel private blobs with regional caching",
5
5
  "type": "module",
6
6
  "files": [