@whatwg-node/server 0.3.0 → 0.4.0-alpha-20220916163416-8c6abfa

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 +25 -26
  2. package/index.d.ts +18 -22
  3. package/index.js +30 -22
  4. package/index.mjs +30 -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) {
17
- return new Response(`Hello World!`, { status: 200 })
18
- }
15
+ export default createServerAdapter((request: Request) => {
16
+ return new Response(`Hello World!`, { status: 200 })
19
17
  })
20
18
  ```
21
19
 
@@ -203,25 +201,22 @@ 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({
204
+ const myServerAdapter = createServerAdapter(async request => {
205
+ // Parse the request as `FormData`
206
+ const formData = await request.formData()
207
+ // Select the file
208
+ const file = formData.get('file')
209
+ // Process it as a string
210
+ const fileTextContent = await file.text()
211
+ // Select the other text parameter
212
+ const regularTextData = formData.get('additionalStuff')
207
213
  // ...
208
- async handleRequest(request) {
209
- // Parse the request as `FormData`
210
- const formData = await request.formData()
211
- // Select the file
212
- const file = formData.get('file')
213
- // Process it as a string
214
- const fileTextContent = await file.text()
215
- // Select the other text parameter
216
- const regularTextData = formData.get('additionalStuff')
217
- // ...
218
- return new Response('{ "message": "ok" }', {
219
- status: 200,
220
- headers: {
221
- 'Content-Type': 'application/json'
222
- }
223
- })
224
- }
214
+ return new Response('{ "message": "ok" }', {
215
+ status: 200,
216
+ headers: {
217
+ 'Content-Type': 'application/json'
218
+ }
219
+ })
225
220
  })
226
221
  ```
227
222
 
@@ -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,22 @@ 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 ||
265
+ prop in genericRequestHandler ||
266
+ (serverAdapterBaseObject && prop in serverAdapterBaseObject));
257
267
  },
258
268
  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
269
  if (adapterObj[prop]) {
268
270
  if (adapterObj[prop].bind) {
269
271
  return adapterObj[prop].bind(adapterObj);
@@ -276,6 +278,12 @@ function createServerAdapter({ Request: RequestCtor = fetch.Request, handleReque
276
278
  }
277
279
  return genericRequestHandler[prop];
278
280
  }
281
+ if (prop in serverAdapterBaseObject) {
282
+ if (serverAdapterBaseObject[prop].bind) {
283
+ return serverAdapterBaseObject[prop].bind(serverAdapterBaseObject);
284
+ }
285
+ return serverAdapterBaseObject[prop];
286
+ }
279
287
  },
280
288
  apply(_, __, [input, ctx]) {
281
289
  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,22 @@ 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 ||
261
+ prop in genericRequestHandler ||
262
+ (serverAdapterBaseObject && prop in serverAdapterBaseObject));
253
263
  },
254
264
  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
265
  if (adapterObj[prop]) {
264
266
  if (adapterObj[prop].bind) {
265
267
  return adapterObj[prop].bind(adapterObj);
@@ -272,6 +274,12 @@ function createServerAdapter({ Request: RequestCtor = Request, handleRequest, ba
272
274
  }
273
275
  return genericRequestHandler[prop];
274
276
  }
277
+ if (prop in serverAdapterBaseObject) {
278
+ if (serverAdapterBaseObject[prop].bind) {
279
+ return serverAdapterBaseObject[prop].bind(serverAdapterBaseObject);
280
+ }
281
+ return serverAdapterBaseObject[prop];
282
+ }
275
283
  },
276
284
  apply(_, __, [input, ctx]) {
277
285
  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-20220916163416-8c6abfa",
4
4
  "description": "Fetch API compliant HTTP Server adapter",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {