@orpc/server 0.0.0-next.68241f5 → 0.0.0-next.683f2ee

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 (65) hide show
  1. package/README.md +131 -20
  2. package/dist/adapters/aws-lambda/index.d.mts +46 -0
  3. package/dist/adapters/aws-lambda/index.d.ts +46 -0
  4. package/dist/adapters/aws-lambda/index.mjs +40 -0
  5. package/dist/adapters/bun-ws/index.d.mts +36 -0
  6. package/dist/adapters/bun-ws/index.d.ts +36 -0
  7. package/dist/adapters/bun-ws/index.mjs +47 -0
  8. package/dist/adapters/crossws/index.d.mts +33 -0
  9. package/dist/adapters/crossws/index.d.ts +33 -0
  10. package/dist/adapters/crossws/index.mjs +45 -0
  11. package/dist/adapters/fastify/index.d.mts +53 -0
  12. package/dist/adapters/fastify/index.d.ts +53 -0
  13. package/dist/adapters/fastify/index.mjs +52 -0
  14. package/dist/adapters/fetch/index.d.mts +66 -12
  15. package/dist/adapters/fetch/index.d.ts +66 -12
  16. package/dist/adapters/fetch/index.mjs +81 -7
  17. package/dist/adapters/message-port/index.d.mts +57 -0
  18. package/dist/adapters/message-port/index.d.ts +57 -0
  19. package/dist/adapters/message-port/index.mjs +55 -0
  20. package/dist/adapters/node/index.d.mts +43 -13
  21. package/dist/adapters/node/index.d.ts +43 -13
  22. package/dist/adapters/node/index.mjs +76 -16
  23. package/dist/adapters/standard/index.d.mts +8 -13
  24. package/dist/adapters/standard/index.d.ts +8 -13
  25. package/dist/adapters/standard/index.mjs +5 -5
  26. package/dist/adapters/standard-peer/index.d.mts +18 -0
  27. package/dist/adapters/standard-peer/index.d.ts +18 -0
  28. package/dist/adapters/standard-peer/index.mjs +21 -0
  29. package/dist/adapters/websocket/index.d.mts +56 -0
  30. package/dist/adapters/websocket/index.d.ts +56 -0
  31. package/dist/adapters/websocket/index.mjs +67 -0
  32. package/dist/adapters/ws/index.d.mts +31 -0
  33. package/dist/adapters/ws/index.d.ts +31 -0
  34. package/dist/adapters/ws/index.mjs +37 -0
  35. package/dist/helpers/index.d.mts +149 -0
  36. package/dist/helpers/index.d.ts +149 -0
  37. package/dist/helpers/index.mjs +198 -0
  38. package/dist/hibernation/index.d.mts +44 -0
  39. package/dist/hibernation/index.d.ts +44 -0
  40. package/dist/hibernation/index.mjs +65 -0
  41. package/dist/index.d.mts +117 -143
  42. package/dist/index.d.ts +117 -143
  43. package/dist/index.mjs +56 -35
  44. package/dist/plugins/index.d.mts +67 -19
  45. package/dist/plugins/index.d.ts +67 -19
  46. package/dist/plugins/index.mjs +142 -66
  47. package/dist/shared/{server.D5fBlF9j.d.ts → server.7cEtMB30.d.ts} +11 -11
  48. package/dist/shared/server.B8gYOD5g.d.mts +12 -0
  49. package/dist/shared/{server.DG7Tamti.mjs → server.BOmqcs4W.mjs} +129 -75
  50. package/dist/shared/{server.DY7OKEoj.d.mts → server.BqadksTP.d.mts} +11 -11
  51. package/dist/shared/server.C8_sRzQB.d.mts +42 -0
  52. package/dist/shared/server.ChUyt5-i.d.mts +32 -0
  53. package/dist/shared/server.ChyoA9XY.d.ts +42 -0
  54. package/dist/shared/{server.BVwwTHyO.mjs → server.DZ5BIITo.mjs} +1 -1
  55. package/dist/shared/{server.qf03T-Xn.mjs → server.Dz5G6M6M.mjs} +87 -51
  56. package/dist/shared/server.EfTOZ2Q7.d.ts +12 -0
  57. package/dist/shared/{server.BW-nUGgA.mjs → server.TEVCLCFC.mjs} +3 -0
  58. package/dist/shared/{server.DPWk5pjW.d.mts → server.qKsRrdxW.d.mts} +18 -17
  59. package/dist/shared/{server.DPWk5pjW.d.ts → server.qKsRrdxW.d.ts} +18 -17
  60. package/dist/shared/server.yoEB3Fx4.d.ts +32 -0
  61. package/package.json +85 -11
  62. package/dist/shared/server.CN0534_m.d.mts +0 -18
  63. package/dist/shared/server.CjlA3NKP.d.ts +0 -10
  64. package/dist/shared/server.CuD15qZB.d.ts +0 -18
  65. package/dist/shared/server.DjgtLwKi.d.mts +0 -10
@@ -1,7 +1,9 @@
1
- import { value, isAsyncIteratorObject } from '@orpc/shared';
1
+ import { runWithSpan, value, setSpanError, isAsyncIteratorObject, AsyncIteratorClass, clone } from '@orpc/shared';
2
+ import { flattenHeader } from '@orpc/standard-server';
2
3
  import { parseBatchRequest, toBatchResponse } from '@orpc/standard-server/batch';
4
+ import { toFetchHeaders } from '@orpc/standard-server-fetch';
3
5
  import { ORPCError } from '@orpc/client';
4
- export { S as StrictGetMethodPlugin } from '../shared/server.BW-nUGgA.mjs';
6
+ export { S as StrictGetMethodPlugin } from '../shared/server.TEVCLCFC.mjs';
5
7
  import '@orpc/contract';
6
8
 
7
9
  class BatchHandlerPlugin {
@@ -25,69 +27,82 @@ class BatchHandlerPlugin {
25
27
  init(options) {
26
28
  options.rootInterceptors ??= [];
27
29
  options.rootInterceptors.unshift(async (options2) => {
28
- if (options2.request.headers["x-orpc-batch"] !== "1") {
30
+ const xHeader = flattenHeader(options2.request.headers["x-orpc-batch"]);
31
+ if (xHeader === void 0) {
29
32
  return options2.next();
30
33
  }
31
34
  let isParsing = false;
32
35
  try {
33
- isParsing = true;
34
- const parsed = parseBatchRequest({ ...options2.request, body: await options2.request.body() });
35
- isParsing = false;
36
- const maxSize = await value(this.maxSize, options2);
37
- if (parsed.length > maxSize) {
38
- return {
39
- matched: true,
40
- response: {
41
- status: 413,
42
- headers: {},
43
- body: "Batch request size exceeds the maximum allowed size"
44
- }
45
- };
46
- }
47
- const responses = parsed.map(
48
- (request, index) => {
49
- const mapped = this.mapRequestItem(request, options2);
50
- return options2.next({ ...options2, request: { ...mapped, body: () => Promise.resolve(mapped.body) } }).then(({ response: response2, matched }) => {
51
- if (matched) {
52
- if (response2.body instanceof Blob || response2.body instanceof FormData || isAsyncIteratorObject(response2.body)) {
53
- return {
54
- index,
55
- status: 500,
56
- headers: {},
57
- body: "Batch responses do not support file/blob, or event-iterator. Please call this procedure separately outside of the batch request."
58
- };
59
- }
60
- return { ...response2, index };
36
+ return await runWithSpan({ name: "handle_batch_request" }, async (span) => {
37
+ const mode = xHeader === "buffered" ? "buffered" : "streaming";
38
+ isParsing = true;
39
+ const parsed = parseBatchRequest({ ...options2.request, body: await options2.request.body() });
40
+ isParsing = false;
41
+ span?.setAttribute("batch.mode", mode);
42
+ span?.setAttribute("batch.size", parsed.length);
43
+ const maxSize = await value(this.maxSize, options2);
44
+ if (parsed.length > maxSize) {
45
+ const message = "Batch request size exceeds the maximum allowed size";
46
+ setSpanError(span, message);
47
+ return {
48
+ matched: true,
49
+ response: {
50
+ status: 413,
51
+ headers: {},
52
+ body: message
61
53
  }
62
- return { index, status: 404, headers: {}, body: "No procedure matched" };
63
- }).catch(() => {
64
- return { index, status: 500, headers: {}, body: "Internal server error" };
65
- });
54
+ };
66
55
  }
67
- );
68
- await Promise.race(responses);
69
- const status = await value(this.successStatus, responses, options2);
70
- const headers = await value(this.headers, responses, options2);
71
- const response = toBatchResponse({
72
- status,
73
- headers,
74
- body: async function* () {
75
- const promises = [...responses];
76
- while (true) {
77
- const handling = promises.filter((p) => p !== void 0);
78
- if (handling.length === 0) {
79
- return;
80
- }
81
- const result = await Promise.race(handling);
82
- promises[result.index] = void 0;
83
- yield result;
56
+ const responses = parsed.map(
57
+ (request, index) => {
58
+ const mapped = this.mapRequestItem(request, options2);
59
+ return options2.next({ ...options2, request: { ...mapped, body: () => Promise.resolve(mapped.body) } }).then(({ response: response2, matched }) => {
60
+ span?.addEvent(`response.${index}.${matched ? "success" : "not_matched"}`);
61
+ if (matched) {
62
+ if (response2.body instanceof Blob || response2.body instanceof FormData || isAsyncIteratorObject(response2.body)) {
63
+ return {
64
+ index,
65
+ status: 500,
66
+ headers: {},
67
+ body: "Batch responses do not support file/blob, or event-iterator. Please call this procedure separately outside of the batch request."
68
+ };
69
+ }
70
+ return { ...response2, index };
71
+ }
72
+ return { index, status: 404, headers: {}, body: "No procedure matched" };
73
+ }).catch((err) => {
74
+ Promise.reject(err);
75
+ return { index, status: 500, headers: {}, body: "Internal server error" };
76
+ });
84
77
  }
85
- }()
78
+ );
79
+ await Promise.race(responses);
80
+ const status = await value(this.successStatus, responses, options2);
81
+ const headers = await value(this.headers, responses, options2);
82
+ const promises = [...responses];
83
+ const response = await toBatchResponse({
84
+ status,
85
+ headers,
86
+ mode,
87
+ body: new AsyncIteratorClass(
88
+ async () => {
89
+ const handling = promises.filter((p) => p !== void 0);
90
+ if (handling.length <= 0) {
91
+ return { done: true, value: void 0 };
92
+ }
93
+ const value2 = await Promise.race(handling);
94
+ promises[value2.index] = void 0;
95
+ return { done: false, value: value2 };
96
+ },
97
+ async () => {
98
+ }
99
+ )
100
+ });
101
+ return {
102
+ matched: true,
103
+ response
104
+ };
86
105
  });
87
- return {
88
- matched: true,
89
- response
90
- };
91
106
  } catch (cause) {
92
107
  if (isParsing) {
93
108
  return {
@@ -123,13 +138,11 @@ class CORSPlugin {
123
138
  resHeaders["access-control-max-age"] = this.options.maxAge.toString();
124
139
  }
125
140
  if (this.options.allowMethods?.length) {
126
- resHeaders["access-control-allow-methods"] = this.options.allowMethods.join(",");
141
+ resHeaders["access-control-allow-methods"] = flattenHeader(this.options.allowMethods);
127
142
  }
128
143
  const allowHeaders = this.options.allowHeaders ?? interceptorOptions.request.headers["access-control-request-headers"];
129
- if (Array.isArray(allowHeaders) && allowHeaders.length) {
130
- resHeaders["access-control-allow-headers"] = allowHeaders.join(",");
131
- } else if (typeof allowHeaders === "string") {
132
- resHeaders["access-control-allow-headers"] = allowHeaders;
144
+ if (typeof allowHeaders === "string" || allowHeaders?.length) {
145
+ resHeaders["access-control-allow-headers"] = flattenHeader(allowHeaders);
133
146
  }
134
147
  return {
135
148
  matched: true,
@@ -147,7 +160,7 @@ class CORSPlugin {
147
160
  if (!result.matched) {
148
161
  return result;
149
162
  }
150
- const origin = Array.isArray(interceptorOptions.request.headers.origin) ? interceptorOptions.request.headers.origin.join(",") : interceptorOptions.request.headers.origin || "";
163
+ const origin = flattenHeader(interceptorOptions.request.headers.origin) ?? "";
151
164
  const allowedOrigin = await value(this.options.origin, origin, interceptorOptions);
152
165
  const allowedOriginArr = Array.isArray(allowedOrigin) ? allowedOrigin : [allowedOrigin];
153
166
  if (allowedOriginArr.includes("*")) {
@@ -169,13 +182,29 @@ class CORSPlugin {
169
182
  result.response.headers["access-control-allow-credentials"] = "true";
170
183
  }
171
184
  if (this.options.exposeHeaders?.length) {
172
- result.response.headers["access-control-expose-headers"] = this.options.exposeHeaders.join(",");
185
+ result.response.headers["access-control-expose-headers"] = flattenHeader(this.options.exposeHeaders);
173
186
  }
174
187
  return result;
175
188
  });
176
189
  }
177
190
  }
178
191
 
192
+ class RequestHeadersPlugin {
193
+ init(options) {
194
+ options.rootInterceptors ??= [];
195
+ options.rootInterceptors.push((interceptorOptions) => {
196
+ const reqHeaders = interceptorOptions.context.reqHeaders ?? toFetchHeaders(interceptorOptions.request.headers);
197
+ return interceptorOptions.next({
198
+ ...interceptorOptions,
199
+ context: {
200
+ ...interceptorOptions.context,
201
+ reqHeaders
202
+ }
203
+ });
204
+ });
205
+ }
206
+ }
207
+
179
208
  class ResponseHeadersPlugin {
180
209
  init(options) {
181
210
  options.rootInterceptors ??= [];
@@ -191,7 +220,7 @@ class ResponseHeadersPlugin {
191
220
  if (!result.matched) {
192
221
  return result;
193
222
  }
194
- const responseHeaders = result.response.headers;
223
+ const responseHeaders = clone(result.response.headers);
195
224
  for (const [key, value] of resHeaders) {
196
225
  if (Array.isArray(responseHeaders[key])) {
197
226
  responseHeaders[key].push(value);
@@ -201,8 +230,55 @@ class ResponseHeadersPlugin {
201
230
  responseHeaders[key] = value;
202
231
  }
203
232
  }
233
+ return {
234
+ ...result,
235
+ response: {
236
+ ...result.response,
237
+ headers: responseHeaders
238
+ }
239
+ };
240
+ });
241
+ }
242
+ }
243
+
244
+ class experimental_RethrowHandlerPlugin {
245
+ filter;
246
+ CONTEXT_SYMBOL = Symbol("ORPC_RETHROW_HANDLER_PLUGIN_CONTEXT");
247
+ constructor(options) {
248
+ this.filter = options.filter;
249
+ }
250
+ init(options) {
251
+ options.rootInterceptors ??= [];
252
+ options.interceptors ??= [];
253
+ options.rootInterceptors.push(async (options2) => {
254
+ const pluginContext = {};
255
+ const result = await options2.next({
256
+ ...options2,
257
+ context: {
258
+ ...options2.context,
259
+ [this.CONTEXT_SYMBOL]: pluginContext
260
+ }
261
+ });
262
+ if (pluginContext.error) {
263
+ throw pluginContext.error.value;
264
+ }
204
265
  return result;
205
266
  });
267
+ options.interceptors.unshift(async (options2) => {
268
+ const pluginContext = options2.context[this.CONTEXT_SYMBOL];
269
+ if (!pluginContext) {
270
+ throw new TypeError("[RethrowHandlerPlugin] Rethrow handler context has been corrupted or modified by another plugin or interceptor");
271
+ }
272
+ try {
273
+ return await options2.next();
274
+ } catch (error) {
275
+ if (value(this.filter, error, options2)) {
276
+ pluginContext.error = { value: error };
277
+ return { matched: false, response: void 0 };
278
+ }
279
+ throw error;
280
+ }
281
+ });
206
282
  }
207
283
  }
208
284
 
@@ -249,4 +325,4 @@ class SimpleCsrfProtectionHandlerPlugin {
249
325
  }
250
326
  }
251
327
 
252
- export { BatchHandlerPlugin, CORSPlugin, ResponseHeadersPlugin, SimpleCsrfProtectionHandlerPlugin };
328
+ export { BatchHandlerPlugin, CORSPlugin, RequestHeadersPlugin, ResponseHeadersPlugin, SimpleCsrfProtectionHandlerPlugin, experimental_RethrowHandlerPlugin };
@@ -1,17 +1,17 @@
1
1
  import { HTTPPath, ORPCError } from '@orpc/client';
2
- import { Meta, InferSchemaOutput, AnySchema, ErrorFromErrorMap } from '@orpc/contract';
3
- import { Interceptor, ThrowableError } from '@orpc/shared';
2
+ import { Meta } from '@orpc/contract';
3
+ import { Interceptor } from '@orpc/shared';
4
4
  import { StandardResponse, StandardLazyRequest } from '@orpc/standard-server';
5
- import { C as Context, f as AnyRouter, h as AnyProcedure, F as ProcedureClientInterceptorOptions, R as Router } from './server.DPWk5pjW.js';
5
+ import { C as Context, R as Router, b as AnyRouter, A as AnyProcedure, d as ProcedureClientInterceptorOptions } from './server.qKsRrdxW.js';
6
6
 
7
- interface StandardHandlerPlugin<TContext extends Context> {
7
+ interface StandardHandlerPlugin<T extends Context> {
8
8
  order?: number;
9
- init?(options: StandardHandlerOptions<TContext>): void;
9
+ init?(options: StandardHandlerOptions<T>, router: Router<any, T>): void;
10
10
  }
11
11
  declare class CompositeStandardHandlerPlugin<T extends Context, TPlugin extends StandardHandlerPlugin<T>> implements StandardHandlerPlugin<T> {
12
12
  protected readonly plugins: TPlugin[];
13
13
  constructor(plugins?: readonly TPlugin[]);
14
- init(options: StandardHandlerOptions<T>): void;
14
+ init(options: StandardHandlerOptions<T>, router: Router<any, T>): void;
15
15
  }
16
16
 
17
17
  type StandardParams = Record<string, string>;
@@ -49,16 +49,16 @@ interface StandardHandlerOptions<TContext extends Context> {
49
49
  /**
50
50
  * Interceptors at the request level, helpful when you want catch errors
51
51
  */
52
- interceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, StandardHandleResult, ThrowableError>[];
52
+ interceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, Promise<StandardHandleResult>>[];
53
53
  /**
54
54
  * Interceptors at the root level, helpful when you want override the request/response
55
55
  */
56
- rootInterceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, StandardHandleResult, ThrowableError>[];
56
+ rootInterceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, Promise<StandardHandleResult>>[];
57
57
  /**
58
58
  *
59
59
  * Interceptors for procedure client.
60
60
  */
61
- clientInterceptors?: Interceptor<ProcedureClientInterceptorOptions<TContext, Record<never, never>, Meta>, InferSchemaOutput<AnySchema>, ErrorFromErrorMap<Record<never, never>>>[];
61
+ clientInterceptors?: Interceptor<ProcedureClientInterceptorOptions<TContext, Record<never, never>, Meta>, Promise<unknown>>[];
62
62
  }
63
63
  declare class StandardHandler<T extends Context> {
64
64
  private readonly matcher;
@@ -70,5 +70,5 @@ declare class StandardHandler<T extends Context> {
70
70
  handle(request: StandardLazyRequest, options: StandardHandleOptions<T>): Promise<StandardHandleResult>;
71
71
  }
72
72
 
73
- export { CompositeStandardHandlerPlugin as C, StandardHandler as i };
74
- export type { StandardHandlerInterceptorOptions as S, StandardHandlerPlugin as a, StandardHandlerOptions as b, StandardCodec as c, StandardParams as d, StandardMatcher as e, StandardMatchResult as f, StandardHandleOptions as g, StandardHandleResult as h };
73
+ export { CompositeStandardHandlerPlugin as C, StandardHandler as f };
74
+ export type { StandardCodec as S, StandardParams as a, StandardHandleOptions as b, StandardHandleResult as c, StandardHandlerInterceptorOptions as d, StandardHandlerOptions as e, StandardHandlerPlugin as g, StandardMatchResult as h, StandardMatcher as i };
@@ -0,0 +1,12 @@
1
+ import { C as Context } from './server.qKsRrdxW.mjs';
2
+ import { b as StandardHandleOptions } from './server.BqadksTP.mjs';
3
+
4
+ type FriendlyStandardHandleOptions<T extends Context> = Omit<StandardHandleOptions<T>, 'context'> & (Record<never, never> extends T ? {
5
+ context?: T;
6
+ } : {
7
+ context: T;
8
+ });
9
+ declare function resolveFriendlyStandardHandleOptions<T extends Context>(options: FriendlyStandardHandleOptions<T>): StandardHandleOptions<T>;
10
+
11
+ export { resolveFriendlyStandardHandleOptions as r };
12
+ export type { FriendlyStandardHandleOptions as F };
@@ -1,6 +1,7 @@
1
- import { isContractProcedure, ValidationError, mergePrefix, mergeErrorMap, enhanceRoute } from '@orpc/contract';
2
- import { fallbackORPCErrorStatus, ORPCError } from '@orpc/client';
3
- import { value, intercept } from '@orpc/shared';
1
+ import { isContractProcedure, validateORPCError, ValidationError, mergePrefix, mergeErrorMap, enhanceRoute } from '@orpc/contract';
2
+ import { resolveMaybeOptionalOptions, toArray, value, runWithSpan, intercept, isAsyncIteratorObject, overlayProxy, asyncIteratorWithSpan } from '@orpc/shared';
3
+ import { ORPCError, mapEventIterator } from '@orpc/client';
4
+ import { HibernationEventIterator } from '@orpc/standard-server';
4
5
 
5
6
  const LAZY_SYMBOL = Symbol("ORPC_LAZY_SYMBOL");
6
7
  function lazy(loader, meta = {}) {
@@ -71,14 +72,15 @@ function createORPCErrorConstructorMap(errors) {
71
72
  if (typeof code !== "string") {
72
73
  return Reflect.get(target, code);
73
74
  }
74
- const item = (...[options]) => {
75
+ const item = (...rest) => {
76
+ const options = resolveMaybeOptionalOptions(rest);
75
77
  const config = errors[code];
76
78
  return new ORPCError(code, {
77
79
  defined: Boolean(config),
78
80
  status: config?.status,
79
- message: options?.message ?? config?.message,
80
- data: options?.data,
81
- cause: options?.cause
81
+ message: options.message ?? config?.message,
82
+ data: options.data,
83
+ cause: options.cause
82
84
  });
83
85
  };
84
86
  return item;
@@ -86,54 +88,64 @@ function createORPCErrorConstructorMap(errors) {
86
88
  });
87
89
  return proxy;
88
90
  }
89
- async function validateORPCError(map, error) {
90
- const { code, status, message, data, cause, defined } = error;
91
- const config = map?.[error.code];
92
- if (!config || fallbackORPCErrorStatus(error.code, config.status) !== error.status) {
93
- return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error;
94
- }
95
- if (!config.data) {
96
- return defined ? error : new ORPCError(code, { defined: true, status, message, data, cause });
97
- }
98
- const validated = await config.data["~standard"].validate(error.data);
99
- if (validated.issues) {
100
- return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error;
101
- }
102
- return new ORPCError(code, { defined: true, status, message, data: validated.value, cause });
103
- }
104
91
 
105
92
  function middlewareOutputFn(output) {
106
93
  return { output, context: {} };
107
94
  }
108
95
 
109
- function createProcedureClient(lazyableProcedure, ...[options]) {
96
+ function createProcedureClient(lazyableProcedure, ...rest) {
97
+ const options = resolveMaybeOptionalOptions(rest);
110
98
  return async (...[input, callerOptions]) => {
111
- const path = options?.path ?? [];
99
+ const path = toArray(options.path);
112
100
  const { default: procedure } = await unlazy(lazyableProcedure);
113
101
  const clientContext = callerOptions?.context ?? {};
114
- const context = await value(options?.context ?? {}, clientContext);
102
+ const context = await value(options.context ?? {}, clientContext);
115
103
  const errors = createORPCErrorConstructorMap(procedure["~orpc"].errorMap);
104
+ const validateError = async (e) => {
105
+ if (e instanceof ORPCError) {
106
+ return await validateORPCError(procedure["~orpc"].errorMap, e);
107
+ }
108
+ return e;
109
+ };
116
110
  try {
117
- return await intercept(
118
- options?.interceptors ?? [],
119
- {
120
- context,
121
- input,
122
- // input only optional when it undefinable so we can safely cast it
123
- errors,
124
- path,
125
- procedure,
126
- signal: callerOptions?.signal,
127
- lastEventId: callerOptions?.lastEventId
128
- },
129
- (interceptorOptions) => executeProcedureInternal(interceptorOptions.procedure, interceptorOptions)
111
+ const output = await runWithSpan(
112
+ { name: "call_procedure", signal: callerOptions?.signal },
113
+ (span) => {
114
+ span?.setAttribute("procedure.path", [...path]);
115
+ return intercept(
116
+ toArray(options.interceptors),
117
+ {
118
+ context,
119
+ input,
120
+ // input only optional when it undefinable so we can safely cast it
121
+ errors,
122
+ path,
123
+ procedure,
124
+ signal: callerOptions?.signal,
125
+ lastEventId: callerOptions?.lastEventId
126
+ },
127
+ (interceptorOptions) => executeProcedureInternal(interceptorOptions.procedure, interceptorOptions)
128
+ );
129
+ }
130
130
  );
131
- } catch (e) {
132
- if (!(e instanceof ORPCError)) {
133
- throw e;
131
+ if (isAsyncIteratorObject(output)) {
132
+ if (output instanceof HibernationEventIterator) {
133
+ return output;
134
+ }
135
+ return overlayProxy(output, mapEventIterator(
136
+ asyncIteratorWithSpan(
137
+ { name: "consume_event_iterator_output", signal: callerOptions?.signal },
138
+ output
139
+ ),
140
+ {
141
+ value: (v) => v,
142
+ error: (e) => validateError(e)
143
+ }
144
+ ));
134
145
  }
135
- const validated = await validateORPCError(procedure["~orpc"].errorMap, e);
136
- throw validated;
146
+ return output;
147
+ } catch (e) {
148
+ throw await validateError(e);
137
149
  }
138
150
  };
139
151
  }
@@ -142,31 +154,49 @@ async function validateInput(procedure, input) {
142
154
  if (!schema) {
143
155
  return input;
144
156
  }
145
- const result = await schema["~standard"].validate(input);
146
- if (result.issues) {
147
- throw new ORPCError("BAD_REQUEST", {
148
- message: "Input validation failed",
149
- data: {
150
- issues: result.issues
151
- },
152
- cause: new ValidationError({ message: "Input validation failed", issues: result.issues })
153
- });
154
- }
155
- return result.value;
157
+ return runWithSpan(
158
+ { name: "validate_input" },
159
+ async () => {
160
+ const result = await schema["~standard"].validate(input);
161
+ if (result.issues) {
162
+ throw new ORPCError("BAD_REQUEST", {
163
+ message: "Input validation failed",
164
+ data: {
165
+ issues: result.issues
166
+ },
167
+ cause: new ValidationError({
168
+ message: "Input validation failed",
169
+ issues: result.issues,
170
+ data: input
171
+ })
172
+ });
173
+ }
174
+ return result.value;
175
+ }
176
+ );
156
177
  }
157
178
  async function validateOutput(procedure, output) {
158
179
  const schema = procedure["~orpc"].outputSchema;
159
180
  if (!schema) {
160
181
  return output;
161
182
  }
162
- const result = await schema["~standard"].validate(output);
163
- if (result.issues) {
164
- throw new ORPCError("INTERNAL_SERVER_ERROR", {
165
- message: "Output validation failed",
166
- cause: new ValidationError({ message: "Output validation failed", issues: result.issues })
167
- });
168
- }
169
- return result.value;
183
+ return runWithSpan(
184
+ { name: "validate_output" },
185
+ async () => {
186
+ const result = await schema["~standard"].validate(output);
187
+ if (result.issues) {
188
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
189
+ message: "Output validation failed",
190
+ cause: new ValidationError({
191
+ message: "Output validation failed",
192
+ issues: result.issues,
193
+ data: output
194
+ })
195
+ });
196
+ }
197
+ return result.value;
198
+ }
199
+ );
170
200
  }
171
201
  async function executeProcedureInternal(procedure, options) {
172
202
  const middlewares = procedure["~orpc"].middlewares;
@@ -178,17 +208,28 @@ async function executeProcedureInternal(procedure, options) {
178
208
  currentInput = await validateInput(procedure, currentInput);
179
209
  }
180
210
  const mid = middlewares[index];
181
- const output = mid ? (await mid({
182
- ...options,
183
- context,
184
- next: async (...[nextOptions]) => {
185
- const nextContext = nextOptions?.context ?? {};
186
- return {
187
- output: await next(index + 1, mergeCurrentContext(context, nextContext), currentInput),
188
- context: nextContext
189
- };
211
+ const output = mid ? await runWithSpan(
212
+ { name: `middleware.${mid.name}`, signal: options.signal },
213
+ async (span) => {
214
+ span?.setAttribute("middleware.index", index);
215
+ span?.setAttribute("middleware.name", mid.name);
216
+ const result = await mid({
217
+ ...options,
218
+ context,
219
+ next: async (...[nextOptions]) => {
220
+ const nextContext = nextOptions?.context ?? {};
221
+ return {
222
+ output: await next(index + 1, mergeCurrentContext(context, nextContext), currentInput),
223
+ context: nextContext
224
+ };
225
+ }
226
+ }, currentInput, middlewareOutputFn);
227
+ return result.output;
190
228
  }
191
- }, currentInput, middlewareOutputFn)).output : await procedure["~orpc"].handler({ ...options, context, input: currentInput });
229
+ ) : await runWithSpan(
230
+ { name: "handler", signal: options.signal },
231
+ () => procedure["~orpc"].handler({ ...options, context, input: currentInput })
232
+ );
192
233
  if (index === outputValidationIndex) {
193
234
  return await validateOutput(procedure, output);
194
235
  }
@@ -222,6 +263,9 @@ function getRouter(router, path) {
222
263
  if (isProcedure(current)) {
223
264
  return void 0;
224
265
  }
266
+ if (typeof current !== "object") {
267
+ return void 0;
268
+ }
225
269
  if (!isLazy(current)) {
226
270
  current = current[segment];
227
271
  continue;
@@ -276,6 +320,9 @@ function enhanceRouter(router, options) {
276
320
  });
277
321
  return enhanced2;
278
322
  }
323
+ if (typeof router !== "object" || router === null) {
324
+ return router;
325
+ }
279
326
  const enhanced = {};
280
327
  for (const key in router) {
281
328
  enhanced[key] = enhanceRouter(router[key], options);
@@ -283,6 +330,9 @@ function enhanceRouter(router, options) {
283
330
  return enhanced;
284
331
  }
285
332
  function traverseContractProcedures(options, callback, lazyOptions = []) {
333
+ if (typeof options.router !== "object" || options.router === null) {
334
+ return lazyOptions;
335
+ }
286
336
  let currentRouter = options.router;
287
337
  const hiddenContract = getHiddenRouterContract(options.router);
288
338
  if (hiddenContract !== void 0) {
@@ -298,7 +348,7 @@ function traverseContractProcedures(options, callback, lazyOptions = []) {
298
348
  contract: currentRouter,
299
349
  path: options.path
300
350
  });
301
- } else {
351
+ } else if (typeof currentRouter === "object" && currentRouter !== null) {
302
352
  for (const key in currentRouter) {
303
353
  traverseContractProcedures(
304
354
  {
@@ -329,6 +379,9 @@ async function unlazyRouter(router) {
329
379
  if (isProcedure(router)) {
330
380
  return router;
331
381
  }
382
+ if (typeof router !== "object" || router === null) {
383
+ return router;
384
+ }
332
385
  const unlazied = {};
333
386
  for (const key in router) {
334
387
  const item = router[key];
@@ -361,7 +414,8 @@ function createContractedProcedure(procedure, contract) {
361
414
  });
362
415
  }
363
416
  function call(procedure, input, ...rest) {
364
- return createProcedureClient(procedure, ...rest)(input);
417
+ const options = resolveMaybeOptionalOptions(rest);
418
+ return createProcedureClient(procedure, options)(input, options);
365
419
  }
366
420
 
367
- export { LAZY_SYMBOL as L, Procedure as P, createContractedProcedure as a, addMiddleware as b, createProcedureClient as c, isLazy as d, enhanceRouter as e, createAssertedLazyProcedure as f, getRouter as g, createORPCErrorConstructorMap as h, isProcedure as i, getLazyMeta as j, middlewareOutputFn as k, lazy as l, mergeCurrentContext as m, isStartWithMiddlewares as n, mergeMiddlewares as o, call as p, getHiddenRouterContract as q, createAccessibleLazyRouter as r, setHiddenRouterContract as s, traverseContractProcedures as t, unlazy as u, validateORPCError as v, resolveContractProcedures as w, unlazyRouter as x };
421
+ export { LAZY_SYMBOL as L, Procedure as P, createContractedProcedure as a, addMiddleware as b, createProcedureClient as c, isLazy as d, enhanceRouter as e, createAssertedLazyProcedure as f, getRouter as g, createORPCErrorConstructorMap as h, isProcedure as i, getLazyMeta as j, middlewareOutputFn as k, lazy as l, mergeCurrentContext as m, isStartWithMiddlewares as n, mergeMiddlewares as o, call as p, getHiddenRouterContract as q, createAccessibleLazyRouter as r, setHiddenRouterContract as s, traverseContractProcedures as t, unlazy as u, resolveContractProcedures as v, unlazyRouter as w };