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 +40 -15
- package/deps/evalCode.js +9 -1
- package/deps/room.js +57 -60
- package/deps/setting.js +6 -3
- package/deps/util.js +133 -20
- package/package.json +1 -1
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 =
|
|
917
|
-
|
|
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
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
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[
|
|
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 (
|
|
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
|
-
|
|
2316
|
-
|
|
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] =
|
|
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
|
-
|
|
154
|
-
room
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
},
|
|
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
|
-
|
|
553
|
-
|
|
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
|
-
`/${
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
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
|
}
|