@statezero/core 0.1.2 → 0.1.3-9.2
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/LICENSE +3 -2
- package/dist/cli/commands/sync.d.ts +6 -0
- package/dist/cli/commands/sync.js +30 -0
- package/dist/cli/commands/syncActions.d.ts +46 -0
- package/dist/cli/commands/syncActions.js +623 -0
- package/dist/cli/commands/syncModels.js +17 -35
- package/dist/cli/index.js +18 -10
- package/dist/config.d.ts +5 -0
- package/dist/config.js +40 -10
- package/dist/flavours/django/dates.js +3 -3
- package/dist/flavours/django/files.d.ts +8 -7
- package/dist/flavours/django/files.js +36 -2
- package/dist/flavours/django/model.d.ts +15 -0
- package/dist/flavours/django/model.js +143 -24
- package/dist/setup.js +11 -0
- package/dist/syncEngine/registries/metricRegistry.d.ts +5 -0
- package/dist/syncEngine/registries/metricRegistry.js +8 -0
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +21 -0
- package/dist/syncEngine/registries/querysetStoreGraph.js +95 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +14 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +64 -16
- package/dist/syncEngine/stores/modelStore.d.ts +3 -0
- package/dist/syncEngine/stores/modelStore.js +76 -41
- package/dist/syncEngine/stores/querysetStore.d.ts +19 -0
- package/dist/syncEngine/stores/querysetStore.js +133 -18
- package/dist/syncEngine/sync.d.ts +5 -0
- package/dist/syncEngine/sync.js +61 -5
- package/package.json +126 -123
- package/readme.md +1 -1
package/dist/setup.js
CHANGED
|
@@ -2,6 +2,9 @@ import { configInstance } from "./config.js";
|
|
|
2
2
|
import { setAdapters } from "./reactiveAdaptor.js";
|
|
3
3
|
import { syncManager } from "./syncEngine/sync.js";
|
|
4
4
|
import { initEventHandler } from "./syncEngine/stores/operationEventHandlers.js";
|
|
5
|
+
import { querysetStoreRegistry } from "./syncEngine/registries/querysetStoreRegistry.js";
|
|
6
|
+
import { modelStoreRegistry } from "./syncEngine/registries/modelStoreRegistry.js";
|
|
7
|
+
import { metricRegistry } from "./syncEngine/registries/metricRegistry.js";
|
|
5
8
|
/**
|
|
6
9
|
* Initialize StateZero with the provided configuration
|
|
7
10
|
*
|
|
@@ -19,4 +22,12 @@ export function setupStateZero(config, getModelClass, adapters) {
|
|
|
19
22
|
setAdapters(adapters.ModelAdaptor, adapters.QuerySetAdaptor, adapters.MetricAdaptor);
|
|
20
23
|
initEventHandler();
|
|
21
24
|
syncManager.initialize();
|
|
25
|
+
// Expose registries and sync manager for devtools de
|
|
26
|
+
if (typeof window !== "undefined") {
|
|
27
|
+
window.__STATEZERO_DEVTOOLS__ = {
|
|
28
|
+
querysetStoreRegistry,
|
|
29
|
+
modelStoreRegistry,
|
|
30
|
+
metricRegistry,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
22
33
|
}
|
|
@@ -8,6 +8,11 @@ export class LiveMetric {
|
|
|
8
8
|
metricType: any;
|
|
9
9
|
field: any;
|
|
10
10
|
get lqs(): import("./querysetStoreRegistry").LiveQueryset;
|
|
11
|
+
/**
|
|
12
|
+
* Refresh the metric data from the database
|
|
13
|
+
* Delegates to the underlying store's sync method
|
|
14
|
+
*/
|
|
15
|
+
refreshFromDb(): any;
|
|
11
16
|
/**
|
|
12
17
|
* Getter that always returns the current value from the store
|
|
13
18
|
*/
|
|
@@ -17,6 +17,14 @@ export class LiveMetric {
|
|
|
17
17
|
get lqs() {
|
|
18
18
|
return querysetStoreRegistry.getEntity(this.queryset);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Refresh the metric data from the database
|
|
22
|
+
* Delegates to the underlying store's sync method
|
|
23
|
+
*/
|
|
24
|
+
refreshFromDb() {
|
|
25
|
+
const store = metricRegistry.getStore(this.metricType, this.queryset, this.field);
|
|
26
|
+
return store.sync();
|
|
27
|
+
}
|
|
20
28
|
/**
|
|
21
29
|
* Getter that always returns the current value from the store
|
|
22
30
|
*/
|
|
@@ -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,95 @@
|
|
|
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
|
+
if (currentKey !== parentKey) {
|
|
33
|
+
this.graph.setEdge(currentKey, parentKey);
|
|
34
|
+
}
|
|
35
|
+
current = current.__parent;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Find the root store for a queryset
|
|
44
|
+
* @param {Object} queryset - The queryset to analyze
|
|
45
|
+
* @returns {Object} { isRoot: boolean, root: semanticKey|null }
|
|
46
|
+
*/
|
|
47
|
+
findRoot(queryset) {
|
|
48
|
+
// Validate input - null/undefined is a programming error
|
|
49
|
+
if (!queryset) {
|
|
50
|
+
throw new Error("findRoot was called with a null object, instead of a queryset");
|
|
51
|
+
}
|
|
52
|
+
// Handle queryset without semanticKey
|
|
53
|
+
if (!queryset.semanticKey) {
|
|
54
|
+
throw new Error("findRoot was called on an object without a semanticKey, which means its not a queryset. findRoot only works on querysets");
|
|
55
|
+
}
|
|
56
|
+
const semanticKey = queryset.semanticKey;
|
|
57
|
+
if (!this.graph.hasNode(semanticKey)) {
|
|
58
|
+
this.addQueryset(queryset);
|
|
59
|
+
}
|
|
60
|
+
// Traverse ALL the way up to find the HIGHEST ancestor with a store
|
|
61
|
+
const visited = new Set();
|
|
62
|
+
let current = semanticKey;
|
|
63
|
+
let highestAncestorWithStore = null;
|
|
64
|
+
while (current && !visited.has(current)) {
|
|
65
|
+
visited.add(current);
|
|
66
|
+
// Check if current node has a store
|
|
67
|
+
if (this.hasStoreFn(current)) {
|
|
68
|
+
highestAncestorWithStore = current;
|
|
69
|
+
}
|
|
70
|
+
// Move to parent
|
|
71
|
+
const parents = this.graph.successors(current) || [];
|
|
72
|
+
if (parents.length > 0) {
|
|
73
|
+
current = parents[0]; // Follow the parent chain
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
break; // No more parents
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (highestAncestorWithStore) {
|
|
80
|
+
if (highestAncestorWithStore === semanticKey) {
|
|
81
|
+
// This queryset itself is the highest with a store
|
|
82
|
+
return { isRoot: true, root: semanticKey };
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Found a higher ancestor with a store
|
|
86
|
+
return { isRoot: false, root: highestAncestorWithStore };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// No stores found anywhere in the chain
|
|
90
|
+
return { isRoot: true, root: null };
|
|
91
|
+
}
|
|
92
|
+
clear() {
|
|
93
|
+
this.graph = new Graph({ directed: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -9,6 +9,11 @@ export class LiveQueryset {
|
|
|
9
9
|
* Serializes the lqs as a simple array of objects, for freezing e.g in the metric stores
|
|
10
10
|
*/
|
|
11
11
|
serialize(): any;
|
|
12
|
+
/**
|
|
13
|
+
* Refresh the queryset data from the database
|
|
14
|
+
* Delegates to the underlying store's sync method
|
|
15
|
+
*/
|
|
16
|
+
refreshFromDb(): any;
|
|
12
17
|
/**
|
|
13
18
|
* Get the current items from the store
|
|
14
19
|
* @private
|
|
@@ -23,6 +28,7 @@ declare class QuerysetStoreRegistry {
|
|
|
23
28
|
_tempStores: WeakMap<object, any>;
|
|
24
29
|
followingQuerysets: Map<any, any>;
|
|
25
30
|
syncManager: () => void;
|
|
31
|
+
querysetStoreGraph: QuerysetStoreGraph;
|
|
26
32
|
clear(): void;
|
|
27
33
|
setSyncManager(syncManager: any): void;
|
|
28
34
|
/**
|
|
@@ -30,6 +36,13 @@ declare class QuerysetStoreRegistry {
|
|
|
30
36
|
*/
|
|
31
37
|
addFollowingQueryset(semanticKey: any, queryset: any): void;
|
|
32
38
|
getStore(queryset: any, seed?: boolean): any;
|
|
39
|
+
/**
|
|
40
|
+
* Function to return the root store for a queryset
|
|
41
|
+
*/
|
|
42
|
+
getRootStore(queryset: any): {
|
|
43
|
+
isRoot: boolean;
|
|
44
|
+
rootStore: any;
|
|
45
|
+
};
|
|
33
46
|
/**
|
|
34
47
|
* Get the current state of the queryset, wrapped in a LiveQueryset
|
|
35
48
|
* @param {Object} queryset - The queryset
|
|
@@ -52,4 +65,5 @@ declare class QuerysetStoreRegistry {
|
|
|
52
65
|
*/
|
|
53
66
|
getAllStoresForModel(ModelClass: any): any[];
|
|
54
67
|
}
|
|
68
|
+
import { QuerysetStoreGraph } from './querysetStoreGraph.js';
|
|
55
69
|
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';
|
|
@@ -40,28 +41,37 @@ export class LiveQueryset {
|
|
|
40
41
|
__classPrivateFieldSet(this, _LiveQueryset_proxy, new Proxy(__classPrivateFieldGet(this, _LiveQueryset_array, "f"), {
|
|
41
42
|
get: (target, prop, receiver) => {
|
|
42
43
|
// Expose the touch method through the proxy
|
|
43
|
-
if (prop ===
|
|
44
|
+
if (prop === "touch") {
|
|
44
45
|
return () => this.touch();
|
|
45
46
|
}
|
|
46
|
-
if (prop ===
|
|
47
|
+
if (prop === "serialize") {
|
|
47
48
|
return () => this.serialize();
|
|
48
49
|
}
|
|
49
50
|
// Special handling for iterators and common array methods
|
|
50
51
|
if (prop === Symbol.iterator) {
|
|
51
52
|
return () => this.getCurrentItems()[Symbol.iterator]();
|
|
52
53
|
}
|
|
53
|
-
else if (typeof prop ===
|
|
54
|
+
else if (typeof prop === "string" &&
|
|
55
|
+
[
|
|
56
|
+
"forEach",
|
|
57
|
+
"map",
|
|
58
|
+
"filter",
|
|
59
|
+
"reduce",
|
|
60
|
+
"some",
|
|
61
|
+
"every",
|
|
62
|
+
"find",
|
|
63
|
+
].includes(prop)) {
|
|
54
64
|
return (...args) => this.getCurrentItems()[prop](...args);
|
|
55
65
|
}
|
|
56
|
-
else if (prop ===
|
|
66
|
+
else if (prop === "length") {
|
|
57
67
|
return this.getCurrentItems().length;
|
|
58
68
|
}
|
|
59
|
-
else if (typeof prop ===
|
|
69
|
+
else if (typeof prop === "string" && !isNaN(parseInt(prop))) {
|
|
60
70
|
// Handle numeric indices
|
|
61
71
|
return this.getCurrentItems()[prop];
|
|
62
72
|
}
|
|
63
73
|
return target[prop];
|
|
64
|
-
}
|
|
74
|
+
},
|
|
65
75
|
}), "f");
|
|
66
76
|
return __classPrivateFieldGet(this, _LiveQueryset_proxy, "f");
|
|
67
77
|
}
|
|
@@ -73,30 +83,37 @@ export class LiveQueryset {
|
|
|
73
83
|
// Get the current primary keys from the store
|
|
74
84
|
const pks = store.render();
|
|
75
85
|
// Map primary keys to full model objects
|
|
76
|
-
return pks.map(pk => {
|
|
86
|
+
return pks.map((pk) => {
|
|
77
87
|
// Get the full model instance from the model store
|
|
78
88
|
const pkField = __classPrivateFieldGet(this, _LiveQueryset_ModelClass, "f").primaryKeyField;
|
|
79
89
|
return __classPrivateFieldGet(this, _LiveQueryset_ModelClass, "f").fromPk(pk, __classPrivateFieldGet(this, _LiveQueryset_queryset, "f")).serialize();
|
|
80
90
|
});
|
|
81
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Refresh the queryset data from the database
|
|
94
|
+
* Delegates to the underlying store's sync method
|
|
95
|
+
*/
|
|
96
|
+
refreshFromDb() {
|
|
97
|
+
const store = querysetStoreRegistry.getStore(__classPrivateFieldGet(this, _LiveQueryset_queryset, "f"));
|
|
98
|
+
return store.sync();
|
|
99
|
+
}
|
|
82
100
|
/**
|
|
83
101
|
* Get the current items from the store
|
|
84
102
|
* @private
|
|
85
103
|
* @returns {Array} The current items in the queryset
|
|
86
104
|
*/
|
|
87
|
-
getCurrentItems(
|
|
105
|
+
getCurrentItems() {
|
|
88
106
|
const store = querysetStoreRegistry.getStore(__classPrivateFieldGet(this, _LiveQueryset_queryset, "f"));
|
|
89
107
|
// Get the current primary keys from the store
|
|
90
108
|
const pks = store.render();
|
|
91
109
|
// Map primary keys to full model objects
|
|
92
|
-
const instances = pks
|
|
110
|
+
const instances = pks
|
|
111
|
+
.map((pk) => {
|
|
93
112
|
// Get the full model instance from the model store
|
|
94
113
|
const pkField = __classPrivateFieldGet(this, _LiveQueryset_ModelClass, "f").primaryKeyField;
|
|
95
114
|
return __classPrivateFieldGet(this, _LiveQueryset_ModelClass, "f").fromPk(pk, __classPrivateFieldGet(this, _LiveQueryset_queryset, "f"));
|
|
96
115
|
});
|
|
97
|
-
|
|
98
|
-
return instances;
|
|
99
|
-
return filter(instances, __classPrivateFieldGet(this, _LiveQueryset_queryset, "f").build(), __classPrivateFieldGet(this, _LiveQueryset_ModelClass, "f"), true);
|
|
116
|
+
return instances;
|
|
100
117
|
}
|
|
101
118
|
}
|
|
102
119
|
_LiveQueryset_queryset = new WeakMap(), _LiveQueryset_ModelClass = new WeakMap(), _LiveQueryset_proxy = new WeakMap(), _LiveQueryset_array = new WeakMap();
|
|
@@ -106,6 +123,9 @@ class QuerysetStoreRegistry {
|
|
|
106
123
|
this._tempStores = new WeakMap(); // WeakMap<Queryset, Store>
|
|
107
124
|
this.followingQuerysets = new Map(); // Map<semanticKey, Set<queryset>>
|
|
108
125
|
this.syncManager = () => { console.warn("SyncManager not set for QuerysetStoreRegistry"); };
|
|
126
|
+
this.querysetStoreGraph = new QuerysetStoreGraph((semanticKey) => {
|
|
127
|
+
return this._stores.has(semanticKey);
|
|
128
|
+
});
|
|
109
129
|
}
|
|
110
130
|
clear() {
|
|
111
131
|
this._stores.forEach((store) => {
|
|
@@ -113,6 +133,7 @@ class QuerysetStoreRegistry {
|
|
|
113
133
|
});
|
|
114
134
|
this._stores = new Map();
|
|
115
135
|
this.followingQuerysets = new Map();
|
|
136
|
+
this.querysetStoreGraph.clear();
|
|
116
137
|
}
|
|
117
138
|
setSyncManager(syncManager) {
|
|
118
139
|
this.syncManager = syncManager;
|
|
@@ -130,6 +151,7 @@ class QuerysetStoreRegistry {
|
|
|
130
151
|
if (isNil(queryset) || isNil(queryset.ModelClass)) {
|
|
131
152
|
throw new Error("QuerysetStoreRegistry.getStore requires a valid queryset");
|
|
132
153
|
}
|
|
154
|
+
this.querysetStoreGraph.addQueryset(queryset);
|
|
133
155
|
// Check if we already have a temporary store for this exact QuerySet instance
|
|
134
156
|
if (this._tempStores.has(queryset)) {
|
|
135
157
|
return this._tempStores.get(queryset);
|
|
@@ -156,17 +178,39 @@ class QuerysetStoreRegistry {
|
|
|
156
178
|
let ast = queryset.build();
|
|
157
179
|
let ModelClass = queryset.ModelClass;
|
|
158
180
|
if (queryset.__parent && seed) {
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
const parentKey = queryset.__parent.semanticKey;
|
|
182
|
+
if (this._stores.has(parentKey)) {
|
|
183
|
+
let parentLiveQuerySet = this.getEntity(queryset.__parent);
|
|
184
|
+
initialGroundTruthPks = filter(parentLiveQuerySet, ast, ModelClass, false);
|
|
185
|
+
}
|
|
161
186
|
}
|
|
162
187
|
// Get the parent registry
|
|
163
188
|
const store = new QuerysetStore(ModelClass, fetchQueryset, queryset, initialGroundTruthPks, // Initial ground truth PKs
|
|
164
|
-
null // Initial operations
|
|
165
|
-
|
|
189
|
+
null, // Initial operations
|
|
190
|
+
{
|
|
191
|
+
getRootStore: this.getRootStore.bind(this),
|
|
192
|
+
isTemp: true,
|
|
193
|
+
});
|
|
166
194
|
// Store it in the temp store map
|
|
167
195
|
this._tempStores.set(queryset, store);
|
|
168
196
|
return store;
|
|
169
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Function to return the root store for a queryset
|
|
200
|
+
*/
|
|
201
|
+
getRootStore(queryset) {
|
|
202
|
+
if (isNil(queryset)) {
|
|
203
|
+
throw new Error("QuerysetStoreRegistry.getRootStore requires a valid queryset");
|
|
204
|
+
}
|
|
205
|
+
const { isRoot, root } = this.querysetStoreGraph.findRoot(queryset);
|
|
206
|
+
const rootStore = this._stores.get(root);
|
|
207
|
+
if (!isRoot && rootStore) {
|
|
208
|
+
return { isRoot: false, rootStore: rootStore };
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
return { isRoot: true, rootStore: null };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
170
214
|
/**
|
|
171
215
|
* Get the current state of the queryset, wrapped in a LiveQueryset
|
|
172
216
|
* @param {Object} queryset - The queryset
|
|
@@ -183,12 +227,14 @@ class QuerysetStoreRegistry {
|
|
|
183
227
|
// If we have a temporary store, promote it
|
|
184
228
|
if (this._tempStores.has(queryset)) {
|
|
185
229
|
store = this._tempStores.get(queryset);
|
|
230
|
+
store.isTemp = false; // Promote to permanent store
|
|
186
231
|
this._stores.set(semanticKey, store);
|
|
187
232
|
this.syncManager.followModel(this, queryset.ModelClass);
|
|
188
233
|
}
|
|
189
234
|
// Otherwise, ensure we have a permanent store
|
|
190
235
|
else if (!this._stores.has(semanticKey)) {
|
|
191
236
|
store = this.getStore(queryset, seed);
|
|
237
|
+
store.isTemp = false;
|
|
192
238
|
this._stores.set(semanticKey, store);
|
|
193
239
|
this.syncManager.followModel(this, queryset.ModelClass);
|
|
194
240
|
}
|
|
@@ -219,11 +265,13 @@ class QuerysetStoreRegistry {
|
|
|
219
265
|
// If we have a temp store, promote it
|
|
220
266
|
if (this._tempStores.has(queryset)) {
|
|
221
267
|
store = this._tempStores.get(queryset);
|
|
268
|
+
store.isTemp = false; // Promote to permanent store
|
|
222
269
|
this._stores.set(semanticKey, store);
|
|
223
270
|
}
|
|
224
271
|
else {
|
|
225
272
|
// Create a new permanent store
|
|
226
273
|
store = this.getStore(queryset);
|
|
274
|
+
store.isTemp = false;
|
|
227
275
|
this._stores.set(semanticKey, store);
|
|
228
276
|
}
|
|
229
277
|
}
|
|
@@ -7,6 +7,9 @@ export class ModelStore {
|
|
|
7
7
|
isSyncing: boolean;
|
|
8
8
|
pruneThreshold: any;
|
|
9
9
|
modelCache: Cache;
|
|
10
|
+
_lastRenderedData: Map<any, any>;
|
|
11
|
+
renderCallbacks: Set<any>;
|
|
12
|
+
registerRenderCallback(callback: any): () => boolean;
|
|
10
13
|
/**
|
|
11
14
|
* Load operations from data and add them to the operations map,
|
|
12
15
|
* reusing existing operations from the registry if they exist
|