jsir 3.1.2 → 3.1.3
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 +2 -3
- package/deps/room.js +55 -5
- package/deps/setting.js +2 -1
- package/deps/util.js +73 -86
- 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, processLock,
|
|
16
|
+
draftModify, isRunningInBackground, fileJson, fileLock, processLock, getMd5Key, terminalTitle,
|
|
17
17
|
getFileOpenExe, formatSec, formatMb
|
|
18
18
|
} = $lib;
|
|
19
19
|
const _args = process.argv.slice(2).map(trim);
|
|
@@ -150,7 +150,7 @@ const $data = {
|
|
|
150
150
|
return _dealOnLazyGet(key, onLazyTempCode);
|
|
151
151
|
}
|
|
152
152
|
let result = null;
|
|
153
|
-
await processLock('gafs:' + key, async () => {
|
|
153
|
+
await processLock('jsir:gafs:' + key, async () => {
|
|
154
154
|
if (!vl(_data[key])) {
|
|
155
155
|
result = _dataSet(key, await fn(), onLazyTempCode)
|
|
156
156
|
} else {
|
|
@@ -203,7 +203,6 @@ function checkWorkspaces() {
|
|
|
203
203
|
|
|
204
204
|
async function start() {
|
|
205
205
|
terminalTitle()
|
|
206
|
-
cleanFileLocks();
|
|
207
206
|
|
|
208
207
|
setting.wrapperInput = wrapperInput;
|
|
209
208
|
interceptStdStreams()
|
package/deps/room.js
CHANGED
|
@@ -2,8 +2,8 @@ const os = require('os');
|
|
|
2
2
|
const {fileJson, fileLock, vl, getKeyTips, getValTips,
|
|
3
3
|
getRoomsDir, e, isRunningInBackground,
|
|
4
4
|
batchAsync, trim, getOr, errorTag, warnStr, getConfig, getConfigDir,
|
|
5
|
-
fileExist, processLock, isPidAlive,
|
|
6
|
-
roomConsole: console, reget, md5
|
|
5
|
+
fileExist, processLock, isPidAlive, getLockDir,
|
|
6
|
+
roomConsole: console, reget, md5, cacheFn
|
|
7
7
|
} = require('./util');
|
|
8
8
|
const {setRoute, createSign} = require('../deps/server')
|
|
9
9
|
const roomDataFile = "jsirRoom.json"
|
|
@@ -340,10 +340,58 @@ async function processTasks(defTasks) {
|
|
|
340
340
|
})
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
function cleanFnCache() {
|
|
344
|
+
const now = Date.now()
|
|
345
|
+
for (let key of Object.keys(setting.fnCache)) {
|
|
346
|
+
let item = setting.fnCache[key]
|
|
347
|
+
if (item && item.validMsTime && now > item.validMsTime) {
|
|
348
|
+
delete setting.fnCache[key]
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
console.$debug("cleanFnCache cost", `${Date.now() - now}ms`)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function readLockPid(lockDir) {
|
|
355
|
+
try {
|
|
356
|
+
const files = await fp.readdir(lockDir);
|
|
357
|
+
const pidFile = files.find(name => name.startsWith('pid-'));
|
|
358
|
+
if (!pidFile) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
return pidFile.split("-")[1];
|
|
362
|
+
} catch (err) {
|
|
363
|
+
// 读取目录出错(可能不存在?)直接返回 null
|
|
364
|
+
return null;
|
|
346
365
|
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function cleanFileLocks() {
|
|
369
|
+
const now = Date.now()
|
|
370
|
+
let lockDir = getLockDir();
|
|
371
|
+
const files = await fp.readdir(lockDir);
|
|
372
|
+
const deadPid = {}
|
|
373
|
+
for (let file of files) {
|
|
374
|
+
let lockKeyDir = lockDir + '/' + file;
|
|
375
|
+
let pid = await readLockPid(lockKeyDir);
|
|
376
|
+
if (!pid) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (deadPid[pid] || !isPidAlive(pid)) {
|
|
380
|
+
deadPid[pid] = true;
|
|
381
|
+
// 持有锁的进程已经没了,则删除锁文件
|
|
382
|
+
try {
|
|
383
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
384
|
+
} catch (err) {
|
|
385
|
+
console.$error(`cleanLock ${lockKeyDir} failed`, err);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
console.$debug("cleanFileLocks cost", `${Date.now() - now}ms`)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function initRoom() {
|
|
393
|
+
// 每分钟清理缓存
|
|
394
|
+
cacheFn("jsir:cleanFnCache", cleanFnCache, 60_000)
|
|
347
395
|
setting.roomTime = Date.now();
|
|
348
396
|
setting.nodeMap = await fileJson(jsirNodesFile);
|
|
349
397
|
let roomUpdateTouchTime = false;
|
|
@@ -355,6 +403,8 @@ async function initRoom() {
|
|
|
355
403
|
}
|
|
356
404
|
})
|
|
357
405
|
if (roomUpdateTouchTime) {
|
|
406
|
+
// 每分钟清理lock 文件
|
|
407
|
+
cacheFn("jsir:cleanFileLocks", cleanFileLocks, 60_000)
|
|
358
408
|
fileLock(updateRoomInfoLockKey, async () => {
|
|
359
409
|
let pros = []
|
|
360
410
|
pros.push(syncRooms())
|
package/deps/setting.js
CHANGED
package/deps/util.js
CHANGED
|
@@ -627,11 +627,12 @@ async function draftModify(fLine) {
|
|
|
627
627
|
console.info("removed")
|
|
628
628
|
}
|
|
629
629
|
if (isEdit && results.length === 1) {
|
|
630
|
-
let tempPath =
|
|
630
|
+
let tempPath = getTempDir() + '/draft_' + uid();
|
|
631
631
|
fs.writeFileSync(tempPath, results[0].split(/\n/).slice(1).join('\n'))
|
|
632
632
|
await eia(getEditor(), [`"${tempPath}"`], true)
|
|
633
633
|
let tempText = String(fs.readFileSync(tempPath))
|
|
634
|
-
fs.
|
|
634
|
+
fs.unlinkSync(tempPath)
|
|
635
|
+
|
|
635
636
|
let lineRange = lineRanges.get(results[0]);
|
|
636
637
|
let before = allLines.filter((_, index) => {
|
|
637
638
|
return index < lineRange.start;
|
|
@@ -705,21 +706,21 @@ function getVl(...obj) {
|
|
|
705
706
|
|
|
706
707
|
async function cacheFn(key, fn, validMs = 0, awaitRefresh = true) {
|
|
707
708
|
const cacheItem = getOr(setting.fnCache, key, {
|
|
708
|
-
|
|
709
|
+
validMsTime: 0,
|
|
709
710
|
});
|
|
710
|
-
if (Date.now()
|
|
711
|
-
|
|
712
|
-
cacheItem.createdAt = Infinity;
|
|
711
|
+
if (Date.now() > cacheItem.validMsTime) {
|
|
712
|
+
cacheItem.validMsTime = Infinity;
|
|
713
713
|
cacheItem.promise = (async () => {
|
|
714
714
|
let val;
|
|
715
715
|
try {
|
|
716
716
|
val = await fn()
|
|
717
|
-
}
|
|
718
|
-
|
|
717
|
+
} catch (e) {
|
|
718
|
+
delete setting.fnCache[key]
|
|
719
|
+
throw e
|
|
719
720
|
}
|
|
720
721
|
cacheItem.val = val;
|
|
721
722
|
cacheItem.valInit = true;
|
|
722
|
-
cacheItem.
|
|
723
|
+
cacheItem.validMsTime = Date.now() + validMs;
|
|
723
724
|
})();
|
|
724
725
|
}
|
|
725
726
|
if (awaitRefresh || !cacheItem.valInit) {
|
|
@@ -770,13 +771,13 @@ function clearConsole(cleanType = 0) {
|
|
|
770
771
|
if (cleanType === 0) {
|
|
771
772
|
// \033[2J:清空屏幕。
|
|
772
773
|
// \033[H:将光标定位到屏幕左上角。
|
|
773
|
-
process.stdout.write('\
|
|
774
|
+
process.stdout.write('\x1B[2J\x1B[H'); // 展开新的屏幕,信息都保留
|
|
774
775
|
} else if (cleanType === 1) {
|
|
775
776
|
// \033[0f 清除从光标位置到屏幕末尾的内容,并把光标移动到屏幕的左上角。
|
|
776
777
|
// process.stdout.write('\033[0f');
|
|
777
778
|
console.clear(); // 清空当前屏幕
|
|
778
779
|
} else if (cleanType > 1) {
|
|
779
|
-
process.stdout.write('\x1B[3J\
|
|
780
|
+
process.stdout.write('\x1B[3J\x1B[H'); // 全清,包括历史记录
|
|
780
781
|
console.clear();
|
|
781
782
|
}
|
|
782
783
|
}
|
|
@@ -1267,40 +1268,6 @@ function getLockKeyDir(key) {
|
|
|
1267
1268
|
return lockDir + "/" + key;
|
|
1268
1269
|
}
|
|
1269
1270
|
|
|
1270
|
-
async function readLockPid(lockDir) {
|
|
1271
|
-
try {
|
|
1272
|
-
const files = await fp.readdir(lockDir);
|
|
1273
|
-
const pidFile = files.find(name => name.startsWith('pid-'));
|
|
1274
|
-
if (!pidFile) {
|
|
1275
|
-
return null;
|
|
1276
|
-
}
|
|
1277
|
-
return pidFile.split("-")[1];
|
|
1278
|
-
} catch (err) {
|
|
1279
|
-
// 读取目录出错(可能不存在?)直接返回 null
|
|
1280
|
-
return null;
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
async function cleanFileLocks() {
|
|
1285
|
-
let lockDir = getLockDir();
|
|
1286
|
-
const files = await fp.readdir(lockDir);
|
|
1287
|
-
for (let file of files) {
|
|
1288
|
-
let lockKeyDir = lockDir + '/' + file;
|
|
1289
|
-
let pid = await readLockPid(lockKeyDir);
|
|
1290
|
-
if (!pid) {
|
|
1291
|
-
continue;
|
|
1292
|
-
}
|
|
1293
|
-
if (!isPidAlive(pid)) {
|
|
1294
|
-
// 持有锁的进程已经没了,则删除锁文件
|
|
1295
|
-
try {
|
|
1296
|
-
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1297
|
-
} catch (err) {
|
|
1298
|
-
console.$error(`cleanLock ${lockKeyDir} failed`, err);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
1271
|
async function _fileLock(key, fn) {
|
|
1305
1272
|
`
|
|
1306
1273
|
文件锁,返回true/false,
|
|
@@ -1310,41 +1277,28 @@ async function _fileLock(key, fn) {
|
|
|
1310
1277
|
throw new Error('invalid arguments');
|
|
1311
1278
|
}
|
|
1312
1279
|
|
|
1313
|
-
let
|
|
1314
|
-
//
|
|
1315
|
-
|
|
1316
|
-
if (lockExists) {
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// 2. 创建锁目录
|
|
1321
|
-
try {
|
|
1322
|
-
await fp.mkdir(lockKeyDir, { recursive: false });
|
|
1323
|
-
} catch (err) {
|
|
1324
|
-
// 如果 mkdir 依旧失败,说明在这段时间里又被别人抢先创建了
|
|
1325
|
-
return false;
|
|
1326
|
-
}
|
|
1280
|
+
let tempLockDir = getLockDir() + '/lock_' + uid()
|
|
1281
|
+
// 创建临时锁目录
|
|
1282
|
+
await fp.mkdir(tempLockDir, { recursive: false });
|
|
1327
1283
|
|
|
1328
1284
|
const lockPidFile = `pid-${process.pid}`;
|
|
1329
|
-
const lockPidFilePath = path.join(
|
|
1285
|
+
const lockPidFilePath = path.join(tempLockDir, lockPidFile);
|
|
1286
|
+
await fp.writeFile(lockPidFilePath, '');
|
|
1287
|
+
|
|
1288
|
+
let lockKeyDir = getLockKeyDir(key)
|
|
1330
1289
|
try {
|
|
1331
|
-
await fp.
|
|
1290
|
+
await fp.rename(tempLockDir, lockKeyDir);
|
|
1332
1291
|
} catch (err) {
|
|
1333
|
-
|
|
1334
|
-
try {
|
|
1335
|
-
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1336
|
-
} catch (_) {}
|
|
1292
|
+
try { await fp.rm(tempLockDir, { recursive: true, force: true }); } catch (_) {}
|
|
1337
1293
|
return false;
|
|
1338
1294
|
}
|
|
1339
1295
|
|
|
1340
|
-
//
|
|
1296
|
+
// 成功加锁后执行 fn
|
|
1341
1297
|
try {
|
|
1342
1298
|
await fn();
|
|
1343
1299
|
return true;
|
|
1344
1300
|
} finally {
|
|
1345
|
-
try {
|
|
1346
|
-
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1347
|
-
} catch (_) {}
|
|
1301
|
+
try { await fp.rm(lockKeyDir, { recursive: true, force: true }); } catch (_) {}
|
|
1348
1302
|
}
|
|
1349
1303
|
}
|
|
1350
1304
|
|
|
@@ -1388,21 +1342,20 @@ async function processLock(key, fn, wait = true) {
|
|
|
1388
1342
|
}
|
|
1389
1343
|
}
|
|
1390
1344
|
|
|
1391
|
-
const $locks = {}
|
|
1392
1345
|
async function _processLock(key, fn) {
|
|
1393
1346
|
if (!key || typeof fn !== 'function') {
|
|
1394
1347
|
throw new Error('invalid arguments');
|
|
1395
1348
|
}
|
|
1396
1349
|
key = key.trim();
|
|
1397
|
-
if (
|
|
1350
|
+
if (setting.locks.hasOwnProperty(key)) {
|
|
1398
1351
|
return false;
|
|
1399
1352
|
}
|
|
1400
|
-
|
|
1353
|
+
setting.locks[key] = true;
|
|
1401
1354
|
try {
|
|
1402
1355
|
await fn();
|
|
1403
1356
|
return true;
|
|
1404
1357
|
} finally {
|
|
1405
|
-
delete
|
|
1358
|
+
delete setting.locks[key];
|
|
1406
1359
|
}
|
|
1407
1360
|
}
|
|
1408
1361
|
|
|
@@ -1519,6 +1472,7 @@ async function cleanFile(path, maxChars = 9 * 1024 * 1024) {
|
|
|
1519
1472
|
const bakFile = `${path}.bak`;
|
|
1520
1473
|
// 备份日志
|
|
1521
1474
|
try {
|
|
1475
|
+
await fp.rm(bakFile, { force: true });
|
|
1522
1476
|
await fp.rename(path, bakFile);
|
|
1523
1477
|
} catch (e) {
|
|
1524
1478
|
console.$error(`Failed to rename ${path} -> ${bakFile}`, e);
|
|
@@ -1610,9 +1564,9 @@ function ei(cmd, args = [], shell = false) {
|
|
|
1610
1564
|
|
|
1611
1565
|
async function eia(cmd, args = [], shell = false, input = null) {
|
|
1612
1566
|
`
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1567
|
+
当前进程不会卡住(事件循环仍在),但会 await 等子进程退出;
|
|
1568
|
+
输入输出默认由 cmd 进程持有;如果传 input,则把 input 写入 stdin。
|
|
1569
|
+
`
|
|
1616
1570
|
if (isRunningInBackground()) {
|
|
1617
1571
|
throw 'Unsupported Operation';
|
|
1618
1572
|
}
|
|
@@ -1643,18 +1597,51 @@ async function eia(cmd, args = [], shell = false, input = null) {
|
|
|
1643
1597
|
});
|
|
1644
1598
|
}
|
|
1645
1599
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1600
|
+
/**
|
|
1601
|
+
* 获取剪贴板文本(macOS)
|
|
1602
|
+
* @returns {Promise<string|null>}
|
|
1603
|
+
*/
|
|
1604
|
+
async function getCbText() {
|
|
1605
|
+
return new Promise((resolve) => {
|
|
1606
|
+
try {
|
|
1607
|
+
const p = spawn("pbpaste");
|
|
1608
|
+
let out = "";
|
|
1609
|
+
|
|
1610
|
+
p.stdout.setEncoding("utf8");
|
|
1611
|
+
p.stdout.on("data", (d) => out += d);
|
|
1612
|
+
|
|
1613
|
+
p.on("error", () => resolve(null));
|
|
1614
|
+
p.on("close", (code) => {
|
|
1615
|
+
if (code === 0) resolve(out);
|
|
1616
|
+
else resolve(null);
|
|
1617
|
+
});
|
|
1618
|
+
} catch (e) {
|
|
1619
|
+
resolve(null);
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1648
1622
|
}
|
|
1649
1623
|
|
|
1624
|
+
/**
|
|
1625
|
+
* 设置剪贴板文本(macOS)
|
|
1626
|
+
* @param {string} str
|
|
1627
|
+
* @returns {Promise<boolean>}
|
|
1628
|
+
*/
|
|
1650
1629
|
async function setCbText(str) {
|
|
1651
|
-
|
|
1652
|
-
mkdir(tempDir)
|
|
1630
|
+
if (typeof str !== "string") return false;
|
|
1653
1631
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1632
|
+
return new Promise((resolve) => {
|
|
1633
|
+
try {
|
|
1634
|
+
const p = spawn("pbcopy");
|
|
1635
|
+
|
|
1636
|
+
p.on("error", () => resolve(false));
|
|
1637
|
+
p.on("close", (code) => resolve(code === 0));
|
|
1638
|
+
|
|
1639
|
+
p.stdin.setDefaultEncoding("utf8");
|
|
1640
|
+
p.stdin.end(str);
|
|
1641
|
+
} catch (e) {
|
|
1642
|
+
resolve(false);
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1658
1645
|
}
|
|
1659
1646
|
|
|
1660
1647
|
async function sleep(milliseconds, tIds = []) {
|
|
@@ -2380,7 +2367,6 @@ module.exports = {
|
|
|
2380
2367
|
hasTips,
|
|
2381
2368
|
tipKeys,
|
|
2382
2369
|
isPidAlive,
|
|
2383
|
-
cleanFileLocks,
|
|
2384
2370
|
getMd5Key,
|
|
2385
2371
|
isMd5Key,
|
|
2386
2372
|
terminalTitle,
|
|
@@ -2392,5 +2378,6 @@ module.exports = {
|
|
|
2392
2378
|
currentRoom,
|
|
2393
2379
|
currentRooms,
|
|
2394
2380
|
uid,
|
|
2395
|
-
consoleStrs
|
|
2381
|
+
consoleStrs,
|
|
2382
|
+
getLockDir
|
|
2396
2383
|
}
|