@orpc/server 2.0.0-beta.44 → 2.0.0-beta.45

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 (37) hide show
  1. package/README.md +55 -48
  2. package/dist/adapters/aws-lambda/index.mjs +2 -2
  3. package/dist/adapters/crossws/index.mjs +2 -2
  4. package/dist/adapters/fastify/index.mjs +2 -2
  5. package/dist/adapters/fetch/index.d.mts +2 -2
  6. package/dist/adapters/fetch/index.d.ts +2 -2
  7. package/dist/adapters/fetch/index.mjs +2 -2
  8. package/dist/adapters/message-port/index.d.mts +1 -1
  9. package/dist/adapters/message-port/index.d.ts +1 -1
  10. package/dist/adapters/message-port/index.mjs +2 -2
  11. package/dist/adapters/node/index.d.mts +1 -1
  12. package/dist/adapters/node/index.d.ts +1 -1
  13. package/dist/adapters/node/index.mjs +2 -2
  14. package/dist/adapters/standard/index.d.mts +8 -7
  15. package/dist/adapters/standard/index.d.ts +8 -7
  16. package/dist/adapters/standard/index.mjs +2 -2
  17. package/dist/adapters/websocket/index.mjs +2 -2
  18. package/dist/helpers/index.mjs +2 -8
  19. package/dist/index.d.mts +14 -7
  20. package/dist/index.d.ts +14 -7
  21. package/dist/index.mjs +2 -2
  22. package/dist/plugins/index.d.mts +57 -32
  23. package/dist/plugins/index.d.ts +57 -32
  24. package/dist/plugins/index.mjs +183 -45
  25. package/dist/shared/{server.BllfVJLm.d.mts → server.B4hFgqPr.d.mts} +2 -2
  26. package/dist/shared/{server.BEdOJs6S.d.mts → server.BWhT-63L.d.mts} +5 -5
  27. package/dist/shared/{server.BEdOJs6S.d.ts → server.BWhT-63L.d.ts} +5 -5
  28. package/dist/shared/{server.CVO0hWyj.d.mts → server.BYNQ06wN.d.ts} +5 -5
  29. package/dist/shared/{server.BgvqVW_v.mjs → server.BcxD3fmU.mjs} +15 -3
  30. package/dist/shared/{server.COfbQ0D8.d.ts → server.CI0qiBzm.d.ts} +2 -2
  31. package/dist/shared/{server.DFEuNUt4.d.ts → server.CO_upro1.d.ts} +3 -3
  32. package/dist/shared/{server.DZeVx8K9.d.ts → server.DcDZdSrg.d.ts} +1 -1
  33. package/dist/shared/{server.UG_xdTub.d.mts → server.DgcwQ-Wr.d.mts} +3 -3
  34. package/dist/shared/{server.B34dZrzY.mjs → server.Kbq-C85k.mjs} +2 -8
  35. package/dist/shared/{server.DJNkZPfD.d.ts → server.TVSii8T9.d.mts} +5 -5
  36. package/dist/shared/{server.DD1roZl9.d.mts → server.TjEHK8V3.d.mts} +1 -1
  37. package/package.json +12 -12
@@ -1,5 +1,7 @@
1
1
  import { ORPCError } from '@orpc/client';
2
- import { toArray, value, promiseWithResolvers, isAsyncIteratorObject, isCompressibleContentType, isNoTransformCacheControl, parseAcceptEncodingQualities, isAcceptableEncoding, varyByAcceptEncoding, stringifyJSON, anyAbortSignal, override, wrapAsyncIterator, wrapReadableStream, AbortError } from '@orpc/shared';
2
+ import { DEFAULT_BATCH_MAX_SIZE, BATCH_HEADER } from '@orpc/client/plugins';
3
+ export { BATCH_HEADER, DEFAULT_BATCH_MAX_SIZE } from '@orpc/client/plugins';
4
+ import { toArray, value, promiseWithResolvers, isAsyncIteratorObject, isCompressibleContentType, isNoTransformCacheControl, parseAcceptEncodingQualities, isAcceptableEncoding, varyByAcceptEncoding, stringifyJSON, anyAbortSignal, override, AsyncIteratorClass, runWithSignal, AbortError, wrapAsyncIterator, wrapReadableStream } from '@orpc/shared';
3
5
  import { flattenStandardHeader, parseStandardUrl, generateContentDisposition, mergeStandardHeaders } from '@standard-server/core';
4
6
  import { isClientPeerSendMessage, ServerPeer, encodePeerMessage } from '@standard-server/peer';
5
7
  import { toFetchHeaders, toStandardBody, toStandardHeaders } from '@standard-server/fetch';
@@ -17,6 +19,45 @@ async function encodeFrame(message) {
17
19
  const bytes = typeof encoded === "string" ? textEncoder.encode(encoded) : encoded;
18
20
  return [encodeLengthPrefix(bytes.byteLength), bytes];
19
21
  }
22
+ function isBodyHeader(name) {
23
+ return name.startsWith("content-") || name === "standard-server";
24
+ }
25
+ function internalError(err) {
26
+ Promise.reject(err);
27
+ return { status: 500, headers: {}, body: "Internal server error" };
28
+ }
29
+ function liftSubresponseHeaders(batchHeaders, subresponseHeaders) {
30
+ const [first = {}, ...rest] = subresponseHeaders;
31
+ const shared = {};
32
+ for (const [name, value2] of Object.entries(first)) {
33
+ if (value2 === void 0 || isBodyHeader(name)) {
34
+ continue;
35
+ }
36
+ const flattened = flattenStandardHeader(value2);
37
+ if (rest.every((other) => flattenStandardHeader(other[name]) === flattened)) {
38
+ shared[name] = value2;
39
+ }
40
+ }
41
+ const headers = { ...shared, ...batchHeaders };
42
+ const cookies = new Set([batchHeaders, ...subresponseHeaders].flatMap((other) => toArray(other["set-cookie"])));
43
+ if (cookies.size > 0) {
44
+ headers["set-cookie"] = [...cookies];
45
+ }
46
+ const varies = /* @__PURE__ */ new Map();
47
+ for (const value2 of [batchHeaders, ...subresponseHeaders].flatMap((other) => toArray(other.vary))) {
48
+ for (const field of value2.split(",")) {
49
+ const name = field.trim();
50
+ const key = name.toLowerCase();
51
+ if (name && !varies.has(key)) {
52
+ varies.set(key, name);
53
+ }
54
+ }
55
+ }
56
+ if (varies.size > 0) {
57
+ headers.vary = varies.has("*") ? "*" : [...varies.values()].join(", ");
58
+ }
59
+ return headers;
60
+ }
20
61
  class BatchHandlerPlugin {
21
62
  name = "~batch";
22
63
  /**
@@ -26,12 +67,13 @@ class BatchHandlerPlugin {
26
67
  after = ["~tracing"];
27
68
  maxSize;
28
69
  mapSubrequest;
70
+ mapSubresponse;
29
71
  successStatus;
30
72
  headers;
31
73
  keepAliveEnabled;
32
74
  keepAliveInterval;
33
75
  constructor(options = {}) {
34
- this.maxSize = options.maxSize ?? 10;
76
+ this.maxSize = options.maxSize ?? DEFAULT_BATCH_MAX_SIZE;
35
77
  this.mapSubrequest = options.mapSubrequest ?? ((subRequest, { request: batchRequest }) => ({
36
78
  ...subRequest,
37
79
  headers: {
@@ -42,10 +84,19 @@ class BatchHandlerPlugin {
42
84
  * or `origin`, cannot be overridden by a subrequest, which is just request payload.
43
85
  */
44
86
  ...batchRequest.headers,
45
- "orpc-batch": void 0
87
+ [BATCH_HEADER]: void 0
46
88
  // useful in case batch plugin is used multiple times
47
89
  }
48
90
  }));
91
+ this.mapSubresponse = options.mapSubresponse ?? ((subresponse, { headers: batchHeaders }) => {
92
+ const headers = {};
93
+ for (const [name, value2] of Object.entries(subresponse.headers)) {
94
+ if (name !== "set-cookie" && flattenStandardHeader(value2) !== flattenStandardHeader(batchHeaders[name])) {
95
+ headers[name] = value2;
96
+ }
97
+ }
98
+ return { ...subresponse, headers };
99
+ });
49
100
  this.successStatus = options.successStatus ?? 207;
50
101
  this.headers = options.headers ?? {};
51
102
  this.keepAliveEnabled = options.keepAlive?.enabled ?? true;
@@ -53,11 +104,9 @@ class BatchHandlerPlugin {
53
104
  }
54
105
  init(options) {
55
106
  const routingInterceptor = async (interceptorOptions) => {
56
- const batchHeader = flattenStandardHeader(interceptorOptions.request.headers["orpc-batch"]);
57
- if (batchHeader === void 0) {
107
+ if (flattenStandardHeader(interceptorOptions.request.headers[BATCH_HEADER]) === void 0) {
58
108
  return interceptorOptions.next();
59
109
  }
60
- const mode = batchHeader === "buffered" ? "buffered" : "streaming";
61
110
  let messages;
62
111
  try {
63
112
  if (interceptorOptions.request.method === "GET") {
@@ -117,6 +166,14 @@ class BatchHandlerPlugin {
117
166
  const bufferedMessages = [];
118
167
  let streamController;
119
168
  let keepAliveTimer;
169
+ const ready = messages.map(() => promiseWithResolvers());
170
+ const lifting = Promise.all(ready.map(({ promise }) => promise)).then((all) => {
171
+ const heads = all.filter((head) => head !== void 0);
172
+ return {
173
+ batchHeaders: liftSubresponseHeaders(headers, heads.map((head) => head.headers)),
174
+ streams: heads.some((head) => head.stream)
175
+ };
176
+ });
120
177
  const clearKeepAlive = () => {
121
178
  if (keepAliveTimer !== void 0) {
122
179
  clearInterval(keepAliveTimer);
@@ -143,50 +200,37 @@ class BatchHandlerPlugin {
143
200
  scheduleKeepAlive();
144
201
  };
145
202
  const peer = new ServerPeer(async (message) => {
146
- if (mode === "streaming") {
147
- startStreaming();
148
- }
149
203
  if (streamController === void 0) {
150
204
  bufferedMessages.push(message);
151
205
  } else {
152
206
  await enqueueMessage(message);
153
207
  }
154
208
  });
155
- const streaming = promiseWithResolvers();
156
- function startStreaming() {
157
- if (streamController !== void 0) {
158
- return;
209
+ const handleIndividualRequest = async (request, markReady) => {
210
+ let response;
211
+ try {
212
+ request = this.mapSubrequest(request, interceptorOptions);
213
+ const result = await interceptorOptions.next({ ...interceptorOptions, request });
214
+ response = result.matched ? result.response : { status: 404, headers: {}, body: "No procedure matched" };
215
+ } catch (err) {
216
+ response = internalError(err);
159
217
  }
160
- const body = new ReadableStream({
161
- start(controller) {
162
- streamController = controller;
163
- scheduleKeepAlive();
164
- },
165
- async cancel(reason) {
166
- clearKeepAlive();
167
- await peer.close(reason);
168
- }
218
+ markReady({
219
+ headers: response.headers,
220
+ stream: response.body instanceof ReadableStream || isAsyncIteratorObject(response.body)
169
221
  });
170
- streaming.resolve({ body, flushed: Promise.all(bufferedMessages.splice(0).map(enqueueMessage)) });
171
- }
172
- const handleIndividualRequest = async (request) => {
222
+ const { batchHeaders: batchHeaders2 } = await lifting;
173
223
  try {
174
- request = this.mapSubrequest(request, interceptorOptions);
175
- const { matched, response } = await interceptorOptions.next({ ...interceptorOptions, request });
176
- if (!matched) {
177
- return { status: 404, headers: {}, body: "No procedure matched" };
178
- }
179
- if (response.body instanceof ReadableStream || isAsyncIteratorObject(response.body)) {
180
- startStreaming();
181
- }
182
- return response;
224
+ return this.mapSubresponse(response, { status, headers: batchHeaders2 }, interceptorOptions);
183
225
  } catch (err) {
184
- Promise.reject(err);
185
- return { status: 500, headers: {}, body: "Internal server error" };
226
+ return internalError(err);
186
227
  }
187
228
  };
188
229
  const runSubrequests = async () => {
189
- const promise = Promise.all(messages.map((msg) => peer.message(msg, handleIndividualRequest)));
230
+ const promise = Promise.all(messages.map((msg, i) => {
231
+ const { resolve } = ready[i];
232
+ return peer.message(msg, (request) => handleIndividualRequest(request, resolve)).finally(() => resolve(void 0));
233
+ }));
190
234
  const signal = interceptorOptions.request.signal;
191
235
  const closePeer = () => peer.close(signal?.reason);
192
236
  if (signal?.aborted) {
@@ -200,8 +244,9 @@ class BatchHandlerPlugin {
200
244
  }
201
245
  };
202
246
  const subrequests = runSubrequests();
203
- const started = await Promise.race([streaming.promise, subrequests.then(() => void 0)]);
204
- if (started === void 0) {
247
+ const { batchHeaders, streams } = await lifting;
248
+ if (!streams) {
249
+ await subrequests;
205
250
  await peer.close();
206
251
  if (bufferedMessages.some((msg) => msg.binary !== void 0)) {
207
252
  const chunks = (await Promise.all(bufferedMessages.map(encodeFrame))).flat();
@@ -209,17 +254,28 @@ class BatchHandlerPlugin {
209
254
  matched: true,
210
255
  response: {
211
256
  status,
212
- headers,
257
+ headers: batchHeaders,
213
258
  body: new Blob(chunks, { type: BATCH_CONTENT_TYPE })
214
259
  }
215
260
  };
216
261
  }
217
262
  return {
218
263
  matched: true,
219
- response: { status, headers, body: bufferedMessages }
264
+ response: { status, headers: batchHeaders, body: bufferedMessages }
220
265
  };
221
266
  }
222
- Promise.all([started.flushed, subrequests]).then(async () => {
267
+ const body = new ReadableStream({
268
+ start(controller) {
269
+ streamController = controller;
270
+ scheduleKeepAlive();
271
+ },
272
+ async cancel(reason) {
273
+ clearKeepAlive();
274
+ await peer.close(reason);
275
+ }
276
+ });
277
+ const flushed = Promise.all(bufferedMessages.splice(0).map(enqueueMessage));
278
+ Promise.all([flushed, subrequests]).then(async () => {
223
279
  clearKeepAlive();
224
280
  streamController.close();
225
281
  await peer.close();
@@ -232,8 +288,8 @@ class BatchHandlerPlugin {
232
288
  matched: true,
233
289
  response: {
234
290
  status,
235
- headers: { ...headers, "content-type": BATCH_CONTENT_TYPE },
236
- body: started.body
291
+ headers: { ...batchHeaders, "content-type": BATCH_CONTENT_TYPE },
292
+ body
237
293
  }
238
294
  };
239
295
  };
@@ -774,6 +830,88 @@ class RethrowHandlerPlugin {
774
830
  }
775
831
  }
776
832
 
833
+ class SignalSyncHandlerPlugin {
834
+ name = "~signal-sync";
835
+ init(options) {
836
+ const clientInterceptor = async (interceptorOptions) => {
837
+ const controller = new AbortController();
838
+ const signal = anyAbortSignal([interceptorOptions.signal, controller.signal]);
839
+ const cancel = () => controller.abort(new AbortError("The response body was cancelled"));
840
+ const output = await interceptorOptions.next({ ...interceptorOptions, signal });
841
+ if (isAsyncIteratorObject(output)) {
842
+ return override(output, syncAsyncIterator(output, signal, cancel));
843
+ }
844
+ if (output instanceof ReadableStream) {
845
+ return override(output, syncReadableStream(output, signal, cancel));
846
+ }
847
+ return output;
848
+ };
849
+ return { ...options, clientInterceptors: [clientInterceptor, ...toArray(options.clientInterceptors)] };
850
+ }
851
+ }
852
+ function listenAbort(signal, onAbort) {
853
+ if (signal.aborted) {
854
+ onAbort();
855
+ } else {
856
+ signal.addEventListener("abort", onAbort, { once: true });
857
+ }
858
+ return () => signal.removeEventListener("abort", onAbort);
859
+ }
860
+ function syncAsyncIterator(iterator, signal, cancel) {
861
+ let cancelling;
862
+ const unlisten = listenAbort(signal, () => {
863
+ cancelling = (async () => iterator.return?.())();
864
+ cancelling.catch(() => {
865
+ });
866
+ });
867
+ return new AsyncIteratorClass(
868
+ () => runWithSignal(signal, () => iterator.next()),
869
+ async ({ kind }) => {
870
+ if (kind === "cancelled") {
871
+ cancel();
872
+ await cancelling;
873
+ }
874
+ unlisten();
875
+ }
876
+ );
877
+ }
878
+ function syncReadableStream(stream, signal, cancel) {
879
+ const reader = stream.getReader();
880
+ let cancelling;
881
+ let unlisten;
882
+ return new ReadableStream({
883
+ start(controller) {
884
+ unlisten = listenAbort(signal, () => {
885
+ controller.error(signal.reason);
886
+ cancelling = reader.cancel(signal.reason);
887
+ cancelling.catch(() => {
888
+ });
889
+ });
890
+ },
891
+ async pull(controller) {
892
+ try {
893
+ const { done, value } = await reader.read();
894
+ if (signal.aborted) {
895
+ return;
896
+ }
897
+ if (done) {
898
+ unlisten();
899
+ controller.close();
900
+ } else {
901
+ controller.enqueue(value);
902
+ }
903
+ } catch (error) {
904
+ unlisten();
905
+ throw error;
906
+ }
907
+ },
908
+ async cancel() {
909
+ cancel();
910
+ await cancelling;
911
+ }
912
+ }, { highWaterMark: 0 });
913
+ }
914
+
777
915
  class TimeoutHandlerPlugin {
778
916
  timeout;
779
917
  streamingTimeout;
@@ -823,4 +961,4 @@ class TimeoutHandlerPlugin {
823
961
  }
824
962
  }
825
963
 
826
- export { BATCH_CONTENT_TYPE, BatchHandlerPlugin, CORSHandlerPlugin, CORSHandlerPlugin as CORSPlugin, GetMethodCsrfProtectionHandlerPlugin, MethodOverrideHandlerPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestHeadersHandlerPlugin as RequestHeadersPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, ResponseHeadersHandlerPlugin as ResponseHeadersPlugin, RethrowHandlerPlugin, TimeoutHandlerPlugin, negotiateResponseCompressionEncoding, toCompressedHeaders };
964
+ export { BATCH_CONTENT_TYPE, BatchHandlerPlugin, CORSHandlerPlugin, CORSHandlerPlugin as CORSPlugin, GetMethodCsrfProtectionHandlerPlugin, MethodOverrideHandlerPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestHeadersHandlerPlugin as RequestHeadersPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, ResponseHeadersHandlerPlugin as ResponseHeadersPlugin, RethrowHandlerPlugin, SignalSyncHandlerPlugin, TimeoutHandlerPlugin, negotiateResponseCompressionEncoding, toCompressedHeaders };
@@ -1,5 +1,5 @@
1
- import { C as Context } from './server.BEdOJs6S.mjs';
2
- import { b as StandardHandlerPlugin, c as StandardHandlerOptions } from './server.CVO0hWyj.mjs';
1
+ import { C as Context } from './server.BWhT-63L.mjs';
2
+ import { b as StandardHandlerPlugin, c as StandardHandlerOptions } from './server.TVSii8T9.mjs';
3
3
 
4
4
  interface RequestLimitHandlerPluginOptions {
5
5
  /**
@@ -13,7 +13,7 @@ type MiddlewareResult<TOutContext extends Context, TOutput> = Promisable<{
13
13
  context: TOutContext;
14
14
  }>;
15
15
  type MiddlewareNextOptions<TOutContext extends Context> = object extends TOutContext ? {
16
- context?: TOutContext;
16
+ context?: TOutContext | undefined;
17
17
  } : {
18
18
  context: TOutContext;
19
19
  };
@@ -21,7 +21,7 @@ interface MiddlewareNext<TOutput> {
21
21
  <T extends Context = object>(...rest: MaybeOptionalOptions<MiddlewareNextOptions<T>>): MiddlewareResult<T, TOutput>;
22
22
  }
23
23
  type MiddlewareDoneOptions<TOutContext, TOutput> = (object extends TOutContext ? {
24
- context?: TOutContext;
24
+ context?: TOutContext | undefined;
25
25
  } : {
26
26
  context: TOutContext;
27
27
  }) & {
@@ -150,10 +150,10 @@ interface ProcedureClientInterceptorOptions<TInitialContext extends Context, TEr
150
150
  }
151
151
  type ProcedureClientInterceptor<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> = Interceptor<ProcedureClientInterceptorOptions<TInitialContext, TErrorMap>, PromiseWithError<InferSchemaOutput<TOutputSchema>, ORPCErrorFromErrorMap<TErrorMap> | ThrowableError>>;
152
152
  type ProcedureClientOptions<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap, TClientContext extends ClientContext> = {
153
- path?: string[];
154
- interceptors?: ProcedureClientInterceptor<TInitialContext, TOutputSchema, TErrorMap>[];
153
+ path?: string[] | undefined;
154
+ interceptors?: ProcedureClientInterceptor<TInitialContext, TOutputSchema, TErrorMap>[] | undefined;
155
155
  } & (object extends TInitialContext ? {
156
- context?: Value<Promisable<TInitialContext>, [clientContext: TClientContext]>;
156
+ context?: Value<Promisable<TInitialContext>, [clientContext: TClientContext]> | undefined;
157
157
  } : {
158
158
  context: Value<Promisable<TInitialContext>, [clientContext: TClientContext]>;
159
159
  });
@@ -13,7 +13,7 @@ type MiddlewareResult<TOutContext extends Context, TOutput> = Promisable<{
13
13
  context: TOutContext;
14
14
  }>;
15
15
  type MiddlewareNextOptions<TOutContext extends Context> = object extends TOutContext ? {
16
- context?: TOutContext;
16
+ context?: TOutContext | undefined;
17
17
  } : {
18
18
  context: TOutContext;
19
19
  };
@@ -21,7 +21,7 @@ interface MiddlewareNext<TOutput> {
21
21
  <T extends Context = object>(...rest: MaybeOptionalOptions<MiddlewareNextOptions<T>>): MiddlewareResult<T, TOutput>;
22
22
  }
23
23
  type MiddlewareDoneOptions<TOutContext, TOutput> = (object extends TOutContext ? {
24
- context?: TOutContext;
24
+ context?: TOutContext | undefined;
25
25
  } : {
26
26
  context: TOutContext;
27
27
  }) & {
@@ -150,10 +150,10 @@ interface ProcedureClientInterceptorOptions<TInitialContext extends Context, TEr
150
150
  }
151
151
  type ProcedureClientInterceptor<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> = Interceptor<ProcedureClientInterceptorOptions<TInitialContext, TErrorMap>, PromiseWithError<InferSchemaOutput<TOutputSchema>, ORPCErrorFromErrorMap<TErrorMap> | ThrowableError>>;
152
152
  type ProcedureClientOptions<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap, TClientContext extends ClientContext> = {
153
- path?: string[];
154
- interceptors?: ProcedureClientInterceptor<TInitialContext, TOutputSchema, TErrorMap>[];
153
+ path?: string[] | undefined;
154
+ interceptors?: ProcedureClientInterceptor<TInitialContext, TOutputSchema, TErrorMap>[] | undefined;
155
155
  } & (object extends TInitialContext ? {
156
- context?: Value<Promisable<TInitialContext>, [clientContext: TClientContext]>;
156
+ context?: Value<Promisable<TInitialContext>, [clientContext: TClientContext]> | undefined;
157
157
  } : {
158
158
  context: Value<Promisable<TInitialContext>, [clientContext: TClientContext]>;
159
159
  });
@@ -1,7 +1,7 @@
1
1
  import { AnyORPCError } from '@orpc/client';
2
2
  import { OrderablePlugin, Value, Promisable, Interceptor } from '@orpc/shared';
3
3
  import { StandardLazyRequest, StandardResponse } from '@standard-server/core';
4
- import { C as Context, c as ProcedureClientInterceptor, A as AnyProcedure } from './server.BEdOJs6S.mjs';
4
+ import { C as Context, c as ProcedureClientInterceptor, A as AnyProcedure } from './server.BWhT-63L.js';
5
5
  import { Schema, ErrorMap } from '@orpc/contract';
6
6
 
7
7
  interface StandardHandlerPlugin<T extends Context> extends OrderablePlugin {
@@ -73,19 +73,19 @@ interface StandardHandlerOptions<TContext extends Context> {
73
73
  * - batch plugins - separate one request into multiple and call multiple next
74
74
  * - openapi spec plugin - to intercept a request and early response
75
75
  */
76
- routingInterceptors?: StandardHandlerRoutingInterceptor<TContext>[];
76
+ routingInterceptors?: StandardHandlerRoutingInterceptor<TContext>[] | undefined;
77
77
  /**
78
78
  * interceptor run after routing and before error handler,
79
79
  * useful for error handling, logging, metrics, etc.
80
80
  */
81
- interceptors?: StandardHandlerInterceptor<TContext>[];
81
+ interceptors?: StandardHandlerInterceptor<TContext>[] | undefined;
82
82
  /**
83
83
  *
84
84
  * ClientInterceptor equivalent with createRouterClient.interceptors / createProcedure.interceptors
85
85
  * useful for error handling, logging, metrics, etc. (not counting encoding/decoding)
86
86
  */
87
- clientInterceptors?: ProcedureClientInterceptor<TContext, Schema<unknown>, ErrorMap>[];
88
- plugins?: StandardHandlerPlugin<TContext>[];
87
+ clientInterceptors?: ProcedureClientInterceptor<TContext, Schema<unknown>, ErrorMap>[] | undefined;
88
+ plugins?: StandardHandlerPlugin<TContext>[] | undefined;
89
89
  }
90
90
  declare class StandardHandler<T extends Context> {
91
91
  private readonly codec;
@@ -1,9 +1,9 @@
1
- import { c as createProcedureClient, P as Procedure, L as Lazy, u as unlazy } from './server.FEqy7Hrj.mjs';
2
1
  import { resolveMetaPlugins, mergeErrorMap, ProcedureContract } from '@orpc/contract';
3
2
  import { omit, isTypescriptObject, getOwn } from '@orpc/shared';
3
+ import { c as createProcedureClient, P as Procedure, L as Lazy, u as unlazy } from './server.FEqy7Hrj.mjs';
4
4
 
5
5
  const DEFAULT_SUCCESS_STATUS = 200;
6
- const DEFAULT_ERROR_STATUS = 500;
6
+ const DEFAULT_ERROR_STATUS = 400;
7
7
 
8
8
  function createGuardedProcedureLazy(lazy) {
9
9
  const guarded = new Lazy({
@@ -130,6 +130,18 @@ function augmentImplementedRouter(router, options) {
130
130
  }
131
131
  return enhanced;
132
132
  }
133
+ async function resolveContractProcedure(router, contract, path) {
134
+ if (contract instanceof Procedure) {
135
+ return contract;
136
+ }
137
+ const { default: maybeProcedure } = await unlazy(getRouter(router, path));
138
+ if (!(maybeProcedure instanceof Procedure)) {
139
+ throw new TypeError(
140
+ `[Contract-First] Missing or invalid implementation for procedure at path: "${path.join(".")}". Ensure the procedure is correctly implemented and matches its contract.`
141
+ );
142
+ }
143
+ return createContractProcedure(maybeProcedure, contract);
144
+ }
133
145
  function getRouter(router, path) {
134
146
  let current = router;
135
147
  for (let i = 0; i < path.length; i++) {
@@ -224,4 +236,4 @@ async function unlazyRouter(router) {
224
236
  return unlazied;
225
237
  }
226
238
 
227
- export { DEFAULT_SUCCESS_STATUS as D, DEFAULT_ERROR_STATUS as a, augmentRouter as b, createContractProcedure as c, augmentImplementedRouter as d, withHiddenRouterContract as e, createGuardedProcedureLazy as f, getRouter as g, call as h, getHiddenRouterContract as i, walkProcedureContractsAsync as j, unlazyRouter as u, walkProcedureContractsSync as w };
239
+ export { DEFAULT_SUCCESS_STATUS as D, DEFAULT_ERROR_STATUS as a, augmentRouter as b, augmentImplementedRouter as c, withHiddenRouterContract as d, createGuardedProcedureLazy as e, call as f, getRouter as g, createContractProcedure as h, getHiddenRouterContract as i, walkProcedureContractsAsync as j, resolveContractProcedure as r, unlazyRouter as u, walkProcedureContractsSync as w };
@@ -1,5 +1,5 @@
1
- import { C as Context } from './server.BEdOJs6S.js';
2
- import { b as StandardHandlerPlugin, c as StandardHandlerOptions } from './server.DJNkZPfD.js';
1
+ import { C as Context } from './server.BWhT-63L.js';
2
+ import { b as StandardHandlerPlugin, c as StandardHandlerOptions } from './server.BYNQ06wN.js';
3
3
 
4
4
  interface RequestLimitHandlerPluginOptions {
5
5
  /**
@@ -1,10 +1,10 @@
1
1
  import { Value, Promisable } from '@orpc/shared';
2
2
  import { StandardLazyRequest } from '@standard-server/core';
3
- import { C as Context } from './server.BEdOJs6S.js';
4
- import { a as StandardHandlerHandleOptions } from './server.DJNkZPfD.js';
3
+ import { C as Context } from './server.BWhT-63L.js';
4
+ import { a as StandardHandlerHandleOptions } from './server.BYNQ06wN.js';
5
5
 
6
6
  type FriendlyStandardHandlerHandleOptions<T extends Context> = Omit<StandardHandlerHandleOptions<T>, 'context'> & (object extends T ? {
7
- context?: Value<Promisable<T>, [request: StandardLazyRequest]>;
7
+ context?: Value<Promisable<T>, [request: StandardLazyRequest]> | undefined;
8
8
  } : {
9
9
  context: Value<Promisable<T>, [request: StandardLazyRequest]>;
10
10
  });
@@ -1,5 +1,5 @@
1
1
  import { RouterContract, ProcedureContract, ORPCErrorFromErrorMap, ThrowableError, InferSchemaInput, InferSchemaOutput } from '@orpc/contract';
2
- import { C as Context, a as Procedure, L as Lazyable, M as MergedContext } from './server.BEdOJs6S.js';
2
+ import { C as Context, a as Procedure, L as Lazyable, M as MergedContext } from './server.BWhT-63L.js';
3
3
 
4
4
  type Router<TInitialContext extends Context> = Procedure<TInitialContext, any, any, any, any> | {
5
5
  [k: string]: Lazyable<Router<TInitialContext>>;
@@ -1,10 +1,10 @@
1
1
  import { Value, Promisable } from '@orpc/shared';
2
2
  import { StandardLazyRequest } from '@standard-server/core';
3
- import { C as Context } from './server.BEdOJs6S.mjs';
4
- import { a as StandardHandlerHandleOptions } from './server.CVO0hWyj.mjs';
3
+ import { C as Context } from './server.BWhT-63L.mjs';
4
+ import { a as StandardHandlerHandleOptions } from './server.TVSii8T9.mjs';
5
5
 
6
6
  type FriendlyStandardHandlerHandleOptions<T extends Context> = Omit<StandardHandlerHandleOptions<T>, 'context'> & (object extends T ? {
7
- context?: Value<Promisable<T>, [request: StandardLazyRequest]>;
7
+ context?: Value<Promisable<T>, [request: StandardLazyRequest]> | undefined;
8
8
  } : {
9
9
  context: Value<Promisable<T>, [request: StandardLazyRequest]>;
10
10
  });
@@ -2,7 +2,7 @@ import { ORPCError, toORPCError, RPCSerializer, COMMON_ERROR_STATUS_MAP } from '
2
2
  import { sortPlugins, toArray, matchesHttpPathPrefix, value, intercept, getTracer, runWithSpan, ORPC_NAME, isAsyncIteratorObject, override, traceAsyncIterator, traceReadableStream, recordSpanError, wrapAsyncIterator, toTracingException, wrapReadableStream, pathToHttpPath, normalizeHttpPath, parseEmptyableJSON, getOwn } from '@orpc/shared';
3
3
  import { flattenStandardHeader, parseStandardUrl, ErrorEvent } from '@standard-server/core';
4
4
  import { c as createProcedureClient, u as unlazy, P as Procedure } from './server.FEqy7Hrj.mjs';
5
- import { w as walkProcedureContractsSync, g as getRouter, c as createContractProcedure, D as DEFAULT_SUCCESS_STATUS, a as DEFAULT_ERROR_STATUS } from './server.BgvqVW_v.mjs';
5
+ import { w as walkProcedureContractsSync, r as resolveContractProcedure, D as DEFAULT_SUCCESS_STATUS, a as DEFAULT_ERROR_STATUS } from './server.BcxD3fmU.mjs';
6
6
 
7
7
  class CompositeStandardHandlerPlugin {
8
8
  name = "~composite";
@@ -298,13 +298,7 @@ class RPCMatcher {
298
298
  this.pendingLazyRouters.delete(httpPathPrefix);
299
299
  }
300
300
  async resolveProcedure(entry) {
301
- const { default: maybeProcedure } = await unlazy(getRouter(this.rootRouter, entry.path));
302
- if (!(maybeProcedure instanceof Procedure)) {
303
- throw new TypeError(
304
- `[Contract-First] Missing or invalid implementation for procedure at path: "${entry.path.join(".")}". Ensure the procedure is correctly implemented and matches its contract.`
305
- );
306
- }
307
- entry.procedure = createContractProcedure(maybeProcedure, entry.contract);
301
+ entry.procedure = await resolveContractProcedure(this.rootRouter, entry.contract, entry.path);
308
302
  return entry.procedure;
309
303
  }
310
304
  }
@@ -1,7 +1,7 @@
1
1
  import { AnyORPCError } from '@orpc/client';
2
2
  import { OrderablePlugin, Value, Promisable, Interceptor } from '@orpc/shared';
3
3
  import { StandardLazyRequest, StandardResponse } from '@standard-server/core';
4
- import { C as Context, c as ProcedureClientInterceptor, A as AnyProcedure } from './server.BEdOJs6S.js';
4
+ import { C as Context, c as ProcedureClientInterceptor, A as AnyProcedure } from './server.BWhT-63L.mjs';
5
5
  import { Schema, ErrorMap } from '@orpc/contract';
6
6
 
7
7
  interface StandardHandlerPlugin<T extends Context> extends OrderablePlugin {
@@ -73,19 +73,19 @@ interface StandardHandlerOptions<TContext extends Context> {
73
73
  * - batch plugins - separate one request into multiple and call multiple next
74
74
  * - openapi spec plugin - to intercept a request and early response
75
75
  */
76
- routingInterceptors?: StandardHandlerRoutingInterceptor<TContext>[];
76
+ routingInterceptors?: StandardHandlerRoutingInterceptor<TContext>[] | undefined;
77
77
  /**
78
78
  * interceptor run after routing and before error handler,
79
79
  * useful for error handling, logging, metrics, etc.
80
80
  */
81
- interceptors?: StandardHandlerInterceptor<TContext>[];
81
+ interceptors?: StandardHandlerInterceptor<TContext>[] | undefined;
82
82
  /**
83
83
  *
84
84
  * ClientInterceptor equivalent with createRouterClient.interceptors / createProcedure.interceptors
85
85
  * useful for error handling, logging, metrics, etc. (not counting encoding/decoding)
86
86
  */
87
- clientInterceptors?: ProcedureClientInterceptor<TContext, Schema<unknown>, ErrorMap>[];
88
- plugins?: StandardHandlerPlugin<TContext>[];
87
+ clientInterceptors?: ProcedureClientInterceptor<TContext, Schema<unknown>, ErrorMap>[] | undefined;
88
+ plugins?: StandardHandlerPlugin<TContext>[] | undefined;
89
89
  }
90
90
  declare class StandardHandler<T extends Context> {
91
91
  private readonly codec;
@@ -1,5 +1,5 @@
1
1
  import { RouterContract, ProcedureContract, ORPCErrorFromErrorMap, ThrowableError, InferSchemaInput, InferSchemaOutput } from '@orpc/contract';
2
- import { C as Context, a as Procedure, L as Lazyable, M as MergedContext } from './server.BEdOJs6S.mjs';
2
+ import { C as Context, a as Procedure, L as Lazyable, M as MergedContext } from './server.BWhT-63L.mjs';
3
3
 
4
4
  type Router<TInitialContext extends Context> = Procedure<TInitialContext, any, any, any, any> | {
5
5
  [k: string]: Lazyable<Router<TInitialContext>>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/server",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.44",
4
+ "version": "2.0.0-beta.45",
5
5
  "description": "Build typesafe APIs with oRPC: RPC and OpenAPI support, middleware, and adapters for Node.js, Bun, Deno, Fetch, Fastify, WebSocket, AWS Lambda, and more",
6
6
  "license": "MIT",
7
7
  "funding": [
@@ -117,23 +117,23 @@
117
117
  }
118
118
  },
119
119
  "dependencies": {
120
- "@orpc/client": "2.0.0-beta.44",
121
- "@orpc/contract": "2.0.0-beta.44",
122
- "@orpc/shared": "2.0.0-beta.44",
123
- "@standard-server/aws-lambda": "~0.10.3",
124
- "@standard-server/core": "~0.10.3",
125
- "@standard-server/fastify": "~0.10.3",
126
- "@standard-server/fetch": "~0.10.3",
127
- "@standard-server/node": "~0.10.3",
128
- "@standard-server/peer": "~0.10.3",
120
+ "@orpc/client": "2.0.0-beta.45",
121
+ "@orpc/contract": "2.0.0-beta.45",
122
+ "@orpc/shared": "2.0.0-beta.45",
123
+ "@standard-server/aws-lambda": "~0.11.0",
124
+ "@standard-server/core": "~0.11.0",
125
+ "@standard-server/fastify": "~0.11.0",
126
+ "@standard-server/fetch": "~0.11.0",
127
+ "@standard-server/node": "~0.11.0",
128
+ "@standard-server/peer": "~0.11.0",
129
129
  "cookie": "^2.0.1"
130
130
  },
131
131
  "devDependencies": {
132
132
  "crossws": "^0.4.15",
133
- "fastify": "^5.12.5",
133
+ "fastify": "^5.12.7",
134
134
  "supertest": "^7.3.1",
135
135
  "ws": "^8.22.0",
136
- "zod": "^4.6.5"
136
+ "zod": "^4.6.7"
137
137
  },
138
138
  "scripts": {
139
139
  "type:check": "tsc -b"