@statezero/core 0.2.51 → 0.2.52
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/vue/components/StateZeroDebugPanel.js +495 -476
- package/dist/adaptors/vue/reactivity.d.ts +1 -0
- package/dist/adaptors/vue/reactivity.js +17 -5
- package/dist/cli/commands/syncActions.js +33 -1
- package/dist/cli/commands/syncModels.js +44 -3
- package/dist/filtering/localFiltering.js +3 -2
- package/dist/flavours/django/errors.d.ts +12 -0
- package/dist/flavours/django/errors.js +20 -0
- package/dist/flavours/django/f.js +16 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/models/backend1/django_app/author.d.ts +284 -0
- package/dist/models/backend1/django_app/author.js +69 -0
- package/dist/models/backend1/django_app/author.schema.json +281 -0
- package/dist/models/backend1/django_app/book.d.ts +262 -0
- package/dist/models/backend1/django_app/book.js +71 -0
- package/dist/models/backend1/django_app/book.schema.json +254 -0
- package/dist/models/backend1/django_app/composeditem.d.ts +140 -0
- package/dist/models/backend1/django_app/composeditem.js +69 -0
- package/dist/models/backend1/django_app/composeditem.schema.json +122 -0
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +3 -3
- package/dist/models/backend1/django_app/dailyrate.schema.json +7 -7
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +1 -1
- package/dist/models/backend1/django_app/dummymodel.schema.json +3 -3
- package/dist/models/backend1/django_app/errortestcompoundunique.d.ts +124 -0
- package/dist/models/backend1/django_app/errortestcompoundunique.js +69 -0
- package/dist/models/backend1/django_app/errortestcompoundunique.schema.json +106 -0
- package/dist/models/backend1/django_app/errortestonetoonemodel.d.ts +118 -0
- package/dist/models/backend1/django_app/errortestonetoonemodel.js +71 -0
- package/dist/models/backend1/django_app/errortestonetoonemodel.schema.json +90 -0
- package/dist/models/backend1/django_app/errortestparent.d.ts +92 -0
- package/dist/models/backend1/django_app/errortestparent.js +69 -0
- package/dist/models/backend1/django_app/errortestparent.schema.json +70 -0
- package/dist/models/backend1/django_app/errortestprotectedchild.d.ts +118 -0
- package/dist/models/backend1/django_app/errortestprotectedchild.js +71 -0
- package/dist/models/backend1/django_app/errortestprotectedchild.schema.json +94 -0
- package/dist/models/backend1/django_app/errortestuniquemodel.d.ts +108 -0
- package/dist/models/backend1/django_app/errortestuniquemodel.js +69 -0
- package/dist/models/backend1/django_app/errortestuniquemodel.schema.json +88 -0
- package/dist/models/backend1/django_app/excludeditem.d.ts +124 -0
- package/dist/models/backend1/django_app/excludeditem.js +69 -0
- package/dist/models/backend1/django_app/excludeditem.schema.json +105 -0
- package/dist/models/backend1/django_app/hfchild.d.ts +118 -0
- package/dist/models/backend1/django_app/hfchild.js +71 -0
- package/dist/models/backend1/django_app/hfchild.schema.json +94 -0
- package/dist/models/backend1/django_app/hfparent.d.ts +124 -0
- package/dist/models/backend1/django_app/hfparent.js +69 -0
- package/dist/models/backend1/django_app/hfparent.schema.json +105 -0
- package/dist/models/backend1/django_app/index.js +19 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +3 -3
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +3 -3
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +5 -5
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +2 -2
- package/dist/models/backend1/django_app/nodeleteitem.d.ts +124 -0
- package/dist/models/backend1/django_app/nodeleteitem.js +69 -0
- package/dist/models/backend1/django_app/nodeleteitem.schema.json +105 -0
- package/dist/models/backend1/django_app/objectlevelitem.d.ts +124 -0
- package/dist/models/backend1/django_app/objectlevelitem.js +69 -0
- package/dist/models/backend1/django_app/objectlevelitem.schema.json +105 -0
- package/dist/models/backend1/django_app/order.schema.json +7 -7
- package/dist/models/backend1/django_app/orderitem.schema.json +5 -5
- package/dist/models/backend1/django_app/product.schema.json +11 -11
- package/dist/models/backend1/django_app/productcategory.schema.json +2 -2
- package/dist/models/backend1/django_app/rateplan.schema.json +2 -2
- package/dist/models/backend1/django_app/readonlyitem.d.ts +124 -0
- package/dist/models/backend1/django_app/readonlyitem.js +69 -0
- package/dist/models/backend1/django_app/readonlyitem.schema.json +105 -0
- package/dist/models/backend1/django_app/restrictedcreateitem.d.ts +124 -0
- package/dist/models/backend1/django_app/restrictedcreateitem.js +69 -0
- package/dist/models/backend1/django_app/restrictedcreateitem.schema.json +105 -0
- package/dist/models/backend1/django_app/restrictededititem.d.ts +124 -0
- package/dist/models/backend1/django_app/restrictededititem.js +69 -0
- package/dist/models/backend1/django_app/restrictededititem.schema.json +105 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +1 -1
- package/dist/models/backend1/django_app/rowfiltereditem.d.ts +124 -0
- package/dist/models/backend1/django_app/rowfiltereditem.js +69 -0
- package/dist/models/backend1/django_app/rowfiltereditem.schema.json +105 -0
- package/dist/models/backend1/django_app/tag.d.ts +554 -0
- package/dist/models/backend1/django_app/tag.js +71 -0
- package/dist/models/backend1/django_app/tag.schema.json +541 -0
- package/dist/models/backend1/django_app/updateonlyitem.d.ts +108 -0
- package/dist/models/backend1/django_app/updateonlyitem.js +69 -0
- package/dist/models/backend1/django_app/updateonlyitem.schema.json +88 -0
- package/dist/models/default/django_app/author.d.ts +284 -0
- package/dist/models/default/django_app/author.js +69 -0
- package/dist/models/default/django_app/author.schema.json +281 -0
- package/dist/models/default/django_app/book.d.ts +262 -0
- package/dist/models/default/django_app/book.js +71 -0
- package/dist/models/default/django_app/book.schema.json +254 -0
- package/dist/models/default/django_app/composeditem.d.ts +140 -0
- package/dist/models/default/django_app/composeditem.js +69 -0
- package/dist/models/default/django_app/composeditem.schema.json +122 -0
- package/dist/models/default/django_app/comprehensivemodel.schema.json +3 -3
- package/dist/models/default/django_app/dailyrate.schema.json +7 -7
- package/dist/models/default/django_app/deepmodellevel1.d.ts +1 -13
- package/dist/models/default/django_app/deepmodellevel1.schema.json +2 -14
- package/dist/models/default/django_app/deepmodellevel2.d.ts +1 -13
- package/dist/models/default/django_app/deepmodellevel2.schema.json +1 -13
- package/dist/models/default/django_app/deepmodellevel3.d.ts +1 -13
- package/dist/models/default/django_app/deepmodellevel3.schema.json +1 -13
- package/dist/models/default/django_app/dummymodel.d.ts +1 -13
- package/dist/models/default/django_app/dummymodel.schema.json +4 -16
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +1 -13
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +1 -13
- package/dist/models/default/django_app/errortestcompoundunique.d.ts +124 -0
- package/dist/models/default/django_app/errortestcompoundunique.js +69 -0
- package/dist/models/default/django_app/errortestcompoundunique.schema.json +106 -0
- package/dist/models/default/django_app/errortestonetoonemodel.d.ts +118 -0
- package/dist/models/default/django_app/errortestonetoonemodel.js +71 -0
- package/dist/models/default/django_app/errortestonetoonemodel.schema.json +90 -0
- package/dist/models/default/django_app/errortestparent.d.ts +92 -0
- package/dist/models/default/django_app/errortestparent.js +69 -0
- package/dist/models/default/django_app/errortestparent.schema.json +70 -0
- package/dist/models/default/django_app/errortestprotectedchild.d.ts +118 -0
- package/dist/models/default/django_app/errortestprotectedchild.js +71 -0
- package/dist/models/default/django_app/errortestprotectedchild.schema.json +94 -0
- package/dist/models/default/django_app/errortestuniquemodel.d.ts +108 -0
- package/dist/models/default/django_app/errortestuniquemodel.js +69 -0
- package/dist/models/default/django_app/errortestuniquemodel.schema.json +88 -0
- package/dist/models/default/django_app/excludeditem.d.ts +124 -0
- package/dist/models/default/django_app/excludeditem.js +69 -0
- package/dist/models/default/django_app/excludeditem.schema.json +105 -0
- package/dist/models/default/django_app/filetest.d.ts +1 -13
- package/dist/models/default/django_app/filetest.schema.json +1 -13
- package/dist/models/default/django_app/hfchild.d.ts +118 -0
- package/dist/models/default/django_app/hfchild.js +71 -0
- package/dist/models/default/django_app/hfchild.schema.json +94 -0
- package/dist/models/default/django_app/hfparent.d.ts +124 -0
- package/dist/models/default/django_app/hfparent.js +69 -0
- package/dist/models/default/django_app/hfparent.schema.json +105 -0
- package/dist/models/default/django_app/index.js +19 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +3 -3
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +3 -3
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +5 -5
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +2 -2
- package/dist/models/default/django_app/nodeleteitem.d.ts +124 -0
- package/dist/models/default/django_app/nodeleteitem.js +69 -0
- package/dist/models/default/django_app/nodeleteitem.schema.json +105 -0
- package/dist/models/default/django_app/objectlevelitem.d.ts +124 -0
- package/dist/models/default/django_app/objectlevelitem.js +69 -0
- package/dist/models/default/django_app/objectlevelitem.schema.json +105 -0
- package/dist/models/default/django_app/order.schema.json +7 -7
- package/dist/models/default/django_app/orderitem.schema.json +5 -5
- package/dist/models/default/django_app/product.schema.json +11 -11
- package/dist/models/default/django_app/productcategory.schema.json +2 -2
- package/dist/models/default/django_app/rateplan.schema.json +2 -2
- package/dist/models/default/django_app/readonlyitem.d.ts +124 -0
- package/dist/models/default/django_app/readonlyitem.js +69 -0
- package/dist/models/default/django_app/readonlyitem.schema.json +105 -0
- package/dist/models/default/django_app/restrictedcreateitem.d.ts +124 -0
- package/dist/models/default/django_app/restrictedcreateitem.js +69 -0
- package/dist/models/default/django_app/restrictedcreateitem.schema.json +105 -0
- package/dist/models/default/django_app/restrictededititem.d.ts +124 -0
- package/dist/models/default/django_app/restrictededititem.js +69 -0
- package/dist/models/default/django_app/restrictededititem.schema.json +105 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +1 -1
- package/dist/models/default/django_app/rowfiltereditem.d.ts +124 -0
- package/dist/models/default/django_app/rowfiltereditem.js +69 -0
- package/dist/models/default/django_app/rowfiltereditem.schema.json +105 -0
- package/dist/models/default/django_app/tag.d.ts +554 -0
- package/dist/models/default/django_app/tag.js +71 -0
- package/dist/models/default/django_app/tag.schema.json +541 -0
- package/dist/models/default/django_app/updateonlyitem.d.ts +108 -0
- package/dist/models/default/django_app/updateonlyitem.js +69 -0
- package/dist/models/default/django_app/updateonlyitem.schema.json +88 -0
- package/dist/schemaIntegrity.d.ts +14 -0
- package/dist/schemaIntegrity.js +29 -0
- package/dist/setup.js +6 -0
- package/dist/syncEngine/stores/operationEventHandlers.js +10 -8
- package/dist/syncEngine/stores/querysetStore.js +9 -0
- package/package.json +1 -1
|
@@ -16,3 +16,4 @@ export function ModelAdaptor(modelInstance: Object, reactivityFn?: Function): an
|
|
|
16
16
|
*/
|
|
17
17
|
export function QuerySetAdaptor(liveQuerySet: Object, reactivityFn?: Function): any | import("vue").Ref;
|
|
18
18
|
export function MetricAdaptor(metric: any): any;
|
|
19
|
+
export function resetVueAdaptors(): void;
|
|
@@ -123,15 +123,26 @@ export function QuerySetAdaptor(liveQuerySet, reactivityFn = reactive) {
|
|
|
123
123
|
wrapper.__version = 0;
|
|
124
124
|
const eventName = `${configKey}::${modelName}::queryset::render`;
|
|
125
125
|
// Handler bumps version to trigger Vue reactivity when this queryset updates
|
|
126
|
+
const pkField = queryset.ModelClass.primaryKeyField;
|
|
126
127
|
const renderHandler = (eventData) => {
|
|
127
128
|
if (eventData && eventData.ast && isEqual(querysetAst, eventData.ast)) {
|
|
128
|
-
|
|
129
|
+
const newData = [...liveQuerySet];
|
|
130
|
+
const newPks = newData.map(item => item[pkField]);
|
|
131
|
+
const currentArray = reactivityFn === ref ? wrapper.value : wrapper;
|
|
132
|
+
const oldPks = currentArray.map(item => item[pkField]);
|
|
133
|
+
// Skip if PKs haven't changed — field-level updates on existing items
|
|
134
|
+
// are already handled by Vue's deep reactivity via model touch().
|
|
135
|
+
// Bumping __version here would cause useQueryset's computed to
|
|
136
|
+
// re-evaluate and re-run the factory, risking circular reactive loops.
|
|
137
|
+
if (isEqual(oldPks, newPks))
|
|
138
|
+
return;
|
|
139
|
+
console.log(`[sz] queryset update: ${modelName}`, { count: newData.length, version: wrapper.__version + 1 });
|
|
129
140
|
if (reactivityFn === ref) {
|
|
130
|
-
wrapper.value =
|
|
141
|
+
wrapper.value = newData;
|
|
131
142
|
}
|
|
132
143
|
else {
|
|
133
144
|
wrapper.splice(0, wrapper.length);
|
|
134
|
-
wrapper.push(...
|
|
145
|
+
wrapper.push(...newData);
|
|
135
146
|
}
|
|
136
147
|
// Bump version so computed/watch can track changes
|
|
137
148
|
wrapper.__version++;
|
|
@@ -185,7 +196,8 @@ export function MetricAdaptor(metric) {
|
|
|
185
196
|
}
|
|
186
197
|
return wrapper;
|
|
187
198
|
}
|
|
188
|
-
|
|
199
|
+
export function resetVueAdaptors() {
|
|
189
200
|
wrappedQuerysetCache.clear();
|
|
190
201
|
wrappedMetricCache.clear();
|
|
191
|
-
}
|
|
202
|
+
}
|
|
203
|
+
registerAdapterReset(resetVueAdaptors);
|
|
@@ -768,6 +768,38 @@ export declare namespace getCurrentUser {
|
|
|
768
768
|
return generatedFiles;
|
|
769
769
|
}
|
|
770
770
|
// ================================================================================================
|
|
771
|
+
// SYNC TOKEN ERROR HANDLING
|
|
772
|
+
// ================================================================================================
|
|
773
|
+
function maskToken(token) {
|
|
774
|
+
if (!token || token.length <= 4)
|
|
775
|
+
return token || '';
|
|
776
|
+
return token.slice(0, 4) + '...';
|
|
777
|
+
}
|
|
778
|
+
function handleSyncFetchError(error, backend, endpoint) {
|
|
779
|
+
const status = error.response?.status;
|
|
780
|
+
const syncToken = backend.SYNC_TOKEN;
|
|
781
|
+
if (status === 409) {
|
|
782
|
+
console.error(`\n❌ Sync token mismatch for backend '${backend.NAME}' (${endpoint}).` +
|
|
783
|
+
`\n The token sent was: '${maskToken(syncToken)}'.` +
|
|
784
|
+
`\n Check that SYNC_TOKEN in your statezero config matches STATEZERO_SYNC_TOKEN on the server.`);
|
|
785
|
+
}
|
|
786
|
+
else if (status === 403) {
|
|
787
|
+
if (syncToken) {
|
|
788
|
+
console.error(`\n❌ Schema access denied for backend '${backend.NAME}' (${endpoint}).` +
|
|
789
|
+
`\n Sync token sent: '${maskToken(syncToken)}'.` +
|
|
790
|
+
`\n The server may not have STATEZERO_SYNC_TOKEN configured, or the token doesn't match.`);
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
console.error(`\n❌ Schema access denied for backend '${backend.NAME}' (${endpoint}).` +
|
|
794
|
+
`\n No sync token was sent.` +
|
|
795
|
+
`\n Set SYNC_TOKEN in your statezero config to match STATEZERO_SYNC_TOKEN on the server, or enable DEBUG=True on the backend.`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
console.error(`❌ Error fetching ${endpoint} from ${backend.NAME}: ${error.message}`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
// ================================================================================================
|
|
771
803
|
// MAIN SCRIPT RUNNER
|
|
772
804
|
// ================================================================================================
|
|
773
805
|
async function main() {
|
|
@@ -789,7 +821,7 @@ async function main() {
|
|
|
789
821
|
return { backend, actions: response.data.actions || {} };
|
|
790
822
|
}
|
|
791
823
|
catch (error) {
|
|
792
|
-
|
|
824
|
+
handleSyncFetchError(error, backend, '/actions-schema/');
|
|
793
825
|
return { backend, actions: {} };
|
|
794
826
|
}
|
|
795
827
|
});
|
|
@@ -794,7 +794,7 @@ function generateImportAlias(backendKey, modelName, className) {
|
|
|
794
794
|
* @param {Object.<string, BackendConfig>} backendConfigs
|
|
795
795
|
* @returns {Promise<void>}
|
|
796
796
|
*/
|
|
797
|
-
async function generateModelRegistry(generatedFiles, backendConfigs) {
|
|
797
|
+
async function generateModelRegistry(generatedFiles, backendConfigs, schemaChecksums = {}) {
|
|
798
798
|
const registryByBackend = {};
|
|
799
799
|
const allUniqueImports = new Set();
|
|
800
800
|
for (const file of generatedFiles) {
|
|
@@ -876,6 +876,10 @@ export function getModelClass(modelName, configKey) {
|
|
|
876
876
|
return null;
|
|
877
877
|
}
|
|
878
878
|
`;
|
|
879
|
+
// Add schema checksums if any were captured during sync
|
|
880
|
+
if (Object.keys(schemaChecksums).length > 0) {
|
|
881
|
+
registryContent += `\ngetModelClass.SYNC_CHECKSUMS = ${JSON.stringify(schemaChecksums)};\n`;
|
|
882
|
+
}
|
|
879
883
|
// --- Write the file ---
|
|
880
884
|
const rootDir = process.cwd();
|
|
881
885
|
const registryFilePath = path.join(rootDir, "model-registry.js");
|
|
@@ -1027,6 +1031,38 @@ export * from './default/index.js';
|
|
|
1027
1031
|
console.log(`✨ Generated top-level index for 'default' backend`);
|
|
1028
1032
|
}
|
|
1029
1033
|
// --------------------
|
|
1034
|
+
// Sync Token Error Handling
|
|
1035
|
+
// --------------------
|
|
1036
|
+
function maskToken(token) {
|
|
1037
|
+
if (!token || token.length <= 4)
|
|
1038
|
+
return token || '';
|
|
1039
|
+
return token.slice(0, 4) + '...';
|
|
1040
|
+
}
|
|
1041
|
+
function handleSyncFetchError(error, backend, endpoint) {
|
|
1042
|
+
const status = error.response?.status;
|
|
1043
|
+
const syncToken = backend.SYNC_TOKEN;
|
|
1044
|
+
if (status === 409) {
|
|
1045
|
+
console.error(`\n❌ Sync token mismatch for backend '${backend.NAME}' (${endpoint}).` +
|
|
1046
|
+
`\n The token sent was: '${maskToken(syncToken)}'.` +
|
|
1047
|
+
`\n Check that SYNC_TOKEN in your statezero config matches STATEZERO_SYNC_TOKEN on the server.`);
|
|
1048
|
+
}
|
|
1049
|
+
else if (status === 403) {
|
|
1050
|
+
if (syncToken) {
|
|
1051
|
+
console.error(`\n❌ Schema access denied for backend '${backend.NAME}' (${endpoint}).` +
|
|
1052
|
+
`\n Sync token sent: '${maskToken(syncToken)}'.` +
|
|
1053
|
+
`\n The server may not have STATEZERO_SYNC_TOKEN configured, or the token doesn't match.`);
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
console.error(`\n❌ Schema access denied for backend '${backend.NAME}' (${endpoint}).` +
|
|
1057
|
+
`\n No sync token was sent.` +
|
|
1058
|
+
`\n Set SYNC_TOKEN in your statezero config to match STATEZERO_SYNC_TOKEN on the server, or enable DEBUG=True on the backend.`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
console.error(`Error fetching ${endpoint} from backend ${backend.NAME}:`, error.message);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
// --------------------
|
|
1030
1066
|
// Main Runner: Fetch models and prompt selection
|
|
1031
1067
|
// --------------------
|
|
1032
1068
|
async function main() {
|
|
@@ -1035,16 +1071,21 @@ async function main() {
|
|
|
1035
1071
|
// Retrieve the validated configuration from the global config singleton.
|
|
1036
1072
|
const configData = configInstance.getConfig();
|
|
1037
1073
|
const backendConfigs = configData.backendConfigs;
|
|
1074
|
+
const schemaChecksums = {};
|
|
1038
1075
|
const fetchPromises = Object.keys(backendConfigs).map(async (key) => {
|
|
1039
1076
|
const backend = backendConfigs[key];
|
|
1040
1077
|
backend.NAME = key;
|
|
1041
1078
|
try {
|
|
1042
1079
|
const headers = backend.SYNC_TOKEN ? { "X-Sync-Token": backend.SYNC_TOKEN } : {};
|
|
1043
1080
|
const response = await axios.get(`${backend.API_URL}/models/`, { headers });
|
|
1081
|
+
const checksum = response.headers['x-statezero-schema-checksum'];
|
|
1082
|
+
if (checksum) {
|
|
1083
|
+
schemaChecksums[key] = checksum;
|
|
1084
|
+
}
|
|
1044
1085
|
return { backend, models: response.data };
|
|
1045
1086
|
}
|
|
1046
1087
|
catch (error) {
|
|
1047
|
-
|
|
1088
|
+
handleSyncFetchError(error, backend, '/models/');
|
|
1048
1089
|
return { backend, models: [] };
|
|
1049
1090
|
}
|
|
1050
1091
|
});
|
|
@@ -1105,7 +1146,7 @@ async function main() {
|
|
|
1105
1146
|
// Generate an index file per app
|
|
1106
1147
|
await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
|
|
1107
1148
|
// Generate model registry
|
|
1108
|
-
await generateModelRegistry(allGeneratedFiles, backendConfigs);
|
|
1149
|
+
await generateModelRegistry(allGeneratedFiles, backendConfigs, schemaChecksums);
|
|
1109
1150
|
// Generate top-level index for 'default' backend if it exists
|
|
1110
1151
|
await generateDefaultBackendIndex(backendConfigs);
|
|
1111
1152
|
console.log(`✨ Generated JavaScript files with TypeScript declarations for ${selectedModels.length} models across ${Object.keys(backendConfigs).length} backends.`);
|
|
@@ -2,6 +2,7 @@ import sift, { createEqualsOperation } from 'sift';
|
|
|
2
2
|
import { configInstance } from '../config.js';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
4
|
import { ModelSerializer } from '../flavours/django/serializers.js';
|
|
5
|
+
import { ValidationError } from '../flavours/django/errors.js';
|
|
5
6
|
/**
|
|
6
7
|
* Gets the backend timezone for a model class
|
|
7
8
|
* @param {Class} ModelClass - The model class
|
|
@@ -54,7 +55,7 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
|
|
|
54
55
|
const knownLookups = [
|
|
55
56
|
'exact', 'iexact', 'contains', 'icontains', 'startswith',
|
|
56
57
|
'istartswith', 'endswith', 'iendswith', 'in', 'gt', 'gte',
|
|
57
|
-
'lt', 'lte', 'isnull', '
|
|
58
|
+
'lt', 'lte', 'isnull', 'year', 'month',
|
|
58
59
|
'day', 'week_day', 'hour', 'minute', 'second', 'date', 'time'
|
|
59
60
|
];
|
|
60
61
|
// Date part lookups that can be followed by comparison lookups
|
|
@@ -627,7 +628,7 @@ function convertToSiftCriteria(conditions, ModelClass) {
|
|
|
627
628
|
}
|
|
628
629
|
}
|
|
629
630
|
catch (error) {
|
|
630
|
-
throw new
|
|
631
|
+
throw new ValidationError(`Failed to process field '${key}': ${error.message}`);
|
|
631
632
|
}
|
|
632
633
|
}
|
|
633
634
|
// Merge M2M conditions: all conditions on same M2M field go into single $elemMatch
|
|
@@ -88,6 +88,18 @@ export class MultipleObjectsReturned extends StateZeroError {
|
|
|
88
88
|
*/
|
|
89
89
|
constructor(detail?: IErrorDetail | Object | string, status?: number);
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Error class for database conflict errors (integrity/constraint violations).
|
|
93
|
+
*/
|
|
94
|
+
export class ConflictError extends StateZeroError {
|
|
95
|
+
/**
|
|
96
|
+
* Creates a new ConflictError.
|
|
97
|
+
*
|
|
98
|
+
* @param {IErrorDetail|Object|string} [detail="A database conflict occurred."] - The error details.
|
|
99
|
+
* @param {number} [status=409] - The HTTP status code.
|
|
100
|
+
*/
|
|
101
|
+
constructor(detail?: IErrorDetail | Object | string, status?: number);
|
|
102
|
+
}
|
|
91
103
|
/**
|
|
92
104
|
* Error class for AST validation errors.
|
|
93
105
|
*/
|
|
@@ -116,6 +116,21 @@ export class MultipleObjectsReturned extends StateZeroError {
|
|
|
116
116
|
this.name = "MultipleObjectsReturned";
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Error class for database conflict errors (integrity/constraint violations).
|
|
121
|
+
*/
|
|
122
|
+
export class ConflictError extends StateZeroError {
|
|
123
|
+
/**
|
|
124
|
+
* Creates a new ConflictError.
|
|
125
|
+
*
|
|
126
|
+
* @param {IErrorDetail|Object|string} [detail="A database conflict occurred."] - The error details.
|
|
127
|
+
* @param {number} [status=409] - The HTTP status code.
|
|
128
|
+
*/
|
|
129
|
+
constructor(detail = "A database conflict occurred.", status = 409) {
|
|
130
|
+
super("Conflict", "conflict", detail, status);
|
|
131
|
+
this.name = "ConflictError";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
119
134
|
/**
|
|
120
135
|
* Error class for AST validation errors.
|
|
121
136
|
*/
|
|
@@ -169,6 +184,8 @@ export function parseStateZeroError(errorResponse) {
|
|
|
169
184
|
return new MultipleObjectsReturned(detail, status);
|
|
170
185
|
case "PermissionDenied":
|
|
171
186
|
return new PermissionDenied(detail, status);
|
|
187
|
+
case "ConflictError":
|
|
188
|
+
return new ConflictError(detail, status);
|
|
172
189
|
case "ASTValidationError":
|
|
173
190
|
return new ASTValidationError(detail, status);
|
|
174
191
|
case "ConfigError":
|
|
@@ -189,6 +206,9 @@ export function parseStateZeroError(errorResponse) {
|
|
|
189
206
|
else if (status === 404) {
|
|
190
207
|
return new DoesNotExist(detail, status);
|
|
191
208
|
}
|
|
209
|
+
else if (status === 409) {
|
|
210
|
+
return new ConflictError(detail, status);
|
|
211
|
+
}
|
|
192
212
|
return new StateZeroError("Unknown error", "unknown", detail, status);
|
|
193
213
|
}
|
|
194
214
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValidationError } from './errors.js';
|
|
2
1
|
import * as math from 'mathjs';
|
|
3
2
|
// Create a math instance
|
|
4
3
|
const mathInstance = math.create();
|
|
@@ -8,7 +7,7 @@ const allowedFunctions = ['abs', 'round', 'floor', 'ceil', 'min', 'max'];
|
|
|
8
7
|
const ALLOWED_OPERATORS = ['+', '-', '*', '/', '%', '^'];
|
|
9
8
|
export function F(expression) {
|
|
10
9
|
if (typeof expression !== 'string' || !expression) {
|
|
11
|
-
throw new
|
|
10
|
+
throw new Error('F expression requires a non-empty string');
|
|
12
11
|
}
|
|
13
12
|
try {
|
|
14
13
|
const node = math.parse(expression);
|
|
@@ -20,7 +19,9 @@ export function F(expression) {
|
|
|
20
19
|
};
|
|
21
20
|
}
|
|
22
21
|
catch (err) {
|
|
23
|
-
|
|
22
|
+
if (err instanceof Error)
|
|
23
|
+
throw err;
|
|
24
|
+
throw new Error(`Invalid F expression: ${err}`);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
function validateExpression(node) {
|
|
@@ -28,19 +29,19 @@ function validateExpression(node) {
|
|
|
28
29
|
return;
|
|
29
30
|
if (node.type === 'OperatorNode') {
|
|
30
31
|
if (!ALLOWED_OPERATORS.includes(node.op)) {
|
|
31
|
-
throw new
|
|
32
|
+
throw new Error(`Unsupported operator: ${node.op}`);
|
|
32
33
|
}
|
|
33
34
|
node.args.forEach(validateExpression);
|
|
34
35
|
}
|
|
35
36
|
else if (node.type === 'FunctionNode') {
|
|
36
37
|
if (!allowedFunctions.includes(node.name)) {
|
|
37
|
-
throw new
|
|
38
|
+
throw new Error(`Function not allowed: ${node.name}`);
|
|
38
39
|
}
|
|
39
40
|
node.args.forEach(validateExpression);
|
|
40
41
|
}
|
|
41
42
|
else if (node.type === 'SymbolNode') {
|
|
42
43
|
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(node.name)) {
|
|
43
|
-
throw new
|
|
44
|
+
throw new Error(`Invalid field name: ${node.name}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
else if (node.type === 'ConstantNode') {
|
|
@@ -50,31 +51,31 @@ function validateExpression(node) {
|
|
|
50
51
|
validateExpression(node.content);
|
|
51
52
|
}
|
|
52
53
|
else if (node.type === 'ConditionalNode') {
|
|
53
|
-
throw new
|
|
54
|
+
throw new Error('Conditional expressions are not supported in F expressions');
|
|
54
55
|
}
|
|
55
56
|
else if (node.type === 'AssignmentNode') {
|
|
56
|
-
throw new
|
|
57
|
+
throw new Error('Assignment expressions are not supported in F expressions');
|
|
57
58
|
}
|
|
58
59
|
else if (node.type === 'BlockNode') {
|
|
59
|
-
throw new
|
|
60
|
+
throw new Error('Block expressions are not supported in F expressions');
|
|
60
61
|
}
|
|
61
62
|
else if (node.type === 'AccessorNode') {
|
|
62
|
-
throw new
|
|
63
|
+
throw new Error('Accessor expressions are not supported in F expressions');
|
|
63
64
|
}
|
|
64
65
|
else if (node.type === 'IndexNode') {
|
|
65
|
-
throw new
|
|
66
|
+
throw new Error('Index expressions are not supported in F expressions');
|
|
66
67
|
}
|
|
67
68
|
else if (node.type === 'RangeNode') {
|
|
68
|
-
throw new
|
|
69
|
+
throw new Error('Range expressions are not supported in F expressions');
|
|
69
70
|
}
|
|
70
71
|
else if (node.type === 'ArrayNode') {
|
|
71
|
-
throw new
|
|
72
|
+
throw new Error('Array expressions are not supported in F expressions');
|
|
72
73
|
}
|
|
73
74
|
else if (node.type === 'ObjectNode') {
|
|
74
|
-
throw new
|
|
75
|
+
throw new Error('Object expressions are not supported in F expressions');
|
|
75
76
|
}
|
|
76
77
|
else {
|
|
77
|
-
throw new
|
|
78
|
+
throw new Error(`Unsupported node type: ${node.type}`);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
export function evaluateExpression(expr, data) {
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { ValidationError } from "./flavours/django/errors.js";
|
|
|
18
18
|
import { DoesNotExist } from "./flavours/django/errors.js";
|
|
19
19
|
import { PermissionDenied } from "./flavours/django/errors.js";
|
|
20
20
|
import { MultipleObjectsReturned } from "./flavours/django/errors.js";
|
|
21
|
+
import { ConflictError } from "./flavours/django/errors.js";
|
|
21
22
|
import { ASTValidationError } from "./flavours/django/errors.js";
|
|
22
23
|
import { ConfigError } from "./flavours/django/errors.js";
|
|
23
24
|
import { parseStateZeroError } from "./flavours/django/errors.js";
|
|
@@ -43,4 +44,4 @@ import { setupTestStateZero } from "./testing.js";
|
|
|
43
44
|
import { createActionMocker } from "./testing.js";
|
|
44
45
|
import { seedRemote } from "./testing.js";
|
|
45
46
|
import { resetRemote } from "./testing.js";
|
|
46
|
-
export { EventType, PusherEventReceiver, setEventReceiver, getEventReceiver, setNamespaceResolver, setupStateZero, resetStateZero, FileObject, querysetStoreRegistry, modelStoreRegistry, metricRegistry, syncManager, Operation, operationRegistry, Q, StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ASTValidationError, ConfigError, parseStateZeroError, QuerySet, Manager, ResultTuple, Model, setConfig, getConfig, setBackendConfig, initializeEventReceiver, configInstance, getModelClass, initEventHandler, cleanupEventHandler, setAdapters, wrapReactiveModel, wrapReactiveQuerySet, serializeActionPayload, onStateZeroError, createTestConfig, setupTestStateZero, createActionMocker, seedRemote, resetRemote };
|
|
47
|
+
export { EventType, PusherEventReceiver, setEventReceiver, getEventReceiver, setNamespaceResolver, setupStateZero, resetStateZero, FileObject, querysetStoreRegistry, modelStoreRegistry, metricRegistry, syncManager, Operation, operationRegistry, Q, StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ConflictError, ASTValidationError, ConfigError, parseStateZeroError, QuerySet, Manager, ResultTuple, Model, setConfig, getConfig, setBackendConfig, initializeEventReceiver, configInstance, getModelClass, initEventHandler, cleanupEventHandler, setAdapters, wrapReactiveModel, wrapReactiveQuerySet, serializeActionPayload, onStateZeroError, createTestConfig, setupTestStateZero, createActionMocker, seedRemote, resetRemote };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { EventType, PusherEventReceiver, setEventReceiver, getEventReceiver, set
|
|
|
3
3
|
import { setupStateZero } from "./setup.js";
|
|
4
4
|
// Django flavor modules
|
|
5
5
|
import { Q } from "./flavours/django/q.js";
|
|
6
|
-
import { StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ASTValidationError, ConfigError, parseStateZeroError, } from "./flavours/django/errors.js";
|
|
6
|
+
import { StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ConflictError, ASTValidationError, ConfigError, parseStateZeroError, } from "./flavours/django/errors.js";
|
|
7
7
|
import { querysetStoreRegistry } from "./syncEngine/registries/querysetStoreRegistry.js";
|
|
8
8
|
import { modelStoreRegistry } from "./syncEngine/registries/modelStoreRegistry.js";
|
|
9
9
|
import { metricRegistry } from "./syncEngine/registries/metricRegistry.js";
|
|
@@ -35,7 +35,7 @@ querysetStoreRegistry, modelStoreRegistry, metricRegistry, syncManager,
|
|
|
35
35
|
// Operations
|
|
36
36
|
Operation, operationRegistry,
|
|
37
37
|
// Django flavor modules
|
|
38
|
-
Q, StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ASTValidationError, ConfigError, parseStateZeroError, QuerySet, Manager, ResultTuple, Model,
|
|
38
|
+
Q, StateZeroError, ValidationError, DoesNotExist, PermissionDenied, MultipleObjectsReturned, ConflictError, ASTValidationError, ConfigError, parseStateZeroError, QuerySet, Manager, ResultTuple, Model,
|
|
39
39
|
// Configuration
|
|
40
40
|
setConfig, getConfig, setBackendConfig, initializeEventReceiver, configInstance, getModelClass,
|
|
41
41
|
// Reactivity framework integration
|