@statezero/core 0.1.18 → 0.1.20
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/syncEngine/metrics/metricOptCalcs.d.ts +3 -3
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +21 -0
- package/dist/syncEngine/registries/querysetStoreGraph.js +93 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +9 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +36 -4
- package/dist/syncEngine/stores/querysetStore.d.ts +6 -0
- package/dist/syncEngine/stores/querysetStore.js +57 -16
- package/package.json +1 -1
|
@@ -48,8 +48,8 @@ export class MaxStrategy extends MetricCalculationStrategy {
|
|
|
48
48
|
* Factory class for creating the appropriate strategy
|
|
49
49
|
*/
|
|
50
50
|
export class MetricStrategyFactory {
|
|
51
|
-
static "__#
|
|
52
|
-
static "__#
|
|
51
|
+
static "__#7@#customStrategies": Map<any, any>;
|
|
52
|
+
static "__#7@#defaultStrategies": Map<string, () => CountStrategy>;
|
|
53
53
|
/**
|
|
54
54
|
* Clear all custom strategy overrides
|
|
55
55
|
*/
|
|
@@ -61,7 +61,7 @@ export class MetricStrategyFactory {
|
|
|
61
61
|
* @param {Function} ModelClass - The model class
|
|
62
62
|
* @returns {string} A unique key
|
|
63
63
|
*/
|
|
64
|
-
private static "__#
|
|
64
|
+
private static "__#7@#generateStrategyKey";
|
|
65
65
|
/**
|
|
66
66
|
* Override a strategy for a specific metric type and model class
|
|
67
67
|
* @param {string} metricType - The type of metric (count, sum, min, max)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple graph for tracking queryset store ancestry
|
|
3
|
+
*/
|
|
4
|
+
export class QuerysetStoreGraph {
|
|
5
|
+
constructor(hasStoreFn?: null);
|
|
6
|
+
graph: any;
|
|
7
|
+
hasStoreFn: () => boolean;
|
|
8
|
+
processedQuerysets: Set<any>;
|
|
9
|
+
setHasStoreFn(hasStoreFn: any): void;
|
|
10
|
+
/**
|
|
11
|
+
* Add a queryset and its parent relationship to the graph
|
|
12
|
+
*/
|
|
13
|
+
addQueryset(queryset: any): void;
|
|
14
|
+
/**
|
|
15
|
+
* Find the root store for a queryset
|
|
16
|
+
* @param {Object} queryset - The queryset to analyze
|
|
17
|
+
* @returns {Object} { isRoot: boolean, root: semanticKey|null }
|
|
18
|
+
*/
|
|
19
|
+
findRoot(queryset: Object): Object;
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Graph } from "graphlib";
|
|
2
|
+
/**
|
|
3
|
+
* Simple graph for tracking queryset store ancestry
|
|
4
|
+
*/
|
|
5
|
+
export class QuerysetStoreGraph {
|
|
6
|
+
constructor(hasStoreFn = null) {
|
|
7
|
+
this.graph = new Graph({ directed: true });
|
|
8
|
+
this.hasStoreFn = hasStoreFn || (() => false);
|
|
9
|
+
this.processedQuerysets = new Set(); // Track UUIDs of processed querysets
|
|
10
|
+
}
|
|
11
|
+
setHasStoreFn(hasStoreFn) {
|
|
12
|
+
this.hasStoreFn = hasStoreFn;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Add a queryset and its parent relationship to the graph
|
|
16
|
+
*/
|
|
17
|
+
addQueryset(queryset) {
|
|
18
|
+
if (!queryset)
|
|
19
|
+
return;
|
|
20
|
+
if (this.processedQuerysets.has(queryset.key)) {
|
|
21
|
+
return; // Already processed, skip
|
|
22
|
+
}
|
|
23
|
+
let current = queryset;
|
|
24
|
+
while (current && !this.processedQuerysets.has(current.key)) {
|
|
25
|
+
const currentKey = current.semanticKey;
|
|
26
|
+
const currentUuid = current.key;
|
|
27
|
+
this.processedQuerysets.add(currentUuid);
|
|
28
|
+
this.graph.setNode(currentKey);
|
|
29
|
+
if (current.__parent) {
|
|
30
|
+
const parentKey = current.__parent.semanticKey;
|
|
31
|
+
this.graph.setNode(parentKey);
|
|
32
|
+
this.graph.setEdge(currentKey, parentKey); // child -> parent
|
|
33
|
+
current = current.__parent;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find the root store for a queryset
|
|
42
|
+
* @param {Object} queryset - The queryset to analyze
|
|
43
|
+
* @returns {Object} { isRoot: boolean, root: semanticKey|null }
|
|
44
|
+
*/
|
|
45
|
+
findRoot(queryset) {
|
|
46
|
+
// Validate input - null/undefined is a programming error
|
|
47
|
+
if (!queryset) {
|
|
48
|
+
throw new Error("findRoot was called with a null object, instead of a queryset");
|
|
49
|
+
}
|
|
50
|
+
// Handle queryset without semanticKey
|
|
51
|
+
if (!queryset.semanticKey) {
|
|
52
|
+
throw new Error("findRoot was called on an object without a semanticKey, which means its not a queryset. findRoot only works on querysets");
|
|
53
|
+
}
|
|
54
|
+
const semanticKey = queryset.semanticKey;
|
|
55
|
+
if (!this.graph.hasNode(semanticKey)) {
|
|
56
|
+
this.addQueryset(queryset);
|
|
57
|
+
}
|
|
58
|
+
// Traverse ALL the way up to find the HIGHEST ancestor with a store
|
|
59
|
+
const visited = new Set();
|
|
60
|
+
let current = semanticKey;
|
|
61
|
+
let highestAncestorWithStore = null;
|
|
62
|
+
while (current && !visited.has(current)) {
|
|
63
|
+
visited.add(current);
|
|
64
|
+
// Check if current node has a store
|
|
65
|
+
if (this.hasStoreFn(current)) {
|
|
66
|
+
highestAncestorWithStore = current;
|
|
67
|
+
}
|
|
68
|
+
// Move to parent
|
|
69
|
+
const parents = this.graph.successors(current) || [];
|
|
70
|
+
if (parents.length > 0) {
|
|
71
|
+
current = parents[0]; // Follow the parent chain
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
break; // No more parents
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (highestAncestorWithStore) {
|
|
78
|
+
if (highestAncestorWithStore === semanticKey) {
|
|
79
|
+
// This queryset itself is the highest with a store
|
|
80
|
+
return { isRoot: true, root: semanticKey };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Found a higher ancestor with a store
|
|
84
|
+
return { isRoot: false, root: highestAncestorWithStore };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// No stores found anywhere in the chain
|
|
88
|
+
return { isRoot: true, root: null };
|
|
89
|
+
}
|
|
90
|
+
clear() {
|
|
91
|
+
this.graph = new Graph({ directed: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -23,6 +23,7 @@ declare class QuerysetStoreRegistry {
|
|
|
23
23
|
_tempStores: WeakMap<object, any>;
|
|
24
24
|
followingQuerysets: Map<any, any>;
|
|
25
25
|
syncManager: () => void;
|
|
26
|
+
querysetStoreGraph: QuerysetStoreGraph;
|
|
26
27
|
clear(): void;
|
|
27
28
|
setSyncManager(syncManager: any): void;
|
|
28
29
|
/**
|
|
@@ -30,6 +31,13 @@ declare class QuerysetStoreRegistry {
|
|
|
30
31
|
*/
|
|
31
32
|
addFollowingQueryset(semanticKey: any, queryset: any): void;
|
|
32
33
|
getStore(queryset: any, seed?: boolean): any;
|
|
34
|
+
/**
|
|
35
|
+
* Function to return the root store for a queryset
|
|
36
|
+
*/
|
|
37
|
+
getRootStore(queryset: any): {
|
|
38
|
+
isRoot: boolean;
|
|
39
|
+
rootStore: any;
|
|
40
|
+
};
|
|
33
41
|
/**
|
|
34
42
|
* Get the current state of the queryset, wrapped in a LiveQueryset
|
|
35
43
|
* @param {Object} queryset - The queryset
|
|
@@ -52,4 +60,5 @@ declare class QuerysetStoreRegistry {
|
|
|
52
60
|
*/
|
|
53
61
|
getAllStoresForModel(ModelClass: any): any[];
|
|
54
62
|
}
|
|
63
|
+
import { QuerysetStoreGraph } from './querysetStoreGraph.js';
|
|
55
64
|
export {};
|
|
@@ -16,6 +16,7 @@ import { wrapReactiveQuerySet } from '../../reactiveAdaptor.js';
|
|
|
16
16
|
import { processQuery, getRequiredFields, pickRequiredFields } from '../../filtering/localFiltering.js';
|
|
17
17
|
import { filter } from '../../filtering/localFiltering.js';
|
|
18
18
|
import { makeApiCall } from '../../flavours/django/makeApiCall.js';
|
|
19
|
+
import { QuerysetStoreGraph } from './querysetStoreGraph.js';
|
|
19
20
|
import { isNil, pick } from 'lodash-es';
|
|
20
21
|
import hash from 'object-hash';
|
|
21
22
|
import { Operation } from '../stores/operation.js';
|
|
@@ -106,6 +107,9 @@ class QuerysetStoreRegistry {
|
|
|
106
107
|
this._tempStores = new WeakMap(); // WeakMap<Queryset, Store>
|
|
107
108
|
this.followingQuerysets = new Map(); // Map<semanticKey, Set<queryset>>
|
|
108
109
|
this.syncManager = () => { console.warn("SyncManager not set for QuerysetStoreRegistry"); };
|
|
110
|
+
this.querysetStoreGraph = new QuerysetStoreGraph((semanticKey) => {
|
|
111
|
+
return this._stores.has(semanticKey);
|
|
112
|
+
});
|
|
109
113
|
}
|
|
110
114
|
clear() {
|
|
111
115
|
this._stores.forEach((store) => {
|
|
@@ -113,6 +117,7 @@ class QuerysetStoreRegistry {
|
|
|
113
117
|
});
|
|
114
118
|
this._stores = new Map();
|
|
115
119
|
this.followingQuerysets = new Map();
|
|
120
|
+
this.querysetStoreGraph.clear();
|
|
116
121
|
}
|
|
117
122
|
setSyncManager(syncManager) {
|
|
118
123
|
this.syncManager = syncManager;
|
|
@@ -130,6 +135,7 @@ class QuerysetStoreRegistry {
|
|
|
130
135
|
if (isNil(queryset) || isNil(queryset.ModelClass)) {
|
|
131
136
|
throw new Error("QuerysetStoreRegistry.getStore requires a valid queryset");
|
|
132
137
|
}
|
|
138
|
+
this.querysetStoreGraph.addQueryset(queryset);
|
|
133
139
|
// Check if we already have a temporary store for this exact QuerySet instance
|
|
134
140
|
if (this._tempStores.has(queryset)) {
|
|
135
141
|
return this._tempStores.get(queryset);
|
|
@@ -156,17 +162,39 @@ class QuerysetStoreRegistry {
|
|
|
156
162
|
let ast = queryset.build();
|
|
157
163
|
let ModelClass = queryset.ModelClass;
|
|
158
164
|
if (queryset.__parent && seed) {
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
const parentKey = queryset.__parent.semanticKey;
|
|
166
|
+
if (this._stores.has(parentKey)) {
|
|
167
|
+
let parentLiveQuerySet = this.getEntity(queryset.__parent);
|
|
168
|
+
initialGroundTruthPks = filter(parentLiveQuerySet, ast, ModelClass, false);
|
|
169
|
+
}
|
|
161
170
|
}
|
|
162
171
|
// Get the parent registry
|
|
163
172
|
const store = new QuerysetStore(ModelClass, fetchQueryset, queryset, initialGroundTruthPks, // Initial ground truth PKs
|
|
164
|
-
null // Initial operations
|
|
165
|
-
|
|
173
|
+
null, // Initial operations
|
|
174
|
+
{
|
|
175
|
+
getRootStore: this.getRootStore.bind(this),
|
|
176
|
+
isTemp: true,
|
|
177
|
+
});
|
|
166
178
|
// Store it in the temp store map
|
|
167
179
|
this._tempStores.set(queryset, store);
|
|
168
180
|
return store;
|
|
169
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Function to return the root store for a queryset
|
|
184
|
+
*/
|
|
185
|
+
getRootStore(queryset) {
|
|
186
|
+
if (isNil(queryset)) {
|
|
187
|
+
throw new Error("QuerysetStoreRegistry.getRootStore requires a valid queryset");
|
|
188
|
+
}
|
|
189
|
+
const { isRoot, root } = this.querysetStoreGraph.findRoot(queryset);
|
|
190
|
+
const rootStore = this._stores.get(root);
|
|
191
|
+
if (!isRoot && rootStore) {
|
|
192
|
+
return { isRoot: false, rootStore: rootStore };
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
return { isRoot: true, rootStore: null };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
170
198
|
/**
|
|
171
199
|
* Get the current state of the queryset, wrapped in a LiveQueryset
|
|
172
200
|
* @param {Object} queryset - The queryset
|
|
@@ -183,12 +211,14 @@ class QuerysetStoreRegistry {
|
|
|
183
211
|
// If we have a temporary store, promote it
|
|
184
212
|
if (this._tempStores.has(queryset)) {
|
|
185
213
|
store = this._tempStores.get(queryset);
|
|
214
|
+
store.isTemp = false; // Promote to permanent store
|
|
186
215
|
this._stores.set(semanticKey, store);
|
|
187
216
|
this.syncManager.followModel(this, queryset.ModelClass);
|
|
188
217
|
}
|
|
189
218
|
// Otherwise, ensure we have a permanent store
|
|
190
219
|
else if (!this._stores.has(semanticKey)) {
|
|
191
220
|
store = this.getStore(queryset, seed);
|
|
221
|
+
store.isTemp = false;
|
|
192
222
|
this._stores.set(semanticKey, store);
|
|
193
223
|
this.syncManager.followModel(this, queryset.ModelClass);
|
|
194
224
|
}
|
|
@@ -219,11 +249,13 @@ class QuerysetStoreRegistry {
|
|
|
219
249
|
// If we have a temp store, promote it
|
|
220
250
|
if (this._tempStores.has(queryset)) {
|
|
221
251
|
store = this._tempStores.get(queryset);
|
|
252
|
+
store.isTemp = false; // Promote to permanent store
|
|
222
253
|
this._stores.set(semanticKey, store);
|
|
223
254
|
}
|
|
224
255
|
else {
|
|
225
256
|
// Create a new permanent store
|
|
226
257
|
store = this.getStore(queryset);
|
|
258
|
+
store.isTemp = false;
|
|
227
259
|
this._stores.set(semanticKey, store);
|
|
228
260
|
}
|
|
229
261
|
}
|
|
@@ -6,7 +6,11 @@ export class QuerysetStore {
|
|
|
6
6
|
operationsMap: Map<any, any>;
|
|
7
7
|
groundTruthPks: never[];
|
|
8
8
|
isSyncing: boolean;
|
|
9
|
+
lastSync: number | null;
|
|
10
|
+
needsSync: boolean;
|
|
11
|
+
isTemp: any;
|
|
9
12
|
pruneThreshold: any;
|
|
13
|
+
getRootStore: any;
|
|
10
14
|
qsCache: Cache;
|
|
11
15
|
get cacheKey(): any;
|
|
12
16
|
onHydrated(hydratedData: any): void;
|
|
@@ -26,6 +30,8 @@ export class QuerysetStore {
|
|
|
26
30
|
getInflightOperations(): any[];
|
|
27
31
|
prune(): void;
|
|
28
32
|
render(optimistic?: boolean, fromCache?: boolean): any[];
|
|
33
|
+
renderFromRoot(optimistic: boolean | undefined, rootStore: any): any[];
|
|
34
|
+
renderFromData(optimistic?: boolean): any[];
|
|
29
35
|
applyOperation(operation: any, currentPks: any): any;
|
|
30
36
|
sync(): Promise<void>;
|
|
31
37
|
}
|
|
@@ -6,13 +6,19 @@ import { modelStoreRegistry } from '../registries/modelStoreRegistry.js';
|
|
|
6
6
|
import { processIncludedEntities } from '../../flavours/django/makeApiCall.js';
|
|
7
7
|
import hash from 'object-hash';
|
|
8
8
|
import { Cache } from '../cache/cache.js';
|
|
9
|
+
import { filter } from "../../filtering/localFiltering.js";
|
|
10
|
+
import { mod } from 'mathjs';
|
|
9
11
|
export class QuerysetStore {
|
|
10
12
|
constructor(modelClass, fetchFn, queryset, initialGroundTruthPks = null, initialOperations = null, options = {}) {
|
|
11
13
|
this.modelClass = modelClass;
|
|
12
14
|
this.fetchFn = fetchFn;
|
|
13
15
|
this.queryset = queryset;
|
|
14
16
|
this.isSyncing = false;
|
|
17
|
+
this.lastSync = null;
|
|
18
|
+
this.needsSync = false;
|
|
19
|
+
this.isTemp = options.isTemp || false;
|
|
15
20
|
this.pruneThreshold = options.pruneThreshold || 10;
|
|
21
|
+
this.getRootStore = options.getRootStore || null;
|
|
16
22
|
this.groundTruthPks = initialGroundTruthPks || [];
|
|
17
23
|
this.operationsMap = new Map();
|
|
18
24
|
if (Array.isArray(initialOperations)) {
|
|
@@ -22,7 +28,7 @@ export class QuerysetStore {
|
|
|
22
28
|
this.operationsMap.set(op.operationId, op);
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
|
-
this.qsCache = new Cache(
|
|
31
|
+
this.qsCache = new Cache("queryset-cache", {}, this.onHydrated.bind(this));
|
|
26
32
|
}
|
|
27
33
|
// Caching
|
|
28
34
|
get cacheKey() {
|
|
@@ -40,7 +46,7 @@ export class QuerysetStore {
|
|
|
40
46
|
setCache(result) {
|
|
41
47
|
let nonTempPks = [];
|
|
42
48
|
result.forEach((pk) => {
|
|
43
|
-
if (typeof pk ===
|
|
49
|
+
if (typeof pk === "string" && containsTempPk(pk)) {
|
|
44
50
|
pk = replaceTempPks(pk);
|
|
45
51
|
if (isNil(pk) || isEmpty(trim(pk))) {
|
|
46
52
|
return;
|
|
@@ -93,9 +99,7 @@ export class QuerysetStore {
|
|
|
93
99
|
this._emitRenderEvent();
|
|
94
100
|
}
|
|
95
101
|
async setGroundTruth(groundTruthPks) {
|
|
96
|
-
this.groundTruthPks = Array.isArray(groundTruthPks)
|
|
97
|
-
? groundTruthPks
|
|
98
|
-
: [];
|
|
102
|
+
this.groundTruthPks = Array.isArray(groundTruthPks) ? groundTruthPks : [];
|
|
99
103
|
this._emitRenderEvent();
|
|
100
104
|
}
|
|
101
105
|
async setOperations(operations) {
|
|
@@ -109,10 +113,10 @@ export class QuerysetStore {
|
|
|
109
113
|
}
|
|
110
114
|
getTrimmedOperations() {
|
|
111
115
|
const cutoff = Date.now() - 1000 * 60 * 2;
|
|
112
|
-
return this.operations.filter(op => op.timestamp > cutoff);
|
|
116
|
+
return this.operations.filter((op) => op.timestamp > cutoff);
|
|
113
117
|
}
|
|
114
118
|
getInflightOperations() {
|
|
115
|
-
return this.operations.filter(operation => operation.status != Status.CONFIRMED &&
|
|
119
|
+
return this.operations.filter((operation) => operation.status != Status.CONFIRMED &&
|
|
116
120
|
operation.status != Status.REJECTED);
|
|
117
121
|
}
|
|
118
122
|
prune() {
|
|
@@ -127,13 +131,16 @@ export class QuerysetStore {
|
|
|
127
131
|
return cachedResult;
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
let result;
|
|
135
|
+
if (this.getRootStore && typeof this.getRootStore === "function" && !this.isTemp) {
|
|
136
|
+
const { isRoot, rootStore } = this.getRootStore(this.queryset);
|
|
137
|
+
if (!isRoot && rootStore) {
|
|
138
|
+
result = this.renderFromRoot(optimistic, rootStore);
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
|
-
|
|
141
|
+
if (isNil(result)) {
|
|
142
|
+
result = this.renderFromData(optimistic);
|
|
143
|
+
}
|
|
137
144
|
let limit = this.queryset.build().serializerOptions?.limit;
|
|
138
145
|
if (limit) {
|
|
139
146
|
result = result.slice(0, limit);
|
|
@@ -141,12 +148,30 @@ export class QuerysetStore {
|
|
|
141
148
|
this.setCache(result);
|
|
142
149
|
return result;
|
|
143
150
|
}
|
|
151
|
+
renderFromRoot(optimistic = true, rootStore) {
|
|
152
|
+
let renderedPks = rootStore.render(optimistic);
|
|
153
|
+
let renderedData = renderedPks.map((pk) => {
|
|
154
|
+
return this.modelClass.fromPk(pk, this.queryset);
|
|
155
|
+
});
|
|
156
|
+
let ast = this.queryset.build();
|
|
157
|
+
let result = filter(renderedData, ast, this.modelClass, false);
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
renderFromData(optimistic = true) {
|
|
161
|
+
const renderedPks = this.groundTruthSet;
|
|
162
|
+
for (const op of this.operations) {
|
|
163
|
+
if (op.status !== Status.REJECTED &&
|
|
164
|
+
(optimistic || op.status === Status.CONFIRMED)) {
|
|
165
|
+
this.applyOperation(op, renderedPks);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let result = Array.from(renderedPks);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
144
171
|
applyOperation(operation, currentPks) {
|
|
145
172
|
const pkField = this.pkField;
|
|
146
173
|
for (const instance of operation.instances) {
|
|
147
|
-
if (!instance ||
|
|
148
|
-
typeof instance !== 'object' ||
|
|
149
|
-
!(pkField in instance)) {
|
|
174
|
+
if (!instance || typeof instance !== "object" || !(pkField in instance)) {
|
|
150
175
|
console.warn(`[QuerysetStore ${this.modelClass.modelName}] Skipping instance in operation ${operation.operationId} due to missing PK '${String(pkField)}' or invalid format.`);
|
|
151
176
|
continue;
|
|
152
177
|
}
|
|
@@ -175,12 +200,25 @@ export class QuerysetStore {
|
|
|
175
200
|
console.warn(`[QuerysetStore ${id}] Already syncing, request ignored.`);
|
|
176
201
|
return;
|
|
177
202
|
}
|
|
203
|
+
// Check if we're delegating to a root store
|
|
204
|
+
if (this.getRootStore && typeof this.getRootStore === "function" && !this.isTemp) {
|
|
205
|
+
const { isRoot, rootStore } = this.getRootStore(this.queryset);
|
|
206
|
+
if (!isRoot && rootStore) {
|
|
207
|
+
// We're delegating to a root store - don't sync, just mark as needing sync
|
|
208
|
+
console.log(`[${id}] Delegating to root store, marking sync needed.`);
|
|
209
|
+
this.needsSync = true;
|
|
210
|
+
this.lastSync = null; // Clear last sync since we're not actually syncing
|
|
211
|
+
this.setOperations(this.getInflightOperations());
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// We're in independent mode - proceed with normal sync
|
|
178
216
|
this.isSyncing = true;
|
|
179
217
|
console.log(`[${id}] Starting sync...`);
|
|
180
218
|
try {
|
|
181
219
|
const response = await this.fetchFn({
|
|
182
220
|
ast: this.queryset.build(),
|
|
183
|
-
modelClass: this.modelClass
|
|
221
|
+
modelClass: this.modelClass,
|
|
184
222
|
});
|
|
185
223
|
const { data, included } = response;
|
|
186
224
|
console.log(`[${id}] Sync fetch completed. Received: ${JSON.stringify(data)}.`);
|
|
@@ -188,10 +226,13 @@ export class QuerysetStore {
|
|
|
188
226
|
processIncludedEntities(modelStoreRegistry, included, this.modelClass);
|
|
189
227
|
this.setGroundTruth(data);
|
|
190
228
|
this.setOperations(this.getInflightOperations());
|
|
229
|
+
this.lastSync = Date.now();
|
|
230
|
+
this.needsSync = false;
|
|
191
231
|
console.log(`[${id}] Sync completed.`);
|
|
192
232
|
}
|
|
193
233
|
catch (e) {
|
|
194
234
|
console.error(`[${id}] Failed to sync ground truth:`, e);
|
|
235
|
+
this.needsSync = true; // Mark as needing sync on error
|
|
195
236
|
}
|
|
196
237
|
finally {
|
|
197
238
|
this.isSyncing = false;
|
package/package.json
CHANGED