keq 5.0.0-beta.9 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -37,6 +37,14 @@ var TimeoutException = class extends AbortException {
37
37
  }
38
38
  };
39
39
  //#endregion
40
+ //#region src/exception/mutex.exception.ts
41
+ var MutexException = class extends Exception {
42
+ constructor(message) {
43
+ super(message);
44
+ Object.defineProperty(this, "name", { value: "MutexException" });
45
+ }
46
+ };
47
+ //#endregion
40
48
  //#region \0@oxc-project+runtime@0.127.0/helpers/typeof.js
41
49
  function _typeof(o) {
42
50
  "@babel/helpers - typeof";
@@ -77,13 +85,18 @@ function _defineProperty(e, r, t) {
77
85
  //#endregion
78
86
  //#region src/exception/http-exceptions/request.exception.ts
79
87
  var RequestException = class extends Exception {
88
+ /** @deprecated Use `fatal` instead. `retry === true` is equivalent to `fatal === false`. */
89
+ get retry() {
90
+ return !this.fatal;
91
+ }
80
92
  constructor(statusCode, message, options) {
81
93
  super(message);
82
94
  _defineProperty(this, "statusCode", void 0);
83
- _defineProperty(this, "retry", void 0);
95
+ _defineProperty(this, "fatal", void 0);
84
96
  _defineProperty(this, "response", void 0);
97
+ if (typeof options === "boolean") options = { fatal: !options };
85
98
  this.statusCode = statusCode;
86
- this.retry = !options?.fatal;
99
+ this.fatal = options?.fatal ?? false;
87
100
  this.response = options?.response;
88
101
  Object.defineProperty(this, "name", { value: "RequestException" });
89
102
  }
@@ -849,6 +862,10 @@ var KeqSharedContext = class {
849
862
  get locationId() {
850
863
  return this[ContextLocationIdProperty];
851
864
  }
865
+ /** @deprecated Use `locationId` instead */
866
+ get identifier() {
867
+ return this[ContextLocationIdProperty];
868
+ }
852
869
  get request() {
853
870
  return this[ContextRequestProperty];
854
871
  }
@@ -1086,8 +1103,9 @@ async function intelligentParseResponse(response) {
1086
1103
  try {
1087
1104
  if (contentType.match(/^application\/(.+\+)?json/)) return resolveWith(response, "json");
1088
1105
  else if (contentType.includes("multipart/form-data")) return resolveWith(response, "form-data");
1089
- else if (contentType.includes("plain/text")) return resolveWith(response, "text");
1106
+ else if (contentType.startsWith("text/") && !contentType.includes("text/event-stream")) return resolveWith(response, "text");
1090
1107
  else if (contentType.includes("text/event-stream")) return resolveWith(response, "sse");
1108
+ else if (contentType.startsWith("image/") || contentType.startsWith("audio/") || contentType.startsWith("video/") || contentType.startsWith("font/") || contentType === "application/octet-stream" || contentType === "application/pdf") return resolveWith(response, "array-buffer");
1091
1109
  } catch (err) {
1092
1110
  console.warn(`Failed to intelligent parse response body: ${response.url}`, err);
1093
1111
  throw err;
@@ -1277,7 +1295,7 @@ var Core = class extends Promise {
1277
1295
  error = err;
1278
1296
  }
1279
1297
  const retryOn = typeof sharedContext.options.retry?.on === "function" ? sharedContext.options.retry.on : (attempt, error) => {
1280
- if (error instanceof RequestException && error.retry === false) return false;
1298
+ if (error instanceof RequestException && error.fatal) return false;
1281
1299
  return !!error;
1282
1300
  };
1283
1301
  let retryTimes = sharedContext.options.retry?.times || 0;
@@ -1323,6 +1341,16 @@ var Core = class extends Promise {
1323
1341
  this.__execute__().then(this.__resolve__, this.__reject__);
1324
1342
  }
1325
1343
  }
1344
+ /** @deprecated Await the Keq instance directly or use `.fire()` for fire-and-forget. */
1345
+ end() {
1346
+ if (!this.__triggered__) {
1347
+ this.__triggered__ = true;
1348
+ this.__execute__().then(this.__resolve__, this.__reject__);
1349
+ }
1350
+ return new Promise((resolve, reject) => {
1351
+ super.then(resolve, reject);
1352
+ });
1353
+ }
1326
1354
  then(onfulfilled, onrejected) {
1327
1355
  if (!this.__triggered__) {
1328
1356
  this.__triggered__ = true;
@@ -1498,6 +1526,9 @@ function keqMethodRoute(method) {
1498
1526
  }
1499
1527
  //#endregion
1500
1528
  //#region src/router/keq-module-route.ts
1529
+ /**
1530
+ * @deprecated This function exists solely for backward compatibility with v2.
1531
+ */
1501
1532
  function keqModuleRoute(moduleName) {
1502
1533
  if (!moduleName) throw new Exception("Module name should not be empty");
1503
1534
  return (ctx) => ctx.options.module?.name === moduleName;
@@ -1512,6 +1543,9 @@ function keqPathnameRoute(pathname) {
1512
1543
  }
1513
1544
  //#endregion
1514
1545
  //#region src/router/keq-router.ts
1546
+ /**
1547
+ * @deprecated Use `request.apply(...middlewares).forRoutes(...)` instead.
1548
+ */
1515
1549
  var KeqRouter = class KeqRouter {
1516
1550
  constructor(middlewares = []) {
1517
1551
  this.middlewares = middlewares;
@@ -1592,6 +1626,83 @@ var KeqRouter = class KeqRouter {
1592
1626
  }
1593
1627
  };
1594
1628
  //#endregion
1629
+ //#region src/router/utils/build-route.ts
1630
+ function buildRoute(pattern) {
1631
+ const routes = [];
1632
+ if (pattern.host) routes.push(keqHostRoute(pattern.host));
1633
+ if (pattern.method) routes.push(keqMethodRoute(pattern.method));
1634
+ if (pattern.pathname) routes.push(keqPathnameRoute(pattern.pathname));
1635
+ if (routes.length === 0) {
1636
+ const always = () => true;
1637
+ always.__keqRouteName__ = "always";
1638
+ return always;
1639
+ }
1640
+ if (routes.length === 1) return routes[0];
1641
+ return composeRoute(routes);
1642
+ }
1643
+ //#endregion
1644
+ //#region src/router/utils/not-route.ts
1645
+ function notRoute(route) {
1646
+ const negated = async (ctx) => !await route(ctx);
1647
+ negated.__keqRouteName__ = `not(${route.__keqRouteName__ || route.name || "anonymous"})`;
1648
+ return negated;
1649
+ }
1650
+ //#endregion
1651
+ //#region src/router/utils/or-route.ts
1652
+ function orRoute(routes) {
1653
+ if (!routes.length) throw new Exception("At least one route");
1654
+ const combined = async (ctx) => {
1655
+ return (await Promise.all(routes.map((route) => route(ctx)))).some((result) => result === true);
1656
+ };
1657
+ combined.__keqRouteName__ = `or(${routes.map((r) => r.__keqRouteName__ || r.name || "anonymous").join(", ")})`;
1658
+ return combined;
1659
+ }
1660
+ //#endregion
1661
+ //#region src/router/keq-middleware-applicator.ts
1662
+ function normalizeTarget(target) {
1663
+ if (typeof target === "function") return target;
1664
+ return buildRoute(target);
1665
+ }
1666
+ var KeqMiddlewareApplicator = class {
1667
+ constructor(middlewares, registeredMiddlewares, returnValue) {
1668
+ this.middlewares = middlewares;
1669
+ this.registeredMiddlewares = registeredMiddlewares;
1670
+ this.returnValue = returnValue;
1671
+ _defineProperty(this, "excludedTargets", []);
1672
+ }
1673
+ exclude(...routes) {
1674
+ this.excludedTargets.push(...routes);
1675
+ return this;
1676
+ }
1677
+ forRoutes(...routes) {
1678
+ const inclusionRoutes = routes.map(normalizeTarget);
1679
+ const inclusionRoute = inclusionRoutes.length === 1 ? inclusionRoutes[0] : orRoute(inclusionRoutes);
1680
+ let finalRoute;
1681
+ if (this.excludedTargets.length > 0) {
1682
+ const exclusionRoutes = this.excludedTargets.map(normalizeTarget);
1683
+ finalRoute = composeRoute([inclusionRoute, notRoute(exclusionRoutes.length === 1 ? exclusionRoutes[0] : orRoute(exclusionRoutes))]);
1684
+ } else finalRoute = inclusionRoute;
1685
+ const composedMiddleware = this.middlewares.length > 1 ? composeMiddleware(this.middlewares) : this.middlewares[0];
1686
+ this.registeredMiddlewares.push(async function routeApply(ctx, next) {
1687
+ if (await finalRoute(ctx)) await composedMiddleware(ctx, next);
1688
+ else await next();
1689
+ });
1690
+ return this.returnValue;
1691
+ }
1692
+ forAllRoutes() {
1693
+ const composedMiddleware = this.middlewares.length > 1 ? composeMiddleware(this.middlewares) : this.middlewares[0];
1694
+ if (this.excludedTargets.length > 0) {
1695
+ const exclusionRoutes = this.excludedTargets.map(normalizeTarget);
1696
+ const finalRoute = notRoute(exclusionRoutes.length === 1 ? exclusionRoutes[0] : orRoute(exclusionRoutes));
1697
+ this.registeredMiddlewares.push(async function routeApply(ctx, next) {
1698
+ if (await finalRoute(ctx)) await composedMiddleware(ctx, next);
1699
+ else await next();
1700
+ });
1701
+ } else this.registeredMiddlewares.push(composedMiddleware);
1702
+ return this.returnValue;
1703
+ }
1704
+ };
1705
+ //#endregion
1595
1706
  //#region src/middlewares/timeout-middleware/index.ts
1596
1707
  function keqTimeoutMiddleware() {
1597
1708
  return async function timeoutMiddleware(ctx, next) {
@@ -1660,9 +1771,34 @@ function keqAbortFlowControlMiddleware() {
1660
1771
  };
1661
1772
  }
1662
1773
  //#endregion
1774
+ //#region src/middlewares/flow-control-middleware/mutex-flow-control-middleware.ts
1775
+ function keqMutexFlowControlMiddleware() {
1776
+ return async function mutexFlowControlMiddleware(ctx, next) {
1777
+ if (!ctx.options.flowControl || ctx.options.flowControl.mode !== "mutex") {
1778
+ await next();
1779
+ return;
1780
+ }
1781
+ const { signal } = ctx.options.flowControl;
1782
+ const key = typeof signal === "string" ? signal : signal(ctx);
1783
+ if (!ctx.global.core) ctx.global.core = {};
1784
+ if (!ctx.global.core.mutexFlowControl) ctx.global.core.mutexFlowControl = /* @__PURE__ */ new Set();
1785
+ if (ctx.global.core.mutexFlowControl.has(key)) throw new MutexException(`Request rejected by MutexFlowControl: a request with key "${key}" is already in-flight`);
1786
+ ctx.global.core.mutexFlowControl.add(key);
1787
+ try {
1788
+ await next();
1789
+ } finally {
1790
+ ctx.global.core.mutexFlowControl.delete(key);
1791
+ }
1792
+ };
1793
+ }
1794
+ //#endregion
1663
1795
  //#region src/middlewares/flow-control-middleware/index.ts
1664
1796
  function keqFlowControlMiddleware() {
1665
- return composeMiddleware([keqSerialFlowControlMiddleware(), keqAbortFlowControlMiddleware()], { name: "flowControlMiddleware" });
1797
+ return composeMiddleware([
1798
+ keqMutexFlowControlMiddleware(),
1799
+ keqSerialFlowControlMiddleware(),
1800
+ keqAbortFlowControlMiddleware()
1801
+ ], { name: "flowControlMiddleware" });
1666
1802
  }
1667
1803
  //#endregion
1668
1804
  //#region src/middlewares/fetch-middleware/index.ts
@@ -1684,34 +1820,39 @@ function keqFetchMiddleware() {
1684
1820
  * Keq instance factory class,
1685
1821
  * and shares global data and middlewares across requests
1686
1822
  */
1687
- var KeqRequest = class {
1823
+ var KeqRequest = class KeqRequest {
1688
1824
  constructor(options) {
1689
1825
  _defineProperty(this, "baseOrigin", void 0);
1690
1826
  _defineProperty(this, "qs", void 0);
1691
1827
  _defineProperty(this, "global", {});
1692
- _defineProperty(this, "preMiddlewares", []);
1693
- _defineProperty(this, "postMiddlewares", []);
1828
+ _defineProperty(this, "middlewares", []);
1829
+ _defineProperty(this, "__terminalMiddlewares__", []);
1830
+ _defineProperty(this, "__parent__", void 0);
1694
1831
  if (options?.baseOrigin) this.baseOrigin = options.baseOrigin;
1695
1832
  else this.baseOrigin = Validator.isBrowser() ? location.origin : "http://127.0.0.1";
1696
1833
  this.qs = options?.qs;
1697
- this.postMiddlewares = options?.postMiddlewares || [
1834
+ this.__terminalMiddlewares__ = options?.terminalMiddlewares || [
1698
1835
  keqFlowControlMiddleware(),
1699
1836
  keqTimeoutMiddleware(),
1700
1837
  keqFetchMiddleware()
1701
1838
  ];
1702
- this.preMiddlewares = options?.preMiddlewares || [];
1839
+ this.middlewares = options?.middlewares || [];
1703
1840
  }
1704
1841
  __formatUrl__(url) {
1705
1842
  if (typeof url === "string") return new URL(url, this.baseOrigin);
1706
1843
  return new URL(url.href);
1707
1844
  }
1845
+ resolveMiddlewares() {
1846
+ if (!this.__parent__) return this.middlewares;
1847
+ return [...this.__parent__.resolveMiddlewares(), ...this.middlewares];
1848
+ }
1708
1849
  __fetch__(url, init, locationId) {
1709
1850
  return new Keq(this.__formatUrl__(url), {
1710
1851
  ...init,
1711
1852
  locationId,
1712
1853
  global: this.global,
1713
1854
  qs: this.qs,
1714
- middlewares: [...this.preMiddlewares, ...this.postMiddlewares]
1855
+ middlewares: [...this.resolveMiddlewares(), ...this.__terminalMiddlewares__]
1715
1856
  });
1716
1857
  }
1717
1858
  fetch(url, init) {
@@ -1742,11 +1883,37 @@ var KeqRequest = class {
1742
1883
  return this.__fetch__(url, { method: "options" }, getLocationId(1));
1743
1884
  }
1744
1885
  use(firstMiddleware, ...middleware) {
1745
- this.preMiddlewares.push(firstMiddleware, ...middleware);
1886
+ this.middlewares.push(firstMiddleware, ...middleware);
1746
1887
  return this;
1747
1888
  }
1889
+ apply(middleware, ...additionalMiddlewares) {
1890
+ return new KeqMiddlewareApplicator(Array.isArray(middleware) ? middleware : [middleware, ...additionalMiddlewares], this.middlewares, this);
1891
+ }
1892
+ /**
1893
+ * @deprecated Use `request.apply(...middlewares).forRoutes(...)` instead.
1894
+ */
1748
1895
  useRouter() {
1749
- return new KeqRouter(this.preMiddlewares);
1896
+ return new KeqRouter(this.middlewares);
1897
+ }
1898
+ fork(options) {
1899
+ const child = new KeqRequest({
1900
+ baseOrigin: options?.baseOrigin ?? this.baseOrigin,
1901
+ qs: options?.qs ?? (this.qs ? { ...this.qs } : void 0)
1902
+ });
1903
+ child.__parent__ = this;
1904
+ return child;
1905
+ }
1906
+ inherit(parent) {
1907
+ this.__parent__ = parent;
1908
+ return this;
1909
+ }
1910
+ adopt(child) {
1911
+ child.__parent__ = this;
1912
+ return this;
1913
+ }
1914
+ /** @deprecated Use `middlewares` instead */
1915
+ get preMiddlewares() {
1916
+ return this.middlewares;
1750
1917
  }
1751
1918
  on(event, listener) {
1752
1919
  const middleware = async (context, next) => {
@@ -1768,6 +1935,6 @@ function createRequest(options) {
1768
1935
  return new KeqRequest(options);
1769
1936
  }
1770
1937
  //#endregion
1771
- export { AbortException, BadGatewayException, BadRequestException, ConflictException, ContentTooLargeException, Exception, ForbiddenException, GatewayTimeoutException, ImATeapotException, InternalServerErrorException, Keq, KeqExecutionContext, KeqMiddlewareExecutor, KeqMiddlewareOrchestrator, KeqRequest, KeqSharedContext, MethodNotAllowedException, NotAcceptableException, NotFoundedException, NotImplementedException, PreconditionFailedException, ProxyAuthenticationRequiredException, RequestException, RequestTimeoutException, ServiceUnavailableException, TimeoutException, TooManyRequestsException, TypeException, UnauthorizedException, UnsupportedMediaTypeException, UriTooLongException, composeMiddleware, composeRoute, createProxyResponse, createRequest, request };
1938
+ export { AbortException, BadGatewayException, BadRequestException, ConflictException, ContentTooLargeException, Exception, ForbiddenException, GatewayTimeoutException, ImATeapotException, InternalServerErrorException, Keq, KeqExecutionContext, KeqMiddlewareApplicator, KeqMiddlewareExecutor, KeqMiddlewareOrchestrator, KeqRequest, KeqSharedContext, MethodNotAllowedException, NotAcceptableException, NotFoundedException, NotImplementedException, PreconditionFailedException, ProxyAuthenticationRequiredException, RequestException, RequestTimeoutException, ServiceUnavailableException, TimeoutException, TooManyRequestsException, TypeException, UnauthorizedException, UnsupportedMediaTypeException, UriTooLongException, composeMiddleware, composeRoute, createProxyResponse, createRequest, request };
1772
1939
 
1773
1940
  //# sourceMappingURL=index.mjs.map