eigen-db 2.1.2 → 3.0.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 CHANGED
@@ -1,3 +1,7 @@
1
+ # v3.0.0
2
+
3
+ Changed: Input vectors should be of type `number[]`, not `Float32Array`.
4
+
1
5
  # v2.1.2
2
6
 
3
7
  Fixed: type export issue
package/README.md CHANGED
@@ -42,7 +42,7 @@ db.setMany([
42
42
 
43
43
  Notes:
44
44
 
45
- - Each vector must be a `Float32Array` with exactly `dimensions` elements.
45
+ - Each vector must be a `number[]` (or `Float32Array`) with exactly `dimensions` elements.
46
46
  - Duplicate keys use last-write-wins semantics.
47
47
 
48
48
  ### 3) Query nearest vectors
@@ -83,7 +83,7 @@ export { DB };
83
83
  export { ResultSet };
84
84
  export type { ResultItem };
85
85
  export { VectorCapacityExceededError };
86
- export type { OpenOptions, OpenOptionsInternal, SetOptions, QueryOptions };
86
+ export type { OpenOptions, OpenOptionsInternal, SetOptions, QueryOptions, VectorInput };
87
87
  export { InMemoryStorageProvider, OPFSStorageProvider };
88
88
  export type { StorageProvider };
89
89
  ```
@@ -105,16 +105,16 @@ Opens (or creates) a database instance and loads persisted data.
105
105
 
106
106
  #### Methods
107
107
 
108
- - `set(key: string, value: Float32Array, options?: SetOptions): void`
108
+ - `set(key: string, value: VectorInput, options?: SetOptions): void`
109
109
  - Inserts or overwrites a vector.
110
110
  - Throws on dimension mismatch.
111
- - `get(key: string): Float32Array | undefined`
111
+ - `get(key: string): number[] | undefined`
112
112
  - Returns a copy of the stored vector.
113
- - `setMany(entries: [string, Float32Array][]): void`
113
+ - `setMany(entries: [string, VectorInput][]): void`
114
114
  - Batch insert/update.
115
- - `getMany(keys: string[]): (Float32Array | undefined)[]`
115
+ - `getMany(keys: string[]): (number[] | undefined)[]`
116
116
  - Batch lookup.
117
- - `query(value: Float32Array, options?: QueryOptions): ResultSet`
117
+ - `query(value: VectorInput, options?: QueryOptions): ResultSet`
118
118
  - Returns similarity-ranked results.
119
119
  - Throws on dimension mismatch.
120
120
  - `flush(): Promise<void>`
package/dist/eigen-db.js CHANGED
@@ -267,10 +267,7 @@ class E {
267
267
  this.memoryManager = e, this.storage = t, this.dimensions = r, this.shouldNormalize = s, this.wasmExports = o, this.keyToSlot = n, this.slotToKey = a;
268
268
  }
269
269
  static async open(e) {
270
- const t = e.name ?? "default", r = e.storage ?? new B(t), s = e.normalize !== !1, [o, n] = await Promise.all([
271
- r.readAll(u),
272
- r.readAll(C)
273
- ]), a = n.byteLength > 0 ? O(n) : [], c = o.byteLength / (e.dimensions * 4), m = /* @__PURE__ */ new Map(), y = [];
270
+ const t = e.name ?? "default", r = e.storage ?? new B(t), s = e.normalize !== !1, [o, n] = await Promise.all([r.readAll(u), r.readAll(C)]), a = n.byteLength > 0 ? O(n) : [], c = o.byteLength / (e.dimensions * 4), m = /* @__PURE__ */ new Map(), y = [];
274
271
  for (let h = 0; h < a.length; h++)
275
272
  m.set(a[h], h), y[h] = a[h];
276
273
  const A = new x(e.dimensions, c);
@@ -282,15 +279,7 @@ class E {
282
279
  f = await V(g, A.memory);
283
280
  } catch {
284
281
  }
285
- return new E(
286
- A,
287
- r,
288
- e.dimensions,
289
- s,
290
- f,
291
- m,
292
- y
293
- );
282
+ return new E(A, r, e.dimensions, s, f, m, y);
294
283
  }
295
284
  /** Total number of key-value pairs in the database */
296
285
  get size() {
@@ -298,13 +287,11 @@ class E {
298
287
  }
299
288
  /**
300
289
  * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).
301
- * The value is a Float32Array of length equal to the configured dimensions.
290
+ * The value is a number[] or Float32Array of length equal to the configured dimensions.
302
291
  */
303
292
  set(e, t, r) {
304
293
  if (this.assertOpen(), t.length !== this.dimensions)
305
- throw new Error(
306
- `Vector dimension mismatch: expected ${this.dimensions}, got ${t.length}`
307
- );
294
+ throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${t.length}`);
308
295
  const s = new Float32Array(t);
309
296
  (r?.normalize ?? this.shouldNormalize) && this.normalizeVector(s);
310
297
  const n = this.keyToSlot.get(e);
@@ -320,13 +307,13 @@ class E {
320
307
  }
321
308
  /**
322
309
  * Get the stored vector for a key. Returns undefined if the key does not exist.
323
- * Returns a copy of the stored vector.
310
+ * Returns a copy of the stored vector as a plain number array.
324
311
  */
325
312
  get(e) {
326
313
  this.assertOpen();
327
314
  const t = this.keyToSlot.get(e);
328
315
  if (t !== void 0)
329
- return new Float32Array(this.memoryManager.readVector(t));
316
+ return Array.from(this.memoryManager.readVector(t));
330
317
  }
331
318
  /**
332
319
  * Set multiple key-value pairs at once. Last-write-wins applies within the batch.
@@ -351,9 +338,7 @@ class E {
351
338
  if (this.size === 0)
352
339
  return d.fromScores(new Float32Array(0), () => "", 0);
353
340
  if (e.length !== this.dimensions)
354
- throw new Error(
355
- `Query vector dimension mismatch: expected ${this.dimensions}, got ${e.length}`
356
- );
341
+ throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${e.length}`);
357
342
  const s = new Float32Array(e);
358
343
  (t?.normalize ?? this.shouldNormalize) && this.normalizeVector(s), this.memoryManager.writeQuery(s), this.memoryManager.ensureCapacity(0);
359
344
  const n = this.memoryManager.vectorCount, a = this.memoryManager.scoresOffset;
@@ -374,11 +359,7 @@ class E {
374
359
  this.memoryManager.memory.buffer,
375
360
  this.memoryManager.dbOffset,
376
361
  n * this.dimensions
377
- ), g = new Float32Array(
378
- this.memoryManager.memory.buffer,
379
- a,
380
- n
381
- );
362
+ ), g = new Float32Array(this.memoryManager.memory.buffer, a, n);
382
363
  I(A, f, g, n, this.dimensions);
383
364
  }
384
365
  const c = new Float32Array(this.memoryManager.readScores()), m = this.slotToKey, y = (A) => m[A];
@@ -389,9 +370,7 @@ class E {
389
370
  */
390
371
  async flush() {
391
372
  this.assertOpen();
392
- const e = this.memoryManager.vectorCount, t = new Uint8Array(
393
- e * this.dimensions * 4
394
- );
373
+ const e = this.memoryManager.vectorCount, t = new Uint8Array(e * this.dimensions * 4);
395
374
  if (e > 0) {
396
375
  const s = new Uint8Array(
397
376
  this.memoryManager.memory.buffer,
@@ -401,10 +380,7 @@ class E {
401
380
  t.set(s);
402
381
  }
403
382
  const r = p(this.slotToKey);
404
- await Promise.all([
405
- this.storage.write(u, t),
406
- this.storage.write(C, r)
407
- ]);
383
+ await Promise.all([this.storage.write(u, t), this.storage.write(C, r)]);
408
384
  }
409
385
  /**
410
386
  * Flush data to storage and release the instance.
@@ -1 +1 @@
1
- {"version":3,"file":"eigen-db.js","sources":["../src/lib/errors.ts","../src/lib/result-set.ts","../src/lib/storage.ts","../src/lib/compute.ts","../src/lib/lexicon.ts","../src/lib/memory-manager.ts","../src/lib/simd-binary.ts","../src/lib/wasm-compute.ts","../src/lib/vector-db.ts"],"sourcesContent":["/**\n * Thrown when the database exceeds the 4GB WebAssembly 32-bit memory limit,\n * or the browser's available RAM.\n */\nexport class VectorCapacityExceededError extends Error {\n constructor(maxVectors: number) {\n super(`Capacity exceeded. Max vectors for this dimension size is ~${maxVectors}.`);\n this.name = \"VectorCapacityExceededError\";\n }\n}\n","/**\n * LAZY RESULT SET\n *\n * Holds pointers to sorted TypedArrays. Prevents JS heap overflow when K is massive.\n * Strings are only instantiated from the Lexicon when explicitly requested.\n */\n\nexport interface ResultItem {\n key: string;\n score: number;\n}\n\nexport type KeyResolver = (index: number) => string;\n\nexport class ResultSet {\n /** Total number of results */\n readonly length: number;\n\n /**\n * Sorted indices into the original database (by descending score).\n * sortedIndices[0] is the index of the best match.\n */\n private readonly sortedIndices: Uint32Array;\n\n /** Raw scores array (not sorted, indexed by original DB position) */\n private readonly scores: Float32Array;\n\n /** Function to lazily resolve key from the slot index */\n private readonly resolveKey: KeyResolver;\n\n constructor(\n scores: Float32Array,\n sortedIndices: Uint32Array,\n resolveKey: KeyResolver,\n topK: number,\n ) {\n this.scores = scores;\n this.sortedIndices = sortedIndices;\n this.resolveKey = resolveKey;\n this.length = Math.min(topK, sortedIndices.length);\n }\n\n /**\n * Sort scores and return a ResultSet with lazy key resolution.\n *\n * @param scores - Float32Array of scores (one per DB vector)\n * @param resolveKey - Function to resolve key by original index\n * @param topK - Maximum number of results to include\n */\n static fromScores(\n scores: Float32Array,\n resolveKey: KeyResolver,\n topK: number,\n ): ResultSet {\n const n = scores.length;\n\n // Create index array for sorting\n const indices = new Uint32Array(n);\n for (let i = 0; i < n; i++) indices[i] = i;\n\n // Sort indices by descending score\n indices.sort((a, b) => scores[b] - scores[a]);\n\n return new ResultSet(scores, indices, resolveKey, topK);\n }\n\n /** Fetch a single result by its rank (0 is best match) */\n get(rank: number): ResultItem {\n if (rank < 0 || rank >= this.length) {\n throw new RangeError(`Rank ${rank} out of bounds [0, ${this.length})`);\n }\n const dbIndex = this.sortedIndices[rank];\n return {\n key: this.resolveKey(dbIndex),\n score: this.scores[dbIndex],\n };\n }\n\n /** Helper for UI pagination. Instantiates strings only for the requested page. */\n getPage(page: number, pageSize: number): ResultItem[] {\n const start = page * pageSize;\n const end = Math.min(start + pageSize, this.length);\n const results: ResultItem[] = [];\n for (let i = start; i < end; i++) {\n results.push(this.get(i));\n }\n return results;\n }\n}\n","/**\n * Storage abstraction for append-only binary files.\n * Supports OPFS for browser and in-memory for testing.\n */\n\nexport interface StorageProvider {\n /** Read the entire contents of a file. Returns empty Uint8Array if file doesn't exist. */\n readAll(fileName: string): Promise<Uint8Array>;\n\n /** Append data to a file (creates if it doesn't exist). */\n append(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Write data to a file, replacing all existing content. */\n write(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Delete the storage directory and all files. */\n destroy(): Promise<void>;\n}\n\n/**\n * OPFS-backed storage provider for browser environments.\n * Uses Origin Private File System for high-performance persistent storage.\n */\nexport class OPFSStorageProvider implements StorageProvider {\n private dirHandle: FileSystemDirectoryHandle | null = null;\n private dirName: string;\n\n constructor(dirName: string) {\n this.dirName = dirName;\n }\n\n private async getDir(): Promise<FileSystemDirectoryHandle> {\n if (!this.dirHandle) {\n const root = await navigator.storage.getDirectory();\n this.dirHandle = await root.getDirectoryHandle(this.dirName, { create: true });\n }\n return this.dirHandle;\n }\n\n async readAll(fileName: string): Promise<Uint8Array> {\n try {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n const buffer = await file.arrayBuffer();\n return new Uint8Array(buffer);\n } catch {\n return new Uint8Array(0);\n }\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n const file = await fileHandle.getFile();\n await writable.seek(file.size);\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: false });\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async destroy(): Promise<void> {\n const root = await navigator.storage.getDirectory();\n await root.removeEntry(this.dirName, { recursive: true });\n this.dirHandle = null;\n }\n}\n\n/**\n * In-memory storage provider for testing.\n */\nexport class InMemoryStorageProvider implements StorageProvider {\n private files = new Map<string, Uint8Array[]>();\n\n async readAll(fileName: string): Promise<Uint8Array> {\n const chunks = this.files.get(fileName);\n if (!chunks || chunks.length === 0) return new Uint8Array(0);\n\n const totalSize = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const result = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n if (!this.files.has(fileName)) {\n this.files.set(fileName, []);\n }\n this.files.get(fileName)!.push(new Uint8Array(data));\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n this.files.set(fileName, [new Uint8Array(data)]);\n }\n\n async destroy(): Promise<void> {\n this.files.clear();\n }\n}\n","/**\n * Pure JavaScript compute functions for vector operations.\n * These serve as the reference implementation and fallback when WASM SIMD is unavailable.\n */\n\n/**\n * Normalizes a vector in-place to unit length.\n * After normalization, cosine similarity reduces to a simple dot product.\n */\nexport function normalize(vec: Float32Array): void {\n let sumSq = 0;\n for (let i = 0; i < vec.length; i++) {\n sumSq += vec[i] * vec[i];\n }\n const mag = Math.sqrt(sumSq);\n if (mag === 0) return;\n const invMag = 1 / mag;\n for (let i = 0; i < vec.length; i++) {\n vec[i] *= invMag;\n }\n}\n\n/**\n * Computes dot products of query against all vectors in the database.\n * Writes scores to the output array.\n *\n * @param query - Normalized query vector (length = dimensions)\n * @param db - Contiguous flat array of normalized vectors (length = dbSize * dimensions)\n * @param scores - Output array for dot product scores (length = dbSize)\n * @param dbSize - Number of vectors in the database\n * @param dimensions - Dimensionality of each vector\n */\nexport function searchAll(\n query: Float32Array,\n db: Float32Array,\n scores: Float32Array,\n dbSize: number,\n dimensions: number,\n): void {\n for (let i = 0; i < dbSize; i++) {\n let dot = 0;\n const offset = i * dimensions;\n for (let j = 0; j < dimensions; j++) {\n dot += query[j] * db[offset + j];\n }\n scores[i] = dot;\n }\n}\n","/**\n * Lexicon: length-prefixed UTF-8 encoding for text strings.\n *\n * Format: Each entry is [4-byte uint32 length][UTF-8 bytes]\n * This allows efficient sequential reading and appending.\n */\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encodes an array of strings into a length-prefixed binary format.\n */\nexport function encodeLexicon(texts: string[]): Uint8Array {\n const encoded = texts.map((t) => encoder.encode(t));\n const totalSize = encoded.reduce((sum, e) => sum + 4 + e.byteLength, 0);\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n const bytes = new Uint8Array(buffer);\n let offset = 0;\n\n for (const e of encoded) {\n view.setUint32(offset, e.byteLength, true); // little-endian\n offset += 4;\n bytes.set(e, offset);\n offset += e.byteLength;\n }\n\n return bytes;\n}\n\n/**\n * Decodes all strings from a length-prefixed binary buffer.\n */\nexport function decodeLexicon(data: Uint8Array): string[] {\n const result: string[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n const len = view.getUint32(offset, true);\n offset += 4;\n const text = decoder.decode(data.subarray(offset, offset + len));\n result.push(text);\n offset += len;\n }\n\n return result;\n}\n\n/**\n * Decodes a single string at a given index from the lexicon.\n * Returns the string and the byte offset of the next entry.\n */\nexport function decodeLexiconAt(data: Uint8Array, index: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n for (let i = 0; i < index; i++) {\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n const len = view.getUint32(offset, true);\n offset += 4;\n return decoder.decode(data.subarray(offset, offset + len));\n}\n\n/**\n * Builds an index of byte offsets for each entry in the lexicon.\n * Enables O(1) access to any entry by index.\n */\nexport function buildLexiconIndex(data: Uint8Array): Uint32Array {\n const offsets: number[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n offsets.push(offset);\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n return new Uint32Array(offsets);\n}\n\n/**\n * Decodes a string at a given byte offset in the lexicon.\n */\nexport function decodeLexiconAtOffset(data: Uint8Array, byteOffset: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const len = view.getUint32(byteOffset, true);\n return decoder.decode(data.subarray(byteOffset + 4, byteOffset + 4 + len));\n}\n","/**\n * Memory Manager for WASM shared memory.\n *\n * Memory Layout:\n * [ 0x00000 ] -> Query Vector Buffer (Fixed, dimensions * 4 bytes, aligned to 64KB page)\n * [ DB_OFFSET ] -> Vector Database (Grows dynamically)\n * [ Dynamic ] -> Scores Buffer (Mapped after DB during search)\n */\n\n/** WASM page size is 64KB */\nconst PAGE_SIZE = 65536;\n\n/** Maximum WASM memory: ~4GB (65536 pages of 64KB each) */\nconst MAX_PAGES = 65536;\n\nexport class MemoryManager {\n readonly memory: WebAssembly.Memory;\n readonly dimensions: number;\n readonly queryOffset: number;\n readonly dbOffset: number;\n private _vectorCount: number;\n\n constructor(dimensions: number, initialVectorCount: number = 0) {\n this.dimensions = dimensions;\n\n // Query buffer: dimensions * 4 bytes, aligned to page boundary\n this.queryOffset = 0;\n const queryBytes = dimensions * 4;\n this.dbOffset = Math.ceil(queryBytes / PAGE_SIZE) * PAGE_SIZE;\n\n // Calculate initial memory needed\n const dbBytes = initialVectorCount * dimensions * 4;\n const totalBytes = this.dbOffset + dbBytes;\n const initialPages = Math.max(1, Math.ceil(totalBytes / PAGE_SIZE));\n\n this.memory = new WebAssembly.Memory({ initial: initialPages });\n this._vectorCount = initialVectorCount;\n }\n\n /** Current number of vectors stored */\n get vectorCount(): number {\n return this._vectorCount;\n }\n\n /** Byte offset where the scores buffer starts (right after DB) */\n get scoresOffset(): number {\n return this.dbOffset + this._vectorCount * this.dimensions * 4;\n }\n\n /** Total bytes needed for scores buffer */\n get scoresBytes(): number {\n return this._vectorCount * 4;\n }\n\n /**\n * Maximum vectors that can be stored given the 4GB WASM memory limit.\n * Accounts for query buffer, DB space, and scores buffer.\n */\n get maxVectors(): number {\n const availableBytes = MAX_PAGES * PAGE_SIZE - this.dbOffset;\n // Each vector needs: dimensions * 4 bytes (DB) + 4 bytes (score)\n const bytesPerVector = this.dimensions * 4 + 4;\n return Math.floor(availableBytes / bytesPerVector);\n }\n\n /**\n * Ensures memory is large enough for the current DB + scores buffer.\n * Calls memory.grow() if needed.\n */\n ensureCapacity(additionalVectors: number): void {\n const newTotal = this._vectorCount + additionalVectors;\n const requiredBytes =\n this.dbOffset + newTotal * this.dimensions * 4 + newTotal * 4; // DB + scores\n const currentBytes = this.memory.buffer.byteLength;\n\n if (requiredBytes > currentBytes) {\n const pagesNeeded = Math.ceil((requiredBytes - currentBytes) / PAGE_SIZE);\n const currentPages = currentBytes / PAGE_SIZE;\n if (currentPages + pagesNeeded > MAX_PAGES) {\n throw new Error(\"WASM memory limit exceeded\");\n }\n this.memory.grow(pagesNeeded);\n }\n }\n\n /**\n * Write a query vector into the query buffer region.\n */\n writeQuery(vector: Float32Array): void {\n new Float32Array(this.memory.buffer, this.queryOffset, this.dimensions).set(vector);\n }\n\n /**\n * Append vectors to the database region.\n * Returns the byte offset where the new vectors were written.\n */\n appendVectors(vectors: Float32Array[]): number {\n const startOffset = this.dbOffset + this._vectorCount * this.dimensions * 4;\n let offset = startOffset;\n for (const vec of vectors) {\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vec);\n offset += this.dimensions * 4;\n }\n this._vectorCount += vectors.length;\n return startOffset;\n }\n\n /**\n * Load raw vector bytes directly into the database region.\n * Used for bulk loading from OPFS.\n */\n loadVectorBytes(data: Uint8Array, vectorCount: number): void {\n new Uint8Array(this.memory.buffer, this.dbOffset, data.byteLength).set(data);\n this._vectorCount = vectorCount;\n }\n\n /**\n * Read the scores buffer as a Float32Array view.\n */\n readScores(): Float32Array {\n return new Float32Array(this.memory.buffer, this.scoresOffset, this._vectorCount);\n }\n\n /**\n * Read the DB region for a specific vector index.\n */\n readVector(index: number): Float32Array {\n const offset = this.dbOffset + index * this.dimensions * 4;\n return new Float32Array(this.memory.buffer, offset, this.dimensions);\n }\n\n /**\n * Write a vector to a specific slot in the database region.\n */\n writeVector(index: number, vector: Float32Array): void {\n const offset = this.dbOffset + index * this.dimensions * 4;\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vector);\n }\n\n /**\n * Reset the vector count to zero, logically clearing the database.\n * WASM memory is not freed but will be overwritten on next writes.\n */\n reset(): void {\n this._vectorCount = 0;\n }\n}\n","// AUTO-GENERATED - Do not edit. Run: npx tsx scripts/compile-wat.ts\nconst SIMD_WASM_BASE64 = \"AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL\";\n\nexport function getSimdWasmBinary(): Uint8Array {\n const binaryString = atob(SIMD_WASM_BASE64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * WASM SIMD compute layer.\n * Compiles the hand-written WAT module and provides typed wrappers\n * that operate on shared WebAssembly.Memory.\n */\n\nexport interface WasmExports {\n normalize(ptr: number, dimensions: number): void;\n search_all(queryPtr: number, dbPtr: number, scoresPtr: number, dbSize: number, dimensions: number): void;\n}\n\n/**\n * Instantiates a WASM module with the given memory and returns typed exports.\n */\nexport async function instantiateWasm(wasmBinary: Uint8Array, memory: WebAssembly.Memory): Promise<WasmExports> {\n const importObject = { env: { memory } };\n const result = await WebAssembly.instantiate(wasmBinary, importObject);\n // WebAssembly.instantiate with a buffer returns { instance, module }\n const instance = (result as unknown as { instance: WebAssembly.Instance }).instance;\n return instance.exports as unknown as WasmExports;\n}\n","/**\n * VectorDB — Key-Value Vector Database\n *\n * Decoupled from embedding providers. Users pass pre-computed vectors\n * as Float32Array values with string keys.\n *\n * Supports:\n * - set/get/setMany/getMany for key-value CRUD\n * - query for similarity search (dot product on normalized vectors)\n * - flush to persist, close to flush+release, clear to wipe\n * - Last-write-wins semantics for duplicate keys (append-only storage)\n */\n\nimport { normalize, searchAll } from \"./compute\";\nimport { VectorCapacityExceededError } from \"./errors\";\nimport { encodeLexicon, decodeLexicon } from \"./lexicon\";\nimport { MemoryManager } from \"./memory-manager\";\nimport { ResultSet } from \"./result-set\";\nimport { getSimdWasmBinary } from \"./simd-binary\";\nimport type { StorageProvider } from \"./storage\";\nimport { OPFSStorageProvider } from \"./storage\";\nimport type { OpenOptions, OpenOptionsInternal, SetOptions, QueryOptions } from \"./types\";\nimport { instantiateWasm, type WasmExports } from \"./wasm-compute\";\n\nconst VECTORS_FILE = \"vectors.bin\";\nconst KEYS_FILE = \"keys.bin\";\n\nexport class VectorDB {\n private readonly memoryManager: MemoryManager;\n private readonly storage: StorageProvider;\n private readonly dimensions: number;\n private readonly shouldNormalize: boolean;\n private wasmExports: WasmExports | null;\n\n /** Maps key to its slot index in the vector array */\n private keyToSlot: Map<string, number>;\n\n /** Maps slot index back to its key */\n private slotToKey: string[];\n\n /** Whether this instance has been closed */\n private closed = false;\n\n private constructor(\n memoryManager: MemoryManager,\n storage: StorageProvider,\n dimensions: number,\n shouldNormalize: boolean,\n wasmExports: WasmExports | null,\n keyToSlot: Map<string, number>,\n slotToKey: string[],\n ) {\n this.memoryManager = memoryManager;\n this.storage = storage;\n this.dimensions = dimensions;\n this.shouldNormalize = shouldNormalize;\n this.wasmExports = wasmExports;\n this.keyToSlot = keyToSlot;\n this.slotToKey = slotToKey;\n }\n\n /**\n * Opens a VectorDB instance.\n * Loads existing data from storage into WASM memory.\n */\n static async open(options: OpenOptions): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB> {\n const name = options.name ?? \"default\";\n const storage = options.storage ?? new OPFSStorageProvider(name);\n const shouldNormalize = options.normalize !== false;\n\n // Load existing data from storage\n const [vectorBytes, keysBytes] = await Promise.all([\n storage.readAll(VECTORS_FILE),\n storage.readAll(KEYS_FILE),\n ]);\n\n // Decode stored keys\n const keys = keysBytes.byteLength > 0 ? decodeLexicon(keysBytes) : [];\n const vectorCount = vectorBytes.byteLength / (options.dimensions * 4);\n\n // Build key-to-slot mapping.\n // flush() always writes deduplicated state, so keys are unique on load.\n const keyToSlot = new Map<string, number>();\n const slotToKey: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n keyToSlot.set(keys[i], i);\n slotToKey[i] = keys[i];\n }\n\n // Initialize memory manager\n const mm = new MemoryManager(options.dimensions, vectorCount);\n\n if (vectorBytes.byteLength > 0) {\n mm.loadVectorBytes(vectorBytes, vectorCount);\n }\n\n // Try to instantiate WASM SIMD module\n let wasmExports: WasmExports | null = null;\n const wasmBinary = options.wasmBinary !== undefined ? options.wasmBinary : getSimdWasmBinary();\n if (wasmBinary !== null) {\n try {\n wasmExports = await instantiateWasm(wasmBinary, mm.memory);\n } catch {\n // Fall back to JS compute\n }\n }\n\n return new VectorDB(\n mm,\n storage,\n options.dimensions,\n shouldNormalize,\n wasmExports,\n keyToSlot,\n slotToKey,\n );\n }\n\n /** Total number of key-value pairs in the database */\n get size(): number {\n return this.keyToSlot.size;\n }\n\n /**\n * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).\n * The value is a Float32Array of length equal to the configured dimensions.\n */\n set(key: string, value: Float32Array, options?: SetOptions): void {\n this.assertOpen();\n\n if (value.length !== this.dimensions) {\n throw new Error(\n `Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,\n );\n }\n\n // Clone to avoid mutating caller's array during normalization\n const vec = new Float32Array(value);\n\n // Normalize if needed\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(vec);\n }\n\n const existingSlot = this.keyToSlot.get(key);\n if (existingSlot !== undefined) {\n // Overwrite existing slot\n this.memoryManager.writeVector(existingSlot, vec);\n } else {\n // Append new entry\n const newTotal = this.memoryManager.vectorCount + 1;\n if (newTotal > this.memoryManager.maxVectors) {\n throw new VectorCapacityExceededError(this.memoryManager.maxVectors);\n }\n this.memoryManager.ensureCapacity(1);\n const slotIndex = this.memoryManager.vectorCount;\n this.memoryManager.appendVectors([vec]);\n this.keyToSlot.set(key, slotIndex);\n this.slotToKey[slotIndex] = key;\n }\n }\n\n /**\n * Get the stored vector for a key. Returns undefined if the key does not exist.\n * Returns a copy of the stored vector.\n */\n get(key: string): Float32Array | undefined {\n this.assertOpen();\n\n const slot = this.keyToSlot.get(key);\n if (slot === undefined) return undefined;\n\n // Return a copy so callers can't corrupt WASM memory\n return new Float32Array(this.memoryManager.readVector(slot));\n }\n\n /**\n * Set multiple key-value pairs at once. Last-write-wins applies within the batch.\n */\n setMany(entries: [string, Float32Array][]): void {\n for (const [key, value] of entries) {\n this.set(key, value);\n }\n }\n\n /**\n * Get vectors for multiple keys. Returns undefined for keys that don't exist.\n */\n getMany(keys: string[]): (Float32Array | undefined)[] {\n return keys.map((key) => this.get(key));\n }\n\n /**\n * Search for the most similar vectors to the given query vector.\n * Returns a ResultSet sorted by descending similarity score.\n */\n query(value: Float32Array, options?: QueryOptions): ResultSet {\n this.assertOpen();\n\n const k = options?.topK ?? this.size;\n\n if (this.size === 0) {\n return ResultSet.fromScores(new Float32Array(0), () => \"\", 0);\n }\n\n if (value.length !== this.dimensions) {\n throw new Error(\n `Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,\n );\n }\n\n // Clone and optionally normalize the query vector\n const queryVec = new Float32Array(value);\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(queryVec);\n }\n\n // Write query to WASM memory\n this.memoryManager.writeQuery(queryVec);\n\n // Ensure memory has space for scores buffer\n this.memoryManager.ensureCapacity(0);\n\n // Total vectors in memory\n const totalVectors = this.memoryManager.vectorCount;\n\n // Execute search\n const scoresOffset = this.memoryManager.scoresOffset;\n if (this.wasmExports) {\n this.wasmExports.search_all(\n this.memoryManager.queryOffset,\n this.memoryManager.dbOffset,\n scoresOffset,\n totalVectors,\n this.dimensions,\n );\n } else {\n const queryView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.queryOffset,\n this.dimensions,\n );\n const dbView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions,\n );\n const scoresView = new Float32Array(\n this.memoryManager.memory.buffer,\n scoresOffset,\n totalVectors,\n );\n searchAll(queryView, dbView, scoresView, totalVectors, this.dimensions);\n }\n\n // Read scores (make a copy so the buffer can be reused)\n const scores = new Float32Array(this.memoryManager.readScores());\n\n // Resolve key from slot index\n const slotToKey = this.slotToKey;\n const resolveKey = (slotIndex: number): string => {\n return slotToKey[slotIndex];\n };\n\n return ResultSet.fromScores(scores, resolveKey, k);\n }\n\n /**\n * Persist the current in-memory state to storage.\n */\n async flush(): Promise<void> {\n this.assertOpen();\n\n const totalVectors = this.memoryManager.vectorCount;\n\n // Serialize vectors from WASM memory\n const vectorBytes = new Uint8Array(\n totalVectors * this.dimensions * 4,\n );\n if (totalVectors > 0) {\n const src = new Uint8Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions * 4,\n );\n vectorBytes.set(src);\n }\n\n // Serialize keys using lexicon format\n const keysBytes = encodeLexicon(this.slotToKey);\n\n await Promise.all([\n this.storage.write(VECTORS_FILE, vectorBytes),\n this.storage.write(KEYS_FILE, keysBytes),\n ]);\n }\n\n /**\n * Flush data to storage and release the instance.\n * The instance cannot be used after close.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n await this.flush();\n this.closed = true;\n }\n\n /**\n * Clear all data from the database and storage.\n */\n async clear(): Promise<void> {\n this.assertOpen();\n\n this.keyToSlot.clear();\n this.slotToKey.length = 0;\n this.memoryManager.reset();\n\n await this.storage.destroy();\n }\n\n /**\n * Normalize a vector using WASM (if available) or JS fallback.\n */\n private normalizeVector(vec: Float32Array): void {\n if (this.wasmExports) {\n const ptr = this.memoryManager.queryOffset;\n new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length).set(vec);\n this.wasmExports.normalize(ptr, vec.length);\n const normalized = new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length);\n vec.set(normalized);\n } else {\n normalize(vec);\n }\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"VectorDB instance has been closed\");\n }\n }\n}\n"],"names":["VectorCapacityExceededError","maxVectors","ResultSet","scores","sortedIndices","resolveKey","topK","n","indices","i","a","b","rank","dbIndex","page","pageSize","start","end","results","OPFSStorageProvider","dirName","root","fileName","buffer","data","fileHandle","writable","file","InMemoryStorageProvider","chunks","totalSize","sum","c","result","offset","chunk","normalize","vec","sumSq","mag","invMag","searchAll","query","db","dbSize","dimensions","dot","j","encoder","decoder","encodeLexicon","texts","encoded","t","e","view","bytes","decodeLexicon","len","text","PAGE_SIZE","MAX_PAGES","MemoryManager","initialVectorCount","queryBytes","dbBytes","totalBytes","initialPages","availableBytes","bytesPerVector","additionalVectors","newTotal","requiredBytes","currentBytes","pagesNeeded","vector","vectors","startOffset","vectorCount","index","SIMD_WASM_BASE64","getSimdWasmBinary","binaryString","instantiateWasm","wasmBinary","memory","importObject","VECTORS_FILE","KEYS_FILE","VectorDB","memoryManager","storage","shouldNormalize","wasmExports","keyToSlot","slotToKey","options","name","vectorBytes","keysBytes","keys","mm","key","value","existingSlot","slotIndex","slot","entries","k","queryVec","totalVectors","scoresOffset","queryView","dbView","scoresView","src","ptr","normalized"],"mappings":"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"}
1
+ {"version":3,"file":"eigen-db.js","sources":["../src/lib/errors.ts","../src/lib/result-set.ts","../src/lib/storage.ts","../src/lib/compute.ts","../src/lib/lexicon.ts","../src/lib/memory-manager.ts","../src/lib/simd-binary.ts","../src/lib/wasm-compute.ts","../src/lib/vector-db.ts"],"sourcesContent":["/**\n * Thrown when the database exceeds the 4GB WebAssembly 32-bit memory limit,\n * or the browser's available RAM.\n */\nexport class VectorCapacityExceededError extends Error {\n constructor(maxVectors: number) {\n super(`Capacity exceeded. Max vectors for this dimension size is ~${maxVectors}.`);\n this.name = \"VectorCapacityExceededError\";\n }\n}\n","/**\n * LAZY RESULT SET\n *\n * Holds pointers to sorted TypedArrays. Prevents JS heap overflow when K is massive.\n * Strings are only instantiated from the Lexicon when explicitly requested.\n */\n\nexport interface ResultItem {\n key: string;\n score: number;\n}\n\nexport type KeyResolver = (index: number) => string;\n\nexport class ResultSet {\n /** Total number of results */\n readonly length: number;\n\n /**\n * Sorted indices into the original database (by descending score).\n * sortedIndices[0] is the index of the best match.\n */\n private readonly sortedIndices: Uint32Array;\n\n /** Raw scores array (not sorted, indexed by original DB position) */\n private readonly scores: Float32Array;\n\n /** Function to lazily resolve key from the slot index */\n private readonly resolveKey: KeyResolver;\n\n constructor(\n scores: Float32Array,\n sortedIndices: Uint32Array,\n resolveKey: KeyResolver,\n topK: number,\n ) {\n this.scores = scores;\n this.sortedIndices = sortedIndices;\n this.resolveKey = resolveKey;\n this.length = Math.min(topK, sortedIndices.length);\n }\n\n /**\n * Sort scores and return a ResultSet with lazy key resolution.\n *\n * @param scores - Float32Array of scores (one per DB vector)\n * @param resolveKey - Function to resolve key by original index\n * @param topK - Maximum number of results to include\n */\n static fromScores(\n scores: Float32Array,\n resolveKey: KeyResolver,\n topK: number,\n ): ResultSet {\n const n = scores.length;\n\n // Create index array for sorting\n const indices = new Uint32Array(n);\n for (let i = 0; i < n; i++) indices[i] = i;\n\n // Sort indices by descending score\n indices.sort((a, b) => scores[b] - scores[a]);\n\n return new ResultSet(scores, indices, resolveKey, topK);\n }\n\n /** Fetch a single result by its rank (0 is best match) */\n get(rank: number): ResultItem {\n if (rank < 0 || rank >= this.length) {\n throw new RangeError(`Rank ${rank} out of bounds [0, ${this.length})`);\n }\n const dbIndex = this.sortedIndices[rank];\n return {\n key: this.resolveKey(dbIndex),\n score: this.scores[dbIndex],\n };\n }\n\n /** Helper for UI pagination. Instantiates strings only for the requested page. */\n getPage(page: number, pageSize: number): ResultItem[] {\n const start = page * pageSize;\n const end = Math.min(start + pageSize, this.length);\n const results: ResultItem[] = [];\n for (let i = start; i < end; i++) {\n results.push(this.get(i));\n }\n return results;\n }\n}\n","/**\n * Storage abstraction for append-only binary files.\n * Supports OPFS for browser and in-memory for testing.\n */\n\nexport interface StorageProvider {\n /** Read the entire contents of a file. Returns empty Uint8Array if file doesn't exist. */\n readAll(fileName: string): Promise<Uint8Array>;\n\n /** Append data to a file (creates if it doesn't exist). */\n append(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Write data to a file, replacing all existing content. */\n write(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Delete the storage directory and all files. */\n destroy(): Promise<void>;\n}\n\n/**\n * OPFS-backed storage provider for browser environments.\n * Uses Origin Private File System for high-performance persistent storage.\n */\nexport class OPFSStorageProvider implements StorageProvider {\n private dirHandle: FileSystemDirectoryHandle | null = null;\n private dirName: string;\n\n constructor(dirName: string) {\n this.dirName = dirName;\n }\n\n private async getDir(): Promise<FileSystemDirectoryHandle> {\n if (!this.dirHandle) {\n const root = await navigator.storage.getDirectory();\n this.dirHandle = await root.getDirectoryHandle(this.dirName, { create: true });\n }\n return this.dirHandle;\n }\n\n async readAll(fileName: string): Promise<Uint8Array> {\n try {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n const buffer = await file.arrayBuffer();\n return new Uint8Array(buffer);\n } catch {\n return new Uint8Array(0);\n }\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n const file = await fileHandle.getFile();\n await writable.seek(file.size);\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: false });\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async destroy(): Promise<void> {\n const root = await navigator.storage.getDirectory();\n await root.removeEntry(this.dirName, { recursive: true });\n this.dirHandle = null;\n }\n}\n\n/**\n * In-memory storage provider for testing.\n */\nexport class InMemoryStorageProvider implements StorageProvider {\n private files = new Map<string, Uint8Array[]>();\n\n async readAll(fileName: string): Promise<Uint8Array> {\n const chunks = this.files.get(fileName);\n if (!chunks || chunks.length === 0) return new Uint8Array(0);\n\n const totalSize = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const result = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n if (!this.files.has(fileName)) {\n this.files.set(fileName, []);\n }\n this.files.get(fileName)!.push(new Uint8Array(data));\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n this.files.set(fileName, [new Uint8Array(data)]);\n }\n\n async destroy(): Promise<void> {\n this.files.clear();\n }\n}\n","/**\n * Pure JavaScript compute functions for vector operations.\n * These serve as the reference implementation and fallback when WASM SIMD is unavailable.\n */\n\n/**\n * Normalizes a vector in-place to unit length.\n * After normalization, cosine similarity reduces to a simple dot product.\n */\nexport function normalize(vec: Float32Array): void {\n let sumSq = 0;\n for (let i = 0; i < vec.length; i++) {\n sumSq += vec[i] * vec[i];\n }\n const mag = Math.sqrt(sumSq);\n if (mag === 0) return;\n const invMag = 1 / mag;\n for (let i = 0; i < vec.length; i++) {\n vec[i] *= invMag;\n }\n}\n\n/**\n * Computes dot products of query against all vectors in the database.\n * Writes scores to the output array.\n *\n * @param query - Normalized query vector (length = dimensions)\n * @param db - Contiguous flat array of normalized vectors (length = dbSize * dimensions)\n * @param scores - Output array for dot product scores (length = dbSize)\n * @param dbSize - Number of vectors in the database\n * @param dimensions - Dimensionality of each vector\n */\nexport function searchAll(\n query: Float32Array,\n db: Float32Array,\n scores: Float32Array,\n dbSize: number,\n dimensions: number,\n): void {\n for (let i = 0; i < dbSize; i++) {\n let dot = 0;\n const offset = i * dimensions;\n for (let j = 0; j < dimensions; j++) {\n dot += query[j] * db[offset + j];\n }\n scores[i] = dot;\n }\n}\n","/**\n * Lexicon: length-prefixed UTF-8 encoding for text strings.\n *\n * Format: Each entry is [4-byte uint32 length][UTF-8 bytes]\n * This allows efficient sequential reading and appending.\n */\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encodes an array of strings into a length-prefixed binary format.\n */\nexport function encodeLexicon(texts: string[]): Uint8Array {\n const encoded = texts.map((t) => encoder.encode(t));\n const totalSize = encoded.reduce((sum, e) => sum + 4 + e.byteLength, 0);\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n const bytes = new Uint8Array(buffer);\n let offset = 0;\n\n for (const e of encoded) {\n view.setUint32(offset, e.byteLength, true); // little-endian\n offset += 4;\n bytes.set(e, offset);\n offset += e.byteLength;\n }\n\n return bytes;\n}\n\n/**\n * Decodes all strings from a length-prefixed binary buffer.\n */\nexport function decodeLexicon(data: Uint8Array): string[] {\n const result: string[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n const len = view.getUint32(offset, true);\n offset += 4;\n const text = decoder.decode(data.subarray(offset, offset + len));\n result.push(text);\n offset += len;\n }\n\n return result;\n}\n\n/**\n * Decodes a single string at a given index from the lexicon.\n * Returns the string and the byte offset of the next entry.\n */\nexport function decodeLexiconAt(data: Uint8Array, index: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n for (let i = 0; i < index; i++) {\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n const len = view.getUint32(offset, true);\n offset += 4;\n return decoder.decode(data.subarray(offset, offset + len));\n}\n\n/**\n * Builds an index of byte offsets for each entry in the lexicon.\n * Enables O(1) access to any entry by index.\n */\nexport function buildLexiconIndex(data: Uint8Array): Uint32Array {\n const offsets: number[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n offsets.push(offset);\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n return new Uint32Array(offsets);\n}\n\n/**\n * Decodes a string at a given byte offset in the lexicon.\n */\nexport function decodeLexiconAtOffset(data: Uint8Array, byteOffset: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const len = view.getUint32(byteOffset, true);\n return decoder.decode(data.subarray(byteOffset + 4, byteOffset + 4 + len));\n}\n","/**\n * Memory Manager for WASM shared memory.\n *\n * Memory Layout:\n * [ 0x00000 ] -> Query Vector Buffer (Fixed, dimensions * 4 bytes, aligned to 64KB page)\n * [ DB_OFFSET ] -> Vector Database (Grows dynamically)\n * [ Dynamic ] -> Scores Buffer (Mapped after DB during search)\n */\n\n/** WASM page size is 64KB */\nconst PAGE_SIZE = 65536;\n\n/** Maximum WASM memory: ~4GB (65536 pages of 64KB each) */\nconst MAX_PAGES = 65536;\n\nexport class MemoryManager {\n readonly memory: WebAssembly.Memory;\n readonly dimensions: number;\n readonly queryOffset: number;\n readonly dbOffset: number;\n private _vectorCount: number;\n\n constructor(dimensions: number, initialVectorCount: number = 0) {\n this.dimensions = dimensions;\n\n // Query buffer: dimensions * 4 bytes, aligned to page boundary\n this.queryOffset = 0;\n const queryBytes = dimensions * 4;\n this.dbOffset = Math.ceil(queryBytes / PAGE_SIZE) * PAGE_SIZE;\n\n // Calculate initial memory needed\n const dbBytes = initialVectorCount * dimensions * 4;\n const totalBytes = this.dbOffset + dbBytes;\n const initialPages = Math.max(1, Math.ceil(totalBytes / PAGE_SIZE));\n\n this.memory = new WebAssembly.Memory({ initial: initialPages });\n this._vectorCount = initialVectorCount;\n }\n\n /** Current number of vectors stored */\n get vectorCount(): number {\n return this._vectorCount;\n }\n\n /** Byte offset where the scores buffer starts (right after DB) */\n get scoresOffset(): number {\n return this.dbOffset + this._vectorCount * this.dimensions * 4;\n }\n\n /** Total bytes needed for scores buffer */\n get scoresBytes(): number {\n return this._vectorCount * 4;\n }\n\n /**\n * Maximum vectors that can be stored given the 4GB WASM memory limit.\n * Accounts for query buffer, DB space, and scores buffer.\n */\n get maxVectors(): number {\n const availableBytes = MAX_PAGES * PAGE_SIZE - this.dbOffset;\n // Each vector needs: dimensions * 4 bytes (DB) + 4 bytes (score)\n const bytesPerVector = this.dimensions * 4 + 4;\n return Math.floor(availableBytes / bytesPerVector);\n }\n\n /**\n * Ensures memory is large enough for the current DB + scores buffer.\n * Calls memory.grow() if needed.\n */\n ensureCapacity(additionalVectors: number): void {\n const newTotal = this._vectorCount + additionalVectors;\n const requiredBytes =\n this.dbOffset + newTotal * this.dimensions * 4 + newTotal * 4; // DB + scores\n const currentBytes = this.memory.buffer.byteLength;\n\n if (requiredBytes > currentBytes) {\n const pagesNeeded = Math.ceil((requiredBytes - currentBytes) / PAGE_SIZE);\n const currentPages = currentBytes / PAGE_SIZE;\n if (currentPages + pagesNeeded > MAX_PAGES) {\n throw new Error(\"WASM memory limit exceeded\");\n }\n this.memory.grow(pagesNeeded);\n }\n }\n\n /**\n * Write a query vector into the query buffer region.\n */\n writeQuery(vector: Float32Array): void {\n new Float32Array(this.memory.buffer, this.queryOffset, this.dimensions).set(vector);\n }\n\n /**\n * Append vectors to the database region.\n * Returns the byte offset where the new vectors were written.\n */\n appendVectors(vectors: Float32Array[]): number {\n const startOffset = this.dbOffset + this._vectorCount * this.dimensions * 4;\n let offset = startOffset;\n for (const vec of vectors) {\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vec);\n offset += this.dimensions * 4;\n }\n this._vectorCount += vectors.length;\n return startOffset;\n }\n\n /**\n * Load raw vector bytes directly into the database region.\n * Used for bulk loading from OPFS.\n */\n loadVectorBytes(data: Uint8Array, vectorCount: number): void {\n new Uint8Array(this.memory.buffer, this.dbOffset, data.byteLength).set(data);\n this._vectorCount = vectorCount;\n }\n\n /**\n * Read the scores buffer as a Float32Array view.\n */\n readScores(): Float32Array {\n return new Float32Array(this.memory.buffer, this.scoresOffset, this._vectorCount);\n }\n\n /**\n * Read the DB region for a specific vector index.\n */\n readVector(index: number): Float32Array {\n const offset = this.dbOffset + index * this.dimensions * 4;\n return new Float32Array(this.memory.buffer, offset, this.dimensions);\n }\n\n /**\n * Write a vector to a specific slot in the database region.\n */\n writeVector(index: number, vector: Float32Array): void {\n const offset = this.dbOffset + index * this.dimensions * 4;\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vector);\n }\n\n /**\n * Reset the vector count to zero, logically clearing the database.\n * WASM memory is not freed but will be overwritten on next writes.\n */\n reset(): void {\n this._vectorCount = 0;\n }\n}\n","// AUTO-GENERATED - Do not edit. Run: npx tsx scripts/compile-wat.ts\nconst SIMD_WASM_BASE64 = \"AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL\";\n\nexport function getSimdWasmBinary(): Uint8Array {\n const binaryString = atob(SIMD_WASM_BASE64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * WASM SIMD compute layer.\n * Compiles the hand-written WAT module and provides typed wrappers\n * that operate on shared WebAssembly.Memory.\n */\n\nexport interface WasmExports {\n normalize(ptr: number, dimensions: number): void;\n search_all(queryPtr: number, dbPtr: number, scoresPtr: number, dbSize: number, dimensions: number): void;\n}\n\n/**\n * Instantiates a WASM module with the given memory and returns typed exports.\n */\nexport async function instantiateWasm(wasmBinary: Uint8Array, memory: WebAssembly.Memory): Promise<WasmExports> {\n const importObject = { env: { memory } };\n const result = await WebAssembly.instantiate(wasmBinary, importObject);\n // WebAssembly.instantiate with a buffer returns { instance, module }\n const instance = (result as unknown as { instance: WebAssembly.Instance }).instance;\n return instance.exports as unknown as WasmExports;\n}\n","/**\n * VectorDB — Key-Value Vector Database\n *\n * Decoupled from embedding providers. Users pass pre-computed vectors\n * as number arrays (or Float32Array) with string keys.\n *\n * Supports:\n * - set/get/setMany/getMany for key-value CRUD\n * - query for similarity search (dot product on normalized vectors)\n * - flush to persist, close to flush+release, clear to wipe\n * - Last-write-wins semantics for duplicate keys (append-only storage)\n */\n\nimport { normalize, searchAll } from \"./compute\";\nimport { VectorCapacityExceededError } from \"./errors\";\nimport { decodeLexicon, encodeLexicon } from \"./lexicon\";\nimport { MemoryManager } from \"./memory-manager\";\nimport { ResultSet } from \"./result-set\";\nimport { getSimdWasmBinary } from \"./simd-binary\";\nimport type { StorageProvider } from \"./storage\";\nimport { OPFSStorageProvider } from \"./storage\";\nimport type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions, VectorInput } from \"./types\";\nimport { instantiateWasm, type WasmExports } from \"./wasm-compute\";\n\nconst VECTORS_FILE = \"vectors.bin\";\nconst KEYS_FILE = \"keys.bin\";\n\nexport class VectorDB {\n private readonly memoryManager: MemoryManager;\n private readonly storage: StorageProvider;\n private readonly dimensions: number;\n private readonly shouldNormalize: boolean;\n private wasmExports: WasmExports | null;\n\n /** Maps key to its slot index in the vector array */\n private keyToSlot: Map<string, number>;\n\n /** Maps slot index back to its key */\n private slotToKey: string[];\n\n /** Whether this instance has been closed */\n private closed = false;\n\n private constructor(\n memoryManager: MemoryManager,\n storage: StorageProvider,\n dimensions: number,\n shouldNormalize: boolean,\n wasmExports: WasmExports | null,\n keyToSlot: Map<string, number>,\n slotToKey: string[],\n ) {\n this.memoryManager = memoryManager;\n this.storage = storage;\n this.dimensions = dimensions;\n this.shouldNormalize = shouldNormalize;\n this.wasmExports = wasmExports;\n this.keyToSlot = keyToSlot;\n this.slotToKey = slotToKey;\n }\n\n /**\n * Opens a VectorDB instance.\n * Loads existing data from storage into WASM memory.\n */\n static async open(options: OpenOptions): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB> {\n const name = options.name ?? \"default\";\n const storage = options.storage ?? new OPFSStorageProvider(name);\n const shouldNormalize = options.normalize !== false;\n\n // Load existing data from storage\n const [vectorBytes, keysBytes] = await Promise.all([storage.readAll(VECTORS_FILE), storage.readAll(KEYS_FILE)]);\n\n // Decode stored keys\n const keys = keysBytes.byteLength > 0 ? decodeLexicon(keysBytes) : [];\n const vectorCount = vectorBytes.byteLength / (options.dimensions * 4);\n\n // Build key-to-slot mapping.\n // flush() always writes deduplicated state, so keys are unique on load.\n const keyToSlot = new Map<string, number>();\n const slotToKey: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n keyToSlot.set(keys[i], i);\n slotToKey[i] = keys[i];\n }\n\n // Initialize memory manager\n const mm = new MemoryManager(options.dimensions, vectorCount);\n\n if (vectorBytes.byteLength > 0) {\n mm.loadVectorBytes(vectorBytes, vectorCount);\n }\n\n // Try to instantiate WASM SIMD module\n let wasmExports: WasmExports | null = null;\n const wasmBinary = options.wasmBinary !== undefined ? options.wasmBinary : getSimdWasmBinary();\n if (wasmBinary !== null) {\n try {\n wasmExports = await instantiateWasm(wasmBinary, mm.memory);\n } catch {\n // Fall back to JS compute\n }\n }\n\n return new VectorDB(mm, storage, options.dimensions, shouldNormalize, wasmExports, keyToSlot, slotToKey);\n }\n\n /** Total number of key-value pairs in the database */\n get size(): number {\n return this.keyToSlot.size;\n }\n\n /**\n * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).\n * The value is a number[] or Float32Array of length equal to the configured dimensions.\n */\n set(key: string, value: VectorInput, options?: SetOptions): void {\n this.assertOpen();\n\n if (value.length !== this.dimensions) {\n throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);\n }\n\n // Convert to Float32Array (also clones to avoid mutating caller's array)\n const vec = new Float32Array(value);\n\n // Normalize if needed\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(vec);\n }\n\n const existingSlot = this.keyToSlot.get(key);\n if (existingSlot !== undefined) {\n // Overwrite existing slot\n this.memoryManager.writeVector(existingSlot, vec);\n } else {\n // Append new entry\n const newTotal = this.memoryManager.vectorCount + 1;\n if (newTotal > this.memoryManager.maxVectors) {\n throw new VectorCapacityExceededError(this.memoryManager.maxVectors);\n }\n this.memoryManager.ensureCapacity(1);\n const slotIndex = this.memoryManager.vectorCount;\n this.memoryManager.appendVectors([vec]);\n this.keyToSlot.set(key, slotIndex);\n this.slotToKey[slotIndex] = key;\n }\n }\n\n /**\n * Get the stored vector for a key. Returns undefined if the key does not exist.\n * Returns a copy of the stored vector as a plain number array.\n */\n get(key: string): number[] | undefined {\n this.assertOpen();\n\n const slot = this.keyToSlot.get(key);\n if (slot === undefined) return undefined;\n\n // Return a plain array copy so callers can't corrupt WASM memory\n return Array.from(this.memoryManager.readVector(slot));\n }\n\n /**\n * Set multiple key-value pairs at once. Last-write-wins applies within the batch.\n */\n setMany(entries: [string, VectorInput][]): void {\n for (const [key, value] of entries) {\n this.set(key, value);\n }\n }\n\n /**\n * Get vectors for multiple keys. Returns undefined for keys that don't exist.\n */\n getMany(keys: string[]): (number[] | undefined)[] {\n return keys.map((key) => this.get(key));\n }\n\n /**\n * Search for the most similar vectors to the given query vector.\n * Returns a ResultSet sorted by descending similarity score.\n */\n query(value: VectorInput, options?: QueryOptions): ResultSet {\n this.assertOpen();\n\n const k = options?.topK ?? this.size;\n\n if (this.size === 0) {\n return ResultSet.fromScores(new Float32Array(0), () => \"\", 0);\n }\n\n if (value.length !== this.dimensions) {\n throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);\n }\n\n // Convert to Float32Array and optionally normalize the query vector\n const queryVec = new Float32Array(value);\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(queryVec);\n }\n\n // Write query to WASM memory\n this.memoryManager.writeQuery(queryVec);\n\n // Ensure memory has space for scores buffer\n this.memoryManager.ensureCapacity(0);\n\n // Total vectors in memory\n const totalVectors = this.memoryManager.vectorCount;\n\n // Execute search\n const scoresOffset = this.memoryManager.scoresOffset;\n if (this.wasmExports) {\n this.wasmExports.search_all(\n this.memoryManager.queryOffset,\n this.memoryManager.dbOffset,\n scoresOffset,\n totalVectors,\n this.dimensions,\n );\n } else {\n const queryView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.queryOffset,\n this.dimensions,\n );\n const dbView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions,\n );\n const scoresView = new Float32Array(this.memoryManager.memory.buffer, scoresOffset, totalVectors);\n searchAll(queryView, dbView, scoresView, totalVectors, this.dimensions);\n }\n\n // Read scores (make a copy so the buffer can be reused)\n const scores = new Float32Array(this.memoryManager.readScores());\n\n // Resolve key from slot index\n const slotToKey = this.slotToKey;\n const resolveKey = (slotIndex: number): string => {\n return slotToKey[slotIndex];\n };\n\n return ResultSet.fromScores(scores, resolveKey, k);\n }\n\n /**\n * Persist the current in-memory state to storage.\n */\n async flush(): Promise<void> {\n this.assertOpen();\n\n const totalVectors = this.memoryManager.vectorCount;\n\n // Serialize vectors from WASM memory\n const vectorBytes = new Uint8Array(totalVectors * this.dimensions * 4);\n if (totalVectors > 0) {\n const src = new Uint8Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions * 4,\n );\n vectorBytes.set(src);\n }\n\n // Serialize keys using lexicon format\n const keysBytes = encodeLexicon(this.slotToKey);\n\n await Promise.all([this.storage.write(VECTORS_FILE, vectorBytes), this.storage.write(KEYS_FILE, keysBytes)]);\n }\n\n /**\n * Flush data to storage and release the instance.\n * The instance cannot be used after close.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n await this.flush();\n this.closed = true;\n }\n\n /**\n * Clear all data from the database and storage.\n */\n async clear(): Promise<void> {\n this.assertOpen();\n\n this.keyToSlot.clear();\n this.slotToKey.length = 0;\n this.memoryManager.reset();\n\n await this.storage.destroy();\n }\n\n /**\n * Normalize a vector using WASM (if available) or JS fallback.\n */\n private normalizeVector(vec: Float32Array): void {\n if (this.wasmExports) {\n const ptr = this.memoryManager.queryOffset;\n new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length).set(vec);\n this.wasmExports.normalize(ptr, vec.length);\n const normalized = new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length);\n vec.set(normalized);\n } else {\n normalize(vec);\n }\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"VectorDB instance has been closed\");\n }\n }\n}\n"],"names":["VectorCapacityExceededError","maxVectors","ResultSet","scores","sortedIndices","resolveKey","topK","n","indices","i","a","b","rank","dbIndex","page","pageSize","start","end","results","OPFSStorageProvider","dirName","root","fileName","buffer","data","fileHandle","writable","file","InMemoryStorageProvider","chunks","totalSize","sum","c","result","offset","chunk","normalize","vec","sumSq","mag","invMag","searchAll","query","db","dbSize","dimensions","dot","j","encoder","decoder","encodeLexicon","texts","encoded","t","e","view","bytes","decodeLexicon","len","text","PAGE_SIZE","MAX_PAGES","MemoryManager","initialVectorCount","queryBytes","dbBytes","totalBytes","initialPages","availableBytes","bytesPerVector","additionalVectors","newTotal","requiredBytes","currentBytes","pagesNeeded","vector","vectors","startOffset","vectorCount","index","SIMD_WASM_BASE64","getSimdWasmBinary","binaryString","instantiateWasm","wasmBinary","memory","importObject","VECTORS_FILE","KEYS_FILE","VectorDB","memoryManager","storage","shouldNormalize","wasmExports","keyToSlot","slotToKey","options","name","vectorBytes","keysBytes","keys","mm","key","value","existingSlot","slotIndex","slot","entries","k","queryVec","totalVectors","scoresOffset","queryView","dbView","scoresView","src","ptr","normalized"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(A,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(A=typeof globalThis<"u"?globalThis:A||self,l(A.EigenDB={}))})(this,(function(A){"use strict";class l extends Error{constructor(e){super(`Capacity exceeded. Max vectors for this dimension size is ~${e}.`),this.name="VectorCapacityExceededError"}}class m{length;sortedIndices;scores;resolveKey;constructor(e,t,r,s){this.scores=e,this.sortedIndices=t,this.resolveKey=r,this.length=Math.min(s,t.length)}static fromScores(e,t,r){const s=e.length,o=new Uint32Array(s);for(let n=0;n<s;n++)o[n]=n;return o.sort((n,a)=>e[a]-e[n]),new m(e,o,t,r)}get(e){if(e<0||e>=this.length)throw new RangeError(`Rank ${e} out of bounds [0, ${this.length})`);const t=this.sortedIndices[e];return{key:this.resolveKey(t),score:this.scores[t]}}getPage(e,t){const r=e*t,s=Math.min(r+t,this.length),o=[];for(let n=r;n<s;n++)o.push(this.get(n));return o}}class E{dirHandle=null;dirName;constructor(e){this.dirName=e}async getDir(){if(!this.dirHandle){const e=await navigator.storage.getDirectory();this.dirHandle=await e.getDirectoryHandle(this.dirName,{create:!0})}return this.dirHandle}async readAll(e){try{const o=await(await(await(await this.getDir()).getFileHandle(e)).getFile()).arrayBuffer();return new Uint8Array(o)}catch{return new Uint8Array(0)}}async append(e,t){const s=await(await this.getDir()).getFileHandle(e,{create:!0}),o=await s.createWritable({keepExistingData:!0}),n=await s.getFile();await o.seek(n.size),await o.write(t),await o.close()}async write(e,t){const o=await(await(await this.getDir()).getFileHandle(e,{create:!0})).createWritable({keepExistingData:!1});await o.write(t),await o.close()}async destroy(){await(await navigator.storage.getDirectory()).removeEntry(this.dirName,{recursive:!0}),this.dirHandle=null}}class I{files=new Map;async readAll(e){const t=this.files.get(e);if(!t||t.length===0)return new Uint8Array(0);const r=t.reduce((n,a)=>n+a.byteLength,0),s=new Uint8Array(r);let o=0;for(const n of t)s.set(n,o),o+=n.byteLength;return s}async append(e,t){this.files.has(e)||this.files.set(e,[]),this.files.get(e).push(new Uint8Array(t))}async write(e,t){this.files.set(e,[new Uint8Array(t)])}async destroy(){this.files.clear()}}function S(i){let e=0;for(let s=0;s<i.length;s++)e+=i[s]*i[s];const t=Math.sqrt(e);if(t===0)return;const r=1/t;for(let s=0;s<i.length;s++)i[s]*=r}function p(i,e,t,r,s){for(let o=0;o<r;o++){let n=0;const a=o*s;for(let c=0;c<s;c++)n+=i[c]*e[a+c];t[o]=n}}const Q=new TextEncoder,O=new TextDecoder;function v(i){const e=i.map(a=>Q.encode(a)),t=e.reduce((a,c)=>a+4+c.byteLength,0),r=new ArrayBuffer(t),s=new DataView(r),o=new Uint8Array(r);let n=0;for(const a of e)s.setUint32(n,a.byteLength,!0),n+=4,o.set(a,n),n+=a.byteLength;return o}function x(i){const e=[],t=new DataView(i.buffer,i.byteOffset,i.byteLength);let r=0;for(;r<i.byteLength;){const s=t.getUint32(r,!0);r+=4;const o=O.decode(i.subarray(r,r+s));e.push(o),r+=s}return e}const y=65536,b=65536;class F{memory;dimensions;queryOffset;dbOffset;_vectorCount;constructor(e,t=0){this.dimensions=e,this.queryOffset=0;const r=e*4;this.dbOffset=Math.ceil(r/y)*y;const s=t*e*4,o=this.dbOffset+s,n=Math.max(1,Math.ceil(o/y));this.memory=new WebAssembly.Memory({initial:n}),this._vectorCount=t}get vectorCount(){return this._vectorCount}get scoresOffset(){return this.dbOffset+this._vectorCount*this.dimensions*4}get scoresBytes(){return this._vectorCount*4}get maxVectors(){const e=b*y-this.dbOffset,t=this.dimensions*4+4;return Math.floor(e/t)}ensureCapacity(e){const t=this._vectorCount+e,r=this.dbOffset+t*this.dimensions*4+t*4,s=this.memory.buffer.byteLength;if(r>s){const o=Math.ceil((r-s)/y);if(s/y+o>b)throw new Error("WASM memory limit exceeded");this.memory.grow(o)}}writeQuery(e){new Float32Array(this.memory.buffer,this.queryOffset,this.dimensions).set(e)}appendVectors(e){const t=this.dbOffset+this._vectorCount*this.dimensions*4;let r=t;for(const s of e)new Float32Array(this.memory.buffer,r,this.dimensions).set(s),r+=this.dimensions*4;return this._vectorCount+=e.length,t}loadVectorBytes(e,t){new Uint8Array(this.memory.buffer,this.dbOffset,e.byteLength).set(e),this._vectorCount=t}readScores(){return new Float32Array(this.memory.buffer,this.scoresOffset,this._vectorCount)}readVector(e){const t=this.dbOffset+e*this.dimensions*4;return new Float32Array(this.memory.buffer,t,this.dimensions)}writeVector(e,t){const r=this.dbOffset+e*this.dimensions*4;new Float32Array(this.memory.buffer,r,this.dimensions).set(t)}reset(){this._vectorCount=0}}const V="AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL";function D(){const i=atob(V),e=new Uint8Array(i.length);for(let t=0;t<i.length;t++)e[t]=i.charCodeAt(t);return e}async function T(i,e){const t={env:{memory:e}};return(await WebAssembly.instantiate(i,t)).instance.exports}const B="vectors.bin",M="keys.bin";class C{memoryManager;storage;dimensions;shouldNormalize;wasmExports;keyToSlot;slotToKey;closed=!1;constructor(e,t,r,s,o,n,a){this.memoryManager=e,this.storage=t,this.dimensions=r,this.shouldNormalize=s,this.wasmExports=o,this.keyToSlot=n,this.slotToKey=a}static async open(e){const t=e.name??"default",r=e.storage??new E(t),s=e.normalize!==!1,[o,n]=await Promise.all([r.readAll(B),r.readAll(M)]),a=n.byteLength>0?x(n):[],c=o.byteLength/(e.dimensions*4),d=new Map,g=[];for(let f=0;f<a.length;f++)d.set(a[f],f),g[f]=a[f];const h=new F(e.dimensions,c);o.byteLength>0&&h.loadVectorBytes(o,c);let u=null;const w=e.wasmBinary!==void 0?e.wasmBinary:D();if(w!==null)try{u=await T(w,h.memory)}catch{}return new C(h,r,e.dimensions,s,u,d,g)}get size(){return this.keyToSlot.size}set(e,t,r){if(this.assertOpen(),t.length!==this.dimensions)throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${t.length}`);const s=new Float32Array(t);(r?.normalize??this.shouldNormalize)&&this.normalizeVector(s);const n=this.keyToSlot.get(e);if(n!==void 0)this.memoryManager.writeVector(n,s);else{if(this.memoryManager.vectorCount+1>this.memoryManager.maxVectors)throw new l(this.memoryManager.maxVectors);this.memoryManager.ensureCapacity(1);const c=this.memoryManager.vectorCount;this.memoryManager.appendVectors([s]),this.keyToSlot.set(e,c),this.slotToKey[c]=e}}get(e){this.assertOpen();const t=this.keyToSlot.get(e);if(t!==void 0)return new Float32Array(this.memoryManager.readVector(t))}setMany(e){for(const[t,r]of e)this.set(t,r)}getMany(e){return e.map(t=>this.get(t))}query(e,t){this.assertOpen();const r=t?.topK??this.size;if(this.size===0)return m.fromScores(new Float32Array(0),()=>"",0);if(e.length!==this.dimensions)throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${e.length}`);const s=new Float32Array(e);(t?.normalize??this.shouldNormalize)&&this.normalizeVector(s),this.memoryManager.writeQuery(s),this.memoryManager.ensureCapacity(0);const n=this.memoryManager.vectorCount,a=this.memoryManager.scoresOffset;if(this.wasmExports)this.wasmExports.search_all(this.memoryManager.queryOffset,this.memoryManager.dbOffset,a,n,this.dimensions);else{const h=new Float32Array(this.memoryManager.memory.buffer,this.memoryManager.queryOffset,this.dimensions),u=new Float32Array(this.memoryManager.memory.buffer,this.memoryManager.dbOffset,n*this.dimensions),w=new Float32Array(this.memoryManager.memory.buffer,a,n);p(h,u,w,n,this.dimensions)}const c=new Float32Array(this.memoryManager.readScores()),d=this.slotToKey,g=h=>d[h];return m.fromScores(c,g,r)}async flush(){this.assertOpen();const e=this.memoryManager.vectorCount,t=new Uint8Array(e*this.dimensions*4);if(e>0){const s=new Uint8Array(this.memoryManager.memory.buffer,this.memoryManager.dbOffset,e*this.dimensions*4);t.set(s)}const r=v(this.slotToKey);await Promise.all([this.storage.write(B,t),this.storage.write(M,r)])}async close(){this.closed||(await this.flush(),this.closed=!0)}async clear(){this.assertOpen(),this.keyToSlot.clear(),this.slotToKey.length=0,this.memoryManager.reset(),await this.storage.destroy()}normalizeVector(e){if(this.wasmExports){const t=this.memoryManager.queryOffset;new Float32Array(this.memoryManager.memory.buffer,t,e.length).set(e),this.wasmExports.normalize(t,e.length);const r=new Float32Array(this.memoryManager.memory.buffer,t,e.length);e.set(r)}else S(e)}assertOpen(){if(this.closed)throw new Error("VectorDB instance has been closed")}}A.DB=C,A.InMemoryStorageProvider=I,A.OPFSStorageProvider=E,A.ResultSet=m,A.VectorCapacityExceededError=l,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(A,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(A=typeof globalThis<"u"?globalThis:A||self,l(A.EigenDB={}))})(this,(function(A){"use strict";class l extends Error{constructor(e){super(`Capacity exceeded. Max vectors for this dimension size is ~${e}.`),this.name="VectorCapacityExceededError"}}class m{length;sortedIndices;scores;resolveKey;constructor(e,t,r,s){this.scores=e,this.sortedIndices=t,this.resolveKey=r,this.length=Math.min(s,t.length)}static fromScores(e,t,r){const s=e.length,o=new Uint32Array(s);for(let n=0;n<s;n++)o[n]=n;return o.sort((n,a)=>e[a]-e[n]),new m(e,o,t,r)}get(e){if(e<0||e>=this.length)throw new RangeError(`Rank ${e} out of bounds [0, ${this.length})`);const t=this.sortedIndices[e];return{key:this.resolveKey(t),score:this.scores[t]}}getPage(e,t){const r=e*t,s=Math.min(r+t,this.length),o=[];for(let n=r;n<s;n++)o.push(this.get(n));return o}}class E{dirHandle=null;dirName;constructor(e){this.dirName=e}async getDir(){if(!this.dirHandle){const e=await navigator.storage.getDirectory();this.dirHandle=await e.getDirectoryHandle(this.dirName,{create:!0})}return this.dirHandle}async readAll(e){try{const o=await(await(await(await this.getDir()).getFileHandle(e)).getFile()).arrayBuffer();return new Uint8Array(o)}catch{return new Uint8Array(0)}}async append(e,t){const s=await(await this.getDir()).getFileHandle(e,{create:!0}),o=await s.createWritable({keepExistingData:!0}),n=await s.getFile();await o.seek(n.size),await o.write(t),await o.close()}async write(e,t){const o=await(await(await this.getDir()).getFileHandle(e,{create:!0})).createWritable({keepExistingData:!1});await o.write(t),await o.close()}async destroy(){await(await navigator.storage.getDirectory()).removeEntry(this.dirName,{recursive:!0}),this.dirHandle=null}}class I{files=new Map;async readAll(e){const t=this.files.get(e);if(!t||t.length===0)return new Uint8Array(0);const r=t.reduce((n,a)=>n+a.byteLength,0),s=new Uint8Array(r);let o=0;for(const n of t)s.set(n,o),o+=n.byteLength;return s}async append(e,t){this.files.has(e)||this.files.set(e,[]),this.files.get(e).push(new Uint8Array(t))}async write(e,t){this.files.set(e,[new Uint8Array(t)])}async destroy(){this.files.clear()}}function S(i){let e=0;for(let s=0;s<i.length;s++)e+=i[s]*i[s];const t=Math.sqrt(e);if(t===0)return;const r=1/t;for(let s=0;s<i.length;s++)i[s]*=r}function p(i,e,t,r,s){for(let o=0;o<r;o++){let n=0;const a=o*s;for(let c=0;c<s;c++)n+=i[c]*e[a+c];t[o]=n}}const Q=new TextEncoder,O=new TextDecoder;function v(i){const e=i.map(a=>Q.encode(a)),t=e.reduce((a,c)=>a+4+c.byteLength,0),r=new ArrayBuffer(t),s=new DataView(r),o=new Uint8Array(r);let n=0;for(const a of e)s.setUint32(n,a.byteLength,!0),n+=4,o.set(a,n),n+=a.byteLength;return o}function x(i){const e=[],t=new DataView(i.buffer,i.byteOffset,i.byteLength);let r=0;for(;r<i.byteLength;){const s=t.getUint32(r,!0);r+=4;const o=O.decode(i.subarray(r,r+s));e.push(o),r+=s}return e}const y=65536,b=65536;class F{memory;dimensions;queryOffset;dbOffset;_vectorCount;constructor(e,t=0){this.dimensions=e,this.queryOffset=0;const r=e*4;this.dbOffset=Math.ceil(r/y)*y;const s=t*e*4,o=this.dbOffset+s,n=Math.max(1,Math.ceil(o/y));this.memory=new WebAssembly.Memory({initial:n}),this._vectorCount=t}get vectorCount(){return this._vectorCount}get scoresOffset(){return this.dbOffset+this._vectorCount*this.dimensions*4}get scoresBytes(){return this._vectorCount*4}get maxVectors(){const e=b*y-this.dbOffset,t=this.dimensions*4+4;return Math.floor(e/t)}ensureCapacity(e){const t=this._vectorCount+e,r=this.dbOffset+t*this.dimensions*4+t*4,s=this.memory.buffer.byteLength;if(r>s){const o=Math.ceil((r-s)/y);if(s/y+o>b)throw new Error("WASM memory limit exceeded");this.memory.grow(o)}}writeQuery(e){new Float32Array(this.memory.buffer,this.queryOffset,this.dimensions).set(e)}appendVectors(e){const t=this.dbOffset+this._vectorCount*this.dimensions*4;let r=t;for(const s of e)new Float32Array(this.memory.buffer,r,this.dimensions).set(s),r+=this.dimensions*4;return this._vectorCount+=e.length,t}loadVectorBytes(e,t){new Uint8Array(this.memory.buffer,this.dbOffset,e.byteLength).set(e),this._vectorCount=t}readScores(){return new Float32Array(this.memory.buffer,this.scoresOffset,this._vectorCount)}readVector(e){const t=this.dbOffset+e*this.dimensions*4;return new Float32Array(this.memory.buffer,t,this.dimensions)}writeVector(e,t){const r=this.dbOffset+e*this.dimensions*4;new Float32Array(this.memory.buffer,r,this.dimensions).set(t)}reset(){this._vectorCount=0}}const V="AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL";function D(){const i=atob(V),e=new Uint8Array(i.length);for(let t=0;t<i.length;t++)e[t]=i.charCodeAt(t);return e}async function T(i,e){const t={env:{memory:e}};return(await WebAssembly.instantiate(i,t)).instance.exports}const B="vectors.bin",M="keys.bin";class C{memoryManager;storage;dimensions;shouldNormalize;wasmExports;keyToSlot;slotToKey;closed=!1;constructor(e,t,r,s,o,n,a){this.memoryManager=e,this.storage=t,this.dimensions=r,this.shouldNormalize=s,this.wasmExports=o,this.keyToSlot=n,this.slotToKey=a}static async open(e){const t=e.name??"default",r=e.storage??new E(t),s=e.normalize!==!1,[o,n]=await Promise.all([r.readAll(B),r.readAll(M)]),a=n.byteLength>0?x(n):[],c=o.byteLength/(e.dimensions*4),d=new Map,g=[];for(let f=0;f<a.length;f++)d.set(a[f],f),g[f]=a[f];const h=new F(e.dimensions,c);o.byteLength>0&&h.loadVectorBytes(o,c);let u=null;const w=e.wasmBinary!==void 0?e.wasmBinary:D();if(w!==null)try{u=await T(w,h.memory)}catch{}return new C(h,r,e.dimensions,s,u,d,g)}get size(){return this.keyToSlot.size}set(e,t,r){if(this.assertOpen(),t.length!==this.dimensions)throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${t.length}`);const s=new Float32Array(t);(r?.normalize??this.shouldNormalize)&&this.normalizeVector(s);const n=this.keyToSlot.get(e);if(n!==void 0)this.memoryManager.writeVector(n,s);else{if(this.memoryManager.vectorCount+1>this.memoryManager.maxVectors)throw new l(this.memoryManager.maxVectors);this.memoryManager.ensureCapacity(1);const c=this.memoryManager.vectorCount;this.memoryManager.appendVectors([s]),this.keyToSlot.set(e,c),this.slotToKey[c]=e}}get(e){this.assertOpen();const t=this.keyToSlot.get(e);if(t!==void 0)return Array.from(this.memoryManager.readVector(t))}setMany(e){for(const[t,r]of e)this.set(t,r)}getMany(e){return e.map(t=>this.get(t))}query(e,t){this.assertOpen();const r=t?.topK??this.size;if(this.size===0)return m.fromScores(new Float32Array(0),()=>"",0);if(e.length!==this.dimensions)throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${e.length}`);const s=new Float32Array(e);(t?.normalize??this.shouldNormalize)&&this.normalizeVector(s),this.memoryManager.writeQuery(s),this.memoryManager.ensureCapacity(0);const n=this.memoryManager.vectorCount,a=this.memoryManager.scoresOffset;if(this.wasmExports)this.wasmExports.search_all(this.memoryManager.queryOffset,this.memoryManager.dbOffset,a,n,this.dimensions);else{const h=new Float32Array(this.memoryManager.memory.buffer,this.memoryManager.queryOffset,this.dimensions),u=new Float32Array(this.memoryManager.memory.buffer,this.memoryManager.dbOffset,n*this.dimensions),w=new Float32Array(this.memoryManager.memory.buffer,a,n);p(h,u,w,n,this.dimensions)}const c=new Float32Array(this.memoryManager.readScores()),d=this.slotToKey,g=h=>d[h];return m.fromScores(c,g,r)}async flush(){this.assertOpen();const e=this.memoryManager.vectorCount,t=new Uint8Array(e*this.dimensions*4);if(e>0){const s=new Uint8Array(this.memoryManager.memory.buffer,this.memoryManager.dbOffset,e*this.dimensions*4);t.set(s)}const r=v(this.slotToKey);await Promise.all([this.storage.write(B,t),this.storage.write(M,r)])}async close(){this.closed||(await this.flush(),this.closed=!0)}async clear(){this.assertOpen(),this.keyToSlot.clear(),this.slotToKey.length=0,this.memoryManager.reset(),await this.storage.destroy()}normalizeVector(e){if(this.wasmExports){const t=this.memoryManager.queryOffset;new Float32Array(this.memoryManager.memory.buffer,t,e.length).set(e),this.wasmExports.normalize(t,e.length);const r=new Float32Array(this.memoryManager.memory.buffer,t,e.length);e.set(r)}else S(e)}assertOpen(){if(this.closed)throw new Error("VectorDB instance has been closed")}}A.DB=C,A.InMemoryStorageProvider=I,A.OPFSStorageProvider=E,A.ResultSet=m,A.VectorCapacityExceededError=l,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=eigen-db.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"eigen-db.umd.cjs","sources":["../src/lib/errors.ts","../src/lib/result-set.ts","../src/lib/storage.ts","../src/lib/compute.ts","../src/lib/lexicon.ts","../src/lib/memory-manager.ts","../src/lib/simd-binary.ts","../src/lib/wasm-compute.ts","../src/lib/vector-db.ts"],"sourcesContent":["/**\n * Thrown when the database exceeds the 4GB WebAssembly 32-bit memory limit,\n * or the browser's available RAM.\n */\nexport class VectorCapacityExceededError extends Error {\n constructor(maxVectors: number) {\n super(`Capacity exceeded. Max vectors for this dimension size is ~${maxVectors}.`);\n this.name = \"VectorCapacityExceededError\";\n }\n}\n","/**\n * LAZY RESULT SET\n *\n * Holds pointers to sorted TypedArrays. Prevents JS heap overflow when K is massive.\n * Strings are only instantiated from the Lexicon when explicitly requested.\n */\n\nexport interface ResultItem {\n key: string;\n score: number;\n}\n\nexport type KeyResolver = (index: number) => string;\n\nexport class ResultSet {\n /** Total number of results */\n readonly length: number;\n\n /**\n * Sorted indices into the original database (by descending score).\n * sortedIndices[0] is the index of the best match.\n */\n private readonly sortedIndices: Uint32Array;\n\n /** Raw scores array (not sorted, indexed by original DB position) */\n private readonly scores: Float32Array;\n\n /** Function to lazily resolve key from the slot index */\n private readonly resolveKey: KeyResolver;\n\n constructor(\n scores: Float32Array,\n sortedIndices: Uint32Array,\n resolveKey: KeyResolver,\n topK: number,\n ) {\n this.scores = scores;\n this.sortedIndices = sortedIndices;\n this.resolveKey = resolveKey;\n this.length = Math.min(topK, sortedIndices.length);\n }\n\n /**\n * Sort scores and return a ResultSet with lazy key resolution.\n *\n * @param scores - Float32Array of scores (one per DB vector)\n * @param resolveKey - Function to resolve key by original index\n * @param topK - Maximum number of results to include\n */\n static fromScores(\n scores: Float32Array,\n resolveKey: KeyResolver,\n topK: number,\n ): ResultSet {\n const n = scores.length;\n\n // Create index array for sorting\n const indices = new Uint32Array(n);\n for (let i = 0; i < n; i++) indices[i] = i;\n\n // Sort indices by descending score\n indices.sort((a, b) => scores[b] - scores[a]);\n\n return new ResultSet(scores, indices, resolveKey, topK);\n }\n\n /** Fetch a single result by its rank (0 is best match) */\n get(rank: number): ResultItem {\n if (rank < 0 || rank >= this.length) {\n throw new RangeError(`Rank ${rank} out of bounds [0, ${this.length})`);\n }\n const dbIndex = this.sortedIndices[rank];\n return {\n key: this.resolveKey(dbIndex),\n score: this.scores[dbIndex],\n };\n }\n\n /** Helper for UI pagination. Instantiates strings only for the requested page. */\n getPage(page: number, pageSize: number): ResultItem[] {\n const start = page * pageSize;\n const end = Math.min(start + pageSize, this.length);\n const results: ResultItem[] = [];\n for (let i = start; i < end; i++) {\n results.push(this.get(i));\n }\n return results;\n }\n}\n","/**\n * Storage abstraction for append-only binary files.\n * Supports OPFS for browser and in-memory for testing.\n */\n\nexport interface StorageProvider {\n /** Read the entire contents of a file. Returns empty Uint8Array if file doesn't exist. */\n readAll(fileName: string): Promise<Uint8Array>;\n\n /** Append data to a file (creates if it doesn't exist). */\n append(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Write data to a file, replacing all existing content. */\n write(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Delete the storage directory and all files. */\n destroy(): Promise<void>;\n}\n\n/**\n * OPFS-backed storage provider for browser environments.\n * Uses Origin Private File System for high-performance persistent storage.\n */\nexport class OPFSStorageProvider implements StorageProvider {\n private dirHandle: FileSystemDirectoryHandle | null = null;\n private dirName: string;\n\n constructor(dirName: string) {\n this.dirName = dirName;\n }\n\n private async getDir(): Promise<FileSystemDirectoryHandle> {\n if (!this.dirHandle) {\n const root = await navigator.storage.getDirectory();\n this.dirHandle = await root.getDirectoryHandle(this.dirName, { create: true });\n }\n return this.dirHandle;\n }\n\n async readAll(fileName: string): Promise<Uint8Array> {\n try {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n const buffer = await file.arrayBuffer();\n return new Uint8Array(buffer);\n } catch {\n return new Uint8Array(0);\n }\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n const file = await fileHandle.getFile();\n await writable.seek(file.size);\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: false });\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async destroy(): Promise<void> {\n const root = await navigator.storage.getDirectory();\n await root.removeEntry(this.dirName, { recursive: true });\n this.dirHandle = null;\n }\n}\n\n/**\n * In-memory storage provider for testing.\n */\nexport class InMemoryStorageProvider implements StorageProvider {\n private files = new Map<string, Uint8Array[]>();\n\n async readAll(fileName: string): Promise<Uint8Array> {\n const chunks = this.files.get(fileName);\n if (!chunks || chunks.length === 0) return new Uint8Array(0);\n\n const totalSize = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const result = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n if (!this.files.has(fileName)) {\n this.files.set(fileName, []);\n }\n this.files.get(fileName)!.push(new Uint8Array(data));\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n this.files.set(fileName, [new Uint8Array(data)]);\n }\n\n async destroy(): Promise<void> {\n this.files.clear();\n }\n}\n","/**\n * Pure JavaScript compute functions for vector operations.\n * These serve as the reference implementation and fallback when WASM SIMD is unavailable.\n */\n\n/**\n * Normalizes a vector in-place to unit length.\n * After normalization, cosine similarity reduces to a simple dot product.\n */\nexport function normalize(vec: Float32Array): void {\n let sumSq = 0;\n for (let i = 0; i < vec.length; i++) {\n sumSq += vec[i] * vec[i];\n }\n const mag = Math.sqrt(sumSq);\n if (mag === 0) return;\n const invMag = 1 / mag;\n for (let i = 0; i < vec.length; i++) {\n vec[i] *= invMag;\n }\n}\n\n/**\n * Computes dot products of query against all vectors in the database.\n * Writes scores to the output array.\n *\n * @param query - Normalized query vector (length = dimensions)\n * @param db - Contiguous flat array of normalized vectors (length = dbSize * dimensions)\n * @param scores - Output array for dot product scores (length = dbSize)\n * @param dbSize - Number of vectors in the database\n * @param dimensions - Dimensionality of each vector\n */\nexport function searchAll(\n query: Float32Array,\n db: Float32Array,\n scores: Float32Array,\n dbSize: number,\n dimensions: number,\n): void {\n for (let i = 0; i < dbSize; i++) {\n let dot = 0;\n const offset = i * dimensions;\n for (let j = 0; j < dimensions; j++) {\n dot += query[j] * db[offset + j];\n }\n scores[i] = dot;\n }\n}\n","/**\n * Lexicon: length-prefixed UTF-8 encoding for text strings.\n *\n * Format: Each entry is [4-byte uint32 length][UTF-8 bytes]\n * This allows efficient sequential reading and appending.\n */\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encodes an array of strings into a length-prefixed binary format.\n */\nexport function encodeLexicon(texts: string[]): Uint8Array {\n const encoded = texts.map((t) => encoder.encode(t));\n const totalSize = encoded.reduce((sum, e) => sum + 4 + e.byteLength, 0);\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n const bytes = new Uint8Array(buffer);\n let offset = 0;\n\n for (const e of encoded) {\n view.setUint32(offset, e.byteLength, true); // little-endian\n offset += 4;\n bytes.set(e, offset);\n offset += e.byteLength;\n }\n\n return bytes;\n}\n\n/**\n * Decodes all strings from a length-prefixed binary buffer.\n */\nexport function decodeLexicon(data: Uint8Array): string[] {\n const result: string[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n const len = view.getUint32(offset, true);\n offset += 4;\n const text = decoder.decode(data.subarray(offset, offset + len));\n result.push(text);\n offset += len;\n }\n\n return result;\n}\n\n/**\n * Decodes a single string at a given index from the lexicon.\n * Returns the string and the byte offset of the next entry.\n */\nexport function decodeLexiconAt(data: Uint8Array, index: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n for (let i = 0; i < index; i++) {\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n const len = view.getUint32(offset, true);\n offset += 4;\n return decoder.decode(data.subarray(offset, offset + len));\n}\n\n/**\n * Builds an index of byte offsets for each entry in the lexicon.\n * Enables O(1) access to any entry by index.\n */\nexport function buildLexiconIndex(data: Uint8Array): Uint32Array {\n const offsets: number[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n offsets.push(offset);\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n return new Uint32Array(offsets);\n}\n\n/**\n * Decodes a string at a given byte offset in the lexicon.\n */\nexport function decodeLexiconAtOffset(data: Uint8Array, byteOffset: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const len = view.getUint32(byteOffset, true);\n return decoder.decode(data.subarray(byteOffset + 4, byteOffset + 4 + len));\n}\n","/**\n * Memory Manager for WASM shared memory.\n *\n * Memory Layout:\n * [ 0x00000 ] -> Query Vector Buffer (Fixed, dimensions * 4 bytes, aligned to 64KB page)\n * [ DB_OFFSET ] -> Vector Database (Grows dynamically)\n * [ Dynamic ] -> Scores Buffer (Mapped after DB during search)\n */\n\n/** WASM page size is 64KB */\nconst PAGE_SIZE = 65536;\n\n/** Maximum WASM memory: ~4GB (65536 pages of 64KB each) */\nconst MAX_PAGES = 65536;\n\nexport class MemoryManager {\n readonly memory: WebAssembly.Memory;\n readonly dimensions: number;\n readonly queryOffset: number;\n readonly dbOffset: number;\n private _vectorCount: number;\n\n constructor(dimensions: number, initialVectorCount: number = 0) {\n this.dimensions = dimensions;\n\n // Query buffer: dimensions * 4 bytes, aligned to page boundary\n this.queryOffset = 0;\n const queryBytes = dimensions * 4;\n this.dbOffset = Math.ceil(queryBytes / PAGE_SIZE) * PAGE_SIZE;\n\n // Calculate initial memory needed\n const dbBytes = initialVectorCount * dimensions * 4;\n const totalBytes = this.dbOffset + dbBytes;\n const initialPages = Math.max(1, Math.ceil(totalBytes / PAGE_SIZE));\n\n this.memory = new WebAssembly.Memory({ initial: initialPages });\n this._vectorCount = initialVectorCount;\n }\n\n /** Current number of vectors stored */\n get vectorCount(): number {\n return this._vectorCount;\n }\n\n /** Byte offset where the scores buffer starts (right after DB) */\n get scoresOffset(): number {\n return this.dbOffset + this._vectorCount * this.dimensions * 4;\n }\n\n /** Total bytes needed for scores buffer */\n get scoresBytes(): number {\n return this._vectorCount * 4;\n }\n\n /**\n * Maximum vectors that can be stored given the 4GB WASM memory limit.\n * Accounts for query buffer, DB space, and scores buffer.\n */\n get maxVectors(): number {\n const availableBytes = MAX_PAGES * PAGE_SIZE - this.dbOffset;\n // Each vector needs: dimensions * 4 bytes (DB) + 4 bytes (score)\n const bytesPerVector = this.dimensions * 4 + 4;\n return Math.floor(availableBytes / bytesPerVector);\n }\n\n /**\n * Ensures memory is large enough for the current DB + scores buffer.\n * Calls memory.grow() if needed.\n */\n ensureCapacity(additionalVectors: number): void {\n const newTotal = this._vectorCount + additionalVectors;\n const requiredBytes =\n this.dbOffset + newTotal * this.dimensions * 4 + newTotal * 4; // DB + scores\n const currentBytes = this.memory.buffer.byteLength;\n\n if (requiredBytes > currentBytes) {\n const pagesNeeded = Math.ceil((requiredBytes - currentBytes) / PAGE_SIZE);\n const currentPages = currentBytes / PAGE_SIZE;\n if (currentPages + pagesNeeded > MAX_PAGES) {\n throw new Error(\"WASM memory limit exceeded\");\n }\n this.memory.grow(pagesNeeded);\n }\n }\n\n /**\n * Write a query vector into the query buffer region.\n */\n writeQuery(vector: Float32Array): void {\n new Float32Array(this.memory.buffer, this.queryOffset, this.dimensions).set(vector);\n }\n\n /**\n * Append vectors to the database region.\n * Returns the byte offset where the new vectors were written.\n */\n appendVectors(vectors: Float32Array[]): number {\n const startOffset = this.dbOffset + this._vectorCount * this.dimensions * 4;\n let offset = startOffset;\n for (const vec of vectors) {\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vec);\n offset += this.dimensions * 4;\n }\n this._vectorCount += vectors.length;\n return startOffset;\n }\n\n /**\n * Load raw vector bytes directly into the database region.\n * Used for bulk loading from OPFS.\n */\n loadVectorBytes(data: Uint8Array, vectorCount: number): void {\n new Uint8Array(this.memory.buffer, this.dbOffset, data.byteLength).set(data);\n this._vectorCount = vectorCount;\n }\n\n /**\n * Read the scores buffer as a Float32Array view.\n */\n readScores(): Float32Array {\n return new Float32Array(this.memory.buffer, this.scoresOffset, this._vectorCount);\n }\n\n /**\n * Read the DB region for a specific vector index.\n */\n readVector(index: number): Float32Array {\n const offset = this.dbOffset + index * this.dimensions * 4;\n return new Float32Array(this.memory.buffer, offset, this.dimensions);\n }\n\n /**\n * Write a vector to a specific slot in the database region.\n */\n writeVector(index: number, vector: Float32Array): void {\n const offset = this.dbOffset + index * this.dimensions * 4;\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vector);\n }\n\n /**\n * Reset the vector count to zero, logically clearing the database.\n * WASM memory is not freed but will be overwritten on next writes.\n */\n reset(): void {\n this._vectorCount = 0;\n }\n}\n","// AUTO-GENERATED - Do not edit. Run: npx tsx scripts/compile-wat.ts\nconst SIMD_WASM_BASE64 = \"AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL\";\n\nexport function getSimdWasmBinary(): Uint8Array {\n const binaryString = atob(SIMD_WASM_BASE64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * WASM SIMD compute layer.\n * Compiles the hand-written WAT module and provides typed wrappers\n * that operate on shared WebAssembly.Memory.\n */\n\nexport interface WasmExports {\n normalize(ptr: number, dimensions: number): void;\n search_all(queryPtr: number, dbPtr: number, scoresPtr: number, dbSize: number, dimensions: number): void;\n}\n\n/**\n * Instantiates a WASM module with the given memory and returns typed exports.\n */\nexport async function instantiateWasm(wasmBinary: Uint8Array, memory: WebAssembly.Memory): Promise<WasmExports> {\n const importObject = { env: { memory } };\n const result = await WebAssembly.instantiate(wasmBinary, importObject);\n // WebAssembly.instantiate with a buffer returns { instance, module }\n const instance = (result as unknown as { instance: WebAssembly.Instance }).instance;\n return instance.exports as unknown as WasmExports;\n}\n","/**\n * VectorDB — Key-Value Vector Database\n *\n * Decoupled from embedding providers. Users pass pre-computed vectors\n * as Float32Array values with string keys.\n *\n * Supports:\n * - set/get/setMany/getMany for key-value CRUD\n * - query for similarity search (dot product on normalized vectors)\n * - flush to persist, close to flush+release, clear to wipe\n * - Last-write-wins semantics for duplicate keys (append-only storage)\n */\n\nimport { normalize, searchAll } from \"./compute\";\nimport { VectorCapacityExceededError } from \"./errors\";\nimport { encodeLexicon, decodeLexicon } from \"./lexicon\";\nimport { MemoryManager } from \"./memory-manager\";\nimport { ResultSet } from \"./result-set\";\nimport { getSimdWasmBinary } from \"./simd-binary\";\nimport type { StorageProvider } from \"./storage\";\nimport { OPFSStorageProvider } from \"./storage\";\nimport type { OpenOptions, OpenOptionsInternal, SetOptions, QueryOptions } from \"./types\";\nimport { instantiateWasm, type WasmExports } from \"./wasm-compute\";\n\nconst VECTORS_FILE = \"vectors.bin\";\nconst KEYS_FILE = \"keys.bin\";\n\nexport class VectorDB {\n private readonly memoryManager: MemoryManager;\n private readonly storage: StorageProvider;\n private readonly dimensions: number;\n private readonly shouldNormalize: boolean;\n private wasmExports: WasmExports | null;\n\n /** Maps key to its slot index in the vector array */\n private keyToSlot: Map<string, number>;\n\n /** Maps slot index back to its key */\n private slotToKey: string[];\n\n /** Whether this instance has been closed */\n private closed = false;\n\n private constructor(\n memoryManager: MemoryManager,\n storage: StorageProvider,\n dimensions: number,\n shouldNormalize: boolean,\n wasmExports: WasmExports | null,\n keyToSlot: Map<string, number>,\n slotToKey: string[],\n ) {\n this.memoryManager = memoryManager;\n this.storage = storage;\n this.dimensions = dimensions;\n this.shouldNormalize = shouldNormalize;\n this.wasmExports = wasmExports;\n this.keyToSlot = keyToSlot;\n this.slotToKey = slotToKey;\n }\n\n /**\n * Opens a VectorDB instance.\n * Loads existing data from storage into WASM memory.\n */\n static async open(options: OpenOptions): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB> {\n const name = options.name ?? \"default\";\n const storage = options.storage ?? new OPFSStorageProvider(name);\n const shouldNormalize = options.normalize !== false;\n\n // Load existing data from storage\n const [vectorBytes, keysBytes] = await Promise.all([\n storage.readAll(VECTORS_FILE),\n storage.readAll(KEYS_FILE),\n ]);\n\n // Decode stored keys\n const keys = keysBytes.byteLength > 0 ? decodeLexicon(keysBytes) : [];\n const vectorCount = vectorBytes.byteLength / (options.dimensions * 4);\n\n // Build key-to-slot mapping.\n // flush() always writes deduplicated state, so keys are unique on load.\n const keyToSlot = new Map<string, number>();\n const slotToKey: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n keyToSlot.set(keys[i], i);\n slotToKey[i] = keys[i];\n }\n\n // Initialize memory manager\n const mm = new MemoryManager(options.dimensions, vectorCount);\n\n if (vectorBytes.byteLength > 0) {\n mm.loadVectorBytes(vectorBytes, vectorCount);\n }\n\n // Try to instantiate WASM SIMD module\n let wasmExports: WasmExports | null = null;\n const wasmBinary = options.wasmBinary !== undefined ? options.wasmBinary : getSimdWasmBinary();\n if (wasmBinary !== null) {\n try {\n wasmExports = await instantiateWasm(wasmBinary, mm.memory);\n } catch {\n // Fall back to JS compute\n }\n }\n\n return new VectorDB(\n mm,\n storage,\n options.dimensions,\n shouldNormalize,\n wasmExports,\n keyToSlot,\n slotToKey,\n );\n }\n\n /** Total number of key-value pairs in the database */\n get size(): number {\n return this.keyToSlot.size;\n }\n\n /**\n * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).\n * The value is a Float32Array of length equal to the configured dimensions.\n */\n set(key: string, value: Float32Array, options?: SetOptions): void {\n this.assertOpen();\n\n if (value.length !== this.dimensions) {\n throw new Error(\n `Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,\n );\n }\n\n // Clone to avoid mutating caller's array during normalization\n const vec = new Float32Array(value);\n\n // Normalize if needed\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(vec);\n }\n\n const existingSlot = this.keyToSlot.get(key);\n if (existingSlot !== undefined) {\n // Overwrite existing slot\n this.memoryManager.writeVector(existingSlot, vec);\n } else {\n // Append new entry\n const newTotal = this.memoryManager.vectorCount + 1;\n if (newTotal > this.memoryManager.maxVectors) {\n throw new VectorCapacityExceededError(this.memoryManager.maxVectors);\n }\n this.memoryManager.ensureCapacity(1);\n const slotIndex = this.memoryManager.vectorCount;\n this.memoryManager.appendVectors([vec]);\n this.keyToSlot.set(key, slotIndex);\n this.slotToKey[slotIndex] = key;\n }\n }\n\n /**\n * Get the stored vector for a key. Returns undefined if the key does not exist.\n * Returns a copy of the stored vector.\n */\n get(key: string): Float32Array | undefined {\n this.assertOpen();\n\n const slot = this.keyToSlot.get(key);\n if (slot === undefined) return undefined;\n\n // Return a copy so callers can't corrupt WASM memory\n return new Float32Array(this.memoryManager.readVector(slot));\n }\n\n /**\n * Set multiple key-value pairs at once. Last-write-wins applies within the batch.\n */\n setMany(entries: [string, Float32Array][]): void {\n for (const [key, value] of entries) {\n this.set(key, value);\n }\n }\n\n /**\n * Get vectors for multiple keys. Returns undefined for keys that don't exist.\n */\n getMany(keys: string[]): (Float32Array | undefined)[] {\n return keys.map((key) => this.get(key));\n }\n\n /**\n * Search for the most similar vectors to the given query vector.\n * Returns a ResultSet sorted by descending similarity score.\n */\n query(value: Float32Array, options?: QueryOptions): ResultSet {\n this.assertOpen();\n\n const k = options?.topK ?? this.size;\n\n if (this.size === 0) {\n return ResultSet.fromScores(new Float32Array(0), () => \"\", 0);\n }\n\n if (value.length !== this.dimensions) {\n throw new Error(\n `Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,\n );\n }\n\n // Clone and optionally normalize the query vector\n const queryVec = new Float32Array(value);\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(queryVec);\n }\n\n // Write query to WASM memory\n this.memoryManager.writeQuery(queryVec);\n\n // Ensure memory has space for scores buffer\n this.memoryManager.ensureCapacity(0);\n\n // Total vectors in memory\n const totalVectors = this.memoryManager.vectorCount;\n\n // Execute search\n const scoresOffset = this.memoryManager.scoresOffset;\n if (this.wasmExports) {\n this.wasmExports.search_all(\n this.memoryManager.queryOffset,\n this.memoryManager.dbOffset,\n scoresOffset,\n totalVectors,\n this.dimensions,\n );\n } else {\n const queryView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.queryOffset,\n this.dimensions,\n );\n const dbView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions,\n );\n const scoresView = new Float32Array(\n this.memoryManager.memory.buffer,\n scoresOffset,\n totalVectors,\n );\n searchAll(queryView, dbView, scoresView, totalVectors, this.dimensions);\n }\n\n // Read scores (make a copy so the buffer can be reused)\n const scores = new Float32Array(this.memoryManager.readScores());\n\n // Resolve key from slot index\n const slotToKey = this.slotToKey;\n const resolveKey = (slotIndex: number): string => {\n return slotToKey[slotIndex];\n };\n\n return ResultSet.fromScores(scores, resolveKey, k);\n }\n\n /**\n * Persist the current in-memory state to storage.\n */\n async flush(): Promise<void> {\n this.assertOpen();\n\n const totalVectors = this.memoryManager.vectorCount;\n\n // Serialize vectors from WASM memory\n const vectorBytes = new Uint8Array(\n totalVectors * this.dimensions * 4,\n );\n if (totalVectors > 0) {\n const src = new Uint8Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions * 4,\n );\n vectorBytes.set(src);\n }\n\n // Serialize keys using lexicon format\n const keysBytes = encodeLexicon(this.slotToKey);\n\n await Promise.all([\n this.storage.write(VECTORS_FILE, vectorBytes),\n this.storage.write(KEYS_FILE, keysBytes),\n ]);\n }\n\n /**\n * Flush data to storage and release the instance.\n * The instance cannot be used after close.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n await this.flush();\n this.closed = true;\n }\n\n /**\n * Clear all data from the database and storage.\n */\n async clear(): Promise<void> {\n this.assertOpen();\n\n this.keyToSlot.clear();\n this.slotToKey.length = 0;\n this.memoryManager.reset();\n\n await this.storage.destroy();\n }\n\n /**\n * Normalize a vector using WASM (if available) or JS fallback.\n */\n private normalizeVector(vec: Float32Array): void {\n if (this.wasmExports) {\n const ptr = this.memoryManager.queryOffset;\n new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length).set(vec);\n this.wasmExports.normalize(ptr, vec.length);\n const normalized = new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length);\n vec.set(normalized);\n } else {\n normalize(vec);\n }\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"VectorDB instance has been closed\");\n }\n }\n}\n"],"names":["VectorCapacityExceededError","maxVectors","ResultSet","scores","sortedIndices","resolveKey","topK","n","indices","i","a","b","rank","dbIndex","page","pageSize","start","end","results","OPFSStorageProvider","dirName","root","fileName","buffer","data","fileHandle","writable","file","InMemoryStorageProvider","chunks","totalSize","sum","c","result","offset","chunk","normalize","vec","sumSq","mag","invMag","searchAll","query","db","dbSize","dimensions","dot","j","encoder","decoder","encodeLexicon","texts","encoded","t","e","view","bytes","decodeLexicon","len","text","PAGE_SIZE","MAX_PAGES","MemoryManager","initialVectorCount","queryBytes","dbBytes","totalBytes","initialPages","availableBytes","bytesPerVector","additionalVectors","newTotal","requiredBytes","currentBytes","pagesNeeded","vector","vectors","startOffset","vectorCount","index","SIMD_WASM_BASE64","getSimdWasmBinary","binaryString","instantiateWasm","wasmBinary","memory","importObject","VECTORS_FILE","KEYS_FILE","VectorDB","memoryManager","storage","shouldNormalize","wasmExports","keyToSlot","slotToKey","options","name","vectorBytes","keysBytes","keys","mm","key","value","existingSlot","slotIndex","slot","entries","k","queryVec","totalVectors","scoresOffset","queryView","dbView","scoresView","src","ptr","normalized"],"mappings":"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"}
1
+ {"version":3,"file":"eigen-db.umd.cjs","sources":["../src/lib/errors.ts","../src/lib/result-set.ts","../src/lib/storage.ts","../src/lib/compute.ts","../src/lib/lexicon.ts","../src/lib/memory-manager.ts","../src/lib/simd-binary.ts","../src/lib/wasm-compute.ts","../src/lib/vector-db.ts"],"sourcesContent":["/**\n * Thrown when the database exceeds the 4GB WebAssembly 32-bit memory limit,\n * or the browser's available RAM.\n */\nexport class VectorCapacityExceededError extends Error {\n constructor(maxVectors: number) {\n super(`Capacity exceeded. Max vectors for this dimension size is ~${maxVectors}.`);\n this.name = \"VectorCapacityExceededError\";\n }\n}\n","/**\n * LAZY RESULT SET\n *\n * Holds pointers to sorted TypedArrays. Prevents JS heap overflow when K is massive.\n * Strings are only instantiated from the Lexicon when explicitly requested.\n */\n\nexport interface ResultItem {\n key: string;\n score: number;\n}\n\nexport type KeyResolver = (index: number) => string;\n\nexport class ResultSet {\n /** Total number of results */\n readonly length: number;\n\n /**\n * Sorted indices into the original database (by descending score).\n * sortedIndices[0] is the index of the best match.\n */\n private readonly sortedIndices: Uint32Array;\n\n /** Raw scores array (not sorted, indexed by original DB position) */\n private readonly scores: Float32Array;\n\n /** Function to lazily resolve key from the slot index */\n private readonly resolveKey: KeyResolver;\n\n constructor(\n scores: Float32Array,\n sortedIndices: Uint32Array,\n resolveKey: KeyResolver,\n topK: number,\n ) {\n this.scores = scores;\n this.sortedIndices = sortedIndices;\n this.resolveKey = resolveKey;\n this.length = Math.min(topK, sortedIndices.length);\n }\n\n /**\n * Sort scores and return a ResultSet with lazy key resolution.\n *\n * @param scores - Float32Array of scores (one per DB vector)\n * @param resolveKey - Function to resolve key by original index\n * @param topK - Maximum number of results to include\n */\n static fromScores(\n scores: Float32Array,\n resolveKey: KeyResolver,\n topK: number,\n ): ResultSet {\n const n = scores.length;\n\n // Create index array for sorting\n const indices = new Uint32Array(n);\n for (let i = 0; i < n; i++) indices[i] = i;\n\n // Sort indices by descending score\n indices.sort((a, b) => scores[b] - scores[a]);\n\n return new ResultSet(scores, indices, resolveKey, topK);\n }\n\n /** Fetch a single result by its rank (0 is best match) */\n get(rank: number): ResultItem {\n if (rank < 0 || rank >= this.length) {\n throw new RangeError(`Rank ${rank} out of bounds [0, ${this.length})`);\n }\n const dbIndex = this.sortedIndices[rank];\n return {\n key: this.resolveKey(dbIndex),\n score: this.scores[dbIndex],\n };\n }\n\n /** Helper for UI pagination. Instantiates strings only for the requested page. */\n getPage(page: number, pageSize: number): ResultItem[] {\n const start = page * pageSize;\n const end = Math.min(start + pageSize, this.length);\n const results: ResultItem[] = [];\n for (let i = start; i < end; i++) {\n results.push(this.get(i));\n }\n return results;\n }\n}\n","/**\n * Storage abstraction for append-only binary files.\n * Supports OPFS for browser and in-memory for testing.\n */\n\nexport interface StorageProvider {\n /** Read the entire contents of a file. Returns empty Uint8Array if file doesn't exist. */\n readAll(fileName: string): Promise<Uint8Array>;\n\n /** Append data to a file (creates if it doesn't exist). */\n append(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Write data to a file, replacing all existing content. */\n write(fileName: string, data: Uint8Array): Promise<void>;\n\n /** Delete the storage directory and all files. */\n destroy(): Promise<void>;\n}\n\n/**\n * OPFS-backed storage provider for browser environments.\n * Uses Origin Private File System for high-performance persistent storage.\n */\nexport class OPFSStorageProvider implements StorageProvider {\n private dirHandle: FileSystemDirectoryHandle | null = null;\n private dirName: string;\n\n constructor(dirName: string) {\n this.dirName = dirName;\n }\n\n private async getDir(): Promise<FileSystemDirectoryHandle> {\n if (!this.dirHandle) {\n const root = await navigator.storage.getDirectory();\n this.dirHandle = await root.getDirectoryHandle(this.dirName, { create: true });\n }\n return this.dirHandle;\n }\n\n async readAll(fileName: string): Promise<Uint8Array> {\n try {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n const buffer = await file.arrayBuffer();\n return new Uint8Array(buffer);\n } catch {\n return new Uint8Array(0);\n }\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: true });\n const file = await fileHandle.getFile();\n await writable.seek(file.size);\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n const dir = await this.getDir();\n const fileHandle = await dir.getFileHandle(fileName, { create: true });\n const writable = await fileHandle.createWritable({ keepExistingData: false });\n await writable.write(data as unknown as BufferSource);\n await writable.close();\n }\n\n async destroy(): Promise<void> {\n const root = await navigator.storage.getDirectory();\n await root.removeEntry(this.dirName, { recursive: true });\n this.dirHandle = null;\n }\n}\n\n/**\n * In-memory storage provider for testing.\n */\nexport class InMemoryStorageProvider implements StorageProvider {\n private files = new Map<string, Uint8Array[]>();\n\n async readAll(fileName: string): Promise<Uint8Array> {\n const chunks = this.files.get(fileName);\n if (!chunks || chunks.length === 0) return new Uint8Array(0);\n\n const totalSize = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const result = new Uint8Array(totalSize);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n }\n\n async append(fileName: string, data: Uint8Array): Promise<void> {\n if (!this.files.has(fileName)) {\n this.files.set(fileName, []);\n }\n this.files.get(fileName)!.push(new Uint8Array(data));\n }\n\n async write(fileName: string, data: Uint8Array): Promise<void> {\n this.files.set(fileName, [new Uint8Array(data)]);\n }\n\n async destroy(): Promise<void> {\n this.files.clear();\n }\n}\n","/**\n * Pure JavaScript compute functions for vector operations.\n * These serve as the reference implementation and fallback when WASM SIMD is unavailable.\n */\n\n/**\n * Normalizes a vector in-place to unit length.\n * After normalization, cosine similarity reduces to a simple dot product.\n */\nexport function normalize(vec: Float32Array): void {\n let sumSq = 0;\n for (let i = 0; i < vec.length; i++) {\n sumSq += vec[i] * vec[i];\n }\n const mag = Math.sqrt(sumSq);\n if (mag === 0) return;\n const invMag = 1 / mag;\n for (let i = 0; i < vec.length; i++) {\n vec[i] *= invMag;\n }\n}\n\n/**\n * Computes dot products of query against all vectors in the database.\n * Writes scores to the output array.\n *\n * @param query - Normalized query vector (length = dimensions)\n * @param db - Contiguous flat array of normalized vectors (length = dbSize * dimensions)\n * @param scores - Output array for dot product scores (length = dbSize)\n * @param dbSize - Number of vectors in the database\n * @param dimensions - Dimensionality of each vector\n */\nexport function searchAll(\n query: Float32Array,\n db: Float32Array,\n scores: Float32Array,\n dbSize: number,\n dimensions: number,\n): void {\n for (let i = 0; i < dbSize; i++) {\n let dot = 0;\n const offset = i * dimensions;\n for (let j = 0; j < dimensions; j++) {\n dot += query[j] * db[offset + j];\n }\n scores[i] = dot;\n }\n}\n","/**\n * Lexicon: length-prefixed UTF-8 encoding for text strings.\n *\n * Format: Each entry is [4-byte uint32 length][UTF-8 bytes]\n * This allows efficient sequential reading and appending.\n */\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encodes an array of strings into a length-prefixed binary format.\n */\nexport function encodeLexicon(texts: string[]): Uint8Array {\n const encoded = texts.map((t) => encoder.encode(t));\n const totalSize = encoded.reduce((sum, e) => sum + 4 + e.byteLength, 0);\n\n const buffer = new ArrayBuffer(totalSize);\n const view = new DataView(buffer);\n const bytes = new Uint8Array(buffer);\n let offset = 0;\n\n for (const e of encoded) {\n view.setUint32(offset, e.byteLength, true); // little-endian\n offset += 4;\n bytes.set(e, offset);\n offset += e.byteLength;\n }\n\n return bytes;\n}\n\n/**\n * Decodes all strings from a length-prefixed binary buffer.\n */\nexport function decodeLexicon(data: Uint8Array): string[] {\n const result: string[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n const len = view.getUint32(offset, true);\n offset += 4;\n const text = decoder.decode(data.subarray(offset, offset + len));\n result.push(text);\n offset += len;\n }\n\n return result;\n}\n\n/**\n * Decodes a single string at a given index from the lexicon.\n * Returns the string and the byte offset of the next entry.\n */\nexport function decodeLexiconAt(data: Uint8Array, index: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n for (let i = 0; i < index; i++) {\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n const len = view.getUint32(offset, true);\n offset += 4;\n return decoder.decode(data.subarray(offset, offset + len));\n}\n\n/**\n * Builds an index of byte offsets for each entry in the lexicon.\n * Enables O(1) access to any entry by index.\n */\nexport function buildLexiconIndex(data: Uint8Array): Uint32Array {\n const offsets: number[] = [];\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n let offset = 0;\n\n while (offset < data.byteLength) {\n offsets.push(offset);\n const len = view.getUint32(offset, true);\n offset += 4 + len;\n }\n\n return new Uint32Array(offsets);\n}\n\n/**\n * Decodes a string at a given byte offset in the lexicon.\n */\nexport function decodeLexiconAtOffset(data: Uint8Array, byteOffset: number): string {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const len = view.getUint32(byteOffset, true);\n return decoder.decode(data.subarray(byteOffset + 4, byteOffset + 4 + len));\n}\n","/**\n * Memory Manager for WASM shared memory.\n *\n * Memory Layout:\n * [ 0x00000 ] -> Query Vector Buffer (Fixed, dimensions * 4 bytes, aligned to 64KB page)\n * [ DB_OFFSET ] -> Vector Database (Grows dynamically)\n * [ Dynamic ] -> Scores Buffer (Mapped after DB during search)\n */\n\n/** WASM page size is 64KB */\nconst PAGE_SIZE = 65536;\n\n/** Maximum WASM memory: ~4GB (65536 pages of 64KB each) */\nconst MAX_PAGES = 65536;\n\nexport class MemoryManager {\n readonly memory: WebAssembly.Memory;\n readonly dimensions: number;\n readonly queryOffset: number;\n readonly dbOffset: number;\n private _vectorCount: number;\n\n constructor(dimensions: number, initialVectorCount: number = 0) {\n this.dimensions = dimensions;\n\n // Query buffer: dimensions * 4 bytes, aligned to page boundary\n this.queryOffset = 0;\n const queryBytes = dimensions * 4;\n this.dbOffset = Math.ceil(queryBytes / PAGE_SIZE) * PAGE_SIZE;\n\n // Calculate initial memory needed\n const dbBytes = initialVectorCount * dimensions * 4;\n const totalBytes = this.dbOffset + dbBytes;\n const initialPages = Math.max(1, Math.ceil(totalBytes / PAGE_SIZE));\n\n this.memory = new WebAssembly.Memory({ initial: initialPages });\n this._vectorCount = initialVectorCount;\n }\n\n /** Current number of vectors stored */\n get vectorCount(): number {\n return this._vectorCount;\n }\n\n /** Byte offset where the scores buffer starts (right after DB) */\n get scoresOffset(): number {\n return this.dbOffset + this._vectorCount * this.dimensions * 4;\n }\n\n /** Total bytes needed for scores buffer */\n get scoresBytes(): number {\n return this._vectorCount * 4;\n }\n\n /**\n * Maximum vectors that can be stored given the 4GB WASM memory limit.\n * Accounts for query buffer, DB space, and scores buffer.\n */\n get maxVectors(): number {\n const availableBytes = MAX_PAGES * PAGE_SIZE - this.dbOffset;\n // Each vector needs: dimensions * 4 bytes (DB) + 4 bytes (score)\n const bytesPerVector = this.dimensions * 4 + 4;\n return Math.floor(availableBytes / bytesPerVector);\n }\n\n /**\n * Ensures memory is large enough for the current DB + scores buffer.\n * Calls memory.grow() if needed.\n */\n ensureCapacity(additionalVectors: number): void {\n const newTotal = this._vectorCount + additionalVectors;\n const requiredBytes =\n this.dbOffset + newTotal * this.dimensions * 4 + newTotal * 4; // DB + scores\n const currentBytes = this.memory.buffer.byteLength;\n\n if (requiredBytes > currentBytes) {\n const pagesNeeded = Math.ceil((requiredBytes - currentBytes) / PAGE_SIZE);\n const currentPages = currentBytes / PAGE_SIZE;\n if (currentPages + pagesNeeded > MAX_PAGES) {\n throw new Error(\"WASM memory limit exceeded\");\n }\n this.memory.grow(pagesNeeded);\n }\n }\n\n /**\n * Write a query vector into the query buffer region.\n */\n writeQuery(vector: Float32Array): void {\n new Float32Array(this.memory.buffer, this.queryOffset, this.dimensions).set(vector);\n }\n\n /**\n * Append vectors to the database region.\n * Returns the byte offset where the new vectors were written.\n */\n appendVectors(vectors: Float32Array[]): number {\n const startOffset = this.dbOffset + this._vectorCount * this.dimensions * 4;\n let offset = startOffset;\n for (const vec of vectors) {\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vec);\n offset += this.dimensions * 4;\n }\n this._vectorCount += vectors.length;\n return startOffset;\n }\n\n /**\n * Load raw vector bytes directly into the database region.\n * Used for bulk loading from OPFS.\n */\n loadVectorBytes(data: Uint8Array, vectorCount: number): void {\n new Uint8Array(this.memory.buffer, this.dbOffset, data.byteLength).set(data);\n this._vectorCount = vectorCount;\n }\n\n /**\n * Read the scores buffer as a Float32Array view.\n */\n readScores(): Float32Array {\n return new Float32Array(this.memory.buffer, this.scoresOffset, this._vectorCount);\n }\n\n /**\n * Read the DB region for a specific vector index.\n */\n readVector(index: number): Float32Array {\n const offset = this.dbOffset + index * this.dimensions * 4;\n return new Float32Array(this.memory.buffer, offset, this.dimensions);\n }\n\n /**\n * Write a vector to a specific slot in the database region.\n */\n writeVector(index: number, vector: Float32Array): void {\n const offset = this.dbOffset + index * this.dimensions * 4;\n new Float32Array(this.memory.buffer, offset, this.dimensions).set(vector);\n }\n\n /**\n * Reset the vector count to zero, logically clearing the database.\n * WASM memory is not freed but will be overwritten on next writes.\n */\n reset(): void {\n this._vectorCount = 0;\n }\n}\n","// AUTO-GENERATED - Do not edit. Run: npx tsx scripts/compile-wat.ts\nconst SIMD_WASM_BASE64 = \"AGFzbQEAAAABDgJgAn9/AGAFf39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQcaAglub3JtYWxpemUAAApzZWFyY2hfYWxsAAEKsgQCtQIFAX8BewN9AXsDf/0MAAAAAAAAAAAAAAAAAAAAACEDIAFBfHEhCEEAIQICQANAIAIgCE8NASAAIAJBAnRqIQogAyAK/QAEACAK/QAEAP3mAf3kASEDIAJBBGohAgwACwsgA/0fACAD/R8BkiAD/R8CIAP9HwOSkiEEIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAEIAoqAgAgCioCAJSSIQQgCUEBaiEJDAALCyAEkSEFIAVDAAAAAFsEQA8LQwAAgD8gBZUhBiAG/RMhB0EAIQICQANAIAIgCE8NASAAIAJBAnRqIQogCiAK/QAEACAH/eYB/QsEACACQQRqIQIMAAsLIAghCQJAA0AgCSABTw0BIAAgCUECdGohCiAKIAoqAgAgBpQ4AgAgCUEBaiEJDAALCwv4AQQCfwF7AX0GfyAEQXxxIQogBEECdCEOQQAhBQJAA0AgBSADTw0BIAEgBSAObGohCf0MAAAAAAAAAAAAAAAAAAAAACEHQQAhBgJAA0AgBiAKTw0BIAAgBkECdGohDCAJIAZBAnRqIQ0gByAM/QAEACAN/QAEAP3mAf3kASEHIAZBBGohBgwACwsgB/0fACAH/R8BkiAH/R8CIAf9HwOSkiEIIAohCwJAA0AgCyAETw0BIAAgC0ECdGohDCAJIAtBAnRqIQ0gCCAMKgIAIA0qAgCUkiEIIAtBAWohCwwACwsgAiAFQQJ0aiAIOAIAIAVBAWohBQwACwsL\";\n\nexport function getSimdWasmBinary(): Uint8Array {\n const binaryString = atob(SIMD_WASM_BASE64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * WASM SIMD compute layer.\n * Compiles the hand-written WAT module and provides typed wrappers\n * that operate on shared WebAssembly.Memory.\n */\n\nexport interface WasmExports {\n normalize(ptr: number, dimensions: number): void;\n search_all(queryPtr: number, dbPtr: number, scoresPtr: number, dbSize: number, dimensions: number): void;\n}\n\n/**\n * Instantiates a WASM module with the given memory and returns typed exports.\n */\nexport async function instantiateWasm(wasmBinary: Uint8Array, memory: WebAssembly.Memory): Promise<WasmExports> {\n const importObject = { env: { memory } };\n const result = await WebAssembly.instantiate(wasmBinary, importObject);\n // WebAssembly.instantiate with a buffer returns { instance, module }\n const instance = (result as unknown as { instance: WebAssembly.Instance }).instance;\n return instance.exports as unknown as WasmExports;\n}\n","/**\n * VectorDB — Key-Value Vector Database\n *\n * Decoupled from embedding providers. Users pass pre-computed vectors\n * as number arrays (or Float32Array) with string keys.\n *\n * Supports:\n * - set/get/setMany/getMany for key-value CRUD\n * - query for similarity search (dot product on normalized vectors)\n * - flush to persist, close to flush+release, clear to wipe\n * - Last-write-wins semantics for duplicate keys (append-only storage)\n */\n\nimport { normalize, searchAll } from \"./compute\";\nimport { VectorCapacityExceededError } from \"./errors\";\nimport { decodeLexicon, encodeLexicon } from \"./lexicon\";\nimport { MemoryManager } from \"./memory-manager\";\nimport { ResultSet } from \"./result-set\";\nimport { getSimdWasmBinary } from \"./simd-binary\";\nimport type { StorageProvider } from \"./storage\";\nimport { OPFSStorageProvider } from \"./storage\";\nimport type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions, VectorInput } from \"./types\";\nimport { instantiateWasm, type WasmExports } from \"./wasm-compute\";\n\nconst VECTORS_FILE = \"vectors.bin\";\nconst KEYS_FILE = \"keys.bin\";\n\nexport class VectorDB {\n private readonly memoryManager: MemoryManager;\n private readonly storage: StorageProvider;\n private readonly dimensions: number;\n private readonly shouldNormalize: boolean;\n private wasmExports: WasmExports | null;\n\n /** Maps key to its slot index in the vector array */\n private keyToSlot: Map<string, number>;\n\n /** Maps slot index back to its key */\n private slotToKey: string[];\n\n /** Whether this instance has been closed */\n private closed = false;\n\n private constructor(\n memoryManager: MemoryManager,\n storage: StorageProvider,\n dimensions: number,\n shouldNormalize: boolean,\n wasmExports: WasmExports | null,\n keyToSlot: Map<string, number>,\n slotToKey: string[],\n ) {\n this.memoryManager = memoryManager;\n this.storage = storage;\n this.dimensions = dimensions;\n this.shouldNormalize = shouldNormalize;\n this.wasmExports = wasmExports;\n this.keyToSlot = keyToSlot;\n this.slotToKey = slotToKey;\n }\n\n /**\n * Opens a VectorDB instance.\n * Loads existing data from storage into WASM memory.\n */\n static async open(options: OpenOptions): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB>;\n static async open(options: OpenOptionsInternal): Promise<VectorDB> {\n const name = options.name ?? \"default\";\n const storage = options.storage ?? new OPFSStorageProvider(name);\n const shouldNormalize = options.normalize !== false;\n\n // Load existing data from storage\n const [vectorBytes, keysBytes] = await Promise.all([storage.readAll(VECTORS_FILE), storage.readAll(KEYS_FILE)]);\n\n // Decode stored keys\n const keys = keysBytes.byteLength > 0 ? decodeLexicon(keysBytes) : [];\n const vectorCount = vectorBytes.byteLength / (options.dimensions * 4);\n\n // Build key-to-slot mapping.\n // flush() always writes deduplicated state, so keys are unique on load.\n const keyToSlot = new Map<string, number>();\n const slotToKey: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n keyToSlot.set(keys[i], i);\n slotToKey[i] = keys[i];\n }\n\n // Initialize memory manager\n const mm = new MemoryManager(options.dimensions, vectorCount);\n\n if (vectorBytes.byteLength > 0) {\n mm.loadVectorBytes(vectorBytes, vectorCount);\n }\n\n // Try to instantiate WASM SIMD module\n let wasmExports: WasmExports | null = null;\n const wasmBinary = options.wasmBinary !== undefined ? options.wasmBinary : getSimdWasmBinary();\n if (wasmBinary !== null) {\n try {\n wasmExports = await instantiateWasm(wasmBinary, mm.memory);\n } catch {\n // Fall back to JS compute\n }\n }\n\n return new VectorDB(mm, storage, options.dimensions, shouldNormalize, wasmExports, keyToSlot, slotToKey);\n }\n\n /** Total number of key-value pairs in the database */\n get size(): number {\n return this.keyToSlot.size;\n }\n\n /**\n * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).\n * The value is a number[] or Float32Array of length equal to the configured dimensions.\n */\n set(key: string, value: VectorInput, options?: SetOptions): void {\n this.assertOpen();\n\n if (value.length !== this.dimensions) {\n throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);\n }\n\n // Convert to Float32Array (also clones to avoid mutating caller's array)\n const vec = new Float32Array(value);\n\n // Normalize if needed\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(vec);\n }\n\n const existingSlot = this.keyToSlot.get(key);\n if (existingSlot !== undefined) {\n // Overwrite existing slot\n this.memoryManager.writeVector(existingSlot, vec);\n } else {\n // Append new entry\n const newTotal = this.memoryManager.vectorCount + 1;\n if (newTotal > this.memoryManager.maxVectors) {\n throw new VectorCapacityExceededError(this.memoryManager.maxVectors);\n }\n this.memoryManager.ensureCapacity(1);\n const slotIndex = this.memoryManager.vectorCount;\n this.memoryManager.appendVectors([vec]);\n this.keyToSlot.set(key, slotIndex);\n this.slotToKey[slotIndex] = key;\n }\n }\n\n /**\n * Get the stored vector for a key. Returns undefined if the key does not exist.\n * Returns a copy of the stored vector as a plain number array.\n */\n get(key: string): number[] | undefined {\n this.assertOpen();\n\n const slot = this.keyToSlot.get(key);\n if (slot === undefined) return undefined;\n\n // Return a plain array copy so callers can't corrupt WASM memory\n return Array.from(this.memoryManager.readVector(slot));\n }\n\n /**\n * Set multiple key-value pairs at once. Last-write-wins applies within the batch.\n */\n setMany(entries: [string, VectorInput][]): void {\n for (const [key, value] of entries) {\n this.set(key, value);\n }\n }\n\n /**\n * Get vectors for multiple keys. Returns undefined for keys that don't exist.\n */\n getMany(keys: string[]): (number[] | undefined)[] {\n return keys.map((key) => this.get(key));\n }\n\n /**\n * Search for the most similar vectors to the given query vector.\n * Returns a ResultSet sorted by descending similarity score.\n */\n query(value: VectorInput, options?: QueryOptions): ResultSet {\n this.assertOpen();\n\n const k = options?.topK ?? this.size;\n\n if (this.size === 0) {\n return ResultSet.fromScores(new Float32Array(0), () => \"\", 0);\n }\n\n if (value.length !== this.dimensions) {\n throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);\n }\n\n // Convert to Float32Array and optionally normalize the query vector\n const queryVec = new Float32Array(value);\n const doNormalize = options?.normalize ?? this.shouldNormalize;\n if (doNormalize) {\n this.normalizeVector(queryVec);\n }\n\n // Write query to WASM memory\n this.memoryManager.writeQuery(queryVec);\n\n // Ensure memory has space for scores buffer\n this.memoryManager.ensureCapacity(0);\n\n // Total vectors in memory\n const totalVectors = this.memoryManager.vectorCount;\n\n // Execute search\n const scoresOffset = this.memoryManager.scoresOffset;\n if (this.wasmExports) {\n this.wasmExports.search_all(\n this.memoryManager.queryOffset,\n this.memoryManager.dbOffset,\n scoresOffset,\n totalVectors,\n this.dimensions,\n );\n } else {\n const queryView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.queryOffset,\n this.dimensions,\n );\n const dbView = new Float32Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions,\n );\n const scoresView = new Float32Array(this.memoryManager.memory.buffer, scoresOffset, totalVectors);\n searchAll(queryView, dbView, scoresView, totalVectors, this.dimensions);\n }\n\n // Read scores (make a copy so the buffer can be reused)\n const scores = new Float32Array(this.memoryManager.readScores());\n\n // Resolve key from slot index\n const slotToKey = this.slotToKey;\n const resolveKey = (slotIndex: number): string => {\n return slotToKey[slotIndex];\n };\n\n return ResultSet.fromScores(scores, resolveKey, k);\n }\n\n /**\n * Persist the current in-memory state to storage.\n */\n async flush(): Promise<void> {\n this.assertOpen();\n\n const totalVectors = this.memoryManager.vectorCount;\n\n // Serialize vectors from WASM memory\n const vectorBytes = new Uint8Array(totalVectors * this.dimensions * 4);\n if (totalVectors > 0) {\n const src = new Uint8Array(\n this.memoryManager.memory.buffer,\n this.memoryManager.dbOffset,\n totalVectors * this.dimensions * 4,\n );\n vectorBytes.set(src);\n }\n\n // Serialize keys using lexicon format\n const keysBytes = encodeLexicon(this.slotToKey);\n\n await Promise.all([this.storage.write(VECTORS_FILE, vectorBytes), this.storage.write(KEYS_FILE, keysBytes)]);\n }\n\n /**\n * Flush data to storage and release the instance.\n * The instance cannot be used after close.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n await this.flush();\n this.closed = true;\n }\n\n /**\n * Clear all data from the database and storage.\n */\n async clear(): Promise<void> {\n this.assertOpen();\n\n this.keyToSlot.clear();\n this.slotToKey.length = 0;\n this.memoryManager.reset();\n\n await this.storage.destroy();\n }\n\n /**\n * Normalize a vector using WASM (if available) or JS fallback.\n */\n private normalizeVector(vec: Float32Array): void {\n if (this.wasmExports) {\n const ptr = this.memoryManager.queryOffset;\n new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length).set(vec);\n this.wasmExports.normalize(ptr, vec.length);\n const normalized = new Float32Array(this.memoryManager.memory.buffer, ptr, vec.length);\n vec.set(normalized);\n } else {\n normalize(vec);\n }\n }\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"VectorDB instance has been closed\");\n }\n }\n}\n"],"names":["VectorCapacityExceededError","maxVectors","ResultSet","scores","sortedIndices","resolveKey","topK","n","indices","i","a","b","rank","dbIndex","page","pageSize","start","end","results","OPFSStorageProvider","dirName","root","fileName","buffer","data","fileHandle","writable","file","InMemoryStorageProvider","chunks","totalSize","sum","c","result","offset","chunk","normalize","vec","sumSq","mag","invMag","searchAll","query","db","dbSize","dimensions","dot","j","encoder","decoder","encodeLexicon","texts","encoded","t","e","view","bytes","decodeLexicon","len","text","PAGE_SIZE","MAX_PAGES","MemoryManager","initialVectorCount","queryBytes","dbBytes","totalBytes","initialPages","availableBytes","bytesPerVector","additionalVectors","newTotal","requiredBytes","currentBytes","pagesNeeded","vector","vectors","startOffset","vectorCount","index","SIMD_WASM_BASE64","getSimdWasmBinary","binaryString","instantiateWasm","wasmBinary","memory","importObject","VECTORS_FILE","KEYS_FILE","VectorDB","memoryManager","storage","shouldNormalize","wasmExports","keyToSlot","slotToKey","options","name","vectorBytes","keysBytes","keys","mm","key","value","existingSlot","slotIndex","slot","entries","k","queryVec","totalVectors","scoresOffset","queryView","dbView","scoresView","src","ptr","normalized"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eigen-db",
3
- "version": "2.1.2",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -58,7 +58,7 @@ describe("VectorDB", () => {
58
58
  wasmBinary,
59
59
  });
60
60
 
61
- db.set("a", new Float32Array([1, 2, 3, 4]));
61
+ db.set("a", [1, 2, 3, 4]);
62
62
  expect(db.size).toBe(1);
63
63
 
64
64
  const result = db.get("a");
@@ -77,8 +77,8 @@ describe("VectorDB", () => {
77
77
  wasmBinary,
78
78
  });
79
79
 
80
- db.set("a", new Float32Array([1, 0, 0, 0]));
81
- db.set("a", new Float32Array([0, 1, 0, 0]));
80
+ db.set("a", [1, 0, 0, 0]);
81
+ db.set("a", [0, 1, 0, 0]);
82
82
 
83
83
  expect(db.size).toBe(1); // only one entry
84
84
  const result = db.get("a");
@@ -94,9 +94,9 @@ describe("VectorDB", () => {
94
94
  wasmBinary,
95
95
  });
96
96
 
97
- db.set("key1", new Float32Array([1, 0, 0, 0]));
98
- db.set("key1", new Float32Array([0, 1, 0, 0]));
99
- db.set("key1", new Float32Array([0, 0, 1, 0]));
97
+ db.set("key1", [1, 0, 0, 0]);
98
+ db.set("key1", [0, 1, 0, 0]);
99
+ db.set("key1", [0, 0, 1, 0]);
100
100
 
101
101
  expect(db.size).toBe(1);
102
102
  const result = db.get("key1");
@@ -120,7 +120,7 @@ describe("VectorDB", () => {
120
120
  wasmBinary,
121
121
  });
122
122
 
123
- expect(() => db.set("a", new Float32Array(8))).toThrow("dimension mismatch");
123
+ expect(() => db.set("a", [1, 2, 3, 4, 5, 6, 7, 8])).toThrow("dimension mismatch");
124
124
  });
125
125
 
126
126
  // --- setMany and getMany ---
@@ -133,9 +133,9 @@ describe("VectorDB", () => {
133
133
  });
134
134
 
135
135
  db.setMany([
136
- ["a", new Float32Array([1, 0, 0, 0])],
137
- ["b", new Float32Array([0, 1, 0, 0])],
138
- ["c", new Float32Array([0, 0, 1, 0])],
136
+ ["a", [1, 0, 0, 0]],
137
+ ["b", [0, 1, 0, 0]],
138
+ ["c", [0, 0, 1, 0]],
139
139
  ]);
140
140
 
141
141
  expect(db.size).toBe(3);
@@ -156,8 +156,8 @@ describe("VectorDB", () => {
156
156
  });
157
157
 
158
158
  db.setMany([
159
- ["x", new Float32Array([1, 0, 0, 0])],
160
- ["x", new Float32Array([0, 0, 0, 1])],
159
+ ["x", [1, 0, 0, 0]],
160
+ ["x", [0, 0, 0, 1]],
161
161
  ]);
162
162
 
163
163
  expect(db.size).toBe(1);
@@ -173,11 +173,11 @@ describe("VectorDB", () => {
173
173
  wasmBinary,
174
174
  });
175
175
 
176
- db.set("x-axis", new Float32Array([1, 0, 0, 0]));
177
- db.set("y-axis", new Float32Array([0, 1, 0, 0]));
178
- db.set("xy-axis", new Float32Array([1, 1, 0, 0]));
176
+ db.set("x-axis", [1, 0, 0, 0]);
177
+ db.set("y-axis", [0, 1, 0, 0]);
178
+ db.set("xy-axis", [1, 1, 0, 0]);
179
179
 
180
- const results = db.query(new Float32Array([1, 0, 0, 0]));
180
+ const results = db.query([1, 0, 0, 0]);
181
181
  expect(results.length).toBe(3);
182
182
 
183
183
  // x-axis should be the best match (identical direction)
@@ -200,11 +200,11 @@ describe("VectorDB", () => {
200
200
  wasmBinary,
201
201
  });
202
202
 
203
- db.set("a", new Float32Array([1, 0, 0, 0]));
204
- db.set("b", new Float32Array([0, 1, 0, 0]));
205
- db.set("c", new Float32Array([0, 0, 1, 0]));
203
+ db.set("a", [1, 0, 0, 0]);
204
+ db.set("b", [0, 1, 0, 0]);
205
+ db.set("c", [0, 0, 1, 0]);
206
206
 
207
- const results = db.query(new Float32Array([1, 0, 0, 0]), { topK: 2 });
207
+ const results = db.query([1, 0, 0, 0], { topK: 2 });
208
208
  expect(results.length).toBe(2);
209
209
  });
210
210
 
@@ -215,7 +215,7 @@ describe("VectorDB", () => {
215
215
  wasmBinary,
216
216
  });
217
217
 
218
- const results = db.query(new Float32Array([1, 0, 0, 0]));
218
+ const results = db.query([1, 0, 0, 0]);
219
219
  expect(results.length).toBe(0);
220
220
  });
221
221
 
@@ -226,8 +226,8 @@ describe("VectorDB", () => {
226
226
  wasmBinary,
227
227
  });
228
228
 
229
- db.set("a", new Float32Array([1, 0, 0, 0]));
230
- expect(() => db.query(new Float32Array(8))).toThrow("dimension mismatch");
229
+ db.set("a", [1, 0, 0, 0]);
230
+ expect(() => db.query([1, 2, 3, 4, 5, 6, 7, 8])).toThrow("dimension mismatch");
231
231
  });
232
232
 
233
233
  it("query results support pagination", async () => {
@@ -239,12 +239,12 @@ describe("VectorDB", () => {
239
239
  });
240
240
 
241
241
  for (let i = 0; i < 5; i++) {
242
- const vec = new Float32Array(4);
242
+ const vec = [0, 0, 0, 0];
243
243
  vec[0] = 1 - i * 0.2;
244
244
  db.set(`t${i}`, vec);
245
245
  }
246
246
 
247
- const results = db.query(new Float32Array([1, 0, 0, 0]), { normalize: false });
247
+ const results = db.query([1, 0, 0, 0], { normalize: false });
248
248
 
249
249
  const page0 = results.getPage(0, 2);
250
250
  expect(page0).toHaveLength(2);
@@ -263,13 +263,13 @@ describe("VectorDB", () => {
263
263
  wasmBinary,
264
264
  });
265
265
 
266
- db.set("point", new Float32Array([1, 0, 0, 0]));
267
- db.set("other", new Float32Array([0, 1, 0, 0]));
266
+ db.set("point", [1, 0, 0, 0]);
267
+ db.set("other", [0, 1, 0, 0]);
268
268
 
269
269
  // Overwrite 'point' to be along y-axis
270
- db.set("point", new Float32Array([0, 1, 0, 0]));
270
+ db.set("point", [0, 1, 0, 0]);
271
271
 
272
- const results = db.query(new Float32Array([0, 1, 0, 0]));
272
+ const results = db.query([0, 1, 0, 0]);
273
273
  // Both 'point' and 'other' are now along y-axis, so both should score high
274
274
  expect(results.get(0).score).toBeCloseTo(1.0, 2);
275
275
  expect(results.get(1).score).toBeCloseTo(1.0, 2);
@@ -285,8 +285,8 @@ describe("VectorDB", () => {
285
285
  wasmBinary,
286
286
  });
287
287
 
288
- db1.set("alpha", new Float32Array([1, 0, 0, 0]));
289
- db1.set("beta", new Float32Array([0, 1, 0, 0]));
288
+ db1.set("alpha", [1, 0, 0, 0]);
289
+ db1.set("beta", [0, 1, 0, 0]);
290
290
  await db1.flush();
291
291
 
292
292
  // Reopen same storage
@@ -310,8 +310,8 @@ describe("VectorDB", () => {
310
310
  wasmBinary,
311
311
  });
312
312
 
313
- db1.set("key", new Float32Array([1, 0, 0, 0]));
314
- db1.set("key", new Float32Array([0, 0, 0, 1]));
313
+ db1.set("key", [1, 0, 0, 0]);
314
+ db1.set("key", [0, 0, 0, 1]);
315
315
  await db1.flush();
316
316
 
317
317
  const db2 = await VectorDB.open({
@@ -334,7 +334,7 @@ describe("VectorDB", () => {
334
334
  wasmBinary,
335
335
  });
336
336
 
337
- db.set("a", new Float32Array([1, 0, 0, 0]));
337
+ db.set("a", [1, 0, 0, 0]);
338
338
  await db.close();
339
339
 
340
340
  // Should be persisted
@@ -347,7 +347,7 @@ describe("VectorDB", () => {
347
347
  expect(db2.size).toBe(1);
348
348
 
349
349
  // Original instance should be closed
350
- expect(() => db.set("b", new Float32Array(4))).toThrow("closed");
350
+ expect(() => db.set("b", [0, 0, 0, 0])).toThrow("closed");
351
351
  });
352
352
 
353
353
  it("close is idempotent", async () => {
@@ -369,8 +369,8 @@ describe("VectorDB", () => {
369
369
  wasmBinary,
370
370
  });
371
371
 
372
- db.set("a", new Float32Array([1, 0, 0, 0]));
373
- db.set("b", new Float32Array([0, 1, 0, 0]));
372
+ db.set("a", [1, 0, 0, 0]);
373
+ db.set("b", [0, 1, 0, 0]);
374
374
  expect(db.size).toBe(2);
375
375
 
376
376
  await db.clear();
@@ -386,10 +386,10 @@ describe("VectorDB", () => {
386
386
  wasmBinary,
387
387
  });
388
388
 
389
- db.set("old", new Float32Array([1, 0, 0, 0]));
389
+ db.set("old", [1, 0, 0, 0]);
390
390
  await db.clear();
391
391
 
392
- db.set("new", new Float32Array([0, 1, 0, 0]));
392
+ db.set("new", [0, 1, 0, 0]);
393
393
  expect(db.size).toBe(1);
394
394
  expect(db.get("new")![1]).toBeCloseTo(1);
395
395
  });
@@ -402,7 +402,7 @@ describe("VectorDB", () => {
402
402
  wasmBinary,
403
403
  });
404
404
 
405
- db.set("a", new Float32Array([3, 0, 0, 0]));
405
+ db.set("a", [3, 0, 0, 0]);
406
406
  const result = db.get("a");
407
407
  // Should be normalized to unit length
408
408
  expect(result![0]).toBeCloseTo(1.0);
@@ -416,7 +416,7 @@ describe("VectorDB", () => {
416
416
  wasmBinary,
417
417
  });
418
418
 
419
- db.set("a", new Float32Array([3, 0, 0, 0]));
419
+ db.set("a", [3, 0, 0, 0]);
420
420
  const result = db.get("a");
421
421
  expect(result![0]).toBeCloseTo(3.0);
422
422
  });
@@ -429,7 +429,7 @@ describe("VectorDB", () => {
429
429
  wasmBinary,
430
430
  });
431
431
 
432
- db.set("a", new Float32Array([3, 0, 0, 0]), { normalize: true });
432
+ db.set("a", [3, 0, 0, 0], { normalize: true });
433
433
  const result = db.get("a");
434
434
  expect(result![0]).toBeCloseTo(1.0);
435
435
  });
package/src/lib/index.ts CHANGED
@@ -10,5 +10,5 @@ export { ResultSet } from "./result-set";
10
10
  export type { ResultItem } from "./result-set";
11
11
  export { InMemoryStorageProvider, OPFSStorageProvider } from "./storage";
12
12
  export type { StorageProvider } from "./storage";
13
- export type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions } from "./types";
13
+ export type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions, VectorInput } from "./types";
14
14
  export { VectorDB as DB } from "./vector-db";
package/src/lib/types.ts CHANGED
@@ -20,6 +20,12 @@ export interface OpenOptionsInternal extends OpenOptions {
20
20
  wasmBinary?: Uint8Array | null;
21
21
  }
22
22
 
23
+ /**
24
+ * Accepted vector input types. Users can pass a plain number[] or a Float32Array.
25
+ * Internally converted to Float32Array for WASM operations.
26
+ */
27
+ export type VectorInput = number[] | Float32Array;
28
+
23
29
  /**
24
30
  * Options for set/setMany operations.
25
31
  */
@@ -2,7 +2,7 @@
2
2
  * VectorDB — Key-Value Vector Database
3
3
  *
4
4
  * Decoupled from embedding providers. Users pass pre-computed vectors
5
- * as Float32Array values with string keys.
5
+ * as number arrays (or Float32Array) with string keys.
6
6
  *
7
7
  * Supports:
8
8
  * - set/get/setMany/getMany for key-value CRUD
@@ -13,13 +13,13 @@
13
13
 
14
14
  import { normalize, searchAll } from "./compute";
15
15
  import { VectorCapacityExceededError } from "./errors";
16
- import { encodeLexicon, decodeLexicon } from "./lexicon";
16
+ import { decodeLexicon, encodeLexicon } from "./lexicon";
17
17
  import { MemoryManager } from "./memory-manager";
18
18
  import { ResultSet } from "./result-set";
19
19
  import { getSimdWasmBinary } from "./simd-binary";
20
20
  import type { StorageProvider } from "./storage";
21
21
  import { OPFSStorageProvider } from "./storage";
22
- import type { OpenOptions, OpenOptionsInternal, SetOptions, QueryOptions } from "./types";
22
+ import type { OpenOptions, OpenOptionsInternal, QueryOptions, SetOptions, VectorInput } from "./types";
23
23
  import { instantiateWasm, type WasmExports } from "./wasm-compute";
24
24
 
25
25
  const VECTORS_FILE = "vectors.bin";
@@ -71,10 +71,7 @@ export class VectorDB {
71
71
  const shouldNormalize = options.normalize !== false;
72
72
 
73
73
  // Load existing data from storage
74
- const [vectorBytes, keysBytes] = await Promise.all([
75
- storage.readAll(VECTORS_FILE),
76
- storage.readAll(KEYS_FILE),
77
- ]);
74
+ const [vectorBytes, keysBytes] = await Promise.all([storage.readAll(VECTORS_FILE), storage.readAll(KEYS_FILE)]);
78
75
 
79
76
  // Decode stored keys
80
77
  const keys = keysBytes.byteLength > 0 ? decodeLexicon(keysBytes) : [];
@@ -108,15 +105,7 @@ export class VectorDB {
108
105
  }
109
106
  }
110
107
 
111
- return new VectorDB(
112
- mm,
113
- storage,
114
- options.dimensions,
115
- shouldNormalize,
116
- wasmExports,
117
- keyToSlot,
118
- slotToKey,
119
- );
108
+ return new VectorDB(mm, storage, options.dimensions, shouldNormalize, wasmExports, keyToSlot, slotToKey);
120
109
  }
121
110
 
122
111
  /** Total number of key-value pairs in the database */
@@ -126,18 +115,16 @@ export class VectorDB {
126
115
 
127
116
  /**
128
117
  * Set a key-value pair. If the key already exists, its vector is overwritten (last-write-wins).
129
- * The value is a Float32Array of length equal to the configured dimensions.
118
+ * The value is a number[] or Float32Array of length equal to the configured dimensions.
130
119
  */
131
- set(key: string, value: Float32Array, options?: SetOptions): void {
120
+ set(key: string, value: VectorInput, options?: SetOptions): void {
132
121
  this.assertOpen();
133
122
 
134
123
  if (value.length !== this.dimensions) {
135
- throw new Error(
136
- `Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,
137
- );
124
+ throw new Error(`Vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);
138
125
  }
139
126
 
140
- // Clone to avoid mutating caller's array during normalization
127
+ // Convert to Float32Array (also clones to avoid mutating caller's array)
141
128
  const vec = new Float32Array(value);
142
129
 
143
130
  // Normalize if needed
@@ -166,22 +153,22 @@ export class VectorDB {
166
153
 
167
154
  /**
168
155
  * Get the stored vector for a key. Returns undefined if the key does not exist.
169
- * Returns a copy of the stored vector.
156
+ * Returns a copy of the stored vector as a plain number array.
170
157
  */
171
- get(key: string): Float32Array | undefined {
158
+ get(key: string): number[] | undefined {
172
159
  this.assertOpen();
173
160
 
174
161
  const slot = this.keyToSlot.get(key);
175
162
  if (slot === undefined) return undefined;
176
163
 
177
- // Return a copy so callers can't corrupt WASM memory
178
- return new Float32Array(this.memoryManager.readVector(slot));
164
+ // Return a plain array copy so callers can't corrupt WASM memory
165
+ return Array.from(this.memoryManager.readVector(slot));
179
166
  }
180
167
 
181
168
  /**
182
169
  * Set multiple key-value pairs at once. Last-write-wins applies within the batch.
183
170
  */
184
- setMany(entries: [string, Float32Array][]): void {
171
+ setMany(entries: [string, VectorInput][]): void {
185
172
  for (const [key, value] of entries) {
186
173
  this.set(key, value);
187
174
  }
@@ -190,7 +177,7 @@ export class VectorDB {
190
177
  /**
191
178
  * Get vectors for multiple keys. Returns undefined for keys that don't exist.
192
179
  */
193
- getMany(keys: string[]): (Float32Array | undefined)[] {
180
+ getMany(keys: string[]): (number[] | undefined)[] {
194
181
  return keys.map((key) => this.get(key));
195
182
  }
196
183
 
@@ -198,7 +185,7 @@ export class VectorDB {
198
185
  * Search for the most similar vectors to the given query vector.
199
186
  * Returns a ResultSet sorted by descending similarity score.
200
187
  */
201
- query(value: Float32Array, options?: QueryOptions): ResultSet {
188
+ query(value: VectorInput, options?: QueryOptions): ResultSet {
202
189
  this.assertOpen();
203
190
 
204
191
  const k = options?.topK ?? this.size;
@@ -208,12 +195,10 @@ export class VectorDB {
208
195
  }
209
196
 
210
197
  if (value.length !== this.dimensions) {
211
- throw new Error(
212
- `Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`,
213
- );
198
+ throw new Error(`Query vector dimension mismatch: expected ${this.dimensions}, got ${value.length}`);
214
199
  }
215
200
 
216
- // Clone and optionally normalize the query vector
201
+ // Convert to Float32Array and optionally normalize the query vector
217
202
  const queryVec = new Float32Array(value);
218
203
  const doNormalize = options?.normalize ?? this.shouldNormalize;
219
204
  if (doNormalize) {
@@ -250,11 +235,7 @@ export class VectorDB {
250
235
  this.memoryManager.dbOffset,
251
236
  totalVectors * this.dimensions,
252
237
  );
253
- const scoresView = new Float32Array(
254
- this.memoryManager.memory.buffer,
255
- scoresOffset,
256
- totalVectors,
257
- );
238
+ const scoresView = new Float32Array(this.memoryManager.memory.buffer, scoresOffset, totalVectors);
258
239
  searchAll(queryView, dbView, scoresView, totalVectors, this.dimensions);
259
240
  }
260
241
 
@@ -279,9 +260,7 @@ export class VectorDB {
279
260
  const totalVectors = this.memoryManager.vectorCount;
280
261
 
281
262
  // Serialize vectors from WASM memory
282
- const vectorBytes = new Uint8Array(
283
- totalVectors * this.dimensions * 4,
284
- );
263
+ const vectorBytes = new Uint8Array(totalVectors * this.dimensions * 4);
285
264
  if (totalVectors > 0) {
286
265
  const src = new Uint8Array(
287
266
  this.memoryManager.memory.buffer,
@@ -294,10 +273,7 @@ export class VectorDB {
294
273
  // Serialize keys using lexicon format
295
274
  const keysBytes = encodeLexicon(this.slotToKey);
296
275
 
297
- await Promise.all([
298
- this.storage.write(VECTORS_FILE, vectorBytes),
299
- this.storage.write(KEYS_FILE, keysBytes),
300
- ]);
276
+ await Promise.all([this.storage.write(VECTORS_FILE, vectorBytes), this.storage.write(KEYS_FILE, keysBytes)]);
301
277
  }
302
278
 
303
279
  /**