| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- import assert from 'assert';
- import { _iterSSEMessages } from '@opencode-ai/sdk/core/streaming';
- import { ReadableStreamFrom } from '@opencode-ai/sdk/internal/shims';
- describe('streaming decoding', () => {
- test('basic', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: completion\n');
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('data without event', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toBeNull();
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('event without data', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(event.value.data).toEqual('');
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('multiple events', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('\n');
- yield Buffer.from('event: ping\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(event.value.data).toEqual('');
- event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(event.value.data).toEqual('');
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('multiple events with data', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {"bar":false}\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
- event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ bar: false });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('multiple data lines with empty line', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {\n');
- yield Buffer.from('data: "foo":\n');
- yield Buffer.from('data: \n');
- yield Buffer.from('data:\n');
- yield Buffer.from('data: true}\n');
- yield Buffer.from('\n\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
- expect(event.value.data).toEqual('{\n"foo":\n\n\ntrue}');
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('data json escaped double new line', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {"foo": "my long\\n\\ncontent"}');
- yield Buffer.from('\n\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ foo: 'my long\n\ncontent' });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('special new line characters', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('data: {"content": "culpa "}\n');
- yield Buffer.from('\n');
- yield Buffer.from('data: {"content": "');
- yield Buffer.from([0xe2, 0x80, 0xa8]);
- yield Buffer.from('"}\n');
- yield Buffer.from('\n');
- yield Buffer.from('data: {"content": "foo"}\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: 'culpa ' });
- event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: Buffer.from([0xe2, 0x80, 0xa8]).toString() });
- event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: 'foo' });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- test('multi-byte characters across chunks', async () => {
- async function* body(): AsyncGenerator<Buffer> {
- yield Buffer.from('event: completion\n');
- yield Buffer.from('data: {"content": "');
- // bytes taken from the string 'известни' and arbitrarily split
- // so that some multi-byte characters span multiple chunks
- yield Buffer.from([0xd0]);
- yield Buffer.from([0xb8, 0xd0, 0xb7, 0xd0]);
- yield Buffer.from([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8]);
- yield Buffer.from('"}\n');
- yield Buffer.from('\n');
- }
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('completion');
- expect(JSON.parse(event.value.data)).toEqual({ content: 'известни' });
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
- });
|