routup 3.0.0-alpha.3 → 3.1.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.
Files changed (63) hide show
  1. package/README.md +23 -19
  2. package/dist/adapters/node/module.d.ts +7 -0
  3. package/dist/{utils → adapters/raw}/header.d.ts +1 -1
  4. package/dist/{layer → adapters/raw}/index.d.ts +1 -1
  5. package/dist/adapters/raw/module.d.ts +4 -0
  6. package/dist/{dispatcher/adapters → adapters}/raw/type.d.ts +1 -1
  7. package/dist/adapters/web/module.d.ts +4 -0
  8. package/dist/constants.d.ts +7 -7
  9. package/dist/dispatcher/event/dispatch.d.ts +4 -0
  10. package/dist/dispatcher/event/error.d.ts +5 -0
  11. package/dist/dispatcher/event/index.d.ts +5 -0
  12. package/dist/dispatcher/event/is.d.ts +3 -0
  13. package/dist/dispatcher/event/module.d.ts +56 -0
  14. package/dist/dispatcher/event/types.d.ts +9 -0
  15. package/dist/dispatcher/index.d.ts +1 -2
  16. package/dist/dispatcher/type.d.ts +2 -30
  17. package/dist/error/create.d.ts +3 -3
  18. package/dist/error/is.d.ts +2 -2
  19. package/dist/error/module.d.ts +1 -1
  20. package/dist/handler/constants.d.ts +1 -0
  21. package/dist/handler/core/define.d.ts +4 -3
  22. package/dist/handler/core/types.d.ts +3 -3
  23. package/dist/handler/error/define.d.ts +4 -3
  24. package/dist/handler/error/types.d.ts +5 -5
  25. package/dist/handler/index.d.ts +1 -0
  26. package/dist/handler/is.d.ts +3 -1
  27. package/dist/handler/module.d.ts +23 -0
  28. package/dist/handler/types-base.d.ts +6 -2
  29. package/dist/handler/types.d.ts +3 -4
  30. package/dist/hook/constants.d.ts +8 -0
  31. package/dist/hook/index.d.ts +3 -0
  32. package/dist/hook/module.d.ts +19 -0
  33. package/dist/hook/types.d.ts +5 -0
  34. package/dist/index.cjs +658 -468
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.mjs +649 -461
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/plugin/types.d.ts +0 -5
  40. package/dist/response/helpers/send-web-blob.d.ts +1 -1
  41. package/dist/response/helpers/send-web-response.d.ts +1 -1
  42. package/dist/router/constants.d.ts +8 -0
  43. package/dist/router/index.d.ts +0 -1
  44. package/dist/router/module.d.ts +58 -24
  45. package/dist/router/types.d.ts +7 -0
  46. package/dist/types.d.ts +1 -0
  47. package/dist/utils/index.d.ts +2 -1
  48. package/dist/utils/method.d.ts +3 -0
  49. package/dist/utils/next.d.ts +2 -0
  50. package/package.json +8 -8
  51. package/dist/dispatcher/adapters/node/module.d.ts +0 -7
  52. package/dist/dispatcher/adapters/raw/module.d.ts +0 -4
  53. package/dist/dispatcher/adapters/web/index.d.ts +0 -2
  54. package/dist/dispatcher/adapters/web/module.d.ts +0 -5
  55. package/dist/dispatcher/utils.d.ts +0 -5
  56. package/dist/layer/constants.d.ts +0 -1
  57. package/dist/layer/module.d.ts +0 -17
  58. package/dist/layer/type.d.ts +0 -8
  59. package/dist/layer/utils.d.ts +0 -2
  60. /package/dist/{dispatcher/adapters → adapters}/index.d.ts +0 -0
  61. /package/dist/{dispatcher/adapters → adapters}/node/index.d.ts +0 -0
  62. /package/dist/{dispatcher/adapters/raw → adapters/web}/index.d.ts +0 -0
  63. /package/dist/{dispatcher/adapters → adapters}/web/type.d.ts +0 -0
package/dist/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { HTTPError } from '@ebec/http';
2
1
  import { merge, hasOwnProperty, distinctArray } from 'smob';
3
2
  import { Buffer } from 'buffer';
4
3
  import { subtle } from 'uncrypto';
@@ -6,17 +5,18 @@ import { compile, all } from 'proxy-addr';
6
5
  import { getType, get } from 'mime-explorer';
7
6
  import Negotiator from 'negotiator';
8
7
  import { Readable, Writable } from 'readable-stream';
8
+ import { HTTPError } from '@ebec/http';
9
9
  import { pathToRegexp } from 'path-to-regexp';
10
10
 
11
11
  var MethodName;
12
12
  (function(MethodName) {
13
- MethodName["GET"] = "get";
14
- MethodName["POST"] = "post";
15
- MethodName["PUT"] = "put";
16
- MethodName["PATCH"] = "patch";
17
- MethodName["DELETE"] = "delete";
18
- MethodName["OPTIONS"] = "options";
19
- MethodName["HEAD"] = "head";
13
+ MethodName["GET"] = "GET";
14
+ MethodName["POST"] = "POST";
15
+ MethodName["PUT"] = "PUT";
16
+ MethodName["PATCH"] = "PATCH";
17
+ MethodName["DELETE"] = "DELETE";
18
+ MethodName["OPTIONS"] = "OPTIONS";
19
+ MethodName["HEAD"] = "HEAD";
20
20
  })(MethodName || (MethodName = {}));
21
21
  var HeaderName;
22
22
  (function(HeaderName) {
@@ -51,32 +51,6 @@ var HeaderName;
51
51
  HeaderName["X_FORWARDED_PROTO"] = "x-forwarded-proto";
52
52
  })(HeaderName || (HeaderName = {}));
53
53
 
54
- class ErrorProxy extends HTTPError {
55
- }
56
-
57
- function isError(input) {
58
- return input instanceof ErrorProxy;
59
- }
60
-
61
- /**
62
- * Create an error proxy by
63
- * - an existing error (accessible via cause property)
64
- * - options
65
- * - message
66
- *
67
- * @param input
68
- */ function createError(input) {
69
- if (isError(input)) {
70
- return input;
71
- }
72
- if (typeof input === 'string') {
73
- return new ErrorProxy(input);
74
- }
75
- return new ErrorProxy({
76
- cause: input
77
- }, input);
78
- }
79
-
80
54
  function isRequestCacheable(req, modifiedTime) {
81
55
  const modifiedSince = req.headers[HeaderName.IF_MODIFIED_SINCE];
82
56
  if (!modifiedSince) {
@@ -207,33 +181,6 @@ function setRequestHeader(req, name, value) {
207
181
  return cookiesStrings;
208
182
  }
209
183
 
210
- function transformHeaderToTuples(key, value) {
211
- const output = [];
212
- if (Array.isArray(value)) {
213
- for(let j = 0; j < value.length; j++){
214
- output.push([
215
- key,
216
- value[j]
217
- ]);
218
- }
219
- } else if (value !== undefined) {
220
- output.push([
221
- key,
222
- String(value)
223
- ]);
224
- }
225
- return output;
226
- }
227
- function transformHeadersToTuples(input) {
228
- const output = [];
229
- const keys = Object.keys(input);
230
- for(let i = 0; i < keys.length; i++){
231
- const key = keys[i].toLowerCase();
232
- output.push(...transformHeaderToTuples(key, input[key]));
233
- }
234
- return output;
235
- }
236
-
237
184
  function isObject(item) {
238
185
  return !!item && typeof item === 'object' && !Array.isArray(item);
239
186
  }
@@ -346,6 +293,15 @@ function getCharsetForMimeType(type) {
346
293
  return undefined;
347
294
  }
348
295
 
296
+ function toMethodName(input, alt) {
297
+ if (input) {
298
+ return input.toUpperCase();
299
+ }
300
+ return alt;
301
+ }
302
+
303
+ const nextPlaceholder = (_err)=>{};
304
+
349
305
  /**
350
306
  * Based on https://github.com/unjs/pathe v1.1.1 (055f50a6f1131f4e5c56cf259dd8816168fba329)
351
307
  */ function normalizeWindowsPath(input = '') {
@@ -749,6 +705,35 @@ function createRequest(context) {
749
705
  return readable;
750
706
  }
751
707
 
708
+ class RoutupError extends HTTPError {
709
+ }
710
+
711
+ function isError(input) {
712
+ return input instanceof RoutupError;
713
+ }
714
+
715
+ /**
716
+ * Create an internal error object by
717
+ * - an existing error (accessible via cause property)
718
+ * - options
719
+ * - message
720
+ *
721
+ * @param input
722
+ */ function createError(input) {
723
+ if (isError(input)) {
724
+ return input;
725
+ }
726
+ if (typeof input === 'string') {
727
+ return new RoutupError(input);
728
+ }
729
+ if (!isObject(input)) {
730
+ return new RoutupError();
731
+ }
732
+ return new RoutupError({
733
+ cause: input
734
+ }, input);
735
+ }
736
+
752
737
  function setResponseCacheHeaders(res, options) {
753
738
  options = options || {};
754
739
  const cacheControls = [
@@ -904,7 +889,7 @@ async function send(res, chunk) {
904
889
  const etagFn = findRouterOption('etag', useRequestRouterPath(res.req));
905
890
  const chunkHash = await etagFn(chunk, encoding, len);
906
891
  if (isResponseGone(res)) {
907
- return Promise.resolve();
892
+ return;
908
893
  }
909
894
  if (typeof chunkHash === 'string') {
910
895
  res.setHeader(HeaderName.ETag, chunkHash);
@@ -925,23 +910,22 @@ async function send(res, chunk) {
925
910
  res.removeHeader(HeaderName.TRANSFER_ENCODING);
926
911
  }
927
912
  if (isResponseGone(res)) {
928
- return Promise.resolve();
913
+ return;
929
914
  }
930
- if (res.req.method === 'HEAD') {
915
+ if (res.req.method === 'HEAD' || res.req.method === 'head') {
931
916
  // skip body for HEAD
932
917
  res.end();
933
- return Promise.resolve();
918
+ return;
934
919
  }
935
920
  if (typeof chunk === 'undefined' || chunk === null) {
936
921
  res.end();
937
- return Promise.resolve();
922
+ return;
938
923
  }
939
924
  if (typeof encoding !== 'undefined') {
940
925
  res.end(chunk, encoding);
941
- return Promise.resolve();
926
+ return;
942
927
  }
943
928
  res.end(chunk);
944
- return Promise.resolve();
945
929
  }
946
930
 
947
931
  function sendAccepted(res, chunk) {
@@ -1087,7 +1071,7 @@ function sendRedirect(res, location, statusCode = 302) {
1087
1071
  return send(res, html);
1088
1072
  }
1089
1073
 
1090
- function sendWebResponse(res, webResponse) {
1074
+ async function sendWebResponse(res, webResponse) {
1091
1075
  if (webResponse.redirected) {
1092
1076
  res.setHeader(HeaderName.LOCATION, webResponse.url);
1093
1077
  }
@@ -1105,15 +1089,16 @@ function sendWebResponse(res, webResponse) {
1105
1089
  }
1106
1090
  });
1107
1091
  if (webResponse.body) {
1108
- return sendStream(res, webResponse.body);
1092
+ await sendStream(res, webResponse.body);
1093
+ return Promise.resolve();
1109
1094
  }
1110
1095
  res.end();
1111
1096
  return Promise.resolve();
1112
1097
  }
1113
1098
 
1114
- function sendWebBlob(res, blob) {
1099
+ async function sendWebBlob(res, blob) {
1115
1100
  setResponseHeaderContentType(res, blob.type);
1116
- return sendStream(res, blob.stream());
1101
+ await sendStream(res, blob.stream());
1117
1102
  }
1118
1103
 
1119
1104
  function createResponse(request) {
@@ -1261,84 +1246,130 @@ function createResponse(request) {
1261
1246
  return writable;
1262
1247
  }
1263
1248
 
1264
- function buildDispatcherMeta(input) {
1265
- return {
1266
- mountPath: input.mountPath || '/',
1267
- params: input.params || {},
1268
- path: input.path || '/',
1269
- routerPath: []
1270
- };
1271
- }
1272
- function cloneDispatcherMeta(input) {
1273
- return {
1274
- path: input.path,
1275
- mountPath: input.mountPath,
1276
- error: input.error,
1277
- routerPath: [
1278
- ...input.routerPath
1279
- ],
1280
- params: cloneDispatcherMetaParams(input.params)
1281
- };
1282
- }
1283
- function cloneDispatcherMetaParams(input) {
1284
- if (typeof input === 'undefined') {
1285
- return {};
1249
+ async function sendData(event, chunk) {
1250
+ if (chunk instanceof Error) {
1251
+ return Promise.reject(createError(chunk));
1286
1252
  }
1287
- const keys = Object.keys(input);
1288
- if (keys.length === 0) {
1289
- return {};
1253
+ if (isStream(chunk)) {
1254
+ await sendStream(event.response, chunk);
1255
+ return Promise.resolve();
1290
1256
  }
1291
- const output = {};
1292
- for(let i = 0; i < keys.length; i++){
1293
- output[keys[i]] = input[keys[i]];
1257
+ if (isWebBlob(chunk)) {
1258
+ await sendWebBlob(event.response, chunk);
1259
+ return Promise.resolve();
1294
1260
  }
1295
- return output;
1296
- }
1297
- function mergeDispatcherMetaParams(t1, t2) {
1298
- if (!t1 && !t2) {
1299
- return {};
1261
+ if (isWebResponse(chunk)) {
1262
+ await sendWebResponse(event.response, chunk);
1263
+ return Promise.resolve();
1300
1264
  }
1301
- if (!t1 || !t2) {
1302
- return t1 || t2;
1265
+ return send(event.response, chunk);
1266
+ }
1267
+ function dispatch(event, target) {
1268
+ setRequestParams(event.request, event.params);
1269
+ setRequestMountPath(event.request, event.mountPath);
1270
+ setRequestRouterPath(event.request, event.routerPath);
1271
+ return new Promise((resolve, reject)=>{
1272
+ let handled = false;
1273
+ const unsubscribe = ()=>{
1274
+ event.response.off('close', done);
1275
+ event.response.off('error', done);
1276
+ };
1277
+ const shutdown = (dispatched, err)=>{
1278
+ if (handled) {
1279
+ return;
1280
+ }
1281
+ handled = true;
1282
+ unsubscribe();
1283
+ if (err) {
1284
+ reject(createError(err));
1285
+ } else {
1286
+ resolve(dispatched);
1287
+ }
1288
+ };
1289
+ const done = (err)=>shutdown(true, err);
1290
+ const next = (err)=>shutdown(false, err);
1291
+ event.response.once('close', done);
1292
+ event.response.once('error', done);
1293
+ const handle = async (data)=>{
1294
+ if (typeof data === 'undefined' || handled) {
1295
+ return false;
1296
+ }
1297
+ handled = true;
1298
+ unsubscribe();
1299
+ if (!event.dispatched) {
1300
+ await sendData(event, data);
1301
+ }
1302
+ return true;
1303
+ };
1304
+ try {
1305
+ const output = target(next);
1306
+ if (isPromise(output)) {
1307
+ output.then((r)=>handle(r)).then((resolved)=>{
1308
+ if (resolved) {
1309
+ resolve(true);
1310
+ }
1311
+ }).catch((e)=>reject(createError(e)));
1312
+ return;
1313
+ }
1314
+ Promise.resolve().then(()=>handle(output)).then((resolved)=>{
1315
+ if (resolved) {
1316
+ resolve(true);
1317
+ }
1318
+ }).catch((e)=>reject(createError(e)));
1319
+ } catch (error) {
1320
+ next(error);
1321
+ }
1322
+ });
1323
+ }
1324
+
1325
+ function isDispatcherErrorEvent(event) {
1326
+ return typeof event.error !== 'undefined';
1327
+ }
1328
+
1329
+ class DispatchEvent {
1330
+ get dispatched() {
1331
+ return this._dispatched || this.response.writableEnded || this.response.headersSent;
1303
1332
  }
1304
- const keys = Object.keys(t2);
1305
- if (keys.length === 0) {
1306
- return t1;
1333
+ set dispatched(value) {
1334
+ this._dispatched = value;
1307
1335
  }
1308
- for(let i = 0; i < keys.length; i++){
1309
- t1[keys[i]] = t2[keys[i]];
1336
+ constructor(context){
1337
+ this.request = context.request;
1338
+ this.response = context.response;
1339
+ this.method = context.method || MethodName.GET;
1340
+ this.methodsAllowed = [];
1341
+ this.mountPath = '/';
1342
+ this.params = {};
1343
+ this.path = context.path || '/';
1344
+ this.routerPath = [];
1345
+ this.next = nextPlaceholder;
1310
1346
  }
1311
- return t1;
1312
1347
  }
1313
1348
 
1314
- async function dispatchNodeRequest(router, req, res) {
1315
- try {
1316
- const dispatched = await router.dispatch({
1317
- req,
1318
- res
1319
- }, buildDispatcherMeta({
1320
- path: useRequestPath(req)
1321
- }));
1322
- if (dispatched) {
1323
- return;
1324
- }
1325
- if (!isResponseGone(res)) {
1326
- res.statusCode = 404;
1327
- res.end();
1328
- }
1329
- } catch (e) {
1330
- if (!isResponseGone(res)) {
1331
- if (isError(e)) {
1332
- res.statusCode = e.statusCode;
1333
- if (e.statusMessage) {
1334
- res.statusMessage = e.statusMessage;
1335
- }
1336
- } else {
1337
- res.statusCode = 500;
1338
- }
1339
- res.end();
1349
+ class DispatchErrorEvent extends DispatchEvent {
1350
+ }
1351
+
1352
+ async function dispatchNodeRequest(router, request, response) {
1353
+ const event = new DispatchEvent({
1354
+ request,
1355
+ response,
1356
+ path: useRequestPath(request),
1357
+ method: toMethodName(request.method, MethodName.GET)
1358
+ });
1359
+ await router.dispatch(event);
1360
+ if (event.dispatched) {
1361
+ return;
1362
+ }
1363
+ if (event.error) {
1364
+ event.response.statusCode = event.error.statusCode;
1365
+ if (event.error.statusMessage) {
1366
+ event.response.statusMessage = event.error.statusMessage;
1340
1367
  }
1368
+ event.response.end();
1369
+ return;
1341
1370
  }
1371
+ event.response.statusCode = 404;
1372
+ event.response.end();
1342
1373
  }
1343
1374
  function createNodeDispatcher(router) {
1344
1375
  return (req, res)=>{
@@ -1347,10 +1378,38 @@ function createNodeDispatcher(router) {
1347
1378
  };
1348
1379
  }
1349
1380
 
1350
- async function dispatchRawRequest(router, request, options = {}) {
1381
+ function transformHeaderToTuples(key, value) {
1382
+ const output = [];
1383
+ if (Array.isArray(value)) {
1384
+ for(let j = 0; j < value.length; j++){
1385
+ output.push([
1386
+ key,
1387
+ value[j]
1388
+ ]);
1389
+ }
1390
+ } else if (value !== undefined) {
1391
+ output.push([
1392
+ key,
1393
+ String(value)
1394
+ ]);
1395
+ }
1396
+ return output;
1397
+ }
1398
+ function transformHeadersToTuples(input) {
1399
+ const output = [];
1400
+ const keys = Object.keys(input);
1401
+ for(let i = 0; i < keys.length; i++){
1402
+ const key = keys[i].toLowerCase();
1403
+ output.push(...transformHeaderToTuples(key, input[key]));
1404
+ }
1405
+ return output;
1406
+ }
1407
+
1408
+ async function dispatchRawRequest(router, request) {
1409
+ const method = toMethodName(request.method, MethodName.GET);
1351
1410
  const req = createRequest({
1352
1411
  url: request.path,
1353
- method: request.method,
1412
+ method,
1354
1413
  body: request.body,
1355
1414
  headers: request.headers
1356
1415
  });
@@ -1375,52 +1434,45 @@ async function dispatchRawRequest(router, request, options = {}) {
1375
1434
  headers: getHeaders(),
1376
1435
  body: res.body
1377
1436
  });
1378
- try {
1379
- const dispatched = await router.dispatch({
1380
- req,
1381
- res
1382
- }, buildDispatcherMeta({
1383
- path: useRequestPath(req)
1384
- }));
1385
- if (dispatched) {
1386
- return createRawResponse();
1387
- }
1388
- return createRawResponse({
1389
- status: 404
1390
- });
1391
- } catch (e) {
1392
- if (options.throwOnError) {
1393
- throw e;
1394
- }
1395
- if (isError(e)) {
1396
- return createRawResponse({
1397
- status: e.statusCode,
1398
- statusMessage: e.statusMessage
1399
- });
1400
- }
1437
+ const event = new DispatchEvent({
1438
+ request: req,
1439
+ response: res,
1440
+ path: request.path,
1441
+ method
1442
+ });
1443
+ await router.dispatch(event);
1444
+ if (event.dispatched) {
1445
+ return createRawResponse();
1446
+ }
1447
+ if (event.error) {
1401
1448
  return createRawResponse({
1402
- status: 500
1449
+ status: event.error.statusCode,
1450
+ statusMessage: event.error.statusMessage
1403
1451
  });
1404
1452
  }
1453
+ return createRawResponse({
1454
+ status: 404
1455
+ });
1405
1456
  }
1406
1457
  function createRawDispatcher(router) {
1407
1458
  return async (request)=>dispatchRawRequest(router, request);
1408
1459
  }
1409
1460
 
1410
- async function dispatchWebRequest(router, request, options = {}) {
1461
+ async function dispatchWebRequest(router, request) {
1411
1462
  const url = new URL(request.url);
1412
1463
  const headers = {};
1413
1464
  request.headers.forEach((value, key)=>{
1414
1465
  headers[key] = value;
1415
1466
  });
1467
+ const method = toMethodName(request.method, MethodName.GET);
1416
1468
  const res = await dispatchRawRequest(router, {
1417
- method: request.method,
1469
+ method,
1418
1470
  path: url.pathname + url.search,
1419
1471
  headers,
1420
1472
  body: request.body
1421
- }, options);
1473
+ });
1422
1474
  let body;
1423
- if (request.method === MethodName.HEAD || res.status === 304 || res.status === 101 || res.status === 204 || res.status === 205) {
1475
+ if (method === MethodName.HEAD || res.status === 304 || res.status === 101 || res.status === 204 || res.status === 205) {
1424
1476
  body = null;
1425
1477
  } else {
1426
1478
  body = res.body;
@@ -1440,35 +1492,98 @@ var HandlerType;
1440
1492
  HandlerType["CORE"] = "core";
1441
1493
  HandlerType["ERROR"] = "error";
1442
1494
  })(HandlerType || (HandlerType = {}));
1443
-
1444
- function coreHandler(input) {
1445
- if (typeof input === 'function') {
1446
- return {
1447
- type: HandlerType.CORE,
1448
- fn: input
1495
+ const HandlerSymbol = Symbol.for('Handler');
1496
+
1497
+ var HookName;
1498
+ (function(HookName) {
1499
+ HookName["ERROR"] = "error";
1500
+ HookName["DISPATCH_START"] = "dispatchStart";
1501
+ HookName["DISPATCH_END"] = "dispatchEnd";
1502
+ HookName["CHILD_MATCH"] = "childMatch";
1503
+ HookName["CHILD_DISPATCH_BEFORE"] = "childDispatchBefore";
1504
+ HookName["CHILD_DISPATCH_AFTER"] = "childDispatchAfter";
1505
+ })(HookName || (HookName = {}));
1506
+
1507
+ class HookManager {
1508
+ // --------------------------------------------------
1509
+ addListener(name, fn) {
1510
+ this.items[name] = this.items[name] || [];
1511
+ this.items[name].push(fn);
1512
+ return ()=>{
1513
+ this.removeListener(name, fn);
1449
1514
  };
1450
1515
  }
1451
- return {
1452
- type: HandlerType.CORE,
1453
- ...input
1454
- };
1455
- }
1456
-
1457
- function errorHandler(input) {
1458
- if (typeof input === 'function') {
1459
- return {
1460
- type: HandlerType.ERROR,
1461
- fn: input
1462
- };
1516
+ removeListener(name, fn) {
1517
+ if (!this.items[name]) {
1518
+ return;
1519
+ }
1520
+ if (typeof fn === 'undefined') {
1521
+ delete this.items[name];
1522
+ return;
1523
+ }
1524
+ if (typeof fn === 'function') {
1525
+ const index = this.items[name].indexOf(fn);
1526
+ if (index !== -1) {
1527
+ this.items[name].splice(index, 1);
1528
+ }
1529
+ }
1530
+ if (this.items[name].length === 0) {
1531
+ delete this.items[name];
1532
+ }
1533
+ }
1534
+ // --------------------------------------------------
1535
+ /**
1536
+ * @throws RoutupError
1537
+ *
1538
+ * @param name
1539
+ * @param event
1540
+ */ async trigger(name, event) {
1541
+ if (!this.items[name] || this.items[name].length === 0) {
1542
+ return;
1543
+ }
1544
+ try {
1545
+ for(let i = 0; i < this.items[name].length; i++){
1546
+ const hook = this.items[name][i];
1547
+ event.dispatched = await dispatch(event, (next)=>Promise.resolve().then(()=>{
1548
+ event.next = next;
1549
+ return this.triggerListener(name, event, hook);
1550
+ }).catch((err)=>next(err)));
1551
+ event.next = nextPlaceholder;
1552
+ if (event.dispatched) {
1553
+ if (event.error) {
1554
+ event.error = undefined;
1555
+ }
1556
+ return;
1557
+ }
1558
+ }
1559
+ } catch (e) {
1560
+ event.error = e;
1561
+ if (!this.isErrorListenerHook(name)) {
1562
+ await this.trigger(HookName.ERROR, event);
1563
+ if (event.dispatched) {
1564
+ if (event.error) {
1565
+ event.error = undefined;
1566
+ }
1567
+ }
1568
+ }
1569
+ }
1570
+ }
1571
+ triggerListener(name, event, listener) {
1572
+ if (this.isErrorListenerHook(name)) {
1573
+ if (isDispatcherErrorEvent(event)) {
1574
+ return listener(event);
1575
+ }
1576
+ return undefined;
1577
+ }
1578
+ return listener(event);
1579
+ }
1580
+ isErrorListenerHook(input) {
1581
+ return input === HookName.ERROR;
1582
+ }
1583
+ // --------------------------------------------------
1584
+ constructor(){
1585
+ this.items = {};
1463
1586
  }
1464
- return {
1465
- type: HandlerType.ERROR,
1466
- ...input
1467
- };
1468
- }
1469
-
1470
- function isHandler(input) {
1471
- return isObject(input) && typeof input.fn === 'function' && typeof input.type === 'string';
1472
1587
  }
1473
1588
 
1474
1589
  function decodeParam(val) {
@@ -1526,93 +1641,59 @@ function isPath(input) {
1526
1641
  return typeof input === 'string' || input instanceof RegExp;
1527
1642
  }
1528
1643
 
1529
- const LayerSymbol = Symbol.for('Layer');
1530
-
1531
- class Layer {
1644
+ class Handler {
1532
1645
  // --------------------------------------------------
1533
1646
  get type() {
1534
- return this.handler.type;
1647
+ return this.config.type;
1535
1648
  }
1536
1649
  get path() {
1537
- return this.handler.path;
1650
+ return this.config.path;
1538
1651
  }
1539
1652
  get method() {
1540
- return this.handler.method ? this.handler.method.toLowerCase() : undefined;
1653
+ if (this._method || !this.config.method) {
1654
+ return this._method;
1655
+ }
1656
+ this._method = toMethodName(this.config.method);
1657
+ return this._method;
1541
1658
  }
1542
1659
  // --------------------------------------------------
1543
- dispatch(event, meta) {
1660
+ async dispatch(event) {
1544
1661
  if (this.pathMatcher) {
1545
- const pathMatch = this.pathMatcher.exec(meta.path);
1662
+ const pathMatch = this.pathMatcher.exec(event.path);
1546
1663
  if (pathMatch) {
1547
- meta.params = mergeDispatcherMetaParams(meta.params, pathMatch.params);
1664
+ event.params = {
1665
+ ...event.params,
1666
+ ...pathMatch.params
1667
+ };
1548
1668
  }
1549
1669
  }
1550
- setRequestParams(event.req, meta.params);
1551
- setRequestMountPath(event.req, meta.mountPath);
1552
- setRequestRouterPath(event.req, meta.routerPath);
1553
- return new Promise((resolve, reject)=>{
1554
- let handled = false;
1555
- const unsubscribe = ()=>{
1556
- event.res.off('close', onFinished);
1557
- event.res.off('error', onFinished);
1558
- };
1559
- const shutdown = (dispatched, err)=>{
1560
- if (handled) {
1561
- return;
1562
- }
1563
- handled = true;
1564
- unsubscribe();
1565
- if (err) {
1566
- reject(createError(err));
1567
- } else {
1568
- resolve(dispatched);
1569
- }
1570
- };
1571
- const onFinished = (err)=>shutdown(true, err);
1572
- const onNext = (err)=>shutdown(false, err);
1573
- event.res.once('close', onFinished);
1574
- event.res.once('error', onFinished);
1575
- const handle = (data)=>{
1576
- if (typeof data === 'undefined' || handled) {
1577
- return Promise.resolve();
1578
- }
1579
- handled = true;
1580
- unsubscribe();
1581
- return this.sendOutput(event.res, data).then(()=>resolve(true)).catch((e)=>reject(createError(e)));
1582
- };
1583
- try {
1584
- let output;
1585
- if (this.handler.type === HandlerType.ERROR) {
1586
- if (meta.error) {
1587
- output = this.handler.fn(meta.error, event.req, event.res, onNext);
1670
+ await this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, event);
1671
+ if (event.dispatched) {
1672
+ return Promise.resolve();
1673
+ }
1674
+ try {
1675
+ event.dispatched = await dispatch(event, (done)=>{
1676
+ if (this.config.type === HandlerType.ERROR) {
1677
+ if (event.error) {
1678
+ return this.config.fn(event.error, event.request, event.response, done);
1588
1679
  }
1589
1680
  } else {
1590
- output = this.handler.fn(event.req, event.res, onNext);
1681
+ return this.config.fn(event.request, event.response, done);
1591
1682
  }
1592
- if (isPromise(output)) {
1593
- output.then((r)=>handle(r)).catch((e)=>reject(createError(e)));
1594
- return;
1683
+ return undefined;
1684
+ });
1685
+ } catch (e) {
1686
+ if (isError(e)) {
1687
+ event.error = e;
1688
+ await this.hookManager.trigger(HookName.ERROR, event);
1689
+ if (event.dispatched) {
1690
+ event.error = undefined;
1691
+ } else {
1692
+ throw e;
1595
1693
  }
1596
- Promise.resolve().then(()=>handle(output)).catch((e)=>reject(createError(e)));
1597
- } catch (error) {
1598
- onNext(error);
1599
1694
  }
1600
- });
1601
- }
1602
- sendOutput(res, input) {
1603
- if (input instanceof Error) {
1604
- return Promise.reject(createError(input));
1605
- }
1606
- if (isStream(input)) {
1607
- return sendStream(res, input);
1608
1695
  }
1609
- if (isWebBlob(input)) {
1610
- return sendWebBlob(res, input);
1611
- }
1612
- if (isWebResponse(input)) {
1613
- return sendWebResponse(res, input);
1614
- }
1615
- return send(res, input);
1696
+ return this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, event);
1616
1697
  }
1617
1698
  // --------------------------------------------------
1618
1699
  matchPath(path) {
@@ -1621,30 +1702,81 @@ class Layer {
1621
1702
  }
1622
1703
  return this.pathMatcher.test(path);
1623
1704
  }
1705
+ setPath(path) {
1706
+ if (typeof path === 'string') {
1707
+ path = withLeadingSlash(path);
1708
+ }
1709
+ this.config.path = path;
1710
+ if (typeof path === 'undefined') {
1711
+ this.pathMatcher = undefined;
1712
+ return;
1713
+ }
1714
+ this.pathMatcher = new PathMatcher(path, {
1715
+ end: !!this.config.method
1716
+ });
1717
+ }
1718
+ // --------------------------------------------------
1624
1719
  matchMethod(method) {
1625
- if (!this.method) {
1626
- return true;
1720
+ return !this.method || method === this.method || method === MethodName.HEAD && this.method === MethodName.GET;
1721
+ }
1722
+ setMethod(input) {
1723
+ const method = toMethodName(input);
1724
+ this.config.method = method;
1725
+ this._method = method;
1726
+ }
1727
+ // --------------------------------------------------
1728
+ mountHooks() {
1729
+ if (this.config.onBefore) {
1730
+ this.hookManager.addListener(HookName.CHILD_DISPATCH_BEFORE, this.config.onBefore);
1627
1731
  }
1628
- const name = method.toLowerCase();
1629
- if (name === this.method) {
1630
- return true;
1732
+ if (this.config.onAfter) {
1733
+ this.hookManager.addListener(HookName.CHILD_DISPATCH_AFTER, this.config.onAfter);
1734
+ }
1735
+ if (this.config.onError) {
1736
+ this.hookManager.addListener(HookName.ERROR, this.config.onError);
1631
1737
  }
1632
- return name === MethodName.HEAD && this.method === MethodName.GET;
1633
1738
  }
1634
1739
  // --------------------------------------------------
1635
1740
  constructor(handler){
1636
- this['@instanceof'] = LayerSymbol;
1637
- this.handler = handler;
1638
- if (handler.path) {
1639
- this.pathMatcher = new PathMatcher(handler.path, {
1640
- end: !!handler.method
1641
- });
1642
- }
1741
+ this['@instanceof'] = HandlerSymbol;
1742
+ this.config = handler;
1743
+ this.hookManager = new HookManager();
1744
+ this.mountHooks();
1745
+ this.setPath(handler.path);
1746
+ }
1747
+ }
1748
+
1749
+ function coreHandler(input) {
1750
+ if (typeof input === 'function') {
1751
+ return new Handler({
1752
+ type: HandlerType.CORE,
1753
+ fn: input
1754
+ });
1755
+ }
1756
+ return new Handler({
1757
+ type: HandlerType.CORE,
1758
+ ...input
1759
+ });
1760
+ }
1761
+
1762
+ function errorHandler(input) {
1763
+ if (typeof input === 'function') {
1764
+ return new Handler({
1765
+ type: HandlerType.ERROR,
1766
+ fn: input
1767
+ });
1643
1768
  }
1769
+ return new Handler({
1770
+ type: HandlerType.ERROR,
1771
+ ...input
1772
+ });
1644
1773
  }
1645
1774
 
1646
- function isLayerInstance(input) {
1647
- return isInstance(input, LayerSymbol);
1775
+ function isHandlerConfig(input) {
1776
+ return isObject(input) && typeof input.fn === 'function' && typeof input.type === 'string';
1777
+ }
1778
+ function isHandler(input) {
1779
+ return isInstance(input, HandlerSymbol);
1648
1780
  }
1649
1781
 
1650
1782
  function isPlugin(input) {
@@ -1654,10 +1786,7 @@ function isPlugin(input) {
1654
1786
  if (typeof input.name !== 'undefined' && typeof input.name !== 'string') {
1655
1787
  return false;
1656
1788
  }
1657
- if (typeof input.install !== 'function' || input.install.length !== 1) {
1658
- return false;
1659
- }
1660
- return typeof input.version === 'undefined' || typeof input.version === 'string';
1789
+ return typeof input.install === 'function' && input.install.length === 1;
1661
1790
  }
1662
1791
 
1663
1792
  function transformRouterOptions(input) {
@@ -1671,6 +1800,15 @@ function transformRouterOptions(input) {
1671
1800
  }
1672
1801
 
1673
1802
  const RouterSymbol = Symbol.for('Router');
1803
+ var RouterPipelineStep;
1804
+ (function(RouterPipelineStep) {
1805
+ RouterPipelineStep[RouterPipelineStep["START"] = 0] = "START";
1806
+ RouterPipelineStep[RouterPipelineStep["LOOKUP"] = 1] = "LOOKUP";
1807
+ RouterPipelineStep[RouterPipelineStep["CHILD_BEFORE"] = 2] = "CHILD_BEFORE";
1808
+ RouterPipelineStep[RouterPipelineStep["CHILD_DISPATCH"] = 3] = "CHILD_DISPATCH";
1809
+ RouterPipelineStep[RouterPipelineStep["CHILD_AFTER"] = 4] = "CHILD_AFTER";
1810
+ RouterPipelineStep[RouterPipelineStep["FINISH"] = 5] = "FINISH";
1811
+ })(RouterPipelineStep || (RouterPipelineStep = {}));
1674
1812
 
1675
1813
  let nextId = 0;
1676
1814
  function generateRouterID() {
@@ -1682,220 +1820,252 @@ function isRouterInstance(input) {
1682
1820
 
1683
1821
  class Router {
1684
1822
  // --------------------------------------------------
1823
+ matchPath(path) {
1824
+ if (this.pathMatcher) {
1825
+ return this.pathMatcher.test(path);
1826
+ }
1827
+ return true;
1828
+ }
1685
1829
  setPath(value) {
1686
- if (value === '/' || !isPath(value)) {
1830
+ if (value === '/' || typeof value === 'undefined') {
1831
+ this.pathMatcher = undefined;
1687
1832
  return;
1688
1833
  }
1689
- let path;
1690
1834
  if (typeof value === 'string') {
1691
- path = withLeadingSlash(withoutTrailingSlash(`${value}`));
1835
+ this.pathMatcher = new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), {
1836
+ end: false
1837
+ });
1692
1838
  } else {
1693
- path = value;
1839
+ this.pathMatcher = new PathMatcher(value, {
1840
+ end: false
1841
+ });
1694
1842
  }
1695
- this.pathMatcher = new PathMatcher(path, {
1696
- end: false
1697
- });
1698
1843
  }
1699
1844
  // --------------------------------------------------
1700
- matchPath(path) {
1701
- if (this.pathMatcher) {
1702
- return this.pathMatcher.test(path);
1845
+ async executePipelineStep(context) {
1846
+ switch(context.step){
1847
+ case RouterPipelineStep.START:
1848
+ {
1849
+ return this.executePipelineStepStart(context);
1850
+ }
1851
+ case RouterPipelineStep.LOOKUP:
1852
+ {
1853
+ return this.executePipelineStepLookup(context);
1854
+ }
1855
+ case RouterPipelineStep.CHILD_BEFORE:
1856
+ {
1857
+ return this.executePipelineStepChildBefore(context);
1858
+ }
1859
+ case RouterPipelineStep.CHILD_DISPATCH:
1860
+ {
1861
+ return this.executePipelineStepChildDispatch(context);
1862
+ }
1863
+ case RouterPipelineStep.CHILD_AFTER:
1864
+ {
1865
+ return this.executePipelineStepChildAfter(context);
1866
+ }
1867
+ case RouterPipelineStep.FINISH:
1868
+ default:
1869
+ {
1870
+ return this.executePipelineStepFinish(context);
1871
+ }
1703
1872
  }
1704
- return true;
1705
1873
  }
1706
- // --------------------------------------------------
1707
- async dispatch(event, meta) {
1708
- const allowedMethods = [];
1709
- if (this.pathMatcher) {
1710
- const output = this.pathMatcher.exec(meta.path);
1711
- if (typeof output !== 'undefined') {
1712
- meta.mountPath = cleanDoubleSlashes(`${meta.mountPath}/${output.path}`);
1713
- if (meta.path === output.path) {
1714
- meta.path = '/';
1715
- } else {
1716
- meta.path = withLeadingSlash(meta.path.substring(output.path.length));
1717
- }
1718
- meta.params = {
1719
- ...meta.params,
1720
- ...output.params
1721
- };
1874
+ async executePipelineStepStart(context) {
1875
+ return this.hookManager.trigger(HookName.DISPATCH_START, context.event).then(()=>{
1876
+ if (context.event.dispatched) {
1877
+ context.step = RouterPipelineStep.FINISH;
1878
+ } else {
1879
+ context.step++;
1722
1880
  }
1723
- }
1724
- meta.routerPath.push(this.id);
1725
- let err;
1726
- let item;
1727
- let itemMeta;
1728
- let match = false;
1729
- for(let i = 0; i < this.stack.length; i++){
1730
- item = this.stack[i];
1731
- if (isLayerInstance(item)) {
1732
- if (item.type !== HandlerType.ERROR && err) {
1733
- continue;
1881
+ return this.executePipelineStep(context);
1882
+ });
1883
+ }
1884
+ async executePipelineStepLookup(context) {
1885
+ if (context.event.dispatched || context.stackIndex >= this.stack.length) {
1886
+ context.step = RouterPipelineStep.FINISH;
1887
+ return this.executePipelineStep(context);
1888
+ }
1889
+ let match;
1890
+ const item = this.stack[context.stackIndex];
1891
+ if (isHandler(item)) {
1892
+ if (context.event.error && item.type === HandlerType.CORE || !context.event.error && item.type === HandlerType.ERROR) {
1893
+ context.stackIndex++;
1894
+ return this.executePipelineStepLookup(context);
1895
+ }
1896
+ match = item.matchPath(context.event.path);
1897
+ if (match) {
1898
+ if (item.method) {
1899
+ context.event.methodsAllowed.push(item.method);
1734
1900
  }
1735
- match = item.matchPath(meta.path);
1736
- if (match && event.req.method) {
1737
- if (!item.matchMethod(event.req.method)) {
1738
- match = false;
1739
- }
1740
- if (item.method) {
1741
- allowedMethods.push(item.method);
1901
+ if (item.matchMethod(context.event.method)) {
1902
+ await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1903
+ if (context.event.dispatched) {
1904
+ context.step = RouterPipelineStep.FINISH;
1905
+ } else {
1906
+ context.step++;
1742
1907
  }
1908
+ return this.executePipelineStep(context);
1743
1909
  }
1744
- } else if (isRouterInstance(item)) {
1745
- match = item.matchPath(meta.path);
1746
1910
  }
1747
- if (!match) {
1748
- continue;
1911
+ context.stackIndex++;
1912
+ return this.executePipelineStepLookup(context);
1913
+ }
1914
+ match = item.matchPath(context.event.path);
1915
+ if (match) {
1916
+ await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1917
+ if (context.event.dispatched) {
1918
+ context.step = RouterPipelineStep.FINISH;
1919
+ } else {
1920
+ context.step++;
1749
1921
  }
1750
- itemMeta = cloneDispatcherMeta(meta);
1751
- if (err) {
1752
- itemMeta.error = err;
1922
+ return this.executePipelineStep(context);
1923
+ }
1924
+ context.stackIndex++;
1925
+ return this.executePipelineStepLookup(context);
1926
+ }
1927
+ async executePipelineStepChildBefore(context) {
1928
+ return this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, context.event).then(()=>{
1929
+ if (context.event.dispatched) {
1930
+ context.step = RouterPipelineStep.FINISH;
1931
+ } else {
1932
+ context.step++;
1753
1933
  }
1754
- try {
1755
- const dispatched = await item.dispatch(event, itemMeta);
1756
- if (dispatched) {
1757
- return true;
1758
- }
1759
- } catch (e) {
1760
- if (isError(e)) {
1761
- err = e;
1762
- }
1934
+ return this.executePipelineStep(context);
1935
+ });
1936
+ }
1937
+ async executePipelineStepChildAfter(context) {
1938
+ return this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, context.event).then(()=>{
1939
+ if (context.event.dispatched) {
1940
+ context.step = RouterPipelineStep.FINISH;
1941
+ } else {
1942
+ context.step = RouterPipelineStep.LOOKUP;
1763
1943
  }
1944
+ return this.executePipelineStep(context);
1945
+ });
1946
+ }
1947
+ async executePipelineStepChildDispatch(context) {
1948
+ if (context.event.dispatched || typeof this.stack[context.stackIndex] === 'undefined') {
1949
+ context.step = RouterPipelineStep.FINISH;
1950
+ return this.executePipelineStep(context);
1764
1951
  }
1765
- if (err) {
1766
- throw err;
1952
+ try {
1953
+ await this.stack[context.stackIndex].dispatch(context.event);
1954
+ } catch (e) {
1955
+ context.event.error = e;
1956
+ await this.hookManager.trigger(HookName.ERROR, context.event);
1767
1957
  }
1768
- if (event.req.method && event.req.method.toLowerCase() === MethodName.OPTIONS) {
1769
- if (allowedMethods.indexOf(MethodName.GET) !== -1) {
1770
- allowedMethods.push(MethodName.HEAD);
1771
- }
1772
- distinctArray(allowedMethods);
1773
- const options = allowedMethods.map((key)=>key.toUpperCase()).join(',');
1774
- if (!isResponseGone(event.res)) {
1775
- event.res.setHeader(HeaderName.ALLOW, options);
1776
- await send(event.res, options);
1958
+ context.stackIndex++;
1959
+ context.step++;
1960
+ return this.executePipelineStep(context);
1961
+ }
1962
+ async executePipelineStepFinish(context) {
1963
+ if (context.event.error || context.event.dispatched) {
1964
+ return this.hookManager.trigger(HookName.DISPATCH_END, context.event);
1965
+ }
1966
+ if (!context.event.dispatched && context.event.routerPath.length === 1 && context.event.method && context.event.method === MethodName.OPTIONS) {
1967
+ if (context.event.methodsAllowed.indexOf(MethodName.GET) !== -1) {
1968
+ context.event.methodsAllowed.push(MethodName.HEAD);
1777
1969
  }
1778
- return true;
1970
+ distinctArray(context.event.methodsAllowed);
1971
+ const options = context.event.methodsAllowed.map((key)=>key.toUpperCase()).join(',');
1972
+ context.event.response.setHeader(HeaderName.ALLOW, options);
1973
+ await send(context.event.response, options);
1974
+ context.event.dispatched = true;
1779
1975
  }
1780
- return false;
1976
+ return this.hookManager.trigger(HookName.DISPATCH_END, context.event);
1781
1977
  }
1782
- delete(path, handler) {
1783
- if (isPath(path)) {
1784
- this.use({
1785
- ...handler,
1786
- method: MethodName.DELETE,
1787
- path
1788
- });
1789
- return this;
1978
+ // --------------------------------------------------
1979
+ async dispatch(event) {
1980
+ if (this.pathMatcher) {
1981
+ const output = this.pathMatcher.exec(event.path);
1982
+ if (typeof output !== 'undefined') {
1983
+ event.mountPath = cleanDoubleSlashes(`${event.mountPath}/${output.path}`);
1984
+ if (event.path === output.path) {
1985
+ event.path = '/';
1986
+ } else {
1987
+ event.path = withLeadingSlash(event.path.substring(output.path.length));
1988
+ }
1989
+ event.params = {
1990
+ ...event.params,
1991
+ ...output.params
1992
+ };
1993
+ }
1790
1994
  }
1791
- this.use({
1792
- ...path,
1793
- method: MethodName.DELETE
1995
+ const context = {
1996
+ step: RouterPipelineStep.START,
1997
+ event,
1998
+ stackIndex: 0
1999
+ };
2000
+ event.routerPath.push(this.id);
2001
+ return this.executePipelineStepStart(context).then(()=>{
2002
+ context.event.routerPath.pop();
1794
2003
  });
2004
+ }
2005
+ delete(...input) {
2006
+ this.useForMethod(MethodName.DELETE, ...input);
1795
2007
  return this;
1796
2008
  }
1797
- get(path, handler) {
1798
- if (isPath(path)) {
1799
- this.use({
1800
- ...handler,
1801
- method: MethodName.GET,
1802
- path
1803
- });
1804
- return this;
1805
- }
1806
- this.use({
1807
- ...path,
1808
- method: MethodName.GET
1809
- });
2009
+ get(...input) {
2010
+ this.useForMethod(MethodName.GET, ...input);
1810
2011
  return this;
1811
2012
  }
1812
- post(path, handler) {
1813
- if (isPath(path)) {
1814
- this.use({
1815
- ...handler,
1816
- method: MethodName.POST,
1817
- path
1818
- });
1819
- return this;
1820
- }
1821
- this.use({
1822
- ...path,
1823
- method: MethodName.POST
1824
- });
2013
+ post(...input) {
2014
+ this.useForMethod(MethodName.POST, ...input);
1825
2015
  return this;
1826
2016
  }
1827
- put(path, handler) {
1828
- if (isPath(path)) {
1829
- this.use({
1830
- ...handler,
1831
- method: MethodName.PUT,
1832
- path
1833
- });
1834
- return this;
1835
- }
1836
- this.use({
1837
- ...path,
1838
- method: MethodName.PUT
1839
- });
2017
+ put(...input) {
2018
+ this.useForMethod(MethodName.PUT, ...input);
1840
2019
  return this;
1841
2020
  }
1842
- patch(path, handler) {
1843
- if (isPath(path)) {
1844
- this.use({
1845
- ...handler,
1846
- method: MethodName.PATCH,
1847
- path
1848
- });
1849
- return this;
1850
- }
1851
- this.use({
1852
- ...path,
1853
- method: MethodName.PATCH
1854
- });
2021
+ patch(...input) {
2022
+ this.useForMethod(MethodName.PATCH, ...input);
1855
2023
  return this;
1856
2024
  }
1857
- head(path, handler) {
1858
- if (isPath(path)) {
1859
- this.use({
1860
- ...handler,
1861
- method: MethodName.HEAD,
1862
- path
1863
- });
1864
- return this;
1865
- }
1866
- this.use({
1867
- ...path,
1868
- method: MethodName.HEAD
1869
- });
2025
+ head(...input) {
2026
+ this.useForMethod(MethodName.HEAD, ...input);
1870
2027
  return this;
1871
2028
  }
1872
- options(path, handler) {
1873
- if (isPath(path)) {
1874
- this.use({
1875
- ...handler,
1876
- method: MethodName.OPTIONS,
1877
- path
1878
- });
1879
- return this;
1880
- }
1881
- this.use({
1882
- ...path,
1883
- method: MethodName.OPTIONS
1884
- });
2029
+ options(...input) {
2030
+ this.useForMethod(MethodName.OPTIONS, ...input);
1885
2031
  return this;
1886
2032
  }
1887
- use(...input) {
1888
- const modifyPath = (input)=>{
1889
- if (typeof input === 'string') {
1890
- return withLeadingSlash(input);
2033
+ // --------------------------------------------------
2034
+ useForMethod(method, ...input) {
2035
+ let path;
2036
+ for(let i = 0; i < input.length; i++){
2037
+ const element = input[i];
2038
+ if (isPath(element)) {
2039
+ path = element;
2040
+ continue;
1891
2041
  }
1892
- return input;
1893
- };
2042
+ if (isHandlerConfig(element)) {
2043
+ if (path) {
2044
+ element.path = path;
2045
+ }
2046
+ element.method = method;
2047
+ this.use(element);
2048
+ continue;
2049
+ }
2050
+ if (isHandler(element)) {
2051
+ if (path) {
2052
+ element.setPath(path);
2053
+ }
2054
+ element.setMethod(method);
2055
+ this.use(element);
2056
+ }
2057
+ }
2058
+ }
2059
+ use(...input) {
1894
2060
  let path;
1895
2061
  for(let i = 0; i < input.length; i++){
1896
2062
  const item = input[i];
1897
2063
  if (isPath(item)) {
1898
- path = modifyPath(item);
2064
+ if (typeof item === 'string') {
2065
+ path = withLeadingSlash(item);
2066
+ } else {
2067
+ path = item;
2068
+ }
1899
2069
  continue;
1900
2070
  }
1901
2071
  if (isRouterInstance(item)) {
@@ -1905,9 +2075,15 @@ class Router {
1905
2075
  this.stack.push(item);
1906
2076
  continue;
1907
2077
  }
2078
+ if (isHandlerConfig(item)) {
2079
+ item.path = path || item.path;
2080
+ this.stack.push(new Handler(item));
2081
+ continue;
2082
+ }
1908
2083
  if (isHandler(item)) {
1909
- item.path = path || modifyPath(item.path);
1910
- this.stack.push(new Layer(item));
2084
+ item.setPath(path || item.path);
2085
+ this.stack.push(item);
2086
+ continue;
1911
2087
  }
1912
2088
  if (isPlugin(item)) {
1913
2089
  if (path) {
@@ -1924,15 +2100,26 @@ class Router {
1924
2100
  // --------------------------------------------------
1925
2101
  install(plugin, context = {}) {
1926
2102
  const name = context.name || plugin.name;
2103
+ const router = new Router({
2104
+ name
2105
+ });
2106
+ plugin.install(router);
1927
2107
  if (context.path) {
1928
- const router = new Router({
1929
- name
1930
- });
1931
- plugin.install(router);
1932
2108
  this.use(context.path, router);
2109
+ } else {
2110
+ this.use(router);
2111
+ }
2112
+ return this;
2113
+ }
2114
+ on(name, fn) {
2115
+ return this.hookManager.addListener(name, fn);
2116
+ }
2117
+ off(name, fn) {
2118
+ if (typeof fn === 'undefined') {
2119
+ this.hookManager.removeListener(name);
1933
2120
  return this;
1934
2121
  }
1935
- plugin.install(this);
2122
+ this.hookManager.removeListener(name, fn);
1936
2123
  return this;
1937
2124
  }
1938
2125
  // --------------------------------------------------
@@ -1945,10 +2132,11 @@ class Router {
1945
2132
  */ this.stack = [];
1946
2133
  this.id = generateRouterID();
1947
2134
  this.name = options.name;
2135
+ this.hookManager = new HookManager();
1948
2136
  this.setPath(options.path);
1949
2137
  setRouterOptions(this.id, transformRouterOptions(options));
1950
2138
  }
1951
2139
  }
1952
2140
 
1953
- export { ErrorProxy, HandlerType, HeaderName, Layer, MethodName, PathMatcher, Router, appendResponseHeader, appendResponseHeaderDirective, buildDispatcherMeta, cloneDispatcherMeta, cloneDispatcherMetaParams, coreHandler, createError, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, errorHandler, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, isError, isHandler, isLayerInstance, isPath, isPlugin, isRequestCacheable, isResponseGone, isRouterInstance, matchRequestContentType, mergeDispatcherMetaParams, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestRouterPath, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseHeaderAttachment, setResponseHeaderContentType, unsetRequestEnv, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestRouterPath };
2141
+ export { DispatchErrorEvent, DispatchEvent, Handler, HandlerSymbol, HandlerType, HeaderName, MethodName, PathMatcher, Router, RoutupError, appendResponseHeader, appendResponseHeaderDirective, coreHandler, createError, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatch, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, errorHandler, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, isDispatcherErrorEvent, isError, isHandler, isHandlerConfig, isPath, isPlugin, isRequestCacheable, isResponseGone, matchRequestContentType, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestRouterPath, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseHeaderAttachment, setResponseHeaderContentType, transformHeaderToTuples, transformHeadersToTuples, unsetRequestEnv, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestRouterPath };
1954
2142
  //# sourceMappingURL=index.mjs.map