@delali/narsil 0.3.1-next.71 → 0.3.1-next.73

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 (69) 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-Dt-7B4Wz.d.ts → capabilities-CYeAWYxQ.d.ts} +19 -4
  5. package/dist/{chunk-3JGWULDF.mjs → chunk-2WLD2NFW.mjs} +5 -5
  6. package/dist/{chunk-LMO5TJUS.mjs → chunk-3YKWSO54.mjs} +642 -196
  7. package/dist/{chunk-HWQLTLNG.mjs → chunk-4MQ5F4IL.mjs} +1 -1
  8. package/dist/{chunk-FVLQ63B7.mjs → chunk-AFKH4ZK3.mjs} +30 -6
  9. package/dist/{chunk-653BNMAF.mjs → chunk-AK3BLGCI.mjs} +195 -25
  10. package/dist/{chunk-IOR2K26O.mjs → chunk-CVFAFZSN.mjs} +8000 -4391
  11. package/dist/{chunk-USKFU7J5.mjs → chunk-I64LXKO3.mjs} +1 -1
  12. package/dist/{chunk-OI42GWLR.mjs → chunk-LJYTW7GT.mjs} +57 -11
  13. package/dist/{chunk-2STVUTNA.mjs → chunk-LPBWCHBE.mjs} +194 -13
  14. package/dist/{chunk-YNVIUGF5.mjs → chunk-PU43WDY4.mjs} +4 -2
  15. package/dist/{chunk-M76V3OGJ.mjs → chunk-THMSFOJO.mjs} +1289 -1405
  16. package/dist/{chunk-YJDQ7NLA.mjs → chunk-WVDFG45M.mjs} +93 -95
  17. package/dist/chunk-XW25X6AU.mjs +614 -0
  18. package/dist/chunk-ZKRV5M2P.mjs +75 -0
  19. package/dist/{chunk-P4DSQ6M4.mjs → chunk-ZULSEZCE.mjs} +68 -28
  20. package/dist/{client-COek5GXK.d.ts → client-D698E5ge.d.ts} +77 -26
  21. package/dist/client.d.ts +9 -9
  22. package/dist/client.mjs +45 -22
  23. package/dist/{config-rBpbgTw8.d.ts → config-Bv8etwUZ.d.ts} +11 -4
  24. package/dist/{config-Dfanyqqt.d.ts → config-QXU3HMpq.d.ts} +26 -2
  25. package/dist/direct-executor-5SRDFZAS.mjs +13 -0
  26. package/dist/distribution/coordinator/etcd.d.ts +29 -16
  27. package/dist/distribution/coordinator/etcd.mjs +163 -76
  28. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  29. package/dist/distribution/coordinator/in-memory.mjs +1 -1
  30. package/dist/distribution/transport/grpc.d.ts +2 -2
  31. package/dist/distribution/transport/grpc.mjs +22 -2
  32. package/dist/distribution/transport/in-memory.d.ts +2 -2
  33. package/dist/distribution/transport/in-memory.mjs +6 -3
  34. package/dist/distribution/transport/tcp.d.ts +2 -2
  35. package/dist/distribution/transport/tcp.mjs +40 -16
  36. package/dist/distribution.d.ts +9 -9
  37. package/dist/distribution.mjs +682 -388
  38. package/dist/embeddings/openai.mjs +1 -1
  39. package/dist/{engine-igk0aMpQ.d.ts → engine-D_OzKZ-6.d.ts} +97 -188
  40. package/dist/index.browser.mjs +11671 -7427
  41. package/dist/index.d.ts +27 -8
  42. package/dist/index.mjs +13 -14
  43. package/dist/invalidation/filesystem.mjs +1 -1
  44. package/dist/{memory-C7UMCMH3.d.ts → memory-B4jMmygw.d.ts} +279 -27
  45. package/dist/pattern-Cledccbl.d.ts +18 -0
  46. package/dist/pattern.d.ts +22 -0
  47. package/dist/pattern.mjs +7 -0
  48. package/dist/persistence/durability/checkpoint-worker.d.ts +41 -17
  49. package/dist/persistence/durability/checkpoint-worker.mjs +24 -16
  50. package/dist/react.d.ts +7 -7
  51. package/dist/react.mjs +1 -1
  52. package/dist/{schema-Bgzr4g7b.d.ts → schema-BBOCdhND.d.ts} +9 -7
  53. package/dist/server.d.ts +11 -11
  54. package/dist/server.mjs +213 -34
  55. package/dist/{tasks-BZt_zes6.d.ts → tasks-DpgCpW1x.d.ts} +3 -3
  56. package/dist/{thread-JEVAYETR.mjs → thread-3LNVA43V.mjs} +94 -27
  57. package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
  58. package/dist/{types-gOUqhs2F.d.ts → types-BiUmaiWT.d.ts} +1 -1
  59. package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
  60. package/dist/vector/search-worker.d.ts +1 -1
  61. package/dist/vector/search-worker.mjs +4 -4
  62. package/dist/workers/entry.mjs +4 -4
  63. package/package.json +15 -7
  64. package/dist/chunk-3L4U6EJQ.mjs +0 -49
  65. package/dist/chunk-GIVAGD3Q.mjs +0 -25
  66. package/dist/chunk-IFBKLBGB.mjs +0 -7
  67. package/dist/direct-executor-GYJEKNV5.mjs +0 -12
  68. package/dist/serialization/crc32-worker.d.ts +0 -20
  69. package/dist/serialization/crc32-worker.mjs +0 -62
package/dist/server.mjs CHANGED
@@ -1,21 +1,22 @@
1
- import { DEFAULT_MAX_RETAINED_TASKS, loadUWebSockets, resolveCors, authorizerFor, createSharedGateBuffer, gateSlotOfWorker, createRouteRunner, corsHeaderReader, createSharedRequestGate, MAIN_THREAD_GATE_SLOT, registerServerRoutes, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_BODY_BYTES, RUNNING_TASK_TTL_MS, DEFAULT_TASK_PAGE_SIZE, TERMINAL_TASK_TTL_MS, TASK_PROGRESS_WRITE_INTERVAL_MS, serializeNarsilError, createVersionHandler, acceptorOf, createDocumentReadHandlers, sendError, respondError, respondJson, MAX_INDEX_TOUCH_INTERVAL_MS, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, clearGateSlot, parseJson, validateCreateIndex, rejectInvalid, validateBatch, serializeBatchResult, validateDocumentBody, validateSuggest, validateQuery, parseTaskListQuery, parseJsonOptional, validateRebalance, sendBinary, IMPORT_FLUSHES_IN_FLIGHT, badRequest, SERVER_CAPABILITIES, toHttpError, MIN_UNBOUNDED_SERVER_LIMIT, MIN_COUNTED_SERVER_LIMIT } from './chunk-2STVUTNA.mjs';
2
- export { ASYNC_IMPORT_CAPABILITY, INDEX_LIFECYCLE_CAPABILITY, REBUILD_ANALYSIS_CAPABILITY, SERVER_CAPABILITIES, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, httpStatusForNarsilError } from './chunk-2STVUTNA.mjs';
3
- import { validateIndexName } from './chunk-3JGWULDF.mjs';
4
- import { engineCoreOf, providedDocId, resolveVectorText } from './chunk-FVLQ63B7.mjs';
5
- import { detectRuntime } from './chunk-HWQLTLNG.mjs';
6
- import './chunk-GIVAGD3Q.mjs';
7
- import './chunk-653BNMAF.mjs';
1
+ import { DEFAULT_MAX_RETAINED_TASKS, loadUWebSockets, resolveCors, authorizerFor, createSharedGateBuffer, gateSlotOfWorker, createRouteRunner, corsHeaderReader, createSharedRequestGate, MAIN_THREAD_GATE_SLOT, registerServerRoutes, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_RESTORE_BYTES, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_BODY_BYTES, RUNNING_TASK_TTL_MS, DEFAULT_TASK_PAGE_SIZE, TERMINAL_TASK_TTL_MS, TASK_PROGRESS_WRITE_INTERVAL_MS, serializeNarsilError, createVersionHandler, acceptorOf, createDocumentReadHandlers, sendError, respondError, respondJson, MAX_INDEX_TOUCH_INTERVAL_MS, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, clearGateSlot, parseJson, validateCreateIndex, rejectInvalid, validateBatch, serializeBatchResult, validateDocumentBody, validateSuggest, validateQuery, parseTaskListQuery, parseJsonOptional, validateRebalance, sendStream, SNAPSHOT_ARCHIVE_CONTENT_TYPE, DOCUMENT_EXPORT_CONTENT_TYPE, IMPORT_FLUSHES_IN_FLIGHT, badRequest, SERVER_CAPABILITIES, toHttpError, RELAY_STREAM_CHUNKS_IN_FLIGHT, MIN_UNBOUNDED_SERVER_LIMIT, MIN_COUNTED_SERVER_LIMIT } from './chunk-LPBWCHBE.mjs';
2
+ export { ASYNC_IMPORT_CAPABILITY, DOCUMENT_EXPORT_CAPABILITY, INDEX_LIFECYCLE_CAPABILITY, REBUILD_ANALYSIS_CAPABILITY, SERVER_CAPABILITIES, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, httpStatusForNarsilError } from './chunk-LPBWCHBE.mjs';
3
+ import { validateIndexName } from './chunk-2WLD2NFW.mjs';
4
+ import { engineCoreOf, providedDocId, resolveVectorText } from './chunk-AFKH4ZK3.mjs';
5
+ import { detectRuntime } from './chunk-4MQ5F4IL.mjs';
6
+ import './chunk-AK3BLGCI.mjs';
8
7
  import './chunk-TGP5M5R7.mjs';
9
- import './chunk-YNVIUGF5.mjs';
8
+ import './chunk-PU43WDY4.mjs';
10
9
  import './chunk-U2BKYEC7.mjs';
11
- import './chunk-OI42GWLR.mjs';
10
+ import './chunk-LJYTW7GT.mjs';
12
11
  import { startIdleHeapCleanup } from './chunk-Y3L5MDRV.mjs';
13
- import { NarsilError, ErrorCodes, ServerErrorCodes, isNarsilError } from './chunk-USKFU7J5.mjs';
14
- export { ClientErrorCodes, ErrorCodes, NarsilError, ServerErrorCodes, isNarsilError } from './chunk-USKFU7J5.mjs';
12
+ import { NarsilError, ErrorCodes, ServerErrorCodes, isNarsilError } from './chunk-I64LXKO3.mjs';
13
+ export { ClientErrorCodes, ErrorCodes, NarsilError, ServerErrorCodes, isNarsilError } from './chunk-I64LXKO3.mjs';
15
14
  import { randomUUID } from 'crypto';
15
+ import { createReadStream } from 'fs';
16
+ import { rm } from 'fs/promises';
17
+ import path from 'path';
16
18
  import { MessageChannel } from 'worker_threads';
17
19
 
18
- // src/server/handlers/admin.ts
19
20
  function createAdminHandlers(deps) {
20
21
  const { engine, tasks, limits } = deps;
21
22
  async function checkpoint(ctx) {
@@ -28,9 +29,12 @@ function createAdminHandlers(deps) {
28
29
  }
29
30
  async function snapshot(ctx) {
30
31
  try {
31
- const bytes = await engine.snapshot(ctx.params[0]);
32
- if (ctx.abort.aborted) return;
33
- sendBinary(ctx.res, bytes, ctx.abort);
32
+ const archive = await engine.snapshot(ctx.params[0]);
33
+ if (ctx.abort.aborted) {
34
+ await archive.cancel();
35
+ return;
36
+ }
37
+ await sendStream(ctx.res, ctx.abort, archive, [["Content-Type", SNAPSHOT_ARCHIVE_CONTENT_TYPE]]);
34
38
  } catch (err) {
35
39
  respondError(ctx, err);
36
40
  }
@@ -118,15 +122,26 @@ function createAdminHandlers(deps) {
118
122
  respondJson(ctx, record, 202);
119
123
  }
120
124
  async function restore(ctx) {
121
- const raw = ctx.rawBody;
122
- if (!raw || raw.length === 0) {
125
+ const spooled = ctx.spooledBody;
126
+ if (spooled === null || spooled.bytes === 0) {
127
+ await spooled?.remove();
123
128
  sendError(ctx.res, 400, ServerErrorCodes.EMPTY_BODY, "Request body is empty");
124
129
  return;
125
130
  }
126
131
  const name = ctx.params[0];
127
- const bytes = new Uint8Array(raw);
128
- const record = await tasks.start("restore", name, () => engine.restore(name, bytes));
129
- respondJson(ctx, record, 202);
132
+ try {
133
+ const record = await tasks.start("restore", name, async () => {
134
+ try {
135
+ await engine.restore(name, createReadStream(spooled.path));
136
+ } finally {
137
+ await spooled.remove();
138
+ }
139
+ });
140
+ respondJson(ctx, record, 202);
141
+ } catch (err) {
142
+ await spooled.remove();
143
+ respondError(ctx, err);
144
+ }
130
145
  }
131
146
  async function getTask(ctx) {
132
147
  const record = await tasks.get(ctx.params[0]);
@@ -270,6 +285,54 @@ function createClusterHandlers(deps) {
270
285
  }
271
286
  return { topology, allocation };
272
287
  }
288
+ function importResultOf(result, maxErrors) {
289
+ const errors = result.refused.slice(0, maxErrors);
290
+ return {
291
+ indexed: result.imported,
292
+ failed: result.refusedCount,
293
+ errors,
294
+ errorsTruncated: result.refusedCount > errors.length
295
+ };
296
+ }
297
+ function createDocumentExportHandlers(deps) {
298
+ const { engine, tasks, limits } = deps;
299
+ async function exportDocuments(ctx) {
300
+ try {
301
+ const lines = await engine.exportDocuments(ctx.params[0]);
302
+ if (ctx.abort.aborted) {
303
+ await lines.cancel();
304
+ return;
305
+ }
306
+ await sendStream(ctx.res, ctx.abort, lines, [["Content-Type", DOCUMENT_EXPORT_CONTENT_TYPE]]);
307
+ } catch (err) {
308
+ respondError(ctx, err);
309
+ }
310
+ }
311
+ async function importExport(ctx) {
312
+ const spooled = ctx.spooledBody;
313
+ if (spooled === null || spooled.bytes === 0) {
314
+ await spooled?.remove();
315
+ sendError(ctx.res, 400, ServerErrorCodes.EMPTY_BODY, "Request body is empty");
316
+ return;
317
+ }
318
+ const name = ctx.params[0];
319
+ try {
320
+ const record = await tasks.start("importExport", name, async (context) => {
321
+ try {
322
+ const result = await engine.importDocumentExport(name, createReadStream(spooled.path));
323
+ context.reportResult(importResultOf(result, limits.maxImportErrors));
324
+ } finally {
325
+ await spooled.remove();
326
+ }
327
+ });
328
+ respondJson(ctx, record, 202);
329
+ } catch (err) {
330
+ await spooled.remove();
331
+ respondError(ctx, err);
332
+ }
333
+ }
334
+ return { exportDocuments, importExport };
335
+ }
273
336
 
274
337
  // src/server/handlers/documents.ts
275
338
  function createDocumentHandlers(deps) {
@@ -757,6 +820,7 @@ var ENGINE_METHOD_SAMPLE = ["createIndex", "listIndexes", "insert", "query", "ge
757
820
  var LIMITS_UNBOUNDED_AT_ZERO = [
758
821
  "maxBodyBytes",
759
822
  "maxImportBytes",
823
+ "maxRestoreBytes",
760
824
  "maxLineBytes",
761
825
  "maxImportErrors",
762
826
  "maxConcurrentRequests",
@@ -808,10 +872,32 @@ function bodyBytes(body) {
808
872
  if (typeof body === "string") return Buffer.from(body, "utf8");
809
873
  return body;
810
874
  }
811
- function createCapturingResponse(remoteAddress) {
875
+ function createRelayAbort() {
876
+ const listeners = [];
877
+ let aborted = false;
878
+ return {
879
+ get aborted() {
880
+ return aborted;
881
+ },
882
+ onAbort(fn) {
883
+ if (aborted) fn();
884
+ else listeners.push(fn);
885
+ },
886
+ trigger() {
887
+ if (aborted) return;
888
+ aborted = true;
889
+ for (const fn of listeners) fn();
890
+ }
891
+ };
892
+ }
893
+ function createCapturingResponse(remoteAddress, requestId, sendPart) {
812
894
  let status = DEFAULT_STATUS;
813
895
  const headers = [];
814
896
  let finished = false;
897
+ let streaming = false;
898
+ let inFlight = 0;
899
+ let writable = null;
900
+ const abort = createRelayAbort();
815
901
  let resolveDone = () => void 0;
816
902
  const done = new Promise((resolve) => {
817
903
  resolveDone = resolve;
@@ -819,25 +905,57 @@ function createCapturingResponse(remoteAddress) {
819
905
  function finish(body) {
820
906
  if (finished) return;
821
907
  finished = true;
908
+ if (streaming) {
909
+ sendPart({ type: "stream-end", requestId, failed: false }, []);
910
+ resolveDone(null);
911
+ return;
912
+ }
822
913
  resolveDone({ status, headers, body });
823
914
  }
915
+ function startStreaming() {
916
+ if (streaming) return;
917
+ streaming = true;
918
+ sendPart({ type: "stream-start", requestId, status, headers }, []);
919
+ }
824
920
  const sink = {
825
921
  cork(callback) {
826
922
  callback();
827
923
  return sink;
828
924
  },
829
925
  writeStatus(line) {
830
- if (!finished) status = statusOf(line);
926
+ if (!finished && !streaming) status = statusOf(line);
831
927
  return sink;
832
928
  },
833
929
  writeHeader(key, value) {
834
- if (!finished) headers.push([key, value]);
930
+ if (!finished && !streaming) headers.push([key, value]);
835
931
  return sink;
836
932
  },
837
933
  end(body) {
838
934
  finish(bodyBytes(body));
839
935
  return sink;
840
936
  },
937
+ write(chunk) {
938
+ if (finished || abort.aborted) return true;
939
+ startStreaming();
940
+ const copy = chunk.slice();
941
+ inFlight += 1;
942
+ sendPart({ type: "stream-chunk", requestId, chunk: copy }, [copy.buffer]);
943
+ return inFlight < RELAY_STREAM_CHUNKS_IN_FLIGHT;
944
+ },
945
+ pause: () => void 0,
946
+ resume: () => void 0,
947
+ close() {
948
+ if (finished) return sink;
949
+ finished = true;
950
+ if (streaming) {
951
+ sendPart({ type: "stream-end", requestId, failed: true }, []);
952
+ resolveDone(null);
953
+ } else {
954
+ status = NO_RESPONSE_STATUS;
955
+ resolveDone({ status, headers, body: new Uint8Array(0) });
956
+ }
957
+ return sink;
958
+ },
841
959
  endWithoutBody() {
842
960
  finish(new Uint8Array(0));
843
961
  return sink;
@@ -847,16 +965,45 @@ function createCapturingResponse(remoteAddress) {
847
965
  return [true, true];
848
966
  },
849
967
  getWriteOffset: () => 0,
850
- onWritable: () => sink,
968
+ onWritable(handler) {
969
+ writable = handler;
970
+ return sink;
971
+ },
851
972
  onAborted: () => sink,
852
973
  onData: () => sink,
853
974
  getRemoteAddressAsText: () => new TextEncoder().encode(remoteAddress).buffer
854
975
  };
855
976
  return {
856
977
  sink,
978
+ abort,
857
979
  done,
980
+ credit() {
981
+ inFlight = Math.max(0, inFlight - 1);
982
+ if (inFlight >= RELAY_STREAM_CHUNKS_IN_FLIGHT || writable === null) return;
983
+ const handler = writable;
984
+ writable = null;
985
+ handler(0);
986
+ },
987
+ cancel() {
988
+ abort.trigger();
989
+ if (writable !== null) {
990
+ const handler = writable;
991
+ writable = null;
992
+ handler(0);
993
+ }
994
+ if (!finished) {
995
+ finished = true;
996
+ resolveDone(null);
997
+ }
998
+ },
858
999
  finishUnanswered() {
859
1000
  if (finished) return;
1001
+ if (streaming) {
1002
+ finished = true;
1003
+ sendPart({ type: "stream-end", requestId, failed: true }, []);
1004
+ resolveDone(null);
1005
+ return;
1006
+ }
860
1007
  status = NO_RESPONSE_STATUS;
861
1008
  finish(new Uint8Array(0));
862
1009
  }
@@ -867,22 +1014,36 @@ function toRelayedResponse(requestId, captured) {
867
1014
  }
868
1015
 
869
1016
  // src/server/request-threads/host.ts
870
- var NO_ABORT = { aborted: false, onAbort: () => void 0 };
1017
+ var RELAYED_TYPES = /* @__PURE__ */ new Set(["request", "authorize", "embed", "stream-credit", "stream-cancel"]);
871
1018
  function isRelayRequest(message) {
872
1019
  if (typeof message !== "object" || message === null) return false;
873
1020
  const { type, requestId } = message;
874
1021
  if (type === "touch") return true;
875
- return typeof requestId === "number" && (type === "request" || type === "authorize" || type === "embed");
1022
+ return typeof requestId === "number" && typeof type === "string" && RELAYED_TYPES.has(type);
876
1023
  }
877
- async function answerRequest(handlers, request) {
878
- const captured = createCapturingResponse(request.hookContext?.remoteAddress ?? "");
1024
+ function spooledBodyOf(relayed) {
1025
+ if (relayed === null) return null;
1026
+ return {
1027
+ path: relayed.path,
1028
+ bytes: relayed.bytes,
1029
+ remove: () => rm(path.dirname(relayed.path), { recursive: true, force: true })
1030
+ };
1031
+ }
1032
+ async function answerRequest(handlers, request, port, streams) {
1033
+ const captured = createCapturingResponse(
1034
+ request.hookContext?.remoteAddress ?? "",
1035
+ request.requestId,
1036
+ (part, moved) => port.postMessage(part, moved)
1037
+ );
1038
+ streams.set(request.requestId, captured);
879
1039
  const ctx = {
880
1040
  res: captured.sink,
881
1041
  params: request.params,
882
1042
  query: new URLSearchParams(request.query),
883
1043
  contentType: request.contentType,
884
1044
  rawBody: request.rawBody === null ? null : Buffer.from(request.rawBody.buffer, request.rawBody.byteOffset, request.rawBody.byteLength),
885
- abort: NO_ABORT,
1045
+ spooledBody: spooledBodyOf(request.spooledBody),
1046
+ abort: captured.abort,
886
1047
  hookContext: request.hookContext
887
1048
  };
888
1049
  try {
@@ -891,7 +1052,9 @@ async function answerRequest(handlers, request) {
891
1052
  sendError(ctx.res, 500, ServerErrorCodes.INTERNAL_ERROR, "An unexpected error occurred");
892
1053
  }
893
1054
  captured.finishUnanswered();
894
- return toRelayedResponse(request.requestId, await captured.done);
1055
+ const answered = await captured.done;
1056
+ streams.delete(request.requestId);
1057
+ return answered === null ? null : toRelayedResponse(request.requestId, answered);
895
1058
  }
896
1059
  async function answerAuthorize(authorize, request) {
897
1060
  if (authorize === void 0) {
@@ -942,14 +1105,25 @@ async function startRequestThreads(options) {
942
1105
  const threads = /* @__PURE__ */ new Map();
943
1106
  let stopped = false;
944
1107
  function relayFrom(port) {
1108
+ const streams = /* @__PURE__ */ new Map();
945
1109
  port.on("message", (message) => {
946
1110
  if (!isRelayRequest(message)) return;
947
1111
  if (message.type === "touch") {
948
1112
  core.orchestrator.noteAccess(message.indexName);
949
1113
  return;
950
1114
  }
951
- const reply = message.type === "request" ? answerRequest(handlers, message) : message.type === "authorize" ? answerAuthorize(authorize, message) : answerEmbed(core, message);
952
- void reply.then((answer) => port.postMessage(answer));
1115
+ if (message.type === "stream-credit") {
1116
+ streams.get(message.requestId)?.credit();
1117
+ return;
1118
+ }
1119
+ if (message.type === "stream-cancel") {
1120
+ streams.get(message.requestId)?.cancel();
1121
+ return;
1122
+ }
1123
+ const reply = message.type === "request" ? answerRequest(handlers, message, port, streams) : message.type === "authorize" ? answerAuthorize(authorize, message) : answerEmbed(core, message);
1124
+ void reply.then((answer) => {
1125
+ if (answer !== null) port.postMessage(answer);
1126
+ });
953
1127
  });
954
1128
  }
955
1129
  async function stopThread(workerId, thread) {
@@ -982,7 +1156,8 @@ async function startRequestThreads(options) {
982
1156
  limits: options.limits,
983
1157
  build: options.build,
984
1158
  authorizes: authorize !== void 0,
985
- searchHooks
1159
+ searchHooks,
1160
+ patternWorkCap: options.core.patternWorkCap
986
1161
  }
987
1162
  },
988
1163
  [channel.port1]
@@ -1253,6 +1428,7 @@ function resolveLimits(limits) {
1253
1428
  return {
1254
1429
  maxBodyBytes: limits?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,
1255
1430
  maxImportBytes: limits?.maxImportBytes ?? DEFAULT_MAX_IMPORT_BYTES,
1431
+ maxRestoreBytes: limits?.maxRestoreBytes ?? DEFAULT_MAX_RESTORE_BYTES,
1256
1432
  maxLineBytes: limits?.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES,
1257
1433
  importBatchSize: limits?.importBatchSize ?? DEFAULT_IMPORT_BATCH_SIZE,
1258
1434
  maxConcurrentRequests: limits?.maxConcurrentRequests ?? DEFAULT_MAX_CONCURRENT_REQUESTS,
@@ -1286,6 +1462,7 @@ function mainThreadHandlers(deps) {
1286
1462
  const doc = createDocumentHandlers(deps);
1287
1463
  const search = createSearchHandlers(deps);
1288
1464
  const admin = createAdminHandlers(deps);
1465
+ const documentExport = createDocumentExportHandlers(deps);
1289
1466
  const health = createHealthHandlers(deps);
1290
1467
  const cluster = createClusterHandlers(deps);
1291
1468
  const version = createVersionHandler(deps);
@@ -1332,7 +1509,9 @@ function mainThreadHandlers(deps) {
1332
1509
  optimize: admin.optimize,
1333
1510
  rebalance: admin.rebalance,
1334
1511
  partitionConfig: admin.partitionConfig,
1335
- restore: admin.restore
1512
+ restore: admin.restore,
1513
+ exportDocuments: documentExport.exportDocuments,
1514
+ importExport: documentExport.importExport
1336
1515
  };
1337
1516
  }
1338
1517
  var NarsilHttpServer = class {
@@ -1,4 +1,4 @@
1
- import { M as MemoryStats } from './memory-C7UMCMH3.js';
1
+ import { M as MemoryStats } from './memory-B4jMmygw.js';
2
2
 
3
3
  /** Declarative index configuration accepted over HTTP. Function-valued engine
4
4
  * options (custom tokenizer, stopWords-as-function, group reducer, embedding
@@ -142,7 +142,7 @@ interface MemoryStatsResponse extends MemoryStats {
142
142
  *
143
143
  * @public
144
144
  */
145
- type TaskType = 'optimizeVectors' | 'rebalance' | 'restore' | 'import' | 'rebuildAnalysis';
145
+ type TaskType = 'optimizeVectors' | 'rebalance' | 'restore' | 'import' | 'importExport' | 'rebuildAnalysis';
146
146
  /**
147
147
  * Where a long-running operation stands.
148
148
  *
@@ -207,7 +207,7 @@ interface TaskRecord {
207
207
  cancelRequestedAt?: number;
208
208
  /** How far the work has gone, which an import reports and other task types omit. */
209
209
  progress?: TaskProgress;
210
- /** What the work produced, which an import reports once it finishes. */
210
+ /** What the work produced, which an `import` or `importExport` task reports once it finishes. */
211
211
  result?: ImportResult;
212
212
  /** This says why the task failed, and a failed task alone carries it. */
213
213
  error?: {
@@ -1,12 +1,12 @@
1
- import { loadUWebSockets, childAppOf, createRouteRunner, corsHeaderReader, createSharedRequestGate, registerServerRoutes, sendRelayed, createDocumentReadHandlers, respondJson, SERVER_CAPABILITIES, versionReport, sendError, serverRoutes, parseJson, validateSuggest, rejectInvalid, respondError, validateQuery } from './chunk-2STVUTNA.mjs';
2
- import { executeListDocuments, executeSuggest, executePreflight, queryBindingOf, executeQuery } from './chunk-P4DSQ6M4.mjs';
3
- import './chunk-GIVAGD3Q.mjs';
4
- import { resolveProjection, projectionKeepsField, setNestedValue } from './chunk-LMO5TJUS.mjs';
5
- import { extractVectorFieldsFromSchema } from './chunk-653BNMAF.mjs';
1
+ import { loadUWebSockets, childAppOf, createRouteRunner, corsHeaderReader, createSharedRequestGate, registerServerRoutes, sendRelayed, createDocumentReadHandlers, respondJson, SERVER_CAPABILITIES, versionReport, statusLine, sendError, serverRoutes, parseJson, validateSuggest, rejectInvalid, respondError, validateQuery } from './chunk-LPBWCHBE.mjs';
2
+ import { executeListDocuments, executeSuggest, executePreflight, queryBindingOf, executeQuery } from './chunk-ZULSEZCE.mjs';
3
+ import { resolveProjection, projectionKeepsField, setNestedValue } from './chunk-3YKWSO54.mjs';
4
+ import { extractVectorFieldsFromSchema } from './chunk-AK3BLGCI.mjs';
6
5
  import './chunk-ZJ4CSAB2.mjs';
7
- import './chunk-YNVIUGF5.mjs';
8
- import './chunk-OI42GWLR.mjs';
9
- import { ServerErrorCodes, NarsilError, ErrorCodes } from './chunk-USKFU7J5.mjs';
6
+ import './chunk-PU43WDY4.mjs';
7
+ import './chunk-ZKRV5M2P.mjs';
8
+ import './chunk-LJYTW7GT.mjs';
9
+ import { ServerErrorCodes, NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
10
10
 
11
11
  // src/server/request-threads/held-vectors.ts
12
12
  function managerWithHeldVectors(manager, fieldPaths, vectorOf) {
@@ -28,7 +28,7 @@ function managerWithHeldVectors(manager, fieldPaths, vectorOf) {
28
28
 
29
29
  // src/server/request-threads/read-engine.ts
30
30
  function createThreadReadEngine(options) {
31
- const { executor, relay, searchHooks } = options;
31
+ const { executor, relay, searchHooks, patternWorkCap } = options;
32
32
  function vectorFieldsOf(context) {
33
33
  return [...extractVectorFieldsFromSchema(context.config.schema).keys()];
34
34
  }
@@ -110,7 +110,8 @@ function createThreadReadEngine(options) {
110
110
  config: context.config,
111
111
  indexName,
112
112
  cursorBinding: queryBindingOf(params),
113
- vectorSearchers: context.vectorSearchers
113
+ vectorSearchers: context.vectorSearchers,
114
+ patternWorkCap
114
115
  });
115
116
  if (context.analysisStale) result.analysisStale = true;
116
117
  return result;
@@ -123,7 +124,8 @@ function createThreadReadEngine(options) {
123
124
  config: context.config,
124
125
  indexName,
125
126
  cursorBinding: queryBindingOf(params),
126
- vectorSearchers: context.vectorSearchers
127
+ vectorSearchers: context.vectorSearchers,
128
+ patternWorkCap
127
129
  });
128
130
  if (context.analysisStale) result.analysisStale = true;
129
131
  return result;
@@ -157,7 +159,8 @@ function createThreadReadEngine(options) {
157
159
  return executeListDocuments(params ?? {}, {
158
160
  manager,
159
161
  schema: context.config.schema,
160
- strict: context.config.strict
162
+ strict: context.config.strict,
163
+ patternWorkCap
161
164
  });
162
165
  }
163
166
  };
@@ -169,6 +172,46 @@ function isRelayReply(message) {
169
172
  const { type, requestId } = message;
170
173
  return typeof requestId === "number" && (type === "response" || type === "authorized" || type === "embedded");
171
174
  }
175
+ function isStreamPart(message) {
176
+ if (typeof message !== "object" || message === null) return false;
177
+ const { type, requestId } = message;
178
+ return typeof requestId === "number" && (type === "stream-start" || type === "stream-chunk" || type === "stream-end");
179
+ }
180
+ function writeStreamPart(port, writer, part) {
181
+ const { ctx } = writer;
182
+ if (part.type === "stream-end") {
183
+ if (!ctx.abort.aborted) {
184
+ ctx.res.cork(() => {
185
+ if (part.failed) ctx.res.close();
186
+ else ctx.res.end();
187
+ });
188
+ }
189
+ writer.finish();
190
+ return;
191
+ }
192
+ if (ctx.abort.aborted) return;
193
+ if (part.type === "stream-start") {
194
+ writer.started = true;
195
+ ctx.res.cork(() => {
196
+ ctx.res.writeStatus(statusLine(part.status));
197
+ for (const [key, value] of part.headers) ctx.res.writeHeader(key, value);
198
+ });
199
+ return;
200
+ }
201
+ let writable = true;
202
+ ctx.res.cork(() => {
203
+ writable = ctx.res.write(part.chunk);
204
+ });
205
+ const credit = () => port.postMessage({ type: "stream-credit", requestId: part.requestId });
206
+ if (writable) {
207
+ credit();
208
+ return;
209
+ }
210
+ ctx.res.onWritable(() => {
211
+ credit();
212
+ return true;
213
+ });
214
+ }
172
215
  function errorOf(reply) {
173
216
  const error = reply.error ?? { code: ErrorCodes.EMBEDDING_FAILED, message: "The main thread returned no vector" };
174
217
  return new NarsilError(error.code, error.message, error.details);
@@ -201,11 +244,18 @@ function createRelayClient(port, touchIntervalMs) {
201
244
  pending.clear();
202
245
  for (const entry of waiting) entry.reject(portClosedError());
203
246
  }
247
+ const writers = /* @__PURE__ */ new Map();
204
248
  port.on("message", (message) => {
249
+ if (isStreamPart(message)) {
250
+ const writer = writers.get(message.requestId);
251
+ if (writer !== void 0) writeStreamPart(port, writer, message);
252
+ return;
253
+ }
205
254
  if (!isRelayReply(message)) return;
206
255
  const waiting = pending.get(message.requestId);
207
256
  if (waiting === void 0) return;
208
257
  pending.delete(message.requestId);
258
+ writers.delete(message.requestId);
209
259
  waiting.resolve(message);
210
260
  });
211
261
  port.on("close", failAll);
@@ -240,24 +290,36 @@ function createRelayClient(port, touchIntervalMs) {
240
290
  let reply;
241
291
  const body = relayedBody(ctx.rawBody);
242
292
  try {
243
- reply = await send(
244
- (requestId) => ({
245
- type: "request",
246
- requestId,
247
- route,
248
- params: ctx.params,
249
- query: ctx.query.toString(),
250
- contentType: ctx.contentType,
251
- rawBody: body.bytes,
252
- hookContext: ctx.hookContext
253
- }),
254
- body.moved
255
- );
293
+ reply = await new Promise((resolve, reject) => {
294
+ void send((requestId) => {
295
+ writers.set(requestId, {
296
+ ctx,
297
+ started: false,
298
+ finish: () => {
299
+ writers.delete(requestId);
300
+ pending.delete(requestId);
301
+ resolve(null);
302
+ }
303
+ });
304
+ ctx.abort.onAbort(() => port.postMessage({ type: "stream-cancel", requestId }));
305
+ return {
306
+ type: "request",
307
+ requestId,
308
+ route,
309
+ params: ctx.params,
310
+ query: ctx.query.toString(),
311
+ contentType: ctx.contentType,
312
+ rawBody: body.bytes,
313
+ spooledBody: ctx.spooledBody === null ? null : { path: ctx.spooledBody.path, bytes: ctx.spooledBody.bytes },
314
+ hookContext: ctx.hookContext
315
+ };
316
+ }, body.moved).then(resolve, reject);
317
+ });
256
318
  } catch {
257
319
  if (!ctx.abort.aborted) sendUnavailable(ctx.res);
258
320
  return;
259
321
  }
260
- if (reply.type !== "response" || ctx.abort.aborted) return;
322
+ if (reply === null || reply.type !== "response" || ctx.abort.aborted) return;
261
323
  sendRelayed(ctx.res, ctx.abort, reply.status, reply.headers, reply.body);
262
324
  },
263
325
  async embed(indexName, vector) {
@@ -381,7 +443,12 @@ async function serveRequests(executor, settings) {
381
443
  const child = childAppOf(app);
382
444
  if (child === null) throw new Error("This uWebSockets.js build offers no child apps");
383
445
  const relay = createRelayClient(settings.port, settings.touchIntervalMs);
384
- const local = createThreadReadEngine({ executor, relay, searchHooks: settings.searchHooks });
446
+ const local = createThreadReadEngine({
447
+ executor,
448
+ relay,
449
+ searchHooks: settings.searchHooks,
450
+ patternWorkCap: settings.patternWorkCap
451
+ });
385
452
  const run = createRouteRunner({
386
453
  authorize: settings.authorizes ? (context) => relay.authorize(context) : void 0,
387
454
  gate: createSharedRequestGate(settings.gate, settings.maxConcurrentRequests, settings.gateSlot),
@@ -1,4 +1,4 @@
1
- import { T as TransportConfig } from './types-D5OwhtsQ.js';
1
+ import { T as TransportConfig } from './types-Cb9kiY5P.js';
2
2
 
3
3
  /**
4
4
  * The certificate material one node presents to its peers.
@@ -1,4 +1,4 @@
1
- import { S as SchemaDefinition } from './schema-Bgzr4g7b.js';
1
+ import { S as SchemaDefinition } from './schema-BBOCdhND.js';
2
2
 
3
3
  /**
4
4
  * What a node does in a cluster. A node may hold several roles at once.
@@ -31,7 +31,7 @@ interface TransportConfig {
31
31
  requestTimeout: number;
32
32
  /** A replication entry may take this long to reach a replica and come back acknowledged. */
33
33
  replicationTimeout: number;
34
- /** A whole snapshot transfer, which moves a partition between nodes, may take this long. */
34
+ /** A node waits this long for each next chunk of a snapshot transfer, which moves a partition or an index between nodes, so that the node can receive a transfer of any size while the sender keeps sending. */
35
35
  snapshotTimeout: number;
36
36
  }
37
37
  /**
@@ -1,4 +1,4 @@
1
- import { e as VectorQuantizationMode } from '../schema-Bgzr4g7b.js';
1
+ import { e as VectorQuantizationMode } from '../schema-BBOCdhND.js';
2
2
  import { A as ArenaSimd } from '../simd-DW5AtJ6B.js';
3
3
  import '../adapters-79OIa4xm.js';
4
4
 
@@ -1,8 +1,8 @@
1
- import { openSharedVectorField, MAX_WORKER_COPIES, createVectorStore, osqBitsOf, createHNSWIndex, createOsqQuantizer, osqRecordBytes } from '../chunk-YJDQ7NLA.mjs';
1
+ import { openSharedVectorField, MAX_WORKER_COPIES, createVectorStore, osqBitsOf, createHNSWIndex, createOsqQuantizer, osqRecordBytes } from '../chunk-WVDFG45M.mjs';
2
2
  import '../chunk-ZJ4CSAB2.mjs';
3
- import '../chunk-YNVIUGF5.mjs';
4
- import '../chunk-OI42GWLR.mjs';
5
- import '../chunk-USKFU7J5.mjs';
3
+ import '../chunk-PU43WDY4.mjs';
4
+ import '../chunk-LJYTW7GT.mjs';
5
+ import '../chunk-I64LXKO3.mjs';
6
6
 
7
7
  // src/vector/worker-copy.ts
8
8
  function restoreQuantizer(snapshot, store) {