@whatwg-node/server 0.10.0-alpha-20240717150008-1474b9d9b679a0e8f6225f44f11b95a6f4bf24ea → 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/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ServerAdapterRequestAbortSignal = void 0;
3
+ exports.decompressedResponseMap = exports.nodeRequestResponseMap = exports.ServerAdapterRequestAbortSignal = void 0;
4
4
  exports.isAsyncIterable = isAsyncIterable;
5
5
  exports.normalizeNodeRequest = normalizeNodeRequest;
6
6
  exports.isReadable = isReadable;
@@ -17,7 +17,9 @@ exports.handleErrorFromRequestHandler = handleErrorFromRequestHandler;
17
17
  exports.isolateObject = isolateObject;
18
18
  exports.createDeferredPromise = createDeferredPromise;
19
19
  exports.handleAbortSignalAndPromiseResponse = handleAbortSignalAndPromiseResponse;
20
- const fetch_1 = require("@whatwg-node/fetch");
20
+ exports.getSupportedEncodings = getSupportedEncodings;
21
+ exports.handleResponseDecompression = handleResponseDecompression;
22
+ exports.ensureDisposableStackRegisteredForTerminateEvents = ensureDisposableStackRegisteredForTerminateEvents;
21
23
  function isAsyncIterable(body) {
22
24
  return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
23
25
  }
@@ -68,11 +70,9 @@ function isRequestBody(body) {
68
70
  return false;
69
71
  }
70
72
  class ServerAdapterRequestAbortSignal extends EventTarget {
71
- constructor() {
72
- super(...arguments);
73
- this.aborted = false;
74
- this._onabort = null;
75
- }
73
+ aborted = false;
74
+ _onabort = null;
75
+ reason;
76
76
  throwIfAborted() {
77
77
  if (this.aborted) {
78
78
  throw this.reason;
@@ -101,22 +101,36 @@ class ServerAdapterRequestAbortSignal extends EventTarget {
101
101
  }
102
102
  exports.ServerAdapterRequestAbortSignal = ServerAdapterRequestAbortSignal;
103
103
  let bunNodeCompatModeWarned = false;
104
- function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
104
+ exports.nodeRequestResponseMap = new WeakMap();
105
+ function normalizeNodeRequest(nodeRequest, fetchAPI, registerSignal) {
105
106
  const rawRequest = nodeRequest.raw || nodeRequest.req || nodeRequest;
106
107
  let fullUrl = buildFullUrl(rawRequest);
107
108
  if (nodeRequest.query) {
108
- const url = new fetch_1.URL(fullUrl);
109
+ const url = new fetchAPI.URL(fullUrl);
109
110
  for (const key in nodeRequest.query) {
110
111
  url.searchParams.set(key, nodeRequest.query[key]);
111
112
  }
112
113
  fullUrl = url.toString();
113
114
  }
114
115
  let signal;
115
- if (nodeResponse.once) {
116
+ const nodeResponse = exports.nodeRequestResponseMap.get(nodeRequest);
117
+ exports.nodeRequestResponseMap.delete(nodeRequest);
118
+ let normalizedHeaders = nodeRequest.headers;
119
+ if (nodeRequest.headers?.[':method']) {
120
+ normalizedHeaders = {};
121
+ for (const key in nodeRequest.headers) {
122
+ if (!key.startsWith(':')) {
123
+ normalizedHeaders[key] = nodeRequest.headers[key];
124
+ }
125
+ }
126
+ }
127
+ if (nodeResponse?.once) {
116
128
  let sendAbortSignal;
117
129
  // If ponyfilled
118
- if (RequestCtor !== globalThis.Request) {
119
- signal = new ServerAdapterRequestAbortSignal();
130
+ if (fetchAPI.Request !== globalThis.Request) {
131
+ const newSignal = new ServerAdapterRequestAbortSignal();
132
+ registerSignal?.(newSignal);
133
+ signal = newSignal;
120
134
  sendAbortSignal = () => signal.sendAbort();
121
135
  }
122
136
  else {
@@ -137,9 +151,9 @@ function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
137
151
  });
138
152
  }
139
153
  if (nodeRequest.method === 'GET' || nodeRequest.method === 'HEAD') {
140
- return new RequestCtor(fullUrl, {
154
+ return new fetchAPI.Request(fullUrl, {
141
155
  method: nodeRequest.method,
142
- headers: nodeRequest.headers,
156
+ headers: normalizedHeaders,
143
157
  signal,
144
158
  });
145
159
  }
@@ -152,16 +166,16 @@ function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
152
166
  const maybeParsedBody = nodeRequest.body;
153
167
  if (maybeParsedBody != null && Object.keys(maybeParsedBody).length > 0) {
154
168
  if (isRequestBody(maybeParsedBody)) {
155
- return new RequestCtor(fullUrl, {
169
+ return new fetchAPI.Request(fullUrl, {
156
170
  method: nodeRequest.method,
157
- headers: nodeRequest.headers,
171
+ headers: normalizedHeaders,
158
172
  body: maybeParsedBody,
159
173
  signal,
160
174
  });
161
175
  }
162
- const request = new RequestCtor(fullUrl, {
176
+ const request = new fetchAPI.Request(fullUrl, {
163
177
  method: nodeRequest.method,
164
- headers: nodeRequest.headers,
178
+ headers: normalizedHeaders,
165
179
  signal,
166
180
  });
167
181
  if (!request.headers.get('content-type')?.includes('json')) {
@@ -187,9 +201,9 @@ function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
187
201
  console.warn(`You use Bun Node compatibility mode, which is not recommended!
188
202
  It will affect your performance. Please check our Bun integration recipe, and avoid using 'http' for your server implementation.`);
189
203
  }
190
- return new RequestCtor(fullUrl, {
204
+ return new fetchAPI.Request(fullUrl, {
191
205
  method: nodeRequest.method,
192
- headers: nodeRequest.headers,
206
+ headers: normalizedHeaders,
193
207
  duplex: 'half',
194
208
  body: new ReadableStream({
195
209
  start(controller) {
@@ -211,9 +225,9 @@ It will affect your performance. Please check our Bun integration recipe, and av
211
225
  });
212
226
  }
213
227
  // perf: instead of spreading the object, we can just pass it as is and it performs better
214
- return new RequestCtor(fullUrl, {
228
+ return new fetchAPI.Request(fullUrl, {
215
229
  method: nodeRequest.method,
216
- headers: nodeRequest.headers,
230
+ headers: normalizedHeaders,
217
231
  body: rawRequest,
218
232
  duplex: 'half',
219
233
  signal,
@@ -249,11 +263,26 @@ function endResponse(serverResponse) {
249
263
  serverResponse.end(null, null, null);
250
264
  }
251
265
  async function sendAsyncIterable(serverResponse, asyncIterable) {
266
+ let closed = false;
267
+ const closeEventListener = () => {
268
+ closed = true;
269
+ };
270
+ serverResponse.once('error', closeEventListener);
271
+ serverResponse.once('close', closeEventListener);
272
+ serverResponse.once('finish', () => {
273
+ serverResponse.removeListener('close', closeEventListener);
274
+ });
252
275
  for await (const chunk of asyncIterable) {
276
+ if (closed) {
277
+ break;
278
+ }
253
279
  if (!serverResponse
254
280
  // @ts-expect-error http and http2 writes are actually compatible
255
281
  .write(chunk)) {
256
- break;
282
+ if (closed) {
283
+ break;
284
+ }
285
+ await new Promise(resolve => serverResponse.once('drain', resolve));
257
286
  }
258
287
  }
259
288
  endResponse(serverResponse);
@@ -264,7 +293,7 @@ function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
264
293
  }
265
294
  if (!fetchResponse) {
266
295
  serverResponse.statusCode = 404;
267
- serverResponse.end();
296
+ endResponse(serverResponse);
268
297
  return;
269
298
  }
270
299
  serverResponse.statusCode = fetchResponse.status;
@@ -313,10 +342,31 @@ function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
313
342
  fetchBody.pipe(serverResponse);
314
343
  return;
315
344
  }
345
+ if (isReadableStream(fetchBody)) {
346
+ return sendReadableStream(serverResponse, fetchBody);
347
+ }
316
348
  if (isAsyncIterable(fetchBody)) {
317
349
  return sendAsyncIterable(serverResponse, fetchBody);
318
350
  }
319
351
  }
352
+ async function sendReadableStream(serverResponse, readableStream) {
353
+ const reader = readableStream.getReader();
354
+ serverResponse.req.once('error', err => {
355
+ reader.cancel(err);
356
+ });
357
+ while (true) {
358
+ const { done, value } = await reader.read();
359
+ if (done) {
360
+ break;
361
+ }
362
+ if (!serverResponse
363
+ // @ts-expect-error http and http2 writes are actually compatible
364
+ .write(value)) {
365
+ await new Promise(resolve => serverResponse.once('drain', resolve));
366
+ }
367
+ }
368
+ endResponse(serverResponse);
369
+ }
320
370
  function isRequestInit(val) {
321
371
  return (val != null &&
322
372
  typeof val === 'object' &&
@@ -341,13 +391,16 @@ function completeAssign(...args) {
341
391
  // modified Object.keys to Object.getOwnPropertyNames
342
392
  // because Object.keys only returns enumerable properties
343
393
  const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
344
- descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
394
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
395
+ if (descriptor) {
396
+ descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
397
+ }
345
398
  return descriptors;
346
399
  }, {});
347
400
  // By default, Object.assign copies enumerable Symbols, too
348
401
  Object.getOwnPropertySymbols(source).forEach(sym => {
349
402
  const descriptor = Object.getOwnPropertyDescriptor(source, sym);
350
- if (descriptor.enumerable) {
403
+ if (descriptor?.enumerable) {
351
404
  descriptors[sym] = descriptor;
352
405
  }
353
406
  });
@@ -390,77 +443,18 @@ function handleErrorFromRequestHandler(error, ResponseCtor) {
390
443
  status: error.status || 500,
391
444
  });
392
445
  }
393
- function isolateObject(originalCtx, waitUntilPromises) {
446
+ function isolateObject(originalCtx, waitUntilFn) {
394
447
  if (originalCtx == null) {
395
- return {};
396
- }
397
- const extraProps = {};
398
- const deletedProps = new Set();
399
- return new Proxy(originalCtx, {
400
- get(originalCtx, prop) {
401
- if (waitUntilPromises != null && prop === 'waitUntil') {
402
- return function waitUntil(promise) {
403
- waitUntilPromises.push(promise.catch(err => console.error(err)));
404
- };
405
- }
406
- const extraPropVal = extraProps[prop];
407
- if (extraPropVal != null) {
408
- if (typeof extraPropVal === 'function') {
409
- return extraPropVal.bind(extraProps);
410
- }
411
- return extraPropVal;
412
- }
413
- if (deletedProps.has(prop)) {
414
- return undefined;
415
- }
416
- return originalCtx[prop];
417
- },
418
- set(_originalCtx, prop, value) {
419
- extraProps[prop] = value;
420
- return true;
421
- },
422
- has(originalCtx, prop) {
423
- if (waitUntilPromises != null && prop === 'waitUntil') {
424
- return true;
425
- }
426
- if (deletedProps.has(prop)) {
427
- return false;
428
- }
429
- if (prop in extraProps) {
430
- return true;
431
- }
432
- return prop in originalCtx;
433
- },
434
- defineProperty(_originalCtx, prop, descriptor) {
435
- return Reflect.defineProperty(extraProps, prop, descriptor);
436
- },
437
- deleteProperty(_originalCtx, prop) {
438
- if (prop in extraProps) {
439
- return Reflect.deleteProperty(extraProps, prop);
440
- }
441
- deletedProps.add(prop);
442
- return true;
443
- },
444
- ownKeys(originalCtx) {
445
- const extraKeys = Reflect.ownKeys(extraProps);
446
- const originalKeys = Reflect.ownKeys(originalCtx);
447
- const deletedKeys = Array.from(deletedProps);
448
- const allKeys = new Set(extraKeys.concat(originalKeys.filter(keys => !deletedKeys.includes(keys))));
449
- if (waitUntilPromises != null) {
450
- allKeys.add('waitUntil');
451
- }
452
- return Array.from(allKeys);
453
- },
454
- getOwnPropertyDescriptor(originalCtx, prop) {
455
- if (prop in extraProps) {
456
- return Reflect.getOwnPropertyDescriptor(extraProps, prop);
457
- }
458
- if (deletedProps.has(prop)) {
459
- return undefined;
460
- }
461
- return Reflect.getOwnPropertyDescriptor(originalCtx, prop);
462
- },
463
- });
448
+ if (waitUntilFn == null) {
449
+ return {};
450
+ }
451
+ return {
452
+ waitUntil: waitUntilFn,
453
+ };
454
+ }
455
+ return completeAssign(Object.create(originalCtx), {
456
+ waitUntil: waitUntilFn,
457
+ }, originalCtx);
464
458
  }
465
459
  function createDeferredPromise() {
466
460
  let resolveFn;
@@ -496,3 +490,87 @@ function handleAbortSignalAndPromiseResponse(response$, abortSignal) {
496
490
  }
497
491
  return response$;
498
492
  }
493
+ exports.decompressedResponseMap = new WeakMap();
494
+ const supportedEncodingsByFetchAPI = new WeakMap();
495
+ function getSupportedEncodings(fetchAPI) {
496
+ let supportedEncodings = supportedEncodingsByFetchAPI.get(fetchAPI);
497
+ if (!supportedEncodings) {
498
+ const possibleEncodings = ['deflate', 'gzip', 'deflate-raw', 'br'];
499
+ if (fetchAPI.DecompressionStream?.['supportedFormats']) {
500
+ supportedEncodings = fetchAPI.DecompressionStream['supportedFormats'];
501
+ }
502
+ else {
503
+ supportedEncodings = possibleEncodings.filter(encoding => {
504
+ // deflate-raw is not supported in Node.js >v20
505
+ if (globalThis.process?.version?.startsWith('v2') &&
506
+ fetchAPI.DecompressionStream === globalThis.DecompressionStream &&
507
+ encoding === 'deflate-raw') {
508
+ return false;
509
+ }
510
+ try {
511
+ // eslint-disable-next-line no-new
512
+ new fetchAPI.DecompressionStream(encoding);
513
+ return true;
514
+ }
515
+ catch {
516
+ return false;
517
+ }
518
+ });
519
+ }
520
+ supportedEncodingsByFetchAPI.set(fetchAPI, supportedEncodings);
521
+ }
522
+ return supportedEncodings;
523
+ }
524
+ function handleResponseDecompression(response, fetchAPI) {
525
+ const contentEncodingHeader = response?.headers.get('content-encoding');
526
+ if (!contentEncodingHeader || contentEncodingHeader === 'none') {
527
+ return response;
528
+ }
529
+ if (!response?.body) {
530
+ return response;
531
+ }
532
+ let decompressedResponse = exports.decompressedResponseMap.get(response);
533
+ if (!decompressedResponse || decompressedResponse.bodyUsed) {
534
+ let decompressedBody = response.body;
535
+ const contentEncodings = contentEncodingHeader.split(',');
536
+ if (!contentEncodings.every(encoding => getSupportedEncodings(fetchAPI).includes(encoding))) {
537
+ return new fetchAPI.Response(`Unsupported 'Content-Encoding': ${contentEncodingHeader}`, {
538
+ status: 415,
539
+ statusText: 'Unsupported Media Type',
540
+ });
541
+ }
542
+ for (const contentEncoding of contentEncodings) {
543
+ decompressedBody = decompressedBody.pipeThrough(new fetchAPI.DecompressionStream(contentEncoding));
544
+ }
545
+ decompressedResponse = new fetchAPI.Response(decompressedBody, response);
546
+ exports.decompressedResponseMap.set(response, decompressedResponse);
547
+ }
548
+ return decompressedResponse;
549
+ }
550
+ const terminateEvents = ['SIGINT', 'SIGTERM', 'exit'];
551
+ const disposableStacks = new Set();
552
+ let eventListenerRegistered = false;
553
+ function ensureEventListenerForDisposableStacks() {
554
+ if (eventListenerRegistered) {
555
+ return;
556
+ }
557
+ eventListenerRegistered = true;
558
+ for (const event of terminateEvents) {
559
+ globalThis.process.once(event, function terminateHandler() {
560
+ return Promise.allSettled([...disposableStacks].map(stack => stack.disposeAsync().catch(e => {
561
+ console.error('Error while disposing:', e);
562
+ })));
563
+ });
564
+ }
565
+ }
566
+ function ensureDisposableStackRegisteredForTerminateEvents(disposableStack) {
567
+ if (globalThis.process) {
568
+ ensureEventListenerForDisposableStacks();
569
+ if (!disposableStacks.has(disposableStack)) {
570
+ disposableStacks.add(disposableStack);
571
+ disposableStack.defer(() => {
572
+ disposableStacks.delete(disposableStack);
573
+ });
574
+ }
575
+ }
576
+ }
@@ -2,26 +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();
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);
24
+ }
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();
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;
12
44
  if (method !== 'get' && method !== 'head') {
13
- body = new fetchAPI.ReadableStream({});
14
- const readable = body.readable;
45
+ duplex = 'half';
15
46
  signal.addEventListener('abort', () => {
16
- readable.push(null);
47
+ stop();
17
48
  });
18
- res.onData(function (ab, isLast) {
19
- const chunk = Buffer.from(ab, 0, ab.byteLength);
20
- readable.push(Buffer.from(chunk));
21
- if (isLast) {
22
- readable.push(null);
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
+ });
23
71
  }
24
- });
72
+ return readableStream;
73
+ };
25
74
  }
26
75
  const headers = new fetchAPI.Headers();
27
76
  req.forEach((key, value) => {
@@ -32,27 +81,97 @@ function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
32
81
  if (query) {
33
82
  url += `?${query}`;
34
83
  }
35
- return new fetchAPI.Request(url, {
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, {
36
95
  method,
37
96
  headers,
38
- body: body,
97
+ get body() {
98
+ return getBody();
99
+ },
39
100
  signal,
101
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
102
+ // @ts-ignore - not in the TS types yet
103
+ duplex,
40
104
  });
41
- }
42
- async function forwardResponseBodyToUWSResponse(uwsResponse, fetchResponse, signal) {
43
- for await (const chunk of fetchResponse.body) {
44
- if (signal.aborted) {
45
- return;
105
+ function getBufferFromChunks() {
106
+ if (!buffer) {
107
+ buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
46
108
  }
47
- uwsResponse.cork(() => {
48
- uwsResponse.write(chunk);
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
+ }
49
124
  });
50
125
  }
51
- uwsResponse.cork(() => {
52
- uwsResponse.end();
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
+ },
157
+ });
158
+ return request;
159
+ }
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
+ },
53
172
  });
54
173
  }
55
- function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
174
+ function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal, fetchAPI) {
56
175
  if (!fetchResponse) {
57
176
  uwsResponse.writeStatus('404 Not Found');
58
177
  uwsResponse.end();
@@ -82,13 +201,59 @@ function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
82
201
  if (bufferOfRes) {
83
202
  uwsResponse.end(bufferOfRes);
84
203
  }
204
+ else if (!fetchResponse.body) {
205
+ uwsResponse.end();
206
+ }
85
207
  });
86
- if (bufferOfRes) {
208
+ if (bufferOfRes || !fetchResponse.body) {
87
209
  return;
88
210
  }
89
- if (!fetchResponse.body) {
90
- uwsResponse.end();
91
- return;
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;
92
230
  }
93
- return forwardResponseBodyToUWSResponse(uwsResponse, fetchResponse, signal);
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
+ };
94
259
  }