verso-db 0.1.1

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/LICENSE +21 -0
  3. package/README.md +252 -0
  4. package/dist/BinaryHeap.d.ts +25 -0
  5. package/dist/BinaryHeap.d.ts.map +1 -0
  6. package/dist/Collection.d.ts +156 -0
  7. package/dist/Collection.d.ts.map +1 -0
  8. package/dist/HNSWIndex.d.ts +357 -0
  9. package/dist/HNSWIndex.d.ts.map +1 -0
  10. package/dist/MaxBinaryHeap.d.ts +63 -0
  11. package/dist/MaxBinaryHeap.d.ts.map +1 -0
  12. package/dist/Storage.d.ts +54 -0
  13. package/dist/Storage.d.ts.map +1 -0
  14. package/dist/VectorDB.d.ts +44 -0
  15. package/dist/VectorDB.d.ts.map +1 -0
  16. package/dist/backends/DistanceBackend.d.ts +5 -0
  17. package/dist/backends/DistanceBackend.d.ts.map +1 -0
  18. package/dist/backends/JsDistanceBackend.d.ts +37 -0
  19. package/dist/backends/JsDistanceBackend.d.ts.map +1 -0
  20. package/dist/encoding/DeltaEncoder.d.ts +61 -0
  21. package/dist/encoding/DeltaEncoder.d.ts.map +1 -0
  22. package/dist/errors.d.ts +58 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/index.d.ts +64 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +3732 -0
  27. package/dist/presets.d.ts +91 -0
  28. package/dist/presets.d.ts.map +1 -0
  29. package/dist/quantization/ScalarQuantizer.d.ts +114 -0
  30. package/dist/quantization/ScalarQuantizer.d.ts.map +1 -0
  31. package/dist/storage/BatchWriter.d.ts +104 -0
  32. package/dist/storage/BatchWriter.d.ts.map +1 -0
  33. package/dist/storage/BunStorageBackend.d.ts +58 -0
  34. package/dist/storage/BunStorageBackend.d.ts.map +1 -0
  35. package/dist/storage/MemoryBackend.d.ts +44 -0
  36. package/dist/storage/MemoryBackend.d.ts.map +1 -0
  37. package/dist/storage/OPFSBackend.d.ts +59 -0
  38. package/dist/storage/OPFSBackend.d.ts.map +1 -0
  39. package/dist/storage/StorageBackend.d.ts +66 -0
  40. package/dist/storage/StorageBackend.d.ts.map +1 -0
  41. package/dist/storage/WriteAheadLog.d.ts +111 -0
  42. package/dist/storage/WriteAheadLog.d.ts.map +1 -0
  43. package/dist/storage/createStorageBackend.d.ts +40 -0
  44. package/dist/storage/createStorageBackend.d.ts.map +1 -0
  45. package/dist/storage/index.d.ts +30 -0
  46. package/dist/storage/index.d.ts.map +1 -0
  47. package/package.json +98 -0
  48. package/src/BinaryHeap.ts +131 -0
  49. package/src/Collection.ts +695 -0
  50. package/src/HNSWIndex.ts +1839 -0
  51. package/src/MaxBinaryHeap.ts +175 -0
  52. package/src/Storage.ts +435 -0
  53. package/src/VectorDB.ts +109 -0
  54. package/src/backends/DistanceBackend.ts +17 -0
  55. package/src/backends/JsDistanceBackend.ts +227 -0
  56. package/src/encoding/DeltaEncoder.ts +217 -0
  57. package/src/errors.ts +110 -0
  58. package/src/index.ts +138 -0
  59. package/src/presets.ts +229 -0
  60. package/src/quantization/ScalarQuantizer.ts +383 -0
  61. package/src/storage/BatchWriter.ts +336 -0
  62. package/src/storage/BunStorageBackend.ts +161 -0
  63. package/src/storage/MemoryBackend.ts +120 -0
  64. package/src/storage/OPFSBackend.ts +250 -0
  65. package/src/storage/StorageBackend.ts +74 -0
  66. package/src/storage/WriteAheadLog.ts +326 -0
  67. package/src/storage/createStorageBackend.ts +137 -0
  68. package/src/storage/index.ts +53 -0
@@ -0,0 +1,17 @@
1
+ export interface DistanceBackend {
2
+ batchL2(
3
+ base: Float32Array,
4
+ dim: number,
5
+ ids: Uint32Array,
6
+ query: Float32Array,
7
+ out: Float32Array
8
+ ): void;
9
+
10
+ batchDot(
11
+ base: Float32Array,
12
+ dim: number,
13
+ ids: Uint32Array,
14
+ query: Float32Array,
15
+ out: Float32Array
16
+ ): void;
17
+ }
@@ -0,0 +1,227 @@
1
+ import { DistanceBackend } from './DistanceBackend';
2
+
3
+ /**
4
+ * Optimized JavaScript distance backend with SIMD-style 4-wide unrolling.
5
+ * This provides ~1.5-2x speedup over naive loops by reducing loop overhead
6
+ * and enabling better CPU pipelining.
7
+ */
8
+ export class JsDistanceBackend implements DistanceBackend {
9
+ batchL2(base: Float32Array, dim: number, ids: Uint32Array, query: Float32Array, out: Float32Array): void {
10
+ const len = ids.length;
11
+ for (let i = 0; i < len; i++) {
12
+ const off = ids[i] * dim;
13
+ out[i] = this.l2SquaredUnrolled(base, off, query, dim);
14
+ }
15
+ }
16
+
17
+ batchDot(base: Float32Array, dim: number, ids: Uint32Array, query: Float32Array, out: Float32Array): void {
18
+ const len = ids.length;
19
+ for (let i = 0; i < len; i++) {
20
+ const off = ids[i] * dim;
21
+ out[i] = this.dotProductUnrolled(base, off, query, dim);
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Compute dot product with 4-wide manual unrolling for better performance.
27
+ * Uses separate accumulators to enable instruction-level parallelism.
28
+ */
29
+ private dotProductUnrolled(base: Float32Array, offset: number, query: Float32Array, dim: number): number {
30
+ let sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
31
+ let d = 0;
32
+
33
+ // Process 4 elements at a time
34
+ const limit = dim - 3;
35
+ for (; d < limit; d += 4) {
36
+ sum0 += base[offset + d] * query[d];
37
+ sum1 += base[offset + d + 1] * query[d + 1];
38
+ sum2 += base[offset + d + 2] * query[d + 2];
39
+ sum3 += base[offset + d + 3] * query[d + 3];
40
+ }
41
+
42
+ // Handle remaining elements
43
+ for (; d < dim; d++) {
44
+ sum0 += base[offset + d] * query[d];
45
+ }
46
+
47
+ return sum0 + sum1 + sum2 + sum3;
48
+ }
49
+
50
+ /**
51
+ * Compute L2 squared distance with 4-wide manual unrolling.
52
+ */
53
+ private l2SquaredUnrolled(base: Float32Array, offset: number, query: Float32Array, dim: number): number {
54
+ let sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
55
+ let d = 0;
56
+
57
+ // Process 4 elements at a time
58
+ const limit = dim - 3;
59
+ for (; d < limit; d += 4) {
60
+ const diff0 = base[offset + d] - query[d];
61
+ const diff1 = base[offset + d + 1] - query[d + 1];
62
+ const diff2 = base[offset + d + 2] - query[d + 2];
63
+ const diff3 = base[offset + d + 3] - query[d + 3];
64
+ sum0 += diff0 * diff0;
65
+ sum1 += diff1 * diff1;
66
+ sum2 += diff2 * diff2;
67
+ sum3 += diff3 * diff3;
68
+ }
69
+
70
+ // Handle remaining elements
71
+ for (; d < dim; d++) {
72
+ const diff = base[offset + d] - query[d];
73
+ sum0 += diff * diff;
74
+ }
75
+
76
+ return sum0 + sum1 + sum2 + sum3;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Fast inline distance functions for single-vector comparisons.
82
+ * These avoid the batch interface overhead for point-to-point distance calculations.
83
+ */
84
+ export function dotProductFast(a: Float32Array, b: Float32Array): number {
85
+ const len = a.length;
86
+ // Use 8 accumulators for better ILP (instruction-level parallelism)
87
+ let sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
88
+ let sum4 = 0, sum5 = 0, sum6 = 0, sum7 = 0;
89
+ let i = 0;
90
+
91
+ // 8-wide unrolling for high-dimensional vectors (768D, 1536D)
92
+ const limit8 = len - 7;
93
+ for (; i < limit8; i += 8) {
94
+ sum0 += a[i] * b[i];
95
+ sum1 += a[i + 1] * b[i + 1];
96
+ sum2 += a[i + 2] * b[i + 2];
97
+ sum3 += a[i + 3] * b[i + 3];
98
+ sum4 += a[i + 4] * b[i + 4];
99
+ sum5 += a[i + 5] * b[i + 5];
100
+ sum6 += a[i + 6] * b[i + 6];
101
+ sum7 += a[i + 7] * b[i + 7];
102
+ }
103
+
104
+ // Handle remaining elements
105
+ for (; i < len; i++) {
106
+ sum0 += a[i] * b[i];
107
+ }
108
+
109
+ return sum0 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7;
110
+ }
111
+
112
+ export function l2SquaredFast(a: Float32Array, b: Float32Array): number {
113
+ const len = a.length;
114
+ // Use 8 accumulators for better ILP
115
+ let sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
116
+ let sum4 = 0, sum5 = 0, sum6 = 0, sum7 = 0;
117
+ let i = 0;
118
+
119
+ // 8-wide unrolling for high-dimensional vectors
120
+ const limit8 = len - 7;
121
+ for (; i < limit8; i += 8) {
122
+ const d0 = a[i] - b[i];
123
+ const d1 = a[i + 1] - b[i + 1];
124
+ const d2 = a[i + 2] - b[i + 2];
125
+ const d3 = a[i + 3] - b[i + 3];
126
+ const d4 = a[i + 4] - b[i + 4];
127
+ const d5 = a[i + 5] - b[i + 5];
128
+ const d6 = a[i + 6] - b[i + 6];
129
+ const d7 = a[i + 7] - b[i + 7];
130
+ sum0 += d0 * d0;
131
+ sum1 += d1 * d1;
132
+ sum2 += d2 * d2;
133
+ sum3 += d3 * d3;
134
+ sum4 += d4 * d4;
135
+ sum5 += d5 * d5;
136
+ sum6 += d6 * d6;
137
+ sum7 += d7 * d7;
138
+ }
139
+
140
+ // Handle remaining elements
141
+ for (; i < len; i++) {
142
+ const d = a[i] - b[i];
143
+ sum0 += d * d;
144
+ }
145
+
146
+ return sum0 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7;
147
+ }
148
+
149
+ /**
150
+ * Normalize a vector in place.
151
+ * Uses 8-wide unrolling for better ILP.
152
+ */
153
+ export function normalizeInPlace(v: Float32Array): number {
154
+ const len = v.length;
155
+ let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
156
+ let s4 = 0, s5 = 0, s6 = 0, s7 = 0;
157
+ let i = 0;
158
+ const limit8 = len - 7;
159
+
160
+ for (; i < limit8; i += 8) {
161
+ s0 += v[i] * v[i];
162
+ s1 += v[i + 1] * v[i + 1];
163
+ s2 += v[i + 2] * v[i + 2];
164
+ s3 += v[i + 3] * v[i + 3];
165
+ s4 += v[i + 4] * v[i + 4];
166
+ s5 += v[i + 5] * v[i + 5];
167
+ s6 += v[i + 6] * v[i + 6];
168
+ s7 += v[i + 7] * v[i + 7];
169
+ }
170
+ for (; i < len; i++) {
171
+ s0 += v[i] * v[i];
172
+ }
173
+
174
+ const norm = Math.sqrt(s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7);
175
+ if (norm > 0) {
176
+ const invNorm = 1 / norm;
177
+ for (let j = 0; j < len; j++) {
178
+ v[j] *= invNorm;
179
+ }
180
+ }
181
+ return norm;
182
+ }
183
+
184
+ /**
185
+ * Compute cosine distance between two vectors.
186
+ * For pre-normalized vectors, this is simply 1 - dot(a, b).
187
+ * Uses 8-wide unrolling for norm computation.
188
+ */
189
+ export function cosineDistanceFast(a: Float32Array, b: Float32Array, aIsNormalized = false, bIsNormalized = false): number {
190
+ const dot = dotProductFast(a, b);
191
+
192
+ if (aIsNormalized && bIsNormalized) {
193
+ // Both vectors are normalized, cosine distance = 1 - dot
194
+ const dist = 1 - dot;
195
+ return dist < 1e-10 ? 0 : dist;
196
+ }
197
+
198
+ // Need to compute norms with 8-wide unrolling
199
+ const len = a.length;
200
+ let nA0 = 0, nA1 = 0, nA2 = 0, nA3 = 0;
201
+ let nB0 = 0, nB1 = 0, nB2 = 0, nB3 = 0;
202
+ let i = 0;
203
+ const limit8 = len - 7;
204
+
205
+ for (; i < limit8; i += 8) {
206
+ nA0 += a[i] * a[i] + a[i + 4] * a[i + 4];
207
+ nA1 += a[i + 1] * a[i + 1] + a[i + 5] * a[i + 5];
208
+ nA2 += a[i + 2] * a[i + 2] + a[i + 6] * a[i + 6];
209
+ nA3 += a[i + 3] * a[i + 3] + a[i + 7] * a[i + 7];
210
+ nB0 += b[i] * b[i] + b[i + 4] * b[i + 4];
211
+ nB1 += b[i + 1] * b[i + 1] + b[i + 5] * b[i + 5];
212
+ nB2 += b[i + 2] * b[i + 2] + b[i + 6] * b[i + 6];
213
+ nB3 += b[i + 3] * b[i + 3] + b[i + 7] * b[i + 7];
214
+ }
215
+ for (; i < len; i++) {
216
+ nA0 += a[i] * a[i];
217
+ nB0 += b[i] * b[i];
218
+ }
219
+
220
+ const normA = nA0 + nA1 + nA2 + nA3;
221
+ const normB = nB0 + nB1 + nB2 + nB3;
222
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
223
+ if (magnitude === 0) return 1;
224
+
225
+ const dist = 1 - (dot / magnitude);
226
+ return dist < 1e-10 ? 0 : dist;
227
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Delta Encoding with Varint for Neighbor Lists
3
+ *
4
+ * Implements delta-encoded neighbor lists as used by Qdrant for ~38% storage reduction.
5
+ * Neighbor IDs are sorted, then stored as deltas with variable-length encoding.
6
+ *
7
+ * Format:
8
+ * - First ID stored as full uint32
9
+ * - Subsequent IDs stored as varint deltas from previous ID
10
+ *
11
+ * Varint encoding (like Protocol Buffers):
12
+ * - Values 0-127: 1 byte
13
+ * - Values 128-16383: 2 bytes
14
+ * - Values 16384-2097151: 3 bytes
15
+ * - Values 2097152-268435455: 4 bytes
16
+ * - Larger: 5 bytes
17
+ */
18
+
19
+ /**
20
+ * Encode an unsigned integer as a varint
21
+ * Returns the number of bytes written
22
+ */
23
+ export function encodeVarint(value: number, buffer: Uint8Array, offset: number): number {
24
+ let v = value >>> 0; // Ensure unsigned
25
+ let bytesWritten = 0;
26
+
27
+ while (v >= 0x80) {
28
+ buffer[offset + bytesWritten] = (v & 0x7f) | 0x80;
29
+ v >>>= 7;
30
+ bytesWritten++;
31
+ }
32
+ buffer[offset + bytesWritten] = v;
33
+ return bytesWritten + 1;
34
+ }
35
+
36
+ /**
37
+ * Decode a varint from buffer
38
+ * Returns [value, bytesRead]
39
+ */
40
+ export function decodeVarint(buffer: Uint8Array, offset: number): [number, number] {
41
+ let result = 0;
42
+ let shift = 0;
43
+ let bytesRead = 0;
44
+
45
+ while (offset + bytesRead < buffer.length) {
46
+ const byte = buffer[offset + bytesRead];
47
+ result |= (byte & 0x7f) << shift;
48
+ bytesRead++;
49
+
50
+ if ((byte & 0x80) === 0) {
51
+ return [result >>> 0, bytesRead];
52
+ }
53
+
54
+ shift += 7;
55
+ if (shift > 35) {
56
+ throw new Error('Varint too long');
57
+ }
58
+ }
59
+
60
+ throw new Error('Unexpected end of buffer');
61
+ }
62
+
63
+ /**
64
+ * Calculate the number of bytes needed to encode a varint
65
+ */
66
+ export function varintSize(value: number): number {
67
+ let v = value >>> 0;
68
+ let size = 1;
69
+ while (v >= 0x80) {
70
+ v >>>= 7;
71
+ size++;
72
+ }
73
+ return size;
74
+ }
75
+
76
+ /**
77
+ * Delta-encode a sorted array of neighbor IDs
78
+ * Returns the encoded buffer
79
+ */
80
+ export function deltaEncodeNeighbors(neighbors: number[]): Uint8Array {
81
+ if (neighbors.length === 0) {
82
+ return new Uint8Array(0);
83
+ }
84
+
85
+ // Sort neighbors for optimal delta encoding
86
+ const sorted = neighbors.slice().sort((a, b) => a - b);
87
+
88
+ // Calculate required buffer size
89
+ let size = 4; // First ID as uint32
90
+ let prev = sorted[0];
91
+ for (let i = 1; i < sorted.length; i++) {
92
+ const delta = sorted[i] - prev;
93
+ size += varintSize(delta);
94
+ prev = sorted[i];
95
+ }
96
+
97
+ // Encode
98
+ const buffer = new Uint8Array(size);
99
+ const view = new DataView(buffer.buffer);
100
+
101
+ // First ID as full uint32 (little-endian)
102
+ view.setUint32(0, sorted[0], true);
103
+ let offset = 4;
104
+
105
+ // Remaining as deltas
106
+ prev = sorted[0];
107
+ for (let i = 1; i < sorted.length; i++) {
108
+ const delta = sorted[i] - prev;
109
+ offset += encodeVarint(delta, buffer, offset);
110
+ prev = sorted[i];
111
+ }
112
+
113
+ return buffer;
114
+ }
115
+
116
+ /**
117
+ * Decode a delta-encoded neighbor list
118
+ * Returns the original neighbor IDs (sorted)
119
+ */
120
+ export function deltaDecodeNeighbors(buffer: Uint8Array, count: number): number[] {
121
+ if (count === 0 || buffer.length === 0) {
122
+ return [];
123
+ }
124
+
125
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
126
+ const neighbors = new Array<number>(count);
127
+
128
+ // First ID as full uint32
129
+ neighbors[0] = view.getUint32(0, true);
130
+ let offset = 4;
131
+
132
+ // Remaining as deltas
133
+ for (let i = 1; i < count; i++) {
134
+ const [delta, bytesRead] = decodeVarint(buffer, offset);
135
+ neighbors[i] = neighbors[i - 1] + delta;
136
+ offset += bytesRead;
137
+ }
138
+
139
+ return neighbors;
140
+ }
141
+
142
+ /**
143
+ * Calculate the encoded size for a neighbor list without actually encoding
144
+ * Useful for calculating total buffer size before serialization
145
+ */
146
+ export function deltaEncodedSize(neighbors: number[]): number {
147
+ if (neighbors.length === 0) {
148
+ return 0;
149
+ }
150
+
151
+ const sorted = neighbors.slice().sort((a, b) => a - b);
152
+
153
+ let size = 4; // First ID as uint32
154
+ let prev = sorted[0];
155
+ for (let i = 1; i < sorted.length; i++) {
156
+ const delta = sorted[i] - prev;
157
+ size += varintSize(delta);
158
+ prev = sorted[i];
159
+ }
160
+
161
+ return size;
162
+ }
163
+
164
+ /**
165
+ * Batch encode multiple neighbor lists efficiently
166
+ * Returns a single buffer with all encoded lists concatenated
167
+ * Also returns offsets for each list
168
+ */
169
+ export function deltaEncodeBatch(neighborLists: number[][]): {
170
+ buffer: Uint8Array;
171
+ offsets: number[];
172
+ sizes: number[];
173
+ } {
174
+ // Calculate total size and individual sizes
175
+ const sizes = neighborLists.map(list => deltaEncodedSize(list));
176
+ const totalSize = sizes.reduce((a, b) => a + b, 0);
177
+
178
+ // Allocate single buffer
179
+ const buffer = new Uint8Array(totalSize);
180
+ const offsets: number[] = [];
181
+
182
+ let currentOffset = 0;
183
+ for (let i = 0; i < neighborLists.length; i++) {
184
+ offsets.push(currentOffset);
185
+
186
+ if (neighborLists[i].length > 0) {
187
+ const encoded = deltaEncodeNeighbors(neighborLists[i]);
188
+ buffer.set(encoded, currentOffset);
189
+ currentOffset += encoded.length;
190
+ }
191
+ }
192
+
193
+ return { buffer, offsets, sizes };
194
+ }
195
+
196
+ /**
197
+ * Decode a batch of neighbor lists from a single buffer
198
+ */
199
+ export function deltaDecodeBatch(
200
+ buffer: Uint8Array,
201
+ offsets: number[],
202
+ sizes: number[],
203
+ counts: number[]
204
+ ): number[][] {
205
+ const results: number[][] = [];
206
+
207
+ for (let i = 0; i < offsets.length; i++) {
208
+ if (counts[i] === 0 || sizes[i] === 0) {
209
+ results.push([]);
210
+ } else {
211
+ const slice = buffer.subarray(offsets[i], offsets[i] + sizes[i]);
212
+ results.push(deltaDecodeNeighbors(slice, counts[i]));
213
+ }
214
+ }
215
+
216
+ return results;
217
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Base error class for all VectorDB errors
3
+ */
4
+ export class VectorDBError extends Error {
5
+ readonly code: string;
6
+
7
+ constructor(message: string, code: string) {
8
+ super(message);
9
+ this.name = 'VectorDBError';
10
+ this.code = code;
11
+ }
12
+ }
13
+
14
+ /**
15
+ * Thrown when vector dimensions don't match expected dimensions
16
+ */
17
+ export class DimensionMismatchError extends VectorDBError {
18
+ readonly expected: number;
19
+ readonly actual: number;
20
+
21
+ constructor(expected: number, actual: number, context?: string) {
22
+ const message = context
23
+ ? `${context}: expected dimension ${expected}, got ${actual}`
24
+ : `Dimension mismatch: expected ${expected}, got ${actual}`;
25
+ super(message, 'DIMENSION_MISMATCH');
26
+ this.name = 'DimensionMismatchError';
27
+ this.expected = expected;
28
+ this.actual = actual;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Thrown when attempting to add a vector with an ID that already exists
34
+ */
35
+ export class DuplicateVectorError extends VectorDBError {
36
+ readonly ids: string[];
37
+
38
+ constructor(ids: string[]) {
39
+ const message = ids.length === 1
40
+ ? `Vector with ID '${ids[0]}' already exists`
41
+ : `Vectors with IDs already exist: ${ids.join(', ')}`;
42
+ super(message, 'DUPLICATE_VECTOR');
43
+ this.name = 'DuplicateVectorError';
44
+ this.ids = ids;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Thrown when a requested collection does not exist
50
+ */
51
+ export class CollectionNotFoundError extends VectorDBError {
52
+ readonly collectionName: string;
53
+
54
+ constructor(collectionName: string) {
55
+ super(`Collection '${collectionName}' does not exist`, 'COLLECTION_NOT_FOUND');
56
+ this.name = 'CollectionNotFoundError';
57
+ this.collectionName = collectionName;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Thrown when attempting to create a collection that already exists
63
+ */
64
+ export class CollectionExistsError extends VectorDBError {
65
+ readonly collectionName: string;
66
+
67
+ constructor(collectionName: string) {
68
+ super(`Collection '${collectionName}' already exists`, 'COLLECTION_EXISTS');
69
+ this.name = 'CollectionExistsError';
70
+ this.collectionName = collectionName;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Thrown when a storage operation fails
76
+ */
77
+ export class StorageError extends VectorDBError {
78
+ readonly operation: string;
79
+ readonly path?: string;
80
+
81
+ constructor(operation: string, message: string, path?: string) {
82
+ super(`Storage ${operation} failed: ${message}`, 'STORAGE_ERROR');
83
+ this.name = 'StorageError';
84
+ this.operation = operation;
85
+ this.path = path;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Thrown when quantization operations fail
91
+ */
92
+ export class QuantizationError extends VectorDBError {
93
+ constructor(message: string) {
94
+ super(message, 'QUANTIZATION_ERROR');
95
+ this.name = 'QuantizationError';
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Thrown when a vector is not found in the index
101
+ */
102
+ export class VectorNotFoundError extends VectorDBError {
103
+ readonly vectorId: string | number;
104
+
105
+ constructor(vectorId: string | number) {
106
+ super(`Vector '${vectorId}' not found`, 'VECTOR_NOT_FOUND');
107
+ this.name = 'VectorNotFoundError';
108
+ this.vectorId = vectorId;
109
+ }
110
+ }
package/src/index.ts ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * verso - High-performance vector search with HNSW indexing
3
+ *
4
+ * Features:
5
+ * - HNSW algorithm for approximate nearest neighbor search
6
+ * - Multiple distance metrics: cosine, euclidean, dot product
7
+ * - Int8 scalar quantization for 4x memory reduction
8
+ * - Batch query support for improved throughput
9
+ * - Parameter presets for different use cases
10
+ * - Multi-platform: Bun (file system) and Browser (OPFS)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { VectorDB, getRecommendedPreset } from 'verso';
15
+ *
16
+ * const db = new VectorDB();
17
+ * const preset = getRecommendedPreset(768);
18
+ *
19
+ * const collection = await db.createCollection('my-vectors', {
20
+ * dimension: 768,
21
+ * metric: 'cosine',
22
+ * M: preset.M,
23
+ * efConstruction: preset.efConstruction
24
+ * });
25
+ *
26
+ * await collection.add({
27
+ * ids: ['doc1', 'doc2'],
28
+ * vectors: [new Float32Array(768), new Float32Array(768)]
29
+ * });
30
+ *
31
+ * const results = await collection.query({
32
+ * queryVector: new Float32Array(768),
33
+ * k: 10,
34
+ * efSearch: preset.efSearch
35
+ * });
36
+ * ```
37
+ *
38
+ * @packageDocumentation
39
+ * @module verso
40
+ */
41
+
42
+ // Core components
43
+ export { VectorDB } from './VectorDB';
44
+ export type { VectorDBConfig } from './VectorDB';
45
+ export { Collection } from './Collection';
46
+ export type { AddConfig, QueryConfig, QueryResult } from './Collection';
47
+ export { HNSWIndex } from './HNSWIndex';
48
+ export type { DistanceMetric, Node } from './HNSWIndex';
49
+
50
+ // Distance backends
51
+ export type { DistanceBackend } from './backends/DistanceBackend';
52
+ export {
53
+ JsDistanceBackend,
54
+ dotProductFast,
55
+ l2SquaredFast,
56
+ normalizeInPlace,
57
+ cosineDistanceFast
58
+ } from './backends/JsDistanceBackend';
59
+
60
+ // Data structures
61
+ export { BinaryHeap } from './BinaryHeap';
62
+ export { MaxBinaryHeap } from './MaxBinaryHeap';
63
+
64
+ // Scalar Quantization (Int8 - 4x reduction)
65
+ export {
66
+ ScalarQuantizer,
67
+ QuantizedVectorStore,
68
+ dotProductInt8,
69
+ l2SquaredInt8,
70
+ cosineDistanceInt8
71
+ } from './quantization/ScalarQuantizer';
72
+ export type { QuantizationParams } from './quantization/ScalarQuantizer';
73
+
74
+
75
+ // Parameter presets
76
+ export type { HNSWPreset } from './presets';
77
+ export {
78
+ PRESET_LOW_DIM,
79
+ PRESET_MEDIUM_DIM,
80
+ PRESET_HIGH_DIM,
81
+ PRESET_VERY_HIGH_DIM,
82
+ PRESET_SMALL_DATASET,
83
+ PRESET_LARGE_DATASET,
84
+ PRESET_MAX_RECALL,
85
+ PRESET_LOW_LATENCY,
86
+ PRESETS,
87
+ getRecommendedPreset,
88
+ getPreset,
89
+ getRAGPreset
90
+ } from './presets';
91
+
92
+ // Storage backends (Bun, Browser, Memory)
93
+ export type { StorageBackend, StorageOptions } from './storage/StorageBackend';
94
+ export { BunStorageBackend } from './storage/BunStorageBackend';
95
+ export { MemoryBackend } from './storage/MemoryBackend';
96
+ export { OPFSBackend } from './storage/OPFSBackend';
97
+ export {
98
+ createStorageBackend,
99
+ getRecommendedStorageType,
100
+ isStorageTypeAvailable,
101
+ type StorageType,
102
+ type CreateStorageOptions,
103
+ } from './storage/createStorageBackend';
104
+
105
+ // Write-ahead log for incremental updates
106
+ export {
107
+ WriteAheadLog,
108
+ WALOperationType,
109
+ type WALEntry,
110
+ } from './storage/WriteAheadLog';
111
+
112
+ // Batch write coalescing for reduced I/O
113
+ export {
114
+ BatchWriter,
115
+ createBatchWriter,
116
+ type BatchWriterOptions,
117
+ } from './storage/BatchWriter';
118
+
119
+ // Delta encoding for compact neighbor lists
120
+ export {
121
+ deltaEncodeNeighbors,
122
+ deltaDecodeNeighbors,
123
+ deltaEncodedSize,
124
+ encodeVarint,
125
+ decodeVarint,
126
+ } from './encoding/DeltaEncoder';
127
+
128
+ // Error classes for better error handling
129
+ export {
130
+ VectorDBError,
131
+ DimensionMismatchError,
132
+ DuplicateVectorError,
133
+ CollectionNotFoundError,
134
+ CollectionExistsError,
135
+ StorageError,
136
+ QuantizationError,
137
+ VectorNotFoundError,
138
+ } from './errors';