biz-a-cli 2.3.78 → 2.3.79-15211

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.
@@ -1,5 +1,12 @@
1
- import { queryData, save as dbSave, deleteMD } from "../../db/db.js";
1
+ import {
2
+ queryData,
3
+ save as dbSave,
4
+ deleteMD,
5
+ getGlobalConfig,
6
+ } from "../../db/db.js";
2
7
  import vm from "node:vm";
8
+ import { execCLIScriptWorker } from "../../worker/cliWorkerPool.js";
9
+ import axios from "axios";
3
10
 
4
11
  // ================
5
12
  // INTERNAL HELPERS
@@ -243,7 +250,13 @@ export async function initiate(payload) {
243
250
  }
244
251
 
245
252
  export async function availableTransitions(payload) {
246
- const { instanceId, userRoles = [], entityData = {}, apiConfig } = payload;
253
+ const {
254
+ instanceId,
255
+ userRoles = [],
256
+ entityData = {},
257
+ triggerType = "USER_EVENT",
258
+ apiConfig,
259
+ } = payload;
247
260
 
248
261
  const instParam = {
249
262
  length: 1,
@@ -311,6 +324,12 @@ export async function availableTransitions(payload) {
311
324
  operator: "=",
312
325
  value1: `'${instance.current_state}'`,
313
326
  },
327
+ {
328
+ junction: "AND",
329
+ column: "SYS$BPM_TRANS_DEF.TRIGGER_TYPE",
330
+ operator: "=",
331
+ value1: `'${triggerType}'`,
332
+ },
314
333
  ],
315
334
  };
316
335
 
@@ -461,6 +480,10 @@ export async function executeTransition(payload) {
461
480
  key: "effect_type",
462
481
  },
463
482
  { data: "SYS$BPM_TRANS_EFFECT_DEF.EVENT_NAME", key: "event_name" },
483
+ {
484
+ data: "SYS$BPM_TRANS_EFFECT_DEF.PAYLOAD_TEMPLATE",
485
+ key: "payload_template",
486
+ },
464
487
  ],
465
488
  filter: [
466
489
  {
@@ -583,7 +606,7 @@ export async function executeTransition(payload) {
583
606
  from_state_code: transition.from_state_code,
584
607
  to_state_code: transition.to_state_code,
585
608
  sys$bpm_trans_def_id: transition.id,
586
- trigger_type: "MANUAL",
609
+ trigger_type: "USER_EVENT",
587
610
  actor_type: "USER",
588
611
  actor_id: userId,
589
612
  request_id: `TRANS_${transition.transition_code}_${instanceId}_${new Date().getTime()}`,
@@ -597,6 +620,7 @@ export async function executeTransition(payload) {
597
620
  sys$bpm_trans_def_id: transition.id,
598
621
  effect_type: eff.effect_type,
599
622
  event_name: eff.event_name,
623
+ payload_evaluated: eff.payload_template || null,
600
624
  status: "PENDING",
601
625
  retry_count: 0,
602
626
  }));
@@ -691,7 +715,7 @@ export async function terminate(payload) {
691
715
  sys$bpm_wf_instance_id: Number(instanceId),
692
716
  from_state_code: currentStateCode,
693
717
  to_state_code: currentStateCode,
694
- trigger_type: "MANUAL",
718
+ trigger_type: "USER_EVENT",
695
719
  actor_type: "USER",
696
720
  actor_id: userId || null,
697
721
  request_id: `TERMINATE_${instanceId}_${new Date().getTime()}`,
@@ -779,10 +803,6 @@ async function fetchInstanceTasksAndTimers(payload) {
779
803
  length: -1,
780
804
  columns: [
781
805
  { data: "SYS$BPM_TASK.ID", key: "id" },
782
- {
783
- data: "SYS$BPM_TASK.SYS$BPM_WF_INSTANCE_ID",
784
- key: "sys$bpm_wf_instance_id",
785
- },
786
806
  { data: "SYS$BPM_TASK.STATE_CODE", key: "state_code" },
787
807
  { data: "SYS$BPM_TASK.NAME", key: "name" },
788
808
  { data: "SYS$BPM_TASK.STATUS", key: "status" },
@@ -822,10 +842,6 @@ async function fetchInstanceTasksAndTimers(payload) {
822
842
  length: -1,
823
843
  columns: [
824
844
  { data: "SYS$BPM_TIMER_JOB.ID", key: "id" },
825
- {
826
- data: "SYS$BPM_TIMER_JOB.SYS$BPM_WF_INSTANCE_ID",
827
- key: "sys$bpm_wf_instance_id",
828
- },
829
845
  {
830
846
  data: "SYS$BPM_TIMER_JOB.SYS$BPM_TASK_ID",
831
847
  key: "sys$bpm_task_id",
@@ -950,7 +966,7 @@ export async function instanceLogs(payload) {
950
966
  value1: String(instanceId),
951
967
  },
952
968
  ],
953
- orders: [{ column: "SYS$BPM_STATE_HISTORY.CREATED_AT", dir: "DESC" }],
969
+ order: [{ column: "SYS$BPM_STATE_HISTORY.CREATED_AT", dir: "ASC" }],
954
970
  };
955
971
  return await queryData(historyQuery, apiConfig);
956
972
  }
@@ -1009,7 +1025,7 @@ export async function instanceTasks(payload) {
1009
1025
  { data: "SYS$BPM_TASK.CLOSED_REASON", key: "closed_reason" },
1010
1026
  ],
1011
1027
  filter: filters,
1012
- orders: [{ column: "SYS$BPM_TASK.CREATED_AT", dir: "DESC" }],
1028
+ order: [{ column: "SYS$BPM_TASK.CREATED_AT", dir: "ASC" }],
1013
1029
  };
1014
1030
  return await queryData(taskQuery, apiConfig);
1015
1031
  }
@@ -1093,7 +1109,7 @@ export async function instanceEffectLogs(payload) {
1093
1109
  value1: String(instanceId),
1094
1110
  },
1095
1111
  ],
1096
- orders: [{ column: "SYS$BPM_EFFECT_LOG.CREATED_AT", dir: "DESC" }],
1112
+ order: [{ column: "SYS$BPM_EFFECT_LOG.CREATED_AT", dir: "ASC" }],
1097
1113
  };
1098
1114
  return await queryData(effectQuery, apiConfig);
1099
1115
  }
@@ -1105,6 +1121,7 @@ export async function retryEffect(payload) {
1105
1121
  length: -1,
1106
1122
  columns: [
1107
1123
  { data: "SYS$BPM_EFFECT_LOG.ID", key: "id" },
1124
+ { data: "SYS$BPM_EFFECT_LOG.STATUS", key: "status" },
1108
1125
  { data: "SYS$BPM_EFFECT_LOG.RETRY_COUNT", key: "retry_count" },
1109
1126
  ],
1110
1127
  filter: [
@@ -1116,21 +1133,376 @@ export async function retryEffect(payload) {
1116
1133
  },
1117
1134
  ],
1118
1135
  };
1119
- const effectRes = await queryData(effectQuery, apiConfig);
1120
1136
 
1121
- if (!effectRes.data || effectRes.data.length === 0) {
1122
- throw new Error("Effect not found.");
1137
+ const effectRes = await queryData(effectQuery, apiConfig);
1138
+ if (!effectRes || effectRes.length !== 1) {
1139
+ throw new Error("Effect not found or found duplicate effects");
1123
1140
  }
1124
1141
 
1125
- const currentRetry = parseInt(effectRes.data[0].retry_count, 10) || 0;
1142
+ if (effectRes[0].status !== "FAILED") {
1143
+ throw new Error("Action Denied: You can only retry FAILED effects.");
1144
+ }
1126
1145
 
1127
1146
  const dbModel = {
1128
1147
  id: effectId,
1129
- status: "PENDING",
1130
- retry_count: currentRetry + 1,
1148
+ status: "RETRYING",
1131
1149
  error_message: "",
1132
1150
  };
1133
1151
 
1134
1152
  const result = await dbSave({ SYS$BPM_EFFECT_LOG: dbModel });
1135
1153
  return result.data;
1136
1154
  }
1155
+
1156
+ // ==========================================
1157
+ // BACKGROUND JOB EXECUTION BOUNDARIES
1158
+ // ==========================================
1159
+
1160
+ export async function executeBackgroundTransition(payload, triggerType) {
1161
+ const { instanceId, transitionCode, entityData = {}, apiConfig } = payload;
1162
+
1163
+ // 1. Reuse availableTransitions to automatically enforce Guard Rules and Trigger Types!
1164
+ const validTransitions = await availableTransitions({
1165
+ instanceId: Number(instanceId),
1166
+ userRoles: ["SYSTEM"],
1167
+ entityData: entityData,
1168
+ apiConfig: apiConfig,
1169
+ triggerType: triggerType, // Dynamic: "SYSTEM_EVENT" or "EXTERNAL_EVENT"
1170
+ });
1171
+
1172
+ // 2. Find the exact matching transition
1173
+ const target = validTransitions.find(
1174
+ (t) =>
1175
+ String(t.transition_code).toUpperCase() ===
1176
+ String(transitionCode).toUpperCase(),
1177
+ );
1178
+
1179
+ if (!target) {
1180
+ throw new Error(
1181
+ `Background Transition '${transitionCode}' failed. It is invalid, blocked by Guards, or is not a ${triggerType}.`,
1182
+ );
1183
+ }
1184
+
1185
+ // 3. Execute natively
1186
+ await executeTransition({
1187
+ instanceId: Number(instanceId),
1188
+ transition: target,
1189
+ userRoles: ["SYSTEM"],
1190
+ entityData: entityData,
1191
+ apiConfig: apiConfig,
1192
+ userId: 0,
1193
+ });
1194
+ }
1195
+
1196
+ /**
1197
+ * Executes the pure business logic for a BPM Effect.
1198
+ * @param {Object} effectData - The raw row data from SYS$BPM_EFFECT_LOG
1199
+ * @returns {Object} { success: boolean, message?: string, error?: string }
1200
+ */
1201
+ export async function executeEffect(effectData) {
1202
+ const effectType = String(
1203
+ effectData.effect_type || effectData.EFFECT_TYPE,
1204
+ ).toUpperCase();
1205
+ const eventName = effectData.event_name || effectData.EVENT_NAME;
1206
+
1207
+ console.log(
1208
+ `[BPM Engine] Executing Effect [${effectType}] for Instance ${effectData.sys$bpm_wf_instance_id || effectData.SYS$BPM_WF_INSTANCE_ID}`,
1209
+ );
1210
+
1211
+ try {
1212
+ const rawPayload =
1213
+ effectData.payload_evaluated || effectData.PAYLOAD_EVALUATED;
1214
+ let parsedPayload = {};
1215
+ if (rawPayload) {
1216
+ try {
1217
+ parsedPayload =
1218
+ typeof rawPayload === "string"
1219
+ ? JSON.parse(rawPayload)
1220
+ : rawPayload;
1221
+ } catch (e) {
1222
+ parsedPayload = { rawData: rawPayload };
1223
+ }
1224
+ }
1225
+
1226
+ const apiConfig = getGlobalConfig();
1227
+
1228
+ if (effectType === "CLI_SCRIPT") {
1229
+ console.log(`[BPM Engine] Bridging Script: ${eventName}`);
1230
+ const scriptResult = await execCLIScriptWorker(
1231
+ apiConfig,
1232
+ eventName,
1233
+ parsedPayload,
1234
+ );
1235
+
1236
+ // --- if cliScript of Effect need to run executeTransition ---
1237
+ if (scriptResult && scriptResult.executeTransition) {
1238
+ const targetCode = scriptResult.executeTransition;
1239
+ const instanceId = Number(
1240
+ effectData.sys$bpm_wf_instance_id ||
1241
+ effectData.SYS$BPM_WF_INSTANCE_ID,
1242
+ );
1243
+ delete scriptResult.executeTransition;
1244
+
1245
+ await executeBackgroundTransition(
1246
+ {
1247
+ instanceId: instanceId,
1248
+ transitionCode: targetCode,
1249
+ entityData: parsedPayload || {},
1250
+ apiConfig: apiConfig,
1251
+ },
1252
+ "SYSTEM_EVENT",
1253
+ );
1254
+ }
1255
+
1256
+ return {
1257
+ success: true,
1258
+ message: `Effect executed successfully.`,
1259
+ data: scriptResult,
1260
+ };
1261
+ } else if (effectType === "HTTP") {
1262
+ console.log(
1263
+ `[BPM Engine] Executing HTTP Request: ${parsedPayload.url}`,
1264
+ );
1265
+ const response = await axios({
1266
+ method: parsedPayload.method || "POST",
1267
+ url: parsedPayload.url,
1268
+ data: parsedPayload.data || {},
1269
+ headers: parsedPayload.headers || {},
1270
+ });
1271
+ if (response.status >= 400) {
1272
+ throw new Error(
1273
+ `HTTP Error: ${response.status} - ${response.statusText}`,
1274
+ );
1275
+ }
1276
+ } else if (effectType === "WEBHOOK") {
1277
+ console.log(
1278
+ `[BPM Engine] Dispatching Webhook: ${parsedPayload.url}`,
1279
+ );
1280
+ axios({
1281
+ method: parsedPayload.method || "POST",
1282
+ url: parsedPayload.url,
1283
+ data: parsedPayload.data || {},
1284
+ headers: parsedPayload.headers || {},
1285
+ }).catch((err) => {
1286
+ console.log(
1287
+ `[BPM Engine] Webhook Silent Failure (${eventName}): ${err.message}`,
1288
+ );
1289
+ });
1290
+ } else {
1291
+ throw new Error(`Unrecognized EFFECT_TYPE: ${effectType}`);
1292
+ }
1293
+
1294
+ return { success: true, message: `Effect executed successfully.` };
1295
+ } catch (error) {
1296
+ return {
1297
+ success: false,
1298
+ error: error.message || "Unknown execution error",
1299
+ };
1300
+ }
1301
+ }
1302
+
1303
+ /**
1304
+ * Executes the pure business logic for a BPM Timer / SLA breach.
1305
+ * @param {Object} timerData - The raw row data from SYS$BPM_TIMER_JOB
1306
+ * @returns {Object} { success: boolean, message?: string, error?: string }
1307
+ */
1308
+ export async function executeTimerJob(timerData) {
1309
+ console.log(
1310
+ `[BPM Engine] Firing Timer [${timerData.timer_type || timerData.TIMER_TYPE}] for Instance ${timerData.sys$bpm_wf_instance_id || timerData.SYS$BPM_WF_INSTANCE_ID}`,
1311
+ );
1312
+
1313
+ try {
1314
+ const type = String(
1315
+ timerData.timer_type || timerData.TIMER_TYPE,
1316
+ ).toUpperCase();
1317
+ const instanceId =
1318
+ timerData.sys$bpm_wf_instance_id ||
1319
+ timerData.SYS$BPM_WF_INSTANCE_ID;
1320
+ const stateCode = timerData.state_code || timerData.STATE_CODE;
1321
+ const apiConfig = getGlobalConfig();
1322
+
1323
+ if (type === "SLA_TIMEOUT") {
1324
+ // 1. Look up Workflow Def ID
1325
+ const instRes = await queryData(
1326
+ {
1327
+ length: 1,
1328
+ columns: [
1329
+ {
1330
+ data: "SYS$BPM_WF_INSTANCE.SYS$BPM_WORKFLOW_DEF_ID",
1331
+ key: "def_id",
1332
+ },
1333
+ ],
1334
+ filter: [
1335
+ {
1336
+ junction: "",
1337
+ column: "SYS$BPM_WF_INSTANCE.ID",
1338
+ operator: "=",
1339
+ value1: String(instanceId),
1340
+ },
1341
+ ],
1342
+ },
1343
+ apiConfig,
1344
+ );
1345
+
1346
+ if (!instRes || instRes.length === 0)
1347
+ throw new Error("Workflow Instance not found.");
1348
+
1349
+ // 2. Look up State Configuration
1350
+ const stateRes = await queryData(
1351
+ {
1352
+ length: 1,
1353
+ columns: [
1354
+ {
1355
+ data: "SYS$BPM_STATE_DEF.TIMEOUT_ACTION_TYPE",
1356
+ key: "timeout_action_type",
1357
+ },
1358
+ {
1359
+ data: "SYS$BPM_STATE_DEF.TIMEOUT_EVENT_NAME",
1360
+ key: "timeout_event_name",
1361
+ },
1362
+ ],
1363
+ filter: [
1364
+ {
1365
+ junction: "",
1366
+ column: "SYS$BPM_STATE_DEF.SYS$BPM_WORKFLOW_DEF_ID",
1367
+ operator: "=",
1368
+ value1: String(instRes[0].def_id),
1369
+ },
1370
+ {
1371
+ junction: "AND",
1372
+ column: "SYS$BPM_STATE_DEF.STATE_CODE",
1373
+ operator: "=",
1374
+ value1: `'${stateCode}'`,
1375
+ },
1376
+ ],
1377
+ },
1378
+ apiConfig,
1379
+ );
1380
+
1381
+ if (stateRes && stateRes.length > 0) {
1382
+ const actionType = String(
1383
+ stateRes[0].timeout_action_type || "",
1384
+ ).toUpperCase();
1385
+ const eventName = stateRes[0].timeout_event_name;
1386
+
1387
+ if (actionType === "EMIT EVENT" && eventName) {
1388
+ const generatedPayload = JSON.stringify({
1389
+ instanceId: Number(instanceId),
1390
+ });
1391
+ await dbSave(
1392
+ {
1393
+ SYS$BPM_EFFECT_LOG: {
1394
+ id: null,
1395
+ sys$bpm_wf_instance_id: Number(instanceId),
1396
+ effect_type: "CLI_SCRIPT",
1397
+ event_name: eventName,
1398
+ payload_evaluated: generatedPayload, // INJECT IT HERE
1399
+ status: "PENDING",
1400
+ retry_count: 0,
1401
+ },
1402
+ },
1403
+ apiConfig,
1404
+ );
1405
+ } else if (actionType === "AUTO TRANSITION") {
1406
+ const transParam = {
1407
+ length: -1,
1408
+ columns: [
1409
+ { data: "SYS$BPM_TRANS_DEF.ID", key: "id" },
1410
+ {
1411
+ data: "SYS$BPM_TRANS_DEF.TRANSITION_CODE",
1412
+ key: "transition_code",
1413
+ },
1414
+ {
1415
+ data: "SYS$BPM_TRANS_DEF.FROM_STATE_CODE",
1416
+ key: "from_state_code",
1417
+ },
1418
+ {
1419
+ data: "SYS$BPM_TRANS_DEF.TO_STATE_CODE",
1420
+ key: "to_state_code",
1421
+ },
1422
+ {
1423
+ data: "SYS$BPM_TRANS_DEF.GUARD_TYPE",
1424
+ key: "guard_type",
1425
+ },
1426
+ {
1427
+ data: "SYS$BPM_TRANS_DEF.GUARD_RULE_KEY",
1428
+ key: "guard_rule_key",
1429
+ },
1430
+ ],
1431
+ filter: [
1432
+ {
1433
+ junction: "",
1434
+ column: "SYS$BPM_TRANS_DEF.SYS$BPM_WORKFLOW_DEF_ID",
1435
+ operator: "=",
1436
+ value1: String(instRes[0].def_id),
1437
+ },
1438
+ {
1439
+ junction: "AND",
1440
+ column: "SYS$BPM_TRANS_DEF.FROM_STATE_CODE",
1441
+ operator: "=",
1442
+ value1: `'${stateCode}'`,
1443
+ },
1444
+ {
1445
+ junction: "AND",
1446
+ column: "SYS$BPM_TRANS_DEF.TRIGGER_TYPE",
1447
+ operator: "=",
1448
+ value1: "'TIMER_EVENT'",
1449
+ },
1450
+ ],
1451
+ };
1452
+
1453
+ const timerTransitions = await queryData(
1454
+ transParam,
1455
+ apiConfig,
1456
+ );
1457
+ if (!timerTransitions || timerTransitions.length === 0) {
1458
+ throw new Error(
1459
+ `SLA Breached but no TIMER_EVENT transition found for state ${stateCode}`,
1460
+ );
1461
+ }
1462
+
1463
+ let validTransition = null;
1464
+ for (const t of timerTransitions) {
1465
+ // Assuming empty entityData for timer execution; Domain updates should happen prior to timer trigger
1466
+ const guard = await evaluateGuardRule(
1467
+ t.guard_type,
1468
+ t.guard_rule_key,
1469
+ {},
1470
+ );
1471
+ if (guard.isPassed) {
1472
+ if (validTransition)
1473
+ throw new Error(
1474
+ "Ambiguous timer flow: Multiple valid TIMER_EVENT paths.",
1475
+ );
1476
+ validTransition = t;
1477
+ }
1478
+ }
1479
+
1480
+ if (!validTransition) {
1481
+ throw new Error(
1482
+ "SLA Breached but no TIMER_EVENT transition passed the guard rules.",
1483
+ );
1484
+ }
1485
+
1486
+ await executeTransition({
1487
+ instanceId: Number(instanceId),
1488
+ transition: validTransition,
1489
+ userRoles: ["SYSTEM"],
1490
+ entityData: {},
1491
+ apiConfig: apiConfig,
1492
+ userId: null,
1493
+ });
1494
+ }
1495
+ }
1496
+ }
1497
+
1498
+ return {
1499
+ success: true,
1500
+ message: `Timer ${timerData.id || timerData.ID} fired successfully.`,
1501
+ };
1502
+ } catch (error) {
1503
+ return {
1504
+ success: false,
1505
+ error: error.message || "Unknown timer execution error",
1506
+ };
1507
+ }
1508
+ }
@@ -144,7 +144,7 @@ export async function publish(payload) {
144
144
  `${trans.fromStateCode}_TO_${trans.toStateCode}_${index}`,
145
145
  from_state_code: trans.fromStateCode,
146
146
  to_state_code: trans.toStateCode,
147
- trigger_type: trans.triggerType || "MANUAL",
147
+ trigger_type: trans.triggerType || "USER_EVENT",
148
148
  label: trans.label,
149
149
  guard_type: trans.guardType || null,
150
150
  guard_rule_key: trans.guardRuleKey || null,
@@ -0,0 +1,74 @@
1
+ export const publishApp = async (data, argv = {}) => {
2
+ try {
3
+ process.env.BIZA_APP_SKIP_PARSE = "1";
4
+ const { addApp } = await import("../../bin/app.js");
5
+
6
+ const sourcePayload = Object.prototype.hasOwnProperty.call(
7
+ data || {},
8
+ "body",
9
+ )
10
+ ? data.body
11
+ : data?.data;
12
+ const requestBody =
13
+ typeof sourcePayload === "string"
14
+ ? JSON.parse(sourcePayload || "{}")
15
+ : sourcePayload || {};
16
+ const hasTransportEnvelope =
17
+ requestBody &&
18
+ typeof requestBody === "object" &&
19
+ typeof requestBody.body === "object" &&
20
+ (Object.prototype.hasOwnProperty.call(requestBody, "method") ||
21
+ Object.prototype.hasOwnProperty.call(requestBody, "query") ||
22
+ Object.prototype.hasOwnProperty.call(requestBody, "headers"));
23
+ const unwrappedPayload = hasTransportEnvelope
24
+ ? requestBody.body
25
+ : requestBody;
26
+ const publishPayload =
27
+ unwrappedPayload &&
28
+ typeof unwrappedPayload === "object" &&
29
+ unwrappedPayload.addApp &&
30
+ typeof unwrappedPayload.addApp === "object"
31
+ ? unwrappedPayload.addApp
32
+ : unwrappedPayload;
33
+ const params =
34
+ publishPayload &&
35
+ typeof publishPayload === "object" &&
36
+ typeof publishPayload.options === "object"
37
+ ? publishPayload.options
38
+ : publishPayload && typeof publishPayload === "object"
39
+ ? publishPayload
40
+ : {};
41
+ const toNumber = (value, fallback) => {
42
+ const parsed = Number(value);
43
+ return Number.isFinite(parsed) ? parsed : fallback;
44
+ };
45
+
46
+ const response = await addApp({
47
+ workingDir: params.workingDir || process.cwd(),
48
+ verbose: !!params.verbose,
49
+ server: argv.server || params.server || "http://localhost",
50
+ apiPort: toNumber(argv.port, toNumber(params.apiPort, 212)),
51
+ dbIndex: toNumber(argv.dbindex, toNumber(params.dbIndex, 2)),
52
+ sub:
53
+ argv.subdomain ||
54
+ params.sub ||
55
+ params.subdomain ||
56
+ data?.domainId,
57
+ files: params.files || params.fileList,
58
+ body: publishPayload,
59
+ });
60
+
61
+ return response;
62
+ } catch (error) {
63
+ return {
64
+ success: false,
65
+ error: error?.message || error,
66
+ };
67
+ } finally {
68
+ delete process.env.BIZA_APP_SKIP_PARSE;
69
+ }
70
+ };
71
+
72
+ export function publish(payload, argv = {}) {
73
+ return publishApp(payload, argv);
74
+ }
@@ -61,7 +61,7 @@ BEGIN
61
61
  TRANSITION_CODE VARCHAR(100) NOT NULL,
62
62
  FROM_STATE_CODE VARCHAR(100) NOT NULL,
63
63
  TO_STATE_CODE VARCHAR(100) NOT NULL,
64
- TRIGGER_TYPE VARCHAR(30) DEFAULT ''MANUAL'' NOT NULL,
64
+ TRIGGER_TYPE VARCHAR(30) DEFAULT ''USER_EVENT'' NOT NULL,
65
65
  LABEL VARCHAR(100),
66
66
  GUARD_TYPE VARCHAR(30),
67
67
  GUARD_RULE_KEY VARCHAR(100),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biz-a-cli",
3
- "version": "2.3.78",
3
+ "version": "2.3.79-15211",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",
package/readme.md CHANGED
@@ -82,4 +82,29 @@
82
82
  hub --server [BizA Hub Server] --sub [subdomain] --hostname [ip_API] --port [port_API] --publish [true]
83
83
 
84
84
  Example :
85
- hub --server https://server.biz-a.id --sub imamatek --hostname localhost --port 212 --publish
85
+ hub --server https://server.biz-a.id --sub imamatek --hostname localhost --port 212 --publish
86
+
87
+
88
+ ## IV. Scalability
89
+
90
+ ### a. Monolithic Mode (Default)
91
+ hub --mode monolithic --server [BizA Hub Server] --sub [subdomain]
92
+
93
+ Example :
94
+ hub --server https://server.biz-a.id --sub imamatek
95
+
96
+ ### b. Dispatcher Mode (Load Balancer & ESB)
97
+ hub --mode dispatcher --server [BizA Hub Server] --sub [subdomain]
98
+
99
+ Example :
100
+ hub --mode dispatcher --server https://server.biz-a.id --sub imamatek
101
+
102
+ ### c. Multiple Agent Mode (Worker Nodes)
103
+ hub --mode agent --dispatcherUrl [Dispatcher URL] --services [Services] --sp [Port]
104
+
105
+ Example :
106
+ hub --mode agent --dispatcherUrl http://localhost:3000 --services BPM,EMAIL --sp 3001
107
+ hub --mode agent --dispatcherUrl http://localhost:3000 --services BPM,EMAIL --sp 3002
108
+
109
+ > [!TIP]
110
+ > When running multiple Agents on the same server, always define unique ports using --sp to avoid collisions.
@@ -176,7 +176,11 @@ export async function setLibrary(config, libraries, defaultLib) {
176
176
  return libs;
177
177
  }
178
178
 
179
- export async function extractFunctionScript(selectedConfig, data) {
179
+ export async function extractFunctionScript(
180
+ selectedConfig,
181
+ data,
182
+ extraLib = {},
183
+ ) {
180
184
  const config = getConfig(selectedConfig);
181
185
 
182
186
  if (data.error) {
@@ -197,6 +201,7 @@ export async function extractFunctionScript(selectedConfig, data) {
197
201
  crypto: crypto,
198
202
  log: logger,
199
203
  data: { sendModel, queryData, crudData, genId, getUrlAndParam },
204
+ ...extraLib,
200
205
  };
201
206
 
202
207
  if (scriptFn().functions.useLibrary) {