@statezero/core 0.2.38 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/backend1/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/backend1/django_app/calculate-hash.js +80 -0
- package/dist/actions/backend1/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/backend1/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/backend1/django_app/get-current-username.js +65 -0
- package/dist/actions/backend1/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/backend1/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/backend1/django_app/get-server-status.js +68 -0
- package/dist/actions/backend1/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/backend1/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/backend1/django_app/get-user-info.js +70 -0
- package/dist/actions/backend1/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/backend1/django_app/index.d.ts +1 -0
- package/dist/actions/backend1/django_app/index.js +6 -0
- package/dist/actions/backend1/django_app/process-data.d.ts +51 -0
- package/dist/actions/backend1/django_app/process-data.js +78 -0
- package/dist/actions/backend1/django_app/process-data.schema.json +117 -0
- package/dist/actions/backend1/django_app/send-notification.d.ts +55 -0
- package/dist/actions/backend1/django_app/send-notification.js +81 -0
- package/dist/actions/backend1/django_app/send-notification.schema.json +175 -0
- package/dist/actions/backend1/index.d.ts +1 -0
- package/dist/actions/backend1/index.js +1 -0
- package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/default/django_app/calculate-hash.js +80 -0
- package/dist/actions/default/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/default/django_app/get-current-username.js +65 -0
- package/dist/actions/default/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/default/django_app/get-server-status.js +68 -0
- package/dist/actions/default/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/default/django_app/get-user-info.js +70 -0
- package/dist/actions/default/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/default/django_app/index.d.ts +1 -0
- package/dist/actions/default/django_app/index.js +6 -0
- package/dist/actions/default/django_app/process-data.d.ts +51 -0
- package/dist/actions/default/django_app/process-data.js +78 -0
- package/dist/actions/default/django_app/process-data.schema.json +117 -0
- package/dist/actions/default/django_app/send-notification.d.ts +55 -0
- package/dist/actions/default/django_app/send-notification.js +81 -0
- package/dist/actions/default/django_app/send-notification.schema.json +175 -0
- package/dist/actions/default/index.d.ts +1 -0
- package/dist/actions/default/index.js +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/adaptors/react/composables.d.ts +1 -0
- package/dist/adaptors/react/composables.js +4 -0
- package/dist/adaptors/react/index.d.ts +1 -0
- package/dist/adaptors/react/index.js +1 -0
- package/dist/adaptors/vue/components/LayoutRenderer.js +46 -49
- package/dist/adaptors/vue/components/defaults/index.d.ts +7 -0
- package/dist/adaptors/vue/components/defaults/index.js +31 -0
- package/dist/adaptors/vue/components/index.d.ts +1 -0
- package/dist/adaptors/vue/components/index.js +7 -0
- package/dist/adaptors/vue/composables.d.ts +2 -0
- package/dist/adaptors/vue/composables.js +44 -0
- package/dist/adaptors/vue/index.d.ts +3 -0
- package/dist/adaptors/vue/index.js +4 -0
- package/dist/adaptors/vue/reactivity.d.ts +18 -0
- package/dist/adaptors/vue/reactivity.js +132 -0
- package/dist/cli/commands/sync.d.ts +6 -0
- package/dist/cli/commands/sync.js +30 -0
- package/dist/cli/commands/syncActions.d.ts +46 -0
- package/dist/cli/commands/syncActions.js +717 -0
- package/dist/cli/commands/syncModels.d.ts +132 -0
- package/dist/cli/commands/syncModels.js +1120 -0
- package/dist/cli/configFileLoader.d.ts +10 -0
- package/dist/cli/configFileLoader.js +85 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +22 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.js +273 -0
- package/dist/core/eventReceivers.d.ts +185 -0
- package/dist/core/eventReceivers.js +266 -0
- package/dist/core/utils.d.ts +8 -0
- package/dist/core/utils.js +62 -0
- package/dist/errorHandler.d.ts +21 -0
- package/dist/errorHandler.js +27 -0
- package/dist/filtering/localFiltering.d.ts +110 -0
- package/dist/filtering/localFiltering.js +1080 -0
- package/dist/flavours/django/dates.d.ts +34 -0
- package/dist/flavours/django/dates.js +113 -0
- package/dist/flavours/django/errors.d.ts +138 -0
- package/dist/flavours/django/errors.js +195 -0
- package/dist/flavours/django/f.d.ts +6 -0
- package/dist/flavours/django/f.js +91 -0
- package/dist/flavours/django/files.d.ts +62 -0
- package/dist/flavours/django/files.js +355 -0
- package/dist/flavours/django/makeApiCall.d.ts +36 -0
- package/dist/flavours/django/makeApiCall.js +169 -0
- package/dist/flavours/django/manager.d.ts +204 -0
- package/dist/flavours/django/manager.js +222 -0
- package/dist/flavours/django/model.d.ts +137 -0
- package/dist/flavours/django/model.js +366 -0
- package/dist/flavours/django/operationFactory.d.ts +73 -0
- package/dist/flavours/django/operationFactory.js +248 -0
- package/dist/flavours/django/q.d.ts +70 -0
- package/dist/flavours/django/q.js +43 -0
- package/dist/flavours/django/queryExecutor.d.ts +149 -0
- package/dist/flavours/django/queryExecutor.js +590 -0
- package/dist/flavours/django/querySet.d.ts +301 -0
- package/dist/flavours/django/querySet.js +736 -0
- package/dist/flavours/django/serializers.d.ts +39 -0
- package/dist/flavours/django/serializers.js +296 -0
- package/dist/flavours/django/tempPk.d.ts +31 -0
- package/dist/flavours/django/tempPk.js +92 -0
- package/dist/flavours/django/utils.d.ts +19 -0
- package/dist/flavours/django/utils.js +29 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/backend1/django_app/comprehensivemodel.js +71 -0
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/backend1/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/custompkmodel.js +69 -0
- package/dist/models/backend1/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/dailyrate.d.ts +230 -0
- package/dist/models/backend1/django_app/dailyrate.js +71 -0
- package/dist/models/backend1/django_app/dailyrate.schema.json +212 -0
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +140 -0
- package/dist/models/backend1/django_app/deepmodellevel1.js +72 -0
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +114 -0
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/deepmodellevel2.js +71 -0
- package/dist/models/backend1/django_app/deepmodellevel2.schema.json +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.js +69 -0
- package/dist/models/backend1/django_app/deepmodellevel3.schema.json +69 -0
- package/dist/models/backend1/django_app/dummymodel.d.ts +134 -0
- package/dist/models/backend1/django_app/dummymodel.js +71 -0
- package/dist/models/backend1/django_app/dummymodel.schema.json +109 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +69 -0
- package/dist/models/backend1/django_app/filetest.d.ts +140 -0
- package/dist/models/backend1/django_app/filetest.js +69 -0
- package/dist/models/backend1/django_app/filetest.schema.json +111 -0
- package/dist/models/backend1/django_app/index.d.ts +1 -0
- package/dist/models/backend1/django_app/index.js +21 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/order.d.ts +220 -0
- package/dist/models/backend1/django_app/order.js +71 -0
- package/dist/models/backend1/django_app/order.schema.json +203 -0
- package/dist/models/backend1/django_app/orderitem.d.ts +172 -0
- package/dist/models/backend1/django_app/orderitem.js +72 -0
- package/dist/models/backend1/django_app/orderitem.schema.json +149 -0
- package/dist/models/backend1/django_app/product.d.ts +254 -0
- package/dist/models/backend1/django_app/product.js +71 -0
- package/dist/models/backend1/django_app/product.schema.json +277 -0
- package/dist/models/backend1/django_app/productcategory.d.ts +92 -0
- package/dist/models/backend1/django_app/productcategory.js +69 -0
- package/dist/models/backend1/django_app/productcategory.schema.json +70 -0
- package/dist/models/backend1/django_app/rateplan.d.ts +92 -0
- package/dist/models/backend1/django_app/rateplan.js +69 -0
- package/dist/models/backend1/django_app/rateplan.schema.json +70 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/backend1/fileobject.d.ts +4 -0
- package/dist/models/backend1/fileobject.js +9 -0
- package/dist/models/backend1/index.d.ts +2 -0
- package/dist/models/backend1/index.js +2 -0
- package/dist/models/default/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/default/django_app/comprehensivemodel.js +71 -0
- package/dist/models/default/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/default/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/custompkmodel.js +69 -0
- package/dist/models/default/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/dailyrate.d.ts +230 -0
- package/dist/models/default/django_app/dailyrate.js +71 -0
- package/dist/models/default/django_app/dailyrate.schema.json +212 -0
- package/dist/models/default/django_app/deepmodellevel1.d.ts +128 -0
- package/dist/models/default/django_app/deepmodellevel1.js +72 -0
- package/dist/models/default/django_app/deepmodellevel1.schema.json +102 -0
- package/dist/models/default/django_app/deepmodellevel2.d.ts +106 -0
- package/dist/models/default/django_app/deepmodellevel2.js +71 -0
- package/dist/models/default/django_app/deepmodellevel2.schema.json +80 -0
- package/dist/models/default/django_app/deepmodellevel3.d.ts +80 -0
- package/dist/models/default/django_app/deepmodellevel3.js +69 -0
- package/dist/models/default/django_app/deepmodellevel3.schema.json +57 -0
- package/dist/models/default/django_app/dummymodel.d.ts +122 -0
- package/dist/models/default/django_app/dummymodel.js +71 -0
- package/dist/models/default/django_app/dummymodel.schema.json +97 -0
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +80 -0
- package/dist/models/default/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +57 -0
- package/dist/models/default/django_app/filetest.d.ts +128 -0
- package/dist/models/default/django_app/filetest.js +69 -0
- package/dist/models/default/django_app/filetest.schema.json +99 -0
- package/dist/models/default/django_app/index.d.ts +1 -0
- package/dist/models/default/django_app/index.js +21 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/order.d.ts +220 -0
- package/dist/models/default/django_app/order.js +71 -0
- package/dist/models/default/django_app/order.schema.json +203 -0
- package/dist/models/default/django_app/orderitem.d.ts +172 -0
- package/dist/models/default/django_app/orderitem.js +72 -0
- package/dist/models/default/django_app/orderitem.schema.json +149 -0
- package/dist/models/default/django_app/product.d.ts +254 -0
- package/dist/models/default/django_app/product.js +71 -0
- package/dist/models/default/django_app/product.schema.json +277 -0
- package/dist/models/default/django_app/productcategory.d.ts +92 -0
- package/dist/models/default/django_app/productcategory.js +69 -0
- package/dist/models/default/django_app/productcategory.schema.json +70 -0
- package/dist/models/default/django_app/rateplan.d.ts +92 -0
- package/dist/models/default/django_app/rateplan.js +69 -0
- package/dist/models/default/django_app/rateplan.schema.json +70 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/default/fileobject.d.ts +4 -0
- package/dist/models/default/fileobject.js +9 -0
- package/dist/models/default/index.d.ts +2 -0
- package/dist/models/default/index.js +2 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +5 -0
- package/dist/react-entry.d.ts +2 -0
- package/dist/react-entry.js +2 -0
- package/dist/reactiveAdaptor.d.ts +24 -0
- package/dist/reactiveAdaptor.js +38 -0
- package/dist/reset.d.ts +15 -0
- package/dist/reset.js +97 -0
- package/dist/setup.d.ts +15 -0
- package/dist/setup.js +33 -0
- package/dist/syncEngine/cache/cache.d.ts +75 -0
- package/dist/syncEngine/cache/cache.js +355 -0
- package/dist/syncEngine/metrics/metricOptCalcs.d.ts +79 -0
- package/dist/syncEngine/metrics/metricOptCalcs.js +284 -0
- package/dist/syncEngine/registries/metricRegistry.d.ts +58 -0
- package/dist/syncEngine/registries/metricRegistry.js +171 -0
- package/dist/syncEngine/registries/modelStoreRegistry.d.ts +11 -0
- package/dist/syncEngine/registries/modelStoreRegistry.js +63 -0
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +41 -0
- package/dist/syncEngine/registries/querysetStoreGraph.js +174 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +72 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +335 -0
- package/dist/syncEngine/stores/metricStore.d.ts +55 -0
- package/dist/syncEngine/stores/metricStore.js +222 -0
- package/dist/syncEngine/stores/modelStore.d.ts +53 -0
- package/dist/syncEngine/stores/modelStore.js +565 -0
- package/dist/syncEngine/stores/operation.d.ts +139 -0
- package/dist/syncEngine/stores/operation.js +291 -0
- package/dist/syncEngine/stores/operationEventHandlers.d.ts +8 -0
- package/dist/syncEngine/stores/operationEventHandlers.js +322 -0
- package/dist/syncEngine/stores/querysetStore.d.ts +60 -0
- package/dist/syncEngine/stores/querysetStore.js +294 -0
- package/dist/syncEngine/stores/reactivity.d.ts +3 -0
- package/dist/syncEngine/stores/reactivity.js +4 -0
- package/dist/syncEngine/stores/utils.d.ts +14 -0
- package/dist/syncEngine/stores/utils.js +32 -0
- package/dist/syncEngine/sync.d.ts +46 -0
- package/dist/syncEngine/sync.js +389 -0
- package/dist/testing.d.ts +63 -0
- package/dist/testing.js +175 -0
- package/dist/vue-entry.d.ts +15 -0
- package/dist/vue-entry.js +7 -0
- package/package.json +6 -7
- package/dist/adaptors/vue/components/layout.tailwind.css +0 -51
- /package/{dist → src}/adaptors/vue/components/layout.css +0 -0
|
@@ -0,0 +1,717 @@
|
|
|
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 { z } from "zod";
|
|
8
|
+
import { configInstance } from "../../config.js";
|
|
9
|
+
import { loadConfigFromFile } from "../configFileLoader.js";
|
|
10
|
+
// ================================================================================================
|
|
11
|
+
// JSDOC TYPE DEFINITIONS
|
|
12
|
+
// ================================================================================================
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} ActionProperty
|
|
15
|
+
* @property {string} type
|
|
16
|
+
* @property {string} [format]
|
|
17
|
+
* @property {boolean} [required]
|
|
18
|
+
* @property {boolean} [nullable]
|
|
19
|
+
* @property {any} [default]
|
|
20
|
+
* @property {string[]} [choices]
|
|
21
|
+
* @property {number} [min_length]
|
|
22
|
+
* @property {number} [max_length]
|
|
23
|
+
* @property {number} [min_value]
|
|
24
|
+
* @property {number} [max_value]
|
|
25
|
+
* @property {ActionProperty} [items]
|
|
26
|
+
* @property {Object.<string, ActionProperty>} [properties]
|
|
27
|
+
* @property {string} [description]
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} ActionDefinition
|
|
31
|
+
* @property {string} action_name
|
|
32
|
+
* @property {string} title
|
|
33
|
+
* @property {string} class_name
|
|
34
|
+
* @property {string | null} app - The application group for the action.
|
|
35
|
+
* @property {string | null} docstring - The action's documentation string.
|
|
36
|
+
* @property {Object.<string, ActionProperty>} input_properties
|
|
37
|
+
* @property {Object.<string, ActionProperty>} response_properties
|
|
38
|
+
* @property {string[]} permissions
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} BackendConfig
|
|
42
|
+
* @property {string} NAME
|
|
43
|
+
* @property {string} API_URL
|
|
44
|
+
* @property {string} GENERATED_ACTIONS_DIR
|
|
45
|
+
*/
|
|
46
|
+
// ================================================================================================
|
|
47
|
+
// CLI INTERACTIVITY & FALLBACKS
|
|
48
|
+
// ================================================================================================
|
|
49
|
+
async function fallbackSelectAll(choices, message) {
|
|
50
|
+
console.log(`\n${message}`);
|
|
51
|
+
console.log("Interactive selection not available - generating ALL actions:");
|
|
52
|
+
const allActions = [];
|
|
53
|
+
for (const choice of choices) {
|
|
54
|
+
if (!choice.value) {
|
|
55
|
+
console.log(choice.name);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
allActions.push(choice.value);
|
|
59
|
+
console.log(` ✓ ${choice.name}`);
|
|
60
|
+
}
|
|
61
|
+
console.log(`\nGenerating ALL ${allActions.length} actions.`);
|
|
62
|
+
return allActions;
|
|
63
|
+
}
|
|
64
|
+
async function selectActions(choices, message) {
|
|
65
|
+
try {
|
|
66
|
+
const inquirer = (await import("inquirer")).default;
|
|
67
|
+
const { selectedActions } = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: "checkbox",
|
|
70
|
+
name: "selectedActions",
|
|
71
|
+
message,
|
|
72
|
+
choices,
|
|
73
|
+
pageSize: 20,
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
return selectedActions;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.warn("Interactive selection failed, generating all available actions:", error.message);
|
|
80
|
+
return await fallbackSelectAll(choices, message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ================================================================================================
|
|
84
|
+
// HANDLEBARS TEMPLATES
|
|
85
|
+
// ================================================================================================
|
|
86
|
+
// Register a helper to format multi-line docstrings for JSDoc.
|
|
87
|
+
Handlebars.registerHelper("formatJsDoc", function (text) {
|
|
88
|
+
if (!text)
|
|
89
|
+
return "";
|
|
90
|
+
return text
|
|
91
|
+
.split("\n")
|
|
92
|
+
.map((line) => ` * ${line}`)
|
|
93
|
+
.join("\n");
|
|
94
|
+
});
|
|
95
|
+
const JS_ACTION_TEMPLATE = `/**
|
|
96
|
+
* This file was auto-generated. Do not make direct changes to the file.
|
|
97
|
+
* Action: {{title}}
|
|
98
|
+
* App: {{app}}
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
import axios from 'axios';
|
|
102
|
+
import { z } from 'zod';
|
|
103
|
+
import { configInstance, parseStateZeroError, serializeActionPayload } from '{{modulePath}}';
|
|
104
|
+
import actionSchema from './{{schemaFileName}}' assert { type: 'json' };
|
|
105
|
+
|
|
106
|
+
{{#if inputSchemaString}}
|
|
107
|
+
/**
|
|
108
|
+
* Zod schema for the input of {{functionName}}.
|
|
109
|
+
* NOTE: This is an object schema for validating the data payload.
|
|
110
|
+
*/
|
|
111
|
+
export const {{functionName}}InputSchema = z.object({ {{{inputSchemaString}}} });
|
|
112
|
+
{{/if}}
|
|
113
|
+
|
|
114
|
+
{{#if responseSchemaString}}
|
|
115
|
+
/**
|
|
116
|
+
* Zod schema for the response of {{functionName}}.
|
|
117
|
+
*/
|
|
118
|
+
export const {{functionName}}ResponseSchema = z.object({ {{{responseSchemaString}}} });
|
|
119
|
+
{{/if}}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
{{#if docstring}}
|
|
123
|
+
{{{formatJsDoc docstring}}}
|
|
124
|
+
*
|
|
125
|
+
{{else}}
|
|
126
|
+
* {{title}}
|
|
127
|
+
{{/if}}
|
|
128
|
+
{{#each tsDocParams}}
|
|
129
|
+
* @param {{{this.type}}} {{this.name}} - {{this.description}}
|
|
130
|
+
{{/each}}
|
|
131
|
+
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
132
|
+
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
133
|
+
*/
|
|
134
|
+
export async function {{functionName}}({{{jsFunctionParams}}}) {
|
|
135
|
+
// Construct the data payload from the function arguments
|
|
136
|
+
{{#if payloadProperties}}
|
|
137
|
+
const rawPayload = {
|
|
138
|
+
{{{payloadProperties}}}
|
|
139
|
+
};
|
|
140
|
+
{{else}}
|
|
141
|
+
const rawPayload = {};
|
|
142
|
+
{{/if}}
|
|
143
|
+
|
|
144
|
+
// Serialize payload - handles model instances (extracts PK), files, dates, etc.
|
|
145
|
+
const payload = serializeActionPayload(rawPayload, actionSchema.input_properties);
|
|
146
|
+
|
|
147
|
+
const config = configInstance.getConfig();
|
|
148
|
+
const backend = config.backendConfigs['{{configKey}}'];
|
|
149
|
+
|
|
150
|
+
if (!backend) {
|
|
151
|
+
throw new Error(\`No backend configuration found for key: {{configKey}}\`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const baseUrl = backend.API_URL.replace(/\\/+$/, '');
|
|
155
|
+
const actionUrl = \`\${baseUrl}/actions/{{actionName}}/\`;
|
|
156
|
+
const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const response = await axios.post(actionUrl, payload, {
|
|
160
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
161
|
+
...axiosOverrides,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
{{#if responseSchemaString}}
|
|
165
|
+
return {{functionName}}ResponseSchema.parse(response.data);
|
|
166
|
+
{{else}}
|
|
167
|
+
return response.data;
|
|
168
|
+
{{/if}}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (error instanceof z.ZodError) {
|
|
171
|
+
throw new Error(\`{{title}} failed: Invalid response from server. Details: \${error.message}\`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (error.response && error.response.data) {
|
|
175
|
+
const parsedError = parseStateZeroError(error.response.data);
|
|
176
|
+
|
|
177
|
+
if (Error.captureStackTrace) {
|
|
178
|
+
Error.captureStackTrace(parsedError, {{functionName}});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw parsedError;
|
|
182
|
+
} else if (error.request) {
|
|
183
|
+
throw new Error(\`{{title}} failed: No response received from server.\`);
|
|
184
|
+
} else {
|
|
185
|
+
throw new Error(\`{{title}} failed: \${error.message}\`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export default {{functionName}};
|
|
191
|
+
|
|
192
|
+
{{functionName}}.actionName = '{{actionName}}';
|
|
193
|
+
{{functionName}}.title = '{{title}}';
|
|
194
|
+
{{functionName}}.app = {{#if app}}'{{app}}'{{else}}null{{/if}};
|
|
195
|
+
{{functionName}}.permissions = [{{#each permissions}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}];
|
|
196
|
+
{{functionName}}.configKey = '{{configKey}}';
|
|
197
|
+
`;
|
|
198
|
+
const TS_ACTION_DECLARATION_TEMPLATE = `/**
|
|
199
|
+
* This file was auto-generated. Do not make direct changes to the file.
|
|
200
|
+
* Action: {{title}}
|
|
201
|
+
* App: {{app}}
|
|
202
|
+
*/
|
|
203
|
+
import { z } from 'zod';
|
|
204
|
+
import { AxiosRequestConfig } from 'axios';
|
|
205
|
+
|
|
206
|
+
{{#if inputTsSchemaString}}
|
|
207
|
+
export type {{functionName}}Input = { {{{inputTsSchemaString}}} };
|
|
208
|
+
{{/if}}
|
|
209
|
+
|
|
210
|
+
{{#if responseTsSchemaString}}
|
|
211
|
+
export type {{functionName}}Response = { {{{responseTsSchemaString}}} };
|
|
212
|
+
{{else}}
|
|
213
|
+
export type {{functionName}}Response = any;
|
|
214
|
+
{{/if}}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
{{#if docstring}}
|
|
218
|
+
{{{formatJsDoc docstring}}}
|
|
219
|
+
*
|
|
220
|
+
{{else}}
|
|
221
|
+
* {{title}}
|
|
222
|
+
{{/if}}
|
|
223
|
+
{{#each tsDocParams}}
|
|
224
|
+
* @param {{{this.type}}} {{this.name}} - {{this.description}}
|
|
225
|
+
{{/each}}
|
|
226
|
+
* @param {AxiosRequestConfig} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
227
|
+
* @returns {Promise<{{functionName}}Response>} A promise that resolves with the action's result.
|
|
228
|
+
*/
|
|
229
|
+
export declare function {{functionName}}(
|
|
230
|
+
{{{tsFunctionParams}}}
|
|
231
|
+
): Promise<{{functionName}}Response>;
|
|
232
|
+
|
|
233
|
+
export default {{functionName}};
|
|
234
|
+
|
|
235
|
+
export declare namespace {{functionName}} {
|
|
236
|
+
export const actionName: string;
|
|
237
|
+
export const title: string;
|
|
238
|
+
export const app: string | null;
|
|
239
|
+
export const permissions: string[];
|
|
240
|
+
export const configKey: string;
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
const jsActionTemplate = Handlebars.compile(JS_ACTION_TEMPLATE);
|
|
244
|
+
const dtsActionTemplate = Handlebars.compile(TS_ACTION_DECLARATION_TEMPLATE);
|
|
245
|
+
// ================================================================================================
|
|
246
|
+
// SCHEMA TRANSLATION HELPERS
|
|
247
|
+
// ================================================================================================
|
|
248
|
+
function generateZodSchemaForProperty(prop) {
|
|
249
|
+
let zodString;
|
|
250
|
+
if (prop.choices && Object.keys(prop.choices).length > 0) {
|
|
251
|
+
// Extract just the keys (values) from the choices dict
|
|
252
|
+
const choiceValues = Object.keys(prop.choices);
|
|
253
|
+
zodString = `z.enum(${JSON.stringify(choiceValues)})`;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
switch (prop.type) {
|
|
257
|
+
case "string":
|
|
258
|
+
zodString = prop.max_digits
|
|
259
|
+
? 'z.string().regex(/^-?\\d+(\\.\\d+)?$/, "Must be a numeric string")'
|
|
260
|
+
: "z.string()";
|
|
261
|
+
break;
|
|
262
|
+
case "integer":
|
|
263
|
+
zodString = "z.number().int()";
|
|
264
|
+
break;
|
|
265
|
+
case "number":
|
|
266
|
+
zodString = "z.number()";
|
|
267
|
+
break;
|
|
268
|
+
case "boolean":
|
|
269
|
+
zodString = "z.boolean()";
|
|
270
|
+
break;
|
|
271
|
+
case "array":
|
|
272
|
+
const itemSchema = prop.items
|
|
273
|
+
? generateZodSchemaForProperty(prop.items)
|
|
274
|
+
: "z.any()";
|
|
275
|
+
zodString = `z.array(${itemSchema})`;
|
|
276
|
+
break;
|
|
277
|
+
case "object":
|
|
278
|
+
if (prop.properties) {
|
|
279
|
+
const nestedProps = Object.entries(prop.properties)
|
|
280
|
+
.map(([key, value]) => `${key}: ${generateZodSchemaForProperty(value)}`)
|
|
281
|
+
.join(", ");
|
|
282
|
+
zodString = `z.object({ ${nestedProps} })`;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
zodString = "z.record(z.any())";
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
default:
|
|
289
|
+
zodString = "z.any()";
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (prop.format) {
|
|
294
|
+
switch (prop.format) {
|
|
295
|
+
case "email":
|
|
296
|
+
zodString += ".email()";
|
|
297
|
+
break;
|
|
298
|
+
case "uri":
|
|
299
|
+
zodString += ".url()";
|
|
300
|
+
break;
|
|
301
|
+
case "uuid":
|
|
302
|
+
zodString += ".uuid()";
|
|
303
|
+
break;
|
|
304
|
+
case "date-time":
|
|
305
|
+
zodString +=
|
|
306
|
+
'.datetime({ message: "Invalid ISO 8601 datetime string" })';
|
|
307
|
+
break;
|
|
308
|
+
case "date":
|
|
309
|
+
zodString +=
|
|
310
|
+
'.regex(/^\\d{4}-\\d{2}-\\d{2}$/, "Must be in YYYY-MM-DD format")';
|
|
311
|
+
break;
|
|
312
|
+
case "time":
|
|
313
|
+
zodString +=
|
|
314
|
+
'.regex(/^\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$/, "Must be in HH:MM:SS format")';
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (prop.min_length != null)
|
|
319
|
+
zodString += `.min(${prop.min_length})`;
|
|
320
|
+
if (prop.max_length != null)
|
|
321
|
+
zodString += `.max(${prop.max_length})`;
|
|
322
|
+
if (prop.min_value != null)
|
|
323
|
+
zodString += `.min(${prop.min_value})`;
|
|
324
|
+
if (prop.max_value != null)
|
|
325
|
+
zodString += `.max(${prop.max_value})`;
|
|
326
|
+
if (prop.nullable)
|
|
327
|
+
zodString += ".nullable()";
|
|
328
|
+
if (!prop.required)
|
|
329
|
+
zodString += ".optional()";
|
|
330
|
+
if (prop.default !== undefined && prop.default !== null) {
|
|
331
|
+
zodString += `.default(${JSON.stringify(prop.default)})`;
|
|
332
|
+
}
|
|
333
|
+
return zodString;
|
|
334
|
+
}
|
|
335
|
+
function generateTsTypeForProperty(prop) {
|
|
336
|
+
if (prop.choices && Object.keys(prop.choices).length > 0) {
|
|
337
|
+
return Object.keys(prop.choices)
|
|
338
|
+
.map((c) => `'${String(c).replace(/'/g, "\\'")}'`)
|
|
339
|
+
.join(" | ");
|
|
340
|
+
}
|
|
341
|
+
let tsType;
|
|
342
|
+
switch (prop.type) {
|
|
343
|
+
case "string":
|
|
344
|
+
tsType = "string";
|
|
345
|
+
break;
|
|
346
|
+
case "integer":
|
|
347
|
+
case "number":
|
|
348
|
+
tsType = "number";
|
|
349
|
+
break;
|
|
350
|
+
case "boolean":
|
|
351
|
+
tsType = "boolean";
|
|
352
|
+
break;
|
|
353
|
+
case "array":
|
|
354
|
+
tsType = prop.items
|
|
355
|
+
? `Array<${generateTsTypeForProperty(prop.items)}>`
|
|
356
|
+
: "any[]";
|
|
357
|
+
break;
|
|
358
|
+
case "object":
|
|
359
|
+
tsType = "Record<string, any>";
|
|
360
|
+
break;
|
|
361
|
+
default:
|
|
362
|
+
tsType = "any";
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
if (prop.nullable) {
|
|
366
|
+
tsType = `${tsType} | null`;
|
|
367
|
+
}
|
|
368
|
+
return tsType;
|
|
369
|
+
}
|
|
370
|
+
// ================================================================================================
|
|
371
|
+
// DATA PREPARATION & FILE GENERATION
|
|
372
|
+
// ================================================================================================
|
|
373
|
+
function prepareActionTemplateData(modulePath, functionName, actionName, actionDefinition, configKey) {
|
|
374
|
+
const inputProps = actionDefinition.input_properties || {};
|
|
375
|
+
const responseProps = actionDefinition.response_properties || {};
|
|
376
|
+
const processProperties = (properties) => {
|
|
377
|
+
const propertyEntries = Object.entries(properties);
|
|
378
|
+
if (propertyEntries.length === 0)
|
|
379
|
+
return "";
|
|
380
|
+
const propertyStrings = propertyEntries
|
|
381
|
+
.map(([name, prop]) => ` ${name}: ${generateZodSchemaForProperty(prop)}`)
|
|
382
|
+
.join(",\n");
|
|
383
|
+
return `\n${propertyStrings}\n`;
|
|
384
|
+
};
|
|
385
|
+
// For TypeScript declarations, we need a different format
|
|
386
|
+
const processPropertiesForTS = (properties) => {
|
|
387
|
+
const propertyEntries = Object.entries(properties);
|
|
388
|
+
if (propertyEntries.length === 0)
|
|
389
|
+
return "";
|
|
390
|
+
const propertyStrings = propertyEntries
|
|
391
|
+
.map(([name, prop]) => `${name}: ${generateTsTypeForProperty(prop)}`)
|
|
392
|
+
.join(", ");
|
|
393
|
+
// Wrap in SafeString to prevent Handlebars from escaping quotes
|
|
394
|
+
return new Handlebars.SafeString(propertyStrings);
|
|
395
|
+
};
|
|
396
|
+
const inputSchemaString = processProperties(inputProps);
|
|
397
|
+
const responseSchemaString = processProperties(responseProps);
|
|
398
|
+
// Generate TypeScript type strings for the .d.ts file
|
|
399
|
+
const inputTsSchemaString = processPropertiesForTS(inputProps);
|
|
400
|
+
const responseTsSchemaString = processPropertiesForTS(responseProps);
|
|
401
|
+
const requiredParams = [], optionalParams = [];
|
|
402
|
+
Object.entries(inputProps).forEach(([name, prop]) => {
|
|
403
|
+
(prop.required ? requiredParams : optionalParams).push({ name, prop });
|
|
404
|
+
});
|
|
405
|
+
const allParams = [...requiredParams, ...optionalParams];
|
|
406
|
+
const jsParams = allParams.map(({ name, prop }) => !prop.required && prop.default !== undefined && prop.default !== null
|
|
407
|
+
? `${name} = ${JSON.stringify(prop.default)}`
|
|
408
|
+
: name);
|
|
409
|
+
jsParams.push("axiosOverrides = {}");
|
|
410
|
+
const tsParams = allParams.map(({ name, prop }) => {
|
|
411
|
+
const type = generateTsTypeForProperty(prop);
|
|
412
|
+
const optionalMarker = prop.required ? "" : "?";
|
|
413
|
+
return `${name}${optionalMarker}: ${type}`;
|
|
414
|
+
});
|
|
415
|
+
tsParams.push(`axiosOverrides?: AxiosRequestConfig`);
|
|
416
|
+
const tsDocParams = allParams.map(({ name, prop }) => ({
|
|
417
|
+
name,
|
|
418
|
+
type: generateTsTypeForProperty(prop),
|
|
419
|
+
description: prop.description || `The ${name} parameter.`,
|
|
420
|
+
}));
|
|
421
|
+
return {
|
|
422
|
+
modulePath,
|
|
423
|
+
functionName,
|
|
424
|
+
actionName,
|
|
425
|
+
title: actionDefinition.title || _.startCase(functionName),
|
|
426
|
+
app: actionDefinition.app,
|
|
427
|
+
docstring: actionDefinition.docstring,
|
|
428
|
+
permissions: actionDefinition.permissions || [],
|
|
429
|
+
configKey,
|
|
430
|
+
inputSchemaString: inputSchemaString ? inputSchemaString.trim() : null,
|
|
431
|
+
responseSchemaString: responseSchemaString
|
|
432
|
+
? responseSchemaString.trim()
|
|
433
|
+
: null,
|
|
434
|
+
inputTsSchemaString: inputTsSchemaString || null,
|
|
435
|
+
responseTsSchemaString: responseTsSchemaString || null,
|
|
436
|
+
jsFunctionParams: jsParams.join(", "),
|
|
437
|
+
tsFunctionParams: tsParams.join(",\n "),
|
|
438
|
+
payloadProperties: Object.keys(inputProps).length > 0
|
|
439
|
+
? Object.keys(inputProps).join(",\n ")
|
|
440
|
+
: null,
|
|
441
|
+
tsDocParams: tsDocParams,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
async function generateActionFile(backend, actionName, actionDefinition) {
|
|
445
|
+
const functionName = _.camelCase(actionName);
|
|
446
|
+
const modulePath = process.env.NODE_ENV === "test" ? "../../../../src" : "@statezero/core";
|
|
447
|
+
const appName = (actionDefinition.app || "general").toLowerCase();
|
|
448
|
+
// Always include the backend key as the top-level folder
|
|
449
|
+
const outDir = path.join(backend.GENERATED_ACTIONS_DIR, backend.NAME, appName);
|
|
450
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
451
|
+
const fileName = _.kebabCase(actionName);
|
|
452
|
+
const schemaFileName = `${fileName}.schema.json`;
|
|
453
|
+
const templateData = prepareActionTemplateData(modulePath, functionName, actionName, actionDefinition, backend.NAME);
|
|
454
|
+
templateData.schemaFileName = schemaFileName;
|
|
455
|
+
// Write the raw schema file (like models do)
|
|
456
|
+
const schemaFilePath = path.join(outDir, schemaFileName);
|
|
457
|
+
await fs.writeFile(schemaFilePath, JSON.stringify(actionDefinition, null, 2));
|
|
458
|
+
// Write JS and TS files
|
|
459
|
+
const jsFilePath = path.join(outDir, `${fileName}.js`);
|
|
460
|
+
await fs.writeFile(jsFilePath, jsActionTemplate(templateData));
|
|
461
|
+
const dtsFilePath = path.join(outDir, `${fileName}.d.ts`);
|
|
462
|
+
await fs.writeFile(dtsFilePath, dtsActionTemplate(templateData));
|
|
463
|
+
const relativePath = path
|
|
464
|
+
.relative(backend.GENERATED_ACTIONS_DIR, jsFilePath)
|
|
465
|
+
.replace(/\\/g, "/")
|
|
466
|
+
.replace(/\.js$/, "");
|
|
467
|
+
return {
|
|
468
|
+
action: actionName,
|
|
469
|
+
relativePath,
|
|
470
|
+
functionName,
|
|
471
|
+
backend: backend.NAME,
|
|
472
|
+
appName,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async function generateActionRegistry(generatedFiles, backendConfigs) {
|
|
476
|
+
const registryByBackend = {};
|
|
477
|
+
const schemasByBackend = {};
|
|
478
|
+
const allImports = new Set();
|
|
479
|
+
const allSchemaImports = new Set();
|
|
480
|
+
for (const file of generatedFiles) {
|
|
481
|
+
const backendKey = file.backend;
|
|
482
|
+
if (!backendKey)
|
|
483
|
+
continue;
|
|
484
|
+
registryByBackend[backendKey] = registryByBackend[backendKey] || {
|
|
485
|
+
actions: {},
|
|
486
|
+
};
|
|
487
|
+
schemasByBackend[backendKey] = schemasByBackend[backendKey] || {};
|
|
488
|
+
const functionName = file.functionName;
|
|
489
|
+
const actionsDir = backendConfigs[backendKey].GENERATED_ACTIONS_DIR;
|
|
490
|
+
const importPath = path
|
|
491
|
+
.relative(process.cwd(), path.join(actionsDir, `${file.relativePath}.js`))
|
|
492
|
+
.replace(/\\/g, "/");
|
|
493
|
+
// Generate action import
|
|
494
|
+
const importStatement = `import { ${functionName} } from './${importPath}';`;
|
|
495
|
+
allImports.add(importStatement);
|
|
496
|
+
registryByBackend[backendKey].actions[file.action] = functionName;
|
|
497
|
+
// Generate schema import
|
|
498
|
+
const schemaImportName = `${functionName}Schema`;
|
|
499
|
+
const schemaPath = `./${importPath.replace(/\.js$/, '')}.schema.json`;
|
|
500
|
+
const schemaImportStatement = `import ${schemaImportName} from '${schemaPath}' assert { type: 'json' };`;
|
|
501
|
+
allSchemaImports.add(schemaImportStatement);
|
|
502
|
+
schemasByBackend[backendKey][file.action] = schemaImportName;
|
|
503
|
+
}
|
|
504
|
+
let registryContent = `/**
|
|
505
|
+
* This file was auto-generated. Do not make direct changes to the file.
|
|
506
|
+
* It provides a registry of all generated actions.
|
|
507
|
+
*/
|
|
508
|
+
|
|
509
|
+
`;
|
|
510
|
+
// Add action imports
|
|
511
|
+
registryContent += Array.from(allImports).sort().join("\n") + "\n\n";
|
|
512
|
+
// Add schema imports
|
|
513
|
+
registryContent += Array.from(allSchemaImports).sort().join("\n") + "\n\n";
|
|
514
|
+
// Add ACTION_REGISTRY
|
|
515
|
+
registryContent += `export const ACTION_REGISTRY = {\n`;
|
|
516
|
+
Object.entries(registryByBackend).forEach(([backendKey, data], index, arr) => {
|
|
517
|
+
registryContent += ` '${backendKey}': {\n`;
|
|
518
|
+
const actionEntries = Object.entries(data.actions);
|
|
519
|
+
actionEntries.forEach(([actionName, funcName], idx, actionsArr) => {
|
|
520
|
+
registryContent += ` '${actionName}': ${funcName}${idx < actionsArr.length - 1 ? "," : ""}\n`;
|
|
521
|
+
});
|
|
522
|
+
registryContent += ` }${index < arr.length - 1 ? "," : ""}\n`;
|
|
523
|
+
});
|
|
524
|
+
registryContent += `};\n\n`;
|
|
525
|
+
// Add SCHEMA_REGISTRY
|
|
526
|
+
registryContent += `export const SCHEMA_REGISTRY = {\n`;
|
|
527
|
+
Object.entries(schemasByBackend).forEach(([backendKey, schemas], index, arr) => {
|
|
528
|
+
registryContent += ` '${backendKey}': {\n`;
|
|
529
|
+
const schemaEntries = Object.entries(schemas);
|
|
530
|
+
schemaEntries.forEach(([actionName, schemaName], idx, schemasArr) => {
|
|
531
|
+
registryContent += ` '${actionName}': ${schemaName}${idx < schemasArr.length - 1 ? "," : ""}\n`;
|
|
532
|
+
});
|
|
533
|
+
registryContent += ` }${index < arr.length - 1 ? "," : ""}\n`;
|
|
534
|
+
});
|
|
535
|
+
registryContent += `};\n\n`;
|
|
536
|
+
registryContent += `/**
|
|
537
|
+
* Get an action function by name and config key
|
|
538
|
+
* @param {string} actionName - The name of the action
|
|
539
|
+
* @param {string} configKey - The backend config key
|
|
540
|
+
* @returns {Function|null} The action function or null if not found
|
|
541
|
+
*/
|
|
542
|
+
export function getAction(actionName, configKey) {
|
|
543
|
+
const action = ACTION_REGISTRY[configKey]?.[actionName];
|
|
544
|
+
if (!action) {
|
|
545
|
+
console.warn(\`Action '\${actionName}' not found for config key '\${configKey}'.\`);
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
return action;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Get the full schema for an action
|
|
553
|
+
* @param {string} actionName - The name of the action
|
|
554
|
+
* @param {string} configKey - The backend config key
|
|
555
|
+
* @returns {Object|null} The action schema or null if not found
|
|
556
|
+
*/
|
|
557
|
+
export function getSchema(actionName, configKey) {
|
|
558
|
+
const schema = SCHEMA_REGISTRY[configKey]?.[actionName];
|
|
559
|
+
if (!schema) {
|
|
560
|
+
console.warn(\`Schema for action '\${actionName}' not found for config key '\${configKey}'.\`);
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
return schema;
|
|
564
|
+
}
|
|
565
|
+
`;
|
|
566
|
+
const registryFilePath = path.join(process.cwd(), "action-registry.js");
|
|
567
|
+
await fs.writeFile(registryFilePath, registryContent);
|
|
568
|
+
console.log(`\n✨ Generated action registry at ${registryFilePath}`);
|
|
569
|
+
}
|
|
570
|
+
async function generateAppLevelIndexFiles(generatedFiles, backendConfigs) {
|
|
571
|
+
const filesByBackend = _.groupBy(generatedFiles, "backend");
|
|
572
|
+
const indexTemplate = Handlebars.compile(`{{#each files}}
|
|
573
|
+
export * from '{{this.relativePath}}';
|
|
574
|
+
{{/each}}`);
|
|
575
|
+
for (const backendName in filesByBackend) {
|
|
576
|
+
const backendFiles = filesByBackend[backendName];
|
|
577
|
+
const backendConfig = backendConfigs[backendName];
|
|
578
|
+
const rootActionsDir = backendConfig.GENERATED_ACTIONS_DIR;
|
|
579
|
+
const rootExports = [];
|
|
580
|
+
const filesByApp = _.groupBy(backendFiles, "appName");
|
|
581
|
+
for (const appName in filesByApp) {
|
|
582
|
+
const appFiles = filesByApp[appName];
|
|
583
|
+
// Include backend key in the path
|
|
584
|
+
const appDir = path.join(rootActionsDir, backendName, appName);
|
|
585
|
+
const appIndexExports = appFiles.map((file) => {
|
|
586
|
+
const relativePathToAppDir = `./${path.basename(file.relativePath)}`;
|
|
587
|
+
return { ...file, relativePath: relativePathToAppDir };
|
|
588
|
+
});
|
|
589
|
+
const indexContent = indexTemplate({ files: appIndexExports }).trim();
|
|
590
|
+
await fs.writeFile(path.join(appDir, "index.js"), indexContent);
|
|
591
|
+
await fs.writeFile(path.join(appDir, "index.d.ts"), indexContent);
|
|
592
|
+
rootExports.push(`export * from './${appName}/index.js';`);
|
|
593
|
+
}
|
|
594
|
+
const rootIndexContent = rootExports.sort().join("\n");
|
|
595
|
+
// Include backend key in the path for root index files
|
|
596
|
+
const backendDir = path.join(rootActionsDir, backendName);
|
|
597
|
+
await fs.writeFile(path.join(backendDir, "index.js"), rootIndexContent);
|
|
598
|
+
await fs.writeFile(path.join(backendDir, "index.d.ts"), rootIndexContent);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Generates a top-level index file that re-exports from the 'default' backend
|
|
603
|
+
* if it exists. This improves backwards compatibility and reduces verbosity.
|
|
604
|
+
* @param {Object.<string, BackendConfig>} backendConfigs
|
|
605
|
+
* @returns {Promise<void>}
|
|
606
|
+
*/
|
|
607
|
+
async function generateDefaultBackendIndex(backendConfigs) {
|
|
608
|
+
// Check if a 'default' backend exists
|
|
609
|
+
if (!backendConfigs['default']) {
|
|
610
|
+
return; // No default backend, skip
|
|
611
|
+
}
|
|
612
|
+
const defaultBackend = backendConfigs['default'];
|
|
613
|
+
const actionsDir = defaultBackend.GENERATED_ACTIONS_DIR;
|
|
614
|
+
// Create top-level index files that re-export from default
|
|
615
|
+
const indexContent = `/**
|
|
616
|
+
* This file was auto-generated. Do not make direct changes to the file.
|
|
617
|
+
* Re-exports from the 'default' backend for backwards compatibility.
|
|
618
|
+
*/
|
|
619
|
+
|
|
620
|
+
export * from './default/index.js';
|
|
621
|
+
`;
|
|
622
|
+
const jsIndexPath = path.join(actionsDir, 'index.js');
|
|
623
|
+
const dtsIndexPath = path.join(actionsDir, 'index.d.ts');
|
|
624
|
+
await fs.writeFile(jsIndexPath, indexContent);
|
|
625
|
+
await fs.writeFile(dtsIndexPath, indexContent);
|
|
626
|
+
console.log(`✨ Generated top-level index for 'default' backend`);
|
|
627
|
+
}
|
|
628
|
+
// ================================================================================================
|
|
629
|
+
// MAIN SCRIPT RUNNER
|
|
630
|
+
// ================================================================================================
|
|
631
|
+
async function main() {
|
|
632
|
+
loadConfigFromFile();
|
|
633
|
+
const configData = configInstance.getConfig();
|
|
634
|
+
const backendConfigs = configData.backendConfigs;
|
|
635
|
+
for (const [key, backend] of Object.entries(backendConfigs)) {
|
|
636
|
+
if (!backend.GENERATED_ACTIONS_DIR) {
|
|
637
|
+
console.error(`❌ Backend '${key}' is missing the GENERATED_ACTIONS_DIR configuration.`);
|
|
638
|
+
process.exit(1);
|
|
639
|
+
}
|
|
640
|
+
backend.NAME = key;
|
|
641
|
+
}
|
|
642
|
+
console.log("Fetching action schemas from backends...");
|
|
643
|
+
const fetchPromises = Object.values(backendConfigs).map(async (backend) => {
|
|
644
|
+
try {
|
|
645
|
+
const headers = backend.SYNC_TOKEN ? { "X-Sync-Token": backend.SYNC_TOKEN } : {};
|
|
646
|
+
const response = await axios.get(`${backend.API_URL}/actions-schema/`, { headers });
|
|
647
|
+
return { backend, actions: response.data.actions || {} };
|
|
648
|
+
}
|
|
649
|
+
catch (error) {
|
|
650
|
+
console.error(`❌ Error fetching actions from ${backend.NAME}: ${error.message}`);
|
|
651
|
+
return { backend, actions: {} };
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
const backendActions = await Promise.all(fetchPromises);
|
|
655
|
+
const choices = [];
|
|
656
|
+
for (const { backend, actions } of backendActions) {
|
|
657
|
+
const actionNames = Object.keys(actions);
|
|
658
|
+
if (actionNames.length > 0) {
|
|
659
|
+
choices.push({ name: `\n=== ${backend.NAME} ===\n`, disabled: true });
|
|
660
|
+
for (const actionName of actionNames.sort()) {
|
|
661
|
+
const definition = actions[actionName];
|
|
662
|
+
choices.push({
|
|
663
|
+
name: ` ${definition.title || _.startCase(actionName)}`,
|
|
664
|
+
value: { backend, action: actionName, definition },
|
|
665
|
+
checked: true,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (choices.length === 0) {
|
|
671
|
+
console.log("No actions found to synchronize.");
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
const selectedActions = await selectActions(choices, "Select actions to generate:");
|
|
675
|
+
if (!selectedActions || selectedActions.length === 0) {
|
|
676
|
+
console.log("No actions selected. Exiting.");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
console.log("\n⚙️ Generating actions...");
|
|
680
|
+
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
|
|
681
|
+
progressBar.start(selectedActions.length, 0);
|
|
682
|
+
const allGeneratedFiles = [];
|
|
683
|
+
for (const item of selectedActions) {
|
|
684
|
+
try {
|
|
685
|
+
const result = await generateActionFile(item.backend, item.action, item.definition);
|
|
686
|
+
allGeneratedFiles.push(result);
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
progressBar.stop();
|
|
690
|
+
console.error(`\n❌ Error generating action ${item.action}: ${error.message}`);
|
|
691
|
+
}
|
|
692
|
+
progressBar.increment();
|
|
693
|
+
}
|
|
694
|
+
progressBar.stop();
|
|
695
|
+
await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
|
|
696
|
+
await generateActionRegistry(allGeneratedFiles, backendConfigs);
|
|
697
|
+
// Generate top-level index for 'default' backend if it exists
|
|
698
|
+
await generateDefaultBackendIndex(backendConfigs);
|
|
699
|
+
console.log(`\n✨ Generated ${allGeneratedFiles.length} actions successfully.`);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* CLI entry point.
|
|
703
|
+
*/
|
|
704
|
+
export async function generateActions() {
|
|
705
|
+
try {
|
|
706
|
+
console.log("🚀 Starting action synchronization...");
|
|
707
|
+
await main();
|
|
708
|
+
console.log("\n✅ Action synchronization completed!");
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
console.error("\n❌ Action synchronization failed:", error.message);
|
|
712
|
+
if (process.env.DEBUG) {
|
|
713
|
+
console.error("Stack trace:", error.stack);
|
|
714
|
+
}
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
}
|