@whatwg-node/server 0.10.0-alpha-20240726141316-c6ce93b3598457ebe73b3b725986723af8f5e609 → 0.10.0-alpha-20241123133536-975c9068dde45574fcfa26567e4bab96f45d1f85
Sign up to get free protection for your applications and to get access to all the features.
- package/cjs/createServerAdapter.js +53 -23
- package/cjs/plugins/useContentEncoding.js +44 -18
- package/cjs/plugins/useErrorHandling.js +5 -1
- package/cjs/utils.js +116 -104
- package/cjs/uwebsockets.js +192 -54
- package/esm/createServerAdapter.js +54 -24
- package/esm/plugins/useContentEncoding.js +45 -19
- package/esm/plugins/useErrorHandling.js +5 -1
- package/esm/utils.js +114 -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/esm/utils.js
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import { URL } from '@whatwg-node/fetch';
|
2
1
|
export function isAsyncIterable(body) {
|
3
2
|
return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
|
4
3
|
}
|
@@ -49,11 +48,9 @@ function isRequestBody(body) {
|
|
49
48
|
return false;
|
50
49
|
}
|
51
50
|
export class ServerAdapterRequestAbortSignal extends EventTarget {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
this._onabort = null;
|
56
|
-
}
|
51
|
+
aborted = false;
|
52
|
+
_onabort = null;
|
53
|
+
reason;
|
57
54
|
throwIfAborted() {
|
58
55
|
if (this.aborted) {
|
59
56
|
throw this.reason;
|
@@ -81,17 +78,20 @@ export class ServerAdapterRequestAbortSignal extends EventTarget {
|
|
81
78
|
}
|
82
79
|
}
|
83
80
|
let bunNodeCompatModeWarned = false;
|
84
|
-
export
|
81
|
+
export const nodeRequestResponseMap = new WeakMap();
|
82
|
+
export function normalizeNodeRequest(nodeRequest, fetchAPI, registerSignal) {
|
85
83
|
const rawRequest = nodeRequest.raw || nodeRequest.req || nodeRequest;
|
86
84
|
let fullUrl = buildFullUrl(rawRequest);
|
87
85
|
if (nodeRequest.query) {
|
88
|
-
const url = new URL(fullUrl);
|
86
|
+
const url = new fetchAPI.URL(fullUrl);
|
89
87
|
for (const key in nodeRequest.query) {
|
90
88
|
url.searchParams.set(key, nodeRequest.query[key]);
|
91
89
|
}
|
92
90
|
fullUrl = url.toString();
|
93
91
|
}
|
94
92
|
let signal;
|
93
|
+
const nodeResponse = nodeRequestResponseMap.get(nodeRequest);
|
94
|
+
nodeRequestResponseMap.delete(nodeRequest);
|
95
95
|
let normalizedHeaders = nodeRequest.headers;
|
96
96
|
if (nodeRequest.headers?.[':method']) {
|
97
97
|
normalizedHeaders = {};
|
@@ -104,8 +104,10 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
|
|
104
104
|
if (nodeResponse?.once) {
|
105
105
|
let sendAbortSignal;
|
106
106
|
// If ponyfilled
|
107
|
-
if (
|
108
|
-
|
107
|
+
if (fetchAPI.Request !== globalThis.Request) {
|
108
|
+
const newSignal = new ServerAdapterRequestAbortSignal();
|
109
|
+
registerSignal?.(newSignal);
|
110
|
+
signal = newSignal;
|
109
111
|
sendAbortSignal = () => signal.sendAbort();
|
110
112
|
}
|
111
113
|
else {
|
@@ -126,7 +128,7 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
|
|
126
128
|
});
|
127
129
|
}
|
128
130
|
if (nodeRequest.method === 'GET' || nodeRequest.method === 'HEAD') {
|
129
|
-
return new
|
131
|
+
return new fetchAPI.Request(fullUrl, {
|
130
132
|
method: nodeRequest.method,
|
131
133
|
headers: normalizedHeaders,
|
132
134
|
signal,
|
@@ -141,14 +143,14 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
|
|
141
143
|
const maybeParsedBody = nodeRequest.body;
|
142
144
|
if (maybeParsedBody != null && Object.keys(maybeParsedBody).length > 0) {
|
143
145
|
if (isRequestBody(maybeParsedBody)) {
|
144
|
-
return new
|
146
|
+
return new fetchAPI.Request(fullUrl, {
|
145
147
|
method: nodeRequest.method,
|
146
148
|
headers: normalizedHeaders,
|
147
149
|
body: maybeParsedBody,
|
148
150
|
signal,
|
149
151
|
});
|
150
152
|
}
|
151
|
-
const request = new
|
153
|
+
const request = new fetchAPI.Request(fullUrl, {
|
152
154
|
method: nodeRequest.method,
|
153
155
|
headers: normalizedHeaders,
|
154
156
|
signal,
|
@@ -176,7 +178,7 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
|
|
176
178
|
console.warn(`You use Bun Node compatibility mode, which is not recommended!
|
177
179
|
It will affect your performance. Please check our Bun integration recipe, and avoid using 'http' for your server implementation.`);
|
178
180
|
}
|
179
|
-
return new
|
181
|
+
return new fetchAPI.Request(fullUrl, {
|
180
182
|
method: nodeRequest.method,
|
181
183
|
headers: normalizedHeaders,
|
182
184
|
duplex: 'half',
|
@@ -200,7 +202,7 @@ It will affect your performance. Please check our Bun integration recipe, and av
|
|
200
202
|
});
|
201
203
|
}
|
202
204
|
// perf: instead of spreading the object, we can just pass it as is and it performs better
|
203
|
-
return new
|
205
|
+
return new fetchAPI.Request(fullUrl, {
|
204
206
|
method: nodeRequest.method,
|
205
207
|
headers: normalizedHeaders,
|
206
208
|
body: rawRequest,
|
@@ -238,11 +240,26 @@ function endResponse(serverResponse) {
|
|
238
240
|
serverResponse.end(null, null, null);
|
239
241
|
}
|
240
242
|
async function sendAsyncIterable(serverResponse, asyncIterable) {
|
243
|
+
let closed = false;
|
244
|
+
const closeEventListener = () => {
|
245
|
+
closed = true;
|
246
|
+
};
|
247
|
+
serverResponse.once('error', closeEventListener);
|
248
|
+
serverResponse.once('close', closeEventListener);
|
249
|
+
serverResponse.once('finish', () => {
|
250
|
+
serverResponse.removeListener('close', closeEventListener);
|
251
|
+
});
|
241
252
|
for await (const chunk of asyncIterable) {
|
253
|
+
if (closed) {
|
254
|
+
break;
|
255
|
+
}
|
242
256
|
if (!serverResponse
|
243
257
|
// @ts-expect-error http and http2 writes are actually compatible
|
244
258
|
.write(chunk)) {
|
245
|
-
|
259
|
+
if (closed) {
|
260
|
+
break;
|
261
|
+
}
|
262
|
+
await new Promise(resolve => serverResponse.once('drain', resolve));
|
246
263
|
}
|
247
264
|
}
|
248
265
|
endResponse(serverResponse);
|
@@ -253,7 +270,7 @@ export function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
|
|
253
270
|
}
|
254
271
|
if (!fetchResponse) {
|
255
272
|
serverResponse.statusCode = 404;
|
256
|
-
serverResponse
|
273
|
+
endResponse(serverResponse);
|
257
274
|
return;
|
258
275
|
}
|
259
276
|
serverResponse.statusCode = fetchResponse.status;
|
@@ -302,10 +319,31 @@ export function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
|
|
302
319
|
fetchBody.pipe(serverResponse);
|
303
320
|
return;
|
304
321
|
}
|
322
|
+
if (isReadableStream(fetchBody)) {
|
323
|
+
return sendReadableStream(serverResponse, fetchBody);
|
324
|
+
}
|
305
325
|
if (isAsyncIterable(fetchBody)) {
|
306
326
|
return sendAsyncIterable(serverResponse, fetchBody);
|
307
327
|
}
|
308
328
|
}
|
329
|
+
async function sendReadableStream(serverResponse, readableStream) {
|
330
|
+
const reader = readableStream.getReader();
|
331
|
+
serverResponse.req.once('error', err => {
|
332
|
+
reader.cancel(err);
|
333
|
+
});
|
334
|
+
while (true) {
|
335
|
+
const { done, value } = await reader.read();
|
336
|
+
if (done) {
|
337
|
+
break;
|
338
|
+
}
|
339
|
+
if (!serverResponse
|
340
|
+
// @ts-expect-error http and http2 writes are actually compatible
|
341
|
+
.write(value)) {
|
342
|
+
await new Promise(resolve => serverResponse.once('drain', resolve));
|
343
|
+
}
|
344
|
+
}
|
345
|
+
endResponse(serverResponse);
|
346
|
+
}
|
309
347
|
export function isRequestInit(val) {
|
310
348
|
return (val != null &&
|
311
349
|
typeof val === 'object' &&
|
@@ -382,77 +420,18 @@ export function handleErrorFromRequestHandler(error, ResponseCtor) {
|
|
382
420
|
status: error.status || 500,
|
383
421
|
});
|
384
422
|
}
|
385
|
-
export function isolateObject(originalCtx,
|
423
|
+
export function isolateObject(originalCtx, waitUntilFn) {
|
386
424
|
if (originalCtx == null) {
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
}
|
398
|
-
const extraPropVal = extraProps[prop];
|
399
|
-
if (extraPropVal != null) {
|
400
|
-
if (typeof extraPropVal === 'function') {
|
401
|
-
return extraPropVal.bind(extraProps);
|
402
|
-
}
|
403
|
-
return extraPropVal;
|
404
|
-
}
|
405
|
-
if (deletedProps.has(prop)) {
|
406
|
-
return undefined;
|
407
|
-
}
|
408
|
-
return originalCtx[prop];
|
409
|
-
},
|
410
|
-
set(_originalCtx, prop, value) {
|
411
|
-
extraProps[prop] = value;
|
412
|
-
return true;
|
413
|
-
},
|
414
|
-
has(originalCtx, prop) {
|
415
|
-
if (waitUntilPromises != null && prop === 'waitUntil') {
|
416
|
-
return true;
|
417
|
-
}
|
418
|
-
if (deletedProps.has(prop)) {
|
419
|
-
return false;
|
420
|
-
}
|
421
|
-
if (prop in extraProps) {
|
422
|
-
return true;
|
423
|
-
}
|
424
|
-
return prop in originalCtx;
|
425
|
-
},
|
426
|
-
defineProperty(_originalCtx, prop, descriptor) {
|
427
|
-
return Reflect.defineProperty(extraProps, prop, descriptor);
|
428
|
-
},
|
429
|
-
deleteProperty(_originalCtx, prop) {
|
430
|
-
if (prop in extraProps) {
|
431
|
-
return Reflect.deleteProperty(extraProps, prop);
|
432
|
-
}
|
433
|
-
deletedProps.add(prop);
|
434
|
-
return true;
|
435
|
-
},
|
436
|
-
ownKeys(originalCtx) {
|
437
|
-
const extraKeys = Reflect.ownKeys(extraProps);
|
438
|
-
const originalKeys = Reflect.ownKeys(originalCtx);
|
439
|
-
const deletedKeys = Array.from(deletedProps);
|
440
|
-
const allKeys = new Set(extraKeys.concat(originalKeys.filter(keys => !deletedKeys.includes(keys))));
|
441
|
-
if (waitUntilPromises != null) {
|
442
|
-
allKeys.add('waitUntil');
|
443
|
-
}
|
444
|
-
return Array.from(allKeys);
|
445
|
-
},
|
446
|
-
getOwnPropertyDescriptor(originalCtx, prop) {
|
447
|
-
if (prop in extraProps) {
|
448
|
-
return Reflect.getOwnPropertyDescriptor(extraProps, prop);
|
449
|
-
}
|
450
|
-
if (deletedProps.has(prop)) {
|
451
|
-
return undefined;
|
452
|
-
}
|
453
|
-
return Reflect.getOwnPropertyDescriptor(originalCtx, prop);
|
454
|
-
},
|
455
|
-
});
|
425
|
+
if (waitUntilFn == null) {
|
426
|
+
return {};
|
427
|
+
}
|
428
|
+
return {
|
429
|
+
waitUntil: waitUntilFn,
|
430
|
+
};
|
431
|
+
}
|
432
|
+
return completeAssign(Object.create(originalCtx), {
|
433
|
+
waitUntil: waitUntilFn,
|
434
|
+
}, originalCtx);
|
456
435
|
}
|
457
436
|
export function createDeferredPromise() {
|
458
437
|
let resolveFn;
|
@@ -494,22 +473,27 @@ export function getSupportedEncodings(fetchAPI) {
|
|
494
473
|
let supportedEncodings = supportedEncodingsByFetchAPI.get(fetchAPI);
|
495
474
|
if (!supportedEncodings) {
|
496
475
|
const possibleEncodings = ['deflate', 'gzip', 'deflate-raw', 'br'];
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
476
|
+
if (fetchAPI.DecompressionStream?.['supportedFormats']) {
|
477
|
+
supportedEncodings = fetchAPI.DecompressionStream['supportedFormats'];
|
478
|
+
}
|
479
|
+
else {
|
480
|
+
supportedEncodings = possibleEncodings.filter(encoding => {
|
481
|
+
// deflate-raw is not supported in Node.js >v20
|
482
|
+
if (globalThis.process?.version?.startsWith('v2') &&
|
483
|
+
fetchAPI.DecompressionStream === globalThis.DecompressionStream &&
|
484
|
+
encoding === 'deflate-raw') {
|
485
|
+
return false;
|
486
|
+
}
|
487
|
+
try {
|
488
|
+
// eslint-disable-next-line no-new
|
489
|
+
new fetchAPI.DecompressionStream(encoding);
|
490
|
+
return true;
|
491
|
+
}
|
492
|
+
catch {
|
493
|
+
return false;
|
494
|
+
}
|
495
|
+
});
|
496
|
+
}
|
513
497
|
supportedEncodingsByFetchAPI.set(fetchAPI, supportedEncodings);
|
514
498
|
}
|
515
499
|
return supportedEncodings;
|
@@ -540,3 +524,30 @@ export function handleResponseDecompression(response, fetchAPI) {
|
|
540
524
|
}
|
541
525
|
return decompressedResponse;
|
542
526
|
}
|
527
|
+
const terminateEvents = ['SIGINT', 'SIGTERM', 'exit'];
|
528
|
+
const disposableStacks = new Set();
|
529
|
+
let eventListenerRegistered = false;
|
530
|
+
function ensureEventListenerForDisposableStacks() {
|
531
|
+
if (eventListenerRegistered) {
|
532
|
+
return;
|
533
|
+
}
|
534
|
+
eventListenerRegistered = true;
|
535
|
+
for (const event of terminateEvents) {
|
536
|
+
globalThis.process.once(event, function terminateHandler() {
|
537
|
+
return Promise.allSettled([...disposableStacks].map(stack => stack.disposeAsync().catch(e => {
|
538
|
+
console.error('Error while disposing:', e);
|
539
|
+
})));
|
540
|
+
});
|
541
|
+
}
|
542
|
+
}
|
543
|
+
export function ensureDisposableStackRegisteredForTerminateEvents(disposableStack) {
|
544
|
+
if (globalThis.process) {
|
545
|
+
ensureEventListenerForDisposableStacks();
|
546
|
+
if (!disposableStacks.has(disposableStack)) {
|
547
|
+
disposableStacks.add(disposableStack);
|
548
|
+
disposableStack.defer(() => {
|
549
|
+
disposableStacks.delete(disposableStack);
|
550
|
+
});
|
551
|
+
}
|
552
|
+
}
|
553
|
+
}
|
package/esm/uwebsockets.js
CHANGED
@@ -1,46 +1,69 @@
|
|
1
|
+
import { isPromise } from './utils.js';
|
1
2
|
export function isUWSResponse(res) {
|
2
3
|
return !!res.onData;
|
3
4
|
}
|
4
5
|
export function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
|
5
|
-
let body;
|
6
6
|
const method = req.getMethod();
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
readable.push(null);
|
18
|
-
});
|
19
|
-
res.onData(function (ab, isLast) {
|
20
|
-
const chunk = Buffer.from(ab, 0, ab.byteLength);
|
21
|
-
readable.push(Buffer.from(chunk));
|
22
|
-
if (isLast) {
|
23
|
-
readable.push(null);
|
24
|
-
}
|
25
|
-
});
|
7
|
+
let duplex;
|
8
|
+
const chunks = [];
|
9
|
+
const pushFns = [
|
10
|
+
(chunk) => {
|
11
|
+
chunks.push(chunk);
|
12
|
+
},
|
13
|
+
];
|
14
|
+
const push = (chunk) => {
|
15
|
+
for (const pushFn of pushFns) {
|
16
|
+
pushFn(chunk);
|
26
17
|
}
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
controller.enqueue(Buffer.from(chunk));
|
38
|
-
if (isLast) {
|
39
|
-
closed = true;
|
40
|
-
controller.close();
|
41
|
-
}
|
42
|
-
});
|
18
|
+
};
|
19
|
+
let stopped = false;
|
20
|
+
const stopFns = [
|
21
|
+
() => {
|
22
|
+
stopped = true;
|
23
|
+
},
|
24
|
+
];
|
25
|
+
const stop = () => {
|
26
|
+
for (const stopFn of stopFns) {
|
27
|
+
stopFn();
|
43
28
|
}
|
29
|
+
};
|
30
|
+
res.onData(function (ab, isLast) {
|
31
|
+
push(Buffer.from(Buffer.from(ab, 0, ab.byteLength)));
|
32
|
+
if (isLast) {
|
33
|
+
stop();
|
34
|
+
}
|
35
|
+
});
|
36
|
+
let getReadableStream;
|
37
|
+
if (method !== 'get' && method !== 'head') {
|
38
|
+
duplex = 'half';
|
39
|
+
signal.addEventListener('abort', () => {
|
40
|
+
stop();
|
41
|
+
});
|
42
|
+
let readableStream;
|
43
|
+
getReadableStream = () => {
|
44
|
+
if (!readableStream) {
|
45
|
+
readableStream = new fetchAPI.ReadableStream({
|
46
|
+
start(controller) {
|
47
|
+
for (const chunk of chunks) {
|
48
|
+
controller.enqueue(chunk);
|
49
|
+
}
|
50
|
+
if (stopped) {
|
51
|
+
controller.close();
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
pushFns.push((chunk) => {
|
55
|
+
controller.enqueue(chunk);
|
56
|
+
});
|
57
|
+
stopFns.push(() => {
|
58
|
+
if (controller.desiredSize) {
|
59
|
+
controller.close();
|
60
|
+
}
|
61
|
+
});
|
62
|
+
},
|
63
|
+
});
|
64
|
+
}
|
65
|
+
return readableStream;
|
66
|
+
};
|
44
67
|
}
|
45
68
|
const headers = new fetchAPI.Headers();
|
46
69
|
req.forEach((key, value) => {
|
@@ -51,30 +74,97 @@ export function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
|
|
51
74
|
if (query) {
|
52
75
|
url += `?${query}`;
|
53
76
|
}
|
54
|
-
|
77
|
+
let buffer;
|
78
|
+
function getBody() {
|
79
|
+
if (!getReadableStream) {
|
80
|
+
return null;
|
81
|
+
}
|
82
|
+
if (stopped) {
|
83
|
+
return getBufferFromChunks();
|
84
|
+
}
|
85
|
+
return getReadableStream();
|
86
|
+
}
|
87
|
+
const request = new fetchAPI.Request(url, {
|
55
88
|
method,
|
56
89
|
headers,
|
57
|
-
body
|
90
|
+
get body() {
|
91
|
+
return getBody();
|
92
|
+
},
|
58
93
|
signal,
|
59
94
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
60
95
|
// @ts-ignore - not in the TS types yet
|
61
|
-
duplex
|
96
|
+
duplex,
|
62
97
|
});
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if (signal.aborted) {
|
67
|
-
return;
|
98
|
+
function getBufferFromChunks() {
|
99
|
+
if (!buffer) {
|
100
|
+
buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
|
68
101
|
}
|
69
|
-
|
70
|
-
|
102
|
+
return buffer;
|
103
|
+
}
|
104
|
+
function collectBuffer() {
|
105
|
+
if (stopped) {
|
106
|
+
return fakePromise(getBufferFromChunks());
|
107
|
+
}
|
108
|
+
return new Promise((resolve, reject) => {
|
109
|
+
try {
|
110
|
+
stopFns.push(() => {
|
111
|
+
resolve(getBufferFromChunks());
|
112
|
+
});
|
113
|
+
}
|
114
|
+
catch (e) {
|
115
|
+
reject(e);
|
116
|
+
}
|
71
117
|
});
|
72
118
|
}
|
73
|
-
|
74
|
-
|
119
|
+
Object.defineProperties(request, {
|
120
|
+
body: {
|
121
|
+
get() {
|
122
|
+
return getBody();
|
123
|
+
},
|
124
|
+
configurable: true,
|
125
|
+
enumerable: true,
|
126
|
+
},
|
127
|
+
json: {
|
128
|
+
value() {
|
129
|
+
return collectBuffer()
|
130
|
+
.then(b => b.toString('utf8'))
|
131
|
+
.then(t => JSON.parse(t));
|
132
|
+
},
|
133
|
+
configurable: true,
|
134
|
+
enumerable: true,
|
135
|
+
},
|
136
|
+
text: {
|
137
|
+
value() {
|
138
|
+
return collectBuffer().then(b => b.toString('utf8'));
|
139
|
+
},
|
140
|
+
configurable: true,
|
141
|
+
enumerable: true,
|
142
|
+
},
|
143
|
+
arrayBuffer: {
|
144
|
+
value() {
|
145
|
+
return collectBuffer();
|
146
|
+
},
|
147
|
+
configurable: true,
|
148
|
+
enumerable: true,
|
149
|
+
},
|
75
150
|
});
|
151
|
+
return request;
|
76
152
|
}
|
77
|
-
export function
|
153
|
+
export function createWritableFromUWS(uwsResponse, fetchAPI) {
|
154
|
+
return new fetchAPI.WritableStream({
|
155
|
+
write(chunk) {
|
156
|
+
uwsResponse.cork(() => {
|
157
|
+
uwsResponse.write(chunk);
|
158
|
+
});
|
159
|
+
},
|
160
|
+
close() {
|
161
|
+
uwsResponse.cork(() => {
|
162
|
+
uwsResponse.end();
|
163
|
+
});
|
164
|
+
},
|
165
|
+
});
|
166
|
+
}
|
167
|
+
export function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal, fetchAPI) {
|
78
168
|
if (!fetchResponse) {
|
79
169
|
uwsResponse.writeStatus('404 Not Found');
|
80
170
|
uwsResponse.end();
|
@@ -104,13 +194,59 @@ export function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
|
|
104
194
|
if (bufferOfRes) {
|
105
195
|
uwsResponse.end(bufferOfRes);
|
106
196
|
}
|
197
|
+
else if (!fetchResponse.body) {
|
198
|
+
uwsResponse.end();
|
199
|
+
}
|
107
200
|
});
|
108
|
-
if (bufferOfRes) {
|
201
|
+
if (bufferOfRes || !fetchResponse.body) {
|
109
202
|
return;
|
110
203
|
}
|
111
|
-
|
112
|
-
|
113
|
-
|
204
|
+
signal.addEventListener('abort', () => {
|
205
|
+
if (!fetchResponse.body?.locked) {
|
206
|
+
fetchResponse.body?.cancel(signal.reason);
|
207
|
+
}
|
208
|
+
});
|
209
|
+
return fetchResponse.body
|
210
|
+
.pipeTo(createWritableFromUWS(uwsResponse, fetchAPI), {
|
211
|
+
signal,
|
212
|
+
})
|
213
|
+
.catch(err => {
|
214
|
+
if (signal.aborted) {
|
215
|
+
return;
|
216
|
+
}
|
217
|
+
throw err;
|
218
|
+
});
|
219
|
+
}
|
220
|
+
export function fakePromise(value) {
|
221
|
+
if (isPromise(value)) {
|
222
|
+
return value;
|
114
223
|
}
|
115
|
-
|
224
|
+
// Write a fake promise to avoid the promise constructor
|
225
|
+
// being called with `new Promise` in the browser.
|
226
|
+
return {
|
227
|
+
then(resolve) {
|
228
|
+
if (resolve) {
|
229
|
+
const callbackResult = resolve(value);
|
230
|
+
if (isPromise(callbackResult)) {
|
231
|
+
return callbackResult;
|
232
|
+
}
|
233
|
+
return fakePromise(callbackResult);
|
234
|
+
}
|
235
|
+
return this;
|
236
|
+
},
|
237
|
+
catch() {
|
238
|
+
return this;
|
239
|
+
},
|
240
|
+
finally(cb) {
|
241
|
+
if (cb) {
|
242
|
+
const callbackResult = cb();
|
243
|
+
if (isPromise(callbackResult)) {
|
244
|
+
return callbackResult.then(() => value);
|
245
|
+
}
|
246
|
+
return fakePromise(value);
|
247
|
+
}
|
248
|
+
return this;
|
249
|
+
},
|
250
|
+
[Symbol.toStringTag]: 'Promise',
|
251
|
+
};
|
116
252
|
}
|
package/package.json
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@whatwg-node/server",
|
3
|
-
"version": "0.10.0-alpha-
|
3
|
+
"version": "0.10.0-alpha-20241123133536-975c9068dde45574fcfa26567e4bab96f45d1f85",
|
4
4
|
"description": "Fetch API compliant HTTP Server adapter",
|
5
5
|
"sideEffects": false,
|
6
6
|
"dependencies": {
|
7
|
-
"@whatwg-node/
|
7
|
+
"@whatwg-node/disposablestack": "^0.0.5",
|
8
|
+
"@whatwg-node/fetch": "^0.10.0",
|
8
9
|
"tslib": "^2.6.3"
|
9
10
|
},
|
10
11
|
"repository": {
|
@@ -15,7 +16,7 @@
|
|
15
16
|
"author": "Arda TANRIKULU <ardatanrikulu@gmail.com>",
|
16
17
|
"license": "MIT",
|
17
18
|
"engines": {
|
18
|
-
"node": ">=
|
19
|
+
"node": ">=18.0.0"
|
19
20
|
},
|
20
21
|
"main": "cjs/index.js",
|
21
22
|
"module": "esm/index.js",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ServerAdapterPlugin } from './types.cjs';
|
1
|
+
import type { ServerAdapterPlugin } from './types.cjs';
|
2
2
|
export declare function createDefaultErrorHandler<TServerContext = {}>(ResponseCtor?: typeof Response): ErrorHandler<TServerContext>;
|
3
3
|
export declare class HTTPError extends Error {
|
4
4
|
status: number;
|
@@ -6,7 +6,7 @@ export declare class HTTPError extends Error {
|
|
6
6
|
headers: HeadersInit;
|
7
7
|
details?: any;
|
8
8
|
name: string;
|
9
|
-
constructor(status: number, message: string, headers?: HeadersInit, details?: any);
|
9
|
+
constructor(status: number | undefined, message: string, headers?: HeadersInit, details?: any);
|
10
10
|
}
|
11
11
|
export type ErrorHandler<TServerContext> = (e: any, request: Request, ctx: TServerContext) => Response | Promise<Response>;
|
12
12
|
export declare function useErrorHandling<TServerContext>(onError?: ErrorHandler<TServerContext>): ServerAdapterPlugin<TServerContext>;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ServerAdapterPlugin } from './types.js';
|
1
|
+
import type { ServerAdapterPlugin } from './types.js';
|
2
2
|
export declare function createDefaultErrorHandler<TServerContext = {}>(ResponseCtor?: typeof Response): ErrorHandler<TServerContext>;
|
3
3
|
export declare class HTTPError extends Error {
|
4
4
|
status: number;
|
@@ -6,7 +6,7 @@ export declare class HTTPError extends Error {
|
|
6
6
|
headers: HeadersInit;
|
7
7
|
details?: any;
|
8
8
|
name: string;
|
9
|
-
constructor(status: number, message: string, headers?: HeadersInit, details?: any);
|
9
|
+
constructor(status: number | undefined, message: string, headers?: HeadersInit, details?: any);
|
10
10
|
}
|
11
11
|
export type ErrorHandler<TServerContext> = (e: any, request: Request, ctx: TServerContext) => Response | Promise<Response>;
|
12
12
|
export declare function useErrorHandling<TServerContext>(onError?: ErrorHandler<TServerContext>): ServerAdapterPlugin<TServerContext>;
|