@topgunbuild/client 0.8.0 → 0.8.1
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 +223 -1
- package/dist/index.d.ts +223 -1
- package/dist/index.js +419 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
DEFAULT_CLUSTER_CONFIG: () => DEFAULT_CLUSTER_CONFIG,
|
|
40
40
|
EncryptedStorageAdapter: () => EncryptedStorageAdapter,
|
|
41
41
|
EventJournalReader: () => EventJournalReader,
|
|
42
|
+
HybridQueryHandle: () => HybridQueryHandle,
|
|
42
43
|
IDBAdapter: () => IDBAdapter,
|
|
43
44
|
LWWMap: () => import_core9.LWWMap,
|
|
44
45
|
PNCounterHandle: () => PNCounterHandle,
|
|
@@ -775,6 +776,11 @@ var _SyncEngine = class _SyncEngine {
|
|
|
775
776
|
// ============================================
|
|
776
777
|
/** Pending search requests by requestId */
|
|
777
778
|
this.pendingSearchRequests = /* @__PURE__ */ new Map();
|
|
779
|
+
// ============================================
|
|
780
|
+
// Hybrid Query Support (Phase 12)
|
|
781
|
+
// ============================================
|
|
782
|
+
/** Active hybrid query subscriptions */
|
|
783
|
+
this.hybridQueries = /* @__PURE__ */ new Map();
|
|
778
784
|
if (!config.serverUrl && !config.connectionProvider) {
|
|
779
785
|
throw new Error("SyncEngine requires either serverUrl or connectionProvider");
|
|
780
786
|
}
|
|
@@ -2507,6 +2513,151 @@ var _SyncEngine = class _SyncEngine {
|
|
|
2507
2513
|
getConflictResolverClient() {
|
|
2508
2514
|
return this.conflictResolverClient;
|
|
2509
2515
|
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Subscribe to a hybrid query (FTS + filter combination).
|
|
2518
|
+
*/
|
|
2519
|
+
subscribeToHybridQuery(query) {
|
|
2520
|
+
this.hybridQueries.set(query.id, query);
|
|
2521
|
+
const filter = query.getFilter();
|
|
2522
|
+
const mapName = query.getMapName();
|
|
2523
|
+
if (query.hasFTSPredicate() && this.stateMachine.getState() === "CONNECTED" /* CONNECTED */) {
|
|
2524
|
+
this.sendHybridQuerySubscription(query.id, mapName, filter);
|
|
2525
|
+
}
|
|
2526
|
+
this.runLocalHybridQuery(mapName, filter).then((results) => {
|
|
2527
|
+
query.onResult(results, "local");
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Unsubscribe from a hybrid query.
|
|
2532
|
+
*/
|
|
2533
|
+
unsubscribeFromHybridQuery(queryId) {
|
|
2534
|
+
const query = this.hybridQueries.get(queryId);
|
|
2535
|
+
if (query) {
|
|
2536
|
+
this.hybridQueries.delete(queryId);
|
|
2537
|
+
if (this.stateMachine.getState() === "CONNECTED" /* CONNECTED */ && query.hasFTSPredicate()) {
|
|
2538
|
+
this.sendMessage({
|
|
2539
|
+
type: "HYBRID_QUERY_UNSUBSCRIBE",
|
|
2540
|
+
payload: { subscriptionId: queryId }
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Send hybrid query subscription to server.
|
|
2547
|
+
*/
|
|
2548
|
+
sendHybridQuerySubscription(queryId, mapName, filter) {
|
|
2549
|
+
this.sendMessage({
|
|
2550
|
+
type: "HYBRID_QUERY_SUBSCRIBE",
|
|
2551
|
+
payload: {
|
|
2552
|
+
subscriptionId: queryId,
|
|
2553
|
+
mapName,
|
|
2554
|
+
predicate: filter.predicate,
|
|
2555
|
+
where: filter.where,
|
|
2556
|
+
sort: filter.sort,
|
|
2557
|
+
limit: filter.limit,
|
|
2558
|
+
offset: filter.offset
|
|
2559
|
+
}
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Run a local hybrid query (FTS + filter combination).
|
|
2564
|
+
* For FTS predicates, returns results with score = 0 (local-only mode).
|
|
2565
|
+
* Server provides actual FTS scoring.
|
|
2566
|
+
*/
|
|
2567
|
+
async runLocalHybridQuery(mapName, filter) {
|
|
2568
|
+
if (!this.storageAdapter) {
|
|
2569
|
+
return [];
|
|
2570
|
+
}
|
|
2571
|
+
const results = [];
|
|
2572
|
+
const allKeys = await this.storageAdapter.getAllKeys();
|
|
2573
|
+
const mapPrefix = `${mapName}:`;
|
|
2574
|
+
const entries = [];
|
|
2575
|
+
for (const fullKey of allKeys) {
|
|
2576
|
+
if (fullKey.startsWith(mapPrefix)) {
|
|
2577
|
+
const key = fullKey.substring(mapPrefix.length);
|
|
2578
|
+
const record = await this.storageAdapter.get(fullKey);
|
|
2579
|
+
if (record) {
|
|
2580
|
+
entries.push([key, record]);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
for (const [key, record] of entries) {
|
|
2585
|
+
if (record === null || record.value === null) continue;
|
|
2586
|
+
const value = record.value;
|
|
2587
|
+
if (filter.predicate) {
|
|
2588
|
+
const matches = (0, import_core.evaluatePredicate)(filter.predicate, value);
|
|
2589
|
+
if (!matches) continue;
|
|
2590
|
+
}
|
|
2591
|
+
if (filter.where) {
|
|
2592
|
+
let whereMatches = true;
|
|
2593
|
+
for (const [field, expected] of Object.entries(filter.where)) {
|
|
2594
|
+
if (value[field] !== expected) {
|
|
2595
|
+
whereMatches = false;
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
if (!whereMatches) continue;
|
|
2600
|
+
}
|
|
2601
|
+
results.push({
|
|
2602
|
+
key,
|
|
2603
|
+
value,
|
|
2604
|
+
score: 0,
|
|
2605
|
+
// Local doesn't have FTS scoring
|
|
2606
|
+
matchedTerms: []
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
if (filter.sort) {
|
|
2610
|
+
results.sort((a, b) => {
|
|
2611
|
+
for (const [field, direction] of Object.entries(filter.sort)) {
|
|
2612
|
+
let valA;
|
|
2613
|
+
let valB;
|
|
2614
|
+
if (field === "_score") {
|
|
2615
|
+
valA = a.score ?? 0;
|
|
2616
|
+
valB = b.score ?? 0;
|
|
2617
|
+
} else if (field === "_key") {
|
|
2618
|
+
valA = a.key;
|
|
2619
|
+
valB = b.key;
|
|
2620
|
+
} else {
|
|
2621
|
+
valA = a.value[field];
|
|
2622
|
+
valB = b.value[field];
|
|
2623
|
+
}
|
|
2624
|
+
if (valA < valB) return direction === "asc" ? -1 : 1;
|
|
2625
|
+
if (valA > valB) return direction === "asc" ? 1 : -1;
|
|
2626
|
+
}
|
|
2627
|
+
return 0;
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
let sliced = results;
|
|
2631
|
+
if (filter.offset) {
|
|
2632
|
+
sliced = sliced.slice(filter.offset);
|
|
2633
|
+
}
|
|
2634
|
+
if (filter.limit) {
|
|
2635
|
+
sliced = sliced.slice(0, filter.limit);
|
|
2636
|
+
}
|
|
2637
|
+
return sliced;
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Handle hybrid query response from server.
|
|
2641
|
+
*/
|
|
2642
|
+
handleHybridQueryResponse(payload) {
|
|
2643
|
+
const query = this.hybridQueries.get(payload.subscriptionId);
|
|
2644
|
+
if (query) {
|
|
2645
|
+
query.onResult(payload.results, "server");
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Handle hybrid query delta update from server.
|
|
2650
|
+
*/
|
|
2651
|
+
handleHybridQueryDelta(payload) {
|
|
2652
|
+
const query = this.hybridQueries.get(payload.subscriptionId);
|
|
2653
|
+
if (query) {
|
|
2654
|
+
if (payload.type === "LEAVE") {
|
|
2655
|
+
query.onUpdate(payload.key, null);
|
|
2656
|
+
} else {
|
|
2657
|
+
query.onUpdate(payload.key, payload.value, payload.score, payload.matchedTerms);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2510
2661
|
};
|
|
2511
2662
|
/** Default timeout for entry processor requests (ms) */
|
|
2512
2663
|
_SyncEngine.PROCESSOR_TIMEOUT = 3e4;
|
|
@@ -3387,6 +3538,236 @@ var SearchHandle = class {
|
|
|
3387
3538
|
}
|
|
3388
3539
|
};
|
|
3389
3540
|
|
|
3541
|
+
// src/HybridQueryHandle.ts
|
|
3542
|
+
var HybridQueryHandle = class {
|
|
3543
|
+
constructor(syncEngine, mapName, filter = {}) {
|
|
3544
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
3545
|
+
this.currentResults = /* @__PURE__ */ new Map();
|
|
3546
|
+
// Change tracking
|
|
3547
|
+
this.changeTracker = new ChangeTracker();
|
|
3548
|
+
this.pendingChanges = [];
|
|
3549
|
+
this.changeListeners = /* @__PURE__ */ new Set();
|
|
3550
|
+
// Track server data reception
|
|
3551
|
+
this.hasReceivedServerData = false;
|
|
3552
|
+
this.id = crypto.randomUUID();
|
|
3553
|
+
this.syncEngine = syncEngine;
|
|
3554
|
+
this.mapName = mapName;
|
|
3555
|
+
this.filter = filter;
|
|
3556
|
+
}
|
|
3557
|
+
/**
|
|
3558
|
+
* Subscribe to query results.
|
|
3559
|
+
*/
|
|
3560
|
+
subscribe(callback) {
|
|
3561
|
+
this.listeners.add(callback);
|
|
3562
|
+
if (this.listeners.size === 1) {
|
|
3563
|
+
this.syncEngine.subscribeToHybridQuery(this);
|
|
3564
|
+
} else {
|
|
3565
|
+
callback(this.getSortedResults());
|
|
3566
|
+
}
|
|
3567
|
+
this.loadInitialLocalData().then((data) => {
|
|
3568
|
+
if (this.currentResults.size === 0) {
|
|
3569
|
+
this.onResult(data, "local");
|
|
3570
|
+
}
|
|
3571
|
+
});
|
|
3572
|
+
return () => {
|
|
3573
|
+
this.listeners.delete(callback);
|
|
3574
|
+
if (this.listeners.size === 0) {
|
|
3575
|
+
this.syncEngine.unsubscribeFromHybridQuery(this.id);
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
async loadInitialLocalData() {
|
|
3580
|
+
return this.syncEngine.runLocalHybridQuery(this.mapName, this.filter);
|
|
3581
|
+
}
|
|
3582
|
+
/**
|
|
3583
|
+
* Called by SyncEngine with query results.
|
|
3584
|
+
*/
|
|
3585
|
+
onResult(items, source = "server") {
|
|
3586
|
+
logger.debug(
|
|
3587
|
+
{
|
|
3588
|
+
mapName: this.mapName,
|
|
3589
|
+
itemCount: items.length,
|
|
3590
|
+
source,
|
|
3591
|
+
currentResultsCount: this.currentResults.size,
|
|
3592
|
+
hasReceivedServerData: this.hasReceivedServerData
|
|
3593
|
+
},
|
|
3594
|
+
"HybridQueryHandle onResult"
|
|
3595
|
+
);
|
|
3596
|
+
if (source === "server" && items.length === 0 && !this.hasReceivedServerData) {
|
|
3597
|
+
logger.debug(
|
|
3598
|
+
{ mapName: this.mapName },
|
|
3599
|
+
"HybridQueryHandle ignoring empty server response"
|
|
3600
|
+
);
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
if (source === "server" && items.length > 0) {
|
|
3604
|
+
this.hasReceivedServerData = true;
|
|
3605
|
+
}
|
|
3606
|
+
const newKeys = new Set(items.map((i) => i.key));
|
|
3607
|
+
for (const key of this.currentResults.keys()) {
|
|
3608
|
+
if (!newKeys.has(key)) {
|
|
3609
|
+
this.currentResults.delete(key);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
for (const item of items) {
|
|
3613
|
+
this.currentResults.set(item.key, {
|
|
3614
|
+
value: item.value,
|
|
3615
|
+
score: item.score,
|
|
3616
|
+
matchedTerms: item.matchedTerms
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3619
|
+
this.computeAndNotifyChanges(Date.now());
|
|
3620
|
+
this.notify();
|
|
3621
|
+
}
|
|
3622
|
+
/**
|
|
3623
|
+
* Called by SyncEngine on live update.
|
|
3624
|
+
*/
|
|
3625
|
+
onUpdate(key, value, score, matchedTerms) {
|
|
3626
|
+
if (value === null) {
|
|
3627
|
+
this.currentResults.delete(key);
|
|
3628
|
+
} else {
|
|
3629
|
+
this.currentResults.set(key, { value, score, matchedTerms });
|
|
3630
|
+
}
|
|
3631
|
+
this.computeAndNotifyChanges(Date.now());
|
|
3632
|
+
this.notify();
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Subscribe to change events.
|
|
3636
|
+
*/
|
|
3637
|
+
onChanges(listener) {
|
|
3638
|
+
this.changeListeners.add(listener);
|
|
3639
|
+
return () => this.changeListeners.delete(listener);
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Get and clear pending changes.
|
|
3643
|
+
*/
|
|
3644
|
+
consumeChanges() {
|
|
3645
|
+
const changes = [...this.pendingChanges];
|
|
3646
|
+
this.pendingChanges = [];
|
|
3647
|
+
return changes;
|
|
3648
|
+
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Get last change without consuming.
|
|
3651
|
+
*/
|
|
3652
|
+
getLastChange() {
|
|
3653
|
+
return this.pendingChanges.length > 0 ? this.pendingChanges[this.pendingChanges.length - 1] : null;
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Get all pending changes without consuming.
|
|
3657
|
+
*/
|
|
3658
|
+
getPendingChanges() {
|
|
3659
|
+
return [...this.pendingChanges];
|
|
3660
|
+
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Clear all pending changes.
|
|
3663
|
+
*/
|
|
3664
|
+
clearChanges() {
|
|
3665
|
+
this.pendingChanges = [];
|
|
3666
|
+
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Reset change tracker.
|
|
3669
|
+
*/
|
|
3670
|
+
resetChangeTracker() {
|
|
3671
|
+
this.changeTracker.reset();
|
|
3672
|
+
this.pendingChanges = [];
|
|
3673
|
+
}
|
|
3674
|
+
computeAndNotifyChanges(timestamp) {
|
|
3675
|
+
const dataMap = /* @__PURE__ */ new Map();
|
|
3676
|
+
for (const [key, entry] of this.currentResults) {
|
|
3677
|
+
dataMap.set(key, entry.value);
|
|
3678
|
+
}
|
|
3679
|
+
const changes = this.changeTracker.computeChanges(dataMap, timestamp);
|
|
3680
|
+
if (changes.length > 0) {
|
|
3681
|
+
this.pendingChanges.push(...changes);
|
|
3682
|
+
this.notifyChangeListeners(changes);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
notifyChangeListeners(changes) {
|
|
3686
|
+
for (const listener of this.changeListeners) {
|
|
3687
|
+
try {
|
|
3688
|
+
listener(changes);
|
|
3689
|
+
} catch (e) {
|
|
3690
|
+
logger.error({ err: e }, "HybridQueryHandle change listener error");
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
notify() {
|
|
3695
|
+
const results = this.getSortedResults();
|
|
3696
|
+
for (const listener of this.listeners) {
|
|
3697
|
+
listener(results);
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
/**
|
|
3701
|
+
* Get sorted results with _key and _score.
|
|
3702
|
+
*/
|
|
3703
|
+
getSortedResults() {
|
|
3704
|
+
const results = Array.from(this.currentResults.entries()).map(
|
|
3705
|
+
([key, entry]) => ({
|
|
3706
|
+
value: entry.value,
|
|
3707
|
+
_key: key,
|
|
3708
|
+
_score: entry.score,
|
|
3709
|
+
_matchedTerms: entry.matchedTerms
|
|
3710
|
+
})
|
|
3711
|
+
);
|
|
3712
|
+
if (this.filter.sort) {
|
|
3713
|
+
results.sort((a, b) => {
|
|
3714
|
+
for (const [field, direction] of Object.entries(this.filter.sort)) {
|
|
3715
|
+
let valA;
|
|
3716
|
+
let valB;
|
|
3717
|
+
if (field === "_score") {
|
|
3718
|
+
valA = a._score ?? 0;
|
|
3719
|
+
valB = b._score ?? 0;
|
|
3720
|
+
} else if (field === "_key") {
|
|
3721
|
+
valA = a._key;
|
|
3722
|
+
valB = b._key;
|
|
3723
|
+
} else {
|
|
3724
|
+
valA = a.value[field];
|
|
3725
|
+
valB = b.value[field];
|
|
3726
|
+
}
|
|
3727
|
+
if (valA < valB) return direction === "asc" ? -1 : 1;
|
|
3728
|
+
if (valA > valB) return direction === "asc" ? 1 : -1;
|
|
3729
|
+
}
|
|
3730
|
+
return 0;
|
|
3731
|
+
});
|
|
3732
|
+
}
|
|
3733
|
+
let sliced = results;
|
|
3734
|
+
if (this.filter.offset) {
|
|
3735
|
+
sliced = sliced.slice(this.filter.offset);
|
|
3736
|
+
}
|
|
3737
|
+
if (this.filter.limit) {
|
|
3738
|
+
sliced = sliced.slice(0, this.filter.limit);
|
|
3739
|
+
}
|
|
3740
|
+
return sliced;
|
|
3741
|
+
}
|
|
3742
|
+
/**
|
|
3743
|
+
* Get the filter configuration.
|
|
3744
|
+
*/
|
|
3745
|
+
getFilter() {
|
|
3746
|
+
return this.filter;
|
|
3747
|
+
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Get the map name.
|
|
3750
|
+
*/
|
|
3751
|
+
getMapName() {
|
|
3752
|
+
return this.mapName;
|
|
3753
|
+
}
|
|
3754
|
+
/**
|
|
3755
|
+
* Check if this query contains FTS predicates.
|
|
3756
|
+
*/
|
|
3757
|
+
hasFTSPredicate() {
|
|
3758
|
+
return this.filter.predicate ? this.containsFTS(this.filter.predicate) : false;
|
|
3759
|
+
}
|
|
3760
|
+
containsFTS(predicate) {
|
|
3761
|
+
if (predicate.op === "match" || predicate.op === "matchPhrase" || predicate.op === "matchPrefix") {
|
|
3762
|
+
return true;
|
|
3763
|
+
}
|
|
3764
|
+
if (predicate.children) {
|
|
3765
|
+
return predicate.children.some((child) => this.containsFTS(child));
|
|
3766
|
+
}
|
|
3767
|
+
return false;
|
|
3768
|
+
}
|
|
3769
|
+
};
|
|
3770
|
+
|
|
3390
3771
|
// src/cluster/ClusterClient.ts
|
|
3391
3772
|
var import_core6 = require("@topgunbuild/core");
|
|
3392
3773
|
|
|
@@ -5270,6 +5651,43 @@ var TopGunClient = class {
|
|
|
5270
5651
|
return new SearchHandle(this.syncEngine, mapName, query, options);
|
|
5271
5652
|
}
|
|
5272
5653
|
// ============================================
|
|
5654
|
+
// Hybrid Query API (Phase 12)
|
|
5655
|
+
// ============================================
|
|
5656
|
+
/**
|
|
5657
|
+
* Create a hybrid query combining FTS with traditional filters.
|
|
5658
|
+
*
|
|
5659
|
+
* Hybrid queries allow combining full-text search predicates (match, matchPhrase, matchPrefix)
|
|
5660
|
+
* with traditional filter predicates (eq, gt, lt, contains, etc.) in a single query.
|
|
5661
|
+
* Results include relevance scores for FTS ranking.
|
|
5662
|
+
*
|
|
5663
|
+
* @param mapName Name of the map to query
|
|
5664
|
+
* @param filter Hybrid query filter with predicate, where, sort, limit, offset
|
|
5665
|
+
* @returns HybridQueryHandle for managing the subscription
|
|
5666
|
+
*
|
|
5667
|
+
* @example
|
|
5668
|
+
* ```typescript
|
|
5669
|
+
* import { Predicates } from '@topgunbuild/core';
|
|
5670
|
+
*
|
|
5671
|
+
* // Hybrid query: FTS + filter
|
|
5672
|
+
* const handle = client.hybridQuery<Article>('articles', {
|
|
5673
|
+
* predicate: Predicates.and(
|
|
5674
|
+
* Predicates.match('body', 'machine learning'),
|
|
5675
|
+
* Predicates.equal('category', 'tech')
|
|
5676
|
+
* ),
|
|
5677
|
+
* sort: { _score: 'desc' },
|
|
5678
|
+
* limit: 20
|
|
5679
|
+
* });
|
|
5680
|
+
*
|
|
5681
|
+
* // Subscribe to results
|
|
5682
|
+
* handle.subscribe((results) => {
|
|
5683
|
+
* results.forEach(r => console.log(`${r._key}: score=${r._score}`));
|
|
5684
|
+
* });
|
|
5685
|
+
* ```
|
|
5686
|
+
*/
|
|
5687
|
+
hybridQuery(mapName, filter = {}) {
|
|
5688
|
+
return new HybridQueryHandle(this.syncEngine, mapName, filter);
|
|
5689
|
+
}
|
|
5690
|
+
// ============================================
|
|
5273
5691
|
// Entry Processor API (Phase 5.03)
|
|
5274
5692
|
// ============================================
|
|
5275
5693
|
/**
|
|
@@ -5877,6 +6295,7 @@ var import_core9 = require("@topgunbuild/core");
|
|
|
5877
6295
|
DEFAULT_CLUSTER_CONFIG,
|
|
5878
6296
|
EncryptedStorageAdapter,
|
|
5879
6297
|
EventJournalReader,
|
|
6298
|
+
HybridQueryHandle,
|
|
5880
6299
|
IDBAdapter,
|
|
5881
6300
|
LWWMap,
|
|
5882
6301
|
PNCounterHandle,
|