[Init] Initial commit - NetMesh terminal manager
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
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
This commit is contained in:
139
electron/preload/terminalDataBacklog.cjs
Normal file
139
electron/preload/terminalDataBacklog.cjs
Normal file
@@ -0,0 +1,139 @@
|
||||
"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,
|
||||
};
|
||||
Reference in New Issue
Block a user