New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

trpc-bun-adapter

Package Overview
Dependencies
Maintainers
1
Versions
8
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 to 1.1.0

.editorconfig

98

dist/index.d.ts

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

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, BaseHandlerOptions } from '@trpc/server/http';
import { TRPCClientOutgoingMessage } from '@trpc/server/rpc';
type BunHttpHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<
TRouter,
Request
> & {
endpoint?: string;
createContext?: (opts: {
req: Request;
}) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
type CreateBunContextOptions = {
req: Request;
};
declare function createBunHttpHandler<TRouter extends AnyRouter>(
opts: BunHttpHandlerOptions<TRouter> & {
emitWsUpgrades?: boolean;
},
): (
request: Request,
server: Server,
) => Response | Promise<Response> | undefined;
type BunHttpHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<TRouter, Request> & {
endpoint?: string;
createContext?: (opts: CreateBunContextOptions) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
};
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, type CreateBunContextOptions, createBunHttpHandler, createBunServeHandler, createBunWSHandler };

@@ -7,18 +7,14 @@ "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);

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

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

@@ -38,19 +34,16 @@ 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
});
};
}

@@ -61,222 +54,225 @@

var import_server = require("@trpc/server");
var import_shared = require("@trpc/server/shared");
var import_observable = require("@trpc/server/observable");
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_server.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_server.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,
input,
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_server.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_server.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_server.getErrorShape)({
config: router._def._config,
error,
type: "unknown",
path: void 0,
input: void 0,
ctx: void 0
})
});
}
}
};
}

@@ -286,26 +282,25 @@

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",
"version": "1.1.0",
"description": "TRPC adapter for bun js runtime",

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

"devDependencies": {
"@trpc/server": "10.43.0",
"@trpc/server": "next",
"bun-types": "^1.0.20",

@@ -33,4 +33,4 @@ "tsup": "^8.0.1",

"peerDependencies": {
"@trpc/server": "^10.43.0"
"@trpc/server": "^11.0.0-next-beta.228"
}
}

@@ -9,32 +9,32 @@ import { Server } from "bun";

export type BunHttpHandlerOptions<TRouter extends AnyRouter> =
HTTPBaseHandlerOptions<TRouter, Request> & {
endpoint?: string;
createContext?: (
opts: CreateBunContextOptions,
) => 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,
});
};
}
import type { ServeOptions, Server } from "bun";
import { createBunWSHandler } from "./createBunWSHandler";
import {
BunHttpHandlerOptions,
createBunHttpHandler,
BunHttpHandlerOptions,
createBunHttpHandler,
} from "./createBunHttpHandler";

@@ -12,23 +12,23 @@ import type { AnyRouter } from "@trpc/server";

export function createBunServeHandler<TRouter extends AnyRouter>(
opts: BunHttpHandlerOptions<TRouter>,
serveOptions?: Optional<ServeOptions, "fetch">,
opts: BunHttpHandlerOptions<TRouter>,
serveOptions?: Optional<ServeOptions, "fetch">,
) {
const trpcHandler = createBunHttpHandler({
...opts,
emitWsUpgrades: true,
});
const trpcHandler = createBunHttpHandler({
...opts,
emitWsUpgrades: true,
});
return {
...serveOptions,
async fetch(req: Request, server: Server) {
const trpcReponse = trpcHandler(req, server);
return {
...serveOptions,
async fetch(req: Request, server: Server) {
const trpcReponse = trpcHandler(req, server);
if (trpcReponse) {
return trpcReponse;
}
if (trpcReponse) {
return trpcReponse;
}
return serveOptions?.fetch?.call(server, req, server);
},
websocket: createBunWSHandler(opts),
};
return serveOptions?.fetch?.call(server, req, server);
},
websocket: createBunWSHandler(opts),
};
}
import { ServerWebSocket, WebSocketHandler } from "bun";
import {
JSONRPC2,
parseTRPCMessage,
TRPCClientOutgoingMessage,
TRPCResponseMessage,
JSONRPC2,
parseTRPCMessage,
TRPCClientOutgoingMessage,
TRPCResponseMessage,
} from "@trpc/server/rpc";
import {
AnyRouter,
callProcedure,
getTRPCErrorFromUnknown,
inferRouterContext,
TRPCError,
AnyRouter,
callProcedure,
getErrorShape,
transformTRPCResponse,
getTRPCErrorFromUnknown,
inferRouterContext,
TRPCError,
} from "@trpc/server";
import { getErrorShape, transformTRPCResponse } from "@trpc/server/shared";
import { isObservable, Unsubscribable } from "@trpc/server/observable";
import type { BaseHandlerOptions } from "@trpc/server/src/internals/types";
import type { BaseHandlerOptions } from "@trpc/server/http";
export type BunWSAdapterOptions<TRouter extends AnyRouter> = BaseHandlerOptions<
TRouter,
Request
TRouter,
Request
> & {
createContext?: (params: {
req: Request;
client: ServerWebSocket<BunWSClientCtx>;
}) => Promise<unknown> | unknown;
createContext?: (params: {
req: Request;
client: ServerWebSocket<BunWSClientCtx>;
}) => Promise<unknown> | unknown;
};
export type BunWSClientCtx = {
req: Request;
handleRequest: (msg: TRPCClientOutgoingMessage) => Promise<void>;
unsubscribe(): void;
req: Request;
handleRequest: (msg: TRPCClientOutgoingMessage) => Promise<void>;
unsubscribe(): void;
};
export function createBunWSHandler<TRouter extends AnyRouter>(
opts: BunWSAdapterOptions<TRouter>,
opts: BunWSAdapterOptions<TRouter>,
): WebSocketHandler<BunWSClientCtx> {
const { router, createContext } = opts;
const { router, createContext } = opts;
const respond = (
client: ServerWebSocket<unknown>,
untransformedJSON: TRPCResponseMessage,
) => {
client.send(
JSON.stringify(
transformTRPCResponse(opts.router._def._config, untransformedJSON),
),
);
};
const respond = (
client: ServerWebSocket<unknown>,
untransformedJSON: TRPCResponseMessage,
) => {
client.send(
JSON.stringify(
transformTRPCResponse(
opts.router._def._config,
untransformedJSON,
),
),
);
};
return {
async open(client) {
const { req } = client.data;
const clientSubscriptions = new Map<string | number, Unsubscribable>();
return {
async open(client) {
const { req } = client.data;
const clientSubscriptions = new Map<
string | number,
Unsubscribable
>();
const ctxPromise = createContext?.({ req, client });
let ctx: inferRouterContext<TRouter> | undefined = undefined;
await (async () => {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: "unknown",
ctx,
req,
input: undefined,
});
respond(client, {
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: "unknown",
path: undefined,
input: undefined,
ctx,
}),
});
const ctxPromise = createContext?.({ req, client });
let ctx: inferRouterContext<TRouter> | undefined = undefined;
await (async () => {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: "unknown",
ctx,
req,
input: undefined,
});
respond(client, {
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: "unknown",
path: undefined,
input: undefined,
ctx,
}),
});
// close in next tick
setImmediate(() => client.close());
}
})();
// close in next tick
setImmediate(() => client.close());
}
})();
const stopSubscription = (
subscription: Unsubscribable,
{ id, jsonrpc }: JSONRPC2.BaseEnvelope & { id: JSONRPC2.RequestId },
) => {
subscription.unsubscribe();
const stopSubscription = (
subscription: Unsubscribable,
{
id,
jsonrpc,
}: JSONRPC2.BaseEnvelope & { id: JSONRPC2.RequestId },
) => {
subscription.unsubscribe();
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
};
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
};
client.data.handleRequest = async (msg: TRPCClientOutgoingMessage) => {
const { id, jsonrpc } = msg;
/* istanbul ignore next -- @preserve */
if (id === null) {
throw new 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; // asserts context has been set
client.data.handleRequest = async (
msg: TRPCClientOutgoingMessage,
) => {
const { id, jsonrpc } = msg;
/* istanbul ignore next -- @preserve */
if (id === null) {
throw new 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; // asserts context has been set
const result = await 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,
});
const result = await callProcedure({
procedures: router._def.procedures,
path,
input,
getRawInput: () => Promise.resolve(input),
ctx,
type,
});
if (type === "subscription") {
if (!isObservable(result)) {
throw new TRPCError({
message: `Subscription ${path} did not return an observable`,
code: "INTERNAL_SERVER_ERROR",
});
}
} else {
// send the value as data if the method is not a subscription
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data: result,
},
});
return;
}
if (type === "subscription") {
if (!isObservable(result)) {
throw new TRPCError({
message: `Subscription ${path} did not return an observable`,
code: "INTERNAL_SERVER_ERROR",
});
}
} else {
// send the value as data if the method is not a subscription
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 = getTRPCErrorFromUnknown(err);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
},
complete() {
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
},
});
const observable = result;
const sub = observable.subscribe({
next(data) {
respond(client, {
id,
jsonrpc,
result: {
type: "data",
data,
},
});
},
error(err) {
const error = getTRPCErrorFromUnknown(err);
opts.onError?.({
error,
path,
type,
ctx,
req,
input,
});
respond(client, {
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
},
complete() {
respond(client, {
id,
jsonrpc,
result: {
type: "stopped",
},
});
},
});
if (client.readyState !== WebSocket.OPEN) {
// if the client got disconnected whilst initializing the subscription
// no need to send stopped message if the client is disconnected
sub.unsubscribe();
return;
}
if (client.readyState !== WebSocket.OPEN) {
// if the client got disconnected whilst initializing the subscription
// no need to send stopped message if the client is disconnected
sub.unsubscribe();
return;
}
if (clientSubscriptions.has(id)) {
// duplicate request ids for client
stopSubscription(sub, { id, jsonrpc });
throw new TRPCError({
message: `Duplicate id ${id}`,
code: "BAD_REQUEST",
});
}
clientSubscriptions.set(id, sub);
if (clientSubscriptions.has(id)) {
// duplicate request ids for client
stopSubscription(sub, { id, jsonrpc });
throw new TRPCError({
message: `Duplicate id ${id}`,
code: "BAD_REQUEST",
});
}
clientSubscriptions.set(id, sub);
respond(client, {
id,
jsonrpc,
result: {
type: "started",
},
});
} catch (cause) {
// procedure threw an error
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
}
};
respond(client, {
id,
jsonrpc,
result: {
type: "started",
},
});
} catch (cause) {
// procedure threw an error
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({ error, path, type, ctx, req, input });
respond(client, {
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
}
};
client.data.unsubscribe = () => {
for (const sub of clientSubscriptions.values()) {
sub.unsubscribe();
}
clientSubscriptions.clear();
};
},
client.data.unsubscribe = () => {
for (const sub of clientSubscriptions.values()) {
sub.unsubscribe();
}
clientSubscriptions.clear();
};
},
async close(client) {
client.data.unsubscribe?.();
},
async close(client) {
client.data.unsubscribe?.();
},
async message(client, message) {
try {
const msgJSON: unknown = JSON.parse(message.toString());
const msgs: unknown[] = Array.isArray(msgJSON) ? msgJSON : [msgJSON];
async message(client, message) {
try {
const msgJSON: unknown = JSON.parse(message.toString());
const msgs: unknown[] = Array.isArray(msgJSON)
? msgJSON
: [msgJSON];
const promises = msgs
.map((raw) => parseTRPCMessage(raw, router._def._config.transformer))
.map(client.data.handleRequest);
const promises = msgs
.map((raw) =>
parseTRPCMessage(raw, router._def._config.transformer),
)
.map(client.data.handleRequest);
await Promise.all(promises);
} catch (cause) {
const error = new TRPCError({
code: "PARSE_ERROR",
cause,
});
await Promise.all(promises);
} catch (cause) {
const error = new TRPCError({
code: "PARSE_ERROR",
cause,
});
respond(client, {
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: "unknown",
path: undefined,
input: undefined,
ctx: undefined,
}),
});
}
},
};
respond(client, {
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: "unknown",
path: undefined,
input: undefined,
ctx: undefined,
}),
});
}
},
};
}

@@ -8,299 +8,299 @@ import { test, describe, beforeAll, afterAll, expect } from "bun:test";

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

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