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

@trpc/server

Package Overview
Dependencies
Maintainers
3
Versions
1155
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@trpc/server - npm Package Compare versions

Comparing version 11.0.0-alpha-tmp-opt-peers.204 to 11.0.0-alpha-tmp-query-optoins-codemod.670

adapters/next-app-dir/index.d.ts

22

dist/adapters/aws-lambda/index.d.ts
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,11 +10,14 @@ * ```ts

*/
import type { APIGatewayProxyEvent, APIGatewayProxyEventV2, APIGatewayProxyResult, APIGatewayProxyStructuredResultV2, Context as APIGWContext } from 'aws-lambda';
import type { AnyRouter } from '../../@trpc-server';
import type { APIGatewayEvent, AWSLambdaOptions } from './utils';
export * from './utils';
/** 1:1 mapping of v1 or v2 input events, deduces which is which.
* @internal
**/
type inferAPIGWReturn<TType> = TType extends APIGatewayProxyEvent ? APIGatewayProxyResult : TType extends APIGatewayProxyEventV2 ? APIGatewayProxyStructuredResultV2 : never;
export declare function awsLambdaRequestHandler<TRouter extends AnyRouter, TEvent extends APIGatewayEvent, TResult extends inferAPIGWReturn<TEvent>>(opts: AWSLambdaOptions<TRouter, TEvent>): (event: TEvent, context: APIGWContext) => Promise<TResult>;
import type { Context as APIGWContext } from 'aws-lambda';
import type { AnyRouter, CreateContextCallback, inferRouterContext } from '../../@trpc/server';
import type { HTTPBaseHandlerOptions, TRPCRequestInfo } from '../../@trpc/server/http';
import type { inferAPIGWReturn, LambdaEvent } from './getPlanner';
export type CreateAWSLambdaContextOptions<TEvent extends LambdaEvent> = {
event: TEvent;
context: APIGWContext;
info: TRPCRequestInfo;
};
export type AWSLambdaOptions<TRouter extends AnyRouter, TEvent extends LambdaEvent> = HTTPBaseHandlerOptions<TRouter, TEvent> & CreateContextCallback<inferRouterContext<AnyRouter>, AWSLambdaCreateContextFn<TRouter, TEvent>>;
export type AWSLambdaCreateContextFn<TRouter extends AnyRouter, TEvent extends LambdaEvent> = ({ event, context, info, }: CreateAWSLambdaContextOptions<TEvent>) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
export declare function awsLambdaRequestHandler<TRouter extends AnyRouter, TEvent extends LambdaEvent>(opts: AWSLambdaOptions<TRouter, TEvent>): (event: TEvent, context: APIGWContext) => Promise<inferAPIGWReturn<TEvent>>;
//# sourceMappingURL=index.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var resolveResponse = require('../../unstable-core-do-not-import/http/resolveResponse.js');
require('../../unstable-core-do-not-import/rootConfig.js');
require('../../vendor/unpromise/unpromise.js');
require('../../unstable-core-do-not-import/stream/utils/disposable.js');
var getPlanner = require('./getPlanner.js');
var core = require('@trpc/core');
var http = require('@trpc/core/http');
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/ // @trpc/server
function isPayloadV1(event) {
return determinePayloadFormat(event) == '1.0';
}
function isPayloadV2(event) {
return determinePayloadFormat(event) == '2.0';
}
function determinePayloadFormat(event) {
// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
// According to AWS support, version is is extracted from the version property in the event.
// If there is no version property, then the version is implied as 1.0
const unknownEvent = event;
if (typeof unknownEvent.version === 'undefined') {
return '1.0';
} else {
if ([
'1.0',
'2.0'
].includes(unknownEvent.version)) {
return unknownEvent.version;
} else {
return 'custom';
}
}
}
function getHTTPMethod(event) {
if (isPayloadV1(event)) {
return event.httpMethod;
}
if (isPayloadV2(event)) {
return event.requestContext.http.method;
}
throw new core.TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE
});
}
function getPath(event) {
if (isPayloadV1(event)) {
if (!event.pathParameters) {
// Then this event was not triggered by a resource denoted with {proxy+}
return event.path.split('/').pop() ?? '';
}
const matches = event.resource.matchAll(/\{(.*?)\}/g);
for (const match of matches){
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const group = match[1];
if (group.includes('+') && event.pathParameters) {
return event.pathParameters[group.replace('+', '')] ?? '';
}
}
return event.path.slice(1);
}
if (isPayloadV2(event)) {
const matches1 = event.routeKey.matchAll(/\{(.*?)\}/g);
for (const match1 of matches1){
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const group1 = match1[1];
if (group1.includes('+') && event.pathParameters) {
return event.pathParameters[group1.replace('+', '')] ?? '';
}
}
return event.rawPath.slice(1);
}
throw new core.TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE
});
}
function transformHeaders(headers) {
const obj = {};
for (const [key, value] of Object.entries(headers)){
if (typeof value === 'undefined') {
continue;
}
obj[key] = Array.isArray(value) ? value.join(',') : value;
}
return obj;
}
const UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE = 'Custom payload format version not handled by this adapter. Please use either 1.0 or 2.0. More information here' + 'https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html';
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/ // @trpc/server
function lambdaEventToHTTPRequest(event) {
const query = new URLSearchParams();
for (const [key, value] of Object.entries(event.queryStringParameters ?? {})){
if (typeof value !== 'undefined') {
query.append(key, value);
}
}
let body;
if (event.body && event.isBase64Encoded) {
body = Buffer.from(event.body, 'base64').toString('utf8');
} else {
body = event.body;
}
return {
method: getHTTPMethod(event),
query: query,
headers: event.headers,
body: body
};
}
function tRPCOutputToAPIGatewayOutput(event, response) {
if (isPayloadV1(event)) {
const resp = {
statusCode: response.status,
body: response.body ?? '',
headers: transformHeaders(response.headers ?? {})
};
return resp;
} else if (isPayloadV2(event)) {
const resp1 = {
statusCode: response.status,
body: response.body ?? undefined,
headers: transformHeaders(response.headers ?? {})
};
return resp1;
} else {
throw new core.TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE
});
}
}
function awsLambdaRequestHandler(opts) {
return async (event, context)=>{
const req = lambdaEventToHTTPRequest(event);
const path = getPath(event);
const planner = getPlanner.getPlanner(event);
const createContext = async (innerOpts)=>{

@@ -161,9 +19,7 @@ return await opts.createContext?.({

};
const response = await http.resolveHTTPResponse({
router: opts.router,
batching: opts.batching,
responseMeta: opts?.responseMeta,
const response = await resolveResponse.resolveResponse({
...opts,
createContext,
req,
path,
req: planner.request,
path: planner.path,
error: null,

@@ -177,12 +33,6 @@ onError (o) {

});
return tRPCOutputToAPIGatewayOutput(event, response);
return await planner.toResult(response);
};
}
exports.UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE = UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE;
exports.awsLambdaRequestHandler = awsLambdaRequestHandler;
exports.getHTTPMethod = getHTTPMethod;
exports.getPath = getPath;
exports.isPayloadV1 = isPayloadV1;
exports.isPayloadV2 = isPayloadV2;
exports.transformHeaders = transformHeaders;
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,3 +11,3 @@ * ```ts

import type * as express from 'express';
import type { AnyRouter } from '../@trpc-server';
import type { AnyRouter } from '../@trpc/server';
import type { NodeHTTPCreateContextFnOptions, NodeHTTPHandlerOptions } from './node-http';

@@ -15,0 +14,0 @@ export type CreateExpressContextOptions = NodeHTTPCreateContextFnOptions<express.Request, express.Response>;

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var utils = require('../unstable-core-do-not-import/utils.js');
require('../vendor/unpromise/unpromise.js');
require('../unstable-core-do-not-import/stream/utils/disposable.js');
require('../unstable-core-do-not-import/rootConfig.js');
var nodeHTTPRequestHandler = require('./node-http/nodeHTTPRequestHandler.js');
var nodeHTTPRequestHandler = require('../nodeHTTPRequestHandler-83441c73.js');
require('@trpc/core/http');
require('./node-http/content-type/json/index.js');
require('../contentType-d9d22104.js');
require('@trpc/core');
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/ // eslint-disable-next-line no-restricted-imports
function createExpressMiddleware(opts) {
return async (req, res)=>{
const endpoint = req.path.slice(1);
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...opts,
return (req, res)=>{
let path = '';
utils.run(async ()=>{
path = req.path.slice(req.path.lastIndexOf('/') + 1);
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
...opts,
req,
res,
path
});
}).catch(nodeHTTPRequestHandler.internal_exceptionHandler({
req,
res,
path: endpoint
});
path,
...opts
}));
};

@@ -22,0 +36,0 @@ }

@@ -0,5 +1,14 @@

/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/
import type { FastifyReply, FastifyRequest } from 'fastify';
import type { AnyRouter } from '../../@trpc-server';
import type { HTTPBaseHandlerOptions } from '../../http';
import type { NodeHTTPCreateContextOption } from '../node-http';
import type { AnyRouter } from '../../@trpc/server';
import { type HTTPBaseHandlerOptions } from '../../@trpc/server/http';
import { type NodeHTTPCreateContextOption } from '../node-http';
export type FastifyHandlerOptions<TRouter extends AnyRouter, TRequest extends FastifyRequest, TResponse extends FastifyReply> = HTTPBaseHandlerOptions<TRouter, TRequest> & NodeHTTPCreateContextOption<TRouter, TRequest, TResponse>;

@@ -11,4 +20,4 @@ type FastifyRequestHandlerOptions<TRouter extends AnyRouter, TRequest extends FastifyRequest, TResponse extends FastifyReply> = FastifyHandlerOptions<TRouter, TRequest, TResponse> & {

};
export declare function fastifyRequestHandler<TRouter extends AnyRouter, TRequest extends FastifyRequest, TResponse extends FastifyReply>(opts: FastifyRequestHandlerOptions<TRouter, TRequest, TResponse>): Promise<never>;
export declare function fastifyRequestHandler<TRouter extends AnyRouter, TRequest extends FastifyRequest, TResponse extends FastifyReply>(opts: FastifyRequestHandlerOptions<TRouter, TRequest, TResponse>): Promise<void>;
export {};
//# sourceMappingURL=fastifyRequestHandler.d.ts.map
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,5 +11,5 @@ * ```ts

import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
import type { FastifyHandlerOptions } from '.';
import type { AnyRouter } from '../../@trpc-server';
import type { AnyRouter } from '../../@trpc/server';
import type { NodeHTTPCreateContextFnOptions } from '../node-http';
import type { FastifyHandlerOptions } from './fastifyRequestHandler';
export interface FastifyTRPCPluginOptions<TRouter extends AnyRouter> {

@@ -17,0 +16,0 @@ prefix?: string;

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var fastifyRequestHandler = require('./fastifyRequestHandler.js');
var fastifyTRPCPlugin = require('./fastifyTRPCPlugin.js');
var node_stream = require('node:stream');
var http = require('@trpc/core/http');
var adapters_ws = require('../ws.js');
require('@trpc/core/observable');
require('@trpc/core/rpc');
require('@trpc/core');
async function fastifyRequestHandler(opts) {
const createContext = async (innerOpts)=>{
return await opts.createContext?.({
...opts,
...innerOpts
});
};
const query = opts.req.query ? new URLSearchParams(opts.req.query) : new URLSearchParams(opts.req.url.split('?')[1]);
const req = {
query,
method: opts.req.method,
headers: opts.req.headers,
body: opts.req.body ?? 'null'
};
let resolve;
const promise = new Promise((r)=>resolve = r);
let isStream = false;
let stream;
let formatter;
const unstable_onHead = (head, isStreaming)=>{
if (!opts.res.statusCode || opts.res.statusCode === 200) {
opts.res.statusCode = head.status;
}
for (const [key, value] of Object.entries(head.headers ?? {})){
/* istanbul ignore if -- @preserve */ if (typeof value === 'undefined') {
continue;
}
void opts.res.header(key, value);
}
if (isStreaming) {
void opts.res.header('Transfer-Encoding', 'chunked');
void opts.res.header('Vary', opts.res.hasHeader('Vary') ? 'trpc-batch-mode, ' + opts.res.getHeader('Vary') : 'trpc-batch-mode');
stream = new node_stream.Readable();
stream._read = ()=>{}; // eslint-disable-line @typescript-eslint/no-empty-function -- https://github.com/fastify/fastify/issues/805#issuecomment-369172154
resolve(opts.res.send(stream));
isStream = true;
formatter = http.getBatchStreamFormatter();
}
};
const unstable_onChunk = ([index, string])=>{
if (index === -1) {
// full response, no streaming
resolve(opts.res.send(string));
} else {
stream.push(formatter(index, string));
}
};
http.resolveHTTPResponse({
req,
createContext,
path: opts.path,
router: opts.router,
batching: opts.batching,
responseMeta: opts.responseMeta,
onError (o) {
opts?.onError?.({
...o,
req: opts.req
});
},
unstable_onHead,
unstable_onChunk
}).then(()=>{
if (isStream) {
stream.push(formatter.end());
stream.push(null); // https://github.com/fastify/fastify/issues/805#issuecomment-369172154
}
}).catch(()=>{
if (isStream) {
stream.push(null);
}
});
return promise;
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/ /// <reference types="@fastify/websocket" />
function fastifyTRPCPlugin(fastify, opts, done) {
fastify.removeContentTypeParser('application/json');
fastify.addContentTypeParser('application/json', {
parseAs: 'string'
}, function(_, body, _done) {
_done(null, body);
});
let prefix = opts.prefix ?? '';
// https://github.com/fastify/fastify-plugin/blob/fe079bef6557a83794bf437e14b9b9edb8a74104/plugin.js#L11
// @ts-expect-error property 'default' does not exists on type ...
if (typeof fastifyTRPCPlugin.default !== 'function') {
prefix = ''; // handled by fastify internally
}
fastify.all(`${prefix}/:path`, async (req, res)=>{
const path = req.params.path;
await fastifyRequestHandler({
...opts.trpcOptions,
req,
res,
path
});
});
if (opts.useWSS) {
adapters_ws.applyWSSHandler({
...opts.trpcOptions,
wss: fastify.websocketServer
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
fastify.get(prefix ?? '/', {
websocket: true
}, ()=>{});
}
done();
}
exports.fastifyRequestHandler = fastifyRequestHandler;
exports.fastifyTRPCPlugin = fastifyTRPCPlugin;
exports.fastifyRequestHandler = fastifyRequestHandler.fastifyRequestHandler;
exports.fastifyTRPCPlugin = fastifyTRPCPlugin.fastifyTRPCPlugin;
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,9 +10,5 @@ * ```ts

*/
import type { AnyRouter } from '../../@trpc-server';
import type { FetchHandlerOptions } from './types';
export type FetchHandlerRequestOptions<TRouter extends AnyRouter> = FetchHandlerOptions<TRouter> & {
req: Request;
endpoint: string;
};
import type { AnyRouter } from '../../@trpc/server';
import type { FetchHandlerRequestOptions } from './types';
export declare function fetchRequestHandler<TRouter extends AnyRouter>(opts: FetchHandlerRequestOptions<TRouter>): Promise<Response>;
//# sourceMappingURL=fetchRequestHandler.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var fetchRequestHandler = require('./fetchRequestHandler.js');
var http = require('@trpc/core/http');
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example
* ```ts
* import type { AnyTRPCRouter } from '@trpc/server'
* import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
* ```
*/ // @trpc/server
const trimSlashes = (path)=>{
path = path.startsWith('/') ? path.slice(1) : path;
path = path.endsWith('/') ? path.slice(0, -1) : path;
return path;
};
async function fetchRequestHandler(opts) {
const resHeaders = new Headers();
const createContext = async (innerOpts)=>{
return opts.createContext?.({
req: opts.req,
resHeaders,
...innerOpts
});
};
const url = new URL(opts.req.url);
const pathname = trimSlashes(url.pathname);
const endpoint = trimSlashes(opts.endpoint);
const path = trimSlashes(pathname.slice(endpoint.length));
const req = {
query: url.searchParams,
method: opts.req.method,
headers: Object.fromEntries(opts.req.headers),
body: opts.req.headers.get('content-type')?.startsWith('application/json') ? await opts.req.text() : ''
};
let resolve;
const promise = new Promise((r)=>resolve = r);
let status = 200;
let isStream = false;
let controller;
let encoder;
let formatter;
const unstable_onHead = (head, isStreaming)=>{
for (const [key, value] of Object.entries(head.headers ?? {})){
/* istanbul ignore if -- @preserve */ if (typeof value === 'undefined') {
continue;
}
if (typeof value === 'string') {
resHeaders.set(key, value);
continue;
}
for (const v of value){
resHeaders.append(key, v);
}
}
status = head.status;
if (isStreaming) {
resHeaders.set('Transfer-Encoding', 'chunked');
resHeaders.append('Vary', 'trpc-batch-mode');
const stream = new ReadableStream({
start (c) {
controller = c;
}
});
const response = new Response(stream, {
status,
headers: resHeaders
});
resolve(response);
encoder = new TextEncoder();
formatter = http.getBatchStreamFormatter();
isStream = true;
}
};
const unstable_onChunk = ([index, string])=>{
if (index === -1) {
// full response, no streaming
const response = new Response(string || null, {
status,
headers: resHeaders
});
resolve(response);
} else {
controller.enqueue(encoder.encode(formatter(index, string)));
}
};
http.resolveHTTPResponse({
req,
createContext,
path,
router: opts.router,
batching: opts.batching,
responseMeta: opts.responseMeta,
onError (o) {
opts?.onError?.({
...o,
req: opts.req
});
},
unstable_onHead,
unstable_onChunk
}).then(()=>{
if (isStream) {
controller.enqueue(encoder.encode(formatter.end()));
controller.close();
}
}).catch(()=>{
if (isStream) {
controller.close();
}
});
return promise;
}
exports.fetchRequestHandler = fetchRequestHandler;
exports.fetchRequestHandler = fetchRequestHandler.fetchRequestHandler;
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,4 +10,4 @@ * ```ts

*/
import type { AnyRouter, inferRouterContext } from '../../@trpc-server';
import type { HTTPBaseHandlerOptions, TRPCRequestInfo } from '../../@trpc-server/http';
import type { AnyRouter, CreateContextCallback, inferRouterContext } from '../../@trpc/server';
import type { HTTPBaseHandlerOptions, TRPCRequestInfo } from '../../@trpc/server/http';
export type FetchCreateContextFnOptions = {

@@ -20,14 +19,11 @@ req: Request;

export type FetchCreateContextFn<TRouter extends AnyRouter> = (opts: FetchCreateContextFnOptions) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
export type FetchCreateContextOption<TRouter extends AnyRouter> = unknown extends inferRouterContext<TRouter> ? {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: FetchCreateContextFn<TRouter>;
} : {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: FetchCreateContextFn<TRouter>;
export type FetchCreateContextOption<TRouter extends AnyRouter> = CreateContextCallback<inferRouterContext<TRouter>, FetchCreateContextFn<TRouter>>;
export type FetchHandlerOptions<TRouter extends AnyRouter> = FetchCreateContextOption<TRouter> & HTTPBaseHandlerOptions<TRouter, Request> & {
req: Request;
endpoint: string;
};
export type FetchHandlerOptions<TRouter extends AnyRouter> = FetchCreateContextOption<TRouter> & HTTPBaseHandlerOptions<TRouter, Request>;
export type FetchHandlerRequestOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<TRouter, Request> & CreateContextCallback<inferRouterContext<TRouter>, FetchCreateContextFn<TRouter>> & {
req: Request;
endpoint: string;
};
//# sourceMappingURL=types.d.ts.map
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,7 +11,10 @@ * ```ts

import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
import type { AnyRouter } from '../@trpc-server';
import type { AnyRouter } from '../@trpc/server';
import type { NodeHTTPCreateContextFnOptions, NodeHTTPHandlerOptions } from './node-http';
export type CreateNextContextOptions = NodeHTTPCreateContextFnOptions<NextApiRequest, NextApiResponse>;
/**
* Preventing "TypeScript where it's tough not to get "The inferred type of 'xxxx' cannot be named without a reference to [...]"
*/
export type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
export declare function createNextApiHandler<TRouter extends AnyRouter>(opts: NodeHTTPHandlerOptions<TRouter, NextApiRequest, NextApiResponse>): NextApiHandler;
//# sourceMappingURL=next.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var utils = require('../unstable-core-do-not-import/utils.js');
var TRPCError = require('../unstable-core-do-not-import/error/TRPCError.js');
require('../vendor/unpromise/unpromise.js');
require('../unstable-core-do-not-import/stream/utils/disposable.js');
require('../unstable-core-do-not-import/rootConfig.js');
var nodeHTTPRequestHandler = require('./node-http/nodeHTTPRequestHandler.js');
var core = require('@trpc/core');
var nodeHTTPRequestHandler = require('../nodeHTTPRequestHandler-83441c73.js');
require('@trpc/core/http');
require('./node-http/content-type/json/index.js');
require('../contentType-d9d22104.js');
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -23,38 +21,28 @@ * ```ts

return async (req, res)=>{
function getPath() {
if (typeof req.query['trpc'] === 'string') {
return req.query['trpc'];
}
if (Array.isArray(req.query['trpc'])) {
return req.query['trpc'].join('/');
}
return null;
}
const path = getPath();
if (path === null) {
const error = core.getErrorShape({
config: opts.router._def._config,
error: new core.TRPCError({
let path = '';
await utils.run(async ()=>{
path = utils.run(()=>{
if (typeof req.query['trpc'] === 'string') {
return req.query['trpc'];
}
if (Array.isArray(req.query['trpc'])) {
return req.query['trpc'].join('/');
}
throw new TRPCError.TRPCError({
message: 'Query "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
code: 'INTERNAL_SERVER_ERROR'
}),
type: 'unknown',
ctx: undefined,
path: undefined,
input: undefined
});
});
res.statusCode = 500;
res.json({
id: -1,
error
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
...opts,
req,
res,
path
});
return;
}
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...opts,
}).catch(nodeHTTPRequestHandler.internal_exceptionHandler({
req,
res,
path
});
path,
...opts
}));
};

@@ -61,0 +49,0 @@ }

export * from './nodeHTTPRequestHandler';
export * from './types';
export * from './incomingMessageToRequest';
//# sourceMappingURL=index.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var nodeHTTPRequestHandler = require('./nodeHTTPRequestHandler.js');
var incomingMessageToRequest = require('./incomingMessageToRequest.js');
var nodeHTTPRequestHandler = require('../../nodeHTTPRequestHandler-83441c73.js');
require('@trpc/core/http');
require('./content-type/json/index.js');
require('../../contentType-d9d22104.js');
require('@trpc/core');
exports.internal_exceptionHandler = nodeHTTPRequestHandler.internal_exceptionHandler;
exports.nodeHTTPRequestHandler = nodeHTTPRequestHandler.nodeHTTPRequestHandler;
exports.createURL = incomingMessageToRequest.createURL;
exports.incomingMessageToRequest = incomingMessageToRequest.incomingMessageToRequest;
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,5 +10,12 @@ * ```ts

*/
import type { AnyRouter } from '../../@trpc-server';
import { type AnyRouter } from '../../@trpc/server';
import type { NodeHTTPRequest, NodeHTTPRequestHandlerOptions, NodeHTTPResponse } from './types';
/**
* @internal
*/
export declare function internal_exceptionHandler<TRouter extends AnyRouter, TRequest extends NodeHTTPRequest, TResponse extends NodeHTTPResponse>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>): (cause: unknown) => void;
/**
* @remark the promise never rejects
*/
export declare function nodeHTTPRequestHandler<TRouter extends AnyRouter, TRequest extends NodeHTTPRequest, TResponse extends NodeHTTPResponse>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>): Promise<void>;
//# sourceMappingURL=nodeHTTPRequestHandler.d.ts.map

@@ -1,6 +0,4 @@

/// <reference types="node" />
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,15 +10,21 @@ * ```ts

*/
import type { IncomingMessage, ServerResponse } from 'http';
import type { MaybePromise } from '@trpc/core';
import type { AnyRouter, inferRouterContext } from '../../@trpc-server';
import type { HTTPBaseHandlerOptions, TRPCRequestInfo } from '../../@trpc-server/http';
import type { NodeHTTPContentTypeHandler } from './internals/contentType';
interface ParsedQs {
[key: string]: ParsedQs | ParsedQs[] | string[] | string | undefined;
}
export type NodeHTTPRequest = IncomingMessage & {
query?: ParsedQs;
import type * as http from 'http';
import type * as http2 from 'http2';
import type { AnyRouter, CreateContextCallback, inferRouterContext } from '../../@trpc/server';
import type { HTTPBaseHandlerOptions, TRPCRequestInfo } from '../../@trpc/server/http';
import type { DistributiveOmit, MaybePromise } from '../../unstable-core-do-not-import';
export type NodeHTTPRequest = DistributiveOmit<http.IncomingMessage | http2.Http2ServerRequest, 'socket'> & {
/**
* Many adapters will add a `body` property to the incoming message and pre-parse the body
*/
body?: unknown;
/**
* Socket is not always available in all deployments, so we need to make it optional
* @see https://github.com/trpc/trpc/issues/6341
* The socket object provided in the request does not fully implement the expected Node.js Socket interface.
* @see https://github.com/trpc/trpc/pull/6358
*/
socket?: Partial<http.IncomingMessage['socket']> | Partial<http2.Http2ServerRequest['socket']>;
};
export type NodeHTTPResponse = ServerResponse & {
export type NodeHTTPResponse = DistributiveOmit<http.ServerResponse | http2.Http2ServerResponse, 'write'> & {
/**

@@ -35,14 +39,5 @@ * Force the partially-compressed response to be flushed to the client.

flush?: () => void;
write: (chunk: string | Uint8Array) => boolean;
};
export type NodeHTTPCreateContextOption<TRouter extends AnyRouter, TRequest, TResponse> = object extends inferRouterContext<TRouter> ? {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>;
} : {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>;
};
export type NodeHTTPCreateContextOption<TRouter extends AnyRouter, TRequest, TResponse> = CreateContextCallback<inferRouterContext<TRouter>, NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>>;
/**

@@ -69,11 +64,14 @@ * @internal

*/
middleware?: ConnectMiddleware;
middleware?: ConnectMiddleware<TRequest, TResponse>;
maxBodySize?: number;
experimental_contentTypeHandlers?: NodeHTTPContentTypeHandler<TRequest, TResponse>[];
};
export type NodeHTTPRequestHandlerOptions<TRouter extends AnyRouter, TRequest extends NodeHTTPRequest, TResponse extends NodeHTTPResponse> = {
export type NodeHTTPRequestHandlerOptions<TRouter extends AnyRouter, TRequest extends NodeHTTPRequest, TResponse extends NodeHTTPResponse> = NodeHTTPHandlerOptions<TRouter, TRequest, TResponse> & {
req: TRequest;
res: TResponse;
/**
* The tRPC path to handle requests for
* @example 'post.all'
*/
path: string;
} & NodeHTTPHandlerOptions<TRouter, TRequest, TResponse>;
};
export type NodeHTTPCreateContextFnOptions<TRequest, TResponse> = {

@@ -80,0 +78,0 @@ req: TRequest;

@@ -1,6 +0,4 @@

/// <reference types="node" />
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -13,8 +11,27 @@ * ```ts

import http from 'http';
import type { AnyRouter } from '../@trpc-server';
import type { NodeHTTPCreateContextFnOptions, NodeHTTPHandlerOptions } from './node-http';
export type CreateHTTPHandlerOptions<TRouter extends AnyRouter> = NodeHTTPHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;
import type * as http2 from 'http2';
import { type AnyRouter } from '../@trpc/server';
import type { NodeHTTPCreateContextFnOptions, NodeHTTPHandlerOptions, NodeHTTPRequest, NodeHTTPResponse } from './node-http';
type StandaloneHandlerOptions<TRouter extends AnyRouter, TRequest extends NodeHTTPRequest, TResponse extends NodeHTTPResponse> = NodeHTTPHandlerOptions<TRouter, TRequest, TResponse> & {
/**
* The base path to handle requests for.
* This will be sliced from the beginning of the request path
* (Do not miss including the trailing slash)
* @default '/'
* @example '/trpc/'
* @example '/trpc/api/'
*/
basePath?: string;
};
export type CreateHTTPHandlerOptions<TRouter extends AnyRouter> = StandaloneHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;
export type CreateHTTPContextOptions = NodeHTTPCreateContextFnOptions<http.IncomingMessage, http.ServerResponse>;
export declare function createHTTPHandler<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
/**
* @internal
*/
export declare function createHTTPHandler<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): http.RequestListener;
export declare function createHTTPServer<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
export type CreateHTTP2HandlerOptions<TRouter extends AnyRouter> = StandaloneHandlerOptions<TRouter, http2.Http2ServerRequest, http2.Http2ServerResponse>;
export type CreateHTTP2ContextOptions = NodeHTTPCreateContextFnOptions<http2.Http2ServerRequest, http2.Http2ServerResponse>;
export declare function createHTTP2Handler(opts: CreateHTTP2HandlerOptions<AnyRouter>): (req: http2.Http2ServerRequest, res: http2.Http2ServerResponse<http2.Http2ServerRequest>) => void;
export {};
//# sourceMappingURL=standalone.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var http = require('node:http');
var nodeHTTPRequestHandler = require('../nodeHTTPRequestHandler-83441c73.js');
require('@trpc/core/http');
require('./node-http/content-type/json/index.js');
require('../contentType-d9d22104.js');
require('@trpc/core');
var utils = require('../unstable-core-do-not-import/utils.js');
require('../vendor/unpromise/unpromise.js');
require('../unstable-core-do-not-import/stream/utils/disposable.js');
require('../unstable-core-do-not-import/rootConfig.js');
var nodeHTTPRequestHandler = require('./node-http/nodeHTTPRequestHandler.js');
var incomingMessageToRequest = require('./node-http/incomingMessageToRequest.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
function createHTTPHandler(opts) {
return async (req, res)=>{
// Get procedure path and remove the leading slash, `/procedure -> procedure`
// Use dummy hostname if one is not provided.
const path = new URL(req.url, 'http://127.0.0.1').pathname.slice(1);
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...opts,
function createHandler(opts) {
const basePath = opts.basePath ?? '/';
const sliceLength = basePath.length;
return (req, res)=>{
let path = '';
utils.run(async ()=>{
const url = incomingMessageToRequest.createURL(req);
// get procedure(s) path and remove the leading slash
path = url.pathname.slice(sliceLength);
await nodeHTTPRequestHandler.nodeHTTPRequestHandler({
...opts,
req,
res,
path
});
}).catch(nodeHTTPRequestHandler.internal_exceptionHandler({
req,
res,
path
});
path,
...opts
}));
};
}
/**
* @internal
*/ function createHTTPHandler(opts) {
return createHandler(opts);
}
function createHTTPServer(opts) {
const handler = createHTTPHandler(opts);
return http__default["default"].createServer((req, res)=>handler(req, res));
return http.createServer(createHTTPHandler(opts));
}
function createHTTP2Handler(opts) {
return createHandler(opts);
}
exports.createHTTP2Handler = createHTTP2Handler;
exports.createHTTPHandler = createHTTPHandler;
exports.createHTTPServer = createHTTPServer;

@@ -1,14 +0,11 @@

/// <reference types="node" />
/// <reference types="./types/global" />
/// <reference types="node" />
import type { IncomingMessage } from 'http';
import type { MaybePromise } from '@trpc/core';
import type { BaseHandlerOptions } from '@trpc/core/http';
import type ws from 'ws';
import type { AnyRouter, inferRouterContext } from '../@trpc-server';
import type { NodeHTTPCreateContextFnOptions } from './node-http';
import type { AnyRouter, CreateContextCallback, inferRouterContext } from '../@trpc/server';
import { type BaseHandlerOptions } from '../@trpc/server/http';
import { type MaybePromise } from '../unstable-core-do-not-import';
import { type NodeHTTPCreateContextFnOptions } from './node-http';
/**
* @public
*/
export type CreateWSSContextFnOptions = Omit<NodeHTTPCreateContextFnOptions<IncomingMessage, ws.WebSocket>, 'info'>;
export type CreateWSSContextFnOptions = NodeHTTPCreateContextFnOptions<IncomingMessage, ws.WebSocket>;
/**

@@ -18,19 +15,38 @@ * @public

export type CreateWSSContextFn<TRouter extends AnyRouter> = (opts: CreateWSSContextFnOptions) => MaybePromise<inferRouterContext<TRouter>>;
export type WSConnectionHandlerOptions<TRouter extends AnyRouter> = BaseHandlerOptions<TRouter, IncomingMessage> & CreateContextCallback<inferRouterContext<TRouter>, CreateWSSContextFn<TRouter>>;
/**
* Web socket server handler
*/
export type WSSHandlerOptions<TRouter extends AnyRouter> = BaseHandlerOptions<TRouter, IncomingMessage> & (object extends inferRouterContext<TRouter> ? {
export type WSSHandlerOptions<TRouter extends AnyRouter> = WSConnectionHandlerOptions<TRouter> & {
wss: ws.WebSocketServer;
prefix?: string;
keepAlive?: {
/**
* Enable heartbeat messages
* @default false
*/
enabled: boolean;
/**
* Heartbeat interval in milliseconds
* @default 30_000
*/
pingMs?: number;
/**
* Terminate the WebSocket if no pong is received after this many milliseconds
* @default 5_000
*/
pongWaitMs?: number;
};
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: CreateWSSContextFn<TRouter>;
} : {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: CreateWSSContextFn<TRouter>;
}) & {
wss: ws.WebSocketServer;
process?: NodeJS.Process;
* Disable responding to ping messages from the client
* **Not recommended** - this is mainly used for testing
* @default false
*/
dangerouslyDisablePong?: boolean;
};
export declare function getWSConnectionHandler<TRouter extends AnyRouter>(opts: WSSHandlerOptions<TRouter>): (client: ws.WebSocket, req: IncomingMessage) => Promise<void>;
/**
* Handle WebSocket keep-alive messages
*/
export declare function handleKeepAlive(client: ws.WebSocket, pingMs?: number, pongWaitMs?: number): void;
export declare function applyWSSHandler<TRouter extends AnyRouter>(opts: WSSHandlerOptions<TRouter>): {

@@ -37,0 +53,0 @@ broadcastReconnectNotification: () => void;

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var getErrorShape = require('../unstable-core-do-not-import/error/getErrorShape.js');
var TRPCError = require('../unstable-core-do-not-import/error/TRPCError.js');
var router = require('../unstable-core-do-not-import/router.js');
var utils = require('../unstable-core-do-not-import/utils.js');
var parseConnectionParams = require('../unstable-core-do-not-import/http/parseConnectionParams.js');
var parseTRPCMessage = require('../unstable-core-do-not-import/rpc/parseTRPCMessage.js');
var observable = require('../observable/observable.js');
var asyncIterable = require('../unstable-core-do-not-import/stream/utils/asyncIterable.js');
require('../unstable-core-do-not-import/stream/utils/disposable.js');
var unpromise = require('../vendor/unpromise/unpromise.js');
var tracked = require('../unstable-core-do-not-import/stream/tracked.js');
var transformer = require('../unstable-core-do-not-import/transformer.js');
require('../unstable-core-do-not-import/rootConfig.js');
var incomingMessageToRequest = require('./node-http/incomingMessageToRequest.js');
var observable = require('@trpc/core/observable');
var rpc = require('@trpc/core/rpc');
var core = require('@trpc/core');
function _ts_add_disposable_resource(env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
{
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() {
try {
inner.call(this);
} catch (e) {
return Promise.reject(e);
}
};
env.stack.push({
value: value,
dispose: dispose,
async: async
});
} else {
env.stack.push({
async: true
});
}
return value;
}
function _ts_dispose_resources(env) {
var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
return (_ts_dispose_resources = function _ts_dispose_resources(env) {
function fail(e) {
env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while(r = env.stack.pop()){
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
fail(e);
return next();
});
} else s |= 1;
} catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
})(env);
}
/**
* Importing ws causes a build error
* @link https://github.com/trpc/trpc/pull/5279
* @see https://github.com/trpc/trpc/pull/5279
*/ const WEBSOCKET_OPEN = 1; /* ws.WebSocket.OPEN */
function applyWSSHandler(opts) {
const { wss , createContext , router } = opts;
const { transformer } = router._def._config;
wss.on('connection', async (client, req)=>{
const unsetContextPromiseSymbol = Symbol('unsetContextPromise');
function getWSConnectionHandler(opts) {
const { createContext, router: router$1 } = opts;
const { transformer: transformer$1 } = router$1._def._config;
return async (client, req)=>{
const clientSubscriptions = new Map();
const abortController = new AbortController();
if (opts.keepAlive?.enabled) {
const { pingMs, pongWaitMs } = opts.keepAlive;
handleKeepAlive(client, pingMs, pongWaitMs);
}
function respond(untransformedJSON) {
client.send(JSON.stringify(core.transformTRPCResponse(router._def._config, untransformedJSON)));
client.send(JSON.stringify(transformer.transformTRPCResponse(router$1._def._config, untransformedJSON)));
}
function stopSubscription(subscription, { id , jsonrpc }) {
subscription.unsubscribe();
respond({
id,
jsonrpc,
result: {
type: 'stopped'
}
function createCtxPromise(getConnectionParams) {
return utils.run(async ()=>{
ctx = await createContext?.({
req,
res: client,
info: {
connectionParams: getConnectionParams(),
calls: [],
isBatchCall: false,
accept: null,
type: 'unknown',
signal: abortController.signal,
url: null
}
});
return ctx;
}).catch((cause)=>{
const error = TRPCError.getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: 'unknown',
ctx,
req,
input: undefined
});
respond({
id: null,
error: getErrorShape.getErrorShape({
config: router$1._def._config,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx
})
});
// close in next tick
(globalThis.setImmediate ?? globalThis.setTimeout)(()=>{
client.close();
});
throw error;
});
}
const ctxPromise = createContext?.({
req,
res: client
});
let ctx = undefined;
/**
* promise for initializing the context
*
* - the context promise will be created immediately on connection if no connectionParams are expected
* - if connection params are expected, they will be created once received
*/ let ctxPromise = incomingMessageToRequest.createURL(req).searchParams.get('connectionParams') === '1' ? unsetContextPromiseSymbol : createCtxPromise(()=>null);
async function handleRequest(msg) {
const { id , jsonrpc } = msg;
const { id, jsonrpc } = msg;
/* istanbul ignore next -- @preserve */ if (id === null) {
throw new core.TRPCError({
throw new TRPCError.TRPCError({
code: 'BAD_REQUEST',

@@ -45,31 +161,39 @@ message: '`id` is required'

if (msg.method === 'subscription.stop') {
const sub = clientSubscriptions.get(id);
if (sub) {
stopSubscription(sub, {
id,
jsonrpc
});
}
clientSubscriptions.delete(id);
clientSubscriptions.get(id)?.abort();
return;
}
const { path , input } = msg.params;
const { path, lastEventId } = msg.params;
let { input } = msg.params;
const type = msg.method;
try {
if (lastEventId !== undefined) {
if (utils.isObject(input)) {
input = {
...input,
lastEventId: lastEventId
};
} else {
input ?? (input = {
lastEventId: lastEventId
});
}
}
await ctxPromise; // asserts context has been set
const result = await core.callProcedure({
procedures: router._def.procedures,
const abortController = new AbortController();
const result = await router.callProcedure({
router: router$1,
path,
getRawInput: async ()=>input,
ctx,
type
type,
signal: abortController.signal
});
if (type === 'subscription') {
if (!observable.isObservable(result)) {
throw new core.TRPCError({
message: `Subscription ${path} did not return an observable`,
code: 'INTERNAL_SERVER_ERROR'
const isIterableResult = utils.isAsyncIterable(result) || observable.isObservable(result);
if (type !== 'subscription') {
if (isIterableResult) {
throw new TRPCError.TRPCError({
code: 'UNSUPPORTED_MEDIA_TYPE',
message: `Cannot return an async iterable or observable from a ${type} procedure with WebSockets`
});
}
} else {
// send the value as data if the method is not a subscription

@@ -86,41 +210,97 @@ respond({

}
const observable$1 = result;
const sub1 = observable$1.subscribe({
next (data) {
respond({
id,
jsonrpc,
result: {
if (!isIterableResult) {
throw new TRPCError.TRPCError({
message: `Subscription ${path} did not return an observable or a AsyncGenerator`,
code: 'INTERNAL_SERVER_ERROR'
});
}
/* istanbul ignore next -- @preserve */ 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
return;
}
/* istanbul ignore next -- @preserve */ if (clientSubscriptions.has(id)) {
// duplicate request ids for client
throw new TRPCError.TRPCError({
message: `Duplicate id ${id}`,
code: 'BAD_REQUEST'
});
}
const iterable = observable.isObservable(result) ? observable.observableToAsyncIterable(result, abortController.signal) : result;
utils.run(async ()=>{
const env = {
stack: [],
error: void 0,
hasError: false
};
try {
const iterator = _ts_add_disposable_resource(env, asyncIterable.iteratorResource(iterable), true);
;
const abortPromise = new Promise((resolve)=>{
abortController.signal.onabort = ()=>resolve('abort');
});
// We need those declarations outside the loop for garbage collection reasons. If they
// were declared inside, they would not be freed until the next value is present.
let next;
let result;
while(true){
next = await unpromise.Unpromise.race([
iterator.next().catch(TRPCError.getTRPCErrorFromUnknown),
abortPromise
]);
if (next === 'abort') {
await iterator.return?.();
break;
}
if (next instanceof Error) {
const error = TRPCError.getTRPCErrorFromUnknown(next);
opts.onError?.({
error,
path,
type,
ctx,
req,
input
});
respond({
id,
jsonrpc,
error: getErrorShape.getErrorShape({
config: router$1._def._config,
error,
type,
path,
input,
ctx
})
});
break;
}
if (next.done) {
break;
}
result = {
type: 'data',
data
data: next.value
};
if (tracked.isTrackedEnvelope(next.value)) {
const [id, data] = next.value;
result.id = id;
result.data = {
id,
data
};
}
});
},
error (err) {
const error = core.getTRPCErrorFromUnknown(err);
opts.onError?.({
error,
path,
type,
ctx,
req,
input
});
respond({
id,
jsonrpc,
result
});
// free up references for garbage collection
next = null;
result = null;
}
respond({
id,
jsonrpc,
error: core.getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx
})
});
},
complete () {
respond({
id,
jsonrpc,
result: {

@@ -130,22 +310,35 @@ type: 'stopped'

});
clientSubscriptions.delete(id);
} catch (e) {
env.error = e;
env.hasError = true;
} finally{
const result = _ts_dispose_resources(env);
if (result) await result;
}
});
/* istanbul ignore next -- @preserve */ 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
sub1.unsubscribe();
return;
}
/* istanbul ignore next -- @preserve */ if (clientSubscriptions.has(id)) {
// duplicate request ids for client
stopSubscription(sub1, {
}).catch((cause)=>{
const error = TRPCError.getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path,
type,
ctx,
req,
input
});
respond({
id,
jsonrpc
jsonrpc,
error: getErrorShape.getErrorShape({
config: router$1._def._config,
error,
type,
path,
input,
ctx
})
});
throw new core.TRPCError({
message: `Duplicate id ${id}`,
code: 'BAD_REQUEST'
});
}
clientSubscriptions.set(id, sub1);
abortController.abort();
});
clientSubscriptions.set(id, abortController);
respond({

@@ -160,3 +353,3 @@ id,

// procedure threw an error
const error = core.getTRPCErrorFromUnknown(cause);
const error = TRPCError.getTRPCErrorFromUnknown(cause);
opts.onError?.({

@@ -173,4 +366,4 @@ error,

jsonrpc,
error: core.getErrorShape({
config: router._def._config,
error: getErrorShape.getErrorShape({
config: router$1._def._config,
error,

@@ -185,13 +378,44 @@ type,

}
client.on('message', async (message)=>{
client.on('message', async (rawData)=>{
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const msgStr = rawData.toString();
if (msgStr === 'PONG') {
return;
}
if (msgStr === 'PING') {
if (!opts.dangerouslyDisablePong) {
client.send('PONG');
}
return;
}
if (ctxPromise === unsetContextPromiseSymbol) {
// If the ctxPromise wasn't created immediately, we're expecting the first message to be a TRPCConnectionParamsMessage
ctxPromise = createCtxPromise(()=>{
let msg;
try {
msg = JSON.parse(msgStr);
if (!utils.isObject(msg)) {
throw new Error('Message was not an object');
}
} catch (cause) {
throw new TRPCError.TRPCError({
code: 'PARSE_ERROR',
message: `Malformed TRPCConnectionParamsMessage`,
cause
});
}
const connectionParams = parseConnectionParams.parseConnectionParamsFromUnknown(msg.data);
return connectionParams;
});
return;
}
try {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const msgJSON = JSON.parse(message.toString());
const msgJSON = JSON.parse(msgStr);
const msgs = Array.isArray(msgJSON) ? msgJSON : [
msgJSON
];
const promises = msgs.map((raw)=>rpc.parseTRPCMessage(raw, transformer)).map(handleRequest);
const promises = msgs.map((raw)=>parseTRPCMessage.parseTRPCMessage(raw, transformer$1)).map(handleRequest);
await Promise.all(promises);
} catch (cause) {
const error = new core.TRPCError({
const error = new TRPCError.TRPCError({
code: 'PARSE_ERROR',

@@ -202,4 +426,4 @@ cause

id: null,
error: core.getErrorShape({
config: router._def._config,
error: getErrorShape.getErrorShape({
config: router$1._def._config,
error,

@@ -221,3 +445,3 @@ type: 'unknown',

ctx,
error: core.getTRPCErrorFromUnknown(cause),
error: TRPCError.getTRPCErrorFromUnknown(cause),
input: undefined,

@@ -231,38 +455,62 @@ path: undefined,

for (const sub of clientSubscriptions.values()){
sub.unsubscribe();
sub.abort();
}
clientSubscriptions.clear();
abortController.abort();
});
async function createContextAsync() {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = core.getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: 'unknown',
ctx,
req,
input: undefined
});
respond({
id: null,
error: core.getErrorShape({
config: router._def._config,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx
})
});
// close in next tick
(global.setImmediate ?? global.setTimeout)(()=>{
client.close();
});
}
if (ctxPromise !== unsetContextPromiseSymbol) {
await ctxPromise;
}
await createContextAsync();
};
}
/**
* Handle WebSocket keep-alive messages
*/ function handleKeepAlive(client, pingMs = 30000, pongWaitMs = 5000) {
let timeout = undefined;
let ping = undefined;
const schedulePing = ()=>{
const scheduleTimeout = ()=>{
timeout = setTimeout(()=>{
client.terminate();
}, pongWaitMs);
};
ping = setTimeout(()=>{
client.send('PING');
scheduleTimeout();
}, pingMs);
};
const onMessage = ()=>{
clearTimeout(ping);
clearTimeout(timeout);
schedulePing();
};
client.on('message', onMessage);
client.on('close', ()=>{
clearTimeout(ping);
clearTimeout(timeout);
});
schedulePing();
}
function applyWSSHandler(opts) {
const onConnection = getWSConnectionHandler(opts);
opts.wss.on('connection', (client, req)=>{
if (opts.prefix && !req.url?.startsWith(opts.prefix)) {
return;
}
onConnection(client, req).catch((cause)=>{
opts.onError?.({
error: new TRPCError.TRPCError({
code: 'INTERNAL_SERVER_ERROR',
cause,
message: 'Failed to handle WebSocket connection'
}),
req: req,
path: undefined,
type: 'unknown',
ctx: undefined,
input: undefined
});
client.close();
});
});
return {

@@ -275,3 +523,3 @@ broadcastReconnectNotification: ()=>{

const data = JSON.stringify(response);
for (const client of wss.clients){
for (const client of opts.wss.clients){
if (client.readyState === WEBSOCKET_OPEN) {

@@ -286,1 +534,3 @@ client.send(data);

exports.applyWSSHandler = applyWSSHandler;
exports.getWSConnectionHandler = getWSConnectionHandler;
exports.handleKeepAlive = handleKeepAlive;

@@ -1,2 +0,2 @@

export * from './@trpc-server/http';
export * from './@trpc/server/http';
//# sourceMappingURL=http.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var getHTTPStatusCode = require('./unstable-core-do-not-import/http/getHTTPStatusCode.js');
var parseConnectionParams = require('./unstable-core-do-not-import/http/parseConnectionParams.js');
var contentTypeParsers = require('./unstable-core-do-not-import/http/contentTypeParsers.js');
var resolveResponse = require('./unstable-core-do-not-import/http/resolveResponse.js');
require('./unstable-core-do-not-import/rootConfig.js');
require('./vendor/unpromise/unpromise.js');
require('./unstable-core-do-not-import/stream/utils/disposable.js');
var http = require('@trpc/core/http');
Object.keys(http).forEach(function (k) {
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return http[k]; }
});
});
exports.getHTTPStatusCode = getHTTPStatusCode.getHTTPStatusCode;
exports.getHTTPStatusCodeFromError = getHTTPStatusCode.getHTTPStatusCodeFromError;
exports.parseConnectionParamsFromString = parseConnectionParams.parseConnectionParamsFromString;
exports.parseConnectionParamsFromUnknown = parseConnectionParams.parseConnectionParamsFromUnknown;
exports.octetInputParser = contentTypeParsers.octetInputParser;
exports.resolveResponse = resolveResponse.resolveResponse;

@@ -1,2 +0,2 @@

export * from './@trpc-server';
export * from './@trpc/server';
//# sourceMappingURL=index.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var createProxy = require('./unstable-core-do-not-import/createProxy.js');
var getErrorShape = require('./unstable-core-do-not-import/error/getErrorShape.js');
var TRPCError = require('./unstable-core-do-not-import/error/TRPCError.js');
var router = require('./unstable-core-do-not-import/router.js');
require('./vendor/unpromise/unpromise.js');
require('./unstable-core-do-not-import/stream/utils/disposable.js');
var tracked = require('./unstable-core-do-not-import/stream/tracked.js');
var transformer = require('./unstable-core-do-not-import/transformer.js');
var initTRPC = require('./unstable-core-do-not-import/initTRPC.js');
var middleware = require('./unstable-core-do-not-import/middleware.js');
var error = require('./vendor/standard-schema-v1/error.js');
require('./unstable-core-do-not-import/rootConfig.js');
var core = require('@trpc/core');
Object.defineProperty(exports, 'TRPCError', {
enumerable: true,
get: function () { return core.TRPCError; }
});
Object.defineProperty(exports, 'callProcedure', {
enumerable: true,
get: function () { return core.callProcedure; }
});
Object.defineProperty(exports, 'callTRPCProcedure', {
enumerable: true,
get: function () { return core.callProcedure; }
});
Object.defineProperty(exports, 'createTRPCFlatProxy', {
enumerable: true,
get: function () { return core.createFlatProxy; }
});
Object.defineProperty(exports, 'experimental_standaloneMiddleware', {
enumerable: true,
get: function () { return core.experimental_standaloneMiddleware; }
});
Object.defineProperty(exports, 'experimental_trpcMiddleware', {
enumerable: true,
get: function () { return core.experimental_standaloneMiddleware; }
});
Object.defineProperty(exports, 'getErrorShape', {
enumerable: true,
get: function () { return core.getErrorShape; }
});
Object.defineProperty(exports, 'getTRPCErrorFromUnknown', {
enumerable: true,
get: function () { return core.getTRPCErrorFromUnknown; }
});
Object.defineProperty(exports, 'initTRPC', {
enumerable: true,
get: function () { return core.initTRPC; }
});
Object.defineProperty(exports, 'transformTRPCResponse', {
enumerable: true,
get: function () { return core.transformTRPCResponse; }
});
exports.createTRPCFlatProxy = createProxy.createFlatProxy;
exports.getErrorShape = getErrorShape.getErrorShape;
exports.TRPCError = TRPCError.TRPCError;
exports.getTRPCErrorFromUnknown = TRPCError.getTRPCErrorFromUnknown;
exports.callTRPCProcedure = router.callProcedure;
exports.experimental_lazy = router.lazy;
exports.isTrackedEnvelope = tracked.isTrackedEnvelope;
exports.sse = tracked.sse;
exports.tracked = tracked.tracked;
exports.transformTRPCResponse = transformer.transformTRPCResponse;
exports.initTRPC = initTRPC.initTRPC;
exports.experimental_standaloneMiddleware = middleware.experimental_standaloneMiddleware;
exports.experimental_trpcMiddleware = middleware.experimental_standaloneMiddleware;
exports.StandardSchemaV1Error = error.StandardSchemaV1Error;

@@ -1,2 +0,2 @@

export * from '@trpc/core/rpc';
export * from './@trpc/server/rpc';
//# sourceMappingURL=rpc.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var codes = require('./unstable-core-do-not-import/rpc/codes.js');
var parseTRPCMessage = require('./unstable-core-do-not-import/rpc/parseTRPCMessage.js');
require('./vendor/unpromise/unpromise.js');
require('./unstable-core-do-not-import/stream/utils/disposable.js');
require('./unstable-core-do-not-import/rootConfig.js');
var rpc = require('@trpc/core/rpc');
Object.keys(rpc).forEach(function (k) {
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return rpc[k]; }
});
});
exports.TRPC_ERROR_CODES_BY_KEY = codes.TRPC_ERROR_CODES_BY_KEY;
exports.TRPC_ERROR_CODES_BY_NUMBER = codes.TRPC_ERROR_CODES_BY_NUMBER;
exports.parseTRPCMessage = parseTRPCMessage.parseTRPCMessage;

@@ -25,3 +25,3 @@ export {

*/
createTRPCFlatProxy as createFlatProxy, } from './@trpc-server';
createTRPCFlatProxy as createFlatProxy, } from './@trpc/server';
//# sourceMappingURL=shared.d.ts.map
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var createProxy = require('./unstable-core-do-not-import/createProxy.js');
var getErrorShape = require('./unstable-core-do-not-import/error/getErrorShape.js');
require('./vendor/unpromise/unpromise.js');
require('./unstable-core-do-not-import/stream/utils/disposable.js');
require('./unstable-core-do-not-import/rootConfig.js');
var core = require('@trpc/core');
Object.defineProperty(exports, 'createFlatProxy', {
enumerable: true,
get: function () { return core.createFlatProxy; }
});
Object.defineProperty(exports, 'getErrorShape', {
enumerable: true,
get: function () { return core.getErrorShape; }
});
exports.createFlatProxy = createProxy.createFlatProxy;
exports.getErrorShape = getErrorShape.getErrorShape;
{
"name": "@trpc/server",
"version": "11.0.0-alpha-tmp-opt-peers.204+febd6b2c2",
"version": "11.0.0-alpha-tmp-query-optoins-codemod.670+e01d94921",
"description": "The tRPC server library",

@@ -21,3 +21,3 @@ "author": "KATT",

"benchmark": "tsc --project tsconfig.benchmark.json",
"lint": "eslint --cache --ext \".js,.ts,.tsx\" --ignore-path ../../.gitignore src",
"lint": "eslint --cache src",
"ts-watch": "tsc --watch"

@@ -52,2 +52,7 @@ },

},
"./adapters/next-app-dir": {
"import": "./dist/adapters/next-app-dir.mjs",
"require": "./dist/adapters/next-app-dir.js",
"default": "./dist/adapters/next-app-dir.js"
},
"./adapters/next": {

@@ -58,12 +63,2 @@ "import": "./dist/adapters/next.mjs",

},
"./adapters/node-http/content-type/form-data": {
"import": "./dist/adapters/node-http/content-type/form-data/index.mjs",
"require": "./dist/adapters/node-http/content-type/form-data/index.js",
"default": "./dist/adapters/node-http/content-type/form-data/index.js"
},
"./adapters/node-http/content-type/json": {
"import": "./dist/adapters/node-http/content-type/json/index.mjs",
"require": "./dist/adapters/node-http/content-type/json/index.js",
"default": "./dist/adapters/node-http/content-type/json/index.js"
},
"./adapters/node-http": {

@@ -90,5 +85,5 @@ "import": "./dist/adapters/node-http/index.mjs",

"./observable": {
"import": "./dist/observable.mjs",
"require": "./dist/observable.js",
"default": "./dist/observable.js"
"import": "./dist/observable/index.mjs",
"require": "./dist/observable/index.js",
"default": "./dist/observable/index.js"
},

@@ -104,2 +99,7 @@ "./rpc": {

"default": "./dist/shared.js"
},
"./unstable-core-do-not-import": {
"import": "./dist/unstable-core-do-not-import.mjs",
"require": "./dist/unstable-core-do-not-import.js",
"default": "./dist/unstable-core-do-not-import.js"
}

@@ -117,3 +117,5 @@ },

"shared",
"!**/*.test.*"
"unstable-core-do-not-import",
"!**/*.test.*",
"!**/__tests__"
],

@@ -123,35 +125,28 @@ "publishConfig": {

},
"dependencies": {
"@trpc/core": "11.0.0-alpha-tmp-opt-peers.204+febd6b2c2"
},
"devDependencies": {
"@fastify/websocket": "^7.1.2",
"@tanstack/react-query": "^5.0.0",
"@types/aws-lambda": "^8.10.97",
"@types/express": "^4.17.17",
"@fastify/websocket": "^10.0.1",
"@tanstack/react-query": "^5.66.0",
"@types/aws-lambda": "^8.10.137",
"@types/express": "^5.0.0",
"@types/hash-sum": "^1.0.0",
"@types/node": "^20.10.0",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"@types/node": "^22.9.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/ws": "^8.2.0",
"@web3-storage/multipart-parser": "^1.0.0",
"aws-lambda": "^1.0.7",
"devalue": "^4.0.0",
"eslint": "^8.40.0",
"express": "^4.17.1",
"fastify": "^4.13.0",
"fastify-plugin": "^4.5.0",
"devalue": "^5.0.0",
"eslint": "^9.13.0",
"express": "^5.0.0",
"fastify": "^4.16.0",
"fastify-plugin": "^5.0.0",
"hash-sum": "^2.0.0",
"myzod": "^1.3.1",
"next": "^14.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "^2.79.1",
"next": "^15.1.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.24.4",
"superjson": "^1.12.4",
"superstruct": "^1.0.0",
"tslib": "^2.5.0",
"superstruct": "^2.0.0",
"tsx": "^4.0.0",
"typescript": "^5.3.3",
"valibot": "^0.26.0",
"vitest": "^0.32.0",
"typescript": "^5.7.2",
"valibot": "1.0.0-rc.0",
"ws": "^8.0.0",

@@ -164,3 +159,6 @@ "yup": "^1.0.0",

],
"gitHead": "febd6b2c2def8277bb12798d89e4060bcf26218a"
"peerDependencies": {
"typescript": ">=5.7.2"
},
"gitHead": "e01d949212c0240bbd94b7f967c41d26b4b41ed8"
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,104 +10,54 @@ * ```ts

*/
import type { Context as APIGWContext } from 'aws-lambda';
// @trpc/server
import type {
APIGatewayProxyEvent,
APIGatewayProxyEventV2,
APIGatewayProxyResult,
APIGatewayProxyStructuredResultV2,
Context as APIGWContext,
} from 'aws-lambda';
AnyRouter,
CreateContextCallback,
inferRouterContext,
} from '../../@trpc/server';
// @trpc/server
import type { AnyRouter } from '../../@trpc-server';
// @trpc/server
import { TRPCError } from '../../@trpc-server';
import type {
HTTPRequest,
HTTPResponse,
HTTPBaseHandlerOptions,
ResolveHTTPRequestOptionsContextFn,
} from '../../http';
import { resolveHTTPResponse } from '../../http';
import type {
APIGatewayEvent,
APIGatewayResult,
AWSLambdaOptions,
} from './utils';
import {
getHTTPMethod,
getPath,
isPayloadV1,
isPayloadV2,
transformHeaders,
UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE,
} from './utils';
TRPCRequestInfo,
} from '../../@trpc/server/http';
import { resolveResponse } from '../../@trpc/server/http';
import type { inferAPIGWReturn, LambdaEvent } from './getPlanner';
import { getPlanner } from './getPlanner';
export * from './utils';
export type CreateAWSLambdaContextOptions<TEvent extends LambdaEvent> = {
event: TEvent;
context: APIGWContext;
info: TRPCRequestInfo;
};
function lambdaEventToHTTPRequest(event: APIGatewayEvent): HTTPRequest {
const query = new URLSearchParams();
for (const [key, value] of Object.entries(
event.queryStringParameters ?? {},
)) {
if (typeof value !== 'undefined') {
query.append(key, value);
}
}
export type AWSLambdaOptions<
TRouter extends AnyRouter,
TEvent extends LambdaEvent,
> = HTTPBaseHandlerOptions<TRouter, TEvent> &
CreateContextCallback<
inferRouterContext<AnyRouter>,
AWSLambdaCreateContextFn<TRouter, TEvent>
>;
let body: string | null | undefined;
if (event.body && event.isBase64Encoded) {
body = Buffer.from(event.body, 'base64').toString('utf8');
} else {
body = event.body;
}
export type AWSLambdaCreateContextFn<
TRouter extends AnyRouter,
TEvent extends LambdaEvent,
> = ({
event,
context,
info,
}: CreateAWSLambdaContextOptions<TEvent>) =>
| inferRouterContext<TRouter>
| Promise<inferRouterContext<TRouter>>;
return {
method: getHTTPMethod(event),
query: query,
headers: event.headers,
body: body,
};
}
function tRPCOutputToAPIGatewayOutput<
TEvent extends APIGatewayEvent,
TResult extends APIGatewayResult,
>(event: TEvent, response: HTTPResponse): TResult {
if (isPayloadV1(event)) {
const resp: APIGatewayProxyResult = {
statusCode: response.status,
body: response.body ?? '',
headers: transformHeaders(response.headers ?? {}),
};
return resp as TResult;
} else if (isPayloadV2(event)) {
const resp: APIGatewayProxyStructuredResultV2 = {
statusCode: response.status,
body: response.body ?? undefined,
headers: transformHeaders(response.headers ?? {}),
};
return resp as TResult;
} else {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE,
});
}
}
/** 1:1 mapping of v1 or v2 input events, deduces which is which.
* @internal
**/
type inferAPIGWReturn<TType> = TType extends APIGatewayProxyEvent
? APIGatewayProxyResult
: TType extends APIGatewayProxyEventV2
? APIGatewayProxyStructuredResultV2
: never;
export function awsLambdaRequestHandler<
TRouter extends AnyRouter,
TEvent extends APIGatewayEvent,
TResult extends inferAPIGWReturn<TEvent>,
TEvent extends LambdaEvent,
>(
opts: AWSLambdaOptions<TRouter, TEvent>,
): (event: TEvent, context: APIGWContext) => Promise<TResult> {
): (event: TEvent, context: APIGWContext) => Promise<inferAPIGWReturn<TEvent>> {
return async (event, context) => {
const req = lambdaEventToHTTPRequest(event);
const path = getPath(event);
const planner = getPlanner(event);
const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (

@@ -120,9 +69,7 @@ innerOpts,

const response = await resolveHTTPResponse({
router: opts.router,
batching: opts.batching,
responseMeta: opts?.responseMeta,
const response = await resolveResponse({
...opts,
createContext,
req,
path,
req: planner.request,
path: planner.path,
error: null,

@@ -137,4 +84,4 @@ onError(o) {

return tRPCOutputToAPIGatewayOutput<TEvent, TResult>(event, response);
return await planner.toResult(response);
};
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,4 +11,5 @@ * ```ts

import type * as express from 'express';
// @trpc/server
import type { AnyRouter } from '../@trpc-server';
import type { AnyRouter } from '../@trpc/server';
// eslint-disable-next-line no-restricted-imports
import { run } from '../unstable-core-do-not-import';
import type {

@@ -19,3 +19,3 @@ NodeHTTPCreateContextFnOptions,

} from './node-http';
import { nodeHTTPRequestHandler } from './node-http';
import { internal_exceptionHandler, nodeHTTPRequestHandler } from './node-http';

@@ -30,17 +30,22 @@ export type CreateExpressContextOptions = NodeHTTPCreateContextFnOptions<

): express.Handler {
return async (req, res) => {
const endpoint = req.path.slice(1);
return (req, res) => {
let path = '';
run(async () => {
path = req.path.slice(req.path.lastIndexOf('/') + 1);
await nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...(opts as NodeHTTPHandlerOptions<
AnyRouter,
express.Request,
express.Response
>),
req,
res,
path: endpoint,
});
await nodeHTTPRequestHandler({
...(opts as any),
req,
res,
path,
});
}).catch(
internal_exceptionHandler({
req,
res,
path,
...opts,
}),
);
};
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,15 +10,17 @@ * ```ts

*/
import { Readable } from 'node:stream';
import type { FastifyReply, FastifyRequest } from 'fastify';
// @trpc/server
import type { AnyRouter } from '../../@trpc-server';
import type {
HTTPBaseHandlerOptions,
HTTPRequest,
HTTPResponse,
ResolveHTTPRequestOptionsContextFn,
ResponseChunk,
} from '../../http';
import { getBatchStreamFormatter, resolveHTTPResponse } from '../../http';
import type { NodeHTTPCreateContextOption } from '../node-http';
import type { AnyRouter } from '../../@trpc/server';
// @trpc/server/http
import {
resolveResponse,
type HTTPBaseHandlerOptions,
type ResolveHTTPRequestOptionsContextFn,
} from '../../@trpc/server/http';
// @trpc/server/node-http
import type { NodeHTTPRequest } from '../node-http';
import {
incomingMessageToRequest,
type NodeHTTPCreateContextOption,
} from '../node-http';

@@ -57,81 +58,26 @@ export type FastifyHandlerOptions<

const query = opts.req.query
? new URLSearchParams(opts.req.query as any)
: new URLSearchParams(opts.req.url.split('?')[1]);
const incomingMessage: NodeHTTPRequest = opts.req.raw;
const req: HTTPRequest = {
query,
method: opts.req.method,
headers: opts.req.headers,
body: opts.req.body ?? 'null',
};
// monkey-path body to the IncomingMessage
if ('body' in opts.req) {
incomingMessage.body = opts.req.body;
}
const req = incomingMessageToRequest(incomingMessage, opts.res.raw, {
maxBodySize: null,
});
let resolve: (value: FastifyReply) => void;
const promise = new Promise<FastifyReply>((r) => (resolve = r));
let isStream = false;
let stream: Readable;
let formatter: ReturnType<typeof getBatchStreamFormatter>;
const unstable_onHead = (head: HTTPResponse, isStreaming: boolean) => {
if (!opts.res.statusCode || opts.res.statusCode === 200) {
opts.res.statusCode = head.status;
}
for (const [key, value] of Object.entries(head.headers ?? {})) {
/* istanbul ignore if -- @preserve */
if (typeof value === 'undefined') {
continue;
}
void opts.res.header(key, value);
}
if (isStreaming) {
void opts.res.header('Transfer-Encoding', 'chunked');
void opts.res.header(
'Vary',
opts.res.hasHeader('Vary')
? 'trpc-batch-mode, ' + opts.res.getHeader('Vary')
: 'trpc-batch-mode',
);
stream = new Readable();
stream._read = () => {}; // eslint-disable-line @typescript-eslint/no-empty-function -- https://github.com/fastify/fastify/issues/805#issuecomment-369172154
resolve(opts.res.send(stream));
isStream = true;
formatter = getBatchStreamFormatter();
}
};
const unstable_onChunk = ([index, string]: ResponseChunk) => {
if (index === -1) {
// full response, no streaming
resolve(opts.res.send(string));
} else {
stream.push(formatter(index, string));
}
};
resolveHTTPResponse({
const res = await resolveResponse({
...opts,
req,
error: null,
createContext,
path: opts.path,
router: opts.router,
batching: opts.batching,
responseMeta: opts.responseMeta,
onError(o) {
opts?.onError?.({ ...o, req: opts.req });
opts?.onError?.({
...o,
req: opts.req,
});
},
unstable_onHead,
unstable_onChunk,
})
.then(() => {
if (isStream) {
stream.push(formatter.end());
stream.push(null); // https://github.com/fastify/fastify/issues/805#issuecomment-369172154
}
})
.catch(() => {
if (isStream) {
stream.push(null);
}
});
});
return promise;
await opts.res.send(res);
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -13,8 +12,13 @@ * ```ts

import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
import type { FastifyHandlerOptions } from '.';
// @trpc/server
import type { AnyRouter } from '../../@trpc-server';
import type { AnyRouter } from '../../@trpc/server';
// @trpc/server/http
import type { NodeHTTPCreateContextFnOptions } from '../node-http';
import type { WSSHandlerOptions } from '../ws';
import { applyWSSHandler } from '../ws';
// @trpc/server/ws
import {
getWSConnectionHandler,
handleKeepAlive,
type WSSHandlerOptions,
} from '../ws';
import type { FastifyHandlerOptions } from './fastifyRequestHandler';
import { fastifyRequestHandler } from './fastifyRequestHandler';

@@ -61,8 +65,16 @@

if (opts.useWSS) {
applyWSSHandler<TRouter>({
...(opts.trpcOptions as unknown as WSSHandlerOptions<TRouter>),
wss: fastify.websocketServer,
const trpcOptions =
opts.trpcOptions as unknown as WSSHandlerOptions<TRouter>;
const onConnection = getWSConnectionHandler<TRouter>({
...trpcOptions,
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
fastify.get(prefix ?? '/', { websocket: true }, () => {});
fastify.get(prefix ?? '/', { websocket: true }, async (socket, req) => {
await onConnection(socket, req.raw);
if (trpcOptions?.keepAlive?.enabled) {
const { pingMs, pongWaitMs } = trpcOptions.keepAlive;
handleKeepAlive(socket, pingMs, pongWaitMs);
}
});
}

@@ -69,0 +81,0 @@

/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,17 +11,7 @@ * ```ts

// @trpc/server
import type { AnyRouter } from '../../@trpc-server';
import type {
HTTPRequest,
HTTPResponse,
ResolveHTTPRequestOptionsContextFn,
ResponseChunk,
} from '../../http';
import { getBatchStreamFormatter, resolveHTTPResponse } from '../../http';
import type { FetchHandlerOptions } from './types';
export type FetchHandlerRequestOptions<TRouter extends AnyRouter> =
FetchHandlerOptions<TRouter> & {
req: Request;
endpoint: string;
};
import type { AnyRouter } from '../../@trpc/server';
import type { ResolveHTTPRequestOptionsContextFn } from '../../@trpc/server/http';
import { resolveResponse } from '../../@trpc/server/http';
import type { FetchHandlerRequestOptions } from './types';

@@ -53,92 +42,41 @@ const trimSlashes = (path: string): string => {

const req: HTTPRequest = {
query: url.searchParams,
method: opts.req.method,
headers: Object.fromEntries(opts.req.headers),
body: opts.req.headers.get('content-type')?.startsWith('application/json')
? await opts.req.text()
: '',
};
let resolve: (value: Response) => void;
const promise = new Promise<Response>((r) => (resolve = r));
let status = 200;
let isStream = false;
let controller: ReadableStreamController<any>;
let encoder: TextEncoder;
let formatter: ReturnType<typeof getBatchStreamFormatter>;
const unstable_onHead = (head: HTTPResponse, isStreaming: boolean) => {
for (const [key, value] of Object.entries(head.headers ?? {})) {
/* istanbul ignore if -- @preserve */
if (typeof value === 'undefined') {
continue;
}
if (typeof value === 'string') {
resHeaders.set(key, value);
continue;
}
for (const v of value) {
resHeaders.append(key, v);
}
}
status = head.status;
if (isStreaming) {
resHeaders.set('Transfer-Encoding', 'chunked');
resHeaders.append('Vary', 'trpc-batch-mode');
const stream = new ReadableStream({
start(c) {
controller = c;
},
});
const response = new Response(stream, {
status,
headers: resHeaders,
});
resolve(response);
encoder = new TextEncoder();
formatter = getBatchStreamFormatter();
isStream = true;
}
};
const unstable_onChunk = ([index, string]: ResponseChunk) => {
if (index === -1) {
// full response, no streaming
const response = new Response(string || null, {
status,
headers: resHeaders,
});
resolve(response);
} else {
controller.enqueue(encoder.encode(formatter(index, string)));
}
};
resolveHTTPResponse({
req,
return await resolveResponse({
...opts,
req: opts.req,
createContext,
path,
router: opts.router,
batching: opts.batching,
responseMeta: opts.responseMeta,
error: null,
onError(o) {
opts?.onError?.({ ...o, req: opts.req });
},
unstable_onHead,
unstable_onChunk,
})
.then(() => {
if (isStream) {
controller.enqueue(encoder.encode(formatter.end()));
controller.close();
responseMeta(data) {
const meta = opts.responseMeta?.(data);
if (meta?.headers) {
if (meta.headers instanceof Headers) {
for (const [key, value] of meta.headers.entries()) {
resHeaders.append(key, value);
}
} else {
/**
* @deprecated, delete in v12
*/
for (const [key, value] of Object.entries(meta.headers)) {
if (Array.isArray(value)) {
for (const v of value) {
resHeaders.append(key, v);
}
} else if (typeof value === 'string') {
resHeaders.set(key, value);
}
}
}
}
})
.catch(() => {
if (isStream) {
controller.close();
}
});
return promise;
return {
headers: resHeaders,
status: meta?.status,
};
},
});
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -12,3 +11,7 @@ * ```ts

// @trpc/server
import type { AnyRouter, inferRouterContext } from '../../@trpc-server';
import type {
AnyRouter,
CreateContextCallback,
inferRouterContext,
} from '../../@trpc/server';
// @trpc/server/http

@@ -18,3 +21,3 @@ import type {

TRPCRequestInfo,
} from '../../@trpc-server/http';
} from '../../@trpc/server/http';

@@ -32,17 +35,22 @@ export type FetchCreateContextFnOptions = {

export type FetchCreateContextOption<TRouter extends AnyRouter> =
unknown extends inferRouterContext<TRouter>
? {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: FetchCreateContextFn<TRouter>;
}
: {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: FetchCreateContextFn<TRouter>;
};
CreateContextCallback<
inferRouterContext<TRouter>,
FetchCreateContextFn<TRouter>
>;
export type FetchHandlerOptions<TRouter extends AnyRouter> =
FetchCreateContextOption<TRouter> & HTTPBaseHandlerOptions<TRouter, Request>;
FetchCreateContextOption<TRouter> &
HTTPBaseHandlerOptions<TRouter, Request> & {
req: Request;
endpoint: string;
};
export type FetchHandlerRequestOptions<TRouter extends AnyRouter> =
HTTPBaseHandlerOptions<TRouter, Request> &
CreateContextCallback<
inferRouterContext<TRouter>,
FetchCreateContextFn<TRouter>
> & {
req: Request;
endpoint: string;
};
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -13,5 +12,7 @@ * ```ts

// @trpc/server
import type { AnyRouter } from '../@trpc-server';
import type { AnyRouter } from '../@trpc/server';
// @trpc/server
import { getErrorShape, TRPCError } from '../@trpc-server';
import { TRPCError } from '../@trpc/server';
// eslint-disable-next-line no-restricted-imports
import { run } from '../unstable-core-do-not-import';
import type {

@@ -21,3 +22,3 @@ NodeHTTPCreateContextFnOptions,

} from './node-http';
import { nodeHTTPRequestHandler } from './node-http';
import { internal_exceptionHandler, nodeHTTPRequestHandler } from './node-http';

@@ -28,2 +29,6 @@ export type CreateNextContextOptions = NodeHTTPCreateContextFnOptions<

>;
/**
* Preventing "TypeScript where it's tough not to get "The inferred type of 'xxxx' cannot be named without a reference to [...]"
*/
export type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';

@@ -35,46 +40,34 @@

return async (req, res) => {
function getPath(): string | null {
if (typeof req.query['trpc'] === 'string') {
return req.query['trpc'];
}
if (Array.isArray(req.query['trpc'])) {
return req.query['trpc'].join('/');
}
return null;
}
const path = getPath();
let path = '';
if (path === null) {
const error = getErrorShape({
config: opts.router._def._config,
error: new TRPCError({
await run(async () => {
path = run(() => {
if (typeof req.query['trpc'] === 'string') {
return req.query['trpc'];
}
if (Array.isArray(req.query['trpc'])) {
return req.query['trpc'].join('/');
}
throw new TRPCError({
message:
'Query "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
code: 'INTERNAL_SERVER_ERROR',
}),
type: 'unknown',
ctx: undefined,
path: undefined,
input: undefined,
});
});
res.statusCode = 500;
res.json({
id: -1,
error,
await nodeHTTPRequestHandler({
...(opts as any),
req,
res,
path,
});
return;
}
await nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...(opts as NodeHTTPHandlerOptions<
AnyRouter,
NextApiRequest,
NextApiResponse
>),
req,
res,
path,
});
}).catch(
internal_exceptionHandler({
req,
res,
path,
...opts,
}),
);
};
}
export * from './nodeHTTPRequestHandler';
export * from './types';
export * from './incomingMessageToRequest';
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,15 +10,16 @@ * ```ts

*/
/* eslint-disable @typescript-eslint/no-non-null-assertion */
// @trpc/server
import type { AnyRouter } from '../../@trpc-server';
import {
getTRPCErrorFromUnknown,
transformTRPCResponse,
type AnyRouter,
} from '../../@trpc/server';
import type { ResolveHTTPRequestOptionsContextFn } from '../../@trpc/server/http';
import { resolveResponse } from '../../@trpc/server/http';
// eslint-disable-next-line no-restricted-imports
import { getErrorShape, run } from '../../unstable-core-do-not-import';
import { incomingMessageToRequest } from './incomingMessageToRequest';
import type {
HTTPRequest,
HTTPResponse,
ResolveHTTPRequestOptionsContextFn,
ResponseChunk,
} from '../../http';
import { getBatchStreamFormatter, resolveHTTPResponse } from '../../http';
import { nodeHTTPJSONContentTypeHandler } from './content-type/json';
import type { NodeHTTPContentTypeHandler } from './internals/contentType';
import type {
NodeHTTPRequest,

@@ -29,6 +29,8 @@ NodeHTTPRequestHandlerOptions,

} from './types';
import { writeResponse } from './writeResponse';
const defaultJSONContentTypeHandler = nodeHTTPJSONContentTypeHandler();
export async function nodeHTTPRequestHandler<
/**
* @internal
*/
export function internal_exceptionHandler<
TRouter extends AnyRouter,

@@ -38,125 +40,85 @@ TRequest extends NodeHTTPRequest,

>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {
const handleViaMiddleware = opts.middleware ?? ((_req, _res, next) => next());
return (cause: unknown) => {
const { res, req } = opts;
const error = getTRPCErrorFromUnknown(cause);
return handleViaMiddleware(opts.req, opts.res, async (err) => {
if (err) throw err;
const shape = getErrorShape({
config: opts.router._def._config,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx: undefined,
});
//
// Build tRPC dependencies
opts.onError?.({
req,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx: undefined,
});
const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (
innerOpts,
) => {
return await opts.createContext?.({
...opts,
...innerOpts,
});
};
const transformed = transformTRPCResponse(opts.router._def._config, {
error: shape,
});
const query = opts.req.query
? new URLSearchParams(opts.req.query as any)
: new URLSearchParams(opts.req.url!.split('?')[1]);
res.statusCode = shape.data.httpStatus;
res.end(JSON.stringify(transformed));
};
}
const jsonContentTypeHandler =
defaultJSONContentTypeHandler as unknown as NodeHTTPContentTypeHandler<
TRequest,
TResponse
>;
/**
* @remark the promise never rejects
*/
export async function nodeHTTPRequestHandler<
TRouter extends AnyRouter,
TRequest extends NodeHTTPRequest,
TResponse extends NodeHTTPResponse,
>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {
return new Promise<void>((resolve) => {
const handleViaMiddleware =
opts.middleware ?? ((_req, _res, next) => next());
const contentTypeHandlers = opts.experimental_contentTypeHandlers ?? [
jsonContentTypeHandler,
];
const contentTypeHandler =
contentTypeHandlers.find((handler) =>
handler.isMatch({
...opts,
query,
}),
) ??
// fallback to json
jsonContentTypeHandler;
const bodyResult = await contentTypeHandler.getBody({
...opts,
query,
opts.res.once('finish', () => {
resolve();
});
return handleViaMiddleware(opts.req, opts.res, (err: unknown) => {
run(async () => {
const request = incomingMessageToRequest(opts.req, opts.res, {
maxBodySize: opts.maxBodySize ?? null,
});
const req: HTTPRequest = {
method: opts.req.method!,
headers: opts.req.headers,
query,
body: bodyResult.ok ? bodyResult.data : undefined,
};
// Build tRPC dependencies
const createContext: ResolveHTTPRequestOptionsContextFn<
TRouter
> = async (innerOpts) => {
return await opts.createContext?.({
...opts,
...innerOpts,
});
};
let isStream = false;
let formatter: ReturnType<typeof getBatchStreamFormatter>;
const unstable_onHead = (head: HTTPResponse, isStreaming: boolean) => {
if (
'status' in head &&
(!opts.res.statusCode || opts.res.statusCode === 200)
) {
opts.res.statusCode = head.status;
}
for (const [key, value] of Object.entries(head.headers ?? {})) {
/* istanbul ignore if -- @preserve */
if (typeof value === 'undefined') {
continue;
}
opts.res.setHeader(key, value);
}
if (isStreaming) {
opts.res.setHeader('Transfer-Encoding', 'chunked');
const vary = opts.res.getHeader('Vary');
opts.res.setHeader(
'Vary',
vary ? 'trpc-batch-mode, ' + vary : 'trpc-batch-mode',
);
isStream = true;
formatter = getBatchStreamFormatter();
opts.res.flushHeaders();
}
};
const response = await resolveResponse({
...opts,
req: request,
error: err ? getTRPCErrorFromUnknown(err) : null,
createContext,
onError(o) {
opts?.onError?.({
...o,
req: opts.req,
});
},
});
const unstable_onChunk = ([index, string]: ResponseChunk) => {
if (index === -1) {
/**
* Full response, no streaming. This can happen
* - if the response is an error
* - if response is empty (HEAD request)
*/
opts.res.end(string);
} else {
opts.res.write(formatter!(index, string));
opts.res.flush?.();
}
};
await resolveHTTPResponse({
batching: opts.batching,
responseMeta: opts.responseMeta,
path: opts.path,
createContext,
router: opts.router,
req,
error: bodyResult.ok ? null : bodyResult.error,
preprocessedBody: bodyResult.ok ? bodyResult.preprocessed : false,
onError(o) {
opts?.onError?.({
...o,
req: opts.req,
await writeResponse({
request,
response,
rawResponse: opts.res,
});
},
contentTypeHandler,
unstable_onHead,
unstable_onChunk,
}).catch(internal_exceptionHandler(opts));
});
if (isStream) {
opts.res.write(formatter!.end());
opts.res.end();
}
return opts.res;
});
}
/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,7 +10,10 @@ * ```ts

*/
import type { IncomingMessage, ServerResponse } from 'http';
// eslint-disable-next-line no-restricted-imports
import type { MaybePromise } from '@trpc/core';
import type * as http from 'http';
import type * as http2 from 'http2';
// @trpc/server
import type { AnyRouter, inferRouterContext } from '../../@trpc-server';
import type {
AnyRouter,
CreateContextCallback,
inferRouterContext,
} from '../../@trpc/server';
// @trpc/server/http

@@ -21,14 +23,32 @@ import type {

TRPCRequestInfo,
} from '../../@trpc-server/http';
import type { NodeHTTPContentTypeHandler } from './internals/contentType';
} from '../../@trpc/server/http';
// eslint-disable-next-line no-restricted-imports
import type {
DistributiveOmit,
MaybePromise,
} from '../../unstable-core-do-not-import';
interface ParsedQs {
[key: string]: ParsedQs | ParsedQs[] | string[] | string | undefined;
}
export type NodeHTTPRequest = IncomingMessage & {
query?: ParsedQs;
export type NodeHTTPRequest = DistributiveOmit<
http.IncomingMessage | http2.Http2ServerRequest,
'socket'
> & {
/**
* Many adapters will add a `body` property to the incoming message and pre-parse the body
*/
body?: unknown;
/**
* Socket is not always available in all deployments, so we need to make it optional
* @see https://github.com/trpc/trpc/issues/6341
* The socket object provided in the request does not fully implement the expected Node.js Socket interface.
* @see https://github.com/trpc/trpc/pull/6358
*/
socket?:
| Partial<http.IncomingMessage['socket']>
| Partial<http2.Http2ServerRequest['socket']>;
};
export type NodeHTTPResponse = ServerResponse & {
export type NodeHTTPResponse = DistributiveOmit<
http.ServerResponse | http2.Http2ServerResponse,
'write'
> & {
/**

@@ -43,4 +63,5 @@ * Force the partially-compressed response to be flushed to the client.

flush?: () => void;
write: (chunk: string | Uint8Array) => boolean;
};
export type NodeHTTPCreateContextOption<

@@ -50,15 +71,6 @@ TRouter extends AnyRouter,

TResponse,
> = object extends inferRouterContext<TRouter>
? {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>;
}
: {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>;
};
> = CreateContextCallback<
inferRouterContext<TRouter>,
NodeHTTPCreateContextFn<TRouter, TRequest, TResponse>
>;

@@ -95,8 +107,4 @@ /**

*/
middleware?: ConnectMiddleware;
middleware?: ConnectMiddleware<TRequest, TResponse>;
maxBodySize?: number;
experimental_contentTypeHandlers?: NodeHTTPContentTypeHandler<
TRequest,
TResponse
>[];
};

@@ -108,7 +116,11 @@

TResponse extends NodeHTTPResponse,
> = {
> = NodeHTTPHandlerOptions<TRouter, TRequest, TResponse> & {
req: TRequest;
res: TResponse;
/**
* The tRPC path to handle requests for
* @example 'post.all'
*/
path: string;
} & NodeHTTPHandlerOptions<TRouter, TRequest, TResponse>;
};

@@ -115,0 +127,0 @@ export type NodeHTTPCreateContextFnOptions<TRequest, TResponse> = {

/**
* If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`
*
* Do **not** import from `@trpc/core`
* @example

@@ -11,14 +10,41 @@ * ```ts

*/
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import http from 'http';
// --- http2 ---
import type * as http2 from 'http2';
// @trpc/server
import type { AnyRouter } from '../@trpc-server';
import { type AnyRouter } from '../@trpc/server';
// eslint-disable-next-line no-restricted-imports
import { run } from '../unstable-core-do-not-import';
import type {
NodeHTTPCreateContextFnOptions,
NodeHTTPHandlerOptions,
NodeHTTPRequest,
NodeHTTPResponse,
} from './node-http';
import { nodeHTTPRequestHandler } from './node-http';
import {
createURL,
internal_exceptionHandler,
nodeHTTPRequestHandler,
} from './node-http';
type StandaloneHandlerOptions<
TRouter extends AnyRouter,
TRequest extends NodeHTTPRequest,
TResponse extends NodeHTTPResponse,
> = NodeHTTPHandlerOptions<TRouter, TRequest, TResponse> & {
/**
* The base path to handle requests for.
* This will be sliced from the beginning of the request path
* (Do not miss including the trailing slash)
* @default '/'
* @example '/trpc/'
* @example '/trpc/api/'
*/
basePath?: string;
};
// --- http1 ---
export type CreateHTTPHandlerOptions<TRouter extends AnyRouter> =
NodeHTTPHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;
StandaloneHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;

@@ -30,25 +56,68 @@ export type CreateHTTPContextOptions = NodeHTTPCreateContextFnOptions<

export function createHTTPHandler<TRouter extends AnyRouter>(
opts: CreateHTTPHandlerOptions<TRouter>,
) {
return async (req: http.IncomingMessage, res: http.ServerResponse) => {
// Get procedure path and remove the leading slash, `/procedure -> procedure`
// Use dummy hostname if one is not provided.
const path = new URL(req.url!, 'http://127.0.0.1').pathname.slice(1);
function createHandler<
TRouter extends AnyRouter,
TRequest extends NodeHTTPRequest,
TResponse extends NodeHTTPResponse,
>(
opts: StandaloneHandlerOptions<TRouter, TRequest, TResponse>,
): (req: TRequest, res: TResponse) => void {
const basePath = opts.basePath ?? '/';
const sliceLength = basePath.length;
await nodeHTTPRequestHandler({
// FIXME: no typecasting should be needed here
...(opts as CreateHTTPHandlerOptions<AnyRouter>),
req,
res,
path,
});
return (req, res) => {
let path = '';
run(async () => {
const url = createURL(req);
// get procedure(s) path and remove the leading slash
path = url.pathname.slice(sliceLength);
await nodeHTTPRequestHandler({
...(opts as any),
req,
res,
path,
});
}).catch(
internal_exceptionHandler({
req,
res,
path,
...opts,
}),
);
};
}
/**
* @internal
*/
export function createHTTPHandler<TRouter extends AnyRouter>(
opts: CreateHTTPHandlerOptions<TRouter>,
): http.RequestListener {
return createHandler(opts);
}
export function createHTTPServer<TRouter extends AnyRouter>(
opts: CreateHTTPHandlerOptions<TRouter>,
) {
const handler = createHTTPHandler(opts);
return http.createServer((req, res) => handler(req, res));
return http.createServer(createHTTPHandler(opts));
}
// --- http2 ---
export type CreateHTTP2HandlerOptions<TRouter extends AnyRouter> =
StandaloneHandlerOptions<
TRouter,
http2.Http2ServerRequest,
http2.Http2ServerResponse
>;
export type CreateHTTP2ContextOptions = NodeHTTPCreateContextFnOptions<
http2.Http2ServerRequest,
http2.Http2ServerResponse
>;
export function createHTTP2Handler(opts: CreateHTTP2HandlerOptions<AnyRouter>) {
return createHandler(opts);
}
import type { IncomingMessage } from 'http';
// eslint-disable-next-line no-restricted-imports
import type { MaybePromise } from '@trpc/core';
import type { BaseHandlerOptions } from '@trpc/core/http';
import type { Unsubscribable } from '@trpc/core/observable';
import { isObservable } from '@trpc/core/observable';
import type ws from 'ws';
import type {
JSONRPC2,
TRPCClientOutgoingMessage,
TRPCReconnectNotification,
TRPCResponseMessage,
} from '@trpc/core/rpc';
import { parseTRPCMessage } from '@trpc/core/rpc';
import type ws from 'ws';
import type { AnyRouter, inferRouterContext } from '../@trpc-server';
AnyRouter,
CreateContextCallback,
inferRouterContext,
} from '../@trpc/server';
import {
callProcedure,
callTRPCProcedure,
getErrorShape,

@@ -22,8 +14,32 @@ getTRPCErrorFromUnknown,

TRPCError,
} from '../@trpc-server';
import type { NodeHTTPCreateContextFnOptions } from './node-http';
} from '../@trpc/server';
import type { TRPCRequestInfo } from '../@trpc/server/http';
import { type BaseHandlerOptions } from '../@trpc/server/http';
import { parseTRPCMessage } from '../@trpc/server/rpc';
// @trpc/server/rpc
import type {
TRPCClientOutgoingMessage,
TRPCConnectionParamsMessage,
TRPCReconnectNotification,
TRPCResponseMessage,
TRPCResultMessage,
} from '../@trpc/server/rpc';
import { parseConnectionParamsFromUnknown } from '../http';
import { isObservable, observableToAsyncIterable } from '../observable';
// eslint-disable-next-line no-restricted-imports
import {
isAsyncIterable,
isObject,
isTrackedEnvelope,
run,
type MaybePromise,
} from '../unstable-core-do-not-import';
// eslint-disable-next-line no-restricted-imports
import { iteratorResource } from '../unstable-core-do-not-import/stream/utils/asyncIterable';
import { Unpromise } from '../vendor/unpromise';
import { createURL, type NodeHTTPCreateContextFnOptions } from './node-http';
/**
* Importing ws causes a build error
* @link https://github.com/trpc/trpc/pull/5279
* @see https://github.com/trpc/trpc/pull/5279
*/

@@ -35,5 +51,5 @@ const WEBSOCKET_OPEN = 1; /* ws.WebSocket.OPEN */

*/
export type CreateWSSContextFnOptions = Omit<
NodeHTTPCreateContextFnOptions<IncomingMessage, ws.WebSocket>,
'info'
export type CreateWSSContextFnOptions = NodeHTTPCreateContextFnOptions<
IncomingMessage,
ws.WebSocket
>;

@@ -48,35 +64,57 @@

export type WSConnectionHandlerOptions<TRouter extends AnyRouter> =
BaseHandlerOptions<TRouter, IncomingMessage> &
CreateContextCallback<
inferRouterContext<TRouter>,
CreateWSSContextFn<TRouter>
>;
/**
* Web socket server handler
*/
export type WSSHandlerOptions<TRouter extends AnyRouter> = BaseHandlerOptions<
TRouter,
IncomingMessage
> &
(object extends inferRouterContext<TRouter>
? {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext?: CreateWSSContextFn<TRouter>;
}
: {
/**
* @link https://trpc.io/docs/v11/context
**/
createContext: CreateWSSContextFn<TRouter>;
}) & {
export type WSSHandlerOptions<TRouter extends AnyRouter> =
WSConnectionHandlerOptions<TRouter> & {
wss: ws.WebSocketServer;
process?: NodeJS.Process;
prefix?: string;
keepAlive?: {
/**
* Enable heartbeat messages
* @default false
*/
enabled: boolean;
/**
* Heartbeat interval in milliseconds
* @default 30_000
*/
pingMs?: number;
/**
* Terminate the WebSocket if no pong is received after this many milliseconds
* @default 5_000
*/
pongWaitMs?: number;
};
/**
* Disable responding to ping messages from the client
* **Not recommended** - this is mainly used for testing
* @default false
*/
dangerouslyDisablePong?: boolean;
};
export function applyWSSHandler<TRouter extends AnyRouter>(
const unsetContextPromiseSymbol = Symbol('unsetContextPromise');
export function getWSConnectionHandler<TRouter extends AnyRouter>(
opts: WSSHandlerOptions<TRouter>,
) {
const { wss, createContext, router } = opts;
const { createContext, router } = opts;
const { transformer } = router._def._config;
wss.on('connection', async (client, req) => {
const clientSubscriptions = new Map<number | string, Unsubscribable>();
return async (client: ws.WebSocket, req: IncomingMessage) => {
const clientSubscriptions = new Map<number | string, AbortController>();
const abortController = new AbortController();
if (opts.keepAlive?.enabled) {
const { pingMs, pongWaitMs } = opts.keepAlive;
handleKeepAlive(client, pingMs, pongWaitMs);
}
function respond(untransformedJSON: TRPCResponseMessage) {

@@ -90,22 +128,68 @@ client.send(

function stopSubscription(
subscription: Unsubscribable,
{ id, jsonrpc }: JSONRPC2.BaseEnvelope & { id: JSONRPC2.RequestId },
) {
subscription.unsubscribe();
function createCtxPromise(
getConnectionParams: () => TRPCRequestInfo['connectionParams'],
): Promise<inferRouterContext<TRouter>> {
return run(async () => {
ctx = await createContext?.({
req,
res: client,
info: {
connectionParams: getConnectionParams(),
calls: [],
isBatchCall: false,
accept: null,
type: 'unknown',
signal: abortController.signal,
url: null,
},
});
respond({
id,
jsonrpc,
result: {
type: 'stopped',
},
return ctx;
}).catch((cause) => {
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: 'unknown',
ctx,
req,
input: undefined,
});
respond({
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx,
}),
});
// close in next tick
(globalThis.setImmediate ?? globalThis.setTimeout)(() => {
client.close();
});
throw error;
});
}
const ctxPromise = createContext?.({ req, res: client });
let ctx: inferRouterContext<TRouter> | undefined = undefined;
/**
* promise for initializing the context
*
* - the context promise will be created immediately on connection if no connectionParams are expected
* - if connection params are expected, they will be created once received
*/
let ctxPromise =
createURL(req).searchParams.get('connectionParams') === '1'
? unsetContextPromiseSymbol
: createCtxPromise(() => null);
async function handleRequest(msg: TRPCClientOutgoingMessage) {
const { id, jsonrpc } = msg;
/* istanbul ignore next -- @preserve */

@@ -119,16 +203,26 @@ if (id === null) {

if (msg.method === 'subscription.stop') {
const sub = clientSubscriptions.get(id);
if (sub) {
stopSubscription(sub, { id, jsonrpc });
}
clientSubscriptions.delete(id);
clientSubscriptions.get(id)?.abort();
return;
}
const { path, input } = msg.params;
const { path, lastEventId } = msg.params;
let { input } = msg.params;
const type = msg.method;
try {
if (lastEventId !== undefined) {
if (isObject(input)) {
input = {
...input,
lastEventId: lastEventId,
};
} else {
input ??= {
lastEventId: lastEventId,
};
}
}
await ctxPromise; // asserts context has been set
const result = await callProcedure({
procedures: router._def.procedures,
const abortController = new AbortController();
const result = await callTRPCProcedure({
router,
path,

@@ -138,12 +232,15 @@ getRawInput: async () => input,

type,
signal: abortController.signal,
});
if (type === 'subscription') {
if (!isObservable(result)) {
const isIterableResult =
isAsyncIterable(result) || isObservable(result);
if (type !== 'subscription') {
if (isIterableResult) {
throw new TRPCError({
message: `Subscription ${path} did not return an observable`,
code: 'INTERNAL_SERVER_ERROR',
code: 'UNSUPPORTED_MEDIA_TYPE',
message: `Cannot return an async iterable or observable from a ${type} procedure with WebSockets`,
});
}
} else {
// send the value as data if the method is not a subscription

@@ -161,40 +258,9 @@ respond({

const observable = result;
const sub = observable.subscribe({
next(data) {
respond({
id,
jsonrpc,
result: {
type: 'data',
data,
},
});
},
error(err) {
const error = getTRPCErrorFromUnknown(err);
opts.onError?.({ error, path, type, ctx, req, input });
respond({
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
},
complete() {
respond({
id,
jsonrpc,
result: {
type: 'stopped',
},
});
},
});
if (!isIterableResult) {
throw new TRPCError({
message: `Subscription ${path} did not return an observable or a AsyncGenerator`,
code: 'INTERNAL_SERVER_ERROR',
});
}
/* istanbul ignore next -- @preserve */

@@ -204,3 +270,3 @@ if (client.readyState !== WEBSOCKET_OPEN) {

// no need to send stopped message if the client is disconnected
sub.unsubscribe();
return;

@@ -212,3 +278,3 @@ }

// duplicate request ids for client
stopSubscription(sub, { id, jsonrpc });
throw new TRPCError({

@@ -219,4 +285,106 @@ message: `Duplicate id ${id}`,

}
clientSubscriptions.set(id, sub);
const iterable = isObservable(result)
? observableToAsyncIterable(result, abortController.signal)
: result;
run(async () => {
await using iterator = iteratorResource(iterable);
const abortPromise = new Promise<'abort'>((resolve) => {
abortController.signal.onabort = () => resolve('abort');
});
// We need those declarations outside the loop for garbage collection reasons. If they
// were declared inside, they would not be freed until the next value is present.
let next:
| null
| TRPCError
| Awaited<
typeof abortPromise | ReturnType<(typeof iterator)['next']>
>;
let result: null | TRPCResultMessage<unknown>['result'];
while (true) {
next = await Unpromise.race([
iterator.next().catch(getTRPCErrorFromUnknown),
abortPromise,
]);
if (next === 'abort') {
await iterator.return?.();
break;
}
if (next instanceof Error) {
const error = getTRPCErrorFromUnknown(next);
opts.onError?.({ error, path, type, ctx, req, input });
respond({
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
break;
}
if (next.done) {
break;
}
result = {
type: 'data',
data: next.value,
};
if (isTrackedEnvelope(next.value)) {
const [id, data] = next.value;
result.id = id;
result.data = {
id,
data,
};
}
respond({
id,
jsonrpc,
result,
});
// free up references for garbage collection
next = null;
result = null;
}
respond({
id,
jsonrpc,
result: {
type: 'stopped',
},
});
clientSubscriptions.delete(id);
}).catch((cause) => {
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({ error, path, type, ctx, req, input });
respond({
id,
jsonrpc,
error: getErrorShape({
config: router._def._config,
error,
type,
path,
input,
ctx,
}),
});
abortController.abort();
});
clientSubscriptions.set(id, abortController);
respond({

@@ -247,6 +415,40 @@ id,

}
client.on('message', async (message) => {
client.on('message', async (rawData) => {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const msgStr = rawData.toString();
if (msgStr === 'PONG') {
return;
}
if (msgStr === 'PING') {
if (!opts.dangerouslyDisablePong) {
client.send('PONG');
}
return;
}
if (ctxPromise === unsetContextPromiseSymbol) {
// If the ctxPromise wasn't created immediately, we're expecting the first message to be a TRPCConnectionParamsMessage
ctxPromise = createCtxPromise(() => {
let msg;
try {
msg = JSON.parse(msgStr) as TRPCConnectionParamsMessage;
if (!isObject(msg)) {
throw new Error('Message was not an object');
}
} catch (cause) {
throw new TRPCError({
code: 'PARSE_ERROR',
message: `Malformed TRPCConnectionParamsMessage`,
cause,
});
}
const connectionParams = parseConnectionParamsFromUnknown(msg.data);
return connectionParams;
});
return;
}
try {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const msgJSON: unknown = JSON.parse(message.toString());
const msgJSON: unknown = JSON.parse(msgStr);
const msgs: unknown[] = Array.isArray(msgJSON) ? msgJSON : [msgJSON];

@@ -294,40 +496,82 @@ const promises = msgs

for (const sub of clientSubscriptions.values()) {
sub.unsubscribe();
sub.abort();
}
clientSubscriptions.clear();
abortController.abort();
});
async function createContextAsync() {
try {
ctx = await ctxPromise;
} catch (cause) {
const error = getTRPCErrorFromUnknown(cause);
opts.onError?.({
error,
path: undefined,
type: 'unknown',
ctx,
req,
input: undefined,
});
respond({
id: null,
error: getErrorShape({
config: router._def._config,
error,
type: 'unknown',
path: undefined,
input: undefined,
ctx,
}),
});
// close in next tick
(global.setImmediate ?? global.setTimeout)(() => {
client.close();
});
}
if (ctxPromise !== unsetContextPromiseSymbol) {
await ctxPromise;
}
await createContextAsync();
};
}
/**
* Handle WebSocket keep-alive messages
*/
export function handleKeepAlive(
client: ws.WebSocket,
pingMs = 30_000,
pongWaitMs = 5_000,
) {
let timeout: NodeJS.Timeout | undefined = undefined;
let ping: NodeJS.Timeout | undefined = undefined;
const schedulePing = () => {
const scheduleTimeout = () => {
timeout = setTimeout(() => {
client.terminate();
}, pongWaitMs);
};
ping = setTimeout(() => {
client.send('PING');
scheduleTimeout();
}, pingMs);
};
const onMessage = () => {
clearTimeout(ping);
clearTimeout(timeout);
schedulePing();
};
client.on('message', onMessage);
client.on('close', () => {
clearTimeout(ping);
clearTimeout(timeout);
});
schedulePing();
}
export function applyWSSHandler<TRouter extends AnyRouter>(
opts: WSSHandlerOptions<TRouter>,
) {
const onConnection = getWSConnectionHandler(opts);
opts.wss.on('connection', (client, req) => {
if (opts.prefix && !req.url?.startsWith(opts.prefix)) {
return;
}
onConnection(client, req).catch((cause) => {
opts.onError?.({
error: new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
cause,
message: 'Failed to handle WebSocket connection',
}),
req: req,
path: undefined,
type: 'unknown',
ctx: undefined,
input: undefined,
});
client.close();
});
});
return {

@@ -340,3 +584,3 @@ broadcastReconnectNotification: () => {

const data = JSON.stringify(response);
for (const client of wss.clients) {
for (const client of opts.wss.clients) {
if (client.readyState === WEBSOCKET_OPEN) {

@@ -343,0 +587,0 @@ client.send(data);

@@ -1,1 +0,1 @@

export * from './@trpc-server/http';
export * from './@trpc/server/http';

@@ -1,1 +0,1 @@

export * from './@trpc-server';
export * from './@trpc/server';

@@ -1,2 +0,1 @@

// Note: this should likely be moved to a sort of `@trpc/plugin` package
export * from '@trpc/core/rpc';
export * from './@trpc/server/rpc';

@@ -26,2 +26,2 @@ export {

createTRPCFlatProxy as createFlatProxy,
} from './@trpc-server';
} from './@trpc/server';

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

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

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

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

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

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

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