dsh-sessions-manager 3.6.0 → 3.6.1
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/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/index.js +113 -91
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
- package/src/index.js +154 -113
- package/src/session-meta-cache.js +18 -0
package/README.en.md
CHANGED
|
@@ -154,7 +154,7 @@ lib/index.js pre-built host (ESM)
|
|
|
154
154
|
lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
`lib/` is pre-built, so cloning and using it needs no esbuild. To modify the source, run `npm i -D esbuild && npm run build` to regenerate `lib
|
|
157
|
+
`lib/` is pre-built, so cloning and using it needs no esbuild. To modify the source, run `npm i -D esbuild && npm run build` to regenerate `lib/`; before committing, `npm run check:dist` rebuilds and diffs `lib/` against the committed bundles (CI runs the same check), so **always rebuild after a version bump** — the build stamp is reproducible as `vX.Y.Z+source-hash`, which is what makes that comparison exact.
|
|
158
158
|
|
|
159
159
|
## Host routes
|
|
160
160
|
|
package/README.md
CHANGED
|
@@ -154,7 +154,7 @@ lib/index.js 预构建 host(ESM)
|
|
|
154
154
|
lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
`lib/` 已预构建,clone 下来即可直接用、无需 esbuild。若要改源码,运行 `npm i -D esbuild && npm run build` 重新生成 `lib
|
|
157
|
+
`lib/` 已预构建,clone 下来即可直接用、无需 esbuild。若要改源码,运行 `npm i -D esbuild && npm run build` 重新生成 `lib/`;提交前用 `npm run check:dist` 重建并与仓库产物比对(CI 跑同一道检查),**改版本号后务必一并重建**——构建指纹可复现为 `vX.Y.Z+源码哈希`,正是为了让这道比对精确可靠。
|
|
158
158
|
|
|
159
159
|
## Host 路由
|
|
160
160
|
|
package/lib/index.js
CHANGED
|
@@ -614,6 +614,12 @@ function fingerprintOf(stat5) {
|
|
|
614
614
|
function isPersistableFingerprint(fingerprint) {
|
|
615
615
|
return typeof fingerprint === "string" && fingerprint !== "" && !fingerprint.startsWith(REVISION_PREFIX);
|
|
616
616
|
}
|
|
617
|
+
function persistFingerprintOf(stat5) {
|
|
618
|
+
const fp = fingerprintOf(stat5);
|
|
619
|
+
if (fp && isPersistableFingerprint(fp)) return fp;
|
|
620
|
+
if (stat5 && Number.isFinite(stat5.sizeBytes) && stat5.sizeBytes >= 0) return `sz:${stat5.sizeBytes}`;
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
617
623
|
function isFresh(entry, stat5, now, ttlMs = DEFAULT_TTL_MS) {
|
|
618
624
|
if (!entry) return false;
|
|
619
625
|
const fp = fingerprintOf(stat5);
|
|
@@ -1408,7 +1414,7 @@ function classifyLineage(header, sizeBytes) {
|
|
|
1408
1414
|
}
|
|
1409
1415
|
|
|
1410
1416
|
// src/index.js
|
|
1411
|
-
var BUILD_STAMP = true ? "3.6.
|
|
1417
|
+
var BUILD_STAMP = true ? "3.6.1+f0814a9e" : "dev";
|
|
1412
1418
|
var name = "dsh-sessions-manager";
|
|
1413
1419
|
var inject = ["webServer", "workspaceRegistry", "sessionPersistence", "sessionQuery", "storageDomain"];
|
|
1414
1420
|
var MAX_TITLE = 80;
|
|
@@ -1530,8 +1536,8 @@ function apply(ctx) {
|
|
|
1530
1536
|
const stat5 = statsById.get(id);
|
|
1531
1537
|
const entry = store && store[id];
|
|
1532
1538
|
if (!stat5 || !entry) continue;
|
|
1533
|
-
const fp =
|
|
1534
|
-
if (!
|
|
1539
|
+
const fp = persistFingerprintOf(stat5);
|
|
1540
|
+
if (!fp) continue;
|
|
1535
1541
|
if (fp && entry.fingerprint === fp) {
|
|
1536
1542
|
hits.set(id, { title: entry.title, cwd: entry.cwd, createdAt: entry.createdAt });
|
|
1537
1543
|
}
|
|
@@ -1543,8 +1549,8 @@ function apply(ctx) {
|
|
|
1543
1549
|
const batch = {};
|
|
1544
1550
|
const now = Date.now();
|
|
1545
1551
|
for (const [id, meta] of decoded) {
|
|
1546
|
-
const fp =
|
|
1547
|
-
if (!
|
|
1552
|
+
const fp = persistFingerprintOf(statsById.get(id));
|
|
1553
|
+
if (!fp) continue;
|
|
1548
1554
|
batch[id] = { title: meta.title, cwd: meta.cwd, createdAt: meta.createdAt, fingerprint: fp, updatedAt: now };
|
|
1549
1555
|
}
|
|
1550
1556
|
if (!Object.keys(batch).length) return;
|
|
@@ -1623,50 +1629,63 @@ function apply(ctx) {
|
|
|
1623
1629
|
}
|
|
1624
1630
|
return base;
|
|
1625
1631
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1632
|
+
const EMPTY_META = { title: null, cwd: null, createdAt: null };
|
|
1633
|
+
const WARM_CHUNK = 4;
|
|
1634
|
+
const warmQueue = /* @__PURE__ */ new Map();
|
|
1635
|
+
let warmRunning = false;
|
|
1636
|
+
let warmKickTimer = null;
|
|
1637
|
+
function scheduleWarm() {
|
|
1638
|
+
if (warmKickTimer || warmRunning) return;
|
|
1639
|
+
warmKickTimer = setTimeout(() => {
|
|
1640
|
+
warmKickTimer = null;
|
|
1641
|
+
runWarm();
|
|
1642
|
+
}, 25);
|
|
1643
|
+
if (typeof warmKickTimer.unref === "function") warmKickTimer.unref();
|
|
1644
|
+
}
|
|
1645
|
+
function enqueueWarm(ids, statsById, entryById) {
|
|
1646
|
+
if (!ids || !ids.length) return;
|
|
1647
|
+
for (const id of ids) {
|
|
1648
|
+
const key = String(id);
|
|
1649
|
+
const entry = entryById ? entryById.get(key) : null;
|
|
1650
|
+
warmQueue.set(key, {
|
|
1651
|
+
stat: statsById ? statsById.get(key) || null : null,
|
|
1652
|
+
header: entry && entry.header || null
|
|
1653
|
+
});
|
|
1649
1654
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1655
|
+
scheduleWarm();
|
|
1656
|
+
}
|
|
1657
|
+
async function runWarm() {
|
|
1658
|
+
if (warmRunning) return;
|
|
1659
|
+
warmRunning = true;
|
|
1660
|
+
try {
|
|
1661
|
+
while (warmQueue.size) {
|
|
1662
|
+
const batch = [...warmQueue.entries()].slice(0, WARM_CHUNK);
|
|
1663
|
+
for (const [id] of batch) warmQueue.delete(id);
|
|
1664
|
+
try {
|
|
1665
|
+
const ids = batch.map(([id]) => id);
|
|
1666
|
+
const snapshotById = await projectTitles(ids);
|
|
1667
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
1668
|
+
const statsById = /* @__PURE__ */ new Map();
|
|
1669
|
+
for (const [id, desc] of batch) {
|
|
1670
|
+
const snapshot = snapshotById.has(id) ? snapshotById.get(id) : null;
|
|
1671
|
+
const meta = metaFromSnapshot(snapshot);
|
|
1672
|
+
if (desc.header) {
|
|
1673
|
+
if (typeof desc.header.cwd === "string") meta.cwd = desc.header.cwd;
|
|
1674
|
+
if (desc.header.createdAt != null) meta.createdAt = desc.header.createdAt;
|
|
1675
|
+
}
|
|
1676
|
+
metaCache.set(id, desc.stat, meta);
|
|
1677
|
+
statsById.set(id, desc.stat);
|
|
1678
|
+
if (desc.stat) decoded.set(id, meta);
|
|
1657
1679
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
if (!meta.cwd) meta.cwd = summary.meta.cwd || null;
|
|
1661
|
-
if (!meta.createdAt) meta.createdAt = summary.meta.createdAt || null;
|
|
1680
|
+
await persistDecoded(decoded, statsById);
|
|
1681
|
+
} catch (e) {
|
|
1662
1682
|
}
|
|
1663
|
-
|
|
1664
|
-
} catch (e2) {
|
|
1683
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1665
1684
|
}
|
|
1685
|
+
} finally {
|
|
1686
|
+
warmRunning = false;
|
|
1687
|
+
if (warmQueue.size) scheduleWarm();
|
|
1666
1688
|
}
|
|
1667
|
-
metaCache.set(key, statInfo, meta);
|
|
1668
|
-
if (opts.collectDecoded && statInfo) opts.collectDecoded(key, meta);
|
|
1669
|
-
return buildItem(key, meta, usage, opts.exposeUsage);
|
|
1670
1689
|
}
|
|
1671
1690
|
async function collectUsage(preloadedEntries) {
|
|
1672
1691
|
const sizeById = /* @__PURE__ */ new Map();
|
|
@@ -1690,7 +1709,7 @@ function apply(ctx) {
|
|
|
1690
1709
|
if (!id) return;
|
|
1691
1710
|
if (entry && typeof entry.revision === "string" && entry.revision) {
|
|
1692
1711
|
if (Number.isFinite(entry.sizeBytes)) sizeById.set(id, Number(entry.sizeBytes));
|
|
1693
|
-
statsById.set(id, { revision: entry.revision });
|
|
1712
|
+
statsById.set(id, Number.isFinite(entry.sizeBytes) ? { revision: entry.revision, sizeBytes: Number(entry.sizeBytes) } : { revision: entry.revision });
|
|
1694
1713
|
return;
|
|
1695
1714
|
}
|
|
1696
1715
|
if (entry && Number.isFinite(entry.sizeBytes)) sizeById.set(id, Number(entry.sizeBytes));
|
|
@@ -2491,35 +2510,48 @@ function apply(ctx) {
|
|
|
2491
2510
|
wsByPath = {};
|
|
2492
2511
|
}
|
|
2493
2512
|
const currentArchived = new Set((await archivedState().catch(() => ({ archivedSessionIds: [] }))).archivedSessionIds || []);
|
|
2494
|
-
const items = [];
|
|
2495
2513
|
const usage = await collectUsage(entries);
|
|
2496
2514
|
const statsById = new Map(visibleIds.map((id) => [
|
|
2497
2515
|
id,
|
|
2498
2516
|
usage.statsById && usage.statsById.get(id) || { mtimeMs: usage.mtimeById.get(id), size: usage.sizeById.get(id) }
|
|
2499
2517
|
]));
|
|
2500
|
-
const { missing } = metaCache.partition(visibleIds, statsById);
|
|
2518
|
+
const { cached, missing } = metaCache.partition(visibleIds, statsById);
|
|
2501
2519
|
const persisted = await hydrateFromPersist(missing, statsById);
|
|
2502
|
-
for (const [id, meta] of persisted)
|
|
2520
|
+
for (const [id, meta] of persisted) {
|
|
2521
|
+
const entry = entryById.get(id);
|
|
2522
|
+
const header = entry && entry.header ? entry.header : null;
|
|
2523
|
+
metaCache.set(id, statsById.get(id), {
|
|
2524
|
+
title: meta.title,
|
|
2525
|
+
cwd: header && typeof header.cwd === "string" && header.cwd ? header.cwd : meta.cwd,
|
|
2526
|
+
createdAt: header && header.createdAt != null ? header.createdAt : meta.createdAt
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2503
2529
|
const stillMissing = missing.filter((id) => !persisted.has(id));
|
|
2504
|
-
|
|
2505
|
-
const
|
|
2506
|
-
const
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
for (let i = 0; i < visibleIds.length; i += CHUNK) {
|
|
2511
|
-
const res2 = await Promise.all(visibleIds.slice(i, i + CHUNK).map((id) => {
|
|
2530
|
+
if (stillMissing.length) enqueueWarm(stillMissing, statsById, entryById);
|
|
2531
|
+
const items = [];
|
|
2532
|
+
for (const id of visibleIds) {
|
|
2533
|
+
let meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || null;
|
|
2534
|
+
if (!meta) {
|
|
2535
|
+
meta = { title: null, cwd: null, createdAt: null };
|
|
2512
2536
|
const entry = entryById.get(id);
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2537
|
+
const header = entry && entry.header ? entry.header : null;
|
|
2538
|
+
if (header) {
|
|
2539
|
+
if (typeof header.cwd === "string") meta.cwd = header.cwd;
|
|
2540
|
+
if (header.createdAt != null) meta.createdAt = header.createdAt;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
const it = buildItem(id, meta, usage, !!(opts && opts.usage));
|
|
2544
|
+
items.push({ ...it, archived: currentArchived.has(it.sessionId) });
|
|
2545
|
+
}
|
|
2546
|
+
if (opts && opts.onlyArchived) {
|
|
2547
|
+
const archivedItems = [];
|
|
2548
|
+
for (const it of items) {
|
|
2549
|
+
if (!it.archived) continue;
|
|
2550
|
+
const { archived: _drop, ...rest } = it;
|
|
2551
|
+
archivedItems.push(rest);
|
|
2552
|
+
}
|
|
2553
|
+
return { items: archivedItems, usage };
|
|
2521
2554
|
}
|
|
2522
|
-
await persistDecoded(decoded, statsById);
|
|
2523
2555
|
let starredSet = /* @__PURE__ */ new Set();
|
|
2524
2556
|
try {
|
|
2525
2557
|
starredSet = new Set((await stars.read()).starredSessionIds);
|
|
@@ -2597,36 +2629,28 @@ function apply(ctx) {
|
|
|
2597
2629
|
const activeTombstones = store.purgedSessionIds.map(String).filter((id) => !present.has(id));
|
|
2598
2630
|
if (ids.length) {
|
|
2599
2631
|
const usage = await collectUsage(entries);
|
|
2632
|
+
const entryById = new Map(entries.map((entry) => [entry.id, entry]));
|
|
2600
2633
|
const statsById = new Map(ids.map((id) => [
|
|
2601
2634
|
id,
|
|
2602
2635
|
usage.statsById && usage.statsById.get(id) || { mtimeMs: usage.mtimeById.get(id), size: usage.sizeById.get(id) }
|
|
2603
2636
|
]));
|
|
2604
2637
|
const { cached, missing } = metaCache.partition(ids, statsById);
|
|
2605
2638
|
const persisted = await hydrateFromPersist(missing, statsById);
|
|
2606
|
-
for (const [id, meta] of persisted)
|
|
2639
|
+
for (const [id, meta] of persisted) {
|
|
2640
|
+
const entry = entryById.get(id);
|
|
2641
|
+
const header = entry && entry.header ? entry.header : null;
|
|
2642
|
+
metaCache.set(id, statsById.get(id), {
|
|
2643
|
+
title: meta.title,
|
|
2644
|
+
cwd: header && typeof header.cwd === "string" && header.cwd ? header.cwd : meta.cwd,
|
|
2645
|
+
createdAt: header && header.createdAt != null ? header.createdAt : meta.createdAt
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2607
2648
|
const rest = missing.filter((id) => !persisted.has(id));
|
|
2608
|
-
|
|
2609
|
-
const decoded = /* @__PURE__ */ new Map();
|
|
2610
|
-
const collectDecoded = (id, meta) => {
|
|
2611
|
-
decoded.set(id, meta);
|
|
2612
|
-
};
|
|
2649
|
+
if (rest.length) enqueueWarm(rest, statsById, entryById);
|
|
2613
2650
|
for (const id of ids) {
|
|
2614
|
-
|
|
2615
|
-
if (!meta) {
|
|
2616
|
-
const entry = entries.find((e) => e.id === id);
|
|
2617
|
-
const snapshot = snapshotById.has(id) ? snapshotById.get(id) : typeof sq.readTitleSnapshot === "function" ? await sq.readTitleSnapshot(id).catch(() => null) : null;
|
|
2618
|
-
const next = metaFromSnapshot(snapshot);
|
|
2619
|
-
if (entry && entry.header) {
|
|
2620
|
-
if (!next.cwd && typeof entry.header.cwd === "string") next.cwd = entry.header.cwd;
|
|
2621
|
-
if (!next.createdAt && entry.header.createdAt != null) next.createdAt = entry.header.createdAt;
|
|
2622
|
-
}
|
|
2623
|
-
metaCache.set(id, statsById.get(id), next);
|
|
2624
|
-
if (statsById.get(id)) collectDecoded(id, next);
|
|
2625
|
-
meta = next;
|
|
2626
|
-
}
|
|
2651
|
+
const meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || null;
|
|
2627
2652
|
if (meta && meta.title) authorityTitleCache.set(id, String(meta.title));
|
|
2628
2653
|
}
|
|
2629
|
-
await persistDecoded(decoded, statsById);
|
|
2630
2654
|
}
|
|
2631
2655
|
const lineage = {};
|
|
2632
2656
|
for (const entry of entries) {
|
|
@@ -2866,12 +2890,10 @@ function apply(ctx) {
|
|
|
2866
2890
|
} catch (e) {
|
|
2867
2891
|
wsByPath = {};
|
|
2868
2892
|
}
|
|
2869
|
-
const
|
|
2870
|
-
const
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
items.push.apply(items, res2);
|
|
2874
|
-
}
|
|
2893
|
+
const wanted = new Set(idStrs);
|
|
2894
|
+
const detailed = await allSessionItemsDetailed();
|
|
2895
|
+
const items = detailed.items.filter((it) => wanted.has(String(it.sessionId)) && it.archived);
|
|
2896
|
+
for (const it of items) delete it.archived;
|
|
2875
2897
|
json(res, { items });
|
|
2876
2898
|
} catch (e) {
|
|
2877
2899
|
json(res, { error: String(e && e.message || e) }, 500);
|