eigen-db 4.2.0 → 4.4.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.
- package/CHANGELOG.md +8 -0
- package/README.md +55 -8
- package/dist/compute.d.ts +20 -0
- package/dist/eigen-db.js +242 -190
- package/dist/eigen-db.js.map +1 -1
- package/dist/eigen-db.umd.cjs +1 -1
- package/dist/eigen-db.umd.cjs.map +1 -1
- package/dist/errors.d.ts +7 -0
- package/dist/index.d.ts +12 -0
- package/dist/lexicon.d.ts +28 -0
- package/dist/memory-manager.d.ts +68 -0
- package/dist/result-set.d.ts +35 -0
- package/dist/simd-binary.d.ts +1 -0
- package/dist/storage.d.ts +38 -0
- package/dist/types.d.ts +44 -0
- package/dist/vector-db.d.ts +131 -0
- package/dist/wasm-compute.d.ts +13 -0
- package/package.json +4 -4
- package/src/lib/__tests__/vector-db.test.ts +328 -0
- package/src/lib/simd-binary.ts +1 -1
- package/src/lib/simd-optimized.wat +362 -0
- package/src/lib/simd.wat +42 -248
- package/src/lib/types.ts +2 -4
- package/src/lib/vector-db.ts +93 -19
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VectorDB — Key-Value Vector Database
|
|
3
|
+
*
|
|
4
|
+
* Decoupled from embedding providers. Users pass pre-computed vectors
|
|
5
|
+
* as number arrays (or Float32Array) with string keys.
|
|
6
|
+
*
|
|
7
|
+
* Supports:
|
|
8
|
+
* - set/get/setMany/getMany for key-value CRUD
|
|
9
|
+
* - query for similarity search (dot product on normalized vectors)
|
|
10
|
+
* - flush to persist, close to flush+release, clear to wipe
|
|
11
|
+
* - Streaming export/import using Web Streams API
|
|
12
|
+
* - Last-write-wins semantics for duplicate keys (append-only storage)
|
|
13
|
+
*/
|
|
14
|
+
import type { ResultItem } from "./result-set";
|
|
15
|
+
import type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions, VectorInput } from "./types";
|
|
16
|
+
export declare class VectorDB {
|
|
17
|
+
private readonly memoryManager;
|
|
18
|
+
private readonly storage;
|
|
19
|
+
private readonly _dimensions;
|
|
20
|
+
private readonly shouldNormalize;
|
|
21
|
+
private wasmExports;
|
|
22
|
+
/** Maps key to its slot index in the vector array */
|
|
23
|
+
private keyToSlot;
|
|
24
|
+
/** Maps slot index back to its key */
|
|
25
|
+
private slotToKey;
|
|
26
|
+
/** Whether this instance has been closed */
|
|
27
|
+
private closed;
|
|
28
|
+
private constructor();
|
|
29
|
+
/**
|
|
30
|
+
* Opens a VectorDB instance.
|
|
31
|
+
* Loads existing data from storage into WASM memory.
|
|
32
|
+
*/
|
|
33
|
+
static open(options: OpenOptions): Promise<VectorDB>;
|
|
34
|
+
static open(options: OpenOptionsInternal): Promise<VectorDB>;
|
|
35
|
+
/** Total number of key-value pairs in the database */
|
|
36
|
+
get size(): number;
|
|
37
|
+
/** Number of dimensions per vector */
|
|
38
|
+
get dimensions(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Check whether a key exists in the database.
|
|
41
|
+
* Uses the internal key-to-slot map for O(1) lookup.
|
|
42
|
+
*/
|
|
43
|
+
has(key: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Delete an entry by key. Returns true if the key existed, false otherwise.
|
|
46
|
+
* Uses swap-and-pop to avoid gaps in the underlying vector array.
|
|
47
|
+
*/
|
|
48
|
+
delete(key: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Returns an iterable of all keys in the database.
|
|
51
|
+
*/
|
|
52
|
+
keys(): IterableIterator<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Returns an iterable of [key, value] pairs.
|
|
55
|
+
* Values are returned as plain number array copies.
|
|
56
|
+
*/
|
|
57
|
+
entries(): IterableIterator<[string, number[]]>;
|
|
58
|
+
/**
|
|
59
|
+
* Implements the iterable protocol. Same as entries().
|
|
60
|
+
*/
|
|
61
|
+
[Symbol.iterator](): IterableIterator<[string, number[]]>;
|
|
62
|
+
/**
|
|
63
|
+
* Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).
|
|
64
|
+
* The value is a number[] or Float32Array of length equal to the configured dimensions.
|
|
65
|
+
*/
|
|
66
|
+
set(key: string, value: VectorInput, options?: SetOptions): void;
|
|
67
|
+
/**
|
|
68
|
+
* Get the stored vector for a key. Returns undefined if the key does not exist.
|
|
69
|
+
* Returns a copy of the stored vector as a plain number array.
|
|
70
|
+
*/
|
|
71
|
+
get(key: string): number[] | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Set multiple key-value pairs at once. Last-write-wins applies within the batch.
|
|
74
|
+
*/
|
|
75
|
+
setMany(entries: [string, VectorInput][]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Get vectors for multiple keys. Returns undefined for keys that don't exist.
|
|
78
|
+
*/
|
|
79
|
+
getMany(keys: string[]): (number[] | undefined)[];
|
|
80
|
+
/**
|
|
81
|
+
* Search for the most similar vectors to the given query vector.
|
|
82
|
+
*
|
|
83
|
+
* Default: returns a plain ResultItem[] sorted by descending similarity.
|
|
84
|
+
* With `{ iterable: true }`: returns a lazy Iterable<ResultItem> where keys
|
|
85
|
+
* are resolved only as each item is consumed.
|
|
86
|
+
*
|
|
87
|
+
* Similarity is the dot product of query and stored vectors. With
|
|
88
|
+
* normalization (default), this equals cosine similarity: 1 = identical,
|
|
89
|
+
* -1 = opposite.
|
|
90
|
+
*/
|
|
91
|
+
query(value: VectorInput, options: QueryOptions & {
|
|
92
|
+
iterable: true;
|
|
93
|
+
}): Iterable<ResultItem>;
|
|
94
|
+
query(value: VectorInput, options?: QueryOptions): ResultItem[];
|
|
95
|
+
/**
|
|
96
|
+
* Persist the current in-memory state to storage.
|
|
97
|
+
*/
|
|
98
|
+
flush(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Flush data to storage and release the instance.
|
|
101
|
+
* The instance cannot be used after close.
|
|
102
|
+
*/
|
|
103
|
+
close(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Clear all data from the database and storage.
|
|
106
|
+
*/
|
|
107
|
+
clear(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Export the entire database as a ReadableStream of binary chunks.
|
|
110
|
+
*
|
|
111
|
+
* Format: [Header 24 bytes][Vector data][Keys data]
|
|
112
|
+
* Header: magic(4) + version(4) + dimensions(4) + vectorCount(4) + vectorDataLen(4) + keysDataLen(4)
|
|
113
|
+
*
|
|
114
|
+
* Vectors are streamed in 64KB chunks from WASM memory to avoid large
|
|
115
|
+
* heap allocations.
|
|
116
|
+
*/
|
|
117
|
+
export(): Promise<ReadableStream<Uint8Array>>;
|
|
118
|
+
/**
|
|
119
|
+
* Import data from a ReadableStream, replacing all existing data.
|
|
120
|
+
* Performs a dimension check against the configured dimensions.
|
|
121
|
+
*
|
|
122
|
+
* Vectors are streamed directly into WASM memory in chunks to avoid
|
|
123
|
+
* large heap allocations.
|
|
124
|
+
*/
|
|
125
|
+
import(stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Normalize a vector using WASM (if available) or JS fallback.
|
|
128
|
+
*/
|
|
129
|
+
private normalizeVector;
|
|
130
|
+
private assertOpen;
|
|
131
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WASM SIMD compute layer.
|
|
3
|
+
* Compiles the hand-written WAT module and provides typed wrappers
|
|
4
|
+
* that operate on shared WebAssembly.Memory.
|
|
5
|
+
*/
|
|
6
|
+
export interface WasmExports {
|
|
7
|
+
normalize(ptr: number, dimensions: number): void;
|
|
8
|
+
search_all(queryPtr: number, dbPtr: number, scoresPtr: number, dbSize: number, dimensions: number): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Instantiates a WASM module with the given memory and returns typed exports.
|
|
12
|
+
*/
|
|
13
|
+
export declare function instantiateWasm(wasmBinary: Uint8Array, memory: WebAssembly.Memory): Promise<WasmExports>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eigen-db",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"module": "./dist/eigen-db.js",
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
15
|
-
"types": "./
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
16
|
"import": "./dist/eigen-db.js",
|
|
17
17
|
"require": "./dist/eigen-db.umd.cjs"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
"types": "./
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
21
|
"scripts": {
|
|
22
22
|
"dev": "vite",
|
|
23
23
|
"compile-wat": "tsx scripts/compile-wat.ts",
|
|
24
|
-
"build": "npm run compile-wat && tsc && vite build",
|
|
24
|
+
"build": "npm run compile-wat && tsc && vite build && tsc -p tsconfig.build.json",
|
|
25
25
|
"preview": "vite preview",
|
|
26
26
|
"test": "vitest run",
|
|
27
27
|
"test:watch": "vitest",
|
|
@@ -94,6 +94,16 @@ describe("VectorDB", () => {
|
|
|
94
94
|
expect(db.size).toBe(0);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
it("exposes dimensions property", async () => {
|
|
98
|
+
const db = await VectorDB.open({
|
|
99
|
+
dimensions: 4,
|
|
100
|
+
storage,
|
|
101
|
+
wasmBinary,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(db.dimensions).toBe(4);
|
|
105
|
+
});
|
|
106
|
+
|
|
97
107
|
// --- set and get ---
|
|
98
108
|
it("stores and retrieves a vector by key", async () => {
|
|
99
109
|
const db = await VectorDB.open({
|
|
@@ -838,6 +848,284 @@ describe("VectorDB", () => {
|
|
|
838
848
|
expect(db2.get("gamma")![2]).toBeCloseTo(1);
|
|
839
849
|
});
|
|
840
850
|
|
|
851
|
+
// --- has ---
|
|
852
|
+
it("has returns true for existing key", async () => {
|
|
853
|
+
const db = await VectorDB.open({
|
|
854
|
+
dimensions: 4,
|
|
855
|
+
normalize: false,
|
|
856
|
+
storage,
|
|
857
|
+
wasmBinary,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
db.set("a", [1, 0, 0, 0]);
|
|
861
|
+
expect(db.has("a")).toBe(true);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it("has returns false for non-existent key", async () => {
|
|
865
|
+
const db = await VectorDB.open({
|
|
866
|
+
dimensions: 4,
|
|
867
|
+
storage,
|
|
868
|
+
wasmBinary,
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
expect(db.has("nonexistent")).toBe(false);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
it("has throws on closed database", async () => {
|
|
875
|
+
const db = await VectorDB.open({
|
|
876
|
+
dimensions: 4,
|
|
877
|
+
storage,
|
|
878
|
+
wasmBinary,
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
await db.close();
|
|
882
|
+
expect(() => db.has("a")).toThrow("closed");
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
// --- delete ---
|
|
886
|
+
it("delete removes an existing entry and returns true", async () => {
|
|
887
|
+
const db = await VectorDB.open({
|
|
888
|
+
dimensions: 4,
|
|
889
|
+
normalize: false,
|
|
890
|
+
storage,
|
|
891
|
+
wasmBinary,
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
db.set("a", [1, 0, 0, 0]);
|
|
895
|
+
db.set("b", [0, 1, 0, 0]);
|
|
896
|
+
expect(db.size).toBe(2);
|
|
897
|
+
|
|
898
|
+
const result = db.delete("a");
|
|
899
|
+
expect(result).toBe(true);
|
|
900
|
+
expect(db.size).toBe(1);
|
|
901
|
+
expect(db.get("a")).toBeUndefined();
|
|
902
|
+
expect(db.has("a")).toBe(false);
|
|
903
|
+
expect(db.get("b")).toBeDefined();
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it("delete returns false for non-existent key", async () => {
|
|
907
|
+
const db = await VectorDB.open({
|
|
908
|
+
dimensions: 4,
|
|
909
|
+
storage,
|
|
910
|
+
wasmBinary,
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
expect(db.delete("nonexistent")).toBe(false);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it("delete last entry leaves empty database", async () => {
|
|
917
|
+
const db = await VectorDB.open({
|
|
918
|
+
dimensions: 4,
|
|
919
|
+
normalize: false,
|
|
920
|
+
storage,
|
|
921
|
+
wasmBinary,
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
db.set("only", [1, 2, 3, 4]);
|
|
925
|
+
db.delete("only");
|
|
926
|
+
expect(db.size).toBe(0);
|
|
927
|
+
expect(db.get("only")).toBeUndefined();
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it("delete preserves remaining entries and query works", async () => {
|
|
931
|
+
const db = await VectorDB.open({
|
|
932
|
+
dimensions: 4,
|
|
933
|
+
storage,
|
|
934
|
+
wasmBinary,
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
db.set("x-axis", [1, 0, 0, 0]);
|
|
938
|
+
db.set("y-axis", [0, 1, 0, 0]);
|
|
939
|
+
db.set("z-axis", [0, 0, 1, 0]);
|
|
940
|
+
|
|
941
|
+
db.delete("y-axis");
|
|
942
|
+
expect(db.size).toBe(2);
|
|
943
|
+
|
|
944
|
+
const results = db.query([1, 0, 0, 0]);
|
|
945
|
+
expect(results.length).toBe(2);
|
|
946
|
+
expect(results[0].key).toBe("x-axis");
|
|
947
|
+
expect(results.find((r) => r.key === "y-axis")).toBeUndefined();
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it("delete then set reuses the database correctly", async () => {
|
|
951
|
+
const db = await VectorDB.open({
|
|
952
|
+
dimensions: 4,
|
|
953
|
+
normalize: false,
|
|
954
|
+
storage,
|
|
955
|
+
wasmBinary,
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
db.set("a", [1, 0, 0, 0]);
|
|
959
|
+
db.set("b", [0, 1, 0, 0]);
|
|
960
|
+
db.delete("a");
|
|
961
|
+
|
|
962
|
+
db.set("c", [0, 0, 1, 0]);
|
|
963
|
+
expect(db.size).toBe(2);
|
|
964
|
+
expect(db.get("a")).toBeUndefined();
|
|
965
|
+
expect(db.get("b")![1]).toBeCloseTo(1);
|
|
966
|
+
expect(db.get("c")![2]).toBeCloseTo(1);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
it("delete persists correctly after flush", async () => {
|
|
970
|
+
const db1 = await VectorDB.open({
|
|
971
|
+
dimensions: 4,
|
|
972
|
+
normalize: false,
|
|
973
|
+
storage,
|
|
974
|
+
wasmBinary,
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
db1.set("a", [1, 0, 0, 0]);
|
|
978
|
+
db1.set("b", [0, 1, 0, 0]);
|
|
979
|
+
db1.delete("a");
|
|
980
|
+
await db1.flush();
|
|
981
|
+
|
|
982
|
+
const db2 = await VectorDB.open({
|
|
983
|
+
dimensions: 4,
|
|
984
|
+
normalize: false,
|
|
985
|
+
storage,
|
|
986
|
+
wasmBinary,
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
expect(db2.size).toBe(1);
|
|
990
|
+
expect(db2.get("a")).toBeUndefined();
|
|
991
|
+
expect(db2.get("b")![1]).toBeCloseTo(1);
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it("delete throws on closed database", async () => {
|
|
995
|
+
const db = await VectorDB.open({
|
|
996
|
+
dimensions: 4,
|
|
997
|
+
storage,
|
|
998
|
+
wasmBinary,
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
await db.close();
|
|
1002
|
+
expect(() => db.delete("a")).toThrow("closed");
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
// --- keys ---
|
|
1006
|
+
it("keys returns an iterable of all keys", async () => {
|
|
1007
|
+
const db = await VectorDB.open({
|
|
1008
|
+
dimensions: 4,
|
|
1009
|
+
normalize: false,
|
|
1010
|
+
storage,
|
|
1011
|
+
wasmBinary,
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
db.set("a", [1, 0, 0, 0]);
|
|
1015
|
+
db.set("b", [0, 1, 0, 0]);
|
|
1016
|
+
db.set("c", [0, 0, 1, 0]);
|
|
1017
|
+
|
|
1018
|
+
const keys = [...db.keys()];
|
|
1019
|
+
expect(keys).toHaveLength(3);
|
|
1020
|
+
expect(keys).toContain("a");
|
|
1021
|
+
expect(keys).toContain("b");
|
|
1022
|
+
expect(keys).toContain("c");
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
it("keys returns empty iterable for empty database", async () => {
|
|
1026
|
+
const db = await VectorDB.open({
|
|
1027
|
+
dimensions: 4,
|
|
1028
|
+
storage,
|
|
1029
|
+
wasmBinary,
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
expect([...db.keys()]).toEqual([]);
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
it("keys throws on closed database", async () => {
|
|
1036
|
+
const db = await VectorDB.open({
|
|
1037
|
+
dimensions: 4,
|
|
1038
|
+
storage,
|
|
1039
|
+
wasmBinary,
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
await db.close();
|
|
1043
|
+
expect(() => db.keys()).toThrow("closed");
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// --- entries ---
|
|
1047
|
+
it("entries returns an iterable of [key, value] pairs", async () => {
|
|
1048
|
+
const db = await VectorDB.open({
|
|
1049
|
+
dimensions: 4,
|
|
1050
|
+
normalize: false,
|
|
1051
|
+
storage,
|
|
1052
|
+
wasmBinary,
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
db.set("a", [1, 0, 0, 0]);
|
|
1056
|
+
db.set("b", [0, 1, 0, 0]);
|
|
1057
|
+
|
|
1058
|
+
const entries = [...db.entries()];
|
|
1059
|
+
expect(entries).toHaveLength(2);
|
|
1060
|
+
|
|
1061
|
+
const aEntry = entries.find(([key]) => key === "a");
|
|
1062
|
+
expect(aEntry).toBeDefined();
|
|
1063
|
+
expect(aEntry![1][0]).toBeCloseTo(1);
|
|
1064
|
+
|
|
1065
|
+
const bEntry = entries.find(([key]) => key === "b");
|
|
1066
|
+
expect(bEntry).toBeDefined();
|
|
1067
|
+
expect(bEntry![1][1]).toBeCloseTo(1);
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
it("entries returns empty iterable for empty database", async () => {
|
|
1071
|
+
const db = await VectorDB.open({
|
|
1072
|
+
dimensions: 4,
|
|
1073
|
+
storage,
|
|
1074
|
+
wasmBinary,
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
expect([...db.entries()]).toEqual([]);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it("entries throws on closed database", async () => {
|
|
1081
|
+
const db = await VectorDB.open({
|
|
1082
|
+
dimensions: 4,
|
|
1083
|
+
storage,
|
|
1084
|
+
wasmBinary,
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
await db.close();
|
|
1088
|
+
expect(() => db.entries()).toThrow("closed");
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
// --- Symbol.iterator ---
|
|
1092
|
+
it("supports spread operator via Symbol.iterator", async () => {
|
|
1093
|
+
const db = await VectorDB.open({
|
|
1094
|
+
dimensions: 4,
|
|
1095
|
+
normalize: false,
|
|
1096
|
+
storage,
|
|
1097
|
+
wasmBinary,
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
db.set("a", [1, 0, 0, 0]);
|
|
1101
|
+
db.set("b", [0, 1, 0, 0]);
|
|
1102
|
+
|
|
1103
|
+
const spread = [...db];
|
|
1104
|
+
expect(spread).toHaveLength(2);
|
|
1105
|
+
|
|
1106
|
+
// Same as entries()
|
|
1107
|
+
const entries = [...db.entries()];
|
|
1108
|
+
expect(spread).toEqual(entries);
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
it("supports for-of iteration", async () => {
|
|
1112
|
+
const db = await VectorDB.open({
|
|
1113
|
+
dimensions: 4,
|
|
1114
|
+
normalize: false,
|
|
1115
|
+
storage,
|
|
1116
|
+
wasmBinary,
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
db.set("a", [1, 0, 0, 0]);
|
|
1120
|
+
db.set("b", [0, 1, 0, 0]);
|
|
1121
|
+
|
|
1122
|
+
const collected: [string, number[]][] = [];
|
|
1123
|
+
for (const entry of db) {
|
|
1124
|
+
collected.push(entry);
|
|
1125
|
+
}
|
|
1126
|
+
expect(collected).toHaveLength(2);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
841
1129
|
it("import works correctly with single-byte stream chunks", async () => {
|
|
842
1130
|
const db1 = await VectorDB.open({
|
|
843
1131
|
dimensions: 4,
|
|
@@ -871,4 +1159,44 @@ describe("VectorDB", () => {
|
|
|
871
1159
|
expect(err).toBeInstanceOf(VectorCapacityExceededError);
|
|
872
1160
|
expect(err.message).toContain("100");
|
|
873
1161
|
});
|
|
1162
|
+
|
|
1163
|
+
// --- storage decoupling ---
|
|
1164
|
+
it("defaults to in-memory storage when no storage is provided", async () => {
|
|
1165
|
+
const db = await VectorDB.open({
|
|
1166
|
+
dimensions: 4,
|
|
1167
|
+
wasmBinary: null,
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
db.set("a", [1, 0, 0, 0]);
|
|
1171
|
+
expect(db.size).toBe(1);
|
|
1172
|
+
expect(db.get("a")).toBeDefined();
|
|
1173
|
+
|
|
1174
|
+
// Flush should succeed with default in-memory storage
|
|
1175
|
+
await db.flush();
|
|
1176
|
+
await db.close();
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
it("accepts an explicit storage provider via options", async () => {
|
|
1180
|
+
const customStorage = new InMemoryStorageProvider();
|
|
1181
|
+
const db = await VectorDB.open({
|
|
1182
|
+
dimensions: 4,
|
|
1183
|
+
normalize: false,
|
|
1184
|
+
storage: customStorage,
|
|
1185
|
+
wasmBinary: null,
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
db.set("key1", [1, 2, 3, 4]);
|
|
1189
|
+
await db.flush();
|
|
1190
|
+
|
|
1191
|
+
// Reopen with the same storage to verify persistence
|
|
1192
|
+
const db2 = await VectorDB.open({
|
|
1193
|
+
dimensions: 4,
|
|
1194
|
+
normalize: false,
|
|
1195
|
+
storage: customStorage,
|
|
1196
|
+
wasmBinary: null,
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
expect(db2.size).toBe(1);
|
|
1200
|
+
expect(db2.get("key1")![0]).toBeCloseTo(1);
|
|
1201
|
+
});
|
|
874
1202
|
});
|
package/src/lib/simd-binary.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// AUTO-GENERATED - Do not edit. Run: npx tsx scripts/compile-wat.ts
|
|
2
|
-
const SIMD_WASM_BASE64 = "AGFzbQEAAAABDgJgAn9/AGAFf39/
|
|
2
|
+
const SIMD_WASM_BASE64 = "AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKpgQCrQIFAX8BewN9AXsCf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQkgAyAJ/QAEACAJ/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEAkADQCACIAFPDQEgACACQQJ0aiEJIAQgCSoCACAJKgIAlJIhBCACQQFqIQIMAAsLIASRIQUgBUMAAAAAWwRADwtDAACAPyAFlSEGIAb9EyEHQQAhAgJAA0AgAiAITw0BIAAgAkECdGohCSAJIAn9AAQAIAf95gH9CwQAIAJBBGohAgwACwsCQANAIAIgAU8NASAAIAJBAnRqIQkgCSAJKgIAIAaUOAIAIAJBAWohAgwACwsL9AEEAn8BewF9BX8gBEF8cSEKIARBAnQhDUEAIQUCQANAIAUgA08NASABIAUgDWxqIQn9DAAAAAAAAAAAAAAAAAAAAAAhB0EAIQYCQANAIAYgCk8NASAAIAZBAnRqIQsgCSAGQQJ0aiEMIAcgC/0ABAAgDP0ABAD95gH95AEhByAGQQRqIQYMAAsLIAf9HwAgB/0fAZIgB/0fAiAH/R8DkpIhCAJAA0AgBiAETw0BIAAgBkECdGohCyAJIAZBAnRqIQwgCCALKgIAIAwqAgCUkiEIIAZBAWohBgwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL";
|
|
3
3
|
|
|
4
4
|
export function getSimdWasmBinary(): Uint8Array {
|
|
5
5
|
const binaryString = atob(SIMD_WASM_BASE64);
|