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

Commit

Permalink
add example project
Browse files Browse the repository at this point in the history
  • Loading branch information
KATT committed Oct 6, 2023
1 parent cc4379e commit 61ebf63
Show file tree
Hide file tree
Showing 9 changed files with 698 additions and 124 deletions.
28 changes: 28 additions & 0 deletions examples/async/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@examples/minimal",
"version": "10.38.5",
"private": true,
"module": "module",
"workspaces": [
"client",
"server"
],
"scripts": {
"build": "tsc",
"dev:server": "tsx watch src/server",
"dev:client": "wait-port 3000 && tsx watch src/client",
"dev": "run-p dev:* --print-label",
"lint": "eslint --ext \".js,.ts,.tsx\" --report-unused-disable-directives */*.ts",
"test-dev": "start-server-and-test 'tsx src/server' 3000 'tsx src/client'"
},
"devDependencies": {
"@types/node": "^18.16.16",
"eslint": "^8.40.0",
"npm-run-all": "^4.1.5",
"start-server-and-test": "^1.12.0",
"tsx": "^3.12.7",
"tupleson": "latest",
"typescript": "^5.1.3",
"wait-port": "^1.0.1"
}
}
46 changes: 46 additions & 0 deletions examples/async/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { ResponseShape } from "./server";

Check failure on line 1 in examples/async/src/client.ts

View workflow job for this annotation

GitHub Actions / lint

"./server" is not found

import { mapIterable, readableStreamToAsyncIterable } from "./iteratorUtils";

Check failure on line 3 in examples/async/src/client.ts

View workflow job for this annotation

GitHub Actions / lint

"./iteratorUtils" is not found
import { tsonAsync } from "./shared";

Check failure on line 4 in examples/async/src/client.ts

View workflow job for this annotation

GitHub Actions / lint

"./shared" is not found

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

if (!response.body) {
throw new Error("Response body is empty");
}

const textDecoder = new TextDecoder();

// convert the response body to an async iterable
const stringIterator = mapIterable(
readableStreamToAsyncIterable(response.body),
(v) => textDecoder.decode(v),
);

const parsedRaw = await tsonAsync.parse(stringIterator);

Check failure on line 23 in examples/async/src/client.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an `any` value

Check failure on line 23 in examples/async/src/client.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe call of an `any` typed value
const parsed = parsedRaw as ResponseShape;

const printBigInts = async () => {
for await (const value of parsed.bigints) {
console.log(`Received bigint:`, value);
}
};

const printNumbers = async () => {
for await (const value of parsed.numbers) {
console.log(`Received number:`, value);
}
};

await Promise.all([printBigInts(), printNumbers()]);

console.log("Output ended");
}

main().catch((err) => {
console.error(err);
throw err;
});
33 changes: 33 additions & 0 deletions examples/async/src/iteratorUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export async function* readableStreamToAsyncIterable<T>(
stream: ReadableStream<T>,
): AsyncIterable<T> {
// Get a lock on the stream
const reader = stream.getReader();

try {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
while (true) {
// Read from the stream
const result = await reader.read();

// Exit if we're done
if (result.done) {
return;
}

// Else yield the chunk
yield result.value;
}
} finally {
reader.releaseLock();
}
}

export async function* mapIterable<T, TValue>(
iterable: AsyncIterable<T>,
fn: (v: T) => TValue,
): AsyncIterable<TValue> {
for await (const value of iterable) {
yield fn(value);
}
}
62 changes: 62 additions & 0 deletions examples/async/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import http from "node:http";

import { tsonAsync } from "./shared";

Check failure on line 3 in examples/async/src/server.ts

View workflow job for this annotation

GitHub Actions / lint

"./shared" is not found

const randomNumber = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};

export function getResponseShape() {
async function* bigintGenerator() {
const iterate = new Array(10).fill(0).map((_, i) => BigInt(i));
for (const number of iterate) {
await new Promise((resolve) => setTimeout(resolve, randomNumber(1, 400)));
yield number;
}
}

async function* numberGenerator() {
const iterate = new Array(10).fill(0).map((_, i) => i);
for (const number of iterate) {
await new Promise((resolve) => setTimeout(resolve, randomNumber(1, 400)));
yield number;
}
}

return {
bigints: bigintGenerator(),
foo: "bar",
numbers: numberGenerator(),
promise: Promise.resolve(42),
rejectedPromise: Promise.reject(new Error("rejected promise")),
};
}

export type ResponseShape = ReturnType<typeof getResponseShape>;
async function handleRequest(
req: http.IncomingMessage,
res: http.ServerResponse,
) {
res.writeHead(200, { "Content-Type": "application/json" });

const obj = getResponseShape();

for await (const chunk of tsonAsync.stringify(obj)) {

Check failure on line 44 in examples/async/src/server.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe call of an `any` typed value
res.write(chunk);
}
}

const server = http.createServer(
(req: http.IncomingMessage, res: http.ServerResponse) => {
handleRequest(req, res).catch((err) => {
console.error(err);
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Internal Server Error\n");
});
},
);

const port = 3000;
server.listen(port);

console.log(`Server running at http://localhost:${port}`);
10 changes: 10 additions & 0 deletions examples/async/src/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {
createTsonAsync,
tsonAsyncIterator,
tsonBigint,
tsonPromise,
} from "tupleson";

Check failure on line 6 in examples/async/src/shared.ts

View workflow job for this annotation

GitHub Actions / lint

"tupleson" is not found

export const tsonAsync = createTsonAsync({

Check failure on line 8 in examples/async/src/shared.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an `any` value

Check failure on line 8 in examples/async/src/shared.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe call of an `any` typed value
types: [tsonPromise, tsonAsyncIterator, tsonBigint],
});
11 changes: 11 additions & 0 deletions examples/async/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "dist"
},
"include": ["src"]
}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,10 @@
},
"publishConfig": {
"provenance": true
},
"pnpm": {
"overrides": {
"@trpc/server": "link:./"
}
}
}
Loading

0 comments on commit 61ebf63

Please sign in to comment.