@delali/narsil 0.2.4-next.48 → 0.2.4-next.49

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 (48) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/indexeddb.mjs +15 -10
  3. package/dist/{chunk-BFIH7OV5.mjs → chunk-2GWVQWGA.mjs} +3 -1
  4. package/dist/{chunk-UZTT2BKQ.mjs → chunk-56PJBIJ6.mjs} +891 -542
  5. package/dist/chunk-CBLS46IE.mjs +10 -0
  6. package/dist/{chunk-JQUL2T6I.mjs → chunk-GCM3JFVA.mjs} +1 -2
  7. package/dist/{chunk-4COAUBLJ.mjs → chunk-HGJC5L65.mjs} +4 -7
  8. package/dist/{chunk-DFZDV5FV.mjs → chunk-JXHUHFJ5.mjs} +27 -14
  9. package/dist/{chunk-XWICBGYT.mjs → chunk-KGRYQVZ5.mjs} +2 -5
  10. package/dist/{chunk-EV45L35L.mjs → chunk-M6JGMKA5.mjs} +391 -183
  11. package/dist/{chunk-PM52SMBT.mjs → chunk-OOSIDHA3.mjs} +2 -1
  12. package/dist/chunk-ORBLD3EL.mjs +6 -0
  13. package/dist/chunk-OWCFK42Q.mjs +30 -0
  14. package/dist/{chunk-WTONLDU3.mjs → chunk-RDGXYRF4.mjs} +1 -0
  15. package/dist/chunk-UNEEFTEU.mjs +7 -0
  16. package/dist/chunk-YEO6G2NG.mjs +6 -0
  17. package/dist/{client-C5BkUqB8.d.ts → client-DX7xPcNA.d.ts} +1 -1
  18. package/dist/client.d.ts +5 -5
  19. package/dist/client.mjs +224 -222
  20. package/dist/{config-B-ZI-ft5.d.ts → config-B41W23Us.d.ts} +1 -1
  21. package/dist/{config-BZRExglL.d.ts → config-Co-zAXqO.d.ts} +27 -11
  22. package/dist/direct-executor-HRTDWMVB.mjs +9 -0
  23. package/dist/distribution/coordinator/etcd.mjs +2 -3
  24. package/dist/distribution/coordinator/in-memory.mjs +2 -2
  25. package/dist/distribution/transport/grpc.mjs +7 -9
  26. package/dist/distribution/transport/in-memory.mjs +2 -1
  27. package/dist/distribution/transport/tcp.mjs +3 -2
  28. package/dist/distribution.d.ts +5 -5
  29. package/dist/distribution.mjs +121 -97
  30. package/dist/embeddings/openai.mjs +5 -3
  31. package/dist/{engine-Cjr1uBLa.d.ts → engine-CABZg25p.d.ts} +12 -8
  32. package/dist/index.browser.mjs +1514 -894
  33. package/dist/index.d.ts +5 -5
  34. package/dist/index.mjs +9 -8
  35. package/dist/invalidation/broadcast-channel.mjs +2 -1
  36. package/dist/invalidation/filesystem.mjs +3 -4
  37. package/dist/{options-BC68VPoE.d.ts → options-Dtk99qUs.d.ts} +1 -1
  38. package/dist/persistence/durability/checkpoint-worker.mjs +9 -8
  39. package/dist/react.d.ts +3 -3
  40. package/dist/react.mjs +7 -5
  41. package/dist/{search-DDHjfXhJ.d.ts → search-FjWhZN3f.d.ts} +20 -3
  42. package/dist/server.d.ts +6 -6
  43. package/dist/server.mjs +43 -30
  44. package/dist/vector/hnsw-build-worker.mjs +2 -4
  45. package/dist/vector/search-worker.mjs +3 -4
  46. package/dist/workers/entry.mjs +2 -2
  47. package/package.json +1 -1
  48. package/dist/direct-executor-EOTFGXTO.mjs +0 -8
@@ -1,8 +1,9 @@
1
+ import { MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, MAX_CURSOR_LENGTH, RESULT_WINDOW, DEFAULT_PAGE_SIZE, PREFIX_MAX_EXPANSIONS, MAX_CURSOR_ANCHOR_CODE_POINTS } from './chunk-CBLS46IE.mjs';
1
2
  import { english } from './chunk-TGP5M5R7.mjs';
2
- import { createScalarQuantizer } from './chunk-4COAUBLJ.mjs';
3
- import { MAX_M, createVectorStore, createHNSWIndex, createOrdinalFilter, addToOrdinalFilter, removeFromOrdinalFilter, ordinalFilterValues, magnitude, toScore, euclideanDistance, dotProduct, cosineSimilarityWithMagnitudes, ordinalFilterHas, ABSENT_DOCUMENT_RANK } from './chunk-DFZDV5FV.mjs';
4
- import { compareCodePoints, compareComparableKeys, toComparableSortValue, readSortField, compareSortValues, compareComparableValues, SORT_VALUE_MAX_CODE_POINTS } from './chunk-BFIH7OV5.mjs';
5
- import { generateId, createRequestId } from './chunk-WTONLDU3.mjs';
3
+ import { createScalarQuantizer } from './chunk-HGJC5L65.mjs';
4
+ import { MAX_M, createVectorStore, createHNSWIndex, createOrdinalFilter, addToOrdinalFilter, removeFromOrdinalFilter, ordinalFilterValues, magnitude, toScore, euclideanDistance, dotProduct, cosineSimilarityWithMagnitudes, ordinalFilterHas, WASM_PAGE_BYTES, ABSENT_DOCUMENT_RANK, HNSW_BUILD_TIMEOUT_MS, VECTOR_SEARCH_LOAD_TIMEOUT_MS, MAX_WASM_PAGES, VECTOR_SEARCH_TIMEOUT_MS } from './chunk-JXHUHFJ5.mjs';
5
+ import { compareCodePoints, compareComparableKeys, toComparableSortValue, readSortField, SORT_VALUE_MAX_CODE_POINTS, compareSortValues, compareComparableValues } from './chunk-2GWVQWGA.mjs';
6
+ import { generateId, createRequestId } from './chunk-RDGXYRF4.mjs';
6
7
  import { NarsilError, ErrorCodes, createNarsilError } from './chunk-P4ELXVJQ.mjs';
7
8
  import { encode, decode } from '@msgpack/msgpack';
8
9
 
@@ -27,7 +28,6 @@ var VECTOR_PATTERN = /^vector\[(\d+)]$/;
27
28
  var FIELD_NAME_PATTERN = /^[A-Za-z0-9_]+$/;
28
29
  var RESERVED_ROOT_FIELDS = /* @__PURE__ */ new Set(["id"]);
29
30
  var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
30
- var MAX_NESTING_DEPTH = 4;
31
31
  function isPlainObject(value) {
32
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
33
33
  }
@@ -199,8 +199,11 @@ function validateFieldValue(path, value, type) {
199
199
  }
200
200
  }
201
201
 
202
- // src/schema/validator/storable.ts
202
+ // src/schema/constants.ts
203
+ var MAX_NESTING_DEPTH = 4;
203
204
  var MAX_DOCUMENT_NESTING_DEPTH = 32;
205
+
206
+ // src/schema/validator/storable.ts
204
207
  var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
205
208
  function reject(path, reason) {
206
209
  throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path === "" ? "(document)" : path}" ${reason}`, {
@@ -484,6 +487,11 @@ function validateVectorPromotion(config) {
484
487
  fail("vectorPromotion.hnswConfig.metric must be 'cosine', 'dotProduct', or 'euclidean'", { metric });
485
488
  }
486
489
  }
490
+
491
+ // src/serialization/constants.ts
492
+ var CHECKSUM_TIMEOUT_MS = 12e4;
493
+ var CHECKSUM_YIELD_CHUNK_BYTES = 4 * 1024 * 1024;
494
+ var VALID_HNSW_METRICS = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
487
495
  function metadataToWire(meta) {
488
496
  const wire = {
489
497
  index_name: meta.indexName,
@@ -726,7 +734,6 @@ function sanitizeSurfaceForms(raw) {
726
734
  }
727
735
  return count > 0 ? result : void 0;
728
736
  }
729
- var VALID_HNSW_METRICS = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
730
737
  function validateHnswMetric(value) {
731
738
  if (value === void 0 || value === null) return void 0;
732
739
  if (typeof value === "string" && VALID_HNSW_METRICS.has(value)) {
@@ -824,10 +831,9 @@ function deserializePayloadV1(data) {
824
831
  function encodeRawPayloadV2(wire) {
825
832
  return encode(wire);
826
833
  }
827
- var VALID_HNSW_METRICS2 = /* @__PURE__ */ new Set(["cosine", "dotProduct", "euclidean"]);
828
834
  function validateHnswMetric2(value) {
829
835
  if (value === void 0 || value === null) return void 0;
830
- if (typeof value === "string" && VALID_HNSW_METRICS2.has(value)) {
836
+ if (typeof value === "string" && VALID_HNSW_METRICS.has(value)) {
831
837
  return value;
832
838
  }
833
839
  return void 0;
@@ -1168,6 +1174,11 @@ function createDocumentStore() {
1168
1174
  };
1169
1175
  }
1170
1176
 
1177
+ // src/core/constants.ts
1178
+ var POSTING_LIST_INITIAL_CAPACITY = 8;
1179
+ var POSTING_LIST_COMPACTION_THRESHOLD = 0.3;
1180
+ var MAX_TERM_FREQUENCY = 65535;
1181
+
1171
1182
  // src/core/fuzzy.ts
1172
1183
  function boundedLevenshtein(a, b, tolerance) {
1173
1184
  if (tolerance < 0) return { distance: -1, withinTolerance: false };
@@ -1218,15 +1229,12 @@ function boundedLevenshtein(a, b, tolerance) {
1218
1229
  }
1219
1230
 
1220
1231
  // src/core/posting-list.ts
1221
- var INITIAL_CAPACITY = 8;
1222
- var MAX_TERM_FREQUENCY = 65535;
1223
- var COMPACTION_THRESHOLD = 0.3;
1224
1232
  function createPostingList() {
1225
1233
  return {
1226
1234
  length: 0,
1227
1235
  docIds: [],
1228
- termFrequencies: new Uint16Array(INITIAL_CAPACITY),
1229
- fieldNameIndices: new Uint8Array(INITIAL_CAPACITY),
1236
+ termFrequencies: new Uint16Array(POSTING_LIST_INITIAL_CAPACITY),
1237
+ fieldNameIndices: new Uint8Array(POSTING_LIST_INITIAL_CAPACITY),
1230
1238
  positions: null,
1231
1239
  docIdSet: /* @__PURE__ */ new Set(),
1232
1240
  deletedDocs: /* @__PURE__ */ new Set(),
@@ -1394,7 +1402,7 @@ function createInvertedIndex(fieldNameTable) {
1394
1402
  batchDirtyTokens.add(token);
1395
1403
  return;
1396
1404
  }
1397
- if (list.deletedDocs.size / list.length > COMPACTION_THRESHOLD) {
1405
+ if (list.deletedDocs.size / list.length > POSTING_LIST_COMPACTION_THRESHOLD) {
1398
1406
  compactList(list);
1399
1407
  }
1400
1408
  },
@@ -1407,7 +1415,7 @@ function createInvertedIndex(fieldNameTable) {
1407
1415
  for (const token of batchDirtyTokens) {
1408
1416
  const list = index.get(token);
1409
1417
  if (!list) continue;
1410
- if (list.deletedDocs.size / list.length > COMPACTION_THRESHOLD) {
1418
+ if (list.deletedDocs.size / list.length > POSTING_LIST_COMPACTION_THRESHOLD) {
1411
1419
  compactList(list);
1412
1420
  }
1413
1421
  }
@@ -2190,9 +2198,13 @@ function computeFacets(state, matched, config, schema) {
2190
2198
  return result;
2191
2199
  }
2192
2200
 
2201
+ // src/geo/constants.ts
2202
+ var DEG_TO_RAD = Math.PI / 180;
2203
+ var VINCENTY_MAX_ITERATIONS = 200;
2204
+ var VINCENTY_CONVERGENCE_THRESHOLD = 1e-12;
2205
+
2193
2206
  // src/geo/haversine.ts
2194
2207
  var EARTH_RADIUS_METERS = 63710088e-1;
2195
- var DEG_TO_RAD = Math.PI / 180;
2196
2208
  function haversineDistance(lat1, lon1, lat2, lon2) {
2197
2209
  const phi1 = lat1 * DEG_TO_RAD;
2198
2210
  const phi2 = lat2 * DEG_TO_RAD;
@@ -2230,13 +2242,10 @@ function isPointInPolygon(lat, lon, polygon) {
2230
2242
  var WGS84_A = 6378137;
2231
2243
  var WGS84_F = 1 / 298.257223563;
2232
2244
  var WGS84_B = WGS84_A * (1 - WGS84_F);
2233
- var DEG_TO_RAD2 = Math.PI / 180;
2234
- var MAX_ITERATIONS = 200;
2235
- var CONVERGENCE_THRESHOLD = 1e-12;
2236
2245
  function vincentyDistance(lat1, lon1, lat2, lon2) {
2237
- const phi1 = lat1 * DEG_TO_RAD2;
2238
- const phi2 = lat2 * DEG_TO_RAD2;
2239
- const L = (lon2 - lon1) * DEG_TO_RAD2;
2246
+ const phi1 = lat1 * DEG_TO_RAD;
2247
+ const phi2 = lat2 * DEG_TO_RAD;
2248
+ const L = (lon2 - lon1) * DEG_TO_RAD;
2240
2249
  const U1 = Math.atan((1 - WGS84_F) * Math.tan(phi1));
2241
2250
  const U2 = Math.atan((1 - WGS84_F) * Math.tan(phi2));
2242
2251
  const sinU1 = Math.sin(U1);
@@ -2251,7 +2260,7 @@ function vincentyDistance(lat1, lon1, lat2, lon2) {
2251
2260
  let sinAlpha = 0;
2252
2261
  let cos2Alpha = 0;
2253
2262
  let cos2SigmaM = 0;
2254
- let iterations = MAX_ITERATIONS;
2263
+ let iterations = VINCENTY_MAX_ITERATIONS;
2255
2264
  do {
2256
2265
  const sinLambda = Math.sin(lambda);
2257
2266
  const cosLambda = Math.cos(lambda);
@@ -2267,7 +2276,7 @@ function vincentyDistance(lat1, lon1, lat2, lon2) {
2267
2276
  const C = WGS84_F / 16 * cos2Alpha * (4 + WGS84_F * (4 - 3 * cos2Alpha));
2268
2277
  prevLambda = lambda;
2269
2278
  lambda = L + (1 - C) * WGS84_F * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
2270
- } while (Math.abs(lambda - prevLambda) > CONVERGENCE_THRESHOLD && --iterations > 0);
2279
+ } while (Math.abs(lambda - prevLambda) > VINCENTY_CONVERGENCE_THRESHOLD && --iterations > 0);
2271
2280
  if (iterations === 0) {
2272
2281
  return haversineDistance(lat1, lon1, lat2, lon2);
2273
2282
  }
@@ -2696,6 +2705,20 @@ function createNumericIndex() {
2696
2705
  };
2697
2706
  }
2698
2707
 
2708
+ // src/core/tokenizer/constants.ts
2709
+ var TOKEN_CACHE_SIZE_FLOOR = 5e4;
2710
+ var TOKEN_CACHE_SIZE_CEILING = 2e6;
2711
+ var TOKEN_CACHE_BYTES_PER_ENTRY = 200;
2712
+ var NODE_TOKEN_CACHE_ENTRIES = 1e6;
2713
+ var UNKNOWN_MEMORY_TOKEN_CACHE_ENTRIES = 2e5;
2714
+ var CONSTRAINED_MEMORY_TOKEN_CACHE_FRACTION = 0.05;
2715
+ var SMALL_DEVICE_MEMORY_GB = 1;
2716
+ var MEDIUM_DEVICE_MEMORY_GB = 4;
2717
+ var SMALL_DEVICE_TOKEN_CACHE_ENTRIES = 1e5;
2718
+ var MEDIUM_DEVICE_TOKEN_CACHE_ENTRIES = 25e4;
2719
+ var LARGE_DEVICE_TOKEN_CACHE_ENTRIES = 5e5;
2720
+ var DEFAULT_MIN_TOKEN_LENGTH = 1;
2721
+
2699
2722
  // src/core/tokenizer/normalize.ts
2700
2723
  var DOTTED_CAPITAL_I = "\u0130";
2701
2724
  var COMBINING_DOT_ABOVE = "\u0307";
@@ -2742,37 +2765,33 @@ function stripDiacritics(text) {
2742
2765
  }
2743
2766
 
2744
2767
  // src/core/tokenizer/cache.ts
2745
- var CACHE_SIZE_FLOOR = 5e4;
2746
- var CACHE_SIZE_CEILING = 2e6;
2747
- var BYTES_PER_ENTRY = 200;
2748
- var NODE_CACHE_ENTRIES = 1e6;
2749
- var UNKNOWN_MEMORY_CACHE_ENTRIES = 2e5;
2750
2768
  function clampCacheSize(entries) {
2751
- return Math.max(CACHE_SIZE_FLOOR, Math.min(entries, CACHE_SIZE_CEILING));
2769
+ return Math.max(TOKEN_CACHE_SIZE_FLOOR, Math.min(entries, TOKEN_CACHE_SIZE_CEILING));
2752
2770
  }
2753
2771
  function computeDefaultCacheSize() {
2754
2772
  try {
2755
2773
  if (typeof process !== "undefined" && typeof process.versions?.node === "string" && typeof window === "undefined") {
2756
2774
  const constrainedMemory = typeof process.constrainedMemory === "function" ? process.constrainedMemory() : 0;
2757
2775
  if (constrainedMemory > 0) {
2758
- return clampCacheSize(Math.floor(constrainedMemory * 0.05 / BYTES_PER_ENTRY));
2776
+ const budget = constrainedMemory * CONSTRAINED_MEMORY_TOKEN_CACHE_FRACTION;
2777
+ return clampCacheSize(Math.floor(budget / TOKEN_CACHE_BYTES_PER_ENTRY));
2759
2778
  }
2760
- return clampCacheSize(NODE_CACHE_ENTRIES);
2779
+ return clampCacheSize(NODE_TOKEN_CACHE_ENTRIES);
2761
2780
  }
2762
2781
  if (typeof navigator !== "undefined") {
2763
2782
  const mem = navigator.deviceMemory;
2764
2783
  if (typeof mem === "number" && mem > 0) {
2765
2784
  let entries;
2766
- if (mem <= 1) entries = 1e5;
2767
- else if (mem <= 4) entries = 25e4;
2768
- else entries = 5e5;
2785
+ if (mem <= SMALL_DEVICE_MEMORY_GB) entries = SMALL_DEVICE_TOKEN_CACHE_ENTRIES;
2786
+ else if (mem <= MEDIUM_DEVICE_MEMORY_GB) entries = MEDIUM_DEVICE_TOKEN_CACHE_ENTRIES;
2787
+ else entries = LARGE_DEVICE_TOKEN_CACHE_ENTRIES;
2769
2788
  return clampCacheSize(entries);
2770
2789
  }
2771
2790
  }
2772
2791
  } catch {
2773
- return clampCacheSize(UNKNOWN_MEMORY_CACHE_ENTRIES);
2792
+ return clampCacheSize(UNKNOWN_MEMORY_TOKEN_CACHE_ENTRIES);
2774
2793
  }
2775
- return clampCacheSize(UNKNOWN_MEMORY_CACHE_ENTRIES);
2794
+ return clampCacheSize(UNKNOWN_MEMORY_TOKEN_CACHE_ENTRIES);
2776
2795
  }
2777
2796
  var normalizationCache = /* @__PURE__ */ new Map();
2778
2797
  var normalizationCacheSize = 0;
@@ -2800,7 +2819,7 @@ function configureNormalizationCache(maxSize) {
2800
2819
  }
2801
2820
  );
2802
2821
  }
2803
- maxCacheSize = Math.max(CACHE_SIZE_FLOOR, Math.min(Math.floor(maxSize), CACHE_SIZE_CEILING));
2822
+ maxCacheSize = Math.max(TOKEN_CACHE_SIZE_FLOOR, Math.min(Math.floor(maxSize), TOKEN_CACHE_SIZE_CEILING));
2804
2823
  if (normalizationCacheSize > maxCacheSize) {
2805
2824
  evictOldestEntries(normalizationCacheSize - maxCacheSize);
2806
2825
  }
@@ -3013,7 +3032,6 @@ function expandNgrams(part, size, out) {
3013
3032
 
3014
3033
  // src/core/tokenizer/tokenize.ts
3015
3034
  var DEFAULT_SPLIT_PATTERN = /[^\p{L}\p{M}\p{N}_'-]+/u;
3016
- var DEFAULT_MIN_TOKEN_LENGTH = 1;
3017
3035
  function resolveStopWords(language, override) {
3018
3036
  if (!override) return language.stopWords;
3019
3037
  if (override instanceof Set) return override;
@@ -3998,14 +4016,27 @@ function partitionFilterMatches(state, filters, schema) {
3998
4016
  };
3999
4017
  }
4000
4018
 
4019
+ // src/core/partition/posting-columns.ts
4020
+ function postingColumns(list) {
4021
+ const deletedDocs = list.deletedDocs;
4022
+ return {
4023
+ docIds: list.docIds,
4024
+ termFrequencies: list.termFrequencies,
4025
+ fieldNameIndices: list.fieldNameIndices,
4026
+ deletedDocs,
4027
+ hasDeleted: deletedDocs.size > 0,
4028
+ count: list.length
4029
+ };
4030
+ }
4031
+
4001
4032
  // src/core/partition/matches.ts
4002
4033
  function markPostingList(matched, list, fieldNames, fields, filterBitset) {
4003
- const hasDeleted = list.deletedDocs.size > 0;
4004
- for (let pi = 0; pi < list.length; pi++) {
4005
- const internalId = list.docIds[pi];
4006
- if (hasDeleted && list.deletedDocs.has(internalId)) continue;
4034
+ const { docIds, fieldNameIndices, deletedDocs, hasDeleted, count } = postingColumns(list);
4035
+ for (let pi = 0; pi < count; pi++) {
4036
+ const internalId = docIds[pi];
4037
+ if (hasDeleted && deletedDocs.has(internalId)) continue;
4007
4038
  if (filterBitset && !bitsetHas(filterBitset, internalId)) continue;
4008
- if (fields && !fields.includes(fieldNames[list.fieldNameIndices[pi]])) continue;
4039
+ if (fields && !fields.includes(fieldNames[fieldNameIndices[pi]])) continue;
4009
4040
  bitsetSet(matched, internalId);
4010
4041
  }
4011
4042
  }
@@ -4076,6 +4107,12 @@ function searchFulltextMatches(state, params) {
4076
4107
  };
4077
4108
  }
4078
4109
 
4110
+ // src/core/partition/constants.ts
4111
+ var TARGET_BLOCK_ENTRIES = 128;
4112
+ var SCORE_BUFFER_INITIAL_TOUCHED_CAPACITY = 1024;
4113
+ var SORT_COLUMN_MINIMUM_REBUILD_THRESHOLD = 1024;
4114
+ var SORT_COLUMN_REBUILD_FRACTION_SHIFT = 2;
4115
+
4079
4116
  // src/core/partition/sort-columns/order.ts
4080
4117
  var MISSING_RANK = -1;
4081
4118
  var BYTES_PER_RANK = 4;
@@ -4301,8 +4338,6 @@ function createValueStore(initialKind) {
4301
4338
  }
4302
4339
 
4303
4340
  // src/core/partition/sort-columns/index.ts
4304
- var MINIMUM_REBUILD_THRESHOLD = 1024;
4305
- var REBUILD_FRACTION_SHIFT = 2;
4306
4341
  function liveInternalIds(docStore) {
4307
4342
  const ids = [];
4308
4343
  for (const internalId of docStore.allInternalIds()) ids.push(internalId);
@@ -4312,8 +4347,8 @@ function createSortColumnSet(docStore) {
4312
4347
  const columns = /* @__PURE__ */ new Map();
4313
4348
  function rebuildThreshold() {
4314
4349
  const count = docStore.count();
4315
- const fraction = count >> REBUILD_FRACTION_SHIFT;
4316
- return fraction > MINIMUM_REBUILD_THRESHOLD ? fraction : MINIMUM_REBUILD_THRESHOLD;
4350
+ const fraction = count >> SORT_COLUMN_REBUILD_FRACTION_SHIFT;
4351
+ return fraction > SORT_COLUMN_MINIMUM_REBUILD_THRESHOLD ? fraction : SORT_COLUMN_MINIMUM_REBUILD_THRESHOLD;
4317
4352
  }
4318
4353
  function rebuild(entry) {
4319
4354
  entry.order = buildOrder(entry.store, docStore.allInternalIds(), docStore.internalIdCapacity());
@@ -4942,13 +4977,12 @@ function sortSelection(heap) {
4942
4977
  }
4943
4978
 
4944
4979
  // src/core/partition/score-buffer.ts
4945
- var INITIAL_TOUCHED_CAPACITY = 1024;
4946
4980
  var MAX_GENERATION = 2147483647;
4947
4981
  function createScoreBuffer(capacity) {
4948
4982
  return {
4949
4983
  scores: new Float64Array(capacity),
4950
4984
  stamps: new Int32Array(capacity),
4951
- touched: new Int32Array(Math.max(1, Math.min(capacity, INITIAL_TOUCHED_CAPACITY))),
4985
+ touched: new Int32Array(Math.max(1, Math.min(capacity, SCORE_BUFFER_INITIAL_TOUCHED_CAPACITY))),
4952
4986
  touchedCount: 0,
4953
4987
  generation: 0
4954
4988
  };
@@ -5033,7 +5067,6 @@ function topKFromBuffer(buffer, k, resolver, components) {
5033
5067
  }
5034
5068
 
5035
5069
  // src/core/partition/block-bounds.ts
5036
- var TARGET_BLOCK_ENTRIES = 128;
5037
5070
  var ABSENT_FIELD_LENGTH_BOUND = 0;
5038
5071
  var boundsByList = /* @__PURE__ */ new WeakMap();
5039
5072
  function fieldLengthBound(columns, fieldIndex, internalId) {
@@ -5142,12 +5175,27 @@ function bestSearchable(searchable, values) {
5142
5175
  }
5143
5176
  return best;
5144
5177
  }
5178
+ function prunableSingleTermList(params, index) {
5179
+ if (params.queryTokens.length !== 1) return null;
5180
+ if (params.prefixExpansion !== void 0) return null;
5181
+ if (params.exact !== true && (params.tolerance ?? 0) !== 0) return null;
5182
+ if (params.termMatch !== void 0 && params.termMatch !== "any") return null;
5183
+ if (params.collectComponents !== false) return null;
5184
+ if (params.collectMatchedSet !== void 0) return null;
5185
+ if (params.maxResults === void 0) return null;
5186
+ if (params.fields !== void 0) return null;
5187
+ if (params.filterBitset !== void 0) return null;
5188
+ if (!bm25PruningSound(params.bm25Params)) return null;
5189
+ const list = index.lookup(params.queryTokens[0].token);
5190
+ if (list === void 0) return null;
5191
+ if (!list.ordered) return null;
5192
+ return list;
5193
+ }
5145
5194
  function singleTermTopK(request) {
5146
5195
  const { list, docFrequency, totalDocs, bm25Params, limit, resolver } = request;
5147
5196
  const { fieldSearchable, fieldBoosts, fieldAvgLengths, fieldLengthColumns } = request;
5148
5197
  const wanted = Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 0;
5149
- const deleted = list.deletedDocs;
5150
- const hasDeleted = deleted.size > 0;
5198
+ const { docIds, termFrequencies, fieldNameIndices, deletedDocs: deleted, hasDeleted } = postingColumns(list);
5151
5199
  const bounds = blockBoundsFor(list, fieldLengthColumns);
5152
5200
  const maxBoost = bestSearchable(fieldSearchable, fieldBoosts);
5153
5201
  const maxAverageLength = bestSearchable(fieldSearchable, fieldAvgLengths);
@@ -5174,19 +5222,19 @@ function singleTermTopK(request) {
5174
5222
  }
5175
5223
  let entry = start;
5176
5224
  while (entry < end) {
5177
- const internalId = list.docIds[entry];
5225
+ const internalId = docIds[entry];
5178
5226
  if (hasDeleted && deleted.has(internalId)) {
5179
- while (entry < end && list.docIds[entry] === internalId) entry++;
5227
+ while (entry < end && docIds[entry] === internalId) entry++;
5180
5228
  continue;
5181
5229
  }
5182
5230
  let score = 0;
5183
5231
  let scored2 = false;
5184
- while (entry < end && list.docIds[entry] === internalId) {
5185
- const fieldIndex = list.fieldNameIndices[entry];
5232
+ while (entry < end && docIds[entry] === internalId) {
5233
+ const fieldIndex = fieldNameIndices[entry];
5186
5234
  if (fieldSearchable[fieldIndex] === 1) {
5187
5235
  const fieldLength = fieldLengthOf(fieldLengthColumns, fieldIndex, internalId, fieldAvgLengths[fieldIndex]);
5188
5236
  score += computeBM25(
5189
- list.termFrequencies[entry],
5237
+ termFrequencies[entry],
5190
5238
  docFrequency,
5191
5239
  totalDocs,
5192
5240
  fieldLength,
@@ -5238,21 +5286,10 @@ function singleTermTopK(request) {
5238
5286
  function globalDocFreqFor(docFreqs, term, fallback) {
5239
5287
  return Object.hasOwn(docFreqs, term) ? docFreqs[term] : fallback;
5240
5288
  }
5241
- function prunableSingleTermList(params, index) {
5242
- if (params.queryTokens.length !== 1) return null;
5243
- if (params.prefixExpansion !== void 0) return null;
5244
- if (params.exact !== true && (params.tolerance ?? 0) !== 0) return null;
5245
- if (params.termMatch !== void 0 && params.termMatch !== "any") return null;
5246
- if (params.collectComponents !== false) return null;
5247
- if (params.collectMatchedSet !== void 0) return null;
5248
- if (params.maxResults === void 0) return null;
5249
- if (params.fields !== void 0) return null;
5250
- if (params.filterBitset !== void 0) return null;
5251
- if (!bm25PruningSound(params.bm25Params)) return null;
5252
- const list = index.lookup(params.queryTokens[0].token);
5253
- if (list === void 0) return null;
5254
- if (!list.ordered) return null;
5255
- return list;
5289
+ function matchesFor(index, token, exact, tolerance, prefixLength) {
5290
+ if (!exact) return index.fuzzyLookup(token, tolerance, prefixLength);
5291
+ const postingList = index.lookup(token);
5292
+ return postingList ? [{ token, postingList }] : [];
5256
5293
  }
5257
5294
  function searchFulltext(state, params) {
5258
5295
  const {
@@ -5333,17 +5370,18 @@ function searchFulltext(state, params) {
5333
5370
  const best = /* @__PURE__ */ new Map();
5334
5371
  for (const match of matches) {
5335
5372
  const perTerm = /* @__PURE__ */ new Map();
5336
- const list = match.postingList;
5337
- const hasDeleted = list.deletedDocs.size > 0;
5338
- for (let pi = 0; pi < list.length; pi++) {
5339
- const internalId = list.docIds[pi];
5340
- if (hasDeleted && list.deletedDocs.has(internalId)) continue;
5373
+ const { docIds, termFrequencies, fieldNameIndices, deletedDocs, hasDeleted, count } = postingColumns(
5374
+ match.postingList
5375
+ );
5376
+ for (let pi = 0; pi < count; pi++) {
5377
+ const internalId = docIds[pi];
5378
+ if (hasDeleted && deletedDocs.has(internalId)) continue;
5341
5379
  if (filterBitset && !bitsetHas(filterBitset, internalId)) continue;
5342
- const fieldIndex = list.fieldNameIndices[pi];
5380
+ const fieldIndex = fieldNameIndices[pi];
5343
5381
  loadFieldMeta(fieldIndex);
5344
5382
  if (fieldSearchable[fieldIndex] === 0) continue;
5345
5383
  const fieldName = fieldNames[fieldIndex];
5346
- const termFrequency = list.termFrequencies[pi];
5384
+ const termFrequency = termFrequencies[pi];
5347
5385
  const fieldBoost = fieldBoosts[fieldIndex];
5348
5386
  const avgLen = fieldAvgLengths[fieldIndex];
5349
5387
  const actualFieldLength = resolveFieldLength(internalId, fieldIndex, avgLen);
@@ -5415,10 +5453,7 @@ function searchFulltext(state, params) {
5415
5453
  resolved.push({ token: qt.token, matches: [], totalPostings: totalPostings2, isPrefix: true });
5416
5454
  continue;
5417
5455
  }
5418
- const rawMatches = exact ? (() => {
5419
- const postingList = state.invertedIdx.lookup(qt.token);
5420
- return postingList ? [{ token: qt.token, postingList }] : [];
5421
- })() : state.invertedIdx.fuzzyLookup(qt.token, tolerance, prefixLength);
5456
+ const rawMatches = matchesFor(state.invertedIdx, qt.token, exact, tolerance, prefixLength);
5422
5457
  let totalPostings = 0;
5423
5458
  const matches = [];
5424
5459
  for (const m of rawMatches) {
@@ -5440,17 +5475,18 @@ function searchFulltext(state, params) {
5440
5475
  continue;
5441
5476
  }
5442
5477
  for (const match of resolved[tokenIndex].matches) {
5443
- const list = match.postingList;
5444
- const hasDeleted = list.deletedDocs.size > 0;
5445
- for (let pi = 0; pi < list.length; pi++) {
5446
- const internalId = list.docIds[pi];
5447
- if (hasDeleted && list.deletedDocs.has(internalId)) continue;
5478
+ const { docIds, termFrequencies, fieldNameIndices, deletedDocs, hasDeleted, count } = postingColumns(
5479
+ match.postingList
5480
+ );
5481
+ for (let pi = 0; pi < count; pi++) {
5482
+ const internalId = docIds[pi];
5483
+ if (hasDeleted && deletedDocs.has(internalId)) continue;
5448
5484
  if (filterBitset && !bitsetHas(filterBitset, internalId)) continue;
5449
5485
  if (tokenIndex > 0 && !hasScore(scoreBuffer, internalId)) continue;
5450
- const fieldIndex = list.fieldNameIndices[pi];
5486
+ const fieldIndex = fieldNameIndices[pi];
5451
5487
  loadFieldMeta(fieldIndex);
5452
5488
  if (fieldSearchable[fieldIndex] === 0) continue;
5453
- const termFrequency = list.termFrequencies[pi];
5489
+ const termFrequency = termFrequencies[pi];
5454
5490
  const fieldBoost = fieldBoosts[fieldIndex];
5455
5491
  const avgLen = fieldAvgLengths[fieldIndex];
5456
5492
  const actualFieldLength = resolveFieldLength(internalId, fieldIndex, avgLen);
@@ -5483,23 +5519,21 @@ function searchFulltext(state, params) {
5483
5519
  }
5484
5520
  continue;
5485
5521
  }
5486
- const matchingPostings = exact ? (() => {
5487
- const postingList = state.invertedIdx.lookup(qt.token);
5488
- return postingList ? [{ token: qt.token, postingList }] : [];
5489
- })() : state.invertedIdx.fuzzyLookup(qt.token, tolerance, prefixLength);
5522
+ const matchingPostings = matchesFor(state.invertedIdx, qt.token, exact, tolerance, prefixLength);
5490
5523
  for (const match of matchingPostings) {
5491
5524
  const docFreq = globalStats ? globalDocFreqFor(globalDocFreqs, match.token, match.postingList.docIdSet.size) : match.postingList.docIdSet.size;
5492
5525
  const idf = computeIDF(docFreq, totalDocs);
5493
- const list = match.postingList;
5494
- const hasDeleted = list.deletedDocs.size > 0;
5495
- for (let pi = 0; pi < list.length; pi++) {
5496
- const internalId = list.docIds[pi];
5497
- if (hasDeleted && list.deletedDocs.has(internalId)) continue;
5526
+ const { docIds, termFrequencies, fieldNameIndices, deletedDocs, hasDeleted, count } = postingColumns(
5527
+ match.postingList
5528
+ );
5529
+ for (let pi = 0; pi < count; pi++) {
5530
+ const internalId = docIds[pi];
5531
+ if (hasDeleted && deletedDocs.has(internalId)) continue;
5498
5532
  if (filterBitset && !bitsetHas(filterBitset, internalId)) continue;
5499
- const fieldIndex = list.fieldNameIndices[pi];
5533
+ const fieldIndex = fieldNameIndices[pi];
5500
5534
  loadFieldMeta(fieldIndex);
5501
5535
  if (fieldSearchable[fieldIndex] === 0) continue;
5502
- const termFrequency = list.termFrequencies[pi];
5536
+ const termFrequency = termFrequencies[pi];
5503
5537
  const fieldBoost = fieldBoosts[fieldIndex];
5504
5538
  const avgLen = fieldAvgLengths[fieldIndex];
5505
5539
  const actualFieldLength = resolveFieldLength(internalId, fieldIndex, avgLen);
@@ -7147,6 +7181,9 @@ function createFrozenTombstones() {
7147
7181
  removed.add(internalId);
7148
7182
  revision++;
7149
7183
  return true;
7184
+ },
7185
+ values() {
7186
+ return removed.values();
7150
7187
  }
7151
7188
  };
7152
7189
  }
@@ -7270,7 +7307,7 @@ function buildGeoReaders(entries) {
7270
7307
  }
7271
7308
  return readers;
7272
7309
  }
7273
- function assembleFrozenSegment(segmentId, source, documentSource) {
7310
+ function assembleFrozenSegment(segmentId, source, documentSource, sharedSnapshot) {
7274
7311
  const tombstones = createFrozenTombstones();
7275
7312
  const postingViews = createFrozenPostingViews(source, tombstones);
7276
7313
  const lengthColumns = /* @__PURE__ */ new Map();
@@ -7291,6 +7328,7 @@ function assembleFrozenSegment(segmentId, source, documentSource) {
7291
7328
  const segment = {
7292
7329
  segmentId,
7293
7330
  documentSource,
7331
+ sharedSnapshot,
7294
7332
  invertedIdx: createFrozenInvertedReader(source.tokenTable, postingViews),
7295
7333
  docStore,
7296
7334
  stats: buildStatsView(source),
@@ -7321,6 +7359,11 @@ function assembleFrozenSegment(segmentId, source, documentSource) {
7321
7359
  segment.scoreBuffer = null;
7322
7360
  }
7323
7361
  return added;
7362
+ },
7363
+ tombstonedDocIds() {
7364
+ const docIds = [];
7365
+ for (const ordinal of tombstones.values()) docIds.push(source.idTable.idAt(ordinal));
7366
+ return docIds;
7324
7367
  }
7325
7368
  };
7326
7369
  return segment;
@@ -7334,7 +7377,8 @@ function createFrozenSegment(payload, documents, segmentId) {
7334
7377
  idTable: buildExternalIdTable(payload.docIds),
7335
7378
  docFrequencies: () => payload.docFrequencies
7336
7379
  },
7337
- wrapDocumentArray(documents)
7380
+ wrapDocumentArray(documents),
7381
+ null
7338
7382
  );
7339
7383
  }
7340
7384
  function createSharedFrozenSegment(snapshot) {
@@ -7353,7 +7397,8 @@ function createSharedFrozenSegment(snapshot) {
7353
7397
  return frequencies;
7354
7398
  }
7355
7399
  },
7356
- wrapEncodedDocumentTable(snapshot.documentTable)
7400
+ wrapEncodedDocumentTable(snapshot.documentTable),
7401
+ snapshot
7357
7402
  );
7358
7403
  }
7359
7404
 
@@ -7395,6 +7440,15 @@ function buildCompactedSegmentPayload(segments) {
7395
7440
  }
7396
7441
  return { payload: scratch.encodeSegment(), documents };
7397
7442
  }
7443
+ function freezeLiveTailInto(live, liveState, frozen, freeze) {
7444
+ if (live.count() === 0) return null;
7445
+ const payload = live.encodeSegment();
7446
+ const segment = freeze(payload, survivorDocumentsOf(liveState, payload));
7447
+ if (segment === null) return null;
7448
+ live.clear();
7449
+ frozen.push(segment);
7450
+ return segment;
7451
+ }
7398
7452
  function swapFrozenSegmentList(frozen, dropSegmentIds, replacement, partitionId) {
7399
7453
  const dropped = resolveFrozenSegments(frozen, dropSegmentIds, partitionId);
7400
7454
  const dropSet = new Set(dropSegmentIds);
@@ -7434,11 +7488,15 @@ function subBitsetView(composite, layout, subIndex) {
7434
7488
  const endWord = subIndex + 1 < layout.bases.length ? layout.bases[subIndex + 1] >> 5 : Math.ceil(layout.totalCapacity / WORD_BITS);
7435
7489
  return composite.subarray(startWord, endWord);
7436
7490
  }
7491
+ function placeSubBitset(composite, layout, subIndex, subBits) {
7492
+ const words = subBitsetView(composite, layout, subIndex).length;
7493
+ if (words === 0) return;
7494
+ composite.set(subBits.length > words ? subBits.subarray(0, words) : subBits, layout.bases[subIndex] >> 5);
7495
+ }
7437
7496
  function compositeFiltersBitset(subs, layout, filters, schema) {
7438
7497
  const composite = new Uint32Array(Math.ceil(layout.totalCapacity / WORD_BITS));
7439
7498
  for (let i = 0; i < subs.length; i++) {
7440
- const subBits = applyPartitionFiltersBitset(subs[i], filters, schema);
7441
- composite.set(subBits, layout.bases[i] >> 5);
7499
+ placeSubBitset(composite, layout, i, applyPartitionFiltersBitset(subs[i], filters, schema));
7442
7500
  }
7443
7501
  return composite;
7444
7502
  }
@@ -7700,10 +7758,30 @@ function subParams(params, layout, subIndex, globalStats) {
7700
7758
  filterBitset: params.filterBitset === void 0 ? void 0 : subBitsetView(params.filterBitset, layout, subIndex)
7701
7759
  };
7702
7760
  }
7761
+ function onlyPopulatedSub(subs) {
7762
+ let found = -1;
7763
+ for (let i = 0; i < subs.length; i++) {
7764
+ if (subs[i].stats.totalDocuments === 0) continue;
7765
+ if (found !== -1) return -1;
7766
+ found = i;
7767
+ }
7768
+ return found;
7769
+ }
7770
+ function searchSingleSub(subs, layout, params, index) {
7771
+ const result = searchFulltext(subs[index], subParams(params, layout, index, params.globalStats));
7772
+ if (result.matchedOrdinalBitset === void 0) return result;
7773
+ const matchedOrdinalBitset = createBitSet(layout.totalCapacity);
7774
+ placeSubBitset(matchedOrdinalBitset, layout, index, result.matchedOrdinalBitset);
7775
+ return { ...result, matchedOrdinalBitset };
7776
+ }
7703
7777
  function compositeSearchFulltext(subs, layout, params) {
7704
7778
  if (params.queryTokens.length === 0) {
7705
7779
  return { scored: [], totalMatched: 0 };
7706
7780
  }
7781
+ const single = onlyPopulatedSub(subs);
7782
+ if (single !== -1) {
7783
+ return searchSingleSub(subs, layout, params, single);
7784
+ }
7707
7785
  const globalStats = compositeQueryStats(subs, params);
7708
7786
  const results = subs.map((sub, index) => searchFulltext(sub, subParams(params, layout, index, globalStats)));
7709
7787
  const scored = kWayMerge(results.map((result) => result.scored));
@@ -7721,7 +7799,7 @@ function compositeSearchFulltext(subs, layout, params) {
7721
7799
  const matchedOrdinalBitset = createBitSet(layout.totalCapacity);
7722
7800
  for (let i = 0; i < results.length; i++) {
7723
7801
  const subBitset = results[i].matchedOrdinalBitset;
7724
- if (subBitset !== void 0) matchedOrdinalBitset.set(subBitset, layout.bases[i] >> 5);
7802
+ if (subBitset !== void 0) placeSubBitset(matchedOrdinalBitset, layout, i, subBitset);
7725
7803
  }
7726
7804
  return { scored, totalMatched, matchedOrdinalBitset };
7727
7805
  }
@@ -7765,7 +7843,7 @@ function compositeSearchMatches(subs, layout, params) {
7765
7843
  ordinalBitset() {
7766
7844
  const combined = createBitSet(layout.totalCapacity);
7767
7845
  for (let i = 0; i < subMatches.length; i++) {
7768
- combined.set(subMatches[i].ordinalBitset(), layout.bases[i] >> 5);
7846
+ placeSubBitset(combined, layout, i, subMatches[i].ordinalBitset());
7769
7847
  }
7770
7848
  return combined;
7771
7849
  }
@@ -7848,17 +7926,6 @@ function compositeExpandTermPrefix(subs, surfacePrefix, stemmedToken, maxExpansi
7848
7926
  function isCompositePartition(partition) {
7849
7927
  return "attachFrozenSegment" in partition;
7850
7928
  }
7851
- function survivorDocuments(sub, payload) {
7852
- return payload.docIds.map((docId) => {
7853
- const stored = sub.docStore.get(docId);
7854
- if (stored === void 0) {
7855
- throw new NarsilError(ErrorCodes.PARTITION_CORRUPTED, `Document "${docId}" vanished while composing a segment`, {
7856
- docId
7857
- });
7858
- }
7859
- return stored.fields;
7860
- });
7861
- }
7862
7929
  function createCompositePartition(partitionId, trackPositions = true, existingLive) {
7863
7930
  const live = existingLive ?? createPartitionIndex(partitionId, trackPositions);
7864
7931
  const liveState = partitionStateOf(live);
@@ -7881,7 +7948,7 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
7881
7948
  for (const sub of subs()) {
7882
7949
  const payload = encodeSegmentState(sub);
7883
7950
  if (payload.documentCount === 0) continue;
7884
- scratch.mergeSegmentPayload(payload, survivorDocuments(sub, payload));
7951
+ scratch.mergeSegmentPayload(payload, survivorDocumentsOf(sub, payload));
7885
7952
  }
7886
7953
  return scratch;
7887
7954
  }
@@ -7917,6 +7984,16 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
7917
7984
  swapFrozenSegmentList(frozen, dropSegmentIds, replacement, partitionId);
7918
7985
  invalidateDocFrequencies();
7919
7986
  },
7987
+ freezeLiveTail(freeze) {
7988
+ const segment = freezeLiveTailInto(live, liveState, frozen, freeze);
7989
+ if (segment !== null) invalidateDocFrequencies();
7990
+ return segment;
7991
+ },
7992
+ replaceLiveTail(segment) {
7993
+ live.clear();
7994
+ frozen.push(segment);
7995
+ invalidateDocFrequencies();
7996
+ },
7920
7997
  appendFrozenSegment(payload, documents) {
7921
7998
  for (const docId of payload.docIds) {
7922
7999
  if (live.has(docId) || frozenOwner(docId) !== void 0) {
@@ -7961,7 +8038,7 @@ function createCompositePartition(partitionId, trackPositions = true, existingLi
7961
8038
  for (const segment of frozen) {
7962
8039
  const payload = encodeSegmentState(segment);
7963
8040
  if (payload.documentCount === 0) continue;
7964
- live.mergeSegmentPayload(payload, survivorDocuments(segment, payload));
8041
+ live.mergeSegmentPayload(payload, survivorDocumentsOf(segment, payload));
7965
8042
  }
7966
8043
  frozen.length = 0;
7967
8044
  }
@@ -8304,11 +8381,6 @@ function applySorting(hits, sort, sortKeyOf) {
8304
8381
 
8305
8382
  // src/search/cursor.ts
8306
8383
  var CURSOR_VERSION = 3;
8307
- var MAX_CURSOR_LENGTH = 40960;
8308
- var MAX_SORT_FIELDS = 8;
8309
- var MAX_SORT_FIELD_NAME_LENGTH = 255;
8310
- var MAX_ANCHOR_CODE_POINTS = 512;
8311
- var MAX_SORT_VALUE_CODE_POINTS = 512;
8312
8384
  function invalidCursor(cursor, reason) {
8313
8385
  const shown = cursor.length > 100 ? `${cursor.slice(0, 100)}...` : cursor;
8314
8386
  return new NarsilError(ErrorCodes.SEARCH_INVALID_CURSOR, `Failed to decode cursor: ${reason}`, { cursor: shown });
@@ -8376,7 +8448,7 @@ function decodeSortKey(value, cursor) {
8376
8448
  continue;
8377
8449
  }
8378
8450
  if (typeof entry === "string") {
8379
- if (exceedsCodePoints(entry, MAX_SORT_VALUE_CODE_POINTS)) {
8451
+ if (exceedsCodePoints(entry, SORT_VALUE_MAX_CODE_POINTS)) {
8380
8452
  throw invalidCursor(cursor, '"k" holds an oversized sort value');
8381
8453
  }
8382
8454
  key.push(entry);
@@ -8409,7 +8481,7 @@ function decodePageCursor(cursor) {
8409
8481
  throw invalidCursor(cursor, '"q" must be 8 lowercase hex digits');
8410
8482
  }
8411
8483
  if (typeof a !== "string" || a.length === 0) throw invalidCursor(cursor, '"a" must be a non-empty string');
8412
- if (exceedsCodePoints(a, MAX_ANCHOR_CODE_POINTS)) {
8484
+ if (exceedsCodePoints(a, MAX_CURSOR_ANCHOR_CODE_POINTS)) {
8413
8485
  throw invalidCursor(cursor, '"a" is too long to be a document id');
8414
8486
  }
8415
8487
  if (s !== void 0 && (typeof s !== "number" || !Number.isFinite(s))) {
@@ -8451,8 +8523,6 @@ function requireMatchingCursor(cursor, encoded, signature, scoreAnchored, bindin
8451
8523
  }
8452
8524
 
8453
8525
  // src/search/pagination.ts
8454
- var RESULT_WINDOW = 1e4;
8455
- var DEFAULT_PAGE_SIZE = 10;
8456
8526
  function clampRowCount(value, fallback) {
8457
8527
  if (value === void 0 || !Number.isFinite(value)) return fallback;
8458
8528
  return Math.max(0, Math.floor(value));
@@ -8524,7 +8594,6 @@ function applyPagination(results, limit, offset, binding, cursor, sort, pinnedId
8524
8594
  }
8525
8595
 
8526
8596
  // src/search/fulltext.ts
8527
- var PREFIX_MAX_EXPANSIONS = 50;
8528
8597
  function resolvePrefixExpansion(partition, params, language, options, queryTokens, lastToken) {
8529
8598
  const term = params.term;
8530
8599
  if (term === void 0) return void 0;
@@ -9171,6 +9240,14 @@ function createPartitionManager(indexName, config, language, router, initialPart
9171
9240
  if (docId !== void 0) docPartitionMap.set(docId, partitionId);
9172
9241
  }
9173
9242
  },
9243
+ freezeLiveTail(partitionId, freeze) {
9244
+ validatePartitionId(partitionId);
9245
+ return asCompositePartition(partitionId).freezeLiveTail(freeze);
9246
+ },
9247
+ replaceLiveTail(partitionId, segment) {
9248
+ validatePartitionId(partitionId);
9249
+ asCompositePartition(partitionId).replaceLiveTail(segment);
9250
+ },
9174
9251
  deserializePartition(partitionId, data) {
9175
9252
  validatePartitionId(partitionId);
9176
9253
  partitions[partitionId].deserialize(data, config.schema);
@@ -9280,10 +9357,10 @@ function createPartitionRouter() {
9280
9357
  }
9281
9358
 
9282
9359
  // src/workers/node-worker.ts
9283
- async function spawnNodeWorker(entryPoint) {
9360
+ async function spawnNodeWorker(entryPoint, resourceLimits) {
9284
9361
  try {
9285
9362
  const workerThreads = await import('worker_threads');
9286
- return new workerThreads.Worker(entryPoint);
9363
+ return new workerThreads.Worker(entryPoint, { resourceLimits });
9287
9364
  } catch {
9288
9365
  return null;
9289
9366
  }
@@ -9332,7 +9409,6 @@ function detectRuntime() {
9332
9409
  }
9333
9410
 
9334
9411
  // src/vector/hnsw-worker-dispatch.ts
9335
- var BUILD_TIMEOUT_MS = 12e4;
9336
9412
  function resolveWorkerEntryPoint() {
9337
9413
  const base = import.meta.url;
9338
9414
  const distIndex = base.lastIndexOf("/dist/");
@@ -9398,7 +9474,7 @@ async function dispatchWorkerBuild(docIds, vectorData, dimension, config, timeou
9398
9474
  message: "No worker thread support available in the current runtime"
9399
9475
  };
9400
9476
  }
9401
- const effectiveTimeout = timeoutMs ?? BUILD_TIMEOUT_MS;
9477
+ const effectiveTimeout = timeoutMs ?? HNSW_BUILD_TIMEOUT_MS;
9402
9478
  let transferBuffer;
9403
9479
  if (skipCopy) {
9404
9480
  transferBuffer = vectorData;
@@ -9468,7 +9544,7 @@ async function dispatchWorkerBuild(docIds, vectorData, dimension, config, timeou
9468
9544
  });
9469
9545
  }
9470
9546
 
9471
- // src/vector/vector-index/shared.ts
9547
+ // src/vector/vector-index/constants.ts
9472
9548
  var DEFAULT_PROMOTION_THRESHOLD = 1024;
9473
9549
  var DEFAULT_FILTER_THRESHOLD = 0.03;
9474
9550
  var ESTIMATED_MS_PER_TOMBSTONE = 0.05;
@@ -9477,6 +9553,9 @@ var WORKER_BUILD_SIZE_THRESHOLD = 5e3;
9477
9553
  var WORKER_COPY_MIN_VECTORS = 1024;
9478
9554
  var BUILD_CHUNK_SIZE = 100;
9479
9555
  var REBUILD_REMOVED_RATIO = 0.2;
9556
+
9557
+ // src/vector/vector-index/shared.ts
9558
+ var VECTOR_WORKER_COPIES_ALLOWED = { enabled: true };
9480
9559
  function liveSize(state) {
9481
9560
  return state.store.size - state.tombstones.size;
9482
9561
  }
@@ -9588,6 +9667,16 @@ function recalibrateFromStore(state) {
9588
9667
  sq8.recalibrateAll(storeVectors());
9589
9668
  }
9590
9669
 
9670
+ // src/workers/constants.ts
9671
+ var WORKER_YOUNG_GENERATION_MB = 24;
9672
+ var MIN_WORKER_COUNT = 2;
9673
+ var MAX_WORKER_COUNT = 8;
9674
+ var FALLBACK_CPU_COUNT = 4;
9675
+ var DEFAULT_BACKPRESSURE_LIMIT = 100;
9676
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
9677
+ var SHUTDOWN_TIMEOUT_MS = 5e3;
9678
+ var CONSECUTIVE_TIMEOUTS_BEFORE_DEATH = 3;
9679
+
9591
9680
  // src/workers/pool.ts
9592
9681
  function toFinite(value) {
9593
9682
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -9596,7 +9685,7 @@ function resolveWorkerCount(requested) {
9596
9685
  if (requested !== void 0 && requested > 0) {
9597
9686
  return requested;
9598
9687
  }
9599
- let cpuCount = 4;
9688
+ let cpuCount = FALLBACK_CPU_COUNT;
9600
9689
  try {
9601
9690
  if (navigator?.hardwareConcurrency) {
9602
9691
  cpuCount = navigator.hardwareConcurrency;
@@ -9607,9 +9696,13 @@ function resolveWorkerCount(requested) {
9607
9696
  }
9608
9697
  }
9609
9698
  } catch {
9610
- cpuCount = 4;
9699
+ cpuCount = FALLBACK_CPU_COUNT;
9611
9700
  }
9612
- return Math.max(2, Math.min(8, cpuCount - 1));
9701
+ return Math.max(MIN_WORKER_COUNT, Math.min(MAX_WORKER_COUNT, cpuCount - 1));
9702
+ }
9703
+ function splitWorkerBudget(total) {
9704
+ const keyword = Math.ceil(total / 2);
9705
+ return { keyword, vector: total - keyword };
9613
9706
  }
9614
9707
  function createWorkerPool(config) {
9615
9708
  const workerCount = resolveWorkerCount(config.count);
@@ -9618,29 +9711,61 @@ function createWorkerPool(config) {
9618
9711
  const indexAssignment = /* @__PURE__ */ new Map();
9619
9712
  let isShutdown = false;
9620
9713
  function handleWorkerDeath(slotIndex, error) {
9621
- if (isShutdown || deadSlots.has(slotIndex)) {
9714
+ if (isShutdown) {
9622
9715
  return;
9623
9716
  }
9624
- deadSlots.add(slotIndex);
9625
9717
  const slot = workers.get(slotIndex);
9718
+ if (slot !== void 0 && !slot.serving) {
9719
+ workers.delete(slotIndex);
9720
+ return;
9721
+ }
9722
+ if (deadSlots.has(slotIndex)) {
9723
+ return;
9724
+ }
9725
+ deadSlots.add(slotIndex);
9626
9726
  const indexNames = slot ? [...slot.indexes].sort() : [];
9627
9727
  workers.delete(slotIndex);
9628
9728
  config.onWorkerCrash?.(slotIndex, indexNames, error);
9629
9729
  }
9630
- function ensureWorker(slotIndex) {
9631
- if (deadSlots.has(slotIndex)) {
9632
- return void 0;
9633
- }
9634
- let slot = workers.get(slotIndex);
9635
- if (!slot) {
9636
- slot = {
9637
- executor: config.workerFactory(slotIndex, (error) => handleWorkerDeath(slotIndex, error)),
9638
- indexes: /* @__PURE__ */ new Set()
9639
- };
9640
- workers.set(slotIndex, slot);
9641
- }
9730
+ function spawnSlot(slotIndex, serving) {
9731
+ const slot = {
9732
+ executor: config.workerFactory(slotIndex, (error) => handleWorkerDeath(slotIndex, error)),
9733
+ indexes: /* @__PURE__ */ new Set(),
9734
+ inFlight: 0,
9735
+ serving
9736
+ };
9737
+ workers.set(slotIndex, slot);
9642
9738
  return slot;
9643
9739
  }
9740
+ function ensureWorker(slotIndex) {
9741
+ const slot = workers.get(slotIndex);
9742
+ if (slot !== void 0) return slot;
9743
+ if (deadSlots.has(slotIndex)) return void 0;
9744
+ return spawnSlot(slotIndex, true);
9745
+ }
9746
+ function deadWorkerIds() {
9747
+ return [...deadSlots].filter((slotIndex) => !workers.has(slotIndex)).sort((a, b) => a - b);
9748
+ }
9749
+ function spawnReplacement(slotIndex) {
9750
+ if (isShutdown || !deadSlots.has(slotIndex) || workers.has(slotIndex)) return null;
9751
+ const slot = spawnSlot(slotIndex, false);
9752
+ return {
9753
+ workerId: slotIndex,
9754
+ executor: slot.executor,
9755
+ hold(indexName) {
9756
+ slot.indexes.add(indexName);
9757
+ },
9758
+ admit() {
9759
+ if (workers.get(slotIndex) !== slot) return;
9760
+ deadSlots.delete(slotIndex);
9761
+ slot.serving = true;
9762
+ },
9763
+ abandon() {
9764
+ if (workers.get(slotIndex) === slot) workers.delete(slotIndex);
9765
+ void slot.executor.shutdown().catch(() => void 0);
9766
+ }
9767
+ };
9768
+ }
9644
9769
  function assignSlot(indexName) {
9645
9770
  const preferred = fnv1a(indexName) % workerCount;
9646
9771
  for (let probe = 0; probe < workerCount; probe++) {
@@ -9679,11 +9804,11 @@ function createWorkerPool(config) {
9679
9804
  throw new NarsilError(ErrorCodes.INDEX_NOT_FOUND, `Index "${indexName}" is not registered in the worker pool`);
9680
9805
  }
9681
9806
  const slot = workers.get(slotIndex);
9682
- if (slot) {
9807
+ if (slot?.serving) {
9683
9808
  return slot.executor;
9684
9809
  }
9685
9810
  for (const survivor of workers.values()) {
9686
- if (survivor.indexes.has(indexName)) {
9811
+ if (survivor.serving && survivor.indexes.has(indexName)) {
9687
9812
  return survivor.executor;
9688
9813
  }
9689
9814
  }
@@ -9728,6 +9853,54 @@ function createWorkerPool(config) {
9728
9853
  function getAllExecutors() {
9729
9854
  return [...workers.values()].map((slot) => slot.executor);
9730
9855
  }
9856
+ function executorsHolding(indexName) {
9857
+ const holders = [];
9858
+ for (const slot of workers.values()) {
9859
+ if (slot.indexes.has(indexName)) holders.push(slot.executor);
9860
+ }
9861
+ return holders;
9862
+ }
9863
+ function lease(workerId, slot) {
9864
+ slot.inFlight += 1;
9865
+ let released = false;
9866
+ return {
9867
+ workerId,
9868
+ executor: slot.executor,
9869
+ release() {
9870
+ if (released) return;
9871
+ released = true;
9872
+ slot.inFlight -= 1;
9873
+ }
9874
+ };
9875
+ }
9876
+ function leaseLeastBusy() {
9877
+ let chosenId = -1;
9878
+ let chosen = null;
9879
+ for (const [workerId, slot] of workers) {
9880
+ if (!slot.serving) continue;
9881
+ if (chosen === null || slot.inFlight < chosen.inFlight) {
9882
+ chosenId = workerId;
9883
+ chosen = slot;
9884
+ }
9885
+ if (chosen.inFlight === 0) break;
9886
+ }
9887
+ return chosen === null ? null : lease(chosenId, chosen);
9888
+ }
9889
+ function queriesInFlight() {
9890
+ let total = 0;
9891
+ for (const slot of workers.values()) {
9892
+ if (slot.serving) total += slot.inFlight;
9893
+ }
9894
+ return total;
9895
+ }
9896
+ function leaseIdle(limit) {
9897
+ const leases = [];
9898
+ for (const [workerId, slot] of workers) {
9899
+ if (leases.length >= limit) break;
9900
+ if (slot.serving && slot.inFlight === 0) leases.push(lease(workerId, slot));
9901
+ }
9902
+ return leases;
9903
+ }
9731
9904
  function addIndexToAll(indexName) {
9732
9905
  if (isShutdown) {
9733
9906
  throw new NarsilError(ErrorCodes.WORKER_CRASHED, "Worker pool has been shut down");
@@ -9737,9 +9910,22 @@ function createWorkerPool(config) {
9737
9910
  }
9738
9911
  indexAssignment.set(indexName, assignSlot(indexName));
9739
9912
  }
9913
+ function spawnAll() {
9914
+ if (isShutdown) {
9915
+ throw new NarsilError(ErrorCodes.WORKER_CRASHED, "Worker pool has been shut down");
9916
+ }
9917
+ for (let i = 0; i < workerCount; i++) ensureWorker(i);
9918
+ }
9740
9919
  return {
9741
9920
  getExecutor,
9742
9921
  getAllExecutors,
9922
+ executorsHolding,
9923
+ leaseLeastBusy,
9924
+ leaseIdle,
9925
+ queriesInFlight,
9926
+ spawnAll,
9927
+ deadWorkerIds,
9928
+ spawnReplacement,
9743
9929
  get workerCount() {
9744
9930
  return workerCount;
9745
9931
  },
@@ -9751,6 +9937,9 @@ function createWorkerPool(config) {
9751
9937
  };
9752
9938
  }
9753
9939
 
9940
+ // src/workers/resource-limits.ts
9941
+ var WORKER_RESOURCE_LIMITS = { maxYoungGenerationSizeMb: WORKER_YOUNG_GENERATION_MB };
9942
+
9754
9943
  // src/vector/search-pool/spawn.ts
9755
9944
  function resolveWorkerEntryPoint2() {
9756
9945
  const base = import.meta.url;
@@ -9764,7 +9953,7 @@ async function spawnWorker2(entryPoint) {
9764
9953
  const runtime = detectRuntime();
9765
9954
  if (runtime.supportsWorkerThreads) {
9766
9955
  try {
9767
- return await spawnNodeWorker(new URL(entryPoint));
9956
+ return await spawnNodeWorker(new URL(entryPoint), WORKER_RESOURCE_LIMITS);
9768
9957
  } catch {
9769
9958
  return null;
9770
9959
  }
@@ -9799,8 +9988,6 @@ function listenForFailure(worker, handler) {
9799
9988
  }
9800
9989
 
9801
9990
  // src/vector/search-pool/index.ts
9802
- var SEARCH_TIMEOUT_MS = 3e4;
9803
- var LOAD_TIMEOUT_MS = 3e5;
9804
9991
  async function createVectorSearchPool(requestedCount) {
9805
9992
  const entryPoint = resolveWorkerEntryPoint2();
9806
9993
  const count = resolveWorkerCount(requestedCount);
@@ -9863,7 +10050,7 @@ async function createVectorSearchPool(requestedCount) {
9863
10050
  async function sendSearch(slot, request) {
9864
10051
  slot.outstanding += 1;
9865
10052
  try {
9866
- return await send(slot, request.requestId, request, SEARCH_TIMEOUT_MS);
10053
+ return await send(slot, request.requestId, request, VECTOR_SEARCH_TIMEOUT_MS);
9867
10054
  } finally {
9868
10055
  slot.outstanding -= 1;
9869
10056
  }
@@ -9880,7 +10067,7 @@ async function createVectorSearchPool(requestedCount) {
9880
10067
  slots.map((slot) => {
9881
10068
  requestCounter += 1;
9882
10069
  const requestId = `${requestCounter}`;
9883
- return send(slot, requestId, { type: "load", requestId, handle, snapshot }, LOAD_TIMEOUT_MS);
10070
+ return send(slot, requestId, { type: "load", requestId, handle, snapshot }, VECTOR_SEARCH_LOAD_TIMEOUT_MS);
9884
10071
  })
9885
10072
  );
9886
10073
  return outcomes.every(
@@ -9896,7 +10083,7 @@ async function createVectorSearchPool(requestedCount) {
9896
10083
  slot,
9897
10084
  requestId,
9898
10085
  { type: "loadShared", requestId, handle, scratchSlot, snapshot },
9899
- LOAD_TIMEOUT_MS
10086
+ VECTOR_SEARCH_LOAD_TIMEOUT_MS
9900
10087
  );
9901
10088
  })
9902
10089
  );
@@ -9909,7 +10096,7 @@ async function createVectorSearchPool(requestedCount) {
9909
10096
  slots.map((slot) => {
9910
10097
  requestCounter += 1;
9911
10098
  const requestId = `${requestCounter}`;
9912
- return send(slot, requestId, { type: "drop", requestId, handle }, SEARCH_TIMEOUT_MS);
10099
+ return send(slot, requestId, { type: "drop", requestId, handle }, VECTOR_SEARCH_TIMEOUT_MS);
9913
10100
  })
9914
10101
  );
9915
10102
  },
@@ -9977,11 +10164,11 @@ async function createVectorSearchPool(requestedCount) {
9977
10164
  var sharedPool = null;
9978
10165
  var poolHolders = 0;
9979
10166
  var workersUnavailable = false;
9980
- function acquireVectorSearchPool() {
10167
+ function acquireVectorSearchPool(requestedCount) {
9981
10168
  poolHolders += 1;
9982
10169
  if (workersUnavailable) return Promise.resolve(null);
9983
10170
  if (sharedPool === null) {
9984
- sharedPool = createVectorSearchPool().then(
10171
+ sharedPool = createVectorSearchPool(requestedCount).then(
9985
10172
  (pool) => {
9986
10173
  if (pool === null) workersUnavailable = true;
9987
10174
  return pool;
@@ -10005,8 +10192,6 @@ async function releaseVectorSearchPool() {
10005
10192
  }
10006
10193
 
10007
10194
  // src/vector/shared-generation/layout.ts
10008
- var WASM_PAGE_BYTES = 65536;
10009
- var MAX_WASM_PAGES = 65536;
10010
10195
  var SCRATCH_ALIGNMENT = 16;
10011
10196
  function alignUp(bytes) {
10012
10197
  return Math.ceil(bytes / SCRATCH_ALIGNMENT) * SCRATCH_ALIGNMENT;
@@ -10148,6 +10333,7 @@ function captureCloneSnapshot(state) {
10148
10333
  };
10149
10334
  }
10150
10335
  function scheduleWorkerCopyLoad(state) {
10336
+ if (!state.workerCopies.enabled) return;
10151
10337
  if (state.disposed || state.workerCopyLoading || state.building) return;
10152
10338
  if (state.workerCopyHandle !== null) return;
10153
10339
  if (!state.hnsw || state.buffer.size > 0) return;
@@ -10159,7 +10345,7 @@ function scheduleWorkerCopyLoad(state) {
10159
10345
  }
10160
10346
  async function loadWorkerCopies(state) {
10161
10347
  const revision = state.revision;
10162
- const pool = await acquireVectorSearchPool();
10348
+ const pool = await acquireVectorSearchPool(state.workerCopies.count);
10163
10349
  if (pool === null) {
10164
10350
  await releaseVectorSearchPool();
10165
10351
  return;
@@ -10760,7 +10946,7 @@ function searchWithFilter(state, query, k, options, filter) {
10760
10946
  }
10761
10947
 
10762
10948
  // src/vector/vector-index/index.ts
10763
- function createVectorIndex(fieldName, dimension, config) {
10949
+ function createVectorIndex(fieldName, dimension, config, workerCopies = VECTOR_WORKER_COPIES_ALLOWED) {
10764
10950
  if (!Number.isInteger(dimension) || dimension <= 0) {
10765
10951
  throw new NarsilError(
10766
10952
  ErrorCodes.VECTOR_DIMENSION_MISMATCH,
@@ -10783,6 +10969,7 @@ function createVectorIndex(fieldName, dimension, config) {
10783
10969
  filterThreshold,
10784
10970
  quantizationMode,
10785
10971
  hnswConfig,
10972
+ workerCopies,
10786
10973
  store,
10787
10974
  tombstones: /* @__PURE__ */ new Set(),
10788
10975
  buffer: /* @__PURE__ */ new Set(),
@@ -10925,8 +11112,15 @@ function createVectorIndex(fieldName, dimension, config) {
10925
11112
  }
10926
11113
 
10927
11114
  // src/workers/direct-executor.ts
10928
- function createDirectExecutor() {
11115
+ var NO_VECTOR_WORKER_COPIES = { enabled: false };
11116
+ function growPartitionsTo(manager, partitionId) {
11117
+ while (manager.partitionCount <= partitionId) {
11118
+ manager.addPartition();
11119
+ }
11120
+ }
11121
+ function createDirectExecutor(options) {
10929
11122
  const indexes = /* @__PURE__ */ new Map();
11123
+ const vectorWorkerCopies = options?.vectorWorkerCopies ?? NO_VECTOR_WORKER_COPIES;
10930
11124
  function requireIndex(indexName) {
10931
11125
  const entry = indexes.get(indexName);
10932
11126
  if (!entry) {
@@ -10947,7 +11141,7 @@ function createDirectExecutor() {
10947
11141
  const vectorFields = extractVectorFieldsFromSchema(config.schema);
10948
11142
  const vectorIndexes = /* @__PURE__ */ new Map();
10949
11143
  for (const [fieldPath, dim] of vectorFields) {
10950
- vectorIndexes.set(fieldPath, createVectorIndex(fieldPath, dim, config.vectorPromotion));
11144
+ vectorIndexes.set(fieldPath, createVectorIndex(fieldPath, dim, config.vectorPromotion, vectorWorkerCopies));
10951
11145
  }
10952
11146
  const manager = createPartitionManager(indexName, config, language, router, partitionCount, vectorIndexes);
10953
11147
  indexes.set(indexName, {
@@ -11004,10 +11198,10 @@ function createDirectExecutor() {
11004
11198
  case "buildSegment": {
11005
11199
  const entry = requireIndex(action.indexName);
11006
11200
  const segment = createPartitionIndex(0, entry.config.trackPositions ?? true);
11007
- const options = resolvePartitionInsertOptions(entry.config, entry.manager.analysis, action.options);
11201
+ const options2 = resolvePartitionInsertOptions(entry.config, entry.manager.analysis, action.options);
11008
11202
  segment.beginBatch();
11009
11203
  for (const doc of action.documents) {
11010
- segment.insert(doc.docId, doc.document, entry.config.schema, entry.language, options);
11204
+ segment.insert(doc.docId, doc.document, entry.config.schema, entry.language, options2);
11011
11205
  }
11012
11206
  segment.endBatch();
11013
11207
  return segment.encodeSegment();
@@ -11028,10 +11222,10 @@ function createDirectExecutor() {
11028
11222
  case "attachSegments": {
11029
11223
  const entry = requireIndex(action.indexName);
11030
11224
  for (const segment of action.segments) {
11031
- while (entry.manager.partitionCount <= segment.partitionId) {
11032
- entry.manager.addPartition();
11033
- }
11034
- entry.manager.attachFrozenSegment(segment.partitionId, createSharedFrozenSegment(segment.snapshot));
11225
+ growPartitionsTo(entry.manager, segment.partitionId);
11226
+ const frozen = createSharedFrozenSegment(segment.snapshot);
11227
+ for (const docId of segment.tombstonedDocIds ?? []) frozen.tombstoneDocument(docId);
11228
+ entry.manager.attachFrozenSegment(segment.partitionId, frozen);
11035
11229
  }
11036
11230
  return void 0;
11037
11231
  }
@@ -11062,6 +11256,19 @@ function createDirectExecutor() {
11062
11256
  partition.swapFrozenSegments(action.dropSegmentIds, createSharedFrozenSegment(action.snapshot));
11063
11257
  return void 0;
11064
11258
  }
11259
+ case "freezeLiveTail": {
11260
+ const entry = requireIndex(action.indexName);
11261
+ growPartitionsTo(entry.manager, action.partitionId);
11262
+ const tail = entry.manager.getPartition(action.partitionId);
11263
+ const held = isCompositePartition(tail) ? tail.live.count() : tail.count();
11264
+ if (held !== action.snapshot.documentCount) {
11265
+ console.warn(
11266
+ `Partition ${action.partitionId} of "${action.indexName}" held ${held} live documents where the frozen tail holds ${action.snapshot.documentCount}`
11267
+ );
11268
+ }
11269
+ entry.manager.replaceLiveTail(action.partitionId, createSharedFrozenSegment(action.snapshot));
11270
+ return void 0;
11271
+ }
11065
11272
  case "remove": {
11066
11273
  const entry = requireIndex(action.indexName);
11067
11274
  entry.manager.remove(action.docId);
@@ -11137,7 +11344,10 @@ function createDirectExecutor() {
11137
11344
  const vectorFields = extractVectorFieldsFromSchema(entry.config.schema);
11138
11345
  const newVectorIndexes = /* @__PURE__ */ new Map();
11139
11346
  for (const [fieldPath, dim] of vectorFields) {
11140
- newVectorIndexes.set(fieldPath, createVectorIndex(fieldPath, dim, entry.config.vectorPromotion));
11347
+ newVectorIndexes.set(
11348
+ fieldPath,
11349
+ createVectorIndex(fieldPath, dim, entry.config.vectorPromotion, vectorWorkerCopies)
11350
+ );
11141
11351
  }
11142
11352
  entry.manager.resetVectorIndexes(newVectorIndexes);
11143
11353
  entry.vectorIndexes = entry.manager.getVectorIndexes();
@@ -11149,9 +11359,7 @@ function createDirectExecutor() {
11149
11359
  }
11150
11360
  case "deserialize": {
11151
11361
  const entry = requireIndex(action.indexName);
11152
- while (entry.manager.partitionCount <= action.partitionId) {
11153
- entry.manager.addPartition();
11154
- }
11362
+ growPartitionsTo(entry.manager, action.partitionId);
11155
11363
  entry.manager.deserializePartition(action.partitionId, action.data);
11156
11364
  return void 0;
11157
11365
  }
@@ -11183,4 +11391,4 @@ function createDirectExecutor() {
11183
11391
  };
11184
11392
  }
11185
11393
 
11186
- 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 };
11394
+ export { CHECKSUM_TIMEOUT_MS, CHECKSUM_YIELD_CHUNK_BYTES, CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, FIELD_FILTER_OPERATORS, FILTER_EXPRESSION_KEYS, MAX_NESTING_DEPTH, SHUTDOWN_TIMEOUT_MS, WORKER_RESOURCE_LIMITS, applyPagination, applyProjection, applySorting, buildCompactedSegmentPayload, clampRowCount, clearNormalizationCache, collectQueryTermStats, configureNormalizationCache, countsWithoutScores, createDirectExecutor, createFrozenSegment, createPartitionIndex, createPartitionManager, createPartitionRouter, createSharedFrozenSegment, createVectorIndex, createWorkerPool, decodePageCursor, deserializeMetadata, deserializePayloadV1, deserializePayloadV2, detectRuntime, encodePageCursor, encodeSegmentState, 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, resolveWorkerCount, serializeMetadata, sortSignatureOf, spawnNodeWorker, splitWorkerBudget, tokenize, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion };