@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/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
- constructor() {
53
- super(...arguments);
54
- this.aborted = false;
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,22 +78,36 @@ export class ServerAdapterRequestAbortSignal extends EventTarget {
81
78
  }
82
79
  }
83
80
  let bunNodeCompatModeWarned = false;
84
- export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
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;
95
- if (nodeResponse.once) {
93
+ const nodeResponse = nodeRequestResponseMap.get(nodeRequest);
94
+ nodeRequestResponseMap.delete(nodeRequest);
95
+ let normalizedHeaders = nodeRequest.headers;
96
+ if (nodeRequest.headers?.[':method']) {
97
+ normalizedHeaders = {};
98
+ for (const key in nodeRequest.headers) {
99
+ if (!key.startsWith(':')) {
100
+ normalizedHeaders[key] = nodeRequest.headers[key];
101
+ }
102
+ }
103
+ }
104
+ if (nodeResponse?.once) {
96
105
  let sendAbortSignal;
97
106
  // If ponyfilled
98
- if (RequestCtor !== globalThis.Request) {
99
- signal = new ServerAdapterRequestAbortSignal();
107
+ if (fetchAPI.Request !== globalThis.Request) {
108
+ const newSignal = new ServerAdapterRequestAbortSignal();
109
+ registerSignal?.(newSignal);
110
+ signal = newSignal;
100
111
  sendAbortSignal = () => signal.sendAbort();
101
112
  }
102
113
  else {
@@ -117,9 +128,9 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
117
128
  });
118
129
  }
119
130
  if (nodeRequest.method === 'GET' || nodeRequest.method === 'HEAD') {
120
- return new RequestCtor(fullUrl, {
131
+ return new fetchAPI.Request(fullUrl, {
121
132
  method: nodeRequest.method,
122
- headers: nodeRequest.headers,
133
+ headers: normalizedHeaders,
123
134
  signal,
124
135
  });
125
136
  }
@@ -132,16 +143,16 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
132
143
  const maybeParsedBody = nodeRequest.body;
133
144
  if (maybeParsedBody != null && Object.keys(maybeParsedBody).length > 0) {
134
145
  if (isRequestBody(maybeParsedBody)) {
135
- return new RequestCtor(fullUrl, {
146
+ return new fetchAPI.Request(fullUrl, {
136
147
  method: nodeRequest.method,
137
- headers: nodeRequest.headers,
148
+ headers: normalizedHeaders,
138
149
  body: maybeParsedBody,
139
150
  signal,
140
151
  });
141
152
  }
142
- const request = new RequestCtor(fullUrl, {
153
+ const request = new fetchAPI.Request(fullUrl, {
143
154
  method: nodeRequest.method,
144
- headers: nodeRequest.headers,
155
+ headers: normalizedHeaders,
145
156
  signal,
146
157
  });
147
158
  if (!request.headers.get('content-type')?.includes('json')) {
@@ -167,9 +178,9 @@ export function normalizeNodeRequest(nodeRequest, nodeResponse, RequestCtor) {
167
178
  console.warn(`You use Bun Node compatibility mode, which is not recommended!
168
179
  It will affect your performance. Please check our Bun integration recipe, and avoid using 'http' for your server implementation.`);
169
180
  }
170
- return new RequestCtor(fullUrl, {
181
+ return new fetchAPI.Request(fullUrl, {
171
182
  method: nodeRequest.method,
172
- headers: nodeRequest.headers,
183
+ headers: normalizedHeaders,
173
184
  duplex: 'half',
174
185
  body: new ReadableStream({
175
186
  start(controller) {
@@ -191,9 +202,9 @@ It will affect your performance. Please check our Bun integration recipe, and av
191
202
  });
192
203
  }
193
204
  // perf: instead of spreading the object, we can just pass it as is and it performs better
194
- return new RequestCtor(fullUrl, {
205
+ return new fetchAPI.Request(fullUrl, {
195
206
  method: nodeRequest.method,
196
- headers: nodeRequest.headers,
207
+ headers: normalizedHeaders,
197
208
  body: rawRequest,
198
209
  duplex: 'half',
199
210
  signal,
@@ -229,11 +240,26 @@ function endResponse(serverResponse) {
229
240
  serverResponse.end(null, null, null);
230
241
  }
231
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
+ });
232
252
  for await (const chunk of asyncIterable) {
253
+ if (closed) {
254
+ break;
255
+ }
233
256
  if (!serverResponse
234
257
  // @ts-expect-error http and http2 writes are actually compatible
235
258
  .write(chunk)) {
236
- break;
259
+ if (closed) {
260
+ break;
261
+ }
262
+ await new Promise(resolve => serverResponse.once('drain', resolve));
237
263
  }
238
264
  }
239
265
  endResponse(serverResponse);
@@ -244,7 +270,7 @@ export function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
244
270
  }
245
271
  if (!fetchResponse) {
246
272
  serverResponse.statusCode = 404;
247
- serverResponse.end();
273
+ endResponse(serverResponse);
248
274
  return;
249
275
  }
250
276
  serverResponse.statusCode = fetchResponse.status;
@@ -293,10 +319,31 @@ export function sendNodeResponse(fetchResponse, serverResponse, nodeRequest) {
293
319
  fetchBody.pipe(serverResponse);
294
320
  return;
295
321
  }
322
+ if (isReadableStream(fetchBody)) {
323
+ return sendReadableStream(serverResponse, fetchBody);
324
+ }
296
325
  if (isAsyncIterable(fetchBody)) {
297
326
  return sendAsyncIterable(serverResponse, fetchBody);
298
327
  }
299
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
+ }
300
347
  export function isRequestInit(val) {
301
348
  return (val != null &&
302
349
  typeof val === 'object' &&
@@ -321,13 +368,16 @@ export function completeAssign(...args) {
321
368
  // modified Object.keys to Object.getOwnPropertyNames
322
369
  // because Object.keys only returns enumerable properties
323
370
  const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
324
- descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
371
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
372
+ if (descriptor) {
373
+ descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
374
+ }
325
375
  return descriptors;
326
376
  }, {});
327
377
  // By default, Object.assign copies enumerable Symbols, too
328
378
  Object.getOwnPropertySymbols(source).forEach(sym => {
329
379
  const descriptor = Object.getOwnPropertyDescriptor(source, sym);
330
- if (descriptor.enumerable) {
380
+ if (descriptor?.enumerable) {
331
381
  descriptors[sym] = descriptor;
332
382
  }
333
383
  });
@@ -370,77 +420,18 @@ export function handleErrorFromRequestHandler(error, ResponseCtor) {
370
420
  status: error.status || 500,
371
421
  });
372
422
  }
373
- export function isolateObject(originalCtx, waitUntilPromises) {
423
+ export function isolateObject(originalCtx, waitUntilFn) {
374
424
  if (originalCtx == null) {
375
- return {};
376
- }
377
- const extraProps = {};
378
- const deletedProps = new Set();
379
- return new Proxy(originalCtx, {
380
- get(originalCtx, prop) {
381
- if (waitUntilPromises != null && prop === 'waitUntil') {
382
- return function waitUntil(promise) {
383
- waitUntilPromises.push(promise.catch(err => console.error(err)));
384
- };
385
- }
386
- const extraPropVal = extraProps[prop];
387
- if (extraPropVal != null) {
388
- if (typeof extraPropVal === 'function') {
389
- return extraPropVal.bind(extraProps);
390
- }
391
- return extraPropVal;
392
- }
393
- if (deletedProps.has(prop)) {
394
- return undefined;
395
- }
396
- return originalCtx[prop];
397
- },
398
- set(_originalCtx, prop, value) {
399
- extraProps[prop] = value;
400
- return true;
401
- },
402
- has(originalCtx, prop) {
403
- if (waitUntilPromises != null && prop === 'waitUntil') {
404
- return true;
405
- }
406
- if (deletedProps.has(prop)) {
407
- return false;
408
- }
409
- if (prop in extraProps) {
410
- return true;
411
- }
412
- return prop in originalCtx;
413
- },
414
- defineProperty(_originalCtx, prop, descriptor) {
415
- return Reflect.defineProperty(extraProps, prop, descriptor);
416
- },
417
- deleteProperty(_originalCtx, prop) {
418
- if (prop in extraProps) {
419
- return Reflect.deleteProperty(extraProps, prop);
420
- }
421
- deletedProps.add(prop);
422
- return true;
423
- },
424
- ownKeys(originalCtx) {
425
- const extraKeys = Reflect.ownKeys(extraProps);
426
- const originalKeys = Reflect.ownKeys(originalCtx);
427
- const deletedKeys = Array.from(deletedProps);
428
- const allKeys = new Set(extraKeys.concat(originalKeys.filter(keys => !deletedKeys.includes(keys))));
429
- if (waitUntilPromises != null) {
430
- allKeys.add('waitUntil');
431
- }
432
- return Array.from(allKeys);
433
- },
434
- getOwnPropertyDescriptor(originalCtx, prop) {
435
- if (prop in extraProps) {
436
- return Reflect.getOwnPropertyDescriptor(extraProps, prop);
437
- }
438
- if (deletedProps.has(prop)) {
439
- return undefined;
440
- }
441
- return Reflect.getOwnPropertyDescriptor(originalCtx, prop);
442
- },
443
- });
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);
444
435
  }
445
436
  export function createDeferredPromise() {
446
437
  let resolveFn;
@@ -476,3 +467,87 @@ export function handleAbortSignalAndPromiseResponse(response$, abortSignal) {
476
467
  }
477
468
  return response$;
478
469
  }
470
+ export const decompressedResponseMap = new WeakMap();
471
+ const supportedEncodingsByFetchAPI = new WeakMap();
472
+ export function getSupportedEncodings(fetchAPI) {
473
+ let supportedEncodings = supportedEncodingsByFetchAPI.get(fetchAPI);
474
+ if (!supportedEncodings) {
475
+ const possibleEncodings = ['deflate', 'gzip', 'deflate-raw', 'br'];
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
+ }
497
+ supportedEncodingsByFetchAPI.set(fetchAPI, supportedEncodings);
498
+ }
499
+ return supportedEncodings;
500
+ }
501
+ export function handleResponseDecompression(response, fetchAPI) {
502
+ const contentEncodingHeader = response?.headers.get('content-encoding');
503
+ if (!contentEncodingHeader || contentEncodingHeader === 'none') {
504
+ return response;
505
+ }
506
+ if (!response?.body) {
507
+ return response;
508
+ }
509
+ let decompressedResponse = decompressedResponseMap.get(response);
510
+ if (!decompressedResponse || decompressedResponse.bodyUsed) {
511
+ let decompressedBody = response.body;
512
+ const contentEncodings = contentEncodingHeader.split(',');
513
+ if (!contentEncodings.every(encoding => getSupportedEncodings(fetchAPI).includes(encoding))) {
514
+ return new fetchAPI.Response(`Unsupported 'Content-Encoding': ${contentEncodingHeader}`, {
515
+ status: 415,
516
+ statusText: 'Unsupported Media Type',
517
+ });
518
+ }
519
+ for (const contentEncoding of contentEncodings) {
520
+ decompressedBody = decompressedBody.pipeThrough(new fetchAPI.DecompressionStream(contentEncoding));
521
+ }
522
+ decompressedResponse = new fetchAPI.Response(decompressedBody, response);
523
+ decompressedResponseMap.set(response, decompressedResponse);
524
+ }
525
+ return decompressedResponse;
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
+ }
@@ -1,22 +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
+ 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);
17
+ }
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();
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;
7
37
  if (method !== 'get' && method !== 'head') {
8
- body = new fetchAPI.ReadableStream({});
9
- const readable = body.readable;
38
+ duplex = 'half';
10
39
  signal.addEventListener('abort', () => {
11
- readable.push(null);
40
+ stop();
12
41
  });
13
- res.onData(function (ab, isLast) {
14
- const chunk = Buffer.from(ab, 0, ab.byteLength);
15
- readable.push(Buffer.from(chunk));
16
- if (isLast) {
17
- readable.push(null);
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
+ });
18
64
  }
19
- });
65
+ return readableStream;
66
+ };
20
67
  }
21
68
  const headers = new fetchAPI.Headers();
22
69
  req.forEach((key, value) => {
@@ -27,27 +74,97 @@ export function getRequestFromUWSRequest({ req, res, fetchAPI, signal }) {
27
74
  if (query) {
28
75
  url += `?${query}`;
29
76
  }
30
- return new fetchAPI.Request(url, {
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, {
31
88
  method,
32
89
  headers,
33
- body: body,
90
+ get body() {
91
+ return getBody();
92
+ },
34
93
  signal,
94
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
95
+ // @ts-ignore - not in the TS types yet
96
+ duplex,
35
97
  });
36
- }
37
- async function forwardResponseBodyToUWSResponse(uwsResponse, fetchResponse, signal) {
38
- for await (const chunk of fetchResponse.body) {
39
- if (signal.aborted) {
40
- return;
98
+ function getBufferFromChunks() {
99
+ if (!buffer) {
100
+ buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
41
101
  }
42
- uwsResponse.cork(() => {
43
- uwsResponse.write(chunk);
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
+ }
44
117
  });
45
118
  }
46
- uwsResponse.cork(() => {
47
- uwsResponse.end();
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
+ },
150
+ });
151
+ return request;
152
+ }
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
+ },
48
165
  });
49
166
  }
50
- export function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
167
+ export function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal, fetchAPI) {
51
168
  if (!fetchResponse) {
52
169
  uwsResponse.writeStatus('404 Not Found');
53
170
  uwsResponse.end();
@@ -77,13 +194,59 @@ export function sendResponseToUwsOpts(uwsResponse, fetchResponse, signal) {
77
194
  if (bufferOfRes) {
78
195
  uwsResponse.end(bufferOfRes);
79
196
  }
197
+ else if (!fetchResponse.body) {
198
+ uwsResponse.end();
199
+ }
80
200
  });
81
- if (bufferOfRes) {
201
+ if (bufferOfRes || !fetchResponse.body) {
82
202
  return;
83
203
  }
84
- if (!fetchResponse.body) {
85
- uwsResponse.end();
86
- return;
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;
87
223
  }
88
- return forwardResponseBodyToUWSResponse(uwsResponse, fetchResponse, signal);
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
+ };
89
252
  }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@whatwg-node/server",
3
- "version": "0.10.0-alpha-20240717150008-1474b9d9b679a0e8f6225f44f11b95a6f4bf24ea",
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/fetch": "^0.9.17",
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": ">=16.0.0"
19
+ "node": ">=18.0.0"
19
20
  },
20
21
  "main": "cjs/index.js",
21
22
  "module": "esm/index.js",
@@ -4,5 +4,6 @@ export * from './utils.cjs';
4
4
  export * from './plugins/types.cjs';
5
5
  export * from './plugins/useCors.cjs';
6
6
  export * from './plugins/useErrorHandling.cjs';
7
+ export * from './plugins/useContentEncoding.cjs';
7
8
  export * from './uwebsockets.cjs';
8
9
  export { Response } from '@whatwg-node/fetch';
@@ -4,5 +4,6 @@ export * from './utils.js';
4
4
  export * from './plugins/types.js';
5
5
  export * from './plugins/useCors.js';
6
6
  export * from './plugins/useErrorHandling.js';
7
+ export * from './plugins/useContentEncoding.js';
7
8
  export * from './uwebsockets.js';
8
9
  export { Response } from '@whatwg-node/fetch';
@@ -6,6 +6,7 @@ export interface ServerAdapterPlugin<TServerContext = {}> {
6
6
  export type OnRequestHook<TServerContext> = (payload: OnRequestEventPayload<TServerContext>) => Promise<void> | void;
7
7
  export interface OnRequestEventPayload<TServerContext> {
8
8
  request: Request;
9
+ setRequest(newRequest: Request): void;
9
10
  serverContext: TServerContext;
10
11
  fetchAPI: FetchAPI;
11
12
  requestHandler: ServerAdapterRequestHandler<TServerContext>;
@@ -18,4 +19,6 @@ export interface OnResponseEventPayload<TServerContext> {
18
19
  request: Request;
19
20
  serverContext: TServerContext;
20
21
  response: Response;
22
+ setResponse(newResponse: Response): void;
23
+ fetchAPI: FetchAPI;
21
24
  }