@orpc/client
Advanced tools
| import { sortPlugins, runWithSpan, ORPC_NAME, isAsyncIteratorObject, override, traceAsyncIterator, intercept, getOpenTelemetryConfig, value, pathToHttpPath, stringifyJSON } from '@orpc/shared'; | ||
| import { mergeStandardHeaders, parseStandardUrl } from '@standardserver/core'; | ||
| import { toStandardHeaders } from '@standardserver/fetch'; | ||
| import { O as ORPCError } from './client.Dnfj8jnT.mjs'; | ||
| import { R as RPCSerializer, i as isORPCErrorJson, c as createORPCErrorFromJson } from './client.DQpq_Sdn.mjs'; | ||
| class CompositeStandardLinkPlugin { | ||
| name = "~composite"; | ||
| plugins; | ||
| constructor(plugins = []) { | ||
| this.plugins = sortPlugins(plugins); | ||
| } | ||
| init(options) { | ||
| for (const plugin of this.plugins) { | ||
| if (plugin.init) { | ||
| options = plugin.init(options); | ||
| } | ||
| } | ||
| return options; | ||
| } | ||
| } | ||
| class StandardLink { | ||
| constructor(codec, transport, options = {}) { | ||
| this.codec = codec; | ||
| this.transport = transport; | ||
| options = new CompositeStandardLinkPlugin(options.plugins).init(options); | ||
| this.interceptors = options.interceptors; | ||
| this.transportInterceptors = options.transportInterceptors; | ||
| } | ||
| interceptors; | ||
| transportInterceptors; | ||
| /** | ||
| * @throws ORPCError, transport-level errors (network failures, timeouts, etc.) | ||
| */ | ||
| call(path, input, options) { | ||
| return runWithSpan(`${ORPC_NAME}.${path.join("/")}`, (span) => { | ||
| span?.setAttribute("rpc.system", ORPC_NAME); | ||
| span?.setAttribute("rpc.method", path.join(".")); | ||
| if (isAsyncIteratorObject(input)) { | ||
| input = override(input, traceAsyncIterator("consume_async_iterator_object_input", input)); | ||
| } | ||
| return intercept(this.interceptors, { ...options, path, input }, async ({ path: path2, input: input2, ...options2 }) => { | ||
| const otel = getOpenTelemetryConfig(); | ||
| let activeContext; | ||
| const activeSpan = otel?.trace.getActiveSpan() ?? span; | ||
| if (activeSpan && otel) { | ||
| activeContext = otel.trace.setSpan(otel.context.active(), activeSpan); | ||
| } | ||
| let request = await runWithSpan( | ||
| { name: "encode_input", context: activeContext }, | ||
| () => this.codec.encodeInput(input2, path2, options2) | ||
| ); | ||
| if (activeContext && otel?.propagation) { | ||
| const headers = { ...request.headers }; | ||
| otel.propagation.inject(activeContext, headers); | ||
| request = { ...request, headers }; | ||
| } | ||
| const response = await intercept( | ||
| this.transportInterceptors, | ||
| { ...options2, path: path2, request }, | ||
| ({ path: path3, request: request2, ...options3 }) => { | ||
| let activeTransportContext; | ||
| const activeTransportSpan = otel?.trace.getActiveSpan() ?? activeSpan; | ||
| if (activeTransportSpan && otel) { | ||
| activeTransportContext = otel.trace.setSpan(otel.context.active(), activeTransportSpan); | ||
| } | ||
| return runWithSpan( | ||
| { name: "send_request", context: activeTransportContext }, | ||
| () => this.transport.send(request2, path3, options3) | ||
| ); | ||
| } | ||
| ); | ||
| const decodedResult = await runWithSpan( | ||
| { name: "decode_response", context: activeContext }, | ||
| () => this.codec.decodeResponse(response, path2, options2) | ||
| ); | ||
| if (decodedResult.kind === "error") { | ||
| throw decodedResult.error; | ||
| } | ||
| const output = decodedResult.output; | ||
| if (isAsyncIteratorObject(output)) { | ||
| return override(output, traceAsyncIterator("consume_async_iterator_object_output", output)); | ||
| } | ||
| return output; | ||
| }); | ||
| }); | ||
| } | ||
| } | ||
| const END_SLASH_REGEX = /\/$/; | ||
| class RPCLinkCodec { | ||
| baseUrl; | ||
| maxUrlLength; | ||
| fallbackMethod; | ||
| expectedMethod; | ||
| headers; | ||
| serializer; | ||
| constructor(options) { | ||
| this.baseUrl = options.url ?? "/"; | ||
| this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
| this.fallbackMethod = options.fallbackMethod ?? "POST"; | ||
| this.expectedMethod = options.method ?? this.fallbackMethod; | ||
| this.headers = options.headers ?? {}; | ||
| this.serializer = options.serializer ?? new RPCSerializer(); | ||
| } | ||
| async encodeInput(input, path, options) { | ||
| let headers = toResolvedStandardHeaders(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 [pathname, search, hash] = parseStandardUrl(baseUrl); | ||
| const newPathname = `${pathname.replace(END_SLASH_REGEX, "")}${pathToHttpPath(path)}`; | ||
| const serialized = this.serializer.serialize(input); | ||
| if (expectedMethod === "GET" && !(serialized instanceof Blob) && !(serialized instanceof ReadableStream) && !(serialized instanceof FormData) && !isAsyncIteratorObject(serialized)) { | ||
| const maxUrlLength = await value(this.maxUrlLength, options, path, input); | ||
| const mergedSearch = new URLSearchParams(search); | ||
| mergedSearch.append("data", stringifyJSON(serialized) ?? ""); | ||
| const url2 = `${newPathname}?${mergedSearch}${hash ?? ""}`; | ||
| if (url2.length <= maxUrlLength) { | ||
| return { | ||
| body: void 0, | ||
| method: expectedMethod, | ||
| headers, | ||
| url: url2, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| } | ||
| const url = `${newPathname}${search ?? ""}${hash ?? ""}`; | ||
| return { | ||
| url, | ||
| method: expectedMethod === "GET" ? this.fallbackMethod : expectedMethod, | ||
| headers, | ||
| body: serialized, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| async decodeResponse(response) { | ||
| const isOk = response.status < 400; | ||
| const body = await response.resolveBody(); | ||
| const deserialized = await (async () => { | ||
| try { | ||
| return this.serializer.deserialize(body); | ||
| } catch (cause) { | ||
| throw new Error("Invalid RPC response format.", { | ||
| cause | ||
| }); | ||
| } | ||
| })(); | ||
| if (!isOk) { | ||
| if (isORPCErrorJson(deserialized)) { | ||
| return { kind: "error", error: createORPCErrorFromJson(deserialized) }; | ||
| } | ||
| return { | ||
| kind: "error", | ||
| error: new ORPCError("MALFORMED_ORPC_ERROR_RESPONSE", { | ||
| data: { headers: response.headers, status: response.status, body: deserialized } | ||
| }) | ||
| }; | ||
| } | ||
| return { kind: "output", output: deserialized }; | ||
| } | ||
| } | ||
| function toResolvedStandardHeaders(headers) { | ||
| if (typeof headers.forEach === "function") { | ||
| return toStandardHeaders(headers); | ||
| } | ||
| return headers; | ||
| } | ||
| export { CompositeStandardLinkPlugin as C, RPCLinkCodec as R, StandardLink as S }; |
| import { StandardBody } from '@standardserver/core'; | ||
| import { Segment } from '@orpc/shared'; | ||
| type RPCJsonSerializationMeta = [type: string, ...path: Segment[]]; | ||
| type RPCJsonSerialization = { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps?: undefined; | ||
| blobs?: undefined; | ||
| } | { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps: Segment[][]; | ||
| blobs: Blob[]; | ||
| }; | ||
| interface RPCJsonSerializerHandler { | ||
| condition(value: unknown): boolean; | ||
| serialize(value: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| /** | ||
| * If false, the result of this serializer will not be further processed by other serializers, | ||
| * even if it matches their conditions and treat it as final serialized value. | ||
| * This can be useful for serializers that return primitive values, which should not be further processed. | ||
| * to improve performance and avoid potential issues with other serializers. | ||
| * | ||
| * @default false | ||
| */ | ||
| isTerminal?: boolean; | ||
| } | ||
| interface RPCJsonSerializerOptions { | ||
| /** | ||
| * Extend or override the built-in type handlers used during serialization and deserialization. | ||
| * | ||
| * Each key is a unique type identifier (e.g. `"date"`, `"bigint"`) and maps to a handler | ||
| * that defines how to detect, serialize, and deserialize values of that type. | ||
| * | ||
| * **Extending:** Add new keys to support custom types: | ||
| * ```ts | ||
| * handlers: { | ||
| * buffer: { | ||
| * condition: (v) => v instanceof Buffer, | ||
| * serialize: (v: Buffer) => v.toString('base64'), | ||
| * deserialize: (s: string) => Buffer.from(s, 'base64'), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Overriding:** Use an existing key to replace a built-in handler: | ||
| * ```ts | ||
| * handlers: { | ||
| * date: { | ||
| * condition: (v) => v instanceof Date, | ||
| * serialize: (v: Date) => v.getTime(), | ||
| * deserialize: (n: number) => new Date(n), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Disabling:** Set a key to `undefined` to remove a built-in handler: | ||
| * ```ts | ||
| * handlers: { regexp: undefined } | ||
| * ``` | ||
| * | ||
| * Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `url`, `regexp`, `set`, `map`. | ||
| */ | ||
| handlers?: Record<string, undefined | RPCJsonSerializerHandler> | undefined; | ||
| /** | ||
| * If true, properties with undefined values will be omitted during serialization. | ||
| * | ||
| * @default true | ||
| */ | ||
| omitUndefinedProperties?: boolean | undefined; | ||
| } | ||
| declare class RPCJsonSerializer { | ||
| private readonly handlers; | ||
| private readonly inlineBuiltInHandlers; | ||
| private readonly handlerEntries; | ||
| private readonly omitUndefinedProperties; | ||
| constructor(options?: RPCJsonSerializerOptions); | ||
| serialize(data: unknown): RPCJsonSerialization; | ||
| /** | ||
| * `segments` is a shared mutable stack (push/pop while walking), | ||
| * so it must be copied before being stored in `meta` or `maps`. | ||
| */ | ||
| private serializeValue; | ||
| deserialize(serialized: RPCJsonSerialization): unknown; | ||
| } | ||
| interface RPCSerializerSerializeOptions { | ||
| /** | ||
| * Use FormData for serialization when nested blobs are present. | ||
| * Does not apply to root-level Blob values. | ||
| * | ||
| * @default true | ||
| */ | ||
| useFormDataForBlobFields?: boolean; | ||
| } | ||
| interface RPCSerializerOptions extends RPCJsonSerializerOptions { | ||
| /** | ||
| * Default options for serialize method | ||
| */ | ||
| serialize?: RPCSerializerSerializeOptions | undefined; | ||
| } | ||
| declare class RPCSerializer { | ||
| private readonly jsonSerializer; | ||
| private readonly defaultSerializeOptions; | ||
| constructor(options?: RPCSerializerOptions); | ||
| serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody; | ||
| private serializeValue; | ||
| deserialize(data: StandardBody): unknown; | ||
| private deserializeValue; | ||
| } | ||
| export { RPCJsonSerializer as b, RPCSerializer as e }; | ||
| export type { RPCJsonSerialization as R, RPCJsonSerializationMeta as a, RPCJsonSerializerHandler as c, RPCJsonSerializerOptions as d, RPCSerializerOptions as f, RPCSerializerSerializeOptions as g }; |
| import { StandardBody } from '@standardserver/core'; | ||
| import { Segment } from '@orpc/shared'; | ||
| type RPCJsonSerializationMeta = [type: string, ...path: Segment[]]; | ||
| type RPCJsonSerialization = { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps?: undefined; | ||
| blobs?: undefined; | ||
| } | { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps: Segment[][]; | ||
| blobs: Blob[]; | ||
| }; | ||
| interface RPCJsonSerializerHandler { | ||
| condition(value: unknown): boolean; | ||
| serialize(value: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| /** | ||
| * If false, the result of this serializer will not be further processed by other serializers, | ||
| * even if it matches their conditions and treat it as final serialized value. | ||
| * This can be useful for serializers that return primitive values, which should not be further processed. | ||
| * to improve performance and avoid potential issues with other serializers. | ||
| * | ||
| * @default false | ||
| */ | ||
| isTerminal?: boolean; | ||
| } | ||
| interface RPCJsonSerializerOptions { | ||
| /** | ||
| * Extend or override the built-in type handlers used during serialization and deserialization. | ||
| * | ||
| * Each key is a unique type identifier (e.g. `"date"`, `"bigint"`) and maps to a handler | ||
| * that defines how to detect, serialize, and deserialize values of that type. | ||
| * | ||
| * **Extending:** Add new keys to support custom types: | ||
| * ```ts | ||
| * handlers: { | ||
| * buffer: { | ||
| * condition: (v) => v instanceof Buffer, | ||
| * serialize: (v: Buffer) => v.toString('base64'), | ||
| * deserialize: (s: string) => Buffer.from(s, 'base64'), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Overriding:** Use an existing key to replace a built-in handler: | ||
| * ```ts | ||
| * handlers: { | ||
| * date: { | ||
| * condition: (v) => v instanceof Date, | ||
| * serialize: (v: Date) => v.getTime(), | ||
| * deserialize: (n: number) => new Date(n), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Disabling:** Set a key to `undefined` to remove a built-in handler: | ||
| * ```ts | ||
| * handlers: { regexp: undefined } | ||
| * ``` | ||
| * | ||
| * Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `url`, `regexp`, `set`, `map`. | ||
| */ | ||
| handlers?: Record<string, undefined | RPCJsonSerializerHandler> | undefined; | ||
| /** | ||
| * If true, properties with undefined values will be omitted during serialization. | ||
| * | ||
| * @default true | ||
| */ | ||
| omitUndefinedProperties?: boolean | undefined; | ||
| } | ||
| declare class RPCJsonSerializer { | ||
| private readonly handlers; | ||
| private readonly inlineBuiltInHandlers; | ||
| private readonly handlerEntries; | ||
| private readonly omitUndefinedProperties; | ||
| constructor(options?: RPCJsonSerializerOptions); | ||
| serialize(data: unknown): RPCJsonSerialization; | ||
| /** | ||
| * `segments` is a shared mutable stack (push/pop while walking), | ||
| * so it must be copied before being stored in `meta` or `maps`. | ||
| */ | ||
| private serializeValue; | ||
| deserialize(serialized: RPCJsonSerialization): unknown; | ||
| } | ||
| interface RPCSerializerSerializeOptions { | ||
| /** | ||
| * Use FormData for serialization when nested blobs are present. | ||
| * Does not apply to root-level Blob values. | ||
| * | ||
| * @default true | ||
| */ | ||
| useFormDataForBlobFields?: boolean; | ||
| } | ||
| interface RPCSerializerOptions extends RPCJsonSerializerOptions { | ||
| /** | ||
| * Default options for serialize method | ||
| */ | ||
| serialize?: RPCSerializerSerializeOptions | undefined; | ||
| } | ||
| declare class RPCSerializer { | ||
| private readonly jsonSerializer; | ||
| private readonly defaultSerializeOptions; | ||
| constructor(options?: RPCSerializerOptions); | ||
| serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody; | ||
| private serializeValue; | ||
| deserialize(data: StandardBody): unknown; | ||
| private deserializeValue; | ||
| } | ||
| export { RPCJsonSerializer as b, RPCSerializer as e }; | ||
| export type { RPCJsonSerialization as R, RPCJsonSerializationMeta as a, RPCJsonSerializerHandler as c, RPCJsonSerializerOptions as d, RPCSerializerOptions as f, RPCSerializerSerializeOptions as g }; |
| import { isPlainObject, wrapAsyncIterator, isTypescriptObject, NullProtoObj, isAsyncIteratorObject, stringifyJSON } from '@orpc/shared'; | ||
| import { getEventMeta, withEventMeta, ErrorEvent } from '@standardserver/core'; | ||
| import { O as ORPCError } from './client.Dnfj8jnT.mjs'; | ||
| function isInferableError(error) { | ||
| return error instanceof ORPCError && error.inferable; | ||
| } | ||
| function toORPCError(error) { | ||
| return error instanceof ORPCError ? error : new ORPCError("INTERNAL_SERVER_ERROR", { cause: error }); | ||
| } | ||
| function isORPCErrorJson(json) { | ||
| if (!isPlainObject(json)) { | ||
| return false; | ||
| } | ||
| const validKeys = ["defined", "inferable", "code", "message", "data"]; | ||
| if (Object.keys(json).some((k) => !validKeys.includes(k))) { | ||
| return false; | ||
| } | ||
| return "defined" in json && typeof json.defined === "boolean" && "inferable" in json && typeof json.inferable === "boolean" && "code" in json && typeof json.code === "string" && "message" in json && typeof json.message === "string"; | ||
| } | ||
| function createORPCErrorFromJson(json, options = {}) { | ||
| const error = new ORPCError(json.code, { | ||
| ...json, | ||
| ...options | ||
| }); | ||
| error.defined = json.defined; | ||
| error.inferable = json.inferable; | ||
| return error; | ||
| } | ||
| function cloneORPCError(error) { | ||
| const cloned = new ORPCError(error.code, { | ||
| ...error, | ||
| message: error.message, | ||
| data: error.data, | ||
| cause: error.cause | ||
| }); | ||
| cloned.stack = error.stack; | ||
| cloned.defined = error.defined; | ||
| cloned.inferable = error.inferable; | ||
| return cloned; | ||
| } | ||
| function wrapAsyncIteratorPreservingEventMeta(iterator, { mapResult, mapError, ...rest }) { | ||
| return wrapAsyncIterator(iterator, { | ||
| ...rest, | ||
| mapResult: mapResult && (async (result) => { | ||
| const mapped = await mapResult(result); | ||
| if (mapped.value !== result.value) { | ||
| const meta = getEventMeta(result.value); | ||
| if (meta && isTypescriptObject(mapped.value)) { | ||
| return { done: mapped.done, value: withEventMeta(mapped.value, meta) }; | ||
| } | ||
| } | ||
| return mapped; | ||
| }), | ||
| mapError: mapError && (async (error) => { | ||
| const mapped = await mapError(error); | ||
| if (mapped !== error) { | ||
| const meta = getEventMeta(error); | ||
| if (meta && isTypescriptObject(mapped)) { | ||
| return withEventMeta(mapped, meta); | ||
| } | ||
| } | ||
| return mapped; | ||
| }) | ||
| }); | ||
| } | ||
| const REGEX_STRING_PATTERN = /^\/([\s\S]*)\/([a-z]*)$/; | ||
| const DEFAULT_RPC_JSON_SERIALIZER_HANDLERS = { | ||
| undefined: { | ||
| condition(data) { | ||
| return data === void 0; | ||
| }, | ||
| serialize() { | ||
| return null; | ||
| }, | ||
| deserialize() { | ||
| return void 0; | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| bigint: { | ||
| condition(data) { | ||
| return typeof data === "bigint"; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return BigInt(serialized); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| date: { | ||
| condition(data) { | ||
| return data instanceof Date; | ||
| }, | ||
| serialize(data) { | ||
| if (Number.isNaN(data.getTime())) { | ||
| return null; | ||
| } | ||
| return data.toISOString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Date(serialized ?? "Invalid Date"); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| nan: { | ||
| condition(data) { | ||
| return typeof data === "number" && Number.isNaN(data); | ||
| }, | ||
| serialize() { | ||
| return null; | ||
| }, | ||
| deserialize() { | ||
| return Number.NaN; | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| url: { | ||
| condition(data) { | ||
| return data instanceof URL; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new URL(serialized); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| regexp: { | ||
| condition(data) { | ||
| return data instanceof RegExp; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| const [, pattern, flags] = serialized.match(REGEX_STRING_PATTERN); | ||
| return new RegExp(pattern, flags); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| set: { | ||
| condition(data) { | ||
| return data instanceof Set; | ||
| }, | ||
| serialize(data) { | ||
| return Array.from(data); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Set(serialized); | ||
| } | ||
| }, | ||
| map: { | ||
| condition(data) { | ||
| return data instanceof Map; | ||
| }, | ||
| serialize(data) { | ||
| return Array.from(data.entries()); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Map(serialized); | ||
| } | ||
| } | ||
| }; | ||
| class RPCJsonSerializer { | ||
| handlers; | ||
| inlineBuiltInHandlers; | ||
| handlerEntries; | ||
| omitUndefinedProperties; | ||
| constructor(options = {}) { | ||
| this.omitUndefinedProperties = options.omitUndefinedProperties !== false; | ||
| this.handlers = Object.assign(new NullProtoObj(), DEFAULT_RPC_JSON_SERIALIZER_HANDLERS); | ||
| const customHandlers = options.handlers; | ||
| if (customHandlers === void 0) { | ||
| this.inlineBuiltInHandlers = true; | ||
| return; | ||
| } | ||
| let inlineBuiltInHandlers = true; | ||
| let handlerEntries = []; | ||
| for (const key in customHandlers) { | ||
| const handler = customHandlers[key]; | ||
| this.handlers[key] = handler; | ||
| if (inlineBuiltInHandlers && key in DEFAULT_RPC_JSON_SERIALIZER_HANDLERS) { | ||
| inlineBuiltInHandlers = false; | ||
| } | ||
| if (inlineBuiltInHandlers && handler !== void 0) { | ||
| handlerEntries.push([key, handler]); | ||
| } | ||
| } | ||
| if (!inlineBuiltInHandlers) { | ||
| handlerEntries = []; | ||
| for (const key in this.handlers) { | ||
| const handler = this.handlers[key]; | ||
| if (handler !== void 0) { | ||
| handlerEntries.push([key, handler]); | ||
| } | ||
| } | ||
| } | ||
| this.inlineBuiltInHandlers = inlineBuiltInHandlers; | ||
| this.handlerEntries = handlerEntries; | ||
| } | ||
| serialize(data) { | ||
| let meta = []; | ||
| const maps = []; | ||
| const blobs = []; | ||
| const json = this.serializeValue(data, [], meta, maps, blobs); | ||
| meta = meta.length === 0 ? void 0 : meta; | ||
| if (maps.length === 0) { | ||
| return { json, meta }; | ||
| } | ||
| return { json, meta, maps, blobs }; | ||
| } | ||
| /** | ||
| * `segments` is a shared mutable stack (push/pop while walking), | ||
| * so it must be copied before being stored in `meta` or `maps`. | ||
| */ | ||
| serializeValue(data, segments, meta, maps, blobs) { | ||
| if (this.inlineBuiltInHandlers) { | ||
| switch (typeof data) { | ||
| case "string": | ||
| case "boolean": | ||
| return data; | ||
| case "number": | ||
| if (Number.isNaN(data)) { | ||
| meta.push(["nan", ...segments]); | ||
| return null; | ||
| } | ||
| return data; | ||
| case "undefined": | ||
| meta.push(["undefined", ...segments]); | ||
| return null; | ||
| case "bigint": | ||
| meta.push(["bigint", ...segments]); | ||
| return data.toString(); | ||
| case "object": { | ||
| if (data === null) { | ||
| return data; | ||
| } | ||
| if (data instanceof Date) { | ||
| meta.push(["date", ...segments]); | ||
| return Number.isNaN(data.getTime()) ? null : data.toISOString(); | ||
| } | ||
| if (data instanceof URL) { | ||
| meta.push(["url", ...segments]); | ||
| return data.toString(); | ||
| } | ||
| if (data instanceof RegExp) { | ||
| meta.push(["regexp", ...segments]); | ||
| return data.toString(); | ||
| } | ||
| if (data instanceof Set) { | ||
| const result = this.serializeValue(Array.from(data), segments, meta, maps, blobs); | ||
| meta.push(["set", ...segments]); | ||
| return result; | ||
| } | ||
| if (data instanceof Map) { | ||
| const result = this.serializeValue(Array.from(data.entries()), segments, meta, maps, blobs); | ||
| meta.push(["map", ...segments]); | ||
| return result; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| const handlerEntries = this.handlerEntries; | ||
| if (handlerEntries) { | ||
| for (let i = 0; i < handlerEntries.length; i++) { | ||
| const entry = handlerEntries[i]; | ||
| const handler = entry[1]; | ||
| if (handler.condition(data)) { | ||
| const serialized = handler.serialize(data); | ||
| if (handler.isTerminal) { | ||
| meta.push([entry[0], ...segments]); | ||
| if (serialized instanceof Blob) { | ||
| maps.push(segments.slice()); | ||
| blobs.push(serialized); | ||
| } | ||
| return serialized; | ||
| } | ||
| const result = this.serializeValue(serialized, segments, meta, maps, blobs); | ||
| meta.push([entry[0], ...segments]); | ||
| return result; | ||
| } | ||
| } | ||
| } | ||
| if (data instanceof Blob) { | ||
| maps.push(segments.slice()); | ||
| blobs.push(data); | ||
| return data; | ||
| } | ||
| if (Array.isArray(data)) { | ||
| const json = []; | ||
| for (let i = 0; i < data.length; i++) { | ||
| segments.push(i); | ||
| json.push(this.serializeValue(data[i], segments, meta, maps, blobs)); | ||
| segments.pop(); | ||
| } | ||
| return json; | ||
| } | ||
| if (isPlainObject(data)) { | ||
| const json = new NullProtoObj(); | ||
| for (const k in data) { | ||
| const v = data[k]; | ||
| if (k === "toJSON" && typeof v === "function") { | ||
| continue; | ||
| } | ||
| if (v === void 0 && this.omitUndefinedProperties) { | ||
| continue; | ||
| } | ||
| segments.push(k); | ||
| json[k] = this.serializeValue(v, segments, meta, maps, blobs); | ||
| segments.pop(); | ||
| } | ||
| return json; | ||
| } | ||
| return data; | ||
| } | ||
| deserialize(serialized) { | ||
| const ref = { data: serialized.json }; | ||
| if (serialized.blobs?.length) { | ||
| for (let i = 0; i < serialized.maps.length; i++) { | ||
| const segments = serialized.maps[i]; | ||
| let currentRef = ref; | ||
| let preSegment = "data"; | ||
| for (let j = 0; j < segments.length; j++) { | ||
| currentRef = currentRef[preSegment]; | ||
| preSegment = segments[j]; | ||
| if (!Object.hasOwn(currentRef, preSegment)) { | ||
| throw new Error(`Security error: Invalid serialized data. Segment "${preSegment}" does not exist.`); | ||
| } | ||
| } | ||
| currentRef[preSegment] = serialized.blobs[i]; | ||
| } | ||
| } | ||
| if (serialized.meta) { | ||
| for (const item of serialized.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: Invalid serialized data. Segment "${preSegment}" does not exist.`); | ||
| } | ||
| } | ||
| currentRef[preSegment] = this.handlers[type].deserialize(currentRef[preSegment]); | ||
| } | ||
| } | ||
| return ref.data; | ||
| } | ||
| } | ||
| class RPCSerializer { | ||
| jsonSerializer; | ||
| defaultSerializeOptions; | ||
| constructor(options = {}) { | ||
| this.jsonSerializer = new RPCJsonSerializer(options); | ||
| this.defaultSerializeOptions = options.serialize; | ||
| } | ||
| serialize(data, options = {}) { | ||
| if (data === void 0 || data instanceof ReadableStream || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| if (isAsyncIteratorObject(data)) { | ||
| return wrapAsyncIteratorPreservingEventMeta(data, { | ||
| mapResult: (result) => { | ||
| if (result.value === void 0) { | ||
| return result; | ||
| } | ||
| return { done: result.done, value: this.serializeValue(result.value, false) }; | ||
| }, | ||
| mapError: (e) => new ErrorEvent( | ||
| this.serializeValue(toORPCError(e).toJSON(), false), | ||
| { cause: e } | ||
| ) | ||
| }); | ||
| } | ||
| const useFormDataForBlobs = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true; | ||
| return this.serializeValue(data, useFormDataForBlobs); | ||
| } | ||
| serializeValue(data, useFormDataForBlobs) { | ||
| const { json, meta, maps, blobs } = this.jsonSerializer.serialize(data); | ||
| if (!useFormDataForBlobs || !blobs?.length) { | ||
| 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 (data === void 0 || data instanceof ReadableStream || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| if (isAsyncIteratorObject(data)) { | ||
| return wrapAsyncIteratorPreservingEventMeta(data, { | ||
| mapResult: (result) => { | ||
| if (result.value === void 0) { | ||
| return result; | ||
| } | ||
| return { done: result.done, value: this.deserializeValue(result.value) }; | ||
| }, | ||
| mapError: (e) => { | ||
| if (!(e instanceof ErrorEvent)) { | ||
| return e; | ||
| } | ||
| const deserialized = this.deserializeValue(e.data); | ||
| if (isORPCErrorJson(deserialized)) { | ||
| return createORPCErrorFromJson(deserialized, { cause: e }); | ||
| } | ||
| return new ErrorEvent(deserialized, { cause: e }); | ||
| } | ||
| }); | ||
| } | ||
| return this.deserializeValue(data); | ||
| } | ||
| deserializeValue(data) { | ||
| if (!(data instanceof FormData)) { | ||
| return this.jsonSerializer.deserialize(data); | ||
| } | ||
| const serialized = JSON.parse(data.get("data")); | ||
| const blobs = []; | ||
| for (const [key, value] of data) { | ||
| if (value instanceof Blob) { | ||
| blobs[Number(key)] = value; | ||
| } | ||
| } | ||
| return this.jsonSerializer.deserialize({ ...serialized, blobs }); | ||
| } | ||
| } | ||
| export { RPCSerializer as R, isInferableError as a, RPCJsonSerializer as b, createORPCErrorFromJson as c, cloneORPCError as d, isORPCErrorJson as i, toORPCError as t, wrapAsyncIteratorPreservingEventMeta as w }; |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.mjs'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.mjs'; | ||
| import '../../shared/client.BdItY5DT.mjs'; | ||
| import '../../shared/client.Dlxz9Qw5.mjs'; | ||
@@ -10,0 +10,0 @@ interface FetchLinkTransportFetchInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.js'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.js'; | ||
| import '../../shared/client.BdItY5DT.js'; | ||
| import '../../shared/client.Dlxz9Qw5.js'; | ||
@@ -10,0 +10,0 @@ interface FetchLinkTransportFetchInterceptorOptions<T extends ClientContext> extends ClientOptions<T> { |
| import { sortPlugins, value, intercept, once } from '@orpc/shared'; | ||
| import { toFetchBody, toFetchHeaders, toStandardLazyResponse } from '@standardserver/fetch'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.BRJnOJ0R.mjs'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.Bl4junIW.mjs'; | ||
| import '@standardserver/core'; | ||
| import '../../shared/client.Dnfj8jnT.mjs'; | ||
| import '../../shared/client.DqYwRDUO.mjs'; | ||
| import '../../shared/client.DQpq_Sdn.mjs'; | ||
@@ -8,0 +8,0 @@ class CompositeFetchLinkTransportPlugin { |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.mjs'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.mjs'; | ||
| import '../../shared/client.BdItY5DT.mjs'; | ||
| import '../../shared/client.Dlxz9Qw5.mjs'; | ||
@@ -10,0 +10,0 @@ /** |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.js'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.js'; | ||
| import '../../shared/client.BdItY5DT.js'; | ||
| import '../../shared/client.Dlxz9Qw5.js'; | ||
@@ -10,0 +10,0 @@ /** |
| import { value } from '@orpc/shared'; | ||
| import { ClientPeer, encodePeerMessage, decodePeerMessage, isServerPeerSendMessage, isPeerMessage } from '@standardserver/peer'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.BRJnOJ0R.mjs'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.Bl4junIW.mjs'; | ||
| import '@standardserver/core'; | ||
| import '@standardserver/fetch'; | ||
| import '../../shared/client.Dnfj8jnT.mjs'; | ||
| import '../../shared/client.DqYwRDUO.mjs'; | ||
| import '../../shared/client.DQpq_Sdn.mjs'; | ||
@@ -9,0 +9,0 @@ function postMessagePortMessage(port, data, transfer) { |
@@ -7,3 +7,3 @@ import { f as StandardLinkCodec, g as StandardLinkCodecDecodedResponse } from '../../shared/client.8f4DNmdE.mjs'; | ||
| import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.mjs'; | ||
| import { e as RPCSerializer } from '../../shared/client.BdItY5DT.mjs'; | ||
| import { e as RPCSerializer } from '../../shared/client.Dlxz9Qw5.mjs'; | ||
@@ -10,0 +10,0 @@ interface RPCLinkCodecOptions<T extends ClientContext> { |
@@ -7,3 +7,3 @@ import { f as StandardLinkCodec, g as StandardLinkCodecDecodedResponse } from '../../shared/client.BMKYqpdy.js'; | ||
| import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.js'; | ||
| import { e as RPCSerializer } from '../../shared/client.BdItY5DT.js'; | ||
| import { e as RPCSerializer } from '../../shared/client.Dlxz9Qw5.js'; | ||
@@ -10,0 +10,0 @@ interface RPCLinkCodecOptions<T extends ClientContext> { |
@@ -1,2 +0,2 @@ | ||
| export { C as CompositeStandardLinkPlugin, R as RPCLinkCodec, S as StandardLink } from '../../shared/client.BRJnOJ0R.mjs'; | ||
| export { C as CompositeStandardLinkPlugin, R as RPCLinkCodec, S as StandardLink } from '../../shared/client.Bl4junIW.mjs'; | ||
| import '@orpc/shared'; | ||
@@ -6,2 +6,2 @@ import '@standardserver/core'; | ||
| import '../../shared/client.Dnfj8jnT.mjs'; | ||
| import '../../shared/client.DqYwRDUO.mjs'; | ||
| import '../../shared/client.DQpq_Sdn.mjs'; |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.mjs'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.mjs'; | ||
| import '../../shared/client.BdItY5DT.mjs'; | ||
| import '../../shared/client.Dlxz9Qw5.mjs'; | ||
@@ -10,0 +10,0 @@ type WebSocketLike = Pick<WebSocket, 'addEventListener' | 'removeEventListener' | 'send' | 'readyState'>; |
@@ -7,3 +7,3 @@ import { C as ClientContext, a as ClientOptions } from '../../shared/client.BBZBQID8.js'; | ||
| import { RPCLinkCodecOptions } from '../standard/index.js'; | ||
| import '../../shared/client.BdItY5DT.js'; | ||
| import '../../shared/client.Dlxz9Qw5.js'; | ||
@@ -10,0 +10,0 @@ type WebSocketLike = Pick<WebSocket, 'addEventListener' | 'removeEventListener' | 'send' | 'readyState'>; |
| import { runWithSignal, AbortError, sleep, promiseWithResolvers, sequential, loadBytes, toStringOrBytes } from '@orpc/shared'; | ||
| import { ClientPeer, encodePeerMessage, decodePeerMessage, isServerPeerSendMessage } from '@standardserver/peer'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.BRJnOJ0R.mjs'; | ||
| import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.Bl4junIW.mjs'; | ||
| import '@standardserver/core'; | ||
| import '@standardserver/fetch'; | ||
| import '../../shared/client.Dnfj8jnT.mjs'; | ||
| import '../../shared/client.DqYwRDUO.mjs'; | ||
| import '../../shared/client.DQpq_Sdn.mjs'; | ||
@@ -9,0 +9,0 @@ const WEBSOCKET_CONNECTING = 0; |
+1
-1
@@ -5,3 +5,3 @@ import { WrapAsyncIteratorOptions, AsyncIteratorClass, Interceptor, PromiseWithError, ThrowableError, Promisable } from '@orpc/shared'; | ||
| export { i as AnyORPCErrorJSON, j as COMMON_ERROR_STATUS_MAP, k as InferClientBodyInputs, l as InferClientBodyOutputs, m as InferClientErrors, n as InferClientInputs, o as InferClientOutputs, N as NestedClient, p as ORPCErrorOptions } from './shared/client.BBZBQID8.mjs'; | ||
| export { R as RPCJsonSerialization, a as RPCJsonSerializationMeta, b as RPCJsonSerializer, c as RPCJsonSerializerHandler, d as RPCJsonSerializerOptions, e as RPCSerializer, f as RPCSerializerOptions, g as RPCSerializerSerializeOptions } from './shared/client.BdItY5DT.mjs'; | ||
| export { R as RPCJsonSerialization, a as RPCJsonSerializationMeta, b as RPCJsonSerializer, c as RPCJsonSerializerHandler, d as RPCJsonSerializerOptions, e as RPCSerializer, f as RPCSerializerOptions, g as RPCSerializerSerializeOptions } from './shared/client.Dlxz9Qw5.mjs'; | ||
| export { ErrorEvent, EventMeta, getEventMeta, unwrapEvent, withEventMeta } from '@standardserver/core'; | ||
@@ -8,0 +8,0 @@ |
+1
-1
@@ -5,3 +5,3 @@ import { WrapAsyncIteratorOptions, AsyncIteratorClass, Interceptor, PromiseWithError, ThrowableError, Promisable } from '@orpc/shared'; | ||
| export { i as AnyORPCErrorJSON, j as COMMON_ERROR_STATUS_MAP, k as InferClientBodyInputs, l as InferClientBodyOutputs, m as InferClientErrors, n as InferClientInputs, o as InferClientOutputs, N as NestedClient, p as ORPCErrorOptions } from './shared/client.BBZBQID8.js'; | ||
| export { R as RPCJsonSerialization, a as RPCJsonSerializationMeta, b as RPCJsonSerializer, c as RPCJsonSerializerHandler, d as RPCJsonSerializerOptions, e as RPCSerializer, f as RPCSerializerOptions, g as RPCSerializerSerializeOptions } from './shared/client.BdItY5DT.js'; | ||
| export { R as RPCJsonSerialization, a as RPCJsonSerializationMeta, b as RPCJsonSerializer, c as RPCJsonSerializerHandler, d as RPCJsonSerializerOptions, e as RPCSerializer, f as RPCSerializerOptions, g as RPCSerializerSerializeOptions } from './shared/client.Dlxz9Qw5.js'; | ||
| export { ErrorEvent, EventMeta, getEventMeta, unwrapEvent, withEventMeta } from '@standardserver/core'; | ||
@@ -8,0 +8,0 @@ |
+2
-2
@@ -1,3 +0,3 @@ | ||
| import { a as isInferableError } from './shared/client.DqYwRDUO.mjs'; | ||
| export { b as RPCJsonSerializer, R as RPCSerializer, d as cloneORPCError, c as createORPCErrorFromJson, i as isORPCErrorJson, t as toORPCError, w as wrapAsyncIteratorPreservingEventMeta } from './shared/client.DqYwRDUO.mjs'; | ||
| import { a as isInferableError } from './shared/client.DQpq_Sdn.mjs'; | ||
| export { b as RPCJsonSerializer, R as RPCSerializer, d as cloneORPCError, c as createORPCErrorFromJson, i as isORPCErrorJson, t as toORPCError, w as wrapAsyncIteratorPreservingEventMeta } from './shared/client.DQpq_Sdn.mjs'; | ||
| import { getOrBind, toArray, intercept, isTypescriptObject } from '@orpc/shared'; | ||
@@ -4,0 +4,0 @@ export { AsyncIteratorClass, asyncIteratorToStream, asyncIteratorToUnproxiedDataStream, consumeAsyncIterator, consumeAsyncIterator as consumeEventIterator, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onAsyncIteratorObjectError, onError, onFinish, onReadableStreamError, onStart, onSuccess, streamToAsyncIteratorObject, streamToAsyncIteratorObject as streamToEventIterator } from '@orpc/shared'; |
+5
-5
| { | ||
| "name": "@orpc/client", | ||
| "type": "module", | ||
| "version": "2.0.0-beta.21", | ||
| "version": "2.0.0-beta.22", | ||
| "license": "MIT", | ||
@@ -53,6 +53,6 @@ "homepage": "https://orpc.dev", | ||
| "dependencies": { | ||
| "@standardserver/core": "^0.5.0", | ||
| "@standardserver/fetch": "^0.5.0", | ||
| "@standardserver/peer": "^0.5.0", | ||
| "@orpc/shared": "2.0.0-beta.21" | ||
| "@standardserver/core": "^0.6.0", | ||
| "@standardserver/fetch": "^0.6.0", | ||
| "@standardserver/peer": "^0.6.0", | ||
| "@orpc/shared": "2.0.0-beta.22" | ||
| }, | ||
@@ -59,0 +59,0 @@ "devDependencies": { |
+5
-5
@@ -112,7 +112,6 @@ <h1 align="center">oRPC - Typesafe APIs Made Simple 🪄</h1> | ||
| <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> | ||
| <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/christ12938?ref=orpc" target="_blank" rel="noopener" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="139" alt="christ12938"/><br />christ12938</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> | ||
| <tr> | ||
| <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> | ||
| <td align="center"><a href="https://github.com/itigoore01?ref=orpc" target="_blank" rel="noopener" title="shota"><img src="https://avatars.githubusercontent.com/u/11831107?u=c976a6dc7e055eb026304c46c99100ed22b0c8e0&v=4" width="139" alt="shota"/><br />shota</a></td> | ||
@@ -127,3 +126,2 @@ </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/Nic13Gamer?ref=orpc" target="_blank" rel="noopener" title="Nicholas"><img src="https://avatars.githubusercontent.com/u/54724556?u=56a7ab430ce7a80d648ab6eba051d454a818ed0b&v=4" width="119" alt="Nicholas"/><br />Nicholas</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> | ||
@@ -134,5 +132,5 @@ <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/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/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> | ||
| <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> | ||
@@ -144,5 +142,5 @@ <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/ldub?ref=orpc" target="_blank" rel="noopener" title="Lev Dubinets"><img src="https://avatars.githubusercontent.com/u/3114081?u=f547f5d5012cab54851f1b1ad72d10e537f78fc2&v=4" width="119" alt="Lev Dubinets"/><br />Lev Dubinets</a></td> | ||
| <td align="center"><a href="https://github.com/mr-kelly?ref=orpc" target="_blank" rel="noopener" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td> | ||
| </tr> | ||
| <tr> | ||
| <td align="center"><a href="https://github.com/mr-kelly?ref=orpc" target="_blank" rel="noopener" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</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> | ||
@@ -180,2 +178,3 @@ <td align="center"><a href="https://github.com/finom?ref=orpc" target="_blank" rel="noopener" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=29e91400dbd4a9c217048a8f59562c4f740498e6&v=4" width="119" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td> | ||
| <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="32" height="32" alt="Andrew Peters" /></a> | ||
| <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="32" height="32" alt="Ryan Vogel" /></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> | ||
@@ -193,2 +192,3 @@ <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="32" height="32" alt="Peter Adam" /></a> | ||
| <a href="https://github.com/plancraft?ref=orpc" target="_blank" rel="noopener" title="plancraft"><img src="https://avatars.githubusercontent.com/u/46482287?v=4" width="32" height="32" alt="plancraft" /></a> | ||
| <a href="https://github.com/Nic13Gamer?ref=orpc" target="_blank" rel="noopener" title="Nicholas"><img src="https://avatars.githubusercontent.com/u/54724556?u=56a7ab430ce7a80d648ab6eba051d454a818ed0b&v=4" width="32" height="32" alt="Nicholas" /></a> | ||
| </p> | ||
@@ -195,0 +195,0 @@ |
| import { StandardBody } from '@standardserver/core'; | ||
| import { Segment } from '@orpc/shared'; | ||
| type RPCJsonSerializationMeta = [type: string, ...path: Segment[]]; | ||
| type RPCJsonSerialization = { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps?: undefined; | ||
| blobs?: undefined; | ||
| } | { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps: Segment[][]; | ||
| blobs: Blob[]; | ||
| }; | ||
| interface RPCJsonSerializerHandler { | ||
| condition(value: unknown): boolean; | ||
| serialize(value: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| /** | ||
| * If false, the result of this serializer will not be further processed by other serializers, | ||
| * even if it matches their conditions and treat it as final serialized value. | ||
| * This can be useful for serializers that return primitive values, which should not be further processed. | ||
| * to improve performance and avoid potential issues with other serializers. | ||
| * | ||
| * @default false | ||
| */ | ||
| isTerminal?: boolean; | ||
| } | ||
| interface RPCJsonSerializerOptions { | ||
| /** | ||
| * Extend or override the built-in type handlers used during serialization and deserialization. | ||
| * | ||
| * Each key is a unique type identifier (e.g. `"date"`, `"bigint"`) and maps to a handler | ||
| * that defines how to detect, serialize, and deserialize values of that type. | ||
| * | ||
| * **Extending:** Add new keys to support custom types: | ||
| * ```ts | ||
| * handlers: { | ||
| * buffer: { | ||
| * condition: (v) => v instanceof Buffer, | ||
| * serialize: (v: Buffer) => v.toString('base64'), | ||
| * deserialize: (s: string) => Buffer.from(s, 'base64'), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Overriding:** Use an existing key to replace a built-in handler: | ||
| * ```ts | ||
| * handlers: { | ||
| * date: { | ||
| * condition: (v) => v instanceof Date, | ||
| * serialize: (v: Date) => v.getTime(), | ||
| * deserialize: (n: number) => new Date(n), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Disabling:** Set a key to `undefined` to remove a built-in handler: | ||
| * ```ts | ||
| * handlers: { regexp: undefined } | ||
| * ``` | ||
| * | ||
| * Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `url`, `regexp`, `set`, `map`. | ||
| */ | ||
| handlers?: Record<string, undefined | RPCJsonSerializerHandler> | undefined; | ||
| /** | ||
| * If true, properties with undefined values will be omitted during serialization. | ||
| * | ||
| * @default true | ||
| */ | ||
| omitUndefinedProperties?: boolean | undefined; | ||
| } | ||
| declare class RPCJsonSerializer { | ||
| private readonly handlers; | ||
| private readonly omitUndefinedProperties; | ||
| constructor(options?: RPCJsonSerializerOptions); | ||
| serialize(data: unknown): RPCJsonSerialization; | ||
| private serializeValue; | ||
| deserialize(serialized: RPCJsonSerialization): unknown; | ||
| } | ||
| interface RPCSerializerSerializeOptions { | ||
| /** | ||
| * Use FormData for serialization when nested blobs are present. | ||
| * Does not apply to root-level Blob values. | ||
| * | ||
| * @default true | ||
| */ | ||
| useFormDataForBlobFields?: boolean; | ||
| } | ||
| interface RPCSerializerOptions extends RPCJsonSerializerOptions { | ||
| /** | ||
| * Default options for serialize method | ||
| */ | ||
| serialize?: RPCSerializerSerializeOptions | undefined; | ||
| } | ||
| declare class RPCSerializer { | ||
| private readonly jsonSerializer; | ||
| private readonly defaultSerializeOptions; | ||
| constructor(options?: RPCSerializerOptions); | ||
| serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody; | ||
| private serializeValue; | ||
| deserialize(data: StandardBody): unknown; | ||
| private deserializeValue; | ||
| } | ||
| export { RPCJsonSerializer as b, RPCSerializer as e }; | ||
| export type { RPCJsonSerialization as R, RPCJsonSerializationMeta as a, RPCJsonSerializerHandler as c, RPCJsonSerializerOptions as d, RPCSerializerOptions as f, RPCSerializerSerializeOptions as g }; |
| import { StandardBody } from '@standardserver/core'; | ||
| import { Segment } from '@orpc/shared'; | ||
| type RPCJsonSerializationMeta = [type: string, ...path: Segment[]]; | ||
| type RPCJsonSerialization = { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps?: undefined; | ||
| blobs?: undefined; | ||
| } | { | ||
| json: unknown; | ||
| meta?: RPCJsonSerializationMeta[] | undefined; | ||
| maps: Segment[][]; | ||
| blobs: Blob[]; | ||
| }; | ||
| interface RPCJsonSerializerHandler { | ||
| condition(value: unknown): boolean; | ||
| serialize(value: any): unknown; | ||
| deserialize(serialized: any): unknown; | ||
| /** | ||
| * If false, the result of this serializer will not be further processed by other serializers, | ||
| * even if it matches their conditions and treat it as final serialized value. | ||
| * This can be useful for serializers that return primitive values, which should not be further processed. | ||
| * to improve performance and avoid potential issues with other serializers. | ||
| * | ||
| * @default false | ||
| */ | ||
| isTerminal?: boolean; | ||
| } | ||
| interface RPCJsonSerializerOptions { | ||
| /** | ||
| * Extend or override the built-in type handlers used during serialization and deserialization. | ||
| * | ||
| * Each key is a unique type identifier (e.g. `"date"`, `"bigint"`) and maps to a handler | ||
| * that defines how to detect, serialize, and deserialize values of that type. | ||
| * | ||
| * **Extending:** Add new keys to support custom types: | ||
| * ```ts | ||
| * handlers: { | ||
| * buffer: { | ||
| * condition: (v) => v instanceof Buffer, | ||
| * serialize: (v: Buffer) => v.toString('base64'), | ||
| * deserialize: (s: string) => Buffer.from(s, 'base64'), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Overriding:** Use an existing key to replace a built-in handler: | ||
| * ```ts | ||
| * handlers: { | ||
| * date: { | ||
| * condition: (v) => v instanceof Date, | ||
| * serialize: (v: Date) => v.getTime(), | ||
| * deserialize: (n: number) => new Date(n), | ||
| * isTerminal: true, | ||
| * } | ||
| * } | ||
| * ``` | ||
| * | ||
| * **Disabling:** Set a key to `undefined` to remove a built-in handler: | ||
| * ```ts | ||
| * handlers: { regexp: undefined } | ||
| * ``` | ||
| * | ||
| * Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `url`, `regexp`, `set`, `map`. | ||
| */ | ||
| handlers?: Record<string, undefined | RPCJsonSerializerHandler> | undefined; | ||
| /** | ||
| * If true, properties with undefined values will be omitted during serialization. | ||
| * | ||
| * @default true | ||
| */ | ||
| omitUndefinedProperties?: boolean | undefined; | ||
| } | ||
| declare class RPCJsonSerializer { | ||
| private readonly handlers; | ||
| private readonly omitUndefinedProperties; | ||
| constructor(options?: RPCJsonSerializerOptions); | ||
| serialize(data: unknown): RPCJsonSerialization; | ||
| private serializeValue; | ||
| deserialize(serialized: RPCJsonSerialization): unknown; | ||
| } | ||
| interface RPCSerializerSerializeOptions { | ||
| /** | ||
| * Use FormData for serialization when nested blobs are present. | ||
| * Does not apply to root-level Blob values. | ||
| * | ||
| * @default true | ||
| */ | ||
| useFormDataForBlobFields?: boolean; | ||
| } | ||
| interface RPCSerializerOptions extends RPCJsonSerializerOptions { | ||
| /** | ||
| * Default options for serialize method | ||
| */ | ||
| serialize?: RPCSerializerSerializeOptions | undefined; | ||
| } | ||
| declare class RPCSerializer { | ||
| private readonly jsonSerializer; | ||
| private readonly defaultSerializeOptions; | ||
| constructor(options?: RPCSerializerOptions); | ||
| serialize(data: unknown, options?: RPCSerializerSerializeOptions): StandardBody; | ||
| private serializeValue; | ||
| deserialize(data: StandardBody): unknown; | ||
| private deserializeValue; | ||
| } | ||
| export { RPCJsonSerializer as b, RPCSerializer as e }; | ||
| export type { RPCJsonSerialization as R, RPCJsonSerializationMeta as a, RPCJsonSerializerHandler as c, RPCJsonSerializerOptions as d, RPCSerializerOptions as f, RPCSerializerSerializeOptions as g }; |
| import { sortPlugins, runWithSpan, ORPC_NAME, isAsyncIteratorObject, override, traceAsyncIterator, intercept, getOpenTelemetryConfig, value, pathToHttpPath, stringifyJSON } from '@orpc/shared'; | ||
| import { mergeStandardHeaders, parseStandardUrl } from '@standardserver/core'; | ||
| import { toStandardHeaders } from '@standardserver/fetch'; | ||
| import { O as ORPCError } from './client.Dnfj8jnT.mjs'; | ||
| import { R as RPCSerializer, i as isORPCErrorJson, c as createORPCErrorFromJson } from './client.DqYwRDUO.mjs'; | ||
| class CompositeStandardLinkPlugin { | ||
| name = "~composite"; | ||
| plugins; | ||
| constructor(plugins = []) { | ||
| this.plugins = sortPlugins(plugins); | ||
| } | ||
| init(options) { | ||
| for (const plugin of this.plugins) { | ||
| if (plugin.init) { | ||
| options = plugin.init(options); | ||
| } | ||
| } | ||
| return options; | ||
| } | ||
| } | ||
| class StandardLink { | ||
| constructor(codec, transport, options = {}) { | ||
| this.codec = codec; | ||
| this.transport = transport; | ||
| options = new CompositeStandardLinkPlugin(options.plugins).init(options); | ||
| this.interceptors = options.interceptors; | ||
| this.transportInterceptors = options.transportInterceptors; | ||
| } | ||
| interceptors; | ||
| transportInterceptors; | ||
| /** | ||
| * @throws ORPCError, transport-level errors (network failures, timeouts, etc.) | ||
| */ | ||
| call(path, input, options) { | ||
| return runWithSpan(`${ORPC_NAME}.${path.join("/")}`, (span) => { | ||
| span?.setAttribute("rpc.system", ORPC_NAME); | ||
| span?.setAttribute("rpc.method", path.join(".")); | ||
| if (isAsyncIteratorObject(input)) { | ||
| input = override(input, traceAsyncIterator("consume_async_iterator_object_input", input)); | ||
| } | ||
| return intercept(this.interceptors, { ...options, path, input }, async ({ path: path2, input: input2, ...options2 }) => { | ||
| const otel = getOpenTelemetryConfig(); | ||
| let activeContext; | ||
| const activeSpan = otel?.trace.getActiveSpan() ?? span; | ||
| if (activeSpan && otel) { | ||
| activeContext = otel.trace.setSpan(otel.context.active(), activeSpan); | ||
| } | ||
| let request = await runWithSpan( | ||
| { name: "encode_input", context: activeContext }, | ||
| () => this.codec.encodeInput(input2, path2, options2) | ||
| ); | ||
| if (activeContext && otel?.propagation) { | ||
| const headers = { ...request.headers }; | ||
| otel.propagation.inject(activeContext, headers); | ||
| request = { ...request, headers }; | ||
| } | ||
| const response = await intercept( | ||
| this.transportInterceptors, | ||
| { ...options2, path: path2, request }, | ||
| ({ path: path3, request: request2, ...options3 }) => { | ||
| let activeTransportContext; | ||
| const activeTransportSpan = otel?.trace.getActiveSpan() ?? activeSpan; | ||
| if (activeTransportSpan && otel) { | ||
| activeTransportContext = otel.trace.setSpan(otel.context.active(), activeTransportSpan); | ||
| } | ||
| return runWithSpan( | ||
| { name: "send_request", context: activeTransportContext }, | ||
| () => this.transport.send(request2, path3, options3) | ||
| ); | ||
| } | ||
| ); | ||
| const decodedResult = await runWithSpan( | ||
| { name: "decode_response", context: activeContext }, | ||
| () => this.codec.decodeResponse(response, path2, options2) | ||
| ); | ||
| if (decodedResult.kind === "error") { | ||
| throw decodedResult.error; | ||
| } | ||
| const output = decodedResult.output; | ||
| if (isAsyncIteratorObject(output)) { | ||
| return override(output, traceAsyncIterator("consume_async_iterator_object_output", output)); | ||
| } | ||
| return output; | ||
| }); | ||
| }); | ||
| } | ||
| } | ||
| const END_SLASH_REGEX = /\/$/; | ||
| class RPCLinkCodec { | ||
| baseUrl; | ||
| maxUrlLength; | ||
| fallbackMethod; | ||
| expectedMethod; | ||
| headers; | ||
| serializer; | ||
| constructor(options) { | ||
| this.baseUrl = options.url ?? "/"; | ||
| this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
| this.fallbackMethod = options.fallbackMethod ?? "POST"; | ||
| this.expectedMethod = options.method ?? this.fallbackMethod; | ||
| this.headers = options.headers ?? {}; | ||
| this.serializer = options.serializer ?? new RPCSerializer(); | ||
| } | ||
| async encodeInput(input, path, options) { | ||
| let headers = toResolvedStandardHeaders(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 [pathname, search, hash] = parseStandardUrl(baseUrl); | ||
| const newPathname = `${pathname.replace(END_SLASH_REGEX, "")}${pathToHttpPath(path)}`; | ||
| const serialized = this.serializer.serialize(input); | ||
| if (expectedMethod === "GET" && !(serialized instanceof Blob) && !(serialized instanceof ReadableStream) && !(serialized instanceof FormData) && !isAsyncIteratorObject(serialized)) { | ||
| const maxUrlLength = await value(this.maxUrlLength, options, path, input); | ||
| const mergedSearch = new URLSearchParams(search); | ||
| mergedSearch.append("data", stringifyJSON(serialized) ?? ""); | ||
| const url2 = `${newPathname}?${mergedSearch}${hash ?? ""}`; | ||
| if (url2.length <= maxUrlLength) { | ||
| return { | ||
| body: void 0, | ||
| method: expectedMethod, | ||
| headers, | ||
| url: url2, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| } | ||
| const url = `${newPathname}${search ?? ""}${hash ?? ""}`; | ||
| return { | ||
| url, | ||
| method: expectedMethod === "GET" ? this.fallbackMethod : expectedMethod, | ||
| headers, | ||
| body: serialized, | ||
| signal: options.signal | ||
| }; | ||
| } | ||
| async decodeResponse(response) { | ||
| const isOk = response.status < 400; | ||
| const body = await response.resolveBody(); | ||
| const deserialized = await (async () => { | ||
| try { | ||
| return this.serializer.deserialize(body); | ||
| } catch (cause) { | ||
| throw new Error("Invalid RPC response format.", { | ||
| cause | ||
| }); | ||
| } | ||
| })(); | ||
| if (!isOk) { | ||
| if (isORPCErrorJson(deserialized)) { | ||
| return { kind: "error", error: createORPCErrorFromJson(deserialized) }; | ||
| } | ||
| return { | ||
| kind: "error", | ||
| error: new ORPCError("MALFORMED_ORPC_ERROR_RESPONSE", { | ||
| data: { headers: response.headers, status: response.status, body: deserialized } | ||
| }) | ||
| }; | ||
| } | ||
| return { kind: "output", output: deserialized }; | ||
| } | ||
| } | ||
| function toResolvedStandardHeaders(headers) { | ||
| if (typeof headers.forEach === "function") { | ||
| return toStandardHeaders(headers); | ||
| } | ||
| return headers; | ||
| } | ||
| export { CompositeStandardLinkPlugin as C, RPCLinkCodec as R, StandardLink as S }; |
| import { isPlainObject, wrapAsyncIterator, isTypescriptObject, isAsyncIteratorObject, stringifyJSON } from '@orpc/shared'; | ||
| import { getEventMeta, withEventMeta, ErrorEvent } from '@standardserver/core'; | ||
| import { O as ORPCError } from './client.Dnfj8jnT.mjs'; | ||
| function isInferableError(error) { | ||
| return error instanceof ORPCError && error.inferable; | ||
| } | ||
| function toORPCError(error) { | ||
| return error instanceof ORPCError ? error : new ORPCError("INTERNAL_SERVER_ERROR", { cause: error }); | ||
| } | ||
| function isORPCErrorJson(json) { | ||
| if (!isPlainObject(json)) { | ||
| return false; | ||
| } | ||
| const validKeys = ["defined", "inferable", "code", "message", "data"]; | ||
| if (Object.keys(json).some((k) => !validKeys.includes(k))) { | ||
| return false; | ||
| } | ||
| return "defined" in json && typeof json.defined === "boolean" && "inferable" in json && typeof json.inferable === "boolean" && "code" in json && typeof json.code === "string" && "message" in json && typeof json.message === "string"; | ||
| } | ||
| function createORPCErrorFromJson(json, options = {}) { | ||
| const error = new ORPCError(json.code, { | ||
| ...json, | ||
| ...options | ||
| }); | ||
| error.defined = json.defined; | ||
| error.inferable = json.inferable; | ||
| return error; | ||
| } | ||
| function cloneORPCError(error) { | ||
| const cloned = new ORPCError(error.code, { | ||
| ...error, | ||
| message: error.message, | ||
| data: error.data, | ||
| cause: error.cause | ||
| }); | ||
| cloned.stack = error.stack; | ||
| cloned.defined = error.defined; | ||
| cloned.inferable = error.inferable; | ||
| return cloned; | ||
| } | ||
| function wrapAsyncIteratorPreservingEventMeta(iterator, { mapResult, mapError, ...rest }) { | ||
| return wrapAsyncIterator(iterator, { | ||
| ...rest, | ||
| mapResult: mapResult && (async (result) => { | ||
| const mapped = await mapResult(result); | ||
| if (mapped.value !== result.value) { | ||
| const meta = getEventMeta(result.value); | ||
| if (meta && isTypescriptObject(mapped.value)) { | ||
| return { done: mapped.done, value: withEventMeta(mapped.value, meta) }; | ||
| } | ||
| } | ||
| return mapped; | ||
| }), | ||
| mapError: mapError && (async (error) => { | ||
| const mapped = await mapError(error); | ||
| if (mapped !== error) { | ||
| const meta = getEventMeta(error); | ||
| if (meta && isTypescriptObject(mapped)) { | ||
| return withEventMeta(mapped, meta); | ||
| } | ||
| } | ||
| return mapped; | ||
| }) | ||
| }); | ||
| } | ||
| const REGEX_STRING_PATTERN = /^\/(.*)\/([a-z]*)$/; | ||
| const DEFAULT_RPC_JSON_SERIALIZER_HANDLERS = { | ||
| undefined: { | ||
| condition(data) { | ||
| return data === void 0; | ||
| }, | ||
| serialize() { | ||
| return null; | ||
| }, | ||
| deserialize() { | ||
| return void 0; | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| bigint: { | ||
| condition(data) { | ||
| return typeof data === "bigint"; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return BigInt(serialized); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| date: { | ||
| condition(data) { | ||
| return data instanceof Date; | ||
| }, | ||
| serialize(data) { | ||
| if (Number.isNaN(data.getTime())) { | ||
| return null; | ||
| } | ||
| return data.toISOString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Date(serialized ?? "Invalid Date"); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| nan: { | ||
| condition(data) { | ||
| return typeof data === "number" && Number.isNaN(data); | ||
| }, | ||
| serialize() { | ||
| return null; | ||
| }, | ||
| deserialize() { | ||
| return Number.NaN; | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| url: { | ||
| condition(data) { | ||
| return data instanceof URL; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new URL(serialized); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| regexp: { | ||
| condition(data) { | ||
| return data instanceof RegExp; | ||
| }, | ||
| serialize(data) { | ||
| return data.toString(); | ||
| }, | ||
| deserialize(serialized) { | ||
| const [, pattern, flags] = serialized.match(REGEX_STRING_PATTERN); | ||
| return new RegExp(pattern, flags); | ||
| }, | ||
| isTerminal: true | ||
| }, | ||
| set: { | ||
| condition(data) { | ||
| return data instanceof Set; | ||
| }, | ||
| serialize(data) { | ||
| return Array.from(data); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Set(serialized); | ||
| } | ||
| }, | ||
| map: { | ||
| condition(data) { | ||
| return data instanceof Map; | ||
| }, | ||
| serialize(data) { | ||
| return Array.from(data.entries()); | ||
| }, | ||
| deserialize(serialized) { | ||
| return new Map(serialized); | ||
| } | ||
| } | ||
| }; | ||
| class RPCJsonSerializer { | ||
| handlers; | ||
| omitUndefinedProperties; | ||
| constructor(options = {}) { | ||
| this.handlers = { | ||
| ...DEFAULT_RPC_JSON_SERIALIZER_HANDLERS, | ||
| ...options.handlers | ||
| }; | ||
| this.omitUndefinedProperties = options.omitUndefinedProperties !== false; | ||
| } | ||
| serialize(data) { | ||
| const [json, meta_, maps, blobs] = this.serializeValue(data, [], [], [], []); | ||
| const meta = meta_.length === 0 ? void 0 : meta_; | ||
| if (maps.length === 0) { | ||
| return { json, meta }; | ||
| } | ||
| return { json, meta, maps, blobs }; | ||
| } | ||
| serializeValue(data, segments, meta, maps, blobs) { | ||
| for (const key in this.handlers) { | ||
| const handler = this.handlers[key]; | ||
| if (handler && handler.condition(data)) { | ||
| const serialized = handler.serialize(data); | ||
| if (handler.isTerminal) { | ||
| meta.push([key, ...segments]); | ||
| return [serialized, meta, maps, blobs]; | ||
| } | ||
| const result = this.serializeValue(serialized, segments, meta, maps, blobs); | ||
| meta.push([key, ...segments]); | ||
| return result; | ||
| } | ||
| } | ||
| if (data instanceof Blob) { | ||
| maps.push(segments); | ||
| blobs.push(data); | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| if (Array.isArray(data)) { | ||
| const json = data.map((v, i) => { | ||
| return this.serializeValue(v, [...segments, i], meta, maps, blobs)[0]; | ||
| }); | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| if (isPlainObject(data)) { | ||
| const json = {}; | ||
| for (const k in data) { | ||
| const v = data[k]; | ||
| if (k === "toJSON" && typeof v === "function") { | ||
| continue; | ||
| } | ||
| if (v === void 0 && this.omitUndefinedProperties) { | ||
| continue; | ||
| } | ||
| json[k] = this.serializeValue(v, [...segments, k], meta, maps, blobs)[0]; | ||
| } | ||
| return [json, meta, maps, blobs]; | ||
| } | ||
| return [data, meta, maps, blobs]; | ||
| } | ||
| deserialize(serialized) { | ||
| const ref = { data: serialized.json }; | ||
| if (serialized.blobs?.length) { | ||
| serialized.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: Invalid serialized data. Segment "${preSegment}" does not exist.`); | ||
| } | ||
| }); | ||
| currentRef[preSegment] = serialized.blobs[i]; | ||
| }); | ||
| } | ||
| serialized.meta?.forEach((item) => { | ||
| 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: Invalid serialized data. Segment "${preSegment}" does not exist.`); | ||
| } | ||
| } | ||
| currentRef[preSegment] = this.handlers[type].deserialize(currentRef[preSegment]); | ||
| }); | ||
| return ref.data; | ||
| } | ||
| } | ||
| class RPCSerializer { | ||
| jsonSerializer; | ||
| defaultSerializeOptions; | ||
| constructor(options = {}) { | ||
| this.jsonSerializer = new RPCJsonSerializer(options); | ||
| this.defaultSerializeOptions = options.serialize; | ||
| } | ||
| serialize(data, options = {}) { | ||
| if (data === void 0 || data instanceof ReadableStream || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| if (isAsyncIteratorObject(data)) { | ||
| return wrapAsyncIteratorPreservingEventMeta(data, { | ||
| mapResult: (result) => { | ||
| if (result.value === void 0) { | ||
| return result; | ||
| } | ||
| return { done: result.done, value: this.serializeValue(result.value, options) }; | ||
| }, | ||
| mapError: (e) => new ErrorEvent( | ||
| this.serializeValue(toORPCError(e).toJSON(), { ...options, useFormDataForBlobFields: false }), | ||
| { cause: e } | ||
| ) | ||
| }); | ||
| } | ||
| return this.serializeValue(data, options); | ||
| } | ||
| serializeValue(data, options) { | ||
| const useFormDataForBlobs = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true; | ||
| const { json, meta, maps, blobs } = this.jsonSerializer.serialize(data); | ||
| if (!useFormDataForBlobs || !blobs?.length) { | ||
| 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 (data === void 0 || data instanceof ReadableStream || data instanceof Blob) { | ||
| return data; | ||
| } | ||
| if (isAsyncIteratorObject(data)) { | ||
| return wrapAsyncIteratorPreservingEventMeta(data, { | ||
| mapResult: (result) => { | ||
| if (result.value === void 0) { | ||
| return result; | ||
| } | ||
| return { done: result.done, value: this.deserializeValue(result.value) }; | ||
| }, | ||
| mapError: (e) => { | ||
| if (!(e instanceof ErrorEvent)) { | ||
| return e; | ||
| } | ||
| const deserialized = this.deserializeValue(e.data); | ||
| if (isORPCErrorJson(deserialized)) { | ||
| return createORPCErrorFromJson(deserialized, { cause: e }); | ||
| } | ||
| return new ErrorEvent(deserialized, { cause: e }); | ||
| } | ||
| }); | ||
| } | ||
| return this.deserializeValue(data); | ||
| } | ||
| deserializeValue(data) { | ||
| if (!(data instanceof FormData)) { | ||
| return this.jsonSerializer.deserialize(data); | ||
| } | ||
| const serialized = JSON.parse(data.get("data")); | ||
| const blobs = []; | ||
| for (const [key, value] of data) { | ||
| if (value instanceof Blob) { | ||
| blobs[Number(key)] = value; | ||
| } | ||
| } | ||
| return this.jsonSerializer.deserialize({ ...serialized, blobs }); | ||
| } | ||
| } | ||
| export { RPCSerializer as R, isInferableError as a, RPCJsonSerializer as b, createORPCErrorFromJson as c, cloneORPCError as d, isORPCErrorJson as i, toORPCError as t, wrapAsyncIteratorPreservingEventMeta as w }; |
199143
1.79%3040
3.47%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated