@brainwires/idbvec 0.1.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/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # idbvec - Vector Database (WASM + IndexedDB)
2
+
3
+ A high-performance client-side vector database built with Rust/WebAssembly and IndexedDB for persistence.
4
+
5
+ ## Features
6
+
7
+ - **🚀 WASM-Accelerated**: Near-native performance for vector operations
8
+ - **💾 Persistent**: Automatic IndexedDB persistence
9
+ - **🎯 ANN Search**: HNSW (Hierarchical Navigable Small World) index for approximate nearest neighbor search
10
+ - **📊 Distance Metrics**: Cosine similarity, Euclidean distance, dot product
11
+ - **🔧 Type-Safe**: Full TypeScript support
12
+ - **📦 Zero Dependencies**: Self-contained WASM module
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ ┌─────────────────────────────────┐
18
+ │ TypeScript API (wrapper.ts) │
19
+ ├─────────────────────────────────┤
20
+ │ WASM Module (Rust) │
21
+ │ - HNSW Index │
22
+ │ - Distance Metrics │
23
+ │ - Vector Operations │
24
+ ├─────────────────────────────────┤
25
+ │ IndexedDB Storage │
26
+ │ - Persistent State │
27
+ │ - Automatic Serialization │
28
+ └─────────────────────────────────┘
29
+ ```
30
+
31
+ ## Building
32
+
33
+ ```bash
34
+ # Install wasm-pack (if not already installed)
35
+ cargo install wasm-pack
36
+
37
+ # Build WASM modules
38
+ ./build-wasm.sh
39
+
40
+ # Outputs:
41
+ # - pkg/bundler (for webpack/rollup/vite)
42
+ # - pkg/nodejs (for Node.js)
43
+ # - pkg/web (for ES modules)
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Basic Example
49
+
50
+ ```typescript
51
+ import { VectorDatabase } from './wrapper'
52
+
53
+ // Create database
54
+ const db = new VectorDatabase({
55
+ name: 'my-vectors',
56
+ dimensions: 384, // e.g., for all-MiniLM-L6-v2 embeddings
57
+ m: 16, // max connections per layer
58
+ efConstruction: 200, // construction quality
59
+ })
60
+
61
+ // Initialize
62
+ await db.init()
63
+
64
+ // Insert vectors
65
+ await db.insert(
66
+ 'doc1',
67
+ new Float32Array([0.1, 0.2, 0.3, ...]),
68
+ { title: 'Document 1', category: 'tech' }
69
+ )
70
+
71
+ // Search
72
+ const results = await db.search(
73
+ queryVector,
74
+ { k: 5, ef: 50 }
75
+ )
76
+
77
+ console.log(results)
78
+ // [
79
+ // { id: 'doc1', score: 0.95, metadata: { title: 'Document 1', ... } },
80
+ // ...
81
+ // ]
82
+
83
+ // Delete
84
+ await db.delete('doc1')
85
+
86
+ // Close
87
+ db.close()
88
+ ```
89
+
90
+ ### Batch Insert
91
+
92
+ ```typescript
93
+ const records = [
94
+ { id: 'vec1', vector: new Float32Array([...]), metadata: { ... } },
95
+ { id: 'vec2', vector: new Float32Array([...]), metadata: { ... } },
96
+ // ...
97
+ ]
98
+
99
+ await db.insertBatch(records)
100
+ ```
101
+
102
+ ### Distance Functions
103
+
104
+ ```typescript
105
+ import { cosineSimilarity, euclideanDistance, dotProduct } from './wrapper'
106
+
107
+ const a = new Float32Array([1, 0, 0])
108
+ const b = new Float32Array([0, 1, 0])
109
+
110
+ const similarity = await cosineSimilarity(a, b) // 0.0
111
+ const distance = await euclideanDistance(a, b) // 1.414...
112
+ const dot = await dotProduct(a, b) // 0.0
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ ### VectorDBConfig
118
+
119
+ | Parameter | Type | Default | Description |
120
+ |-----------|------|---------|-------------|
121
+ | `name` | string | - | Database name (IndexedDB key) |
122
+ | `dimensions` | number | - | Vector dimensionality |
123
+ | `m` | number | 16 | Max connections per layer (higher = better recall, more memory) |
124
+ | `efConstruction` | number | 200 | Construction quality (higher = better index, slower insert) |
125
+
126
+ ### Search Options
127
+
128
+ | Parameter | Type | Default | Description |
129
+ |-----------|------|---------|-------------|
130
+ | `k` | number | 10 | Number of nearest neighbors to return |
131
+ | `ef` | number | 50 | Search quality (higher = better recall, slower search) |
132
+
133
+ ## HNSW Parameters Guide
134
+
135
+ ### M (Max Connections)
136
+ - **8-12**: Low memory, faster search, lower recall
137
+ - **16-32**: Balanced (recommended)
138
+ - **32+**: High recall, more memory
139
+
140
+ ### ef_construction
141
+ - **100**: Fast build, lower quality
142
+ - **200**: Balanced (recommended)
143
+ - **400+**: Slow build, high quality
144
+
145
+ ### ef (search)
146
+ - **k**: Minimum (fast, lower recall)
147
+ - **k * 2-5**: Balanced
148
+ - **k * 10+**: High recall (slower)
149
+
150
+ ## Performance
151
+
152
+ Typical performance on modern hardware:
153
+
154
+ - **Insert**: ~1-10ms per vector (depends on ef_construction)
155
+ - **Search**: ~1-5ms for k=10 (depends on ef and database size)
156
+ - **Memory**: ~(dimensions * 4 + M * 8) bytes per vector
157
+
158
+ ## Integration with Next.js
159
+
160
+ 1. Copy WASM build to `public/`:
161
+
162
+ ```bash
163
+ cp -r rust/idbvec/pkg/bundler public/idbvec-wasm
164
+ ```
165
+
166
+ 2. Use in a client component:
167
+
168
+ ```tsx
169
+ 'use client'
170
+
171
+ import { VectorDatabase } from '@/rust/idbvec/wrapper'
172
+ import { useEffect, useState } from 'react'
173
+
174
+ export function VectorSearch() {
175
+ const [db, setDb] = useState<VectorDatabase | null>(null)
176
+
177
+ useEffect(() => {
178
+ const initDB = async () => {
179
+ const vectorDB = new VectorDatabase({
180
+ name: 'app-vectors',
181
+ dimensions: 384,
182
+ })
183
+ await vectorDB.init()
184
+ setDb(vectorDB)
185
+ }
186
+ initDB()
187
+ }, [])
188
+
189
+ // Use db for search, insert, etc.
190
+ }
191
+ ```
192
+
193
+ ## Browser Compatibility
194
+
195
+ - ✅ Chrome 90+
196
+ - ✅ Firefox 88+
197
+ - ✅ Safari 15+
198
+ - ✅ Edge 90+
199
+
200
+ Requires:
201
+ - WebAssembly support
202
+ - IndexedDB support
203
+ - ES modules
204
+
205
+ ## License
206
+
207
+ MIT OR Apache-2.0
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@brainwires/idbvec",
3
+ "version": "0.1.0",
4
+ "description": "Client-side vector database built on IndexedDB with WASM",
5
+ "main": "wrapper.js",
6
+ "types": "wrapper.d.ts",
7
+ "files": [
8
+ "wrapper.js",
9
+ "wrapper.d.ts",
10
+ "pkg/bundler/"
11
+ ],
12
+ "keywords": [
13
+ "vector",
14
+ "database",
15
+ "wasm",
16
+ "indexeddb",
17
+ "hnsw",
18
+ "ann"
19
+ ],
20
+ "license": "MIT OR Apache-2.0",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/Brainwires/idbvec.git"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.9.3"
27
+ }
28
+ }
package/wrapper.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * TypeScript wrapper for Vector Database with IndexedDB persistence
3
+ *
4
+ * This provides a high-level API for vector operations with:
5
+ * - WASM-accelerated similarity search
6
+ * - IndexedDB persistence
7
+ * - Automatic serialization/deserialization
8
+ */
9
+ export interface SearchResult {
10
+ id: string;
11
+ score: number;
12
+ metadata?: Record<string, string>;
13
+ }
14
+ export interface VectorRecord {
15
+ id: string;
16
+ vector: Float32Array;
17
+ metadata?: Record<string, string>;
18
+ }
19
+ export interface SearchOptions {
20
+ k?: number;
21
+ ef?: number;
22
+ }
23
+ export interface VectorDBConfig {
24
+ name: string;
25
+ dimensions: number;
26
+ m?: number;
27
+ efConstruction?: number;
28
+ }
29
+ /**
30
+ * Vector Database with IndexedDB persistence
31
+ */
32
+ export declare class VectorDatabase {
33
+ private wasmDB;
34
+ private idb;
35
+ private config;
36
+ constructor(config: VectorDBConfig);
37
+ /**
38
+ * Initialize the database (load WASM + IndexedDB)
39
+ */
40
+ init(): Promise<void>;
41
+ /**
42
+ * Insert a vector into the database
43
+ */
44
+ insert(id: string, vector: Float32Array, metadata?: Record<string, string>): Promise<void>;
45
+ /**
46
+ * Batch insert multiple vectors
47
+ */
48
+ insertBatch(records: VectorRecord[]): Promise<void>;
49
+ /**
50
+ * Search for nearest neighbors
51
+ */
52
+ search(query: Float32Array, options?: SearchOptions): Promise<SearchResult[]>;
53
+ /**
54
+ * Delete a vector by ID
55
+ */
56
+ delete(id: string): Promise<boolean>;
57
+ /**
58
+ * Get total number of vectors
59
+ */
60
+ size(): number;
61
+ /**
62
+ * Clear all data
63
+ */
64
+ clear(): Promise<void>;
65
+ /**
66
+ * Open IndexedDB connection
67
+ */
68
+ private openIndexedDB;
69
+ /**
70
+ * Save WASM state to IndexedDB
71
+ */
72
+ private saveToIndexedDB;
73
+ /**
74
+ * Load WASM state from IndexedDB
75
+ */
76
+ private loadFromIndexedDB;
77
+ /**
78
+ * Close the database
79
+ */
80
+ close(): void;
81
+ }
82
+ /**
83
+ * Standalone distance functions
84
+ */
85
+ export declare function cosineSimilarity(a: Float32Array, b: Float32Array): Promise<number>;
86
+ export declare function euclideanDistance(a: Float32Array, b: Float32Array): Promise<number>;
87
+ export declare function dotProduct(a: Float32Array, b: Float32Array): Promise<number>;
package/wrapper.js ADDED
@@ -0,0 +1,195 @@
1
+ /**
2
+ * TypeScript wrapper for Vector Database with IndexedDB persistence
3
+ *
4
+ * This provides a high-level API for vector operations with:
5
+ * - WASM-accelerated similarity search
6
+ * - IndexedDB persistence
7
+ * - Automatic serialization/deserialization
8
+ */
9
+ /**
10
+ * Vector Database with IndexedDB persistence
11
+ */
12
+ export class VectorDatabase {
13
+ wasmDB = null;
14
+ idb = null;
15
+ config;
16
+ constructor(config) {
17
+ this.config = {
18
+ name: config.name,
19
+ dimensions: config.dimensions,
20
+ m: config.m ?? 16,
21
+ efConstruction: config.efConstruction ?? 200,
22
+ };
23
+ }
24
+ /**
25
+ * Initialize the database (load WASM + IndexedDB)
26
+ */
27
+ async init() {
28
+ // Load WASM module (auto-initializes on import for bundler target)
29
+ const wasmModule = await import('./pkg/bundler/idbvec');
30
+ // Open IndexedDB
31
+ this.idb = await this.openIndexedDB();
32
+ // Try to restore from IndexedDB
33
+ const saved = await this.loadFromIndexedDB();
34
+ if (saved) {
35
+ this.wasmDB = wasmModule.VectorDB.deserialize(saved);
36
+ // console.log('Restored VectorDB from IndexedDB')
37
+ }
38
+ else {
39
+ this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction);
40
+ // console.log('Created new VectorDB')
41
+ }
42
+ }
43
+ /**
44
+ * Insert a vector into the database
45
+ */
46
+ async insert(id, vector, metadata) {
47
+ if (!this.wasmDB)
48
+ throw new Error('Database not initialized');
49
+ this.wasmDB.insert(id, vector, metadata ?? null);
50
+ // Persist to IndexedDB
51
+ await this.saveToIndexedDB();
52
+ }
53
+ /**
54
+ * Batch insert multiple vectors
55
+ */
56
+ async insertBatch(records) {
57
+ if (!this.wasmDB)
58
+ throw new Error('Database not initialized');
59
+ 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
+ this.wasmDB.insert(record.id, record.vector, record.metadata ?? null);
68
+ }
69
+ await this.saveToIndexedDB();
70
+ }
71
+ /**
72
+ * Search for nearest neighbors
73
+ */
74
+ async search(query, options = {}) {
75
+ if (!this.wasmDB)
76
+ throw new Error('Database not initialized');
77
+ const k = options.k ?? 10;
78
+ const ef = options.ef ?? 50;
79
+ const results = this.wasmDB.search(query, k, ef);
80
+ return results;
81
+ }
82
+ /**
83
+ * Delete a vector by ID
84
+ */
85
+ async delete(id) {
86
+ if (!this.wasmDB)
87
+ throw new Error('Database not initialized');
88
+ const deleted = this.wasmDB.delete(id);
89
+ if (deleted) {
90
+ await this.saveToIndexedDB();
91
+ }
92
+ return deleted;
93
+ }
94
+ /**
95
+ * Get total number of vectors
96
+ */
97
+ size() {
98
+ if (!this.wasmDB)
99
+ throw new Error('Database not initialized');
100
+ return this.wasmDB.size();
101
+ }
102
+ /**
103
+ * Clear all data
104
+ */
105
+ async clear() {
106
+ if (!this.wasmDB)
107
+ throw new Error('Database not initialized');
108
+ const wasmModule = await import('./pkg/bundler/idbvec');
109
+ this.wasmDB = new wasmModule.VectorDB(this.config.dimensions, this.config.m, this.config.efConstruction);
110
+ await this.saveToIndexedDB();
111
+ }
112
+ /**
113
+ * Open IndexedDB connection
114
+ */
115
+ openIndexedDB() {
116
+ return new Promise((resolve, reject) => {
117
+ const request = indexedDB.open(this.config.name, 1);
118
+ request.onerror = () => reject(request.error);
119
+ request.onsuccess = () => {
120
+ const db = request.result;
121
+ // CRITICAL: Handle versionchange event to auto-close when database is being deleted
122
+ // This prevents "blocked" errors when trying to delete the database
123
+ db.onversionchange = () => {
124
+ // console.log(`🔒 VectorDB "${this.config.name}" received versionchange event - closing connection`)
125
+ db.close();
126
+ this.idb = null;
127
+ };
128
+ resolve(db);
129
+ };
130
+ request.onupgradeneeded = (event) => {
131
+ const db = event.target.result;
132
+ if (!db.objectStoreNames.contains('vectordb')) {
133
+ db.createObjectStore('vectordb');
134
+ }
135
+ };
136
+ });
137
+ }
138
+ /**
139
+ * Save WASM state to IndexedDB
140
+ */
141
+ async saveToIndexedDB() {
142
+ if (!this.idb || !this.wasmDB)
143
+ return;
144
+ const serialized = this.wasmDB.serialize();
145
+ return new Promise((resolve, reject) => {
146
+ const tx = this.idb.transaction('vectordb', 'readwrite');
147
+ const store = tx.objectStore('vectordb');
148
+ const request = store.put(serialized, 'state');
149
+ request.onerror = () => reject(request.error);
150
+ request.onsuccess = () => resolve();
151
+ });
152
+ }
153
+ /**
154
+ * Load WASM state from IndexedDB
155
+ */
156
+ async loadFromIndexedDB() {
157
+ if (!this.idb)
158
+ return null;
159
+ return new Promise((resolve, reject) => {
160
+ const tx = this.idb.transaction('vectordb', 'readonly');
161
+ const store = tx.objectStore('vectordb');
162
+ const request = store.get('state');
163
+ request.onerror = () => reject(request.error);
164
+ request.onsuccess = () => {
165
+ const result = request.result;
166
+ resolve(result ?? null);
167
+ };
168
+ });
169
+ }
170
+ /**
171
+ * Close the database
172
+ */
173
+ close() {
174
+ if (this.idb) {
175
+ this.idb.close();
176
+ this.idb = null;
177
+ }
178
+ this.wasmDB = null;
179
+ }
180
+ }
181
+ /**
182
+ * Standalone distance functions
183
+ */
184
+ export async function cosineSimilarity(a, b) {
185
+ const wasmModule = await import('./pkg/bundler/idbvec');
186
+ return wasmModule.cosine_similarity(a, b);
187
+ }
188
+ export async function euclideanDistance(a, b) {
189
+ const wasmModule = await import('./pkg/bundler/idbvec');
190
+ return wasmModule.euclidean_distance(a, b);
191
+ }
192
+ export async function dotProduct(a, b) {
193
+ const wasmModule = await import('./pkg/bundler/idbvec');
194
+ return wasmModule.dot_product(a, b);
195
+ }