stratal 0.0.17 → 0.0.19
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 +8 -8
- package/dist/{base-email.provider-DypUAfWm.mjs → base-email.provider-mjynzewK.mjs} +1 -1
- package/dist/{base-email.provider-DypUAfWm.mjs.map → base-email.provider-mjynzewK.mjs.map} +1 -1
- package/dist/bin/cloudflare-workers-loader.mjs +33 -1
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +169 -7
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +3 -2
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +3 -10
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{colors-Y7WIFXs7.mjs → colors-DJaRDXoS.mjs} +1 -1
- package/dist/{colors-Y7WIFXs7.mjs.map → colors-DJaRDXoS.mjs.map} +1 -1
- package/dist/{command-B1CPgsrU.mjs → command-BgSlsS4M.mjs} +3 -3
- package/dist/command-BgSlsS4M.mjs.map +1 -0
- package/dist/{command-TnkPYWta.d.mts → command-DsQq56Lp.d.mts} +2 -2
- package/dist/{command-TnkPYWta.d.mts.map → command-DsQq56Lp.d.mts.map} +1 -1
- package/dist/config/index.d.mts +81 -37
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +135 -61
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts → consumer-registry-Doom7BEh.d.mts} +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts.map → consumer-registry-Doom7BEh.d.mts.map} +1 -1
- package/dist/controller.decorator-LZY9aHYG.mjs +66 -0
- package/dist/controller.decorator-LZY9aHYG.mjs.map +1 -0
- package/dist/cron/index.d.mts +4 -3
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -3
- package/dist/{cron-manager-CFBamKKk.mjs → cron-manager-C30t9UZM.mjs} +29 -19
- package/dist/cron-manager-C30t9UZM.mjs.map +1 -0
- package/dist/{cron-manager-D7imGwUT.d.mts → cron-manager-RuPtFVLy.d.mts} +28 -14
- package/dist/cron-manager-RuPtFVLy.d.mts.map +1 -0
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +3 -3
- package/dist/email/index.d.mts +3 -3
- package/dist/email/index.mjs +87 -18
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-DaewN8hc.mjs → en-rHmW6vD9.mjs} +14 -31
- package/dist/en-rHmW6vD9.mjs.map +1 -0
- package/dist/env-CamWD-U1.d.mts +25 -0
- package/dist/env-CamWD-U1.d.mts.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +2 -3
- package/dist/errors-B4pYgYON.mjs +1714 -0
- package/dist/errors-B4pYgYON.mjs.map +1 -0
- package/dist/{errors-DuAR5Wke.mjs → errors-BUyUfr2Z.mjs} +14 -7
- package/dist/errors-BUyUfr2Z.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +1 -2
- package/dist/{events-CvUSgEuN.mjs → events-COKixqnG.mjs} +2 -2
- package/dist/{events-CvUSgEuN.mjs.map → events-COKixqnG.mjs.map} +1 -1
- package/dist/{gateway-context-CNOLkLUC.mjs → gateway-context-cqZ8wMoi.mjs} +4 -9
- package/dist/gateway-context-cqZ8wMoi.mjs.map +1 -0
- package/dist/guards/index.d.mts +14 -5
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DUk_Kzst.mjs → guards-DMbsAxSX.mjs} +1 -1
- package/dist/guards-DMbsAxSX.mjs.map +1 -0
- package/dist/http-method.decorator-BT3ufnz8.mjs +96 -0
- package/dist/http-method.decorator-BT3ufnz8.mjs.map +1 -0
- package/dist/i18n/index.d.mts +3 -3
- package/dist/i18n/index.mjs +3 -16
- package/dist/i18n/messages/en/index.d.mts +1 -1
- package/dist/i18n/messages/en/index.mjs +1 -1
- package/dist/i18n/utils/index.d.mts +30 -0
- package/dist/i18n/utils/index.d.mts.map +1 -0
- package/dist/i18n/utils/index.mjs +2 -0
- package/dist/i18n/validation/index.d.mts +1 -1
- package/dist/i18n/validation/index.mjs +1 -1
- package/dist/i18n.module-CI_prYFD.mjs +2340 -0
- package/dist/i18n.module-CI_prYFD.mjs.map +1 -0
- package/dist/{index-NGxg-KP_.d.mts → index-B437eK7p.d.mts} +59 -16
- package/dist/index-B437eK7p.d.mts.map +1 -0
- package/dist/{index-Dp6A5ywM.d.mts → index-CWRS7Ri3.d.mts} +1 -1
- package/dist/{index-Dp6A5ywM.d.mts.map → index-CWRS7Ri3.d.mts.map} +1 -1
- package/dist/{index-D_w_Rmtd.d.mts → index-DFhEeFfC.d.mts} +13 -30
- package/dist/{index-D_w_Rmtd.d.mts.map → index-DFhEeFfC.d.mts.map} +1 -1
- package/dist/index-DPFqRs8L.d.mts +4318 -0
- package/dist/index-DPFqRs8L.d.mts.map +1 -0
- package/dist/{index-DGRe6Yoa.d.mts → index-Dnqm9ZB6.d.mts} +5 -4
- package/dist/index-Dnqm9ZB6.d.mts.map +1 -0
- package/dist/index-SHx31sBJ.d.mts +101 -0
- package/dist/index-SHx31sBJ.d.mts.map +1 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -20
- package/dist/{is-command-DJVI6wEJ.mjs → is-command-C6a7WTPw.mjs} +2 -2
- package/dist/{is-command-DJVI6wEJ.mjs.map → is-command-C6a7WTPw.mjs.map} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs → is-seeder-CebjZCDn.mjs} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
- package/dist/logger/index.d.mts +1 -1
- package/dist/logger/index.mjs +1 -1
- package/dist/{logger-CGT91VY6.mjs → logger-V6Ms3QnQ.mjs} +63 -45
- package/dist/logger-V6Ms3QnQ.mjs.map +1 -0
- package/dist/macroable/index.d.mts +2 -0
- package/dist/macroable/index.mjs +2 -0
- package/dist/macroable-BmufBshB.mjs +122 -0
- package/dist/macroable-BmufBshB.mjs.map +1 -0
- package/dist/module/index.d.mts +3 -4
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +1 -10
- package/dist/module-qGE_1duv.mjs +732 -0
- package/dist/module-qGE_1duv.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.mjs +2 -15
- package/dist/{openapi-tools.service-B3TxYKoQ.mjs → openapi-tools.service-CYWGuhue.mjs} +4 -1
- package/dist/{openapi-tools.service-B3TxYKoQ.mjs.map → openapi-tools.service-CYWGuhue.mjs.map} +1 -1
- package/dist/{openapi.service-DGnX3Fc4.d.mts → openapi.service-Bv_NioM9.d.mts} +9 -17
- package/dist/openapi.service-Bv_NioM9.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +26 -12
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +4 -8
- package/dist/{quarry-registry-B2rkO-JS.mjs → quarry-registry-DFfRRkA7.mjs} +45 -40
- package/dist/quarry-registry-DFfRRkA7.mjs.map +1 -0
- package/dist/queue/index.d.mts +2 -2
- package/dist/queue/index.mjs +3 -13
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BtI8f4Jo.mjs → queue.module-P-G-nCYz.mjs} +39 -42
- package/dist/queue.module-P-G-nCYz.mjs.map +1 -0
- package/dist/r2-storage.provider-LdzK9tfG.mjs +244 -0
- package/dist/r2-storage.provider-LdzK9tfG.mjs.map +1 -0
- package/dist/{resend.provider-bXMEkdRJ.mjs → resend.provider-bwILp0WI.mjs} +2 -4
- package/dist/{resend.provider-bXMEkdRJ.mjs.map → resend.provider-bwILp0WI.mjs.map} +1 -1
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +7 -16
- package/dist/seeder/index.d.mts +3 -4
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +2 -6
- package/dist/{seeder-R7RXJC35.mjs → seeder-BcqIFa2X.mjs} +5 -5
- package/dist/{seeder-R7RXJC35.mjs.map → seeder-BcqIFa2X.mjs.map} +1 -1
- package/dist/setup-CtekcwuO.mjs +37 -0
- package/dist/setup-CtekcwuO.mjs.map +1 -0
- package/dist/signed-url-COX7cCWR.mjs +74 -0
- package/dist/signed-url-COX7cCWR.mjs.map +1 -0
- package/dist/{smtp.provider-DrbHQztF.mjs → smtp.provider-B07yuARi.mjs} +2 -4
- package/dist/{smtp.provider-DrbHQztF.mjs.map → smtp.provider-B07yuARi.mjs.map} +1 -1
- package/dist/storage/index.d.mts +39 -17
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +3 -14
- package/dist/storage/providers/index.d.mts +30 -69
- package/dist/storage/providers/index.d.mts.map +1 -1
- package/dist/storage/providers/index.mjs +2 -5
- package/dist/{storage-CZKHOhci.mjs → storage-P6X4h9So.mjs} +102 -29
- package/dist/storage-P6X4h9So.mjs.map +1 -0
- package/dist/{storage-provider.interface-0IqcdhBf.d.mts → storage-provider.interface-CC1nniHk.d.mts} +20 -15
- package/dist/storage-provider.interface-CC1nniHk.d.mts.map +1 -0
- package/dist/stratal-BCiwCFN9.mjs +533 -0
- package/dist/stratal-BCiwCFN9.mjs.map +1 -0
- package/dist/{types-DahElfUw.d.mts → types-DIWemRad.d.mts} +2 -2
- package/dist/types-DIWemRad.d.mts.map +1 -0
- package/dist/{usage-generator-CVIsENuE.mjs → usage-generator-MBcRo0Q2.mjs} +2 -2
- package/dist/{usage-generator-CVIsENuE.mjs.map → usage-generator-MBcRo0Q2.mjs.map} +1 -1
- package/dist/{validation-DQTC259A.mjs → validation-Dbg3ehdP.mjs} +2 -2
- package/dist/{validation-DQTC259A.mjs.map → validation-Dbg3ehdP.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +3 -3
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +1 -4
- package/dist/workers/index.d.mts +2 -1
- package/dist/workers/index.d.mts.map +1 -1
- package/dist/workers/index.mjs +2 -20
- package/dist/workers/index.mjs.map +1 -1
- package/package.json +41 -50
- package/dist/application-DfPtIzxF.d.mts +0 -177
- package/dist/application-DfPtIzxF.d.mts.map +0 -1
- package/dist/command-B1CPgsrU.mjs.map +0 -1
- package/dist/cron-manager-CFBamKKk.mjs.map +0 -1
- package/dist/cron-manager-D7imGwUT.d.mts.map +0 -1
- package/dist/en-DaewN8hc.mjs.map +0 -1
- package/dist/errors-DSKapqD8.mjs +0 -707
- package/dist/errors-DSKapqD8.mjs.map +0 -1
- package/dist/errors-DuAR5Wke.mjs.map +0 -1
- package/dist/gateway-context-CNOLkLUC.mjs.map +0 -1
- package/dist/guards-DUk_Kzst.mjs.map +0 -1
- package/dist/i18n.module-Dn9SrFdS.mjs +0 -1841
- package/dist/i18n.module-Dn9SrFdS.mjs.map +0 -1
- package/dist/index-BFCxSp_f.d.mts +0 -2625
- package/dist/index-BFCxSp_f.d.mts.map +0 -1
- package/dist/index-DGRe6Yoa.d.mts.map +0 -1
- package/dist/index-NGxg-KP_.d.mts.map +0 -1
- package/dist/logger-CGT91VY6.mjs.map +0 -1
- package/dist/middleware/index.d.mts +0 -2
- package/dist/middleware/index.mjs +0 -5
- package/dist/middleware-Bl-b5pkt.mjs +0 -362
- package/dist/middleware-Bl-b5pkt.mjs.map +0 -1
- package/dist/module-registry-CmjBX6ol.d.mts +0 -121
- package/dist/module-registry-CmjBX6ol.d.mts.map +0 -1
- package/dist/module-tUtyVJ5E.mjs +0 -371
- package/dist/module-tUtyVJ5E.mjs.map +0 -1
- package/dist/openapi.service-DGnX3Fc4.d.mts.map +0 -1
- package/dist/quarry-registry-B2rkO-JS.mjs.map +0 -1
- package/dist/queue.module-BtI8f4Jo.mjs.map +0 -1
- package/dist/router-context-D9R1v2Ac.mjs +0 -267
- package/dist/router-context-D9R1v2Ac.mjs.map +0 -1
- package/dist/s3-storage.provider-CttzNnDR.mjs +0 -335
- package/dist/s3-storage.provider-CttzNnDR.mjs.map +0 -1
- package/dist/storage-CZKHOhci.mjs.map +0 -1
- package/dist/storage-provider.interface-0IqcdhBf.d.mts.map +0 -1
- package/dist/stratal-D5smIU1y.mjs +0 -315
- package/dist/stratal-D5smIU1y.mjs.map +0 -1
- package/dist/types-DahElfUw.d.mts.map +0 -1
|
@@ -0,0 +1,4318 @@
|
|
|
1
|
+
import { t as Constructor } from "./types-DIWemRad.mjs";
|
|
2
|
+
import { t as Macroable } from "./index-SHx31sBJ.mjs";
|
|
3
|
+
import { t as StratalEnv } from "./env-CamWD-U1.mjs";
|
|
4
|
+
import { t as index_d_exports } from "./index-DFhEeFfC.mjs";
|
|
5
|
+
import { a as index_d_exports$1, b as MessageKeys, i as ZodError, o as z, t as OpenAPIHono, v as II18nService, x as MessageParams, y as MessageKeyPrefix } from "./index-B437eK7p.mjs";
|
|
6
|
+
import { i as LoggerService, l as LogLevel } from "./index-CWRS7Ri3.mjs";
|
|
7
|
+
import { DependencyContainer, DependencyContainer as DependencyContainer$1, container as container$1, delay, inject as inject$1, injectable as injectable$1, instancePerContainerCachingFactory as instancePerContainerCachingFactory$1, singleton } from "tsyringe";
|
|
8
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
|
+
import { SSEMessage, SSEStreamingApi, SSEStreamingApi as SSEStreamingApi$1 } from "hono/streaming";
|
|
10
|
+
import { CoreContext } from "@intlify/core-base";
|
|
11
|
+
import { DetectorOptions } from "hono/language";
|
|
12
|
+
import { Context, MiddlewareHandler, Next } from "hono";
|
|
13
|
+
import { ContentfulStatusCode, RedirectStatusCode } from "hono/utils/http-status";
|
|
14
|
+
import { StreamingApi, StreamingApi as StreamingApi$1 } from "hono/utils/stream";
|
|
15
|
+
import InjectionToken$1, { default as InjectionToken$2 } from "tsyringe/dist/typings/providers/injection-token";
|
|
16
|
+
|
|
17
|
+
//#region src/errors/error-response.d.ts
|
|
18
|
+
type Environment = 'development' | 'staging' | 'production';
|
|
19
|
+
interface ErrorResponse {
|
|
20
|
+
/**
|
|
21
|
+
* Numeric error code for identification and escalation
|
|
22
|
+
* See error-codes.ts for the complete registry
|
|
23
|
+
*/
|
|
24
|
+
code: number;
|
|
25
|
+
/**
|
|
26
|
+
* Human-readable error message
|
|
27
|
+
* Fixed per error type, not customizable
|
|
28
|
+
*/
|
|
29
|
+
message: string;
|
|
30
|
+
/**
|
|
31
|
+
* ISO timestamp when the error occurred
|
|
32
|
+
*/
|
|
33
|
+
timestamp: string;
|
|
34
|
+
/**
|
|
35
|
+
* Additional structured data about the error
|
|
36
|
+
* Only included in development environment
|
|
37
|
+
*/
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Stack trace for debugging
|
|
41
|
+
* Only included in development environment
|
|
42
|
+
*/
|
|
43
|
+
stack?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Type guard to check if an object is an ErrorResponse
|
|
47
|
+
*/
|
|
48
|
+
declare function isErrorResponse(obj: unknown): obj is ErrorResponse;
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/di/types.d.ts
|
|
51
|
+
/**
|
|
52
|
+
* Service scope for DI registration
|
|
53
|
+
*
|
|
54
|
+
* Maps directly to tsyringe's Lifecycle enum.
|
|
55
|
+
* Scope is specified at registration time via provider configuration,
|
|
56
|
+
* not at class decoration time.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // In module providers:
|
|
61
|
+
* { provide: MY_TOKEN, useClass: MyService, scope: Scope.Singleton }
|
|
62
|
+
*
|
|
63
|
+
* // In Application.ts:
|
|
64
|
+
* container.register(MY_TOKEN, MyService, Scope.Request)
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare enum Scope {
|
|
68
|
+
/** New instance per resolution (default) */
|
|
69
|
+
Transient = 0,
|
|
70
|
+
/** Single instance shared globally */
|
|
71
|
+
Singleton = 1,
|
|
72
|
+
/** New instance per child container (per request) */
|
|
73
|
+
Request = 3
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Options for conditional binding with `when()` method
|
|
77
|
+
*/
|
|
78
|
+
interface WhenOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Cache predicate result after first evaluation.
|
|
81
|
+
* When true, the predicate is evaluated once and the result is reused.
|
|
82
|
+
* When false (default), predicate is evaluated on each resolution.
|
|
83
|
+
*/
|
|
84
|
+
cache?: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Decorator function type for extend() method
|
|
88
|
+
*
|
|
89
|
+
* @template T The service type being decorated
|
|
90
|
+
*/
|
|
91
|
+
type ExtensionDecorator<T> = (service: T, container: ContainerLike) => T;
|
|
92
|
+
/**
|
|
93
|
+
* Minimal container interface for decorator functions
|
|
94
|
+
* Avoids circular dependency with Container class
|
|
95
|
+
*/
|
|
96
|
+
interface ContainerLike {
|
|
97
|
+
resolve<T>(token: InjectionToken$1<T>): T;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/di/conditional-binding-builder.d.ts
|
|
101
|
+
/**
|
|
102
|
+
* Container interface for predicate functions
|
|
103
|
+
* Using a minimal interface to avoid circular imports
|
|
104
|
+
*/
|
|
105
|
+
interface PredicateContainer {
|
|
106
|
+
resolve<T>(token: InjectionToken$1<T>): T;
|
|
107
|
+
isRegistered<T>(token: InjectionToken$1<T>): boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Initial builder returned by container.when()
|
|
111
|
+
*/
|
|
112
|
+
interface ConditionalBindingBuilder {
|
|
113
|
+
/**
|
|
114
|
+
* Specify the token to conditionally bind
|
|
115
|
+
*
|
|
116
|
+
* @param token - DI token for the service
|
|
117
|
+
* @returns Builder for specifying implementations
|
|
118
|
+
*/
|
|
119
|
+
use<T extends object>(token: InjectionToken$1<T>): ConditionalBindingUse<T>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Builder after specifying token with use()
|
|
123
|
+
*/
|
|
124
|
+
interface ConditionalBindingUse<T extends object> {
|
|
125
|
+
/**
|
|
126
|
+
* Specify the implementation when predicate returns true.
|
|
127
|
+
* Registration is completed immediately.
|
|
128
|
+
*
|
|
129
|
+
* If predicate is false at resolution time:
|
|
130
|
+
* - Uses `otherwise()` implementation if provided
|
|
131
|
+
* - Falls back to existing registration if available
|
|
132
|
+
* - Throws error if no fallback exists
|
|
133
|
+
*
|
|
134
|
+
* @param implementation - Service class to use when predicate is true
|
|
135
|
+
* @returns Builder for optional fallback specification
|
|
136
|
+
*/
|
|
137
|
+
give(implementation: Constructor<T>): ConditionalBindingGive<T>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Builder after specifying true implementation with give()
|
|
141
|
+
* Registration is already complete at this point.
|
|
142
|
+
*/
|
|
143
|
+
interface ConditionalBindingGive<T extends object> {
|
|
144
|
+
/**
|
|
145
|
+
* Optionally specify a fallback implementation when predicate returns false.
|
|
146
|
+
* This re-registers with the explicit fallback instead of existing registration.
|
|
147
|
+
*
|
|
148
|
+
* @param implementation - Service class to use when predicate is false
|
|
149
|
+
*/
|
|
150
|
+
otherwise(implementation: Constructor<T>): void;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Implementation of ConditionalBindingBuilder
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
157
|
+
declare class ConditionalBindingBuilderImpl implements ConditionalBindingBuilder {
|
|
158
|
+
private readonly tsyringeContainer;
|
|
159
|
+
private readonly predicateContainer;
|
|
160
|
+
private readonly predicate;
|
|
161
|
+
private readonly options;
|
|
162
|
+
constructor(tsyringeContainer: DependencyContainer, predicateContainer: PredicateContainer, predicate: (container: PredicateContainer) => boolean, options: WhenOptions);
|
|
163
|
+
use<T extends object>(token: InjectionToken$1<T>): ConditionalBindingUse<T>;
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/di/container.d.ts
|
|
167
|
+
/**
|
|
168
|
+
* Options for creating a Container instance
|
|
169
|
+
*/
|
|
170
|
+
interface ContainerOptions {
|
|
171
|
+
/** Pre-created DependencyContainer */
|
|
172
|
+
container: DependencyContainer;
|
|
173
|
+
/** Whether this is a request-scoped container */
|
|
174
|
+
isRequestScoped?: boolean;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Unified Container for DI management
|
|
178
|
+
*
|
|
179
|
+
* Manages the two-tier container hierarchy:
|
|
180
|
+
* - Global scope: Singletons, base instances of request-scoped services
|
|
181
|
+
* - Request scope: Context-enriched instances per HTTP request
|
|
182
|
+
*
|
|
183
|
+
* @example Basic registration
|
|
184
|
+
* ```typescript
|
|
185
|
+
* import { container as tsyringeRootContainer } from 'tsyringe'
|
|
186
|
+
*
|
|
187
|
+
* const container = new Container({
|
|
188
|
+
* container: tsyringeRootContainer.createChildContainer()
|
|
189
|
+
* })
|
|
190
|
+
*
|
|
191
|
+
* container.register(I18nService)
|
|
192
|
+
* container.register(MY_TOKEN, MyService)
|
|
193
|
+
* container.registerSingleton(ConfigService)
|
|
194
|
+
* container.registerValue(MY_TOKEN, myInstance)
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @example Request scope (automatic lifecycle)
|
|
198
|
+
* ```typescript
|
|
199
|
+
* await container.runInRequestScope(routerContext, async (requestContainer) => {
|
|
200
|
+
* const i18n = requestContainer.resolve(I18N_TOKEN)
|
|
201
|
+
* })
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare class Container {
|
|
205
|
+
private readonly container;
|
|
206
|
+
private readonly isRequestScoped;
|
|
207
|
+
constructor(options: ContainerOptions);
|
|
208
|
+
/**
|
|
209
|
+
* Register a service with optional explicit token and scope
|
|
210
|
+
*/
|
|
211
|
+
register<T extends object>(serviceClass: Constructor<T>, scope?: Scope): void;
|
|
212
|
+
register<T extends object>(token: InjectionToken$1<T>, serviceClass: Constructor<T>, scope?: Scope): void;
|
|
213
|
+
/**
|
|
214
|
+
* Register a service as singleton
|
|
215
|
+
*/
|
|
216
|
+
registerSingleton<T extends object>(serviceClass: Constructor<T>): void;
|
|
217
|
+
registerSingleton<T extends object>(token: InjectionToken$1<T>, serviceClass: Constructor<T>): void;
|
|
218
|
+
/**
|
|
219
|
+
* Register a value (instance) directly
|
|
220
|
+
*/
|
|
221
|
+
registerValue<T>(token: InjectionToken$1<T>, value: T): void;
|
|
222
|
+
/**
|
|
223
|
+
* Register with factory function
|
|
224
|
+
*/
|
|
225
|
+
registerFactory<T>(token: InjectionToken$1<T>, factory: (container: Container) => T): void;
|
|
226
|
+
/**
|
|
227
|
+
* Register an alias to an existing token
|
|
228
|
+
*/
|
|
229
|
+
registerExisting<T>(alias: InjectionToken$1<T>, target: InjectionToken$1<T>): void;
|
|
230
|
+
/**
|
|
231
|
+
* Resolve a service from the container
|
|
232
|
+
*/
|
|
233
|
+
resolve<T>(token: InjectionToken$1<T>): T;
|
|
234
|
+
/**
|
|
235
|
+
* Check if a token is registered
|
|
236
|
+
*/
|
|
237
|
+
isRegistered<T>(token: InjectionToken$1<T>): boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Start a conditional binding with predicate evaluation
|
|
240
|
+
*/
|
|
241
|
+
when(predicate: (container: PredicateContainer) => boolean, options?: WhenOptions): ConditionalBindingBuilder;
|
|
242
|
+
/**
|
|
243
|
+
* Replace a service registration with a decorated version
|
|
244
|
+
*/
|
|
245
|
+
extend<T>(token: InjectionToken$1<T>, decorator: ExtensionDecorator<T>): void;
|
|
246
|
+
/**
|
|
247
|
+
* Run callback within request scope
|
|
248
|
+
*
|
|
249
|
+
* Creates a child container with fresh instances for services registered with `scope: Scope.Request`.
|
|
250
|
+
* Callback receives the request-scoped container as argument.
|
|
251
|
+
*
|
|
252
|
+
* Can only be called on global container (not request-scoped).
|
|
253
|
+
*/
|
|
254
|
+
runInRequestScope<T>(routerContext: RouterContext, callback: (requestContainer: Container) => T | Promise<T>): Promise<T>;
|
|
255
|
+
/**
|
|
256
|
+
* Create request scope container
|
|
257
|
+
*
|
|
258
|
+
* Can only be called on global container (not request-scoped).
|
|
259
|
+
*/
|
|
260
|
+
createRequestScope(routerContext: RouterContext): Container;
|
|
261
|
+
/**
|
|
262
|
+
* Get underlying tsyringe container
|
|
263
|
+
*/
|
|
264
|
+
getTsyringeContainer(): DependencyContainer;
|
|
265
|
+
dispose(): void | Promise<void>;
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region src/router/route-map.d.ts
|
|
269
|
+
/**
|
|
270
|
+
* Augmentable route map for type-safe URL generation.
|
|
271
|
+
*
|
|
272
|
+
* Users augment this interface via `declare module 'stratal/router'` to get
|
|
273
|
+
* autocomplete on `route()` calls and type-checked params.
|
|
274
|
+
* Generated automatically by `quarry route:types`.
|
|
275
|
+
*
|
|
276
|
+
* Follows the same augmentation pattern as `CustomEventRegistry` in `stratal/events`.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* declare module 'stratal/router' {
|
|
281
|
+
* interface StratalRouteMap {
|
|
282
|
+
* 'users.index': { params: never }
|
|
283
|
+
* 'users.show': { params: { id: string } }
|
|
284
|
+
* 'tenant.dashboard': { params: { tenant: string } }
|
|
285
|
+
* }
|
|
286
|
+
* }
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
interface StratalRouteMap {}
|
|
290
|
+
/**
|
|
291
|
+
* All valid route names.
|
|
292
|
+
* Falls back to `string` when no routes are registered in StratalRouteMap.
|
|
293
|
+
*/
|
|
294
|
+
type RouteName = keyof StratalRouteMap extends never ? string : Extract<keyof StratalRouteMap, string>;
|
|
295
|
+
/**
|
|
296
|
+
* Resolves the required params for a named route.
|
|
297
|
+
* When StratalRouteMap is augmented, provides type-safe param objects.
|
|
298
|
+
* Falls back to `Record<string, string> | undefined` for untyped routes.
|
|
299
|
+
*/
|
|
300
|
+
type RouteParams<N extends RouteName> = N extends keyof StratalRouteMap ? StratalRouteMap[N] extends {
|
|
301
|
+
params: infer P;
|
|
302
|
+
} ? [P] extends [never] ? Record<string, string> | undefined : P : Record<string, string> | undefined : Record<string, string> | undefined;
|
|
303
|
+
/**
|
|
304
|
+
* Minimal route data for client-side URL generation.
|
|
305
|
+
* Contains only the fields needed by URL building logic — no server metadata.
|
|
306
|
+
*/
|
|
307
|
+
interface SerializedRoute {
|
|
308
|
+
path: string;
|
|
309
|
+
paramNames: string[];
|
|
310
|
+
domain?: string;
|
|
311
|
+
domainParamNames: string[];
|
|
312
|
+
localePaths?: string[];
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* A record of named routes keyed by route name, used for client-side URL building.
|
|
316
|
+
* Serialized from `RouteRegistry.named()` on the server, consumed by `useRoute()` on the client.
|
|
317
|
+
*/
|
|
318
|
+
type SerializedRoutes = Record<string, SerializedRoute>;
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/di/tokens.d.ts
|
|
321
|
+
/**
|
|
322
|
+
* Token for the Container instance
|
|
323
|
+
* Used for injecting the Container into services that need dynamic resolution
|
|
324
|
+
*/
|
|
325
|
+
declare const CONTAINER_TOKEN: unique symbol;
|
|
326
|
+
declare const DI_TOKENS: {
|
|
327
|
+
readonly CloudflareEnv: symbol;
|
|
328
|
+
readonly ExecutionContext: symbol;
|
|
329
|
+
readonly Container: typeof CONTAINER_TOKEN;
|
|
330
|
+
readonly Application: symbol;
|
|
331
|
+
readonly ModuleRegistry: symbol;
|
|
332
|
+
readonly ExceptionHandler: symbol;
|
|
333
|
+
readonly Database: symbol;
|
|
334
|
+
readonly Queue: symbol;
|
|
335
|
+
readonly ConsumerRegistry: symbol;
|
|
336
|
+
readonly Cron: symbol;
|
|
337
|
+
readonly EventRegistry: symbol;
|
|
338
|
+
readonly Quarry: symbol;
|
|
339
|
+
/**
|
|
340
|
+
* AuthContext: Use for services that need user authentication (userId).
|
|
341
|
+
*/
|
|
342
|
+
readonly AuthContext: symbol;
|
|
343
|
+
readonly DurableObjectState: symbol;
|
|
344
|
+
readonly DurableObjectId: symbol;
|
|
345
|
+
};
|
|
346
|
+
type DIToken = typeof DI_TOKENS[keyof typeof DI_TOKENS];
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/di/decorators/inject-param.decorator.d.ts
|
|
349
|
+
/**
|
|
350
|
+
* Metadata key for storing parameter injection information
|
|
351
|
+
*/
|
|
352
|
+
declare const INJECT_PARAM_METADATA_KEY: unique symbol;
|
|
353
|
+
/**
|
|
354
|
+
* Describes a parameter injection
|
|
355
|
+
*/
|
|
356
|
+
interface ParamInjection {
|
|
357
|
+
/** Parameter index in the method signature (0-based) */
|
|
358
|
+
index: number;
|
|
359
|
+
/** DI token to resolve */
|
|
360
|
+
token: InjectionToken$1;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Mark a method parameter for DI injection
|
|
364
|
+
*
|
|
365
|
+
* The parameter will be resolved from the request-scoped container
|
|
366
|
+
* when the controller method is invoked.
|
|
367
|
+
*
|
|
368
|
+
* @param token - DI token to resolve (class or symbol)
|
|
369
|
+
*
|
|
370
|
+
* @example With class token
|
|
371
|
+
* ```typescript
|
|
372
|
+
* async show(
|
|
373
|
+
* ctx: RouterContext,
|
|
374
|
+
* @InjectParam(UserService) userService: UserService
|
|
375
|
+
* ) { }
|
|
376
|
+
* ```
|
|
377
|
+
*
|
|
378
|
+
* @example With symbol token
|
|
379
|
+
* ```typescript
|
|
380
|
+
* async show(
|
|
381
|
+
* ctx: RouterContext,
|
|
382
|
+
* @InjectParam(DI_TOKENS.Cache) cache: ICacheService
|
|
383
|
+
* ) { }
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
declare function InjectParam<T>(token: InjectionToken$1<T>): ParameterDecorator;
|
|
387
|
+
/**
|
|
388
|
+
* Get method parameter injections
|
|
389
|
+
*
|
|
390
|
+
* @param target - Controller prototype
|
|
391
|
+
* @param propertyKey - Method name
|
|
392
|
+
* @returns Array of parameter injections sorted by index
|
|
393
|
+
*/
|
|
394
|
+
declare function getMethodInjections(target: object, propertyKey: string | symbol): ParamInjection[];
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/di/decorators.d.ts
|
|
397
|
+
/**
|
|
398
|
+
* Mark a class as injectable
|
|
399
|
+
*
|
|
400
|
+
* This decorator wraps tsyringe's `@injectable` decorator and optionally
|
|
401
|
+
* associates a token with the class. The actual lifecycle (Singleton, Request,
|
|
402
|
+
* Transient) is determined at registration time, not decoration time.
|
|
403
|
+
*
|
|
404
|
+
* **Lifecycle Control:**
|
|
405
|
+
* - Use `scope: Scope.Singleton` in module providers for singleton
|
|
406
|
+
* - Use `scope: Scope.Request` in module providers for request-scoped
|
|
407
|
+
* - Default is Transient (new instance per resolution)
|
|
408
|
+
*
|
|
409
|
+
* @param token - Optional DI token for service resolution
|
|
410
|
+
*
|
|
411
|
+
* @example Basic usage (no token)
|
|
412
|
+
* ```typescript
|
|
413
|
+
* @Transient()
|
|
414
|
+
* export class UserService {
|
|
415
|
+
* constructor(@inject(DI_TOKENS.Database) private db: DatabaseService) {}
|
|
416
|
+
* }
|
|
417
|
+
*
|
|
418
|
+
* // In module:
|
|
419
|
+
* @Module({
|
|
420
|
+
* providers: [UserService] // Transient by default
|
|
421
|
+
* })
|
|
422
|
+
* ```
|
|
423
|
+
*
|
|
424
|
+
* @example With token
|
|
425
|
+
* ```typescript
|
|
426
|
+
* @Transient(DI_TOKENS.ConnectionManager)
|
|
427
|
+
* export class ConnectionManager implements Disposable {
|
|
428
|
+
* // ...
|
|
429
|
+
* }
|
|
430
|
+
*
|
|
431
|
+
* // In Application.ts:
|
|
432
|
+
* container.register(DI_TOKENS.ConnectionManager, ConnectionManager, Scope.Request)
|
|
433
|
+
* ```
|
|
434
|
+
*
|
|
435
|
+
* @example Singleton via provider scope
|
|
436
|
+
* ```typescript
|
|
437
|
+
* @Transient()
|
|
438
|
+
* export class ConsumerRegistry {
|
|
439
|
+
* // ...
|
|
440
|
+
* }
|
|
441
|
+
*
|
|
442
|
+
* // In module:
|
|
443
|
+
* @Module({
|
|
444
|
+
* providers: [
|
|
445
|
+
* { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry, scope: Scope.Singleton }
|
|
446
|
+
* ]
|
|
447
|
+
* })
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
declare function Transient<T>(token?: InjectionToken$1<T>): <TFunction extends abstract new (...args: never[]) => unknown>(target: TFunction) => TFunction;
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/di/errors/conditional-binding-fallback.error.d.ts
|
|
453
|
+
/**
|
|
454
|
+
* ConditionalBindingFallbackError
|
|
455
|
+
*
|
|
456
|
+
* Thrown when a conditional binding predicate returns false but no fallback
|
|
457
|
+
* implementation was provided and no existing registration exists for the token.
|
|
458
|
+
*
|
|
459
|
+
* This typically indicates a misconfiguration in the DI setup where:
|
|
460
|
+
* - A `when().use().give()` chain was used without `otherwise()`
|
|
461
|
+
* - AND the token wasn't previously registered
|
|
462
|
+
* - AND the predicate evaluated to false at resolution time
|
|
463
|
+
*/
|
|
464
|
+
declare class ConditionalBindingFallbackError extends ApplicationError {
|
|
465
|
+
constructor(token: string);
|
|
466
|
+
}
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region src/di/errors/request-scope-operation-not-allowed.error.d.ts
|
|
469
|
+
/**
|
|
470
|
+
* RequestScopeOperationNotAllowedError
|
|
471
|
+
*
|
|
472
|
+
* Thrown when attempting to call a method that is not allowed on the current container scope.
|
|
473
|
+
* - `createRequestScope()` and `runInRequestScope()` can only be called on global containers
|
|
474
|
+
*/
|
|
475
|
+
declare class RequestScopeOperationNotAllowedError extends ApplicationError {
|
|
476
|
+
constructor(methodName: string);
|
|
477
|
+
}
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/di/container-storage.d.ts
|
|
480
|
+
/**
|
|
481
|
+
* AsyncLocalStorage for the application container.
|
|
482
|
+
*
|
|
483
|
+
* Set by `Application.initialize()` — all code from that point onward
|
|
484
|
+
* (Stratal handlers, TestingModuleBuilder, standalone functions like `route()`)
|
|
485
|
+
* can access the container without DI or static singletons.
|
|
486
|
+
*
|
|
487
|
+
* Follows the same pattern as `errorMapContextStorage` in `i18n/validation/validation.context.ts`.
|
|
488
|
+
*/
|
|
489
|
+
declare const containerStorage: AsyncLocalStorage<Container>;
|
|
490
|
+
/**
|
|
491
|
+
* Get the application container from AsyncLocalStorage.
|
|
492
|
+
*
|
|
493
|
+
* @throws ContainerNotInitializedError if called outside `Application.initialize()` scope
|
|
494
|
+
*/
|
|
495
|
+
declare function getContainer(): Container;
|
|
496
|
+
/**
|
|
497
|
+
* Run a function within a container context.
|
|
498
|
+
*
|
|
499
|
+
* @param container - The application container to store
|
|
500
|
+
* @param fn - The function to execute with container access
|
|
501
|
+
*/
|
|
502
|
+
declare function runWithContainer<T>(container: Container, fn: () => T): T;
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region src/router/constants.d.ts
|
|
505
|
+
/**
|
|
506
|
+
* Type-safe context keys for Hono router variables
|
|
507
|
+
* Using symbols to avoid string collisions
|
|
508
|
+
*/
|
|
509
|
+
declare const ROUTER_CONTEXT_KEYS: {
|
|
510
|
+
readonly REQUEST_CONTAINER: "requestContainer";
|
|
511
|
+
readonly LOCALE: "locale";
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* Metadata keys for storing route and controller configuration
|
|
515
|
+
* Using symbols to avoid collisions with other decorators
|
|
516
|
+
*/
|
|
517
|
+
declare const ROUTE_METADATA_KEYS: {
|
|
518
|
+
readonly CONTROLLER_ROUTE: symbol;
|
|
519
|
+
readonly CONTROLLER_OPTIONS: symbol;
|
|
520
|
+
readonly CONTROLLER_MIDDLEWARES: symbol;
|
|
521
|
+
readonly ROUTE_CONFIG: symbol;
|
|
522
|
+
readonly DECORATED_METHODS: symbol;
|
|
523
|
+
readonly AUTH_GUARD: symbol;
|
|
524
|
+
readonly GATEWAY_MARKER: symbol;
|
|
525
|
+
readonly WS_ON_MESSAGE: symbol;
|
|
526
|
+
readonly WS_ON_CLOSE: symbol;
|
|
527
|
+
readonly WS_ON_ERROR: symbol;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Security scheme identifiers for OpenAPI
|
|
531
|
+
* These reference the security scheme definitions in security.schemas.ts
|
|
532
|
+
*/
|
|
533
|
+
declare const SECURITY_SCHEMES: {
|
|
534
|
+
readonly BEARER_AUTH: "bearerAuth";
|
|
535
|
+
readonly API_KEY: "apiKey";
|
|
536
|
+
readonly SESSION_COOKIE: "sessionCookie";
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* HTTP method mapping for RESTful controller methods
|
|
540
|
+
* Maps controller method names to HTTP verbs and path patterns
|
|
541
|
+
*/
|
|
542
|
+
declare const HTTP_METHODS: {
|
|
543
|
+
readonly index: {
|
|
544
|
+
readonly method: "get";
|
|
545
|
+
readonly path: "";
|
|
546
|
+
};
|
|
547
|
+
readonly show: {
|
|
548
|
+
readonly method: "get";
|
|
549
|
+
readonly path: "/:id";
|
|
550
|
+
};
|
|
551
|
+
readonly create: {
|
|
552
|
+
readonly method: "post";
|
|
553
|
+
readonly path: "";
|
|
554
|
+
};
|
|
555
|
+
readonly update: {
|
|
556
|
+
readonly method: "put";
|
|
557
|
+
readonly path: "/:id";
|
|
558
|
+
};
|
|
559
|
+
readonly patch: {
|
|
560
|
+
readonly method: "patch";
|
|
561
|
+
readonly path: "/:id";
|
|
562
|
+
};
|
|
563
|
+
readonly destroy: {
|
|
564
|
+
readonly method: "delete";
|
|
565
|
+
readonly path: "/:id";
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
/**
|
|
569
|
+
* Sentinel symbol to opt a controller out of versioning.
|
|
570
|
+
* When used as the version, no prefix is applied even when defaultVersion is set.
|
|
571
|
+
*/
|
|
572
|
+
declare const VERSION_NEUTRAL: unique symbol;
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/router/types.d.ts
|
|
575
|
+
/**
|
|
576
|
+
* Route parameter type for OpenAPI
|
|
577
|
+
* ZodObject or ZodPipe (piped validation)
|
|
578
|
+
*/
|
|
579
|
+
type ZodObjectWithEffect = index_d_exports$1.ZodObject<any> | index_d_exports$1.ZodPipe<any, any>;
|
|
580
|
+
type RouteParameter = ZodObjectWithEffect | undefined;
|
|
581
|
+
/**
|
|
582
|
+
* Hono context variables with type-safe keys
|
|
583
|
+
*/
|
|
584
|
+
interface RouterVariables {
|
|
585
|
+
[ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER]: Container;
|
|
586
|
+
[ROUTER_CONTEXT_KEYS.LOCALE]?: string;
|
|
587
|
+
/**
|
|
588
|
+
* When set by middleware, the defaultHook returns this response after
|
|
589
|
+
* successful validation — skipping the controller handler entirely.
|
|
590
|
+
* Used by packages like `@stratal/inertia` for precognition support.
|
|
591
|
+
*/
|
|
592
|
+
validationSuccessResponse?: Response;
|
|
593
|
+
/** Domain parameters set by the domain matching middleware (e.g., `domain:tenant`) */
|
|
594
|
+
[key: `domain:${string}`]: string;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Hono environment type for router
|
|
598
|
+
*/
|
|
599
|
+
interface RouterEnv {
|
|
600
|
+
Bindings: StratalEnv;
|
|
601
|
+
Variables: RouterVariables;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Security scheme identifier type
|
|
605
|
+
* Matches the keys in SECURITY_SCHEMES constant
|
|
606
|
+
*/
|
|
607
|
+
type SecurityScheme = typeof SECURITY_SCHEMES[keyof typeof SECURITY_SCHEMES];
|
|
608
|
+
/**
|
|
609
|
+
* HTTP method type from OpenAPI spec
|
|
610
|
+
*/
|
|
611
|
+
type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options' | 'trace' | 'all';
|
|
612
|
+
/**
|
|
613
|
+
* Object form for request body with optional content type
|
|
614
|
+
*/
|
|
615
|
+
interface RouteBodyObject {
|
|
616
|
+
schema: index_d_exports$1.ZodType;
|
|
617
|
+
contentType?: string;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Request body definition for @Route() decorator
|
|
621
|
+
* Bare ZodType defaults to application/json
|
|
622
|
+
*/
|
|
623
|
+
type RouteBody = index_d_exports$1.ZodType | RouteBodyObject;
|
|
624
|
+
/**
|
|
625
|
+
* Object form for response with optional description and content type
|
|
626
|
+
*/
|
|
627
|
+
interface RouteResponseObject {
|
|
628
|
+
schema: index_d_exports$1.ZodType;
|
|
629
|
+
description?: string;
|
|
630
|
+
contentType?: string;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Single response definition for @Route() decorator
|
|
634
|
+
* Status code is auto-derived from method name (create->201, others->200)
|
|
635
|
+
*/
|
|
636
|
+
type RouteResponse = index_d_exports$1.ZodType | RouteResponseObject;
|
|
637
|
+
/**
|
|
638
|
+
* Route configuration for @Route() decorator
|
|
639
|
+
* Defines OpenAPI metadata for a controller method
|
|
640
|
+
*/
|
|
641
|
+
interface RouteConfig {
|
|
642
|
+
/**
|
|
643
|
+
* Request body schema (for POST, PUT, PATCH)
|
|
644
|
+
* @example z.object({}) or { schema: z.object({}), contentType: 'multipart/form-data' }
|
|
645
|
+
*/
|
|
646
|
+
body?: RouteBody;
|
|
647
|
+
/**
|
|
648
|
+
* URL parameters schema (e.g., { id: z.string().uuid() })
|
|
649
|
+
* Must be ZodObject or ZodPipe for OpenAPI compatibility
|
|
650
|
+
*/
|
|
651
|
+
params?: RouteParameter;
|
|
652
|
+
/**
|
|
653
|
+
* Query parameters schema (e.g., pagination, filters)
|
|
654
|
+
* Must be ZodObject or ZodPipe for OpenAPI compatibility
|
|
655
|
+
*/
|
|
656
|
+
query?: RouteParameter;
|
|
657
|
+
/**
|
|
658
|
+
* Response schema for success case
|
|
659
|
+
* Status code auto-derived: create()->201, others->200
|
|
660
|
+
* @example userSchema or { schema: userSchema, description: 'User details' }
|
|
661
|
+
*/
|
|
662
|
+
response?: RouteResponse;
|
|
663
|
+
/**
|
|
664
|
+
* OpenAPI tags for grouping endpoints
|
|
665
|
+
* Merged with controller-level tags
|
|
666
|
+
*/
|
|
667
|
+
tags?: string[];
|
|
668
|
+
/**
|
|
669
|
+
* Security schemes required for this route
|
|
670
|
+
* Merged with controller-level security
|
|
671
|
+
* Empty array = public route (no auth)
|
|
672
|
+
*/
|
|
673
|
+
security?: SecurityScheme[];
|
|
674
|
+
/**
|
|
675
|
+
* Human-readable description for OpenAPI docs
|
|
676
|
+
*/
|
|
677
|
+
description?: string;
|
|
678
|
+
/**
|
|
679
|
+
* Detailed summary for OpenAPI docs
|
|
680
|
+
*/
|
|
681
|
+
summary?: string;
|
|
682
|
+
/**
|
|
683
|
+
* Hide this route from OpenAPI documentation
|
|
684
|
+
* Route remains functional but won't appear in /api/docs or /api/openapi.json
|
|
685
|
+
* Useful for internal-only endpoints, debug routes, or work-in-progress features
|
|
686
|
+
*/
|
|
687
|
+
hideFromDocs?: boolean;
|
|
688
|
+
/**
|
|
689
|
+
* HTTP success status code for the response
|
|
690
|
+
* Used by HTTP method decorators (@Get, @Post, etc.) to set the success status
|
|
691
|
+
* For @Route() decorator, status code is auto-derived from method name
|
|
692
|
+
*/
|
|
693
|
+
statusCode?: number;
|
|
694
|
+
/**
|
|
695
|
+
* Explicit route name for URL generation.
|
|
696
|
+
* Used with the `route()` helper and type generation.
|
|
697
|
+
* For convention-based @Route(), names are auto-generated if not provided.
|
|
698
|
+
* For explicit @Get/@Post/etc., names must be provided manually.
|
|
699
|
+
*
|
|
700
|
+
* @example 'users.show', 'health.check'
|
|
701
|
+
*/
|
|
702
|
+
name?: string;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Metadata for convention-based routes (@Route decorator)
|
|
706
|
+
* HTTP method and path are auto-derived from the method name
|
|
707
|
+
*/
|
|
708
|
+
interface ConventionRouteMetadata {
|
|
709
|
+
type: 'convention';
|
|
710
|
+
config: RouteConfig;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Metadata for explicit routes (@Get, @Post, @Put, @Patch, @Delete, @All decorators)
|
|
714
|
+
* HTTP method and path are explicitly specified
|
|
715
|
+
*/
|
|
716
|
+
interface ExplicitRouteMetadata {
|
|
717
|
+
type: 'explicit';
|
|
718
|
+
method: HttpMethod;
|
|
719
|
+
path: string;
|
|
720
|
+
config: RouteConfig;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Discriminated union for all route metadata
|
|
724
|
+
* Stored under a single metadata key for unified registration
|
|
725
|
+
*/
|
|
726
|
+
type RouteMetadata = ConventionRouteMetadata | ExplicitRouteMetadata;
|
|
727
|
+
/**
|
|
728
|
+
* Controller options for @Controller() decorator
|
|
729
|
+
* Provides default configuration for all routes in the controller
|
|
730
|
+
*/
|
|
731
|
+
interface ControllerOptions {
|
|
732
|
+
/**
|
|
733
|
+
* Default tags applied to all routes in this controller
|
|
734
|
+
* Routes can append additional tags
|
|
735
|
+
*/
|
|
736
|
+
tags?: string[];
|
|
737
|
+
/**
|
|
738
|
+
* Default security schemes applied to all routes
|
|
739
|
+
* Routes can add more schemes or override with empty array
|
|
740
|
+
*/
|
|
741
|
+
security?: SecurityScheme[];
|
|
742
|
+
/**
|
|
743
|
+
* Hide all routes in this controller from OpenAPI documentation
|
|
744
|
+
* Routes remain functional but won't appear in /api/docs or /api/openapi.json
|
|
745
|
+
* Can be overridden at route level with hideFromDocs: false
|
|
746
|
+
* Useful for internal-only controllers, debug endpoints, or utilities
|
|
747
|
+
*/
|
|
748
|
+
hideFromDocs?: boolean;
|
|
749
|
+
/**
|
|
750
|
+
* API version(s) for this controller.
|
|
751
|
+
* When versioning is enabled, routes are prefixed with the version (e.g., /v1/users).
|
|
752
|
+
* Use VERSION_NEUTRAL to opt out of versioning (no prefix applied).
|
|
753
|
+
* Can be a single version string, array of versions, or VERSION_NEUTRAL symbol.
|
|
754
|
+
*/
|
|
755
|
+
version?: string | string[] | typeof VERSION_NEUTRAL;
|
|
756
|
+
/**
|
|
757
|
+
* Route name prefix for this controller.
|
|
758
|
+
* Prepended to all route names in this controller.
|
|
759
|
+
* Overrides the Router-level name prefix entirely.
|
|
760
|
+
*
|
|
761
|
+
* @example 'admin.' — routes become 'admin.users.index', 'admin.users.show'
|
|
762
|
+
*/
|
|
763
|
+
name?: string;
|
|
764
|
+
/**
|
|
765
|
+
* Domain pattern for this controller.
|
|
766
|
+
* Overrides the Router-level domain.
|
|
767
|
+
* Use `{param}` for dynamic subdomain segments.
|
|
768
|
+
*
|
|
769
|
+
* @example '{tenant}.myapp.com', 'admin.myapp.com'
|
|
770
|
+
*/
|
|
771
|
+
domain?: string;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Versioning configuration for the application.
|
|
775
|
+
* Enables URI-based API versioning when provided to Stratal config.
|
|
776
|
+
*/
|
|
777
|
+
interface VersioningOptions {
|
|
778
|
+
/**
|
|
779
|
+
* Prefix for version segments in the URL.
|
|
780
|
+
* @default 'v'
|
|
781
|
+
* @example 'v' produces /v1, /v2; 'api/v' produces /api/v1, /api/v2
|
|
782
|
+
*/
|
|
783
|
+
prefix?: string;
|
|
784
|
+
/**
|
|
785
|
+
* Default version applied to controllers without explicit version.
|
|
786
|
+
* Controllers with VERSION_NEUTRAL are not affected.
|
|
787
|
+
*/
|
|
788
|
+
defaultVersion?: string | string[];
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Locale path configuration for route registration.
|
|
792
|
+
* Controls how locale prefixes are applied to route paths.
|
|
793
|
+
*
|
|
794
|
+
* Built from {@link I18nModuleOptions} when path-based locale detection is enabled.
|
|
795
|
+
*/
|
|
796
|
+
interface LocalePathConfig {
|
|
797
|
+
/** All supported locale codes (e.g., `['en', 'fr']`) */
|
|
798
|
+
allLocales: string[];
|
|
799
|
+
/**
|
|
800
|
+
* Locales that require a `/{locale}` path prefix.
|
|
801
|
+
* Excludes `defaultLocale` when `prefixDefaultLocale` is not `true`.
|
|
802
|
+
*/
|
|
803
|
+
prefixedLocales: string[];
|
|
804
|
+
/**
|
|
805
|
+
* The default locale used as the unprefixed root, derived from
|
|
806
|
+
* {@link I18nModuleOptions.defaultLocale}.
|
|
807
|
+
* `null` when all locales are prefixed (`prefixDefaultLocale: true`).
|
|
808
|
+
*/
|
|
809
|
+
defaultLocale: string | null;
|
|
810
|
+
}
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region src/router/hono-app.d.ts
|
|
813
|
+
/**
|
|
814
|
+
* HonoApp — extends OpenAPIHono with Stratal-specific setup
|
|
815
|
+
*
|
|
816
|
+
* - Request scope middleware (child container per request)
|
|
817
|
+
* - Global middleware (CORS, logging, error handling)
|
|
818
|
+
* - defaultHook for validation errors
|
|
819
|
+
* - `use()` overload for Stratal middleware classes
|
|
820
|
+
* - `configure()` for OpenAPI, routes, and 404
|
|
821
|
+
*/
|
|
822
|
+
declare class HonoApp extends OpenAPIHono<RouterEnv> {
|
|
823
|
+
private configured;
|
|
824
|
+
private readonly _container;
|
|
825
|
+
private readonly _logger;
|
|
826
|
+
/**
|
|
827
|
+
* Reference to the original Hono `use` implementation.
|
|
828
|
+
* Captured in constructor after super() sets it as an instance property.
|
|
829
|
+
* Used by private methods to register middleware without going through the override.
|
|
830
|
+
*/
|
|
831
|
+
private nativeUse;
|
|
832
|
+
constructor(container: Container, logger: LoggerService);
|
|
833
|
+
/**
|
|
834
|
+
* Apply global middleware (logger + error handler).
|
|
835
|
+
* Called by Application after locale middleware is applied by LocalePathService.
|
|
836
|
+
*/
|
|
837
|
+
private applyGlobalMiddleware;
|
|
838
|
+
/**
|
|
839
|
+
* Configure OpenAPI endpoints, controller routes, and 404 handler.
|
|
840
|
+
* Called once by Application.initialize().
|
|
841
|
+
*/
|
|
842
|
+
configure(): Promise<void>;
|
|
843
|
+
private setupRequestScope;
|
|
844
|
+
private handleException;
|
|
845
|
+
}
|
|
846
|
+
//#endregion
|
|
847
|
+
//#region src/router/services/locale-path.service.d.ts
|
|
848
|
+
/**
|
|
849
|
+
* A resolved path with locale variant metadata.
|
|
850
|
+
*/
|
|
851
|
+
interface ResolvedPath {
|
|
852
|
+
/** The fully resolved path (may include /:locale prefix) */
|
|
853
|
+
path: string;
|
|
854
|
+
/** Whether this path is a locale-prefixed variant */
|
|
855
|
+
isLocaleVariant: boolean;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Resolves locale path variants for route paths.
|
|
859
|
+
*
|
|
860
|
+
* Computes `LocalePathConfig` from `I18nModuleOptions` and provides
|
|
861
|
+
* path expansion for locale-prefixed route variants.
|
|
862
|
+
*
|
|
863
|
+
* Also applies language detection and default locale redirect middleware
|
|
864
|
+
* to HonoApp when resolved from the container.
|
|
865
|
+
*
|
|
866
|
+
* Registered as a singleton in the container.
|
|
867
|
+
*/
|
|
868
|
+
declare class LocalePathService {
|
|
869
|
+
private readonly honoApp;
|
|
870
|
+
private readonly _config;
|
|
871
|
+
private readonly _pathDetectionEnabled;
|
|
872
|
+
private readonly _prefixDefaultLocale;
|
|
873
|
+
constructor(container: Container, honoApp: HonoApp);
|
|
874
|
+
/** Whether path-based locale detection is enabled */
|
|
875
|
+
get enabled(): boolean;
|
|
876
|
+
/** The computed locale path config, or null if path detection is disabled */
|
|
877
|
+
get localePathConfig(): LocalePathConfig | null;
|
|
878
|
+
/** The prefixDefaultLocale setting (false, true, or 'redirect') */
|
|
879
|
+
get prefixDefaultLocale(): false | true | 'redirect';
|
|
880
|
+
/**
|
|
881
|
+
* Expand a path into primary + locale-prefixed variants.
|
|
882
|
+
*
|
|
883
|
+
* @param path - The base path to expand
|
|
884
|
+
* @returns Array of resolved paths with locale metadata
|
|
885
|
+
*/
|
|
886
|
+
resolve(path: string): ResolvedPath[];
|
|
887
|
+
/**
|
|
888
|
+
* Build a Hono regex constraint from prefixed locales.
|
|
889
|
+
* e.g., `{en|de|fr}` — restricts `:locale` to only match known values.
|
|
890
|
+
*/
|
|
891
|
+
private buildLocaleConstraint;
|
|
892
|
+
/**
|
|
893
|
+
* Apply Hono's languageDetector middleware and bridge the detected language
|
|
894
|
+
* to Stratal's LOCALE context variable.
|
|
895
|
+
*/
|
|
896
|
+
private setupLanguageDetection;
|
|
897
|
+
/**
|
|
898
|
+
* Redirect requests that include the default locale prefix to the unprefixed path.
|
|
899
|
+
* For example, `/en/users` → 301 redirect to `/users`.
|
|
900
|
+
*
|
|
901
|
+
* Only active when `prefixDefaultLocale` is `'redirect'`.
|
|
902
|
+
*/
|
|
903
|
+
private setupDefaultLocaleRedirect;
|
|
904
|
+
}
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/execution-context.d.ts
|
|
907
|
+
interface StratalExecutionContext {
|
|
908
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
909
|
+
}
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/errors/exception-handler.types.d.ts
|
|
912
|
+
/**
|
|
913
|
+
* Log severity levels for exception reporting.
|
|
914
|
+
*/
|
|
915
|
+
type LogSeverity = 'error' | 'warn' | 'info' | 'debug';
|
|
916
|
+
/**
|
|
917
|
+
* Callback invoked when a specific exception type is reported.
|
|
918
|
+
*
|
|
919
|
+
* @typeParam T - The exception type this callback handles
|
|
920
|
+
* @param error - The matched exception instance
|
|
921
|
+
* @param context - The execution context where the error occurred
|
|
922
|
+
*/
|
|
923
|
+
type ReportableCallback<T extends ApplicationError> = (error: T, context: ExceptionContext) => void | Promise<void>;
|
|
924
|
+
/**
|
|
925
|
+
* Callback invoked to render a specific exception type into a Response.
|
|
926
|
+
*
|
|
927
|
+
* Return `undefined` to fall through to the default renderer.
|
|
928
|
+
*
|
|
929
|
+
* @typeParam T - The exception type this callback handles
|
|
930
|
+
* @param error - The matched exception instance
|
|
931
|
+
* @param context - The execution context where the error occurred
|
|
932
|
+
* @returns A Response, ErrorResponse, or undefined to fall through
|
|
933
|
+
*/
|
|
934
|
+
type RenderableCallback<T extends ApplicationError> = (error: T, context: ExceptionContext) => Response | ErrorResponse | Promise<Response> | undefined;
|
|
935
|
+
/**
|
|
936
|
+
* Callback invoked to post-process every error Response before it is returned.
|
|
937
|
+
*
|
|
938
|
+
* Use this to add headers, change the response body, swap content type, etc.
|
|
939
|
+
*
|
|
940
|
+
* @param response - The rendered Response
|
|
941
|
+
* @param error - The original exception
|
|
942
|
+
* @param context - The execution context where the error occurred
|
|
943
|
+
* @returns The (possibly modified) Response
|
|
944
|
+
*/
|
|
945
|
+
type RespondCallback = (response: Response, error: ApplicationError, context: ExceptionContext) => Response;
|
|
946
|
+
/**
|
|
947
|
+
* Callback that returns additional context data to include in all exception logs.
|
|
948
|
+
*
|
|
949
|
+
* @returns Key-value pairs merged into every log entry
|
|
950
|
+
*/
|
|
951
|
+
type ContextCallback = () => Record<string, unknown>;
|
|
952
|
+
/**
|
|
953
|
+
* Handle returned by `reportable()` to control whether default reporting runs.
|
|
954
|
+
*/
|
|
955
|
+
interface Reportable {
|
|
956
|
+
/**
|
|
957
|
+
* Prevent the default logger from reporting this exception
|
|
958
|
+
* after the custom reportable callback has run.
|
|
959
|
+
*/
|
|
960
|
+
stop(): void;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Constructor type for ApplicationError subclasses.
|
|
964
|
+
*/
|
|
965
|
+
type ApplicationErrorConstructor<T extends ApplicationError = ApplicationError> = new (...args: any[]) => T;
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region src/errors/exception-handler.d.ts
|
|
968
|
+
/**
|
|
969
|
+
* ExceptionHandler — Laravel-inspired exception handling for Stratal.
|
|
970
|
+
*
|
|
971
|
+
* Provides a composable, expressive API for controlling how exceptions are
|
|
972
|
+
* reported (logged / sent to external services) and rendered (turned into
|
|
973
|
+
* HTTP Responses or ErrorResponse objects).
|
|
974
|
+
*
|
|
975
|
+
* **Lifecycle:**
|
|
976
|
+
* 1. The framework resolves this from the DI container (once at init time).
|
|
977
|
+
* 2. `register()` is called to let the user configure reporting / rendering.
|
|
978
|
+
* 3. Module `onException()` hooks contribute additional configuration.
|
|
979
|
+
* 4. On every error, `handle()` runs the pipeline: normalize → report → render → respond.
|
|
980
|
+
*
|
|
981
|
+
* **Usage — extend and override `register()`:**
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* export class AppExceptionHandler extends ExceptionHandler {
|
|
986
|
+
* register(): void {
|
|
987
|
+
* this.reportable(PaymentError, (e, ctx) => {
|
|
988
|
+
* this.resolve(SentryService).captureException(e)
|
|
989
|
+
* }).stop()
|
|
990
|
+
*
|
|
991
|
+
* this.renderable(MaintenanceError, (e, ctx) => {
|
|
992
|
+
* if (ctx.type === 'http') return ctx.ctx.html('<h1>Maintenance</h1>', 503)
|
|
993
|
+
* })
|
|
994
|
+
*
|
|
995
|
+
* this.dontReport([RouteNotFoundError])
|
|
996
|
+
* this.level(RecordNotFoundError, 'warn')
|
|
997
|
+
* this.context(() => ({ region: 'us-east-1' }))
|
|
998
|
+
* this.respond((res, err) => {
|
|
999
|
+
* res.headers.set('X-Error-Code', String(err.code))
|
|
1000
|
+
* return res
|
|
1001
|
+
* })
|
|
1002
|
+
* }
|
|
1003
|
+
* }
|
|
1004
|
+
* ```
|
|
1005
|
+
*/
|
|
1006
|
+
declare abstract class ExceptionHandler {
|
|
1007
|
+
protected readonly logger: LoggerService;
|
|
1008
|
+
protected readonly env: StratalEnv;
|
|
1009
|
+
private readonly container;
|
|
1010
|
+
private readonly executionContext;
|
|
1011
|
+
private readonly reportables;
|
|
1012
|
+
private readonly renderables;
|
|
1013
|
+
private readonly dontReportSet;
|
|
1014
|
+
private readonly levelOverrides;
|
|
1015
|
+
private readonly contextCallbacks;
|
|
1016
|
+
private readonly respondCallbacks;
|
|
1017
|
+
private readonly environment;
|
|
1018
|
+
constructor(logger: LoggerService, env: StratalEnv, container: Container, executionContext: StratalExecutionContext);
|
|
1019
|
+
/**
|
|
1020
|
+
* Configure exception reporting and rendering.
|
|
1021
|
+
*
|
|
1022
|
+
* Override this method in your handler class to register custom
|
|
1023
|
+
* `reportable()`, `renderable()`, `dontReport()`, `level()`,
|
|
1024
|
+
* `context()`, and `respond()` callbacks.
|
|
1025
|
+
*/
|
|
1026
|
+
abstract register(): void;
|
|
1027
|
+
/**
|
|
1028
|
+
* Register a custom reporting callback for a specific exception type.
|
|
1029
|
+
*
|
|
1030
|
+
* The callback is invoked when an error matching `errorClass` (via `instanceof`)
|
|
1031
|
+
* is thrown. Chain `.stop()` to prevent the default logger from also reporting.
|
|
1032
|
+
*
|
|
1033
|
+
* @typeParam T - The exception type to match
|
|
1034
|
+
* @param errorClass - Constructor of the exception to match
|
|
1035
|
+
* @param callback - Reporting function receiving the typed error and context
|
|
1036
|
+
* @returns A {@link Reportable} with a `stop()` method
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```typescript
|
|
1040
|
+
* this.reportable(PaymentError, (e, ctx) => {
|
|
1041
|
+
* sentry.captureException(e)
|
|
1042
|
+
* }).stop() // skip default logging
|
|
1043
|
+
* ```
|
|
1044
|
+
*/
|
|
1045
|
+
reportable<T extends ApplicationError>(errorClass: ApplicationErrorConstructor<T>, callback: ReportableCallback<T>): Reportable;
|
|
1046
|
+
/**
|
|
1047
|
+
* Register a custom rendering callback for a specific exception type.
|
|
1048
|
+
*
|
|
1049
|
+
* The callback should return a `Response` (for HTTP contexts), an `ErrorResponse`,
|
|
1050
|
+
* or `undefined` to fall through to the default renderer.
|
|
1051
|
+
*
|
|
1052
|
+
* @typeParam T - The exception type to match
|
|
1053
|
+
* @param errorClass - Constructor of the exception to match
|
|
1054
|
+
* @param callback - Rendering function receiving the typed error and context
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```typescript
|
|
1058
|
+
* this.renderable(MaintenanceError, (e, ctx) => {
|
|
1059
|
+
* if (ctx.type === 'http') {
|
|
1060
|
+
* return ctx.ctx.html('<h1>Down for maintenance</h1>', 503)
|
|
1061
|
+
* }
|
|
1062
|
+
* })
|
|
1063
|
+
* ```
|
|
1064
|
+
*/
|
|
1065
|
+
renderable<T extends ApplicationError>(errorClass: ApplicationErrorConstructor<T>, callback: RenderableCallback<T>): void;
|
|
1066
|
+
/**
|
|
1067
|
+
* Suppress reporting (logging) for the given exception types.
|
|
1068
|
+
*
|
|
1069
|
+
* Errors matching these classes will still be rendered into responses
|
|
1070
|
+
* but will not be logged or sent to external reporters.
|
|
1071
|
+
*
|
|
1072
|
+
* @param errorClasses - Array of exception constructors to suppress
|
|
1073
|
+
*
|
|
1074
|
+
* @example
|
|
1075
|
+
* ```typescript
|
|
1076
|
+
* this.dontReport([RouteNotFoundError, SchemaValidationError])
|
|
1077
|
+
* ```
|
|
1078
|
+
*/
|
|
1079
|
+
dontReport(errorClasses: ApplicationErrorConstructor[]): void;
|
|
1080
|
+
/**
|
|
1081
|
+
* Override the log severity for a specific exception type.
|
|
1082
|
+
*
|
|
1083
|
+
* By default, severity is derived from the error code range.
|
|
1084
|
+
* Use this to promote or demote specific errors.
|
|
1085
|
+
*
|
|
1086
|
+
* @param errorClass - Constructor of the exception to override
|
|
1087
|
+
* @param severity - The log severity to use
|
|
1088
|
+
*
|
|
1089
|
+
* @example
|
|
1090
|
+
* ```typescript
|
|
1091
|
+
* this.level(RecordNotFoundError, 'warn')
|
|
1092
|
+
* ```
|
|
1093
|
+
*/
|
|
1094
|
+
level(errorClass: ApplicationErrorConstructor, severity: LogSeverity): void;
|
|
1095
|
+
/**
|
|
1096
|
+
* Add global context data to all exception log entries.
|
|
1097
|
+
*
|
|
1098
|
+
* The callback is invoked on every reported error and its return value
|
|
1099
|
+
* is merged into the log data.
|
|
1100
|
+
*
|
|
1101
|
+
* @param callback - Function returning key-value pairs to include in logs
|
|
1102
|
+
*
|
|
1103
|
+
* @example
|
|
1104
|
+
* ```typescript
|
|
1105
|
+
* this.context(() => ({
|
|
1106
|
+
* appVersion: '1.2.3',
|
|
1107
|
+
* region: env.CF_REGION,
|
|
1108
|
+
* }))
|
|
1109
|
+
* ```
|
|
1110
|
+
*/
|
|
1111
|
+
context(callback: ContextCallback): void;
|
|
1112
|
+
/**
|
|
1113
|
+
* Register a callback to post-process every error Response before it is returned.
|
|
1114
|
+
*
|
|
1115
|
+
* Use this to add headers, modify the body, change content type, or
|
|
1116
|
+
* transform the response in any way.
|
|
1117
|
+
*
|
|
1118
|
+
* @param callback - Function receiving (response, error, context) and returning a Response
|
|
1119
|
+
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```typescript
|
|
1122
|
+
* this.respond((response, error, ctx) => {
|
|
1123
|
+
* response.headers.set('X-Error-Code', String(error.code))
|
|
1124
|
+
* return response
|
|
1125
|
+
* })
|
|
1126
|
+
* ```
|
|
1127
|
+
*/
|
|
1128
|
+
respond(callback: RespondCallback): void;
|
|
1129
|
+
/**
|
|
1130
|
+
* Resolve a service from the DI container.
|
|
1131
|
+
*
|
|
1132
|
+
* Useful inside `register()` callbacks for accessing injected services
|
|
1133
|
+
* (e.g., Sentry, analytics, custom loggers).
|
|
1134
|
+
*
|
|
1135
|
+
* @typeParam T - The type of the service to resolve
|
|
1136
|
+
* @param token - DI token (symbol or constructor)
|
|
1137
|
+
* @returns The resolved service instance
|
|
1138
|
+
*
|
|
1139
|
+
* @example
|
|
1140
|
+
* ```typescript
|
|
1141
|
+
* this.reportable(CriticalError, (e) => {
|
|
1142
|
+
* this.resolve(SentryService).captureException(e)
|
|
1143
|
+
* })
|
|
1144
|
+
* ```
|
|
1145
|
+
*/
|
|
1146
|
+
resolve<T>(token: symbol | (new (...args: unknown[]) => T)): T;
|
|
1147
|
+
/**
|
|
1148
|
+
* Handle an error through the full exception pipeline.
|
|
1149
|
+
*
|
|
1150
|
+
* This is the single entry point used by all contexts (HTTP, queue, cron, CLI).
|
|
1151
|
+
* It normalizes the error, reports it (non-blocking via `waitUntil`),
|
|
1152
|
+
* renders it into a Response, and applies post-processing.
|
|
1153
|
+
*
|
|
1154
|
+
* @param error - The thrown error (may or may not be an ApplicationError)
|
|
1155
|
+
* @param context - The execution context where the error occurred
|
|
1156
|
+
* @returns A Response (JSON by default, customizable via renderable/respond)
|
|
1157
|
+
*/
|
|
1158
|
+
handle(error: unknown, context: ExceptionContext): Promise<Response>;
|
|
1159
|
+
/**
|
|
1160
|
+
* Normalize an unknown error into an ApplicationError.
|
|
1161
|
+
* Non-ApplicationError values are wrapped in InternalError.
|
|
1162
|
+
*/
|
|
1163
|
+
private normalizeError;
|
|
1164
|
+
/**
|
|
1165
|
+
* Run the reporting pipeline for an error.
|
|
1166
|
+
*/
|
|
1167
|
+
private performReport;
|
|
1168
|
+
/**
|
|
1169
|
+
* Run the rendering pipeline for an error, producing a Response.
|
|
1170
|
+
*/
|
|
1171
|
+
private performRender;
|
|
1172
|
+
/**
|
|
1173
|
+
* Apply all respond() callbacks to post-process a Response.
|
|
1174
|
+
*/
|
|
1175
|
+
private applyRespondCallbacks;
|
|
1176
|
+
/**
|
|
1177
|
+
* Check if an error is in the dontReport set.
|
|
1178
|
+
*/
|
|
1179
|
+
private shouldNotReport;
|
|
1180
|
+
/**
|
|
1181
|
+
* Find the most-specific reportable entry for an error.
|
|
1182
|
+
* Walks entries in registration order; picks the most-specific `instanceof` match.
|
|
1183
|
+
*/
|
|
1184
|
+
private findReportable;
|
|
1185
|
+
/**
|
|
1186
|
+
* Find the most-specific renderable entry for an error.
|
|
1187
|
+
*/
|
|
1188
|
+
private findRenderable;
|
|
1189
|
+
/**
|
|
1190
|
+
* Default reporting — log with appropriate severity and i18n translation.
|
|
1191
|
+
*/
|
|
1192
|
+
private defaultReport;
|
|
1193
|
+
/**
|
|
1194
|
+
* Default rendering — content-negotiated.
|
|
1195
|
+
*
|
|
1196
|
+
* For HTTP requests that accept HTML: renders a minimal branded HTML page.
|
|
1197
|
+
* For everything else (API, queue, cron, CLI): returns JSON.
|
|
1198
|
+
*
|
|
1199
|
+
* Errors are always logged via `performReport` (non-blocking waitUntil),
|
|
1200
|
+
* so they appear in the console regardless of the rendered response format.
|
|
1201
|
+
*/
|
|
1202
|
+
private defaultRender;
|
|
1203
|
+
/**
|
|
1204
|
+
* Check if the HTTP request prefers an HTML response.
|
|
1205
|
+
*
|
|
1206
|
+
* Uses the `Accept` header to determine format. Inertia v3 XHR requests
|
|
1207
|
+
* send `Accept: text/html, application/xhtml+xml`, so they naturally
|
|
1208
|
+
* receive HTML error pages (displayed in Inertia's error modal in dev).
|
|
1209
|
+
*
|
|
1210
|
+
* Override in a subclass to customize content negotiation logic.
|
|
1211
|
+
*/
|
|
1212
|
+
protected wantsHtml(context: HttpExceptionContext): boolean;
|
|
1213
|
+
/**
|
|
1214
|
+
* Minimal production HTML error page with inline styles.
|
|
1215
|
+
*/
|
|
1216
|
+
private renderDefaultHtml;
|
|
1217
|
+
private escapeHtml;
|
|
1218
|
+
/**
|
|
1219
|
+
* Convert a render result (Response or ErrorResponse) into a Response.
|
|
1220
|
+
*/
|
|
1221
|
+
private toResponse;
|
|
1222
|
+
/**
|
|
1223
|
+
* Translate an error's message key via i18n.
|
|
1224
|
+
* Uses the request container (from HTTP context) for correct locale,
|
|
1225
|
+
* falling back to the global container or raw message string.
|
|
1226
|
+
*/
|
|
1227
|
+
private translateError;
|
|
1228
|
+
/**
|
|
1229
|
+
* Resolve the log severity for an error.
|
|
1230
|
+
* Checks level overrides first, then falls back to code-range-based severity.
|
|
1231
|
+
*/
|
|
1232
|
+
private resolveSeverity;
|
|
1233
|
+
/**
|
|
1234
|
+
* Determine default log severity based on error code range.
|
|
1235
|
+
*/
|
|
1236
|
+
private getDefaultSeverity;
|
|
1237
|
+
/**
|
|
1238
|
+
* Gather all global context data from registered callbacks.
|
|
1239
|
+
*/
|
|
1240
|
+
private gatherContext;
|
|
1241
|
+
}
|
|
1242
|
+
//#endregion
|
|
1243
|
+
//#region src/module/types.d.ts
|
|
1244
|
+
/**
|
|
1245
|
+
* Provider that uses a class constructor
|
|
1246
|
+
*
|
|
1247
|
+
* @example Transient (default)
|
|
1248
|
+
* ```typescript
|
|
1249
|
+
* { provide: UserService, useClass: UserService }
|
|
1250
|
+
* ```
|
|
1251
|
+
*
|
|
1252
|
+
* @example Singleton
|
|
1253
|
+
* ```typescript
|
|
1254
|
+
* { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry, scope: Scope.Singleton }
|
|
1255
|
+
* ```
|
|
1256
|
+
*
|
|
1257
|
+
* @example Request-scoped
|
|
1258
|
+
* ```typescript
|
|
1259
|
+
* { provide: DI_TOKENS.ConnectionManager, useClass: ConnectionManager, scope: Scope.Request }
|
|
1260
|
+
* ```
|
|
1261
|
+
*/
|
|
1262
|
+
interface ClassProvider<T extends object = object> {
|
|
1263
|
+
provide: InjectionToken$2<T>;
|
|
1264
|
+
useClass: Constructor<T>;
|
|
1265
|
+
/** Lifecycle scope - defaults to Transient if not specified */
|
|
1266
|
+
scope?: Scope;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Provider that uses a pre-created value
|
|
1270
|
+
*
|
|
1271
|
+
* Note: Values are inherently singleton-like (same instance always returned).
|
|
1272
|
+
* No scope option needed.
|
|
1273
|
+
*/
|
|
1274
|
+
interface ValueProvider<T extends object = object> {
|
|
1275
|
+
provide: InjectionToken$2<T>;
|
|
1276
|
+
useValue: T;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Provider that uses a factory function with auto-injection support
|
|
1280
|
+
*
|
|
1281
|
+
* Note: Factory providers do not support scope/lifecycle in tsyringe.
|
|
1282
|
+
* Factories are always called fresh on each resolution (transient-like behavior).
|
|
1283
|
+
*
|
|
1284
|
+
* @example Factory with dependencies
|
|
1285
|
+
* ```typescript
|
|
1286
|
+
* {
|
|
1287
|
+
* provide: LOGGER_TOKENS.Transports,
|
|
1288
|
+
* useFactory: (console) => [console],
|
|
1289
|
+
* inject: [LOGGER_TOKENS.ConsoleTransport]
|
|
1290
|
+
* }
|
|
1291
|
+
* ```
|
|
1292
|
+
*/
|
|
1293
|
+
interface FactoryProvider<T extends object = object> {
|
|
1294
|
+
provide: InjectionToken$2<T>;
|
|
1295
|
+
useFactory: (...deps: any[]) => T | Promise<T>;
|
|
1296
|
+
inject?: InjectionToken$2<unknown>[];
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Provider that creates an alias to an existing token
|
|
1300
|
+
*
|
|
1301
|
+
* When the `provide` token is resolved, the container resolves `useExisting` instead.
|
|
1302
|
+
* Both tokens return the same instance (for singleton/request-scoped services).
|
|
1303
|
+
*
|
|
1304
|
+
* Use cases:
|
|
1305
|
+
* - Creating interface tokens that alias concrete implementations
|
|
1306
|
+
* - Multiple tokens resolving to the same service
|
|
1307
|
+
*
|
|
1308
|
+
* @example Basic alias
|
|
1309
|
+
* ```typescript
|
|
1310
|
+
* {
|
|
1311
|
+
* provide: 'IUserService',
|
|
1312
|
+
* useExisting: UserService
|
|
1313
|
+
* }
|
|
1314
|
+
* // Resolving 'IUserService' returns the UserService instance
|
|
1315
|
+
* ```
|
|
1316
|
+
*
|
|
1317
|
+
* @example Interface abstraction
|
|
1318
|
+
* ```typescript
|
|
1319
|
+
* providers: [
|
|
1320
|
+
* UserService,
|
|
1321
|
+
* { provide: I_USER_SERVICE, useExisting: UserService }
|
|
1322
|
+
* ]
|
|
1323
|
+
* // Both UserService and I_USER_SERVICE resolve to the same instance
|
|
1324
|
+
* ```
|
|
1325
|
+
*/
|
|
1326
|
+
interface ExistingProvider<T extends object = object> {
|
|
1327
|
+
provide: InjectionToken$2<T>;
|
|
1328
|
+
useExisting: InjectionToken$2<T>;
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Union type for all provider types
|
|
1332
|
+
*/
|
|
1333
|
+
type Provider<T extends object = object> = Constructor<T> | ClassProvider<T> | ValueProvider<T> | FactoryProvider<T> | ExistingProvider<T>;
|
|
1334
|
+
/**
|
|
1335
|
+
* Module class type (decorated with @Module)
|
|
1336
|
+
*
|
|
1337
|
+
* Static methods for dynamic module configuration:
|
|
1338
|
+
* - forRoot: Synchronous configuration (like NestJS forRoot)
|
|
1339
|
+
* - forRootAsync: Async configuration with factory (like NestJS forRootAsync)
|
|
1340
|
+
*/
|
|
1341
|
+
interface ModuleClass<T extends object = object> extends Constructor<T> {
|
|
1342
|
+
/**
|
|
1343
|
+
* Synchronous module configuration
|
|
1344
|
+
*
|
|
1345
|
+
* Use for global singleton modules with static configuration
|
|
1346
|
+
*
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```typescript
|
|
1349
|
+
* @Module({ providers: [] })
|
|
1350
|
+
* export class ConfigModule {
|
|
1351
|
+
* static forRoot(options: ConfigOptions): DynamicModule {
|
|
1352
|
+
* return {
|
|
1353
|
+
* providers: [
|
|
1354
|
+
* { provide: CONFIG_TOKEN, useValue: options }
|
|
1355
|
+
* ]
|
|
1356
|
+
* }
|
|
1357
|
+
* }
|
|
1358
|
+
* }
|
|
1359
|
+
*
|
|
1360
|
+
* // Usage in AppModule
|
|
1361
|
+
* @Module({ imports: [ConfigModule.forRoot({ apiKey: '...' })] })
|
|
1362
|
+
* ```
|
|
1363
|
+
*/
|
|
1364
|
+
forRoot?: (...args: unknown[]) => DynamicModule;
|
|
1365
|
+
/**
|
|
1366
|
+
* Async module configuration with dependency injection
|
|
1367
|
+
*
|
|
1368
|
+
* Use when configuration depends on other services
|
|
1369
|
+
*
|
|
1370
|
+
* @example
|
|
1371
|
+
* ```typescript
|
|
1372
|
+
* @Module({ providers: [] })
|
|
1373
|
+
* export class DatabaseModule {
|
|
1374
|
+
* static forRootAsync<T>(options: AsyncModuleOptions<T>): DynamicModule {
|
|
1375
|
+
* return {
|
|
1376
|
+
* providers: [
|
|
1377
|
+
* {
|
|
1378
|
+
* provide: DB_TOKEN,
|
|
1379
|
+
* useFactory: options.useFactory,
|
|
1380
|
+
* inject: options.inject
|
|
1381
|
+
* }
|
|
1382
|
+
* ]
|
|
1383
|
+
* }
|
|
1384
|
+
* }
|
|
1385
|
+
* }
|
|
1386
|
+
*
|
|
1387
|
+
* // Usage in AppModule
|
|
1388
|
+
* @Module({
|
|
1389
|
+
* imports: [
|
|
1390
|
+
* DatabaseModule.forRootAsync({
|
|
1391
|
+
* inject: [CONFIG_TOKEN],
|
|
1392
|
+
* useFactory: (config) => ({ url: config.databaseUrl })
|
|
1393
|
+
* })
|
|
1394
|
+
* ]
|
|
1395
|
+
* })
|
|
1396
|
+
* ```
|
|
1397
|
+
*/
|
|
1398
|
+
forRootAsync?: <TOptions>(options: AsyncModuleOptions<TOptions>) => DynamicModule;
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Module options for `@Module` decorator
|
|
1402
|
+
*
|
|
1403
|
+
* Note: Middlewares are configured via the RouteConfigurable interface's
|
|
1404
|
+
* configureRoutes() method, not via this options object. See router/router.ts.
|
|
1405
|
+
*/
|
|
1406
|
+
interface ModuleOptions {
|
|
1407
|
+
imports?: (ModuleClass | DynamicModule)[];
|
|
1408
|
+
providers?: Provider[];
|
|
1409
|
+
controllers?: Constructor[];
|
|
1410
|
+
consumers?: Constructor[];
|
|
1411
|
+
jobs?: Constructor[];
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Dynamic module returned by forRoot/forRootAsync
|
|
1415
|
+
*
|
|
1416
|
+
* Contains additional providers, controllers, consumers, and jobs
|
|
1417
|
+
* that are added to the module when configured dynamically.
|
|
1418
|
+
*/
|
|
1419
|
+
interface DynamicModule extends Omit<ModuleOptions, 'imports'> {
|
|
1420
|
+
/**
|
|
1421
|
+
* Reference to the module class that created this dynamic module
|
|
1422
|
+
*
|
|
1423
|
+
* Required for dynamic modules to support lifecycle methods (configure, onInitialize, onShutdown).
|
|
1424
|
+
* ModuleRegistry uses this to instantiate the actual module class instead of an anonymous wrapper.
|
|
1425
|
+
*
|
|
1426
|
+
* Note: This is NOT for provider scoping (tsyringe is always global).
|
|
1427
|
+
* It's purely for preserving the class reference for lifecycle method calls.
|
|
1428
|
+
*/
|
|
1429
|
+
module: Constructor;
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Async configuration options for forRootAsync
|
|
1433
|
+
*/
|
|
1434
|
+
interface AsyncModuleOptions<TOptions> {
|
|
1435
|
+
inject?: InjectionToken$2<unknown>[];
|
|
1436
|
+
useFactory: (...deps: any[]) => TOptions | Promise<TOptions>;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Context passed to lifecycle hooks
|
|
1440
|
+
*/
|
|
1441
|
+
interface ModuleContext {
|
|
1442
|
+
container: Container;
|
|
1443
|
+
logger: LoggerService;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Lifecycle hook: called after all providers are registered
|
|
1447
|
+
*/
|
|
1448
|
+
interface OnInitialize {
|
|
1449
|
+
onInitialize(context: ModuleContext): void | Promise<void>;
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Lifecycle hook: called during application shutdown
|
|
1453
|
+
*/
|
|
1454
|
+
interface OnShutdown {
|
|
1455
|
+
onShutdown(context: ModuleContext): void | Promise<void>;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Lifecycle hook: called after the {@link ExceptionHandler} is initialized.
|
|
1459
|
+
*
|
|
1460
|
+
* Implement this interface on a module class to contribute custom
|
|
1461
|
+
* `reportable()`, `renderable()`, `dontReport()`, etc. registrations
|
|
1462
|
+
* to the application's exception handler.
|
|
1463
|
+
*
|
|
1464
|
+
* @example
|
|
1465
|
+
* ```typescript
|
|
1466
|
+
* @Module({ providers: [PaymentService] })
|
|
1467
|
+
* export class PaymentModule implements OnException {
|
|
1468
|
+
* onException(handler: ExceptionHandler): void {
|
|
1469
|
+
* handler.reportable(PaymentError, (e) => { ... })
|
|
1470
|
+
* handler.renderable(PaymentDeclinedError, (e, ctx) => {
|
|
1471
|
+
* if (ctx.type === 'http') return ctx.ctx.json({ retryable: true }, 402)
|
|
1472
|
+
* })
|
|
1473
|
+
* }
|
|
1474
|
+
* }
|
|
1475
|
+
* ```
|
|
1476
|
+
*/
|
|
1477
|
+
interface OnException {
|
|
1478
|
+
onException(handler: ExceptionHandler): void;
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Tsyringe registry entry type (for internal use)
|
|
1482
|
+
*
|
|
1483
|
+
* Note: useFactory receives DependencyContainer from tsyringe,
|
|
1484
|
+
* but we resolve our Container via CONTAINER_TOKEN for consistency.
|
|
1485
|
+
*/
|
|
1486
|
+
interface RegistryEntry<T extends object = object> {
|
|
1487
|
+
token: InjectionToken$2<T>;
|
|
1488
|
+
useClass?: Constructor<T>;
|
|
1489
|
+
useValue?: T;
|
|
1490
|
+
useFactory?: (dependencyContainer: DependencyContainer) => T;
|
|
1491
|
+
useToken?: InjectionToken$2<T>;
|
|
1492
|
+
}
|
|
1493
|
+
//#endregion
|
|
1494
|
+
//#region src/quarry/types.d.ts
|
|
1495
|
+
/**
|
|
1496
|
+
* Flat input object for programmatic command invocation.
|
|
1497
|
+
*/
|
|
1498
|
+
type CommandInput = Record<string, unknown>;
|
|
1499
|
+
/**
|
|
1500
|
+
* Result of a command execution.
|
|
1501
|
+
*/
|
|
1502
|
+
interface CommandResult {
|
|
1503
|
+
exitCode: number;
|
|
1504
|
+
output: string[];
|
|
1505
|
+
errors: string[];
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* User-facing Quarry interface. Only exposes the `call()` method.
|
|
1509
|
+
*
|
|
1510
|
+
* Inject via `@inject(DI_TOKENS.Quarry)` and type as `Quarry`.
|
|
1511
|
+
*/
|
|
1512
|
+
interface Quarry {
|
|
1513
|
+
call(name: string, input?: CommandInput): Promise<CommandResult>;
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Internal mutable state stored on Command instances via Symbol key.
|
|
1517
|
+
* @internal
|
|
1518
|
+
*/
|
|
1519
|
+
interface CommandInternals {
|
|
1520
|
+
inputs: CommandInput;
|
|
1521
|
+
output: string[];
|
|
1522
|
+
errors: string[];
|
|
1523
|
+
exitCode: number;
|
|
1524
|
+
quarry: Quarry | null;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* A parsed argument from a Laravel-style signature string.
|
|
1528
|
+
*/
|
|
1529
|
+
interface ParsedArgument {
|
|
1530
|
+
name: string;
|
|
1531
|
+
required: boolean;
|
|
1532
|
+
default?: string;
|
|
1533
|
+
description?: string;
|
|
1534
|
+
isArray: boolean;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* A parsed option from a Laravel-style signature string.
|
|
1538
|
+
*/
|
|
1539
|
+
interface ParsedOption {
|
|
1540
|
+
name: string;
|
|
1541
|
+
alias?: string;
|
|
1542
|
+
isFlag: boolean;
|
|
1543
|
+
isArray: boolean;
|
|
1544
|
+
default?: string;
|
|
1545
|
+
description?: string;
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Fully parsed command signature.
|
|
1549
|
+
*/
|
|
1550
|
+
interface ParsedSignature {
|
|
1551
|
+
name: string;
|
|
1552
|
+
arguments: ParsedArgument[];
|
|
1553
|
+
options: ParsedOption[];
|
|
1554
|
+
}
|
|
1555
|
+
//#endregion
|
|
1556
|
+
//#region src/router/controller.d.ts
|
|
1557
|
+
/**
|
|
1558
|
+
* Controller interface for handling HTTP requests
|
|
1559
|
+
*
|
|
1560
|
+
* Controllers can implement RESTful methods or a custom handle() method.
|
|
1561
|
+
* The route for the controller is set via the `@Controller` decorator.
|
|
1562
|
+
*
|
|
1563
|
+
* RESTful methods auto-map to HTTP verbs:
|
|
1564
|
+
* - index() → GET /route
|
|
1565
|
+
* - show() → GET /route/:id
|
|
1566
|
+
* - create() → POST /route
|
|
1567
|
+
* - update() → PUT /route/:id
|
|
1568
|
+
* - patch() → PATCH /route/:id
|
|
1569
|
+
* - destroy() → DELETE /route/:id
|
|
1570
|
+
*
|
|
1571
|
+
* For non-RESTful routes (wildcards, custom patterns), implement handle()
|
|
1572
|
+
*/
|
|
1573
|
+
interface IController {
|
|
1574
|
+
/**
|
|
1575
|
+
* GET /route
|
|
1576
|
+
* List all resources
|
|
1577
|
+
*/
|
|
1578
|
+
index?(ctx: RouterContext): Promise<Response> | Response;
|
|
1579
|
+
/**
|
|
1580
|
+
* GET /route/:id
|
|
1581
|
+
* Show a specific resource
|
|
1582
|
+
*/
|
|
1583
|
+
show?(ctx: RouterContext): Promise<Response> | Response;
|
|
1584
|
+
/**
|
|
1585
|
+
* POST /route
|
|
1586
|
+
* Create a new resource
|
|
1587
|
+
*/
|
|
1588
|
+
create?(ctx: RouterContext): Promise<Response> | Response;
|
|
1589
|
+
/**
|
|
1590
|
+
* PUT /route/:id
|
|
1591
|
+
* Update a resource (full replacement)
|
|
1592
|
+
*/
|
|
1593
|
+
update?(ctx: RouterContext): Promise<Response> | Response;
|
|
1594
|
+
/**
|
|
1595
|
+
* PATCH /route/:id
|
|
1596
|
+
* Patch a resource (partial update)
|
|
1597
|
+
*/
|
|
1598
|
+
patch?(ctx: RouterContext): Promise<Response> | Response;
|
|
1599
|
+
/**
|
|
1600
|
+
* DELETE /route/:id
|
|
1601
|
+
* Delete a resource
|
|
1602
|
+
*/
|
|
1603
|
+
destroy?(ctx: RouterContext): Promise<Response> | Response;
|
|
1604
|
+
/**
|
|
1605
|
+
* Custom handler for non-RESTful routes
|
|
1606
|
+
* Use this for wildcards (e.g., /api/v1/auth/*) or custom patterns
|
|
1607
|
+
* Takes precedence over RESTful methods if defined
|
|
1608
|
+
*/
|
|
1609
|
+
handle?(ctx: RouterContext): Promise<Response> | Response;
|
|
1610
|
+
}
|
|
1611
|
+
//#endregion
|
|
1612
|
+
//#region src/router/middleware.interface.d.ts
|
|
1613
|
+
type Next$1 = Next;
|
|
1614
|
+
/**
|
|
1615
|
+
* Middleware interface for request processing
|
|
1616
|
+
*
|
|
1617
|
+
* Middlewares use the `@Transient()` decorator and are registered via
|
|
1618
|
+
* `configureRoutes(router)` in modules implementing `RouteConfigurable`.
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```typescript
|
|
1622
|
+
* @Transient()
|
|
1623
|
+
* export class LoggingMiddleware implements Middleware {
|
|
1624
|
+
* async handle(ctx: RouterContext, next: () => Promise<void>): Promise<void> {
|
|
1625
|
+
* const start = Date.now()
|
|
1626
|
+
* await next()
|
|
1627
|
+
* console.log(`Request took ${Date.now() - start}ms`)
|
|
1628
|
+
* }
|
|
1629
|
+
* }
|
|
1630
|
+
*
|
|
1631
|
+
* // Register in module:
|
|
1632
|
+
* @Module({ providers: [LoggingMiddleware] })
|
|
1633
|
+
* export class AppModule implements RouteConfigurable {
|
|
1634
|
+
* configureRoutes(router: Router): void {
|
|
1635
|
+
* router.middleware(LoggingMiddleware)
|
|
1636
|
+
* }
|
|
1637
|
+
* }
|
|
1638
|
+
* ```
|
|
1639
|
+
*/
|
|
1640
|
+
interface Middleware {
|
|
1641
|
+
/**
|
|
1642
|
+
* Handle middleware logic
|
|
1643
|
+
* Call next() to continue the middleware chain
|
|
1644
|
+
*
|
|
1645
|
+
* @param ctx - Router context with request/response helpers
|
|
1646
|
+
* @param next - Function to call the next middleware or route handler
|
|
1647
|
+
*/
|
|
1648
|
+
handle(ctx: RouterContext, next: Next$1): Promise<Response | void>;
|
|
1649
|
+
}
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region src/router/router.internals.d.ts
|
|
1652
|
+
/**
|
|
1653
|
+
* Symbol keys for Router internal accessors.
|
|
1654
|
+
*
|
|
1655
|
+
* These symbols are NOT exported from the public `stratal/router` barrel.
|
|
1656
|
+
* Only internal modules (RouterResolver) import them, keeping the Router's
|
|
1657
|
+
* public API clean — users never see these methods.
|
|
1658
|
+
*
|
|
1659
|
+
* Declared as individual unique symbols so TypeScript can distinguish
|
|
1660
|
+
* their return types in computed property access.
|
|
1661
|
+
*
|
|
1662
|
+
* @internal
|
|
1663
|
+
*/
|
|
1664
|
+
/** @internal */
|
|
1665
|
+
declare const getDefaultEntry: unique symbol;
|
|
1666
|
+
/** @internal */
|
|
1667
|
+
declare const getGroups: unique symbol;
|
|
1668
|
+
/** @internal */
|
|
1669
|
+
declare const getGlobalMiddleware: unique symbol;
|
|
1670
|
+
//#endregion
|
|
1671
|
+
//#region src/router/router.d.ts
|
|
1672
|
+
/**
|
|
1673
|
+
* Configuration for a sub-group created via `router.group()`.
|
|
1674
|
+
*/
|
|
1675
|
+
interface RouterGroupConfig {
|
|
1676
|
+
prefix?: string;
|
|
1677
|
+
domain?: string;
|
|
1678
|
+
name?: string;
|
|
1679
|
+
middleware?: Constructor<Middleware>[];
|
|
1680
|
+
version?: string | string[];
|
|
1681
|
+
hideFromDocs?: boolean;
|
|
1682
|
+
params?: index_d_exports$1.ZodObject<any>;
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Internal entry representing a sub-group or the default scope.
|
|
1686
|
+
* @internal — used by RouterResolver, not exported publicly.
|
|
1687
|
+
*/
|
|
1688
|
+
interface RouterEntry {
|
|
1689
|
+
prefix?: string;
|
|
1690
|
+
domain?: string;
|
|
1691
|
+
name?: string;
|
|
1692
|
+
middleware: Constructor<Middleware>[];
|
|
1693
|
+
version?: string | string[];
|
|
1694
|
+
hideFromDocs?: boolean;
|
|
1695
|
+
params?: index_d_exports$1.ZodObject<any>;
|
|
1696
|
+
/** Controllers in this entry. undefined = all controllers not in any sub-group */
|
|
1697
|
+
controllers?: Constructor[];
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Modules implement this to configure routes and middleware.
|
|
1701
|
+
* Replaces `MiddlewareConfigurable`.
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* ```typescript
|
|
1705
|
+
* @Module({ controllers: [UsersController, PostsController] })
|
|
1706
|
+
* export class ApiModule implements RouteConfigurable {
|
|
1707
|
+
* configureRoutes(router: Router): void {
|
|
1708
|
+
* router
|
|
1709
|
+
* .name('api.')
|
|
1710
|
+
* .middleware(CorsMiddleware)
|
|
1711
|
+
* .version('1')
|
|
1712
|
+
* }
|
|
1713
|
+
* }
|
|
1714
|
+
* ```
|
|
1715
|
+
*/
|
|
1716
|
+
interface RouteConfigurable {
|
|
1717
|
+
configureRoutes(router: Router): void;
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Fluent builder for route and middleware configuration.
|
|
1721
|
+
*
|
|
1722
|
+
* Scoped methods (`middleware()`, `prefix()`, `domain()`, `name()`, `version()`, `hideFromDocs()`)
|
|
1723
|
+
* apply only to this module's controllers or the sub-group's controllers.
|
|
1724
|
+
*
|
|
1725
|
+
* `use()` registers global middleware (all routes in the entire app).
|
|
1726
|
+
* Only callable on the root Router — throws inside `group()` callbacks.
|
|
1727
|
+
*
|
|
1728
|
+
* `group()` creates sub-groups for specific controllers with a callback.
|
|
1729
|
+
* Controllers in a sub-group are excluded from the parent scope.
|
|
1730
|
+
*/
|
|
1731
|
+
declare class Router {
|
|
1732
|
+
private readonly _isChild;
|
|
1733
|
+
private readonly _defaultEntry;
|
|
1734
|
+
private readonly _groups;
|
|
1735
|
+
private readonly _globalMiddleware;
|
|
1736
|
+
constructor(isChild?: boolean);
|
|
1737
|
+
/** Dynamic path prefix. For shared segments like `/:companyId` */
|
|
1738
|
+
prefix(path: string, params?: index_d_exports$1.ZodObject<any>): this;
|
|
1739
|
+
/** Domain pattern for controllers in this scope */
|
|
1740
|
+
domain(pattern: string): this;
|
|
1741
|
+
/** Name prefix for routes in this scope */
|
|
1742
|
+
name(prefix: string): this;
|
|
1743
|
+
/** Middleware applied to controllers in this scope */
|
|
1744
|
+
middleware(...middlewares: Constructor<Middleware>[]): this;
|
|
1745
|
+
/** API version for controllers in this scope */
|
|
1746
|
+
version(version: string | string[]): this;
|
|
1747
|
+
/** Hide/show routes in this scope from OpenAPI docs */
|
|
1748
|
+
hideFromDocs(hide?: boolean): this;
|
|
1749
|
+
/**
|
|
1750
|
+
* Global middleware — applied to ALL routes in the entire app.
|
|
1751
|
+
* Only callable on the root Router. Throws if called inside `group()`.
|
|
1752
|
+
*/
|
|
1753
|
+
use(...middlewares: Constructor<Middleware>[]): this;
|
|
1754
|
+
/**
|
|
1755
|
+
* Create a sub-group for specific controllers/gateways.
|
|
1756
|
+
* Controllers in a sub-group are excluded from the parent (default) scope.
|
|
1757
|
+
* The callback receives a new Router (without `use()`) for fluent configuration.
|
|
1758
|
+
*/
|
|
1759
|
+
group(controllers: Constructor[], callback: (router: Omit<Router, 'use'>) => void): this;
|
|
1760
|
+
[getDefaultEntry](): RouterEntry;
|
|
1761
|
+
[getGroups](): RouterEntry[];
|
|
1762
|
+
[getGlobalMiddleware](): Constructor<Middleware>[];
|
|
1763
|
+
}
|
|
1764
|
+
//#endregion
|
|
1765
|
+
//#region src/module/module-registry.d.ts
|
|
1766
|
+
/**
|
|
1767
|
+
* ModuleRegistry - manages module lifecycle
|
|
1768
|
+
*
|
|
1769
|
+
* @example
|
|
1770
|
+
* ```typescript
|
|
1771
|
+
* const registry = new ModuleRegistry(container, logger)
|
|
1772
|
+
* registry.register(AppModule) // Traverses imports recursively
|
|
1773
|
+
* await registry.initialize()
|
|
1774
|
+
* // ... application running ...
|
|
1775
|
+
* await registry.shutdown()
|
|
1776
|
+
* ```
|
|
1777
|
+
*/
|
|
1778
|
+
declare class ModuleRegistry {
|
|
1779
|
+
private readonly container;
|
|
1780
|
+
private readonly logger;
|
|
1781
|
+
private modules;
|
|
1782
|
+
private registeredClasses;
|
|
1783
|
+
private initialized;
|
|
1784
|
+
private allControllers;
|
|
1785
|
+
private allConsumers;
|
|
1786
|
+
private allJobs;
|
|
1787
|
+
private allListeners;
|
|
1788
|
+
private allCommands;
|
|
1789
|
+
private allSeeders;
|
|
1790
|
+
private allRouterConfigs;
|
|
1791
|
+
constructor(container: Container, logger: LoggerService);
|
|
1792
|
+
/**
|
|
1793
|
+
* Register a module (static or dynamic)
|
|
1794
|
+
*
|
|
1795
|
+
* @param moduleOrDynamic - Module class decorated with `@Module` or DynamicModule from configure()
|
|
1796
|
+
*/
|
|
1797
|
+
register(moduleOrDynamic: ModuleClass | DynamicModule): void;
|
|
1798
|
+
/**
|
|
1799
|
+
* Register multiple modules in order
|
|
1800
|
+
*/
|
|
1801
|
+
registerAll(modules: (ModuleClass | DynamicModule)[]): void;
|
|
1802
|
+
/**
|
|
1803
|
+
* Initialize all modules (call configure and onInitialize hooks)
|
|
1804
|
+
*/
|
|
1805
|
+
initialize(): Promise<void>;
|
|
1806
|
+
/**
|
|
1807
|
+
* Get all controllers registered from all modules
|
|
1808
|
+
*/
|
|
1809
|
+
getAllControllers(): Constructor[];
|
|
1810
|
+
/**
|
|
1811
|
+
* Get all consumers registered from all modules
|
|
1812
|
+
*/
|
|
1813
|
+
getAllConsumers(): Constructor[];
|
|
1814
|
+
/**
|
|
1815
|
+
* Get all jobs registered from all modules
|
|
1816
|
+
*/
|
|
1817
|
+
getAllJobs(): Constructor[];
|
|
1818
|
+
/**
|
|
1819
|
+
* Get all listeners registered from all modules
|
|
1820
|
+
*/
|
|
1821
|
+
getAllListeners(): Constructor[];
|
|
1822
|
+
/**
|
|
1823
|
+
* Get all commands registered from all modules
|
|
1824
|
+
*/
|
|
1825
|
+
getAllCommands(): Constructor[];
|
|
1826
|
+
/**
|
|
1827
|
+
* Get all seeders registered from all modules
|
|
1828
|
+
*/
|
|
1829
|
+
getAllSeeders(): Constructor[];
|
|
1830
|
+
/**
|
|
1831
|
+
* Get all Router configurations from modules implementing RouteConfigurable.
|
|
1832
|
+
* Runs configureRoutes() lazily on first call (deferred from initialize()).
|
|
1833
|
+
*/
|
|
1834
|
+
getAllRouterConfigs(): {
|
|
1835
|
+
router: Router;
|
|
1836
|
+
controllers: Constructor[];
|
|
1837
|
+
}[];
|
|
1838
|
+
/**
|
|
1839
|
+
* Call `onException()` on all modules that implement the OnException interface.
|
|
1840
|
+
* Invoked by Application after the ExceptionHandler is resolved and `register()` is called.
|
|
1841
|
+
*
|
|
1842
|
+
* @param handler - The resolved ExceptionHandler instance
|
|
1843
|
+
*/
|
|
1844
|
+
configureExceptionHandlers(handler: ExceptionHandler): void;
|
|
1845
|
+
/**
|
|
1846
|
+
* Shutdown all modules (call onShutdown hooks in reverse order)
|
|
1847
|
+
*/
|
|
1848
|
+
shutdown(): Promise<void>;
|
|
1849
|
+
/**
|
|
1850
|
+
* Type guard for RouteConfigurable
|
|
1851
|
+
*/
|
|
1852
|
+
private hasRouteConfigurable;
|
|
1853
|
+
/**
|
|
1854
|
+
* Type guard for OnInitialize
|
|
1855
|
+
*/
|
|
1856
|
+
private hasOnInitialize;
|
|
1857
|
+
/**
|
|
1858
|
+
* Type guard for OnShutdown
|
|
1859
|
+
*/
|
|
1860
|
+
private hasOnShutdown;
|
|
1861
|
+
/**
|
|
1862
|
+
* Type guard for OnException
|
|
1863
|
+
*/
|
|
1864
|
+
private hasOnException;
|
|
1865
|
+
/**
|
|
1866
|
+
* Resolve module class and options from static or dynamic module
|
|
1867
|
+
*
|
|
1868
|
+
* For DynamicModules, merges the decorator metadata (consumers, controllers, jobs)
|
|
1869
|
+
* with the DynamicModule options (providers, imports). This ensures modules using
|
|
1870
|
+
* forRoot/forRootAsync patterns still have their decorator-defined consumers registered.
|
|
1871
|
+
*/
|
|
1872
|
+
private resolveModule;
|
|
1873
|
+
/**
|
|
1874
|
+
* Type guard for DynamicModule
|
|
1875
|
+
*/
|
|
1876
|
+
private isDynamicModule;
|
|
1877
|
+
/**
|
|
1878
|
+
* Register a single provider in the container
|
|
1879
|
+
*/
|
|
1880
|
+
private registerProvider;
|
|
1881
|
+
/**
|
|
1882
|
+
* Check if a class is a `Command` and collect it for auto-wiring
|
|
1883
|
+
*/
|
|
1884
|
+
private collectIfCommand;
|
|
1885
|
+
/**
|
|
1886
|
+
* Check if a class is a `Seeder` and collect it for auto-wiring
|
|
1887
|
+
*/
|
|
1888
|
+
private collectIfSeeder;
|
|
1889
|
+
/**
|
|
1890
|
+
* Check if a class is a `@Listener()` and collect it for auto-wiring
|
|
1891
|
+
*/
|
|
1892
|
+
private collectIfListener;
|
|
1893
|
+
}
|
|
1894
|
+
//#endregion
|
|
1895
|
+
//#region src/router/router-resolver.d.ts
|
|
1896
|
+
/**
|
|
1897
|
+
* Resolved configuration for a single controller.
|
|
1898
|
+
* Merges Router default entry, sub-group overrides, and inheritance rules.
|
|
1899
|
+
*/
|
|
1900
|
+
interface ResolvedRouterConfig {
|
|
1901
|
+
prefix?: string;
|
|
1902
|
+
domain?: string;
|
|
1903
|
+
name?: string;
|
|
1904
|
+
middleware: Constructor<Middleware>[];
|
|
1905
|
+
version?: string | string[];
|
|
1906
|
+
hideFromDocs?: boolean;
|
|
1907
|
+
params?: index_d_exports$1.ZodObject<any>;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Internal resolver that computes the effective Router config for each controller.
|
|
1911
|
+
*
|
|
1912
|
+
* Inheritance rules:
|
|
1913
|
+
* - `middleware`: parent middleware runs first, then child (concatenated)
|
|
1914
|
+
* - `prefix`: concatenated (parent + child)
|
|
1915
|
+
* - `name`: concatenated (parent + child)
|
|
1916
|
+
* - `domain`: child overrides parent
|
|
1917
|
+
* - `version`: child overrides parent
|
|
1918
|
+
* - `hideFromDocs`: child overrides parent
|
|
1919
|
+
*
|
|
1920
|
+
* @internal — not exported from stratal/router
|
|
1921
|
+
*/
|
|
1922
|
+
declare class RouterResolver {
|
|
1923
|
+
private readonly routers;
|
|
1924
|
+
constructor(routers: {
|
|
1925
|
+
router: Router;
|
|
1926
|
+
controllers: Constructor[];
|
|
1927
|
+
}[]);
|
|
1928
|
+
/**
|
|
1929
|
+
* Resolve the effective config for a given controller class.
|
|
1930
|
+
* Searches through all module routers to find the one owning this controller.
|
|
1931
|
+
*/
|
|
1932
|
+
resolveForController(controller: Constructor): ResolvedRouterConfig;
|
|
1933
|
+
/**
|
|
1934
|
+
* Collect all global middleware registered via `router.use()` across all modules.
|
|
1935
|
+
*/
|
|
1936
|
+
getGlobalMiddleware(): Constructor<Middleware>[];
|
|
1937
|
+
/**
|
|
1938
|
+
* Merge parent default entry with child group entry following inheritance rules.
|
|
1939
|
+
*/
|
|
1940
|
+
private mergeEntries;
|
|
1941
|
+
private entryToConfig;
|
|
1942
|
+
private mergeParams;
|
|
1943
|
+
private concatPrefixes;
|
|
1944
|
+
private concatNames;
|
|
1945
|
+
}
|
|
1946
|
+
//#endregion
|
|
1947
|
+
//#region src/router/services/route-registration.service.d.ts
|
|
1948
|
+
/**
|
|
1949
|
+
* Route registration service
|
|
1950
|
+
* Manages controller and route registration with OpenAPI support
|
|
1951
|
+
*
|
|
1952
|
+
* Responsibilities:
|
|
1953
|
+
* - Register RESTful controllers with OpenAPI metadata
|
|
1954
|
+
* - Auto-derive HTTP methods/paths from controller method names
|
|
1955
|
+
* - Build OpenAPI route configurations with guard execution
|
|
1956
|
+
* - Validate all controllers have access decorators (strict mode)
|
|
1957
|
+
* - Create controller handlers with DI resolution
|
|
1958
|
+
*
|
|
1959
|
+
* Two-pass strategy:
|
|
1960
|
+
* 1. Collect: iterate controllers, register in RouteRegistry, store Hono actions
|
|
1961
|
+
* 2. Register: iterate registry.all() (sorted), execute stored actions in Hono
|
|
1962
|
+
*/
|
|
1963
|
+
declare class RouteRegistrationService {
|
|
1964
|
+
private logger;
|
|
1965
|
+
private registry;
|
|
1966
|
+
private routerResolver;
|
|
1967
|
+
private localePathService;
|
|
1968
|
+
private app;
|
|
1969
|
+
private moduleRegistry;
|
|
1970
|
+
private controllerClasses;
|
|
1971
|
+
private upgradeWebSocketFn;
|
|
1972
|
+
constructor(logger: LoggerService, registry: RouteRegistry, routerResolver: RouterResolver | null, localePathService: LocalePathService, app: HonoApp, moduleRegistry: ModuleRegistry);
|
|
1973
|
+
/**
|
|
1974
|
+
* Configure router with controllers and global middleware.
|
|
1975
|
+
* Resolves controllers from ModuleRegistry and global middleware from RouterResolver.
|
|
1976
|
+
*/
|
|
1977
|
+
configure(): Promise<void>;
|
|
1978
|
+
/**
|
|
1979
|
+
* Pass 1: Collect routes from a controller into RouteRegistry and store Hono actions.
|
|
1980
|
+
* Versioning and locale expansion are handled by RouteRegistry.register().
|
|
1981
|
+
*/
|
|
1982
|
+
private collectRoutes;
|
|
1983
|
+
/**
|
|
1984
|
+
* Register a single WebSocket gateway route
|
|
1985
|
+
*/
|
|
1986
|
+
private registerGatewayForPath;
|
|
1987
|
+
/**
|
|
1988
|
+
* Create a guard execution middleware
|
|
1989
|
+
*
|
|
1990
|
+
* This middleware executes all guards for a route before the handler.
|
|
1991
|
+
* Guards are executed in order; all must pass for the request to proceed.
|
|
1992
|
+
*
|
|
1993
|
+
* @param guards - Array of guards to execute
|
|
1994
|
+
* @returns Hono middleware function
|
|
1995
|
+
*/
|
|
1996
|
+
private createGuardMiddleware;
|
|
1997
|
+
/**
|
|
1998
|
+
* Wrap a controller handler with a `scopedMiddleware → guards → handler`
|
|
1999
|
+
* chain that runs *inside* the Hono route handler — after request
|
|
2000
|
+
* validators have populated `c.req.valid(...)`. This is the only place
|
|
2001
|
+
* we can run user middleware after `@hono/zod-openapi`'s validators in
|
|
2002
|
+
* the same pipeline.
|
|
2003
|
+
*
|
|
2004
|
+
* Returns a Hono handler with the same signature as the original so
|
|
2005
|
+
* `app.openapi(route, wrapped)` works transparently.
|
|
2006
|
+
*/
|
|
2007
|
+
private wrapHandlerWithChain;
|
|
2008
|
+
/**
|
|
2009
|
+
* Register wildcard route for non-RESTful controllers
|
|
2010
|
+
*/
|
|
2011
|
+
private registerWildcardRoute;
|
|
2012
|
+
/**
|
|
2013
|
+
* Resolve HTTP method, path, route config, and status code from route metadata.
|
|
2014
|
+
*/
|
|
2015
|
+
private resolveMethodAndPath;
|
|
2016
|
+
/**
|
|
2017
|
+
* Join a base path and a route path, normalizing slashes
|
|
2018
|
+
*/
|
|
2019
|
+
private joinPaths;
|
|
2020
|
+
/**
|
|
2021
|
+
* Auto-derive HTTP method and path from controller method name
|
|
2022
|
+
* Uses HTTP_METHODS constant for RESTful convention mapping
|
|
2023
|
+
*/
|
|
2024
|
+
private deriveHttpMethodAndPath;
|
|
2025
|
+
/**
|
|
2026
|
+
* Merge controller-level and route-level metadata
|
|
2027
|
+
* Tags are merged (appended), security is merged (union)
|
|
2028
|
+
* Guards automatically add sessionCookie security if present
|
|
2029
|
+
*/
|
|
2030
|
+
private mergeMetadata;
|
|
2031
|
+
/**
|
|
2032
|
+
* Build OpenAPI route configuration from metadata
|
|
2033
|
+
* Creates a route definition compatible with @hono/zod-openapi.
|
|
2034
|
+
*
|
|
2035
|
+
* Scoped middleware and guards are NOT attached to `route.middleware`
|
|
2036
|
+
* here — they're composed into a wrapped handler in `collectRoutes` so
|
|
2037
|
+
* they run after Hono's request validators. See `wrapHandlerWithChain`.
|
|
2038
|
+
*/
|
|
2039
|
+
private buildOpenAPIRoute;
|
|
2040
|
+
/**
|
|
2041
|
+
* Check if a body definition is a RouteBodyObject (has schema key) vs bare ZodType
|
|
2042
|
+
*/
|
|
2043
|
+
private isRouteBodyObject;
|
|
2044
|
+
/**
|
|
2045
|
+
* Resolve method parameter injections from the container
|
|
2046
|
+
*
|
|
2047
|
+
* @param prototype - Controller prototype
|
|
2048
|
+
* @param methodName - Method name to get injections for
|
|
2049
|
+
* @param container - Request-scoped container
|
|
2050
|
+
* @returns Array of resolved dependencies in parameter order
|
|
2051
|
+
*/
|
|
2052
|
+
private resolveMethodInjections;
|
|
2053
|
+
/**
|
|
2054
|
+
* Name a handler function so Hono's inspectRoutes() can identify it.
|
|
2055
|
+
* Format: `{type}:{Controller}.{method}` (e.g. `http:UsersController.create`)
|
|
2056
|
+
*/
|
|
2057
|
+
private nameHandler;
|
|
2058
|
+
/**
|
|
2059
|
+
* Create controller handler that resolves controller from request-scoped container
|
|
2060
|
+
* This ensures each request gets a fresh controller with request-scoped context
|
|
2061
|
+
*/
|
|
2062
|
+
private createControllerHandler;
|
|
2063
|
+
/**
|
|
2064
|
+
* Extract the Zod schema from a RouteResponse definition.
|
|
2065
|
+
* Returns null for non-JSON content types or when no response is defined.
|
|
2066
|
+
*/
|
|
2067
|
+
private extractResponseSchema;
|
|
2068
|
+
/**
|
|
2069
|
+
* Check if a response definition is a RouteResponseObject (has schema key) vs bare ZodType
|
|
2070
|
+
*/
|
|
2071
|
+
private isRouteResponseObject;
|
|
2072
|
+
/**
|
|
2073
|
+
* Validate a Response body against its declared Zod schema.
|
|
2074
|
+
*
|
|
2075
|
+
* Skips validation for:
|
|
2076
|
+
* - Non-JSON content types
|
|
2077
|
+
* - Empty bodies (204 No Content, 304 Not Modified)
|
|
2078
|
+
*
|
|
2079
|
+
* Clones the response to read the body without consuming the original stream.
|
|
2080
|
+
*/
|
|
2081
|
+
private validateResponse;
|
|
2082
|
+
}
|
|
2083
|
+
//#endregion
|
|
2084
|
+
//#region src/router/router.tokens.d.ts
|
|
2085
|
+
/**
|
|
2086
|
+
* Dependency injection tokens for the router system
|
|
2087
|
+
*/
|
|
2088
|
+
declare const ROUTER_TOKENS: {
|
|
2089
|
+
/**
|
|
2090
|
+
* Token for RouterContext (request-scoped)
|
|
2091
|
+
* Contains Hono context wrapper with helper methods
|
|
2092
|
+
*/
|
|
2093
|
+
readonly RouterContext: symbol;
|
|
2094
|
+
/**
|
|
2095
|
+
* Token for RouteRegistry (singleton)
|
|
2096
|
+
* Central registry of all application routes — source of truth for route:list, route:types, URL generation
|
|
2097
|
+
*/
|
|
2098
|
+
readonly RouteRegistry: symbol;
|
|
2099
|
+
/**
|
|
2100
|
+
* Token for VersioningService (singleton)
|
|
2101
|
+
* Resolves version prefixes for route paths
|
|
2102
|
+
*/
|
|
2103
|
+
readonly VersioningService: symbol;
|
|
2104
|
+
/**
|
|
2105
|
+
* Token for LocalePathService (singleton)
|
|
2106
|
+
* Resolves locale path variants and computes LocalePathConfig
|
|
2107
|
+
*/
|
|
2108
|
+
readonly LocalePathService: symbol;
|
|
2109
|
+
/**
|
|
2110
|
+
* Token for RouterResolver (singleton, may be null)
|
|
2111
|
+
* Internal resolver that computes effective Router config per controller
|
|
2112
|
+
*/
|
|
2113
|
+
readonly RouterResolver: symbol;
|
|
2114
|
+
/**
|
|
2115
|
+
* Token for HonoApp (singleton)
|
|
2116
|
+
* The Hono application instance with Stratal-specific setup
|
|
2117
|
+
*/
|
|
2118
|
+
readonly HonoApp: symbol;
|
|
2119
|
+
/**
|
|
2120
|
+
* Token for Uri (request-scoped)
|
|
2121
|
+
* URL generation service — route URLs, signed URLs, current/previous URL access
|
|
2122
|
+
*/
|
|
2123
|
+
readonly Uri: symbol;
|
|
2124
|
+
};
|
|
2125
|
+
//#endregion
|
|
2126
|
+
//#region src/router/route-url.d.ts
|
|
2127
|
+
/**
|
|
2128
|
+
* Generate a URL from a named route.
|
|
2129
|
+
*
|
|
2130
|
+
* Keys in `params` matching `:param` placeholders fill the path.
|
|
2131
|
+
* Domain params (`{tenant}`) are also consumed from `params`.
|
|
2132
|
+
* Extra keys become query string parameters.
|
|
2133
|
+
*
|
|
2134
|
+
* Resolves RouteRegistry from the application container via AsyncLocalStorage.
|
|
2135
|
+
* Available after `Application.initialize()` has been called.
|
|
2136
|
+
*
|
|
2137
|
+
* @param name - Named route identifier
|
|
2138
|
+
* @param params - Route params + domain params + extra query params
|
|
2139
|
+
* @returns Generated URL string
|
|
2140
|
+
*
|
|
2141
|
+
* @example
|
|
2142
|
+
* ```typescript
|
|
2143
|
+
* // In a controller (preferred):
|
|
2144
|
+
* ctx.route('users.show', { id: '1' })
|
|
2145
|
+
*
|
|
2146
|
+
* // Outside controllers (standalone function):
|
|
2147
|
+
* import { route } from 'stratal/router'
|
|
2148
|
+
*
|
|
2149
|
+
* route('users.show', { id: '1' })
|
|
2150
|
+
* ```
|
|
2151
|
+
*/
|
|
2152
|
+
declare function route<N extends RouteName>(name: N, params?: RouteParams<N>): string;
|
|
2153
|
+
//#endregion
|
|
2154
|
+
//#region src/router/utils/path.d.ts
|
|
2155
|
+
/**
|
|
2156
|
+
* Path normalization and route ordering utilities.
|
|
2157
|
+
*
|
|
2158
|
+
* Users always write Hono-style `:param` paths (`:companyId`, `:id`).
|
|
2159
|
+
* OpenAPI requires `{param}` style — conversion happens only at registration time.
|
|
2160
|
+
*/
|
|
2161
|
+
/**
|
|
2162
|
+
* Convert Hono-style `:param` path segments to OpenAPI-style `{param}`.
|
|
2163
|
+
* Strips regex constraints (e.g., `:locale{sw}` → `{locale}`).
|
|
2164
|
+
*
|
|
2165
|
+
* @example
|
|
2166
|
+
* toOpenAPIPath('/users/:id') // '/users/{id}'
|
|
2167
|
+
* toOpenAPIPath('/:companyId/users/:userId') // '/{companyId}/users/{userId}'
|
|
2168
|
+
* toOpenAPIPath('/users/:id/posts') // '/users/{id}/posts'
|
|
2169
|
+
* toOpenAPIPath('/:locale{en|fr}/users') // '/{locale}/users'
|
|
2170
|
+
*/
|
|
2171
|
+
declare function toOpenAPIPath(path: string): string;
|
|
2172
|
+
/**
|
|
2173
|
+
* Compute a specificity score for route ordering.
|
|
2174
|
+
* Lower score = higher priority (registered first in Hono).
|
|
2175
|
+
*
|
|
2176
|
+
* Scoring: static = 0, `:param{constraint}` = 5, `:param` = 10, wildcard `{.+}` / `{.*}` = 100.
|
|
2177
|
+
*/
|
|
2178
|
+
declare function getPathSpecificityScore(path: string): number;
|
|
2179
|
+
/**
|
|
2180
|
+
* Sort routes by specificity so Hono registers them in the correct order.
|
|
2181
|
+
*
|
|
2182
|
+
* 1. Static paths before parameterized before wildcards
|
|
2183
|
+
* 2. More segments = more specific (tie-breaker)
|
|
2184
|
+
* 3. Primary paths before locale-prefixed variants
|
|
2185
|
+
*/
|
|
2186
|
+
declare function sortRoutesBySpecificity<T extends {
|
|
2187
|
+
path: string;
|
|
2188
|
+
}>(routes: T[]): T[];
|
|
2189
|
+
//#endregion
|
|
2190
|
+
//#region src/router/utils/route-name.d.ts
|
|
2191
|
+
/**
|
|
2192
|
+
* Route naming utilities.
|
|
2193
|
+
*
|
|
2194
|
+
* Extracts parameter names from paths and domains,
|
|
2195
|
+
* and generates convention-based route names.
|
|
2196
|
+
*/
|
|
2197
|
+
/**
|
|
2198
|
+
* Extract parameter names from a Hono-style path.
|
|
2199
|
+
*
|
|
2200
|
+
* @example
|
|
2201
|
+
* extractParamNames('/users/:id') // ['id']
|
|
2202
|
+
* extractParamNames('/:companyId/users/:userId') // ['companyId', 'userId']
|
|
2203
|
+
* extractParamNames('/users') // []
|
|
2204
|
+
*/
|
|
2205
|
+
declare function extractParamNames(path: string): string[];
|
|
2206
|
+
/**
|
|
2207
|
+
* Extract parameter names from a domain pattern.
|
|
2208
|
+
*
|
|
2209
|
+
* @example
|
|
2210
|
+
* extractDomainParamNames('{tenant}.example.com') // ['tenant']
|
|
2211
|
+
* extractDomainParamNames('{region}.{tenant}.example.com') // ['region', 'tenant']
|
|
2212
|
+
* extractDomainParamNames('example.com') // []
|
|
2213
|
+
*/
|
|
2214
|
+
declare function extractDomainParamNames(domain: string): string[];
|
|
2215
|
+
/**
|
|
2216
|
+
* Auto-generate a route name for convention-based `@Route` methods.
|
|
2217
|
+
*
|
|
2218
|
+
* Strips common prefixes (`/api/`, `/v{N}/`) and parameter segments,
|
|
2219
|
+
* then joins remaining static segments with dots and appends the method name.
|
|
2220
|
+
*
|
|
2221
|
+
* @example
|
|
2222
|
+
* generateConventionRouteName('/users', 'index') // 'users.index'
|
|
2223
|
+
* generateConventionRouteName('/users', 'show') // 'users.show'
|
|
2224
|
+
* generateConventionRouteName('/api/v1/users', 'create') // 'users.create'
|
|
2225
|
+
* generateConventionRouteName('/api/v1/users/:userId/notes', 'index') // 'users.notes.index'
|
|
2226
|
+
* generateConventionRouteName('/:companyId/users', 'index') // 'users.index'
|
|
2227
|
+
* generateConventionRouteName('/users/:userId/notes/:noteId/tags', 'index') // 'users.notes.tags.index'
|
|
2228
|
+
*/
|
|
2229
|
+
declare function generateConventionRouteName(basePath: string, methodName: string): string;
|
|
2230
|
+
//#endregion
|
|
2231
|
+
//#region src/router/middleware/middleware-chain.d.ts
|
|
2232
|
+
/**
|
|
2233
|
+
* Create a Hono middleware handler that executes a chain of Stratal middleware classes.
|
|
2234
|
+
*
|
|
2235
|
+
* Each middleware is resolved from the request-scoped container per request,
|
|
2236
|
+
* then executed in order (first registered = outermost in the chain).
|
|
2237
|
+
*
|
|
2238
|
+
* @param classes - Middleware classes to chain
|
|
2239
|
+
* @returns Hono middleware handler
|
|
2240
|
+
*/
|
|
2241
|
+
declare function createMiddlewareChain(classes: Constructor<Middleware>[]): MiddlewareHandler<RouterEnv>;
|
|
2242
|
+
//#endregion
|
|
2243
|
+
//#region src/router/decorators/controller.decorator.d.ts
|
|
2244
|
+
/**
|
|
2245
|
+
* Base controller decorator for route registration
|
|
2246
|
+
*
|
|
2247
|
+
* This is the core controller decorator that handles:
|
|
2248
|
+
* - Transient scope registration (request-scoped)
|
|
2249
|
+
* - Route metadata storage
|
|
2250
|
+
* - Controller options (tags, security schemes, hideFromDocs)
|
|
2251
|
+
*
|
|
2252
|
+
* @param route - Base route for this controller (e.g., '/api/v1/users')
|
|
2253
|
+
* @param options - Optional configuration (tags, security schemes, hideFromDocs)
|
|
2254
|
+
*
|
|
2255
|
+
* @example
|
|
2256
|
+
* ```typescript
|
|
2257
|
+
* import { Controller } from 'stratal/router'
|
|
2258
|
+
*
|
|
2259
|
+
* @Controller('/api/v1/users', { tags: ['Users'] })
|
|
2260
|
+
* export class UsersController implements IController {
|
|
2261
|
+
* // All routes accessible
|
|
2262
|
+
* }
|
|
2263
|
+
* ```
|
|
2264
|
+
*/
|
|
2265
|
+
declare function Controller(route: string, options?: ControllerOptions): <T extends Constructor>(target: T) => T;
|
|
2266
|
+
/**
|
|
2267
|
+
* Get the route from controller class metadata
|
|
2268
|
+
*
|
|
2269
|
+
* @param target - Controller class or instance
|
|
2270
|
+
* @returns Route string or undefined if not set
|
|
2271
|
+
*/
|
|
2272
|
+
declare function getControllerRoute(target: object): string | undefined;
|
|
2273
|
+
/**
|
|
2274
|
+
* Get the options from controller class metadata
|
|
2275
|
+
*
|
|
2276
|
+
* @param target - Controller class or instance
|
|
2277
|
+
* @returns Controller options or undefined if not set
|
|
2278
|
+
*/
|
|
2279
|
+
declare function getControllerOptions(target: object): ControllerOptions | undefined;
|
|
2280
|
+
/**
|
|
2281
|
+
* Get the version from controller class metadata
|
|
2282
|
+
*
|
|
2283
|
+
* @param target - Controller class or instance
|
|
2284
|
+
* @returns Version string, array, VERSION_NEUTRAL symbol, or undefined if not set
|
|
2285
|
+
*/
|
|
2286
|
+
declare function getControllerVersion(target: object): ControllerOptions['version'];
|
|
2287
|
+
//#endregion
|
|
2288
|
+
//#region src/router/decorators/http-method.decorator.d.ts
|
|
2289
|
+
/**
|
|
2290
|
+
* Registers a GET route on the controller method.
|
|
2291
|
+
*
|
|
2292
|
+
* @param path - Route path relative to the controller base path
|
|
2293
|
+
* @param config - Optional route configuration (response schema, body, params, etc.)
|
|
2294
|
+
*
|
|
2295
|
+
* @example
|
|
2296
|
+
* ```typescript
|
|
2297
|
+
* @Controller('/api/v1/users')
|
|
2298
|
+
* class UsersController {
|
|
2299
|
+
* @Get('/', { response: z.array(userSchema), summary: 'List users' })
|
|
2300
|
+
* async list(ctx: RouterContext) { ... }
|
|
2301
|
+
*
|
|
2302
|
+
* @Get('/:id', { params: z.object({ id: z.string().uuid() }), response: userSchema })
|
|
2303
|
+
* async getUser(ctx: RouterContext) { ... }
|
|
2304
|
+
* }
|
|
2305
|
+
* ```
|
|
2306
|
+
*/
|
|
2307
|
+
declare const Get: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2308
|
+
/**
|
|
2309
|
+
* Registers a POST route on the controller method.
|
|
2310
|
+
*
|
|
2311
|
+
* @param path - Route path relative to the controller base path
|
|
2312
|
+
* @param config - Optional route configuration (response schema, body, params, etc.)
|
|
2313
|
+
*
|
|
2314
|
+
* @example
|
|
2315
|
+
* ```typescript
|
|
2316
|
+
* @Controller('/api/v1/users')
|
|
2317
|
+
* class UsersController {
|
|
2318
|
+
* @Post('/', { body: createUserSchema, response: userSchema, statusCode: 201 })
|
|
2319
|
+
* async createUser(ctx: RouterContext) { ... }
|
|
2320
|
+
* }
|
|
2321
|
+
* ```
|
|
2322
|
+
*/
|
|
2323
|
+
declare const Post: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2324
|
+
/**
|
|
2325
|
+
* Registers a PUT route on the controller method.
|
|
2326
|
+
*
|
|
2327
|
+
* @param path - Route path relative to the controller base path
|
|
2328
|
+
* @param config - Optional route configuration
|
|
2329
|
+
*/
|
|
2330
|
+
declare const Put: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2331
|
+
/**
|
|
2332
|
+
* Registers a PATCH route on the controller method.
|
|
2333
|
+
*
|
|
2334
|
+
* @param path - Route path relative to the controller base path
|
|
2335
|
+
* @param config - Optional route configuration
|
|
2336
|
+
*/
|
|
2337
|
+
declare const Patch: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2338
|
+
/**
|
|
2339
|
+
* Registers a DELETE route on the controller method.
|
|
2340
|
+
*
|
|
2341
|
+
* @param path - Route path relative to the controller base path
|
|
2342
|
+
* @param config - Optional route configuration
|
|
2343
|
+
*/
|
|
2344
|
+
declare const Delete: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2345
|
+
/**
|
|
2346
|
+
* Registers an ALL (any HTTP method) route on the controller method.
|
|
2347
|
+
* Routes using @All are registered without OpenAPI validation
|
|
2348
|
+
* since OpenAPI does not support a catch-all HTTP method.
|
|
2349
|
+
*
|
|
2350
|
+
* @param path - Route path relative to the controller base path
|
|
2351
|
+
* @param config - Optional route configuration
|
|
2352
|
+
*/
|
|
2353
|
+
declare const All: (path: string, config?: RouteConfig) => (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2354
|
+
//#endregion
|
|
2355
|
+
//#region src/router/decorators/route.decorator.d.ts
|
|
2356
|
+
/**
|
|
2357
|
+
* Decorator to add OpenAPI metadata to a controller method using convention-based routing.
|
|
2358
|
+
*
|
|
2359
|
+
* **Cannot be mixed with HTTP method decorators** (`@Get`, `@Post`, `@Put`, `@Patch`,
|
|
2360
|
+
* `@Delete`, `@All`) in the same controller. Use one pattern or the other.
|
|
2361
|
+
*
|
|
2362
|
+
* Stores route configuration (schemas, response, tags, security) in metadata.
|
|
2363
|
+
* HTTP method, path, and success status code are auto-derived from the method name:
|
|
2364
|
+
* - index() → GET /base-path → 200
|
|
2365
|
+
* - show() → GET /base-path/:id → 200
|
|
2366
|
+
* - create() → POST /base-path → 201
|
|
2367
|
+
* - update() → PUT /base-path/:id → 200
|
|
2368
|
+
* - patch() → PATCH /base-path/:id → 200
|
|
2369
|
+
* - destroy() → DELETE /base-path/:id → 200
|
|
2370
|
+
*
|
|
2371
|
+
* @param config - Route configuration (schemas, response, tags, security)
|
|
2372
|
+
*
|
|
2373
|
+
* @example
|
|
2374
|
+
* ```typescript
|
|
2375
|
+
* @Controller('/api/v1/notes', {
|
|
2376
|
+
* tags: ['Notes'],
|
|
2377
|
+
* security: ['bearerAuth']
|
|
2378
|
+
* })
|
|
2379
|
+
* export class NotesController implements Controller {
|
|
2380
|
+
* @Route({
|
|
2381
|
+
* body: createNoteSchema,
|
|
2382
|
+
* response: noteSchema, // 201 auto-derived from 'create' method
|
|
2383
|
+
* tags: ['Mutations'],
|
|
2384
|
+
* description: 'Create a new note'
|
|
2385
|
+
* })
|
|
2386
|
+
* async create(ctx: RouterContext): Promise<Response> {
|
|
2387
|
+
* // POST /api/v1/notes (auto-derived from method name)
|
|
2388
|
+
* // Body schema: createNoteSchema (auto-validated)
|
|
2389
|
+
* // Response: 201 → noteSchema (status auto-derived)
|
|
2390
|
+
* // Tags: ['Notes', 'Mutations'] (merged with controller)
|
|
2391
|
+
* // Security: ['bearerAuth'] (inherited from controller)
|
|
2392
|
+
* const body = ctx.body()
|
|
2393
|
+
* const note = await this.notesService.create(body)
|
|
2394
|
+
* return ctx.json(note, 201)
|
|
2395
|
+
* }
|
|
2396
|
+
*
|
|
2397
|
+
* @Route({
|
|
2398
|
+
* query: paginationSchema,
|
|
2399
|
+
* response: z.array(noteSchema) // 200 auto-derived from 'index' method
|
|
2400
|
+
* })
|
|
2401
|
+
* async index(ctx: RouterContext): Promise<Response> {
|
|
2402
|
+
* // GET /api/v1/notes (auto-derived)
|
|
2403
|
+
* // Query params auto-validated
|
|
2404
|
+
* const notes = await this.notesService.list()
|
|
2405
|
+
* return ctx.json(notes)
|
|
2406
|
+
* }
|
|
2407
|
+
*
|
|
2408
|
+
* @Route({
|
|
2409
|
+
* params: z.object({ id: z.string().uuid() }),
|
|
2410
|
+
* response: {
|
|
2411
|
+
* schema: noteSchema,
|
|
2412
|
+
* description: 'Note details'
|
|
2413
|
+
* },
|
|
2414
|
+
* security: [] // Override to make public
|
|
2415
|
+
* })
|
|
2416
|
+
* async show(ctx: RouterContext): Promise<Response> {
|
|
2417
|
+
* // GET /api/v1/notes/:id (auto-derived)
|
|
2418
|
+
* // URL params auto-validated
|
|
2419
|
+
* // Response: 200 → noteSchema (status auto-derived)
|
|
2420
|
+
* // Security: [] (public route, override controller security)
|
|
2421
|
+
* const id = ctx.param('id')
|
|
2422
|
+
* const note = await this.notesService.findById(id)
|
|
2423
|
+
* return ctx.json(note)
|
|
2424
|
+
* }
|
|
2425
|
+
* }
|
|
2426
|
+
* ```
|
|
2427
|
+
*/
|
|
2428
|
+
declare function Route(config: Omit<RouteConfig, 'statusCode'>): (target: object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
2429
|
+
/**
|
|
2430
|
+
* Get the route metadata from a controller method
|
|
2431
|
+
*
|
|
2432
|
+
* @param target - Controller instance or prototype
|
|
2433
|
+
* @param methodName - Name of the method
|
|
2434
|
+
* @returns Route metadata or undefined if not decorated
|
|
2435
|
+
*/
|
|
2436
|
+
declare function getRouteMetadata(target: object, methodName: string): RouteMetadata | undefined;
|
|
2437
|
+
/**
|
|
2438
|
+
* Get all methods with route decorators (@Route, @Get, @Post, etc.) from a controller
|
|
2439
|
+
*
|
|
2440
|
+
* @param ControllerClass - Controller class
|
|
2441
|
+
* @returns Array of method names that have route metadata
|
|
2442
|
+
*/
|
|
2443
|
+
declare function getRouteDecoratedMethods(ControllerClass: new (...args: unknown[]) => object): string[];
|
|
2444
|
+
//#endregion
|
|
2445
|
+
//#region src/router/schemas/common.schemas.d.ts
|
|
2446
|
+
/**
|
|
2447
|
+
* Common OpenAPI Schemas
|
|
2448
|
+
*
|
|
2449
|
+
* Reusable schema definitions for common API patterns:
|
|
2450
|
+
* - Error responses
|
|
2451
|
+
* - Pagination
|
|
2452
|
+
* - Common parameters
|
|
2453
|
+
*/
|
|
2454
|
+
/**
|
|
2455
|
+
* Generic error response schema
|
|
2456
|
+
* Used for all error responses (4xx, 5xx)
|
|
2457
|
+
* Matches ApplicationError.toErrorResponse() structure
|
|
2458
|
+
*/
|
|
2459
|
+
declare const errorResponseSchema: z.ZodObject<{
|
|
2460
|
+
code: z.ZodNumber;
|
|
2461
|
+
message: z.ZodString;
|
|
2462
|
+
timestamp: z.ZodString;
|
|
2463
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2464
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2465
|
+
}, z.core.$strip>;
|
|
2466
|
+
/**
|
|
2467
|
+
* Validation error response schema
|
|
2468
|
+
* Used for 400 Bad Request with validation failures
|
|
2469
|
+
* Matches ApplicationError.toErrorResponse() structure with validation-specific metadata
|
|
2470
|
+
*/
|
|
2471
|
+
declare const validationErrorResponseSchema: z.ZodObject<{
|
|
2472
|
+
code: z.ZodNumber;
|
|
2473
|
+
message: z.ZodString;
|
|
2474
|
+
timestamp: z.ZodString;
|
|
2475
|
+
metadata: z.ZodObject<{
|
|
2476
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
2477
|
+
path: z.ZodString;
|
|
2478
|
+
message: z.ZodString;
|
|
2479
|
+
code: z.ZodString;
|
|
2480
|
+
}, z.core.$strip>>;
|
|
2481
|
+
}, z.core.$strip>;
|
|
2482
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2483
|
+
}, z.core.$strip>;
|
|
2484
|
+
/**
|
|
2485
|
+
* Pagination query parameters schema
|
|
2486
|
+
* Used for list endpoints
|
|
2487
|
+
*/
|
|
2488
|
+
declare const paginationQuerySchema: z.ZodObject<{
|
|
2489
|
+
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
2490
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
2491
|
+
}, z.core.$strip>;
|
|
2492
|
+
/**
|
|
2493
|
+
* Paginated response wrapper schema
|
|
2494
|
+
* Generic wrapper for paginated list responses
|
|
2495
|
+
*/
|
|
2496
|
+
declare const paginatedResponseSchema: <T extends z.ZodType>(itemSchema: T) => z.ZodObject<{
|
|
2497
|
+
data: z.ZodArray<T>;
|
|
2498
|
+
pagination: z.ZodObject<{
|
|
2499
|
+
page: z.ZodNumber;
|
|
2500
|
+
limit: z.ZodNumber;
|
|
2501
|
+
total: z.ZodNumber;
|
|
2502
|
+
totalPages: z.ZodNumber;
|
|
2503
|
+
}, z.core.$strip>;
|
|
2504
|
+
}, z.core.$strip>;
|
|
2505
|
+
/**
|
|
2506
|
+
* UUID parameter schema
|
|
2507
|
+
* Used for :id parameters in RESTful routes
|
|
2508
|
+
*/
|
|
2509
|
+
declare const uuidParamSchema: z.ZodObject<{
|
|
2510
|
+
id: z.ZodString;
|
|
2511
|
+
}, z.core.$strip>;
|
|
2512
|
+
/**
|
|
2513
|
+
* Success message response schema
|
|
2514
|
+
* Used for operations that don't return data (e.g., DELETE)
|
|
2515
|
+
*/
|
|
2516
|
+
declare const successMessageSchema: z.ZodObject<{
|
|
2517
|
+
message: z.ZodString;
|
|
2518
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2519
|
+
}, z.core.$strip>;
|
|
2520
|
+
/**
|
|
2521
|
+
* Common HTTP status error schemas
|
|
2522
|
+
* Pre-configured for standard error responses
|
|
2523
|
+
*/
|
|
2524
|
+
declare const commonErrorSchemas: {
|
|
2525
|
+
readonly 400: {
|
|
2526
|
+
readonly schema: z.ZodObject<{
|
|
2527
|
+
code: z.ZodNumber;
|
|
2528
|
+
message: z.ZodString;
|
|
2529
|
+
timestamp: z.ZodString;
|
|
2530
|
+
metadata: z.ZodObject<{
|
|
2531
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
2532
|
+
path: z.ZodString;
|
|
2533
|
+
message: z.ZodString;
|
|
2534
|
+
code: z.ZodString;
|
|
2535
|
+
}, z.core.$strip>>;
|
|
2536
|
+
}, z.core.$strip>;
|
|
2537
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2538
|
+
}, z.core.$strip>;
|
|
2539
|
+
readonly description: "Validation error";
|
|
2540
|
+
};
|
|
2541
|
+
readonly 401: {
|
|
2542
|
+
readonly schema: z.ZodObject<{
|
|
2543
|
+
code: z.ZodNumber;
|
|
2544
|
+
message: z.ZodString;
|
|
2545
|
+
timestamp: z.ZodString;
|
|
2546
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2547
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2548
|
+
}, z.core.$strip>;
|
|
2549
|
+
readonly description: "Unauthorized";
|
|
2550
|
+
};
|
|
2551
|
+
readonly 403: {
|
|
2552
|
+
readonly schema: z.ZodObject<{
|
|
2553
|
+
code: z.ZodNumber;
|
|
2554
|
+
message: z.ZodString;
|
|
2555
|
+
timestamp: z.ZodString;
|
|
2556
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2557
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2558
|
+
}, z.core.$strip>;
|
|
2559
|
+
readonly description: "Forbidden";
|
|
2560
|
+
};
|
|
2561
|
+
readonly 404: {
|
|
2562
|
+
readonly schema: z.ZodObject<{
|
|
2563
|
+
code: z.ZodNumber;
|
|
2564
|
+
message: z.ZodString;
|
|
2565
|
+
timestamp: z.ZodString;
|
|
2566
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2567
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2568
|
+
}, z.core.$strip>;
|
|
2569
|
+
readonly description: "Not found";
|
|
2570
|
+
};
|
|
2571
|
+
readonly 409: {
|
|
2572
|
+
readonly schema: z.ZodObject<{
|
|
2573
|
+
code: z.ZodNumber;
|
|
2574
|
+
message: z.ZodString;
|
|
2575
|
+
timestamp: z.ZodString;
|
|
2576
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2577
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2578
|
+
}, z.core.$strip>;
|
|
2579
|
+
readonly description: "Conflict";
|
|
2580
|
+
};
|
|
2581
|
+
readonly 500: {
|
|
2582
|
+
readonly schema: z.ZodObject<{
|
|
2583
|
+
code: z.ZodNumber;
|
|
2584
|
+
message: z.ZodString;
|
|
2585
|
+
timestamp: z.ZodString;
|
|
2586
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
2587
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
2588
|
+
}, z.core.$strip>;
|
|
2589
|
+
readonly description: "Internal server error";
|
|
2590
|
+
};
|
|
2591
|
+
};
|
|
2592
|
+
//#endregion
|
|
2593
|
+
//#region src/router/middleware/domain.middleware.d.ts
|
|
2594
|
+
/**
|
|
2595
|
+
* Parse a domain pattern into a regex and extract parameter names.
|
|
2596
|
+
*
|
|
2597
|
+
* @example
|
|
2598
|
+
* parseDomainPattern('{tenant}.example.com')
|
|
2599
|
+
* // => { regex: /^([^.]+)\.example\.com$/, paramNames: ['tenant'] }
|
|
2600
|
+
*
|
|
2601
|
+
* parseDomainPattern('{region}.{tenant}.example.com')
|
|
2602
|
+
* // => { regex: /^([^.]+)\.([^.]+)\.example\.com$/, paramNames: ['region', 'tenant'] }
|
|
2603
|
+
*/
|
|
2604
|
+
declare function parseDomainPattern(pattern: string): {
|
|
2605
|
+
regex: RegExp;
|
|
2606
|
+
paramNames: string[];
|
|
2607
|
+
};
|
|
2608
|
+
/**
|
|
2609
|
+
* Create a Hono middleware that matches the request host against a domain pattern.
|
|
2610
|
+
*
|
|
2611
|
+
* When the host matches, domain parameters are extracted and stored in context
|
|
2612
|
+
* variables accessible via `ctx.domain(key)`.
|
|
2613
|
+
*
|
|
2614
|
+
* When the host does NOT match, throws `DomainMismatchError` (404).
|
|
2615
|
+
*
|
|
2616
|
+
* @param pattern - Domain pattern with `{param}` placeholders (e.g., '{tenant}.myapp.com')
|
|
2617
|
+
*
|
|
2618
|
+
* @example
|
|
2619
|
+
* ```typescript
|
|
2620
|
+
* // Applied automatically by RouteRegistrationService for controllers with domain config
|
|
2621
|
+
* @Controller('/dashboard', { domain: '{tenant}.myapp.com' })
|
|
2622
|
+
* export class DashboardController {
|
|
2623
|
+
* async index(ctx: RouterContext) {
|
|
2624
|
+
* const tenant = ctx.domain('tenant')
|
|
2625
|
+
* }
|
|
2626
|
+
* }
|
|
2627
|
+
* ```
|
|
2628
|
+
*/
|
|
2629
|
+
declare function createDomainMiddleware(pattern: string): MiddlewareHandler<RouterEnv>;
|
|
2630
|
+
//#endregion
|
|
2631
|
+
//#region src/router/middleware/verify-signature.middleware.d.ts
|
|
2632
|
+
/**
|
|
2633
|
+
* Middleware that verifies signed URLs.
|
|
2634
|
+
*
|
|
2635
|
+
* Checks the `signature` (and optionally `expires`) query params against the
|
|
2636
|
+
* request URL using HMAC-SHA256 via `crypto.subtle.verify()`.
|
|
2637
|
+
*
|
|
2638
|
+
* Requires `APP_SECRET` in the Cloudflare Workers environment bindings.
|
|
2639
|
+
*
|
|
2640
|
+
* @throws InvalidSignatureError (403) if signature is missing, invalid, or expired
|
|
2641
|
+
*
|
|
2642
|
+
* @example
|
|
2643
|
+
* ```typescript
|
|
2644
|
+
* @Module({ controllers: [UnsubscribeController], providers: [VerifySignatureMiddleware] })
|
|
2645
|
+
* export class EmailModule implements RouteConfigurable {
|
|
2646
|
+
* configureRoutes(router: Router): void {
|
|
2647
|
+
* router.middleware(VerifySignatureMiddleware)
|
|
2648
|
+
* }
|
|
2649
|
+
* }
|
|
2650
|
+
* ```
|
|
2651
|
+
*/
|
|
2652
|
+
declare class VerifySignatureMiddleware implements Middleware {
|
|
2653
|
+
handle(ctx: RouterContext, next: Next$1): Promise<void>;
|
|
2654
|
+
}
|
|
2655
|
+
//#endregion
|
|
2656
|
+
//#region src/router/signed-url.d.ts
|
|
2657
|
+
/**
|
|
2658
|
+
* Signed URL utilities using HMAC-SHA256 via Web Crypto API.
|
|
2659
|
+
*
|
|
2660
|
+
* Follows the Cloudflare Workers signing pattern:
|
|
2661
|
+
* https://developers.cloudflare.com/workers/examples/signing-requests/
|
|
2662
|
+
*
|
|
2663
|
+
* Uses `crypto.subtle.verify()` for timing-attack-safe comparison.
|
|
2664
|
+
*/
|
|
2665
|
+
/**
|
|
2666
|
+
* Options for signing a URL.
|
|
2667
|
+
*/
|
|
2668
|
+
interface SignedUrlOptions {
|
|
2669
|
+
/** Time-to-live in seconds. URL expires after this duration. */
|
|
2670
|
+
expiresIn?: number;
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Sign a URL with HMAC-SHA256.
|
|
2674
|
+
*
|
|
2675
|
+
* Appends `signature` and optionally `expires` query parameters.
|
|
2676
|
+
* The signature covers the pathname + search (excluding the signature params themselves).
|
|
2677
|
+
*
|
|
2678
|
+
* @param url - Full URL or path to sign
|
|
2679
|
+
* @param secret - HMAC secret key (e.g., from env.APP_SECRET)
|
|
2680
|
+
* @param options - Optional expiration
|
|
2681
|
+
* @returns URL string with `signature` (and `expires`) query params appended
|
|
2682
|
+
*/
|
|
2683
|
+
declare function signUrl(url: string, secret: string, options?: SignedUrlOptions): Promise<string>;
|
|
2684
|
+
/**
|
|
2685
|
+
* Verify a signed URL using `crypto.subtle.verify()` (timing-attack-safe).
|
|
2686
|
+
*
|
|
2687
|
+
* @param url - Full URL or path with signature query param
|
|
2688
|
+
* @param secret - HMAC secret key (same key used for signing)
|
|
2689
|
+
* @returns true if signature is valid and not expired
|
|
2690
|
+
*/
|
|
2691
|
+
declare function verifySignedUrl(url: string, secret: string): Promise<boolean>;
|
|
2692
|
+
//#endregion
|
|
2693
|
+
//#region src/router/errors/controller-registration.error.d.ts
|
|
2694
|
+
/**
|
|
2695
|
+
* Error thrown when a controller fails to register
|
|
2696
|
+
*
|
|
2697
|
+
* This typically happens when:
|
|
2698
|
+
* - Controller is missing the `@Controller` decorator
|
|
2699
|
+
* - Controller route metadata is not set
|
|
2700
|
+
* - Controller class name is invalid
|
|
2701
|
+
*
|
|
2702
|
+
* Error Code: 9005
|
|
2703
|
+
*/
|
|
2704
|
+
declare class ControllerRegistrationError extends ApplicationError {
|
|
2705
|
+
constructor(controllerName: string, reason?: string);
|
|
2706
|
+
}
|
|
2707
|
+
//#endregion
|
|
2708
|
+
//#region src/router/errors/hono-app-already-configured.error.d.ts
|
|
2709
|
+
/**
|
|
2710
|
+
* Error thrown when HonoApp.configure() is called more than once.
|
|
2711
|
+
*
|
|
2712
|
+
* HonoApp can only be configured a single time during application bootstrap.
|
|
2713
|
+
*/
|
|
2714
|
+
declare class HonoAppAlreadyConfiguredError extends ApplicationError {
|
|
2715
|
+
constructor();
|
|
2716
|
+
}
|
|
2717
|
+
//#endregion
|
|
2718
|
+
//#region src/router/errors/openapi-route-registration.error.d.ts
|
|
2719
|
+
/**
|
|
2720
|
+
* OpenAPIRouteRegistrationError
|
|
2721
|
+
*
|
|
2722
|
+
* Thrown when an OpenAPI route fails to register properly
|
|
2723
|
+
* This indicates a configuration issue with route decorators or metadata
|
|
2724
|
+
* Uses i18n key for localized error messages
|
|
2725
|
+
*
|
|
2726
|
+
* @example
|
|
2727
|
+
* ```typescript
|
|
2728
|
+
* throw new OpenAPIRouteRegistrationError('/api/v1/users', 'Missing response schema')
|
|
2729
|
+
* ```
|
|
2730
|
+
*/
|
|
2731
|
+
declare class OpenAPIRouteRegistrationError extends ApplicationError {
|
|
2732
|
+
constructor(path: string, reason: string);
|
|
2733
|
+
}
|
|
2734
|
+
//#endregion
|
|
2735
|
+
//#region src/router/errors/openapi-validation.error.d.ts
|
|
2736
|
+
/**
|
|
2737
|
+
* OpenAPIValidationError
|
|
2738
|
+
*
|
|
2739
|
+
* Thrown when OpenAPI request/response validation fails
|
|
2740
|
+
* Uses i18n key for localized error messages
|
|
2741
|
+
*
|
|
2742
|
+
* HTTP Status: 400 Bad Request
|
|
2743
|
+
* Error Code: 1004
|
|
2744
|
+
*
|
|
2745
|
+
* @example
|
|
2746
|
+
* ```typescript
|
|
2747
|
+
* throw new OpenAPIValidationError('Request body missing required field: email')
|
|
2748
|
+
* ```
|
|
2749
|
+
*/
|
|
2750
|
+
declare class OpenAPIValidationError extends ApplicationError {
|
|
2751
|
+
constructor(details: string);
|
|
2752
|
+
}
|
|
2753
|
+
//#endregion
|
|
2754
|
+
//#region src/router/errors/route-not-found.error.d.ts
|
|
2755
|
+
/**
|
|
2756
|
+
* Error thrown when a requested route is not found
|
|
2757
|
+
*
|
|
2758
|
+
* HTTP Status: 404 Not Found
|
|
2759
|
+
* Error Code: 4004
|
|
2760
|
+
*/
|
|
2761
|
+
declare class RouteNotFoundError extends ApplicationError {
|
|
2762
|
+
constructor(path: string, method: string);
|
|
2763
|
+
}
|
|
2764
|
+
//#endregion
|
|
2765
|
+
//#region src/router/errors/schema-validation.error.d.ts
|
|
2766
|
+
/**
|
|
2767
|
+
* SchemaValidationError
|
|
2768
|
+
*
|
|
2769
|
+
* Thrown when Zod schema validation fails
|
|
2770
|
+
*/
|
|
2771
|
+
declare class SchemaValidationError extends ApplicationError {
|
|
2772
|
+
constructor(zodError: ZodError);
|
|
2773
|
+
}
|
|
2774
|
+
//#endregion
|
|
2775
|
+
//#region src/router/errors/index.d.ts
|
|
2776
|
+
/**
|
|
2777
|
+
* Error thrown when a request's host header does not match the expected domain pattern.
|
|
2778
|
+
*
|
|
2779
|
+
* HTTP Status: 404 Not Found
|
|
2780
|
+
*/
|
|
2781
|
+
declare class DomainMismatchError extends HttpException {
|
|
2782
|
+
constructor();
|
|
2783
|
+
}
|
|
2784
|
+
/**
|
|
2785
|
+
* Thrown when registering a named route that conflicts with an existing route name.
|
|
2786
|
+
*
|
|
2787
|
+
* Error Code: 9010
|
|
2788
|
+
*/
|
|
2789
|
+
declare class DuplicateRouteNameError extends ApplicationError {
|
|
2790
|
+
constructor(name: string, existingHandler: string, newHandler: string);
|
|
2791
|
+
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Error thrown when a signed URL has an invalid or expired signature.
|
|
2794
|
+
*
|
|
2795
|
+
* HTTP Status: 403 Forbidden
|
|
2796
|
+
*/
|
|
2797
|
+
declare class InvalidSignatureError extends HttpException {
|
|
2798
|
+
constructor();
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Thrown when a required environment variable is not set.
|
|
2802
|
+
*
|
|
2803
|
+
* Maps to HTTP 500 via error code range (9xxx → 500).
|
|
2804
|
+
*/
|
|
2805
|
+
declare class MissingEnvironmentVariableError extends ApplicationError {
|
|
2806
|
+
constructor(variable: string);
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Thrown when a required path or domain parameter is missing during URL generation.
|
|
2810
|
+
*
|
|
2811
|
+
* Error Code: 9012
|
|
2812
|
+
*/
|
|
2813
|
+
declare class MissingRouteParamError extends ApplicationError {
|
|
2814
|
+
constructor(param: string, name: string, path: string);
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* ResponseValidationError
|
|
2818
|
+
*
|
|
2819
|
+
* Thrown when a controller's response body does not match the declared Zod response schema.
|
|
2820
|
+
* Indicates a server-side schema mismatch — the controller is returning data that
|
|
2821
|
+
* violates its own API contract.
|
|
2822
|
+
*/
|
|
2823
|
+
declare class ResponseValidationError extends ApplicationError {
|
|
2824
|
+
constructor(zodError: ZodError);
|
|
2825
|
+
}
|
|
2826
|
+
/**
|
|
2827
|
+
* Thrown when attempting to generate a URL for a route name that doesn't exist in the registry.
|
|
2828
|
+
*
|
|
2829
|
+
* Error Code: 9011
|
|
2830
|
+
*/
|
|
2831
|
+
declare class RouteNameNotFoundError extends ApplicationError {
|
|
2832
|
+
constructor(name: string);
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Thrown when `router.use()` is called inside a `group()` callback.
|
|
2836
|
+
* `use()` registers global middleware and is only allowed on the root Router.
|
|
2837
|
+
*
|
|
2838
|
+
* Error Code: 9013
|
|
2839
|
+
*/
|
|
2840
|
+
declare class RouterUseScopeError extends ApplicationError {
|
|
2841
|
+
constructor();
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Thrown when a middleware calls next() more than once.
|
|
2845
|
+
* This is a programming error — each middleware must call next() at most once.
|
|
2846
|
+
*
|
|
2847
|
+
* Error Code: 9014
|
|
2848
|
+
*/
|
|
2849
|
+
declare class MiddlewareNextCalledMultipleTimesError extends ApplicationError {
|
|
2850
|
+
constructor(middlewareName: string);
|
|
2851
|
+
}
|
|
2852
|
+
//#endregion
|
|
2853
|
+
//#region src/application.d.ts
|
|
2854
|
+
interface ApplicationConfig {
|
|
2855
|
+
/** Root application module */
|
|
2856
|
+
module: ModuleClass | DynamicModule;
|
|
2857
|
+
/** Logging configuration. Defaults: level=INFO, formatter='json' */
|
|
2858
|
+
logging?: {
|
|
2859
|
+
level?: LogLevel;
|
|
2860
|
+
formatter?: 'json' | 'pretty';
|
|
2861
|
+
};
|
|
2862
|
+
/**
|
|
2863
|
+
* API versioning configuration.
|
|
2864
|
+
* When provided, enables URI-based versioning for controllers.
|
|
2865
|
+
*/
|
|
2866
|
+
versioning?: VersioningOptions;
|
|
2867
|
+
/**
|
|
2868
|
+
* Custom exception handler class.
|
|
2869
|
+
*
|
|
2870
|
+
* Extend {@link ExceptionHandler} and override `register()` to configure
|
|
2871
|
+
* custom reporting, rendering, and post-processing of exceptions.
|
|
2872
|
+
*
|
|
2873
|
+
* When not provided, {@link DefaultExceptionHandler} is used (standard
|
|
2874
|
+
* severity-based logging and JSON error responses).
|
|
2875
|
+
*
|
|
2876
|
+
* @example
|
|
2877
|
+
* ```typescript
|
|
2878
|
+
* new Stratal({
|
|
2879
|
+
* module: AppModule,
|
|
2880
|
+
* exceptionHandler: AppExceptionHandler,
|
|
2881
|
+
* })
|
|
2882
|
+
* ```
|
|
2883
|
+
*/
|
|
2884
|
+
exceptionHandler?: Constructor<ExceptionHandler>;
|
|
2885
|
+
}
|
|
2886
|
+
interface ApplicationOptions extends ApplicationConfig {
|
|
2887
|
+
env: StratalEnv;
|
|
2888
|
+
ctx: StratalExecutionContext;
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Application
|
|
2892
|
+
*
|
|
2893
|
+
* Main application class managing the two-tier container hierarchy:
|
|
2894
|
+
* - Global Container: All services (singletons via tsyringe native)
|
|
2895
|
+
* - Request Container: Child of global, context-enriched instances per request
|
|
2896
|
+
*
|
|
2897
|
+
* @example
|
|
2898
|
+
* ```typescript
|
|
2899
|
+
* const app = new Application({ module: AppModule, env, ctx })
|
|
2900
|
+
* await app.initialize()
|
|
2901
|
+
*
|
|
2902
|
+
* // Access container via getter
|
|
2903
|
+
* const service = app.container.resolve(MY_TOKEN)
|
|
2904
|
+
*
|
|
2905
|
+
* // Handle HTTP request (via HonoApp)
|
|
2906
|
+
* // Handle queue batch
|
|
2907
|
+
* await app.handleQueue(batch, 'my-queue')
|
|
2908
|
+
* ```
|
|
2909
|
+
*/
|
|
2910
|
+
declare class Application {
|
|
2911
|
+
/**
|
|
2912
|
+
* Unified Container - manages all DI operations
|
|
2913
|
+
*/
|
|
2914
|
+
private _container;
|
|
2915
|
+
private honoApp;
|
|
2916
|
+
private moduleRegistry;
|
|
2917
|
+
private consumerRegistry;
|
|
2918
|
+
private cronManager;
|
|
2919
|
+
private quarry;
|
|
2920
|
+
private initialized;
|
|
2921
|
+
private routingInitPromise;
|
|
2922
|
+
private handlerInitPromise;
|
|
2923
|
+
readonly env: StratalEnv;
|
|
2924
|
+
private readonly appConfig;
|
|
2925
|
+
constructor({
|
|
2926
|
+
env,
|
|
2927
|
+
ctx,
|
|
2928
|
+
...config
|
|
2929
|
+
}: ApplicationOptions);
|
|
2930
|
+
/**
|
|
2931
|
+
* Get the Container instance
|
|
2932
|
+
*/
|
|
2933
|
+
get container(): Container;
|
|
2934
|
+
/**
|
|
2935
|
+
* Lazily initialize routing and return the HonoApp instance.
|
|
2936
|
+
*
|
|
2937
|
+
* Routing (service registration, HonoApp resolution, route configuration)
|
|
2938
|
+
* is deferred so that `scheduled` and `queue` handlers don't pay the CPU
|
|
2939
|
+
* cost of route setup on cold start.
|
|
2940
|
+
*/
|
|
2941
|
+
ensureHono(): Promise<HonoApp>;
|
|
2942
|
+
/**
|
|
2943
|
+
* Get the application configuration
|
|
2944
|
+
*/
|
|
2945
|
+
get config(): ApplicationConfig;
|
|
2946
|
+
initialize(): Promise<void>;
|
|
2947
|
+
private initializeInternal;
|
|
2948
|
+
/**
|
|
2949
|
+
* Register routing services as singletons in the container.
|
|
2950
|
+
* Called after module initialization so I18N_TOKENS.Options is available.
|
|
2951
|
+
*/
|
|
2952
|
+
private registerRoutingServices;
|
|
2953
|
+
/**
|
|
2954
|
+
* Wire up queue consumers and event listeners.
|
|
2955
|
+
* Called lazily on first fetch/queue — not during scheduled handling.
|
|
2956
|
+
*/
|
|
2957
|
+
private initializeHandlers;
|
|
2958
|
+
/**
|
|
2959
|
+
* Register routing services, resolve HonoApp, and configure routes.
|
|
2960
|
+
* Called lazily on first fetch — not during scheduled/queue handling.
|
|
2961
|
+
*/
|
|
2962
|
+
private initializeRouting;
|
|
2963
|
+
/**
|
|
2964
|
+
* Resolve a service from the container
|
|
2965
|
+
*/
|
|
2966
|
+
resolve<T>(token: symbol): T;
|
|
2967
|
+
/**
|
|
2968
|
+
* Handle queue batch processing
|
|
2969
|
+
*/
|
|
2970
|
+
handleQueue(batch: MessageBatch, queueName: string): Promise<void>;
|
|
2971
|
+
/**
|
|
2972
|
+
* Handle scheduled cron trigger
|
|
2973
|
+
*/
|
|
2974
|
+
handleScheduled(controller: ScheduledController): Promise<void>;
|
|
2975
|
+
/**
|
|
2976
|
+
* Create mock RouterContext for queue/cron/seeder processing
|
|
2977
|
+
*/
|
|
2978
|
+
createMockRouterContext(locale?: string): RouterContext;
|
|
2979
|
+
shutdown(): Promise<void>;
|
|
2980
|
+
/**
|
|
2981
|
+
* Execute a command by name in a request-scoped container.
|
|
2982
|
+
*/
|
|
2983
|
+
handleCommand(name: string, input?: CommandInput): Promise<CommandResult>;
|
|
2984
|
+
private registerCommands;
|
|
2985
|
+
private registerSeeders;
|
|
2986
|
+
private registerQueueConsumers;
|
|
2987
|
+
private registerCronJobs;
|
|
2988
|
+
/**
|
|
2989
|
+
* Auto-wire `@Listener()` classes with the EventRegistry.
|
|
2990
|
+
*/
|
|
2991
|
+
private registerEventListeners;
|
|
2992
|
+
/**
|
|
2993
|
+
* Register LoggerService and dependencies
|
|
2994
|
+
*/
|
|
2995
|
+
private registerLoggerService;
|
|
2996
|
+
/**
|
|
2997
|
+
* Register core services with explicit scope
|
|
2998
|
+
*/
|
|
2999
|
+
private registerCoreServices;
|
|
3000
|
+
/**
|
|
3001
|
+
* Initialize the ExceptionHandler: call register(), then module onException hooks.
|
|
3002
|
+
*/
|
|
3003
|
+
private initializeExceptionHandler;
|
|
3004
|
+
}
|
|
3005
|
+
//#endregion
|
|
3006
|
+
//#region src/router/services/versioning.service.d.ts
|
|
3007
|
+
/**
|
|
3008
|
+
* Resolves version prefixes for route paths.
|
|
3009
|
+
*
|
|
3010
|
+
* Handles VERSION_NEUTRAL, multi-version arrays, default version fallback,
|
|
3011
|
+
* and configurable prefix (default: 'v').
|
|
3012
|
+
*
|
|
3013
|
+
* Registered as a singleton in the container.
|
|
3014
|
+
*/
|
|
3015
|
+
declare class VersioningService {
|
|
3016
|
+
private readonly options;
|
|
3017
|
+
constructor(app: Application);
|
|
3018
|
+
/** Whether versioning is enabled */
|
|
3019
|
+
get enabled(): boolean;
|
|
3020
|
+
/**
|
|
3021
|
+
* Resolve versioned paths for a base path.
|
|
3022
|
+
*
|
|
3023
|
+
* @param basePath - The base path (e.g., '/users')
|
|
3024
|
+
* @param version - Explicit version from controller/router config
|
|
3025
|
+
* @returns Array of versioned path strings (e.g., ['/v1/users', '/v2/users'])
|
|
3026
|
+
*/
|
|
3027
|
+
resolve(basePath: string, version?: string | string[] | typeof VERSION_NEUTRAL): string[];
|
|
3028
|
+
}
|
|
3029
|
+
//#endregion
|
|
3030
|
+
//#region src/router/route-registry.d.ts
|
|
3031
|
+
/**
|
|
3032
|
+
* A single registered route in the application.
|
|
3033
|
+
* Tracks both named and unnamed routes, HTTP and WebSocket.
|
|
3034
|
+
*/
|
|
3035
|
+
interface RegisteredRoute {
|
|
3036
|
+
/** Route name for URL generation (undefined = unnamed, still tracked) */
|
|
3037
|
+
name?: string;
|
|
3038
|
+
/** HTTP method or 'ws' for WebSocket gateways */
|
|
3039
|
+
method: HttpMethod | 'ws';
|
|
3040
|
+
/** Primary path in Hono-style :param format */
|
|
3041
|
+
path: string;
|
|
3042
|
+
/** Locale-prefixed path variants (e.g., '/:locale/users/:id') */
|
|
3043
|
+
localePaths?: string[];
|
|
3044
|
+
/** Parameter names extracted from path */
|
|
3045
|
+
paramNames: string[];
|
|
3046
|
+
/** Domain pattern (e.g., '{tenant}.example.com') */
|
|
3047
|
+
domain?: string;
|
|
3048
|
+
/** Parameter names extracted from domain */
|
|
3049
|
+
domainParamNames: string[];
|
|
3050
|
+
/** Controller class name */
|
|
3051
|
+
controller: string;
|
|
3052
|
+
/** Controller method name */
|
|
3053
|
+
action: string;
|
|
3054
|
+
/** Whether the route is hidden from OpenAPI docs */
|
|
3055
|
+
hidden: boolean;
|
|
3056
|
+
/** Middleware class names applied to this route */
|
|
3057
|
+
middleware: string[];
|
|
3058
|
+
/** Whether this is a locale-prefixed variant */
|
|
3059
|
+
isLocaleVariant?: boolean;
|
|
3060
|
+
}
|
|
3061
|
+
/**
|
|
3062
|
+
* Input for registering a route. The registry auto-extracts param names
|
|
3063
|
+
* and expands versioned/locale paths via injected services.
|
|
3064
|
+
*/
|
|
3065
|
+
type RouteRegistrationInput = Omit<RegisteredRoute, 'paramNames' | 'domainParamNames' | 'path' | 'localePaths' | 'isLocaleVariant'> & {
|
|
3066
|
+
/** Base path before versioning/locale expansion */basePath: string; /** Version from controller/router config (used by VersioningService). Accepts VERSION_NEUTRAL symbol. */
|
|
3067
|
+
version?: string | string[] | typeof VERSION_NEUTRAL; /** Pre-computed param names (optional, auto-extracted if omitted) */
|
|
3068
|
+
paramNames?: string[]; /** Pre-computed domain param names (optional, auto-extracted if omitted) */
|
|
3069
|
+
domainParamNames?: string[];
|
|
3070
|
+
};
|
|
3071
|
+
/**
|
|
3072
|
+
* Central registry for all application routes.
|
|
3073
|
+
* Single source of truth — used by `route:list`, `route:types`, and URL generation.
|
|
3074
|
+
*
|
|
3075
|
+
* Routes are automatically expanded via VersioningService and LocalePathService
|
|
3076
|
+
* during registration, and sorted by specificity when retrieved via `all()`.
|
|
3077
|
+
*
|
|
3078
|
+
* Registered as a singleton in the container.
|
|
3079
|
+
*/
|
|
3080
|
+
declare class RouteRegistry {
|
|
3081
|
+
private readonly versioningService;
|
|
3082
|
+
private readonly localePathService;
|
|
3083
|
+
private readonly routes;
|
|
3084
|
+
private readonly namedRoutes;
|
|
3085
|
+
private _sortedCache;
|
|
3086
|
+
constructor(versioningService: VersioningService, localePathService: LocalePathService);
|
|
3087
|
+
/**
|
|
3088
|
+
* Register a route. Expands via VersioningService + LocalePathService.
|
|
3089
|
+
* Named routes must have unique names.
|
|
3090
|
+
*
|
|
3091
|
+
* @returns Array of expanded RegisteredRoute entries (primary + locale variants)
|
|
3092
|
+
* @throws DuplicateRouteNameError if a named route with the same name already exists
|
|
3093
|
+
*/
|
|
3094
|
+
register(input: RouteRegistrationInput): RegisteredRoute[];
|
|
3095
|
+
/** Get a named route by name */
|
|
3096
|
+
get(name: string): RegisteredRoute | undefined;
|
|
3097
|
+
/** Check if a named route exists */
|
|
3098
|
+
has(name: string): boolean;
|
|
3099
|
+
/** Get all routes sorted by specificity (static > param > wildcard, primary before locale) */
|
|
3100
|
+
all(): RegisteredRoute[];
|
|
3101
|
+
/** Get only named routes */
|
|
3102
|
+
named(): RegisteredRoute[];
|
|
3103
|
+
}
|
|
3104
|
+
//#endregion
|
|
3105
|
+
//#region src/router/uri.d.ts
|
|
3106
|
+
/**
|
|
3107
|
+
* Options for URL generation methods.
|
|
3108
|
+
*/
|
|
3109
|
+
interface UriOptions {
|
|
3110
|
+
/** Generate absolute URL (scheme + host). Defaults to false. */
|
|
3111
|
+
absolute?: boolean;
|
|
3112
|
+
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Options for signed URL generation methods.
|
|
3115
|
+
*/
|
|
3116
|
+
interface SignedUriOptions extends UriOptions, SignedUrlOptions {}
|
|
3117
|
+
/**
|
|
3118
|
+
* Build a URL from a registered route, filling path/domain params and appending extras as query string.
|
|
3119
|
+
*
|
|
3120
|
+
* Pure function — no request context needed. Used by both the `Uri` class and the standalone `route()` function.
|
|
3121
|
+
*
|
|
3122
|
+
* @param route - The registered route to build a URL for
|
|
3123
|
+
* @param name - Route name (used in error messages)
|
|
3124
|
+
* @param params - Path params, domain params, and extra query params
|
|
3125
|
+
* @returns Relative URL string (or absolute with domain prefix if route has a domain pattern)
|
|
3126
|
+
*
|
|
3127
|
+
* @throws MissingRouteParamError if a required path or domain param is missing
|
|
3128
|
+
*/
|
|
3129
|
+
declare function buildRouteUrl(route: RegisteredRoute, name: string, params?: Record<string, string>): string;
|
|
3130
|
+
/**
|
|
3131
|
+
* URL generation service for named routes, signed URLs, and request URL access.
|
|
3132
|
+
*
|
|
3133
|
+
* Registered as request-scoped in the container — has access to the current request
|
|
3134
|
+
* via RouterContext for features like `current()`, `full()`, and signed URLs.
|
|
3135
|
+
*
|
|
3136
|
+
* @example
|
|
3137
|
+
* ```typescript
|
|
3138
|
+
* // In a controller:
|
|
3139
|
+
* const uri = ctx.getContainer().resolve<Uri>(ROUTER_TOKENS.Uri)
|
|
3140
|
+
* uri.route('users.show', { id: '1' })
|
|
3141
|
+
* uri.current()
|
|
3142
|
+
* await uri.signedRoute('unsubscribe', { user: '1' }, { expiresIn: 3600 })
|
|
3143
|
+
*
|
|
3144
|
+
* // Set defaults (e.g., in middleware):
|
|
3145
|
+
* uri.defaults({ locale: 'en' })
|
|
3146
|
+
* uri.route('posts.index') // auto-fills :locale param
|
|
3147
|
+
* ```
|
|
3148
|
+
*/
|
|
3149
|
+
declare class Uri {
|
|
3150
|
+
private readonly registry;
|
|
3151
|
+
private readonly routerContext;
|
|
3152
|
+
private _defaults;
|
|
3153
|
+
constructor(registry: RouteRegistry, routerContext: RouterContext);
|
|
3154
|
+
/**
|
|
3155
|
+
* Set default URL parameters for this request.
|
|
3156
|
+
* Applied to all subsequent `route()` calls — explicit params override defaults.
|
|
3157
|
+
*
|
|
3158
|
+
* @param params - Default parameters (e.g., `{ locale: 'en' }`)
|
|
3159
|
+
*/
|
|
3160
|
+
defaults(params: Record<string, string>): void;
|
|
3161
|
+
/**
|
|
3162
|
+
* Generate a URL from a named route.
|
|
3163
|
+
*
|
|
3164
|
+
* Keys matching `:param` placeholders fill the path.
|
|
3165
|
+
* Domain params (`{tenant}`) are consumed from the same object.
|
|
3166
|
+
* Extra keys become query string parameters.
|
|
3167
|
+
* Default params (from `defaults()`) are merged — explicit params override.
|
|
3168
|
+
*
|
|
3169
|
+
* @param name - Named route identifier
|
|
3170
|
+
* @param params - Route params + domain params + extra query params
|
|
3171
|
+
* @param options - URL generation options
|
|
3172
|
+
* @returns Generated URL string
|
|
3173
|
+
*
|
|
3174
|
+
* @throws RouteNameNotFoundError if route name not found
|
|
3175
|
+
* @throws MissingRouteParamError if required params missing
|
|
3176
|
+
*/
|
|
3177
|
+
route<N extends RouteName>(name: N, params?: RouteParams<N>, options?: UriOptions): string;
|
|
3178
|
+
/**
|
|
3179
|
+
* Generate a signed URL from a named route.
|
|
3180
|
+
*
|
|
3181
|
+
* @param name - Named route identifier
|
|
3182
|
+
* @param params - Route params + domain params + extra query params
|
|
3183
|
+
* @param options - Signing options (e.g., expiresIn) and URL options
|
|
3184
|
+
* @returns Signed URL string with signature query param
|
|
3185
|
+
*
|
|
3186
|
+
* @throws Error if APP_SECRET environment variable is not set
|
|
3187
|
+
*/
|
|
3188
|
+
signedRoute<N extends RouteName>(name: N, params?: RouteParams<N>, options?: SignedUriOptions): Promise<string>;
|
|
3189
|
+
/**
|
|
3190
|
+
* Generate a temporary signed URL from a named route.
|
|
3191
|
+
*
|
|
3192
|
+
* @param name - Named route identifier
|
|
3193
|
+
* @param expiresIn - Time-to-live in seconds
|
|
3194
|
+
* @param params - Route params + domain params + extra query params
|
|
3195
|
+
* @param options - URL generation options
|
|
3196
|
+
* @returns Signed URL string with signature and expires query params
|
|
3197
|
+
*
|
|
3198
|
+
* @throws Error if APP_SECRET environment variable is not set
|
|
3199
|
+
*/
|
|
3200
|
+
temporarySignedRoute<N extends RouteName>(name: N, expiresIn: number, params?: RouteParams<N>, options?: UriOptions): Promise<string>;
|
|
3201
|
+
/**
|
|
3202
|
+
* Check if the current request has a valid signature.
|
|
3203
|
+
*
|
|
3204
|
+
* @returns true if the URL signature is valid and not expired
|
|
3205
|
+
*/
|
|
3206
|
+
hasValidSignature(): Promise<boolean>;
|
|
3207
|
+
/**
|
|
3208
|
+
* Get the current request URL pathname (without query string).
|
|
3209
|
+
*/
|
|
3210
|
+
current(): string;
|
|
3211
|
+
/**
|
|
3212
|
+
* Get the current request URL with query string (pathname + search).
|
|
3213
|
+
*/
|
|
3214
|
+
full(): string;
|
|
3215
|
+
/**
|
|
3216
|
+
* Get the previous request URL from the Referer header.
|
|
3217
|
+
*
|
|
3218
|
+
* @param fallback - URL to return if no Referer header (default: '/')
|
|
3219
|
+
*/
|
|
3220
|
+
previous(fallback?: string): string;
|
|
3221
|
+
/**
|
|
3222
|
+
* Get the previous request URL pathname (no query string or host) from the Referer header.
|
|
3223
|
+
*
|
|
3224
|
+
* @param fallback - Path to return if no Referer header (default: '/')
|
|
3225
|
+
*/
|
|
3226
|
+
previousPath(fallback?: string): string;
|
|
3227
|
+
/**
|
|
3228
|
+
* Build a URL to a raw path (not a named route) with optional query params.
|
|
3229
|
+
*
|
|
3230
|
+
* @param path - URL path (e.g., '/users')
|
|
3231
|
+
* @param queryParams - Query parameters to append
|
|
3232
|
+
* @param options - URL generation options
|
|
3233
|
+
*/
|
|
3234
|
+
to(path: string, queryParams?: Record<string, string>, options?: UriOptions): string;
|
|
3235
|
+
/**
|
|
3236
|
+
* Build a URL with query string parameters. Merges with existing query params in path.
|
|
3237
|
+
*
|
|
3238
|
+
* @param path - URL path, may already contain query params
|
|
3239
|
+
* @param queryParams - Query parameters to merge (new values override existing)
|
|
3240
|
+
*/
|
|
3241
|
+
query(path: string, queryParams: Record<string, string>): string;
|
|
3242
|
+
private getAppSecret;
|
|
3243
|
+
}
|
|
3244
|
+
//#endregion
|
|
3245
|
+
//#region src/router/router-context.d.ts
|
|
3246
|
+
type ContextQueryResult<R extends Record<string, unknown> | undefined, K extends string | undefined> = K extends string ? string : R extends undefined ? Record<string, unknown> : R;
|
|
3247
|
+
/**
|
|
3248
|
+
* Router context wrapper with helper methods
|
|
3249
|
+
*
|
|
3250
|
+
* Provides convenient access to Hono's context and common request/response operations.
|
|
3251
|
+
* The native Hono context is available via the `c` property for advanced use cases.
|
|
3252
|
+
*
|
|
3253
|
+
* @example
|
|
3254
|
+
* ```typescript
|
|
3255
|
+
* async index(ctx: RouterContext): Promise<Response> {
|
|
3256
|
+
* // Use helper methods
|
|
3257
|
+
* const users = await this.service.findAll()
|
|
3258
|
+
* return ctx.json(users)
|
|
3259
|
+
* }
|
|
3260
|
+
*
|
|
3261
|
+
* async show(ctx: RouterContext): Promise<Response> {
|
|
3262
|
+
* // Access route params
|
|
3263
|
+
* const id = ctx.param('id')
|
|
3264
|
+
* const user = await this.service.findById(id)
|
|
3265
|
+
* return ctx.json(user)
|
|
3266
|
+
* }
|
|
3267
|
+
*
|
|
3268
|
+
* async create(ctx: RouterContext): Promise<Response> {
|
|
3269
|
+
* // Parse request body
|
|
3270
|
+
* const body = await ctx.body<CreateUserInput>()
|
|
3271
|
+
* const user = await this.service.create(body)
|
|
3272
|
+
* return ctx.json(user, 201)
|
|
3273
|
+
* }
|
|
3274
|
+
* ```
|
|
3275
|
+
*/
|
|
3276
|
+
declare class RouterContext<T extends RouterEnv = RouterEnv> extends Macroable {
|
|
3277
|
+
readonly c: Context<T>;
|
|
3278
|
+
/**
|
|
3279
|
+
* Native Hono context
|
|
3280
|
+
* Access for advanced use cases not covered by helper methods
|
|
3281
|
+
*/
|
|
3282
|
+
constructor(c: Context<T>);
|
|
3283
|
+
/**
|
|
3284
|
+
* Get request-scoped DI container
|
|
3285
|
+
* Contains request-specific services and context (AuthContext)
|
|
3286
|
+
*
|
|
3287
|
+
* @throws Error if container not initialized
|
|
3288
|
+
*/
|
|
3289
|
+
getContainer(): Container;
|
|
3290
|
+
/**
|
|
3291
|
+
* Set locale for the current request
|
|
3292
|
+
*
|
|
3293
|
+
* @param locale - Locale code (e.g., 'en', 'fr')
|
|
3294
|
+
*/
|
|
3295
|
+
setLocale(locale: string): void;
|
|
3296
|
+
/**
|
|
3297
|
+
* Get locale for the current request
|
|
3298
|
+
*
|
|
3299
|
+
* @returns Current locale code
|
|
3300
|
+
*/
|
|
3301
|
+
getLocale(): string;
|
|
3302
|
+
/**
|
|
3303
|
+
* Return JSON response
|
|
3304
|
+
*
|
|
3305
|
+
* When data is null, automatically returns 204 No Content (configurable via status param).
|
|
3306
|
+
*
|
|
3307
|
+
* @param data - Data to serialize as JSON, or null for 204
|
|
3308
|
+
* @param status - HTTP status code (default: 200, or 204 when data is null)
|
|
3309
|
+
*/
|
|
3310
|
+
json(data: object | null, status?: ContentfulStatusCode): Response;
|
|
3311
|
+
/**
|
|
3312
|
+
* Get route parameter value
|
|
3313
|
+
*
|
|
3314
|
+
* @param key - Parameter name (e.g., 'id' for /users/:id)
|
|
3315
|
+
*/
|
|
3316
|
+
param(key: string): string;
|
|
3317
|
+
/**
|
|
3318
|
+
* Get query parameter value
|
|
3319
|
+
*
|
|
3320
|
+
* @param key - Query parameter name
|
|
3321
|
+
*/
|
|
3322
|
+
query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K>;
|
|
3323
|
+
/**
|
|
3324
|
+
* Get request header value
|
|
3325
|
+
*
|
|
3326
|
+
* @param name - Header name (case-insensitive)
|
|
3327
|
+
*/
|
|
3328
|
+
header(name: string): string | undefined;
|
|
3329
|
+
/**
|
|
3330
|
+
* Get validated request body from OpenAPI route
|
|
3331
|
+
* Returns pre-validated data that has passed schema validation
|
|
3332
|
+
*
|
|
3333
|
+
* @returns Validated JSON body
|
|
3334
|
+
*/
|
|
3335
|
+
body<T>(): Promise<T>;
|
|
3336
|
+
/**
|
|
3337
|
+
* Return text response
|
|
3338
|
+
*
|
|
3339
|
+
* @param text - Text content
|
|
3340
|
+
* @param status - HTTP status code (default: 200)
|
|
3341
|
+
*/
|
|
3342
|
+
text(text: string, status?: ContentfulStatusCode): Response;
|
|
3343
|
+
/**
|
|
3344
|
+
* Return HTML response
|
|
3345
|
+
*
|
|
3346
|
+
* @param html - HTML content
|
|
3347
|
+
* @param status - HTTP status code (default: 200)
|
|
3348
|
+
*/
|
|
3349
|
+
html(html: string, status?: ContentfulStatusCode): Response;
|
|
3350
|
+
/**
|
|
3351
|
+
* Generate a URL from a named route.
|
|
3352
|
+
*
|
|
3353
|
+
* Keys matching `:param` placeholders fill the path.
|
|
3354
|
+
* Domain params are consumed from the same object.
|
|
3355
|
+
* Extra keys become query string parameters.
|
|
3356
|
+
*
|
|
3357
|
+
* @param name - Named route identifier
|
|
3358
|
+
* @param params - Route params + domain params + extra query params
|
|
3359
|
+
* @param options - URL generation options (e.g., `{ absolute: true }`)
|
|
3360
|
+
*
|
|
3361
|
+
* @example
|
|
3362
|
+
* ```typescript
|
|
3363
|
+
* ctx.route('users.show', { id: '1' }) // '/v1/users/1'
|
|
3364
|
+
* ctx.route('users.show', { id: '1', q: 'test' }) // '/v1/users/1?q=test'
|
|
3365
|
+
* ```
|
|
3366
|
+
*/
|
|
3367
|
+
route<N extends RouteName>(name: N, params?: RouteParams<N>, options?: UriOptions): string;
|
|
3368
|
+
/**
|
|
3369
|
+
* Get a domain parameter value from the current request.
|
|
3370
|
+
* Domain params are set by the domain matching middleware.
|
|
3371
|
+
*
|
|
3372
|
+
* @param key - Domain parameter name (e.g., 'tenant' from '{tenant}.myapp.com')
|
|
3373
|
+
*
|
|
3374
|
+
* @example
|
|
3375
|
+
* ```typescript
|
|
3376
|
+
* const tenant = ctx.domain('tenant')
|
|
3377
|
+
* ```
|
|
3378
|
+
*/
|
|
3379
|
+
domain(key: string): string;
|
|
3380
|
+
/**
|
|
3381
|
+
* Generate a signed URL from a named route.
|
|
3382
|
+
*
|
|
3383
|
+
* @param name - Named route identifier
|
|
3384
|
+
* @param params - Route params (same as route())
|
|
3385
|
+
* @param options - Signing options (e.g., expiresIn) and URL options
|
|
3386
|
+
* @returns Signed URL string with signature query param
|
|
3387
|
+
*/
|
|
3388
|
+
signedUrl<N extends RouteName>(name: N, params?: RouteParams<N>, options?: SignedUriOptions): Promise<string>;
|
|
3389
|
+
/**
|
|
3390
|
+
* Check if the current request has a valid signature.
|
|
3391
|
+
*
|
|
3392
|
+
* @returns true if the URL signature is valid and not expired
|
|
3393
|
+
*/
|
|
3394
|
+
hasValidSignature(): Promise<boolean>;
|
|
3395
|
+
/**
|
|
3396
|
+
* Redirect to another URL
|
|
3397
|
+
*
|
|
3398
|
+
* @param url - Target URL
|
|
3399
|
+
* @param status - HTTP status code (default: 302)
|
|
3400
|
+
*/
|
|
3401
|
+
redirect(url: string, status?: RedirectStatusCode): Response;
|
|
3402
|
+
/**
|
|
3403
|
+
* Return a streaming response (binary/generic)
|
|
3404
|
+
*
|
|
3405
|
+
* @param callback - Async function that writes to the stream
|
|
3406
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
3407
|
+
*/
|
|
3408
|
+
stream(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response;
|
|
3409
|
+
/**
|
|
3410
|
+
* Return a streaming text response
|
|
3411
|
+
*
|
|
3412
|
+
* Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
|
|
3413
|
+
*
|
|
3414
|
+
* @param callback - Async function that writes text to the stream
|
|
3415
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
3416
|
+
*/
|
|
3417
|
+
streamText(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response;
|
|
3418
|
+
/**
|
|
3419
|
+
* Return a Server-Sent Events (SSE) streaming response
|
|
3420
|
+
*
|
|
3421
|
+
* Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
|
|
3422
|
+
*
|
|
3423
|
+
* @param callback - Async function that writes SSE events to the stream
|
|
3424
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
3425
|
+
*/
|
|
3426
|
+
streamSSE(callback: (stream: SSEStreamingApi) => Promise<void>, onError?: (err: Error, stream: SSEStreamingApi) => Promise<void>): Response;
|
|
3427
|
+
private resolveUri;
|
|
3428
|
+
}
|
|
3429
|
+
//#endregion
|
|
3430
|
+
//#region src/errors/exception-context.d.ts
|
|
3431
|
+
/**
|
|
3432
|
+
* Exception context for errors occurring during HTTP request handling.
|
|
3433
|
+
*
|
|
3434
|
+
* Provides access to the full {@link RouterContext} for building responses
|
|
3435
|
+
* with `ctx.json()`, `ctx.text()`, `ctx.html()`, etc.
|
|
3436
|
+
*/
|
|
3437
|
+
interface HttpExceptionContext {
|
|
3438
|
+
readonly type: 'http';
|
|
3439
|
+
/** Stratal RouterContext — use for building HTTP responses */
|
|
3440
|
+
readonly ctx: RouterContext;
|
|
3441
|
+
}
|
|
3442
|
+
/**
|
|
3443
|
+
* Exception context for errors occurring during queue message processing.
|
|
3444
|
+
*/
|
|
3445
|
+
interface QueueExceptionContext {
|
|
3446
|
+
readonly type: 'queue';
|
|
3447
|
+
/** Name of the queue being processed */
|
|
3448
|
+
readonly queueName: string;
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Exception context for errors occurring during scheduled cron execution.
|
|
3452
|
+
*/
|
|
3453
|
+
interface CronExceptionContext {
|
|
3454
|
+
readonly type: 'cron';
|
|
3455
|
+
}
|
|
3456
|
+
/**
|
|
3457
|
+
* Exception context for errors occurring during CLI command execution.
|
|
3458
|
+
*/
|
|
3459
|
+
interface CliExceptionContext {
|
|
3460
|
+
readonly type: 'cli';
|
|
3461
|
+
/** Name of the command that threw */
|
|
3462
|
+
readonly commandName: string;
|
|
3463
|
+
}
|
|
3464
|
+
/**
|
|
3465
|
+
* Discriminated union of all exception context types.
|
|
3466
|
+
*
|
|
3467
|
+
* Narrow via `ctx.type` to access context-specific properties:
|
|
3468
|
+
*
|
|
3469
|
+
* @example
|
|
3470
|
+
* ```typescript
|
|
3471
|
+
* handler.renderable(MyError, (error, ctx) => {
|
|
3472
|
+
* if (ctx.type === 'http') {
|
|
3473
|
+
* return ctx.ctx.json({ message: 'Something went wrong' }, 500)
|
|
3474
|
+
* }
|
|
3475
|
+
* // Non-HTTP contexts: return undefined to use default rendering
|
|
3476
|
+
* })
|
|
3477
|
+
* ```
|
|
3478
|
+
*/
|
|
3479
|
+
type ExceptionContext = HttpExceptionContext | QueueExceptionContext | CronExceptionContext | CliExceptionContext;
|
|
3480
|
+
/**
|
|
3481
|
+
* Create an HTTP exception context from a Hono context.
|
|
3482
|
+
*
|
|
3483
|
+
* @param c - The raw Hono context from the request
|
|
3484
|
+
* @returns An {@link HttpExceptionContext} wrapping a RouterContext
|
|
3485
|
+
*/
|
|
3486
|
+
declare function createHttpExceptionContext(c: Context<RouterEnv>): HttpExceptionContext;
|
|
3487
|
+
/**
|
|
3488
|
+
* Create a queue exception context.
|
|
3489
|
+
*
|
|
3490
|
+
* @param queueName - The name of the queue being processed
|
|
3491
|
+
* @returns A {@link QueueExceptionContext}
|
|
3492
|
+
*/
|
|
3493
|
+
declare function createQueueExceptionContext(queueName: string): QueueExceptionContext;
|
|
3494
|
+
/**
|
|
3495
|
+
* Create a cron exception context.
|
|
3496
|
+
*
|
|
3497
|
+
* @returns A {@link CronExceptionContext}
|
|
3498
|
+
*/
|
|
3499
|
+
declare function createCronExceptionContext(): CronExceptionContext;
|
|
3500
|
+
/**
|
|
3501
|
+
* Create a CLI command exception context.
|
|
3502
|
+
*
|
|
3503
|
+
* @param commandName - The name of the command that threw
|
|
3504
|
+
* @returns A {@link CliExceptionContext}
|
|
3505
|
+
*/
|
|
3506
|
+
declare function createCliExceptionContext(commandName: string): CliExceptionContext;
|
|
3507
|
+
//#endregion
|
|
3508
|
+
//#region src/i18n/errors/locale-not-supported.error.d.ts
|
|
3509
|
+
declare class LocaleNotSupportedError extends ApplicationError {
|
|
3510
|
+
constructor(locale: string, supportedLocales: string[]);
|
|
3511
|
+
}
|
|
3512
|
+
//#endregion
|
|
3513
|
+
//#region src/i18n/errors/translation-missing.error.d.ts
|
|
3514
|
+
declare class TranslationMissingError extends ApplicationError {
|
|
3515
|
+
constructor(key: string, locale: string);
|
|
3516
|
+
}
|
|
3517
|
+
//#endregion
|
|
3518
|
+
//#region src/i18n/i18n.options.d.ts
|
|
3519
|
+
/**
|
|
3520
|
+
* Detection strategy for locale resolution
|
|
3521
|
+
*
|
|
3522
|
+
* - `'cookie'` — reads from the `locale` cookie (default)
|
|
3523
|
+
* - `'header'` — reads from the `Accept-Language` header
|
|
3524
|
+
* - `'querystring'` — reads from the `?locale=` query parameter
|
|
3525
|
+
* - `'path'` — reads from the first URL path segment (e.g., `/en/api/users`)
|
|
3526
|
+
*/
|
|
3527
|
+
type DetectionStrategy = 'cookie' | 'header' | 'querystring' | 'path';
|
|
3528
|
+
interface BaseDetection {
|
|
3529
|
+
/** Set to false to disable language detection entirely. @default true */
|
|
3530
|
+
enabled?: boolean;
|
|
3531
|
+
}
|
|
3532
|
+
/**
|
|
3533
|
+
* Language detection options (discriminated by strategy)
|
|
3534
|
+
*
|
|
3535
|
+
* @example Cookie detection (default)
|
|
3536
|
+
* ```typescript
|
|
3537
|
+
* { strategy: 'cookie' }
|
|
3538
|
+
* ```
|
|
3539
|
+
*
|
|
3540
|
+
* @example Header detection
|
|
3541
|
+
* ```typescript
|
|
3542
|
+
* { strategy: 'header' }
|
|
3543
|
+
* ```
|
|
3544
|
+
*
|
|
3545
|
+
* @example Path detection
|
|
3546
|
+
* ```typescript
|
|
3547
|
+
* { strategy: 'path' }
|
|
3548
|
+
* ```
|
|
3549
|
+
*
|
|
3550
|
+
* @example Disable detection
|
|
3551
|
+
* ```typescript
|
|
3552
|
+
* { enabled: false }
|
|
3553
|
+
* ```
|
|
3554
|
+
*/
|
|
3555
|
+
type LanguageDetectionOptions = (BaseDetection & {
|
|
3556
|
+
strategy?: 'cookie';
|
|
3557
|
+
cookieOptions?: DetectorOptions['cookieOptions'];
|
|
3558
|
+
}) | (BaseDetection & {
|
|
3559
|
+
strategy: 'header';
|
|
3560
|
+
}) | (BaseDetection & {
|
|
3561
|
+
strategy: 'querystring';
|
|
3562
|
+
}) | (BaseDetection & {
|
|
3563
|
+
strategy: 'path';
|
|
3564
|
+
/**
|
|
3565
|
+
* Controls whether the default locale gets a URL path prefix.
|
|
3566
|
+
*
|
|
3567
|
+
* - `false` (default) — The default locale has no prefix (`/users`), other locales
|
|
3568
|
+
* are prefixed (`/fr/users`). Requests to the prefixed default locale (`/en/users`) return 404.
|
|
3569
|
+
* - `'redirect'` — Same as `false`, but requests to the prefixed default locale
|
|
3570
|
+
* (`/en/users`) are 301-redirected to the unprefixed path (`/users`).
|
|
3571
|
+
* - `true` — All locales are prefixed (`/en/users`, `/fr/users`).
|
|
3572
|
+
*
|
|
3573
|
+
* @default false
|
|
3574
|
+
*/
|
|
3575
|
+
prefixDefaultLocale?: false | true | 'redirect';
|
|
3576
|
+
}) | {
|
|
3577
|
+
enabled: false;
|
|
3578
|
+
};
|
|
3579
|
+
/**
|
|
3580
|
+
* Options for configuring the I18n module
|
|
3581
|
+
*
|
|
3582
|
+
* @example
|
|
3583
|
+
* ```typescript
|
|
3584
|
+
* I18nModule.forRoot({
|
|
3585
|
+
* defaultLocale: 'en',
|
|
3586
|
+
* fallbackLocale: 'en',
|
|
3587
|
+
* locales: ['en', 'fr'],
|
|
3588
|
+
* detection: { strategy: 'header' },
|
|
3589
|
+
* })
|
|
3590
|
+
* ```
|
|
3591
|
+
*/
|
|
3592
|
+
interface I18nModuleOptions {
|
|
3593
|
+
/**
|
|
3594
|
+
* Default locale for the application
|
|
3595
|
+
* @default 'en'
|
|
3596
|
+
*/
|
|
3597
|
+
defaultLocale?: string;
|
|
3598
|
+
/**
|
|
3599
|
+
* Fallback locale when translation is missing
|
|
3600
|
+
* @default 'en'
|
|
3601
|
+
*/
|
|
3602
|
+
fallbackLocale?: string;
|
|
3603
|
+
/**
|
|
3604
|
+
* List of supported locales
|
|
3605
|
+
* Request locales not in this list will fall back to defaultLocale
|
|
3606
|
+
*/
|
|
3607
|
+
locales?: string[];
|
|
3608
|
+
/**
|
|
3609
|
+
* Language detection configuration
|
|
3610
|
+
* Controls how the locale is extracted from incoming requests
|
|
3611
|
+
*/
|
|
3612
|
+
detection?: LanguageDetectionOptions;
|
|
3613
|
+
}
|
|
3614
|
+
/**
|
|
3615
|
+
* Resolved options with all defaults applied
|
|
3616
|
+
* Used internally by I18n services
|
|
3617
|
+
*/
|
|
3618
|
+
interface ResolvedI18nOptions {
|
|
3619
|
+
defaultLocale: string;
|
|
3620
|
+
fallbackLocale: string;
|
|
3621
|
+
locales: string[];
|
|
3622
|
+
detection: {
|
|
3623
|
+
enabled: boolean;
|
|
3624
|
+
strategy: DetectionStrategy; /** Resolved value of the path detection `prefixDefaultLocale` option. Only meaningful when `strategy` is `'path'`. */
|
|
3625
|
+
prefixDefaultLocale: false | true | 'redirect';
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
/**
|
|
3629
|
+
* Resolve I18n options with defaults
|
|
3630
|
+
*/
|
|
3631
|
+
declare function resolveI18nOptions(options?: I18nModuleOptions): ResolvedI18nOptions;
|
|
3632
|
+
/**
|
|
3633
|
+
* Build Hono languageDetector options from I18n module options
|
|
3634
|
+
*/
|
|
3635
|
+
declare function buildDetectorOptions(options?: I18nModuleOptions): Partial<DetectorOptions>;
|
|
3636
|
+
//#endregion
|
|
3637
|
+
//#region src/i18n/i18n.module.d.ts
|
|
3638
|
+
declare class I18nModule implements RouteConfigurable {
|
|
3639
|
+
/**
|
|
3640
|
+
* Configure I18n locale settings
|
|
3641
|
+
*
|
|
3642
|
+
* Call once in the root module. Does not accept messages —
|
|
3643
|
+
* use `registerMessages()` to add translations.
|
|
3644
|
+
*
|
|
3645
|
+
* @param options - Locale configuration (defaultLocale, fallbackLocale, locales)
|
|
3646
|
+
*/
|
|
3647
|
+
static forRoot(options?: I18nModuleOptions): DynamicModule;
|
|
3648
|
+
/**
|
|
3649
|
+
* Register i18n messages
|
|
3650
|
+
*
|
|
3651
|
+
* Can be called from any module, as many times as needed.
|
|
3652
|
+
* Messages are deep-merged in registration order — later calls override earlier ones at leaf level.
|
|
3653
|
+
*
|
|
3654
|
+
* @param messages - Messages keyed by locale code
|
|
3655
|
+
*
|
|
3656
|
+
* @example App-level messages
|
|
3657
|
+
* ```typescript
|
|
3658
|
+
* I18nModule.registerMessages({
|
|
3659
|
+
* en: { common: { hello: 'Hello' }, errors: { notFound: 'Not found' } },
|
|
3660
|
+
* fr: { common: { hello: 'Bonjour' }, errors: { notFound: 'Introuvable' } },
|
|
3661
|
+
* })
|
|
3662
|
+
* ```
|
|
3663
|
+
*
|
|
3664
|
+
* @example Package-level messages
|
|
3665
|
+
* ```typescript
|
|
3666
|
+
* I18nModule.registerMessages({
|
|
3667
|
+
* en: { tenancy: { tenantNotFound: 'Tenant not found' } },
|
|
3668
|
+
* })
|
|
3669
|
+
* ```
|
|
3670
|
+
*/
|
|
3671
|
+
static registerMessages(messages: Record<string, Record<string, unknown>>): DynamicModule;
|
|
3672
|
+
configureRoutes(router: Router): void;
|
|
3673
|
+
}
|
|
3674
|
+
//#endregion
|
|
3675
|
+
//#region src/i18n/i18n.tokens.d.ts
|
|
3676
|
+
/**
|
|
3677
|
+
* I18n Module DI Tokens
|
|
3678
|
+
* Symbol-based tokens to avoid string collisions
|
|
3679
|
+
*/
|
|
3680
|
+
declare const I18N_TOKENS: {
|
|
3681
|
+
/** MessageLoaderService - loads and caches locale messages */readonly MessageLoader: symbol; /** I18nService - request-scoped translation service */
|
|
3682
|
+
readonly I18nService: symbol; /** I18nModuleOptions - configuration options from forRoot() */
|
|
3683
|
+
readonly Options: symbol; /** MessageRegistry - singleton accumulator for registerMessages() contributions */
|
|
3684
|
+
readonly MessageRegistry: symbol;
|
|
3685
|
+
};
|
|
3686
|
+
//#endregion
|
|
3687
|
+
//#region src/i18n/messages/index.d.ts
|
|
3688
|
+
/**
|
|
3689
|
+
* All locale messages
|
|
3690
|
+
* Explicitly import and export (no filesystem scanning - Cloudflare Workers compatible)
|
|
3691
|
+
*/
|
|
3692
|
+
declare const messages: {
|
|
3693
|
+
readonly en: typeof index_d_exports;
|
|
3694
|
+
};
|
|
3695
|
+
/**
|
|
3696
|
+
* Type for all messages
|
|
3697
|
+
*/
|
|
3698
|
+
type Messages = typeof messages;
|
|
3699
|
+
/**
|
|
3700
|
+
* Get messages for all locales
|
|
3701
|
+
*/
|
|
3702
|
+
declare function getMessages(): Record<string, Record<string, unknown>>;
|
|
3703
|
+
/**
|
|
3704
|
+
* Get available locales
|
|
3705
|
+
*/
|
|
3706
|
+
declare function getLocales(): string[];
|
|
3707
|
+
//#endregion
|
|
3708
|
+
//#region src/i18n/services/message-registry.d.ts
|
|
3709
|
+
/**
|
|
3710
|
+
* Message Registry
|
|
3711
|
+
*
|
|
3712
|
+
* Accumulates i18n messages from multiple `I18nModule.registerMessages()` calls.
|
|
3713
|
+
* Messages are collected statically (at module import time) and deep-merged
|
|
3714
|
+
* when `getMergedMessages()` is called by `MessageLoaderService`.
|
|
3715
|
+
*
|
|
3716
|
+
* Later registrations override earlier ones at leaf level.
|
|
3717
|
+
*/
|
|
3718
|
+
declare class MessageRegistry {
|
|
3719
|
+
/**
|
|
3720
|
+
* Add messages (called statically by I18nModule.registerMessages)
|
|
3721
|
+
*/
|
|
3722
|
+
static addMessages(messages: Record<string, Record<string, unknown>>): void;
|
|
3723
|
+
/**
|
|
3724
|
+
* Get all messages deep-merged in registration order
|
|
3725
|
+
*/
|
|
3726
|
+
getMergedMessages(): Record<string, Record<string, unknown>>;
|
|
3727
|
+
/**
|
|
3728
|
+
* Reset registry (for testing)
|
|
3729
|
+
* @internal
|
|
3730
|
+
*/
|
|
3731
|
+
static reset(): void;
|
|
3732
|
+
}
|
|
3733
|
+
//#endregion
|
|
3734
|
+
//#region src/i18n/services/message-loader.service.d.ts
|
|
3735
|
+
declare class MessageLoaderService {
|
|
3736
|
+
private readonly registry;
|
|
3737
|
+
private readonly options?;
|
|
3738
|
+
private readonly cache;
|
|
3739
|
+
private readonly contextCache;
|
|
3740
|
+
private readonly locales;
|
|
3741
|
+
private readonly defaultLocale;
|
|
3742
|
+
constructor(registry: MessageRegistry, options?: I18nModuleOptions | undefined);
|
|
3743
|
+
/**
|
|
3744
|
+
* Get CoreContext for a locale (lazily built and cached on first access)
|
|
3745
|
+
* Falls back to default locale if locale not found
|
|
3746
|
+
*/
|
|
3747
|
+
getCoreContext(locale: string): CoreContext;
|
|
3748
|
+
/**
|
|
3749
|
+
* Get messages for a specific locale.
|
|
3750
|
+
* Falls back to default locale if not found.
|
|
3751
|
+
*/
|
|
3752
|
+
getMessages(locale: string): Record<string, unknown>;
|
|
3753
|
+
/** Get list of available locale codes */
|
|
3754
|
+
getAvailableLocales(): string[];
|
|
3755
|
+
/** Check if a locale is supported */
|
|
3756
|
+
isLocaleSupported(locale: string): boolean;
|
|
3757
|
+
/** Get default locale */
|
|
3758
|
+
getDefaultLocale(): string;
|
|
3759
|
+
/**
|
|
3760
|
+
* Get flattened (dot-notation) messages for a locale, optionally filtered by namespace prefixes.
|
|
3761
|
+
*
|
|
3762
|
+
* Returns flat key-value pairs matching the format used by `@intlify/core-base`'s
|
|
3763
|
+
* `createCoreContext`. Requires `registerMessageCompiler(compile)` to be called
|
|
3764
|
+
* before `translate()` can resolve these flat keys.
|
|
3765
|
+
*
|
|
3766
|
+
* @param locale - Locale code (falls back to default locale if not found)
|
|
3767
|
+
* @param options - Optional filter configuration
|
|
3768
|
+
* @param options.only - Dot-notation prefixes to include (e.g., `['common', 'nav.sidebar']`)
|
|
3769
|
+
* @returns Flattened messages as `{ 'key.path': 'translated value' }`
|
|
3770
|
+
*
|
|
3771
|
+
* @example
|
|
3772
|
+
* ```typescript
|
|
3773
|
+
* // All messages for the locale
|
|
3774
|
+
* loader.getFilteredMessages('en')
|
|
3775
|
+
*
|
|
3776
|
+
* // Only 'common' and 'nav' namespaces
|
|
3777
|
+
* loader.getFilteredMessages('en', { only: ['common', 'nav'] })
|
|
3778
|
+
*
|
|
3779
|
+
* // Deeply nested prefix
|
|
3780
|
+
* loader.getFilteredMessages('en', { only: ['common.actions'] })
|
|
3781
|
+
* ```
|
|
3782
|
+
*/
|
|
3783
|
+
getFilteredMessages(locale: string, options?: {
|
|
3784
|
+
only?: MessageKeyPrefix[];
|
|
3785
|
+
}): Record<string, string>;
|
|
3786
|
+
/**
|
|
3787
|
+
* Flatten nested messages to dot-notation.
|
|
3788
|
+
* e.g. `{ a: { b: 'hello' } }` → `{ 'a.b': 'hello' }`
|
|
3789
|
+
*/
|
|
3790
|
+
private flattenMessages;
|
|
3791
|
+
}
|
|
3792
|
+
//#endregion
|
|
3793
|
+
//#region src/i18n/services/i18n.service.d.ts
|
|
3794
|
+
/**
|
|
3795
|
+
* I18n Service
|
|
3796
|
+
*
|
|
3797
|
+
* Provides internationalization (i18n) support for the application.
|
|
3798
|
+
* Injects RouterContext to access request-specific locale.
|
|
3799
|
+
*
|
|
3800
|
+
* @example Usage in services
|
|
3801
|
+
* ```typescript
|
|
3802
|
+
* @Transient(MY_TOKENS.UserService)
|
|
3803
|
+
* export class UserService {
|
|
3804
|
+
* constructor(
|
|
3805
|
+
* @inject(I18N_TOKENS.I18nService) private readonly i18n: II18nService
|
|
3806
|
+
* ) {}
|
|
3807
|
+
*
|
|
3808
|
+
* getWelcomeMessage(): string {
|
|
3809
|
+
* return this.i18n.t('common.welcome')
|
|
3810
|
+
* }
|
|
3811
|
+
* }
|
|
3812
|
+
* ```
|
|
3813
|
+
*/
|
|
3814
|
+
declare class I18nService implements II18nService {
|
|
3815
|
+
private readonly loader;
|
|
3816
|
+
private readonly routerContext?;
|
|
3817
|
+
constructor(loader: MessageLoaderService, routerContext?: RouterContext | undefined);
|
|
3818
|
+
/**
|
|
3819
|
+
* Translate a message key
|
|
3820
|
+
*
|
|
3821
|
+
* @param key - Message key (e.g., 'common.actions.save')
|
|
3822
|
+
* @param params - Optional parameters for interpolation
|
|
3823
|
+
* @returns Translated string
|
|
3824
|
+
*/
|
|
3825
|
+
t(key: MessageKeys, params?: MessageParams): string;
|
|
3826
|
+
/**
|
|
3827
|
+
* Get current locale
|
|
3828
|
+
*
|
|
3829
|
+
* @returns Current locale code from RouterContext or default locale
|
|
3830
|
+
*/
|
|
3831
|
+
getLocale(): string;
|
|
3832
|
+
}
|
|
3833
|
+
//#endregion
|
|
3834
|
+
//#region src/i18n/middleware/i18n-context.middleware.d.ts
|
|
3835
|
+
declare class I18nContextMiddleware implements Middleware {
|
|
3836
|
+
private readonly i18n;
|
|
3837
|
+
constructor(i18n: I18nService);
|
|
3838
|
+
handle(ctx: RouterContext, next: Next$1): Promise<void>;
|
|
3839
|
+
}
|
|
3840
|
+
//#endregion
|
|
3841
|
+
//#region src/errors/error-codes.d.ts
|
|
3842
|
+
/**
|
|
3843
|
+
* Centralized Error Code Registry
|
|
3844
|
+
*
|
|
3845
|
+
* Error codes are organized by category with specific ranges:
|
|
3846
|
+
* - 1000-1999: Validation errors
|
|
3847
|
+
* - 2000-2999: Database errors (generic)
|
|
3848
|
+
* - 3000-3999: Authentication & Authorization
|
|
3849
|
+
* - 4000-4999: Resource errors
|
|
3850
|
+
* - 5000-5999: Domain-specific business logic (per module)
|
|
3851
|
+
* - 9000-9999: System/Internal errors
|
|
3852
|
+
* - 9000-9099: Router errors
|
|
3853
|
+
* - 9100-9199: Configuration errors
|
|
3854
|
+
* - 9200-9299: Infrastructure errors
|
|
3855
|
+
* - 9300-9399: I18n errors
|
|
3856
|
+
*/
|
|
3857
|
+
declare const ERROR_CODES: {
|
|
3858
|
+
/**
|
|
3859
|
+
* Database Errors (2000-2999)
|
|
3860
|
+
* Generic database errors thrown by Prisma client extensions
|
|
3861
|
+
*/
|
|
3862
|
+
readonly DATABASE: {
|
|
3863
|
+
/** Generic database error */readonly GENERIC: 2000; /** Record not found in database */
|
|
3864
|
+
readonly RECORD_NOT_FOUND: 2001; /** Unique constraint violation */
|
|
3865
|
+
readonly UNIQUE_CONSTRAINT: 2002; /** Foreign key constraint violation */
|
|
3866
|
+
readonly FOREIGN_KEY_CONSTRAINT: 2003; /** Database connection failed */
|
|
3867
|
+
readonly CONNECTION_FAILED: 2004; /** Database timeout */
|
|
3868
|
+
readonly TIMEOUT: 2005; /** Null constraint violation */
|
|
3869
|
+
readonly NULL_CONSTRAINT: 2006; /** Too many database connections */
|
|
3870
|
+
readonly TOO_MANY_CONNECTIONS: 2007; /** Transaction conflict or deadlock */
|
|
3871
|
+
readonly TRANSACTION_CONFLICT: 2008;
|
|
3872
|
+
};
|
|
3873
|
+
/**
|
|
3874
|
+
* Authentication Errors (3000-3099)
|
|
3875
|
+
* Authentication-related failures
|
|
3876
|
+
*/
|
|
3877
|
+
readonly AUTH: {
|
|
3878
|
+
/** Invalid credentials provided */readonly INVALID_CREDENTIALS: 3000; /** Session expired or invalid */
|
|
3879
|
+
readonly SESSION_EXPIRED: 3001; /** Account locked or disabled */
|
|
3880
|
+
readonly ACCOUNT_LOCKED: 3002; /** Invalid or expired token */
|
|
3881
|
+
readonly INVALID_TOKEN: 3003; /** Context not initialized */
|
|
3882
|
+
readonly CONTEXT_NOT_INITIALIZED: 3004; /** User not authenticated */
|
|
3883
|
+
readonly USER_NOT_AUTHENTICATED: 3005; /** Email verification required before login */
|
|
3884
|
+
readonly EMAIL_NOT_VERIFIED: 3007; /** Password doesn't meet minimum length */
|
|
3885
|
+
readonly PASSWORD_TOO_SHORT: 3008; /** Password exceeds maximum length */
|
|
3886
|
+
readonly PASSWORD_TOO_LONG: 3009; /** Account with email already exists */
|
|
3887
|
+
readonly ACCOUNT_ALREADY_EXISTS: 3010; /** User creation failed */
|
|
3888
|
+
readonly FAILED_TO_CREATE_USER: 3011; /** Session creation failed */
|
|
3889
|
+
readonly FAILED_TO_CREATE_SESSION: 3012; /** User update failed */
|
|
3890
|
+
readonly FAILED_TO_UPDATE_USER: 3013; /** Social account already linked */
|
|
3891
|
+
readonly SOCIAL_ACCOUNT_LINKED: 3014; /** Last account cannot be unlinked */
|
|
3892
|
+
readonly CANNOT_UNLINK_LAST_ACCOUNT: 3015; /** Organization not found */
|
|
3893
|
+
readonly ORGANIZATION_NOT_FOUND: 3020; /** Organization member not found */
|
|
3894
|
+
readonly MEMBER_NOT_FOUND: 3021; /** Organization invitation not found */
|
|
3895
|
+
readonly INVITATION_NOT_FOUND: 3022; /** Invitation recipient mismatch */
|
|
3896
|
+
readonly INVITATION_RECIPIENT_MISMATCH: 3023; /** Organization limit reached */
|
|
3897
|
+
readonly ORGANIZATION_LIMIT_REACHED: 3024; /** Organization membership constraint violation */
|
|
3898
|
+
readonly ORGANIZATION_MEMBERSHIP_REQUIRED: 3025;
|
|
3899
|
+
};
|
|
3900
|
+
/**
|
|
3901
|
+
* Authorization Errors (3100-3199)
|
|
3902
|
+
* Permission and access control failures
|
|
3903
|
+
*/
|
|
3904
|
+
readonly AUTHZ: {
|
|
3905
|
+
/** Insufficient permissions */readonly FORBIDDEN: 3100; /** Resource access denied */
|
|
3906
|
+
readonly ACCESS_DENIED: 3101; /** User lacks required role */
|
|
3907
|
+
readonly INSUFFICIENT_PERMISSIONS: 3102;
|
|
3908
|
+
};
|
|
3909
|
+
/**
|
|
3910
|
+
* Resource Errors (4000-4999)
|
|
3911
|
+
* Generic resource-related errors
|
|
3912
|
+
*/
|
|
3913
|
+
readonly RESOURCE: {
|
|
3914
|
+
/** Generic resource not found */readonly NOT_FOUND: 4000; /** Route/endpoint not found */
|
|
3915
|
+
readonly ROUTE_NOT_FOUND: 4004; /** Resource conflict or duplicate */
|
|
3916
|
+
readonly CONFLICT: 4100; /** Resource already exists */
|
|
3917
|
+
readonly ALREADY_EXISTS: 4101;
|
|
3918
|
+
};
|
|
3919
|
+
/**
|
|
3920
|
+
* Validation Errors (1000-1999)
|
|
3921
|
+
* Input validation failures
|
|
3922
|
+
*/
|
|
3923
|
+
readonly VALIDATION: {
|
|
3924
|
+
/** Generic validation error */readonly GENERIC: 1000; /** Required field missing */
|
|
3925
|
+
readonly REQUIRED_FIELD: 1001; /** Invalid format */
|
|
3926
|
+
readonly INVALID_FORMAT: 1002; /** Schema validation failed */
|
|
3927
|
+
readonly SCHEMA_VALIDATION: 1003; /** Request validation failed (OpenAPI, etc.) */
|
|
3928
|
+
readonly REQUEST_VALIDATION: 1004; /** Response validation failed (response body doesn't match declared schema) */
|
|
3929
|
+
readonly RESPONSE_VALIDATION: 1005;
|
|
3930
|
+
};
|
|
3931
|
+
/**
|
|
3932
|
+
* Router Errors (9000-9099)
|
|
3933
|
+
* Router and controller-related INTERNAL errors
|
|
3934
|
+
*/
|
|
3935
|
+
readonly ROUTER: {
|
|
3936
|
+
/** Controller registration error */readonly CONTROLLER_REGISTRATION_ERROR: 9005; /** Controller method not found */
|
|
3937
|
+
readonly CONTROLLER_METHOD_NOT_FOUND: 9006; /** OpenAPI route registration failed */
|
|
3938
|
+
readonly OPENAPI_ROUTE_REGISTRATION: 9008; /** Duplicate route name in RouteRegistry */
|
|
3939
|
+
readonly DUPLICATE_ROUTE_NAME: 9010; /** Named route not found in RouteRegistry */
|
|
3940
|
+
readonly ROUTE_NAME_NOT_FOUND: 9011; /** Required route parameter missing during URL generation */
|
|
3941
|
+
readonly MISSING_ROUTE_PARAM: 9012; /** router.use() called inside group() callback */
|
|
3942
|
+
readonly USE_SCOPE_VIOLATION: 9013; /** next() called more than once in a middleware */
|
|
3943
|
+
readonly MIDDLEWARE_NEXT_CALLED_MULTIPLE_TIMES: 9014;
|
|
3944
|
+
};
|
|
3945
|
+
/**
|
|
3946
|
+
* I18n Errors (9300-9399)
|
|
3947
|
+
* Internationalization and localization errors
|
|
3948
|
+
*/
|
|
3949
|
+
readonly I18N: {
|
|
3950
|
+
/** Translation key missing from all locales */readonly TRANSLATION_MISSING: 9300; /** Requested locale not supported */
|
|
3951
|
+
readonly LOCALE_NOT_SUPPORTED: 9301;
|
|
3952
|
+
};
|
|
3953
|
+
/**
|
|
3954
|
+
* System Errors (9000-9999)
|
|
3955
|
+
* Internal system errors and unexpected failures
|
|
3956
|
+
*/
|
|
3957
|
+
readonly SYSTEM: {
|
|
3958
|
+
/** Internal server error */readonly INTERNAL_ERROR: 9000; /** Generic configuration error */
|
|
3959
|
+
readonly CONFIGURATION_ERROR: 9100; /** ConfigService not initialized */
|
|
3960
|
+
readonly CONFIG_NOT_INITIALIZED: 9101; /** Module already registered */
|
|
3961
|
+
readonly MODULE_ALREADY_REGISTERED: 9102; /** Circular module dependency detected */
|
|
3962
|
+
readonly MODULE_CIRCULAR_DEPENDENCY: 9103; /** Module dependency not found */
|
|
3963
|
+
readonly MODULE_DEPENDENCY_NOT_FOUND: 9104; /** Invalid error code range */
|
|
3964
|
+
readonly INVALID_ERROR_CODE_RANGE: 9105; /** Invalid module provider configuration */
|
|
3965
|
+
readonly INVALID_MODULE_PROVIDER: 9106; /** ConfigModule.forRoot() was not called */
|
|
3966
|
+
readonly CONFIG_MODULE_NOT_INITIALIZED: 9107; /** Generic infrastructure error */
|
|
3967
|
+
readonly INFRASTRUCTURE_ERROR: 9200; /** Execution context not initialized */
|
|
3968
|
+
readonly EXECUTION_CONTEXT_NOT_INITIALIZED: 9201; /** Request container not initialized */
|
|
3969
|
+
readonly REQUEST_CONTAINER_NOT_INITIALIZED: 9202; /** Queue binding not found */
|
|
3970
|
+
readonly QUEUE_BINDING_NOT_FOUND: 9203; /** Cron job execution failed */
|
|
3971
|
+
readonly CRON_EXECUTION_FAILED: 9204; /** Queue provider not supported */
|
|
3972
|
+
readonly QUEUE_PROVIDER_NOT_SUPPORTED: 9205; /** body() called on WebSocket gateway context */
|
|
3973
|
+
readonly WEBSOCKET_BODY_NOT_AVAILABLE: 9206; /** Duplicate WebSocket event decorator on a gateway */
|
|
3974
|
+
readonly WEBSOCKET_DUPLICATE_EVENT_HANDLER: 9207; /** Seeder name collision — two seeders share the same class name */
|
|
3975
|
+
readonly SEEDER_NAME_COLLISION: 9208; /** Seeder not registered in the SeederRegistry */
|
|
3976
|
+
readonly SEEDER_NOT_REGISTERED: 9209; /** Application container not initialized (AsyncLocalStorage) */
|
|
3977
|
+
readonly CONTAINER_NOT_INITIALIZED: 9210; /** Required environment variable not set */
|
|
3978
|
+
readonly MISSING_ENVIRONMENT_VARIABLE: 9211;
|
|
3979
|
+
};
|
|
3980
|
+
};
|
|
3981
|
+
/**
|
|
3982
|
+
* Recursively extract all leaf values from a nested object type
|
|
3983
|
+
* Similar to DeepKeys but extracts values instead of keys
|
|
3984
|
+
*
|
|
3985
|
+
* Example:
|
|
3986
|
+
* { DATABASE: { GENERIC: 2000, NOT_FOUND: 2001 }, AUTH: { INVALID: 3000 } }
|
|
3987
|
+
* becomes
|
|
3988
|
+
* 2000 | 2001 | 3000
|
|
3989
|
+
*/
|
|
3990
|
+
type DeepValues<T> = T extends object ? { [K in keyof T]: DeepValues<T[K]> }[keyof T] : T;
|
|
3991
|
+
/**
|
|
3992
|
+
* Type helper to extract all error code values
|
|
3993
|
+
* Union type of all numeric error codes defined in ERROR_CODES
|
|
3994
|
+
*
|
|
3995
|
+
* Type: 2000 | 2001 | 2002 | ... | 9203
|
|
3996
|
+
*/
|
|
3997
|
+
type ErrorCode = DeepValues<typeof ERROR_CODES>;
|
|
3998
|
+
//#endregion
|
|
3999
|
+
//#region src/errors/application-error.d.ts
|
|
4000
|
+
/**
|
|
4001
|
+
* ApplicationError
|
|
4002
|
+
*
|
|
4003
|
+
* Abstract base class for all application errors.
|
|
4004
|
+
*
|
|
4005
|
+
* @deprecated Use {@link HttpException} for new error classes. `HttpException` provides
|
|
4006
|
+
* a simpler constructor that takes `(httpStatus, message?)` and derives the error code
|
|
4007
|
+
* automatically. Existing subclasses will continue to work but should be migrated over time.
|
|
4008
|
+
*
|
|
4009
|
+
* Features:
|
|
4010
|
+
* - Type-safe error codes from ERROR_CODES registry
|
|
4011
|
+
* - Type-safe message keys from i18n module
|
|
4012
|
+
* - Localized message keys (translated by ExceptionHandler)
|
|
4013
|
+
* - Structured metadata for logging and interpolation
|
|
4014
|
+
* - Proper Error prototype chain
|
|
4015
|
+
* - Automatic timestamp generation
|
|
4016
|
+
* - Serialization for RPC transmission
|
|
4017
|
+
* - Optional self-reporting via `report()` method
|
|
4018
|
+
* - Optional self-rendering via `render()` method
|
|
4019
|
+
*
|
|
4020
|
+
* Message Localization:
|
|
4021
|
+
* - Each error class passes an i18n key (e.g., 'errors.userNotFound') to super()
|
|
4022
|
+
* - `Error.message` contains the i18n key for useful stack traces and fallback display
|
|
4023
|
+
* - Metadata provides interpolation parameters (e.g., { userId: '123' })
|
|
4024
|
+
* - ExceptionHandler translates the message key using I18nService before sending response
|
|
4025
|
+
* - This ensures errors are localized based on the user's locale
|
|
4026
|
+
*/
|
|
4027
|
+
declare abstract class ApplicationError extends Error {
|
|
4028
|
+
/**
|
|
4029
|
+
* Controls whether stack traces are captured.
|
|
4030
|
+
* Set to false in production to skip the expensive Error.captureStackTrace() call,
|
|
4031
|
+
* since stack traces are stripped from responses in production anyway.
|
|
4032
|
+
*/
|
|
4033
|
+
static captureStackTraces: boolean;
|
|
4034
|
+
/**
|
|
4035
|
+
* Type-safe error code from ERROR_CODES registry
|
|
4036
|
+
* See error-codes.ts for the complete registry
|
|
4037
|
+
*/
|
|
4038
|
+
readonly code: ErrorCode;
|
|
4039
|
+
/**
|
|
4040
|
+
* ISO timestamp when the error was created
|
|
4041
|
+
*/
|
|
4042
|
+
readonly timestamp: string;
|
|
4043
|
+
/**
|
|
4044
|
+
* Additional structured data about the error
|
|
4045
|
+
* Used for:
|
|
4046
|
+
* 1. Logging and debugging
|
|
4047
|
+
* 2. Message interpolation (e.g., { userId: '123', email: 'user@example.com' })
|
|
4048
|
+
*/
|
|
4049
|
+
readonly metadata?: Record<string, unknown>;
|
|
4050
|
+
/**
|
|
4051
|
+
* @param i18nKey - Type-safe i18n message key (e.g., 'errors.userNotFound')
|
|
4052
|
+
* @param code - Type-safe error code from ERROR_CODES registry
|
|
4053
|
+
* @param metadata - Optional data for logging and interpolation
|
|
4054
|
+
*/
|
|
4055
|
+
constructor(i18nKey: MessageKeys, code: ErrorCode, metadata?: Record<string, unknown>);
|
|
4056
|
+
/**
|
|
4057
|
+
* Filter metadata to include only user-facing properties
|
|
4058
|
+
*
|
|
4059
|
+
* User-facing properties (validation/constraint errors):
|
|
4060
|
+
* - issues: Validation errors from SchemaValidationError
|
|
4061
|
+
* - fields: Constraint violation fields
|
|
4062
|
+
* - field: Single field constraint/foreign key
|
|
4063
|
+
*
|
|
4064
|
+
* Internal properties (excluded from response):
|
|
4065
|
+
* - path, method: Route debugging
|
|
4066
|
+
* - controllerName, reason: Controller errors
|
|
4067
|
+
* - details, etc.: Internal debugging info
|
|
4068
|
+
*
|
|
4069
|
+
* @param metadata - Raw metadata object
|
|
4070
|
+
* @returns Filtered metadata with only whitelisted properties
|
|
4071
|
+
*/
|
|
4072
|
+
private static filterMetadata;
|
|
4073
|
+
/**
|
|
4074
|
+
* Serialize error to ErrorResponse format for RPC transmission
|
|
4075
|
+
*
|
|
4076
|
+
* @param env - Environment (development | production)
|
|
4077
|
+
* @param translatedMessage - Optional translated message (from ExceptionHandler)
|
|
4078
|
+
* @returns ErrorResponse object suitable for JSON serialization
|
|
4079
|
+
*/
|
|
4080
|
+
toErrorResponse(env: Environment, translatedMessage?: string): ErrorResponse;
|
|
4081
|
+
/**
|
|
4082
|
+
* JSON serialization (used by JSON.stringify)
|
|
4083
|
+
* Defaults to development mode for backward compatibility
|
|
4084
|
+
* Note: This will use the untranslated message key - use ExceptionHandler for proper localization
|
|
4085
|
+
*/
|
|
4086
|
+
toJSON(): ErrorResponse;
|
|
4087
|
+
/**
|
|
4088
|
+
* Self-reporting hook. Override in subclasses to define custom reporting logic
|
|
4089
|
+
* that runs instead of the default logger.
|
|
4090
|
+
*
|
|
4091
|
+
* - Return `void` (or nothing) to **skip** default reporting after this runs.
|
|
4092
|
+
* - Return `false` to **also run** default reporting after this runs.
|
|
4093
|
+
*
|
|
4094
|
+
* @example
|
|
4095
|
+
* ```typescript
|
|
4096
|
+
* class PaymentError extends HttpException {
|
|
4097
|
+
* report(): void {
|
|
4098
|
+
* sentry.captureException(this)
|
|
4099
|
+
* // Default logging is skipped
|
|
4100
|
+
* }
|
|
4101
|
+
* }
|
|
4102
|
+
*
|
|
4103
|
+
* class SoftError extends HttpException {
|
|
4104
|
+
* report(): false {
|
|
4105
|
+
* analytics.track(this)
|
|
4106
|
+
* return false // Default logging also runs
|
|
4107
|
+
* }
|
|
4108
|
+
* }
|
|
4109
|
+
* ```
|
|
4110
|
+
*/
|
|
4111
|
+
report?(): void | false;
|
|
4112
|
+
/**
|
|
4113
|
+
* Self-rendering hook. Override in subclasses to define how this error
|
|
4114
|
+
* is rendered into a Response.
|
|
4115
|
+
*
|
|
4116
|
+
* Return `undefined` to fall through to the default renderer.
|
|
4117
|
+
*
|
|
4118
|
+
* @param ctx - The execution context (narrow via `ctx.type` for HTTP helpers)
|
|
4119
|
+
* @returns A Response, ErrorResponse, or undefined to use default rendering
|
|
4120
|
+
*
|
|
4121
|
+
* @example
|
|
4122
|
+
* ```typescript
|
|
4123
|
+
* class MaintenanceError extends HttpException {
|
|
4124
|
+
* render(ctx: ExceptionContext): Response | undefined {
|
|
4125
|
+
* if (ctx.type === 'http') {
|
|
4126
|
+
* return ctx.ctx.html('<h1>Down for maintenance</h1>', 503)
|
|
4127
|
+
* }
|
|
4128
|
+
* }
|
|
4129
|
+
* }
|
|
4130
|
+
* ```
|
|
4131
|
+
*/
|
|
4132
|
+
render?(ctx: ExceptionContext): Response | ErrorResponse | undefined;
|
|
4133
|
+
}
|
|
4134
|
+
//#endregion
|
|
4135
|
+
//#region src/errors/default-exception-handler.d.ts
|
|
4136
|
+
/**
|
|
4137
|
+
* DefaultExceptionHandler — the built-in exception handler used when no
|
|
4138
|
+
* custom handler is provided via `ApplicationConfig.exceptionHandler`.
|
|
4139
|
+
*
|
|
4140
|
+
* Has an empty `register()` method, so all exceptions flow through the
|
|
4141
|
+
* default pipeline: severity-based logging, i18n translation, and JSON
|
|
4142
|
+
* error response serialization.
|
|
4143
|
+
*
|
|
4144
|
+
* To customize exception handling, extend {@link ExceptionHandler} and
|
|
4145
|
+
* override `register()`, then pass your class to the Stratal config:
|
|
4146
|
+
*
|
|
4147
|
+
* @example
|
|
4148
|
+
* ```typescript
|
|
4149
|
+
* new Stratal({
|
|
4150
|
+
* module: AppModule,
|
|
4151
|
+
* exceptionHandler: AppExceptionHandler,
|
|
4152
|
+
* })
|
|
4153
|
+
* ```
|
|
4154
|
+
*/
|
|
4155
|
+
declare class DefaultExceptionHandler extends ExceptionHandler {
|
|
4156
|
+
register(): void;
|
|
4157
|
+
}
|
|
4158
|
+
//#endregion
|
|
4159
|
+
//#region src/errors/get-http-status.d.ts
|
|
4160
|
+
/**
|
|
4161
|
+
* Maps error codes to HTTP status codes
|
|
4162
|
+
*
|
|
4163
|
+
* This utility is used by the frontend to set appropriate HTTP status codes
|
|
4164
|
+
* when returning errors from API routes.
|
|
4165
|
+
*
|
|
4166
|
+
* @param code - Numeric error code from ERROR_CODES registry
|
|
4167
|
+
* @returns HTTP status code (200-599)
|
|
4168
|
+
*/
|
|
4169
|
+
declare function getHttpStatus(code: number): ContentfulStatusCode;
|
|
4170
|
+
/**
|
|
4171
|
+
* Resolve the HTTP status code for an ApplicationError.
|
|
4172
|
+
*
|
|
4173
|
+
* If the error is an {@link HttpException}, its `httpStatus` property takes precedence.
|
|
4174
|
+
* Otherwise, falls back to the code-range-based mapping via {@link getHttpStatus}.
|
|
4175
|
+
*
|
|
4176
|
+
* @param error - The application error to resolve the status for
|
|
4177
|
+
* @returns HTTP status code
|
|
4178
|
+
*/
|
|
4179
|
+
declare function resolveHttpStatus(error: ApplicationError): ContentfulStatusCode;
|
|
4180
|
+
//#endregion
|
|
4181
|
+
//#region src/errors/http-exception.d.ts
|
|
4182
|
+
/**
|
|
4183
|
+
* HTTP-centric exception base class.
|
|
4184
|
+
*
|
|
4185
|
+
* Unlike {@link ApplicationError} which requires `(i18nKey, code, metadata)`,
|
|
4186
|
+
* `HttpException` takes just `(httpStatus, message?)` and derives the error code
|
|
4187
|
+
* from the HTTP status automatically.
|
|
4188
|
+
*
|
|
4189
|
+
* The message can be a plain string or an i18n key — the {@link ExceptionHandler}
|
|
4190
|
+
* tries to translate it via `i18n.t()`, falling back to the raw string if the
|
|
4191
|
+
* key is not found.
|
|
4192
|
+
*
|
|
4193
|
+
* Existing {@link ApplicationError} subclasses can be migrated to this gradually.
|
|
4194
|
+
*
|
|
4195
|
+
* @example
|
|
4196
|
+
* ```typescript
|
|
4197
|
+
* // Simple usage with plain message
|
|
4198
|
+
* throw new HttpException(404, 'User not found')
|
|
4199
|
+
*
|
|
4200
|
+
* // With i18n key (auto-translated if key exists)
|
|
4201
|
+
* throw new HttpException(422, 'errors.invalidInput')
|
|
4202
|
+
*
|
|
4203
|
+
* // Default message for status code
|
|
4204
|
+
* throw new HttpException(500)
|
|
4205
|
+
*
|
|
4206
|
+
* // Subclass for domain-specific errors
|
|
4207
|
+
* class PaymentDeclinedError extends HttpException {
|
|
4208
|
+
* constructor() {
|
|
4209
|
+
* super(402, 'errors.paymentDeclined')
|
|
4210
|
+
* }
|
|
4211
|
+
* }
|
|
4212
|
+
* ```
|
|
4213
|
+
*/
|
|
4214
|
+
declare class HttpException extends ApplicationError {
|
|
4215
|
+
/**
|
|
4216
|
+
* The HTTP status code for this exception.
|
|
4217
|
+
* Used by the {@link ExceptionHandler} to set the response status.
|
|
4218
|
+
*/
|
|
4219
|
+
readonly httpStatus: ContentfulStatusCode;
|
|
4220
|
+
/**
|
|
4221
|
+
* @param httpStatus - HTTP status code (e.g., 404, 422, 500)
|
|
4222
|
+
* @param message - Optional message string or i18n key. Defaults to the
|
|
4223
|
+
* standard HTTP status message (e.g., "Not Found" for 404).
|
|
4224
|
+
*/
|
|
4225
|
+
constructor(httpStatus: ContentfulStatusCode, message?: string);
|
|
4226
|
+
}
|
|
4227
|
+
/**
|
|
4228
|
+
* Throw an HTTP exception from anywhere in the application.
|
|
4229
|
+
*
|
|
4230
|
+
* The message can be a plain string or an i18n key — the {@link ExceptionHandler}
|
|
4231
|
+
* translates it automatically, falling back to the raw string if the key is not found.
|
|
4232
|
+
*
|
|
4233
|
+
* @param status - HTTP status code
|
|
4234
|
+
* @param message - Optional message (plain string or i18n key)
|
|
4235
|
+
* @throws {@link HttpException} — always throws, never returns
|
|
4236
|
+
*
|
|
4237
|
+
* @example
|
|
4238
|
+
* ```typescript
|
|
4239
|
+
* // With plain message
|
|
4240
|
+
* abort(404, 'User not found')
|
|
4241
|
+
*
|
|
4242
|
+
* // Default message for status
|
|
4243
|
+
* abort(403)
|
|
4244
|
+
*
|
|
4245
|
+
* // With i18n key
|
|
4246
|
+
* abort(422, 'errors.invalidInput')
|
|
4247
|
+
* ```
|
|
4248
|
+
*/
|
|
4249
|
+
declare function abort(status: ContentfulStatusCode, message?: MessageKeys | string & {}): never;
|
|
4250
|
+
//#endregion
|
|
4251
|
+
//#region src/errors/internal-error.d.ts
|
|
4252
|
+
/**
|
|
4253
|
+
* InternalError
|
|
4254
|
+
*
|
|
4255
|
+
* Represents an unexpected internal server error.
|
|
4256
|
+
* Used to wrap unknown errors that don't fit into specific error categories.
|
|
4257
|
+
*
|
|
4258
|
+
* This error is thrown when:
|
|
4259
|
+
* - An unexpected exception occurs
|
|
4260
|
+
* - An error type is not recognized
|
|
4261
|
+
* - A system-level failure happens
|
|
4262
|
+
*/
|
|
4263
|
+
declare class InternalError extends ApplicationError {
|
|
4264
|
+
constructor(metadata?: Record<string, unknown>);
|
|
4265
|
+
}
|
|
4266
|
+
//#endregion
|
|
4267
|
+
//#region src/errors/is-application-error.d.ts
|
|
4268
|
+
/**
|
|
4269
|
+
* Type guard to check if an error is an ApplicationError.
|
|
4270
|
+
*
|
|
4271
|
+
* Uses `instanceof` first, then falls back to a structural check
|
|
4272
|
+
* for the `code` and `timestamp` properties that all ApplicationError
|
|
4273
|
+
* instances have. This handles cross-module boundary cases where
|
|
4274
|
+
* `instanceof` fails due to duplicate class identities (e.g., Vite's
|
|
4275
|
+
* module graph in workerd).
|
|
4276
|
+
*
|
|
4277
|
+
* @param error - The error to check
|
|
4278
|
+
* @returns True if the error is an ApplicationError instance
|
|
4279
|
+
*/
|
|
4280
|
+
declare function isApplicationError(error: unknown): error is ApplicationError;
|
|
4281
|
+
//#endregion
|
|
4282
|
+
//#region src/errors/container-not-initialized.error.d.ts
|
|
4283
|
+
/**
|
|
4284
|
+
* Thrown when attempting to access the application container via AsyncLocalStorage
|
|
4285
|
+
* before `Application.initialize()` has been called.
|
|
4286
|
+
*
|
|
4287
|
+
* This typically means `route()` or another standalone function is being called
|
|
4288
|
+
* outside the application lifecycle.
|
|
4289
|
+
*/
|
|
4290
|
+
declare class ContainerNotInitializedError extends ApplicationError {
|
|
4291
|
+
constructor();
|
|
4292
|
+
}
|
|
4293
|
+
//#endregion
|
|
4294
|
+
//#region src/errors/request-container-not-initialized.error.d.ts
|
|
4295
|
+
/**
|
|
4296
|
+
* RequestContainerNotInitializedError
|
|
4297
|
+
*
|
|
4298
|
+
* Thrown when attempting to access the request-scoped container before it has been initialized.
|
|
4299
|
+
* This typically indicates that the RouterService middleware hasn't run yet,
|
|
4300
|
+
* or the router context is being accessed outside of a request lifecycle.
|
|
4301
|
+
*/
|
|
4302
|
+
declare class RequestContainerNotInitializedError extends ApplicationError {
|
|
4303
|
+
constructor();
|
|
4304
|
+
}
|
|
4305
|
+
//#endregion
|
|
4306
|
+
//#region src/errors/stratal-not-initialized.error.d.ts
|
|
4307
|
+
/**
|
|
4308
|
+
* StratalNotInitializedError
|
|
4309
|
+
*
|
|
4310
|
+
* Thrown when attempting to resolve the Application instance before Stratal has been instantiated.
|
|
4311
|
+
* This typically indicates that the Stratal instance is not exported as the default export.
|
|
4312
|
+
*/
|
|
4313
|
+
declare class StratalNotInitializedError extends ApplicationError {
|
|
4314
|
+
constructor();
|
|
4315
|
+
}
|
|
4316
|
+
//#endregion
|
|
4317
|
+
export { SSEMessage as $, VersioningOptions as $n, ModuleRegistry as $t, LocaleNotSupportedError as A, ContextCallback as An, instancePerContainerCachingFactory$1 as Ar, Route as At, ContextQueryResult as B, ControllerOptions as Bn, WhenOptions as Br, getControllerOptions as Bt, DetectionStrategy as C, OnInitialize as Cn, Container as Cr, commonErrorSchemas as Ct, buildDetectorOptions as D, ValueProvider as Dn, delay as Dr, successMessageSchema as Dt, ResolvedI18nOptions as E, RegistryEntry as En, container$1 as Er, paginationQuerySchema as Et, QueueExceptionContext as F, RespondCallback as Fn, ConditionalBindingUse as Fr, Get as Ft, buildRouteUrl as G, RouteBodyObject as Gn, extractParamNames as Gt, SignedUriOptions as H, ExplicitRouteMetadata as Hn, ErrorResponse as Hr, getControllerVersion as Ht, createCliExceptionContext as I, StratalExecutionContext as In, PredicateContainer as Ir, Patch as It, RouteRegistry as J, RouteResponse as Jn, sortRoutesBySpecificity as Jt, RegisteredRoute as K, RouteConfig as Kn, generateConventionRouteName as Kt, createCronExceptionContext as L, LocalePathService as Ln, ContainerLike as Lr, Post as Lt, CronExceptionContext as M, RenderableCallback as Mn, ConditionalBindingBuilder as Mr, getRouteMetadata as Mt, ExceptionContext as N, Reportable as Nn, ConditionalBindingBuilderImpl as Nr, All as Nt, resolveI18nOptions as O, ExceptionHandler as On, inject$1 as Or, uuidParamSchema as Ot, HttpExceptionContext as P, ReportableCallback as Pn, ConditionalBindingGive as Pr, Delete as Pt, ApplicationOptions as Q, SecurityScheme as Qn, RouteRegistrationService as Qt, createHttpExceptionContext as R, ResolvedPath as Rn, ExtensionDecorator as Rr, Put as Rt, I18nModule as S, OnException as Sn, StratalRouteMap as Sr, parseDomainPattern as St, LanguageDetectionOptions as T, Provider as Tn, DependencyContainer$1 as Tr, paginatedResponseSchema as Tt, Uri as U, LocalePathConfig as Un, isErrorResponse as Ur, createMiddlewareChain as Ut, RouterContext as V, ConventionRouteMetadata as Vn, Environment as Vr, getControllerRoute as Vt, UriOptions as W, RouteBody as Wn, extractDomainParamNames as Wt, Application as X, RouterEnv as Xn, route as Xt, VersioningService as Y, RouteResponseObject as Yn, toOpenAPIPath as Yt, ApplicationConfig as Z, RouterVariables as Zn, ROUTER_TOKENS as Zt, Messages as _, FactoryProvider as _n, DI_TOKENS as _r, SignedUrlOptions as _t, InternalError as a, IController as an, containerStorage as ar, MiddlewareNextCalledMultipleTimesError as at, messages as b, ModuleContext as bn, SerializedRoute as br, VerifySignatureMiddleware as bt, getHttpStatus as c, CommandResult as cn, RequestScopeOperationNotAllowedError as cr, ResponseValidationError as ct, ApplicationError as d, ParsedSignature as dn, INJECT_PARAM_METADATA_KEY as dr, SchemaValidationError as dt, RouteConfigurable as en, HTTP_METHODS as er, SSEStreamingApi$1 as et, ERROR_CODES as f, Quarry as fn, InjectParam as fr, RouteNotFoundError as ft, MessageRegistry as g, ExistingProvider as gn, DIToken as gr, ControllerRegistrationError as gt, MessageLoaderService as h, DynamicModule as hn, CONTAINER_TOKEN as hr, HonoAppAlreadyConfiguredError as ht, isApplicationError as i, Next$1 as in, VERSION_NEUTRAL as ir, InvalidSignatureError as it, CliExceptionContext as j, LogSeverity as jn, singleton as jr, getRouteDecoratedMethods as jt, TranslationMissingError as k, ApplicationErrorConstructor as kn, injectable$1 as kr, validationErrorResponseSchema as kt, resolveHttpStatus as l, ParsedArgument as ln, ConditionalBindingFallbackError as lr, RouteNameNotFoundError as lt, I18nContextMiddleware as m, ClassProvider as mn, getMethodInjections as mr, OpenAPIRouteRegistrationError as mt, RequestContainerNotInitializedError as n, RouterGroupConfig as nn, ROUTE_METADATA_KEYS as nr, DomainMismatchError as nt, HttpException as o, CommandInput as on, getContainer as or, MissingEnvironmentVariableError as ot, ErrorCode as p, AsyncModuleOptions as pn, ParamInjection as pr, OpenAPIValidationError as pt, RouteRegistrationInput as q, RouteMetadata as qn, getPathSpecificityScore as qt, ContainerNotInitializedError as r, Middleware as rn, SECURITY_SCHEMES as rr, DuplicateRouteNameError as rt, abort as s, CommandInternals as sn, runWithContainer as sr, MissingRouteParamError as st, StratalNotInitializedError as t, Router as tn, ROUTER_CONTEXT_KEYS as tr, StreamingApi$1 as tt, DefaultExceptionHandler as u, ParsedOption as un, Transient as ur, RouterUseScopeError as ut, getLocales as v, InjectionToken$2 as vn, RouteName as vr, signUrl as vt, I18nModuleOptions as w, OnShutdown as wn, ContainerOptions as wr, errorResponseSchema as wt, I18N_TOKENS as x, ModuleOptions as xn, SerializedRoutes as xr, createDomainMiddleware as xt, getMessages as y, ModuleClass as yn, RouteParams as yr, verifySignedUrl as yt, createQueueExceptionContext as z, HonoApp as zn, Scope as zr, Controller as zt };
|
|
4318
|
+
//# sourceMappingURL=index-DPFqRs8L.d.mts.map
|