@delali/narsil 0.2.3-next.35 → 0.2.3-next.36

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 (39) hide show
  1. package/dist/adapters/filesystem.mjs +1 -1
  2. package/dist/adapters/indexeddb.mjs +1 -1
  3. package/dist/{chunk-EMI63EXP.mjs → chunk-GB2IIJJG.mjs} +2 -0
  4. package/dist/{chunk-RO4EFWE6.mjs → chunk-GRWFY2HI.mjs} +10 -1
  5. package/dist/{chunk-36ER4WCG.mjs → chunk-JQUL2T6I.mjs} +2 -1
  6. package/dist/{chunk-OOA7OBLA.mjs → chunk-MMSWEEZT.mjs} +69 -163
  7. package/dist/{chunk-GODECMDT.mjs → chunk-PM52SMBT.mjs} +1 -1
  8. package/dist/chunk-WU2UZEVD.mjs +137 -0
  9. package/dist/{client-DAJ_UHOa.d.ts → client-BobVnoIK.d.ts} +2 -2
  10. package/dist/client.d.ts +8 -4
  11. package/dist/client.mjs +2 -0
  12. package/dist/{config-CzTJJili.d.ts → config-Byyl1QqH.d.ts} +2 -2
  13. package/dist/config-fLC4lbmg.d.ts +194 -0
  14. package/dist/{direct-executor-YARCDMJF.mjs → direct-executor-MSJEVPZU.mjs} +2 -2
  15. package/dist/distribution/coordinator/etcd.d.ts +5 -7
  16. package/dist/distribution/coordinator/etcd.mjs +27 -8
  17. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  18. package/dist/distribution/transport/grpc.mjs +3 -3
  19. package/dist/distribution/transport/in-memory.mjs +1 -1
  20. package/dist/distribution/transport/tcp.mjs +2 -2
  21. package/dist/distribution.d.ts +10 -132
  22. package/dist/distribution.mjs +1089 -339
  23. package/dist/embeddings/openai.mjs +1 -1
  24. package/dist/{engine-DEtRLYpB.d.ts → engine-BE45D5ZG.d.ts} +3 -3
  25. package/dist/index.browser.mjs +71 -23
  26. package/dist/index.d.ts +7 -7
  27. package/dist/index.mjs +5 -4
  28. package/dist/{options-BORJg5XB.d.ts → options-EXYFcem-.d.ts} +9 -0
  29. package/dist/persistence/durability/checkpoint-worker.d.ts +3 -1
  30. package/dist/persistence/durability/checkpoint-worker.mjs +5 -4
  31. package/dist/react.d.ts +4 -4
  32. package/dist/react.mjs +2 -0
  33. package/dist/{schema-emTQfEiX.d.ts → schema-ZRbk8BgH.d.ts} +1 -0
  34. package/dist/{search-Dt4dkzpf.d.ts → search-DHuXwJYk.d.ts} +5 -1
  35. package/dist/server.d.ts +9 -5
  36. package/dist/server.mjs +101 -7
  37. package/dist/{types-BhdINMtE.d.ts → types-B5Tsaalz.d.ts} +19 -2
  38. package/dist/workers/entry.mjs +2 -2
  39. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { NarsilError, ErrorCodes } from '../chunk-EMI63EXP.mjs';
1
+ import { NarsilError, ErrorCodes } from '../chunk-GB2IIJJG.mjs';
2
2
 
3
3
  // src/embeddings/openai.ts
4
4
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503]);
@@ -1,6 +1,6 @@
1
1
  import { E as EmbeddingAdapter } from './adapters-79OIa4xm.js';
2
- import { I as IndexInfo, f as IndexStats, P as PartitionStatsResult, B as BatchResult, L as ListParams, g as ListResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult, M as MemoryStats, V as VectorMaintenanceResult } from './search-Dt4dkzpf.js';
3
- import { I as IndexConfig, A as AnyDocument, a as InsertOptions, P as PartitionConfig } from './schema-emTQfEiX.js';
2
+ import { I as IndexInfo, f as IndexStats, P as PartitionStatsResult, B as BatchResult, L as ListParams, g as ListResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult, M as MemoryStats, V as VectorMaintenanceResult } from './search-DHuXwJYk.js';
3
+ import { I as IndexConfig, A as AnyDocument, a as InsertOptions, P as PartitionConfig } from './schema-ZRbk8BgH.js';
4
4
 
5
5
  /**
6
6
  * Every event the engine emits, with the payload each one carries.
@@ -455,4 +455,4 @@ interface Narsil {
455
455
  shutdown(): Promise<void>;
456
456
  }
457
457
 
458
- export type { Narsil as N, NarsilEventMap as a };
458
+ export type { NarsilEventMap as N, Narsil as a };
@@ -55,6 +55,8 @@ var ErrorCodes = {
55
55
  NODE_BOOTSTRAP_FAILED: "NODE_BOOTSTRAP_FAILED",
56
56
  NODE_ALREADY_JOINED: "NODE_ALREADY_JOINED",
57
57
  NODE_NOT_JOINED: "NODE_NOT_JOINED",
58
+ NODE_NOT_READY: "NODE_NOT_READY",
59
+ NODE_NOT_CONTROLLER: "NODE_NOT_CONTROLLER",
58
60
  COORDINATOR_DEPENDENCY_MISSING: "COORDINATOR_DEPENDENCY_MISSING",
59
61
  TRANSPORT_DEPENDENCY_MISSING: "TRANSPORT_DEPENDENCY_MISSING",
60
62
  REPLICATION_ENTRY_INVALID: "REPLICATION_ENTRY_INVALID",
@@ -2544,6 +2546,9 @@ function metadataToWire(meta) {
2544
2546
  if (meta.indexUuid !== void 0) {
2545
2547
  wire.index_uuid = meta.indexUuid;
2546
2548
  }
2549
+ if (meta.heldPartitions !== void 0) {
2550
+ wire.held_partitions = meta.heldPartitions;
2551
+ }
2547
2552
  return wire;
2548
2553
  }
2549
2554
  function wireToMetadata(raw) {
@@ -2633,8 +2638,14 @@ function wireToMetadata(raw) {
2633
2638
  if (typeof raw.index_uuid === "string" && raw.index_uuid.length > 0) {
2634
2639
  meta.indexUuid = raw.index_uuid;
2635
2640
  }
2641
+ if (Array.isArray(raw.held_partitions) && raw.held_partitions.every(isNonNegativeInteger)) {
2642
+ meta.heldPartitions = raw.held_partitions;
2643
+ }
2636
2644
  return meta;
2637
2645
  }
2646
+ function isNonNegativeInteger(value) {
2647
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
2648
+ }
2638
2649
  function isPositiveInteger2(value) {
2639
2650
  return typeof value === "number" && Number.isInteger(value) && value >= 1;
2640
2651
  }
@@ -15845,7 +15856,7 @@ function normalizePartitions(raw) {
15845
15856
  }
15846
15857
  const result = [];
15847
15858
  for (const entry of raw) {
15848
- if (!isNonNegativeInteger(entry?.partitionId)) {
15859
+ if (!isNonNegativeInteger2(entry?.partitionId)) {
15849
15860
  throw new NarsilError(ErrorCodes.PERSISTENCE_LOAD_FAILED, "Segment manifest has a partition without a valid id", {
15850
15861
  partitionId: entry?.partitionId
15851
15862
  });
@@ -15880,7 +15891,7 @@ function normalizeSegments(raw, partitionId) {
15880
15891
  const seenIds = /* @__PURE__ */ new Set();
15881
15892
  const result = [];
15882
15893
  for (const segment of raw) {
15883
- if (!isSegmentId(segment?.id) || !isNonNegativeInteger(segment.docCount) || !isNonNegativeInteger(segment.tombstoneCount) || !isKey(segment.key)) {
15894
+ if (!isSegmentId(segment?.id) || !isNonNegativeInteger2(segment.docCount) || !isNonNegativeInteger2(segment.tombstoneCount) || !isKey(segment.key)) {
15884
15895
  throw new NarsilError(ErrorCodes.PERSISTENCE_LOAD_FAILED, "Segment manifest has an invalid segment reference", {
15885
15896
  partitionId,
15886
15897
  segment
@@ -15932,7 +15943,7 @@ function normalizeVectors(raw, partitionId) {
15932
15943
  }
15933
15944
  const result = [];
15934
15945
  for (const vector of raw) {
15935
- if (typeof vector?.fieldPath !== "string" || !isNonNegativeInteger(vector.generation) || !isKey(vector.key)) {
15946
+ if (typeof vector?.fieldPath !== "string" || !isNonNegativeInteger2(vector.generation) || !isKey(vector.key)) {
15936
15947
  throw new NarsilError(ErrorCodes.PERSISTENCE_LOAD_FAILED, "Segment manifest has an invalid vector reference", {
15937
15948
  partitionId,
15938
15949
  vector
@@ -15942,7 +15953,7 @@ function normalizeVectors(raw, partitionId) {
15942
15953
  }
15943
15954
  return result;
15944
15955
  }
15945
- function isNonNegativeInteger(value) {
15956
+ function isNonNegativeInteger2(value) {
15946
15957
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
15947
15958
  }
15948
15959
  function isSegmentId(value) {
@@ -17728,6 +17739,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
17728
17739
  const mode = config.mode ?? "sync";
17729
17740
  const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_ASYNC_FLUSH_INTERVAL_MS;
17730
17741
  const indexes = /* @__PURE__ */ new Map();
17742
+ const metadataWrites = /* @__PURE__ */ new Map();
17731
17743
  let checkpointTimer = null;
17732
17744
  let asyncFlushTimer = null;
17733
17745
  let shuttingDown = false;
@@ -17780,6 +17792,15 @@ function createDurabilityManager(config, hooks, directoryOverride) {
17780
17792
  }
17781
17793
  return partition;
17782
17794
  }
17795
+ function queueMetadataWrite(indexName, write) {
17796
+ const previous = metadataWrites.get(indexName) ?? Promise.resolve();
17797
+ const queued = previous.then(write);
17798
+ metadataWrites.set(
17799
+ indexName,
17800
+ queued.catch(() => void 0)
17801
+ );
17802
+ return queued;
17803
+ }
17783
17804
  function startCheckpointTimer() {
17784
17805
  if (checkpointTimer !== null || shuttingDown || fatalError !== null || checkpointIntervalMs <= 0) {
17785
17806
  return;
@@ -17937,6 +17958,9 @@ function createDurabilityManager(config, hooks, directoryOverride) {
17937
17958
  }
17938
17959
  startCheckpointTimer();
17939
17960
  },
17961
+ highestPersistedSeqNo(indexName, partitionId) {
17962
+ return indexes.get(indexName)?.partitions.get(partitionId)?.appliedSeqNo ?? 0;
17963
+ },
17940
17964
  async recordMutation(record) {
17941
17965
  if (fatalError !== null) {
17942
17966
  throw fatalError;
@@ -17984,14 +18008,16 @@ function createDurabilityManager(config, hooks, directoryOverride) {
17984
18008
  }
17985
18009
  return allocatedSeqNo;
17986
18010
  },
17987
- async persistMetadata(indexName) {
17988
- const metadata = hooks.buildMetadata(indexName);
17989
- if (metadata === void 0) {
17990
- return;
17991
- }
17992
- const bytes = await writeMetadataEnvelope(metadata, { checksum: true });
17993
- await directory.atomicWrite(`${indexName}/meta`, bytes);
17994
- getOrCreateIndexState(indexName);
18011
+ persistMetadata(indexName) {
18012
+ return queueMetadataWrite(indexName, async () => {
18013
+ const metadata = hooks.buildMetadata(indexName);
18014
+ if (metadata === void 0) {
18015
+ return;
18016
+ }
18017
+ const bytes = await writeMetadataEnvelope(metadata, { checksum: true });
18018
+ await directory.atomicWrite(`${indexName}/meta`, bytes);
18019
+ getOrCreateIndexState(indexName);
18020
+ });
17995
18021
  },
17996
18022
  checkpoint(indexName) {
17997
18023
  return checkpointIndex(indexName);
@@ -18048,6 +18074,7 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
18048
18074
  const checkpointIntervalMs = config.checkpointIntervalMs ?? DEFAULT_CHECKPOINT_INTERVAL_MS;
18049
18075
  const checkpointMutationThreshold = config.checkpointMutationThreshold ?? DEFAULT_CHECKPOINT_MUTATION_THRESHOLD;
18050
18076
  const indexes = /* @__PURE__ */ new Map();
18077
+ const metadataWrites = /* @__PURE__ */ new Map();
18051
18078
  let checkpointTimer = null;
18052
18079
  let shuttingDown = false;
18053
18080
  function getOrCreateIndexState(indexName) {
@@ -18126,6 +18153,15 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
18126
18153
  indexState.checkpointInFlight = run;
18127
18154
  return run;
18128
18155
  }
18156
+ function queueMetadataWrite(indexName, write) {
18157
+ const previous = metadataWrites.get(indexName) ?? Promise.resolve();
18158
+ const queued = previous.then(write);
18159
+ metadataWrites.set(
18160
+ indexName,
18161
+ queued.catch(() => void 0)
18162
+ );
18163
+ return queued;
18164
+ }
18129
18165
  async function recoverIndex(indexName) {
18130
18166
  const metaBytes = await adapter.load(metadataKey(indexName));
18131
18167
  if (metaBytes === null) {
@@ -18160,6 +18196,9 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
18160
18196
  }
18161
18197
  startCheckpointTimer();
18162
18198
  },
18199
+ highestPersistedSeqNo() {
18200
+ return 0;
18201
+ },
18163
18202
  async recordMutation(record) {
18164
18203
  const indexState = getOrCreateIndexState(record.indexName);
18165
18204
  const buffered = indexState.applyChain.then(() => record.apply());
@@ -18174,14 +18213,16 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
18174
18213
  }
18175
18214
  return 0;
18176
18215
  },
18177
- async persistMetadata(indexName) {
18178
- const metadata = hooks.buildMetadata(indexName);
18179
- if (metadata === void 0) {
18180
- return;
18181
- }
18182
- const bytes = await writeMetadataEnvelope(metadata, { checksum: true });
18183
- await adapter.save(metadataKey(indexName), bytes);
18184
- getOrCreateIndexState(indexName);
18216
+ persistMetadata(indexName) {
18217
+ return queueMetadataWrite(indexName, async () => {
18218
+ const metadata = hooks.buildMetadata(indexName);
18219
+ if (metadata === void 0) {
18220
+ return;
18221
+ }
18222
+ const bytes = await writeMetadataEnvelope(metadata, { checksum: true });
18223
+ await adapter.save(metadataKey(indexName), bytes);
18224
+ getOrCreateIndexState(indexName);
18225
+ });
18185
18226
  },
18186
18227
  checkpoint(indexName) {
18187
18228
  return checkpointIndex(indexName);
@@ -18274,6 +18315,9 @@ function buildMetadata(indexName, hooks) {
18274
18315
  if (config.indexUuid !== void 0) {
18275
18316
  metadata.indexUuid = config.indexUuid;
18276
18317
  }
18318
+ if (config.heldPartitions !== void 0) {
18319
+ metadata.heldPartitions = config.heldPartitions;
18320
+ }
18277
18321
  return metadata;
18278
18322
  }
18279
18323
  function createDurabilityIntegration(tier, hooks) {
@@ -18335,6 +18379,7 @@ function createDurabilityFromTier(tier, wiring) {
18335
18379
  } : void 0;
18336
18380
  return {
18337
18381
  ...entry.indexUuid !== null ? { indexUuid: entry.indexUuid } : {},
18382
+ ...entry.heldPartitions !== null ? { heldPartitions: entry.heldPartitions } : {},
18338
18383
  schema: flattenSchema(entry.config.schema),
18339
18384
  language: entry.language.name,
18340
18385
  k1: entry.config.bm25?.k1 ?? 1.2,
@@ -19446,7 +19491,8 @@ function createEngineCore(config) {
19446
19491
  embeddingAdapter,
19447
19492
  embeddingAdapterName: adapterName,
19448
19493
  vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
19449
- indexUuid: metadata.indexUuid ?? null
19494
+ indexUuid: metadata.indexUuid ?? null,
19495
+ heldPartitions: metadata.heldPartitions ?? null
19450
19496
  });
19451
19497
  if (metadata.analysisRevision !== language.revision) {
19452
19498
  analysisRebuild.markStale({
@@ -19726,7 +19772,8 @@ async function createEngineIndex(core, config, name, indexConfig, indexUuid) {
19726
19772
  embeddingAdapter: resolvedEmbeddingAdapter,
19727
19773
  embeddingAdapterName,
19728
19774
  vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
19729
- indexUuid: null
19775
+ indexUuid: null,
19776
+ heldPartitions: null
19730
19777
  });
19731
19778
  if (core.durability) {
19732
19779
  await core.durability.manager.persistMetadata(name);
@@ -21656,7 +21703,8 @@ async function restoreFromSnapshot(indexName, data, deps) {
21656
21703
  embeddingAdapter,
21657
21704
  embeddingAdapterName: adapterName,
21658
21705
  vectorFieldPaths: vectorFieldPaths2,
21659
- indexUuid: null
21706
+ indexUuid: null,
21707
+ heldPartitions: null
21660
21708
  });
21661
21709
  try {
21662
21710
  const manager = deps.requireManager(indexName);
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { c as StopWordOverride, C as CustomTokenizer } from './schema-emTQfEiX.js';
2
- export { A as AnyDocument, B as BM25Params, E as EmbeddingFieldConfig, F as FieldType, I as IndexConfig, a as InsertOptions, P as PartitionConfig, S as SchemaDefinition, b as ScoringMode, V as VectorIndexConfig, d as VectorQuantizationMode } from './schema-emTQfEiX.js';
1
+ import { c as StopWordOverride, C as CustomTokenizer } from './schema-ZRbk8BgH.js';
2
+ export { A as AnyDocument, B as BM25Params, E as EmbeddingFieldConfig, F as FieldType, I as IndexConfig, a as InsertOptions, P as PartitionConfig, S as SchemaDefinition, b as ScoringMode, V as VectorIndexConfig, d as VectorQuantizationMode } from './schema-ZRbk8BgH.js';
3
3
  import { L as LanguageModule } from './language-DB8KG1z5.js';
4
4
  export { T as TokenizerConfig } from './language-DB8KG1z5.js';
5
- export { A as ArrayFilter, B as BatchResult, C as ClientErrorCode, a as ClientErrorCodes, k as ComparisonFilter, D as DocumentProjection, E as ErrorCode, b as ErrorCodes, F as FacetConfig, l as FacetResult, m as FieldFilter, n as FilterExpression, G as GeoFilter, o as GeoPolygonFilter, p as GeoRadiusFilter, q as GroupConfig, r as GroupReducer, s as GroupResult, H as HighlightConfig, t as HighlightMatch, u as Hit, v as HybridConfig, I as IndexInfo, f as IndexStats, L as ListParams, g as ListResult, w as ListedDocument, M as MemoryStats, N as NarsilError, c as NarsilErrorCode, P as PartitionStatsResult, h as PreflightResult, x as PresenceFilter, y as ProcessMemoryReport, z as QueryCoverage, Q as QueryParams, e as QueryResult, J as ScoreComponents, K as SearchMode, S as ServerErrorCode, d as ServerErrorCodes, O as SortField, R as SortSpec, T as StringFilter, i as SuggestParams, j as SuggestResult, U as TermMatchPolicy, V as VectorMaintenanceResult, W as VectorQueryConfig } from './search-Dt4dkzpf.js';
6
- import { N as NarsilConfig } from './config-CzTJJili.js';
7
- export { A as AnalysisConfig, D as DurabilityConfig, I as IndexContext, a as InsertContext, b as NarsilPlugin, P as PartitionContext, R as RemoveContext, S as SearchContext, c as StaleAnalysis, U as UpdateContext, W as WorkerConfig, d as WorkerContext } from './config-CzTJJili.js';
8
- import { N as Narsil } from './engine-DEtRLYpB.js';
9
- export { a as NarsilEventMap } from './engine-DEtRLYpB.js';
5
+ export { A as ArrayFilter, B as BatchResult, C as ClientErrorCode, a as ClientErrorCodes, k as ComparisonFilter, D as DocumentProjection, E as ErrorCode, b as ErrorCodes, F as FacetConfig, l as FacetResult, m as FieldFilter, n as FilterExpression, G as GeoFilter, o as GeoPolygonFilter, p as GeoRadiusFilter, q as GroupConfig, r as GroupReducer, s as GroupResult, H as HighlightConfig, t as HighlightMatch, u as Hit, v as HybridConfig, I as IndexInfo, f as IndexStats, L as ListParams, g as ListResult, w as ListedDocument, M as MemoryStats, N as NarsilError, c as NarsilErrorCode, P as PartitionStatsResult, h as PreflightResult, x as PresenceFilter, y as ProcessMemoryReport, z as QueryCoverage, Q as QueryParams, e as QueryResult, J as ScoreComponents, K as SearchMode, S as ServerErrorCode, d as ServerErrorCodes, O as SortField, R as SortSpec, T as StringFilter, i as SuggestParams, j as SuggestResult, U as TermMatchPolicy, V as VectorMaintenanceResult, W as VectorQueryConfig } from './search-DHuXwJYk.js';
6
+ import { N as NarsilConfig } from './config-Byyl1QqH.js';
7
+ export { A as AnalysisConfig, D as DurabilityConfig, I as IndexContext, a as InsertContext, b as NarsilPlugin, P as PartitionContext, R as RemoveContext, S as SearchContext, c as StaleAnalysis, U as UpdateContext, W as WorkerConfig, d as WorkerContext } from './config-Byyl1QqH.js';
8
+ import { a as Narsil } from './engine-BE45D5ZG.js';
9
+ export { N as NarsilEventMap } from './engine-BE45D5ZG.js';
10
10
  export { E as EmbeddingAdapter, I as InvalidationAdapter, a as InvalidationEvent, b as PartitionStatistics, P as PersistenceAdapter } from './adapters-79OIa4xm.js';
11
11
 
12
12
  /**
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
- export { VERSION, createNarsil } from './chunk-OOA7OBLA.mjs';
2
- import './chunk-36ER4WCG.mjs';
3
- export { clearNormalizationCache, configureNormalizationCache, getLanguage, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, registerLanguage, registerStopWords, registerTokenizer, resetNormalizationCache } from './chunk-RO4EFWE6.mjs';
1
+ export { VERSION, createNarsil } from './chunk-MMSWEEZT.mjs';
2
+ import './chunk-JQUL2T6I.mjs';
3
+ import './chunk-WU2UZEVD.mjs';
4
+ export { clearNormalizationCache, configureNormalizationCache, getLanguage, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, registerLanguage, registerStopWords, registerTokenizer, resetNormalizationCache } from './chunk-GRWFY2HI.mjs';
4
5
  import './chunk-TGP5M5R7.mjs';
5
6
  import './chunk-OBXS7OZM.mjs';
6
7
  export { isSimdAvailable } from './chunk-WDL632CN.mjs';
@@ -8,4 +9,4 @@ import './chunk-KMMJMDUA.mjs';
8
9
  import './chunk-U2BKYEC7.mjs';
9
10
  import './chunk-BFIH7OV5.mjs';
10
11
  import './chunk-WTONLDU3.mjs';
11
- export { ClientErrorCodes, ErrorCodes, NarsilError, ServerErrorCodes } from './chunk-EMI63EXP.mjs';
12
+ export { ClientErrorCodes, ErrorCodes, NarsilError, ServerErrorCodes } from './chunk-GB2IIJJG.mjs';
@@ -1,3 +1,4 @@
1
+ import { C as ClusterNamespace } from './config-fLC4lbmg.js';
1
2
  import { E as EmbeddingAdapter } from './adapters-79OIa4xm.js';
2
3
  import { h as TaskStore } from './tasks-CnMLpfk-.js';
3
4
 
@@ -194,6 +195,14 @@ interface ServerOptions {
194
195
  * is controlled elsewhere.
195
196
  */
196
197
  allowInsecure?: boolean;
198
+ /**
199
+ * The cluster-facing side of the node this server fronts, which is
200
+ * `node.cluster` on a {@link ClusterNode}. With it set, `/readyz` answers
201
+ * 200 only while the node reports `SERVING`, `/cluster` reports the
202
+ * topology, and `/indexes/:name/cluster` reports one index's allocation.
203
+ * Without it, both cluster routes answer 501.
204
+ */
205
+ cluster?: ClusterNamespace;
197
206
  }
198
207
  /**
199
208
  * The running HTTP server, as {@link createServer} returns it.
@@ -1,10 +1,12 @@
1
- import { P as PartitionConfig, b as ScoringMode, V as VectorIndexConfig } from '../../schema-emTQfEiX.js';
1
+ import { P as PartitionConfig, b as ScoringMode, V as VectorIndexConfig } from '../../schema-ZRbk8BgH.js';
2
2
  import '../../adapters-79OIa4xm.js';
3
3
 
4
4
  interface IndexMetadata {
5
5
  indexName: string;
6
6
  /** The identity a cluster gave the index at creation; absent on a single engine. */
7
7
  indexUuid?: string;
8
+ /** The partitions this copy holds, which a cluster node answers a partition-stores request from. */
9
+ heldPartitions?: number[];
8
10
  schema: Record<string, string>;
9
11
  language: string;
10
12
  partitionCount: number;
@@ -1,6 +1,7 @@
1
- import { createDurableDirectory, writeSegmentedCheckpoint } from '../../chunk-OOA7OBLA.mjs';
2
- import '../../chunk-36ER4WCG.mjs';
3
- import '../../chunk-RO4EFWE6.mjs';
1
+ import { createDurableDirectory, writeSegmentedCheckpoint } from '../../chunk-MMSWEEZT.mjs';
2
+ import '../../chunk-JQUL2T6I.mjs';
3
+ import '../../chunk-WU2UZEVD.mjs';
4
+ import '../../chunk-GRWFY2HI.mjs';
4
5
  import '../../chunk-TGP5M5R7.mjs';
5
6
  import '../../chunk-OBXS7OZM.mjs';
6
7
  import '../../chunk-WDL632CN.mjs';
@@ -8,7 +9,7 @@ import '../../chunk-KMMJMDUA.mjs';
8
9
  import '../../chunk-U2BKYEC7.mjs';
9
10
  import '../../chunk-BFIH7OV5.mjs';
10
11
  import '../../chunk-WTONLDU3.mjs';
11
- import '../../chunk-EMI63EXP.mjs';
12
+ import '../../chunk-GB2IIJJG.mjs';
12
13
 
13
14
  // src/persistence/durability/rebuild.ts
14
15
  async function rebuildSnapshotFromDurable(root, metadata, targets, compactionThreshold) {
package/dist/react.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { N as NarsilError, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, Q as QueryParams, h as PreflightResult, e as QueryResult, i as SuggestParams, j as SuggestResult } from './search-Dt4dkzpf.js';
2
- export { C as ClientErrorCode, a as ClientErrorCodes, E as ErrorCode, b as ErrorCodes, c as NarsilErrorCode, S as ServerErrorCode, d as ServerErrorCodes } from './search-Dt4dkzpf.js';
1
+ import { N as NarsilError, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, Q as QueryParams, h as PreflightResult, e as QueryResult, i as SuggestParams, j as SuggestResult } from './search-DHuXwJYk.js';
2
+ export { C as ClientErrorCode, a as ClientErrorCodes, E as ErrorCode, b as ErrorCodes, c as NarsilErrorCode, S as ServerErrorCode, d as ServerErrorCodes } from './search-DHuXwJYk.js';
3
3
  import { e as TaskRecord, d as TaskProgress, b as ImportResult, c as TaskListQuery, T as TaskListPage } from './tasks-CnMLpfk-.js';
4
4
  export { I as ImportError, f as TaskStatus, g as TaskType } from './tasks-CnMLpfk-.js';
5
5
  import { ReactNode, ReactElement } from 'react';
6
- import { N as NarsilClient, I as ImportSource } from './client-DAJ_UHOa.js';
7
- import { A as AnyDocument } from './schema-emTQfEiX.js';
6
+ import { N as NarsilClient, I as ImportSource } from './client-BobVnoIK.js';
7
+ import { A as AnyDocument } from './schema-ZRbk8BgH.js';
8
8
  import './adapters-79OIa4xm.js';
9
9
 
10
10
  /**
package/dist/react.mjs CHANGED
@@ -55,6 +55,8 @@ var ErrorCodes = {
55
55
  NODE_BOOTSTRAP_FAILED: "NODE_BOOTSTRAP_FAILED",
56
56
  NODE_ALREADY_JOINED: "NODE_ALREADY_JOINED",
57
57
  NODE_NOT_JOINED: "NODE_NOT_JOINED",
58
+ NODE_NOT_READY: "NODE_NOT_READY",
59
+ NODE_NOT_CONTROLLER: "NODE_NOT_CONTROLLER",
58
60
  COORDINATOR_DEPENDENCY_MISSING: "COORDINATOR_DEPENDENCY_MISSING",
59
61
  TRANSPORT_DEPENDENCY_MISSING: "TRANSPORT_DEPENDENCY_MISSING",
60
62
  REPLICATION_ENTRY_INVALID: "REPLICATION_ENTRY_INVALID",
@@ -9,6 +9,7 @@ import { E as EmbeddingAdapter } from './adapters-79OIa4xm.js';
9
9
  * @public
10
10
  */
11
11
  type AnyDocument = Record<string, unknown> & {
12
+ /** Identifier the engine stores this document under when you insert without passing one separately. */
12
13
  id?: string;
13
14
  };
14
15
  /**
@@ -1,4 +1,4 @@
1
- import { A as AnyDocument, S as SchemaDefinition, b as ScoringMode } from './schema-emTQfEiX.js';
1
+ import { A as AnyDocument, S as SchemaDefinition, b as ScoringMode } from './schema-ZRbk8BgH.js';
2
2
 
3
3
  /**
4
4
  * Every code a {@link NarsilError} carries, grouped by the part of the engine
@@ -64,6 +64,8 @@ declare const ErrorCodes: {
64
64
  readonly NODE_BOOTSTRAP_FAILED: "NODE_BOOTSTRAP_FAILED";
65
65
  readonly NODE_ALREADY_JOINED: "NODE_ALREADY_JOINED";
66
66
  readonly NODE_NOT_JOINED: "NODE_NOT_JOINED";
67
+ readonly NODE_NOT_READY: "NODE_NOT_READY";
68
+ readonly NODE_NOT_CONTROLLER: "NODE_NOT_CONTROLLER";
67
69
  readonly COORDINATOR_DEPENDENCY_MISSING: "COORDINATOR_DEPENDENCY_MISSING";
68
70
  readonly TRANSPORT_DEPENDENCY_MISSING: "TRANSPORT_DEPENDENCY_MISSING";
69
71
  readonly REPLICATION_ENTRY_INVALID: "REPLICATION_ENTRY_INVALID";
@@ -829,7 +831,9 @@ interface QueryParams {
829
831
  * @public
830
832
  */
831
833
  type DocumentProjection = boolean | {
834
+ /** Names the fields to keep, so every field left out of this list is dropped. */
832
835
  include?: string[];
836
+ /** Names the fields to drop, so every other field is kept. */
833
837
  exclude?: string[];
834
838
  };
835
839
  /**
package/dist/server.d.ts CHANGED
@@ -1,11 +1,15 @@
1
- import { b as ServerOptions, N as NarsilServer } from './options-BORJg5XB.js';
2
- export { A as ASYNC_IMPORT_CAPABILITY, C as CorsOptions, E as ErrorEnvelope, O as OnRequestHook, R as REBUILD_ANALYSIS_CAPABILITY, c as RequestContext, d as RequestDenial, S as SERVER_CAPABILITIES, e as ServerLimits, T as TASK_CANCEL_CAPABILITY, a as TASK_FILTER_CAPABILITY } from './options-BORJg5XB.js';
3
- import { N as Narsil } from './engine-DEtRLYpB.js';
1
+ import { b as ServerOptions, N as NarsilServer } from './options-EXYFcem-.js';
2
+ export { A as ASYNC_IMPORT_CAPABILITY, C as CorsOptions, E as ErrorEnvelope, O as OnRequestHook, R as REBUILD_ANALYSIS_CAPABILITY, c as RequestContext, d as RequestDenial, S as SERVER_CAPABILITIES, e as ServerLimits, T as TASK_CANCEL_CAPABILITY, a as TASK_FILTER_CAPABILITY } from './options-EXYFcem-.js';
3
+ import { a as Narsil } from './engine-BE45D5ZG.js';
4
4
  import { h as TaskStore, e as TaskRecord, d as TaskProgress, b as ImportResult } from './tasks-CnMLpfk-.js';
5
5
  export { C as CapabilitiesResponse, a as CreateIndexEmbedding, i as CreateIndexRequest, H as HttpIndexConfig, I as ImportError, T as TaskListPage, c as TaskListQuery, f as TaskStatus, g as TaskType } from './tasks-CnMLpfk-.js';
6
- export { d as ServerErrorCodes } from './search-Dt4dkzpf.js';
6
+ export { d as ServerErrorCodes } from './search-DHuXwJYk.js';
7
+ import './config-fLC4lbmg.js';
8
+ import './config-Byyl1QqH.js';
7
9
  import './adapters-79OIa4xm.js';
8
- import './schema-emTQfEiX.js';
10
+ import './schema-ZRbk8BgH.js';
11
+ import './types-B5Tsaalz.js';
12
+ import './types-D5OwhtsQ.js';
9
13
 
10
14
  /**
11
15
  * Wraps a Narsil engine in an HTTP server. The caller builds and owns the engine
package/dist/server.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { ServerErrorCodes, NarsilError, ErrorCodes } from './chunk-EMI63EXP.mjs';
2
- export { ServerErrorCodes } from './chunk-EMI63EXP.mjs';
1
+ import { validateIndexName } from './chunk-WU2UZEVD.mjs';
2
+ import { ServerErrorCodes, NarsilError, ErrorCodes } from './chunk-GB2IIJJG.mjs';
3
+ export { ServerErrorCodes } from './chunk-GB2IIJJG.mjs';
3
4
  import { randomUUID } from 'crypto';
4
5
 
5
6
  // src/server/capabilities.ts
@@ -117,6 +118,8 @@ var STATUS_BY_CODE = {
117
118
  CONTROLLER_NOT_ACTIVE: 503,
118
119
  NODE_ALREADY_JOINED: 409,
119
120
  NODE_NOT_JOINED: 503,
121
+ NODE_NOT_READY: 503,
122
+ NODE_NOT_CONTROLLER: 503,
120
123
  NODE_BOOTSTRAP_FAILED: 503,
121
124
  COORDINATOR_DEPENDENCY_MISSING: 500,
122
125
  SNAPSHOT_SYNC_UNAUTHORIZED: 403,
@@ -649,6 +652,79 @@ function createCapabilitiesHandler() {
649
652
  return { report };
650
653
  }
651
654
 
655
+ // src/server/handlers/cluster.ts
656
+ function requireCluster(deps) {
657
+ if (deps.cluster === void 0) {
658
+ throw new NarsilError(
659
+ ErrorCodes.CLUSTER_OPERATION_UNSUPPORTED,
660
+ "This server fronts a single engine, so it reports no cluster",
661
+ {}
662
+ );
663
+ }
664
+ return deps.cluster;
665
+ }
666
+ function describeNode(cluster) {
667
+ return { ...cluster.getNodeInfo(), readiness: cluster.getReadiness(), isController: cluster.isControllerActive() };
668
+ }
669
+ function malformedIndexName(indexName) {
670
+ try {
671
+ validateIndexName(indexName);
672
+ return null;
673
+ } catch (error) {
674
+ return error instanceof Error ? error.message : String(error);
675
+ }
676
+ }
677
+ function createClusterHandlers(deps) {
678
+ async function topology(ctx) {
679
+ try {
680
+ const cluster = requireCluster(deps);
681
+ const [nodes, controllerNodeId] = await Promise.all([cluster.listNodes(), cluster.getControllerNodeId()]);
682
+ respondJson(ctx, {
683
+ node: describeNode(cluster),
684
+ controllerNodeId,
685
+ nodes: nodes.map((node) => ({ nodeId: node.nodeId, address: node.address, roles: [...node.roles] }))
686
+ });
687
+ } catch (err) {
688
+ respondError(ctx, err);
689
+ }
690
+ }
691
+ async function allocation(ctx) {
692
+ try {
693
+ const cluster = requireCluster(deps);
694
+ const indexName = ctx.params[0] ?? "";
695
+ const malformed = malformedIndexName(indexName);
696
+ if (malformed !== null) {
697
+ badRequest(ctx.res, malformed, { indexName });
698
+ return;
699
+ }
700
+ const table = await cluster.getAllocation(indexName);
701
+ if (table === null) {
702
+ respondJson(ctx, { indexName, allocated: false, version: null, partitions: [] });
703
+ return;
704
+ }
705
+ const partitions = [];
706
+ for (const [partitionId, assignment] of table.assignments) {
707
+ partitions.push({
708
+ partitionId,
709
+ state: assignment.state,
710
+ primary: assignment.primary,
711
+ primaryTerm: assignment.primaryTerm,
712
+ commitPoint: assignment.commitPoint,
713
+ replicas: [...assignment.replicas],
714
+ inSyncSet: [...assignment.inSyncSet],
715
+ lastHolders: [...assignment.lastHolders ?? []],
716
+ unassignedReason: assignment.unassignedReason ?? null
717
+ });
718
+ }
719
+ partitions.sort((left, right) => left.partitionId - right.partitionId);
720
+ respondJson(ctx, { indexName, allocated: partitions.length > 0, version: table.version, partitions });
721
+ } catch (err) {
722
+ respondError(ctx, err);
723
+ }
724
+ }
725
+ return { topology, allocation };
726
+ }
727
+
652
728
  // src/server/handlers/documents.ts
653
729
  function createDocumentHandlers(deps) {
654
730
  const { engine, limits } = deps;
@@ -804,11 +880,25 @@ function createHealthHandlers(deps) {
804
880
  respondJson(ctx, { status: "ok" });
805
881
  }
806
882
  function readyz(ctx) {
807
- if (deps.isReady()) {
808
- respondJson(ctx, { status: "ready" });
809
- } else {
810
- respondJson(ctx, { status: "unavailable" }, 503);
883
+ const cluster = deps.cluster;
884
+ if (cluster === void 0) {
885
+ if (deps.isReady()) {
886
+ respondJson(ctx, { status: "ready" });
887
+ } else {
888
+ respondJson(ctx, { status: "unavailable" }, 503);
889
+ }
890
+ return;
811
891
  }
892
+ const readiness = cluster.getReadiness();
893
+ const ready = deps.isReady() && readiness === "SERVING";
894
+ respondJson(
895
+ ctx,
896
+ {
897
+ status: ready ? "ready" : "unavailable",
898
+ cluster: describeNode(cluster)
899
+ },
900
+ ready ? 200 : 503
901
+ );
812
902
  }
813
903
  return { livez, readyz };
814
904
  }
@@ -1576,7 +1666,8 @@ var NarsilHttpServer = class {
1576
1666
  tasks: new TaskRegistry(taskStore, instanceId, limits.maxConcurrentTasks),
1577
1667
  limits,
1578
1668
  isReady: () => this.ready,
1579
- build: resolveBuild(options.build)
1669
+ build: resolveBuild(options.build),
1670
+ cluster: options.cluster
1580
1671
  };
1581
1672
  }
1582
1673
  async listen() {
@@ -1622,6 +1713,7 @@ var NarsilHttpServer = class {
1622
1713
  const search = createSearchHandlers(this.deps);
1623
1714
  const admin = createAdminHandlers(this.deps);
1624
1715
  const health = createHealthHandlers(this.deps);
1716
+ const cluster = createClusterHandlers(this.deps);
1625
1717
  const version = createVersionHandler(this.deps);
1626
1718
  const capabilities = createCapabilitiesHandler();
1627
1719
  const importNdjson = createImportHandler(this.deps);
@@ -1641,6 +1733,7 @@ var NarsilHttpServer = class {
1641
1733
  app.get("/health", json(health.livez, { maxBytes: 0, skipHooks: true }));
1642
1734
  app.get("/version", json(version.report, { maxBytes: 0, skipHooks: true }));
1643
1735
  app.get("/capabilities", json(capabilities.report, { maxBytes: 0, skipHooks: true }));
1736
+ app.get("/cluster", json(cluster.topology, { maxBytes: 0 }));
1644
1737
  app.get("/stats/memory", json(admin.memory, { maxBytes: 0 }));
1645
1738
  app.get("/tasks", json(admin.listTasks, { maxBytes: 0 }));
1646
1739
  app.get("/tasks/:id", json(admin.getTask, { paramCount: 1, maxBytes: 0 }));
@@ -1650,6 +1743,7 @@ var NarsilHttpServer = class {
1650
1743
  app.del("/indexes/:name", json(idx.drop, { paramCount: 1, maxBytes: 0 }));
1651
1744
  app.get("/indexes/:name/stats", json(idx.stats, { paramCount: 1, maxBytes: 0 }));
1652
1745
  app.get("/indexes/:name/partitions", json(idx.partitions, { paramCount: 1, maxBytes: 0 }));
1746
+ app.get("/indexes/:name/cluster", json(cluster.allocation, { paramCount: 1, maxBytes: 0 }));
1653
1747
  app.post("/indexes/:name/_clear", json(idx.clear, { paramCount: 1, maxBytes: 0 }));
1654
1748
  app.get("/indexes/:name/count", json(doc.count, { paramCount: 1, maxBytes: 0 }));
1655
1749
  app.post("/indexes/:name/documents", json(doc.insert, { paramCount: 1, needsBody: true, maxBytes: maxBodyBytes }));
@@ -1,4 +1,4 @@
1
- import { S as SchemaDefinition } from './schema-emTQfEiX.js';
1
+ import { S as SchemaDefinition } from './schema-ZRbk8BgH.js';
2
2
 
3
3
  /**
4
4
  * What a node does in a cluster. A node may hold several roles at once.
@@ -20,6 +20,19 @@ type NodeRole = 'data' | 'coordinator' | 'controller';
20
20
  * @public
21
21
  */
22
22
  type PartitionState = 'UNASSIGNED' | 'INITIALISING' | 'ACTIVE' | 'MIGRATING' | 'DECOMMISSIONING';
23
+ /**
24
+ * Why a partition stays `UNASSIGNED` after the controller has asked its last holders for a copy.
25
+ *
26
+ * `HOLDER_OFFLINE` means a last holder has yet to register again, so the controller is still waiting for it.
27
+ * `HOLDER_UNREACHABLE` means a registered holder left the request unanswered or answered with a payload the
28
+ * controller could not read. `HOLDER_IDENTITY_MISMATCH` means a holder answered under a different index identity,
29
+ * so its copy holds the documents of an earlier index of the same name, or it keeps no copy of that name at all.
30
+ * `HOLDER_WITHOUT_DATA` means every holder answered under the coordinator's identity with a list that left the
31
+ * partition out.
32
+ *
33
+ * @public
34
+ */
35
+ type UnassignedReason = 'HOLDER_OFFLINE' | 'HOLDER_UNREACHABLE' | 'HOLDER_IDENTITY_MISMATCH' | 'HOLDER_WITHOUT_DATA';
23
36
  /**
24
37
  * What one node has to offer, which the allocator weighs when it places
25
38
  * partitions.
@@ -67,12 +80,16 @@ interface PartitionAssignment {
67
80
  replicas: string[];
68
81
  /** These replicas are current enough to be promoted, which is the set a failover picks from. */
69
82
  inSyncSet: string[];
83
+ /** These nodes still hold a copy of a partition no node serves, and the field stays until the partition is `ACTIVE`. */
84
+ lastHolders?: string[];
70
85
  /** This says where the partition stands in its move between nodes. */
71
86
  state: PartitionState;
72
87
  /** This rises each time the primary changes, which is what makes a stale primary's writes detectable. */
73
88
  primaryTerm: number;
74
89
  /** The primary has acknowledged every sequence number up to here, so every in-sync replica holds them. */
75
90
  commitPoint: number;
91
+ /** Why the partition stays `UNASSIGNED`, which the controller records each time it asks the last holders. */
92
+ unassignedReason?: UnassignedReason;
76
93
  }
77
94
  /**
78
95
  * How one index's partitions are spread across the cluster.
@@ -323,4 +340,4 @@ interface ClusterCoordinator {
323
340
  shutdown(): Promise<void>;
324
341
  }
325
342
 
326
- export type { AllocationTable as A, ClusterCoordinator as C, NodeRole as N, PartitionAssignment as P, SchemaEvent as S, NodeCapacity as a, AllocationConstraints as b, AllocationEvent as c, NodeEvent as d, NodeRegistration as e, PartitionState as f };
343
+ export type { AllocationTable as A, ClusterCoordinator as C, NodeRole as N, PartitionAssignment as P, SchemaEvent as S, UnassignedReason as U, NodeRegistration as a, NodeCapacity as b, AllocationConstraints as c, AllocationEvent as d, NodeEvent as e, PartitionState as f };