Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

chore: simplify serialization a bit #22

Merged
merged 2 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 25 additions & 24 deletions src/async/deserializeAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync {

deferreds.set(idx, deferred);

if (typeof window === "undefined") {
deferred.promise.catch(() => {
// prevent unhandled promise rejection crashes 🤷‍♂️
});
}

return deferred.promise;
},
);
Expand All @@ -94,27 +100,31 @@ export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync {
) {
function readLine(str: string) {
str = str.trimStart();
if (!str) {
return;
}

if (str.startsWith(",")) {
// ignore leading comma
str = str.slice(1);
}

if (!str.startsWith("[")) {
if (str.length < 2) {
// minimum length is 2: '[]'
return;
}

// console.log("got something that looks like a value", str);

const [index, status, result] = JSON.parse(str) as TsonAsyncValueTuple;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const deferred = deferreds.get(index)!;

const deferred = deferreds.get(index);
// console.log("got value", index, status, result, deferred);
const walkedResult = walk(result);

if (!deferred) {
throw new TsonError(
`No deferred found for index ${index} (status: ${status})`,
);
}

status === PROMISE_RESOLVED
? deferred.resolve(walkedResult)
: deferred.reject(
Expand Down Expand Up @@ -156,7 +166,7 @@ export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync {
lines.push(...(lastResult.value as string).split("\n").filter(Boolean));

// console.log("got line", lines);
} while (lines.length < 4);
} while (lines.length < 2);

const [
/**
Expand All @@ -166,31 +176,22 @@ export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync {
/**
* Second line is the shape of the JSON
*/
secondLine,
/**
* Third line is a `,`
*/
_thirdLine,
/**
* Fourth line is the start of the values array
*/
_fourthLine,
/**
* Buffer is the rest of the iterator that came in the chunks while we were waiting for the first 4 lines
*/
headLine,
// .. third line is a `,`
// .. fourth line is the start of the values array
...buffer
] = lines;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const secondValueParsed = JSON.parse(secondLine!) as TsonSerialized<any>;
const head = JSON.parse(headLine!) as TsonSerialized<any>;

const walk = walker(secondValueParsed.nonce);
const walk = walker(head.nonce);

void getStreamedValues(buffer, !!lastResult.done, walk).catch((cause) => {
// Something went wrong while getting the streamed values

const err = new TsonError(
"Stream interrupted: failed to get streamed values",
`Stream interrupted: ${(cause as Error).message}`,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
{ cause },
);
Expand All @@ -203,7 +204,7 @@ export function createTsonParseAsync(opts: TsonAsyncOptions): TsonParseAsync {
deferreds.clear();
});

return walk(secondValueParsed.json);
return walk(head.json);
}

const result = await init().catch((cause: unknown) => {
Expand Down
23 changes: 12 additions & 11 deletions src/handlers/tsonPromise.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ test("stringifier - no promises", async () => {
buffer.push(value.trimEnd());
}

// expect(buffer).toHaveLength(5);
expect(buffer).toMatchInlineSnapshot(`
[
"[",
Expand Down Expand Up @@ -388,6 +387,7 @@ test("stringify and parse promise with a promise over a network connection", asy
}>;
}

// ----- server --------
const server = await new Promise<http.Server>((resolve) => {
const server = http.createServer((_req, res) => {
async function handle() {
Expand All @@ -404,7 +404,7 @@ test("stringify and parse promise with a promise over a network connection", asy
}, 8),
rejectedPromise: createPromise<number>(() => {
throw new Error("foo");
}, 10),
}, 1),
};
}, 3),
};
Expand All @@ -428,6 +428,12 @@ test("stringify and parse promise with a promise over a network connection", asy
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
const port = (server.address() as any).port as number;

// ----- client --------
const tson = createTsonAsync({
nonce: () => "__tson",
types: [tsonPromise],
});

// do a streamed fetch request
const response = await fetch(`http://localhost:${port}`);

Expand All @@ -440,18 +446,17 @@ test("stringify and parse promise with a promise over a network connection", asy
readableStreamToAsyncIterable(response.body),
(v) => textDecoder.decode(v),
);
const tson = createTsonAsync({
nonce: () => "__tson",
types: [tsonPromise],
});

const value = await tson.parse(stringIterator);
const asObj = value as Obj;

const firstPromise = await asObj.promise;

expect(firstPromise).toHaveProperty("anotherPromise");

const secondPromise = await firstPromise.anotherPromise;

expect(secondPromise).toBe(42);

// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const err = await firstPromise.rejectedPromise.catch((err) => err);
assert.instanceOf(err, Error);
Expand All @@ -463,10 +468,6 @@ test("stringify and parse promise with a promise over a network connection", asy
}
`);

const secondPromise = await firstPromise.anotherPromise;

expect(secondPromise).toBe(42);

expect(err).toMatchInlineSnapshot("[TsonError: Promise rejected on server]");

server.close();
Expand Down