From 6e1f7e722b2814283e2291de49bbaef901894ef9 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 16:44:38 +0200 Subject: [PATCH 01/13] feat: add abort signal --- src/async/asyncTypes.ts | 4 -- src/async/deserializeAsync.test.ts | 59 ++++++++++++++++---------- src/async/deserializeAsync.ts | 45 +++++++++++++++++--- src/async/handlers/tsonPromise.test.ts | 2 +- src/errors.ts | 7 +++ src/internals/createDeferred.ts | 14 ++++++ src/internals/testUtils.ts | 15 ------- 7 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 src/internals/createDeferred.ts diff --git a/src/async/asyncTypes.ts b/src/async/asyncTypes.ts index 1a3c57d9..5ed6bb2a 100644 --- a/src/async/asyncTypes.ts +++ b/src/async/asyncTypes.ts @@ -62,10 +62,6 @@ export interface TsonAsyncOptions { * @default `${crypto.randomUUID} if available, otherwise a random string generated by Math.random` */ nonce?: () => number | string; - /** - * On stream error - */ - onStreamError?: (err: TsonStreamInterruptedError) => void; /** * The list of types to use diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index 27895524..c18e1feb 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -1,4 +1,5 @@ -import { expect, test, vi, vitest } from "vitest"; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { expect, test, vitest } from "vitest"; import { TsonType, @@ -9,8 +10,8 @@ import { tsonPromise, } from "../index.js"; import { assert } from "../internals/assert.js"; +import { createDeferred } from "../internals/createDeferred.js"; import { - createDeferred, createTestServer, sleep, waitError, @@ -18,6 +19,7 @@ import { } from "../internals/testUtils.js"; import { TsonSerialized } from "../sync/syncTypes.js"; import { TsonAsyncOptions } from "./asyncTypes.js"; +import { TsonParseAsyncOptions } from "./deserializeAsync.js"; import { mapIterable, readableStreamToAsyncIterable } from "./iterableUtils.js"; test("deserialize variable chunk length", async () => { @@ -92,13 +94,15 @@ test("deserialize async iterable", async () => { }); test("stringify async iterable + promise", async () => { - const onErr = vi.fn(); const tson = createTsonAsync({ nonce: () => "__tson", - onStreamError: onErr, types: [tsonAsyncIterator, tsonPromise, tsonBigint], }); + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; + async function* iterable() { await sleep(1); yield 1n; @@ -119,7 +123,7 @@ test("stringify async iterable + promise", async () => { const strIterable = tson.stringify(input); - const output = await tson.parse(strIterable); + const output = await tson.parse(strIterable, parseOptions); expect(output.foo).toEqual("bar"); @@ -348,16 +352,19 @@ test("values missing when stream ends", async () => { } const opts = { - onStreamError: vitest.fn(), types: [tsonPromise, tsonAsyncIterator], } satisfies TsonAsyncOptions; + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; + const parse = createTsonParseAsync(opts); const result = await parse<{ iterable: AsyncIterable; promise: Promise; - }>(generator()); + }>(generator(), parseOptions); { // iterator should error @@ -388,8 +395,8 @@ test("values missing when stream ends", async () => { ); } - expect(opts.onStreamError).toHaveBeenCalledTimes(1); - expect(opts.onStreamError.mock.calls).toMatchInlineSnapshot(` + expect(parseOptions.onStreamError).toHaveBeenCalledTimes(1); + expect(parseOptions.onStreamError.mock.calls).toMatchInlineSnapshot(` [ [ [TsonStreamInterruptedError: Stream interrupted: Stream ended unexpectedly], @@ -420,17 +427,19 @@ test("async: missing values of promise", async () => { // yield "]]\n"; // <-- stream and values ended symbol } - const onErrorSpy = vitest.fn(); + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; + await createTsonAsync({ - onStreamError: onErrorSpy, types: [tsonPromise], - }).parse(generator()); + }).parse(generator(), parseOptions); await waitFor(() => { - expect(onErrorSpy).toHaveBeenCalledTimes(1); + expect(parseOptions.onStreamError).toHaveBeenCalledTimes(1); }); - expect(onErrorSpy.mock.calls[0][0]).toMatchInlineSnapshot( + expect(parseOptions.onStreamError.mock.calls[0]![0]!).toMatchInlineSnapshot( "[TsonStreamInterruptedError: Stream interrupted: Stream ended unexpectedly]", ); }); @@ -469,16 +478,18 @@ test("1 iterator completed but another never finishes", async () => { } const opts = { - onStreamError: vitest.fn(), types: [tsonPromise, tsonAsyncIterator], } satisfies TsonAsyncOptions; + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; const parse = createTsonParseAsync(opts); const result = await parse<{ iterable1: AsyncIterable; iterable2: AsyncIterable; - }>(generator()); + }>(generator(), parseOptions); { // iterator 1 should complete @@ -517,9 +528,9 @@ test("1 iterator completed but another never finishes", async () => { ); } - expect(opts.onStreamError).toHaveBeenCalledTimes(1); + expect(parseOptions.onStreamError).toHaveBeenCalledTimes(1); - expect(opts.onStreamError.mock.calls).toMatchInlineSnapshot(` + expect(parseOptions.onStreamError.mock.calls).toMatchInlineSnapshot(` [ [ [TsonStreamInterruptedError: Stream interrupted: Stream ended unexpectedly], @@ -556,10 +567,13 @@ test("e2e: simulated server crash", async () => { // ------------- server ------------------- const opts = { - onStreamError: vi.fn(), types: [tsonPromise, tsonAsyncIterator], } satisfies TsonAsyncOptions; + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; + const server = await createTestServer({ handleRequest: async (_req, res) => { const tson = createTsonAsync(opts); @@ -594,7 +608,7 @@ test("e2e: simulated server crash", async () => { (v) => textDecoder.decode(v), ); - const parsed = await tson.parse(stringIterator); + const parsed = await tson.parse(stringIterator, parseOptions); { // check the iterator const results = []; @@ -622,10 +636,9 @@ test("e2e: simulated server crash", async () => { parsed.rejectedPromise, ).rejects.toThrowErrorMatchingInlineSnapshot('"Promise rejected"'); - expect(opts.onStreamError).toHaveBeenCalledTimes(1); + expect(parseOptions.onStreamError).toHaveBeenCalledTimes(1); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const streamError = opts.onStreamError.mock.calls[0]![0]!; + const streamError = parseOptions.onStreamError.mock.calls[0]![0]!; expect(streamError).toMatchInlineSnapshot( "[TsonStreamInterruptedError: Stream interrupted: terminated]", ); diff --git a/src/async/deserializeAsync.ts b/src/async/deserializeAsync.ts index 0f298b4e..e12c30f4 100644 --- a/src/async/deserializeAsync.ts +++ b/src/async/deserializeAsync.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { TsonError } from "../errors.js"; +import { TsonAbortError, TsonError } from "../errors.js"; import { assert } from "../internals/assert.js"; +import { createDeferred } from "../internals/createDeferred.js"; import { isTsonTuple } from "../internals/isTsonTuple.js"; import { mapOrReturn } from "../internals/mapOrReturn.js"; import { @@ -25,8 +26,20 @@ type AnyTsonTransformerSerializeDeserialize = | TsonAsyncType | TsonTransformerSerializeDeserialize; +export interface TsonParseAsyncOptions { + /** + * Abort signal to abort the parsing + */ + abortSignal?: AbortSignal; + /** + * On stream error + */ + onStreamError?: (err: TsonStreamInterruptedError) => void; +} + type TsonParseAsync = ( string: AsyncIterable | TsonAsyncStringifierIterable, + opts?: TsonParseAsyncOptions, ) => Promise; export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { @@ -43,7 +56,10 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { } } - return async (iterable: AsyncIterable) => { + return async ( + iterable: AsyncIterable, + parseOptions: TsonParseAsyncOptions, + ) => { // this is an awful hack to get around making a some sort of pipeline const cache = new Map< TsonAsyncIndex, @@ -51,6 +67,19 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { >(); const iterator = iterable[Symbol.asyncIterator](); + const abortSignalDeferred = createDeferred(); + + function resetAbortSignal() { + parseOptions.abortSignal?.removeEventListener("abort", onAbort); + } + + function onAbort(event: Event) { + abortSignalDeferred.reject(new TsonAbortError(event)); + resetAbortSignal(); + } + + parseOptions.abortSignal?.addEventListener("abort", onAbort); + const walker: WalkerFactory = (nonce) => { const walk: WalkFn = (value) => { if (isTsonTuple(value, nonce)) { @@ -139,7 +168,11 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { do { lines.forEach(readLine); lines.length = 0; - const nextValue = await iterator.next(); + + const nextValue = await Promise.race([ + iterator.next(), + abortSignalDeferred.promise, + ]); if (!nextValue.done) { accumulator += nextValue.value; const parts = accumulator.split("\n"); @@ -205,7 +238,7 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { controller.enqueue(err); } - opts.onStreamError?.(err); + parseOptions.onStreamError?.(err); }); } } @@ -220,8 +253,8 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync { const instance = createTsonParseAsyncInner(opts); - return (async (iterable) => { - const [result] = await instance(iterable); + return (async (iterable, opts) => { + const [result] = await instance(iterable, opts ?? {}); return result; }) as TsonParseAsync; diff --git a/src/async/handlers/tsonPromise.test.ts b/src/async/handlers/tsonPromise.test.ts index af02d7b8..06d18a35 100644 --- a/src/async/handlers/tsonPromise.test.ts +++ b/src/async/handlers/tsonPromise.test.ts @@ -462,7 +462,7 @@ test("does not crash node when it receives a promise rejection", async () => { }; const iterator = stringify(original); - await parse(iterator); + await parse(iterator, {}); await sleep(10); }); diff --git a/src/errors.ts b/src/errors.ts index eda4169e..6d3a4029 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -5,6 +5,13 @@ export class TsonError extends Error { } } +export class TsonAbortError extends TsonError { + constructor(cause: unknown) { + super(`TSON operation aborted`, { cause }); + this.name = "TsonAbortError"; + } +} + export class TsonCircularReferenceError extends TsonError { /** * The circular reference that was found diff --git a/src/internals/createDeferred.ts b/src/internals/createDeferred.ts new file mode 100644 index 00000000..cad0ca48 --- /dev/null +++ b/src/internals/createDeferred.ts @@ -0,0 +1,14 @@ +export function createDeferred() { + type PromiseResolve = (...args: T extends never ? [] : [T]) => void; + type PromiseReject = (reason: unknown) => void; + const deferred = {} as { + promise: Promise; + reject: PromiseReject; + resolve: PromiseResolve; + }; + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve as PromiseResolve; + deferred.reject = reject; + }); + return deferred; +} diff --git a/src/internals/testUtils.ts b/src/internals/testUtils.ts index 0183ff5d..83a8ea7a 100644 --- a/src/internals/testUtils.ts +++ b/src/internals/testUtils.ts @@ -72,21 +72,6 @@ export async function createTestServer(opts: { }; } -export function createDeferred() { - type PromiseResolve = (value: T) => void; - type PromiseReject = (reason: unknown) => void; - const deferred = {} as { - promise: Promise; - reject: PromiseReject; - resolve: PromiseResolve; - }; - deferred.promise = new Promise((resolve, reject) => { - deferred.resolve = resolve; - deferred.reject = reject; - }); - return deferred; -} - export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const createPromise = (result: () => T, wait = 1) => { From 6a9c77d57b28c6038c81c923d27a01eb6429447e Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:30:31 +0200 Subject: [PATCH 02/13] aborted request test --- src/async/deserializeAsync.test.ts | 126 +++++++++++++++++++++++++++++ src/internals/createDeferred.ts | 2 +- 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index c18e1feb..d2f57d0e 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -645,3 +645,129 @@ test("e2e: simulated server crash", async () => { expect(streamError.cause).toMatchInlineSnapshot("[TypeError: terminated]"); }); + +test("e2e: aborted request", async () => { + // ------------- server ------------------- + const serverSentChunks: string[] = []; + const iteratorChunks: number[] = []; + function createMockObj() { + async function* generator() { + for (let i = 0; i < 10; i++) { + yield i; + iteratorChunks.push(i); + await sleep(1); + } + } + + return { + iterable: generator(), + }; + } + + type MockObj = ReturnType; + const opts = { + nonce: () => "__tson", + types: [tsonPromise, tsonAsyncIterator], + } satisfies TsonAsyncOptions; + + const parseOptions = { + onStreamError: vitest.fn(), + } satisfies TsonParseAsyncOptions; + + const server = await createTestServer({ + handleRequest: async (_req, res) => { + const tson = createTsonAsync(opts); + + const obj = createMockObj(); + const strIterarable = tson.stringify(obj, 4); + + for await (const value of strIterarable) { + serverSentChunks.push(value.trimEnd()); + res.write(value); + } + + res.end(); + }, + }); + + // ------------- client ------------------- + const abortController = new AbortController(); + + const tson = createTsonAsync(opts); + + // do a streamed fetch request + const response = await fetch(server.url, { + signal: abortController.signal, + }); + + assert(response.body); + + const textDecoder = new TextDecoder(); + const stringIterator = mapIterable( + readableStreamToAsyncIterable(response.body), + (v) => textDecoder.decode(v), + ); + + const parsed = await tson.parse(stringIterator, parseOptions); + { + // check the iterator + const results = []; + let iteratorError: Error | null = null; + try { + for await (const value of parsed.iterable) { + results.push(value); + + if (value === 5) { + // abort the request after when receiving 5 + abortController.abort(); + } + } + } catch (err) { + iteratorError = err as Error; + } finally { + server.close(); + } + + expect(results).toEqual([0, 1, 2, 3, 4, 5]); + expect(iteratorError).toMatchInlineSnapshot( + "[TsonStreamInterruptedError: Stream interrupted: The operation was aborted.]", + ); + } + + expect(parseOptions.onStreamError).toHaveBeenCalledTimes(1); + + const streamError = parseOptions.onStreamError.mock.calls[0]![0]!; + expect(streamError).toMatchInlineSnapshot( + "[TsonStreamInterruptedError: Stream interrupted: The operation was aborted.]", + ); + + expect(streamError.cause).toMatchInlineSnapshot( + "[AbortError: The operation was aborted.]", + ); + + expect(serverSentChunks).toMatchInlineSnapshot(` + [ + "[", + " {\\"json\\":{\\"iterable\\":[\\"AsyncIterable\\",0,\\"__tson\\"]},\\"nonce\\":\\"__tson\\"}", + " ,", + " [", + " [0,[0,0]]", + " ,[0,[0,1]]", + " ,[0,[0,2]]", + " ,[0,[0,3]]", + " ,[0,[0,4]]", + " ,[0,[0,5]]", + ] + `); + expect(iteratorChunks.length).toBeLessThan(10); + expect(iteratorChunks).toMatchInlineSnapshot(` + [ + 0, + 1, + 2, + 3, + 4, + 5, + ] + `); +}); diff --git a/src/internals/createDeferred.ts b/src/internals/createDeferred.ts index cad0ca48..19a3ab2d 100644 --- a/src/internals/createDeferred.ts +++ b/src/internals/createDeferred.ts @@ -1,5 +1,5 @@ export function createDeferred() { - type PromiseResolve = (...args: T extends never ? [] : [T]) => void; + type PromiseResolve = (value: T) => void; type PromiseReject = (reason: unknown) => void; const deferred = {} as { promise: Promise; From ad6080a07c8708a236923f60a830366fcaeedc99 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:31:57 +0200 Subject: [PATCH 03/13] cool --- src/async/deserializeAsync.test.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index d2f57d0e..9b0c6001 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -646,7 +646,7 @@ test("e2e: simulated server crash", async () => { expect(streamError.cause).toMatchInlineSnapshot("[TypeError: terminated]"); }); -test("e2e: aborted request", async () => { +test("e2e: client aborted request", async () => { // ------------- server ------------------- const serverSentChunks: string[] = []; const iteratorChunks: number[] = []; @@ -745,20 +745,6 @@ test("e2e: aborted request", async () => { "[AbortError: The operation was aborted.]", ); - expect(serverSentChunks).toMatchInlineSnapshot(` - [ - "[", - " {\\"json\\":{\\"iterable\\":[\\"AsyncIterable\\",0,\\"__tson\\"]},\\"nonce\\":\\"__tson\\"}", - " ,", - " [", - " [0,[0,0]]", - " ,[0,[0,1]]", - " ,[0,[0,2]]", - " ,[0,[0,3]]", - " ,[0,[0,4]]", - " ,[0,[0,5]]", - ] - `); expect(iteratorChunks.length).toBeLessThan(10); expect(iteratorChunks).toMatchInlineSnapshot(` [ @@ -770,4 +756,5 @@ test("e2e: aborted request", async () => { 5, ] `); + expect(serverSentChunks).toMatchInlineSnapshot(); }); From 79ffaefe6453de87380ade6e2840d35b01e24918 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:32:46 +0200 Subject: [PATCH 04/13] cool --- src/async/deserializeAsync.ts | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/async/deserializeAsync.ts b/src/async/deserializeAsync.ts index e12c30f4..251dee80 100644 --- a/src/async/deserializeAsync.ts +++ b/src/async/deserializeAsync.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { TsonAbortError, TsonError } from "../errors.js"; +import { TsonError } from "../errors.js"; import { assert } from "../internals/assert.js"; -import { createDeferred } from "../internals/createDeferred.js"; import { isTsonTuple } from "../internals/isTsonTuple.js"; import { mapOrReturn } from "../internals/mapOrReturn.js"; import { @@ -27,10 +26,6 @@ type AnyTsonTransformerSerializeDeserialize = | TsonTransformerSerializeDeserialize; export interface TsonParseAsyncOptions { - /** - * Abort signal to abort the parsing - */ - abortSignal?: AbortSignal; /** * On stream error */ @@ -67,19 +62,6 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { >(); const iterator = iterable[Symbol.asyncIterator](); - const abortSignalDeferred = createDeferred(); - - function resetAbortSignal() { - parseOptions.abortSignal?.removeEventListener("abort", onAbort); - } - - function onAbort(event: Event) { - abortSignalDeferred.reject(new TsonAbortError(event)); - resetAbortSignal(); - } - - parseOptions.abortSignal?.addEventListener("abort", onAbort); - const walker: WalkerFactory = (nonce) => { const walk: WalkFn = (value) => { if (isTsonTuple(value, nonce)) { @@ -169,10 +151,7 @@ export function createTsonParseAsyncInner(opts: TsonAsyncOptions) { lines.forEach(readLine); lines.length = 0; - const nextValue = await Promise.race([ - iterator.next(), - abortSignalDeferred.promise, - ]); + const nextValue = await iterator.next(); if (!nextValue.done) { accumulator += nextValue.value; const parts = accumulator.split("\n"); From 1b9ae08b0484420b0d5b89af9ed9c2b194e9dd9a Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:34:16 +0200 Subject: [PATCH 05/13] tweak --- src/async/deserializeAsync.test.ts | 2 +- src/internals/createDeferred.ts | 14 -------------- src/internals/testUtils.ts | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 15 deletions(-) delete mode 100644 src/internals/createDeferred.ts diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index 9b0c6001..208f5cb8 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -10,7 +10,7 @@ import { tsonPromise, } from "../index.js"; import { assert } from "../internals/assert.js"; -import { createDeferred } from "../internals/createDeferred.js"; +import { createDeferred } from "../internals/testUtils.js"; import { createTestServer, sleep, diff --git a/src/internals/createDeferred.ts b/src/internals/createDeferred.ts deleted file mode 100644 index 19a3ab2d..00000000 --- a/src/internals/createDeferred.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function createDeferred() { - type PromiseResolve = (value: T) => void; - type PromiseReject = (reason: unknown) => void; - const deferred = {} as { - promise: Promise; - reject: PromiseReject; - resolve: PromiseResolve; - }; - deferred.promise = new Promise((resolve, reject) => { - deferred.resolve = resolve as PromiseResolve; - deferred.reject = reject; - }); - return deferred; -} diff --git a/src/internals/testUtils.ts b/src/internals/testUtils.ts index 83a8ea7a..3140eaa5 100644 --- a/src/internals/testUtils.ts +++ b/src/internals/testUtils.ts @@ -72,6 +72,21 @@ export async function createTestServer(opts: { }; } +export function createDeferred() { + type PromiseResolve = (value: T) => void; + type PromiseReject = (reason: unknown) => void; + const deferred = {} as { + promise: Promise; + reject: PromiseReject; + resolve: PromiseResolve; + }; + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve as PromiseResolve; + deferred.reject = reject; + }); + return deferred; +} + export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const createPromise = (result: () => T, wait = 1) => { From 31b37de919efa34d139661dde8aa11ba82dbefaa Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:34:22 +0200 Subject: [PATCH 06/13] tweak --- src/internals/testUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internals/testUtils.ts b/src/internals/testUtils.ts index 3140eaa5..aece4784 100644 --- a/src/internals/testUtils.ts +++ b/src/internals/testUtils.ts @@ -72,7 +72,7 @@ export async function createTestServer(opts: { }; } -export function createDeferred() { +export function createDeferred() { type PromiseResolve = (value: T) => void; type PromiseReject = (reason: unknown) => void; const deferred = {} as { From a6993811e7517acd1f1411e7d0eb88b909f3520f Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:34:41 +0200 Subject: [PATCH 07/13] rm --- src/errors.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/errors.ts b/src/errors.ts index 6d3a4029..eda4169e 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -5,13 +5,6 @@ export class TsonError extends Error { } } -export class TsonAbortError extends TsonError { - constructor(cause: unknown) { - super(`TSON operation aborted`, { cause }); - this.name = "TsonAbortError"; - } -} - export class TsonCircularReferenceError extends TsonError { /** * The circular reference that was found From d42dfe6f41fbeadad685ac61e158c3798a5ef6ed Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:35:57 +0200 Subject: [PATCH 08/13] clean --- src/internals/testUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internals/testUtils.ts b/src/internals/testUtils.ts index aece4784..0183ff5d 100644 --- a/src/internals/testUtils.ts +++ b/src/internals/testUtils.ts @@ -81,7 +81,7 @@ export function createDeferred() { resolve: PromiseResolve; }; deferred.promise = new Promise((resolve, reject) => { - deferred.resolve = resolve as PromiseResolve; + deferred.resolve = resolve; deferred.reject = reject; }); return deferred; From 1f0fb6d808bc558670b8af19ce216f8c7a6c06de Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:36:04 +0200 Subject: [PATCH 09/13] space --- src/internals/testUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internals/testUtils.ts b/src/internals/testUtils.ts index 0183ff5d..2fa6c2ea 100644 --- a/src/internals/testUtils.ts +++ b/src/internals/testUtils.ts @@ -89,6 +89,7 @@ export function createDeferred() { export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + export const createPromise = (result: () => T, wait = 1) => { return new Promise((resolve, reject) => { setTimeout(() => { From a2b489afce854d0a6a43a0e103ad8310f2cbad18 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:37:23 +0200 Subject: [PATCH 10/13] org imports --- src/async/deserializeAsync.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index 208f5cb8..4c5cdb3c 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -10,8 +10,8 @@ import { tsonPromise, } from "../index.js"; import { assert } from "../internals/assert.js"; -import { createDeferred } from "../internals/testUtils.js"; import { + createDeferred, createTestServer, sleep, waitError, From c21f5afb75dd5cb7dc6844996c92b92f671174ce Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:38:38 +0200 Subject: [PATCH 11/13] tweak, import from root --- src/async/deserializeAsync.test.ts | 4 ++-- src/index.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index 4c5cdb3c..c93f3a32 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -2,6 +2,8 @@ import { expect, test, vitest } from "vitest"; import { + TsonAsyncOptions, + TsonParseAsyncOptions, TsonType, createTsonAsync, createTsonParseAsync, @@ -18,8 +20,6 @@ import { waitFor, } from "../internals/testUtils.js"; import { TsonSerialized } from "../sync/syncTypes.js"; -import { TsonAsyncOptions } from "./asyncTypes.js"; -import { TsonParseAsyncOptions } from "./deserializeAsync.js"; import { mapIterable, readableStreamToAsyncIterable } from "./iterableUtils.js"; test("deserialize variable chunk length", async () => { diff --git a/src/index.ts b/src/index.ts index 9da7b167..9f36bd70 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,10 @@ export * from "./sync/handlers/tsonSymbol.js"; // --- async -- export type { TsonAsyncOptions } from "./async/asyncTypes.js"; export { createTsonAsync } from "./async/createTsonAsync.js"; -export { createTsonParseAsync } from "./async/deserializeAsync.js"; +export { + TsonParseAsyncOptions, + createTsonParseAsync, +} from "./async/deserializeAsync.js"; export { createAsyncTsonSerialize, createTsonStringifyAsync, From 6e1b57ef027ccfac90c7d36f4d74c395098f8b26 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:39:38 +0200 Subject: [PATCH 12/13] errors --- src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 9f36bd70..42cc0781 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ +export * from "./errors.js"; + // --- sync -- export { createTson } from "./sync/createTson.js"; export { createTsonDeserialize, createTsonParser } from "./sync/deserialize.js"; @@ -21,13 +23,14 @@ export * from "./sync/handlers/tsonSymbol.js"; export type { TsonAsyncOptions } from "./async/asyncTypes.js"; export { createTsonAsync } from "./async/createTsonAsync.js"; export { - TsonParseAsyncOptions, + type TsonParseAsyncOptions, createTsonParseAsync, } from "./async/deserializeAsync.js"; export { createAsyncTsonSerialize, createTsonStringifyAsync, } from "./async/serializeAsync.js"; +export * from "./async/asyncErrors.js"; // type handlers export * from "./async/handlers/tsonPromise.js"; From e27e36ddeb64a32fd5e6333b3bd6f51e80676e52 Mon Sep 17 00:00:00 2001 From: KATT Date: Sun, 8 Oct 2023 17:40:30 +0200 Subject: [PATCH 13/13] sleep longer --- src/async/deserializeAsync.test.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/async/deserializeAsync.test.ts b/src/async/deserializeAsync.test.ts index c93f3a32..939223b7 100644 --- a/src/async/deserializeAsync.test.ts +++ b/src/async/deserializeAsync.test.ts @@ -655,7 +655,7 @@ test("e2e: client aborted request", async () => { for (let i = 0; i < 10; i++) { yield i; iteratorChunks.push(i); - await sleep(1); + await sleep(5); } } @@ -756,5 +756,18 @@ test("e2e: client aborted request", async () => { 5, ] `); - expect(serverSentChunks).toMatchInlineSnapshot(); + expect(serverSentChunks).toMatchInlineSnapshot(` + [ + "[", + " {\\"json\\":{\\"iterable\\":[\\"AsyncIterable\\",0,\\"__tson\\"]},\\"nonce\\":\\"__tson\\"}", + " ,", + " [", + " [0,[0,0]]", + " ,[0,[0,1]]", + " ,[0,[0,2]]", + " ,[0,[0,3]]", + " ,[0,[0,4]]", + " ,[0,[0,5]]", + ] + `); });