@topgunbuild/core 0.10.1 → 0.12.0

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.
package/dist/index.js CHANGED
@@ -30,13 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AuthAckMessageSchema: () => AuthAckMessageSchema,
33
34
  AuthFailMessageSchema: () => AuthFailMessageSchema,
34
35
  AuthMessageSchema: () => AuthMessageSchema,
36
+ AuthRequiredMessageSchema: () => AuthRequiredMessageSchema,
35
37
  BM25Scorer: () => BM25Scorer,
36
38
  BatchMessageSchema: () => BatchMessageSchema,
37
39
  BuiltInProcessors: () => BuiltInProcessors,
38
40
  BuiltInResolvers: () => BuiltInResolvers,
41
+ COST_WEIGHTS: () => COST_WEIGHTS,
39
42
  CRDTDebugger: () => CRDTDebugger,
43
+ CRDTInvariants: () => CRDTInvariants,
44
+ ChangeEventTypeSchema: () => ChangeEventTypeSchema,
40
45
  ClientOpMessageSchema: () => ClientOpMessageSchema,
41
46
  ClientOpSchema: () => ClientOpSchema,
42
47
  ClusterSearchReqMessageSchema: () => ClusterSearchReqMessageSchema,
@@ -78,6 +83,7 @@ __export(index_exports, {
78
83
  DEFAULT_RESOLVER_RATE_LIMITS: () => DEFAULT_RESOLVER_RATE_LIMITS,
79
84
  DEFAULT_STOP_WORDS: () => DEFAULT_STOP_WORDS,
80
85
  DEFAULT_WRITE_CONCERN_TIMEOUT: () => DEFAULT_WRITE_CONCERN_TIMEOUT,
86
+ DeltaRecordSchema: () => DeltaRecordSchema,
81
87
  ENGLISH_STOPWORDS: () => ENGLISH_STOPWORDS,
82
88
  EntryProcessBatchRequestSchema: () => EntryProcessBatchRequestSchema,
83
89
  EntryProcessBatchResponseSchema: () => EntryProcessBatchResponseSchema,
@@ -86,6 +92,7 @@ __export(index_exports, {
86
92
  EntryProcessResponseSchema: () => EntryProcessResponseSchema,
87
93
  EntryProcessorDefSchema: () => EntryProcessorDefSchema,
88
94
  EntryProcessorSchema: () => EntryProcessorSchema,
95
+ ErrorMessageSchema: () => ErrorMessageSchema,
89
96
  EventJournalImpl: () => EventJournalImpl,
90
97
  FORBIDDEN_PATTERNS: () => FORBIDDEN_PATTERNS,
91
98
  FTSInvertedIndex: () => BM25InvertedIndex,
@@ -97,12 +104,18 @@ __export(index_exports, {
97
104
  GcPrunePayloadSchema: () => GcPrunePayloadSchema,
98
105
  HLC: () => HLC,
99
106
  HashIndex: () => HashIndex,
100
- HybridQueryDeltaPayloadSchema: () => HybridQueryDeltaPayloadSchema,
101
- HybridQueryRespPayloadSchema: () => HybridQueryRespPayloadSchema,
107
+ HttpQueryRequestSchema: () => HttpQueryRequestSchema,
108
+ HttpQueryResultSchema: () => HttpQueryResultSchema,
109
+ HttpSearchRequestSchema: () => HttpSearchRequestSchema,
110
+ HttpSearchResultSchema: () => HttpSearchResultSchema,
111
+ HttpSyncErrorSchema: () => HttpSyncErrorSchema,
112
+ HttpSyncRequestSchema: () => HttpSyncRequestSchema,
113
+ HttpSyncResponseSchema: () => HttpSyncResponseSchema,
102
114
  IndexRegistry: () => IndexRegistry,
103
115
  IndexedLWWMap: () => IndexedLWWMap,
104
116
  IndexedORMap: () => IndexedORMap,
105
117
  IntersectionResultSet: () => IntersectionResultSet,
118
+ InvariantChecker: () => InvariantChecker,
106
119
  InvertedIndex: () => InvertedIndex,
107
120
  JournalEventDataSchema: () => JournalEventDataSchema,
108
121
  JournalEventMessageSchema: () => JournalEventMessageSchema,
@@ -118,11 +131,14 @@ __export(index_exports, {
118
131
  ListResolversRequestSchema: () => ListResolversRequestSchema,
119
132
  ListResolversResponseSchema: () => ListResolversResponseSchema,
120
133
  LiveQueryManager: () => LiveQueryManager,
134
+ LockGrantedMessageSchema: () => LockGrantedMessageSchema,
121
135
  LockGrantedPayloadSchema: () => LockGrantedPayloadSchema,
122
136
  LockReleaseSchema: () => LockReleaseSchema,
137
+ LockReleasedMessageSchema: () => LockReleasedMessageSchema,
123
138
  LockReleasedPayloadSchema: () => LockReleasedPayloadSchema,
124
139
  LockRequestSchema: () => LockRequestSchema,
125
140
  LowercaseFilter: () => LowercaseFilter,
141
+ MapDeltaSchema: () => MapDeltaSchema,
126
142
  MaxLengthFilter: () => MaxLengthFilter,
127
143
  MergeRejectedMessageSchema: () => MergeRejectedMessageSchema,
128
144
  MerkleReqBucketMessageSchema: () => MerkleReqBucketMessageSchema,
@@ -132,9 +148,11 @@ __export(index_exports, {
132
148
  MultiValueAttribute: () => MultiValueAttribute,
133
149
  NGramTokenizer: () => NGramTokenizer,
134
150
  NavigableIndex: () => NavigableIndex,
151
+ NodeInfoSchema: () => NodeInfoSchema,
135
152
  ORMap: () => ORMap,
136
153
  ORMapDiffRequestSchema: () => ORMapDiffRequestSchema,
137
154
  ORMapDiffResponseSchema: () => ORMapDiffResponseSchema,
155
+ ORMapEntrySchema: () => ORMapEntrySchema,
138
156
  ORMapMerkleReqBucketSchema: () => ORMapMerkleReqBucketSchema,
139
157
  ORMapMerkleTree: () => ORMapMerkleTree,
140
158
  ORMapPushDiffSchema: () => ORMapPushDiffSchema,
@@ -150,6 +168,9 @@ __export(index_exports, {
150
168
  PARTITION_COUNT: () => PARTITION_COUNT,
151
169
  PNCounterImpl: () => PNCounterImpl,
152
170
  PNCounterStateObjectSchema: () => PNCounterStateObjectSchema,
171
+ PartitionInfoSchema: () => PartitionInfoSchema,
172
+ PartitionMapMessageSchema: () => PartitionMapMessageSchema,
173
+ PartitionMapPayloadSchema: () => PartitionMapPayloadSchema,
153
174
  PartitionMapRequestSchema: () => PartitionMapRequestSchema,
154
175
  PartitionState: () => PartitionState,
155
176
  PingMessageSchema: () => PingMessageSchema,
@@ -167,10 +188,12 @@ __export(index_exports, {
167
188
  QueryUpdateMessageSchema: () => QueryUpdateMessageSchema,
168
189
  QueryUpdatePayloadSchema: () => QueryUpdatePayloadSchema,
169
190
  RESOLVER_FORBIDDEN_PATTERNS: () => RESOLVER_FORBIDDEN_PATTERNS,
191
+ RealClock: () => RealClock,
170
192
  ReciprocalRankFusion: () => ReciprocalRankFusion,
171
193
  RegisterResolverRequestSchema: () => RegisterResolverRequestSchema,
172
194
  RegisterResolverResponseSchema: () => RegisterResolverResponseSchema,
173
195
  Ringbuffer: () => Ringbuffer,
196
+ ScenarioRunner: () => ScenarioRunner,
174
197
  SearchCursor: () => SearchCursor,
175
198
  SearchDebugger: () => SearchDebugger,
176
199
  SearchMessageSchema: () => SearchMessageSchema,
@@ -185,6 +208,7 @@ __export(index_exports, {
185
208
  SearchUpdateMessageSchema: () => SearchUpdateMessageSchema,
186
209
  SearchUpdatePayloadSchema: () => SearchUpdatePayloadSchema,
187
210
  SearchUpdateTypeSchema: () => SearchUpdateTypeSchema,
211
+ SeededRNG: () => SeededRNG,
188
212
  ServerBatchEventMessageSchema: () => ServerBatchEventMessageSchema,
189
213
  ServerEventMessageSchema: () => ServerEventMessageSchema,
190
214
  ServerEventPayloadSchema: () => ServerEventPayloadSchema,
@@ -196,6 +220,8 @@ __export(index_exports, {
196
220
  StandingQueryRegistry: () => StandingQueryRegistry,
197
221
  StopWordFilter: () => StopWordFilter,
198
222
  SyncInitMessageSchema: () => SyncInitMessageSchema,
223
+ SyncMapEntrySchema: () => SyncMapEntrySchema,
224
+ SyncResetRequiredMessageSchema: () => SyncResetRequiredMessageSchema,
199
225
  SyncResetRequiredPayloadSchema: () => SyncResetRequiredPayloadSchema,
200
226
  SyncRespBucketsMessageSchema: () => SyncRespBucketsMessageSchema,
201
227
  SyncRespLeafMessageSchema: () => SyncRespLeafMessageSchema,
@@ -211,11 +237,14 @@ __export(index_exports, {
211
237
  UniqueFilter: () => UniqueFilter,
212
238
  UnregisterResolverRequestSchema: () => UnregisterResolverRequestSchema,
213
239
  UnregisterResolverResponseSchema: () => UnregisterResolverResponseSchema,
240
+ VirtualClock: () => VirtualClock,
241
+ VirtualNetwork: () => VirtualNetwork,
214
242
  WRITE_CONCERN_ORDER: () => WRITE_CONCERN_ORDER,
215
243
  WhitespaceTokenizer: () => WhitespaceTokenizer,
216
244
  WordBoundaryTokenizer: () => WordBoundaryTokenizer,
217
245
  WriteConcern: () => WriteConcern,
218
246
  WriteConcernSchema: () => WriteConcernSchema,
247
+ calculateTotalCost: () => calculateTotalCost,
219
248
  combineHashes: () => combineHashes,
220
249
  compareHLCTimestamps: () => compareHLCTimestamps,
221
250
  compareTimestamps: () => compareTimestamps,
@@ -225,7 +254,6 @@ __export(index_exports, {
225
254
  decodeBase64Url: () => decodeBase64Url,
226
255
  deepMerge: () => deepMerge,
227
256
  deserialize: () => deserialize,
228
- disableNativeHash: () => disableNativeHash,
229
257
  encodeBase64Url: () => encodeBase64Url,
230
258
  evaluatePredicate: () => evaluatePredicate,
231
259
  getCRDTDebugger: () => getCRDTDebugger,
@@ -237,13 +265,11 @@ __export(index_exports, {
237
265
  hashString: () => hashString,
238
266
  isLogicalQuery: () => isLogicalQuery,
239
267
  isSimpleQuery: () => isSimpleQuery,
240
- isUsingNativeHash: () => isUsingNativeHash,
241
268
  isWriteConcernAchieved: () => isWriteConcernAchieved,
242
269
  logger: () => logger,
243
270
  multiAttribute: () => multiAttribute,
244
271
  porterStem: () => porterStem,
245
272
  resetCRDTDebugger: () => resetCRDTDebugger,
246
- resetNativeHash: () => resetNativeHash,
247
273
  resetSearchDebugger: () => resetSearchDebugger,
248
274
  serialize: () => serialize,
249
275
  simpleAttribute: () => simpleAttribute,
@@ -266,9 +292,13 @@ var logger = (0, import_pino.default)({
266
292
  // src/HLC.ts
267
293
  var HLC = class {
268
294
  constructor(nodeId, options = {}) {
295
+ if (nodeId.includes(":")) {
296
+ throw new Error('Node ID must not contain ":" (used as delimiter in timestamp format)');
297
+ }
269
298
  this.nodeId = nodeId;
270
299
  this.strictMode = options.strictMode ?? false;
271
300
  this.maxDriftMs = options.maxDriftMs ?? 6e4;
301
+ this.clockSource = options.clockSource ?? { now: () => Date.now() };
272
302
  this.lastMillis = 0;
273
303
  this.lastCounter = 0;
274
304
  }
@@ -281,12 +311,19 @@ var HLC = class {
281
311
  get getMaxDriftMs() {
282
312
  return this.maxDriftMs;
283
313
  }
314
+ /**
315
+ * Returns the clock source used by this HLC instance.
316
+ * Useful for LWWMap/ORMap to access the same clock for TTL checks.
317
+ */
318
+ getClockSource() {
319
+ return this.clockSource;
320
+ }
284
321
  /**
285
322
  * Generates a new unique timestamp for a local event.
286
323
  * Ensures monotonicity: always greater than any previously generated or received timestamp.
287
324
  */
288
325
  now() {
289
- const systemTime = Date.now();
326
+ const systemTime = this.clockSource.now();
290
327
  if (systemTime > this.lastMillis) {
291
328
  this.lastMillis = systemTime;
292
329
  this.lastCounter = 0;
@@ -304,27 +341,33 @@ var HLC = class {
304
341
  * Must be called whenever a message/event is received from another node.
305
342
  */
306
343
  update(remote) {
307
- const systemTime = Date.now();
308
- const drift = remote.millis - systemTime;
344
+ const remoteMillis = Number(remote.millis);
345
+ const remoteCounter = Number(remote.counter);
346
+ if (!Number.isFinite(remoteMillis) || !Number.isFinite(remoteCounter)) {
347
+ logger.warn({ remoteMillis, remoteCounter, remote }, "HLC.update() received invalid timestamp, ignoring");
348
+ return;
349
+ }
350
+ const systemTime = this.clockSource.now();
351
+ const drift = remoteMillis - systemTime;
309
352
  if (drift > this.maxDriftMs) {
310
353
  if (this.strictMode) {
311
- throw new Error(`Clock drift detected: Remote time ${remote.millis} is ${drift}ms ahead of local ${systemTime} (threshold: ${this.maxDriftMs}ms)`);
354
+ throw new Error(`Clock drift detected: Remote time ${remoteMillis} is ${drift}ms ahead of local ${systemTime} (threshold: ${this.maxDriftMs}ms)`);
312
355
  } else {
313
356
  logger.warn({
314
357
  drift,
315
- remoteMillis: remote.millis,
358
+ remoteMillis,
316
359
  localMillis: systemTime,
317
360
  maxDriftMs: this.maxDriftMs
318
361
  }, "Clock drift detected");
319
362
  }
320
363
  }
321
- const maxMillis = Math.max(this.lastMillis, systemTime, remote.millis);
322
- if (maxMillis === this.lastMillis && maxMillis === remote.millis) {
323
- this.lastCounter = Math.max(this.lastCounter, remote.counter) + 1;
364
+ const maxMillis = Math.max(this.lastMillis, systemTime, remoteMillis);
365
+ if (maxMillis === this.lastMillis && maxMillis === remoteMillis) {
366
+ this.lastCounter = Math.max(this.lastCounter, remoteCounter) + 1;
324
367
  } else if (maxMillis === this.lastMillis) {
325
368
  this.lastCounter++;
326
- } else if (maxMillis === remote.millis) {
327
- this.lastCounter = remote.counter + 1;
369
+ } else if (maxMillis === remoteMillis) {
370
+ this.lastCounter = remoteCounter + 1;
328
371
  } else {
329
372
  this.lastCounter = 0;
330
373
  }
@@ -367,17 +410,7 @@ var HLC = class {
367
410
  };
368
411
 
369
412
  // src/utils/hash.ts
370
- var nativeHash = null;
371
- var nativeLoadAttempted = false;
372
- function tryLoadNative() {
373
- if (nativeLoadAttempted) return;
374
- nativeLoadAttempted = true;
375
- try {
376
- nativeHash = require("@topgunbuild/native");
377
- } catch {
378
- }
379
- }
380
- function fnv1aHash(str) {
413
+ function hashString(str) {
381
414
  let hash = 2166136261;
382
415
  for (let i = 0; i < str.length; i++) {
383
416
  hash ^= str.charCodeAt(i);
@@ -385,13 +418,6 @@ function fnv1aHash(str) {
385
418
  }
386
419
  return hash >>> 0;
387
420
  }
388
- function hashString(str) {
389
- tryLoadNative();
390
- if (nativeHash && nativeHash.isNativeHashAvailable()) {
391
- return nativeHash.hashString(str);
392
- }
393
- return fnv1aHash(str);
394
- }
395
421
  function combineHashes(hashes) {
396
422
  let result = 0;
397
423
  for (const h of hashes) {
@@ -399,18 +425,6 @@ function combineHashes(hashes) {
399
425
  }
400
426
  return result >>> 0;
401
427
  }
402
- function isUsingNativeHash() {
403
- tryLoadNative();
404
- return nativeHash?.isNativeHashAvailable() === true;
405
- }
406
- function disableNativeHash() {
407
- nativeHash = null;
408
- nativeLoadAttempted = true;
409
- }
410
- function resetNativeHash() {
411
- nativeHash = null;
412
- nativeLoadAttempted = false;
413
- }
414
428
  function hashObject(obj) {
415
429
  const json = JSON.stringify(obj, (_, value) => {
416
430
  if (value && typeof value === "object" && !Array.isArray(value)) {
@@ -588,7 +602,7 @@ var LWWMap = class {
588
602
  return void 0;
589
603
  }
590
604
  if (record.ttlMs) {
591
- const now = Date.now();
605
+ const now = this.hlc.getClockSource().now();
592
606
  if (record.timestamp.millis + record.ttlMs < now) {
593
607
  return void 0;
594
608
  }
@@ -665,7 +679,7 @@ var LWWMap = class {
665
679
  */
666
680
  entries() {
667
681
  const iterator = this.data.entries();
668
- const now = Date.now();
682
+ const clockSource = this.hlc.getClockSource();
669
683
  return {
670
684
  [Symbol.iterator]() {
671
685
  return this;
@@ -675,7 +689,7 @@ var LWWMap = class {
675
689
  while (!result.done) {
676
690
  const [key, record] = result.value;
677
691
  if (record.value !== null) {
678
- if (record.ttlMs && record.timestamp.millis + record.ttlMs < now) {
692
+ if (record.ttlMs && record.timestamp.millis + record.ttlMs < clockSource.now()) {
679
693
  result = iterator.next();
680
694
  continue;
681
695
  }
@@ -1012,7 +1026,7 @@ var ORMap = class {
1012
1026
  const keyMap = this.items.get(key);
1013
1027
  if (!keyMap) return [];
1014
1028
  const values = [];
1015
- const now = Date.now();
1029
+ const now = this.hlc.getClockSource().now();
1016
1030
  for (const [tag, record] of keyMap.entries()) {
1017
1031
  if (!this.tombstones.has(tag)) {
1018
1032
  if (record.ttlMs && record.timestamp.millis + record.ttlMs < now) {
@@ -1032,7 +1046,7 @@ var ORMap = class {
1032
1046
  const keyMap = this.items.get(key);
1033
1047
  if (!keyMap) return [];
1034
1048
  const records = [];
1035
- const now = Date.now();
1049
+ const now = this.hlc.getClockSource().now();
1036
1050
  for (const [tag, record] of keyMap.entries()) {
1037
1051
  if (!this.tombstones.has(tag)) {
1038
1052
  if (record.ttlMs && record.timestamp.millis + record.ttlMs < now) {
@@ -1248,11 +1262,49 @@ var ORMap = class {
1248
1262
  // src/serializer.ts
1249
1263
  var import_msgpackr = require("msgpackr");
1250
1264
  function serialize(data) {
1251
- return (0, import_msgpackr.pack)(data);
1265
+ return (0, import_msgpackr.pack)(stripUndefined(data));
1252
1266
  }
1253
1267
  function deserialize(data) {
1254
1268
  const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
1255
- return (0, import_msgpackr.unpack)(buffer);
1269
+ const result = (0, import_msgpackr.unpack)(buffer);
1270
+ return coerceBigInts(result);
1271
+ }
1272
+ function stripUndefined(value) {
1273
+ if (value === void 0) {
1274
+ return null;
1275
+ }
1276
+ if (Array.isArray(value)) {
1277
+ return value.map(stripUndefined);
1278
+ }
1279
+ if (value !== null && typeof value === "object") {
1280
+ const result = {};
1281
+ for (const [k, v] of Object.entries(value)) {
1282
+ if (v !== void 0) {
1283
+ result[k] = stripUndefined(v);
1284
+ }
1285
+ }
1286
+ return result;
1287
+ }
1288
+ return value;
1289
+ }
1290
+ function coerceBigInts(value) {
1291
+ if (typeof value === "bigint") {
1292
+ return Number(value);
1293
+ }
1294
+ if (Array.isArray(value)) {
1295
+ for (let i = 0; i < value.length; i++) {
1296
+ value[i] = coerceBigInts(value[i]);
1297
+ }
1298
+ return value;
1299
+ }
1300
+ if (value !== null && typeof value === "object") {
1301
+ const obj = value;
1302
+ for (const key of Object.keys(obj)) {
1303
+ obj[key] = coerceBigInts(obj[key]);
1304
+ }
1305
+ return obj;
1306
+ }
1307
+ return value;
1256
1308
  }
1257
1309
 
1258
1310
  // src/PNCounter.ts
@@ -2427,6 +2479,7 @@ var ORMapRecordSchema = import_zod3.z.object({
2427
2479
  tag: import_zod3.z.string(),
2428
2480
  ttlMs: import_zod3.z.number().optional()
2429
2481
  });
2482
+ var ChangeEventTypeSchema = import_zod3.z.enum(["ENTER", "UPDATE", "LEAVE"]);
2430
2483
  var PredicateOpSchema = import_zod3.z.enum([
2431
2484
  "eq",
2432
2485
  "neq",
@@ -2467,7 +2520,11 @@ var ClientOpSchema = import_zod3.z.object({
2467
2520
  });
2468
2521
  var AuthMessageSchema = import_zod3.z.object({
2469
2522
  type: import_zod3.z.literal("AUTH"),
2470
- token: import_zod3.z.string()
2523
+ token: import_zod3.z.string(),
2524
+ protocolVersion: import_zod3.z.number().optional()
2525
+ });
2526
+ var AuthRequiredMessageSchema = import_zod3.z.object({
2527
+ type: import_zod3.z.literal("AUTH_REQUIRED")
2471
2528
  });
2472
2529
 
2473
2530
  // src/schemas/sync-schemas.ts
@@ -2523,6 +2580,11 @@ var MerkleReqBucketMessageSchema = import_zod4.z.object({
2523
2580
  path: import_zod4.z.string()
2524
2581
  })
2525
2582
  });
2583
+ var ORMapEntrySchema = import_zod4.z.object({
2584
+ key: import_zod4.z.string(),
2585
+ records: import_zod4.z.array(ORMapRecordSchema),
2586
+ tombstones: import_zod4.z.array(import_zod4.z.string())
2587
+ });
2526
2588
  var ORMapSyncInitSchema = import_zod4.z.object({
2527
2589
  type: import_zod4.z.literal("ORMAP_SYNC_INIT"),
2528
2590
  mapName: import_zod4.z.string(),
@@ -2558,11 +2620,7 @@ var ORMapSyncRespLeafSchema = import_zod4.z.object({
2558
2620
  payload: import_zod4.z.object({
2559
2621
  mapName: import_zod4.z.string(),
2560
2622
  path: import_zod4.z.string(),
2561
- entries: import_zod4.z.array(import_zod4.z.object({
2562
- key: import_zod4.z.string(),
2563
- records: import_zod4.z.array(ORMapRecordSchema),
2564
- tombstones: import_zod4.z.array(import_zod4.z.string())
2565
- }))
2623
+ entries: import_zod4.z.array(ORMapEntrySchema)
2566
2624
  })
2567
2625
  });
2568
2626
  var ORMapDiffRequestSchema = import_zod4.z.object({
@@ -2576,22 +2634,14 @@ var ORMapDiffResponseSchema = import_zod4.z.object({
2576
2634
  type: import_zod4.z.literal("ORMAP_DIFF_RESPONSE"),
2577
2635
  payload: import_zod4.z.object({
2578
2636
  mapName: import_zod4.z.string(),
2579
- entries: import_zod4.z.array(import_zod4.z.object({
2580
- key: import_zod4.z.string(),
2581
- records: import_zod4.z.array(ORMapRecordSchema),
2582
- tombstones: import_zod4.z.array(import_zod4.z.string())
2583
- }))
2637
+ entries: import_zod4.z.array(ORMapEntrySchema)
2584
2638
  })
2585
2639
  });
2586
2640
  var ORMapPushDiffSchema = import_zod4.z.object({
2587
2641
  type: import_zod4.z.literal("ORMAP_PUSH_DIFF"),
2588
2642
  payload: import_zod4.z.object({
2589
2643
  mapName: import_zod4.z.string(),
2590
- entries: import_zod4.z.array(import_zod4.z.object({
2591
- key: import_zod4.z.string(),
2592
- records: import_zod4.z.array(ORMapRecordSchema),
2593
- tombstones: import_zod4.z.array(import_zod4.z.string())
2594
- }))
2644
+ entries: import_zod4.z.array(ORMapEntrySchema)
2595
2645
  })
2596
2646
  });
2597
2647
  var OpResultSchema = import_zod4.z.object({
@@ -2686,7 +2736,7 @@ var SearchRespMessageSchema = import_zod6.z.object({
2686
2736
  type: import_zod6.z.literal("SEARCH_RESP"),
2687
2737
  payload: SearchRespPayloadSchema
2688
2738
  });
2689
- var SearchUpdateTypeSchema = import_zod6.z.enum(["ENTER", "UPDATE", "LEAVE"]);
2739
+ var SearchUpdateTypeSchema = ChangeEventTypeSchema;
2690
2740
  var SearchSubPayloadSchema = import_zod6.z.object({
2691
2741
  subscriptionId: import_zod6.z.string(),
2692
2742
  mapName: import_zod6.z.string(),
@@ -2703,7 +2753,7 @@ var SearchUpdatePayloadSchema = import_zod6.z.object({
2703
2753
  value: import_zod6.z.unknown(),
2704
2754
  score: import_zod6.z.number(),
2705
2755
  matchedTerms: import_zod6.z.array(import_zod6.z.string()),
2706
- type: SearchUpdateTypeSchema
2756
+ changeType: SearchUpdateTypeSchema
2707
2757
  });
2708
2758
  var SearchUpdateMessageSchema = import_zod6.z.object({
2709
2759
  type: import_zod6.z.literal("SEARCH_UPDATE"),
@@ -2725,17 +2775,37 @@ var PartitionMapRequestSchema = import_zod7.z.object({
2725
2775
  currentVersion: import_zod7.z.number().optional()
2726
2776
  }).optional()
2727
2777
  });
2778
+ var NodeInfoSchema = import_zod7.z.object({
2779
+ nodeId: import_zod7.z.string(),
2780
+ endpoints: import_zod7.z.object({
2781
+ websocket: import_zod7.z.string(),
2782
+ http: import_zod7.z.string().optional()
2783
+ }),
2784
+ status: import_zod7.z.enum(["ACTIVE", "JOINING", "LEAVING", "SUSPECTED", "FAILED"])
2785
+ });
2786
+ var PartitionInfoSchema = import_zod7.z.object({
2787
+ partitionId: import_zod7.z.number(),
2788
+ ownerNodeId: import_zod7.z.string(),
2789
+ backupNodeIds: import_zod7.z.array(import_zod7.z.string())
2790
+ });
2791
+ var PartitionMapPayloadSchema = import_zod7.z.object({
2792
+ version: import_zod7.z.number(),
2793
+ partitionCount: import_zod7.z.number(),
2794
+ nodes: import_zod7.z.array(NodeInfoSchema),
2795
+ partitions: import_zod7.z.array(PartitionInfoSchema),
2796
+ generatedAt: import_zod7.z.number()
2797
+ });
2798
+ var PartitionMapMessageSchema = import_zod7.z.object({
2799
+ type: import_zod7.z.literal("PARTITION_MAP"),
2800
+ payload: PartitionMapPayloadSchema
2801
+ });
2728
2802
  var ClusterSubRegisterPayloadSchema = import_zod7.z.object({
2729
2803
  subscriptionId: import_zod7.z.string(),
2730
2804
  coordinatorNodeId: import_zod7.z.string(),
2731
2805
  mapName: import_zod7.z.string(),
2732
2806
  type: import_zod7.z.enum(["SEARCH", "QUERY"]),
2733
2807
  searchQuery: import_zod7.z.string().optional(),
2734
- searchOptions: import_zod7.z.object({
2735
- limit: import_zod7.z.number().int().positive().optional(),
2736
- minScore: import_zod7.z.number().optional(),
2737
- boost: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.number()).optional()
2738
- }).optional(),
2808
+ searchOptions: SearchOptionsSchema.optional(),
2739
2809
  queryPredicate: import_zod7.z.any().optional(),
2740
2810
  querySort: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.enum(["asc", "desc"])).optional()
2741
2811
  });
@@ -2767,7 +2837,7 @@ var ClusterSubUpdatePayloadSchema = import_zod7.z.object({
2767
2837
  value: import_zod7.z.unknown(),
2768
2838
  score: import_zod7.z.number().optional(),
2769
2839
  matchedTerms: import_zod7.z.array(import_zod7.z.string()).optional(),
2770
- changeType: import_zod7.z.enum(["ENTER", "UPDATE", "LEAVE"]),
2840
+ changeType: ChangeEventTypeSchema,
2771
2841
  timestamp: import_zod7.z.number()
2772
2842
  });
2773
2843
  var ClusterSubUpdateMessageSchema = import_zod7.z.object({
@@ -2785,10 +2855,8 @@ var ClusterSearchReqPayloadSchema = import_zod7.z.object({
2785
2855
  requestId: import_zod7.z.string(),
2786
2856
  mapName: import_zod7.z.string(),
2787
2857
  query: import_zod7.z.string(),
2788
- options: import_zod7.z.object({
2858
+ options: SearchOptionsSchema.extend({
2789
2859
  limit: import_zod7.z.number().int().positive().max(1e3),
2790
- minScore: import_zod7.z.number().optional(),
2791
- boost: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.number()).optional(),
2792
2860
  includeMatchedTerms: import_zod7.z.boolean().optional(),
2793
2861
  afterScore: import_zod7.z.number().optional(),
2794
2862
  afterKey: import_zod7.z.string().optional()
@@ -2840,7 +2908,7 @@ var ClusterSearchUpdatePayloadSchema = import_zod7.z.object({
2840
2908
  value: import_zod7.z.unknown(),
2841
2909
  score: import_zod7.z.number(),
2842
2910
  matchedTerms: import_zod7.z.array(import_zod7.z.string()).optional(),
2843
- type: SearchUpdateTypeSchema
2911
+ changeType: ChangeEventTypeSchema
2844
2912
  });
2845
2913
  var ClusterSearchUpdateMessageSchema = import_zod7.z.object({
2846
2914
  type: import_zod7.z.literal("CLUSTER_SEARCH_UPDATE"),
@@ -3083,7 +3151,7 @@ var QueryUpdatePayloadSchema = import_zod9.z.object({
3083
3151
  queryId: import_zod9.z.string(),
3084
3152
  key: import_zod9.z.string(),
3085
3153
  value: import_zod9.z.unknown(),
3086
- type: import_zod9.z.enum(["ENTER", "UPDATE", "REMOVE"])
3154
+ changeType: ChangeEventTypeSchema
3087
3155
  });
3088
3156
  var QueryUpdateMessageSchema = import_zod9.z.object({
3089
3157
  type: import_zod9.z.literal("QUERY_UPDATE"),
@@ -3096,50 +3164,135 @@ var GcPruneMessageSchema = import_zod9.z.object({
3096
3164
  type: import_zod9.z.literal("GC_PRUNE"),
3097
3165
  payload: GcPrunePayloadSchema
3098
3166
  });
3167
+ var AuthAckMessageSchema = import_zod9.z.object({
3168
+ type: import_zod9.z.literal("AUTH_ACK"),
3169
+ protocolVersion: import_zod9.z.number().optional(),
3170
+ userId: import_zod9.z.string().optional()
3171
+ });
3099
3172
  var AuthFailMessageSchema = import_zod9.z.object({
3100
3173
  type: import_zod9.z.literal("AUTH_FAIL"),
3101
3174
  error: import_zod9.z.string().optional(),
3102
3175
  code: import_zod9.z.number().optional()
3103
3176
  });
3104
- var HybridQueryRespPayloadSchema = import_zod9.z.object({
3105
- subscriptionId: import_zod9.z.string(),
3106
- results: import_zod9.z.array(import_zod9.z.object({
3107
- key: import_zod9.z.string(),
3108
- value: import_zod9.z.unknown(),
3109
- score: import_zod9.z.number(),
3110
- matchedTerms: import_zod9.z.array(import_zod9.z.string())
3111
- })),
3112
- nextCursor: import_zod9.z.string().optional(),
3113
- hasMore: import_zod9.z.boolean().optional(),
3114
- cursorStatus: CursorStatusSchema.optional()
3115
- });
3116
- var HybridQueryDeltaPayloadSchema = import_zod9.z.object({
3117
- subscriptionId: import_zod9.z.string(),
3118
- key: import_zod9.z.string(),
3119
- value: import_zod9.z.unknown().nullable(),
3120
- score: import_zod9.z.number().optional(),
3121
- matchedTerms: import_zod9.z.array(import_zod9.z.string()).optional(),
3122
- type: import_zod9.z.enum(["ENTER", "UPDATE", "LEAVE"])
3177
+ var ErrorMessageSchema = import_zod9.z.object({
3178
+ type: import_zod9.z.literal("ERROR"),
3179
+ payload: import_zod9.z.object({
3180
+ code: import_zod9.z.number(),
3181
+ message: import_zod9.z.string(),
3182
+ details: import_zod9.z.unknown().optional()
3183
+ })
3123
3184
  });
3124
3185
  var LockGrantedPayloadSchema = import_zod9.z.object({
3125
3186
  requestId: import_zod9.z.string(),
3187
+ name: import_zod9.z.string(),
3126
3188
  fencingToken: import_zod9.z.number()
3127
3189
  });
3128
3190
  var LockReleasedPayloadSchema = import_zod9.z.object({
3129
3191
  requestId: import_zod9.z.string(),
3192
+ name: import_zod9.z.string(),
3130
3193
  success: import_zod9.z.boolean()
3131
3194
  });
3195
+ var LockGrantedMessageSchema = import_zod9.z.object({
3196
+ type: import_zod9.z.literal("LOCK_GRANTED"),
3197
+ payload: LockGrantedPayloadSchema
3198
+ });
3199
+ var LockReleasedMessageSchema = import_zod9.z.object({
3200
+ type: import_zod9.z.literal("LOCK_RELEASED"),
3201
+ payload: LockReleasedPayloadSchema
3202
+ });
3132
3203
  var SyncResetRequiredPayloadSchema = import_zod9.z.object({
3133
3204
  mapName: import_zod9.z.string(),
3134
3205
  reason: import_zod9.z.string()
3135
3206
  });
3207
+ var SyncResetRequiredMessageSchema = import_zod9.z.object({
3208
+ type: import_zod9.z.literal("SYNC_RESET_REQUIRED"),
3209
+ payload: SyncResetRequiredPayloadSchema
3210
+ });
3136
3211
 
3137
- // src/schemas/index.ts
3212
+ // src/schemas/http-sync-schemas.ts
3138
3213
  var import_zod10 = require("zod");
3139
- var MessageSchema = import_zod10.z.discriminatedUnion("type", [
3214
+ var SyncMapEntrySchema = import_zod10.z.object({
3215
+ mapName: import_zod10.z.string(),
3216
+ lastSyncTimestamp: TimestampSchema
3217
+ });
3218
+ var HttpQueryRequestSchema = import_zod10.z.object({
3219
+ queryId: import_zod10.z.string(),
3220
+ mapName: import_zod10.z.string(),
3221
+ filter: import_zod10.z.any(),
3222
+ limit: import_zod10.z.number().optional(),
3223
+ offset: import_zod10.z.number().optional()
3224
+ });
3225
+ var HttpSearchRequestSchema = import_zod10.z.object({
3226
+ searchId: import_zod10.z.string(),
3227
+ mapName: import_zod10.z.string(),
3228
+ query: import_zod10.z.string(),
3229
+ options: import_zod10.z.any().optional()
3230
+ });
3231
+ var HttpSyncRequestSchema = import_zod10.z.object({
3232
+ // Client identification
3233
+ clientId: import_zod10.z.string(),
3234
+ // Client's current HLC for causality tracking
3235
+ clientHlc: TimestampSchema,
3236
+ // Batch of operations to push (optional)
3237
+ operations: import_zod10.z.array(ClientOpSchema).optional(),
3238
+ // Maps the client wants deltas for, with their last known sync HLC timestamp
3239
+ syncMaps: import_zod10.z.array(SyncMapEntrySchema).optional(),
3240
+ // One-shot queries to execute (optional)
3241
+ queries: import_zod10.z.array(HttpQueryRequestSchema).optional(),
3242
+ // One-shot search requests (optional)
3243
+ searches: import_zod10.z.array(HttpSearchRequestSchema).optional()
3244
+ });
3245
+ var DeltaRecordSchema = import_zod10.z.object({
3246
+ key: import_zod10.z.string(),
3247
+ record: LWWRecordSchema,
3248
+ eventType: import_zod10.z.enum(["PUT", "REMOVE"])
3249
+ });
3250
+ var MapDeltaSchema = import_zod10.z.object({
3251
+ mapName: import_zod10.z.string(),
3252
+ records: import_zod10.z.array(DeltaRecordSchema),
3253
+ serverSyncTimestamp: TimestampSchema
3254
+ });
3255
+ var HttpQueryResultSchema = import_zod10.z.object({
3256
+ queryId: import_zod10.z.string(),
3257
+ results: import_zod10.z.array(import_zod10.z.any()),
3258
+ hasMore: import_zod10.z.boolean().optional(),
3259
+ nextCursor: import_zod10.z.string().optional()
3260
+ });
3261
+ var HttpSearchResultSchema = import_zod10.z.object({
3262
+ searchId: import_zod10.z.string(),
3263
+ results: import_zod10.z.array(import_zod10.z.any()),
3264
+ totalCount: import_zod10.z.number().optional()
3265
+ });
3266
+ var HttpSyncErrorSchema = import_zod10.z.object({
3267
+ code: import_zod10.z.number(),
3268
+ message: import_zod10.z.string(),
3269
+ context: import_zod10.z.string().optional()
3270
+ });
3271
+ var HttpSyncResponseSchema = import_zod10.z.object({
3272
+ // Server's current HLC
3273
+ serverHlc: TimestampSchema,
3274
+ // Acknowledgment of received operations
3275
+ ack: import_zod10.z.object({
3276
+ lastId: import_zod10.z.string(),
3277
+ results: import_zod10.z.array(OpResultSchema).optional()
3278
+ }).optional(),
3279
+ // Delta records for requested maps (new/changed since lastSyncTimestamp)
3280
+ deltas: import_zod10.z.array(MapDeltaSchema).optional(),
3281
+ // Query results
3282
+ queryResults: import_zod10.z.array(HttpQueryResultSchema).optional(),
3283
+ // Search results
3284
+ searchResults: import_zod10.z.array(HttpSearchResultSchema).optional(),
3285
+ // Errors for individual operations
3286
+ errors: import_zod10.z.array(HttpSyncErrorSchema).optional()
3287
+ });
3288
+
3289
+ // src/schemas/index.ts
3290
+ var import_zod11 = require("zod");
3291
+ var MessageSchema = import_zod11.z.discriminatedUnion("type", [
3292
+ // --- Base ---
3140
3293
  AuthMessageSchema,
3141
- QuerySubMessageSchema,
3142
- QueryUnsubMessageSchema,
3294
+ AuthRequiredMessageSchema,
3295
+ // --- Sync ---
3143
3296
  ClientOpMessageSchema,
3144
3297
  OpBatchMessageSchema,
3145
3298
  SyncInitMessageSchema,
@@ -3147,13 +3300,10 @@ var MessageSchema = import_zod10.z.discriminatedUnion("type", [
3147
3300
  SyncRespBucketsMessageSchema,
3148
3301
  SyncRespLeafMessageSchema,
3149
3302
  MerkleReqBucketMessageSchema,
3150
- LockRequestSchema,
3151
- LockReleaseSchema,
3152
- TopicSubSchema,
3153
- TopicUnsubSchema,
3154
- TopicPubSchema,
3155
- PingMessageSchema,
3156
- PongMessageSchema,
3303
+ OpAckMessageSchema,
3304
+ OpRejectedMessageSchema,
3305
+ BatchMessageSchema,
3306
+ // --- ORMap Sync ---
3157
3307
  ORMapSyncInitSchema,
3158
3308
  ORMapSyncRespRootSchema,
3159
3309
  ORMapSyncRespBucketsSchema,
@@ -3162,18 +3312,54 @@ var MessageSchema = import_zod10.z.discriminatedUnion("type", [
3162
3312
  ORMapDiffRequestSchema,
3163
3313
  ORMapDiffResponseSchema,
3164
3314
  ORMapPushDiffSchema,
3315
+ // --- Query ---
3316
+ QuerySubMessageSchema,
3317
+ QueryUnsubMessageSchema,
3318
+ QueryRespMessageSchema,
3319
+ QueryUpdateMessageSchema,
3320
+ // --- Search ---
3321
+ SearchMessageSchema,
3322
+ SearchRespMessageSchema,
3323
+ SearchSubMessageSchema,
3324
+ SearchUpdateMessageSchema,
3325
+ SearchUnsubMessageSchema,
3326
+ // --- Cluster ---
3165
3327
  PartitionMapRequestSchema,
3328
+ PartitionMapMessageSchema,
3329
+ ClusterSubRegisterMessageSchema,
3330
+ ClusterSubAckMessageSchema,
3331
+ ClusterSubUpdateMessageSchema,
3332
+ ClusterSubUnregisterMessageSchema,
3333
+ ClusterSearchReqMessageSchema,
3334
+ ClusterSearchRespMessageSchema,
3335
+ ClusterSearchSubscribeMessageSchema,
3336
+ ClusterSearchUnsubscribeMessageSchema,
3337
+ ClusterSearchUpdateMessageSchema,
3338
+ // --- Messaging ---
3339
+ TopicSubSchema,
3340
+ TopicUnsubSchema,
3341
+ TopicPubSchema,
3342
+ TopicMessageEventSchema,
3343
+ LockRequestSchema,
3344
+ LockReleaseSchema,
3166
3345
  CounterRequestSchema,
3167
3346
  CounterSyncSchema,
3347
+ CounterResponseSchema,
3348
+ CounterUpdateSchema,
3349
+ PingMessageSchema,
3350
+ PongMessageSchema,
3351
+ // --- Entry Processor ---
3168
3352
  EntryProcessRequestSchema,
3169
3353
  EntryProcessBatchRequestSchema,
3170
3354
  EntryProcessResponseSchema,
3171
3355
  EntryProcessBatchResponseSchema,
3356
+ // --- Journal ---
3172
3357
  JournalSubscribeRequestSchema,
3173
3358
  JournalUnsubscribeRequestSchema,
3174
3359
  JournalEventMessageSchema,
3175
3360
  JournalReadRequestSchema,
3176
3361
  JournalReadResponseSchema,
3362
+ // --- Conflict Resolver ---
3177
3363
  RegisterResolverRequestSchema,
3178
3364
  RegisterResolverResponseSchema,
3179
3365
  UnregisterResolverRequestSchema,
@@ -3181,15 +3367,16 @@ var MessageSchema = import_zod10.z.discriminatedUnion("type", [
3181
3367
  MergeRejectedMessageSchema,
3182
3368
  ListResolversRequestSchema,
3183
3369
  ListResolversResponseSchema,
3184
- SearchMessageSchema,
3185
- SearchRespMessageSchema,
3186
- SearchSubMessageSchema,
3187
- SearchUpdateMessageSchema,
3188
- SearchUnsubMessageSchema,
3189
- ClusterSubRegisterMessageSchema,
3190
- ClusterSubAckMessageSchema,
3191
- ClusterSubUpdateMessageSchema,
3192
- ClusterSubUnregisterMessageSchema
3370
+ // --- Server-to-Client ---
3371
+ ServerEventMessageSchema,
3372
+ ServerBatchEventMessageSchema,
3373
+ GcPruneMessageSchema,
3374
+ AuthAckMessageSchema,
3375
+ AuthFailMessageSchema,
3376
+ ErrorMessageSchema,
3377
+ LockGrantedMessageSchema,
3378
+ LockReleasedMessageSchema,
3379
+ SyncResetRequiredMessageSchema
3193
3380
  ]);
3194
3381
 
3195
3382
  // src/types/WriteConcern.ts
@@ -4149,6 +4336,18 @@ function createPredicateMatcher(getAttribute) {
4149
4336
  }
4150
4337
 
4151
4338
  // src/query/QueryTypes.ts
4339
+ var COST_WEIGHTS = {
4340
+ CPU: 1,
4341
+ NETWORK: 10,
4342
+ // Network is expensive (latency, bandwidth)
4343
+ IO: 5,
4344
+ // Disk I/O is moderately expensive
4345
+ ROWS: 1e-3
4346
+ // Row count factor
4347
+ };
4348
+ function calculateTotalCost(cost) {
4349
+ return cost.rows * COST_WEIGHTS.ROWS + cost.cpu * COST_WEIGHTS.CPU + cost.network * COST_WEIGHTS.NETWORK + cost.io * COST_WEIGHTS.IO;
4350
+ }
4152
4351
  function isSimpleQuery(query) {
4153
4352
  return [
4154
4353
  "eq",
@@ -6470,13 +6669,22 @@ var QueryOptimizer = class {
6470
6669
  * Optimize a query and return an execution plan.
6471
6670
  *
6472
6671
  * Optimization order (by cost):
6473
- * 1. StandingQueryIndex (cost: 10) - pre-computed results
6474
- * 2. Other indexes via optimizeNode
6672
+ * 1. Point lookup (cost: 1) - direct primary key access
6673
+ * 2. StandingQueryIndex (cost: 10) - pre-computed results
6674
+ * 3. Other indexes via optimizeNode
6475
6675
  *
6476
6676
  * @param query - Query to optimize
6477
6677
  * @returns Query execution plan
6478
6678
  */
6479
6679
  optimize(query) {
6680
+ const pointLookupStep = this.tryPointLookup(query);
6681
+ if (pointLookupStep) {
6682
+ return {
6683
+ root: pointLookupStep,
6684
+ estimatedCost: this.estimateCost(pointLookupStep),
6685
+ usesIndexes: this.usesIndexes(pointLookupStep)
6686
+ };
6687
+ }
6480
6688
  if (this.standingQueryRegistry) {
6481
6689
  const standingIndex = this.standingQueryRegistry.getIndex(query);
6482
6690
  if (standingIndex) {
@@ -6500,16 +6708,68 @@ var QueryOptimizer = class {
6500
6708
  };
6501
6709
  }
6502
6710
  /**
6503
- * Optimize a query with sort/limit/offset options.
6711
+ * Optimize a query with sort/limit/offset options and index hints.
6712
+ *
6713
+ * Hint precedence: disableOptimization > useIndex > forceIndexScan.
6504
6714
  *
6505
6715
  * @param query - Query to optimize
6506
- * @param options - Query options (sort, limit, offset)
6716
+ * @param options - Query options (sort, limit, cursor, hints)
6507
6717
  * @returns Query execution plan with options
6508
6718
  */
6509
6719
  optimizeWithOptions(query, options) {
6720
+ if (options.disableOptimization) {
6721
+ return {
6722
+ root: { type: "full-scan", predicate: query },
6723
+ estimatedCost: Number.MAX_SAFE_INTEGER,
6724
+ usesIndexes: false
6725
+ };
6726
+ }
6727
+ if (options.useIndex) {
6728
+ const indexes = this.indexRegistry.getIndexes(options.useIndex);
6729
+ if (indexes.length === 0) {
6730
+ throw new Error(
6731
+ `Index hint: no index found for attribute "${options.useIndex}"`
6732
+ );
6733
+ }
6734
+ let best = indexes[0];
6735
+ for (let i = 1; i < indexes.length; i++) {
6736
+ if (indexes[i].getRetrievalCost() < best.getRetrievalCost()) {
6737
+ best = indexes[i];
6738
+ }
6739
+ }
6740
+ const step = {
6741
+ type: "index-scan",
6742
+ index: best,
6743
+ query: this.buildHintedIndexQuery(query, options.useIndex)
6744
+ };
6745
+ return this.applyPlanOptions(
6746
+ {
6747
+ root: step,
6748
+ estimatedCost: best.getRetrievalCost(),
6749
+ usesIndexes: true,
6750
+ hint: options.useIndex
6751
+ },
6752
+ options
6753
+ );
6754
+ }
6510
6755
  const basePlan = this.optimize(query);
6756
+ if (options.forceIndexScan && !basePlan.usesIndexes) {
6757
+ throw new Error(
6758
+ "No suitable index found and forceIndexScan is enabled"
6759
+ );
6760
+ }
6761
+ return this.applyPlanOptions(basePlan, options);
6762
+ }
6763
+ /**
6764
+ * Apply sort/limit/cursor options to a query plan.
6765
+ *
6766
+ * @param plan - Base query plan
6767
+ * @param options - Query options with sort/limit/cursor
6768
+ * @returns Plan with options applied
6769
+ */
6770
+ applyPlanOptions(plan, options) {
6511
6771
  if (!options.sort && options.limit === void 0 && options.cursor === void 0) {
6512
- return basePlan;
6772
+ return plan;
6513
6773
  }
6514
6774
  let indexedSort = false;
6515
6775
  let sortField;
@@ -6526,14 +6786,73 @@ var QueryOptimizer = class {
6526
6786
  }
6527
6787
  }
6528
6788
  return {
6529
- ...basePlan,
6789
+ ...plan,
6530
6790
  indexedSort,
6531
6791
  sort: sortField && sortDirection ? { field: sortField, direction: sortDirection } : void 0,
6532
6792
  limit: options.limit,
6533
6793
  cursor: options.cursor
6534
- // replaces offset
6535
6794
  };
6536
6795
  }
6796
+ /**
6797
+ * Extract the relevant index query for a hinted attribute from the query tree.
6798
+ * If the query directly references the attribute, build an index query from it.
6799
+ * For compound (logical) queries, extract the matching child predicate.
6800
+ * Falls back to { type: 'has' } when no matching predicate is found,
6801
+ * retrieving all entries from the index for post-filtering.
6802
+ * FTS query nodes also fall back to { type: 'has' } since full-text search
6803
+ * queries are not compatible with regular index lookups.
6804
+ *
6805
+ * @param query - Original query tree
6806
+ * @param attributeName - Hinted attribute name
6807
+ * @returns Index query for the hinted attribute
6808
+ */
6809
+ buildHintedIndexQuery(query, attributeName) {
6810
+ if (isSimpleQuery(query) && query.attribute === attributeName) {
6811
+ return this.buildIndexQuery(query);
6812
+ }
6813
+ if (isFTSQuery(query)) {
6814
+ return { type: "has" };
6815
+ }
6816
+ if (isLogicalQuery(query) && query.children) {
6817
+ for (const child of query.children) {
6818
+ if (isSimpleQuery(child) && child.attribute === attributeName) {
6819
+ return this.buildIndexQuery(child);
6820
+ }
6821
+ }
6822
+ }
6823
+ return { type: "has" };
6824
+ }
6825
+ /**
6826
+ * Try to optimize query as a point lookup.
6827
+ * Returns a point lookup step if query is an equality or IN query on primary key.
6828
+ *
6829
+ * @param query - Query to check
6830
+ * @returns Point lookup step or null
6831
+ */
6832
+ tryPointLookup(query) {
6833
+ if (!isSimpleQuery(query)) {
6834
+ return null;
6835
+ }
6836
+ const primaryKeyFields = ["_key", "key", "id"];
6837
+ if (!primaryKeyFields.includes(query.attribute)) {
6838
+ return null;
6839
+ }
6840
+ if (query.type === "eq") {
6841
+ return {
6842
+ type: "point-lookup",
6843
+ key: query.value,
6844
+ cost: 1
6845
+ };
6846
+ }
6847
+ if (query.type === "in" && query.values) {
6848
+ return {
6849
+ type: "multi-point-lookup",
6850
+ keys: query.values,
6851
+ cost: query.values.length
6852
+ };
6853
+ }
6854
+ return null;
6855
+ }
6537
6856
  /**
6538
6857
  * Optimize a single query node.
6539
6858
  */
@@ -6922,6 +7241,9 @@ var QueryOptimizer = class {
6922
7241
  */
6923
7242
  estimateCost(step) {
6924
7243
  switch (step.type) {
7244
+ case "point-lookup":
7245
+ case "multi-point-lookup":
7246
+ return step.cost;
6925
7247
  case "index-scan":
6926
7248
  return step.index.getRetrievalCost();
6927
7249
  case "full-scan":
@@ -6956,11 +7278,89 @@ var QueryOptimizer = class {
6956
7278
  return Number.MAX_SAFE_INTEGER;
6957
7279
  }
6958
7280
  }
7281
+ /**
7282
+ * Estimate distributed cost including network overhead.
7283
+ *
7284
+ * Network cost is assigned based on step type:
7285
+ * - full-scan: broadcast to all nodes (highest cost)
7286
+ * - index-scan: 0 if local partition, 5 if remote
7287
+ * - point-lookup: 0 if local key, 5 if remote
7288
+ * - intersection/union: aggregating results from multiple sources
7289
+ *
7290
+ * @param step - Plan step to estimate
7291
+ * @param context - Distributed query context (optional)
7292
+ * @returns Distributed cost breakdown
7293
+ */
7294
+ estimateDistributedCost(step, context) {
7295
+ const baseCost = this.estimateCost(step);
7296
+ if (!context?.isDistributed || context.nodeCount <= 1) {
7297
+ return {
7298
+ rows: baseCost,
7299
+ cpu: baseCost,
7300
+ network: 0,
7301
+ io: 0
7302
+ };
7303
+ }
7304
+ let networkCost = 0;
7305
+ switch (step.type) {
7306
+ case "full-scan":
7307
+ networkCost = context.nodeCount * 10;
7308
+ break;
7309
+ case "index-scan":
7310
+ networkCost = 5;
7311
+ break;
7312
+ case "point-lookup":
7313
+ networkCost = 5;
7314
+ break;
7315
+ case "multi-point-lookup":
7316
+ networkCost = Math.min(step.keys.length, context.nodeCount) * 5;
7317
+ break;
7318
+ case "intersection":
7319
+ case "union":
7320
+ networkCost = step.steps.length * 5;
7321
+ break;
7322
+ case "filter":
7323
+ return this.estimateDistributedCost(step.source, context);
7324
+ case "not":
7325
+ networkCost = context.nodeCount * 5;
7326
+ break;
7327
+ case "fts-scan":
7328
+ networkCost = Math.ceil(context.nodeCount / 2) * 5;
7329
+ break;
7330
+ case "fusion":
7331
+ networkCost = step.steps.reduce(
7332
+ (sum, s) => sum + this.estimateDistributedCost(s, context).network,
7333
+ 0
7334
+ );
7335
+ break;
7336
+ }
7337
+ return {
7338
+ rows: baseCost,
7339
+ cpu: baseCost,
7340
+ network: networkCost,
7341
+ io: context.usesStorage ? baseCost * 0.5 : 0
7342
+ };
7343
+ }
7344
+ /**
7345
+ * Get total distributed cost for a plan step.
7346
+ * Convenience method combining estimateDistributedCost and calculateTotalCost.
7347
+ *
7348
+ * @param step - Plan step to estimate
7349
+ * @param context - Distributed query context (optional)
7350
+ * @returns Weighted total cost
7351
+ */
7352
+ getTotalDistributedCost(step, context) {
7353
+ const distributedCost = this.estimateDistributedCost(step, context);
7354
+ return calculateTotalCost(distributedCost);
7355
+ }
6959
7356
  /**
6960
7357
  * Check if a plan step uses any indexes.
6961
7358
  */
6962
7359
  usesIndexes(step) {
6963
7360
  switch (step.type) {
7361
+ case "point-lookup":
7362
+ case "multi-point-lookup":
7363
+ return true;
6964
7364
  case "index-scan":
6965
7365
  return true;
6966
7366
  case "full-scan":
@@ -9141,6 +9541,24 @@ var IndexedLWWMap = class extends LWWMap {
9141
9541
  */
9142
9542
  executePlan(step) {
9143
9543
  switch (step.type) {
9544
+ case "point-lookup": {
9545
+ const key = step.key;
9546
+ const result = /* @__PURE__ */ new Set();
9547
+ if (this.get(key) !== void 0) {
9548
+ result.add(key);
9549
+ }
9550
+ return new SetResultSet(result, 1);
9551
+ }
9552
+ case "multi-point-lookup": {
9553
+ const result = /* @__PURE__ */ new Set();
9554
+ for (const key of step.keys) {
9555
+ const k = key;
9556
+ if (this.get(k) !== void 0) {
9557
+ result.add(k);
9558
+ }
9559
+ }
9560
+ return new SetResultSet(result, step.keys.length);
9561
+ }
9144
9562
  case "index-scan":
9145
9563
  return step.index.retrieve(step.query);
9146
9564
  case "full-scan": {
@@ -12199,15 +12617,518 @@ function getSearchDebugger() {
12199
12617
  function resetSearchDebugger() {
12200
12618
  globalSearchDebugger = null;
12201
12619
  }
12620
+
12621
+ // src/testing/VirtualClock.ts
12622
+ var RealClock = {
12623
+ now: () => Date.now()
12624
+ };
12625
+ var VirtualClock = class {
12626
+ /**
12627
+ * @param initialTime Starting timestamp in milliseconds (default: 0)
12628
+ */
12629
+ constructor(initialTime = 0) {
12630
+ if (!Number.isFinite(initialTime) || initialTime < 0) {
12631
+ throw new Error("Initial time must be a non-negative finite number");
12632
+ }
12633
+ this.currentTime = initialTime;
12634
+ }
12635
+ /**
12636
+ * Returns the current virtual time.
12637
+ * Time remains frozen until advance() or set() is called.
12638
+ */
12639
+ now() {
12640
+ return this.currentTime;
12641
+ }
12642
+ /**
12643
+ * Advances time forward by the specified milliseconds.
12644
+ * @param ms Milliseconds to advance (must be non-negative)
12645
+ */
12646
+ advance(ms) {
12647
+ if (!Number.isFinite(ms) || ms < 0) {
12648
+ throw new Error("Advance amount must be a non-negative finite number");
12649
+ }
12650
+ this.currentTime += ms;
12651
+ }
12652
+ /**
12653
+ * Sets the clock to a specific time.
12654
+ * Allows moving time forward or backward (useful for testing).
12655
+ * @param time Absolute timestamp in milliseconds
12656
+ */
12657
+ set(time) {
12658
+ if (!Number.isFinite(time) || time < 0) {
12659
+ throw new Error("Time must be a non-negative finite number");
12660
+ }
12661
+ this.currentTime = time;
12662
+ }
12663
+ /**
12664
+ * Resets the clock to zero.
12665
+ */
12666
+ reset() {
12667
+ this.currentTime = 0;
12668
+ }
12669
+ };
12670
+
12671
+ // src/testing/SeededRNG.ts
12672
+ var SeededRNG = class {
12673
+ /**
12674
+ * @param seed Integer seed value. Same seed = same sequence.
12675
+ */
12676
+ constructor(seed) {
12677
+ if (!Number.isInteger(seed)) {
12678
+ throw new Error("Seed must be an integer");
12679
+ }
12680
+ this.state = seed >>> 0;
12681
+ this.originalSeed = this.state;
12682
+ }
12683
+ /**
12684
+ * Returns the original seed used to construct this RNG.
12685
+ */
12686
+ getSeed() {
12687
+ return this.originalSeed;
12688
+ }
12689
+ /**
12690
+ * Generates the next random number in [0, 1).
12691
+ * Uses mulberry32 algorithm for deterministic, high-quality randomness.
12692
+ */
12693
+ random() {
12694
+ let t = this.state += 1831565813;
12695
+ t = Math.imul(t ^ t >>> 15, t | 1);
12696
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
12697
+ this.state = t;
12698
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
12699
+ }
12700
+ /**
12701
+ * Generates a random integer in [min, max] (inclusive).
12702
+ * @param min Minimum value (inclusive)
12703
+ * @param max Maximum value (inclusive)
12704
+ */
12705
+ randomInt(min, max) {
12706
+ if (!Number.isInteger(min) || !Number.isInteger(max)) {
12707
+ throw new Error("Min and max must be integers");
12708
+ }
12709
+ if (min > max) {
12710
+ throw new Error("Min must be less than or equal to max");
12711
+ }
12712
+ const range = max - min + 1;
12713
+ return Math.floor(this.random() * range) + min;
12714
+ }
12715
+ /**
12716
+ * Generates a random boolean value.
12717
+ * @param probability Probability of returning true (default: 0.5)
12718
+ */
12719
+ randomBool(probability = 0.5) {
12720
+ if (probability < 0 || probability > 1) {
12721
+ throw new Error("Probability must be between 0 and 1");
12722
+ }
12723
+ return this.random() < probability;
12724
+ }
12725
+ /**
12726
+ * Shuffles an array in place using Fisher-Yates algorithm.
12727
+ * Returns the shuffled array.
12728
+ * @param array Array to shuffle
12729
+ */
12730
+ shuffle(array) {
12731
+ for (let i = array.length - 1; i > 0; i--) {
12732
+ const j = this.randomInt(0, i);
12733
+ [array[i], array[j]] = [array[j], array[i]];
12734
+ }
12735
+ return array;
12736
+ }
12737
+ /**
12738
+ * Picks a random element from an array.
12739
+ * @param array Array to pick from
12740
+ * @returns Random element, or undefined if array is empty
12741
+ */
12742
+ pick(array) {
12743
+ if (array.length === 0) return void 0;
12744
+ return array[this.randomInt(0, array.length - 1)];
12745
+ }
12746
+ /**
12747
+ * Resets the RNG to its original seed.
12748
+ * Useful for reproducing a sequence from the start.
12749
+ */
12750
+ reset() {
12751
+ this.state = this.originalSeed;
12752
+ }
12753
+ };
12754
+
12755
+ // src/testing/VirtualNetwork.ts
12756
+ var VirtualNetwork = class {
12757
+ constructor(rng, clock) {
12758
+ this.pendingMessages = [];
12759
+ this.rng = rng;
12760
+ this.clock = clock;
12761
+ this.config = {
12762
+ latencyMs: { min: 0, max: 0 },
12763
+ packetLossRate: 0,
12764
+ partitions: []
12765
+ };
12766
+ }
12767
+ /**
12768
+ * Updates network configuration.
12769
+ * Partially updates existing config with provided values.
12770
+ */
12771
+ configure(config) {
12772
+ if (config.latencyMs !== void 0) {
12773
+ const { min, max } = config.latencyMs;
12774
+ if (min < 0 || max < 0 || min > max) {
12775
+ throw new Error("Invalid latency range");
12776
+ }
12777
+ this.config.latencyMs = config.latencyMs;
12778
+ }
12779
+ if (config.packetLossRate !== void 0) {
12780
+ if (config.packetLossRate < 0 || config.packetLossRate > 1) {
12781
+ throw new Error("Packet loss rate must be between 0 and 1");
12782
+ }
12783
+ this.config.packetLossRate = config.packetLossRate;
12784
+ }
12785
+ if (config.partitions !== void 0) {
12786
+ this.config.partitions = config.partitions;
12787
+ }
12788
+ }
12789
+ /**
12790
+ * Sends a message through the network.
12791
+ * Subject to packet loss, latency, and partition rules.
12792
+ */
12793
+ send(from, to, payload) {
12794
+ if (this.rng.random() < this.config.packetLossRate) {
12795
+ return;
12796
+ }
12797
+ if (this.isPartitioned(from, to)) {
12798
+ return;
12799
+ }
12800
+ const latency = this.rng.randomInt(
12801
+ this.config.latencyMs.min,
12802
+ this.config.latencyMs.max
12803
+ );
12804
+ const scheduledTime = this.clock.now() + latency;
12805
+ this.pendingMessages.push({
12806
+ from,
12807
+ to,
12808
+ payload,
12809
+ scheduledTime
12810
+ });
12811
+ }
12812
+ /**
12813
+ * Creates a network partition between two groups.
12814
+ * Nodes in groupA cannot communicate with nodes in groupB.
12815
+ */
12816
+ partition(groupA, groupB) {
12817
+ this.config.partitions.push(groupA, groupB);
12818
+ }
12819
+ /**
12820
+ * Removes all network partitions.
12821
+ */
12822
+ heal() {
12823
+ this.config.partitions = [];
12824
+ }
12825
+ /**
12826
+ * Delivers all messages scheduled at or before the current time.
12827
+ * @returns Array of delivered messages
12828
+ */
12829
+ tick() {
12830
+ const currentTime = this.clock.now();
12831
+ const delivered = [];
12832
+ const remaining = [];
12833
+ for (const msg of this.pendingMessages) {
12834
+ if (msg.scheduledTime <= currentTime) {
12835
+ delivered.push(msg);
12836
+ } else {
12837
+ remaining.push(msg);
12838
+ }
12839
+ }
12840
+ this.pendingMessages = remaining;
12841
+ return delivered;
12842
+ }
12843
+ /**
12844
+ * Returns the number of messages currently in flight.
12845
+ */
12846
+ getPendingCount() {
12847
+ return this.pendingMessages.length;
12848
+ }
12849
+ /**
12850
+ * Clears all pending messages.
12851
+ */
12852
+ clear() {
12853
+ this.pendingMessages = [];
12854
+ }
12855
+ /**
12856
+ * Checks if two nodes are partitioned from each other.
12857
+ */
12858
+ isPartitioned(from, to) {
12859
+ for (let i = 0; i < this.config.partitions.length; i += 2) {
12860
+ const groupA = this.config.partitions[i];
12861
+ const groupB = this.config.partitions[i + 1];
12862
+ if (groupA.includes(from) && groupB.includes(to) || groupB.includes(from) && groupA.includes(to)) {
12863
+ return true;
12864
+ }
12865
+ }
12866
+ return false;
12867
+ }
12868
+ /**
12869
+ * Returns all pending messages (useful for debugging).
12870
+ */
12871
+ getPendingMessages() {
12872
+ return [...this.pendingMessages];
12873
+ }
12874
+ };
12875
+
12876
+ // src/testing/InvariantChecker.ts
12877
+ var InvariantChecker = class {
12878
+ constructor() {
12879
+ this.invariants = /* @__PURE__ */ new Map();
12880
+ }
12881
+ /**
12882
+ * Adds an invariant to be checked.
12883
+ * @param name Unique name for this invariant
12884
+ * @param check Function that returns true if invariant holds
12885
+ */
12886
+ addInvariant(name, check) {
12887
+ if (this.invariants.has(name)) {
12888
+ throw new Error(`Invariant '${name}' already exists`);
12889
+ }
12890
+ this.invariants.set(name, check);
12891
+ }
12892
+ /**
12893
+ * Removes an invariant by name.
12894
+ */
12895
+ removeInvariant(name) {
12896
+ return this.invariants.delete(name);
12897
+ }
12898
+ /**
12899
+ * Verifies all invariants against the provided state.
12900
+ * @returns Result with pass/fail status and list of failed invariants
12901
+ */
12902
+ verify(state) {
12903
+ const failures = [];
12904
+ for (const [name, check] of this.invariants.entries()) {
12905
+ try {
12906
+ if (!check(state)) {
12907
+ failures.push(name);
12908
+ }
12909
+ } catch (error) {
12910
+ failures.push(`${name} (exception: ${error instanceof Error ? error.message : String(error)})`);
12911
+ }
12912
+ }
12913
+ return {
12914
+ passed: failures.length === 0,
12915
+ failures
12916
+ };
12917
+ }
12918
+ /**
12919
+ * Returns the number of registered invariants.
12920
+ */
12921
+ get count() {
12922
+ return this.invariants.size;
12923
+ }
12924
+ /**
12925
+ * Clears all invariants.
12926
+ */
12927
+ clear() {
12928
+ this.invariants.clear();
12929
+ }
12930
+ };
12931
+ var CRDTInvariants = {
12932
+ /**
12933
+ * Verifies LWW-Map convergence: all maps contain the same values for same keys.
12934
+ */
12935
+ lwwConvergence: (maps) => {
12936
+ if (maps.length < 2) return true;
12937
+ const reference = maps[0];
12938
+ const refKeys = new Set(reference.allKeys());
12939
+ for (let i = 1; i < maps.length; i++) {
12940
+ const other = maps[i];
12941
+ const otherKeys = new Set(other.allKeys());
12942
+ if (refKeys.size !== otherKeys.size) return false;
12943
+ for (const key of refKeys) {
12944
+ if (!otherKeys.has(key)) return false;
12945
+ }
12946
+ for (const key of refKeys) {
12947
+ const refRecord = reference.getRecord(key);
12948
+ const otherRecord = other.getRecord(key);
12949
+ if (!refRecord || !otherRecord) {
12950
+ if (refRecord !== otherRecord) return false;
12951
+ continue;
12952
+ }
12953
+ if (refRecord.value !== otherRecord.value) return false;
12954
+ if (HLC.compare(refRecord.timestamp, otherRecord.timestamp) !== 0) {
12955
+ return false;
12956
+ }
12957
+ }
12958
+ }
12959
+ return true;
12960
+ },
12961
+ /**
12962
+ * Verifies OR-Map convergence: all maps contain the same values for same keys.
12963
+ */
12964
+ orMapConvergence: (maps) => {
12965
+ if (maps.length < 2) return true;
12966
+ const reference = maps[0];
12967
+ const refKeys = reference.allKeys();
12968
+ for (let i = 1; i < maps.length; i++) {
12969
+ const other = maps[i];
12970
+ const otherKeys = new Set(other.allKeys());
12971
+ if (refKeys.length !== otherKeys.size) return false;
12972
+ for (const key of refKeys) {
12973
+ if (!otherKeys.has(key)) return false;
12974
+ }
12975
+ for (const key of refKeys) {
12976
+ const refRecords = reference.getRecords(key);
12977
+ const otherRecords = other.getRecords(key);
12978
+ if (refRecords.length !== otherRecords.length) return false;
12979
+ const refSorted = [...refRecords].sort((a, b) => a.tag.localeCompare(b.tag));
12980
+ const otherSorted = [...otherRecords].sort((a, b) => a.tag.localeCompare(b.tag));
12981
+ for (let j = 0; j < refSorted.length; j++) {
12982
+ if (refSorted[j].tag !== otherSorted[j].tag) return false;
12983
+ if (refSorted[j].value !== otherSorted[j].value) return false;
12984
+ if (HLC.compare(refSorted[j].timestamp, otherSorted[j].timestamp) !== 0) {
12985
+ return false;
12986
+ }
12987
+ }
12988
+ }
12989
+ const refTombstones = new Set(reference.getTombstones());
12990
+ const otherTombstones = new Set(other.getTombstones());
12991
+ if (refTombstones.size !== otherTombstones.size) return false;
12992
+ for (const tag of refTombstones) {
12993
+ if (!otherTombstones.has(tag)) return false;
12994
+ }
12995
+ }
12996
+ return true;
12997
+ },
12998
+ /**
12999
+ * Verifies HLC monotonicity: timestamps are strictly increasing.
13000
+ */
13001
+ hlcMonotonicity: (timestamps) => {
13002
+ for (let i = 1; i < timestamps.length; i++) {
13003
+ if (HLC.compare(timestamps[i - 1], timestamps[i]) >= 0) {
13004
+ return false;
13005
+ }
13006
+ }
13007
+ return true;
13008
+ },
13009
+ /**
13010
+ * Verifies Merkle tree consistency: trees with same data have same root hash.
13011
+ */
13012
+ merkleConsistency: (trees) => {
13013
+ if (trees.length < 2) return true;
13014
+ const referenceHash = trees[0].getRootHash();
13015
+ for (let i = 1; i < trees.length; i++) {
13016
+ if (trees[i].getRootHash() !== referenceHash) {
13017
+ return false;
13018
+ }
13019
+ }
13020
+ return true;
13021
+ }
13022
+ };
13023
+
13024
+ // src/testing/ScenarioRunner.ts
13025
+ var ScenarioRunner = class {
13026
+ constructor(config) {
13027
+ if (!config.nodes || config.nodes.length === 0) {
13028
+ throw new Error("Scenario must have at least one node");
13029
+ }
13030
+ if (config.duration <= 0) {
13031
+ throw new Error("Duration must be positive");
13032
+ }
13033
+ this.seed = config.seed ?? Math.floor(Math.random() * 2147483647);
13034
+ this.config = {
13035
+ ...config,
13036
+ seed: this.seed,
13037
+ tickInterval: config.tickInterval ?? 1
13038
+ };
13039
+ this.clock = new VirtualClock(0);
13040
+ this.rng = new SeededRNG(this.seed);
13041
+ this.network = new VirtualNetwork(this.rng, this.clock);
13042
+ }
13043
+ /**
13044
+ * Returns the seed used for this scenario.
13045
+ */
13046
+ getSeed() {
13047
+ return this.seed;
13048
+ }
13049
+ /**
13050
+ * Returns the virtual clock instance.
13051
+ */
13052
+ getClock() {
13053
+ return this.clock;
13054
+ }
13055
+ /**
13056
+ * Returns the seeded RNG instance.
13057
+ */
13058
+ getRNG() {
13059
+ return this.rng;
13060
+ }
13061
+ /**
13062
+ * Returns the virtual network instance.
13063
+ */
13064
+ getNetwork() {
13065
+ return this.network;
13066
+ }
13067
+ /**
13068
+ * Returns the list of nodes in this scenario.
13069
+ */
13070
+ getNodes() {
13071
+ return [...this.config.nodes];
13072
+ }
13073
+ /**
13074
+ * Executes the simulation scenario.
13075
+ *
13076
+ * @param setup Called once before simulation starts. Initialize state here.
13077
+ * @param step Called on each tick. Perform operations and message delivery.
13078
+ * @param invariants Checker for verifying correctness throughout execution.
13079
+ * @returns Result with pass/fail status and captured state
13080
+ */
13081
+ run(setup, step, invariants) {
13082
+ const finalStates = /* @__PURE__ */ new Map();
13083
+ const invariantFailures = [];
13084
+ setup(this);
13085
+ let tickCount = 0;
13086
+ const tickInterval = this.config.tickInterval;
13087
+ const endTime = this.config.duration;
13088
+ while (this.clock.now() < endTime) {
13089
+ this.clock.advance(tickInterval);
13090
+ tickCount++;
13091
+ step(this, tickCount);
13092
+ const delivered = this.network.tick();
13093
+ if (delivered.length > 0) {
13094
+ finalStates.set(`_tick_${tickCount}_delivered`, delivered.length);
13095
+ }
13096
+ }
13097
+ const result = invariants.verify(null);
13098
+ if (!result.passed) {
13099
+ invariantFailures.push(...result.failures);
13100
+ }
13101
+ return {
13102
+ seed: this.seed,
13103
+ passed: invariantFailures.length === 0,
13104
+ ticks: tickCount,
13105
+ invariantFailures,
13106
+ finalStates
13107
+ };
13108
+ }
13109
+ /**
13110
+ * Stores state for a node (useful for capturing final state).
13111
+ */
13112
+ setState(nodeId, state) {
13113
+ if (!this.config.nodes.includes(nodeId)) {
13114
+ throw new Error(`Unknown node: ${nodeId}`);
13115
+ }
13116
+ }
13117
+ };
12202
13118
  // Annotate the CommonJS export names for ESM import in node:
12203
13119
  0 && (module.exports = {
13120
+ AuthAckMessageSchema,
12204
13121
  AuthFailMessageSchema,
12205
13122
  AuthMessageSchema,
13123
+ AuthRequiredMessageSchema,
12206
13124
  BM25Scorer,
12207
13125
  BatchMessageSchema,
12208
13126
  BuiltInProcessors,
12209
13127
  BuiltInResolvers,
13128
+ COST_WEIGHTS,
12210
13129
  CRDTDebugger,
13130
+ CRDTInvariants,
13131
+ ChangeEventTypeSchema,
12211
13132
  ClientOpMessageSchema,
12212
13133
  ClientOpSchema,
12213
13134
  ClusterSearchReqMessageSchema,
@@ -12249,6 +13170,7 @@ function resetSearchDebugger() {
12249
13170
  DEFAULT_RESOLVER_RATE_LIMITS,
12250
13171
  DEFAULT_STOP_WORDS,
12251
13172
  DEFAULT_WRITE_CONCERN_TIMEOUT,
13173
+ DeltaRecordSchema,
12252
13174
  ENGLISH_STOPWORDS,
12253
13175
  EntryProcessBatchRequestSchema,
12254
13176
  EntryProcessBatchResponseSchema,
@@ -12257,6 +13179,7 @@ function resetSearchDebugger() {
12257
13179
  EntryProcessResponseSchema,
12258
13180
  EntryProcessorDefSchema,
12259
13181
  EntryProcessorSchema,
13182
+ ErrorMessageSchema,
12260
13183
  EventJournalImpl,
12261
13184
  FORBIDDEN_PATTERNS,
12262
13185
  FTSInvertedIndex,
@@ -12268,12 +13191,18 @@ function resetSearchDebugger() {
12268
13191
  GcPrunePayloadSchema,
12269
13192
  HLC,
12270
13193
  HashIndex,
12271
- HybridQueryDeltaPayloadSchema,
12272
- HybridQueryRespPayloadSchema,
13194
+ HttpQueryRequestSchema,
13195
+ HttpQueryResultSchema,
13196
+ HttpSearchRequestSchema,
13197
+ HttpSearchResultSchema,
13198
+ HttpSyncErrorSchema,
13199
+ HttpSyncRequestSchema,
13200
+ HttpSyncResponseSchema,
12273
13201
  IndexRegistry,
12274
13202
  IndexedLWWMap,
12275
13203
  IndexedORMap,
12276
13204
  IntersectionResultSet,
13205
+ InvariantChecker,
12277
13206
  InvertedIndex,
12278
13207
  JournalEventDataSchema,
12279
13208
  JournalEventMessageSchema,
@@ -12289,11 +13218,14 @@ function resetSearchDebugger() {
12289
13218
  ListResolversRequestSchema,
12290
13219
  ListResolversResponseSchema,
12291
13220
  LiveQueryManager,
13221
+ LockGrantedMessageSchema,
12292
13222
  LockGrantedPayloadSchema,
12293
13223
  LockReleaseSchema,
13224
+ LockReleasedMessageSchema,
12294
13225
  LockReleasedPayloadSchema,
12295
13226
  LockRequestSchema,
12296
13227
  LowercaseFilter,
13228
+ MapDeltaSchema,
12297
13229
  MaxLengthFilter,
12298
13230
  MergeRejectedMessageSchema,
12299
13231
  MerkleReqBucketMessageSchema,
@@ -12303,9 +13235,11 @@ function resetSearchDebugger() {
12303
13235
  MultiValueAttribute,
12304
13236
  NGramTokenizer,
12305
13237
  NavigableIndex,
13238
+ NodeInfoSchema,
12306
13239
  ORMap,
12307
13240
  ORMapDiffRequestSchema,
12308
13241
  ORMapDiffResponseSchema,
13242
+ ORMapEntrySchema,
12309
13243
  ORMapMerkleReqBucketSchema,
12310
13244
  ORMapMerkleTree,
12311
13245
  ORMapPushDiffSchema,
@@ -12321,6 +13255,9 @@ function resetSearchDebugger() {
12321
13255
  PARTITION_COUNT,
12322
13256
  PNCounterImpl,
12323
13257
  PNCounterStateObjectSchema,
13258
+ PartitionInfoSchema,
13259
+ PartitionMapMessageSchema,
13260
+ PartitionMapPayloadSchema,
12324
13261
  PartitionMapRequestSchema,
12325
13262
  PartitionState,
12326
13263
  PingMessageSchema,
@@ -12338,10 +13275,12 @@ function resetSearchDebugger() {
12338
13275
  QueryUpdateMessageSchema,
12339
13276
  QueryUpdatePayloadSchema,
12340
13277
  RESOLVER_FORBIDDEN_PATTERNS,
13278
+ RealClock,
12341
13279
  ReciprocalRankFusion,
12342
13280
  RegisterResolverRequestSchema,
12343
13281
  RegisterResolverResponseSchema,
12344
13282
  Ringbuffer,
13283
+ ScenarioRunner,
12345
13284
  SearchCursor,
12346
13285
  SearchDebugger,
12347
13286
  SearchMessageSchema,
@@ -12356,6 +13295,7 @@ function resetSearchDebugger() {
12356
13295
  SearchUpdateMessageSchema,
12357
13296
  SearchUpdatePayloadSchema,
12358
13297
  SearchUpdateTypeSchema,
13298
+ SeededRNG,
12359
13299
  ServerBatchEventMessageSchema,
12360
13300
  ServerEventMessageSchema,
12361
13301
  ServerEventPayloadSchema,
@@ -12367,6 +13307,8 @@ function resetSearchDebugger() {
12367
13307
  StandingQueryRegistry,
12368
13308
  StopWordFilter,
12369
13309
  SyncInitMessageSchema,
13310
+ SyncMapEntrySchema,
13311
+ SyncResetRequiredMessageSchema,
12370
13312
  SyncResetRequiredPayloadSchema,
12371
13313
  SyncRespBucketsMessageSchema,
12372
13314
  SyncRespLeafMessageSchema,
@@ -12382,11 +13324,14 @@ function resetSearchDebugger() {
12382
13324
  UniqueFilter,
12383
13325
  UnregisterResolverRequestSchema,
12384
13326
  UnregisterResolverResponseSchema,
13327
+ VirtualClock,
13328
+ VirtualNetwork,
12385
13329
  WRITE_CONCERN_ORDER,
12386
13330
  WhitespaceTokenizer,
12387
13331
  WordBoundaryTokenizer,
12388
13332
  WriteConcern,
12389
13333
  WriteConcernSchema,
13334
+ calculateTotalCost,
12390
13335
  combineHashes,
12391
13336
  compareHLCTimestamps,
12392
13337
  compareTimestamps,
@@ -12396,7 +13341,6 @@ function resetSearchDebugger() {
12396
13341
  decodeBase64Url,
12397
13342
  deepMerge,
12398
13343
  deserialize,
12399
- disableNativeHash,
12400
13344
  encodeBase64Url,
12401
13345
  evaluatePredicate,
12402
13346
  getCRDTDebugger,
@@ -12408,13 +13352,11 @@ function resetSearchDebugger() {
12408
13352
  hashString,
12409
13353
  isLogicalQuery,
12410
13354
  isSimpleQuery,
12411
- isUsingNativeHash,
12412
13355
  isWriteConcernAchieved,
12413
13356
  logger,
12414
13357
  multiAttribute,
12415
13358
  porterStem,
12416
13359
  resetCRDTDebugger,
12417
- resetNativeHash,
12418
13360
  resetSearchDebugger,
12419
13361
  serialize,
12420
13362
  simpleAttribute,