fix(client): accept larger SSE events (#36442) · argszero/opencode@66b9cc7 · GitHub
Skip to content

Commit 66b9cc7

Browse files
authored
fix(client): accept larger SSE events (anomalyco#36442)
1 parent e3f7637 commit 66b9cc7

4 files changed

Lines changed: 48 additions & 4 deletions

File tree

packages/client/src/promise/generated/client-error.ts

Lines changed: 6 additions & 1 deletion

packages/client/src/promise/generated/client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ interface RequestDescriptor {
213213
readonly binary?: true
214214
}
215215

216+
const maxSseEventBytes = 16 * 1024 * 1024
217+
216218
export function make(options: ClientOptions) {
217219
const fetch = options.fetch ?? globalThis.fetch
218220

@@ -289,7 +291,7 @@ export function make(options: ClientOptions) {
289291
throw new ClientError("Transport", { cause })
290292
}
291293
buffer += decoder.decode(next.value, { stream: !next.done })
292-
if (buffer.length > 1_048_576) throw new ClientError("MalformedResponse")
294+
if (buffer.length > maxSseEventBytes) throw new ClientError("SseEventTooLarge")
293295
const trailingCarriageReturn = !next.done && buffer.endsWith("\r")
294296
if (trailingCarriageReturn) buffer = buffer.slice(0, -1)
295297
buffer = buffer.replaceAll("\r\n", "\n").replaceAll("\r", "\n")

packages/client/test/promise.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,43 @@ test("event.subscribe terminates on malformed Promise SSE data", async () => {
284284
})
285285
})
286286

287+
test("event.subscribe accepts a fragmented SSE event below the size limit", async () => {
288+
const event = { id: "evt_large", type: "test.large", data: { output: "x".repeat(12 * 1024 * 1024) } }
289+
const encoded = new TextEncoder().encode(`data: ${JSON.stringify(event)}\n\n`)
290+
const client = OpenCode.make({
291+
baseUrl: "http://localhost:3000",
292+
fetch: async () =>
293+
new Response(
294+
new ReadableStream({
295+
start(controller) {
296+
for (let offset = 0; offset < encoded.length; offset += 64 * 1024) {
297+
controller.enqueue(encoded.slice(offset, offset + 64 * 1024))
298+
}
299+
controller.close()
300+
},
301+
}),
302+
{ headers: { "content-type": "text/event-stream" } },
303+
),
304+
})
305+
306+
await expect(client.event.subscribe()[Symbol.asyncIterator]().next()).resolves.toEqual({ done: false, value: event })
307+
})
308+
309+
test("event.subscribe rejects an SSE event above the size limit", async () => {
310+
const client = OpenCode.make({
311+
baseUrl: "http://localhost:3000",
312+
fetch: async () =>
313+
new Response(`data: ${JSON.stringify({ output: "x".repeat(16 * 1024 * 1024) })}`, {
314+
headers: { "content-type": "text/event-stream" },
315+
}),
316+
})
317+
318+
await expect(client.event.subscribe()[Symbol.asyncIterator]().next()).rejects.toMatchObject({
319+
name: "ClientError",
320+
reason: "SseEventTooLarge",
321+
})
322+
})
323+
287324
test("session methods use the public HTTP contract", async () => {
288325
const requests: Array<{ url: string; init?: RequestInit }> = []
289326
const client = OpenCode.make({

packages/httpapi-codegen/src/index.ts

Lines changed: 2 additions & 2 deletions

0 commit comments

Comments
 (0)