@statezero/core 0.2.37 → 0.2.38
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/LayoutRenderer.js +166 -0
- package/dist/adaptors/vue/components/defaults/AlertElement.js +31 -0
- package/dist/adaptors/vue/components/defaults/DisplayElement.js +44 -0
- package/dist/adaptors/vue/components/defaults/DividerElement.js +10 -0
- package/dist/adaptors/vue/components/defaults/ErrorBlock.js +24 -0
- package/dist/adaptors/vue/components/defaults/GroupElement.js +41 -0
- package/dist/adaptors/vue/components/defaults/LabelElement.js +21 -0
- package/dist/adaptors/vue/components/defaults/TabsElement.js +38 -0
- package/package.json +6 -4
- package/dist/actions/backend1/django_app/calculate-hash.d.ts +0 -57
- package/dist/actions/backend1/django_app/calculate-hash.js +0 -80
- package/dist/actions/backend1/django_app/calculate-hash.schema.json +0 -148
- package/dist/actions/backend1/django_app/get-current-username.d.ts +0 -29
- package/dist/actions/backend1/django_app/get-current-username.js +0 -65
- package/dist/actions/backend1/django_app/get-current-username.schema.json +0 -47
- package/dist/actions/backend1/django_app/get-server-status.d.ts +0 -38
- package/dist/actions/backend1/django_app/get-server-status.js +0 -68
- package/dist/actions/backend1/django_app/get-server-status.schema.json +0 -93
- package/dist/actions/backend1/django_app/get-user-info.d.ts +0 -44
- package/dist/actions/backend1/django_app/get-user-info.js +0 -70
- package/dist/actions/backend1/django_app/get-user-info.schema.json +0 -127
- package/dist/actions/backend1/django_app/index.d.ts +0 -1
- package/dist/actions/backend1/django_app/index.js +0 -6
- package/dist/actions/backend1/django_app/process-data.d.ts +0 -51
- package/dist/actions/backend1/django_app/process-data.js +0 -78
- package/dist/actions/backend1/django_app/process-data.schema.json +0 -117
- package/dist/actions/backend1/django_app/send-notification.d.ts +0 -55
- package/dist/actions/backend1/django_app/send-notification.js +0 -81
- package/dist/actions/backend1/django_app/send-notification.schema.json +0 -175
- package/dist/actions/backend1/index.d.ts +0 -1
- package/dist/actions/backend1/index.js +0 -1
- package/dist/actions/default/django_app/calculate-hash.d.ts +0 -57
- package/dist/actions/default/django_app/calculate-hash.js +0 -80
- package/dist/actions/default/django_app/calculate-hash.schema.json +0 -148
- package/dist/actions/default/django_app/get-current-username.d.ts +0 -29
- package/dist/actions/default/django_app/get-current-username.js +0 -65
- package/dist/actions/default/django_app/get-current-username.schema.json +0 -47
- package/dist/actions/default/django_app/get-server-status.d.ts +0 -38
- package/dist/actions/default/django_app/get-server-status.js +0 -68
- package/dist/actions/default/django_app/get-server-status.schema.json +0 -93
- package/dist/actions/default/django_app/get-user-info.d.ts +0 -44
- package/dist/actions/default/django_app/get-user-info.js +0 -70
- package/dist/actions/default/django_app/get-user-info.schema.json +0 -127
- package/dist/actions/default/django_app/index.d.ts +0 -1
- package/dist/actions/default/django_app/index.js +0 -6
- package/dist/actions/default/django_app/process-data.d.ts +0 -51
- package/dist/actions/default/django_app/process-data.js +0 -78
- package/dist/actions/default/django_app/process-data.schema.json +0 -117
- package/dist/actions/default/django_app/send-notification.d.ts +0 -55
- package/dist/actions/default/django_app/send-notification.js +0 -81
- package/dist/actions/default/django_app/send-notification.schema.json +0 -175
- package/dist/actions/default/index.d.ts +0 -1
- package/dist/actions/default/index.js +0 -1
- package/dist/actions/index.d.ts +0 -1
- package/dist/actions/index.js +0 -5
- package/dist/adaptors/react/composables.d.ts +0 -1
- package/dist/adaptors/react/composables.js +0 -4
- package/dist/adaptors/react/index.d.ts +0 -1
- package/dist/adaptors/react/index.js +0 -1
- package/dist/adaptors/vue/components/LayoutRenderer.vue +0 -361
- package/dist/adaptors/vue/components/defaults/AlertElement.vue +0 -38
- package/dist/adaptors/vue/components/defaults/DisplayElement.vue +0 -57
- package/dist/adaptors/vue/components/defaults/DividerElement.vue +0 -13
- package/dist/adaptors/vue/components/defaults/ErrorBlock.vue +0 -28
- package/dist/adaptors/vue/components/defaults/GroupElement.vue +0 -53
- package/dist/adaptors/vue/components/defaults/LabelElement.vue +0 -25
- package/dist/adaptors/vue/components/defaults/TabsElement.vue +0 -54
- package/dist/adaptors/vue/components/defaults/index.d.ts +0 -7
- package/dist/adaptors/vue/components/defaults/index.js +0 -31
- package/dist/adaptors/vue/components/index.d.ts +0 -1
- package/dist/adaptors/vue/components/index.js +0 -7
- package/dist/adaptors/vue/composables.d.ts +0 -2
- package/dist/adaptors/vue/composables.js +0 -44
- package/dist/adaptors/vue/index.d.ts +0 -3
- package/dist/adaptors/vue/index.js +0 -4
- package/dist/adaptors/vue/reactivity.d.ts +0 -18
- package/dist/adaptors/vue/reactivity.js +0 -132
- package/dist/cli/commands/sync.d.ts +0 -6
- package/dist/cli/commands/sync.js +0 -30
- package/dist/cli/commands/syncActions.d.ts +0 -46
- package/dist/cli/commands/syncActions.js +0 -717
- package/dist/cli/commands/syncModels.d.ts +0 -132
- package/dist/cli/commands/syncModels.js +0 -1120
- package/dist/cli/configFileLoader.d.ts +0 -10
- package/dist/cli/configFileLoader.js +0 -85
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -22
- package/dist/config.d.ts +0 -57
- package/dist/config.js +0 -273
- package/dist/core/eventReceivers.d.ts +0 -185
- package/dist/core/eventReceivers.js +0 -266
- package/dist/core/utils.d.ts +0 -8
- package/dist/core/utils.js +0 -62
- package/dist/errorHandler.d.ts +0 -21
- package/dist/errorHandler.js +0 -27
- package/dist/filtering/localFiltering.d.ts +0 -110
- package/dist/filtering/localFiltering.js +0 -1080
- package/dist/flavours/django/dates.d.ts +0 -34
- package/dist/flavours/django/dates.js +0 -113
- package/dist/flavours/django/errors.d.ts +0 -138
- package/dist/flavours/django/errors.js +0 -195
- package/dist/flavours/django/f.d.ts +0 -6
- package/dist/flavours/django/f.js +0 -91
- package/dist/flavours/django/files.d.ts +0 -62
- package/dist/flavours/django/files.js +0 -355
- package/dist/flavours/django/makeApiCall.d.ts +0 -36
- package/dist/flavours/django/makeApiCall.js +0 -169
- package/dist/flavours/django/manager.d.ts +0 -204
- package/dist/flavours/django/manager.js +0 -222
- package/dist/flavours/django/model.d.ts +0 -137
- package/dist/flavours/django/model.js +0 -366
- package/dist/flavours/django/operationFactory.d.ts +0 -73
- package/dist/flavours/django/operationFactory.js +0 -248
- package/dist/flavours/django/q.d.ts +0 -70
- package/dist/flavours/django/q.js +0 -43
- package/dist/flavours/django/queryExecutor.d.ts +0 -149
- package/dist/flavours/django/queryExecutor.js +0 -590
- package/dist/flavours/django/querySet.d.ts +0 -301
- package/dist/flavours/django/querySet.js +0 -736
- package/dist/flavours/django/serializers.d.ts +0 -39
- package/dist/flavours/django/serializers.js +0 -296
- package/dist/flavours/django/tempPk.d.ts +0 -31
- package/dist/flavours/django/tempPk.js +0 -92
- package/dist/flavours/django/utils.d.ts +0 -19
- package/dist/flavours/django/utils.js +0 -29
- package/dist/index.d.ts +0 -46
- package/dist/index.js +0 -48
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +0 -894
- package/dist/models/backend1/django_app/comprehensivemodel.js +0 -71
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +0 -870
- package/dist/models/backend1/django_app/custompkmodel.d.ts +0 -92
- package/dist/models/backend1/django_app/custompkmodel.js +0 -69
- package/dist/models/backend1/django_app/custompkmodel.schema.json +0 -71
- package/dist/models/backend1/django_app/dailyrate.d.ts +0 -230
- package/dist/models/backend1/django_app/dailyrate.js +0 -71
- package/dist/models/backend1/django_app/dailyrate.schema.json +0 -212
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +0 -140
- package/dist/models/backend1/django_app/deepmodellevel1.js +0 -72
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +0 -114
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +0 -118
- package/dist/models/backend1/django_app/deepmodellevel2.js +0 -71
- package/dist/models/backend1/django_app/deepmodellevel2.schema.json +0 -92
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +0 -92
- package/dist/models/backend1/django_app/deepmodellevel3.js +0 -69
- package/dist/models/backend1/django_app/deepmodellevel3.schema.json +0 -69
- package/dist/models/backend1/django_app/dummymodel.d.ts +0 -134
- package/dist/models/backend1/django_app/dummymodel.js +0 -71
- package/dist/models/backend1/django_app/dummymodel.schema.json +0 -109
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +0 -92
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +0 -69
- package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +0 -69
- package/dist/models/backend1/django_app/filetest.d.ts +0 -140
- package/dist/models/backend1/django_app/filetest.js +0 -69
- package/dist/models/backend1/django_app/filetest.schema.json +0 -111
- package/dist/models/backend1/django_app/index.d.ts +0 -1
- package/dist/models/backend1/django_app/index.js +0 -21
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +0 -118
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +0 -71
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +0 -94
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +0 -118
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +0 -71
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +0 -94
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +0 -134
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +0 -71
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +0 -112
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +0 -118
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +0 -71
- package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +0 -93
- package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +0 -134
- package/dist/models/backend1/django_app/modelwithrestrictedfields.js +0 -71
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +0 -111
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +0 -92
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +0 -69
- package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +0 -71
- package/dist/models/backend1/django_app/order.d.ts +0 -220
- package/dist/models/backend1/django_app/order.js +0 -71
- package/dist/models/backend1/django_app/order.schema.json +0 -203
- package/dist/models/backend1/django_app/orderitem.d.ts +0 -172
- package/dist/models/backend1/django_app/orderitem.js +0 -72
- package/dist/models/backend1/django_app/orderitem.schema.json +0 -149
- package/dist/models/backend1/django_app/product.d.ts +0 -254
- package/dist/models/backend1/django_app/product.js +0 -71
- package/dist/models/backend1/django_app/product.schema.json +0 -277
- package/dist/models/backend1/django_app/productcategory.d.ts +0 -92
- package/dist/models/backend1/django_app/productcategory.js +0 -69
- package/dist/models/backend1/django_app/productcategory.schema.json +0 -70
- package/dist/models/backend1/django_app/rateplan.d.ts +0 -92
- package/dist/models/backend1/django_app/rateplan.js +0 -69
- package/dist/models/backend1/django_app/rateplan.schema.json +0 -70
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +0 -108
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +0 -69
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +0 -87
- package/dist/models/backend1/fileobject.d.ts +0 -4
- package/dist/models/backend1/fileobject.js +0 -9
- package/dist/models/backend1/index.d.ts +0 -2
- package/dist/models/backend1/index.js +0 -2
- package/dist/models/default/django_app/comprehensivemodel.d.ts +0 -894
- package/dist/models/default/django_app/comprehensivemodel.js +0 -71
- package/dist/models/default/django_app/comprehensivemodel.schema.json +0 -870
- package/dist/models/default/django_app/custompkmodel.d.ts +0 -92
- package/dist/models/default/django_app/custompkmodel.js +0 -69
- package/dist/models/default/django_app/custompkmodel.schema.json +0 -71
- package/dist/models/default/django_app/dailyrate.d.ts +0 -230
- package/dist/models/default/django_app/dailyrate.js +0 -71
- package/dist/models/default/django_app/dailyrate.schema.json +0 -212
- package/dist/models/default/django_app/deepmodellevel1.d.ts +0 -128
- package/dist/models/default/django_app/deepmodellevel1.js +0 -72
- package/dist/models/default/django_app/deepmodellevel1.schema.json +0 -102
- package/dist/models/default/django_app/deepmodellevel2.d.ts +0 -106
- package/dist/models/default/django_app/deepmodellevel2.js +0 -71
- package/dist/models/default/django_app/deepmodellevel2.schema.json +0 -80
- package/dist/models/default/django_app/deepmodellevel3.d.ts +0 -80
- package/dist/models/default/django_app/deepmodellevel3.js +0 -69
- package/dist/models/default/django_app/deepmodellevel3.schema.json +0 -57
- package/dist/models/default/django_app/dummymodel.d.ts +0 -122
- package/dist/models/default/django_app/dummymodel.js +0 -71
- package/dist/models/default/django_app/dummymodel.schema.json +0 -97
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +0 -80
- package/dist/models/default/django_app/dummyrelatedmodel.js +0 -69
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +0 -57
- package/dist/models/default/django_app/filetest.d.ts +0 -128
- package/dist/models/default/django_app/filetest.js +0 -69
- package/dist/models/default/django_app/filetest.schema.json +0 -99
- package/dist/models/default/django_app/index.d.ts +0 -1
- package/dist/models/default/django_app/index.js +0 -21
- package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +0 -118
- package/dist/models/default/django_app/m2mdepthtestlevel1.js +0 -71
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +0 -94
- package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +0 -118
- package/dist/models/default/django_app/m2mdepthtestlevel2.js +0 -71
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +0 -94
- package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +0 -134
- package/dist/models/default/django_app/m2mdepthtestlevel3.js +0 -71
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +0 -112
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +0 -118
- package/dist/models/default/django_app/modelwithcustompkrelation.js +0 -71
- package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +0 -93
- package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +0 -134
- package/dist/models/default/django_app/modelwithrestrictedfields.js +0 -71
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +0 -111
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +0 -92
- package/dist/models/default/django_app/namefiltercustompkmodel.js +0 -69
- package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +0 -71
- package/dist/models/default/django_app/order.d.ts +0 -220
- package/dist/models/default/django_app/order.js +0 -71
- package/dist/models/default/django_app/order.schema.json +0 -203
- package/dist/models/default/django_app/orderitem.d.ts +0 -172
- package/dist/models/default/django_app/orderitem.js +0 -72
- package/dist/models/default/django_app/orderitem.schema.json +0 -149
- package/dist/models/default/django_app/product.d.ts +0 -254
- package/dist/models/default/django_app/product.js +0 -71
- package/dist/models/default/django_app/product.schema.json +0 -277
- package/dist/models/default/django_app/productcategory.d.ts +0 -92
- package/dist/models/default/django_app/productcategory.js +0 -69
- package/dist/models/default/django_app/productcategory.schema.json +0 -70
- package/dist/models/default/django_app/rateplan.d.ts +0 -92
- package/dist/models/default/django_app/rateplan.js +0 -69
- package/dist/models/default/django_app/rateplan.schema.json +0 -70
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +0 -108
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +0 -69
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +0 -87
- package/dist/models/default/fileobject.d.ts +0 -4
- package/dist/models/default/fileobject.js +0 -9
- package/dist/models/default/index.d.ts +0 -2
- package/dist/models/default/index.js +0 -2
- package/dist/models/index.d.ts +0 -1
- package/dist/models/index.js +0 -5
- package/dist/react-entry.d.ts +0 -2
- package/dist/react-entry.js +0 -2
- package/dist/reactiveAdaptor.d.ts +0 -24
- package/dist/reactiveAdaptor.js +0 -38
- package/dist/reset.d.ts +0 -15
- package/dist/reset.js +0 -97
- package/dist/setup.d.ts +0 -15
- package/dist/setup.js +0 -33
- package/dist/syncEngine/cache/cache.d.ts +0 -75
- package/dist/syncEngine/cache/cache.js +0 -355
- package/dist/syncEngine/metrics/metricOptCalcs.d.ts +0 -79
- package/dist/syncEngine/metrics/metricOptCalcs.js +0 -284
- package/dist/syncEngine/registries/metricRegistry.d.ts +0 -58
- package/dist/syncEngine/registries/metricRegistry.js +0 -171
- package/dist/syncEngine/registries/modelStoreRegistry.d.ts +0 -11
- package/dist/syncEngine/registries/modelStoreRegistry.js +0 -63
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +0 -41
- package/dist/syncEngine/registries/querysetStoreGraph.js +0 -174
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +0 -72
- package/dist/syncEngine/registries/querysetStoreRegistry.js +0 -335
- package/dist/syncEngine/stores/metricStore.d.ts +0 -55
- package/dist/syncEngine/stores/metricStore.js +0 -222
- package/dist/syncEngine/stores/modelStore.d.ts +0 -53
- package/dist/syncEngine/stores/modelStore.js +0 -565
- package/dist/syncEngine/stores/operation.d.ts +0 -139
- package/dist/syncEngine/stores/operation.js +0 -291
- package/dist/syncEngine/stores/operationEventHandlers.d.ts +0 -8
- package/dist/syncEngine/stores/operationEventHandlers.js +0 -322
- package/dist/syncEngine/stores/querysetStore.d.ts +0 -60
- package/dist/syncEngine/stores/querysetStore.js +0 -294
- package/dist/syncEngine/stores/reactivity.d.ts +0 -3
- package/dist/syncEngine/stores/reactivity.js +0 -4
- package/dist/syncEngine/stores/utils.d.ts +0 -14
- package/dist/syncEngine/stores/utils.js +0 -32
- package/dist/syncEngine/sync.d.ts +0 -46
- package/dist/syncEngine/sync.js +0 -389
- package/dist/testing.d.ts +0 -63
- package/dist/testing.js +0 -175
- package/dist/vue-entry.d.ts +0 -15
- package/dist/vue-entry.js +0 -7
|
@@ -1,1120 +0,0 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
import * as fs from "fs/promises";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import cliProgress from "cli-progress";
|
|
5
|
-
import Handlebars from "handlebars";
|
|
6
|
-
import _ from "lodash-es";
|
|
7
|
-
import { configInstance } from "../../config.js"; // Global config singleton
|
|
8
|
-
import { loadConfigFromFile } from "../configFileLoader.js";
|
|
9
|
-
// --------------------
|
|
10
|
-
// JSDoc Type Definitions
|
|
11
|
-
// --------------------
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} GenerateArgs
|
|
14
|
-
* // Additional arguments for generation if needed.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {Object} SchemaProperty
|
|
18
|
-
* @property {string} type
|
|
19
|
-
* @property {string} [format]
|
|
20
|
-
* @property {SchemaProperty} [items]
|
|
21
|
-
* @property {Object.<string, SchemaProperty>} [properties]
|
|
22
|
-
* @property {string[]} [required]
|
|
23
|
-
* @property {string[]} [enum]
|
|
24
|
-
* @property {string} [description]
|
|
25
|
-
* @property {boolean} [nullable]
|
|
26
|
-
* @property {any} [default]
|
|
27
|
-
* @property {string} [ref]
|
|
28
|
-
*/
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {Object} RelationshipField
|
|
31
|
-
* @property {string} field - Name of the relationship field
|
|
32
|
-
* @property {string} ModelClass - The class name of the related model
|
|
33
|
-
* @property {string} relationshipType - Type of relationship (e.g., "many-to-many", "foreign-key")
|
|
34
|
-
*/
|
|
35
|
-
/**
|
|
36
|
-
* @typedef {Object} RelationshipData
|
|
37
|
-
* @property {string} type
|
|
38
|
-
* @property {string} model - e.g. "django_app.deepmodellevel1"
|
|
39
|
-
* @property {string} class_name - e.g. "DeepModelLevel1"
|
|
40
|
-
* @property {string} primary_key_field
|
|
41
|
-
*/
|
|
42
|
-
/**
|
|
43
|
-
* @typedef {Object} SchemaDefinition
|
|
44
|
-
* @property {string} type
|
|
45
|
-
* @property {Object.<string, SchemaProperty>} properties
|
|
46
|
-
* @property {string[]} [required]
|
|
47
|
-
* @property {string} [description]
|
|
48
|
-
* @property {Object.<string, SchemaDefinition>} [definitions]
|
|
49
|
-
* @property {string} model_name
|
|
50
|
-
* @property {string} class_name
|
|
51
|
-
* @property {string} [primary_key_field]
|
|
52
|
-
* @property {Object.<string, RelationshipData>} [relationships]
|
|
53
|
-
*/
|
|
54
|
-
/**
|
|
55
|
-
* @typedef {Object} PropertyDefinition
|
|
56
|
-
* @property {string} name
|
|
57
|
-
* @property {string} type
|
|
58
|
-
* @property {boolean} required
|
|
59
|
-
* @property {string} defaultValue
|
|
60
|
-
* @property {boolean} [isRelationship]
|
|
61
|
-
* @property {string} [relationshipClassName]
|
|
62
|
-
* @property {boolean} [isArrayRelationship]
|
|
63
|
-
* @property {string} [relationshipPrimaryKeyField]
|
|
64
|
-
* @property {boolean} [isString]
|
|
65
|
-
* @property {boolean} [isNumber]
|
|
66
|
-
* @property {boolean} [isBoolean]
|
|
67
|
-
* @property {boolean} [isDate]
|
|
68
|
-
* @property {boolean} [isPrimaryKey]
|
|
69
|
-
*/
|
|
70
|
-
/**
|
|
71
|
-
* @typedef {Object} TemplateData
|
|
72
|
-
* @property {string} modulePath - Dynamic module path for imports.
|
|
73
|
-
* @property {string} className - Exported full model class name (from schema.class_name).
|
|
74
|
-
* @property {string} interfaceName - Full model fields interface name (e.g. DeepModelLevel1Fields).
|
|
75
|
-
* @property {string} modelName - Raw schema.model_name (including app label path).
|
|
76
|
-
* @property {PropertyDefinition[]} properties
|
|
77
|
-
* @property {RelationshipField[]} relationshipFields - List of relationship fields for the model
|
|
78
|
-
* @property {string} [description]
|
|
79
|
-
* @property {string[]} [definitions]
|
|
80
|
-
* @property {string[]} [jsImports] - For JS generation: full class imports.
|
|
81
|
-
* @property {string[]} [tsImports] - For TS generation: type imports (Fields).
|
|
82
|
-
* @property {string} configKey - The backend config key.
|
|
83
|
-
* @property {string} primaryKeyField - Primary key field from schema.
|
|
84
|
-
*/
|
|
85
|
-
/**
|
|
86
|
-
* @typedef {Object} BackendConfig
|
|
87
|
-
* @property {string} NAME
|
|
88
|
-
* @property {string} API_URL
|
|
89
|
-
* @property {string} GENERATED_TYPES_DIR
|
|
90
|
-
*/
|
|
91
|
-
/**
|
|
92
|
-
* @typedef {Object} SelectedModel
|
|
93
|
-
* @property {BackendConfig} backend
|
|
94
|
-
* @property {string} model
|
|
95
|
-
*/
|
|
96
|
-
// --------------------
|
|
97
|
-
// Fallback Selection for Inquirer Errors
|
|
98
|
-
// --------------------
|
|
99
|
-
/**
|
|
100
|
-
* Simple fallback that generates all models when inquirer fails
|
|
101
|
-
* @param {Array} choices - Array of choice objects with {name, value, checked} properties
|
|
102
|
-
* @param {string} message - Selection message
|
|
103
|
-
* @returns {Promise<Array>} - All model values
|
|
104
|
-
*/
|
|
105
|
-
async function fallbackSelectAll(choices, message) {
|
|
106
|
-
console.log(`\n${message}`);
|
|
107
|
-
console.log("Interactive selection not available - generating ALL models:");
|
|
108
|
-
const allModels = [];
|
|
109
|
-
for (const choice of choices) {
|
|
110
|
-
// Skip separators (they don't have a 'value' property)
|
|
111
|
-
if (!choice.value) {
|
|
112
|
-
console.log(choice.name); // Print separator text
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
// Add ALL models, regardless of checked status
|
|
116
|
-
allModels.push(choice.value);
|
|
117
|
-
console.log(` ✓ ${choice.name}`);
|
|
118
|
-
}
|
|
119
|
-
console.log(`\nGenerating ALL ${allModels.length} models.`);
|
|
120
|
-
return allModels;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Model selection with inquirer fallback
|
|
124
|
-
* @param {Array} choices - Array of choice objects
|
|
125
|
-
* @param {string} message - Selection message
|
|
126
|
-
* @returns {Promise<Array>} - Selected model objects
|
|
127
|
-
*/
|
|
128
|
-
async function selectModels(choices, message) {
|
|
129
|
-
try {
|
|
130
|
-
// Try to use inquirer first
|
|
131
|
-
const inquirer = (await import("inquirer")).default;
|
|
132
|
-
const { selectedModels } = await inquirer.prompt([
|
|
133
|
-
{
|
|
134
|
-
type: "checkbox",
|
|
135
|
-
name: "selectedModels",
|
|
136
|
-
message,
|
|
137
|
-
choices,
|
|
138
|
-
pageSize: 20,
|
|
139
|
-
},
|
|
140
|
-
]);
|
|
141
|
-
return selectedModels;
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
// Fall back to generating all models if inquirer fails for any reason
|
|
145
|
-
console.warn("Interactive selection failed, generating all models:", error.message);
|
|
146
|
-
return await fallbackSelectAll(choices, message);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// --------------------
|
|
150
|
-
// Handlebars Templates & Helpers
|
|
151
|
-
// --------------------
|
|
152
|
-
// Updated JS_MODEL_TEMPLATE with getters and setters
|
|
153
|
-
const JS_MODEL_TEMPLATE = `/**
|
|
154
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
155
|
-
{{#if description}}
|
|
156
|
-
* {{description}}
|
|
157
|
-
{{/if}}
|
|
158
|
-
*/
|
|
159
|
-
|
|
160
|
-
import { Model, Manager, QuerySet, getModelClass } from '{{modulePath}}';
|
|
161
|
-
import { wrapReactiveModel } from '{{modulePath}}';
|
|
162
|
-
import schemaData from './{{toLowerCase className}}.schema.json';
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Model-specific QuerySet implementation
|
|
166
|
-
*/
|
|
167
|
-
export class {{className}}QuerySet extends QuerySet {
|
|
168
|
-
// QuerySet implementation with model-specific typing
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Model-specific Manager implementation
|
|
173
|
-
*/
|
|
174
|
-
export class {{className}}Manager extends Manager {
|
|
175
|
-
constructor(ModelClass) {
|
|
176
|
-
super(ModelClass, {{className}}QuerySet);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
newQuerySet() {
|
|
180
|
-
return new {{className}}QuerySet(this.ModelClass);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Implementation of the {{className}} model
|
|
186
|
-
*/
|
|
187
|
-
export class {{className}} extends Model {
|
|
188
|
-
// Bind this model to its backend
|
|
189
|
-
static configKey = '{{configKey}}';
|
|
190
|
-
static modelName = '{{modelName}}';
|
|
191
|
-
static primaryKeyField = '{{primaryKeyField}}';
|
|
192
|
-
static objects = new {{className}}Manager({{className}});
|
|
193
|
-
static fields = [{{#each properties}}'{{name}}'{{#unless @last}}, {{/unless}}{{/each}}];
|
|
194
|
-
static schema = schemaData;
|
|
195
|
-
static relationshipFields = new Map([
|
|
196
|
-
{{#each relationshipFields}}
|
|
197
|
-
['{{field}}', { 'ModelClass': () => getModelClass('{{modelName}}', '{{../configKey}}'), 'relationshipType': '{{relationshipType}}' }]{{#unless @last}},{{/unless}}
|
|
198
|
-
{{/each}}
|
|
199
|
-
]);
|
|
200
|
-
|
|
201
|
-
constructor(data) {
|
|
202
|
-
{{className}}.validateFields(data);
|
|
203
|
-
super(data);
|
|
204
|
-
|
|
205
|
-
// Define getters and setters for all fields
|
|
206
|
-
this._defineProperties();
|
|
207
|
-
|
|
208
|
-
return wrapReactiveModel(this);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Define property getters and setters for all model fields
|
|
213
|
-
* @private
|
|
214
|
-
*/
|
|
215
|
-
_defineProperties() {
|
|
216
|
-
// For each field, define a property that gets/sets from internal storage
|
|
217
|
-
{{className}}.fields.forEach(field => {
|
|
218
|
-
Object.defineProperty(this, field, {
|
|
219
|
-
get: function() {
|
|
220
|
-
return this.getField(field);
|
|
221
|
-
},
|
|
222
|
-
set: function(value) {
|
|
223
|
-
this.setField(field, value);
|
|
224
|
-
},
|
|
225
|
-
enumerable: true, // Make sure fields are enumerable for serialization
|
|
226
|
-
configurable: true
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// Add a special read-only getter for the repr field
|
|
231
|
-
Object.defineProperty(this, 'repr', {
|
|
232
|
-
get: function() {
|
|
233
|
-
return this.getField('repr');
|
|
234
|
-
},
|
|
235
|
-
enumerable: true, // Make sure repr is enumerable
|
|
236
|
-
configurable: true
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
`;
|
|
241
|
-
// Updated TS_DECLARATION_TEMPLATE with improved relationship handling
|
|
242
|
-
const TS_DECLARATION_TEMPLATE = `/**
|
|
243
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
244
|
-
{{#if description}}
|
|
245
|
-
* {{description}}
|
|
246
|
-
{{/if}}
|
|
247
|
-
*/
|
|
248
|
-
|
|
249
|
-
import { Model, Manager } from '{{modulePath}}';
|
|
250
|
-
import { StringOperators, NumberOperators, BooleanOperators, DateOperators } from '{{modulePath}}';
|
|
251
|
-
import { QuerySet, LiveQuerySet, LiveQuerySetOptions, MetricResult, ResultTuple, SerializerOptions, NestedPaths } from '{{modulePath}}';
|
|
252
|
-
|
|
253
|
-
// Re-export the real Manager for runtime use
|
|
254
|
-
import { Manager as RuntimeManager } from '{{modulePath}}';
|
|
255
|
-
{{#if tsImports}}
|
|
256
|
-
{{#each tsImports}}
|
|
257
|
-
{{{this}}}
|
|
258
|
-
{{/each}}
|
|
259
|
-
{{/if}}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Base fields interface - defines the shape of a model instance
|
|
263
|
-
* This is the single source of truth for the model's data structure
|
|
264
|
-
*/
|
|
265
|
-
export interface {{interfaceName}} {
|
|
266
|
-
{{#each properties}}
|
|
267
|
-
{{name}}{{#unless required}}?{{/unless}}: {{{type}}};
|
|
268
|
-
{{/each}}
|
|
269
|
-
// Read-only representation field
|
|
270
|
-
readonly repr: {
|
|
271
|
-
str: string;
|
|
272
|
-
img: string | null;
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Relationship field structure
|
|
278
|
-
*/
|
|
279
|
-
export interface RelationshipField {
|
|
280
|
-
ModelClass: any;
|
|
281
|
-
relationshipType: string;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Relationship fields map type
|
|
286
|
-
*/
|
|
287
|
-
export type RelationshipFieldsMap = Map<string, RelationshipField>;
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Type for creating new instances
|
|
291
|
-
* Similar to base fields but makes ID fields optional
|
|
292
|
-
*/
|
|
293
|
-
export type {{className}}CreateData = {
|
|
294
|
-
{{#each properties}}
|
|
295
|
-
{{name}}{{#unless isPrimaryKey}}{{#unless required}}?{{/unless}}{{else}}?{{/unless}}: {{{type}}};
|
|
296
|
-
{{/each}}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Type for updating instances
|
|
301
|
-
* All fields are optional since updates can be partial
|
|
302
|
-
*/
|
|
303
|
-
export type {{className}}UpdateData = Partial<{{interfaceName}}>;
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Type for filtering with field lookups
|
|
307
|
-
* Supports advanced filtering with operators like __gte, __contains, etc.
|
|
308
|
-
*/
|
|
309
|
-
export interface {{className}}FilterData {
|
|
310
|
-
{{#each properties}}
|
|
311
|
-
{{#if isRelationship}}
|
|
312
|
-
{{#if isArrayRelationship}}
|
|
313
|
-
// Many-to-many relationship field
|
|
314
|
-
{{name}}?: number; // Exact match by ID
|
|
315
|
-
{{name}}__in?: number[]; // Match any of these IDs
|
|
316
|
-
{{name}}__isnull?: boolean; // Check if relation exists
|
|
317
|
-
{{else}}
|
|
318
|
-
// Foreign key relationship field
|
|
319
|
-
{{name}}?: number; // Exact match by ID
|
|
320
|
-
{{name}}__isnull?: boolean; // Check if relation exists
|
|
321
|
-
{{/if}}
|
|
322
|
-
{{else}}
|
|
323
|
-
{{#if isString}}
|
|
324
|
-
{{name}}?: string | StringOperators;
|
|
325
|
-
{{name}}__contains?: string;
|
|
326
|
-
{{name}}__icontains?: string;
|
|
327
|
-
{{name}}__startswith?: string;
|
|
328
|
-
{{name}}__istartswith?: string;
|
|
329
|
-
{{name}}__endswith?: string;
|
|
330
|
-
{{name}}__iendswith?: string;
|
|
331
|
-
{{name}}__exact?: string;
|
|
332
|
-
{{name}}__iexact?: string;
|
|
333
|
-
{{name}}__in?: string[];
|
|
334
|
-
{{name}}__isnull?: boolean;
|
|
335
|
-
{{/if}}
|
|
336
|
-
{{#if isNumber}}
|
|
337
|
-
{{name}}?: number | NumberOperators;
|
|
338
|
-
{{name}}__gt?: number;
|
|
339
|
-
{{name}}__gte?: number;
|
|
340
|
-
{{name}}__lt?: number;
|
|
341
|
-
{{name}}__lte?: number;
|
|
342
|
-
{{name}}__exact?: number;
|
|
343
|
-
{{name}}__in?: number[];
|
|
344
|
-
{{name}}__isnull?: boolean;
|
|
345
|
-
{{/if}}
|
|
346
|
-
{{#if isBoolean}}
|
|
347
|
-
{{name}}?: boolean | BooleanOperators;
|
|
348
|
-
{{name}}__exact?: boolean;
|
|
349
|
-
{{name}}__isnull?: boolean;
|
|
350
|
-
{{/if}}
|
|
351
|
-
{{#if isDate}}
|
|
352
|
-
{{name}}?: Date | DateOperators;
|
|
353
|
-
{{name}}__gt?: Date;
|
|
354
|
-
{{name}}__gte?: Date;
|
|
355
|
-
{{name}}__lt?: Date;
|
|
356
|
-
{{name}}__lte?: Date;
|
|
357
|
-
{{name}}__exact?: Date;
|
|
358
|
-
{{name}}__in?: Date[];
|
|
359
|
-
{{name}}__isnull?: boolean;
|
|
360
|
-
{{/if}}
|
|
361
|
-
{{/if}}
|
|
362
|
-
{{/each}}
|
|
363
|
-
// Support for nested filtering on related fields
|
|
364
|
-
[key: string]: any;
|
|
365
|
-
|
|
366
|
-
// Support for Q objects
|
|
367
|
-
Q?: Array<any>;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Model-specific QuerySet with strictly typed methods
|
|
372
|
-
*/
|
|
373
|
-
export declare class {{className}}QuerySet extends QuerySet<any> {
|
|
374
|
-
// Chain methods
|
|
375
|
-
filter(conditions: {{className}}FilterData): {{className}}QuerySet;
|
|
376
|
-
exclude(conditions: {{className}}FilterData): {{className}}QuerySet;
|
|
377
|
-
orderBy(...fields: Array<keyof {{interfaceName}} | string>): {{className}}QuerySet;
|
|
378
|
-
search(searchQuery: string, searchFields?: Array<string>): {{className}}QuerySet;
|
|
379
|
-
|
|
380
|
-
// Terminal methods
|
|
381
|
-
get(filters?: {{className}}FilterData, serializerOptions?: SerializerOptions): Promise<{{className}}>;
|
|
382
|
-
first(serializerOptions?: SerializerOptions): Promise<{{className}} | null>;
|
|
383
|
-
last(serializerOptions?: SerializerOptions): Promise<{{className}} | null>;
|
|
384
|
-
all(): {{className}}QuerySet;
|
|
385
|
-
count(field?: string): Promise<number>;
|
|
386
|
-
update(updates: {{className}}UpdateData): Promise<[number, Record<string, number>]>;
|
|
387
|
-
delete(): Promise<[number, Record<string, number>]>;
|
|
388
|
-
exists(): Promise<boolean>;
|
|
389
|
-
fetch(serializerOptions?: SerializerOptions): Promise<{{className}}[]>;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Model-specific Manager with strictly typed methods
|
|
394
|
-
*/
|
|
395
|
-
export declare class {{className}}Manager extends Manager {
|
|
396
|
-
newQuerySet(): {{className}}QuerySet;
|
|
397
|
-
filter(conditions: {{className}}FilterData): {{className}}QuerySet;
|
|
398
|
-
exclude(conditions: {{className}}FilterData): {{className}}QuerySet;
|
|
399
|
-
all(): {{className}}QuerySet;
|
|
400
|
-
get(filters?: {{className}}FilterData, serializerOptions?: SerializerOptions): Promise<{{className}}>;
|
|
401
|
-
create(data: {{className}}CreateData): Promise<{{className}}>;
|
|
402
|
-
delete(): Promise<[number, Record<string, number>]>;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Model-specific LiveQuerySet with strictly typed methods
|
|
407
|
-
*/
|
|
408
|
-
export declare class {{className}}LiveQuerySet extends LiveQuerySet {
|
|
409
|
-
// Data access
|
|
410
|
-
get data(): {{className}}[];
|
|
411
|
-
|
|
412
|
-
// Chain methods
|
|
413
|
-
filter(conditions: {{className}}FilterData): {{className}}LiveQuerySet;
|
|
414
|
-
|
|
415
|
-
// Terminal methods
|
|
416
|
-
fetch(serializerOptions?: SerializerOptions): Promise<{{className}}[]>;
|
|
417
|
-
get(filters?: {{className}}FilterData, serializerOptions?: SerializerOptions): Promise<{{className}}>;
|
|
418
|
-
create(item: {{className}}CreateData): Promise<{{className}}>;
|
|
419
|
-
update(updates: {{className}}UpdateData): Promise<{{className}}[]>;
|
|
420
|
-
delete(): Promise<void>;
|
|
421
|
-
count(field?: string): Promise<MetricResult<number>>;
|
|
422
|
-
sum(field: string): Promise<MetricResult<number>>;
|
|
423
|
-
avg(field: string): Promise<MetricResult<number>>;
|
|
424
|
-
min(field: string): Promise<MetricResult<any>>;
|
|
425
|
-
max(field: string): Promise<MetricResult<any>>;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Enhanced RuntimeManager to provide TypeScript typings
|
|
430
|
-
* This creates a concrete class that both extends RuntimeManager and matches type expectations
|
|
431
|
-
*/
|
|
432
|
-
export class {{className}}Manager extends RuntimeManager {
|
|
433
|
-
filter(conditions: {{className}}FilterData): ReturnType<RuntimeManager['filter']> {
|
|
434
|
-
return super.filter(conditions as any);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
get(filters?: {{className}}FilterData, serializerOptions?: SerializerOptions): Promise<{{className}}> {
|
|
438
|
-
return super.get(filters as any, serializerOptions);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
all() {
|
|
442
|
-
return super.all();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
create(data: {{className}}CreateData): Promise<{{className}}> {
|
|
446
|
-
return super.create(data);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
update(data: {{className}}UpdateData): Promise<any> {
|
|
450
|
-
return super.update(data);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Class declarations
|
|
455
|
-
export declare class {{className}} extends Model implements {{interfaceName}} {
|
|
456
|
-
{{#each properties}}
|
|
457
|
-
{{name}}{{#unless required}}?:{{else}}:{{/unless}} {{{type}}};
|
|
458
|
-
{{/each}}
|
|
459
|
-
readonly repr: {
|
|
460
|
-
str: string;
|
|
461
|
-
img: string | null;
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
static configKey: string;
|
|
465
|
-
static modelName: string;
|
|
466
|
-
static primaryKeyField: string;
|
|
467
|
-
static relationshipFields: RelationshipFieldsMap;
|
|
468
|
-
|
|
469
|
-
// Use model-specific manager class instead of generic manager
|
|
470
|
-
static objects: {{className}}Manager;
|
|
471
|
-
|
|
472
|
-
constructor(data: Partial<{{interfaceName}}>);
|
|
473
|
-
serialize(): Partial<{{interfaceName}}>;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Runtime initialization
|
|
478
|
-
*/
|
|
479
|
-
{{className}}.objects = new {{className}}Manager({{className}});
|
|
480
|
-
`;
|
|
481
|
-
// --------------------
|
|
482
|
-
// Handlebars Helpers
|
|
483
|
-
// --------------------
|
|
484
|
-
Handlebars.registerHelper("ifDefaultProvided", function (defaultValue, options) {
|
|
485
|
-
if (defaultValue !== "null") {
|
|
486
|
-
return options.fn(this);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
return options.inverse(this);
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
Handlebars.registerHelper("isRequired", function (required) {
|
|
493
|
-
return required ? "" : "?";
|
|
494
|
-
});
|
|
495
|
-
Handlebars.registerHelper("toLowerCase", function (str) {
|
|
496
|
-
return str.toLowerCase();
|
|
497
|
-
});
|
|
498
|
-
const jsTemplate = Handlebars.compile(JS_MODEL_TEMPLATE);
|
|
499
|
-
const dtsTemplate = Handlebars.compile(TS_DECLARATION_TEMPLATE);
|
|
500
|
-
// --------------------
|
|
501
|
-
// Core Generation Functions
|
|
502
|
-
// --------------------
|
|
503
|
-
/**
|
|
504
|
-
* Generates the schema for a given model.
|
|
505
|
-
* @param {BackendConfig} backend
|
|
506
|
-
* @param {string} model
|
|
507
|
-
* @returns {Promise<{model: string, relativePath: string}>}
|
|
508
|
-
*/
|
|
509
|
-
async function generateSchemaForModel(backend, model) {
|
|
510
|
-
const schemaUrl = `${backend.API_URL}/${model}/get-schema/`;
|
|
511
|
-
const headers = backend.SYNC_TOKEN ? { "X-Sync-Token": backend.SYNC_TOKEN } : {};
|
|
512
|
-
const schemaResponse = await axios.get(schemaUrl, { headers });
|
|
513
|
-
/** @type {SchemaDefinition} */
|
|
514
|
-
let schema;
|
|
515
|
-
if (schemaResponse.data.components?.schemas?.[model]) {
|
|
516
|
-
schema = schemaResponse.data.components.schemas[model];
|
|
517
|
-
}
|
|
518
|
-
else if (schemaResponse.data.properties) {
|
|
519
|
-
schema = schemaResponse.data;
|
|
520
|
-
}
|
|
521
|
-
else {
|
|
522
|
-
console.error("Unexpected schema structure for model:", model);
|
|
523
|
-
throw new Error(`Invalid schema structure for model: ${model}`);
|
|
524
|
-
}
|
|
525
|
-
if (!schema.model_name) {
|
|
526
|
-
console.error(`Missing model_name attribute in schema for model: ${model}`);
|
|
527
|
-
process.exit(1);
|
|
528
|
-
}
|
|
529
|
-
const rawModelName = schema.model_name;
|
|
530
|
-
const className = schema.class_name;
|
|
531
|
-
const interfaceName = `${className}Fields`;
|
|
532
|
-
const parts = model.split(".");
|
|
533
|
-
const currentApp = parts.length > 1 ? parts[0] : "";
|
|
534
|
-
// With the backend folder added, we need an extra ../
|
|
535
|
-
const modulePath = process.env.NODE_ENV === "test" ? "../../../../src" : "@statezero/core";
|
|
536
|
-
const templateData = prepareTemplateData(modulePath, className, interfaceName, rawModelName, schema, currentApp, backend.NAME);
|
|
537
|
-
// Always include the backend key as the top-level folder
|
|
538
|
-
let outDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
|
|
539
|
-
if (parts.length > 1) {
|
|
540
|
-
outDir = path.join(outDir, ...parts.slice(0, -1).map((p) => p.toLowerCase()));
|
|
541
|
-
}
|
|
542
|
-
await fs.mkdir(outDir, { recursive: true });
|
|
543
|
-
const schemaFilePath = path.join(outDir, `${className.toLowerCase()}.schema.json`);
|
|
544
|
-
await fs.writeFile(schemaFilePath, JSON.stringify(schema, null, 2));
|
|
545
|
-
const jsContent = jsTemplate(templateData);
|
|
546
|
-
const baseName = parts[parts.length - 1].toLowerCase();
|
|
547
|
-
const jsFilePath = path.join(outDir, `${baseName}.js`);
|
|
548
|
-
await fs.writeFile(jsFilePath, jsContent);
|
|
549
|
-
const dtsContent = dtsTemplate(templateData);
|
|
550
|
-
const dtsFilePath = path.join(outDir, `${baseName}.d.ts`);
|
|
551
|
-
await fs.writeFile(dtsFilePath, dtsContent);
|
|
552
|
-
const relativePath = "./" +
|
|
553
|
-
path
|
|
554
|
-
.relative(backend.GENERATED_TYPES_DIR, jsFilePath)
|
|
555
|
-
.replace(/\\/g, "/")
|
|
556
|
-
.replace(/\.js$/, "");
|
|
557
|
-
return { model, relativePath, className };
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Given a related model string (e.g. "django_app.deepmodellevel1"),
|
|
561
|
-
* extract the app label and model name to construct an import path.
|
|
562
|
-
* @param {string} currentApp
|
|
563
|
-
* @param {string} relModel
|
|
564
|
-
* @returns {string}
|
|
565
|
-
*/
|
|
566
|
-
function getImportPath(currentApp, relModel) {
|
|
567
|
-
const parts = relModel.split(".");
|
|
568
|
-
const appLabel = parts[0];
|
|
569
|
-
const fileName = parts[parts.length - 1].toLowerCase();
|
|
570
|
-
return currentApp === appLabel
|
|
571
|
-
? `./${fileName}`
|
|
572
|
-
: `../${appLabel}/${fileName}`;
|
|
573
|
-
}
|
|
574
|
-
/**
|
|
575
|
-
* Prepares template data for Handlebars.
|
|
576
|
-
* @param {string} modulePath
|
|
577
|
-
* @param {string} className
|
|
578
|
-
* @param {string} interfaceName
|
|
579
|
-
* @param {string} rawModelName
|
|
580
|
-
* @param {SchemaDefinition} schema
|
|
581
|
-
* @param {string} currentApp
|
|
582
|
-
* @param {string} configKey
|
|
583
|
-
* @returns {TemplateData}
|
|
584
|
-
*/
|
|
585
|
-
function prepareTemplateData(modulePath, className, interfaceName, rawModelName, schema, currentApp, configKey) {
|
|
586
|
-
/** @type {PropertyDefinition[]} */
|
|
587
|
-
const properties = [];
|
|
588
|
-
/** @type {RelationshipField[]} */
|
|
589
|
-
const relationshipFields = [];
|
|
590
|
-
const usedDefs = new Set();
|
|
591
|
-
for (const [propName, prop] of Object.entries(schema.properties)) {
|
|
592
|
-
const propType = generateTypeForProperty(prop, schema.definitions, schema.relationships, propName);
|
|
593
|
-
const isRelationship = schema.relationships && schema.relationships[propName] !== undefined;
|
|
594
|
-
const isString = prop.type === "string";
|
|
595
|
-
const isNumber = prop.type === "integer" || prop.type === "number";
|
|
596
|
-
const isBoolean = prop.type === "boolean";
|
|
597
|
-
const isDate = prop.type === "string" && prop.format === "date-time";
|
|
598
|
-
const isPrimaryKey = schema.primary_key_field === propName;
|
|
599
|
-
const propDef = {
|
|
600
|
-
name: propName,
|
|
601
|
-
type: propType,
|
|
602
|
-
required: schema.required?.includes(propName) ?? false,
|
|
603
|
-
defaultValue: getDefaultValueForType(prop),
|
|
604
|
-
isRelationship,
|
|
605
|
-
isArrayRelationship: isRelationship
|
|
606
|
-
? propType.startsWith("Array<")
|
|
607
|
-
: false,
|
|
608
|
-
isString,
|
|
609
|
-
isNumber,
|
|
610
|
-
isBoolean,
|
|
611
|
-
isDate,
|
|
612
|
-
isPrimaryKey,
|
|
613
|
-
};
|
|
614
|
-
if (isRelationship) {
|
|
615
|
-
const relData = schema.relationships[propName];
|
|
616
|
-
propDef.relationshipClassName = relData.class_name;
|
|
617
|
-
propDef.relationshipPrimaryKeyField = relData.primary_key_field;
|
|
618
|
-
// Add to relationshipFields array with modelName for dynamic loading
|
|
619
|
-
relationshipFields.push({
|
|
620
|
-
field: propName,
|
|
621
|
-
ModelClass: relData.class_name,
|
|
622
|
-
relationshipType: prop.format, // Use the format value directly
|
|
623
|
-
modelName: relData.model, // Add the full model name for getModelClass
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
properties.push(propDef);
|
|
627
|
-
const match = propType.match(/^(\w+)Fields$/);
|
|
628
|
-
if (match && schema.definitions && schema.definitions[match[1]]) {
|
|
629
|
-
usedDefs.add(match[1]);
|
|
630
|
-
}
|
|
631
|
-
const arrMatch = propType.match(/^Array<(\w+)Fields>$/);
|
|
632
|
-
if (arrMatch && schema.definitions && schema.definitions[arrMatch[1]]) {
|
|
633
|
-
usedDefs.add(arrMatch[1]);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
const definitionsTs = [];
|
|
637
|
-
if (schema.definitions) {
|
|
638
|
-
for (const [defKey, defSchema] of Object.entries(schema.definitions)) {
|
|
639
|
-
if (!usedDefs.has(defKey))
|
|
640
|
-
continue;
|
|
641
|
-
let tsInterface = `export interface ${defKey}Fields {`;
|
|
642
|
-
const req = defSchema.required || [];
|
|
643
|
-
for (const [propName, prop] of Object.entries(defSchema.properties)) {
|
|
644
|
-
tsInterface += `\n ${propName}${req.includes(propName) ? "" : "?"}: ${generateTypeForProperty(prop, schema.definitions)};`;
|
|
645
|
-
}
|
|
646
|
-
tsInterface += `\n}`;
|
|
647
|
-
definitionsTs.push(tsInterface);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
// Only add TypeScript definition imports - JS imports are handled dynamically
|
|
651
|
-
const tsImportSet = new Set();
|
|
652
|
-
if (schema.relationships) {
|
|
653
|
-
for (const [propName, rel] of Object.entries(schema.relationships)) {
|
|
654
|
-
const importPath = getImportPath(currentApp, rel.model);
|
|
655
|
-
tsImportSet.add(`import { ${rel.class_name}Fields, ${rel.class_name}QuerySet, ${rel.class_name}LiveQuerySet } from '${importPath}';`);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
// Convert Sets to Arrays
|
|
659
|
-
const jsImports = []; // No static JS imports needed anymore
|
|
660
|
-
const tsImports = Array.from(tsImportSet);
|
|
661
|
-
let primaryKeyField = "id";
|
|
662
|
-
if (schema.primary_key_field !== undefined) {
|
|
663
|
-
primaryKeyField = schema.primary_key_field;
|
|
664
|
-
}
|
|
665
|
-
return {
|
|
666
|
-
modulePath,
|
|
667
|
-
className,
|
|
668
|
-
interfaceName,
|
|
669
|
-
modelName: rawModelName,
|
|
670
|
-
properties,
|
|
671
|
-
relationshipFields,
|
|
672
|
-
description: schema.description,
|
|
673
|
-
definitions: definitionsTs.length > 0 ? definitionsTs : undefined,
|
|
674
|
-
jsImports,
|
|
675
|
-
tsImports,
|
|
676
|
-
configKey,
|
|
677
|
-
primaryKeyField,
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
/**
|
|
681
|
-
* Generates a TypeScript type for a property.
|
|
682
|
-
* @param {SchemaProperty} prop
|
|
683
|
-
* @param {Object.<string, SchemaDefinition>} [definitions]
|
|
684
|
-
* @param {Object.<string, RelationshipData>} [relationships]
|
|
685
|
-
* @param {string} [propName]
|
|
686
|
-
* @returns {string}
|
|
687
|
-
*/
|
|
688
|
-
function generateTypeForProperty(prop, definitions, relationships, propName) {
|
|
689
|
-
if (relationships && propName && relationships[propName]) {
|
|
690
|
-
const relData = relationships[propName];
|
|
691
|
-
const idType = prop.type === "integer" || prop.type === "number"
|
|
692
|
-
? "number"
|
|
693
|
-
: prop.type === "string"
|
|
694
|
-
? "string"
|
|
695
|
-
: "any";
|
|
696
|
-
return prop.format === "many-to-many"
|
|
697
|
-
? `Array<${relData.class_name}Fields | ${idType}>`
|
|
698
|
-
: `${relData.class_name}Fields | ${idType}`;
|
|
699
|
-
}
|
|
700
|
-
if (prop.ref && prop.ref.startsWith("#/components/schemas/")) {
|
|
701
|
-
const defName = prop.ref.split("/").pop() || prop.ref;
|
|
702
|
-
return `${defName}Fields`;
|
|
703
|
-
}
|
|
704
|
-
if (prop.ref) {
|
|
705
|
-
return prop.format === "many-to-many"
|
|
706
|
-
? `Array<${prop.ref}Fields>`
|
|
707
|
-
: `${prop.ref}Fields`;
|
|
708
|
-
}
|
|
709
|
-
let tsType;
|
|
710
|
-
switch (prop.type) {
|
|
711
|
-
case "string":
|
|
712
|
-
tsType = prop.enum
|
|
713
|
-
? prop.enum.map((v) => `'${v}'`).join(" | ")
|
|
714
|
-
: "string";
|
|
715
|
-
break;
|
|
716
|
-
case "number":
|
|
717
|
-
case "integer":
|
|
718
|
-
tsType = "number";
|
|
719
|
-
break;
|
|
720
|
-
case "boolean":
|
|
721
|
-
tsType = "boolean";
|
|
722
|
-
break;
|
|
723
|
-
case "array":
|
|
724
|
-
tsType = prop.items
|
|
725
|
-
? `Array<${generateTypeForProperty(prop.items, definitions)}>`
|
|
726
|
-
: "any[]";
|
|
727
|
-
break;
|
|
728
|
-
case "object":
|
|
729
|
-
if (prop.format === "json") {
|
|
730
|
-
tsType = "any";
|
|
731
|
-
}
|
|
732
|
-
else if (prop.properties) {
|
|
733
|
-
const nestedProps = Object.entries(prop.properties).map(([key, value]) => {
|
|
734
|
-
const isRequired = prop.required?.includes(key) ? "" : "?";
|
|
735
|
-
return `${key}${isRequired}: ${generateTypeForProperty(value, definitions)}`;
|
|
736
|
-
});
|
|
737
|
-
tsType = `{ ${nestedProps.join("; ")} }`;
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
tsType = "Record<string, any>";
|
|
741
|
-
}
|
|
742
|
-
break;
|
|
743
|
-
default:
|
|
744
|
-
tsType = "any";
|
|
745
|
-
break;
|
|
746
|
-
}
|
|
747
|
-
if (prop.nullable) {
|
|
748
|
-
tsType = `${tsType} | null`;
|
|
749
|
-
}
|
|
750
|
-
return tsType;
|
|
751
|
-
}
|
|
752
|
-
/**
|
|
753
|
-
* Gets the default value for a property.
|
|
754
|
-
* @param {SchemaProperty} prop
|
|
755
|
-
* @returns {string}
|
|
756
|
-
*/
|
|
757
|
-
function getDefaultValueForType(prop) {
|
|
758
|
-
return prop.default !== undefined ? JSON.stringify(prop.default) : "null";
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Creates a unique alias for importing a model class based on backend, model path, and class name.
|
|
762
|
-
* Example: ('default', 'django_app.level1.deepmodel', 'DeepModel') => 'Default__django_app__level1__DeepModel'
|
|
763
|
-
* Ensures uniqueness by including sanitized path components.
|
|
764
|
-
*
|
|
765
|
-
* @param {string} backendKey - The backend configuration key (e.g., 'default', 'microservice').
|
|
766
|
-
* @param {string} modelName - The full model name string (e.g., 'django_app.level1.deepmodel').
|
|
767
|
-
* @param {string} className - The base class name (e.g., 'DeepModel').
|
|
768
|
-
* @returns {string} - A unique alias (e.g., 'Default__django_app__level1__DeepModel').
|
|
769
|
-
*/
|
|
770
|
-
function generateImportAlias(backendKey, modelName, className) {
|
|
771
|
-
// 1. Sanitize Backend Key
|
|
772
|
-
const sanitizedBackendKey = (backendKey.charAt(0).toUpperCase() + backendKey.slice(1)).replace(/[^a-zA-Z0-9_]/g, ""); // Allow underscore
|
|
773
|
-
// 2. Sanitize Model Path Parts (all parts except the last, which relates to className)
|
|
774
|
-
const modelPathParts = modelName.split(".").slice(0, -1); // Get path parts like ['django_app', 'level1']
|
|
775
|
-
const sanitizedModelPath = modelPathParts
|
|
776
|
-
.map((part) => part.replace(/[^a-zA-Z0-9_]/g, "_")) // Replace invalid chars with underscore
|
|
777
|
-
.join("__"); // Join parts with double underscore -> "django_app__level1"
|
|
778
|
-
// 3. Combine: Backend__Path__ClassName
|
|
779
|
-
// Ensure className itself is sanitized just in case, although usually it should be a valid identifier
|
|
780
|
-
const sanitizedClassName = className.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
781
|
-
if (sanitizedModelPath) {
|
|
782
|
-
// If path parts exist, include them: Default__django_app__level1__DeepModel
|
|
783
|
-
return `${sanitizedBackendKey}__${sanitizedModelPath}__${sanitizedClassName}`;
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
// If no path parts (e.g., modelName is just 'simplemodel'), use: Default__SimpleModel
|
|
787
|
-
return `${sanitizedBackendKey}__${sanitizedClassName}`;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Generates a model registry file...
|
|
792
|
-
*
|
|
793
|
-
* @param {Array<{model: string, relativePath: string, backend: string, className: string}>} generatedFiles
|
|
794
|
-
* @param {Object.<string, BackendConfig>} backendConfigs
|
|
795
|
-
* @returns {Promise<void>}
|
|
796
|
-
*/
|
|
797
|
-
async function generateModelRegistry(generatedFiles, backendConfigs) {
|
|
798
|
-
const registryByBackend = {};
|
|
799
|
-
const allUniqueImports = new Set();
|
|
800
|
-
for (const file of generatedFiles) {
|
|
801
|
-
const backendKey = file.backend;
|
|
802
|
-
const modelName = file.model; // e.g., 'django_app.dummymodel'
|
|
803
|
-
const originalClassName = file.className; // e.g., 'DummyModel'
|
|
804
|
-
const typesDir = backendConfigs[backendKey].GENERATED_TYPES_DIR;
|
|
805
|
-
const importPath = "./" +
|
|
806
|
-
path
|
|
807
|
-
.relative("./", path.join(typesDir, file.relativePath))
|
|
808
|
-
.replace(/\\/g, "/");
|
|
809
|
-
const importAlias = generateImportAlias(backendKey, modelName, originalClassName);
|
|
810
|
-
// Example result: 'Default__django_app__DummyModel'
|
|
811
|
-
registryByBackend[backendKey] = registryByBackend[backendKey] || {
|
|
812
|
-
imports: [],
|
|
813
|
-
models: {},
|
|
814
|
-
};
|
|
815
|
-
const importStatement = `import { ${originalClassName} as ${importAlias} } from '${importPath}.js';`;
|
|
816
|
-
registryByBackend[backendKey].imports.push(importStatement);
|
|
817
|
-
allUniqueImports.add(importStatement);
|
|
818
|
-
registryByBackend[backendKey].models[modelName] = importAlias; // Store mapping: 'django_app.dummymodel': 'Default__django_app__DummyModel'
|
|
819
|
-
}
|
|
820
|
-
// --- Generate registry content ---
|
|
821
|
-
let registryContent = `/**
|
|
822
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
823
|
-
* It provides a registry of all models organized by config key and model name.
|
|
824
|
-
* Uses import aliases incorporating model paths to ensure uniqueness.
|
|
825
|
-
*/
|
|
826
|
-
|
|
827
|
-
// --- Imports ---
|
|
828
|
-
`;
|
|
829
|
-
// Add all unique import statements collected
|
|
830
|
-
registryContent += Array.from(allUniqueImports).sort().join("\n");
|
|
831
|
-
registryContent += "\n\n";
|
|
832
|
-
// --- Create the registry object ---
|
|
833
|
-
registryContent += `/**
|
|
834
|
-
* Model registry mapped by configKey and modelName.
|
|
835
|
-
* Values are the aliased imported classes.
|
|
836
|
-
* @type {Object.<string, Object.<string, Function>>}
|
|
837
|
-
*/
|
|
838
|
-
export const MODEL_REGISTRY = {
|
|
839
|
-
`;
|
|
840
|
-
// Add entries for each backend
|
|
841
|
-
const backendEntries = Object.entries(registryByBackend);
|
|
842
|
-
backendEntries.forEach(([backendKey, data], backendIndex) => {
|
|
843
|
-
registryContent += ` '${backendKey}': {\n`;
|
|
844
|
-
// Add model entries: 'model.name': Alias (e.g., 'django_app.dummymodel': Default__django_app__DummyModel)
|
|
845
|
-
const modelEntries = Object.entries(data.models);
|
|
846
|
-
modelEntries.forEach(([modelName, alias], modelIndex) => {
|
|
847
|
-
registryContent += ` '${modelName}': ${alias}`; // Use the generated alias here
|
|
848
|
-
if (modelIndex < modelEntries.length - 1) {
|
|
849
|
-
registryContent += ",";
|
|
850
|
-
}
|
|
851
|
-
registryContent += "\n";
|
|
852
|
-
});
|
|
853
|
-
registryContent += ` }`;
|
|
854
|
-
if (backendIndex < backendEntries.length - 1) {
|
|
855
|
-
registryContent += ",";
|
|
856
|
-
}
|
|
857
|
-
registryContent += "\n";
|
|
858
|
-
});
|
|
859
|
-
registryContent += `};
|
|
860
|
-
|
|
861
|
-
/**
|
|
862
|
-
* Get a model class by name.
|
|
863
|
-
* This remains synchronous.
|
|
864
|
-
*
|
|
865
|
-
* @param {string} modelName - The model name (e.g., 'django_app.dummymodel')
|
|
866
|
-
* @param {string} configKey - The config key (backend name, e.g., 'default')
|
|
867
|
-
* @returns {Function|null} - The model class (via its alias) or null if not found.
|
|
868
|
-
*/
|
|
869
|
-
export function getModelClass(modelName, configKey) {
|
|
870
|
-
if (MODEL_REGISTRY[configKey] && MODEL_REGISTRY[configKey][modelName]) {
|
|
871
|
-
// Returns the specific aliased class for that backend/model combination
|
|
872
|
-
return MODEL_REGISTRY[configKey][modelName];
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
console.warn(\`Model class not found for '\${modelName}' in config '\${configKey}'\`);
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
`;
|
|
879
|
-
// --- Write the file ---
|
|
880
|
-
const rootDir = process.cwd();
|
|
881
|
-
const registryFilePath = path.join(rootDir, "model-registry.js");
|
|
882
|
-
try {
|
|
883
|
-
await fs.writeFile(registryFilePath, registryContent);
|
|
884
|
-
console.log(`✨ Generated model registry with path-based aliases at ${registryFilePath}`);
|
|
885
|
-
}
|
|
886
|
-
catch (err) {
|
|
887
|
-
console.error(`❌ Failed to write model registry at ${registryFilePath}:`, err);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Generates app-level index files and a root index file that imports from app indexes.
|
|
892
|
-
* @param {Array<{model: string, relativePath: string, backend: string}>} generatedFiles
|
|
893
|
-
* @param {Object.<string, BackendConfig>} backendConfigs
|
|
894
|
-
* @returns {Promise<void>}
|
|
895
|
-
*/
|
|
896
|
-
async function generateAppLevelIndexFiles(generatedFiles, backendConfigs) {
|
|
897
|
-
// Group files by backend and app
|
|
898
|
-
const filesByBackendAndApp = generatedFiles.reduce((acc, file) => {
|
|
899
|
-
const backend = file.backend;
|
|
900
|
-
const parts = file.model.split(".");
|
|
901
|
-
const app = parts.length > 1 ? parts[0] : "root"; // Use 'root' for models without an app
|
|
902
|
-
acc[backend] = acc[backend] || {};
|
|
903
|
-
acc[backend][app] = acc[backend][app] || [];
|
|
904
|
-
acc[backend][app].push(file);
|
|
905
|
-
return acc;
|
|
906
|
-
}, {});
|
|
907
|
-
const indexTemplate = Handlebars.compile(`{{#each files}}
|
|
908
|
-
export * from '{{this.relativePath}}';
|
|
909
|
-
{{/each}}`);
|
|
910
|
-
// Generate app-level index files for each backend and app
|
|
911
|
-
for (const [backendName, appGroups] of Object.entries(filesByBackendAndApp)) {
|
|
912
|
-
const backend = backendConfigs[backendName];
|
|
913
|
-
const rootExports = [];
|
|
914
|
-
for (const [app, files] of Object.entries(appGroups)) {
|
|
915
|
-
if (app === "root") {
|
|
916
|
-
// Handle models without an app prefix
|
|
917
|
-
for (const file of files) {
|
|
918
|
-
rootExports.push(`export * from '${file.relativePath}';`);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
// Create app-level index files with proper relative paths
|
|
923
|
-
// Include backend key in the path
|
|
924
|
-
const appDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME, app.toLowerCase());
|
|
925
|
-
// Create relative paths for imports within the app directory
|
|
926
|
-
// These should be relative to the app directory, not the backend root
|
|
927
|
-
const appFiles = files.map((file) => {
|
|
928
|
-
// Get the last part of the path (the actual file name without extension)
|
|
929
|
-
const fileName = path.basename(file.relativePath);
|
|
930
|
-
return {
|
|
931
|
-
...file,
|
|
932
|
-
relativePath: "./" + fileName,
|
|
933
|
-
};
|
|
934
|
-
});
|
|
935
|
-
const indexContent = indexTemplate({ files: appFiles });
|
|
936
|
-
await fs.writeFile(path.join(appDir, "index.js"), indexContent.trim());
|
|
937
|
-
await fs.writeFile(path.join(appDir, "index.d.ts"), indexContent.trim());
|
|
938
|
-
// Add an export for this app's index to the backend root index
|
|
939
|
-
rootExports.push(`export * from './${app.toLowerCase()}/index.js';`);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
// Write the backend root index file (add FileObject export)
|
|
943
|
-
const backendIndexContent = [
|
|
944
|
-
"export * from './fileobject.js';", // Add this line
|
|
945
|
-
...rootExports,
|
|
946
|
-
].join("\n");
|
|
947
|
-
// Include backend key in the path for index files
|
|
948
|
-
const backendDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
|
|
949
|
-
await fs.writeFile(path.join(backendDir, "index.js"), backendIndexContent);
|
|
950
|
-
await fs.writeFile(path.join(backendDir, "index.d.ts"), backendIndexContent);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
// FileObject template
|
|
954
|
-
const FILEOBJECT_TEMPLATE = `/**
|
|
955
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
956
|
-
* Backend-specific FileObject class for {{backendName}}
|
|
957
|
-
*/
|
|
958
|
-
|
|
959
|
-
import { FileObject as BaseFileObject } from '{{modulePath}}';
|
|
960
|
-
|
|
961
|
-
export class {{backendName}}FileObject extends BaseFileObject {
|
|
962
|
-
static configKey = '{{backendName}}';
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
export const FileObject = {{backendName}}FileObject;
|
|
966
|
-
`;
|
|
967
|
-
const fileObjectTemplate = Handlebars.compile(FILEOBJECT_TEMPLATE);
|
|
968
|
-
/**
|
|
969
|
-
* Generates a FileObject class for a backend.
|
|
970
|
-
* @param {BackendConfig} backend
|
|
971
|
-
* @returns {Promise<void>}
|
|
972
|
-
*/
|
|
973
|
-
async function generateFileObjectForBackend(backend) {
|
|
974
|
-
const modulePath = process.env.NODE_ENV === "test" ? "../../../src" : "@statezero/core";
|
|
975
|
-
const templateData = {
|
|
976
|
-
backendName: backend.NAME,
|
|
977
|
-
modulePath: modulePath,
|
|
978
|
-
};
|
|
979
|
-
const fileObjectContent = fileObjectTemplate(templateData);
|
|
980
|
-
// Include backend key in the path
|
|
981
|
-
const backendDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
|
|
982
|
-
await fs.mkdir(backendDir, { recursive: true });
|
|
983
|
-
const fileObjectPath = path.join(backendDir, "fileobject.js");
|
|
984
|
-
await fs.writeFile(fileObjectPath, fileObjectContent);
|
|
985
|
-
// Also generate TypeScript declaration
|
|
986
|
-
const dtsContent = `/**
|
|
987
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
988
|
-
* Backend-specific FileObject class for ${backend.NAME}
|
|
989
|
-
*/
|
|
990
|
-
|
|
991
|
-
import { FileObject as BaseFileObject } from '${modulePath}';
|
|
992
|
-
|
|
993
|
-
export declare class ${backend.NAME}FileObject extends BaseFileObject {
|
|
994
|
-
static configKey: string;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
export declare const FileObject: typeof ${backend.NAME}FileObject;
|
|
998
|
-
`;
|
|
999
|
-
const dtsPath = path.join(backendDir, "fileobject.d.ts");
|
|
1000
|
-
await fs.writeFile(dtsPath, dtsContent);
|
|
1001
|
-
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Generates a top-level index file that re-exports from the 'default' backend
|
|
1004
|
-
* if it exists. This improves backwards compatibility and reduces verbosity.
|
|
1005
|
-
* @param {Object.<string, BackendConfig>} backendConfigs
|
|
1006
|
-
* @returns {Promise<void>}
|
|
1007
|
-
*/
|
|
1008
|
-
async function generateDefaultBackendIndex(backendConfigs) {
|
|
1009
|
-
// Check if a 'default' backend exists
|
|
1010
|
-
if (!backendConfigs['default']) {
|
|
1011
|
-
return; // No default backend, skip
|
|
1012
|
-
}
|
|
1013
|
-
const defaultBackend = backendConfigs['default'];
|
|
1014
|
-
const typesDir = defaultBackend.GENERATED_TYPES_DIR;
|
|
1015
|
-
// Create top-level index files that re-export from default
|
|
1016
|
-
const indexContent = `/**
|
|
1017
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
1018
|
-
* Re-exports from the 'default' backend for backwards compatibility.
|
|
1019
|
-
*/
|
|
1020
|
-
|
|
1021
|
-
export * from './default/index.js';
|
|
1022
|
-
`;
|
|
1023
|
-
const jsIndexPath = path.join(typesDir, 'index.js');
|
|
1024
|
-
const dtsIndexPath = path.join(typesDir, 'index.d.ts');
|
|
1025
|
-
await fs.writeFile(jsIndexPath, indexContent);
|
|
1026
|
-
await fs.writeFile(dtsIndexPath, indexContent);
|
|
1027
|
-
console.log(`✨ Generated top-level index for 'default' backend`);
|
|
1028
|
-
}
|
|
1029
|
-
// --------------------
|
|
1030
|
-
// Main Runner: Fetch models and prompt selection
|
|
1031
|
-
// --------------------
|
|
1032
|
-
async function main() {
|
|
1033
|
-
// Load configuration from file (CLI-only or tests) before any other operations.
|
|
1034
|
-
loadConfigFromFile();
|
|
1035
|
-
// Retrieve the validated configuration from the global config singleton.
|
|
1036
|
-
const configData = configInstance.getConfig();
|
|
1037
|
-
const backendConfigs = configData.backendConfigs;
|
|
1038
|
-
const fetchPromises = Object.keys(backendConfigs).map(async (key) => {
|
|
1039
|
-
const backend = backendConfigs[key];
|
|
1040
|
-
backend.NAME = key;
|
|
1041
|
-
try {
|
|
1042
|
-
const headers = backend.SYNC_TOKEN ? { "X-Sync-Token": backend.SYNC_TOKEN } : {};
|
|
1043
|
-
const response = await axios.get(`${backend.API_URL}/models/`, { headers });
|
|
1044
|
-
return { backend, models: response.data };
|
|
1045
|
-
}
|
|
1046
|
-
catch (error) {
|
|
1047
|
-
console.error(`Error fetching models from backend ${backend.NAME}:`, error.message);
|
|
1048
|
-
return { backend, models: [] };
|
|
1049
|
-
}
|
|
1050
|
-
});
|
|
1051
|
-
const backendModels = await Promise.all(fetchPromises);
|
|
1052
|
-
const choices = [];
|
|
1053
|
-
// Create a simple separator object for environments where inquirer.Separator isn't available
|
|
1054
|
-
const createSeparator = (text) => ({ name: text, value: null });
|
|
1055
|
-
for (const { backend, models } of backendModels) {
|
|
1056
|
-
choices.push(createSeparator(`\n=== ${backend.NAME} ===\n`));
|
|
1057
|
-
for (const model of models) {
|
|
1058
|
-
choices.push({
|
|
1059
|
-
name: model,
|
|
1060
|
-
value: { backend, model },
|
|
1061
|
-
checked: true,
|
|
1062
|
-
});
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
if (choices.length === 0) {
|
|
1066
|
-
console.log("No models to synchronise");
|
|
1067
|
-
process.exit(0);
|
|
1068
|
-
}
|
|
1069
|
-
const selectedModels = await selectModels(choices, "Select models to synchronise:");
|
|
1070
|
-
if (!selectedModels || selectedModels.length === 0) {
|
|
1071
|
-
console.log("No models selected. Exiting.");
|
|
1072
|
-
process.exit(0);
|
|
1073
|
-
}
|
|
1074
|
-
const modelsByBackend = selectedModels.reduce((acc, item) => {
|
|
1075
|
-
const key = item.backend.NAME;
|
|
1076
|
-
acc[key] = acc[key] || { backend: item.backend, models: [] };
|
|
1077
|
-
acc[key].models.push(item.model);
|
|
1078
|
-
return acc;
|
|
1079
|
-
}, {});
|
|
1080
|
-
const allGeneratedFiles = [];
|
|
1081
|
-
for (const group of Object.values(modelsByBackend)) {
|
|
1082
|
-
console.log(`\nProcessing backend: ${group.backend.NAME}`);
|
|
1083
|
-
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
|
|
1084
|
-
progressBar.start(group.models.length, 0);
|
|
1085
|
-
for (const model of group.models) {
|
|
1086
|
-
try {
|
|
1087
|
-
const result = await generateSchemaForModel(group.backend, model);
|
|
1088
|
-
allGeneratedFiles.push({ ...result, backend: group.backend.NAME });
|
|
1089
|
-
}
|
|
1090
|
-
catch (error) {
|
|
1091
|
-
console.error(`Error generating schema for model ${model} from backend ${group.backend.NAME}:`, error.message);
|
|
1092
|
-
}
|
|
1093
|
-
progressBar.increment();
|
|
1094
|
-
}
|
|
1095
|
-
progressBar.stop();
|
|
1096
|
-
// Generate FileObject for this backend
|
|
1097
|
-
try {
|
|
1098
|
-
await generateFileObjectForBackend(group.backend);
|
|
1099
|
-
console.log(`✨ Generated FileObject for backend: ${group.backend.NAME}`);
|
|
1100
|
-
}
|
|
1101
|
-
catch (error) {
|
|
1102
|
-
console.error(`Error generating FileObject for backend ${group.backend.NAME}:`, error.message);
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
// Generate an index file per app
|
|
1106
|
-
await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
|
|
1107
|
-
// Generate model registry
|
|
1108
|
-
await generateModelRegistry(allGeneratedFiles, backendConfigs);
|
|
1109
|
-
// Generate top-level index for 'default' backend if it exists
|
|
1110
|
-
await generateDefaultBackendIndex(backendConfigs);
|
|
1111
|
-
console.log(`✨ Generated JavaScript files with TypeScript declarations for ${selectedModels.length} models across ${Object.keys(backendConfigs).length} backends.`);
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Main exported function to generate schema.
|
|
1115
|
-
* @param {GenerateArgs} args
|
|
1116
|
-
* @returns {Promise<void>}
|
|
1117
|
-
*/
|
|
1118
|
-
export async function generateSchema(args) {
|
|
1119
|
-
await main();
|
|
1120
|
-
}
|