refactor(core): canonicalize pty service (#32182)
This commit is contained in:
parent
7efade2d53
commit
f2cf607376
30 changed files with 1132 additions and 504 deletions
|
|
@ -1,24 +0,0 @@
|
|||
import { Effect } from "effect"
|
||||
|
||||
const inputDecoder = new TextDecoder("utf-8", { fatal: true })
|
||||
|
||||
export function handlePtyInput(
|
||||
handler: { onMessage: (message: string | ArrayBuffer) => void },
|
||||
message: string | Uint8Array,
|
||||
) {
|
||||
if (typeof message === "string") {
|
||||
handler.onMessage(message)
|
||||
return Effect.void
|
||||
}
|
||||
return Effect.try({
|
||||
try: () => inputDecoder.decode(message),
|
||||
catch: () => new Error("invalid PTY websocket input"),
|
||||
}).pipe(
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.flatMap((decoded) => {
|
||||
if (decoded === undefined) return Effect.void
|
||||
handler.onMessage(decoded)
|
||||
return Effect.void
|
||||
}),
|
||||
)
|
||||
}
|
||||
37
packages/core/src/pty/protocol.ts
Normal file
37
packages/core/src/pty/protocol.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export * as PtyProtocol from "./protocol"
|
||||
|
||||
// Wire protocol for PTY websocket transports. The PTY domain service is transport-free; server
|
||||
// routes adapt Pty.attach to websockets with these helpers so every surface speaks one protocol.
|
||||
//
|
||||
// Outbound frames are raw UTF-8 terminal chunks. One control frame — a 0x00 byte followed by
|
||||
// UTF-8 JSON — carries the absolute output cursor after replay so clients can resume later.
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
const decoder = new TextDecoder("utf-8", { fatal: true })
|
||||
|
||||
// Replay can be megabytes; send it in bounded frames.
|
||||
export const REPLAY_CHUNK = 64 * 1024
|
||||
|
||||
export function metaFrame(cursor: number) {
|
||||
const bytes = encoder.encode(JSON.stringify({ cursor }))
|
||||
const out = new Uint8Array(bytes.length + 1)
|
||||
out[0] = 0
|
||||
out.set(bytes, 1)
|
||||
return out
|
||||
}
|
||||
|
||||
export function chunks(data: string) {
|
||||
const out: string[] = []
|
||||
for (let i = 0; i < data.length; i += REPLAY_CHUNK) out.push(data.slice(i, i + REPLAY_CHUNK))
|
||||
return out
|
||||
}
|
||||
|
||||
// Inbound client frames are UTF-8 text or binary; invalid UTF-8 input is dropped.
|
||||
export function decodeInput(message: string | Uint8Array | ArrayBuffer) {
|
||||
if (typeof message === "string") return message
|
||||
try {
|
||||
return decoder.decode(message instanceof ArrayBuffer ? new Uint8Array(message) : message)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue