@orpc/server 2.0.0-beta.23 → 2.0.0-beta.25

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 (55) hide show
  1. package/README.md +4 -1
  2. package/dist/adapters/aws-lambda/index.d.mts +93 -0
  3. package/dist/adapters/aws-lambda/index.d.ts +93 -0
  4. package/dist/adapters/aws-lambda/index.mjs +66 -0
  5. package/dist/adapters/crossws/index.d.mts +10 -4
  6. package/dist/adapters/crossws/index.d.ts +10 -4
  7. package/dist/adapters/crossws/index.mjs +3 -3
  8. package/dist/adapters/fastify/index.d.mts +85 -0
  9. package/dist/adapters/fastify/index.d.ts +85 -0
  10. package/dist/adapters/fastify/index.mjs +66 -0
  11. package/dist/adapters/fetch/index.d.mts +13 -16
  12. package/dist/adapters/fetch/index.d.ts +13 -16
  13. package/dist/adapters/fetch/index.mjs +6 -12
  14. package/dist/adapters/message-port/index.d.mts +12 -7
  15. package/dist/adapters/message-port/index.d.ts +12 -7
  16. package/dist/adapters/message-port/index.mjs +3 -3
  17. package/dist/adapters/node/index.d.mts +12 -16
  18. package/dist/adapters/node/index.d.ts +12 -16
  19. package/dist/adapters/node/index.mjs +7 -12
  20. package/dist/adapters/standard/index.d.mts +5 -5
  21. package/dist/adapters/standard/index.d.ts +5 -5
  22. package/dist/adapters/standard/index.mjs +3 -3
  23. package/dist/adapters/standard-peer/index.d.mts +2 -2
  24. package/dist/adapters/standard-peer/index.d.ts +2 -2
  25. package/dist/adapters/websocket/index.d.mts +10 -4
  26. package/dist/adapters/websocket/index.d.ts +10 -4
  27. package/dist/adapters/websocket/index.mjs +3 -3
  28. package/dist/extensions/callable.d.mts +1 -1
  29. package/dist/extensions/callable.d.ts +1 -1
  30. package/dist/extensions/callable.mjs +2 -2
  31. package/dist/helpers/index.d.mts +20 -3
  32. package/dist/helpers/index.d.ts +20 -3
  33. package/dist/index.d.mts +39 -7
  34. package/dist/index.d.ts +39 -7
  35. package/dist/index.mjs +23 -18
  36. package/dist/plugins/index.d.mts +59 -48
  37. package/dist/plugins/index.d.ts +59 -48
  38. package/dist/plugins/index.mjs +34 -67
  39. package/dist/shared/{server.DofEOwf-.mjs → server.7T18Khvf.mjs} +6 -32
  40. package/dist/shared/{server.DBs0rrf9.mjs → server.B2pXdfpL.mjs} +1 -1
  41. package/dist/shared/server.BhHrioCw.d.ts +34 -0
  42. package/dist/shared/{server.DG9aQpJK.d.ts → server.C2n16pp0.d.ts} +27 -5
  43. package/dist/shared/{server.A8J9H8OE.mjs → server.Cbv46U7N.mjs} +21 -5
  44. package/dist/shared/server.Cd4Z1hpV.mjs +69 -0
  45. package/dist/shared/{server.omY4cUs0.d.mts → server.CkNnZ5F-.d.mts} +27 -5
  46. package/dist/shared/{server.CcR4kgje.d.ts → server.CwrYlF72.d.mts} +10 -42
  47. package/dist/shared/{server.CcR4kgje.d.mts → server.CwrYlF72.d.ts} +10 -42
  48. package/dist/shared/server.D0Ipbmdv.d.mts +34 -0
  49. package/dist/shared/{server.D4cnq66J.mjs → server.DgunZU0v.mjs} +3 -3
  50. package/dist/shared/{server.m3szVJGU.d.ts → server.Dm0os-OP.d.mts} +24 -12
  51. package/dist/shared/{server.BhsVw7m7.d.ts → server.DrN1Pj1-.d.ts} +3 -3
  52. package/dist/shared/{server.15O7oC1p.d.mts → server.DtfwuV6U.d.ts} +24 -12
  53. package/dist/shared/{server.DDFizwfU.d.mts → server.JbCIPL4P.d.mts} +3 -3
  54. package/package.json +26 -9
  55. package/dist/shared/server.D_QauotT.mjs +0 -30
@@ -1,10 +1,19 @@
1
1
  import { Value, Promisable, ThrowableError } from '@orpc/shared';
2
2
  import { StandardLazyRequest, StandardHeaders } from '@standardserver/core';
3
- import { C as Context } from '../shared/server.CcR4kgje.js';
4
- import { e as StandardHandlerPlugin, f as StandardHandlerRoutingInterceptorOptions, a as StandardHandlerOptions, g as StandardHandlerInterceptorOptions } from '../shared/server.BhsVw7m7.js';
3
+ import { C as Context } from '../shared/server.CwrYlF72.js';
4
+ import { S as StandardHandlerPlugin, f as StandardHandlerRoutingInterceptorOptions, a as StandardHandlerOptions, g as StandardHandlerInterceptorOptions } from '../shared/server.DrN1Pj1-.js';
5
+ export { R as RequestLimitHandlerPlugin, a as RequestLimitHandlerPluginOptions } from '../shared/server.BhHrioCw.js';
5
6
  import '@orpc/client';
6
7
  import '@orpc/contract';
7
8
 
9
+ /**
10
+ * Content type for batch responses that use the length-prefixed binary framing
11
+ * (streaming mode, and buffered mode when any sub-response contains binary).
12
+ *
13
+ * Decoding is driven by the `standard-server` body hint, not this header,
14
+ * so it only serves to describe the payload to logs, proxies, and dev tools.
15
+ */
16
+ declare const BATCH_CONTENT_TYPE = "application/vnd.orpc.batch";
8
17
  interface BatchHandlerPluginOptions<T extends Context> {
9
18
  /**
10
19
  * The max size of the batch allowed.
@@ -54,6 +63,15 @@ interface BatchHandlerPluginOptions<T extends Context> {
54
63
  interval?: number;
55
64
  };
56
65
  }
66
+ /**
67
+ * Handles batch requests sent by the client Batch Link Plugin, splitting each
68
+ * batch into sub-requests and streaming their responses back together.
69
+ *
70
+ * @remarks
71
+ * **Note**: HTTP/2 and later already multiplex requests over a single connection, which often makes this plugin unnecessary.
72
+ *
73
+ * @see {@link https://orpc.dev/docs/plugins/batch | Batch Plugin}
74
+ */
57
75
  declare class BatchHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
58
76
  name: string;
59
77
  /**
@@ -119,9 +137,10 @@ interface CORSHandlerPluginOptions<T extends Context> {
119
137
  exposeHeaders?: readonly string[];
120
138
  }
121
139
  /**
122
- * CORSHandlerPlugin is a plugin for oRPC that allows you to configure CORS for your API.
140
+ * Configures the [CORS Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
141
+ * for your API, including preflight requests.
123
142
  *
124
- * @see {@link https://orpc.dev/docs/plugins/cors CORS Plugin Docs}
143
+ * @see {@link https://orpc.dev/docs/plugins/cors | CORS Handler Plugin}
125
144
  */
126
145
  declare class CORSHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
127
146
  private readonly options;
@@ -137,17 +156,11 @@ declare class CORSHandlerPlugin<T extends Context> implements StandardHandlerPlu
137
156
  }
138
157
 
139
158
  /**
140
- * Adds basic Cross-Site Request Forgery (CSRF) protection to your oRPC application.
141
- * When a request includes cookies, it helps ensure the request originates from JavaScript
142
- * (for example, fetch/XHR) rather than from standard HTML forms or direct browser navigation.
159
+ * Decompresses incoming request bodies based on the Content-Encoding header,
160
+ * supporting gzip, deflate, and deflate-raw.
143
161
  *
144
- * @info This plugin is enabled by default for `RPCHandler` over HTTP.
162
+ * @see {@link https://orpc.dev/docs/plugins/request-compression | Request Compression Plugin}
145
163
  */
146
- declare class CSRFGuardHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
147
- name: string;
148
- init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
149
- }
150
-
151
164
  declare class RequestCompressionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
152
165
  name: string;
153
166
  /**
@@ -157,6 +170,11 @@ declare class RequestCompressionHandlerPlugin<T extends Context> implements Stan
157
170
  init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
158
171
  }
159
172
 
173
+ /**
174
+ * The context shape into which the Request Headers Plugin injects `reqHeaders`.
175
+ *
176
+ * @see {@link https://orpc.dev/docs/plugins/request-headers | Request Headers Plugin}
177
+ */
160
178
  interface RequestHeadersHandlerPluginContext {
161
179
  /**
162
180
  * Request headers as a Headers instance. This is injected by the Request Headers Plugin.
@@ -167,42 +185,13 @@ interface RequestHeadersHandlerPluginContext {
167
185
  * The Request Headers Plugin injects a `reqHeaders` instance into the context,
168
186
  * allowing access to request headers in oRPC.
169
187
  *
170
- * @see {@link https://orpc.dev/docs/plugins/request-headers Request Headers Plugin Docs}
188
+ * @see {@link https://orpc.dev/docs/plugins/request-headers | Request Headers Plugin}
171
189
  */
172
190
  declare class RequestHeadersHandlerPlugin<T extends RequestHeadersHandlerPluginContext> implements StandardHandlerPlugin<T> {
173
191
  name: string;
174
192
  init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
175
193
  }
176
194
 
177
- interface RequestLimitHandlerPluginOptions {
178
- /**
179
- * The maximum allowed request body size in bytes.
180
- */
181
- maxBodySize: number;
182
- }
183
- /**
184
- * Rejects requests whose body exceeds `maxBodySize`.
185
- *
186
- * When used with the request compression plugin, the limit applies to the
187
- * decompressed payload rather than the compressed wire size.
188
- *
189
- * @see {@link https://orpc.dev/docs/plugins/request-limit Request Limit Plugin Docs}
190
- */
191
- declare class RequestLimitHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
192
- name: string;
193
- /**
194
- * Should limit the original batch request body instead of sub-requests.
195
- */
196
- after: string[];
197
- /**
198
- * Should limit the final body size instead of the compressed one.
199
- */
200
- before: string[];
201
- private readonly maxBodySize;
202
- constructor(options: RequestLimitHandlerPluginOptions);
203
- init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
204
- }
205
-
206
195
  interface ResponseCompressionHandlerPluginOptions<_T extends Context> {
207
196
  /**
208
197
  * The compression schemes to use for response compression.
@@ -225,7 +214,7 @@ interface ResponseCompressionHandlerPluginOptions<_T extends Context> {
225
214
  * Compresses response bodies based on the client's Accept-Encoding header.
226
215
  * Works at the standard handler level, so it supports all adapters.
227
216
  *
228
- * @see {@link https://orpc.dev/docs/plugins/response-compression Response Compression Plugin Docs}
217
+ * @see {@link https://orpc.dev/docs/plugins/response-compression | Response Compression Plugin}
229
218
  */
230
219
  declare class ResponseCompressionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
231
220
  name: string;
@@ -240,6 +229,11 @@ declare class ResponseCompressionHandlerPlugin<T extends Context> implements Sta
240
229
  init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
241
230
  }
242
231
 
232
+ /**
233
+ * The context shape into which the Response Headers Plugin injects `resHeaders`.
234
+ *
235
+ * @see {@link https://orpc.dev/docs/plugins/response-headers | Response Headers Plugin}
236
+ */
243
237
  interface ResponseHeadersHandlerPluginContext {
244
238
  /**
245
239
  * Response headers as a Headers instance. This is injected by the Response Headers Plugin.
@@ -251,7 +245,7 @@ interface ResponseHeadersHandlerPluginContext {
251
245
  * The Response Headers Plugin allows you to set response headers in oRPC.
252
246
  * It injects a resHeaders instance into the context, enabling you to modify response headers easily.
253
247
  *
254
- * @see {@link https://orpc.dev/docs/plugins/response-headers Response Headers Plugin Docs}
248
+ * @see {@link https://orpc.dev/docs/plugins/response-headers | Response Headers Plugin}
255
249
  */
256
250
  declare class ResponseHeadersHandlerPlugin<T extends ResponseHeadersHandlerPluginContext> implements StandardHandlerPlugin<T> {
257
251
  name: string;
@@ -283,7 +277,7 @@ interface RethrowHandlerPluginOptions<T extends Context> {
283
277
  * and rethrow matching errors directly to your framework's error handling mechanism
284
278
  * (e.g., NestJS exception filters, Express error middleware).
285
279
  *
286
- * @see {@link https://orpc.dev/docs/plugins/rethrow Rethrow Plugin Documentation}
280
+ * @see {@link https://orpc.dev/docs/plugins/rethrow | Rethrow Handler Plugin}
287
281
  */
288
282
  declare class RethrowHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
289
283
  name: string;
@@ -293,5 +287,22 @@ declare class RethrowHandlerPlugin<T extends Context> implements StandardHandler
293
287
  init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
294
288
  }
295
289
 
296
- export { BatchHandlerPlugin, CORSHandlerPlugin, CSRFGuardHandlerPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestLimitHandlerPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, RethrowHandlerPlugin };
297
- export type { BatchHandlerPluginOptions, CORSHandlerPluginOptions, RequestHeadersHandlerPluginContext, RequestLimitHandlerPluginOptions, ResponseCompressionHandlerPluginOptions, ResponseHeadersHandlerPluginContext, RethrowHandlerPluginOptions };
290
+ /**
291
+ * Adds basic Cross-Site Request Forgery (CSRF) protection to your oRPC application.
292
+ * When a request includes cookies, it helps ensure the request originates from JavaScript
293
+ * (for example, fetch/XHR) rather than from standard HTML forms or direct browser navigation.
294
+ *
295
+ * @remarks
296
+ * Unlike `SameSite` cookies, this protection also covers cross-site requests that
297
+ * still carry cookies, so it is the recommended safeguard when you enable the `GET`
298
+ * method on RPC handlers or rely on cookie-based authentication.
299
+ *
300
+ * @see {@link https://orpc.dev/docs/plugins/simple-csrf-protection | Simple CSRF Protection Plugin}
301
+ */
302
+ declare class SimpleCsrfProtectionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
303
+ name: string;
304
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
305
+ }
306
+
307
+ export { BATCH_CONTENT_TYPE, BatchHandlerPlugin, CORSHandlerPlugin, CORSHandlerPlugin as CORSPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestHeadersHandlerPlugin as RequestHeadersPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, ResponseHeadersHandlerPlugin as ResponseHeadersPlugin, RethrowHandlerPlugin, SimpleCsrfProtectionHandlerPlugin };
308
+ export type { BatchHandlerPluginOptions, CORSHandlerPluginOptions, RequestHeadersHandlerPluginContext, RequestHeadersHandlerPluginContext as RequestHeadersPluginContext, ResponseCompressionHandlerPluginOptions, ResponseHeadersHandlerPluginContext, ResponseHeadersHandlerPluginContext as ResponseHeadersPluginContext, RethrowHandlerPluginOptions };
@@ -1,10 +1,11 @@
1
1
  import { toArray, value, isCompressibleContentType, isAsyncIteratorObject, stringifyJSON } from '@orpc/shared';
2
2
  import { flattenStandardHeader, parseStandardUrl, generateContentDisposition, mergeStandardHeaders } from '@standardserver/core';
3
3
  import { isClientPeerSendMessage, ServerPeer, encodePeerMessage } from '@standardserver/peer';
4
- export { C as CSRFGuardHandlerPlugin } from '../shared/server.D_QauotT.mjs';
5
4
  import { toFetchHeaders, toStandardBody, toStandardHeaders } from '@standardserver/fetch';
5
+ export { R as RequestLimitHandlerPlugin } from '../shared/server.Cd4Z1hpV.mjs';
6
6
  import { ORPCError } from '@orpc/client';
7
7
 
8
+ const BATCH_CONTENT_TYPE = "application/vnd.orpc.batch";
8
9
  class BatchHandlerPlugin {
9
10
  name = "~batch";
10
11
  /**
@@ -121,7 +122,7 @@ class BatchHandlerPlugin {
121
122
  response: {
122
123
  status,
123
124
  headers,
124
- body: new Blob(chunks, { type: "application/octet-stream" })
125
+ body: new Blob(chunks, { type: BATCH_CONTENT_TYPE })
125
126
  }
126
127
  };
127
128
  }
@@ -182,7 +183,11 @@ class BatchHandlerPlugin {
182
183
  });
183
184
  return {
184
185
  matched: true,
185
- response: { status, headers, body: stream }
186
+ response: {
187
+ status,
188
+ headers: { ...headers, "content-type": BATCH_CONTENT_TYPE },
189
+ body: stream
190
+ }
186
191
  };
187
192
  };
188
193
  return {
@@ -365,69 +370,6 @@ class RequestHeadersHandlerPlugin {
365
370
  }
366
371
  }
367
372
 
368
- class RequestLimitHandlerPlugin {
369
- name = "~request-limit";
370
- /**
371
- * Should limit the original batch request body instead of sub-requests.
372
- */
373
- after = ["~batch"];
374
- /**
375
- * Should limit the final body size instead of the compressed one.
376
- */
377
- before = ["~request-compression"];
378
- maxBodySize;
379
- constructor(options) {
380
- this.maxBodySize = options.maxBodySize;
381
- }
382
- init(options) {
383
- const maxBodySize = this.maxBodySize;
384
- const routingInterceptor = async ({ next, ...interceptorOptions }) => {
385
- return next({
386
- ...interceptorOptions,
387
- request: {
388
- ...interceptorOptions.request,
389
- async resolveBody(hint) {
390
- const contentLength = Number(
391
- flattenStandardHeader(interceptorOptions.request.headers["content-length"])
392
- );
393
- if (Number.isFinite(contentLength) && contentLength > maxBodySize) {
394
- throw new ORPCError("PAYLOAD_TOO_LARGE");
395
- }
396
- const stream = await interceptorOptions.request.resolveBody("octet-stream");
397
- if (!(stream instanceof ReadableStream)) {
398
- return stream;
399
- }
400
- let currentBodySize = 0;
401
- const limitedStream = stream.pipeThrough(
402
- new TransformStream({
403
- transform(chunk, controller) {
404
- currentBodySize += chunk.byteLength;
405
- if (currentBodySize > maxBodySize) {
406
- controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
407
- return;
408
- }
409
- controller.enqueue(chunk);
410
- }
411
- })
412
- );
413
- const response = new Response(limitedStream, {
414
- headers: toFetchHeaders(interceptorOptions.request.headers)
415
- });
416
- return toStandardBody(response, { hint });
417
- }
418
- }
419
- });
420
- };
421
- return {
422
- ...options,
423
- routingInterceptors: [
424
- routingInterceptor,
425
- ...toArray(options.routingInterceptors)
426
- ]
427
- };
428
- }
429
- }
430
-
431
373
  const AVG_BYTES_PER_CHAR = 1.2;
432
374
  class ResponseCompressionHandlerPlugin {
433
375
  name = "~response-compression";
@@ -690,4 +632,29 @@ class RethrowHandlerPlugin {
690
632
  }
691
633
  }
692
634
 
693
- export { BatchHandlerPlugin, CORSHandlerPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestLimitHandlerPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, RethrowHandlerPlugin };
635
+ class SimpleCsrfProtectionHandlerPlugin {
636
+ name = "~simple-csrf-protection";
637
+ init(options) {
638
+ const interceptor = async (interceptorOptions) => {
639
+ const mode = flattenStandardHeader(
640
+ interceptorOptions.request.headers["sec-fetch-mode"]
641
+ )?.toLowerCase();
642
+ if (mode === void 0) {
643
+ return interceptorOptions.next();
644
+ }
645
+ if (mode === "cors" || mode === "same-origin") {
646
+ return interceptorOptions.next();
647
+ }
648
+ throw new ORPCError("FORBIDDEN", {
649
+ message: "Request blocked by CSRF protection."
650
+ });
651
+ };
652
+ return {
653
+ ...options,
654
+ // appended last so user's interceptors can catch ORPCError
655
+ interceptors: [...toArray(options.interceptors), interceptor]
656
+ };
657
+ }
658
+ }
659
+
660
+ export { BATCH_CONTENT_TYPE, BatchHandlerPlugin, CORSHandlerPlugin, CORSHandlerPlugin as CORSPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestHeadersHandlerPlugin as RequestHeadersPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, ResponseHeadersHandlerPlugin as ResponseHeadersPlugin, RethrowHandlerPlugin, SimpleCsrfProtectionHandlerPlugin };
@@ -1,32 +1,6 @@
1
- import { ORPCError, wrapAsyncIteratorPreservingEventMeta, cloneORPCError } from '@orpc/client';
2
- import { reconcileORPCError, ValidationError, ProcedureContract } from '@orpc/contract';
3
- import { getOrBind, resolveMaybeOptionalOptions, getConstructor, isTypescriptObject, toArray, value, runWithSpan, intercept, isAsyncIteratorObject, override, traceAsyncIterator, traceReadableStream } from '@orpc/shared';
4
-
5
- function createORPCErrorConstructorMap(errorMap) {
6
- const proxy = new Proxy(errorMap, {
7
- get(target, code) {
8
- if (typeof code !== "string") {
9
- return getOrBind(target, code);
10
- }
11
- const item = (...rest) => {
12
- const options = resolveMaybeOptionalOptions(rest);
13
- const config = errorMap[code];
14
- const error = new ORPCError(code, {
15
- message: options.message ?? config?.message,
16
- data: options.data,
17
- cause: options.cause
18
- });
19
- if (config) {
20
- error.defined = true;
21
- error.inferable = true;
22
- }
23
- return error;
24
- };
25
- return item;
26
- }
27
- });
28
- return proxy;
29
- }
1
+ import { wrapAsyncIteratorPreservingEventMeta, ORPCError, cloneORPCError } from '@orpc/client';
2
+ import { createORPCErrorConstructorMap, reconcileORPCError, ValidationError, ProcedureContract } from '@orpc/contract';
3
+ import { getConstructor, isTypescriptObject, resolveMaybeOptionalOptions, toArray, value, runWithSpan, intercept, isAsyncIteratorObject, override, traceAsyncIterator, traceReadableStream } from '@orpc/shared';
30
4
 
31
5
  class Lazy {
32
6
  "~orpc";
@@ -34,7 +8,7 @@ class Lazy {
34
8
  this["~orpc"] = def;
35
9
  }
36
10
  /**
37
- * Checks if the given instance satisfies the {@see Lazy} class/interface.
11
+ * Checks if the given instance satisfies the {@link Lazy} class/interface.
38
12
  */
39
13
  static [Symbol.hasInstance](instance) {
40
14
  if (this !== Lazy) {
@@ -220,7 +194,7 @@ class Procedure {
220
194
  this["~orpc"] = def;
221
195
  }
222
196
  /**
223
- * Checks if the given instance satisfies the {@see Procedure} class/interface.
197
+ * Checks if the given instance satisfies the {@link Procedure} class/interface.
224
198
  */
225
199
  static [Symbol.hasInstance](instance) {
226
200
  if (this !== Procedure) {
@@ -234,4 +208,4 @@ class Procedure {
234
208
  }
235
209
  }
236
210
 
237
- export { Lazy as L, Procedure as P, createORPCErrorConstructorMap as a, createProcedureClient as c, unlazy as u };
211
+ export { Lazy as L, Procedure as P, createProcedureClient as c, unlazy as u };
@@ -1,5 +1,5 @@
1
1
  import { resolveMetaPlugins, mergeErrorMap, getHiddenMetaPlugins } from '@orpc/contract';
2
- import { P as Procedure } from './server.DofEOwf-.mjs';
2
+ import { P as Procedure } from './server.7T18Khvf.mjs';
3
3
 
4
4
  class DecoratedProcedure extends Procedure {
5
5
  meta(...plugins) {
@@ -0,0 +1,34 @@
1
+ import { C as Context } from './server.CwrYlF72.js';
2
+ import { S as StandardHandlerPlugin, a as StandardHandlerOptions } from './server.DrN1Pj1-.js';
3
+
4
+ interface RequestLimitHandlerPluginOptions {
5
+ /**
6
+ * The maximum allowed request body size in bytes.
7
+ */
8
+ maxBodySize: number;
9
+ }
10
+ /**
11
+ * Rejects requests whose body exceeds `maxBodySize`.
12
+ *
13
+ * When used with the request compression plugin, the limit applies to the
14
+ * decompressed payload rather than the compressed wire size.
15
+ *
16
+ * @see {@link https://orpc.dev/docs/plugins/request-limit | Request Limit Plugin}
17
+ */
18
+ declare class RequestLimitHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
19
+ name: string;
20
+ /**
21
+ * Should limit the original batch request body instead of sub-requests.
22
+ */
23
+ after: string[];
24
+ /**
25
+ * Should limit the final body size instead of the compressed one.
26
+ */
27
+ before: string[];
28
+ private readonly maxBodySize;
29
+ constructor(options: RequestLimitHandlerPluginOptions);
30
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
31
+ }
32
+
33
+ export { RequestLimitHandlerPlugin as R };
34
+ export type { RequestLimitHandlerPluginOptions as a };
@@ -1,11 +1,21 @@
1
1
  import { RPCSerializer, AnyORPCError } from '@orpc/client';
2
2
  import { Value, Promisable } from '@orpc/shared';
3
3
  import { StandardMethod, StandardLazyRequest, StandardResponse } from '@standardserver/core';
4
- import { A as AnyProcedure, C as Context } from './server.CcR4kgje.js';
5
- import { A as AnyRouter } from './server.m3szVJGU.js';
4
+ import { A as AnyProcedure, C as Context } from './server.CwrYlF72.js';
5
+ import { A as AnyRouter } from './server.DtfwuV6U.js';
6
6
  import { AnyProcedureContract } from '@orpc/contract';
7
- import { b as StandardHandlerHandleOptions, c as StandardHandlerCodec, d as StandardHandlerCodecResolvedProcedure } from './server.BhsVw7m7.js';
7
+ import { c as StandardHandlerHandleOptions, d as StandardHandlerCodec, e as StandardHandlerCodecResolvedProcedure } from './server.DrN1Pj1-.js';
8
8
 
9
+ /**
10
+ * Methods that can invoke procedures by default. Browsers cannot trigger them
11
+ * cross-site without a CORS preflight or an HTML form, unlike `GET`, which a plain
12
+ * `<a>` click or redirect can trigger with `SameSite=Lax` cookies attached. Other
13
+ * methods (`HEAD`, `OPTIONS`, `QUERY`, ...) have safe semantics that should not
14
+ * invoke a procedure that can modify data.
15
+ *
16
+ * @see {@link https://orpc.dev/docs/rpc/handler#supported-http-methods | RPC Handler - Supported HTTP Methods}
17
+ */
18
+ declare const RPC_DEFAULT_ALLOW_METHODS: readonly StandardMethod[];
9
19
  interface RPCMatcherOptions {
10
20
  /**
11
21
  * Filter which procedures are exposed for matching. Return `false` to exclude.
@@ -13,15 +23,27 @@ interface RPCMatcherOptions {
13
23
  * @default true
14
24
  */
15
25
  filter?: Value<boolean, [procedure: AnyProcedureContract | AnyProcedure, path: string[]]>;
26
+ /**
27
+ * Restricts which HTTP methods can invoke procedures, either with a list of allowed
28
+ * methods or decided per request via a function. Requests using a disallowed method
29
+ * are treated as unmatched. `GET` is excluded by default because it is exposed to
30
+ * Cross-Site Request Forgery (CSRF) attacks.
31
+ *
32
+ * @default RPC_DEFAULT_ALLOW_METHODS (['POST', 'PUT', 'PATCH', 'DELETE'])
33
+ * @see {@link https://orpc.dev/docs/rpc/handler#supported-http-methods | RPC Handler - Supported HTTP Methods}
34
+ */
35
+ allowMethods?: readonly StandardMethod[] | ((method: StandardMethod, procedure: AnyProcedure, path: string[]) => boolean);
16
36
  }
17
37
  declare class RPCMatcher {
18
38
  private readonly filter;
39
+ private readonly allowMethodsSet;
40
+ private readonly allowMethodsFn;
19
41
  private readonly rootRouter;
20
42
  private readonly tree;
21
43
  private readonly pendingLazyRouters;
22
44
  constructor(router: AnyRouter, options?: RPCMatcherOptions);
23
45
  private index;
24
- match(_method: StandardMethod, pathname: `/${string}`, prefix: `/${string}` | undefined): Promise<{
46
+ match(method: StandardMethod, pathname: `/${string}`, prefix: `/${string}` | undefined): Promise<{
25
47
  path: string[];
26
48
  procedure: AnyProcedure;
27
49
  } | undefined>;
@@ -65,5 +87,5 @@ declare class RPCHandlerCodec<T extends Context> implements StandardHandlerCodec
65
87
  encodeError(error: AnyORPCError, _procedure: AnyProcedure, _path: string[], _options: StandardHandlerHandleOptions<T>): Promisable<StandardResponse>;
66
88
  }
67
89
 
68
- export { RPCHandlerCodec as a, RPCMatcher as b };
90
+ export { RPCHandlerCodec as a, RPCMatcher as b, RPC_DEFAULT_ALLOW_METHODS as d };
69
91
  export type { RPCHandlerCodecOptions as R, RPCMatcherOptions as c };
@@ -1,8 +1,8 @@
1
1
  import { ORPCError, toORPCError, RPCSerializer, COMMON_ERROR_STATUS_MAP } from '@orpc/client';
2
2
  import { sortPlugins, getOpenTelemetryConfig, runWithSpan, toArray, matchesHttpPathPrefix, intercept, ORPC_NAME, isAsyncIteratorObject, override, traceAsyncIterator, recordSpanError, pathToHttpPath, normalizeHttpPath, value, parseEmptyableJSON } from '@orpc/shared';
3
3
  import { flattenStandardHeader, parseStandardUrl } from '@standardserver/core';
4
- import { c as createProcedureClient, u as unlazy, P as Procedure } from './server.DofEOwf-.mjs';
5
- import { w as walkProcedureContractsSync, g as getRouter, c as createContractProcedure, D as DEFAULT_SUCCESS_STATUS, a as DEFAULT_ERROR_STATUS } from './server.D4cnq66J.mjs';
4
+ import { c as createProcedureClient, u as unlazy, P as Procedure } from './server.7T18Khvf.mjs';
5
+ import { w as walkProcedureContractsSync, g as getRouter, c as createContractProcedure, D as DEFAULT_SUCCESS_STATUS, a as DEFAULT_ERROR_STATUS } from './server.DgunZU0v.mjs';
6
6
 
7
7
  class CompositeStandardHandlerPlugin {
8
8
  name = "~composite";
@@ -121,13 +121,22 @@ class OtelHandlerPlugin {
121
121
  }
122
122
  }
123
123
 
124
+ const RPC_DEFAULT_ALLOW_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
124
125
  class RPCMatcher {
125
126
  filter;
127
+ allowMethodsSet;
128
+ allowMethodsFn;
126
129
  rootRouter;
127
130
  tree = /* @__PURE__ */ new Map();
128
131
  pendingLazyRouters = /* @__PURE__ */ new Map();
129
132
  constructor(router, options = {}) {
130
133
  this.filter = options.filter ?? true;
134
+ const allowMethods = options.allowMethods ?? RPC_DEFAULT_ALLOW_METHODS;
135
+ if (typeof allowMethods !== "function") {
136
+ this.allowMethodsSet = new Set(allowMethods);
137
+ } else {
138
+ this.allowMethodsFn = allowMethods;
139
+ }
131
140
  this.rootRouter = router;
132
141
  this.index(router);
133
142
  }
@@ -147,7 +156,10 @@ class RPCMatcher {
147
156
  this.pendingLazyRouters.set(pathToHttpPath(result.path), result);
148
157
  }
149
158
  }
150
- async match(_method, pathname, prefix) {
159
+ async match(method, pathname, prefix) {
160
+ if (this.allowMethodsSet && !this.allowMethodsSet.has(method)) {
161
+ return void 0;
162
+ }
151
163
  if (pathname.length > 1 && pathname.endsWith("/")) {
152
164
  pathname = pathname.slice(0, -1);
153
165
  }
@@ -182,9 +194,13 @@ class RPCMatcher {
182
194
  if (entry === void 0) {
183
195
  return void 0;
184
196
  }
197
+ const procedure = entry.procedure ?? await this.resolveProcedure(entry);
198
+ if (this.allowMethodsFn && !this.allowMethodsFn(method, procedure, entry.path)) {
199
+ return void 0;
200
+ }
185
201
  return {
186
202
  path: entry.path,
187
- procedure: entry.procedure ?? await this.resolveProcedure(entry)
203
+ procedure
188
204
  };
189
205
  }
190
206
  resolvePendingLazyRouters(pathname) {
@@ -285,4 +301,4 @@ class RPCHandlerCodec {
285
301
  }
286
302
  }
287
303
 
288
- export { CompositeStandardHandlerPlugin as C, OtelHandlerPlugin as O, RPCHandlerCodec as R, StandardHandler as S, RPCMatcher as a };
304
+ export { CompositeStandardHandlerPlugin as C, OtelHandlerPlugin as O, RPCHandlerCodec as R, StandardHandler as S, RPCMatcher as a, RPC_DEFAULT_ALLOW_METHODS as b };
@@ -0,0 +1,69 @@
1
+ import { ORPCError } from '@orpc/client';
2
+ import { toArray } from '@orpc/shared';
3
+ import { flattenStandardHeader } from '@standardserver/core';
4
+ import { toFetchHeaders, toStandardBody } from '@standardserver/fetch';
5
+
6
+ class RequestLimitHandlerPlugin {
7
+ name = "~request-limit";
8
+ /**
9
+ * Should limit the original batch request body instead of sub-requests.
10
+ */
11
+ after = ["~batch"];
12
+ /**
13
+ * Should limit the final body size instead of the compressed one.
14
+ */
15
+ before = ["~request-compression"];
16
+ maxBodySize;
17
+ constructor(options) {
18
+ this.maxBodySize = options.maxBodySize;
19
+ }
20
+ init(options) {
21
+ const maxBodySize = this.maxBodySize;
22
+ const routingInterceptor = async ({ next, ...interceptorOptions }) => {
23
+ return next({
24
+ ...interceptorOptions,
25
+ request: {
26
+ ...interceptorOptions.request,
27
+ async resolveBody(hint) {
28
+ const contentLength = Number(
29
+ flattenStandardHeader(interceptorOptions.request.headers["content-length"])
30
+ );
31
+ if (Number.isFinite(contentLength) && contentLength > maxBodySize) {
32
+ throw new ORPCError("PAYLOAD_TOO_LARGE");
33
+ }
34
+ const stream = await interceptorOptions.request.resolveBody("octet-stream");
35
+ if (!(stream instanceof ReadableStream)) {
36
+ return stream;
37
+ }
38
+ let currentBodySize = 0;
39
+ const limitedStream = stream.pipeThrough(
40
+ new TransformStream({
41
+ transform(chunk, controller) {
42
+ currentBodySize += chunk.byteLength;
43
+ if (currentBodySize > maxBodySize) {
44
+ controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
45
+ return;
46
+ }
47
+ controller.enqueue(chunk);
48
+ }
49
+ })
50
+ );
51
+ const response = new Response(limitedStream, {
52
+ headers: toFetchHeaders(interceptorOptions.request.headers)
53
+ });
54
+ return toStandardBody(response, { hint });
55
+ }
56
+ }
57
+ });
58
+ };
59
+ return {
60
+ ...options,
61
+ routingInterceptors: [
62
+ routingInterceptor,
63
+ ...toArray(options.routingInterceptors)
64
+ ]
65
+ };
66
+ }
67
+ }
68
+
69
+ export { RequestLimitHandlerPlugin as R };