@trpc/server
Version:
1 lines • 18.8 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["cookiesString: string | string[]","cookiesStrings: string[]","pos: number","start: number","ch: string","lastComma: number","nextStart: number","cookiesSeparatorFound: boolean","event: LambdaEvent","response: Response","cookies: string[]","v1Processor: Processor<APIGatewayProxyEvent>","hostname: string","result: APIGatewayProxyResult","v2Processor: Processor<APIGatewayProxyEventV2>","result: APIGatewayProxyStructuredResultV2","event: TEvent","processor: Processor<TEvent>","init: RequestInit","opts: AWSLambdaOptions<TRouter, TEvent>","createContext: ResolveHTTPRequestOptionsContextFn<TRouter>"],"sources":["../../../src/vendor/cookie-es/set-cookie/split.ts","../../../src/adapters/aws-lambda/getPlanner.ts","../../../src/adapters/aws-lambda/index.ts"],"sourcesContent":["/**\n * Based on https://github.com/unjs/cookie-es/tree/v1.2.2\n * MIT License\n * \n * Cookie-es copyright (c) Pooya Parsa <pooya@pi0.io>\n * Set-Cookie parsing based on https://github.com/nfriedly/set-cookie-parser\n * Copyright (c) 2015 Nathan Friedly <nathan@nfriedly.com> (http://nfriedly.com/)\n * \n * @see https://github.com/unjs/cookie-es/blob/main/src/set-cookie/split.ts\n */\n\n/**\n * Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas\n * that are within a single set-cookie field-value, such as in the Expires portion.\n *\n * See https://tools.ietf.org/html/rfc2616#section-4.2\n */\nexport function splitSetCookieString(\n cookiesString: string | string[],\n ): string[] {\n if (Array.isArray(cookiesString)) {\n return cookiesString.flatMap((c) => splitSetCookieString(c));\n }\n \n if (typeof cookiesString !== \"string\") {\n return [];\n }\n \n const cookiesStrings: string[] = [];\n let pos: number = 0;\n let start: number;\n let ch: string;\n let lastComma: number;\n let nextStart: number;\n let cookiesSeparatorFound: boolean;\n \n const skipWhitespace = () => {\n while (pos < cookiesString.length && /\\s/.test(cookiesString.charAt(pos))) {\n pos += 1;\n }\n return pos < cookiesString.length;\n };\n \n const notSpecialChar = () => {\n ch = cookiesString.charAt(pos);\n return ch !== \"=\" && ch !== \";\" && ch !== \",\";\n };\n \n while (pos < cookiesString.length) {\n start = pos;\n cookiesSeparatorFound = false;\n \n while (skipWhitespace()) {\n ch = cookiesString.charAt(pos);\n if (ch === \",\") {\n // ',' is a cookie separator if we have later first '=', not ';' or ','\n lastComma = pos;\n pos += 1;\n \n skipWhitespace();\n nextStart = pos;\n \n while (pos < cookiesString.length && notSpecialChar()) {\n pos += 1;\n }\n \n // currently special character\n if (pos < cookiesString.length && cookiesString.charAt(pos) === \"=\") {\n // we found cookies separator\n cookiesSeparatorFound = true;\n // pos is inside the next cookie, so back up and return it.\n pos = nextStart;\n cookiesStrings.push(cookiesString.slice(start, lastComma));\n start = pos;\n } else {\n // in param ',' or param separator ';',\n // we continue from that comma\n pos = lastComma + 1;\n }\n } else {\n pos += 1;\n }\n }\n \n if (!cookiesSeparatorFound || pos >= cookiesString.length) {\n cookiesStrings.push(cookiesString.slice(start));\n }\n }\n \n return cookiesStrings;\n }","import { Readable, type Writable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type {\n APIGatewayProxyEvent,\n APIGatewayProxyEventV2,\n APIGatewayProxyResult,\n APIGatewayProxyStructuredResultV2,\n} from 'aws-lambda';\nimport { splitSetCookieString } from '../../vendor/cookie-es/set-cookie/split';\n\nexport type LambdaEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2;\n\nexport type APIGatewayResult =\n | APIGatewayProxyResult\n | APIGatewayProxyStructuredResultV2;\n\nfunction determinePayloadFormat(event: LambdaEvent): string {\n // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html\n // According to AWS support, version is is extracted from the version property in the event.\n // If there is no version property, then the version is implied as 1.0\n const unknownEvent = event as { version?: string };\n if (typeof unknownEvent.version === 'undefined') {\n return '1.0';\n } else {\n return unknownEvent.version;\n }\n}\n\n/** 1:1 mapping of v1 or v2 input events, deduces which is which.\n * @internal\n **/\nexport type inferAPIGWReturn<TEvent> = TEvent extends APIGatewayProxyEvent\n ? APIGatewayProxyResult\n : TEvent extends APIGatewayProxyEventV2\n ? APIGatewayProxyStructuredResultV2\n : never;\n\ninterface Processor<TEvent extends LambdaEvent> {\n getTRPCPath: (event: TEvent) => string;\n url(event: TEvent): Pick<URL, 'hostname' | 'pathname' | 'search'>;\n getHeaders: (event: TEvent) => Headers;\n getMethod: (event: TEvent) => string;\n toResult: (response: Response) => Promise<inferAPIGWReturn<TEvent>>;\n toStream?: (response: Response, stream: Writable) => Promise<void>;\n}\n\nfunction getHeadersAndCookiesFromResponse(response: Response) {\n const headers = Object.fromEntries(response.headers.entries());\n\n const cookies: string[] = splitSetCookieString(\n response.headers.getSetCookie(),\n ).map((cookie) => cookie.trim());\n\n delete headers['set-cookie'];\n\n return { headers, cookies };\n}\n\nconst v1Processor: Processor<APIGatewayProxyEvent> = {\n // same as getPath above\n getTRPCPath: (event) => {\n if (!event.pathParameters) {\n // Then this event was not triggered by a resource denoted with {proxy+}\n return event.path.split('/').pop() ?? '';\n }\n const matches = event.resource.matchAll(/\\{(.*?)\\}/g);\n for (const match of matches) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const group = match[1]!;\n if (group.includes('+') && event.pathParameters) {\n return event.pathParameters[group.replace('+', '')] ?? '';\n }\n }\n return event.path.slice(1);\n },\n url(event) {\n const hostname: string =\n event.requestContext.domainName ??\n event.headers['host'] ??\n event.multiValueHeaders?.['host']?.[0] ??\n 'localhost';\n\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of Object.entries(\n event.queryStringParameters ?? {},\n )) {\n if (value !== undefined) {\n searchParams.append(key, value);\n }\n }\n const qs = searchParams.toString();\n return {\n hostname,\n pathname: event.path,\n search: qs && `?${qs}`,\n };\n },\n getHeaders: (event) => {\n const headers = new Headers();\n for (const [key, value] of Object.entries(event.headers ?? {})) {\n if (value !== undefined) {\n headers.append(key, value);\n }\n }\n\n for (const [k, values] of Object.entries(event.multiValueHeaders ?? {})) {\n if (values) {\n values.forEach((v) => headers.append(k, v));\n }\n }\n\n return headers;\n },\n getMethod: (event) => event.httpMethod,\n toResult: async (response) => {\n const { headers, cookies } = getHeadersAndCookiesFromResponse(response);\n\n const result: APIGatewayProxyResult = {\n ...(cookies.length && { multiValueHeaders: { 'set-cookie': cookies } }),\n statusCode: response.status,\n body: await response.text(),\n headers,\n };\n\n return result;\n },\n};\n\nconst v2Processor: Processor<APIGatewayProxyEventV2> = {\n getTRPCPath: (event) => {\n const matches = event.routeKey.matchAll(/\\{(.*?)\\}/g);\n for (const match of matches) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const group = match[1]!;\n if (group.includes('+') && event.pathParameters) {\n return event.pathParameters[group.replace('+', '')] ?? '';\n }\n }\n return event.rawPath.slice(1);\n },\n url(event) {\n return {\n hostname: event.requestContext.domainName,\n pathname: event.rawPath,\n search: event.rawQueryString && `?${event.rawQueryString}`,\n };\n },\n getHeaders: (event) => {\n const headers = new Headers();\n for (const [key, value] of Object.entries(event.headers ?? {})) {\n if (value !== undefined) {\n headers.append(key, value);\n }\n }\n\n if (event.cookies) {\n headers.append('cookie', event.cookies.join('; '));\n }\n return headers;\n },\n getMethod: (event) => event.requestContext.http.method,\n toResult: async (response) => {\n const { headers, cookies } = getHeadersAndCookiesFromResponse(response);\n\n const result: APIGatewayProxyStructuredResultV2 = {\n cookies,\n statusCode: response.status,\n body: await response.text(),\n headers,\n };\n\n return result;\n },\n\n toStream: async (response, stream) => {\n const { headers, cookies } = getHeadersAndCookiesFromResponse(response);\n\n const metadata = {\n statusCode: response.status,\n headers,\n cookies,\n };\n\n const responseStream = awslambda.HttpResponseStream.from(stream, metadata);\n\n if (response.body) {\n await pipeline(Readable.fromWeb(response.body as any), responseStream);\n } else {\n responseStream.end();\n }\n },\n};\n\nexport function getPlanner<TEvent extends LambdaEvent>(event: TEvent) {\n const version = determinePayloadFormat(event);\n let processor: Processor<TEvent>;\n switch (version) {\n case '1.0':\n processor = v1Processor as Processor<TEvent>;\n break;\n case '2.0':\n processor = v2Processor as Processor<TEvent>;\n break;\n default:\n throw new Error(`Unsupported version: ${version}`);\n }\n\n const urlParts = processor.url(event);\n const url = `https://${urlParts.hostname}${urlParts.pathname}${urlParts.search}`;\n\n const init: RequestInit = {\n headers: processor.getHeaders(event),\n method: processor.getMethod(event),\n // @ts-expect-error this is fine\n duplex: 'half',\n };\n if (event.body) {\n init.body = event.isBase64Encoded\n ? Buffer.from(event.body, 'base64')\n : event.body;\n }\n\n const request = new Request(url, init);\n\n return {\n path: processor.getTRPCPath(event),\n request,\n toResult: processor.toResult,\n toStream: processor.toStream,\n };\n}\n","/**\n * 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`\n *\n * @example\n * ```ts\n * import type { AnyTRPCRouter } from '@trpc/server'\n * import type { HTTPBaseHandlerOptions } from '@trpc/server/http'\n * ```\n */\nimport type {\n APIGatewayProxyEventV2,\n Context as APIGWContext,\n StreamifyHandler,\n} from 'aws-lambda';\n// @trpc/server\nimport type {\n AnyRouter,\n CreateContextCallback,\n inferRouterContext,\n} from '../../@trpc/server';\n// @trpc/server\nimport type {\n HTTPBaseHandlerOptions,\n ResolveHTTPRequestOptionsContextFn,\n TRPCRequestInfo,\n} from '../../@trpc/server/http';\nimport { resolveResponse } from '../../@trpc/server/http';\nimport type { inferAPIGWReturn, LambdaEvent } from './getPlanner';\nimport { getPlanner } from './getPlanner';\n\nexport type CreateAWSLambdaContextOptions<TEvent extends LambdaEvent> = {\n event: TEvent;\n context: APIGWContext;\n info: TRPCRequestInfo;\n};\n\nexport type AWSLambdaOptions<\n TRouter extends AnyRouter,\n TEvent extends LambdaEvent,\n> = HTTPBaseHandlerOptions<TRouter, TEvent> &\n CreateContextCallback<\n inferRouterContext<AnyRouter>,\n AWSLambdaCreateContextFn<TRouter, TEvent>\n >;\n\nexport type AWSLambdaCreateContextFn<\n TRouter extends AnyRouter,\n TEvent extends LambdaEvent,\n> = ({\n event,\n context,\n info,\n}: CreateAWSLambdaContextOptions<TEvent>) =>\n | inferRouterContext<TRouter>\n | Promise<inferRouterContext<TRouter>>;\n\nexport function awsLambdaRequestHandler<\n TRouter extends AnyRouter,\n TEvent extends LambdaEvent,\n>(\n opts: AWSLambdaOptions<TRouter, TEvent>,\n): (event: TEvent, context: APIGWContext) => Promise<inferAPIGWReturn<TEvent>> {\n return async (event, context) => {\n const planner = getPlanner(event);\n\n const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (\n innerOpts,\n ) => {\n return await opts.createContext?.({ event, context, ...innerOpts });\n };\n\n const response = await resolveResponse({\n ...opts,\n createContext,\n req: planner.request,\n path: planner.path,\n error: null,\n onError(o) {\n opts?.onError?.({\n ...o,\n req: event,\n });\n },\n });\n\n return await planner.toResult(response);\n };\n}\n\nexport function awsLambdaStreamingRequestHandler<\n TRouter extends AnyRouter,\n TEvent extends APIGatewayProxyEventV2,\n>(opts: AWSLambdaOptions<TRouter, TEvent>): StreamifyHandler<TEvent> {\n return async (event, responseStream, context) => {\n const planner = getPlanner(event);\n\n if (!planner.toStream) {\n throw new Error('Streaming is not supported for this event version');\n }\n\n const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (\n innerOpts,\n ) => {\n return await opts.createContext?.({ event, context, ...innerOpts });\n };\n\n const response = await resolveResponse({\n ...opts,\n createContext,\n req: planner.request,\n path: planner.path,\n error: null,\n onError(o) {\n opts?.onError?.({\n ...o,\n req: event,\n });\n },\n });\n\n await planner.toStream(response, responseStream);\n };\n}\n"],"mappings":"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"}