eigen-db 4.1.0 → 4.3.0

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.
@@ -5,15 +5,15 @@ describe("topKResults", () => {
5
5
  const keys = ["apple", "banana", "cherry", "date", "elderberry"];
6
6
  const resolveKey = (index: number) => keys[index];
7
7
 
8
- it("sorts results by ascending distance", () => {
8
+ it("sorts results by descending similarity", () => {
9
9
  const scores = new Float32Array([0.3, 0.9, 0.1, 0.7, 0.5]);
10
10
  const results = topKResults(scores, resolveKey, 5);
11
11
 
12
12
  expect(results).toHaveLength(5);
13
13
  expect(results[0].key).toBe("banana");
14
- expect(results[0].distance).toBeCloseTo(0.1, 4);
14
+ expect(results[0].similarity).toBeCloseTo(0.9, 4);
15
15
  expect(results[1].key).toBe("date");
16
- expect(results[1].distance).toBeCloseTo(0.3, 4);
16
+ expect(results[1].similarity).toBeCloseTo(0.7, 4);
17
17
  expect(results[2].key).toBe("elderberry");
18
18
  expect(results[3].key).toBe("apple");
19
19
  expect(results[4].key).toBe("cherry");
@@ -25,7 +25,7 @@ describe("topKResults", () => {
25
25
 
26
26
  expect(results).toHaveLength(3);
27
27
  expect(results[0].key).toBe("banana");
28
- expect(results[0].distance).toBeCloseTo(0.1, 4);
28
+ expect(results[0].similarity).toBeCloseTo(0.9, 4);
29
29
  expect(results[2].key).toBe("elderberry");
30
30
  });
31
31
 
@@ -41,23 +41,23 @@ describe("topKResults", () => {
41
41
  expect(results).toHaveLength(2);
42
42
  });
43
43
 
44
- it("filters results by maxDistance", () => {
44
+ it("filters results by minSimilarity", () => {
45
45
  const scores = new Float32Array([0.3, 0.9, 0.1, 0.7, 0.5]);
46
- // distances: apple=0.7, banana=0.1, cherry=0.9, date=0.3, elderberry=0.5
46
+ // similarities: apple=0.3, banana=0.9, cherry=0.1, date=0.7, elderberry=0.5
47
47
  const results = topKResults(scores, resolveKey, 5, 0.5);
48
48
 
49
49
  expect(results).toHaveLength(3);
50
50
  expect(results[0].key).toBe("banana");
51
- expect(results[0].distance).toBeCloseTo(0.1, 4);
51
+ expect(results[0].similarity).toBeCloseTo(0.9, 4);
52
52
  expect(results[1].key).toBe("date");
53
- expect(results[1].distance).toBeCloseTo(0.3, 4);
53
+ expect(results[1].similarity).toBeCloseTo(0.7, 4);
54
54
  expect(results[2].key).toBe("elderberry");
55
- expect(results[2].distance).toBeCloseTo(0.5, 4);
55
+ expect(results[2].similarity).toBeCloseTo(0.5, 4);
56
56
  });
57
57
 
58
- it("maxDistance is inclusive", () => {
58
+ it("minSimilarity is inclusive", () => {
59
59
  const scores = new Float32Array([0.5]);
60
- // distance = 1 - 0.5 = 0.5
60
+ // similarity = 0.5
61
61
  const results = topKResults(scores, resolveKey, 10, 0.5);
62
62
  expect(results).toHaveLength(1);
63
63
  });
@@ -73,13 +73,13 @@ describe("iterableResults", () => {
73
73
  const keys = ["apple", "banana", "cherry", "date", "elderberry"];
74
74
  const resolveKey = (index: number) => keys[index];
75
75
 
76
- it("sorts results by ascending distance", () => {
76
+ it("sorts results by descending similarity", () => {
77
77
  const scores = new Float32Array([0.3, 0.9, 0.1, 0.7, 0.5]);
78
78
  const results = [...iterableResults(scores, resolveKey, 5)];
79
79
 
80
80
  expect(results).toHaveLength(5);
81
81
  expect(results[0].key).toBe("banana");
82
- expect(results[0].distance).toBeCloseTo(0.1, 4);
82
+ expect(results[0].similarity).toBeCloseTo(0.9, 4);
83
83
  expect(results[1].key).toBe("date");
84
84
  expect(results[4].key).toBe("cherry");
85
85
  });
@@ -136,13 +136,13 @@ describe("iterableResults", () => {
136
136
  if (partial.length === 2) break;
137
137
  }
138
138
  expect(partial).toHaveLength(2);
139
- expect(partial[0]).toBe("elderberry"); // distance 0.5 (lowest)
140
- expect(partial[1]).toBe("date"); // distance 0.6
139
+ expect(partial[0]).toBe("elderberry"); // similarity 0.5 (highest)
140
+ expect(partial[1]).toBe("date"); // similarity 0.4
141
141
  });
142
142
 
143
- it("early stops iteration by maxDistance", () => {
143
+ it("early stops iteration by minSimilarity", () => {
144
144
  const scores = new Float32Array([0.3, 0.9, 0.1, 0.7, 0.5]);
145
- // distances: apple=0.7, banana=0.1, cherry=0.9, date=0.3, elderberry=0.5
145
+ // similarities: apple=0.3, banana=0.9, cherry=0.1, date=0.7, elderberry=0.5
146
146
  const results = [...iterableResults(scores, resolveKey, 5, 0.5)];
147
147
 
148
148
  expect(results).toHaveLength(3);
@@ -151,9 +151,9 @@ describe("iterableResults", () => {
151
151
  expect(results[2].key).toBe("elderberry");
152
152
  });
153
153
 
154
- it("maxDistance is inclusive", () => {
154
+ it("minSimilarity is inclusive", () => {
155
155
  const scores = new Float32Array([0.5]);
156
- // distance = 1 - 0.5 = 0.5
156
+ // similarity = 0.5
157
157
  const results = [...iterableResults(scores, resolveKey, 10, 0.5)];
158
158
  expect(results).toHaveLength(1);
159
159
  });
@@ -16,6 +16,51 @@ function getWasmBinary(): Promise<Uint8Array> {
16
16
  return wasmBinaryPromise;
17
17
  }
18
18
 
19
+ /** Collect a ReadableStream into a single Uint8Array. */
20
+ async function streamToBytes(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
21
+ const chunks: Uint8Array[] = [];
22
+ const reader = stream.getReader();
23
+ for (;;) {
24
+ const { done, value } = await reader.read();
25
+ if (done) break;
26
+ chunks.push(value);
27
+ }
28
+ const totalSize = chunks.reduce((sum, c) => sum + c.byteLength, 0);
29
+ const result = new Uint8Array(totalSize);
30
+ let offset = 0;
31
+ for (const chunk of chunks) {
32
+ result.set(chunk, offset);
33
+ offset += chunk.byteLength;
34
+ }
35
+ return result;
36
+ }
37
+
38
+ /** Create a ReadableStream from a Uint8Array (single chunk). */
39
+ function bytesToStream(data: Uint8Array): ReadableStream<Uint8Array> {
40
+ return new ReadableStream({
41
+ start(controller) {
42
+ controller.enqueue(data);
43
+ controller.close();
44
+ },
45
+ });
46
+ }
47
+
48
+ /** Create a ReadableStream from a Uint8Array, delivering it in small chunks. */
49
+ function bytesToChunkedStream(data: Uint8Array, chunkSize: number): ReadableStream<Uint8Array> {
50
+ let offset = 0;
51
+ return new ReadableStream({
52
+ pull(controller) {
53
+ if (offset >= data.byteLength) {
54
+ controller.close();
55
+ return;
56
+ }
57
+ const end = Math.min(offset + chunkSize, data.byteLength);
58
+ controller.enqueue(data.subarray(offset, end));
59
+ offset = end;
60
+ },
61
+ });
62
+ }
63
+
19
64
  describe("VectorDB", () => {
20
65
  let storage: InMemoryStorageProvider;
21
66
 
@@ -180,17 +225,17 @@ describe("VectorDB", () => {
180
225
  const results = db.query([1, 0, 0, 0]);
181
226
  expect(results.length).toBe(3);
182
227
 
183
- // x-axis should be the best match (identical direction, distance0)
228
+ // x-axis should be the best match (identical direction, similarity1)
184
229
  expect(results[0].key).toBe("x-axis");
185
- expect(results[0].distance).toBeCloseTo(0.0, 2);
230
+ expect(results[0].similarity).toBeCloseTo(1.0, 2);
186
231
 
187
232
  // xy-axis should be second (partially aligned)
188
233
  expect(results[1].key).toBe("xy-axis");
189
- expect(results[1].distance).toBeLessThan(1);
234
+ expect(results[1].similarity).toBeGreaterThan(0);
190
235
 
191
- // y-axis should be last (orthogonal, distance1)
236
+ // y-axis should be last (orthogonal, similarity0)
192
237
  expect(results[2].key).toBe("y-axis");
193
- expect(results[2].distance).toBeCloseTo(1.0, 2);
238
+ expect(results[2].similarity).toBeCloseTo(0.0, 2);
194
239
  });
195
240
 
196
241
  it("query respects topK option", async () => {
@@ -251,7 +296,7 @@ describe("VectorDB", () => {
251
296
  expect(all).toHaveLength(5);
252
297
 
253
298
  // Partial iteration (simulate pagination)
254
- const page: { key: string; distance: number }[] = [];
299
+ const page: { key: string; similarity: number }[] = [];
255
300
  for (const item of results) {
256
301
  page.push(item);
257
302
  if (page.length === 2) break;
@@ -273,13 +318,13 @@ describe("VectorDB", () => {
273
318
  db.set("point", [0, 1, 0, 0]);
274
319
 
275
320
  const results = db.query([0, 1, 0, 0]);
276
- // Both 'point' and 'other' are now along y-axis, so both should have distance0
277
- expect(results[0].distance).toBeCloseTo(0.0, 2);
278
- expect(results[1].distance).toBeCloseTo(0.0, 2);
321
+ // Both 'point' and 'other' are now along y-axis, so both should have similarity1
322
+ expect(results[0].similarity).toBeCloseTo(1.0, 2);
323
+ expect(results[1].similarity).toBeCloseTo(1.0, 2);
279
324
  expect(db.size).toBe(2);
280
325
  });
281
326
 
282
- it("query respects maxDistance option", async () => {
327
+ it("query respects minSimilarity option", async () => {
283
328
  const db = await VectorDB.open({
284
329
  dimensions: 4,
285
330
  storage,
@@ -290,16 +335,16 @@ describe("VectorDB", () => {
290
335
  db.set("y-axis", [0, 1, 0, 0]);
291
336
  db.set("xy-axis", [1, 1, 0, 0]);
292
337
 
293
- // Only return results with distance 0.5 from the x-axis query
294
- const results = db.query([1, 0, 0, 0], { maxDistance: 0.5 });
295
- // x-axis: distance0, xy-axis: distance ≈ 0.29
296
- // y-axis: distance1 (excluded)
338
+ // Only return results with similarity 0.5 from the x-axis query
339
+ const results = db.query([1, 0, 0, 0], { minSimilarity: 0.5 });
340
+ // x-axis: similarity1, xy-axis: similarity ≈ 0.71
341
+ // y-axis: similarity0 (excluded)
297
342
  expect(results.length).toBe(2);
298
343
  expect(results[0].key).toBe("x-axis");
299
344
  expect(results[1].key).toBe("xy-axis");
300
345
  });
301
346
 
302
- it("query maxDistance works with iterable mode", async () => {
347
+ it("query minSimilarity works with iterable mode", async () => {
303
348
  const db = await VectorDB.open({
304
349
  dimensions: 4,
305
350
  storage,
@@ -310,7 +355,7 @@ describe("VectorDB", () => {
310
355
  db.set("y-axis", [0, 1, 0, 0]);
311
356
  db.set("xy-axis", [1, 1, 0, 0]);
312
357
 
313
- const results = [...db.query([1, 0, 0, 0], { maxDistance: 0.5, iterable: true })];
358
+ const results = [...db.query([1, 0, 0, 0], { minSimilarity: 0.5, iterable: true })];
314
359
  expect(results.length).toBe(2);
315
360
  expect(results[0].key).toBe("x-axis");
316
361
  expect(results[1].key).toBe("xy-axis");
@@ -491,6 +536,334 @@ describe("VectorDB", () => {
491
536
  const result = db.get("a");
492
537
  expect(result![0]).toBeCloseTo(1.0);
493
538
  });
539
+
540
+ // --- export and import (streaming) ---
541
+ it("export returns a readable stream of the database", async () => {
542
+ const db = await VectorDB.open({
543
+ dimensions: 4,
544
+ normalize: false,
545
+ storage,
546
+ wasmBinary,
547
+ });
548
+
549
+ db.set("a", [1, 2, 3, 4]);
550
+ db.set("b", [5, 6, 7, 8]);
551
+
552
+ const stream = await db.export();
553
+ expect(stream).toBeInstanceOf(ReadableStream);
554
+
555
+ const bytes = await streamToBytes(stream);
556
+ expect(bytes.byteLength).toBeGreaterThan(0);
557
+ });
558
+
559
+ it("export of empty database returns a valid stream", async () => {
560
+ const db = await VectorDB.open({
561
+ dimensions: 4,
562
+ normalize: false,
563
+ storage,
564
+ wasmBinary,
565
+ });
566
+
567
+ const stream = await db.export();
568
+ const bytes = await streamToBytes(stream);
569
+ expect(bytes.byteLength).toBeGreaterThan(0);
570
+ });
571
+
572
+ it("import restores data from an exported stream", async () => {
573
+ const db1 = await VectorDB.open({
574
+ dimensions: 4,
575
+ normalize: false,
576
+ storage,
577
+ wasmBinary,
578
+ });
579
+
580
+ db1.set("alpha", [1, 0, 0, 0]);
581
+ db1.set("beta", [0, 1, 0, 0]);
582
+
583
+ const stream = await db1.export();
584
+
585
+ // Import into a fresh database
586
+ const storage2 = new InMemoryStorageProvider();
587
+ const db2 = await VectorDB.open({
588
+ dimensions: 4,
589
+ normalize: false,
590
+ storage: storage2,
591
+ wasmBinary,
592
+ });
593
+
594
+ await db2.import(stream);
595
+ expect(db2.size).toBe(2);
596
+ expect(db2.get("alpha")![0]).toBeCloseTo(1);
597
+ expect(db2.get("beta")![1]).toBeCloseTo(1);
598
+ });
599
+
600
+ it("import overrides existing data", async () => {
601
+ const db1 = await VectorDB.open({
602
+ dimensions: 4,
603
+ normalize: false,
604
+ storage,
605
+ wasmBinary,
606
+ });
607
+
608
+ db1.set("a", [1, 0, 0, 0]);
609
+ const stream = await db1.export();
610
+
611
+ // Create db2 with different data
612
+ const storage2 = new InMemoryStorageProvider();
613
+ const db2 = await VectorDB.open({
614
+ dimensions: 4,
615
+ normalize: false,
616
+ storage: storage2,
617
+ wasmBinary,
618
+ });
619
+
620
+ db2.set("x", [0, 0, 0, 1]);
621
+ db2.set("y", [0, 0, 1, 0]);
622
+ expect(db2.size).toBe(2);
623
+
624
+ await db2.import(stream);
625
+ expect(db2.size).toBe(1);
626
+ expect(db2.get("a")![0]).toBeCloseTo(1);
627
+ expect(db2.get("x")).toBeUndefined();
628
+ expect(db2.get("y")).toBeUndefined();
629
+ });
630
+
631
+ it("import throws on dimension mismatch", async () => {
632
+ const db1 = await VectorDB.open({
633
+ dimensions: 4,
634
+ normalize: false,
635
+ storage,
636
+ wasmBinary,
637
+ });
638
+
639
+ db1.set("a", [1, 0, 0, 0]);
640
+ const bytes = await streamToBytes(await db1.export());
641
+
642
+ const storage2 = new InMemoryStorageProvider();
643
+ const db2 = await VectorDB.open({
644
+ dimensions: 8,
645
+ normalize: false,
646
+ storage: storage2,
647
+ wasmBinary,
648
+ });
649
+
650
+ await expect(db2.import(bytesToStream(bytes))).rejects.toThrow("dimension");
651
+ });
652
+
653
+ it("import of empty export clears the database", async () => {
654
+ const db1 = await VectorDB.open({
655
+ dimensions: 4,
656
+ normalize: false,
657
+ storage,
658
+ wasmBinary,
659
+ });
660
+
661
+ const stream = await db1.export();
662
+
663
+ const storage2 = new InMemoryStorageProvider();
664
+ const db2 = await VectorDB.open({
665
+ dimensions: 4,
666
+ normalize: false,
667
+ storage: storage2,
668
+ wasmBinary,
669
+ });
670
+
671
+ db2.set("existing", [1, 0, 0, 0]);
672
+ await db2.import(stream);
673
+ expect(db2.size).toBe(0);
674
+ });
675
+
676
+ it("imported data is queryable", async () => {
677
+ const db1 = await VectorDB.open({
678
+ dimensions: 4,
679
+ storage,
680
+ wasmBinary,
681
+ });
682
+
683
+ db1.set("x-axis", [1, 0, 0, 0]);
684
+ db1.set("y-axis", [0, 1, 0, 0]);
685
+ db1.set("xy-axis", [1, 1, 0, 0]);
686
+
687
+ const stream = await db1.export();
688
+
689
+ const storage2 = new InMemoryStorageProvider();
690
+ const db2 = await VectorDB.open({
691
+ dimensions: 4,
692
+ storage: storage2,
693
+ wasmBinary,
694
+ });
695
+
696
+ await db2.import(stream);
697
+
698
+ const results = db2.query([1, 0, 0, 0]);
699
+ expect(results.length).toBe(3);
700
+ expect(results[0].key).toBe("x-axis");
701
+ expect(results[0].similarity).toBeCloseTo(1.0, 2);
702
+ });
703
+
704
+ it("export and import preserve data after set operations on imported db", async () => {
705
+ const db1 = await VectorDB.open({
706
+ dimensions: 4,
707
+ normalize: false,
708
+ storage,
709
+ wasmBinary,
710
+ });
711
+
712
+ db1.set("a", [1, 0, 0, 0]);
713
+ const stream = await db1.export();
714
+
715
+ const storage2 = new InMemoryStorageProvider();
716
+ const db2 = await VectorDB.open({
717
+ dimensions: 4,
718
+ normalize: false,
719
+ storage: storage2,
720
+ wasmBinary,
721
+ });
722
+
723
+ await db2.import(stream);
724
+ db2.set("b", [0, 1, 0, 0]);
725
+
726
+ expect(db2.size).toBe(2);
727
+ expect(db2.get("a")![0]).toBeCloseTo(1);
728
+ expect(db2.get("b")![1]).toBeCloseTo(1);
729
+ });
730
+
731
+ it("export throws on closed database", async () => {
732
+ const db = await VectorDB.open({
733
+ dimensions: 4,
734
+ storage,
735
+ wasmBinary,
736
+ });
737
+
738
+ await db.close();
739
+ await expect(db.export()).rejects.toThrow("closed");
740
+ });
741
+
742
+ it("import throws on closed database", async () => {
743
+ const db1 = await VectorDB.open({
744
+ dimensions: 4,
745
+ storage,
746
+ wasmBinary,
747
+ });
748
+
749
+ const stream = await db1.export();
750
+
751
+ const storage2 = new InMemoryStorageProvider();
752
+ const db2 = await VectorDB.open({
753
+ dimensions: 4,
754
+ storage: storage2,
755
+ wasmBinary,
756
+ });
757
+
758
+ await db2.close();
759
+ await expect(db2.import(stream)).rejects.toThrow("closed");
760
+ });
761
+
762
+ it("import throws on invalid stream (bad magic)", async () => {
763
+ const db = await VectorDB.open({
764
+ dimensions: 4,
765
+ storage,
766
+ wasmBinary,
767
+ });
768
+
769
+ const badBlob = new Uint8Array(24);
770
+ await expect(db.import(bytesToStream(badBlob))).rejects.toThrow();
771
+ });
772
+
773
+ it("import throws on stream too short for header", async () => {
774
+ const db = await VectorDB.open({
775
+ dimensions: 4,
776
+ storage,
777
+ wasmBinary,
778
+ });
779
+
780
+ const tooShort = new Uint8Array(10);
781
+ await expect(db.import(bytesToStream(tooShort))).rejects.toThrow();
782
+ });
783
+
784
+ it("import throws on truncated stream body", async () => {
785
+ const db1 = await VectorDB.open({
786
+ dimensions: 4,
787
+ normalize: false,
788
+ storage,
789
+ wasmBinary,
790
+ });
791
+
792
+ db1.set("a", [1, 0, 0, 0]);
793
+ const bytes = await streamToBytes(await db1.export());
794
+
795
+ // Truncate the blob to have valid header but incomplete body
796
+ const truncated = bytes.slice(0, 25);
797
+
798
+ const storage2 = new InMemoryStorageProvider();
799
+ const db2 = await VectorDB.open({
800
+ dimensions: 4,
801
+ normalize: false,
802
+ storage: storage2,
803
+ wasmBinary,
804
+ });
805
+
806
+ await expect(db2.import(bytesToStream(truncated))).rejects.toThrow("unexpected end of stream");
807
+ });
808
+
809
+ it("import works correctly with chunked stream (small chunks)", async () => {
810
+ const db1 = await VectorDB.open({
811
+ dimensions: 4,
812
+ normalize: false,
813
+ storage,
814
+ wasmBinary,
815
+ });
816
+
817
+ db1.set("alpha", [1, 0, 0, 0]);
818
+ db1.set("beta", [0, 1, 0, 0]);
819
+ db1.set("gamma", [0, 0, 1, 0]);
820
+
821
+ const bytes = await streamToBytes(await db1.export());
822
+
823
+ // Feed it back as a stream with tiny 7-byte chunks (crosses header/vector/key boundaries)
824
+ const chunkedStream = bytesToChunkedStream(bytes, 7);
825
+
826
+ const storage2 = new InMemoryStorageProvider();
827
+ const db2 = await VectorDB.open({
828
+ dimensions: 4,
829
+ normalize: false,
830
+ storage: storage2,
831
+ wasmBinary,
832
+ });
833
+
834
+ await db2.import(chunkedStream);
835
+ expect(db2.size).toBe(3);
836
+ expect(db2.get("alpha")![0]).toBeCloseTo(1);
837
+ expect(db2.get("beta")![1]).toBeCloseTo(1);
838
+ expect(db2.get("gamma")![2]).toBeCloseTo(1);
839
+ });
840
+
841
+ it("import works correctly with single-byte stream chunks", async () => {
842
+ const db1 = await VectorDB.open({
843
+ dimensions: 4,
844
+ normalize: false,
845
+ storage,
846
+ wasmBinary,
847
+ });
848
+
849
+ db1.set("k", [1, 2, 3, 4]);
850
+
851
+ const bytes = await streamToBytes(await db1.export());
852
+ const chunkedStream = bytesToChunkedStream(bytes, 1);
853
+
854
+ const storage2 = new InMemoryStorageProvider();
855
+ const db2 = await VectorDB.open({
856
+ dimensions: 4,
857
+ normalize: false,
858
+ storage: storage2,
859
+ wasmBinary,
860
+ });
861
+
862
+ await db2.import(chunkedStream);
863
+ expect(db2.size).toBe(1);
864
+ expect(db2.get("k")![0]).toBeCloseTo(1);
865
+ expect(db2.get("k")![3]).toBeCloseTo(4);
866
+ });
494
867
  }
495
868
 
496
869
  it("throws VectorCapacityExceededError when full", async () => {
@@ -498,4 +871,44 @@ describe("VectorDB", () => {
498
871
  expect(err).toBeInstanceOf(VectorCapacityExceededError);
499
872
  expect(err.message).toContain("100");
500
873
  });
874
+
875
+ // --- storage decoupling ---
876
+ it("defaults to in-memory storage when no storage is provided", async () => {
877
+ const db = await VectorDB.open({
878
+ dimensions: 4,
879
+ wasmBinary: null,
880
+ });
881
+
882
+ db.set("a", [1, 0, 0, 0]);
883
+ expect(db.size).toBe(1);
884
+ expect(db.get("a")).toBeDefined();
885
+
886
+ // Flush should succeed with default in-memory storage
887
+ await db.flush();
888
+ await db.close();
889
+ });
890
+
891
+ it("accepts an explicit storage provider via options", async () => {
892
+ const customStorage = new InMemoryStorageProvider();
893
+ const db = await VectorDB.open({
894
+ dimensions: 4,
895
+ normalize: false,
896
+ storage: customStorage,
897
+ wasmBinary: null,
898
+ });
899
+
900
+ db.set("key1", [1, 2, 3, 4]);
901
+ await db.flush();
902
+
903
+ // Reopen with the same storage to verify persistence
904
+ const db2 = await VectorDB.open({
905
+ dimensions: 4,
906
+ normalize: false,
907
+ storage: customStorage,
908
+ wasmBinary: null,
909
+ });
910
+
911
+ expect(db2.size).toBe(1);
912
+ expect(db2.get("key1")![0]).toBeCloseTo(1);
913
+ });
501
914
  });
@@ -144,4 +144,12 @@ export class MemoryManager {
144
144
  reset(): void {
145
145
  this._vectorCount = 0;
146
146
  }
147
+
148
+ /**
149
+ * Set the vector count after bulk-loading data directly into the DB region.
150
+ * Use when writing raw bytes to the DB region externally (e.g., streaming import).
151
+ */
152
+ setVectorCount(count: number): void {
153
+ this._vectorCount = count;
154
+ }
147
155
  }