@delali/narsil 0.2.4-next.42 → 0.2.4-next.44

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 (36) hide show
  1. package/dist/adapters/filesystem.mjs +1 -1
  2. package/dist/adapters/indexeddb.mjs +1 -1
  3. package/dist/{chunk-GB2IIJJG.mjs → chunk-P4ELXVJQ.mjs} +2 -4
  4. package/dist/{chunk-MMSWEEZT.mjs → chunk-WASNWIEK.mjs} +1452 -373
  5. package/dist/{chunk-GRWFY2HI.mjs → chunk-XNCJFU2E.mjs} +239 -61
  6. package/dist/{chunk-WU2UZEVD.mjs → chunk-XWICBGYT.mjs} +1 -1
  7. package/dist/{client-BobVnoIK.d.ts → client-C5BkUqB8.d.ts} +26 -3
  8. package/dist/client.d.ts +8 -8
  9. package/dist/client.mjs +12 -6
  10. package/dist/{config-fLC4lbmg.d.ts → config-B-ZI-ft5.d.ts} +2 -2
  11. package/dist/{config-Byyl1QqH.d.ts → config-BZRExglL.d.ts} +20 -3
  12. package/dist/{direct-executor-MSJEVPZU.mjs → direct-executor-IMXNRS7O.mjs} +2 -2
  13. package/dist/distribution/coordinator/etcd.d.ts +2 -2
  14. package/dist/distribution/coordinator/etcd.mjs +1 -1
  15. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  16. package/dist/distribution/transport/grpc.mjs +1 -1
  17. package/dist/distribution.d.ts +24 -10
  18. package/dist/distribution.mjs +1721 -1129
  19. package/dist/embeddings/openai.mjs +1 -1
  20. package/dist/{engine-BE45D5ZG.d.ts → engine-Cjr1uBLa.d.ts} +47 -24
  21. package/dist/index.browser.mjs +1668 -416
  22. package/dist/index.d.ts +9 -9
  23. package/dist/index.mjs +4 -4
  24. package/dist/{options-EXYFcem-.d.ts → options-BC68VPoE.d.ts} +10 -3
  25. package/dist/persistence/durability/checkpoint-worker.d.ts +3 -1
  26. package/dist/persistence/durability/checkpoint-worker.mjs +4 -4
  27. package/dist/react.d.ts +6 -6
  28. package/dist/react.mjs +2 -4
  29. package/dist/{schema-ZRbk8BgH.d.ts → schema-B7juWg8Q.d.ts} +17 -6
  30. package/dist/{search-DHuXwJYk.d.ts → search-DDHjfXhJ.d.ts} +35 -11
  31. package/dist/server.d.ts +10 -10
  32. package/dist/server.mjs +30 -12
  33. package/dist/{tasks-CnMLpfk-.d.ts → tasks-Bn4R1NPN.d.ts} +1 -1
  34. package/dist/{types-B5Tsaalz.d.ts → types-DsSSjY4d.d.ts} +1 -1
  35. package/dist/workers/entry.mjs +2 -2
  36. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { createScalarQuantizer } from './chunk-OBXS7OZM.mjs';
3
3
  import { MAX_M, createVectorStore, createHNSWIndex, createOrdinalFilter, addToOrdinalFilter, removeFromOrdinalFilter, ordinalFilterValues, magnitude, createBoundedMaxHeap, toScore, euclideanDistance, dotProduct, cosineSimilarityWithMagnitudes, ordinalFilterHas, ABSENT_DOCUMENT_RANK } from './chunk-WDL632CN.mjs';
4
4
  import { compareCodePoints, compareComparableKeys, toComparableSortValue, readSortField, compareSortValues, compareComparableValues, SORT_VALUE_MAX_CODE_POINTS } from './chunk-BFIH7OV5.mjs';
5
5
  import { generateId, createRequestId } from './chunk-WTONLDU3.mjs';
6
- import { NarsilError, ErrorCodes, createNarsilError } from './chunk-GB2IIJJG.mjs';
6
+ import { NarsilError, ErrorCodes, createNarsilError } from './chunk-P4ELXVJQ.mjs';
7
7
  import { encode, decode } from '@msgpack/msgpack';
8
8
 
9
9
  // src/schema/validator/shared.ts
@@ -497,6 +497,9 @@ function metadataToWire(meta) {
497
497
  if (meta.vectorFields) {
498
498
  wire.vector_fields = meta.vectorFields;
499
499
  }
500
+ if (isNonNegativeInteger(meta.documentCount) && Number.isSafeInteger(meta.documentCount)) {
501
+ wire.document_count = meta.documentCount;
502
+ }
500
503
  if (meta.embedding) {
501
504
  wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
502
505
  }
@@ -570,6 +573,9 @@ function wireToMetadata(raw) {
570
573
  if (raw.vector_fields) {
571
574
  meta.vectorFields = raw.vector_fields;
572
575
  }
576
+ if (isNonNegativeInteger(raw.document_count) && Number.isSafeInteger(raw.document_count)) {
577
+ meta.documentCount = raw.document_count;
578
+ }
573
579
  if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
574
580
  meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
575
581
  }
@@ -628,7 +634,7 @@ function wireToMetadata(raw) {
628
634
  if (isPositiveInteger2(promotion.threshold)) {
629
635
  restored.threshold = promotion.threshold;
630
636
  }
631
- if (isPositiveInteger2(promotion.filter_threshold)) {
637
+ if (isSelectivityRatio(promotion.filter_threshold)) {
632
638
  restored.filterThreshold = promotion.filter_threshold;
633
639
  }
634
640
  if (Object.keys(hnswConfig).length > 0) {
@@ -655,6 +661,9 @@ function isNonNegativeInteger(value) {
655
661
  function isPositiveInteger2(value) {
656
662
  return typeof value === "number" && Number.isInteger(value) && value >= 1;
657
663
  }
664
+ function isSelectivityRatio(value) {
665
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
666
+ }
658
667
  function isWatermarkFraction(value) {
659
668
  return typeof value === "number" && value > 0 && value <= 1;
660
669
  }
@@ -961,6 +970,46 @@ function documentWithOwnBuffers(document) {
961
970
  if (!sharesABiggerBuffer(document)) return document;
962
971
  return withOwnBuffers(document);
963
972
  }
973
+ var STRING_HEADER_BYTES = 16;
974
+ var TWO_BYTE_SAMPLE_LIMIT = 16;
975
+ var NUMBER_BYTES = 16;
976
+ var PRIMITIVE_SLOT_BYTES = 4;
977
+ var ARRAY_HEADER_BYTES = 32;
978
+ var ARRAY_SLOT_BYTES = 8;
979
+ var OBJECT_HEADER_BYTES = 40;
980
+ var PROPERTY_SLOT_BYTES = 8;
981
+ var TYPED_ARRAY_HEADER_BYTES = 96;
982
+ function stringBytes(value) {
983
+ const sampled = Math.min(value.length, TWO_BYTE_SAMPLE_LIMIT);
984
+ for (let i = 0; i < sampled; i += 1) {
985
+ if (value.charCodeAt(i) > 255) {
986
+ return STRING_HEADER_BYTES + value.length * 2;
987
+ }
988
+ }
989
+ return STRING_HEADER_BYTES + value.length;
990
+ }
991
+ function documentValueBytes(value) {
992
+ if (typeof value === "string") return stringBytes(value);
993
+ if (typeof value === "number") return NUMBER_BYTES;
994
+ if (value === null || value === void 0 || typeof value === "boolean") return PRIMITIVE_SLOT_BYTES;
995
+ if (ArrayBuffer.isView(value)) return TYPED_ARRAY_HEADER_BYTES + value.byteLength;
996
+ if (Array.isArray(value)) {
997
+ let bytes = ARRAY_HEADER_BYTES + value.length * ARRAY_SLOT_BYTES;
998
+ for (let i = 0; i < value.length; i += 1) {
999
+ bytes += documentValueBytes(value[i]);
1000
+ }
1001
+ return bytes;
1002
+ }
1003
+ if (typeof value === "object") {
1004
+ const source = value;
1005
+ let bytes = OBJECT_HEADER_BYTES;
1006
+ for (const key of Object.keys(source)) {
1007
+ bytes += PROPERTY_SLOT_BYTES + documentValueBytes(source[key]);
1008
+ }
1009
+ return bytes;
1010
+ }
1011
+ return PRIMITIVE_SLOT_BYTES;
1012
+ }
964
1013
  function createDocumentStore() {
965
1014
  const docs = /* @__PURE__ */ new Map();
966
1015
  const forwardMap = /* @__PURE__ */ new Map();
@@ -968,6 +1017,19 @@ function createDocumentStore() {
968
1017
  const lengthColumns = /* @__PURE__ */ new Map();
969
1018
  let nextInternalId = 0;
970
1019
  let sortedIds = null;
1020
+ let storedContentBytes = 0;
1021
+ function replaceStored(docId, document, fieldLengths) {
1022
+ const existing = docs.get(docId);
1023
+ if (existing !== void 0) {
1024
+ storedContentBytes -= documentValueBytes(existing.fields);
1025
+ } else {
1026
+ sortedIds = null;
1027
+ }
1028
+ writeFieldLengths(assignInternalId(docId), fieldLengths);
1029
+ const fields = documentWithOwnBuffers(document);
1030
+ storedContentBytes += documentValueBytes(fields);
1031
+ docs.set(docId, { fields, fieldLengths });
1032
+ }
971
1033
  function writeFieldLengths(internalId, fieldLengths) {
972
1034
  for (const fieldName of Object.keys(fieldLengths)) {
973
1035
  let column = lengthColumns.get(fieldName);
@@ -1004,21 +1066,21 @@ function createDocumentStore() {
1004
1066
  };
1005
1067
  return {
1006
1068
  store(docId, document, fieldLengths) {
1007
- if (!docs.has(docId)) sortedIds = null;
1008
- writeFieldLengths(assignInternalId(docId), fieldLengths);
1009
- docs.set(docId, { fields: documentWithOwnBuffers(document), fieldLengths });
1069
+ replaceStored(docId, document, fieldLengths);
1010
1070
  },
1011
1071
  storeRef(docId, document, fieldLengths) {
1012
- if (!docs.has(docId)) sortedIds = null;
1013
- writeFieldLengths(assignInternalId(docId), fieldLengths);
1014
- docs.set(docId, { fields: documentWithOwnBuffers(document), fieldLengths });
1072
+ replaceStored(docId, document, fieldLengths);
1015
1073
  },
1016
1074
  get(docId) {
1017
1075
  return docs.get(docId);
1018
1076
  },
1019
1077
  remove(docId) {
1078
+ const existing = docs.get(docId);
1020
1079
  const removed = docs.delete(docId);
1021
1080
  if (removed) {
1081
+ if (existing !== void 0) {
1082
+ storedContentBytes -= documentValueBytes(existing.fields);
1083
+ }
1022
1084
  sortedIds = null;
1023
1085
  const internalId = forwardMap.get(docId);
1024
1086
  if (internalId !== void 0) {
@@ -1055,6 +1117,7 @@ function createDocumentStore() {
1055
1117
  docs.clear();
1056
1118
  lengthColumns.clear();
1057
1119
  clearMappings();
1120
+ storedContentBytes = 0;
1058
1121
  },
1059
1122
  serialize() {
1060
1123
  const result = {};
@@ -1067,10 +1130,12 @@ function createDocumentStore() {
1067
1130
  docs.clear();
1068
1131
  lengthColumns.clear();
1069
1132
  clearMappings();
1133
+ storedContentBytes = 0;
1070
1134
  for (const docId of Object.keys(data)) {
1071
1135
  const stored = data[docId];
1072
1136
  writeFieldLengths(assignInternalId(docId), stored.fieldLengths);
1073
1137
  const fields = documentWithOwnBuffers(stored.fields);
1138
+ storedContentBytes += documentValueBytes(fields);
1074
1139
  docs.set(docId, fields === stored.fields ? stored : { fields, fieldLengths: stored.fieldLengths });
1075
1140
  }
1076
1141
  },
@@ -1094,6 +1159,9 @@ function createDocumentStore() {
1094
1159
  internalIdCapacity() {
1095
1160
  return nextInternalId;
1096
1161
  },
1162
+ contentBytes() {
1163
+ return storedContentBytes;
1164
+ },
1097
1165
  resolver() {
1098
1166
  return idResolver;
1099
1167
  }
@@ -4102,7 +4170,7 @@ var BYTES_PER_NUMBER_SLOT = 8;
4102
4170
  var BYTES_PER_BOOLEAN_SLOT = 1;
4103
4171
  var BYTES_PER_MIXED_SLOT = 8;
4104
4172
  var BYTES_PER_STRING_CODE_UNIT = 2;
4105
- var STRING_HEADER_BYTES = 24;
4173
+ var STRING_HEADER_BYTES2 = 24;
4106
4174
  function grow(length, wanted) {
4107
4175
  let next = length === 0 ? 64 : length;
4108
4176
  while (next <= wanted) next *= 2;
@@ -4224,7 +4292,7 @@ function createValueStore(initialKind) {
4224
4292
  for (const value of mixed) {
4225
4293
  if (typeof value !== "string") continue;
4226
4294
  if (value.length < SORT_VALUE_MAX_CODE_POINTS) continue;
4227
- bytes += STRING_HEADER_BYTES + value.length * BYTES_PER_STRING_CODE_UNIT;
4295
+ bytes += STRING_HEADER_BYTES2 + value.length * BYTES_PER_STRING_CODE_UNIT;
4228
4296
  }
4229
4297
  return bytes;
4230
4298
  }
@@ -4563,15 +4631,17 @@ function sortColumnBytes(state) {
4563
4631
  }
4564
4632
 
4565
4633
  // src/core/partition/memory.ts
4566
- var AVG_DOC_OVERHEAD = 350;
4567
- var POSTING_ENTRY_SIZE = 24;
4568
- var PER_TERM_OVERHEAD = 180;
4569
- var SURFACE_ENTRY_OVERHEAD = 140;
4570
- var FIELD_ENTRY_OVERHEAD = 42;
4634
+ var DOC_ENTRY_OVERHEAD_BYTES = 260;
4635
+ var LENGTH_COLUMN_SLOT_BYTES = 6;
4636
+ var POSTING_ENTRY_BYTES = 40;
4637
+ var POSITIONS_ARRAY_OVERHEAD_BYTES = 88;
4638
+ var POSITION_BYTES = 8;
4639
+ var PER_TERM_OVERHEAD_BYTES = 180;
4640
+ var SURFACE_ENTRY_OVERHEAD_BYTES = 140;
4641
+ var FIELD_ENTRY_OVERHEAD_BYTES = 42;
4571
4642
  function estimatePartitionBytes(state) {
4572
4643
  const docCount = state.docStore.count();
4573
4644
  if (docCount === 0) return 0;
4574
- let bytes = docCount * AVG_DOC_OVERHEAD;
4575
4645
  const docFreqs = state.stats.docFrequencies;
4576
4646
  let totalPostings = 0;
4577
4647
  let termCount = 0;
@@ -4579,13 +4649,27 @@ function estimatePartitionBytes(state) {
4579
4649
  totalPostings += docFreqs[term];
4580
4650
  termCount++;
4581
4651
  }
4582
- bytes += totalPostings * POSTING_ENTRY_SIZE;
4583
- bytes += termCount * PER_TERM_OVERHEAD;
4584
- bytes += state.surfaceRegistry.size() * SURFACE_ENTRY_OVERHEAD;
4585
- bytes += docCount * state.numericIndexes.size * FIELD_ENTRY_OVERHEAD;
4586
- bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD;
4587
- bytes += docCount * state.enumIndexes.size * FIELD_ENTRY_OVERHEAD;
4588
- bytes += docCount * state.geoIndexes.size * FIELD_ENTRY_OVERHEAD;
4652
+ const fieldLengthTotals = state.stats.totalFieldLengths;
4653
+ let totalTokenOccurrences = 0;
4654
+ let textFieldCount = 0;
4655
+ for (const field in fieldLengthTotals) {
4656
+ totalTokenOccurrences += fieldLengthTotals[field];
4657
+ textFieldCount++;
4658
+ }
4659
+ let bytes = state.docStore.contentBytes();
4660
+ bytes += docCount * DOC_ENTRY_OVERHEAD_BYTES;
4661
+ bytes += textFieldCount * state.docStore.internalIdCapacity() * LENGTH_COLUMN_SLOT_BYTES;
4662
+ bytes += totalPostings * POSTING_ENTRY_BYTES;
4663
+ if (state.trackPositions) {
4664
+ bytes += totalPostings * POSITIONS_ARRAY_OVERHEAD_BYTES;
4665
+ bytes += totalTokenOccurrences * POSITION_BYTES;
4666
+ }
4667
+ bytes += termCount * PER_TERM_OVERHEAD_BYTES;
4668
+ bytes += state.surfaceRegistry.size() * SURFACE_ENTRY_OVERHEAD_BYTES;
4669
+ bytes += docCount * state.numericIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
4670
+ bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
4671
+ bytes += docCount * state.enumIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
4672
+ bytes += docCount * state.geoIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
4589
4673
  return bytes + sortColumnBytes(state);
4590
4674
  }
4591
4675
 
@@ -6434,6 +6518,9 @@ function createFrozenDocTable(idTable, lengthColumns, documents, tombstones) {
6434
6518
  internalIdCapacity() {
6435
6519
  return capacity;
6436
6520
  },
6521
+ contentBytes() {
6522
+ return documents.byteLength;
6523
+ },
6437
6524
  resolver() {
6438
6525
  return resolver;
6439
6526
  }
@@ -6461,21 +6548,32 @@ function wrapEncodedDocumentTable(data) {
6461
6548
  const { blob, offsets } = data;
6462
6549
  const count = offsets.length - 1;
6463
6550
  const decoded = new Array(count);
6551
+ const encodedBytes = blob.byteLength + offsets.byteLength;
6552
+ let decodedBytes = 0;
6464
6553
  return {
6465
6554
  count,
6555
+ get byteLength() {
6556
+ return encodedBytes + decodedBytes;
6557
+ },
6466
6558
  docAt(ordinal) {
6467
6559
  let document = decoded[ordinal];
6468
6560
  if (document === void 0) {
6469
6561
  document = decode(blob.slice(offsets[ordinal], offsets[ordinal + 1]));
6470
6562
  decoded[ordinal] = document;
6563
+ decodedBytes += documentValueBytes(document);
6471
6564
  }
6472
6565
  return document;
6473
6566
  }
6474
6567
  };
6475
6568
  }
6476
6569
  function wrapDocumentArray(documents) {
6570
+ let residentBytes = 0;
6571
+ for (const document of documents) {
6572
+ residentBytes += documentValueBytes(document);
6573
+ }
6477
6574
  return {
6478
6575
  count: documents.length,
6576
+ byteLength: residentBytes,
6479
6577
  docAt(ordinal) {
6480
6578
  return documents[ordinal];
6481
6579
  }
@@ -8205,7 +8303,7 @@ function applySorting(hits, sort, sortKeyOf) {
8205
8303
  }
8206
8304
 
8207
8305
  // src/search/cursor.ts
8208
- var CURSOR_VERSION = 2;
8306
+ var CURSOR_VERSION = 3;
8209
8307
  var MAX_CURSOR_LENGTH = 40960;
8210
8308
  var MAX_SORT_FIELDS = 8;
8211
8309
  var MAX_SORT_FIELD_NAME_LENGTH = 255;
@@ -8260,6 +8358,7 @@ function encodePageCursor(cursor) {
8260
8358
  } else if (cursor.score !== null) {
8261
8359
  payload.s = cursor.score;
8262
8360
  }
8361
+ payload.q = cursor.binding;
8263
8362
  return encodeCursorText(JSON.stringify(payload));
8264
8363
  }
8265
8364
  function decodeSortKey(value, cursor) {
@@ -8304,8 +8403,11 @@ function decodePageCursor(cursor) {
8304
8403
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
8305
8404
  throw invalidCursor(cursor, "expected an object");
8306
8405
  }
8307
- const { v, a, s, k, o } = parsed;
8406
+ const { v, a, s, k, o, q } = parsed;
8308
8407
  if (v !== CURSOR_VERSION) throw invalidCursor(cursor, `unsupported cursor version ${String(v)}`);
8408
+ if (typeof q !== "string" || !/^[0-9a-f]{8}$/.test(q)) {
8409
+ throw invalidCursor(cursor, '"q" must be 8 lowercase hex digits');
8410
+ }
8309
8411
  if (typeof a !== "string" || a.length === 0) throw invalidCursor(cursor, '"a" must be a non-empty string');
8310
8412
  if (exceedsCodePoints(a, MAX_ANCHOR_CODE_POINTS)) {
8311
8413
  throw invalidCursor(cursor, '"a" is too long to be a document id');
@@ -8323,10 +8425,14 @@ function decodePageCursor(cursor) {
8323
8425
  anchor: a,
8324
8426
  score: typeof s === "number" ? s : null,
8325
8427
  sortKey,
8326
- sortSignature: typeof o === "string" ? o : null
8428
+ sortSignature: typeof o === "string" ? o : null,
8429
+ binding: q
8327
8430
  };
8328
8431
  }
8329
- function requireMatchingCursor(cursor, encoded, signature, scoreAnchored) {
8432
+ function requireMatchingCursor(cursor, encoded, signature, scoreAnchored, binding) {
8433
+ if (cursor.binding !== binding) {
8434
+ throw invalidCursor(encoded, "the cursor belongs to a different query");
8435
+ }
8330
8436
  if (signature !== null) {
8331
8437
  if (cursor.sortSignature !== signature) {
8332
8438
  throw invalidCursor(encoded, "the cursor belongs to a different sort order");
@@ -8360,6 +8466,12 @@ function requireWithinResultWindow(limit, offset) {
8360
8466
  { limit, offset, window: RESULT_WINDOW }
8361
8467
  );
8362
8468
  }
8469
+ function lastOrganicResult(sliced, pinnedIds) {
8470
+ for (let i = sliced.length - 1; i >= 0; i--) {
8471
+ if (pinnedIds === void 0 || !pinnedIds.has(sliced[i].id)) return sliced[i];
8472
+ }
8473
+ return void 0;
8474
+ }
8363
8475
  function ordersAfterAnchor(result, anchor, sort) {
8364
8476
  if (sort !== void 0 && anchor.sortKey !== null) {
8365
8477
  const comparison = compareSortValues(sort.sortKeyOf(result.id), anchor.sortKey, sort.directions) || compareCodePoints(result.id, anchor.anchor);
@@ -8370,10 +8482,10 @@ function ordersAfterAnchor(result, anchor, sort) {
8370
8482
  if (result.score < anchor.score) return true;
8371
8483
  return result.score === anchor.score && compareCodePoints(result.id, anchor.anchor) > 0;
8372
8484
  }
8373
- function applyPagination(results, limit, offset, cursor, sort) {
8485
+ function applyPagination(results, limit, offset, binding, cursor, sort, pinnedIds) {
8374
8486
  const decoded = cursor ? decodePageCursor(cursor) : null;
8375
8487
  if (decoded !== null && cursor !== void 0) {
8376
- requireMatchingCursor(decoded, cursor, sort?.signature ?? null, true);
8488
+ requireMatchingCursor(decoded, cursor, sort?.signature ?? null, true, binding);
8377
8489
  }
8378
8490
  if (limit <= 0) {
8379
8491
  return { paginated: [] };
@@ -8392,18 +8504,20 @@ function applyPagination(results, limit, offset, cursor, sort) {
8392
8504
  const sliced = results.slice(afterOffset, afterOffset + limit);
8393
8505
  let nextCursor;
8394
8506
  const hasMore = afterOffset + limit < results.length;
8395
- if (hasMore && sliced.length > 0) {
8396
- const lastResult = sliced[sliced.length - 1];
8507
+ const lastResult = lastOrganicResult(sliced, pinnedIds);
8508
+ if (hasMore && lastResult !== void 0) {
8397
8509
  nextCursor = sort !== void 0 ? encodePageCursor({
8398
8510
  anchor: lastResult.id,
8399
8511
  score: null,
8400
8512
  sortKey: sort.sortKeyOf(lastResult.id).map(toComparableSortValue),
8401
- sortSignature: sort.signature
8513
+ sortSignature: sort.signature,
8514
+ binding
8402
8515
  }) : encodePageCursor({
8403
8516
  anchor: lastResult.id,
8404
8517
  score: lastResult.score ?? null,
8405
8518
  sortKey: null,
8406
- sortSignature: null
8519
+ sortSignature: null,
8520
+ binding
8407
8521
  });
8408
8522
  }
8409
8523
  return { paginated: sliced, nextCursor };
@@ -8603,9 +8717,24 @@ function queryTermSatisfied(queryToken, indexTokens, tolerance, exact) {
8603
8717
  return false;
8604
8718
  }
8605
8719
 
8720
+ // src/partitioning/partition-selection.ts
8721
+ function partitionsIn(manager, partitionIds) {
8722
+ if (partitionIds === void 0) {
8723
+ return manager.getAllPartitions();
8724
+ }
8725
+ const partitions = [];
8726
+ for (const partitionId of partitionIds) {
8727
+ const partition = manager.partitionAt(partitionId);
8728
+ if (partition !== void 0) {
8729
+ partitions.push(partition);
8730
+ }
8731
+ }
8732
+ return partitions;
8733
+ }
8734
+
8606
8735
  // src/partitioning/fan-out.ts
8607
8736
  async function fanOutQuery(manager, params, language, schema, config, searchOptions) {
8608
- const partitions = config.partitionIds ? config.partitionIds.map((id) => manager.partitionAt(id)).filter((p) => p !== void 0) : manager.getAllPartitions();
8737
+ const partitions = partitionsIn(manager, config.partitionIds);
8609
8738
  if (partitions.length === 0) {
8610
8739
  return { scored: [], totalMatched: 0 };
8611
8740
  }
@@ -9093,12 +9222,24 @@ function createPartitionManager(indexName, config, language, router, initialPart
9093
9222
  return manager;
9094
9223
  }
9095
9224
 
9225
+ // src/partitioning/match-count.ts
9226
+ function countsWithoutScores(params) {
9227
+ return params.minScore === void 0 && (params.termMatch === void 0 || params.termMatch === "any");
9228
+ }
9229
+ function fanOutMatchCount(manager, params, language, schema, options) {
9230
+ let count = 0;
9231
+ for (const partition of partitionsIn(manager, options?.partitionIds)) {
9232
+ const matches = fulltextMatches(partition, params, language, schema, options?.searchOptions);
9233
+ if (matches !== null) count += matches.count;
9234
+ }
9235
+ return count;
9236
+ }
9237
+
9096
9238
  // src/core/hash.ts
9097
9239
  var FNV_OFFSET_BASIS = 2166136261;
9098
9240
  var FNV_PRIME = 16777619;
9099
9241
  var encoder3 = new TextEncoder();
9100
- function fnv1a(input) {
9101
- const bytes = encoder3.encode(input);
9242
+ function fnv1aBytes(bytes) {
9102
9243
  let hash = FNV_OFFSET_BASIS;
9103
9244
  for (let i = 0; i < bytes.length; i++) {
9104
9245
  hash ^= bytes[i];
@@ -9106,6 +9247,9 @@ function fnv1a(input) {
9106
9247
  }
9107
9248
  return hash >>> 0;
9108
9249
  }
9250
+ function fnv1a(input) {
9251
+ return fnv1aBytes(encoder3.encode(input));
9252
+ }
9109
9253
 
9110
9254
  // src/partitioning/router.ts
9111
9255
  function createPartitionRouter() {
@@ -9424,20 +9568,43 @@ function resolveWorkerCount(requested) {
9424
9568
  }
9425
9569
  function createWorkerPool(config) {
9426
9570
  const workerCount = resolveWorkerCount(config.count);
9427
- const workers = [];
9571
+ const workers = /* @__PURE__ */ new Map();
9572
+ const deadSlots = /* @__PURE__ */ new Set();
9428
9573
  const indexAssignment = /* @__PURE__ */ new Map();
9429
9574
  let isShutdown = false;
9575
+ function handleWorkerDeath(slotIndex, error) {
9576
+ if (isShutdown || deadSlots.has(slotIndex)) {
9577
+ return;
9578
+ }
9579
+ deadSlots.add(slotIndex);
9580
+ const slot = workers.get(slotIndex);
9581
+ const indexNames = slot ? [...slot.indexes].sort() : [];
9582
+ workers.delete(slotIndex);
9583
+ config.onWorkerCrash?.(slotIndex, indexNames, error);
9584
+ }
9430
9585
  function ensureWorker(slotIndex) {
9431
- if (!workers[slotIndex]) {
9432
- workers[slotIndex] = {
9433
- executor: config.workerFactory(slotIndex),
9586
+ if (deadSlots.has(slotIndex)) {
9587
+ return void 0;
9588
+ }
9589
+ let slot = workers.get(slotIndex);
9590
+ if (!slot) {
9591
+ slot = {
9592
+ executor: config.workerFactory(slotIndex, (error) => handleWorkerDeath(slotIndex, error)),
9434
9593
  indexes: /* @__PURE__ */ new Set()
9435
9594
  };
9595
+ workers.set(slotIndex, slot);
9436
9596
  }
9437
- return workers[slotIndex];
9597
+ return slot;
9438
9598
  }
9439
9599
  function assignSlot(indexName) {
9440
- return fnv1a(indexName) % workerCount;
9600
+ const preferred = fnv1a(indexName) % workerCount;
9601
+ for (let probe = 0; probe < workerCount; probe++) {
9602
+ const candidate = (preferred + probe) % workerCount;
9603
+ if (!deadSlots.has(candidate)) {
9604
+ return candidate;
9605
+ }
9606
+ }
9607
+ throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Every worker in the pool has crashed`);
9441
9608
  }
9442
9609
  function addIndex(indexName) {
9443
9610
  if (isShutdown) {
@@ -9448,18 +9615,14 @@ function createWorkerPool(config) {
9448
9615
  }
9449
9616
  const slotIndex = assignSlot(indexName);
9450
9617
  const slot = ensureWorker(slotIndex);
9618
+ if (!slot) {
9619
+ throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Worker ${slotIndex} for index "${indexName}" has crashed`);
9620
+ }
9451
9621
  slot.indexes.add(indexName);
9452
9622
  indexAssignment.set(indexName, slotIndex);
9453
9623
  }
9454
9624
  function removeIndex(indexName) {
9455
- const slotIndex = indexAssignment.get(indexName);
9456
- if (slotIndex === void 0) {
9457
- return;
9458
- }
9459
- const slot = workers[slotIndex];
9460
- if (slot) {
9461
- slot.indexes.delete(indexName);
9462
- }
9625
+ for (const slot of workers.values()) slot.indexes.delete(indexName);
9463
9626
  indexAssignment.delete(indexName);
9464
9627
  }
9465
9628
  function getExecutor(indexName) {
@@ -9470,15 +9633,22 @@ function createWorkerPool(config) {
9470
9633
  if (slotIndex === void 0) {
9471
9634
  throw new NarsilError(ErrorCodes.INDEX_NOT_FOUND, `Index "${indexName}" is not registered in the worker pool`);
9472
9635
  }
9473
- return workers[slotIndex].executor;
9636
+ const slot = workers.get(slotIndex);
9637
+ if (slot) {
9638
+ return slot.executor;
9639
+ }
9640
+ for (const survivor of workers.values()) {
9641
+ if (survivor.indexes.has(indexName)) {
9642
+ return survivor.executor;
9643
+ }
9644
+ }
9645
+ throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Every worker holding index "${indexName}" has crashed`);
9474
9646
  }
9475
9647
  async function getMemoryStats() {
9476
9648
  const indices = [];
9477
9649
  const pending = [];
9478
- for (let i = 0; i < workers.length; i++) {
9479
- const slot = workers[i];
9480
- if (!slot) continue;
9481
- indices.push(i);
9650
+ for (const [slotIndex, slot] of workers) {
9651
+ indices.push(slotIndex);
9482
9652
  pending.push(
9483
9653
  slot.executor.execute({ type: "memoryReport", requestId: createRequestId() }).catch(() => null)
9484
9654
  );
@@ -9501,7 +9671,7 @@ function createWorkerPool(config) {
9501
9671
  return;
9502
9672
  }
9503
9673
  isShutdown = true;
9504
- const shutdownPromises = workers.filter(Boolean).map((slot) => {
9674
+ const shutdownPromises = [...workers.values()].map((slot) => {
9505
9675
  const timeoutPromise = new Promise((resolve) => {
9506
9676
  setTimeout(resolve, 5e3);
9507
9677
  });
@@ -9511,17 +9681,16 @@ function createWorkerPool(config) {
9511
9681
  indexAssignment.clear();
9512
9682
  }
9513
9683
  function getAllExecutors() {
9514
- return workers.filter(Boolean).map((slot) => slot.executor);
9684
+ return [...workers.values()].map((slot) => slot.executor);
9515
9685
  }
9516
9686
  function addIndexToAll(indexName) {
9517
9687
  if (isShutdown) {
9518
9688
  throw new NarsilError(ErrorCodes.WORKER_CRASHED, "Worker pool has been shut down");
9519
9689
  }
9520
9690
  for (let i = 0; i < workerCount; i++) {
9521
- const slot = ensureWorker(i);
9522
- slot.indexes.add(indexName);
9691
+ ensureWorker(i)?.indexes.add(indexName);
9523
9692
  }
9524
- indexAssignment.set(indexName, 0);
9693
+ indexAssignment.set(indexName, assignSlot(indexName));
9525
9694
  }
9526
9695
  return {
9527
9696
  getExecutor,
@@ -10651,6 +10820,9 @@ function createDirectExecutor() {
10651
10820
  }
10652
10821
  function dropIndex(indexName) {
10653
10822
  const entry = requireIndex(indexName);
10823
+ for (const vectorIndex of entry.vectorIndexes.values()) {
10824
+ vectorIndex.dispose();
10825
+ }
10654
10826
  for (const partition of entry.manager.getAllPartitions()) {
10655
10827
  partition.clear();
10656
10828
  }
@@ -10774,6 +10946,12 @@ function createDirectExecutor() {
10774
10946
  }
10775
10947
  case "preflight": {
10776
10948
  const entry = requireIndex(action.indexName);
10949
+ if (countsWithoutScores(action.params)) {
10950
+ const count = fanOutMatchCount(entry.manager, action.params, entry.language, entry.config.schema, {
10951
+ searchOptions: entry.searchOptions
10952
+ });
10953
+ return { count };
10954
+ }
10777
10955
  const result = await fanOutQuery(
10778
10956
  entry.manager,
10779
10957
  action.params,
@@ -10861,4 +11039,4 @@ function createDirectExecutor() {
10861
11039
  };
10862
11040
  }
10863
11041
 
10864
- export { DEFAULT_PAGE_SIZE, FIELD_FILTER_OPERATORS, FILTER_EXPRESSION_KEYS, MAX_CURSOR_LENGTH, MAX_NESTING_DEPTH, applyPagination, applyProjection, applySorting, buildCompactedSegmentPayload, clampRowCount, clearNormalizationCache, collectQueryTermStats, configureNormalizationCache, createDirectExecutor, createFrozenSegment, createPartitionIndex, createPartitionManager, createPartitionRouter, createSharedFrozenSegment, createVectorIndex, createWorkerPool, decodePageCursor, deserializeMetadata, deserializePayloadV1, deserializePayloadV2, detectRuntime, encodePageCursor, extractVectorFieldsFromSchema, fanOutQuery, flattenSchema, fnv1a, freezeSegmentShared, fulltextMatches, getLanguage, getNestedValue, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, isCompositePartition, isNodeMainThread, isTextFieldType, kWayMerge, mergeFacets, mergePartitionStats, normalizeSort, pruneStatsToQueryTerms, readSortValues, registerLanguage, registerStopWords, registerTokenizer, requireMatchingCursor, requireSortableFields, requireWithinResultWindow, resetNormalizationCache, resolveIndexAnalysis, resolveProjection, serializeMetadata, sortSignatureOf, spawnNodeWorker, tokenize, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion };
11042
+ export { DEFAULT_PAGE_SIZE, FIELD_FILTER_OPERATORS, FILTER_EXPRESSION_KEYS, MAX_CURSOR_LENGTH, MAX_NESTING_DEPTH, RESULT_WINDOW, applyPagination, applyProjection, applySorting, buildCompactedSegmentPayload, clampRowCount, clearNormalizationCache, collectQueryTermStats, configureNormalizationCache, countsWithoutScores, createDirectExecutor, createFrozenSegment, createPartitionIndex, createPartitionManager, createPartitionRouter, createSharedFrozenSegment, createVectorIndex, createWorkerPool, decodePageCursor, deserializeMetadata, deserializePayloadV1, deserializePayloadV2, detectRuntime, encodePageCursor, extractVectorFieldsFromSchema, fanOutMatchCount, fanOutQuery, flattenSchema, fnv1a, fnv1aBytes, freezeSegmentShared, fulltextMatches, getLanguage, getNestedValue, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, isCompositePartition, isNodeMainThread, isTextFieldType, kWayMerge, mergeFacets, mergePartitionStats, normalizeSort, partitionsIn, pruneStatsToQueryTerms, readSortValues, registerLanguage, registerStopWords, registerTokenizer, requireMatchingCursor, requireSortableFields, requireWithinResultWindow, resetNormalizationCache, resolveIndexAnalysis, resolveProjection, serializeMetadata, sortSignatureOf, spawnNodeWorker, tokenize, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion };
@@ -1,4 +1,4 @@
1
- import { NarsilError, ErrorCodes } from './chunk-GB2IIJJG.mjs';
1
+ import { NarsilError, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
2
2
  import { encode, decode } from '@msgpack/msgpack';
3
3
 
4
4
  var INDEX_CONFIG_PREFIX = "_narsil/index/";
@@ -1,6 +1,6 @@
1
- import { V as VectorMaintenanceResult, M as MemoryStats, B as BatchResult, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, P as PartitionStatsResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult } from './search-DHuXwJYk.js';
2
- import { P as PartitionConfig, A as AnyDocument, a as InsertOptions } from './schema-ZRbk8BgH.js';
3
- import { e as TaskRecord, b as ImportResult, H as HttpIndexConfig, c as TaskListQuery, T as TaskListPage } from './tasks-CnMLpfk-.js';
1
+ import { V as VectorMaintenanceResult, M as MemoryStats, B as BatchResult, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, P as PartitionStatsResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult } from './search-DDHjfXhJ.js';
2
+ import { P as PartitionConfig, A as AnyDocument, b as InsertOptions } from './schema-B7juWg8Q.js';
3
+ import { e as TaskRecord, b as ImportResult, H as HttpIndexConfig, c as TaskListQuery, T as TaskListPage } from './tasks-Bn4R1NPN.js';
4
4
 
5
5
  /**
6
6
  * This is the `fetch` implementation the client sends every request through.
@@ -521,6 +521,29 @@ interface IndexOperations {
521
521
  * this request.
522
522
  */
523
523
  clear(indexName: string, options?: RequestOptions): Promise<void>;
524
+ /**
525
+ * Loads a closed index into server memory and marks it active, so the first
526
+ * query pays no recovery wait. Opening an index that failed to reopen clears
527
+ * its parked error and retries the recovery.
528
+ *
529
+ * @param indexName - This names the index to open.
530
+ * @param options - This sets the signal, the deadline, and the headers for
531
+ * this request.
532
+ * @throws A `NarsilError` with `INDEX_NOT_FOUND` for an unknown name, and
533
+ * with the recovery error when the index cannot load.
534
+ */
535
+ open(indexName: string, options?: RequestOptions): Promise<void>;
536
+ /**
537
+ * Checkpoints an index and releases its server memory. The data stays on
538
+ * disk, and the next operation that names the index reopens it.
539
+ *
540
+ * @param indexName - This names the index to close.
541
+ * @param options - This sets the signal, the deadline, and the headers for
542
+ * this request.
543
+ * @throws A `NarsilError` with `INDEX_NOT_FOUND` for an unknown name, and
544
+ * with `CONFIG_INVALID` when the server runs without durability.
545
+ */
546
+ close(indexName: string, options?: RequestOptions): Promise<void>;
524
547
  }
525
548
 
526
549
  /**
package/dist/client.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { C as ClientErrorCode, a as ClientErrorCodes, E as ErrorCode, b as ErrorCodes, N as NarsilError, c as NarsilErrorCode, S as ServerErrorCode, d as ServerErrorCodes } from './search-DHuXwJYk.js';
2
- export { A as ASYNC_IMPORT_CAPABILITY, E as ErrorEnvelope, R as REBUILD_ANALYSIS_CAPABILITY, S as SERVER_CAPABILITIES, T as TASK_CANCEL_CAPABILITY, a as TASK_FILTER_CAPABILITY } from './options-EXYFcem-.js';
3
- export { C as CapabilitiesResponse, a as CreateIndexEmbedding, H as HttpIndexConfig, I as ImportError, b as ImportResult, T as TaskListPage, c as TaskListQuery, d as TaskProgress, e as TaskRecord, f as TaskStatus, g as TaskType } from './tasks-CnMLpfk-.js';
4
- export { A as AdminOperations, B as BulkOperations, D as DocumentOperations, F as FetchFunction, I as ImportSource, a as IndexOperations, N as NarsilClient, b as NarsilClientOptions, P as PutResult, R as RequestOptions, S as SearchOperations, c as ServerOperations, d as ServerVersion, T as TaskOperations, W as WaitForTaskOptions, e as createNarsilClient } from './client-BobVnoIK.js';
5
- import './schema-ZRbk8BgH.js';
1
+ export { C as ClientErrorCode, a as ClientErrorCodes, E as ErrorCode, b as ErrorCodes, N as NarsilError, c as NarsilErrorCode, S as ServerErrorCode, d as ServerErrorCodes } from './search-DDHjfXhJ.js';
2
+ export { A as ASYNC_IMPORT_CAPABILITY, E as ErrorEnvelope, I as INDEX_LIFECYCLE_CAPABILITY, R as REBUILD_ANALYSIS_CAPABILITY, S as SERVER_CAPABILITIES, T as TASK_CANCEL_CAPABILITY, a as TASK_FILTER_CAPABILITY } from './options-BC68VPoE.js';
3
+ export { C as CapabilitiesResponse, a as CreateIndexEmbedding, H as HttpIndexConfig, I as ImportError, b as ImportResult, T as TaskListPage, c as TaskListQuery, d as TaskProgress, e as TaskRecord, f as TaskStatus, g as TaskType } from './tasks-Bn4R1NPN.js';
4
+ export { A as AdminOperations, B as BulkOperations, D as DocumentOperations, F as FetchFunction, I as ImportSource, a as IndexOperations, N as NarsilClient, b as NarsilClientOptions, P as PutResult, R as RequestOptions, S as SearchOperations, c as ServerOperations, d as ServerVersion, T as TaskOperations, W as WaitForTaskOptions, e as createNarsilClient } from './client-C5BkUqB8.js';
5
+ import './schema-B7juWg8Q.js';
6
6
  import './adapters-79OIa4xm.js';
7
- import './config-fLC4lbmg.js';
8
- import './config-Byyl1QqH.js';
9
- import './types-B5Tsaalz.js';
7
+ import './config-B-ZI-ft5.js';
8
+ import './config-BZRExglL.js';
9
+ import './types-DsSSjY4d.js';
10
10
  import './types-D5OwhtsQ.js';