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

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 (57) hide show
  1. package/dist/adapters/filesystem.mjs +1 -1
  2. package/dist/adapters/indexeddb.mjs +1 -1
  3. package/dist/{capabilities-Dt-7B4Wz.d.ts → capabilities-D6gjjQyA.d.ts} +2 -2
  4. package/dist/{chunk-P4DSQ6M4.mjs → chunk-2X2ZQOAB.mjs} +69 -28
  5. package/dist/chunk-3HQPUCCT.mjs +785 -0
  6. package/dist/{chunk-VNMTB3KC.mjs → chunk-7QMTA7AA.mjs} +194 -59
  7. package/dist/{chunk-FVLQ63B7.mjs → chunk-CVPA5Q7R.mjs} +24 -3
  8. package/dist/{chunk-2STVUTNA.mjs → chunk-EDMCZLSJ.mjs} +4 -3
  9. package/dist/{chunk-M76V3OGJ.mjs → chunk-HVJYTKK7.mjs} +940 -647
  10. package/dist/{chunk-HWQLTLNG.mjs → chunk-KZTI25D3.mjs} +1 -1
  11. package/dist/{chunk-YJDQ7NLA.mjs → chunk-LCCL6EEH.mjs} +3 -3
  12. package/dist/{chunk-OI42GWLR.mjs → chunk-LJYTW7GT.mjs} +57 -11
  13. package/dist/{chunk-653BNMAF.mjs → chunk-M6XF5HDE.mjs} +57 -17
  14. package/dist/{chunk-YNVIUGF5.mjs → chunk-P2ZZN32C.mjs} +1 -1
  15. package/dist/{chunk-LMO5TJUS.mjs → chunk-QR4OKRVY.mjs} +637 -196
  16. package/dist/{chunk-USKFU7J5.mjs → chunk-RV77FR2F.mjs} +1 -0
  17. package/dist/{chunk-GIVAGD3Q.mjs → chunk-XZ4PAREY.mjs} +1 -1
  18. package/dist/{chunk-3JGWULDF.mjs → chunk-Y52WZWAU.mjs} +5 -5
  19. package/dist/chunk-ZKRV5M2P.mjs +75 -0
  20. package/dist/{client-COek5GXK.d.ts → client-C2TiW29y.d.ts} +3 -3
  21. package/dist/client.d.ts +8 -8
  22. package/dist/client.mjs +1 -0
  23. package/dist/{config-rBpbgTw8.d.ts → config-BLguB-om.d.ts} +2 -2
  24. package/dist/{config-Dfanyqqt.d.ts → config-C7Ip8Ife.d.ts} +17 -2
  25. package/dist/direct-executor-SVCHXR74.mjs +14 -0
  26. package/dist/distribution/coordinator/etcd.d.ts +2 -2
  27. package/dist/distribution/coordinator/etcd.mjs +2 -2
  28. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  29. package/dist/distribution/coordinator/in-memory.mjs +1 -1
  30. package/dist/distribution/transport/grpc.mjs +1 -1
  31. package/dist/distribution.d.ts +8 -8
  32. package/dist/distribution.mjs +58 -35
  33. package/dist/embeddings/openai.mjs +1 -1
  34. package/dist/{engine-CXKfmK7I.d.ts → engine-Dm4446j1.d.ts} +104 -62
  35. package/dist/index.browser.mjs +2222 -1100
  36. package/dist/index.d.ts +27 -8
  37. package/dist/index.mjs +14 -12
  38. package/dist/invalidation/filesystem.mjs +1 -1
  39. package/dist/{memory-C7UMCMH3.d.ts → memory-CKHTU1CL.d.ts} +66 -25
  40. package/dist/pattern-Cledccbl.d.ts +18 -0
  41. package/dist/pattern.d.ts +22 -0
  42. package/dist/pattern.mjs +7 -0
  43. package/dist/persistence/durability/checkpoint-worker.d.ts +17 -1
  44. package/dist/persistence/durability/checkpoint-worker.mjs +14 -12
  45. package/dist/react.d.ts +277 -182
  46. package/dist/react.mjs +190 -45
  47. package/dist/{schema-Bgzr4g7b.d.ts → schema-BBOCdhND.d.ts} +9 -7
  48. package/dist/server.d.ts +10 -10
  49. package/dist/server.mjs +13 -12
  50. package/dist/{tasks-BZt_zes6.d.ts → tasks-BECd0b__.d.ts} +1 -1
  51. package/dist/{thread-JEVAYETR.mjs → thread-IBSIZZ6W.mjs} +22 -13
  52. package/dist/{types-gOUqhs2F.d.ts → types-BiUmaiWT.d.ts} +1 -1
  53. package/dist/vector/search-worker.d.ts +1 -1
  54. package/dist/vector/search-worker.mjs +4 -4
  55. package/dist/workers/entry.mjs +4 -4
  56. package/package.json +15 -7
  57. package/dist/direct-executor-GYJEKNV5.mjs +0 -12
@@ -1,18 +1,20 @@
1
+ import { validateWorkerConfig, validateIndexName, validatePatternValueLimit, validateBM25Params, validatePartitionConfig, executeQuery, queryBindingOf, executePreflight, executeSuggest, executeListDocuments, validateDocId } from './chunk-2X2ZQOAB.mjs';
1
2
  import { ReplicationMessageTypes } from './chunk-GCM3JFVA.mjs';
2
- import { WIRE_BATCH_BUDGET, resolveVectorText, bindEngineCore, restoredConfigFields, restoredEmbedding, collectRequiredFieldFailures, embedChunkDocuments, providedDocId, admitInsert, asBatchInsertError, embedDocumentFields, assertDocumentCarriesMappedVectors, admitBufferedUpsert, createPendingAdmissions, recordPendingAdmission } from './chunk-FVLQ63B7.mjs';
3
- import { generateId, splitWorkerBudget, resolveWorkerCount, createDirectExecutor, createPartitionRouter, createCompositePartition, deserializePayloadV2, createSharedFrozenSegment, createPartitionManager, buildSegmentPayload, resolvePartitionInsertOptions, resolveWorkerEntry, missingWorkerEntry, readHeapStatistics, resolveIndexAnalysis, deserializePayloadV1, decodeVectorIndexParts, nodeThreadId, resolveRequestThreadCount, createPartitionIndex, VECTOR_FILE_MAX_VECTORS, deadBytesFor, mergeFrozenSegments, isCompositePartition, workerResourceLimits, deserializeMetadata, isNodeMainThread, spawnNodeWorker, freezeSegmentShared, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, SHUTDOWN_TIMEOUT_MS, serializeMetadata, encodeSegmentState, CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, decodeVectorFilePayload, documentsOfPayloadV2, countDead, decodeVectorGraphPayload, deadBitsWhere, NO_LIVE_VECTOR_AT_POSITION, createFrozenSegment } from './chunk-M76V3OGJ.mjs';
4
- import { getLanguage, detectRuntime } from './chunk-HWQLTLNG.mjs';
5
- import { validateWorkerConfig, validateIndexName, validatePatternValueLimit, validateBM25Params, validatePartitionConfig, executeQuery, queryBindingOf, executePreflight, executeSuggest, executeListDocuments, validateDocId } from './chunk-P4DSQ6M4.mjs';
6
- import { BATCH_CHUNK_SIZE, STATISTICS_BROADCAST_INTERVAL_MS, MAX_SWEEP_INTERVAL_MS, MIN_SWEEP_INTERVAL_MS, HEAP_PRESSURE_WARNING_FRACTION, FOREIGN_STATS_TTL_MS, MIN_DOCUMENTS_FOR_SEGMENTS, HEAP_PRESSURE_REARM_FRACTION } from './chunk-GIVAGD3Q.mjs';
7
- import { getNestedValue, collectQueryTermStats, mergePartitionStats, oversampledFacetConfig, fnv1a, kWayMerge, mergeFacets } from './chunk-LMO5TJUS.mjs';
8
- import { extractVectorFieldsFromSchema, validateSchema, validateVectorPromotion, validateVectorStorage, flattenSchema, isSingleStringFieldType, requireValidFieldTypes, validateRequiredFields, validateDocument, validateDocumentStrict } from './chunk-653BNMAF.mjs';
9
- import { releaseLocksHeldBy } from './chunk-YJDQ7NLA.mjs';
10
- import { requireSearchBackendSettings } from './chunk-YNVIUGF5.mjs';
3
+ import { WIRE_BATCH_BUDGET, resolveVectorText, bindEngineCore, restoredConfigFields, restoredEmbedding, collectRequiredFieldFailures, embedChunkDocuments, providedDocId, requireDocumentObject, admitInsert, asBatchInsertError, requireMatchingDocId, embedDocumentFields, assertDocumentCarriesMappedVectors, admitBufferedUpsert, createPendingAdmissions, recordPendingAdmission } from './chunk-CVPA5Q7R.mjs';
4
+ import { BATCH_CHUNK_SIZE, STATISTICS_BROADCAST_INTERVAL_MS, MAX_SWEEP_INTERVAL_MS, MIN_SWEEP_INTERVAL_MS, HEAP_PRESSURE_WARNING_FRACTION, FOREIGN_STATS_TTL_MS, MIN_DOCUMENTS_FOR_SEGMENTS, HEAP_PRESSURE_REARM_FRACTION } from './chunk-XZ4PAREY.mjs';
5
+ import { generateId, splitWorkerBudget, resolveWorkerCount, createDirectExecutor, createPartitionRouter, requirePatternSearchFor, createCompositePartition, deserializePayloadV2, createSharedFrozenSegment, createPartitionManager, buildSegmentPayload, resolvePartitionInsertOptions, resolveWorkerEntry, missingWorkerEntry, readHeapStatistics, resolveIndexAnalysis, deserializePayloadV1, decodeVectorIndexParts, nodeThreadId, resolveRequestThreadCount, createPartitionIndex, VECTOR_FILE_MAX_VECTORS, deadBytesFor, mergeFrozenSegments, isCompositePartition, workerResourceLimits, deserializeMetadata, isNodeMainThread, spawnNodeWorker, freezeSegmentShared, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, SHUTDOWN_TIMEOUT_MS, serializeMetadata, encodeSegmentState, CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, decodeVectorFilePayload, documentsOfPayloadV2, countDead, decodeVectorGraphPayload, deadBitsWhere, NO_LIVE_VECTOR_AT_POSITION, createFrozenSegment } from './chunk-HVJYTKK7.mjs';
6
+ import { getNestedValue, collectQueryTermStats, mergePartitionStats, oversampledFacetConfig, fnv1a, kWayMerge, mergeFacets } from './chunk-QR4OKRVY.mjs';
7
+ import { getLanguage, detectRuntime } from './chunk-KZTI25D3.mjs';
8
+ import { extractVectorFieldsFromSchema, validateSchema, validateVectorPromotion, validateVectorStorage, flattenSchema, isSingleStringFieldType, requireValidFieldTypes, schemaFieldsOf, validateRequiredFields, validateDocument, validateDocumentStrict } from './chunk-M6XF5HDE.mjs';
9
+ import { releaseLocksHeldBy } from './chunk-LCCL6EEH.mjs';
10
+ import { requireSearchBackendSettings } from './chunk-P2ZZN32C.mjs';
11
11
  import { crc32, nativeCrc32, crc32Final, crc32Init, crc32Update } from './chunk-3L4U6EJQ.mjs';
12
12
  import { CHECKSUM_TIMEOUT_MS, CHECKSUM_YIELD_CHUNK_BYTES } from './chunk-IFBKLBGB.mjs';
13
- import { compareCodePoints } from './chunk-OI42GWLR.mjs';
13
+ import { widestPatternSplit } from './chunk-3HQPUCCT.mjs';
14
+ import { DEFAULT_PATTERN_WORK_CAP, PATTERN_TEST_OPERATORS } from './chunk-ZKRV5M2P.mjs';
15
+ import { compareCodePoints, foldedCodePointCount, codePointCount } from './chunk-LJYTW7GT.mjs';
14
16
  import { createRequestId, isValidWorkerResponse, threadSlotOfWorker } from './chunk-POWOTUKJ.mjs';
15
- import { ErrorCodes, NarsilError } from './chunk-USKFU7J5.mjs';
17
+ import { ErrorCodes, NarsilError } from './chunk-RV77FR2F.mjs';
16
18
  import { encode, decode } from '@msgpack/msgpack';
17
19
 
18
20
  // src/engine/config-keys.ts
@@ -26,7 +28,8 @@ var NARSIL_CONFIG_KEYS = {
26
28
  embeddingAdapters: true,
27
29
  durability: true,
28
30
  analysis: true,
29
- lifecycle: true
31
+ lifecycle: true,
32
+ patternWorkCap: true
30
33
  };
31
34
  var DURABILITY_CONFIG_KEYS = {
32
35
  tier: true,
@@ -91,6 +94,16 @@ function requireKnownConfig(config) {
91
94
  requireKnownKeys("lifecycle.", config.lifecycle, LIFECYCLE_CONFIG_KEYS);
92
95
  requireKnownKeys("analysis.", config.analysis, ANALYSIS_CONFIG_KEYS);
93
96
  requireNamedPlugins(config.plugins);
97
+ requireValidPatternWorkCap(config.patternWorkCap);
98
+ }
99
+ function requireValidPatternWorkCap(cap) {
100
+ if (cap === void 0) return;
101
+ if (typeof cap === "number" && Number.isSafeInteger(cap) && cap >= 1) return;
102
+ throw new NarsilError(
103
+ ErrorCodes.CONFIG_INVALID,
104
+ `The configuration sets "patternWorkCap" to ${String(cap)}, while the cap must be a whole number from 1 to ${Number.MAX_SAFE_INTEGER}`,
105
+ { setting: "patternWorkCap" }
106
+ );
94
107
  }
95
108
 
96
109
  // src/engine/orchestration/turn.ts
@@ -8174,6 +8187,49 @@ function stopIdleSweep(state) {
8174
8187
  state.idleSweep = null;
8175
8188
  }
8176
8189
 
8190
+ // src/core/pattern-index/query-positions.ts
8191
+ function isRecord3(value) {
8192
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8193
+ }
8194
+ function textPositions(text, caseFold) {
8195
+ if (typeof text !== "string") return 0;
8196
+ return caseFold ? foldedCodePointCount(text) : codePointCount(text);
8197
+ }
8198
+ function larger(left, right) {
8199
+ if (left === null) return right;
8200
+ if (right === null) return left;
8201
+ return Math.max(left, right);
8202
+ }
8203
+ function largestTestOf(filter) {
8204
+ const caseFold = filter.caseFold === true;
8205
+ let largest = null;
8206
+ for (const operator of PATTERN_TEST_OPERATORS) {
8207
+ const operand = filter[operator];
8208
+ if (operand === void 0) continue;
8209
+ const texts = Array.isArray(operand) ? operand : [operand];
8210
+ for (const text of texts) largest = larger(largest, textPositions(text, caseFold));
8211
+ }
8212
+ return largest;
8213
+ }
8214
+ function largestIn(expression, patternFields) {
8215
+ let largest = null;
8216
+ if (isRecord3(expression.fields)) {
8217
+ for (const [fieldPath, filter] of Object.entries(expression.fields)) {
8218
+ if (patternFields.has(fieldPath) && isRecord3(filter)) largest = larger(largest, largestTestOf(filter));
8219
+ }
8220
+ }
8221
+ const nested = [...expression.and ?? [], ...expression.or ?? []];
8222
+ if (expression.not !== void 0) nested.push(expression.not);
8223
+ for (const child of nested) largest = larger(largest, largestIn(child, patternFields));
8224
+ return largest;
8225
+ }
8226
+ function largestPatternTest(expression, schema) {
8227
+ if (expression === void 0) return null;
8228
+ const patternFields = /* @__PURE__ */ new Set();
8229
+ for (const { path, pattern } of schemaFieldsOf(schema)) if (pattern) patternFields.add(path);
8230
+ return patternFields.size === 0 ? null : largestIn(expression, patternFields);
8231
+ }
8232
+
8177
8233
  // src/engine/orchestration/search.ts
8178
8234
  function partitionsPerLease(scope, leaseCount) {
8179
8235
  const assignments = Array.from({ length: leaseCount }, () => []);
@@ -8185,15 +8241,18 @@ function partitionsPerLease(scope, leaseCount) {
8185
8241
  function mergeWorkerResults(results, facetConfig) {
8186
8242
  const merged = kWayMerge(results.map((result) => result.scored));
8187
8243
  let totalMatched = 0;
8244
+ let patternWorkSpent = 0;
8188
8245
  const workerFacets = [];
8189
8246
  for (const result of results) {
8190
8247
  totalMatched += result.totalMatched;
8248
+ patternWorkSpent += result.patternWorkSpent ?? 0;
8191
8249
  if (result.facets !== void 0) workerFacets.push(result.facets);
8192
8250
  }
8193
8251
  return {
8194
8252
  scored: merged,
8195
8253
  totalMatched,
8196
- facets: facetConfig !== void 0 && workerFacets.length > 0 ? mergeFacets(workerFacets, facetConfig) : void 0
8254
+ facets: facetConfig !== void 0 && workerFacets.length > 0 ? mergeFacets(workerFacets, facetConfig) : void 0,
8255
+ patternWorkSpent
8197
8256
  };
8198
8257
  }
8199
8258
  function scoresPerPartition(state, indexName, params, globalStats) {
@@ -8201,26 +8260,45 @@ function scoresPerPartition(state, indexName, params, globalStats) {
8201
8260
  if (mode === "local") return true;
8202
8261
  return mode === "broadcast" && globalStats !== void 0;
8203
8262
  }
8204
- function queryAction(indexName, params, globalStats, partitionIds) {
8263
+ function queryAction(indexName, params, globalStats, partitionIds, patternWork) {
8205
8264
  return {
8206
8265
  type: "query",
8207
8266
  indexName,
8208
8267
  params,
8209
8268
  requestId: createRequestId(),
8269
+ patternWork,
8210
8270
  ...partitionIds !== void 0 ? { partitionIds } : {},
8211
8271
  ...globalStats !== void 0 ? { globalStats } : {}
8212
8272
  };
8213
8273
  }
8214
- async function runSplit(leases, scope, indexName, params, globalStats) {
8274
+ async function runSplit(leases, scope, indexName, params, globalStats, patternWork) {
8215
8275
  const assignments = partitionsPerLease(scope, leases.length);
8216
8276
  const workerParams = params.facets !== void 0 ? { ...params, facets: oversampledFacetConfig(params.facets) } : params;
8217
- const results = await Promise.all(
8277
+ const outcomes = await Promise.allSettled(
8218
8278
  assignments.map(
8219
- (partitionIds, at) => leases[at].executor.execute(queryAction(indexName, workerParams, globalStats, partitionIds)).finally(() => leases[at].release())
8279
+ (partitionIds, at) => leases[at].executor.execute(queryAction(indexName, workerParams, globalStats, partitionIds, patternWork)).finally(() => leases[at].release())
8220
8280
  )
8221
8281
  );
8282
+ const results = [];
8283
+ const failures = [];
8284
+ for (const outcome of outcomes) {
8285
+ if (outcome.status === "fulfilled") results.push(outcome.value);
8286
+ else failures.push(outcome.reason);
8287
+ }
8288
+ if (failures.length > 0) throw failures.find(isWorkCapError) ?? failures[0];
8222
8289
  return mergeWorkerResults(results, params.facets);
8223
8290
  }
8291
+ function isWorkCapError(err) {
8292
+ return err instanceof NarsilError && err.code === ErrorCodes.SEARCH_WORK_CAP_EXCEEDED;
8293
+ }
8294
+ function patternSplitWidth(state, indexName, params) {
8295
+ const schema = state.indexRegistry.get(indexName)?.config.schema;
8296
+ if (schema === void 0) return Number.POSITIVE_INFINITY;
8297
+ const largest = largestPatternTest(params.filters, schema);
8298
+ if (largest === null) return Number.POSITIVE_INFINITY;
8299
+ if (typeof SharedArrayBuffer !== "function") return 1;
8300
+ return widestPatternSplit(largest);
8301
+ }
8224
8302
  function takeMainCopyTurn(state) {
8225
8303
  if (state.mainCopyQueries === "none" || state.mainCopyTurnTaken) return false;
8226
8304
  state.mainCopyTurnTaken = true;
@@ -8234,7 +8312,7 @@ function answersFasterOnMainCopy(state, pool, manager) {
8234
8312
  if (manager.countDocuments() > MAIN_COPY_LONE_QUERY_DOCUMENTS) return false;
8235
8313
  return takeMainCopyTurn(state);
8236
8314
  }
8237
- async function searchViaWorker(state, indexName, params, globalStats, partitionIds) {
8315
+ async function searchViaWorker(state, indexName, params, globalStats, partitionIds, patternWork) {
8238
8316
  const pool = state.workerPool;
8239
8317
  if (!pool) return null;
8240
8318
  if (!state.scaledOutIndexes.has(indexName) || state.copyLoadBuffers.has(indexName)) return null;
@@ -8242,19 +8320,30 @@ async function searchViaWorker(state, indexName, params, globalStats, partitionI
8242
8320
  if (!manager) return null;
8243
8321
  if (answersFasterOnMainCopy(state, pool, manager)) return null;
8244
8322
  const scope = partitionIds ?? Array.from({ length: manager.partitionCount }, (_, partitionId) => partitionId);
8245
- const idle = pool.leaseIdle(scope.length);
8323
+ const idle = pool.leaseIdle(Math.min(scope.length, patternSplitWidth(state, indexName, params)));
8246
8324
  const leases = [];
8325
+ let fork = null;
8247
8326
  try {
8248
8327
  if (idle.length >= 2 && scoresPerPartition(state, indexName, params, globalStats)) {
8249
8328
  leases.push(...idle);
8250
- return await runSplit(leases, scope, indexName, params, globalStats);
8329
+ fork = patternWork.fork(leases.length);
8330
+ const merged = await runSplit(leases, scope, indexName, params, globalStats, fork.shared);
8331
+ fork.settle(merged.patternWorkSpent ?? 0);
8332
+ return merged;
8251
8333
  }
8252
8334
  for (const lease2 of idle.slice(1)) lease2.release();
8253
8335
  const lease = idle[0] ?? (takeMainCopyTurn(state) ? null : pool.leaseLeastBusy());
8254
8336
  if (lease === null) return null;
8255
8337
  leases.push(lease);
8256
- return await lease.executor.execute(queryAction(indexName, params, globalStats, partitionIds));
8338
+ fork = patternWork.fork(1);
8339
+ const result = await lease.executor.execute(
8340
+ queryAction(indexName, params, globalStats, partitionIds, fork.shared)
8341
+ );
8342
+ fork.settle(result.patternWorkSpent ?? 0);
8343
+ return result;
8257
8344
  } catch (err) {
8345
+ if (isWorkCapError(err)) throw err;
8346
+ fork?.settle(0);
8258
8347
  console.warn("Worker search failed, falling back to local:", err);
8259
8348
  return null;
8260
8349
  } finally {
@@ -8483,7 +8572,7 @@ function createWorkerOrchestrator(config, executor, indexRegistry, callbacks, ve
8483
8572
  segmentBuildConcurrency: (indexName) => segmentBuildConcurrency(state, indexName),
8484
8573
  holdUnbroadcastSegments: (indexName, segmentIds) => holdUnbroadcastSegments(state, indexName, segmentIds),
8485
8574
  releaseUnbroadcastSegments: (indexName, segmentIds) => releaseUnbroadcastSegments(state, indexName, segmentIds),
8486
- searchViaWorker: (indexName, params, globalStats, partitionIds) => searchViaWorker(state, indexName, params, globalStats, partitionIds),
8575
+ searchViaWorker: (indexName, params, globalStats, partitionIds, patternWork) => searchViaWorker(state, indexName, params, globalStats, partitionIds, patternWork),
8487
8576
  hasWorkerPool: () => state.workerPool !== null,
8488
8577
  mainCopyQueries: () => state.mainCopyQueries ?? "lone",
8489
8578
  shareMainThread: () => {
@@ -8507,6 +8596,30 @@ function createWorkerOrchestrator(config, executor, indexRegistry, callbacks, ve
8507
8596
  };
8508
8597
  }
8509
8598
 
8599
+ // src/engine/mutations/write-visibility.ts
8600
+ async function awaitWriteVisibility(ctx, indexName) {
8601
+ await ctx.awaitRebalanceReplay(indexName);
8602
+ await ctx.orchestrator.awaitWrites(indexName);
8603
+ }
8604
+
8605
+ // src/engine/write-events.ts
8606
+ var WRITE_EVENT = "write";
8607
+ function emitWriteEvent(source, indexName) {
8608
+ const handlers = source.eventHandlers.get(WRITE_EVENT);
8609
+ if (handlers === void 0 || handlers.size === 0) return;
8610
+ const emit = () => {
8611
+ emitEngineEvent(source.eventHandlers, WRITE_EVENT, { indexName });
8612
+ };
8613
+ awaitWriteVisibility(source.mutationCtx, indexName).then(emit, emit);
8614
+ }
8615
+ async function emitWriteEventAfter(source, indexName, write) {
8616
+ try {
8617
+ return await write;
8618
+ } finally {
8619
+ emitWriteEvent(source, indexName);
8620
+ }
8621
+ }
8622
+
8510
8623
  // src/engine/core.ts
8511
8624
  function createEngineCore(config, hooks, capacityScope = "index") {
8512
8625
  validateWorkerConfig(config?.workers, config?.lifecycle);
@@ -8646,7 +8759,11 @@ function createEngineCore(config, hooks, capacityScope = "index") {
8646
8759
  }
8647
8760
  return names;
8648
8761
  },
8649
- reloadIndex: (indexName) => durability?.manager.reloadIndex?.(indexName) ?? Promise.resolve(),
8762
+ reloadIndex: (indexName) => emitWriteEventAfter(
8763
+ { eventHandlers, mutationCtx },
8764
+ indexName,
8765
+ durability?.manager.reloadIndex?.(indexName) ?? Promise.resolve()
8766
+ ),
8650
8767
  onError(error) {
8651
8768
  if (emitEngineEvent(eventHandlers, "invalidationError", { error }) === 0) {
8652
8769
  console.warn("Invalidation error:", error);
@@ -8744,7 +8861,8 @@ function createEngineCore(config, hooks, capacityScope = "index") {
8744
8861
  analysisRebuild,
8745
8862
  indexState,
8746
8863
  mutationCtx,
8747
- rebalanceCtx
8864
+ rebalanceCtx,
8865
+ patternWorkCap: config?.patternWorkCap ?? DEFAULT_PATTERN_WORK_CAP
8748
8866
  };
8749
8867
  }
8750
8868
 
@@ -8844,6 +8962,7 @@ async function createEngineIndex(core, config, name, requestedConfig, creation =
8844
8962
  }
8845
8963
  const schema = validateSchema(requestedConfig.schema);
8846
8964
  const indexConfig = schema === requestedConfig.schema ? requestedConfig : { ...requestedConfig, schema };
8965
+ requirePatternSearchFor(schema);
8847
8966
  validatePatternValueLimit(indexConfig.patternValueLimit);
8848
8967
  validateVectorPromotion(indexConfig.vectorPromotion);
8849
8968
  validateVectorStorage(indexConfig.vectorPromotion, core.filesystemDurability);
@@ -9011,25 +9130,12 @@ async function rollbackUpdatedDocument(ctx, indexName, docId, restoreDoc, origin
9011
9130
  }
9012
9131
  }
9013
9132
 
9014
- // src/engine/mutations/write-visibility.ts
9015
- async function awaitWriteVisibility(ctx, indexName) {
9016
- await ctx.awaitRebalanceReplay(indexName);
9017
- await ctx.orchestrator.awaitWrites(indexName);
9018
- }
9019
-
9020
9133
  // src/engine/mutations/insert.ts
9021
9134
  async function insertDocument(ctx, indexName, document, docId, options) {
9022
9135
  ctx.guardShutdown();
9023
9136
  const entry = ctx.requireIndex(indexName);
9024
- const embeddedDocId = providedDocId(document);
9025
- if (docId !== void 0 && embeddedDocId !== void 0 && embeddedDocId !== docId) {
9026
- throw new NarsilError(
9027
- ErrorCodes.DOC_VALIDATION_FAILED,
9028
- `The call names document id "${docId}" while the document's own id field is "${embeddedDocId}", so the stored document would contradict the key that it is stored under`,
9029
- { docId, documentId: embeddedDocId }
9030
- );
9031
- }
9032
- const resolvedDocId = docId ?? embeddedDocId ?? ctx.idGenerator();
9137
+ if (docId !== void 0) requireMatchingDocId(docId, document);
9138
+ const resolvedDocId = docId ?? providedDocId(document) ?? ctx.idGenerator();
9033
9139
  validateDocId(resolvedDocId);
9034
9140
  await ctx.pluginRegistry.runHook("beforeInsert", { indexName, docId: resolvedDocId, document });
9035
9141
  if (entry.config.required && entry.config.required.length > 0) {
@@ -9266,6 +9372,7 @@ async function admitBatchDocuments(ctx, indexName, documents, failed) {
9266
9372
  if (chunkFailedIndexes.has(i)) continue;
9267
9373
  const docId = providedDocId(documents[i]) ?? ctx.idGenerator();
9268
9374
  try {
9375
+ requireDocumentObject(documents[i]);
9269
9376
  validateDocId(docId);
9270
9377
  if (hasBeforeHook) {
9271
9378
  await ctx.pluginRegistry.runHook("beforeInsert", { indexName, docId, document: documents[i] });
@@ -9764,6 +9871,7 @@ async function insertDocumentBatch(ctx, indexName, documents, options) {
9764
9871
  if (chunkFailedIndexes.has(i)) continue;
9765
9872
  const batchDocId = providedDocId(documents[i]) ?? ctx.idGenerator();
9766
9873
  try {
9874
+ requireDocumentObject(documents[i]);
9767
9875
  validateDocId(batchDocId);
9768
9876
  if (hasBeforeHook) {
9769
9877
  await ctx.pluginRegistry.runHook("beforeInsert", { indexName, docId: batchDocId, document: documents[i] });
@@ -9986,6 +10094,7 @@ async function settleUpdate(ctx, indexName, prepared) {
9986
10094
  async function prepareUpdate(ctx, indexName, entry, docId, document) {
9987
10095
  ctx.guardShutdown();
9988
10096
  validateDocId(docId);
10097
+ requireMatchingDocId(docId, document);
9989
10098
  const manager = ctx.requireManager(indexName);
9990
10099
  if (ctx.pluginRegistry.hasHooks("beforeUpdate")) {
9991
10100
  await ctx.pluginRegistry.runHook("beforeUpdate", {
@@ -10095,6 +10204,22 @@ async function updateDocument(ctx, indexName, docId, document, options) {
10095
10204
  ctx.checkHeapPressure(indexName);
10096
10205
  if (options?.wait === true) await awaitWriteVisibility(ctx, indexName);
10097
10206
  }
10207
+ function reportedDocIdOf(update) {
10208
+ const docId = update !== null && typeof update === "object" ? update.docId : void 0;
10209
+ return typeof docId === "string" ? docId : "";
10210
+ }
10211
+ function requireUpdateEntry(update) {
10212
+ if (update !== null && typeof update === "object" && !Array.isArray(update)) {
10213
+ const { docId, document } = update;
10214
+ if (typeof docId === "string" && document !== null && typeof document === "object" && !Array.isArray(document)) {
10215
+ return { document };
10216
+ }
10217
+ }
10218
+ throw new NarsilError(
10219
+ ErrorCodes.DOC_VALIDATION_FAILED,
10220
+ "Each entry of a batch update must be an object whose docId is a string and whose document is an object"
10221
+ );
10222
+ }
10098
10223
  async function updateDocumentBatch(ctx, indexName, updates, options) {
10099
10224
  ctx.guardShutdown();
10100
10225
  const entry = ctx.requireIndex(indexName);
@@ -10107,10 +10232,13 @@ async function updateDocumentBatch(ctx, indexName, updates, options) {
10107
10232
  const chunkEnd = Math.min(chunkStart + BATCH_CHUNK_SIZE, updates.length);
10108
10233
  const prepared = [];
10109
10234
  for (let i = chunkStart; i < chunkEnd; i++) {
10235
+ const update = updates[i];
10236
+ const docId = reportedDocIdOf(update);
10110
10237
  try {
10111
- prepared.push(await prepareUpdate(ctx, indexName, entry, updates[i].docId, updates[i].document));
10238
+ const { document } = requireUpdateEntry(update);
10239
+ prepared.push(await prepareUpdate(ctx, indexName, entry, docId, document));
10112
10240
  } catch (err) {
10113
- failed.push({ docId: updates[i].docId, error: asUpdateError(err) });
10241
+ failed.push({ docId, error: asUpdateError(err) });
10114
10242
  }
10115
10243
  }
10116
10244
  const applies = prepared.map((update) => applyOfUpdate(ctx, indexName, update));
@@ -10748,7 +10876,8 @@ async function runEngineQuery(core, indexName, params, options) {
10748
10876
  indexName,
10749
10877
  broadcastStats: broadcastStatsFor(core, options),
10750
10878
  partitionIds: options?.partitionIds,
10751
- cursorBinding: queryBindingOf(params)
10879
+ cursorBinding: queryBindingOf(params),
10880
+ patternWorkCap: core.patternWorkCap
10752
10881
  });
10753
10882
  if (core.pluginRegistry.hasHooks("afterSearch")) {
10754
10883
  const observed = { ...result, hits: [...result.hits] };
@@ -10789,7 +10918,8 @@ async function runEnginePreflight(core, indexName, params, options) {
10789
10918
  indexName,
10790
10919
  broadcastStats: broadcastStatsFor(core, options),
10791
10920
  partitionIds: options?.partitionIds,
10792
- cursorBinding: queryBindingOf(params)
10921
+ cursorBinding: queryBindingOf(params),
10922
+ patternWorkCap: core.patternWorkCap
10793
10923
  });
10794
10924
  if (core.analysisRebuild.isStale(indexName)) {
10795
10925
  result.analysisStale = true;
@@ -10851,7 +10981,8 @@ async function runEngineListDocuments(core, indexName, params, partitionIds) {
10851
10981
  manager: core.requireManager(indexName),
10852
10982
  schema: entry.config.schema,
10853
10983
  strict: entry.config.strict,
10854
- partitionIds
10984
+ partitionIds,
10985
+ patternWorkCap: core.patternWorkCap
10855
10986
  });
10856
10987
  } finally {
10857
10988
  release();
@@ -10885,15 +11016,18 @@ function createNarsilFromCore(core, config) {
10885
11016
  release();
10886
11017
  }
10887
11018
  }
11019
+ function withOpenIndexWrite(indexName, action) {
11020
+ return emitWriteEventAfter(core, indexName, withOpenIndex(indexName, action));
11021
+ }
10888
11022
  const narsil = {
10889
11023
  createIndex(name, indexConfig) {
10890
- return createEngineIndex(core, config, name, indexConfig);
11024
+ return emitWriteEventAfter(core, name, createEngineIndex(core, config, name, indexConfig));
10891
11025
  },
10892
11026
  registerEmbeddingAdapter(name, adapter) {
10893
11027
  registerEngineEmbeddingAdapter(core, name, adapter);
10894
11028
  },
10895
11029
  dropIndex(name) {
10896
- return dropEngineIndex(core, name);
11030
+ return emitWriteEventAfter(core, name, dropEngineIndex(core, name));
10897
11031
  },
10898
11032
  open(indexName) {
10899
11033
  guardShutdown();
@@ -10939,22 +11073,22 @@ function createNarsilFromCore(core, config) {
10939
11073
  return executor.getManager(indexName)?.getPartitionStats() ?? [];
10940
11074
  },
10941
11075
  insert(indexName, document, docId, options) {
10942
- return withOpenIndex(indexName, () => insertDocument(mutationCtx, indexName, document, docId, options));
11076
+ return withOpenIndexWrite(indexName, () => insertDocument(mutationCtx, indexName, document, docId, options));
10943
11077
  },
10944
11078
  insertBatch(indexName, documents, options) {
10945
- return withOpenIndex(indexName, () => insertDocumentBatch(mutationCtx, indexName, documents, options));
11079
+ return withOpenIndexWrite(indexName, () => insertDocumentBatch(mutationCtx, indexName, documents, options));
10946
11080
  },
10947
11081
  remove(indexName, docId, options) {
10948
- return withOpenIndex(indexName, () => removeDocument(mutationCtx, indexName, docId, options));
11082
+ return withOpenIndexWrite(indexName, () => removeDocument(mutationCtx, indexName, docId, options));
10949
11083
  },
10950
11084
  removeBatch(indexName, docIds, options) {
10951
- return withOpenIndex(indexName, () => removeDocumentBatch(mutationCtx, indexName, docIds, options));
11085
+ return withOpenIndexWrite(indexName, () => removeDocumentBatch(mutationCtx, indexName, docIds, options));
10952
11086
  },
10953
11087
  update(indexName, docId, document, options) {
10954
- return withOpenIndex(indexName, () => updateDocument(mutationCtx, indexName, docId, document, options));
11088
+ return withOpenIndexWrite(indexName, () => updateDocument(mutationCtx, indexName, docId, document, options));
10955
11089
  },
10956
11090
  updateBatch(indexName, updates, options) {
10957
- return withOpenIndex(indexName, () => updateDocumentBatch(mutationCtx, indexName, updates, options));
11091
+ return withOpenIndexWrite(indexName, () => updateDocumentBatch(mutationCtx, indexName, updates, options));
10958
11092
  },
10959
11093
  waitForWrites(indexName) {
10960
11094
  guardShutdown();
@@ -11002,7 +11136,7 @@ function createNarsilFromCore(core, config) {
11002
11136
  async rebuildAnalysis(indexName) {
11003
11137
  guardShutdown();
11004
11138
  requireIndex(indexName);
11005
- await withOpenIndex(indexName, () => core.analysisRebuild.rebuild(indexName));
11139
+ await withOpenIndexWrite(indexName, () => core.analysisRebuild.rebuild(indexName));
11006
11140
  },
11007
11141
  async snapshot(indexName) {
11008
11142
  guardShutdown();
@@ -11010,11 +11144,11 @@ function createNarsilFromCore(core, config) {
11010
11144
  },
11011
11145
  async restore(indexName, data) {
11012
11146
  guardShutdown();
11013
- await restoreFromSnapshot(indexName, data, {
11147
+ const restored = restoreFromSnapshot(indexName, data, {
11014
11148
  executor,
11015
11149
  indexRegistry,
11016
11150
  getVectorFieldPaths,
11017
- dropIndex: narsil.dropIndex.bind(narsil),
11151
+ dropIndex: (name) => dropEngineIndex(core, name),
11018
11152
  announceIndexCreated: (name, restoredConfig) => announceIndexCreated(core, name, restoredConfig),
11019
11153
  requireManager,
11020
11154
  durability,
@@ -11025,6 +11159,7 @@ function createNarsilFromCore(core, config) {
11025
11159
  clearAnalysisStale: core.analysisRebuild.clearStale,
11026
11160
  indexState: core.indexState
11027
11161
  });
11162
+ await emitWriteEventAfter(core, indexName, restored);
11028
11163
  core.heapPressureNotifier.check(indexName);
11029
11164
  },
11030
11165
  async checkpoint(indexName) {
@@ -11037,7 +11172,7 @@ function createNarsilFromCore(core, config) {
11037
11172
  async clear(indexName) {
11038
11173
  guardShutdown();
11039
11174
  requireIndex(indexName);
11040
- await withOpenIndex(indexName, async () => {
11175
+ await withOpenIndexWrite(indexName, async () => {
11041
11176
  await executor.execute({ type: "clear", indexName, requestId: indexName });
11042
11177
  await orchestrator.replicateToWorkers({ type: "clear", indexName, requestId: `replicate-clear-${indexName}` });
11043
11178
  core.watermarkNotifier.forget(indexName);
@@ -11046,7 +11181,7 @@ function createNarsilFromCore(core, config) {
11046
11181
  async rebalance(indexName, targetPartitionCount) {
11047
11182
  guardShutdown();
11048
11183
  requireIndex(indexName);
11049
- return withOpenIndex(
11184
+ return withOpenIndexWrite(
11050
11185
  indexName,
11051
11186
  () => executeRebalance(requireManager(indexName), indexName, targetPartitionCount, rebalanceCtx)
11052
11187
  );
@@ -11112,14 +11247,14 @@ function createNarsilFromCore(core, config) {
11112
11247
  async compactVectors(indexName, fieldName) {
11113
11248
  guardShutdown();
11114
11249
  requireIndex(indexName);
11115
- await withOpenIndex(indexName, async () => {
11250
+ await withOpenIndexWrite(indexName, async () => {
11116
11251
  compactVectors(requireManager(indexName), indexName, fieldName);
11117
11252
  });
11118
11253
  },
11119
11254
  async optimizeVectors(indexName, fieldName) {
11120
11255
  guardShutdown();
11121
11256
  requireIndex(indexName);
11122
- await withOpenIndex(indexName, () => optimizeVectors(requireManager(indexName), indexName, fieldName));
11257
+ await withOpenIndexWrite(indexName, () => optimizeVectors(requireManager(indexName), indexName, fieldName));
11123
11258
  },
11124
11259
  vectorMaintenanceStatus(indexName) {
11125
11260
  guardShutdown();
@@ -1,5 +1,5 @@
1
- import { validateRequiredFields } from './chunk-653BNMAF.mjs';
2
- import { ErrorCodes, NarsilError } from './chunk-USKFU7J5.mjs';
1
+ import { validateRequiredFields } from './chunk-M6XF5HDE.mjs';
2
+ import { ErrorCodes, NarsilError } from './chunk-RV77FR2F.mjs';
3
3
 
4
4
  // src/narsil/internals.ts
5
5
  var engineCores = /* @__PURE__ */ new WeakMap();
@@ -239,9 +239,30 @@ async function embedBatchDocumentFields(documents, embeddingConfig, adapter, sig
239
239
 
240
240
  // src/engine/mutations/insert-admission.ts
241
241
  function providedDocId(document) {
242
+ if (document === null || typeof document !== "object") return void 0;
242
243
  const id = document.id;
243
244
  return typeof id === "string" && id.length > 0 ? id : void 0;
244
245
  }
246
+ function requireDocumentObject(document) {
247
+ if (document !== null && typeof document === "object" && !Array.isArray(document)) return;
248
+ const received = Array.isArray(document) ? "a list" : document === null ? "null" : `a ${typeof document}`;
249
+ throw new NarsilError(
250
+ ErrorCodes.DOC_VALIDATION_FAILED,
251
+ `Each document must be an object, while this entry is ${received}`,
252
+ {
253
+ received
254
+ }
255
+ );
256
+ }
257
+ function requireMatchingDocId(docId, document) {
258
+ const embeddedDocId = providedDocId(document);
259
+ if (embeddedDocId === void 0 || embeddedDocId === docId) return;
260
+ throw new NarsilError(
261
+ ErrorCodes.DOC_VALIDATION_FAILED,
262
+ `The call names document id "${docId}" while the document's own id field is "${embeddedDocId}", so the stored document would contradict the key that it is stored under`,
263
+ { docId, documentId: embeddedDocId }
264
+ );
265
+ }
245
266
  function asBatchInsertError(err) {
246
267
  return err instanceof NarsilError ? err : new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, String(err));
247
268
  }
@@ -566,4 +587,4 @@ function restoredConfigFields(envelope) {
566
587
  };
567
588
  }
568
589
 
569
- export { MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR, WIRE_BATCH_BUDGET, admitBufferedUpsert, admitInsert, asBatchInsertError, assertDocumentCarriesMappedVectors, bindEngineCore, collectRequiredFieldFailures, createPendingAdmissions, embedChunkDocuments, embedDocumentFields, engineCoreOf, providedDocId, readSharedConfigFields, recordPendingAdmission, resolveVectorText, restoredConfigFields, restoredEmbedding, restoredPartitionConfig };
590
+ export { MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR, WIRE_BATCH_BUDGET, admitBufferedUpsert, admitInsert, asBatchInsertError, assertDocumentCarriesMappedVectors, bindEngineCore, collectRequiredFieldFailures, createPendingAdmissions, embedChunkDocuments, embedDocumentFields, engineCoreOf, providedDocId, readSharedConfigFields, recordPendingAdmission, requireDocumentObject, requireMatchingDocId, resolveVectorText, restoredConfigFields, restoredEmbedding, restoredPartitionConfig };
@@ -1,6 +1,6 @@
1
- import { MAX_SORT_FIELDS } from './chunk-653BNMAF.mjs';
2
- import { nativeSearchCoreIsEnabled, loadNativeCore } from './chunk-YNVIUGF5.mjs';
3
- import { NarsilError, ServerErrorCodes, ErrorCodes } from './chunk-USKFU7J5.mjs';
1
+ import { MAX_SORT_FIELDS } from './chunk-M6XF5HDE.mjs';
2
+ import { nativeSearchCoreIsEnabled, loadNativeCore } from './chunk-P2ZZN32C.mjs';
3
+ import { NarsilError, ServerErrorCodes, ErrorCodes } from './chunk-RV77FR2F.mjs';
4
4
 
5
5
  // src/server/capabilities.ts
6
6
  var ASYNC_IMPORT_CAPABILITY = "documents.import.async";
@@ -113,6 +113,7 @@ var STATUS_BY_CODE = {
113
113
  SEARCH_INVALID_MODE: 400,
114
114
  SEARCH_INVALID_CURSOR: 400,
115
115
  SEARCH_RESULT_WINDOW_EXCEEDED: 400,
116
+ SEARCH_WORK_CAP_EXCEEDED: 400,
116
117
  LANGUAGE_NOT_SUPPORTED: 400,
117
118
  CONFIG_INVALID: 400,
118
119
  EMBEDDING_DIMENSION_MISMATCH: 400,