@delali/narsil 0.2.4-next.50 → 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-JrVQOwi4.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-CBOVHdjp.d.ts → client-BMJWVxOP.d.ts} +5 -5
  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 +80 -36
  40. package/dist/simd-DW5AtJ6B.d.ts +41 -0
  41. package/dist/{tasks-Bn4R1NPN.d.ts → tasks-DiF1c9v2.d.ts} +20 -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
@@ -0,0 +1,96 @@
1
+ import { performance } from 'perf_hooks';
2
+ import { getHeapStatistics, setFlagsFromString } from 'v8';
3
+ import { runInNewContext } from 'vm';
4
+
5
+ // src/runtime/idle-heap-cleanup.ts
6
+
7
+ // src/runtime/constants.ts
8
+ var IDLE_HEAP_CLEANUP_CHECK_MS = 1e3;
9
+ var IDLE_HEAP_CLEANUP_QUIET_CHECKS = 2;
10
+ var IDLE_HEAP_CLEANUP_MAX_UTILISATION = 0.1;
11
+ var IDLE_HEAP_CLEANUP_MAX_GROWTH_BYTES = 4 * 1024 * 1024;
12
+ var IDLE_HEAP_CLEANUP_MIN_RECLAIMABLE_BYTES = 64 * 1024 * 1024;
13
+
14
+ // src/runtime/idle-heap-cleaner.ts
15
+ function createIdleHeapCleaner(deps) {
16
+ let quietChecks = 0;
17
+ let liveBytesAfterLastCleanup = 0;
18
+ let heldBytesAtLastCheck = Number.POSITIVE_INFINITY;
19
+ return {
20
+ check() {
21
+ const busy = deps.utilisationSinceLastCheck() > IDLE_HEAP_CLEANUP_MAX_UTILISATION;
22
+ const before = deps.readHeap();
23
+ if (before === null) return false;
24
+ const heapGrew = before.heldBytes > heldBytesAtLastCheck + IDLE_HEAP_CLEANUP_MAX_GROWTH_BYTES;
25
+ heldBytesAtLastCheck = before.heldBytes;
26
+ if (busy || heapGrew) {
27
+ quietChecks = 0;
28
+ return false;
29
+ }
30
+ quietChecks += 1;
31
+ if (quietChecks < IDLE_HEAP_CLEANUP_QUIET_CHECKS) return false;
32
+ if (before.heldBytes - liveBytesAfterLastCleanup < IDLE_HEAP_CLEANUP_MIN_RECLAIMABLE_BYTES) return false;
33
+ if (!deps.collectAndReturnPages()) return false;
34
+ const after = deps.readHeap();
35
+ liveBytesAfterLastCleanup = after?.liveBytes ?? before.liveBytes;
36
+ heldBytesAtLastCheck = after?.heldBytes ?? before.heldBytes;
37
+ quietChecks = 0;
38
+ return true;
39
+ }
40
+ };
41
+ }
42
+
43
+ // src/runtime/idle-heap-cleanup.ts
44
+ var collectorOfThisThread = null;
45
+ function acquireCollector() {
46
+ if (collectorOfThisThread !== null) return collectorOfThisThread;
47
+ try {
48
+ setFlagsFromString("--expose-gc");
49
+ const collect = runInNewContext("gc");
50
+ setFlagsFromString("--no-expose-gc");
51
+ if (typeof collect !== "function") return null;
52
+ collectorOfThisThread = collect;
53
+ return collectorOfThisThread;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ function collectAndReturnPages() {
59
+ const collect = acquireCollector();
60
+ if (collect === null) return false;
61
+ try {
62
+ collect({ type: "major", execution: "sync", flavor: "last-resort" });
63
+ return true;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ function readHeap() {
69
+ try {
70
+ const stats = getHeapStatistics();
71
+ return { heldBytes: stats.total_physical_size, liveBytes: stats.used_heap_size };
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+ function startIdleHeapCleanup() {
77
+ if (typeof performance.eventLoopUtilization !== "function") return () => void 0;
78
+ let lastReading = performance.eventLoopUtilization();
79
+ const cleaner = createIdleHeapCleaner({
80
+ utilisationSinceLastCheck() {
81
+ const reading = performance.eventLoopUtilization();
82
+ const { utilization } = performance.eventLoopUtilization(reading, lastReading);
83
+ lastReading = reading;
84
+ return utilization;
85
+ },
86
+ readHeap,
87
+ collectAndReturnPages
88
+ });
89
+ const timer = setInterval(() => {
90
+ cleaner.check();
91
+ }, IDLE_HEAP_CLEANUP_CHECK_MS);
92
+ timer.unref();
93
+ return () => clearInterval(timer);
94
+ }
95
+
96
+ export { startIdleHeapCleanup };
@@ -1,5 +1,7 @@
1
- import { MAX_INDEX_NAME_LENGTH, INDEX_NAME_PATTERN, MAX_DOC_ID_LENGTH, DEFAULT_LIMIT, DEFAULT_SUGGEST_LIMIT, MAX_SUGGEST_LIMIT, MAX_SUGGEST_SCATTER_LIMIT, DEFAULT_OFFSET } from './chunk-XE6XR2OE.mjs';
2
- import { clampRowCount, sortSignatureOf, requireSortableFields, decodePageCursor, requireMatchingCursor, resolveProjection, encodePageCursor, requireWithinResultWindow, normalizeSort, fanOutQuery, flattenSchema, applySorting, applyPagination, countsWithoutScores, fanOutMatchCount, tokenize, fnv1aBytes, fulltextMatches, mergeFacets, pruneStatsToQueryTerms, partitionsIn } from './chunk-CMVG2UEF.mjs';
1
+ import { MAX_INDEX_NAME_LENGTH, INDEX_NAME_PATTERN, MAX_DOC_ID_LENGTH, DEFAULT_LIMIT, DEFAULT_SUGGEST_LIMIT, MAX_SUGGEST_LIMIT, MAX_SUGGEST_SCATTER_LIMIT, DEFAULT_OFFSET } from './chunk-3GWP2TO6.mjs';
2
+ import { clampRowCount, sortSignatureOf, requireSortableFields, decodePageCursor, requireMatchingCursor, resolveProjection, encodePageCursor, requireWithinResultWindow, normalizeSort, fanOutQuery, applySorting, applyPagination, countsWithoutScores, fanOutMatchCount, tokenize, fnv1aBytes, fulltextMatches, mergeFacets, pruneStatsToQueryTerms, partitionsIn } from './chunk-WM7IS45M.mjs';
3
+ import { flattenSchema } from './chunk-ZT5BRDFW.mjs';
4
+ import { MIN_OVERSAMPLE } from './chunk-ZJ4CSAB2.mjs';
3
5
  import { compareCodePoints, compareComparableKeys } from './chunk-2GWVQWGA.mjs';
4
6
  import { NarsilError, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
5
7
 
@@ -938,6 +940,18 @@ function minMaxNormalize(docs) {
938
940
  }
939
941
 
940
942
  // src/engine/query/vector.ts
943
+ function oversampleOf(vectorConfig) {
944
+ const oversample = vectorConfig.oversample;
945
+ if (oversample === void 0) return void 0;
946
+ if (typeof oversample !== "number" || !Number.isFinite(oversample) || oversample < MIN_OVERSAMPLE) {
947
+ throw new NarsilError(
948
+ ErrorCodes.CONFIG_INVALID,
949
+ `vector.oversample must be a finite number of at least ${MIN_OVERSAMPLE}`,
950
+ { oversample }
951
+ );
952
+ }
953
+ return oversample;
954
+ }
941
955
  async function executeVectorSearch(params, context, limit, offset) {
942
956
  const { manager, config, partitionIds } = context;
943
957
  const vectorConfig = params.vector;
@@ -965,7 +979,8 @@ async function executeVectorSearch(params, context, limit, offset) {
965
979
  minSimilarity: vectorConfig.similarity ?? -Infinity,
966
980
  ...filterDocIds !== void 0 ? { filterDocIds } : {},
967
981
  ...filterPartitions !== void 0 ? { filterPartitions } : {},
968
- efSearch: vectorConfig.efSearch
982
+ efSearch: vectorConfig.efSearch,
983
+ oversample: oversampleOf(vectorConfig)
969
984
  });
970
985
  const scored = vectorResultsToScored(results);
971
986
  return { scored, totalMatched: scored.length };
@@ -994,7 +1009,8 @@ async function vectorLeg(vectorConfig, queryVector, context, filterDocIds, k) {
994
1009
  minSimilarity: vectorConfig.similarity ?? -Infinity,
995
1010
  ...filterDocIds !== void 0 ? { filterDocIds } : {},
996
1011
  ...filterPartitions !== void 0 ? { filterPartitions } : {},
997
- efSearch: vectorConfig.efSearch
1012
+ efSearch: vectorConfig.efSearch,
1013
+ oversample: oversampleOf(vectorConfig)
998
1014
  });
999
1015
  return vectorResultsToScored(results);
1000
1016
  }
@@ -0,0 +1,13 @@
1
+ // src/vector/osq/constants.ts
2
+ var OSQ_GRID = { 1: 0.798, 2: 1.493, 4: 2.514, 8: 3.922 };
3
+ var OSQ_STEPS = { 1: 1, 2: 3, 4: 15, 8: 255 };
4
+ var OSQ_QUERY_BITS = { 1: 4, 2: 4, 4: 4, 8: 8 };
5
+ var OSQ_LAMBDA = 0.1;
6
+ var OSQ_REFINE_ROUNDS = 5;
7
+ var OSQ_REFINE_TOLERANCE = 1e-8;
8
+ var DEFAULT_OVERSAMPLE_BY_BITS = { 1: 3, 2: 3, 4: 2, 8: 2 };
9
+ var DEFAULT_OSQ4_NARROW_OVERSAMPLE = 5;
10
+ var OSQ4_NARROW_DIMENSION_LIMIT = 1024;
11
+ var MIN_OVERSAMPLE = 1;
12
+
13
+ export { DEFAULT_OSQ4_NARROW_OVERSAMPLE, DEFAULT_OVERSAMPLE_BY_BITS, MIN_OVERSAMPLE, OSQ4_NARROW_DIMENSION_LIMIT, OSQ_GRID, OSQ_LAMBDA, OSQ_QUERY_BITS, OSQ_REFINE_ROUNDS, OSQ_REFINE_TOLERANCE, OSQ_STEPS };
@@ -0,0 +1,513 @@
1
+ import { MAX_M } from './chunk-WFKIGEBY.mjs';
2
+ import { NarsilError, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
3
+
4
+ // src/schema/constants.ts
5
+ var MAX_NESTING_DEPTH = 4;
6
+ var MAX_DOCUMENT_NESTING_DEPTH = 32;
7
+
8
+ // src/schema/validator/shared.ts
9
+ var SORTABLE_TEXT_FIELD_TYPE = "string:sortable";
10
+ var SCALAR_FIELD_TYPES = /* @__PURE__ */ new Set([
11
+ "string",
12
+ SORTABLE_TEXT_FIELD_TYPE,
13
+ "number",
14
+ "boolean",
15
+ "enum",
16
+ "geopoint",
17
+ "string[]",
18
+ "number[]",
19
+ "boolean[]",
20
+ "enum[]"
21
+ ]);
22
+ function isTextFieldType(fieldType) {
23
+ return fieldType === "string" || fieldType === SORTABLE_TEXT_FIELD_TYPE;
24
+ }
25
+ var VECTOR_PATTERN = /^vector\[(\d+)]$/;
26
+ var FIELD_NAME_PATTERN = /^[A-Za-z0-9_]+$/;
27
+ var RESERVED_ROOT_FIELDS = /* @__PURE__ */ new Set(["id"]);
28
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
29
+ function isPlainObject(value) {
30
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31
+ }
32
+
33
+ // src/schema/validator/schema.ts
34
+ function validateSchemaFields(schema, depth, prefix) {
35
+ if (depth > MAX_NESTING_DEPTH) {
36
+ throw new NarsilError(
37
+ ErrorCodes.SCHEMA_DEPTH_EXCEEDED,
38
+ `Schema nesting exceeds the maximum depth of ${MAX_NESTING_DEPTH} at "${prefix}"`,
39
+ { path: prefix, maxDepth: MAX_NESTING_DEPTH }
40
+ );
41
+ }
42
+ for (const [field, type] of Object.entries(schema)) {
43
+ const path = prefix ? `${prefix}.${field}` : field;
44
+ if (PROTOTYPE_POLLUTION_KEYS.has(field)) {
45
+ throw new NarsilError(ErrorCodes.SCHEMA_INVALID_TYPE, `Field name "${field}" is not allowed in a schema`, {
46
+ field,
47
+ path
48
+ });
49
+ }
50
+ if (!FIELD_NAME_PATTERN.test(field)) {
51
+ throw new NarsilError(
52
+ ErrorCodes.SCHEMA_INVALID_TYPE,
53
+ `Field name "${field}" contains characters that are not allowed; use letters, digits, and underscores only`,
54
+ { field, path }
55
+ );
56
+ }
57
+ if (depth === 1 && RESERVED_ROOT_FIELDS.has(field)) {
58
+ throw new NarsilError(
59
+ ErrorCodes.SCHEMA_INVALID_TYPE,
60
+ `Field "${field}" is reserved and cannot be defined in a schema`,
61
+ { field, path }
62
+ );
63
+ }
64
+ if (isPlainObject(type)) {
65
+ validateSchemaFields(type, depth + 1, path);
66
+ continue;
67
+ }
68
+ if (typeof type !== "string") {
69
+ throw new NarsilError(
70
+ ErrorCodes.SCHEMA_INVALID_TYPE,
71
+ `Field "${path}" has an invalid type definition: ${String(type)}`,
72
+ {
73
+ field: path,
74
+ type: String(type)
75
+ }
76
+ );
77
+ }
78
+ if (SCALAR_FIELD_TYPES.has(type)) continue;
79
+ const vectorMatch = VECTOR_PATTERN.exec(type);
80
+ if (vectorMatch) {
81
+ const dimension = Number.parseInt(vectorMatch[1], 10);
82
+ if (dimension <= 0) {
83
+ throw new NarsilError(
84
+ ErrorCodes.SCHEMA_INVALID_VECTOR_DIMENSION,
85
+ `Vector field "${path}" must have a positive dimension, got ${dimension}`,
86
+ { field: path, dimension }
87
+ );
88
+ }
89
+ continue;
90
+ }
91
+ throw new NarsilError(ErrorCodes.SCHEMA_INVALID_TYPE, `Field "${path}" has an unsupported type: "${type}"`, {
92
+ field: path,
93
+ type
94
+ });
95
+ }
96
+ }
97
+ function validateSchema(schema) {
98
+ if (!isPlainObject(schema)) {
99
+ throw new NarsilError(ErrorCodes.SCHEMA_INVALID_TYPE, "Schema must be a plain object", {
100
+ received: typeof schema
101
+ });
102
+ }
103
+ if (Object.keys(schema).length === 0) {
104
+ throw new NarsilError(ErrorCodes.SCHEMA_INVALID_TYPE, "Schema must define at least one field");
105
+ }
106
+ validateSchemaFields(schema, 1, "");
107
+ }
108
+ function flattenRecursive(schema, prefix, result) {
109
+ for (const [field, type] of Object.entries(schema)) {
110
+ const path = prefix ? `${prefix}.${field}` : field;
111
+ if (isPlainObject(type)) {
112
+ flattenRecursive(type, path, result);
113
+ } else {
114
+ result[path] = type;
115
+ }
116
+ }
117
+ }
118
+ function flattenSchema(schema) {
119
+ const result = {};
120
+ flattenRecursive(schema, "", result);
121
+ return result;
122
+ }
123
+ function extractVectorFieldsFromSchema(schema) {
124
+ const flat = flattenSchema(schema);
125
+ const result = /* @__PURE__ */ new Map();
126
+ for (const [fieldPath, fieldType] of Object.entries(flat)) {
127
+ const match = VECTOR_PATTERN.exec(fieldType);
128
+ if (match) {
129
+ result.set(fieldPath, Number.parseInt(match[1], 10));
130
+ }
131
+ }
132
+ return result;
133
+ }
134
+
135
+ // src/schema/validator/field-values.ts
136
+ function validateGeopoint(path, value) {
137
+ if (!isPlainObject(value)) {
138
+ throw new NarsilError(
139
+ ErrorCodes.SCHEMA_INVALID_GEOPOINT,
140
+ `Field "${path}" expected a geopoint object with lat and lon`,
141
+ {
142
+ field: path,
143
+ received: Array.isArray(value) ? "array" : typeof value
144
+ }
145
+ );
146
+ }
147
+ if (typeof value.lat !== "number" || typeof value.lon !== "number" || Number.isNaN(value.lat) || Number.isNaN(value.lon)) {
148
+ throw new NarsilError(ErrorCodes.SCHEMA_INVALID_GEOPOINT, `Field "${path}" geopoint requires numeric lat and lon`, {
149
+ field: path,
150
+ lat: Number.isNaN(value.lat) ? "NaN" : typeof value.lat,
151
+ lon: Number.isNaN(value.lon) ? "NaN" : typeof value.lon
152
+ });
153
+ }
154
+ if (value.lat < -90 || value.lat > 90) {
155
+ throw new NarsilError(
156
+ ErrorCodes.SCHEMA_INVALID_GEOPOINT,
157
+ `Field "${path}" geopoint lat must be between -90 and 90`,
158
+ {
159
+ field: path,
160
+ lat: value.lat
161
+ }
162
+ );
163
+ }
164
+ if (value.lon < -180 || value.lon > 180) {
165
+ throw new NarsilError(
166
+ ErrorCodes.SCHEMA_INVALID_GEOPOINT,
167
+ `Field "${path}" geopoint lon must be between -180 and 180`,
168
+ {
169
+ field: path,
170
+ lon: value.lon
171
+ }
172
+ );
173
+ }
174
+ }
175
+ function validateVector(path, value, expectedDimension) {
176
+ const isArray = Array.isArray(value);
177
+ const isFloat32 = value instanceof Float32Array;
178
+ if (!isArray && !isFloat32) {
179
+ throw new NarsilError(
180
+ ErrorCodes.DOC_VALIDATION_FAILED,
181
+ `Field "${path}" expected a vector (Array or Float32Array)`,
182
+ { field: path, received: typeof value }
183
+ );
184
+ }
185
+ const arr = value;
186
+ if (arr.length !== expectedDimension) {
187
+ throw new NarsilError(
188
+ ErrorCodes.DOC_VALIDATION_FAILED,
189
+ `Field "${path}" expected vector of dimension ${expectedDimension}, got ${arr.length}`,
190
+ { field: path, expected: expectedDimension, received: arr.length }
191
+ );
192
+ }
193
+ for (let i = 0; i < arr.length; i++) {
194
+ if (typeof arr[i] !== "number" || Number.isNaN(arr[i])) {
195
+ throw new NarsilError(
196
+ ErrorCodes.DOC_VALIDATION_FAILED,
197
+ `Field "${path}" vector element at index ${i} is not a valid number`,
198
+ { field: path, index: i, value: arr[i] }
199
+ );
200
+ }
201
+ }
202
+ }
203
+ function validateTypedArray(path, value, elementType) {
204
+ if (!Array.isArray(value)) {
205
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path}" expected an array`, {
206
+ field: path,
207
+ received: typeof value
208
+ });
209
+ }
210
+ for (let i = 0; i < value.length; i++) {
211
+ const el = value[i];
212
+ if (typeof el !== elementType) {
213
+ throw new NarsilError(
214
+ ErrorCodes.DOC_VALIDATION_FAILED,
215
+ `Field "${path}" array element at index ${i} expected ${elementType}, got ${typeof el}`,
216
+ { field: path, index: i, expected: elementType, received: typeof el }
217
+ );
218
+ }
219
+ if (elementType === "number" && Number.isNaN(el)) {
220
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path}" array element at index ${i} is NaN`, {
221
+ field: path,
222
+ index: i
223
+ });
224
+ }
225
+ }
226
+ }
227
+ function validateFieldValue(path, value, type) {
228
+ switch (type) {
229
+ case "string":
230
+ case SORTABLE_TEXT_FIELD_TYPE:
231
+ if (typeof value !== "string") {
232
+ throw new NarsilError(
233
+ ErrorCodes.DOC_VALIDATION_FAILED,
234
+ `Field "${path}" expected string, got ${typeof value}`,
235
+ {
236
+ field: path,
237
+ expected: "string",
238
+ received: typeof value
239
+ }
240
+ );
241
+ }
242
+ return;
243
+ case "number":
244
+ if (typeof value !== "number" || Number.isNaN(value)) {
245
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path}" expected a valid number`, {
246
+ field: path,
247
+ expected: "number",
248
+ received: Number.isNaN(value) ? "NaN" : typeof value
249
+ });
250
+ }
251
+ return;
252
+ case "boolean":
253
+ if (typeof value !== "boolean") {
254
+ throw new NarsilError(
255
+ ErrorCodes.DOC_VALIDATION_FAILED,
256
+ `Field "${path}" expected boolean, got ${typeof value}`,
257
+ {
258
+ field: path,
259
+ expected: "boolean",
260
+ received: typeof value
261
+ }
262
+ );
263
+ }
264
+ return;
265
+ case "enum":
266
+ if (typeof value !== "string") {
267
+ throw new NarsilError(
268
+ ErrorCodes.DOC_VALIDATION_FAILED,
269
+ `Field "${path}" expected enum (string), got ${typeof value}`,
270
+ {
271
+ field: path,
272
+ expected: "enum",
273
+ received: typeof value
274
+ }
275
+ );
276
+ }
277
+ return;
278
+ case "geopoint":
279
+ validateGeopoint(path, value);
280
+ return;
281
+ case "string[]":
282
+ validateTypedArray(path, value, "string");
283
+ return;
284
+ case "number[]":
285
+ validateTypedArray(path, value, "number");
286
+ return;
287
+ case "boolean[]":
288
+ validateTypedArray(path, value, "boolean");
289
+ return;
290
+ case "enum[]":
291
+ validateTypedArray(path, value, "string");
292
+ return;
293
+ default: {
294
+ const vectorMatch = VECTOR_PATTERN.exec(type);
295
+ if (vectorMatch) {
296
+ validateVector(path, value, Number.parseInt(vectorMatch[1], 10));
297
+ }
298
+ }
299
+ }
300
+ }
301
+
302
+ // src/schema/validator/storable.ts
303
+ var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
304
+ function reject(path, reason) {
305
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path === "" ? "(document)" : path}" ${reason}`, {
306
+ field: path
307
+ });
308
+ }
309
+ function isPlainObject2(value) {
310
+ const proto = Object.getPrototypeOf(value);
311
+ return proto === Object.prototype || proto === null;
312
+ }
313
+ function walkStorable(value, path, depth, ancestors, skip) {
314
+ if (value === null || value === void 0) return;
315
+ const kind = typeof value;
316
+ if (kind === "string" || kind === "boolean") return;
317
+ if (kind === "number") return;
318
+ if (kind === "bigint" || kind === "symbol" || kind === "function") {
319
+ reject(path, `holds a ${kind}, which cannot be stored or recovered`);
320
+ }
321
+ const container = value;
322
+ if (container instanceof Date || container instanceof Uint8Array) return;
323
+ const isArray = Array.isArray(container);
324
+ if (!isArray && !isPlainObject2(container)) {
325
+ reject(
326
+ path,
327
+ "holds a value that does not survive storage; use plain objects, arrays, strings, numbers, booleans, dates, or Uint8Array bytes"
328
+ );
329
+ }
330
+ if (depth >= MAX_DOCUMENT_NESTING_DEPTH) {
331
+ reject(path, `nests deeper than ${MAX_DOCUMENT_NESTING_DEPTH} levels`);
332
+ }
333
+ if (ancestors.has(container)) {
334
+ reject(path, "refers back to one of its own ancestors");
335
+ }
336
+ ancestors.add(container);
337
+ if (isArray) {
338
+ const items = container;
339
+ for (let i = 0; i < items.length; i++) {
340
+ walkStorable(items[i], `${path}[${i}]`, depth + 1, ancestors, skip);
341
+ }
342
+ } else {
343
+ const record = container;
344
+ for (const key of Object.keys(record)) {
345
+ const keyPath = path === "" ? key : `${path}.${key}`;
346
+ if (RESERVED_OBJECT_KEYS.has(key)) {
347
+ reject(keyPath, "uses a reserved object key");
348
+ }
349
+ if (skip.has(keyPath)) continue;
350
+ walkStorable(record[key], keyPath, depth + 1, ancestors, skip);
351
+ }
352
+ }
353
+ ancestors.delete(container);
354
+ }
355
+ function assertStorableDocument(document, skipFieldPaths) {
356
+ walkStorable(document, "", 0, /* @__PURE__ */ new Set(), skipFieldPaths ?? /* @__PURE__ */ new Set());
357
+ }
358
+
359
+ // src/schema/validator/document.ts
360
+ var vectorPathCache = /* @__PURE__ */ new WeakMap();
361
+ function vectorFieldPaths(schema) {
362
+ let paths = vectorPathCache.get(schema);
363
+ if (paths !== void 0) return paths;
364
+ paths = /* @__PURE__ */ new Set();
365
+ const collect = (level, prefix) => {
366
+ for (const [field, type] of Object.entries(level)) {
367
+ const path = prefix === "" ? field : `${prefix}.${field}`;
368
+ if (typeof type === "string" && VECTOR_PATTERN.test(type)) {
369
+ paths?.add(path);
370
+ } else if (isPlainObject(type)) {
371
+ collect(type, path);
372
+ }
373
+ }
374
+ };
375
+ collect(schema, "");
376
+ vectorPathCache.set(schema, paths);
377
+ return paths;
378
+ }
379
+ function validateDocumentFields(doc, schema, prefix) {
380
+ for (const [field, type] of Object.entries(schema)) {
381
+ const path = prefix ? `${prefix}.${field}` : field;
382
+ const value = doc[field];
383
+ if (value === void 0 || value === null) continue;
384
+ if (isPlainObject(type)) {
385
+ if (!isPlainObject(value)) {
386
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, `Field "${path}" expected an object`, {
387
+ field: path,
388
+ received: Array.isArray(value) ? "array" : typeof value
389
+ });
390
+ }
391
+ validateDocumentFields(value, type, path);
392
+ continue;
393
+ }
394
+ validateFieldValue(path, value, type);
395
+ }
396
+ }
397
+ function validateDocument(document, schema) {
398
+ if (!isPlainObject(document)) {
399
+ throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, "Document must be a plain object", {
400
+ received: typeof document
401
+ });
402
+ }
403
+ assertStorableDocument(document, vectorFieldPaths(schema));
404
+ validateDocumentFields(document, schema, "");
405
+ }
406
+ function collectExtraFields(doc, schema, prefix, extras) {
407
+ for (const key of Object.keys(doc)) {
408
+ if (key === "id") continue;
409
+ const path = prefix ? `${prefix}.${key}` : key;
410
+ if (!(key in schema)) {
411
+ extras.push(path);
412
+ continue;
413
+ }
414
+ const schemaType = schema[key];
415
+ if (isPlainObject(schemaType) && isPlainObject(doc[key])) {
416
+ collectExtraFields(doc[key], schemaType, path, extras);
417
+ }
418
+ }
419
+ }
420
+ function validateDocumentStrict(document, schema) {
421
+ const extras = [];
422
+ collectExtraFields(document, schema, "", extras);
423
+ if (extras.length > 0) {
424
+ throw new NarsilError(
425
+ ErrorCodes.DOC_VALIDATION_FAILED,
426
+ `Document contains fields not defined in schema: ${extras.join(", ")}`,
427
+ { extraFields: extras }
428
+ );
429
+ }
430
+ }
431
+ function resolveNestedValue(document, path) {
432
+ if (!path.includes(".")) return document[path];
433
+ const segments = path.split(".");
434
+ let current = document;
435
+ for (const segment of segments) {
436
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
437
+ current = current[segment];
438
+ }
439
+ return current;
440
+ }
441
+ function validateRequiredFields(document, required) {
442
+ if (required.length === 0) return;
443
+ for (const field of required) {
444
+ const value = resolveNestedValue(document, field);
445
+ if (value === void 0 || value === null) {
446
+ throw new NarsilError(ErrorCodes.DOC_MISSING_REQUIRED_FIELD, `Document is missing required field "${field}"`, {
447
+ field
448
+ });
449
+ }
450
+ }
451
+ }
452
+
453
+ // src/schema/validator/vector-promotion.ts
454
+ var QUANTIZATION_MODES = /* @__PURE__ */ new Set(["osq8", "osq4", "osq2", "osq1", "none"]);
455
+ var STORAGE_MODES = /* @__PURE__ */ new Set(["memory", "disk"]);
456
+ function fail(message, details) {
457
+ throw new NarsilError(ErrorCodes.CONFIG_INVALID, message, details);
458
+ }
459
+ function isPositiveInteger(value) {
460
+ return Number.isInteger(value) && value >= 1;
461
+ }
462
+ function isQuantizationMode(value) {
463
+ return typeof value === "string" && QUANTIZATION_MODES.has(value);
464
+ }
465
+ function isStorageMode(value) {
466
+ return typeof value === "string" && STORAGE_MODES.has(value);
467
+ }
468
+ function validateVectorStorage(config, filesystemDurability) {
469
+ if (config?.storage === "disk" && !filesystemDurability) {
470
+ fail("vectorPromotion.storage 'disk' needs filesystem durability, because a checkpoint file holds the vectors", {
471
+ storage: config.storage
472
+ });
473
+ }
474
+ }
475
+ function validateVectorPromotion(config) {
476
+ if (config === void 0) return;
477
+ if (config.threshold !== void 0 && !isPositiveInteger(config.threshold)) {
478
+ fail("vectorPromotion.threshold must be a positive integer", { threshold: config.threshold });
479
+ }
480
+ const quantization = config.quantization;
481
+ if (quantization !== void 0 && !isQuantizationMode(quantization)) {
482
+ fail("vectorPromotion.quantization must be 'osq8', 'osq4', 'osq2', 'osq1', or 'none'", { quantization });
483
+ }
484
+ const storage = config.storage;
485
+ if (storage !== void 0 && !isStorageMode(storage)) {
486
+ fail("vectorPromotion.storage must be 'memory' or 'disk'", { storage });
487
+ }
488
+ const hnsw = config.hnswConfig;
489
+ if (hnsw === void 0) return;
490
+ if (hnsw.m !== void 0 && (!isPositiveInteger(hnsw.m) || hnsw.m > MAX_M)) {
491
+ fail(`vectorPromotion.hnswConfig.m must be a positive integer no greater than ${MAX_M}`, { m: hnsw.m });
492
+ }
493
+ if (hnsw.efConstruction !== void 0 && !isPositiveInteger(hnsw.efConstruction)) {
494
+ fail("vectorPromotion.hnswConfig.efConstruction must be a positive integer", {
495
+ efConstruction: hnsw.efConstruction
496
+ });
497
+ }
498
+ const metric = hnsw.metric;
499
+ if (metric !== void 0 && metric !== "cosine" && metric !== "dotProduct" && metric !== "euclidean") {
500
+ fail("vectorPromotion.hnswConfig.metric must be 'cosine', 'dotProduct', or 'euclidean'", { metric });
501
+ }
502
+ }
503
+
504
+ // src/search/constants.ts
505
+ var RESULT_WINDOW = 1e4;
506
+ var DEFAULT_PAGE_SIZE = 10;
507
+ var MAX_CURSOR_LENGTH = 40960;
508
+ var MAX_SORT_FIELDS = 8;
509
+ var MAX_SORT_FIELD_NAME_LENGTH = 255;
510
+ var MAX_CURSOR_ANCHOR_CODE_POINTS = 512;
511
+ var PREFIX_MAX_EXPANSIONS = 50;
512
+
513
+ export { DEFAULT_PAGE_SIZE, MAX_CURSOR_ANCHOR_CODE_POINTS, MAX_CURSOR_LENGTH, MAX_NESTING_DEPTH, MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, PREFIX_MAX_EXPANSIONS, PROTOTYPE_POLLUTION_KEYS, RESULT_WINDOW, SORTABLE_TEXT_FIELD_TYPE, VECTOR_PATTERN, extractVectorFieldsFromSchema, flattenSchema, isQuantizationMode, isStorageMode, isTextFieldType, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion, validateVectorStorage };
@@ -1,6 +1,6 @@
1
- import { P as PartitionConfig, A as AnyDocument, b as InsertOptions, W as WriteOptions } from './schema-BqRx9t4E.js';
2
- import { V as VectorMaintenanceResult, M as MemoryStats, B as BatchResult, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, P as PartitionStatsResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult } from './memory-Bzd5xcEW.js';
3
- import { e as TaskRecord, b as ImportResult, H as HttpIndexConfig, c as TaskListQuery, T as TaskListPage } from './tasks-Bn4R1NPN.js';
1
+ import { P as PartitionConfig, A as AnyDocument, a as InsertOptions, W as WriteOptions } from './schema-OSWaaZzx.js';
2
+ import { V as VectorMaintenanceResult, B as BatchResult, L as ListParams, g as ListResult, I as IndexInfo, f as IndexStats, P as PartitionStatsResult, Q as QueryParams, e as QueryResult, h as PreflightResult, i as SuggestParams, j as SuggestResult } from './memory-OmgkX_Z2.js';
3
+ import { e as TaskRecord, M as MemoryStatsResponse, b as ImportResult, H as HttpIndexConfig, c as TaskListQuery, T as TaskListPage } from './tasks-DiF1c9v2.js';
4
4
 
5
5
  /**
6
6
  * This is the `fetch` implementation the client sends every request through.
@@ -197,13 +197,13 @@ interface AdminOperations {
197
197
  rebuildAnalysis(indexName: string, options?: RequestOptions): Promise<TaskRecord>;
198
198
  /**
199
199
  * Reports the server process's heap usage, its estimate of what the indexes
200
- * hold, and each worker's heap.
200
+ * hold, each worker's heap, and how many worker threads receive requests.
201
201
  *
202
202
  * @param options - This sets the signal, the deadline, and the headers for
203
203
  * this request.
204
204
  * @returns The figures are what you size a host from.
205
205
  */
206
- getMemoryStats(options?: RequestOptions): Promise<MemoryStats>;
206
+ getMemoryStats(options?: RequestOptions): Promise<MemoryStatsResponse>;
207
207
  }
208
208
 
209
209
  /**