Files
NetMesh/electron/preload/terminalDataBacklog.cjs
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

140 lines
4.2 KiB
JavaScript

"use strict";
const { mergeTerminalDataMeta } = require("./terminalDataMeta.cjs");
function hasPluginPipelineIngress(meta) {
return Number.isFinite(meta?.pluginPipelineIngressBytes)
&& Number(meta.pluginPipelineIngressBytes) > 0;
}
function hasPluginPipelineIngressMarker(meta) {
return Number.isFinite(meta?.pluginPipelineIngressBytes)
&& Number(meta.pluginPipelineIngressBytes) >= 0;
}
function createTerminalDataBacklog(options = {}) {
const maxBytesPerSession = options.maxBytesPerSession ?? 64 * 1024;
const pendingBySession = new Map();
function trimToLimit(value) {
if (value.length <= maxBytesPerSession) return value;
return value.slice(value.length - maxBytesPerSession);
}
function append(sessionId, data, meta) {
if (!sessionId || (!data && !hasPluginPipelineIngressMarker(meta))) return;
const previous = pendingBySession.get(sessionId) || { data: "", meta: undefined };
const nextData = trimToLimit(previous.data + data);
const preserveTerminalPerf = previous.data.length === 0 && nextData === data;
let previousMeta = previous.meta;
let nextChunkMeta = meta;
const previousHasIngress = Number.isFinite(previousMeta?.pluginPipelineIngressBytes);
const nextChunkHasIngress = Number.isFinite(nextChunkMeta?.pluginPipelineIngressBytes);
// Once one merged chunk carries explicit original-ingress accounting, the
// metadata must cover every raw flow unit in the same replay entry. Flow
// control is intentionally charged in JavaScript string length, not UTF-8
// bytes, throughout the terminal renderer/worker path. Otherwise a
// processed chunk followed or preceded by ordinary output would cause the
// renderer to acknowledge only the annotated subset.
if (previousHasIngress && !nextChunkHasIngress && data) {
nextChunkMeta = {
...(nextChunkMeta || {}),
pluginPipelineIngressBytes: data.length,
};
} else if (!previousHasIngress && nextChunkHasIngress && previous.data) {
previousMeta = {
...(previousMeta || {}),
pluginPipelineIngressBytes: previous.data.length,
};
}
const nextMeta = mergeTerminalDataMeta(previousMeta, nextChunkMeta, { preserveTerminalPerf });
pendingBySession.set(sessionId, {
data: nextData,
meta: nextMeta,
});
}
function takeEntry(sessionId) {
const entry = pendingBySession.get(sessionId) || { data: "", meta: undefined };
pendingBySession.delete(sessionId);
return entry;
}
function take(sessionId) {
return takeEntry(sessionId).data;
}
function clear(sessionId) {
pendingBySession.delete(sessionId);
}
function size(sessionId) {
return pendingBySession.get(sessionId)?.data.length ?? 0;
}
return {
append,
take,
takeEntry,
clear,
size,
};
}
function hasSessionListeners(listenersBySession, sessionId) {
return (listenersBySession.get(sessionId)?.size ?? 0) > 0;
}
function createTerminalDataDispatcher({
dataListeners,
displayDataListeners,
terminalDataBacklog,
onCallbackError = console.error,
shouldDropSession = () => false,
}) {
return function deliverToListeners(sessionId, data, meta) {
if (!data && !hasPluginPipelineIngressMarker(meta)) return;
if (shouldDropSession(sessionId)) return;
if (!hasSessionListeners(displayDataListeners, sessionId)) {
terminalDataBacklog?.append?.(sessionId, data, meta);
}
const set = dataListeners.get(sessionId);
if (!set || set.size === 0) return;
set.forEach((cb) => {
try {
cb(data, meta);
} catch (err) {
onCallbackError("Data callback failed", err);
}
});
};
}
function clearTerminalDataSession({
dataListeners,
displayDataListeners,
terminalDataBacklog,
}, sessionId) {
dataListeners?.delete?.(sessionId);
displayDataListeners?.delete?.(sessionId);
terminalDataBacklog?.clear?.(sessionId);
}
function clearTerminalDataBacklog({
terminalDataBacklog,
}, sessionId) {
terminalDataBacklog?.clear?.(sessionId);
}
module.exports = {
clearTerminalDataBacklog,
createTerminalDataBacklog,
createTerminalDataDispatcher,
clearTerminalDataSession,
hasPluginPipelineIngress,
hasPluginPipelineIngressMarker,
};