refactor(daemon): remove abandoned binary framing (#13530)

This commit is contained in:
Neil
2026-08-10 20:24:56 -07:00
committed by GitHub
parent 3e3f48fe89
commit fb39308dfe
3 changed files with 0 additions and 240 deletions
-167
View File
@@ -1,167 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { encodeFrame, createFrameParser, FrameType, FRAME_HEADER_SIZE } from './binary-frame'
describe('encodeFrame', () => {
it('encodes a data frame with correct header', () => {
const payload = Buffer.from('hello')
const frame = encodeFrame(FrameType.Data, payload)
expect(frame.length).toBe(FRAME_HEADER_SIZE + payload.length)
expect(frame[0]).toBe(FrameType.Data)
expect(frame.readUInt32BE(1)).toBe(payload.length)
expect(frame.subarray(FRAME_HEADER_SIZE).toString()).toBe('hello')
})
it('encodes a resize frame', () => {
const payload = Buffer.from(JSON.stringify({ cols: 120, rows: 40 }))
const frame = encodeFrame(FrameType.Resize, payload)
expect(frame[0]).toBe(FrameType.Resize)
expect(frame.readUInt32BE(1)).toBe(payload.length)
})
it('encodes an exit frame with exit code', () => {
const payload = Buffer.from(JSON.stringify({ code: 0 }))
const frame = encodeFrame(FrameType.Exit, payload)
expect(frame[0]).toBe(FrameType.Exit)
const decoded = JSON.parse(frame.subarray(FRAME_HEADER_SIZE).toString())
expect(decoded.code).toBe(0)
})
it('encodes empty payload', () => {
const frame = encodeFrame(FrameType.Kill, Buffer.alloc(0))
expect(frame.length).toBe(FRAME_HEADER_SIZE)
expect(frame[0]).toBe(FrameType.Kill)
expect(frame.readUInt32BE(1)).toBe(0)
})
it('throws on payload exceeding max size', () => {
const oversized = Buffer.alloc(1024 * 1024 + 1)
expect(() => encodeFrame(FrameType.Data, oversized)).toThrow()
})
})
describe('createFrameParser', () => {
it('parses a single complete frame', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const payload = Buffer.from('test data')
const frame = encodeFrame(FrameType.Data, payload)
parser.feed(frame)
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][0]).toBe(FrameType.Data)
expect(onFrame.mock.calls[0][1].toString()).toBe('test data')
})
it('parses multiple frames in one chunk', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const frame1 = encodeFrame(FrameType.Data, Buffer.from('one'))
const frame2 = encodeFrame(FrameType.Data, Buffer.from('two'))
parser.feed(Buffer.concat([frame1, frame2]))
expect(onFrame).toHaveBeenCalledTimes(2)
expect(onFrame.mock.calls[0][1].toString()).toBe('one')
expect(onFrame.mock.calls[1][1].toString()).toBe('two')
})
it('handles frame split across header boundary', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const payload = Buffer.from('split test')
const frame = encodeFrame(FrameType.Data, payload)
// Split in the middle of the header (3 bytes, then the rest)
parser.feed(frame.subarray(0, 3))
expect(onFrame).not.toHaveBeenCalled()
parser.feed(frame.subarray(3))
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][1].toString()).toBe('split test')
})
it('handles frame split in the middle of payload', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const payload = Buffer.from('payload split')
const frame = encodeFrame(FrameType.Data, payload)
// Split after header + 3 bytes of payload
const splitPoint = FRAME_HEADER_SIZE + 3
parser.feed(frame.subarray(0, splitPoint))
expect(onFrame).not.toHaveBeenCalled()
parser.feed(frame.subarray(splitPoint))
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][1].toString()).toBe('payload split')
})
it('handles byte-by-byte feeding', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const frame = encodeFrame(FrameType.Exit, Buffer.from('{"code":42}'))
for (let i = 0; i < frame.length; i++) {
parser.feed(frame.subarray(i, i + 1))
}
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][0]).toBe(FrameType.Exit)
expect(JSON.parse(onFrame.mock.calls[0][1].toString())).toEqual({ code: 42 })
})
it('handles zero-length payload frame', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const frame = encodeFrame(FrameType.Kill, Buffer.alloc(0))
parser.feed(frame)
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][0]).toBe(FrameType.Kill)
expect(onFrame.mock.calls[0][1].length).toBe(0)
})
it('parses different frame types correctly', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
const frames = [
encodeFrame(FrameType.Data, Buffer.from('data')),
encodeFrame(FrameType.Resize, Buffer.from('resize')),
encodeFrame(FrameType.Signal, Buffer.from('signal'))
]
parser.feed(Buffer.concat(frames))
expect(onFrame).toHaveBeenCalledTimes(3)
expect(onFrame.mock.calls[0][0]).toBe(FrameType.Data)
expect(onFrame.mock.calls[1][0]).toBe(FrameType.Resize)
expect(onFrame.mock.calls[2][0]).toBe(FrameType.Signal)
})
it('resets buffer state', () => {
const onFrame = vi.fn()
const parser = createFrameParser(onFrame)
// Feed partial frame then reset
const frame = encodeFrame(FrameType.Data, Buffer.from('lost'))
parser.feed(frame.subarray(0, 3))
parser.reset()
// Feed a new complete frame
const frame2 = encodeFrame(FrameType.Data, Buffer.from('fresh'))
parser.feed(frame2)
expect(onFrame).toHaveBeenCalledOnce()
expect(onFrame.mock.calls[0][1].toString()).toBe('fresh')
})
})
-56
View File
@@ -1,56 +0,0 @@
export { FrameType } from './types'
import type { FrameType } from './types'
import { FRAME_HEADER_SIZE, FRAME_MAX_PAYLOAD } from './types'
export { FRAME_HEADER_SIZE }
export function encodeFrame(type: FrameType, payload: Buffer): Buffer {
if (payload.length > FRAME_MAX_PAYLOAD) {
throw new Error(`Frame payload ${payload.length} exceeds max ${FRAME_MAX_PAYLOAD}`)
}
const frame = Buffer.allocUnsafe(FRAME_HEADER_SIZE + payload.length)
frame[0] = type
frame.writeUInt32BE(payload.length, 1)
payload.copy(frame, FRAME_HEADER_SIZE)
return frame
}
export type FrameParser = {
feed(chunk: Buffer): void
reset(): void
}
export function createFrameParser(
onFrame: (type: FrameType, payload: Buffer) => void
): FrameParser {
let buffer: Buffer = Buffer.alloc(0)
function parse(): void {
while (buffer.length >= FRAME_HEADER_SIZE) {
const payloadLength = buffer.readUInt32BE(1)
const totalLength = FRAME_HEADER_SIZE + payloadLength
if (buffer.length < totalLength) {
break
}
const type = buffer[0] as FrameType
const payload = buffer.subarray(FRAME_HEADER_SIZE, totalLength)
buffer = buffer.subarray(totalLength)
onFrame(type, payload)
}
}
return {
feed(chunk: Buffer): void {
buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk])
parse()
},
reset(): void {
buffer = Buffer.alloc(0)
}
}
}
-17
View File
@@ -383,23 +383,6 @@ export type DaemonSessionInfo = SessionInfo & {
// existing importers keep one types entry point.
export * from './daemon-stream-events'
// ─── Binary Frame Protocol (Daemon ↔ PTY Subprocess) ────────────────
//
// 5-byte header: [type:1][length:4 big-endian]
// Followed by `length` bytes of payload.
export const enum FrameType {
Data = 0x01,
Resize = 0x02,
Exit = 0x03,
Error = 0x04,
Kill = 0x05,
Signal = 0x06
}
export const FRAME_HEADER_SIZE = 5
export const FRAME_MAX_PAYLOAD = 1024 * 1024 // 1MB
// ─── Notify prefix ──────────────────────────────────────────────────
// Requests with IDs starting with this prefix are fire-and-forget:
// the daemon processes them but does not send a response.