@delali/narsil 0.2.4-next.51 → 0.2.4-next.52

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 (55) hide show
  1. package/README.md +27 -25
  2. package/dist/{capabilities-BWm4IITU.d.ts → capabilities-CuTSXvbN.d.ts} +2 -2
  3. package/dist/{chunk-BEIK3EOZ.mjs → chunk-2FO7KZ6K.mjs} +5468 -3837
  4. package/dist/{chunk-XE6XR2OE.mjs → chunk-3GWP2TO6.mjs} +1 -1
  5. package/dist/{chunk-KMMJMDUA.mjs → chunk-3L4U6EJQ.mjs} +15 -10
  6. package/dist/{chunk-MHE7YWAU.mjs → chunk-72AXTIBW.mjs} +2 -2
  7. package/dist/{chunk-CV5H7XL7.mjs → chunk-EY2ULUMT.mjs} +30 -7
  8. package/dist/chunk-IFBKLBGB.mjs +7 -0
  9. package/dist/{chunk-WYJXSLER.mjs → chunk-POWOTUKJ.mjs} +7 -1
  10. package/dist/chunk-T62YOSGS.mjs +4280 -0
  11. package/dist/chunk-U36IDJN7.mjs +366 -0
  12. package/dist/{chunk-ND34I3SV.mjs → chunk-ULJ36FK4.mjs} +3502 -2383
  13. package/dist/{chunk-RP23PWMV.mjs → chunk-WFKIGEBY.mjs} +3 -3
  14. package/dist/{chunk-CMVG2UEF.mjs → chunk-WM7IS45M.mjs} +55 -517
  15. package/dist/chunk-Y3L5MDRV.mjs +96 -0
  16. package/dist/{chunk-ZHUQKZBE.mjs → chunk-YPYULM5E.mjs} +20 -4
  17. package/dist/chunk-ZJ4CSAB2.mjs +13 -0
  18. package/dist/chunk-ZT5BRDFW.mjs +513 -0
  19. package/dist/{client-B_X6_AbT.d.ts → client-BMJWVxOP.d.ts} +3 -3
  20. package/dist/client.d.ts +8 -8
  21. package/dist/{config-OON7IJ_B.d.ts → config-BcYZkdMw.d.ts} +2 -2
  22. package/dist/{config-Czh2YNDi.d.ts → config-qMLpi5T4.d.ts} +2 -2
  23. package/dist/direct-executor-UJC36S2D.mjs +12 -0
  24. package/dist/distribution/coordinator/etcd.d.ts +2 -2
  25. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  26. package/dist/distribution.d.ts +8 -8
  27. package/dist/distribution.mjs +30 -15
  28. package/dist/{engine-9PtFY50q.d.ts → engine-zhms1qId.d.ts} +2 -2
  29. package/dist/index.browser.mjs +12845 -7766
  30. package/dist/index.d.ts +9 -21
  31. package/dist/index.mjs +13 -12
  32. package/dist/{memory-Bzd5xcEW.d.ts → memory-OmgkX_Z2.d.ts} +11 -1
  33. package/dist/persistence/durability/checkpoint-worker.d.ts +138 -3
  34. package/dist/persistence/durability/checkpoint-worker.mjs +31 -17
  35. package/dist/react.d.ts +6 -6
  36. package/dist/{schema-BqRx9t4E.d.ts → schema-OSWaaZzx.d.ts} +30 -6
  37. package/dist/serialization/crc32-worker.mjs +2 -1
  38. package/dist/server.d.ts +10 -10
  39. package/dist/server.mjs +75 -34
  40. package/dist/simd-DW5AtJ6B.d.ts +41 -0
  41. package/dist/{tasks-DT7L1p6w.d.ts → tasks-DiF1c9v2.d.ts} +1 -1
  42. package/dist/{thread-PZLEO6JL.mjs → thread-TLCX43WV.mjs} +129 -41
  43. package/dist/{types-DXPxaXPO.d.ts → types-LEzMyjVX.d.ts} +1 -1
  44. package/dist/vector/search-worker.d.ts +292 -128
  45. package/dist/vector/search-worker.mjs +70 -54
  46. package/dist/workers/entry.mjs +10 -7
  47. package/package.json +52 -31
  48. package/dist/brute-force-DbHYCTc3.d.ts +0 -8
  49. package/dist/chunk-CBLS46IE.mjs +0 -10
  50. package/dist/chunk-PIWKZPP7.mjs +0 -64
  51. package/dist/chunk-PMBHH44R.mjs +0 -492
  52. package/dist/chunk-X6YZO4MQ.mjs +0 -1572
  53. package/dist/direct-executor-OREXU72C.mjs +0 -12
  54. package/dist/vector/hnsw-build-worker.d.ts +0 -105
  55. package/dist/vector/hnsw-build-worker.mjs +0 -123
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
- import { c as StopWordOverride, C as CustomTokenizer } from './schema-BqRx9t4E.js';
2
- export { A as AnyDocument, B as BM25Params, E as EmbeddingFieldConfig, F as FieldType, I as IndexConfig, b as InsertOptions, P as PartitionConfig, S as SchemaDefinition, a as ScoringMode, V as VectorIndexConfig, d as VectorQuantizationMode, W as WriteOptions } from './schema-BqRx9t4E.js';
1
+ import { b as StopWordOverride, C as CustomTokenizer } from './schema-OSWaaZzx.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, c as ScoringMode, V as VectorIndexConfig, d as VectorQuantizationMode, e as VectorStorageMode, W as WriteOptions } from './schema-OSWaaZzx.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, X as WorkerCopyReport } from './memory-Bzd5xcEW.js';
6
- import { N as NarsilConfig } from './config-OON7IJ_B.js';
7
- export { A as AnalysisConfig, D as DurabilityConfig, I as IndexContext, a as IndexLifecycleConfig, b as InsertContext, M as MainCopyQueries, c as NarsilPlugin, P as PartitionContext, R as RemoveContext, S as SearchContext, d as StaleAnalysis, U as UpdateContext, W as WorkerConfig, e as WorkerContext } from './config-OON7IJ_B.js';
8
- import { a as Narsil } from './engine-9PtFY50q.js';
9
- export { D as DocumentWriteOperations, I as IndexLifecycleOperations, N as NarsilEventMap } from './engine-9PtFY50q.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, X as WorkerCopyReport } from './memory-OmgkX_Z2.js';
6
+ import { N as NarsilConfig } from './config-BcYZkdMw.js';
7
+ export { A as AnalysisConfig, D as DurabilityConfig, I as IndexContext, a as IndexLifecycleConfig, b as InsertContext, M as MainCopyQueries, c as NarsilPlugin, P as PartitionContext, R as RemoveContext, S as SearchContext, d as StaleAnalysis, U as UpdateContext, W as WorkerConfig, e as WorkerContext } from './config-BcYZkdMw.js';
8
+ import { a as Narsil } from './engine-zhms1qId.js';
9
+ export { D as DocumentWriteOperations, I as IndexLifecycleOperations, N as NarsilEventMap } from './engine-zhms1qId.js';
10
10
  export { E as EmbeddingAdapter, I as InvalidationAdapter, a as InvalidationEvent, b as PartitionStatistics, P as PersistenceAdapter } from './adapters-79OIa4xm.js';
11
+ export { i as isSimdAvailable } from './simd-DW5AtJ6B.js';
11
12
 
12
13
  /**
13
14
  * Registers a tokeniser under a name that an index config can then reference.
@@ -165,19 +166,6 @@ declare function getLanguage(name: string): LanguageModule;
165
166
  */
166
167
  declare function createNarsil(config?: NarsilConfig): Promise<Narsil>;
167
168
 
168
- /**
169
- * Reports whether the runtime loaded the SIMD module that speeds up vector
170
- * distance work.
171
- *
172
- * Vector search works either way, so read this to explain a difference in
173
- * search latency between two hosts rather than to decide whether to search.
174
- *
175
- * @returns True once the SIMD module has loaded.
176
- *
177
- * @public
178
- */
179
- declare function isSimdAvailable(): boolean;
180
-
181
169
  /**
182
170
  * Engine version written into the engine-version bytes of every `.nrsl` file,
183
171
  * for diagnostics.
@@ -190,4 +178,4 @@ declare function isSimdAvailable(): boolean;
190
178
  */
191
179
  declare const VERSION = "0.1.0";
192
180
 
193
- export { CustomTokenizer, LanguageModule, Narsil, NarsilConfig, StopWordOverride, VERSION, clearNormalizationCache, configureNormalizationCache, createNarsil, getLanguage, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, isSimdAvailable, registerLanguage, registerStopWords, registerTokenizer, resetNormalizationCache };
181
+ export { CustomTokenizer, LanguageModule, Narsil, NarsilConfig, StopWordOverride, VERSION, clearNormalizationCache, configureNormalizationCache, createNarsil, getLanguage, getNormalizationCacheSize, getStopWords, getTokenizer, hasStopWords, hasTokenizer, registerLanguage, registerStopWords, registerTokenizer, resetNormalizationCache };
package/dist/index.mjs CHANGED
@@ -1,18 +1,19 @@
1
- export { VERSION, createNarsil } from './chunk-ND34I3SV.mjs';
2
- import './chunk-ZHUQKZBE.mjs';
1
+ export { VERSION, createNarsil } from './chunk-ULJ36FK4.mjs';
3
2
  import './chunk-GCM3JFVA.mjs';
4
- import './chunk-PIWKZPP7.mjs';
5
- import './chunk-XE6XR2OE.mjs';
6
- export { getLanguage, getStopWords, getTokenizer, hasStopWords, hasTokenizer, registerLanguage, registerStopWords, registerTokenizer } from './chunk-BEIK3EOZ.mjs';
7
- export { clearNormalizationCache, configureNormalizationCache, getNormalizationCacheSize, resetNormalizationCache } from './chunk-CMVG2UEF.mjs';
3
+ import './chunk-U36IDJN7.mjs';
4
+ export { getLanguage, getStopWords, getTokenizer, hasStopWords, hasTokenizer, registerLanguage, registerStopWords, registerTokenizer } from './chunk-2FO7KZ6K.mjs';
8
5
  import './chunk-AV4IJKD2.mjs';
9
- import './chunk-CBLS46IE.mjs';
6
+ import './chunk-YPYULM5E.mjs';
7
+ import './chunk-3GWP2TO6.mjs';
8
+ export { clearNormalizationCache, configureNormalizationCache, getNormalizationCacheSize, resetNormalizationCache } from './chunk-WM7IS45M.mjs';
9
+ import './chunk-ZT5BRDFW.mjs';
10
10
  import './chunk-TGP5M5R7.mjs';
11
- import './chunk-PMBHH44R.mjs';
12
- export { isSimdAvailable } from './chunk-X6YZO4MQ.mjs';
13
- import './chunk-RP23PWMV.mjs';
14
- import './chunk-KMMJMDUA.mjs';
11
+ export { isSimdAvailable } from './chunk-T62YOSGS.mjs';
12
+ import './chunk-ZJ4CSAB2.mjs';
13
+ import './chunk-WFKIGEBY.mjs';
14
+ import './chunk-3L4U6EJQ.mjs';
15
+ import './chunk-IFBKLBGB.mjs';
15
16
  import './chunk-U2BKYEC7.mjs';
16
17
  import './chunk-2GWVQWGA.mjs';
17
- import './chunk-WYJXSLER.mjs';
18
+ import './chunk-POWOTUKJ.mjs';
18
19
  export { ClientErrorCodes, ErrorCodes, NarsilError, ServerErrorCodes } from './chunk-P4ELXVJQ.mjs';
@@ -1,4 +1,4 @@
1
- import { A as AnyDocument, S as SchemaDefinition, a as ScoringMode } from './schema-BqRx9t4E.js';
1
+ import { A as AnyDocument, S as SchemaDefinition, c as ScoringMode } from './schema-OSWaaZzx.js';
2
2
 
3
3
  /**
4
4
  * Every code a {@link NarsilError} carries, grouped by the part of the engine
@@ -845,6 +845,16 @@ interface VectorQueryConfig {
845
845
  * value when omitted.
846
846
  */
847
847
  efSearch?: number;
848
+ /**
849
+ * How many times `limit` a quantized field re-scores against its
850
+ * full-precision vectors before it returns the best `limit`. A value below
851
+ * 1 or one that is not a finite number fails with `CONFIG_INVALID`. The
852
+ * engine ignores this on a field whose quantization is `none`. When a query
853
+ * omits it, the engine takes 3 for `osq1` and `osq2`, 2 for `osq8`, and 2
854
+ * for `osq4` at 1,024 dimensions and above, but 5 for `osq4` below 1,024,
855
+ * where a four-bit code ranks the true neighbours less reliably.
856
+ */
857
+ oversample?: number;
848
858
  }
849
859
  /**
850
860
  * How a hybrid query merges its keyword ranking with its vector ranking.
@@ -1,4 +1,4 @@
1
- import { P as PartitionConfig, a as ScoringMode, V as VectorIndexConfig } from '../../schema-BqRx9t4E.js';
1
+ import { P as PartitionConfig, c as ScoringMode, V as VectorIndexConfig } from '../../schema-OSWaaZzx.js';
2
2
  import '../../adapters-79OIa4xm.js';
3
3
 
4
4
  interface IndexMetadata {
@@ -43,6 +43,119 @@ interface IndexEmbeddingMetadata {
43
43
  adapter?: string;
44
44
  fields: Record<string, string | string[]>;
45
45
  }
46
+ /**
47
+ * Persisted surface-form dictionary of one partition. Keys are the
48
+ * unstemmed display tokens; the value is the occurrence count when the
49
+ * surface equals its index token, or `[count, indexToken]` when stemming
50
+ * changed it.
51
+ */
52
+ type SerializedSurfaceForms = Record<string, number | [number, string]>;
53
+
54
+ interface SegmentPayload {
55
+ documentCount: number;
56
+ docIds: string[];
57
+ fieldNames: string[];
58
+ tokens: string[];
59
+ postingOffsets: Uint32Array;
60
+ postingDocIds: Uint32Array;
61
+ postingFrequencies: Uint16Array;
62
+ postingFieldIndices: Uint8Array;
63
+ positionOffsets: Uint32Array | null;
64
+ positionValues: Uint32Array | null;
65
+ fieldLengthNames: string[];
66
+ fieldLengthColumns: Uint32Array[];
67
+ totalFieldLengths: Record<string, number>;
68
+ docFrequencies: Record<string, number>;
69
+ surfaceForms: SerializedSurfaceForms | null;
70
+ numeric: Array<{
71
+ fieldPath: string;
72
+ docIds: Uint32Array;
73
+ values: Float64Array;
74
+ }>;
75
+ boolean: Array<{
76
+ fieldPath: string;
77
+ trueDocs: Uint32Array;
78
+ falseDocs: Uint32Array;
79
+ }>;
80
+ enums: Array<{
81
+ fieldPath: string;
82
+ values: string[];
83
+ offsets: Uint32Array;
84
+ docIds: Uint32Array;
85
+ }>;
86
+ geo: Array<{
87
+ fieldPath: string;
88
+ docIds: Uint32Array;
89
+ latitudes: Float64Array;
90
+ longitudes: Float64Array;
91
+ }>;
92
+ }
93
+
94
+ interface SegmentPostingColumns {
95
+ postingOffsets: Uint32Array;
96
+ postingDocIds: Uint32Array;
97
+ postingFrequencies: Uint16Array;
98
+ postingFieldIndices: Uint8Array;
99
+ positionOffsets: Uint32Array | null;
100
+ positionValues: Uint32Array | null;
101
+ }
102
+ interface SegmentColumns extends SegmentPostingColumns {
103
+ documentCount: number;
104
+ fieldNames: readonly string[];
105
+ fieldLengthNames: readonly string[];
106
+ fieldLengthColumns: readonly Uint32Array[];
107
+ totalFieldLengths: Readonly<Record<string, number>>;
108
+ numeric: SegmentPayload['numeric'];
109
+ boolean: SegmentPayload['boolean'];
110
+ enums: SegmentPayload['enums'];
111
+ geo: SegmentPayload['geo'];
112
+ surfaceForms: SerializedSurfaceForms | null;
113
+ }
114
+
115
+ interface EncodedDocumentTableData {
116
+ blob: Uint8Array;
117
+ offsets: Uint32Array;
118
+ }
119
+
120
+ interface ExternalIdTableData {
121
+ blob: Uint8Array;
122
+ offsets: Uint32Array;
123
+ sortedOrdinals: Uint32Array;
124
+ }
125
+
126
+ interface SurfaceTableData {
127
+ blob: Uint8Array;
128
+ offsets: Uint32Array;
129
+ counts: Uint32Array;
130
+ }
131
+
132
+ interface FrozenTokenTableData {
133
+ blob: Uint8Array;
134
+ offsets: Uint32Array;
135
+ payloadSlots: Uint32Array;
136
+ documentFrequencies: Uint32Array;
137
+ }
138
+
139
+ interface SharedSegmentSnapshot extends Omit<SegmentColumns, 'surfaceForms'> {
140
+ segmentId: string;
141
+ tokenTable: FrozenTokenTableData;
142
+ idTable: ExternalIdTableData;
143
+ documentTable: EncodedDocumentTableData;
144
+ /** The surface forms packed flat, or null where the segment holds none. */
145
+ surfaceTable: SurfaceTableData | null;
146
+ }
147
+
148
+ interface CapturedFrozenSegment {
149
+ snapshot: SharedSegmentSnapshot;
150
+ tombstonedDocIds: string[];
151
+ }
152
+ interface CapturedWholePartition {
153
+ partitionId: number;
154
+ totalPartitions: number;
155
+ docCount: number;
156
+ liveTail: Uint8Array | null;
157
+ frozen: CapturedFrozenSegment[];
158
+ }
46
159
 
47
160
  interface PartitionCheckpoint {
48
161
  partitionId: number;
@@ -50,20 +163,42 @@ interface PartitionCheckpoint {
50
163
  primaryTerm: number;
51
164
  }
52
165
 
166
+ interface SegmentRef {
167
+ id: number;
168
+ key: string;
169
+ docCount: number;
170
+ tombstoneCount: number;
171
+ }
172
+ interface PartitionManifestEntry {
173
+ partitionId: number;
174
+ nextSegmentId: number;
175
+ segments: SegmentRef[];
176
+ }
177
+
178
+ interface CheckpointSegmentsWritten {
179
+ checkpoint: PartitionCheckpoint[];
180
+ partitions: PartitionManifestEntry[];
181
+ }
182
+
53
183
  interface CheckpointWorkerRequest {
54
184
  root: string;
55
185
  metadata: IndexMetadata;
56
186
  targets: PartitionCheckpoint[];
57
187
  compactionThreshold: number;
188
+ capturedPartitions?: CapturedWholePartition[];
58
189
  }
59
190
  interface CheckpointWorkerSuccess {
60
191
  type: 'success';
192
+ segments: CheckpointSegmentsWritten;
61
193
  }
62
194
  interface CheckpointWorkerError {
63
195
  type: 'error';
64
196
  message: string;
65
197
  }
66
- type CheckpointWorkerMessage = CheckpointWorkerSuccess | CheckpointWorkerError;
198
+ interface CheckpointWorkerHeartbeat {
199
+ type: 'heartbeat';
200
+ }
201
+ type CheckpointWorkerMessage = CheckpointWorkerSuccess | CheckpointWorkerError | CheckpointWorkerHeartbeat;
67
202
  declare function handleRequest(raw: unknown): Promise<CheckpointWorkerSuccess>;
68
203
 
69
- export { type CheckpointWorkerError, type CheckpointWorkerMessage, type CheckpointWorkerRequest, type CheckpointWorkerSuccess, handleRequest };
204
+ export { type CheckpointWorkerError, type CheckpointWorkerHeartbeat, type CheckpointWorkerMessage, type CheckpointWorkerRequest, type CheckpointWorkerSuccess, handleRequest };
@@ -1,26 +1,27 @@
1
- import { createDurableDirectory, writeSegmentedCheckpoint } from '../../chunk-ND34I3SV.mjs';
2
- import '../../chunk-ZHUQKZBE.mjs';
1
+ import { CHECKPOINT_WORKER_HEARTBEAT_MS, createDurableDirectory, writeCheckpointSegments } from '../../chunk-ULJ36FK4.mjs';
3
2
  import '../../chunk-GCM3JFVA.mjs';
4
- import '../../chunk-PIWKZPP7.mjs';
5
- import '../../chunk-XE6XR2OE.mjs';
6
- import '../../chunk-BEIK3EOZ.mjs';
7
- import '../../chunk-CMVG2UEF.mjs';
3
+ import '../../chunk-U36IDJN7.mjs';
4
+ import '../../chunk-2FO7KZ6K.mjs';
8
5
  import '../../chunk-AV4IJKD2.mjs';
9
- import '../../chunk-CBLS46IE.mjs';
6
+ import '../../chunk-YPYULM5E.mjs';
7
+ import '../../chunk-3GWP2TO6.mjs';
8
+ import '../../chunk-WM7IS45M.mjs';
9
+ import '../../chunk-ZT5BRDFW.mjs';
10
10
  import '../../chunk-TGP5M5R7.mjs';
11
- import '../../chunk-PMBHH44R.mjs';
12
- import '../../chunk-X6YZO4MQ.mjs';
13
- import '../../chunk-RP23PWMV.mjs';
14
- import '../../chunk-KMMJMDUA.mjs';
11
+ import '../../chunk-T62YOSGS.mjs';
12
+ import '../../chunk-ZJ4CSAB2.mjs';
13
+ import '../../chunk-WFKIGEBY.mjs';
14
+ import '../../chunk-3L4U6EJQ.mjs';
15
+ import '../../chunk-IFBKLBGB.mjs';
15
16
  import '../../chunk-U2BKYEC7.mjs';
16
17
  import '../../chunk-2GWVQWGA.mjs';
17
- import '../../chunk-WYJXSLER.mjs';
18
+ import '../../chunk-POWOTUKJ.mjs';
18
19
  import '../../chunk-P4ELXVJQ.mjs';
19
20
 
20
21
  // src/persistence/durability/rebuild.ts
21
- async function rebuildSnapshotFromDurable(root, metadata, targets, compactionThreshold) {
22
+ async function rebuildSegmentsFromDurable(root, metadata, targets, compactionThreshold, capturedPartitions = []) {
22
23
  const directory = createDurableDirectory(root);
23
- await writeSegmentedCheckpoint({ directory, metadata, targets, compactionThreshold });
24
+ return writeCheckpointSegments({ directory, metadata, targets, compactionThreshold, capturedPartitions });
24
25
  }
25
26
 
26
27
  // src/persistence/durability/checkpoint-worker.ts
@@ -38,8 +39,17 @@ async function handleRequest(raw) {
38
39
  if (!Number.isInteger(request.compactionThreshold) || request.compactionThreshold <= 0) {
39
40
  throw new Error("Checkpoint request has an invalid compaction threshold");
40
41
  }
41
- await rebuildSnapshotFromDurable(request.root, request.metadata, request.targets, request.compactionThreshold);
42
- return { type: "success" };
42
+ if (request.capturedPartitions !== void 0 && !Array.isArray(request.capturedPartitions)) {
43
+ throw new Error("Checkpoint request names captured partitions in a shape the worker cannot read");
44
+ }
45
+ const segments = await rebuildSegmentsFromDurable(
46
+ request.root,
47
+ request.metadata,
48
+ request.targets,
49
+ request.compactionThreshold,
50
+ request.capturedPartitions
51
+ );
52
+ return { type: "success", segments };
43
53
  }
44
54
  async function setupAsync() {
45
55
  let parentPort = null;
@@ -54,10 +64,14 @@ async function setupAsync() {
54
64
  }
55
65
  const port = parentPort;
56
66
  port.on("message", (raw) => {
67
+ const heartbeat = setInterval(
68
+ () => port.postMessage({ type: "heartbeat" }),
69
+ CHECKPOINT_WORKER_HEARTBEAT_MS
70
+ );
57
71
  handleRequest(raw).then((result) => port.postMessage(result)).catch((err) => {
58
72
  const message = err instanceof Error ? err.message : String(err);
59
73
  port.postMessage({ type: "error", message });
60
- });
74
+ }).finally(() => clearInterval(heartbeat));
61
75
  });
62
76
  }
63
77
  function setupWorker() {
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 './memory-Bzd5xcEW.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 './memory-Bzd5xcEW.js';
3
- import { e as TaskRecord, d as TaskProgress, b as ImportResult, c as TaskListQuery, T as TaskListPage } from './tasks-DT7L1p6w.js';
4
- export { I as ImportError, f as TaskStatus, g as TaskType } from './tasks-DT7L1p6w.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 './memory-OmgkX_Z2.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 './memory-OmgkX_Z2.js';
3
+ import { e as TaskRecord, d as TaskProgress, b as ImportResult, c as TaskListQuery, T as TaskListPage } from './tasks-DiF1c9v2.js';
4
+ export { I as ImportError, f as TaskStatus, g as TaskType } from './tasks-DiF1c9v2.js';
5
5
  import { ReactNode, ReactElement } from 'react';
6
- import { N as NarsilClient, I as ImportSource } from './client-B_X6_AbT.js';
7
- import { A as AnyDocument } from './schema-BqRx9t4E.js';
6
+ import { N as NarsilClient, I as ImportSource } from './client-BMJWVxOP.js';
7
+ import { A as AnyDocument } from './schema-OSWaaZzx.js';
8
8
  import './adapters-79OIa4xm.js';
9
9
 
10
10
  /**
@@ -40,14 +40,36 @@ type SchemaDefinition = {
40
40
  [field: string]: FieldType | SchemaDefinition;
41
41
  };
42
42
  /**
43
- * How the engine stores vectors once a field is promoted to an HNSW graph.
43
+ * How the engine codes vectors once a field is promoted to an HNSW graph.
44
44
  *
45
- * `sq8` quantises each component to a signed byte, which cuts memory to a
46
- * quarter and costs a small amount of recall. `none` keeps full precision.
45
+ * Every `osq` mode applies optimised scalar quantization, which centres each
46
+ * vector on the field's centroid and then fits an interval to that vector
47
+ * alone. The digit in the mode name gives the bits that each dimension's code
48
+ * holds, so `osq8` stores a byte per dimension, while `osq4`, `osq2`, and
49
+ * `osq1` store four, two, and one bit. A search ranks its candidates by their
50
+ * codes before it re-scores the nearest of them against the full-precision
51
+ * vectors. `none` stores no code, so the engine ranks by the vectors alone.
52
+ *
53
+ * When the configuration leaves this unset, the engine takes `osq4` at 384
54
+ * dimensions and above and `osq8` below 384.
55
+ *
56
+ * @public
57
+ */
58
+ type VectorQuantizationMode = 'osq8' | 'osq4' | 'osq2' | 'osq1' | 'none';
59
+ /**
60
+ * Where a vector field keeps its full-precision vectors.
61
+ *
62
+ * A `memory` field holds them in memory. A `disk` field keeps its codes and
63
+ * its graph in memory, while it reads a full-precision vector from the
64
+ * field's checkpoint file whenever a search re-scores that candidate or a
65
+ * document read returns it. A `disk` field therefore needs filesystem
66
+ * durability. Asking for one on an engine without it fails with
67
+ * `CONFIG_INVALID`. When the configuration leaves this unset, an engine with
68
+ * filesystem durability takes `disk` and every other engine takes `memory`.
47
69
  *
48
70
  * @public
49
71
  */
50
- type VectorQuantizationMode = 'sq8' | 'none';
72
+ type VectorStorageMode = 'memory' | 'disk';
51
73
  /**
52
74
  * Controls when a vector field moves from a brute-force scan to an HNSW graph,
53
75
  * and how that graph is built.
@@ -74,8 +96,10 @@ interface VectorIndexConfig {
74
96
  efConstruction?: number;
75
97
  metric?: 'cosine' | 'dotProduct' | 'euclidean';
76
98
  };
77
- /** The engine stores the promoted vectors at this precision, and it quantises to `sq8` by default. Set `none` to keep full precision. */
99
+ /** The engine codes the promoted vectors in this mode. When this is unset, it picks the mode by the field's dimension. Set `none` to rank by the vectors alone. */
78
100
  quantization?: VectorQuantizationMode;
101
+ /** The engine keeps the field's full-precision vectors here. When this is unset, it picks `disk` under filesystem durability and `memory` on every other engine. */
102
+ storage?: VectorStorageMode;
79
103
  }
80
104
  /**
81
105
  * Connects an index to an embedding adapter and says which fields feed it.
@@ -250,4 +274,4 @@ interface InsertOptions extends WriteOptions {
250
274
  skipClone?: boolean;
251
275
  }
252
276
 
253
- export type { AnyDocument as A, BM25Params as B, CustomTokenizer as C, EmbeddingFieldConfig as E, FieldType as F, IndexConfig as I, PartitionConfig as P, SchemaDefinition as S, VectorIndexConfig as V, WriteOptions as W, ScoringMode as a, InsertOptions as b, StopWordOverride as c, VectorQuantizationMode as d };
277
+ export type { AnyDocument as A, BM25Params as B, CustomTokenizer as C, EmbeddingFieldConfig as E, FieldType as F, IndexConfig as I, PartitionConfig as P, SchemaDefinition as S, VectorIndexConfig as V, WriteOptions as W, InsertOptions as a, StopWordOverride as b, ScoringMode as c, VectorQuantizationMode as d, VectorStorageMode as e };
@@ -1,4 +1,5 @@
1
- import { crc32 } from '../chunk-KMMJMDUA.mjs';
1
+ import { crc32 } from '../chunk-3L4U6EJQ.mjs';
2
+ import '../chunk-IFBKLBGB.mjs';
2
3
 
3
4
  // src/serialization/crc32-worker.ts
4
5
  function handleRequest(raw) {
package/dist/server.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { b as ServerOptions, N as NarsilServer } from './capabilities-BWm4IITU.js';
2
- export { A as ASYNC_IMPORT_CAPABILITY, C as CorsOptions, E as ErrorEnvelope, I as INDEX_LIFECYCLE_CAPABILITY, 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 './capabilities-BWm4IITU.js';
3
- import { a as Narsil } from './engine-9PtFY50q.js';
4
- import { d as TaskProgress, b as ImportResult, h as TaskStore, e as TaskRecord } from './tasks-DT7L1p6w.js';
5
- export { C as CapabilitiesResponse, a as CreateIndexEmbedding, i as CreateIndexRequest, H as HttpIndexConfig, I as ImportError, M as MemoryStatsResponse, T as TaskListPage, c as TaskListQuery, f as TaskStatus, g as TaskType } from './tasks-DT7L1p6w.js';
6
- export { d as ServerErrorCodes } from './memory-Bzd5xcEW.js';
1
+ import { b as ServerOptions, N as NarsilServer } from './capabilities-CuTSXvbN.js';
2
+ export { A as ASYNC_IMPORT_CAPABILITY, C as CorsOptions, E as ErrorEnvelope, I as INDEX_LIFECYCLE_CAPABILITY, 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 './capabilities-CuTSXvbN.js';
3
+ import { a as Narsil } from './engine-zhms1qId.js';
4
+ import { d as TaskProgress, b as ImportResult, h as TaskStore, e as TaskRecord } from './tasks-DiF1c9v2.js';
5
+ export { C as CapabilitiesResponse, a as CreateIndexEmbedding, i as CreateIndexRequest, H as HttpIndexConfig, I as ImportError, M as MemoryStatsResponse, T as TaskListPage, c as TaskListQuery, f as TaskStatus, g as TaskType } from './tasks-DiF1c9v2.js';
6
+ export { d as ServerErrorCodes } from './memory-OmgkX_Z2.js';
7
7
  import './adapters-79OIa4xm.js';
8
- import './config-Czh2YNDi.js';
9
- import './config-OON7IJ_B.js';
10
- import './schema-BqRx9t4E.js';
11
- import './types-DXPxaXPO.js';
8
+ import './config-qMLpi5T4.js';
9
+ import './config-BcYZkdMw.js';
10
+ import './schema-OSWaaZzx.js';
11
+ import './types-LEzMyjVX.js';
12
12
  import './types-D5OwhtsQ.js';
13
13
 
14
14
  /**
package/dist/server.mjs CHANGED
@@ -1,10 +1,12 @@
1
- import { DEFAULT_MAX_RETAINED_TASKS, loadUWebSockets, resolveCors, authorizerFor, createSharedGateBuffer, gateSlotOfWorker, createRouteRunner, corsWriter, createSharedRequestGate, MAIN_THREAD_GATE_SLOT, registerServerRoutes, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_BODY_BYTES, RUNNING_TASK_TTL_MS, DEFAULT_TASK_PAGE_SIZE, TERMINAL_TASK_TTL_MS, TASK_PROGRESS_WRITE_INTERVAL_MS, serializeNarsilError, acceptorOf, createDocumentReadHandlers, sendError, respondError, respondJson, MAX_INDEX_TOUCH_INTERVAL_MS, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, clearGateSlot, parseJson, validateCreateIndex, rejectInvalid, validateBatch, serializeBatchResult, validateDocumentBody, validateSuggest, validateQuery, parseTaskListQuery, parseJsonOptional, validateRebalance, sendBinary, badRequest, SERVER_CAPABILITIES } from './chunk-CV5H7XL7.mjs';
2
- export { ASYNC_IMPORT_CAPABILITY, INDEX_LIFECYCLE_CAPABILITY, REBUILD_ANALYSIS_CAPABILITY, SERVER_CAPABILITIES, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, httpStatusForNarsilError } from './chunk-CV5H7XL7.mjs';
3
- import { validateIndexName } from './chunk-MHE7YWAU.mjs';
4
- import { engineCoreOf, resolveVectorText } from './chunk-PIWKZPP7.mjs';
5
- import './chunk-XE6XR2OE.mjs';
1
+ import { DEFAULT_MAX_RETAINED_TASKS, loadUWebSockets, resolveCors, authorizerFor, createSharedGateBuffer, gateSlotOfWorker, createRouteRunner, corsWriter, createSharedRequestGate, MAIN_THREAD_GATE_SLOT, registerServerRoutes, DEFAULT_MAX_CONCURRENT_TASKS, DEFAULT_MAX_TASK_PAGE_SIZE, DEFAULT_MAX_IMPORT_ERRORS, DEFAULT_MAX_FETCH_DOCUMENTS, DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_IMPORT_BATCH_SIZE, DEFAULT_MAX_LINE_BYTES, DEFAULT_MAX_IMPORT_BYTES, DEFAULT_MAX_BODY_BYTES, RUNNING_TASK_TTL_MS, DEFAULT_TASK_PAGE_SIZE, TERMINAL_TASK_TTL_MS, TASK_PROGRESS_WRITE_INTERVAL_MS, serializeNarsilError, acceptorOf, createDocumentReadHandlers, sendError, respondError, respondJson, MAX_INDEX_TOUCH_INTERVAL_MS, INDEX_TOUCH_INTERVALS_PER_IDLE_TIMEOUT, clearGateSlot, parseJson, validateCreateIndex, rejectInvalid, validateBatch, serializeBatchResult, validateDocumentBody, validateSuggest, validateQuery, parseTaskListQuery, parseJsonOptional, validateRebalance, sendBinary, IMPORT_FLUSHES_IN_FLIGHT, badRequest, SERVER_CAPABILITIES } from './chunk-EY2ULUMT.mjs';
2
+ export { ASYNC_IMPORT_CAPABILITY, INDEX_LIFECYCLE_CAPABILITY, REBUILD_ANALYSIS_CAPABILITY, SERVER_CAPABILITIES, TASK_CANCEL_CAPABILITY, TASK_FILTER_CAPABILITY, httpStatusForNarsilError } from './chunk-EY2ULUMT.mjs';
3
+ import { validateIndexName } from './chunk-72AXTIBW.mjs';
4
+ import { engineCoreOf, providedDocId, resolveVectorText } from './chunk-U36IDJN7.mjs';
6
5
  import { detectRuntime } from './chunk-AV4IJKD2.mjs';
7
- import './chunk-CBLS46IE.mjs';
6
+ import './chunk-3GWP2TO6.mjs';
7
+ import './chunk-ZT5BRDFW.mjs';
8
+ import './chunk-WFKIGEBY.mjs';
9
+ import { startIdleHeapCleanup } from './chunk-Y3L5MDRV.mjs';
8
10
  import { NarsilError, ServerErrorCodes, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
9
11
  export { ServerErrorCodes } from './chunk-P4ELXVJQ.mjs';
10
12
  import { randomUUID } from 'crypto';
@@ -444,10 +446,7 @@ async function runImport(engine, options) {
444
446
  failed += 1;
445
447
  if (errors.length < maxErrors) errors.push(error);
446
448
  };
447
- const flush = async () => {
448
- if (pending.length === 0) return;
449
- const documents = pending.map((entry) => entry.document);
450
- pending = [];
449
+ const flushDocuments = async (documents) => {
451
450
  const result = await engine.insertBatch(indexName, documents, { skipClone: true });
452
451
  indexed += result.succeeded.length;
453
452
  for (const failure of result.failed) {
@@ -455,36 +454,74 @@ async function runImport(engine, options) {
455
454
  recordFailure({ docId: failure.docId, code: serialized.code, message: serialized.message });
456
455
  }
457
456
  };
457
+ const inFlight = /* @__PURE__ */ new Set();
458
+ let flushError;
459
+ const startFlush = () => {
460
+ if (pending.length === 0) return;
461
+ const documents = pending.map((entry) => entry.document);
462
+ pending = [];
463
+ const run = flushDocuments(documents).catch((err) => {
464
+ flushError ??= err;
465
+ }).finally(() => inFlight.delete(run));
466
+ inFlight.add(run);
467
+ };
468
+ const settleFlushes = async () => {
469
+ await Promise.all(inFlight);
470
+ if (flushError !== void 0) throw flushError;
471
+ };
458
472
  const report = () => {
459
473
  onProgress?.({ indexed, failed, bytesProcessed, bytesTotal });
460
474
  };
461
- for (const line of iterateNdjson(body, maxLineBytes)) {
462
- bytesProcessed = line.bytesConsumed;
463
- let parsed;
464
- try {
465
- parsed = JSON.parse(line.text);
466
- } catch {
467
- recordFailure({ line: line.lineNumber, code: ServerErrorCodes.INVALID_JSON, message: "Line is not valid JSON" });
468
- continue;
469
- }
470
- if (!isPlainObject(parsed)) {
471
- recordFailure({
472
- line: line.lineNumber,
473
- code: ServerErrorCodes.INVALID_REQUEST,
474
- message: "Line is not a JSON object"
475
- });
476
- continue;
475
+ const seenIds = /* @__PURE__ */ new Set();
476
+ const repeated = [];
477
+ try {
478
+ for (const line of iterateNdjson(body, maxLineBytes)) {
479
+ bytesProcessed = line.bytesConsumed;
480
+ let parsed;
481
+ try {
482
+ parsed = JSON.parse(line.text);
483
+ } catch {
484
+ recordFailure({ line: line.lineNumber, code: ServerErrorCodes.INVALID_JSON, message: "Line is not valid JSON" });
485
+ continue;
486
+ }
487
+ if (!isPlainObject(parsed)) {
488
+ recordFailure({
489
+ line: line.lineNumber,
490
+ code: ServerErrorCodes.INVALID_REQUEST,
491
+ message: "Line is not a JSON object"
492
+ });
493
+ continue;
494
+ }
495
+ const docId = providedDocId(parsed);
496
+ if (docId !== void 0) {
497
+ if (seenIds.has(docId)) {
498
+ repeated.push({ document: parsed, line: line.lineNumber });
499
+ continue;
500
+ }
501
+ seenIds.add(docId);
502
+ }
503
+ pending.push({ document: parsed, line: line.lineNumber });
504
+ if (pending.length >= batchSize) {
505
+ signal?.throwIfAborted();
506
+ startFlush();
507
+ if (inFlight.size >= IMPORT_FLUSHES_IN_FLIGHT) await Promise.race(inFlight);
508
+ if (flushError !== void 0) await settleFlushes();
509
+ report();
510
+ await yieldToEventLoop();
511
+ signal?.throwIfAborted();
512
+ }
477
513
  }
478
- pending.push({ document: parsed, line: line.lineNumber });
479
- if (pending.length >= batchSize) {
480
- signal?.throwIfAborted();
481
- await flush();
482
- report();
483
- await yieldToEventLoop();
514
+ startFlush();
515
+ await settleFlushes();
516
+ for (let start = 0; start < repeated.length; start += batchSize) {
484
517
  signal?.throwIfAborted();
518
+ await flushDocuments(repeated.slice(start, start + batchSize).map((entry) => entry.document));
485
519
  }
520
+ } catch (err) {
521
+ await Promise.allSettled(inFlight);
522
+ report();
523
+ throw err;
486
524
  }
487
- await flush();
488
525
  bytesProcessed = bytesTotal;
489
526
  report();
490
527
  return { indexed, failed, errors, errorsTruncated: failed > errors.length };
@@ -804,7 +841,7 @@ async function answerRequest(handlers, request) {
804
841
  params: request.params,
805
842
  query: new URLSearchParams(request.query),
806
843
  contentType: request.contentType,
807
- rawBody: request.rawBody === null ? null : Buffer.from(request.rawBody),
844
+ rawBody: request.rawBody === null ? null : Buffer.from(request.rawBody.buffer, request.rawBody.byteOffset, request.rawBody.byteLength),
808
845
  abort: NO_ABORT,
809
846
  hookContext: request.hookContext
810
847
  };
@@ -1262,6 +1299,7 @@ var NarsilHttpServer = class {
1262
1299
  listenSocket = null;
1263
1300
  ready = false;
1264
1301
  threads = null;
1302
+ stopIdleHeapCleanup = () => void 0;
1265
1303
  constructor(engine, options = {}) {
1266
1304
  this.host = options.host ?? "127.0.0.1";
1267
1305
  this.port = options.port ?? 9876;
@@ -1301,10 +1339,13 @@ var NarsilHttpServer = class {
1301
1339
  });
1302
1340
  });
1303
1341
  await this.deps.tasks.reconcile();
1342
+ this.stopIdleHeapCleanup = startIdleHeapCleanup();
1304
1343
  this.ready = true;
1305
1344
  }
1306
1345
  async close() {
1307
1346
  this.ready = false;
1347
+ this.stopIdleHeapCleanup();
1348
+ this.stopIdleHeapCleanup = () => void 0;
1308
1349
  if (this.listenSocket && this.uws) {
1309
1350
  this.uws.us_listen_socket_close(this.listenSocket);
1310
1351
  this.listenSocket = null;