@orpc/client
Advanced tools
| import { Value, Promisable } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { DecodedRequestMessage } from '@orpc/standard-server-peer'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.mjs'; | ||
| import { f as StandardLinkClient } from '../../shared/client.CpCa3si8.mjs'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.BFAVy68H.mjs'; | ||
| /** | ||
| * The message port used by electron in main process | ||
| */ | ||
| interface MessagePortMainLike { | ||
| on: <T extends string>(event: T, callback: (event?: { | ||
| data: any; | ||
| }) => void) => void; | ||
| postMessage: (data: any, transfer?: any[]) => void; | ||
| } | ||
| /** | ||
| * The message port used by browser extension | ||
| */ | ||
| interface BrowserPortLike { | ||
| onMessage: { | ||
| addListener: (callback: (data: any) => void) => void; | ||
| }; | ||
| onDisconnect: { | ||
| addListener: (callback: () => void) => void; | ||
| }; | ||
| postMessage: (data: any) => void; | ||
| } | ||
| type SupportedMessagePort = Pick<MessagePort, 'addEventListener' | 'postMessage'> | MessagePortMainLike | BrowserPortLike; | ||
| /** | ||
| * Message port can support [The structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | ||
| */ | ||
| type SupportedMessagePortData = any; | ||
| declare function postMessagePortMessage(port: SupportedMessagePort, data: SupportedMessagePortData, transfer?: any[]): void; | ||
| declare function onMessagePortMessage(port: SupportedMessagePort, callback: (data: SupportedMessagePortData) => void): void; | ||
| declare function onMessagePortClose(port: SupportedMessagePort, callback: () => void): void; | ||
| interface LinkMessagePortClientOptions { | ||
| port: SupportedMessagePort; | ||
| /** | ||
| * By default, oRPC serializes request/response messages to string/binary data before sending over message port. | ||
| * If needed, you can define the this option to utilize full power of [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage), | ||
| * such as transferring ownership of objects to the other side or support unserializable objects like `OffscreenCanvas`. | ||
| * | ||
| * @remarks | ||
| * - return null | undefined to disable this feature | ||
| * | ||
| * @warning Make sure your message port supports `transfer` before using this feature. | ||
| * @example | ||
| * ```ts | ||
| * experimental_transfer: (message, port) => { | ||
| * const transfer = deepFindTransferableObjects(message) // implement your own logic | ||
| * return transfer.length ? transfer : null // only enable when needed | ||
| * } | ||
| * ``` | ||
| * | ||
| * @see {@link https://orpc.dev/docs/adapters/message-port#transfer Message Port Transfer Docs} | ||
| */ | ||
| experimental_transfer?: Value<Promisable<object[] | null | undefined>, [message: DecodedRequestMessage, port: SupportedMessagePort]>; | ||
| } | ||
| declare class LinkMessagePortClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly peer; | ||
| constructor(options: LinkMessagePortClientOptions); | ||
| call(request: StandardRequest, _options: ClientOptions<T>, _path: readonly string[], _input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends Omit<StandardRPCLinkOptions<T>, 'url' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, LinkMessagePortClientOptions { | ||
| } | ||
| /** | ||
| * The RPC Link for common message port implementations. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/adapters/message-port Message Port Adapter Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| } | ||
| export { LinkMessagePortClient, RPCLink, onMessagePortClose, onMessagePortMessage, postMessagePortMessage }; | ||
| export type { BrowserPortLike, LinkMessagePortClientOptions, MessagePortMainLike, RPCLinkOptions, SupportedMessagePort, SupportedMessagePortData }; |
| import { Value, Promisable } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { DecodedRequestMessage } from '@orpc/standard-server-peer'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.js'; | ||
| import { f as StandardLinkClient } from '../../shared/client.2jUAqzYU.js'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.B3pNRBih.js'; | ||
| /** | ||
| * The message port used by electron in main process | ||
| */ | ||
| interface MessagePortMainLike { | ||
| on: <T extends string>(event: T, callback: (event?: { | ||
| data: any; | ||
| }) => void) => void; | ||
| postMessage: (data: any, transfer?: any[]) => void; | ||
| } | ||
| /** | ||
| * The message port used by browser extension | ||
| */ | ||
| interface BrowserPortLike { | ||
| onMessage: { | ||
| addListener: (callback: (data: any) => void) => void; | ||
| }; | ||
| onDisconnect: { | ||
| addListener: (callback: () => void) => void; | ||
| }; | ||
| postMessage: (data: any) => void; | ||
| } | ||
| type SupportedMessagePort = Pick<MessagePort, 'addEventListener' | 'postMessage'> | MessagePortMainLike | BrowserPortLike; | ||
| /** | ||
| * Message port can support [The structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | ||
| */ | ||
| type SupportedMessagePortData = any; | ||
| declare function postMessagePortMessage(port: SupportedMessagePort, data: SupportedMessagePortData, transfer?: any[]): void; | ||
| declare function onMessagePortMessage(port: SupportedMessagePort, callback: (data: SupportedMessagePortData) => void): void; | ||
| declare function onMessagePortClose(port: SupportedMessagePort, callback: () => void): void; | ||
| interface LinkMessagePortClientOptions { | ||
| port: SupportedMessagePort; | ||
| /** | ||
| * By default, oRPC serializes request/response messages to string/binary data before sending over message port. | ||
| * If needed, you can define the this option to utilize full power of [MessagePort: postMessage() method](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage), | ||
| * such as transferring ownership of objects to the other side or support unserializable objects like `OffscreenCanvas`. | ||
| * | ||
| * @remarks | ||
| * - return null | undefined to disable this feature | ||
| * | ||
| * @warning Make sure your message port supports `transfer` before using this feature. | ||
| * @example | ||
| * ```ts | ||
| * experimental_transfer: (message, port) => { | ||
| * const transfer = deepFindTransferableObjects(message) // implement your own logic | ||
| * return transfer.length ? transfer : null // only enable when needed | ||
| * } | ||
| * ``` | ||
| * | ||
| * @see {@link https://orpc.dev/docs/adapters/message-port#transfer Message Port Transfer Docs} | ||
| */ | ||
| experimental_transfer?: Value<Promisable<object[] | null | undefined>, [message: DecodedRequestMessage, port: SupportedMessagePort]>; | ||
| } | ||
| declare class LinkMessagePortClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly peer; | ||
| constructor(options: LinkMessagePortClientOptions); | ||
| call(request: StandardRequest, _options: ClientOptions<T>, _path: readonly string[], _input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends Omit<StandardRPCLinkOptions<T>, 'url' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, LinkMessagePortClientOptions { | ||
| } | ||
| /** | ||
| * The RPC Link for common message port implementations. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/adapters/message-port Message Port Adapter Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| } | ||
| export { LinkMessagePortClient, RPCLink, onMessagePortClose, onMessagePortMessage, postMessagePortMessage }; | ||
| export type { BrowserPortLike, LinkMessagePortClientOptions, MessagePortMainLike, RPCLinkOptions, SupportedMessagePort, SupportedMessagePortData }; |
| import { value, isObject } from '@orpc/shared'; | ||
| import { experimental_ClientPeerWithoutCodec, serializeRequestMessage, encodeRequestMessage, deserializeResponseMessage, decodeResponseMessage } from '@orpc/standard-server-peer'; | ||
| import { c as StandardRPCLink } from '../../shared/client.C6Bgyn6F.mjs'; | ||
| import '@orpc/standard-server'; | ||
| import '../../shared/client.CPTihQgC.mjs'; | ||
| import '@orpc/standard-server-fetch'; | ||
| import '../../shared/client.BLtwTQUg.mjs'; | ||
| function postMessagePortMessage(port, data, transfer) { | ||
| if (transfer) { | ||
| port.postMessage(data, transfer); | ||
| } else { | ||
| port.postMessage(data); | ||
| } | ||
| } | ||
| function onMessagePortMessage(port, callback) { | ||
| if ("addEventListener" in port) { | ||
| port.addEventListener("message", (event) => { | ||
| callback(event.data); | ||
| }); | ||
| } else if ("on" in port) { | ||
| port.on("message", (event) => { | ||
| callback(event?.data); | ||
| }); | ||
| } else if ("onMessage" in port) { | ||
| port.onMessage.addListener((data) => { | ||
| callback(data); | ||
| }); | ||
| } else { | ||
| throw new Error("Cannot find a addEventListener/on/onMessage method on the port"); | ||
| } | ||
| } | ||
| function onMessagePortClose(port, callback) { | ||
| if ("addEventListener" in port) { | ||
| port.addEventListener("close", async () => { | ||
| callback(); | ||
| }); | ||
| } else if ("on" in port) { | ||
| port.on("close", async () => { | ||
| callback(); | ||
| }); | ||
| } else if ("onDisconnect" in port) { | ||
| port.onDisconnect.addListener(() => { | ||
| callback(); | ||
| }); | ||
| } else { | ||
| throw new Error("Cannot find a addEventListener/on/onDisconnect method on the port"); | ||
| } | ||
| } | ||
| class LinkMessagePortClient { | ||
| peer; | ||
| constructor(options) { | ||
| this.peer = new experimental_ClientPeerWithoutCodec(async (message) => { | ||
| const [id, type, payload] = message; | ||
| const transfer = await value(options.experimental_transfer, message, options.port); | ||
| if (transfer) { | ||
| postMessagePortMessage(options.port, serializeRequestMessage(id, type, payload), transfer); | ||
| } else { | ||
| postMessagePortMessage(options.port, await encodeRequestMessage(id, type, payload)); | ||
| } | ||
| }); | ||
| onMessagePortMessage(options.port, async (message) => { | ||
| if (isObject(message)) { | ||
| await this.peer.message(deserializeResponseMessage(message)); | ||
| } else { | ||
| await this.peer.message(await decodeResponseMessage(message)); | ||
| } | ||
| }); | ||
| onMessagePortClose(options.port, () => { | ||
| this.peer.close(); | ||
| }); | ||
| } | ||
| async call(request, _options, _path, _input) { | ||
| const response = await this.peer.request(request); | ||
| return { ...response, body: () => Promise.resolve(response.body) }; | ||
| } | ||
| } | ||
| class RPCLink extends StandardRPCLink { | ||
| constructor(options) { | ||
| const linkClient = new LinkMessagePortClient(options); | ||
| super(linkClient, { ...options, url: "http://orpc" }); | ||
| } | ||
| } | ||
| export { LinkMessagePortClient, RPCLink, onMessagePortClose, onMessagePortMessage, postMessagePortMessage }; |
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.mjs'; | ||
| import { f as StandardLinkClient } from '../../shared/client.CpCa3si8.mjs'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.BFAVy68H.mjs'; | ||
| import '@orpc/shared'; | ||
| interface LinkWebsocketClientOptions { | ||
| websocket: Pick<WebSocket, 'addEventListener' | 'send' | 'readyState'>; | ||
| } | ||
| declare class LinkWebsocketClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly peer; | ||
| constructor(options: LinkWebsocketClientOptions); | ||
| call(request: StandardRequest, _options: ClientOptions<T>, _path: readonly string[], _input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends Omit<StandardRPCLinkOptions<T>, 'url' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, LinkWebsocketClientOptions { | ||
| } | ||
| /** | ||
| * The RPC Link communicates with the server using the RPC protocol over WebSocket. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/adapters/websocket WebSocket Adapter Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| } | ||
| export { LinkWebsocketClient, RPCLink }; | ||
| export type { LinkWebsocketClientOptions, RPCLinkOptions }; |
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.js'; | ||
| import { f as StandardLinkClient } from '../../shared/client.2jUAqzYU.js'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.B3pNRBih.js'; | ||
| import '@orpc/shared'; | ||
| interface LinkWebsocketClientOptions { | ||
| websocket: Pick<WebSocket, 'addEventListener' | 'send' | 'readyState'>; | ||
| } | ||
| declare class LinkWebsocketClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly peer; | ||
| constructor(options: LinkWebsocketClientOptions); | ||
| call(request: StandardRequest, _options: ClientOptions<T>, _path: readonly string[], _input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends Omit<StandardRPCLinkOptions<T>, 'url' | 'method' | 'fallbackMethod' | 'maxUrlLength'>, LinkWebsocketClientOptions { | ||
| } | ||
| /** | ||
| * The RPC Link communicates with the server using the RPC protocol over WebSocket. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/adapters/websocket WebSocket Adapter Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| } | ||
| export { LinkWebsocketClient, RPCLink }; | ||
| export type { LinkWebsocketClientOptions, RPCLinkOptions }; |
| import { readAsBuffer } from '@orpc/shared'; | ||
| import { ClientPeer } from '@orpc/standard-server-peer'; | ||
| import { c as StandardRPCLink } from '../../shared/client.C6Bgyn6F.mjs'; | ||
| import '@orpc/standard-server'; | ||
| import '../../shared/client.CPTihQgC.mjs'; | ||
| import '@orpc/standard-server-fetch'; | ||
| import '../../shared/client.BLtwTQUg.mjs'; | ||
| const WEBSOCKET_CONNECTING = 0; | ||
| class LinkWebsocketClient { | ||
| peer; | ||
| constructor(options) { | ||
| const untilOpen = new Promise((resolve) => { | ||
| if (options.websocket.readyState === WEBSOCKET_CONNECTING) { | ||
| options.websocket.addEventListener("open", () => { | ||
| resolve(); | ||
| }, { once: true }); | ||
| } else { | ||
| resolve(); | ||
| } | ||
| }); | ||
| this.peer = new ClientPeer(async (message) => { | ||
| await untilOpen; | ||
| return options.websocket.send(message); | ||
| }); | ||
| options.websocket.addEventListener("message", async (event) => { | ||
| const message = event.data instanceof Blob ? await readAsBuffer(event.data) : event.data; | ||
| this.peer.message(message); | ||
| }); | ||
| options.websocket.addEventListener("close", () => { | ||
| this.peer.close(); | ||
| }); | ||
| } | ||
| async call(request, _options, _path, _input) { | ||
| const response = await this.peer.request(request); | ||
| return { ...response, body: () => Promise.resolve(response.body) }; | ||
| } | ||
| } | ||
| class RPCLink extends StandardRPCLink { | ||
| constructor(options) { | ||
| const linkClient = new LinkWebsocketClient(options); | ||
| super(linkClient, { ...options, url: "http://orpc" }); | ||
| } | ||
| } | ||
| export { LinkWebsocketClient, RPCLink }; |
| import { Value, Promisable } from '@orpc/shared'; | ||
| import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { BatchResponseMode } from '@orpc/standard-server/batch'; | ||
| import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.CpCa3si8.mjs'; | ||
| import { b as ClientContext } from '../shared/client.i2uoJbEp.mjs'; | ||
| interface BatchLinkPluginGroup<T extends ClientContext> { | ||
| condition(options: StandardLinkClientInterceptorOptions<T>): boolean; | ||
| context: T; | ||
| path?: readonly string[]; | ||
| input?: unknown; | ||
| } | ||
| interface BatchLinkPluginOptions<T extends ClientContext> { | ||
| groups: readonly [BatchLinkPluginGroup<T>, ...BatchLinkPluginGroup<T>[]]; | ||
| /** | ||
| * The maximum number of requests in the batch. | ||
| * | ||
| * @default 10 | ||
| */ | ||
| maxSize?: Value<Promisable<number>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * The batch response mode. | ||
| * | ||
| * @default 'streaming' | ||
| */ | ||
| mode?: Value<BatchResponseMode, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Defines the URL to use for the batch request. | ||
| * | ||
| * @default the URL of the first request in the batch + '/__batch__' | ||
| */ | ||
| url?: Value<Promisable<string | URL>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<Promisable<number>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Defines the HTTP headers to use for the batch request. | ||
| * | ||
| * @default The same headers of all requests in the batch | ||
| */ | ||
| headers?: Value<Promisable<StandardHeaders>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Map the batch request items before sending them. | ||
| * | ||
| * @default Removes headers that are duplicated in the batch headers. | ||
| */ | ||
| mapRequestItem?: (options: StandardLinkClientInterceptorOptions<T> & { | ||
| batchUrl: URL; | ||
| batchHeaders: StandardHeaders; | ||
| }) => StandardRequest; | ||
| /** | ||
| * Exclude a request from the batch. | ||
| * | ||
| * @default () => false | ||
| */ | ||
| exclude?: (options: StandardLinkClientInterceptorOptions<T>) => boolean; | ||
| } | ||
| /** | ||
| * The Batch Requests Plugin allows you to combine multiple requests and responses into a single batch, | ||
| * reducing the overhead of sending each one separately. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/batch-requests Batch Requests Plugin Docs} | ||
| */ | ||
| declare class BatchLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| #private; | ||
| private readonly groups; | ||
| private readonly maxSize; | ||
| private readonly batchUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly batchHeaders; | ||
| private readonly mapRequestItem; | ||
| private readonly exclude; | ||
| private readonly mode; | ||
| private pending; | ||
| order: number; | ||
| constructor(options: NoInfer<BatchLinkPluginOptions<T>>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface DedupeRequestsPluginGroup<T extends ClientContext> { | ||
| condition(options: StandardLinkClientInterceptorOptions<T>): boolean; | ||
| /** | ||
| * The context used for the rest of the request lifecycle. | ||
| */ | ||
| context: T; | ||
| } | ||
| interface DedupeRequestsPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * To enable deduplication, a request must match at least one defined group. | ||
| * Requests that fall into the same group are considered for deduplication together. | ||
| */ | ||
| groups: readonly [DedupeRequestsPluginGroup<T>, ...DedupeRequestsPluginGroup<T>[]]; | ||
| /** | ||
| * Filters requests to dedupe | ||
| * | ||
| * @default (({ request }) => request.method === 'GET') | ||
| */ | ||
| filter?: (options: StandardLinkClientInterceptorOptions<T>) => boolean; | ||
| } | ||
| /** | ||
| * Prevents duplicate requests by deduplicating similar ones to reduce server load. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/dedupe-requests Dedupe Requests Plugin} | ||
| */ | ||
| declare class DedupeRequestsPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| #private; | ||
| order: number; | ||
| constructor(options: NoInfer<DedupeRequestsPluginOptions<T>>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface ClientRetryPluginAttemptOptions<T extends ClientContext> extends StandardLinkInterceptorOptions<T> { | ||
| lastEventRetry: number | undefined; | ||
| attemptIndex: number; | ||
| error: unknown; | ||
| } | ||
| interface ClientRetryPluginContext { | ||
| /** | ||
| * Maximum retry attempts before throwing | ||
| * Use `Number.POSITIVE_INFINITY` for infinite retries (e.g., when handling Server-Sent Events). | ||
| * | ||
| * @default 0 | ||
| */ | ||
| retry?: Value<Promisable<number>, [StandardLinkInterceptorOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * Delay (in ms) before retrying. | ||
| * | ||
| * @default (o) => o.lastEventRetry ?? 2000 | ||
| */ | ||
| retryDelay?: Value<Promisable<number>, [ClientRetryPluginAttemptOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * Determine should retry or not. | ||
| * | ||
| * @default true | ||
| */ | ||
| shouldRetry?: Value<Promisable<boolean>, [ClientRetryPluginAttemptOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * The hook called when retrying, and return the unsubscribe function. | ||
| */ | ||
| onRetry?: (options: ClientRetryPluginAttemptOptions<ClientRetryPluginContext>) => void | ((isSuccess: boolean) => void); | ||
| } | ||
| declare class ClientRetryPluginInvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| interface ClientRetryPluginOptions { | ||
| default?: ClientRetryPluginContext; | ||
| } | ||
| /** | ||
| * The Client Retry Plugin enables retrying client calls when errors occur. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/client-retry Client Retry Plugin Docs} | ||
| */ | ||
| declare class ClientRetryPlugin<T extends ClientRetryPluginContext> implements StandardLinkPlugin<T> { | ||
| private readonly defaultRetry; | ||
| private readonly defaultRetryDelay; | ||
| private readonly defaultShouldRetry; | ||
| private readonly defaultOnRetry; | ||
| order: number; | ||
| constructor(options?: ClientRetryPluginOptions); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface RetryAfterPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * Override condition to determine whether to retry or not. | ||
| * | ||
| * @default ((response) => response.status === 429 || response.status === 503) | ||
| */ | ||
| condition?: Value<boolean, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| /** | ||
| * Maximum attempts before giving up retries. | ||
| * | ||
| * @default 3 | ||
| */ | ||
| maxAttempts?: Value<number, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| /** | ||
| * Maximum timeout in milliseconds to wait before giving up retries. | ||
| * | ||
| * @default 5 * 60 * 1000 (5 minutes) | ||
| */ | ||
| timeout?: Value<number, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| } | ||
| /** | ||
| * The Retry After Plugin automatically retries requests based on server `Retry-After` headers. | ||
| * This is particularly useful for handling rate limiting and temporary server unavailability. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/retry-after Retry After Plugin Docs} | ||
| */ | ||
| declare class RetryAfterPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| private readonly condition; | ||
| private readonly maxAttempts; | ||
| private readonly timeout; | ||
| order: number; | ||
| constructor(options?: RetryAfterPluginOptions<T>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| private parseRetryAfterHeader; | ||
| private delayExecution; | ||
| } | ||
| interface SimpleCsrfProtectionLinkPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * The name of the header to check. | ||
| * | ||
| * @default 'x-csrf-token' | ||
| */ | ||
| headerName?: Value<Promisable<string>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| /** | ||
| * The value of the header to check. | ||
| * | ||
| * @default 'orpc' | ||
| * | ||
| */ | ||
| headerValue?: Value<Promisable<string>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| /** | ||
| * Exclude a procedure from the plugin. | ||
| * | ||
| * @default false | ||
| */ | ||
| exclude?: Value<Promisable<boolean>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| } | ||
| /** | ||
| * This plugin adds basic Cross-Site Request Forgery (CSRF) protection to your oRPC application. | ||
| * It helps ensure that requests to your procedures originate from JavaScript code, | ||
| * not from other sources like standard HTML forms or direct browser navigation. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/simple-csrf-protection Simple CSRF Protection Plugin Docs} | ||
| */ | ||
| declare class SimpleCsrfProtectionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| private readonly headerName; | ||
| private readonly headerValue; | ||
| private readonly exclude; | ||
| constructor(options?: SimpleCsrfProtectionLinkPluginOptions<T>); | ||
| order: number; | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin }; | ||
| export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, RetryAfterPluginOptions, SimpleCsrfProtectionLinkPluginOptions }; |
| import { Value, Promisable } from '@orpc/shared'; | ||
| import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { BatchResponseMode } from '@orpc/standard-server/batch'; | ||
| import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.2jUAqzYU.js'; | ||
| import { b as ClientContext } from '../shared/client.i2uoJbEp.js'; | ||
| interface BatchLinkPluginGroup<T extends ClientContext> { | ||
| condition(options: StandardLinkClientInterceptorOptions<T>): boolean; | ||
| context: T; | ||
| path?: readonly string[]; | ||
| input?: unknown; | ||
| } | ||
| interface BatchLinkPluginOptions<T extends ClientContext> { | ||
| groups: readonly [BatchLinkPluginGroup<T>, ...BatchLinkPluginGroup<T>[]]; | ||
| /** | ||
| * The maximum number of requests in the batch. | ||
| * | ||
| * @default 10 | ||
| */ | ||
| maxSize?: Value<Promisable<number>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * The batch response mode. | ||
| * | ||
| * @default 'streaming' | ||
| */ | ||
| mode?: Value<BatchResponseMode, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Defines the URL to use for the batch request. | ||
| * | ||
| * @default the URL of the first request in the batch + '/__batch__' | ||
| */ | ||
| url?: Value<Promisable<string | URL>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<Promisable<number>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Defines the HTTP headers to use for the batch request. | ||
| * | ||
| * @default The same headers of all requests in the batch | ||
| */ | ||
| headers?: Value<Promisable<StandardHeaders>, [readonly [StandardLinkClientInterceptorOptions<T>, ...StandardLinkClientInterceptorOptions<T>[]]]>; | ||
| /** | ||
| * Map the batch request items before sending them. | ||
| * | ||
| * @default Removes headers that are duplicated in the batch headers. | ||
| */ | ||
| mapRequestItem?: (options: StandardLinkClientInterceptorOptions<T> & { | ||
| batchUrl: URL; | ||
| batchHeaders: StandardHeaders; | ||
| }) => StandardRequest; | ||
| /** | ||
| * Exclude a request from the batch. | ||
| * | ||
| * @default () => false | ||
| */ | ||
| exclude?: (options: StandardLinkClientInterceptorOptions<T>) => boolean; | ||
| } | ||
| /** | ||
| * The Batch Requests Plugin allows you to combine multiple requests and responses into a single batch, | ||
| * reducing the overhead of sending each one separately. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/batch-requests Batch Requests Plugin Docs} | ||
| */ | ||
| declare class BatchLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| #private; | ||
| private readonly groups; | ||
| private readonly maxSize; | ||
| private readonly batchUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly batchHeaders; | ||
| private readonly mapRequestItem; | ||
| private readonly exclude; | ||
| private readonly mode; | ||
| private pending; | ||
| order: number; | ||
| constructor(options: NoInfer<BatchLinkPluginOptions<T>>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface DedupeRequestsPluginGroup<T extends ClientContext> { | ||
| condition(options: StandardLinkClientInterceptorOptions<T>): boolean; | ||
| /** | ||
| * The context used for the rest of the request lifecycle. | ||
| */ | ||
| context: T; | ||
| } | ||
| interface DedupeRequestsPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * To enable deduplication, a request must match at least one defined group. | ||
| * Requests that fall into the same group are considered for deduplication together. | ||
| */ | ||
| groups: readonly [DedupeRequestsPluginGroup<T>, ...DedupeRequestsPluginGroup<T>[]]; | ||
| /** | ||
| * Filters requests to dedupe | ||
| * | ||
| * @default (({ request }) => request.method === 'GET') | ||
| */ | ||
| filter?: (options: StandardLinkClientInterceptorOptions<T>) => boolean; | ||
| } | ||
| /** | ||
| * Prevents duplicate requests by deduplicating similar ones to reduce server load. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/dedupe-requests Dedupe Requests Plugin} | ||
| */ | ||
| declare class DedupeRequestsPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| #private; | ||
| order: number; | ||
| constructor(options: NoInfer<DedupeRequestsPluginOptions<T>>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface ClientRetryPluginAttemptOptions<T extends ClientContext> extends StandardLinkInterceptorOptions<T> { | ||
| lastEventRetry: number | undefined; | ||
| attemptIndex: number; | ||
| error: unknown; | ||
| } | ||
| interface ClientRetryPluginContext { | ||
| /** | ||
| * Maximum retry attempts before throwing | ||
| * Use `Number.POSITIVE_INFINITY` for infinite retries (e.g., when handling Server-Sent Events). | ||
| * | ||
| * @default 0 | ||
| */ | ||
| retry?: Value<Promisable<number>, [StandardLinkInterceptorOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * Delay (in ms) before retrying. | ||
| * | ||
| * @default (o) => o.lastEventRetry ?? 2000 | ||
| */ | ||
| retryDelay?: Value<Promisable<number>, [ClientRetryPluginAttemptOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * Determine should retry or not. | ||
| * | ||
| * @default true | ||
| */ | ||
| shouldRetry?: Value<Promisable<boolean>, [ClientRetryPluginAttemptOptions<ClientRetryPluginContext>]>; | ||
| /** | ||
| * The hook called when retrying, and return the unsubscribe function. | ||
| */ | ||
| onRetry?: (options: ClientRetryPluginAttemptOptions<ClientRetryPluginContext>) => void | ((isSuccess: boolean) => void); | ||
| } | ||
| declare class ClientRetryPluginInvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| interface ClientRetryPluginOptions { | ||
| default?: ClientRetryPluginContext; | ||
| } | ||
| /** | ||
| * The Client Retry Plugin enables retrying client calls when errors occur. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/client-retry Client Retry Plugin Docs} | ||
| */ | ||
| declare class ClientRetryPlugin<T extends ClientRetryPluginContext> implements StandardLinkPlugin<T> { | ||
| private readonly defaultRetry; | ||
| private readonly defaultRetryDelay; | ||
| private readonly defaultShouldRetry; | ||
| private readonly defaultOnRetry; | ||
| order: number; | ||
| constructor(options?: ClientRetryPluginOptions); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface RetryAfterPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * Override condition to determine whether to retry or not. | ||
| * | ||
| * @default ((response) => response.status === 429 || response.status === 503) | ||
| */ | ||
| condition?: Value<boolean, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| /** | ||
| * Maximum attempts before giving up retries. | ||
| * | ||
| * @default 3 | ||
| */ | ||
| maxAttempts?: Value<number, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| /** | ||
| * Maximum timeout in milliseconds to wait before giving up retries. | ||
| * | ||
| * @default 5 * 60 * 1000 (5 minutes) | ||
| */ | ||
| timeout?: Value<number, [ | ||
| response: StandardLazyResponse, | ||
| options: StandardLinkClientInterceptorOptions<T> | ||
| ]>; | ||
| } | ||
| /** | ||
| * The Retry After Plugin automatically retries requests based on server `Retry-After` headers. | ||
| * This is particularly useful for handling rate limiting and temporary server unavailability. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/retry-after Retry After Plugin Docs} | ||
| */ | ||
| declare class RetryAfterPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| private readonly condition; | ||
| private readonly maxAttempts; | ||
| private readonly timeout; | ||
| order: number; | ||
| constructor(options?: RetryAfterPluginOptions<T>); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| private parseRetryAfterHeader; | ||
| private delayExecution; | ||
| } | ||
| interface SimpleCsrfProtectionLinkPluginOptions<T extends ClientContext> { | ||
| /** | ||
| * The name of the header to check. | ||
| * | ||
| * @default 'x-csrf-token' | ||
| */ | ||
| headerName?: Value<Promisable<string>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| /** | ||
| * The value of the header to check. | ||
| * | ||
| * @default 'orpc' | ||
| * | ||
| */ | ||
| headerValue?: Value<Promisable<string>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| /** | ||
| * Exclude a procedure from the plugin. | ||
| * | ||
| * @default false | ||
| */ | ||
| exclude?: Value<Promisable<boolean>, [options: StandardLinkClientInterceptorOptions<T>]>; | ||
| } | ||
| /** | ||
| * This plugin adds basic Cross-Site Request Forgery (CSRF) protection to your oRPC application. | ||
| * It helps ensure that requests to your procedures originate from JavaScript code, | ||
| * not from other sources like standard HTML forms or direct browser navigation. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/plugins/simple-csrf-protection Simple CSRF Protection Plugin Docs} | ||
| */ | ||
| declare class SimpleCsrfProtectionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> { | ||
| private readonly headerName; | ||
| private readonly headerValue; | ||
| private readonly exclude; | ||
| constructor(options?: SimpleCsrfProtectionLinkPluginOptions<T>); | ||
| order: number; | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin }; | ||
| export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, RetryAfterPluginOptions, SimpleCsrfProtectionLinkPluginOptions }; |
| import { isAsyncIteratorObject, defer, value, splitInHalf, toArray, stringifyJSON, overlayProxy, AsyncIteratorClass } from '@orpc/shared'; | ||
| import { toBatchRequest, parseBatchResponse, toBatchAbortSignal } from '@orpc/standard-server/batch'; | ||
| import { replicateStandardLazyResponse, getEventMeta, flattenHeader } from '@orpc/standard-server'; | ||
| import { C as COMMON_ORPC_ERROR_DEFS } from '../shared/client.CPTihQgC.mjs'; | ||
| class BatchLinkPlugin { | ||
| groups; | ||
| maxSize; | ||
| batchUrl; | ||
| maxUrlLength; | ||
| batchHeaders; | ||
| mapRequestItem; | ||
| exclude; | ||
| mode; | ||
| pending; | ||
| order = 5e6; | ||
| constructor(options) { | ||
| this.groups = options.groups; | ||
| this.pending = /* @__PURE__ */ new Map(); | ||
| this.maxSize = options.maxSize ?? 10; | ||
| this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
| this.mode = options.mode ?? "streaming"; | ||
| this.batchUrl = options.url ?? (([options2]) => `${options2.request.url.origin}${options2.request.url.pathname}/__batch__`); | ||
| this.batchHeaders = options.headers ?? (([options2, ...rest]) => { | ||
| const headers = {}; | ||
| for (const [key, value2] of Object.entries(options2.request.headers)) { | ||
| if (rest.every((item) => item.request.headers[key] === value2)) { | ||
| headers[key] = value2; | ||
| } | ||
| } | ||
| return headers; | ||
| }); | ||
| this.mapRequestItem = options.mapRequestItem ?? (({ request, batchHeaders }) => { | ||
| const headers = {}; | ||
| for (const [key, value2] of Object.entries(request.headers)) { | ||
| if (batchHeaders[key] !== value2) { | ||
| headers[key] = value2; | ||
| } | ||
| } | ||
| return { | ||
| method: request.method, | ||
| url: request.url, | ||
| headers, | ||
| body: request.body, | ||
| signal: request.signal | ||
| }; | ||
| }); | ||
| this.exclude = options.exclude ?? (() => false); | ||
| } | ||
| init(options) { | ||
| options.clientInterceptors ??= []; | ||
| options.clientInterceptors.push((options2) => { | ||
| if (options2.request.headers["x-orpc-batch"] !== "1") { | ||
| return options2.next(); | ||
| } | ||
| return options2.next({ | ||
| ...options2, | ||
| request: { | ||
| ...options2.request, | ||
| headers: { | ||
| ...options2.request.headers, | ||
| "x-orpc-batch": void 0 | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| options.clientInterceptors.push((options2) => { | ||
| if (this.exclude(options2) || options2.request.body instanceof Blob || options2.request.body instanceof FormData || isAsyncIteratorObject(options2.request.body) || options2.request.signal?.aborted) { | ||
| return options2.next(); | ||
| } | ||
| const group = this.groups.find((group2) => group2.condition(options2)); | ||
| if (!group) { | ||
| return options2.next(); | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| this.#enqueueRequest(group, options2, resolve, reject); | ||
| defer(() => this.#processPendingBatches()); | ||
| }); | ||
| }); | ||
| } | ||
| #enqueueRequest(group, options, resolve, reject) { | ||
| const items = this.pending.get(group); | ||
| if (items) { | ||
| items.push([options, resolve, reject]); | ||
| } else { | ||
| this.pending.set(group, [[options, resolve, reject]]); | ||
| } | ||
| } | ||
| async #processPendingBatches() { | ||
| const pending = this.pending; | ||
| this.pending = /* @__PURE__ */ new Map(); | ||
| for (const [group, items] of pending) { | ||
| const getItems = items.filter(([options]) => options.request.method === "GET"); | ||
| const restItems = items.filter(([options]) => options.request.method !== "GET"); | ||
| this.#executeBatch("GET", group, getItems); | ||
| this.#executeBatch("POST", group, restItems); | ||
| } | ||
| } | ||
| async #executeBatch(method, group, groupItems) { | ||
| if (!groupItems.length) { | ||
| return; | ||
| } | ||
| const batchItems = groupItems; | ||
| if (batchItems.length === 1) { | ||
| batchItems[0][0].next().then(batchItems[0][1]).catch(batchItems[0][2]); | ||
| return; | ||
| } | ||
| try { | ||
| const options = batchItems.map(([options2]) => options2); | ||
| const maxSize = await value(this.maxSize, options); | ||
| if (batchItems.length > maxSize) { | ||
| const [first, second] = splitInHalf(batchItems); | ||
| this.#executeBatch(method, group, first); | ||
| this.#executeBatch(method, group, second); | ||
| return; | ||
| } | ||
| const batchUrl = new URL(await value(this.batchUrl, options)); | ||
| const batchHeaders = await value(this.batchHeaders, options); | ||
| const mappedItems = batchItems.map(([options2]) => this.mapRequestItem({ ...options2, batchUrl, batchHeaders })); | ||
| const batchRequest = toBatchRequest({ | ||
| method, | ||
| url: batchUrl, | ||
| headers: batchHeaders, | ||
| requests: mappedItems | ||
| }); | ||
| const maxUrlLength = await value(this.maxUrlLength, options); | ||
| if (batchRequest.url.toString().length > maxUrlLength) { | ||
| const [first, second] = splitInHalf(batchItems); | ||
| this.#executeBatch(method, group, first); | ||
| this.#executeBatch(method, group, second); | ||
| return; | ||
| } | ||
| const mode = value(this.mode, options); | ||
| try { | ||
| const lazyResponse = await options[0].next({ | ||
| request: { ...batchRequest, headers: { ...batchRequest.headers, "x-orpc-batch": mode } }, | ||
| signal: batchRequest.signal, | ||
| context: group.context, | ||
| input: group.input, | ||
| path: toArray(group.path) | ||
| }); | ||
| const parsed = parseBatchResponse({ ...lazyResponse, body: await lazyResponse.body() }); | ||
| for await (const item of parsed) { | ||
| batchItems[item.index]?.[1]({ ...item, body: () => Promise.resolve(item.body) }); | ||
| } | ||
| } catch (err) { | ||
| if (batchRequest.signal?.aborted && batchRequest.signal.reason === err) { | ||
| for (const [{ signal }, , reject] of batchItems) { | ||
| if (signal?.aborted) { | ||
| reject(signal.reason); | ||
| } | ||
| } | ||
| } | ||
| throw err; | ||
| } | ||
| throw new Error("Something went wrong make batch response not contains enough responses. This can be a bug please report it."); | ||
| } catch (error) { | ||
| for (const [, , reject] of batchItems) { | ||
| reject(error); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| class DedupeRequestsPlugin { | ||
| #groups; | ||
| #filter; | ||
| order = 4e6; | ||
| // make sure execute before batch plugin | ||
| #queue = /* @__PURE__ */ new Map(); | ||
| constructor(options) { | ||
| this.#groups = options.groups; | ||
| this.#filter = options.filter ?? (({ request }) => request.method === "GET"); | ||
| } | ||
| init(options) { | ||
| options.clientInterceptors ??= []; | ||
| options.clientInterceptors.push((options2) => { | ||
| if (options2.request.body instanceof Blob || options2.request.body instanceof FormData || options2.request.body instanceof URLSearchParams || isAsyncIteratorObject(options2.request.body) || !this.#filter(options2)) { | ||
| return options2.next(); | ||
| } | ||
| const group = this.#groups.find((group2) => group2.condition(options2)); | ||
| if (!group) { | ||
| return options2.next(); | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| this.#enqueue(group, options2, resolve, reject); | ||
| defer(() => this.#dequeue()); | ||
| }); | ||
| }); | ||
| } | ||
| #enqueue(group, options, resolve, reject) { | ||
| let queue = this.#queue.get(group); | ||
| if (!queue) { | ||
| this.#queue.set(group, queue = []); | ||
| } | ||
| const matched = queue.find((item) => { | ||
| const requestString1 = stringifyJSON({ | ||
| body: item.options.request.body, | ||
| headers: item.options.request.headers, | ||
| method: item.options.request.method, | ||
| url: item.options.request.url | ||
| }); | ||
| const requestString2 = stringifyJSON({ | ||
| body: options.request.body, | ||
| headers: options.request.headers, | ||
| method: options.request.method, | ||
| url: options.request.url | ||
| }); | ||
| return requestString1 === requestString2; | ||
| }); | ||
| if (matched) { | ||
| matched.signals.push(options.request.signal); | ||
| matched.resolves.push(resolve); | ||
| matched.rejects.push(reject); | ||
| } else { | ||
| queue.push({ | ||
| options, | ||
| signals: [options.request.signal], | ||
| resolves: [resolve], | ||
| rejects: [reject] | ||
| }); | ||
| } | ||
| } | ||
| async #dequeue() { | ||
| const promises = []; | ||
| for (const [group, items] of this.#queue) { | ||
| for (const { options, signals, resolves, rejects } of items) { | ||
| promises.push( | ||
| this.#execute(group, options, signals, resolves, rejects) | ||
| ); | ||
| } | ||
| } | ||
| this.#queue.clear(); | ||
| await Promise.all(promises); | ||
| } | ||
| async #execute(group, options, signals, resolves, rejects) { | ||
| try { | ||
| const dedupedRequest = { | ||
| ...options.request, | ||
| signal: toBatchAbortSignal(signals) | ||
| }; | ||
| const response = await options.next({ | ||
| ...options, | ||
| request: dedupedRequest, | ||
| signal: dedupedRequest.signal, | ||
| context: group.context | ||
| }); | ||
| const replicatedResponses = replicateStandardLazyResponse(response, resolves.length); | ||
| for (const resolve of resolves) { | ||
| resolve(replicatedResponses.shift()); | ||
| } | ||
| } catch (error) { | ||
| for (const reject of rejects) { | ||
| reject(error); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| class ClientRetryPluginInvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| class ClientRetryPlugin { | ||
| defaultRetry; | ||
| defaultRetryDelay; | ||
| defaultShouldRetry; | ||
| defaultOnRetry; | ||
| order = 18e5; | ||
| constructor(options = {}) { | ||
| this.defaultRetry = options.default?.retry ?? 0; | ||
| this.defaultRetryDelay = options.default?.retryDelay ?? ((o) => o.lastEventRetry ?? 2e3); | ||
| this.defaultShouldRetry = options.default?.shouldRetry ?? true; | ||
| this.defaultOnRetry = options.default?.onRetry; | ||
| } | ||
| init(options) { | ||
| options.interceptors ??= []; | ||
| options.interceptors.push(async (interceptorOptions) => { | ||
| const maxAttempts = await value( | ||
| interceptorOptions.context.retry ?? this.defaultRetry, | ||
| interceptorOptions | ||
| ); | ||
| const retryDelay = interceptorOptions.context.retryDelay ?? this.defaultRetryDelay; | ||
| const shouldRetry = interceptorOptions.context.shouldRetry ?? this.defaultShouldRetry; | ||
| const onRetry = interceptorOptions.context.onRetry ?? this.defaultOnRetry; | ||
| if (maxAttempts <= 0) { | ||
| return interceptorOptions.next(); | ||
| } | ||
| let lastEventId = interceptorOptions.lastEventId; | ||
| let lastEventRetry; | ||
| let callback; | ||
| let attemptIndex = 0; | ||
| const next = async (initialError) => { | ||
| let currentError = initialError; | ||
| while (true) { | ||
| const updatedInterceptorOptions = { ...interceptorOptions, lastEventId }; | ||
| if (currentError) { | ||
| if (attemptIndex >= maxAttempts) { | ||
| throw currentError.error; | ||
| } | ||
| const attemptOptions = { | ||
| ...updatedInterceptorOptions, | ||
| attemptIndex, | ||
| error: currentError.error, | ||
| lastEventRetry | ||
| }; | ||
| const shouldRetryBool = await value( | ||
| shouldRetry, | ||
| attemptOptions | ||
| ); | ||
| if (!shouldRetryBool) { | ||
| throw currentError.error; | ||
| } | ||
| callback = onRetry?.(attemptOptions); | ||
| const retryDelayMs = await value(retryDelay, attemptOptions); | ||
| await new Promise((resolve) => setTimeout(resolve, retryDelayMs)); | ||
| attemptIndex++; | ||
| } | ||
| try { | ||
| currentError = void 0; | ||
| return await interceptorOptions.next(updatedInterceptorOptions); | ||
| } catch (error) { | ||
| currentError = { error }; | ||
| if (updatedInterceptorOptions.signal?.aborted) { | ||
| throw error; | ||
| } | ||
| } finally { | ||
| callback?.(!currentError); | ||
| callback = void 0; | ||
| } | ||
| } | ||
| }; | ||
| const output = await next(); | ||
| if (!isAsyncIteratorObject(output)) { | ||
| return output; | ||
| } | ||
| let current = output; | ||
| let isIteratorAborted = false; | ||
| return overlayProxy(() => current, new AsyncIteratorClass( | ||
| async () => { | ||
| while (true) { | ||
| try { | ||
| const item = await current.next(); | ||
| const meta = getEventMeta(item.value); | ||
| lastEventId = meta?.id ?? lastEventId; | ||
| lastEventRetry = meta?.retry ?? lastEventRetry; | ||
| return item; | ||
| } catch (error) { | ||
| const meta = getEventMeta(error); | ||
| lastEventId = meta?.id ?? lastEventId; | ||
| lastEventRetry = meta?.retry ?? lastEventRetry; | ||
| const maybeEventIterator = await next({ error }); | ||
| if (!isAsyncIteratorObject(maybeEventIterator)) { | ||
| throw new ClientRetryPluginInvalidEventIteratorRetryResponse( | ||
| "RetryPlugin: Expected an Event Iterator, got a non-Event Iterator" | ||
| ); | ||
| } | ||
| current = maybeEventIterator; | ||
| if (isIteratorAborted) { | ||
| await current.return?.(); | ||
| throw error; | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| async (reason) => { | ||
| isIteratorAborted = true; | ||
| if (reason !== "next") { | ||
| await current.return?.(); | ||
| } | ||
| } | ||
| )); | ||
| }); | ||
| } | ||
| } | ||
| class RetryAfterPlugin { | ||
| condition; | ||
| maxAttempts; | ||
| timeout; | ||
| order = 19e5; | ||
| constructor(options = {}) { | ||
| this.condition = options.condition ?? ((response) => response.status === COMMON_ORPC_ERROR_DEFS.TOO_MANY_REQUESTS.status || response.status === COMMON_ORPC_ERROR_DEFS.SERVICE_UNAVAILABLE.status); | ||
| this.maxAttempts = options.maxAttempts ?? 3; | ||
| this.timeout = options.timeout ?? 5 * 60 * 1e3; | ||
| } | ||
| init(options) { | ||
| options.clientInterceptors ??= []; | ||
| options.clientInterceptors.push(async (interceptorOptions) => { | ||
| const startTime = Date.now(); | ||
| let attemptCount = 0; | ||
| while (true) { | ||
| attemptCount++; | ||
| const response = await interceptorOptions.next(); | ||
| if (!value(this.condition, response, interceptorOptions)) { | ||
| return response; | ||
| } | ||
| const retryAfterHeader = flattenHeader(response.headers["retry-after"]); | ||
| const retryAfterMs = this.parseRetryAfterHeader(retryAfterHeader); | ||
| if (retryAfterMs === void 0) { | ||
| return response; | ||
| } | ||
| if (attemptCount >= value(this.maxAttempts, response, interceptorOptions)) { | ||
| return response; | ||
| } | ||
| const timeoutMs = value(this.timeout, response, interceptorOptions); | ||
| const elapsedTime = Date.now() - startTime; | ||
| if (elapsedTime + retryAfterMs > timeoutMs) { | ||
| return response; | ||
| } | ||
| await this.delayExecution(retryAfterMs, interceptorOptions.signal); | ||
| if (interceptorOptions.signal?.aborted) { | ||
| return response; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| parseRetryAfterHeader(value2) { | ||
| value2 = value2?.trim(); | ||
| if (!value2) { | ||
| return void 0; | ||
| } | ||
| const seconds = Number(value2); | ||
| if (Number.isFinite(seconds)) { | ||
| return Math.max(0, seconds * 1e3); | ||
| } | ||
| const retryDate = Date.parse(value2); | ||
| if (!Number.isNaN(retryDate)) { | ||
| return Math.max(0, retryDate - Date.now()); | ||
| } | ||
| return void 0; | ||
| } | ||
| delayExecution(ms, signal) { | ||
| return new Promise((resolve) => { | ||
| if (signal?.aborted) { | ||
| resolve(); | ||
| return; | ||
| } | ||
| let timeout; | ||
| const onAbort = () => { | ||
| clearTimeout(timeout); | ||
| timeout = void 0; | ||
| resolve(); | ||
| }; | ||
| signal?.addEventListener("abort", onAbort, { once: true }); | ||
| timeout = setTimeout(() => { | ||
| signal?.removeEventListener("abort", onAbort); | ||
| resolve(); | ||
| }, ms); | ||
| }); | ||
| } | ||
| } | ||
| class SimpleCsrfProtectionLinkPlugin { | ||
| headerName; | ||
| headerValue; | ||
| exclude; | ||
| constructor(options = {}) { | ||
| this.headerName = options.headerName ?? "x-csrf-token"; | ||
| this.headerValue = options.headerValue ?? "orpc"; | ||
| this.exclude = options.exclude ?? false; | ||
| } | ||
| order = 8e6; | ||
| init(options) { | ||
| options.clientInterceptors ??= []; | ||
| options.clientInterceptors.push(async (options2) => { | ||
| const excluded = await value(this.exclude, options2); | ||
| if (excluded) { | ||
| return options2.next(); | ||
| } | ||
| const headerName = await value(this.headerName, options2); | ||
| const headerValue = await value(this.headerValue, options2); | ||
| return options2.next({ | ||
| ...options2, | ||
| request: { | ||
| ...options2.request, | ||
| headers: { | ||
| ...options2.request.headers, | ||
| [headerName]: headerValue | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| } | ||
| export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin }; |
| import { Interceptor } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { b as ClientContext, c as ClientOptions, C as ClientLink } from './client.i2uoJbEp.js'; | ||
| interface StandardLinkPlugin<T extends ClientContext> { | ||
| order?: number; | ||
| init?(options: StandardLinkOptions<T>): void; | ||
| } | ||
| declare class CompositeStandardLinkPlugin<T extends ClientContext, TPlugin extends StandardLinkPlugin<T>> implements StandardLinkPlugin<T> { | ||
| protected readonly plugins: TPlugin[]; | ||
| constructor(plugins?: readonly TPlugin[]); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface StandardLinkCodec<T extends ClientContext> { | ||
| encode(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<unknown>; | ||
| } | ||
| interface StandardLinkClient<T extends ClientContext> { | ||
| call(request: StandardRequest, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface StandardLinkInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| } | ||
| interface StandardLinkClientInterceptorOptions<T extends ClientContext> extends StandardLinkInterceptorOptions<T> { | ||
| request: StandardRequest; | ||
| } | ||
| interface StandardLinkOptions<T extends ClientContext> { | ||
| interceptors?: Interceptor<StandardLinkInterceptorOptions<T>, Promise<unknown>>[]; | ||
| clientInterceptors?: Interceptor<StandardLinkClientInterceptorOptions<T>, Promise<StandardLazyResponse>>[]; | ||
| plugins?: StandardLinkPlugin<T>[]; | ||
| } | ||
| declare class StandardLink<T extends ClientContext> implements ClientLink<T> { | ||
| readonly codec: StandardLinkCodec<T>; | ||
| readonly sender: StandardLinkClient<T>; | ||
| private readonly interceptors; | ||
| private readonly clientInterceptors; | ||
| constructor(codec: StandardLinkCodec<T>, sender: StandardLinkClient<T>, options?: StandardLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<unknown>; | ||
| } | ||
| export { CompositeStandardLinkPlugin as C, StandardLink as d }; | ||
| export type { StandardLinkClientInterceptorOptions as S, StandardLinkPlugin as a, StandardLinkOptions as b, StandardLinkInterceptorOptions as c, StandardLinkCodec as e, StandardLinkClient as f }; |
| import { b as ClientContext, c as ClientOptions, f as HTTPMethod } from './client.i2uoJbEp.js'; | ||
| import { e as StandardLinkCodec, b as StandardLinkOptions, d as StandardLink, f as StandardLinkClient } from './client.2jUAqzYU.js'; | ||
| import { Segment, Value, Promisable } from '@orpc/shared'; | ||
| import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| declare const STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES: { | ||
| readonly BIGINT: 0; | ||
| readonly DATE: 1; | ||
| readonly NAN: 2; | ||
| readonly UNDEFINED: 3; | ||
| readonly URL: 4; | ||
| readonly REGEXP: 5; | ||
| readonly SET: 6; | ||
| readonly MAP: 7; | ||
| }; | ||
| type StandardRPCJsonSerializedMetaItem = readonly [type: number, ...path: Segment[]]; | ||
| type StandardRPCJsonSerialized = [json: unknown, meta: StandardRPCJsonSerializedMetaItem[], maps: Segment[][], blobs: Blob[]]; | ||
| interface StandardRPCCustomJsonSerializer { | ||
| type: number; | ||
| condition(data: unknown): boolean; | ||
| serialize(data: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| } | ||
| interface StandardRPCJsonSerializerOptions { | ||
| customJsonSerializers?: readonly StandardRPCCustomJsonSerializer[]; | ||
| } | ||
| declare class StandardRPCJsonSerializer { | ||
| private readonly customSerializers; | ||
| constructor(options?: StandardRPCJsonSerializerOptions); | ||
| serialize(data: unknown, segments?: Segment[], meta?: StandardRPCJsonSerializedMetaItem[], maps?: Segment[][], blobs?: Blob[]): StandardRPCJsonSerialized; | ||
| deserialize(json: unknown, meta: readonly StandardRPCJsonSerializedMetaItem[]): unknown; | ||
| deserialize(json: unknown, meta: readonly StandardRPCJsonSerializedMetaItem[], maps: readonly Segment[][], getBlob: (index: number) => Blob): unknown; | ||
| } | ||
| declare class StandardRPCSerializer { | ||
| #private; | ||
| private readonly jsonSerializer; | ||
| constructor(jsonSerializer: StandardRPCJsonSerializer); | ||
| serialize(data: unknown): object; | ||
| deserialize(data: unknown): unknown; | ||
| } | ||
| interface StandardRPCLinkCodecOptions<T extends ClientContext> { | ||
| /** | ||
| * Base url for all requests. | ||
| */ | ||
| url: Value<Promisable<string | URL>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<Promisable<number>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The method used to make the request. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| method?: Value<Promisable<Exclude<HTTPMethod, 'HEAD'>>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The method to use when the payload cannot safely pass to the server with method return from method function. | ||
| * GET is not allowed, it's very dangerous. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| fallbackMethod?: Exclude<HTTPMethod, 'HEAD' | 'GET'>; | ||
| /** | ||
| * Inject headers to the request. | ||
| */ | ||
| headers?: Value<Promisable<StandardHeaders | Headers>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| } | ||
| declare class StandardRPCLinkCodec<T extends ClientContext> implements StandardLinkCodec<T> { | ||
| private readonly serializer; | ||
| private readonly baseUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly fallbackMethod; | ||
| private readonly expectedMethod; | ||
| private readonly headers; | ||
| constructor(serializer: StandardRPCSerializer, options: StandardRPCLinkCodecOptions<T>); | ||
| encode(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse): Promise<unknown>; | ||
| } | ||
| interface StandardRPCLinkOptions<T extends ClientContext> extends StandardLinkOptions<T>, StandardRPCLinkCodecOptions<T>, StandardRPCJsonSerializerOptions { | ||
| } | ||
| declare class StandardRPCLink<T extends ClientContext> extends StandardLink<T> { | ||
| constructor(linkClient: StandardLinkClient<T>, options: StandardRPCLinkOptions<T>); | ||
| } | ||
| export { STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES as S, StandardRPCJsonSerializer as e, StandardRPCLink as g, StandardRPCLinkCodec as i, StandardRPCSerializer as j }; | ||
| export type { StandardRPCJsonSerializedMetaItem as a, StandardRPCJsonSerialized as b, StandardRPCCustomJsonSerializer as c, StandardRPCJsonSerializerOptions as d, StandardRPCLinkOptions as f, StandardRPCLinkCodecOptions as h }; |
| import { b as ClientContext, c as ClientOptions, f as HTTPMethod } from './client.i2uoJbEp.mjs'; | ||
| import { e as StandardLinkCodec, b as StandardLinkOptions, d as StandardLink, f as StandardLinkClient } from './client.CpCa3si8.mjs'; | ||
| import { Segment, Value, Promisable } from '@orpc/shared'; | ||
| import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| declare const STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES: { | ||
| readonly BIGINT: 0; | ||
| readonly DATE: 1; | ||
| readonly NAN: 2; | ||
| readonly UNDEFINED: 3; | ||
| readonly URL: 4; | ||
| readonly REGEXP: 5; | ||
| readonly SET: 6; | ||
| readonly MAP: 7; | ||
| }; | ||
| type StandardRPCJsonSerializedMetaItem = readonly [type: number, ...path: Segment[]]; | ||
| type StandardRPCJsonSerialized = [json: unknown, meta: StandardRPCJsonSerializedMetaItem[], maps: Segment[][], blobs: Blob[]]; | ||
| interface StandardRPCCustomJsonSerializer { | ||
| type: number; | ||
| condition(data: unknown): boolean; | ||
| serialize(data: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| } | ||
| interface StandardRPCJsonSerializerOptions { | ||
| customJsonSerializers?: readonly StandardRPCCustomJsonSerializer[]; | ||
| } | ||
| declare class StandardRPCJsonSerializer { | ||
| private readonly customSerializers; | ||
| constructor(options?: StandardRPCJsonSerializerOptions); | ||
| serialize(data: unknown, segments?: Segment[], meta?: StandardRPCJsonSerializedMetaItem[], maps?: Segment[][], blobs?: Blob[]): StandardRPCJsonSerialized; | ||
| deserialize(json: unknown, meta: readonly StandardRPCJsonSerializedMetaItem[]): unknown; | ||
| deserialize(json: unknown, meta: readonly StandardRPCJsonSerializedMetaItem[], maps: readonly Segment[][], getBlob: (index: number) => Blob): unknown; | ||
| } | ||
| declare class StandardRPCSerializer { | ||
| #private; | ||
| private readonly jsonSerializer; | ||
| constructor(jsonSerializer: StandardRPCJsonSerializer); | ||
| serialize(data: unknown): object; | ||
| deserialize(data: unknown): unknown; | ||
| } | ||
| interface StandardRPCLinkCodecOptions<T extends ClientContext> { | ||
| /** | ||
| * Base url for all requests. | ||
| */ | ||
| url: Value<Promisable<string | URL>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<Promisable<number>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The method used to make the request. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| method?: Value<Promisable<Exclude<HTTPMethod, 'HEAD'>>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| /** | ||
| * The method to use when the payload cannot safely pass to the server with method return from method function. | ||
| * GET is not allowed, it's very dangerous. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| fallbackMethod?: Exclude<HTTPMethod, 'HEAD' | 'GET'>; | ||
| /** | ||
| * Inject headers to the request. | ||
| */ | ||
| headers?: Value<Promisable<StandardHeaders | Headers>, [options: ClientOptions<T>, path: readonly string[], input: unknown]>; | ||
| } | ||
| declare class StandardRPCLinkCodec<T extends ClientContext> implements StandardLinkCodec<T> { | ||
| private readonly serializer; | ||
| private readonly baseUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly fallbackMethod; | ||
| private readonly expectedMethod; | ||
| private readonly headers; | ||
| constructor(serializer: StandardRPCSerializer, options: StandardRPCLinkCodecOptions<T>); | ||
| encode(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse): Promise<unknown>; | ||
| } | ||
| interface StandardRPCLinkOptions<T extends ClientContext> extends StandardLinkOptions<T>, StandardRPCLinkCodecOptions<T>, StandardRPCJsonSerializerOptions { | ||
| } | ||
| declare class StandardRPCLink<T extends ClientContext> extends StandardLink<T> { | ||
| constructor(linkClient: StandardLinkClient<T>, options: StandardRPCLinkOptions<T>); | ||
| } | ||
| export { STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES as S, StandardRPCJsonSerializer as e, StandardRPCLink as g, StandardRPCLinkCodec as i, StandardRPCSerializer as j }; | ||
| export type { StandardRPCJsonSerializedMetaItem as a, StandardRPCJsonSerialized as b, StandardRPCCustomJsonSerializer as c, StandardRPCJsonSerializerOptions as d, StandardRPCLinkOptions as f, StandardRPCLinkCodecOptions as h }; |
| import { AsyncIteratorClass, isTypescriptObject } from '@orpc/shared'; | ||
| import { getEventMeta, withEventMeta } from '@orpc/standard-server'; | ||
| function mapEventIterator(iterator, maps) { | ||
| const mapError = async (error) => { | ||
| let mappedError = await maps.error(error); | ||
| if (mappedError !== error) { | ||
| const meta = getEventMeta(error); | ||
| if (meta && isTypescriptObject(mappedError)) { | ||
| mappedError = withEventMeta(mappedError, meta); | ||
| } | ||
| } | ||
| return mappedError; | ||
| }; | ||
| return new AsyncIteratorClass(async () => { | ||
| const { done, value } = await (async () => { | ||
| try { | ||
| return await iterator.next(); | ||
| } catch (error) { | ||
| throw await mapError(error); | ||
| } | ||
| })(); | ||
| let mappedValue = await maps.value(value, done); | ||
| if (mappedValue !== value) { | ||
| const meta = getEventMeta(value); | ||
| if (meta && isTypescriptObject(mappedValue)) { | ||
| mappedValue = withEventMeta(mappedValue, meta); | ||
| } | ||
| } | ||
| return { done, value: mappedValue }; | ||
| }, async () => { | ||
| try { | ||
| await iterator.return?.(); | ||
| } catch (error) { | ||
| throw await mapError(error); | ||
| } | ||
| }); | ||
| } | ||
| export { mapEventIterator as m }; |
| import { toArray, runWithSpan, ORPC_NAME, isAsyncIteratorObject, asyncIteratorWithSpan, intercept, getGlobalOtelConfig, isObject, value, stringifyJSON } from '@orpc/shared'; | ||
| import { mergeStandardHeaders, ErrorEvent } from '@orpc/standard-server'; | ||
| import { C as COMMON_ORPC_ERROR_DEFS, d as isORPCErrorStatus, e as isORPCErrorJson, g as createORPCErrorFromJson, c as ORPCError, t as toORPCError } from './client.CPTihQgC.mjs'; | ||
| import { toStandardHeaders as toStandardHeaders$1 } from '@orpc/standard-server-fetch'; | ||
| import { m as mapEventIterator } from './client.BLtwTQUg.mjs'; | ||
| class CompositeStandardLinkPlugin { | ||
| plugins; | ||
| constructor(plugins = []) { | ||
| this.plugins = [...plugins].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); | ||
| } | ||
| init(options) { | ||
| for (const plugin of this.plugins) { | ||
| plugin.init?.(options); | ||
| } | ||
| } | ||
| } | ||
| class StandardLink { | ||
| constructor(codec, sender, options = {}) { | ||
| this.codec = codec; | ||
| this.sender = sender; | ||
| const plugin = new CompositeStandardLinkPlugin(options.plugins); | ||
| plugin.init(options); | ||
| this.interceptors = toArray(options.interceptors); | ||
| this.clientInterceptors = toArray(options.clientInterceptors); | ||
| } | ||
| interceptors; | ||
| clientInterceptors; | ||
| call(path, input, options) { | ||
| return runWithSpan( | ||
| { name: `${ORPC_NAME}.${path.join("/")}`, signal: options.signal }, | ||
| (span) => { | ||
| span?.setAttribute("rpc.system", ORPC_NAME); | ||
| span?.setAttribute("rpc.method", path.join(".")); | ||
| if (isAsyncIteratorObject(input)) { | ||
| input = asyncIteratorWithSpan( | ||
| { name: "consume_event_iterator_input", signal: options.signal }, | ||
| input | ||
| ); | ||
| } | ||
| return intercept(this.interceptors, { ...options, path, input }, async ({ path: path2, input: input2, ...options2 }) => { | ||
| const otelConfig = getGlobalOtelConfig(); | ||
| let otelContext; | ||
| const currentSpan = otelConfig?.trace.getActiveSpan() ?? span; | ||
| if (currentSpan && otelConfig) { | ||
| otelContext = otelConfig?.trace.setSpan(otelConfig.context.active(), currentSpan); | ||
| } | ||
| const request = await runWithSpan( | ||
| { name: "encode_request", context: otelContext }, | ||
| () => this.codec.encode(path2, input2, options2) | ||
| ); | ||
| const response = await intercept( | ||
| this.clientInterceptors, | ||
| { ...options2, input: input2, path: path2, request }, | ||
| ({ input: input3, path: path3, request: request2, ...options3 }) => { | ||
| return runWithSpan( | ||
| { name: "send_request", signal: options3.signal, context: otelContext }, | ||
| () => this.sender.call(request2, options3, path3, input3) | ||
| ); | ||
| } | ||
| ); | ||
| const output = await runWithSpan( | ||
| { name: "decode_response", context: otelContext }, | ||
| () => this.codec.decode(response, options2, path2, input2) | ||
| ); | ||
| if (isAsyncIteratorObject(output)) { | ||
| return asyncIteratorWithSpan( | ||
| { name: "consume_event_iterator_output", signal: options2.signal }, | ||
| output | ||
| ); | ||
| } | ||
| return output; | ||
| }); | ||
| } | ||
| ); | ||
| } | ||
| } | ||
| const STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES = { | ||
| BIGINT: 0, | ||
| DATE: 1, | ||
| NAN: 2, | ||
| UNDEFINED: 3, | ||
| URL: 4, | ||
| REGEXP: 5, | ||
| SET: 6, | ||
| MAP: 7 | ||
| }; | ||
| class StandardRPCJsonSerializer { | ||
| customSerializers; | ||
| constructor(options = {}) { | ||
| this.customSerializers = options.customJsonSerializers ?? []; | ||
| if (this.customSerializers.length !== new Set(this.customSerializers.map((custom) => custom.type)).size) { | ||
| throw new Error("Custom serializer type must be unique."); | ||
| } | ||
| } | ||
| serialize(data, segments = [], meta = [], maps = [], blobs = []) { | ||
| for (const custom of this.customSerializers) { | ||
| if (custom.condition(data)) { | ||
| const result = this.serialize(custom.serialize(data), segments, meta, maps, blobs); | ||
| meta.push([custom.type, ...segments]); | ||
| return result; | ||
| } | ||
| } | ||
| if (data instanceof Blob) { | ||
| maps.push(segments); | ||
| blobs.push(data); | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| if (typeof data === "bigint") { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.BIGINT, ...segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof Date) { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.DATE, ...segments]); | ||
| if (Number.isNaN(data.getTime())) { | ||
| return [null, meta, maps, blobs]; | ||
| } | ||
| return [data.toISOString(), meta, maps, blobs]; | ||
| } | ||
| if (Number.isNaN(data)) { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.NAN, ...segments]); | ||
| return [null, meta, maps, blobs]; | ||
| } | ||
| if (data instanceof URL) { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.URL, ...segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof RegExp) { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.REGEXP, ...segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof Set) { | ||
| const result = this.serialize(Array.from(data), segments, meta, maps, blobs); | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.SET, ...segments]); | ||
| return result; | ||
| } | ||
| if (data instanceof Map) { | ||
| const result = this.serialize(Array.from(data.entries()), segments, meta, maps, blobs); | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.MAP, ...segments]); | ||
| return result; | ||
| } | ||
| if (Array.isArray(data)) { | ||
| const json = data.map((v, i) => { | ||
| if (v === void 0) { | ||
| meta.push([STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.UNDEFINED, ...segments, i]); | ||
| return null; | ||
| } | ||
| return this.serialize(v, [...segments, i], meta, maps, blobs)[0]; | ||
| }); | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| if (isObject(data)) { | ||
| const json = {}; | ||
| for (const k in data) { | ||
| if (k === "toJSON" && typeof data[k] === "function") { | ||
| continue; | ||
| } | ||
| json[k] = this.serialize(data[k], [...segments, k], meta, maps, blobs)[0]; | ||
| } | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| deserialize(json, meta, maps, getBlob) { | ||
| const ref = { data: json }; | ||
| if (maps && getBlob) { | ||
| maps.forEach((segments, i) => { | ||
| let currentRef = ref; | ||
| let preSegment = "data"; | ||
| segments.forEach((segment) => { | ||
| currentRef = currentRef[preSegment]; | ||
| preSegment = segment; | ||
| if (!Object.hasOwn(currentRef, preSegment)) { | ||
| throw new Error(`Security error: accessing non-existent path during deserialization. Path segment: ${preSegment}`); | ||
| } | ||
| }); | ||
| currentRef[preSegment] = getBlob(i); | ||
| }); | ||
| } | ||
| for (const item of meta) { | ||
| const type = item[0]; | ||
| let currentRef = ref; | ||
| let preSegment = "data"; | ||
| for (let i = 1; i < item.length; i++) { | ||
| currentRef = currentRef[preSegment]; | ||
| preSegment = item[i]; | ||
| if (!Object.hasOwn(currentRef, preSegment)) { | ||
| throw new Error(`Security error: accessing non-existent path during deserialization. Path segment: ${preSegment}`); | ||
| } | ||
| } | ||
| for (const custom of this.customSerializers) { | ||
| if (custom.type === type) { | ||
| currentRef[preSegment] = custom.deserialize(currentRef[preSegment]); | ||
| break; | ||
| } | ||
| } | ||
| switch (type) { | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.BIGINT: | ||
| currentRef[preSegment] = BigInt(currentRef[preSegment]); | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.DATE: | ||
| currentRef[preSegment] = new Date(currentRef[preSegment] ?? "Invalid Date"); | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.NAN: | ||
| currentRef[preSegment] = Number.NaN; | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.UNDEFINED: | ||
| currentRef[preSegment] = void 0; | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.URL: | ||
| currentRef[preSegment] = new URL(currentRef[preSegment]); | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.REGEXP: { | ||
| const [, pattern, flags] = currentRef[preSegment].match(/^\/(.*)\/([a-z]*)$/); | ||
| currentRef[preSegment] = new RegExp(pattern, flags); | ||
| break; | ||
| } | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.SET: | ||
| currentRef[preSegment] = new Set(currentRef[preSegment]); | ||
| break; | ||
| case STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES.MAP: | ||
| currentRef[preSegment] = new Map(currentRef[preSegment]); | ||
| break; | ||
| } | ||
| } | ||
| return ref.data; | ||
| } | ||
| } | ||
| function toHttpPath(path) { | ||
| return `/${path.map(encodeURIComponent).join("/")}`; | ||
| } | ||
| function toStandardHeaders(headers) { | ||
| if (typeof headers.forEach === "function") { | ||
| return toStandardHeaders$1(headers); | ||
| } | ||
| return headers; | ||
| } | ||
| function getMalformedResponseErrorCode(status) { | ||
| return Object.entries(COMMON_ORPC_ERROR_DEFS).find(([, def]) => def.status === status)?.[0] ?? "MALFORMED_ORPC_ERROR_RESPONSE"; | ||
| } | ||
| class StandardRPCLinkCodec { | ||
| constructor(serializer, options) { | ||
| this.serializer = serializer; | ||
| this.baseUrl = options.url; | ||
| this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
| this.fallbackMethod = options.fallbackMethod ?? "POST"; | ||
| this.expectedMethod = options.method ?? this.fallbackMethod; | ||
| this.headers = options.headers ?? {}; | ||
| } | ||
| baseUrl; | ||
| maxUrlLength; | ||
| fallbackMethod; | ||
| expectedMethod; | ||
| headers; | ||
| async encode(path, input, options) { | ||
| let headers = toStandardHeaders(await value(this.headers, options, path, input)); | ||
| if (options.lastEventId !== void 0) { | ||
| headers = mergeStandardHeaders(headers, { "last-event-id": options.lastEventId }); | ||
| } | ||
| const expectedMethod = await value(this.expectedMethod, options, path, input); | ||
| const baseUrl = await value(this.baseUrl, options, path, input); | ||
| const url = new URL(baseUrl); | ||
| url.pathname = `${url.pathname.replace(/\/$/, "")}${toHttpPath(path)}`; | ||
| const serialized = this.serializer.serialize(input); | ||
| if (expectedMethod === "GET" && !(serialized instanceof FormData) && !isAsyncIteratorObject(serialized)) { | ||
| const maxUrlLength = await value(this.maxUrlLength, options, path, input); | ||
| const getUrl = new URL(url); | ||
| getUrl.searchParams.append("data", stringifyJSON(serialized)); | ||
| if (getUrl.toString().length <= maxUrlLength) { | ||
| return { | ||
| body: void 0, | ||
| method: expectedMethod, | ||
| headers, | ||
| url: getUrl, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| } | ||
| return { | ||
| url, | ||
| method: expectedMethod === "GET" ? this.fallbackMethod : expectedMethod, | ||
| headers, | ||
| body: serialized, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| async decode(response) { | ||
| const isOk = !isORPCErrorStatus(response.status); | ||
| const deserialized = await (async () => { | ||
| let isBodyOk = false; | ||
| try { | ||
| const body = await response.body(); | ||
| isBodyOk = true; | ||
| return this.serializer.deserialize(body); | ||
| } catch (error) { | ||
| if (!isBodyOk) { | ||
| throw new Error("Cannot parse response body, please check the response body and content-type.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| throw new Error("Invalid RPC response format.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| })(); | ||
| if (!isOk) { | ||
| if (isORPCErrorJson(deserialized)) { | ||
| throw createORPCErrorFromJson(deserialized); | ||
| } | ||
| throw new ORPCError(getMalformedResponseErrorCode(response.status), { | ||
| status: response.status, | ||
| data: { ...response, body: deserialized } | ||
| }); | ||
| } | ||
| return deserialized; | ||
| } | ||
| } | ||
| class StandardRPCSerializer { | ||
| constructor(jsonSerializer) { | ||
| this.jsonSerializer = jsonSerializer; | ||
| } | ||
| serialize(data) { | ||
| if (isAsyncIteratorObject(data)) { | ||
| return mapEventIterator(data, { | ||
| value: async (value) => this.#serialize(value, false), | ||
| error: async (e) => { | ||
| return new ErrorEvent({ | ||
| data: this.#serialize(toORPCError(e).toJSON(), false), | ||
| cause: e | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| return this.#serialize(data, true); | ||
| } | ||
| #serialize(data, enableFormData) { | ||
| const [json, meta_, maps, blobs] = this.jsonSerializer.serialize(data); | ||
| const meta = meta_.length === 0 ? void 0 : meta_; | ||
| if (!enableFormData || blobs.length === 0) { | ||
| return { | ||
| json, | ||
| meta | ||
| }; | ||
| } | ||
| const form = new FormData(); | ||
| form.set("data", stringifyJSON({ json, meta, maps })); | ||
| blobs.forEach((blob, i) => { | ||
| form.set(i.toString(), blob); | ||
| }); | ||
| return form; | ||
| } | ||
| deserialize(data) { | ||
| if (isAsyncIteratorObject(data)) { | ||
| return mapEventIterator(data, { | ||
| value: async (value) => this.#deserialize(value), | ||
| error: async (e) => { | ||
| if (!(e instanceof ErrorEvent)) { | ||
| return e; | ||
| } | ||
| const deserialized = this.#deserialize(e.data); | ||
| if (isORPCErrorJson(deserialized)) { | ||
| return createORPCErrorFromJson(deserialized, { cause: e }); | ||
| } | ||
| return new ErrorEvent({ | ||
| data: deserialized, | ||
| cause: e | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| return this.#deserialize(data); | ||
| } | ||
| #deserialize(data) { | ||
| if (data === void 0) { | ||
| return void 0; | ||
| } | ||
| if (!(data instanceof FormData)) { | ||
| return this.jsonSerializer.deserialize(data.json, data.meta ?? []); | ||
| } | ||
| const serialized = JSON.parse(data.get("data")); | ||
| return this.jsonSerializer.deserialize( | ||
| serialized.json, | ||
| serialized.meta ?? [], | ||
| serialized.maps, | ||
| (i) => data.get(i.toString()) | ||
| ); | ||
| } | ||
| } | ||
| class StandardRPCLink extends StandardLink { | ||
| constructor(linkClient, options) { | ||
| const jsonSerializer = new StandardRPCJsonSerializer(options); | ||
| const serializer = new StandardRPCSerializer(jsonSerializer); | ||
| const linkCodec = new StandardRPCLinkCodec(serializer, options); | ||
| super(linkCodec, linkClient, options); | ||
| } | ||
| } | ||
| export { CompositeStandardLinkPlugin as C, StandardLink as S, STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES as a, StandardRPCJsonSerializer as b, StandardRPCLink as c, StandardRPCLinkCodec as d, StandardRPCSerializer as e, toStandardHeaders as f, getMalformedResponseErrorCode as g, toHttpPath as t }; |
| import { Interceptor } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { b as ClientContext, c as ClientOptions, C as ClientLink } from './client.i2uoJbEp.mjs'; | ||
| interface StandardLinkPlugin<T extends ClientContext> { | ||
| order?: number; | ||
| init?(options: StandardLinkOptions<T>): void; | ||
| } | ||
| declare class CompositeStandardLinkPlugin<T extends ClientContext, TPlugin extends StandardLinkPlugin<T>> implements StandardLinkPlugin<T> { | ||
| protected readonly plugins: TPlugin[]; | ||
| constructor(plugins?: readonly TPlugin[]); | ||
| init(options: StandardLinkOptions<T>): void; | ||
| } | ||
| interface StandardLinkCodec<T extends ClientContext> { | ||
| encode(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<unknown>; | ||
| } | ||
| interface StandardLinkClient<T extends ClientContext> { | ||
| call(request: StandardRequest, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface StandardLinkInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| } | ||
| interface StandardLinkClientInterceptorOptions<T extends ClientContext> extends StandardLinkInterceptorOptions<T> { | ||
| request: StandardRequest; | ||
| } | ||
| interface StandardLinkOptions<T extends ClientContext> { | ||
| interceptors?: Interceptor<StandardLinkInterceptorOptions<T>, Promise<unknown>>[]; | ||
| clientInterceptors?: Interceptor<StandardLinkClientInterceptorOptions<T>, Promise<StandardLazyResponse>>[]; | ||
| plugins?: StandardLinkPlugin<T>[]; | ||
| } | ||
| declare class StandardLink<T extends ClientContext> implements ClientLink<T> { | ||
| readonly codec: StandardLinkCodec<T>; | ||
| readonly sender: StandardLinkClient<T>; | ||
| private readonly interceptors; | ||
| private readonly clientInterceptors; | ||
| constructor(codec: StandardLinkCodec<T>, sender: StandardLinkClient<T>, options?: StandardLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptions<T>): Promise<unknown>; | ||
| } | ||
| export { CompositeStandardLinkPlugin as C, StandardLink as d }; | ||
| export type { StandardLinkClientInterceptorOptions as S, StandardLinkPlugin as a, StandardLinkOptions as b, StandardLinkInterceptorOptions as c, StandardLinkCodec as e, StandardLinkClient as f }; |
| import { resolveMaybeOptionalOptions, getConstructor, isObject } from '@orpc/shared'; | ||
| const ORPC_CLIENT_PACKAGE_NAME = "@orpc/client"; | ||
| const ORPC_CLIENT_PACKAGE_VERSION = "0.0.0-next.99d5d75"; | ||
| const COMMON_ORPC_ERROR_DEFS = { | ||
| BAD_REQUEST: { | ||
| status: 400, | ||
| message: "Bad Request" | ||
| }, | ||
| UNAUTHORIZED: { | ||
| status: 401, | ||
| message: "Unauthorized" | ||
| }, | ||
| FORBIDDEN: { | ||
| status: 403, | ||
| message: "Forbidden" | ||
| }, | ||
| NOT_FOUND: { | ||
| status: 404, | ||
| message: "Not Found" | ||
| }, | ||
| METHOD_NOT_SUPPORTED: { | ||
| status: 405, | ||
| message: "Method Not Supported" | ||
| }, | ||
| NOT_ACCEPTABLE: { | ||
| status: 406, | ||
| message: "Not Acceptable" | ||
| }, | ||
| TIMEOUT: { | ||
| status: 408, | ||
| message: "Request Timeout" | ||
| }, | ||
| CONFLICT: { | ||
| status: 409, | ||
| message: "Conflict" | ||
| }, | ||
| PRECONDITION_FAILED: { | ||
| status: 412, | ||
| message: "Precondition Failed" | ||
| }, | ||
| PAYLOAD_TOO_LARGE: { | ||
| status: 413, | ||
| message: "Payload Too Large" | ||
| }, | ||
| UNSUPPORTED_MEDIA_TYPE: { | ||
| status: 415, | ||
| message: "Unsupported Media Type" | ||
| }, | ||
| UNPROCESSABLE_CONTENT: { | ||
| status: 422, | ||
| message: "Unprocessable Content" | ||
| }, | ||
| TOO_MANY_REQUESTS: { | ||
| status: 429, | ||
| message: "Too Many Requests" | ||
| }, | ||
| CLIENT_CLOSED_REQUEST: { | ||
| status: 499, | ||
| message: "Client Closed Request" | ||
| }, | ||
| INTERNAL_SERVER_ERROR: { | ||
| status: 500, | ||
| message: "Internal Server Error" | ||
| }, | ||
| NOT_IMPLEMENTED: { | ||
| status: 501, | ||
| message: "Not Implemented" | ||
| }, | ||
| BAD_GATEWAY: { | ||
| status: 502, | ||
| message: "Bad Gateway" | ||
| }, | ||
| SERVICE_UNAVAILABLE: { | ||
| status: 503, | ||
| message: "Service Unavailable" | ||
| }, | ||
| GATEWAY_TIMEOUT: { | ||
| status: 504, | ||
| message: "Gateway Timeout" | ||
| } | ||
| }; | ||
| function fallbackORPCErrorStatus(code, status) { | ||
| return status ?? COMMON_ORPC_ERROR_DEFS[code]?.status ?? 500; | ||
| } | ||
| function fallbackORPCErrorMessage(code, message) { | ||
| return message || COMMON_ORPC_ERROR_DEFS[code]?.message || code; | ||
| } | ||
| const GLOBAL_ORPC_ERROR_CONSTRUCTORS_SYMBOL = Symbol.for(`__${ORPC_CLIENT_PACKAGE_NAME}@${ORPC_CLIENT_PACKAGE_VERSION}/error/ORPC_ERROR_CONSTRUCTORS__`); | ||
| void (globalThis[GLOBAL_ORPC_ERROR_CONSTRUCTORS_SYMBOL] ??= /* @__PURE__ */ new WeakSet()); | ||
| const globalORPCErrorConstructors = globalThis[GLOBAL_ORPC_ERROR_CONSTRUCTORS_SYMBOL]; | ||
| class ORPCError extends Error { | ||
| defined; | ||
| code; | ||
| status; | ||
| data; | ||
| constructor(code, ...rest) { | ||
| const options = resolveMaybeOptionalOptions(rest); | ||
| if (options.status !== void 0 && !isORPCErrorStatus(options.status)) { | ||
| throw new Error("[ORPCError] Invalid error status code."); | ||
| } | ||
| const message = fallbackORPCErrorMessage(code, options.message); | ||
| super(message, options); | ||
| this.code = code; | ||
| this.status = fallbackORPCErrorStatus(code, options.status); | ||
| this.defined = options.defined ?? false; | ||
| this.data = options.data; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| defined: this.defined, | ||
| code: this.code, | ||
| status: this.status, | ||
| message: this.message, | ||
| data: this.data | ||
| }; | ||
| } | ||
| /** | ||
| * Workaround for Next.js where different contexts use separate | ||
| * dependency graphs, causing multiple ORPCError constructors existing and breaking | ||
| * `instanceof` checks across contexts. | ||
| * | ||
| * This is particularly problematic with "Optimized SSR", where orpc-client | ||
| * executes in one context but is invoked from another. When an error is thrown | ||
| * in the execution context, `instanceof ORPCError` checks fail in the | ||
| * invocation context due to separate class constructors. | ||
| * | ||
| * @todo Remove this and related code if Next.js resolves the multiple dependency graph issue. | ||
| */ | ||
| static [Symbol.hasInstance](instance) { | ||
| if (globalORPCErrorConstructors.has(this)) { | ||
| const constructor = getConstructor(instance); | ||
| if (constructor && globalORPCErrorConstructors.has(constructor)) { | ||
| return true; | ||
| } | ||
| } | ||
| return super[Symbol.hasInstance](instance); | ||
| } | ||
| } | ||
| globalORPCErrorConstructors.add(ORPCError); | ||
| function isDefinedError(error) { | ||
| return error instanceof ORPCError && error.defined; | ||
| } | ||
| function toORPCError(error) { | ||
| return error instanceof ORPCError ? error : new ORPCError("INTERNAL_SERVER_ERROR", { | ||
| message: "Internal server error", | ||
| cause: error | ||
| }); | ||
| } | ||
| function isORPCErrorStatus(status) { | ||
| return status < 200 || status >= 400; | ||
| } | ||
| function isORPCErrorJson(json) { | ||
| if (!isObject(json)) { | ||
| return false; | ||
| } | ||
| const validKeys = ["defined", "code", "status", "message", "data"]; | ||
| if (Object.keys(json).some((k) => !validKeys.includes(k))) { | ||
| return false; | ||
| } | ||
| return "defined" in json && typeof json.defined === "boolean" && "code" in json && typeof json.code === "string" && "status" in json && typeof json.status === "number" && isORPCErrorStatus(json.status) && "message" in json && typeof json.message === "string"; | ||
| } | ||
| function createORPCErrorFromJson(json, options = {}) { | ||
| return new ORPCError(json.code, { | ||
| ...options, | ||
| ...json | ||
| }); | ||
| } | ||
| export { COMMON_ORPC_ERROR_DEFS as C, ORPC_CLIENT_PACKAGE_NAME as O, ORPC_CLIENT_PACKAGE_VERSION as a, fallbackORPCErrorMessage as b, ORPCError as c, isORPCErrorStatus as d, isORPCErrorJson as e, fallbackORPCErrorStatus as f, createORPCErrorFromJson as g, isDefinedError as i, toORPCError as t }; |
| import { PromiseWithError } from '@orpc/shared'; | ||
| type HTTPPath = `/${string}`; | ||
| type HTTPMethod = 'HEAD' | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; | ||
| type ClientContext = Record<PropertyKey, any>; | ||
| interface ClientOptions<T extends ClientContext> { | ||
| signal?: AbortSignal; | ||
| lastEventId?: string | undefined; | ||
| context: T; | ||
| } | ||
| type FriendlyClientOptions<T extends ClientContext> = Omit<ClientOptions<T>, 'context'> & (Record<never, never> extends T ? { | ||
| context?: T; | ||
| } : { | ||
| context: T; | ||
| }); | ||
| type ClientRest<TClientContext extends ClientContext, TInput> = Record<never, never> extends TClientContext ? undefined extends TInput ? [input?: TInput, options?: FriendlyClientOptions<TClientContext>] : [input: TInput, options?: FriendlyClientOptions<TClientContext>] : [input: TInput, options: FriendlyClientOptions<TClientContext>]; | ||
| type ClientPromiseResult<TOutput, TError> = PromiseWithError<TOutput, TError>; | ||
| interface Client<TClientContext extends ClientContext, TInput, TOutput, TError> { | ||
| (...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>; | ||
| } | ||
| type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | { | ||
| [k: string]: NestedClient<TClientContext>; | ||
| }; | ||
| type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never; | ||
| interface ClientLink<TClientContext extends ClientContext> { | ||
| call: (path: readonly string[], input: unknown, options: ClientOptions<TClientContext>) => Promise<unknown>; | ||
| } | ||
| /** | ||
| * Recursively infers the **input types** from a client. | ||
| * | ||
| * Produces a nested map where each endpoint's input type is preserved. | ||
| */ | ||
| type InferClientInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientInputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **body input types** from a client. | ||
| * | ||
| * If an endpoint's input includes `{ body: ... }`, only the `body` portion is extracted. | ||
| * Produces a nested map of body input types. | ||
| */ | ||
| type InferClientBodyInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U extends { | ||
| body: infer UBody; | ||
| } ? UBody : U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyInputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **output types** from a client. | ||
| * | ||
| * Produces a nested map where each endpoint's output type is preserved. | ||
| */ | ||
| type InferClientOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientOutputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **body output types** from a client. | ||
| * | ||
| * If an endpoint's output includes `{ body: ... }`, only the `body` portion is extracted. | ||
| * Produces a nested map of body output types. | ||
| */ | ||
| type InferClientBodyOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U extends { | ||
| body: infer UBody; | ||
| } ? UBody : U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyOutputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **error types** from a client when you use [type-safe errors](https://orpc.dev/docs/error-handling#type‐safe-error-handling). | ||
| * | ||
| * Produces a nested map where each endpoint's error type is preserved. | ||
| */ | ||
| type InferClientErrors<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrors<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers a **union of all error types** from a client when you use [type-safe errors](https://orpc.dev/docs/error-handling#type‐safe-error-handling). | ||
| * | ||
| * Useful when you want to handle all possible errors from any endpoint at once. | ||
| */ | ||
| type InferClientErrorUnion<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrorUnion<T[K]> : never; | ||
| }[keyof T]; | ||
| export type { ClientLink as C, FriendlyClientOptions as F, HTTPPath as H, InferClientContext as I, NestedClient as N, ClientPromiseResult as a, ClientContext as b, ClientOptions as c, Client as d, ClientRest as e, HTTPMethod as f, InferClientInputs as g, InferClientBodyInputs as h, InferClientOutputs as i, InferClientBodyOutputs as j, InferClientErrors as k, InferClientErrorUnion as l }; |
| import { PromiseWithError } from '@orpc/shared'; | ||
| type HTTPPath = `/${string}`; | ||
| type HTTPMethod = 'HEAD' | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; | ||
| type ClientContext = Record<PropertyKey, any>; | ||
| interface ClientOptions<T extends ClientContext> { | ||
| signal?: AbortSignal; | ||
| lastEventId?: string | undefined; | ||
| context: T; | ||
| } | ||
| type FriendlyClientOptions<T extends ClientContext> = Omit<ClientOptions<T>, 'context'> & (Record<never, never> extends T ? { | ||
| context?: T; | ||
| } : { | ||
| context: T; | ||
| }); | ||
| type ClientRest<TClientContext extends ClientContext, TInput> = Record<never, never> extends TClientContext ? undefined extends TInput ? [input?: TInput, options?: FriendlyClientOptions<TClientContext>] : [input: TInput, options?: FriendlyClientOptions<TClientContext>] : [input: TInput, options: FriendlyClientOptions<TClientContext>]; | ||
| type ClientPromiseResult<TOutput, TError> = PromiseWithError<TOutput, TError>; | ||
| interface Client<TClientContext extends ClientContext, TInput, TOutput, TError> { | ||
| (...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>; | ||
| } | ||
| type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | { | ||
| [k: string]: NestedClient<TClientContext>; | ||
| }; | ||
| type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never; | ||
| interface ClientLink<TClientContext extends ClientContext> { | ||
| call: (path: readonly string[], input: unknown, options: ClientOptions<TClientContext>) => Promise<unknown>; | ||
| } | ||
| /** | ||
| * Recursively infers the **input types** from a client. | ||
| * | ||
| * Produces a nested map where each endpoint's input type is preserved. | ||
| */ | ||
| type InferClientInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientInputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **body input types** from a client. | ||
| * | ||
| * If an endpoint's input includes `{ body: ... }`, only the `body` portion is extracted. | ||
| * Produces a nested map of body input types. | ||
| */ | ||
| type InferClientBodyInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U extends { | ||
| body: infer UBody; | ||
| } ? UBody : U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyInputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **output types** from a client. | ||
| * | ||
| * Produces a nested map where each endpoint's output type is preserved. | ||
| */ | ||
| type InferClientOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientOutputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **body output types** from a client. | ||
| * | ||
| * If an endpoint's output includes `{ body: ... }`, only the `body` portion is extracted. | ||
| * Produces a nested map of body output types. | ||
| */ | ||
| type InferClientBodyOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U extends { | ||
| body: infer UBody; | ||
| } ? UBody : U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyOutputs<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers the **error types** from a client when you use [type-safe errors](https://orpc.dev/docs/error-handling#type‐safe-error-handling). | ||
| * | ||
| * Produces a nested map where each endpoint's error type is preserved. | ||
| */ | ||
| type InferClientErrors<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrors<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Recursively infers a **union of all error types** from a client when you use [type-safe errors](https://orpc.dev/docs/error-handling#type‐safe-error-handling). | ||
| * | ||
| * Useful when you want to handle all possible errors from any endpoint at once. | ||
| */ | ||
| type InferClientErrorUnion<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrorUnion<T[K]> : never; | ||
| }[keyof T]; | ||
| export type { ClientLink as C, FriendlyClientOptions as F, HTTPPath as H, InferClientContext as I, NestedClient as N, ClientPromiseResult as a, ClientContext as b, ClientOptions as c, Client as d, ClientRest as e, HTTPMethod as f, InferClientInputs as g, InferClientBodyInputs as h, InferClientOutputs as i, InferClientBodyOutputs as j, InferClientErrors as k, InferClientErrorUnion as l }; |
@@ -0,9 +1,24 @@ | ||
| import { Interceptor } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { ToFetchRequestOptions } from '@orpc/standard-server-fetch'; | ||
| import { C as ClientContext, a as ClientOptionsOut, b as ClientLink } from '../../shared/client.D_CzLDyB.mjs'; | ||
| import { StandardLinkClient, StandardLinkOptions, StandardRPCLinkCodecOptions } from '../standard/index.mjs'; | ||
| import '@orpc/shared'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.mjs'; | ||
| import { a as StandardLinkPlugin, f as StandardLinkClient } from '../../shared/client.CpCa3si8.mjs'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.BFAVy68H.mjs'; | ||
| interface LinkFetchPlugin<T extends ClientContext> extends StandardLinkPlugin<T> { | ||
| initRuntimeAdapter?(options: LinkFetchClientOptions<T>): void; | ||
| } | ||
| interface LinkFetchInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { | ||
| request: Request; | ||
| init: { | ||
| redirect?: Request['redirect']; | ||
| }; | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| } | ||
| interface LinkFetchClientOptions<T extends ClientContext> extends ToFetchRequestOptions { | ||
| fetch?: (request: Request, init: Record<never, never>, options: ClientOptionsOut<T>, path: readonly string[], input: unknown) => Promise<Response>; | ||
| fetch?: (request: Request, init: LinkFetchInterceptorOptions<T>['init'], options: ClientOptions<T>, path: readonly string[], input: unknown) => Promise<Response>; | ||
| adapterInterceptors?: Interceptor<LinkFetchInterceptorOptions<T>, Promise<Response>>[]; | ||
| plugins?: LinkFetchPlugin<T>[]; | ||
| } | ||
@@ -13,18 +28,20 @@ declare class LinkFetchClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly toFetchRequestOptions; | ||
| private readonly adapterInterceptors; | ||
| constructor(options: LinkFetchClientOptions<T>); | ||
| call(request: StandardRequest, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| call(standardRequest: StandardRequest, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends StandardLinkOptions<T>, StandardRPCLinkCodecOptions<T>, LinkFetchClientOptions<T> { | ||
| interface RPCLinkOptions<T extends ClientContext> extends LinkFetchClientOptions<T>, Omit<StandardRPCLinkOptions<T>, 'plugins'> { | ||
| } | ||
| declare class RPCLink<T extends ClientContext> implements ClientLink<T> { | ||
| private readonly standardLink; | ||
| /** | ||
| * The RPC Link communicates with the server using the RPC protocol. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/advanced/rpc-protocol RPC Protocol Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<T>): Promise<unknown>; | ||
| } | ||
| interface FetchWithContext<TClientContext extends ClientContext> { | ||
| (url: URL, init: RequestInit, options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown): Promise<Response>; | ||
| } | ||
| export { type FetchWithContext, LinkFetchClient, type LinkFetchClientOptions, RPCLink, type RPCLinkOptions }; | ||
| export { LinkFetchClient, RPCLink }; | ||
| export type { LinkFetchClientOptions, LinkFetchInterceptorOptions, RPCLinkOptions }; |
@@ -0,9 +1,24 @@ | ||
| import { Interceptor } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse } from '@orpc/standard-server'; | ||
| import { ToFetchRequestOptions } from '@orpc/standard-server-fetch'; | ||
| import { C as ClientContext, a as ClientOptionsOut, b as ClientLink } from '../../shared/client.D_CzLDyB.js'; | ||
| import { StandardLinkClient, StandardLinkOptions, StandardRPCLinkCodecOptions } from '../standard/index.js'; | ||
| import '@orpc/shared'; | ||
| import { b as ClientContext, c as ClientOptions } from '../../shared/client.i2uoJbEp.js'; | ||
| import { a as StandardLinkPlugin, f as StandardLinkClient } from '../../shared/client.2jUAqzYU.js'; | ||
| import { f as StandardRPCLinkOptions, g as StandardRPCLink } from '../../shared/client.B3pNRBih.js'; | ||
| interface LinkFetchPlugin<T extends ClientContext> extends StandardLinkPlugin<T> { | ||
| initRuntimeAdapter?(options: LinkFetchClientOptions<T>): void; | ||
| } | ||
| interface LinkFetchInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { | ||
| request: Request; | ||
| init: { | ||
| redirect?: Request['redirect']; | ||
| }; | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| } | ||
| interface LinkFetchClientOptions<T extends ClientContext> extends ToFetchRequestOptions { | ||
| fetch?: (request: Request, init: Record<never, never>, options: ClientOptionsOut<T>, path: readonly string[], input: unknown) => Promise<Response>; | ||
| fetch?: (request: Request, init: LinkFetchInterceptorOptions<T>['init'], options: ClientOptions<T>, path: readonly string[], input: unknown) => Promise<Response>; | ||
| adapterInterceptors?: Interceptor<LinkFetchInterceptorOptions<T>, Promise<Response>>[]; | ||
| plugins?: LinkFetchPlugin<T>[]; | ||
| } | ||
@@ -13,18 +28,20 @@ declare class LinkFetchClient<T extends ClientContext> implements StandardLinkClient<T> { | ||
| private readonly toFetchRequestOptions; | ||
| private readonly adapterInterceptors; | ||
| constructor(options: LinkFetchClientOptions<T>); | ||
| call(request: StandardRequest, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| call(standardRequest: StandardRequest, options: ClientOptions<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| interface RPCLinkOptions<T extends ClientContext> extends StandardLinkOptions<T>, StandardRPCLinkCodecOptions<T>, LinkFetchClientOptions<T> { | ||
| interface RPCLinkOptions<T extends ClientContext> extends LinkFetchClientOptions<T>, Omit<StandardRPCLinkOptions<T>, 'plugins'> { | ||
| } | ||
| declare class RPCLink<T extends ClientContext> implements ClientLink<T> { | ||
| private readonly standardLink; | ||
| /** | ||
| * The RPC Link communicates with the server using the RPC protocol. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/rpc-link RPC Link Docs} | ||
| * @see {@link https://orpc.dev/docs/advanced/rpc-protocol RPC Protocol Docs} | ||
| */ | ||
| declare class RPCLink<T extends ClientContext> extends StandardRPCLink<T> { | ||
| constructor(options: RPCLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<T>): Promise<unknown>; | ||
| } | ||
| interface FetchWithContext<TClientContext extends ClientContext> { | ||
| (url: URL, init: RequestInit, options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown): Promise<Response>; | ||
| } | ||
| export { type FetchWithContext, LinkFetchClient, type LinkFetchClientOptions, RPCLink, type RPCLinkOptions }; | ||
| export { LinkFetchClient, RPCLink }; | ||
| export type { LinkFetchClientOptions, LinkFetchInterceptorOptions, RPCLinkOptions }; |
@@ -0,18 +1,35 @@ | ||
| import { toArray, intercept } from '@orpc/shared'; | ||
| import { toFetchRequest, toStandardLazyResponse } from '@orpc/standard-server-fetch'; | ||
| import { a as StandardRPCLinkCodec, b as RPCSerializer, S as StandardLink } from '../../shared/client.Df5pd75N.mjs'; | ||
| import '@orpc/shared'; | ||
| import '../../shared/client.XAn8cDTM.mjs'; | ||
| import { C as CompositeStandardLinkPlugin, c as StandardRPCLink } from '../../shared/client.C6Bgyn6F.mjs'; | ||
| import '@orpc/standard-server'; | ||
| import '../../shared/client.CPTihQgC.mjs'; | ||
| import '../../shared/client.BLtwTQUg.mjs'; | ||
| class CompositeLinkFetchPlugin extends CompositeStandardLinkPlugin { | ||
| initRuntimeAdapter(options) { | ||
| for (const plugin of this.plugins) { | ||
| plugin.initRuntimeAdapter?.(options); | ||
| } | ||
| } | ||
| } | ||
| class LinkFetchClient { | ||
| fetch; | ||
| toFetchRequestOptions; | ||
| adapterInterceptors; | ||
| constructor(options) { | ||
| this.fetch = options?.fetch ?? globalThis.fetch.bind(globalThis); | ||
| const plugin = new CompositeLinkFetchPlugin(options.plugins); | ||
| plugin.initRuntimeAdapter(options); | ||
| this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis); | ||
| this.toFetchRequestOptions = options; | ||
| this.adapterInterceptors = toArray(options.adapterInterceptors); | ||
| } | ||
| async call(request, options, path, input) { | ||
| const fetchRequest = toFetchRequest(request, this.toFetchRequestOptions); | ||
| const fetchResponse = await this.fetch(fetchRequest, {}, options, path, input); | ||
| const lazyResponse = toStandardLazyResponse(fetchResponse); | ||
| async call(standardRequest, options, path, input) { | ||
| const request = toFetchRequest(standardRequest, this.toFetchRequestOptions); | ||
| const fetchResponse = await intercept( | ||
| this.adapterInterceptors, | ||
| { ...options, request, path, input, init: { redirect: "manual" } }, | ||
| ({ request: request2, path: path2, input: input2, init, ...options2 }) => this.fetch(request2, init, options2, path2, input2) | ||
| ); | ||
| const lazyResponse = toStandardLazyResponse(fetchResponse, { signal: request.signal }); | ||
| return lazyResponse; | ||
@@ -22,15 +39,9 @@ } | ||
| class RPCLink { | ||
| standardLink; | ||
| class RPCLink extends StandardRPCLink { | ||
| constructor(options) { | ||
| const serializer = new RPCSerializer(); | ||
| const linkCodec = new StandardRPCLinkCodec(serializer, options); | ||
| const linkClient = new LinkFetchClient(options); | ||
| this.standardLink = new StandardLink(linkCodec, linkClient, options); | ||
| super(linkClient, options); | ||
| } | ||
| async call(path, input, options) { | ||
| return this.standardLink.call(path, input, options); | ||
| } | ||
| } | ||
| export { LinkFetchClient, RPCLink }; |
@@ -1,148 +0,11 @@ | ||
| import { Value, Interceptor, Segment } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse, StandardHeaders } from '@orpc/standard-server'; | ||
| import { C as ClientContext, a as ClientOptionsOut, E as EventIteratorReconnectOptions, b as ClientLink } from '../../shared/client.D_CzLDyB.mjs'; | ||
| export { C as CompositeStandardLinkPlugin, d as StandardLink, f as StandardLinkClient, S as StandardLinkClientInterceptorOptions, e as StandardLinkCodec, c as StandardLinkInterceptorOptions, b as StandardLinkOptions, a as StandardLinkPlugin } from '../../shared/client.CpCa3si8.mjs'; | ||
| export { S as STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES, c as StandardRPCCustomJsonSerializer, b as StandardRPCJsonSerialized, a as StandardRPCJsonSerializedMetaItem, e as StandardRPCJsonSerializer, d as StandardRPCJsonSerializerOptions, g as StandardRPCLink, i as StandardRPCLinkCodec, h as StandardRPCLinkCodecOptions, f as StandardRPCLinkOptions, j as StandardRPCSerializer } from '../../shared/client.BFAVy68H.mjs'; | ||
| import { StandardHeaders } from '@orpc/standard-server'; | ||
| import { H as HTTPPath } from '../../shared/client.i2uoJbEp.mjs'; | ||
| import '@orpc/shared'; | ||
| interface StandardLinkCodec<T extends ClientContext> { | ||
| encode(path: readonly string[], input: unknown, options: ClientOptionsOut<any>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<unknown>; | ||
| } | ||
| interface StandardLinkClient<T extends ClientContext> { | ||
| call(request: StandardRequest, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| declare function toHttpPath(path: readonly string[]): HTTPPath; | ||
| declare function toStandardHeaders(headers: Headers | StandardHeaders): StandardHeaders; | ||
| declare function getMalformedResponseErrorCode(status: number): string; | ||
| declare class InvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| interface StandardLinkOptions<T extends ClientContext> { | ||
| /** | ||
| * Maximum number of retry attempts for event iterator errors before throwing. | ||
| * | ||
| * @default 5 | ||
| */ | ||
| eventIteratorMaxRetries?: Value<number, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * Delay (in ms) before retrying an event iterator call. | ||
| * | ||
| * @default (o) => o.lastRetry ?? (1000 * 2 ** o.retryTimes) | ||
| */ | ||
| eventIteratorRetryDelay?: Value<number, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * Function to determine if an error is retryable. | ||
| * | ||
| * @default true | ||
| */ | ||
| eventIteratorShouldRetry?: Value<boolean, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| interceptors?: Interceptor<{ | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| options: ClientOptionsOut<T>; | ||
| }, unknown, unknown>[]; | ||
| clientInterceptors?: Interceptor<{ | ||
| request: StandardRequest; | ||
| }, StandardLazyResponse, unknown>[]; | ||
| } | ||
| declare class StandardLink<T extends ClientContext> implements ClientLink<T> { | ||
| #private; | ||
| readonly codec: StandardLinkCodec<T>; | ||
| readonly sender: StandardLinkClient<T>; | ||
| private readonly eventIteratorMaxRetries; | ||
| private readonly eventIteratorRetryDelay; | ||
| private readonly eventIteratorShouldRetry; | ||
| private readonly interceptors; | ||
| private readonly clientInterceptors; | ||
| constructor(codec: StandardLinkCodec<T>, sender: StandardLinkClient<T>, options: StandardLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<T>): Promise<unknown>; | ||
| } | ||
| type RPCJsonSerializedMeta = [ | ||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7, | ||
| Segment[] | ||
| ][]; | ||
| type RPCJsonSerialized = [json: unknown, meta: RPCJsonSerializedMeta, maps: Segment[][], blobs: Blob[]]; | ||
| declare class RPCJsonSerializer { | ||
| serialize(data: unknown, segments?: Segment[], meta?: RPCJsonSerializedMeta, maps?: Segment[][], blobs?: Blob[]): RPCJsonSerialized; | ||
| deserialize(json: unknown, meta: RPCJsonSerializedMeta): unknown; | ||
| deserialize(json: unknown, meta: RPCJsonSerializedMeta, maps: Segment[][], getBlob: (index: number) => Blob): unknown; | ||
| } | ||
| declare class RPCSerializer { | ||
| #private; | ||
| private readonly jsonSerializer; | ||
| constructor(jsonSerializer?: RPCJsonSerializer); | ||
| serialize(data: unknown): unknown; | ||
| deserialize(data: unknown): unknown; | ||
| } | ||
| type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; | ||
| interface StandardRPCLinkCodecOptions<T extends ClientContext> { | ||
| /** | ||
| * Base url for all requests. | ||
| */ | ||
| url: Value<string | URL, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<number, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The method used to make the request. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| method?: Value<HTTPMethod, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The method to use when the payload cannot safely pass to the server with method return from method function. | ||
| * GET is not allowed, it's very dangerous. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| fallbackMethod?: Exclude<HTTPMethod, 'GET'>; | ||
| /** | ||
| * Inject headers to the request. | ||
| */ | ||
| headers?: Value<StandardHeaders, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| } | ||
| declare class StandardRPCLinkCodec<T extends ClientContext> implements StandardLinkCodec<T> { | ||
| private readonly serializer; | ||
| private readonly baseUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly fallbackMethod; | ||
| private readonly expectedMethod; | ||
| private readonly headers; | ||
| constructor(serializer: RPCSerializer, options: StandardRPCLinkCodecOptions<T>); | ||
| encode(path: readonly string[], input: unknown, options: ClientOptionsOut<any>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse): Promise<unknown>; | ||
| } | ||
| export { InvalidEventIteratorRetryResponse, type RPCJsonSerialized, type RPCJsonSerializedMeta, RPCJsonSerializer, RPCSerializer, StandardLink, type StandardLinkClient, type StandardLinkCodec, type StandardLinkOptions, StandardRPCLinkCodec, type StandardRPCLinkCodecOptions }; | ||
| export { getMalformedResponseErrorCode, toHttpPath, toStandardHeaders }; |
@@ -1,148 +0,11 @@ | ||
| import { Value, Interceptor, Segment } from '@orpc/shared'; | ||
| import { StandardRequest, StandardLazyResponse, StandardHeaders } from '@orpc/standard-server'; | ||
| import { C as ClientContext, a as ClientOptionsOut, E as EventIteratorReconnectOptions, b as ClientLink } from '../../shared/client.D_CzLDyB.js'; | ||
| export { C as CompositeStandardLinkPlugin, d as StandardLink, f as StandardLinkClient, S as StandardLinkClientInterceptorOptions, e as StandardLinkCodec, c as StandardLinkInterceptorOptions, b as StandardLinkOptions, a as StandardLinkPlugin } from '../../shared/client.2jUAqzYU.js'; | ||
| export { S as STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES, c as StandardRPCCustomJsonSerializer, b as StandardRPCJsonSerialized, a as StandardRPCJsonSerializedMetaItem, e as StandardRPCJsonSerializer, d as StandardRPCJsonSerializerOptions, g as StandardRPCLink, i as StandardRPCLinkCodec, h as StandardRPCLinkCodecOptions, f as StandardRPCLinkOptions, j as StandardRPCSerializer } from '../../shared/client.B3pNRBih.js'; | ||
| import { StandardHeaders } from '@orpc/standard-server'; | ||
| import { H as HTTPPath } from '../../shared/client.i2uoJbEp.js'; | ||
| import '@orpc/shared'; | ||
| interface StandardLinkCodec<T extends ClientContext> { | ||
| encode(path: readonly string[], input: unknown, options: ClientOptionsOut<any>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<unknown>; | ||
| } | ||
| interface StandardLinkClient<T extends ClientContext> { | ||
| call(request: StandardRequest, options: ClientOptionsOut<T>, path: readonly string[], input: unknown): Promise<StandardLazyResponse>; | ||
| } | ||
| declare function toHttpPath(path: readonly string[]): HTTPPath; | ||
| declare function toStandardHeaders(headers: Headers | StandardHeaders): StandardHeaders; | ||
| declare function getMalformedResponseErrorCode(status: number): string; | ||
| declare class InvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| interface StandardLinkOptions<T extends ClientContext> { | ||
| /** | ||
| * Maximum number of retry attempts for event iterator errors before throwing. | ||
| * | ||
| * @default 5 | ||
| */ | ||
| eventIteratorMaxRetries?: Value<number, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * Delay (in ms) before retrying an event iterator call. | ||
| * | ||
| * @default (o) => o.lastRetry ?? (1000 * 2 ** o.retryTimes) | ||
| */ | ||
| eventIteratorRetryDelay?: Value<number, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * Function to determine if an error is retryable. | ||
| * | ||
| * @default true | ||
| */ | ||
| eventIteratorShouldRetry?: Value<boolean, [ | ||
| reconnectOptions: EventIteratorReconnectOptions, | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| interceptors?: Interceptor<{ | ||
| path: readonly string[]; | ||
| input: unknown; | ||
| options: ClientOptionsOut<T>; | ||
| }, unknown, unknown>[]; | ||
| clientInterceptors?: Interceptor<{ | ||
| request: StandardRequest; | ||
| }, StandardLazyResponse, unknown>[]; | ||
| } | ||
| declare class StandardLink<T extends ClientContext> implements ClientLink<T> { | ||
| #private; | ||
| readonly codec: StandardLinkCodec<T>; | ||
| readonly sender: StandardLinkClient<T>; | ||
| private readonly eventIteratorMaxRetries; | ||
| private readonly eventIteratorRetryDelay; | ||
| private readonly eventIteratorShouldRetry; | ||
| private readonly interceptors; | ||
| private readonly clientInterceptors; | ||
| constructor(codec: StandardLinkCodec<T>, sender: StandardLinkClient<T>, options: StandardLinkOptions<T>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<T>): Promise<unknown>; | ||
| } | ||
| type RPCJsonSerializedMeta = [ | ||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7, | ||
| Segment[] | ||
| ][]; | ||
| type RPCJsonSerialized = [json: unknown, meta: RPCJsonSerializedMeta, maps: Segment[][], blobs: Blob[]]; | ||
| declare class RPCJsonSerializer { | ||
| serialize(data: unknown, segments?: Segment[], meta?: RPCJsonSerializedMeta, maps?: Segment[][], blobs?: Blob[]): RPCJsonSerialized; | ||
| deserialize(json: unknown, meta: RPCJsonSerializedMeta): unknown; | ||
| deserialize(json: unknown, meta: RPCJsonSerializedMeta, maps: Segment[][], getBlob: (index: number) => Blob): unknown; | ||
| } | ||
| declare class RPCSerializer { | ||
| #private; | ||
| private readonly jsonSerializer; | ||
| constructor(jsonSerializer?: RPCJsonSerializer); | ||
| serialize(data: unknown): unknown; | ||
| deserialize(data: unknown): unknown; | ||
| } | ||
| type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; | ||
| interface StandardRPCLinkCodecOptions<T extends ClientContext> { | ||
| /** | ||
| * Base url for all requests. | ||
| */ | ||
| url: Value<string | URL, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The maximum length of the URL. | ||
| * | ||
| * @default 2083 | ||
| */ | ||
| maxUrlLength?: Value<number, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The method used to make the request. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| method?: Value<HTTPMethod, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| /** | ||
| * The method to use when the payload cannot safely pass to the server with method return from method function. | ||
| * GET is not allowed, it's very dangerous. | ||
| * | ||
| * @default 'POST' | ||
| */ | ||
| fallbackMethod?: Exclude<HTTPMethod, 'GET'>; | ||
| /** | ||
| * Inject headers to the request. | ||
| */ | ||
| headers?: Value<StandardHeaders, [ | ||
| options: ClientOptionsOut<T>, | ||
| path: readonly string[], | ||
| input: unknown | ||
| ]>; | ||
| } | ||
| declare class StandardRPCLinkCodec<T extends ClientContext> implements StandardLinkCodec<T> { | ||
| private readonly serializer; | ||
| private readonly baseUrl; | ||
| private readonly maxUrlLength; | ||
| private readonly fallbackMethod; | ||
| private readonly expectedMethod; | ||
| private readonly headers; | ||
| constructor(serializer: RPCSerializer, options: StandardRPCLinkCodecOptions<T>); | ||
| encode(path: readonly string[], input: unknown, options: ClientOptionsOut<any>): Promise<StandardRequest>; | ||
| decode(response: StandardLazyResponse): Promise<unknown>; | ||
| } | ||
| export { InvalidEventIteratorRetryResponse, type RPCJsonSerialized, type RPCJsonSerializedMeta, RPCJsonSerializer, RPCSerializer, StandardLink, type StandardLinkClient, type StandardLinkCodec, type StandardLinkOptions, StandardRPCLinkCodec, type StandardRPCLinkCodecOptions }; | ||
| export { getMalformedResponseErrorCode, toHttpPath, toStandardHeaders }; |
@@ -1,4 +0,6 @@ | ||
| export { I as InvalidEventIteratorRetryResponse, R as RPCJsonSerializer, b as RPCSerializer, S as StandardLink, a as StandardRPCLinkCodec } from '../../shared/client.Df5pd75N.mjs'; | ||
| export { C as CompositeStandardLinkPlugin, a as STANDARD_RPC_JSON_SERIALIZER_BUILT_IN_TYPES, S as StandardLink, b as StandardRPCJsonSerializer, c as StandardRPCLink, d as StandardRPCLinkCodec, e as StandardRPCSerializer, g as getMalformedResponseErrorCode, t as toHttpPath, f as toStandardHeaders } from '../../shared/client.C6Bgyn6F.mjs'; | ||
| import '@orpc/shared'; | ||
| import '../../shared/client.XAn8cDTM.mjs'; | ||
| import '@orpc/standard-server'; | ||
| import '../../shared/client.CPTihQgC.mjs'; | ||
| import '@orpc/standard-server-fetch'; | ||
| import '../../shared/client.BLtwTQUg.mjs'; |
+108
-33
@@ -1,5 +0,6 @@ | ||
| import { N as NestedClient, b as ClientLink, I as InferClientContext, C as ClientContext, a as ClientOptionsOut, c as ClientPromiseResult } from './shared/client.D_CzLDyB.mjs'; | ||
| export { g as Client, e as ClientOptions, f as ClientRest, E as EventIteratorReconnectOptions, d as createAutoRetryEventIterator, m as mapEventIterator } from './shared/client.D_CzLDyB.mjs'; | ||
| import { Promisable, MaybeOptionalOptions } from '@orpc/shared'; | ||
| export { ErrorEvent } from '@orpc/standard-server'; | ||
| import { N as NestedClient, C as ClientLink, I as InferClientContext, a as ClientPromiseResult, b as ClientContext, F as FriendlyClientOptions, c as ClientOptions, d as Client, e as ClientRest } from './shared/client.i2uoJbEp.mjs'; | ||
| export { f as HTTPMethod, H as HTTPPath, h as InferClientBodyInputs, j as InferClientBodyOutputs, l as InferClientErrorUnion, k as InferClientErrors, g as InferClientInputs, i as InferClientOutputs } from './shared/client.i2uoJbEp.mjs'; | ||
| import { MaybeOptionalOptions, ThrowableError, OnFinishState, Promisable, AsyncIteratorClass } from '@orpc/shared'; | ||
| export { AsyncIteratorClass, EventPublisher, EventPublisherOptions, EventPublisherSubscribeIteratorOptions, Registry, ThrowableError, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared'; | ||
| export { ErrorEvent, EventMeta, getEventMeta, withEventMeta } from '@orpc/standard-server'; | ||
@@ -10,15 +11,10 @@ interface createORPCClientOptions { | ||
| */ | ||
| path?: string[]; | ||
| path?: readonly string[]; | ||
| } | ||
| declare function createORPCClient<T extends NestedClient<any>>(link: ClientLink<InferClientContext<T>>, options?: createORPCClientOptions): T; | ||
| /** | ||
| * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks | ||
| * based on the request path, input, and context. | ||
| * Create a oRPC client-side client from a link. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/client-side Client-side Client Docs} | ||
| */ | ||
| declare class DynamicLink<TClientContext extends ClientContext> implements ClientLink<TClientContext> { | ||
| private readonly linkResolver; | ||
| constructor(linkResolver: (options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown) => Promisable<ClientLink<TClientContext>>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>): Promise<unknown>; | ||
| } | ||
| declare function createORPCClient<T extends NestedClient<any>>(link: ClientLink<InferClientContext<T>>, options?: createORPCClientOptions): T; | ||
@@ -121,6 +117,17 @@ declare const COMMON_ORPC_ERROR_DEFS: { | ||
| readonly data: TData; | ||
| constructor(code: TCode, ...[options]: MaybeOptionalOptions<ORPCErrorOptions<TData>>); | ||
| constructor(code: TCode, ...rest: MaybeOptionalOptions<ORPCErrorOptions<TData>>); | ||
| toJSON(): ORPCErrorJSON<TCode, TData>; | ||
| static fromJSON<TCode extends ORPCErrorCode, TData>(json: ORPCErrorJSON<TCode, TData>, options?: ErrorOptions): ORPCError<TCode, TData>; | ||
| static isValidJSON(json: unknown): json is ORPCErrorJSON<ORPCErrorCode, unknown>; | ||
| /** | ||
| * Workaround for Next.js where different contexts use separate | ||
| * dependency graphs, causing multiple ORPCError constructors existing and breaking | ||
| * `instanceof` checks across contexts. | ||
| * | ||
| * This is particularly problematic with "Optimized SSR", where orpc-client | ||
| * executes in one context but is invoked from another. When an error is thrown | ||
| * in the execution context, `instanceof ORPCError` checks fail in the | ||
| * invocation context due to separate class constructors. | ||
| * | ||
| * @todo Remove this and related code if Next.js resolves the multiple dependency graph issue. | ||
| */ | ||
| static [Symbol.hasInstance](instance: unknown): boolean; | ||
| } | ||
@@ -130,29 +137,97 @@ type ORPCErrorJSON<TCode extends string, TData> = Pick<ORPCError<TCode, TData>, 'defined' | 'code' | 'status' | 'message' | 'data'>; | ||
| declare function toORPCError(error: unknown): ORPCError<any, any>; | ||
| declare function isORPCErrorStatus(status: number): boolean; | ||
| declare function isORPCErrorJson(json: unknown): json is ORPCErrorJSON<ORPCErrorCode, unknown>; | ||
| declare function createORPCErrorFromJson<TCode extends ORPCErrorCode, TData>(json: ORPCErrorJSON<TCode, TData>, options?: ErrorOptions): ORPCError<TCode, TData>; | ||
| type ConnectionStatus = 'reconnecting' | 'connected' | 'closed'; | ||
| interface EventIteratorState { | ||
| status: ConnectionStatus; | ||
| listeners: Array<(newStatus: ConnectionStatus) => void>; | ||
| } | ||
| declare function registerEventIteratorState(iterator: AsyncIteratorObject<unknown, unknown, void>, state: EventIteratorState): void; | ||
| declare function updateEventIteratorStatus(state: EventIteratorState, status: ConnectionStatus): void; | ||
| declare function onEventIteratorStatusChange(iterator: AsyncIteratorObject<unknown, unknown, void>, callback: (status: ConnectionStatus) => void, options?: { | ||
| notifyImmediately?: boolean; | ||
| }): () => void; | ||
| type SafeResult<TOutput, TError extends Error> = [error: null, data: TOutput, isDefined: false] & { | ||
| type SafeResult<TOutput, TError> = [error: null, data: TOutput, isDefined: false, isSuccess: true] & { | ||
| error: null; | ||
| data: TOutput; | ||
| isDefined: false; | ||
| } | [error: Exclude<TError, ORPCError<any, any>>, data: undefined, isDefined: false] & { | ||
| isSuccess: true; | ||
| } | [error: Exclude<TError, ORPCError<any, any>>, data: undefined, isDefined: false, isSuccess: false] & { | ||
| error: Exclude<TError, ORPCError<any, any>>; | ||
| data: undefined; | ||
| isDefined: false; | ||
| } | [error: Extract<TError, ORPCError<any, any>>, data: undefined, isDefined: true] & { | ||
| isSuccess: false; | ||
| } | [error: Extract<TError, ORPCError<any, any>>, data: undefined, isDefined: true, isSuccess: false] & { | ||
| error: Extract<TError, ORPCError<any, any>>; | ||
| data: undefined; | ||
| isDefined: true; | ||
| isSuccess: false; | ||
| }; | ||
| declare function safe<TOutput, TError extends Error>(promise: ClientPromiseResult<TOutput, TError>): Promise<SafeResult<TOutput, TError>>; | ||
| /** | ||
| * Works like try/catch, but can infer error types. | ||
| * | ||
| * @info support both tuple `[error, data, isDefined, isSuccess]` and object `{ error, data, isDefined, isSuccess }` styles. | ||
| * @see {@link https://orpc.dev/docs/client/error-handling Client Error Handling Docs} | ||
| */ | ||
| declare function safe<TOutput, TError = ThrowableError>(promise: ClientPromiseResult<TOutput, TError>): Promise<SafeResult<TOutput, TError>>; | ||
| declare function resolveFriendlyClientOptions<T extends ClientContext>(options: FriendlyClientOptions<T>): ClientOptions<T>; | ||
| interface ConsumeEventIteratorOptions<T, TReturn, TError> { | ||
| /** | ||
| * Called on each event | ||
| */ | ||
| onEvent: (event: T) => void; | ||
| /** | ||
| * Called once error happens | ||
| */ | ||
| onError?: (error: TError) => void; | ||
| /** | ||
| * Called once event iterator is done | ||
| * | ||
| * @info If iterator is canceled, `undefined` can be passed on success | ||
| */ | ||
| onSuccess?: (value: TReturn | undefined) => void; | ||
| /** | ||
| * Called once after onError or onSuccess | ||
| * | ||
| * @info If iterator is canceled, `undefined` can be passed on success | ||
| */ | ||
| onFinish?: (state: OnFinishState<TReturn | undefined, TError>) => void; | ||
| } | ||
| /** | ||
| * Consumes an event iterator with lifecycle callbacks | ||
| * | ||
| * @warning If no `onError` or `onFinish` is provided, unhandled rejections will be thrown | ||
| * @return unsubscribe callback | ||
| */ | ||
| declare function consumeEventIterator<T, TReturn, TError = ThrowableError>(iterator: AsyncIterator<T, TReturn> | ClientPromiseResult<AsyncIterator<T, TReturn>, TError>, options: ConsumeEventIteratorOptions<T, TReturn, TError>): () => Promise<void>; | ||
| export { COMMON_ORPC_ERROR_DEFS, ClientContext, ClientLink, ClientOptionsOut, ClientPromiseResult, type CommonORPCErrorCode, type ConnectionStatus, DynamicLink, type EventIteratorState, InferClientContext, NestedClient, ORPCError, type ORPCErrorCode, type ORPCErrorJSON, type ORPCErrorOptions, type SafeResult, createORPCClient, type createORPCClientOptions, fallbackORPCErrorMessage, fallbackORPCErrorStatus, isDefinedError, onEventIteratorStatusChange, registerEventIteratorState, safe, toORPCError, updateEventIteratorStatus }; | ||
| type SafeClient<T extends NestedClient<any>> = T extends Client<infer UContext, infer UInput, infer UOutput, infer UError> ? (...rest: ClientRest<UContext, UInput>) => Promise<SafeResult<UOutput, UError>> : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? SafeClient<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Create a safe client that automatically wraps all procedure calls with the `safe` util. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const safeClient = createSafeClient(client) | ||
| * const { error, data, isDefined } = await safeClient.doSomething({ id: '123' }) | ||
| * ``` | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/error-handling#using-createsafeclient Safe Client Docs} | ||
| */ | ||
| declare function createSafeClient<T extends NestedClient<any>>(client: T): SafeClient<T>; | ||
| declare const ORPC_CLIENT_PACKAGE_NAME = "@orpc/client"; | ||
| declare const ORPC_CLIENT_PACKAGE_VERSION = "0.0.0-next.99d5d75"; | ||
| /** | ||
| * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks | ||
| * based on the request path, input, and context. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/dynamic-link Dynamic Link Docs} | ||
| */ | ||
| declare class DynamicLink<TClientContext extends ClientContext> implements ClientLink<TClientContext> { | ||
| private readonly linkResolver; | ||
| constructor(linkResolver: (options: ClientOptions<TClientContext>, path: readonly string[], input: unknown) => Promisable<ClientLink<TClientContext>>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptions<TClientContext>): Promise<unknown>; | ||
| } | ||
| declare function mapEventIterator<TYield, TReturn, TNext, TMap = TYield | TReturn>(iterator: AsyncIterator<TYield, TReturn, TNext>, maps: { | ||
| value: (value: NoInfer<TYield | TReturn>, done: boolean | undefined) => Promise<TMap>; | ||
| error: (error: unknown) => Promise<unknown>; | ||
| }): AsyncIteratorClass<TMap, TMap, TNext>; | ||
| export { COMMON_ORPC_ERROR_DEFS, Client, ClientContext, ClientLink, ClientOptions, ClientPromiseResult, ClientRest, DynamicLink, FriendlyClientOptions, InferClientContext, NestedClient, ORPCError, ORPC_CLIENT_PACKAGE_NAME, ORPC_CLIENT_PACKAGE_VERSION, consumeEventIterator, createORPCClient, createORPCErrorFromJson, createSafeClient, fallbackORPCErrorMessage, fallbackORPCErrorStatus, isDefinedError, isORPCErrorJson, isORPCErrorStatus, mapEventIterator, resolveFriendlyClientOptions, safe, toORPCError }; | ||
| export type { CommonORPCErrorCode, ConsumeEventIteratorOptions, ORPCErrorCode, ORPCErrorJSON, ORPCErrorOptions, SafeClient, SafeResult, createORPCClientOptions }; |
+108
-33
@@ -1,5 +0,6 @@ | ||
| import { N as NestedClient, b as ClientLink, I as InferClientContext, C as ClientContext, a as ClientOptionsOut, c as ClientPromiseResult } from './shared/client.D_CzLDyB.js'; | ||
| export { g as Client, e as ClientOptions, f as ClientRest, E as EventIteratorReconnectOptions, d as createAutoRetryEventIterator, m as mapEventIterator } from './shared/client.D_CzLDyB.js'; | ||
| import { Promisable, MaybeOptionalOptions } from '@orpc/shared'; | ||
| export { ErrorEvent } from '@orpc/standard-server'; | ||
| import { N as NestedClient, C as ClientLink, I as InferClientContext, a as ClientPromiseResult, b as ClientContext, F as FriendlyClientOptions, c as ClientOptions, d as Client, e as ClientRest } from './shared/client.i2uoJbEp.js'; | ||
| export { f as HTTPMethod, H as HTTPPath, h as InferClientBodyInputs, j as InferClientBodyOutputs, l as InferClientErrorUnion, k as InferClientErrors, g as InferClientInputs, i as InferClientOutputs } from './shared/client.i2uoJbEp.js'; | ||
| import { MaybeOptionalOptions, ThrowableError, OnFinishState, Promisable, AsyncIteratorClass } from '@orpc/shared'; | ||
| export { AsyncIteratorClass, EventPublisher, EventPublisherOptions, EventPublisherSubscribeIteratorOptions, Registry, ThrowableError, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared'; | ||
| export { ErrorEvent, EventMeta, getEventMeta, withEventMeta } from '@orpc/standard-server'; | ||
@@ -10,15 +11,10 @@ interface createORPCClientOptions { | ||
| */ | ||
| path?: string[]; | ||
| path?: readonly string[]; | ||
| } | ||
| declare function createORPCClient<T extends NestedClient<any>>(link: ClientLink<InferClientContext<T>>, options?: createORPCClientOptions): T; | ||
| /** | ||
| * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks | ||
| * based on the request path, input, and context. | ||
| * Create a oRPC client-side client from a link. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/client-side Client-side Client Docs} | ||
| */ | ||
| declare class DynamicLink<TClientContext extends ClientContext> implements ClientLink<TClientContext> { | ||
| private readonly linkResolver; | ||
| constructor(linkResolver: (options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown) => Promisable<ClientLink<TClientContext>>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>): Promise<unknown>; | ||
| } | ||
| declare function createORPCClient<T extends NestedClient<any>>(link: ClientLink<InferClientContext<T>>, options?: createORPCClientOptions): T; | ||
@@ -121,6 +117,17 @@ declare const COMMON_ORPC_ERROR_DEFS: { | ||
| readonly data: TData; | ||
| constructor(code: TCode, ...[options]: MaybeOptionalOptions<ORPCErrorOptions<TData>>); | ||
| constructor(code: TCode, ...rest: MaybeOptionalOptions<ORPCErrorOptions<TData>>); | ||
| toJSON(): ORPCErrorJSON<TCode, TData>; | ||
| static fromJSON<TCode extends ORPCErrorCode, TData>(json: ORPCErrorJSON<TCode, TData>, options?: ErrorOptions): ORPCError<TCode, TData>; | ||
| static isValidJSON(json: unknown): json is ORPCErrorJSON<ORPCErrorCode, unknown>; | ||
| /** | ||
| * Workaround for Next.js where different contexts use separate | ||
| * dependency graphs, causing multiple ORPCError constructors existing and breaking | ||
| * `instanceof` checks across contexts. | ||
| * | ||
| * This is particularly problematic with "Optimized SSR", where orpc-client | ||
| * executes in one context but is invoked from another. When an error is thrown | ||
| * in the execution context, `instanceof ORPCError` checks fail in the | ||
| * invocation context due to separate class constructors. | ||
| * | ||
| * @todo Remove this and related code if Next.js resolves the multiple dependency graph issue. | ||
| */ | ||
| static [Symbol.hasInstance](instance: unknown): boolean; | ||
| } | ||
@@ -130,29 +137,97 @@ type ORPCErrorJSON<TCode extends string, TData> = Pick<ORPCError<TCode, TData>, 'defined' | 'code' | 'status' | 'message' | 'data'>; | ||
| declare function toORPCError(error: unknown): ORPCError<any, any>; | ||
| declare function isORPCErrorStatus(status: number): boolean; | ||
| declare function isORPCErrorJson(json: unknown): json is ORPCErrorJSON<ORPCErrorCode, unknown>; | ||
| declare function createORPCErrorFromJson<TCode extends ORPCErrorCode, TData>(json: ORPCErrorJSON<TCode, TData>, options?: ErrorOptions): ORPCError<TCode, TData>; | ||
| type ConnectionStatus = 'reconnecting' | 'connected' | 'closed'; | ||
| interface EventIteratorState { | ||
| status: ConnectionStatus; | ||
| listeners: Array<(newStatus: ConnectionStatus) => void>; | ||
| } | ||
| declare function registerEventIteratorState(iterator: AsyncIteratorObject<unknown, unknown, void>, state: EventIteratorState): void; | ||
| declare function updateEventIteratorStatus(state: EventIteratorState, status: ConnectionStatus): void; | ||
| declare function onEventIteratorStatusChange(iterator: AsyncIteratorObject<unknown, unknown, void>, callback: (status: ConnectionStatus) => void, options?: { | ||
| notifyImmediately?: boolean; | ||
| }): () => void; | ||
| type SafeResult<TOutput, TError extends Error> = [error: null, data: TOutput, isDefined: false] & { | ||
| type SafeResult<TOutput, TError> = [error: null, data: TOutput, isDefined: false, isSuccess: true] & { | ||
| error: null; | ||
| data: TOutput; | ||
| isDefined: false; | ||
| } | [error: Exclude<TError, ORPCError<any, any>>, data: undefined, isDefined: false] & { | ||
| isSuccess: true; | ||
| } | [error: Exclude<TError, ORPCError<any, any>>, data: undefined, isDefined: false, isSuccess: false] & { | ||
| error: Exclude<TError, ORPCError<any, any>>; | ||
| data: undefined; | ||
| isDefined: false; | ||
| } | [error: Extract<TError, ORPCError<any, any>>, data: undefined, isDefined: true] & { | ||
| isSuccess: false; | ||
| } | [error: Extract<TError, ORPCError<any, any>>, data: undefined, isDefined: true, isSuccess: false] & { | ||
| error: Extract<TError, ORPCError<any, any>>; | ||
| data: undefined; | ||
| isDefined: true; | ||
| isSuccess: false; | ||
| }; | ||
| declare function safe<TOutput, TError extends Error>(promise: ClientPromiseResult<TOutput, TError>): Promise<SafeResult<TOutput, TError>>; | ||
| /** | ||
| * Works like try/catch, but can infer error types. | ||
| * | ||
| * @info support both tuple `[error, data, isDefined, isSuccess]` and object `{ error, data, isDefined, isSuccess }` styles. | ||
| * @see {@link https://orpc.dev/docs/client/error-handling Client Error Handling Docs} | ||
| */ | ||
| declare function safe<TOutput, TError = ThrowableError>(promise: ClientPromiseResult<TOutput, TError>): Promise<SafeResult<TOutput, TError>>; | ||
| declare function resolveFriendlyClientOptions<T extends ClientContext>(options: FriendlyClientOptions<T>): ClientOptions<T>; | ||
| interface ConsumeEventIteratorOptions<T, TReturn, TError> { | ||
| /** | ||
| * Called on each event | ||
| */ | ||
| onEvent: (event: T) => void; | ||
| /** | ||
| * Called once error happens | ||
| */ | ||
| onError?: (error: TError) => void; | ||
| /** | ||
| * Called once event iterator is done | ||
| * | ||
| * @info If iterator is canceled, `undefined` can be passed on success | ||
| */ | ||
| onSuccess?: (value: TReturn | undefined) => void; | ||
| /** | ||
| * Called once after onError or onSuccess | ||
| * | ||
| * @info If iterator is canceled, `undefined` can be passed on success | ||
| */ | ||
| onFinish?: (state: OnFinishState<TReturn | undefined, TError>) => void; | ||
| } | ||
| /** | ||
| * Consumes an event iterator with lifecycle callbacks | ||
| * | ||
| * @warning If no `onError` or `onFinish` is provided, unhandled rejections will be thrown | ||
| * @return unsubscribe callback | ||
| */ | ||
| declare function consumeEventIterator<T, TReturn, TError = ThrowableError>(iterator: AsyncIterator<T, TReturn> | ClientPromiseResult<AsyncIterator<T, TReturn>, TError>, options: ConsumeEventIteratorOptions<T, TReturn, TError>): () => Promise<void>; | ||
| export { COMMON_ORPC_ERROR_DEFS, ClientContext, ClientLink, ClientOptionsOut, ClientPromiseResult, type CommonORPCErrorCode, type ConnectionStatus, DynamicLink, type EventIteratorState, InferClientContext, NestedClient, ORPCError, type ORPCErrorCode, type ORPCErrorJSON, type ORPCErrorOptions, type SafeResult, createORPCClient, type createORPCClientOptions, fallbackORPCErrorMessage, fallbackORPCErrorStatus, isDefinedError, onEventIteratorStatusChange, registerEventIteratorState, safe, toORPCError, updateEventIteratorStatus }; | ||
| type SafeClient<T extends NestedClient<any>> = T extends Client<infer UContext, infer UInput, infer UOutput, infer UError> ? (...rest: ClientRest<UContext, UInput>) => Promise<SafeResult<UOutput, UError>> : { | ||
| [K in keyof T]: T[K] extends NestedClient<any> ? SafeClient<T[K]> : never; | ||
| }; | ||
| /** | ||
| * Create a safe client that automatically wraps all procedure calls with the `safe` util. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const safeClient = createSafeClient(client) | ||
| * const { error, data, isDefined } = await safeClient.doSomething({ id: '123' }) | ||
| * ``` | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/error-handling#using-createsafeclient Safe Client Docs} | ||
| */ | ||
| declare function createSafeClient<T extends NestedClient<any>>(client: T): SafeClient<T>; | ||
| declare const ORPC_CLIENT_PACKAGE_NAME = "@orpc/client"; | ||
| declare const ORPC_CLIENT_PACKAGE_VERSION = "0.0.0-next.99d5d75"; | ||
| /** | ||
| * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks | ||
| * based on the request path, input, and context. | ||
| * | ||
| * @see {@link https://orpc.dev/docs/client/dynamic-link Dynamic Link Docs} | ||
| */ | ||
| declare class DynamicLink<TClientContext extends ClientContext> implements ClientLink<TClientContext> { | ||
| private readonly linkResolver; | ||
| constructor(linkResolver: (options: ClientOptions<TClientContext>, path: readonly string[], input: unknown) => Promisable<ClientLink<TClientContext>>); | ||
| call(path: readonly string[], input: unknown, options: ClientOptions<TClientContext>): Promise<unknown>; | ||
| } | ||
| declare function mapEventIterator<TYield, TReturn, TNext, TMap = TYield | TReturn>(iterator: AsyncIterator<TYield, TReturn, TNext>, maps: { | ||
| value: (value: NoInfer<TYield | TReturn>, done: boolean | undefined) => Promise<TMap>; | ||
| error: (error: unknown) => Promise<unknown>; | ||
| }): AsyncIteratorClass<TMap, TMap, TNext>; | ||
| export { COMMON_ORPC_ERROR_DEFS, Client, ClientContext, ClientLink, ClientOptions, ClientPromiseResult, ClientRest, DynamicLink, FriendlyClientOptions, InferClientContext, NestedClient, ORPCError, ORPC_CLIENT_PACKAGE_NAME, ORPC_CLIENT_PACKAGE_VERSION, consumeEventIterator, createORPCClient, createORPCErrorFromJson, createSafeClient, fallbackORPCErrorMessage, fallbackORPCErrorStatus, isDefinedError, isORPCErrorJson, isORPCErrorStatus, mapEventIterator, resolveFriendlyClientOptions, safe, toORPCError }; | ||
| export type { CommonORPCErrorCode, ConsumeEventIteratorOptions, ORPCErrorCode, ORPCErrorJSON, ORPCErrorOptions, SafeClient, SafeResult, createORPCClientOptions }; |
+86
-37
@@ -1,16 +0,71 @@ | ||
| import { i as isDefinedError } from './shared/client.XAn8cDTM.mjs'; | ||
| export { C as COMMON_ORPC_ERROR_DEFS, O as ORPCError, c as createAutoRetryEventIterator, a as fallbackORPCErrorMessage, f as fallbackORPCErrorStatus, m as mapEventIterator, o as onEventIteratorStatusChange, r as registerEventIteratorState, t as toORPCError, u as updateEventIteratorStatus } from './shared/client.XAn8cDTM.mjs'; | ||
| export { ErrorEvent } from '@orpc/standard-server'; | ||
| import '@orpc/shared'; | ||
| import { preventNativeAwait, isTypescriptObject } from '@orpc/shared'; | ||
| export { AsyncIteratorClass, EventPublisher, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared'; | ||
| import { i as isDefinedError } from './shared/client.CPTihQgC.mjs'; | ||
| export { C as COMMON_ORPC_ERROR_DEFS, c as ORPCError, O as ORPC_CLIENT_PACKAGE_NAME, a as ORPC_CLIENT_PACKAGE_VERSION, g as createORPCErrorFromJson, b as fallbackORPCErrorMessage, f as fallbackORPCErrorStatus, e as isORPCErrorJson, d as isORPCErrorStatus, t as toORPCError } from './shared/client.CPTihQgC.mjs'; | ||
| export { m as mapEventIterator } from './shared/client.BLtwTQUg.mjs'; | ||
| export { ErrorEvent, getEventMeta, withEventMeta } from '@orpc/standard-server'; | ||
| function createORPCClient(link, options) { | ||
| const path = options?.path ?? []; | ||
| const procedureClient = async (...[input, options2]) => { | ||
| const optionsOut = { | ||
| ...options2, | ||
| context: options2?.context ?? {} | ||
| // options.context can be undefined when all field is optional | ||
| }; | ||
| return await link.call(path, input, optionsOut); | ||
| async function safe(promise) { | ||
| try { | ||
| const output = await promise; | ||
| return Object.assign( | ||
| [null, output, false, true], | ||
| { error: null, data: output, isDefined: false, isSuccess: true } | ||
| ); | ||
| } catch (e) { | ||
| const error = e; | ||
| if (isDefinedError(error)) { | ||
| return Object.assign( | ||
| [error, void 0, true, false], | ||
| { error, data: void 0, isDefined: true, isSuccess: false } | ||
| ); | ||
| } | ||
| return Object.assign( | ||
| [error, void 0, false, false], | ||
| { error, data: void 0, isDefined: false, isSuccess: false } | ||
| ); | ||
| } | ||
| } | ||
| function resolveFriendlyClientOptions(options) { | ||
| return { | ||
| ...options, | ||
| context: options.context ?? {} | ||
| // Context only optional if all fields are optional | ||
| }; | ||
| } | ||
| function consumeEventIterator(iterator, options) { | ||
| void (async () => { | ||
| let onFinishState; | ||
| try { | ||
| const resolvedIterator = await iterator; | ||
| while (true) { | ||
| const { done, value } = await resolvedIterator.next(); | ||
| if (done) { | ||
| const realValue = value; | ||
| onFinishState = [null, realValue, true]; | ||
| options.onSuccess?.(realValue); | ||
| break; | ||
| } | ||
| options.onEvent(value); | ||
| } | ||
| } catch (error) { | ||
| onFinishState = [error, void 0, false]; | ||
| if (!options.onError && !options.onFinish) { | ||
| throw error; | ||
| } | ||
| options.onError?.(error); | ||
| } finally { | ||
| options.onFinish?.(onFinishState); | ||
| } | ||
| })(); | ||
| return async () => { | ||
| await (await iterator)?.return?.(); | ||
| }; | ||
| } | ||
| function createORPCClient(link, options = {}) { | ||
| const path = options.path ?? []; | ||
| const procedureClient = async (...[input, options2 = {}]) => { | ||
| return await link.call(path, input, resolveFriendlyClientOptions(options2)); | ||
| }; | ||
| const recursive = new Proxy(procedureClient, { | ||
@@ -27,5 +82,21 @@ get(target, key) { | ||
| }); | ||
| return recursive; | ||
| return preventNativeAwait(recursive); | ||
| } | ||
| function createSafeClient(client) { | ||
| const proxy = new Proxy((...args) => safe(client(...args)), { | ||
| get(_, prop, receiver) { | ||
| const value = Reflect.get(client, prop, receiver); | ||
| if (typeof prop !== "string") { | ||
| return value; | ||
| } | ||
| if (!isTypescriptObject(value)) { | ||
| return value; | ||
| } | ||
| return createSafeClient(value); | ||
| } | ||
| }); | ||
| return proxy; | ||
| } | ||
| class DynamicLink { | ||
@@ -42,24 +113,2 @@ constructor(linkResolver) { | ||
| async function safe(promise) { | ||
| try { | ||
| const output = await promise; | ||
| return Object.assign( | ||
| [null, output, false], | ||
| { error: null, data: output, isDefined: false } | ||
| ); | ||
| } catch (e) { | ||
| const error = e; | ||
| if (isDefinedError(error)) { | ||
| return Object.assign( | ||
| [error, void 0, true], | ||
| { error, data: void 0, isDefined: true } | ||
| ); | ||
| } | ||
| return Object.assign( | ||
| [error, void 0, false], | ||
| { error, data: void 0, isDefined: false } | ||
| ); | ||
| } | ||
| } | ||
| export { DynamicLink, createORPCClient, isDefinedError, safe }; | ||
| export { DynamicLink, consumeEventIterator, createORPCClient, createSafeClient, isDefinedError, resolveFriendlyClientOptions, safe }; |
+23
-8
| { | ||
| "name": "@orpc/client", | ||
| "type": "module", | ||
| "version": "0.0.0-next.999d654", | ||
| "version": "0.0.0-next.99d5d75", | ||
| "license": "MIT", | ||
| "homepage": "https://orpc.unnoq.com", | ||
| "homepage": "https://orpc.dev", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/unnoq/orpc.git", | ||
| "url": "git+https://github.com/middleapi/orpc.git", | ||
| "directory": "packages/client" | ||
| }, | ||
| "keywords": [ | ||
| "unnoq", | ||
| "orpc" | ||
@@ -22,2 +21,7 @@ ], | ||
| }, | ||
| "./plugins": { | ||
| "types": "./dist/plugins/index.d.mts", | ||
| "import": "./dist/plugins/index.mjs", | ||
| "default": "./dist/plugins/index.mjs" | ||
| }, | ||
| "./standard": { | ||
@@ -32,2 +36,12 @@ "types": "./dist/adapters/standard/index.d.mts", | ||
| "default": "./dist/adapters/fetch/index.mjs" | ||
| }, | ||
| "./websocket": { | ||
| "types": "./dist/adapters/websocket/index.d.mts", | ||
| "import": "./dist/adapters/websocket/index.mjs", | ||
| "default": "./dist/adapters/websocket/index.mjs" | ||
| }, | ||
| "./message-port": { | ||
| "types": "./dist/adapters/message-port/index.d.mts", | ||
| "import": "./dist/adapters/message-port/index.mjs", | ||
| "default": "./dist/adapters/message-port/index.mjs" | ||
| } | ||
@@ -39,8 +53,9 @@ }, | ||
| "dependencies": { | ||
| "@orpc/standard-server": "0.0.0-next.999d654", | ||
| "@orpc/standard-server-fetch": "0.0.0-next.999d654", | ||
| "@orpc/shared": "0.0.0-next.999d654" | ||
| "@orpc/shared": "0.0.0-next.99d5d75", | ||
| "@orpc/standard-server": "0.0.0-next.99d5d75", | ||
| "@orpc/standard-server-fetch": "0.0.0-next.99d5d75", | ||
| "@orpc/standard-server-peer": "0.0.0-next.99d5d75" | ||
| }, | ||
| "devDependencies": { | ||
| "zod": "^3.24.2" | ||
| "zod": "^4.3.6" | ||
| }, | ||
@@ -47,0 +62,0 @@ "scripts": { |
+145
-30
| <div align="center"> | ||
| <image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" /> | ||
| <image align="center" src="https://orpc.dev/logo.webp" width=280 alt="oRPC logo" /> | ||
| </div> | ||
@@ -8,4 +8,4 @@ | ||
| <div align="center"> | ||
| <a href="https://codecov.io/gh/unnoq/orpc"> | ||
| <img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg"> | ||
| <a href="https://codecov.io/gh/middleapi/orpc"> | ||
| <img alt="codecov" src="https://codecov.io/gh/middleapi/orpc/branch/main/graph/badge.svg"> | ||
| </a> | ||
@@ -15,4 +15,4 @@ <a href="https://www.npmjs.com/package/@orpc/client"> | ||
| </a> | ||
| <a href="https://github.com/unnoq/orpc/blob/main/LICENSE"> | ||
| <img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" /> | ||
| <a href="https://github.com/middleapi/orpc/blob/main/LICENSE"> | ||
| <img alt="MIT License" src="https://img.shields.io/github/license/middleapi/orpc?logo=open-source-initiative" /> | ||
| </a> | ||
@@ -22,2 +22,5 @@ <a href="https://discord.gg/TXEbwRBvQn"> | ||
| </a> | ||
| <a href="https://deepwiki.com/middleapi/orpc"> | ||
| <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"> | ||
| </a> | ||
| </div> | ||
@@ -27,3 +30,3 @@ | ||
| **oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards, ensuring a smooth and enjoyable developer experience. | ||
| **oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards | ||
@@ -34,22 +37,18 @@ --- | ||
| - **End-to-End Type Safety 🔒**: Ensure complete type safety from inputs to outputs and errors, bridging server and client seamlessly. | ||
| - **First-Class OpenAPI 📄**: Adheres to the OpenAPI standard out of the box, ensuring seamless integration and comprehensive API documentation. | ||
| - **Contract-First Development 📜**: (Optional) Define your API contract upfront and implement it with confidence. | ||
| - **Exceptional Developer Experience ✨**: Enjoy a streamlined workflow with robust typing and clear, in-code documentation. | ||
| - **Multi-Runtime Support 🌍**: Run your code seamlessly on Cloudflare, Deno, Bun, Node.js, and more. | ||
| - **Framework Integrations 🧩**: Supports Tanstack Query (React, Vue, Solid, Svelte), Pinia Colada, and more. | ||
| - **Server Actions ⚡️**: Fully compatible with React Server Actions on Next.js, TanStack Start, and more. | ||
| - **Standard Schema Support 🗂️**: Effortlessly work with Zod, Valibot, ArkType, and others right out of the box. | ||
| - **Fast & Lightweight 💨**: Built on native APIs across all runtimes – optimized for speed and efficiency. | ||
| - **Native Types 📦**: Enjoy built-in support for Date, File, Blob, BigInt, URL and more with no extra setup. | ||
| - **Lazy Router ⏱️**: Improve cold start times with our lazy routing feature. | ||
| - **SSE & Streaming 📡**: Provides SSE and streaming features – perfect for real-time notifications and AI-powered streaming responses. | ||
| - **Reusability 🔄**: Write once and reuse your code across multiple purposes effortlessly. | ||
| - **Extendability 🔌**: Easily enhance oRPC with plugins, middleware, and interceptors. | ||
| - **Reliability 🛡️**: Well-tested, fully TypeScript, production-ready, and MIT licensed for peace of mind. | ||
| - **Simplicity 💡**: Enjoy straightforward, clean code with no hidden magic. | ||
| - **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server. | ||
| - **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard. | ||
| - **📝 Contract-First Development**: Optionally define your API contract before implementation. | ||
| - **🔍 First-Class OpenTelemetry**: Seamlessly integrate with OpenTelemetry for observability. | ||
| - **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), SWR, Pinia Colada, and more. | ||
| - **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms. | ||
| - **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators. | ||
| - **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more. | ||
| - **⏱️ Lazy Router**: Enhance cold start times with our lazy routing feature. | ||
| - **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming. | ||
| - **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond. | ||
| - **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors. | ||
| ## Documentation | ||
| You can find the full documentation [here](https://orpc.unnoq.com). | ||
| You can find the full documentation [here](https://orpc.dev). | ||
@@ -61,13 +60,17 @@ ## Packages | ||
| - [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety. | ||
| - [@orpc/react-query](https://www.npmjs.com/package/@orpc/react-query): Integration with [React Query](https://tanstack.com/query/latest/docs/framework/react/overview). | ||
| - [@orpc/vue-query](https://www.npmjs.com/package/@orpc/vue-query): Integration with [Vue Query](https://tanstack.com/query/latest/docs/framework/vue/overview). | ||
| - [@orpc/solid-query](https://www.npmjs.com/package/@orpc/solid-query): Integration with [Solid Query](https://tanstack.com/query/latest/docs/framework/solid/overview). | ||
| - [@orpc/svelte-query](https://www.npmjs.com/package/@orpc/svelte-query): Integration with [Svelte Query](https://tanstack.com/query/latest/docs/framework/svelte/overview). | ||
| - [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests. | ||
| - [@orpc/otel](https://www.npmjs.com/package/@orpc/otel): [OpenTelemetry](https://opentelemetry.io/) integration for observability. | ||
| - [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/). | ||
| - [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions. | ||
| - [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration. | ||
| - [@orpc/experimental-react-swr](https://www.npmjs.com/package/@orpc/experimental-react-swr): [SWR](https://swr.vercel.app/) integration. | ||
| - [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/). | ||
| - [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests. | ||
| - [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration. | ||
| - [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet. | ||
| - [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/). | ||
| - [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/). | ||
| ## `@orpc/client` | ||
| Consume your API on the client with type-safety. Read the [documentation](https://orpc.unnoq.com/docs/client/client-side) for more information. | ||
| Consume your API on the client with type-safety. Read the [documentation](https://orpc.dev/docs/client/client-side) for more information. | ||
@@ -94,4 +97,116 @@ ```ts | ||
| ## Sponsors | ||
| If you find oRPC valuable and would like to support its development, you can do so here: [GitHub Sponsors](https://github.com/sponsors/dinwwwh). | ||
| ### 🏆 Platinum Sponsor | ||
| <table> | ||
| <tr> | ||
| <td align="center"><a href="https://screenshotone.com/?ref=orpc" target="_blank" rel="noopener" title="ScreenshotOne.com"><img src="https://avatars.githubusercontent.com/u/97035603?v=4" width="279" alt="ScreenshotOne.com"/><br />ScreenshotOne.com</a></td> | ||
| </tr> | ||
| </table> | ||
| ### 🥈 Silver Sponsor | ||
| <table> | ||
| <tr> | ||
| <td align="center"><a href="https://misskey.io/?ref=orpc" target="_blank" rel="noopener" title="村上さん"><img src="https://avatars.githubusercontent.com/u/37681609?u=0dd4c7e4ba937cbb52b068c55914b1d8164dc0c7&v=4" width="209" alt="村上さん"/><br />村上さん</a></td> | ||
| <td align="center"><a href="https://github.com/christ12938?ref=orpc" target="_blank" rel="noopener" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="209" alt="christ12938"/><br />christ12938</a></td> | ||
| </tr> | ||
| </table> | ||
| ### Generous Sponsors | ||
| <table> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/ln-markets?ref=orpc" target="_blank" rel="noopener" title="LN Markets"><img src="https://avatars.githubusercontent.com/u/70597625?v=4" width="167" alt="LN Markets"/><br />LN Markets</a></td> | ||
| </tr> | ||
| </table> | ||
| ### Sponsors | ||
| <table> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/hrmcdonald?ref=orpc" target="_blank" rel="noopener" title="Reece McDonald"><img src="https://avatars.githubusercontent.com/u/39349270?v=4" width="139" alt="Reece McDonald"/><br />Reece McDonald</a></td> | ||
| <td align="center"><a href="https://github.com/u1-liquid?ref=orpc" target="_blank" rel="noopener" title="あわわわとーにゅ"><img src="https://avatars.githubusercontent.com/u/17376330?u=de3353804be889f009f7e0a1582daf04d0ab292d&v=4" width="139" alt="あわわわとーにゅ"/><br />あわわわとーにゅ</a></td> | ||
| <td align="center"><a href="https://github.com/nicognaW?ref=orpc" target="_blank" rel="noopener" title="nk"><img src="https://avatars.githubusercontent.com/u/66731869?u=4699bda3a9092d3ec34fbd959450767bcc8b8b6d&v=4" width="139" alt="nk"/><br />nk</a></td> | ||
| <td align="center"><a href="https://github.com/supastarter?ref=orpc" target="_blank" rel="noopener" title="supastarter"><img src="https://avatars.githubusercontent.com/u/110960143?v=4" width="139" alt="supastarter"/><br />supastarter</a></td> | ||
| <td align="center"><a href="https://github.com/divmgl?ref=orpc" target="_blank" rel="noopener" title="Dexter Miguel"><img src="https://avatars.githubusercontent.com/u/5452298?u=645993204be8696c085ecf0d228c3062efe2ed65&v=4" width="139" alt="Dexter Miguel"/><br />Dexter Miguel</a></td> | ||
| <td align="center"><a href="https://github.com/herrfugbaum?ref=orpc" target="_blank" rel="noopener" title="herrfugbaum"><img src="https://avatars.githubusercontent.com/u/12859776?u=644dc1666d0220bc0468eb0de3c56b919f635b16&v=4" width="139" alt="herrfugbaum"/><br />herrfugbaum</a></td> | ||
| </tr> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/ryota-murakami?ref=orpc" target="_blank" rel="noopener" title="Ryota Murakami"><img src="https://avatars.githubusercontent.com/u/5501268?u=599389e03340734325726ca3f8f423c021d47d7f&v=4" width="139" alt="Ryota Murakami"/><br />Ryota Murakami</a></td> | ||
| <td align="center"><a href="https://github.com/dcramer?ref=orpc" target="_blank" rel="noopener" title="David Cramer"><img src="https://avatars.githubusercontent.com/u/23610?v=4" width="139" alt="David Cramer"/><br />David Cramer</a></td> | ||
| <td align="center"><a href="https://github.com/valerii15298?ref=orpc" target="_blank" rel="noopener" title="Valerii Petryniak"><img src="https://avatars.githubusercontent.com/u/44531564?u=88ac74d9bacd20401518441907acad21063cd397&v=4" width="139" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td> | ||
| <td align="center"><a href="https://github.com/happyboy2022?ref=orpc" target="_blank" rel="noopener" title="happyboy"><img src="https://avatars.githubusercontent.com/u/103669586?u=65b49c4b893ed3703909fbb3a7a22313f3f9c121&v=4" width="139" alt="happyboy"/><br />happyboy</a></td> | ||
| <td align="center"><a href="https://github.com/letstri?ref=orpc" target="_blank" rel="noopener" title="Valerii Strilets"><img src="https://avatars.githubusercontent.com/u/13253748?u=c7b10399ccc8f8081e24db94ec32cd9858e86ac3&v=4" width="139" alt="Valerii Strilets"/><br />Valerii Strilets</a></td> | ||
| <td align="center"><a href="https://github.com/K-Mistele?ref=orpc" target="_blank" rel="noopener" title="Kyle Mistele"><img src="https://avatars.githubusercontent.com/u/18430555?u=3afebeb81de666e35aaac3ed46f14159d7603ffb&v=4" width="139" alt="Kyle Mistele"/><br />Kyle Mistele</a></td> | ||
| </tr> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/andrewpeters9?ref=orpc" target="_blank" rel="noopener" title="Andrew Peters"><img src="https://avatars.githubusercontent.com/u/36251325?v=4" width="139" alt="Andrew Peters"/><br />Andrew Peters</a></td> | ||
| <td align="center"><a href="https://github.com/R44VC0RP?ref=orpc" target="_blank" rel="noopener" title="Ryan Vogel"><img src="https://avatars.githubusercontent.com/u/89211796?u=1857347b9787d8d8a7ea5bfc333f96be92d5a683&v=4" width="139" alt="Ryan Vogel"/><br />Ryan Vogel</a></td> | ||
| <td align="center"><a href="https://github.com/peter-adam-dy?ref=orpc" target="_blank" rel="noopener" title="Peter Adam"><img src="https://avatars.githubusercontent.com/u/132129459?u=4f3dbbb3b443990b56acb7d6a5d11ed2c555f6db&v=4" width="139" alt="Peter Adam"/><br />Peter Adam</a></td> | ||
| <td align="center"><a href="https://github.com/yukimotochern?ref=orpc" target="_blank" rel="noopener" title="Chen, Zhi-Yuan"><img src="https://avatars.githubusercontent.com/u/20896173?u=945c33fc21725e4d566a0d02afc54b136ca1d67a&v=4" width="139" alt="Chen, Zhi-Yuan"/><br />Chen, Zhi-Yuan</a></td> | ||
| <td align="center"><a href="https://github.com/Ryanjso?ref=orpc" target="_blank" rel="noopener" title="Ryan Soderberg"><img src="https://avatars.githubusercontent.com/u/39172778?u=5ed913c31d57e7221b75784abcad48c7ebddde27&v=4" width="139" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td> | ||
| </tr> | ||
| </table> | ||
| ### Backers | ||
| <table> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/rhinodavid?ref=orpc" target="_blank" rel="noopener" title="David Walsh"><img src="https://avatars.githubusercontent.com/u/5778036?u=b5521f07d2f88c3db2a0dae62b5f2f8357214af0&v=4" width="119" alt="David Walsh"/><br />David Walsh</a></td> | ||
| <td align="center"><a href="https://github.com/Robbe95?ref=orpc" target="_blank" rel="noopener" title="Robbe Vaes"><img src="https://avatars.githubusercontent.com/u/44748019?u=e0232402c045ad4eac7cbd217f1f47e083103b89&v=4" width="119" alt="Robbe Vaes"/><br />Robbe Vaes</a></td> | ||
| <td align="center"><a href="https://github.com/aidansunbury?ref=orpc" target="_blank" rel="noopener" title="Aidan Sunbury"><img src="https://avatars.githubusercontent.com/u/64103161?v=4" width="119" alt="Aidan Sunbury"/><br />Aidan Sunbury</a></td> | ||
| <td align="center"><a href="https://github.com/soonoo?ref=orpc" target="_blank" rel="noopener" title="soonoo"><img src="https://avatars.githubusercontent.com/u/5436405?u=5d0b4aa955c87e30e6bda7f0cccae5402da99528&v=4" width="119" alt="soonoo"/><br />soonoo</a></td> | ||
| <td align="center"><a href="https://github.com/kporten?ref=orpc" target="_blank" rel="noopener" title="Kevin Porten"><img src="https://avatars.githubusercontent.com/u/1839345?u=dc2263d5cfe0d927ce1a0be04a1d55dd6b55405c&v=4" width="119" alt="Kevin Porten"/><br />Kevin Porten</a></td> | ||
| <td align="center"><a href="https://github.com/pumpkinlink?ref=orpc" target="_blank" rel="noopener" title="Denis"><img src="https://avatars.githubusercontent.com/u/11864620?u=5f47bbe6c65d0f6f5cf011021490238e4b0593d0&v=4" width="119" alt="Denis"/><br />Denis</a></td> | ||
| <td align="center"><a href="https://github.com/christopher-kapic?ref=orpc" target="_blank" rel="noopener" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td> | ||
| </tr> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/thomasballinger?ref=orpc" target="_blank" rel="noopener" title="Tom Ballinger"><img src="https://avatars.githubusercontent.com/u/458879?u=4b045ac75d721b6ac2b42a74d7d37f61f0414031&v=4" width="119" alt="Tom Ballinger"/><br />Tom Ballinger</a></td> | ||
| <td align="center"><a href="https://github.com/SSam0419?ref=orpc" target="_blank" rel="noopener" title="Sam"><img src="https://avatars.githubusercontent.com/u/102863520?u=3c89611f549d5070be232eb4532f690c8f2e7a65&v=4" width="119" alt="Sam"/><br />Sam</a></td> | ||
| <td align="center"><a href="https://github.com/Titoine?ref=orpc" target="_blank" rel="noopener" title="Titoine"><img src="https://avatars.githubusercontent.com/u/3514286?u=1bb1e86b0c99c8a1121372e56d51a177eea12191&v=4" width="119" alt="Titoine"/><br />Titoine</a></td> | ||
| <td align="center"><a href="https://github.com/Mnigos?ref=orpc" target="_blank" rel="noopener" title="Igor Makowski"><img src="https://avatars.githubusercontent.com/u/56691628?u=ee8c879478f7c151b9156aef6c74243fa3e247a8&v=4" width="119" alt="Igor Makowski"/><br />Igor Makowski</a></td> | ||
| <td align="center"><a href="https://github.com/steelbrain?ref=orpc" target="_blank" rel="noopener" title="Anees Iqbal"><img src="https://avatars.githubusercontent.com/u/4278113?u=22b80b5399eed68ac76cd58b02961b0481f1db11&v=4" width="119" alt="Anees Iqbal"/><br />Anees Iqbal</a></td> | ||
| <td align="center"><a href="https://github.com/hanayashiki?ref=orpc" target="_blank" rel="noopener" title="wang chenyu"><img src="https://avatars.githubusercontent.com/u/26056783?u=98c5ceda64b19874ed2a31515467332ea991e590&v=4" width="119" alt="wang chenyu"/><br />wang chenyu</a></td> | ||
| <td align="center"><a href="https://github.com/piscis?ref=orpc" target="_blank" rel="noopener" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&v=4" width="119" alt="Alex"/><br />Alex</a></td> | ||
| </tr> | ||
| </table> | ||
| ### Past Sponsors | ||
| <p> | ||
| <a href="https://github.com/MrMaxie?ref=orpc" target="_blank" rel="noopener" title="Maxie"><img src="https://avatars.githubusercontent.com/u/3857836?u=5e6b57973d4385d655663ffdd836e487856f2984&v=4" width="32" height="32" alt="Maxie" /></a> | ||
| <a href="https://github.com/Stijn-Timmer?ref=orpc" target="_blank" rel="noopener" title="Stijn Timmer"><img src="https://avatars.githubusercontent.com/u/100147665?u=106b2c18e9c98a61861b4ee7fc100f5b9906a6c9&v=4" width="32" height="32" alt="Stijn Timmer" /></a> | ||
| <a href="https://github.com/zuplo?ref=orpc" target="_blank" rel="noopener" title="Zuplo"><img src="https://avatars.githubusercontent.com/u/85497839?v=4" width="32" height="32" alt="Zuplo" /></a> | ||
| <a href="https://github.com/motopods?ref=orpc" target="_blank" rel="noopener" title="motopods"><img src="https://avatars.githubusercontent.com/u/58200641?u=18833983d65b481ae90a4adec2373064ec58bcf3&v=4" width="32" height="32" alt="motopods" /></a> | ||
| <a href="https://github.com/franciscohermida?ref=orpc" target="_blank" rel="noopener" title="Francisco Hermida"><img src="https://avatars.githubusercontent.com/u/483242?u=bbcbc80eb9d8781ff401f7dafc3b59cd7bea0561&v=4" width="32" height="32" alt="Francisco Hermida" /></a> | ||
| <a href="https://github.com/theoludwig?ref=orpc" target="_blank" rel="noopener" title="Théo LUDWIG"><img src="https://avatars.githubusercontent.com/u/25207499?u=a6a9653725a2f574c07893748806668e0598cdbe&v=4" width="32" height="32" alt="Théo LUDWIG" /></a> | ||
| <a href="https://github.com/abhay-ramesh?ref=orpc" target="_blank" rel="noopener" title="Abhay Ramesh"><img src="https://avatars.githubusercontent.com/u/66196314?u=c5c2b0327b26606c2efcfaf17046ab18c3d25c57&v=4" width="32" height="32" alt="Abhay Ramesh" /></a> | ||
| <a href="https://github.com/shr-ink?ref=orpc" target="_blank" rel="noopener" title="shr.ink oü"><img src="https://avatars.githubusercontent.com/u/139700438?v=4" width="32" height="32" alt="shr.ink oü" /></a> | ||
| <a href="https://github.com/johngerome?ref=orpc" target="_blank" rel="noopener" title="0x4e32"><img src="https://avatars.githubusercontent.com/u/2002000?u=24e8dd943cfc862aa284d858a023532c75071ade&v=4" width="32" height="32" alt="0x4e32" /></a> | ||
| <a href="https://github.com/yzuyr?ref=orpc" target="_blank" rel="noopener" title="Ryuz"><img src="https://avatars.githubusercontent.com/u/196539378?u=d38374588d219b6748b16406982f6559411466d4&v=4" width="32" height="32" alt="Ryuz" /></a> | ||
| <a href="https://github.com/YiCChi?ref=orpc" target="_blank" rel="noopener" title="yicchi"><img src="https://avatars.githubusercontent.com/u/86967274?u=6c2756f09fe15dd94d572f560e979cd157982852&v=4" width="32" height="32" alt="yicchi" /></a> | ||
| <a href="https://github.com/cloudycotton?ref=orpc" target="_blank" rel="noopener" title="Saksham"><img src="https://avatars.githubusercontent.com/u/168998965?u=9b9634a5aed66a51c1b880663272725b00b92b14&v=4" width="32" height="32" alt="Saksham" /></a> | ||
| <a href="https://github.com/hrynevychroman?ref=orpc" target="_blank" rel="noopener" title="Roman Hrynevych"><img src="https://avatars.githubusercontent.com/u/82209198?u=1a1d111ab3d589855b9cc8a7fefb1b5c6a4fbbaf&v=4" width="32" height="32" alt="Roman Hrynevych" /></a> | ||
| <a href="https://github.com/rokitgg?ref=orpc" target="_blank" rel="noopener" title="rokitg"><img src="https://avatars.githubusercontent.com/u/125133357?u=06c74aefaa2236b06a2e5fba5a5c612339f45912&v=4" width="32" height="32" alt="rokitg" /></a> | ||
| <a href="https://github.com/omarkhatibgg?ref=orpc" target="_blank" rel="noopener" title="Omar Khatib"><img src="https://avatars.githubusercontent.com/u/9054278?u=afbba7331b85c51b8eee4130f5fd31b1017dc919&v=4" width="32" height="32" alt="Omar Khatib" /></a> | ||
| <a href="https://github.com/YuSabo90002?ref=orpc" target="_blank" rel="noopener" title="Yu-Sabo"><img src="https://avatars.githubusercontent.com/u/13120582?v=4" width="32" height="32" alt="Yu-Sabo" /></a> | ||
| <a href="https://github.com/bapspatil?ref=orpc" target="_blank" rel="noopener" title="Bapusaheb Patil"><img src="https://avatars.githubusercontent.com/u/16699418?v=4" width="32" height="32" alt="Bapusaheb Patil" /></a> | ||
| <a href="https://github.com/ripgrim?ref=orpc" target="_blank" rel="noopener" title="grim"><img src="https://avatars.githubusercontent.com/u/75869731?u=b17c42ec2309552fdb822a86b25a2f99146a4d72&v=4" width="32" height="32" alt="grim" /></a> | ||
| <a href="https://github.com/nelsonlaidev?ref=orpc" target="_blank" rel="noopener" title="Nelson Lai"><img src="https://avatars.githubusercontent.com/u/75498339?u=2fc0e0b95dd184c5ffb744df977cb15a18b60672&v=4" width="32" height="32" alt="Nelson Lai" /></a> | ||
| <a href="https://github.com/nguyenlc1993?ref=orpc" target="_blank" rel="noopener" title="Lê Cao Nguyên"><img src="https://avatars.githubusercontent.com/u/13871971?u=83c8b69d9e35b589c4e1f066cc113b1d9461386f&v=4" width="32" height="32" alt="Lê Cao Nguyên" /></a> | ||
| <a href="https://github.com/wobsoriano?ref=orpc" target="_blank" rel="noopener" title="Robert Soriano"><img src="https://avatars.githubusercontent.com/u/13049130?u=6d72104182e7c9ed25934815313fb69107332111&v=4" width="32" height="32" alt="Robert Soriano" /></a> | ||
| <a href="https://github.com/SKostyukovich?ref=orpc" target="_blank" rel="noopener" title="SKostyukovich"><img src="https://avatars.githubusercontent.com/u/10700067?v=4" width="32" height="32" alt="SKostyukovich" /></a> | ||
| <a href="https://github.com/FabworksHQ?ref=orpc" target="_blank" rel="noopener" title="Fabworks"><img src="https://avatars.githubusercontent.com/u/160179500?v=4" width="32" height="32" alt="Fabworks" /></a> | ||
| <a href="https://github.com/NovakAnton?ref=orpc" target="_blank" rel="noopener" title="Novak Antonijevic"><img src="https://avatars.githubusercontent.com/u/157126729?u=ae49fa22292d55c0434ff0ca008206155b18663b&v=4" width="32" height="32" alt="Novak Antonijevic" /></a> | ||
| <a href="https://github.com/laduniestu?ref=orpc" target="_blank" rel="noopener" title="Laduni Estu Syalwa"><img src="https://avatars.githubusercontent.com/u/44757637?u=a2fc1ea8f7d827a96721176f79d30592d1c48059&v=4" width="32" height="32" alt="Laduni Estu Syalwa" /></a> | ||
| <a href="https://github.com/illarionvk?ref=orpc" target="_blank" rel="noopener" title="Illarion Koperski"><img src="https://avatars.githubusercontent.com/u/5012724?u=7cfa13652f7ac5fb3c56d880e3eb3fbe40c3ea34&v=4" width="32" height="32" alt="Illarion Koperski" /></a> | ||
| <a href="https://github.com/Scrumplex?ref=orpc" target="_blank" rel="noopener" title="Sefa Eyeoglu"><img src="https://avatars.githubusercontent.com/u/11587657?u=ab503582165c0bbff0cca47ce31c9450bb1553c9&v=4" width="32" height="32" alt="Sefa Eyeoglu" /></a> | ||
| </p> | ||
| ## License | ||
| Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information. | ||
| Distributed under the MIT License. See [LICENSE](https://github.com/middleapi/orpc/blob/main/LICENSE) for more information. |
| type ClientContext = Record<string, any>; | ||
| type ClientOptions<TClientContext extends ClientContext> = { | ||
| signal?: AbortSignal; | ||
| lastEventId?: string | undefined; | ||
| } & (Record<never, never> extends TClientContext ? { | ||
| context?: TClientContext; | ||
| } : { | ||
| context: TClientContext; | ||
| }); | ||
| type ClientRest<TClientContext extends ClientContext, TInput> = Record<never, never> extends TClientContext ? undefined extends TInput ? [input?: TInput, options?: ClientOptions<TClientContext>] : [input: TInput, options?: ClientOptions<TClientContext>] : [input: TInput, options: ClientOptions<TClientContext>]; | ||
| type ClientPromiseResult<TOutput, TError extends Error> = Promise<TOutput> & { | ||
| __error?: { | ||
| type: TError; | ||
| }; | ||
| }; | ||
| interface Client<TClientContext extends ClientContext, TInput, TOutput, TError extends Error> { | ||
| (...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>; | ||
| } | ||
| type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | { | ||
| [k: string]: NestedClient<TClientContext>; | ||
| }; | ||
| type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never; | ||
| type ClientOptionsOut<TClientContext extends ClientContext> = ClientOptions<TClientContext> & { | ||
| context: TClientContext; | ||
| }; | ||
| interface ClientLink<TClientContext extends ClientContext> { | ||
| call: (path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>) => Promise<unknown>; | ||
| } | ||
| declare function mapEventIterator<TYield, TReturn, TNext, TMap = TYield | TReturn>(iterator: AsyncIterator<TYield, TReturn, TNext>, maps: { | ||
| value: (value: NoInfer<TYield | TReturn>, done: boolean | undefined) => Promise<TMap>; | ||
| error: (error: unknown) => Promise<unknown>; | ||
| }): AsyncGenerator<TMap, TMap, TNext>; | ||
| interface EventIteratorReconnectOptions { | ||
| lastRetry: number | undefined; | ||
| lastEventId: string | undefined; | ||
| retryTimes: number; | ||
| error: unknown; | ||
| } | ||
| declare function createAutoRetryEventIterator<TYield, TReturn>(initial: AsyncIterator<TYield, TReturn, void>, reconnect: (options: EventIteratorReconnectOptions) => Promise<AsyncIterator<TYield, TReturn, void> | null>, initialLastEventId: string | undefined): AsyncGenerator<TYield, TReturn, void>; | ||
| export { type ClientContext as C, type EventIteratorReconnectOptions as E, type InferClientContext as I, type NestedClient as N, type ClientOptionsOut as a, type ClientLink as b, type ClientPromiseResult as c, createAutoRetryEventIterator as d, type ClientOptions as e, type ClientRest as f, type Client as g, mapEventIterator as m }; |
| type ClientContext = Record<string, any>; | ||
| type ClientOptions<TClientContext extends ClientContext> = { | ||
| signal?: AbortSignal; | ||
| lastEventId?: string | undefined; | ||
| } & (Record<never, never> extends TClientContext ? { | ||
| context?: TClientContext; | ||
| } : { | ||
| context: TClientContext; | ||
| }); | ||
| type ClientRest<TClientContext extends ClientContext, TInput> = Record<never, never> extends TClientContext ? undefined extends TInput ? [input?: TInput, options?: ClientOptions<TClientContext>] : [input: TInput, options?: ClientOptions<TClientContext>] : [input: TInput, options: ClientOptions<TClientContext>]; | ||
| type ClientPromiseResult<TOutput, TError extends Error> = Promise<TOutput> & { | ||
| __error?: { | ||
| type: TError; | ||
| }; | ||
| }; | ||
| interface Client<TClientContext extends ClientContext, TInput, TOutput, TError extends Error> { | ||
| (...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>; | ||
| } | ||
| type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | { | ||
| [k: string]: NestedClient<TClientContext>; | ||
| }; | ||
| type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never; | ||
| type ClientOptionsOut<TClientContext extends ClientContext> = ClientOptions<TClientContext> & { | ||
| context: TClientContext; | ||
| }; | ||
| interface ClientLink<TClientContext extends ClientContext> { | ||
| call: (path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>) => Promise<unknown>; | ||
| } | ||
| declare function mapEventIterator<TYield, TReturn, TNext, TMap = TYield | TReturn>(iterator: AsyncIterator<TYield, TReturn, TNext>, maps: { | ||
| value: (value: NoInfer<TYield | TReturn>, done: boolean | undefined) => Promise<TMap>; | ||
| error: (error: unknown) => Promise<unknown>; | ||
| }): AsyncGenerator<TMap, TMap, TNext>; | ||
| interface EventIteratorReconnectOptions { | ||
| lastRetry: number | undefined; | ||
| lastEventId: string | undefined; | ||
| retryTimes: number; | ||
| error: unknown; | ||
| } | ||
| declare function createAutoRetryEventIterator<TYield, TReturn>(initial: AsyncIterator<TYield, TReturn, void>, reconnect: (options: EventIteratorReconnectOptions) => Promise<AsyncIterator<TYield, TReturn, void> | null>, initialLastEventId: string | undefined): AsyncGenerator<TYield, TReturn, void>; | ||
| export { type ClientContext as C, type EventIteratorReconnectOptions as E, type InferClientContext as I, type NestedClient as N, type ClientOptionsOut as a, type ClientLink as b, type ClientPromiseResult as c, createAutoRetryEventIterator as d, type ClientOptions as e, type ClientRest as f, type Client as g, mapEventIterator as m }; |
| import { intercept, isAsyncIteratorObject, value, isObject, trim, stringifyJSON } from '@orpc/shared'; | ||
| import { c as createAutoRetryEventIterator, O as ORPCError, m as mapEventIterator, t as toORPCError } from './client.XAn8cDTM.mjs'; | ||
| import { ErrorEvent } from '@orpc/standard-server'; | ||
| class InvalidEventIteratorRetryResponse extends Error { | ||
| } | ||
| class StandardLink { | ||
| constructor(codec, sender, options) { | ||
| this.codec = codec; | ||
| this.sender = sender; | ||
| this.eventIteratorMaxRetries = options.eventIteratorMaxRetries ?? 5; | ||
| this.eventIteratorRetryDelay = options.eventIteratorRetryDelay ?? ((o) => o.lastRetry ?? 1e3 * 2 ** o.retryTimes); | ||
| this.eventIteratorShouldRetry = options.eventIteratorShouldRetry ?? true; | ||
| this.interceptors = options.interceptors ?? []; | ||
| this.clientInterceptors = options.clientInterceptors ?? []; | ||
| } | ||
| eventIteratorMaxRetries; | ||
| eventIteratorRetryDelay; | ||
| eventIteratorShouldRetry; | ||
| interceptors; | ||
| clientInterceptors; | ||
| call(path, input, options) { | ||
| return intercept(this.interceptors, { path, input, options }, async ({ path: path2, input: input2, options: options2 }) => { | ||
| const output = await this.#call(path2, input2, options2); | ||
| if (!isAsyncIteratorObject(output)) { | ||
| return output; | ||
| } | ||
| return createAutoRetryEventIterator(output, async (reconnectOptions) => { | ||
| const maxRetries = await value(this.eventIteratorMaxRetries, reconnectOptions, options2, path2, input2); | ||
| if (options2.signal?.aborted || reconnectOptions.retryTimes > maxRetries) { | ||
| return null; | ||
| } | ||
| const shouldRetry = await value(this.eventIteratorShouldRetry, reconnectOptions, options2, path2, input2); | ||
| if (!shouldRetry) { | ||
| return null; | ||
| } | ||
| const retryDelay = await value(this.eventIteratorRetryDelay, reconnectOptions, options2, path2, input2); | ||
| await new Promise((resolve) => setTimeout(resolve, retryDelay)); | ||
| const updatedOptions = { ...options2, lastEventId: reconnectOptions.lastEventId }; | ||
| const maybeIterator = await this.#call(path2, input2, updatedOptions); | ||
| if (!isAsyncIteratorObject(maybeIterator)) { | ||
| throw new InvalidEventIteratorRetryResponse("Invalid Event Iterator retry response"); | ||
| } | ||
| return maybeIterator; | ||
| }, options2.lastEventId); | ||
| }); | ||
| } | ||
| async #call(path, input, options) { | ||
| const request = await this.codec.encode(path, input, options); | ||
| const response = await intercept( | ||
| this.clientInterceptors, | ||
| { request }, | ||
| ({ request: request2 }) => this.sender.call(request2, options, path, input) | ||
| ); | ||
| const output = await this.codec.decode(response, options, path, input); | ||
| return output; | ||
| } | ||
| } | ||
| class RPCJsonSerializer { | ||
| serialize(data, segments = [], meta = [], maps = [], blobs = []) { | ||
| if (data instanceof Blob) { | ||
| maps.push(segments); | ||
| blobs.push(data); | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| if (typeof data === "bigint") { | ||
| meta.push([0, segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof Date) { | ||
| meta.push([1, segments]); | ||
| if (Number.isNaN(data.getTime())) { | ||
| return [null, meta, maps, blobs]; | ||
| } | ||
| return [data.toISOString(), meta, maps, blobs]; | ||
| } | ||
| if (Number.isNaN(data)) { | ||
| meta.push([2, segments]); | ||
| return [null, meta, maps, blobs]; | ||
| } | ||
| if (data instanceof URL) { | ||
| meta.push([4, segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof RegExp) { | ||
| meta.push([5, segments]); | ||
| return [data.toString(), meta, maps, blobs]; | ||
| } | ||
| if (data instanceof Set) { | ||
| const result = this.serialize(Array.from(data), segments, meta, maps, blobs); | ||
| meta.push([6, segments]); | ||
| return result; | ||
| } | ||
| if (data instanceof Map) { | ||
| const result = this.serialize(Array.from(data.entries()), segments, meta, maps, blobs); | ||
| meta.push([7, segments]); | ||
| return result; | ||
| } | ||
| if (Array.isArray(data)) { | ||
| const json = data.map((v, i) => { | ||
| if (v === void 0) { | ||
| meta.push([3, [...segments, i]]); | ||
| return v; | ||
| } | ||
| return this.serialize(v, [...segments, i], meta, maps, blobs)[0]; | ||
| }); | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| if (isObject(data)) { | ||
| const json = {}; | ||
| for (const k in data) { | ||
| json[k] = this.serialize(data[k], [...segments, k], meta, maps, blobs)[0]; | ||
| } | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| deserialize(json, meta, maps, getBlob) { | ||
| const ref = { data: json }; | ||
| if (maps && getBlob) { | ||
| maps.forEach((segments, i) => { | ||
| let currentRef = ref; | ||
| let preSegment = "data"; | ||
| segments.forEach((segment) => { | ||
| currentRef = currentRef[preSegment]; | ||
| preSegment = segment; | ||
| }); | ||
| currentRef[preSegment] = getBlob(i); | ||
| }); | ||
| } | ||
| for (const [type, segments] of meta) { | ||
| let currentRef = ref; | ||
| let preSegment = "data"; | ||
| segments.forEach((segment) => { | ||
| currentRef = currentRef[preSegment]; | ||
| preSegment = segment; | ||
| }); | ||
| switch (type) { | ||
| case 0: | ||
| currentRef[preSegment] = BigInt(currentRef[preSegment]); | ||
| break; | ||
| case 1: | ||
| currentRef[preSegment] = new Date(currentRef[preSegment] ?? "Invalid Date"); | ||
| break; | ||
| case 2: | ||
| currentRef[preSegment] = Number.NaN; | ||
| break; | ||
| case 3: | ||
| currentRef[preSegment] = void 0; | ||
| break; | ||
| case 4: | ||
| currentRef[preSegment] = new URL(currentRef[preSegment]); | ||
| break; | ||
| case 5: { | ||
| const [, pattern, flags] = currentRef[preSegment].match(/^\/(.*)\/([a-z]*)$/); | ||
| currentRef[preSegment] = new RegExp(pattern, flags); | ||
| break; | ||
| } | ||
| case 6: | ||
| currentRef[preSegment] = new Set(currentRef[preSegment]); | ||
| break; | ||
| case 7: | ||
| currentRef[preSegment] = new Map(currentRef[preSegment]); | ||
| break; | ||
| } | ||
| } | ||
| return ref.data; | ||
| } | ||
| } | ||
| class StandardRPCLinkCodec { | ||
| constructor(serializer, options) { | ||
| this.serializer = serializer; | ||
| this.baseUrl = options.url; | ||
| this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
| this.fallbackMethod = options.fallbackMethod ?? "POST"; | ||
| this.expectedMethod = options.method ?? this.fallbackMethod; | ||
| this.headers = options.headers ?? {}; | ||
| } | ||
| baseUrl; | ||
| maxUrlLength; | ||
| fallbackMethod; | ||
| expectedMethod; | ||
| headers; | ||
| async encode(path, input, options) { | ||
| const expectedMethod = await value(this.expectedMethod, options, path, input); | ||
| const headers = await value(this.headers, options, path, input); | ||
| const baseUrl = await value(this.baseUrl, options, path, input); | ||
| const url = new URL(`${trim(baseUrl.toString(), "/")}/${path.map(encodeURIComponent).join("/")}`); | ||
| const serialized = this.serializer.serialize(input); | ||
| if (expectedMethod === "GET" && !(serialized instanceof FormData) && !(serialized instanceof Blob) && !isAsyncIteratorObject(serialized)) { | ||
| const maxUrlLength = await value(this.maxUrlLength, options, path, input); | ||
| const getUrl = new URL(url); | ||
| getUrl.searchParams.append("data", stringifyJSON(serialized) ?? ""); | ||
| if (getUrl.toString().length <= maxUrlLength) { | ||
| return { | ||
| body: void 0, | ||
| method: expectedMethod, | ||
| headers, | ||
| url: getUrl, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| } | ||
| return { | ||
| url, | ||
| method: expectedMethod === "GET" ? this.fallbackMethod : expectedMethod, | ||
| headers, | ||
| body: serialized, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| async decode(response) { | ||
| const isOk = response.status >= 200 && response.status < 300; | ||
| const deserialized = await (async () => { | ||
| let isBodyOk = false; | ||
| try { | ||
| const body = await response.body(); | ||
| isBodyOk = true; | ||
| return this.serializer.deserialize(body); | ||
| } catch (error) { | ||
| if (!isBodyOk) { | ||
| throw new Error("Cannot parse response body, please check the response body and content-type.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| throw new Error("Invalid RPC response format.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| })(); | ||
| if (!isOk) { | ||
| if (ORPCError.isValidJSON(deserialized)) { | ||
| throw ORPCError.fromJSON(deserialized); | ||
| } | ||
| throw new Error("Invalid RPC error response format.", { | ||
| cause: deserialized | ||
| }); | ||
| } | ||
| return deserialized; | ||
| } | ||
| } | ||
| class RPCSerializer { | ||
| constructor(jsonSerializer = new RPCJsonSerializer()) { | ||
| this.jsonSerializer = jsonSerializer; | ||
| } | ||
| serialize(data) { | ||
| if (isAsyncIteratorObject(data)) { | ||
| return mapEventIterator(data, { | ||
| value: async (value) => this.#serialize(value, false), | ||
| error: async (e) => { | ||
| return new ErrorEvent({ | ||
| data: this.#serialize(toORPCError(e).toJSON(), false), | ||
| cause: e | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| return this.#serialize(data, true); | ||
| } | ||
| #serialize(data, enableFormData) { | ||
| if (data === void 0 || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| const [json, meta_, maps, blobs] = this.jsonSerializer.serialize(data); | ||
| const meta = meta_.length === 0 ? void 0 : meta_; | ||
| if (!enableFormData || blobs.length === 0) { | ||
| return { | ||
| json, | ||
| meta | ||
| }; | ||
| } | ||
| const form = new FormData(); | ||
| form.set("data", stringifyJSON({ json, meta, maps })); | ||
| blobs.forEach((blob, i) => { | ||
| form.set(i.toString(), blob); | ||
| }); | ||
| return form; | ||
| } | ||
| deserialize(data) { | ||
| if (isAsyncIteratorObject(data)) { | ||
| return mapEventIterator(data, { | ||
| value: async (value) => this.#deserialize(value), | ||
| error: async (e) => { | ||
| if (!(e instanceof ErrorEvent)) { | ||
| return e; | ||
| } | ||
| const deserialized = this.#deserialize(e.data); | ||
| if (ORPCError.isValidJSON(deserialized)) { | ||
| return ORPCError.fromJSON(deserialized, { cause: e }); | ||
| } | ||
| return new ErrorEvent({ | ||
| data: deserialized, | ||
| cause: e | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| return this.#deserialize(data); | ||
| } | ||
| #deserialize(data) { | ||
| if (data === void 0 || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| if (!(data instanceof FormData)) { | ||
| return this.jsonSerializer.deserialize(data.json, data.meta ?? []); | ||
| } | ||
| const serialized = JSON.parse(data.get("data")); | ||
| return this.jsonSerializer.deserialize( | ||
| serialized.json, | ||
| serialized.meta ?? [], | ||
| serialized.maps, | ||
| (i) => data.get(i.toString()) | ||
| ); | ||
| } | ||
| } | ||
| export { InvalidEventIteratorRetryResponse as I, RPCJsonSerializer as R, StandardLink as S, StandardRPCLinkCodec as a, RPCSerializer as b }; |
| import { isObject, isTypescriptObject, retry } from '@orpc/shared'; | ||
| import { getEventMeta, withEventMeta } from '@orpc/standard-server'; | ||
| const COMMON_ORPC_ERROR_DEFS = { | ||
| BAD_REQUEST: { | ||
| status: 400, | ||
| message: "Bad Request" | ||
| }, | ||
| UNAUTHORIZED: { | ||
| status: 401, | ||
| message: "Unauthorized" | ||
| }, | ||
| FORBIDDEN: { | ||
| status: 403, | ||
| message: "Forbidden" | ||
| }, | ||
| NOT_FOUND: { | ||
| status: 404, | ||
| message: "Not Found" | ||
| }, | ||
| METHOD_NOT_SUPPORTED: { | ||
| status: 405, | ||
| message: "Method Not Supported" | ||
| }, | ||
| NOT_ACCEPTABLE: { | ||
| status: 406, | ||
| message: "Not Acceptable" | ||
| }, | ||
| TIMEOUT: { | ||
| status: 408, | ||
| message: "Request Timeout" | ||
| }, | ||
| CONFLICT: { | ||
| status: 409, | ||
| message: "Conflict" | ||
| }, | ||
| PRECONDITION_FAILED: { | ||
| status: 412, | ||
| message: "Precondition Failed" | ||
| }, | ||
| PAYLOAD_TOO_LARGE: { | ||
| status: 413, | ||
| message: "Payload Too Large" | ||
| }, | ||
| UNSUPPORTED_MEDIA_TYPE: { | ||
| status: 415, | ||
| message: "Unsupported Media Type" | ||
| }, | ||
| UNPROCESSABLE_CONTENT: { | ||
| status: 422, | ||
| message: "Unprocessable Content" | ||
| }, | ||
| TOO_MANY_REQUESTS: { | ||
| status: 429, | ||
| message: "Too Many Requests" | ||
| }, | ||
| CLIENT_CLOSED_REQUEST: { | ||
| status: 499, | ||
| message: "Client Closed Request" | ||
| }, | ||
| INTERNAL_SERVER_ERROR: { | ||
| status: 500, | ||
| message: "Internal Server Error" | ||
| }, | ||
| NOT_IMPLEMENTED: { | ||
| status: 501, | ||
| message: "Not Implemented" | ||
| }, | ||
| BAD_GATEWAY: { | ||
| status: 502, | ||
| message: "Bad Gateway" | ||
| }, | ||
| SERVICE_UNAVAILABLE: { | ||
| status: 503, | ||
| message: "Service Unavailable" | ||
| }, | ||
| GATEWAY_TIMEOUT: { | ||
| status: 504, | ||
| message: "Gateway Timeout" | ||
| } | ||
| }; | ||
| function fallbackORPCErrorStatus(code, status) { | ||
| return status ?? COMMON_ORPC_ERROR_DEFS[code]?.status ?? 500; | ||
| } | ||
| function fallbackORPCErrorMessage(code, message) { | ||
| return message || COMMON_ORPC_ERROR_DEFS[code]?.message || code; | ||
| } | ||
| class ORPCError extends Error { | ||
| defined; | ||
| code; | ||
| status; | ||
| data; | ||
| constructor(code, ...[options]) { | ||
| if (options?.status && (options.status < 400 || options.status >= 600)) { | ||
| throw new Error("[ORPCError] The error status code must be in the 400-599 range."); | ||
| } | ||
| const message = fallbackORPCErrorMessage(code, options?.message); | ||
| super(message, options); | ||
| this.code = code; | ||
| this.status = fallbackORPCErrorStatus(code, options?.status); | ||
| this.defined = options?.defined ?? false; | ||
| this.data = options?.data; | ||
| } | ||
| toJSON() { | ||
| return { | ||
| defined: this.defined, | ||
| code: this.code, | ||
| status: this.status, | ||
| message: this.message, | ||
| data: this.data | ||
| }; | ||
| } | ||
| static fromJSON(json, options) { | ||
| return new ORPCError(json.code, { | ||
| ...options, | ||
| ...json | ||
| }); | ||
| } | ||
| static isValidJSON(json) { | ||
| if (!isObject(json)) { | ||
| return false; | ||
| } | ||
| const validKeys = ["defined", "code", "status", "message", "data"]; | ||
| if (Object.keys(json).some((k) => !validKeys.includes(k))) { | ||
| return false; | ||
| } | ||
| return "defined" in json && typeof json.defined === "boolean" && "code" in json && typeof json.code === "string" && "status" in json && typeof json.status === "number" && "message" in json && typeof json.message === "string"; | ||
| } | ||
| } | ||
| function isDefinedError(error) { | ||
| return error instanceof ORPCError && error.defined; | ||
| } | ||
| function toORPCError(error) { | ||
| return error instanceof ORPCError ? error : new ORPCError("INTERNAL_SERVER_ERROR", { | ||
| message: "Internal server error", | ||
| cause: error | ||
| }); | ||
| } | ||
| const iteratorStates = /* @__PURE__ */ new WeakMap(); | ||
| function registerEventIteratorState(iterator, state) { | ||
| iteratorStates.set(iterator, state); | ||
| } | ||
| function updateEventIteratorStatus(state, status) { | ||
| if (state.status !== status) { | ||
| state.status = status; | ||
| state.listeners.forEach((cb) => cb(status)); | ||
| } | ||
| } | ||
| function onEventIteratorStatusChange(iterator, callback, options = {}) { | ||
| const notifyImmediately = options.notifyImmediately ?? true; | ||
| const state = iteratorStates.get(iterator); | ||
| if (!state) { | ||
| throw new Error("Iterator is not registered."); | ||
| } | ||
| if (notifyImmediately) { | ||
| callback(state.status); | ||
| } | ||
| state.listeners.push(callback); | ||
| return () => { | ||
| const index = state.listeners.indexOf(callback); | ||
| if (index !== -1) { | ||
| state.listeners.splice(index, 1); | ||
| } | ||
| }; | ||
| } | ||
| function mapEventIterator(iterator, maps) { | ||
| return async function* () { | ||
| try { | ||
| while (true) { | ||
| const { done, value } = await iterator.next(); | ||
| let mappedValue = await maps.value(value, done); | ||
| if (mappedValue !== value) { | ||
| const meta = getEventMeta(value); | ||
| if (meta && isTypescriptObject(mappedValue)) { | ||
| mappedValue = withEventMeta(mappedValue, meta); | ||
| } | ||
| } | ||
| if (done) { | ||
| return mappedValue; | ||
| } | ||
| yield mappedValue; | ||
| } | ||
| } catch (error) { | ||
| let mappedError = await maps.error(error); | ||
| if (mappedError !== error) { | ||
| const meta = getEventMeta(error); | ||
| if (meta && isTypescriptObject(mappedError)) { | ||
| mappedError = withEventMeta(mappedError, meta); | ||
| } | ||
| } | ||
| throw mappedError; | ||
| } finally { | ||
| await iterator.return?.(); | ||
| } | ||
| }(); | ||
| } | ||
| const MAX_ALLOWED_RETRY_TIMES = 99; | ||
| function createAutoRetryEventIterator(initial, reconnect, initialLastEventId) { | ||
| const state = { | ||
| status: "connected", | ||
| listeners: [] | ||
| }; | ||
| const iterator = async function* () { | ||
| let current = initial; | ||
| let lastEventId = initialLastEventId; | ||
| let lastRetry; | ||
| let retryTimes = 0; | ||
| try { | ||
| while (true) { | ||
| try { | ||
| updateEventIteratorStatus(state, "connected"); | ||
| const { done, value } = await current.next(); | ||
| const meta = getEventMeta(value); | ||
| lastEventId = meta?.id ?? lastEventId; | ||
| lastRetry = meta?.retry ?? lastRetry; | ||
| retryTimes = 0; | ||
| if (done) { | ||
| return value; | ||
| } | ||
| yield value; | ||
| } catch (e) { | ||
| updateEventIteratorStatus(state, "reconnecting"); | ||
| const meta = getEventMeta(e); | ||
| lastEventId = meta?.id ?? lastEventId; | ||
| lastRetry = meta?.retry ?? lastRetry; | ||
| let currentError = e; | ||
| current = await retry({ times: MAX_ALLOWED_RETRY_TIMES }, async (exit) => { | ||
| retryTimes += 1; | ||
| if (retryTimes > MAX_ALLOWED_RETRY_TIMES) { | ||
| throw exit(new Error( | ||
| `Exceeded maximum retry attempts (${MAX_ALLOWED_RETRY_TIMES}) for event iterator. Possible infinite retry loop detected. Please review the retry logic.`, | ||
| { cause: currentError } | ||
| )); | ||
| } | ||
| const reconnected = await (async () => { | ||
| try { | ||
| return await reconnect({ | ||
| lastRetry, | ||
| lastEventId, | ||
| retryTimes, | ||
| error: currentError | ||
| }); | ||
| } catch (e2) { | ||
| currentError = e2; | ||
| throw e2; | ||
| } | ||
| })(); | ||
| if (!reconnected) { | ||
| throw exit(currentError); | ||
| } | ||
| return reconnected; | ||
| }); | ||
| } | ||
| } | ||
| } finally { | ||
| updateEventIteratorStatus(state, "closed"); | ||
| await current.return?.(); | ||
| } | ||
| }(); | ||
| registerEventIteratorState(iterator, state); | ||
| return iterator; | ||
| } | ||
| export { COMMON_ORPC_ERROR_DEFS as C, ORPCError as O, fallbackORPCErrorMessage as a, createAutoRetryEventIterator as c, fallbackORPCErrorStatus as f, isDefinedError as i, mapEventIterator as m, onEventIteratorStatusChange as o, registerEventIteratorState as r, toORPCError as t, updateEventIteratorStatus as u }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
153508
144.37%30
87.5%2199
112.46%206
126.37%4
33.33%17
30.77%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed