routup 5.0.0-beta.7 → 5.0.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.
@@ -924,18 +924,43 @@ function isPath(input) {
924
924
  return typeof input === "string";
925
925
  }
926
926
  //#endregion
927
+ //#region src/handler/utils.ts
928
+ /**
929
+ * Build a `PathMatcher` for a handler-side path.
930
+ *
931
+ * Returns `undefined` when no path is supplied. The `end` flag controls
932
+ * whether the matcher requires a full match (`true` for method handlers
933
+ * matching exact routes) or accepts a prefix (`false` for middleware).
934
+ */
935
+ function buildHandlerPathMatcher(path, end) {
936
+ if (typeof path === "undefined") return;
937
+ return new PathMatcher(typeof path === "string" ? withLeadingSlash(path) : path, { end });
938
+ }
939
+ /**
940
+ * Match a request method against a handler's bound method.
941
+ *
942
+ * - When the handler has no method bound, matches every request method.
943
+ * - Otherwise matches when the request method is the same.
944
+ * - HEAD requests additionally match GET handlers.
945
+ */
946
+ function matchHandlerMethod(handlerMethod, requestMethod) {
947
+ return !handlerMethod || requestMethod === handlerMethod || requestMethod === MethodName.HEAD && handlerMethod === MethodName.GET;
948
+ }
949
+ //#endregion
927
950
  //#region src/handler/module.ts
928
951
  var Handler = class {
929
952
  "@instanceof" = HandlerSymbol;
930
953
  config;
931
954
  hookManager;
932
955
  pathMatcher;
933
- _method;
956
+ method;
934
957
  constructor(handler) {
935
958
  this.config = handler;
936
959
  this.hookManager = new HookManager();
937
960
  this.mountHooks();
938
- this.setPath(handler.path);
961
+ if (typeof handler.path === "string") this.config.path = withLeadingSlash(handler.path);
962
+ this.pathMatcher = buildHandlerPathMatcher(this.config.path, !!this.config.method);
963
+ this.method = this.config.method ? toMethodName(this.config.method) : void 0;
939
964
  }
940
965
  get type() {
941
966
  return this.config.type;
@@ -943,11 +968,6 @@ var Handler = class {
943
968
  get path() {
944
969
  return this.config.path;
945
970
  }
946
- get method() {
947
- if (this._method || !this.config.method) return this._method;
948
- this._method = toMethodName(this.config.method);
949
- return this._method;
950
- }
951
971
  async dispatch(event) {
952
972
  if (this.pathMatcher) {
953
973
  const pathMatch = this.pathMatcher.exec(event.path);
@@ -1006,23 +1026,6 @@ var Handler = class {
1006
1026
  if (!this.pathMatcher) return true;
1007
1027
  return this.pathMatcher.test(path);
1008
1028
  }
1009
- setPath(path) {
1010
- if (typeof path === "string") path = withLeadingSlash(path);
1011
- this.config.path = path;
1012
- if (typeof path === "undefined") {
1013
- this.pathMatcher = void 0;
1014
- return;
1015
- }
1016
- this.pathMatcher = new PathMatcher(path, { end: !!this.config.method });
1017
- }
1018
- matchMethod(method) {
1019
- return !this.method || method === this.method || method === MethodName.HEAD && this.method === MethodName.GET;
1020
- }
1021
- setMethod(input) {
1022
- const method = toMethodName(input);
1023
- this.config.method = method;
1024
- this._method = method;
1025
- }
1026
1029
  /**
1027
1030
  * Resolve a handler's return value into the final value handed to `toResponse`.
1028
1031
  *
@@ -1461,12 +1464,26 @@ const RouterPipelineStep = {
1461
1464
  CHILD_AFTER: 4,
1462
1465
  FINISH: 5
1463
1466
  };
1467
+ const RouterStackEntryType = {
1468
+ ROUTER: "router",
1469
+ HANDLER: "handler"
1470
+ };
1464
1471
  //#endregion
1465
1472
  //#region src/router/utils.ts
1466
1473
  function isRouterInstance(input) {
1467
1474
  return isInstance(input, RouterSymbol);
1468
1475
  }
1469
1476
  /**
1477
+ * Build a non-terminal `PathMatcher` for a router mount path.
1478
+ *
1479
+ * Returns `undefined` when the path is the root (`/`) or omitted entirely —
1480
+ * a router mounted at the root has no intrinsic path filter.
1481
+ */
1482
+ function buildRouterPathMatcher(value) {
1483
+ if (value === "/" || typeof value === "undefined") return;
1484
+ return new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), { end: false });
1485
+ }
1486
+ /**
1470
1487
  * Check if the request accepts JSON responses.
1471
1488
  * Matches application/json and +json suffixes (e.g. application/vnd.api+json).
1472
1489
  * Returns true if no Accept header is present (API-first default).
@@ -1485,7 +1502,9 @@ var Router = class Router {
1485
1502
  */
1486
1503
  name;
1487
1504
  /**
1488
- * Array of mounted layers, routes & routers.
1505
+ * Array of mounted layers, routes & routers, each tagged by kind so the
1506
+ * dispatch loop can discriminate without `isRouterInstance`/`isHandler`
1507
+ * runtime checks.
1489
1508
  *
1490
1509
  * @protected
1491
1510
  */
@@ -1516,19 +1535,12 @@ var Router = class Router {
1516
1535
  this.name = input.name;
1517
1536
  this.hookManager = new HookManager();
1518
1537
  this._options = normalizeRouterOptions(input);
1519
- this.setPath(input.path);
1538
+ this.pathMatcher = buildRouterPathMatcher(input.path);
1520
1539
  }
1521
1540
  matchPath(path) {
1522
1541
  if (this.pathMatcher) return this.pathMatcher.test(path);
1523
1542
  return true;
1524
1543
  }
1525
- setPath(value) {
1526
- if (value === "/" || typeof value === "undefined") {
1527
- this.pathMatcher = void 0;
1528
- return;
1529
- }
1530
- this.pathMatcher = new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), { end: false });
1531
- }
1532
1544
  /**
1533
1545
  * Public entry point — creates a DispatcherEvent from the request,
1534
1546
  * runs the pipeline, and returns a Response (with 404/500 fallbacks).
@@ -1611,15 +1623,17 @@ var Router = class Router {
1611
1623
  }
1612
1624
  async executePipelineStepLookup(context) {
1613
1625
  while (!context.event.dispatched && context.stackIndex < this.stack.length) {
1614
- const item = this.stack[context.stackIndex];
1615
- if (isHandler(item)) {
1616
- if (context.event.error && item.type === HandlerType.CORE || !context.event.error && item.type === HandlerType.ERROR) {
1626
+ const entry = this.stack[context.stackIndex];
1627
+ if (entry.type === RouterStackEntryType.HANDLER) {
1628
+ const handler = entry.data;
1629
+ if (context.event.error && handler.type === HandlerType.CORE || !context.event.error && handler.type === HandlerType.ERROR) {
1617
1630
  context.stackIndex++;
1618
1631
  continue;
1619
1632
  }
1620
- if (item.matchPath(context.event.path)) {
1621
- if (item.method) context.event.methodsAllowed.add(item.method);
1622
- if (item.matchMethod(context.event.method)) {
1633
+ if (entry.pathMatcher ? entry.pathMatcher.test(context.event.path) : handler.matchPath(context.event.path)) {
1634
+ const method = entry.method ?? handler.method;
1635
+ if (method) context.event.methodsAllowed.add(method);
1636
+ if (matchHandlerMethod(method, context.event.method)) {
1623
1637
  await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1624
1638
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1625
1639
  else context.step = RouterPipelineStep.CHILD_BEFORE;
@@ -1629,7 +1643,7 @@ var Router = class Router {
1629
1643
  context.stackIndex++;
1630
1644
  continue;
1631
1645
  }
1632
- if (item.matchPath(context.event.path)) {
1646
+ if (entry.pathMatcher ? entry.pathMatcher.test(context.event.path) : entry.data.matchPath(context.event.path)) {
1633
1647
  await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1634
1648
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1635
1649
  else context.step = RouterPipelineStep.CHILD_BEFORE;
@@ -1650,12 +1664,33 @@ var Router = class Router {
1650
1664
  else context.step = RouterPipelineStep.LOOKUP;
1651
1665
  }
1652
1666
  async executePipelineStepChildDispatch(context) {
1653
- if (context.event.dispatched || typeof this.stack[context.stackIndex] === "undefined") {
1667
+ const entry = this.stack[context.stackIndex];
1668
+ if (context.event.dispatched || typeof entry === "undefined") {
1654
1669
  context.step = RouterPipelineStep.FINISH;
1655
1670
  return;
1656
1671
  }
1657
- const item = this.stack[context.stackIndex];
1658
1672
  const { event } = context;
1673
+ const savedPath = event.path;
1674
+ const savedMountPath = event.mountPath;
1675
+ const savedParams = event.params;
1676
+ if (entry.type === RouterStackEntryType.ROUTER && entry.pathMatcher) {
1677
+ const output = entry.pathMatcher.exec(event.path);
1678
+ if (typeof output !== "undefined") {
1679
+ event.mountPath = cleanDoubleSlashes(`${event.mountPath}/${output.path}`);
1680
+ if (event.path === output.path) event.path = "/";
1681
+ else event.path = withLeadingSlash(event.path.substring(output.path.length));
1682
+ event.params = {
1683
+ ...event.params,
1684
+ ...output.params
1685
+ };
1686
+ }
1687
+ } else if (entry.type === RouterStackEntryType.HANDLER && entry.pathMatcher) {
1688
+ const output = entry.pathMatcher.exec(event.path);
1689
+ if (typeof output !== "undefined") event.params = {
1690
+ ...event.params,
1691
+ ...output.params
1692
+ };
1693
+ }
1659
1694
  try {
1660
1695
  event.setNext(async (error) => {
1661
1696
  if (error) event.error = createError(error);
@@ -1668,7 +1703,7 @@ var Router = class Router {
1668
1703
  await this.executePipelineStep(nextContext);
1669
1704
  return nextContext.response;
1670
1705
  });
1671
- const response = await item.dispatch(event);
1706
+ const response = await entry.data.dispatch(event);
1672
1707
  if (response) {
1673
1708
  context.response = response;
1674
1709
  event.dispatched = true;
@@ -1677,6 +1712,11 @@ var Router = class Router {
1677
1712
  event.error = createError(e);
1678
1713
  await this.hookManager.trigger(HookName.ERROR, event);
1679
1714
  }
1715
+ if (!event.dispatched) {
1716
+ event.path = savedPath;
1717
+ event.mountPath = savedMountPath;
1718
+ event.params = savedParams;
1719
+ }
1680
1720
  context.stackIndex++;
1681
1721
  context.step = RouterPipelineStep.CHILD_AFTER;
1682
1722
  }
@@ -1696,6 +1736,9 @@ var Router = class Router {
1696
1736
  return this.hookManager.trigger(HookName.RESPONSE, context.event);
1697
1737
  }
1698
1738
  async dispatch(event) {
1739
+ const savedPath = event.path;
1740
+ const savedMountPath = event.mountPath;
1741
+ const savedParams = event.params;
1699
1742
  if (this.pathMatcher) {
1700
1743
  const output = this.pathMatcher.exec(event.path);
1701
1744
  if (typeof output !== "undefined") {
@@ -1721,6 +1764,11 @@ var Router = class Router {
1721
1764
  await this.executePipelineStep(context);
1722
1765
  } finally {
1723
1766
  event.routerPath.pop();
1767
+ if (!event.dispatched) {
1768
+ event.path = savedPath;
1769
+ event.mountPath = savedMountPath;
1770
+ event.params = savedParams;
1771
+ }
1724
1772
  }
1725
1773
  return context.response;
1726
1774
  }
@@ -1759,17 +1807,20 @@ var Router = class Router {
1759
1807
  path = element;
1760
1808
  continue;
1761
1809
  }
1762
- if (isHandlerOptions(element)) {
1763
- if (path) element.path = path;
1764
- element.method = method;
1765
- this.use(element);
1766
- continue;
1767
- }
1768
- if (isHandler(element)) {
1769
- if (path) element.setPath(path);
1770
- element.setMethod(method);
1771
- this.use(element);
1772
- }
1810
+ let handler;
1811
+ if (isHandlerOptions(element)) handler = new Handler({
1812
+ ...element,
1813
+ method,
1814
+ path: path ?? element.path
1815
+ });
1816
+ else if (isHandler(element)) handler = element;
1817
+ else continue;
1818
+ this.stack.push({
1819
+ type: RouterStackEntryType.HANDLER,
1820
+ data: handler,
1821
+ method,
1822
+ pathMatcher: buildHandlerPathMatcher(path ?? handler.path, true)
1823
+ });
1773
1824
  }
1774
1825
  }
1775
1826
  use(...input) {
@@ -1780,18 +1831,30 @@ var Router = class Router {
1780
1831
  continue;
1781
1832
  }
1782
1833
  if (isRouterInstance(item)) {
1783
- if (path) item.setPath(path);
1784
- this.stack.push(item);
1834
+ this.stack.push({
1835
+ type: RouterStackEntryType.ROUTER,
1836
+ data: item,
1837
+ pathMatcher: buildRouterPathMatcher(path)
1838
+ });
1785
1839
  continue;
1786
1840
  }
1787
1841
  if (isHandlerOptions(item)) {
1788
- item.path = path || item.path;
1789
- this.stack.push(new Handler(item));
1842
+ const handler = new Handler({
1843
+ ...item,
1844
+ path: path ?? item.path
1845
+ });
1846
+ this.stack.push({
1847
+ type: RouterStackEntryType.HANDLER,
1848
+ data: handler
1849
+ });
1790
1850
  continue;
1791
1851
  }
1792
1852
  if (isHandler(item)) {
1793
- item.setPath(path || item.path);
1794
- this.stack.push(item);
1853
+ this.stack.push({
1854
+ type: RouterStackEntryType.HANDLER,
1855
+ data: item,
1856
+ pathMatcher: buildHandlerPathMatcher(path, !!item.method)
1857
+ });
1795
1858
  continue;
1796
1859
  }
1797
1860
  if (isPlugin(item)) if (path) this.install(item, { path });
@@ -1834,6 +1897,6 @@ var Router = class Router {
1834
1897
  }
1835
1898
  };
1836
1899
  //#endregion
1837
- export { appendResponseHeader as $, Handler as A, getRequestAcceptableContentType as B, fromWebHandler as C, fromNodeMiddleware as D, fromNodeHandler as E, DispatcherEvent as F, sendCreated as G, useRequestNegotiator as H, RoutupEvent as I, createError as J, sendAccepted as K, sendStream as L, PathMatcher as M, HandlerSymbol as N, defineErrorHandler as O, HandlerType as P, setResponseContentTypeByFileName as Q, sendRedirect as R, isHandlerOptions as S, isWebHandlerProvider as T, getRequestHeader as U, getRequestAcceptableContentTypes as V, sendFile as W, setResponseHeaderContentType as X, isError as Y, setResponseHeaderAttachment as Z, getRequestAcceptableEncodings as _, PluginInstallError as a, setResponseCacheHeaders as at, isRequestCacheable as b, isPluginError as c, getRequestIP as d, appendResponseHeaderDirective as et, getRequestHostName as f, getRequestAcceptableEncoding as g, getRequestAcceptableLanguages as h, PluginNotInstalledError as i, RoutupError as it, isPath as j, defineCoreHandler as k, PluginErrorCode as l, getRequestAcceptableLanguage as m, normalizeRouterOptions as n, serializeEventStreamMessage as nt, PluginAlreadyInstalledError as o, HeaderName as ot, matchRequestContentType as p, toResponse as q, isPlugin as r, ErrorSymbol as rt, PluginError as s, MethodName as st, Router as t, createEventStream as tt, getRequestProtocol as u, getRequestAcceptableCharset as v, isWebHandler as w, isHandler as x, getRequestAcceptableCharsets as y, sendFormat as z };
1900
+ export { setResponseHeaderAttachment as $, Handler as A, sendRedirect as B, fromWebHandler as C, fromNodeMiddleware as D, fromNodeHandler as E, HandlerSymbol as F, getRequestHeader as G, getRequestAcceptableContentType as H, HandlerType as I, sendAccepted as J, sendFile as K, DispatcherEvent as L, matchHandlerMethod as M, isPath as N, defineErrorHandler as O, PathMatcher as P, setResponseHeaderContentType as Q, RoutupEvent as R, isHandlerOptions as S, isWebHandlerProvider as T, getRequestAcceptableContentTypes as U, sendFormat as V, useRequestNegotiator as W, createError as X, toResponse as Y, isError as Z, getRequestAcceptableEncodings as _, PluginInstallError as a, ErrorSymbol as at, isRequestCacheable as b, isPluginError as c, HeaderName as ct, getRequestIP as d, setResponseContentTypeByFileName as et, getRequestHostName as f, getRequestAcceptableEncoding as g, getRequestAcceptableLanguages as h, PluginNotInstalledError as i, serializeEventStreamMessage as it, buildHandlerPathMatcher as j, defineCoreHandler as k, PluginErrorCode as l, MethodName as lt, getRequestAcceptableLanguage as m, normalizeRouterOptions as n, appendResponseHeaderDirective as nt, PluginAlreadyInstalledError as o, RoutupError as ot, matchRequestContentType as p, sendCreated as q, isPlugin as r, createEventStream as rt, PluginError as s, setResponseCacheHeaders as st, Router as t, appendResponseHeader as tt, getRequestProtocol as u, getRequestAcceptableCharset as v, isWebHandler as w, isHandler as x, getRequestAcceptableCharsets as y, sendStream as z };
1838
1901
 
1839
- //# sourceMappingURL=src-B5i9q3Ix.mjs.map
1902
+ //# sourceMappingURL=src-BIwUJ5wD.mjs.map