@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.
- package/lib/application.js +12 -3
- package/lib/endpoint.js +35 -15
- package/package.json +1 -1
- package/test/index.js +13 -0
package/lib/application.js
CHANGED
|
@@ -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 (
|
|
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
|
|
181
|
+
if (idx === this._layers.length) {
|
|
182
|
+
|
|
183
|
+
if (path.isLast) {
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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 !==
|
|
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] =
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
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(
|