Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

trpc-bun-adapter

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

trpc-bun-adapter - npm Package Compare versions

Comparing version 1.0.0-rc.0 to 1.0.0

98

dist/index.d.ts

@@ -1,45 +0,73 @@

import * as bun from 'bun';
import { Server, ServerWebSocket, WebSocketHandler, ServeOptions } from 'bun';
import { AnyRouter, inferRouterContext } from '@trpc/server';
import { HTTPBaseHandlerOptions } from '@trpc/server/http';
import { TRPCClientOutgoingMessage } from '@trpc/server/rpc';
import { BaseHandlerOptions } from '@trpc/server/src/internals/types';
import * as bun from "bun";
import { Server, ServerWebSocket, WebSocketHandler, ServeOptions } from "bun";
import { AnyRouter, inferRouterContext } from "@trpc/server";
import { HTTPBaseHandlerOptions } from "@trpc/server/http";
import { TRPCClientOutgoingMessage } from "@trpc/server/rpc";
import { BaseHandlerOptions } from "@trpc/server/src/internals/types";
type BunHttpHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<TRouter, Request> & {
endpoint?: string;
createContext?: (opts: {
req: Request;
}) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
type BunHttpHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<
TRouter,
Request
> & {
endpoint?: string;
createContext?: (opts: {
req: Request;
}) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
};
declare function createBunHttpHandler<TRouter extends AnyRouter>(opts: BunHttpHandlerOptions<TRouter> & {
emitWsUpgrades?: boolean;
}): (request: Request, server: Server) => Response | Promise<Response> | undefined;
declare function createBunHttpHandler<TRouter extends AnyRouter>(
opts: BunHttpHandlerOptions<TRouter> & {
emitWsUpgrades?: boolean;
},
): (
request: Request,
server: Server,
) => Response | Promise<Response> | undefined;
type BunWSAdapterOptions<TRouter extends AnyRouter> = BaseHandlerOptions<TRouter, Request> & {
createContext?: (params: {
req: Request;
client: ServerWebSocket<BunWSClientCtx>;
}) => Promise<unknown> | unknown;
type BunWSAdapterOptions<TRouter extends AnyRouter> = BaseHandlerOptions<
TRouter,
Request
> & {
createContext?: (params: {
req: Request;
client: ServerWebSocket<BunWSClientCtx>;
}) => Promise<unknown> | unknown;
};
type BunWSClientCtx = {
req: Request;
handleRequest: (msg: TRPCClientOutgoingMessage) => Promise<void>;
unsubscribe(): void;
req: Request;
handleRequest: (msg: TRPCClientOutgoingMessage) => Promise<void>;
unsubscribe(): void;
};
declare function createBunWSHandler<TRouter extends AnyRouter>(opts: BunWSAdapterOptions<TRouter>): WebSocketHandler<BunWSClientCtx>;
declare function createBunWSHandler<TRouter extends AnyRouter>(
opts: BunWSAdapterOptions<TRouter>,
): WebSocketHandler<BunWSClientCtx>;
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
declare function createBunServeHandler<TRouter extends AnyRouter>(opts: BunHttpHandlerOptions<TRouter>, serveOptions?: Optional<ServeOptions, "fetch">): {
fetch(req: Request, server: Server): Promise<Response | undefined>;
websocket: bun.WebSocketHandler<BunWSClientCtx>;
error?: ((this: Server, request: bun.Errorlike) => Response | Promise<Response> | Promise<undefined> | undefined) | undefined;
id?: string | null | undefined;
port?: string | number | undefined;
reusePort?: boolean | undefined;
hostname?: string | undefined;
unix?: undefined;
maxRequestBodySize?: number | undefined;
development?: boolean | undefined;
declare function createBunServeHandler<TRouter extends AnyRouter>(
opts: BunHttpHandlerOptions<TRouter>,
serveOptions?: Optional<ServeOptions, "fetch">,
): {
fetch(req: Request, server: Server): Promise<Response | undefined>;
websocket: bun.WebSocketHandler<BunWSClientCtx>;
error?:
| ((
this: Server,
request: bun.Errorlike,
) => Response | Promise<Response> | Promise<undefined> | undefined)
| undefined;
id?: string | null | undefined;
port?: string | number | undefined;
reusePort?: boolean | undefined;
hostname?: string | undefined;
unix?: undefined;
maxRequestBodySize?: number | undefined;
development?: boolean | undefined;
};
export { type BunHttpHandlerOptions, type BunWSAdapterOptions, type BunWSClientCtx, createBunHttpHandler, createBunServeHandler, createBunWSHandler };
export {
type BunHttpHandlerOptions,
type BunWSAdapterOptions,
type BunWSClientCtx,
createBunHttpHandler,
createBunServeHandler,
createBunWSHandler,
};

@@ -7,14 +7,18 @@ "use strict";

var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
if ((from && typeof from === "object") || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, {
get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
});
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __toCommonJS = (mod) =>
__copyProps(__defProp({}, "__esModule", { value: true }), mod);

@@ -24,5 +28,5 @@ // src/index.ts

__export(src_exports, {
createBunHttpHandler: () => createBunHttpHandler,
createBunServeHandler: () => createBunServeHandler,
createBunWSHandler: () => createBunWSHandler
createBunHttpHandler: () => createBunHttpHandler,
createBunServeHandler: () => createBunServeHandler,
createBunWSHandler: () => createBunWSHandler,
});

@@ -34,16 +38,19 @@ module.exports = __toCommonJS(src_exports);

function createBunHttpHandler(opts) {
return (request, server) => {
const url = new URL(request.url);
if (opts.endpoint && !url.pathname.startsWith(opts.endpoint)) {
return;
}
if (opts.emitWsUpgrades && server.upgrade(request, { data: { req: request } })) {
return new Response(null, { status: 101 });
}
return (0, import_fetch.fetchRequestHandler)({
endpoint: opts.endpoint ?? "",
...opts,
req: request
});
};
return (request, server) => {
const url = new URL(request.url);
if (opts.endpoint && !url.pathname.startsWith(opts.endpoint)) {
return;
}
if (
opts.emitWsUpgrades &&
server.upgrade(request, { data: { req: request } })
) {
return new Response(null, { status: 101 });
}
return (0, import_fetch.fetchRequestHandler)({
endpoint: opts.endpoint ?? "",
...opts,
req: request,
});
};
}

@@ -57,209 +64,219 @@

function createBunWSHandler(opts) {
const { router, createContext } = opts;
const respond = (client, untransformedJSON) => {
client.send(
JSON.stringify(
(0, import_shared.transformTRPCResponse)(opts.router._def._config, untransformedJSON)
)
);
};
return {
async open(client) {
const { req } = client.data;
const clientSubscriptions = /* @__PURE__ */ new Map();
const ctxPromise = createContext?.({ req, client });
let ctx = void 0;
await (async () => {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = (0, import_server.getTRPCErrorFromUnknown)(cause);
opts.onError?.({
error,
path: void 0,
type: "unknown",
ctx,
req,
input: void 0
});
respond(client, {
id: null,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type: "unknown",
path: void 0,
input: void 0,
ctx
})
});
setImmediate(() => client.close());
}
})();
const stopSubscription = (subscription, { id, jsonrpc }) => {
subscription.unsubscribe();
respond(client, {
id,
jsonrpc,
result: {
type: "stopped"
}
});
};
client.data.handleRequest = async (msg) => {
const { id, jsonrpc } = msg;
if (id === null) {
throw new import_server.TRPCError({
code: "BAD_REQUEST",
message: "`id` is required"
});
}
if (msg.method === "subscription.stop") {
const sub = clientSubscriptions.get(id);
if (sub) {
stopSubscription(sub, { id, jsonrpc });
}
clientSubscriptions.delete(id);
return;
}
const { path, input } = msg.params;
const type = msg.method;
try {
await ctxPromise;
const result = await (0, import_server.callProcedure)({
procedures: router._def.procedures,
path,
rawInput: input,
// @ts-expect-error: for a newer trpc versions, cuz we use internal API
getRawInput: () => Promise.resolve(input),
ctx,
type
});
if (type === "subscription") {
if (!(0, import_observable.isObservable)(result)) {
throw new import_server.TRPCError({
message: `Subscription ${path} did not return an observable`,
code: "INTERNAL_SERVER_ERROR"
});
}
} else {
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data: result
}
});
return;
}
const observable = result;
const sub = observable.subscribe({
next(data) {
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data
}
});
},
error(err) {
const error = (0, import_server.getTRPCErrorFromUnknown)(err);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type,
path,
input,
ctx
})
});
},
complete() {
respond(client, {
id,
jsonrpc,
result: {
type: "stopped"
}
});
}
});
if (client.readyState !== WebSocket.OPEN) {
sub.unsubscribe();
return;
}
if (clientSubscriptions.has(id)) {
stopSubscription(sub, { id, jsonrpc });
throw new import_server.TRPCError({
message: `Duplicate id ${id}`,
code: "BAD_REQUEST"
});
}
clientSubscriptions.set(id, sub);
respond(client, {
id,
jsonrpc,
result: {
type: "started"
}
});
} catch (cause) {
const error = (0, import_server.getTRPCErrorFromUnknown)(cause);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type,
path,
input,
ctx
})
});
}
};
client.data.unsubscribe = () => {
for (const sub of clientSubscriptions.values()) {
sub.unsubscribe();
}
clientSubscriptions.clear();
};
},
async close(client) {
client.data.unsubscribe?.();
},
async message(client, message) {
try {
const msgJSON = JSON.parse(message.toString());
const msgs = Array.isArray(msgJSON) ? msgJSON : [msgJSON];
const promises = msgs.map((raw) => (0, import_rpc.parseTRPCMessage)(raw, router._def._config.transformer)).map(client.data.handleRequest);
await Promise.all(promises);
} catch (cause) {
const error = new import_server.TRPCError({
code: "PARSE_ERROR",
cause
});
respond(client, {
id: null,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type: "unknown",
path: void 0,
input: void 0,
ctx: void 0
})
});
}
}
};
const { router, createContext } = opts;
const respond = (client, untransformedJSON) => {
client.send(
JSON.stringify(
(0, import_shared.transformTRPCResponse)(
opts.router._def._config,
untransformedJSON,
),
),
);
};
return {
async open(client) {
const { req } = client.data;
const clientSubscriptions = /* @__PURE__ */ new Map();
const ctxPromise = createContext?.({ req, client });
let ctx = void 0;
await (async () => {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = (0, import_server.getTRPCErrorFromUnknown)(cause);
opts.onError?.({
error,
path: void 0,
type: "unknown",
ctx,
req,
input: void 0,
});
respond(client, {
id: null,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type: "unknown",
path: void 0,
input: void 0,
ctx,
}),
});
setImmediate(() => client.close());
}
})();
const stopSubscription = (subscription, { id, jsonrpc }) => {
subscription.unsubscribe();
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
};
client.data.handleRequest = async (msg) => {
const { id, jsonrpc } = msg;
if (id === null) {
throw new import_server.TRPCError({
code: "BAD_REQUEST",
message: "`id` is required",
});
}
if (msg.method === "subscription.stop") {
const sub = clientSubscriptions.get(id);
if (sub) {
stopSubscription(sub, { id, jsonrpc });
}
clientSubscriptions.delete(id);
return;
}
const { path, input } = msg.params;
const type = msg.method;
try {
await ctxPromise;
const result = await (0, import_server.callProcedure)({
procedures: router._def.procedures,
path,
rawInput: input,
// @ts-expect-error: for a newer trpc versions, cuz we use internal API
getRawInput: () => Promise.resolve(input),
ctx,
type,
});
if (type === "subscription") {
if (!(0, import_observable.isObservable)(result)) {
throw new import_server.TRPCError({
message: `Subscription ${path} did not return an observable`,
code: "INTERNAL_SERVER_ERROR",
});
}
} else {
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data: result,
},
});
return;
}
const observable = result;
const sub = observable.subscribe({
next(data) {
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data,
},
});
},
error(err) {
const error = (0, import_server.getTRPCErrorFromUnknown)(err);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
},
complete() {
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
},
});
if (client.readyState !== WebSocket.OPEN) {
sub.unsubscribe();
return;
}
if (clientSubscriptions.has(id)) {
stopSubscription(sub, { id, jsonrpc });
throw new import_server.TRPCError({
message: `Duplicate id ${id}`,
code: "BAD_REQUEST",
});
}
clientSubscriptions.set(id, sub);
respond(client, {
id,
jsonrpc,
result: {
type: "started",
},
});
} catch (cause) {
const error = (0, import_server.getTRPCErrorFromUnknown)(cause);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
}
};
client.data.unsubscribe = () => {
for (const sub of clientSubscriptions.values()) {
sub.unsubscribe();
}
clientSubscriptions.clear();
};
},
async close(client) {
client.data.unsubscribe?.();
},
async message(client, message) {
try {
const msgJSON = JSON.parse(message.toString());
const msgs = Array.isArray(msgJSON) ? msgJSON : [msgJSON];
const promises = msgs
.map((raw) =>
(0, import_rpc.parseTRPCMessage)(
raw,
router._def._config.transformer,
),
)
.map(client.data.handleRequest);
await Promise.all(promises);
} catch (cause) {
const error = new import_server.TRPCError({
code: "PARSE_ERROR",
cause,
});
respond(client, {
id: null,
error: (0, import_shared.getErrorShape)({
config: router._def._config,
error,
type: "unknown",
path: void 0,
input: void 0,
ctx: void 0,
}),
});
}
},
};
}

@@ -269,25 +286,26 @@

function createBunServeHandler(opts, serveOptions) {
const trpcHandler = createBunHttpHandler({
...opts,
emitWsUpgrades: true
});
return {
...serveOptions,
async fetch(req, server) {
const trpcReponse = trpcHandler(req, server);
if (trpcReponse) {
return trpcReponse;
}
return serveOptions?.fetch?.call(server, req, server);
},
websocket: createBunWSHandler(opts)
};
const trpcHandler = createBunHttpHandler({
...opts,
emitWsUpgrades: true,
});
return {
...serveOptions,
async fetch(req, server) {
const trpcReponse = trpcHandler(req, server);
if (trpcReponse) {
return trpcReponse;
}
return serveOptions?.fetch?.call(server, req, server);
},
websocket: createBunWSHandler(opts),
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createBunHttpHandler,
createBunServeHandler,
createBunWSHandler
});
0 &&
(module.exports = {
createBunHttpHandler,
createBunServeHandler,
createBunWSHandler,
});
/* istanbul ignore next -- @preserve */
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map
{
"name": "trpc-bun-adapter",
"version": "1.0.0-rc.0",
"version": "1.0.0",
"description": "TRPC adapter for bun js runtime",

@@ -10,3 +10,3 @@ "main": "dist/index.js",

"test": "bun test",
"fmt": "bunx @biomejs/biome format --write ./src",
"fmt": "bunx @biomejs/biome format --write .",
"build": "npx tsup src/index.ts --format cjs,esm --dts --clean --sourcemap"

@@ -20,3 +20,5 @@ },

],
"repository": "https://github.com/cah4a/trpc-bun-adapter",
"repository": {
"url": "git+https://github.com/cah4a/trpc-bun-adapter.git"
},
"bugs": "https://github.com/cah4a/trpc-bun-adapter/issues",

@@ -23,0 +25,0 @@ "author": "Sancha <cah4o3@gmail.com>",

@@ -1,13 +0,16 @@

# TRPC Bun Adapter
# tRPC Bun Adapter
[![npm version](https://badge.fury.io/js/trpc-bun-adapter.svg)](https://badge.fury.io/js/trpc-bun-adapter)
[![License](https://img.shields.io/github/license/cah4a/trpc-bun-adapter)](https://opensource.org/licenses/MIT)
## Description
Leverage [TRPC](https://trpc.io/) on [Bun](https://bun.sh/) with ease.
Supports both HTTP and WebSockets transports.
`trpc-bun-adapter` is a [tRPC](https://trpc.io/) adapter for [Bun](https://github.com/OptimalBits/bun).
Start both HTTP and WebSockets transports with ease.
## Quick Start
Install the package:
Install packages:
```bash

@@ -17,26 +20,40 @@ bun install @trpc/server trpc-bun-adapter

Paste the following code into `server.ts`:
Create a server.ts file with the following content:
```ts
import {initTRPC} from '@trpc/server';
import {createBunServeHandler} from 'trpc-bun-adapter';
import {router} from './router';
const t = initTRPC.create();
export const router = t.router({
ping: t.procedure.query(() => "pong"),
});
Bun.serve(createBunServeHandler({ router }));
```
Run the server with HTTP and WebSocket transports:
To start the server, run:
```bash
bun run server.ts
bun run --watch server.ts # to restart on file changes
```
Check that it works:
```bash
curl http://localhost:3000/ping
```
## API Reference
Ensure you have created a `router.ts` file as outlined in the tRPC documentation: [Define Routers](https://trpc.io/docs/server/routers).
### createBunServeHandler
Creates a Bun serve handler for HTTP and WebSocket transports:
Creates a Bun serve handler:
```ts
import {createBunServeHandler} from 'trpc-bun-adapter';
import {createBunServeHandler, CreateBunContextOptions} from 'trpc-bun-adapter';
import {router} from './router';
const createContext = (opts: { req: Request }) => ({
const createContext = (opts: CreateBunContextOptions) => ({
user: 1,

@@ -75,15 +92,11 @@ });

Arguments are:
- `options` - TRPC options
- `bunOptions` - Bun serve options
### createBunHttpHandler
Creates a Bun HTTP handler for HTTP transport:
Creates a Bun HTTP handler for tRPC HTTP requests:
```ts
import {createBunHttpHandler} from 'trpc-bun-adapter';
import {createBunHttpHandler, CreateBunContextOptions} from 'trpc-bun-adapter';
import {router} from './router';
const createContext = (opts: { req: Request }) => ({
const createContext = (opts: CreateBunContextOptions) => ({
user: 1,

@@ -119,10 +132,9 @@ });

Creates a Bun WebSocket handler for WebSocket transport:
Creates a Bun WebSocket handler for tRPC websocket requests:
```ts
import {ServerWebSocket} from "bun";
import {createBunWSHandler, BunWSClientCtx} from './src';
import {router} from './router';
import { createBunWSHandler, CreateBunContextOptions } from './src';
import { router } from './router';
const createContext = (opts: { req: Request, client: ServerWebSocket<BunWSClientCtx> }) => ({
const createContext = (opts: CreateBunContextOptions) => ({
user: 1,

@@ -153,2 +165,37 @@ });

### CreateBunContextOptions
To ensure your router recognizes the context type, define a `createContext` function utilizing the `CreateBunContextOptions` type:
```ts
import { initTRPC } from '@trpc/server';
import type { CreateBunContextOptions } from "src/createBunHttpHandler";
export const createContext = async (opts: CreateBunContextOptions) => {
return {
authorization: req.headers.get('Authorization')
};
};
```
With `createContext` defined, you can use it in your router to access the context, such as the authorization information:
```ts
const t = initTRPC.context<typeof createContext>().create();
export const router = t.router({
session: t.procedure.query(({ ctx }) => ctx.authorization),
});
```
Finally, pass your `createContext` function besides `router` to `createBunHttpHandler`.
This integrates your custom context into the HTTP handler setup:
```ts
createBunHttpHandler({
router,
createContext,
})
```
Read more documentation about tRPC contexts here: [Contexts](https://trpc.io/docs/server/context)
## License

@@ -155,0 +202,0 @@

@@ -1,34 +0,39 @@

import {Server} from "bun";
import {fetchRequestHandler} from "@trpc/server/adapters/fetch";
import type {AnyRouter, inferRouterContext} from "@trpc/server";
import type {HTTPBaseHandlerOptions} from "@trpc/server/http";
import { Server } from "bun";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import type { AnyRouter, inferRouterContext } from "@trpc/server";
import type { HTTPBaseHandlerOptions } from "@trpc/server/http";
export type CreateBunContextOptions = { req: Request };
export type BunHttpHandlerOptions<TRouter extends AnyRouter> =
HTTPBaseHandlerOptions<TRouter, Request> & {
endpoint?: string;
createContext?: (opts: { req: Request }) =>
| inferRouterContext<TRouter>
| Promise<inferRouterContext<TRouter>>;
};
HTTPBaseHandlerOptions<TRouter, Request> & {
endpoint?: string;
createContext?: (
opts: CreateBunContextOptions,
) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
};
export function createBunHttpHandler<TRouter extends AnyRouter>(
opts: BunHttpHandlerOptions<TRouter> & { emitWsUpgrades?: boolean },
opts: BunHttpHandlerOptions<TRouter> & { emitWsUpgrades?: boolean },
) {
return (request: Request, server: Server) => {
const url = new URL(request.url);
return (request: Request, server: Server) => {
const url = new URL(request.url);
if (opts.endpoint && !url.pathname.startsWith(opts.endpoint)) {
return;
}
if (opts.endpoint && !url.pathname.startsWith(opts.endpoint)) {
return;
}
if (opts.emitWsUpgrades && server.upgrade(request, {data: {req: request}})) {
return new Response(null, {status: 101});
}
if (
opts.emitWsUpgrades &&
server.upgrade(request, { data: { req: request } })
) {
return new Response(null, { status: 101 });
}
return fetchRequestHandler({
endpoint: opts.endpoint ?? "",
...opts,
req: request,
});
};
return fetchRequestHandler({
endpoint: opts.endpoint ?? "",
...opts,
req: request,
});
};
}

@@ -1,306 +0,305 @@

import {test, describe, beforeAll, afterAll, expect} from "bun:test";
import {createBunServeHandler} from "./createBunServeHandler";
import {initTRPC} from "@trpc/server";
import {observable} from "@trpc/server/observable";
import {Server} from "bun";
import { test, describe, beforeAll, afterAll, expect } from "bun:test";
import { createBunServeHandler } from "./createBunServeHandler";
import { initTRPC } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import { Server } from "bun";
describe("e2e", () => {
let server: Server;
let server: Server;
const createContext = ({req}: { req: Request }) => {
return {
name: req.headers.get("x-name") ?? "World",
};
};
const createContext = ({ req }: { req: Request }) => {
return {
name: req.headers.get("x-name") ?? "World",
};
};
const t = initTRPC.context<typeof createContext>().create();
const t = initTRPC.context<typeof createContext>().create();
const router = t.router({
hello: t.procedure.query(({ctx}) => `Hello ${ctx.name}!`),
const router = t.router({
hello: t.procedure.query(({ ctx }) => `Hello ${ctx.name}!`),
exception: t.procedure.query(() => {
throw new Error("MyError");
}),
exception: t.procedure.query(() => {
throw new Error("MyError");
}),
digits: t.procedure.subscription(() =>
observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(0);
subscriber.next(1);
subscriber.next(2);
subscriber.error(new Error("MyError"));
}, 10)
}),
),
});
digits: t.procedure.subscription(() =>
observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(0);
subscriber.next(1);
subscriber.next(2);
subscriber.error(new Error("MyError"));
}, 10);
}),
),
});
beforeAll(async () => {
server = Bun.serve(
createBunServeHandler(
{
router,
endpoint: "/trpc",
createContext,
},
{
port: 13123,
fetch(request, server): Response | Promise<Response> {
return new Response("Falling back to fetch");
},
},
),
);
});
beforeAll(async () => {
server = Bun.serve(
createBunServeHandler(
{
router,
endpoint: "/trpc",
createContext,
},
{
port: 13123,
fetch(request, server): Response | Promise<Response> {
return new Response("Falling back to fetch");
},
},
),
);
});
afterAll(() => server.stop());
afterAll(() => server.stop());
test("http call procedure", async () => {
const response = await fetch("http://localhost:13123/trpc/hello");
expect(response.ok).toBe(true);
const result = await response.json();
expect(result).toEqual({result: {data: "Hello World!"}});
});
test("http call procedure", async () => {
const response = await fetch("http://localhost:13123/trpc/hello");
expect(response.ok).toBe(true);
const result = await response.json();
expect(result).toEqual({ result: { data: "Hello World!" } });
});
test("http call procedure +ctx", async () => {
const response = await fetch("http://localhost:13123/trpc/hello", {
headers: {
"x-name": "John",
},
});
expect(response.ok).toBe(true);
const result = await response.json();
expect(result).toEqual({result: {data: "Hello John!"}});
});
test("http call procedure +ctx", async () => {
const response = await fetch("http://localhost:13123/trpc/hello", {
headers: {
"x-name": "John",
},
});
expect(response.ok).toBe(true);
const result = await response.json();
expect(result).toEqual({ result: { data: "Hello John!" } });
});
test("http call exception", async () => {
const response = await fetch("http://localhost:13123/trpc/exception");
expect(response.ok).toBe(false);
const result = await response.json();
expect(result).toEqual({
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "exception",
stack: expect.any(String),
},
},
});
});
test("http call exception", async () => {
const response = await fetch("http://localhost:13123/trpc/exception");
expect(response.ok).toBe(false);
const result = await response.json();
expect(result).toEqual({
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "exception",
stack: expect.any(String),
},
},
});
});
test("websocket call procedure", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
test("websocket call procedure", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "hello",
},
}),
);
};
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "hello",
},
}),
);
};
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
result: {
type: "data",
data: "Hello World!",
},
});
} finally {
resolve(true);
}
};
});
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
result: {
type: "data",
data: "Hello World!",
},
});
} finally {
resolve(true);
}
};
});
ws.close();
});
ws.close();
});
test("ws error", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
test("ws error", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "unknown",
},
}),
);
};
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "unknown",
},
}),
);
};
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
error: {
code: -32004,
message: `No "query"-procedure on path "unknown"`,
data: {
code: "NOT_FOUND",
httpStatus: 404,
path: "unknown",
stack: expect.any(String),
}
}
});
} finally {
resolve(true);
}
};
});
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
error: {
code: -32004,
message: `No "query"-procedure on path "unknown"`,
data: {
code: "NOT_FOUND",
httpStatus: 404,
path: "unknown",
stack: expect.any(String),
},
},
});
} finally {
resolve(true);
}
};
});
ws.close();
})
ws.close();
});
test("ws exception", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
test("ws exception", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const id = Math.random();
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "exception",
},
}),
);
};
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "query",
params: {
path: "exception",
},
}),
);
};
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "exception",
stack: expect.any(String),
}
}
});
} finally {
resolve(true);
}
};
});
await new Promise((resolve, reject) => {
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
try {
expect(data).toEqual({
id,
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "exception",
stack: expect.any(String),
},
},
});
} finally {
resolve(true);
}
};
});
ws.close();
})
ws.close();
});
test(
"websocket call subscription",
async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
test("websocket call subscription", async () => {
const ws = new WebSocket("ws://localhost:13123/trpc");
const messages: unknown[] = [];
const id = Math.random();
const messages: unknown[] = [];
const id = Math.random();
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "subscription",
params: {
path: "digits",
},
}),
);
};
ws.onopen = () => {
ws.send(
JSON.stringify({
id,
method: "subscription",
params: {
path: "digits",
},
}),
);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
messages.push(data);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
messages.push(data);
};
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 100));
ws.send(JSON.stringify({
id,
method: "subscription.stop",
}))
ws.send(
JSON.stringify({
id,
method: "subscription.stop",
}),
);
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 100));
ws.close();
ws.close();
expect(messages).toEqual([
{
id,
result: {
type: "started",
},
},
{
id,
result: {
type: "data",
data: 0,
},
},
{
id,
result: {
type: "data",
data: 1,
},
},
{
id,
result: {
type: "data",
data: 2,
},
},
{
id,
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "digits",
stack: expect.any(String),
},
}
},
{
id,
result: {
type: "stopped",
},
},
]);
},
);
expect(messages).toEqual([
{
id,
result: {
type: "started",
},
},
{
id,
result: {
type: "data",
data: 0,
},
},
{
id,
result: {
type: "data",
data: 1,
},
},
{
id,
result: {
type: "data",
data: 2,
},
},
{
id,
error: {
code: -32603,
message: "MyError",
data: {
code: "INTERNAL_SERVER_ERROR",
httpStatus: 500,
path: "digits",
stack: expect.any(String),
},
},
},
{
id,
result: {
type: "stopped",
},
},
]);
});
test("fall through to fetch", async () => {
const response = await fetch("http://localhost:13123/other");
expect(response.ok).toBe(true);
const result = await response.text();
expect(result).toEqual("Falling back to fetch");
});
test("fall through to fetch", async () => {
const response = await fetch("http://localhost:13123/other");
expect(response.ok).toBe(true);
const result = await response.text();
expect(result).toEqual("Falling back to fetch");
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc