biz-a-cli 2.3.78 → 2.3.79-15208

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/bin/hub.js CHANGED
@@ -21,6 +21,9 @@ import {
21
21
  } from "./directHubEvent.js";
22
22
  import { env } from "../envs/env.js";
23
23
  import { setGlobalConfig } from "../db/ds.js";
24
+ import { CLI_Agent } from "../message-broker/agent.js";
25
+ import { initBpmAgent } from "../engine/bpm/bpm-agent.js";
26
+ import { bootMicroservices } from "../message-broker/orchestrator.js";
24
27
 
25
28
  const logger = createLogger({
26
29
  level: "info",
@@ -54,15 +57,14 @@ const argv = yargs(process.argv.slice(2))
54
57
  .options("s", {
55
58
  alias: "server",
56
59
  // default: env.BIZA_SERVER_LINK, // handle by serverMode
57
- describe: "(Required) Tunnel server endpoint",
60
+ describe: "Tunnel server endpoint",
58
61
  type: "string",
59
- demandOption: false,
60
62
  })
61
63
  .options("sub", {
62
64
  alias: "subdomain",
63
65
  describe: "Public URL the tunnel server is forwarding to us",
64
66
  type: "string",
65
- demandOption: true,
67
+ // demandOption: true, // handle required argument manually
66
68
  })
67
69
  .options("h", {
68
70
  alias: "hostname",
@@ -70,55 +72,66 @@ const argv = yargs(process.argv.slice(2))
70
72
  describe:
71
73
  'Address of API server for forwarding over socket-tunnel. Please emit "HTTP" or "HTTPS" from address',
72
74
  type: "string",
73
- demandOption: false,
74
75
  })
75
76
  .options("p", {
76
77
  alias: "port",
77
78
  default: 212,
78
79
  describe: "Port of API server for forwarding over socket-tunnel",
79
80
  type: "number",
80
- demandOption: false,
81
81
  })
82
82
  .options("secure", {
83
83
  default: false,
84
84
  describe: "Is API server using ssl (HTTPS) ?",
85
85
  type: "boolean",
86
- demandOption: false,
87
86
  })
88
87
  .options("publish", {
89
88
  default: true,
90
89
  describe: "Will the CLI be published to the internet??",
91
90
  type: "boolean",
92
- demandOption: false,
93
91
  })
94
92
  .options("d", {
95
93
  alias: "dbindex",
96
94
  default: 2,
97
95
  describe: "Biz-A Database Index (Callback Feature)",
98
96
  type: "number",
99
- demandOption: false,
100
97
  })
101
98
  .options("sp", {
102
99
  alias: "serverport",
103
100
  default: defaultPort,
104
101
  describe: "Express Port (Callback Feature)",
105
102
  type: "number",
106
- demandOption: false,
107
103
  })
108
104
  .options("hs", {
109
105
  alias: "hubServer",
110
106
  // default: `${env.BIZA_HUB_SERVER_LINK}`, // handle by serverMode
111
107
  describe: "BizA hub",
112
108
  type: "string",
113
- demandOption: false,
114
109
  })
115
110
  .options("serverMode", {
116
111
  alias: "sMode",
117
112
  describe: "Backend server mode: prod, dev, or backup",
118
113
  type: "string",
119
114
  // choices: ["prod", "dev", "backup"],
120
- choices: ["prod", "dev"],
115
+ choices: ["prod", "dev", "local"],
121
116
  default: "prod",
117
+ })
118
+ .options("mode", {
119
+ describe:
120
+ "Message Broker mode: 'monolithic' [default], 'dispatcher', or 'agent'",
121
+ type: "string",
122
+ choices: ["monolithic", "dispatcher", "agent"],
123
+ default: "monolithic",
124
+ })
125
+ .options("dispatcherUrl", {
126
+ describe: "Required if mode is 'agent'. URL of the Dispatcher.",
127
+ type: "string",
128
+ default: "http://127.0.0.1:3002",
129
+ })
130
+ .options("services", {
131
+ describe:
132
+ "Comma-separated list of engine jobs this agent handles (e.g., 'BPM,EMAIL')",
133
+ type: "string",
134
+ default: "ALL",
122
135
  }).argv;
123
136
 
124
137
  // Server endpoint mapping by mode
@@ -135,6 +148,10 @@ const SERVER_ENDPOINTS = {
135
148
  // server: "https://backupServer.biz-a.id",
136
149
  // hub: "https://backupHub.biz-a.id",
137
150
  // },
151
+ local: {
152
+ server: "http://localhost:3000",
153
+ hub: "http://localhost:3001",
154
+ },
138
155
  };
139
156
 
140
157
  if (!argv.server) {
@@ -147,6 +164,23 @@ if (!argv.hubServer) {
147
164
  SERVER_ENDPOINTS[argv.serverMode]?.hub || SERVER_ENDPOINTS.prod.hub;
148
165
  }
149
166
 
167
+ // handle required options manually, not using yargs demandOption
168
+ if (argv.mode !== "agent") {
169
+ const requiredArgs = ["subdomain"];
170
+ if (!argv["server"] || !argv["subdomain"] || !argv["port"]) {
171
+ for (let key of requiredArgs) {
172
+ if (argv[key]) continue;
173
+ console.log(
174
+ "Error: missing required option : " + requiredArgs.join(","),
175
+ );
176
+ yargs().showHelp();
177
+ process.exit();
178
+ }
179
+ }
180
+ } else {
181
+ argv.serverport += 1; // make sure agent used different default port
182
+ }
183
+
150
184
  setGlobalConfig(argv);
151
185
 
152
186
  if (argv.help) {
@@ -154,17 +188,6 @@ if (argv.help) {
154
188
  process.exit();
155
189
  }
156
190
 
157
- if (!argv["server"] || !argv["subdomain"] || !argv["port"]) {
158
- for (let key in ["server", "subdomain", "port"]) {
159
- if (argv[key]) continue;
160
-
161
- console.log("Error: Required option, but nothing found");
162
-
163
- yargs().showHelp();
164
- process.exit();
165
- }
166
- }
167
-
168
191
  import express from "express";
169
192
  import compression from "compression";
170
193
  import cors from "cors";
@@ -236,38 +259,51 @@ server.listen(argv.serverport, () => {
236
259
  );
237
260
  });
238
261
 
239
- await streamEvent(
240
- ioClient(argv["server"], { query: { isSockStream: true } }),
241
- argv,
242
- ); // as socket client to BizA SERVER
243
-
244
- // as socket client to BizA HUB
245
- if (argv.hubServer) {
246
- socket = hubEvent(
247
- ioClient(argv["hubServer"], {
248
- reconnectionDelay: RECONNECT_SOCKET_DELAY,
249
- reconnectionDelayMax: RECONNECT_SOCKET_DELAY,
250
- query: { isCLI: true, room: argv["subdomain"] },
251
- }),
262
+ if (argv.mode.trim().toLowerCase() === "agent") {
263
+ // Message broker agent not need connection to server, hubServer, directHub
264
+ console.log(`[Message Broker] Starting as isolated CLI Agent...`);
265
+ const genericAgent = new CLI_Agent(argv.dispatcherUrl, argv.services);
266
+ initBpmAgent(genericAgent, argv.services);
267
+ } else {
268
+ // as socket client to BizA SERVER
269
+ await streamEvent(
270
+ ioClient(argv["server"], { query: { isSockStream: true } }),
252
271
  argv,
253
- (url) => {
254
- if (url !== argv.connectedHubUrl) {
255
- argv.connectedHubUrl = url;
256
- }
257
- },
258
272
  );
259
- }
260
273
 
261
- // as socket server for BizA Client and BizA devTools
262
- directHubEvent(createSocketServer(server, argv.cliAddress().ip), argv);
274
+ // as socket client to BizA HUB
275
+ if (argv.hubServer) {
276
+ socket = hubEvent(
277
+ ioClient(argv["hubServer"], {
278
+ reconnectionDelay: RECONNECT_SOCKET_DELAY,
279
+ reconnectionDelayMax: RECONNECT_SOCKET_DELAY,
280
+ query: { isCLI: true, room: argv["subdomain"] },
281
+ }),
282
+ argv,
283
+ (url) => {
284
+ if (url !== argv.connectedHubUrl) {
285
+ argv.connectedHubUrl = url;
286
+ }
287
+ },
288
+ );
289
+ }
263
290
 
264
- if (argv.publish == true) {
265
- localhostTunnel(argv.serverport, (url) => {
266
- // publish CLI
267
- if (url !== argv.connectedPublicUrl) {
268
- argv.connectedPublicUrl = url;
269
- }
270
- });
291
+ const ioServerInstance = createSocketServer(server, argv.cliAddress().ip); // used by directHub at "/" namespace and "/message-broker" namespace
292
+
293
+ // as socket server for BizA Client and BizA devTools
294
+ // directHubEvent(createSocketServer(server, argv.cliAddress().ip), argv);
295
+ directHubEvent(ioServerInstance, argv);
296
+
297
+ bootMicroservices(argv, ioServerInstance);
298
+
299
+ if (argv.publish == true) {
300
+ localhostTunnel(argv.serverport, (url) => {
301
+ // publish CLI
302
+ if (url !== argv.connectedPublicUrl) {
303
+ argv.connectedPublicUrl = url;
304
+ }
305
+ });
306
+ }
271
307
  }
272
308
 
273
309
  export { app };
package/bin/hubEvent.js CHANGED
@@ -17,7 +17,7 @@ const DISCONNECT_REASON_BY_SOCKET_SERVER = "io server disconnect";
17
17
 
18
18
  export const socketAgent = (isUsingHttps) => (isUsingHttps == true ? tls : net);
19
19
 
20
- const getIdText = (id) => (id ? id + " " : "");
20
+ const getIdText = (id) => (id ? id + " " : "");
21
21
 
22
22
  export const streamEvent = async (socket, argv) =>
23
23
  new Promise((resolve, reject) => {
@@ -164,76 +164,9 @@ export const streamEvent = async (socket, argv) =>
164
164
  }
165
165
  };
166
166
 
167
- const publishReqCb = async (data, callback) => {
168
- try {
169
- process.env.BIZA_APP_SKIP_PARSE = "1";
170
- const { addApp } = await import("./app.js");
171
-
172
- const requestBody =
173
- typeof data?.body === "string"
174
- ? JSON.parse(data.body || "{}")
175
- : data?.body || {};
176
- const hasTransportEnvelope =
177
- requestBody &&
178
- typeof requestBody === "object" &&
179
- typeof requestBody.body === "object" &&
180
- (Object.prototype.hasOwnProperty.call(
181
- requestBody,
182
- "method",
183
- ) ||
184
- Object.prototype.hasOwnProperty.call(
185
- requestBody,
186
- "query",
187
- ) ||
188
- Object.prototype.hasOwnProperty.call(
189
- requestBody,
190
- "headers",
191
- ));
192
- const unwrappedPayload = hasTransportEnvelope
193
- ? requestBody.body
194
- : requestBody;
195
- const publishPayload =
196
- unwrappedPayload &&
197
- typeof unwrappedPayload === "object" &&
198
- unwrappedPayload.addApp &&
199
- typeof unwrappedPayload.addApp === "object"
200
- ? unwrappedPayload.addApp
201
- : unwrappedPayload;
202
- const params =
203
- publishPayload && typeof publishPayload.options === "object"
204
- ? publishPayload.options
205
- : publishPayload;
206
- const toNumber = (value, fallback) => {
207
- const parsed = Number(value);
208
- return Number.isFinite(parsed) ? parsed : fallback;
209
- };
210
-
211
- const response = await addApp({
212
- workingDir: params.workingDir || process.cwd(),
213
- verbose: !!params.verbose,
214
- server: params.server || argv.server || "http://localhost",
215
- apiPort: toNumber(params.apiPort, argv.port || 212),
216
- dbIndex: toNumber(params.dbIndex, argv.dbindex || 2),
217
- sub: params.sub || params.subdomain || argv.subdomain,
218
- files: params.files || params.fileList,
219
- body: publishPayload,
220
- });
221
-
222
- callback(response);
223
- } catch (error) {
224
- callback({
225
- success: false,
226
- error: error?.message || error,
227
- });
228
- } finally {
229
- delete process.env.BIZA_APP_SKIP_PARSE;
230
- }
231
- };
232
-
233
167
  socket.on("connect", connectCb);
234
168
  socket.on("incomingClient", incomingHubCb);
235
169
  socket.on("cli-req", cliReqCb);
236
- socket.on("publish-req", publishReqCb);
237
170
  socket.on("cliCommand", (data, cb) => handleCliCommand(data, cb, argv));
238
171
 
239
172
  socket.on("disconnect", (reason) => {
@@ -363,7 +296,7 @@ const EngineRegistry = {
363
296
  // ZERO-MEMORY LAZY REGISTRY. Lazy load the module into memory (Only impacts performance on the very first call)
364
297
  "bpm/workflow": () => import("../engine/bpm/workflow.js"),
365
298
  "bpm/workflowRT": () => import("../engine/bpm/workflow-runtime.js"),
366
- // 'domain/system': () => import('../engine/domain/system.js') // example for Domain Engine
299
+ "domain/system": () => import("../engine/domain/system.js"),
367
300
  };
368
301
 
369
302
  const handleCliCommand = async (data, cb, argv) => {
@@ -414,7 +347,10 @@ const handleCliCommand = async (data, cb, argv) => {
414
347
  );
415
348
  }
416
349
 
417
- const result = await engineModule[methodName](data.payload);
350
+ const result = await engineModule[methodName](
351
+ data.payload,
352
+ argv,
353
+ );
418
354
 
419
355
  cb(null, { success: true, data: result });
420
356
  break;
package/bin/migrate.js CHANGED
@@ -7,14 +7,14 @@ const __filename = fileURLToPath(import.meta.url);
7
7
  const __dirname = path.dirname(__filename);
8
8
  const migrationDir = path.resolve(__dirname, "..", "migrations");
9
9
 
10
- const flattenSql = (sqlString) => {
11
- return sqlString
12
- .replace(/--.*$/gm, "") // 1. Strip all SQL comments
13
- .split(/\r?\n/) // 2. Split into array of individual lines
14
- .map((line) => line.trim()) // 3. Trim leading/trailing indentation
15
- .filter((line) => line.length > 0) // 4. Remove empty lines entirely
16
- .join(" "); // 5. Join safely with exactly one space
17
- };
10
+ // const flattenSql = (sqlString) => {
11
+ // return sqlString
12
+ // .replace(/--.*$/gm, "") // 1. Strip all SQL comments
13
+ // .split(/\r?\n/) // 2. Split into array of individual lines
14
+ // .map((line) => line.trim()) // 3. Trim leading/trailing indentation
15
+ // .filter((line) => line.length > 0) // 4. Remove empty lines entirely
16
+ // .join(" "); // 5. Join safely with exactly one space
17
+ // };
18
18
 
19
19
  export const newMigration = (argv) => {
20
20
  if (!fs.existsSync(migrationDir)) {
@@ -72,7 +72,8 @@ export const runMigrations = async (argv) => {
72
72
  END
73
73
  END
74
74
  `;
75
- await executeBlock(flattenSql(bootstrapSql), apiConfig);
75
+ // await executeBlock(flattenSql(bootstrapSql), apiConfig);
76
+ await executeBlock(bootstrapSql, apiConfig);
76
77
  };
77
78
 
78
79
  try {
@@ -154,7 +155,8 @@ export const runMigrations = async (argv) => {
154
155
  const trackerSql = `\n INSERT INTO SYS$MIGRATIONS (FILE_NAME) VALUES ('${file}');\n`;
155
156
  let rawSql = sqlContent.replace(/END\s*$/i, trackerSql + "END");
156
157
 
157
- await executeBlock(flattenSql(rawSql), apiConfig);
158
+ // await executeBlock(flattenSql(rawSql), apiConfig);
159
+ await executeBlock(rawSql, apiConfig);
158
160
 
159
161
  console.log(`[Migrate] Success: ${file}`);
160
162
  }
package/db/db.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as adapter from "./ds.js";
2
2
 
3
+ export const getGlobalConfig = () => adapter.getGlobalConfig();
4
+
3
5
  export const queryData = async (param, config = {}) => {
4
6
  return await adapter.queryData(param, config);
5
7
  };
package/db/ds.js CHANGED
@@ -7,6 +7,8 @@ export const setGlobalConfig = (cfg) => {
7
7
  injectedConfig = cfg;
8
8
  };
9
9
 
10
+ export const getGlobalConfig = () => injectedConfig;
11
+
10
12
  // ===============
11
13
  // PRIVATE HELPERS
12
14
  // ===============
@@ -53,8 +55,47 @@ function buildDataSnapPayload(resolvedConfig, ormMethod, payloadObject) {
53
55
  object: payloadObject,
54
56
  };
55
57
 
58
+ // return JSON.stringify({
59
+ // _parameters: [JSON.stringify(paramObj)],
60
+ // });
61
+
62
+ // convert to FB Valid sql string before stringification
63
+ const fbSafeStr = JSON.stringify(paramObj, (key, value) => {
64
+ // convert js datetime to expected format by firebird
65
+ // if (Object.prototype.toString.call(value) === "[object Date]") {
66
+ // return value.toISOString().replace("T", " ").replace("Z", "");
67
+ // }
68
+ // if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
69
+ // return value.replace("T", " ").replace(/Z$/, "");
70
+ // }
71
+
72
+ const isDateObject =
73
+ Object.prototype.toString.call(value) === "[object Date]";
74
+ const isIsoString =
75
+ typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value);
76
+
77
+ if (isDateObject || isIsoString) {
78
+ // Convert strings to real Date objects so JavaScript handles the timezone math
79
+ const dateObj = isDateObject ? value : new Date(value);
80
+
81
+ // Build local time string manually to avoid toISOString() UTC conversion
82
+ const pad = (num) => String(num).padStart(2, "0");
83
+ const YYYY = dateObj.getFullYear();
84
+ const MM = pad(dateObj.getMonth() + 1);
85
+ const DD = pad(dateObj.getDate());
86
+ const HH = pad(dateObj.getHours());
87
+ const mm = pad(dateObj.getMinutes());
88
+ const ss = pad(dateObj.getSeconds());
89
+ const ms = String(dateObj.getMilliseconds()).padStart(3, "0");
90
+
91
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}.${ms}`;
92
+ }
93
+
94
+ return value;
95
+ });
96
+
56
97
  return JSON.stringify({
57
- _parameters: [JSON.stringify(paramObj)],
98
+ _parameters: [fbSafeStr],
58
99
  });
59
100
  }
60
101
 
@@ -97,7 +138,7 @@ export const queryData = async (payload, config = {}) => {
97
138
  typeof res.data === "string" ? JSON.parse(res.data) : res.data;
98
139
  }
99
140
  if (payload && Array.isArray(payload.columns) && Array.isArray(rawData)) {
100
- return rawData.map((row) => {
141
+ const result = rawData.map((row) => {
101
142
  const mappedObj = {};
102
143
 
103
144
  payload.columns.forEach((col, index) => {
@@ -125,6 +166,36 @@ export const queryData = async (payload, config = {}) => {
125
166
  });
126
167
  return mappedObj;
127
168
  });
169
+ return result;
170
+
171
+ // return rawData.map((row) => {
172
+ // const mappedObj = {};
173
+
174
+ // payload.columns.forEach((col, index) => {
175
+ // if (!col.key) return;
176
+
177
+ // if (Array.isArray(row)) {
178
+ // mappedObj[col.key] = row[index];
179
+ // } else {
180
+ // const exactDataMatch = row[col.data];
181
+ // const upperDataMatch = row[String(col.data).toUpperCase()];
182
+ // const aliasMatch = col.alias ? row[col.alias] : undefined;
183
+ // const upperAliasMatch = col.alias
184
+ // ? row[String(col.alias).toUpperCase()]
185
+ // : undefined;
186
+
187
+ // if (exactDataMatch !== undefined)
188
+ // mappedObj[col.key] = exactDataMatch;
189
+ // else if (upperDataMatch !== undefined)
190
+ // mappedObj[col.key] = upperDataMatch;
191
+ // else if (aliasMatch !== undefined)
192
+ // mappedObj[col.key] = aliasMatch;
193
+ // else if (upperAliasMatch !== undefined)
194
+ // mappedObj[col.key] = upperAliasMatch;
195
+ // }
196
+ // });
197
+ // return mappedObj;
198
+ // });
128
199
  }
129
200
  return rawData;
130
201
  };
@@ -181,9 +252,20 @@ export const list = async (payload, config = {}) => {
181
252
  return await dsReq(url, data, cfg.timeout);
182
253
  };
183
254
 
255
+ function flattenSql(sqlString) {
256
+ return sqlString
257
+ .replace(/--.*$/gm, "") // 1. Strip all SQL comments
258
+ .split(/\r?\n/) // 2. Split into array of individual lines
259
+ .map((line) => line.trim()) // 3. Trim leading/trailing indentation
260
+ .filter((line) => line.length > 0) // 4. Remove empty lines entirely
261
+ .join(" "); // 5. Join safely with exactly one space
262
+ }
263
+
184
264
  export const executeBlock = async (sqlString, config = {}) => {
185
265
  const cfg = resolveConfig(config);
186
266
  const url = buildDataSnapUrl(cfg, "block");
187
- const data = buildDataSnapPayload(cfg, "block", sqlString);
267
+ const flatSql = flattenSql(sqlString);
268
+ // const data = buildDataSnapPayload(cfg, "block", sqlString);
269
+ const data = buildDataSnapPayload(cfg, "block", flatSql);
188
270
  return await dsReq(url, data, cfg.timeout);
189
271
  };