stratal 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{base-email.provider-DypUAfWm.mjs → base-email.provider-Cuw4OAB0.mjs} +1 -1
- package/dist/{base-email.provider-DypUAfWm.mjs.map → base-email.provider-Cuw4OAB0.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 +182 -5
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +2 -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-BTAnQRGU.mjs} +1 -1
- package/dist/{colors-Y7WIFXs7.mjs.map → colors-BTAnQRGU.mjs.map} +1 -1
- package/dist/{command-TnkPYWta.d.mts → command-B1YuV-UZ.d.mts} +2 -2
- package/dist/{command-TnkPYWta.d.mts.map → command-B1YuV-UZ.d.mts.map} +1 -1
- package/dist/{command-B1CPgsrU.mjs → command-DjGqCYHv.mjs} +3 -3
- package/dist/command-DjGqCYHv.mjs.map +1 -0
- package/dist/config/index.d.mts +2 -2
- package/dist/config/index.mjs +12 -19
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts → consumer-registry-BkuHXR_u.d.mts} +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts.map → consumer-registry-BkuHXR_u.d.mts.map} +1 -1
- package/dist/cron/index.d.mts +2 -2
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -3
- package/dist/{cron-manager-CFBamKKk.mjs → cron-manager-1KnZvojs.mjs} +3 -3
- package/dist/{cron-manager-CFBamKKk.mjs.map → cron-manager-1KnZvojs.mjs.map} +1 -1
- package/dist/{cron-manager-D7imGwUT.d.mts → cron-manager-BnEZquBL.d.mts} +2 -2
- package/dist/{cron-manager-D7imGwUT.d.mts.map → cron-manager-BnEZquBL.d.mts.map} +1 -1
- 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 +8 -16
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-DaewN8hc.mjs → en-3QnZwP-u.mjs} +10 -1
- package/dist/en-3QnZwP-u.mjs.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +2 -3
- package/dist/errors--RBIvDXr.mjs +1560 -0
- package/dist/errors--RBIvDXr.mjs.map +1 -0
- package/dist/{errors-DuAR5Wke.mjs → errors-B7hCnXgB.mjs} +2 -2
- package/dist/{errors-DuAR5Wke.mjs.map → errors-B7hCnXgB.mjs.map} +1 -1
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +1 -2
- package/dist/{events-CvUSgEuN.mjs → events-UTJliZhl.mjs} +2 -2
- package/dist/{events-CvUSgEuN.mjs.map → events-UTJliZhl.mjs.map} +1 -1
- package/dist/{gateway-context-CNOLkLUC.mjs → gateway-context-BdBFoQd8.mjs} +66 -10
- package/dist/gateway-context-BdBFoQd8.mjs.map +1 -0
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DUk_Kzst.mjs → guards-MtDgcHnF.mjs} +1 -1
- package/dist/{guards-DUk_Kzst.mjs.map → guards-MtDgcHnF.mjs.map} +1 -1
- 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-BpLLLCTg.mjs +2462 -0
- package/dist/i18n.module-BpLLLCTg.mjs.map +1 -0
- package/dist/{index-D_w_Rmtd.d.mts → index-BDh9J2KD.d.mts} +10 -1
- package/dist/{index-D_w_Rmtd.d.mts.map → index-BDh9J2KD.d.mts.map} +1 -1
- package/dist/{index-Dp6A5ywM.d.mts → index-BR23zDMy.d.mts} +1 -1
- package/dist/{index-Dp6A5ywM.d.mts.map → index-BR23zDMy.d.mts.map} +1 -1
- package/dist/index-BrmS34sa.d.mts +4287 -0
- package/dist/index-BrmS34sa.d.mts.map +1 -0
- package/dist/{index-DGRe6Yoa.d.mts → index-DPxmo6AY.d.mts} +4 -4
- package/dist/{index-DGRe6Yoa.d.mts.map → index-DPxmo6AY.d.mts.map} +1 -1
- package/dist/{index-NGxg-KP_.d.mts → index-Dfpd_ypO.d.mts} +36 -7
- package/dist/index-Dfpd_ypO.d.mts.map +1 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -20
- package/dist/{is-command-DJVI6wEJ.mjs → is-command-PvULqiTa.mjs} +2 -2
- package/dist/{is-command-DJVI6wEJ.mjs.map → is-command-PvULqiTa.mjs.map} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs → is-seeder-BN9Ej1r7.mjs} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs.map → is-seeder-BN9Ej1r7.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-c0ftIK4G.mjs} +29 -29
- package/dist/logger-c0ftIK4G.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-C3YZ-kZN.mjs +719 -0
- package/dist/module-C3YZ-kZN.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-B77QXD56.mjs} +1 -1
- package/dist/{openapi-tools.service-B3TxYKoQ.mjs.map → openapi-tools.service-B77QXD56.mjs.map} +1 -1
- package/dist/{openapi.service-DGnX3Fc4.d.mts → openapi.service-6yj0BUY4.d.mts} +9 -17
- package/dist/openapi.service-6yj0BUY4.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +25 -11
- 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-CQCIlYTO.mjs} +37 -34
- package/dist/quarry-registry-CQCIlYTO.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-DIjD6nr-.mjs} +39 -42
- package/dist/queue.module-DIjD6nr-.mjs.map +1 -0
- package/dist/{resend.provider-bXMEkdRJ.mjs → resend.provider-Bvw36rQy.mjs} +2 -4
- package/dist/{resend.provider-bXMEkdRJ.mjs.map → resend.provider-Bvw36rQy.mjs.map} +1 -1
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +5 -16
- package/dist/{s3-storage.provider-CttzNnDR.mjs → s3-storage.provider-BAhHDMI3.mjs} +13 -5
- package/dist/s3-storage.provider-BAhHDMI3.mjs.map +1 -0
- 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-D7VXULXB.mjs} +5 -5
- package/dist/{seeder-R7RXJC35.mjs.map → seeder-D7VXULXB.mjs.map} +1 -1
- package/dist/setup-BRIN-iYT.mjs +37 -0
- package/dist/setup-BRIN-iYT.mjs.map +1 -0
- package/dist/{smtp.provider-DrbHQztF.mjs → smtp.provider-CAwpvzvD.mjs} +2 -4
- package/dist/{smtp.provider-DrbHQztF.mjs.map → smtp.provider-CAwpvzvD.mjs.map} +1 -1
- package/dist/storage/index.d.mts +2 -2
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +2 -13
- package/dist/storage/providers/index.d.mts +2 -1
- package/dist/storage/providers/index.d.mts.map +1 -1
- package/dist/storage/providers/index.mjs +1 -4
- package/dist/{storage-CZKHOhci.mjs → storage-CJ-QOwNv.mjs} +8 -9
- package/dist/storage-CJ-QOwNv.mjs.map +1 -0
- package/dist/{storage-provider.interface-0IqcdhBf.d.mts → storage-provider.interface-YRtyYBxV.d.mts} +8 -2
- package/dist/storage-provider.interface-YRtyYBxV.d.mts.map +1 -0
- package/dist/stratal-B7G4i9-N.mjs +502 -0
- package/dist/stratal-B7G4i9-N.mjs.map +1 -0
- package/dist/{types-DahElfUw.d.mts → types-CN0zONAZ.d.mts} +2 -2
- package/dist/types-CN0zONAZ.d.mts.map +1 -0
- package/dist/{usage-generator-CVIsENuE.mjs → usage-generator-Cl1HPlUp.mjs} +2 -2
- package/dist/{usage-generator-CVIsENuE.mjs.map → usage-generator-Cl1HPlUp.mjs.map} +1 -1
- package/dist/{validation-DQTC259A.mjs → validation-B4bePOa_.mjs} +2 -2
- package/dist/{validation-DQTC259A.mjs.map → validation-B4bePOa_.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +2 -2
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +1 -4
- package/dist/workers/index.d.mts +1 -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 +34 -31
- 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/en-DaewN8hc.mjs.map +0 -1
- package/dist/errors-DSKapqD8.mjs +0 -707
- package/dist/errors-DSKapqD8.mjs.map +0 -1
- package/dist/gateway-context-CNOLkLUC.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-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.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,1560 @@
|
|
|
1
|
+
import { a as __decorate, d as CONTAINER_TOKEN, f as DI_TOKENS, o as __decorateParam, p as Transient, s as __decorateMetadata, u as LOGGER_TOKENS } from "./logger-c0ftIK4G.mjs";
|
|
2
|
+
import { Lifecycle, container as container$1, delay, inject, inject as inject$1, injectable as injectable$1, instancePerContainerCachingFactory as instancePerContainerCachingFactory$1, predicateAwareClassFactory, singleton } from "tsyringe";
|
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
import { stream, streamSSE, streamText } from "hono/streaming";
|
|
5
|
+
//#region src/errors/application-error.ts
|
|
6
|
+
/**
|
|
7
|
+
* ApplicationError
|
|
8
|
+
*
|
|
9
|
+
* Abstract base class for all application errors.
|
|
10
|
+
*
|
|
11
|
+
* @deprecated Use {@link HttpException} for new error classes. `HttpException` provides
|
|
12
|
+
* a simpler constructor that takes `(httpStatus, message?)` and derives the error code
|
|
13
|
+
* automatically. Existing subclasses will continue to work but should be migrated over time.
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Type-safe error codes from ERROR_CODES registry
|
|
17
|
+
* - Type-safe message keys from i18n module
|
|
18
|
+
* - Localized message keys (translated by ExceptionHandler)
|
|
19
|
+
* - Structured metadata for logging and interpolation
|
|
20
|
+
* - Proper Error prototype chain
|
|
21
|
+
* - Automatic timestamp generation
|
|
22
|
+
* - Serialization for RPC transmission
|
|
23
|
+
* - Optional self-reporting via `report()` method
|
|
24
|
+
* - Optional self-rendering via `render()` method
|
|
25
|
+
*
|
|
26
|
+
* Message Localization:
|
|
27
|
+
* - Each error class passes an i18n key (e.g., 'errors.userNotFound') to super()
|
|
28
|
+
* - `Error.message` contains the i18n key for useful stack traces and fallback display
|
|
29
|
+
* - Metadata provides interpolation parameters (e.g., { userId: '123' })
|
|
30
|
+
* - ExceptionHandler translates the message key using I18nService before sending response
|
|
31
|
+
* - This ensures errors are localized based on the user's locale
|
|
32
|
+
*/
|
|
33
|
+
var ApplicationError = class ApplicationError extends Error {
|
|
34
|
+
/**
|
|
35
|
+
* Controls whether stack traces are captured.
|
|
36
|
+
* Set to false in production to skip the expensive Error.captureStackTrace() call,
|
|
37
|
+
* since stack traces are stripped from responses in production anyway.
|
|
38
|
+
*/
|
|
39
|
+
static captureStackTraces = true;
|
|
40
|
+
/**
|
|
41
|
+
* Type-safe error code from ERROR_CODES registry
|
|
42
|
+
* See error-codes.ts for the complete registry
|
|
43
|
+
*/
|
|
44
|
+
code;
|
|
45
|
+
/**
|
|
46
|
+
* ISO timestamp when the error was created
|
|
47
|
+
*/
|
|
48
|
+
timestamp;
|
|
49
|
+
/**
|
|
50
|
+
* Additional structured data about the error
|
|
51
|
+
* Used for:
|
|
52
|
+
* 1. Logging and debugging
|
|
53
|
+
* 2. Message interpolation (e.g., { userId: '123', email: 'user@example.com' })
|
|
54
|
+
*/
|
|
55
|
+
metadata;
|
|
56
|
+
/**
|
|
57
|
+
* @param i18nKey - Type-safe i18n message key (e.g., 'errors.userNotFound')
|
|
58
|
+
* @param code - Type-safe error code from ERROR_CODES registry
|
|
59
|
+
* @param metadata - Optional data for logging and interpolation
|
|
60
|
+
*/
|
|
61
|
+
constructor(i18nKey, code, metadata) {
|
|
62
|
+
super(i18nKey);
|
|
63
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
64
|
+
this.name = this.constructor.name;
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
67
|
+
this.metadata = metadata;
|
|
68
|
+
if (ApplicationError.captureStackTraces && Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Filter metadata to include only user-facing properties
|
|
72
|
+
*
|
|
73
|
+
* User-facing properties (validation/constraint errors):
|
|
74
|
+
* - issues: Validation errors from SchemaValidationError
|
|
75
|
+
* - fields: Constraint violation fields
|
|
76
|
+
* - field: Single field constraint/foreign key
|
|
77
|
+
*
|
|
78
|
+
* Internal properties (excluded from response):
|
|
79
|
+
* - path, method: Route debugging
|
|
80
|
+
* - controllerName, reason: Controller errors
|
|
81
|
+
* - details, etc.: Internal debugging info
|
|
82
|
+
*
|
|
83
|
+
* @param metadata - Raw metadata object
|
|
84
|
+
* @returns Filtered metadata with only whitelisted properties
|
|
85
|
+
*/
|
|
86
|
+
static filterMetadata(metadata) {
|
|
87
|
+
if (!metadata) return void 0;
|
|
88
|
+
const whitelist = [
|
|
89
|
+
"issues",
|
|
90
|
+
"fields",
|
|
91
|
+
"field"
|
|
92
|
+
];
|
|
93
|
+
const filtered = {};
|
|
94
|
+
let hasUserFacingData = false;
|
|
95
|
+
for (const key of whitelist) if (key in metadata && metadata[key] !== void 0) {
|
|
96
|
+
filtered[key] = metadata[key];
|
|
97
|
+
hasUserFacingData = true;
|
|
98
|
+
}
|
|
99
|
+
return hasUserFacingData ? filtered : void 0;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Serialize error to ErrorResponse format for RPC transmission
|
|
103
|
+
*
|
|
104
|
+
* @param env - Environment (development | production)
|
|
105
|
+
* @param translatedMessage - Optional translated message (from ExceptionHandler)
|
|
106
|
+
* @returns ErrorResponse object suitable for JSON serialization
|
|
107
|
+
*/
|
|
108
|
+
toErrorResponse(env, translatedMessage) {
|
|
109
|
+
const message = translatedMessage ?? this.message;
|
|
110
|
+
return {
|
|
111
|
+
code: this.code,
|
|
112
|
+
message,
|
|
113
|
+
timestamp: this.timestamp,
|
|
114
|
+
metadata: ApplicationError.filterMetadata(this.metadata),
|
|
115
|
+
stack: env === "development" ? this.stack?.replace(this.message, message) : void 0
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* JSON serialization (used by JSON.stringify)
|
|
120
|
+
* Defaults to development mode for backward compatibility
|
|
121
|
+
* Note: This will use the untranslated message key - use ExceptionHandler for proper localization
|
|
122
|
+
*/
|
|
123
|
+
toJSON() {
|
|
124
|
+
return this.toErrorResponse("development");
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/router/router.tokens.ts
|
|
129
|
+
/**
|
|
130
|
+
* Dependency injection tokens for the router system
|
|
131
|
+
*/
|
|
132
|
+
const ROUTER_TOKENS = {
|
|
133
|
+
RouterContext: Symbol.for("stratal:router:context"),
|
|
134
|
+
RouteRegistry: Symbol.for("stratal:router:route-registry"),
|
|
135
|
+
VersioningService: Symbol.for("stratal:router:versioning-service"),
|
|
136
|
+
LocalePathService: Symbol.for("stratal:router:locale-path-service"),
|
|
137
|
+
RouterResolver: Symbol.for("stratal:router:router-resolver"),
|
|
138
|
+
HonoApp: Symbol.for("stratal:router:hono-app"),
|
|
139
|
+
Uri: Symbol.for("stratal:router:uri")
|
|
140
|
+
};
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/di/errors/conditional-binding-fallback.error.ts
|
|
143
|
+
/**
|
|
144
|
+
* ConditionalBindingFallbackError
|
|
145
|
+
*
|
|
146
|
+
* Thrown when a conditional binding predicate returns false but no fallback
|
|
147
|
+
* implementation was provided and no existing registration exists for the token.
|
|
148
|
+
*
|
|
149
|
+
* This typically indicates a misconfiguration in the DI setup where:
|
|
150
|
+
* - A `when().use().give()` chain was used without `otherwise()`
|
|
151
|
+
* - AND the token wasn't previously registered
|
|
152
|
+
* - AND the predicate evaluated to false at resolution time
|
|
153
|
+
*/
|
|
154
|
+
var ConditionalBindingFallbackError = class extends ApplicationError {
|
|
155
|
+
constructor(token) {
|
|
156
|
+
super("errors.conditionalBindingFallback", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { token });
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/di/errors/request-scope-operation-not-allowed.error.ts
|
|
161
|
+
/**
|
|
162
|
+
* RequestScopeOperationNotAllowedError
|
|
163
|
+
*
|
|
164
|
+
* Thrown when attempting to call a method that is not allowed on the current container scope.
|
|
165
|
+
* - `createRequestScope()` and `runInRequestScope()` can only be called on global containers
|
|
166
|
+
*/
|
|
167
|
+
var RequestScopeOperationNotAllowedError = class extends ApplicationError {
|
|
168
|
+
constructor(methodName) {
|
|
169
|
+
super("errors.requestScopeOperationNotAllowed", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { methodName });
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/di/conditional-binding-builder.ts
|
|
174
|
+
/**
|
|
175
|
+
* Implementation of ConditionalBindingBuilder
|
|
176
|
+
*
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
var ConditionalBindingBuilderImpl = class {
|
|
180
|
+
constructor(tsyringeContainer, predicateContainer, predicate, options) {
|
|
181
|
+
this.tsyringeContainer = tsyringeContainer;
|
|
182
|
+
this.predicateContainer = predicateContainer;
|
|
183
|
+
this.predicate = predicate;
|
|
184
|
+
this.options = options;
|
|
185
|
+
}
|
|
186
|
+
use(token) {
|
|
187
|
+
return new ConditionalBindingUseImpl(this.tsyringeContainer, this.predicateContainer, this.predicate, this.options, token);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Implementation of ConditionalBindingUse
|
|
192
|
+
*
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
var ConditionalBindingUseImpl = class {
|
|
196
|
+
constructor(tsyringeContainer, predicateContainer, predicate, options, token) {
|
|
197
|
+
this.tsyringeContainer = tsyringeContainer;
|
|
198
|
+
this.predicateContainer = predicateContainer;
|
|
199
|
+
this.predicate = predicate;
|
|
200
|
+
this.options = options;
|
|
201
|
+
this.token = token;
|
|
202
|
+
}
|
|
203
|
+
give(trueImplementation) {
|
|
204
|
+
const falseImplementation = this.getFallbackImplementation();
|
|
205
|
+
this.registerWithPredicate(trueImplementation, falseImplementation);
|
|
206
|
+
return { otherwise: (implementation) => {
|
|
207
|
+
this.registerWithPredicate(trueImplementation, implementation);
|
|
208
|
+
} };
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get fallback implementation: existing registration or throw-on-resolve class
|
|
212
|
+
*/
|
|
213
|
+
getFallbackImplementation() {
|
|
214
|
+
if (this.tsyringeContainer.isRegistered(this.token)) {
|
|
215
|
+
const existingInstance = this.tsyringeContainer.resolve(this.token);
|
|
216
|
+
return class ExistingInstanceWrapper {
|
|
217
|
+
static instance = existingInstance;
|
|
218
|
+
constructor() {
|
|
219
|
+
return ExistingInstanceWrapper.instance;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const tokenStr = typeof this.token === "symbol" ? this.token.description ?? "unknown" : typeof this.token === "function" ? this.token.name : String(this.token);
|
|
224
|
+
return class NoFallbackError {
|
|
225
|
+
constructor() {
|
|
226
|
+
throw new ConditionalBindingFallbackError(tokenStr);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
registerWithPredicate(trueImplementation, falseImplementation) {
|
|
231
|
+
const { predicate, predicateContainer, options } = this;
|
|
232
|
+
this.tsyringeContainer.register(this.token, { useFactory: predicateAwareClassFactory(() => predicate(predicateContainer), trueImplementation, falseImplementation, options.cache ?? false) });
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/di/container.ts
|
|
237
|
+
/**
|
|
238
|
+
* Unified Container for DI management
|
|
239
|
+
*
|
|
240
|
+
* Manages the two-tier container hierarchy:
|
|
241
|
+
* - Global scope: Singletons, base instances of request-scoped services
|
|
242
|
+
* - Request scope: Context-enriched instances per HTTP request
|
|
243
|
+
*
|
|
244
|
+
* @example Basic registration
|
|
245
|
+
* ```typescript
|
|
246
|
+
* import { container as tsyringeRootContainer } from 'tsyringe'
|
|
247
|
+
*
|
|
248
|
+
* const container = new Container({
|
|
249
|
+
* container: tsyringeRootContainer.createChildContainer()
|
|
250
|
+
* })
|
|
251
|
+
*
|
|
252
|
+
* container.register(I18nService)
|
|
253
|
+
* container.register(MY_TOKEN, MyService)
|
|
254
|
+
* container.registerSingleton(ConfigService)
|
|
255
|
+
* container.registerValue(MY_TOKEN, myInstance)
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* @example Request scope (automatic lifecycle)
|
|
259
|
+
* ```typescript
|
|
260
|
+
* await container.runInRequestScope(routerContext, async (requestContainer) => {
|
|
261
|
+
* const i18n = requestContainer.resolve(I18N_TOKEN)
|
|
262
|
+
* })
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
var Container = class Container {
|
|
266
|
+
container;
|
|
267
|
+
isRequestScoped;
|
|
268
|
+
constructor(options) {
|
|
269
|
+
this.isRequestScoped = options.isRequestScoped ?? false;
|
|
270
|
+
this.container = options.container;
|
|
271
|
+
if (!this.isRequestScoped) this.container.register(CONTAINER_TOKEN, { useValue: this });
|
|
272
|
+
}
|
|
273
|
+
register(tokenOrClass, serviceClassOrScope, scope) {
|
|
274
|
+
let token;
|
|
275
|
+
let serviceClass;
|
|
276
|
+
let lifecycle;
|
|
277
|
+
if (typeof serviceClassOrScope === "function") {
|
|
278
|
+
token = tokenOrClass;
|
|
279
|
+
serviceClass = serviceClassOrScope;
|
|
280
|
+
lifecycle = scope;
|
|
281
|
+
} else {
|
|
282
|
+
token = tokenOrClass;
|
|
283
|
+
serviceClass = tokenOrClass;
|
|
284
|
+
lifecycle = serviceClassOrScope;
|
|
285
|
+
}
|
|
286
|
+
if (lifecycle !== void 0) this.container.register(token, { useClass: serviceClass }, { lifecycle });
|
|
287
|
+
else this.container.register(token, { useClass: serviceClass });
|
|
288
|
+
}
|
|
289
|
+
registerSingleton(tokenOrClass, serviceClass) {
|
|
290
|
+
if (serviceClass !== void 0) this.container.registerSingleton(tokenOrClass, serviceClass);
|
|
291
|
+
else {
|
|
292
|
+
const targetClass = tokenOrClass;
|
|
293
|
+
this.container.registerSingleton(targetClass, targetClass);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Register a value (instance) directly
|
|
298
|
+
*/
|
|
299
|
+
registerValue(token, value) {
|
|
300
|
+
this.container.register(token, { useValue: value });
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Register with factory function
|
|
304
|
+
*/
|
|
305
|
+
registerFactory(token, factory) {
|
|
306
|
+
this.container.register(token, { useFactory: () => factory(this) });
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Register an alias to an existing token
|
|
310
|
+
*/
|
|
311
|
+
registerExisting(alias, target) {
|
|
312
|
+
this.container.register(alias, { useToken: target });
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Resolve a service from the container
|
|
316
|
+
*/
|
|
317
|
+
resolve(token) {
|
|
318
|
+
return this.container.resolve(token);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check if a token is registered
|
|
322
|
+
*/
|
|
323
|
+
isRegistered(token) {
|
|
324
|
+
return this.container.isRegistered(token);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Start a conditional binding with predicate evaluation
|
|
328
|
+
*/
|
|
329
|
+
when(predicate, options = {}) {
|
|
330
|
+
return new ConditionalBindingBuilderImpl(this.container, this, predicate, options);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Replace a service registration with a decorated version
|
|
334
|
+
*/
|
|
335
|
+
extend(token, decorator) {
|
|
336
|
+
const decoratedInstance = decorator(this.container.resolve(token), this);
|
|
337
|
+
this.container.register(token, { useValue: decoratedInstance });
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Run callback within request scope
|
|
341
|
+
*
|
|
342
|
+
* Creates a child container with fresh instances for services registered with `scope: Scope.Request`.
|
|
343
|
+
* Callback receives the request-scoped container as argument.
|
|
344
|
+
*
|
|
345
|
+
* Can only be called on global container (not request-scoped).
|
|
346
|
+
*/
|
|
347
|
+
async runInRequestScope(routerContext, callback) {
|
|
348
|
+
if (this.isRequestScoped) throw new RequestScopeOperationNotAllowedError("runInRequestScope");
|
|
349
|
+
const requestContainer = this.createRequestScope(routerContext);
|
|
350
|
+
try {
|
|
351
|
+
return await callback(requestContainer);
|
|
352
|
+
} finally {
|
|
353
|
+
await requestContainer.dispose();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Create request scope container
|
|
358
|
+
*
|
|
359
|
+
* Can only be called on global container (not request-scoped).
|
|
360
|
+
*/
|
|
361
|
+
createRequestScope(routerContext) {
|
|
362
|
+
if (this.isRequestScoped) throw new RequestScopeOperationNotAllowedError("createRequestScope");
|
|
363
|
+
const childContainer = this.container.createChildContainer();
|
|
364
|
+
childContainer.register(ROUTER_TOKENS.RouterContext, { useValue: routerContext });
|
|
365
|
+
return new Container({
|
|
366
|
+
container: childContainer,
|
|
367
|
+
isRequestScoped: true
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Get underlying tsyringe container
|
|
372
|
+
*/
|
|
373
|
+
getTsyringeContainer() {
|
|
374
|
+
return this.container;
|
|
375
|
+
}
|
|
376
|
+
dispose() {
|
|
377
|
+
return this.container.dispose();
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/di/types.ts
|
|
382
|
+
/**
|
|
383
|
+
* DI Type Definitions
|
|
384
|
+
*
|
|
385
|
+
* Core type definitions for the dependency injection system.
|
|
386
|
+
* Simplified after removing LazyProxy - no more type wrappers needed.
|
|
387
|
+
*/
|
|
388
|
+
/**
|
|
389
|
+
* Service scope for DI registration
|
|
390
|
+
*
|
|
391
|
+
* Maps directly to tsyringe's Lifecycle enum.
|
|
392
|
+
* Scope is specified at registration time via provider configuration,
|
|
393
|
+
* not at class decoration time.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```typescript
|
|
397
|
+
* // In module providers:
|
|
398
|
+
* { provide: MY_TOKEN, useClass: MyService, scope: Scope.Singleton }
|
|
399
|
+
*
|
|
400
|
+
* // In Application.ts:
|
|
401
|
+
* container.register(MY_TOKEN, MyService, Scope.Request)
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
let Scope = /* @__PURE__ */ function(Scope) {
|
|
405
|
+
/** New instance per resolution (default) */
|
|
406
|
+
Scope[Scope["Transient"] = Lifecycle.Transient] = "Transient";
|
|
407
|
+
/** Single instance shared globally */
|
|
408
|
+
Scope[Scope["Singleton"] = Lifecycle.Singleton] = "Singleton";
|
|
409
|
+
/** New instance per child container (per request) */
|
|
410
|
+
Scope[Scope["Request"] = Lifecycle.ContainerScoped] = "Request";
|
|
411
|
+
return Scope;
|
|
412
|
+
}({});
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region src/errors/error-codes.ts
|
|
415
|
+
/**
|
|
416
|
+
* Centralized Error Code Registry
|
|
417
|
+
*
|
|
418
|
+
* Error codes are organized by category with specific ranges:
|
|
419
|
+
* - 1000-1999: Validation errors
|
|
420
|
+
* - 2000-2999: Database errors (generic)
|
|
421
|
+
* - 3000-3999: Authentication & Authorization
|
|
422
|
+
* - 4000-4999: Resource errors
|
|
423
|
+
* - 5000-5999: Domain-specific business logic (per module)
|
|
424
|
+
* - 9000-9999: System/Internal errors
|
|
425
|
+
* - 9000-9099: Router errors
|
|
426
|
+
* - 9100-9199: Configuration errors
|
|
427
|
+
* - 9200-9299: Infrastructure errors
|
|
428
|
+
* - 9300-9399: I18n errors
|
|
429
|
+
*/
|
|
430
|
+
const ERROR_CODES = {
|
|
431
|
+
DATABASE: {
|
|
432
|
+
GENERIC: 2e3,
|
|
433
|
+
RECORD_NOT_FOUND: 2001,
|
|
434
|
+
UNIQUE_CONSTRAINT: 2002,
|
|
435
|
+
FOREIGN_KEY_CONSTRAINT: 2003,
|
|
436
|
+
CONNECTION_FAILED: 2004,
|
|
437
|
+
TIMEOUT: 2005,
|
|
438
|
+
NULL_CONSTRAINT: 2006,
|
|
439
|
+
TOO_MANY_CONNECTIONS: 2007,
|
|
440
|
+
TRANSACTION_CONFLICT: 2008
|
|
441
|
+
},
|
|
442
|
+
AUTH: {
|
|
443
|
+
INVALID_CREDENTIALS: 3e3,
|
|
444
|
+
SESSION_EXPIRED: 3001,
|
|
445
|
+
ACCOUNT_LOCKED: 3002,
|
|
446
|
+
INVALID_TOKEN: 3003,
|
|
447
|
+
CONTEXT_NOT_INITIALIZED: 3004,
|
|
448
|
+
USER_NOT_AUTHENTICATED: 3005,
|
|
449
|
+
EMAIL_NOT_VERIFIED: 3007,
|
|
450
|
+
PASSWORD_TOO_SHORT: 3008,
|
|
451
|
+
PASSWORD_TOO_LONG: 3009,
|
|
452
|
+
ACCOUNT_ALREADY_EXISTS: 3010,
|
|
453
|
+
FAILED_TO_CREATE_USER: 3011,
|
|
454
|
+
FAILED_TO_CREATE_SESSION: 3012,
|
|
455
|
+
FAILED_TO_UPDATE_USER: 3013,
|
|
456
|
+
SOCIAL_ACCOUNT_LINKED: 3014,
|
|
457
|
+
CANNOT_UNLINK_LAST_ACCOUNT: 3015
|
|
458
|
+
},
|
|
459
|
+
AUTHZ: {
|
|
460
|
+
FORBIDDEN: 3100,
|
|
461
|
+
ACCESS_DENIED: 3101,
|
|
462
|
+
INSUFFICIENT_PERMISSIONS: 3102
|
|
463
|
+
},
|
|
464
|
+
RESOURCE: {
|
|
465
|
+
NOT_FOUND: 4e3,
|
|
466
|
+
ROUTE_NOT_FOUND: 4004,
|
|
467
|
+
CONFLICT: 4100,
|
|
468
|
+
ALREADY_EXISTS: 4101
|
|
469
|
+
},
|
|
470
|
+
VALIDATION: {
|
|
471
|
+
GENERIC: 1e3,
|
|
472
|
+
REQUIRED_FIELD: 1001,
|
|
473
|
+
INVALID_FORMAT: 1002,
|
|
474
|
+
SCHEMA_VALIDATION: 1003,
|
|
475
|
+
REQUEST_VALIDATION: 1004,
|
|
476
|
+
RESPONSE_VALIDATION: 1005
|
|
477
|
+
},
|
|
478
|
+
ROUTER: {
|
|
479
|
+
CONTROLLER_REGISTRATION_ERROR: 9005,
|
|
480
|
+
CONTROLLER_METHOD_NOT_FOUND: 9006,
|
|
481
|
+
OPENAPI_ROUTE_REGISTRATION: 9008,
|
|
482
|
+
DUPLICATE_ROUTE_NAME: 9010,
|
|
483
|
+
ROUTE_NAME_NOT_FOUND: 9011,
|
|
484
|
+
MISSING_ROUTE_PARAM: 9012,
|
|
485
|
+
USE_SCOPE_VIOLATION: 9013
|
|
486
|
+
},
|
|
487
|
+
I18N: {
|
|
488
|
+
TRANSLATION_MISSING: 9300,
|
|
489
|
+
LOCALE_NOT_SUPPORTED: 9301
|
|
490
|
+
},
|
|
491
|
+
SYSTEM: {
|
|
492
|
+
INTERNAL_ERROR: 9e3,
|
|
493
|
+
CONFIGURATION_ERROR: 9100,
|
|
494
|
+
CONFIG_NOT_INITIALIZED: 9101,
|
|
495
|
+
MODULE_ALREADY_REGISTERED: 9102,
|
|
496
|
+
MODULE_CIRCULAR_DEPENDENCY: 9103,
|
|
497
|
+
MODULE_DEPENDENCY_NOT_FOUND: 9104,
|
|
498
|
+
INVALID_ERROR_CODE_RANGE: 9105,
|
|
499
|
+
INVALID_MODULE_PROVIDER: 9106,
|
|
500
|
+
CONFIG_MODULE_NOT_INITIALIZED: 9107,
|
|
501
|
+
INFRASTRUCTURE_ERROR: 9200,
|
|
502
|
+
EXECUTION_CONTEXT_NOT_INITIALIZED: 9201,
|
|
503
|
+
REQUEST_CONTAINER_NOT_INITIALIZED: 9202,
|
|
504
|
+
QUEUE_BINDING_NOT_FOUND: 9203,
|
|
505
|
+
CRON_EXECUTION_FAILED: 9204,
|
|
506
|
+
QUEUE_PROVIDER_NOT_SUPPORTED: 9205,
|
|
507
|
+
WEBSOCKET_BODY_NOT_AVAILABLE: 9206,
|
|
508
|
+
WEBSOCKET_DUPLICATE_EVENT_HANDLER: 9207,
|
|
509
|
+
SEEDER_NAME_COLLISION: 9208,
|
|
510
|
+
SEEDER_NOT_REGISTERED: 9209,
|
|
511
|
+
CONTAINER_NOT_INITIALIZED: 9210,
|
|
512
|
+
MISSING_ENVIRONMENT_VARIABLE: 9211
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/errors/container-not-initialized.error.ts
|
|
517
|
+
/**
|
|
518
|
+
* Thrown when attempting to access the application container via AsyncLocalStorage
|
|
519
|
+
* before `Application.initialize()` has been called.
|
|
520
|
+
*
|
|
521
|
+
* This typically means `route()` or another standalone function is being called
|
|
522
|
+
* outside the application lifecycle.
|
|
523
|
+
*/
|
|
524
|
+
var ContainerNotInitializedError = class extends ApplicationError {
|
|
525
|
+
constructor() {
|
|
526
|
+
super("errors.containerNotInitialized", ERROR_CODES.SYSTEM.CONTAINER_NOT_INITIALIZED);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/di/container-storage.ts
|
|
531
|
+
/**
|
|
532
|
+
* AsyncLocalStorage for the application container.
|
|
533
|
+
*
|
|
534
|
+
* Set by `Application.initialize()` — all code from that point onward
|
|
535
|
+
* (Stratal handlers, TestingModuleBuilder, standalone functions like `route()`)
|
|
536
|
+
* can access the container without DI or static singletons.
|
|
537
|
+
*
|
|
538
|
+
* Follows the same pattern as `errorMapContextStorage` in `i18n/validation/validation.context.ts`.
|
|
539
|
+
*/
|
|
540
|
+
const containerStorage = new AsyncLocalStorage();
|
|
541
|
+
/**
|
|
542
|
+
* Get the application container from AsyncLocalStorage.
|
|
543
|
+
*
|
|
544
|
+
* @throws ContainerNotInitializedError if called outside `Application.initialize()` scope
|
|
545
|
+
*/
|
|
546
|
+
function getContainer() {
|
|
547
|
+
const container = containerStorage.getStore();
|
|
548
|
+
if (!container) throw new ContainerNotInitializedError();
|
|
549
|
+
return container;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Run a function within a container context.
|
|
553
|
+
*
|
|
554
|
+
* @param container - The application container to store
|
|
555
|
+
* @param fn - The function to execute with container access
|
|
556
|
+
*/
|
|
557
|
+
function runWithContainer(container, fn) {
|
|
558
|
+
return containerStorage.run(container, fn);
|
|
559
|
+
}
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/i18n/i18n.tokens.ts
|
|
562
|
+
/**
|
|
563
|
+
* I18n Module DI Tokens
|
|
564
|
+
* Symbol-based tokens to avoid string collisions
|
|
565
|
+
*/
|
|
566
|
+
const I18N_TOKENS = {
|
|
567
|
+
MessageLoader: Symbol.for("stratal:i18n:message:loader"),
|
|
568
|
+
I18nService: Symbol.for("stratal:i18n:service"),
|
|
569
|
+
Options: Symbol.for("stratal:i18n:options"),
|
|
570
|
+
MessageRegistry: Symbol.for("stratal:i18n:message:registry")
|
|
571
|
+
};
|
|
572
|
+
//#endregion
|
|
573
|
+
//#region src/errors/http-exception.ts
|
|
574
|
+
/**
|
|
575
|
+
* Maps common HTTP status codes to their default error codes.
|
|
576
|
+
* Used by {@link HttpException} to derive the error code automatically.
|
|
577
|
+
*/
|
|
578
|
+
const HTTP_STATUS_TO_ERROR_CODE = {
|
|
579
|
+
400: ERROR_CODES.VALIDATION.GENERIC,
|
|
580
|
+
401: ERROR_CODES.AUTH.USER_NOT_AUTHENTICATED,
|
|
581
|
+
403: ERROR_CODES.AUTHZ.FORBIDDEN,
|
|
582
|
+
404: ERROR_CODES.RESOURCE.NOT_FOUND,
|
|
583
|
+
409: ERROR_CODES.RESOURCE.CONFLICT,
|
|
584
|
+
422: ERROR_CODES.VALIDATION.GENERIC,
|
|
585
|
+
500: ERROR_CODES.SYSTEM.INTERNAL_ERROR
|
|
586
|
+
};
|
|
587
|
+
/**
|
|
588
|
+
* Default human-readable messages for common HTTP status codes.
|
|
589
|
+
* Used as fallback when no message is provided to {@link HttpException}.
|
|
590
|
+
*/
|
|
591
|
+
const HTTP_STATUS_MESSAGES = {
|
|
592
|
+
400: "Bad Request",
|
|
593
|
+
401: "Unauthorized",
|
|
594
|
+
403: "Forbidden",
|
|
595
|
+
404: "Not Found",
|
|
596
|
+
409: "Conflict",
|
|
597
|
+
422: "Unprocessable Entity",
|
|
598
|
+
500: "Internal Server Error"
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* HTTP-centric exception base class.
|
|
602
|
+
*
|
|
603
|
+
* Unlike {@link ApplicationError} which requires `(i18nKey, code, metadata)`,
|
|
604
|
+
* `HttpException` takes just `(httpStatus, message?)` and derives the error code
|
|
605
|
+
* from the HTTP status automatically.
|
|
606
|
+
*
|
|
607
|
+
* The message can be a plain string or an i18n key — the {@link ExceptionHandler}
|
|
608
|
+
* tries to translate it via `i18n.t()`, falling back to the raw string if the
|
|
609
|
+
* key is not found.
|
|
610
|
+
*
|
|
611
|
+
* Existing {@link ApplicationError} subclasses can be migrated to this gradually.
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* ```typescript
|
|
615
|
+
* // Simple usage with plain message
|
|
616
|
+
* throw new HttpException(404, 'User not found')
|
|
617
|
+
*
|
|
618
|
+
* // With i18n key (auto-translated if key exists)
|
|
619
|
+
* throw new HttpException(422, 'errors.invalidInput')
|
|
620
|
+
*
|
|
621
|
+
* // Default message for status code
|
|
622
|
+
* throw new HttpException(500)
|
|
623
|
+
*
|
|
624
|
+
* // Subclass for domain-specific errors
|
|
625
|
+
* class PaymentDeclinedError extends HttpException {
|
|
626
|
+
* constructor() {
|
|
627
|
+
* super(402, 'errors.paymentDeclined')
|
|
628
|
+
* }
|
|
629
|
+
* }
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
var HttpException = class extends ApplicationError {
|
|
633
|
+
/**
|
|
634
|
+
* The HTTP status code for this exception.
|
|
635
|
+
* Used by the {@link ExceptionHandler} to set the response status.
|
|
636
|
+
*/
|
|
637
|
+
httpStatus;
|
|
638
|
+
/**
|
|
639
|
+
* @param httpStatus - HTTP status code (e.g., 404, 422, 500)
|
|
640
|
+
* @param message - Optional message string or i18n key. Defaults to the
|
|
641
|
+
* standard HTTP status message (e.g., "Not Found" for 404).
|
|
642
|
+
*/
|
|
643
|
+
constructor(httpStatus, message) {
|
|
644
|
+
const code = HTTP_STATUS_TO_ERROR_CODE[httpStatus] ?? ERROR_CODES.SYSTEM.INTERNAL_ERROR;
|
|
645
|
+
const messageStr = message ?? HTTP_STATUS_MESSAGES[httpStatus] ?? "Internal Server Error";
|
|
646
|
+
super(messageStr, code);
|
|
647
|
+
this.httpStatus = httpStatus;
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* Throw an HTTP exception from anywhere in the application.
|
|
652
|
+
*
|
|
653
|
+
* The message can be a plain string or an i18n key — the {@link ExceptionHandler}
|
|
654
|
+
* translates it automatically, falling back to the raw string if the key is not found.
|
|
655
|
+
*
|
|
656
|
+
* @param status - HTTP status code
|
|
657
|
+
* @param message - Optional message (plain string or i18n key)
|
|
658
|
+
* @throws {@link HttpException} — always throws, never returns
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```typescript
|
|
662
|
+
* // With plain message
|
|
663
|
+
* abort(404, 'User not found')
|
|
664
|
+
*
|
|
665
|
+
* // Default message for status
|
|
666
|
+
* abort(403)
|
|
667
|
+
*
|
|
668
|
+
* // With i18n key
|
|
669
|
+
* abort(422, 'errors.invalidInput')
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
function abort(status, message) {
|
|
673
|
+
throw new HttpException(status, message);
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region src/errors/get-http-status.ts
|
|
677
|
+
/**
|
|
678
|
+
* Maps error codes to HTTP status codes
|
|
679
|
+
*
|
|
680
|
+
* This utility is used by the frontend to set appropriate HTTP status codes
|
|
681
|
+
* when returning errors from API routes.
|
|
682
|
+
*
|
|
683
|
+
* @param code - Numeric error code from ERROR_CODES registry
|
|
684
|
+
* @returns HTTP status code (200-599)
|
|
685
|
+
*/
|
|
686
|
+
function getHttpStatus(code) {
|
|
687
|
+
if (code >= 1e3 && code < 2e3) return 400;
|
|
688
|
+
if (code >= 2e3 && code < 3e3) {
|
|
689
|
+
if (code === ERROR_CODES.DATABASE.RECORD_NOT_FOUND) return 404;
|
|
690
|
+
if (code === ERROR_CODES.DATABASE.UNIQUE_CONSTRAINT) return 409;
|
|
691
|
+
return 500;
|
|
692
|
+
}
|
|
693
|
+
if (code >= 3e3 && code < 3100) return 401;
|
|
694
|
+
if (code >= 3100 && code < 3200) return 403;
|
|
695
|
+
if (code >= 4e3 && code < 4100) return 404;
|
|
696
|
+
if (code >= 4100 && code < 4200) return 409;
|
|
697
|
+
if (code >= 5e3 && code < 6e3) {
|
|
698
|
+
if (code === 5e3 || code === 5100 || code === 5200) return 404;
|
|
699
|
+
return 422;
|
|
700
|
+
}
|
|
701
|
+
if (code >= 9e3) return 500;
|
|
702
|
+
return 500;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Resolve the HTTP status code for an ApplicationError.
|
|
706
|
+
*
|
|
707
|
+
* If the error is an {@link HttpException}, its `httpStatus` property takes precedence.
|
|
708
|
+
* Otherwise, falls back to the code-range-based mapping via {@link getHttpStatus}.
|
|
709
|
+
*
|
|
710
|
+
* @param error - The application error to resolve the status for
|
|
711
|
+
* @returns HTTP status code
|
|
712
|
+
*/
|
|
713
|
+
function resolveHttpStatus(error) {
|
|
714
|
+
if (error instanceof HttpException) return error.httpStatus;
|
|
715
|
+
return getHttpStatus(error.code);
|
|
716
|
+
}
|
|
717
|
+
//#endregion
|
|
718
|
+
//#region src/errors/internal-error.ts
|
|
719
|
+
/**
|
|
720
|
+
* InternalError
|
|
721
|
+
*
|
|
722
|
+
* Represents an unexpected internal server error.
|
|
723
|
+
* Used to wrap unknown errors that don't fit into specific error categories.
|
|
724
|
+
*
|
|
725
|
+
* This error is thrown when:
|
|
726
|
+
* - An unexpected exception occurs
|
|
727
|
+
* - An error type is not recognized
|
|
728
|
+
* - A system-level failure happens
|
|
729
|
+
*/
|
|
730
|
+
var InternalError = class extends ApplicationError {
|
|
731
|
+
constructor(metadata) {
|
|
732
|
+
super("errors.internalError", ERROR_CODES.SYSTEM.INTERNAL_ERROR, metadata);
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
//#endregion
|
|
736
|
+
//#region src/errors/is-application-error.ts
|
|
737
|
+
/**
|
|
738
|
+
* Type guard to check if an error is an ApplicationError
|
|
739
|
+
*
|
|
740
|
+
* @param error - The error to check
|
|
741
|
+
* @returns True if the error is an ApplicationError instance
|
|
742
|
+
*/
|
|
743
|
+
function isApplicationError(error) {
|
|
744
|
+
return error instanceof ApplicationError;
|
|
745
|
+
}
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/errors/exception-handler.ts
|
|
748
|
+
let ExceptionHandler = class ExceptionHandler {
|
|
749
|
+
reportables = [];
|
|
750
|
+
renderables = [];
|
|
751
|
+
dontReportSet = /* @__PURE__ */ new Set();
|
|
752
|
+
levelOverrides = /* @__PURE__ */ new Map();
|
|
753
|
+
contextCallbacks = [];
|
|
754
|
+
respondCallbacks = [];
|
|
755
|
+
environment;
|
|
756
|
+
constructor(logger, env, container, executionContext) {
|
|
757
|
+
this.logger = logger;
|
|
758
|
+
this.env = env;
|
|
759
|
+
this.container = container;
|
|
760
|
+
this.executionContext = executionContext;
|
|
761
|
+
this.environment = this.env.ENVIRONMENT;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Register a custom reporting callback for a specific exception type.
|
|
765
|
+
*
|
|
766
|
+
* The callback is invoked when an error matching `errorClass` (via `instanceof`)
|
|
767
|
+
* is thrown. Chain `.stop()` to prevent the default logger from also reporting.
|
|
768
|
+
*
|
|
769
|
+
* @typeParam T - The exception type to match
|
|
770
|
+
* @param errorClass - Constructor of the exception to match
|
|
771
|
+
* @param callback - Reporting function receiving the typed error and context
|
|
772
|
+
* @returns A {@link Reportable} with a `stop()` method
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* ```typescript
|
|
776
|
+
* this.reportable(PaymentError, (e, ctx) => {
|
|
777
|
+
* sentry.captureException(e)
|
|
778
|
+
* }).stop() // skip default logging
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
reportable(errorClass, callback) {
|
|
782
|
+
const entry = {
|
|
783
|
+
errorClass,
|
|
784
|
+
callback,
|
|
785
|
+
shouldStop: false
|
|
786
|
+
};
|
|
787
|
+
this.reportables.push(entry);
|
|
788
|
+
return { stop: () => {
|
|
789
|
+
entry.shouldStop = true;
|
|
790
|
+
} };
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Register a custom rendering callback for a specific exception type.
|
|
794
|
+
*
|
|
795
|
+
* The callback should return a `Response` (for HTTP contexts), an `ErrorResponse`,
|
|
796
|
+
* or `undefined` to fall through to the default renderer.
|
|
797
|
+
*
|
|
798
|
+
* @typeParam T - The exception type to match
|
|
799
|
+
* @param errorClass - Constructor of the exception to match
|
|
800
|
+
* @param callback - Rendering function receiving the typed error and context
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```typescript
|
|
804
|
+
* this.renderable(MaintenanceError, (e, ctx) => {
|
|
805
|
+
* if (ctx.type === 'http') {
|
|
806
|
+
* return ctx.ctx.html('<h1>Down for maintenance</h1>', 503)
|
|
807
|
+
* }
|
|
808
|
+
* })
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
renderable(errorClass, callback) {
|
|
812
|
+
this.renderables.push({
|
|
813
|
+
errorClass,
|
|
814
|
+
callback
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Suppress reporting (logging) for the given exception types.
|
|
819
|
+
*
|
|
820
|
+
* Errors matching these classes will still be rendered into responses
|
|
821
|
+
* but will not be logged or sent to external reporters.
|
|
822
|
+
*
|
|
823
|
+
* @param errorClasses - Array of exception constructors to suppress
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* this.dontReport([RouteNotFoundError, SchemaValidationError])
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
dontReport(errorClasses) {
|
|
831
|
+
for (const cls of errorClasses) this.dontReportSet.add(cls);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Override the log severity for a specific exception type.
|
|
835
|
+
*
|
|
836
|
+
* By default, severity is derived from the error code range.
|
|
837
|
+
* Use this to promote or demote specific errors.
|
|
838
|
+
*
|
|
839
|
+
* @param errorClass - Constructor of the exception to override
|
|
840
|
+
* @param severity - The log severity to use
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```typescript
|
|
844
|
+
* this.level(RecordNotFoundError, 'warn')
|
|
845
|
+
* ```
|
|
846
|
+
*/
|
|
847
|
+
level(errorClass, severity) {
|
|
848
|
+
this.levelOverrides.set(errorClass, severity);
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Add global context data to all exception log entries.
|
|
852
|
+
*
|
|
853
|
+
* The callback is invoked on every reported error and its return value
|
|
854
|
+
* is merged into the log data.
|
|
855
|
+
*
|
|
856
|
+
* @param callback - Function returning key-value pairs to include in logs
|
|
857
|
+
*
|
|
858
|
+
* @example
|
|
859
|
+
* ```typescript
|
|
860
|
+
* this.context(() => ({
|
|
861
|
+
* appVersion: '1.2.3',
|
|
862
|
+
* region: env.CF_REGION,
|
|
863
|
+
* }))
|
|
864
|
+
* ```
|
|
865
|
+
*/
|
|
866
|
+
context(callback) {
|
|
867
|
+
this.contextCallbacks.push(callback);
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Register a callback to post-process every error Response before it is returned.
|
|
871
|
+
*
|
|
872
|
+
* Use this to add headers, modify the body, change content type, or
|
|
873
|
+
* transform the response in any way.
|
|
874
|
+
*
|
|
875
|
+
* @param callback - Function receiving (response, error, context) and returning a Response
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* ```typescript
|
|
879
|
+
* this.respond((response, error, ctx) => {
|
|
880
|
+
* response.headers.set('X-Error-Code', String(error.code))
|
|
881
|
+
* return response
|
|
882
|
+
* })
|
|
883
|
+
* ```
|
|
884
|
+
*/
|
|
885
|
+
respond(callback) {
|
|
886
|
+
this.respondCallbacks.push(callback);
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Resolve a service from the DI container.
|
|
890
|
+
*
|
|
891
|
+
* Useful inside `register()` callbacks for accessing injected services
|
|
892
|
+
* (e.g., Sentry, analytics, custom loggers).
|
|
893
|
+
*
|
|
894
|
+
* @typeParam T - The type of the service to resolve
|
|
895
|
+
* @param token - DI token (symbol or constructor)
|
|
896
|
+
* @returns The resolved service instance
|
|
897
|
+
*
|
|
898
|
+
* @example
|
|
899
|
+
* ```typescript
|
|
900
|
+
* this.reportable(CriticalError, (e) => {
|
|
901
|
+
* this.resolve(SentryService).captureException(e)
|
|
902
|
+
* })
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
resolve(token) {
|
|
906
|
+
return this.container.resolve(token);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Handle an error through the full exception pipeline.
|
|
910
|
+
*
|
|
911
|
+
* This is the single entry point used by all contexts (HTTP, queue, cron, CLI).
|
|
912
|
+
* It normalizes the error, reports it (non-blocking via `waitUntil`),
|
|
913
|
+
* renders it into a Response, and applies post-processing.
|
|
914
|
+
*
|
|
915
|
+
* @param error - The thrown error (may or may not be an ApplicationError)
|
|
916
|
+
* @param context - The execution context where the error occurred
|
|
917
|
+
* @returns A Response (JSON by default, customizable via renderable/respond)
|
|
918
|
+
*/
|
|
919
|
+
async handle(error, context) {
|
|
920
|
+
const appError = this.normalizeError(error);
|
|
921
|
+
this.executionContext.waitUntil(this.performReport(appError, context));
|
|
922
|
+
const response = await this.performRender(appError, context);
|
|
923
|
+
return this.applyRespondCallbacks(response, appError, context);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Normalize an unknown error into an ApplicationError.
|
|
927
|
+
* Non-ApplicationError values are wrapped in InternalError.
|
|
928
|
+
*/
|
|
929
|
+
normalizeError(error) {
|
|
930
|
+
if (isApplicationError(error)) return error;
|
|
931
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
932
|
+
const internalError = new InternalError({
|
|
933
|
+
originalError: originalMessage,
|
|
934
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
935
|
+
});
|
|
936
|
+
if (this.environment === "development") {
|
|
937
|
+
internalError.message = originalMessage;
|
|
938
|
+
if (error instanceof Error && error.stack) internalError.stack = error.stack;
|
|
939
|
+
}
|
|
940
|
+
return internalError;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Run the reporting pipeline for an error.
|
|
944
|
+
*/
|
|
945
|
+
async performReport(error, context) {
|
|
946
|
+
if (typeof error.report === "function") {
|
|
947
|
+
if (error.report() !== false) return;
|
|
948
|
+
}
|
|
949
|
+
if (this.shouldNotReport(error)) return;
|
|
950
|
+
const entry = this.findReportable(error);
|
|
951
|
+
if (entry) {
|
|
952
|
+
await entry.callback(error, context);
|
|
953
|
+
if (entry.shouldStop) return;
|
|
954
|
+
}
|
|
955
|
+
this.defaultReport(error, context);
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Run the rendering pipeline for an error, producing a Response.
|
|
959
|
+
*/
|
|
960
|
+
async performRender(error, context) {
|
|
961
|
+
if (typeof error.render === "function") {
|
|
962
|
+
const result = error.render(context);
|
|
963
|
+
if (result !== void 0) return this.toResponse(result, error);
|
|
964
|
+
}
|
|
965
|
+
const entry = this.findRenderable(error);
|
|
966
|
+
if (entry) {
|
|
967
|
+
const result = entry.callback(error, context);
|
|
968
|
+
if (result !== void 0) return this.toResponse(await result, error);
|
|
969
|
+
}
|
|
970
|
+
return this.defaultRender(error, context);
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Apply all respond() callbacks to post-process a Response.
|
|
974
|
+
*/
|
|
975
|
+
applyRespondCallbacks(response, error, context) {
|
|
976
|
+
let result = response;
|
|
977
|
+
for (const callback of this.respondCallbacks) result = callback(result, error, context);
|
|
978
|
+
return result;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Check if an error is in the dontReport set.
|
|
982
|
+
*/
|
|
983
|
+
shouldNotReport(error) {
|
|
984
|
+
for (const cls of this.dontReportSet) if (error instanceof cls) return true;
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Find the most-specific reportable entry for an error.
|
|
989
|
+
* Walks entries in registration order; picks the most-specific `instanceof` match.
|
|
990
|
+
*/
|
|
991
|
+
findReportable(error) {
|
|
992
|
+
let best;
|
|
993
|
+
for (const entry of this.reportables) if (error instanceof entry.errorClass) {
|
|
994
|
+
if (!best || !(error instanceof best.errorClass) || entry.errorClass.prototype instanceof best.errorClass) best = entry;
|
|
995
|
+
}
|
|
996
|
+
return best;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Find the most-specific renderable entry for an error.
|
|
1000
|
+
*/
|
|
1001
|
+
findRenderable(error) {
|
|
1002
|
+
let best;
|
|
1003
|
+
for (const entry of this.renderables) if (error instanceof entry.errorClass) {
|
|
1004
|
+
if (!best || !(error instanceof best.errorClass) || entry.errorClass.prototype instanceof best.errorClass) best = entry;
|
|
1005
|
+
}
|
|
1006
|
+
return best;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Default reporting — log with appropriate severity and i18n translation.
|
|
1010
|
+
*/
|
|
1011
|
+
defaultReport(error, context) {
|
|
1012
|
+
const translatedMessage = this.translateError(error, context);
|
|
1013
|
+
const severity = this.resolveSeverity(error);
|
|
1014
|
+
const globalContext = this.gatherContext();
|
|
1015
|
+
const logData = {
|
|
1016
|
+
code: error.code,
|
|
1017
|
+
message: translatedMessage,
|
|
1018
|
+
timestamp: error.timestamp,
|
|
1019
|
+
metadata: error.metadata,
|
|
1020
|
+
name: error.name,
|
|
1021
|
+
...globalContext
|
|
1022
|
+
};
|
|
1023
|
+
switch (severity) {
|
|
1024
|
+
case "debug":
|
|
1025
|
+
this.logger.debug("[ApplicationError]", logData);
|
|
1026
|
+
break;
|
|
1027
|
+
case "info":
|
|
1028
|
+
this.logger.info("[ApplicationError]", logData);
|
|
1029
|
+
break;
|
|
1030
|
+
case "warn":
|
|
1031
|
+
this.logger.warn("[ApplicationError]", logData);
|
|
1032
|
+
break;
|
|
1033
|
+
case "error":
|
|
1034
|
+
this.logger.error("[ApplicationError]", logData);
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Default rendering — content-negotiated.
|
|
1040
|
+
*
|
|
1041
|
+
* For HTTP requests that accept HTML: renders a minimal branded HTML page.
|
|
1042
|
+
* For everything else (API, queue, cron, CLI): returns JSON.
|
|
1043
|
+
*
|
|
1044
|
+
* Errors are always logged via `performReport` (non-blocking waitUntil),
|
|
1045
|
+
* so they appear in the console regardless of the rendered response format.
|
|
1046
|
+
*/
|
|
1047
|
+
defaultRender(error, context) {
|
|
1048
|
+
const translatedMessage = this.translateError(error, context);
|
|
1049
|
+
const errorResponse = error.toErrorResponse(this.environment, translatedMessage);
|
|
1050
|
+
const status = resolveHttpStatus(error);
|
|
1051
|
+
if (context.type === "http" && this.wantsHtml(context)) return this.renderDefaultHtml(errorResponse, status);
|
|
1052
|
+
return Response.json(errorResponse, { status });
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Check if the HTTP request prefers an HTML response.
|
|
1056
|
+
*
|
|
1057
|
+
* Uses the `Accept` header to determine format. Inertia v3 XHR requests
|
|
1058
|
+
* send `Accept: text/html, application/xhtml+xml`, so they naturally
|
|
1059
|
+
* receive HTML error pages (displayed in Inertia's error modal in dev).
|
|
1060
|
+
*
|
|
1061
|
+
* Override in a subclass to customize content negotiation logic.
|
|
1062
|
+
*/
|
|
1063
|
+
wantsHtml(context) {
|
|
1064
|
+
return (context.ctx.c.req.header("accept") ?? "").includes("text/html");
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Minimal production HTML error page with inline styles.
|
|
1068
|
+
*/
|
|
1069
|
+
renderDefaultHtml(errorResponse, status) {
|
|
1070
|
+
const title = this.escapeHtml(errorResponse.message);
|
|
1071
|
+
const html = `<!DOCTYPE html>
|
|
1072
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
1073
|
+
<title>${status} - ${title}</title>
|
|
1074
|
+
<style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,-apple-system,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f8fafc;color:#334155}.container{text-align:center;padding:2rem}.status{font-size:6rem;font-weight:800;color:#13c397;line-height:1}.message{font-size:1.25rem;color:#64748b;margin-top:1rem}</style>
|
|
1075
|
+
</head><body><div class="container"><div class="status">${status}</div><div class="message">${title}</div></div></body></html>`;
|
|
1076
|
+
return new Response(html, {
|
|
1077
|
+
status,
|
|
1078
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
escapeHtml(str) {
|
|
1082
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Convert a render result (Response or ErrorResponse) into a Response.
|
|
1086
|
+
*/
|
|
1087
|
+
toResponse(result, error) {
|
|
1088
|
+
if (result instanceof Response) return result;
|
|
1089
|
+
const status = resolveHttpStatus(error);
|
|
1090
|
+
return Response.json(result, { status });
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Translate an error's message key via i18n.
|
|
1094
|
+
* Uses the request container (from HTTP context) for correct locale,
|
|
1095
|
+
* falling back to the global container or raw message string.
|
|
1096
|
+
*/
|
|
1097
|
+
translateError(error, context) {
|
|
1098
|
+
try {
|
|
1099
|
+
const i18n = (context.type === "http" ? context.ctx.getContainer() : this.container).resolve(I18N_TOKENS.I18nService);
|
|
1100
|
+
const params = error.metadata;
|
|
1101
|
+
return i18n.t(error.message, params);
|
|
1102
|
+
} catch {
|
|
1103
|
+
return error.message;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Resolve the log severity for an error.
|
|
1108
|
+
* Checks level overrides first, then falls back to code-range-based severity.
|
|
1109
|
+
*/
|
|
1110
|
+
resolveSeverity(error) {
|
|
1111
|
+
let bestClass;
|
|
1112
|
+
let bestSeverity;
|
|
1113
|
+
for (const [cls, severity] of this.levelOverrides) if (error instanceof cls) {
|
|
1114
|
+
if (!bestClass || cls.prototype instanceof bestClass) {
|
|
1115
|
+
bestClass = cls;
|
|
1116
|
+
bestSeverity = severity;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return bestSeverity ?? this.getDefaultSeverity(error.code);
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Determine default log severity based on error code range.
|
|
1123
|
+
*/
|
|
1124
|
+
getDefaultSeverity(code) {
|
|
1125
|
+
if (code >= 9e3) return "error";
|
|
1126
|
+
if (code >= 2e3 && code < 3e3) return "error";
|
|
1127
|
+
if (code >= 5e3 && code < 6e3) return "warn";
|
|
1128
|
+
if (code >= 1e3 && code < 2e3) return "info";
|
|
1129
|
+
if (code >= 3e3 && code < 5e3) return "warn";
|
|
1130
|
+
return "error";
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Gather all global context data from registered callbacks.
|
|
1134
|
+
*/
|
|
1135
|
+
gatherContext() {
|
|
1136
|
+
if (this.contextCallbacks.length === 0) return {};
|
|
1137
|
+
const merged = {};
|
|
1138
|
+
for (const callback of this.contextCallbacks) Object.assign(merged, callback());
|
|
1139
|
+
return merged;
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
ExceptionHandler = __decorate([
|
|
1143
|
+
Transient(),
|
|
1144
|
+
__decorateParam(0, inject(LOGGER_TOKENS.LoggerService)),
|
|
1145
|
+
__decorateParam(1, inject(DI_TOKENS.CloudflareEnv)),
|
|
1146
|
+
__decorateParam(2, inject(CONTAINER_TOKEN)),
|
|
1147
|
+
__decorateParam(3, inject(DI_TOKENS.ExecutionContext)),
|
|
1148
|
+
__decorateMetadata("design:paramtypes", [
|
|
1149
|
+
Object,
|
|
1150
|
+
Object,
|
|
1151
|
+
Object,
|
|
1152
|
+
Object
|
|
1153
|
+
])
|
|
1154
|
+
], ExceptionHandler);
|
|
1155
|
+
//#endregion
|
|
1156
|
+
//#region src/errors/default-exception-handler.ts
|
|
1157
|
+
let DefaultExceptionHandler = class DefaultExceptionHandler extends ExceptionHandler {
|
|
1158
|
+
register() {}
|
|
1159
|
+
};
|
|
1160
|
+
DefaultExceptionHandler = __decorate([Transient()], DefaultExceptionHandler);
|
|
1161
|
+
//#endregion
|
|
1162
|
+
//#region src/errors/error-response.ts
|
|
1163
|
+
/**
|
|
1164
|
+
* Type guard to check if an object is an ErrorResponse
|
|
1165
|
+
*/
|
|
1166
|
+
function isErrorResponse(obj) {
|
|
1167
|
+
return typeof obj === "object" && obj !== null && "code" in obj && typeof obj.code === "number" && "message" in obj && typeof obj.message === "string" && "timestamp" in obj && typeof obj.timestamp === "string";
|
|
1168
|
+
}
|
|
1169
|
+
//#endregion
|
|
1170
|
+
//#region src/router/constants.ts
|
|
1171
|
+
/**
|
|
1172
|
+
* Type-safe context keys for Hono router variables
|
|
1173
|
+
* Using symbols to avoid string collisions
|
|
1174
|
+
*/
|
|
1175
|
+
const ROUTER_CONTEXT_KEYS = {
|
|
1176
|
+
REQUEST_CONTAINER: "requestContainer",
|
|
1177
|
+
LOCALE: "locale"
|
|
1178
|
+
};
|
|
1179
|
+
/**
|
|
1180
|
+
* Metadata keys for storing route and controller configuration
|
|
1181
|
+
* Using symbols to avoid collisions with other decorators
|
|
1182
|
+
*/
|
|
1183
|
+
const ROUTE_METADATA_KEYS = {
|
|
1184
|
+
CONTROLLER_ROUTE: Symbol.for("stratal:controller:route"),
|
|
1185
|
+
CONTROLLER_OPTIONS: Symbol.for("stratal:controller:options"),
|
|
1186
|
+
CONTROLLER_MIDDLEWARES: Symbol.for("stratal:controller:middlewares"),
|
|
1187
|
+
ROUTE_CONFIG: Symbol.for("stratal:route:config"),
|
|
1188
|
+
DECORATED_METHODS: Symbol.for("stratal:decorated:methods"),
|
|
1189
|
+
AUTH_GUARD: Symbol.for("stratal:auth:guard"),
|
|
1190
|
+
GATEWAY_MARKER: Symbol.for("stratal:gateway:marker"),
|
|
1191
|
+
WS_ON_MESSAGE: Symbol.for("stratal:ws:on-message"),
|
|
1192
|
+
WS_ON_CLOSE: Symbol.for("stratal:ws:on-close"),
|
|
1193
|
+
WS_ON_ERROR: Symbol.for("stratal:ws:on-error")
|
|
1194
|
+
};
|
|
1195
|
+
/**
|
|
1196
|
+
* Security scheme identifiers for OpenAPI
|
|
1197
|
+
* These reference the security scheme definitions in security.schemas.ts
|
|
1198
|
+
*/
|
|
1199
|
+
const SECURITY_SCHEMES = {
|
|
1200
|
+
BEARER_AUTH: "bearerAuth",
|
|
1201
|
+
API_KEY: "apiKey",
|
|
1202
|
+
SESSION_COOKIE: "sessionCookie"
|
|
1203
|
+
};
|
|
1204
|
+
/**
|
|
1205
|
+
* HTTP method mapping for RESTful controller methods
|
|
1206
|
+
* Maps controller method names to HTTP verbs and path patterns
|
|
1207
|
+
*/
|
|
1208
|
+
const HTTP_METHODS = {
|
|
1209
|
+
index: {
|
|
1210
|
+
method: "get",
|
|
1211
|
+
path: ""
|
|
1212
|
+
},
|
|
1213
|
+
show: {
|
|
1214
|
+
method: "get",
|
|
1215
|
+
path: "/:id"
|
|
1216
|
+
},
|
|
1217
|
+
create: {
|
|
1218
|
+
method: "post",
|
|
1219
|
+
path: ""
|
|
1220
|
+
},
|
|
1221
|
+
update: {
|
|
1222
|
+
method: "put",
|
|
1223
|
+
path: "/:id"
|
|
1224
|
+
},
|
|
1225
|
+
patch: {
|
|
1226
|
+
method: "patch",
|
|
1227
|
+
path: "/:id"
|
|
1228
|
+
},
|
|
1229
|
+
destroy: {
|
|
1230
|
+
method: "delete",
|
|
1231
|
+
path: "/:id"
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
/**
|
|
1235
|
+
* Default success status codes for RESTful controller methods
|
|
1236
|
+
* Used by @Route() decorator to auto-derive response status
|
|
1237
|
+
*/
|
|
1238
|
+
const METHOD_STATUS_CODES = {
|
|
1239
|
+
index: 200,
|
|
1240
|
+
show: 200,
|
|
1241
|
+
create: 201,
|
|
1242
|
+
update: 200,
|
|
1243
|
+
patch: 200,
|
|
1244
|
+
destroy: 200
|
|
1245
|
+
};
|
|
1246
|
+
/**
|
|
1247
|
+
* Sentinel symbol to opt a controller out of versioning.
|
|
1248
|
+
* When used as the version, no prefix is applied even when defaultVersion is set.
|
|
1249
|
+
*/
|
|
1250
|
+
const VERSION_NEUTRAL = Symbol.for("stratal:version:neutral");
|
|
1251
|
+
/**
|
|
1252
|
+
* Default content type for request bodies and responses
|
|
1253
|
+
*/
|
|
1254
|
+
const DEFAULT_CONTENT_TYPE = "application/json";
|
|
1255
|
+
//#endregion
|
|
1256
|
+
//#region src/router/router-context.ts
|
|
1257
|
+
/**
|
|
1258
|
+
* Router context wrapper with helper methods
|
|
1259
|
+
*
|
|
1260
|
+
* Provides convenient access to Hono's context and common request/response operations.
|
|
1261
|
+
* The native Hono context is available via the `c` property for advanced use cases.
|
|
1262
|
+
*
|
|
1263
|
+
* @example
|
|
1264
|
+
* ```typescript
|
|
1265
|
+
* async index(ctx: RouterContext): Promise<Response> {
|
|
1266
|
+
* // Use helper methods
|
|
1267
|
+
* const users = await this.service.findAll()
|
|
1268
|
+
* return ctx.json(users)
|
|
1269
|
+
* }
|
|
1270
|
+
*
|
|
1271
|
+
* async show(ctx: RouterContext): Promise<Response> {
|
|
1272
|
+
* // Access route params
|
|
1273
|
+
* const id = ctx.param('id')
|
|
1274
|
+
* const user = await this.service.findById(id)
|
|
1275
|
+
* return ctx.json(user)
|
|
1276
|
+
* }
|
|
1277
|
+
*
|
|
1278
|
+
* async create(ctx: RouterContext): Promise<Response> {
|
|
1279
|
+
* // Parse request body
|
|
1280
|
+
* const body = await ctx.body<CreateUserInput>()
|
|
1281
|
+
* const user = await this.service.create(body)
|
|
1282
|
+
* return ctx.json(user, 201)
|
|
1283
|
+
* }
|
|
1284
|
+
* ```
|
|
1285
|
+
*/
|
|
1286
|
+
var RouterContext = class {
|
|
1287
|
+
/**
|
|
1288
|
+
* Native Hono context
|
|
1289
|
+
* Access for advanced use cases not covered by helper methods
|
|
1290
|
+
*/
|
|
1291
|
+
constructor(c) {
|
|
1292
|
+
this.c = c;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Get request-scoped DI container
|
|
1296
|
+
* Contains request-specific services and context (AuthContext)
|
|
1297
|
+
*
|
|
1298
|
+
* @throws Error if container not initialized
|
|
1299
|
+
*/
|
|
1300
|
+
getContainer() {
|
|
1301
|
+
const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
|
|
1302
|
+
if (!container) throw new RequestContainerNotInitializedError();
|
|
1303
|
+
return container;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Set locale for the current request
|
|
1307
|
+
*
|
|
1308
|
+
* @param locale - Locale code (e.g., 'en', 'fr')
|
|
1309
|
+
*/
|
|
1310
|
+
setLocale(locale) {
|
|
1311
|
+
this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale);
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Get locale for the current request
|
|
1315
|
+
*
|
|
1316
|
+
* @returns Current locale code
|
|
1317
|
+
*/
|
|
1318
|
+
getLocale() {
|
|
1319
|
+
return this.c.get(ROUTER_CONTEXT_KEYS.LOCALE) || "en";
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Return JSON response
|
|
1323
|
+
*
|
|
1324
|
+
* When data is null, automatically returns 204 No Content (configurable via status param).
|
|
1325
|
+
*
|
|
1326
|
+
* @param data - Data to serialize as JSON, or null for 204
|
|
1327
|
+
* @param status - HTTP status code (default: 200, or 204 when data is null)
|
|
1328
|
+
*/
|
|
1329
|
+
json(data, status) {
|
|
1330
|
+
if (data === null) return this.c.body(null, status ?? 204);
|
|
1331
|
+
return this.c.json(data, status);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Get route parameter value
|
|
1335
|
+
*
|
|
1336
|
+
* @param key - Parameter name (e.g., 'id' for /users/:id)
|
|
1337
|
+
*/
|
|
1338
|
+
param(key) {
|
|
1339
|
+
return this.c.req.valid("param")[key];
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Get query parameter value
|
|
1343
|
+
*
|
|
1344
|
+
* @param key - Query parameter name
|
|
1345
|
+
*/
|
|
1346
|
+
query(key) {
|
|
1347
|
+
const validated = this.c.req.valid("query");
|
|
1348
|
+
return key ? validated[key] : validated;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Get request header value
|
|
1352
|
+
*
|
|
1353
|
+
* @param name - Header name (case-insensitive)
|
|
1354
|
+
*/
|
|
1355
|
+
header(name) {
|
|
1356
|
+
return this.c.req.header(name);
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Get validated request body from OpenAPI route
|
|
1360
|
+
* Returns pre-validated data that has passed schema validation
|
|
1361
|
+
*
|
|
1362
|
+
* @returns Validated JSON body
|
|
1363
|
+
*/
|
|
1364
|
+
body() {
|
|
1365
|
+
return this.c.req.valid("json");
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Return text response
|
|
1369
|
+
*
|
|
1370
|
+
* @param text - Text content
|
|
1371
|
+
* @param status - HTTP status code (default: 200)
|
|
1372
|
+
*/
|
|
1373
|
+
text(text, status) {
|
|
1374
|
+
return this.c.text(text, status);
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Return HTML response
|
|
1378
|
+
*
|
|
1379
|
+
* @param html - HTML content
|
|
1380
|
+
* @param status - HTTP status code (default: 200)
|
|
1381
|
+
*/
|
|
1382
|
+
html(html, status) {
|
|
1383
|
+
return this.c.html(html, status);
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Generate a URL from a named route.
|
|
1387
|
+
*
|
|
1388
|
+
* Keys matching `:param` placeholders fill the path.
|
|
1389
|
+
* Domain params are consumed from the same object.
|
|
1390
|
+
* Extra keys become query string parameters.
|
|
1391
|
+
*
|
|
1392
|
+
* @param name - Named route identifier
|
|
1393
|
+
* @param params - Route params + domain params + extra query params
|
|
1394
|
+
* @param options - URL generation options (e.g., `{ absolute: true }`)
|
|
1395
|
+
*
|
|
1396
|
+
* @example
|
|
1397
|
+
* ```typescript
|
|
1398
|
+
* ctx.route('users.show', { id: '1' }) // '/v1/users/1'
|
|
1399
|
+
* ctx.route('users.show', { id: '1', q: 'test' }) // '/v1/users/1?q=test'
|
|
1400
|
+
* ```
|
|
1401
|
+
*/
|
|
1402
|
+
route(name, params, options) {
|
|
1403
|
+
return this.resolveUri().route(name, params, options);
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Get a domain parameter value from the current request.
|
|
1407
|
+
* Domain params are set by the domain matching middleware.
|
|
1408
|
+
*
|
|
1409
|
+
* @param key - Domain parameter name (e.g., 'tenant' from '{tenant}.myapp.com')
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* ```typescript
|
|
1413
|
+
* const tenant = ctx.domain('tenant')
|
|
1414
|
+
* ```
|
|
1415
|
+
*/
|
|
1416
|
+
domain(key) {
|
|
1417
|
+
return this.c.get(`domain:${key}`);
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Generate a signed URL from a named route.
|
|
1421
|
+
*
|
|
1422
|
+
* @param name - Named route identifier
|
|
1423
|
+
* @param params - Route params (same as route())
|
|
1424
|
+
* @param options - Signing options (e.g., expiresIn) and URL options
|
|
1425
|
+
* @returns Signed URL string with signature query param
|
|
1426
|
+
*/
|
|
1427
|
+
async signedUrl(name, params, options) {
|
|
1428
|
+
return this.resolveUri().signedRoute(name, params, options);
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Check if the current request has a valid signature.
|
|
1432
|
+
*
|
|
1433
|
+
* @returns true if the URL signature is valid and not expired
|
|
1434
|
+
*/
|
|
1435
|
+
async hasValidSignature() {
|
|
1436
|
+
return this.resolveUri().hasValidSignature();
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Redirect to another URL
|
|
1440
|
+
*
|
|
1441
|
+
* @param url - Target URL
|
|
1442
|
+
* @param status - HTTP status code (default: 302)
|
|
1443
|
+
*/
|
|
1444
|
+
redirect(url, status) {
|
|
1445
|
+
return this.c.redirect(url, status);
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Return a streaming response (binary/generic)
|
|
1449
|
+
*
|
|
1450
|
+
* @param callback - Async function that writes to the stream
|
|
1451
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
1452
|
+
*/
|
|
1453
|
+
stream(callback, onError) {
|
|
1454
|
+
return stream(this.c, callback, onError);
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Return a streaming text response
|
|
1458
|
+
*
|
|
1459
|
+
* Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
|
|
1460
|
+
*
|
|
1461
|
+
* @param callback - Async function that writes text to the stream
|
|
1462
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
1463
|
+
*/
|
|
1464
|
+
streamText(callback, onError) {
|
|
1465
|
+
this.c.header("Content-Encoding", "Identity");
|
|
1466
|
+
return streamText(this.c, callback, onError);
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Return a Server-Sent Events (SSE) streaming response
|
|
1470
|
+
*
|
|
1471
|
+
* Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.
|
|
1472
|
+
*
|
|
1473
|
+
* @param callback - Async function that writes SSE events to the stream
|
|
1474
|
+
* @param onError - Optional error handler called if an error occurs during streaming
|
|
1475
|
+
*/
|
|
1476
|
+
streamSSE(callback, onError) {
|
|
1477
|
+
this.c.header("Content-Encoding", "Identity");
|
|
1478
|
+
return streamSSE(this.c, callback, onError);
|
|
1479
|
+
}
|
|
1480
|
+
resolveUri() {
|
|
1481
|
+
return this.getContainer().resolve(ROUTER_TOKENS.Uri);
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
//#endregion
|
|
1485
|
+
//#region src/errors/exception-context.ts
|
|
1486
|
+
/**
|
|
1487
|
+
* Create an HTTP exception context from a Hono context.
|
|
1488
|
+
*
|
|
1489
|
+
* @param c - The raw Hono context from the request
|
|
1490
|
+
* @returns An {@link HttpExceptionContext} wrapping a RouterContext
|
|
1491
|
+
*/
|
|
1492
|
+
function createHttpExceptionContext(c) {
|
|
1493
|
+
return {
|
|
1494
|
+
type: "http",
|
|
1495
|
+
ctx: new RouterContext(c)
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Create a queue exception context.
|
|
1500
|
+
*
|
|
1501
|
+
* @param queueName - The name of the queue being processed
|
|
1502
|
+
* @returns A {@link QueueExceptionContext}
|
|
1503
|
+
*/
|
|
1504
|
+
function createQueueExceptionContext(queueName) {
|
|
1505
|
+
return {
|
|
1506
|
+
type: "queue",
|
|
1507
|
+
queueName
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Create a cron exception context.
|
|
1512
|
+
*
|
|
1513
|
+
* @returns A {@link CronExceptionContext}
|
|
1514
|
+
*/
|
|
1515
|
+
function createCronExceptionContext() {
|
|
1516
|
+
return { type: "cron" };
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Create a CLI command exception context.
|
|
1520
|
+
*
|
|
1521
|
+
* @param commandName - The name of the command that threw
|
|
1522
|
+
* @returns A {@link CliExceptionContext}
|
|
1523
|
+
*/
|
|
1524
|
+
function createCliExceptionContext(commandName) {
|
|
1525
|
+
return {
|
|
1526
|
+
type: "cli",
|
|
1527
|
+
commandName
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
//#endregion
|
|
1531
|
+
//#region src/errors/request-container-not-initialized.error.ts
|
|
1532
|
+
/**
|
|
1533
|
+
* RequestContainerNotInitializedError
|
|
1534
|
+
*
|
|
1535
|
+
* Thrown when attempting to access the request-scoped container before it has been initialized.
|
|
1536
|
+
* This typically indicates that the RouterService middleware hasn't run yet,
|
|
1537
|
+
* or the router context is being accessed outside of a request lifecycle.
|
|
1538
|
+
*/
|
|
1539
|
+
var RequestContainerNotInitializedError = class extends ApplicationError {
|
|
1540
|
+
constructor() {
|
|
1541
|
+
super("errors.requestContainerNotInitialized", ERROR_CODES.SYSTEM.REQUEST_CONTAINER_NOT_INITIALIZED);
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
//#endregion
|
|
1545
|
+
//#region src/errors/stratal-not-initialized.error.ts
|
|
1546
|
+
/**
|
|
1547
|
+
* StratalNotInitializedError
|
|
1548
|
+
*
|
|
1549
|
+
* Thrown when attempting to resolve the Application instance before Stratal has been instantiated.
|
|
1550
|
+
* This typically indicates that the Stratal instance is not exported as the default export.
|
|
1551
|
+
*/
|
|
1552
|
+
var StratalNotInitializedError = class extends ApplicationError {
|
|
1553
|
+
constructor() {
|
|
1554
|
+
super("errors.stratalNotInitialized", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
//#endregion
|
|
1558
|
+
export { Scope as A, ConditionalBindingFallbackError as B, abort as C, runWithContainer as D, getContainer as E, injectable$1 as F, ApplicationError as H, instancePerContainerCachingFactory$1 as I, singleton as L, container$1 as M, delay as N, ContainerNotInitializedError as O, inject$1 as P, ConditionalBindingBuilderImpl as R, HttpException as S, containerStorage as T, ROUTER_TOKENS as V, ExceptionHandler as _, createHttpExceptionContext as a, getHttpStatus as b, DEFAULT_CONTENT_TYPE as c, ROUTER_CONTEXT_KEYS as d, ROUTE_METADATA_KEYS as f, DefaultExceptionHandler as g, isErrorResponse as h, createCronExceptionContext as i, Container as j, ERROR_CODES as k, HTTP_METHODS as l, VERSION_NEUTRAL as m, RequestContainerNotInitializedError as n, createQueueExceptionContext as o, SECURITY_SCHEMES as p, createCliExceptionContext as r, RouterContext as s, StratalNotInitializedError as t, METHOD_STATUS_CODES as u, isApplicationError as v, I18N_TOKENS as w, resolveHttpStatus as x, InternalError as y, RequestScopeOperationNotAllowedError as z };
|
|
1559
|
+
|
|
1560
|
+
//# sourceMappingURL=errors--RBIvDXr.mjs.map
|