UNPKG

@trpc/client

Version:

The tRPC client library

1 lines • 13.9 kB
{"version":3,"file":"httpUtils-5Hmhcy-z.mjs","names":[],"sources":["../src/getFetch.ts","../src/internals/signals.ts","../src/links/internals/httpUtils.ts"],"sourcesContent":["import type { FetchEsque, NativeFetchEsque } from './internals/types';\n\ntype AnyFn = (...args: any[]) => unknown;\n\nconst isFunction = (fn: unknown): fn is AnyFn => typeof fn === 'function';\n\n/**\n * The `fetch` to use: `customFetchImpl` if given, or else the global one.\n * Throws if there's none.\n */\nexport function getFetch(\n customFetchImpl?: FetchEsque | NativeFetchEsque,\n): FetchEsque {\n if (customFetchImpl) {\n return customFetchImpl as FetchEsque;\n }\n\n if (typeof window !== 'undefined' && isFunction(window.fetch)) {\n return window.fetch.bind(window);\n }\n\n if (typeof globalThis !== 'undefined' && isFunction(globalThis.fetch)) {\n return globalThis.fetch;\n }\n\n throw new Error('No fetch implementation found');\n}\n","import type { Maybe } from '@trpc/server/unstable-core-do-not-import';\n\n/**\n * Like `Promise.all()` but for abort signals\n * - When all signals have been aborted, the merged signal will be aborted\n * - If one signal is `null`, no signal will be aborted\n */\nexport function allAbortSignals(...signals: Maybe<AbortSignal>[]): AbortSignal {\n const ac = new AbortController();\n\n const count = signals.length;\n\n let abortedCount = 0;\n\n const onAbort = () => {\n if (++abortedCount === count) {\n ac.abort();\n }\n };\n\n for (const signal of signals) {\n if (signal?.aborted) {\n onAbort();\n } else {\n signal?.addEventListener('abort', onAbort, {\n once: true,\n });\n }\n }\n\n return ac.signal;\n}\n\n/**\n * Like `Promise.race` but for abort signals\n *\n * Basically, a ponyfill for\n * [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).\n */\nexport function raceAbortSignals(\n ...signals: Maybe<AbortSignal>[]\n): AbortSignal {\n return raceAbortSignalsWithCleanup(...signals).signal;\n}\n\n// Completion can release input listeners without aborting a consumed response.\nexport function raceAbortSignalsWithCleanup(...signals: Maybe<AbortSignal>[]) {\n const ac = new AbortController();\n const cleanup = () => {\n for (const signal of signals) {\n signal?.removeEventListener('abort', onAbort);\n }\n };\n const onAbort = () => {\n cleanup();\n ac.abort();\n };\n\n for (const signal of signals) {\n if (signal?.aborted) {\n onAbort();\n break;\n }\n signal?.addEventListener('abort', onAbort, { once: true });\n }\n\n return { signal: ac.signal, cleanup };\n}\n\nexport function abortSignalToPromise(signal: AbortSignal): Promise<never> {\n return new Promise((_, reject) => {\n if (signal.aborted) {\n reject(signal.reason);\n return;\n }\n signal.addEventListener(\n 'abort',\n () => {\n reject(signal.reason);\n },\n { once: true },\n );\n });\n}\n","import type {\n AnyClientTypes,\n CombinedDataTransformer,\n Maybe,\n PathSeparator,\n ProcedureType,\n TRPCAcceptHeader,\n TRPCResponse,\n} from '@trpc/server/unstable-core-do-not-import';\nimport {\n procedurePathToURLPath,\n resolvePathSeparator,\n} from '@trpc/server/unstable-core-do-not-import';\nimport { getFetch } from '../../getFetch';\nimport type {\n FetchEsque,\n RequestInitEsque,\n ResponseEsque,\n} from '../../internals/types';\nimport type { TransformerOptions } from '../../unstable-internals';\nimport { getTransformer } from '../../unstable-internals';\nimport type { HTTPHeaders } from '../types';\n\n/**\n * The options every HTTP link takes.\n * @internal\n */\nexport type HTTPLinkBaseOptions<\n TRoot extends Pick<AnyClientTypes, 'transformer'>,\n> = {\n /** The URL of your tRPC endpoint, such as `http://localhost:3000/trpc`. */\n url: string | URL;\n /**\n * The `fetch` to use, if not the global one.\n */\n fetch?: FetchEsque;\n /**\n * Sends every call as a POST, whatever its type. The server must set\n * `allowMethodOverride: true`.\n * @see https://trpc.io/docs/rpc#overriding-the-default-http-method\n */\n methodOverride?: 'POST';\n /**\n * The character between a procedure path's segments in URLs. Must match\n * the server's `pathSeparator`.\n * @default '.'\n * @see https://trpc.io/docs/rpc#path-separator\n */\n pathSeparator?: PathSeparator;\n} & TransformerOptions<TRoot>;\n\nexport interface ResolvedHTTPLinkOptions {\n url: string;\n fetch?: FetchEsque;\n transformer: CombinedDataTransformer;\n methodOverride?: 'POST';\n pathSeparator: PathSeparator;\n}\n\nexport function resolveHTTPLinkOptions(\n opts: HTTPLinkBaseOptions<AnyClientTypes>,\n): ResolvedHTTPLinkOptions {\n return {\n url: opts.url.toString(),\n fetch: opts.fetch,\n transformer: getTransformer(opts.transformer),\n methodOverride: opts.methodOverride,\n pathSeparator: resolvePathSeparator(opts.pathSeparator),\n };\n}\n\n// https://github.com/trpc/trpc/pull/669\nfunction arrayToDict(array: unknown[]) {\n const dict: Record<number, unknown> = {};\n for (let index = 0; index < array.length; index++) {\n const element = array[index];\n dict[index] = element;\n }\n return dict;\n}\n\nconst METHOD = {\n query: 'GET',\n mutation: 'POST',\n subscription: 'PATCH',\n} as const;\n\nexport interface HTTPResult {\n json: TRPCResponse;\n meta: {\n response: ResponseEsque;\n responseJSON?: unknown;\n };\n}\n\ntype GetInputOptions = {\n transformer: CombinedDataTransformer;\n} & ({ input: unknown } | { inputs: unknown[] });\n\nexport function getInput(opts: GetInputOptions) {\n return 'input' in opts\n ? opts.transformer.input.serialize(opts.input)\n : arrayToDict(\n opts.inputs.map((_input) => opts.transformer.input.serialize(_input)),\n );\n}\n\nexport type HTTPBaseRequestOptions = GetInputOptions &\n ResolvedHTTPLinkOptions & {\n type: ProcedureType;\n path: string;\n signal: Maybe<AbortSignal>;\n };\n\ntype GetUrl = (opts: HTTPBaseRequestOptions) => string;\ntype GetBody = (opts: HTTPBaseRequestOptions) => RequestInitEsque['body'];\n\nexport type ContentOptions = {\n trpcAcceptHeader?: TRPCAcceptHeader;\n trpcAcceptHeaderKey?: 'trpc-accept' | 'accept';\n contentTypeHeader?: string;\n getUrl: GetUrl;\n getBody: GetBody;\n};\n\nexport const getUrl: GetUrl = (opts) => {\n const parts = opts.url.split('?') as [string, string?];\n const base = parts[0].replace(/\\/$/, ''); // Remove any trailing slashes\n\n let url = base + '/' + procedurePathToURLPath(opts.path, opts.pathSeparator);\n const queryParts: string[] = [];\n\n if (parts[1]) {\n queryParts.push(parts[1]);\n }\n if ('inputs' in opts) {\n queryParts.push('batch=1');\n }\n if (opts.type === 'query' || opts.type === 'subscription') {\n const input = getInput(opts);\n if (input !== undefined && opts.methodOverride !== 'POST') {\n queryParts.push(`input=${encodeURIComponent(JSON.stringify(input))}`);\n }\n }\n if (queryParts.length) {\n url += '?' + queryParts.join('&');\n }\n return url;\n};\n\nexport const getBody: GetBody = (opts) => {\n if (opts.type === 'query' && opts.methodOverride !== 'POST') {\n return undefined;\n }\n const input = getInput(opts);\n return input !== undefined ? JSON.stringify(input) : undefined;\n};\n\nexport type Requester = (\n opts: HTTPBaseRequestOptions & {\n headers: () => HTTPHeaders | Promise<HTTPHeaders>;\n },\n) => Promise<HTTPResult>;\n\nexport const jsonHttpRequester: Requester = (opts) => {\n return httpRequest({\n ...opts,\n contentTypeHeader: 'application/json',\n getUrl,\n getBody,\n });\n};\n\n/**\n * Polyfill for DOMException with AbortError name\n */\nclass AbortError extends Error {\n constructor() {\n const name = 'AbortError';\n super(name);\n this.name = name;\n this.message = name;\n }\n}\n\nexport type HTTPRequestOptions = ContentOptions &\n HTTPBaseRequestOptions & {\n headers: () => HTTPHeaders | Promise<HTTPHeaders>;\n };\n\n/**\n * Polyfill for `signal.throwIfAborted()`\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted\n */\nconst throwIfAborted = (signal: Maybe<AbortSignal>) => {\n if (!signal?.aborted) {\n return;\n }\n // If available, use the native implementation\n signal.throwIfAborted?.();\n\n // If we have `DOMException`, use it\n if (typeof DOMException !== 'undefined') {\n throw new DOMException('AbortError', 'AbortError');\n }\n\n // Otherwise, use our own implementation\n throw new AbortError();\n};\n\nexport async function fetchHTTPResponse(opts: HTTPRequestOptions) {\n throwIfAborted(opts.signal);\n\n const url = opts.getUrl(opts);\n const body = opts.getBody(opts);\n const method = opts.methodOverride ?? METHOD[opts.type];\n const resolvedHeaders = await (async () => {\n const heads = await opts.headers();\n if (Symbol.iterator in heads) {\n return Object.fromEntries(heads);\n }\n return heads;\n })();\n // Header names are case-insensitive, so a `Content-Type` set by the caller\n // replaces ours instead of being sent alongside it\n const hasContentType = Object.keys(resolvedHeaders).some(\n (key) => key.toLowerCase() === 'content-type',\n );\n const headers = {\n ...(opts.contentTypeHeader && method !== 'GET' && !hasContentType\n ? { 'content-type': opts.contentTypeHeader }\n : {}),\n ...(opts.trpcAcceptHeader\n ? { [opts.trpcAcceptHeaderKey ?? 'trpc-accept']: opts.trpcAcceptHeader }\n : undefined),\n ...resolvedHeaders,\n };\n\n return getFetch(opts.fetch)(url, {\n method,\n signal: opts.signal,\n body,\n headers,\n });\n}\n\nexport async function httpRequest(\n opts: HTTPRequestOptions,\n): Promise<HTTPResult> {\n const meta = {} as HTTPResult['meta'];\n\n const res = await fetchHTTPResponse(opts);\n meta.response = res;\n\n const json = await res.json();\n\n meta.responseJSON = json;\n\n return {\n json: json as TRPCResponse,\n meta,\n };\n}\n"],"mappings":"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"}