@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
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @vercel/kv2
2
+
3
+ A type-safe key-value store backed by Vercel Blob with edge caching and copy-on-write branch isolation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @vercel/kv2
9
+ # or
10
+ pnpm add @vercel/kv2
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createKV } from "@vercel/kv2";
17
+
18
+ const kv = createKV({ prefix: "myapp/" });
19
+
20
+ interface User {
21
+ name: string;
22
+ email: string;
23
+ }
24
+
25
+ const users = kv.getStore<User>("users/");
26
+
27
+ await users.set("alice", { name: "Alice", email: "alice@example.com" });
28
+
29
+ const result = await users.get("alice");
30
+ if (result.exists) {
31
+ console.log((await result.value).name); // "Alice"
32
+ }
33
+
34
+ // Delete, iterate keys, entries, getMany — see docs
35
+ await users.delete("alice");
36
+ ```
37
+
38
+ ## Features
39
+
40
+ | Feature | Description | Docs |
41
+ |---------|-------------|------|
42
+ | **Typed Stores** | Type-safe sub-stores with automatic key prefixing | [Typed Stores](docs/typed-stores.md) |
43
+ | **Iteration** | `entries()` and `getMany()` with bounded concurrency | [Iterating and Pagination](docs/iterating-and-pagination.md) |
44
+ | **Pagination** | Cursor-based pagination for HTTP APIs | [Iterating and Pagination](docs/iterating-and-pagination.md) |
45
+ | **Optimistic Locking** | Version-based conflict detection and retry | [Optimistic Locking](docs/optimistic-locking.md) |
46
+ | **Metadata** | Typed per-entry metadata, available without loading values | [Metadata](docs/metadata.md) |
47
+ | **Schema & Trees** | Hierarchical data with batched tree loading | [Schema and Trees](docs/schema-and-trees.md) |
48
+ | **Indexes** | Secondary indexes with unique constraints | [Indexes](docs/indexes.md) |
49
+ | **Edge Caching** | Write-through cache with tag invalidation | [Caching](docs/caching.md) |
50
+ | **Streaming** | Large values streamed without buffering | [Streaming](docs/streaming.md) |
51
+ | **Copy-on-Write** | Preview branches inherit from production | [Copy-on-Write Branches](docs/copy-on-write-branches.md) |
52
+ | **CLI Explorer** | Interactive KV store explorer for debugging | [CLI](docs/cli.md) |
53
+
54
+ ## Documentation
55
+
56
+ 1. [Getting Started](docs/getting-started.md) — installation, quick start, environment setup
57
+ 2. [Iterating and Pagination](docs/iterating-and-pagination.md) — keys, entries, getMany, cursor pagination
58
+ 3. [Typed Stores](docs/typed-stores.md) — getStore, key prefixing, nested stores
59
+ 4. [Optimistic Locking](docs/optimistic-locking.md) — versions, conflict detection, retry patterns
60
+ 5. [Metadata](docs/metadata.md) — typed metadata, filtering without loading values
61
+ 6. [Schema and Trees](docs/schema-and-trees.md) — defineSchema, tree loading, key builders
62
+ 7. [Indexes](docs/indexes.md) — secondary indexes, unique constraints, reindexing
63
+ 8. [Caching](docs/caching.md) — cache hierarchy, TTL, custom cache
64
+ 9. [Streaming](docs/streaming.md) — binary format, large values, streaming reads/writes
65
+ 10. [Copy-on-Write Branches](docs/copy-on-write-branches.md) — branch isolation, upstream config
66
+ 11. [Testing and Tracing](docs/testing-and-tracing.md) — FakeBlobStore, tracers, stats
67
+ 12. [CLI](docs/cli.md) — interactive KV store explorer
68
+ 13. [API Reference](docs/api-reference.md) — full interface and options documentation
69
+
70
+ ## Environment Variables
71
+
72
+ ```
73
+ BLOB_READ_WRITE_TOKEN=vercel_blob_...
74
+ ```
75
+
76
+ See [Getting Started](docs/getting-started.md) for full environment setup.
77
+
78
+ ## Testing
79
+
80
+ ```bash
81
+ pnpm test # Unit tests (fake blob store)
82
+ pnpm test:integration # Integration tests (real Vercel Blob)
83
+ ```
84
+
85
+ ## License
86
+
87
+ ISC
package/SKILL.md ADDED
@@ -0,0 +1,65 @@
1
+ # Using @vercel/kv2
2
+
3
+ ## Getting started
4
+
5
+ Read the README for a quick overview and code examples:
6
+
7
+ ```
8
+ node_modules/@vercel/kv2/README.md
9
+ ```
10
+
11
+ ## Docs
12
+
13
+ Detailed documentation is available in the package's `docs/` directory. Read files as needed from `node_modules/@vercel/kv2/docs/`:
14
+
15
+ | File | Topic |
16
+ |------|-------|
17
+ | `getting-started.md` | Installation, quick start, environment setup |
18
+ | `typed-stores.md` | Type-safe sub-stores, key prefixing, nesting |
19
+ | `iterating-and-pagination.md` | keys, entries, getMany, cursor pagination |
20
+ | `optimistic-locking.md` | Versions, conflict detection, retry patterns |
21
+ | `metadata.md` | Typed per-entry metadata |
22
+ | `schema-and-trees.md` | defineSchema, tree loading, key builders |
23
+ | `indexes.md` | Secondary indexes, unique constraints, reindexing |
24
+ | `caching.md` | Cache hierarchy, TTL, custom cache |
25
+ | `streaming.md` | Binary format, large values, streaming reads/writes |
26
+ | `copy-on-write-branches.md` | Branch isolation, upstream config |
27
+ | `testing-and-tracing.md` | FakeBlobStore, tracers, stats |
28
+ | `cli.md` | CLI explorer reference |
29
+ | `api-reference.md` | Full interface and options documentation |
30
+
31
+ ## Types
32
+
33
+ All public types and interfaces are exported from the main entry point. To look up type signatures, read:
34
+
35
+ ```
36
+ node_modules/@vercel/kv2/dist/index.d.ts
37
+ ```
38
+
39
+ Key type definition files in `node_modules/@vercel/kv2/dist/`:
40
+
41
+ | File | Types |
42
+ |------|-------|
43
+ | `types.d.ts` | `KVLike`, `KVEntry`, `KVGetResult`, `KVSetResult`, `KeysIterable`, `KeysPage`, `EntriesIterable`, `SetOptions`, `BlobStore`, `Tracer` |
44
+ | `cached-kv.d.ts` | `KV2` class |
45
+ | `typed-kv.d.ts` | `TypedKV` class, `IndexDef`, `IndexQuery` |
46
+ | `create-kv.d.ts` | `createKV()`, `CreateKVOptions`, `UpstreamConfig` |
47
+ | `upstream-kv.d.ts` | `UpstreamKV` class |
48
+ | `manifest-log.d.ts` | `ManifestLog`, `KeyMeta` |
49
+ | `tracing.d.ts` | Tracer factories, `TimingStats` |
50
+ | `schema/index.d.ts` | `defineSchema`, `createSchemaKV`, `SchemaKV`, `TreeNode` |
51
+
52
+ ## Verifying KV state
53
+
54
+ The package ships a `kv2` CLI for inspecting and debugging the KV store:
55
+
56
+ ```bash
57
+ npx kv2 keys # List keys (first 100)
58
+ npx kv2 --all keys # List all keys
59
+ npx kv2 get <key> # Print a value as JSON
60
+ npx kv2 --verbose get <key> # Also show version and metadata
61
+ npx kv2 --allow-writes set <key> <json> # Write a value
62
+ npx kv2 --allow-writes del <key> # Delete a key
63
+ ```
64
+
65
+ Use `--prefix <prefix>` if your app uses `createKV({ prefix: "myapp/" })`.
@@ -0,0 +1,35 @@
1
+ import type { StoredEntry } from "./types.js";
2
+ export interface ParsedBlob<M> {
3
+ header: StoredEntry<M>;
4
+ payload: Buffer | null;
5
+ }
6
+ /**
7
+ * Detects whether a blob uses pure JSON format or binary format.
8
+ * Pure JSON starts with '{' (0x7B), binary format starts with uint32 length.
9
+ */
10
+ export declare function isPureJsonFormat(buffer: Buffer): boolean;
11
+ /**
12
+ * Parses a blob buffer into header and optional payload.
13
+ * Automatically detects format based on first byte.
14
+ *
15
+ * @throws {SyntaxError} If JSON parsing fails
16
+ * @throws {RangeError} If header length exceeds buffer size
17
+ */
18
+ export declare function parseBlob<M>(buffer: Buffer): ParsedBlob<M>;
19
+ /**
20
+ * Creates a blob buffer from header and optional payload.
21
+ * Uses pure JSON format when no payload, binary format otherwise.
22
+ *
23
+ * @throws {Error} If header exceeds MAX_HEADER_SIZE in binary format
24
+ */
25
+ export declare function createBlob<M>(header: StoredEntry<M>, payload?: Buffer): Buffer;
26
+ /**
27
+ * Extracts just the header from a blob without fully parsing the payload.
28
+ * Useful for metadata-only reads.
29
+ */
30
+ export declare function parseHeader<M>(buffer: Buffer): StoredEntry<M>;
31
+ /**
32
+ * Checks if a blob has a payload (large value stored after header).
33
+ */
34
+ export declare function hasPayload<M>(header: StoredEntry<M>): boolean;
35
+ //# sourceMappingURL=blob-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-format.d.ts","sourceRoot":"","sources":["../src/blob-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAS9C,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAuC1D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC3B,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,OAAO,CAAC,EAAE,MAAM,GACd,MAAM,CA8BR;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAE7D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,91 @@
1
+ const HEADER_LENGTH_BYTES = 4;
2
+ // Max header size ensures uint32 BE first byte < 0x7B ('{'), preventing format ambiguity
3
+ // 0x7B000000 = 2,063,597,568 bytes; we use 100MB as a practical limit
4
+ const MAX_HEADER_SIZE = 100 * 1024 * 1024;
5
+ // ASCII code for '{'
6
+ const OPEN_BRACE = 0x7b;
7
+ /**
8
+ * Detects whether a blob uses pure JSON format or binary format.
9
+ * Pure JSON starts with '{' (0x7B), binary format starts with uint32 length.
10
+ */
11
+ export function isPureJsonFormat(buffer) {
12
+ return buffer.length > 0 && buffer[0] === OPEN_BRACE;
13
+ }
14
+ /**
15
+ * Parses a blob buffer into header and optional payload.
16
+ * Automatically detects format based on first byte.
17
+ *
18
+ * @throws {SyntaxError} If JSON parsing fails
19
+ * @throws {RangeError} If header length exceeds buffer size
20
+ */
21
+ export function parseBlob(buffer) {
22
+ if (buffer.length === 0) {
23
+ throw new Error("Cannot parse empty buffer");
24
+ }
25
+ if (isPureJsonFormat(buffer)) {
26
+ // Pure JSON format: entire buffer is the header
27
+ const header = JSON.parse(buffer.toString("utf-8"));
28
+ return { header, payload: null };
29
+ }
30
+ // Binary format: length-prefixed header + optional payload
31
+ if (buffer.length < HEADER_LENGTH_BYTES) {
32
+ throw new Error(`Buffer too small for binary format: ${buffer.length} bytes`);
33
+ }
34
+ const headerLength = buffer.readUInt32BE(0);
35
+ const headerEnd = HEADER_LENGTH_BYTES + headerLength;
36
+ if (headerEnd > buffer.length) {
37
+ throw new RangeError(`Header length ${headerLength} exceeds buffer size ${buffer.length}`);
38
+ }
39
+ // Parse header JSON
40
+ const headerJson = buffer
41
+ .subarray(HEADER_LENGTH_BYTES, headerEnd)
42
+ .toString("utf-8");
43
+ const header = JSON.parse(headerJson);
44
+ // Extract payload if present (for "raw-*" encodings)
45
+ const hasPayload = header.encoding === "raw-json" || header.encoding === "raw-binary";
46
+ const payload = hasPayload ? buffer.subarray(headerEnd) : null;
47
+ return { header, payload };
48
+ }
49
+ /**
50
+ * Creates a blob buffer from header and optional payload.
51
+ * Uses pure JSON format when no payload, binary format otherwise.
52
+ *
53
+ * @throws {Error} If header exceeds MAX_HEADER_SIZE in binary format
54
+ */
55
+ export function createBlob(header, payload) {
56
+ const headerJson = JSON.stringify(header);
57
+ const headerBuffer = Buffer.from(headerJson, "utf-8");
58
+ // Use binary format if payload exists OR if encoding indicates payload follows
59
+ const hasPayload = payload ||
60
+ header.encoding === "raw-json" ||
61
+ header.encoding === "raw-binary";
62
+ if (hasPayload) {
63
+ // Safety: header size must be < MAX_HEADER_SIZE to ensure first byte < 0x7B
64
+ if (headerBuffer.length >= MAX_HEADER_SIZE) {
65
+ throw new Error(`Header too large: ${headerBuffer.length} bytes (max ${MAX_HEADER_SIZE})`);
66
+ }
67
+ // Binary format: length-prefixed header + payload
68
+ const lengthBuffer = Buffer.alloc(HEADER_LENGTH_BYTES);
69
+ lengthBuffer.writeUInt32BE(headerBuffer.length, 0);
70
+ if (payload) {
71
+ return Buffer.concat([lengthBuffer, headerBuffer, payload]);
72
+ }
73
+ return Buffer.concat([lengthBuffer, headerBuffer]);
74
+ }
75
+ // Pure JSON format: just the header (no length prefix)
76
+ return headerBuffer;
77
+ }
78
+ /**
79
+ * Extracts just the header from a blob without fully parsing the payload.
80
+ * Useful for metadata-only reads.
81
+ */
82
+ export function parseHeader(buffer) {
83
+ return parseBlob(buffer).header;
84
+ }
85
+ /**
86
+ * Checks if a blob has a payload (large value stored after header).
87
+ */
88
+ export function hasPayload(header) {
89
+ return header.encoding === "raw-json" || header.encoding === "raw-binary";
90
+ }
91
+ //# sourceMappingURL=blob-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-format.js","sourceRoot":"","sources":["../src/blob-format.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,yFAAyF;AACzF,sEAAsE;AACtE,MAAM,eAAe,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1C,qBAAqB;AACrB,MAAM,UAAU,GAAG,IAAI,CAAC;AAOxB;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC9C,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC;AACtD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAI,MAAc;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,gDAAgD;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAmB,CAAC;QACtE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IAED,2DAA2D;IAC3D,IAAI,MAAM,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACd,uCAAuC,MAAM,CAAC,MAAM,QAAQ,CAC5D,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,mBAAmB,GAAG,YAAY,CAAC;IAErD,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,IAAI,UAAU,CACnB,iBAAiB,YAAY,wBAAwB,MAAM,CAAC,MAAM,EAAE,CACpE,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,UAAU,GAAG,MAAM;SACvB,QAAQ,CAAC,mBAAmB,EAAE,SAAS,CAAC;SACxC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAmB,CAAC;IAExD,qDAAqD;IACrD,MAAM,UAAU,GACf,MAAM,CAAC,QAAQ,KAAK,UAAU,IAAI,MAAM,CAAC,QAAQ,KAAK,YAAY,CAAC;IACpE,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACzB,MAAsB,EACtB,OAAgB;IAEhB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEtD,+EAA+E;IAC/E,MAAM,UAAU,GACf,OAAO;QACP,MAAM,CAAC,QAAQ,KAAK,UAAU;QAC9B,MAAM,CAAC,QAAQ,KAAK,YAAY,CAAC;IAElC,IAAI,UAAU,EAAE,CAAC;QAChB,4EAA4E;QAC5E,IAAI,YAAY,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CACd,qBAAqB,YAAY,CAAC,MAAM,eAAe,eAAe,GAAG,CACzE,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvD,YAAY,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEnD,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,uDAAuD;IACvD,OAAO,YAAY,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAI,MAAc;IAC5C,OAAO,SAAS,CAAI,MAAM,CAAC,CAAC,MAAM,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAI,MAAsB;IACnD,OAAO,MAAM,CAAC,QAAQ,KAAK,UAAU,IAAI,MAAM,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type GetBlobResult, type GetCommandOptions, type ListBlobResult, type ListCommandOptions, type PutBlobResult, type PutCommandOptions } from "@vercel/blob";
2
+ import type { BlobStore, PutBody } from "./types.js";
3
+ export declare class VercelBlobStore implements BlobStore {
4
+ private token?;
5
+ constructor(token?: string);
6
+ get(pathname: string, options: GetCommandOptions): Promise<GetBlobResult | null>;
7
+ put(pathname: string, body: PutBody, options: PutCommandOptions): Promise<PutBlobResult>;
8
+ del(urlOrPathname: string | string[]): Promise<void>;
9
+ list(options?: ListCommandOptions): Promise<ListBlobResult>;
10
+ }
11
+ //# sourceMappingURL=blob-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-store.d.ts","sourceRoot":"","sources":["../src/blob-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAKtB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErD,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,CAAC,CAAS;gBAEX,KAAK,CAAC,EAAE,MAAM;IAIpB,GAAG,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAQ1B,GAAG,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,CAAC;IAOnB,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC;CAMjE"}
@@ -0,0 +1,32 @@
1
+ import { del, get, list, put, } from "@vercel/blob";
2
+ export class VercelBlobStore {
3
+ token;
4
+ constructor(token) {
5
+ this.token = token;
6
+ }
7
+ async get(pathname, options) {
8
+ return get(pathname, {
9
+ useCache: false,
10
+ ...options,
11
+ token: this.token,
12
+ });
13
+ }
14
+ async put(pathname, body, options) {
15
+ return put(pathname, body, {
16
+ ...options,
17
+ token: this.token,
18
+ });
19
+ }
20
+ async del(urlOrPathname) {
21
+ return del(urlOrPathname, {
22
+ token: this.token,
23
+ });
24
+ }
25
+ async list(options) {
26
+ return list({
27
+ ...options,
28
+ token: this.token,
29
+ });
30
+ }
31
+ }
32
+ //# sourceMappingURL=blob-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-store.js","sourceRoot":"","sources":["../src/blob-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,GAAG,EACH,GAAG,EACH,IAAI,EACJ,GAAG,GACH,MAAM,cAAc,CAAC;AAGtB,MAAM,OAAO,eAAe;IACnB,KAAK,CAAU;IAEvB,YAAY,KAAc;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,GAAG,CACR,QAAgB,EAChB,OAA0B;QAE1B,OAAO,GAAG,CAAC,QAAQ,EAAE;YACpB,QAAQ,EAAE,KAAK;YACf,GAAG,OAAO;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;SACjB,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CACR,QAAgB,EAChB,IAAa,EACb,OAA0B;QAE1B,OAAO,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE;YAC1B,GAAG,OAAO;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;SACjB,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,aAAgC;QACzC,OAAO,GAAG,CAAC,aAAa,EAAE;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;SACjB,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA4B;QACtC,OAAO,IAAI,CAAC;YACX,GAAG,OAAO;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;SACjB,CAAC,CAAC;IACJ,CAAC;CACD"}
@@ -0,0 +1,33 @@
1
+ import type { CacheLike, CachedEntry } from "./types.js";
2
+ /**
3
+ * Encode cache keys/tags to be safe for HTTP headers (used by Vercel cache tags).
4
+ * HTTP headers only allow ASCII printable characters (0x20-0x7E), excluding certain chars.
5
+ * We use percent-encoding for non-ASCII and problematic characters, keeping ASCII readable.
6
+ * @internal Exported for testing
7
+ */
8
+ export declare function encodeCacheKey(path: string): string;
9
+ /** Error handler function type for KVCache error logging */
10
+ export type ErrorHandler = (message: string, error: unknown) => void;
11
+ export interface KVCacheOptions {
12
+ ttl: number;
13
+ /** Optional cache implementation for testing */
14
+ cache?: CacheLike;
15
+ /** Optional error handler for testing (defaults to console.error) */
16
+ onError?: ErrorHandler;
17
+ }
18
+ export declare class KVCache {
19
+ private ttl;
20
+ private useProxy;
21
+ private useMemory;
22
+ private injectedCache;
23
+ private errorHandler;
24
+ constructor(options: KVCacheOptions | number);
25
+ private getCache;
26
+ private getCacheKey;
27
+ private getCacheTag;
28
+ private sleep;
29
+ get<M>(path: string): Promise<CachedEntry<M> | null>;
30
+ set<M>(path: string, entry: CachedEntry<M>): Promise<void>;
31
+ invalidate(path: string): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOzD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwBnD;AAcD,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAErE,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,qBAAa,OAAO;IACnB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,YAAY,CAAe;gBAEvB,OAAO,EAAE,cAAc,GAAG,MAAM;YAgB9B,QAAQ;IAiBtB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,KAAK;IAIP,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAYpD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB1D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA2B7C"}
package/dist/cache.js ADDED
@@ -0,0 +1,146 @@
1
+ import { getMemoryCache, shouldUseMemoryCache } from "./memory-cache.js";
2
+ import { getProxyCache, shouldUseProxyCache } from "./proxy-cache.js";
3
+ const CACHE_KEY_PREFIX = "cached-kv:";
4
+ const MAX_CACHE_SIZE = 1 * 1024 * 1024; // 1MB - keep cache lightweight
5
+ const INVALIDATE_RETRIES = 3;
6
+ const INVALIDATE_BACKOFF_MS = [100, 500, 1000];
7
+ /**
8
+ * Encode cache keys/tags to be safe for HTTP headers (used by Vercel cache tags).
9
+ * HTTP headers only allow ASCII printable characters (0x20-0x7E), excluding certain chars.
10
+ * We use percent-encoding for non-ASCII and problematic characters, keeping ASCII readable.
11
+ * @internal Exported for testing
12
+ */
13
+ export function encodeCacheKey(path) {
14
+ let result = "";
15
+ for (const char of path) {
16
+ const code = char.charCodeAt(0);
17
+ // Keep ASCII printable chars except %, ", and + (which we use for space encoding)
18
+ if (code >= 0x21 &&
19
+ code <= 0x7e &&
20
+ char !== "%" &&
21
+ char !== '"' &&
22
+ char !== "+") {
23
+ result += char;
24
+ }
25
+ else if (char === " ") {
26
+ result += "+"; // Space as + for readability
27
+ }
28
+ else if (char === "+") {
29
+ result += "%2B"; // Encode + to avoid collision with space
30
+ }
31
+ else {
32
+ // Percent-encode everything else (unicode, control chars, etc.)
33
+ const encoded = encodeURIComponent(char);
34
+ result += encoded;
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ // Lazy import to avoid @vercel/functions initialization message when using proxy
40
+ let vercelCache = null;
41
+ async function getVercelCache() {
42
+ if (!vercelCache) {
43
+ const { getCache } = await import("@vercel/functions");
44
+ vercelCache = getCache();
45
+ }
46
+ return vercelCache;
47
+ }
48
+ let loggedProxyUsage = false;
49
+ export class KVCache {
50
+ ttl;
51
+ useProxy;
52
+ useMemory;
53
+ injectedCache;
54
+ errorHandler;
55
+ constructor(options) {
56
+ if (typeof options === "number") {
57
+ // Legacy: just TTL
58
+ this.ttl = options;
59
+ this.injectedCache = null;
60
+ this.errorHandler = (msg, err) => console.error(msg, err);
61
+ }
62
+ else {
63
+ this.ttl = options.ttl;
64
+ this.injectedCache = options.cache ?? null;
65
+ this.errorHandler =
66
+ options.onError ?? ((msg, err) => console.error(msg, err));
67
+ }
68
+ this.useProxy = shouldUseProxyCache();
69
+ this.useMemory = shouldUseMemoryCache();
70
+ }
71
+ async getCache() {
72
+ if (this.injectedCache) {
73
+ return this.injectedCache;
74
+ }
75
+ if (this.useProxy) {
76
+ if (!loggedProxyUsage) {
77
+ console.log("[KVCache] Using proxy cache for integration tests");
78
+ loggedProxyUsage = true;
79
+ }
80
+ return getProxyCache();
81
+ }
82
+ if (this.useMemory) {
83
+ return getMemoryCache();
84
+ }
85
+ return getVercelCache();
86
+ }
87
+ getCacheKey(path) {
88
+ return `${CACHE_KEY_PREFIX}${encodeCacheKey(path)}`;
89
+ }
90
+ getCacheTag(path) {
91
+ return `${CACHE_KEY_PREFIX}${encodeCacheKey(path)}`;
92
+ }
93
+ sleep(ms) {
94
+ return new Promise((resolve) => setTimeout(resolve, ms));
95
+ }
96
+ async get(path) {
97
+ try {
98
+ const cache = await this.getCache();
99
+ const key = this.getCacheKey(path);
100
+ const result = await cache.get(key);
101
+ return result;
102
+ }
103
+ catch (err) {
104
+ this.errorHandler("[KVCache] cache read failed:", err);
105
+ return null;
106
+ }
107
+ }
108
+ async set(path, entry) {
109
+ // Don't cache entries larger than max size
110
+ if (entry.size > MAX_CACHE_SIZE) {
111
+ return;
112
+ }
113
+ try {
114
+ const cache = await this.getCache();
115
+ const key = this.getCacheKey(path);
116
+ const tag = this.getCacheTag(path);
117
+ await cache.set(key, entry, {
118
+ tags: [tag],
119
+ ttl: this.ttl,
120
+ });
121
+ }
122
+ catch (err) {
123
+ this.errorHandler("[KVCache] cache write failed:", err);
124
+ }
125
+ }
126
+ async invalidate(path) {
127
+ const tag = this.getCacheTag(path);
128
+ let lastError;
129
+ for (let attempt = 0; attempt < INVALIDATE_RETRIES; attempt++) {
130
+ try {
131
+ const cache = await this.getCache();
132
+ await cache.expireTag(tag);
133
+ return;
134
+ }
135
+ catch (err) {
136
+ lastError = err;
137
+ this.errorHandler(`[KVCache] invalidation failed (attempt ${attempt + 1}/${INVALIDATE_RETRIES}):`, err);
138
+ if (attempt < INVALIDATE_RETRIES - 1) {
139
+ await this.sleep(INVALIDATE_BACKOFF_MS[attempt]);
140
+ }
141
+ }
142
+ }
143
+ this.errorHandler(`[KVCache] invalidation failed after ${INVALIDATE_RETRIES} retries, giving up:`, lastError);
144
+ }
145
+ }
146
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { TypedKV } from "./typed-kv.js";
2
+ import type { EntriesIterable, KV2Options, KVEntry, KVGetResult, KVLike, KVSetResult, KeysIterable, SetOptions } from "./types.js";
3
+ export declare class KV2<M = undefined> implements KVLike<M> {
4
+ private prefix;
5
+ private blobStore;
6
+ private cache;
7
+ private largeValueThreshold;
8
+ private tracer;
9
+ constructor(options?: KV2Options);
10
+ private getFullPath;
11
+ private getListPrefix;
12
+ private stripPrefix;
13
+ private readBlob;
14
+ /**
15
+ * Reads blob header without buffering the entire payload.
16
+ * For binary format (large values), returns a reader positioned at the payload.
17
+ * For pure JSON format (small values), returns the complete buffer.
18
+ */
19
+ private readBlobStreaming;
20
+ private readBlobWithConsistencyCheck;
21
+ /**
22
+ * Streaming version of readBlobWithConsistencyCheck.
23
+ * Returns header and a reader for the payload without buffering.
24
+ */
25
+ private readBlobStreamingWithConsistencyCheck;
26
+ private sleep;
27
+ private serializeValue;
28
+ private deserializeValue;
29
+ get<V = unknown>(key: string): Promise<KVGetResult<V, M>>;
30
+ /**
31
+ * Creates a result from a fully-buffered blob (pure JSON format or cached).
32
+ */
33
+ private createResultFromBuffer;
34
+ /**
35
+ * Creates a result with true streaming for binary format (large values).
36
+ * The payload is streamed directly from the blob store without buffering.
37
+ */
38
+ private createStreamingResult;
39
+ private resolveValue;
40
+ private getPayloadBytes;
41
+ set<V = unknown>(key: string, value: V | ReadableStream<Uint8Array>, ...[metadata, options]: undefined extends M ? [M?, SetOptions?] : [M, SetOptions?]): Promise<KVSetResult>;
42
+ private concatStreams;
43
+ delete(key: string): Promise<void>;
44
+ keys(prefix?: string): KeysIterable;
45
+ /**
46
+ * Fetch multiple keys concurrently with bounded concurrency.
47
+ * Returns a Map of key -> entry for all existing keys.
48
+ *
49
+ * @param keys - Array of keys to fetch
50
+ * @param concurrency - Number of concurrent get operations (default: 10)
51
+ */
52
+ getMany<V = unknown>(keys: string[], concurrency?: number): Promise<Map<string, KVEntry<V, M>>>;
53
+ /**
54
+ * Iterate over key-value entries with concurrent fetching.
55
+ * Yields [key, entry] pairs as soon as each fetch completes.
56
+ *
57
+ * @param prefix - Optional prefix to filter keys
58
+ * @param concurrency - Number of concurrent get operations (default: 20)
59
+ */
60
+ entries<V = unknown>(prefix?: string, concurrency?: number): EntriesIterable<V, M>;
61
+ getStore<V, SubM = M, I extends string = never>(subPrefix: string, indexes?: Record<I, import("./typed-kv.js").IndexDef<V>>): TypedKV<V, SubM, I>;
62
+ }
63
+ //# sourceMappingURL=cached-kv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cached-kv.d.ts","sourceRoot":"","sources":["../src/cached-kv.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAGX,eAAe,EAEf,UAAU,EACV,OAAO,EACP,WAAW,EACX,MAAM,EACN,WAAW,EACX,YAAY,EAEZ,UAAU,EAGV,MAAM,YAAY,CAAC;AA4BpB,qBAAa,GAAG,CAAC,CAAC,GAAG,SAAS,CAAE,YAAW,MAAM,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE,UAAe;IAgBpC,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;YASL,QAAQ;IAkBtB;;;;OAIG;YACW,iBAAiB;YA6EjB,4BAA4B;IA0C1C;;;OAGG;YACW,qCAAqC;IAgDnD,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,gBAAgB;IAUlB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAqH/D;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAwE9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;YAoJf,YAAY;IA4C1B,OAAO,CAAC,eAAe;IAUjB,GAAG,CAAC,CAAC,GAAG,OAAO,EACpB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,EACrC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,SAAS,SAAS,CAAC,GACxC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,GACjB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,GACjB,OAAO,CAAC,WAAW,CAAC;IAoIvB,OAAO,CAAC,aAAa;IAiCf,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBxC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY;IA+EnC;;;;;;OAMG;IACG,OAAO,CAAC,CAAC,GAAG,OAAO,EACxB,IAAI,EAAE,MAAM,EAAE,EACd,WAAW,SAAK,GACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAoCtC;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,GAAG,OAAO,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,SAAK,GACd,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC;IA6GxB,QAAQ,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,KAAK,EAC7C,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;CAOtB"}