@topgunbuild/server 0.4.0 → 0.6.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.d.mts +186 -2
- package/dist/index.d.ts +186 -2
- package/dist/index.js +508 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +511 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
ConnectionRateLimiter: () => ConnectionRateLimiter,
|
|
39
39
|
DEFAULT_CLUSTER_COORDINATOR_CONFIG: () => DEFAULT_CLUSTER_COORDINATOR_CONFIG,
|
|
40
40
|
DEFAULT_CONFLICT_RESOLVER_CONFIG: () => DEFAULT_CONFLICT_RESOLVER_CONFIG,
|
|
41
|
+
DEFAULT_INDEX_CONFIG: () => DEFAULT_INDEX_CONFIG,
|
|
41
42
|
DEFAULT_JOURNAL_SERVICE_CONFIG: () => DEFAULT_JOURNAL_SERVICE_CONFIG,
|
|
42
43
|
DEFAULT_LAG_TRACKER_CONFIG: () => DEFAULT_LAG_TRACKER_CONFIG,
|
|
43
44
|
DEFAULT_SANDBOX_CONFIG: () => DEFAULT_SANDBOX_CONFIG,
|
|
@@ -48,6 +49,7 @@ __export(index_exports, {
|
|
|
48
49
|
IteratorTasklet: () => IteratorTasklet,
|
|
49
50
|
LagTracker: () => LagTracker,
|
|
50
51
|
LockManager: () => LockManager,
|
|
52
|
+
MapFactory: () => MapFactory,
|
|
51
53
|
MapTasklet: () => MapTasklet,
|
|
52
54
|
MapWithResolver: () => MapWithResolver,
|
|
53
55
|
MemoryServerAdapter: () => MemoryServerAdapter,
|
|
@@ -78,11 +80,13 @@ __export(index_exports, {
|
|
|
78
80
|
getNativeStats: () => getNativeStats,
|
|
79
81
|
logNativeStatus: () => logNativeStatus,
|
|
80
82
|
logger: () => logger,
|
|
83
|
+
mergeWithDefaults: () => mergeWithDefaults,
|
|
81
84
|
setGlobalBufferPool: () => setGlobalBufferPool,
|
|
82
85
|
setGlobalEventPayloadPool: () => setGlobalEventPayloadPool,
|
|
83
86
|
setGlobalMessagePool: () => setGlobalMessagePool,
|
|
84
87
|
setGlobalRecordPool: () => setGlobalRecordPool,
|
|
85
|
-
setGlobalTimestampPool: () => setGlobalTimestampPool
|
|
88
|
+
setGlobalTimestampPool: () => setGlobalTimestampPool,
|
|
89
|
+
validateIndexConfig: () => validateIndexConfig
|
|
86
90
|
});
|
|
87
91
|
module.exports = __toCommonJS(index_exports);
|
|
88
92
|
|
|
@@ -443,14 +447,70 @@ var QueryRegistry = class {
|
|
|
443
447
|
/**
|
|
444
448
|
* Processes a record change for all relevant subscriptions.
|
|
445
449
|
* Calculates diffs and sends updates.
|
|
450
|
+
*
|
|
451
|
+
* For IndexedLWWMap: Uses StandingQueryRegistry for O(1) affected query detection.
|
|
452
|
+
* For regular maps: Falls back to ReverseQueryIndex.
|
|
446
453
|
*/
|
|
447
454
|
processChange(mapName, map, changeKey, changeRecord, oldRecord) {
|
|
448
455
|
const index = this.indexes.get(mapName);
|
|
449
456
|
if (!index) return;
|
|
450
457
|
const newVal = this.extractValue(changeRecord);
|
|
451
458
|
const oldVal = this.extractValue(oldRecord);
|
|
459
|
+
if (map instanceof import_core2.IndexedLWWMap) {
|
|
460
|
+
this.processChangeWithStandingQuery(mapName, map, changeKey, newVal, oldVal);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
this.processChangeWithReverseIndex(mapName, map, changeKey, newVal, oldVal, index);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Process change using IndexedLWWMap's StandingQueryRegistry.
|
|
467
|
+
* O(1) detection of affected queries.
|
|
468
|
+
*/
|
|
469
|
+
processChangeWithStandingQuery(mapName, map, changeKey, newVal, oldVal) {
|
|
470
|
+
const subs = this.subscriptions.get(mapName);
|
|
471
|
+
if (!subs || subs.size === 0) return;
|
|
472
|
+
const subsByQueryId = /* @__PURE__ */ new Map();
|
|
473
|
+
for (const sub of subs) {
|
|
474
|
+
subsByQueryId.set(sub.id, sub);
|
|
475
|
+
}
|
|
476
|
+
const standingRegistry = map.getStandingQueryRegistry();
|
|
477
|
+
let changes;
|
|
478
|
+
if (oldVal === null || oldVal === void 0) {
|
|
479
|
+
if (newVal !== null && newVal !== void 0) {
|
|
480
|
+
changes = standingRegistry.onRecordAdded(changeKey, newVal);
|
|
481
|
+
} else {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
} else if (newVal === null || newVal === void 0) {
|
|
485
|
+
changes = standingRegistry.onRecordRemoved(changeKey, oldVal);
|
|
486
|
+
} else {
|
|
487
|
+
changes = standingRegistry.onRecordUpdated(changeKey, oldVal, newVal);
|
|
488
|
+
}
|
|
489
|
+
for (const sub of subs) {
|
|
490
|
+
const coreQuery = this.convertToCoreQuery(sub.query);
|
|
491
|
+
if (!coreQuery) {
|
|
492
|
+
this.processSubscriptionFallback(sub, map, changeKey, newVal);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const queryHash = this.hashCoreQuery(coreQuery);
|
|
496
|
+
const change = changes.get(queryHash);
|
|
497
|
+
if (change === "added") {
|
|
498
|
+
sub.previousResultKeys.add(changeKey);
|
|
499
|
+
this.sendUpdate(sub, changeKey, newVal, "UPDATE");
|
|
500
|
+
} else if (change === "removed") {
|
|
501
|
+
sub.previousResultKeys.delete(changeKey);
|
|
502
|
+
this.sendUpdate(sub, changeKey, null, "REMOVE");
|
|
503
|
+
} else if (change === "updated") {
|
|
504
|
+
this.sendUpdate(sub, changeKey, newVal, "UPDATE");
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Process change using legacy ReverseQueryIndex.
|
|
510
|
+
*/
|
|
511
|
+
processChangeWithReverseIndex(mapName, map, changeKey, newVal, oldVal, index) {
|
|
452
512
|
const changedFields = this.getChangedFields(oldVal, newVal);
|
|
453
|
-
if (changedFields !== "ALL" && changedFields.size === 0 &&
|
|
513
|
+
if (changedFields !== "ALL" && changedFields.size === 0 && oldVal && newVal) {
|
|
454
514
|
return;
|
|
455
515
|
}
|
|
456
516
|
const candidates = index.getCandidates(changedFields, oldVal, newVal);
|
|
@@ -492,6 +552,103 @@ var QueryRegistry = class {
|
|
|
492
552
|
sub.previousResultKeys = newResultKeys;
|
|
493
553
|
}
|
|
494
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* Fallback processing for subscriptions that can't use StandingQueryRegistry.
|
|
557
|
+
*/
|
|
558
|
+
processSubscriptionFallback(sub, map, changeKey, newVal) {
|
|
559
|
+
const dummyRecord = {
|
|
560
|
+
value: newVal,
|
|
561
|
+
timestamp: { millis: 0, counter: 0, nodeId: "" }
|
|
562
|
+
};
|
|
563
|
+
const isMatch = newVal !== null && matchesQuery(dummyRecord, sub.query);
|
|
564
|
+
const wasInResult = sub.previousResultKeys.has(changeKey);
|
|
565
|
+
if (isMatch && !wasInResult) {
|
|
566
|
+
sub.previousResultKeys.add(changeKey);
|
|
567
|
+
this.sendUpdate(sub, changeKey, newVal, "UPDATE");
|
|
568
|
+
} else if (!isMatch && wasInResult) {
|
|
569
|
+
sub.previousResultKeys.delete(changeKey);
|
|
570
|
+
this.sendUpdate(sub, changeKey, null, "REMOVE");
|
|
571
|
+
} else if (isMatch && wasInResult) {
|
|
572
|
+
this.sendUpdate(sub, changeKey, newVal, "UPDATE");
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Convert server Query format to core Query format.
|
|
577
|
+
*/
|
|
578
|
+
convertToCoreQuery(query) {
|
|
579
|
+
if (query.predicate) {
|
|
580
|
+
return this.predicateToCoreQuery(query.predicate);
|
|
581
|
+
}
|
|
582
|
+
if (query.where) {
|
|
583
|
+
const conditions = [];
|
|
584
|
+
for (const [attribute, condition] of Object.entries(query.where)) {
|
|
585
|
+
if (typeof condition !== "object" || condition === null) {
|
|
586
|
+
conditions.push({ type: "eq", attribute, value: condition });
|
|
587
|
+
} else {
|
|
588
|
+
for (const [op, value] of Object.entries(condition)) {
|
|
589
|
+
const coreOp = this.convertOperator(op);
|
|
590
|
+
if (coreOp) {
|
|
591
|
+
conditions.push({ type: coreOp, attribute, value });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (conditions.length === 0) return null;
|
|
597
|
+
if (conditions.length === 1) return conditions[0];
|
|
598
|
+
return { type: "and", children: conditions };
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
predicateToCoreQuery(predicate) {
|
|
603
|
+
if (!predicate || !predicate.op) return null;
|
|
604
|
+
switch (predicate.op) {
|
|
605
|
+
case "eq":
|
|
606
|
+
case "neq":
|
|
607
|
+
case "gt":
|
|
608
|
+
case "gte":
|
|
609
|
+
case "lt":
|
|
610
|
+
case "lte":
|
|
611
|
+
return {
|
|
612
|
+
type: predicate.op,
|
|
613
|
+
attribute: predicate.attribute,
|
|
614
|
+
value: predicate.value
|
|
615
|
+
};
|
|
616
|
+
case "and":
|
|
617
|
+
case "or":
|
|
618
|
+
if (predicate.children && Array.isArray(predicate.children)) {
|
|
619
|
+
const children = predicate.children.map((c) => this.predicateToCoreQuery(c)).filter((c) => c !== null);
|
|
620
|
+
if (children.length === 0) return null;
|
|
621
|
+
if (children.length === 1) return children[0];
|
|
622
|
+
return { type: predicate.op, children };
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
625
|
+
case "not":
|
|
626
|
+
if (predicate.children && predicate.children[0]) {
|
|
627
|
+
const child = this.predicateToCoreQuery(predicate.children[0]);
|
|
628
|
+
if (child) {
|
|
629
|
+
return { type: "not", child };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
default:
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
convertOperator(op) {
|
|
638
|
+
const mapping = {
|
|
639
|
+
"$eq": "eq",
|
|
640
|
+
"$ne": "neq",
|
|
641
|
+
"$neq": "neq",
|
|
642
|
+
"$gt": "gt",
|
|
643
|
+
"$gte": "gte",
|
|
644
|
+
"$lt": "lt",
|
|
645
|
+
"$lte": "lte"
|
|
646
|
+
};
|
|
647
|
+
return mapping[op] || null;
|
|
648
|
+
}
|
|
649
|
+
hashCoreQuery(query) {
|
|
650
|
+
return JSON.stringify(query);
|
|
651
|
+
}
|
|
495
652
|
extractValue(record) {
|
|
496
653
|
if (!record) return null;
|
|
497
654
|
if (Array.isArray(record)) {
|
|
@@ -9657,6 +9814,26 @@ var ServerCoordinator = class {
|
|
|
9657
9814
|
}
|
|
9658
9815
|
async executeLocalQuery(mapName, query) {
|
|
9659
9816
|
const map = await this.getMapAsync(mapName);
|
|
9817
|
+
const localQuery = { ...query };
|
|
9818
|
+
delete localQuery.offset;
|
|
9819
|
+
delete localQuery.limit;
|
|
9820
|
+
if (map instanceof import_core15.IndexedLWWMap) {
|
|
9821
|
+
const coreQuery = this.convertToCoreQuery(localQuery);
|
|
9822
|
+
if (coreQuery) {
|
|
9823
|
+
const entries = map.queryEntries(coreQuery);
|
|
9824
|
+
return entries.map(([key, value]) => {
|
|
9825
|
+
const record = map.getRecord(key);
|
|
9826
|
+
return { key, value, timestamp: record?.timestamp };
|
|
9827
|
+
});
|
|
9828
|
+
}
|
|
9829
|
+
}
|
|
9830
|
+
if (map instanceof import_core15.IndexedORMap) {
|
|
9831
|
+
const coreQuery = this.convertToCoreQuery(localQuery);
|
|
9832
|
+
if (coreQuery) {
|
|
9833
|
+
const results = map.query(coreQuery);
|
|
9834
|
+
return results.map(({ key, value }) => ({ key, value }));
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
9660
9837
|
const records = /* @__PURE__ */ new Map();
|
|
9661
9838
|
if (map instanceof import_core15.LWWMap) {
|
|
9662
9839
|
for (const key of map.allKeys()) {
|
|
@@ -9674,11 +9851,89 @@ var ServerCoordinator = class {
|
|
|
9674
9851
|
}
|
|
9675
9852
|
}
|
|
9676
9853
|
}
|
|
9677
|
-
const localQuery = { ...query };
|
|
9678
|
-
delete localQuery.offset;
|
|
9679
|
-
delete localQuery.limit;
|
|
9680
9854
|
return executeQuery(records, localQuery);
|
|
9681
9855
|
}
|
|
9856
|
+
/**
|
|
9857
|
+
* Convert server Query format to core Query format for indexed execution.
|
|
9858
|
+
* Returns null if conversion is not possible (complex queries).
|
|
9859
|
+
*/
|
|
9860
|
+
convertToCoreQuery(query) {
|
|
9861
|
+
if (query.predicate) {
|
|
9862
|
+
return this.predicateToCoreQuery(query.predicate);
|
|
9863
|
+
}
|
|
9864
|
+
if (query.where) {
|
|
9865
|
+
const conditions = [];
|
|
9866
|
+
for (const [attribute, condition] of Object.entries(query.where)) {
|
|
9867
|
+
if (typeof condition !== "object" || condition === null) {
|
|
9868
|
+
conditions.push({ type: "eq", attribute, value: condition });
|
|
9869
|
+
} else {
|
|
9870
|
+
for (const [op, value] of Object.entries(condition)) {
|
|
9871
|
+
const coreOp = this.convertOperator(op);
|
|
9872
|
+
if (coreOp) {
|
|
9873
|
+
conditions.push({ type: coreOp, attribute, value });
|
|
9874
|
+
}
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9878
|
+
if (conditions.length === 0) return null;
|
|
9879
|
+
if (conditions.length === 1) return conditions[0];
|
|
9880
|
+
return { type: "and", children: conditions };
|
|
9881
|
+
}
|
|
9882
|
+
return null;
|
|
9883
|
+
}
|
|
9884
|
+
/**
|
|
9885
|
+
* Convert predicate node to core Query format.
|
|
9886
|
+
*/
|
|
9887
|
+
predicateToCoreQuery(predicate) {
|
|
9888
|
+
if (!predicate || !predicate.op) return null;
|
|
9889
|
+
switch (predicate.op) {
|
|
9890
|
+
case "eq":
|
|
9891
|
+
case "neq":
|
|
9892
|
+
case "gt":
|
|
9893
|
+
case "gte":
|
|
9894
|
+
case "lt":
|
|
9895
|
+
case "lte":
|
|
9896
|
+
return {
|
|
9897
|
+
type: predicate.op,
|
|
9898
|
+
attribute: predicate.attribute,
|
|
9899
|
+
value: predicate.value
|
|
9900
|
+
};
|
|
9901
|
+
case "and":
|
|
9902
|
+
case "or":
|
|
9903
|
+
if (predicate.children && Array.isArray(predicate.children)) {
|
|
9904
|
+
const children = predicate.children.map((c) => this.predicateToCoreQuery(c)).filter((c) => c !== null);
|
|
9905
|
+
if (children.length === 0) return null;
|
|
9906
|
+
if (children.length === 1) return children[0];
|
|
9907
|
+
return { type: predicate.op, children };
|
|
9908
|
+
}
|
|
9909
|
+
return null;
|
|
9910
|
+
case "not":
|
|
9911
|
+
if (predicate.children && predicate.children[0]) {
|
|
9912
|
+
const child = this.predicateToCoreQuery(predicate.children[0]);
|
|
9913
|
+
if (child) {
|
|
9914
|
+
return { type: "not", child };
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
9917
|
+
return null;
|
|
9918
|
+
default:
|
|
9919
|
+
return null;
|
|
9920
|
+
}
|
|
9921
|
+
}
|
|
9922
|
+
/**
|
|
9923
|
+
* Convert server operator to core query type.
|
|
9924
|
+
*/
|
|
9925
|
+
convertOperator(op) {
|
|
9926
|
+
const mapping = {
|
|
9927
|
+
"$eq": "eq",
|
|
9928
|
+
"$ne": "neq",
|
|
9929
|
+
"$neq": "neq",
|
|
9930
|
+
"$gt": "gt",
|
|
9931
|
+
"$gte": "gte",
|
|
9932
|
+
"$lt": "lt",
|
|
9933
|
+
"$lte": "lte"
|
|
9934
|
+
};
|
|
9935
|
+
return mapping[op] || null;
|
|
9936
|
+
}
|
|
9682
9937
|
finalizeClusterQuery(requestId, timeout = false) {
|
|
9683
9938
|
const pending = this.pendingClusterQueries.get(requestId);
|
|
9684
9939
|
if (!pending) return;
|
|
@@ -11603,6 +11858,249 @@ var MapWithResolver = class {
|
|
|
11603
11858
|
return this.map.prune(olderThan);
|
|
11604
11859
|
}
|
|
11605
11860
|
};
|
|
11861
|
+
|
|
11862
|
+
// src/config/IndexConfig.ts
|
|
11863
|
+
var DEFAULT_INDEX_CONFIG = {
|
|
11864
|
+
autoIndex: false,
|
|
11865
|
+
maxAutoIndexesPerMap: 10,
|
|
11866
|
+
maps: [],
|
|
11867
|
+
logStats: false,
|
|
11868
|
+
statsLogInterval: 6e4
|
|
11869
|
+
};
|
|
11870
|
+
function validateIndexConfig(config) {
|
|
11871
|
+
const errors = [];
|
|
11872
|
+
if (config.maxAutoIndexesPerMap !== void 0) {
|
|
11873
|
+
if (typeof config.maxAutoIndexesPerMap !== "number" || config.maxAutoIndexesPerMap < 1) {
|
|
11874
|
+
errors.push("maxAutoIndexesPerMap must be a positive number");
|
|
11875
|
+
}
|
|
11876
|
+
}
|
|
11877
|
+
if (config.statsLogInterval !== void 0) {
|
|
11878
|
+
if (typeof config.statsLogInterval !== "number" || config.statsLogInterval < 1e3) {
|
|
11879
|
+
errors.push("statsLogInterval must be at least 1000ms");
|
|
11880
|
+
}
|
|
11881
|
+
}
|
|
11882
|
+
if (config.maps) {
|
|
11883
|
+
const mapNames = /* @__PURE__ */ new Set();
|
|
11884
|
+
for (const mapConfig of config.maps) {
|
|
11885
|
+
if (!mapConfig.mapName || typeof mapConfig.mapName !== "string") {
|
|
11886
|
+
errors.push("Each map config must have a valid mapName");
|
|
11887
|
+
continue;
|
|
11888
|
+
}
|
|
11889
|
+
if (mapNames.has(mapConfig.mapName)) {
|
|
11890
|
+
errors.push(`Duplicate map config for: ${mapConfig.mapName}`);
|
|
11891
|
+
}
|
|
11892
|
+
mapNames.add(mapConfig.mapName);
|
|
11893
|
+
if (!Array.isArray(mapConfig.indexes)) {
|
|
11894
|
+
errors.push(`Map ${mapConfig.mapName}: indexes must be an array`);
|
|
11895
|
+
continue;
|
|
11896
|
+
}
|
|
11897
|
+
const attrNames = /* @__PURE__ */ new Set();
|
|
11898
|
+
for (const indexDef of mapConfig.indexes) {
|
|
11899
|
+
if (!indexDef.attribute || typeof indexDef.attribute !== "string") {
|
|
11900
|
+
errors.push(`Map ${mapConfig.mapName}: index must have valid attribute`);
|
|
11901
|
+
continue;
|
|
11902
|
+
}
|
|
11903
|
+
if (!["hash", "navigable"].includes(indexDef.type)) {
|
|
11904
|
+
errors.push(
|
|
11905
|
+
`Map ${mapConfig.mapName}: index type must be 'hash' or 'navigable'`
|
|
11906
|
+
);
|
|
11907
|
+
}
|
|
11908
|
+
if (indexDef.comparator && !["number", "string", "date"].includes(indexDef.comparator)) {
|
|
11909
|
+
errors.push(
|
|
11910
|
+
`Map ${mapConfig.mapName}: comparator must be 'number', 'string', or 'date'`
|
|
11911
|
+
);
|
|
11912
|
+
}
|
|
11913
|
+
const key = `${indexDef.attribute}:${indexDef.type}`;
|
|
11914
|
+
if (attrNames.has(key)) {
|
|
11915
|
+
errors.push(
|
|
11916
|
+
`Map ${mapConfig.mapName}: duplicate ${indexDef.type} index on ${indexDef.attribute}`
|
|
11917
|
+
);
|
|
11918
|
+
}
|
|
11919
|
+
attrNames.add(key);
|
|
11920
|
+
}
|
|
11921
|
+
}
|
|
11922
|
+
}
|
|
11923
|
+
return errors;
|
|
11924
|
+
}
|
|
11925
|
+
function mergeWithDefaults(userConfig) {
|
|
11926
|
+
return {
|
|
11927
|
+
...DEFAULT_INDEX_CONFIG,
|
|
11928
|
+
...userConfig,
|
|
11929
|
+
maps: userConfig.maps ?? DEFAULT_INDEX_CONFIG.maps
|
|
11930
|
+
};
|
|
11931
|
+
}
|
|
11932
|
+
|
|
11933
|
+
// src/config/MapFactory.ts
|
|
11934
|
+
var import_core19 = require("@topgunbuild/core");
|
|
11935
|
+
var MapFactory = class {
|
|
11936
|
+
/**
|
|
11937
|
+
* Create a MapFactory.
|
|
11938
|
+
*
|
|
11939
|
+
* @param config - Server index configuration
|
|
11940
|
+
*/
|
|
11941
|
+
constructor(config) {
|
|
11942
|
+
this.config = mergeWithDefaults(config ?? {});
|
|
11943
|
+
this.mapConfigs = /* @__PURE__ */ new Map();
|
|
11944
|
+
for (const mapConfig of this.config.maps ?? []) {
|
|
11945
|
+
this.mapConfigs.set(mapConfig.mapName, mapConfig);
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
/**
|
|
11949
|
+
* Create an LWWMap or IndexedLWWMap based on configuration.
|
|
11950
|
+
*
|
|
11951
|
+
* @param mapName - Name of the map
|
|
11952
|
+
* @param hlc - Hybrid Logical Clock instance
|
|
11953
|
+
* @returns LWWMap or IndexedLWWMap depending on configuration
|
|
11954
|
+
*/
|
|
11955
|
+
createLWWMap(mapName, hlc) {
|
|
11956
|
+
const mapConfig = this.mapConfigs.get(mapName);
|
|
11957
|
+
if (!mapConfig || mapConfig.indexes.length === 0) {
|
|
11958
|
+
return new import_core19.LWWMap(hlc);
|
|
11959
|
+
}
|
|
11960
|
+
const map = new import_core19.IndexedLWWMap(hlc);
|
|
11961
|
+
for (const indexDef of mapConfig.indexes) {
|
|
11962
|
+
this.addIndexToLWWMap(map, indexDef);
|
|
11963
|
+
}
|
|
11964
|
+
return map;
|
|
11965
|
+
}
|
|
11966
|
+
/**
|
|
11967
|
+
* Create an ORMap or IndexedORMap based on configuration.
|
|
11968
|
+
*
|
|
11969
|
+
* @param mapName - Name of the map
|
|
11970
|
+
* @param hlc - Hybrid Logical Clock instance
|
|
11971
|
+
* @returns ORMap or IndexedORMap depending on configuration
|
|
11972
|
+
*/
|
|
11973
|
+
createORMap(mapName, hlc) {
|
|
11974
|
+
const mapConfig = this.mapConfigs.get(mapName);
|
|
11975
|
+
if (!mapConfig || mapConfig.indexes.length === 0) {
|
|
11976
|
+
return new import_core19.ORMap(hlc);
|
|
11977
|
+
}
|
|
11978
|
+
const map = new import_core19.IndexedORMap(hlc);
|
|
11979
|
+
for (const indexDef of mapConfig.indexes) {
|
|
11980
|
+
this.addIndexToORMap(map, indexDef);
|
|
11981
|
+
}
|
|
11982
|
+
return map;
|
|
11983
|
+
}
|
|
11984
|
+
/**
|
|
11985
|
+
* Add an index to an IndexedLWWMap based on definition.
|
|
11986
|
+
*/
|
|
11987
|
+
addIndexToLWWMap(map, indexDef) {
|
|
11988
|
+
const attribute = this.createAttribute(indexDef.attribute);
|
|
11989
|
+
if (indexDef.type === "hash") {
|
|
11990
|
+
map.addHashIndex(attribute);
|
|
11991
|
+
} else if (indexDef.type === "navigable") {
|
|
11992
|
+
const navAttribute = attribute;
|
|
11993
|
+
const comparator = this.createComparator(indexDef.comparator);
|
|
11994
|
+
map.addNavigableIndex(navAttribute, comparator);
|
|
11995
|
+
}
|
|
11996
|
+
}
|
|
11997
|
+
/**
|
|
11998
|
+
* Add an index to an IndexedORMap based on definition.
|
|
11999
|
+
*/
|
|
12000
|
+
addIndexToORMap(map, indexDef) {
|
|
12001
|
+
const attribute = this.createAttribute(indexDef.attribute);
|
|
12002
|
+
if (indexDef.type === "hash") {
|
|
12003
|
+
map.addHashIndex(attribute);
|
|
12004
|
+
} else if (indexDef.type === "navigable") {
|
|
12005
|
+
const navAttribute = attribute;
|
|
12006
|
+
const comparator = this.createComparator(indexDef.comparator);
|
|
12007
|
+
map.addNavigableIndex(navAttribute, comparator);
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
/**
|
|
12011
|
+
* Create an Attribute for extracting values from records.
|
|
12012
|
+
* Supports dot notation for nested paths.
|
|
12013
|
+
*/
|
|
12014
|
+
createAttribute(path) {
|
|
12015
|
+
return (0, import_core19.simpleAttribute)(path, (record) => {
|
|
12016
|
+
return this.getNestedValue(record, path);
|
|
12017
|
+
});
|
|
12018
|
+
}
|
|
12019
|
+
/**
|
|
12020
|
+
* Get a nested value from an object using dot notation.
|
|
12021
|
+
*
|
|
12022
|
+
* @param obj - Object to extract value from
|
|
12023
|
+
* @param path - Dot-notation path (e.g., "user.email")
|
|
12024
|
+
* @returns Value at the path or undefined
|
|
12025
|
+
*/
|
|
12026
|
+
getNestedValue(obj, path) {
|
|
12027
|
+
if (obj === null || obj === void 0) {
|
|
12028
|
+
return void 0;
|
|
12029
|
+
}
|
|
12030
|
+
const parts = path.split(".");
|
|
12031
|
+
let current = obj;
|
|
12032
|
+
for (const part of parts) {
|
|
12033
|
+
if (current === void 0 || current === null) {
|
|
12034
|
+
return void 0;
|
|
12035
|
+
}
|
|
12036
|
+
if (typeof current !== "object") {
|
|
12037
|
+
return void 0;
|
|
12038
|
+
}
|
|
12039
|
+
current = current[part];
|
|
12040
|
+
}
|
|
12041
|
+
return current;
|
|
12042
|
+
}
|
|
12043
|
+
/**
|
|
12044
|
+
* Create a comparator function for navigable indexes.
|
|
12045
|
+
*/
|
|
12046
|
+
createComparator(type) {
|
|
12047
|
+
switch (type) {
|
|
12048
|
+
case "number":
|
|
12049
|
+
return (a, b) => {
|
|
12050
|
+
const numA = typeof a === "number" ? a : parseFloat(String(a));
|
|
12051
|
+
const numB = typeof b === "number" ? b : parseFloat(String(b));
|
|
12052
|
+
return numA - numB;
|
|
12053
|
+
};
|
|
12054
|
+
case "date":
|
|
12055
|
+
return (a, b) => {
|
|
12056
|
+
const dateA = new Date(a).getTime();
|
|
12057
|
+
const dateB = new Date(b).getTime();
|
|
12058
|
+
return dateA - dateB;
|
|
12059
|
+
};
|
|
12060
|
+
case "string":
|
|
12061
|
+
return (a, b) => {
|
|
12062
|
+
const strA = String(a);
|
|
12063
|
+
const strB = String(b);
|
|
12064
|
+
return strA.localeCompare(strB);
|
|
12065
|
+
};
|
|
12066
|
+
default:
|
|
12067
|
+
return void 0;
|
|
12068
|
+
}
|
|
12069
|
+
}
|
|
12070
|
+
/**
|
|
12071
|
+
* Check if a map should be indexed based on configuration.
|
|
12072
|
+
*
|
|
12073
|
+
* @param mapName - Name of the map
|
|
12074
|
+
* @returns true if map has index configuration
|
|
12075
|
+
*/
|
|
12076
|
+
hasIndexConfig(mapName) {
|
|
12077
|
+
const config = this.mapConfigs.get(mapName);
|
|
12078
|
+
return config !== void 0 && config.indexes.length > 0;
|
|
12079
|
+
}
|
|
12080
|
+
/**
|
|
12081
|
+
* Get index configuration for a map.
|
|
12082
|
+
*
|
|
12083
|
+
* @param mapName - Name of the map
|
|
12084
|
+
* @returns Map index config or undefined
|
|
12085
|
+
*/
|
|
12086
|
+
getMapConfig(mapName) {
|
|
12087
|
+
return this.mapConfigs.get(mapName);
|
|
12088
|
+
}
|
|
12089
|
+
/**
|
|
12090
|
+
* Get all configured map names.
|
|
12091
|
+
*
|
|
12092
|
+
* @returns Array of map names with index configuration
|
|
12093
|
+
*/
|
|
12094
|
+
getConfiguredMaps() {
|
|
12095
|
+
return Array.from(this.mapConfigs.keys());
|
|
12096
|
+
}
|
|
12097
|
+
/**
|
|
12098
|
+
* Get the full server index configuration.
|
|
12099
|
+
*/
|
|
12100
|
+
getConfig() {
|
|
12101
|
+
return this.config;
|
|
12102
|
+
}
|
|
12103
|
+
};
|
|
11606
12104
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11607
12105
|
0 && (module.exports = {
|
|
11608
12106
|
BufferPool,
|
|
@@ -11613,6 +12111,7 @@ var MapWithResolver = class {
|
|
|
11613
12111
|
ConnectionRateLimiter,
|
|
11614
12112
|
DEFAULT_CLUSTER_COORDINATOR_CONFIG,
|
|
11615
12113
|
DEFAULT_CONFLICT_RESOLVER_CONFIG,
|
|
12114
|
+
DEFAULT_INDEX_CONFIG,
|
|
11616
12115
|
DEFAULT_JOURNAL_SERVICE_CONFIG,
|
|
11617
12116
|
DEFAULT_LAG_TRACKER_CONFIG,
|
|
11618
12117
|
DEFAULT_SANDBOX_CONFIG,
|
|
@@ -11623,6 +12122,7 @@ var MapWithResolver = class {
|
|
|
11623
12122
|
IteratorTasklet,
|
|
11624
12123
|
LagTracker,
|
|
11625
12124
|
LockManager,
|
|
12125
|
+
MapFactory,
|
|
11626
12126
|
MapTasklet,
|
|
11627
12127
|
MapWithResolver,
|
|
11628
12128
|
MemoryServerAdapter,
|
|
@@ -11653,10 +12153,12 @@ var MapWithResolver = class {
|
|
|
11653
12153
|
getNativeStats,
|
|
11654
12154
|
logNativeStatus,
|
|
11655
12155
|
logger,
|
|
12156
|
+
mergeWithDefaults,
|
|
11656
12157
|
setGlobalBufferPool,
|
|
11657
12158
|
setGlobalEventPayloadPool,
|
|
11658
12159
|
setGlobalMessagePool,
|
|
11659
12160
|
setGlobalRecordPool,
|
|
11660
|
-
setGlobalTimestampPool
|
|
12161
|
+
setGlobalTimestampPool,
|
|
12162
|
+
validateIndexConfig
|
|
11661
12163
|
});
|
|
11662
12164
|
//# sourceMappingURL=index.js.map
|