biz-a-cli 2.3.57 → 2.3.59

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.
@@ -0,0 +1,105 @@
1
+ import axios from 'axios';
2
+ import { prepareScript, encryptScript } from './script.js';
3
+ import { IDLE_SOCKET_TIMEOUT_MILLISECONDS } from './hubEvent.js';
4
+ import { CLIENT_ROOM } from './directHubEvent.js';
5
+
6
+ export async function getAppData(apiOpts){
7
+ return await axios.request({
8
+ timeout : IDLE_SOCKET_TIMEOUT_MILLISECONDS,
9
+ baseURL : `${apiOpts['secure']==true ? 'https://' : 'http://'}${apiOpts['hostname']}:${apiOpts['port']}`,
10
+ url : 'fina/rest/TOrmMethod/%22list%22',
11
+ method : 'POST',
12
+ "data" : {
13
+ "dbIndex": apiOpts['dbindex'],
14
+ "object":{
15
+ "columns":[
16
+ {"title":"app", "data":"SYS$APPS.APPNAME"},
17
+ {"title":"metadata", "data":"SYS$APPS.METADATA"}
18
+ ]
19
+ }
20
+ }
21
+ })
22
+ .then(response=>{
23
+ return {error: null, apps: JSON.parse(response.data.data)};
24
+ })
25
+ .catch(err=>{
26
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), apps: []};
27
+ });
28
+ };
29
+
30
+ async function getDeployScript(serverUrl, appList, data){
31
+ let script = await prepareScript(data.fileName, data.script, false);
32
+ if (script) {
33
+ script = await axios.request({
34
+ timeout : IDLE_SOCKET_TIMEOUT_MILLISECONDS,
35
+ baseURL : serverUrl,
36
+ url : 'api/acquirerKey',
37
+ method : 'POST',
38
+ "data" : {"metadata": JSON.stringify(JSON.parse((appList.find((app)=>app['SYS$APPS.APPNAME']===data.appName))['SYS$APPS.METADATA'])['acquirer'])},
39
+ })
40
+ .then(response=>encryptScript(script, response.data).toString('base64'));
41
+ };
42
+ return script;
43
+ };
44
+
45
+ export async function onScriptChangeFromVsCode(sock, serverUrl, appList, data){
46
+ let script;
47
+ try {
48
+ script = await getDeployScript(serverUrl, appList, data);
49
+ if (script) {
50
+ sock.to(CLIENT_ROOM).emit('scriptDeploy', {...data, ...{script}});
51
+ return {error: null, script};
52
+ }
53
+ else {
54
+ throw new Error('Invalid Script');
55
+ };
56
+ }
57
+ catch (err) {
58
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), script};
59
+ };
60
+ };
61
+
62
+ const addAppListListener = (sock,argv)=>{
63
+ sock.on('appList', async (data, cb)=>{
64
+ const result = await getAppData(argv);
65
+ cb(result.error, ((result.error) ? [] : result.apps.map((app)=>app['SYS$APPS.APPNAME'])));
66
+ });
67
+ };
68
+
69
+ const addOnScriptChangeListener = (sock, argv, deployFn)=>{
70
+ sock.on('scriptChange', async (data, cb)=>{
71
+ const result = await getAppData(argv);
72
+ if (result.error) {
73
+ cb(result);
74
+ }
75
+ else {
76
+ const deployResult = await deployFn(result.apps, data);
77
+ cb(deployResult.error, deployResult.script);
78
+ };
79
+ });
80
+ };
81
+
82
+ export const deploymentListenerForVSCode = (socket, argv)=>{
83
+ addAppListListener(socket,argv);
84
+ addOnScriptChangeListener(socket, argv, async (apps, data)=>{
85
+ return (await onScriptChangeFromVsCode(socket, argv['server'], apps, data));
86
+ });
87
+ };
88
+
89
+ export async function onScriptChangeFromHubServer(serverUrl, appList, data){
90
+ let script;
91
+ try {
92
+ script = await getDeployScript(serverUrl, appList, data);
93
+ return {error: (script ? null : 'Invalid Script'), script};
94
+ }
95
+ catch (err) {
96
+ return {"error": ((err && (err instanceof Error)) ? err.message : err), script};
97
+ };
98
+ };
99
+
100
+ export const deploymentListenerForHubServer = (socket, argv)=>{
101
+ addAppListListener(socket, argv);
102
+ addOnScriptChangeListener(socket, argv, async (apps, data)=>{
103
+ return (await onScriptChangeFromHubServer(argv['server'], apps, data));
104
+ });
105
+ };
@@ -1,49 +1,70 @@
1
- import { apiRequestListener } from './hubEvent.js'
1
+ import { apiRequestListener, RECONNECT_SOCKET_DELAY } from './hubEvent.js'
2
+ import { deploymentListenerForVSCode } from './deployEvent.js'
2
3
  import { Tunnel as QuickTunnel } from 'cloudflared'
3
4
 
5
+ export const CLIENT_ROOM = 'clientRoom'
6
+
4
7
  export async function localhostTunnel(port, notifier){
5
- let tunnelUrl = ''
6
- let qt = QuickTunnel.quick('127.0.0.1:'+port)
8
+ let tunnelUrl = '';
9
+ let qt = QuickTunnel.quick('127.0.0.1:'+port);
10
+ function reconnectTunnel(){
11
+ setTimeout(()=>{qt = localhostTunnel(port, notifier)}, RECONNECT_SOCKET_DELAY);
12
+ };
7
13
  qt.on('url', (qtUrl)=>{
8
14
  // console.log('qt url', qtUrl)
9
- tunnelUrl = qtUrl
15
+ tunnelUrl = qtUrl;
10
16
  })
11
- qt.on('connected', (conn)=>{
17
+ .on('connected', (conn)=>{
12
18
  // console.log('qt connect', conn)
13
- notifier(tunnelUrl)
14
- console.log(`${new Date()}: Connected to Direct Hub at public URL`)
19
+ notifier(tunnelUrl);
20
+ console.log(`${new Date()}: Connected to Direct Hub at public URL ${tunnelUrl}`)
15
21
  })
16
- qt.on('disconnected', (conn)=>{
22
+ .on('disconnected', (conn)=>{
17
23
  // console.log('qt disconnect', conn)
18
- notifier('')
19
- qt.stop
24
+ notifier('');
25
+ qt.stop();
20
26
  })
21
- qt.on('error', (err)=>{
27
+ .on('error', (err)=>{
22
28
  // console.log('qt err', err)
23
- notifier('')
24
- qt.stop
29
+ notifier('');
30
+ qt.stop();
25
31
  })
26
- qt.on('exit', (code)=>{
32
+ .on('exit', (code)=>{
27
33
  // console.log('qt exit', code)
28
- notifier('')
29
- console.log(`${new Date()}: Direct Hub is not available, will try to reinitiate it in 5 minutes. Error code : `, code)
30
- setTimeout(()=>{qt = localhostTunnel(port, notifier)}, 5*60000)
31
- })
32
- // qt.on('stdout', (data)=>{
34
+ notifier('');
35
+ console.log(`${new Date()}: Direct Hub is not available, will try to reinitiate it in ${RECONNECT_SOCKET_DELAY/1000} seconds. Error code : `, code);
36
+ reconnectTunnel();
37
+ // })
38
+ // on('stdout', (data)=>{
33
39
  // console.log('qt stdout', data)
34
40
  // })
35
- // qt.on('stderr', (data)=>{
41
+ // on('stderr', (data)=>{
36
42
  // console.log('qt stderr', data)
37
- // })
43
+ });
38
44
  return qt
39
45
  }
40
46
 
41
47
  export function directHubEvent(serverSocket, argv){
42
48
  serverSocket.on('connection', (clientSocket) => {
43
- if (process.env.NODE_ENV !== 'production') {
44
- console.log(clientSocket.id, 'connected')
45
- clientSocket.on('disconnect', (reason)=>{console.log(`${clientSocket.id||'Direct Hub'} disconnected. Reason : ${reason}`)})
49
+ if (clientSocket.handshake.query.isClient) {
50
+ clientSocket.join(CLIENT_ROOM);
51
+ apiRequestListener(clientSocket, argv);
46
52
  }
47
- apiRequestListener(clientSocket, argv)
53
+ else if (clientSocket.handshake.query.isDeploy) {
54
+ deploymentListenerForVSCode(clientSocket, argv)
55
+ }
56
+
57
+ // shall not log in production mode
58
+ if (process.env.NODE_ENV !== 'production') {
59
+ const id = `<${clientSocket.handshake?.query?.isClient ? `Client` : clientSocket.handshake?.query?.isDeploy ? 'Deployer' : 'Anonymous'}> ${clientSocket.id}`;
60
+ clientSocket.on('disconnect', (reason)=>{
61
+ console.log(id, `disconnected. Reason : ${reason}`)
62
+ })
63
+ console.log(id, `connected`)
64
+ };
48
65
  })
66
+ }
67
+
68
+ export function getSocketCORSOrigin(cliIpAddress){
69
+ return ['https://biz-a.id', 'https://test.biz-a.id', /\.biz-a\.id$/, 'vscode-file://vscode-app', /\.vscode-cdn\.net$/].concat((process.env.NODE_ENV === 'production') ? [] : [`http://${cliIpAddress}:4200`, 'http://localhost:4200'])
49
70
  }
package/bin/hub.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  import yargs from 'yargs';
4
4
  import { io as ioClient } from "socket.io-client";
5
- import { streamEvent, hubEvent } from './hubEvent.js'
5
+ import { streamEvent, hubEvent, RECONNECT_SOCKET_DELAY } from './hubEvent.js'
6
6
  import { createLogger, transports, format } from "winston";
7
7
  import { Server as ioServer } from 'socket.io'
8
8
  import os from 'node:os'
9
- import { directHubEvent, localhostTunnel }from './directHubEvent.js'
9
+ import { directHubEvent, localhostTunnel, getSocketCORSOrigin }from './directHubEvent.js'
10
10
  import { env } from "../envs/env.js"
11
11
 
12
12
  const logger = createLogger({
@@ -149,16 +149,15 @@ await streamEvent(ioClient(argv['server'], {query: {isSockStream: true }}), argv
149
149
 
150
150
  // as socket client to BizA HUB
151
151
  if (argv.hub) {
152
- hubEvent(ioClient(argv['hub'], { reconnectionDelay: 5*60*1000, reconnectionDelayMax: 5*60*1000, query: {isCLI: true, room: argv['subdomain']} }), argv, (url)=>{
152
+ hubEvent(ioClient(argv['hub'], { reconnectionDelay: RECONNECT_SOCKET_DELAY, reconnectionDelayMax: RECONNECT_SOCKET_DELAY, query: {isCLI: true, room: argv['subdomain']} }), argv, (url)=>{
153
153
  if (url!==argv.connectedHubUrl) {
154
154
  argv.connectedHubUrl = url
155
155
  }
156
156
  })
157
157
  }
158
158
 
159
- // as socket server from BizA Client
160
- const serverCORSOrigin = ['https://biz-a.id', 'https://test.biz-a.id', /\.biz-a\.id$/].concat((process.env.NODE_ENV === 'production') ? [] : [`http://${argv.cliAddress().ip}:4200`, 'http://localhost:4200'])
161
- directHubEvent(new ioServer(server, {cors: {origin: serverCORSOrigin}}), argv);
159
+ // as socket server for BizA Client and BizA devTools
160
+ directHubEvent(new ioServer(server, {cors: {origin: getSocketCORSOrigin(argv.cliAddress().ip)}}), argv);
162
161
  if (argv.publish==true) {
163
162
  localhostTunnel(argv.serverport, (url)=>{ // publish CLI
164
163
  if (url!==argv.connectedPublicUrl) {
package/bin/hubEvent.js CHANGED
@@ -6,8 +6,10 @@ const require = createRequire(import.meta.url);
6
6
  const ss = require('socket.io-stream'); //SCY: Temporary, next will be replaced with import
7
7
  import { Transform } from 'node:stream'
8
8
  // import { pipeline } from 'node:stream'
9
+ import { deploymentListenerForHubServer } from './deployEvent.js';
9
10
 
10
- const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 30;
11
+ export const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 30;
12
+ export const RECONNECT_SOCKET_DELAY = 60 * 1000;
11
13
  const DISCONNECT_REASON_BY_SOCKET_SERVER = 'io server disconnect'
12
14
 
13
15
  export const socketAgent = isUsingHttps=>(isUsingHttps==true) ? tls : net
@@ -156,7 +158,8 @@ export const hubEvent = (socket, argv, notifier)=>{
156
158
  notifier('')
157
159
  console.log(`${new Date()}: ${getIdText(id)}reconnecting to BizA Hub`)
158
160
  })
159
- apiRequestListener(socket, argv)
161
+ apiRequestListener(socket, argv);
162
+ deploymentListenerForHubServer(socket, argv);
160
163
  }
161
164
 
162
165
  export const apiRequestListener = (socket, argv)=>{
package/bin/script.js ADDED
@@ -0,0 +1,125 @@
1
+ import { runInNewContext } from "vm"
2
+ import uglify from "uglify-js"
3
+ import * as vm from 'node:vm'
4
+ import { randomBytes, createCipheriv } from "node:crypto"
5
+
6
+ export const prepareScript = async (file, script, shallLog=false) => {
7
+
8
+ const printInfo = (msg) => {
9
+ if (shallLog) {
10
+ console.log(msg)
11
+ }
12
+ }
13
+
14
+ const minifyiedWithUglify = (fileName, scriptText)=>{
15
+ const minifyResult = uglify.minify(scriptText, {compress: false, mangle: false})
16
+ if (minifyResult.error ) {throw (`${fileName}:${minifyResult.error.line}:${minifyResult.error.col}: ` + minifyResult.error)}
17
+
18
+ let jsMinData = minifyResult.code
19
+ jsMinData = jsMinData.replace('module.exports=get;', '') // Ex : at "executeBlockLib.js" of Imamatek App, will throw error. We need this line code for testing or debugging
20
+
21
+ const minifiedBuffer = Buffer.from(jsMinData)
22
+ printInfo(['Minify : \n', minifiedBuffer.toString()].join(''))
23
+
24
+ return minifiedBuffer.toString()
25
+ }
26
+
27
+ const isJsonFile = (fileName)=>{
28
+ return fileName.split('.').pop().toLowerCase().match(/^(json)$/)
29
+ }
30
+
31
+ const jsonReplacer = (key, value) =>{
32
+
33
+ // // with line break
34
+ // if (typeof value == 'function') {
35
+ // let arr = value.toString().replace(/(\r\n|\n|\r)/gm, "°").split("°");
36
+ // if (arr.length < 3) throw 'Function must be minimal 3 lines';
37
+ // return [
38
+ // 'window.Function',
39
+ // getParam(arr[0]),
40
+ // arr.slice(1, arr.length - 1)
41
+ // ];
42
+ // } else {
43
+ // return value;
44
+ // }
45
+
46
+ // without line break
47
+ if (typeof value == 'function') {
48
+ const fnScript = value.toString()
49
+ let params, body = ''
50
+ if (fnScript.indexOf('{')==-1) {
51
+ const arr = fnScript.split('=>')
52
+ params = arr[0]
53
+ body = arr.slice(1).join()
54
+ } else {
55
+ params = fnScript.split('{')[0]
56
+ body = fnScript.substring(fnScript.indexOf('{')+1, fnScript.lastIndexOf('}'))
57
+ }
58
+ params = params.replace(/(\s|=>|\(|\)|function)/gim, '')
59
+ printInfo(['window.Function', params, body])
60
+ return ['window.Function', params, body]
61
+ } else {
62
+ return value;
63
+ }
64
+ }
65
+
66
+ const compileScriptWithNodeVM = (fileName, scriptText)=>{
67
+ try {
68
+ printInfo('Running script with VM')
69
+ const minifiedScript = minifyiedWithUglify(fileName, scriptText)
70
+ if (isJsonFile(fileName)) {
71
+ const vmResult = runInNewContext(minifiedScript)
72
+ return vmResult ? JSON.stringify((typeof vmResult=='function') ? vmResult() : (vmResult || ''), jsonReplacer) : ''
73
+ } else {
74
+ // for handling "get" function in local scope (let, var, const)
75
+ const sandbox = vm.createContext({'script': ''});
76
+ vm.runInContext(
77
+ minifiedScript + "\n script = (typeof get=='undefined') ? '' : (typeof get=='function') ? get() : (get||'')",
78
+ sandbox
79
+ )
80
+ return JSON.stringify(sandbox.script, jsonReplacer)
81
+ }
82
+ } catch (err){
83
+ printInfo((err.toString().indexOf(fileName)===-1 ? `${fileName} : ` : '') + err) // Do not log it as error, we will try to compile the script using ES6 Import
84
+ return ''
85
+ }
86
+ }
87
+
88
+ const compileScriptWithESImport = async (fileName, scriptText)=>{
89
+ try {
90
+ printInfo('Running script with Import function')
91
+ // const lib = await import(`file://${process.cwd()}\\${fileName}`, isJsonFile(fileName) ? {with: {type: "json"}} : {}) // we can not use file import if script in memory
92
+ const lib = await import(`data:text/javascript,${scriptText}`, isJsonFile(fileName) ? {with: {type: "json"}} : {}) // used data import if script in memory
93
+ return JSON.stringify((typeof lib.default=='function') ? lib.default() : (lib.default || ''), jsonReplacer) // remove trailing whitespace
94
+ // stringifyData = stringifyData.replace(/\\r\\n/g,'') // remove line break. We can not do this because "inline css" still requires line breaks
95
+ } catch (error){
96
+ throw(`${fileName} : ` + error)
97
+ }
98
+ }
99
+
100
+ const isEmptyScript = (scriptText) => {
101
+ return ((typeof scriptText=='undefined') || (scriptText===null) || (scriptText.trim()==='""') || (typeof scriptText=='string' && scriptText.trim().length==0))
102
+ }
103
+
104
+ printInfo(`===================\n${file.toUpperCase()}\n===================`)
105
+ let stringifyData = ''
106
+ try {
107
+ stringifyData = compileScriptWithNodeVM(file, script)
108
+ stringifyData = isEmptyScript(stringifyData) ? await compileScriptWithESImport(file, script) : stringifyData
109
+ if (isEmptyScript(stringifyData)) {
110
+ throw(`${file} : ` + 'Failed to compile template script.\nPlease make sure the script is correct and not returning empty result')
111
+ } else {
112
+ printInfo('Array function :')
113
+ printInfo(['Stringify : \n', stringifyData].join(''))
114
+ }
115
+ return Buffer.from(stringifyData)
116
+ } finally {
117
+ printInfo(`===================\nEOF ${file.toUpperCase()}\n===================`)
118
+ }
119
+ }
120
+
121
+ export const encryptScript = (data, encryptKey)=>{
122
+ const initializeVector = randomBytes(16) // "iv" is unique for each template file
123
+ const cipher = createCipheriv('aes-256-cbc', Buffer.from(encryptKey, 'base64'), initializeVector)
124
+ return Buffer.concat([initializeVector, cipher.update(data), cipher.final()]) // put "iv" at beginning of cipherText, must seperate it when doing decryption
125
+ }
package/bin/uploadApp.js CHANGED
@@ -121,7 +121,7 @@ const getFileMinifiedData = async (fileName) => {
121
121
  const lib = await import(fn)
122
122
  printInfo('Array function :')
123
123
  stringifyData = JSON.stringify((typeof lib.default=='function') ? lib.default() : lib.default, replacer)
124
- stringifyData = stringifyData.replace(/ /g,'') // remove trailing whitespace
124
+ stringifyData = stringifyData.replace(/ /g,'') // remove trailing whitespace
125
125
  // stringifyData = stringifyData.replace(/\\r\\n/g,'') // remove line break. We don't do this because "inline css" still requires line breaks
126
126
  printInfo(['Stringify : \n', stringifyData].join(''))
127
127
  } catch (error){
package/debug.log ADDED
@@ -0,0 +1,2 @@
1
+ [0605/214850.930:ERROR:registration_protocol_win.cc(108)] CreateFile: The system cannot find the file specified. (0x2)
2
+ [0605/220241.671:ERROR:registration_protocol_win.cc(108)] CreateFile: The system cannot find the file specified. (0x2)