jsir 2.6.13 → 3.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.
package/deps/util.js CHANGED
@@ -2,7 +2,6 @@ const setting = require('./setting')
2
2
  const os = require('os')
3
3
  const home = os.homedir()
4
4
  const {exec, spawnSync, spawn} = require('child_process');
5
- const readline = require('readline')
6
5
  const fs = require('fs')
7
6
  const fp = require('fs').promises
8
7
  const BigNumber = require('bignumber.js');
@@ -24,7 +23,6 @@ let roomsDir;
24
23
  let dataDir;
25
24
  let console = global.console
26
25
 
27
-
28
26
  function isRunningInBackground() {
29
27
  return !(process.stdout.isTTY && process.stdin.isTTY);
30
28
  }
@@ -61,42 +59,73 @@ function hasFormat(str) {
61
59
  return /%[sdjifoO%]/.test(str);
62
60
  }
63
61
 
64
- class SyncQueue {
65
- constructor() {
66
- this.queue = [];
67
- this.isProcessing = false;
68
- }
69
- // 添加任务到队列
70
- async enqueue(task) {
71
- this.queue.push(task);
72
- await this.processQueue();
73
- }
74
- // 处理队列
75
- async processQueue() {
76
- if (this.isProcessing) return;
77
- this.isProcessing = true;
78
- while (this.queue.length > 0) {
79
- const task = this.queue.shift();
80
- try {
81
- await task();
82
- } catch (error) {
83
- console.$error('Error during SyncQueue operation:', error);
84
- }
62
+ async function taskQueue(task, { key, size = 1, runner = 1 } = {}) {
63
+ if (size < 1) throw new Error('invalid queue size');
64
+ if (runner < 1) throw new Error('invalid queue runner');
65
+ key = trim(key);
66
+
67
+ let ins = getOr(setting.syncQueues, key, {
68
+ name: key,
69
+ queue: [],
70
+ isProcessing: false,
71
+ waiter: null
72
+ });
73
+
74
+ ins.size = size;
75
+ ins.runner = runner;
76
+
77
+ // 如果队列满了,等待空位
78
+ while (ins.size !== Infinity && ins.queue.length >= ins.size) {
79
+ if (!ins.waiter) {
80
+ ins.waiter = {};
81
+ ins.waiter.promise = new Promise(resolve => ins.waiter.resolve = resolve);
85
82
  }
86
- this.isProcessing = false;
83
+ await ins.waiter.promise;
87
84
  }
85
+
86
+ ins.queue.push(task);
87
+ _processQueue(ins);
88
88
  }
89
89
 
90
- const syncQueues = {}
91
- function syncQueue(task, key) {
92
- `
93
- 同步队列分发器
94
- task: function
95
- key: 根据key值区分不同队列, 可选
96
- return Promise
97
- `
98
- let queue = getOrFn(syncQueues, trim(key), () => new SyncQueue())
99
- return queue.enqueue(task)
90
+ async function _processQueue(ins) {
91
+ if (ins.isProcessing) return;
92
+ ins.isProcessing = true;
93
+
94
+ const runs = new Set();
95
+
96
+ while (ins.queue.length > 0 || runs.size > 0) {
97
+ // 启动任务,控制并发数量
98
+ while (ins.queue.length > 0 && runs.size < ins.runner) {
99
+ const task = ins.queue.shift();
100
+
101
+ const promise = (async () => {
102
+ try {
103
+ await task();
104
+ } catch (error) {
105
+ console.$error(`Error during [${ins.name}] SyncQueue`, error);
106
+ }
107
+ })();
108
+
109
+ promise.finally(() => {
110
+ runs.delete(promise);
111
+
112
+ // 唤醒等待者
113
+ if (ins.waiter && ins.queue.length < ins.size) {
114
+ ins.waiter.resolve();
115
+ ins.waiter = null;
116
+ }
117
+ });
118
+
119
+ runs.add(promise);
120
+ }
121
+
122
+ if (runs.size > 0) {
123
+ await Promise.race(runs);
124
+ }
125
+ }
126
+
127
+ ins.isProcessing = false;
128
+ delete setting.syncQueues[ins.name];
100
129
  }
101
130
 
102
131
  const $log = createLimitLogger(`${setting.name}.log`, {
@@ -107,15 +136,18 @@ const $error = createLimitLogger(`${setting.name}.error`, {
107
136
  error: true,
108
137
  syncLogs: [$log]
109
138
  });
139
+ const $errorOnly = createLimitLogger(`${setting.name}.error`, {
140
+ logInfo: false,
141
+ error: true
142
+ });
110
143
 
111
144
  const roomLog = createLimitLogger(`room.log`, {
112
- logInfo: false,
113
- syncLogs: [$log]
145
+ logInfo: false
114
146
  });
115
147
  const roomError = createLimitLogger(`room.error`, {
116
148
  logInfo: false,
117
149
  error: true,
118
- syncLogs: [roomLog, $error]
150
+ syncLogs: [roomLog]
119
151
  });
120
152
 
121
153
  let _globalDraft= createLimitLogger(`draft.log`, {
@@ -246,23 +278,23 @@ function createConfig(uniqueName) {
246
278
  return result;
247
279
  }
248
280
 
249
- function createConsole(uniqueName) {
281
+ function createConsole(uniqueName, _logToFile, _errorToFile) {
250
282
  let result = Object.assign({}, _console);
251
- result.$log = $log;
252
- result.$error = $error;
283
+ let logToFile = _logToFile || $log;
284
+ let errorToFile = _errorToFile || $error;
253
285
  result.$draft = draftLog;
254
286
  result.$abort = () => false;
255
287
  if (uniqueName) {
256
288
  let pair = parseUniqueName(uniqueName)
257
289
  let fileName = pair[0] + '/' + pair[1].split(".")[0]
258
- result.$log = createLimitLogger(fileName + '.log', {
290
+ logToFile = createLimitLogger(fileName + '.log', {
259
291
  logInfo: false,
260
- syncLogs: [roomLog]
292
+ syncLogs: [$log]
261
293
  });
262
- result.$error = createLimitLogger(fileName + '.error', {
294
+ errorToFile = createLimitLogger(fileName + '.error', {
263
295
  logInfo: false,
264
296
  error: true,
265
- syncLogs: [result.$log, roomError]
297
+ syncLogs: [logToFile, $errorOnly]
266
298
  });
267
299
  const promptId = setting.promptId;
268
300
  result.$abort = () => promptId !== setting.promptId;
@@ -272,8 +304,14 @@ function createConsole(uniqueName) {
272
304
  if (uniqueName) {
273
305
  promptId = setting.promptId;
274
306
  }
275
- for (let key of Object.keys(_consoleFns)) {
276
- result[key] = (...args) => {
307
+ for (let key of [...Object.keys(_consoleFns), ...Object.keys(_consoleFns).map(i => '$' + i)]) {
308
+ let fnKey = key;
309
+ result[fnKey] = (...args) => {
310
+ let is$ = false;
311
+ if (fnKey.startsWith("$")) {
312
+ is$ = true;
313
+ key = fnKey.substring(1)
314
+ }
277
315
  if (promptId !== setting.promptId) {
278
316
  quite = true;
279
317
  }
@@ -295,15 +333,15 @@ function createConsole(uniqueName) {
295
333
  }
296
334
  setting.lastChangeFlag = flag
297
335
  }
298
- if (uniqueName && quite) {
336
+ if (is$ || (uniqueName && quite) || (setting.lastOutput && !hasFlag && 'debug' === key)) {
299
337
  if ((key === 'table' || key === 'nable') && typeof args[0] === "object") {
300
338
  args = ['', ...args]
301
339
  }
302
340
  let _args = _consoleFns[key].args(args);
303
341
  if ("error" === key) {
304
- result.$error(..._args)
342
+ errorToFile(..._args)
305
343
  } else {
306
- result.$log(..._args)
344
+ logToFile(..._args)
307
345
  }
308
346
  } else {
309
347
  let _args = _consoleFns[key].args(args);
@@ -321,12 +359,7 @@ function createConsole(uniqueName) {
321
359
  }
322
360
 
323
361
  _consoleFns[key].fn(..._args);
324
-
325
- if (hasFlag) {
326
- setting.lastOutput = lastOutput;
327
- } else {
328
- setting.lastOutput = null;
329
- }
362
+ setting.lastOutput = lastOutput;
330
363
  }
331
364
  }
332
365
  }
@@ -594,7 +627,7 @@ async function draftModify(fLine) {
594
627
  if (isEdit && results.length === 1) {
595
628
  let tempPath = getLibDataDir() + "/log/draft.temp";
596
629
  fs.writeFileSync(tempPath, results[0].split(/\n/).slice(1).join('\n'))
597
- await eia(getEditor(), [tempPath])
630
+ await eia(getEditor(), [`"${tempPath}"`], true)
598
631
  let tempText = String(fs.readFileSync(tempPath))
599
632
  let lineRange = lineRanges.get(results[0]);
600
633
  let before = allLines.filter((_, index) => {
@@ -640,6 +673,10 @@ function getEditor() {
640
673
  return getConfig("defaultEditor", "vi")
641
674
  }
642
675
 
676
+ function getFileOpenExe() {
677
+ return getConfig("defaultExe", "open")
678
+ }
679
+
643
680
  function timeStr(fmt, date) {
644
681
  return dayJs(date || new Date()).format(fmt || 'YYYY-MM-DD HH:mm:ss')
645
682
  }
@@ -663,31 +700,29 @@ function getVl(...obj) {
663
700
  }
664
701
  }
665
702
 
666
- const $fnCache = {}
667
703
  async function cacheFn(key, fn, validMs = 0, awaitRefresh = true) {
668
- getOr($fnCache, key, {
669
- validMsTime: 0
704
+ const cacheItem = getOr(setting.fnCache, key, {
705
+ createdAt: 0,
670
706
  });
671
- if (Date.now() <= $fnCache[key].validMsTime) {
672
- // do nothing
673
- } else {
674
- $fnCache[key].validMsTime = Infinity;
675
- $fnCache[key].promise = (async () => {
707
+ if (Date.now() - cacheItem.createdAt > validMs) {
708
+ // 临时设为已创建,防止并发请求多次调用 fn
709
+ cacheItem.createdAt = Infinity;
710
+ cacheItem.promise = (async () => {
676
711
  let val;
677
712
  try {
678
713
  val = await fn()
679
714
  } finally {
680
- $fnCache[key].validMsTime = 0
715
+ cacheItem.createdAt = 0; // 恢复为无效状态
681
716
  }
682
- $fnCache[key].val = val;
683
- $fnCache[key].valInit = true;
684
- $fnCache[key].validMsTime = Date.now() + validMs;
717
+ cacheItem.val = val;
718
+ cacheItem.valInit = true;
719
+ cacheItem.createdAt = Date.now(); // 设置新的创建时间
685
720
  })();
686
721
  }
687
- if (awaitRefresh || !$fnCache[key].valInit) {
688
- await $fnCache[key].promise;
722
+ if (awaitRefresh || !cacheItem.valInit) {
723
+ await cacheItem.promise;
689
724
  }
690
- return $fnCache[key].val
725
+ return cacheItem.val;
691
726
  }
692
727
 
693
728
  function getFnVl(...fns) {
@@ -703,9 +738,9 @@ function getFnVl(...fns) {
703
738
  function randomInt(min,max){
704
739
  switch(arguments.length){
705
740
  case 1:
706
- return parseInt(String(Math.random()*min+1),10);
741
+ return Math.floor(Math.random()*min+1);
707
742
  case 2:
708
- return parseInt(String(Math.random()*(max-min+1)+min),10);
743
+ return Math.floor(Math.random()*(max-min+1)+min);
709
744
  default:
710
745
  return 0;
711
746
  }
@@ -713,8 +748,12 @@ function randomInt(min,max){
713
748
 
714
749
  async function timeLimit(proms, mills) {
715
750
  let tIds = []
716
- let result = await Promise.race([Promise.all(proms), sleep(mills, tIds)])
717
- clearTimeout(tIds[0])
751
+ let result
752
+ try {
753
+ result = await Promise.race([Promise.all(proms), sleep(mills, tIds)])
754
+ } finally {
755
+ clearTimeout(tIds[0])
756
+ }
718
757
  if (!result) {
719
758
  throw new Error(`timeLimit: exceed ${mills} ms`)
720
759
  }
@@ -807,7 +846,7 @@ function createLimitLogger(fileName, {
807
846
  if (time) {
808
847
  text = `${timeStr('YYYY-MM-DD HH:mm:ss.SSS')} ${String(process.pid%1000).padStart(3, '0')}> ${text}`
809
848
  }
810
- syncQueue(() => fp.appendFile(logPath, text + '\n'), logPath)
849
+ fp.appendFile(logPath, text + '\n')
811
850
  let _minNum = (Date.now()/(1000 * 60 * 10)).toFixed(0)
812
851
  if (_minNum !== minNum) {
813
852
  minNum = _minNum
@@ -850,17 +889,20 @@ function dataFile(fileName, fn, fmt, defaultObj = {}, returnStr = false) {
850
889
  if (fileName.startsWith("/")) {
851
890
  path = fileName
852
891
  } else {
892
+ if (fileName.includes('/')) {
893
+ throw 'invalid fileName';
894
+ }
853
895
  path = dataDir + "/" + fileName
854
896
  }
855
897
  let prefixStr = returnStr ? 'return ' : '';
856
898
  if (!fs.existsSync(path)) {
857
- mkdir(dataDir)
899
+ createDirs(dataDir)
858
900
  fs.writeFileSync(path, prefixStr + JSON.stringify(defaultObj, null, fmt ? 2:null))
859
901
  }
860
902
 
861
903
  let text = String(fs.readFileSync(path));
862
904
  if (returnStr) {
863
- text = text.replace(/^return\s+/, '')
905
+ text = text.replace(/^return\s*/, '')
864
906
  }
865
907
  let obj = JSON.parse(text)
866
908
  if (!fn) {
@@ -900,13 +942,16 @@ async function fileJson(key, fn = null, fmt = true) {
900
942
  `
901
943
  let dataDir = getDataDir()
902
944
  let fileName = trim(key)
945
+ if (fileName.includes('/')) {
946
+ throw 'invalid fileName';
947
+ }
903
948
  let path = dataDir + "/" + fileName;
904
949
  let homeDir = setting.workspaceMap[setting.defaultSpace]
905
950
  let isInit = getJsirTypeKey(fileName) === setting.initKey;
906
951
  if (isInit) {
907
952
  path = homeDir + '/' + toJsirFileName(fileName);
908
953
  }
909
- let prefixStr = isInit ? 'return ' : '';
954
+ let prefixStr = isInit ? 'module.exports = ' : '';
910
955
  let result = null;
911
956
  await fileLock(path, async () => {
912
957
  if (!await fileExist(path)) {
@@ -914,7 +959,7 @@ async function fileJson(key, fn = null, fmt = true) {
914
959
  }
915
960
  let text = String(await fp.readFile(path));
916
961
  if (isInit) {
917
- text = text.replace(/^return\s+/, '')
962
+ text = text.replace(/^module\.exports =\s*/, '')
918
963
  }
919
964
  result = JSON.parse(text)
920
965
  if (!fn) {
@@ -1097,7 +1142,7 @@ function _getConfig(key, defaultVal, uniqueName) {
1097
1142
  return config
1098
1143
  }
1099
1144
  if (uniqueName) {
1100
- console.debug(`use ${uniqueName} config "${key}"`)
1145
+ console.$debug(`use ${uniqueName} config "${key}"`)
1101
1146
  }
1102
1147
  let writeFlag = false
1103
1148
  if (!(key in config)) {
@@ -1122,7 +1167,7 @@ function _getConfig(key, defaultVal, uniqueName) {
1122
1167
  return val
1123
1168
  }
1124
1169
 
1125
- function setConfig(key, val) {
1170
+ function setConfig(key, val, uniqueName) {
1126
1171
  if (typeof val === "string") {
1127
1172
  val = trim(val)
1128
1173
  }
@@ -1131,6 +1176,9 @@ function setConfig(key, val) {
1131
1176
  configInit[key] = val
1132
1177
  }
1133
1178
  let configFile = getLibDataDir() + '/config.json';
1179
+ if (uniqueName) {
1180
+ configFile = getConfigDir() + "/" + md5(uniqueName + (global.$TEST ? ".test":"")) + ".json"
1181
+ }
1134
1182
  if (!fs.existsSync(configFile)) {
1135
1183
  fs.writeFileSync(configFile, JSON.stringify(configInit, null, 2));
1136
1184
  return val
@@ -1369,7 +1417,6 @@ function removeFirst(array, obj) {
1369
1417
  return array.splice(deleteIndex, 1)
1370
1418
  }
1371
1419
 
1372
- const _tipsOnRm = {}
1373
1420
  function setTips(key, value, onRm) {
1374
1421
  `
1375
1422
  可以设置相同的key, value值,会在左侧提示符体现出来
@@ -1378,17 +1425,22 @@ function setTips(key, value, onRm) {
1378
1425
  throw "invalid tip key";
1379
1426
  }
1380
1427
  key = trim(key)
1381
- getOr(setting.tips, key, []).push(vl(value) ? value:'');
1428
+ value = vl(value) ? value:key
1429
+ if (typeof value === 'string') {
1430
+ value = trim(value)
1431
+ }
1432
+ getOr(setting.tips, key, []).push(value);
1382
1433
  if (onRm) {
1383
- getOr(_tipsOnRm, key, []).push(onRm)
1434
+ getOr(setting.tipsOnRm, key, []).push(onRm)
1384
1435
  }
1385
1436
  }
1386
1437
 
1387
1438
  function delTips(...keys) {
1439
+ keys = keys.map(trim).filter(i => i)
1388
1440
  let pros = []
1389
1441
  for (let key of Object.keys(setting.tips)) {
1390
1442
  if (keys.length === 0) {
1391
- if (key === 'DEBUG') {
1443
+ if (['TEST', 'DEBUG'].includes(key)) {
1392
1444
  continue
1393
1445
  }
1394
1446
  delete setting.tips[key]
@@ -1399,8 +1451,8 @@ function delTips(...keys) {
1399
1451
  }
1400
1452
  }
1401
1453
  if (keys.length === 0) {
1402
- for (let key of Object.keys(_tipsOnRm)) {
1403
- if (key === 'DEBUG') {
1454
+ for (let key of Object.keys(setting.tipsOnRm)) {
1455
+ if (['TEST', 'DEBUG'].includes(key)) {
1404
1456
  continue
1405
1457
  }
1406
1458
  pros.push(tipsOnRmCallback(key))
@@ -1415,6 +1467,9 @@ function delTips(...keys) {
1415
1467
  function hasTips(key, value) {
1416
1468
  key = trim(key)
1417
1469
  if (vl(value)) {
1470
+ if (typeof value === 'string') {
1471
+ value = trim(value)
1472
+ }
1418
1473
  return setting.tips.hasOwnProperty(key) && setting.tips[key].includes(value);
1419
1474
  } else {
1420
1475
  return setting.tips.hasOwnProperty(key);
@@ -1426,18 +1481,18 @@ function tipKeys() {
1426
1481
  }
1427
1482
 
1428
1483
  function tipsOnRmCallback(key) {
1429
- let callbacks = _tipsOnRm[key]
1430
- delete _tipsOnRm[key]
1484
+ let callbacks = setting.tipsOnRm[key]
1485
+ delete setting.tipsOnRm[key]
1431
1486
  const pros = [];
1432
1487
  if (callbacks && callbacks.length > 0) {
1433
1488
  for (let callback of callbacks) {
1434
1489
  try {
1435
1490
  let result = callback()
1436
1491
  if (getType(result) === 'Promise') {
1437
- pros.push(result.catch(e => $error(`[${key}] OnRmCallback`, e)))
1492
+ pros.push(result.catch(e => console.$error(`[${key}] OnRmCallback`, e)))
1438
1493
  }
1439
1494
  } catch (e) {
1440
- $error(`[${key}] OnRmCallback`, e)
1495
+ console.$error(`[${key}] OnRmCallback`, e)
1441
1496
  }
1442
1497
  }
1443
1498
  }
@@ -1446,66 +1501,12 @@ function tipsOnRmCallback(key) {
1446
1501
  }
1447
1502
  }
1448
1503
 
1449
- async function timer(key, ms, fn, label, printInfo = true){
1450
- `
1451
- 定时控制器
1452
- clear tips退出
1453
- fn方法返回true退出
1454
- return void;
1455
- `
1456
- if (!(key && ms && fn)) {
1457
- throw 'invalid args'
1458
- }
1459
- let ids = []
1460
- let showLabel = vl(label) ? label : key;
1461
- let flag = [...new Set([key, showLabel].map(trim))].filter(i => i).join(' ')
1462
- setTips(key, showLabel, () => {
1463
- clearTimeout(ids[0])
1464
- ids[0] = false
1465
- if (printInfo) {
1466
- console.info(`${flag} stop`)
1467
- }
1468
- })
1469
- if (printInfo) {
1470
- console.info(`${flag} start`)
1471
- }
1472
- await timeLoop(fn, ms, ids)
1473
- }
1474
-
1475
- const cleanFiles = {}
1476
- function fileCleaner(path, maxChars) {
1477
- `
1478
- 文件清理器,提示符为sys f
1479
- `
1480
- cleanFiles[path] = Date.now() + (1000 * 60)
1481
- let flag = "sys"
1482
- if (!setting.tips.hasOwnProperty(flag)) {
1483
- timer(flag, 1000 * 9, () => {
1484
- for (let aPath of Object.keys(cleanFiles)) {
1485
- if (cleanFiles[aPath] < Date.now()) {
1486
- delete cleanFiles[path]
1487
- continue;
1488
- }
1489
- try {
1490
- cleanFile(path, maxChars)
1491
- } catch (e) {
1492
- console.$error(e)
1493
- }
1494
- }
1495
- }, "f", false);
1496
- }
1497
- }
1498
-
1499
- function cleanFile(path, maxChars = 9 * 1024 * 1024) {
1500
- `
1501
- 进入文件操作队列,不会同步去做
1502
- `
1504
+ async function cleanFile(path, maxChars = 9 * 1024 * 1024) {
1503
1505
  path = trim(path);
1504
1506
  if (!path) {
1505
1507
  return
1506
1508
  }
1507
- syncQueue(async () => {
1508
- maxChars = maxChars || 1024 * 1024
1509
+ await fileLock(path, async () => {
1509
1510
  // 获取文件信息
1510
1511
  let stats;
1511
1512
  try {
@@ -1519,46 +1520,15 @@ function cleanFile(path, maxChars = 9 * 1024 * 1024) {
1519
1520
  if (fileSize <= maxChars) {
1520
1521
  return;
1521
1522
  }
1522
- await fileLock(path, async () => {
1523
- const bakFile = `${path}.bak`;
1524
- // 备份日志
1525
- try {
1526
- await fp.rename(path, bakFile);
1527
- } catch (e) {
1528
- console.$error(`Failed to rename ${path} -> ${bakFile}`, e);
1529
- }
1530
- }, false)
1531
- }, path)
1532
- }
1533
1523
 
1534
- function lisPid(name) {
1535
- `
1536
- 调用此方法后,将会创建一个 ${dir}/${name}.pid 文件
1537
- 删除该文件,当前进程将会结束
1538
- `
1539
- let dir = getLisPidDir()
1540
- let pidFile = `${dir}/${name}.pid`
1541
- fs.writeFileSync(pidFile, String(process.pid))
1542
- let beforeExit = () => {
1543
- fs.writeFileSync(pidFile, '')
1544
- process.exit()
1545
- }
1546
- process.on('SIGINT', beforeExit)
1547
- process.on('exit', beforeExit)
1548
- timeLoop(()=>{
1549
- if (fs.existsSync(pidFile) && String(process.pid) !== String(fs.readFileSync(pidFile))) {
1550
- process.exit(0)
1524
+ const bakFile = `${path}.bak`;
1525
+ // 备份日志
1526
+ try {
1527
+ await fp.rename(path, bakFile);
1528
+ } catch (e) {
1529
+ console.$error(`Failed to rename ${path} -> ${bakFile}`, e);
1551
1530
  }
1552
- }, 1000 * 3)
1553
- }
1554
-
1555
- function getLisPidDir() {
1556
- let home = getLibDataDir()
1557
- let dir = `${home}/pid`
1558
- try {
1559
- fs.mkdirSync(dir)
1560
- } catch(e) {}
1561
- return dir
1531
+ }, false)
1562
1532
  }
1563
1533
 
1564
1534
  function isPidAlive(pid) {
@@ -1574,72 +1544,6 @@ function isPidAlive(pid) {
1574
1544
  }
1575
1545
  }
1576
1546
 
1577
- function linuxAskAndKill(...keys){
1578
- let rl = readline.createInterface({
1579
- input: process.stdin,
1580
- output: process.stdout
1581
- })
1582
- rl.on('line', (str) => {
1583
- str = trim(str)
1584
- if (str) {
1585
- if (['q', 'exit', 'bye'].indexOf(str) !== -1) {
1586
- rl.close()
1587
- return;
1588
- }
1589
- e(`kill -9 ${str}`).then(() => {
1590
- _linuxAskAndKill(rl, keys)
1591
- })
1592
- } else {
1593
- process.stdout.write("> ")
1594
- }
1595
- })
1596
- rl.on('close', () => {
1597
- process.exit(0)
1598
- })
1599
- _linuxAskAndKill(rl, keys)
1600
- }
1601
-
1602
- function timeLoop(fn, timestamp, lastIds = []){
1603
- `
1604
- fn的结果===true, 则退出;
1605
- lastIds[0]===false, 则退出;
1606
- return lastIds;
1607
- `
1608
- let val
1609
- try {
1610
- val = fn()
1611
- } catch (e) {
1612
- $error(e)
1613
- }
1614
- if (vl(val) && getType(val) === 'Promise') {
1615
- return val.then(result => {
1616
- if (lastIds[0] !== false && result !== true) {
1617
- lastIds[0] = setTimeout(() => timeLoop(fn, timestamp, lastIds), timestamp)
1618
- }
1619
- return lastIds
1620
- }).catch(e => {
1621
- $error(e)
1622
-
1623
- if (lastIds[0] !== false) {
1624
- lastIds[0] = setTimeout(() => timeLoop(fn, timestamp, lastIds), timestamp)
1625
- }
1626
- return lastIds
1627
- })
1628
- } else {
1629
- if (lastIds[0] !== false && val !== true) {
1630
- lastIds[0] = setTimeout(() => timeLoop(fn, timestamp, lastIds), timestamp)
1631
- }
1632
- return lastIds
1633
- }
1634
- }
1635
-
1636
- function _linuxAskAndKill(rl, keys){
1637
- e(`ps -ef | ${keys.filter(item => item).map(item => 'grep ' + item).join(' | ')}`).then(resp => {
1638
- console.log(resp.replace(/\n\s+/g, '\n'))
1639
- process.stdout.write("> ")
1640
- })
1641
- }
1642
-
1643
1547
  function trim(obj) {
1644
1548
  `
1645
1549
  返回值一定是string
@@ -1801,14 +1705,14 @@ function range(start, end, step) {
1801
1705
  }
1802
1706
 
1803
1707
  function getOr(obj, key, defaultVal) {
1804
- if (!vl(obj[key])) {
1708
+ if (!Object.prototype.hasOwnProperty.call(obj, key) || !vl(obj[key])) {
1805
1709
  obj[key] = defaultVal
1806
1710
  }
1807
1711
  return obj[key]
1808
1712
  }
1809
1713
 
1810
1714
  function getOrFn(obj, key, defaultValFn) {
1811
- if (!vl(obj[key])) {
1715
+ if (!Object.prototype.hasOwnProperty.call(obj, key) || !vl(obj[key])) {
1812
1716
  obj[key] = defaultValFn()
1813
1717
  }
1814
1718
  return obj[key]
@@ -2111,7 +2015,10 @@ function errorTag(e, tag) {
2111
2015
  }
2112
2016
  let newError = isError(e) ? e:new Error(e);
2113
2017
  let stack = newError.stack
2114
- newError.stack = stack + `\n at ${tag}`
2018
+ let append = ` at ${tag}`;
2019
+ if (!stack.endsWith(append)) {
2020
+ newError.stack = stack + '\n' + append
2021
+ }
2115
2022
  return newError
2116
2023
  }
2117
2024
 
@@ -2157,7 +2064,8 @@ function getTextComments(text) {
2157
2064
 
2158
2065
  function wrapperJsirText(text) {
2159
2066
  return text
2160
- .replace(/^require\s*\(\s*(["'`][ei][^a-zA-Z]+)/mg, 'await $require($2')
2067
+ .replace(/^require\s*\(\s*(["'`][ei][^a-zA-Z]+)/mg, 'await $require($1')
2068
+ .replace(/([\s=;]require\s*\(\s*["'`])\.\/([ei][^a-zA-Z]+)/g, '$1$2')
2161
2069
  .replace(/([\s=;])require\s*\(\s*(["'`][ei][^a-zA-Z]+)/g, '$1await $require($2')
2162
2070
  .replace(/([\s=;])import\s*\(/g, '$1$import(')
2163
2071
  .replace(/^import\s*\(/mg, '$import(')
@@ -2165,24 +2073,6 @@ function wrapperJsirText(text) {
2165
2073
  ;
2166
2074
  }
2167
2075
 
2168
- async function terminalRun(cmd) {
2169
- `
2170
- 打开新的苹果终端并执行命令
2171
- `
2172
- const scriptText = `tell application "Terminal"
2173
- activate
2174
- do script "${cmd}"
2175
- end tell
2176
- `;
2177
- let tempScriptPath = getTempDir() + `/terminalRun_${Date.now()}.scpt`
2178
- fs.writeFileSync(tempScriptPath, scriptText);
2179
- try {
2180
- console.log(await e(`osascript ${tempScriptPath}`))
2181
- } finally {
2182
- fs.unlinkSync(tempScriptPath)
2183
- }
2184
- }
2185
-
2186
2076
  function getKeyTips() {
2187
2077
  let items = [];
2188
2078
  for (let key of Object.keys(setting.tips)) {
@@ -2200,7 +2090,7 @@ function getValTips() {
2200
2090
  if (key.startsWith("_")) {
2201
2091
  continue;
2202
2092
  }
2203
- let val = setting.tips[key].map(i => {
2093
+ let vals = setting.tips[key].map(i => {
2204
2094
  if (typeof i === 'function') {
2205
2095
  try {
2206
2096
  i = i();
@@ -2214,8 +2104,18 @@ function getValTips() {
2214
2104
  item = `[${item}]`;
2215
2105
  }
2216
2106
  return item;
2217
- }).join("|");
2218
- items.push(val || trim(key))
2107
+ });
2108
+ let valMap = {}
2109
+ for (let val of vals) {
2110
+ valMap[val] = getOr(valMap, val, 0) + 1
2111
+ }
2112
+ items.push(Object.keys(valMap).map(i => {
2113
+ if (valMap[i] > 1) {
2114
+ return i + '*' + valMap[i]
2115
+ } else {
2116
+ return i;
2117
+ }
2118
+ }).join("|"))
2219
2119
  }
2220
2120
  return items
2221
2121
  }
@@ -2263,13 +2163,21 @@ function interceptStdStreams() {
2263
2163
  };
2264
2164
  }
2265
2165
 
2266
- function formatUptime() {
2267
- const uptimeSeconds = process.uptime();
2166
+ function formatMb(mb) {
2167
+ if (typeof mb !== 'number') {
2168
+ return mb;
2169
+ }
2170
+ return mb > 1024 ? ((mb/1024).toFixed(2) + 'G') : (mb + 'M')
2171
+ }
2268
2172
 
2269
- const days = Math.floor(uptimeSeconds / (3600 * 24)); // 计算天数
2270
- const hours = Math.floor((uptimeSeconds % (3600 * 24)) / 3600); // 计算小时
2271
- const minutes = Math.floor((uptimeSeconds % 3600) / 60); // 计算分钟
2272
- const seconds = Math.floor(uptimeSeconds % 60); // 计算秒数
2173
+ function formatSec(sec) {
2174
+ if (typeof sec !== 'number') {
2175
+ return sec;
2176
+ }
2177
+ const days = Math.floor(sec / (3600 * 24)); // 计算天数
2178
+ const hours = Math.floor((sec % (3600 * 24)) / 3600); // 计算小时
2179
+ const minutes = Math.floor((sec % 3600) / 60); // 计算分钟
2180
+ const seconds = Math.floor(sec % 60); // 计算秒数
2273
2181
 
2274
2182
  if (days > 0) {
2275
2183
  return `${days}d${hours}h`;
@@ -2286,22 +2194,28 @@ function aop(items, fn, args) {
2286
2194
  let runners = [fn]
2287
2195
  let i = 0
2288
2196
  for (let item of [...items].reverse()) {
2197
+ if (!item.run) {
2198
+ throw `${item.constructor.name} not suitable for sync function`
2199
+ }
2289
2200
  const j = i;
2290
- runners.push((args) => item.run(runners[j], args))
2201
+ runners.push(() => item.run(runners[j], args))
2291
2202
  i ++;
2292
2203
  }
2293
- return runners[i](args)
2204
+ return runners[i]()
2294
2205
  }
2295
2206
 
2296
2207
  async function aopAsync(items, fn, args) {
2297
2208
  let runners = [fn]
2298
2209
  let i = 0
2299
2210
  for (let item of [...items].reverse()) {
2211
+ if (!item.runAsync) {
2212
+ throw `${item.constructor.name} not suitable for async function`
2213
+ }
2300
2214
  const j = i;
2301
- runners.push(async (args) => await item.runAsync(runners[j], args))
2215
+ runners.push(async () => await item.runAsync(runners[j], args))
2302
2216
  i ++;
2303
2217
  }
2304
- return await runners[i](args)
2218
+ return await runners[i]()
2305
2219
  }
2306
2220
 
2307
2221
  function getMd5Key(str) {
@@ -2320,7 +2234,7 @@ const tipFns = {
2320
2234
  }
2321
2235
 
2322
2236
  module.exports = {
2323
- formatUptime,
2237
+ formatSec,
2324
2238
  wrapperJsirText,
2325
2239
  run,
2326
2240
  reget,
@@ -2329,13 +2243,8 @@ module.exports = {
2329
2243
  ee,
2330
2244
  regEach,
2331
2245
  runSync,
2332
- linuxAskAndKill,
2333
2246
  cleanFile,
2334
- fileCleaner,
2335
2247
  getLibDataDir,
2336
- lisPid,
2337
- timeLoop,
2338
- getLisPidDir,
2339
2248
  mkdir,
2340
2249
  bAdd,
2341
2250
  bDiv,
@@ -2400,12 +2309,11 @@ module.exports = {
2400
2309
  createConsole,
2401
2310
  setTips,
2402
2311
  delTips,
2403
- timer,
2404
2312
  createConfig,
2405
2313
  getEditor,
2406
2314
  setConfig,
2407
2315
  trimEmptyLine,
2408
- syncQueue,
2316
+ taskQueue,
2409
2317
  getLogDir,
2410
2318
  getConfigDir,
2411
2319
  getFullPath,
@@ -2418,7 +2326,6 @@ module.exports = {
2418
2326
  getAlias,
2419
2327
  createDirs,
2420
2328
  getTempDir,
2421
- terminalRun,
2422
2329
  eia,
2423
2330
  getKeyTips,
2424
2331
  getValTips,
@@ -2440,5 +2347,8 @@ module.exports = {
2440
2347
  getMd5Key,
2441
2348
  isMd5Key,
2442
2349
  terminalTitle,
2443
- tipFns
2350
+ tipFns,
2351
+ getFileOpenExe,
2352
+ roomConsole: createConsole(null, roomLog, roomError),
2353
+ formatMb
2444
2354
  }