@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,222 @@
1
+ [Home](../README.md) | [Previous: Metadata](metadata.md) | [Next: Indexes](indexes.md)
2
+
3
+ # Schema and Trees
4
+
5
+ For hierarchical data, define a schema and fetch trees with batched concurrent loading.
6
+
7
+ ## Defining a Schema
8
+
9
+ Use `defineSchema()` to declare your entity hierarchy with patterns and value types:
10
+
11
+ ```typescript
12
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
13
+
14
+ interface Board { name: string }
15
+ interface Column { name: string; order: number }
16
+ interface Task { title: string; done: boolean }
17
+
18
+ const schema = defineSchema("kanban/", {
19
+ boards: {
20
+ pattern: "*",
21
+ value: {} as Board,
22
+ children: {
23
+ columns: {
24
+ pattern: "columns/*",
25
+ value: {} as Column,
26
+ children: {
27
+ tasks: { pattern: "tasks/*", value: {} as Task }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ });
33
+ ```
34
+
35
+ Each node in the schema defines:
36
+ - `pattern` — a glob pattern for matching keys at that level
37
+ - `value` — a type witness (use `{} as T`)
38
+ - `children` — optional nested entity definitions
39
+
40
+ ## Creating a Schema KV
41
+
42
+ Combine a schema with a KV store:
43
+
44
+ ```typescript
45
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
46
+
47
+ interface Board { name: string }
48
+ interface Column { name: string; order: number }
49
+ interface Task { title: string; done: boolean }
50
+
51
+ const schema = defineSchema("kanban/", {
52
+ boards: {
53
+ pattern: "*",
54
+ value: {} as Board,
55
+ children: {
56
+ columns: {
57
+ pattern: "columns/*",
58
+ value: {} as Column,
59
+ children: {
60
+ tasks: { pattern: "tasks/*", value: {} as Task }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ });
66
+
67
+ const kv = createKV({ prefix: "kanban/" });
68
+ const kanban = createSchemaKV(schema, kv);
69
+ ```
70
+
71
+ ## Type-Safe Key Builders
72
+
73
+ The schema KV provides key builders that produce correctly-formatted keys:
74
+
75
+ ```typescript
76
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
77
+
78
+ interface Board { name: string }
79
+ interface Column { name: string; order: number }
80
+ interface Task { title: string; done: boolean }
81
+
82
+ const schema = defineSchema("kanban/", {
83
+ boards: {
84
+ pattern: "*",
85
+ value: {} as Board,
86
+ children: {
87
+ columns: {
88
+ pattern: "columns/*",
89
+ value: {} as Column,
90
+ children: {
91
+ tasks: { pattern: "tasks/*", value: {} as Task }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ });
97
+
98
+ const kv = createKV({ prefix: "kanban/" });
99
+ const kanban = createSchemaKV(schema, kv);
100
+
101
+ kanban.key.boards("board-1"); // "board-1"
102
+ kanban.key.columns("board-1", "col-1"); // "board-1/columns/col-1"
103
+ kanban.key.tasks("board-1", "col-1", "task-1"); // "board-1/columns/col-1/tasks/task-1"
104
+ ```
105
+
106
+ ## Fetching Trees
107
+
108
+ Use `tree()` to fetch an entity and all its descendants. Internally, it makes a single `keys()` call to discover descendants, then `getMany()` to fetch values concurrently:
109
+
110
+ ```typescript
111
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
112
+
113
+ interface Board { name: string }
114
+ interface Column { name: string; order: number }
115
+ interface Task { title: string; done: boolean }
116
+
117
+ const schema = defineSchema("kanban/", {
118
+ boards: {
119
+ pattern: "*",
120
+ value: {} as Board,
121
+ children: {
122
+ columns: {
123
+ pattern: "columns/*",
124
+ value: {} as Column,
125
+ children: {
126
+ tasks: { pattern: "tasks/*", value: {} as Task }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ });
132
+
133
+ const kv = createKV({ prefix: "kanban/" });
134
+ const kanban = createSchemaKV(schema, kv);
135
+
136
+ const board = await kanban.tree("boards", "board-1");
137
+ console.log(board.value.name);
138
+ ```
139
+
140
+ ## Lazy Iteration
141
+
142
+ Children on a tree node are async iterables. They support `page()`, `collect()`, `count()`, and `keys()`:
143
+
144
+ ```typescript
145
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
146
+
147
+ interface Board { name: string }
148
+ interface Column { name: string; order: number }
149
+ interface Task { title: string; done: boolean }
150
+
151
+ const schema = defineSchema("kanban/", {
152
+ boards: {
153
+ pattern: "*",
154
+ value: {} as Board,
155
+ children: {
156
+ columns: {
157
+ pattern: "columns/*",
158
+ value: {} as Column,
159
+ children: {
160
+ tasks: { pattern: "tasks/*", value: {} as Task }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ });
166
+
167
+ const kv = createKV({ prefix: "kanban/" });
168
+ const kanban = createSchemaKV(schema, kv);
169
+
170
+ const board = await kanban.tree("boards", "board-1");
171
+
172
+ // Iterate children lazily
173
+ for await (const column of board.columns) {
174
+ console.log(column.value.name);
175
+ for await (const task of column.tasks) {
176
+ console.log(task.value.title);
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## CRUD via Schema
182
+
183
+ The schema KV provides typed `get`, `set`, and `delete` methods:
184
+
185
+ ```typescript
186
+ import { defineSchema, createSchemaKV, createKV } from "@vercel/kv2";
187
+
188
+ interface Board { name: string }
189
+ interface Column { name: string; order: number }
190
+ interface Task { title: string; done: boolean }
191
+
192
+ const schema = defineSchema("kanban/", {
193
+ boards: {
194
+ pattern: "*",
195
+ value: {} as Board,
196
+ children: {
197
+ columns: {
198
+ pattern: "columns/*",
199
+ value: {} as Column,
200
+ children: {
201
+ tasks: { pattern: "tasks/*", value: {} as Task }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ });
207
+
208
+ const kv = createKV({ prefix: "kanban/" });
209
+ const kanban = createSchemaKV(schema, kv);
210
+
211
+ // Set a board
212
+ await kanban.set("boards", { name: "Sprint 1" }, undefined, "board-1");
213
+
214
+ // Get a specific task
215
+ const task = await kanban.get("tasks", "board-1", "col-1", "task-1");
216
+ if (task.exists) {
217
+ console.log((await task.value).title);
218
+ }
219
+
220
+ // Delete a column
221
+ await kanban.delete("columns", "board-1", "col-1");
222
+ ```
@@ -0,0 +1,61 @@
1
+ [Home](../README.md) | [Previous: Caching](caching.md) | [Next: Copy-on-Write Branches](copy-on-write-branches.md)
2
+
3
+ # Streaming
4
+
5
+ ## Why Streaming?
6
+
7
+ By default, `result.value` buffers the entire value into memory before returning it. For a 2 KB JSON object that's fine, but for a 50 MB image or a large dataset it means your serverless function allocates 50 MB of RAM just to pass the bytes through to a response.
8
+
9
+ Streaming avoids this. With `result.stream` you get a `ReadableStream<Uint8Array>` that flows bytes directly from blob storage to the consumer — memory stays flat regardless of value size.
10
+
11
+ Use streaming when you're:
12
+ - Serving files or media to clients without processing them
13
+ - Piping data between storage and another service
14
+ - Working near your function's memory limit
15
+
16
+ ## Streaming Reads
17
+
18
+ Use `result.stream` instead of `result.value` to read without buffering:
19
+
20
+ ```typescript
21
+ const result = await kv.get("large-file");
22
+ if (result.exists) {
23
+ const stream = await result.stream;
24
+ // Pipe directly to the HTTP response — no buffering
25
+ const response = new Response(stream);
26
+ }
27
+ ```
28
+
29
+ ## Streaming Writes
30
+
31
+ Pass a `ReadableStream<Uint8Array>` to `set()` to write without buffering the full payload:
32
+
33
+ ```typescript
34
+ const stream = new ReadableStream<Uint8Array>({
35
+ start(controller) {
36
+ controller.enqueue(new TextEncoder().encode("hello world"));
37
+ controller.close();
38
+ },
39
+ });
40
+
41
+ await kv.set("streamed-value", stream, metadata);
42
+ ```
43
+
44
+ This is useful for proxying uploads or piping data from another source directly into the store.
45
+
46
+ ## Large Value Threshold
47
+
48
+ Values below the threshold (default: 1 MB) are inlined in a single JSON blob — small and fast. Values above it are stored in a binary format that separates the header (metadata) from the payload, enabling streaming without parsing the entire blob.
49
+
50
+ You can tune the threshold:
51
+
52
+ ```typescript
53
+ import { KV2 } from "@vercel/kv2";
54
+
55
+ const kv = new KV2({
56
+ prefix: "files/",
57
+ largeValueThreshold: 512 * 1024, // 512KB
58
+ });
59
+ ```
60
+
61
+ Lower it if you serve many medium-sized values and want to stream them. Raise it (or leave the default) if your values are mostly small JSON and you want the simplicity of a single fetch.
@@ -0,0 +1,141 @@
1
+ [Home](../README.md) | [Previous: Copy-on-Write Branches](copy-on-write-branches.md) | [Next: CLI](cli.md)
2
+
3
+ # Testing and Tracing
4
+
5
+ ## Testing
6
+
7
+ ### FakeBlobStore
8
+
9
+ An in-memory blob store for unit tests. No network calls, no tokens needed:
10
+
11
+ ```typescript
12
+ import { FakeBlobStore } from "@vercel/kv2/testing";
13
+ import { KV2 } from "@vercel/kv2";
14
+
15
+ const blobStore = new FakeBlobStore();
16
+ const kv = new KV2({ prefix: "test/", blobStore });
17
+
18
+ await kv.set("key", { hello: "world" });
19
+ const result = await kv.get("key");
20
+ ```
21
+
22
+ ### FakeCache
23
+
24
+ An in-memory cache with call tracking and programmable error injection:
25
+
26
+ ```typescript
27
+ import { FakeCache } from "@vercel/kv2/testing";
28
+ import { KV2 } from "@vercel/kv2";
29
+
30
+ const cache = new FakeCache();
31
+ const blobStore = new FakeBlobStore();
32
+ const kv = new KV2({ prefix: "test/", blobStore, cache });
33
+ ```
34
+
35
+ ### createTestKV
36
+
37
+ A convenience factory that creates a KV2 backed by `FakeBlobStore` (or real Vercel Blob when `INTEGRATION_TEST=1`):
38
+
39
+ ```typescript
40
+ import { createTestKV } from "@vercel/kv2/testing";
41
+
42
+ const { kv, blobStore, cleanup } = createTestKV();
43
+
44
+ await kv.set("key", { hello: "world" });
45
+
46
+ // Clean up after tests
47
+ await cleanup();
48
+ ```
49
+
50
+ ### Integration Tests
51
+
52
+ Set environment variables and run:
53
+
54
+ ```bash
55
+ INTEGRATION_TEST=1 pnpm test
56
+ ```
57
+
58
+ Required env vars for integration tests:
59
+ - `BLOB_READ_WRITE_TOKEN` — Vercel Blob access token
60
+ - `PROTECTION_BYPASS` — Protection bypass token
61
+
62
+ ## Tracing
63
+
64
+ ### Built-in Tracers
65
+
66
+ **No-op tracer** — zero overhead, the default:
67
+
68
+ ```typescript
69
+ import { noopTracer, KV2 } from "@vercel/kv2";
70
+
71
+ const kv = new KV2({ prefix: "app/", tracer: noopTracer });
72
+ ```
73
+
74
+ **Console tracer** — logs span timing and attributes to console:
75
+
76
+ ```typescript
77
+ import { consoleTracer, KV2 } from "@vercel/kv2";
78
+
79
+ const kv = new KV2({ prefix: "app/", tracer: consoleTracer });
80
+ // [KV] kv.get 2.3ms key=users/alice source=blob
81
+ ```
82
+
83
+ ### OpenTelemetry
84
+
85
+ Adapt an OpenTelemetry tracer with `createOtelTracer()`:
86
+
87
+ ```typescript
88
+ import { createOtelTracer, KV2 } from "@vercel/kv2";
89
+
90
+ // Provide any OTEL-compatible tracer
91
+ const otelTracer: import("@vercel/kv2").OtelTracerLike = {
92
+ startSpan(name, options) {
93
+ return {
94
+ setAttribute() {},
95
+ setStatus() {},
96
+ recordException() {},
97
+ end() {},
98
+ };
99
+ },
100
+ };
101
+
102
+ const tracer = createOtelTracer(otelTracer);
103
+ const kv = new KV2({ prefix: "app/", tracer });
104
+ ```
105
+
106
+ ### Statistics Tracer
107
+
108
+ Collect timing data for performance analysis:
109
+
110
+ ```typescript
111
+ import { createStatsTracer, KV2 } from "@vercel/kv2";
112
+
113
+ const { tracer, printStats, getStats, clear } = createStatsTracer();
114
+ const kv = new KV2({ prefix: "app/", tracer });
115
+
116
+ // ... run operations ...
117
+
118
+ // Print a summary table
119
+ printStats();
120
+
121
+ // Get stats for a specific operation
122
+ const stats = getStats("kv.get");
123
+ if (stats) {
124
+ console.log(`avg: ${stats.avg}ms, p95: ${stats.p95}ms`);
125
+ }
126
+ ```
127
+
128
+ ### Traced Operations
129
+
130
+ KV2 emits spans for these operations:
131
+
132
+ | Span Name | Description |
133
+ |-----------|-------------|
134
+ | `kv.get` | Single key read |
135
+ | `kv.set` | Single key write |
136
+ | `kv.delete` | Single key delete |
137
+ | `kv.keys` | Key listing |
138
+ | `kv.entries` | Entry listing |
139
+ | `kv.getMany` | Batch read |
140
+
141
+ Each span includes attributes like `key`, `source` (cache/blob), and `hit` (cache hit/miss).
@@ -0,0 +1,68 @@
1
+ [Home](../README.md) | [Previous: Iterating and Pagination](iterating-and-pagination.md) | [Next: Optimistic Locking](optimistic-locking.md)
2
+
3
+ # Typed Stores
4
+
5
+ ## Why Typed Stores?
6
+
7
+ A bare KV store is untyped — every `get()` returns `unknown` and every `set()` accepts anything. As your app grows you end up with ad-hoc key conventions ("users/" + id, "posts/" + slug) scattered across your codebase and `as User` casts at every call site.
8
+
9
+ Typed stores solve this by giving you a narrow, strongly-typed view over a slice of your keyspace:
10
+
11
+ - **Type safety** — `get()` returns `Post`, `set()` only accepts `Post`. No casts, no runtime surprises.
12
+ - **Key namespacing** — the store owns its prefix, so you work with short relative keys (`"hello-world"`) instead of full paths (`"posts/hello-world"`).
13
+ - **Composability** — stores nest, so you can model `posts/drafts/` or `users/active/` as their own typed sub-stores without any string concatenation.
14
+
15
+ ## Creating a Typed Store
16
+
17
+ Use `getStore<T>()` to create a type-safe sub-store with automatic key prefixing:
18
+
19
+ ```typescript
20
+ interface Post {
21
+ title: string;
22
+ content: string;
23
+ }
24
+
25
+ const posts = kv.getStore<Post>("posts/");
26
+
27
+ await posts.set("hello-world", { title: "Hello", content: "World" });
28
+
29
+ const result = await posts.get("hello-world");
30
+ if (result.exists) {
31
+ const post = await result.value; // Post
32
+ console.log(post.title);
33
+ }
34
+ ```
35
+
36
+ ## Key Prefixing
37
+
38
+ Keys are relative to the store. The store prepends its prefix automatically, so you never build key strings by hand:
39
+
40
+ ```typescript
41
+ const posts = kv.getStore<Post>("posts/");
42
+
43
+ // You use relative keys
44
+ await posts.set("hello-world", { title: "Hello", content: "World" });
45
+
46
+ for await (const key of posts.keys()) {
47
+ console.log(key); // "hello-world" (not "posts/hello-world")
48
+ }
49
+ ```
50
+
51
+ ## Nested Stores
52
+
53
+ Stores can be nested. Prefixes accumulate, giving you a natural hierarchy without manual string building:
54
+
55
+ ```typescript
56
+ interface Post {
57
+ title: string;
58
+ content: string;
59
+ }
60
+
61
+ const posts = kv.getStore<Post>("posts/");
62
+ const drafts = posts.getStore<Post>("drafts/");
63
+
64
+ await drafts.set("my-draft", { title: "Draft", content: "..." });
65
+ // Actual blob path includes: "posts/drafts/my-draft"
66
+ ```
67
+
68
+ This lets different parts of your application own their own keyspace — a `drafts` module only sees draft keys and can't accidentally read or overwrite published posts.
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@vercel/kv2",
3
+ "version": "0.0.1",
4
+ "description": "KV2 - A type-safe KV store backed by Vercel private blobs with regional caching",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "!dist/**/*.test.*",
9
+ "!dist/chaos",
10
+ "README.md",
11
+ "SKILL.md",
12
+ "docs"
13
+ ],
14
+ "bin": {
15
+ "kv2": "./dist/cli.js"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./testing": {
25
+ "types": "./dist/testing/index.d.ts",
26
+ "import": "./dist/testing/index.js"
27
+ },
28
+ "./testing/test-index": {
29
+ "import": "./dist/testing/test-index.js"
30
+ }
31
+ },
32
+ "keywords": [
33
+ "kv",
34
+ "cache",
35
+ "vercel",
36
+ "blob"
37
+ ],
38
+ "author": "",
39
+ "license": "ISC",
40
+ "dependencies": {
41
+ "@vercel/blob": "2.2.0-f6bb7b4-20260204165325",
42
+ "@vercel/functions": "^3.3.6"
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "^1.9.0",
46
+ "@types/node": "^22.0.0",
47
+ "dotenv": "^17.2.3",
48
+ "knip": "^5.0.0",
49
+ "typescript": "^5.7.0"
50
+ },
51
+ "scripts": {
52
+ "cli": "node dist/cli.js",
53
+ "build": "tsc",
54
+ "typecheck": "tsc --noEmit --project tsconfig.test.json",
55
+ "check": "biome check --write .",
56
+ "lint": "biome lint .",
57
+ "lint:fix": "biome lint --write .",
58
+ "test": "tsc && node dist/testing/run-tests.js",
59
+ "test:integration": "INTEGRATION_TEST=1 sh -c 'tsc && node dist/testing/run-tests.js'",
60
+ "knip": "knip",
61
+ "validate": "pnpm run knip && pnpm run typecheck && pnpm run test"
62
+ }
63
+ }