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