@orpc/server 2.0.0-beta.5 → 2.0.0-beta.50

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 (171) hide show
  1. package/README.md +137 -102
  2. package/dist/adapters/aws-lambda/handler.d.ts +27 -0
  3. package/dist/adapters/aws-lambda/handler.js +24 -0
  4. package/dist/adapters/aws-lambda/index.d.ts +4 -0
  5. package/dist/adapters/aws-lambda/index.js +2 -0
  6. package/dist/adapters/aws-lambda/rpc-handler.d.ts +19 -0
  7. package/dist/adapters/aws-lambda/rpc-handler.js +21 -0
  8. package/dist/adapters/crossws/handler.d.ts +47 -0
  9. package/dist/adapters/crossws/handler.js +53 -0
  10. package/dist/adapters/crossws/index.d.ts +3 -54
  11. package/dist/adapters/crossws/index.js +2 -0
  12. package/dist/adapters/crossws/rpc-handler.d.ts +16 -0
  13. package/dist/adapters/crossws/rpc-handler.js +18 -0
  14. package/dist/adapters/fastify/handler.d.ts +23 -0
  15. package/dist/adapters/fastify/handler.js +20 -0
  16. package/dist/adapters/fastify/index.d.ts +3 -0
  17. package/dist/adapters/fastify/index.js +2 -0
  18. package/dist/adapters/fastify/rpc-handler.d.ts +15 -0
  19. package/dist/adapters/fastify/rpc-handler.js +17 -0
  20. package/dist/adapters/fetch/handler.d.ts +25 -0
  21. package/dist/adapters/fetch/handler.js +22 -0
  22. package/dist/adapters/fetch/index.d.ts +4 -140
  23. package/dist/adapters/fetch/index.js +3 -0
  24. package/dist/adapters/fetch/rpc-handler.d.ts +17 -0
  25. package/dist/adapters/fetch/rpc-handler.js +19 -0
  26. package/dist/adapters/message-port/handler.d.ts +62 -0
  27. package/dist/adapters/message-port/handler.js +72 -0
  28. package/dist/adapters/message-port/index.d.ts +3 -74
  29. package/dist/adapters/message-port/index.js +2 -0
  30. package/dist/adapters/message-port/rpc-handler.d.ts +16 -0
  31. package/dist/adapters/message-port/rpc-handler.js +18 -0
  32. package/dist/adapters/node/handler.d.ts +23 -0
  33. package/dist/adapters/node/handler.js +20 -0
  34. package/dist/adapters/node/index.d.ts +4 -121
  35. package/dist/adapters/node/index.js +3 -0
  36. package/dist/adapters/node/rpc-handler.d.ts +16 -0
  37. package/dist/adapters/node/rpc-handler.js +18 -0
  38. package/dist/adapters/standard/codec.d.ts +16 -0
  39. package/dist/adapters/standard/handler.d.ts +68 -0
  40. package/dist/adapters/standard/handler.js +205 -0
  41. package/dist/adapters/standard/index.d.ts +7 -19
  42. package/dist/adapters/standard/index.js +4 -0
  43. package/dist/adapters/standard/plugin.d.ts +26 -0
  44. package/dist/adapters/standard/plugin.js +14 -0
  45. package/dist/adapters/standard/rpc-handler-codec.d.ts +42 -0
  46. package/dist/adapters/standard/rpc-handler-codec.js +50 -0
  47. package/dist/adapters/standard/rpc-matcher.d.ts +53 -0
  48. package/dist/adapters/standard/rpc-matcher.js +106 -0
  49. package/dist/adapters/standard/utils.d.ts +10 -0
  50. package/dist/adapters/standard/utils.js +8 -0
  51. package/dist/adapters/standard-peer/index.d.ts +2 -16
  52. package/dist/adapters/standard-peer/index.js +1 -0
  53. package/dist/adapters/standard-peer/utils.d.ts +6 -0
  54. package/dist/adapters/standard-peer/utils.js +13 -0
  55. package/dist/adapters/websocket/handler.d.ts +61 -0
  56. package/dist/adapters/websocket/handler.js +75 -0
  57. package/dist/adapters/websocket/index.d.ts +3 -61
  58. package/dist/adapters/websocket/index.js +2 -0
  59. package/dist/adapters/websocket/rpc-handler.d.ts +16 -0
  60. package/dist/adapters/websocket/rpc-handler.js +18 -0
  61. package/dist/builder-variants.d.ts +226 -0
  62. package/dist/builder.d.ts +126 -0
  63. package/dist/builder.js +200 -0
  64. package/dist/constants.d.ts +2 -0
  65. package/dist/constants.js +4 -0
  66. package/dist/context.d.ts +6 -0
  67. package/dist/extensions/callable.d.ts +11 -10
  68. package/dist/extensions/callable.js +8 -0
  69. package/dist/helpers/base64url.d.ts +31 -0
  70. package/dist/helpers/base64url.js +48 -0
  71. package/dist/helpers/cookie.d.ts +50 -0
  72. package/dist/helpers/cookie.js +62 -0
  73. package/dist/helpers/encryption.d.ts +28 -0
  74. package/dist/helpers/encryption.js +90 -0
  75. package/dist/helpers/index.d.ts +5 -149
  76. package/dist/helpers/index.js +4 -0
  77. package/dist/helpers/signing.d.ts +50 -0
  78. package/dist/helpers/signing.js +78 -0
  79. package/dist/implementer-procedure.d.ts +15 -0
  80. package/dist/implementer-procedure.js +37 -0
  81. package/dist/implementer-router.d.ts +34 -0
  82. package/dist/implementer-router.js +98 -0
  83. package/dist/implementer.d.ts +16 -0
  84. package/dist/implementer.js +30 -0
  85. package/dist/index.d.ts +22 -196
  86. package/dist/index.js +16 -0
  87. package/dist/lazy.d.ts +20 -0
  88. package/dist/lazy.js +20 -0
  89. package/dist/middleware-decorated.d.ts +9 -0
  90. package/dist/middleware-decorated.js +67 -0
  91. package/dist/middleware.d.ts +54 -0
  92. package/dist/plugins/batch.d.ts +102 -0
  93. package/dist/plugins/batch.js +371 -0
  94. package/dist/plugins/cors.d.ts +69 -0
  95. package/dist/plugins/cors.js +119 -0
  96. package/dist/plugins/get-method-csrf-protection.d.ts +25 -0
  97. package/dist/plugins/get-method-csrf-protection.js +48 -0
  98. package/dist/plugins/head-method.d.ts +25 -0
  99. package/dist/plugins/head-method.js +107 -0
  100. package/dist/plugins/index.d.ts +15 -197
  101. package/dist/plugins/index.js +14 -0
  102. package/dist/plugins/method-override.d.ts +40 -0
  103. package/dist/plugins/method-override.js +54 -0
  104. package/dist/plugins/request-compression.d.ts +18 -0
  105. package/dist/plugins/request-compression.js +55 -0
  106. package/dist/plugins/request-headers.d.ts +24 -0
  107. package/dist/plugins/request-headers.js +28 -0
  108. package/dist/plugins/request-limit.d.ts +32 -0
  109. package/dist/plugins/request-limit.js +61 -0
  110. package/dist/plugins/response-compression.d.ts +62 -0
  111. package/dist/plugins/response-compression.js +157 -0
  112. package/dist/plugins/response-headers.d.ts +32 -0
  113. package/dist/plugins/response-headers.js +44 -0
  114. package/dist/plugins/rethrow.d.ts +35 -0
  115. package/dist/plugins/rethrow.js +47 -0
  116. package/dist/plugins/signal-sync.d.ts +15 -0
  117. package/dist/plugins/signal-sync.js +76 -0
  118. package/dist/plugins/timeout.d.ts +39 -0
  119. package/dist/plugins/timeout.js +62 -0
  120. package/dist/procedure-client.d.ts +18 -0
  121. package/dist/procedure-client.js +158 -0
  122. package/dist/procedure-decorated.d.ts +10 -0
  123. package/dist/procedure-decorated.js +36 -0
  124. package/dist/procedure-utils.d.ts +21 -0
  125. package/dist/procedure-utils.js +48 -0
  126. package/dist/procedure.d.ts +72 -0
  127. package/dist/procedure.js +18 -0
  128. package/dist/router-client.d.ts +21 -0
  129. package/dist/router-client.js +35 -0
  130. package/dist/router-hidden.js +12 -0
  131. package/dist/router-utils.d.ts +31 -0
  132. package/dist/router-utils.js +157 -0
  133. package/dist/router.d.ts +58 -0
  134. package/package.json +95 -63
  135. package/dist/adapters/crossws/index.d.mts +0 -54
  136. package/dist/adapters/crossws/index.mjs +0 -64
  137. package/dist/adapters/fetch/index.d.mts +0 -140
  138. package/dist/adapters/fetch/index.mjs +0 -204
  139. package/dist/adapters/message-port/index.d.mts +0 -74
  140. package/dist/adapters/message-port/index.mjs +0 -90
  141. package/dist/adapters/node/index.d.mts +0 -121
  142. package/dist/adapters/node/index.mjs +0 -179
  143. package/dist/adapters/standard/index.d.mts +0 -19
  144. package/dist/adapters/standard/index.mjs +0 -8
  145. package/dist/adapters/standard-peer/index.d.mts +0 -16
  146. package/dist/adapters/standard-peer/index.mjs +0 -2
  147. package/dist/adapters/websocket/index.d.mts +0 -61
  148. package/dist/adapters/websocket/index.mjs +0 -76
  149. package/dist/extensions/callable.d.mts +0 -10
  150. package/dist/extensions/callable.mjs +0 -11
  151. package/dist/helpers/index.d.mts +0 -149
  152. package/dist/helpers/index.mjs +0 -198
  153. package/dist/index.d.mts +0 -196
  154. package/dist/index.mjs +0 -385
  155. package/dist/plugins/index.d.mts +0 -197
  156. package/dist/plugins/index.mjs +0 -357
  157. package/dist/shared/server.BL22TloH.d.mts +0 -184
  158. package/dist/shared/server.BL22TloH.d.ts +0 -184
  159. package/dist/shared/server.BsNNjG5J.d.mts +0 -61
  160. package/dist/shared/server.BwHnWUuN.mjs +0 -222
  161. package/dist/shared/server.CX4vUnDk.mjs +0 -11
  162. package/dist/shared/server.Cj5lgPuG.d.mts +0 -66
  163. package/dist/shared/server.CjOb6ItT.mjs +0 -41
  164. package/dist/shared/server.CrlKQucM.mjs +0 -233
  165. package/dist/shared/server.DBANIheG.d.ts +0 -104
  166. package/dist/shared/server.DJIkub_t.d.mts +0 -104
  167. package/dist/shared/server.D_QauotT.mjs +0 -30
  168. package/dist/shared/server.GDpX6Df8.mjs +0 -271
  169. package/dist/shared/server.Pa0F03f_.d.ts +0 -61
  170. package/dist/shared/server.W91HSRkE.mjs +0 -9
  171. package/dist/shared/server.d_2NS3g5.d.ts +0 -66
@@ -0,0 +1,61 @@
1
+ import { toArray } from "@orpc/shared";
2
+ import { ORPCError } from "@orpc/client";
3
+ import { flattenStandardHeader } from "@standard-server/core";
4
+ import { toFetchHeaders, toStandardBody } from "@standard-server/fetch";
5
+ // src/plugins/request-limit.ts
6
+ /**
7
+ * Rejects requests whose body exceeds `maxBodySize`.
8
+ *
9
+ * When used with the request compression plugin, the limit applies to the
10
+ * decompressed payload rather than the compressed wire size.
11
+ *
12
+ * @see {@link https://orpc.dev/docs/plugins/request-limit | Request Limit Plugin}
13
+ */
14
+ var RequestLimitHandlerPlugin = class {
15
+ name = "~request-limit";
16
+ /**
17
+ * Should limit the original batch request body instead of sub-requests.
18
+ */
19
+ after = ["~batch"];
20
+ /**
21
+ * Should limit the final body size instead of the compressed one.
22
+ */
23
+ before = ["~request-compression"];
24
+ maxBodySize;
25
+ constructor(options) {
26
+ this.maxBodySize = options.maxBodySize;
27
+ }
28
+ init(options) {
29
+ const maxBodySize = this.maxBodySize;
30
+ const routingInterceptor = async ({ next, ...interceptorOptions }) => {
31
+ return next({
32
+ ...interceptorOptions,
33
+ request: {
34
+ ...interceptorOptions.request,
35
+ async resolveBody(hint) {
36
+ const contentLength = Number(flattenStandardHeader(interceptorOptions.request.headers["content-length"]));
37
+ if (Number.isFinite(contentLength) && contentLength > maxBodySize) throw new ORPCError("PAYLOAD_TOO_LARGE");
38
+ const stream = await interceptorOptions.request.resolveBody("octet-stream");
39
+ if (!(stream instanceof ReadableStream)) return stream;
40
+ let currentBodySize = 0;
41
+ const limitedStream = stream.pipeThrough(new TransformStream({ 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
+ const response = new Response(limitedStream, { headers: toFetchHeaders(interceptorOptions.request.headers) });
50
+ return toStandardBody(response, { hint });
51
+ }
52
+ }
53
+ });
54
+ };
55
+ return {
56
+ ...options,
57
+ routingInterceptors: [routingInterceptor, ...toArray(options.routingInterceptors)]
58
+ };
59
+ }
60
+ };
61
+ export { RequestLimitHandlerPlugin };
@@ -0,0 +1,62 @@
1
+ import "../adapters/standard/index.js";
2
+ import { StandardHandlerPlugin } from "../adapters/standard/plugin.js";
3
+ import { StandardHandlerOptions, StandardHandlerRoutingInterceptorOptions } from "../adapters/standard/handler.js";
4
+ import { Context } from "../context.js";
5
+ import { CompressionEncoding } from "@orpc/shared";
6
+ import { StandardHeaders, StandardResponse } from "@standard-server/core";
7
+ export interface ResponseCompressionHandlerPluginOptions<T extends Context> {
8
+ /**
9
+ * The compression schemes to use for response compression.
10
+ * Schemes are prioritized by their order in this array and
11
+ * only applied if the client supports them (via Accept-Encoding).
12
+ *
13
+ * @default ['gzip', 'deflate']
14
+ */
15
+ encodings?: readonly CompressionEncoding[] | undefined;
16
+ /**
17
+ * The minimum response size in bytes required to trigger compression.
18
+ * Responses smaller than this threshold will not be compressed to avoid overhead.
19
+ * If the response size cannot be determined, compression will still be applied.
20
+ *
21
+ * @default 1024 (1KB)
22
+ */
23
+ threshold?: number | undefined;
24
+ /**
25
+ * Determines whether a response with the given Content-Type should be compressed.
26
+ * Only consulted for binary transfers (streams, files, and file form-data parts).
27
+ * Also receives the routing interceptor options for per-request decisions.
28
+ *
29
+ * @default isCompressibleContentType (covers common text-based formats)
30
+ */
31
+ isCompressibleContentType?: ((contentType: string | null | undefined, options: StandardHandlerRoutingInterceptorOptions<T>) => boolean) | undefined;
32
+ }
33
+ /**
34
+ * Compresses response bodies based on the client's Accept-Encoding header.
35
+ * Works at the standard handler level, so it supports all adapters.
36
+ *
37
+ * @see {@link https://orpc.dev/docs/plugins/response-compression | Response Compression Plugin}
38
+ */
39
+ export declare class ResponseCompressionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
40
+ name: string;
41
+ /**
42
+ * Compression should be done after batching, to compress the final response.
43
+ * Compression should also be done after response headers are set, to access final headers like Content-Type and Cache-Control.
44
+ */
45
+ after: string[];
46
+ private readonly encodings;
47
+ private readonly threshold;
48
+ private readonly isCompressibleContentType;
49
+ constructor(options?: ResponseCompressionHandlerPluginOptions<T>);
50
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
51
+ }
52
+ /**
53
+ * Picks the first of `encodings` the client accepts, or `undefined` when the response
54
+ * must be sent as is because it is already encoded, partial, or forbids transforms.
55
+ * Shared by the response compression plugins so they agree on when to compress.
56
+ */
57
+ export declare function negotiateResponseCompressionEncoding<TEncoding extends string>(response: StandardResponse, requestHeaders: StandardHeaders, encodings: readonly TEncoding[]): TEncoding | undefined;
58
+ /**
59
+ * The headers of a response whose body is compressed with `encoding`.
60
+ * Shared by the response compression plugins so compressed responses carry the same headers.
61
+ */
62
+ export declare function toCompressedHeaders(headers: StandardHeaders, encoding: string): StandardHeaders;
@@ -0,0 +1,157 @@
1
+ import { estimateCompressibleFormDataSize, estimateUtf8ByteLength, isAcceptableEncoding, isAsyncIteratorObject, isCompressibleContentType, isNoTransformCacheControl, parseAcceptEncodingQualities, toArray, varyByAcceptEncoding } from "@orpc/shared";
2
+ import { flattenStandardHeader, generateContentDisposition } from "@standard-server/core";
3
+ import { toFetchBody } from "@standard-server/fetch";
4
+ // src/plugins/response-compression.ts
5
+ /**
6
+ * Compresses response bodies based on the client's Accept-Encoding header.
7
+ * Works at the standard handler level, so it supports all adapters.
8
+ *
9
+ * @see {@link https://orpc.dev/docs/plugins/response-compression | Response Compression Plugin}
10
+ */
11
+ var ResponseCompressionHandlerPlugin = class {
12
+ name = "~response-compression";
13
+ /**
14
+ * Compression should be done after batching, to compress the final response.
15
+ * Compression should also be done after response headers are set, to access final headers like Content-Type and Cache-Control.
16
+ */
17
+ after = ["~batch", "~response-headers"];
18
+ encodings;
19
+ threshold;
20
+ isCompressibleContentType;
21
+ constructor(options = {}) {
22
+ this.encodings = options.encodings ?? ["gzip", "deflate"];
23
+ this.threshold = options.threshold ?? 1024;
24
+ this.isCompressibleContentType = options.isCompressibleContentType ?? isCompressibleContentType;
25
+ }
26
+ init(options) {
27
+ const routingInterceptor = async ({ next, ...interceptorOptions }) => {
28
+ const result = await next();
29
+ if (!result.matched) return result;
30
+ const response = result.response;
31
+ const encoding = negotiateResponseCompressionEncoding(response, interceptorOptions.request.headers, this.encodings);
32
+ if (encoding === void 0) return result;
33
+ const body = response.body;
34
+ const headers = response.headers;
35
+ if (body instanceof ReadableStream) {
36
+ const contentLength = Number(flattenStandardHeader(headers["content-length"]));
37
+ if ((!Number.isFinite(contentLength) || contentLength >= this.threshold) && this.isCompressibleContentType(flattenStandardHeader(headers["content-type"]), interceptorOptions)) return {
38
+ ...result,
39
+ response: {
40
+ ...response,
41
+ body: body.pipeThrough(new CompressionStream(encoding)),
42
+ headers: toCompressedHeaders(headers, encoding)
43
+ }
44
+ };
45
+ } else if (body instanceof Blob) {
46
+ const contentType = headers["content-type"] ?? body.type;
47
+ if ((!Number.isFinite(body.size) || body.size >= this.threshold) && this.isCompressibleContentType(flattenStandardHeader(contentType), interceptorOptions)) {
48
+ const contentDisposition = headers["content-disposition"] ?? generateContentDisposition(body instanceof File ? body.name : "blob");
49
+ return {
50
+ ...result,
51
+ response: {
52
+ ...response,
53
+ body: body.stream().pipeThrough(new CompressionStream(encoding)),
54
+ headers: {
55
+ ...toCompressedHeaders(headers, encoding),
56
+ "standard-server": headers["standard-server"] ?? "file",
57
+ "content-type": contentType,
58
+ "content-disposition": contentDisposition
59
+ }
60
+ }
61
+ };
62
+ }
63
+ } else if (body instanceof FormData) {
64
+ if (estimateCompressibleFormDataSize(body, (contentType) => this.isCompressibleContentType(contentType, interceptorOptions)) >= this.threshold) {
65
+ const res = new Response(body);
66
+ const compressedStream = res.body.pipeThrough(new CompressionStream(encoding));
67
+ return {
68
+ ...result,
69
+ response: {
70
+ ...response,
71
+ body: compressedStream,
72
+ headers: {
73
+ ...toCompressedHeaders(headers, encoding),
74
+ "standard-server": [],
75
+ "content-type": res.headers.get("content-type")
76
+ }
77
+ }
78
+ };
79
+ }
80
+ } else if (body instanceof URLSearchParams) {
81
+ const string = body.toString();
82
+ if (estimateUtf8ByteLength(string) >= this.threshold) {
83
+ const [, bodyHeaders] = toFetchBody(body, headers);
84
+ return {
85
+ ...result,
86
+ response: {
87
+ ...response,
88
+ body: new Blob([string]).stream().pipeThrough(new CompressionStream(encoding)),
89
+ headers: {
90
+ ...toCompressedHeaders(bodyHeaders, encoding),
91
+ "standard-server": bodyHeaders["standard-server"] ?? []
92
+ }
93
+ }
94
+ };
95
+ }
96
+ } else if (body !== void 0 && !isAsyncIteratorObject(body)) {
97
+ const [string, bodyHeaders] = toFetchBody(body, headers);
98
+ if (estimateUtf8ByteLength(string) >= this.threshold) return {
99
+ ...result,
100
+ response: {
101
+ ...response,
102
+ body: new Blob([string]).stream().pipeThrough(new CompressionStream(encoding)),
103
+ headers: {
104
+ ...toCompressedHeaders(bodyHeaders, encoding),
105
+ "standard-server": bodyHeaders["standard-server"] ?? []
106
+ }
107
+ }
108
+ };
109
+ }
110
+ return result;
111
+ };
112
+ return {
113
+ ...options,
114
+ routingInterceptors: [routingInterceptor, ...toArray(options.routingInterceptors)]
115
+ };
116
+ }
117
+ };
118
+ /**
119
+ * Picks the first of `encodings` the client accepts, or `undefined` when the response
120
+ * must be sent as is because it is already encoded, partial, or forbids transforms.
121
+ * Shared by the response compression plugins so they agree on when to compress.
122
+ */
123
+ function negotiateResponseCompressionEncoding(response, requestHeaders, encodings) {
124
+ if (flattenStandardHeader(response.headers["content-encoding"]) !== void 0) return;
125
+ /**
126
+ * A partial response body is a byte range of the identity representation, so compressing it
127
+ * would leave `Content-Range` describing offsets the client never receives.
128
+ */
129
+ if (response.status === 206 || flattenStandardHeader(response.headers["content-range"]) !== void 0) return;
130
+ if (isNoTransformCacheControl(flattenStandardHeader(response.headers["cache-control"]))) return;
131
+ const acceptEncodings = parseAcceptEncodingQualities(flattenStandardHeader(requestHeaders["accept-encoding"]));
132
+ return encodings.find((encoding) => isAcceptableEncoding(acceptEncodings, encoding));
133
+ }
134
+ /**
135
+ * The headers of a response whose body is compressed with `encoding`.
136
+ * Shared by the response compression plugins so compressed responses carry the same headers.
137
+ */
138
+ function toCompressedHeaders(headers, encoding) {
139
+ return {
140
+ ...headers,
141
+ "content-length": [],
142
+ "content-encoding": encoding,
143
+ "vary": varyByAcceptEncoding(flattenStandardHeader(headers.vary)),
144
+ /**
145
+ * A strong tag shared with the identity bytes would let `If-Range` splice them after compressed ones.
146
+ * A weak tag still revalidates through `If-None-Match`, and the compressed body serves no ranges.
147
+ *
148
+ * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-etag
149
+ */
150
+ "etag": weakenEtag(flattenStandardHeader(headers.etag)),
151
+ "accept-ranges": []
152
+ };
153
+ }
154
+ function weakenEtag(etag) {
155
+ return etag === void 0 || etag.startsWith("W/") ? etag : `W/${etag}`;
156
+ }
157
+ export { ResponseCompressionHandlerPlugin, negotiateResponseCompressionEncoding, toCompressedHeaders };
@@ -0,0 +1,32 @@
1
+ import { StandardHandlerPlugin } from "../adapters/standard/plugin.js";
2
+ import { StandardHandlerOptions } from "../adapters/standard/handler.js";
3
+ import "../adapters/standard/index.js";
4
+ /**
5
+ * The context shape into which the Response Headers Plugin injects `resHeaders`.
6
+ *
7
+ * @see {@link https://orpc.dev/docs/plugins/response-headers | Response Headers Plugin}
8
+ */
9
+ export interface ResponseHeadersHandlerPluginContext {
10
+ /**
11
+ * Response headers as a Headers instance. This is injected by the Response Headers Plugin.
12
+ * When set before the response is sent, these headers will be included in the response. If not set, no additional headers will be added.
13
+ */
14
+ resHeaders?: Headers | undefined;
15
+ }
16
+ /**
17
+ * The Response Headers Plugin allows you to set response headers in oRPC.
18
+ * It injects a resHeaders instance into the context, enabling you to modify response headers easily.
19
+ *
20
+ * @remarks
21
+ * **Warning**: In a `streaming` batch, only the cookies of the first procedure to respond reach the client.
22
+ *
23
+ * @see {@link https://orpc.dev/docs/plugins/response-headers | Response Headers Plugin}
24
+ */
25
+ export declare class ResponseHeadersHandlerPlugin<T extends ResponseHeadersHandlerPluginContext> implements StandardHandlerPlugin<T> {
26
+ name: string;
27
+ /**
28
+ * Interceptors should run after batch interceptors so headers are applied to each sub-response.
29
+ */
30
+ before: string[];
31
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
32
+ }
@@ -0,0 +1,44 @@
1
+ import { toArray } from "@orpc/shared";
2
+ import { mergeStandardHeaders } from "@standard-server/core";
3
+ import { toStandardHeaders } from "@standard-server/fetch";
4
+ // src/plugins/response-headers.ts
5
+ /**
6
+ * The Response Headers Plugin allows you to set response headers in oRPC.
7
+ * It injects a resHeaders instance into the context, enabling you to modify response headers easily.
8
+ *
9
+ * @remarks
10
+ * **Warning**: In a `streaming` batch, only the cookies of the first procedure to respond reach the client.
11
+ *
12
+ * @see {@link https://orpc.dev/docs/plugins/response-headers | Response Headers Plugin}
13
+ */
14
+ var ResponseHeadersHandlerPlugin = class {
15
+ name = "~response-headers";
16
+ /**
17
+ * Interceptors should run after batch interceptors so headers are applied to each sub-response.
18
+ */
19
+ before = ["~batch"];
20
+ init(options) {
21
+ return {
22
+ ...options,
23
+ routingInterceptors: [async (interceptorOptions) => {
24
+ const resHeaders = new Headers(interceptorOptions.context.resHeaders);
25
+ const result = await interceptorOptions.next({
26
+ ...interceptorOptions,
27
+ context: {
28
+ ...interceptorOptions.context,
29
+ resHeaders
30
+ }
31
+ });
32
+ if (!result.response) return result;
33
+ return {
34
+ ...result,
35
+ response: {
36
+ ...result.response,
37
+ headers: mergeStandardHeaders(result.response.headers, toStandardHeaders(resHeaders))
38
+ }
39
+ };
40
+ }, ...toArray(options.routingInterceptors)]
41
+ };
42
+ }
43
+ };
44
+ export { ResponseHeadersHandlerPlugin };
@@ -0,0 +1,35 @@
1
+ import "../adapters/standard/index.js";
2
+ import { StandardHandlerPlugin } from "../adapters/standard/plugin.js";
3
+ import { StandardHandlerInterceptorOptions, StandardHandlerOptions } from "../adapters/standard/handler.js";
4
+ import { Context } from "../context.js";
5
+ import { ThrowableError } from "@orpc/shared";
6
+ export interface RethrowHandlerPluginOptions<T extends Context> {
7
+ /**
8
+ * Decide which errors should be rethrown.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const rethrowPlugin = new RethrowHandlerPlugin({
13
+ * filter: (error) => {
14
+ * // Rethrow all non-ORPCError errors
15
+ * return !(error instanceof ORPCError)
16
+ * }
17
+ * })
18
+ * ```
19
+ */
20
+ filter: (error: ThrowableError, options: StandardHandlerInterceptorOptions<T>) => boolean;
21
+ }
22
+ /**
23
+ * The plugin can bypass oRPC's built-in error handling
24
+ * and rethrow matching errors directly to your framework's error handling mechanism
25
+ * (e.g., NestJS exception filters, Express error middleware).
26
+ *
27
+ * @see {@link https://orpc.dev/docs/plugins/rethrow | Rethrow Handler Plugin}
28
+ */
29
+ export declare class RethrowHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
30
+ name: string;
31
+ private readonly filter;
32
+ private readonly CONTEXT_SYMBOL;
33
+ constructor(options: RethrowHandlerPluginOptions<T>);
34
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
35
+ }
@@ -0,0 +1,47 @@
1
+ import { toArray } from "@orpc/shared";
2
+ // src/plugins/rethrow.ts
3
+ /**
4
+ * The plugin can bypass oRPC's built-in error handling
5
+ * and rethrow matching errors directly to your framework's error handling mechanism
6
+ * (e.g., NestJS exception filters, Express error middleware).
7
+ *
8
+ * @see {@link https://orpc.dev/docs/plugins/rethrow | Rethrow Handler Plugin}
9
+ */
10
+ var RethrowHandlerPlugin = class {
11
+ name = "~rethrow";
12
+ filter;
13
+ CONTEXT_SYMBOL = Symbol("ORPC_RETHROW_HANDLER_PLUGIN_CONTEXT");
14
+ constructor(options) {
15
+ this.filter = options.filter;
16
+ }
17
+ init(options) {
18
+ const routingInterceptor = async (options) => {
19
+ const pluginContext = {};
20
+ const result = await options.next({
21
+ ...options,
22
+ context: {
23
+ ...options.context,
24
+ [this.CONTEXT_SYMBOL]: pluginContext
25
+ }
26
+ });
27
+ if (pluginContext.error) throw pluginContext.error.value;
28
+ return result;
29
+ };
30
+ const interceptor = async (options) => {
31
+ const pluginContext = options.context[this.CONTEXT_SYMBOL];
32
+ if (!pluginContext) throw new TypeError("[RethrowHandlerPlugin] Rethrow handler context has been corrupted or modified by another plugin or interceptor");
33
+ try {
34
+ return await options.next();
35
+ } catch (error) {
36
+ if (this.filter(error, options)) pluginContext.error = { value: error };
37
+ throw error;
38
+ }
39
+ };
40
+ return {
41
+ ...options,
42
+ routingInterceptors: [...toArray(options.routingInterceptors), routingInterceptor],
43
+ interceptors: [interceptor, ...toArray(options.interceptors)]
44
+ };
45
+ }
46
+ };
47
+ export { RethrowHandlerPlugin };
@@ -0,0 +1,15 @@
1
+ import "../adapters/standard/index.js";
2
+ import { StandardHandlerPlugin } from "../adapters/standard/plugin.js";
3
+ import { StandardHandlerOptions } from "../adapters/standard/handler.js";
4
+ import { Context } from "../context.js";
5
+ /**
6
+ * Keeps the request signal and a streaming output (async iterator object or readable stream) in sync:
7
+ * cancelling the output aborts the signal with an `AbortError`,
8
+ * and aborting the signal ends the output with the abort reason and cancels it.
9
+ *
10
+ * @see {@link https://orpc.dev/docs/plugins/signal-sync | Signal Sync Plugin}
11
+ */
12
+ export declare class SignalSyncHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
13
+ name: string;
14
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
15
+ }
@@ -0,0 +1,76 @@
1
+ import { AbortError, AsyncIteratorClass, anyAbortSignal, isAsyncIteratorObject, listenAbort, override, runWithSignal, toArray } from "@orpc/shared";
2
+ // src/plugins/signal-sync.ts
3
+ /**
4
+ * Keeps the request signal and a streaming output (async iterator object or readable stream) in sync:
5
+ * cancelling the output aborts the signal with an `AbortError`,
6
+ * and aborting the signal ends the output with the abort reason and cancels it.
7
+ *
8
+ * @see {@link https://orpc.dev/docs/plugins/signal-sync | Signal Sync Plugin}
9
+ */
10
+ var SignalSyncHandlerPlugin = class {
11
+ name = "~signal-sync";
12
+ init(options) {
13
+ const clientInterceptor = async (interceptorOptions) => {
14
+ const controller = new AbortController();
15
+ const signal = anyAbortSignal([interceptorOptions.signal, controller.signal]);
16
+ const cancel = () => controller.abort(new AbortError("The response body was cancelled"));
17
+ const output = await interceptorOptions.next({
18
+ ...interceptorOptions,
19
+ signal
20
+ });
21
+ if (isAsyncIteratorObject(output)) return override(output, syncAsyncIterator(output, signal, cancel));
22
+ if (output instanceof ReadableStream) return override(output, syncReadableStream(output, signal, cancel));
23
+ return output;
24
+ };
25
+ return {
26
+ ...options,
27
+ clientInterceptors: [clientInterceptor, ...toArray(options.clientInterceptors)]
28
+ };
29
+ }
30
+ };
31
+ function syncAsyncIterator(iterator, signal, cancel) {
32
+ let cancelling;
33
+ const unlisten = listenAbort(signal, () => {
34
+ cancelling = (async () => iterator.return?.())();
35
+ cancelling.catch(() => {});
36
+ });
37
+ return new AsyncIteratorClass(() => runWithSignal(signal, () => iterator.next()), async ({ kind }) => {
38
+ if (kind === "cancelled") {
39
+ cancel();
40
+ await cancelling;
41
+ }
42
+ unlisten();
43
+ });
44
+ }
45
+ function syncReadableStream(stream, signal, cancel) {
46
+ const reader = stream.getReader();
47
+ let cancelling;
48
+ let unlisten;
49
+ return new ReadableStream({
50
+ start(controller) {
51
+ unlisten = listenAbort(signal, (reason) => {
52
+ controller.error(reason);
53
+ cancelling = reader.cancel(reason);
54
+ cancelling.catch(() => {});
55
+ });
56
+ },
57
+ async pull(controller) {
58
+ try {
59
+ const { done, value } = await reader.read();
60
+ if (signal.aborted) return;
61
+ if (done) {
62
+ unlisten();
63
+ controller.close();
64
+ } else controller.enqueue(value);
65
+ } catch (error) {
66
+ unlisten();
67
+ throw error;
68
+ }
69
+ },
70
+ async cancel() {
71
+ cancel();
72
+ await cancelling;
73
+ }
74
+ }, { highWaterMark: 0 });
75
+ }
76
+ export { SignalSyncHandlerPlugin };
@@ -0,0 +1,39 @@
1
+ import "../adapters/standard/index.js";
2
+ import { StandardHandlerPlugin } from "../adapters/standard/plugin.js";
3
+ import { StandardHandlerInterceptorOptions, StandardHandlerOptions } from "../adapters/standard/handler.js";
4
+ import { Context } from "../context.js";
5
+ import { Value } from "@orpc/shared";
6
+ export interface TimeoutHandlerPluginOptions<T extends Context> {
7
+ /**
8
+ * Timeout in milliseconds before the request signal is aborted.
9
+ * This only covers producing the response, use `streamingTimeout`
10
+ * to limit streaming response bodies.
11
+ * Use `null` or `undefined` to disable the timeout.
12
+ */
13
+ timeout: Value<number | null | undefined, [options: StandardHandlerInterceptorOptions<T>]>;
14
+ /**
15
+ * Timeout in milliseconds for the full duration of a streaming response body
16
+ * (async iterator object or readable stream), measured from when the response is produced.
17
+ * When exceeded, the request signal is aborted and the body ends
18
+ * once its producer honors the signal.
19
+ * Usually higher than `timeout`.
20
+ * Use `null` or `undefined` to disable the timeout.
21
+ *
22
+ * @default undefined (streaming responses run without limit)
23
+ */
24
+ streamingTimeout?: Value<number | null | undefined, [options: StandardHandlerInterceptorOptions<T>]> | undefined;
25
+ }
26
+ /**
27
+ * The Timeout Handler Plugin aborts the request signal with an `AbortError`
28
+ * when handling exceeds a configured timeout. It only aborts the signal,
29
+ * the procedure must honor it to stop early and produce the response.
30
+ *
31
+ * @see {@link https://orpc.dev/docs/plugins/timeout | Timeout Plugin}
32
+ */
33
+ export declare class TimeoutHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
34
+ private readonly timeout;
35
+ private readonly streamingTimeout;
36
+ name: string;
37
+ constructor(options: NoInfer<TimeoutHandlerPluginOptions<T>>);
38
+ init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T>;
39
+ }
@@ -0,0 +1,62 @@
1
+ import { AbortError, anyAbortSignal, isAsyncIteratorObject, override, toArray, value, wrapAsyncIterator, wrapReadableStream } from "@orpc/shared";
2
+ // src/plugins/timeout.ts
3
+ /**
4
+ * The Timeout Handler Plugin aborts the request signal with an `AbortError`
5
+ * when handling exceeds a configured timeout. It only aborts the signal,
6
+ * the procedure must honor it to stop early and produce the response.
7
+ *
8
+ * @see {@link https://orpc.dev/docs/plugins/timeout | Timeout Plugin}
9
+ */
10
+ var TimeoutHandlerPlugin = class {
11
+ timeout;
12
+ streamingTimeout;
13
+ name = "~timeout";
14
+ constructor(options) {
15
+ this.timeout = options.timeout;
16
+ this.streamingTimeout = options.streamingTimeout;
17
+ }
18
+ init(options) {
19
+ const interceptor = async (interceptorOptions) => {
20
+ const timeoutMs = value(this.timeout, interceptorOptions);
21
+ const streamingTimeoutMs = value(this.streamingTimeout, interceptorOptions);
22
+ const hasTimeout = timeoutMs !== null && timeoutMs !== void 0;
23
+ const hasStreamingTimeout = streamingTimeoutMs !== null && streamingTimeoutMs !== void 0;
24
+ if (!hasTimeout && !hasStreamingTimeout) return interceptorOptions.next();
25
+ const controller = new AbortController();
26
+ const abortWithTimeout = (ms) => {
27
+ controller.abort(new AbortError(`Request timed out after ${ms}ms`));
28
+ };
29
+ const timeoutId = hasTimeout ? setTimeout(abortWithTimeout, timeoutMs, timeoutMs) : void 0;
30
+ const signal = anyAbortSignal([interceptorOptions.request.signal, controller.signal]);
31
+ try {
32
+ const response = await interceptorOptions.next({
33
+ ...interceptorOptions,
34
+ request: {
35
+ ...interceptorOptions.request,
36
+ signal
37
+ }
38
+ });
39
+ if (hasStreamingTimeout) {
40
+ const body = response.body;
41
+ const isIterator = isAsyncIteratorObject(body);
42
+ if (isIterator || body instanceof ReadableStream) {
43
+ const streamingTimeoutId = setTimeout(abortWithTimeout, streamingTimeoutMs, streamingTimeoutMs);
44
+ const onFinish = () => clearTimeout(streamingTimeoutId);
45
+ return {
46
+ ...response,
47
+ body: isIterator ? override(body, wrapAsyncIterator(body, { onFinish })) : override(body, wrapReadableStream(body, { onFinish }))
48
+ };
49
+ }
50
+ }
51
+ return response;
52
+ } finally {
53
+ clearTimeout(timeoutId);
54
+ }
55
+ };
56
+ return {
57
+ ...options,
58
+ interceptors: [interceptor, ...toArray(options.interceptors)]
59
+ };
60
+ }
61
+ };
62
+ export { TimeoutHandlerPlugin };
@@ -0,0 +1,18 @@
1
+ import { Context } from "./context.js";
2
+ import { Lazyable } from "./lazy.js";
3
+ import { Procedure, ProcedureHandlerOptions } from "./procedure.js";
4
+ import { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, ORPCErrorConstructorMap, ORPCErrorFromErrorMap } from "@orpc/contract";
5
+ import { Interceptor, MaybeOptionalOptions, Promisable, PromiseWithError, ThrowableError as ThrowableError$1, Value } from "@orpc/shared";
6
+ import { Client, ClientContext } from "@orpc/client";
7
+ export type ProcedureClient<TClientContext extends ClientContext, TInputSchema extends AnySchema, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> = Client<TClientContext, InferSchemaInput<TInputSchema>, InferSchemaOutput<TOutputSchema>, ORPCErrorFromErrorMap<TErrorMap> | ThrowableError$1>;
8
+ export interface ProcedureClientInterceptorOptions<TInitialContext extends Context, TErrorMap extends ErrorMap> extends ProcedureHandlerOptions<TInitialContext, unknown, ORPCErrorConstructorMap<TErrorMap>> {}
9
+ export type ProcedureClientInterceptor<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> = Interceptor<ProcedureClientInterceptorOptions<TInitialContext, TErrorMap>, PromiseWithError<InferSchemaOutput<TOutputSchema>, ORPCErrorFromErrorMap<TErrorMap> | ThrowableError$1>>;
10
+ export type ProcedureClientOptions<TInitialContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap, TClientContext extends ClientContext> = {
11
+ path?: string[] | undefined;
12
+ interceptors?: ProcedureClientInterceptor<TInitialContext, TOutputSchema, TErrorMap>[] | undefined;
13
+ } & (object extends TInitialContext ? {
14
+ context?: Value<Promisable<TInitialContext>, [clientContext: TClientContext]> | undefined;
15
+ } : {
16
+ context: Value<Promisable<TInitialContext>, [clientContext: TClientContext]>;
17
+ });
18
+ export declare function createProcedureClient<TInitialContext extends Context, TInputSchema extends AnySchema, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap, TClientContext extends ClientContext = object>(lazyableProcedure: Lazyable<Procedure<TInitialContext, any, TInputSchema, TOutputSchema, TErrorMap>>, ...rest: MaybeOptionalOptions<ProcedureClientOptions<TInitialContext, TOutputSchema, TErrorMap, TClientContext>>): ProcedureClient<TClientContext, TInputSchema, TOutputSchema, TErrorMap>;