@statezero/core 0.2.38 → 0.2.40
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/actions/backend1/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/backend1/django_app/calculate-hash.js +80 -0
- package/dist/actions/backend1/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/backend1/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/backend1/django_app/get-current-username.js +65 -0
- package/dist/actions/backend1/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/backend1/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/backend1/django_app/get-server-status.js +68 -0
- package/dist/actions/backend1/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/backend1/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/backend1/django_app/get-user-info.js +70 -0
- package/dist/actions/backend1/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/backend1/django_app/index.d.ts +1 -0
- package/dist/actions/backend1/django_app/index.js +6 -0
- package/dist/actions/backend1/django_app/process-data.d.ts +51 -0
- package/dist/actions/backend1/django_app/process-data.js +78 -0
- package/dist/actions/backend1/django_app/process-data.schema.json +117 -0
- package/dist/actions/backend1/django_app/send-notification.d.ts +55 -0
- package/dist/actions/backend1/django_app/send-notification.js +81 -0
- package/dist/actions/backend1/django_app/send-notification.schema.json +175 -0
- package/dist/actions/backend1/index.d.ts +1 -0
- package/dist/actions/backend1/index.js +1 -0
- package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/default/django_app/calculate-hash.js +80 -0
- package/dist/actions/default/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/default/django_app/get-current-username.js +65 -0
- package/dist/actions/default/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/default/django_app/get-server-status.js +68 -0
- package/dist/actions/default/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/default/django_app/get-user-info.js +70 -0
- package/dist/actions/default/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/default/django_app/index.d.ts +1 -0
- package/dist/actions/default/django_app/index.js +6 -0
- package/dist/actions/default/django_app/process-data.d.ts +51 -0
- package/dist/actions/default/django_app/process-data.js +78 -0
- package/dist/actions/default/django_app/process-data.schema.json +117 -0
- package/dist/actions/default/django_app/send-notification.d.ts +55 -0
- package/dist/actions/default/django_app/send-notification.js +81 -0
- package/dist/actions/default/django_app/send-notification.schema.json +175 -0
- package/dist/actions/default/index.d.ts +1 -0
- package/dist/actions/default/index.js +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +5 -0
- 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/components/LayoutRenderer.js +46 -49
- package/dist/adaptors/vue/components/defaults/index.d.ts +7 -0
- package/dist/adaptors/vue/components/defaults/index.js +31 -0
- package/dist/adaptors/vue/components/index.d.ts +1 -0
- package/dist/adaptors/vue/components/index.js +7 -0
- package/dist/adaptors/vue/composables.d.ts +2 -0
- package/dist/adaptors/vue/composables.js +44 -0
- package/dist/adaptors/vue/index.d.ts +3 -0
- package/dist/adaptors/vue/index.js +4 -0
- package/dist/adaptors/vue/reactivity.d.ts +18 -0
- package/dist/adaptors/vue/reactivity.js +132 -0
- 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 +717 -0
- package/dist/cli/commands/syncModels.d.ts +132 -0
- package/dist/cli/commands/syncModels.js +1120 -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 +22 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.js +273 -0
- package/dist/core/eventReceivers.d.ts +185 -0
- package/dist/core/eventReceivers.js +266 -0
- package/dist/core/utils.d.ts +8 -0
- package/dist/core/utils.js +62 -0
- package/dist/errorHandler.d.ts +21 -0
- package/dist/errorHandler.js +27 -0
- package/dist/filtering/localFiltering.d.ts +110 -0
- package/dist/filtering/localFiltering.js +1080 -0
- package/dist/flavours/django/dates.d.ts +34 -0
- package/dist/flavours/django/dates.js +113 -0
- package/dist/flavours/django/errors.d.ts +138 -0
- package/dist/flavours/django/errors.js +195 -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 +62 -0
- package/dist/flavours/django/files.js +355 -0
- package/dist/flavours/django/makeApiCall.d.ts +36 -0
- package/dist/flavours/django/makeApiCall.js +169 -0
- package/dist/flavours/django/manager.d.ts +204 -0
- package/dist/flavours/django/manager.js +222 -0
- package/dist/flavours/django/model.d.ts +137 -0
- package/dist/flavours/django/model.js +366 -0
- package/dist/flavours/django/operationFactory.d.ts +73 -0
- package/dist/flavours/django/operationFactory.js +248 -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 +149 -0
- package/dist/flavours/django/queryExecutor.js +590 -0
- package/dist/flavours/django/querySet.d.ts +301 -0
- package/dist/flavours/django/querySet.js +736 -0
- package/dist/flavours/django/serializers.d.ts +39 -0
- package/dist/flavours/django/serializers.js +296 -0
- package/dist/flavours/django/tempPk.d.ts +31 -0
- package/dist/flavours/django/tempPk.js +92 -0
- package/dist/flavours/django/utils.d.ts +19 -0
- package/dist/flavours/django/utils.js +29 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/backend1/django_app/comprehensivemodel.js +71 -0
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/backend1/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/custompkmodel.js +69 -0
- package/dist/models/backend1/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/dailyrate.d.ts +230 -0
- package/dist/models/backend1/django_app/dailyrate.js +71 -0
- package/dist/models/backend1/django_app/dailyrate.schema.json +212 -0
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +140 -0
- package/dist/models/backend1/django_app/deepmodellevel1.js +72 -0
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +114 -0
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/deepmodellevel2.js +71 -0
- package/dist/models/backend1/django_app/deepmodellevel2.schema.json +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.js +69 -0
- package/dist/models/backend1/django_app/deepmodellevel3.schema.json +69 -0
- package/dist/models/backend1/django_app/dummymodel.d.ts +134 -0
- package/dist/models/backend1/django_app/dummymodel.js +71 -0
- package/dist/models/backend1/django_app/dummymodel.schema.json +109 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +69 -0
- package/dist/models/backend1/django_app/filetest.d.ts +140 -0
- package/dist/models/backend1/django_app/filetest.js +69 -0
- package/dist/models/backend1/django_app/filetest.schema.json +111 -0
- package/dist/models/backend1/django_app/index.d.ts +1 -0
- package/dist/models/backend1/django_app/index.js +21 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/order.d.ts +220 -0
- package/dist/models/backend1/django_app/order.js +71 -0
- package/dist/models/backend1/django_app/order.schema.json +203 -0
- package/dist/models/backend1/django_app/orderitem.d.ts +172 -0
- package/dist/models/backend1/django_app/orderitem.js +72 -0
- package/dist/models/backend1/django_app/orderitem.schema.json +149 -0
- package/dist/models/backend1/django_app/product.d.ts +254 -0
- package/dist/models/backend1/django_app/product.js +71 -0
- package/dist/models/backend1/django_app/product.schema.json +277 -0
- package/dist/models/backend1/django_app/productcategory.d.ts +92 -0
- package/dist/models/backend1/django_app/productcategory.js +69 -0
- package/dist/models/backend1/django_app/productcategory.schema.json +70 -0
- package/dist/models/backend1/django_app/rateplan.d.ts +92 -0
- package/dist/models/backend1/django_app/rateplan.js +69 -0
- package/dist/models/backend1/django_app/rateplan.schema.json +70 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/backend1/fileobject.d.ts +4 -0
- package/dist/models/backend1/fileobject.js +9 -0
- package/dist/models/backend1/index.d.ts +2 -0
- package/dist/models/backend1/index.js +2 -0
- package/dist/models/default/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/default/django_app/comprehensivemodel.js +71 -0
- package/dist/models/default/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/default/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/custompkmodel.js +69 -0
- package/dist/models/default/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/dailyrate.d.ts +230 -0
- package/dist/models/default/django_app/dailyrate.js +71 -0
- package/dist/models/default/django_app/dailyrate.schema.json +212 -0
- package/dist/models/default/django_app/deepmodellevel1.d.ts +128 -0
- package/dist/models/default/django_app/deepmodellevel1.js +72 -0
- package/dist/models/default/django_app/deepmodellevel1.schema.json +102 -0
- package/dist/models/default/django_app/deepmodellevel2.d.ts +106 -0
- package/dist/models/default/django_app/deepmodellevel2.js +71 -0
- package/dist/models/default/django_app/deepmodellevel2.schema.json +80 -0
- package/dist/models/default/django_app/deepmodellevel3.d.ts +80 -0
- package/dist/models/default/django_app/deepmodellevel3.js +69 -0
- package/dist/models/default/django_app/deepmodellevel3.schema.json +57 -0
- package/dist/models/default/django_app/dummymodel.d.ts +122 -0
- package/dist/models/default/django_app/dummymodel.js +71 -0
- package/dist/models/default/django_app/dummymodel.schema.json +97 -0
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +80 -0
- package/dist/models/default/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +57 -0
- package/dist/models/default/django_app/filetest.d.ts +128 -0
- package/dist/models/default/django_app/filetest.js +69 -0
- package/dist/models/default/django_app/filetest.schema.json +99 -0
- package/dist/models/default/django_app/index.d.ts +1 -0
- package/dist/models/default/django_app/index.js +21 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/order.d.ts +220 -0
- package/dist/models/default/django_app/order.js +71 -0
- package/dist/models/default/django_app/order.schema.json +203 -0
- package/dist/models/default/django_app/orderitem.d.ts +172 -0
- package/dist/models/default/django_app/orderitem.js +72 -0
- package/dist/models/default/django_app/orderitem.schema.json +149 -0
- package/dist/models/default/django_app/product.d.ts +254 -0
- package/dist/models/default/django_app/product.js +71 -0
- package/dist/models/default/django_app/product.schema.json +277 -0
- package/dist/models/default/django_app/productcategory.d.ts +92 -0
- package/dist/models/default/django_app/productcategory.js +69 -0
- package/dist/models/default/django_app/productcategory.schema.json +70 -0
- package/dist/models/default/django_app/rateplan.d.ts +92 -0
- package/dist/models/default/django_app/rateplan.js +69 -0
- package/dist/models/default/django_app/rateplan.schema.json +70 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/default/fileobject.d.ts +4 -0
- package/dist/models/default/fileobject.js +9 -0
- package/dist/models/default/index.d.ts +2 -0
- package/dist/models/default/index.js +2 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +5 -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/reset.d.ts +15 -0
- package/dist/reset.js +97 -0
- package/dist/setup.d.ts +15 -0
- package/dist/setup.js +33 -0
- package/dist/syncEngine/cache/cache.d.ts +75 -0
- package/dist/syncEngine/cache/cache.js +355 -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 +58 -0
- package/dist/syncEngine/registries/metricRegistry.js +171 -0
- package/dist/syncEngine/registries/modelStoreRegistry.d.ts +11 -0
- package/dist/syncEngine/registries/modelStoreRegistry.js +63 -0
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +41 -0
- package/dist/syncEngine/registries/querysetStoreGraph.js +174 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +72 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +335 -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 +53 -0
- package/dist/syncEngine/stores/modelStore.js +565 -0
- package/dist/syncEngine/stores/operation.d.ts +139 -0
- package/dist/syncEngine/stores/operation.js +291 -0
- package/dist/syncEngine/stores/operationEventHandlers.d.ts +8 -0
- package/dist/syncEngine/stores/operationEventHandlers.js +348 -0
- package/dist/syncEngine/stores/querysetStore.d.ts +60 -0
- package/dist/syncEngine/stores/querysetStore.js +294 -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 +51 -0
- package/dist/syncEngine/sync.js +419 -0
- package/dist/testing.d.ts +63 -0
- package/dist/testing.js +175 -0
- package/dist/vue-entry.d.ts +15 -0
- package/dist/vue-entry.js +7 -0
- package/package.json +6 -7
- package/dist/adaptors/vue/components/layout.tailwind.css +0 -51
- /package/{dist → src}/adaptors/vue/components/layout.css +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Operation, Status, Type, operationRegistry } from './operation.js';
|
|
2
|
+
import { querysetEventEmitter } from './reactivity.js';
|
|
3
|
+
import { isNil, isEmpty, trim, isEqual } from 'lodash-es';
|
|
4
|
+
import { replaceTempPks, containsTempPk } from '../../flavours/django/tempPk.js';
|
|
5
|
+
import { modelStoreRegistry } from '../registries/modelStoreRegistry.js';
|
|
6
|
+
import { processIncludedEntities } from '../../flavours/django/makeApiCall.js';
|
|
7
|
+
import { Cache } from '../cache/cache.js';
|
|
8
|
+
import { filter } from "../../filtering/localFiltering.js";
|
|
9
|
+
export class QuerysetStore {
|
|
10
|
+
constructor(modelClass, fetchFn, queryset, initialGroundTruthPks = null, initialOperations = null, options = {}) {
|
|
11
|
+
this.modelClass = modelClass;
|
|
12
|
+
this.fetchFn = fetchFn;
|
|
13
|
+
this.queryset = queryset;
|
|
14
|
+
this.isSyncing = false;
|
|
15
|
+
this.lastSync = null;
|
|
16
|
+
this.isTemp = options.isTemp || false;
|
|
17
|
+
this.pruneThreshold = options.pruneThreshold || 10;
|
|
18
|
+
this.groundTruthPks = initialGroundTruthPks || [];
|
|
19
|
+
this.operationsMap = new Map();
|
|
20
|
+
// Track which model PKs are in this queryset's included data
|
|
21
|
+
// Map<modelName, Set<pk>>
|
|
22
|
+
this.includedPks = new Map();
|
|
23
|
+
if (Array.isArray(initialOperations)) {
|
|
24
|
+
for (const opData of initialOperations) {
|
|
25
|
+
const existing = operationRegistry.get(opData.operationId);
|
|
26
|
+
const op = existing || new Operation(opData, true);
|
|
27
|
+
this.operationsMap.set(op.operationId, op);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
this.qsCache = new Cache("queryset-cache", {}, this.onHydrated.bind(this));
|
|
31
|
+
this._lastRenderedPks = null;
|
|
32
|
+
this.renderCallbacks = new Set();
|
|
33
|
+
// Register for model store changes to re-render when model data changes
|
|
34
|
+
// Only register permanent stores - temp stores are transient and should not
|
|
35
|
+
// accumulate callbacks (causes reactivity cascade when Vue creates new querysets)
|
|
36
|
+
if (!this.isTemp) {
|
|
37
|
+
const modelStore = modelStoreRegistry.getStore(this.modelClass);
|
|
38
|
+
this._modelStoreUnregister = modelStore.registerRenderCallback(() => {
|
|
39
|
+
this._emitRenderEvent();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Caching
|
|
44
|
+
get cacheKey() {
|
|
45
|
+
return this.queryset.semanticKey;
|
|
46
|
+
}
|
|
47
|
+
onHydrated(hydratedData) {
|
|
48
|
+
if (this.groundTruthPks.length === 0 && this.operationsMap.size === 0) {
|
|
49
|
+
const cached = this.qsCache.get(this.cacheKey);
|
|
50
|
+
if (!isNil(cached) && !isEmpty(cached)) {
|
|
51
|
+
console.log(`[QuerysetStore] Hydrated ${this.modelClass.modelName} queryset with ${cached.length} items from cache`);
|
|
52
|
+
this.setGroundTruth(cached);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setCache(result) {
|
|
57
|
+
let nonTempPks = [];
|
|
58
|
+
result.forEach((pk) => {
|
|
59
|
+
if (typeof pk === "string" && containsTempPk(pk)) {
|
|
60
|
+
pk = replaceTempPks(pk);
|
|
61
|
+
if (isNil(pk) || isEmpty(trim(pk))) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
nonTempPks.push(pk);
|
|
66
|
+
});
|
|
67
|
+
this.qsCache.set(this.cacheKey, nonTempPks);
|
|
68
|
+
}
|
|
69
|
+
clearCache() {
|
|
70
|
+
this.qsCache.delete(this.cacheKey);
|
|
71
|
+
}
|
|
72
|
+
// -- Core methods --
|
|
73
|
+
get operations() {
|
|
74
|
+
return Array.from(this.operationsMap.values());
|
|
75
|
+
}
|
|
76
|
+
get pkField() {
|
|
77
|
+
return this.modelClass.primaryKeyField;
|
|
78
|
+
}
|
|
79
|
+
get groundTruthSet() {
|
|
80
|
+
return new Set(this.groundTruthPks);
|
|
81
|
+
}
|
|
82
|
+
_emitRenderEvent() {
|
|
83
|
+
const newPks = this.render(true, false);
|
|
84
|
+
// 1. Always notify direct child stores to trigger their own re-evaluation.
|
|
85
|
+
// They will perform their own check to see if their own results have changed.
|
|
86
|
+
this.renderCallbacks.forEach((callback) => {
|
|
87
|
+
try {
|
|
88
|
+
callback();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.warn("Error in render callback:", error);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// 2. Only emit the global event for UI components if the final list of PKs has actually changed.
|
|
95
|
+
if (!isEqual(newPks, this._lastRenderedPks)) {
|
|
96
|
+
this._lastRenderedPks = newPks; // Update the cache with the new state
|
|
97
|
+
querysetEventEmitter.emit(`${this.modelClass.configKey}::${this.modelClass.modelName}::queryset::render`, { ast: this.queryset.build(), ModelClass: this.modelClass });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async addOperation(operation) {
|
|
101
|
+
this.operationsMap.set(operation.operationId, operation);
|
|
102
|
+
if (this.operationsMap.size > this.pruneThreshold) {
|
|
103
|
+
this.prune();
|
|
104
|
+
}
|
|
105
|
+
this._emitRenderEvent();
|
|
106
|
+
}
|
|
107
|
+
async updateOperation(operation) {
|
|
108
|
+
if (!this.operationsMap.has(operation.operationId))
|
|
109
|
+
return;
|
|
110
|
+
this.operationsMap.set(operation.operationId, operation);
|
|
111
|
+
this._emitRenderEvent();
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
async confirm(operation) {
|
|
115
|
+
if (!this.operationsMap.has(operation.operationId))
|
|
116
|
+
return;
|
|
117
|
+
this.operationsMap.set(operation.operationId, operation);
|
|
118
|
+
this._emitRenderEvent();
|
|
119
|
+
}
|
|
120
|
+
async reject(operation) {
|
|
121
|
+
if (!this.operationsMap.has(operation.operationId))
|
|
122
|
+
return;
|
|
123
|
+
this.operationsMap.set(operation.operationId, operation);
|
|
124
|
+
this._emitRenderEvent();
|
|
125
|
+
}
|
|
126
|
+
async setGroundTruth(groundTruthPks) {
|
|
127
|
+
this.groundTruthPks = Array.isArray(groundTruthPks) ? groundTruthPks : [];
|
|
128
|
+
this.lastSync = Date.now();
|
|
129
|
+
this._emitRenderEvent();
|
|
130
|
+
}
|
|
131
|
+
async setOperations(operations) {
|
|
132
|
+
this.operationsMap.clear();
|
|
133
|
+
if (Array.isArray(operations)) {
|
|
134
|
+
for (const op of operations) {
|
|
135
|
+
this.operationsMap.set(op.operationId, op);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this._emitRenderEvent();
|
|
139
|
+
}
|
|
140
|
+
getTrimmedOperations() {
|
|
141
|
+
const cutoff = Date.now() - 1000 * 60 * 2;
|
|
142
|
+
return this.operations.filter((op) => op.timestamp > cutoff);
|
|
143
|
+
}
|
|
144
|
+
getInflightOperations() {
|
|
145
|
+
return this.operations.filter((operation) => operation.status != Status.CONFIRMED &&
|
|
146
|
+
operation.status != Status.REJECTED);
|
|
147
|
+
}
|
|
148
|
+
prune() {
|
|
149
|
+
const renderedPks = this.render(false);
|
|
150
|
+
this.setGroundTruth(renderedPks);
|
|
151
|
+
this.setOperations(this.getInflightOperations());
|
|
152
|
+
}
|
|
153
|
+
registerRenderCallback(callback) {
|
|
154
|
+
this.renderCallbacks.add(callback);
|
|
155
|
+
return () => this.renderCallbacks.delete(callback);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Register this store with the model store for change notifications.
|
|
159
|
+
* Called when a temp store is promoted to permanent.
|
|
160
|
+
*/
|
|
161
|
+
registerWithModelStore() {
|
|
162
|
+
if (this._modelStoreUnregister)
|
|
163
|
+
return; // Already registered
|
|
164
|
+
const modelStore = modelStoreRegistry.getStore(this.modelClass);
|
|
165
|
+
this._modelStoreUnregister = modelStore.registerRenderCallback(() => {
|
|
166
|
+
this._emitRenderEvent();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Helper to validate PKs against the model store and apply local filtering/sorting.
|
|
171
|
+
* This is the core of the rendering logic.
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
_getValidatedAndFilteredPks(pks) {
|
|
175
|
+
// 1. Convert PKs to instances, filtering out any that are null (deleted).
|
|
176
|
+
const instances = Array.from(pks)
|
|
177
|
+
.map((pk) => this.modelClass.fromPk(pk, this.queryset))
|
|
178
|
+
.filter((instance) => modelStoreRegistry.getEntity(this.modelClass, instance.pk) !== null);
|
|
179
|
+
// 2. Apply the queryset's AST (filters, ordering) to the validated instances.
|
|
180
|
+
const ast = this.queryset.build();
|
|
181
|
+
const finalPks = filter(instances, ast, this.modelClass, false); // false = return PKs
|
|
182
|
+
return finalPks;
|
|
183
|
+
}
|
|
184
|
+
render(optimistic = true, fromCache = false) {
|
|
185
|
+
// Check cache first if requested
|
|
186
|
+
if (fromCache) {
|
|
187
|
+
const cachedResult = this.qsCache.get(this.cacheKey);
|
|
188
|
+
if (Array.isArray(cachedResult)) {
|
|
189
|
+
return cachedResult;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// If no ground truth AND hasn't been synced, render from model store
|
|
193
|
+
// This handles chained optimistic filters, newly created stores, etc.
|
|
194
|
+
// (If synced with empty results, that's valid ground truth)
|
|
195
|
+
const pks = this.groundTruthPks.length === 0 && this.lastSync === null
|
|
196
|
+
? this.renderFromModelStore()
|
|
197
|
+
: this.renderFromData(optimistic);
|
|
198
|
+
// Validate against model store and apply local filtering/sorting
|
|
199
|
+
let result = this._getValidatedAndFilteredPks(pks);
|
|
200
|
+
// Apply pagination limit
|
|
201
|
+
const limit = this.queryset.build().serializerOptions?.limit;
|
|
202
|
+
if (limit) {
|
|
203
|
+
result = result.slice(0, limit);
|
|
204
|
+
}
|
|
205
|
+
this.setCache(result);
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
renderFromData(optimistic = true) {
|
|
209
|
+
const renderedPks = this.groundTruthSet;
|
|
210
|
+
for (const op of this.operations) {
|
|
211
|
+
if (op.status !== Status.REJECTED &&
|
|
212
|
+
(optimistic || op.status === Status.CONFIRMED)) {
|
|
213
|
+
this.applyOperation(op, renderedPks);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let result = Array.from(renderedPks);
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Render by getting all instances from the model store and filtering locally.
|
|
221
|
+
* Used when a queryset has no ground truth (temp stores, newly created stores, etc.)
|
|
222
|
+
*/
|
|
223
|
+
renderFromModelStore() {
|
|
224
|
+
const modelStore = modelStoreRegistry.getStore(this.modelClass);
|
|
225
|
+
const allPks = modelStore.groundTruthPks;
|
|
226
|
+
const allInstances = allPks.map((pk) => this.modelClass.fromPk(pk, this.queryset));
|
|
227
|
+
const ast = this.queryset.build();
|
|
228
|
+
return filter(allInstances, ast, this.modelClass, false);
|
|
229
|
+
}
|
|
230
|
+
applyOperation(operation, currentPks) {
|
|
231
|
+
const pkField = this.pkField;
|
|
232
|
+
for (const instance of operation.instances) {
|
|
233
|
+
if (!instance || typeof instance !== "object" || !(pkField in instance)) {
|
|
234
|
+
console.warn(`[QuerysetStore ${this.modelClass.modelName}] Skipping instance in operation ${operation.operationId} due to missing PK '${String(pkField)}' or invalid format.`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
let pk = instance[pkField];
|
|
238
|
+
switch (operation.type) {
|
|
239
|
+
case Type.CREATE:
|
|
240
|
+
case Type.BULK_CREATE:
|
|
241
|
+
currentPks.add(pk);
|
|
242
|
+
break;
|
|
243
|
+
case Type.CHECKPOINT:
|
|
244
|
+
case Type.UPDATE:
|
|
245
|
+
case Type.UPDATE_INSTANCE:
|
|
246
|
+
break;
|
|
247
|
+
case Type.DELETE:
|
|
248
|
+
case Type.DELETE_INSTANCE:
|
|
249
|
+
currentPks.delete(pk);
|
|
250
|
+
break;
|
|
251
|
+
default:
|
|
252
|
+
console.error(`[QuerysetStore ${this.modelClass.modelName}] Unknown operation type: ${operation.type}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return currentPks;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Sync this queryset with the database.
|
|
259
|
+
* Fetches from DB and sets ground truth.
|
|
260
|
+
*/
|
|
261
|
+
async sync() {
|
|
262
|
+
const id = this.modelClass.modelName;
|
|
263
|
+
if (this.isSyncing) {
|
|
264
|
+
console.warn(`[QuerysetStore ${id}] Already syncing, request ignored.`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.isSyncing = true;
|
|
268
|
+
console.log(`[${id}] Starting sync...`);
|
|
269
|
+
try {
|
|
270
|
+
const response = await this.fetchFn({
|
|
271
|
+
ast: this.queryset.build(),
|
|
272
|
+
modelClass: this.modelClass,
|
|
273
|
+
});
|
|
274
|
+
const { data, included } = response;
|
|
275
|
+
if (!isNil(data)) {
|
|
276
|
+
console.log(`[${id}] Sync fetch completed. Received: ${JSON.stringify(data)}.`);
|
|
277
|
+
// Clear previous included PKs tracking before processing new data
|
|
278
|
+
this.includedPks.clear();
|
|
279
|
+
// Persist all instances (including nested) to the model store
|
|
280
|
+
processIncludedEntities(modelStoreRegistry, included, this.modelClass, this.queryset);
|
|
281
|
+
this.setGroundTruth(data);
|
|
282
|
+
}
|
|
283
|
+
this.setOperations(this.getInflightOperations());
|
|
284
|
+
this.lastSync = Date.now();
|
|
285
|
+
console.log(`[${id}] Sync completed.`);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
console.error(`[${id}] Failed to sync ground truth:`, e);
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
this.isSyncing = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guarantees an array of operations, is an array of operations. Handles
|
|
3
|
+
* the basic failure case of a single operation.
|
|
4
|
+
*/
|
|
5
|
+
export function validateOperationsArray(operationsArray: any): any;
|
|
6
|
+
/**
|
|
7
|
+
* Extract the minimal signature we're using for “direct‐match” comparison.
|
|
8
|
+
*/
|
|
9
|
+
export function getFingerprint(qs: any): {
|
|
10
|
+
nodes: any;
|
|
11
|
+
initialQueryset: any;
|
|
12
|
+
offset: any;
|
|
13
|
+
limit: any;
|
|
14
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isNil, isEqual } from 'lodash-es';
|
|
2
|
+
import { filter } from '../../filtering/localFiltering.js';
|
|
3
|
+
/**
|
|
4
|
+
* Guarantees an array of operations, is an array of operations. Handles
|
|
5
|
+
* the basic failure case of a single operation.
|
|
6
|
+
*/
|
|
7
|
+
export function validateOperationsArray(operationsArray) {
|
|
8
|
+
// verify the operations list
|
|
9
|
+
if (Array.isArray(operationsArray)) {
|
|
10
|
+
if (operationsArray.length > 0) {
|
|
11
|
+
if (!(operationsArray[0] instanceof Operation)) {
|
|
12
|
+
throw new Error(`operationsArray must be Operations not ${typeof (operationsArray[0])}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (!isNil(operationsArray)) {
|
|
17
|
+
if (!(operationsArray instanceof Operation)) {
|
|
18
|
+
throw new Error(`operationsArray must be Operations not ${typeof (operationsArray)}`);
|
|
19
|
+
}
|
|
20
|
+
operationsArray = [operationsArray]; // coerce it to be an array
|
|
21
|
+
}
|
|
22
|
+
return operationsArray;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract the minimal signature we're using for “direct‐match” comparison.
|
|
26
|
+
*/
|
|
27
|
+
export function getFingerprint(qs) {
|
|
28
|
+
const { nodes } = qs;
|
|
29
|
+
const initialQueryset = qs._initialQueryset;
|
|
30
|
+
const { offset = null, limit = null } = qs._serializerOptions || {};
|
|
31
|
+
return { nodes, initialQueryset, offset, limit };
|
|
32
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export class EventPayload {
|
|
2
|
+
constructor(data: any);
|
|
3
|
+
event: any;
|
|
4
|
+
model: any;
|
|
5
|
+
operation_id: any;
|
|
6
|
+
pk_field_name: any;
|
|
7
|
+
configKey: any;
|
|
8
|
+
instances: any;
|
|
9
|
+
_cachedInstances: any;
|
|
10
|
+
get modelClass(): Function | null;
|
|
11
|
+
getFullInstances(): Promise<any>;
|
|
12
|
+
}
|
|
13
|
+
export class SyncManager {
|
|
14
|
+
registries: Map<any, any>;
|
|
15
|
+
followedModels: Map<any, any>;
|
|
16
|
+
followAllQuerysets: boolean;
|
|
17
|
+
followedQuerysets: Map<any, any>;
|
|
18
|
+
periodicSyncTimer: NodeJS.Timeout | null;
|
|
19
|
+
eventBatch: Map<any, any>;
|
|
20
|
+
debounceTimer: NodeJS.Timeout | null;
|
|
21
|
+
maxWaitTimer: NodeJS.Timeout | null;
|
|
22
|
+
debounceMs: number;
|
|
23
|
+
maxWaitMs: number;
|
|
24
|
+
batchStartTime: number | null;
|
|
25
|
+
withTimeout(promise: any, ms: any): Promise<any>;
|
|
26
|
+
/**
|
|
27
|
+
* Initialize event handlers for all event receivers
|
|
28
|
+
*/
|
|
29
|
+
initialize(): void;
|
|
30
|
+
startPeriodicSync(): void;
|
|
31
|
+
syncStaleQuerysets(): Promise<void>;
|
|
32
|
+
pruneUnreferencedModels(): void;
|
|
33
|
+
isStoreFollowed(registry: any, semanticKey: any): boolean;
|
|
34
|
+
cleanup(): void;
|
|
35
|
+
followModel(registry: any, modelClass: any): void;
|
|
36
|
+
unfollowModel(registry: any, modelClass: any): void;
|
|
37
|
+
manageRegistry(registry: any): void;
|
|
38
|
+
removeRegistry(registry: any): void;
|
|
39
|
+
/**
|
|
40
|
+
* Sync querysets that were marked MAYBE for a local operation.
|
|
41
|
+
* Called when a backend event arrives for an operation we initiated locally.
|
|
42
|
+
*/
|
|
43
|
+
syncMaybeQuerysets(operationId: any): void;
|
|
44
|
+
handleEvent: (event: any) => void;
|
|
45
|
+
processBatch(reason?: string): void;
|
|
46
|
+
isQuerysetFollowed(queryset: any): boolean;
|
|
47
|
+
processQuerysetsBatch(representativeEvent: any, allEvents: any): void;
|
|
48
|
+
processMetrics(event: any): void;
|
|
49
|
+
processModels(event: any): void;
|
|
50
|
+
}
|
|
51
|
+
export const syncManager: SyncManager;
|