@orpc/server 2.0.0-beta.47 → 2.0.0-beta.49
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 +6 -7
- package/dist/adapters/aws-lambda/handler.d.ts +25 -0
- package/dist/adapters/aws-lambda/handler.js +24 -0
- package/dist/adapters/aws-lambda/index.d.ts +10 -45
- package/dist/adapters/aws-lambda/index.js +2 -0
- package/dist/adapters/aws-lambda/rpc-handler.d.ts +19 -0
- package/dist/adapters/aws-lambda/rpc-handler.js +18 -0
- package/dist/adapters/crossws/handler.d.ts +45 -0
- package/dist/adapters/crossws/handler.js +60 -0
- package/dist/adapters/crossws/index.d.ts +2 -63
- package/dist/adapters/crossws/index.js +2 -0
- package/dist/adapters/crossws/rpc-handler.d.ts +16 -0
- package/dist/adapters/crossws/rpc-handler.js +15 -0
- package/dist/adapters/fastify/handler.d.ts +21 -0
- package/dist/adapters/fastify/handler.js +20 -0
- package/dist/adapters/fastify/index.d.ts +2 -42
- package/dist/adapters/fastify/index.js +2 -0
- package/dist/adapters/fastify/rpc-handler.d.ts +15 -0
- package/dist/adapters/fastify/rpc-handler.js +14 -0
- package/dist/adapters/fetch/handler.d.ts +23 -0
- package/dist/adapters/fetch/handler.js +22 -0
- package/dist/adapters/fetch/index.d.ts +5 -45
- package/dist/adapters/fetch/index.js +7 -0
- package/dist/adapters/fetch/rpc-handler.d.ts +17 -0
- package/dist/adapters/fetch/rpc-handler.js +16 -0
- package/dist/adapters/message-port/{index.d.mts → handler.d.ts} +17 -30
- package/dist/adapters/message-port/handler.js +86 -0
- package/dist/adapters/message-port/index.d.ts +2 -77
- package/dist/adapters/message-port/index.js +2 -0
- package/dist/adapters/message-port/rpc-handler.d.ts +16 -0
- package/dist/adapters/message-port/rpc-handler.js +15 -0
- package/dist/adapters/node/handler.d.ts +21 -0
- package/dist/adapters/node/handler.js +20 -0
- package/dist/adapters/node/index.d.ts +5 -42
- package/dist/adapters/node/index.js +7 -0
- package/dist/adapters/node/rpc-handler.d.ts +16 -0
- package/dist/adapters/node/rpc-handler.js +15 -0
- package/dist/adapters/standard/codec.d.ts +16 -0
- package/dist/adapters/standard/codec.js +1 -0
- package/dist/adapters/standard/handler.d.ts +68 -0
- package/dist/adapters/standard/handler.js +205 -0
- package/dist/adapters/standard/index.d.ts +6 -95
- package/dist/adapters/standard/index.js +5 -0
- package/dist/adapters/standard/plugin.d.ts +32 -0
- package/dist/adapters/standard/plugin.js +16 -0
- package/dist/adapters/standard/rpc-handler-codec.d.ts +46 -0
- package/dist/adapters/standard/rpc-handler-codec.js +51 -0
- package/dist/adapters/standard/rpc-matcher.d.ts +53 -0
- package/dist/adapters/standard/rpc-matcher.js +152 -0
- package/dist/adapters/standard/utils.d.ts +10 -0
- package/dist/adapters/standard/utils.js +6 -0
- package/dist/adapters/standard-peer/index.d.ts +1 -11
- package/dist/adapters/standard-peer/index.js +1 -0
- package/dist/adapters/standard-peer/utils.d.ts +4 -0
- package/dist/adapters/standard-peer/utils.js +7 -0
- package/dist/adapters/websocket/{index.d.mts → handler.d.ts} +11 -29
- package/dist/adapters/websocket/handler.js +85 -0
- package/dist/adapters/websocket/index.d.ts +2 -77
- package/dist/adapters/websocket/index.js +2 -0
- package/dist/adapters/websocket/rpc-handler.d.ts +16 -0
- package/dist/adapters/websocket/rpc-handler.js +15 -0
- package/dist/builder-variants.d.ts +226 -0
- package/dist/builder-variants.js +1 -0
- package/dist/builder.d.ts +127 -0
- package/dist/builder.js +226 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/context.d.ts +6 -0
- package/dist/context.js +1 -0
- package/dist/extensions/callable.d.ts +6 -5
- package/dist/extensions/callable.js +7 -0
- package/dist/helpers/base64url.d.ts +31 -0
- package/dist/helpers/base64url.js +53 -0
- package/dist/helpers/cookie.d.ts +51 -0
- package/dist/helpers/cookie.js +69 -0
- package/dist/helpers/encryption.d.ts +28 -0
- package/dist/helpers/encryption.js +80 -0
- package/dist/helpers/index.d.ts +4 -167
- package/dist/helpers/index.js +4 -0
- package/dist/helpers/signing.d.ts +50 -0
- package/dist/helpers/signing.js +88 -0
- package/dist/implementer-procedure.d.ts +16 -0
- package/dist/implementer-procedure.js +44 -0
- package/dist/implementer-router.d.ts +40 -0
- package/dist/implementer-router.js +117 -0
- package/dist/implementer.d.ts +16 -0
- package/dist/implementer.js +37 -0
- package/dist/index.d.ts +45 -460
- package/dist/index.js +48 -0
- package/dist/lazy.d.ts +20 -0
- package/dist/lazy.js +31 -0
- package/dist/middleware-decorated.d.ts +10 -0
- package/dist/middleware-decorated.js +71 -0
- package/dist/middleware.d.ts +54 -0
- package/dist/middleware.js +1 -0
- package/dist/plugins/batch.d.ts +102 -0
- package/dist/plugins/batch.js +405 -0
- package/dist/plugins/cors.d.ts +73 -0
- package/dist/plugins/cors.js +112 -0
- package/dist/plugins/get-method-csrf-protection.d.ts +23 -0
- package/dist/plugins/get-method-csrf-protection.js +64 -0
- package/dist/plugins/head-method.d.ts +23 -0
- package/dist/plugins/head-method.js +104 -0
- package/dist/plugins/index.d.ts +29 -422
- package/dist/plugins/index.js +29 -0
- package/dist/plugins/method-override.d.ts +38 -0
- package/dist/plugins/method-override.js +50 -0
- package/dist/plugins/request-compression.d.ts +16 -0
- package/dist/plugins/request-compression.js +82 -0
- package/dist/plugins/request-headers.d.ts +22 -0
- package/dist/plugins/request-headers.js +30 -0
- package/dist/{shared/server.CgBF3Vj1.d.ts → plugins/request-limit.d.ts} +4 -8
- package/dist/plugins/request-limit.js +68 -0
- package/dist/plugins/response-compression.d.ts +59 -0
- package/dist/plugins/response-compression.js +209 -0
- package/dist/plugins/response-headers.d.ts +30 -0
- package/dist/plugins/response-headers.js +47 -0
- package/dist/plugins/rethrow.d.ts +33 -0
- package/dist/plugins/rethrow.js +59 -0
- package/dist/plugins/signal-sync.d.ts +13 -0
- package/dist/plugins/signal-sync.js +91 -0
- package/dist/plugins/timeout.d.ts +37 -0
- package/dist/plugins/timeout.js +59 -0
- package/dist/procedure-client.d.ts +19 -0
- package/dist/procedure-client.js +175 -0
- package/dist/procedure-decorated.d.ts +10 -0
- package/dist/procedure-decorated.js +44 -0
- package/dist/procedure-utils.d.ts +41 -0
- package/dist/procedure-utils.js +49 -0
- package/dist/procedure.d.ts +72 -0
- package/dist/procedure.js +24 -0
- package/dist/router-client.d.ts +23 -0
- package/dist/router-client.js +43 -0
- package/dist/router-hidden.d.ts +5 -0
- package/dist/router-hidden.js +14 -0
- package/dist/router-utils.d.ts +48 -0
- package/dist/router-utils.js +202 -0
- package/dist/{shared/server.Dz7j9BEh.d.ts → router.d.ts} +14 -15
- package/dist/router.js +1 -0
- package/package.json +84 -86
- package/dist/adapters/aws-lambda/index.d.mts +0 -51
- package/dist/adapters/aws-lambda/index.mjs +0 -39
- package/dist/adapters/crossws/index.d.mts +0 -63
- package/dist/adapters/crossws/index.mjs +0 -68
- package/dist/adapters/fastify/index.d.mts +0 -42
- package/dist/adapters/fastify/index.mjs +0 -39
- package/dist/adapters/fetch/index.d.mts +0 -47
- package/dist/adapters/fetch/index.mjs +0 -42
- package/dist/adapters/message-port/index.mjs +0 -87
- package/dist/adapters/node/index.d.mts +0 -44
- package/dist/adapters/node/index.mjs +0 -41
- package/dist/adapters/standard/index.d.mts +0 -95
- package/dist/adapters/standard/index.mjs +0 -8
- package/dist/adapters/standard-peer/index.d.mts +0 -11
- package/dist/adapters/standard-peer/index.mjs +0 -10
- package/dist/adapters/websocket/index.mjs +0 -87
- package/dist/extensions/callable.d.mts +0 -10
- package/dist/extensions/callable.mjs +0 -11
- package/dist/helpers/index.d.mts +0 -167
- package/dist/helpers/index.mjs +0 -200
- package/dist/index.d.mts +0 -476
- package/dist/index.mjs +0 -463
- package/dist/plugins/index.d.mts +0 -432
- package/dist/plugins/index.mjs +0 -965
- package/dist/shared/server.1ExWFMze.mjs +0 -69
- package/dist/shared/server.9B1jEm5a.d.mts +0 -105
- package/dist/shared/server.BB464y-6.mjs +0 -207
- package/dist/shared/server.BC5BIu02.d.mts +0 -73
- package/dist/shared/server.BSYGskId.d.ts +0 -14
- package/dist/shared/server.BvG3boBj.d.mts +0 -14
- package/dist/shared/server.CTV_mC2P.d.mts +0 -34
- package/dist/shared/server.CYbIpHbH.mjs +0 -239
- package/dist/shared/server.Chq5FpXf.mjs +0 -353
- package/dist/shared/server.DFPxP2v3.d.mts +0 -166
- package/dist/shared/server.DFPxP2v3.d.ts +0 -166
- package/dist/shared/server.DOg7ke6y.d.ts +0 -105
- package/dist/shared/server.W91HSRkE.mjs +0 -9
- package/dist/shared/server.sBQSgqK3.mjs +0 -41
package/dist/plugins/index.mjs
DELETED
|
@@ -1,965 +0,0 @@
|
|
|
1
|
-
import { ORPCError } from '@orpc/client';
|
|
2
|
-
import { DEFAULT_BATCH_MAX_SIZE, BATCH_HEADER } from '@orpc/client/plugins';
|
|
3
|
-
export { BATCH_HEADER, DEFAULT_BATCH_MAX_SIZE } from '@orpc/client/plugins';
|
|
4
|
-
import { toArray, value, promiseWithResolvers, isAsyncIteratorObject, isCompressibleContentType, isNoTransformCacheControl, parseAcceptEncodingQualities, isAcceptableEncoding, varyByAcceptEncoding, anyAbortSignal, override, AsyncIteratorClass, runWithSignal, AbortError, wrapAsyncIterator, wrapReadableStream } from '@orpc/shared';
|
|
5
|
-
import { flattenStandardHeader, parseStandardUrl, generateContentDisposition, mergeStandardHeaders } from '@standard-server/core';
|
|
6
|
-
import { ServerPeer, isPeerMessage, isClientPeerSendMessage, encodePeerMessage } from '@standard-server/peer';
|
|
7
|
-
import { toFetchHeaders, toStandardBody, toFetchBody, toStandardHeaders } from '@standard-server/fetch';
|
|
8
|
-
export { R as RequestLimitHandlerPlugin } from '../shared/server.1ExWFMze.mjs';
|
|
9
|
-
|
|
10
|
-
const BATCH_CONTENT_TYPE = "application/vnd.orpc.batch";
|
|
11
|
-
const textEncoder = new TextEncoder();
|
|
12
|
-
function encodeLengthPrefix(length) {
|
|
13
|
-
const prefix = new Uint8Array(4);
|
|
14
|
-
new DataView(prefix.buffer).setUint32(0, length, false);
|
|
15
|
-
return prefix;
|
|
16
|
-
}
|
|
17
|
-
async function encodeFrame(message) {
|
|
18
|
-
const encoded = await encodePeerMessage(message);
|
|
19
|
-
const bytes = typeof encoded === "string" ? textEncoder.encode(encoded) : encoded;
|
|
20
|
-
return [encodeLengthPrefix(bytes.byteLength), bytes];
|
|
21
|
-
}
|
|
22
|
-
function isBodyHeader(name) {
|
|
23
|
-
return name.startsWith("content-") || name === "standard-server";
|
|
24
|
-
}
|
|
25
|
-
function isClientPeerSendMessages(maybe) {
|
|
26
|
-
return Array.isArray(maybe) && maybe.every((m) => isPeerMessage(m) && isClientPeerSendMessage(m));
|
|
27
|
-
}
|
|
28
|
-
function internalError(err) {
|
|
29
|
-
Promise.reject(err);
|
|
30
|
-
return { status: 500, headers: {}, body: "Internal server error" };
|
|
31
|
-
}
|
|
32
|
-
function liftSubresponseHeaders(batchHeaders, subresponseHeaders) {
|
|
33
|
-
const [first = {}, ...rest] = subresponseHeaders;
|
|
34
|
-
const shared = {};
|
|
35
|
-
for (const [name, value2] of Object.entries(first)) {
|
|
36
|
-
if (value2 === void 0 || isBodyHeader(name)) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
const flattened = flattenStandardHeader(value2);
|
|
40
|
-
if (rest.every((other) => flattenStandardHeader(other[name]) === flattened)) {
|
|
41
|
-
shared[name] = value2;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const headers = { ...shared, ...batchHeaders };
|
|
45
|
-
const cookies = new Set([batchHeaders, ...subresponseHeaders].flatMap((other) => toArray(other["set-cookie"])));
|
|
46
|
-
if (cookies.size > 0) {
|
|
47
|
-
headers["set-cookie"] = [...cookies];
|
|
48
|
-
}
|
|
49
|
-
const varies = /* @__PURE__ */ new Map();
|
|
50
|
-
for (const value2 of [batchHeaders, ...subresponseHeaders].flatMap((other) => toArray(other.vary))) {
|
|
51
|
-
for (const field of value2.split(",")) {
|
|
52
|
-
const name = field.trim();
|
|
53
|
-
const key = name.toLowerCase();
|
|
54
|
-
if (name && !varies.has(key)) {
|
|
55
|
-
varies.set(key, name);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (varies.size > 0) {
|
|
60
|
-
headers.vary = varies.has("*") ? "*" : [...varies.values()].join(", ");
|
|
61
|
-
}
|
|
62
|
-
return headers;
|
|
63
|
-
}
|
|
64
|
-
class BatchHandlerPlugin {
|
|
65
|
-
name = "~batch";
|
|
66
|
-
/**
|
|
67
|
-
* Run batch interceptors inside the tracing interceptors
|
|
68
|
-
* so each subrequest gets its own span instead of sharing one batch-level span.
|
|
69
|
-
*/
|
|
70
|
-
after = ["~tracing"];
|
|
71
|
-
maxSize;
|
|
72
|
-
mapSubrequest;
|
|
73
|
-
mapSubresponse;
|
|
74
|
-
successStatus;
|
|
75
|
-
headers;
|
|
76
|
-
keepAliveEnabled;
|
|
77
|
-
keepAliveInterval;
|
|
78
|
-
constructor(options = {}) {
|
|
79
|
-
this.maxSize = options.maxSize ?? DEFAULT_BATCH_MAX_SIZE;
|
|
80
|
-
this.mapSubrequest = options.mapSubrequest ?? ((subRequest, { request: batchRequest }) => ({
|
|
81
|
-
...subRequest,
|
|
82
|
-
headers: {
|
|
83
|
-
...subRequest.headers,
|
|
84
|
-
/**
|
|
85
|
-
* The batch request headers win over the subrequest ones. They are the only ones the
|
|
86
|
-
* transport actually saw, so headers the browser injects on its own, such as `cookie`
|
|
87
|
-
* or `origin`, cannot be overridden by a subrequest, which is just request payload.
|
|
88
|
-
*/
|
|
89
|
-
...batchRequest.headers,
|
|
90
|
-
[BATCH_HEADER]: void 0
|
|
91
|
-
// useful in case batch plugin is used multiple times
|
|
92
|
-
}
|
|
93
|
-
}));
|
|
94
|
-
this.mapSubresponse = options.mapSubresponse ?? ((subresponse, { headers: batchHeaders }) => {
|
|
95
|
-
const headers = {};
|
|
96
|
-
for (const [name, value2] of Object.entries(subresponse.headers)) {
|
|
97
|
-
if (name !== "set-cookie" && flattenStandardHeader(value2) !== flattenStandardHeader(batchHeaders[name])) {
|
|
98
|
-
headers[name] = value2;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return { ...subresponse, headers };
|
|
102
|
-
});
|
|
103
|
-
this.successStatus = options.successStatus ?? 207;
|
|
104
|
-
this.headers = options.headers ?? {};
|
|
105
|
-
this.keepAliveEnabled = options.keepAlive?.enabled ?? true;
|
|
106
|
-
this.keepAliveInterval = options.keepAlive?.interval ?? 15e3;
|
|
107
|
-
}
|
|
108
|
-
init(options) {
|
|
109
|
-
const routingInterceptor = async (interceptorOptions) => {
|
|
110
|
-
if (flattenStandardHeader(interceptorOptions.request.headers[BATCH_HEADER]) === void 0) {
|
|
111
|
-
return interceptorOptions.next();
|
|
112
|
-
}
|
|
113
|
-
let messages;
|
|
114
|
-
try {
|
|
115
|
-
if (interceptorOptions.request.method === "GET") {
|
|
116
|
-
const [, search] = parseStandardUrl(interceptorOptions.request.url);
|
|
117
|
-
const params = new URLSearchParams(search);
|
|
118
|
-
const data = params.getAll("data").at(-1);
|
|
119
|
-
if (!data) {
|
|
120
|
-
return {
|
|
121
|
-
matched: true,
|
|
122
|
-
response: { status: 400, headers: {}, body: "Missing data parameter for batch request" }
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
const mightBeMessages = JSON.parse(data);
|
|
126
|
-
if (!isClientPeerSendMessages(mightBeMessages)) {
|
|
127
|
-
return {
|
|
128
|
-
matched: true,
|
|
129
|
-
response: { status: 400, headers: {}, body: "Invalid batch request data parameter" }
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
messages = mightBeMessages;
|
|
133
|
-
} else {
|
|
134
|
-
const mightBeMessages = await interceptorOptions.request.resolveBody();
|
|
135
|
-
if (!isClientPeerSendMessages(mightBeMessages)) {
|
|
136
|
-
return {
|
|
137
|
-
matched: true,
|
|
138
|
-
response: { status: 400, headers: {}, body: "Invalid batch request body" }
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
messages = mightBeMessages;
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
return {
|
|
145
|
-
matched: true,
|
|
146
|
-
response: {
|
|
147
|
-
status: 400,
|
|
148
|
-
headers: {},
|
|
149
|
-
body: error instanceof ORPCError ? error.message : "Invalid batch request"
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
const outerMethod = interceptorOptions.request.method;
|
|
154
|
-
if ((outerMethod === "GET" || outerMethod === "QUERY") && messages.some((message) => message.kind === "request" && message.json.method !== outerMethod)) {
|
|
155
|
-
return {
|
|
156
|
-
matched: true,
|
|
157
|
-
response: { status: 400, headers: {}, body: `${outerMethod} batch requests only accept ${outerMethod} sub-requests` }
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
const maxSize = await value(this.maxSize, interceptorOptions);
|
|
161
|
-
if (messages.length > maxSize) {
|
|
162
|
-
return {
|
|
163
|
-
matched: true,
|
|
164
|
-
response: { status: 413, headers: {}, body: "Batch request size exceeds the maximum allowed size" }
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
const status = await value(this.successStatus, interceptorOptions);
|
|
168
|
-
const headers = await value(this.headers, interceptorOptions);
|
|
169
|
-
const bufferedMessages = [];
|
|
170
|
-
let streamController;
|
|
171
|
-
let keepAliveTimer;
|
|
172
|
-
const ready = messages.map(() => promiseWithResolvers());
|
|
173
|
-
const lifting = Promise.all(ready.map(({ promise }) => promise)).then((all) => {
|
|
174
|
-
const heads = all.filter((head) => head !== void 0);
|
|
175
|
-
return {
|
|
176
|
-
batchHeaders: liftSubresponseHeaders(headers, heads.map((head) => head.headers)),
|
|
177
|
-
streams: heads.some((head) => head.stream)
|
|
178
|
-
};
|
|
179
|
-
});
|
|
180
|
-
const clearKeepAlive = () => {
|
|
181
|
-
if (keepAliveTimer !== void 0) {
|
|
182
|
-
clearInterval(keepAliveTimer);
|
|
183
|
-
keepAliveTimer = void 0;
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
const scheduleKeepAlive = () => {
|
|
187
|
-
if (!this.keepAliveEnabled) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
clearKeepAlive();
|
|
191
|
-
keepAliveTimer = setInterval(() => {
|
|
192
|
-
try {
|
|
193
|
-
streamController.enqueue(encodeLengthPrefix(0));
|
|
194
|
-
} catch {
|
|
195
|
-
clearKeepAlive();
|
|
196
|
-
}
|
|
197
|
-
}, this.keepAliveInterval);
|
|
198
|
-
};
|
|
199
|
-
const enqueueMessage = async (message) => {
|
|
200
|
-
for (const chunk of await encodeFrame(message)) {
|
|
201
|
-
streamController.enqueue(chunk);
|
|
202
|
-
}
|
|
203
|
-
scheduleKeepAlive();
|
|
204
|
-
};
|
|
205
|
-
const peer = new ServerPeer(async (message) => {
|
|
206
|
-
if (streamController === void 0) {
|
|
207
|
-
bufferedMessages.push(message);
|
|
208
|
-
} else {
|
|
209
|
-
await enqueueMessage(message);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
const handleIndividualRequest = async (request, markReady) => {
|
|
213
|
-
let response;
|
|
214
|
-
try {
|
|
215
|
-
request = this.mapSubrequest(request, interceptorOptions);
|
|
216
|
-
const result = await interceptorOptions.next({ ...interceptorOptions, request });
|
|
217
|
-
response = result.matched ? result.response : { status: 404, headers: {}, body: "No procedure matched" };
|
|
218
|
-
} catch (err) {
|
|
219
|
-
response = internalError(err);
|
|
220
|
-
}
|
|
221
|
-
markReady({
|
|
222
|
-
headers: response.headers,
|
|
223
|
-
stream: response.body instanceof ReadableStream || isAsyncIteratorObject(response.body)
|
|
224
|
-
});
|
|
225
|
-
const { batchHeaders: batchHeaders2 } = await lifting;
|
|
226
|
-
try {
|
|
227
|
-
return this.mapSubresponse(response, { status, headers: batchHeaders2 }, interceptorOptions);
|
|
228
|
-
} catch (err) {
|
|
229
|
-
return internalError(err);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
const runSubrequests = async () => {
|
|
233
|
-
const promise = Promise.all(messages.map((msg, i) => {
|
|
234
|
-
const { resolve } = ready[i];
|
|
235
|
-
return peer.message(msg, (request) => handleIndividualRequest(request, resolve)).finally(() => resolve(void 0));
|
|
236
|
-
}));
|
|
237
|
-
const signal = interceptorOptions.request.signal;
|
|
238
|
-
const closePeer = () => peer.close(signal?.reason);
|
|
239
|
-
if (signal?.aborted) {
|
|
240
|
-
closePeer();
|
|
241
|
-
}
|
|
242
|
-
signal?.addEventListener("abort", closePeer);
|
|
243
|
-
try {
|
|
244
|
-
await promise;
|
|
245
|
-
} finally {
|
|
246
|
-
signal?.removeEventListener("abort", closePeer);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
const subrequests = runSubrequests();
|
|
250
|
-
const { batchHeaders, streams } = await lifting;
|
|
251
|
-
const framedHeaders = { ...batchHeaders, "content-type": batchHeaders["content-type"] ?? BATCH_CONTENT_TYPE };
|
|
252
|
-
if (!streams) {
|
|
253
|
-
await subrequests;
|
|
254
|
-
await peer.close();
|
|
255
|
-
if (bufferedMessages.some((msg) => msg.binary !== void 0)) {
|
|
256
|
-
const chunks = (await Promise.all(bufferedMessages.map(encodeFrame))).flat();
|
|
257
|
-
return {
|
|
258
|
-
matched: true,
|
|
259
|
-
response: {
|
|
260
|
-
status,
|
|
261
|
-
headers: framedHeaders,
|
|
262
|
-
body: new Blob(chunks)
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
matched: true,
|
|
268
|
-
response: { status, headers: batchHeaders, body: bufferedMessages }
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
const body = new ReadableStream({
|
|
272
|
-
start(controller) {
|
|
273
|
-
streamController = controller;
|
|
274
|
-
scheduleKeepAlive();
|
|
275
|
-
},
|
|
276
|
-
async cancel(reason) {
|
|
277
|
-
clearKeepAlive();
|
|
278
|
-
await peer.close(reason);
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
const flushed = Promise.all(bufferedMessages.splice(0).map(enqueueMessage));
|
|
282
|
-
Promise.all([flushed, subrequests]).then(async () => {
|
|
283
|
-
clearKeepAlive();
|
|
284
|
-
streamController.close();
|
|
285
|
-
await peer.close();
|
|
286
|
-
}).catch(async (error) => {
|
|
287
|
-
clearKeepAlive();
|
|
288
|
-
streamController.error(error);
|
|
289
|
-
await peer.close(error);
|
|
290
|
-
});
|
|
291
|
-
return {
|
|
292
|
-
matched: true,
|
|
293
|
-
response: {
|
|
294
|
-
status,
|
|
295
|
-
headers: framedHeaders,
|
|
296
|
-
body
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
};
|
|
300
|
-
return {
|
|
301
|
-
...options,
|
|
302
|
-
routingInterceptors: [routingInterceptor, ...toArray(options.routingInterceptors)]
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
class CORSHandlerPlugin {
|
|
308
|
-
options;
|
|
309
|
-
name = "~cors";
|
|
310
|
-
/**
|
|
311
|
-
* - Do not create spans for CORS preflight requests.
|
|
312
|
-
* - Run CORS interceptors before batch interceptors so headers are applied to
|
|
313
|
-
* the actual response rather than sub-responses.
|
|
314
|
-
*/
|
|
315
|
-
after = ["~tracing", "~batch"];
|
|
316
|
-
constructor(options = {}) {
|
|
317
|
-
const defaults = {
|
|
318
|
-
origin: "*",
|
|
319
|
-
allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "QUERY"]
|
|
320
|
-
};
|
|
321
|
-
this.options = {
|
|
322
|
-
...defaults,
|
|
323
|
-
...options
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
init(options) {
|
|
327
|
-
const corsHeadersInterceptor = async (interceptorOptions) => {
|
|
328
|
-
const result = await interceptorOptions.next();
|
|
329
|
-
if (!result.matched) {
|
|
330
|
-
return result;
|
|
331
|
-
}
|
|
332
|
-
const resHeaders = { ...result.response.headers };
|
|
333
|
-
const origin = flattenStandardHeader(interceptorOptions.request.headers.origin);
|
|
334
|
-
const allowOrigin = await this.resolveOrigin(this.options.origin, origin, interceptorOptions);
|
|
335
|
-
const timingAllowOrigin = await this.resolveOrigin(this.options.timingOrigin, origin, interceptorOptions);
|
|
336
|
-
if (allowOrigin.value !== void 0) {
|
|
337
|
-
resHeaders["access-control-allow-origin"] = allowOrigin.value;
|
|
338
|
-
}
|
|
339
|
-
if (timingAllowOrigin.value !== void 0) {
|
|
340
|
-
resHeaders["timing-allow-origin"] = timingAllowOrigin.value;
|
|
341
|
-
}
|
|
342
|
-
if (allowOrigin.varies || timingAllowOrigin.varies) {
|
|
343
|
-
const existingVary = flattenStandardHeader(resHeaders.vary);
|
|
344
|
-
if (!existingVary?.split(",").some((v) => v.trim().toLowerCase() === "origin")) {
|
|
345
|
-
resHeaders.vary = existingVary ? `${existingVary}, Origin` : "Origin";
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (this.options.credentials) {
|
|
349
|
-
resHeaders["access-control-allow-credentials"] = "true";
|
|
350
|
-
}
|
|
351
|
-
if (this.options.exposeHeaders?.length) {
|
|
352
|
-
resHeaders["access-control-expose-headers"] = flattenStandardHeader(this.options.exposeHeaders);
|
|
353
|
-
}
|
|
354
|
-
return { ...result, response: { ...result.response, headers: resHeaders } };
|
|
355
|
-
};
|
|
356
|
-
const preflightInterceptor = async (interceptorOptions) => {
|
|
357
|
-
if (interceptorOptions.request.method === "OPTIONS") {
|
|
358
|
-
const resHeaders = {};
|
|
359
|
-
if (this.options.maxAge !== void 0) {
|
|
360
|
-
resHeaders["access-control-max-age"] = this.options.maxAge.toString();
|
|
361
|
-
}
|
|
362
|
-
if (this.options.allowMethods?.length) {
|
|
363
|
-
resHeaders["access-control-allow-methods"] = flattenStandardHeader(this.options.allowMethods);
|
|
364
|
-
}
|
|
365
|
-
const allowHeaders = this.options.allowHeaders ?? interceptorOptions.request.headers["access-control-request-headers"];
|
|
366
|
-
if (typeof allowHeaders === "string" || allowHeaders?.length) {
|
|
367
|
-
resHeaders["access-control-allow-headers"] = flattenStandardHeader(allowHeaders);
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
matched: true,
|
|
371
|
-
response: {
|
|
372
|
-
status: 204,
|
|
373
|
-
headers: resHeaders,
|
|
374
|
-
body: void 0
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
return interceptorOptions.next();
|
|
379
|
-
};
|
|
380
|
-
return {
|
|
381
|
-
...options,
|
|
382
|
-
routingInterceptors: [
|
|
383
|
-
corsHeadersInterceptor,
|
|
384
|
-
preflightInterceptor,
|
|
385
|
-
...toArray(options.routingInterceptors)
|
|
386
|
-
]
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
async resolveOrigin(option, origin, interceptorOptions) {
|
|
390
|
-
if (typeof option === "function") {
|
|
391
|
-
if (origin === void 0) {
|
|
392
|
-
return { value: void 0, varies: true };
|
|
393
|
-
}
|
|
394
|
-
const allowed2 = toArray(await option(origin, interceptorOptions));
|
|
395
|
-
if (allowed2.includes("*")) {
|
|
396
|
-
return { value: "*", varies: true };
|
|
397
|
-
}
|
|
398
|
-
return { value: allowed2.includes(origin) ? origin : void 0, varies: true };
|
|
399
|
-
}
|
|
400
|
-
const allowed = toArray(await option);
|
|
401
|
-
if (allowed.includes("*")) {
|
|
402
|
-
return { value: "*", varies: false };
|
|
403
|
-
}
|
|
404
|
-
if (origin !== void 0 && allowed.includes(origin)) {
|
|
405
|
-
return { value: origin, varies: true };
|
|
406
|
-
}
|
|
407
|
-
return { value: void 0, varies: allowed.length > 0 };
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
class GetMethodCsrfProtectionHandlerPlugin {
|
|
412
|
-
name = "~get-method-csrf-protection";
|
|
413
|
-
init(options) {
|
|
414
|
-
const interceptor = (interceptorOptions) => {
|
|
415
|
-
if (this.isAllowed(interceptorOptions)) {
|
|
416
|
-
return interceptorOptions.next();
|
|
417
|
-
}
|
|
418
|
-
throw new ORPCError("FORBIDDEN", { message: "Request blocked by CSRF protection." });
|
|
419
|
-
};
|
|
420
|
-
return {
|
|
421
|
-
...options,
|
|
422
|
-
interceptors: [interceptor, ...toArray(options.interceptors)]
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
isAllowed({ request }) {
|
|
426
|
-
if (request.method !== "GET") {
|
|
427
|
-
return true;
|
|
428
|
-
}
|
|
429
|
-
const site = flattenStandardHeader(request.headers["sec-fetch-site"])?.toLowerCase();
|
|
430
|
-
if (site === void 0 || site === "same-origin" || site === "same-site") {
|
|
431
|
-
return true;
|
|
432
|
-
}
|
|
433
|
-
const mode = flattenStandardHeader(request.headers["sec-fetch-mode"])?.toLowerCase();
|
|
434
|
-
const dest = flattenStandardHeader(request.headers["sec-fetch-dest"])?.toLowerCase();
|
|
435
|
-
if (mode === void 0 || dest === void 0) {
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
return mode !== "navigate" || dest !== "document";
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
class MethodOverrideHandlerPlugin {
|
|
443
|
-
name = "~method-override";
|
|
444
|
-
/**
|
|
445
|
-
* Should override batch sub-request methods, not the original batch request.
|
|
446
|
-
*/
|
|
447
|
-
before = ["~batch"];
|
|
448
|
-
param;
|
|
449
|
-
methods;
|
|
450
|
-
constructor(options = {}) {
|
|
451
|
-
this.param = options.param ?? "method";
|
|
452
|
-
this.methods = new Set((options.methods ?? ["PUT", "PATCH", "DELETE"]).map((method) => method.toUpperCase()));
|
|
453
|
-
}
|
|
454
|
-
init(options) {
|
|
455
|
-
const routingInterceptor = async ({ next, ...interceptorOptions }) => {
|
|
456
|
-
const { request } = interceptorOptions;
|
|
457
|
-
if (request.method !== "POST") {
|
|
458
|
-
return next();
|
|
459
|
-
}
|
|
460
|
-
const [pathname, search, hash] = parseStandardUrl(request.url);
|
|
461
|
-
const params = new URLSearchParams(search);
|
|
462
|
-
const raw = params.getAll(this.param).at(-1);
|
|
463
|
-
if (raw === void 0) {
|
|
464
|
-
return next();
|
|
465
|
-
}
|
|
466
|
-
const method = raw.toUpperCase();
|
|
467
|
-
if (!this.methods.has(method)) {
|
|
468
|
-
return next();
|
|
469
|
-
}
|
|
470
|
-
params.delete(this.param);
|
|
471
|
-
const url = `${pathname}${params.size ? `?${params}` : ""}${hash ?? ""}`;
|
|
472
|
-
return next({
|
|
473
|
-
...interceptorOptions,
|
|
474
|
-
request: { ...request, method, url }
|
|
475
|
-
});
|
|
476
|
-
};
|
|
477
|
-
return {
|
|
478
|
-
...options,
|
|
479
|
-
routingInterceptors: [routingInterceptor, ...toArray(options.routingInterceptors)]
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const MAX_CONTENT_ENCODINGS = 5;
|
|
485
|
-
class RequestCompressionHandlerPlugin {
|
|
486
|
-
name = "~request-compression";
|
|
487
|
-
/**
|
|
488
|
-
* Should decompress the original batch request body instead of sub-requests.
|
|
489
|
-
*/
|
|
490
|
-
after = ["~batch"];
|
|
491
|
-
init(options) {
|
|
492
|
-
const routingInterceptor = async ({ next, ...interceptorOptions }) => {
|
|
493
|
-
const encodings = parseContentEncodings(
|
|
494
|
-
flattenStandardHeader(interceptorOptions.request.headers["content-encoding"])
|
|
495
|
-
);
|
|
496
|
-
if (encodings.length === 0 || !encodings.every(isSupportedEncoding)) {
|
|
497
|
-
return next();
|
|
498
|
-
}
|
|
499
|
-
const decompressedHeaders = {
|
|
500
|
-
...interceptorOptions.request.headers,
|
|
501
|
-
"content-length": void 0,
|
|
502
|
-
"content-encoding": void 0
|
|
503
|
-
};
|
|
504
|
-
return next({
|
|
505
|
-
...interceptorOptions,
|
|
506
|
-
request: {
|
|
507
|
-
...interceptorOptions.request,
|
|
508
|
-
headers: decompressedHeaders,
|
|
509
|
-
async resolveBody(hint) {
|
|
510
|
-
const stream = await interceptorOptions.request.resolveBody("octet-stream");
|
|
511
|
-
if (!(stream instanceof ReadableStream)) {
|
|
512
|
-
return stream;
|
|
513
|
-
}
|
|
514
|
-
if (encodings.length > MAX_CONTENT_ENCODINGS) {
|
|
515
|
-
throw new ORPCError("UNSUPPORTED_MEDIA_TYPE", { message: "Too many content encodings." });
|
|
516
|
-
}
|
|
517
|
-
let decompressedStream = stream;
|
|
518
|
-
for (let i = encodings.length - 1; i >= 0; i--) {
|
|
519
|
-
decompressedStream = decompressedStream.pipeThrough(
|
|
520
|
-
new DecompressionStream(encodings[i])
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
const response = new Response(decompressedStream, {
|
|
524
|
-
headers: toFetchHeaders(decompressedHeaders)
|
|
525
|
-
});
|
|
526
|
-
return toStandardBody(response, { hint });
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
};
|
|
531
|
-
return {
|
|
532
|
-
...options,
|
|
533
|
-
routingInterceptors: [
|
|
534
|
-
routingInterceptor,
|
|
535
|
-
...toArray(options.routingInterceptors)
|
|
536
|
-
]
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
const SUPPORTED_ENCODINGS = ["gzip", "deflate", "deflate-raw"];
|
|
541
|
-
function isSupportedEncoding(encoding) {
|
|
542
|
-
return SUPPORTED_ENCODINGS.includes(encoding);
|
|
543
|
-
}
|
|
544
|
-
function parseContentEncodings(header) {
|
|
545
|
-
if (header === void 0) {
|
|
546
|
-
return [];
|
|
547
|
-
}
|
|
548
|
-
return header.split(",").map((part) => part.trim().toLowerCase());
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
class RequestHeadersHandlerPlugin {
|
|
552
|
-
name = "~request-headers";
|
|
553
|
-
init(options) {
|
|
554
|
-
return {
|
|
555
|
-
...options,
|
|
556
|
-
interceptors: [
|
|
557
|
-
(interceptorOptions) => {
|
|
558
|
-
const reqHeaders = interceptorOptions.context.reqHeaders ?? toFetchHeaders(interceptorOptions.request.headers);
|
|
559
|
-
return interceptorOptions.next({
|
|
560
|
-
...interceptorOptions,
|
|
561
|
-
context: {
|
|
562
|
-
...interceptorOptions.context,
|
|
563
|
-
reqHeaders
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
},
|
|
567
|
-
...toArray(options.interceptors)
|
|
568
|
-
]
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const AVG_BYTES_PER_CHAR = 1.2;
|
|
574
|
-
class ResponseCompressionHandlerPlugin {
|
|
575
|
-
name = "~response-compression";
|
|
576
|
-
/**
|
|
577
|
-
* Compression should be done after batching, to compress the final response.
|
|
578
|
-
* Compression should also be done after response headers are set, to access final headers like Content-Type and Cache-Control.
|
|
579
|
-
*/
|
|
580
|
-
after = ["~batch", "~response-headers"];
|
|
581
|
-
encodings;
|
|
582
|
-
threshold;
|
|
583
|
-
isCompressibleContentType;
|
|
584
|
-
constructor(options = {}) {
|
|
585
|
-
this.encodings = options.encodings ?? ["gzip", "deflate"];
|
|
586
|
-
this.threshold = options.threshold ?? 1024;
|
|
587
|
-
this.isCompressibleContentType = options.isCompressibleContentType ?? isCompressibleContentType;
|
|
588
|
-
}
|
|
589
|
-
init(options) {
|
|
590
|
-
const routingInterceptor = async ({ next, ...interceptorOptions }) => {
|
|
591
|
-
const result = await next();
|
|
592
|
-
if (!result.matched) {
|
|
593
|
-
return result;
|
|
594
|
-
}
|
|
595
|
-
const response = result.response;
|
|
596
|
-
const encoding = negotiateResponseCompressionEncoding(response, interceptorOptions.request.headers, this.encodings);
|
|
597
|
-
if (encoding === void 0) {
|
|
598
|
-
return result;
|
|
599
|
-
}
|
|
600
|
-
const body = response.body;
|
|
601
|
-
const headers = response.headers;
|
|
602
|
-
if (body instanceof ReadableStream) {
|
|
603
|
-
const contentLength = Number(flattenStandardHeader(headers["content-length"]));
|
|
604
|
-
if ((!Number.isFinite(contentLength) || contentLength >= this.threshold) && this.isCompressibleContentType(flattenStandardHeader(headers["content-type"]), interceptorOptions)) {
|
|
605
|
-
return {
|
|
606
|
-
...result,
|
|
607
|
-
response: {
|
|
608
|
-
...response,
|
|
609
|
-
body: body.pipeThrough(new CompressionStream(encoding)),
|
|
610
|
-
headers: toCompressedHeaders(headers, encoding)
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
} else if (body instanceof Blob) {
|
|
615
|
-
const contentType = headers["content-type"] ?? body.type;
|
|
616
|
-
if ((!Number.isFinite(body.size) || body.size >= this.threshold) && this.isCompressibleContentType(flattenStandardHeader(contentType), interceptorOptions)) {
|
|
617
|
-
const contentDisposition = headers["content-disposition"] ?? generateContentDisposition(
|
|
618
|
-
body instanceof File ? body.name : "blob"
|
|
619
|
-
);
|
|
620
|
-
return {
|
|
621
|
-
...result,
|
|
622
|
-
response: {
|
|
623
|
-
...response,
|
|
624
|
-
body: body.stream().pipeThrough(new CompressionStream(encoding)),
|
|
625
|
-
headers: {
|
|
626
|
-
...toCompressedHeaders(headers, encoding),
|
|
627
|
-
"standard-server": headers["standard-server"] ?? "file",
|
|
628
|
-
"content-type": contentType,
|
|
629
|
-
"content-disposition": contentDisposition
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
} else if (body instanceof FormData) {
|
|
635
|
-
const PART_OVERHEAD = 64;
|
|
636
|
-
let contentLength = 0;
|
|
637
|
-
for (const [key, value] of body) {
|
|
638
|
-
contentLength += PART_OVERHEAD + key.length;
|
|
639
|
-
if (value instanceof Blob) {
|
|
640
|
-
if (!Number.isFinite(value.size)) {
|
|
641
|
-
if (!this.isCompressibleContentType(value.type, interceptorOptions)) {
|
|
642
|
-
contentLength = -Infinity;
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
|
-
contentLength = Infinity;
|
|
646
|
-
} else {
|
|
647
|
-
contentLength += this.isCompressibleContentType(value.type, interceptorOptions) ? value.size : -value.size;
|
|
648
|
-
}
|
|
649
|
-
} else {
|
|
650
|
-
contentLength += value.length * AVG_BYTES_PER_CHAR;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (contentLength >= this.threshold) {
|
|
654
|
-
const res = new Response(body);
|
|
655
|
-
const compressedStream = res.body.pipeThrough(new CompressionStream(encoding));
|
|
656
|
-
return {
|
|
657
|
-
...result,
|
|
658
|
-
response: {
|
|
659
|
-
...response,
|
|
660
|
-
body: compressedStream,
|
|
661
|
-
headers: {
|
|
662
|
-
...toCompressedHeaders(headers, encoding),
|
|
663
|
-
"standard-server": [],
|
|
664
|
-
"content-type": res.headers.get("content-type")
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
} else if (body instanceof URLSearchParams) {
|
|
670
|
-
const string = body.toString();
|
|
671
|
-
if (string.length * AVG_BYTES_PER_CHAR >= this.threshold) {
|
|
672
|
-
const [, bodyHeaders] = toFetchBody(body, headers);
|
|
673
|
-
return {
|
|
674
|
-
...result,
|
|
675
|
-
response: {
|
|
676
|
-
...response,
|
|
677
|
-
body: new Blob([string]).stream().pipeThrough(new CompressionStream(encoding)),
|
|
678
|
-
headers: {
|
|
679
|
-
...toCompressedHeaders(bodyHeaders, encoding),
|
|
680
|
-
// An empty hint rather than none, so the adapter does not hint the compressed stream as `octet-stream`
|
|
681
|
-
"standard-server": bodyHeaders["standard-server"] ?? []
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
} else if (body !== void 0 && !isAsyncIteratorObject(body)) {
|
|
687
|
-
const [string, bodyHeaders] = toFetchBody(body, headers);
|
|
688
|
-
if (string.length * AVG_BYTES_PER_CHAR >= this.threshold) {
|
|
689
|
-
return {
|
|
690
|
-
...result,
|
|
691
|
-
response: {
|
|
692
|
-
...response,
|
|
693
|
-
body: new Blob([string]).stream().pipeThrough(new CompressionStream(encoding)),
|
|
694
|
-
headers: {
|
|
695
|
-
...toCompressedHeaders(bodyHeaders, encoding),
|
|
696
|
-
"standard-server": bodyHeaders["standard-server"] ?? []
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
return result;
|
|
703
|
-
};
|
|
704
|
-
return {
|
|
705
|
-
...options,
|
|
706
|
-
routingInterceptors: [
|
|
707
|
-
routingInterceptor,
|
|
708
|
-
...toArray(options.routingInterceptors)
|
|
709
|
-
]
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
function negotiateResponseCompressionEncoding(response, requestHeaders, encodings) {
|
|
714
|
-
if (flattenStandardHeader(response.headers["content-encoding"]) !== void 0) {
|
|
715
|
-
return void 0;
|
|
716
|
-
}
|
|
717
|
-
if (response.status === 206 || flattenStandardHeader(response.headers["content-range"]) !== void 0) {
|
|
718
|
-
return void 0;
|
|
719
|
-
}
|
|
720
|
-
if (isNoTransformCacheControl(flattenStandardHeader(response.headers["cache-control"]))) {
|
|
721
|
-
return void 0;
|
|
722
|
-
}
|
|
723
|
-
const acceptEncodings = parseAcceptEncodingQualities(flattenStandardHeader(requestHeaders["accept-encoding"]));
|
|
724
|
-
return encodings.find((encoding) => isAcceptableEncoding(acceptEncodings, encoding));
|
|
725
|
-
}
|
|
726
|
-
function toCompressedHeaders(headers, encoding) {
|
|
727
|
-
return {
|
|
728
|
-
...headers,
|
|
729
|
-
"content-length": [],
|
|
730
|
-
"content-encoding": encoding,
|
|
731
|
-
"vary": varyByAcceptEncoding(flattenStandardHeader(headers.vary)),
|
|
732
|
-
/**
|
|
733
|
-
* A strong tag shared with the identity bytes would let `If-Range` splice them after compressed ones.
|
|
734
|
-
* A weak tag still revalidates through `If-None-Match`, and the compressed body serves no ranges.
|
|
735
|
-
*
|
|
736
|
-
* @see https://www.rfc-editor.org/rfc/rfc9110.html#name-etag
|
|
737
|
-
*/
|
|
738
|
-
"etag": weakenEtag(flattenStandardHeader(headers.etag)),
|
|
739
|
-
"accept-ranges": []
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function weakenEtag(etag) {
|
|
743
|
-
return etag === void 0 || etag.startsWith("W/") ? etag : `W/${etag}`;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
class ResponseHeadersHandlerPlugin {
|
|
747
|
-
name = "~response-headers";
|
|
748
|
-
/**
|
|
749
|
-
* Interceptors should run after batch interceptors so headers are applied to each sub-response.
|
|
750
|
-
*/
|
|
751
|
-
before = ["~batch"];
|
|
752
|
-
init(options) {
|
|
753
|
-
return {
|
|
754
|
-
...options,
|
|
755
|
-
routingInterceptors: [
|
|
756
|
-
async (interceptorOptions) => {
|
|
757
|
-
const resHeaders = new Headers(interceptorOptions.context.resHeaders);
|
|
758
|
-
const result = await interceptorOptions.next({
|
|
759
|
-
...interceptorOptions,
|
|
760
|
-
context: {
|
|
761
|
-
...interceptorOptions.context,
|
|
762
|
-
resHeaders
|
|
763
|
-
}
|
|
764
|
-
});
|
|
765
|
-
if (!result.response) {
|
|
766
|
-
return result;
|
|
767
|
-
}
|
|
768
|
-
return {
|
|
769
|
-
...result,
|
|
770
|
-
response: {
|
|
771
|
-
...result.response,
|
|
772
|
-
headers: mergeStandardHeaders(result.response.headers, toStandardHeaders(resHeaders))
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
},
|
|
776
|
-
...toArray(options.routingInterceptors)
|
|
777
|
-
]
|
|
778
|
-
};
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
class RethrowHandlerPlugin {
|
|
783
|
-
name = "~rethrow";
|
|
784
|
-
filter;
|
|
785
|
-
CONTEXT_SYMBOL = Symbol("ORPC_RETHROW_HANDLER_PLUGIN_CONTEXT");
|
|
786
|
-
constructor(options) {
|
|
787
|
-
this.filter = options.filter;
|
|
788
|
-
}
|
|
789
|
-
init(options) {
|
|
790
|
-
const routingInterceptor = async (options2) => {
|
|
791
|
-
const pluginContext = {};
|
|
792
|
-
const result = await options2.next({
|
|
793
|
-
...options2,
|
|
794
|
-
context: {
|
|
795
|
-
...options2.context,
|
|
796
|
-
[this.CONTEXT_SYMBOL]: pluginContext
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
if (pluginContext.error) {
|
|
800
|
-
throw pluginContext.error.value;
|
|
801
|
-
}
|
|
802
|
-
return result;
|
|
803
|
-
};
|
|
804
|
-
const interceptor = async (options2) => {
|
|
805
|
-
const pluginContext = options2.context[this.CONTEXT_SYMBOL];
|
|
806
|
-
if (!pluginContext) {
|
|
807
|
-
throw new TypeError("[RethrowHandlerPlugin] Rethrow handler context has been corrupted or modified by another plugin or interceptor");
|
|
808
|
-
}
|
|
809
|
-
try {
|
|
810
|
-
return await options2.next();
|
|
811
|
-
} catch (error) {
|
|
812
|
-
if (this.filter(error, options2)) {
|
|
813
|
-
pluginContext.error = { value: error };
|
|
814
|
-
}
|
|
815
|
-
throw error;
|
|
816
|
-
}
|
|
817
|
-
};
|
|
818
|
-
return {
|
|
819
|
-
...options,
|
|
820
|
-
routingInterceptors: [
|
|
821
|
-
...toArray(options.routingInterceptors),
|
|
822
|
-
routingInterceptor
|
|
823
|
-
// rethrow as early as possible and keep it close to `interceptor` to avoid a corrupted or modified context
|
|
824
|
-
],
|
|
825
|
-
interceptors: [
|
|
826
|
-
interceptor,
|
|
827
|
-
// catch as many errors as possible
|
|
828
|
-
...toArray(options.interceptors)
|
|
829
|
-
]
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
class SignalSyncHandlerPlugin {
|
|
835
|
-
name = "~signal-sync";
|
|
836
|
-
init(options) {
|
|
837
|
-
const clientInterceptor = async (interceptorOptions) => {
|
|
838
|
-
const controller = new AbortController();
|
|
839
|
-
const signal = anyAbortSignal([interceptorOptions.signal, controller.signal]);
|
|
840
|
-
const cancel = () => controller.abort(new AbortError("The response body was cancelled"));
|
|
841
|
-
const output = await interceptorOptions.next({ ...interceptorOptions, signal });
|
|
842
|
-
if (isAsyncIteratorObject(output)) {
|
|
843
|
-
return override(output, syncAsyncIterator(output, signal, cancel));
|
|
844
|
-
}
|
|
845
|
-
if (output instanceof ReadableStream) {
|
|
846
|
-
return override(output, syncReadableStream(output, signal, cancel));
|
|
847
|
-
}
|
|
848
|
-
return output;
|
|
849
|
-
};
|
|
850
|
-
return { ...options, clientInterceptors: [clientInterceptor, ...toArray(options.clientInterceptors)] };
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
function listenAbort(signal, onAbort) {
|
|
854
|
-
if (signal.aborted) {
|
|
855
|
-
onAbort();
|
|
856
|
-
} else {
|
|
857
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
858
|
-
}
|
|
859
|
-
return () => signal.removeEventListener("abort", onAbort);
|
|
860
|
-
}
|
|
861
|
-
function syncAsyncIterator(iterator, signal, cancel) {
|
|
862
|
-
let cancelling;
|
|
863
|
-
const unlisten = listenAbort(signal, () => {
|
|
864
|
-
cancelling = (async () => iterator.return?.())();
|
|
865
|
-
cancelling.catch(() => {
|
|
866
|
-
});
|
|
867
|
-
});
|
|
868
|
-
return new AsyncIteratorClass(
|
|
869
|
-
() => runWithSignal(signal, () => iterator.next()),
|
|
870
|
-
async ({ kind }) => {
|
|
871
|
-
if (kind === "cancelled") {
|
|
872
|
-
cancel();
|
|
873
|
-
await cancelling;
|
|
874
|
-
}
|
|
875
|
-
unlisten();
|
|
876
|
-
}
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
function syncReadableStream(stream, signal, cancel) {
|
|
880
|
-
const reader = stream.getReader();
|
|
881
|
-
let cancelling;
|
|
882
|
-
let unlisten;
|
|
883
|
-
return new ReadableStream({
|
|
884
|
-
start(controller) {
|
|
885
|
-
unlisten = listenAbort(signal, () => {
|
|
886
|
-
controller.error(signal.reason);
|
|
887
|
-
cancelling = reader.cancel(signal.reason);
|
|
888
|
-
cancelling.catch(() => {
|
|
889
|
-
});
|
|
890
|
-
});
|
|
891
|
-
},
|
|
892
|
-
async pull(controller) {
|
|
893
|
-
try {
|
|
894
|
-
const { done, value } = await reader.read();
|
|
895
|
-
if (signal.aborted) {
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
if (done) {
|
|
899
|
-
unlisten();
|
|
900
|
-
controller.close();
|
|
901
|
-
} else {
|
|
902
|
-
controller.enqueue(value);
|
|
903
|
-
}
|
|
904
|
-
} catch (error) {
|
|
905
|
-
unlisten();
|
|
906
|
-
throw error;
|
|
907
|
-
}
|
|
908
|
-
},
|
|
909
|
-
async cancel() {
|
|
910
|
-
cancel();
|
|
911
|
-
await cancelling;
|
|
912
|
-
}
|
|
913
|
-
}, { highWaterMark: 0 });
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
class TimeoutHandlerPlugin {
|
|
917
|
-
timeout;
|
|
918
|
-
streamingTimeout;
|
|
919
|
-
name = "~timeout";
|
|
920
|
-
constructor(options) {
|
|
921
|
-
this.timeout = options.timeout;
|
|
922
|
-
this.streamingTimeout = options.streamingTimeout;
|
|
923
|
-
}
|
|
924
|
-
init(options) {
|
|
925
|
-
const interceptor = async (interceptorOptions) => {
|
|
926
|
-
const timeoutMs = value(this.timeout, interceptorOptions);
|
|
927
|
-
const streamingTimeoutMs = value(this.streamingTimeout, interceptorOptions);
|
|
928
|
-
const hasTimeout = timeoutMs !== null && timeoutMs !== void 0;
|
|
929
|
-
const hasStreamingTimeout = streamingTimeoutMs !== null && streamingTimeoutMs !== void 0;
|
|
930
|
-
if (!hasTimeout && !hasStreamingTimeout) {
|
|
931
|
-
return interceptorOptions.next();
|
|
932
|
-
}
|
|
933
|
-
const controller = new AbortController();
|
|
934
|
-
const abortWithTimeout = (ms) => {
|
|
935
|
-
controller.abort(new AbortError(`Request timed out after ${ms}ms`));
|
|
936
|
-
};
|
|
937
|
-
const timeoutId = hasTimeout ? setTimeout(abortWithTimeout, timeoutMs, timeoutMs) : void 0;
|
|
938
|
-
const signal = anyAbortSignal([interceptorOptions.request.signal, controller.signal]);
|
|
939
|
-
try {
|
|
940
|
-
const response = await interceptorOptions.next({
|
|
941
|
-
...interceptorOptions,
|
|
942
|
-
request: { ...interceptorOptions.request, signal }
|
|
943
|
-
});
|
|
944
|
-
if (hasStreamingTimeout) {
|
|
945
|
-
const body = response.body;
|
|
946
|
-
const isIterator = isAsyncIteratorObject(body);
|
|
947
|
-
if (isIterator || body instanceof ReadableStream) {
|
|
948
|
-
const streamingTimeoutId = setTimeout(abortWithTimeout, streamingTimeoutMs, streamingTimeoutMs);
|
|
949
|
-
const onFinish = () => clearTimeout(streamingTimeoutId);
|
|
950
|
-
return {
|
|
951
|
-
...response,
|
|
952
|
-
body: isIterator ? override(body, wrapAsyncIterator(body, { onFinish })) : override(body, wrapReadableStream(body, { onFinish }))
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
return response;
|
|
957
|
-
} finally {
|
|
958
|
-
clearTimeout(timeoutId);
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
return { ...options, interceptors: [interceptor, ...toArray(options.interceptors)] };
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
export { BATCH_CONTENT_TYPE, BatchHandlerPlugin, CORSHandlerPlugin, CORSHandlerPlugin as CORSPlugin, GetMethodCsrfProtectionHandlerPlugin, MethodOverrideHandlerPlugin, RequestCompressionHandlerPlugin, RequestHeadersHandlerPlugin, RequestHeadersHandlerPlugin as RequestHeadersPlugin, ResponseCompressionHandlerPlugin, ResponseHeadersHandlerPlugin, ResponseHeadersHandlerPlugin as ResponseHeadersPlugin, RethrowHandlerPlugin, SignalSyncHandlerPlugin, TimeoutHandlerPlugin, negotiateResponseCompressionEncoding, toCompressedHeaders };
|