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

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 (59) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/filesystem.mjs +1 -1
  3. package/dist/adapters/indexeddb.mjs +1 -1
  4. package/dist/{capabilities-D6gjjQyA.d.ts → capabilities-CYeAWYxQ.d.ts} +19 -4
  5. package/dist/{chunk-Y52WZWAU.mjs → chunk-2WLD2NFW.mjs} +4 -4
  6. package/dist/{chunk-QR4OKRVY.mjs → chunk-3YKWSO54.mjs} +8 -3
  7. package/dist/{chunk-KZTI25D3.mjs → chunk-4MQ5F4IL.mjs} +1 -1
  8. package/dist/{chunk-CVPA5Q7R.mjs → chunk-AFKH4ZK3.mjs} +9 -6
  9. package/dist/{chunk-M6XF5HDE.mjs → chunk-AK3BLGCI.mjs} +141 -11
  10. package/dist/{chunk-7QMTA7AA.mjs → chunk-CVFAFZSN.mjs} +7771 -4271
  11. package/dist/{chunk-RV77FR2F.mjs → chunk-I64LXKO3.mjs} +0 -1
  12. package/dist/{chunk-EDMCZLSJ.mjs → chunk-LPBWCHBE.mjs} +193 -13
  13. package/dist/{chunk-P2ZZN32C.mjs → chunk-PU43WDY4.mjs} +4 -2
  14. package/dist/{chunk-HVJYTKK7.mjs → chunk-THMSFOJO.mjs} +365 -774
  15. package/dist/{chunk-LCCL6EEH.mjs → chunk-WVDFG45M.mjs} +92 -94
  16. package/dist/{chunk-3HQPUCCT.mjs → chunk-XW25X6AU.mjs} +66 -237
  17. package/dist/{chunk-2X2ZQOAB.mjs → chunk-ZULSEZCE.mjs} +3 -4
  18. package/dist/{client-C2TiW29y.d.ts → client-D698E5ge.d.ts} +76 -25
  19. package/dist/client.d.ts +7 -7
  20. package/dist/client.mjs +44 -22
  21. package/dist/{config-BLguB-om.d.ts → config-Bv8etwUZ.d.ts} +10 -3
  22. package/dist/{config-C7Ip8Ife.d.ts → config-QXU3HMpq.d.ts} +10 -1
  23. package/dist/direct-executor-5SRDFZAS.mjs +13 -0
  24. package/dist/distribution/coordinator/etcd.d.ts +27 -14
  25. package/dist/distribution/coordinator/etcd.mjs +162 -75
  26. package/dist/distribution/transport/grpc.d.ts +2 -2
  27. package/dist/distribution/transport/grpc.mjs +22 -2
  28. package/dist/distribution/transport/in-memory.d.ts +2 -2
  29. package/dist/distribution/transport/in-memory.mjs +6 -3
  30. package/dist/distribution/transport/tcp.d.ts +2 -2
  31. package/dist/distribution/transport/tcp.mjs +40 -16
  32. package/dist/distribution.d.ts +6 -6
  33. package/dist/distribution.mjs +637 -366
  34. package/dist/embeddings/openai.mjs +1 -1
  35. package/dist/{engine-Dm4446j1.d.ts → engine-D_OzKZ-6.d.ts} +96 -187
  36. package/dist/index.browser.mjs +7179 -4031
  37. package/dist/index.d.ts +5 -5
  38. package/dist/index.mjs +11 -14
  39. package/dist/{memory-CKHTU1CL.d.ts → memory-B4jMmygw.d.ts} +213 -2
  40. package/dist/pattern.mjs +1 -1
  41. package/dist/persistence/durability/checkpoint-worker.d.ts +24 -16
  42. package/dist/persistence/durability/checkpoint-worker.mjs +22 -16
  43. package/dist/react.d.ts +6 -6
  44. package/dist/react.mjs +0 -1
  45. package/dist/server.d.ts +9 -9
  46. package/dist/server.mjs +210 -32
  47. package/dist/{tasks-BECd0b__.d.ts → tasks-DpgCpW1x.d.ts} +3 -3
  48. package/dist/{thread-IBSIZZ6W.mjs → thread-3LNVA43V.mjs} +79 -21
  49. package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
  50. package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
  51. package/dist/vector/search-worker.mjs +3 -3
  52. package/dist/workers/entry.mjs +4 -4
  53. package/package.json +7 -7
  54. package/dist/chunk-3L4U6EJQ.mjs +0 -49
  55. package/dist/chunk-IFBKLBGB.mjs +0 -7
  56. package/dist/chunk-XZ4PAREY.mjs +0 -25
  57. package/dist/direct-executor-SVCHXR74.mjs +0 -14
  58. package/dist/serialization/crc32-worker.d.ts +0 -20
  59. package/dist/serialization/crc32-worker.mjs +0 -62
package/README.md CHANGED
@@ -106,7 +106,7 @@ Every hit holds the document, its id, and its BM25 score. Beside the hits, `resu
106
106
 
107
107
  **Geosearch.** [Geo filters](https://github.com/assetcorp/narsil/blob/main/docs/geosearch.md) match documents by radius, using Haversine or Vincenty distance, or by polygon containment. You can combine a geo filter with every other query feature.
108
108
 
109
- **Storage.** [Persistence adapters](https://github.com/assetcorp/narsil/blob/main/docs/persistence-and-durability.md) store indexes on the filesystem, in IndexedDB, in memory, or in a backend of your own. With durability on, the engine keeps a write-ahead log with periodic checkpoints and recovers on its own after a crash. A snapshot captures a whole index as one portable byte array.
109
+ **Storage.** [Persistence adapters](https://github.com/assetcorp/narsil/blob/main/docs/persistence-and-durability.md) store indexes on the filesystem, in IndexedDB, in memory, or in a backend of your own. With durability on, the engine keeps a write-ahead log with periodic checkpoints and recovers on its own after a crash. You can stream a whole index into a portable snapshot archive, or export its documents as JSON Lines to move the index across releases.
110
110
 
111
111
  **Scale.** The engine routes each document to a [partition](https://github.com/assetcorp/narsil/blob/main/docs/partitions-and-workers.md) by a deterministic hash. When you call `rebalance()`, the engine reshapes the partitions online and buffers incoming writes in a write-ahead queue. Once an index holds 1,000 documents, the engine serves keyword queries from worker copies on half its worker threads, while the HTTP server receives requests on the threads that hold those copies.
112
112
 
@@ -124,7 +124,7 @@ Every hit holds the document, its id, and its BM25 score. Beside the hits, `resu
124
124
  | [Hybrid search](https://github.com/assetcorp/narsil/blob/main/docs/hybrid-search.md) | Reciprocal rank fusion and linear blending of text and vector rankings |
125
125
  | [Geosearch](https://github.com/assetcorp/narsil/blob/main/docs/geosearch.md) | Radius and polygon filters, and the two distance formulas |
126
126
  | [Embedding adapters](https://github.com/assetcorp/narsil/blob/main/docs/embedding-adapters.md) | Automatic embedding on insert and query, named adapters, the bundled ones, and custom ones |
127
- | [Persistence and durability](https://github.com/assetcorp/narsil/blob/main/docs/persistence-and-durability.md) | Storage backends, the write-ahead log, checkpoints, recovery, and snapshots |
127
+ | [Persistence and durability](https://github.com/assetcorp/narsil/blob/main/docs/persistence-and-durability.md) | Storage backends, the write-ahead log, checkpoints, recovery, snapshots, document exports, and indexes from an earlier release |
128
128
  | [Partitions and workers](https://github.com/assetcorp/narsil/blob/main/docs/partitions-and-workers.md) | Partition routing, online rebalancing, worker copies, and multi-instance invalidation |
129
129
  | [Language support](https://github.com/assetcorp/narsil/blob/main/docs/language-support.md) | The 107 language modules, analysis revisions and rebuilds, and named tokenizers and stop words |
130
130
  | [HTTP server](https://github.com/assetcorp/narsil/blob/main/docs/http-server.md) | Wrapping an engine in a REST API, every route it serves, and long-running tasks |
@@ -1,4 +1,4 @@
1
- import { NarsilError, ErrorCodes } from '../chunk-RV77FR2F.mjs';
1
+ import { NarsilError, ErrorCodes } from '../chunk-I64LXKO3.mjs';
2
2
 
3
3
  // src/persistence/filesystem.ts
4
4
  var cachedFs = null;
@@ -1,4 +1,4 @@
1
- import { NarsilError, ErrorCodes } from '../chunk-RV77FR2F.mjs';
1
+ import { NarsilError, ErrorCodes } from '../chunk-I64LXKO3.mjs';
2
2
 
3
3
  // src/persistence/constants.ts
4
4
  var DEFAULT_INDEXEDDB_NAME = "narsil";
@@ -1,6 +1,6 @@
1
1
  import { E as EmbeddingAdapter } from './adapters-79OIa4xm.js';
2
- import { C as ClusterNamespace } from './config-BLguB-om.js';
3
- import { h as TaskStore } from './tasks-BECd0b__.js';
2
+ import { C as ClusterNamespace } from './config-Bv8etwUZ.js';
3
+ import { h as TaskStore } from './tasks-DpgCpW1x.js';
4
4
 
5
5
  /**
6
6
  * Context handed to {@link OnRequestHook} for every inbound request, captured
@@ -70,8 +70,15 @@ interface CorsOptions {
70
70
  interface ServerLimits {
71
71
  /** Cap for JSON request bodies (single doc, batch, search). Excess → 413. */
72
72
  maxBodyBytes?: number;
73
- /** Cap for the NDJSON import stream and binary restore body. Excess → 413. */
73
+ /** Cap for the NDJSON import stream. Excess → 413. */
74
74
  maxImportBytes?: number;
75
+ /** The server accepts a snapshot archive for `POST /indexes/{name}/restore`
76
+ * and a document export for `POST /indexes/{name}/import` of up to this many
77
+ * bytes, and it answers a larger upload with 413. The server writes each
78
+ * upload to a temporary file as the upload arrives, so it needs the same
79
+ * memory for an upload of any size. The default is 64 GiB, and 0 accepts an
80
+ * upload of any size. */
81
+ maxRestoreBytes?: number;
75
82
  /** Cap for a single NDJSON line, so one unterminated line cannot exhaust memory. Excess → 413. */
76
83
  maxLineBytes?: number;
77
84
  /** Documents handed to the engine per batch during NDJSON import; the loop yields between batches. */
@@ -255,6 +262,14 @@ declare const REBUILD_ANALYSIS_CAPABILITY = "indexes.rebuildAnalysis";
255
262
  * @public
256
263
  */
257
264
  declare const INDEX_LIFECYCLE_CAPABILITY = "indexes.lifecycle";
265
+ /**
266
+ * Marks a server that answers `GET /indexes/{name}/export` with a stream of a
267
+ * document export and imports an export sent to `POST /indexes/{name}/import`
268
+ * in a task.
269
+ *
270
+ * @public
271
+ */
272
+ declare const DOCUMENT_EXPORT_CAPABILITY = "indexes.documentExport";
258
273
  /**
259
274
  * Every capability this server announces at `/capabilities`.
260
275
  *
@@ -266,4 +281,4 @@ declare const INDEX_LIFECYCLE_CAPABILITY = "indexes.lifecycle";
266
281
  */
267
282
  declare const SERVER_CAPABILITIES: readonly string[];
268
283
 
269
- export { ASYNC_IMPORT_CAPABILITY as A, type CorsOptions as C, type ErrorEnvelope as E, INDEX_LIFECYCLE_CAPABILITY as I, type NarsilServer as N, type OnRequestHook as O, REBUILD_ANALYSIS_CAPABILITY as R, SERVER_CAPABILITIES as S, TASK_CANCEL_CAPABILITY as T, TASK_FILTER_CAPABILITY as a, type ServerOptions as b, type RequestContext as c, type RequestDenial as d, type ServerLimits as e };
284
+ export { ASYNC_IMPORT_CAPABILITY as A, type CorsOptions as C, DOCUMENT_EXPORT_CAPABILITY as D, type ErrorEnvelope as E, INDEX_LIFECYCLE_CAPABILITY as I, type NarsilServer as N, type OnRequestHook as O, REBUILD_ANALYSIS_CAPABILITY as R, SERVER_CAPABILITIES as S, TASK_CANCEL_CAPABILITY as T, TASK_FILTER_CAPABILITY as a, type ServerOptions as b, type RequestContext as c, type RequestDenial as d, type ServerLimits as e };
@@ -1,8 +1,8 @@
1
- import { readSharedConfigFields, restoredEmbedding, restoredPartitionConfig, MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR } from './chunk-CVPA5Q7R.mjs';
2
- import { MAX_INDEX_NAME_LENGTH, INDEX_NAME_PATTERN } from './chunk-XZ4PAREY.mjs';
3
- import { getLanguage } from './chunk-KZTI25D3.mjs';
1
+ import { readSharedConfigFields, restoredEmbedding, restoredPartitionConfig, MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR } from './chunk-AFKH4ZK3.mjs';
2
+ import { getLanguage } from './chunk-4MQ5F4IL.mjs';
3
+ import { MAX_INDEX_NAME_LENGTH, INDEX_NAME_PATTERN } from './chunk-AK3BLGCI.mjs';
4
4
  import { compareCodePoints } from './chunk-LJYTW7GT.mjs';
5
- import { NarsilError, ErrorCodes } from './chunk-RV77FR2F.mjs';
5
+ import { NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
6
6
  import { encode, decode } from '@msgpack/msgpack';
7
7
 
8
8
  // src/distribution/cluster/index-settings.ts
@@ -1,7 +1,7 @@
1
- import { isWordIndexedBase, flattenSchema, MAX_LATITUDE_DEGREES, MAX_LONGITUDE_DEGREES, MIN_POLYGON_POINTS, parsedTypeOf, isStringValuedBase, withSortableOption, isGeopointOrVectorType, MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, MAX_CURSOR_LENGTH, MAX_CURSOR_ANCHOR_CODE_POINTS, RESULT_WINDOW, DEFAULT_FUZZY_PREFIX_LENGTH, EARTH_RADIUS_METERS, RADIUS_BOX_MARGIN_RATIO, RADIUS_BOX_MARGIN_RADIANS, FULL_TURN_DEGREES, ANTIMERIDIAN_SPAN_DEGREES, DEFAULT_PAGE_SIZE, PROTOTYPE_POLLUTION_KEYS, DEG_TO_RAD, LOCATION_CODE_BITS_PER_AXIS, isLatitude, isLongitude, isWordIndexedFieldType, PREFIX_MAX_EXPANSIONS, MAX_COVERING_CELLS } from './chunk-M6XF5HDE.mjs';
1
+ import { isWordIndexedBase, flattenSchema, MAX_LATITUDE_DEGREES, MAX_LONGITUDE_DEGREES, MIN_POLYGON_POINTS, parsedTypeOf, isStringValuedBase, withSortableOption, isGeopointOrVectorType, MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, MAX_CURSOR_LENGTH, MAX_CURSOR_ANCHOR_CODE_POINTS, RESULT_WINDOW, DEFAULT_FUZZY_PREFIX_LENGTH, EARTH_RADIUS_METERS, RADIUS_BOX_MARGIN_RATIO, RADIUS_BOX_MARGIN_RADIANS, FULL_TURN_DEGREES, ANTIMERIDIAN_SPAN_DEGREES, DEFAULT_PAGE_SIZE, PROTOTYPE_POLLUTION_KEYS, DEG_TO_RAD, LOCATION_CODE_BITS_PER_AXIS, isLatitude, isLongitude, isWordIndexedFieldType, PREFIX_MAX_EXPANSIONS, MAX_COVERING_CELLS } from './chunk-AK3BLGCI.mjs';
2
2
  import { DEFAULT_PATTERN_WORK_CAP, PATTERN_TEST_OPERATORS, bitsetIsEmpty, MAX_PATTERN_TEXT_CODE_POINTS, MAX_PATTERN_POSITIONS, createBitSet, bitsetSet, PATTERN_WORK_FLUSH_UNITS, MATCHER_STATE_BUDGET_BYTES } from './chunk-ZKRV5M2P.mjs';
3
3
  import { compareCodePoints, sortModeOf, defaultSortMode, SORT_VALUE_MAX_CODE_POINTS, toReducedSortValue, readSortField, compareComparableKeys, toComparableSortValue, exceedsCodePoints, holdsUnpairedSurrogate, isSortMode, SORT_MODES, compareSortValues, foldedCodePointCount } from './chunk-LJYTW7GT.mjs';
4
- import { createNarsilError, ErrorCodes, NarsilError } from './chunk-RV77FR2F.mjs';
4
+ import { createNarsilError, ErrorCodes, NarsilError } from './chunk-I64LXKO3.mjs';
5
5
 
6
6
  // src/core/tokenizer/constants.ts
7
7
  var TOKEN_CACHE_SIZE_FLOOR = 5e4;
@@ -777,6 +777,7 @@ function getOrCreateFieldNameIndex(table, fieldName) {
777
777
  table.indexMap.set(fieldName, idx);
778
778
  return idx;
779
779
  }
780
+ var VECTOR_PATTERN = /^vector\[(\d+)]$/;
780
781
  function getNestedValue(obj, path) {
781
782
  const parts = path.split(".");
782
783
  let current = obj;
@@ -809,6 +810,10 @@ function textFieldsChanged(oldDoc, newDoc, fields) {
809
810
  }
810
811
  return false;
811
812
  }
813
+ function parseVectorDimension(fieldType) {
814
+ const match = VECTOR_PATTERN.exec(fieldType);
815
+ return match ? Number.parseInt(match[1], 10) : null;
816
+ }
812
817
  function tokenizeOptions(options) {
813
818
  return {
814
819
  stem: true,
@@ -2573,4 +2578,4 @@ function setNestedValue(obj, path, value) {
2573
2578
  current[segments[segments.length - 1]] = value;
2574
2579
  }
2575
2580
 
2576
- export { GEO_UNIT_METRES, MAX_FILTER_ARRAY_SIZE, MAX_FILTER_DEPTH, MAX_FILTER_TESTS, applyPagination, applyProjection, applySorting, boundedLevenshtein, clampRowCount, clearNormalizationCache, cloneProjected, collectQueryTermStats, computeFacets, configureNormalizationCache, countsWithoutScores, coveringRanges, createPatternWorkMeter, decodePageCursor, encodePageCursor, everyValueFacetConfig, fanOutMatchCount, fanOutQuery, filterTestCount, fnv1a, fnv1aBytes, fnv1aUint32s, fulltextMatches, fuzzyPrefixOf, fuzzyTermMatches, getFieldValueByInternalId, getFlatSchema, getNestedValue, getNormalizationCacheSize, getOrCreateFieldNameIndex, joinPatternWorkMeter, kWayMerge, locationCode, mergeFacets, mergePartitionStats, normalizeSort, oversampledFacetConfig, oversampledShardSize, partitionsIn, polygonBoxes, polygonContainment, projectionKeepsField, pruneStatsToQueryTerms, radiusBoxes, readSortValues, requireMatchingCursor, requireSortableFields, requireValidFilter, requireValidOperands, requireWithinResultWindow, resetNormalizationCache, resolveProjection, sanitizeGlobalStats, setNestedValue, sortModesOf, sortSignatureEntry, sortSignatureOf, textFieldsChanged, tokenize, tokenizeIterator, tokenizeOptions, undeclaredFieldOnStrictIndex, undeclaredFilterField, visitTokens };
2581
+ export { GEO_UNIT_METRES, MAX_FILTER_ARRAY_SIZE, MAX_FILTER_DEPTH, MAX_FILTER_TESTS, applyPagination, applyProjection, applySorting, boundedLevenshtein, clampRowCount, clearNormalizationCache, cloneProjected, collectQueryTermStats, computeFacets, configureNormalizationCache, countsWithoutScores, coveringRanges, createPatternWorkMeter, decodePageCursor, encodePageCursor, everyValueFacetConfig, fanOutMatchCount, fanOutQuery, filterTestCount, fnv1a, fnv1aBytes, fnv1aUint32s, fulltextMatches, fuzzyPrefixOf, fuzzyTermMatches, getFieldValueByInternalId, getFlatSchema, getNestedValue, getNormalizationCacheSize, getOrCreateFieldNameIndex, joinPatternWorkMeter, kWayMerge, locationCode, mergeFacets, mergePartitionStats, normalizeSort, oversampledFacetConfig, oversampledShardSize, parseVectorDimension, partitionsIn, polygonBoxes, polygonContainment, projectionKeepsField, pruneStatsToQueryTerms, radiusBoxes, readSortValues, requireMatchingCursor, requireSortableFields, requireValidFilter, requireValidOperands, requireWithinResultWindow, resetNormalizationCache, resolveProjection, sanitizeGlobalStats, setNestedValue, sortModesOf, sortSignatureEntry, sortSignatureOf, textFieldsChanged, tokenize, tokenizeIterator, tokenizeOptions, undeclaredFieldOnStrictIndex, undeclaredFilterField, visitTokens };
@@ -1,5 +1,5 @@
1
1
  import { english } from './chunk-TGP5M5R7.mjs';
2
- import { NarsilError, ErrorCodes } from './chunk-RV77FR2F.mjs';
2
+ import { NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
3
3
 
4
4
  // src/languages/registry.ts
5
5
  var languages = /* @__PURE__ */ new Map();
@@ -1,5 +1,5 @@
1
- import { validateRequiredFields } from './chunk-M6XF5HDE.mjs';
2
- import { ErrorCodes, NarsilError } from './chunk-RV77FR2F.mjs';
1
+ import { requireDocumentWithinSizeLimit, validateRequiredFields } from './chunk-AK3BLGCI.mjs';
2
+ import { ErrorCodes, NarsilError } from './chunk-I64LXKO3.mjs';
3
3
 
4
4
  // src/narsil/internals.ts
5
5
  var engineCores = /* @__PURE__ */ new WeakMap();
@@ -244,7 +244,10 @@ function providedDocId(document) {
244
244
  return typeof id === "string" && id.length > 0 ? id : void 0;
245
245
  }
246
246
  function requireDocumentObject(document) {
247
- if (document !== null && typeof document === "object" && !Array.isArray(document)) return;
247
+ if (document !== null && typeof document === "object" && !Array.isArray(document)) {
248
+ requireDocumentWithinSizeLimit(document);
249
+ return;
250
+ }
248
251
  const received = Array.isArray(document) ? "a list" : document === null ? "null" : `a ${typeof document}`;
249
252
  throw new NarsilError(
250
253
  ErrorCodes.DOC_VALIDATION_FAILED,
@@ -578,9 +581,9 @@ function readSharedConfigFields(source, reject) {
578
581
  ...typeof source.patternValueLimit === "number" ? { patternValueLimit: source.patternValueLimit } : {}
579
582
  };
580
583
  }
581
- function restoredConfigFields(envelope) {
582
- const shared = readSharedConfigFields(envelope, rejectSnapshot);
583
- const partitions = restoredPartitionConfig(envelope.partitionConfig);
584
+ function restoredConfigFields(envelope, reject = rejectSnapshot) {
585
+ const shared = readSharedConfigFields(envelope, reject);
586
+ const partitions = restoredPartitionConfig(envelope.partitionConfig, reject);
584
587
  return {
585
588
  ...shared,
586
589
  ...partitions !== void 0 ? { partitions } : {}
@@ -1,6 +1,7 @@
1
- import { MAX_M } from './chunk-P2ZZN32C.mjs';
1
+ import { EARLIER_RELEASE_MIN_M, MIN_M, MAX_M } from './chunk-PU43WDY4.mjs';
2
2
  import { holdsUnpairedSurrogate, exceedsCodePoints } from './chunk-LJYTW7GT.mjs';
3
- import { NarsilError, ErrorCodes } from './chunk-RV77FR2F.mjs';
3
+ import { NarsilError, ErrorCodes } from './chunk-I64LXKO3.mjs';
4
+ import { encode } from '@msgpack/msgpack';
4
5
 
5
6
  // src/schema/constants.ts
6
7
  var MAX_NESTING_DEPTH = 4;
@@ -8,6 +9,10 @@ var MAX_DOCUMENT_NESTING_DEPTH = 32;
8
9
  var DEFAULT_PATTERN_VALUE_LIMIT = 8192;
9
10
  var MIN_PATTERN_VALUE_LIMIT = 1;
10
11
  var MAX_PATTERN_VALUE_LIMIT = 65536;
12
+ var MAX_VECTOR_DIMENSION = 4096;
13
+ var MAX_STORED_VECTOR_DIMENSION = 65535;
14
+ var MAX_TEXT_FIELDS = 65536;
15
+ var MAX_DOCUMENT_BYTES = 16777216;
11
16
 
12
17
  // src/schema/validator/shared.ts
13
18
  var VECTOR_PATTERN = /^vector\[(\d+)]$/;
@@ -57,11 +62,11 @@ function parseBase(baseName, path, type) {
57
62
  invalidType(path, type, `Field "${path}" has an unsupported type: "${type}"`);
58
63
  }
59
64
  const dimension = Number.parseInt(vectorMatch[1], 10);
60
- if (dimension <= 0) {
65
+ if (dimension <= 0 || dimension > MAX_STORED_VECTOR_DIMENSION) {
61
66
  throw new NarsilError(
62
67
  ErrorCodes.SCHEMA_INVALID_VECTOR_DIMENSION,
63
- `Vector field "${path}" must have a positive dimension, got ${dimension}`,
64
- { field: path, dimension }
68
+ `Vector field "${path}" has ${dimension} dimensions, but a vector field holds from 1 to ${MAX_STORED_VECTOR_DIMENSION}`,
69
+ { field: path, dimension, maximum: MAX_STORED_VECTOR_DIMENSION }
65
70
  );
66
71
  }
67
72
  return { base: "vector", dimension };
@@ -194,9 +199,30 @@ function validateSchema(schema) {
194
199
  if (Object.keys(schema).length === 0) {
195
200
  throw new NarsilError(ErrorCodes.SCHEMA_INVALID_TYPE, "Schema must define at least one field");
196
201
  }
197
- return validateSchemaFields(schema, 1, "");
202
+ const validated = validateSchemaFields(schema, 1, "");
203
+ requireTextFieldCount(schemaFieldsOf(validated).filter((field) => isWordIndexedBase(field.base)).length);
204
+ return validated;
205
+ }
206
+ function requireTextFieldCount(count) {
207
+ if (count <= MAX_TEXT_FIELDS) return;
208
+ throw new NarsilError(
209
+ ErrorCodes.SCHEMA_INVALID_TYPE,
210
+ `The schema declares ${count} string and string[] fields, but a schema holds at most ${MAX_TEXT_FIELDS}`,
211
+ { textFields: count, maximum: MAX_TEXT_FIELDS }
212
+ );
213
+ }
214
+ function requireCreatableSchema(schema) {
215
+ for (const field of schemaFieldsOf(schema)) {
216
+ if (field.base !== "vector" || field.dimension <= MAX_VECTOR_DIMENSION) continue;
217
+ throw new NarsilError(
218
+ ErrorCodes.SCHEMA_INVALID_VECTOR_DIMENSION,
219
+ `Vector field "${field.path}" has ${field.dimension} dimensions, but a new index takes at most ${MAX_VECTOR_DIMENSION}`,
220
+ { field: field.path, dimension: field.dimension, maximum: MAX_VECTOR_DIMENSION }
221
+ );
222
+ }
198
223
  }
199
224
  function requireValidFieldTypes(flatSchema) {
225
+ let textFields = 0;
200
226
  for (const [path, type] of Object.entries(flatSchema)) {
201
227
  if (typeof type !== "string") {
202
228
  throw new NarsilError(
@@ -205,8 +231,9 @@ function requireValidFieldTypes(flatSchema) {
205
231
  { field: path, type: String(type) }
206
232
  );
207
233
  }
208
- parseFieldType(type, path);
234
+ if (isWordIndexedBase(parseFieldType(type, path).base)) textFields += 1;
209
235
  }
236
+ requireTextFieldCount(textFields);
210
237
  }
211
238
  function flattenRecursive(schema, prefix, result) {
212
239
  for (const [field, type] of Object.entries(schema)) {
@@ -474,7 +501,11 @@ function walkStorable(value, path, depth, ancestors, skip) {
474
501
  reject(path, `holds a ${kind}, which cannot be stored or recovered`);
475
502
  }
476
503
  const container = value;
477
- if (container instanceof Date || container instanceof Uint8Array) return;
504
+ if (container instanceof Date) {
505
+ if (Number.isNaN(container.getTime())) reject(path, "holds an invalid date, which the engine cannot store");
506
+ return;
507
+ }
508
+ if (container instanceof Uint8Array) return;
478
509
  const isArray = Array.isArray(container);
479
510
  if (!isArray && !isPlainObject2(container)) {
480
511
  reject(
@@ -628,6 +659,71 @@ function validateRequiredFields(document, required) {
628
659
  }
629
660
  }
630
661
  }
662
+ function holdsNegativeZero(value) {
663
+ const pending = [value];
664
+ while (pending.length > 0) {
665
+ const next = pending.pop();
666
+ if (typeof next === "number") {
667
+ if (Object.is(next, -0)) return true;
668
+ continue;
669
+ }
670
+ if (next === null || typeof next !== "object" || ArrayBuffer.isView(next)) continue;
671
+ for (const child of Array.isArray(next) ? next : Object.values(next)) pending.push(child);
672
+ }
673
+ return false;
674
+ }
675
+ function encodeDocument(document) {
676
+ return encode(document, holdsNegativeZero(document) ? { forceIntegerToFloat: true } : void 0);
677
+ }
678
+
679
+ // src/schema/validator/document-size.ts
680
+ var LARGEST_HEADER_BYTES = 5;
681
+ var LARGEST_NUMBER_BYTES = 9;
682
+ var LARGEST_TIMESTAMP_BYTES = 15;
683
+ var LARGEST_UTF8_BYTES_PER_UNIT = 3;
684
+ function upperBoundBytes(value, budget, depth) {
685
+ if (value === null || value === void 0 || typeof value === "boolean") return 1;
686
+ if (typeof value === "number") return LARGEST_NUMBER_BYTES;
687
+ if (typeof value === "string") return LARGEST_HEADER_BYTES + value.length * LARGEST_UTF8_BYTES_PER_UNIT;
688
+ if (value instanceof Date) return LARGEST_TIMESTAMP_BYTES;
689
+ if (ArrayBuffer.isView(value)) return LARGEST_HEADER_BYTES + value.byteLength;
690
+ if (typeof value !== "object" || depth > MAX_DOCUMENT_NESTING_DEPTH) return LARGEST_NUMBER_BYTES;
691
+ let total = LARGEST_HEADER_BYTES;
692
+ if (Array.isArray(value)) {
693
+ for (const item of value) {
694
+ total += upperBoundBytes(item, budget - total, depth + 1);
695
+ if (total > budget) return total;
696
+ }
697
+ return total;
698
+ }
699
+ const record = value;
700
+ for (const key of Object.keys(record)) {
701
+ total += LARGEST_HEADER_BYTES + key.length * LARGEST_UTF8_BYTES_PER_UNIT;
702
+ total += upperBoundBytes(record[key], budget - total, depth + 1);
703
+ if (total > budget) return total;
704
+ }
705
+ return total;
706
+ }
707
+ function messagePackUpperBoundBytes(value) {
708
+ return upperBoundBytes(value, Number.POSITIVE_INFINITY, 0);
709
+ }
710
+ function encodedBytesOf(document) {
711
+ try {
712
+ return encodeDocument(document).byteLength;
713
+ } catch {
714
+ return null;
715
+ }
716
+ }
717
+ function requireDocumentWithinSizeLimit(document) {
718
+ if (upperBoundBytes(document, MAX_DOCUMENT_BYTES, 0) <= MAX_DOCUMENT_BYTES) return;
719
+ const encodedBytes = encodedBytesOf(document);
720
+ if (encodedBytes === null || encodedBytes <= MAX_DOCUMENT_BYTES) return;
721
+ throw new NarsilError(
722
+ ErrorCodes.DOC_VALIDATION_FAILED,
723
+ `The document takes ${encodedBytes} bytes as MessagePack, vector values included, but a document may take at most ${MAX_DOCUMENT_BYTES}`,
724
+ { bytes: encodedBytes, maximum: MAX_DOCUMENT_BYTES }
725
+ );
726
+ }
631
727
 
632
728
  // src/schema/validator/vector-promotion.ts
633
729
  var QUANTIZATION_MODES = /* @__PURE__ */ new Set(["osq8", "osq4", "osq2", "osq1", "none"]);
@@ -651,6 +747,13 @@ function validateVectorStorage(config, filesystemDurability) {
651
747
  });
652
748
  }
653
749
  }
750
+ function raiseEarlierReleaseGraphSetting(config) {
751
+ if (typeof config !== "object" || config === null) return config;
752
+ const hnsw = config.hnswConfig;
753
+ if (typeof hnsw !== "object" || hnsw === null) return config;
754
+ if (hnsw.m !== EARLIER_RELEASE_MIN_M) return config;
755
+ return { ...config, hnswConfig: { ...hnsw, m: MIN_M } };
756
+ }
654
757
  function validateVectorPromotion(config) {
655
758
  if (config === void 0) return;
656
759
  if (config.threshold !== void 0 && !isPositiveInteger(config.threshold)) {
@@ -666,8 +769,8 @@ function validateVectorPromotion(config) {
666
769
  }
667
770
  const hnsw = config.hnswConfig;
668
771
  if (hnsw === void 0) return;
669
- if (hnsw.m !== void 0 && (!isPositiveInteger(hnsw.m) || hnsw.m > MAX_M)) {
670
- fail(`vectorPromotion.hnswConfig.m must be a positive integer no greater than ${MAX_M}`, { m: hnsw.m });
772
+ if (hnsw.m !== void 0 && (!Number.isInteger(hnsw.m) || hnsw.m < MIN_M || hnsw.m > MAX_M)) {
773
+ fail(`vectorPromotion.hnswConfig.m must be a whole number from ${MIN_M} to ${MAX_M}`, { m: hnsw.m });
671
774
  }
672
775
  if (hnsw.efConstruction !== void 0 && !isPositiveInteger(hnsw.efConstruction)) {
673
776
  fail("vectorPromotion.hnswConfig.efConstruction must be a positive integer", {
@@ -698,4 +801,31 @@ var DEFAULT_LINEAR_ALPHA = 0.5;
698
801
  var MIN_LINEAR_ALPHA = 0;
699
802
  var MAX_LINEAR_ALPHA = 1;
700
803
 
701
- export { ANTIMERIDIAN_SPAN_DEGREES, DEFAULT_FUZZY_PREFIX_LENGTH, DEFAULT_LINEAR_ALPHA, DEFAULT_MAX_PER_GROUP, DEFAULT_PAGE_SIZE, DEFAULT_RANK_CONSTANT, DEG_TO_RAD, EARTH_RADIUS_METERS, FULL_TURN_DEGREES, LOCATION_CODE_BITS_PER_AXIS, MAX_COVERING_CELLS, MAX_CURSOR_ANCHOR_CODE_POINTS, MAX_CURSOR_LENGTH, MAX_LATITUDE_DEGREES, MAX_LINEAR_ALPHA, MAX_LONGITUDE_DEGREES, MAX_NESTING_DEPTH, MAX_PATTERN_VALUE_LIMIT, MAX_PREFIX_LENGTH, MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, MAX_TOLERANCE, MIN_LINEAR_ALPHA, MIN_PATTERN_VALUE_LIMIT, MIN_POLYGON_POINTS, MIN_RANK_CONSTANT, PREFIX_MAX_EXPANSIONS, PROTOTYPE_POLLUTION_KEYS, RADIUS_BOX_MARGIN_RADIANS, RADIUS_BOX_MARGIN_RATIO, RESULT_WINDOW, VECTOR_PATTERN, VINCENTY_CONVERGENCE_THRESHOLD, VINCENTY_MAX_ITERATIONS, extractVectorFieldsFromSchema, flattenSchema, isGeopointOrVectorType, isLatitude, isListBase, isLongitude, isQuantizationMode, isSingleStringFieldType, isStorageMode, isStringValuedBase, isWordIndexedBase, isWordIndexedFieldType, parsedTypeOf, requireValidFieldTypes, schemaFieldsOf, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion, validateVectorStorage, withSortableOption };
804
+ // src/engine/constants.ts
805
+ var MIN_DOCUMENTS_FOR_SEGMENTS = 64;
806
+ var BATCH_CHUNK_SIZE = 1e3;
807
+ var DEFAULT_LIMIT = DEFAULT_PAGE_SIZE;
808
+ var DEFAULT_OFFSET = 0;
809
+ var INDEX_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
810
+ var ABSOLUTE_WINDOWS_PATH_PATTERN = /^[a-zA-Z]:[\\/]/;
811
+ var MODULE_URL_SCHEME_PATTERN = /^(file|https?|data|node):/;
812
+ var MAX_INDEX_NAME_LENGTH = 255;
813
+ var MAX_DOC_ID_LENGTH = 512;
814
+ var EXPORT_CHUNK_BYTES = 1048576;
815
+ var MAX_EXPORT_LINE_BYTES = 117443592;
816
+ var MAX_INDEX_CONFIGURATION_BYTES = 16777216;
817
+ var IMPORT_BATCH_DOCUMENTS = 1e3;
818
+ var MAX_IMPORT_REFUSALS_REPORTED = 1e3;
819
+ var MIN_SWEEP_INTERVAL_MS = 100;
820
+ var MAX_SWEEP_INTERVAL_MS = 6e4;
821
+ var HEAP_PRESSURE_WARNING_FRACTION = 0.9;
822
+ var HEAP_PRESSURE_REARM_FRACTION = 0.8;
823
+ var STATISTICS_BROADCAST_INTERVAL_MS = 5e3;
824
+ var FOREIGN_STATS_TTL_MS = 6e4;
825
+ var DEFAULT_SUGGEST_LIMIT = 10;
826
+ var MAX_SUGGEST_LIMIT = 100;
827
+ var SUGGEST_OVERSAMPLE_FACTOR = 1.5;
828
+ var SUGGEST_OVERSAMPLE_PADDING = 10;
829
+ var MAX_SUGGEST_SCATTER_LIMIT = Math.ceil(MAX_SUGGEST_LIMIT * SUGGEST_OVERSAMPLE_FACTOR) + SUGGEST_OVERSAMPLE_PADDING;
830
+
831
+ export { ABSOLUTE_WINDOWS_PATH_PATTERN, ANTIMERIDIAN_SPAN_DEGREES, BATCH_CHUNK_SIZE, DEFAULT_FUZZY_PREFIX_LENGTH, DEFAULT_LIMIT, DEFAULT_LINEAR_ALPHA, DEFAULT_MAX_PER_GROUP, DEFAULT_OFFSET, DEFAULT_PAGE_SIZE, DEFAULT_RANK_CONSTANT, DEFAULT_SUGGEST_LIMIT, DEG_TO_RAD, EARTH_RADIUS_METERS, EXPORT_CHUNK_BYTES, FOREIGN_STATS_TTL_MS, FULL_TURN_DEGREES, HEAP_PRESSURE_REARM_FRACTION, HEAP_PRESSURE_WARNING_FRACTION, IMPORT_BATCH_DOCUMENTS, INDEX_NAME_PATTERN, LOCATION_CODE_BITS_PER_AXIS, MAX_COVERING_CELLS, MAX_CURSOR_ANCHOR_CODE_POINTS, MAX_CURSOR_LENGTH, MAX_DOCUMENT_NESTING_DEPTH, MAX_DOC_ID_LENGTH, MAX_EXPORT_LINE_BYTES, MAX_IMPORT_REFUSALS_REPORTED, MAX_INDEX_CONFIGURATION_BYTES, MAX_INDEX_NAME_LENGTH, MAX_LATITUDE_DEGREES, MAX_LINEAR_ALPHA, MAX_LONGITUDE_DEGREES, MAX_NESTING_DEPTH, MAX_PATTERN_VALUE_LIMIT, MAX_PREFIX_LENGTH, MAX_SORT_FIELDS, MAX_SORT_FIELD_NAME_LENGTH, MAX_STORED_VECTOR_DIMENSION, MAX_SUGGEST_LIMIT, MAX_SUGGEST_SCATTER_LIMIT, MAX_SWEEP_INTERVAL_MS, MAX_TOLERANCE, MIN_DOCUMENTS_FOR_SEGMENTS, MIN_LINEAR_ALPHA, MIN_PATTERN_VALUE_LIMIT, MIN_POLYGON_POINTS, MIN_RANK_CONSTANT, MIN_SWEEP_INTERVAL_MS, MODULE_URL_SCHEME_PATTERN, PREFIX_MAX_EXPANSIONS, PROTOTYPE_POLLUTION_KEYS, RADIUS_BOX_MARGIN_RADIANS, RADIUS_BOX_MARGIN_RATIO, RESULT_WINDOW, STATISTICS_BROADCAST_INTERVAL_MS, SUGGEST_OVERSAMPLE_FACTOR, SUGGEST_OVERSAMPLE_PADDING, VECTOR_PATTERN, VINCENTY_CONVERGENCE_THRESHOLD, VINCENTY_MAX_ITERATIONS, encodeDocument, extractVectorFieldsFromSchema, flattenSchema, isGeopointOrVectorType, isLatitude, isListBase, isLongitude, isQuantizationMode, isSingleStringFieldType, isStorageMode, isStringValuedBase, isWordIndexedBase, isWordIndexedFieldType, messagePackUpperBoundBytes, parsedTypeOf, raiseEarlierReleaseGraphSetting, requireCreatableSchema, requireDocumentWithinSizeLimit, requireValidFieldTypes, schemaFieldsOf, validateDocument, validateDocumentStrict, validateRequiredFields, validateSchema, validateVectorPromotion, validateVectorStorage, withSortableOption };