jsir 2.6.4 → 2.6.6

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
 
@@ -703,7 +704,6 @@ async function execLibFn(fn, fnArgs) {
703
704
  }
704
705
 
705
706
  async function joinServer(uniqueName, isApi, exportLib) {
706
- Room.onRoom();
707
707
  if (!setting.serviceReg.test(uniqueName)) {
708
708
  throw 'invalid service name';
709
709
  }
@@ -730,6 +730,7 @@ async function joinServer(uniqueName, isApi, exportLib) {
730
730
  }, isApi)
731
731
  }
732
732
  console.msg(uniqueName, `has become a ${isApi ? "api ":""}service.`);
733
+ Room.onRoom();
733
734
  }
734
735
 
735
736
  async function offServer(uniqueName) {
@@ -913,8 +914,10 @@ function dealStarCmd(rows, cmd, filterStr) {
913
914
  let commentContent = trimText(comments.join('\n'));
914
915
  let row = getInfo(functionContent, fnName, fnType);
915
916
  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");
917
+ row.value = [
918
+ infoStr(pair[0] + '/' + trimJsirFileName(pair[1])) + ' ' + getCmdMd5Key(parseUniqueName(cmd)[1]),
919
+ [commentContent, row.value].filter(i => trim(i)).join("\n")
920
+ ].filter(i => trim(i)).join("\n");
918
921
  rows.push(row);
919
922
 
920
923
  resetState();
@@ -1281,6 +1284,7 @@ const keywordDef = {
1281
1284
  if (!fs.existsSync(path)) {
1282
1285
  let cmds = filterCmd(uniqueName);
1283
1286
  if (cmds.length === 1) {
1287
+ uniqueName = cmds[0];
1284
1288
  path = getFullPath(cmds[0])
1285
1289
  } else {
1286
1290
  console.warn("no items")
@@ -2048,6 +2052,7 @@ async function getScriptArgs(argDef, oriArgs) {
2048
2052
  let argNames = Object.keys(argDef)
2049
2053
  let exactArgs = {}
2050
2054
  let scriptArgs = {}
2055
+ let argIdx = 0;
2051
2056
  for (let i = 0; i<oriArgs.length; i++) {
2052
2057
  let arg = oriArgs[i]
2053
2058
  let needTrans
@@ -2079,12 +2084,13 @@ async function getScriptArgs(argDef, oriArgs) {
2079
2084
  }
2080
2085
  pair[1] = needTrans ? await evalText( 'return ' + pair[1]):pair[1]
2081
2086
 
2082
- let curIdx = Object.keys(scriptArgs).length
2083
- if (argNames[curIdx]) {
2084
- scriptArgs[argNames[curIdx]] = pair[1]
2087
+ scriptArgs["$" + argIdx] = pair[1]
2088
+ argIdx ++;
2089
+ if (argNames[i]) {
2090
+ scriptArgs[argNames[i]] = pair[1]
2085
2091
  }
2086
2092
  if (pair[0] && argNames.indexOf(pair[0]) !== -1) {
2087
- delete scriptArgs[argNames[curIdx]]
2093
+ delete scriptArgs[argNames[i]]
2088
2094
  exactArgs[pair[0]] = pair[1]
2089
2095
  }
2090
2096
  }
@@ -2272,7 +2278,7 @@ async function _requireSource(currSpace, cmdMatchStr, force = false) {
2272
2278
  let typeKey = getJsirTypeKey(pair[1]);
2273
2279
  if (typeKey === setting.initKey) {
2274
2280
  let pair = parseUniqueName(uniqueName)
2275
- if (global.$TEST || force) {
2281
+ if (force) {
2276
2282
  result = await evalText(text, uniqueName)
2277
2283
  } else {
2278
2284
  if (setting.serviceReg.test(uniqueName)) {
@@ -2310,26 +2316,44 @@ function addErrorTag(text) {
2310
2316
  let lines = text.split(/\n/);
2311
2317
  let functionEnd = true;
2312
2318
  let fnName = '';
2319
+ let wrapperFn = [];
2313
2320
  for (let i = 0; i < lines.length; i++) {
2314
2321
  let line = lines[i];
2315
- result.push(line);
2316
- if (line.startsWith('async function') || line.startsWith('function')) {
2322
+ if (line.startsWith("@")) {
2323
+ wrapperFn.push(line.substring(1).trim())
2324
+ result.push(null);
2325
+ } else {
2326
+ result.push(line);
2327
+ }
2328
+ let isAsyncFn = line.startsWith('async function');
2329
+ let isSyncFn = line.startsWith('function');
2330
+ if (isAsyncFn || isSyncFn) {
2317
2331
  fnName = reget(line, /function\s+([\s\S]+)\s*\(/)
2318
2332
  }
2319
2333
  if (functionEnd && fnName && (/\)\s*\{$/.test(trim(line)) || (line.startsWith("{") && trim(line) === '{'))) {
2320
2334
  result[i] += 'try{';
2335
+ if (wrapperFn.length > 0) {
2336
+ let defaultArgsList = '("'+ fnName +'")';
2337
+ let runner = `([${wrapperFn.map(i => 'new ' + i + (i.endsWith(")") ? '':defaultArgsList)).join(",")}],`
2338
+ if (isAsyncFn) {
2339
+ result[i] += `return await $aopAsync${runner}async ()=>{`;
2340
+ } else if (isSyncFn) {
2341
+ result[i] += `return $aop${runner}()=>{`;
2342
+ }
2343
+ }
2321
2344
  functionEnd = false;
2322
2345
  }
2323
2346
  if (line.startsWith("}") && !functionEnd) {
2324
- result[i] = `}catch(e){throw($errorTag(e,$cmdName+'[${fnName}]'))}` + line;
2347
+ result[i] = `${wrapperFn.length > 0 ? '},arguments)':''}}catch(e){throw($errorTag(e,$cmdName+'[${fnName}]'))}` + line;
2325
2348
  functionEnd = true;
2326
2349
  fnName = '';
2350
+ wrapperFn = []
2327
2351
  }
2328
2352
  }
2329
- return result.join('\n');
2353
+ return result.filter(i => i !== null).join('\n');
2330
2354
  }
2331
2355
 
2332
- async function evalText($text = '', $cmdName = '', $args = []) {
2356
+ async function evalText($text = '', $cmdName = '', $args = {}) {
2333
2357
  if ($cmdName) {
2334
2358
  console.$log(`Execute ${$cmdName}`);
2335
2359
  }
package/deps/evalCode.js CHANGED
@@ -20,6 +20,8 @@ module.exports = async ($text = '', $cmdName = '', $args = [],
20
20
  const $setTips = $lib.setTips;
21
21
  const $delTips = $lib.delTips;
22
22
  const $errorTag = $lib.errorTag;
23
+ const $aop = $lib.aop;
24
+ const $aopAsync = $lib.aopAsync;
23
25
 
24
26
  const $context = {
25
27
  $defArgs,
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"
@@ -121,9 +121,12 @@ async function initNodes() {
121
121
  if (configNodes.length > 0) {
122
122
  for (const node of configNodes) {
123
123
  let ss = node.split("@").map(trim).filter(i => i);
124
- nodes[ss[1]] = {}
125
124
  if (ss.length > 1) {
126
- nodes[ss[1]].account = ss[0]
125
+ nodes[ss[1]] = {
126
+ account: ss[0]
127
+ }
128
+ } else {
129
+ nodes[ss[0]] = {}
127
130
  }
128
131
  }
129
132
  } else {
@@ -143,23 +146,21 @@ async function initNodes() {
143
146
  setting.nodeMap = await fileJson("jsirNodes.json", obj => nodes);
144
147
  }
145
148
 
146
- async function updateRoomInfo() {
149
+ async function updateRoomInfo(room) {
147
150
  await initNodes();
148
151
  let nodes = Object.keys(setting.nodeMap)
149
152
  let ip = getSelfIP(nodes)
150
- await fileJson(roomDataFile, async room => {
151
- room = {
152
- jsirs: room.jsirs
153
- }
154
- // 设置roomName
155
- room.name = setting.nodeMap[ip]?.name || os.hostname();
156
- room.selfNode = ip;
157
- room.lastUpdateTime = setting.roomTime
158
- debug("init room", room.name)
159
-
160
- await cleanRoom(room)
161
- return room;
162
- })
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;
163
164
  }
164
165
 
165
166
  async function syncRooms() {
@@ -330,11 +331,11 @@ async function initRoom() {
330
331
  await initRoomJsir(room)
331
332
  if (!vl(room.lastUpdateTime) || (Date.now() - room.lastUpdateTime) > setting.roomTimer) {
332
333
  roomUpdateTouchTime = true;
334
+ return await updateRoomInfo(room)
333
335
  }
334
336
  })
335
337
  if (roomUpdateTouchTime) {
336
338
  fileLock(updateRoomInfoLockKey, async () => {
337
- await updateRoomInfo()
338
339
  let pros = []
339
340
  pros.push(syncRooms())
340
341
  if (setting.serviceFns[setting.configMainFnKey] && !getConfig("configMain")) {
@@ -343,8 +344,8 @@ async function initRoom() {
343
344
  await Promise.all(pros);
344
345
  }, false)
345
346
  }
346
- if (defTasks) {
347
- await processTasks();
347
+ if (defTasks && Object.keys(defTasks).length > 0) {
348
+ processLock(roomTasksFile, processTasks, false)
348
349
  }
349
350
  await syncSetting();
350
351
  }
@@ -502,58 +503,54 @@ async function reqNode(node, method, url, port, body) {
502
503
  hostname: node,
503
504
  port: port, // HTTP 默认端口
504
505
  path: url,
505
- method: method.toUpperCase(),
506
- headers: {
507
- 'sign': createSign()
508
- }
506
+ method: method.toUpperCase()
509
507
  };
510
508
  let time = Date.now();
511
509
  try {
512
510
  return await new Promise((resolve, reject) => {
513
- try {
514
- let timeoutMs = setting.reqNodeTimeouts[url] || setting.reqNodeDefaultTimeout;
515
- let timeout = null
516
-
517
- const req = http.request(opt, (res) => {
518
- let data = '';
519
- // 数据块接收
520
- res.on('data', (chunk) => {
521
- data += chunk;
522
- });
523
- // 响应结束
524
- res.on('end', () => {
525
- clearTimeout(timeout)
526
- // 检查响应状态码
527
- if (res.statusCode !== 200) {
528
- reject(errorTag(new Error(data), decodeURIComponent(url)));
529
- return;
530
- }
531
- resolve(data)
532
- });
511
+ let timeout = null
512
+
513
+ const req = http.request({...opt, headers: {
514
+ 'sign': createSign()
515
+ }}, (res) => {
516
+ let data = '';
517
+ // 数据块接收
518
+ res.on('data', (chunk) => {
519
+ data += chunk;
533
520
  });
521
+ // 响应结束
522
+ res.on('end', () => {
523
+ clearTimeout(timeout)
524
+ // 检查响应状态码
525
+ if (res.statusCode !== 200) {
526
+ reject(errorTag(new Error(data), decodeURIComponent(url)));
527
+ return;
528
+ }
529
+ resolve(data)
530
+ });
531
+ });
534
532
 
535
- // 设置超时逻辑
533
+ // 设置超时逻辑
534
+ if (setting.reqNodeTimeouts[url]) {
536
535
  timeout = setTimeout(() => {
537
536
  req.destroy(); // 中断请求
538
- }, timeoutMs); // 10秒超时
539
-
540
- // 错误处理
541
- req.on('error', (e) => {
542
- clearTimeout(timeout); // 清除超时定时器
543
- if (room) {
544
- setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
545
- }
546
- reject(e)
547
- });
537
+ }, setting.reqNodeTimeouts[url]); // 10秒超时
538
+ }
548
539
 
549
- if (body) {
550
- req.write(body)
540
+ // 错误处理
541
+ req.on('error', (e) => {
542
+ clearTimeout(timeout); // 清除超时定时器
543
+ if (room) {
544
+ setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
551
545
  }
552
- // 结束请求
553
- req.end();
554
- } catch (e) {
555
546
  reject(e)
547
+ });
548
+
549
+ if (body) {
550
+ req.write(body)
556
551
  }
552
+ // 结束请求
553
+ req.end();
557
554
  });
558
555
  } finally {
559
556
  debug(`Request cost ${Date.now() - time}ms`, JSON.stringify(opt))
package/deps/setting.js CHANGED
@@ -34,7 +34,6 @@ module.exports = {
34
34
  serviceFns: {},
35
35
  enterOutputs: null,
36
36
  defaultPort: 52108,
37
- reqNodeDefaultTimeout: 33000,
38
37
  reqNodeTimeouts: {
39
38
  '/': 2000
40
39
  },
@@ -44,5 +43,8 @@ module.exports = {
44
43
  roomHeartbeatExpire: 9000,
45
44
  configMainFnKey: "config/main",
46
45
  serviceReg: /[^a-zA-Z]service[^a-zA-Z]|[^a-zA-Z]service$/i,
47
- wrapperInput: null
46
+ wrapperInput: null,
47
+ lastOutput: null,
48
+ locks: {},
49
+ lastChangeFlag: null
48
50
  }
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].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,22 @@ function createConsole(uniqueName) {
289
307
  }
290
308
  } else {
291
309
  let _args = _consoleFns[key].args(args);
310
+
311
+ if (hasFlag && !isRunningInBackground() && setting.lastOutput && sameFlag) {
312
+ let lines = (setting.lastOutput.match(/\n/g) || []).length + 1;
313
+ for (let i = 0; i < lines; i++) {
314
+ process.stdout.write('\x1b[A\r'+ ' '.repeat(process.stdout.columns))
315
+ }
316
+ _args[0] = '\r' + _args[0]
317
+ }
318
+
292
319
  _consoleFns[key].fn(..._args);
320
+
321
+ if (hasFlag) {
322
+ setting.lastOutput = _args.join(" ");
323
+ } else {
324
+ setting.lastOutput = null;
325
+ }
293
326
  }
294
327
  }
295
328
  }
@@ -1044,6 +1077,9 @@ function _getConfig(key, defaultVal, uniqueName) {
1044
1077
  if (key === undefined) {
1045
1078
  return config
1046
1079
  }
1080
+ if (uniqueName) {
1081
+ console.debug(`use ${uniqueName} config "${key}"`)
1082
+ }
1047
1083
  let writeFlag = false
1048
1084
  if (!(key in config)) {
1049
1085
  writeFlag = true
@@ -1250,7 +1286,7 @@ async function _fileLock(key, fn, expireMs = 49000) {
1250
1286
 
1251
1287
  // 4. 成功加锁后执行 fn
1252
1288
  try {
1253
- await fn();
1289
+ await timeLimit([fn()], expireMs)
1254
1290
  return true;
1255
1291
  } finally {
1256
1292
  // 5. 执行完毕后,手动释放锁目录(也可选择保留到过期自动失效,但一般都建议主动释放)
@@ -1278,6 +1314,41 @@ async function fileLock(key, fn, wait = true, expireMs = 49000) {
1278
1314
  }
1279
1315
  }
1280
1316
 
1317
+ async function processLock(key, fn, wait = true) {
1318
+ `
1319
+ 内存锁, 默认一直等待直到加锁成功执行fn
1320
+ wait = false, 加锁失败则不执行fn
1321
+ return void
1322
+ `
1323
+ if (wait) {
1324
+ while (true) {
1325
+ if (await _processLock(key, fn)) {
1326
+ break;
1327
+ }
1328
+ await sleep(9);
1329
+ }
1330
+ } else {
1331
+ await _processLock(key, fn);
1332
+ }
1333
+ }
1334
+
1335
+ async function _processLock(key, fn) {
1336
+ if (!key || typeof fn !== 'function') {
1337
+ throw new Error('invalid arguments');
1338
+ }
1339
+ key = key.trim();
1340
+ if (setting.locks.hasOwnProperty(key)) {
1341
+ return false;
1342
+ }
1343
+ setting.locks[key] = true;
1344
+ try {
1345
+ await fn();
1346
+ return true;
1347
+ } finally {
1348
+ delete setting.locks[key];
1349
+ }
1350
+ }
1351
+
1281
1352
  function removeFirst(array, obj) {
1282
1353
  let deleteIndex
1283
1354
  for(let i = 0;i<array.length;i++) {
@@ -2149,6 +2220,28 @@ function formatUptime() {
2149
2220
  }
2150
2221
  }
2151
2222
 
2223
+ function aop(items, fn, args) {
2224
+ let runners = [fn]
2225
+ let i = 0
2226
+ for (let item of [...items].reverse()) {
2227
+ const j = i;
2228
+ runners.push((args) => item.run(runners[j], args))
2229
+ i ++;
2230
+ }
2231
+ return runners[i](args)
2232
+ }
2233
+
2234
+ async function aopAsync(items, fn, args) {
2235
+ let runners = [fn]
2236
+ let i = 0
2237
+ for (let item of [...items].reverse()) {
2238
+ const j = i;
2239
+ runners.push(async (args) => await item.runAsync(runners[j], args))
2240
+ i ++;
2241
+ }
2242
+ return await runners[i](args)
2243
+ }
2244
+
2152
2245
  module.exports = {
2153
2246
  formatUptime,
2154
2247
  wrapperJsirText,
@@ -2260,5 +2353,8 @@ module.exports = {
2260
2353
  createDetachedProcess,
2261
2354
  interceptStdStreams,
2262
2355
  draftModify,
2263
- fileExist
2356
+ fileExist,
2357
+ aop,
2358
+ aopAsync,
2359
+ processLock
2264
2360
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsir",
3
- "version": "2.6.4",
3
+ "version": "2.6.6",
4
4
  "description": "JavaScript Script Management Tool",
5
5
  "main": "index.js",
6
6
  "scripts": {