stratal 0.0.19 → 0.0.20

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 (146) hide show
  1. package/dist/{base-email.provider-mjynzewK.mjs → base-email.provider-CfQCA08m.mjs} +1 -1
  2. package/dist/{base-email.provider-mjynzewK.mjs.map → base-email.provider-CfQCA08m.mjs.map} +1 -1
  3. package/dist/bin/quarry.mjs +6 -0
  4. package/dist/bin/quarry.mjs.map +1 -1
  5. package/dist/cache/index.d.mts +2 -154
  6. package/dist/cache/index.d.mts.map +1 -1
  7. package/dist/cache/index.mjs +3 -5
  8. package/dist/cache/index.mjs.map +1 -1
  9. package/dist/cache.service-DsnKuNyO.d.mts +156 -0
  10. package/dist/cache.service-DsnKuNyO.d.mts.map +1 -0
  11. package/dist/cache.tokens-B7Rw1C9Q.mjs +6 -0
  12. package/dist/cache.tokens-B7Rw1C9Q.mjs.map +1 -0
  13. package/dist/{command-DsQq56Lp.d.mts → command-Bu-PjJrX.d.mts} +2 -2
  14. package/dist/{command-DsQq56Lp.d.mts.map → command-Bu-PjJrX.d.mts.map} +1 -1
  15. package/dist/config/index.d.mts +3 -3
  16. package/dist/config/index.mjs +2 -2
  17. package/dist/{consumer-registry-Doom7BEh.d.mts → consumer-registry-B7yUNh0q.d.mts} +1 -1
  18. package/dist/{consumer-registry-Doom7BEh.d.mts.map → consumer-registry-B7yUNh0q.d.mts.map} +1 -1
  19. package/dist/{controller.decorator-LZY9aHYG.mjs → controller.decorator-DQzenvSN.mjs} +2 -2
  20. package/dist/{controller.decorator-LZY9aHYG.mjs.map → controller.decorator-DQzenvSN.mjs.map} +1 -1
  21. package/dist/cron/index.d.mts +2 -2
  22. package/dist/cron/index.mjs +1 -1
  23. package/dist/{cron-manager-C30t9UZM.mjs → cron-manager-7Symz_TE.mjs} +2 -2
  24. package/dist/{cron-manager-C30t9UZM.mjs.map → cron-manager-7Symz_TE.mjs.map} +1 -1
  25. package/dist/{cron-manager-RuPtFVLy.d.mts → cron-manager-BEsH1mjW.d.mts} +3 -3
  26. package/dist/{cron-manager-RuPtFVLy.d.mts.map → cron-manager-BEsH1mjW.d.mts.map} +1 -1
  27. package/dist/di/index.d.mts +1 -1
  28. package/dist/di/index.mjs +1 -1
  29. package/dist/email/index.d.mts +3 -3
  30. package/dist/email/index.mjs +7 -7
  31. package/dist/{en-rHmW6vD9.mjs → en-DSH_bhh6.mjs} +7 -1
  32. package/dist/en-DSH_bhh6.mjs.map +1 -0
  33. package/dist/{env-CamWD-U1.d.mts → env-D1rcZ8_r.d.mts} +1 -1
  34. package/dist/env-D1rcZ8_r.d.mts.map +1 -0
  35. package/dist/errors/index.d.mts +1 -1
  36. package/dist/errors/index.mjs +1 -1
  37. package/dist/{errors-B4pYgYON.mjs → errors-BdyV5PnY.mjs} +20 -9
  38. package/dist/errors-BdyV5PnY.mjs.map +1 -0
  39. package/dist/{errors-BUyUfr2Z.mjs → errors-Da3Pz2X7.mjs} +2 -2
  40. package/dist/{errors-BUyUfr2Z.mjs.map → errors-Da3Pz2X7.mjs.map} +1 -1
  41. package/dist/events/index.d.mts +2 -2
  42. package/dist/{gateway-context-cqZ8wMoi.mjs → gateway-context-CdJjpUCW.mjs} +4 -8
  43. package/dist/{gateway-context-cqZ8wMoi.mjs.map → gateway-context-CdJjpUCW.mjs.map} +1 -1
  44. package/dist/guards/index.d.mts +3 -3
  45. package/dist/guards/index.mjs +1 -1
  46. package/dist/{guards-DMbsAxSX.mjs → guards-DUk_Kzst.mjs} +1 -1
  47. package/dist/{guards-DMbsAxSX.mjs.map → guards-DUk_Kzst.mjs.map} +1 -1
  48. package/dist/{http-method.decorator-BT3ufnz8.mjs → http-method.decorator-DXwxAfb_.mjs} +3 -3
  49. package/dist/{http-method.decorator-BT3ufnz8.mjs.map → http-method.decorator-DXwxAfb_.mjs.map} +1 -1
  50. package/dist/i18n/index.d.mts +2 -2
  51. package/dist/i18n/index.mjs +2 -2
  52. package/dist/i18n/messages/en/index.d.mts +1 -1
  53. package/dist/i18n/messages/en/index.mjs +1 -1
  54. package/dist/i18n/utils/index.mjs +1 -1
  55. package/dist/i18n/validation/index.d.mts +2 -2
  56. package/dist/i18n/validation/index.mjs +2 -2
  57. package/dist/{i18n.module-CI_prYFD.mjs → i18n.module-BBlNNlcG.mjs} +191 -39
  58. package/dist/i18n.module-BBlNNlcG.mjs.map +1 -0
  59. package/dist/{index-SHx31sBJ.d.mts → index-7-hU3GTV.d.mts} +1 -1
  60. package/dist/{index-SHx31sBJ.d.mts.map → index-7-hU3GTV.d.mts.map} +1 -1
  61. package/dist/{index-B437eK7p.d.mts → index-Bnpfq6uk.d.mts} +58 -10
  62. package/dist/index-Bnpfq6uk.d.mts.map +1 -0
  63. package/dist/{index-DFhEeFfC.d.mts → index-C1KvMncZ.d.mts} +7 -1
  64. package/dist/{index-DFhEeFfC.d.mts.map → index-C1KvMncZ.d.mts.map} +1 -1
  65. package/dist/{index-Dnqm9ZB6.d.mts → index-CjaQ6_tZ.d.mts} +5 -5
  66. package/dist/{index-Dnqm9ZB6.d.mts.map → index-CjaQ6_tZ.d.mts.map} +1 -1
  67. package/dist/{index-DPFqRs8L.d.mts → index-D0US0X14.d.mts} +313 -204
  68. package/dist/index-D0US0X14.d.mts.map +1 -0
  69. package/dist/{index-CWRS7Ri3.d.mts → index-DBd_2wv8.d.mts} +1 -1
  70. package/dist/{index-CWRS7Ri3.d.mts.map → index-DBd_2wv8.d.mts.map} +1 -1
  71. package/dist/index.d.mts +3 -3
  72. package/dist/index.mjs +1 -1
  73. package/dist/logger/index.d.mts +1 -1
  74. package/dist/macroable/index.d.mts +1 -1
  75. package/dist/module/index.d.mts +2 -2
  76. package/dist/module/index.mjs +1 -1
  77. package/dist/{module-qGE_1duv.mjs → module-Dk2qTa77.mjs} +132 -4
  78. package/dist/module-Dk2qTa77.mjs.map +1 -0
  79. package/dist/openapi/index.d.mts +3 -3
  80. package/dist/openapi/index.mjs +2 -2
  81. package/dist/{openapi-tools.service-CYWGuhue.mjs → openapi-tools.service-Zs-Ewv7F.mjs} +1 -1
  82. package/dist/{openapi-tools.service-CYWGuhue.mjs.map → openapi-tools.service-Zs-Ewv7F.mjs.map} +1 -1
  83. package/dist/{openapi.service-Bv_NioM9.d.mts → openapi.service-BLgvn3hJ.d.mts} +3 -3
  84. package/dist/{openapi.service-Bv_NioM9.d.mts.map → openapi.service-BLgvn3hJ.d.mts.map} +1 -1
  85. package/dist/quarry/index.d.mts +6 -6
  86. package/dist/quarry/index.mjs +2 -2
  87. package/dist/{quarry-registry-DFfRRkA7.mjs → quarry-registry-DNEej-Db.mjs} +5 -5
  88. package/dist/{quarry-registry-DFfRRkA7.mjs.map → quarry-registry-DNEej-Db.mjs.map} +1 -1
  89. package/dist/queue/index.d.mts +2 -2
  90. package/dist/queue/index.mjs +1 -1
  91. package/dist/{queue.module-P-G-nCYz.mjs → queue.module-BCdCiySt.mjs} +3 -3
  92. package/dist/{queue.module-P-G-nCYz.mjs.map → queue.module-BCdCiySt.mjs.map} +1 -1
  93. package/dist/{r2-storage.provider-LdzK9tfG.mjs → r2-storage.provider-Co6F0ZYV.mjs} +3 -3
  94. package/dist/{r2-storage.provider-LdzK9tfG.mjs.map → r2-storage.provider-Co6F0ZYV.mjs.map} +1 -1
  95. package/dist/rate-limit.decorator--o6Q6p9w.mjs +55 -0
  96. package/dist/rate-limit.decorator--o6Q6p9w.mjs.map +1 -0
  97. package/dist/rate-limiter/index.d.mts +420 -0
  98. package/dist/rate-limiter/index.d.mts.map +1 -0
  99. package/dist/rate-limiter/index.mjs +365 -0
  100. package/dist/rate-limiter/index.mjs.map +1 -0
  101. package/dist/{resend.provider-bwILp0WI.mjs → resend.provider-M6qRLrcy.mjs} +2 -2
  102. package/dist/{resend.provider-bwILp0WI.mjs.map → resend.provider-M6qRLrcy.mjs.map} +1 -1
  103. package/dist/router/index.d.mts +2 -2
  104. package/dist/router/index.mjs +6 -6
  105. package/dist/seeder/index.d.mts +3 -3
  106. package/dist/seeder/index.mjs +1 -1
  107. package/dist/{seeder-BcqIFa2X.mjs → seeder-CJAOHEIo.mjs} +2 -2
  108. package/dist/{seeder-BcqIFa2X.mjs.map → seeder-CJAOHEIo.mjs.map} +1 -1
  109. package/dist/{setup-CtekcwuO.mjs → setup-CefZKV_e.mjs} +1 -1
  110. package/dist/{setup-CtekcwuO.mjs.map → setup-CefZKV_e.mjs.map} +1 -1
  111. package/dist/{signed-url-COX7cCWR.mjs → signed-url-BQPbv2In.mjs} +1 -1
  112. package/dist/{signed-url-COX7cCWR.mjs.map → signed-url-BQPbv2In.mjs.map} +1 -1
  113. package/dist/{smtp.provider-B07yuARi.mjs → smtp.provider-w0Ve52Xg.mjs} +2 -2
  114. package/dist/{smtp.provider-B07yuARi.mjs.map → smtp.provider-w0Ve52Xg.mjs.map} +1 -1
  115. package/dist/storage/index.d.mts +3 -3
  116. package/dist/storage/index.mjs +2 -2
  117. package/dist/storage/providers/index.d.mts +2 -2
  118. package/dist/storage/providers/index.mjs +1 -1
  119. package/dist/{storage-P6X4h9So.mjs → storage-1zw-6Yiz.mjs} +8 -8
  120. package/dist/{storage-P6X4h9So.mjs.map → storage-1zw-6Yiz.mjs.map} +1 -1
  121. package/dist/{storage-provider.interface-CC1nniHk.d.mts → storage-provider.interface-Bd6vA4ak.d.mts} +2 -2
  122. package/dist/{storage-provider.interface-CC1nniHk.d.mts.map → storage-provider.interface-Bd6vA4ak.d.mts.map} +1 -1
  123. package/dist/{stratal-BCiwCFN9.mjs → stratal-DeEcGgdq.mjs} +8 -8
  124. package/dist/stratal-DeEcGgdq.mjs.map +1 -0
  125. package/dist/{types-DIWemRad.d.mts → types-cySNS_lp.d.mts} +1 -1
  126. package/dist/types-cySNS_lp.d.mts.map +1 -0
  127. package/dist/{usage-generator-MBcRo0Q2.mjs → usage-generator-BUdlhnCK.mjs} +1 -1
  128. package/dist/{usage-generator-MBcRo0Q2.mjs.map → usage-generator-BUdlhnCK.mjs.map} +1 -1
  129. package/dist/{validation-Dbg3ehdP.mjs → validation-DtJwAv7O.mjs} +62 -8
  130. package/dist/validation-DtJwAv7O.mjs.map +1 -0
  131. package/dist/websocket/index.d.mts +8 -3
  132. package/dist/websocket/index.d.mts.map +1 -1
  133. package/dist/websocket/index.mjs +1 -1
  134. package/dist/workers/index.d.mts +2 -2
  135. package/dist/workers/index.mjs +1 -1
  136. package/package.json +10 -6
  137. package/dist/en-rHmW6vD9.mjs.map +0 -1
  138. package/dist/env-CamWD-U1.d.mts.map +0 -1
  139. package/dist/errors-B4pYgYON.mjs.map +0 -1
  140. package/dist/i18n.module-CI_prYFD.mjs.map +0 -1
  141. package/dist/index-B437eK7p.d.mts.map +0 -1
  142. package/dist/index-DPFqRs8L.d.mts.map +0 -1
  143. package/dist/module-qGE_1duv.mjs.map +0 -1
  144. package/dist/stratal-BCiwCFN9.mjs.map +0 -1
  145. package/dist/types-DIWemRad.d.mts.map +0 -1
  146. package/dist/validation-Dbg3ehdP.mjs.map +0 -1
@@ -1,15 +1,16 @@
1
- import { A as Scope, D as runWithContainer, E as getContainer, H as ApplicationError, V as ROUTER_TOKENS, a as createHttpExceptionContext, c as DEFAULT_CONTENT_TYPE, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext, u as METHOD_STATUS_CODES, w as I18N_TOKENS } from "./errors-B4pYgYON.mjs";
1
+ import { A as Scope, D as runWithContainer, E as getContainer, H as ApplicationError, V as ROUTER_TOKENS, a as createHttpExceptionContext, c as DEFAULT_CONTENT_TYPE, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext, u as METHOD_STATUS_CODES, w as I18N_TOKENS } from "./errors-BdyV5PnY.mjs";
2
2
  import { a as __decorate, d as CONTAINER_TOKEN, f as DI_TOKENS, g as getMethodInjections, o as __decorateParam, p as Transient, s as __decorateMetadata, u as LOGGER_TOKENS } from "./logger-V6Ms3QnQ.mjs";
3
- import { C as Module, b as ControllerRegistrationError, c as InvalidSignatureError, d as MissingRouteParamError, f as ResponseValidationError, g as RouteNotFoundError, h as SchemaValidationError, l as MiddlewareNextCalledMultipleTimesError, o as DomainMismatchError, p as RouteNameNotFoundError, s as DuplicateRouteNameError, u as MissingEnvironmentVariableError, v as OpenAPIRouteRegistrationError, x as ControllerMethodNotFoundError, y as HonoAppAlreadyConfiguredError } from "./module-qGE_1duv.mjs";
4
- import { i as z, o as backendErrorMap, r as validation_exports, s as runWithErrorMapContext, t as OpenAPIHono } from "./validation-Dbg3ehdP.mjs";
5
- import { n as OPENAPI_TOKENS } from "./openapi-tools.service-CYWGuhue.mjs";
6
- import { t as en_exports } from "./en-rHmW6vD9.mjs";
7
- import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-DMbsAxSX.mjs";
8
- import { n as getControllerOptions, r as getControllerRoute } from "./controller.decorator-LZY9aHYG.mjs";
9
- import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-cqZ8wMoi.mjs";
10
- import "./http-method.decorator-BT3ufnz8.mjs";
11
- import { n as verifySignedUrl, t as signUrl } from "./signed-url-COX7cCWR.mjs";
12
- import { t as setupI18nCompiler } from "./setup-CtekcwuO.mjs";
3
+ import { S as createThrottleMiddleware, b as ControllerRegistrationError, c as InvalidSignatureError, d as MissingRouteParamError, f as ResponseValidationError, g as RouteNotFoundError, h as SchemaValidationError, k as Module, l as MiddlewareNextCalledMultipleTimesError, o as DomainMismatchError, p as RouteNameNotFoundError, s as DuplicateRouteNameError, u as MissingEnvironmentVariableError, v as OpenAPIRouteRegistrationError, x as ControllerMethodNotFoundError, y as HonoAppAlreadyConfiguredError } from "./module-Dk2qTa77.mjs";
4
+ import { c as backendErrorMap, i as z, l as runWithErrorMapContext, r as validation_exports, t as OpenAPIHono } from "./validation-DtJwAv7O.mjs";
5
+ import { n as OPENAPI_TOKENS } from "./openapi-tools.service-Zs-Ewv7F.mjs";
6
+ import { t as en_exports } from "./en-DSH_bhh6.mjs";
7
+ import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-DUk_Kzst.mjs";
8
+ import { n as getControllerOptions, r as getControllerRoute } from "./controller.decorator-DQzenvSN.mjs";
9
+ import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-CdJjpUCW.mjs";
10
+ import "./http-method.decorator-DXwxAfb_.mjs";
11
+ import { n as getRateLimits } from "./rate-limit.decorator--o6Q6p9w.mjs";
12
+ import { n as verifySignedUrl, t as signUrl } from "./signed-url-BQPbv2In.mjs";
13
+ import { t as setupI18nCompiler } from "./setup-CefZKV_e.mjs";
13
14
  import { inject } from "tsyringe";
14
15
  import { createCoreContext, translate } from "@intlify/core-base";
15
16
  import { swaggerUI } from "@hono/swagger-ui";
@@ -730,6 +731,67 @@ function createMiddlewareChain(classes) {
730
731
  };
731
732
  }
732
733
  //#endregion
734
+ //#region src/router/trailing-slash.ts
735
+ /**
736
+ * Apply a trailing-slash mode to a URL or path.
737
+ *
738
+ * - `'ignore'` — return as-is.
739
+ * - `'always'` — append `/` to the pathname unless it already has one.
740
+ * Skipped when the last segment contains `.` (file-like paths) and for the
741
+ * root `/` path.
742
+ * - `'never'` — strip a trailing `/` from the pathname. Skipped for root.
743
+ *
744
+ * Preserves query string and hash. Handles both relative paths
745
+ * (`/foo?x=1`) and absolute URLs (`https://host/foo?x=1`).
746
+ *
747
+ * Used by URL-generation helpers and the redirect middleware so canonical
748
+ * form is computed in one place.
749
+ */
750
+ function applyTrailingSlash(url, mode) {
751
+ if (mode === "ignore") return url;
752
+ const isAbsolute = /^https?:\/\//i.test(url);
753
+ const parsed = isAbsolute ? new URL(url) : new URL(url, "http://placeholder.local");
754
+ const path = parsed.pathname;
755
+ if (path === "/") return url;
756
+ const hasTrailing = path.endsWith("/");
757
+ if (mode === "always" && !hasTrailing) {
758
+ if (path.slice(path.lastIndexOf("/") + 1).includes(".")) return url;
759
+ parsed.pathname = `${path}/`;
760
+ } else if (mode === "never" && hasTrailing) parsed.pathname = path.slice(0, -1);
761
+ else return url;
762
+ return isAbsolute ? parsed.toString() : `${parsed.pathname}${parsed.search}${parsed.hash}`;
763
+ }
764
+ //#endregion
765
+ //#region src/router/middleware/trailing-slash-redirect.ts
766
+ const REDIRECT_STATUS = 308;
767
+ /**
768
+ * Create a Hono middleware that canonicalises trailing slashes via 308 redirects.
769
+ *
770
+ * - `'ignore'` — returns `null`; routes match both `/foo` and `/foo/` natively
771
+ * (Hono handles this when constructed with `strict: false`).
772
+ * - `'always'` — non-trailing requests redirect to the trailing-slash form.
773
+ * Paths whose last segment contains `.` (e.g. `/api/openapi.json`) are skipped.
774
+ * - `'never'` — trailing requests redirect to the non-trailing form.
775
+ *
776
+ * Root (`/`) is always passed through unchanged.
777
+ *
778
+ * 308 is used so that POST/PUT/PATCH bodies survive the redirect.
779
+ *
780
+ * Location headers are emitted as path-relative URIs so the user agent
781
+ * resolves them against the effective request URI — sidestepping scheme
782
+ * mismatches behind HTTPS-terminating proxies that proxy HTTPS pages to an
783
+ * HTTP-speaking backend (which would otherwise produce a mixed-content block).
784
+ */
785
+ function createTrailingSlashRedirect(mode) {
786
+ if (mode === "ignore") return null;
787
+ return async (c, next) => {
788
+ const url = new URL(c.req.url);
789
+ const canonicalPath = applyTrailingSlash(url.pathname, mode);
790
+ if (canonicalPath === url.pathname) return next();
791
+ return c.redirect(`${canonicalPath}${url.search}`, REDIRECT_STATUS);
792
+ };
793
+ }
794
+ //#endregion
733
795
  //#region src/router/decorators/route.decorator.ts
734
796
  /**
735
797
  * Decorator to add OpenAPI metadata to a controller method using convention-based routing.
@@ -1138,10 +1200,12 @@ let RouteRegistrationService = class RouteRegistrationService {
1138
1200
  const controllerOpts = getControllerOptions(ControllerClass);
1139
1201
  const controllerGuards = getControllerGuards(ControllerClass)?.guards ?? [];
1140
1202
  const routerConfig = this.routerResolver?.resolveForController(ControllerClass) ?? { middleware: [] };
1203
+ const classThrottleMiddleware = Array.from(new Set(getRateLimits(ControllerClass).map(createThrottleMiddleware)));
1141
1204
  const basePath = routerConfig.prefix ? this.joinPaths(routerConfig.prefix, controllerRoute) : controllerRoute;
1142
1205
  const effectiveVersion = controllerOpts?.version ?? routerConfig.version;
1143
1206
  const effectiveDomain = controllerOpts?.domain ?? routerConfig.domain;
1144
1207
  if (isWsGateway) {
1208
+ const wsMiddleware = [...routerConfig.middleware, ...classThrottleMiddleware];
1145
1209
  const expandedRoutes = this.registry.register({
1146
1210
  method: "ws",
1147
1211
  basePath,
@@ -1150,10 +1214,10 @@ let RouteRegistrationService = class RouteRegistrationService {
1150
1214
  controller: ControllerClass.name,
1151
1215
  action: "ws",
1152
1216
  hidden: routerConfig.hideFromDocs ?? false,
1153
- middleware: routerConfig.middleware.map((m) => m.name)
1217
+ middleware: wsMiddleware.map((m) => m.name)
1154
1218
  });
1155
1219
  for (const route of expandedRoutes) actions.set(route, () => {
1156
- if (routerConfig.middleware.length > 0) this.app.use(route.path, createMiddlewareChain(routerConfig.middleware));
1220
+ if (wsMiddleware.length > 0) this.app.use(route.path, createMiddlewareChain(wsMiddleware));
1157
1221
  if (effectiveDomain) {
1158
1222
  const domainHandler = createDomainMiddleware(effectiveDomain);
1159
1223
  this.app.use(route.path, domainHandler);
@@ -1167,6 +1231,7 @@ let RouteRegistrationService = class RouteRegistrationService {
1167
1231
  this.controllerClasses.set(className, ControllerClass);
1168
1232
  const prototype = ControllerClass.prototype;
1169
1233
  if (prototype.handle) {
1234
+ const wildcardMiddleware = [...routerConfig.middleware, ...classThrottleMiddleware];
1170
1235
  const expandedRoutes = this.registry.register({
1171
1236
  method: "all",
1172
1237
  basePath,
@@ -1175,10 +1240,10 @@ let RouteRegistrationService = class RouteRegistrationService {
1175
1240
  controller: className,
1176
1241
  action: "handle",
1177
1242
  hidden: routerConfig.hideFromDocs ?? false,
1178
- middleware: routerConfig.middleware.map((m) => m.name)
1243
+ middleware: wildcardMiddleware.map((m) => m.name)
1179
1244
  });
1180
1245
  for (const route of expandedRoutes) actions.set(route, () => {
1181
- if (routerConfig.middleware.length > 0) this.app.use(route.path, createMiddlewareChain(routerConfig.middleware));
1246
+ if (wildcardMiddleware.length > 0) this.app.use(route.path, createMiddlewareChain(wildcardMiddleware));
1182
1247
  this.registerWildcardRoute(ControllerClass, route.path);
1183
1248
  });
1184
1249
  return;
@@ -1204,12 +1269,26 @@ let RouteRegistrationService = class RouteRegistrationService {
1204
1269
  const routerName = routerConfig.name;
1205
1270
  const controllerName = controllerOpts?.name;
1206
1271
  const effectiveNamePrefix = routerName && controllerName ? `${routerName}${controllerName}` : routerName ?? controllerName;
1207
- const middlewareNames = routerConfig.middleware.map((m) => m.name);
1208
1272
  for (const { method: methodName, meta } of methodMetadata) {
1209
1273
  const resolved = this.resolveMethodAndPath(meta, methodName, basePath, className);
1210
1274
  if (!resolved) continue;
1211
- const { httpMethod, fullPath, routeConfig, statusCodeOverride } = resolved;
1212
- if (routerConfig.params) routeConfig.params = routeConfig.params ? routerConfig.params.extend(routeConfig.params.shape) : routerConfig.params;
1275
+ const methodThrottleMiddleware = getRateLimits(prototype, methodName).map(createThrottleMiddleware);
1276
+ const effectiveMiddleware = Array.from(new Set([
1277
+ ...routerConfig.middleware,
1278
+ ...classThrottleMiddleware,
1279
+ ...methodThrottleMiddleware
1280
+ ]));
1281
+ const middlewareNames = effectiveMiddleware.map((m) => m.name);
1282
+ const { httpMethod, fullPath, routeConfig: rawRouteConfig, statusCodeOverride } = resolved;
1283
+ let mergedParams = rawRouteConfig.params;
1284
+ if (routerConfig.params) {
1285
+ const prefixShape = routerConfig.params.shape;
1286
+ mergedParams = mergedParams ? mergedParams.extend(prefixShape) : routerConfig.params.extend({});
1287
+ }
1288
+ const routeConfig = mergedParams === rawRouteConfig.params ? rawRouteConfig : {
1289
+ ...rawRouteConfig,
1290
+ params: mergedParams
1291
+ };
1213
1292
  const hideFromDocs = routeConfig.hideFromDocs ?? routerHidden ?? controllerHidden;
1214
1293
  let routeName;
1215
1294
  if (routeConfig.name) routeName = effectiveNamePrefix ? `${effectiveNamePrefix}${routeConfig.name}` : routeConfig.name;
@@ -1251,7 +1330,7 @@ let RouteRegistrationService = class RouteRegistrationService {
1251
1330
  path: route.path,
1252
1331
  methodName
1253
1332
  });
1254
- if (routerConfig.middleware.length > 0) this.app.use(route.path, createMiddlewareChain(routerConfig.middleware));
1333
+ if (effectiveMiddleware.length > 0) this.app.use(route.path, createMiddlewareChain(effectiveMiddleware));
1255
1334
  if (allGuards.length > 0) this.app.use(route.path, this.createGuardMiddleware(allGuards));
1256
1335
  this.app.all(route.path, handler);
1257
1336
  return;
@@ -1266,7 +1345,7 @@ let RouteRegistrationService = class RouteRegistrationService {
1266
1345
  tags: metadata.tags,
1267
1346
  hidden: route.hidden
1268
1347
  });
1269
- const wrappedHandler = this.wrapHandlerWithChain(handler, routerConfig.middleware, allGuards);
1348
+ const wrappedHandler = this.wrapHandlerWithChain(handler, effectiveMiddleware, allGuards);
1270
1349
  this.app.openapi(openApiRoute, wrappedHandler);
1271
1350
  if (!route.hidden) {
1272
1351
  const { hide: _, ...specRoute } = openApiRoute;
@@ -1629,12 +1708,16 @@ let HonoApp = class HonoApp extends OpenAPIHono {
1629
1708
  * Used by private methods to register middleware without going through the override.
1630
1709
  */
1631
1710
  nativeUse;
1632
- constructor(container, logger) {
1633
- super({ defaultHook: (result, c) => {
1634
- if (!result.success) throw new SchemaValidationError(result.error);
1635
- const override = c.get("validationSuccessResponse");
1636
- if (override) return override;
1637
- } });
1711
+ constructor(container, logger, application) {
1712
+ const trailingSlash = application.config.trailingSlash ?? "ignore";
1713
+ super({
1714
+ strict: false,
1715
+ defaultHook: (result, c) => {
1716
+ if (!result.success) throw new SchemaValidationError(result.error);
1717
+ const override = c.get("validationSuccessResponse");
1718
+ if (override) return override;
1719
+ }
1720
+ });
1638
1721
  this._container = container;
1639
1722
  this._logger = logger;
1640
1723
  this.nativeUse = this.use;
@@ -1649,6 +1732,8 @@ let HonoApp = class HonoApp extends OpenAPIHono {
1649
1732
  }
1650
1733
  return this.nativeUse(...args);
1651
1734
  });
1735
+ const trailingSlashRedirect = createTrailingSlashRedirect(trailingSlash);
1736
+ if (trailingSlashRedirect) this.nativeUse("*", trailingSlashRedirect);
1652
1737
  this.setupRequestScope();
1653
1738
  this.applyGlobalMiddleware();
1654
1739
  }
@@ -1691,7 +1776,12 @@ HonoApp = __decorate([
1691
1776
  Transient(),
1692
1777
  __decorateParam(0, inject(CONTAINER_TOKEN)),
1693
1778
  __decorateParam(1, inject(LOGGER_TOKENS.LoggerService)),
1694
- __decorateMetadata("design:paramtypes", [Object, Object])
1779
+ __decorateParam(2, inject(DI_TOKENS.Application)),
1780
+ __decorateMetadata("design:paramtypes", [
1781
+ Object,
1782
+ Object,
1783
+ Object
1784
+ ])
1695
1785
  ], HonoApp);
1696
1786
  //#endregion
1697
1787
  //#region src/router/services/locale-path.service.ts
@@ -1840,10 +1930,21 @@ VersioningService = __decorate([
1840
1930
  ], VersioningService);
1841
1931
  //#endregion
1842
1932
  //#region src/router/route-registry.ts
1933
+ const CONCRETE_HTTP_METHODS = [
1934
+ "get",
1935
+ "post",
1936
+ "put",
1937
+ "delete",
1938
+ "patch",
1939
+ "head",
1940
+ "options",
1941
+ "trace"
1942
+ ];
1843
1943
  let RouteRegistry = class RouteRegistry {
1844
1944
  routes = [];
1845
1945
  namedRoutes = /* @__PURE__ */ new Map();
1846
1946
  _sortedCache = null;
1947
+ _routeToNameCache = null;
1847
1948
  constructor(versioningService, localePathService) {
1848
1949
  this.versioningService = versioningService;
1849
1950
  this.localePathService = localePathService;
@@ -1894,6 +1995,7 @@ let RouteRegistry = class RouteRegistry {
1894
1995
  }
1895
1996
  }
1896
1997
  this._sortedCache = null;
1998
+ this._routeToNameCache = null;
1897
1999
  return expandedRoutes;
1898
2000
  }
1899
2001
  /** Get a named route by name */
@@ -1904,6 +2006,25 @@ let RouteRegistry = class RouteRegistry {
1904
2006
  has(name) {
1905
2007
  return this.namedRoutes.has(name);
1906
2008
  }
2009
+ /**
2010
+ * Resolve a Hono-style route path pattern (e.g. as exposed by `c.req.routePath`)
2011
+ * back to its registered name, scoped to the request's HTTP method. Locale variant
2012
+ * paths resolve to the canonical primary route name. Method matching is
2013
+ * case-insensitive; routes registered with `'all'` resolve under any verb.
2014
+ */
2015
+ findNameByRoute(method, path) {
2016
+ this._routeToNameCache ??= this.buildRouteToNameCache();
2017
+ return this._routeToNameCache.get(`${method.toLowerCase()}:${path}`);
2018
+ }
2019
+ buildRouteToNameCache() {
2020
+ const cache = /* @__PURE__ */ new Map();
2021
+ for (const route of this.namedRoutes.values()) {
2022
+ const methods = route.method === "all" ? CONCRETE_HTTP_METHODS : [route.method];
2023
+ const paths = route.localePaths ? [route.path, ...route.localePaths] : [route.path];
2024
+ for (const m of methods) for (const p of paths) cache.set(`${m}:${p}`, route.name);
2025
+ }
2026
+ return cache;
2027
+ }
1907
2028
  /** Get all routes sorted by specificity (static > param > wildcard, primary before locale) */
1908
2029
  all() {
1909
2030
  this._sortedCache ??= sortRoutesBySpecificity(this.routes);
@@ -1923,6 +2044,17 @@ RouteRegistry = __decorate([
1923
2044
  //#endregion
1924
2045
  //#region src/router/uri.ts
1925
2046
  /**
2047
+ * Encode a value for use as a path parameter.
2048
+ *
2049
+ * Splits on `/` and encodes each segment with `encodeURIComponent`, so callers
2050
+ * can pass slash-containing values for catch-all params (e.g. `:slug{.+}`) and
2051
+ * still get a usable URL — `'auth/login'` becomes `'auth/login'`, not
2052
+ * `'auth%2Flogin'`. Single segments behave exactly like `encodeURIComponent`.
2053
+ */
2054
+ function encodePathParam(value) {
2055
+ return value.split("/").map(encodeURIComponent).join("/");
2056
+ }
2057
+ /**
1926
2058
  * Build a URL from a registered route, filling path/domain params and appending extras as query string.
1927
2059
  *
1928
2060
  * Pure function — no request context needed. Used by both the `Uri` class and the standalone `route()` function.
@@ -1945,7 +2077,7 @@ function buildRouteUrl(route, name, params) {
1945
2077
  for (const paramName of route.paramNames) {
1946
2078
  const value = allParams[paramName];
1947
2079
  if (value === void 0) throw new MissingRouteParamError(paramName, name, route.path);
1948
- url = url.replace(new RegExp(`:${paramName}(\\{[^}]*\\})?`), encodeURIComponent(value));
2080
+ url = url.replace(new RegExp(`:${paramName}(\\{[^}]*\\})?`), encodePathParam(value));
1949
2081
  consumedKeys.add(paramName);
1950
2082
  }
1951
2083
  let domain;
@@ -1968,9 +2100,11 @@ function buildRouteUrl(route, name, params) {
1968
2100
  }
1969
2101
  let Uri = class Uri {
1970
2102
  _defaults = {};
1971
- constructor(registry, routerContext) {
2103
+ trailingSlash;
2104
+ constructor(registry, routerContext, application) {
1972
2105
  this.registry = registry;
1973
2106
  this.routerContext = routerContext;
2107
+ this.trailingSlash = application.config.trailingSlash ?? "ignore";
1974
2108
  }
1975
2109
  /**
1976
2110
  * Set default URL parameters for this request.
@@ -1985,6 +2119,16 @@ let Uri = class Uri {
1985
2119
  };
1986
2120
  }
1987
2121
  /**
2122
+ * Read the currently configured default URL parameters.
2123
+ *
2124
+ * Used by frameworks that need to share these with the client (e.g. the
2125
+ * Inertia adapter ships them as a shared prop so `route()` calls in the
2126
+ * browser auto-fill the same sticky params as the server).
2127
+ */
2128
+ getDefaults() {
2129
+ return { ...this._defaults };
2130
+ }
2131
+ /**
1988
2132
  * Generate a URL from a named route.
1989
2133
  *
1990
2134
  * Keys matching `:param` placeholders fill the path.
@@ -2003,10 +2147,10 @@ let Uri = class Uri {
2003
2147
  route(name, params, options) {
2004
2148
  const registeredRoute = this.registry.get(name);
2005
2149
  if (!registeredRoute) throw new RouteNameNotFoundError(name);
2006
- let url = buildRouteUrl(registeredRoute, name, {
2150
+ let url = applyTrailingSlash(buildRouteUrl(registeredRoute, name, {
2007
2151
  ...this._defaults,
2008
2152
  ...params
2009
- });
2153
+ }), this.trailingSlash);
2010
2154
  if (options?.absolute && !url.startsWith("http")) url = `${new URL(this.routerContext.c.req.url).origin}${url}`;
2011
2155
  return url;
2012
2156
  }
@@ -2054,14 +2198,14 @@ let Uri = class Uri {
2054
2198
  * Get the current request URL pathname (without query string).
2055
2199
  */
2056
2200
  current() {
2057
- return new URL(this.routerContext.c.req.url).pathname;
2201
+ return applyTrailingSlash(new URL(this.routerContext.c.req.url).pathname, this.trailingSlash);
2058
2202
  }
2059
2203
  /**
2060
2204
  * Get the current request URL with query string (pathname + search).
2061
2205
  */
2062
2206
  full() {
2063
2207
  const parsed = new URL(this.routerContext.c.req.url);
2064
- return `${parsed.pathname}${parsed.search}`;
2208
+ return applyTrailingSlash(`${parsed.pathname}${parsed.search}`, this.trailingSlash);
2065
2209
  }
2066
2210
  /**
2067
2211
  * Get the previous request URL from the Referer header.
@@ -2093,7 +2237,7 @@ let Uri = class Uri {
2093
2237
  * @param options - URL generation options
2094
2238
  */
2095
2239
  to(path, queryParams, options) {
2096
- let url = path;
2240
+ let url = applyTrailingSlash(path, this.trailingSlash);
2097
2241
  if (queryParams) {
2098
2242
  const entries = Object.entries(queryParams);
2099
2243
  if (entries.length > 0) {
@@ -2113,7 +2257,7 @@ let Uri = class Uri {
2113
2257
  query(path, queryParams) {
2114
2258
  const parsed = new URL(path, "https://placeholder.local");
2115
2259
  for (const [key, value] of Object.entries(queryParams)) parsed.searchParams.set(key, value);
2116
- return `${parsed.pathname}${parsed.search}`;
2260
+ return applyTrailingSlash(`${parsed.pathname}${parsed.search}`, this.trailingSlash);
2117
2261
  }
2118
2262
  getAppSecret() {
2119
2263
  const secret = this.routerContext.c.env.APP_SECRET;
@@ -2125,7 +2269,12 @@ Uri = __decorate([
2125
2269
  Transient(),
2126
2270
  __decorateParam(0, inject(ROUTER_TOKENS.RouteRegistry)),
2127
2271
  __decorateParam(1, inject(ROUTER_TOKENS.RouterContext)),
2128
- __decorateMetadata("design:paramtypes", [Object, Object])
2272
+ __decorateParam(2, inject(DI_TOKENS.Application)),
2273
+ __decorateMetadata("design:paramtypes", [
2274
+ Object,
2275
+ Object,
2276
+ Object
2277
+ ])
2129
2278
  ], Uri);
2130
2279
  //#endregion
2131
2280
  //#region src/router/route-url.ts
@@ -2155,9 +2304,12 @@ Uri = __decorate([
2155
2304
  * ```
2156
2305
  */
2157
2306
  function route(name, params) {
2158
- const registeredRoute = getContainer().resolve(ROUTER_TOKENS.RouteRegistry).get(name);
2307
+ const container = getContainer();
2308
+ const registry = container.resolve(ROUTER_TOKENS.RouteRegistry);
2309
+ const application = container.resolve(DI_TOKENS.Application);
2310
+ const registeredRoute = registry.get(name);
2159
2311
  if (!registeredRoute) throw new RouteNameNotFoundError(name);
2160
- return buildRouteUrl(registeredRoute, name, params);
2312
+ return applyTrailingSlash(buildRouteUrl(registeredRoute, name, params), application.config.trailingSlash ?? "ignore");
2161
2313
  }
2162
2314
  //#endregion
2163
2315
  //#region src/router/middleware/verify-signature.middleware.ts
@@ -2337,4 +2489,4 @@ I18nModule = _I18nModule = __decorate([Module({ providers: [
2337
2489
  //#endregion
2338
2490
  export { OpenAPIModule as A, LocaleNotSupportedError as B, validationErrorResponseSchema as C, createMiddlewareChain as D, getRouteMetadata as E, getMessages as F, I18nContextMiddleware as H, messages as I, buildDetectorOptions as L, MessageRegistry as M, MessageLoaderService as N, createDomainMiddleware as O, getLocales as P, resolveI18nOptions as R, uuidParamSchema as S, getRouteDecoratedMethods as T, OpenAPIConfigService as V, commonErrorSchemas as _, buildRouteUrl as a, paginationQuerySchema as b, LocalePathService as c, extractDomainParamNames as d, extractParamNames as f, toOpenAPIPath as g, sortRoutesBySpecificity as h, Uri as i, OpenAPIService as j, parseDomainPattern as k, HonoApp as l, getPathSpecificityScore as m, VerifySignatureMiddleware as n, RouteRegistry as o, generateConventionRouteName as p, route as r, VersioningService as s, I18nModule as t, RouteRegistrationService as u, errorResponseSchema as v, Route as w, successMessageSchema as x, paginatedResponseSchema as y, TranslationMissingError as z };
2339
2491
 
2340
- //# sourceMappingURL=i18n.module-CI_prYFD.mjs.map
2492
+ //# sourceMappingURL=i18n.module-BBlNNlcG.mjs.map