@delali/narsil 0.3.1-next.72 → 0.3.1-next.74

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 (70) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/filesystem.mjs +1 -1
  3. package/dist/adapters/indexeddb.mjs +1 -1
  4. package/dist/{capabilities-D6gjjQyA.d.ts → capabilities-CAMzmkAg.d.ts} +19 -4
  5. package/dist/{chunk-P2ZZN32C.mjs → chunk-2E35UM3G.mjs} +2 -12
  6. package/dist/chunk-3DDUNI6N.mjs +48 -0
  7. package/dist/{chunk-KZTI25D3.mjs → chunk-4MQ5F4IL.mjs} +1 -1
  8. package/dist/{chunk-HVJYTKK7.mjs → chunk-BVBIQQKG.mjs} +947 -1327
  9. package/dist/{chunk-2X2ZQOAB.mjs → chunk-CTOSGRM7.mjs} +101 -21
  10. package/dist/{chunk-7QMTA7AA.mjs → chunk-CUZOX6CN.mjs} +7784 -4276
  11. package/dist/chunk-I23G3IZC.mjs +593 -0
  12. package/dist/{chunk-RV77FR2F.mjs → chunk-I64LXKO3.mjs} +0 -1
  13. package/dist/chunk-KEHK2O6P.mjs +13 -0
  14. package/dist/{chunk-CVPA5Q7R.mjs → chunk-LX4JERXI.mjs} +9 -6
  15. package/dist/{chunk-EDMCZLSJ.mjs → chunk-NOZUQM7J.mjs} +193 -13
  16. package/dist/{chunk-LCCL6EEH.mjs → chunk-PO53FV4Q.mjs} +93 -94
  17. package/dist/chunk-T5ZWEWVD.mjs +658 -0
  18. package/dist/{chunk-Y52WZWAU.mjs → chunk-TVFLHM7M.mjs} +4 -4
  19. package/dist/{chunk-QR4OKRVY.mjs → chunk-VKX2BPXD.mjs} +147 -642
  20. package/dist/{chunk-3HQPUCCT.mjs → chunk-XW25X6AU.mjs} +66 -237
  21. package/dist/{chunk-M6XF5HDE.mjs → chunk-Z3DQPOJQ.mjs} +127 -30
  22. package/dist/{client-C2TiW29y.d.ts → client-C-I70ArH.d.ts} +77 -26
  23. package/dist/client.d.ts +9 -9
  24. package/dist/client.mjs +44 -22
  25. package/dist/{config-BLguB-om.d.ts → config-C6Iw5rCX.d.ts} +11 -4
  26. package/dist/{config-C7Ip8Ife.d.ts → config-DJPH3IQB.d.ts} +11 -2
  27. package/dist/direct-executor-KFAGILJC.mjs +17 -0
  28. package/dist/distribution/coordinator/etcd.d.ts +29 -16
  29. package/dist/distribution/coordinator/etcd.mjs +162 -75
  30. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  31. package/dist/distribution/transport/grpc.d.ts +2 -2
  32. package/dist/distribution/transport/grpc.mjs +22 -2
  33. package/dist/distribution/transport/in-memory.d.ts +2 -2
  34. package/dist/distribution/transport/in-memory.mjs +6 -3
  35. package/dist/distribution/transport/tcp.d.ts +2 -2
  36. package/dist/distribution/transport/tcp.mjs +40 -16
  37. package/dist/distribution.d.ts +9 -9
  38. package/dist/distribution.mjs +647 -366
  39. package/dist/embeddings/openai.mjs +1 -1
  40. package/dist/{engine-Dm4446j1.d.ts → engine-B8eRPO7m.d.ts} +97 -188
  41. package/dist/index.browser.mjs +12132 -8592
  42. package/dist/index.d.ts +27 -8
  43. package/dist/index.mjs +15 -14
  44. package/dist/{memory-CKHTU1CL.d.ts → memory-DJEwWoCi.d.ts} +1204 -968
  45. package/dist/partial-words-DTIHO8G4.d.ts +19 -0
  46. package/dist/partial-words.d.ts +22 -0
  47. package/dist/partial-words.mjs +11 -0
  48. package/dist/pattern.mjs +1 -1
  49. package/dist/persistence/durability/checkpoint-worker.d.ts +57 -29
  50. package/dist/persistence/durability/checkpoint-worker.mjs +26 -16
  51. package/dist/react.d.ts +7 -7
  52. package/dist/react.mjs +0 -1
  53. package/dist/{schema-BBOCdhND.d.ts → schema-DgyDijhf.d.ts} +6 -2
  54. package/dist/server.d.ts +11 -11
  55. package/dist/server.mjs +212 -32
  56. package/dist/{tasks-BECd0b__.d.ts → tasks-BVmAKYBh.d.ts} +3 -3
  57. package/dist/{thread-IBSIZZ6W.mjs → thread-W6DPKEF6.mjs} +82 -21
  58. package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
  59. package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
  60. package/dist/{types-BiUmaiWT.d.ts → types-ChrVy4lH.d.ts} +1 -1
  61. package/dist/vector/search-worker.d.ts +1 -1
  62. package/dist/vector/search-worker.mjs +4 -3
  63. package/dist/workers/entry.mjs +4 -4
  64. package/package.json +15 -7
  65. package/dist/chunk-3L4U6EJQ.mjs +0 -49
  66. package/dist/chunk-IFBKLBGB.mjs +0 -7
  67. package/dist/chunk-XZ4PAREY.mjs +0 -25
  68. package/dist/direct-executor-SVCHXR74.mjs +0 -14
  69. package/dist/serialization/crc32-worker.d.ts +0 -20
  70. package/dist/serialization/crc32-worker.mjs +0 -62
@@ -1,6 +1,9 @@
1
- import { MAX_SORT_FIELDS } from './chunk-M6XF5HDE.mjs';
2
- import { nativeSearchCoreIsEnabled, loadNativeCore } from './chunk-P2ZZN32C.mjs';
3
- import { NarsilError, ServerErrorCodes, ErrorCodes } from './chunk-RV77FR2F.mjs';
1
+ import { MAX_SORT_FIELDS } from './chunk-3DDUNI6N.mjs';
2
+ import { nativeSearchCoreIsEnabled, loadNativeCore } from './chunk-2E35UM3G.mjs';
3
+ import { NarsilError, ServerErrorCodes, ErrorCodes } from './chunk-I64LXKO3.mjs';
4
+ import { mkdtemp, open, rm } from 'fs/promises';
5
+ import { tmpdir } from 'os';
6
+ import path from 'path';
4
7
 
5
8
  // src/server/capabilities.ts
6
9
  var ASYNC_IMPORT_CAPABILITY = "documents.import.async";
@@ -8,12 +11,14 @@ var TASK_CANCEL_CAPABILITY = "tasks.cancel";
8
11
  var TASK_FILTER_CAPABILITY = "tasks.filter";
9
12
  var REBUILD_ANALYSIS_CAPABILITY = "indexes.rebuildAnalysis";
10
13
  var INDEX_LIFECYCLE_CAPABILITY = "indexes.lifecycle";
14
+ var DOCUMENT_EXPORT_CAPABILITY = "indexes.documentExport";
11
15
  var SERVER_CAPABILITIES = [
12
16
  ASYNC_IMPORT_CAPABILITY,
13
17
  TASK_CANCEL_CAPABILITY,
14
18
  TASK_FILTER_CAPABILITY,
15
19
  REBUILD_ANALYSIS_CAPABILITY,
16
- INDEX_LIFECYCLE_CAPABILITY
20
+ INDEX_LIFECYCLE_CAPABILITY,
21
+ DOCUMENT_EXPORT_CAPABILITY
17
22
  ];
18
23
 
19
24
  // src/server/concurrency-gate.ts
@@ -171,7 +176,6 @@ var STATUS_BY_CODE = {
171
176
  SNAPSHOT_SYNC_HEADER_INVALID: 400,
172
177
  SNAPSHOT_SYNC_FRAME_INVALID: 400,
173
178
  SNAPSHOT_SYNC_INDEX_NOT_FOUND: 404,
174
- SNAPSHOT_SYNC_TOO_LARGE: 413,
175
179
  SNAPSHOT_SYNC_CHUNK_SIZE_EXCEEDED: 413,
176
180
  SNAPSHOT_SYNC_ABORTED: 409,
177
181
  SNAPSHOT_SYNC_CAPACITY_EXHAUSTED: 503,
@@ -226,6 +230,11 @@ function encodeJson(value) {
226
230
  var BYTES_PER_MB = 1024 * 1024;
227
231
  var DEFAULT_MAX_BODY_BYTES = 16 * BYTES_PER_MB;
228
232
  var DEFAULT_MAX_IMPORT_BYTES = 100 * BYTES_PER_MB;
233
+ var DEFAULT_MAX_RESTORE_BYTES = 64 * 1024 * BYTES_PER_MB;
234
+ var SPOOL_PAUSE_ABOVE_BYTES = 8 * BYTES_PER_MB;
235
+ var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/x-tar";
236
+ var DOCUMENT_EXPORT_CONTENT_TYPE = "application/x-ndjson";
237
+ var RELAY_STREAM_CHUNKS_IN_FLIGHT = 4;
229
238
  var DEFAULT_MAX_LINE_BYTES = 4 * BYTES_PER_MB;
230
239
  var DEFAULT_IMPORT_BATCH_SIZE = 1e3;
231
240
  var IMPORT_FLUSHES_IN_FLIGHT = 4;
@@ -300,8 +309,56 @@ function sendError(res, status, code, message, details) {
300
309
  res.writeStatus(statusLine(status)).writeHeader("Content-Type", "application/json").end(payload);
301
310
  });
302
311
  }
303
- function sendBinary(res, data, abort, status = 200) {
304
- streamBody(res, abort, data, status, [["Content-Type", "application/octet-stream"]]);
312
+ async function sendStream(res, abort, source, headers) {
313
+ const reader = source.getReader();
314
+ let wake = null;
315
+ abort.onAbort(() => wake?.());
316
+ const waitUntilWritable = () => new Promise((resolve) => {
317
+ wake = () => {
318
+ wake = null;
319
+ resolve();
320
+ };
321
+ res.onWritable(() => {
322
+ wake?.();
323
+ return true;
324
+ });
325
+ });
326
+ let started = false;
327
+ const begin = () => {
328
+ if (started) return;
329
+ started = true;
330
+ res.writeStatus(statusLine(200));
331
+ for (const [key, value] of headers) res.writeHeader(key, value);
332
+ };
333
+ try {
334
+ for (; ; ) {
335
+ const step = await reader.read();
336
+ if (abort.aborted) {
337
+ await reader.cancel().catch(() => void 0);
338
+ return;
339
+ }
340
+ if (step.done) {
341
+ res.cork(() => {
342
+ begin();
343
+ res.end();
344
+ });
345
+ return;
346
+ }
347
+ let writable = true;
348
+ res.cork(() => {
349
+ begin();
350
+ writable = res.write(step.value);
351
+ });
352
+ if (!writable && !abort.aborted) await waitUntilWritable();
353
+ }
354
+ } catch (err) {
355
+ await reader.cancel().catch(() => void 0);
356
+ if (abort.aborted) return;
357
+ if (!started) throw err;
358
+ res.cork(() => {
359
+ res.close();
360
+ });
361
+ }
305
362
  }
306
363
  function sendRelayed(res, abort, status, headers, body) {
307
364
  if (abort.aborted) return;
@@ -496,7 +553,14 @@ function validateRebalance(body) {
496
553
  }
497
554
  return null;
498
555
  }
499
- var TASK_TYPES = ["optimizeVectors", "rebalance", "restore", "import", "rebuildAnalysis"];
556
+ var TASK_TYPES = [
557
+ "optimizeVectors",
558
+ "rebalance",
559
+ "restore",
560
+ "import",
561
+ "importExport",
562
+ "rebuildAnalysis"
563
+ ];
500
564
  var TASK_STATUSES = ["running", "succeeded", "failed", "cancelled"];
501
565
  function parseCountParam(raw, field, minimum) {
502
566
  if (raw === null) return minimum;
@@ -631,6 +695,87 @@ function createVersionHandler(deps) {
631
695
  }
632
696
  return { report };
633
697
  }
698
+ var NotAdmittedError = class extends Error {
699
+ };
700
+ async function removeDirectory(directory) {
701
+ if (directory !== null) await rm(directory, { recursive: true, force: true });
702
+ }
703
+ function spoolBody(res, maxBytes, abort, admitted) {
704
+ return new Promise((resolve, reject) => {
705
+ const state = {
706
+ directory: null,
707
+ handle: null,
708
+ chain: Promise.resolve(),
709
+ queued: 0,
710
+ total: 0,
711
+ paused: true,
712
+ done: false
713
+ };
714
+ const fail = (error, answer) => {
715
+ if (state.done) return;
716
+ state.done = true;
717
+ if (answer && !abort.aborted) {
718
+ sendError(res, 500, ServerErrorCodes.INTERNAL_ERROR, "The server could not store the request body");
719
+ }
720
+ void state.chain.catch(() => void 0).then(async () => {
721
+ await state.handle?.close().catch(() => void 0);
722
+ await removeDirectory(state.directory).catch(() => void 0);
723
+ }).finally(() => reject(error));
724
+ };
725
+ const failFromChain = (error) => {
726
+ if (error instanceof NotAdmittedError) fail(error, false);
727
+ else fail(error instanceof Error ? error : new Error(String(error)), true);
728
+ };
729
+ res.pause();
730
+ state.chain = (async () => {
731
+ if (!await admitted) throw new NotAdmittedError("the request was not admitted");
732
+ state.directory = await mkdtemp(path.join(tmpdir(), "narsil-spool-"));
733
+ state.handle = await open(path.join(state.directory, "body"), "w");
734
+ if (!abort.aborted && !state.done && state.queued <= SPOOL_PAUSE_ABOVE_BYTES) {
735
+ state.paused = false;
736
+ res.resume();
737
+ }
738
+ })();
739
+ state.chain.catch(failFromChain);
740
+ abort.onAbort(() => fail(new Error("aborted"), false));
741
+ res.onData((chunk, isLast) => {
742
+ if (state.done || abort.aborted) return;
743
+ state.total += chunk.byteLength;
744
+ if (maxBytes > 0 && state.total > maxBytes) {
745
+ sendError(res, 413, ServerErrorCodes.PAYLOAD_TOO_LARGE, "Request body exceeds the configured size limit");
746
+ fail(new Error("payload too large"), false);
747
+ return;
748
+ }
749
+ const copy = new Uint8Array(chunk.slice(0));
750
+ state.queued += copy.byteLength;
751
+ if (!state.paused && state.queued > SPOOL_PAUSE_ABOVE_BYTES) {
752
+ state.paused = true;
753
+ res.pause();
754
+ }
755
+ state.chain = state.chain.then(async () => {
756
+ if (state.handle === null) throw new Error("the spool file is not open");
757
+ await state.handle.write(copy);
758
+ state.queued -= copy.byteLength;
759
+ if (state.paused && state.queued <= SPOOL_PAUSE_ABOVE_BYTES && !abort.aborted && !state.done) {
760
+ state.paused = false;
761
+ res.resume();
762
+ }
763
+ });
764
+ state.chain.catch(failFromChain);
765
+ if (!isLast) return;
766
+ state.chain.then(async () => {
767
+ if (state.done) return;
768
+ state.done = true;
769
+ await state.handle?.close();
770
+ resolve({
771
+ path: path.join(state.directory ?? "", "body"),
772
+ bytes: state.total,
773
+ remove: () => removeDirectory(state.directory)
774
+ });
775
+ }, failFromChain);
776
+ });
777
+ });
778
+ }
634
779
 
635
780
  // src/server/request.ts
636
781
  function initAbortHandler(res) {
@@ -732,6 +877,19 @@ function sinkWritingAfterStatus(res, headers) {
732
877
  res.end(body);
733
878
  return sink;
734
879
  },
880
+ write(chunk) {
881
+ return res.write(chunk);
882
+ },
883
+ pause() {
884
+ res.pause();
885
+ },
886
+ resume() {
887
+ res.resume();
888
+ },
889
+ close() {
890
+ res.close();
891
+ return sink;
892
+ },
735
893
  endWithoutBody() {
736
894
  res.endWithoutBody();
737
895
  return sink;
@@ -830,18 +988,33 @@ function createRouteRunner(deps) {
830
988
  }
831
989
  const bodyPromise = needsBody ? readBody(res, opts.maxBytes, abort, declaredBytes) : Promise.resolve(null);
832
990
  const hookPromise = hookContext !== null && authorize !== void 0 ? authorize(hookContext) : Promise.resolve({ allowed: true });
833
- Promise.all([bodyPromise, hookPromise]).then(async ([rawBody, authorization]) => {
834
- if (abort.aborted) return;
991
+ const spoolPromise = opts.spoolsBody === true ? spoolBody(
992
+ res,
993
+ opts.maxBytes,
994
+ abort,
995
+ hookPromise.then((authorization) => authorization.allowed)
996
+ ).catch(() => null) : Promise.resolve(null);
997
+ Promise.all([bodyPromise, spoolPromise, hookPromise]).then(async ([rawBody, spooledBody, authorization]) => {
998
+ const discardSpool = () => {
999
+ void spooledBody?.remove().catch(() => void 0);
1000
+ };
1001
+ if (abort.aborted) {
1002
+ discardSpool();
1003
+ return;
1004
+ }
835
1005
  if (!authorization.allowed) {
1006
+ discardSpool();
836
1007
  sendDenial(res, authorization);
837
1008
  return;
838
1009
  }
1010
+ if (opts.spoolsBody === true && spooledBody === null) return;
839
1011
  if (gated && !gate.tryAcquire()) {
1012
+ discardSpool();
840
1013
  sendError(res, 429, ServerErrorCodes.TOO_MANY_REQUESTS, "The server is at capacity; retry shortly");
841
1014
  return;
842
1015
  }
843
1016
  try {
844
- await handler({ res, params, query, contentType, rawBody, abort, hookContext });
1017
+ await handler({ res, params, query, contentType, rawBody, spooledBody, abort, hookContext });
845
1018
  } catch (err) {
846
1019
  if (!abort.aborted) {
847
1020
  sendError(res, 500, ServerErrorCodes.INTERNAL_ERROR, "An unexpected error occurred");
@@ -916,7 +1089,14 @@ function serverRoutes(limits) {
916
1089
  method: "post",
917
1090
  path: "/indexes/:name/restore",
918
1091
  handler: "restore",
919
- options: { paramCount: 1, needsBody: true, maxBytes: maxImportBytes }
1092
+ options: { paramCount: 1, spoolsBody: true, maxBytes: limits.maxRestoreBytes }
1093
+ },
1094
+ { method: "get", path: "/indexes/:name/export", handler: "exportDocuments", options: named },
1095
+ {
1096
+ method: "post",
1097
+ path: "/indexes/:name/import",
1098
+ handler: "importExport",
1099
+ options: { paramCount: 1, spoolsBody: true, maxBytes: limits.maxRestoreBytes }
920
1100
  }
921
1101
  ];
922
1102
  }
@@ -975,4 +1155,4 @@ function childAppOf(app) {
975
1155
  return candidate;
976
1156
  }
977
1157
 
978
- export { ASYNC_IMPORT_CAPABILITY, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_RETAINED_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_TASK_PAGE_SIZE, IMPORT_FLUSHES_IN_FLIGHT, INDEX_LIFECYCLE_CAPABILITY, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, MAIN_THREAD_GATE_SLOT, MAX_INDEX_TOUCH_INTERVAL_MS, MIN_COUNTED_SERVER_LIMIT, MIN_UNBOUNDED_SERVER_LIMIT, REBUILD_ANALYSIS_CAPABILITY, RUNNING_TASK_TTL_MS, SERVER_CAPABILITIES, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, TASK_PROGRESS_WRITE_INTERVAL_MS, TERMINAL_TASK_TTL_MS, acceptorOf, authorizerFor, badRequest, childAppOf, clearGateSlot, corsHeaderReader, createDocumentReadHandlers, createRouteRunner, createSharedGateBuffer, createSharedRequestGate, createVersionHandler, gateSlotOfWorker, httpStatusForNarsilError, loadUWebSockets, parseJson, parseJsonOptional, parseTaskListQuery, registerServerRoutes, rejectInvalid, resolveCors, respondError, respondJson, sendBinary, sendError, sendRelayed, serializeBatchResult, serializeNarsilError, serverRoutes, toHttpError, validateBatch, validateCreateIndex, validateDocumentBody, validateQuery, validateRebalance, validateSuggest, versionReport };
1158
+ export { ASYNC_IMPORT_CAPABILITY, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_RESTORE_BYTES, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_RETAINED_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_TASK_PAGE_SIZE, DOCUMENT_EXPORT_CAPABILITY, DOCUMENT_EXPORT_CONTENT_TYPE, IMPORT_FLUSHES_IN_FLIGHT, INDEX_LIFECYCLE_CAPABILITY, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, MAIN_THREAD_GATE_SLOT, MAX_INDEX_TOUCH_INTERVAL_MS, MIN_COUNTED_SERVER_LIMIT, MIN_UNBOUNDED_SERVER_LIMIT, REBUILD_ANALYSIS_CAPABILITY, RELAY_STREAM_CHUNKS_IN_FLIGHT, RUNNING_TASK_TTL_MS, SERVER_CAPABILITIES, SNAPSHOT_ARCHIVE_CONTENT_TYPE, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, TASK_PROGRESS_WRITE_INTERVAL_MS, TERMINAL_TASK_TTL_MS, acceptorOf, authorizerFor, badRequest, childAppOf, clearGateSlot, corsHeaderReader, createDocumentReadHandlers, createRouteRunner, createSharedGateBuffer, createSharedRequestGate, createVersionHandler, gateSlotOfWorker, httpStatusForNarsilError, loadUWebSockets, parseJson, parseJsonOptional, parseTaskListQuery, registerServerRoutes, rejectInvalid, resolveCors, respondError, respondJson, sendError, sendRelayed, sendStream, serializeBatchResult, serializeNarsilError, serverRoutes, statusLine, toHttpError, validateBatch, validateCreateIndex, validateDocumentBody, validateQuery, validateRebalance, validateSuggest, versionReport };
@@ -1,7 +1,8 @@
1
1
  import { OSQ_STEPS, OSQ_GRID, OSQ_REFINE_ROUNDS, OSQ_REFINE_TOLERANCE, OSQ4_NARROW_DIMENSION_LIMIT, DEFAULT_OSQ4_NARROW_OVERSAMPLE, DEFAULT_OVERSAMPLE_BY_BITS, OSQ_QUERY_BITS, OSQ_LAMBDA } from './chunk-ZJ4CSAB2.mjs';
2
- import { COMPACTION_TOMBSTONE_RATIO, COMPACTION_ABSOLUTE_THRESHOLD, nativeSearchCoreIsEnabled, loadNativeCore, stopUsingTheNativeSearchCore, noteFallback, MAX_LAYER_CAP, PLACEMENT_ROOM_REQUESTS, INITIAL_LIST_CAPACITY, DEFAULT_EF_SEARCH, INITIAL_UPPER_SLOTS } from './chunk-P2ZZN32C.mjs';
2
+ import { nativeSearchCoreIsEnabled, loadNativeCore, stopUsingTheNativeSearchCore, noteFallback } from './chunk-2E35UM3G.mjs';
3
+ import { COMPACTION_TOMBSTONE_RATIO, COMPACTION_ABSOLUTE_THRESHOLD, MAX_LAYER_CAP, PLACEMENT_ROOM_REQUESTS, INITIAL_LIST_CAPACITY, DEFAULT_EF_SEARCH, INITIAL_UPPER_SLOTS } from './chunk-KEHK2O6P.mjs';
3
4
  import { compareCodePoints } from './chunk-LJYTW7GT.mjs';
4
- import { NarsilError, ErrorCodes } from './chunk-RV77FR2F.mjs';
5
+ import { NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
5
6
  import { readSync, closeSync, openSync } from 'fs';
6
7
 
7
8
  // src/vector/constants.ts
@@ -3651,6 +3652,13 @@ function createVectorStore(options) {
3651
3652
  yield [docId, entryAt(open, ordinal)];
3652
3653
  }
3653
3654
  },
3655
+ *docIds() {
3656
+ if (open === null) return;
3657
+ for (let ordinal = 0; ordinal < ordToDoc.length; ordinal++) {
3658
+ const docId = ordToDoc[ordinal];
3659
+ if (docId !== void 0) yield docId;
3660
+ }
3661
+ },
3654
3662
  clear() {
3655
3663
  forgetDocuments();
3656
3664
  if (open === null) return;
@@ -3789,30 +3797,24 @@ function savedTopLayer(state, ordinal) {
3789
3797
  if (ordinal === NO_ORDINAL || isTombstoned(state, ordinal)) return NO_LAYER;
3790
3798
  return Math.min(nodeMaxLayer(state, ordinal), MAX_LAYER_CAP);
3791
3799
  }
3792
- function largestNeighbourValueCount(state, topLayers) {
3800
+ function neighbourValueCount(state, topLayers, start, end) {
3793
3801
  let values = 0;
3794
- for (let number = 0; number < topLayers.length; number++) {
3802
+ for (let number = start; number < end; number++) {
3795
3803
  const top = topLayers[number];
3796
3804
  if (top < 0) continue;
3797
3805
  values += state.adjacency.level0Stride + top * state.adjacency.upperStride;
3798
3806
  }
3799
3807
  return values;
3800
3808
  }
3801
- function serializeNumberedGraph(state, numberOfOrdinal, ordinalOfNumber) {
3809
+ function serializePart(state, numbers, start, end) {
3810
+ const { numberOfOrdinal, ordinalOfNumber, topLayers } = numbers;
3802
3811
  const adjacency = state.adjacency;
3803
- const slots = adjacencySlots(adjacency);
3804
- const topLayers = new Int8Array(ordinalOfNumber.length).fill(-1);
3805
- const levels = new Uint8Array(ordinalOfNumber.length);
3806
- for (let number = 0; number < ordinalOfNumber.length; number++) {
3807
- const ordinal = ordinalOfNumber[number];
3808
- if (ordinal >= slots) continue;
3809
- topLayers[number] = savedTopLayer(state, ordinal);
3810
- levels[number] = topLayers[number] + 1;
3811
- }
3812
- const values = new Uint32Array(largestNeighbourValueCount(state, topLayers));
3812
+ const levels = new Uint8Array(end - start);
3813
+ const values = new Uint32Array(neighbourValueCount(state, topLayers, start, end));
3813
3814
  let cursor = 0;
3814
- for (let number = 0; number < ordinalOfNumber.length; number++) {
3815
+ for (let number = start; number < end; number++) {
3815
3816
  const top = topLayers[number];
3817
+ levels[number - start] = top + 1;
3816
3818
  for (let layer = 0; layer <= top; layer++) {
3817
3819
  const base = layerBase(adjacency, ordinalOfNumber[number], layer);
3818
3820
  const array = layerArray(adjacency, layer);
@@ -3830,18 +3832,33 @@ function serializeNumberedGraph(state, numberOfOrdinal, ordinalOfNumber) {
3830
3832
  values[countAt] = cursor - countAt - 1;
3831
3833
  }
3832
3834
  }
3835
+ return { levels, neighbours: littleEndianBytesOf(values.subarray(0, cursor)) };
3836
+ }
3837
+ function serializeNumberedGraphParts(state, numberOfOrdinal, ordinalOfNumber, partSize) {
3838
+ const slots = adjacencySlots(state.adjacency);
3839
+ const topLayers = new Int8Array(ordinalOfNumber.length).fill(-1);
3840
+ for (let number = 0; number < ordinalOfNumber.length; number++) {
3841
+ const ordinal = ordinalOfNumber[number];
3842
+ if (ordinal >= slots) continue;
3843
+ topLayers[number] = savedTopLayer(state, ordinal);
3844
+ }
3833
3845
  const entryOrdinal = entryPointOf(state);
3834
3846
  const entryNumber = entryOrdinal >= 0 && entryOrdinal < numberOfOrdinal.length ? numberOfOrdinal[entryOrdinal] : NO_NUMBER;
3835
3847
  const entryHoldsNode = entryNumber !== NO_NUMBER && topLayers[entryNumber] >= 0;
3836
- return {
3848
+ const header = {
3837
3849
  entryPoint: entryHoldsNode ? entryNumber : null,
3838
3850
  maxLayer: Math.max(0, Math.min(topLayerOf(state), MAX_LAYER_CAP)),
3839
3851
  m: state.M,
3840
3852
  efConstruction: state.efCons,
3841
- metric: state.buildMetric,
3842
- levels,
3843
- neighbours: littleEndianBytesOf(values.subarray(0, cursor))
3853
+ metric: state.buildMetric
3844
3854
  };
3855
+ const numbers = { numberOfOrdinal, ordinalOfNumber, topLayers };
3856
+ const parts = [];
3857
+ for (let start = 0; start < ordinalOfNumber.length; start += partSize) {
3858
+ const end = Math.min(start + partSize, ordinalOfNumber.length);
3859
+ parts.push({ ...header, ...serializePart(state, numbers, start, end) });
3860
+ }
3861
+ return parts;
3845
3862
  }
3846
3863
  function endsInsideAList() {
3847
3864
  throw new NarsilError(
@@ -3862,95 +3879,78 @@ function requireWholeNeighbourLists(graph) {
3862
3879
  }
3863
3880
  }
3864
3881
  }
3865
- function topLayerIn(graph, number) {
3866
- return number < graph.levels.length ? graph.levels[number] - 1 : -1;
3882
+ function topLayerInParts(graph, number) {
3883
+ const part = graph.parts[Math.floor(number / graph.partSize)];
3884
+ if (part === void 0) return -1;
3885
+ const at = number % graph.partSize;
3886
+ return at < part.levels.length ? part.levels[at] - 1 : -1;
3887
+ }
3888
+ function highestNodeOrdinal(graph, ordinalAt) {
3889
+ let highest = -1;
3890
+ for (let p = 0; p < graph.parts.length; p++) {
3891
+ const levels = graph.parts[p].levels;
3892
+ for (let at = 0; at < levels.length; at++) {
3893
+ const ordinal = ordinalAt(p * graph.partSize + at);
3894
+ if (levels[at] > 0 && ordinal > highest) highest = ordinal;
3895
+ }
3896
+ }
3897
+ return highest;
3867
3898
  }
3868
3899
  function deserializeNumberedGraph(state, graph, ordinalOfNumber) {
3869
- requireWholeNeighbourLists(graph);
3900
+ for (const part of graph.parts) requireWholeNeighbourLists(part);
3870
3901
  resetGraph(state);
3871
- const values = valuesOf(graph.neighbours);
3872
3902
  const ordinalAt = (number) => number < ordinalOfNumber.length ? ordinalOfNumber[number] : NO_ORDINAL;
3873
- let highestOrdinal = -1;
3874
- for (let number = 0; number < graph.levels.length; number++) {
3875
- if (graph.levels[number] > 0 && ordinalAt(number) > highestOrdinal) highestOrdinal = ordinalAt(number);
3876
- }
3903
+ const highestOrdinal = highestNodeOrdinal(graph, ordinalAt);
3877
3904
  if (highestOrdinal >= 0) ensureCapacity(state, highestOrdinal + 1);
3878
3905
  const kept = [];
3879
- let cursor = 0;
3880
3906
  let highestLayer = -1;
3881
3907
  let nodeOnHighestLayer = NO_ORDINAL;
3882
- for (let number = 0; number < graph.levels.length; number++) {
3883
- const top = graph.levels[number] - 1;
3884
- if (top < 0) continue;
3885
- const ordinal = ordinalAt(number);
3886
- const keptTop = Math.min(top, MAX_LAYER_CAP);
3887
- if (ordinal !== NO_ORDINAL) {
3888
- createNode(state.adjacency, ordinal, keptTop);
3889
- Atomics.add(state.header, GRAPH_NODE_COUNT, 1);
3890
- if (keptTop > highestLayer) {
3891
- highestLayer = keptTop;
3892
- nodeOnHighestLayer = ordinal;
3893
- }
3894
- }
3895
- for (let layer = 0; layer <= top; layer++) {
3896
- const count = values[cursor];
3897
- kept.length = 0;
3898
- const limit = maxConns(state, layer);
3899
- for (let i = 1; i <= count && kept.length < limit; i++) {
3900
- const neighbour = values[cursor + i];
3901
- const neighbourOrdinal = ordinalAt(neighbour);
3902
- if (neighbourOrdinal === NO_ORDINAL || neighbour === number || topLayerIn(graph, neighbour) < layer) continue;
3903
- if (!kept.includes(neighbourOrdinal)) kept.push(neighbourOrdinal);
3908
+ for (let p = 0; p < graph.parts.length; p++) {
3909
+ const part = graph.parts[p];
3910
+ const values = valuesOf(part.neighbours);
3911
+ let cursor = 0;
3912
+ for (let at = 0; at < part.levels.length; at++) {
3913
+ const number = p * graph.partSize + at;
3914
+ const top = part.levels[at] - 1;
3915
+ if (top < 0) continue;
3916
+ const ordinal = ordinalAt(number);
3917
+ const keptTop = Math.min(top, MAX_LAYER_CAP);
3918
+ if (ordinal !== NO_ORDINAL) {
3919
+ createNode(state.adjacency, ordinal, keptTop);
3920
+ Atomics.add(state.header, GRAPH_NODE_COUNT, 1);
3921
+ if (keptTop > highestLayer) {
3922
+ highestLayer = keptTop;
3923
+ nodeOnHighestLayer = ordinal;
3924
+ }
3904
3925
  }
3905
- cursor += count + 1;
3906
- if (ordinal !== NO_ORDINAL && layer <= keptTop) {
3907
- replaceNeighbors(state.adjacency, ordinal, layer, kept, kept.length);
3926
+ for (let layer = 0; layer <= top; layer++) {
3927
+ const count = values[cursor];
3928
+ kept.length = 0;
3929
+ const limit = maxConns(state, layer);
3930
+ for (let i = 1; i <= count && kept.length < limit; i++) {
3931
+ const neighbour = values[cursor + i];
3932
+ const neighbourOrdinal = ordinalAt(neighbour);
3933
+ if (neighbourOrdinal === NO_ORDINAL || neighbour === number) continue;
3934
+ if (topLayerInParts(graph, neighbour) < layer) continue;
3935
+ if (!kept.includes(neighbourOrdinal)) kept.push(neighbourOrdinal);
3936
+ }
3937
+ cursor += count + 1;
3938
+ if (ordinal !== NO_ORDINAL && layer <= keptTop) {
3939
+ replaceNeighbors(state.adjacency, ordinal, layer, kept, kept.length);
3940
+ }
3908
3941
  }
3909
3942
  }
3910
3943
  }
3911
- const entryOrdinal = graph.entryPoint === null ? NO_ORDINAL : ordinalAt(graph.entryPoint);
3912
- const entryTop = graph.entryPoint === null ? -1 : Math.min(topLayerIn(graph, graph.entryPoint), MAX_LAYER_CAP);
3944
+ const first = graph.parts[0];
3945
+ const entryPoint = first === void 0 ? null : first.entryPoint;
3946
+ const entryOrdinal = entryPoint === null ? NO_ORDINAL : ordinalAt(entryPoint);
3947
+ const entryTop = entryPoint === null ? -1 : Math.min(topLayerInParts(graph, entryPoint), MAX_LAYER_CAP);
3913
3948
  const entryIsUsable = entryOrdinal !== NO_ORDINAL && entryTop >= 0;
3914
3949
  Atomics.store(state.header, GRAPH_ENTRY_POINT, entryIsUsable ? entryOrdinal : nodeOnHighestLayer);
3915
3950
  Atomics.store(state.header, GRAPH_TOP_LAYER, entryIsUsable ? entryTop : highestLayer);
3916
3951
  }
3917
3952
 
3918
3953
  // src/vector/hnsw/persistence.ts
3919
- function serializeGraph(state, store) {
3920
- const nodeArray = [];
3921
- const slots = adjacencySlots(state.adjacency);
3922
- for (let ord = 0; ord < slots; ord++) {
3923
- const maxLayer = nodeMaxLayer(state, ord);
3924
- if (maxLayer === -1) continue;
3925
- if (isTombstoned(state, ord)) continue;
3926
- const docId = store.docIdForOrdinal(ord);
3927
- if (docId === void 0) continue;
3928
- const layerConns = [];
3929
- for (let layer = 0; layer <= maxLayer; layer++) {
3930
- const liveNeighbors = [];
3931
- for (const neighborOrd of collectNeighbors(state.adjacency, ord, layer)) {
3932
- if (isTombstoned(state, neighborOrd)) continue;
3933
- const neighborDoc = store.docIdForOrdinal(neighborOrd);
3934
- if (neighborDoc === void 0) continue;
3935
- liveNeighbors.push(neighborDoc);
3936
- }
3937
- if (liveNeighbors.length > 0) {
3938
- layerConns.push([layer, liveNeighbors]);
3939
- }
3940
- }
3941
- nodeArray.push([docId, maxLayer, layerConns]);
3942
- }
3943
- const entryOrd = entryPointOf(state);
3944
- const entryPoint = entryOrd === -1 ? null : store.docIdForOrdinal(entryOrd) ?? null;
3945
- return {
3946
- entryPoint,
3947
- maxLayer: topLayerOf(state),
3948
- m: state.M,
3949
- efConstruction: state.efCons,
3950
- metric: state.buildMetric,
3951
- nodes: nodeArray
3952
- };
3953
- }
3954
3954
  function resolveNodes(state, store, data) {
3955
3955
  const nodes = [];
3956
3956
  let maxOrd = -1;
@@ -4186,13 +4186,12 @@ function createHNSWIndex(dimension, store, config, quantizer, handles) {
4186
4186
  compactTombstones: () => exclusively(() => compactTombstones(state)),
4187
4187
  rebuild: () => exclusively(() => rebuild(state)),
4188
4188
  exclusively,
4189
- serialize: () => serializeGraph(state, store),
4190
4189
  deserialize: (data) => exclusively(() => deserializeGraph(state, store, data)),
4191
- serializeNumbered: (numberOfOrdinal, ordinalOfNumber) => serializeNumberedGraph(state, numberOfOrdinal, ordinalOfNumber),
4190
+ serializeNumbered: (numberOfOrdinal, ordinalOfNumber, partSize) => serializeNumberedGraphParts(state, numberOfOrdinal, ordinalOfNumber, partSize),
4192
4191
  deserializeNumbered: (graph, ordinalOfNumber) => exclusively(() => deserializeNumberedGraph(state, graph, ordinalOfNumber)),
4193
4192
  exportSnapshot: () => exportSnapshot(state),
4194
4193
  restoreSnapshot: (snapshot) => exclusively(() => restoreSnapshot(state, snapshot))
4195
4194
  };
4196
4195
  }
4197
4196
 
4198
- export { DISK_STORAGE_BLOCK_BYTES, GRAPH_BUILD_CHUNKS_IN_FLIGHT_PER_WORKER, GRAPH_BUILD_DISPATCH_CHUNK, MAX_WORKER_COPIES, SCRATCH_SLOTS_PER_THREAD_POOL, STORE_CALIBRATION_GENERATION, VECTOR_INSERT_TIMEOUT_MS, VECTOR_SCRATCH_SLOTS, VECTOR_SEARCH_LOAD_TIMEOUT_MS, VECTOR_SEARCH_TIMEOUT_MS, addToOrdinalFilter, cosineSimilarityWithMagnitudes, createHNSWIndex, createOrdinalFilter, createOsqQuantizer, createVectorStore, dotProduct, entryForOrd, euclideanDistance, isSimdAvailable, isTombstoned, magnitude, nativeScoresOf, nativeStoreScratchBytes, openSharedVectorField, ordinalFilterHas, ordinalFilterValues, osqBitsOf, osqRecordBytes, releaseLocksHeldBy, removeFromOrdinalFilter, requireWholeNeighbourLists, sharedMemoryAvailable, toDistance, toScore };
4197
+ export { DISK_STORAGE_BLOCK_BYTES, GRAPH_BUILD_CHUNKS_IN_FLIGHT_PER_WORKER, GRAPH_BUILD_DISPATCH_CHUNK, MAX_WORKER_COPIES, SCRATCH_SLOTS_PER_THREAD_POOL, STORE_CALIBRATION_GENERATION, VECTOR_INSERT_TIMEOUT_MS, VECTOR_SCRATCH_SLOTS, VECTOR_SEARCH_LOAD_TIMEOUT_MS, VECTOR_SEARCH_TIMEOUT_MS, addToOrdinalFilter, cosineSimilarityWithMagnitudes, createHNSWIndex, createOrdinalFilter, createOsqQuantizer, createVectorStore, dotProduct, entryForOrd, euclideanDistance, isSimdAvailable, isTombstoned, magnitude, nativeScoresOf, nativeStoreScratchBytes, openSharedVectorField, ordinalFilterHas, ordinalFilterValues, osqBitsOf, osqRecordBytes, releaseLocksHeldBy, removeFromOrdinalFilter, requireWholeNeighbourLists, sharedMemoryAvailable, toDistance, toScore, topLayerInParts };