@delali/narsil 0.2.2-next.29 → 0.2.3-next.32

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 (66) hide show
  1. package/README.md +12 -9
  2. package/dist/adapters/filesystem.d.ts +1 -1
  3. package/dist/adapters/filesystem.mjs +1 -1
  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-CVqI5NBt.d.ts → adapters-79OIa4xm.d.ts} +1 -1
  8. package/dist/brute-force-DbHYCTc3.d.ts +8 -0
  9. package/dist/chunk-BFIH7OV5.mjs +185 -0
  10. package/dist/chunk-C6VVLMRX.mjs +63 -0
  11. package/dist/{chunk-UNJZ7ZDB.mjs → chunk-EMI63EXP.mjs} +33 -2
  12. package/dist/chunk-MFFAJ2NR.mjs +135 -0
  13. package/dist/chunk-OBXS7OZM.mjs +376 -0
  14. package/dist/{chunk-ASI6AOW2.mjs → chunk-RO4EFWE6.mjs} +7833 -3524
  15. package/dist/{chunk-WLR5BFL3.mjs → chunk-TEAJO7CU.mjs} +3067 -2087
  16. package/dist/{chunk-S7RJJMHI.mjs → chunk-WDL632CN.mjs} +699 -222
  17. package/dist/{chunk-BKAI2WXI.mjs → chunk-WTONLDU3.mjs} +5 -0
  18. package/dist/client-DQHB_swQ.d.ts +793 -0
  19. package/dist/client.d.ts +6 -0
  20. package/dist/client.mjs +843 -0
  21. package/dist/{config-DXypXAfN.d.ts → config-ChZ7lfqc.d.ts} +3 -3
  22. package/dist/direct-executor-YARCDMJF.mjs +8 -0
  23. package/dist/distribution/coordinator/etcd.d.ts +3 -3
  24. package/dist/distribution/coordinator/etcd.mjs +471 -2
  25. package/dist/distribution/coordinator/in-memory.d.ts +3 -3
  26. package/dist/distribution/coordinator/in-memory.mjs +286 -1
  27. package/dist/distribution/transport/grpc.d.ts +45 -0
  28. package/dist/distribution/transport/grpc.mjs +586 -0
  29. package/dist/distribution/transport/in-memory.d.ts +118 -0
  30. package/dist/distribution/transport/in-memory.mjs +214 -0
  31. package/dist/distribution/transport/tcp.d.ts +27 -0
  32. package/dist/distribution/transport/tcp.mjs +595 -0
  33. package/dist/distribution.d.ts +241 -192
  34. package/dist/distribution.mjs +6620 -4795
  35. package/dist/embeddings/openai.d.ts +1 -1
  36. package/dist/embeddings/openai.mjs +1 -1
  37. package/dist/{engine-CO9Wnfie.d.ts → engine-DScGlT2Z.d.ts} +30 -3
  38. package/dist/index.browser.mjs +10035 -3914
  39. package/dist/index.d.ts +8 -8
  40. package/dist/index.mjs +8 -5
  41. package/dist/invalidation/broadcast-channel.d.ts +1 -1
  42. package/dist/invalidation/filesystem.d.ts +1 -1
  43. package/dist/invalidation/filesystem.mjs +3 -1
  44. package/dist/invalidation/noop.d.ts +1 -1
  45. package/dist/options-BORJg5XB.d.ts +234 -0
  46. package/dist/persistence/durability/checkpoint-worker.d.ts +4 -2
  47. package/dist/persistence/durability/checkpoint-worker.mjs +8 -5
  48. package/dist/react.d.ts +340 -0
  49. package/dist/react.mjs +687 -0
  50. package/dist/{schema-CE6S_aAr.d.ts → schema-emTQfEiX.d.ts} +21 -7
  51. package/dist/{search-DLBtiPdQ.d.ts → search-CQ-9bKxo.d.ts} +226 -14
  52. package/dist/server.d.ts +42 -356
  53. package/dist/server.mjs +493 -106
  54. package/dist/tasks-CnMLpfk-.d.ts +274 -0
  55. package/dist/types-BQDlIvfS.d.ts +34 -0
  56. package/dist/{types-BV3nAKTL.d.ts → types-Bfhhnh3G.d.ts} +23 -1
  57. package/dist/types-D5OwhtsQ.d.ts +97 -0
  58. package/dist/vector/hnsw-build-worker.d.ts +2 -7
  59. package/dist/vector/hnsw-build-worker.mjs +2 -1
  60. package/dist/vector/search-worker.d.ts +373 -0
  61. package/dist/vector/search-worker.mjs +279 -0
  62. package/dist/workers/entry.mjs +43 -25
  63. package/package.json +42 -2
  64. package/dist/chunk-FKK76OR7.mjs +0 -456
  65. package/dist/chunk-RQLXC2YS.mjs +0 -269
  66. package/dist/direct-executor-3W4QRA4E.mjs +0 -5
@@ -1,5 +1,8 @@
1
+ import { compareCodePoints } from './chunk-BFIH7OV5.mjs';
2
+
1
3
  // src/vector/simd-wasm-binary.ts
2
4
  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=";
5
+ var SIMD_DISTANCE_SHARED_MEMORY_WASM_BASE64 = "AGFzbQEAAAABFQNgA39/fwF9YAJ/fwF9YAN/f38BfwISAQNlbnYGbWVtb3J5AgMBgIAEAwcGAAEAAAICB2IGC2RvdF9wcm9kdWN0AAAJbWFnbml0dWRlAAEac3F1YXJlZF9ldWNsaWRlYW5fZGlzdGFuY2UAAhJldWNsaWRlYW5fZGlzdGFuY2UAAwZkb3RfdTgABAlzcWRpc3RfdTgABQqvBwauAQQBfwF7AX0BfyACQXxxIQb9DAAAAAAAAAAAAAAAAAAAAAAhBEEAIQMCQANAIAMgBk8NASAEIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/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
6
 
4
7
  // src/vector/simd.ts
5
8
  var wasmExports = null;
@@ -97,7 +100,7 @@ function createArenaSimd() {
97
100
  if (module === null) return null;
98
101
  const instance = new WebAssembly.Instance(module);
99
102
  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)) {
103
+ if (typeof exports$1.dot_u8 !== "function" || typeof exports$1.sqdist_u8 !== "function" || typeof exports$1.dot_product !== "function" || typeof exports$1.magnitude !== "function" || typeof exports$1.squared_euclidean_distance !== "function" || !(exports$1.memory instanceof WebAssembly.Memory)) {
101
104
  return null;
102
105
  }
103
106
  return exports$1;
@@ -105,6 +108,88 @@ function createArenaSimd() {
105
108
  return null;
106
109
  }
107
110
  }
111
+ var sharedMemoryModule;
112
+ function getSharedMemoryModule() {
113
+ if (sharedMemoryModule !== void 0) return sharedMemoryModule;
114
+ try {
115
+ const bytes = decodeBase64(SIMD_DISTANCE_SHARED_MEMORY_WASM_BASE64);
116
+ const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
117
+ if (typeof WebAssembly === "undefined" || typeof WebAssembly.validate !== "function" || !WebAssembly.validate(wasmBuffer)) {
118
+ sharedMemoryModule = null;
119
+ return null;
120
+ }
121
+ sharedMemoryModule = new WebAssembly.Module(wasmBuffer);
122
+ } catch {
123
+ sharedMemoryModule = null;
124
+ }
125
+ return sharedMemoryModule;
126
+ }
127
+ function createSharedArenaSimd(memory) {
128
+ try {
129
+ const module = getSharedMemoryModule();
130
+ if (module === null) return null;
131
+ const instance = new WebAssembly.Instance(module, { env: { memory } });
132
+ const exports$1 = instance.exports;
133
+ if (typeof exports$1.dot_u8 !== "function" || typeof exports$1.sqdist_u8 !== "function" || typeof exports$1.dot_product !== "function" || typeof exports$1.magnitude !== "function" || typeof exports$1.squared_euclidean_distance !== "function") {
134
+ return null;
135
+ }
136
+ return {
137
+ memory,
138
+ dot_product: exports$1.dot_product,
139
+ magnitude: exports$1.magnitude,
140
+ squared_euclidean_distance: exports$1.squared_euclidean_distance,
141
+ dot_u8: exports$1.dot_u8,
142
+ sqdist_u8: exports$1.sqdist_u8
143
+ };
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+ function arenaFloat32Distance(simd, byteA, byteB, dimension, metric, magnitudeA, magnitudeB) {
149
+ if (metric === "euclidean") {
150
+ return Math.sqrt(simd.squared_euclidean_distance(byteA, byteB, dimension));
151
+ }
152
+ const dot = simd.dot_product(byteA, byteB, dimension);
153
+ if (metric === "dotProduct") return -dot;
154
+ if (magnitudeA === 0 || magnitudeB === 0) return 1;
155
+ return 1 - dot / (magnitudeA * magnitudeB);
156
+ }
157
+
158
+ // src/vector/ordinal-filter.ts
159
+ function createOrdinalFilter(slots) {
160
+ return { bits: new Uint8Array(slots + 7 >>> 3), count: 0 };
161
+ }
162
+ function addToOrdinalFilter(filter, ordinal) {
163
+ const index = ordinal >>> 3;
164
+ if (index >= filter.bits.length) return;
165
+ const bit = 1 << (ordinal & 7);
166
+ if ((filter.bits[index] & bit) !== 0) return;
167
+ filter.bits[index] |= bit;
168
+ filter.count += 1;
169
+ }
170
+ function ordinalFilterHas(filter, ordinal) {
171
+ const index = ordinal >>> 3;
172
+ if (index >= filter.bits.length) return false;
173
+ return (filter.bits[index] & 1 << (ordinal & 7)) !== 0;
174
+ }
175
+ function removeFromOrdinalFilter(filter, ordinal) {
176
+ const index = ordinal >>> 3;
177
+ if (index >= filter.bits.length) return;
178
+ const bit = 1 << (ordinal & 7);
179
+ if ((filter.bits[index] & bit) === 0) return;
180
+ filter.bits[index] &= ~bit;
181
+ filter.count -= 1;
182
+ }
183
+ function* ordinalFilterValues(filter) {
184
+ const bits = filter.bits;
185
+ for (let index = 0; index < bits.length; index++) {
186
+ const byte = bits[index];
187
+ if (byte === 0) continue;
188
+ for (let offset = 0; offset < 8; offset++) {
189
+ if ((byte & 1 << offset) !== 0) yield index << 3 | offset;
190
+ }
191
+ }
192
+ }
108
193
 
109
194
  // src/vector/similarity.ts
110
195
  function magnitude(v) {
@@ -140,22 +225,30 @@ function euclideanDistance(a, b) {
140
225
  return Math.sqrt(sum);
141
226
  }
142
227
 
143
- // src/vector/vector-store.ts
228
+ // src/vector/vector-store/index.ts
144
229
  var INITIAL_CAPACITY = 16;
145
230
  var PAGE_BYTES = 65536;
231
+ var UNKNOWN_PARTITION = -1;
232
+ var NO_PARTITION = -2;
146
233
  function createVectorStore() {
147
234
  const docToOrd = /* @__PURE__ */ new Map();
148
235
  const recycledOrds = /* @__PURE__ */ new Map();
149
236
  const ordToDoc = [];
237
+ let partitionOf = new Int32Array(0);
238
+ let unknownPartitions = 0;
150
239
  let simd = createArenaSimd();
151
240
  let dimension = 0;
152
241
  let capacity = 0;
153
242
  let arena = new Float32Array(0);
154
243
  let mags = new Float64Array(0);
155
244
  let liveCount = 0;
245
+ let scratchByteLength = 0;
246
+ let scratchFloatLength = 0;
156
247
  function initStorage(dim) {
157
248
  dimension = dim;
158
249
  if (simd) {
250
+ scratchByteLength = Math.max(16, Math.ceil(dim * 4 / 16) * 16);
251
+ scratchFloatLength = scratchByteLength / 4;
159
252
  arena = new Float32Array(simd.memory.buffer);
160
253
  }
161
254
  }
@@ -167,16 +260,18 @@ function createVectorStore() {
167
260
  nextMags.set(mags);
168
261
  mags = nextMags;
169
262
  if (simd) {
170
- const requiredBytes = newCap * dimension * 4;
263
+ const requiredBytes = scratchByteLength + newCap * dimension * 4;
171
264
  const have = simd.memory.buffer.byteLength;
172
265
  if (requiredBytes > have) {
173
266
  try {
174
267
  simd.memory.grow(Math.ceil((requiredBytes - have) / PAGE_BYTES));
175
268
  } catch {
176
269
  const migrated = new Float32Array(newCap * dimension);
177
- migrated.set(arena.subarray(0, capacity * dimension));
270
+ migrated.set(arena.subarray(scratchFloatLength, scratchFloatLength + capacity * dimension));
178
271
  arena = migrated;
179
272
  simd = null;
273
+ scratchByteLength = 0;
274
+ scratchFloatLength = 0;
180
275
  capacity = newCap;
181
276
  return;
182
277
  }
@@ -190,22 +285,48 @@ function createVectorStore() {
190
285
  capacity = newCap;
191
286
  }
192
287
  function writeVector(ord, vector) {
193
- arena.set(vector, ord * dimension);
288
+ arena.set(vector, scratchFloatLength + ord * dimension);
194
289
  mags[ord] = magnitude(vector);
195
290
  }
196
291
  function entryAt(ord) {
197
- const base = ord * dimension;
292
+ const base = scratchFloatLength + ord * dimension;
198
293
  return { vector: arena.subarray(base, base + dimension), magnitude: mags[ord] };
199
294
  }
295
+ function ensurePartitionSlots(ord) {
296
+ if (ord < partitionOf.length) return;
297
+ let length = partitionOf.length === 0 ? INITIAL_CAPACITY : partitionOf.length;
298
+ while (length <= ord) length *= 2;
299
+ const grown = new Int32Array(length).fill(UNKNOWN_PARTITION);
300
+ grown.set(partitionOf);
301
+ partitionOf = grown;
302
+ }
303
+ function recordPartition(ord, partitionId, wasLive) {
304
+ ensurePartitionSlots(ord);
305
+ const previous = partitionOf[ord];
306
+ const given = partitionId !== void 0 && partitionId >= 0 ? partitionId : void 0;
307
+ const next = given ?? (wasLive ? previous : UNKNOWN_PARTITION);
308
+ partitionOf[ord] = next;
309
+ if (!wasLive && next === UNKNOWN_PARTITION) {
310
+ unknownPartitions += 1;
311
+ return;
312
+ }
313
+ if (wasLive && previous === UNKNOWN_PARTITION && next !== UNKNOWN_PARTITION) {
314
+ unknownPartitions -= 1;
315
+ }
316
+ }
200
317
  return {
201
318
  get size() {
202
319
  return liveCount;
203
320
  },
204
- insert(docId, vector) {
321
+ get slots() {
322
+ return ordToDoc.length;
323
+ },
324
+ insert(docId, vector, partitionId) {
205
325
  if (dimension === 0) initStorage(vector.length);
206
326
  const existing = docToOrd.get(docId);
207
327
  if (existing !== void 0) {
208
328
  writeVector(existing, vector);
329
+ recordPartition(existing, partitionId, true);
209
330
  return;
210
331
  }
211
332
  const recycled = recycledOrds.get(docId);
@@ -215,6 +336,7 @@ function createVectorStore() {
215
336
  ordToDoc[recycled] = docId;
216
337
  ensureCapacity2(recycled + 1);
217
338
  writeVector(recycled, vector);
339
+ recordPartition(recycled, partitionId, false);
218
340
  liveCount++;
219
341
  return;
220
342
  }
@@ -223,14 +345,59 @@ function createVectorStore() {
223
345
  ordToDoc.push(docId);
224
346
  ensureCapacity2(ord + 1);
225
347
  writeVector(ord, vector);
348
+ recordPartition(ord, partitionId, false);
226
349
  liveCount++;
227
350
  },
351
+ setPartition(docId, partitionId) {
352
+ const ord = docToOrd.get(docId);
353
+ if (ord === void 0) return;
354
+ recordPartition(ord, partitionId, true);
355
+ },
356
+ forgetPartition(docId) {
357
+ const ord = docToOrd.get(docId);
358
+ if (ord === void 0 || ord >= partitionOf.length) return;
359
+ if (partitionOf[ord] === UNKNOWN_PARTITION) {
360
+ unknownPartitions -= 1;
361
+ }
362
+ partitionOf[ord] = NO_PARTITION;
363
+ },
364
+ partitionOfOrdinal(ordinal) {
365
+ if (ordinal < 0 || ordinal >= partitionOf.length) return void 0;
366
+ const partitionId = partitionOf[ordinal];
367
+ return partitionId < 0 ? void 0 : partitionId;
368
+ },
369
+ get partitionsKnown() {
370
+ return unknownPartitions === 0;
371
+ },
372
+ partitionFilter(partitionIds) {
373
+ const filter = createOrdinalFilter(ordToDoc.length);
374
+ let highest = -1;
375
+ for (const partitionId of partitionIds) {
376
+ if (partitionId > highest) highest = partitionId;
377
+ }
378
+ if (highest < 0) return filter;
379
+ const wanted = new Uint8Array(highest + 1);
380
+ for (const partitionId of partitionIds) {
381
+ if (partitionId >= 0) wanted[partitionId] = 1;
382
+ }
383
+ const upperBound = Math.min(ordToDoc.length, partitionOf.length);
384
+ for (let ord = 0; ord < upperBound; ord++) {
385
+ if (ordToDoc[ord] === void 0) continue;
386
+ const partitionId = partitionOf[ord];
387
+ if (partitionId < 0 || partitionId > highest || wanted[partitionId] === 0) continue;
388
+ addToOrdinalFilter(filter, ord);
389
+ }
390
+ return filter;
391
+ },
228
392
  remove(docId) {
229
393
  const ord = docToOrd.get(docId);
230
394
  if (ord === void 0) return;
231
395
  docToOrd.delete(docId);
232
396
  ordToDoc[ord] = void 0;
233
397
  recycledOrds.set(docId, ord);
398
+ if (ord < partitionOf.length && partitionOf[ord] === UNKNOWN_PARTITION) {
399
+ unknownPartitions -= 1;
400
+ }
234
401
  liveCount--;
235
402
  },
236
403
  get(docId) {
@@ -251,6 +418,8 @@ function createVectorStore() {
251
418
  docToOrd.clear();
252
419
  recycledOrds.clear();
253
420
  ordToDoc.length = 0;
421
+ partitionOf = new Int32Array(0);
422
+ unknownPartitions = 0;
254
423
  mags = new Float64Array(0);
255
424
  if (!simd) {
256
425
  arena = new Float32Array(0);
@@ -258,6 +427,8 @@ function createVectorStore() {
258
427
  dimension = 0;
259
428
  capacity = 0;
260
429
  liveCount = 0;
430
+ scratchByteLength = 0;
431
+ scratchFloatLength = 0;
261
432
  },
262
433
  getOrdinal(docId) {
263
434
  return docToOrd.get(docId);
@@ -272,20 +443,12 @@ function createVectorStore() {
272
443
  distanceByOrdinal(ordA, ordB, metric) {
273
444
  if (ordToDoc[ordA] === void 0 || ordToDoc[ordB] === void 0) return Number.POSITIVE_INFINITY;
274
445
  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);
446
+ const byteA = scratchByteLength + ordA * dimension * 4;
447
+ const byteB = scratchByteLength + ordB * dimension * 4;
448
+ return arenaFloat32Distance(simd, byteA, byteB, dimension, metric, mags[ordA], mags[ordB]);
286
449
  }
287
- const baseA = ordA * dimension;
288
- const baseB = ordB * dimension;
450
+ const baseA = scratchFloatLength + ordA * dimension;
451
+ const baseB = scratchFloatLength + ordB * dimension;
289
452
  const a = arena.subarray(baseA, baseA + dimension);
290
453
  const b = arena.subarray(baseB, baseB + dimension);
291
454
  switch (metric) {
@@ -297,6 +460,68 @@ function createVectorStore() {
297
460
  return euclideanDistance(a, b);
298
461
  }
299
462
  },
463
+ prepareQueryArena(query) {
464
+ if (!simd || dimension === 0 || query.length !== dimension) return null;
465
+ arena.set(query, 0);
466
+ return { magnitude: simd.magnitude(0, dimension) };
467
+ },
468
+ distanceFromArena(prepared, ordinal, metric) {
469
+ if (!simd || ordinal < 0 || ordinal >= ordToDoc.length || ordToDoc[ordinal] === void 0) {
470
+ return Number.POSITIVE_INFINITY;
471
+ }
472
+ const byteOffset = scratchByteLength + ordinal * dimension * 4;
473
+ return arenaFloat32Distance(simd, 0, byteOffset, dimension, metric, prepared.magnitude, mags[ordinal]);
474
+ },
475
+ exportSnapshot() {
476
+ const slots = ordToDoc.length;
477
+ const vectors = new Float32Array(slots * dimension);
478
+ if (slots > 0 && dimension > 0) {
479
+ vectors.set(arena.subarray(scratchFloatLength, scratchFloatLength + slots * dimension));
480
+ }
481
+ const magnitudes = new Float64Array(slots);
482
+ magnitudes.set(mags.subarray(0, Math.min(slots, mags.length)));
483
+ const docIds = new Array(slots);
484
+ for (let ord = 0; ord < slots; ord++) {
485
+ docIds[ord] = ordToDoc[ord] ?? null;
486
+ }
487
+ return { dimension, slots, vectors, magnitudes, docIds };
488
+ },
489
+ copySnapshotInto(vectors, magnitudes) {
490
+ const slots = ordToDoc.length;
491
+ if (slots > 0 && dimension > 0) {
492
+ vectors.set(arena.subarray(scratchFloatLength, scratchFloatLength + slots * dimension));
493
+ }
494
+ magnitudes.set(mags.subarray(0, Math.min(slots, mags.length)));
495
+ },
496
+ restoreSnapshot(snapshot) {
497
+ docToOrd.clear();
498
+ recycledOrds.clear();
499
+ ordToDoc.length = 0;
500
+ partitionOf = new Int32Array(0);
501
+ unknownPartitions = 0;
502
+ liveCount = 0;
503
+ capacity = 0;
504
+ dimension = 0;
505
+ scratchByteLength = 0;
506
+ scratchFloatLength = 0;
507
+ if (!simd) {
508
+ arena = new Float32Array(0);
509
+ }
510
+ if (snapshot.dimension === 0 || snapshot.slots === 0) return;
511
+ initStorage(snapshot.dimension);
512
+ ensureCapacity2(snapshot.slots);
513
+ arena.set(snapshot.vectors.subarray(0, snapshot.slots * snapshot.dimension), scratchFloatLength);
514
+ mags.set(snapshot.magnitudes.subarray(0, Math.min(snapshot.slots, mags.length)));
515
+ ensurePartitionSlots(snapshot.slots);
516
+ for (let ord = 0; ord < snapshot.slots; ord++) {
517
+ const docId = snapshot.docIds[ord];
518
+ ordToDoc.push(docId ?? void 0);
519
+ if (docId === null || docId === void 0) continue;
520
+ docToOrd.set(docId, ord);
521
+ unknownPartitions += 1;
522
+ liveCount++;
523
+ }
524
+ },
300
525
  estimateMemory(dimension2) {
301
526
  const count = liveCount;
302
527
  if (count === 0) return 0;
@@ -395,12 +620,201 @@ function createHeap(compare) {
395
620
  };
396
621
  }
397
622
 
398
- // src/vector/hnsw/shared.ts
623
+ // src/vector/hnsw/adjacency.ts
399
624
  var MAX_LAYER_CAP = 32;
625
+ var MAX_M = 512;
626
+ var ABSENT = -1;
627
+ var INITIAL_UPPER_SLOTS = 64;
628
+ function createAdjacency(m, mMax0) {
629
+ return {
630
+ m,
631
+ mMax0,
632
+ level0Stride: mMax0 + 2,
633
+ upperStride: m + 2,
634
+ capacity: 0,
635
+ slots: 0,
636
+ nodeLevels: new Int8Array(0),
637
+ level0: new Int32Array(0),
638
+ upperBase: new Int32Array(0),
639
+ upper: new Int32Array(0),
640
+ upperUsed: 0,
641
+ freeBlocks: Array.from({ length: MAX_LAYER_CAP + 1 }, () => [])
642
+ };
643
+ }
644
+ function ensureAdjacencyCapacity(adj, needed) {
645
+ if (needed <= adj.capacity) return;
646
+ let newCapacity = adj.capacity === 0 ? 16 : adj.capacity;
647
+ while (newCapacity < needed) newCapacity *= 2;
648
+ const nextLevels = new Int8Array(newCapacity).fill(ABSENT);
649
+ nextLevels.set(adj.nodeLevels);
650
+ adj.nodeLevels = nextLevels;
651
+ const nextLevel0 = new Int32Array(newCapacity * adj.level0Stride);
652
+ nextLevel0.set(adj.level0);
653
+ adj.level0 = nextLevel0;
654
+ const nextUpperBase = new Int32Array(newCapacity).fill(ABSENT);
655
+ nextUpperBase.set(adj.upperBase);
656
+ adj.upperBase = nextUpperBase;
657
+ adj.capacity = newCapacity;
658
+ }
659
+ function allocateUpperBlock(adj, levels) {
660
+ const reusable = adj.freeBlocks[levels].pop();
661
+ const size = levels * adj.upperStride;
662
+ if (reusable !== void 0) {
663
+ adj.upper.fill(0, reusable, reusable + size);
664
+ return reusable;
665
+ }
666
+ if (adj.upperUsed + size > adj.upper.length) {
667
+ const required = adj.upperUsed + size;
668
+ let nextLength = adj.upper.length === 0 ? INITIAL_UPPER_SLOTS * adj.upperStride : adj.upper.length;
669
+ while (nextLength < required) nextLength *= 2;
670
+ const nextUpper = new Int32Array(nextLength);
671
+ nextUpper.set(adj.upper);
672
+ adj.upper = nextUpper;
673
+ }
674
+ const offset = adj.upperUsed;
675
+ adj.upperUsed += size;
676
+ adj.upper.fill(0, offset, offset + size);
677
+ return offset;
678
+ }
679
+ function createNode(adj, ord, maxLayer) {
680
+ ensureAdjacencyCapacity(adj, ord + 1);
681
+ if (ord >= adj.slots) adj.slots = ord + 1;
682
+ const existingLevel = adj.nodeLevels[ord];
683
+ const existingBase = adj.upperBase[ord];
684
+ if (existingLevel >= 1 && existingBase !== ABSENT) {
685
+ adj.freeBlocks[existingLevel].push(existingBase);
686
+ }
687
+ adj.nodeLevels[ord] = maxLayer;
688
+ adj.level0[ord * adj.level0Stride] = 0;
689
+ adj.upperBase[ord] = maxLayer >= 1 ? allocateUpperBlock(adj, maxLayer) : ABSENT;
690
+ }
691
+ function deleteNode(adj, ord) {
692
+ if (ord < 0 || ord >= adj.capacity) return;
693
+ const level = adj.nodeLevels[ord];
694
+ const base = adj.upperBase[ord];
695
+ if (level >= 1 && base !== ABSENT) {
696
+ adj.freeBlocks[level].push(base);
697
+ }
698
+ adj.nodeLevels[ord] = ABSENT;
699
+ adj.upperBase[ord] = ABSENT;
700
+ adj.level0[ord * adj.level0Stride] = 0;
701
+ }
702
+ function resetAdjacency(adj) {
703
+ adj.nodeLevels = new Int8Array(0);
704
+ adj.level0 = new Int32Array(0);
705
+ adj.upperBase = new Int32Array(0);
706
+ adj.upper = new Int32Array(0);
707
+ adj.upperUsed = 0;
708
+ adj.capacity = 0;
709
+ adj.slots = 0;
710
+ for (const list of adj.freeBlocks) list.length = 0;
711
+ }
712
+ function nodeLevel(adj, ord) {
713
+ if (ord < 0 || ord >= adj.capacity) return ABSENT;
714
+ return adj.nodeLevels[ord];
715
+ }
716
+ function hasNode(adj, ord) {
717
+ return nodeLevel(adj, ord) !== ABSENT;
718
+ }
719
+ function layerArray(adj, layer) {
720
+ return layer === 0 ? adj.level0 : adj.upper;
721
+ }
722
+ function layerBase(adj, ord, layer) {
723
+ const level = nodeLevel(adj, ord);
724
+ if (level === ABSENT || layer > level) return ABSENT;
725
+ if (layer === 0) return ord * adj.level0Stride;
726
+ const base = adj.upperBase[ord];
727
+ if (base === ABSENT) return ABSENT;
728
+ return base + (layer - 1) * adj.upperStride;
729
+ }
730
+ function neighborCount(adj, ord, layer) {
731
+ const base = layerBase(adj, ord, layer);
732
+ if (base === ABSENT) return 0;
733
+ return layerArray(adj, layer)[base];
734
+ }
735
+ function collectNeighbors(adj, ord, layer) {
736
+ const base = layerBase(adj, ord, layer);
737
+ if (base === ABSENT) return [];
738
+ const array = layerArray(adj, layer);
739
+ const count = array[base];
740
+ const collected = new Array(count);
741
+ for (let i = 0; i < count; i++) {
742
+ collected[i] = array[base + i + 1];
743
+ }
744
+ return collected;
745
+ }
746
+ function addNeighbor(adj, ord, layer, neighborOrd) {
747
+ const base = layerBase(adj, ord, layer);
748
+ if (base === ABSENT) return;
749
+ const array = layerArray(adj, layer);
750
+ const count = array[base];
751
+ for (let i = 1; i <= count; i++) {
752
+ if (array[base + i] === neighborOrd) return;
753
+ }
754
+ const stride = layer === 0 ? adj.level0Stride : adj.upperStride;
755
+ if (count + 1 > stride - 1) return;
756
+ array[base + count + 1] = neighborOrd;
757
+ array[base] = count + 1;
758
+ }
759
+ function removeNeighbor(adj, ord, layer, neighborOrd) {
760
+ const base = layerBase(adj, ord, layer);
761
+ if (base === ABSENT) return;
762
+ const array = layerArray(adj, layer);
763
+ const count = array[base];
764
+ for (let i = 1; i <= count; i++) {
765
+ if (array[base + i] !== neighborOrd) continue;
766
+ for (let j = i; j < count; j++) {
767
+ array[base + j] = array[base + j + 1];
768
+ }
769
+ array[base] = count - 1;
770
+ return;
771
+ }
772
+ }
773
+ function replaceNeighbors(adj, ord, layer, neighbors) {
774
+ const base = layerBase(adj, ord, layer);
775
+ if (base === ABSENT) return;
776
+ const array = layerArray(adj, layer);
777
+ const stride = layer === 0 ? adj.level0Stride : adj.upperStride;
778
+ const limit = Math.min(neighbors.length, stride - 1);
779
+ for (let i = 0; i < limit; i++) {
780
+ array[base + i + 1] = neighbors[i];
781
+ }
782
+ array[base] = limit;
783
+ }
784
+ function exportAdjacency(adj) {
785
+ return {
786
+ m: adj.m,
787
+ mMax0: adj.mMax0,
788
+ capacity: adj.capacity,
789
+ slots: adj.slots,
790
+ upperUsed: adj.upperUsed,
791
+ nodeLevels: adj.nodeLevels.slice(),
792
+ level0: adj.level0.slice(),
793
+ upperBase: adj.upperBase.slice(),
794
+ upper: adj.upper.slice(0, adj.upperUsed)
795
+ };
796
+ }
797
+ function importAdjacency(snapshot) {
798
+ const adj = createAdjacency(snapshot.m, snapshot.mMax0);
799
+ adj.capacity = snapshot.capacity;
800
+ adj.slots = snapshot.slots;
801
+ adj.upperUsed = snapshot.upperUsed;
802
+ adj.nodeLevels = snapshot.nodeLevels;
803
+ adj.level0 = snapshot.level0;
804
+ adj.upperBase = snapshot.upperBase;
805
+ adj.upper = snapshot.upper;
806
+ return adj;
807
+ }
808
+ function estimateAdjacencyBytes(adj) {
809
+ return adj.nodeLevels.byteLength + adj.level0.byteLength + adj.upperBase.byteLength + adj.upper.byteLength;
810
+ }
811
+
812
+ // src/vector/hnsw/shared.ts
400
813
  var COMPACTION_TOMBSTONE_RATIO = 0.1;
401
814
  var COMPACTION_ABSOLUTE_THRESHOLD = 1e3;
402
815
  var SQ8_OVERSELECTION_FACTOR = 2;
403
816
  function ensureCapacity(state, needed) {
817
+ ensureAdjacencyCapacity(state.adjacency, needed);
404
818
  if (needed <= state.capacity) return;
405
819
  let newCap = state.capacity === 0 ? 16 : state.capacity;
406
820
  while (newCap < needed) newCap *= 2;
@@ -419,6 +833,12 @@ function nextVisitStamp(state) {
419
833
  }
420
834
  return state.visitStamp;
421
835
  }
836
+ function nodeExists(state, ord) {
837
+ return hasNode(state.adjacency, ord);
838
+ }
839
+ function nodeMaxLayer(state, ord) {
840
+ return nodeLevel(state.adjacency, ord);
841
+ }
422
842
  function toDistance(a, b, magA, magB, metric) {
423
843
  switch (metric) {
424
844
  case "cosine":
@@ -470,6 +890,8 @@ function addConnection(connections, ord) {
470
890
  // src/vector/hnsw/graph-ops.ts
471
891
  function searchLayer(state, qVec, qMag, eps, ef, layer, metric, skipTombstones, distFn) {
472
892
  const getDistance = distFn ?? ((ord) => queryDistanceByOrd(state, qVec, qMag, ord, metric));
893
+ const adjacency = state.adjacency;
894
+ const neighbors = layerArray(adjacency, layer);
473
895
  const visited = state.visited;
474
896
  const stamp = nextVisitStamp(state);
475
897
  const candidates = createMinHeap(distanceAsc);
@@ -478,8 +900,7 @@ function searchLayer(state, qVec, qMag, eps, ef, layer, metric, skipTombstones,
478
900
  for (const epOrd of eps) {
479
901
  if (visited[epOrd] === stamp) continue;
480
902
  visited[epOrd] = stamp;
481
- const node = state.nodesByOrd[epOrd];
482
- if (!node) continue;
903
+ if (!nodeExists(state, epOrd)) continue;
483
904
  if (skipTombstones && state.tombstones[epOrd] === 1) continue;
484
905
  const dist = getDistance(epOrd);
485
906
  if (dist === Number.POSITIVE_INFINITY) continue;
@@ -496,14 +917,15 @@ function searchLayer(state, qVec, qMag, eps, ef, layer, metric, skipTombstones,
496
917
  const nearest = candidates.pop();
497
918
  if (!nearest) break;
498
919
  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]) {
920
+ const base = layerBase(adjacency, nearest.ord, layer);
921
+ if (base === -1) continue;
922
+ const count = neighbors[base];
923
+ for (let i = 1; i <= count; i++) {
924
+ const neighborOrd = neighbors[base + i];
502
925
  if (visited[neighborOrd] === stamp) continue;
503
926
  visited[neighborOrd] = stamp;
504
927
  if (skipTombstones && state.tombstones[neighborOrd] === 1) continue;
505
- const neighborNode = state.nodesByOrd[neighborOrd];
506
- if (!neighborNode) continue;
928
+ if (!nodeExists(state, neighborOrd)) continue;
507
929
  const dist = getDistance(neighborOrd);
508
930
  if (dist === Number.POSITIVE_INFINITY) continue;
509
931
  if (dist < furthestDist || results.size < ef) {
@@ -557,12 +979,16 @@ function selectNeighborsHeuristic(state, targetOrd, candidates, maxConnections,
557
979
  return selected;
558
980
  }
559
981
  function pruneConnections(state, ord, layer, metric) {
560
- const node = state.nodesByOrd[ord];
561
- if (!node) return;
982
+ const adjacency = state.adjacency;
983
+ const base = layerBase(adjacency, ord, layer);
984
+ if (base === -1) return;
562
985
  const mc = maxConns(state, layer);
563
- if (node.connections[layer].length <= mc) return;
986
+ const neighbors = layerArray(adjacency, layer);
987
+ const count = neighbors[base];
988
+ if (count <= mc) return;
564
989
  const conns = [];
565
- for (const connOrd of node.connections[layer]) {
990
+ for (let i = 1; i <= count; i++) {
991
+ const connOrd = neighbors[base + i];
566
992
  const dist = nodeDistanceByOrd(state, ord, connOrd, metric);
567
993
  if (dist === Number.POSITIVE_INFINITY) continue;
568
994
  conns.push({ ord: connOrd, distance: dist });
@@ -570,14 +996,131 @@ function pruneConnections(state, ord, layer, metric) {
570
996
  const pruned = selectNeighborsHeuristic(state, ord, conns, mc, layer, metric);
571
997
  const next = [];
572
998
  for (const p of pruned) addConnection(next, p.ord);
573
- node.connections[layer] = next;
999
+ replaceNeighbors(adjacency, ord, layer, next);
574
1000
  }
575
1001
 
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);
1002
+ // src/vector/hnsw/search.ts
1003
+ var ABSENT_DOCUMENT_RANK = 4294967295;
1004
+ function collectHits(state, query, k, searchMetric, minSimilarity, filter, efSearch, hasDocument) {
1005
+ if (query.length !== state.dimension) {
1006
+ throw new Error(`Query dimension mismatch: expected ${state.dimension}, got ${query.length}`);
1007
+ }
1008
+ const liveSize = state.nodeCount - state.tombstoneCount;
1009
+ if (state.entryPointOrd === -1 || liveSize === 0) {
1010
+ return [];
1011
+ }
1012
+ const useQuantized = state.quantizer?.isCalibrated() === true && state.quantizer.size > 0;
1013
+ const defaultEf = 50;
1014
+ let ef = Math.max(efSearch ?? defaultEf, k);
1015
+ if (filter && filter.count < liveSize) {
1016
+ const selectivity = filter.count / liveSize;
1017
+ ef = Math.max(ef, Math.ceil(k / Math.max(selectivity, 0.01)));
1018
+ ef = Math.min(ef, liveSize);
1019
+ }
1020
+ const store = state.store;
1021
+ const arenaQuery = store.prepareQueryArena(query);
1022
+ const qMag = arenaQuery ? arenaQuery.magnitude : magnitude(query);
1023
+ let quantizedDistFn;
1024
+ if (useQuantized && state.quantizer) {
1025
+ const q = state.quantizer;
1026
+ const metric = searchMetric;
1027
+ const quantizedArenaQuery = q.prepareQueryArena(query);
1028
+ if (quantizedArenaQuery) {
1029
+ quantizedDistFn = (ord) => q.distanceFromArena(quantizedArenaQuery, ord, metric);
1030
+ } else {
1031
+ const prepared = q.prepareQuery(query);
1032
+ if (prepared) {
1033
+ quantizedDistFn = (ord) => q.distanceFromPreparedByOrdinal(prepared, ord, metric);
1034
+ }
1035
+ }
1036
+ }
1037
+ let distFn = quantizedDistFn;
1038
+ if (!distFn && arenaQuery) {
1039
+ const metric = searchMetric;
1040
+ distFn = (ord) => store.distanceFromArena(arenaQuery, ord, metric);
1041
+ }
1042
+ let currentEPs = [state.entryPointOrd];
1043
+ for (let layer = state.topLayer; layer >= 1; layer--) {
1044
+ const heap = searchLayer(state, query, qMag, currentEPs, 1, layer, searchMetric, true, distFn);
1045
+ const nearest = nearestFromHeap(heap);
1046
+ if (nearest) {
1047
+ currentEPs = [nearest.ord];
1048
+ }
1049
+ }
1050
+ const candidateHeap = searchLayer(state, query, qMag, currentEPs, ef, 0, searchMetric, true, distFn);
1051
+ const candidateArray = candidateHeap.toSortedArray().reverse();
1052
+ if (useQuantized) {
1053
+ return rerankWithFullPrecision(
1054
+ state,
1055
+ candidateArray,
1056
+ query,
1057
+ qMag,
1058
+ arenaQuery,
1059
+ k,
1060
+ searchMetric,
1061
+ minSimilarity,
1062
+ filter,
1063
+ hasDocument
1064
+ );
1065
+ }
1066
+ const hits = [];
1067
+ for (const cand of candidateArray) {
1068
+ if (filter && !ordinalFilterHas(filter, cand.ord)) continue;
1069
+ const score = toScore(cand.distance, searchMetric);
1070
+ if (score < minSimilarity) continue;
1071
+ if (!hasDocument(cand.ord)) continue;
1072
+ hits.push({ ord: cand.ord, score });
1073
+ }
1074
+ return hits;
1075
+ }
1076
+ function rerankWithFullPrecision(state, candidates, query, qMag, arenaQuery, k, metric, minSimilarity, filter, hasDocument) {
1077
+ const reranked = [];
1078
+ const rerankLimit = Math.max(k * SQ8_OVERSELECTION_FACTOR, 10);
1079
+ for (const cand of candidates) {
1080
+ if (filter && !ordinalFilterHas(filter, cand.ord)) continue;
1081
+ let fullDistance;
1082
+ if (arenaQuery) {
1083
+ fullDistance = state.store.distanceFromArena(arenaQuery, cand.ord, metric);
1084
+ if (fullDistance === Number.POSITIVE_INFINITY) continue;
1085
+ } else {
1086
+ const entry = entryForOrd(state, cand.ord);
1087
+ if (!entry) continue;
1088
+ fullDistance = toDistance(query, entry.vector, qMag, entry.magnitude, metric);
1089
+ }
1090
+ const score = toScore(fullDistance, metric);
1091
+ if (score < minSimilarity) continue;
1092
+ if (!hasDocument(cand.ord)) continue;
1093
+ reranked.push({ ord: cand.ord, score });
1094
+ if (reranked.length >= rerankLimit) break;
1095
+ }
1096
+ return reranked;
580
1097
  }
1098
+ function search(state, query, k, searchMetric, minSimilarity, filter, efSearch) {
1099
+ const hasDocument = (ord) => state.store.docIdForOrdinal(ord) !== void 0;
1100
+ const hits = collectHits(state, query, k, searchMetric, minSimilarity, filter, efSearch, hasDocument);
1101
+ const results = [];
1102
+ for (const hit of hits) {
1103
+ const docId = state.store.docIdForOrdinal(hit.ord);
1104
+ if (docId === void 0) continue;
1105
+ results.push({
1106
+ docId,
1107
+ score: hit.score,
1108
+ termFrequencies: {},
1109
+ fieldLengths: {},
1110
+ idf: {}
1111
+ });
1112
+ }
1113
+ results.sort((a, b) => b.score - a.score || compareCodePoints(a.docId, b.docId));
1114
+ return results.slice(0, k);
1115
+ }
1116
+ function searchOrdinals(state, query, k, searchMetric, minSimilarity, rankByOrdinal, filter, efSearch) {
1117
+ const hasDocument = (ord) => ord < rankByOrdinal.length && rankByOrdinal[ord] !== ABSENT_DOCUMENT_RANK;
1118
+ const hits = collectHits(state, query, k, searchMetric, minSimilarity, filter, efSearch, hasDocument);
1119
+ hits.sort((a, b) => b.score - a.score || rankByOrdinal[a.ord] - rankByOrdinal[b.ord]);
1120
+ return hits.slice(0, k);
1121
+ }
1122
+
1123
+ // src/vector/hnsw/mutation.ts
581
1124
  function clearTombstone(state, ord) {
582
1125
  if (state.tombstones[ord] === 1) {
583
1126
  state.tombstones[ord] = 0;
@@ -587,21 +1130,21 @@ function clearTombstone(state, ord) {
587
1130
  function reassignEntryPoint(state) {
588
1131
  let bestOrd = -1;
589
1132
  let bestLayer = -1;
590
- for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
591
- const n = state.nodesByOrd[ord];
592
- if (!n) continue;
1133
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1134
+ const level = nodeMaxLayer(state, ord);
1135
+ if (level === -1) continue;
593
1136
  if (state.tombstones[ord] === 1) continue;
594
- if (n.maxLayer > bestLayer) {
595
- bestLayer = n.maxLayer;
1137
+ if (level > bestLayer) {
1138
+ bestLayer = level;
596
1139
  bestOrd = ord;
597
1140
  }
598
1141
  }
599
1142
  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;
1143
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1144
+ const level = nodeMaxLayer(state, ord);
1145
+ if (level === -1) continue;
1146
+ if (level > bestLayer) {
1147
+ bestLayer = level;
605
1148
  bestOrd = ord;
606
1149
  }
607
1150
  }
@@ -622,16 +1165,12 @@ function insertNode(state, docId) {
622
1165
  throw new Error(`Vector dimension mismatch: expected ${state.dimension}, got ${entry.vector.length}`);
623
1166
  }
624
1167
  ensureCapacity(state, ord + 1);
625
- if (state.nodesByOrd[ord] !== void 0) {
1168
+ if (nodeExists(state, ord)) {
626
1169
  removeNodeEager(state, ord);
627
1170
  }
628
1171
  clearTombstone(state, ord);
629
1172
  const l = randomLevel(state.mL);
630
- const node = {
631
- maxLayer: l,
632
- connections: Array.from({ length: l + 1 }, () => [])
633
- };
634
- state.nodesByOrd[ord] = node;
1173
+ createNode(state.adjacency, ord, l);
635
1174
  state.nodeCount++;
636
1175
  if (state.entryPointOrd === -1) {
637
1176
  state.entryPointOrd = ord;
@@ -664,10 +1203,9 @@ function insertNode(state, docId) {
664
1203
  const mc = maxConns(state, layer);
665
1204
  const neighbors = selectNeighborsHeuristic(state, ord, candidates, mc, layer, metric);
666
1205
  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);
1206
+ addNeighbor(state.adjacency, ord, layer, neighbor.ord);
1207
+ if (layer <= nodeMaxLayer(state, neighbor.ord)) {
1208
+ addNeighbor(state.adjacency, neighbor.ord, layer, ord);
671
1209
  pruneConnections(state, neighbor.ord, layer, metric);
672
1210
  }
673
1211
  }
@@ -681,24 +1219,21 @@ function insertNode(state, docId) {
681
1219
  }
682
1220
  }
683
1221
  function removeNodeEager(state, ord, excludeOrds) {
684
- const node = state.nodesByOrd[ord];
685
- if (!node) return;
1222
+ const maxLayer = nodeMaxLayer(state, ord);
1223
+ if (maxLayer === -1) return;
686
1224
  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]];
1225
+ for (let layer = 0; layer <= maxLayer; layer++) {
1226
+ const formerNeighbors = collectNeighbors(state.adjacency, ord, layer);
690
1227
  for (const neighborOrd of formerNeighbors) {
691
- const neighborNode = state.nodesByOrd[neighborOrd];
692
- if (neighborNode && layer < neighborNode.connections.length) {
693
- removeFromArray(neighborNode.connections[layer], ord);
1228
+ if (layer <= nodeMaxLayer(state, neighborOrd)) {
1229
+ removeNeighbor(state.adjacency, neighborOrd, layer, ord);
694
1230
  }
695
1231
  }
696
1232
  for (const neighborOrd of formerNeighbors) {
697
- const neighborNode = state.nodesByOrd[neighborOrd];
698
- if (!neighborNode || layer >= neighborNode.connections.length) continue;
1233
+ if (layer > nodeMaxLayer(state, neighborOrd)) continue;
699
1234
  const mc = maxConns(state, layer);
700
- if (neighborNode.connections[layer].length >= mc) continue;
701
- const candidateOrds = new Set(neighborNode.connections[layer]);
1235
+ if (neighborCount(state.adjacency, neighborOrd, layer) >= mc) continue;
1236
+ const candidateOrds = new Set(collectNeighbors(state.adjacency, neighborOrd, layer));
702
1237
  for (const otherOrd of formerNeighbors) {
703
1238
  if (otherOrd !== neighborOrd && otherOrd !== ord) {
704
1239
  if (excludeOrds?.has(otherOrd)) continue;
@@ -714,17 +1249,16 @@ function removeNodeEager(state, ord, excludeOrds) {
714
1249
  const selected = selectNeighborsHeuristic(state, neighborOrd, candidates, mc, layer, metric);
715
1250
  const newConns = [];
716
1251
  for (const s of selected) addConnection(newConns, s.ord);
717
- neighborNode.connections[layer] = newConns;
1252
+ replaceNeighbors(state.adjacency, neighborOrd, layer, newConns);
718
1253
  for (const newConnOrd of newConns) {
719
- const newConnNode = state.nodesByOrd[newConnOrd];
720
- if (newConnNode && layer < newConnNode.connections.length) {
721
- addConnection(newConnNode.connections[layer], neighborOrd);
1254
+ if (layer <= nodeMaxLayer(state, newConnOrd)) {
1255
+ addNeighbor(state.adjacency, newConnOrd, layer, neighborOrd);
722
1256
  pruneConnections(state, newConnOrd, layer, metric);
723
1257
  }
724
1258
  }
725
1259
  }
726
1260
  }
727
- state.nodesByOrd[ord] = void 0;
1261
+ deleteNode(state.adjacency, ord);
728
1262
  state.nodeCount--;
729
1263
  clearTombstone(state, ord);
730
1264
  if (state.entryPointOrd === ord) {
@@ -738,7 +1272,7 @@ function removeNodeEager(state, ord, excludeOrds) {
738
1272
  }
739
1273
  function markTombstone(state, docId) {
740
1274
  const ord = state.store.getOrdinal(docId);
741
- if (ord === void 0 || state.nodesByOrd[ord] === void 0) return;
1275
+ if (ord === void 0 || !nodeExists(state, ord)) return;
742
1276
  if (state.tombstones[ord] === 0) {
743
1277
  state.tombstones[ord] = 1;
744
1278
  state.tombstoneCount++;
@@ -746,12 +1280,12 @@ function markTombstone(state, docId) {
746
1280
  if (state.entryPointOrd === ord) {
747
1281
  let bestOrd = -1;
748
1282
  let bestLayer = -1;
749
- for (let o = 0; o < state.nodesByOrd.length; o++) {
750
- const n = state.nodesByOrd[o];
751
- if (!n) continue;
1283
+ for (let o = 0; o < state.adjacency.slots; o++) {
1284
+ const level = nodeMaxLayer(state, o);
1285
+ if (level === -1) continue;
752
1286
  if (state.tombstones[o] === 1) continue;
753
- if (n.maxLayer > bestLayer) {
754
- bestLayer = n.maxLayer;
1287
+ if (level > bestLayer) {
1288
+ bestLayer = level;
755
1289
  bestOrd = o;
756
1290
  }
757
1291
  }
@@ -767,8 +1301,8 @@ function markTombstone(state, docId) {
767
1301
  function compactTombstones(state) {
768
1302
  if (state.tombstoneCount === 0) return;
769
1303
  const tombstonedOrds = [];
770
- for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
771
- if (state.tombstones[ord] === 1 && state.nodesByOrd[ord] !== void 0) {
1304
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1305
+ if (state.tombstones[ord] === 1 && nodeExists(state, ord)) {
772
1306
  tombstonedOrds.push(ord);
773
1307
  }
774
1308
  }
@@ -780,12 +1314,12 @@ function compactTombstones(state) {
780
1314
  function rebuild(state) {
781
1315
  if (state.tombstoneCount === 0 && state.nodeCount === 0) return;
782
1316
  const liveOrds = [];
783
- for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
784
- if (state.nodesByOrd[ord] !== void 0 && state.tombstones[ord] !== 1) {
1317
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1318
+ if (nodeExists(state, ord) && state.tombstones[ord] !== 1) {
785
1319
  liveOrds.push(ord);
786
1320
  }
787
1321
  }
788
- state.nodesByOrd = [];
1322
+ resetAdjacency(state.adjacency);
789
1323
  state.tombstones.fill(0);
790
1324
  state.tombstoneCount = 0;
791
1325
  state.nodeCount = 0;
@@ -802,16 +1336,16 @@ function rebuild(state) {
802
1336
  // src/vector/hnsw/persistence.ts
803
1337
  function serializeGraph(state) {
804
1338
  const nodeArray = [];
805
- for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
806
- const node = state.nodesByOrd[ord];
807
- if (!node) continue;
1339
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1340
+ const maxLayer = nodeMaxLayer(state, ord);
1341
+ if (maxLayer === -1) continue;
808
1342
  if (state.tombstones[ord] === 1) continue;
809
1343
  const docId = state.store.docIdForOrdinal(ord);
810
1344
  if (docId === void 0) continue;
811
1345
  const layerConns = [];
812
- for (let layer = 0; layer < node.connections.length; layer++) {
1346
+ for (let layer = 0; layer <= maxLayer; layer++) {
813
1347
  const liveNeighbors = [];
814
- for (const neighborOrd of node.connections[layer]) {
1348
+ for (const neighborOrd of collectNeighbors(state.adjacency, ord, layer)) {
815
1349
  if (state.tombstones[neighborOrd] === 1) continue;
816
1350
  const neighborDoc = state.store.docIdForOrdinal(neighborOrd);
817
1351
  if (neighborDoc === void 0) continue;
@@ -821,7 +1355,7 @@ function serializeGraph(state) {
821
1355
  layerConns.push([layer, liveNeighbors]);
822
1356
  }
823
1357
  }
824
- nodeArray.push([docId, node.maxLayer, layerConns]);
1358
+ nodeArray.push([docId, maxLayer, layerConns]);
825
1359
  }
826
1360
  const entryPoint = state.entryPointOrd === -1 ? null : state.store.docIdForOrdinal(state.entryPointOrd) ?? null;
827
1361
  return {
@@ -833,54 +1367,66 @@ function serializeGraph(state) {
833
1367
  nodes: nodeArray
834
1368
  };
835
1369
  }
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;
1370
+ function resolveNodes(state, data) {
1371
+ const nodes = [];
843
1372
  let maxOrd = -1;
844
1373
  for (const [docId, maxLayer, layerConns] of data.nodes) {
845
1374
  const ord = state.store.getOrdinal(docId);
846
1375
  if (ord === void 0) continue;
847
1376
  if (state.store.entryForOrdinal(ord) === void 0) continue;
848
- const connections = Array.from({ length: maxLayer + 1 }, () => []);
1377
+ const clampedMaxLayer = Math.min(Math.max(maxLayer, 0), MAX_LAYER_CAP);
1378
+ const layers = [];
849
1379
  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;
1380
+ if (layer < 0 || layer > clampedMaxLayer) continue;
1381
+ const limit = maxConns(state, layer);
1382
+ const resolved = [];
1383
+ for (const neighborDoc of neighbors) {
1384
+ if (resolved.length >= limit) break;
1385
+ const neighborOrd = state.store.getOrdinal(neighborDoc);
1386
+ if (neighborOrd === void 0) continue;
1387
+ addConnection(resolved, neighborOrd);
1388
+ if (neighborOrd > maxOrd) maxOrd = neighborOrd;
859
1389
  }
1390
+ layers.push([layer, resolved]);
860
1391
  }
861
- const node = { maxLayer, connections };
862
- state.nodesByOrd[ord] = node;
863
- state.nodeCount++;
864
1392
  if (ord > maxOrd) maxOrd = ord;
1393
+ nodes.push({ ord, maxLayer: clampedMaxLayer, layers });
865
1394
  }
1395
+ return { nodes, maxOrd };
1396
+ }
1397
+ function deserializeGraph(state, data) {
1398
+ resetAdjacency(state.adjacency);
1399
+ state.tombstones.fill(0);
1400
+ state.tombstoneCount = 0;
1401
+ state.nodeCount = 0;
1402
+ state.entryPointOrd = -1;
1403
+ state.topLayer = -1;
1404
+ const { nodes, maxOrd } = resolveNodes(state, data);
866
1405
  if (maxOrd >= 0) {
867
1406
  ensureCapacity(state, maxOrd + 1);
868
1407
  }
1408
+ for (const node of nodes) {
1409
+ createNode(state.adjacency, node.ord, node.maxLayer);
1410
+ for (const [layer, neighbors] of node.layers) {
1411
+ replaceNeighbors(state.adjacency, node.ord, layer, neighbors);
1412
+ }
1413
+ state.nodeCount++;
1414
+ }
869
1415
  if (data.entryPoint != null && data.entryPoint !== "") {
870
1416
  const epOrd = state.store.getOrdinal(data.entryPoint);
871
- if (epOrd !== void 0 && state.nodesByOrd[epOrd] !== void 0) {
1417
+ if (epOrd !== void 0 && nodeExists(state, epOrd)) {
872
1418
  state.entryPointOrd = epOrd;
873
- state.topLayer = data.maxLayer;
1419
+ state.topLayer = Math.min(Math.max(data.maxLayer, 0), MAX_LAYER_CAP);
874
1420
  }
875
1421
  }
876
1422
  if (state.entryPointOrd === -1 && state.nodeCount > 0) {
877
1423
  let bestOrd = -1;
878
1424
  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;
1425
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1426
+ const level = nodeMaxLayer(state, ord);
1427
+ if (level === -1) continue;
1428
+ if (level > bestLayer) {
1429
+ bestLayer = level;
884
1430
  bestOrd = ord;
885
1431
  }
886
1432
  }
@@ -889,102 +1435,6 @@ function deserializeGraph(state, data) {
889
1435
  }
890
1436
  }
891
1437
 
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
1438
  // src/vector/hnsw/index.ts
989
1439
  function createHNSWIndex(dimension, store, config, quantizer) {
990
1440
  const M = config?.m ?? 16;
@@ -1001,7 +1451,7 @@ function createHNSWIndex(dimension, store, config, quantizer) {
1001
1451
  efCons,
1002
1452
  buildMetric,
1003
1453
  mL,
1004
- nodesByOrd: [],
1454
+ adjacency: createAdjacency(M, Mmax0),
1005
1455
  tombstones: new Uint8Array(0),
1006
1456
  tombstoneCount: 0,
1007
1457
  nodeCount: 0,
@@ -1012,7 +1462,7 @@ function createHNSWIndex(dimension, store, config, quantizer) {
1012
1462
  topLayer: -1
1013
1463
  };
1014
1464
  function clear() {
1015
- state.nodesByOrd = [];
1465
+ resetAdjacency(state.adjacency);
1016
1466
  state.tombstones = new Uint8Array(0);
1017
1467
  state.tombstoneCount = 0;
1018
1468
  state.nodeCount = 0;
@@ -1023,8 +1473,8 @@ function createHNSWIndex(dimension, store, config, quantizer) {
1023
1473
  state.topLayer = -1;
1024
1474
  }
1025
1475
  function* entriesIterator() {
1026
- for (let ord = 0; ord < state.nodesByOrd.length; ord++) {
1027
- if (state.nodesByOrd[ord] === void 0) continue;
1476
+ for (let ord = 0; ord < state.adjacency.slots; ord++) {
1477
+ if (!hasNode(state.adjacency, ord)) continue;
1028
1478
  if (state.tombstones[ord] === 1) continue;
1029
1479
  const docId = state.store.docIdForOrdinal(ord);
1030
1480
  if (docId === void 0) continue;
@@ -1062,25 +1512,52 @@ function createHNSWIndex(dimension, store, config, quantizer) {
1062
1512
  get metric() {
1063
1513
  return state.buildMetric;
1064
1514
  },
1515
+ get adjacencyBytes() {
1516
+ return estimateAdjacencyBytes(state.adjacency);
1517
+ },
1065
1518
  insertNode: (docId) => insertNode(state, docId),
1066
1519
  markTombstone: (docId) => markTombstone(state, docId),
1067
1520
  has: (docId) => {
1068
1521
  const ord = state.store.getOrdinal(docId);
1069
- return ord !== void 0 && state.nodesByOrd[ord] !== void 0 && state.tombstones[ord] !== 1;
1522
+ return ord !== void 0 && hasNode(state.adjacency, ord) && state.tombstones[ord] !== 1;
1070
1523
  },
1071
1524
  isTombstoned: (docId) => {
1072
1525
  const ord = state.store.getOrdinal(docId);
1073
1526
  return ord !== void 0 && state.tombstones[ord] === 1;
1074
1527
  },
1075
- search: (query, k, searchMetric, minSimilarity, filterDocIds, efSearch) => search(state, query, k, searchMetric, minSimilarity, filterDocIds, efSearch),
1528
+ search: (query, k, searchMetric, minSimilarity, filter, efSearch) => search(state, query, k, searchMetric, minSimilarity, filter, efSearch),
1076
1529
  clear,
1077
1530
  entries: entriesIterator,
1078
1531
  compactionNeeded,
1079
1532
  compactTombstones: () => compactTombstones(state),
1080
1533
  rebuild: () => rebuild(state),
1081
1534
  serialize: () => serializeGraph(state),
1082
- deserialize: (data) => deserializeGraph(state, data)
1535
+ deserialize: (data) => deserializeGraph(state, data),
1536
+ exportSnapshot: () => ({
1537
+ dimension: state.dimension,
1538
+ m: state.M,
1539
+ efConstruction: state.efCons,
1540
+ metric: state.buildMetric,
1541
+ adjacency: exportAdjacency(state.adjacency),
1542
+ tombstones: state.tombstones.slice(),
1543
+ tombstoneCount: state.tombstoneCount,
1544
+ nodeCount: state.nodeCount,
1545
+ capacity: state.capacity,
1546
+ entryPointOrd: state.entryPointOrd,
1547
+ topLayer: state.topLayer
1548
+ }),
1549
+ restoreSnapshot: (snapshot) => {
1550
+ state.adjacency = importAdjacency(snapshot.adjacency);
1551
+ state.tombstones = snapshot.tombstones;
1552
+ state.tombstoneCount = snapshot.tombstoneCount;
1553
+ state.nodeCount = snapshot.nodeCount;
1554
+ state.capacity = Math.max(snapshot.capacity, snapshot.adjacency.capacity);
1555
+ state.visited = new Uint32Array(state.capacity);
1556
+ state.visitStamp = 0;
1557
+ state.entryPointOrd = snapshot.entryPointOrd;
1558
+ state.topLayer = snapshot.topLayer;
1559
+ }
1083
1560
  };
1084
1561
  }
1085
1562
 
1086
- export { cosineSimilarityWithMagnitudes, createArenaSimd, createBoundedMaxHeap, createHNSWIndex, createVectorStore, dotProduct, euclideanDistance, isSimdAvailable, magnitude };
1563
+ export { ABSENT_DOCUMENT_RANK, MAX_M, addToOrdinalFilter, arenaFloat32Distance, cosineSimilarityWithMagnitudes, createArenaSimd, createBoundedMaxHeap, createHNSWIndex, createOrdinalFilter, createSharedArenaSimd, createVectorStore, dotProduct, euclideanDistance, importAdjacency, isSimdAvailable, magnitude, ordinalFilterHas, ordinalFilterValues, removeFromOrdinalFilter, searchOrdinals, toScore };