mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 22:52:58 +00:00
Restore configured terminal history on reconnect, retaining 10,000 rows plus the screen. Serialize the worker-owned xterm buffer directly to ANSI and forward negotiated bounded replay chunks while preserving ANSI-era clients and per-viewer live ordering. Preserve captured geometry through replay, bound pressure recovery to 500 rows, and keep the omitted visible-history default at 200. Remove the full cell-grid copy and obsolete JSON rendering. All 21 CI checks pass. Real browser, native Debug emulator/simulator and Linux Electron coverage and platform limits are documented in the PR. The Android Debug invalid native delegate dispatch also reproduces on upstream main; its cause and any change in frequency remain unproven. Credit Manav Dhindsa (DrakenKor), #6139 and #6148, and Lennart Schoch, #6743, for prior ANSI replay and retention work informing this change. Closes #6137. Closes #6147.
107 lines
3.4 KiB
TypeScript
107 lines
3.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
decodeBinaryFrame,
|
|
encodeHostRouteFrame,
|
|
decodeHostRouteFrame,
|
|
MAX_HOST_ROUTE_ID_BYTES,
|
|
encodeFileTransferFrame,
|
|
encodeTerminalStreamFrame,
|
|
FileTransferOpcode,
|
|
TerminalStreamOpcode,
|
|
} from "./index.js";
|
|
|
|
describe("binary frame demux", () => {
|
|
it("routes terminal frames by opcode", () => {
|
|
expect(
|
|
decodeBinaryFrame(
|
|
encodeTerminalStreamFrame({
|
|
opcode: TerminalStreamOpcode.Input,
|
|
slot: 7,
|
|
payload: "ls",
|
|
}),
|
|
),
|
|
).toEqual({
|
|
kind: "terminal",
|
|
frame: {
|
|
opcode: TerminalStreamOpcode.Input,
|
|
slot: 7,
|
|
payload: new TextEncoder().encode("ls"),
|
|
},
|
|
});
|
|
});
|
|
|
|
it("routes file-transfer frames by opcode", () => {
|
|
expect(
|
|
decodeBinaryFrame(
|
|
encodeFileTransferFrame({
|
|
opcode: FileTransferOpcode.FileChunk,
|
|
requestId: "req-upload",
|
|
payload: new TextEncoder().encode("hello"),
|
|
}),
|
|
),
|
|
).toEqual({
|
|
kind: "file_transfer",
|
|
frame: {
|
|
opcode: FileTransferOpcode.FileChunk,
|
|
requestId: "req-upload",
|
|
payload: new TextEncoder().encode("hello"),
|
|
},
|
|
});
|
|
});
|
|
|
|
it("rejects unknown binary opcodes", () => {
|
|
expect(decodeBinaryFrame(new Uint8Array([0xff, 0]))).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("host route binary framing", () => {
|
|
it("wraps terminal and file frames unchanged and leaves their direct interpretation intact", () => {
|
|
const frames = [
|
|
encodeTerminalStreamFrame({ opcode: TerminalStreamOpcode.Input, slot: 7, payload: "echo" }),
|
|
encodeFileTransferFrame({
|
|
opcode: FileTransferOpcode.FileChunk,
|
|
requestId: "upload",
|
|
payload: new Uint8Array([0, 255, 128, 1]),
|
|
}),
|
|
];
|
|
for (const payload of frames) {
|
|
const wire = encodeHostRouteFrame("channel", payload);
|
|
const decoded = decodeBinaryFrame(wire);
|
|
expect(decoded).toEqual({ kind: "host_route", frame: { routeId: "channel", payload } });
|
|
expect(wire.subarray(10)).toEqual(payload);
|
|
expect(decodeBinaryFrame(decodeHostRouteFrame(wire)!.payload)).toEqual(
|
|
decodeBinaryFrame(payload),
|
|
);
|
|
}
|
|
});
|
|
it("rejects truncated, oversized identifiers and invalid UTF-8 without throwing", () => {
|
|
for (const bytes of [
|
|
new Uint8Array([0x30]),
|
|
new Uint8Array([0x30, 0, 4, 65]),
|
|
new Uint8Array([0x30, 0, 0]),
|
|
new Uint8Array([0x30, 1, 1]),
|
|
new Uint8Array([0x30, 0, 1, 255]),
|
|
])
|
|
expect(decodeBinaryFrame(bytes)).toBeNull();
|
|
expect(() =>
|
|
encodeHostRouteFrame("a".repeat(MAX_HOST_ROUTE_ID_BYTES + 1), new Uint8Array()),
|
|
).toThrow("Invalid host route ID");
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
TerminalStreamOpcode.ReplayBegin,
|
|
TerminalStreamOpcode.ReplayData,
|
|
TerminalStreamOpcode.ReplayEnd,
|
|
])("decodes replay opcode %s directly and inside a host route", (opcode) => {
|
|
const payload = new TextEncoder().encode("界 replay");
|
|
const bytes = encodeTerminalStreamFrame({ opcode, slot: 12, payload });
|
|
const expected = { kind: "terminal", frame: { opcode, slot: 12, payload } };
|
|
expect(decodeBinaryFrame(bytes)).toEqual(expected);
|
|
const routed = decodeBinaryFrame(encodeHostRouteFrame("host", bytes));
|
|
expect(routed?.kind).toBe("host_route");
|
|
if (routed?.kind !== "host_route") throw new Error("Missing host route");
|
|
expect(decodeBinaryFrame(routed.frame.payload)).toEqual(expected);
|
|
});
|