stratal 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +4 -0
  2. package/dist/{application-zG8b-pol.d.mts → application-DfPtIzxF.d.mts} +65 -4
  3. package/dist/application-DfPtIzxF.d.mts.map +1 -0
  4. package/dist/{base-email.provider-Cuw4OAB0.mjs → base-email.provider-DypUAfWm.mjs} +1 -1
  5. package/dist/{base-email.provider-Cuw4OAB0.mjs.map → base-email.provider-DypUAfWm.mjs.map} +1 -1
  6. package/dist/bin/cloudflare-workers-loader.mjs +0 -0
  7. package/dist/bin/quarry.mjs +1 -50
  8. package/dist/bin/quarry.mjs.map +1 -1
  9. package/dist/cache/index.d.mts +1 -1
  10. package/dist/cache/index.mjs +10 -11
  11. package/dist/cache/index.mjs.map +1 -1
  12. package/dist/{colors-DJaRDXoS.mjs → colors-Y7WIFXs7.mjs} +1 -1
  13. package/dist/{colors-DJaRDXoS.mjs.map → colors-Y7WIFXs7.mjs.map} +1 -1
  14. package/dist/{command-BvCOD6df.mjs → command-B1CPgsrU.mjs} +6 -3
  15. package/dist/{command-BvCOD6df.mjs.map → command-B1CPgsrU.mjs.map} +1 -1
  16. package/dist/{command-B-QH-Vu3.d.mts → command-TnkPYWta.d.mts} +2 -2
  17. package/dist/{command-B-QH-Vu3.d.mts.map → command-TnkPYWta.d.mts.map} +1 -1
  18. package/dist/config/index.d.mts +2 -2
  19. package/dist/config/index.mjs +10 -11
  20. package/dist/config/index.mjs.map +1 -1
  21. package/dist/consumer-registry-Bymm6ff4.d.mts +142 -0
  22. package/dist/consumer-registry-Bymm6ff4.d.mts.map +1 -0
  23. package/dist/cron/index.d.mts +3 -116
  24. package/dist/cron/index.d.mts.map +1 -1
  25. package/dist/cron/index.mjs +3 -4
  26. package/dist/{cron-manager-DR7fiG6o.mjs → cron-manager-CFBamKKk.mjs} +3 -3
  27. package/dist/{cron-manager-DR7fiG6o.mjs.map → cron-manager-CFBamKKk.mjs.map} +1 -1
  28. package/dist/cron-manager-D7imGwUT.d.mts +117 -0
  29. package/dist/cron-manager-D7imGwUT.d.mts.map +1 -0
  30. package/dist/di/index.d.mts +1 -1
  31. package/dist/di/index.mjs +2 -3
  32. package/dist/email/index.d.mts +3 -3
  33. package/dist/email/index.mjs +16 -17
  34. package/dist/email/index.mjs.map +1 -1
  35. package/dist/errors/index.d.mts +1 -1
  36. package/dist/errors/index.mjs +2 -3
  37. package/dist/{errors-CtCi1wn6.mjs → errors-DSKapqD8.mjs} +4 -4
  38. package/dist/{errors-CtCi1wn6.mjs.map → errors-DSKapqD8.mjs.map} +1 -1
  39. package/dist/{errors-H3TZnVeX.mjs → errors-DuAR5Wke.mjs} +2 -2
  40. package/dist/{errors-H3TZnVeX.mjs.map → errors-DuAR5Wke.mjs.map} +1 -1
  41. package/dist/events/index.mjs +2 -3
  42. package/dist/{events-CXl-o1Ad.mjs → events-CvUSgEuN.mjs} +2 -3
  43. package/dist/{events-CXl-o1Ad.mjs.map → events-CvUSgEuN.mjs.map} +1 -1
  44. package/dist/{gateway-context-BkZ4UKaX.mjs → gateway-context-CNOLkLUC.mjs} +4 -4
  45. package/dist/{gateway-context-BkZ4UKaX.mjs.map → gateway-context-CNOLkLUC.mjs.map} +1 -1
  46. package/dist/guards/index.d.mts +1 -1
  47. package/dist/i18n/index.d.mts +3 -3
  48. package/dist/i18n/index.mjs +15 -15
  49. package/dist/i18n/validation/index.d.mts +1 -1
  50. package/dist/i18n/validation/index.mjs +1 -1
  51. package/dist/{i18n.module-W8OJxg3d.mjs → i18n.module-Dn9SrFdS.mjs} +210 -160
  52. package/dist/i18n.module-Dn9SrFdS.mjs.map +1 -0
  53. package/dist/{index-BJWm863C.d.mts → index-BFCxSp_f.d.mts} +82 -73
  54. package/dist/index-BFCxSp_f.d.mts.map +1 -0
  55. package/dist/{index-D9iYu2Yc.d.mts → index-DGRe6Yoa.d.mts} +5 -144
  56. package/dist/index-DGRe6Yoa.d.mts.map +1 -0
  57. package/dist/{index-DVhdhLvE.d.mts → index-NGxg-KP_.d.mts} +4 -4
  58. package/dist/{index-DVhdhLvE.d.mts.map → index-NGxg-KP_.d.mts.map} +1 -1
  59. package/dist/index.d.mts +2 -2
  60. package/dist/index.mjs +19 -19
  61. package/dist/{is-command-BfCgWAcQ.mjs → is-command-DJVI6wEJ.mjs} +2 -2
  62. package/dist/{is-command-BfCgWAcQ.mjs.map → is-command-DJVI6wEJ.mjs.map} +1 -1
  63. package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-D5MIEcdz.mjs} +1 -1
  64. package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-D5MIEcdz.mjs.map} +1 -1
  65. package/dist/logger/index.mjs +1 -2
  66. package/dist/{logger-BR1-s1Um.mjs → logger-CGT91VY6.mjs} +170 -4
  67. package/dist/logger-CGT91VY6.mjs.map +1 -0
  68. package/dist/middleware/index.d.mts +1 -1
  69. package/dist/middleware/index.mjs +4 -5
  70. package/dist/{middleware-C0Ebzswy.mjs → middleware-Bl-b5pkt.mjs} +3 -3
  71. package/dist/{middleware-C0Ebzswy.mjs.map → middleware-Bl-b5pkt.mjs.map} +1 -1
  72. package/dist/module/index.d.mts +2 -117
  73. package/dist/module/index.d.mts.map +1 -1
  74. package/dist/module/index.mjs +10 -11
  75. package/dist/module-registry-CmjBX6ol.d.mts +121 -0
  76. package/dist/module-registry-CmjBX6ol.d.mts.map +1 -0
  77. package/dist/{module-BgdxxzBe.mjs → module-tUtyVJ5E.mjs} +9 -8
  78. package/dist/module-tUtyVJ5E.mjs.map +1 -0
  79. package/dist/openapi/index.d.mts +54 -54
  80. package/dist/openapi/index.d.mts.map +1 -1
  81. package/dist/openapi/index.mjs +15 -15
  82. package/dist/openapi-tools.service-B3TxYKoQ.mjs +197 -0
  83. package/dist/openapi-tools.service-B3TxYKoQ.mjs.map +1 -0
  84. package/dist/openapi.service-DGnX3Fc4.d.mts +58 -0
  85. package/dist/openapi.service-DGnX3Fc4.d.mts.map +1 -0
  86. package/dist/quarry/index.d.mts +109 -28
  87. package/dist/quarry/index.d.mts.map +1 -1
  88. package/dist/quarry/index.mjs +9 -7
  89. package/dist/quarry-registry-B2rkO-JS.mjs +683 -0
  90. package/dist/quarry-registry-B2rkO-JS.mjs.map +1 -0
  91. package/dist/queue/index.d.mts +2 -1
  92. package/dist/queue/index.mjs +11 -12
  93. package/dist/queue/index.mjs.map +1 -1
  94. package/dist/{queue.module-BZvmeAMj.mjs → queue.module-BtI8f4Jo.mjs} +4 -4
  95. package/dist/{queue.module-BZvmeAMj.mjs.map → queue.module-BtI8f4Jo.mjs.map} +1 -1
  96. package/dist/{resend.provider-BCCACQAU.mjs → resend.provider-bXMEkdRJ.mjs} +4 -5
  97. package/dist/{resend.provider-BCCACQAU.mjs.map → resend.provider-bXMEkdRJ.mjs.map} +1 -1
  98. package/dist/router/index.d.mts +1 -1
  99. package/dist/router/index.mjs +14 -14
  100. package/dist/{router-context-BEJe9HEB.mjs → router-context-D9R1v2Ac.mjs} +7 -4
  101. package/dist/router-context-D9R1v2Ac.mjs.map +1 -0
  102. package/dist/{s3-storage.provider-BLlzQYiJ.mjs → s3-storage.provider-CttzNnDR.mjs} +5 -6
  103. package/dist/{s3-storage.provider-BLlzQYiJ.mjs.map → s3-storage.provider-CttzNnDR.mjs.map} +1 -1
  104. package/dist/seeder/index.d.mts +3 -3
  105. package/dist/seeder/index.mjs +6 -7
  106. package/dist/{seeder-Cupi5jl-.mjs → seeder-R7RXJC35.mjs} +20 -17
  107. package/dist/seeder-R7RXJC35.mjs.map +1 -0
  108. package/dist/{smtp.provider-B8XtOcHU.mjs → smtp.provider-DrbHQztF.mjs} +4 -5
  109. package/dist/{smtp.provider-B8XtOcHU.mjs.map → smtp.provider-DrbHQztF.mjs.map} +1 -1
  110. package/dist/storage/index.d.mts +2 -195
  111. package/dist/storage/index.d.mts.map +1 -1
  112. package/dist/storage/index.mjs +13 -14
  113. package/dist/storage/providers/index.d.mts +272 -0
  114. package/dist/storage/providers/index.d.mts.map +1 -0
  115. package/dist/storage/providers/index.mjs +5 -0
  116. package/dist/{storage-By_ow2o_.mjs → storage-CZKHOhci.mjs} +7 -7
  117. package/dist/{storage-By_ow2o_.mjs.map → storage-CZKHOhci.mjs.map} +1 -1
  118. package/dist/storage-provider.interface-0IqcdhBf.d.mts +197 -0
  119. package/dist/storage-provider.interface-0IqcdhBf.d.mts.map +1 -0
  120. package/dist/{stratal-CE0iTz4f.mjs → stratal-D5smIU1y.mjs} +22 -12
  121. package/dist/stratal-D5smIU1y.mjs.map +1 -0
  122. package/dist/{usage-generator-C9hWziY4.mjs → usage-generator-CVIsENuE.mjs} +2 -2
  123. package/dist/{usage-generator-C9hWziY4.mjs.map → usage-generator-CVIsENuE.mjs.map} +1 -1
  124. package/dist/{validation-Bh875Lyg.mjs → validation-DQTC259A.mjs} +4 -4
  125. package/dist/{validation-Bh875Lyg.mjs.map → validation-DQTC259A.mjs.map} +1 -1
  126. package/dist/websocket/index.d.mts +1 -1
  127. package/dist/websocket/index.mjs +4 -5
  128. package/dist/workers/index.d.mts +1 -1
  129. package/dist/workers/index.mjs +19 -19
  130. package/package.json +13 -8
  131. package/dist/application-zG8b-pol.d.mts.map +0 -1
  132. package/dist/decorate-D5j-d9_z.mjs +0 -171
  133. package/dist/decorate-D5j-d9_z.mjs.map +0 -1
  134. package/dist/i18n.module-W8OJxg3d.mjs.map +0 -1
  135. package/dist/index-BJWm863C.d.mts.map +0 -1
  136. package/dist/index-D9iYu2Yc.d.mts.map +0 -1
  137. package/dist/logger-BR1-s1Um.mjs.map +0 -1
  138. package/dist/module-BgdxxzBe.mjs.map +0 -1
  139. package/dist/quarry-registry-DCwqVcRp.mjs +0 -310
  140. package/dist/quarry-registry-DCwqVcRp.mjs.map +0 -1
  141. package/dist/router-context-BEJe9HEB.mjs.map +0 -1
  142. package/dist/seeder-Cupi5jl-.mjs.map +0 -1
  143. package/dist/stratal-CE0iTz4f.mjs.map +0 -1
  144. package/dist/types-CLhOhYsQ.d.mts +0 -64
  145. package/dist/types-CLhOhYsQ.d.mts.map +0 -1
@@ -1,12 +1,13 @@
1
- import { S as ApplicationError, b as ERROR_CODES, o as I18N_TOKENS, s as Scope, v as ROUTER_TOKENS, y as getHttpStatus } from "./errors-CtCi1wn6.mjs";
2
- import { i as Transient, l as DI_TOKENS, n as __decorateParam, r as __decorateMetadata, s as getMethodInjections, t as __decorate } from "./decorate-D5j-d9_z.mjs";
3
- import { r as Module } from "./module-BgdxxzBe.mjs";
4
- import { i as getControllerRoute, r as getControllerOptions, t as MiddlewareConfigurationService } from "./middleware-C0Ebzswy.mjs";
5
- import { a as ROUTER_CONTEXT_KEYS, c as VERSION_NEUTRAL, i as METHOD_STATUS_CODES, n as DEFAULT_CONTENT_TYPE, o as ROUTE_METADATA_KEYS, r as HTTP_METHODS, s as SECURITY_SCHEMES, t as RouterContext } from "./router-context-BEJe9HEB.mjs";
6
- import { i as z, o as backendErrorMap, r as validation_exports, s as runWithErrorMapContext, t as OpenAPIHono } from "./validation-Bh875Lyg.mjs";
1
+ import { S as ApplicationError, b as ERROR_CODES, o as I18N_TOKENS, s as Scope, v as ROUTER_TOKENS, y as getHttpStatus } from "./errors-DSKapqD8.mjs";
2
+ import { a as __decorate, d as Transient, g as DI_TOKENS, m as getMethodInjections, o as __decorateParam, s as __decorateMetadata } from "./logger-CGT91VY6.mjs";
3
+ import { r as Module } from "./module-tUtyVJ5E.mjs";
4
+ import { i as getControllerRoute, r as getControllerOptions, t as MiddlewareConfigurationService } from "./middleware-Bl-b5pkt.mjs";
5
+ import { a as ROUTER_CONTEXT_KEYS, c as VERSION_NEUTRAL, i as METHOD_STATUS_CODES, n as DEFAULT_CONTENT_TYPE, o as ROUTE_METADATA_KEYS, r as HTTP_METHODS, s as SECURITY_SCHEMES, t as RouterContext } from "./router-context-D9R1v2Ac.mjs";
6
+ import { i as z, o as backendErrorMap, r as validation_exports, s as runWithErrorMapContext, t as OpenAPIHono } from "./validation-DQTC259A.mjs";
7
+ import { n as OPENAPI_TOKENS } from "./openapi-tools.service-B3TxYKoQ.mjs";
7
8
  import { t as en_exports } from "./en-DaewN8hc.mjs";
8
9
  import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-DUk_Kzst.mjs";
9
- import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-BkZ4UKaX.mjs";
10
+ import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-CNOLkLUC.mjs";
10
11
  import { inject } from "tsyringe";
11
12
  import { compile, createCoreContext, registerMessageCompiler, translate } from "@intlify/core-base";
12
13
  import { swaggerUI } from "@hono/swagger-ui";
@@ -65,16 +66,6 @@ LocaleExtractionMiddleware = __decorate([
65
66
  __decorateMetadata("design:paramtypes", [Object])
66
67
  ], LocaleExtractionMiddleware);
67
68
  //#endregion
68
- //#region src/openapi/openapi.tokens.ts
69
- /**
70
- * OpenAPI Module DI Tokens
71
- */
72
- const OPENAPI_TOKENS = {
73
- Options: Symbol.for("stratal:openapi:options"),
74
- ConfigService: Symbol.for("stratal:openapi:config:service"),
75
- OpenAPIService: Symbol.for("stratal:openapi:service")
76
- };
77
- //#endregion
78
69
  //#region src/openapi/services/openapi-config.service.ts
79
70
  let OpenAPIConfigService = class OpenAPIConfigService {
80
71
  overrides = [];
@@ -82,15 +73,13 @@ let OpenAPIConfigService = class OpenAPIConfigService {
82
73
  this.baseOptions = baseOptions;
83
74
  }
84
75
  /**
85
- * Add configuration override for this request
86
- * Overrides are merged in the order they are added
76
+ * Add configuration override for this request.
77
+ * Overrides are merged in the order they are added.
87
78
  */
88
79
  override(config) {
89
80
  this.overrides.push(config);
90
81
  }
91
- /**
92
- * Get effective configuration (base merged with all overrides)
93
- */
82
+ /** Get effective configuration (base merged with all overrides) */
94
83
  getEffectiveConfig() {
95
84
  let effective = {
96
85
  jsonPath: this.baseOptions?.jsonPath ?? "/api/openapi.json",
@@ -106,8 +95,8 @@ let OpenAPIConfigService = class OpenAPIConfigService {
106
95
  return effective;
107
96
  }
108
97
  /**
109
- * Merge override into effective config
110
- * Info is shallow-merged, routeFilter is replaced
98
+ * Merge override into effective config.
99
+ * Info is shallow-merged, routeFilter is replaced.
111
100
  */
112
101
  mergeConfig(base, override) {
113
102
  return {
@@ -155,6 +144,52 @@ function getLocales() {
155
144
  return Object.keys(messages);
156
145
  }
157
146
  //#endregion
147
+ //#region src/i18n/utils/deep-merge.ts
148
+ /**
149
+ * Deep merge two objects. Source values override target at leaf level.
150
+ */
151
+ function deepMerge(target, source) {
152
+ const result = { ...target };
153
+ for (const key of Object.keys(source)) {
154
+ const targetValue = target[key];
155
+ const sourceValue = source[key];
156
+ if (typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue)) result[key] = deepMerge(targetValue, sourceValue);
157
+ else result[key] = sourceValue;
158
+ }
159
+ return result;
160
+ }
161
+ //#endregion
162
+ //#region src/i18n/services/message-registry.ts
163
+ var _MessageRegistry;
164
+ let MessageRegistry = class MessageRegistry {
165
+ static {
166
+ _MessageRegistry = this;
167
+ }
168
+ static contributions = [];
169
+ /**
170
+ * Add messages (called statically by I18nModule.registerMessages)
171
+ */
172
+ static addMessages(messages) {
173
+ if (Boolean(messages) && typeof messages === "object" && Object.keys(messages).length > 0) _MessageRegistry.contributions.push(messages);
174
+ }
175
+ /**
176
+ * Get all messages deep-merged in registration order
177
+ */
178
+ getMergedMessages() {
179
+ const merged = {};
180
+ for (const contribution of _MessageRegistry.contributions) for (const locale of Object.keys(contribution)) merged[locale] = deepMerge(merged[locale] ?? {}, contribution[locale]);
181
+ return merged;
182
+ }
183
+ /**
184
+ * Reset registry (for testing)
185
+ * @internal
186
+ */
187
+ static reset() {
188
+ _MessageRegistry.contributions = [];
189
+ }
190
+ };
191
+ MessageRegistry = _MessageRegistry = __decorate([Transient(I18N_TOKENS.MessageRegistry)], MessageRegistry);
192
+ //#endregion
158
193
  //#region src/i18n/setup.ts
159
194
  /**
160
195
  * I18n Setup - Message Compiler Registration
@@ -196,8 +231,7 @@ function resolveI18nOptions(options) {
196
231
  return {
197
232
  defaultLocale: options?.defaultLocale ?? "en",
198
233
  fallbackLocale: options?.fallbackLocale ?? "en",
199
- locales: options?.locales ?? ["en"],
200
- messages: options?.messages ?? {}
234
+ locales: options?.locales ?? ["en"]
201
235
  };
202
236
  }
203
237
  //#endregion
@@ -449,38 +483,46 @@ function getDecoratedMethods(ControllerClass) {
449
483
  //#region src/openapi/services/openapi.service.ts
450
484
  let OpenAPIService = class OpenAPIService {
451
485
  routeInfoMap = /* @__PURE__ */ new Map();
452
- constructor(configService) {
453
- this.configService = configService;
486
+ /**
487
+ * Generate a filtered OpenAPI spec using the user's config.
488
+ * Usable from both HTTP handlers and CLI commands.
489
+ */
490
+ getSpec(app, container) {
491
+ const configService = container.resolve(OPENAPI_TOKENS.ConfigService);
492
+ const i18n = container.resolve(I18N_TOKENS.I18nService);
493
+ const config = configService.getEffectiveConfig();
494
+ const fullSpec = app.getOpenAPIDocument({
495
+ openapi: "3.0.0",
496
+ info: {
497
+ version: config.info.version,
498
+ title: config.info.title,
499
+ description: config.info.description
500
+ }
501
+ });
502
+ fullSpec.components ??= {};
503
+ fullSpec.components.securitySchemes = this.getSecuritySchemeDefinitions(i18n);
504
+ fullSpec.paths = this.filterRoutes(fullSpec.paths, config);
505
+ if (fullSpec.components.schemas) fullSpec.components.schemas = this.filterSchemas(fullSpec);
506
+ return fullSpec;
454
507
  }
455
508
  /**
456
509
  * Setup OpenAPI documentation endpoints
457
510
  */
458
- setupEndpoints(app, controllers) {
511
+ setupEndpoints(app, controllers, container) {
459
512
  this.buildRouteInfoMap(controllers);
460
- const config = this.configService.getEffectiveConfig();
513
+ const config = container.resolve(OPENAPI_TOKENS.ConfigService).getEffectiveConfig();
461
514
  app.get(config.jsonPath, (c) => {
462
515
  const requestContainer = c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER);
463
- const i18n = requestContainer.resolve(I18N_TOKENS.I18nService);
464
- const effectiveConfig = requestContainer.resolve(OPENAPI_TOKENS.ConfigService).getEffectiveConfig();
516
+ const fullSpec = this.getSpec(app, requestContainer);
465
517
  const url = new URL(c.req.raw.url);
466
- const fullSpec = app.getOpenAPIDocument({
467
- openapi: "3.0.0",
468
- info: {
469
- version: effectiveConfig.info.version,
470
- title: effectiveConfig.info.title,
471
- description: effectiveConfig.info.description
472
- },
473
- servers: [{
474
- url: `${url.protocol}//${url.host}`,
475
- description: i18n.t("common.api.serverDescription")
476
- }]
477
- });
478
- fullSpec.components ??= {};
479
- fullSpec.components.securitySchemes = this.getSecuritySchemeDefinitions(i18n);
480
- fullSpec.paths = this.filterRoutes(fullSpec.paths, effectiveConfig);
481
- if (fullSpec.components.schemas) fullSpec.components.schemas = this.filterSchemas(fullSpec);
518
+ const i18n = requestContainer.resolve(I18N_TOKENS.I18nService);
519
+ fullSpec.servers = [{
520
+ url: `${url.protocol}//${url.host}`,
521
+ description: i18n.t("common.api.serverDescription")
522
+ }];
482
523
  return c.json(fullSpec);
483
524
  });
525
+ this.nameLastHandler(app, "OpenAPI", "spec");
484
526
  if (config.ui !== false) {
485
527
  const uiPath = config.ui?.path ?? "/api/docs";
486
528
  const uiRenderer = config.ui?.renderer;
@@ -493,8 +535,13 @@ let OpenAPIService = class OpenAPIService {
493
535
  if (uiRenderer) return uiRenderer(uiContext)(c, next);
494
536
  return swaggerUI({ url: uiContext.specUrl })(c, next);
495
537
  });
538
+ this.nameLastHandler(app, "OpenAPI", "docs");
496
539
  }
497
540
  }
541
+ nameLastHandler(app, controller, method) {
542
+ const last = app.routes[app.routes.length - 1];
543
+ Object.defineProperty(last.handler, "name", { value: `http:${controller}.${method}` });
544
+ }
498
545
  /**
499
546
  * Get localized security scheme definitions
500
547
  */
@@ -561,11 +608,15 @@ let OpenAPIService = class OpenAPIService {
561
608
  const filteredSchemas = {};
562
609
  const components = spec.components;
563
610
  if (components?.schemas) {
564
- for (const [schemaName, schemaValue] of Object.entries(components.schemas)) if (referencedSchemas.has(schemaName)) {
565
- filteredSchemas[schemaName] = schemaValue;
566
- this.collectSchemaRefs(schemaValue, referencedSchemas);
611
+ const allSchemas = components.schemas;
612
+ let prevSize = 0;
613
+ while (referencedSchemas.size > prevSize) {
614
+ prevSize = referencedSchemas.size;
615
+ for (const [schemaName, schemaValue] of Object.entries(allSchemas)) if (referencedSchemas.has(schemaName) && !filteredSchemas[schemaName]) {
616
+ filteredSchemas[schemaName] = schemaValue;
617
+ this.collectSchemaRefs(schemaValue, referencedSchemas);
618
+ }
567
619
  }
568
- for (const [schemaName, schemaValue] of Object.entries(components.schemas)) if (referencedSchemas.has(schemaName) && !filteredSchemas[schemaName]) filteredSchemas[schemaName] = schemaValue;
569
620
  }
570
621
  return filteredSchemas;
571
622
  }
@@ -583,11 +634,7 @@ let OpenAPIService = class OpenAPIService {
583
634
  else for (const value of Object.values(record)) this.collectSchemaRefs(value, refs);
584
635
  }
585
636
  };
586
- OpenAPIService = __decorate([
587
- Transient(OPENAPI_TOKENS.OpenAPIService),
588
- __decorateParam(0, inject(OPENAPI_TOKENS.ConfigService)),
589
- __decorateMetadata("design:paramtypes", [Object])
590
- ], OpenAPIService);
637
+ OpenAPIService = __decorate([Transient(OPENAPI_TOKENS.OpenAPIService)], OpenAPIService);
591
638
  //#endregion
592
639
  //#region src/openapi/openapi.module.ts
593
640
  /**
@@ -681,7 +728,8 @@ OpenAPIModule = _OpenAPIModule = __decorate([Module({ providers: [{
681
728
  scope: Scope.Request
682
729
  }, {
683
730
  provide: OPENAPI_TOKENS.OpenAPIService,
684
- useClass: OpenAPIService
731
+ useClass: OpenAPIService,
732
+ scope: Scope.Singleton
685
733
  }] })], OpenAPIModule);
686
734
  //#endregion
687
735
  //#region src/router/errors/controller-method-not-found.error.ts
@@ -1031,6 +1079,7 @@ var RouteRegistrationService = class {
1031
1079
  if (onErrMethod) events.onError = bindWsHandler(onErrMethod);
1032
1080
  return events;
1033
1081
  });
1082
+ this.nameHandler(wsHandler, GatewayClass.name, onMsgMethod ?? "[anonymous]", "ws");
1034
1083
  this.logger.info("Registering WebSocket gateway", {
1035
1084
  gateway: GatewayClass.name,
1036
1085
  path: fullPath
@@ -1171,7 +1220,7 @@ var RouteRegistrationService = class {
1171
1220
  methodName,
1172
1221
  tags: metadata.tags
1173
1222
  });
1174
- app.openapi(openApiRoute, async (c) => {
1223
+ const handler = async (c) => {
1175
1224
  const ctx = new RouterContext(c);
1176
1225
  const requestContainer = ctx.getContainer();
1177
1226
  const controller = requestContainer.resolve(ControllerClass);
@@ -1181,7 +1230,9 @@ var RouteRegistrationService = class {
1181
1230
  return await method.call(controller, ctx, ...injectedArgs);
1182
1231
  }
1183
1232
  throw new ControllerMethodNotFoundError(methodName, className);
1184
- });
1233
+ };
1234
+ this.nameHandler(handler, className, methodName);
1235
+ app.openapi(openApiRoute, handler);
1185
1236
  }
1186
1237
  }
1187
1238
  /**
@@ -1231,7 +1282,7 @@ var RouteRegistrationService = class {
1231
1282
  methodName,
1232
1283
  tags: metadata.tags
1233
1284
  });
1234
- app.openapi(openApiRoute, async (c) => {
1285
+ const handler = async (c) => {
1235
1286
  const ctx = new RouterContext(c);
1236
1287
  const requestContainer = ctx.getContainer();
1237
1288
  const controller = requestContainer.resolve(ControllerClass);
@@ -1241,7 +1292,9 @@ var RouteRegistrationService = class {
1241
1292
  return await method.call(controller, ctx, ...injectedArgs);
1242
1293
  }
1243
1294
  throw new ControllerMethodNotFoundError(methodName, className);
1244
- });
1295
+ };
1296
+ this.nameHandler(handler, className, methodName);
1297
+ app.openapi(openApiRoute, handler);
1245
1298
  }
1246
1299
  }
1247
1300
  /**
@@ -1268,6 +1321,7 @@ var RouteRegistrationService = class {
1268
1321
  }
1269
1322
  throw new ControllerMethodNotFoundError(methodName, ControllerClass.name);
1270
1323
  };
1324
+ this.nameHandler(handler, ControllerClass.name, methodName);
1271
1325
  switch (derived.method) {
1272
1326
  case "get":
1273
1327
  app.get(derived.path, handler);
@@ -1412,11 +1466,18 @@ var RouteRegistrationService = class {
1412
1466
  return injections.map((inj) => container.resolve(inj.token));
1413
1467
  }
1414
1468
  /**
1469
+ * Name a handler function so Hono's inspectRoutes() can identify it.
1470
+ * Format: `{type}:{Controller}.{method}` (e.g. `http:UsersController.create`)
1471
+ */
1472
+ nameHandler(fn, controller, method, type = "http") {
1473
+ Object.defineProperty(fn, "name", { value: `${type}:${controller}.${method}` });
1474
+ }
1475
+ /**
1415
1476
  * Create controller handler that resolves controller from request-scoped container
1416
1477
  * This ensures each request gets a fresh controller with request-scoped context
1417
1478
  */
1418
1479
  createControllerHandler(ControllerClass, methodName) {
1419
- return async (c) => {
1480
+ const handler = async (c) => {
1420
1481
  this.logger.info("Handler invoked", {
1421
1482
  path: c.req.path,
1422
1483
  method: c.req.method,
@@ -1443,6 +1504,8 @@ var RouteRegistrationService = class {
1443
1504
  throw error;
1444
1505
  }
1445
1506
  };
1507
+ this.nameHandler(handler, ControllerClass.name, methodName);
1508
+ return handler;
1446
1509
  }
1447
1510
  };
1448
1511
  //#endregion
@@ -1501,7 +1564,7 @@ var HonoApp = class extends OpenAPIHono {
1501
1564
  async configure(middlewareConfigs, controllers, versioningOptions) {
1502
1565
  if (this.configured) throw new HonoAppAlreadyConfiguredError();
1503
1566
  new MiddlewareConfigurationService(this._logger, versioningOptions ?? null).applyMiddlewares(this, middlewareConfigs, controllers, this._container);
1504
- this._container.resolve(OPENAPI_TOKENS.OpenAPIService).setupEndpoints(this, controllers);
1567
+ this._container.resolve(OPENAPI_TOKENS.OpenAPIService).setupEndpoints(this, controllers, this._container);
1505
1568
  await new RouteRegistrationService(this._logger, versioningOptions ?? null).configure(this, controllers);
1506
1569
  this.notFound((c) => {
1507
1570
  throw new RouteNotFoundError(c.req.path, c.req.method);
@@ -1591,30 +1654,26 @@ let MessageLoaderService = class MessageLoaderService {
1591
1654
  contextCache;
1592
1655
  locales;
1593
1656
  defaultLocale;
1594
- constructor(options) {
1657
+ constructor(registry, options) {
1658
+ this.registry = registry;
1595
1659
  this.options = options;
1596
1660
  this.defaultLocale = this.options?.defaultLocale ?? "en";
1597
1661
  this.cache = /* @__PURE__ */ new Map();
1598
1662
  this.contextCache = /* @__PURE__ */ new Map();
1599
1663
  const coreMessages = getMessages();
1600
1664
  const coreLocales = getLocales();
1601
- const appMessages = this.options?.messages ?? {};
1602
- const appLocales = Object.keys(appMessages);
1603
- const allLocales = [...new Set([...coreLocales, ...appLocales])];
1665
+ const registryMessages = this.registry.getMergedMessages();
1666
+ const registryLocales = Object.keys(registryMessages);
1667
+ const allLocales = [...new Set([...coreLocales, ...registryLocales])];
1604
1668
  this.locales = allLocales;
1605
1669
  for (const locale of allLocales) {
1606
- const coreLocaleMessages = coreMessages[locale] ?? {};
1607
- const appLocaleMessages = appMessages[locale] ?? {};
1608
- const merged = this.deepMerge(coreLocaleMessages, appLocaleMessages);
1670
+ const merged = deepMerge(coreMessages[locale] ?? {}, registryMessages[locale] ?? {});
1609
1671
  this.cache.set(locale, merged);
1610
1672
  }
1611
1673
  }
1612
1674
  /**
1613
1675
  * Get CoreContext for a locale (lazily built and cached on first access)
1614
1676
  * Falls back to default locale if locale not found
1615
- *
1616
- * @param locale - Locale code
1617
- * @returns Cached CoreContext ready for translation
1618
1677
  */
1619
1678
  getCoreContext(locale) {
1620
1679
  const cached = this.contextCache.get(locale);
@@ -1634,47 +1693,27 @@ let MessageLoaderService = class MessageLoaderService {
1634
1693
  return ctx;
1635
1694
  }
1636
1695
  /**
1637
- * Get messages for a specific locale
1638
- * Falls back to default locale if locale not found
1639
- *
1640
- * @param locale - Locale code
1641
- * @returns Message object for the locale
1696
+ * Get messages for a specific locale.
1697
+ * Falls back to default locale if not found.
1642
1698
  */
1643
1699
  getMessages(locale) {
1644
- const msgs = this.cache.get(locale);
1645
- if (!msgs) return this.cache.get(this.defaultLocale) ?? {};
1646
- return msgs;
1700
+ return this.cache.get(locale) ?? this.cache.get(this.defaultLocale) ?? {};
1647
1701
  }
1648
- /**
1649
- * Get list of available locale codes
1650
- *
1651
- * @returns Array of locale codes
1652
- */
1702
+ /** Get list of available locale codes */
1653
1703
  getAvailableLocales() {
1654
1704
  return this.locales;
1655
1705
  }
1656
- /**
1657
- * Check if a locale is supported
1658
- *
1659
- * @param locale - Locale code to check
1660
- * @returns true if locale is supported
1661
- */
1706
+ /** Check if a locale is supported */
1662
1707
  isLocaleSupported(locale) {
1663
1708
  return this.cache.has(locale);
1664
1709
  }
1665
- /**
1666
- * Get default locale
1667
- *
1668
- * @returns Default locale code
1669
- */
1710
+ /** Get default locale */
1670
1711
  getDefaultLocale() {
1671
1712
  return this.defaultLocale;
1672
1713
  }
1673
1714
  /**
1674
- * Flatten nested messages to dot-notation
1675
- *
1676
- * Converts { auth: { login: { title: 'Sign In' } } }
1677
- * to { 'auth.login.title': 'Sign In' }
1715
+ * Flatten nested messages to dot-notation.
1716
+ * e.g. `{ a: { b: 'hello' } }` → `{ 'a.b': 'hello' }`
1678
1717
  */
1679
1718
  flattenMessages(messages, prefix = "") {
1680
1719
  const result = {};
@@ -1686,25 +1725,12 @@ let MessageLoaderService = class MessageLoaderService {
1686
1725
  }
1687
1726
  return result;
1688
1727
  }
1689
- /**
1690
- * Deep merge two objects
1691
- * App messages override core messages at leaf level
1692
- */
1693
- deepMerge(target, source) {
1694
- const result = { ...target };
1695
- for (const key of Object.keys(source)) {
1696
- const targetValue = target[key];
1697
- const sourceValue = source[key];
1698
- if (typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue) && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue)) result[key] = this.deepMerge(targetValue, sourceValue);
1699
- else result[key] = sourceValue;
1700
- }
1701
- return result;
1702
- }
1703
1728
  };
1704
1729
  MessageLoaderService = __decorate([
1705
1730
  Transient(I18N_TOKENS.MessageLoader),
1706
- __decorateParam(0, inject(I18N_TOKENS.Options, { isOptional: true })),
1707
- __decorateMetadata("design:paramtypes", [Object])
1731
+ __decorateParam(0, inject(I18N_TOKENS.MessageRegistry)),
1732
+ __decorateParam(1, inject(I18N_TOKENS.Options, { isOptional: true })),
1733
+ __decorateMetadata("design:paramtypes", [Object, Object])
1708
1734
  ], MessageLoaderService);
1709
1735
  //#endregion
1710
1736
  //#region src/i18n/i18n.module.ts
@@ -1714,21 +1740,28 @@ MessageLoaderService = __decorate([
1714
1740
  * Core infrastructure module for internationalization.
1715
1741
  * Provides message translation and locale handling.
1716
1742
  *
1717
- * @example Default usage (system messages only)
1743
+ * - `forRoot()` configures locale settings (call once in root module)
1744
+ * - `registerMessages()` adds translations (call from any module, as many times as needed)
1745
+ *
1746
+ * @example
1718
1747
  * ```typescript
1719
- * // In Application.ts - I18nModule is already included in core modules
1720
- * // Works with system messages and 'en' default locale
1748
+ * @Module({
1749
+ * imports: [
1750
+ * I18nModule.forRoot({ defaultLocale: 'en', locales: ['en', 'fr'] }),
1751
+ * I18nModule.registerMessages(appMessages),
1752
+ * ],
1753
+ * })
1754
+ * export class AppModule {}
1721
1755
  * ```
1722
1756
  *
1723
- * @example With app-specific configuration
1757
+ * @example Package contributing messages
1724
1758
  * ```typescript
1725
- * // In apps/backend/src/app.module.ts
1726
- * import { i18nConfig } from './i18n'
1727
- *
1728
1759
  * @Module({
1729
- * imports: [I18nModule.forRoot(i18nConfig)],
1760
+ * imports: [
1761
+ * I18nModule.registerMessages(tenancyMessages),
1762
+ * ],
1730
1763
  * })
1731
- * export class AppModule {}
1764
+ * export class TenancyModule {}
1732
1765
  * ```
1733
1766
  */
1734
1767
  var _I18nModule;
@@ -1736,30 +1769,12 @@ setupI18nCompiler();
1736
1769
  z.config({ customError: backendErrorMap });
1737
1770
  let I18nModule = _I18nModule = class I18nModule {
1738
1771
  /**
1739
- * Configure I18n with app-specific options
1740
- *
1741
- * Use this method in AppModule to provide custom locale configuration
1742
- * and app-specific messages that merge with system messages.
1772
+ * Configure I18n locale settings
1743
1773
  *
1744
- * @param options - I18n configuration options
1745
- * @returns Dynamic module with options provider
1774
+ * Call once in the root module. Does not accept messages —
1775
+ * use `registerMessages()` to add translations.
1746
1776
  *
1747
- * @example
1748
- * ```typescript
1749
- * // apps/backend/src/i18n/index.ts
1750
- * export const i18nConfig: I18nModuleOptions = {
1751
- * defaultLocale: 'en',
1752
- * fallbackLocale: 'en',
1753
- * locales: ['en', 'fr'],
1754
- * messages: appMessages
1755
- * }
1756
- *
1757
- * // apps/backend/src/app.module.ts
1758
- * @Module({
1759
- * imports: [I18nModule.forRoot(i18nConfig)],
1760
- * })
1761
- * export class AppModule {}
1762
- * ```
1777
+ * @param options - Locale configuration (defaultLocale, fallbackLocale, locales)
1763
1778
  */
1764
1779
  static forRoot(options = {}) {
1765
1780
  return {
@@ -1771,21 +1786,56 @@ let I18nModule = _I18nModule = class I18nModule {
1771
1786
  };
1772
1787
  }
1773
1788
  /**
1774
- * Configure middleware for locale extraction and i18n context
1789
+ * Register i18n messages
1790
+ *
1791
+ * Can be called from any module, as many times as needed.
1792
+ * Messages are deep-merged in registration order — later calls override earlier ones at leaf level.
1793
+ *
1794
+ * @param messages - Messages keyed by locale code
1795
+ *
1796
+ * @example App-level messages
1797
+ * ```typescript
1798
+ * I18nModule.registerMessages({
1799
+ * en: { common: { hello: 'Hello' }, errors: { notFound: 'Not found' } },
1800
+ * fr: { common: { hello: 'Bonjour' }, errors: { notFound: 'Introuvable' } },
1801
+ * })
1802
+ * ```
1803
+ *
1804
+ * @example Package-level messages
1805
+ * ```typescript
1806
+ * I18nModule.registerMessages({
1807
+ * en: { tenancy: { tenantNotFound: 'Tenant not found' } },
1808
+ * })
1809
+ * ```
1775
1810
  */
1811
+ static registerMessages(messages) {
1812
+ MessageRegistry.addMessages(messages);
1813
+ return {
1814
+ module: _I18nModule,
1815
+ providers: []
1816
+ };
1817
+ }
1776
1818
  configure(consumer) {
1777
1819
  consumer.apply(LocaleExtractionMiddleware, I18nContextMiddleware).forRoutes("*");
1778
1820
  }
1779
1821
  };
1780
- I18nModule = _I18nModule = __decorate([Module({ providers: [{
1781
- provide: I18N_TOKENS.MessageLoader,
1782
- useClass: MessageLoaderService,
1783
- scope: Scope.Singleton
1784
- }, {
1785
- provide: I18N_TOKENS.I18nService,
1786
- useClass: I18nService
1787
- }] })], I18nModule);
1822
+ I18nModule = _I18nModule = __decorate([Module({ providers: [
1823
+ {
1824
+ provide: I18N_TOKENS.MessageRegistry,
1825
+ useClass: MessageRegistry,
1826
+ scope: Scope.Singleton
1827
+ },
1828
+ {
1829
+ provide: I18N_TOKENS.MessageLoader,
1830
+ useClass: MessageLoaderService,
1831
+ scope: Scope.Singleton
1832
+ },
1833
+ {
1834
+ provide: I18N_TOKENS.I18nService,
1835
+ useClass: I18nService
1836
+ }
1837
+ ] })], I18nModule);
1788
1838
  //#endregion
1789
- export { LocaleNotSupportedError as A, Get as C, getHttpDecoratedMethods as D, Put as E, messages as F, OpenAPIConfigService as I, OPENAPI_TOKENS as L, setupI18nCompiler as M, getLocales as N, getHttpRouteMetadata as O, getMessages as P, LocaleExtractionMiddleware as R, Delete as S, Post as T, OpenAPIService as _, errorResponseSchema as a, getRouteConfig as b, successMessageSchema as c, RouteNotFoundError as d, OpenAPIValidationError as f, OpenAPIModule as g, HonoAppAlreadyConfiguredError as h, commonErrorSchemas as i, resolveI18nOptions as j, TranslationMissingError as k, uuidParamSchema as l, ControllerRegistrationError as m, HonoApp as n, paginatedResponseSchema as o, OpenAPIRouteRegistrationError as p, RouteRegistrationService as r, paginationQuerySchema as s, I18nModule as t, validationErrorResponseSchema as u, Route as v, Patch as w, All as x, getDecoratedMethods as y, I18nContextMiddleware as z };
1839
+ export { LocaleNotSupportedError as A, Get as C, getHttpDecoratedMethods as D, Put as E, getMessages as F, messages as I, OpenAPIConfigService as L, setupI18nCompiler as M, MessageRegistry as N, getHttpRouteMetadata as O, getLocales as P, LocaleExtractionMiddleware as R, Delete as S, Post as T, OpenAPIService as _, errorResponseSchema as a, getRouteConfig as b, successMessageSchema as c, RouteNotFoundError as d, OpenAPIValidationError as f, OpenAPIModule as g, HonoAppAlreadyConfiguredError as h, commonErrorSchemas as i, resolveI18nOptions as j, TranslationMissingError as k, uuidParamSchema as l, ControllerRegistrationError as m, HonoApp as n, paginatedResponseSchema as o, OpenAPIRouteRegistrationError as p, RouteRegistrationService as r, paginationQuerySchema as s, I18nModule as t, validationErrorResponseSchema as u, Route as v, Patch as w, All as x, getDecoratedMethods as y, I18nContextMiddleware as z };
1790
1840
 
1791
- //# sourceMappingURL=i18n.module-W8OJxg3d.mjs.map
1841
+ //# sourceMappingURL=i18n.module-Dn9SrFdS.mjs.map