@whatwg-node/server 0.3.0 → 0.4.0-alpha-20220916163145-ea58ba1

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.
Files changed (5) hide show
  1. package/README.md +11 -12
  2. package/index.d.ts +18 -22
  3. package/index.js +28 -22
  4. package/index.mjs +28 -22
  5. package/package.json +1 -1
package/README.md CHANGED
@@ -12,10 +12,8 @@ Let's create a basic Hello World server adapter.
12
12
  // myServerAdapter.ts
13
13
  import { createServerAdapter } from '@whatwg-node/server'
14
14
 
15
- export default createServerAdapter({
16
- handleRequest(request: Request) {
15
+ export default createServerAdapter((request: Request) => {
17
16
  return new Response(`Hello World!`, { status: 200 })
18
- }
19
17
  })
20
18
  ```
21
19
 
@@ -203,9 +201,7 @@ For example, if you send a multipart request from a browser with `FormData`, you
203
201
  ```ts
204
202
  import { createServerAdapter } from '@whatwg-node/server'
205
203
 
206
- const myServerAdapter = createServerAdapter({
207
- // ...
208
- async handleRequest(request) {
204
+ const myServerAdapter = createServerAdapter(async request => {
209
205
  // Parse the request as `FormData`
210
206
  const formData = await request.formData()
211
207
  // Select the file
@@ -221,8 +217,7 @@ const myServerAdapter = createServerAdapter({
221
217
  'Content-Type': 'application/json'
222
218
  }
223
219
  })
224
- }
225
- })
220
+ })
226
221
  ```
227
222
 
228
223
  You can learn more about [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) on MDN documentation.
@@ -236,6 +231,7 @@ We'd recommend to use [itty-router](https://github.com/kwhitley/itty-router) to
236
231
  ```ts
237
232
  import { Router } from 'itty-router'
238
233
  import { createServerAdapter } from '@whatwg-node/server'
234
+
239
235
  // now let's create a router (note the lack of "new")
240
236
  const router = Router()
241
237
  // GET collection index
@@ -247,12 +243,15 @@ router.post('/todos', async request => {
247
243
  const content = await request.json()
248
244
  return new Response('Creating Todo: ' + JSON.stringify(content))
249
245
  })
246
+
247
+ // Redirect to a URL
248
+ router.get('/google', () => Response.redirect('http://www.google.com'))
249
+
250
250
  // 404 for everything else
251
251
  router.all('*', () => new Response('Not Found.', { status: 404 }))
252
- // attach the router "handle" to our server adapter
253
- const myServerAdapter = createServerAdapter({
254
- handleRequest: router.handle
255
- })
252
+
253
+ // attach the router to our server adapter
254
+ const myServerAdapter = createServerAdapter(router)
256
255
 
257
256
  // Then use it in any environment
258
257
  import { createServer } from 'http'
package/index.d.ts CHANGED
@@ -2,38 +2,30 @@
2
2
  /// <reference lib="webworker" />
3
3
  import type { RequestListener, ServerResponse } from 'node:http';
4
4
  import { NodeRequest } from './utils';
5
- export interface CreateServerAdapterOptions<TServerContext, TBaseObject> {
6
- /**
7
- * WHATWG Fetch spec compliant `Request` constructor.
8
- */
9
- Request?: typeof Request;
5
+ export interface ServerAdapterBaseObject<TServerContext, THandleRequest extends HandleRequestFn<TServerContext>> {
10
6
  /**
11
7
  * An async function that takes `Request` and the server context and returns a `Response`.
12
8
  * If you use `requestListener`, the server context is `{ req: IncomingMessage, res: ServerResponse }`.
13
9
  */
14
- handleRequest: (request: Request, serverContext: TServerContext) => Promise<Response>;
15
- /**
16
- * If you extend a server object with this, you can pass the original object and it will be extended with the required methods and functionalities.
17
- */
18
- baseObject?: TBaseObject;
10
+ handle: THandleRequest;
19
11
  }
20
- export interface ServerAdapterObject<TServerContext> extends EventListenerObject {
12
+ export interface ServerAdapterObject<TServerContext, TBaseObject extends ServerAdapterBaseObject<TServerContext, HandleRequestFn<TServerContext>>> extends EventListenerObject {
21
13
  /**
22
14
  * A basic request listener that takes a `Request` with the server context and returns a `Response`.
23
15
  */
24
- handleRequest: (request: Request, serverContext: TServerContext) => Promise<Response>;
16
+ handleRequest: TBaseObject['handle'];
25
17
  /**
26
18
  * WHATWG Fetch spec compliant `fetch` function that can be used for testing purposes.
27
19
  */
28
- fetch(request: Request, ...ctx: any[]): Promise<Response>;
29
- fetch(urlStr: string, ...ctx: any[]): Promise<Response>;
30
- fetch(urlStr: string, init: RequestInit, ...ctx: any[]): Promise<Response>;
31
- fetch(url: URL, ...ctx: any[]): Promise<Response>;
32
- fetch(url: URL, init: RequestInit, ...ctx: any[]): Promise<Response>;
20
+ fetch(request: Request, ...ctx: any[]): Promise<Response> | Response;
21
+ fetch(urlStr: string, ...ctx: any[]): Promise<Response> | Response;
22
+ fetch(urlStr: string, init: RequestInit, ...ctx: any[]): Promise<Response> | Response;
23
+ fetch(url: URL, ...ctx: any[]): Promise<Response> | Response;
24
+ fetch(url: URL, init: RequestInit, ...ctx: any[]): Promise<Response> | Response;
33
25
  /**
34
26
  * This function takes Node's request object and returns a WHATWG Fetch spec compliant `Response` object.
35
27
  **/
36
- handleNodeRequest(nodeRequest: NodeRequest, serverContext: TServerContext): Promise<Response>;
28
+ handleNodeRequest(nodeRequest: NodeRequest, serverContext: TServerContext): Promise<Response> | Response;
37
29
  /**
38
30
  * A request listener function that can be used with any Node server variation.
39
31
  */
@@ -41,11 +33,15 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
41
33
  /**
42
34
  * Proxy to requestListener to mimic Node middlewares
43
35
  */
44
- handle: RequestListener;
36
+ handle: RequestListener & ServerAdapterObject<TServerContext, TBaseObject>['fetch'];
45
37
  }
46
- export declare type ServerAdapter<TServerContext, TBaseObject> = TBaseObject & RequestListener & ServerAdapterObject<TServerContext>['fetch'] & ServerAdapterObject<TServerContext>;
47
- export declare function createServerAdapter<TServerContext = {
38
+ export declare type ServerAdapter<TServerContext, THandleRequest extends HandleRequestFn<TServerContext>, TBaseObject extends ServerAdapterBaseObject<TServerContext, THandleRequest>> = Omit<TBaseObject, 'handle'> & RequestListener & ServerAdapterObject<TServerContext, TBaseObject>['fetch'] & ServerAdapterObject<TServerContext, TBaseObject>;
39
+ declare type HandleRequestFn<TServerContext> = (request: Request, serverContext: TServerContext) => Promise<Response> | Response;
40
+ declare type DefaultServerContext = {
48
41
  req: NodeRequest;
49
42
  res: ServerResponse;
50
43
  waitUntil(promise: Promise<unknown>): void;
51
- }, TBaseObject = unknown>({ Request: RequestCtor, handleRequest, baseObject, }: CreateServerAdapterOptions<TServerContext, TBaseObject>): ServerAdapter<TServerContext, TBaseObject>;
44
+ };
45
+ declare function createServerAdapter<TServerContext = DefaultServerContext, THandleRequest extends HandleRequestFn<TServerContext> = HandleRequestFn<TServerContext>>(serverAdapterBaseObject: THandleRequest): ServerAdapter<TServerContext, THandleRequest, ServerAdapterBaseObject<TServerContext, THandleRequest>>;
46
+ declare function createServerAdapter<TServerContext = DefaultServerContext, THandleRequest extends HandleRequestFn<TServerContext> = HandleRequestFn<TServerContext>, TBaseObject extends ServerAdapterBaseObject<TServerContext, THandleRequest> = ServerAdapterBaseObject<TServerContext, THandleRequest>>(serverAdapterBaseObject: TBaseObject): ServerAdapter<TServerContext, THandleRequest, TBaseObject>;
47
+ export { createServerAdapter };
package/index.js CHANGED
@@ -156,14 +156,20 @@ async function sendNodeResponse({ headers, status, statusText, body }, serverRes
156
156
  }
157
157
 
158
158
  /// <reference lib="webworker" />
159
- function handleWaitUntils(waitUntilPromises) {
160
- return Promise.allSettled(waitUntilPromises).then(waitUntils => waitUntils.forEach(waitUntil => {
159
+ async function handleWaitUntils(waitUntilPromises) {
160
+ const waitUntils = await Promise.allSettled(waitUntilPromises);
161
+ waitUntils.forEach(waitUntil => {
161
162
  if (waitUntil.status === 'rejected') {
162
163
  console.error(waitUntil.reason);
163
164
  }
164
- }));
165
+ });
165
166
  }
166
- function createServerAdapter({ Request: RequestCtor = fetch.Request, handleRequest, baseObject, }) {
167
+ function createServerAdapter(serverAdapterBaseObject,
168
+ /**
169
+ * WHATWG Fetch spec compliant `Request` constructor.
170
+ */
171
+ RequestCtor = fetch.Request) {
172
+ const handleRequest = typeof serverAdapterBaseObject === 'function' ? serverAdapterBaseObject : serverAdapterBaseObject.handle;
167
173
  function fetchFn(input, init, ...ctx) {
168
174
  if (typeof input === 'string' || input instanceof URL) {
169
175
  return handleRequest(new RequestCtor(input, init), Object.assign({}, ...ctx));
@@ -192,7 +198,9 @@ function createServerAdapter({ Request: RequestCtor = fetch.Request, handleReque
192
198
  serverResponse.end(resolve);
193
199
  });
194
200
  }
195
- await handleWaitUntils(waitUntilPromises);
201
+ if (waitUntilPromises.length > 0) {
202
+ await handleWaitUntils(waitUntilPromises);
203
+ }
196
204
  }
197
205
  function handleEvent(event) {
198
206
  if (!event.respondWith || !event.request) {
@@ -201,14 +209,6 @@ function createServerAdapter({ Request: RequestCtor = fetch.Request, handleReque
201
209
  const response$ = handleRequest(event.request, event);
202
210
  event.respondWith(response$);
203
211
  }
204
- const adapterObj = {
205
- handleRequest,
206
- fetch: fetchFn,
207
- handleNodeRequest,
208
- requestListener,
209
- handleEvent,
210
- handle: requestListener,
211
- };
212
212
  function genericRequestHandler(input, ctx, ...rest) {
213
213
  var _a;
214
214
  if ('process' in globalThis && ((_a = process.versions) === null || _a === void 0 ? void 0 : _a['bun']) != null) {
@@ -250,20 +250,20 @@ function createServerAdapter({ Request: RequestCtor = fetch.Request, handleReque
250
250
  }
251
251
  return handleRequest(input, ctx);
252
252
  }
253
+ const adapterObj = {
254
+ handleRequest,
255
+ fetch: fetchFn,
256
+ handleNodeRequest,
257
+ requestListener,
258
+ handleEvent,
259
+ handle: genericRequestHandler,
260
+ };
253
261
  return new Proxy(genericRequestHandler, {
254
262
  // It should have all the attributes of the handler function and the server instance
255
263
  has: (_, prop) => {
256
- return (baseObject && prop in baseObject) || prop in adapterObj || prop in genericRequestHandler;
264
+ return prop in adapterObj || prop in genericRequestHandler || (serverAdapterBaseObject && prop in serverAdapterBaseObject);
257
265
  },
258
266
  get: (_, prop) => {
259
- if (baseObject) {
260
- if (prop in baseObject) {
261
- if (baseObject[prop].bind) {
262
- return baseObject[prop].bind(baseObject);
263
- }
264
- return baseObject[prop];
265
- }
266
- }
267
267
  if (adapterObj[prop]) {
268
268
  if (adapterObj[prop].bind) {
269
269
  return adapterObj[prop].bind(adapterObj);
@@ -276,6 +276,12 @@ function createServerAdapter({ Request: RequestCtor = fetch.Request, handleReque
276
276
  }
277
277
  return genericRequestHandler[prop];
278
278
  }
279
+ if (prop in serverAdapterBaseObject) {
280
+ if (serverAdapterBaseObject[prop].bind) {
281
+ return serverAdapterBaseObject[prop].bind(serverAdapterBaseObject);
282
+ }
283
+ return serverAdapterBaseObject[prop];
284
+ }
279
285
  },
280
286
  apply(_, __, [input, ctx]) {
281
287
  return genericRequestHandler(input, ctx);
package/index.mjs CHANGED
@@ -152,14 +152,20 @@ async function sendNodeResponse({ headers, status, statusText, body }, serverRes
152
152
  }
153
153
 
154
154
  /// <reference lib="webworker" />
155
- function handleWaitUntils(waitUntilPromises) {
156
- return Promise.allSettled(waitUntilPromises).then(waitUntils => waitUntils.forEach(waitUntil => {
155
+ async function handleWaitUntils(waitUntilPromises) {
156
+ const waitUntils = await Promise.allSettled(waitUntilPromises);
157
+ waitUntils.forEach(waitUntil => {
157
158
  if (waitUntil.status === 'rejected') {
158
159
  console.error(waitUntil.reason);
159
160
  }
160
- }));
161
+ });
161
162
  }
162
- function createServerAdapter({ Request: RequestCtor = Request, handleRequest, baseObject, }) {
163
+ function createServerAdapter(serverAdapterBaseObject,
164
+ /**
165
+ * WHATWG Fetch spec compliant `Request` constructor.
166
+ */
167
+ RequestCtor = Request) {
168
+ const handleRequest = typeof serverAdapterBaseObject === 'function' ? serverAdapterBaseObject : serverAdapterBaseObject.handle;
163
169
  function fetchFn(input, init, ...ctx) {
164
170
  if (typeof input === 'string' || input instanceof URL) {
165
171
  return handleRequest(new RequestCtor(input, init), Object.assign({}, ...ctx));
@@ -188,7 +194,9 @@ function createServerAdapter({ Request: RequestCtor = Request, handleRequest, ba
188
194
  serverResponse.end(resolve);
189
195
  });
190
196
  }
191
- await handleWaitUntils(waitUntilPromises);
197
+ if (waitUntilPromises.length > 0) {
198
+ await handleWaitUntils(waitUntilPromises);
199
+ }
192
200
  }
193
201
  function handleEvent(event) {
194
202
  if (!event.respondWith || !event.request) {
@@ -197,14 +205,6 @@ function createServerAdapter({ Request: RequestCtor = Request, handleRequest, ba
197
205
  const response$ = handleRequest(event.request, event);
198
206
  event.respondWith(response$);
199
207
  }
200
- const adapterObj = {
201
- handleRequest,
202
- fetch: fetchFn,
203
- handleNodeRequest,
204
- requestListener,
205
- handleEvent,
206
- handle: requestListener,
207
- };
208
208
  function genericRequestHandler(input, ctx, ...rest) {
209
209
  var _a;
210
210
  if ('process' in globalThis && ((_a = process.versions) === null || _a === void 0 ? void 0 : _a['bun']) != null) {
@@ -246,20 +246,20 @@ function createServerAdapter({ Request: RequestCtor = Request, handleRequest, ba
246
246
  }
247
247
  return handleRequest(input, ctx);
248
248
  }
249
+ const adapterObj = {
250
+ handleRequest,
251
+ fetch: fetchFn,
252
+ handleNodeRequest,
253
+ requestListener,
254
+ handleEvent,
255
+ handle: genericRequestHandler,
256
+ };
249
257
  return new Proxy(genericRequestHandler, {
250
258
  // It should have all the attributes of the handler function and the server instance
251
259
  has: (_, prop) => {
252
- return (baseObject && prop in baseObject) || prop in adapterObj || prop in genericRequestHandler;
260
+ return prop in adapterObj || prop in genericRequestHandler || (serverAdapterBaseObject && prop in serverAdapterBaseObject);
253
261
  },
254
262
  get: (_, prop) => {
255
- if (baseObject) {
256
- if (prop in baseObject) {
257
- if (baseObject[prop].bind) {
258
- return baseObject[prop].bind(baseObject);
259
- }
260
- return baseObject[prop];
261
- }
262
- }
263
263
  if (adapterObj[prop]) {
264
264
  if (adapterObj[prop].bind) {
265
265
  return adapterObj[prop].bind(adapterObj);
@@ -272,6 +272,12 @@ function createServerAdapter({ Request: RequestCtor = Request, handleRequest, ba
272
272
  }
273
273
  return genericRequestHandler[prop];
274
274
  }
275
+ if (prop in serverAdapterBaseObject) {
276
+ if (serverAdapterBaseObject[prop].bind) {
277
+ return serverAdapterBaseObject[prop].bind(serverAdapterBaseObject);
278
+ }
279
+ return serverAdapterBaseObject[prop];
280
+ }
275
281
  },
276
282
  apply(_, __, [input, ctx]) {
277
283
  return genericRequestHandler(input, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/server",
3
- "version": "0.3.0",
3
+ "version": "0.4.0-alpha-20220916163145-ea58ba1",
4
4
  "description": "Fetch API compliant HTTP Server adapter",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {