@orpc/server 1.14.6 → 2.0.0-beta.10
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.
- package/README.md +78 -137
- package/dist/adapters/crossws/index.d.mts +42 -21
- package/dist/adapters/crossws/index.d.ts +42 -21
- package/dist/adapters/crossws/index.mjs +37 -18
- package/dist/adapters/fetch/index.d.mts +83 -67
- package/dist/adapters/fetch/index.d.ts +83 -67
- package/dist/adapters/fetch/index.mjs +131 -106
- package/dist/adapters/message-port/index.d.mts +51 -34
- package/dist/adapters/message-port/index.d.ts +51 -34
- package/dist/adapters/message-port/index.mjs +73 -38
- package/dist/adapters/node/index.d.mts +82 -60
- package/dist/adapters/node/index.d.ts +82 -60
- package/dist/adapters/node/index.mjs +127 -98
- package/dist/adapters/standard/index.d.mts +16 -18
- package/dist/adapters/standard/index.d.ts +16 -18
- package/dist/adapters/standard/index.mjs +5 -5
- package/dist/adapters/standard-peer/index.d.mts +12 -14
- package/dist/adapters/standard-peer/index.d.ts +12 -14
- package/dist/adapters/standard-peer/index.mjs +2 -21
- package/dist/adapters/websocket/index.d.mts +50 -36
- package/dist/adapters/websocket/index.d.ts +50 -36
- package/dist/adapters/websocket/index.mjs +55 -42
- package/dist/extensions/callable.d.mts +10 -0
- package/dist/extensions/callable.d.ts +10 -0
- package/dist/extensions/callable.mjs +11 -0
- package/dist/helpers/index.d.mts +2 -2
- package/dist/helpers/index.d.ts +2 -2
- package/dist/helpers/index.mjs +1 -1
- package/dist/index.d.mts +163 -770
- package/dist/index.d.ts +163 -770
- package/dist/index.mjs +296 -403
- package/dist/plugins/index.d.mts +120 -127
- package/dist/plugins/index.d.ts +120 -127
- package/dist/plugins/index.mjs +252 -223
- package/dist/shared/server.BL22TloH.d.mts +184 -0
- package/dist/shared/server.BL22TloH.d.ts +184 -0
- package/dist/shared/server.BsNNjG5J.d.mts +61 -0
- package/dist/shared/server.BwHnWUuN.mjs +222 -0
- package/dist/shared/server.CX4vUnDk.mjs +11 -0
- package/dist/shared/server.Cj5lgPuG.d.mts +66 -0
- package/dist/shared/server.CjOb6ItT.mjs +41 -0
- package/dist/shared/server.CrlKQucM.mjs +233 -0
- package/dist/shared/server.DBANIheG.d.ts +104 -0
- package/dist/shared/server.DJIkub_t.d.mts +104 -0
- package/dist/shared/server.D_QauotT.mjs +30 -0
- package/dist/shared/server.GDpX6Df8.mjs +271 -0
- package/dist/shared/server.Pa0F03f_.d.ts +61 -0
- package/dist/shared/{server.DZ5BIITo.mjs → server.W91HSRkE.mjs} +2 -2
- package/dist/shared/server.d_2NS3g5.d.ts +66 -0
- package/package.json +26 -55
- package/dist/adapters/aws-lambda/index.d.mts +0 -46
- package/dist/adapters/aws-lambda/index.d.ts +0 -46
- package/dist/adapters/aws-lambda/index.mjs +0 -40
- package/dist/adapters/bun-ws/index.d.mts +0 -36
- package/dist/adapters/bun-ws/index.d.ts +0 -36
- package/dist/adapters/bun-ws/index.mjs +0 -47
- package/dist/adapters/fastify/index.d.mts +0 -53
- package/dist/adapters/fastify/index.d.ts +0 -53
- package/dist/adapters/fastify/index.mjs +0 -52
- package/dist/adapters/ws/index.d.mts +0 -31
- package/dist/adapters/ws/index.d.ts +0 -31
- package/dist/adapters/ws/index.mjs +0 -37
- package/dist/hibernation/index.d.mts +0 -44
- package/dist/hibernation/index.d.ts +0 -44
- package/dist/hibernation/index.mjs +0 -65
- package/dist/shared/server.7cEtMB30.d.ts +0 -74
- package/dist/shared/server.B8gYOD5g.d.mts +0 -12
- package/dist/shared/server.BqadksTP.d.mts +0 -74
- package/dist/shared/server.C8_sRzQB.d.mts +0 -42
- package/dist/shared/server.ChUyt5-i.d.mts +0 -32
- package/dist/shared/server.ChyoA9XY.d.ts +0 -42
- package/dist/shared/server.DEBcqOjg.mjs +0 -418
- package/dist/shared/server.EfTOZ2Q7.d.ts +0 -12
- package/dist/shared/server.TEVCLCFC.mjs +0 -39
- package/dist/shared/server.ZxHCEN1h.mjs +0 -226
- package/dist/shared/server.qKsRrdxW.d.mts +0 -193
- package/dist/shared/server.qKsRrdxW.d.ts +0 -193
- package/dist/shared/server.yoEB3Fx4.d.ts +0 -32
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,124 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import '@orpc/contract';
|
|
1
|
+
import { toArray, value } from '@orpc/shared';
|
|
2
|
+
import { flattenStandardHeader, parseStandardUrl, mergeStandardHeaders } from '@standardserver/core';
|
|
3
|
+
import { isClientPeerSendMessage, ServerPeer, encodePeerMessage } from '@standardserver/peer';
|
|
4
|
+
export { C as CSRFGuardHandlerPlugin } from '../shared/server.D_QauotT.mjs';
|
|
5
|
+
import { toFetchHeaders, toStandardHeaders } from '@standardserver/fetch';
|
|
6
|
+
import '@orpc/client';
|
|
8
7
|
|
|
9
8
|
class BatchHandlerPlugin {
|
|
9
|
+
name = "~batch";
|
|
10
|
+
/**
|
|
11
|
+
* Run batch interceptors before OpenTelemetry interceptors
|
|
12
|
+
* so each subrequest gets its own span instead of sharing one batch-level span.
|
|
13
|
+
*/
|
|
14
|
+
after = ["~opentelemetry"];
|
|
10
15
|
maxSize;
|
|
11
|
-
|
|
16
|
+
mapSubrequest;
|
|
12
17
|
successStatus;
|
|
13
18
|
headers;
|
|
14
|
-
order = 5e6;
|
|
15
19
|
constructor(options = {}) {
|
|
16
20
|
this.maxSize = options.maxSize ?? 10;
|
|
17
|
-
this.
|
|
18
|
-
...
|
|
21
|
+
this.mapSubrequest = options.mapSubrequest ?? ((subRequest, { request: batchRequest }) => ({
|
|
22
|
+
...subRequest,
|
|
19
23
|
headers: {
|
|
20
24
|
...batchRequest.headers,
|
|
21
|
-
...
|
|
25
|
+
...subRequest.headers,
|
|
26
|
+
"orpc-batch": void 0
|
|
27
|
+
// useful in case batch plugin is used multiple times
|
|
22
28
|
}
|
|
23
29
|
}));
|
|
24
30
|
this.successStatus = options.successStatus ?? 207;
|
|
25
31
|
this.headers = options.headers ?? {};
|
|
26
32
|
}
|
|
27
33
|
init(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return options2.next();
|
|
34
|
+
const routingInterceptor = async (interceptorOptions) => {
|
|
35
|
+
const batchHeader = flattenStandardHeader(interceptorOptions.request.headers["orpc-batch"]);
|
|
36
|
+
if (batchHeader === void 0) {
|
|
37
|
+
return interceptorOptions.next();
|
|
33
38
|
}
|
|
34
|
-
|
|
39
|
+
const mode = batchHeader === "buffered" ? "buffered" : "streaming";
|
|
40
|
+
let messages;
|
|
35
41
|
try {
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
span?.setAttribute("batch.mode", mode);
|
|
42
|
-
span?.setAttribute("batch.size", parsed.length);
|
|
43
|
-
const maxSize = await value(this.maxSize, options2);
|
|
44
|
-
if (parsed.length > maxSize) {
|
|
45
|
-
const message = "Batch request size exceeds the maximum allowed size";
|
|
46
|
-
setSpanError(span, message);
|
|
42
|
+
if (interceptorOptions.request.method === "GET") {
|
|
43
|
+
const [, search] = parseStandardUrl(interceptorOptions.request.url);
|
|
44
|
+
const params = new URLSearchParams(search);
|
|
45
|
+
const data = params.getAll("data").at(-1);
|
|
46
|
+
if (!data) {
|
|
47
47
|
return {
|
|
48
48
|
matched: true,
|
|
49
|
-
response: {
|
|
50
|
-
status: 413,
|
|
51
|
-
headers: {},
|
|
52
|
-
body: message
|
|
53
|
-
}
|
|
49
|
+
response: { status: 400, headers: {}, body: "Missing data parameter for batch request" }
|
|
54
50
|
};
|
|
55
51
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
52
|
+
const mightBeMessages = JSON.parse(data);
|
|
53
|
+
if (!Array.isArray(mightBeMessages) || mightBeMessages.some((m) => !isClientPeerSendMessage(m))) {
|
|
54
|
+
return {
|
|
55
|
+
matched: true,
|
|
56
|
+
response: { status: 400, headers: {}, body: "Invalid batch request data parameter" }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
messages = mightBeMessages;
|
|
60
|
+
} else {
|
|
61
|
+
const mightBeMessages = await interceptorOptions.request.resolveBody();
|
|
62
|
+
if (!Array.isArray(mightBeMessages)) {
|
|
63
|
+
return {
|
|
64
|
+
matched: true,
|
|
65
|
+
response: { status: 400, headers: {}, body: "Invalid batch request body" }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
messages = mightBeMessages;
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
return {
|
|
72
|
+
matched: true,
|
|
73
|
+
response: { status: 400, headers: {}, body: "Invalid batch request" }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const maxSize = await value(this.maxSize, interceptorOptions);
|
|
77
|
+
if (messages.length > maxSize) {
|
|
78
|
+
return {
|
|
79
|
+
matched: true,
|
|
80
|
+
response: { status: 413, headers: {}, body: "Batch request size exceeds the maximum allowed size" }
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const handleIndividualRequest = async (request) => {
|
|
84
|
+
try {
|
|
85
|
+
request = this.mapSubrequest(request, interceptorOptions);
|
|
86
|
+
const { matched, response } = await interceptorOptions.next({ ...interceptorOptions, request });
|
|
87
|
+
if (!matched) {
|
|
88
|
+
return { status: 404, headers: {}, body: "No procedure matched" };
|
|
89
|
+
}
|
|
90
|
+
return response;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
Promise.reject(err);
|
|
93
|
+
return { status: 500, headers: {}, body: "Internal server error" };
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const status = await value(this.successStatus, interceptorOptions);
|
|
97
|
+
const headers = await value(this.headers, interceptorOptions);
|
|
98
|
+
if (mode === "buffered") {
|
|
99
|
+
const responseMessages = [];
|
|
100
|
+
const peer2 = new ServerPeer(async (message) => {
|
|
101
|
+
responseMessages.push(message);
|
|
105
102
|
});
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
await Promise.all(messages.map((msg) => peer2.message(msg, handleIndividualRequest)));
|
|
104
|
+
await peer2.close();
|
|
105
|
+
if (responseMessages.some((msg) => msg.binary !== void 0)) {
|
|
106
|
+
const chunks = [];
|
|
107
|
+
for (const message of responseMessages) {
|
|
108
|
+
const encoded = await encodePeerMessage(message);
|
|
109
|
+
const bytes = typeof encoded === "string" ? new TextEncoder().encode(encoded) : encoded;
|
|
110
|
+
const lengthBuffer = new ArrayBuffer(4);
|
|
111
|
+
new DataView(lengthBuffer).setUint32(0, bytes.byteLength, false);
|
|
112
|
+
chunks.push(new Uint8Array(lengthBuffer));
|
|
113
|
+
chunks.push(bytes);
|
|
114
|
+
}
|
|
108
115
|
return {
|
|
109
116
|
matched: true,
|
|
110
|
-
response: {
|
|
117
|
+
response: {
|
|
118
|
+
status,
|
|
119
|
+
headers,
|
|
120
|
+
body: new Blob(chunks, { type: "application/octet-stream" })
|
|
121
|
+
}
|
|
111
122
|
};
|
|
112
123
|
}
|
|
113
|
-
|
|
124
|
+
return {
|
|
125
|
+
matched: true,
|
|
126
|
+
response: { status, headers, body: responseMessages }
|
|
127
|
+
};
|
|
114
128
|
}
|
|
115
|
-
|
|
129
|
+
let streamController;
|
|
130
|
+
const stream = new ReadableStream({
|
|
131
|
+
start(controller) {
|
|
132
|
+
streamController = controller;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
const peer = new ServerPeer(async (message) => {
|
|
136
|
+
const encoded = await encodePeerMessage(message);
|
|
137
|
+
const bytes = typeof encoded === "string" ? new TextEncoder().encode(encoded) : encoded;
|
|
138
|
+
const lengthBuffer = new ArrayBuffer(4);
|
|
139
|
+
new DataView(lengthBuffer).setUint32(0, bytes.byteLength, false);
|
|
140
|
+
streamController.enqueue(new Uint8Array(lengthBuffer));
|
|
141
|
+
streamController.enqueue(bytes);
|
|
142
|
+
});
|
|
143
|
+
Promise.all(messages.map((msg) => peer.message(msg, handleIndividualRequest))).then(async () => {
|
|
144
|
+
streamController.close();
|
|
145
|
+
await peer.close();
|
|
146
|
+
}).catch(async (error) => {
|
|
147
|
+
streamController.error(error);
|
|
148
|
+
await peer.close(error);
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
matched: true,
|
|
152
|
+
response: { status, headers, body: stream }
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
...options,
|
|
157
|
+
routingInterceptors: [routingInterceptor, ...toArray(options.routingInterceptors)]
|
|
158
|
+
};
|
|
116
159
|
}
|
|
117
160
|
}
|
|
118
161
|
|
|
119
|
-
class
|
|
162
|
+
class CORSHandlerPlugin {
|
|
120
163
|
options;
|
|
121
|
-
|
|
164
|
+
name = "~cors";
|
|
165
|
+
/**
|
|
166
|
+
* - Do not create spans for CORS preflight requests.
|
|
167
|
+
* - Run CORS interceptors before batch interceptors so headers are applied to
|
|
168
|
+
* the actual response rather than sub-responses.
|
|
169
|
+
*/
|
|
170
|
+
after = ["~opentelemetry", "~batch"];
|
|
122
171
|
constructor(options = {}) {
|
|
123
172
|
const defaults = {
|
|
124
173
|
origin: (origin) => origin,
|
|
@@ -130,19 +179,48 @@ class CORSPlugin {
|
|
|
130
179
|
};
|
|
131
180
|
}
|
|
132
181
|
init(options) {
|
|
133
|
-
|
|
134
|
-
|
|
182
|
+
const corsHeadersInterceptor = async (interceptorOptions) => {
|
|
183
|
+
const result = await interceptorOptions.next();
|
|
184
|
+
if (!result.matched) {
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
const resHeaders = { ...result.response.headers };
|
|
188
|
+
const origin = flattenStandardHeader(interceptorOptions.request.headers.origin) ?? "";
|
|
189
|
+
const allowedOrigins = toArray(await value(this.options.origin, origin, interceptorOptions));
|
|
190
|
+
if (allowedOrigins.includes("*")) {
|
|
191
|
+
resHeaders["access-control-allow-origin"] = "*";
|
|
192
|
+
} else {
|
|
193
|
+
if (allowedOrigins.includes(origin)) {
|
|
194
|
+
resHeaders["access-control-allow-origin"] = origin;
|
|
195
|
+
}
|
|
196
|
+
resHeaders.vary = interceptorOptions.request.headers.vary ?? "origin";
|
|
197
|
+
}
|
|
198
|
+
const allowedTimingOrigins = toArray(await value(this.options.timingOrigin, origin, interceptorOptions));
|
|
199
|
+
if (allowedTimingOrigins.includes("*")) {
|
|
200
|
+
resHeaders["timing-allow-origin"] = "*";
|
|
201
|
+
} else if (allowedTimingOrigins.includes(origin)) {
|
|
202
|
+
resHeaders["timing-allow-origin"] = origin;
|
|
203
|
+
}
|
|
204
|
+
if (this.options.credentials) {
|
|
205
|
+
resHeaders["access-control-allow-credentials"] = "true";
|
|
206
|
+
}
|
|
207
|
+
if (this.options.exposeHeaders?.length) {
|
|
208
|
+
resHeaders["access-control-expose-headers"] = flattenStandardHeader(this.options.exposeHeaders);
|
|
209
|
+
}
|
|
210
|
+
return { ...result, response: { ...result.response, headers: resHeaders } };
|
|
211
|
+
};
|
|
212
|
+
const preflightInterceptor = async (interceptorOptions) => {
|
|
135
213
|
if (interceptorOptions.request.method === "OPTIONS") {
|
|
136
214
|
const resHeaders = {};
|
|
137
215
|
if (this.options.maxAge !== void 0) {
|
|
138
216
|
resHeaders["access-control-max-age"] = this.options.maxAge.toString();
|
|
139
217
|
}
|
|
140
218
|
if (this.options.allowMethods?.length) {
|
|
141
|
-
resHeaders["access-control-allow-methods"] =
|
|
219
|
+
resHeaders["access-control-allow-methods"] = flattenStandardHeader(this.options.allowMethods);
|
|
142
220
|
}
|
|
143
221
|
const allowHeaders = this.options.allowHeaders ?? interceptorOptions.request.headers["access-control-request-headers"];
|
|
144
222
|
if (typeof allowHeaders === "string" || allowHeaders?.length) {
|
|
145
|
-
resHeaders["access-control-allow-headers"] =
|
|
223
|
+
resHeaders["access-control-allow-headers"] = flattenStandardHeader(allowHeaders);
|
|
146
224
|
}
|
|
147
225
|
return {
|
|
148
226
|
matched: true,
|
|
@@ -154,103 +232,85 @@ class CORSPlugin {
|
|
|
154
232
|
};
|
|
155
233
|
}
|
|
156
234
|
return interceptorOptions.next();
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (allowedOriginArr.includes("*")) {
|
|
167
|
-
result.response.headers["access-control-allow-origin"] = "*";
|
|
168
|
-
} else {
|
|
169
|
-
if (allowedOriginArr.includes(origin)) {
|
|
170
|
-
result.response.headers["access-control-allow-origin"] = origin;
|
|
171
|
-
}
|
|
172
|
-
result.response.headers.vary = interceptorOptions.request.headers.vary ?? "origin";
|
|
173
|
-
}
|
|
174
|
-
const allowedTimingOrigin = await value(this.options.timingOrigin, origin, interceptorOptions);
|
|
175
|
-
const allowedTimingOriginArr = Array.isArray(allowedTimingOrigin) ? allowedTimingOrigin : [allowedTimingOrigin];
|
|
176
|
-
if (allowedTimingOriginArr.includes("*")) {
|
|
177
|
-
result.response.headers["timing-allow-origin"] = "*";
|
|
178
|
-
} else if (allowedTimingOriginArr.includes(origin)) {
|
|
179
|
-
result.response.headers["timing-allow-origin"] = origin;
|
|
180
|
-
}
|
|
181
|
-
if (this.options.credentials) {
|
|
182
|
-
result.response.headers["access-control-allow-credentials"] = "true";
|
|
183
|
-
}
|
|
184
|
-
if (this.options.exposeHeaders?.length) {
|
|
185
|
-
result.response.headers["access-control-expose-headers"] = flattenHeader(this.options.exposeHeaders);
|
|
186
|
-
}
|
|
187
|
-
return result;
|
|
188
|
-
});
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
...options,
|
|
238
|
+
routingInterceptors: [
|
|
239
|
+
corsHeadersInterceptor,
|
|
240
|
+
preflightInterceptor,
|
|
241
|
+
...toArray(options.routingInterceptors)
|
|
242
|
+
]
|
|
243
|
+
};
|
|
189
244
|
}
|
|
190
245
|
}
|
|
191
246
|
|
|
192
|
-
class
|
|
247
|
+
class RequestHeadersHandlerPlugin {
|
|
248
|
+
name = "~request-headers";
|
|
193
249
|
init(options) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
250
|
+
return {
|
|
251
|
+
...options,
|
|
252
|
+
interceptors: [
|
|
253
|
+
(interceptorOptions) => {
|
|
254
|
+
const reqHeaders = interceptorOptions.context.reqHeaders ?? toFetchHeaders(interceptorOptions.request.headers);
|
|
255
|
+
return interceptorOptions.next({
|
|
256
|
+
...interceptorOptions,
|
|
257
|
+
context: {
|
|
258
|
+
...interceptorOptions.context,
|
|
259
|
+
reqHeaders
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
...toArray(options.interceptors)
|
|
264
|
+
]
|
|
265
|
+
};
|
|
205
266
|
}
|
|
206
267
|
}
|
|
207
268
|
|
|
208
|
-
class
|
|
269
|
+
class ResponseHeadersHandlerPlugin {
|
|
270
|
+
name = "~response-headers";
|
|
271
|
+
/**
|
|
272
|
+
* Interceptors should run after batch interceptors so headers are applied to each sub-response.
|
|
273
|
+
*/
|
|
274
|
+
before = ["~batch"];
|
|
209
275
|
init(options) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
...result.response,
|
|
237
|
-
headers: responseHeaders
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
});
|
|
276
|
+
return {
|
|
277
|
+
...options,
|
|
278
|
+
routingInterceptors: [
|
|
279
|
+
async (interceptorOptions) => {
|
|
280
|
+
const resHeaders = new Headers(interceptorOptions.context.resHeaders);
|
|
281
|
+
const result = await interceptorOptions.next({
|
|
282
|
+
...interceptorOptions,
|
|
283
|
+
context: {
|
|
284
|
+
...interceptorOptions.context,
|
|
285
|
+
resHeaders
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
if (!result.response) {
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
...result,
|
|
293
|
+
response: {
|
|
294
|
+
...result.response,
|
|
295
|
+
headers: mergeStandardHeaders(result.response.headers, toStandardHeaders(resHeaders))
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
...toArray(options.routingInterceptors)
|
|
300
|
+
]
|
|
301
|
+
};
|
|
241
302
|
}
|
|
242
303
|
}
|
|
243
304
|
|
|
244
|
-
class
|
|
305
|
+
class RethrowHandlerPlugin {
|
|
306
|
+
name = "~rethrow";
|
|
245
307
|
filter;
|
|
246
308
|
CONTEXT_SYMBOL = Symbol("ORPC_RETHROW_HANDLER_PLUGIN_CONTEXT");
|
|
247
309
|
constructor(options) {
|
|
248
310
|
this.filter = options.filter;
|
|
249
311
|
}
|
|
250
312
|
init(options) {
|
|
251
|
-
|
|
252
|
-
options.interceptors ??= [];
|
|
253
|
-
options.rootInterceptors.push(async (options2) => {
|
|
313
|
+
const routingInterceptor = async (options2) => {
|
|
254
314
|
const pluginContext = {};
|
|
255
315
|
const result = await options2.next({
|
|
256
316
|
...options2,
|
|
@@ -263,8 +323,8 @@ class experimental_RethrowHandlerPlugin {
|
|
|
263
323
|
throw pluginContext.error.value;
|
|
264
324
|
}
|
|
265
325
|
return result;
|
|
266
|
-
}
|
|
267
|
-
|
|
326
|
+
};
|
|
327
|
+
const interceptor = async (options2) => {
|
|
268
328
|
const pluginContext = options2.context[this.CONTEXT_SYMBOL];
|
|
269
329
|
if (!pluginContext) {
|
|
270
330
|
throw new TypeError("[RethrowHandlerPlugin] Rethrow handler context has been corrupted or modified by another plugin or interceptor");
|
|
@@ -272,57 +332,26 @@ class experimental_RethrowHandlerPlugin {
|
|
|
272
332
|
try {
|
|
273
333
|
return await options2.next();
|
|
274
334
|
} catch (error) {
|
|
275
|
-
if (
|
|
335
|
+
if (this.filter(error, options2)) {
|
|
276
336
|
pluginContext.error = { value: error };
|
|
277
|
-
return { matched: false, response: void 0 };
|
|
278
337
|
}
|
|
279
338
|
throw error;
|
|
280
339
|
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.error = options.error ?? new ORPCError("CSRF_TOKEN_MISMATCH", {
|
|
296
|
-
status: 403,
|
|
297
|
-
message: "Invalid CSRF token"
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
order = 8e6;
|
|
301
|
-
init(options) {
|
|
302
|
-
options.rootInterceptors ??= [];
|
|
303
|
-
options.clientInterceptors ??= [];
|
|
304
|
-
options.rootInterceptors.unshift(async (options2) => {
|
|
305
|
-
const headerName = await value(this.headerName, options2);
|
|
306
|
-
const headerValue = await value(this.headerValue, options2);
|
|
307
|
-
return options2.next({
|
|
308
|
-
...options2,
|
|
309
|
-
context: {
|
|
310
|
-
...options2.context,
|
|
311
|
-
[SIMPLE_CSRF_PROTECTION_CONTEXT_SYMBOL]: options2.request.headers[headerName] === headerValue
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
options.clientInterceptors.unshift(async (options2) => {
|
|
316
|
-
if (typeof options2.context[SIMPLE_CSRF_PROTECTION_CONTEXT_SYMBOL] !== "boolean") {
|
|
317
|
-
throw new TypeError("[SimpleCsrfProtectionHandlerPlugin] CSRF protection context has been corrupted or modified by another plugin or interceptor");
|
|
318
|
-
}
|
|
319
|
-
const excluded = await value(this.exclude, options2);
|
|
320
|
-
if (!excluded && !options2.context[SIMPLE_CSRF_PROTECTION_CONTEXT_SYMBOL]) {
|
|
321
|
-
throw this.error;
|
|
322
|
-
}
|
|
323
|
-
return options2.next();
|
|
324
|
-
});
|
|
340
|
+
};
|
|
341
|
+
return {
|
|
342
|
+
...options,
|
|
343
|
+
routingInterceptors: [
|
|
344
|
+
...toArray(options.routingInterceptors),
|
|
345
|
+
routingInterceptor
|
|
346
|
+
// rethrow as early as possible and keep it close to `interceptor` to avoid a corrupted or modified context
|
|
347
|
+
],
|
|
348
|
+
interceptors: [
|
|
349
|
+
interceptor,
|
|
350
|
+
// catch as many errors as possible
|
|
351
|
+
...toArray(options.interceptors)
|
|
352
|
+
]
|
|
353
|
+
};
|
|
325
354
|
}
|
|
326
355
|
}
|
|
327
356
|
|
|
328
|
-
export { BatchHandlerPlugin,
|
|
357
|
+
export { BatchHandlerPlugin, CORSHandlerPlugin, RequestHeadersHandlerPlugin, ResponseHeadersHandlerPlugin, RethrowHandlerPlugin };
|