@orpc/client
Advanced tools
Comparing version 0.0.0-next.3f6c426 to 0.0.0-next.430a0fe
@@ -1,87 +0,128 @@ | ||
// src/adapters/fetch/orpc-link.ts | ||
import { ORPCPayloadCodec } from "@orpc/server/fetch"; | ||
import { ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE, trim } from "@orpc/shared"; | ||
import { ORPCError } from "@orpc/shared/error"; | ||
var ORPCLink = class { | ||
import { | ||
RPCSerializer | ||
} from "./chunk-TPEMQB7D.js"; | ||
import { | ||
ORPCError, | ||
createAutoRetryEventIterator | ||
} from "./chunk-2UPNYYFF.js"; | ||
// src/adapters/fetch/rpc-link.ts | ||
import { isAsyncIteratorObject } from "@orpc/server-standard"; | ||
import { toFetchBody, toStandardBody } from "@orpc/server-standard-fetch"; | ||
import { trim, value } from "@orpc/shared"; | ||
var InvalidEventSourceRetryResponse = class extends Error { | ||
}; | ||
var RPCLink = class { | ||
fetch; | ||
payloadCodec; | ||
maxURLLength; | ||
rpcSerializer; | ||
maxUrlLength; | ||
fallbackMethod; | ||
getMethod; | ||
getHeaders; | ||
method; | ||
headers; | ||
url; | ||
eventSourceMaxNumberOfRetries; | ||
eventSourceRetryDelay; | ||
eventSourceRetry; | ||
constructor(options) { | ||
this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis); | ||
this.payloadCodec = options.payloadCodec ?? new ORPCPayloadCodec(); | ||
this.maxURLLength = options.maxURLLength ?? 2083; | ||
this.rpcSerializer = options.rpcSerializer ?? new RPCSerializer(); | ||
this.maxUrlLength = options.maxUrlLength ?? 2083; | ||
this.fallbackMethod = options.fallbackMethod ?? "POST"; | ||
this.url = options.url; | ||
this.getMethod = async (path, input, context) => { | ||
return await options.method?.(path, input, context) ?? this.fallbackMethod; | ||
}; | ||
this.getHeaders = async (path, input, context) => { | ||
return new Headers(await options.headers?.(path, input, context)); | ||
}; | ||
this.eventSourceMaxNumberOfRetries = options.eventSourceMaxNumberOfRetries ?? 5; | ||
this.method = options.method ?? this.fallbackMethod; | ||
this.headers = options.headers ?? {}; | ||
this.eventSourceRetry = options.eventSourceRetry ?? true; | ||
this.eventSourceRetryDelay = options.eventSourceRetryDelay ?? (({ retryTimes, lastRetry }) => lastRetry ?? 1e3 * 2 ** retryTimes); | ||
} | ||
async call(path, input, options) { | ||
const clientContext = options.context; | ||
const encoded = await this.encode(path, input, options); | ||
const output = await this.performCall(path, input, options); | ||
if (!isAsyncIteratorObject(output)) { | ||
return output; | ||
} | ||
return createAutoRetryEventIterator(output, async (reconnectOptions) => { | ||
if (options.signal?.aborted || reconnectOptions.retryTimes > this.eventSourceMaxNumberOfRetries) { | ||
return null; | ||
} | ||
if (!await value(this.eventSourceRetry, reconnectOptions, options, path, input)) { | ||
return null; | ||
} | ||
const delay = await value(this.eventSourceRetryDelay, reconnectOptions, options, path, input); | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
const updatedOptions = { ...options, lastEventId: reconnectOptions.lastEventId }; | ||
const maybeIterator = await this.performCall(path, input, updatedOptions); | ||
if (!isAsyncIteratorObject(maybeIterator)) { | ||
throw new InvalidEventSourceRetryResponse("Invalid EventSource retry response"); | ||
} | ||
return maybeIterator; | ||
}, void 0); | ||
} | ||
async performCall(path, input, options) { | ||
const encoded = await this.encodeRequest(path, input, options); | ||
const fetchBody = toFetchBody(encoded.body, encoded.headers); | ||
if (options.lastEventId !== void 0) { | ||
encoded.headers.set("last-event-id", options.lastEventId); | ||
} | ||
const response = await this.fetch(encoded.url, { | ||
method: encoded.method, | ||
headers: encoded.headers, | ||
body: encoded.body, | ||
body: fetchBody, | ||
signal: options.signal | ||
}, clientContext); | ||
const decoded = await this.payloadCodec.decode(response); | ||
}, options, path, input); | ||
const body = await toStandardBody(response); | ||
const deserialized = (() => { | ||
try { | ||
return this.rpcSerializer.deserialize(body); | ||
} catch (error) { | ||
if (response.ok) { | ||
throw new ORPCError("INTERNAL_SERVER_ERROR", { | ||
message: "Invalid RPC response", | ||
cause: error | ||
}); | ||
} | ||
throw new ORPCError(response.status.toString(), { | ||
message: response.statusText | ||
}); | ||
} | ||
})(); | ||
if (!response.ok) { | ||
const error = ORPCError.fromJSON(decoded) ?? new ORPCError({ | ||
status: response.status, | ||
code: "INTERNAL_SERVER_ERROR", | ||
message: "Internal server error", | ||
cause: decoded | ||
if (ORPCError.isValidJSON(deserialized)) { | ||
throw ORPCError.fromJSON(deserialized); | ||
} | ||
throw new ORPCError("INTERNAL_SERVER_ERROR", { | ||
message: "Invalid RPC error response", | ||
cause: deserialized | ||
}); | ||
throw error; | ||
} | ||
return decoded; | ||
return deserialized; | ||
} | ||
async encode(path, input, options) { | ||
const clientContext = options.context; | ||
const expectMethod = await this.getMethod(path, input, clientContext); | ||
const methods = /* @__PURE__ */ new Set([expectMethod, this.fallbackMethod]); | ||
const baseHeaders = await this.getHeaders(path, input, clientContext); | ||
const baseUrl = new URL(`${trim(this.url, "/")}/${path.map(encodeURIComponent).join("/")}`); | ||
baseHeaders.append(ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE); | ||
for (const method of methods) { | ||
const url = new URL(baseUrl); | ||
const headers = new Headers(baseHeaders); | ||
const encoded = this.payloadCodec.encode(input, method, this.fallbackMethod); | ||
if (encoded.query) { | ||
for (const [key, value] of encoded.query.entries()) { | ||
url.searchParams.append(key, value); | ||
} | ||
async encodeRequest(path, input, options) { | ||
const expectedMethod = await value(this.method, options, path, input); | ||
const headers = new Headers(await value(this.headers, options, path, input)); | ||
const url = new URL(`${trim(this.url, "/")}/${path.map(encodeURIComponent).join("/")}`); | ||
const serialized = this.rpcSerializer.serialize(input); | ||
if (expectedMethod === "GET" && !(serialized instanceof FormData) && !isAsyncIteratorObject(serialized)) { | ||
const getUrl = new URL(url); | ||
getUrl.searchParams.append("data", JSON.stringify(serialized)); | ||
if (getUrl.toString().length <= this.maxUrlLength) { | ||
return { | ||
body: void 0, | ||
method: expectedMethod, | ||
headers, | ||
url: getUrl | ||
}; | ||
} | ||
if (url.toString().length > this.maxURLLength) { | ||
continue; | ||
} | ||
if (encoded.headers) { | ||
for (const [key, value] of encoded.headers.entries()) { | ||
headers.append(key, value); | ||
} | ||
} | ||
return { | ||
url, | ||
headers, | ||
method: encoded.method, | ||
body: encoded.body | ||
}; | ||
} | ||
throw new ORPCError({ | ||
code: "BAD_REQUEST", | ||
message: "Cannot encode the request, please check the url length or payload." | ||
}); | ||
return { | ||
url, | ||
method: expectedMethod === "GET" ? this.fallbackMethod : expectedMethod, | ||
headers, | ||
body: serialized | ||
}; | ||
} | ||
}; | ||
export { | ||
ORPCLink | ||
InvalidEventSourceRetryResponse, | ||
RPCLink | ||
}; | ||
//# sourceMappingURL=fetch.js.map |
@@ -0,1 +1,15 @@ | ||
import { | ||
COMMON_ORPC_ERROR_DEFS, | ||
ORPCError, | ||
createAutoRetryEventIterator, | ||
fallbackORPCErrorMessage, | ||
fallbackORPCErrorStatus, | ||
isDefinedError, | ||
mapEventIterator, | ||
onEventIteratorStatusChange, | ||
registerEventIteratorState, | ||
toORPCError, | ||
updateEventIteratorStatus | ||
} from "./chunk-2UPNYYFF.js"; | ||
// src/client.ts | ||
@@ -5,3 +19,8 @@ function createORPCClient(link, options) { | ||
const procedureClient = async (...[input, options2]) => { | ||
return await link.call(path, 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); | ||
}; | ||
@@ -28,3 +47,3 @@ const recursive = new Proxy(procedureClient, { | ||
async call(path, input, options) { | ||
const resolvedLink = await this.linkResolver(path, input, options.context); | ||
const resolvedLink = await this.linkResolver(options, path, input); | ||
const output = await resolvedLink.call(path, input, options); | ||
@@ -35,8 +54,31 @@ return output; | ||
// src/index.ts | ||
export * from "@orpc/shared/error"; | ||
// src/utils.ts | ||
async function safe(promise) { | ||
try { | ||
const output = await promise; | ||
return [output, void 0, false]; | ||
} catch (e) { | ||
const error = e; | ||
if (isDefinedError(error)) { | ||
return [void 0, error, true]; | ||
} | ||
return [void 0, error, false]; | ||
} | ||
} | ||
export { | ||
COMMON_ORPC_ERROR_DEFS, | ||
DynamicLink, | ||
createORPCClient | ||
ORPCError, | ||
createAutoRetryEventIterator, | ||
createORPCClient, | ||
fallbackORPCErrorMessage, | ||
fallbackORPCErrorStatus, | ||
isDefinedError, | ||
mapEventIterator, | ||
onEventIteratorStatusChange, | ||
registerEventIteratorState, | ||
safe, | ||
toORPCError, | ||
updateEventIteratorStatus | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -1,3 +0,3 @@ | ||
export * from './orpc-link'; | ||
export * from './rpc-link'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,4 +0,5 @@ | ||
export interface FetchWithContext<TClientContext> { | ||
(input: RequestInfo | URL, init: RequestInit | undefined, context: TClientContext): Promise<Response>; | ||
import type { ClientContext, ClientOptionsOut } from '../../types'; | ||
export interface FetchWithContext<TClientContext extends ClientContext> { | ||
(url: URL, init: RequestInit, options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown): Promise<Response>; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,4 +0,2 @@ | ||
import type { ContractRouter } from '@orpc/contract'; | ||
import type { ANY_ROUTER, RouterClient } from '@orpc/server'; | ||
import type { ClientLink } from './types'; | ||
import type { ClientLink, InferClientContext, NestedClient } from './types'; | ||
export interface createORPCClientOptions { | ||
@@ -10,3 +8,3 @@ /** | ||
} | ||
export declare function createORPCClient<TRouter extends ANY_ROUTER | ContractRouter, TClientContext = unknown>(link: ClientLink<TClientContext>, options?: createORPCClientOptions): RouterClient<TRouter, TClientContext>; | ||
export declare function createORPCClient<T extends NestedClient<any>>(link: ClientLink<InferClientContext<T>>, options?: createORPCClientOptions): T; | ||
//# sourceMappingURL=client.d.ts.map |
@@ -1,4 +0,3 @@ | ||
import type { ProcedureClientOptions } from '@orpc/server'; | ||
import type { Promisable } from '@orpc/shared'; | ||
import type { ClientLink } from './types'; | ||
import type { ClientContext, ClientLink, ClientOptionsOut } from './types'; | ||
/** | ||
@@ -8,7 +7,7 @@ * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks | ||
*/ | ||
export declare class DynamicLink<TClientContext> implements ClientLink<TClientContext> { | ||
export declare class DynamicLink<TClientContext extends ClientContext> implements ClientLink<TClientContext> { | ||
private readonly linkResolver; | ||
constructor(linkResolver: (path: readonly string[], input: unknown, context: TClientContext) => Promisable<ClientLink<TClientContext>>); | ||
call(path: readonly string[], input: unknown, options: ProcedureClientOptions<TClientContext>): Promise<unknown>; | ||
constructor(linkResolver: (options: ClientOptionsOut<TClientContext>, path: readonly string[], input: unknown) => Promisable<ClientLink<TClientContext>>); | ||
call(path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>): Promise<unknown>; | ||
} | ||
//# sourceMappingURL=dynamic-link.d.ts.map |
/** unnoq */ | ||
export * from './client'; | ||
export * from './dynamic-link'; | ||
export * from './error'; | ||
export * from './event-iterator'; | ||
export * from './event-iterator-state'; | ||
export * from './types'; | ||
export * from '@orpc/shared/error'; | ||
export * from './utils'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,5 +0,29 @@ | ||
import type { ProcedureClientOptions } from '@orpc/server'; | ||
export interface ClientLink<TClientContext> { | ||
call: (path: readonly string[], input: unknown, options: ProcedureClientOptions<TClientContext>) => Promise<unknown>; | ||
export type ClientContext = Record<string, any>; | ||
export type ClientOptions<TClientContext extends ClientContext> = { | ||
signal?: AbortSignal; | ||
lastEventId?: string | undefined; | ||
} & (Record<never, never> extends TClientContext ? { | ||
context?: TClientContext; | ||
} : { | ||
context: TClientContext; | ||
}); | ||
export 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>]; | ||
export type ClientPromiseResult<TOutput, TError extends Error> = Promise<TOutput> & { | ||
__error?: { | ||
type: TError; | ||
}; | ||
}; | ||
export interface Client<TClientContext extends ClientContext, TInput, TOutput, TError extends Error> { | ||
(...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>; | ||
} | ||
export type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | { | ||
[k: string]: NestedClient<TClientContext>; | ||
}; | ||
export type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never; | ||
export type ClientOptionsOut<TClientContext extends ClientContext> = ClientOptions<TClientContext> & { | ||
context: TClientContext; | ||
}; | ||
export interface ClientLink<TClientContext extends ClientContext> { | ||
call: (path: readonly string[], input: unknown, options: ClientOptionsOut<TClientContext>) => Promise<unknown>; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@orpc/client", | ||
"type": "module", | ||
"version": "0.0.0-next.3f6c426", | ||
"version": "0.0.0-next.430a0fe", | ||
"license": "MIT", | ||
@@ -22,2 +22,12 @@ "homepage": "https://orpc.unnoq.com", | ||
}, | ||
"./openapi": { | ||
"types": "./dist/src/openapi/index.d.ts", | ||
"import": "./dist/openapi.js", | ||
"default": "./dist/openapi.js" | ||
}, | ||
"./rpc": { | ||
"types": "./dist/src/rpc/index.d.ts", | ||
"import": "./dist/rpc.js", | ||
"default": "./dist/rpc.js" | ||
}, | ||
"./fetch": { | ||
@@ -37,15 +47,12 @@ "types": "./dist/src/adapters/fetch/index.d.ts", | ||
], | ||
"peerDependencies": { | ||
"@orpc/contract": "0.0.0-next.3f6c426" | ||
}, | ||
"dependencies": { | ||
"@orpc/server": "0.0.0-next.3f6c426", | ||
"@orpc/shared": "0.0.0-next.3f6c426" | ||
"@orpc/server-standard": "^0.4.0", | ||
"@orpc/server-standard-fetch": "^0.4.0", | ||
"@orpc/shared": "0.0.0-next.430a0fe" | ||
}, | ||
"devDependencies": { | ||
"zod": "^3.24.1", | ||
"@orpc/openapi": "0.0.0-next.3f6c426" | ||
"zod": "^3.24.1" | ||
}, | ||
"scripts": { | ||
"build": "tsup --clean --sourcemap --entry.index=src/index.ts --entry.fetch=src/adapters/fetch/index.ts --format=esm --onSuccess='tsc -b --noCheck'", | ||
"build": "tsup --onSuccess='tsc -b --noCheck'", | ||
"build:watch": "pnpm run build --watch", | ||
@@ -52,0 +59,0 @@ "type:check": "tsc -b" |
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
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
48057
1
25
1405
+ Added@orpc/server-standard@^0.4.0
+ Added@orpc/server-standard@0.4.0(transitive)
+ Added@orpc/server-standard-fetch@0.4.0(transitive)
+ Added@orpc/shared@0.0.0-next.430a0fe(transitive)
+ Added@tinyhttp/content-disposition@2.2.2(transitive)
- Removed@orpc/server@0.0.0-next.3f6c426
- Removed@mjackson/node-fetch-server@0.5.1(transitive)
- Removed@orpc/contract@0.0.0-next.3f6c426(transitive)
- Removed@orpc/server@0.0.0-next.3f6c426(transitive)
- Removed@orpc/shared@0.0.0-next.3f6c426(transitive)
- Removed@standard-schema/spec@1.0.0-beta.4(transitive)
- Removedis-what@5.2.0(transitive)