@statezero/core 0.1.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/adaptors/react/composables.d.ts +1 -0
- package/dist/adaptors/react/composables.js +4 -0
- package/dist/adaptors/react/index.d.ts +1 -0
- package/dist/adaptors/react/index.js +1 -0
- package/dist/adaptors/vue/composables.d.ts +2 -0
- package/dist/adaptors/vue/composables.js +36 -0
- package/dist/adaptors/vue/index.d.ts +2 -0
- package/dist/adaptors/vue/index.js +2 -0
- package/dist/adaptors/vue/reactivity.d.ts +18 -0
- package/dist/adaptors/vue/reactivity.js +125 -0
- package/dist/cli/commands/syncModels.d.ts +132 -0
- package/dist/cli/commands/syncModels.js +1040 -0
- package/dist/cli/configFileLoader.d.ts +10 -0
- package/dist/cli/configFileLoader.js +85 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +14 -0
- package/dist/config.d.ts +52 -0
- package/dist/config.js +242 -0
- package/dist/core/eventReceivers.d.ts +179 -0
- package/dist/core/eventReceivers.js +210 -0
- package/dist/core/utils.d.ts +8 -0
- package/dist/core/utils.js +62 -0
- package/dist/filtering/localFiltering.d.ts +116 -0
- package/dist/filtering/localFiltering.js +834 -0
- package/dist/flavours/django/dates.d.ts +33 -0
- package/dist/flavours/django/dates.js +99 -0
- package/dist/flavours/django/errors.d.ts +138 -0
- package/dist/flavours/django/errors.js +187 -0
- package/dist/flavours/django/f.d.ts +6 -0
- package/dist/flavours/django/f.js +91 -0
- package/dist/flavours/django/files.d.ts +76 -0
- package/dist/flavours/django/files.js +338 -0
- package/dist/flavours/django/makeApiCall.d.ts +20 -0
- package/dist/flavours/django/makeApiCall.js +169 -0
- package/dist/flavours/django/manager.d.ts +197 -0
- package/dist/flavours/django/manager.js +222 -0
- package/dist/flavours/django/model.d.ts +112 -0
- package/dist/flavours/django/model.js +253 -0
- package/dist/flavours/django/operationFactory.d.ts +65 -0
- package/dist/flavours/django/operationFactory.js +216 -0
- package/dist/flavours/django/q.d.ts +70 -0
- package/dist/flavours/django/q.js +43 -0
- package/dist/flavours/django/queryExecutor.d.ts +131 -0
- package/dist/flavours/django/queryExecutor.js +468 -0
- package/dist/flavours/django/querySet.d.ts +412 -0
- package/dist/flavours/django/querySet.js +601 -0
- package/dist/flavours/django/tempPk.d.ts +19 -0
- package/dist/flavours/django/tempPk.js +48 -0
- package/dist/flavours/django/utils.d.ts +19 -0
- package/dist/flavours/django/utils.js +29 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +38 -0
- package/dist/react-entry.d.ts +2 -0
- package/dist/react-entry.js +2 -0
- package/dist/reactiveAdaptor.d.ts +24 -0
- package/dist/reactiveAdaptor.js +38 -0
- package/dist/setup.d.ts +15 -0
- package/dist/setup.js +22 -0
- package/dist/syncEngine/cache/cache.d.ts +75 -0
- package/dist/syncEngine/cache/cache.js +341 -0
- package/dist/syncEngine/metrics/metricOptCalcs.d.ts +79 -0
- package/dist/syncEngine/metrics/metricOptCalcs.js +284 -0
- package/dist/syncEngine/registries/metricRegistry.d.ts +53 -0
- package/dist/syncEngine/registries/metricRegistry.js +162 -0
- package/dist/syncEngine/registries/modelStoreRegistry.d.ts +11 -0
- package/dist/syncEngine/registries/modelStoreRegistry.js +56 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +55 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +244 -0
- package/dist/syncEngine/stores/metricStore.d.ts +55 -0
- package/dist/syncEngine/stores/metricStore.js +222 -0
- package/dist/syncEngine/stores/modelStore.d.ts +40 -0
- package/dist/syncEngine/stores/modelStore.js +405 -0
- package/dist/syncEngine/stores/operation.d.ts +99 -0
- package/dist/syncEngine/stores/operation.js +224 -0
- package/dist/syncEngine/stores/operationEventHandlers.d.ts +8 -0
- package/dist/syncEngine/stores/operationEventHandlers.js +239 -0
- package/dist/syncEngine/stores/querysetStore.d.ts +32 -0
- package/dist/syncEngine/stores/querysetStore.js +200 -0
- package/dist/syncEngine/stores/reactivity.d.ts +3 -0
- package/dist/syncEngine/stores/reactivity.js +4 -0
- package/dist/syncEngine/stores/utils.d.ts +14 -0
- package/dist/syncEngine/stores/utils.js +32 -0
- package/dist/syncEngine/sync.d.ts +32 -0
- package/dist/syncEngine/sync.js +169 -0
- package/dist/vue-entry.d.ts +6 -0
- package/dist/vue-entry.js +2 -0
- package/license.md +116 -0
- package/package.json +123 -0
- package/readme.md +222 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _Operation__instances, _Operation__frozenInstances;
|
|
13
|
+
import { MAX, v7 as uuidv7 } from "uuid";
|
|
14
|
+
import { isNil } from 'lodash-es';
|
|
15
|
+
import mitt from 'mitt';
|
|
16
|
+
import { tempPkMap } from "../../flavours/django/tempPk";
|
|
17
|
+
export const operationEvents = mitt();
|
|
18
|
+
export const Status = {
|
|
19
|
+
CREATED: 'operation:created',
|
|
20
|
+
UPDATED: 'operation:updated',
|
|
21
|
+
CONFIRMED: 'operation:confirmed',
|
|
22
|
+
REJECTED: 'operation:rejected',
|
|
23
|
+
CLEAR: 'clear:all',
|
|
24
|
+
MUTATED: 'operation:mutated'
|
|
25
|
+
};
|
|
26
|
+
export const Type = {
|
|
27
|
+
CREATE: 'create',
|
|
28
|
+
UPDATE: 'update',
|
|
29
|
+
DELETE: 'delete',
|
|
30
|
+
UPDATE_INSTANCE: 'update_instance',
|
|
31
|
+
DELETE_INSTANCE: 'delete_instance',
|
|
32
|
+
GET_OR_CREATE: 'get_or_create',
|
|
33
|
+
UPDATE_OR_CREATE: 'update_or_create',
|
|
34
|
+
CHECKPOINT: 'checkpoint',
|
|
35
|
+
// Aggregation operations
|
|
36
|
+
COUNT: 'count',
|
|
37
|
+
MIN: 'min',
|
|
38
|
+
MAX: 'max',
|
|
39
|
+
AVG: 'avg',
|
|
40
|
+
SUM: 'sum',
|
|
41
|
+
AGGREGATE: 'aggregate',
|
|
42
|
+
};
|
|
43
|
+
export class Operation {
|
|
44
|
+
constructor(data, restore = false) {
|
|
45
|
+
_Operation__instances.set(this, void 0);
|
|
46
|
+
_Operation__frozenInstances.set(this, void 0);
|
|
47
|
+
if (!data || typeof data !== 'object') {
|
|
48
|
+
throw new Error("Operation constructor requires a data object.");
|
|
49
|
+
}
|
|
50
|
+
if (!data.type) {
|
|
51
|
+
throw new Error("Operation data must include a 'type'.");
|
|
52
|
+
}
|
|
53
|
+
if (!data.instances) {
|
|
54
|
+
throw new Error("Operation data must include 'instances'.");
|
|
55
|
+
}
|
|
56
|
+
this.operationId = data.operationId || `op_${uuidv7()}`;
|
|
57
|
+
this.type = data.type;
|
|
58
|
+
this.status = data.status || Status.CREATED;
|
|
59
|
+
this.queryset = data.queryset;
|
|
60
|
+
this.args = data.args;
|
|
61
|
+
this.doNotPropagate = data.doNotPropagate || false;
|
|
62
|
+
let ModelClass = this.queryset.ModelClass;
|
|
63
|
+
let instances = data.instances;
|
|
64
|
+
// guarantee instances is an array
|
|
65
|
+
if (!isNil(instances)) {
|
|
66
|
+
instances = Array.isArray(data.instances) ? data.instances : [data.instances];
|
|
67
|
+
}
|
|
68
|
+
// coerce to object format if its not already
|
|
69
|
+
let pkField = ModelClass.primaryKeyField;
|
|
70
|
+
if (instances.some(instance => isNil(instance) || typeof instance !== 'object' || !(pkField in instance))) {
|
|
71
|
+
throw new Error(`All operation instances must be objects with the '${pkField}' field`);
|
|
72
|
+
}
|
|
73
|
+
__classPrivateFieldSet(this, _Operation__instances, instances, "f");
|
|
74
|
+
__classPrivateFieldSet(this, _Operation__frozenInstances, instances.map(i => ModelClass.fromPk(i[ModelClass.primaryKeyField]).serialize()), "f");
|
|
75
|
+
this.timestamp = data.timestamp || Date.now();
|
|
76
|
+
if (restore)
|
|
77
|
+
return;
|
|
78
|
+
operationRegistry.register(this);
|
|
79
|
+
// Emit operation created event with the entire operation
|
|
80
|
+
operationEvents.emit(Status.CREATED, this);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Getter for instances that replaces any temporary PKs with real PKs
|
|
84
|
+
*/
|
|
85
|
+
get instances() {
|
|
86
|
+
if (tempPkMap.size === 0)
|
|
87
|
+
return __classPrivateFieldGet(this, _Operation__instances, "f");
|
|
88
|
+
const ModelClass = this.queryset.ModelClass;
|
|
89
|
+
const pkField = ModelClass.primaryKeyField;
|
|
90
|
+
return __classPrivateFieldGet(this, _Operation__instances, "f").map(instance => {
|
|
91
|
+
const pk = instance[pkField];
|
|
92
|
+
if (typeof pk === 'string' && tempPkMap.has(pk.replace(/[{}]/g, ''))) {
|
|
93
|
+
// Return a new instance with the real PK
|
|
94
|
+
return {
|
|
95
|
+
...instance,
|
|
96
|
+
[pkField]: tempPkMap.get(pk.replace(/[{}]/g, ''))
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return instance;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Setter for instances
|
|
104
|
+
*/
|
|
105
|
+
set instances(value) {
|
|
106
|
+
__classPrivateFieldSet(this, _Operation__instances, Array.isArray(value) ? value : [value], "f");
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Getter for frozenInstances that replaces any temporary PKs with real PKs
|
|
110
|
+
*/
|
|
111
|
+
get frozenInstances() {
|
|
112
|
+
if (tempPkMap.size === 0)
|
|
113
|
+
return __classPrivateFieldGet(this, _Operation__frozenInstances, "f");
|
|
114
|
+
const ModelClass = this.queryset.ModelClass;
|
|
115
|
+
const pkField = ModelClass.primaryKeyField;
|
|
116
|
+
return __classPrivateFieldGet(this, _Operation__frozenInstances, "f").map(instance => {
|
|
117
|
+
const pk = instance[pkField];
|
|
118
|
+
if (typeof pk === 'string' && tempPkMap.has(pk.replace(/[{}]/g, ''))) {
|
|
119
|
+
// Return a new instance with the real PK
|
|
120
|
+
return {
|
|
121
|
+
...instance,
|
|
122
|
+
[pkField]: tempPkMap.get(pk.replace(/[{}]/g, ''))
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return instance;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Setter for frozenInstances
|
|
130
|
+
*/
|
|
131
|
+
set frozenInstances(value) {
|
|
132
|
+
__classPrivateFieldSet(this, _Operation__frozenInstances, Array.isArray(value) ? value : [value], "f");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get primary keys of all instances in this operation
|
|
136
|
+
* Returns primary keys as simple values (not objects)
|
|
137
|
+
*/
|
|
138
|
+
get instancePks() {
|
|
139
|
+
const pkField = this.queryset.ModelClass.primaryKeyField;
|
|
140
|
+
return this.instances.map(instance => instance[pkField]);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Update this operation's status and emit an event
|
|
144
|
+
* @param {string} status - New status ('confirmed', 'rejected', etc.)
|
|
145
|
+
* @param {Array|Object|null} [instances=null] - New instances for the operation
|
|
146
|
+
*/
|
|
147
|
+
updateStatus(status, instances = null) {
|
|
148
|
+
this.status = status;
|
|
149
|
+
this.timestamp = Date.now();
|
|
150
|
+
if (instances !== null) {
|
|
151
|
+
this.instances = Array.isArray(instances) ? instances : [instances];
|
|
152
|
+
}
|
|
153
|
+
operationEvents.emit(status, this);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Updates this operation with new data and emits the appropriate event
|
|
157
|
+
* @param {Object} newData - New data to update the operation with
|
|
158
|
+
* @returns {Operation} - Returns this operation instance for chaining
|
|
159
|
+
*/
|
|
160
|
+
mutate(newData) {
|
|
161
|
+
// Ensure instances is always an array
|
|
162
|
+
if (newData.instances && !Array.isArray(newData.instances)) {
|
|
163
|
+
newData.instances = [newData.instances];
|
|
164
|
+
}
|
|
165
|
+
// Use Object.assign to update all properties at once
|
|
166
|
+
Object.assign(this, newData);
|
|
167
|
+
// Update timestamp
|
|
168
|
+
this.timestamp = Date.now();
|
|
169
|
+
// Emit the mutated event with the updated operation
|
|
170
|
+
operationEvents.emit(Status.MUTATED, this);
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
_Operation__instances = new WeakMap(), _Operation__frozenInstances = new WeakMap();
|
|
175
|
+
class OperationRegistry {
|
|
176
|
+
constructor() {
|
|
177
|
+
this._operations = new Map();
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Registers a pre-constructed Operation instance in the registry.
|
|
181
|
+
* Ensures the operationId is unique within the registry.
|
|
182
|
+
* Throws an Error if the operationId already exists.
|
|
183
|
+
*
|
|
184
|
+
* @param {Operation} operation - The fully instantiated Operation object to register.
|
|
185
|
+
* @throws {Error} If the input is not a valid operation object or if an operation with the same operationId already exists.
|
|
186
|
+
*/
|
|
187
|
+
register(operation) {
|
|
188
|
+
if (!(operation instanceof Operation)) {
|
|
189
|
+
throw new Error("OperationRegistry.register requires an Operation object.");
|
|
190
|
+
}
|
|
191
|
+
// Ensure the ID is unique before registering
|
|
192
|
+
if (this._operations.has(operation.operationId)) {
|
|
193
|
+
// Throw an error if the ID is already used.
|
|
194
|
+
console.warn(`OperationId ${operation.operationId} is already used, overriding existing operation!`);
|
|
195
|
+
}
|
|
196
|
+
// Register the provided operation object
|
|
197
|
+
this._operations.set(operation.operationId, operation);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Retrieves an operation by its ID.
|
|
201
|
+
* @param {string} operationId - The ID of the operation.
|
|
202
|
+
* @returns {Operation | undefined} The operation instance or undefined if not found.
|
|
203
|
+
*/
|
|
204
|
+
get(operationId) {
|
|
205
|
+
return this._operations.get(operationId);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Checks if an operation with the given ID exists in the registry.
|
|
209
|
+
* @param {string} operationId - The ID of the operation to check.
|
|
210
|
+
* @returns {boolean} True if the operation exists, false otherwise.
|
|
211
|
+
*/
|
|
212
|
+
has(operationId) {
|
|
213
|
+
return this._operations.has(operationId);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Clears all operations from the registry.
|
|
217
|
+
*/
|
|
218
|
+
clear() {
|
|
219
|
+
console.log("OperationRegistry: Clearing all operations.");
|
|
220
|
+
this._operations.clear();
|
|
221
|
+
operationEvents.emit(Status.CLEAR);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
export const operationRegistry = new OperationRegistry();
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { operationEvents, Status, Type } from './operation.js';
|
|
2
|
+
import { modelStoreRegistry } from '../registries/modelStoreRegistry.js';
|
|
3
|
+
import { querysetStoreRegistry } from '../registries/querysetStoreRegistry.js';
|
|
4
|
+
import { metricRegistry } from '../registries/metricRegistry.js';
|
|
5
|
+
import { getFingerprint } from './utils.js';
|
|
6
|
+
import { QuerySet } from '../../flavours/django/querySet.js';
|
|
7
|
+
import { isEqual, isNil } from 'lodash-es';
|
|
8
|
+
import hash from 'object-hash';
|
|
9
|
+
/**
|
|
10
|
+
* Returns querysets that have the same nodes as any ancestor of the specific queryset
|
|
11
|
+
* @param {QuerySet} queryset
|
|
12
|
+
* @returns {Map<QuerySet, Store>}
|
|
13
|
+
*/
|
|
14
|
+
function relatedQuerysets(queryset) {
|
|
15
|
+
// Collect ancestor nodes for comparison
|
|
16
|
+
let ancestorNodes = [];
|
|
17
|
+
let current = queryset;
|
|
18
|
+
while (current) {
|
|
19
|
+
ancestorNodes.push(current.nodes);
|
|
20
|
+
current = current.__parent;
|
|
21
|
+
}
|
|
22
|
+
const modelClass = queryset.ModelClass;
|
|
23
|
+
const result = new Map();
|
|
24
|
+
Array.from(querysetStoreRegistry._stores.entries()).forEach(([queryset, store]) => {
|
|
25
|
+
if (store.modelClass !== modelClass)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
if (ancestorNodes.some(nodes => isEqual(nodes, store.queryset.nodes))) {
|
|
29
|
+
result.set(store.queryset, store);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.warn('Error comparing nodes for related querysets', e);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns querysets that contain any of the specified instances
|
|
40
|
+
* @param {Operation} operation - The operation containing instances to check
|
|
41
|
+
* @returns {Map<QuerySet, Store>}
|
|
42
|
+
*/
|
|
43
|
+
function querysetsContainingInstances(operation) {
|
|
44
|
+
const ModelClass = operation.queryset.ModelClass;
|
|
45
|
+
const pkField = ModelClass.primaryKeyField;
|
|
46
|
+
const instancePks = new Set(operation.instances
|
|
47
|
+
.filter(instance => instance && typeof instance === 'object' && pkField in instance)
|
|
48
|
+
.map(instance => instance[pkField]));
|
|
49
|
+
if (instancePks.size === 0) {
|
|
50
|
+
return new Map();
|
|
51
|
+
}
|
|
52
|
+
const result = new Map();
|
|
53
|
+
Array.from(querysetStoreRegistry._stores.entries()).forEach(([queryset, store]) => {
|
|
54
|
+
if (store.modelClass !== ModelClass)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
// Check if this queryset contains any of the instances
|
|
58
|
+
const renderedPks = new Set(store.render(false)); // Get without optimistic updates
|
|
59
|
+
const hasIntersection = [...instancePks].some(pk => renderedPks.has(pk));
|
|
60
|
+
if (hasIntersection) {
|
|
61
|
+
result.set(store.queryset, store);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
console.warn('Error checking queryset for instances', e);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Process an operation in the model store
|
|
72
|
+
*
|
|
73
|
+
* @param {Operation} operation - The operation to process
|
|
74
|
+
* @param {string} actionType - The action to perform ('add', 'update', 'confirm', 'reject')
|
|
75
|
+
*/
|
|
76
|
+
function processModelStore(operation, actionType) {
|
|
77
|
+
const ModelClass = operation.queryset.ModelClass;
|
|
78
|
+
const modelStore = modelStoreRegistry.getStore(ModelClass);
|
|
79
|
+
if (!modelStore)
|
|
80
|
+
return;
|
|
81
|
+
switch (actionType) {
|
|
82
|
+
case 'add':
|
|
83
|
+
modelStore.addOperation(operation);
|
|
84
|
+
break;
|
|
85
|
+
case 'update':
|
|
86
|
+
modelStore.updateOperation(operation);
|
|
87
|
+
break;
|
|
88
|
+
case 'confirm':
|
|
89
|
+
modelStore.confirm(operation);
|
|
90
|
+
break;
|
|
91
|
+
case 'reject':
|
|
92
|
+
modelStore.reject(operation);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Process an operation in the queryset stores based on operation type
|
|
98
|
+
* Uses different routing strategies based on the operation type
|
|
99
|
+
*
|
|
100
|
+
* @param {Operation} operation - The operation to process
|
|
101
|
+
* @param {string} actionType - The action to perform ('add', 'update', 'confirm', 'reject')
|
|
102
|
+
*/
|
|
103
|
+
function processQuerysetStores(operation, actionType) {
|
|
104
|
+
const ModelClass = operation.queryset.ModelClass;
|
|
105
|
+
const queryset = operation.queryset;
|
|
106
|
+
// Apply the appropriate action to a single queryset store
|
|
107
|
+
const applyAction = (store) => {
|
|
108
|
+
switch (actionType) {
|
|
109
|
+
case 'add':
|
|
110
|
+
store.addOperation(operation);
|
|
111
|
+
break;
|
|
112
|
+
case 'update':
|
|
113
|
+
store.updateOperation(operation);
|
|
114
|
+
break;
|
|
115
|
+
case 'confirm':
|
|
116
|
+
store.confirm(operation);
|
|
117
|
+
break;
|
|
118
|
+
case 'reject':
|
|
119
|
+
store.reject(operation);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
let querysetStoreMap;
|
|
124
|
+
// Different routing strategies based on operation type
|
|
125
|
+
switch (operation.type) {
|
|
126
|
+
case Type.CREATE:
|
|
127
|
+
case Type.GET_OR_CREATE:
|
|
128
|
+
case Type.UPDATE_OR_CREATE:
|
|
129
|
+
// For creates, route to related querysets (they might want to include the new item)
|
|
130
|
+
querysetStoreMap = relatedQuerysets(queryset);
|
|
131
|
+
break;
|
|
132
|
+
case Type.UPDATE:
|
|
133
|
+
case Type.UPDATE_INSTANCE:
|
|
134
|
+
case Type.DELETE:
|
|
135
|
+
case Type.DELETE_INSTANCE:
|
|
136
|
+
// For updates and deletes, only route to querysets that actually contain the instances
|
|
137
|
+
querysetStoreMap = querysetsContainingInstances(operation);
|
|
138
|
+
break;
|
|
139
|
+
case Type.CHECKPOINT:
|
|
140
|
+
// For checkpoints, route to querysets that contain the instances
|
|
141
|
+
querysetStoreMap = querysetsContainingInstances(operation);
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
// For other operation types, use the existing related querysets logic
|
|
145
|
+
querysetStoreMap = relatedQuerysets(queryset);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
Array.from(querysetStoreMap.values()).forEach(applyAction);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Process an operation in the metric stores based on operation type
|
|
152
|
+
*
|
|
153
|
+
* @param {Operation} operation - The operation to process
|
|
154
|
+
* @param {string} actionType - The action to perform ('add', 'update', 'confirm', 'reject')
|
|
155
|
+
*/
|
|
156
|
+
function processMetricStores(operation, actionType) {
|
|
157
|
+
const queryset = operation.queryset;
|
|
158
|
+
const ModelClass = queryset.ModelClass;
|
|
159
|
+
// For metrics, we can use a similar strategy but might be more conservative
|
|
160
|
+
// and always use related querysets since metrics are aggregations
|
|
161
|
+
const allQuerysets = Array.from(relatedQuerysets(queryset).keys());
|
|
162
|
+
let allMetricStores = new Set();
|
|
163
|
+
allQuerysets.forEach(qs => {
|
|
164
|
+
const stores = metricRegistry.getAllStoresForQueryset(qs);
|
|
165
|
+
if (stores && stores.length > 0) {
|
|
166
|
+
stores.forEach(store => allMetricStores.add(store));
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (!allMetricStores || allMetricStores.size === 0) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Apply the action to each matching metric store
|
|
173
|
+
allMetricStores.forEach(store => {
|
|
174
|
+
switch (actionType) {
|
|
175
|
+
case 'add':
|
|
176
|
+
store.addOperation(operation);
|
|
177
|
+
break;
|
|
178
|
+
case 'update':
|
|
179
|
+
store.updateOperation(operation);
|
|
180
|
+
break;
|
|
181
|
+
case 'confirm':
|
|
182
|
+
store.confirm(operation);
|
|
183
|
+
break;
|
|
184
|
+
case 'reject':
|
|
185
|
+
store.reject(operation);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Common processing logic for operations, handling validation and routing
|
|
192
|
+
* to the appropriate store processors
|
|
193
|
+
*
|
|
194
|
+
* @param {Operation} operation - The operation to process
|
|
195
|
+
* @param {string} actionType - The action to perform ('add', 'update', 'confirm', 'reject')
|
|
196
|
+
*/
|
|
197
|
+
function processOperation(operation, actionType) {
|
|
198
|
+
if (!operation || !operation.queryset || !operation.queryset.ModelClass) {
|
|
199
|
+
console.warn(`Received invalid operation in processOperation (${actionType})`, operation);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (operation.doNotPropagate) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Process model store first
|
|
206
|
+
processModelStore(operation, actionType);
|
|
207
|
+
// Then process queryset stores with improved routing
|
|
208
|
+
processQuerysetStores(operation, actionType);
|
|
209
|
+
// Finally process metric stores
|
|
210
|
+
processMetricStores(operation, actionType);
|
|
211
|
+
}
|
|
212
|
+
// Define handlers as named arrow functions at the top level
|
|
213
|
+
const handleOperationCreated = operation => processOperation(operation, 'add');
|
|
214
|
+
const handleOperationUpdated = operation => processOperation(operation, 'update');
|
|
215
|
+
const handleOperationMutated = operation => processOperation(operation, 'update');
|
|
216
|
+
const handleOperationConfirmed = operation => processOperation(operation, 'confirm');
|
|
217
|
+
const handleOperationRejected = operation => processOperation(operation, 'reject');
|
|
218
|
+
/**
|
|
219
|
+
* Initialize the operation event handler system by setting up event listeners
|
|
220
|
+
*/
|
|
221
|
+
export function initEventHandler() {
|
|
222
|
+
operationEvents.on(Status.CREATED, handleOperationCreated);
|
|
223
|
+
operationEvents.on(Status.UPDATED, handleOperationUpdated);
|
|
224
|
+
operationEvents.on(Status.CONFIRMED, handleOperationConfirmed);
|
|
225
|
+
operationEvents.on(Status.REJECTED, handleOperationRejected);
|
|
226
|
+
operationEvents.on(Status.MUTATED, handleOperationMutated);
|
|
227
|
+
console.log('Operation event handler initialized');
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Clean up by removing all event listeners
|
|
231
|
+
*/
|
|
232
|
+
export function cleanupEventHandler() {
|
|
233
|
+
operationEvents.off(Status.CREATED, handleOperationCreated);
|
|
234
|
+
operationEvents.off(Status.UPDATED, handleOperationUpdated);
|
|
235
|
+
operationEvents.off(Status.CONFIRMED, handleOperationConfirmed);
|
|
236
|
+
operationEvents.off(Status.REJECTED, handleOperationRejected);
|
|
237
|
+
operationEvents.off(Status.MUTATED, handleOperationMutated);
|
|
238
|
+
console.log('Operation event handler cleaned up');
|
|
239
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class QuerysetStore {
|
|
2
|
+
constructor(modelClass: any, fetchFn: any, queryset: any, initialGroundTruthPks?: null, initialOperations?: null, options?: {});
|
|
3
|
+
modelClass: any;
|
|
4
|
+
fetchFn: any;
|
|
5
|
+
queryset: any;
|
|
6
|
+
operationsMap: Map<any, any>;
|
|
7
|
+
groundTruthPks: never[];
|
|
8
|
+
isSyncing: boolean;
|
|
9
|
+
pruneThreshold: any;
|
|
10
|
+
qsCache: Cache;
|
|
11
|
+
get cacheKey(): any;
|
|
12
|
+
onHydrated(hydratedData: any): void;
|
|
13
|
+
setCache(result: any): void;
|
|
14
|
+
clearCache(): void;
|
|
15
|
+
get operations(): any[];
|
|
16
|
+
get pkField(): any;
|
|
17
|
+
get groundTruthSet(): Set<never>;
|
|
18
|
+
_emitRenderEvent(): void;
|
|
19
|
+
addOperation(operation: any): Promise<void>;
|
|
20
|
+
updateOperation(operation: any): Promise<true | undefined>;
|
|
21
|
+
confirm(operation: any): Promise<void>;
|
|
22
|
+
reject(operation: any): Promise<void>;
|
|
23
|
+
setGroundTruth(groundTruthPks: any): Promise<void>;
|
|
24
|
+
setOperations(operations: any): Promise<void>;
|
|
25
|
+
getTrimmedOperations(): any[];
|
|
26
|
+
getInflightOperations(): any[];
|
|
27
|
+
prune(): void;
|
|
28
|
+
render(optimistic?: boolean, fromCache?: boolean): any[];
|
|
29
|
+
applyOperation(operation: any, currentPks: any): any;
|
|
30
|
+
sync(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
import { Cache } from '../cache/cache.js';
|