@whatwg-node/server 0.10.0-alpha-20240726141316-c6ce93b3598457ebe73b3b725986723af8f5e609 → 0.10.0-alpha-20241125124208-52a1220ca8b2a3b86a8338c8f96b80dde432e1bb
Sign up to get free protection for your applications and to get access to all the features.
- package/cjs/createServerAdapter.js +60 -23
- package/cjs/plugins/useContentEncoding.js +44 -18
- package/cjs/plugins/useErrorHandling.js +5 -1
- package/cjs/utils.js +117 -104
- package/cjs/uwebsockets.js +192 -54
- package/esm/createServerAdapter.js +62 -25
- package/esm/plugins/useContentEncoding.js +45 -19
- package/esm/plugins/useErrorHandling.js +5 -1
- package/esm/utils.js +115 -103
- package/esm/uwebsockets.js +190 -54
- package/package.json +4 -3
- package/typings/plugins/useCors.d.cts +1 -1
- package/typings/plugins/useCors.d.ts +1 -1
- package/typings/plugins/useErrorHandling.d.cts +2 -2
- package/typings/plugins/useErrorHandling.d.ts +2 -2
- package/typings/types.d.cts +10 -2
- package/typings/types.d.ts +10 -2
- package/typings/utils.d.cts +4 -2
- package/typings/utils.d.ts +4 -2
- package/typings/uwebsockets.d.cts +3 -1
- package/typings/uwebsockets.d.ts +3 -1
package/cjs/uwebsockets.js
CHANGED
@@ -2,50 +2,75 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.isUWSResponse = isUWSResponse;
|
4
4
|
exports.getRequestFromUWSRequest = getRequestFromUWSRequest;
|
5
|
+
exports.createWritableFromUWS = createWritableFromUWS;
|
5
6
|
exports.sendResponseToUwsOpts = sendResponseToUwsOpts;
|
7
|
+
exports.fakePromise = fakePromise;
|
8
|
+
const utils_js_1 = require("./utils.js");
|
6
9
|
function isUWSResponse(res) {
|
7
10
|
return !!res.onData;
|
8
11
|
}
|
9
12
|
function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
|
10
|
-
let body;
|
11
13
|
const method = req.getMethod();
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
readable.push(null);
|
23
|
-
});
|
24
|
-
res.onData(function (ab, isLast) {
|
25
|
-
const chunk = Buffer.from(ab, 0, ab.byteLength);
|
26
|
-
readable.push(Buffer.from(chunk));
|
27
|
-
if (isLast) {
|
28
|
-
readable.push(null);
|
29
|
-
}
|
30
|
-
});
|
14
|
+
let duplex;
|
15
|
+
const chunks = [];
|
16
|
+
const pushFns = [
|
17
|
+
(chunk) => {
|
18
|
+
chunks.push(chunk);
|
19
|
+
},
|
20
|
+
];
|
21
|
+
const push = (chunk) => {
|
22
|
+
for (const pushFn of pushFns) {
|
23
|
+
pushFn(chunk);
|
31
24
|
}
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
controller.enqueue(Buffer.from(chunk));
|
43
|
-
if (isLast) {
|
44
|
-
closed = true;
|
45
|
-
controller.close();
|
46
|
-
}
|
47
|
-
});
|
25
|
+
};
|
26
|
+
let stopped = false;
|
27
|
+
const stopFns = [
|
28
|
+
() => {
|
29
|
+
stopped = true;
|
30
|
+
},
|
31
|
+
];
|
32
|
+
const stop = () => {
|
33
|
+
for (const stopFn of stopFns) {
|
34
|
+
stopFn();
|
48
35
|
}
|
36
|
+
};
|
37
|
+
res.onData(function (ab, isLast) {
|
38
|
+
push(Buffer.from(Buffer.from(ab, 0, ab.byteLength)));
|
39
|
+
if (isLast) {
|
40
|
+
stop();
|
41
|
+
}
|
42
|
+
});
|
43
|
+
let getReadableStream;
|
44
|
+
if (method !== 'get' && method !== 'head') {
|
45
|
+
duplex = 'half';
|
46
|
+
signal.addEventListener('abort', () => {
|
47
|
+
stop();
|
48
|
+
});
|
49
|
+
let readableStream;
|
50
|
+
getReadableStream = () => {
|
51
|
+
if (!readableStream) {
|
52
|
+
readableStream = new fetchAPI.ReadableStream({
|
53
|
+
start(controller) {
|
54
|
+
for (const chunk of chunks) {
|
55
|
+
controller.enqueue(chunk);
|
56
|
+
}
|
57
|
+
if (stopped) {
|
58
|
+
controller.close();
|
59
|
+
return;
|
60
|
+
}
|
61
|
+
pushFns.push((chunk) => {
|
62
|
+
controller.enqueue(chunk);
|
63
|
+
});
|
64
|
+
stopFns.push(() => {
|
65
|
+
if (controller.desiredSize) {
|
66
|
+
controller.close();
|
67
|
+
}
|
68
|
+
});
|
69
|
+
},
|
70
|
+
});
|
71
|
+
}
|
72
|
+
return readableStream;
|
73
|
+
};
|
49
74
|
}
|
50
75
|
const headers = new fetchAPI.Headers();
|
51
76
|
req.forEach((key, value) => {
|
@@ -56,30 +81,97 @@ function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
|
|
56
81
|
if (query) {
|
57
82
|
url += `?${query}`;
|
58
83
|
}
|
59
|
-
|
84
|
+
let buffer;
|
85
|
+
function getBody() {
|
86
|
+
if (!getReadableStream) {
|
87
|
+
return null;
|
88
|
+
}
|
89
|
+
if (stopped) {
|
90
|
+
return getBufferFromChunks();
|
91
|
+
}
|
92
|
+
return getReadableStream();
|
93
|
+
}
|
94
|
+
const request = new fetchAPI.Request(url, {
|
60
95
|
method,
|
61
96
|
headers,
|
62
|
-
body
|
97
|
+
get body() {
|
98
|
+
return getBody();
|
99
|
+
},
|
63
100
|
signal,
|
64
101
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
65
102
|
// @ts-ignore - not in the TS types yet
|
66
|
-
duplex
|
103
|
+
duplex,
|
67
104
|
});
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
if (signal.aborted) {
|
72
|
-
return;
|
105
|
+
function getBufferFromChunks() {
|
106
|
+
if (!buffer) {
|
107
|
+
buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
73
108
|
}
|
74
|
-
|
75
|
-
|
109
|
+
return buffer;
|
110
|
+
}
|
111
|
+
function collectBuffer() {
|
112
|
+
if (stopped) {
|
113
|
+
return fakePromise(getBufferFromChunks());
|
114
|
+
}
|
115
|
+
return new Promise((resolve, reject) => {
|
116
|
+
try {
|
117
|
+
stopFns.push(() => {
|
118
|
+
resolve(getBufferFromChunks());
|
119
|
+
});
|
120
|
+
}
|
121
|
+
catch (e) {
|
122
|
+
reject(e);
|
123
|
+
}
|
76
124
|
});
|
77
125
|
}
|
78
|
-
|
79
|
-
|
126
|
+
Object.defineProperties(request, {
|
127
|
+
body: {
|
128
|
+
get() {
|
129
|
+
return getBody();
|
130
|
+
},
|
131
|
+
configurable: true,
|
132
|
+
enumerable: true,
|
133
|
+
},
|
134
|
+
json: {
|
135
|
+
value() {
|
136
|
+
return collectBuffer()
|
137
|
+
.then(b => b.toString('utf8'))
|
138
|
+
.then(t => JSON.parse(t));
|
139
|
+
},
|
140
|
+
configurable: true,
|
141
|
+
enumerable: true,
|
142
|
+
},
|
143
|
+
text: {
|
144
|
+
value() {
|
145
|
+
return collectBuffer().then(b => b.toString('utf8'));
|
146
|
+
},
|
147
|
+
configurable: true,
|
148
|
+
enumerable: true,
|
149
|
+
},
|
150
|
+
arrayBuffer: {
|
151
|
+
value() {
|
152
|
+
return collectBuffer();
|
153
|
+
},
|
154
|
+
configurable: true,
|
155
|
+
enumerable: true,
|
156
|
+
},
|
80
157
|
});
|
158
|
+
return request;
|
81
159
|
}
|
82
|
-
function
|
160
|
+
function createWritableFromUWS(uwsResponse, fetchAPI) {
|
161
|
+
return new fetchAPI.WritableStream({
|
162
|
+
write(chunk) {
|
163
|
+
uwsResponse.cork(() => {
|
164
|
+
uwsResponse.write(chunk);
|
165
|
+
});
|
166
|
+
},
|
167
|
+
close() {
|
168
|
+
uwsResponse.cork(() => {
|
169
|
+
uwsResponse.end();
|
170
|
+
});
|
171
|
+
},
|
172
|
+
});
|
173
|
+
}
|
174
|
+
function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal, fetchAPI) {
|
83
175
|
if (!fetchResponse) {
|
84
176
|
uwsResponse.writeStatus('404 Not Found');
|
85
177
|
uwsResponse.end();
|
@@ -109,13 +201,59 @@ function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
|
|
109
201
|
if (bufferOfRes) {
|
110
202
|
uwsResponse.end(bufferOfRes);
|
111
203
|
}
|
204
|
+
else if (!fetchResponse.body) {
|
205
|
+
uwsResponse.end();
|
206
|
+
}
|
112
207
|
});
|
113
|
-
if (bufferOfRes) {
|
208
|
+
if (bufferOfRes || !fetchResponse.body) {
|
114
209
|
return;
|
115
210
|
}
|
116
|
-
|
117
|
-
|
118
|
-
|
211
|
+
signal.addEventListener('abort', () => {
|
212
|
+
if (!fetchResponse.body?.locked) {
|
213
|
+
fetchResponse.body?.cancel(signal.reason);
|
214
|
+
}
|
215
|
+
});
|
216
|
+
return fetchResponse.body
|
217
|
+
.pipeTo(createWritableFromUWS(uwsResponse, fetchAPI), {
|
218
|
+
signal,
|
219
|
+
})
|
220
|
+
.catch(err => {
|
221
|
+
if (signal.aborted) {
|
222
|
+
return;
|
223
|
+
}
|
224
|
+
throw err;
|
225
|
+
});
|
226
|
+
}
|
227
|
+
function fakePromise(value) {
|
228
|
+
if ((0, utils_js_1.isPromise)(value)) {
|
229
|
+
return value;
|
119
230
|
}
|
120
|
-
|
231
|
+
// Write a fake promise to avoid the promise constructor
|
232
|
+
// being called with `new Promise` in the browser.
|
233
|
+
return {
|
234
|
+
then(resolve) {
|
235
|
+
if (resolve) {
|
236
|
+
const callbackResult = resolve(value);
|
237
|
+
if ((0, utils_js_1.isPromise)(callbackResult)) {
|
238
|
+
return callbackResult;
|
239
|
+
}
|
240
|
+
return fakePromise(callbackResult);
|
241
|
+
}
|
242
|
+
return this;
|
243
|
+
},
|
244
|
+
catch() {
|
245
|
+
return this;
|
246
|
+
},
|
247
|
+
finally(cb) {
|
248
|
+
if (cb) {
|
249
|
+
const callbackResult = cb();
|
250
|
+
if ((0, utils_js_1.isPromise)(callbackResult)) {
|
251
|
+
return callbackResult.then(() => value);
|
252
|
+
}
|
253
|
+
return fakePromise(value);
|
254
|
+
}
|
255
|
+
return this;
|
256
|
+
},
|
257
|
+
[Symbol.toStringTag]: 'Promise',
|
258
|
+
};
|
121
259
|
}
|
@@ -1,10 +1,8 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-types */
|
2
|
+
import { AsyncDisposableStack, DisposableSymbols } from '@whatwg-node/disposablestack';
|
2
3
|
import * as DefaultFetchAPI from '@whatwg-node/fetch';
|
3
|
-
import { completeAssign, handleAbortSignalAndPromiseResponse, handleErrorFromRequestHandler, isFetchEvent, isNodeRequest, isolateObject, isPromise, isRequestInit, isServerResponse, iterateAsyncVoid, normalizeNodeRequest, sendNodeResponse, ServerAdapterRequestAbortSignal, } from './utils.js';
|
4
|
-
import { getRequestFromUWSRequest, isUWSResponse, sendResponseToUwsOpts, } from './uwebsockets.js';
|
5
|
-
async function handleWaitUntils(waitUntilPromises) {
|
6
|
-
await Promise.allSettled(waitUntilPromises);
|
7
|
-
}
|
4
|
+
import { completeAssign, ensureDisposableStackRegisteredForTerminateEvents, handleAbortSignalAndPromiseResponse, handleErrorFromRequestHandler, isFetchEvent, isNodeRequest, isolateObject, isPromise, isRequestInit, isServerResponse, iterateAsyncVoid, nodeRequestResponseMap, normalizeNodeRequest, sendNodeResponse, ServerAdapterRequestAbortSignal, } from './utils.js';
|
5
|
+
import { fakePromise, getRequestFromUWSRequest, isUWSResponse, sendResponseToUwsOpts, } from './uwebsockets.js';
|
8
6
|
// Required for envs like nextjs edge runtime
|
9
7
|
function isRequestAccessible(serverContext) {
|
10
8
|
try {
|
@@ -25,6 +23,37 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
25
23
|
: serverAdapterBaseObject.handle;
|
26
24
|
const onRequestHooks = [];
|
27
25
|
const onResponseHooks = [];
|
26
|
+
const waitUntilPromises = new Set();
|
27
|
+
const disposableStack = new AsyncDisposableStack();
|
28
|
+
const signals = new Set();
|
29
|
+
function registerSignal(signal) {
|
30
|
+
signals.add(signal);
|
31
|
+
signal.addEventListener('abort', () => {
|
32
|
+
signals.delete(signal);
|
33
|
+
});
|
34
|
+
}
|
35
|
+
disposableStack.defer(() => {
|
36
|
+
for (const signal of signals) {
|
37
|
+
signal.sendAbort();
|
38
|
+
}
|
39
|
+
});
|
40
|
+
disposableStack.defer(() => {
|
41
|
+
if (waitUntilPromises.size > 0) {
|
42
|
+
return Promise.allSettled(waitUntilPromises).then(() => { }, () => { });
|
43
|
+
}
|
44
|
+
});
|
45
|
+
function waitUntil(promiseLike) {
|
46
|
+
// If it is a Node.js environment, we should register the disposable stack to handle process termination events
|
47
|
+
if (globalThis.process) {
|
48
|
+
ensureDisposableStackRegisteredForTerminateEvents(disposableStack);
|
49
|
+
}
|
50
|
+
waitUntilPromises.add(promiseLike.then(() => {
|
51
|
+
waitUntilPromises.delete(promiseLike);
|
52
|
+
}, err => {
|
53
|
+
console.error(`Unexpected error while waiting: ${err.message || err}`);
|
54
|
+
waitUntilPromises.delete(promiseLike);
|
55
|
+
}));
|
56
|
+
}
|
28
57
|
if (options?.plugins != null) {
|
29
58
|
for (const plugin of options.plugins) {
|
30
59
|
if (plugin.onRequest) {
|
@@ -103,20 +132,21 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
103
132
|
}
|
104
133
|
: givenHandleRequest;
|
105
134
|
// TODO: Remove this on the next major version
|
106
|
-
function
|
107
|
-
const nodeResponse = nodeResponseOrContainer.raw || nodeResponseOrContainer;
|
135
|
+
function handleNodeRequest(nodeRequest, ...ctx) {
|
108
136
|
const serverContext = ctx.length > 1 ? completeAssign(...ctx) : ctx[0] || {};
|
109
|
-
const request = normalizeNodeRequest(nodeRequest,
|
137
|
+
const request = normalizeNodeRequest(nodeRequest, fetchAPI, registerSignal);
|
110
138
|
return handleRequest(request, serverContext);
|
111
139
|
}
|
140
|
+
function handleNodeRequestAndResponse(nodeRequest, nodeResponseOrContainer, ...ctx) {
|
141
|
+
const nodeResponse = nodeResponseOrContainer.raw || nodeResponseOrContainer;
|
142
|
+
nodeRequestResponseMap.set(nodeRequest, nodeResponse);
|
143
|
+
return handleNodeRequest(nodeRequest, ...ctx);
|
144
|
+
}
|
112
145
|
function requestListener(nodeRequest, nodeResponse, ...ctx) {
|
113
|
-
const waitUntilPromises = [];
|
114
146
|
const defaultServerContext = {
|
115
147
|
req: nodeRequest,
|
116
148
|
res: nodeResponse,
|
117
|
-
waitUntil
|
118
|
-
waitUntilPromises.push(cb.catch(err => console.error(err)));
|
119
|
-
},
|
149
|
+
waitUntil,
|
120
150
|
};
|
121
151
|
let response$;
|
122
152
|
try {
|
@@ -141,19 +171,17 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
141
171
|
}
|
142
172
|
}
|
143
173
|
function handleUWS(res, req, ...ctx) {
|
144
|
-
const waitUntilPromises = [];
|
145
174
|
const defaultServerContext = {
|
146
175
|
res,
|
147
176
|
req,
|
148
|
-
waitUntil
|
149
|
-
waitUntilPromises.push(cb.catch(err => console.error(err)));
|
150
|
-
},
|
177
|
+
waitUntil,
|
151
178
|
};
|
152
179
|
const filteredCtxParts = ctx.filter(partCtx => partCtx != null);
|
153
180
|
const serverContext = filteredCtxParts.length > 0
|
154
181
|
? completeAssign(defaultServerContext, ...ctx)
|
155
182
|
: defaultServerContext;
|
156
183
|
const signal = new ServerAdapterRequestAbortSignal();
|
184
|
+
registerSignal(signal);
|
157
185
|
const originalResEnd = res.end.bind(res);
|
158
186
|
let resEnded = false;
|
159
187
|
res.end = function (data) {
|
@@ -185,7 +213,7 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
185
213
|
.catch((e) => handleErrorFromRequestHandler(e, fetchAPI.Response))
|
186
214
|
.then(response => {
|
187
215
|
if (!signal.aborted && !resEnded) {
|
188
|
-
return sendResponseToUwsOpts(res, response, signal);
|
216
|
+
return sendResponseToUwsOpts(res, response, signal, fetchAPI);
|
189
217
|
}
|
190
218
|
})
|
191
219
|
.catch(err => {
|
@@ -194,7 +222,7 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
194
222
|
}
|
195
223
|
try {
|
196
224
|
if (!signal.aborted && !resEnded) {
|
197
|
-
return sendResponseToUwsOpts(res, response$, signal);
|
225
|
+
return sendResponseToUwsOpts(res, response$, signal, fetchAPI);
|
198
226
|
}
|
199
227
|
}
|
200
228
|
catch (err) {
|
@@ -214,17 +242,12 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
214
242
|
}
|
215
243
|
function handleRequestWithWaitUntil(request, ...ctx) {
|
216
244
|
const filteredCtxParts = ctx.filter(partCtx => partCtx != null);
|
217
|
-
let waitUntilPromises;
|
218
245
|
const serverContext = filteredCtxParts.length > 1
|
219
246
|
? completeAssign({}, ...filteredCtxParts)
|
220
247
|
: isolateObject(filteredCtxParts[0], filteredCtxParts[0] == null || filteredCtxParts[0].waitUntil == null
|
221
|
-
?
|
248
|
+
? waitUntil
|
222
249
|
: undefined);
|
223
|
-
|
224
|
-
if (waitUntilPromises?.length) {
|
225
|
-
return handleWaitUntils(waitUntilPromises).then(() => response$);
|
226
|
-
}
|
227
|
-
return response$;
|
250
|
+
return handleRequest(request, serverContext);
|
228
251
|
}
|
229
252
|
const fetchFn = (input, ...maybeCtx) => {
|
230
253
|
if (typeof input === 'string' || 'href' in input) {
|
@@ -271,11 +294,25 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
271
294
|
const adapterObj = {
|
272
295
|
handleRequest: handleRequestWithWaitUntil,
|
273
296
|
fetch: fetchFn,
|
297
|
+
handleNodeRequest,
|
274
298
|
handleNodeRequestAndResponse,
|
275
299
|
requestListener,
|
276
300
|
handleEvent,
|
277
301
|
handleUWS,
|
278
302
|
handle: genericRequestHandler,
|
303
|
+
disposableStack,
|
304
|
+
[DisposableSymbols.asyncDispose]() {
|
305
|
+
if (!disposableStack.disposed) {
|
306
|
+
return disposableStack.disposeAsync();
|
307
|
+
}
|
308
|
+
return fakePromise(undefined);
|
309
|
+
},
|
310
|
+
dispose() {
|
311
|
+
if (!disposableStack.disposed) {
|
312
|
+
return disposableStack.disposeAsync();
|
313
|
+
}
|
314
|
+
return fakePromise(undefined);
|
315
|
+
},
|
279
316
|
};
|
280
317
|
const serverAdapter = new Proxy(genericRequestHandler, {
|
281
318
|
// It should have all the attributes of the handler function and the server instance
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { decompressedResponseMap, getSupportedEncodings } from '../utils.js';
|
1
|
+
import { decompressedResponseMap, getSupportedEncodings, isAsyncIterable, isReadable, } from '../utils.js';
|
2
2
|
export function useContentEncoding() {
|
3
3
|
const encodingMap = new WeakMap();
|
4
4
|
return {
|
@@ -18,7 +18,7 @@ export function useContentEncoding() {
|
|
18
18
|
for (const contentEncoding of contentEncodings) {
|
19
19
|
newBody = newBody.pipeThrough(new fetchAPI.DecompressionStream(contentEncoding));
|
20
20
|
}
|
21
|
-
|
21
|
+
request = new fetchAPI.Request(request.url, {
|
22
22
|
body: newBody,
|
23
23
|
cache: request.cache,
|
24
24
|
credentials: request.credentials,
|
@@ -35,7 +35,7 @@ export function useContentEncoding() {
|
|
35
35
|
// @ts-ignore - not in the TS types yet
|
36
36
|
duplex: 'half',
|
37
37
|
});
|
38
|
-
setRequest(
|
38
|
+
setRequest(request);
|
39
39
|
}
|
40
40
|
}
|
41
41
|
const acceptEncoding = request.headers.get('accept-encoding');
|
@@ -43,7 +43,8 @@ export function useContentEncoding() {
|
|
43
43
|
encodingMap.set(request, acceptEncoding.split(','));
|
44
44
|
}
|
45
45
|
},
|
46
|
-
onResponse({ request, response, setResponse, fetchAPI }) {
|
46
|
+
onResponse({ request, response, setResponse, fetchAPI, serverContext }) {
|
47
|
+
const waitUntil = serverContext.waitUntil?.bind(serverContext) || (() => { });
|
47
48
|
// Hack for avoiding to create whatwg-node to create a readable stream until it's needed
|
48
49
|
if (response['bodyInit'] || response.body) {
|
49
50
|
const encodings = encodingMap.get(request);
|
@@ -57,21 +58,15 @@ export function useContentEncoding() {
|
|
57
58
|
const bufOfRes = response._buffer;
|
58
59
|
if (bufOfRes) {
|
59
60
|
const writer = compressionStream.writable.getWriter();
|
60
|
-
writer.write(bufOfRes);
|
61
|
-
writer.close();
|
62
|
-
const
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
break;
|
70
|
-
}
|
71
|
-
else if (value) {
|
72
|
-
chunks.push(...value);
|
73
|
-
}
|
74
|
-
}
|
61
|
+
waitUntil(writer.write(bufOfRes));
|
62
|
+
waitUntil(writer.close());
|
63
|
+
const uint8Arrays$ = isReadable(compressionStream.readable['readable'])
|
64
|
+
? collectReadableValues(compressionStream.readable['readable'])
|
65
|
+
: isAsyncIterable(compressionStream.readable)
|
66
|
+
? collectAsyncIterableValues(compressionStream.readable)
|
67
|
+
: collectReadableStreamValues(compressionStream.readable);
|
68
|
+
return uint8Arrays$.then(uint8Arrays => {
|
69
|
+
const chunks = uint8Arrays.flatMap(uint8Array => [...uint8Array]);
|
75
70
|
const uint8Array = new Uint8Array(chunks);
|
76
71
|
const newHeaders = new fetchAPI.Headers(response.headers);
|
77
72
|
newHeaders.set('content-encoding', supportedEncoding);
|
@@ -82,6 +77,7 @@ export function useContentEncoding() {
|
|
82
77
|
});
|
83
78
|
decompressedResponseMap.set(compressedResponse, response);
|
84
79
|
setResponse(compressedResponse);
|
80
|
+
waitUntil(compressionStream.writable.close());
|
85
81
|
});
|
86
82
|
}
|
87
83
|
}
|
@@ -102,3 +98,33 @@ export function useContentEncoding() {
|
|
102
98
|
},
|
103
99
|
};
|
104
100
|
}
|
101
|
+
function collectReadableValues(readable) {
|
102
|
+
const values = [];
|
103
|
+
readable.on('data', value => values.push(value));
|
104
|
+
return new Promise((resolve, reject) => {
|
105
|
+
readable.once('end', () => resolve(values));
|
106
|
+
readable.once('error', reject);
|
107
|
+
});
|
108
|
+
}
|
109
|
+
async function collectAsyncIterableValues(asyncIterable) {
|
110
|
+
const values = [];
|
111
|
+
for await (const value of asyncIterable) {
|
112
|
+
values.push(value);
|
113
|
+
}
|
114
|
+
return values;
|
115
|
+
}
|
116
|
+
async function collectReadableStreamValues(readableStream) {
|
117
|
+
const reader = readableStream.getReader();
|
118
|
+
const values = [];
|
119
|
+
while (true) {
|
120
|
+
const { done, value } = await reader.read();
|
121
|
+
if (done) {
|
122
|
+
reader.releaseLock();
|
123
|
+
break;
|
124
|
+
}
|
125
|
+
else if (value) {
|
126
|
+
values.push(value);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
return values;
|
130
|
+
}
|
@@ -19,13 +19,17 @@ function createDefaultErrorResponse(ResponseCtor) {
|
|
19
19
|
return new ResponseCtor(null, { status: 500 });
|
20
20
|
}
|
21
21
|
export class HTTPError extends Error {
|
22
|
+
status;
|
23
|
+
message;
|
24
|
+
headers;
|
25
|
+
details;
|
26
|
+
name = 'HTTPError';
|
22
27
|
constructor(status = 500, message, headers = {}, details) {
|
23
28
|
super(message);
|
24
29
|
this.status = status;
|
25
30
|
this.message = message;
|
26
31
|
this.headers = headers;
|
27
32
|
this.details = details;
|
28
|
-
this.name = 'HTTPError';
|
29
33
|
Error.captureStackTrace(this, HTTPError);
|
30
34
|
}
|
31
35
|
}
|