@delali/narsil 0.1.7 → 0.1.9

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 (87) hide show
  1. package/README.md +800 -111
  2. package/dist/adapters/filesystem.d.ts +1 -1
  3. package/dist/adapters/filesystem.mjs +46 -2
  4. package/dist/adapters/indexeddb.d.ts +1 -1
  5. package/dist/adapters/indexeddb.mjs +1 -1
  6. package/dist/adapters/memory.d.ts +1 -1
  7. package/dist/{adapters-BbRkoiu9.d.ts → adapters-Bo2JQg8b.d.ts} +2 -1
  8. package/dist/chunk-ALB35PRQ.mjs +98 -0
  9. package/dist/chunk-KMMJMDUA.mjs +44 -0
  10. package/dist/chunk-RQLXC2YS.mjs +269 -0
  11. package/dist/chunk-S7RJJMHI.mjs +1086 -0
  12. package/dist/chunk-SVDEEMO6.mjs +7023 -0
  13. package/dist/{chunk-I24SGLFS.mjs → chunk-TW6BXTOL.mjs} +5 -1
  14. package/dist/{chunk-KHL4RPC3.mjs → chunk-VWF7RKSF.mjs} +2566 -2282
  15. package/dist/chunk-WISGW24C.mjs +455 -0
  16. package/dist/config-DX--2Owg.d.ts +472 -0
  17. package/dist/direct-executor-KC5O5D2R.mjs +4 -0
  18. package/dist/distribution/coordinator/etcd.d.ts +16 -0
  19. package/dist/distribution/coordinator/etcd.mjs +2 -0
  20. package/dist/distribution/coordinator/in-memory.d.ts +7 -0
  21. package/dist/distribution/coordinator/in-memory.mjs +1 -0
  22. package/dist/distribution.d.ts +122 -0
  23. package/dist/distribution.mjs +7410 -0
  24. package/dist/embeddings/openai.d.ts +1 -1
  25. package/dist/embeddings/openai.mjs +6 -1
  26. package/dist/index.browser.mjs +11750 -7532
  27. package/dist/index.d.ts +6 -450
  28. package/dist/index.mjs +7 -3236
  29. package/dist/invalidation/broadcast-channel.d.ts +1 -1
  30. package/dist/invalidation/filesystem.d.ts +1 -1
  31. package/dist/invalidation/noop.d.ts +1 -1
  32. package/dist/{language-ClUGLuoC.d.ts → language-ONQ3iVDf.d.ts} +1 -0
  33. package/dist/languages/arabic.d.ts +1 -1
  34. package/dist/languages/armenian.d.ts +1 -1
  35. package/dist/languages/bulgarian.d.ts +1 -1
  36. package/dist/languages/chinese.d.ts +1 -1
  37. package/dist/languages/dagbani.d.ts +1 -1
  38. package/dist/languages/danish.d.ts +1 -1
  39. package/dist/languages/dutch.d.ts +1 -1
  40. package/dist/languages/english.d.ts +1 -1
  41. package/dist/languages/english.mjs +1 -1
  42. package/dist/languages/ewe.d.ts +1 -1
  43. package/dist/languages/finnish.d.ts +1 -1
  44. package/dist/languages/french.d.ts +1 -1
  45. package/dist/languages/ga.d.ts +1 -1
  46. package/dist/languages/german.d.ts +1 -1
  47. package/dist/languages/greek.d.ts +1 -1
  48. package/dist/languages/hausa.d.ts +1 -1
  49. package/dist/languages/hindi.d.ts +1 -1
  50. package/dist/languages/hungarian.d.ts +1 -1
  51. package/dist/languages/igbo.d.ts +1 -1
  52. package/dist/languages/indonesian.d.ts +1 -1
  53. package/dist/languages/irish.d.ts +1 -1
  54. package/dist/languages/italian.d.ts +1 -1
  55. package/dist/languages/japanese.d.ts +1 -1
  56. package/dist/languages/nepali.d.ts +1 -1
  57. package/dist/languages/norwegian.d.ts +1 -1
  58. package/dist/languages/portuguese.d.ts +1 -1
  59. package/dist/languages/romanian.d.ts +1 -1
  60. package/dist/languages/russian.d.ts +1 -1
  61. package/dist/languages/sanskrit.d.ts +1 -1
  62. package/dist/languages/serbian.d.ts +1 -1
  63. package/dist/languages/slovenian.d.ts +1 -1
  64. package/dist/languages/spanish.d.ts +1 -1
  65. package/dist/languages/swahili.d.ts +1 -1
  66. package/dist/languages/swedish.d.ts +1 -1
  67. package/dist/languages/tamil.d.ts +1 -1
  68. package/dist/languages/turkish.d.ts +1 -1
  69. package/dist/languages/twi.d.ts +1 -1
  70. package/dist/languages/ukrainian.d.ts +1 -1
  71. package/dist/languages/yoruba.d.ts +1 -1
  72. package/dist/languages/zulu.d.ts +1 -1
  73. package/dist/narsil-B3jKyXCr.d.ts +72 -0
  74. package/dist/persistence/durability/checkpoint-worker.d.ts +48 -0
  75. package/dist/persistence/durability/checkpoint-worker.mjs +59 -0
  76. package/dist/schema-C3VhNvfw.d.ts +60 -0
  77. package/dist/serialization/crc32-worker.d.ts +20 -0
  78. package/dist/serialization/crc32-worker.mjs +61 -0
  79. package/dist/server.d.ts +218 -0
  80. package/dist/server.mjs +1184 -0
  81. package/dist/types-CTVVm4GG.d.ts +73 -0
  82. package/dist/vector/hnsw-build-worker.mjs +1 -1
  83. package/dist/workers/entry.mjs +1 -1
  84. package/package.json +38 -2
  85. package/dist/chunk-R2C2EQGO.mjs +0 -53
  86. package/dist/chunk-VVORJOXR.mjs +0 -759
  87. package/dist/direct-executor-LBJLGMJ2.mjs +0 -4
@@ -0,0 +1,1086 @@
1
+ // src/vector/simd-wasm-binary.ts
2
+ var SIMD_DISTANCE_WASM_BASE64 = "AGFzbQEAAAABFQNgA39/fwF9YAJ/fwF9YAN/f38BfwMHBgABAAACAgUDAQABB2sHBm1lbW9yeQIAC2RvdF9wcm9kdWN0AAAJbWFnbml0dWRlAAEac3F1YXJlZF9ldWNsaWRlYW5fZGlzdGFuY2UAAhJldWNsaWRlYW5fZGlzdGFuY2UAAwZkb3RfdTgABAlzcWRpc3RfdTgABQqvBwauAQQBfwF7AX0BfyACQXxxIQb9DAAAAAAAAAAAAAAAAAAAAAAhBEEAIQMCQANAIAMgBk8NASAEIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/eYB/eQBIQQgA0EEaiEDDAALCyAE/R8AIAT9HwGSIAT9HwIgBP0fA5KSIQUCQANAIAMgAk8NASAFIAAgA0ECdGoqAgAgASADQQJ0aioCAJSSIQUgA0EBaiEDDAALCyAFC6kBBAF/AnsBfQF/IAFBfHEhBv0MAAAAAAAAAAAAAAAAAAAAACEDQQAhAgJAA0AgAiAGTw0BIAAgAkECdGr9AAQAIQQgAyAEIAT95gH95AEhAyACQQRqIQIMAAsLIAP9HwAgA/0fAZIgA/0fAiAD/R8DkpIhBQJAA0AgAiABTw0BIAUgACACQQJ0aioCACAAIAJBAnRqKgIAlJIhBSACQQFqIQIMAAsLIAWRC8ABBQF/AnsBfQF/AX0gAkF8cSEH/QwAAAAAAAAAAAAAAAAAAAAAIQRBACEDAkADQCADIAdPDQEgACADQQJ0av0ABAAgASADQQJ0av0ABAD95QEhBSAEIAUgBf3mAf3kASEEIANBBGohAwwACwsgBP0fACAE/R8BkiAE/R8CIAT9HwOSkiEGAkADQCADIAJPDQEgACADQQJ0aioCACABIANBAnRqKgIAkyEIIAYgCCAIlJIhBiADQQFqIQMMAAsLIAYLCwAgACABIAIQApELtAEDAX8DewJ/IAJBcHEhCEEA/REhBEEAIQMCQANAIAMgCE8NASAAIANq/QAEACEFIAEgA2r9AAQAIQYgBCAF/YkBIAb9iQH9ugH9rgEhBCAEIAX9igEgBv2KAf26Af2uASEEIANBEGohAwwACwsgBP0bACAE/RsBaiAE/RsCIAT9GwNqaiEHAkADQCADIAJPDQEgByAAIANqLQAAIAEgA2otAABsaiEHIANBAWohAwwACwsgBwvNAQMBfwV7A38gAkFwcSELQQD9ESEEQQAhAwJAA0AgAyALTw0BIAAgA2r9AAQAIQUgASADav0ABAAhBiAF/YkBIAb9iQH9kQEhByAF/YoBIAb9igH9kQEhCCAEIAcgB/26Af2uASEEIAQgCCAI/boB/a4BIQQgA0EQaiEDDAALCyAE/RsAIAT9GwFqIAT9GwIgBP0bA2pqIQkCQANAIAMgAk8NASAAIANqLQAAIAEgA2otAABrIQogCSAKIApsaiEJIANBAWohAwwACwsgCQs=";
3
+
4
+ // src/vector/simd.ts
5
+ var wasmExports = null;
6
+ var f32View = null;
7
+ function decodeBase64(base64) {
8
+ if (typeof Buffer !== "undefined") {
9
+ return new Uint8Array(Buffer.from(base64, "base64"));
10
+ }
11
+ const binary = atob(base64);
12
+ const bytes = new Uint8Array(binary.length);
13
+ for (let i = 0; i < binary.length; i++) {
14
+ bytes[i] = binary.charCodeAt(i);
15
+ }
16
+ return bytes;
17
+ }
18
+ try {
19
+ const bytes = decodeBase64(SIMD_DISTANCE_WASM_BASE64);
20
+ const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
21
+ if (typeof WebAssembly !== "undefined" && typeof WebAssembly.validate === "function" && WebAssembly.validate(wasmBuffer)) {
22
+ const module = new WebAssembly.Module(wasmBuffer);
23
+ const instance = new WebAssembly.Instance(module);
24
+ wasmExports = instance.exports;
25
+ f32View = new Float32Array(wasmExports.memory.buffer);
26
+ }
27
+ } catch {
28
+ wasmExports = null;
29
+ f32View = null;
30
+ }
31
+ var OFFSET_B_BYTES = 16384;
32
+ function ensureMemory(dim) {
33
+ if (!wasmExports || !f32View) return;
34
+ const needed = OFFSET_B_BYTES + dim * 4;
35
+ const currentSize = wasmExports.memory.buffer.byteLength;
36
+ if (needed > currentSize) {
37
+ const pagesNeeded = Math.ceil(needed / 65536);
38
+ const currentPages = currentSize / 65536;
39
+ wasmExports.memory.grow(pagesNeeded - currentPages);
40
+ f32View = new Float32Array(wasmExports.memory.buffer);
41
+ }
42
+ }
43
+ function getF32View() {
44
+ if (!f32View) {
45
+ throw new Error("SIMD memory is not initialized");
46
+ }
47
+ return f32View;
48
+ }
49
+ function copyOneVector(a) {
50
+ getF32View().set(a, 0);
51
+ }
52
+ function copyTwoVectors(a, b) {
53
+ const view = getF32View();
54
+ view.set(a, 0);
55
+ view.set(b, OFFSET_B_BYTES / 4);
56
+ }
57
+ function simdDotProduct(a, b) {
58
+ if (!wasmExports || !f32View) return null;
59
+ ensureMemory(a.length);
60
+ copyTwoVectors(a, b);
61
+ return wasmExports.dot_product(0, OFFSET_B_BYTES, a.length);
62
+ }
63
+ function simdMagnitude(a) {
64
+ if (!wasmExports || !f32View) return null;
65
+ ensureMemory(a.length);
66
+ copyOneVector(a);
67
+ return wasmExports.magnitude(0, a.length);
68
+ }
69
+ function simdEuclideanDistance(a, b) {
70
+ if (!wasmExports || !f32View) return null;
71
+ ensureMemory(a.length);
72
+ copyTwoVectors(a, b);
73
+ return wasmExports.euclidean_distance(0, OFFSET_B_BYTES, a.length);
74
+ }
75
+ function isSimdAvailable() {
76
+ return wasmExports !== null;
77
+ }
78
+ var arenaModule;
79
+ function getArenaModule() {
80
+ if (arenaModule !== void 0) return arenaModule;
81
+ try {
82
+ const bytes = decodeBase64(SIMD_DISTANCE_WASM_BASE64);
83
+ const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
84
+ if (typeof WebAssembly === "undefined" || typeof WebAssembly.validate !== "function" || !WebAssembly.validate(wasmBuffer)) {
85
+ arenaModule = null;
86
+ return null;
87
+ }
88
+ arenaModule = new WebAssembly.Module(wasmBuffer);
89
+ } catch {
90
+ arenaModule = null;
91
+ }
92
+ return arenaModule;
93
+ }
94
+ function createArenaSimd() {
95
+ try {
96
+ const module = getArenaModule();
97
+ if (module === null) return null;
98
+ const instance = new WebAssembly.Instance(module);
99
+ const exports$1 = instance.exports;
100
+ if (typeof exports$1.dot_u8 !== "function" || typeof exports$1.sqdist_u8 !== "function" || !(exports$1.memory instanceof WebAssembly.Memory)) {
101
+ return null;
102
+ }
103
+ return exports$1;
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ // src/vector/similarity.ts
110
+ function magnitude(v) {
111
+ const result = simdMagnitude(v);
112
+ if (result !== null) return result;
113
+ let sum = 0;
114
+ for (let i = 0; i < v.length; i++) {
115
+ sum += v[i] * v[i];
116
+ }
117
+ return Math.sqrt(sum);
118
+ }
119
+ function cosineSimilarityWithMagnitudes(a, b, magA, magB) {
120
+ if (magA === 0 || magB === 0) return 0;
121
+ return dotProduct(a, b) / (magA * magB);
122
+ }
123
+ function dotProduct(a, b) {
124
+ const result = simdDotProduct(a, b);
125
+ if (result !== null) return result;
126
+ let sum = 0;
127
+ for (let i = 0; i < a.length; i++) {
128
+ sum += a[i] * b[i];
129
+ }
130
+ return sum;
131
+ }
132
+ function euclideanDistance(a, b) {
133
+ const result = simdEuclideanDistance(a, b);
134
+ if (result !== null) return result;
135
+ let sum = 0;
136
+ for (let i = 0; i < a.length; i++) {
137
+ const diff = a[i] - b[i];
138
+ sum += diff * diff;
139
+ }
140
+ return Math.sqrt(sum);
141
+ }
142
+
143
+ // src/vector/vector-store.ts
144
+ var INITIAL_CAPACITY = 16;
145
+ var PAGE_BYTES = 65536;
146
+ function createVectorStore() {
147
+ const docToOrd = /* @__PURE__ */ new Map();
148
+ const recycledOrds = /* @__PURE__ */ new Map();
149
+ const ordToDoc = [];
150
+ let simd = createArenaSimd();
151
+ let dimension = 0;
152
+ let capacity = 0;
153
+ let arena = new Float32Array(0);
154
+ let mags = new Float64Array(0);
155
+ let liveCount = 0;
156
+ function initStorage(dim) {
157
+ dimension = dim;
158
+ if (simd) {
159
+ arena = new Float32Array(simd.memory.buffer);
160
+ }
161
+ }
162
+ function ensureCapacity2(needed) {
163
+ if (needed <= capacity) return;
164
+ let newCap = capacity === 0 ? INITIAL_CAPACITY : capacity;
165
+ while (newCap < needed) newCap *= 2;
166
+ const nextMags = new Float64Array(newCap);
167
+ nextMags.set(mags);
168
+ mags = nextMags;
169
+ if (simd) {
170
+ const requiredBytes = newCap * dimension * 4;
171
+ const have = simd.memory.buffer.byteLength;
172
+ if (requiredBytes > have) {
173
+ try {
174
+ simd.memory.grow(Math.ceil((requiredBytes - have) / PAGE_BYTES));
175
+ } catch {
176
+ const migrated = new Float32Array(newCap * dimension);
177
+ migrated.set(arena.subarray(0, capacity * dimension));
178
+ arena = migrated;
179
+ simd = null;
180
+ capacity = newCap;
181
+ return;
182
+ }
183
+ }
184
+ arena = new Float32Array(simd.memory.buffer);
185
+ } else {
186
+ const nextArena = new Float32Array(newCap * dimension);
187
+ nextArena.set(arena.subarray(0, capacity * dimension));
188
+ arena = nextArena;
189
+ }
190
+ capacity = newCap;
191
+ }
192
+ function writeVector(ord, vector) {
193
+ arena.set(vector, ord * dimension);
194
+ mags[ord] = magnitude(vector);
195
+ }
196
+ function entryAt(ord) {
197
+ const base = ord * dimension;
198
+ return { vector: arena.subarray(base, base + dimension), magnitude: mags[ord] };
199
+ }
200
+ return {
201
+ get size() {
202
+ return liveCount;
203
+ },
204
+ insert(docId, vector) {
205
+ if (dimension === 0) initStorage(vector.length);
206
+ const existing = docToOrd.get(docId);
207
+ if (existing !== void 0) {
208
+ writeVector(existing, vector);
209
+ return;
210
+ }
211
+ const recycled = recycledOrds.get(docId);
212
+ if (recycled !== void 0) {
213
+ recycledOrds.delete(docId);
214
+ docToOrd.set(docId, recycled);
215
+ ordToDoc[recycled] = docId;
216
+ ensureCapacity2(recycled + 1);
217
+ writeVector(recycled, vector);
218
+ liveCount++;
219
+ return;
220
+ }
221
+ const ord = ordToDoc.length;
222
+ docToOrd.set(docId, ord);
223
+ ordToDoc.push(docId);
224
+ ensureCapacity2(ord + 1);
225
+ writeVector(ord, vector);
226
+ liveCount++;
227
+ },
228
+ remove(docId) {
229
+ const ord = docToOrd.get(docId);
230
+ if (ord === void 0) return;
231
+ docToOrd.delete(docId);
232
+ ordToDoc[ord] = void 0;
233
+ recycledOrds.set(docId, ord);
234
+ liveCount--;
235
+ },
236
+ get(docId) {
237
+ const ord = docToOrd.get(docId);
238
+ return ord === void 0 ? void 0 : entryAt(ord);
239
+ },
240
+ has(docId) {
241
+ return docToOrd.has(docId);
242
+ },
243
+ *entries() {
244
+ for (let ord = 0; ord < ordToDoc.length; ord++) {
245
+ const docId = ordToDoc[ord];
246
+ if (docId === void 0) continue;
247
+ yield [docId, entryAt(ord)];
248
+ }
249
+ },
250
+ clear() {
251
+ docToOrd.clear();
252
+ recycledOrds.clear();
253
+ ordToDoc.length = 0;
254
+ mags = new Float64Array(0);
255
+ if (!simd) {
256
+ arena = new Float32Array(0);
257
+ }
258
+ dimension = 0;
259
+ capacity = 0;
260
+ liveCount = 0;
261
+ },
262
+ getOrdinal(docId) {
263
+ return docToOrd.get(docId);
264
+ },
265
+ docIdForOrdinal(ordinal) {
266
+ return ordToDoc[ordinal];
267
+ },
268
+ entryForOrdinal(ordinal) {
269
+ if (ordinal < 0 || ordinal >= ordToDoc.length || ordToDoc[ordinal] === void 0) return void 0;
270
+ return entryAt(ordinal);
271
+ },
272
+ distanceByOrdinal(ordA, ordB, metric) {
273
+ if (ordToDoc[ordA] === void 0 || ordToDoc[ordB] === void 0) return Number.POSITIVE_INFINITY;
274
+ if (simd) {
275
+ const byteA = ordA * dimension * 4;
276
+ const byteB = ordB * dimension * 4;
277
+ if (metric === "euclidean") {
278
+ return Math.sqrt(simd.squared_euclidean_distance(byteA, byteB, dimension));
279
+ }
280
+ const dot = simd.dot_product(byteA, byteB, dimension);
281
+ if (metric === "dotProduct") return -dot;
282
+ const magA = mags[ordA];
283
+ const magB = mags[ordB];
284
+ if (magA === 0 || magB === 0) return 1;
285
+ return 1 - dot / (magA * magB);
286
+ }
287
+ const baseA = ordA * dimension;
288
+ const baseB = ordB * dimension;
289
+ const a = arena.subarray(baseA, baseA + dimension);
290
+ const b = arena.subarray(baseB, baseB + dimension);
291
+ switch (metric) {
292
+ case "cosine":
293
+ return 1 - cosineSimilarityWithMagnitudes(a, b, mags[ordA], mags[ordB]);
294
+ case "dotProduct":
295
+ return -dotProduct(a, b);
296
+ case "euclidean":
297
+ return euclideanDistance(a, b);
298
+ }
299
+ },
300
+ estimateMemory(dimension2) {
301
+ const count = liveCount;
302
+ if (count === 0) return 0;
303
+ const MAP_OVERHEAD = 64;
304
+ const MAP_ENTRY = 72;
305
+ const AVG_DOCID_BYTES = 56;
306
+ const MAGNITUDE_BYTES = 8;
307
+ const ORDINAL_SLOT = 16;
308
+ const perEntry = MAP_ENTRY + AVG_DOCID_BYTES + MAGNITUDE_BYTES + ORDINAL_SLOT;
309
+ return MAP_OVERHEAD + count * (perEntry + dimension2 * 4);
310
+ }
311
+ };
312
+ }
313
+
314
+ // src/core/heap.ts
315
+ function createMinHeap(compare) {
316
+ return createHeap(compare);
317
+ }
318
+ function createMaxHeap(compare) {
319
+ return createHeap((a, b) => compare(b, a));
320
+ }
321
+ function createBoundedMaxHeap(compare, capacity) {
322
+ const heap = createMaxHeap(compare);
323
+ const innerPush = heap.push;
324
+ return {
325
+ push(item) {
326
+ if (heap.size < capacity) {
327
+ innerPush(item);
328
+ return;
329
+ }
330
+ const top = heap.peek();
331
+ if (top !== void 0 && compare(item, top) < 0) {
332
+ heap.pop();
333
+ innerPush(item);
334
+ }
335
+ },
336
+ pop: heap.pop,
337
+ peek: heap.peek,
338
+ get size() {
339
+ return heap.size;
340
+ },
341
+ toSortedArray: heap.toSortedArray
342
+ };
343
+ }
344
+ function createHeap(compare) {
345
+ const items = [];
346
+ function siftUp(idx) {
347
+ while (idx > 0) {
348
+ const parent = idx - 1 >> 1;
349
+ if (compare(items[idx], items[parent]) >= 0) break;
350
+ const tmp = items[idx];
351
+ items[idx] = items[parent];
352
+ items[parent] = tmp;
353
+ idx = parent;
354
+ }
355
+ }
356
+ function siftDown(idx) {
357
+ const len = items.length;
358
+ while (true) {
359
+ let smallest = idx;
360
+ const left = 2 * idx + 1;
361
+ const right = 2 * idx + 2;
362
+ if (left < len && compare(items[left], items[smallest]) < 0) smallest = left;
363
+ if (right < len && compare(items[right], items[smallest]) < 0) smallest = right;
364
+ if (smallest === idx) break;
365
+ const tmp = items[idx];
366
+ items[idx] = items[smallest];
367
+ items[smallest] = tmp;
368
+ idx = smallest;
369
+ }
370
+ }
371
+ return {
372
+ push(item) {
373
+ items.push(item);
374
+ siftUp(items.length - 1);
375
+ },
376
+ pop() {
377
+ if (items.length === 0) return void 0;
378
+ const top = items[0];
379
+ const last = items.pop();
380
+ if (items.length > 0) {
381
+ items[0] = last;
382
+ siftDown(0);
383
+ }
384
+ return top;
385
+ },
386
+ peek() {
387
+ return items[0];
388
+ },
389
+ get size() {
390
+ return items.length;
391
+ },
392
+ toSortedArray() {
393
+ return [...items].sort(compare);
394
+ }
395
+ };
396
+ }
397
+
398
+ // src/vector/hnsw/shared.ts
399
+ var MAX_LAYER_CAP = 32;
400
+ var COMPACTION_TOMBSTONE_RATIO = 0.1;
401
+ var COMPACTION_ABSOLUTE_THRESHOLD = 1e3;
402
+ var SQ8_OVERSELECTION_FACTOR = 2;
403
+ function ensureCapacity(state, needed) {
404
+ if (needed <= state.capacity) return;
405
+ let newCap = state.capacity === 0 ? 16 : state.capacity;
406
+ while (newCap < needed) newCap *= 2;
407
+ const nextTombstones = new Uint8Array(newCap);
408
+ nextTombstones.set(state.tombstones);
409
+ state.tombstones = nextTombstones;
410
+ state.visited = new Uint32Array(newCap);
411
+ state.visitStamp = 0;
412
+ state.capacity = newCap;
413
+ }
414
+ function nextVisitStamp(state) {
415
+ state.visitStamp++;
416
+ if (state.visitStamp === 4294967295) {
417
+ state.visited.fill(0);
418
+ state.visitStamp = 1;
419
+ }
420
+ return state.visitStamp;
421
+ }
422
+ function toDistance(a, b, magA, magB, metric) {
423
+ switch (metric) {
424
+ case "cosine":
425
+ return 1 - cosineSimilarityWithMagnitudes(a, b, magA, magB);
426
+ case "dotProduct":
427
+ return -dotProduct(a, b);
428
+ case "euclidean":
429
+ return euclideanDistance(a, b);
430
+ }
431
+ }
432
+ function toScore(distance, metric) {
433
+ switch (metric) {
434
+ case "cosine":
435
+ return 1 - distance;
436
+ case "dotProduct":
437
+ return -distance;
438
+ case "euclidean":
439
+ return 1 / (1 + distance);
440
+ }
441
+ }
442
+ var distanceAsc = (a, b) => a.distance - b.distance;
443
+ var distanceDesc = (a, b) => b.distance - a.distance;
444
+ function randomLevel(mL) {
445
+ let u = Math.random();
446
+ if (u === 0) u = Number.MIN_VALUE;
447
+ return Math.min(Math.floor(-Math.log(u) * mL), MAX_LAYER_CAP);
448
+ }
449
+ function entryForOrd(state, ord) {
450
+ return state.store.entryForOrdinal(ord);
451
+ }
452
+ function nodeDistanceByOrd(state, aOrd, bOrd, metric) {
453
+ return state.store.distanceByOrdinal(aOrd, bOrd, metric);
454
+ }
455
+ function queryDistanceByOrd(state, qVec, qMag, ord, metric) {
456
+ const entry = state.store.entryForOrdinal(ord);
457
+ if (!entry) return Number.POSITIVE_INFINITY;
458
+ return toDistance(qVec, entry.vector, qMag, entry.magnitude, metric);
459
+ }
460
+ function maxConns(state, layer) {
461
+ return layer === 0 ? state.Mmax0 : state.M;
462
+ }
463
+ function addConnection(connections, ord) {
464
+ for (let i = 0; i < connections.length; i++) {
465
+ if (connections[i] === ord) return;
466
+ }
467
+ connections.push(ord);
468
+ }
469
+
470
+ // src/vector/hnsw/graph-ops.ts
471
+ function searchLayer(state, qVec, qMag, eps, ef, layer, metric, skipTombstones, distFn) {
472
+ const getDistance = distFn ?? ((ord) => queryDistanceByOrd(state, qVec, qMag, ord, metric));
473
+ const visited = state.visited;
474
+ const stamp = nextVisitStamp(state);
475
+ const candidates = createMinHeap(distanceAsc);
476
+ const results = createMinHeap(distanceDesc);
477
+ let furthestDist = Number.POSITIVE_INFINITY;
478
+ for (const epOrd of eps) {
479
+ if (visited[epOrd] === stamp) continue;
480
+ visited[epOrd] = stamp;
481
+ const node = state.nodesByOrd[epOrd];
482
+ if (!node) continue;
483
+ if (skipTombstones && state.tombstones[epOrd] === 1) continue;
484
+ const dist = getDistance(epOrd);
485
+ if (dist === Number.POSITIVE_INFINITY) continue;
486
+ const pair = { ord: epOrd, distance: dist };
487
+ candidates.push(pair);
488
+ results.push(pair);
489
+ if (results.size > ef) {
490
+ results.pop();
491
+ }
492
+ }
493
+ const topResult = results.peek();
494
+ if (topResult) furthestDist = topResult.distance;
495
+ while (candidates.size > 0) {
496
+ const nearest = candidates.pop();
497
+ if (!nearest) break;
498
+ if (nearest.distance > furthestDist) break;
499
+ const node = state.nodesByOrd[nearest.ord];
500
+ if (!node || layer >= node.connections.length) continue;
501
+ for (const neighborOrd of node.connections[layer]) {
502
+ if (visited[neighborOrd] === stamp) continue;
503
+ visited[neighborOrd] = stamp;
504
+ if (skipTombstones && state.tombstones[neighborOrd] === 1) continue;
505
+ const neighborNode = state.nodesByOrd[neighborOrd];
506
+ if (!neighborNode) continue;
507
+ const dist = getDistance(neighborOrd);
508
+ if (dist === Number.POSITIVE_INFINITY) continue;
509
+ if (dist < furthestDist || results.size < ef) {
510
+ const pair = { ord: neighborOrd, distance: dist };
511
+ candidates.push(pair);
512
+ results.push(pair);
513
+ if (results.size > ef) {
514
+ results.pop();
515
+ }
516
+ const newTop = results.peek();
517
+ if (newTop) furthestDist = newTop.distance;
518
+ }
519
+ }
520
+ }
521
+ return results;
522
+ }
523
+ function nearestFromHeap(heap) {
524
+ let nearest;
525
+ while (heap.size > 0) {
526
+ nearest = heap.pop();
527
+ }
528
+ return nearest;
529
+ }
530
+ function selectNeighborsHeuristic(state, targetOrd, candidates, maxConnections, layer, metric, extendCandidates, keepPruned) {
531
+ const working = [...candidates];
532
+ working.sort((a, b) => a.distance - b.distance);
533
+ const selected = [];
534
+ const discarded = [];
535
+ for (const candidate of working) {
536
+ if (selected.length >= maxConnections) break;
537
+ let accepted = true;
538
+ for (const sel of selected) {
539
+ const distBetween = nodeDistanceByOrd(state, candidate.ord, sel.ord, metric);
540
+ if (candidate.distance >= distBetween) {
541
+ accepted = false;
542
+ break;
543
+ }
544
+ }
545
+ if (accepted) {
546
+ selected.push(candidate);
547
+ } else {
548
+ discarded.push(candidate);
549
+ }
550
+ }
551
+ {
552
+ for (const disc of discarded) {
553
+ if (selected.length >= maxConnections) break;
554
+ selected.push(disc);
555
+ }
556
+ }
557
+ return selected;
558
+ }
559
+ function pruneConnections(state, ord, layer, metric) {
560
+ const node = state.nodesByOrd[ord];
561
+ if (!node) return;
562
+ const mc = maxConns(state, layer);
563
+ if (node.connections[layer].length <= mc) return;
564
+ const conns = [];
565
+ for (const connOrd of node.connections[layer]) {
566
+ const dist = nodeDistanceByOrd(state, ord, connOrd, metric);
567
+ if (dist === Number.POSITIVE_INFINITY) continue;
568
+ conns.push({ ord: connOrd, distance: dist });
569
+ }
570
+ const pruned = selectNeighborsHeuristic(state, ord, conns, mc, layer, metric);
571
+ const next = [];
572
+ for (const p of pruned) addConnection(next, p.ord);
573
+ node.connections[layer] = next;
574
+ }
575
+
576
+ // src/vector/hnsw/mutation.ts
577
+ function removeFromArray(arr, ord) {
578
+ const idx = arr.indexOf(ord);
579
+ if (idx !== -1) arr.splice(idx, 1);
580
+ }
581
+ function clearTombstone(state, ord) {
582
+ if (state.tombstones[ord] === 1) {
583
+ state.tombstones[ord] = 0;
584
+ state.tombstoneCount--;
585
+ }
586
+ }
587
+ function reassignEntryPoint(state) {
588
+ let bestOrd = -1;
589
+ let bestLayer = -1;
590
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
591
+ const n = state.nodesByOrd[ord];
592
+ if (!n) continue;
593
+ if (state.tombstones[ord] === 1) continue;
594
+ if (n.maxLayer > bestLayer) {
595
+ bestLayer = n.maxLayer;
596
+ bestOrd = ord;
597
+ }
598
+ }
599
+ if (bestOrd === -1) {
600
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
601
+ const n = state.nodesByOrd[ord];
602
+ if (!n) continue;
603
+ if (n.maxLayer > bestLayer) {
604
+ bestLayer = n.maxLayer;
605
+ bestOrd = ord;
606
+ }
607
+ }
608
+ }
609
+ state.entryPointOrd = bestOrd;
610
+ state.topLayer = bestOrd === -1 ? -1 : bestLayer;
611
+ }
612
+ function insertNode(state, docId) {
613
+ const ord = state.store.getOrdinal(docId);
614
+ if (ord === void 0) {
615
+ throw new Error(`Cannot insert HNSW node: vector for "${docId}" not found in VectorStore`);
616
+ }
617
+ const entry = state.store.entryForOrdinal(ord);
618
+ if (!entry) {
619
+ throw new Error(`Cannot insert HNSW node: vector for "${docId}" not found in VectorStore`);
620
+ }
621
+ if (entry.vector.length !== state.dimension) {
622
+ throw new Error(`Vector dimension mismatch: expected ${state.dimension}, got ${entry.vector.length}`);
623
+ }
624
+ ensureCapacity(state, ord + 1);
625
+ if (state.nodesByOrd[ord] !== void 0) {
626
+ removeNodeEager(state, ord);
627
+ }
628
+ clearTombstone(state, ord);
629
+ const l = randomLevel(state.mL);
630
+ const node = {
631
+ maxLayer: l,
632
+ connections: Array.from({ length: l + 1 }, () => [])
633
+ };
634
+ state.nodesByOrd[ord] = node;
635
+ state.nodeCount++;
636
+ if (state.entryPointOrd === -1) {
637
+ state.entryPointOrd = ord;
638
+ state.topLayer = l;
639
+ return;
640
+ }
641
+ const metric = state.buildMetric;
642
+ const insertDistFn = (candOrd) => nodeDistanceByOrd(state, ord, candOrd, metric);
643
+ let currentEPs = [state.entryPointOrd];
644
+ for (let layer = state.topLayer; layer > l; layer--) {
645
+ const heap = searchLayer(state, entry.vector, entry.magnitude, currentEPs, 1, layer, metric, false, insertDistFn);
646
+ const nearest = nearestFromHeap(heap);
647
+ if (nearest) {
648
+ currentEPs = [nearest.ord];
649
+ }
650
+ }
651
+ for (let layer = Math.min(l, state.topLayer); layer >= 0; layer--) {
652
+ const heap = searchLayer(
653
+ state,
654
+ entry.vector,
655
+ entry.magnitude,
656
+ currentEPs,
657
+ state.efCons,
658
+ layer,
659
+ metric,
660
+ false,
661
+ insertDistFn
662
+ );
663
+ const candidates = heap.toSortedArray().reverse();
664
+ const mc = maxConns(state, layer);
665
+ const neighbors = selectNeighborsHeuristic(state, ord, candidates, mc, layer, metric);
666
+ for (const neighbor of neighbors) {
667
+ addConnection(node.connections[layer], neighbor.ord);
668
+ const neighborNode = state.nodesByOrd[neighbor.ord];
669
+ if (neighborNode && layer < neighborNode.connections.length) {
670
+ addConnection(neighborNode.connections[layer], ord);
671
+ pruneConnections(state, neighbor.ord, layer, metric);
672
+ }
673
+ }
674
+ if (candidates.length > 0) {
675
+ currentEPs = candidates.map((n) => n.ord);
676
+ }
677
+ }
678
+ if (l > state.topLayer) {
679
+ state.entryPointOrd = ord;
680
+ state.topLayer = l;
681
+ }
682
+ }
683
+ function removeNodeEager(state, ord, excludeOrds) {
684
+ const node = state.nodesByOrd[ord];
685
+ if (!node) return;
686
+ const metric = state.buildMetric;
687
+ for (let layer = 0; layer <= node.maxLayer; layer++) {
688
+ if (layer >= node.connections.length) continue;
689
+ const formerNeighbors = [...node.connections[layer]];
690
+ for (const neighborOrd of formerNeighbors) {
691
+ const neighborNode = state.nodesByOrd[neighborOrd];
692
+ if (neighborNode && layer < neighborNode.connections.length) {
693
+ removeFromArray(neighborNode.connections[layer], ord);
694
+ }
695
+ }
696
+ for (const neighborOrd of formerNeighbors) {
697
+ const neighborNode = state.nodesByOrd[neighborOrd];
698
+ if (!neighborNode || layer >= neighborNode.connections.length) continue;
699
+ const mc = maxConns(state, layer);
700
+ if (neighborNode.connections[layer].length >= mc) continue;
701
+ const candidateOrds = new Set(neighborNode.connections[layer]);
702
+ for (const otherOrd of formerNeighbors) {
703
+ if (otherOrd !== neighborOrd && otherOrd !== ord) {
704
+ if (excludeOrds?.has(otherOrd)) continue;
705
+ candidateOrds.add(otherOrd);
706
+ }
707
+ }
708
+ const candidates = [];
709
+ for (const candOrd of candidateOrds) {
710
+ const dist = nodeDistanceByOrd(state, neighborOrd, candOrd, metric);
711
+ if (dist === Number.POSITIVE_INFINITY) continue;
712
+ candidates.push({ ord: candOrd, distance: dist });
713
+ }
714
+ const selected = selectNeighborsHeuristic(state, neighborOrd, candidates, mc, layer, metric);
715
+ const newConns = [];
716
+ for (const s of selected) addConnection(newConns, s.ord);
717
+ neighborNode.connections[layer] = newConns;
718
+ for (const newConnOrd of newConns) {
719
+ const newConnNode = state.nodesByOrd[newConnOrd];
720
+ if (newConnNode && layer < newConnNode.connections.length) {
721
+ addConnection(newConnNode.connections[layer], neighborOrd);
722
+ pruneConnections(state, newConnOrd, layer, metric);
723
+ }
724
+ }
725
+ }
726
+ }
727
+ state.nodesByOrd[ord] = void 0;
728
+ state.nodeCount--;
729
+ clearTombstone(state, ord);
730
+ if (state.entryPointOrd === ord) {
731
+ if (state.nodeCount === 0) {
732
+ state.entryPointOrd = -1;
733
+ state.topLayer = -1;
734
+ return;
735
+ }
736
+ reassignEntryPoint(state);
737
+ }
738
+ }
739
+ function markTombstone(state, docId) {
740
+ const ord = state.store.getOrdinal(docId);
741
+ if (ord === void 0 || state.nodesByOrd[ord] === void 0) return;
742
+ if (state.tombstones[ord] === 0) {
743
+ state.tombstones[ord] = 1;
744
+ state.tombstoneCount++;
745
+ }
746
+ if (state.entryPointOrd === ord) {
747
+ let bestOrd = -1;
748
+ let bestLayer = -1;
749
+ for (let o = 0; o < state.nodesByOrd.length; o++) {
750
+ const n = state.nodesByOrd[o];
751
+ if (!n) continue;
752
+ if (state.tombstones[o] === 1) continue;
753
+ if (n.maxLayer > bestLayer) {
754
+ bestLayer = n.maxLayer;
755
+ bestOrd = o;
756
+ }
757
+ }
758
+ if (bestOrd !== -1) {
759
+ state.entryPointOrd = bestOrd;
760
+ state.topLayer = bestLayer;
761
+ } else {
762
+ state.entryPointOrd = -1;
763
+ state.topLayer = -1;
764
+ }
765
+ }
766
+ }
767
+ function compactTombstones(state) {
768
+ if (state.tombstoneCount === 0) return;
769
+ const tombstonedOrds = [];
770
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
771
+ if (state.tombstones[ord] === 1 && state.nodesByOrd[ord] !== void 0) {
772
+ tombstonedOrds.push(ord);
773
+ }
774
+ }
775
+ const excl = new Set(tombstonedOrds);
776
+ for (const ord of tombstonedOrds) {
777
+ removeNodeEager(state, ord, excl);
778
+ }
779
+ }
780
+ function rebuild(state) {
781
+ if (state.tombstoneCount === 0 && state.nodeCount === 0) return;
782
+ const liveOrds = [];
783
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
784
+ if (state.nodesByOrd[ord] !== void 0 && state.tombstones[ord] !== 1) {
785
+ liveOrds.push(ord);
786
+ }
787
+ }
788
+ state.nodesByOrd = [];
789
+ state.tombstones.fill(0);
790
+ state.tombstoneCount = 0;
791
+ state.nodeCount = 0;
792
+ state.entryPointOrd = -1;
793
+ state.topLayer = -1;
794
+ for (const ord of liveOrds) {
795
+ const docId = state.store.docIdForOrdinal(ord);
796
+ if (docId === void 0) continue;
797
+ if (state.store.entryForOrdinal(ord) === void 0) continue;
798
+ insertNode(state, docId);
799
+ }
800
+ }
801
+
802
+ // src/vector/hnsw/persistence.ts
803
+ function serializeGraph(state) {
804
+ const nodeArray = [];
805
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
806
+ const node = state.nodesByOrd[ord];
807
+ if (!node) continue;
808
+ if (state.tombstones[ord] === 1) continue;
809
+ const docId = state.store.docIdForOrdinal(ord);
810
+ if (docId === void 0) continue;
811
+ const layerConns = [];
812
+ for (let layer = 0; layer < node.connections.length; layer++) {
813
+ const liveNeighbors = [];
814
+ for (const neighborOrd of node.connections[layer]) {
815
+ if (state.tombstones[neighborOrd] === 1) continue;
816
+ const neighborDoc = state.store.docIdForOrdinal(neighborOrd);
817
+ if (neighborDoc === void 0) continue;
818
+ liveNeighbors.push(neighborDoc);
819
+ }
820
+ if (liveNeighbors.length > 0) {
821
+ layerConns.push([layer, liveNeighbors]);
822
+ }
823
+ }
824
+ nodeArray.push([docId, node.maxLayer, layerConns]);
825
+ }
826
+ const entryPoint = state.entryPointOrd === -1 ? null : state.store.docIdForOrdinal(state.entryPointOrd) ?? null;
827
+ return {
828
+ entryPoint,
829
+ maxLayer: state.topLayer,
830
+ m: state.M,
831
+ efConstruction: state.efCons,
832
+ metric: state.buildMetric,
833
+ nodes: nodeArray
834
+ };
835
+ }
836
+ function deserializeGraph(state, data) {
837
+ state.nodesByOrd = [];
838
+ state.tombstones.fill(0);
839
+ state.tombstoneCount = 0;
840
+ state.nodeCount = 0;
841
+ state.entryPointOrd = -1;
842
+ state.topLayer = -1;
843
+ let maxOrd = -1;
844
+ for (const [docId, maxLayer, layerConns] of data.nodes) {
845
+ const ord = state.store.getOrdinal(docId);
846
+ if (ord === void 0) continue;
847
+ if (state.store.entryForOrdinal(ord) === void 0) continue;
848
+ const connections = Array.from({ length: maxLayer + 1 }, () => []);
849
+ for (const [layer, neighbors] of layerConns) {
850
+ if (layer < connections.length) {
851
+ const arr = [];
852
+ for (const neighborDoc of neighbors) {
853
+ const neighborOrd = state.store.getOrdinal(neighborDoc);
854
+ if (neighborOrd === void 0) continue;
855
+ addConnection(arr, neighborOrd);
856
+ if (neighborOrd > maxOrd) maxOrd = neighborOrd;
857
+ }
858
+ connections[layer] = arr;
859
+ }
860
+ }
861
+ const node = { maxLayer, connections };
862
+ state.nodesByOrd[ord] = node;
863
+ state.nodeCount++;
864
+ if (ord > maxOrd) maxOrd = ord;
865
+ }
866
+ if (maxOrd >= 0) {
867
+ ensureCapacity(state, maxOrd + 1);
868
+ }
869
+ if (data.entryPoint != null && data.entryPoint !== "") {
870
+ const epOrd = state.store.getOrdinal(data.entryPoint);
871
+ if (epOrd !== void 0 && state.nodesByOrd[epOrd] !== void 0) {
872
+ state.entryPointOrd = epOrd;
873
+ state.topLayer = data.maxLayer;
874
+ }
875
+ }
876
+ if (state.entryPointOrd === -1 && state.nodeCount > 0) {
877
+ let bestOrd = -1;
878
+ let bestLayer = -1;
879
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
880
+ const n = state.nodesByOrd[ord];
881
+ if (!n) continue;
882
+ if (n.maxLayer > bestLayer) {
883
+ bestLayer = n.maxLayer;
884
+ bestOrd = ord;
885
+ }
886
+ }
887
+ state.entryPointOrd = bestOrd;
888
+ state.topLayer = bestLayer;
889
+ }
890
+ }
891
+
892
+ // src/vector/hnsw/search.ts
893
+ function search(state, query, k, searchMetric, minSimilarity, filterDocIds, efSearch) {
894
+ if (query.length !== state.dimension) {
895
+ throw new Error(`Query dimension mismatch: expected ${state.dimension}, got ${query.length}`);
896
+ }
897
+ const liveSize = state.nodeCount - state.tombstoneCount;
898
+ if (state.entryPointOrd === -1 || liveSize === 0) {
899
+ return [];
900
+ }
901
+ const useQuantized = state.quantizer?.isCalibrated() === true && state.quantizer.size > 0;
902
+ const defaultEf = 50;
903
+ let ef = Math.max(efSearch ?? defaultEf, k);
904
+ let filterOrds;
905
+ if (filterDocIds) {
906
+ filterOrds = /* @__PURE__ */ new Set();
907
+ for (const docId of filterDocIds) {
908
+ const o = state.store.getOrdinal(docId);
909
+ if (o !== void 0) filterOrds.add(o);
910
+ }
911
+ if (filterOrds.size < liveSize) {
912
+ const selectivity = filterOrds.size / liveSize;
913
+ ef = Math.max(ef, Math.ceil(k / Math.max(selectivity, 0.01)));
914
+ ef = Math.min(ef, liveSize);
915
+ }
916
+ }
917
+ const qMag = magnitude(query);
918
+ let quantizedDistFn;
919
+ if (useQuantized && state.quantizer) {
920
+ const q = state.quantizer;
921
+ const metric = searchMetric;
922
+ const arenaQuery = q.prepareQueryArena(query);
923
+ if (arenaQuery) {
924
+ quantizedDistFn = (ord) => q.distanceFromArena(arenaQuery, ord, metric);
925
+ } else {
926
+ const prepared = q.prepareQuery(query);
927
+ if (prepared) {
928
+ quantizedDistFn = (ord) => q.distanceFromPreparedByOrdinal(prepared, ord, metric);
929
+ }
930
+ }
931
+ }
932
+ let currentEPs = [state.entryPointOrd];
933
+ for (let layer = state.topLayer; layer >= 1; layer--) {
934
+ const heap = searchLayer(state, query, qMag, currentEPs, 1, layer, searchMetric, true, quantizedDistFn);
935
+ const nearest = nearestFromHeap(heap);
936
+ if (nearest) {
937
+ currentEPs = [nearest.ord];
938
+ }
939
+ }
940
+ const candidateHeap = searchLayer(state, query, qMag, currentEPs, ef, 0, searchMetric, true, quantizedDistFn);
941
+ const candidateArray = candidateHeap.toSortedArray().reverse();
942
+ if (useQuantized) {
943
+ return rerankWithFullPrecision(state, candidateArray, query, qMag, k, searchMetric, minSimilarity, filterOrds);
944
+ }
945
+ const results = [];
946
+ for (const cand of candidateArray) {
947
+ if (filterOrds && !filterOrds.has(cand.ord)) continue;
948
+ const score = toScore(cand.distance, searchMetric);
949
+ if (score < minSimilarity) continue;
950
+ const docId = state.store.docIdForOrdinal(cand.ord);
951
+ if (docId === void 0) continue;
952
+ results.push({
953
+ docId,
954
+ score,
955
+ termFrequencies: {},
956
+ fieldLengths: {},
957
+ idf: {}
958
+ });
959
+ }
960
+ results.sort((a, b) => b.score - a.score || a.docId.localeCompare(b.docId));
961
+ return results.slice(0, k);
962
+ }
963
+ function rerankWithFullPrecision(state, candidates, query, qMag, k, metric, minSimilarity, filterOrds) {
964
+ const reranked = [];
965
+ const rerankLimit = Math.max(k * SQ8_OVERSELECTION_FACTOR, 10);
966
+ for (const cand of candidates) {
967
+ if (filterOrds && !filterOrds.has(cand.ord)) continue;
968
+ const entry = entryForOrd(state, cand.ord);
969
+ if (!entry) continue;
970
+ const fullDistance = toDistance(query, entry.vector, qMag, entry.magnitude, metric);
971
+ const score = toScore(fullDistance, metric);
972
+ if (score < minSimilarity) continue;
973
+ const docId = state.store.docIdForOrdinal(cand.ord);
974
+ if (docId === void 0) continue;
975
+ reranked.push({
976
+ docId,
977
+ score,
978
+ termFrequencies: {},
979
+ fieldLengths: {},
980
+ idf: {}
981
+ });
982
+ if (reranked.length >= rerankLimit) break;
983
+ }
984
+ reranked.sort((a, b) => b.score - a.score || a.docId.localeCompare(b.docId));
985
+ return reranked.slice(0, k);
986
+ }
987
+
988
+ // src/vector/hnsw/index.ts
989
+ function createHNSWIndex(dimension, store, config, quantizer) {
990
+ const M = config?.m ?? 16;
991
+ const Mmax0 = M * 2;
992
+ const efCons = config?.efConstruction ?? 200;
993
+ const buildMetric = config?.metric ?? "cosine";
994
+ const mL = 1 / Math.log(M);
995
+ const state = {
996
+ dimension,
997
+ store,
998
+ quantizer,
999
+ M,
1000
+ Mmax0,
1001
+ efCons,
1002
+ buildMetric,
1003
+ mL,
1004
+ nodesByOrd: [],
1005
+ tombstones: new Uint8Array(0),
1006
+ tombstoneCount: 0,
1007
+ nodeCount: 0,
1008
+ capacity: 0,
1009
+ visited: new Uint32Array(0),
1010
+ visitStamp: 0,
1011
+ entryPointOrd: -1,
1012
+ topLayer: -1
1013
+ };
1014
+ function clear() {
1015
+ state.nodesByOrd = [];
1016
+ state.tombstones = new Uint8Array(0);
1017
+ state.tombstoneCount = 0;
1018
+ state.nodeCount = 0;
1019
+ state.capacity = 0;
1020
+ state.visited = new Uint32Array(0);
1021
+ state.visitStamp = 0;
1022
+ state.entryPointOrd = -1;
1023
+ state.topLayer = -1;
1024
+ }
1025
+ function* entriesIterator() {
1026
+ for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
1027
+ if (state.nodesByOrd[ord] === void 0) continue;
1028
+ if (state.tombstones[ord] === 1) continue;
1029
+ const docId = state.store.docIdForOrdinal(ord);
1030
+ if (docId === void 0) continue;
1031
+ const entry = state.store.entryForOrdinal(ord);
1032
+ if (!entry) continue;
1033
+ yield [docId, { docId, vector: entry.vector, magnitude: entry.magnitude }];
1034
+ }
1035
+ }
1036
+ function compactionNeeded() {
1037
+ if (state.nodeCount === 0) return false;
1038
+ return state.tombstoneCount / state.nodeCount > COMPACTION_TOMBSTONE_RATIO || state.tombstoneCount > COMPACTION_ABSOLUTE_THRESHOLD;
1039
+ }
1040
+ return {
1041
+ get dimension() {
1042
+ return state.dimension;
1043
+ },
1044
+ get size() {
1045
+ return state.nodeCount - state.tombstoneCount;
1046
+ },
1047
+ get tombstoneCount() {
1048
+ return state.tombstoneCount;
1049
+ },
1050
+ get entryPointId() {
1051
+ return state.entryPointOrd === -1 ? null : state.store.docIdForOrdinal(state.entryPointOrd) ?? null;
1052
+ },
1053
+ get topLayer() {
1054
+ return state.topLayer;
1055
+ },
1056
+ get m() {
1057
+ return state.M;
1058
+ },
1059
+ get efConstruction() {
1060
+ return state.efCons;
1061
+ },
1062
+ get metric() {
1063
+ return state.buildMetric;
1064
+ },
1065
+ insertNode: (docId) => insertNode(state, docId),
1066
+ markTombstone: (docId) => markTombstone(state, docId),
1067
+ has: (docId) => {
1068
+ const ord = state.store.getOrdinal(docId);
1069
+ return ord !== void 0 && state.nodesByOrd[ord] !== void 0 && state.tombstones[ord] !== 1;
1070
+ },
1071
+ isTombstoned: (docId) => {
1072
+ const ord = state.store.getOrdinal(docId);
1073
+ return ord !== void 0 && state.tombstones[ord] === 1;
1074
+ },
1075
+ search: (query, k, searchMetric, minSimilarity, filterDocIds, efSearch) => search(state, query, k, searchMetric, minSimilarity, filterDocIds, efSearch),
1076
+ clear,
1077
+ entries: entriesIterator,
1078
+ compactionNeeded,
1079
+ compactTombstones: () => compactTombstones(state),
1080
+ rebuild: () => rebuild(state),
1081
+ serialize: () => serializeGraph(state),
1082
+ deserialize: (data) => deserializeGraph(state, data)
1083
+ };
1084
+ }
1085
+
1086
+ export { cosineSimilarityWithMagnitudes, createArenaSimd, createBoundedMaxHeap, createHNSWIndex, createVectorStore, dotProduct, euclideanDistance, isSimdAvailable, magnitude };