diff --git a/src/async/serializeAsync.test.ts b/src/async/serializeAsync.test.ts index bd075bfe..35d97cc4 100644 --- a/src/async/serializeAsync.test.ts +++ b/src/async/serializeAsync.test.ts @@ -117,7 +117,7 @@ test("serialize async iterable", async () => { expect(head).toMatchInlineSnapshot(` { "json": [ - "AsyncIterator", + "AsyncIterable", 0, "__tson", ], @@ -184,7 +184,7 @@ test("stringify async iterable + promise", async () => { expect(buffer).toMatchInlineSnapshot(` [ "[", - " {\\"json\\":{\\"foo\\":\\"bar\\",\\"iterable\\":[\\"AsyncIterator\\",0,\\"__tson\\"],\\"promise\\":[\\"Promise\\",1,\\"__tson\\"]},\\"nonce\\":\\"__tson\\"}", + " {\\"json\\":{\\"foo\\":\\"bar\\",\\"iterable\\":[\\"AsyncIterable\\",0,\\"__tson\\"],\\"promise\\":[\\"Promise\\",1,\\"__tson\\"]},\\"nonce\\":\\"__tson\\"}", " ,", " [", " [1,[0,42]]", @@ -201,7 +201,7 @@ test("stringify async iterable + promise", async () => { "json": { "foo": "bar", "iterable": [ - "AsyncIterator", + "AsyncIterable", 0, "__tson", ], @@ -215,6 +215,6 @@ test("stringify async iterable + promise", async () => { } `); - expect(head.json.iterable[0]).toBe("AsyncIterator"); + expect(head.json.iterable[0]).toBe("AsyncIterable"); expect(head.json.promise[0]).toBe("Promise"); }); diff --git a/src/handlers/index.ts b/src/handlers/index.ts index 48b6bb4a..49c84855 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -11,3 +11,4 @@ export * from "./tsonSymbol.js"; // Async types export * from "./tsonPromise.js"; +export * from "./tsonAsyncIterable.js"; diff --git a/src/handlers/tsonAsyncIterable.ts b/src/handlers/tsonAsyncIterable.ts new file mode 100644 index 00000000..625a2844 --- /dev/null +++ b/src/handlers/tsonAsyncIterable.ts @@ -0,0 +1,62 @@ +import { TsonAsyncType } from "../async/asyncTypes.js"; +import { TsonPromiseRejectionError } from "../errors.js"; + +const ITERATOR_VALUE = 0; +const ITERATOR_ERROR = 1; +const ITERATOR_DONE = 2; +type SerializedIteratorResult = + | [typeof ITERATOR_DONE] + | [typeof ITERATOR_ERROR, unknown] + | [typeof ITERATOR_VALUE, unknown]; +function isAsyncIterator(value: unknown): value is AsyncIterable { + return ( + !!value && + typeof value === "object" && + typeof (value as any)[Symbol.asyncIterator] === "function" + ); +} + +export const tsonAsyncIterator: TsonAsyncType< + AsyncIterable, + SerializedIteratorResult +> = { + async: true, + deserialize: (opts) => { + return (async function* generator() { + try { + for await (const value of opts.stream) { + switch (value[0]) { + case ITERATOR_DONE: { + return; + } + + case ITERATOR_ERROR: { + throw TsonPromiseRejectionError.from(value[1]); + } + + case ITERATOR_VALUE: { + yield value[1]; + break; + } + } + } + } finally { + // `onDone` is a hack and shouldn't be needed + opts.onDone(); + } + })(); + }, + key: "AsyncIterable", + serializeIterator: async function* serialize(opts) { + try { + for await (const value of opts.value) { + yield [ITERATOR_VALUE, value]; + } + + yield [ITERATOR_DONE]; + } catch (err) { + yield [ITERATOR_ERROR, err]; + } + }, + test: isAsyncIterator, +}; diff --git a/src/handlers/tsonPromise.ts b/src/handlers/tsonPromise.ts index 020261f4..da4aed4d 100644 --- a/src/handlers/tsonPromise.ts +++ b/src/handlers/tsonPromise.ts @@ -33,7 +33,7 @@ export const tsonPromise: TsonAsyncType = { const [status, result] = value.value; status === PROMISE_RESOLVED - ? resolve(result as any) + ? resolve(result) : reject(TsonPromiseRejectionError.from(result)); } @@ -56,65 +56,3 @@ export const tsonPromise: TsonAsyncType = { }, test: isPromise, }; - -const ITERATOR_VALUE = 0; -const ITERATOR_ERROR = 1; -const ITERATOR_DONE = 2; - -type SerializedIteratorResult = - | [typeof ITERATOR_DONE] - | [typeof ITERATOR_ERROR, unknown] - | [typeof ITERATOR_VALUE, unknown]; - -function isAsyncIterator(value: unknown): value is AsyncIterable { - return ( - !!value && - typeof value === "object" && - typeof (value as any)[Symbol.asyncIterator] === "function" - ); -} - -export const tsonAsyncIterator: TsonAsyncType< - AsyncIterable, - SerializedIteratorResult -> = { - async: true, - deserialize: (opts) => { - return (async function* generator() { - try { - for await (const value of opts.stream) { - switch (value[0]) { - case ITERATOR_DONE: { - return; - } - - case ITERATOR_ERROR: { - throw TsonPromiseRejectionError.from(value[1]); - } - - case ITERATOR_VALUE: { - yield value[1]; - break; - } - } - } - } finally { - // `onDone` is a hack and shouldn't be needed - opts.onDone(); - } - })(); - }, - key: "AsyncIterator", - serializeIterator: async function* serialize(opts) { - try { - for await (const value of opts.value) { - yield [ITERATOR_VALUE, value]; - } - - yield [ITERATOR_DONE]; - } catch (err) { - yield [ITERATOR_ERROR, err]; - } - }, - test: isAsyncIterator, -};