@trenskow/app 0.5.8 → 0.5.12

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.
@@ -64,8 +64,10 @@ export default class Application extends EventEmitter {
64
64
  this.#_rootEndpoint = new Endpoint()
65
65
  .use(() => { throw new ApiError.NotFound(); });
66
66
 
67
- this.#_renderer = async ({ result, response }) => {
68
- if (result instanceof ApiError) {
67
+ this.#_renderer = async ({ result, request, response }) => {
68
+ if (request.method.toLowerCase() === 'head') {
69
+ response.end();
70
+ } else if (result instanceof ApiError) {
69
71
  response.end();
70
72
  } else if (typeof result === 'string') {
71
73
  response.headers.contentType = 'text/plain; charset=utf-8';
@@ -256,7 +258,7 @@ export default class Application extends EventEmitter {
256
258
  ({ error, brutally } = error);
257
259
  }
258
260
 
259
- if (!['completed', 'aborted'].includes(state)) {
261
+ if (!['completed', 'aborted', 'ignored'].includes(state)) {
260
262
  state = 'aborted';
261
263
  if (error) context.result = error;
262
264
  }
@@ -269,6 +271,11 @@ export default class Application extends EventEmitter {
269
271
  });
270
272
  }
271
273
 
274
+ },
275
+ ignore: () => {
276
+ if (!['completed', 'aborted', 'ignored'].includes(state)) {
277
+ state = 'ignored';
278
+ }
272
279
  }
273
280
 
274
281
  };
@@ -321,6 +328,8 @@ export default class Application extends EventEmitter {
321
328
  });
322
329
  });
323
330
 
331
+ if (state === 'ignored') return;
332
+
324
333
  if (context.response.statusCode === 200) {
325
334
  context.response.statusCode = typeof context.result !== 'undefined' ? 200 : 204;
326
335
  }
package/lib/endpoint.js CHANGED
@@ -121,6 +121,10 @@ export default class Endpoint extends Router {
121
121
 
122
122
  endpoint = resolveInlineImport(endpoint);
123
123
 
124
+ if (typeof transform !== 'undefined' && typeof transform !== 'function') {
125
+ throw new Error('Transforms must be a function.');
126
+ }
127
+
124
128
  if (!(endpoint instanceof Endpoint)) {
125
129
  throw new Error('Endpoints must be of type Endpoint');
126
130
  }
@@ -174,16 +178,22 @@ export default class Endpoint extends Router {
174
178
 
175
179
  async _route(path, context, next, idx = 0) {
176
180
 
177
- if (idx === this._layers.length && path.isLast) {
181
+ if (idx === this._layers.length) {
182
+
183
+ if (path.isLast) {
178
184
 
179
- const methods = this._layers
180
- .filter((layer) => layer.type === 'method')
181
- .map((layer) => layer.method.toUpperCase())
182
- .filter((value, index, array) => array.indexOf(value) === index);
185
+ const methods = this._layers
186
+ .filter((layer) => layer.type === 'method')
187
+ .map((layer) => layer.method.toUpperCase())
188
+ .filter((value, index, array) => array.indexOf(value) === index);
189
+
190
+ if (methods.length) {
191
+ context.response.headers.allow = methods.join(', ');
192
+ throw new ApiError.MethodNotAllowed();
193
+ }
183
194
 
184
- if (methods.length) {
185
- context.response.headers.allow = methods.join(', ');
186
- throw new ApiError.MethodNotAllowed();
195
+ } else {
196
+ throw new ApiError.NotFound();
187
197
  }
188
198
 
189
199
  }
@@ -214,8 +224,14 @@ export default class Endpoint extends Router {
214
224
 
215
225
  case 'method': {
216
226
 
227
+ let underlyingMethod = context.request.method.toLowerCase();
228
+
229
+ if (underlyingMethod === 'head') {
230
+ underlyingMethod = 'get';
231
+ }
232
+
217
233
  if (layer.match === 'direct' && !path.isLast) return await next();
218
- if (layer.method !== 'all' && layer.method !== context.request.method.toLowerCase()) return await next();
234
+ if (layer.method !== 'all' && layer.method !== underlyingMethod) return await next();
219
235
 
220
236
  for (let handler of layer.handlers) {
221
237
  const result = await handler(context);
@@ -233,12 +249,16 @@ export default class Endpoint extends Router {
233
249
 
234
250
  if (!component) return await path.popped(next);
235
251
 
236
- context.parameters[layer.name] = await layer.transform?.(
237
- Object.fromEntries([
238
- [ 'context', context ],
239
- [ layer.name, component ]
240
- ])
241
- ) || component;
252
+ context.parameters[layer.name] = component;
253
+
254
+ if (typeof layer.transform === 'function') {
255
+ context.parameters[layer.name] = await layer.transform(
256
+ Object.fromEntries([
257
+ ['context', context],
258
+ [layer.name, component]
259
+ ])
260
+ );
261
+ }
242
262
 
243
263
  return await layer.endpoint._route(path, context, next);
244
264
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/app",
3
- "version": "0.5.8",
3
+ "version": "0.5.12",
4
4
  "description": "A small HTTP router.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/test/index.js CHANGED
@@ -74,6 +74,19 @@ describe('Application', () => {
74
74
 
75
75
  });
76
76
 
77
+ it ('should respond with 200 and no content when a GET method is configured and HEAD is requested.', async () => {
78
+
79
+ app.root(
80
+ new Endpoint()
81
+ .get(() => 'Hello, World!')
82
+ );
83
+
84
+ await request
85
+ .head('/')
86
+ .expect(200, undefined);
87
+
88
+ });
89
+
77
90
  it ('should ignore multiple GET method handlers and respond with 200 and `Hello, World!`.', async () => {
78
91
 
79
92
  app.root(