@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.
Files changed (89) hide show
  1. package/dist/adaptors/react/composables.d.ts +1 -0
  2. package/dist/adaptors/react/composables.js +4 -0
  3. package/dist/adaptors/react/index.d.ts +1 -0
  4. package/dist/adaptors/react/index.js +1 -0
  5. package/dist/adaptors/vue/composables.d.ts +2 -0
  6. package/dist/adaptors/vue/composables.js +36 -0
  7. package/dist/adaptors/vue/index.d.ts +2 -0
  8. package/dist/adaptors/vue/index.js +2 -0
  9. package/dist/adaptors/vue/reactivity.d.ts +18 -0
  10. package/dist/adaptors/vue/reactivity.js +125 -0
  11. package/dist/cli/commands/syncModels.d.ts +132 -0
  12. package/dist/cli/commands/syncModels.js +1040 -0
  13. package/dist/cli/configFileLoader.d.ts +10 -0
  14. package/dist/cli/configFileLoader.js +85 -0
  15. package/dist/cli/index.d.ts +2 -0
  16. package/dist/cli/index.js +14 -0
  17. package/dist/config.d.ts +52 -0
  18. package/dist/config.js +242 -0
  19. package/dist/core/eventReceivers.d.ts +179 -0
  20. package/dist/core/eventReceivers.js +210 -0
  21. package/dist/core/utils.d.ts +8 -0
  22. package/dist/core/utils.js +62 -0
  23. package/dist/filtering/localFiltering.d.ts +116 -0
  24. package/dist/filtering/localFiltering.js +834 -0
  25. package/dist/flavours/django/dates.d.ts +33 -0
  26. package/dist/flavours/django/dates.js +99 -0
  27. package/dist/flavours/django/errors.d.ts +138 -0
  28. package/dist/flavours/django/errors.js +187 -0
  29. package/dist/flavours/django/f.d.ts +6 -0
  30. package/dist/flavours/django/f.js +91 -0
  31. package/dist/flavours/django/files.d.ts +76 -0
  32. package/dist/flavours/django/files.js +338 -0
  33. package/dist/flavours/django/makeApiCall.d.ts +20 -0
  34. package/dist/flavours/django/makeApiCall.js +169 -0
  35. package/dist/flavours/django/manager.d.ts +197 -0
  36. package/dist/flavours/django/manager.js +222 -0
  37. package/dist/flavours/django/model.d.ts +112 -0
  38. package/dist/flavours/django/model.js +253 -0
  39. package/dist/flavours/django/operationFactory.d.ts +65 -0
  40. package/dist/flavours/django/operationFactory.js +216 -0
  41. package/dist/flavours/django/q.d.ts +70 -0
  42. package/dist/flavours/django/q.js +43 -0
  43. package/dist/flavours/django/queryExecutor.d.ts +131 -0
  44. package/dist/flavours/django/queryExecutor.js +468 -0
  45. package/dist/flavours/django/querySet.d.ts +412 -0
  46. package/dist/flavours/django/querySet.js +601 -0
  47. package/dist/flavours/django/tempPk.d.ts +19 -0
  48. package/dist/flavours/django/tempPk.js +48 -0
  49. package/dist/flavours/django/utils.d.ts +19 -0
  50. package/dist/flavours/django/utils.js +29 -0
  51. package/dist/index.d.ts +38 -0
  52. package/dist/index.js +38 -0
  53. package/dist/react-entry.d.ts +2 -0
  54. package/dist/react-entry.js +2 -0
  55. package/dist/reactiveAdaptor.d.ts +24 -0
  56. package/dist/reactiveAdaptor.js +38 -0
  57. package/dist/setup.d.ts +15 -0
  58. package/dist/setup.js +22 -0
  59. package/dist/syncEngine/cache/cache.d.ts +75 -0
  60. package/dist/syncEngine/cache/cache.js +341 -0
  61. package/dist/syncEngine/metrics/metricOptCalcs.d.ts +79 -0
  62. package/dist/syncEngine/metrics/metricOptCalcs.js +284 -0
  63. package/dist/syncEngine/registries/metricRegistry.d.ts +53 -0
  64. package/dist/syncEngine/registries/metricRegistry.js +162 -0
  65. package/dist/syncEngine/registries/modelStoreRegistry.d.ts +11 -0
  66. package/dist/syncEngine/registries/modelStoreRegistry.js +56 -0
  67. package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +55 -0
  68. package/dist/syncEngine/registries/querysetStoreRegistry.js +244 -0
  69. package/dist/syncEngine/stores/metricStore.d.ts +55 -0
  70. package/dist/syncEngine/stores/metricStore.js +222 -0
  71. package/dist/syncEngine/stores/modelStore.d.ts +40 -0
  72. package/dist/syncEngine/stores/modelStore.js +405 -0
  73. package/dist/syncEngine/stores/operation.d.ts +99 -0
  74. package/dist/syncEngine/stores/operation.js +224 -0
  75. package/dist/syncEngine/stores/operationEventHandlers.d.ts +8 -0
  76. package/dist/syncEngine/stores/operationEventHandlers.js +239 -0
  77. package/dist/syncEngine/stores/querysetStore.d.ts +32 -0
  78. package/dist/syncEngine/stores/querysetStore.js +200 -0
  79. package/dist/syncEngine/stores/reactivity.d.ts +3 -0
  80. package/dist/syncEngine/stores/reactivity.js +4 -0
  81. package/dist/syncEngine/stores/utils.d.ts +14 -0
  82. package/dist/syncEngine/stores/utils.js +32 -0
  83. package/dist/syncEngine/sync.d.ts +32 -0
  84. package/dist/syncEngine/sync.js +169 -0
  85. package/dist/vue-entry.d.ts +6 -0
  86. package/dist/vue-entry.js +2 -0
  87. package/license.md +116 -0
  88. package/package.json +123 -0
  89. package/readme.md +222 -0
@@ -0,0 +1,405 @@
1
+ import { Operation, Status, Type, operationRegistry } from './operation.js';
2
+ import { isNil, isEmpty, trim } from 'lodash-es';
3
+ import { modelEventEmitter } from './reactivity.js';
4
+ import { Cache } from '../cache/cache.js';
5
+ import { replaceTempPks, containsTempPk } from '../../flavours/django/tempPk.js';
6
+ const emitEvents = (modelClass, events) => {
7
+ // helper method to emit every event
8
+ events.forEach(event => {
9
+ modelEventEmitter.emit(`${modelClass.configKey}::${modelClass.modelName}::render`, event);
10
+ });
11
+ };
12
+ class EventData {
13
+ constructor(ModelClass, pk) {
14
+ this.ModelClass = ModelClass;
15
+ this.pk = pk;
16
+ }
17
+ /**
18
+ * One event per instance in a single operation
19
+ */
20
+ static fromOperation(operation) {
21
+ const ModelClass = operation.queryset.ModelClass;
22
+ const pkField = ModelClass.primaryKeyField;
23
+ return operation.instances.map(instance => new EventData(ModelClass, instance[pkField]));
24
+ }
25
+ /**
26
+ * One event per unique PK across multiple operations
27
+ */
28
+ static fromOperations(operations) {
29
+ if (!operations.length)
30
+ return [];
31
+ const ModelClass = operations[0].queryset.ModelClass;
32
+ const pkField = ModelClass.primaryKeyField;
33
+ const uniquePks = new Set();
34
+ for (const op of operations) {
35
+ for (const inst of op.instances) {
36
+ uniquePks.add(inst[pkField]);
37
+ }
38
+ }
39
+ return Array.from(uniquePks).map(pk => new EventData(ModelClass, pk));
40
+ }
41
+ /**
42
+ * One event per unique PK in an array of instances
43
+ */
44
+ static fromInstances(instances, ModelClass) {
45
+ const pkField = ModelClass.primaryKeyField;
46
+ const uniquePks = new Set(instances
47
+ .filter(inst => inst && inst[pkField] != null)
48
+ .map(inst => inst[pkField]));
49
+ return Array.from(uniquePks).map(pk => new EventData(ModelClass, pk));
50
+ }
51
+ }
52
+ export class ModelStore {
53
+ constructor(modelClass, fetchFn, initialGroundTruth = null, initialOperations = null, options = {}) {
54
+ this.modelClass = modelClass;
55
+ this.fetchFn = fetchFn;
56
+ this.isSyncing = false;
57
+ this.pruneThreshold = options.pruneThreshold || 10;
58
+ this.groundTruthArray = initialGroundTruth || [];
59
+ this.operationsMap = new Map();
60
+ // Handle initial operations if provided
61
+ if (initialOperations && initialOperations.length > 0) {
62
+ this._loadOperations(initialOperations);
63
+ }
64
+ this.modelCache = new Cache('model-cache', {}, this.onHydrated.bind(this));
65
+ }
66
+ /**
67
+ * Load operations from data and add them to the operations map,
68
+ * reusing existing operations from the registry if they exist
69
+ */
70
+ _loadOperations(operationsData) {
71
+ operationsData.forEach(opData => {
72
+ const existingOp = operationRegistry.get(opData.operationId);
73
+ if (existingOp) {
74
+ // If the operation exists in the registry, use it
75
+ this.operationsMap.set(existingOp.operationId, existingOp);
76
+ }
77
+ else {
78
+ // Otherwise just use the plain object data
79
+ this.operationsMap.set(opData.operationId, new Operation(opData, true));
80
+ }
81
+ });
82
+ }
83
+ // Caching
84
+ get cacheKey() {
85
+ return `${this.modelClass.configKey}::${this.modelClass.modelName}`;
86
+ }
87
+ onHydrated() {
88
+ if (this.groundTruthArray.length === 0 && this.operationsMap.size === 0) {
89
+ let cached = this.modelCache.get(this.cacheKey);
90
+ console.log(`[ModelStore] Hydrated ${this.modelClass.modelName} with ${(cached || []).length} items from cache`);
91
+ if (!isNil(cached) && !isEmpty(cached)) {
92
+ this.setGroundTruth(cached);
93
+ }
94
+ }
95
+ }
96
+ setCache(result) {
97
+ const pkField = this.pkField;
98
+ let nonTempPkItems = [];
99
+ result.forEach((item) => {
100
+ let pk = item[pkField];
101
+ if (typeof pk === 'string' && containsTempPk(pk)) {
102
+ pk = replaceTempPks(item[pkField]);
103
+ if (isNil(pk) || isEmpty(trim(pk))) {
104
+ return;
105
+ }
106
+ }
107
+ item[pkField] = pk;
108
+ nonTempPkItems.push(item);
109
+ });
110
+ this.modelCache.set(this.cacheKey, nonTempPkItems);
111
+ }
112
+ clearCache() {
113
+ this.modelCache.delete(this.cacheKey);
114
+ }
115
+ updateCache(items, requestedPks) {
116
+ const pkField = this.pkField;
117
+ let nonTempPkItems = [];
118
+ items.forEach((item) => {
119
+ let pk = item[pkField];
120
+ if (typeof pk === 'string' && containsTempPk(pk)) {
121
+ pk = replaceTempPks(item[pkField]);
122
+ if (isNil(pk) || isEmpty(trim(pk))) {
123
+ return;
124
+ }
125
+ }
126
+ item[pkField] = pk;
127
+ nonTempPkItems.push(item);
128
+ });
129
+ // If rendering ALL items (requestedPks is null), simply replace the cache
130
+ if (requestedPks === null) {
131
+ this.setCache(nonTempPkItems);
132
+ return;
133
+ }
134
+ // Otherwise, we're rendering specific items - update only those items
135
+ const currentCache = this.modelCache.get(this.cacheKey) || [];
136
+ // Filter out items that were requested but not in the result (they were deleted)
137
+ const filteredCache = currentCache.filter(item => item &&
138
+ typeof item === 'object' &&
139
+ pkField in item &&
140
+ (!requestedPks.has(item[pkField]) ||
141
+ nonTempPkItems.some(newItem => newItem[pkField] === item[pkField])));
142
+ // Create a map for faster lookups
143
+ const cacheMap = new Map(filteredCache.map(item => [item[pkField], item]));
144
+ // Add or update items from the result
145
+ for (const item of nonTempPkItems) {
146
+ if (item && typeof item === 'object' && pkField in item) {
147
+ cacheMap.set(item[pkField], item);
148
+ }
149
+ }
150
+ // Update the cache
151
+ const updatedCache = Array.from(cacheMap.values());
152
+ this.setCache(updatedCache);
153
+ }
154
+ // Main modelStore methods
155
+ get operations() {
156
+ return Array.from(this.operationsMap.values());
157
+ }
158
+ get pkField() {
159
+ return this.modelClass.primaryKeyField;
160
+ }
161
+ // Commit optimistic updates
162
+ addOperation(operation) {
163
+ this.operationsMap.set(operation.operationId, operation);
164
+ if (this.operationsMap.size > this.pruneThreshold) {
165
+ this.prune();
166
+ }
167
+ emitEvents(this.modelClass, EventData.fromOperation(operation));
168
+ }
169
+ updateOperation(operation) {
170
+ if (!this.operationsMap.has(operation.operationId))
171
+ return false;
172
+ this.operationsMap.set(operation.operationId, operation);
173
+ emitEvents(this.modelClass, EventData.fromOperation(operation));
174
+ return true;
175
+ }
176
+ confirm(operation) {
177
+ if (!this.operationsMap.has(operation.operationId))
178
+ return;
179
+ this.operationsMap.set(operation.operationId, operation);
180
+ emitEvents(this.modelClass, EventData.fromOperation(operation));
181
+ }
182
+ reject(operation) {
183
+ if (!this.operationsMap.has(operation.operationId))
184
+ return;
185
+ this.operationsMap.set(operation.operationId, operation);
186
+ emitEvents(this.modelClass, EventData.fromOperation(operation));
187
+ }
188
+ setOperations(operations = []) {
189
+ const prevOps = this.operations;
190
+ this.operationsMap.clear();
191
+ operations.forEach(op => {
192
+ this.operationsMap.set(op.operationId, op);
193
+ });
194
+ const allOps = [...prevOps, ...this.operations];
195
+ emitEvents(this.modelClass, EventData.fromOperations(allOps));
196
+ }
197
+ // Ground truth data methods
198
+ setGroundTruth(groundTruth) {
199
+ let prevGroundTruth = this.groundTruthArray;
200
+ this.groundTruthArray = Array.isArray(groundTruth) ? groundTruth : [];
201
+ // reactivity - gather all ops
202
+ const allOps = [...prevGroundTruth, ...this.groundTruthArray];
203
+ emitEvents(this.modelClass, EventData.fromInstances(allOps, this.modelClass));
204
+ }
205
+ getGroundTruth() {
206
+ return this.groundTruthArray;
207
+ }
208
+ get groundTruthPks() {
209
+ const pk = this.pkField;
210
+ return this.groundTruthArray
211
+ .filter(instance => instance && typeof instance === 'object' && pk in instance)
212
+ .map(instance => instance[pk]);
213
+ }
214
+ addToGroundTruth(instances) {
215
+ if (!Array.isArray(instances) || instances.length === 0)
216
+ return;
217
+ const pkField = this.pkField;
218
+ const pkMap = new Map();
219
+ instances.forEach(inst => {
220
+ if (inst && typeof inst === 'object' && pkField in inst) {
221
+ pkMap.set(inst[pkField], inst);
222
+ }
223
+ else {
224
+ console.warn(`[ModelStore ${this.modelClass.modelName}] Skipping invalid instance in addToGroundTruth:`, inst);
225
+ }
226
+ });
227
+ if (pkMap.size === 0)
228
+ return;
229
+ const updatedGroundTruth = [];
230
+ const processedPks = new Set();
231
+ const checkpointInstances = []; // Track instances that need CHECKPOINT operations
232
+ for (const existingItem of this.groundTruthArray) {
233
+ if (!existingItem || typeof existingItem !== 'object' || !(pkField in existingItem)) {
234
+ continue;
235
+ }
236
+ const pk = existingItem[pkField];
237
+ if (pkMap.has(pk)) {
238
+ const updatedInstance = { ...existingItem, ...pkMap.get(pk) };
239
+ updatedGroundTruth.push(updatedInstance);
240
+ processedPks.add(pk);
241
+ // This instance already existed - add it to checkpoint list
242
+ checkpointInstances.push(updatedInstance);
243
+ pkMap.delete(pk);
244
+ }
245
+ else {
246
+ updatedGroundTruth.push(existingItem);
247
+ }
248
+ }
249
+ // Add completely new instances (these don't need checkpoint operations)
250
+ updatedGroundTruth.push(...Array.from(pkMap.values()));
251
+ this.groundTruthArray = updatedGroundTruth;
252
+ // Create CHECKPOINT operation for instances that already existed
253
+ if (checkpointInstances.length > 0) {
254
+ const checkpointOperation = new Operation({
255
+ operationId: `checkpoint_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
256
+ type: Type.CHECKPOINT,
257
+ instances: checkpointInstances,
258
+ status: Status.CONFIRMED,
259
+ timestamp: Date.now(),
260
+ queryset: this.modelClass.objects.all()
261
+ });
262
+ this.operationsMap.set(checkpointOperation.operationId, checkpointOperation);
263
+ console.log(`[ModelStore ${this.modelClass.modelName}] Created CHECKPOINT operation for ${checkpointInstances.length} existing instances`);
264
+ }
265
+ // reactivity - use all the newly added instances (both new and updated)
266
+ emitEvents(this.modelClass, EventData.fromInstances([...checkpointInstances, ...Array.from(pkMap.values())], this.modelClass));
267
+ }
268
+ _filteredOperations(pks, operations) {
269
+ if (!pks)
270
+ return operations;
271
+ const pkField = this.pkField;
272
+ let filteredOps = [];
273
+ for (const op of operations) {
274
+ let relevantInstances = op.instances.filter(instance => pks.has(instance[pkField] || instance));
275
+ if (relevantInstances.length > 0) {
276
+ filteredOps.push({
277
+ operationId: op.operationId,
278
+ instances: relevantInstances,
279
+ timestamp: op.timestamp,
280
+ queryset: op.queryset,
281
+ type: op.type,
282
+ status: op.status,
283
+ args: op.args
284
+ });
285
+ }
286
+ }
287
+ return filteredOps;
288
+ }
289
+ _filteredGroundTruth(pks, groundTruthArray) {
290
+ const pkField = this.pkField;
291
+ let groundTruthMap = new Map();
292
+ for (const instance of groundTruthArray) {
293
+ if (!instance || typeof instance !== 'object' || !(pkField in instance)) {
294
+ continue;
295
+ }
296
+ const pk = instance[pkField];
297
+ if (!pks || pks.has(pk)) {
298
+ groundTruthMap.set(pk, instance);
299
+ }
300
+ }
301
+ return groundTruthMap;
302
+ }
303
+ applyOperation(operation, currentInstances) {
304
+ const pkField = this.pkField;
305
+ for (const instance of operation.instances) {
306
+ if (!instance || typeof instance !== 'object' || !(pkField in instance)) {
307
+ console.warn(`[ModelStore ${this.modelClass.modelName}] Skipping instance ${instance} in operation ${operation.operationId} during applyOperation due to missing PK field '${String(pkField)}' or invalid format.`);
308
+ continue;
309
+ }
310
+ let pk = instance[pkField];
311
+ switch (operation.type) {
312
+ case Type.CREATE:
313
+ if (!currentInstances.has(pk)) {
314
+ currentInstances.set(pk, instance);
315
+ }
316
+ break;
317
+ case Type.CHECKPOINT:
318
+ case Type.UPDATE_INSTANCE:
319
+ case Type.UPDATE: {
320
+ const existing = currentInstances.get(pk);
321
+ if (existing) {
322
+ currentInstances.set(pk, { ...existing, ...instance });
323
+ }
324
+ else {
325
+ const wasDeletedLocally = this.operations.some(op => op.type === Type.DELETE &&
326
+ op.status !== Status.REJECTED &&
327
+ op.instances.some(inst => inst && inst[pkField] === pk));
328
+ if (!wasDeletedLocally) {
329
+ currentInstances.set(pk, instance);
330
+ }
331
+ }
332
+ break;
333
+ }
334
+ case Type.DELETE_INSTANCE:
335
+ case Type.DELETE:
336
+ currentInstances.delete(pk);
337
+ break;
338
+ default:
339
+ console.error(`[ModelStore ${this.modelClass.modelName}] Unknown operation type: ${operation.type}`);
340
+ }
341
+ }
342
+ return currentInstances;
343
+ }
344
+ getTrimmedOperations() {
345
+ const twoMinutesAgo = Date.now() - 1000 * 60 * 2;
346
+ return this.operations.filter(operation => operation.timestamp > twoMinutesAgo);
347
+ }
348
+ getInflightOperations() {
349
+ return this.operations.filter(operation => operation.status != Status.CONFIRMED &&
350
+ operation.status != Status.REJECTED);
351
+ }
352
+ // Pruning
353
+ prune() {
354
+ let renderedPks = this.render(null, false);
355
+ this.setGroundTruth(renderedPks);
356
+ this.setOperations(this.getInflightOperations());
357
+ this.setCache(renderedPks);
358
+ }
359
+ // Render methods
360
+ render(pks = null, optimistic = true) {
361
+ const pksSet = pks === null ? null :
362
+ (pks instanceof Set ? pks : new Set(Array.isArray(pks) ? pks : [pks]));
363
+ const renderedInstancesMap = this._filteredGroundTruth(pksSet, this.groundTruthArray);
364
+ const relevantOperations = this._filteredOperations(pksSet, this.operations);
365
+ for (const op of relevantOperations) {
366
+ if (op.status !== Status.REJECTED && (optimistic || op.status === Status.CONFIRMED)) {
367
+ this.applyOperation(op, renderedInstancesMap);
368
+ }
369
+ }
370
+ let result = Array.from(renderedInstancesMap.values());
371
+ if (pks)
372
+ this.updateCache(result, pksSet);
373
+ if (isNil(pks))
374
+ this.setCache(result);
375
+ return result;
376
+ }
377
+ async sync(pks = null) {
378
+ const storeIdForLog = this.modelClass.modelName;
379
+ if (this.isSyncing)
380
+ return;
381
+ this.isSyncing = true;
382
+ try {
383
+ const currentPks = pks || this.groundTruthPks;
384
+ if (currentPks.length === 0) {
385
+ const trimmedOps = this.getTrimmedOperations();
386
+ this.setOperations(trimmedOps);
387
+ return;
388
+ }
389
+ const newGroundTruth = await this.fetchFn({ pks: currentPks, modelClass: this.modelClass });
390
+ if (pks) {
391
+ this.addToGroundTruth(newGroundTruth);
392
+ return;
393
+ }
394
+ this.setGroundTruth(newGroundTruth);
395
+ const trimmedOps = this.getTrimmedOperations();
396
+ this.setOperations(trimmedOps);
397
+ }
398
+ catch (error) {
399
+ console.error(`[ModelStore ${storeIdForLog}] Failed to sync ground truth:`, error);
400
+ }
401
+ finally {
402
+ this.isSyncing = false;
403
+ }
404
+ }
405
+ }
@@ -0,0 +1,99 @@
1
+ export const operationEvents: import("mitt").Emitter<Record<import("mitt").EventType, unknown>>;
2
+ export namespace Status {
3
+ let CREATED: string;
4
+ let UPDATED: string;
5
+ let CONFIRMED: string;
6
+ let REJECTED: string;
7
+ let CLEAR: string;
8
+ let MUTATED: string;
9
+ }
10
+ export namespace Type {
11
+ let CREATE: string;
12
+ let UPDATE: string;
13
+ let DELETE: string;
14
+ let UPDATE_INSTANCE: string;
15
+ let DELETE_INSTANCE: string;
16
+ let GET_OR_CREATE: string;
17
+ let UPDATE_OR_CREATE: string;
18
+ let CHECKPOINT: string;
19
+ let COUNT: string;
20
+ let MIN: string;
21
+ let MAX: string;
22
+ let AVG: string;
23
+ let SUM: string;
24
+ let AGGREGATE: string;
25
+ }
26
+ export class Operation {
27
+ constructor(data: any, restore?: boolean);
28
+ operationId: any;
29
+ type: any;
30
+ status: any;
31
+ queryset: any;
32
+ args: any;
33
+ timestamp: any;
34
+ doNotPropagate: any;
35
+ /**
36
+ * Setter for instances
37
+ */
38
+ set instances(value: any);
39
+ /**
40
+ * Getter for instances that replaces any temporary PKs with real PKs
41
+ */
42
+ get instances(): any;
43
+ /**
44
+ * Setter for frozenInstances
45
+ */
46
+ set frozenInstances(value: any);
47
+ /**
48
+ * Getter for frozenInstances that replaces any temporary PKs with real PKs
49
+ */
50
+ get frozenInstances(): any;
51
+ /**
52
+ * Get primary keys of all instances in this operation
53
+ * Returns primary keys as simple values (not objects)
54
+ */
55
+ get instancePks(): any;
56
+ /**
57
+ * Update this operation's status and emit an event
58
+ * @param {string} status - New status ('confirmed', 'rejected', etc.)
59
+ * @param {Array|Object|null} [instances=null] - New instances for the operation
60
+ */
61
+ updateStatus(status: string, instances?: any[] | Object | null): void;
62
+ /**
63
+ * Updates this operation with new data and emits the appropriate event
64
+ * @param {Object} newData - New data to update the operation with
65
+ * @returns {Operation} - Returns this operation instance for chaining
66
+ */
67
+ mutate(newData: Object): Operation;
68
+ #private;
69
+ }
70
+ export const operationRegistry: OperationRegistry;
71
+ declare class OperationRegistry {
72
+ _operations: Map<any, any>;
73
+ /**
74
+ * Registers a pre-constructed Operation instance in the registry.
75
+ * Ensures the operationId is unique within the registry.
76
+ * Throws an Error if the operationId already exists.
77
+ *
78
+ * @param {Operation} operation - The fully instantiated Operation object to register.
79
+ * @throws {Error} If the input is not a valid operation object or if an operation with the same operationId already exists.
80
+ */
81
+ register(operation: Operation): void;
82
+ /**
83
+ * Retrieves an operation by its ID.
84
+ * @param {string} operationId - The ID of the operation.
85
+ * @returns {Operation | undefined} The operation instance or undefined if not found.
86
+ */
87
+ get(operationId: string): Operation | undefined;
88
+ /**
89
+ * Checks if an operation with the given ID exists in the registry.
90
+ * @param {string} operationId - The ID of the operation to check.
91
+ * @returns {boolean} True if the operation exists, false otherwise.
92
+ */
93
+ has(operationId: string): boolean;
94
+ /**
95
+ * Clears all operations from the registry.
96
+ */
97
+ clear(): void;
98
+ }
99
+ export {};