@brainwires/idbvec 0.1.0 → 0.2.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.
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "idbvec",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "nightness"
6
+ ],
7
+ "description": "Client-side vector database built on IndexedDB with WASM",
8
+ "version": "0.2.0",
9
+ "license": "MIT OR Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/Brainwires/idbvec"
13
+ },
14
+ "files": [
15
+ "idbvec_bg.wasm",
16
+ "idbvec.js",
17
+ "idbvec_bg.js",
18
+ "idbvec.d.ts",
19
+ "LICENSE-APACHE",
20
+ "LICENSE-MIT"
21
+ ],
22
+ "main": "idbvec.js",
23
+ "types": "idbvec.d.ts",
24
+ "sideEffects": [
25
+ "./idbvec.js",
26
+ "./snippets/*"
27
+ ],
28
+ "keywords": [
29
+ "vector",
30
+ "database",
31
+ "wasm",
32
+ "indexeddb",
33
+ "hnsw"
34
+ ]
35
+ }
package/wrapper.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export interface SearchResult {
10
10
  id: string;
11
- score: number;
11
+ distance: number;
12
12
  metadata?: Record<string, string>;
13
13
  }
14
14
  export interface VectorRecord {
@@ -16,15 +16,22 @@ export interface VectorRecord {
16
16
  vector: Float32Array;
17
17
  metadata?: Record<string, string>;
18
18
  }
19
+ export interface GetResult {
20
+ id: string;
21
+ vector: Float32Array;
22
+ metadata?: Record<string, string>;
23
+ }
19
24
  export interface SearchOptions {
20
25
  k?: number;
21
26
  ef?: number;
22
27
  }
28
+ export type DistanceMetric = 'euclidean' | 'cosine' | 'dotproduct';
23
29
  export interface VectorDBConfig {
24
30
  name: string;
25
31
  dimensions: number;
26
32
  m?: number;
27
33
  efConstruction?: number;
34
+ metric?: DistanceMetric;
28
35
  }
29
36
  /**
30
37
  * Vector Database with IndexedDB persistence
@@ -33,6 +40,8 @@ export declare class VectorDatabase {
33
40
  private wasmDB;
34
41
  private idb;
35
42
  private config;
43
+ private saveTimer;
44
+ private saveDebounceMs;
36
45
  constructor(config: VectorDBConfig);
37
46
  /**
38
47
  * Initialize the database (load WASM + IndexedDB)
@@ -50,10 +59,26 @@ export declare class VectorDatabase {
50
59
  * Search for nearest neighbors
51
60
  */
52
61
  search(query: Float32Array, options?: SearchOptions): Promise<SearchResult[]>;
62
+ /**
63
+ * Get a vector and its metadata by ID
64
+ */
65
+ get(id: string): Promise<GetResult | null>;
66
+ /**
67
+ * Check if a vector exists by ID
68
+ */
69
+ has(id: string): boolean;
70
+ /**
71
+ * List all vector IDs
72
+ */
73
+ listIds(): string[];
53
74
  /**
54
75
  * Delete a vector by ID
55
76
  */
56
77
  delete(id: string): Promise<boolean>;
78
+ /**
79
+ * Delete multiple vectors by ID
80
+ */
81
+ deleteBatch(ids: string[]): Promise<number>;
57
82
  /**
58
83
  * Get total number of vectors
59
84
  */
@@ -62,10 +87,30 @@ export declare class VectorDatabase {
62
87
  * Clear all data
63
88
  */
64
89
  clear(): Promise<void>;
90
+ /**
91
+ * Export the database state as a JSON string
92
+ */
93
+ exportData(): string;
94
+ /**
95
+ * Import database state from a JSON string
96
+ */
97
+ importData(json: string): Promise<void>;
98
+ /**
99
+ * Flush any pending saves to IndexedDB immediately
100
+ */
101
+ flush(): Promise<void>;
102
+ /**
103
+ * Destroy the database completely (deletes IndexedDB)
104
+ */
105
+ destroy(): Promise<void>;
65
106
  /**
66
107
  * Open IndexedDB connection
67
108
  */
68
109
  private openIndexedDB;
110
+ /**
111
+ * Debounced save to IndexedDB
112
+ */
113
+ private debounceSave;
69
114
  /**
70
115
  * Save WASM state to IndexedDB
71
116
  */
@@ -78,6 +123,10 @@ export declare class VectorDatabase {
78
123
  * Close the database
79
124
  */
80
125
  close(): void;
126
+ /**
127
+ * Support for explicit resource management (using syntax)
128
+ */
129
+ [Symbol.dispose](): void;
81
130
  }
82
131
  /**
83
132
  * Standalone distance functions
package/wrapper.js CHANGED
@@ -13,12 +13,15 @@ export class VectorDatabase {
13
13
  wasmDB = null;
14
14
  idb = null;
15
15
  config;
16
+ saveTimer = null;
17
+ saveDebounceMs = 1000;
16
18
  constructor(config) {
17
19
  this.config = {
18
20
  name: config.name,
19
21
  dimensions: config.dimensions,
20
22
  m: config.m ?? 16,
21
23
  efConstruction: config.efConstruction ?? 200,
24
+ metric: config.metric ?? 'euclidean',
22
25
  };
23
26
  }
24
27
  /**
@@ -33,11 +36,9 @@ export class VectorDatabase {
33
36
  const saved = await this.loadFromIndexedDB();
34
37
  if (saved) {
35
38
  this.wasmDB = wasmModule.VectorDB.deserialize(saved);
36
- // console.log('Restored VectorDB from IndexedDB')
37
39
  }
38
40
  else {
39
- this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction);
40
- // console.log('Created new VectorDB')
41
+ this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction, this.config.metric);
41
42
  }
42
43
  }
43
44
  /**
@@ -47,8 +48,8 @@ export class VectorDatabase {
47
48
  if (!this.wasmDB)
48
49
  throw new Error('Database not initialized');
49
50
  this.wasmDB.insert(id, vector, metadata ?? null);
50
- // Persist to IndexedDB
51
- await this.saveToIndexedDB();
51
+ // Debounced persistence
52
+ this.debounceSave();
52
53
  }
53
54
  /**
54
55
  * Batch insert multiple vectors
@@ -57,13 +58,6 @@ export class VectorDatabase {
57
58
  if (!this.wasmDB)
58
59
  throw new Error('Database not initialized');
59
60
  for (const record of records) {
60
- // console.log('🔍 Inserting to WASM:', {
61
- // id: record.id,
62
- // vectorLength: record.vector.length,
63
- // metadata: record.metadata,
64
- // metadataType: typeof record.metadata,
65
- // metadataKeys: record.metadata ? Object.keys(record.metadata) : null
66
- // })
67
61
  this.wasmDB.insert(record.id, record.vector, record.metadata ?? null);
68
62
  }
69
63
  await this.saveToIndexedDB();
@@ -79,6 +73,33 @@ export class VectorDatabase {
79
73
  const results = this.wasmDB.search(query, k, ef);
80
74
  return results;
81
75
  }
76
+ /**
77
+ * Get a vector and its metadata by ID
78
+ */
79
+ async get(id) {
80
+ if (!this.wasmDB)
81
+ throw new Error('Database not initialized');
82
+ const result = this.wasmDB.get(id);
83
+ if (result === null || result === undefined)
84
+ return null;
85
+ return result;
86
+ }
87
+ /**
88
+ * Check if a vector exists by ID
89
+ */
90
+ has(id) {
91
+ if (!this.wasmDB)
92
+ throw new Error('Database not initialized');
93
+ return this.wasmDB.has(id);
94
+ }
95
+ /**
96
+ * List all vector IDs
97
+ */
98
+ listIds() {
99
+ if (!this.wasmDB)
100
+ throw new Error('Database not initialized');
101
+ return this.wasmDB.list_ids();
102
+ }
82
103
  /**
83
104
  * Delete a vector by ID
84
105
  */
@@ -87,10 +108,22 @@ export class VectorDatabase {
87
108
  throw new Error('Database not initialized');
88
109
  const deleted = this.wasmDB.delete(id);
89
110
  if (deleted) {
90
- await this.saveToIndexedDB();
111
+ this.debounceSave();
91
112
  }
92
113
  return deleted;
93
114
  }
115
+ /**
116
+ * Delete multiple vectors by ID
117
+ */
118
+ async deleteBatch(ids) {
119
+ if (!this.wasmDB)
120
+ throw new Error('Database not initialized');
121
+ const count = this.wasmDB.delete_batch(ids);
122
+ if (count > 0) {
123
+ await this.saveToIndexedDB();
124
+ }
125
+ return count;
126
+ }
94
127
  /**
95
128
  * Get total number of vectors
96
129
  */
@@ -106,9 +139,50 @@ export class VectorDatabase {
106
139
  if (!this.wasmDB)
107
140
  throw new Error('Database not initialized');
108
141
  const wasmModule = await import('./pkg/bundler/idbvec');
109
- this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction);
142
+ this.wasmDB.free();
143
+ this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction, this.config.metric);
144
+ await this.saveToIndexedDB();
145
+ }
146
+ /**
147
+ * Export the database state as a JSON string
148
+ */
149
+ exportData() {
150
+ if (!this.wasmDB)
151
+ throw new Error('Database not initialized');
152
+ return this.wasmDB.serialize();
153
+ }
154
+ /**
155
+ * Import database state from a JSON string
156
+ */
157
+ async importData(json) {
158
+ if (!this.wasmDB)
159
+ throw new Error('Database not initialized');
160
+ const wasmModule = await import('./pkg/bundler/idbvec');
161
+ this.wasmDB.free();
162
+ this.wasmDB = wasmModule.VectorDB.deserialize(json);
110
163
  await this.saveToIndexedDB();
111
164
  }
165
+ /**
166
+ * Flush any pending saves to IndexedDB immediately
167
+ */
168
+ async flush() {
169
+ if (this.saveTimer) {
170
+ clearTimeout(this.saveTimer);
171
+ this.saveTimer = null;
172
+ }
173
+ await this.saveToIndexedDB();
174
+ }
175
+ /**
176
+ * Destroy the database completely (deletes IndexedDB)
177
+ */
178
+ async destroy() {
179
+ this.close();
180
+ return new Promise((resolve, reject) => {
181
+ const request = indexedDB.deleteDatabase(this.config.name);
182
+ request.onerror = () => reject(request.error);
183
+ request.onsuccess = () => resolve();
184
+ });
185
+ }
112
186
  /**
113
187
  * Open IndexedDB connection
114
188
  */
@@ -121,7 +195,6 @@ export class VectorDatabase {
121
195
  // CRITICAL: Handle versionchange event to auto-close when database is being deleted
122
196
  // This prevents "blocked" errors when trying to delete the database
123
197
  db.onversionchange = () => {
124
- // console.log(`🔒 VectorDB "${this.config.name}" received versionchange event - closing connection`)
125
198
  db.close();
126
199
  this.idb = null;
127
200
  };
@@ -135,6 +208,18 @@ export class VectorDatabase {
135
208
  };
136
209
  });
137
210
  }
211
+ /**
212
+ * Debounced save to IndexedDB
213
+ */
214
+ debounceSave() {
215
+ if (this.saveTimer) {
216
+ clearTimeout(this.saveTimer);
217
+ }
218
+ this.saveTimer = setTimeout(() => {
219
+ this.saveTimer = null;
220
+ this.saveToIndexedDB();
221
+ }, this.saveDebounceMs);
222
+ }
138
223
  /**
139
224
  * Save WASM state to IndexedDB
140
225
  */
@@ -171,11 +256,25 @@ export class VectorDatabase {
171
256
  * Close the database
172
257
  */
173
258
  close() {
259
+ // Flush pending saves
260
+ if (this.saveTimer) {
261
+ clearTimeout(this.saveTimer);
262
+ this.saveTimer = null;
263
+ }
174
264
  if (this.idb) {
175
265
  this.idb.close();
176
266
  this.idb = null;
177
267
  }
178
- this.wasmDB = null;
268
+ if (this.wasmDB) {
269
+ this.wasmDB.free();
270
+ this.wasmDB = null;
271
+ }
272
+ }
273
+ /**
274
+ * Support for explicit resource management (using syntax)
275
+ */
276
+ [Symbol.dispose]() {
277
+ this.close();
179
278
  }
180
279
  }
181
280
  /**