routup 5.1.0 → 5.2.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.
@@ -1,4 +1,5 @@
1
1
  import { FastURL } from "srvx";
2
+ import { hasInstanceof, markInstanceof } from "@ebec/core";
2
3
  import { HTTPError, isHTTPError } from "@ebec/http";
3
4
  import { subtle } from "uncrypto";
4
5
  import { merge } from "smob";
@@ -65,10 +66,10 @@ function setResponseCacheHeaders(event, options) {
65
66
  //#region src/error/module.ts
66
67
  const ErrorSymbol = Symbol.for("RoutupError");
67
68
  var RoutupError = class extends HTTPError {
68
- "@instanceof" = ErrorSymbol;
69
69
  constructor(input = {}) {
70
70
  super(input);
71
71
  this.name = "RoutupError";
72
+ markInstanceof(this, ErrorSymbol);
72
73
  }
73
74
  };
74
75
  //#endregion
@@ -189,12 +190,6 @@ function buildTrustProxyFn(input) {
189
190
  return compile(input || []);
190
191
  }
191
192
  //#endregion
192
- //#region src/utils/is-instance.ts
193
- function isInstance(input, sym) {
194
- if (!isObject(input)) return false;
195
- return input["@instanceof"] === sym;
196
- }
197
- //#endregion
198
193
  //#region src/utils/mime.ts
199
194
  function getMimeType(type) {
200
195
  if (type.includes("/")) return type;
@@ -341,8 +336,7 @@ function setResponseHeaderContentType(event, input, ifNotExists) {
341
336
  //#endregion
342
337
  //#region src/error/is.ts
343
338
  function isError(input) {
344
- if (!isHTTPError(input)) return false;
345
- return isInstance(input, ErrorSymbol);
339
+ return hasInstanceof(input, ErrorSymbol);
346
340
  }
347
341
  //#endregion
348
342
  //#region src/error/create.ts
@@ -833,7 +827,7 @@ const HookName = {
833
827
  };
834
828
  //#endregion
835
829
  //#region src/hook/module.ts
836
- var HookManager = class {
830
+ var Hooks = class Hooks {
837
831
  items;
838
832
  constructor() {
839
833
  this.items = {};
@@ -863,6 +857,23 @@ var HookManager = class {
863
857
  }
864
858
  if (this.items[name].length === 0) delete this.items[name];
865
859
  }
860
+ /**
861
+ * Create a new `Hooks` instance seeded with the same listeners as this
862
+ * one.
863
+ *
864
+ * Listener functions are shared by reference; priority and ordering are
865
+ * preserved. Future mutations on the returned instance do not affect this
866
+ * one (and vice versa).
867
+ */
868
+ clone() {
869
+ const next = new Hooks();
870
+ const names = Object.keys(this.items);
871
+ for (const name of names) {
872
+ const entries = this.items[name];
873
+ for (const entry of entries) next.addListener(name, entry.fn, entry.priority);
874
+ }
875
+ return next;
876
+ }
866
877
  async trigger(name, event) {
867
878
  if (!this.items[name] || this.items[name].length === 0) return;
868
879
  try {
@@ -973,18 +984,18 @@ function matchHandlerMethod(handlerMethod, requestMethod) {
973
984
  //#endregion
974
985
  //#region src/handler/module.ts
975
986
  var Handler = class {
976
- "@instanceof" = HandlerSymbol;
977
987
  config;
978
- hookManager;
988
+ hooks;
979
989
  pathMatcher;
980
990
  method;
981
991
  constructor(handler) {
982
992
  this.config = handler;
983
- this.hookManager = new HookManager();
993
+ this.hooks = new Hooks();
984
994
  this.mountHooks();
985
995
  if (typeof handler.path === "string") this.config.path = withLeadingSlash(handler.path);
986
996
  this.pathMatcher = buildHandlerPathMatcher(this.config.path, !!this.config.method);
987
997
  this.method = this.config.method ? toMethodName(this.config.method) : void 0;
998
+ markInstanceof(this, HandlerSymbol);
988
999
  }
989
1000
  get type() {
990
1001
  return this.config.type;
@@ -1000,7 +1011,7 @@ var Handler = class {
1000
1011
  ...pathMatch.params
1001
1012
  };
1002
1013
  }
1003
- await this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, event);
1014
+ await this.hooks.trigger(HookName.CHILD_DISPATCH_BEFORE, event);
1004
1015
  if (event.dispatched) return;
1005
1016
  let response;
1006
1017
  try {
@@ -1039,11 +1050,11 @@ var Handler = class {
1039
1050
  if (response) event.dispatched = true;
1040
1051
  } catch (e) {
1041
1052
  event.error = isError(e) ? e : createError(e);
1042
- await this.hookManager.trigger(HookName.ERROR, event);
1053
+ await this.hooks.trigger(HookName.ERROR, event);
1043
1054
  if (event.dispatched) event.error = void 0;
1044
1055
  else throw event.error;
1045
1056
  }
1046
- await this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, event);
1057
+ await this.hooks.trigger(HookName.CHILD_DISPATCH_AFTER, event);
1047
1058
  return response;
1048
1059
  }
1049
1060
  matchPath(path) {
@@ -1113,9 +1124,9 @@ var Handler = class {
1113
1124
  return Math.min(routerDefault, handlerOverride);
1114
1125
  }
1115
1126
  mountHooks() {
1116
- if (this.config.onBefore) this.hookManager.addListener(HookName.CHILD_DISPATCH_BEFORE, this.config.onBefore);
1117
- if (this.config.onAfter) this.hookManager.addListener(HookName.CHILD_DISPATCH_AFTER, this.config.onAfter);
1118
- if (this.config.onError) this.hookManager.addListener(HookName.ERROR, this.config.onError);
1127
+ if (this.config.onBefore) this.hooks.addListener(HookName.CHILD_DISPATCH_BEFORE, this.config.onBefore);
1128
+ if (this.config.onAfter) this.hooks.addListener(HookName.CHILD_DISPATCH_AFTER, this.config.onAfter);
1129
+ if (this.config.onError) this.hooks.addListener(HookName.ERROR, this.config.onError);
1119
1130
  }
1120
1131
  };
1121
1132
  //#endregion
@@ -1275,7 +1286,7 @@ function isHandlerOptions(input) {
1275
1286
  return isObject(input) && typeof input.fn === "function" && typeof input.type === "string";
1276
1287
  }
1277
1288
  function isHandler(input) {
1278
- return isInstance(input, HandlerSymbol);
1289
+ return hasInstanceof(input, HandlerSymbol);
1279
1290
  }
1280
1291
  //#endregion
1281
1292
  //#region src/request/helpers/cache.ts
@@ -1496,7 +1507,7 @@ const RouterStackEntryType = {
1496
1507
  //#endregion
1497
1508
  //#region src/router/utils.ts
1498
1509
  function isRouterInstance(input) {
1499
- return isInstance(input, RouterSymbol);
1510
+ return hasInstanceof(input, RouterSymbol);
1500
1511
  }
1501
1512
  /**
1502
1513
  * Build a non-terminal `PathMatcher` for a router mount path.
@@ -1520,8 +1531,16 @@ function acceptsJson(request) {
1520
1531
  }
1521
1532
  //#endregion
1522
1533
  //#region src/router/module.ts
1534
+ const METHOD_TO_REGISTER = {
1535
+ [MethodName.GET]: "get",
1536
+ [MethodName.POST]: "post",
1537
+ [MethodName.PUT]: "put",
1538
+ [MethodName.PATCH]: "patch",
1539
+ [MethodName.DELETE]: "delete",
1540
+ [MethodName.HEAD]: "head",
1541
+ [MethodName.OPTIONS]: "options"
1542
+ };
1523
1543
  var Router = class Router {
1524
- "@instanceof" = RouterSymbol;
1525
1544
  /**
1526
1545
  * A label for the router instance.
1527
1546
  */
@@ -1541,11 +1560,11 @@ var Router = class Router {
1541
1560
  */
1542
1561
  pathMatcher;
1543
1562
  /**
1544
- * A hook manager.
1563
+ * Lifecycle hook registry.
1545
1564
  *
1546
1565
  * @protected
1547
1566
  */
1548
- hookManager;
1567
+ hooks;
1549
1568
  /**
1550
1569
  * Normalized options for this router instance.
1551
1570
  */
@@ -1558,9 +1577,12 @@ var Router = class Router {
1558
1577
  plugins = /* @__PURE__ */ new Map();
1559
1578
  constructor(input = {}) {
1560
1579
  this.name = input.name;
1561
- this.hookManager = new HookManager();
1562
- this._options = normalizeRouterOptions(input);
1563
- this.pathMatcher = buildRouterPathMatcher(input.path);
1580
+ const { hooks = new Hooks(), plugins = /* @__PURE__ */ new Map(), ...options } = input;
1581
+ this.hooks = hooks;
1582
+ this.plugins = new Map(plugins);
1583
+ this._options = normalizeRouterOptions(options);
1584
+ this.pathMatcher = buildRouterPathMatcher(options.path);
1585
+ markInstanceof(this, RouterSymbol);
1564
1586
  }
1565
1587
  matchPath(path) {
1566
1588
  if (this.pathMatcher) return this.pathMatcher.test(path);
@@ -1642,7 +1664,7 @@ var Router = class Router {
1642
1664
  await this.executePipelineStepFinish(context);
1643
1665
  }
1644
1666
  async executePipelineStepStart(context) {
1645
- await this.hookManager.trigger(HookName.REQUEST, context.event);
1667
+ await this.hooks.trigger(HookName.REQUEST, context.event);
1646
1668
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1647
1669
  else context.step = RouterPipelineStep.LOOKUP;
1648
1670
  }
@@ -1659,7 +1681,7 @@ var Router = class Router {
1659
1681
  const method = entry.method ?? handler.method;
1660
1682
  if (method) context.event.methodsAllowed.add(method);
1661
1683
  if (matchHandlerMethod(method, context.event.method)) {
1662
- await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1684
+ await this.hooks.trigger(HookName.CHILD_MATCH, context.event);
1663
1685
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1664
1686
  else context.step = RouterPipelineStep.CHILD_BEFORE;
1665
1687
  return;
@@ -1669,7 +1691,7 @@ var Router = class Router {
1669
1691
  continue;
1670
1692
  }
1671
1693
  if (entry.pathMatcher ? entry.pathMatcher.test(context.event.path) : entry.data.matchPath(context.event.path)) {
1672
- await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
1694
+ await this.hooks.trigger(HookName.CHILD_MATCH, context.event);
1673
1695
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1674
1696
  else context.step = RouterPipelineStep.CHILD_BEFORE;
1675
1697
  return;
@@ -1679,12 +1701,12 @@ var Router = class Router {
1679
1701
  context.step = RouterPipelineStep.FINISH;
1680
1702
  }
1681
1703
  async executePipelineStepChildBefore(context) {
1682
- await this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, context.event);
1704
+ await this.hooks.trigger(HookName.CHILD_DISPATCH_BEFORE, context.event);
1683
1705
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1684
1706
  else context.step = RouterPipelineStep.CHILD_DISPATCH;
1685
1707
  }
1686
1708
  async executePipelineStepChildAfter(context) {
1687
- await this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, context.event);
1709
+ await this.hooks.trigger(HookName.CHILD_DISPATCH_AFTER, context.event);
1688
1710
  if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
1689
1711
  else context.step = RouterPipelineStep.LOOKUP;
1690
1712
  }
@@ -1735,7 +1757,7 @@ var Router = class Router {
1735
1757
  }
1736
1758
  } catch (e) {
1737
1759
  event.error = createError(e);
1738
- await this.hookManager.trigger(HookName.ERROR, event);
1760
+ await this.hooks.trigger(HookName.ERROR, event);
1739
1761
  }
1740
1762
  if (!event.dispatched) {
1741
1763
  event.path = savedPath;
@@ -1746,7 +1768,7 @@ var Router = class Router {
1746
1768
  context.step = RouterPipelineStep.CHILD_AFTER;
1747
1769
  }
1748
1770
  async executePipelineStepFinish(context) {
1749
- if (context.event.error || context.event.dispatched) return this.hookManager.trigger(HookName.RESPONSE, context.event);
1771
+ if (context.event.error || context.event.dispatched) return this.hooks.trigger(HookName.RESPONSE, context.event);
1750
1772
  if (!context.event.dispatched && context.event.routerPath.length === 1 && context.event.method && context.event.method === MethodName.OPTIONS) {
1751
1773
  if (context.event.methodsAllowed.has(MethodName.GET)) context.event.methodsAllowed.add(MethodName.HEAD);
1752
1774
  const options = [...context.event.methodsAllowed].map((key) => key.toUpperCase()).join(",");
@@ -1758,7 +1780,7 @@ var Router = class Router {
1758
1780
  });
1759
1781
  context.event.dispatched = true;
1760
1782
  }
1761
- return this.hookManager.trigger(HookName.RESPONSE, context.event);
1783
+ return this.hooks.trigger(HookName.RESPONSE, context.event);
1762
1784
  }
1763
1785
  async dispatch(event) {
1764
1786
  const savedPath = event.path;
@@ -1844,6 +1866,7 @@ var Router = class Router {
1844
1866
  type: RouterStackEntryType.HANDLER,
1845
1867
  data: handler,
1846
1868
  method,
1869
+ path,
1847
1870
  pathMatcher: buildHandlerPathMatcher(path ?? handler.path, true)
1848
1871
  });
1849
1872
  }
@@ -1859,6 +1882,7 @@ var Router = class Router {
1859
1882
  this.stack.push({
1860
1883
  type: RouterStackEntryType.ROUTER,
1861
1884
  data: item,
1885
+ path,
1862
1886
  pathMatcher: buildRouterPathMatcher(path)
1863
1887
  });
1864
1888
  continue;
@@ -1870,7 +1894,8 @@ var Router = class Router {
1870
1894
  });
1871
1895
  this.stack.push({
1872
1896
  type: RouterStackEntryType.HANDLER,
1873
- data: handler
1897
+ data: handler,
1898
+ path
1874
1899
  });
1875
1900
  continue;
1876
1901
  }
@@ -1878,6 +1903,7 @@ var Router = class Router {
1878
1903
  this.stack.push({
1879
1904
  type: RouterStackEntryType.HANDLER,
1880
1905
  data: item,
1906
+ path,
1881
1907
  pathMatcher: buildHandlerPathMatcher(path, !!item.method)
1882
1908
  });
1883
1909
  continue;
@@ -1909,19 +1935,59 @@ var Router = class Router {
1909
1935
  this.plugins.set(plugin.name, plugin.version);
1910
1936
  return this;
1911
1937
  }
1938
+ /**
1939
+ * Return a new `Router` that mirrors this one but owns independent
1940
+ * mountable state.
1941
+ *
1942
+ * The new router has:
1943
+ * - a fresh `stack` array of shallow-copied entries (handlers and child
1944
+ * routers are shared by reference; only the wrapping entries are new)
1945
+ * - the same `pathMatcher` reference (it is stateless)
1946
+ * - a fresh `Hooks` instance seeded with the current listeners
1947
+ * - a shallow copy of `_options`
1948
+ * - a fresh `plugins` map with the same entries
1949
+ *
1950
+ * Use this when the same logical router needs to be mounted under
1951
+ * multiple paths — each mount can receive its own clone so subsequent
1952
+ * mutations on one mount do not bleed into the others.
1953
+ */
1954
+ clone() {
1955
+ const next = new Router({
1956
+ ...this._options,
1957
+ hooks: this.hooks.clone(),
1958
+ plugins: this.plugins
1959
+ });
1960
+ for (const entry of this.stack) {
1961
+ if (entry.type === RouterStackEntryType.ROUTER) {
1962
+ const data = entry.data.clone();
1963
+ if (entry.path) next.use(entry.path, data);
1964
+ else next.use(data);
1965
+ continue;
1966
+ }
1967
+ if (entry.method) {
1968
+ const register = METHOD_TO_REGISTER[entry.method];
1969
+ if (entry.path) next[register](entry.path, entry.data);
1970
+ else next[register](entry.data);
1971
+ continue;
1972
+ }
1973
+ if (entry.path) next.use(entry.path, entry.data);
1974
+ else next.use(entry.data);
1975
+ }
1976
+ return next;
1977
+ }
1912
1978
  on(name, fn, priority) {
1913
- return this.hookManager.addListener(name, fn, priority);
1979
+ return this.hooks.addListener(name, fn, priority);
1914
1980
  }
1915
1981
  off(name, fn) {
1916
1982
  if (typeof fn === "undefined") {
1917
- this.hookManager.removeListener(name);
1983
+ this.hooks.removeListener(name);
1918
1984
  return this;
1919
1985
  }
1920
- this.hookManager.removeListener(name, fn);
1986
+ this.hooks.removeListener(name, fn);
1921
1987
  return this;
1922
1988
  }
1923
1989
  };
1924
1990
  //#endregion
1925
1991
  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, serializeEventStreamMessage as at, isRequestCacheable as b, isPluginError as c, setResponseCacheHeaders as ct, getRequestIP as d, setResponseHeaderInline as et, getRequestHostName as f, getRequestAcceptableEncoding as g, getRequestAcceptableLanguages as h, PluginNotInstalledError as i, createEventStream as it, buildHandlerPathMatcher as j, defineCoreHandler as k, PluginErrorCode as l, HeaderName as lt, getRequestAcceptableLanguage as m, normalizeRouterOptions as n, appendResponseHeader as nt, PluginAlreadyInstalledError as o, ErrorSymbol as ot, matchRequestContentType as p, sendCreated as q, isPlugin as r, appendResponseHeaderDirective as rt, PluginError as s, RoutupError as st, Router as t, setResponseContentTypeByFileName as tt, getRequestProtocol as u, MethodName as ut, getRequestAcceptableCharset as v, isWebHandler as w, isHandler as x, getRequestAcceptableCharsets as y, sendStream as z };
1926
1992
 
1927
- //# sourceMappingURL=src-BYuhKm-d.mjs.map
1993
+ //# sourceMappingURL=src-DX0rndew.mjs.map