stratal 0.0.21 → 0.0.22
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/README.md +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs → base-email.provider-BWZHIjt8.mjs} +1 -1
- package/dist/{base-email.provider-CfQCA08m.mjs.map → base-email.provider-BWZHIjt8.mjs.map} +1 -1
- package/dist/bin/quarry.mjs +46 -109
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +6 -46
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +20 -67
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{cache.service-DsnKuNyO.d.mts → cache.service-e34gV6tz.d.mts} +8 -8
- package/dist/{cache.service-DsnKuNyO.d.mts.map → cache.service-e34gV6tz.d.mts.map} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs → cache.tokens-ovi_c52J.mjs} +1 -1
- package/dist/{cache.tokens-B7Rw1C9Q.mjs.map → cache.tokens-ovi_c52J.mjs.map} +1 -1
- package/dist/{colors-DJaRDXoS.mjs → colors-axmupKdp.mjs} +1 -1
- package/dist/{colors-DJaRDXoS.mjs.map → colors-axmupKdp.mjs.map} +1 -1
- package/dist/{command-BgSlsS4M.mjs → command-BU4ApTo5.mjs} +2 -3
- package/dist/command-BU4ApTo5.mjs.map +1 -0
- package/dist/{command-Cmmf0oHX.d.mts → command-wXfvHbBZ.d.mts} +3 -2
- package/dist/command-wXfvHbBZ.d.mts.map +1 -0
- package/dist/config/index.d.mts +24 -11
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +31 -57
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts → consumer-registry-DHQtypr1.d.mts} +1 -1
- package/dist/{consumer-registry-B7yUNh0q.d.mts.map → consumer-registry-DHQtypr1.d.mts.map} +1 -1
- package/dist/container-storage-GpNNz79X.mjs +52 -0
- package/dist/container-storage-GpNNz79X.mjs.map +1 -0
- package/dist/{controller.decorator-B9vwn0zK.mjs → controller.decorator-DIUazNU7.mjs} +8 -8
- package/dist/controller.decorator-DIUazNU7.mjs.map +1 -0
- package/dist/cron/index.d.mts +26 -5
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -1
- package/dist/{cron-manager-DQSK8uoV.mjs → cron-manager-9bpN9bu4.mjs} +35 -15
- package/dist/cron-manager-9bpN9bu4.mjs.map +1 -0
- package/dist/{cron-manager-CmTimEjf.d.mts → cron-manager-CSTIBPcM.d.mts} +6 -13
- package/dist/cron-manager-CSTIBPcM.d.mts.map +1 -0
- package/dist/decorate-HgTKAYK8.mjs +16 -0
- package/dist/deep-merge-C8NgcXw4.mjs +18 -0
- package/dist/deep-merge-C8NgcXw4.mjs.map +1 -0
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +4 -3
- package/dist/di-BO1QIb5H.mjs +415 -0
- package/dist/di-BO1QIb5H.mjs.map +1 -0
- package/dist/email/index.d.mts +14 -89
- package/dist/email/index.d.mts.map +1 -1
- package/dist/email/index.mjs +25 -125
- package/dist/email/index.mjs.map +1 -1
- package/dist/en-BPP6h6y5.mjs +202 -0
- package/dist/en-BPP6h6y5.mjs.map +1 -0
- package/dist/{env-D1rcZ8_r.d.mts → env-DKSbuBi5.d.mts} +1 -1
- package/dist/env-DKSbuBi5.d.mts.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +4 -2
- package/dist/errors-BBZTnjdq.mjs +333 -0
- package/dist/errors-BBZTnjdq.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +1 -1
- package/dist/{events-CzCV8jI8.mjs → events-D1KdDaiP.mjs} +11 -11
- package/dist/events-D1KdDaiP.mjs.map +1 -0
- package/dist/exception-context-B4kM-M53.mjs +429 -0
- package/dist/exception-context-B4kM-M53.mjs.map +1 -0
- package/dist/{gateway-context-CXmXtaUP.mjs → gateway-context-CFe6a9gz.mjs} +19 -31
- package/dist/gateway-context-CFe6a9gz.mjs.map +1 -0
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DU1_J9YA.mjs → guards-Ced-uNIF.mjs} +6 -5
- package/dist/guards-Ced-uNIF.mjs.map +1 -0
- package/dist/{http-method.decorator-BrgHMdLQ.mjs → http-method.decorator-CdjKFJZZ.mjs} +7 -6
- package/dist/http-method.decorator-CdjKFJZZ.mjs.map +1 -0
- package/dist/i18n/index.d.mts +238 -3
- package/dist/i18n/index.d.mts.map +1 -0
- package/dist/i18n/index.mjs +39 -3
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/i18n/messages/en/index.d.mts +2 -2
- package/dist/i18n/messages/en/index.mjs +2 -2
- package/dist/i18n/utils/index.d.mts +4 -26
- package/dist/i18n/utils/index.d.mts.map +1 -1
- package/dist/i18n/utils/index.mjs +2 -2
- package/dist/i18n/validation/index.d.mts +3 -2
- package/dist/i18n/validation/index.mjs +4 -2
- package/dist/i18n.module-BlXrtAlV.mjs +219 -0
- package/dist/i18n.module-BlXrtAlV.mjs.map +1 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs +19 -0
- package/dist/i18n.tokens-hwRpmjRq.mjs.map +1 -0
- package/dist/{index-7-hU3GTV.d.mts → index-B4UBK-2T.d.mts} +1 -1
- package/dist/{index-7-hU3GTV.d.mts.map → index-B4UBK-2T.d.mts.map} +1 -1
- package/dist/index-BtlE9RuO.d.mts +124 -0
- package/dist/index-BtlE9RuO.d.mts.map +1 -0
- package/dist/{index-DUzWs0z7.d.mts → index-CW1YHSft.d.mts} +71 -167
- package/dist/index-CW1YHSft.d.mts.map +1 -0
- package/dist/{index-ByOyTmqf.d.mts → index-DEncMcC6.d.mts} +554 -2237
- package/dist/index-DEncMcC6.d.mts.map +1 -0
- package/dist/index-Dj5IMwtr.d.mts +44 -0
- package/dist/index-Dj5IMwtr.d.mts.map +1 -0
- package/dist/{index-C1KvMncZ.d.mts → index-KMgSCSM7.d.mts} +3 -108
- package/dist/index-KMgSCSM7.d.mts.map +1 -0
- package/dist/index.d.mts +5 -43
- package/dist/index.mjs +1 -1
- package/dist/{is-command-C6a7WTPw.mjs → is-command-CX5rAfZW.mjs} +2 -2
- package/dist/{is-command-C6a7WTPw.mjs.map → is-command-CX5rAfZW.mjs.map} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-CYCtELlm.mjs} +1 -1
- package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-CYCtELlm.mjs.map} +1 -1
- package/dist/logger/index.d.mts +2 -2
- package/dist/logger/index.mjs +170 -2
- package/dist/logger/index.mjs.map +1 -0
- package/dist/macroable/index.d.mts +1 -1
- package/dist/macroable/index.mjs +1 -1
- package/dist/{macroable-BmufBshB.mjs → macroable-DzlfzT50.mjs} +1 -1
- package/dist/{macroable-BmufBshB.mjs.map → macroable-DzlfzT50.mjs.map} +1 -1
- package/dist/metadata-BVkc4aUu.mjs +39 -0
- package/dist/metadata-BVkc4aUu.mjs.map +1 -0
- package/dist/module/index.d.mts +6 -24
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +2 -2
- package/dist/module-xYoHba6B.mjs +422 -0
- package/dist/module-xYoHba6B.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.d.mts.map +1 -1
- package/dist/openapi/index.mjs +1 -2
- package/dist/openapi-C6lm0RmV.mjs +483 -0
- package/dist/openapi-C6lm0RmV.mjs.map +1 -0
- package/dist/{openapi.service-Bt9bCIrd.d.mts → openapi.service-CrLlsXAd.d.mts} +3 -3
- package/dist/openapi.service-CrLlsXAd.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +5 -163
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +5 -5
- package/dist/quarry/runner.d.mts +184 -0
- package/dist/quarry/runner.d.mts.map +1 -0
- package/dist/quarry/runner.mjs +775 -0
- package/dist/quarry/runner.mjs.map +1 -0
- package/dist/quarry-registry-D4hIGScf.d.mts +69 -0
- package/dist/quarry-registry-D4hIGScf.d.mts.map +1 -0
- package/dist/quarry-registry-DkraZNwn.mjs +311 -0
- package/dist/quarry-registry-DkraZNwn.mjs.map +1 -0
- package/dist/queue/index.d.mts +3 -3
- package/dist/queue/index.mjs +26 -28
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BhCjZp6H.mjs → queue.module-DeWJ0tQM.mjs} +59 -113
- package/dist/queue.module-DeWJ0tQM.mjs.map +1 -0
- package/dist/{r2-storage.provider-DuonKeYm.mjs → r2-storage.provider-Hfm6LdZQ.mjs} +5 -5
- package/dist/r2-storage.provider-Hfm6LdZQ.mjs.map +1 -0
- package/dist/{rate-limit.decorator-6qzNcSOt.mjs → rate-limit.decorator-D69zdZbp.mjs} +6 -11
- package/dist/rate-limit.decorator-D69zdZbp.mjs.map +1 -0
- package/dist/rate-limiter/index.d.mts +11 -50
- package/dist/rate-limiter/index.d.mts.map +1 -1
- package/dist/rate-limiter/index.mjs +16 -30
- package/dist/rate-limiter/index.mjs.map +1 -1
- package/dist/{resend.provider-DB4IlFjG.mjs → resend.provider-Ur6tU7fK.mjs} +7 -7
- package/dist/resend.provider-Ur6tU7fK.mjs.map +1 -0
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +8 -7
- package/dist/{i18n.module-CzXLW9Hy.mjs → router-Cy6DjkvP.mjs} +171 -851
- package/dist/router-Cy6DjkvP.mjs.map +1 -0
- package/dist/seeder/index.d.mts +6 -11
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +3 -3
- package/dist/{seeder-zoEfEw9i.mjs → seeder-BADTig4n.mjs} +14 -22
- package/dist/seeder-BADTig4n.mjs.map +1 -0
- package/dist/{signed-url-BQPbv2In.mjs → signed-url-BqUqt5dF.mjs} +1 -1
- package/dist/{signed-url-BQPbv2In.mjs.map → signed-url-BqUqt5dF.mjs.map} +1 -1
- package/dist/{smtp.provider-B6D7zuWX.mjs → smtp.provider-C129sNBT.mjs} +6 -6
- package/dist/smtp.provider-C129sNBT.mjs.map +1 -0
- package/dist/storage/index.d.mts +15 -39
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +3 -3
- package/dist/storage/providers/index.d.mts +2 -2
- package/dist/storage/providers/index.mjs +1 -1
- package/dist/{storage-D8CBP72Z.mjs → storage-BA3ppVYM.mjs} +65 -59
- package/dist/storage-BA3ppVYM.mjs.map +1 -0
- package/dist/{storage-provider.interface-Bd6vA4ak.d.mts → storage-provider.interface-DQMtT42e.d.mts} +2 -3
- package/dist/storage-provider.interface-DQMtT42e.d.mts.map +1 -0
- package/dist/storage.error-C6FY037a.mjs +8 -0
- package/dist/storage.error-C6FY037a.mjs.map +1 -0
- package/dist/{stratal-CNwpbSZl.mjs → stratal-Bdq4IdB3.mjs} +31 -185
- package/dist/stratal-Bdq4IdB3.mjs.map +1 -0
- package/dist/stratal-BsKmvP6J.d.mts +43 -0
- package/dist/stratal-BsKmvP6J.d.mts.map +1 -0
- package/dist/{types-cySNS_lp.d.mts → types-BaeHi67f.d.mts} +1 -1
- package/dist/types-BaeHi67f.d.mts.map +1 -0
- package/dist/{usage-generator-BUdlhnCK.mjs → usage-generator-DTqaUMR9.mjs} +6 -3
- package/dist/usage-generator-DTqaUMR9.mjs.map +1 -0
- package/dist/validation-DUzcjb8Q.mjs +49 -0
- package/dist/validation-DUzcjb8Q.mjs.map +1 -0
- package/dist/validation.context-XTysWJ3b.mjs +117 -0
- package/dist/validation.context-XTysWJ3b.mjs.map +1 -0
- package/dist/websocket/index.d.mts +7 -14
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +2 -2
- package/dist/workers/index.d.mts +2 -2
- package/dist/workers/index.mjs +3 -2
- package/dist/workers/index.mjs.map +1 -1
- package/dist/{index-Bnpfq6uk.d.mts → zod-DvWTfRpI.d.mts} +58 -133
- package/dist/zod-DvWTfRpI.d.mts.map +1 -0
- package/dist/zod-hMa3rSHV.mjs +72 -0
- package/dist/zod-hMa3rSHV.mjs.map +1 -0
- package/package.json +10 -10
- package/dist/command-BgSlsS4M.mjs.map +0 -1
- package/dist/command-Cmmf0oHX.d.mts.map +0 -1
- package/dist/controller.decorator-B9vwn0zK.mjs.map +0 -1
- package/dist/cron-manager-CmTimEjf.d.mts.map +0 -1
- package/dist/cron-manager-DQSK8uoV.mjs.map +0 -1
- package/dist/en-DSH_bhh6.mjs +0 -308
- package/dist/en-DSH_bhh6.mjs.map +0 -1
- package/dist/env-D1rcZ8_r.d.mts.map +0 -1
- package/dist/errors-COW9-Mar.mjs +0 -1739
- package/dist/errors-COW9-Mar.mjs.map +0 -1
- package/dist/errors-ORxu1-Bb.mjs +0 -74
- package/dist/errors-ORxu1-Bb.mjs.map +0 -1
- package/dist/events-CzCV8jI8.mjs.map +0 -1
- package/dist/gateway-context-CXmXtaUP.mjs.map +0 -1
- package/dist/guards-DU1_J9YA.mjs.map +0 -1
- package/dist/http-method.decorator-BrgHMdLQ.mjs.map +0 -1
- package/dist/i18n.module-CzXLW9Hy.mjs.map +0 -1
- package/dist/index-Bnpfq6uk.d.mts.map +0 -1
- package/dist/index-ByOyTmqf.d.mts.map +0 -1
- package/dist/index-C1KvMncZ.d.mts.map +0 -1
- package/dist/index-DBd_2wv8.d.mts +0 -263
- package/dist/index-DBd_2wv8.d.mts.map +0 -1
- package/dist/index-DUzWs0z7.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/logger-DlV7NtvD.mjs +0 -440
- package/dist/logger-DlV7NtvD.mjs.map +0 -1
- package/dist/module-BzLg57FK.mjs +0 -866
- package/dist/module-BzLg57FK.mjs.map +0 -1
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs +0 -200
- package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +0 -1
- package/dist/openapi.service-Bt9bCIrd.d.mts.map +0 -1
- package/dist/quarry-registry-BwY2hOxm.mjs +0 -699
- package/dist/quarry-registry-BwY2hOxm.mjs.map +0 -1
- package/dist/queue.module-BhCjZp6H.mjs.map +0 -1
- package/dist/r2-storage.provider-DuonKeYm.mjs.map +0 -1
- package/dist/rate-limit.decorator-6qzNcSOt.mjs.map +0 -1
- package/dist/resend.provider-DB4IlFjG.mjs.map +0 -1
- package/dist/seeder-zoEfEw9i.mjs.map +0 -1
- package/dist/setup-CefZKV_e.mjs +0 -37
- package/dist/setup-CefZKV_e.mjs.map +0 -1
- package/dist/smtp.provider-B6D7zuWX.mjs.map +0 -1
- package/dist/storage-D8CBP72Z.mjs.map +0 -1
- package/dist/storage-provider.interface-Bd6vA4ak.d.mts.map +0 -1
- package/dist/stratal-CNwpbSZl.mjs.map +0 -1
- package/dist/types-cySNS_lp.d.mts.map +0 -1
- package/dist/usage-generator-BUdlhnCK.mjs.map +0 -1
- package/dist/validation-DtJwAv7O.mjs +0 -248
- package/dist/validation-DtJwAv7O.mjs.map +0 -1
|
@@ -1,604 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import "./
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
1
|
+
import { n as getContainer, r as runWithContainer } from "./container-storage-GpNNz79X.mjs";
|
|
2
|
+
import { _ as getMethodInjections, c as DI_TOKENS, l as Request, m as inject, o as ROUTER_TOKENS, s as CONTAINER_TOKEN, u as Singleton } from "./di-BO1QIb5H.mjs";
|
|
3
|
+
import { n as getMetadata, t as defineMetadata } from "./metadata-BVkc4aUu.mjs";
|
|
4
|
+
import { n as __decorateParam, t as __decorate } from "./decorate-HgTKAYK8.mjs";
|
|
5
|
+
import { LOGGER_TOKENS } from "./logger/index.mjs";
|
|
6
|
+
import { d as abort, u as HttpException } from "./errors-BBZTnjdq.mjs";
|
|
7
|
+
import { a as RouterContext, c as METHOD_STATUS_CODES, d as SECURITY_SCHEMES, f as VERSION_NEUTRAL, l as ROUTER_CONTEXT_KEYS, o as DEFAULT_CONTENT_TYPE, r as createHttpExceptionContext, s as HTTP_METHODS, u as ROUTE_METADATA_KEYS } from "./exception-context-B4kM-M53.mjs";
|
|
8
|
+
import { o as RouterError, s as createThrottleMiddleware } from "./module-xYoHba6B.mjs";
|
|
9
|
+
import { t as I18N_TOKENS } from "./i18n.tokens-hwRpmjRq.mjs";
|
|
10
|
+
import { i as zod_exports, r as z, t as OpenAPIHono } from "./zod-hMa3rSHV.mjs";
|
|
11
|
+
import { a as OPENAPI_TOKENS } from "./openapi-C6lm0RmV.mjs";
|
|
12
|
+
import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-Ced-uNIF.mjs";
|
|
13
|
+
import { n as getRateLimits } from "./rate-limit.decorator-D69zdZbp.mjs";
|
|
14
|
+
import { n as getControllerOptions, r as getControllerRoute } from "./controller.decorator-DIUazNU7.mjs";
|
|
15
|
+
import { a as getWsOnCloseMethod, o as getWsOnErrorMethod, s as getWsOnMessageMethod, t as GatewayContext, u as isGateway } from "./gateway-context-CFe6a9gz.mjs";
|
|
16
|
+
import "./http-method.decorator-CdjKFJZZ.mjs";
|
|
17
|
+
import { n as verifySignedUrl, t as signUrl } from "./signed-url-BqUqt5dF.mjs";
|
|
17
18
|
import { languageDetector } from "hono/language";
|
|
18
|
-
//#region src/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
i18n;
|
|
27
|
-
constructor(i18n) {
|
|
28
|
-
this.i18n = i18n;
|
|
29
|
-
}
|
|
30
|
-
async handle(ctx, next) {
|
|
31
|
-
await runWithErrorMapContext({
|
|
32
|
-
t: (key, params) => this.i18n.t(key, params),
|
|
33
|
-
locale: ctx.getLocale()
|
|
34
|
-
}, () => next());
|
|
19
|
+
//#region src/router/errors/route-not-found.error.ts
|
|
20
|
+
var RouteNotFoundError = class extends HttpException {
|
|
21
|
+
path;
|
|
22
|
+
method;
|
|
23
|
+
constructor(path, method) {
|
|
24
|
+
super(404, `Route not found: ${method} ${path}`);
|
|
25
|
+
this.path = path;
|
|
26
|
+
this.method = method;
|
|
35
27
|
}
|
|
36
28
|
};
|
|
37
|
-
I18nContextMiddleware = __decorate([
|
|
38
|
-
Transient(),
|
|
39
|
-
__decorateParam(0, inject(I18N_TOKENS.I18nService)),
|
|
40
|
-
__decorateMetadata("design:paramtypes", [Object])
|
|
41
|
-
], I18nContextMiddleware);
|
|
42
29
|
//#endregion
|
|
43
|
-
//#region src/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
override(config) {
|
|
55
|
-
this.overrides.push(config);
|
|
56
|
-
}
|
|
57
|
-
/** Get effective configuration (base merged with all overrides) */
|
|
58
|
-
getEffectiveConfig() {
|
|
59
|
-
let effective = {
|
|
60
|
-
jsonPath: this.baseOptions?.jsonPath ?? "/api/openapi.json",
|
|
61
|
-
ui: this.baseOptions?.ui,
|
|
62
|
-
info: {
|
|
63
|
-
title: this.baseOptions?.info?.title ?? "API",
|
|
64
|
-
version: this.baseOptions?.info?.version ?? "1.0.0",
|
|
65
|
-
description: this.baseOptions?.info?.description
|
|
66
|
-
},
|
|
67
|
-
securitySchemes: this.baseOptions?.securitySchemes
|
|
68
|
-
};
|
|
69
|
-
for (const override of this.overrides) effective = this.mergeConfig(effective, override);
|
|
70
|
-
return effective;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Merge override into effective config.
|
|
74
|
-
* Info is shallow-merged, routeFilter is replaced.
|
|
75
|
-
*/
|
|
76
|
-
mergeConfig(base, override) {
|
|
77
|
-
return {
|
|
78
|
-
...base,
|
|
79
|
-
info: {
|
|
80
|
-
...base.info,
|
|
81
|
-
...override.info && {
|
|
82
|
-
title: override.info.title ?? base.info.title,
|
|
83
|
-
version: override.info.version ?? base.info.version,
|
|
84
|
-
description: override.info.description ?? base.info.description
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
routeFilter: override.routeFilter ?? base.routeFilter
|
|
88
|
-
};
|
|
30
|
+
//#region src/router/errors/schema-validation.error.ts
|
|
31
|
+
var SchemaValidationError = class extends HttpException {
|
|
32
|
+
issues;
|
|
33
|
+
constructor(zodError) {
|
|
34
|
+
super(400, "Schema validation failed");
|
|
35
|
+
this.issues = zodError.issues.map((err) => ({
|
|
36
|
+
path: err.path.join("."),
|
|
37
|
+
message: err.message,
|
|
38
|
+
code: err.code
|
|
39
|
+
}));
|
|
89
40
|
}
|
|
90
41
|
};
|
|
91
|
-
OpenAPIConfigService = __decorate([
|
|
92
|
-
Transient(OPENAPI_TOKENS.ConfigService),
|
|
93
|
-
__decorateParam(0, inject(OPENAPI_TOKENS.Options, { isOptional: true })),
|
|
94
|
-
__decorateMetadata("design:paramtypes", [Object])
|
|
95
|
-
], OpenAPIConfigService);
|
|
96
42
|
//#endregion
|
|
97
|
-
//#region src/
|
|
43
|
+
//#region src/router/errors/index.ts
|
|
98
44
|
/**
|
|
99
|
-
*
|
|
100
|
-
* Thrown when an unsupported locale is requested
|
|
45
|
+
* Error thrown when a signed URL has an invalid or expired signature.
|
|
101
46
|
*
|
|
102
|
-
* HTTP Status:
|
|
103
|
-
* Error Code: 9301
|
|
47
|
+
* HTTP Status: 403 Forbidden
|
|
104
48
|
*/
|
|
105
|
-
var
|
|
106
|
-
constructor(
|
|
107
|
-
super(
|
|
108
|
-
locale,
|
|
109
|
-
supportedLocales: supportedLocales.join(", ")
|
|
110
|
-
});
|
|
49
|
+
var InvalidSignatureError = class extends HttpException {
|
|
50
|
+
constructor() {
|
|
51
|
+
super(403, "Invalid or expired signature");
|
|
111
52
|
}
|
|
112
53
|
};
|
|
113
|
-
//#endregion
|
|
114
|
-
//#region src/i18n/errors/translation-missing.error.ts
|
|
115
54
|
/**
|
|
116
|
-
*
|
|
117
|
-
* Thrown when a translation key is missing from all locales
|
|
55
|
+
* ResponseValidationError
|
|
118
56
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
57
|
+
* Thrown when a controller's response body does not match the declared Zod response schema.
|
|
58
|
+
* Indicates a server-side schema mismatch — the controller is returning data that
|
|
59
|
+
* violates its own API contract.
|
|
121
60
|
*/
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
61
|
+
var ResponseValidationError = class extends HttpException {
|
|
62
|
+
issues;
|
|
63
|
+
constructor(zodError) {
|
|
64
|
+
super(500, "Response validation failed");
|
|
65
|
+
this.issues = zodError.issues.map((err) => ({
|
|
66
|
+
path: err.path.join("."),
|
|
67
|
+
message: err.message,
|
|
68
|
+
code: err.code
|
|
69
|
+
}));
|
|
128
70
|
}
|
|
129
71
|
};
|
|
130
72
|
//#endregion
|
|
131
|
-
//#region src/i18n/i18n.options.ts
|
|
132
|
-
/**
|
|
133
|
-
* Resolve I18n options with defaults
|
|
134
|
-
*/
|
|
135
|
-
function resolveI18nOptions(options) {
|
|
136
|
-
const detection = options?.detection;
|
|
137
|
-
const enabled = detection ? detection.enabled !== false : true;
|
|
138
|
-
const strategy = detection && "strategy" in detection ? detection.strategy ?? "cookie" : "cookie";
|
|
139
|
-
const prefixDefaultLocale = detection && "prefixDefaultLocale" in detection && detection.prefixDefaultLocale !== void 0 ? detection.prefixDefaultLocale : false;
|
|
140
|
-
return {
|
|
141
|
-
defaultLocale: options?.defaultLocale ?? "en",
|
|
142
|
-
fallbackLocale: options?.fallbackLocale ?? "en",
|
|
143
|
-
locales: options?.locales ?? ["en"],
|
|
144
|
-
detection: {
|
|
145
|
-
enabled,
|
|
146
|
-
strategy,
|
|
147
|
-
prefixDefaultLocale
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Build Hono languageDetector options from I18n module options
|
|
153
|
-
*/
|
|
154
|
-
function buildDetectorOptions(options) {
|
|
155
|
-
const resolved = resolveI18nOptions(options);
|
|
156
|
-
const strategy = resolved.detection.strategy;
|
|
157
|
-
const detectorOptions = {
|
|
158
|
-
order: [strategy],
|
|
159
|
-
fallbackLanguage: resolved.defaultLocale,
|
|
160
|
-
supportedLanguages: resolved.locales,
|
|
161
|
-
lookupCookie: "locale",
|
|
162
|
-
lookupQueryString: "locale",
|
|
163
|
-
lookupFromPathIndex: 0,
|
|
164
|
-
ignoreCase: true
|
|
165
|
-
};
|
|
166
|
-
if (strategy === "cookie") {
|
|
167
|
-
detectorOptions.caches = ["cookie"];
|
|
168
|
-
if (options?.detection && "cookieOptions" in options.detection && options.detection.cookieOptions) detectorOptions.cookieOptions = options.detection.cookieOptions;
|
|
169
|
-
} else detectorOptions.caches = false;
|
|
170
|
-
return detectorOptions;
|
|
171
|
-
}
|
|
172
|
-
//#endregion
|
|
173
|
-
//#region src/i18n/messages/index.ts
|
|
174
|
-
/**
|
|
175
|
-
* Core Messages
|
|
176
|
-
*
|
|
177
|
-
* Messages used by packages/modules infrastructure.
|
|
178
|
-
* These are automatically merged with application-specific messages.
|
|
179
|
-
*/
|
|
180
|
-
/**
|
|
181
|
-
* All locale messages
|
|
182
|
-
* Explicitly import and export (no filesystem scanning - Cloudflare Workers compatible)
|
|
183
|
-
*/
|
|
184
|
-
const messages = { en: en_exports };
|
|
185
|
-
/**
|
|
186
|
-
* Get messages for all locales
|
|
187
|
-
*/
|
|
188
|
-
function getMessages() {
|
|
189
|
-
return messages;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Get available locales
|
|
193
|
-
*/
|
|
194
|
-
function getLocales() {
|
|
195
|
-
return Object.keys(messages);
|
|
196
|
-
}
|
|
197
|
-
//#endregion
|
|
198
|
-
//#region src/i18n/utils/deep-merge.ts
|
|
199
|
-
/**
|
|
200
|
-
* Deep merge two objects. Source values override target at leaf level.
|
|
201
|
-
*/
|
|
202
|
-
function deepMerge(target, source) {
|
|
203
|
-
const result = { ...target };
|
|
204
|
-
for (const key of Object.keys(source)) {
|
|
205
|
-
const targetValue = target[key];
|
|
206
|
-
const sourceValue = source[key];
|
|
207
|
-
if (typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue)) result[key] = deepMerge(targetValue, sourceValue);
|
|
208
|
-
else result[key] = sourceValue;
|
|
209
|
-
}
|
|
210
|
-
return result;
|
|
211
|
-
}
|
|
212
|
-
//#endregion
|
|
213
|
-
//#region src/i18n/services/message-loader.service.ts
|
|
214
|
-
let MessageLoaderService = class MessageLoaderService {
|
|
215
|
-
registry;
|
|
216
|
-
options;
|
|
217
|
-
cache;
|
|
218
|
-
contextCache;
|
|
219
|
-
locales;
|
|
220
|
-
defaultLocale;
|
|
221
|
-
constructor(registry, options) {
|
|
222
|
-
this.registry = registry;
|
|
223
|
-
this.options = options;
|
|
224
|
-
this.defaultLocale = this.options?.defaultLocale ?? "en";
|
|
225
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
226
|
-
this.contextCache = /* @__PURE__ */ new Map();
|
|
227
|
-
const coreMessages = getMessages();
|
|
228
|
-
const coreLocales = getLocales();
|
|
229
|
-
const registryMessages = this.registry.getMergedMessages();
|
|
230
|
-
const registryLocales = Object.keys(registryMessages);
|
|
231
|
-
const allLocales = [...new Set([...coreLocales, ...registryLocales])];
|
|
232
|
-
this.locales = allLocales;
|
|
233
|
-
for (const locale of allLocales) {
|
|
234
|
-
const merged = deepMerge(coreMessages[locale] ?? {}, registryMessages[locale] ?? {});
|
|
235
|
-
this.cache.set(locale, merged);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Get CoreContext for a locale (lazily built and cached on first access)
|
|
240
|
-
* Falls back to default locale if locale not found
|
|
241
|
-
*/
|
|
242
|
-
getCoreContext(locale) {
|
|
243
|
-
const cached = this.contextCache.get(locale);
|
|
244
|
-
if (cached) return cached;
|
|
245
|
-
const effectiveLocale = this.cache.has(locale) ? locale : this.defaultLocale;
|
|
246
|
-
const cachedEffective = this.contextCache.get(effectiveLocale);
|
|
247
|
-
if (cachedEffective) return cachedEffective;
|
|
248
|
-
const messages = this.cache.get(effectiveLocale) ?? {};
|
|
249
|
-
const flattened = this.flattenMessages(messages);
|
|
250
|
-
const ctx = createCoreContext({
|
|
251
|
-
locale: effectiveLocale,
|
|
252
|
-
messages: { [effectiveLocale]: flattened },
|
|
253
|
-
missingWarn: false,
|
|
254
|
-
fallbackWarn: false
|
|
255
|
-
});
|
|
256
|
-
this.contextCache.set(effectiveLocale, ctx);
|
|
257
|
-
return ctx;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Get messages for a specific locale.
|
|
261
|
-
* Falls back to default locale if not found.
|
|
262
|
-
*/
|
|
263
|
-
getMessages(locale) {
|
|
264
|
-
return this.cache.get(locale) ?? this.cache.get(this.defaultLocale) ?? {};
|
|
265
|
-
}
|
|
266
|
-
/** Get list of available locale codes */
|
|
267
|
-
getAvailableLocales() {
|
|
268
|
-
return this.locales;
|
|
269
|
-
}
|
|
270
|
-
/** Check if a locale is supported */
|
|
271
|
-
isLocaleSupported(locale) {
|
|
272
|
-
return this.cache.has(locale);
|
|
273
|
-
}
|
|
274
|
-
/** Get default locale */
|
|
275
|
-
getDefaultLocale() {
|
|
276
|
-
return this.defaultLocale;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Get flattened (dot-notation) messages for a locale, optionally filtered by namespace prefixes.
|
|
280
|
-
*
|
|
281
|
-
* Returns flat key-value pairs matching the format used by `@intlify/core-base`'s
|
|
282
|
-
* `createCoreContext`. Requires `registerMessageCompiler(compile)` to be called
|
|
283
|
-
* before `translate()` can resolve these flat keys.
|
|
284
|
-
*
|
|
285
|
-
* @param locale - Locale code (falls back to default locale if not found)
|
|
286
|
-
* @param options - Optional filter configuration
|
|
287
|
-
* @param options.only - Dot-notation prefixes to include (e.g., `['common', 'nav.sidebar']`)
|
|
288
|
-
* @returns Flattened messages as `{ 'key.path': 'translated value' }`
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```typescript
|
|
292
|
-
* // All messages for the locale
|
|
293
|
-
* loader.getFilteredMessages('en')
|
|
294
|
-
*
|
|
295
|
-
* // Only 'common' and 'nav' namespaces
|
|
296
|
-
* loader.getFilteredMessages('en', { only: ['common', 'nav'] })
|
|
297
|
-
*
|
|
298
|
-
* // Deeply nested prefix
|
|
299
|
-
* loader.getFilteredMessages('en', { only: ['common.actions'] })
|
|
300
|
-
* ```
|
|
301
|
-
*/
|
|
302
|
-
getFilteredMessages(locale, options) {
|
|
303
|
-
const messages = this.getMessages(locale);
|
|
304
|
-
const flattened = this.flattenMessages(messages);
|
|
305
|
-
if (!options?.only?.length) return flattened;
|
|
306
|
-
const result = {};
|
|
307
|
-
for (const [key, value] of Object.entries(flattened)) if (options.only.some((prefix) => key === prefix || key.startsWith(`${prefix}.`))) result[key] = value;
|
|
308
|
-
return result;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Flatten nested messages to dot-notation.
|
|
312
|
-
* e.g. `{ a: { b: 'hello' } }` → `{ 'a.b': 'hello' }`
|
|
313
|
-
*/
|
|
314
|
-
flattenMessages(messages, prefix = "") {
|
|
315
|
-
const result = {};
|
|
316
|
-
for (const key of Object.keys(messages)) {
|
|
317
|
-
const value = messages[key];
|
|
318
|
-
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
319
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) Object.assign(result, this.flattenMessages(value, newKey));
|
|
320
|
-
else result[newKey] = String(value);
|
|
321
|
-
}
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
MessageLoaderService = __decorate([
|
|
326
|
-
Transient(I18N_TOKENS.MessageLoader),
|
|
327
|
-
__decorateParam(0, inject(I18N_TOKENS.MessageRegistry)),
|
|
328
|
-
__decorateParam(1, inject(I18N_TOKENS.Options, { isOptional: true })),
|
|
329
|
-
__decorateMetadata("design:paramtypes", [Object, Object])
|
|
330
|
-
], MessageLoaderService);
|
|
331
|
-
//#endregion
|
|
332
|
-
//#region src/i18n/services/message-registry.ts
|
|
333
|
-
/**
|
|
334
|
-
* Global key for the shared contributions array.
|
|
335
|
-
*
|
|
336
|
-
* When stratal is installed via portal/symlink (e.g., in monorepos), bundlers
|
|
337
|
-
* like esbuild may inline multiple copies of this module. Each copy gets its
|
|
338
|
-
* own static class fields, so messages registered by one copy are invisible
|
|
339
|
-
* to another. Using a `Symbol.for()` key on `globalThis` ensures all copies
|
|
340
|
-
* share the same contributions array.
|
|
341
|
-
*/
|
|
342
|
-
const CONTRIBUTIONS_KEY = Symbol.for("stratal:i18n:message-registry:contributions");
|
|
343
|
-
function getContributions() {
|
|
344
|
-
const g = globalThis;
|
|
345
|
-
g[CONTRIBUTIONS_KEY] ??= [];
|
|
346
|
-
return g[CONTRIBUTIONS_KEY];
|
|
347
|
-
}
|
|
348
|
-
let MessageRegistry = class MessageRegistry {
|
|
349
|
-
/**
|
|
350
|
-
* Add messages (called statically by I18nModule.registerMessages)
|
|
351
|
-
*/
|
|
352
|
-
static addMessages(messages) {
|
|
353
|
-
if (Boolean(messages) && typeof messages === "object" && Object.keys(messages).length > 0) getContributions().push(messages);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Get all messages deep-merged in registration order
|
|
357
|
-
*/
|
|
358
|
-
getMergedMessages() {
|
|
359
|
-
const merged = {};
|
|
360
|
-
for (const contribution of getContributions()) for (const locale of Object.keys(contribution)) merged[locale] = deepMerge(merged[locale] ?? {}, contribution[locale]);
|
|
361
|
-
return merged;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Reset registry (for testing)
|
|
365
|
-
* @internal
|
|
366
|
-
*/
|
|
367
|
-
static reset() {
|
|
368
|
-
globalThis[CONTRIBUTIONS_KEY] = [];
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
MessageRegistry = __decorate([Transient(I18N_TOKENS.MessageRegistry)], MessageRegistry);
|
|
372
|
-
//#endregion
|
|
373
|
-
//#region src/openapi/services/openapi.service.ts
|
|
374
|
-
let OpenAPIService = class OpenAPIService {
|
|
375
|
-
/**
|
|
376
|
-
* Generate a filtered OpenAPI spec using the user's config.
|
|
377
|
-
* Usable from both HTTP handlers and CLI commands.
|
|
378
|
-
*/
|
|
379
|
-
getSpec(app, container) {
|
|
380
|
-
const configService = container.resolve(OPENAPI_TOKENS.ConfigService);
|
|
381
|
-
const i18n = container.resolve(I18N_TOKENS.I18nService);
|
|
382
|
-
const config = configService.getEffectiveConfig();
|
|
383
|
-
const fullSpec = app.getOpenAPIDocument({
|
|
384
|
-
openapi: "3.0.0",
|
|
385
|
-
info: {
|
|
386
|
-
version: config.info.version,
|
|
387
|
-
title: config.info.title,
|
|
388
|
-
description: config.info.description
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
fullSpec.components ??= {};
|
|
392
|
-
fullSpec.components.securitySchemes = this.getSecuritySchemeDefinitions(i18n);
|
|
393
|
-
if (config.routeFilter) fullSpec.paths = this.filterRoutes(fullSpec.paths, config);
|
|
394
|
-
if (fullSpec.components.schemas) fullSpec.components.schemas = this.filterSchemas(fullSpec);
|
|
395
|
-
return fullSpec;
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Setup OpenAPI documentation endpoints
|
|
399
|
-
*/
|
|
400
|
-
setupEndpoints(app, container) {
|
|
401
|
-
const config = container.resolve(OPENAPI_TOKENS.ConfigService).getEffectiveConfig();
|
|
402
|
-
app.get(config.jsonPath, (c) => {
|
|
403
|
-
const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
|
|
404
|
-
const fullSpec = this.getSpec(app, requestContainer);
|
|
405
|
-
const url = new URL(c.req.raw.url);
|
|
406
|
-
const i18n = requestContainer.resolve(I18N_TOKENS.I18nService);
|
|
407
|
-
fullSpec.servers = [{
|
|
408
|
-
url: `${url.protocol}//${url.host}`,
|
|
409
|
-
description: i18n.t("common.api.serverDescription")
|
|
410
|
-
}];
|
|
411
|
-
return c.json(fullSpec);
|
|
412
|
-
});
|
|
413
|
-
this.nameLastHandler(app, "OpenAPI", "spec");
|
|
414
|
-
if (config.ui !== false) {
|
|
415
|
-
const uiPath = config.ui?.path ?? "/api/docs";
|
|
416
|
-
const uiRenderer = config.ui?.renderer;
|
|
417
|
-
app.get(uiPath, (c, next) => {
|
|
418
|
-
const effectiveConfig = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER).resolve(OPENAPI_TOKENS.ConfigService).getEffectiveConfig();
|
|
419
|
-
const uiContext = {
|
|
420
|
-
specUrl: effectiveConfig.jsonPath,
|
|
421
|
-
title: effectiveConfig.info.title
|
|
422
|
-
};
|
|
423
|
-
if (uiRenderer) return uiRenderer(uiContext)(c, next);
|
|
424
|
-
return swaggerUI({ url: uiContext.specUrl })(c, next);
|
|
425
|
-
});
|
|
426
|
-
this.nameLastHandler(app, "OpenAPI", "docs");
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
nameLastHandler(app, controller, method) {
|
|
430
|
-
const last = app.routes[app.routes.length - 1];
|
|
431
|
-
Object.defineProperty(last.handler, "name", { value: `http:${controller}.${method}` });
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Get localized security scheme definitions
|
|
435
|
-
*/
|
|
436
|
-
getSecuritySchemeDefinitions(i18n) {
|
|
437
|
-
return {
|
|
438
|
-
[SECURITY_SCHEMES.BEARER_AUTH]: {
|
|
439
|
-
type: "http",
|
|
440
|
-
scheme: "bearer",
|
|
441
|
-
bearerFormat: "JWT",
|
|
442
|
-
description: i18n.t("common.api.security.bearerAuth")
|
|
443
|
-
},
|
|
444
|
-
[SECURITY_SCHEMES.API_KEY]: {
|
|
445
|
-
type: "apiKey",
|
|
446
|
-
in: "header",
|
|
447
|
-
name: "X-API-Key",
|
|
448
|
-
description: i18n.t("common.api.security.apiKey")
|
|
449
|
-
},
|
|
450
|
-
[SECURITY_SCHEMES.SESSION_COOKIE]: {
|
|
451
|
-
type: "apiKey",
|
|
452
|
-
in: "cookie",
|
|
453
|
-
name: "session",
|
|
454
|
-
description: i18n.t("common.api.security.sessionCookie")
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Filter OpenAPI paths using custom routeFilter
|
|
460
|
-
*/
|
|
461
|
-
filterRoutes(paths, config) {
|
|
462
|
-
const filteredPaths = {};
|
|
463
|
-
for (const [path, pathItem] of Object.entries(paths)) {
|
|
464
|
-
if (config.routeFilter && !config.routeFilter(path, pathItem)) continue;
|
|
465
|
-
filteredPaths[path] = pathItem;
|
|
466
|
-
}
|
|
467
|
-
return filteredPaths;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Filter unreferenced schemas from OpenAPI spec
|
|
471
|
-
*/
|
|
472
|
-
filterSchemas(spec) {
|
|
473
|
-
const referencedSchemas = /* @__PURE__ */ new Set();
|
|
474
|
-
this.collectSchemaRefs(spec.paths, referencedSchemas);
|
|
475
|
-
const filteredSchemas = {};
|
|
476
|
-
const components = spec.components;
|
|
477
|
-
if (components?.schemas) {
|
|
478
|
-
const allSchemas = components.schemas;
|
|
479
|
-
let prevSize = 0;
|
|
480
|
-
while (referencedSchemas.size > prevSize) {
|
|
481
|
-
prevSize = referencedSchemas.size;
|
|
482
|
-
for (const [schemaName, schemaValue] of Object.entries(allSchemas)) if (referencedSchemas.has(schemaName) && !filteredSchemas[schemaName]) {
|
|
483
|
-
filteredSchemas[schemaName] = schemaValue;
|
|
484
|
-
this.collectSchemaRefs(schemaValue, referencedSchemas);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return filteredSchemas;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Recursively collect all schema references from an object
|
|
492
|
-
*/
|
|
493
|
-
collectSchemaRefs(obj, refs) {
|
|
494
|
-
if (!obj || typeof obj !== "object") return;
|
|
495
|
-
const record = obj;
|
|
496
|
-
if (record.$ref && typeof record.$ref === "string") {
|
|
497
|
-
const match = /^#\/components\/schemas\/(.+)$/.exec(record.$ref);
|
|
498
|
-
if (match) refs.add(match[1]);
|
|
499
|
-
}
|
|
500
|
-
if (Array.isArray(obj)) for (const item of obj) this.collectSchemaRefs(item, refs);
|
|
501
|
-
else for (const value of Object.values(record)) this.collectSchemaRefs(value, refs);
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
OpenAPIService = __decorate([Transient(OPENAPI_TOKENS.OpenAPIService)], OpenAPIService);
|
|
505
|
-
//#endregion
|
|
506
|
-
//#region src/openapi/openapi.module.ts
|
|
507
|
-
/**
|
|
508
|
-
* OpenAPI Module
|
|
509
|
-
*
|
|
510
|
-
* Provides configurable OpenAPI documentation endpoints with runtime override support.
|
|
511
|
-
*
|
|
512
|
-
* Features:
|
|
513
|
-
* - Configurable paths for /openapi.json and /docs
|
|
514
|
-
* - Runtime config overrides via middleware
|
|
515
|
-
* - i18n support for titles and descriptions
|
|
516
|
-
* - Route filtering via hideFromDocs and custom routeFilter
|
|
517
|
-
*
|
|
518
|
-
* @example Basic usage
|
|
519
|
-
* ```typescript
|
|
520
|
-
* @Module({
|
|
521
|
-
* imports: [
|
|
522
|
-
* OpenAPIModule.forRoot({
|
|
523
|
-
* info: { title: 'My API', version: '1.0.0' }
|
|
524
|
-
* })
|
|
525
|
-
* ]
|
|
526
|
-
* })
|
|
527
|
-
* export class AppModule {}
|
|
528
|
-
* ```
|
|
529
|
-
*
|
|
530
|
-
* @example With runtime override in middleware
|
|
531
|
-
* ```typescript
|
|
532
|
-
* // In RouteAccessMiddleware
|
|
533
|
-
* constructor(
|
|
534
|
-
* @inject(OPENAPI_TOKENS.ConfigService) private openAPIConfig: IOpenAPIConfigService
|
|
535
|
-
* ) {}
|
|
536
|
-
*
|
|
537
|
-
* async handle(ctx, next) {
|
|
538
|
-
* this.openAPIConfig.override({
|
|
539
|
-
* info: { title: 'Custom API' },
|
|
540
|
-
* routeFilter: (path) => this.shouldInclude(path)
|
|
541
|
-
* })
|
|
542
|
-
* await next()
|
|
543
|
-
* }
|
|
544
|
-
* ```
|
|
545
|
-
*/
|
|
546
|
-
var _OpenAPIModule;
|
|
547
|
-
/** Default options when none provided */
|
|
548
|
-
const DEFAULT_OPTIONS = {
|
|
549
|
-
jsonPath: "/api/openapi.json",
|
|
550
|
-
info: {
|
|
551
|
-
title: "API",
|
|
552
|
-
version: "1.0.0"
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
let OpenAPIModule = _OpenAPIModule = class OpenAPIModule {
|
|
556
|
-
/**
|
|
557
|
-
* Configure OpenAPI module with static options
|
|
558
|
-
*
|
|
559
|
-
* @param options - OpenAPI configuration (paths, info, security schemes)
|
|
560
|
-
* @returns DynamicModule with options provider
|
|
561
|
-
*/
|
|
562
|
-
static forRoot(options = {}) {
|
|
563
|
-
const mergedOptions = {
|
|
564
|
-
...DEFAULT_OPTIONS,
|
|
565
|
-
...options,
|
|
566
|
-
info: {
|
|
567
|
-
...DEFAULT_OPTIONS.info,
|
|
568
|
-
...options.info,
|
|
569
|
-
title: options.info?.title ?? DEFAULT_OPTIONS.info?.title ?? "API",
|
|
570
|
-
version: options.info?.version ?? DEFAULT_OPTIONS.info?.version ?? "1.0.0"
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
|
-
return {
|
|
574
|
-
module: _OpenAPIModule,
|
|
575
|
-
providers: [{
|
|
576
|
-
provide: OPENAPI_TOKENS.Options,
|
|
577
|
-
useValue: mergedOptions
|
|
578
|
-
}]
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
static forRootAsync(options) {
|
|
582
|
-
return {
|
|
583
|
-
module: _OpenAPIModule,
|
|
584
|
-
providers: [{
|
|
585
|
-
provide: OPENAPI_TOKENS.Options,
|
|
586
|
-
useFactory: options.useFactory,
|
|
587
|
-
inject: options.inject
|
|
588
|
-
}]
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
OpenAPIModule = _OpenAPIModule = __decorate([Module({ providers: [{
|
|
593
|
-
provide: OPENAPI_TOKENS.ConfigService,
|
|
594
|
-
useClass: OpenAPIConfigService,
|
|
595
|
-
scope: Scope.Request
|
|
596
|
-
}, {
|
|
597
|
-
provide: OPENAPI_TOKENS.OpenAPIService,
|
|
598
|
-
useClass: OpenAPIService,
|
|
599
|
-
scope: Scope.Singleton
|
|
600
|
-
}] })], OpenAPIModule);
|
|
601
|
-
//#endregion
|
|
602
73
|
//#region src/router/middleware/logger.middleware.ts
|
|
603
74
|
/**
|
|
604
75
|
* Create a Hono middleware that logs HTTP requests using our Logger service
|
|
@@ -670,7 +141,7 @@ function stripPort(host) {
|
|
|
670
141
|
* When the host matches, domain parameters are extracted and stored in context
|
|
671
142
|
* variables accessible via `ctx.domain(key)`.
|
|
672
143
|
*
|
|
673
|
-
* When the host does NOT match,
|
|
144
|
+
* When the host does NOT match, aborts with 404.
|
|
674
145
|
*
|
|
675
146
|
* @param pattern - Domain pattern with `{param}` placeholders (e.g., '{tenant}.myapp.com')
|
|
676
147
|
*
|
|
@@ -690,7 +161,7 @@ function createDomainMiddleware(pattern) {
|
|
|
690
161
|
return async (c, next) => {
|
|
691
162
|
const host = stripPort(c.req.header("host") ?? "");
|
|
692
163
|
const match = regex.exec(host);
|
|
693
|
-
if (!match)
|
|
164
|
+
if (!match) abort(404, "Domain mismatch");
|
|
694
165
|
for (let i = 0; i < paramNames.length; i++) c.set(`domain:${paramNames[i]}`, match[i + 1]);
|
|
695
166
|
await next();
|
|
696
167
|
};
|
|
@@ -718,12 +189,7 @@ function createMiddlewareChain(classes) {
|
|
|
718
189
|
const middleware = requestContainer.resolve(middlewareClass);
|
|
719
190
|
let called = false;
|
|
720
191
|
const guardedNext = () => {
|
|
721
|
-
if (called) {
|
|
722
|
-
const err = new MiddlewareNextCalledMultipleTimesError(middlewareClass.name ?? "anonymous");
|
|
723
|
-
console.error("[STRATAL DEBUG] next() called multiple times for " + middlewareClass.name);
|
|
724
|
-
console.error("[STRATAL DEBUG] Stack trace:", (/* @__PURE__ */ new Error()).stack);
|
|
725
|
-
return Promise.reject(err);
|
|
726
|
-
}
|
|
192
|
+
if (called) return Promise.reject(new RouterError(`Middleware "${middlewareClass.name ?? "anonymous"}" called next() multiple times`));
|
|
727
193
|
called = true;
|
|
728
194
|
return prevNext();
|
|
729
195
|
};
|
|
@@ -875,10 +341,10 @@ function Route(config) {
|
|
|
875
341
|
type: "convention",
|
|
876
342
|
config
|
|
877
343
|
};
|
|
878
|
-
|
|
879
|
-
const existing =
|
|
344
|
+
defineMetadata(ROUTE_METADATA_KEYS.ROUTE_CONFIG, metadata, target, propertyKey);
|
|
345
|
+
const existing = getMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, target) ?? [];
|
|
880
346
|
existing.push(propertyKey);
|
|
881
|
-
|
|
347
|
+
defineMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, existing, target);
|
|
882
348
|
return descriptor;
|
|
883
349
|
};
|
|
884
350
|
}
|
|
@@ -890,7 +356,7 @@ function Route(config) {
|
|
|
890
356
|
* @returns Route metadata or undefined if not decorated
|
|
891
357
|
*/
|
|
892
358
|
function getRouteMetadata(target, methodName) {
|
|
893
|
-
return
|
|
359
|
+
return getMetadata(ROUTE_METADATA_KEYS.ROUTE_CONFIG, target, methodName);
|
|
894
360
|
}
|
|
895
361
|
/**
|
|
896
362
|
* Get all methods with route decorators (@Route, @Get, @Post, etc.) from a controller
|
|
@@ -902,7 +368,7 @@ function getRouteDecoratedMethods(ControllerClass) {
|
|
|
902
368
|
const methods = /* @__PURE__ */ new Set();
|
|
903
369
|
let proto = ControllerClass.prototype;
|
|
904
370
|
while (proto && proto !== Object.prototype) {
|
|
905
|
-
const own =
|
|
371
|
+
const own = getMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, proto);
|
|
906
372
|
if (own) for (const m of own) methods.add(m);
|
|
907
373
|
proto = Object.getPrototypeOf(proto);
|
|
908
374
|
}
|
|
@@ -921,31 +387,19 @@ function getRouteDecoratedMethods(ControllerClass) {
|
|
|
921
387
|
/**
|
|
922
388
|
* Generic error response schema
|
|
923
389
|
* Used for all error responses (4xx, 5xx)
|
|
924
|
-
* Matches
|
|
390
|
+
* Matches the ErrorResponse shape produced by ExceptionHandler
|
|
925
391
|
*/
|
|
926
392
|
const errorResponseSchema = z.object({
|
|
927
|
-
code: z.number().int().describe("Application error code"),
|
|
928
393
|
message: z.string().describe("Human-readable error message"),
|
|
929
394
|
timestamp: z.string().datetime().describe("ISO timestamp when error occurred"),
|
|
930
|
-
metadata: z.record(z.string(), z.unknown()).optional().describe("Additional error context"),
|
|
931
395
|
stack: z.string().optional().describe("Stack trace (development only)")
|
|
932
396
|
}).openapi("ErrorResponse");
|
|
933
397
|
/**
|
|
934
398
|
* Validation error response schema
|
|
935
399
|
* Used for 400 Bad Request with validation failures
|
|
936
|
-
* Matches
|
|
400
|
+
* Matches the ErrorResponse shape produced by ExceptionHandler
|
|
937
401
|
*/
|
|
938
|
-
const validationErrorResponseSchema =
|
|
939
|
-
code: z.number().int().describe("Application error code"),
|
|
940
|
-
message: z.string().describe("Human-readable error message"),
|
|
941
|
-
timestamp: z.string().datetime().describe("ISO timestamp when error occurred"),
|
|
942
|
-
metadata: z.object({ issues: z.array(z.object({
|
|
943
|
-
path: z.string().describe("Field path that failed validation"),
|
|
944
|
-
message: z.string().describe("Validation failure message"),
|
|
945
|
-
code: z.string().describe("Zod validation error code")
|
|
946
|
-
})) }).describe("Validation error details"),
|
|
947
|
-
stack: z.string().optional().describe("Stack trace (development only)")
|
|
948
|
-
}).openapi("ValidationErrorResponse");
|
|
402
|
+
const validationErrorResponseSchema = errorResponseSchema.openapi("ValidationErrorResponse");
|
|
949
403
|
/**
|
|
950
404
|
* Pagination query parameters schema
|
|
951
405
|
* Used for list endpoints
|
|
@@ -1229,7 +683,7 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1229
683
|
collectRoutes(ControllerClass, actions) {
|
|
1230
684
|
const isWsGateway = isGateway(ControllerClass);
|
|
1231
685
|
const controllerRoute = getControllerRoute(ControllerClass);
|
|
1232
|
-
if (!controllerRoute) throw new
|
|
686
|
+
if (!controllerRoute) throw new RouterError(`Controller "${ControllerClass.name}" registration failed: ${isWsGateway ? "Missing @Gateway decorator or route metadata" : "Missing @Controller decorator or route metadata"}`);
|
|
1233
687
|
const controllerOpts = getControllerOptions(ControllerClass);
|
|
1234
688
|
const controllerGuards = getControllerGuards(ControllerClass)?.guards ?? [];
|
|
1235
689
|
const routerConfig = this.routerResolver?.resolveForController(ControllerClass) ?? { middleware: [] };
|
|
@@ -1282,7 +736,7 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1282
736
|
return;
|
|
1283
737
|
}
|
|
1284
738
|
const decoratedMethods = getRouteDecoratedMethods(ControllerClass);
|
|
1285
|
-
if (decoratedMethods.length === 0) throw new
|
|
739
|
+
if (decoratedMethods.length === 0) throw new RouterError(`Controller "${ControllerClass.name}" registration failed: No route decorators found. Use @Route() or HTTP method decorators (@Get, @Post, etc.) on controller methods.`);
|
|
1286
740
|
const methodMetadata = [];
|
|
1287
741
|
let hasConvention = false;
|
|
1288
742
|
let hasExplicit = false;
|
|
@@ -1296,7 +750,7 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1296
750
|
if (meta.type === "convention") hasConvention = true;
|
|
1297
751
|
else if (meta.type === "explicit") hasExplicit = true;
|
|
1298
752
|
}
|
|
1299
|
-
if (hasConvention && hasExplicit) throw new
|
|
753
|
+
if (hasConvention && hasExplicit) throw new RouterError(`Controller "${ControllerClass.name}" registration failed: Cannot mix @Route() with HTTP method decorators (@Get, @Post, etc.) in the same controller. Use one pattern or the other.`);
|
|
1300
754
|
const routerHidden = routerConfig.hideFromDocs;
|
|
1301
755
|
const controllerHidden = controllerOpts?.hideFromDocs ?? false;
|
|
1302
756
|
const routerName = routerConfig.name;
|
|
@@ -1403,16 +857,16 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1403
857
|
const bindWsHandler = (method, onCatch) => {
|
|
1404
858
|
return (evt, ws) => {
|
|
1405
859
|
invokeHandler(gateway, method, evt, new GatewayContext(c, ws)).catch((err) => {
|
|
1406
|
-
this.logger.error(`WebSocket ${method} handler error`, {
|
|
1407
|
-
gateway: GatewayClass.name,
|
|
1408
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1409
|
-
});
|
|
860
|
+
this.logger.error(`WebSocket ${method} handler error`, err, { gateway: GatewayClass.name });
|
|
1410
861
|
onCatch?.(err, ws);
|
|
1411
862
|
});
|
|
1412
863
|
};
|
|
1413
864
|
};
|
|
1414
865
|
if (onMsgMethod) events.onMessage = bindWsHandler(onMsgMethod, (_err, ws) => ws.close(1011, "Internal Error"));
|
|
1415
866
|
if (onCloseMethod) events.onClose = bindWsHandler(onCloseMethod);
|
|
867
|
+
else events.onClose = (_evt, ws) => {
|
|
868
|
+
ws.close();
|
|
869
|
+
};
|
|
1416
870
|
if (onErrMethod) events.onError = bindWsHandler(onErrMethod);
|
|
1417
871
|
return events;
|
|
1418
872
|
});
|
|
@@ -1495,7 +949,7 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1495
949
|
resolveMethodAndPath(meta, methodName, basePath, className) {
|
|
1496
950
|
if (meta.type === "convention") {
|
|
1497
951
|
const derived = this.deriveHttpMethodAndPath(methodName, basePath);
|
|
1498
|
-
if (!derived) throw new
|
|
952
|
+
if (!derived) throw new RouterError(`Cannot derive HTTP method/path for convention-based route "${className}.${methodName}". Ensure the method name follows the naming convention (e.g., index, create, show).`);
|
|
1499
953
|
return {
|
|
1500
954
|
httpMethod: derived.method,
|
|
1501
955
|
fullPath: derived.path,
|
|
@@ -1615,9 +1069,9 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1615
1069
|
if (metadata.security.length > 0) route.security = metadata.security;
|
|
1616
1070
|
if (routeConfig.description) route.description = routeConfig.description;
|
|
1617
1071
|
if (routeConfig.summary) route.summary = routeConfig.summary;
|
|
1618
|
-
return (0,
|
|
1072
|
+
return (0, zod_exports.createRoute)(route);
|
|
1619
1073
|
} catch (error) {
|
|
1620
|
-
throw new
|
|
1074
|
+
throw new RouterError(`OpenAPI route registration failed for "${path}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1621
1075
|
}
|
|
1622
1076
|
}
|
|
1623
1077
|
/**
|
|
@@ -1662,7 +1116,7 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1662
1116
|
if (responseSchema && c.env.ENVIRONMENT !== "production") return this.validateResponse(response, responseSchema);
|
|
1663
1117
|
return response;
|
|
1664
1118
|
}
|
|
1665
|
-
throw new
|
|
1119
|
+
throw new RouterError(`Method "${methodName}" not found on controller "${ControllerClass.name}"`);
|
|
1666
1120
|
};
|
|
1667
1121
|
this.nameHandler(handler, ControllerClass.name, methodName);
|
|
1668
1122
|
return handler;
|
|
@@ -1712,21 +1166,13 @@ let RouteRegistrationService = class RouteRegistrationService {
|
|
|
1712
1166
|
}
|
|
1713
1167
|
};
|
|
1714
1168
|
RouteRegistrationService = __decorate([
|
|
1715
|
-
|
|
1169
|
+
Singleton(),
|
|
1716
1170
|
__decorateParam(0, inject(LOGGER_TOKENS.LoggerService)),
|
|
1717
1171
|
__decorateParam(1, inject(ROUTER_TOKENS.RouteRegistry)),
|
|
1718
|
-
__decorateParam(2, inject(ROUTER_TOKENS.RouterResolver)),
|
|
1172
|
+
__decorateParam(2, inject(ROUTER_TOKENS.RouterResolver, { isOptional: true })),
|
|
1719
1173
|
__decorateParam(3, inject(ROUTER_TOKENS.LocalePathService)),
|
|
1720
1174
|
__decorateParam(4, inject(ROUTER_TOKENS.HonoApp)),
|
|
1721
|
-
__decorateParam(5, inject(DI_TOKENS.ModuleRegistry))
|
|
1722
|
-
__decorateMetadata("design:paramtypes", [
|
|
1723
|
-
Object,
|
|
1724
|
-
Object,
|
|
1725
|
-
Object,
|
|
1726
|
-
Object,
|
|
1727
|
-
Object,
|
|
1728
|
-
Object
|
|
1729
|
-
])
|
|
1175
|
+
__decorateParam(5, inject(DI_TOKENS.ModuleRegistry))
|
|
1730
1176
|
], RouteRegistrationService);
|
|
1731
1177
|
//#endregion
|
|
1732
1178
|
//#region src/router/hono-app.ts
|
|
@@ -1783,7 +1229,7 @@ let HonoApp = class HonoApp extends OpenAPIHono {
|
|
|
1783
1229
|
* Called once by Application.initialize().
|
|
1784
1230
|
*/
|
|
1785
1231
|
async configure() {
|
|
1786
|
-
if (this.configured) throw new
|
|
1232
|
+
if (this.configured) throw new RouterError("HonoApp has already been configured");
|
|
1787
1233
|
this._container.resolve(OPENAPI_TOKENS.OpenAPIService).setupEndpoints(this, this._container);
|
|
1788
1234
|
await this._container.resolve(RouteRegistrationService).configure();
|
|
1789
1235
|
this.notFound((c) => {
|
|
@@ -1806,17 +1252,54 @@ let HonoApp = class HonoApp extends OpenAPIHono {
|
|
|
1806
1252
|
}
|
|
1807
1253
|
};
|
|
1808
1254
|
HonoApp = __decorate([
|
|
1809
|
-
|
|
1255
|
+
Singleton(),
|
|
1810
1256
|
__decorateParam(0, inject(CONTAINER_TOKEN)),
|
|
1811
1257
|
__decorateParam(1, inject(LOGGER_TOKENS.LoggerService)),
|
|
1812
|
-
__decorateParam(2, inject(DI_TOKENS.Application))
|
|
1813
|
-
__decorateMetadata("design:paramtypes", [
|
|
1814
|
-
Object,
|
|
1815
|
-
Object,
|
|
1816
|
-
Object
|
|
1817
|
-
])
|
|
1258
|
+
__decorateParam(2, inject(DI_TOKENS.Application))
|
|
1818
1259
|
], HonoApp);
|
|
1819
1260
|
//#endregion
|
|
1261
|
+
//#region src/i18n/i18n.options.ts
|
|
1262
|
+
/**
|
|
1263
|
+
* Resolve I18n options with defaults
|
|
1264
|
+
*/
|
|
1265
|
+
function resolveI18nOptions(options) {
|
|
1266
|
+
const detection = options?.detection;
|
|
1267
|
+
const enabled = detection ? detection.enabled !== false : true;
|
|
1268
|
+
const strategy = detection && "strategy" in detection ? detection.strategy ?? "cookie" : "cookie";
|
|
1269
|
+
const prefixDefaultLocale = detection && "prefixDefaultLocale" in detection && detection.prefixDefaultLocale !== void 0 ? detection.prefixDefaultLocale : false;
|
|
1270
|
+
return {
|
|
1271
|
+
defaultLocale: options?.defaultLocale ?? "en",
|
|
1272
|
+
fallbackLocale: options?.fallbackLocale ?? "en",
|
|
1273
|
+
locales: options?.locales ?? ["en"],
|
|
1274
|
+
detection: {
|
|
1275
|
+
enabled,
|
|
1276
|
+
strategy,
|
|
1277
|
+
prefixDefaultLocale
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Build Hono languageDetector options from I18n module options
|
|
1283
|
+
*/
|
|
1284
|
+
function buildDetectorOptions(options) {
|
|
1285
|
+
const resolved = resolveI18nOptions(options);
|
|
1286
|
+
const strategy = resolved.detection.strategy;
|
|
1287
|
+
const detectorOptions = {
|
|
1288
|
+
order: [strategy],
|
|
1289
|
+
fallbackLanguage: resolved.defaultLocale,
|
|
1290
|
+
supportedLanguages: resolved.locales,
|
|
1291
|
+
lookupCookie: "locale",
|
|
1292
|
+
lookupQueryString: "locale",
|
|
1293
|
+
lookupFromPathIndex: 0,
|
|
1294
|
+
ignoreCase: true
|
|
1295
|
+
};
|
|
1296
|
+
if (strategy === "cookie") {
|
|
1297
|
+
detectorOptions.caches = ["cookie"];
|
|
1298
|
+
if (options?.detection && "cookieOptions" in options.detection && options.detection.cookieOptions) detectorOptions.cookieOptions = options.detection.cookieOptions;
|
|
1299
|
+
} else detectorOptions.caches = false;
|
|
1300
|
+
return detectorOptions;
|
|
1301
|
+
}
|
|
1302
|
+
//#endregion
|
|
1820
1303
|
//#region src/router/services/locale-path.service.ts
|
|
1821
1304
|
let LocalePathService = class LocalePathService {
|
|
1822
1305
|
honoApp;
|
|
@@ -1925,10 +1408,9 @@ let LocalePathService = class LocalePathService {
|
|
|
1925
1408
|
}
|
|
1926
1409
|
};
|
|
1927
1410
|
LocalePathService = __decorate([
|
|
1928
|
-
|
|
1411
|
+
Singleton(),
|
|
1929
1412
|
__decorateParam(0, inject(CONTAINER_TOKEN)),
|
|
1930
|
-
__decorateParam(1, inject(ROUTER_TOKENS.HonoApp))
|
|
1931
|
-
__decorateMetadata("design:paramtypes", [Object, Object])
|
|
1413
|
+
__decorateParam(1, inject(ROUTER_TOKENS.HonoApp))
|
|
1932
1414
|
], LocalePathService);
|
|
1933
1415
|
//#endregion
|
|
1934
1416
|
//#region src/router/services/versioning.service.ts
|
|
@@ -1957,11 +1439,7 @@ let VersioningService = class VersioningService {
|
|
|
1957
1439
|
return [basePath];
|
|
1958
1440
|
}
|
|
1959
1441
|
};
|
|
1960
|
-
VersioningService = __decorate([
|
|
1961
|
-
Transient(),
|
|
1962
|
-
__decorateParam(0, inject(DI_TOKENS.Application)),
|
|
1963
|
-
__decorateMetadata("design:paramtypes", [Object])
|
|
1964
|
-
], VersioningService);
|
|
1442
|
+
VersioningService = __decorate([Singleton(), __decorateParam(0, inject(DI_TOKENS.Application))], VersioningService);
|
|
1965
1443
|
//#endregion
|
|
1966
1444
|
//#region src/router/route-registry.ts
|
|
1967
1445
|
const CONCRETE_HTTP_METHODS = [
|
|
@@ -1990,7 +1468,7 @@ let RouteRegistry = class RouteRegistry {
|
|
|
1990
1468
|
* Named routes must have unique names.
|
|
1991
1469
|
*
|
|
1992
1470
|
* @returns Array of expanded RegisteredRoute entries (primary + locale variants)
|
|
1993
|
-
* @throws
|
|
1471
|
+
* @throws RouterError if a named route with the same name already exists
|
|
1994
1472
|
*/
|
|
1995
1473
|
register(input) {
|
|
1996
1474
|
const domainParamNames = input.domainParamNames ?? (input.domain ? extractDomainParamNames(input.domain) : []);
|
|
@@ -2022,7 +1500,7 @@ let RouteRegistry = class RouteRegistry {
|
|
|
2022
1500
|
if (route.name) {
|
|
2023
1501
|
if (this.namedRoutes.has(route.name)) {
|
|
2024
1502
|
const existing = this.namedRoutes.get(route.name);
|
|
2025
|
-
throw new
|
|
1503
|
+
throw new RouterError(`Duplicate route name "${route.name}": already registered by ${existing.controller}.${existing.action}, cannot register ${route.controller}.${route.action}`);
|
|
2026
1504
|
}
|
|
2027
1505
|
this.namedRoutes.set(route.name, route);
|
|
2028
1506
|
}
|
|
@@ -2072,12 +1550,41 @@ let RouteRegistry = class RouteRegistry {
|
|
|
2072
1550
|
}
|
|
2073
1551
|
};
|
|
2074
1552
|
RouteRegistry = __decorate([
|
|
2075
|
-
|
|
1553
|
+
Singleton(),
|
|
2076
1554
|
__decorateParam(0, inject(ROUTER_TOKENS.VersioningService)),
|
|
2077
|
-
__decorateParam(1, inject(ROUTER_TOKENS.LocalePathService))
|
|
2078
|
-
__decorateMetadata("design:paramtypes", [Object, Object])
|
|
1555
|
+
__decorateParam(1, inject(ROUTER_TOKENS.LocalePathService))
|
|
2079
1556
|
], RouteRegistry);
|
|
2080
1557
|
//#endregion
|
|
1558
|
+
//#region src/router/route-url.ts
|
|
1559
|
+
/**
|
|
1560
|
+
* Generate a URL from a named route.
|
|
1561
|
+
*
|
|
1562
|
+
* Keys in `params` matching `:param` placeholders fill the path.
|
|
1563
|
+
* Domain params (`{tenant}`) are also consumed from `params`.
|
|
1564
|
+
* Extra keys become query string parameters.
|
|
1565
|
+
*
|
|
1566
|
+
* Resolves RouteRegistry from the application container via AsyncLocalStorage.
|
|
1567
|
+
* Available after `Application.initialize()` has been called.
|
|
1568
|
+
*
|
|
1569
|
+
* @param name - Named route identifier
|
|
1570
|
+
* @param params - Route params + domain params + extra query params
|
|
1571
|
+
* @returns Generated URL string
|
|
1572
|
+
*
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```typescript
|
|
1575
|
+
* // In a controller (preferred):
|
|
1576
|
+
* ctx.route('users.show', { id: '1' })
|
|
1577
|
+
*
|
|
1578
|
+
* // Outside controllers (standalone function):
|
|
1579
|
+
* import { route } from 'stratal/router'
|
|
1580
|
+
*
|
|
1581
|
+
* route('users.show', { id: '1' })
|
|
1582
|
+
* ```
|
|
1583
|
+
*/
|
|
1584
|
+
function route(name, params, options) {
|
|
1585
|
+
return getContainer().resolve(ROUTER_TOKENS.Uri).route(name, params, options);
|
|
1586
|
+
}
|
|
1587
|
+
//#endregion
|
|
2081
1588
|
//#region src/router/uri.ts
|
|
2082
1589
|
/**
|
|
2083
1590
|
* Encode a value for use as a path parameter.
|
|
@@ -2100,7 +1607,7 @@ function encodePathParam(value) {
|
|
|
2100
1607
|
* @param params - Path params, domain params, and extra query params
|
|
2101
1608
|
* @returns Relative URL string (or absolute with domain prefix if route has a domain pattern)
|
|
2102
1609
|
*
|
|
2103
|
-
* @throws
|
|
1610
|
+
* @throws RouterError if a required path or domain param is missing
|
|
2104
1611
|
*/
|
|
2105
1612
|
function buildRouteUrl(route, name, params) {
|
|
2106
1613
|
const allParams = { ...params };
|
|
@@ -2112,7 +1619,7 @@ function buildRouteUrl(route, name, params) {
|
|
|
2112
1619
|
}
|
|
2113
1620
|
for (const paramName of route.paramNames) {
|
|
2114
1621
|
const value = allParams[paramName];
|
|
2115
|
-
if (value === void 0) throw new
|
|
1622
|
+
if (value === void 0) throw new RouterError(`Missing required route parameter "${paramName}" for route "${name}" (path: ${route.path})`);
|
|
2116
1623
|
url = url.replace(new RegExp(`:${paramName}(\\{[^}]*\\})?`), encodePathParam(value));
|
|
2117
1624
|
consumedKeys.add(paramName);
|
|
2118
1625
|
}
|
|
@@ -2121,7 +1628,7 @@ function buildRouteUrl(route, name, params) {
|
|
|
2121
1628
|
domain = route.domain;
|
|
2122
1629
|
for (const domainParam of route.domainParamNames) {
|
|
2123
1630
|
const value = allParams[domainParam];
|
|
2124
|
-
if (value === void 0) throw new
|
|
1631
|
+
if (value === void 0) throw new RouterError(`Missing required domain parameter "${domainParam}" for route "${name}" (domain: ${route.domain})`);
|
|
2125
1632
|
domain = domain.replace(`{${domainParam}}`, encodeURIComponent(value));
|
|
2126
1633
|
consumedKeys.add(domainParam);
|
|
2127
1634
|
}
|
|
@@ -2179,12 +1686,11 @@ let Uri = class Uri {
|
|
|
2179
1686
|
* @param options - URL generation options
|
|
2180
1687
|
* @returns Generated URL string
|
|
2181
1688
|
*
|
|
2182
|
-
* @throws
|
|
2183
|
-
* @throws MissingRouteParamError if required params missing
|
|
1689
|
+
* @throws RouterError if route name not found or required params missing
|
|
2184
1690
|
*/
|
|
2185
1691
|
route(name, params, options) {
|
|
2186
1692
|
const registeredRoute = this.registry.get(name);
|
|
2187
|
-
if (!registeredRoute) throw new
|
|
1693
|
+
if (!registeredRoute) throw new RouterError(`Route name "${name}" was not found in the registry`);
|
|
2188
1694
|
let url = applyTrailingSlash(buildRouteUrl(registeredRoute, name, {
|
|
2189
1695
|
...this._defaults,
|
|
2190
1696
|
...params
|
|
@@ -2304,52 +1810,12 @@ let Uri = class Uri {
|
|
|
2304
1810
|
}
|
|
2305
1811
|
};
|
|
2306
1812
|
Uri = __decorate([
|
|
2307
|
-
|
|
1813
|
+
Request(),
|
|
2308
1814
|
__decorateParam(0, inject(ROUTER_TOKENS.RouteRegistry)),
|
|
2309
1815
|
__decorateParam(1, inject(ROUTER_TOKENS.RouterContext)),
|
|
2310
|
-
__decorateParam(2, inject(DI_TOKENS.Application))
|
|
2311
|
-
__decorateMetadata("design:paramtypes", [
|
|
2312
|
-
Object,
|
|
2313
|
-
Object,
|
|
2314
|
-
Object
|
|
2315
|
-
])
|
|
1816
|
+
__decorateParam(2, inject(DI_TOKENS.Application))
|
|
2316
1817
|
], Uri);
|
|
2317
1818
|
//#endregion
|
|
2318
|
-
//#region src/router/route-url.ts
|
|
2319
|
-
/**
|
|
2320
|
-
* Generate a URL from a named route.
|
|
2321
|
-
*
|
|
2322
|
-
* Keys in `params` matching `:param` placeholders fill the path.
|
|
2323
|
-
* Domain params (`{tenant}`) are also consumed from `params`.
|
|
2324
|
-
* Extra keys become query string parameters.
|
|
2325
|
-
*
|
|
2326
|
-
* Resolves RouteRegistry from the application container via AsyncLocalStorage.
|
|
2327
|
-
* Available after `Application.initialize()` has been called.
|
|
2328
|
-
*
|
|
2329
|
-
* @param name - Named route identifier
|
|
2330
|
-
* @param params - Route params + domain params + extra query params
|
|
2331
|
-
* @returns Generated URL string
|
|
2332
|
-
*
|
|
2333
|
-
* @example
|
|
2334
|
-
* ```typescript
|
|
2335
|
-
* // In a controller (preferred):
|
|
2336
|
-
* ctx.route('users.show', { id: '1' })
|
|
2337
|
-
*
|
|
2338
|
-
* // Outside controllers (standalone function):
|
|
2339
|
-
* import { route } from 'stratal/router'
|
|
2340
|
-
*
|
|
2341
|
-
* route('users.show', { id: '1' })
|
|
2342
|
-
* ```
|
|
2343
|
-
*/
|
|
2344
|
-
function route(name, params) {
|
|
2345
|
-
const container = getContainer();
|
|
2346
|
-
const registry = container.resolve(ROUTER_TOKENS.RouteRegistry);
|
|
2347
|
-
const application = container.resolve(DI_TOKENS.Application);
|
|
2348
|
-
const registeredRoute = registry.get(name);
|
|
2349
|
-
if (!registeredRoute) throw new RouteNameNotFoundError(name);
|
|
2350
|
-
return applyTrailingSlash(buildRouteUrl(registeredRoute, name, params), application.config.trailingSlash ?? "ignore");
|
|
2351
|
-
}
|
|
2352
|
-
//#endregion
|
|
2353
1819
|
//#region src/router/middleware/verify-signature.middleware.ts
|
|
2354
1820
|
/**
|
|
2355
1821
|
* Middleware that verifies signed URLs.
|
|
@@ -2375,158 +1841,12 @@ var VerifySignatureMiddleware = class {
|
|
|
2375
1841
|
async handle(ctx, next) {
|
|
2376
1842
|
const url = ctx.c.req.url;
|
|
2377
1843
|
const secret = ctx.c.env.APP_SECRET;
|
|
2378
|
-
if (!secret) throw new
|
|
1844
|
+
if (!secret) throw new RouterError("Missing required environment variable \"APP_SECRET\"");
|
|
2379
1845
|
if (!await verifySignedUrl(url, secret)) throw new InvalidSignatureError();
|
|
2380
1846
|
await next();
|
|
2381
1847
|
}
|
|
2382
1848
|
};
|
|
2383
1849
|
//#endregion
|
|
2384
|
-
|
|
2385
|
-
/**
|
|
2386
|
-
* I18n Service
|
|
2387
|
-
*
|
|
2388
|
-
* Request-scoped service for translations.
|
|
2389
|
-
* Injects RouterContext to access request-specific locale.
|
|
2390
|
-
* Uses pre-built CoreContext from MessageLoaderService (singleton) for zero-cost lookups.
|
|
2391
|
-
*/
|
|
2392
|
-
let I18nService = class I18nService {
|
|
2393
|
-
loader;
|
|
2394
|
-
routerContext;
|
|
2395
|
-
constructor(loader, routerContext) {
|
|
2396
|
-
this.loader = loader;
|
|
2397
|
-
this.routerContext = routerContext;
|
|
2398
|
-
}
|
|
2399
|
-
/**
|
|
2400
|
-
* Translate a message key
|
|
2401
|
-
*
|
|
2402
|
-
* @param key - Message key (e.g., 'common.actions.save')
|
|
2403
|
-
* @param params - Optional parameters for interpolation
|
|
2404
|
-
* @returns Translated string
|
|
2405
|
-
*/
|
|
2406
|
-
t(key, params) {
|
|
2407
|
-
const context = this.loader.getCoreContext(this.getLocale());
|
|
2408
|
-
const result = params !== void 0 ? translate(context, key, params) : translate(context, key);
|
|
2409
|
-
return typeof result === "string" ? result : key;
|
|
2410
|
-
}
|
|
2411
|
-
/**
|
|
2412
|
-
* Get current locale
|
|
2413
|
-
*
|
|
2414
|
-
* @returns Current locale code from RouterContext or default locale
|
|
2415
|
-
*/
|
|
2416
|
-
getLocale() {
|
|
2417
|
-
return this.routerContext?.getLocale() ?? "en";
|
|
2418
|
-
}
|
|
2419
|
-
};
|
|
2420
|
-
I18nService = __decorate([
|
|
2421
|
-
Transient(I18N_TOKENS.I18nService),
|
|
2422
|
-
__decorateParam(0, inject(I18N_TOKENS.MessageLoader)),
|
|
2423
|
-
__decorateParam(1, inject(ROUTER_TOKENS.RouterContext, { isOptional: true })),
|
|
2424
|
-
__decorateMetadata("design:paramtypes", [Object, Object])
|
|
2425
|
-
], I18nService);
|
|
2426
|
-
//#endregion
|
|
2427
|
-
//#region src/i18n/i18n.module.ts
|
|
2428
|
-
/**
|
|
2429
|
-
* I18n Module
|
|
2430
|
-
*
|
|
2431
|
-
* Core infrastructure module for internationalization.
|
|
2432
|
-
* Provides message translation and locale handling.
|
|
2433
|
-
*
|
|
2434
|
-
* - `forRoot()` configures locale settings (call once in root module)
|
|
2435
|
-
* - `registerMessages()` adds translations (call from any module, as many times as needed)
|
|
2436
|
-
*
|
|
2437
|
-
* @example
|
|
2438
|
-
* ```typescript
|
|
2439
|
-
* @Module({
|
|
2440
|
-
* imports: [
|
|
2441
|
-
* I18nModule.forRoot({ defaultLocale: 'en', locales: ['en', 'fr'] }),
|
|
2442
|
-
* I18nModule.registerMessages(appMessages),
|
|
2443
|
-
* ],
|
|
2444
|
-
* })
|
|
2445
|
-
* export class AppModule {}
|
|
2446
|
-
* ```
|
|
2447
|
-
*
|
|
2448
|
-
* @example Package contributing messages
|
|
2449
|
-
* ```typescript
|
|
2450
|
-
* @Module({
|
|
2451
|
-
* imports: [
|
|
2452
|
-
* I18nModule.registerMessages(tenancyMessages),
|
|
2453
|
-
* ],
|
|
2454
|
-
* })
|
|
2455
|
-
* export class TenancyModule {}
|
|
2456
|
-
* ```
|
|
2457
|
-
*/
|
|
2458
|
-
var _I18nModule;
|
|
2459
|
-
setupI18nCompiler();
|
|
2460
|
-
z.config({ customError: backendErrorMap });
|
|
2461
|
-
let I18nModule = _I18nModule = class I18nModule {
|
|
2462
|
-
/**
|
|
2463
|
-
* Configure I18n locale settings
|
|
2464
|
-
*
|
|
2465
|
-
* Call once in the root module. Does not accept messages —
|
|
2466
|
-
* use `registerMessages()` to add translations.
|
|
2467
|
-
*
|
|
2468
|
-
* @param options - Locale configuration (defaultLocale, fallbackLocale, locales)
|
|
2469
|
-
*/
|
|
2470
|
-
static forRoot(options = {}) {
|
|
2471
|
-
return {
|
|
2472
|
-
module: _I18nModule,
|
|
2473
|
-
providers: [{
|
|
2474
|
-
provide: I18N_TOKENS.Options,
|
|
2475
|
-
useValue: options
|
|
2476
|
-
}]
|
|
2477
|
-
};
|
|
2478
|
-
}
|
|
2479
|
-
/**
|
|
2480
|
-
* Register i18n messages
|
|
2481
|
-
*
|
|
2482
|
-
* Can be called from any module, as many times as needed.
|
|
2483
|
-
* Messages are deep-merged in registration order — later calls override earlier ones at leaf level.
|
|
2484
|
-
*
|
|
2485
|
-
* @param messages - Messages keyed by locale code
|
|
2486
|
-
*
|
|
2487
|
-
* @example App-level messages
|
|
2488
|
-
* ```typescript
|
|
2489
|
-
* I18nModule.registerMessages({
|
|
2490
|
-
* en: { common: { hello: 'Hello' }, errors: { notFound: 'Not found' } },
|
|
2491
|
-
* fr: { common: { hello: 'Bonjour' }, errors: { notFound: 'Introuvable' } },
|
|
2492
|
-
* })
|
|
2493
|
-
* ```
|
|
2494
|
-
*
|
|
2495
|
-
* @example Package-level messages
|
|
2496
|
-
* ```typescript
|
|
2497
|
-
* I18nModule.registerMessages({
|
|
2498
|
-
* en: { tenancy: { tenantNotFound: 'Tenant not found' } },
|
|
2499
|
-
* })
|
|
2500
|
-
* ```
|
|
2501
|
-
*/
|
|
2502
|
-
static registerMessages(messages) {
|
|
2503
|
-
MessageRegistry.addMessages(messages);
|
|
2504
|
-
return {
|
|
2505
|
-
module: _I18nModule,
|
|
2506
|
-
providers: []
|
|
2507
|
-
};
|
|
2508
|
-
}
|
|
2509
|
-
configureRoutes(router) {
|
|
2510
|
-
router.use(I18nContextMiddleware);
|
|
2511
|
-
}
|
|
2512
|
-
};
|
|
2513
|
-
I18nModule = _I18nModule = __decorate([Module({ providers: [
|
|
2514
|
-
{
|
|
2515
|
-
provide: I18N_TOKENS.MessageRegistry,
|
|
2516
|
-
useClass: MessageRegistry,
|
|
2517
|
-
scope: Scope.Singleton
|
|
2518
|
-
},
|
|
2519
|
-
{
|
|
2520
|
-
provide: I18N_TOKENS.MessageLoader,
|
|
2521
|
-
useClass: MessageLoaderService,
|
|
2522
|
-
scope: Scope.Singleton
|
|
2523
|
-
},
|
|
2524
|
-
{
|
|
2525
|
-
provide: I18N_TOKENS.I18nService,
|
|
2526
|
-
useClass: I18nService
|
|
2527
|
-
}
|
|
2528
|
-
] })], I18nModule);
|
|
2529
|
-
//#endregion
|
|
2530
|
-
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 };
|
|
1850
|
+
export { parseDomainPattern as A, uuidParamSchema as C, getRouteMetadata as D, getRouteDecoratedMethods as E, ResponseValidationError as M, SchemaValidationError as N, createMiddlewareChain as O, RouteNotFoundError as P, successMessageSchema as S, Route as T, toOpenAPIPath as _, RouteRegistry as a, paginatedResponseSchema as b, buildDetectorOptions as c, RouteRegistrationService as d, extractDomainParamNames as f, sortRoutesBySpecificity as g, getPathSpecificityScore as h, route as i, InvalidSignatureError as j, createDomainMiddleware as k, resolveI18nOptions as l, generateConventionRouteName as m, Uri as n, VersioningService as o, extractParamNames as p, buildRouteUrl as r, LocalePathService as s, VerifySignatureMiddleware as t, HonoApp as u, commonErrorSchemas as v, validationErrorResponseSchema as w, paginationQuerySchema as x, errorResponseSchema as y };
|
|
2531
1851
|
|
|
2532
|
-
//# sourceMappingURL=
|
|
1852
|
+
//# sourceMappingURL=router-Cy6DjkvP.mjs.map
|