@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,101 @@
1
+ /**
2
+ * Performance comparison test: raw blob access vs cached reads
3
+ */
4
+ import { VercelBlobStore } from "../blob-store.js";
5
+ import { KV2 } from "../cached-kv.js";
6
+ import { cleanupTestBlobs, uniqueTestPrefix } from "./index.js";
7
+ async function readBlobDirect(blobStore, path) {
8
+ const result = await blobStore.get(path, { access: "public" });
9
+ if (!result)
10
+ return null;
11
+ const chunks = [];
12
+ const reader = result.stream.getReader();
13
+ while (true) {
14
+ const { done, value } = await reader.read();
15
+ if (done)
16
+ break;
17
+ chunks.push(value);
18
+ }
19
+ return Buffer.concat(chunks);
20
+ }
21
+ export async function runPerfTest(iterations = 10) {
22
+ const prefix = uniqueTestPrefix();
23
+ const blobStore = new VercelBlobStore();
24
+ const kv = new KV2({ prefix, blobStore });
25
+ const testValue = {
26
+ data: "x".repeat(1000), // 1KB payload
27
+ nested: { deeply: { value: 42 } },
28
+ array: Array.from({ length: 100 }, (_, i) => i),
29
+ };
30
+ // Write test data
31
+ await kv.set("perf-test-key", testValue, { test: true });
32
+ const blobPath = `cached-kv/${prefix}perf-test-key`;
33
+ // Raw blob reads - direct blob store access, bypasses all caching
34
+ const rawBlobReads = [];
35
+ for (let i = 0; i < iterations; i++) {
36
+ const start = performance.now();
37
+ const buffer = await readBlobDirect(blobStore, blobPath);
38
+ if (buffer) {
39
+ // Parse the blob to simulate full read
40
+ const headerLength = buffer.readUInt32BE(0);
41
+ const headerJson = buffer.subarray(4, 4 + headerLength).toString("utf-8");
42
+ JSON.parse(headerJson);
43
+ }
44
+ rawBlobReads.push(performance.now() - start);
45
+ }
46
+ // Cached reads via KV2 - uses @vercel/functions cache
47
+ const cachedReads = [];
48
+ // First read to populate cache
49
+ const warmup = await kv.get("perf-test-key");
50
+ if (warmup.exists)
51
+ await warmup.value;
52
+ for (let i = 0; i < iterations; i++) {
53
+ const start = performance.now();
54
+ const result = await kv.get("perf-test-key");
55
+ if (result.exists) {
56
+ await result.value;
57
+ }
58
+ cachedReads.push(performance.now() - start);
59
+ }
60
+ // Cleanup
61
+ await cleanupTestBlobs(prefix, blobStore);
62
+ // Calculate stats
63
+ const rawBlobAvg = rawBlobReads.reduce((a, b) => a + b, 0) / rawBlobReads.length;
64
+ const cachedAvg = cachedReads.reduce((a, b) => a + b, 0) / cachedReads.length;
65
+ const speedup = rawBlobAvg / cachedAvg;
66
+ const summary = [
67
+ "",
68
+ "=".repeat(60),
69
+ "PERFORMANCE COMPARISON: Raw Blob vs Cached Reads",
70
+ "=".repeat(60),
71
+ "",
72
+ `Iterations: ${iterations}`,
73
+ "Payload size: ~1KB JSON",
74
+ "",
75
+ "Direct Blob Store Reads (no cache):",
76
+ ` Average: ${rawBlobAvg.toFixed(2)}ms`,
77
+ ` Min: ${Math.min(...rawBlobReads).toFixed(2)}ms`,
78
+ ` Max: ${Math.max(...rawBlobReads).toFixed(2)}ms`,
79
+ ` All: [${rawBlobReads.map((r) => `${r.toFixed(0)}ms`).join(", ")}]`,
80
+ "",
81
+ "KV2 Reads (with regional cache):",
82
+ ` Average: ${cachedAvg.toFixed(2)}ms`,
83
+ ` Min: ${Math.min(...cachedReads).toFixed(2)}ms`,
84
+ ` Max: ${Math.max(...cachedReads).toFixed(2)}ms`,
85
+ ` All: [${cachedReads.map((r) => `${r.toFixed(0)}ms`).join(", ")}]`,
86
+ "",
87
+ "-".repeat(60),
88
+ `SPEEDUP: ${speedup.toFixed(1)}x faster with cache`,
89
+ "-".repeat(60),
90
+ "",
91
+ ].join("\n");
92
+ return {
93
+ rawBlobReads,
94
+ cachedReads,
95
+ rawBlobAvg,
96
+ cachedAvg,
97
+ speedup,
98
+ summary,
99
+ };
100
+ }
101
+ //# sourceMappingURL=perf-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-test.js","sourceRoot":"","sources":["../../src/testing/perf-test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAWhE,KAAK,UAAU,cAAc,CAC5B,SAA0B,EAC1B,IAAY;IAEZ,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;IACzC,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAU,GAAG,EAAE;IAChD,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAC;IACxC,MAAM,EAAE,GAAG,IAAI,GAAG,CAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAE7D,MAAM,SAAS,GAAG;QACjB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc;QACtC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QACjC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;KAC/C,CAAC;IAEF,kBAAkB;IAClB,MAAM,EAAE,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,aAAa,MAAM,eAAe,CAAC;IAEpD,kEAAkE;IAClE,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACzD,IAAI,MAAM,EAAE,CAAC;YACZ,uCAAuC;YACvC,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC1E,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;QACD,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,sDAAsD;IACtD,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,+BAA+B;IAC/B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,MAAM;QAAE,MAAM,MAAM,CAAC,KAAK,CAAC;IAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,MAAM,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,UAAU;IACV,MAAM,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAE1C,kBAAkB;IAClB,MAAM,UAAU,GACf,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC;IAC/D,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC;IAC9E,MAAM,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IAEvC,MAAM,OAAO,GAAG;QACf,EAAE;QACF,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACd,kDAAkD;QAClD,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACd,EAAE;QACF,eAAe,UAAU,EAAE;QAC3B,yBAAyB;QACzB,EAAE;QACF,qCAAqC;QACrC,cAAc,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACvC,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QAClD,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QAClD,WAAW,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACrE,EAAE;QACF,kCAAkC;QAClC,cAAc,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACtC,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACjD,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACjD,WAAW,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACpE,EAAE;QACF,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACd,YAAY,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;QACnD,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACd,EAAE;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO;QACN,YAAY;QACZ,WAAW;QACX,UAAU;QACV,SAAS;QACT,OAAO;QACP,OAAO;KACP,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import "./test-index.js";
2
+ //# sourceMappingURL=run-tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tests.d.ts","sourceRoot":"","sources":["../../src/testing/run-tests.ts"],"names":[],"mappings":"AAiHA,OAAO,iBAAiB,CAAC"}
@@ -0,0 +1,141 @@
1
+ // Load environment variables from .env.local before anything else
2
+ import dotenv from "dotenv";
3
+ dotenv.config({ path: ".env.local" });
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { printTimingStats, useRealBlobStore, validateIntegrationTestEnv, } from "./index.js";
7
+ import { runTests } from "./vitest-compat.js";
8
+ /**
9
+ * Finds all *.test.ts files in src/ directory recursively.
10
+ */
11
+ function findTestFiles(dir) {
12
+ const results = [];
13
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ const fullPath = path.join(dir, entry.name);
16
+ if (entry.isDirectory() && entry.name !== "node_modules") {
17
+ results.push(...findTestFiles(fullPath));
18
+ }
19
+ else if (entry.isFile() && entry.name.endsWith(".test.ts")) {
20
+ results.push(fullPath);
21
+ }
22
+ }
23
+ return results;
24
+ }
25
+ /**
26
+ * Extracts imported test file paths from test-index.ts content.
27
+ */
28
+ function extractImportedTests(indexContent) {
29
+ const imports = new Set();
30
+ const importRegex = /import\s+["']([^"']+\.test\.js)["']/g;
31
+ for (const match of indexContent.matchAll(importRegex)) {
32
+ // Convert ./foo.test.js to foo.test.ts
33
+ const importPath = match[1].replace(/\.js$/, ".ts");
34
+ imports.add(importPath);
35
+ }
36
+ return imports;
37
+ }
38
+ /**
39
+ * Checks that all test files are imported in test-index.ts.
40
+ * Only runs when VERCEL_ENV is not set (local development).
41
+ */
42
+ function checkAllTestsImported() {
43
+ if (process.env.VERCEL_ENV) {
44
+ return; // Skip on Vercel deployments
45
+ }
46
+ // import.meta.url points to dist/testing/, we need src/testing/
47
+ const distTestingDir = path.dirname(new URL(import.meta.url).pathname);
48
+ const projectRoot = path.dirname(path.dirname(distTestingDir)); // Go up from dist/testing/ to project root
49
+ const srcDir = path.join(projectRoot, "src");
50
+ const testIndexPath = path.join(srcDir, "testing", "test-index.ts");
51
+ // Find all test files
52
+ const allTestFiles = findTestFiles(srcDir);
53
+ // Read test-index.ts and extract imports
54
+ const indexContent = fs.readFileSync(testIndexPath, "utf-8");
55
+ const importedTests = extractImportedTests(indexContent);
56
+ // Normalize paths relative to test-index.ts location
57
+ const testIndexDir = path.dirname(testIndexPath);
58
+ const missingTests = [];
59
+ for (const testFile of allTestFiles) {
60
+ // Skip test-index.ts itself and vitest-compat tests
61
+ if (testFile.includes("test-index") || testFile.includes("vitest-compat")) {
62
+ continue;
63
+ }
64
+ // Convert absolute path to relative import path from test-index.ts
65
+ const relativePath = path.relative(testIndexDir, testFile);
66
+ const importPath = relativePath.startsWith(".")
67
+ ? relativePath
68
+ : `./${relativePath}`;
69
+ // Normalize for comparison (../ prefix for files in parent dir)
70
+ const normalizedImport = importPath.replace(/\\/g, "/");
71
+ if (!importedTests.has(normalizedImport)) {
72
+ missingTests.push(testFile);
73
+ }
74
+ }
75
+ if (missingTests.length > 0) {
76
+ console.error("\x1b[31m=== Missing Test Imports ===\x1b[0m");
77
+ console.error("The following test files are not imported in test-index.ts:");
78
+ for (const file of missingTests) {
79
+ console.error(` - ${path.relative(srcDir, file)}`);
80
+ }
81
+ console.error("\nAdd them to src/testing/test-index.ts");
82
+ process.exit(1);
83
+ }
84
+ }
85
+ // Check all tests are imported (local dev only)
86
+ checkAllTestsImported();
87
+ // Validate env vars before loading tests
88
+ validateIntegrationTestEnv();
89
+ import "./test-index.js";
90
+ // Support filtering: `pnpm test -- <filter>`
91
+ // Matches against the full test name (suite > test name).
92
+ const filter = process.argv.filter((a) => a !== "--").slice(2)[0] || undefined;
93
+ if (filter) {
94
+ console.log(`\x1b[36mFilter: ${filter}\x1b[0m\n`);
95
+ }
96
+ // All tests use isolated context, safe for concurrent execution.
97
+ const concurrency = 30;
98
+ const results = await runTests({
99
+ filter,
100
+ concurrency,
101
+ keepAliveInterval: 5000, // 5 seconds
102
+ onKeepAlive: (testName, elapsedMs) => {
103
+ const elapsedSec = Math.floor(elapsedMs / 1000);
104
+ console.log(`\x1b[33m⏳ [${elapsedSec}s] Still running: ${testName}\x1b[0m`);
105
+ },
106
+ onProgress: (result) => {
107
+ const suitePath = result.suite.join(" > ");
108
+ const fullName = suitePath ? `${suitePath} > ${result.name}` : result.name;
109
+ const status = result.passed
110
+ ? "\x1b[32m✓ PASS\x1b[0m"
111
+ : "\x1b[31m✗ FAIL\x1b[0m";
112
+ console.log(`${status} ${fullName} (${result.duration}ms)`);
113
+ if (!result.passed && result.error) {
114
+ console.log(` \x1b[31m${result.error}\x1b[0m`);
115
+ }
116
+ },
117
+ });
118
+ console.log("\n=== Test Results ===");
119
+ console.log("Total:", results.total);
120
+ console.log("\x1b[32mPassed:", results.passed, "\x1b[0m");
121
+ if (results.failed > 0) {
122
+ console.log("\x1b[31mFailed:", results.failed, "\x1b[0m");
123
+ }
124
+ const failures = results.results.filter((r) => !r.passed);
125
+ if (failures.length > 0) {
126
+ console.log("\n=== FAILURES ===");
127
+ for (const f of failures) {
128
+ const suitePath = f.suite.join(" > ");
129
+ const fullName = suitePath ? `${suitePath} > ${f.name}` : f.name;
130
+ console.log(`\x1b[31m✗ FAIL ${fullName}\x1b[0m`);
131
+ if (f.error) {
132
+ console.log(` ${f.error}`);
133
+ }
134
+ }
135
+ }
136
+ // Print timing stats for integration tests
137
+ if (useRealBlobStore()) {
138
+ printTimingStats();
139
+ }
140
+ process.exit(results.failed > 0 ? 1 : 0);
141
+ //# sourceMappingURL=run-tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tests.js","sourceRoot":"","sources":["../../src/testing/run-tests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/testing/setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import { config } from "dotenv";
2
+ config({ path: ".env.local" });
3
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/testing/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Test index - imports all test files to register them with vitest-compat.
3
+ * Import this file to load all tests before running.
4
+ */
5
+ import "../blob-format.test.js";
6
+ import "../cache.test.js";
7
+ import "../cached-kv.test.js";
8
+ import "../cached-kv.values.test.js";
9
+ import "../cached-kv.keys.test.js";
10
+ import "../cached-kv.entries.test.js";
11
+ import "../cached-kv.cache.test.js";
12
+ import "../typed-kv.test.js";
13
+ import "../no-metadata.test.js";
14
+ import "../optimistic-locking.test.js";
15
+ import "../schema/schema.test.js";
16
+ import "../manifest-log.test.js";
17
+ import "../upstream-kv.test.js";
18
+ import "../upstream-overlay.test.js";
19
+ import "../create-kv.test.js";
20
+ import "../chaos/boundaries.chaos.test.js";
21
+ import "../chaos/cache.chaos.test.js";
22
+ import "../chaos/concurrency.chaos.test.js";
23
+ import "../chaos/streams.chaos.test.js";
24
+ import "../chaos/malformed.chaos.test.js";
25
+ import "../indexed-kv.test.js";
26
+ import "../cli.test.js";
27
+ import "../readme.test.js";
28
+ //# sourceMappingURL=test-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-index.d.ts","sourceRoot":"","sources":["../../src/testing/test-index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,wBAAwB,CAAC;AAChC,OAAO,kBAAkB,CAAC;AAC1B,OAAO,sBAAsB,CAAC;AAC9B,OAAO,6BAA6B,CAAC;AACrC,OAAO,2BAA2B,CAAC;AACnC,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,+BAA+B,CAAC;AAGvC,OAAO,0BAA0B,CAAC;AAGlC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAChC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sBAAsB,CAAC;AAG9B,OAAO,mCAAmC,CAAC;AAC3C,OAAO,8BAA8B,CAAC;AACtC,OAAO,oCAAoC,CAAC;AAC5C,OAAO,gCAAgC,CAAC;AACxC,OAAO,kCAAkC,CAAC;AAG1C,OAAO,uBAAuB,CAAC;AAG/B,OAAO,gBAAgB,CAAC;AAGxB,OAAO,mBAAmB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Test index - imports all test files to register them with vitest-compat.
3
+ * Import this file to load all tests before running.
4
+ */
5
+ // Core tests
6
+ import "../blob-format.test.js";
7
+ import "../cache.test.js";
8
+ import "../cached-kv.test.js";
9
+ import "../cached-kv.values.test.js";
10
+ import "../cached-kv.keys.test.js";
11
+ import "../cached-kv.entries.test.js";
12
+ import "../cached-kv.cache.test.js";
13
+ import "../typed-kv.test.js";
14
+ import "../no-metadata.test.js";
15
+ import "../optimistic-locking.test.js";
16
+ // Schema tests
17
+ import "../schema/schema.test.js";
18
+ // CoW / Environment-aware tests
19
+ import "../manifest-log.test.js";
20
+ import "../upstream-kv.test.js";
21
+ import "../upstream-overlay.test.js";
22
+ import "../create-kv.test.js";
23
+ // Chaos tests
24
+ import "../chaos/boundaries.chaos.test.js";
25
+ import "../chaos/cache.chaos.test.js";
26
+ import "../chaos/concurrency.chaos.test.js";
27
+ import "../chaos/streams.chaos.test.js";
28
+ import "../chaos/malformed.chaos.test.js";
29
+ // TypedKV index tests
30
+ import "../indexed-kv.test.js";
31
+ // CLI tests
32
+ import "../cli.test.js";
33
+ // Documentation tests
34
+ import "../readme.test.js";
35
+ //# sourceMappingURL=test-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-index.js","sourceRoot":"","sources":["../../src/testing/test-index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,aAAa;AACb,OAAO,wBAAwB,CAAC;AAChC,OAAO,kBAAkB,CAAC;AAC1B,OAAO,sBAAsB,CAAC;AAC9B,OAAO,6BAA6B,CAAC;AACrC,OAAO,2BAA2B,CAAC;AACnC,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,+BAA+B,CAAC;AAEvC,eAAe;AACf,OAAO,0BAA0B,CAAC;AAElC,gCAAgC;AAChC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAChC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sBAAsB,CAAC;AAE9B,cAAc;AACd,OAAO,mCAAmC,CAAC;AAC3C,OAAO,8BAA8B,CAAC;AACtC,OAAO,oCAAoC,CAAC;AAC5C,OAAO,gCAAgC,CAAC;AACxC,OAAO,kCAAkC,CAAC;AAE1C,sBAAsB;AACtB,OAAO,uBAAuB,CAAC;AAE/B,YAAY;AACZ,OAAO,gBAAgB,CAAC;AAExB,sBAAsB;AACtB,OAAO,mBAAmB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { KV2 } from "../cached-kv.js";
2
+ import type { KV2Options } from "../types.js";
3
+ import { FakeBlobStore } from "./fake-blob-store.js";
4
+ import { type TestContext as BaseTestContext } from "./vitest-compat.js";
5
+ export interface TestMetadata {
6
+ createdBy: string;
7
+ version: number;
8
+ }
9
+ /** Context available to each test */
10
+ export interface KVTestContext extends BaseTestContext {
11
+ kv: KV2<TestMetadata>;
12
+ blobStore: FakeBlobStore | undefined;
13
+ cleanup: () => Promise<void>;
14
+ isReal: boolean;
15
+ }
16
+ /**
17
+ * Sets up a test context with beforeAll/afterAll/beforeEach/afterEach hooks.
18
+ * Uses the test context system for concurrent-safe test execution.
19
+ *
20
+ * Each test gets its own isolated FakeBlobStore (for unit tests) or
21
+ * unique prefix (for integration tests) to enable concurrent execution.
22
+ */
23
+ export declare function setupTestContext(options?: Partial<KV2Options>): void;
24
+ export { createTestKV, uniqueTestPrefix, useRealBlobStore } from "./index.js";
25
+ export type { PrefixString } from "../types.js";
26
+ /** Typed test function that receives KVTestContext */
27
+ type TypedTestFn = (ctx: KVTestContext) => Promise<void> | void;
28
+ /** Typed `it` that provides KVTestContext to tests */
29
+ export declare const it: (name: string, fn: TypedTestFn) => void;
30
+ /** Typed `test` that provides KVTestContext to tests (alias for `it`) */
31
+ export declare const test: (name: string, fn: TypedTestFn) => void;
32
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../../src/testing/test-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EACN,KAAK,WAAW,IAAI,eAAe,EAOnC,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAc,SAAQ,eAAe;IACrD,EAAE,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IACtB,SAAS,EAAE,aAAa,GAAG,SAAS,CAAC;IACrC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;CAChB;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAqDpE;AAED,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,sDAAsD;AACtD,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEhE,sDAAsD;AACtD,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,EAAE,IAAI,WAAW,KAAG,IAElD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,IAAI,SALQ,MAAM,MAAM,WAAW,KAAG,IAK7B,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { KV2 } from "../cached-kv.js";
2
+ import { FakeBlobStore } from "./fake-blob-store.js";
3
+ import { createTestKV, uniqueTestPrefix, useRealBlobStore } from "./index.js";
4
+ import { afterAll, afterEach, it as baseIt, beforeAll, beforeEach, } from "./vitest-compat.js";
5
+ /** Suite-level cleanup functions (not copied to test context) */
6
+ const suiteCleanupFns = new Map();
7
+ /**
8
+ * Sets up a test context with beforeAll/afterAll/beforeEach/afterEach hooks.
9
+ * Uses the test context system for concurrent-safe test execution.
10
+ *
11
+ * Each test gets its own isolated FakeBlobStore (for unit tests) or
12
+ * unique prefix (for integration tests) to enable concurrent execution.
13
+ */
14
+ export function setupTestContext(options) {
15
+ // Use a unique key for this suite's cleanup functions
16
+ const suiteKey = Math.random().toString(36).slice(2);
17
+ beforeAll(() => {
18
+ suiteCleanupFns.set(suiteKey, []);
19
+ if (useRealBlobStore()) {
20
+ console.log("Running tests against REAL blob store");
21
+ }
22
+ });
23
+ beforeEach((ctx) => {
24
+ const testCtx = ctx;
25
+ if (useRealBlobStore()) {
26
+ // For real blob store, each test gets its own unique prefix
27
+ const result = createTestKV(options);
28
+ testCtx.kv = result.kv;
29
+ testCtx.blobStore = undefined;
30
+ testCtx.cleanup = result.cleanup;
31
+ testCtx.isReal = true;
32
+ // Track cleanup for afterAll (thread-safe push)
33
+ suiteCleanupFns.get(suiteKey)?.push(result.cleanup);
34
+ }
35
+ else {
36
+ // For unit tests, each test gets its own FakeBlobStore for isolation
37
+ const blobStore = new FakeBlobStore();
38
+ const prefix = options?.prefix ?? uniqueTestPrefix();
39
+ testCtx.kv = new KV2({
40
+ ...options,
41
+ prefix,
42
+ blobStore,
43
+ });
44
+ testCtx.blobStore = blobStore;
45
+ testCtx.cleanup = async () => blobStore.clear();
46
+ testCtx.isReal = false;
47
+ }
48
+ });
49
+ afterEach((ctx) => {
50
+ const testCtx = ctx;
51
+ // Clear the blob store after each test
52
+ if (testCtx.blobStore) {
53
+ testCtx.blobStore.clear();
54
+ }
55
+ });
56
+ afterAll(async () => {
57
+ // Clean up all real blob store prefixes created during tests
58
+ const cleanups = suiteCleanupFns.get(suiteKey) ?? [];
59
+ for (const cleanup of cleanups) {
60
+ await cleanup();
61
+ }
62
+ suiteCleanupFns.delete(suiteKey);
63
+ });
64
+ }
65
+ export { createTestKV, uniqueTestPrefix, useRealBlobStore } from "./index.js";
66
+ /** Typed `it` that provides KVTestContext to tests */
67
+ export const it = (name, fn) => {
68
+ baseIt(name, fn);
69
+ };
70
+ /** Typed `test` that provides KVTestContext to tests (alias for `it`) */
71
+ export const test = it;
72
+ //# sourceMappingURL=test-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.js","sourceRoot":"","sources":["../../src/testing/test-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { KV2 } from "../cached-kv.js";
2
+ import { ManifestLog } from "../manifest-log.js";
3
+ import type { BlobStore } from "../types.js";
4
+ import { UpstreamKV } from "../upstream-kv.js";
5
+ import { type TestContext as BaseTestContext } from "./vitest-compat.js";
6
+ /** Metadata type for UpstreamKV tests */
7
+ interface TestVersionMetadata {
8
+ version: number;
9
+ }
10
+ /** Context available to each UpstreamKV test */
11
+ export interface UpstreamKVTestContext extends BaseTestContext {
12
+ blobStore: BlobStore;
13
+ localKV: KV2<TestVersionMetadata>;
14
+ upstreamKV: KV2<TestVersionMetadata>;
15
+ manifest: ManifestLog;
16
+ kv: UpstreamKV<TestVersionMetadata>;
17
+ cleanup?: () => Promise<void>;
18
+ }
19
+ /**
20
+ * Sets up an UpstreamKV test context.
21
+ * Each test gets its own isolated FakeBlobStore and UpstreamKV.
22
+ * Uses real blob store if INTEGRATION_TEST=1.
23
+ */
24
+ export declare function setupUpstreamKVTestContext(): void;
25
+ /** Typed test function that receives UpstreamKVTestContext */
26
+ type TypedTestFn = (ctx: UpstreamKVTestContext) => Promise<void> | void;
27
+ /** Typed `it` that provides UpstreamKVTestContext to tests */
28
+ export declare const it: (name: string, fn: TypedTestFn) => void;
29
+ export {};
30
+ //# sourceMappingURL=upstream-kv-test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-kv-test-setup.d.ts","sourceRoot":"","sources":["../../src/testing/upstream-kv-test-setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAS/C,OAAO,EACN,KAAK,WAAW,IAAI,eAAe,EAInC,MAAM,oBAAoB,CAAC;AAE5B,yCAAyC;AACzC,UAAU,mBAAmB;IAC5B,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,gDAAgD;AAChD,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClC,UAAU,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACrC,QAAQ,EAAE,WAAW,CAAC;IACtB,EAAE,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAqEjD;AAED,8DAA8D;AAC9D,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAExE,8DAA8D;AAC9D,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,EAAE,IAAI,WAAW,KAAG,IAElD,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { VercelBlobStore } from "../blob-store.js";
2
+ import { KV2 } from "../cached-kv.js";
3
+ import { ManifestLog } from "../manifest-log.js";
4
+ import { UpstreamKV } from "../upstream-kv.js";
5
+ import { FakeBlobStore } from "./fake-blob-store.js";
6
+ import { FakeCache } from "./fake-cache.js";
7
+ import { cleanupTestBlobs, getStatsTracer, uniqueTestPrefix, useRealBlobStore, } from "./index.js";
8
+ import { afterEach, it as baseIt, beforeEach, } from "./vitest-compat.js";
9
+ /**
10
+ * Sets up an UpstreamKV test context.
11
+ * Each test gets its own isolated FakeBlobStore and UpstreamKV.
12
+ * Uses real blob store if INTEGRATION_TEST=1.
13
+ */
14
+ export function setupUpstreamKVTestContext() {
15
+ beforeEach((ctx) => {
16
+ const testCtx = ctx;
17
+ if (useRealBlobStore()) {
18
+ // Integration test: use real blob store with unique prefix and stats tracer
19
+ const prefix = uniqueTestPrefix();
20
+ const tracer = getStatsTracer();
21
+ testCtx.blobStore = new VercelBlobStore();
22
+ testCtx.localKV = new KV2({
23
+ prefix: `${prefix}local/`,
24
+ blobStore: testCtx.blobStore,
25
+ tracer,
26
+ });
27
+ testCtx.upstreamKV = new KV2({
28
+ prefix: `${prefix}upstream/`,
29
+ blobStore: testCtx.blobStore,
30
+ tracer,
31
+ });
32
+ testCtx.manifest = new ManifestLog(testCtx.blobStore, `${prefix}_manifest/`);
33
+ testCtx.kv = new UpstreamKV(testCtx.localKV, testCtx.upstreamKV, testCtx.manifest);
34
+ testCtx.cleanup = () => cleanupTestBlobs(prefix, testCtx.blobStore);
35
+ }
36
+ else {
37
+ // Unit test: use fakes
38
+ testCtx.blobStore = new FakeBlobStore();
39
+ const cache = new FakeCache();
40
+ testCtx.localKV = new KV2({
41
+ prefix: "local/",
42
+ blobStore: testCtx.blobStore,
43
+ cache,
44
+ });
45
+ testCtx.upstreamKV = new KV2({
46
+ prefix: "upstream/",
47
+ blobStore: testCtx.blobStore,
48
+ cache,
49
+ });
50
+ // Manifest uses a separate namespace to avoid being listed with data keys
51
+ testCtx.manifest = new ManifestLog(testCtx.blobStore, "_manifest/local/", cache);
52
+ testCtx.kv = new UpstreamKV(testCtx.localKV, testCtx.upstreamKV, testCtx.manifest);
53
+ }
54
+ });
55
+ afterEach(async (ctx) => {
56
+ const testCtx = ctx;
57
+ if (testCtx.cleanup) {
58
+ await testCtx.cleanup();
59
+ }
60
+ });
61
+ }
62
+ /** Typed `it` that provides UpstreamKVTestContext to tests */
63
+ export const it = (name, fn) => {
64
+ baseIt(name, fn);
65
+ };
66
+ //# sourceMappingURL=upstream-kv-test-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-kv-test-setup.js","sourceRoot":"","sources":["../../src/testing/upstream-kv-test-setup.ts"],"names":[],"mappings":"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"}