verteilen-core 1.0.0

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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/bun.lock +730 -0
  4. package/index.ts +1 -0
  5. package/jest.config.js +8 -0
  6. package/package.json +42 -0
  7. package/src/client/analysis.ts +377 -0
  8. package/src/client/client.ts +230 -0
  9. package/src/client/cluster.ts +125 -0
  10. package/src/client/execute.ts +210 -0
  11. package/src/client/http.ts +45 -0
  12. package/src/client/javascript.ts +535 -0
  13. package/src/client/job_execute.ts +216 -0
  14. package/src/client/job_parameter.ts +41 -0
  15. package/src/client/os.ts +210 -0
  16. package/src/client/parameter.ts +58 -0
  17. package/src/client/resource.ts +121 -0
  18. package/src/client/shell.ts +147 -0
  19. package/src/interface/base.ts +82 -0
  20. package/src/interface/bus.ts +144 -0
  21. package/src/interface/enum.ts +181 -0
  22. package/src/interface/execute.ts +47 -0
  23. package/src/interface/record.ts +131 -0
  24. package/src/interface/server.ts +91 -0
  25. package/src/interface/struct.ts +292 -0
  26. package/src/interface/table.ts +34 -0
  27. package/src/interface/ui.ts +35 -0
  28. package/src/interface.ts +50 -0
  29. package/src/lan/en.json +395 -0
  30. package/src/lan/zh_TW.json +395 -0
  31. package/src/plugins/i18n.ts +20 -0
  32. package/src/script/console_manager.ts +135 -0
  33. package/src/script/console_server_manager.ts +46 -0
  34. package/src/script/execute/base.ts +309 -0
  35. package/src/script/execute/feedback.ts +212 -0
  36. package/src/script/execute/region_job.ts +14 -0
  37. package/src/script/execute/region_project.ts +23 -0
  38. package/src/script/execute/region_subtask.ts +14 -0
  39. package/src/script/execute/region_task.ts +23 -0
  40. package/src/script/execute/runner.ts +339 -0
  41. package/src/script/execute/util_parser.ts +175 -0
  42. package/src/script/execute_manager.ts +348 -0
  43. package/src/script/socket_manager.ts +329 -0
  44. package/src/script/webhook_manager.ts +6 -0
  45. package/src/script/webhook_server_manager.ts +102 -0
  46. package/src/util/server/console_handle.ts +248 -0
  47. package/src/util/server/log_handle.ts +194 -0
  48. package/test/TEST.ts +63 -0
  49. package/test/client/execute.test.ts +54 -0
  50. package/test/client/javascript.test.ts +78 -0
  51. package/test/client/server.test.ts +26 -0
  52. package/test/client/task.test.ts +136 -0
  53. package/test/script/parser.test.ts +110 -0
  54. package/test/script/socket.test.ts +27 -0
  55. package/tsconfig.json +15 -0
@@ -0,0 +1,535 @@
1
+ // ========================
2
+ //
3
+ // Share Codebase
4
+ //
5
+ // ========================
6
+ import * as vm from 'vm';
7
+ import { DATA_FOLDER, DataType, JavascriptLib, Job, Libraries, Messager, Messager_log, Parameter, ParameterContainer } from '../interface';
8
+ import { ClientJobParameter } from './job_parameter';
9
+ import { ClientOS } from './os';
10
+ import * as path from 'path';
11
+ import * as os from 'os'
12
+
13
+ export const safeEval = (code:string, context?:any, opts?:vm.RunningCodeInNewContextOptions | string) => {
14
+ let sandbox = {}
15
+ let resultKey = 'SAFE_EVAL_' + Math.floor(Math.random() * 1000000)
16
+ sandbox[resultKey] = {}
17
+ var clearContext = `
18
+ (function(){
19
+ Function = undefined;
20
+ const keys = Object.getOwnPropertyNames(this).concat(['constructor']);
21
+ keys.forEach((key) => {
22
+ const item = this[key];
23
+ if(!item || typeof item.constructor !== 'function') return;
24
+ this[key].constructor = undefined;
25
+ });
26
+ })();
27
+ `
28
+ code = clearContext + resultKey + `=(function(){\n${code}\n})();`
29
+ if (context != undefined) {
30
+ Object.keys(context).forEach(function (key) {
31
+ sandbox[key] = context[key]
32
+ })
33
+ }
34
+ vm.runInNewContext(code, sandbox, opts)
35
+ return sandbox[resultKey]
36
+ }
37
+
38
+ type Getlib = () => Libraries | undefined
39
+ type Getpara = () => Parameter | undefined
40
+ type Getjob = () => Job | undefined
41
+ type DatatypeChecker = (s:DataType) => boolean
42
+
43
+ let getlib:Getlib | undefined = undefined
44
+ let getpara:Getpara | undefined = undefined
45
+ let getjob:Getjob | undefined = undefined
46
+ let messager: Messager
47
+ let messager_log: Messager_log
48
+ let clientos:ClientOS | undefined
49
+ let para:ClientJobParameter | undefined = undefined
50
+ let waiting : number = 0
51
+
52
+ const tag = () => getjob!?.()?.uuid ?? 'unknown'
53
+ const runtime = () => getjob!?.()?.runtime_uuid ?? 'unknown'
54
+
55
+ //#region Global
56
+ function has(key:string, checker?:DatatypeChecker){
57
+ const p = getpara?.() ?? undefined
58
+ if(p == undefined) return false
59
+ return p.containers.findIndex(x => x.name == key && (checker ? checker(x.type) : true )) != -1
60
+ }
61
+ function hasboolean(key:string){
62
+ return has(key, (x) => x == DataType.Boolean)
63
+ }
64
+ function hasnumber(key:string){
65
+ return has(key, (x) => x == DataType.Number || x == DataType.Expression)
66
+ }
67
+ function hasstring(key:string){
68
+ return has(key, (x) => x == DataType.String || x == DataType.Textarea)
69
+ }
70
+ function hasobject(key:string){
71
+ return has(key, (x) => x == DataType.Object)
72
+ }
73
+ function haslist(key:string){
74
+ return has(key, (x) => x == DataType.List)
75
+ }
76
+ function hasselect(key:string){
77
+ return has(key, (x) => x == DataType.Select)
78
+ }
79
+
80
+ function get(key:string, checker?:DatatypeChecker){
81
+ const p = getpara?.() ?? undefined
82
+ if(p == undefined) return undefined
83
+ return p.containers.find(x => x.name == key && (checker ? checker(x.type) : true ))?.value ?? undefined
84
+ }
85
+ function getboolean(key:string){
86
+ return get(key, (x) => x == DataType.Boolean)
87
+ }
88
+ function getnumber(key:string){
89
+ if(key == 'ck'){
90
+ const r = getjob?.()?.index
91
+ if(r != undefined) return r
92
+ return 0
93
+ }
94
+ return get(key, (x) => x == DataType.Number || x == DataType.Expression)
95
+ }
96
+ function getstring(key:string){
97
+ return get(key, (x) => x == DataType.String || x == DataType.Textarea)
98
+ }
99
+ function getobject(key:string){
100
+ return get(key, (x) => x == DataType.Object)
101
+ }
102
+ function getlist(key:string){
103
+ return get(key, (x) => x == DataType.List)
104
+ }
105
+ function getselect(key:string){
106
+ const s = get(key, (x) => x == DataType.Select)
107
+ if(s?.meta == undefined) return undefined
108
+ return s.meta[s.value]
109
+ }
110
+ function getselectlendth(key:string){
111
+ const s = get(key, (x) => x == DataType.Select)
112
+ if(s?.meta == undefined) return undefined
113
+ return s.meta.length
114
+ }
115
+ function _set(key:string, checker?:DatatypeChecker, created:boolean = true):ParameterContainer | undefined{
116
+ const p = getpara?.() ?? undefined
117
+ if(p == undefined) return undefined
118
+ if(!p.canWrite) return undefined
119
+ return p.containers.find(x => x.name == key && (checker ? checker(x.type) : true ))
120
+ }
121
+ function set(key:string, value:any){
122
+ const target = _set(key)
123
+ if(target == undefined) return
124
+ switch(target.type){
125
+ case DataType.Boolean:
126
+ setboolean(key, value)
127
+ break
128
+ case DataType.Number:
129
+ setnumber(key, value)
130
+ break
131
+ case DataType.Textarea:
132
+ case DataType.String:
133
+ setstring(key, value)
134
+ break
135
+ case DataType.Object:
136
+ setobject(key, value)
137
+ break
138
+ case DataType.List:
139
+ setlist(key, value)
140
+ break
141
+ case DataType.Select:
142
+ setselect(key, value)
143
+ break
144
+ }
145
+ }
146
+ function setboolean(key:string, value:boolean){
147
+ let target = _set(key, (x) => x == DataType.Boolean)
148
+ if(target == undefined) {
149
+ target = { name: key, type: DataType.Boolean, hidden: false, runtimeOnly: true, value: value }
150
+ }else{
151
+ target.value = value
152
+ }
153
+ para?.feedbackboolean({key:key,value:value})
154
+ }
155
+ function setnumber(key:string, value:number){
156
+ if(key == 'ck') {
157
+ messager_log("Trying to set a constant ck...", tag(), runtime())
158
+ return
159
+ }
160
+ let target = _set(key, (x) => x == DataType.Number)
161
+ if(target == undefined) {
162
+ target = { name: key, type: DataType.Number, hidden: false, runtimeOnly: true, value: value }
163
+ }else{
164
+ target.value = value
165
+ }
166
+ para?.feedbacknumber({key:key,value:value})
167
+ }
168
+ function setstring(key:string, value:string){
169
+ let target = _set(key, (x) => x == DataType.String)
170
+ if(target == undefined) {
171
+ target = { name: key, type: DataType.String, hidden: false, runtimeOnly: true, value: value }
172
+ }else{
173
+ target.value = value
174
+ }
175
+ para?.feedbackstring({key:key,value:value})
176
+ }
177
+ function setobject(key:string, value:any){
178
+ let target = _set(key, (x) => x == DataType.Object)
179
+ if(target == undefined) {
180
+ target = { name: key, type: DataType.Object, hidden: false, runtimeOnly: true, value: value }
181
+ }else{
182
+ target.value = value
183
+ }
184
+ para?.feedbackobject({key:key,value:value})
185
+ }
186
+ function setlist(key:string, value:Array<string>){
187
+ let target = _set(key, (x) => x == DataType.List)
188
+ if(target == undefined) {
189
+ target = { name: key, type: DataType.List, hidden: false, runtimeOnly: true, value: value }
190
+ }else{
191
+ target.value = value
192
+ }
193
+ para?.feedbackobject({key:key,value:value})
194
+ }
195
+ function setselect(key:string, value:number){
196
+ const target = _set(key, (x) => x == DataType.Select)
197
+ if(target == undefined) return
198
+
199
+ target.value = value
200
+ para?.feedbackobject({key:key,value:value})
201
+ }
202
+ //#endregion
203
+
204
+ export class ClientJavascript {
205
+ path: any
206
+ os:any
207
+ env:any
208
+ message:any
209
+ http:any
210
+
211
+ constructor(_messager: Messager, _messager_log: Messager_log, _getjob:Getjob) {
212
+ messager = _messager
213
+ messager_log = _messager_log
214
+ this.path = {
215
+ filename: this.filename,
216
+ extname: this.extname,
217
+ dirname: this.dirname,
218
+ }
219
+ this.os = {
220
+ exec: this.exec,
221
+ command: this.command,
222
+ plugin_exec: this.plugin_exec,
223
+ plugin_command: this.plugin_command,
224
+ copyfile: this.copyfile,
225
+ copydir: this.copydir,
226
+ deletefile: this.deletefile,
227
+ deletedir: this.deletedir,
228
+ exist: this.exist,
229
+ listfile: this.listfile,
230
+ listdir: this.listdir,
231
+ createdir: this.createdir,
232
+ writefile: this.writefile,
233
+ readfile: this.readfile,
234
+ rename: this.rename,
235
+ }
236
+
237
+ this.env = {
238
+ has: has,
239
+ get: get,
240
+ set: set,
241
+
242
+ hasboolean: hasboolean,
243
+ getboolean: getboolean,
244
+ setboolean: setboolean,
245
+
246
+ hasnumber: hasnumber,
247
+ getnumber: getnumber,
248
+ setnumber: setnumber,
249
+
250
+ hasstring: hasstring,
251
+ getstring: getstring,
252
+ setstring: setstring,
253
+
254
+ hasobject: hasobject,
255
+ getobject: getobject,
256
+ setobject: setobject,
257
+
258
+ haslist: haslist,
259
+ getlist: getlist,
260
+ setlist: setlist,
261
+
262
+ hasselect: hasselect,
263
+ getselect: getselect,
264
+ getsleectlength: getselectlendth,
265
+ setselect: setselect,
266
+ }
267
+
268
+ this.message = {
269
+ messager: (m:any) => _messager(m.toString(), tag()),
270
+ messager_log: (m:any) => _messager_log(m.toString(), tag(), runtime()),
271
+ }
272
+
273
+ this.http = {
274
+ get: this.httpGet,
275
+ post: this.httpPost,
276
+ put: this.httpPut,
277
+ delete: this.httpDelete,
278
+ patch: this.httpPatch,
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Before running the js scripts, We must init first.\
284
+ * ! Otherwise it won't work or throw error
285
+ * @param _messager Message habndle
286
+ * @param _messager_log Message habndle with print on screen feature
287
+ * @param _clientos OS worker
288
+ * @param _para Parameter worker
289
+ * @param _getlib library getter method
290
+ * @param _getpara Parameter getter method
291
+ * @param _getjob Job getter method
292
+ */
293
+ static Init = (_messager: Messager, _messager_log: Messager, _clientos:ClientOS, _para:ClientJobParameter, _getlib:Getlib, _getpara:Getpara, _getjob:Getjob) => {
294
+ messager = _messager
295
+ messager_log = _messager_log
296
+ clientos = _clientos
297
+ para = _para
298
+ getlib = _getlib
299
+ getpara = _getpara
300
+ getjob = _getjob
301
+ }
302
+
303
+ /**
304
+ * Running js\
305
+ * With reference libraries\
306
+ * @param js js script text
307
+ * @param libs Libraries header names
308
+ * @returns Calcuate result
309
+ */
310
+ JavascriptExecuteWithLib = (javascript:string, libs:Array<string>, log?:Messager):Promise<any> => {
311
+ waiting = 0
312
+ let context = this.getJavascriptEnv(JavascriptLib.ALL, log)
313
+ let result = 0
314
+ context = Object.assign(context, { result: result })
315
+ let script = ''
316
+
317
+ const p = getlib?.() ?? undefined
318
+ if(p != undefined){
319
+ libs.forEach(x => {
320
+ const t = p.libs.find(y => y.name == x)
321
+ if(t != undefined) script += ("\n" + t.content + "\n")
322
+ })
323
+ }
324
+ script += javascript
325
+ const r = safeEval(script, context)
326
+
327
+ let time = -1
328
+ return new Promise<any>((resolve) => {
329
+ let handle:any = undefined
330
+ handle = setInterval(() => {
331
+ if(waiting == 0 && time > 1){
332
+ clearInterval(handle)
333
+ resolve(r)
334
+ }
335
+ time = time +1
336
+ }, 100);
337
+ })
338
+ }
339
+
340
+ /**
341
+ * Running js
342
+ * @param js js script text
343
+ * @returns Calcuate result
344
+ */
345
+ JavascriptExecute = (javascript:string, log?:Messager):Promise<any> => {
346
+ waiting = 0
347
+ let context = this.getJavascriptEnv(JavascriptLib.OS | JavascriptLib.MESSAGE | JavascriptLib.HTTP | JavascriptLib.PATH, log)
348
+ let result = 0
349
+ context = Object.assign(context, { result: result })
350
+ let script = ''
351
+ script += javascript
352
+ const r = safeEval(script, context)
353
+
354
+ let time = -1
355
+ return new Promise<any>((resolve) => {
356
+ let handle:any = undefined
357
+ handle = setInterval(() => {
358
+ if(waiting == 0 && time > 1){
359
+ clearInterval(handle)
360
+ resolve(r)
361
+ }
362
+ time = time +1
363
+ }, 100);
364
+ })
365
+ }
366
+
367
+ private getJavascriptEnv(flags:JavascriptLib = JavascriptLib.ALL, log?:Messager){
368
+ let javascriptEnv = {}
369
+ if((flags & JavascriptLib.PATH) == JavascriptLib.PATH) javascriptEnv = Object.assign(javascriptEnv, { path: this.path })
370
+ if((flags & JavascriptLib.OS) == JavascriptLib.OS) javascriptEnv = Object.assign(javascriptEnv, { os: this.os })
371
+ if((flags & JavascriptLib.ENV) == JavascriptLib.ENV) javascriptEnv = Object.assign(javascriptEnv, { env: this.env })
372
+ if((flags & JavascriptLib.MESSAGE) == JavascriptLib.MESSAGE) {
373
+ if(log){
374
+ javascriptEnv = Object.assign(javascriptEnv, {
375
+ messager: (m:any) => log(m.toString(), tag()),
376
+ messager_log: (m:any) => log(m.toString(), tag()),
377
+ })
378
+ }else{
379
+ javascriptEnv = Object.assign(javascriptEnv, { m: this.message })
380
+ }
381
+ }
382
+ if((flags & JavascriptLib.HTTP) == JavascriptLib.HTTP) javascriptEnv = Object.assign(javascriptEnv, { http: this.http })
383
+ javascriptEnv = Object.assign(javascriptEnv, {
384
+ setTimeout: setTimeout,
385
+ wait: this.wait,
386
+ sleep: this.sleep,
387
+ console: { log: log ? log : messager_log },
388
+ JSON: {
389
+ parse: JSON.parse,
390
+ stringify: JSON.stringify
391
+ },
392
+ math: {
393
+ floor: Math.floor,
394
+ abs: Math.abs,
395
+ round: Math.round,
396
+ ceil: Math.ceil,
397
+ PI: Math.PI,
398
+ E: Math.E,
399
+ pow: Math.pow,
400
+ random: Math.random,
401
+ max: Math.max,
402
+ min: Math.min,
403
+ trunc: Math.trunc,
404
+ log: Math.log,
405
+ log10: Math.log10,
406
+ log2: Math.log2,
407
+ exp: Math.exp,
408
+ expm1: Math.expm1,
409
+ sin: Math.sin,
410
+ sinh: Math.sinh,
411
+ cos: Math.cos,
412
+ cosh: Math.cosh,
413
+ tan: Math.tan,
414
+ tanh: Math.tanh,
415
+ asin: Math.asin,
416
+ asinh: Math.asinh,
417
+ acos: Math.acos,
418
+ acosh: Math.acosh,
419
+ atan: Math.atan,
420
+ atanh: Math.atanh,
421
+ atan2: Math.atan2,
422
+ }
423
+ })
424
+
425
+ return javascriptEnv
426
+ }
427
+ private filename(p:string, extension: boolean){
428
+ if(extension){
429
+ return path.basename(p)
430
+ }else{
431
+ return path.basename(p).replace(path.extname(p), "")
432
+ }
433
+ }
434
+ private extname(p:string){
435
+ return path.extname(p)
436
+ }
437
+ private dirname(p:string){
438
+ return path.dirname(p)
439
+ }
440
+ private exec(command:string, args:string, cwd?:string){
441
+ waiting += 1
442
+ clientos?.command_exec(command, args, cwd)
443
+ waiting -= 1
444
+ }
445
+ private command(command:string, args:string, cwd?:string){
446
+ waiting += 1
447
+ return clientos?.command_sync(command, args, cwd).then(() => {
448
+ waiting -= 1
449
+ }).catch(() => {
450
+ waiting -= 1
451
+ })
452
+ }
453
+ private plugin_exec(command:string, args:string){
454
+ waiting += 1
455
+ const cwd = path.join(os.homedir(), DATA_FOLDER, 'exe')
456
+ const cc = process.platform == "win32" ? command : "./" + command
457
+ clientos?.command_exec(cc, args, cwd)
458
+ waiting -= 1
459
+ }
460
+ private plugin_command(command:string, args:string){
461
+ waiting += 1
462
+ const cwd = path.join(os.homedir(), DATA_FOLDER, 'exe')
463
+ const cc = process.platform == "win32" ? command : "./" + command
464
+ return clientos?.command_sync(cc, args, cwd).then(() => {
465
+ waiting -= 1
466
+ }).catch(() => {
467
+ waiting -= 1
468
+ })
469
+ }
470
+ private copyfile(from:string, to:string){
471
+ clientos?.file_copy({from:from,to:to})
472
+ }
473
+ private copydir(from:string, to:string){
474
+ clientos?.dir_copy({from:from,to:to})
475
+ }
476
+ private deletefile(path:string){
477
+ clientos?.file_delete({path:path})
478
+ }
479
+ private deletedir(path:string){
480
+ clientos?.dir_delete({path:path})
481
+ }
482
+ private rename(from:string, to:string){
483
+ return clientos?.rename({from:from, to:to})
484
+ }
485
+ private exist(path:string){
486
+ return clientos?.fs_exist({path:path}) ?? false
487
+ }
488
+ private listfile(path:string){
489
+ return clientos?.dir_files({path:path})
490
+ }
491
+ private listdir(path:string){
492
+ return clientos?.dir_dirs({path:path})
493
+ }
494
+ private createdir(path:string){
495
+ clientos?.dir_create({path:path})
496
+ }
497
+ private writefile(path:string, data:string){
498
+ clientos?.file_write({ from: path, to: data })
499
+ }
500
+ private readfile(path:string){
501
+ return clientos?.file_read({path:path})
502
+ }
503
+ //#region Parameters
504
+ private async wait(time:number){
505
+ return new Promise((resolve) => setTimeout(resolve, time * 1000))
506
+ }
507
+ private async sleep(n:number){
508
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n*1000);
509
+ }
510
+ //#endregion
511
+ //#endregion
512
+ //#region Http
513
+ private async httpGet(url:string, p: any){
514
+ return this.httpGo('GET', url, p.toObject())
515
+ }
516
+ private async httpPost(url:string, p: any){
517
+ return this.httpGo('POST', url, p.toObject())
518
+ }
519
+ private async httpDelete(url:string, p: any){
520
+ return this.httpGo('DELETE', url, p.toObject())
521
+ }
522
+ private async httpPatch(url:string, p: any){
523
+ return this.httpGo('PATCH', url, p.toObject())
524
+ }
525
+ private async httpPut(url:string, p: any){
526
+ return this.httpGo('PUT', url, p.toObject())
527
+ }
528
+ private async httpGo(method:string, url:string, p: any) {
529
+ return fetch(url, {
530
+ method: method,
531
+ body: p
532
+ })
533
+ }
534
+ //#endregion
535
+ }