@orpc/client 0.0.0-next.f50512c → 0.0.0-next.f5149a8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +7 -2
  2. package/dist/adapters/fetch/index.d.mts +23 -10
  3. package/dist/adapters/fetch/index.d.ts +23 -10
  4. package/dist/adapters/fetch/index.mjs +25 -8
  5. package/dist/adapters/message-port/index.d.mts +29 -8
  6. package/dist/adapters/message-port/index.d.ts +29 -8
  7. package/dist/adapters/message-port/index.mjs +26 -10
  8. package/dist/adapters/standard/index.d.mts +6 -5
  9. package/dist/adapters/standard/index.d.ts +6 -5
  10. package/dist/adapters/standard/index.mjs +4 -2
  11. package/dist/adapters/websocket/index.d.mts +4 -4
  12. package/dist/adapters/websocket/index.d.ts +4 -4
  13. package/dist/adapters/websocket/index.mjs +7 -4
  14. package/dist/index.d.mts +52 -7
  15. package/dist/index.d.ts +52 -7
  16. package/dist/index.mjs +36 -6
  17. package/dist/plugins/index.d.mts +52 -5
  18. package/dist/plugins/index.d.ts +52 -5
  19. package/dist/plugins/index.mjs +121 -25
  20. package/dist/shared/client.BH1AYT_p.d.mts +83 -0
  21. package/dist/shared/client.BH1AYT_p.d.ts +83 -0
  22. package/dist/shared/client.BLtwTQUg.mjs +40 -0
  23. package/dist/shared/{client.WCinBImJ.d.ts → client.BxV-mzeR.d.ts} +3 -3
  24. package/dist/shared/{client.CXXEPIbK.d.ts → client.CPgZaUox.d.mts} +1 -2
  25. package/dist/shared/{client.aTp4sII-.d.mts → client.D8lMmWVC.d.mts} +3 -3
  26. package/dist/shared/{client.DwfV9Oyl.mjs → client.DEVw6TRB.mjs} +63 -20
  27. package/dist/shared/{client.C4VxIexA.d.mts → client.De8SW4Kw.d.ts} +1 -2
  28. package/dist/shared/{client.DHOfWE0c.mjs → client.M38fROHr.mjs} +37 -38
  29. package/package.json +6 -6
  30. package/dist/shared/client.BOYsZIRq.d.mts +0 -29
  31. package/dist/shared/client.BOYsZIRq.d.ts +0 -29
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- 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.BOYsZIRq.js';
2
- export { f as HTTPMethod, H as HTTPPath } from './shared/client.BOYsZIRq.js';
3
- import { MaybeOptionalOptions, ThrowableError, Promisable, AsyncIteratorClass } from '@orpc/shared';
4
- export { EventPublisher, EventPublisherOptions, EventPublisherSubscribeIteratorOptions, Registry, ThrowableError, asyncIteratorToStream as eventIteratorToStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared';
1
+ 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.BH1AYT_p.js';
2
+ 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.BH1AYT_p.js';
3
+ import { MaybeOptionalOptions, ThrowableError, OnFinishState, Promisable, AsyncIteratorClass } from '@orpc/shared';
4
+ export { AsyncIteratorClass, EventPublisher, EventPublisherOptions, EventPublisherSubscribeIteratorOptions, Registry, ThrowableError, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared';
5
5
  export { ErrorEvent } from '@orpc/standard-server';
6
6
 
7
7
  interface createORPCClientOptions {
@@ -113,8 +113,21 @@ declare class ORPCError<TCode extends ORPCErrorCode, TData> extends Error {
113
113
  readonly code: TCode;
114
114
  readonly status: number;
115
115
  readonly data: TData;
116
- constructor(code: TCode, ...[options]: MaybeOptionalOptions<ORPCErrorOptions<TData>>);
116
+ constructor(code: TCode, ...rest: MaybeOptionalOptions<ORPCErrorOptions<TData>>);
117
117
  toJSON(): ORPCErrorJSON<TCode, TData>;
118
+ /**
119
+ * Workaround for Next.js where different contexts use separate
120
+ * dependency graphs, causing multiple ORPCError constructors existing and breaking
121
+ * `instanceof` checks across contexts.
122
+ *
123
+ * This is particularly problematic with "Optimized SSR", where orpc-client
124
+ * executes in one context but is invoked from another. When an error is thrown
125
+ * in the execution context, `instanceof ORPCError` checks fail in the
126
+ * invocation context due to separate class constructors.
127
+ *
128
+ * @todo Remove this and related code if Next.js resolves the multiple dependency graph issue.
129
+ */
130
+ static [Symbol.hasInstance](instance: unknown): boolean;
118
131
  }
119
132
  type ORPCErrorJSON<TCode extends string, TData> = Pick<ORPCError<TCode, TData>, 'defined' | 'code' | 'status' | 'message' | 'data'>;
120
133
  declare function isDefinedError<T>(error: T): error is Extract<T, ORPCError<any, any>>;
@@ -147,6 +160,35 @@ type SafeResult<TOutput, TError> = [error: null, data: TOutput, isDefined: false
147
160
  */
148
161
  declare function safe<TOutput, TError = ThrowableError>(promise: ClientPromiseResult<TOutput, TError>): Promise<SafeResult<TOutput, TError>>;
149
162
  declare function resolveFriendlyClientOptions<T extends ClientContext>(options: FriendlyClientOptions<T>): ClientOptions<T>;
163
+ interface ConsumeEventIteratorOptions<T, TReturn, TError> {
164
+ /**
165
+ * Called on each event
166
+ */
167
+ onEvent: (event: T) => void;
168
+ /**
169
+ * Called once error happens
170
+ */
171
+ onError?: (error: TError) => void;
172
+ /**
173
+ * Called once event iterator is done
174
+ *
175
+ * @info If iterator is canceled, `undefined` can be passed on success
176
+ */
177
+ onSuccess?: (value: TReturn | undefined) => void;
178
+ /**
179
+ * Called once after onError or onSuccess
180
+ *
181
+ * @info If iterator is canceled, `undefined` can be passed on success
182
+ */
183
+ onFinish?: (state: OnFinishState<TReturn | undefined, TError>) => void;
184
+ }
185
+ /**
186
+ * Consumes an event iterator with lifecycle callbacks
187
+ *
188
+ * @warning If no `onError` or `onFinish` is provided, unhandled rejections will be thrown
189
+ * @return unsubscribe callback
190
+ */
191
+ declare function consumeEventIterator<T, TReturn, TError = ThrowableError>(iterator: AsyncIterator<T, TReturn> | ClientPromiseResult<AsyncIterator<T, TReturn>, TError>, options: ConsumeEventIteratorOptions<T, TReturn, TError>): () => Promise<void>;
150
192
 
151
193
  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>> : {
152
194
  [K in keyof T]: T[K] extends NestedClient<any> ? SafeClient<T[K]> : never;
@@ -164,6 +206,9 @@ type SafeClient<T extends NestedClient<any>> = T extends Client<infer UContext,
164
206
  */
165
207
  declare function createSafeClient<T extends NestedClient<any>>(client: T): SafeClient<T>;
166
208
 
209
+ declare const ORPC_CLIENT_PACKAGE_NAME = "@orpc/client";
210
+ declare const ORPC_CLIENT_PACKAGE_VERSION = "0.0.0-next.f5149a8";
211
+
167
212
  /**
168
213
  * DynamicLink provides a way to dynamically resolve and delegate calls to other ClientLinks
169
214
  * based on the request path, input, and context.
@@ -181,5 +226,5 @@ declare function mapEventIterator<TYield, TReturn, TNext, TMap = TYield | TRetur
181
226
  error: (error: unknown) => Promise<unknown>;
182
227
  }): AsyncIteratorClass<TMap, TMap, TNext>;
183
228
 
184
- export { COMMON_ORPC_ERROR_DEFS, Client, ClientContext, ClientLink, ClientOptions, ClientPromiseResult, ClientRest, DynamicLink, FriendlyClientOptions, InferClientContext, NestedClient, ORPCError, createORPCClient, createORPCErrorFromJson, createSafeClient, fallbackORPCErrorMessage, fallbackORPCErrorStatus, isDefinedError, isORPCErrorJson, isORPCErrorStatus, mapEventIterator, resolveFriendlyClientOptions, safe, toORPCError };
185
- export type { CommonORPCErrorCode, ORPCErrorCode, ORPCErrorJSON, ORPCErrorOptions, SafeClient, SafeResult, createORPCClientOptions };
229
+ 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 };
230
+ export type { CommonORPCErrorCode, ConsumeEventIteratorOptions, ORPCErrorCode, ORPCErrorJSON, ORPCErrorOptions, SafeClient, SafeResult, createORPCClientOptions };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { i as isDefinedError } from './shared/client.DHOfWE0c.mjs';
2
- export { C as COMMON_ORPC_ERROR_DEFS, O as ORPCError, d as createORPCErrorFromJson, a as fallbackORPCErrorMessage, f as fallbackORPCErrorStatus, c as isORPCErrorJson, b as isORPCErrorStatus, m as mapEventIterator, t as toORPCError } from './shared/client.DHOfWE0c.mjs';
3
- import { isTypescriptObject } from '@orpc/shared';
4
- export { EventPublisher, asyncIteratorToStream as eventIteratorToStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared';
1
+ import { preventNativeAwait, isTypescriptObject } from '@orpc/shared';
2
+ export { AsyncIteratorClass, EventPublisher, asyncIteratorToStream as eventIteratorToStream, asyncIteratorToUnproxiedDataStream as eventIteratorToUnproxiedDataStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared';
3
+ import { i as isDefinedError } from './shared/client.M38fROHr.mjs';
4
+ 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.M38fROHr.mjs';
5
+ export { m as mapEventIterator } from './shared/client.BLtwTQUg.mjs';
5
6
  export { ErrorEvent } from '@orpc/standard-server';
6
7
 
7
8
  async function safe(promise) {
@@ -32,6 +33,35 @@ function resolveFriendlyClientOptions(options) {
32
33
  // Context only optional if all fields are optional
33
34
  };
34
35
  }
36
+ function consumeEventIterator(iterator, options) {
37
+ void (async () => {
38
+ let onFinishState;
39
+ try {
40
+ const resolvedIterator = await iterator;
41
+ while (true) {
42
+ const { done, value } = await resolvedIterator.next();
43
+ if (done) {
44
+ const realValue = value;
45
+ onFinishState = [null, realValue, true];
46
+ options.onSuccess?.(realValue);
47
+ break;
48
+ }
49
+ options.onEvent(value);
50
+ }
51
+ } catch (error) {
52
+ onFinishState = [error, void 0, false];
53
+ if (!options.onError && !options.onFinish) {
54
+ throw error;
55
+ }
56
+ options.onError?.(error);
57
+ } finally {
58
+ options.onFinish?.(onFinishState);
59
+ }
60
+ })();
61
+ return async () => {
62
+ await (await iterator)?.return?.();
63
+ };
64
+ }
35
65
 
36
66
  function createORPCClient(link, options = {}) {
37
67
  const path = options.path ?? [];
@@ -49,7 +79,7 @@ function createORPCClient(link, options = {}) {
49
79
  });
50
80
  }
51
81
  });
52
- return recursive;
82
+ return preventNativeAwait(recursive);
53
83
  }
54
84
 
55
85
  function createSafeClient(client) {
@@ -79,4 +109,4 @@ class DynamicLink {
79
109
  }
80
110
  }
81
111
 
82
- export { DynamicLink, createORPCClient, createSafeClient, isDefinedError, resolveFriendlyClientOptions, safe };
112
+ export { DynamicLink, consumeEventIterator, createORPCClient, createSafeClient, isDefinedError, resolveFriendlyClientOptions, safe };
@@ -1,8 +1,8 @@
1
1
  import { Value, Promisable } from '@orpc/shared';
2
- import { StandardHeaders, StandardRequest } from '@orpc/standard-server';
2
+ import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server';
3
3
  import { BatchResponseMode } from '@orpc/standard-server/batch';
4
- import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.C4VxIexA.mjs';
5
- import { b as ClientContext } from '../shared/client.BOYsZIRq.mjs';
4
+ import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.CPgZaUox.mjs';
5
+ import { b as ClientContext } from '../shared/client.BH1AYT_p.mjs';
6
6
 
7
7
  interface BatchLinkPluginGroup<T extends ClientContext> {
8
8
  condition(options: StandardLinkClientInterceptorOptions<T>): boolean;
@@ -157,10 +157,57 @@ declare class ClientRetryPlugin<T extends ClientRetryPluginContext> implements S
157
157
  private readonly defaultRetryDelay;
158
158
  private readonly defaultShouldRetry;
159
159
  private readonly defaultOnRetry;
160
+ order: number;
160
161
  constructor(options?: ClientRetryPluginOptions);
161
162
  init(options: StandardLinkOptions<T>): void;
162
163
  }
163
164
 
165
+ interface RetryAfterPluginOptions<T extends ClientContext> {
166
+ /**
167
+ * Override condition to determine whether to retry or not.
168
+ *
169
+ * @default ((response) => response.status === 429 || response.status === 503)
170
+ */
171
+ condition?: Value<boolean, [
172
+ response: StandardLazyResponse,
173
+ options: StandardLinkClientInterceptorOptions<T>
174
+ ]>;
175
+ /**
176
+ * Maximum attempts before giving up retries.
177
+ *
178
+ * @default 3
179
+ */
180
+ maxAttempts?: Value<number, [
181
+ response: StandardLazyResponse,
182
+ options: StandardLinkClientInterceptorOptions<T>
183
+ ]>;
184
+ /**
185
+ * Maximum timeout in milliseconds to wait before giving up retries.
186
+ *
187
+ * @default 5 * 60 * 1000 (5 minutes)
188
+ */
189
+ timeout?: Value<number, [
190
+ response: StandardLazyResponse,
191
+ options: StandardLinkClientInterceptorOptions<T>
192
+ ]>;
193
+ }
194
+ /**
195
+ * The Retry After Plugin automatically retries requests based on server `Retry-After` headers.
196
+ * This is particularly useful for handling rate limiting and temporary server unavailability.
197
+ *
198
+ * @see {@link https://orpc.unnoq.com/docs/plugins/retry-after Retry After Plugin Docs}
199
+ */
200
+ declare class RetryAfterPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
201
+ private readonly condition;
202
+ private readonly maxAttempts;
203
+ private readonly timeout;
204
+ order: number;
205
+ constructor(options?: RetryAfterPluginOptions<T>);
206
+ init(options: StandardLinkOptions<T>): void;
207
+ private parseRetryAfterHeader;
208
+ private delayExecution;
209
+ }
210
+
164
211
  interface SimpleCsrfProtectionLinkPluginOptions<T extends ClientContext> {
165
212
  /**
166
213
  * The name of the header to check.
@@ -198,5 +245,5 @@ declare class SimpleCsrfProtectionLinkPlugin<T extends ClientContext> implements
198
245
  init(options: StandardLinkOptions<T>): void;
199
246
  }
200
247
 
201
- export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, SimpleCsrfProtectionLinkPlugin };
202
- export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, SimpleCsrfProtectionLinkPluginOptions };
248
+ export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin };
249
+ export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, RetryAfterPluginOptions, SimpleCsrfProtectionLinkPluginOptions };
@@ -1,8 +1,8 @@
1
1
  import { Value, Promisable } from '@orpc/shared';
2
- import { StandardHeaders, StandardRequest } from '@orpc/standard-server';
2
+ import { StandardHeaders, StandardRequest, StandardLazyResponse } from '@orpc/standard-server';
3
3
  import { BatchResponseMode } from '@orpc/standard-server/batch';
4
- import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.CXXEPIbK.js';
5
- import { b as ClientContext } from '../shared/client.BOYsZIRq.js';
4
+ import { S as StandardLinkClientInterceptorOptions, a as StandardLinkPlugin, b as StandardLinkOptions, c as StandardLinkInterceptorOptions } from '../shared/client.De8SW4Kw.js';
5
+ import { b as ClientContext } from '../shared/client.BH1AYT_p.js';
6
6
 
7
7
  interface BatchLinkPluginGroup<T extends ClientContext> {
8
8
  condition(options: StandardLinkClientInterceptorOptions<T>): boolean;
@@ -157,10 +157,57 @@ declare class ClientRetryPlugin<T extends ClientRetryPluginContext> implements S
157
157
  private readonly defaultRetryDelay;
158
158
  private readonly defaultShouldRetry;
159
159
  private readonly defaultOnRetry;
160
+ order: number;
160
161
  constructor(options?: ClientRetryPluginOptions);
161
162
  init(options: StandardLinkOptions<T>): void;
162
163
  }
163
164
 
165
+ interface RetryAfterPluginOptions<T extends ClientContext> {
166
+ /**
167
+ * Override condition to determine whether to retry or not.
168
+ *
169
+ * @default ((response) => response.status === 429 || response.status === 503)
170
+ */
171
+ condition?: Value<boolean, [
172
+ response: StandardLazyResponse,
173
+ options: StandardLinkClientInterceptorOptions<T>
174
+ ]>;
175
+ /**
176
+ * Maximum attempts before giving up retries.
177
+ *
178
+ * @default 3
179
+ */
180
+ maxAttempts?: Value<number, [
181
+ response: StandardLazyResponse,
182
+ options: StandardLinkClientInterceptorOptions<T>
183
+ ]>;
184
+ /**
185
+ * Maximum timeout in milliseconds to wait before giving up retries.
186
+ *
187
+ * @default 5 * 60 * 1000 (5 minutes)
188
+ */
189
+ timeout?: Value<number, [
190
+ response: StandardLazyResponse,
191
+ options: StandardLinkClientInterceptorOptions<T>
192
+ ]>;
193
+ }
194
+ /**
195
+ * The Retry After Plugin automatically retries requests based on server `Retry-After` headers.
196
+ * This is particularly useful for handling rate limiting and temporary server unavailability.
197
+ *
198
+ * @see {@link https://orpc.unnoq.com/docs/plugins/retry-after Retry After Plugin Docs}
199
+ */
200
+ declare class RetryAfterPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
201
+ private readonly condition;
202
+ private readonly maxAttempts;
203
+ private readonly timeout;
204
+ order: number;
205
+ constructor(options?: RetryAfterPluginOptions<T>);
206
+ init(options: StandardLinkOptions<T>): void;
207
+ private parseRetryAfterHeader;
208
+ private delayExecution;
209
+ }
210
+
164
211
  interface SimpleCsrfProtectionLinkPluginOptions<T extends ClientContext> {
165
212
  /**
166
213
  * The name of the header to check.
@@ -198,5 +245,5 @@ declare class SimpleCsrfProtectionLinkPlugin<T extends ClientContext> implements
198
245
  init(options: StandardLinkOptions<T>): void;
199
246
  }
200
247
 
201
- export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, SimpleCsrfProtectionLinkPlugin };
202
- export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, SimpleCsrfProtectionLinkPluginOptions };
248
+ export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin };
249
+ export type { BatchLinkPluginGroup, BatchLinkPluginOptions, ClientRetryPluginAttemptOptions, ClientRetryPluginContext, ClientRetryPluginOptions, DedupeRequestsPluginGroup, DedupeRequestsPluginOptions, RetryAfterPluginOptions, SimpleCsrfProtectionLinkPluginOptions };
@@ -1,6 +1,7 @@
1
- import { isAsyncIteratorObject, defer, value, splitInHalf, toArray, stringifyJSON } from '@orpc/shared';
1
+ import { isAsyncIteratorObject, defer, value, splitInHalf, toArray, stringifyJSON, overlayProxy, AsyncIteratorClass } from '@orpc/shared';
2
2
  import { toBatchRequest, parseBatchResponse, toBatchAbortSignal } from '@orpc/standard-server/batch';
3
- import { replicateStandardLazyResponse, getEventMeta } from '@orpc/standard-server';
3
+ import { replicateStandardLazyResponse, getEventMeta, flattenHeader } from '@orpc/standard-server';
4
+ import { C as COMMON_ORPC_ERROR_DEFS } from '../shared/client.M38fROHr.mjs';
4
5
 
5
6
  class BatchLinkPlugin {
6
7
  groups;
@@ -64,7 +65,7 @@ class BatchLinkPlugin {
64
65
  });
65
66
  });
66
67
  options.clientInterceptors.push((options2) => {
67
- if (this.exclude(options2) || options2.request.body instanceof Blob || options2.request.body instanceof FormData || isAsyncIteratorObject(options2.request.body)) {
68
+ if (this.exclude(options2) || options2.request.body instanceof Blob || options2.request.body instanceof FormData || isAsyncIteratorObject(options2.request.body) || options2.request.signal?.aborted) {
68
69
  return options2.next();
69
70
  }
70
71
  const group = this.groups.find((group2) => group2.condition(options2));
@@ -130,16 +131,27 @@ class BatchLinkPlugin {
130
131
  return;
131
132
  }
132
133
  const mode = value(this.mode, options);
133
- const lazyResponse = await options[0].next({
134
- request: { ...batchRequest, headers: { ...batchRequest.headers, "x-orpc-batch": mode } },
135
- signal: batchRequest.signal,
136
- context: group.context,
137
- input: group.input,
138
- path: toArray(group.path)
139
- });
140
- const parsed = parseBatchResponse({ ...lazyResponse, body: await lazyResponse.body() });
141
- for await (const item of parsed) {
142
- batchItems[item.index]?.[1]({ ...item, body: () => Promise.resolve(item.body) });
134
+ try {
135
+ const lazyResponse = await options[0].next({
136
+ request: { ...batchRequest, headers: { ...batchRequest.headers, "x-orpc-batch": mode } },
137
+ signal: batchRequest.signal,
138
+ context: group.context,
139
+ input: group.input,
140
+ path: toArray(group.path)
141
+ });
142
+ const parsed = parseBatchResponse({ ...lazyResponse, body: await lazyResponse.body() });
143
+ for await (const item of parsed) {
144
+ batchItems[item.index]?.[1]({ ...item, body: () => Promise.resolve(item.body) });
145
+ }
146
+ } catch (err) {
147
+ if (batchRequest.signal?.aborted && batchRequest.signal.reason === err) {
148
+ for (const [{ signal }, , reject] of batchItems) {
149
+ if (signal?.aborted) {
150
+ reject(signal.reason);
151
+ }
152
+ }
153
+ }
154
+ throw err;
143
155
  }
144
156
  throw new Error("Something went wrong make batch response not contains enough responses. This can be a bug please report it.");
145
157
  } catch (error) {
@@ -252,6 +264,7 @@ class ClientRetryPlugin {
252
264
  defaultRetryDelay;
253
265
  defaultShouldRetry;
254
266
  defaultOnRetry;
267
+ order = 18e5;
255
268
  constructor(options = {}) {
256
269
  this.defaultRetry = options.default?.retry ?? 0;
257
270
  this.defaultRetryDelay = options.default?.retryDelay ?? ((o) => o.lastEventRetry ?? 2e3);
@@ -306,7 +319,7 @@ class ClientRetryPlugin {
306
319
  return await interceptorOptions.next(updatedInterceptorOptions);
307
320
  } catch (error) {
308
321
  currentError = { error };
309
- if (updatedInterceptorOptions.signal?.aborted === true) {
322
+ if (updatedInterceptorOptions.signal?.aborted) {
310
323
  throw error;
311
324
  }
312
325
  } finally {
@@ -319,19 +332,17 @@ class ClientRetryPlugin {
319
332
  if (!isAsyncIteratorObject(output)) {
320
333
  return output;
321
334
  }
322
- return async function* () {
323
- let current = output;
324
- try {
335
+ let current = output;
336
+ let isIteratorAborted = false;
337
+ return overlayProxy(() => current, new AsyncIteratorClass(
338
+ async () => {
325
339
  while (true) {
326
340
  try {
327
341
  const item = await current.next();
328
342
  const meta = getEventMeta(item.value);
329
343
  lastEventId = meta?.id ?? lastEventId;
330
344
  lastEventRetry = meta?.retry ?? lastEventRetry;
331
- if (item.done) {
332
- return item.value;
333
- }
334
- yield item.value;
345
+ return item;
335
346
  } catch (error) {
336
347
  const meta = getEventMeta(error);
337
348
  lastEventId = meta?.id ?? lastEventId;
@@ -343,12 +354,97 @@ class ClientRetryPlugin {
343
354
  );
344
355
  }
345
356
  current = maybeEventIterator;
357
+ if (isIteratorAborted) {
358
+ await current.return?.();
359
+ throw error;
360
+ }
346
361
  }
347
362
  }
348
- } finally {
349
- await current.return?.();
363
+ },
364
+ async (reason) => {
365
+ isIteratorAborted = true;
366
+ if (reason !== "next") {
367
+ await current.return?.();
368
+ }
350
369
  }
351
- }();
370
+ ));
371
+ });
372
+ }
373
+ }
374
+
375
+ class RetryAfterPlugin {
376
+ condition;
377
+ maxAttempts;
378
+ timeout;
379
+ order = 19e5;
380
+ constructor(options = {}) {
381
+ this.condition = options.condition ?? ((response) => response.status === COMMON_ORPC_ERROR_DEFS.TOO_MANY_REQUESTS.status || response.status === COMMON_ORPC_ERROR_DEFS.SERVICE_UNAVAILABLE.status);
382
+ this.maxAttempts = options.maxAttempts ?? 3;
383
+ this.timeout = options.timeout ?? 5 * 60 * 1e3;
384
+ }
385
+ init(options) {
386
+ options.clientInterceptors ??= [];
387
+ options.clientInterceptors.push(async (interceptorOptions) => {
388
+ const startTime = Date.now();
389
+ let attemptCount = 0;
390
+ while (true) {
391
+ attemptCount++;
392
+ const response = await interceptorOptions.next();
393
+ if (!value(this.condition, response, interceptorOptions)) {
394
+ return response;
395
+ }
396
+ const retryAfterHeader = flattenHeader(response.headers["retry-after"]);
397
+ const retryAfterMs = this.parseRetryAfterHeader(retryAfterHeader);
398
+ if (retryAfterMs === void 0) {
399
+ return response;
400
+ }
401
+ if (attemptCount >= value(this.maxAttempts, response, interceptorOptions)) {
402
+ return response;
403
+ }
404
+ const timeoutMs = value(this.timeout, response, interceptorOptions);
405
+ const elapsedTime = Date.now() - startTime;
406
+ if (elapsedTime + retryAfterMs > timeoutMs) {
407
+ return response;
408
+ }
409
+ await this.delayExecution(retryAfterMs, interceptorOptions.signal);
410
+ if (interceptorOptions.signal?.aborted) {
411
+ return response;
412
+ }
413
+ }
414
+ });
415
+ }
416
+ parseRetryAfterHeader(value2) {
417
+ value2 = value2?.trim();
418
+ if (!value2) {
419
+ return void 0;
420
+ }
421
+ const seconds = Number(value2);
422
+ if (Number.isFinite(seconds)) {
423
+ return Math.max(0, seconds * 1e3);
424
+ }
425
+ const retryDate = Date.parse(value2);
426
+ if (!Number.isNaN(retryDate)) {
427
+ return Math.max(0, retryDate - Date.now());
428
+ }
429
+ return void 0;
430
+ }
431
+ delayExecution(ms, signal) {
432
+ return new Promise((resolve) => {
433
+ if (signal?.aborted) {
434
+ resolve();
435
+ return;
436
+ }
437
+ let timeout;
438
+ const onAbort = () => {
439
+ clearTimeout(timeout);
440
+ timeout = void 0;
441
+ resolve();
442
+ };
443
+ signal?.addEventListener("abort", onAbort, { once: true });
444
+ timeout = setTimeout(() => {
445
+ signal?.removeEventListener("abort", onAbort);
446
+ resolve();
447
+ }, ms);
352
448
  });
353
449
  }
354
450
  }
@@ -386,4 +482,4 @@ class SimpleCsrfProtectionLinkPlugin {
386
482
  }
387
483
  }
388
484
 
389
- export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, SimpleCsrfProtectionLinkPlugin };
485
+ export { BatchLinkPlugin, ClientRetryPlugin, ClientRetryPluginInvalidEventIteratorRetryResponse, DedupeRequestsPlugin, RetryAfterPlugin, SimpleCsrfProtectionLinkPlugin };
@@ -0,0 +1,83 @@
1
+ import { PromiseWithError } from '@orpc/shared';
2
+
3
+ type HTTPPath = `/${string}`;
4
+ type HTTPMethod = 'HEAD' | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
5
+ type ClientContext = Record<PropertyKey, any>;
6
+ interface ClientOptions<T extends ClientContext> {
7
+ signal?: AbortSignal;
8
+ lastEventId?: string | undefined;
9
+ context: T;
10
+ }
11
+ type FriendlyClientOptions<T extends ClientContext> = Omit<ClientOptions<T>, 'context'> & (Record<never, never> extends T ? {
12
+ context?: T;
13
+ } : {
14
+ context: T;
15
+ });
16
+ 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>];
17
+ type ClientPromiseResult<TOutput, TError> = PromiseWithError<TOutput, TError>;
18
+ interface Client<TClientContext extends ClientContext, TInput, TOutput, TError> {
19
+ (...rest: ClientRest<TClientContext, TInput>): ClientPromiseResult<TOutput, TError>;
20
+ }
21
+ type NestedClient<TClientContext extends ClientContext> = Client<TClientContext, any, any, any> | {
22
+ [k: string]: NestedClient<TClientContext>;
23
+ };
24
+ type InferClientContext<T extends NestedClient<any>> = T extends NestedClient<infer U> ? U : never;
25
+ interface ClientLink<TClientContext extends ClientContext> {
26
+ call: (path: readonly string[], input: unknown, options: ClientOptions<TClientContext>) => Promise<unknown>;
27
+ }
28
+ /**
29
+ * Recursively infers the **input types** from a client.
30
+ *
31
+ * Produces a nested map where each endpoint's input type is preserved.
32
+ */
33
+ type InferClientInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U : {
34
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientInputs<T[K]> : never;
35
+ };
36
+ /**
37
+ * Recursively infers the **body input types** from a client.
38
+ *
39
+ * If an endpoint's input includes `{ body: ... }`, only the `body` portion is extracted.
40
+ * Produces a nested map of body input types.
41
+ */
42
+ type InferClientBodyInputs<T extends NestedClient<any>> = T extends Client<any, infer U, any, any> ? U extends {
43
+ body: infer UBody;
44
+ } ? UBody : U : {
45
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyInputs<T[K]> : never;
46
+ };
47
+ /**
48
+ * Recursively infers the **output types** from a client.
49
+ *
50
+ * Produces a nested map where each endpoint's output type is preserved.
51
+ */
52
+ type InferClientOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U : {
53
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientOutputs<T[K]> : never;
54
+ };
55
+ /**
56
+ * Recursively infers the **body output types** from a client.
57
+ *
58
+ * If an endpoint's output includes `{ body: ... }`, only the `body` portion is extracted.
59
+ * Produces a nested map of body output types.
60
+ */
61
+ type InferClientBodyOutputs<T extends NestedClient<any>> = T extends Client<any, any, infer U, any> ? U extends {
62
+ body: infer UBody;
63
+ } ? UBody : U : {
64
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientBodyOutputs<T[K]> : never;
65
+ };
66
+ /**
67
+ * Recursively infers the **error types** from a client when you use [type-safe errors](https://orpc.unnoq.com/docs/error-handling#type‐safe-error-handling).
68
+ *
69
+ * Produces a nested map where each endpoint's error type is preserved.
70
+ */
71
+ type InferClientErrors<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : {
72
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrors<T[K]> : never;
73
+ };
74
+ /**
75
+ * Recursively infers a **union of all error types** from a client when you use [type-safe errors](https://orpc.unnoq.com/docs/error-handling#type‐safe-error-handling).
76
+ *
77
+ * Useful when you want to handle all possible errors from any endpoint at once.
78
+ */
79
+ type InferClientErrorUnion<T extends NestedClient<any>> = T extends Client<any, any, any, infer U> ? U : {
80
+ [K in keyof T]: T[K] extends NestedClient<any> ? InferClientErrorUnion<T[K]> : never;
81
+ }[keyof T];
82
+
83
+ 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 };