jsir 2.6.5 → 2.6.7

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/cmd/oaa.js CHANGED
@@ -13,7 +13,7 @@ const {
13
13
  getFullPath, parseUniqueName, toUniqueName, isJsirFileName, toJsirFileName,
14
14
  getAlias, wrapperJsirText, eia, getKeyTips, getValTips, getJsirTypeKey,
15
15
  createDetachedProcess, interceptStdStreams,
16
- draftModify, isRunningInBackground, fileJson, fileLock
16
+ draftModify, isRunningInBackground, fileJson, fileLock, processLock
17
17
  } = $lib;
18
18
  const _args = process.argv.slice(2).map(trim);
19
19
  const evalCode = require('../deps/evalCode')
@@ -130,7 +130,8 @@ const $data = {
130
130
  key: () => Object.keys(_data),
131
131
  has: (key) => vl(_data[key]),
132
132
  json: fileJson,
133
- lock: fileLock
133
+ lock: fileLock,
134
+ pLock: processLock
134
135
  }
135
136
  const $homeDir = getLibDataDir()
136
137
 
@@ -442,6 +443,7 @@ function _nextLine(callback, promptStr, hidden, resolve, end, isText) {
442
443
  _rl.removeAllListeners('line')
443
444
  _rl.on('line', lineHandler)
444
445
  _rl.prompt()
446
+ setting.lastOutput = null;
445
447
  }
446
448
 
447
449
  async function nextText(callback, end, hidden) {
@@ -703,7 +705,6 @@ async function execLibFn(fn, fnArgs) {
703
705
  }
704
706
 
705
707
  async function joinServer(uniqueName, isApi, exportLib) {
706
- Room.onRoom();
707
708
  if (!setting.serviceReg.test(uniqueName)) {
708
709
  throw 'invalid service name';
709
710
  }
@@ -730,6 +731,7 @@ async function joinServer(uniqueName, isApi, exportLib) {
730
731
  }, isApi)
731
732
  }
732
733
  console.msg(uniqueName, `has become a ${isApi ? "api ":""}service.`);
734
+ Room.onRoom();
733
735
  }
734
736
 
735
737
  async function offServer(uniqueName) {
@@ -913,8 +915,10 @@ function dealStarCmd(rows, cmd, filterStr) {
913
915
  let commentContent = trimText(comments.join('\n'));
914
916
  let row = getInfo(functionContent, fnName, fnType);
915
917
  let pair = parseUniqueName(cmd);
916
- row.value = infoStr(pair[0] + '/' + trimJsirFileName(pair[1])) + ' ' +
917
- getCmdMd5Key(parseUniqueName(cmd)[1]) + '\n' + [commentContent, row.value].filter(i => trim(i)).join("\n");
918
+ row.value = [
919
+ infoStr(pair[0] + '/' + trimJsirFileName(pair[1])) + ' ' + getCmdMd5Key(parseUniqueName(cmd)[1]),
920
+ [commentContent, row.value].filter(i => trim(i)).join("\n")
921
+ ].filter(i => trim(i)).join("\n");
918
922
  rows.push(row);
919
923
 
920
924
  resetState();
@@ -1281,6 +1285,7 @@ const keywordDef = {
1281
1285
  if (!fs.existsSync(path)) {
1282
1286
  let cmds = filterCmd(uniqueName);
1283
1287
  if (cmds.length === 1) {
1288
+ uniqueName = cmds[0];
1284
1289
  path = getFullPath(cmds[0])
1285
1290
  } else {
1286
1291
  console.warn("no items")
@@ -2048,6 +2053,7 @@ async function getScriptArgs(argDef, oriArgs) {
2048
2053
  let argNames = Object.keys(argDef)
2049
2054
  let exactArgs = {}
2050
2055
  let scriptArgs = {}
2056
+ let argIdx = 0;
2051
2057
  for (let i = 0; i<oriArgs.length; i++) {
2052
2058
  let arg = oriArgs[i]
2053
2059
  let needTrans
@@ -2079,12 +2085,13 @@ async function getScriptArgs(argDef, oriArgs) {
2079
2085
  }
2080
2086
  pair[1] = needTrans ? await evalText( 'return ' + pair[1]):pair[1]
2081
2087
 
2082
- let curIdx = Object.keys(scriptArgs).length
2083
- if (argNames[curIdx]) {
2084
- scriptArgs[argNames[curIdx]] = pair[1]
2088
+ scriptArgs["$" + argIdx] = pair[1]
2089
+ argIdx ++;
2090
+ if (argNames[i]) {
2091
+ scriptArgs[argNames[i]] = pair[1]
2085
2092
  }
2086
2093
  if (pair[0] && argNames.indexOf(pair[0]) !== -1) {
2087
- delete scriptArgs[argNames[curIdx]]
2094
+ delete scriptArgs[argNames[i]]
2088
2095
  exactArgs[pair[0]] = pair[1]
2089
2096
  }
2090
2097
  }
@@ -2272,7 +2279,7 @@ async function _requireSource(currSpace, cmdMatchStr, force = false) {
2272
2279
  let typeKey = getJsirTypeKey(pair[1]);
2273
2280
  if (typeKey === setting.initKey) {
2274
2281
  let pair = parseUniqueName(uniqueName)
2275
- if (global.$TEST || force) {
2282
+ if (force) {
2276
2283
  result = await evalText(text, uniqueName)
2277
2284
  } else {
2278
2285
  if (setting.serviceReg.test(uniqueName)) {
@@ -2310,26 +2317,44 @@ function addErrorTag(text) {
2310
2317
  let lines = text.split(/\n/);
2311
2318
  let functionEnd = true;
2312
2319
  let fnName = '';
2320
+ let wrapperFn = [];
2313
2321
  for (let i = 0; i < lines.length; i++) {
2314
2322
  let line = lines[i];
2315
- result.push(line);
2316
- if (line.startsWith('async function') || line.startsWith('function')) {
2323
+ if (line.startsWith("@")) {
2324
+ wrapperFn.push(line.substring(1).trim())
2325
+ result.push(null);
2326
+ } else {
2327
+ result.push(line);
2328
+ }
2329
+ let isAsyncFn = line.startsWith('async function');
2330
+ let isSyncFn = line.startsWith('function');
2331
+ if (isAsyncFn || isSyncFn) {
2317
2332
  fnName = reget(line, /function\s+([\s\S]+)\s*\(/)
2318
2333
  }
2319
2334
  if (functionEnd && fnName && (/\)\s*\{$/.test(trim(line)) || (line.startsWith("{") && trim(line) === '{'))) {
2320
2335
  result[i] += 'try{';
2336
+ if (wrapperFn.length > 0) {
2337
+ let defaultArgsList = '("'+ fnName +'")';
2338
+ let runner = `([${wrapperFn.map(i => 'new ' + i + (i.endsWith(")") ? '':defaultArgsList)).join(",")}],`
2339
+ if (isAsyncFn) {
2340
+ result[i] += `return await $aopAsync${runner}async ()=>{`;
2341
+ } else if (isSyncFn) {
2342
+ result[i] += `return $aop${runner}()=>{`;
2343
+ }
2344
+ }
2321
2345
  functionEnd = false;
2322
2346
  }
2323
2347
  if (line.startsWith("}") && !functionEnd) {
2324
- result[i] = `}catch(e){throw($errorTag(e,$cmdName+'[${fnName}]'))}` + line;
2348
+ result[i] = `${wrapperFn.length > 0 ? '},arguments)':''}}catch(e){throw($errorTag(e,$cmdName+'[${fnName}]'))}` + line;
2325
2349
  functionEnd = true;
2326
2350
  fnName = '';
2351
+ wrapperFn = []
2327
2352
  }
2328
2353
  }
2329
- return result.join('\n');
2354
+ return result.filter(i => i !== null).join('\n');
2330
2355
  }
2331
2356
 
2332
- async function evalText($text = '', $cmdName = '', $args = []) {
2357
+ async function evalText($text = '', $cmdName = '', $args = {}) {
2333
2358
  if ($cmdName) {
2334
2359
  console.$log(`Execute ${$cmdName}`);
2335
2360
  }
package/deps/evalCode.js CHANGED
@@ -19,7 +19,15 @@ module.exports = async ($text = '', $cmdName = '', $args = [],
19
19
 
20
20
  const $setTips = $lib.setTips;
21
21
  const $delTips = $lib.delTips;
22
+ const $tips = {
23
+ set: $lib.setTips,
24
+ del: $lib.delTips,
25
+ has: $lib.hasTips,
26
+ key: $lib.tipKeys
27
+ }
22
28
  const $errorTag = $lib.errorTag;
29
+ const $aop = $lib.aop;
30
+ const $aopAsync = $lib.aopAsync;
23
31
 
24
32
  const $context = {
25
33
  $defArgs,
@@ -27,7 +35,7 @@ module.exports = async ($text = '', $cmdName = '', $args = [],
27
35
  $require, $requires, $import,
28
36
  $text, $cmdName, $args,
29
37
  $nextLine, $nextText,
30
- $setTips, $delTips,
38
+ $setTips, $delTips, $tips,
31
39
  $enter, $filterCmd,
32
40
  $currentSpace, $defaultSpace, $workspaceMap,
33
41
  $homeDir, $lib, $cmdMap
package/deps/room.js CHANGED
@@ -2,7 +2,7 @@ const os = require('os');
2
2
  const {fileJson, fileLock, vl, createConsole, getKeyTips, getValTips,
3
3
  getRoomsDir, e, isRunningInBackground,
4
4
  batchAsync, debugStr, trim, getOr, errorTag, warnStr, getConfig, getConfigDir,
5
- fileExist, formatUptime
5
+ fileExist, formatUptime, processLock
6
6
  } = require('./util');
7
7
  const {setRoute, createSign} = require('../deps/server')
8
8
  const roomDataFile = "jsirRoom.json"
@@ -146,23 +146,21 @@ async function initNodes() {
146
146
  setting.nodeMap = await fileJson("jsirNodes.json", obj => nodes);
147
147
  }
148
148
 
149
- async function updateRoomInfo() {
149
+ async function updateRoomInfo(room) {
150
150
  await initNodes();
151
151
  let nodes = Object.keys(setting.nodeMap)
152
152
  let ip = getSelfIP(nodes)
153
- await fileJson(roomDataFile, async room => {
154
- room = {
155
- jsirs: room.jsirs
156
- }
157
- // 设置roomName
158
- room.name = setting.nodeMap[ip]?.name || os.hostname();
159
- room.selfNode = ip;
160
- room.lastUpdateTime = setting.roomTime
161
- debug("init room", room.name)
162
-
163
- await cleanRoom(room)
164
- return room;
165
- })
153
+ room = {
154
+ jsirs: room.jsirs
155
+ }
156
+ // 设置roomName
157
+ room.name = setting.nodeMap[ip]?.name || os.hostname();
158
+ room.selfNode = ip;
159
+ room.lastUpdateTime = setting.roomTime
160
+ debug("init room", room.name)
161
+
162
+ await cleanRoom(room)
163
+ return room;
166
164
  }
167
165
 
168
166
  async function syncRooms() {
@@ -333,21 +331,21 @@ async function initRoom() {
333
331
  await initRoomJsir(room)
334
332
  if (!vl(room.lastUpdateTime) || (Date.now() - room.lastUpdateTime) > setting.roomTimer) {
335
333
  roomUpdateTouchTime = true;
334
+ return await updateRoomInfo(room)
336
335
  }
337
336
  })
338
337
  if (roomUpdateTouchTime) {
339
338
  fileLock(updateRoomInfoLockKey, async () => {
340
- await updateRoomInfo()
341
339
  let pros = []
342
340
  pros.push(syncRooms())
343
341
  if (setting.serviceFns[setting.configMainFnKey] && !getConfig("configMain")) {
344
342
  pros.push(syncConfigs())
345
343
  }
346
344
  await Promise.all(pros);
347
- }, false)
345
+ }, false).catch(debug)
348
346
  }
349
- if (defTasks) {
350
- await processTasks();
347
+ if (defTasks && Object.keys(defTasks).length > 0) {
348
+ processLock(roomTasksFile, processTasks, false).catch(debug)
351
349
  }
352
350
  await syncSetting();
353
351
  }
@@ -505,58 +503,57 @@ async function reqNode(node, method, url, port, body) {
505
503
  hostname: node,
506
504
  port: port, // HTTP 默认端口
507
505
  path: url,
508
- method: method.toUpperCase(),
509
- headers: {
510
- 'sign': createSign()
511
- }
506
+ method: method.toUpperCase()
512
507
  };
513
508
  let time = Date.now();
514
509
  try {
515
510
  return await new Promise((resolve, reject) => {
516
- try {
517
- let timeoutMs = setting.reqNodeTimeouts[url] || setting.reqNodeDefaultTimeout;
518
- let timeout = null
519
-
520
- const req = http.request(opt, (res) => {
521
- let data = '';
522
- // 数据块接收
523
- res.on('data', (chunk) => {
524
- data += chunk;
525
- });
526
- // 响应结束
527
- res.on('end', () => {
528
- clearTimeout(timeout)
529
- // 检查响应状态码
530
- if (res.statusCode !== 200) {
531
- reject(errorTag(new Error(data), decodeURIComponent(url)));
532
- return;
533
- }
534
- resolve(data)
535
- });
511
+ let timeout = null
512
+
513
+ let options = {...opt,
514
+ headers: {
515
+ 'sign': createSign()
516
+ }};
517
+ options.path = encodeURI(opt.path);
518
+ const req = http.request(options, (res) => {
519
+ let data = '';
520
+ // 数据块接收
521
+ res.on('data', (chunk) => {
522
+ data += chunk;
536
523
  });
524
+ // 响应结束
525
+ res.on('end', () => {
526
+ clearTimeout(timeout)
527
+ // 检查响应状态码
528
+ if (res.statusCode !== 200) {
529
+ reject(errorTag(new Error(data), decodeURIComponent(url)));
530
+ return;
531
+ }
532
+ resolve(data)
533
+ });
534
+ });
537
535
 
538
- // 设置超时逻辑
536
+ // 设置超时逻辑
537
+ if (setting.reqNodeTimeouts[url]) {
539
538
  timeout = setTimeout(() => {
540
539
  req.destroy(); // 中断请求
541
- }, timeoutMs); // 10秒超时
542
-
543
- // 错误处理
544
- req.on('error', (e) => {
545
- clearTimeout(timeout); // 清除超时定时器
546
- if (room) {
547
- setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
548
- }
549
- reject(e)
550
- });
540
+ }, setting.reqNodeTimeouts[url]); // 10秒超时
541
+ }
551
542
 
552
- if (body) {
553
- req.write(body)
543
+ // 错误处理
544
+ req.on('error', (e) => {
545
+ clearTimeout(timeout); // 清除超时定时器
546
+ if (room) {
547
+ setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
554
548
  }
555
- // 结束请求
556
- req.end();
557
- } catch (e) {
558
549
  reject(e)
550
+ });
551
+
552
+ if (body) {
553
+ req.write(body)
559
554
  }
555
+ // 结束请求
556
+ req.end();
560
557
  });
561
558
  } finally {
562
559
  debug(`Request cost ${Date.now() - time}ms`, JSON.stringify(opt))
@@ -593,7 +590,7 @@ async function reqFn(fnKey, args = []) {
593
590
  throw `service function ${fnKey} not found`;
594
591
  }
595
592
  let resp = await reqNode(target.node, 'post',
596
- `/${encodeURI(fnKey)}`, target.port, JSON.stringify(args || []))
593
+ `/${fnKey}`, target.port, JSON.stringify(args || []))
597
594
  return JSON.parse(resp).result;
598
595
  }
599
596
 
package/deps/setting.js CHANGED
@@ -34,9 +34,9 @@ module.exports = {
34
34
  serviceFns: {},
35
35
  enterOutputs: null,
36
36
  defaultPort: 52108,
37
- reqNodeDefaultTimeout: 33000,
38
37
  reqNodeTimeouts: {
39
- '/': 2000
38
+ '/': 2000,
39
+ '/config/main': 2000
40
40
  },
41
41
  roomTimer: 2000,
42
42
  roomTime: Date.now(),
@@ -44,5 +44,8 @@ module.exports = {
44
44
  roomHeartbeatExpire: 9000,
45
45
  configMainFnKey: "config/main",
46
46
  serviceReg: /[^a-zA-Z]service[^a-zA-Z]|[^a-zA-Z]service$/i,
47
- wrapperInput: null
47
+ wrapperInput: null,
48
+ lastOutput: null,
49
+ locks: {},
50
+ lastChangeFlag: null
48
51
  }
package/deps/util.js CHANGED
@@ -45,7 +45,7 @@ function consoleStrs(...args) {
45
45
  }
46
46
 
47
47
  // 格式化第一个参数
48
- if (typeof args[0] === 'string') {
48
+ if (args.length > 1 && typeof args[0] === 'string' && hasFormat(args[0])) {
49
49
  return util.format(...args); // 处理格式化字符串 (%s, %d 等)
50
50
  }
51
51
 
@@ -59,6 +59,12 @@ function consoleStrs(...args) {
59
59
  .join(' ');
60
60
  }
61
61
 
62
+ // 判断是否包含格式化占位符的函数
63
+ function hasFormat(str) {
64
+ // 检查是否有 %s, %d, %j 等占位符
65
+ return /%[sdjifoO%]/.test(str);
66
+ }
67
+
62
68
  class SyncQueue {
63
69
  constructor() {
64
70
  this.queue = [];
@@ -194,7 +200,7 @@ const _console =Object.assign({}, global.console);
194
200
  const _consoleFns= {
195
201
  log: {
196
202
  fn: _console.log,
197
- args: args => args
203
+ args: args => [consoleStrs(...args)]
198
204
  },
199
205
  table: {
200
206
  fn: _console.log,
@@ -209,7 +215,7 @@ const _consoleFns= {
209
215
  args: args => [infoStr('[info]'), dealLevelArgs(infoStr, args)]
210
216
  },
211
217
  msg: {
212
- fn: _console.log,
218
+ fn: _console.info,
213
219
  args: args => [msgStr('[msg]'), dealLevelArgs(msgStr, args)]
214
220
  },
215
221
  warn: {
@@ -277,6 +283,18 @@ function createConsole(uniqueName) {
277
283
  if ('error' === key) {
278
284
  global.$newError = true;
279
285
  }
286
+ let hasFlag = false;
287
+ let sameFlag = false;
288
+ if (args.length > 1 && typeof args[0] === 'string' && args[0].length > 2 && args[0].startsWith("*") && args[0].endsWith("*")) {
289
+ hasFlag = true;
290
+ let flag = args[0]
291
+ args = args.slice(1)
292
+
293
+ if (!setting.lastChangeFlag || setting.lastChangeFlag === flag) {
294
+ sameFlag = true;
295
+ }
296
+ setting.lastChangeFlag = flag
297
+ }
280
298
  if (uniqueName && quite) {
281
299
  if ((key === 'table' || key === 'nable') && typeof args[0] === "object") {
282
300
  args = ['', ...args]
@@ -289,7 +307,25 @@ function createConsole(uniqueName) {
289
307
  }
290
308
  } else {
291
309
  let _args = _consoleFns[key].args(args);
310
+ let lastOutput = null;
311
+ if (hasFlag) {
312
+ lastOutput = _args.join(" ")
313
+ if (!isRunningInBackground() && setting.lastOutput && sameFlag) {
314
+ let lines = setting.lastOutput.split('\n').length;
315
+ for (let i = 0; i < lines; i++) {
316
+ process.stdout.write('\x1b[A\r'+ ' '.repeat(process.stdout.columns))
317
+ }
318
+ _args[0] = '\r' + _args[0]
319
+ }
320
+ }
321
+
292
322
  _consoleFns[key].fn(..._args);
323
+
324
+ if (hasFlag) {
325
+ setting.lastOutput = lastOutput;
326
+ } else {
327
+ setting.lastOutput = null;
328
+ }
293
329
  }
294
330
  }
295
331
  }
@@ -1044,6 +1080,9 @@ function _getConfig(key, defaultVal, uniqueName) {
1044
1080
  if (key === undefined) {
1045
1081
  return config
1046
1082
  }
1083
+ if (uniqueName) {
1084
+ console.debug(`use ${uniqueName} config "${key}"`)
1085
+ }
1047
1086
  let writeFlag = false
1048
1087
  if (!(key in config)) {
1049
1088
  writeFlag = true
@@ -1193,26 +1232,20 @@ async function _fileLock(key, fn, expireMs = 49000) {
1193
1232
  if (!key || typeof fn !== 'function') {
1194
1233
  throw new Error('invalid arguments');
1195
1234
  }
1235
+ if (expireMs < 1000) {
1236
+ throw new Error("expireMs can not less than 1000")
1237
+ }
1196
1238
 
1197
1239
  let lockKeyDir = getLockKeyDir(key)
1198
1240
  const now = Date.now();
1199
- const expireAt = expireMs > 0 ? now + expireMs : 0;
1241
+ const expireAt = now + expireMs;
1200
1242
 
1201
1243
  // 1. 尝试判断锁目录是否已存在
1202
1244
  let lockExists = await fileExist(lockKeyDir);
1203
1245
 
1204
1246
  if (lockExists) {
1205
1247
  // 目录存在时,读取文件名中的过期时间戳
1206
- const storedExpireAt = await readExpireTimestamp(lockKeyDir);
1207
- if (storedExpireAt === null) {
1208
- // 理论上不会出现没有“expire-xxxx”文件的情况,除非中途被手动改动
1209
- // 可以选择强制删除目录,或者直接返回 false
1210
- return false;
1211
- }
1212
- if (storedExpireAt === 0) {
1213
- // 说明当时设置的是永不过期
1214
- return false;
1215
- }
1248
+ const storedExpireAt = await readExpireTimestamp(lockKeyDir) || 0;
1216
1249
  if (storedExpireAt > now) {
1217
1250
  // 说明锁尚未过期
1218
1251
  return false;
@@ -1250,13 +1283,15 @@ async function _fileLock(key, fn, expireMs = 49000) {
1250
1283
 
1251
1284
  // 4. 成功加锁后执行 fn
1252
1285
  try {
1253
- await fn();
1286
+ await timeLimit([fn()], expireMs)
1254
1287
  return true;
1255
1288
  } finally {
1256
- // 5. 执行完毕后,手动释放锁目录(也可选择保留到过期自动失效,但一般都建议主动释放)
1257
- try {
1258
- await fp.rm(lockKeyDir, { recursive: true, force: true });
1259
- } catch (_) {}
1289
+ if (Date.now() <= expireAt - 9) {
1290
+ // 5. 执行完毕后,手动释放锁目录(也可选择保留到过期自动失效,但一般都建议主动释放)
1291
+ try {
1292
+ await fp.rm(lockKeyDir, { recursive: true, force: true });
1293
+ } catch (_) {}
1294
+ }
1260
1295
  }
1261
1296
  }
1262
1297
 
@@ -1278,6 +1313,41 @@ async function fileLock(key, fn, wait = true, expireMs = 49000) {
1278
1313
  }
1279
1314
  }
1280
1315
 
1316
+ async function processLock(key, fn, wait = true) {
1317
+ `
1318
+ 内存锁, 默认一直等待直到加锁成功执行fn
1319
+ wait = false, 加锁失败则不执行fn
1320
+ return void
1321
+ `
1322
+ if (wait) {
1323
+ while (true) {
1324
+ if (await _processLock(key, fn)) {
1325
+ break;
1326
+ }
1327
+ await sleep(9);
1328
+ }
1329
+ } else {
1330
+ await _processLock(key, fn);
1331
+ }
1332
+ }
1333
+
1334
+ async function _processLock(key, fn) {
1335
+ if (!key || typeof fn !== 'function') {
1336
+ throw new Error('invalid arguments');
1337
+ }
1338
+ key = key.trim();
1339
+ if (setting.locks.hasOwnProperty(key)) {
1340
+ return false;
1341
+ }
1342
+ setting.locks[key] = true;
1343
+ try {
1344
+ await fn();
1345
+ return true;
1346
+ } finally {
1347
+ delete setting.locks[key];
1348
+ }
1349
+ }
1350
+
1281
1351
  function removeFirst(array, obj) {
1282
1352
  let deleteIndex
1283
1353
  for(let i = 0;i<array.length;i++) {
@@ -1326,6 +1396,14 @@ function delTips(...keys) {
1326
1396
  }
1327
1397
  }
1328
1398
 
1399
+ function hasTips(key) {
1400
+ return setting.tips.hasOwnProperty(key);
1401
+ }
1402
+
1403
+ function tipKeys() {
1404
+ return Object.keys(setting.tips);
1405
+ }
1406
+
1329
1407
  function tipsOnRmCallback(key) {
1330
1408
  let callbacks = _tipsOnRm[key]
1331
1409
  delete _tipsOnRm[key]
@@ -2076,6 +2154,14 @@ function getValTips() {
2076
2154
  let items = [];
2077
2155
  for (let key of Object.keys(setting.tips)) {
2078
2156
  let val = setting.tips[key].map(i => {
2157
+ if (typeof i === 'function') {
2158
+ try {
2159
+ i = i();
2160
+ } catch (e) {
2161
+ console.$error(`tips "${key}"`, e);
2162
+ return '';
2163
+ }
2164
+ }
2079
2165
  let item = trim(i)
2080
2166
  if (item.indexOf(',') !== -1) {
2081
2167
  item = `[${item}]`;
@@ -2149,6 +2235,28 @@ function formatUptime() {
2149
2235
  }
2150
2236
  }
2151
2237
 
2238
+ function aop(items, fn, args) {
2239
+ let runners = [fn]
2240
+ let i = 0
2241
+ for (let item of [...items].reverse()) {
2242
+ const j = i;
2243
+ runners.push((args) => item.run(runners[j], args))
2244
+ i ++;
2245
+ }
2246
+ return runners[i](args)
2247
+ }
2248
+
2249
+ async function aopAsync(items, fn, args) {
2250
+ let runners = [fn]
2251
+ let i = 0
2252
+ for (let item of [...items].reverse()) {
2253
+ const j = i;
2254
+ runners.push(async (args) => await item.runAsync(runners[j], args))
2255
+ i ++;
2256
+ }
2257
+ return await runners[i](args)
2258
+ }
2259
+
2152
2260
  module.exports = {
2153
2261
  formatUptime,
2154
2262
  wrapperJsirText,
@@ -2260,5 +2368,10 @@ module.exports = {
2260
2368
  createDetachedProcess,
2261
2369
  interceptStdStreams,
2262
2370
  draftModify,
2263
- fileExist
2371
+ fileExist,
2372
+ aop,
2373
+ aopAsync,
2374
+ processLock,
2375
+ hasTips,
2376
+ tipKeys
2264
2377
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsir",
3
- "version": "2.6.5",
3
+ "version": "2.6.7",
4
4
  "description": "JavaScript Script Management Tool",
5
5
  "main": "index.js",
6
6
  "scripts": {