@whatwg-node/server 0.6.0-alpha-20230120130300-bc9d705 → 0.6.0

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 CHANGED
@@ -1,8 +1,10 @@
1
1
  # WHATWG Node Generic Server Adapter
2
2
 
3
- `@whatwg-node/server` helps you to create a generic server implementation by using WHATWG Fetch API for Node.js, AWS Lambda, Cloudflare Workers, Deno, Express, Fastify, Koa, Next.js and Sveltekit.
3
+ `@whatwg-node/server` helps you to create a generic server implementation by using WHATWG Fetch API
4
+ for Node.js, AWS Lambda, Cloudflare Workers, Deno, Express, Fastify, Koa, Next.js and Sveltekit.
4
5
 
5
- Once you create an adapter with `createServerAdapter`, you don't need to install any other platform specific package since the generic adapter will handle it automatically.
6
+ Once you create an adapter with `createServerAdapter`, you don't need to install any other platform
7
+ specific package since the generic adapter will handle it automatically.
6
8
 
7
9
  ## How to start
8
10
 
@@ -26,8 +28,8 @@ You can use your server adapter with the following integrations:
26
28
  [Node.js](https://nodejs.org/api/http.html) is the most popular server side JavaScript runtime.
27
29
 
28
30
  ```ts
29
- import myServerAdapter from './myServerAdapter'
30
31
  import { createServer } from 'http'
32
+ import myServerAdapter from './myServerAdapter'
31
33
 
32
34
  // You can create your Node server instance by using our adapter
33
35
  const nodeServer = createServer(myServerAdapter)
@@ -37,21 +39,71 @@ nodeServer.listen(4000)
37
39
 
38
40
  ### AWS Lambda
39
41
 
40
- AWS Lambda is a serverless computing platform that makes it easy to build applications that run on the AWS cloud. Our adaoter is platform agnostic so they can fit together easily. In order to reduce the boilerplate we prefer to use [Serverless Express from Vendia](https://github.com/vendia/serverless-express).
42
+ AWS Lambda is a serverless computing platform that makes it easy to build applications that run on
43
+ the AWS cloud. Our adaoter is platform agnostic so they can fit together easily. In order to reduce
44
+ the boilerplate we prefer to use
45
+ [Serverless Express from Vendia](https://github.com/vendia/serverless-express).
41
46
 
42
47
  ```ts
43
- import myServerAdapter from './myServerAdapter'
48
+ import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda'
44
49
  import type { Handler } from '@aws-cdk/aws-lambda'
45
- import { configure } from '@vendia/serverless-express'
50
+ import myServerAdapter from './myServerAdapter'
46
51
 
47
- export const handler: Handler = configure({
48
- app: myServerAdapter
49
- })
52
+ interface ServerContext {
53
+ event: APIGatewayEvent
54
+ lambdaContext: Context
55
+ }
56
+
57
+ export async function handler(
58
+ event: APIGatewayEvent,
59
+ lambdaContext: Context
60
+ ): Promise<APIGatewayProxyResult> {
61
+ const url = new URL(event.path, 'http://localhost')
62
+ if (event.queryStringParameters != null) {
63
+ for (const name in event.queryStringParameters) {
64
+ const value = event.queryStringParameters[name]
65
+ if (value != null) {
66
+ url.searchParams.set(name, value)
67
+ }
68
+ }
69
+ }
70
+
71
+ const response = await myServerAdapter.fetch(
72
+ url,
73
+ {
74
+ // For v1.0 you should use event.httpMethod
75
+ method: event.requestContext.http.method,
76
+ headers: event.headers as HeadersInit,
77
+ body: event.body
78
+ ? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
79
+ : undefined
80
+ },
81
+ {
82
+ event,
83
+ lambdaContext
84
+ }
85
+ )
86
+
87
+ const responseHeaders: Record<string, string> = {}
88
+
89
+ response.headers.forEach((value, name) => {
90
+ responseHeaders[name] = value
91
+ })
92
+
93
+ return {
94
+ statusCode: response.status,
95
+ headers: responseHeaders,
96
+ body: await response.text(),
97
+ isBase64Encoded: false
98
+ }
99
+ }
50
100
  ```
51
101
 
52
102
  ### Cloudflare Workers
53
103
 
54
- Cloudflare Workers provides a serverless execution environment that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure. It uses Fetch API already so we can use our adapter as an event listener like below;
104
+ Cloudflare Workers provides a serverless execution environment that allows you to create entirely
105
+ new applications or augment existing ones without configuring or maintaining infrastructure. It uses
106
+ Fetch API already so we can use our adapter as an event listener like below;
55
107
 
56
108
  ```ts
57
109
  import myServerAdapter from './myServerAdapter'
@@ -65,18 +117,20 @@ self.addEventListener('fetch', myServerAdapter)
65
117
  You can use our adapter as a Deno request handler like below;
66
118
 
67
119
  ```ts
68
- import { serve } from 'https://deno.land/std@0.117.0/http/server.ts'
120
+ import { serve } from 'https://deno.land/std@0.157.0/http/server.ts'
69
121
  import myServerAdapter from './myServerAdapter'
70
122
 
71
123
  serve(myServerAdapter, {
72
- // Listen any port you want
73
- addr: ':4000'
124
+ onListen({ hostname, port }) {
125
+ console.log(`Listening on http://${hostname}:${port}/graphql`)
126
+ }
74
127
  })
75
128
  ```
76
129
 
77
130
  ### Express
78
131
 
79
- [Express is the most popular web framework for Node.js.](https://expressjs.com/) It is a minimalist framework that provides a robust set of features to handle HTTP on Node.js applications.
132
+ [Express is the most popular web framework for Node.js.](https://expressjs.com/) It is a minimalist
133
+ framework that provides a robust set of features to handle HTTP on Node.js applications.
80
134
 
81
135
  You can easily integrate your adapter into your Express application with a few lines of code.
82
136
 
@@ -96,14 +150,15 @@ app.listen(4000, () => {
96
150
 
97
151
  ### Fastify
98
152
 
99
- [Fastify is one of the popular HTTP server frameworks for Node.js.](https://www.fastify.io/). You can use your adapter easily with Fastify.
153
+ [Fastify is one of the popular HTTP server frameworks for Node.js.](https://www.fastify.io/). You
154
+ can use your adapter easily with Fastify.
100
155
 
101
156
  So you can benefit from the powerful plugins of Fastify ecosystem.
102
157
  [See the ecosystem](https://www.fastify.io/docs/latest/Guides/Ecosystem/)
103
158
 
104
159
  ```ts
160
+ import fastify, { FastifyReply, FastifyRequest } from 'fastify'
105
161
  import myServerAdapter from './myServerAdapter'
106
- import fastify, { FastifyRequest, FastifyReply } from 'fastify'
107
162
 
108
163
  // This is the fastify instance you have created
109
164
  const app = fastify({ logger: true })
@@ -127,7 +182,8 @@ app.route({
127
182
 
128
183
  reply.status(response.status)
129
184
 
130
- reply.send(Readable.from(response.body))
185
+ // Fastify doesn't accept `null` as a response body
186
+ reply.send(response.body || undefined)
131
187
 
132
188
  return reply
133
189
  }
@@ -138,7 +194,9 @@ app.listen(4000)
138
194
 
139
195
  ### Koa
140
196
 
141
- [Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive.](https://koajs.com/) You can add your adapter to your Koa application with a few lines of code then [benefit middlewares written for Koa.](https://github.com/koajs/koa/wiki)
197
+ [Koa is another Node.js server framework designed by the team behind Express, which aims to be a smaller, more expressive.](https://koajs.com/)
198
+ You can add your adapter to your Koa application with a few lines of code then
199
+ [benefit middlewares written for Koa.](https://github.com/koajs/koa/wiki)
142
200
 
143
201
  ```ts
144
202
  import Koa from 'koa'
@@ -157,7 +215,7 @@ app.use(async ctx => {
157
215
  ctx.append(key, value)
158
216
  })
159
217
 
160
- ctx.body = Readable.from(response.body)
218
+ ctx.body = response.body
161
219
  })
162
220
 
163
221
  app.listen(4000, () => {
@@ -167,13 +225,14 @@ app.listen(4000, () => {
167
225
 
168
226
  ### Next.js
169
227
 
170
- [Next.js](https://nextjs.org/) is a web framework that allows you to build websites very quickly and our new server adapter can be integrated with Next.js easily as an API Route.
228
+ [Next.js](https://nextjs.org/) is a web framework that allows you to build websites very quickly and
229
+ our new server adapter can be integrated with Next.js easily as an API Route.
171
230
 
172
231
  ```ts
173
232
  // pages/api/myEndpoint.ts
174
233
  // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
175
- import myServerAdapter from './myServerAdapter'
176
234
  import type { NextApiRequest, NextApiResponse } from 'next'
235
+ import myServerAdapter from './myServerAdapter'
177
236
 
178
237
  export const config = {
179
238
  api: {
@@ -187,7 +246,8 @@ export default myServerAdapter
187
246
 
188
247
  ### SvelteKit
189
248
 
190
- [SvelteKit](https://kit.svelte.dev/) is the fastest way to build svelte apps. It is very simple, and let you build frontend & backend in a single place
249
+ [SvelteKit](https://kit.svelte.dev/) is the fastest way to build svelte apps. It is very simple, and
250
+ let you build frontend & backend in a single place
191
251
 
192
252
  ```ts
193
253
  import myServerAdapter from './myServerAdapter'
@@ -197,8 +257,8 @@ export { myServerAdapter as get, myServerAdapter as post }
197
257
 
198
258
  ### Bun
199
259
 
200
- [Bun](https://bun.sh/) is a modern JavaScript runtime like Node or Deno, and it supports Fetch API as a first class citizen.
201
- So the configuration is really simple like any other JS runtime;
260
+ [Bun](https://bun.sh/) is a modern JavaScript runtime like Node or Deno, and it supports Fetch API
261
+ as a first class citizen. So the configuration is really simple like any other JS runtime;
202
262
 
203
263
  ```ts
204
264
  import myServerAdapter from './myServerAdapter'
@@ -212,9 +272,11 @@ console.info(`Server is running on ${server.hostname}`)
212
272
 
213
273
  ## File Uploads / Multipart Requests
214
274
 
215
- Multipart requests are a type of HTTP request that allows you to send blobs together with regular text data which has a mime-type `multipart/form-data`.
275
+ Multipart requests are a type of HTTP request that allows you to send blobs together with regular
276
+ text data which has a mime-type `multipart/form-data`.
216
277
 
217
- For example, if you send a multipart request from a browser with `FormData`, you can get the same `FormData` object in your request handler.
278
+ For example, if you send a multipart request from a browser with `FormData`, you can get the same
279
+ `FormData` object in your request handler.
218
280
 
219
281
  ```ts
220
282
  import { createServerAdapter } from '@whatwg-node/server'
@@ -238,15 +300,21 @@ const myServerAdapter = createServerAdapter(async request => {
238
300
  })
239
301
  ```
240
302
 
241
- You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN documentation.
303
+ You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN
304
+ documentation.
242
305
 
243
306
  ## Routing and Middlewares
244
307
 
245
- We'd recommend to use `@whatwg-node/router` to handle routing and middleware approach. It uses `@whatwg-node/server` under the hood.
308
+ We'd recommend to use `@whatwg-node/router` to handle routing and middleware approach. It uses
309
+ `@whatwg-node/server` under the hood.
310
+
311
+ > Learn more about `@whatwg-node/router` [here](../router)
246
312
 
247
313
  ### Basic Routing
248
314
 
249
315
  ```ts
316
+ // Then use it in any environment
317
+ import { createServer } from 'http'
250
318
  import { createRouter, Router } from '@whatwg-node/router'
251
319
 
252
320
  const router = createRouter()
@@ -266,15 +334,14 @@ router.get('/google', () => Response.redirect('http://www.google.com'))
266
334
  // 404 for everything else
267
335
  router.all('*', () => new Response('Not Found.', { status: 404 }))
268
336
 
269
- // Then use it in any environment
270
- import { createServer } from 'http'
271
337
  const httpServer = createServer(router)
272
338
  httpServer.listen(4000)
273
339
  ```
274
340
 
275
341
  ### Middlewares to handle CORS, cookies and more
276
342
 
277
- This package also provides some utilities for your platform agnostic server implementation. The following example shows how to get the cookies as an object from the request.
343
+ This package also provides some utilities for your platform agnostic server implementation. The
344
+ following example shows how to get the cookies as an object from the request.
278
345
 
279
346
  ```ts
280
347
  import { withCookies } from '@whatwg-node/server'
@@ -1,9 +1,4 @@
1
- import { DefaultServerAdapterContext, FetchAPI, ServerAdapter, ServerAdapterBaseObject, ServerAdapterRequestHandler } from './types';
2
- import { ServerAdapterPlugin } from './plugins/types';
3
- export interface ServerAdapterOptions<TServerContext> {
4
- plugins?: ServerAdapterPlugin<TServerContext>[];
5
- fetchAPI?: Partial<FetchAPI>;
6
- }
7
- declare function createServerAdapter<TServerContext = DefaultServerAdapterContext, THandleRequest extends ServerAdapterRequestHandler<TServerContext> = ServerAdapterRequestHandler<TServerContext>>(serverAdapterRequestHandler: THandleRequest, options?: ServerAdapterOptions<TServerContext>): ServerAdapter<TServerContext, ServerAdapterBaseObject<TServerContext, THandleRequest>>;
8
- declare function createServerAdapter<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext>>(serverAdapterBaseObject: TBaseObject, options?: ServerAdapterOptions<TServerContext>): ServerAdapter<TServerContext, TBaseObject>;
1
+ import { ServerAdapter, ServerAdapterBaseObject, ServerAdapterRequestHandler } from './types';
2
+ declare function createServerAdapter<TServerContext = {}, THandleRequest extends ServerAdapterRequestHandler<TServerContext> = ServerAdapterRequestHandler<TServerContext>>(serverAdapterRequestHandler: THandleRequest, RequestCtor?: typeof Request): ServerAdapter<TServerContext, ServerAdapterBaseObject<TServerContext, THandleRequest>>;
3
+ declare function createServerAdapter<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext>>(serverAdapterBaseObject: TBaseObject, RequestCtor?: typeof Request): ServerAdapter<TServerContext, TBaseObject>;
9
4
  export { createServerAdapter };
package/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export * from './createServerAdapter';
2
2
  export * from './types';
3
3
  export * from './utils';
4
- export * from './plugins/types';
5
- export * from './plugins/useCors';
6
- export * from './plugins/useErrorHandling';
4
+ export * from './middlewares/withCors';
5
+ export * from './middlewares/withErrorHandling';
7
6
  export { Response } from '@whatwg-node/fetch';
package/index.js CHANGED
@@ -2,32 +2,36 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const DefaultFetchAPI = require('@whatwg-node/fetch');
5
+ const fetch = require('@whatwg-node/fetch');
6
6
 
7
7
  function isAsyncIterable(body) {
8
- return body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function';
8
+ return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
9
9
  }
10
10
  function getPort(nodeRequest) {
11
11
  var _a, _b, _c, _d, _e;
12
12
  if ((_a = nodeRequest.socket) === null || _a === void 0 ? void 0 : _a.localPort) {
13
13
  return (_b = nodeRequest.socket) === null || _b === void 0 ? void 0 : _b.localPort;
14
14
  }
15
- const portInHeader = (_e = (_d = (_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c.host) === null || _d === void 0 ? void 0 : _d.split(':')) === null || _e === void 0 ? void 0 : _e[1];
15
+ const hostInHeader = ((_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c[':authority']) || ((_d = nodeRequest.headers) === null || _d === void 0 ? void 0 : _d.host);
16
+ const portInHeader = (_e = hostInHeader === null || hostInHeader === void 0 ? void 0 : hostInHeader.split(':')) === null || _e === void 0 ? void 0 : _e[1];
16
17
  if (portInHeader) {
17
18
  return portInHeader;
18
19
  }
19
20
  return 80;
20
21
  }
21
22
  function getHostnameWithPort(nodeRequest) {
22
- var _a, _b, _c;
23
- if ((_a = nodeRequest.headers) === null || _a === void 0 ? void 0 : _a.host) {
24
- return (_b = nodeRequest.headers) === null || _b === void 0 ? void 0 : _b.host;
23
+ var _a, _b, _c, _d, _e;
24
+ if ((_a = nodeRequest.headers) === null || _a === void 0 ? void 0 : _a[':authority']) {
25
+ return (_b = nodeRequest.headers) === null || _b === void 0 ? void 0 : _b[':authority'];
26
+ }
27
+ if ((_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c.host) {
28
+ return (_d = nodeRequest.headers) === null || _d === void 0 ? void 0 : _d.host;
25
29
  }
26
30
  const port = getPort(nodeRequest);
27
31
  if (nodeRequest.hostname) {
28
32
  return nodeRequest.hostname + ':' + port;
29
33
  }
30
- const localIp = (_c = nodeRequest.socket) === null || _c === void 0 ? void 0 : _c.localAddress;
34
+ const localIp = (_e = nodeRequest.socket) === null || _e === void 0 ? void 0 : _e.localAddress;
31
35
  if (localIp && !(localIp === null || localIp === void 0 ? void 0 : localIp.includes('::')) && !(localIp === null || localIp === void 0 ? void 0 : localIp.includes('ffff'))) {
32
36
  return `${localIp}:${port}`;
33
37
  }
@@ -111,8 +115,7 @@ function normalizeNodeRequest(nodeRequest, RequestCtor) {
111
115
  });
112
116
  }
113
117
  return new RequestCtor(fullUrl, {
114
- headers: nodeRequest.headers,
115
- method: nodeRequest.method,
118
+ ...baseRequestInit,
116
119
  body: rawRequest,
117
120
  });
118
121
  }
@@ -124,7 +127,11 @@ function isNodeRequest(request) {
124
127
  }
125
128
  function isServerResponse(stream) {
126
129
  // Check all used functions are defined
127
- return (stream != null && stream.setHeader != null && stream.end != null && stream.once != null && stream.write != null);
130
+ return (stream != null &&
131
+ stream.setHeader != null &&
132
+ stream.end != null &&
133
+ stream.once != null &&
134
+ stream.write != null);
128
135
  }
129
136
  function isReadableStream(stream) {
130
137
  return stream != null && stream.getReader != null;
@@ -186,6 +193,7 @@ function isRequestInit(val) {
186
193
  'window' in val));
187
194
  }
188
195
 
196
+ /* eslint-disable @typescript-eslint/ban-types */
189
197
  async function handleWaitUntils(waitUntilPromises) {
190
198
  const waitUntils = await Promise.allSettled(waitUntilPromises);
191
199
  waitUntils.forEach(waitUntil => {
@@ -194,60 +202,17 @@ async function handleWaitUntils(waitUntilPromises) {
194
202
  }
195
203
  });
196
204
  }
197
- function createServerAdapter(serverAdapterBaseObject, options) {
198
- var _a;
199
- const fetchAPI = {
200
- ...DefaultFetchAPI,
201
- ...options === null || options === void 0 ? void 0 : options.fetchAPI,
202
- };
203
- const givenHandleRequest = typeof serverAdapterBaseObject === 'function' ? serverAdapterBaseObject : serverAdapterBaseObject.handle;
204
- const onRequestHooks = [];
205
- const onResponseHooks = [];
206
- for (const plugin of (_a = options === null || options === void 0 ? void 0 : options.plugins) !== null && _a !== void 0 ? _a : []) {
207
- if (plugin.onRequest) {
208
- onRequestHooks.push(plugin.onRequest);
209
- }
210
- if (plugin.onResponse) {
211
- onResponseHooks.push(plugin.onResponse);
212
- }
213
- }
214
- async function handleRequest(request, serverContext) {
215
- const url = new URL(request.url, 'http://localhost');
216
- let requestHandler = givenHandleRequest;
217
- let response;
218
- for (const onRequestHook of onRequestHooks) {
219
- await onRequestHook({
220
- request,
221
- serverContext,
222
- fetchAPI,
223
- url,
224
- requestHandler,
225
- setRequestHandler(newRequestHandler) {
226
- requestHandler = newRequestHandler;
227
- },
228
- endResponse(newResponse) {
229
- response = newResponse;
230
- },
231
- });
232
- if (response) {
233
- break;
234
- }
235
- }
236
- if (!response) {
237
- response = await requestHandler(request, serverContext);
238
- }
239
- for (const onResponseHook of onResponseHooks) {
240
- await onResponseHook({
241
- request,
242
- response,
243
- serverContext,
244
- });
245
- }
246
- return response;
247
- }
205
+ function createServerAdapter(serverAdapterBaseObject,
206
+ /**
207
+ * WHATWG Fetch spec compliant `Request` constructor.
208
+ */
209
+ RequestCtor = fetch.Request) {
210
+ const handleRequest = typeof serverAdapterBaseObject === 'function'
211
+ ? serverAdapterBaseObject
212
+ : serverAdapterBaseObject.handle;
248
213
  function handleNodeRequest(nodeRequest, ...ctx) {
249
214
  const serverContext = ctx.length > 1 ? completeAssign({}, ...ctx) : ctx[0];
250
- const request = normalizeNodeRequest(nodeRequest, fetchAPI.Request);
215
+ const request = normalizeNodeRequest(nodeRequest, RequestCtor);
251
216
  return handleRequest(request, serverContext);
252
217
  }
253
218
  async function requestListener(nodeRequest, serverResponse, ...ctx) {
@@ -307,9 +272,9 @@ function createServerAdapter(serverAdapterBaseObject, options) {
307
272
  if (typeof input === 'string' || input instanceof URL) {
308
273
  const [initOrCtx, ...restOfCtx] = maybeCtx;
309
274
  if (isRequestInit(initOrCtx)) {
310
- return handleRequestWithWaitUntil(new fetchAPI.Request(input, initOrCtx), ...restOfCtx);
275
+ return handleRequestWithWaitUntil(new RequestCtor(input, initOrCtx), ...restOfCtx);
311
276
  }
312
- return handleRequestWithWaitUntil(new fetchAPI.Request(input), ...maybeCtx);
277
+ return handleRequestWithWaitUntil(new RequestCtor(input), ...maybeCtx);
313
278
  }
314
279
  return handleRequestWithWaitUntil(input, ...maybeCtx);
315
280
  };
@@ -386,24 +351,27 @@ function createServerAdapter(serverAdapterBaseObject, options) {
386
351
  // from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copying_accessors
387
352
  function completeAssign(target, ...sources) {
388
353
  sources.forEach(source => {
389
- // modified Object.keys to Object.getOwnPropertyNames
390
- // because Object.keys only returns enumerable properties
391
- const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
392
- descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
393
- return descriptors;
394
- }, {});
395
- // By default, Object.assign copies enumerable Symbols, too
396
- Object.getOwnPropertySymbols(source).forEach(sym => {
397
- const descriptor = Object.getOwnPropertyDescriptor(source, sym);
398
- if (descriptor.enumerable) {
399
- descriptors[sym] = descriptor;
400
- }
401
- });
402
- Object.defineProperties(target, descriptors);
354
+ if (source != null && typeof source === 'object') {
355
+ // modified Object.keys to Object.getOwnPropertyNames
356
+ // because Object.keys only returns enumerable properties
357
+ const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
358
+ descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
359
+ return descriptors;
360
+ }, {});
361
+ // By default, Object.assign copies enumerable Symbols, too
362
+ Object.getOwnPropertySymbols(source).forEach(sym => {
363
+ const descriptor = Object.getOwnPropertyDescriptor(source, sym);
364
+ if (descriptor.enumerable) {
365
+ descriptors[sym] = descriptor;
366
+ }
367
+ });
368
+ Object.defineProperties(target, descriptors);
369
+ }
403
370
  });
404
371
  return target;
405
372
  }
406
373
 
374
+ /* eslint-disable @typescript-eslint/ban-types */
407
375
  function getCORSHeadersByRequestAndOptions(request, corsOptions) {
408
376
  var _a, _b;
409
377
  const headers = {};
@@ -458,8 +426,7 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
458
426
  }
459
427
  }
460
428
  if ((_b = corsOptions.allowedHeaders) === null || _b === void 0 ? void 0 : _b.length) {
461
- headers['Access-Control-Allow-Headers'] =
462
- corsOptions.allowedHeaders.join(', ');
429
+ headers['Access-Control-Allow-Headers'] = corsOptions.allowedHeaders.join(', ');
463
430
  }
464
431
  else {
465
432
  const requestHeaders = request.headers.get('access-control-request-headers');
@@ -480,8 +447,7 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
480
447
  headers['Access-Control-Allow-Credentials'] = 'true';
481
448
  }
482
449
  if (corsOptions.exposedHeaders) {
483
- headers['Access-Control-Expose-Headers'] =
484
- corsOptions.exposedHeaders.join(', ');
450
+ headers['Access-Control-Expose-Headers'] = corsOptions.exposedHeaders.join(', ');
485
451
  }
486
452
  if (corsOptions.maxAge) {
487
453
  headers['Access-Control-Max-Age'] = corsOptions.maxAge.toString();
@@ -492,8 +458,7 @@ async function getCORSResponseHeaders(request, corsOptionsFactory, serverContext
492
458
  const corsOptions = await corsOptionsFactory(request, serverContext);
493
459
  return getCORSHeadersByRequestAndOptions(request, corsOptions);
494
460
  }
495
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
496
- function useCORS(options) {
461
+ function withCORS(obj, options, ResponseCtor = fetch.Response) {
497
462
  let corsOptionsFactory = () => ({});
498
463
  if (options != null) {
499
464
  if (typeof options === 'function') {
@@ -509,32 +474,36 @@ function useCORS(options) {
509
474
  corsOptionsFactory = () => false;
510
475
  }
511
476
  }
512
- return {
513
- onRequest({ request, fetchAPI, endResponse }) {
514
- if (request.method.toUpperCase() === 'OPTIONS') {
515
- const response = new fetchAPI.Response(null, {
516
- status: 204,
517
- // Safari (and potentially other browsers) need content-length 0,
518
- // for 204 or they just hang waiting for a body
519
- // see: https://github.com/expressjs/cors/blob/master/lib/index.js#L176
520
- headers: {
521
- 'Content-Length': '0',
522
- },
523
- });
524
- endResponse(response);
525
- }
526
- },
527
- async onResponse({ request, serverContext, response }) {
528
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
477
+ async function handleWithCORS(request, serverContext) {
478
+ let response;
479
+ if (request.method.toUpperCase() === 'OPTIONS') {
480
+ response = new ResponseCtor(null, {
481
+ status: 204,
482
+ });
483
+ }
484
+ else {
485
+ response = await obj.handle(request, serverContext);
486
+ }
487
+ if (response != null) {
529
488
  const headers = await getCORSResponseHeaders(request, corsOptionsFactory, serverContext);
530
489
  for (const headerName in headers) {
531
490
  response.headers.set(headerName, headers[headerName]);
532
491
  }
492
+ return response;
493
+ }
494
+ }
495
+ return new Proxy(obj, {
496
+ get(_, prop, receiver) {
497
+ if (prop === 'handle') {
498
+ return handleWithCORS;
499
+ }
500
+ return Reflect.get(obj, prop, receiver);
533
501
  },
534
- };
502
+ });
535
503
  }
536
504
 
537
- function createDefaultErrorHandler(ResponseCtor = DefaultFetchAPI.Response) {
505
+ /* eslint-disable @typescript-eslint/ban-types */
506
+ function createDefaultErrorHandler(ResponseCtor = fetch.Response) {
538
507
  return function defaultErrorHandler(e) {
539
508
  return new ResponseCtor(e.stack || e.message || e.toString(), {
540
509
  status: e.statusCode || e.status || 500,
@@ -542,23 +511,6 @@ function createDefaultErrorHandler(ResponseCtor = DefaultFetchAPI.Response) {
542
511
  });
543
512
  };
544
513
  }
545
- function useErrorHandling(onError) {
546
- return {
547
- onRequest({ requestHandler, setRequestHandler, fetchAPI }) {
548
- const errorHandler = onError || createDefaultErrorHandler(fetchAPI.Response);
549
- setRequestHandler(async function handlerWithErrorHandling(request, serverContext) {
550
- try {
551
- const response = await requestHandler(request, serverContext);
552
- return response;
553
- }
554
- catch (e) {
555
- const response = await errorHandler(e, request, serverContext);
556
- return response;
557
- }
558
- });
559
- }
560
- };
561
- }
562
514
  function withErrorHandling(obj, onError = createDefaultErrorHandler()) {
563
515
  async function handleWithErrorHandling(request, ctx) {
564
516
  try {
@@ -582,7 +534,7 @@ function withErrorHandling(obj, onError = createDefaultErrorHandler()) {
582
534
  Object.defineProperty(exports, 'Response', {
583
535
  enumerable: true,
584
536
  get: function () {
585
- return DefaultFetchAPI.Response;
537
+ return fetch.Response;
586
538
  }
587
539
  });
588
540
  exports.createDefaultErrorHandler = createDefaultErrorHandler;
@@ -597,6 +549,5 @@ exports.isRequestInit = isRequestInit;
597
549
  exports.isServerResponse = isServerResponse;
598
550
  exports.normalizeNodeRequest = normalizeNodeRequest;
599
551
  exports.sendNodeResponse = sendNodeResponse;
600
- exports.useCORS = useCORS;
601
- exports.useErrorHandling = useErrorHandling;
552
+ exports.withCORS = withCORS;
602
553
  exports.withErrorHandling = withErrorHandling;
package/index.mjs CHANGED
@@ -1,31 +1,34 @@
1
- import * as DefaultFetchAPI from '@whatwg-node/fetch';
2
- import { Response } from '@whatwg-node/fetch';
1
+ import { Request, Response } from '@whatwg-node/fetch';
3
2
  export { Response } from '@whatwg-node/fetch';
4
3
 
5
4
  function isAsyncIterable(body) {
6
- return body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function';
5
+ return (body != null && typeof body === 'object' && typeof body[Symbol.asyncIterator] === 'function');
7
6
  }
8
7
  function getPort(nodeRequest) {
9
8
  var _a, _b, _c, _d, _e;
10
9
  if ((_a = nodeRequest.socket) === null || _a === void 0 ? void 0 : _a.localPort) {
11
10
  return (_b = nodeRequest.socket) === null || _b === void 0 ? void 0 : _b.localPort;
12
11
  }
13
- const portInHeader = (_e = (_d = (_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c.host) === null || _d === void 0 ? void 0 : _d.split(':')) === null || _e === void 0 ? void 0 : _e[1];
12
+ const hostInHeader = ((_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c[':authority']) || ((_d = nodeRequest.headers) === null || _d === void 0 ? void 0 : _d.host);
13
+ const portInHeader = (_e = hostInHeader === null || hostInHeader === void 0 ? void 0 : hostInHeader.split(':')) === null || _e === void 0 ? void 0 : _e[1];
14
14
  if (portInHeader) {
15
15
  return portInHeader;
16
16
  }
17
17
  return 80;
18
18
  }
19
19
  function getHostnameWithPort(nodeRequest) {
20
- var _a, _b, _c;
21
- if ((_a = nodeRequest.headers) === null || _a === void 0 ? void 0 : _a.host) {
22
- return (_b = nodeRequest.headers) === null || _b === void 0 ? void 0 : _b.host;
20
+ var _a, _b, _c, _d, _e;
21
+ if ((_a = nodeRequest.headers) === null || _a === void 0 ? void 0 : _a[':authority']) {
22
+ return (_b = nodeRequest.headers) === null || _b === void 0 ? void 0 : _b[':authority'];
23
+ }
24
+ if ((_c = nodeRequest.headers) === null || _c === void 0 ? void 0 : _c.host) {
25
+ return (_d = nodeRequest.headers) === null || _d === void 0 ? void 0 : _d.host;
23
26
  }
24
27
  const port = getPort(nodeRequest);
25
28
  if (nodeRequest.hostname) {
26
29
  return nodeRequest.hostname + ':' + port;
27
30
  }
28
- const localIp = (_c = nodeRequest.socket) === null || _c === void 0 ? void 0 : _c.localAddress;
31
+ const localIp = (_e = nodeRequest.socket) === null || _e === void 0 ? void 0 : _e.localAddress;
29
32
  if (localIp && !(localIp === null || localIp === void 0 ? void 0 : localIp.includes('::')) && !(localIp === null || localIp === void 0 ? void 0 : localIp.includes('ffff'))) {
30
33
  return `${localIp}:${port}`;
31
34
  }
@@ -109,8 +112,7 @@ function normalizeNodeRequest(nodeRequest, RequestCtor) {
109
112
  });
110
113
  }
111
114
  return new RequestCtor(fullUrl, {
112
- headers: nodeRequest.headers,
113
- method: nodeRequest.method,
115
+ ...baseRequestInit,
114
116
  body: rawRequest,
115
117
  });
116
118
  }
@@ -122,7 +124,11 @@ function isNodeRequest(request) {
122
124
  }
123
125
  function isServerResponse(stream) {
124
126
  // Check all used functions are defined
125
- return (stream != null && stream.setHeader != null && stream.end != null && stream.once != null && stream.write != null);
127
+ return (stream != null &&
128
+ stream.setHeader != null &&
129
+ stream.end != null &&
130
+ stream.once != null &&
131
+ stream.write != null);
126
132
  }
127
133
  function isReadableStream(stream) {
128
134
  return stream != null && stream.getReader != null;
@@ -184,6 +190,7 @@ function isRequestInit(val) {
184
190
  'window' in val));
185
191
  }
186
192
 
193
+ /* eslint-disable @typescript-eslint/ban-types */
187
194
  async function handleWaitUntils(waitUntilPromises) {
188
195
  const waitUntils = await Promise.allSettled(waitUntilPromises);
189
196
  waitUntils.forEach(waitUntil => {
@@ -192,60 +199,17 @@ async function handleWaitUntils(waitUntilPromises) {
192
199
  }
193
200
  });
194
201
  }
195
- function createServerAdapter(serverAdapterBaseObject, options) {
196
- var _a;
197
- const fetchAPI = {
198
- ...DefaultFetchAPI,
199
- ...options === null || options === void 0 ? void 0 : options.fetchAPI,
200
- };
201
- const givenHandleRequest = typeof serverAdapterBaseObject === 'function' ? serverAdapterBaseObject : serverAdapterBaseObject.handle;
202
- const onRequestHooks = [];
203
- const onResponseHooks = [];
204
- for (const plugin of (_a = options === null || options === void 0 ? void 0 : options.plugins) !== null && _a !== void 0 ? _a : []) {
205
- if (plugin.onRequest) {
206
- onRequestHooks.push(plugin.onRequest);
207
- }
208
- if (plugin.onResponse) {
209
- onResponseHooks.push(plugin.onResponse);
210
- }
211
- }
212
- async function handleRequest(request, serverContext) {
213
- const url = new URL(request.url, 'http://localhost');
214
- let requestHandler = givenHandleRequest;
215
- let response;
216
- for (const onRequestHook of onRequestHooks) {
217
- await onRequestHook({
218
- request,
219
- serverContext,
220
- fetchAPI,
221
- url,
222
- requestHandler,
223
- setRequestHandler(newRequestHandler) {
224
- requestHandler = newRequestHandler;
225
- },
226
- endResponse(newResponse) {
227
- response = newResponse;
228
- },
229
- });
230
- if (response) {
231
- break;
232
- }
233
- }
234
- if (!response) {
235
- response = await requestHandler(request, serverContext);
236
- }
237
- for (const onResponseHook of onResponseHooks) {
238
- await onResponseHook({
239
- request,
240
- response,
241
- serverContext,
242
- });
243
- }
244
- return response;
245
- }
202
+ function createServerAdapter(serverAdapterBaseObject,
203
+ /**
204
+ * WHATWG Fetch spec compliant `Request` constructor.
205
+ */
206
+ RequestCtor = Request) {
207
+ const handleRequest = typeof serverAdapterBaseObject === 'function'
208
+ ? serverAdapterBaseObject
209
+ : serverAdapterBaseObject.handle;
246
210
  function handleNodeRequest(nodeRequest, ...ctx) {
247
211
  const serverContext = ctx.length > 1 ? completeAssign({}, ...ctx) : ctx[0];
248
- const request = normalizeNodeRequest(nodeRequest, fetchAPI.Request);
212
+ const request = normalizeNodeRequest(nodeRequest, RequestCtor);
249
213
  return handleRequest(request, serverContext);
250
214
  }
251
215
  async function requestListener(nodeRequest, serverResponse, ...ctx) {
@@ -305,9 +269,9 @@ function createServerAdapter(serverAdapterBaseObject, options) {
305
269
  if (typeof input === 'string' || input instanceof URL) {
306
270
  const [initOrCtx, ...restOfCtx] = maybeCtx;
307
271
  if (isRequestInit(initOrCtx)) {
308
- return handleRequestWithWaitUntil(new fetchAPI.Request(input, initOrCtx), ...restOfCtx);
272
+ return handleRequestWithWaitUntil(new RequestCtor(input, initOrCtx), ...restOfCtx);
309
273
  }
310
- return handleRequestWithWaitUntil(new fetchAPI.Request(input), ...maybeCtx);
274
+ return handleRequestWithWaitUntil(new RequestCtor(input), ...maybeCtx);
311
275
  }
312
276
  return handleRequestWithWaitUntil(input, ...maybeCtx);
313
277
  };
@@ -384,24 +348,27 @@ function createServerAdapter(serverAdapterBaseObject, options) {
384
348
  // from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copying_accessors
385
349
  function completeAssign(target, ...sources) {
386
350
  sources.forEach(source => {
387
- // modified Object.keys to Object.getOwnPropertyNames
388
- // because Object.keys only returns enumerable properties
389
- const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
390
- descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
391
- return descriptors;
392
- }, {});
393
- // By default, Object.assign copies enumerable Symbols, too
394
- Object.getOwnPropertySymbols(source).forEach(sym => {
395
- const descriptor = Object.getOwnPropertyDescriptor(source, sym);
396
- if (descriptor.enumerable) {
397
- descriptors[sym] = descriptor;
398
- }
399
- });
400
- Object.defineProperties(target, descriptors);
351
+ if (source != null && typeof source === 'object') {
352
+ // modified Object.keys to Object.getOwnPropertyNames
353
+ // because Object.keys only returns enumerable properties
354
+ const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
355
+ descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
356
+ return descriptors;
357
+ }, {});
358
+ // By default, Object.assign copies enumerable Symbols, too
359
+ Object.getOwnPropertySymbols(source).forEach(sym => {
360
+ const descriptor = Object.getOwnPropertyDescriptor(source, sym);
361
+ if (descriptor.enumerable) {
362
+ descriptors[sym] = descriptor;
363
+ }
364
+ });
365
+ Object.defineProperties(target, descriptors);
366
+ }
401
367
  });
402
368
  return target;
403
369
  }
404
370
 
371
+ /* eslint-disable @typescript-eslint/ban-types */
405
372
  function getCORSHeadersByRequestAndOptions(request, corsOptions) {
406
373
  var _a, _b;
407
374
  const headers = {};
@@ -456,8 +423,7 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
456
423
  }
457
424
  }
458
425
  if ((_b = corsOptions.allowedHeaders) === null || _b === void 0 ? void 0 : _b.length) {
459
- headers['Access-Control-Allow-Headers'] =
460
- corsOptions.allowedHeaders.join(', ');
426
+ headers['Access-Control-Allow-Headers'] = corsOptions.allowedHeaders.join(', ');
461
427
  }
462
428
  else {
463
429
  const requestHeaders = request.headers.get('access-control-request-headers');
@@ -478,8 +444,7 @@ function getCORSHeadersByRequestAndOptions(request, corsOptions) {
478
444
  headers['Access-Control-Allow-Credentials'] = 'true';
479
445
  }
480
446
  if (corsOptions.exposedHeaders) {
481
- headers['Access-Control-Expose-Headers'] =
482
- corsOptions.exposedHeaders.join(', ');
447
+ headers['Access-Control-Expose-Headers'] = corsOptions.exposedHeaders.join(', ');
483
448
  }
484
449
  if (corsOptions.maxAge) {
485
450
  headers['Access-Control-Max-Age'] = corsOptions.maxAge.toString();
@@ -490,8 +455,7 @@ async function getCORSResponseHeaders(request, corsOptionsFactory, serverContext
490
455
  const corsOptions = await corsOptionsFactory(request, serverContext);
491
456
  return getCORSHeadersByRequestAndOptions(request, corsOptions);
492
457
  }
493
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
494
- function useCORS(options) {
458
+ function withCORS(obj, options, ResponseCtor = Response) {
495
459
  let corsOptionsFactory = () => ({});
496
460
  if (options != null) {
497
461
  if (typeof options === 'function') {
@@ -507,31 +471,35 @@ function useCORS(options) {
507
471
  corsOptionsFactory = () => false;
508
472
  }
509
473
  }
510
- return {
511
- onRequest({ request, fetchAPI, endResponse }) {
512
- if (request.method.toUpperCase() === 'OPTIONS') {
513
- const response = new fetchAPI.Response(null, {
514
- status: 204,
515
- // Safari (and potentially other browsers) need content-length 0,
516
- // for 204 or they just hang waiting for a body
517
- // see: https://github.com/expressjs/cors/blob/master/lib/index.js#L176
518
- headers: {
519
- 'Content-Length': '0',
520
- },
521
- });
522
- endResponse(response);
523
- }
524
- },
525
- async onResponse({ request, serverContext, response }) {
526
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
474
+ async function handleWithCORS(request, serverContext) {
475
+ let response;
476
+ if (request.method.toUpperCase() === 'OPTIONS') {
477
+ response = new ResponseCtor(null, {
478
+ status: 204,
479
+ });
480
+ }
481
+ else {
482
+ response = await obj.handle(request, serverContext);
483
+ }
484
+ if (response != null) {
527
485
  const headers = await getCORSResponseHeaders(request, corsOptionsFactory, serverContext);
528
486
  for (const headerName in headers) {
529
487
  response.headers.set(headerName, headers[headerName]);
530
488
  }
489
+ return response;
490
+ }
491
+ }
492
+ return new Proxy(obj, {
493
+ get(_, prop, receiver) {
494
+ if (prop === 'handle') {
495
+ return handleWithCORS;
496
+ }
497
+ return Reflect.get(obj, prop, receiver);
531
498
  },
532
- };
499
+ });
533
500
  }
534
501
 
502
+ /* eslint-disable @typescript-eslint/ban-types */
535
503
  function createDefaultErrorHandler(ResponseCtor = Response) {
536
504
  return function defaultErrorHandler(e) {
537
505
  return new ResponseCtor(e.stack || e.message || e.toString(), {
@@ -540,23 +508,6 @@ function createDefaultErrorHandler(ResponseCtor = Response) {
540
508
  });
541
509
  };
542
510
  }
543
- function useErrorHandling(onError) {
544
- return {
545
- onRequest({ requestHandler, setRequestHandler, fetchAPI }) {
546
- const errorHandler = onError || createDefaultErrorHandler(fetchAPI.Response);
547
- setRequestHandler(async function handlerWithErrorHandling(request, serverContext) {
548
- try {
549
- const response = await requestHandler(request, serverContext);
550
- return response;
551
- }
552
- catch (e) {
553
- const response = await errorHandler(e, request, serverContext);
554
- return response;
555
- }
556
- });
557
- }
558
- };
559
- }
560
511
  function withErrorHandling(obj, onError = createDefaultErrorHandler()) {
561
512
  async function handleWithErrorHandling(request, ctx) {
562
513
  try {
@@ -577,4 +528,4 @@ function withErrorHandling(obj, onError = createDefaultErrorHandler()) {
577
528
  });
578
529
  }
579
530
 
580
- export { createDefaultErrorHandler, createServerAdapter, getCORSHeadersByRequestAndOptions, isAsyncIterable, isFetchEvent, isNodeRequest, isReadable, isReadableStream, isRequestInit, isServerResponse, normalizeNodeRequest, sendNodeResponse, useCORS, useErrorHandling, withErrorHandling };
531
+ export { createDefaultErrorHandler, createServerAdapter, getCORSHeadersByRequestAndOptions, isAsyncIterable, isFetchEvent, isNodeRequest, isReadable, isReadableStream, isRequestInit, isServerResponse, normalizeNodeRequest, sendNodeResponse, withCORS, withErrorHandling };
@@ -1,4 +1,4 @@
1
- import { ServerAdapterPlugin } from './types';
1
+ import { ServerAdapterBaseObject } from '../types';
2
2
  export type CORSOptions = {
3
3
  origin?: string[] | string;
4
4
  methods?: string[];
@@ -7,7 +7,7 @@ export type CORSOptions = {
7
7
  credentials?: boolean;
8
8
  maxAge?: number;
9
9
  } | false;
10
- export type CORSPluginOptions<TServerContext> = CORSOptionsFactory<TServerContext> | CORSOptions | boolean;
10
+ export type WithCORSOptions<TServerContext> = CORSOptionsFactory<TServerContext> | CORSOptions | boolean;
11
11
  export type CORSOptionsFactory<TServerContext> = (request: Request, ...args: {} extends TServerContext ? [serverContext?: TServerContext | undefined] : [serverContext: TServerContext]) => Promise<CORSOptions> | CORSOptions;
12
12
  export declare function getCORSHeadersByRequestAndOptions(request: Request, corsOptions: CORSOptions): Record<string, string>;
13
- export declare function useCORS<TServerContext extends Record<string, any>>(options?: CORSPluginOptions<TServerContext>): ServerAdapterPlugin<TServerContext>;
13
+ export declare function withCORS<TServerContext = {}, TBaseObject extends ServerAdapterBaseObject<TServerContext> = ServerAdapterBaseObject<TServerContext>>(obj: TBaseObject, options: WithCORSOptions<TServerContext>, ResponseCtor?: typeof Response): TBaseObject;
@@ -0,0 +1,4 @@
1
+ import { ServerAdapterBaseObject } from '../types';
2
+ export declare function createDefaultErrorHandler<TServerContext = {}>(ResponseCtor?: typeof Response): ErrorHandler<TServerContext>;
3
+ export type ErrorHandler<TServerContext> = (e: any, request: Request, ctx: TServerContext) => Response | Promise<Response>;
4
+ export declare function withErrorHandling<TServerContext = {}, TBaseObject extends ServerAdapterBaseObject<TServerContext> = ServerAdapterBaseObject<TServerContext>>(obj: TBaseObject, onError?: ErrorHandler<TServerContext>): TBaseObject;
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@whatwg-node/server",
3
- "version": "0.6.0-alpha-20230120130300-bc9d705",
3
+ "version": "0.6.0",
4
4
  "description": "Fetch API compliant HTTP Server adapter",
5
5
  "sideEffects": false,
6
- "peerDependencies": {
7
- "@types/node": "^18.0.6"
8
- },
9
6
  "dependencies": {
10
- "@whatwg-node/fetch": "0.6.2",
7
+ "@whatwg-node/fetch": "0.6.6",
11
8
  "tslib": "^2.3.1"
12
9
  },
13
10
  "repository": {
package/types.d.ts CHANGED
@@ -13,11 +13,11 @@ export interface ServerAdapterBaseObject<TServerContext, THandleRequest extends
13
13
  */
14
14
  handle: THandleRequest;
15
15
  }
16
- export interface ServerAdapterObject<TServerContext> extends EventListenerObject {
16
+ export interface ServerAdapterObject<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext, ServerAdapterRequestHandler<TServerContext>>> extends EventListenerObject {
17
17
  /**
18
18
  * A basic request listener that takes a `Request` with the server context and returns a `Response`.
19
19
  */
20
- handleRequest: (request: Request, ctx: TServerContext) => Promise<Response>;
20
+ handleRequest: TBaseObject['handle'];
21
21
  /**
22
22
  * WHATWG Fetch spec compliant `fetch` function that can be used for testing purposes.
23
23
  */
@@ -46,11 +46,13 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
46
46
  request: Request;
47
47
  } & Partial<TServerContext>, ...ctx: Partial<TServerContext>[]): Promise<Response> | Response;
48
48
  }
49
- export type ServerAdapter<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext>> = TBaseObject & ServerAdapterObject<TServerContext>['handle'] & ServerAdapterObject<TServerContext>;
50
- export type ServerAdapterRequestHandler<TServerContext> = (request: Request, ctx: TServerContext) => Promise<Response> | Response;
51
- export type DefaultServerAdapterContext = {
49
+ export type ServerAdapter<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext>> = TBaseObject & ServerAdapterObject<TServerContext, TBaseObject>['handle'] & ServerAdapterObject<TServerContext, TBaseObject>;
50
+ export type ServerAdapterRequestHandler<TServerContext> = (request: Request, ctx: ServerAdapterContext<TServerContext>) => Promise<Response> | Response;
51
+ export type ServerAdapterNodeContext = {
52
52
  req: NodeRequest;
53
53
  res: NodeResponse;
54
- waitUntil(promise: Promise<void> | void): void;
55
54
  };
56
- export type FetchAPI = ReturnType<typeof import('@whatwg-node/fetch').createFetch>;
55
+ export type WaitUntilFn = (promise: Promise<void> | void) => void;
56
+ export type ServerAdapterContext<TServerContext> = TServerContext & {
57
+ waitUntil: WaitUntilFn;
58
+ };
package/utils.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
4
  /// <reference types="node" />
5
- import { IncomingMessage, ServerResponse } from 'node:http';
5
+ import type { IncomingMessage, ServerResponse } from 'node:http';
6
6
  import type { Http2ServerRequest, Http2ServerResponse } from 'node:http2';
7
7
  import type { Socket } from 'node:net';
8
8
  import type { Readable } from 'node:stream';
@@ -1,6 +0,0 @@
1
- import { CookieList, CookieChangeEventInit } from "./types";
2
- export declare class CookieChangeEvent extends Event {
3
- changed: CookieList;
4
- deleted: CookieList;
5
- constructor(type: string, eventInitDict?: CookieChangeEventInit);
6
- }
@@ -1,12 +0,0 @@
1
- import { CookieChangeEvent } from "./CookieChangeEvent";
2
- import { CookieStoreGetOptions, Cookie, CookieListItem, CookieStoreDeleteOptions } from "./types";
3
- export declare class CookieStore extends EventTarget {
4
- cookieString: string;
5
- onchange?: (event: CookieChangeEvent) => void;
6
- get [Symbol.toStringTag](): 'CookieStore';
7
- constructor(cookieString: string);
8
- get(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie | undefined>;
9
- set(init: CookieListItem | string, possibleValue?: string): Promise<void>;
10
- getAll(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie[]>;
11
- delete(init: CookieStoreDeleteOptions['name'] | CookieStoreDeleteOptions): Promise<void>;
12
- }
@@ -1,11 +0,0 @@
1
- import { Cookie } from './types';
2
- export interface ParseOptions {
3
- decode?: boolean;
4
- }
5
- /**
6
- * Parse a cookie header.
7
- *
8
- * Parse the given cookie header string into an object
9
- * The object has the various cookies as keys(names) => values
10
- */
11
- export declare function parse(str: string, options?: ParseOptions): Cookie[];
@@ -1,37 +0,0 @@
1
- export interface Cookie {
2
- domain?: string;
3
- expires?: number;
4
- name: string;
5
- path?: string;
6
- secure?: boolean;
7
- sameSite?: CookieSameSite;
8
- value: string;
9
- }
10
- export interface CookieStoreDeleteOptions {
11
- name: string;
12
- domain?: string;
13
- path?: string;
14
- }
15
- export interface CookieStoreGetOptions {
16
- name?: string;
17
- url?: string;
18
- }
19
- export declare enum CookieSameSite {
20
- strict = "strict",
21
- lax = "lax",
22
- none = "none"
23
- }
24
- export interface CookieListItem {
25
- name?: string;
26
- value?: string;
27
- domain: string | null;
28
- path?: string;
29
- expires: Date | number | null;
30
- secure?: boolean;
31
- sameSite?: CookieSameSite;
32
- }
33
- export type CookieList = CookieListItem[];
34
- export interface CookieChangeEventInit extends EventInit {
35
- changed: CookieList;
36
- deleted: CookieList;
37
- }
@@ -1,8 +0,0 @@
1
- import { ServerAdapterPlugin } from "../types";
2
- import { CookieStore } from "./CookieStore";
3
- declare global {
4
- interface Request {
5
- cookieStore?: CookieStore;
6
- }
7
- }
8
- export declare function useCookies<TServerContext>(): ServerAdapterPlugin<TServerContext>;
@@ -1,21 +0,0 @@
1
- import { DefaultServerAdapterContext, FetchAPI, ServerAdapterRequestHandler } from "../types";
2
- export interface ServerAdapterPlugin<TServerContext = DefaultServerAdapterContext> {
3
- onRequest?: OnRequestHook<TServerContext>;
4
- onResponse?: OnResponseHook<TServerContext>;
5
- }
6
- export type OnRequestHook<TServerContext> = (payload: OnRequestEventPayload<TServerContext>) => Promise<void> | void;
7
- export interface OnRequestEventPayload<TServerContext> {
8
- request: Request;
9
- serverContext: TServerContext | undefined;
10
- fetchAPI: FetchAPI;
11
- requestHandler: ServerAdapterRequestHandler<TServerContext>;
12
- setRequestHandler(newRequestHandler: ServerAdapterRequestHandler<TServerContext>): void;
13
- endResponse(response: Response): void;
14
- url: URL;
15
- }
16
- export type OnResponseHook<TServerContext> = (payload: OnResponseEventPayload<TServerContext>) => Promise<void> | void;
17
- export interface OnResponseEventPayload<TServerContext> {
18
- request: Request;
19
- serverContext: TServerContext | undefined;
20
- response: Response;
21
- }
@@ -1,6 +0,0 @@
1
- import { DefaultServerAdapterContext, ServerAdapterBaseObject } from '../types';
2
- import { ServerAdapterPlugin } from './types';
3
- export declare function createDefaultErrorHandler<TServerContext = DefaultServerAdapterContext>(ResponseCtor?: typeof Response): ErrorHandler<TServerContext>;
4
- export type ErrorHandler<TServerContext> = (e: any, request: Request, ctx: TServerContext) => Response | Promise<Response>;
5
- export declare function useErrorHandling<TServerContext>(onError?: ErrorHandler<TServerContext>): ServerAdapterPlugin<TServerContext>;
6
- export declare function withErrorHandling<TServerContext = DefaultServerAdapterContext, TBaseObject extends ServerAdapterBaseObject<TServerContext> = ServerAdapterBaseObject<TServerContext>>(obj: TBaseObject, onError?: ErrorHandler<TServerContext>): TBaseObject;