@trpc/client
Version:
1 lines • 12.1 kB
Source Map (JSON)
{"version":3,"file":"httpUtils-CB_100ND.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\nexport function getFetch(\n customFetchImpl?: FetchEsque | NativeFetchEsque,\n): FetchEsque {\n if (customFetchImpl) {\n return customFetchImpl as FetchEsque;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\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 const ac = new AbortController();\n\n for (const signal of signals) {\n if (signal?.aborted) {\n ac.abort();\n } else {\n signal?.addEventListener('abort', () => ac.abort(), { once: true });\n }\n }\n\n return ac.signal;\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 ProcedureType,\n TRPCAcceptHeader,\n TRPCResponse,\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 * @internal\n */\nexport type HTTPLinkBaseOptions<\n TRoot extends Pick<AnyClientTypes, 'transformer'>,\n> = {\n url: string | URL;\n /**\n * Add ponyfill for fetch\n */\n fetch?: FetchEsque;\n /**\n * Send all requests `as POST`s requests regardless of the procedure type\n * The HTTP handler must separately allow overriding the method. See:\n * @see https://trpc.io/docs/rpc\n */\n methodOverride?: 'POST';\n} & TransformerOptions<TRoot>;\n\nexport interface ResolvedHTTPLinkOptions {\n url: string;\n fetch?: FetchEsque;\n transformer: CombinedDataTransformer;\n methodOverride?: 'POST';\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 };\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 + '/' + opts.path;\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 const headers = {\n ...(opts.contentTypeHeader && method !== 'GET'\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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"}