stratal 0.0.20 → 0.0.21

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 (113) hide show
  1. package/dist/base-email.provider-CfQCA08m.mjs.map +1 -1
  2. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  3. package/dist/bin/quarry.mjs.map +1 -1
  4. package/dist/cache/index.d.mts +1 -1
  5. package/dist/cache/index.mjs +5 -3
  6. package/dist/cache/index.mjs.map +1 -1
  7. package/dist/command-BgSlsS4M.mjs.map +1 -1
  8. package/dist/{command-Bu-PjJrX.d.mts → command-Cmmf0oHX.d.mts} +2 -2
  9. package/dist/{command-Bu-PjJrX.d.mts.map → command-Cmmf0oHX.d.mts.map} +1 -1
  10. package/dist/config/index.d.mts +1 -1
  11. package/dist/config/index.mjs +5 -3
  12. package/dist/config/index.mjs.map +1 -1
  13. package/dist/{controller.decorator-DQzenvSN.mjs → controller.decorator-B9vwn0zK.mjs} +3 -3
  14. package/dist/{controller.decorator-DQzenvSN.mjs.map → controller.decorator-B9vwn0zK.mjs.map} +1 -1
  15. package/dist/cron/index.d.mts +2 -2
  16. package/dist/cron/index.mjs +1 -1
  17. package/dist/{cron-manager-BEsH1mjW.d.mts → cron-manager-CmTimEjf.d.mts} +2 -2
  18. package/dist/cron-manager-CmTimEjf.d.mts.map +1 -0
  19. package/dist/{cron-manager-7Symz_TE.mjs → cron-manager-DQSK8uoV.mjs} +10 -4
  20. package/dist/cron-manager-DQSK8uoV.mjs.map +1 -0
  21. package/dist/di/index.d.mts +1 -1
  22. package/dist/di/index.mjs +2 -2
  23. package/dist/email/index.d.mts +2 -2
  24. package/dist/email/index.mjs +12 -7
  25. package/dist/email/index.mjs.map +1 -1
  26. package/dist/errors/index.d.mts +1 -1
  27. package/dist/errors/index.mjs +1 -1
  28. package/dist/{errors-BdyV5PnY.mjs → errors-COW9-Mar.mjs} +16 -2
  29. package/dist/{errors-BdyV5PnY.mjs.map → errors-COW9-Mar.mjs.map} +1 -1
  30. package/dist/{errors-Da3Pz2X7.mjs → errors-ORxu1-Bb.mjs} +2 -2
  31. package/dist/{errors-Da3Pz2X7.mjs.map → errors-ORxu1-Bb.mjs.map} +1 -1
  32. package/dist/events/index.mjs +1 -1
  33. package/dist/{events-COKixqnG.mjs → events-CzCV8jI8.mjs} +4 -2
  34. package/dist/{events-COKixqnG.mjs.map → events-CzCV8jI8.mjs.map} +1 -1
  35. package/dist/{gateway-context-CdJjpUCW.mjs → gateway-context-CXmXtaUP.mjs} +4 -3
  36. package/dist/{gateway-context-CdJjpUCW.mjs.map → gateway-context-CXmXtaUP.mjs.map} +1 -1
  37. package/dist/guards/index.d.mts +1 -1
  38. package/dist/guards/index.mjs +1 -1
  39. package/dist/{guards-DUk_Kzst.mjs → guards-DU1_J9YA.mjs} +2 -1
  40. package/dist/{guards-DUk_Kzst.mjs.map → guards-DU1_J9YA.mjs.map} +1 -1
  41. package/dist/{http-method.decorator-DXwxAfb_.mjs → http-method.decorator-BrgHMdLQ.mjs} +2 -2
  42. package/dist/{http-method.decorator-DXwxAfb_.mjs.map → http-method.decorator-BrgHMdLQ.mjs.map} +1 -1
  43. package/dist/i18n/index.d.mts +1 -1
  44. package/dist/i18n/index.mjs +2 -2
  45. package/dist/{i18n.module-BBlNNlcG.mjs → i18n.module-CzXLW9Hy.mjs} +59 -19
  46. package/dist/i18n.module-CzXLW9Hy.mjs.map +1 -0
  47. package/dist/{index-D0US0X14.d.mts → index-ByOyTmqf.d.mts} +6 -3
  48. package/dist/{index-D0US0X14.d.mts.map → index-ByOyTmqf.d.mts.map} +1 -1
  49. package/dist/{index-CjaQ6_tZ.d.mts → index-DUzWs0z7.d.mts} +2 -2
  50. package/dist/{index-CjaQ6_tZ.d.mts.map → index-DUzWs0z7.d.mts.map} +1 -1
  51. package/dist/index.d.mts +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/is-command-C6a7WTPw.mjs.map +1 -1
  54. package/dist/is-seeder-CebjZCDn.mjs.map +1 -1
  55. package/dist/logger/index.mjs +1 -1
  56. package/dist/{logger-V6Ms3QnQ.mjs → logger-DlV7NtvD.mjs} +8 -4
  57. package/dist/{logger-V6Ms3QnQ.mjs.map → logger-DlV7NtvD.mjs.map} +1 -1
  58. package/dist/macroable-BmufBshB.mjs.map +1 -1
  59. package/dist/module/index.d.mts +1 -1
  60. package/dist/module/index.mjs +1 -1
  61. package/dist/{module-Dk2qTa77.mjs → module-BzLg57FK.mjs} +10 -4
  62. package/dist/{module-Dk2qTa77.mjs.map → module-BzLg57FK.mjs.map} +1 -1
  63. package/dist/openapi/index.d.mts +2 -2
  64. package/dist/openapi/index.mjs +1 -1
  65. package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +1 -1
  66. package/dist/{openapi.service-BLgvn3hJ.d.mts → openapi.service-Bt9bCIrd.d.mts} +2 -2
  67. package/dist/{openapi.service-BLgvn3hJ.d.mts.map → openapi.service-Bt9bCIrd.d.mts.map} +1 -1
  68. package/dist/quarry/index.d.mts +4 -4
  69. package/dist/quarry/index.mjs +1 -1
  70. package/dist/{quarry-registry-DNEej-Db.mjs → quarry-registry-BwY2hOxm.mjs} +15 -4
  71. package/dist/{quarry-registry-DNEej-Db.mjs.map → quarry-registry-BwY2hOxm.mjs.map} +1 -1
  72. package/dist/queue/index.d.mts +1 -1
  73. package/dist/queue/index.mjs +3 -2
  74. package/dist/queue/index.mjs.map +1 -1
  75. package/dist/{queue.module-BCdCiySt.mjs → queue.module-BhCjZp6H.mjs} +13 -4
  76. package/dist/{queue.module-BCdCiySt.mjs.map → queue.module-BhCjZp6H.mjs.map} +1 -1
  77. package/dist/{r2-storage.provider-Co6F0ZYV.mjs → r2-storage.provider-DuonKeYm.mjs} +5 -2
  78. package/dist/{r2-storage.provider-Co6F0ZYV.mjs.map → r2-storage.provider-DuonKeYm.mjs.map} +1 -1
  79. package/dist/{rate-limit.decorator--o6Q6p9w.mjs → rate-limit.decorator-6qzNcSOt.mjs} +2 -2
  80. package/dist/{rate-limit.decorator--o6Q6p9w.mjs.map → rate-limit.decorator-6qzNcSOt.mjs.map} +1 -1
  81. package/dist/rate-limiter/index.d.mts +1 -1
  82. package/dist/rate-limiter/index.mjs +13 -4
  83. package/dist/rate-limiter/index.mjs.map +1 -1
  84. package/dist/{resend.provider-M6qRLrcy.mjs → resend.provider-DB4IlFjG.mjs} +2 -1
  85. package/dist/{resend.provider-M6qRLrcy.mjs.map → resend.provider-DB4IlFjG.mjs.map} +1 -1
  86. package/dist/router/index.d.mts +1 -1
  87. package/dist/router/index.mjs +5 -5
  88. package/dist/seeder/index.d.mts +2 -2
  89. package/dist/seeder/index.mjs +1 -1
  90. package/dist/{seeder-CJAOHEIo.mjs → seeder-zoEfEw9i.mjs} +6 -3
  91. package/dist/{seeder-CJAOHEIo.mjs.map → seeder-zoEfEw9i.mjs.map} +1 -1
  92. package/dist/setup-CefZKV_e.mjs.map +1 -1
  93. package/dist/signed-url-BQPbv2In.mjs.map +1 -1
  94. package/dist/{smtp.provider-w0Ve52Xg.mjs → smtp.provider-B6D7zuWX.mjs} +2 -1
  95. package/dist/{smtp.provider-w0Ve52Xg.mjs.map → smtp.provider-B6D7zuWX.mjs.map} +1 -1
  96. package/dist/storage/index.d.mts +1 -1
  97. package/dist/storage/index.mjs +2 -2
  98. package/dist/storage/providers/index.mjs +1 -1
  99. package/dist/{storage-1zw-6Yiz.mjs → storage-D8CBP72Z.mjs} +13 -8
  100. package/dist/{storage-1zw-6Yiz.mjs.map → storage-D8CBP72Z.mjs.map} +1 -1
  101. package/dist/{stratal-DeEcGgdq.mjs → stratal-CNwpbSZl.mjs} +12 -10
  102. package/dist/{stratal-DeEcGgdq.mjs.map → stratal-CNwpbSZl.mjs.map} +1 -1
  103. package/dist/usage-generator-BUdlhnCK.mjs.map +1 -1
  104. package/dist/validation-DtJwAv7O.mjs.map +1 -1
  105. package/dist/websocket/index.d.mts +1 -1
  106. package/dist/websocket/index.mjs +1 -1
  107. package/dist/workers/index.d.mts +1 -1
  108. package/dist/workers/index.mjs +2 -2
  109. package/dist/workers/index.mjs.map +1 -1
  110. package/package.json +13 -13
  111. package/dist/cron-manager-7Symz_TE.mjs.map +0 -1
  112. package/dist/cron-manager-BEsH1mjW.d.mts.map +0 -1
  113. package/dist/i18n.module-BBlNNlcG.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"events-COKixqnG.mjs","names":[],"sources":["../src/events/constants.ts","../src/events/decorators/listener.decorator.ts","../src/events/decorators/on.decorator.ts","../src/events/event-registry.ts"],"sourcesContent":["/**\n * Metadata keys for event listener decorators.\n *\n * Uses `Symbol.for()` (global symbol registry) so that both core and\n * framework packages can reference the same symbols without cross-imports.\n */\nexport const LISTENER_METADATA_KEYS = {\n IS_LISTENER: Symbol.for('stratal:listener'),\n EVENT_HANDLERS: Symbol.for('stratal:listener:handlers'),\n} as const\n","import { Transient } from '../../di/decorators'\nimport type { Constructor } from '../../types'\nimport { LISTENER_METADATA_KEYS } from '../constants'\n\n/**\n * Mark a class as an event listener.\n *\n * Applies `@Transient()` for DI and sets metadata so the module system\n * can auto-discover and wire listener handlers at bootstrap time.\n *\n * @example\n * ```typescript\n * @Listener()\n * export class UserCreatedListener {\n * @On('after.User.create')\n * async sendWelcomeEmail(context: EventContext<'after.User.create'>) {\n * // ...\n * }\n * }\n * ```\n */\nexport function Listener() {\n return function <T extends Constructor>(target: T) {\n Transient()(target)\n Reflect.defineMetadata(LISTENER_METADATA_KEYS.IS_LISTENER, true, target)\n return target\n }\n}\n\n/**\n * Check if a class is decorated with `@Listener()`\n */\nexport function isListener(target: Constructor): boolean {\n return Reflect.getMetadata(LISTENER_METADATA_KEYS.IS_LISTENER, target) === true\n}\n","import { LISTENER_METADATA_KEYS } from '../constants'\nimport type { EventName, EventOptions, ListenerHandlerMetadata } from '../types'\n\n/**\n * Register a method as an event handler within a `@Listener()` class.\n *\n * Accumulates handler metadata on the class so the framework can\n * auto-wire handlers with the EventRegistry at bootstrap time.\n *\n * @param event - Event name to listen for (fully typed with autocomplete)\n * @param options - Optional handler options (priority, blocking)\n *\n * @example\n * ```typescript\n * @Listener()\n * export class AuditListener {\n * @On('after.User.create')\n * async logCreate(context: EventContext<'after.User.create'>) { ... }\n *\n * @On('after.User.delete', { priority: 10 })\n * async logDelete(context: EventContext<'after.User.delete'>) { ... }\n * }\n * ```\n */\nexport function On<E extends EventName>(event: E, options?: EventOptions) {\n return function (\n target: object,\n propertyKey: string,\n _descriptor: PropertyDescriptor\n ) {\n const existingHandlers: ListenerHandlerMetadata[] =\n (Reflect.getMetadata(LISTENER_METADATA_KEYS.EVENT_HANDLERS, target.constructor) as ListenerHandlerMetadata[] | undefined) ?? []\n\n existingHandlers.push({\n methodName: propertyKey,\n event: event as string,\n options,\n })\n\n Reflect.defineMetadata(\n LISTENER_METADATA_KEYS.EVENT_HANDLERS,\n existingHandlers,\n target.constructor\n )\n }\n}\n\n/**\n * Get all `@On()` handler metadata from a listener class\n */\nexport function getListenerHandlers(target: object): ListenerHandlerMetadata[] {\n const metadataTarget = typeof target === 'function' ? target : target.constructor\n return (Reflect.getMetadata(LISTENER_METADATA_KEYS.EVENT_HANDLERS, metadataTarget) as ListenerHandlerMetadata[] | undefined) ?? []\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { LOGGER_TOKENS, type LoggerService } from '../logger'\nimport type {\n EventContext,\n EventHandler,\n EventName,\n EventOptions,\n IEventRegistry,\n RegisteredHandler\n} from './types'\n\n@Transient()\nexport class EventRegistry implements IEventRegistry {\n private handlers = new Map<string, RegisteredHandler[]>()\n\n constructor(\n @inject(DI_TOKENS.ExecutionContext) private readonly ctx: ExecutionContext,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService\n ) { }\n\n on<E extends EventName>(event: E, handler: EventHandler<E>, options?: EventOptions): void {\n const registered: RegisteredHandler = {\n handler: handler as EventHandler,\n priority: options?.priority ?? 0,\n blocking: options?.blocking\n }\n\n const existingHandlers = this.handlers.get(event) ?? []\n existingHandlers.push(registered)\n this.handlers.set(event, existingHandlers)\n\n this.logger.debug('Event handler registered', {\n event,\n priority: registered.priority,\n blocking: registered.blocking\n })\n }\n\n async emit<E extends EventName>(\n event: E,\n context?: Partial<EventContext<E>>\n ): Promise<void> {\n // Build full context with caller-provided fields\n const fullContext = {\n ...context\n } as EventContext<E>\n\n // Find matching handlers using pattern matching\n const matchingHandlers = this.findMatchingHandlers(event)\n\n if (matchingHandlers.length === 0) {\n return\n }\n\n // Sort by priority (higher first)\n const sortedHandlers = [...matchingHandlers].sort(\n (a, b) => b.priority - a.priority\n )\n\n // Determine if we should use waitUntil\n const shouldUseWaitUntil = this.shouldUseWaitUntil(event, sortedHandlers)\n\n // Execute handlers\n const promises = sortedHandlers.map((registered) =>\n this.executeHandler(registered.handler, fullContext, event)\n )\n\n if (shouldUseWaitUntil) {\n // Non-blocking: use ctx.waitUntil\n this.ctx.waitUntil(Promise.all(promises))\n } else {\n // Blocking: await all handlers\n await Promise.all(promises)\n }\n }\n\n off<E extends EventName>(event: E, handler: EventHandler<E>): void {\n const existingHandlers = this.handlers.get(event)\n if (!existingHandlers) return\n\n const filtered = existingHandlers.filter((h) => h.handler !== handler)\n if (filtered.length > 0) {\n this.handlers.set(event, filtered)\n } else {\n this.handlers.delete(event)\n }\n\n this.logger.debug('Event handler unregistered', { event })\n }\n\n once<E extends EventName>(event: E, handler: EventHandler<E>, options?: EventOptions): void {\n const wrappedHandler = (async (context: EventContext<E>) => {\n await handler(context)\n this.off(event, wrappedHandler)\n }) as EventHandler<E>\n\n this.on(event, wrappedHandler, options)\n }\n\n /**\n * Find all handlers matching the event using pattern matching.\n * Order: exact match -> model wildcard -> operation wildcard -> global wildcard\n */\n private findMatchingHandlers(event: string): RegisteredHandler[] {\n const handlers: RegisteredHandler[] = []\n\n const parts = event.split('.')\n\n if (parts.length === 3) {\n // Database event: \"phase.model.operation\"\n const [phase, model, operation] = parts\n\n // 1. Exact match: \"after.user.create\"\n handlers.push(...(this.handlers.get(event) ?? []))\n\n // 2. Model wildcard: \"after.user\"\n handlers.push(...(this.handlers.get(`${phase}.${model}`) ?? []))\n\n // 3. Operation wildcard: \"after.create\"\n handlers.push(...(this.handlers.get(`${phase}.${operation}`) ?? []))\n\n // 4. Global wildcard: \"after\"\n handlers.push(...(this.handlers.get(phase) ?? []))\n } else if (parts.length === 2) {\n // Could be wildcard like \"after.user\" or custom event like \"auth.verified\"\n handlers.push(...(this.handlers.get(event) ?? []))\n\n if (parts[0] === 'before' || parts[0] === 'after') {\n handlers.push(...(this.handlers.get(parts[0]) ?? []))\n }\n } else {\n handlers.push(...(this.handlers.get(event) ?? []))\n }\n\n return handlers\n }\n\n /**\n * Determine if we should use ctx.waitUntil (non-blocking) or await (blocking)\n */\n private shouldUseWaitUntil(\n event: string,\n handlers: RegisteredHandler[]\n ): boolean {\n const hasBlockingHandler = handlers.some((h) => h.blocking === true)\n if (hasBlockingHandler) return false\n\n const hasNonBlockingHandler = handlers.some((h) => h.blocking === false)\n if (hasNonBlockingHandler) return true\n\n const phase = event.split('.')[0]\n if (phase === 'before') return false\n if (phase === 'after') return true\n return false // Custom events block by default\n }\n\n /**\n * Execute a single handler with error isolation\n */\n private async executeHandler<E extends EventName>(\n handler: EventHandler,\n context: EventContext<E>,\n event: string\n ): Promise<void> {\n try {\n await handler(context as EventContext)\n } catch (error) {\n this.logger.error('Event handler error', {\n event,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n })\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"events-CzCV8jI8.mjs","names":[],"sources":["../src/events/constants.ts","../src/events/decorators/listener.decorator.ts","../src/events/decorators/on.decorator.ts","../src/events/event-registry.ts"],"sourcesContent":["/**\n * Metadata keys for event listener decorators.\n *\n * Uses `Symbol.for()` (global symbol registry) so that both core and\n * framework packages can reference the same symbols without cross-imports.\n */\nexport const LISTENER_METADATA_KEYS = {\n IS_LISTENER: Symbol.for('stratal:listener'),\n EVENT_HANDLERS: Symbol.for('stratal:listener:handlers'),\n} as const\n","import { Transient } from '../../di/decorators'\nimport type { Constructor } from '../../types'\nimport { LISTENER_METADATA_KEYS } from '../constants'\n\n/**\n * Mark a class as an event listener.\n *\n * Applies `@Transient()` for DI and sets metadata so the module system\n * can auto-discover and wire listener handlers at bootstrap time.\n *\n * @example\n * ```typescript\n * @Listener()\n * export class UserCreatedListener {\n * @On('after.User.create')\n * async sendWelcomeEmail(context: EventContext<'after.User.create'>) {\n * // ...\n * }\n * }\n * ```\n */\nexport function Listener() {\n return function <T extends Constructor>(target: T) {\n Transient()(target)\n Reflect.defineMetadata(LISTENER_METADATA_KEYS.IS_LISTENER, true, target)\n return target\n }\n}\n\n/**\n * Check if a class is decorated with `@Listener()`\n */\nexport function isListener(target: Constructor): boolean {\n return Reflect.getMetadata(LISTENER_METADATA_KEYS.IS_LISTENER, target) === true\n}\n","import { LISTENER_METADATA_KEYS } from '../constants'\nimport type { EventName, EventOptions, ListenerHandlerMetadata } from '../types'\n\n/**\n * Register a method as an event handler within a `@Listener()` class.\n *\n * Accumulates handler metadata on the class so the framework can\n * auto-wire handlers with the EventRegistry at bootstrap time.\n *\n * @param event - Event name to listen for (fully typed with autocomplete)\n * @param options - Optional handler options (priority, blocking)\n *\n * @example\n * ```typescript\n * @Listener()\n * export class AuditListener {\n * @On('after.User.create')\n * async logCreate(context: EventContext<'after.User.create'>) { ... }\n *\n * @On('after.User.delete', { priority: 10 })\n * async logDelete(context: EventContext<'after.User.delete'>) { ... }\n * }\n * ```\n */\nexport function On<E extends EventName>(event: E, options?: EventOptions) {\n return function (\n target: object,\n propertyKey: string,\n _descriptor: PropertyDescriptor\n ) {\n const existingHandlers: ListenerHandlerMetadata[] =\n (Reflect.getMetadata(LISTENER_METADATA_KEYS.EVENT_HANDLERS, target.constructor) as ListenerHandlerMetadata[] | undefined) ?? []\n\n existingHandlers.push({\n methodName: propertyKey,\n event: event as string,\n options,\n })\n\n Reflect.defineMetadata(\n LISTENER_METADATA_KEYS.EVENT_HANDLERS,\n existingHandlers,\n target.constructor\n )\n }\n}\n\n/**\n * Get all `@On()` handler metadata from a listener class\n */\nexport function getListenerHandlers(target: object): ListenerHandlerMetadata[] {\n const metadataTarget = typeof target === 'function' ? target : target.constructor\n return (Reflect.getMetadata(LISTENER_METADATA_KEYS.EVENT_HANDLERS, metadataTarget) as ListenerHandlerMetadata[] | undefined) ?? []\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { LOGGER_TOKENS, type LoggerService } from '../logger'\nimport type {\n EventContext,\n EventHandler,\n EventName,\n EventOptions,\n IEventRegistry,\n RegisteredHandler\n} from './types'\n\n@Transient()\nexport class EventRegistry implements IEventRegistry {\n private handlers = new Map<string, RegisteredHandler[]>()\n\n constructor(\n @inject(DI_TOKENS.ExecutionContext) private readonly ctx: ExecutionContext,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService\n ) { }\n\n on<E extends EventName>(event: E, handler: EventHandler<E>, options?: EventOptions): void {\n const registered: RegisteredHandler = {\n handler: handler as EventHandler,\n priority: options?.priority ?? 0,\n blocking: options?.blocking\n }\n\n const existingHandlers = this.handlers.get(event) ?? []\n existingHandlers.push(registered)\n this.handlers.set(event, existingHandlers)\n\n this.logger.debug('Event handler registered', {\n event,\n priority: registered.priority,\n blocking: registered.blocking\n })\n }\n\n async emit<E extends EventName>(\n event: E,\n context?: Partial<EventContext<E>>\n ): Promise<void> {\n // Build full context with caller-provided fields\n const fullContext = {\n ...context\n } as EventContext<E>\n\n // Find matching handlers using pattern matching\n const matchingHandlers = this.findMatchingHandlers(event)\n\n if (matchingHandlers.length === 0) {\n return\n }\n\n // Sort by priority (higher first)\n const sortedHandlers = [...matchingHandlers].sort(\n (a, b) => b.priority - a.priority\n )\n\n // Determine if we should use waitUntil\n const shouldUseWaitUntil = this.shouldUseWaitUntil(event, sortedHandlers)\n\n // Execute handlers\n const promises = sortedHandlers.map((registered) =>\n this.executeHandler(registered.handler, fullContext, event)\n )\n\n if (shouldUseWaitUntil) {\n // Non-blocking: use ctx.waitUntil\n this.ctx.waitUntil(Promise.all(promises))\n } else {\n // Blocking: await all handlers\n await Promise.all(promises)\n }\n }\n\n off<E extends EventName>(event: E, handler: EventHandler<E>): void {\n const existingHandlers = this.handlers.get(event)\n if (!existingHandlers) return\n\n const filtered = existingHandlers.filter((h) => h.handler !== handler)\n if (filtered.length > 0) {\n this.handlers.set(event, filtered)\n } else {\n this.handlers.delete(event)\n }\n\n this.logger.debug('Event handler unregistered', { event })\n }\n\n once<E extends EventName>(event: E, handler: EventHandler<E>, options?: EventOptions): void {\n const wrappedHandler = (async (context: EventContext<E>) => {\n await handler(context)\n this.off(event, wrappedHandler)\n }) as EventHandler<E>\n\n this.on(event, wrappedHandler, options)\n }\n\n /**\n * Find all handlers matching the event using pattern matching.\n * Order: exact match -> model wildcard -> operation wildcard -> global wildcard\n */\n private findMatchingHandlers(event: string): RegisteredHandler[] {\n const handlers: RegisteredHandler[] = []\n\n const parts = event.split('.')\n\n if (parts.length === 3) {\n // Database event: \"phase.model.operation\"\n const [phase, model, operation] = parts\n\n // 1. Exact match: \"after.user.create\"\n handlers.push(...(this.handlers.get(event) ?? []))\n\n // 2. Model wildcard: \"after.user\"\n handlers.push(...(this.handlers.get(`${phase}.${model}`) ?? []))\n\n // 3. Operation wildcard: \"after.create\"\n handlers.push(...(this.handlers.get(`${phase}.${operation}`) ?? []))\n\n // 4. Global wildcard: \"after\"\n handlers.push(...(this.handlers.get(phase) ?? []))\n } else if (parts.length === 2) {\n // Could be wildcard like \"after.user\" or custom event like \"auth.verified\"\n handlers.push(...(this.handlers.get(event) ?? []))\n\n if (parts[0] === 'before' || parts[0] === 'after') {\n handlers.push(...(this.handlers.get(parts[0]) ?? []))\n }\n } else {\n handlers.push(...(this.handlers.get(event) ?? []))\n }\n\n return handlers\n }\n\n /**\n * Determine if we should use ctx.waitUntil (non-blocking) or await (blocking)\n */\n private shouldUseWaitUntil(\n event: string,\n handlers: RegisteredHandler[]\n ): boolean {\n const hasBlockingHandler = handlers.some((h) => h.blocking === true)\n if (hasBlockingHandler) return false\n\n const hasNonBlockingHandler = handlers.some((h) => h.blocking === false)\n if (hasNonBlockingHandler) return true\n\n const phase = event.split('.')[0]\n if (phase === 'before') return false\n if (phase === 'after') return true\n return false // Custom events block by default\n }\n\n /**\n * Execute a single handler with error isolation\n */\n private async executeHandler<E extends EventName>(\n handler: EventHandler,\n context: EventContext<E>,\n event: string\n ): Promise<void> {\n try {\n await handler(context as EventContext)\n } catch (error) {\n this.logger.error('Event handler error', {\n event,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n })\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { H as ApplicationError, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, s as RouterContext } from "./errors-BdyV5PnY.mjs";
2
- import { t as Controller } from "./controller.decorator-DQzenvSN.mjs";
1
+ import { H as ApplicationError, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, s as RouterContext } from "./errors-COW9-Mar.mjs";
2
+ import { t as Controller } from "./controller.decorator-B9vwn0zK.mjs";
3
3
  //#region src/websocket/decorators/gateway.decorator.ts
4
4
  const GATEWAY_MARKER_KEY = ROUTE_METADATA_KEYS.GATEWAY_MARKER;
5
5
  /**
@@ -173,6 +173,7 @@ var WebSocketBodyNotAvailableError = class extends ApplicationError {
173
173
  * ```
174
174
  */
175
175
  var GatewayContext = class extends RouterContext {
176
+ ws;
176
177
  constructor(c, ws) {
177
178
  super(c);
178
179
  this.ws = ws;
@@ -214,4 +215,4 @@ var GatewayContext = class extends RouterContext {
214
215
  //#endregion
215
216
  export { OnMessage as a, getWsOnMessageMethod as c, isGateway as d, OnError as i, WebSocketDuplicateEventHandlerError as l, WebSocketBodyNotAvailableError as n, getWsOnCloseMethod as o, OnClose as r, getWsOnErrorMethod as s, GatewayContext as t, Gateway as u };
216
217
 
217
- //# sourceMappingURL=gateway-context-CdJjpUCW.mjs.map
218
+ //# sourceMappingURL=gateway-context-CXmXtaUP.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-context-CdJjpUCW.mjs","names":[],"sources":["../src/websocket/decorators/gateway.decorator.ts","../src/websocket/errors/websocket-duplicate-event-handler.error.ts","../src/websocket/decorators/ws-event.decorator.ts","../src/websocket/errors/websocket-body-not-available.error.ts","../src/websocket/gateway-context.ts"],"sourcesContent":["import { ROUTE_METADATA_KEYS } from '../../router/constants'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { type Constructor } from '../../types'\nimport type { GatewayOptions } from '../../websocket/types'\n\nconst GATEWAY_MARKER_KEY = ROUTE_METADATA_KEYS.GATEWAY_MARKER\n\n/**\n * Gateway decorator for WebSocket route registration\n *\n * Marks a class as a WebSocket gateway and stores route metadata.\n * Reuses the same metadata key as @Controller for middleware compatibility —\n * `getControllerRoute()`, `forRoutes()`, and the entire middleware system work\n * with zero changes.\n *\n * @param route - WebSocket route path (e.g., '/ws/chat')\n *\n * @example\n * ```typescript\n * import { type GatewayContext, Gateway, OnMessage, OnClose } from 'stratal/websocket'\n *\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send('ack')\n * }\n *\n * @OnClose()\n * handleClose(evt: CloseEvent, ctx: GatewayContext) {\n * console.log('closed')\n * }\n * }\n * ```\n */\nexport function Gateway(route: string, options?: GatewayOptions) {\n return function <T extends Constructor>(target: T) {\n Controller(route, options)(target)\n Reflect.defineMetadata(GATEWAY_MARKER_KEY, true, target)\n return target\n }\n}\n\n/**\n * Check if a class is a WebSocket gateway\n *\n * @param target - Class constructor or instance\n * @returns true if the class is decorated with @Gateway\n */\nexport function isGateway(target: object): boolean {\n const metadataTarget = typeof target === 'function' ? target : (target as { constructor: object }).constructor\n return Reflect.getMetadata(GATEWAY_MARKER_KEY, metadataTarget) === true\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\nexport class WebSocketDuplicateEventHandlerError extends ApplicationError {\n constructor(decorator: string, existingMethod: string) {\n super(\n 'errors.websocketDuplicateEventHandler',\n ERROR_CODES.SYSTEM.WEBSOCKET_DUPLICATE_EVENT_HANDLER,\n { decorator, existingMethod }\n )\n }\n}\n","import { ROUTE_METADATA_KEYS } from '../../router/constants'\nimport type { Constructor } from '../../types'\nimport { WebSocketDuplicateEventHandlerError } from '../errors/websocket-duplicate-event-handler.error'\n\nconst WS_ON_MESSAGE_KEY = ROUTE_METADATA_KEYS.WS_ON_MESSAGE\nconst WS_ON_CLOSE_KEY = ROUTE_METADATA_KEYS.WS_ON_CLOSE\nconst WS_ON_ERROR_KEY = ROUTE_METADATA_KEYS.WS_ON_ERROR\n\n/**\n * Define a single-handler metadata key on the prototype.\n * Throws if a different method already owns this key (prevents silent override).\n */\nfunction defineSingleHandlerMetadata(key: string | symbol, propertyKey: string | symbol, target: object, decoratorName: string): void {\n const existing = Reflect.getMetadata(key, target) as string | symbol | undefined\n if (existing !== undefined && existing !== propertyKey) {\n throw new WebSocketDuplicateEventHandlerError(decoratorName, String(existing))\n }\n Reflect.defineMetadata(key, propertyKey, target)\n}\n\n/**\n * Marks a method as the WebSocket message handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send(evt.data)\n * }\n * }\n * ```\n */\nexport function OnMessage(): MethodDecorator {\n // `_target` is the class prototype (method decorator convention).\n // The getter functions below read from `target.prototype` symmetrically.\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_MESSAGE_KEY, propertyKey, _target, 'OnMessage')\n }\n}\n\n/**\n * Marks a method as the WebSocket close handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnClose()\n * handleClose(evt: CloseEvent, ctx: GatewayContext) {\n * console.log('closed')\n * }\n * }\n * ```\n */\nexport function OnClose(): MethodDecorator {\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_CLOSE_KEY, propertyKey, _target, 'OnClose')\n }\n}\n\n/**\n * Marks a method as the WebSocket error handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnError()\n * handleError(evt: Event, ctx: GatewayContext) {\n * console.error('WebSocket error', evt)\n * }\n * }\n * ```\n */\nexport function OnError(): MethodDecorator {\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_ERROR_KEY, propertyKey, _target, 'OnError')\n }\n}\n\n/**\n * Get the method name decorated with @OnMessage\n */\nexport function getWsOnMessageMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_MESSAGE_KEY, target.prototype as object) as string | undefined\n}\n\n/**\n * Get the method name decorated with @OnClose\n */\nexport function getWsOnCloseMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_CLOSE_KEY, target.prototype as object) as string | undefined\n}\n\n/**\n * Get the method name decorated with @OnError\n */\nexport function getWsOnErrorMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_ERROR_KEY, target.prototype as object) as string | undefined\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\nexport class WebSocketBodyNotAvailableError extends ApplicationError {\n constructor() {\n super(\n 'errors.websocketBodyNotAvailable',\n ERROR_CODES.SYSTEM.WEBSOCKET_BODY_NOT_AVAILABLE\n )\n }\n}\n","import type { Context } from 'hono'\nimport type { WSContext, WSReadyState } from 'hono/ws'\nimport type { ContextQueryResult } from '../router/router-context'\nimport { RouterContext } from '../router/router-context'\nimport type { RouterEnv } from '../router/types'\nimport { WebSocketBodyNotAvailableError } from './errors/websocket-body-not-available.error'\n\n/**\n * WebSocket gateway context\n *\n * Extends RouterContext with WebSocket-specific methods.\n * Inherits `getContainer()`, `param()`, `query()`, `header()`, `getLocale()`\n * from RouterContext. HTTP response methods (`json()`, `redirect()`, etc.) are\n * inherited but harmless post-upgrade.\n *\n * @example\n * ```typescript\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send('ack') // convenience method\n * ctx.header('Authorization') // upgrade request headers\n * }\n * ```\n */\nexport class GatewayContext extends RouterContext {\n constructor(c: Context<RouterEnv>, public readonly ws: WSContext) {\n super(c)\n }\n\n /** Send data through the WebSocket connection */\n send(data: string | ArrayBuffer | Uint8Array<ArrayBuffer>): void {\n this.ws.send(data)\n }\n\n /** Close the WebSocket connection */\n close(code?: number, reason?: string): void {\n this.ws.close(code, reason)\n }\n\n /** Current WebSocket ready state */\n get readyState(): WSReadyState {\n return this.ws.readyState\n }\n\n /**\n * Get route parameter value(s) from the raw request — WebSocket gateways are\n * not OpenAPI-registered, so reads come straight from Hono's matcher.\n *\n * - With a key → single string value.\n * - With no args → full `Record<string, string>` (or `{}` when none).\n *\n * @param key - Parameter name (e.g., 'id' for /ws/chat/:id)\n */\n override param(): Record<string, string>\n override param(key: string): string\n override param(key?: string): string | Record<string, string> {\n if (key === undefined) return this.c.req.param() ?? {}\n return this.c.req.param(key)!\n }\n\n /**\n * Get query parameter value from the raw request (no OpenAPI validation)\n *\n * @param key - Query parameter name\n */\n override query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K> {\n if (key) {\n return this.c.req.query(key) as ContextQueryResult<R, K>\n }\n return this.c.req.query() as ContextQueryResult<R, K>\n }\n\n /**\n * Request body is not available in WebSocket gateways\n *\n * @throws WebSocketBodyNotAvailableError always — WebSocket upgrade requests do not have a body\n */\n override body<T>(): Promise<T> {\n throw new WebSocketBodyNotAvailableError()\n }\n}\n"],"mappings":";;;AAKA,MAAM,qBAAqB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B/C,SAAgB,QAAQ,OAAe,SAA0B;AAC/D,QAAO,SAAiC,QAAW;AACjD,aAAW,OAAO,QAAQ,CAAC,OAAO;AAClC,UAAQ,eAAe,oBAAoB,MAAM,OAAO;AACxD,SAAO;;;;;;;;;AAUX,SAAgB,UAAU,QAAyB;CACjD,MAAM,iBAAiB,OAAO,WAAW,aAAa,SAAU,OAAmC;AACnG,QAAO,QAAQ,YAAY,oBAAoB,eAAe,KAAK;;;;ACjDrE,IAAa,sCAAb,cAAyD,iBAAiB;CACxE,YAAY,WAAmB,gBAAwB;AACrD,QACE,yCACA,YAAY,OAAO,mCACnB;GAAE;GAAW;GAAgB,CAC9B;;;;;ACJL,MAAM,oBAAoB,oBAAoB;AAC9C,MAAM,kBAAkB,oBAAoB;AAC5C,MAAM,kBAAkB,oBAAoB;;;;;AAM5C,SAAS,4BAA4B,KAAsB,aAA8B,QAAgB,eAA6B;CACpI,MAAM,WAAW,QAAQ,YAAY,KAAK,OAAO;AACjD,KAAI,aAAa,KAAA,KAAa,aAAa,YACzC,OAAM,IAAI,oCAAoC,eAAe,OAAO,SAAS,CAAC;AAEhF,SAAQ,eAAe,KAAK,aAAa,OAAO;;;;;;;;;;;;;;;;AAiBlD,SAAgB,YAA6B;AAG3C,SAAQ,SAAiB,gBAAiC;AACxD,8BAA4B,mBAAmB,aAAa,SAAS,YAAY;;;;;;;;;;;;;;;;;AAkBrF,SAAgB,UAA2B;AACzC,SAAQ,SAAiB,gBAAiC;AACxD,8BAA4B,iBAAiB,aAAa,SAAS,UAAU;;;;;;;;;;;;;;;;;AAkBjF,SAAgB,UAA2B;AACzC,SAAQ,SAAiB,gBAAiC;AACxD,8BAA4B,iBAAiB,aAAa,SAAS,UAAU;;;;;;AAOjF,SAAgB,qBAAqB,QAAyC;AAC5E,QAAO,QAAQ,YAAY,mBAAmB,OAAO,UAAoB;;;;;AAM3E,SAAgB,mBAAmB,QAAyC;AAC1E,QAAO,QAAQ,YAAY,iBAAiB,OAAO,UAAoB;;;;;AAMzE,SAAgB,mBAAmB,QAAyC;AAC1E,QAAO,QAAQ,YAAY,iBAAiB,OAAO,UAAoB;;;;AClGzE,IAAa,iCAAb,cAAoD,iBAAiB;CACnE,cAAc;AACZ,QACE,oCACA,YAAY,OAAO,6BACpB;;;;;;;;;;;;;;;;;;;;;;ACiBL,IAAa,iBAAb,cAAoC,cAAc;CAChD,YAAY,GAAuB,IAA+B;AAChE,QAAM,EAAE;AADyC,OAAA,KAAA;;;CAKnD,KAAK,MAA4D;AAC/D,OAAK,GAAG,KAAK,KAAK;;;CAIpB,MAAM,MAAe,QAAuB;AAC1C,OAAK,GAAG,MAAM,MAAM,OAAO;;;CAI7B,IAAI,aAA2B;AAC7B,SAAO,KAAK,GAAG;;CAcjB,MAAe,KAA+C;AAC5D,MAAI,QAAQ,KAAA,EAAW,QAAO,KAAK,EAAE,IAAI,OAAO,IAAI,EAAE;AACtD,SAAO,KAAK,EAAE,IAAI,MAAM,IAAI;;;;;;;CAQ9B,MAAoH,KAAmC;AACrJ,MAAI,IACF,QAAO,KAAK,EAAE,IAAI,MAAM,IAAI;AAE9B,SAAO,KAAK,EAAE,IAAI,OAAO;;;;;;;CAQ3B,OAA+B;AAC7B,QAAM,IAAI,gCAAgC"}
1
+ {"version":3,"file":"gateway-context-CXmXtaUP.mjs","names":[],"sources":["../src/websocket/decorators/gateway.decorator.ts","../src/websocket/errors/websocket-duplicate-event-handler.error.ts","../src/websocket/decorators/ws-event.decorator.ts","../src/websocket/errors/websocket-body-not-available.error.ts","../src/websocket/gateway-context.ts"],"sourcesContent":["import { ROUTE_METADATA_KEYS } from '../../router/constants'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { type Constructor } from '../../types'\nimport type { GatewayOptions } from '../../websocket/types'\n\nconst GATEWAY_MARKER_KEY = ROUTE_METADATA_KEYS.GATEWAY_MARKER\n\n/**\n * Gateway decorator for WebSocket route registration\n *\n * Marks a class as a WebSocket gateway and stores route metadata.\n * Reuses the same metadata key as @Controller for middleware compatibility —\n * `getControllerRoute()`, `forRoutes()`, and the entire middleware system work\n * with zero changes.\n *\n * @param route - WebSocket route path (e.g., '/ws/chat')\n *\n * @example\n * ```typescript\n * import { type GatewayContext, Gateway, OnMessage, OnClose } from 'stratal/websocket'\n *\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send('ack')\n * }\n *\n * @OnClose()\n * handleClose(evt: CloseEvent, ctx: GatewayContext) {\n * console.log('closed')\n * }\n * }\n * ```\n */\nexport function Gateway(route: string, options?: GatewayOptions) {\n return function <T extends Constructor>(target: T) {\n Controller(route, options)(target)\n Reflect.defineMetadata(GATEWAY_MARKER_KEY, true, target)\n return target\n }\n}\n\n/**\n * Check if a class is a WebSocket gateway\n *\n * @param target - Class constructor or instance\n * @returns true if the class is decorated with @Gateway\n */\nexport function isGateway(target: object): boolean {\n const metadataTarget = typeof target === 'function' ? target : (target as { constructor: object }).constructor\n return Reflect.getMetadata(GATEWAY_MARKER_KEY, metadataTarget) === true\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\nexport class WebSocketDuplicateEventHandlerError extends ApplicationError {\n constructor(decorator: string, existingMethod: string) {\n super(\n 'errors.websocketDuplicateEventHandler',\n ERROR_CODES.SYSTEM.WEBSOCKET_DUPLICATE_EVENT_HANDLER,\n { decorator, existingMethod }\n )\n }\n}\n","import { ROUTE_METADATA_KEYS } from '../../router/constants'\nimport type { Constructor } from '../../types'\nimport { WebSocketDuplicateEventHandlerError } from '../errors/websocket-duplicate-event-handler.error'\n\nconst WS_ON_MESSAGE_KEY = ROUTE_METADATA_KEYS.WS_ON_MESSAGE\nconst WS_ON_CLOSE_KEY = ROUTE_METADATA_KEYS.WS_ON_CLOSE\nconst WS_ON_ERROR_KEY = ROUTE_METADATA_KEYS.WS_ON_ERROR\n\n/**\n * Define a single-handler metadata key on the prototype.\n * Throws if a different method already owns this key (prevents silent override).\n */\nfunction defineSingleHandlerMetadata(key: string | symbol, propertyKey: string | symbol, target: object, decoratorName: string): void {\n const existing = Reflect.getMetadata(key, target) as string | symbol | undefined\n if (existing !== undefined && existing !== propertyKey) {\n throw new WebSocketDuplicateEventHandlerError(decoratorName, String(existing))\n }\n Reflect.defineMetadata(key, propertyKey, target)\n}\n\n/**\n * Marks a method as the WebSocket message handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send(evt.data)\n * }\n * }\n * ```\n */\nexport function OnMessage(): MethodDecorator {\n // `_target` is the class prototype (method decorator convention).\n // The getter functions below read from `target.prototype` symmetrically.\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_MESSAGE_KEY, propertyKey, _target, 'OnMessage')\n }\n}\n\n/**\n * Marks a method as the WebSocket close handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnClose()\n * handleClose(evt: CloseEvent, ctx: GatewayContext) {\n * console.log('closed')\n * }\n * }\n * ```\n */\nexport function OnClose(): MethodDecorator {\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_CLOSE_KEY, propertyKey, _target, 'OnClose')\n }\n}\n\n/**\n * Marks a method as the WebSocket error handler\n *\n * @example\n * ```typescript\n * @Gateway('/ws/chat')\n * class ChatGateway {\n * @OnError()\n * handleError(evt: Event, ctx: GatewayContext) {\n * console.error('WebSocket error', evt)\n * }\n * }\n * ```\n */\nexport function OnError(): MethodDecorator {\n return (_target: object, propertyKey: string | symbol) => {\n defineSingleHandlerMetadata(WS_ON_ERROR_KEY, propertyKey, _target, 'OnError')\n }\n}\n\n/**\n * Get the method name decorated with @OnMessage\n */\nexport function getWsOnMessageMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_MESSAGE_KEY, target.prototype as object) as string | undefined\n}\n\n/**\n * Get the method name decorated with @OnClose\n */\nexport function getWsOnCloseMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_CLOSE_KEY, target.prototype as object) as string | undefined\n}\n\n/**\n * Get the method name decorated with @OnError\n */\nexport function getWsOnErrorMethod(target: Constructor): string | undefined {\n return Reflect.getMetadata(WS_ON_ERROR_KEY, target.prototype as object) as string | undefined\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\nexport class WebSocketBodyNotAvailableError extends ApplicationError {\n constructor() {\n super(\n 'errors.websocketBodyNotAvailable',\n ERROR_CODES.SYSTEM.WEBSOCKET_BODY_NOT_AVAILABLE\n )\n }\n}\n","import type { Context } from 'hono'\nimport type { WSContext, WSReadyState } from 'hono/ws'\nimport type { ContextQueryResult } from '../router/router-context'\nimport { RouterContext } from '../router/router-context'\nimport type { RouterEnv } from '../router/types'\nimport { WebSocketBodyNotAvailableError } from './errors/websocket-body-not-available.error'\n\n/**\n * WebSocket gateway context\n *\n * Extends RouterContext with WebSocket-specific methods.\n * Inherits `getContainer()`, `param()`, `query()`, `header()`, `getLocale()`\n * from RouterContext. HTTP response methods (`json()`, `redirect()`, etc.) are\n * inherited but harmless post-upgrade.\n *\n * @example\n * ```typescript\n * @OnMessage()\n * handleMessage(evt: MessageEvent, ctx: GatewayContext) {\n * ctx.send('ack') // convenience method\n * ctx.header('Authorization') // upgrade request headers\n * }\n * ```\n */\nexport class GatewayContext extends RouterContext {\n constructor(c: Context<RouterEnv>, public readonly ws: WSContext) {\n super(c)\n }\n\n /** Send data through the WebSocket connection */\n send(data: string | ArrayBuffer | Uint8Array<ArrayBuffer>): void {\n this.ws.send(data)\n }\n\n /** Close the WebSocket connection */\n close(code?: number, reason?: string): void {\n this.ws.close(code, reason)\n }\n\n /** Current WebSocket ready state */\n get readyState(): WSReadyState {\n return this.ws.readyState\n }\n\n /**\n * Get route parameter value(s) from the raw request — WebSocket gateways are\n * not OpenAPI-registered, so reads come straight from Hono's matcher.\n *\n * - With a key → single string value.\n * - With no args → full `Record<string, string>` (or `{}` when none).\n *\n * @param key - Parameter name (e.g., 'id' for /ws/chat/:id)\n */\n override param(): Record<string, string>\n override param(key: string): string\n override param(key?: string): string | Record<string, string> {\n if (key === undefined) return this.c.req.param() ?? {}\n return this.c.req.param(key)!\n }\n\n /**\n * Get query parameter value from the raw request (no OpenAPI validation)\n *\n * @param key - Query parameter name\n */\n override query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K> {\n if (key) {\n return this.c.req.query(key) as ContextQueryResult<R, K>\n }\n return this.c.req.query() as ContextQueryResult<R, K>\n }\n\n /**\n * Request body is not available in WebSocket gateways\n *\n * @throws WebSocketBodyNotAvailableError always — WebSocket upgrade requests do not have a body\n */\n override body<T>(): Promise<T> {\n throw new WebSocketBodyNotAvailableError()\n }\n}\n"],"mappings":";;;AAKA,MAAM,qBAAqB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B/C,SAAgB,QAAQ,OAAe,SAA0B;CAC/D,OAAO,SAAiC,QAAW;EACjD,WAAW,OAAO,QAAQ,CAAC,OAAO;EAClC,QAAQ,eAAe,oBAAoB,MAAM,OAAO;EACxD,OAAO;;;;;;;;;AAUX,SAAgB,UAAU,QAAyB;CACjD,MAAM,iBAAiB,OAAO,WAAW,aAAa,SAAU,OAAmC;CACnG,OAAO,QAAQ,YAAY,oBAAoB,eAAe,KAAK;;;;ACjDrE,IAAa,sCAAb,cAAyD,iBAAiB;CACxE,YAAY,WAAmB,gBAAwB;EACrD,MACE,yCACA,YAAY,OAAO,mCACnB;GAAE;GAAW;GAAgB,CAC9B;;;;;ACJL,MAAM,oBAAoB,oBAAoB;AAC9C,MAAM,kBAAkB,oBAAoB;AAC5C,MAAM,kBAAkB,oBAAoB;;;;;AAM5C,SAAS,4BAA4B,KAAsB,aAA8B,QAAgB,eAA6B;CACpI,MAAM,WAAW,QAAQ,YAAY,KAAK,OAAO;CACjD,IAAI,aAAa,KAAA,KAAa,aAAa,aACzC,MAAM,IAAI,oCAAoC,eAAe,OAAO,SAAS,CAAC;CAEhF,QAAQ,eAAe,KAAK,aAAa,OAAO;;;;;;;;;;;;;;;;AAiBlD,SAAgB,YAA6B;CAG3C,QAAQ,SAAiB,gBAAiC;EACxD,4BAA4B,mBAAmB,aAAa,SAAS,YAAY;;;;;;;;;;;;;;;;;AAkBrF,SAAgB,UAA2B;CACzC,QAAQ,SAAiB,gBAAiC;EACxD,4BAA4B,iBAAiB,aAAa,SAAS,UAAU;;;;;;;;;;;;;;;;;AAkBjF,SAAgB,UAA2B;CACzC,QAAQ,SAAiB,gBAAiC;EACxD,4BAA4B,iBAAiB,aAAa,SAAS,UAAU;;;;;;AAOjF,SAAgB,qBAAqB,QAAyC;CAC5E,OAAO,QAAQ,YAAY,mBAAmB,OAAO,UAAoB;;;;;AAM3E,SAAgB,mBAAmB,QAAyC;CAC1E,OAAO,QAAQ,YAAY,iBAAiB,OAAO,UAAoB;;;;;AAMzE,SAAgB,mBAAmB,QAAyC;CAC1E,OAAO,QAAQ,YAAY,iBAAiB,OAAO,UAAoB;;;;AClGzE,IAAa,iCAAb,cAAoD,iBAAiB;CACnE,cAAc;EACZ,MACE,oCACA,YAAY,OAAO,6BACpB;;;;;;;;;;;;;;;;;;;;;;ACiBL,IAAa,iBAAb,cAAoC,cAAc;CACG;CAAnD,YAAY,GAAuB,IAA+B;EAChE,MAAM,EAAE;EADyC,KAAA,KAAA;;;CAKnD,KAAK,MAA4D;EAC/D,KAAK,GAAG,KAAK,KAAK;;;CAIpB,MAAM,MAAe,QAAuB;EAC1C,KAAK,GAAG,MAAM,MAAM,OAAO;;;CAI7B,IAAI,aAA2B;EAC7B,OAAO,KAAK,GAAG;;CAcjB,MAAe,KAA+C;EAC5D,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAK,EAAE,IAAI,OAAO,IAAI,EAAE;EACtD,OAAO,KAAK,EAAE,IAAI,MAAM,IAAI;;;;;;;CAQ9B,MAAoH,KAAmC;EACrJ,IAAI,KACF,OAAO,KAAK,EAAE,IAAI,MAAM,IAAI;EAE9B,OAAO,KAAK,EAAE,IAAI,OAAO;;;;;;;CAQ3B,OAA+B;EAC7B,MAAM,IAAI,gCAAgC"}
@@ -1,4 +1,4 @@
1
- import { Dr as Container, V as RouterContext } from "../index-D0US0X14.mjs";
1
+ import { Dr as Container, V as RouterContext } from "../index-ByOyTmqf.mjs";
2
2
  import { t as Constructor } from "../types-cySNS_lp.mjs";
3
3
  import { i as LoggerService } from "../index-DBd_2wv8.mjs";
4
4
 
@@ -1,2 +1,2 @@
1
- import { a as GUARD_METADATA_KEY, i as getMethodGuards, n as UseGuards, r as getControllerGuards, t as GuardExecutionService } from "../guards-DUk_Kzst.mjs";
1
+ import { a as GUARD_METADATA_KEY, i as getMethodGuards, n as UseGuards, r as getControllerGuards, t as GuardExecutionService } from "../guards-DU1_J9YA.mjs";
2
2
  export { GUARD_METADATA_KEY, GuardExecutionService, UseGuards, getControllerGuards, getMethodGuards };
@@ -95,6 +95,7 @@ function getMethodGuards(target, propertyKey) {
95
95
  * Guards are executed in order; all must pass for the request to proceed.
96
96
  */
97
97
  var GuardExecutionService = class {
98
+ logger;
98
99
  constructor(logger) {
99
100
  this.logger = logger;
100
101
  }
@@ -148,4 +149,4 @@ var GuardExecutionService = class {
148
149
  //#endregion
149
150
  export { GUARD_METADATA_KEY as a, getMethodGuards as i, UseGuards as n, getControllerGuards as r, GuardExecutionService as t };
150
151
 
151
- //# sourceMappingURL=guards-DUk_Kzst.mjs.map
152
+ //# sourceMappingURL=guards-DU1_J9YA.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"guards-DUk_Kzst.mjs","names":[],"sources":["../src/guards/types.ts","../src/guards/use-guards.decorator.ts","../src/guards/guard-execution.service.ts"],"sourcesContent":["import type { RouterContext } from '../router'\nimport type { Constructor } from '../types'\n\n/**\n * Interface for guards that control access to routes\n *\n * Guards are executed after middlewares but before route handlers.\n * They determine if a request should be allowed to proceed.\n *\n * @example\n * ```typescript\n * class RoleGuard implements CanActivate {\n * constructor(private readonly role: string) {}\n *\n * async canActivate(context: RouterContext): Promise<boolean> {\n * const user = context.getUser()\n * return user?.roles.includes(this.role) ?? false\n * }\n * }\n * ```\n */\nexport interface CanActivate {\n /**\n * Determine if the request should be allowed\n *\n * @param context - Router context with request/response helpers\n * @returns true to allow, false to deny (throws 403)\n */\n canActivate(context: RouterContext): boolean | Promise<boolean>\n}\n\n/**\n * Type for guard class constructors\n */\nexport type GuardClass = Constructor<CanActivate>\n\n/**\n * Guard can be a class constructor or an instance\n * Instances are used for factory-created guards with configuration\n */\nexport type Guard = GuardClass | CanActivate\n\n/**\n * Options for AuthGuard factory\n */\nexport interface AuthGuardOptions {\n /**\n * Required permissions for authorization as `\"resource:action\"` strings.\n * If provided, permission check is performed after authentication.\n * If omitted, only authentication is required.\n *\n * Multiple permissions are combined with AND logic (all must be satisfied).\n *\n * @example\n * ```typescript\n * @UseGuards(AuthGuard({ permissions: 'posts:update' }))\n * @UseGuards(AuthGuard({ permissions: ['posts:update', 'posts:delete'] }))\n * @UseGuards(AuthGuard({ permissions: 'admin:access' }))\n * ```\n */\n permissions?: string | string[]\n}\n\n/**\n * Metadata stored by `@UseGuards` decorator\n */\nexport interface GuardMetadata {\n guards: Guard[]\n}\n\n/**\n * Metadata key for guard storage\n */\nexport const GUARD_METADATA_KEY = Symbol.for('stratal:guards')\n","import { GUARD_METADATA_KEY, type Guard, type GuardMetadata } from './types'\n\n/**\n * UseGuards Decorator\n *\n * Applies one or more guards to a controller or method.\n * Guards are executed in order and all must pass for the request to proceed.\n *\n * **Execution Order:**\n * 1. Request → Global Middlewares → Route Middlewares\n * 2. **Guards (controller-level, then method-level)**\n * 3. Route Handler\n *\n * **Guard Resolution:**\n * - Guard classes are resolved from the request-scoped DI container\n * - Guard instances (from factory functions) are used directly\n *\n * @param guards - Guard classes or instances to apply\n *\n * @example Authentication only\n * ```typescript\n * @Controller('/api/v1/profile')\n * @UseGuards(AuthGuard())\n * export class ProfileController {\n * show() { } // Requires authentication\n * }\n * ```\n *\n * @example Authentication with permissions\n * ```typescript\n * @Controller('/api/v1/students')\n * @UseGuards(AuthGuard({ scopes: ['students:read'] }))\n * export class StudentsController {\n * index() { } // Requires 'students:read' permission\n * }\n * ```\n *\n * @example Method-level guards\n * ```typescript\n * @Controller('/api/v1/students')\n * @UseGuards(AuthGuard()) // Controller-level: auth only\n * export class StudentsController {\n * index() { } // Auth only (inherited)\n *\n * @UseGuards(AuthGuard({ scopes: ['students:create'] }))\n * create() { } // Auth + 'students:create' permission\n * }\n * ```\n *\n * @example Multiple guards\n * ```typescript\n * @UseGuards(AuthGuard(), RateLimitGuard(), CustomGuard())\n * export class SecureController {\n * // All guards must pass\n * }\n * ```\n */\nexport function UseGuards(...guards: Guard[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const metadata: GuardMetadata = { guards }\n\n if (propertyKey !== undefined) {\n // Method decorator - store on method\n Reflect.defineMetadata(GUARD_METADATA_KEY, metadata, target, propertyKey)\n } else {\n // Class decorator - store on class\n Reflect.defineMetadata(GUARD_METADATA_KEY, metadata, target)\n }\n }\n}\n\n/**\n * Get controller-level guard metadata\n *\n * @param target - Controller class\n * @returns Guard metadata or undefined if not decorated\n */\nexport function getControllerGuards(target: object): GuardMetadata | undefined {\n return Reflect.getMetadata(GUARD_METADATA_KEY, target) as GuardMetadata | undefined\n}\n\n/**\n * Get method-level guard metadata\n *\n * @param target - Controller prototype\n * @param propertyKey - Method name\n * @returns Guard metadata or undefined if not decorated\n */\nexport function getMethodGuards(target: object, propertyKey: string | symbol): GuardMetadata | undefined {\n return Reflect.getMetadata(GUARD_METADATA_KEY, target, propertyKey) as GuardMetadata | undefined\n}\n","import type { Container } from '../di'\nimport type { LoggerService } from '../logger'\nimport type { RouterContext } from '../router'\nimport type { CanActivate, Guard } from './types'\n\n/**\n * Guard Execution Service\n *\n * Executes guards for a route and determines if the request should proceed.\n * Guards are executed in order; all must pass for the request to proceed.\n */\nexport class GuardExecutionService {\n constructor(private readonly logger: LoggerService) { }\n\n /**\n * Execute all guards for a route\n *\n * @param guards - Array of guards (classes or instances)\n * @param context - Router context\n * @param container - Request-scoped DI container\n * @returns true if all guards pass\n * @throws Error from first failing guard\n */\n async executeGuards(\n guards: Guard[],\n context: RouterContext,\n container: Container\n ): Promise<boolean> {\n if (guards.length === 0) {\n return true\n }\n\n this.logger.debug('Executing guards', {\n guardCount: guards.length,\n path: context.c.req.path,\n method: context.c.req.method,\n })\n\n for (const guard of guards) {\n const guardInstance = this.resolveGuard(guard, container)\n const canActivate = await guardInstance.canActivate(context)\n\n if (!canActivate) {\n this.logger.debug('Guard denied access', {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- guard.constructor may be null at runtime\n guard: guard.constructor?.name || 'AnonymousGuard',\n path: context.c.req.path,\n })\n return false\n }\n }\n\n this.logger.debug('All guards passed', {\n guardCount: guards.length,\n path: context.c.req.path,\n })\n\n return true\n }\n\n /**\n * Resolve a guard to an instance\n *\n * @param guard - Guard class or instance\n * @param container - Request-scoped DI container\n * @returns Guard instance\n */\n private resolveGuard(guard: Guard, container: Container): CanActivate {\n // If already an instance (has canActivate method), use directly\n if (this.isGuardInstance(guard)) {\n return guard\n }\n\n // Otherwise, resolve from container\n return container.resolve<CanActivate>(guard)\n }\n\n /**\n * Type guard to check if value is a guard instance\n */\n private isGuardInstance(guard: Guard): guard is CanActivate {\n return (\n typeof guard === 'object' &&\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typeof null === 'object', null check is required\n guard !== null &&\n 'canActivate' in guard &&\n typeof guard.canActivate === 'function'\n )\n }\n}\n"],"mappings":";;;;AAyEA,MAAa,qBAAqB,OAAO,IAAI,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChB9D,SAAgB,UAAU,GAAG,QAAmD;AAC9E,SAAQ,QAAgB,gBAAkC;EACxD,MAAM,WAA0B,EAAE,QAAQ;AAE1C,MAAI,gBAAgB,KAAA,EAElB,SAAQ,eAAe,oBAAoB,UAAU,QAAQ,YAAY;MAGzE,SAAQ,eAAe,oBAAoB,UAAU,OAAO;;;;;;;;;AAWlE,SAAgB,oBAAoB,QAA2C;AAC7E,QAAO,QAAQ,YAAY,oBAAoB,OAAO;;;;;;;;;AAUxD,SAAgB,gBAAgB,QAAgB,aAAyD;AACvG,QAAO,QAAQ,YAAY,oBAAoB,QAAQ,YAAY;;;;;;;;;;AC9ErE,IAAa,wBAAb,MAAmC;CACjC,YAAY,QAAwC;AAAvB,OAAA,SAAA;;;;;;;;;;;CAW7B,MAAM,cACJ,QACA,SACA,WACkB;AAClB,MAAI,OAAO,WAAW,EACpB,QAAO;AAGT,OAAK,OAAO,MAAM,oBAAoB;GACpC,YAAY,OAAO;GACnB,MAAM,QAAQ,EAAE,IAAI;GACpB,QAAQ,QAAQ,EAAE,IAAI;GACvB,CAAC;AAEF,OAAK,MAAM,SAAS,OAIlB,KAAI,CAAC,MAHiB,KAAK,aAAa,OAAO,UACR,CAAC,YAAY,QAAQ,EAE1C;AAChB,QAAK,OAAO,MAAM,uBAAuB;IAEvC,OAAO,MAAM,aAAa,QAAQ;IAClC,MAAM,QAAQ,EAAE,IAAI;IACrB,CAAC;AACF,UAAO;;AAIX,OAAK,OAAO,MAAM,qBAAqB;GACrC,YAAY,OAAO;GACnB,MAAM,QAAQ,EAAE,IAAI;GACrB,CAAC;AAEF,SAAO;;;;;;;;;CAUT,aAAqB,OAAc,WAAmC;AAEpE,MAAI,KAAK,gBAAgB,MAAM,CAC7B,QAAO;AAIT,SAAO,UAAU,QAAqB,MAAM;;;;;CAM9C,gBAAwB,OAAoC;AAC1D,SACE,OAAO,UAAU,YAEjB,UAAU,QACV,iBAAiB,SACjB,OAAO,MAAM,gBAAgB"}
1
+ {"version":3,"file":"guards-DU1_J9YA.mjs","names":[],"sources":["../src/guards/types.ts","../src/guards/use-guards.decorator.ts","../src/guards/guard-execution.service.ts"],"sourcesContent":["import type { RouterContext } from '../router'\nimport type { Constructor } from '../types'\n\n/**\n * Interface for guards that control access to routes\n *\n * Guards are executed after middlewares but before route handlers.\n * They determine if a request should be allowed to proceed.\n *\n * @example\n * ```typescript\n * class RoleGuard implements CanActivate {\n * constructor(private readonly role: string) {}\n *\n * async canActivate(context: RouterContext): Promise<boolean> {\n * const user = context.getUser()\n * return user?.roles.includes(this.role) ?? false\n * }\n * }\n * ```\n */\nexport interface CanActivate {\n /**\n * Determine if the request should be allowed\n *\n * @param context - Router context with request/response helpers\n * @returns true to allow, false to deny (throws 403)\n */\n canActivate(context: RouterContext): boolean | Promise<boolean>\n}\n\n/**\n * Type for guard class constructors\n */\nexport type GuardClass = Constructor<CanActivate>\n\n/**\n * Guard can be a class constructor or an instance\n * Instances are used for factory-created guards with configuration\n */\nexport type Guard = GuardClass | CanActivate\n\n/**\n * Options for AuthGuard factory\n */\nexport interface AuthGuardOptions {\n /**\n * Required permissions for authorization as `\"resource:action\"` strings.\n * If provided, permission check is performed after authentication.\n * If omitted, only authentication is required.\n *\n * Multiple permissions are combined with AND logic (all must be satisfied).\n *\n * @example\n * ```typescript\n * @UseGuards(AuthGuard({ permissions: 'posts:update' }))\n * @UseGuards(AuthGuard({ permissions: ['posts:update', 'posts:delete'] }))\n * @UseGuards(AuthGuard({ permissions: 'admin:access' }))\n * ```\n */\n permissions?: string | string[]\n}\n\n/**\n * Metadata stored by `@UseGuards` decorator\n */\nexport interface GuardMetadata {\n guards: Guard[]\n}\n\n/**\n * Metadata key for guard storage\n */\nexport const GUARD_METADATA_KEY = Symbol.for('stratal:guards')\n","import { GUARD_METADATA_KEY, type Guard, type GuardMetadata } from './types'\n\n/**\n * UseGuards Decorator\n *\n * Applies one or more guards to a controller or method.\n * Guards are executed in order and all must pass for the request to proceed.\n *\n * **Execution Order:**\n * 1. Request → Global Middlewares → Route Middlewares\n * 2. **Guards (controller-level, then method-level)**\n * 3. Route Handler\n *\n * **Guard Resolution:**\n * - Guard classes are resolved from the request-scoped DI container\n * - Guard instances (from factory functions) are used directly\n *\n * @param guards - Guard classes or instances to apply\n *\n * @example Authentication only\n * ```typescript\n * @Controller('/api/v1/profile')\n * @UseGuards(AuthGuard())\n * export class ProfileController {\n * show() { } // Requires authentication\n * }\n * ```\n *\n * @example Authentication with permissions\n * ```typescript\n * @Controller('/api/v1/students')\n * @UseGuards(AuthGuard({ scopes: ['students:read'] }))\n * export class StudentsController {\n * index() { } // Requires 'students:read' permission\n * }\n * ```\n *\n * @example Method-level guards\n * ```typescript\n * @Controller('/api/v1/students')\n * @UseGuards(AuthGuard()) // Controller-level: auth only\n * export class StudentsController {\n * index() { } // Auth only (inherited)\n *\n * @UseGuards(AuthGuard({ scopes: ['students:create'] }))\n * create() { } // Auth + 'students:create' permission\n * }\n * ```\n *\n * @example Multiple guards\n * ```typescript\n * @UseGuards(AuthGuard(), RateLimitGuard(), CustomGuard())\n * export class SecureController {\n * // All guards must pass\n * }\n * ```\n */\nexport function UseGuards(...guards: Guard[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const metadata: GuardMetadata = { guards }\n\n if (propertyKey !== undefined) {\n // Method decorator - store on method\n Reflect.defineMetadata(GUARD_METADATA_KEY, metadata, target, propertyKey)\n } else {\n // Class decorator - store on class\n Reflect.defineMetadata(GUARD_METADATA_KEY, metadata, target)\n }\n }\n}\n\n/**\n * Get controller-level guard metadata\n *\n * @param target - Controller class\n * @returns Guard metadata or undefined if not decorated\n */\nexport function getControllerGuards(target: object): GuardMetadata | undefined {\n return Reflect.getMetadata(GUARD_METADATA_KEY, target) as GuardMetadata | undefined\n}\n\n/**\n * Get method-level guard metadata\n *\n * @param target - Controller prototype\n * @param propertyKey - Method name\n * @returns Guard metadata or undefined if not decorated\n */\nexport function getMethodGuards(target: object, propertyKey: string | symbol): GuardMetadata | undefined {\n return Reflect.getMetadata(GUARD_METADATA_KEY, target, propertyKey) as GuardMetadata | undefined\n}\n","import type { Container } from '../di'\nimport type { LoggerService } from '../logger'\nimport type { RouterContext } from '../router'\nimport type { CanActivate, Guard } from './types'\n\n/**\n * Guard Execution Service\n *\n * Executes guards for a route and determines if the request should proceed.\n * Guards are executed in order; all must pass for the request to proceed.\n */\nexport class GuardExecutionService {\n constructor(private readonly logger: LoggerService) { }\n\n /**\n * Execute all guards for a route\n *\n * @param guards - Array of guards (classes or instances)\n * @param context - Router context\n * @param container - Request-scoped DI container\n * @returns true if all guards pass\n * @throws Error from first failing guard\n */\n async executeGuards(\n guards: Guard[],\n context: RouterContext,\n container: Container\n ): Promise<boolean> {\n if (guards.length === 0) {\n return true\n }\n\n this.logger.debug('Executing guards', {\n guardCount: guards.length,\n path: context.c.req.path,\n method: context.c.req.method,\n })\n\n for (const guard of guards) {\n const guardInstance = this.resolveGuard(guard, container)\n const canActivate = await guardInstance.canActivate(context)\n\n if (!canActivate) {\n this.logger.debug('Guard denied access', {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- guard.constructor may be null at runtime\n guard: guard.constructor?.name || 'AnonymousGuard',\n path: context.c.req.path,\n })\n return false\n }\n }\n\n this.logger.debug('All guards passed', {\n guardCount: guards.length,\n path: context.c.req.path,\n })\n\n return true\n }\n\n /**\n * Resolve a guard to an instance\n *\n * @param guard - Guard class or instance\n * @param container - Request-scoped DI container\n * @returns Guard instance\n */\n private resolveGuard(guard: Guard, container: Container): CanActivate {\n // If already an instance (has canActivate method), use directly\n if (this.isGuardInstance(guard)) {\n return guard\n }\n\n // Otherwise, resolve from container\n return container.resolve<CanActivate>(guard)\n }\n\n /**\n * Type guard to check if value is a guard instance\n */\n private isGuardInstance(guard: Guard): guard is CanActivate {\n return (\n typeof guard === 'object' &&\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typeof null === 'object', null check is required\n guard !== null &&\n 'canActivate' in guard &&\n typeof guard.canActivate === 'function'\n )\n }\n}\n"],"mappings":";;;;AAyEA,MAAa,qBAAqB,OAAO,IAAI,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChB9D,SAAgB,UAAU,GAAG,QAAmD;CAC9E,QAAQ,QAAgB,gBAAkC;EACxD,MAAM,WAA0B,EAAE,QAAQ;EAE1C,IAAI,gBAAgB,KAAA,GAElB,QAAQ,eAAe,oBAAoB,UAAU,QAAQ,YAAY;OAGzE,QAAQ,eAAe,oBAAoB,UAAU,OAAO;;;;;;;;;AAWlE,SAAgB,oBAAoB,QAA2C;CAC7E,OAAO,QAAQ,YAAY,oBAAoB,OAAO;;;;;;;;;AAUxD,SAAgB,gBAAgB,QAAgB,aAAyD;CACvG,OAAO,QAAQ,YAAY,oBAAoB,QAAQ,YAAY;;;;;;;;;;AC9ErE,IAAa,wBAAb,MAAmC;CACJ;CAA7B,YAAY,QAAwC;EAAvB,KAAA,SAAA;;;;;;;;;;;CAW7B,MAAM,cACJ,QACA,SACA,WACkB;EAClB,IAAI,OAAO,WAAW,GACpB,OAAO;EAGT,KAAK,OAAO,MAAM,oBAAoB;GACpC,YAAY,OAAO;GACnB,MAAM,QAAQ,EAAE,IAAI;GACpB,QAAQ,QAAQ,EAAE,IAAI;GACvB,CAAC;EAEF,KAAK,MAAM,SAAS,QAIlB,IAAI,CAAC,MAHiB,KAAK,aAAa,OAAO,UACR,CAAC,YAAY,QAAQ,EAE1C;GAChB,KAAK,OAAO,MAAM,uBAAuB;IAEvC,OAAO,MAAM,aAAa,QAAQ;IAClC,MAAM,QAAQ,EAAE,IAAI;IACrB,CAAC;GACF,OAAO;;EAIX,KAAK,OAAO,MAAM,qBAAqB;GACrC,YAAY,OAAO;GACnB,MAAM,QAAQ,EAAE,IAAI;GACrB,CAAC;EAEF,OAAO;;;;;;;;;CAUT,aAAqB,OAAc,WAAmC;EAEpE,IAAI,KAAK,gBAAgB,MAAM,EAC7B,OAAO;EAIT,OAAO,UAAU,QAAqB,MAAM;;;;;CAM9C,gBAAwB,OAAoC;EAC1D,OACE,OAAO,UAAU,YAEjB,UAAU,QACV,iBAAiB,SACjB,OAAO,MAAM,gBAAgB"}
@@ -1,4 +1,4 @@
1
- import { f as ROUTE_METADATA_KEYS } from "./errors-BdyV5PnY.mjs";
1
+ import { f as ROUTE_METADATA_KEYS } from "./errors-COW9-Mar.mjs";
2
2
  import { i as z } from "./validation-DtJwAv7O.mjs";
3
3
  //#region src/router/decorators/http-method.decorator.ts
4
4
  /**
@@ -93,4 +93,4 @@ const All = createHttpMethodDecorator("all");
93
93
  //#endregion
94
94
  export { Post as a, Patch as i, Delete as n, Put as o, Get as r, All as t };
95
95
 
96
- //# sourceMappingURL=http-method.decorator-DXwxAfb_.mjs.map
96
+ //# sourceMappingURL=http-method.decorator-BrgHMdLQ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-method.decorator-DXwxAfb_.mjs","names":[],"sources":["../src/router/decorators/http-method.decorator.ts"],"sourcesContent":["import { z } from '../../i18n/validation'\nimport { ROUTE_METADATA_KEYS } from '../constants'\nimport type { ExplicitRouteMetadata, HttpMethod, RouteConfig } from '../types'\n\n/**\n * Creates an HTTP method decorator factory for the given HTTP method.\n *\n * The returned decorator stores {@link ExplicitRouteMetadata} on the method and\n * tracks the method name under {@link ROUTE_METADATA_KEYS.DECORATED_METHODS}\n * on the controller prototype so they can be discovered at registration time.\n */\nfunction createHttpMethodDecorator(method: HttpMethod) {\n return function (path: string, config?: RouteConfig) {\n return function (\n target: object,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n const metadata: ExplicitRouteMetadata = {\n type: 'explicit',\n method,\n path,\n config: config ?? { response: z.any() },\n }\n\n Reflect.defineMetadata(\n ROUTE_METADATA_KEYS.ROUTE_CONFIG,\n metadata,\n target,\n propertyKey\n )\n\n // Track this method as decorated on the prototype\n const existing: string[] =\n (Reflect.getOwnMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, target) as string[] | undefined) ?? []\n existing.push(propertyKey)\n Reflect.defineMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, existing, target)\n\n return descriptor\n }\n }\n}\n\n/**\n * Registers a GET route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (response schema, body, params, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/users')\n * class UsersController {\n * @Get('/', { response: z.array(userSchema), summary: 'List users' })\n * async list(ctx: RouterContext) { ... }\n *\n * @Get('/:id', { params: z.object({ id: z.string().uuid() }), response: userSchema })\n * async getUser(ctx: RouterContext) { ... }\n * }\n * ```\n */\nexport const Get = createHttpMethodDecorator('get')\n\n/**\n * Registers a POST route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (response schema, body, params, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/users')\n * class UsersController {\n * @Post('/', { body: createUserSchema, response: userSchema, statusCode: 201 })\n * async createUser(ctx: RouterContext) { ... }\n * }\n * ```\n */\nexport const Post = createHttpMethodDecorator('post')\n\n/**\n * Registers a PUT route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Put = createHttpMethodDecorator('put')\n\n/**\n * Registers a PATCH route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Patch = createHttpMethodDecorator('patch')\n\n/**\n * Registers a DELETE route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Delete = createHttpMethodDecorator('delete')\n\n/**\n * Registers an ALL (any HTTP method) route on the controller method.\n * Routes using @All are registered without OpenAPI validation\n * since OpenAPI does not support a catch-all HTTP method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const All = createHttpMethodDecorator('all')\n"],"mappings":";;;;;;;;;;AAWA,SAAS,0BAA0B,QAAoB;AACrD,QAAO,SAAU,MAAc,QAAsB;AACnD,SAAO,SACL,QACA,aACA,YACA;GACA,MAAM,WAAkC;IACtC,MAAM;IACN;IACA;IACA,QAAQ,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE;IACxC;AAED,WAAQ,eACN,oBAAoB,cACpB,UACA,QACA,YACD;GAGD,MAAM,WACH,QAAQ,eAAe,oBAAoB,mBAAmB,OAAO,IAA6B,EAAE;AACvG,YAAS,KAAK,YAAY;AAC1B,WAAQ,eAAe,oBAAoB,mBAAmB,UAAU,OAAO;AAE/E,UAAO;;;;;;;;;;;;;;;;;;;;;;AAuBb,MAAa,MAAM,0BAA0B,MAAM;;;;;;;;;;;;;;;;AAiBnD,MAAa,OAAO,0BAA0B,OAAO;;;;;;;AAQrD,MAAa,MAAM,0BAA0B,MAAM;;;;;;;AAQnD,MAAa,QAAQ,0BAA0B,QAAQ;;;;;;;AAQvD,MAAa,SAAS,0BAA0B,SAAS;;;;;;;;;AAUzD,MAAa,MAAM,0BAA0B,MAAM"}
1
+ {"version":3,"file":"http-method.decorator-BrgHMdLQ.mjs","names":[],"sources":["../src/router/decorators/http-method.decorator.ts"],"sourcesContent":["import { z } from '../../i18n/validation'\nimport { ROUTE_METADATA_KEYS } from '../constants'\nimport type { ExplicitRouteMetadata, HttpMethod, RouteConfig } from '../types'\n\n/**\n * Creates an HTTP method decorator factory for the given HTTP method.\n *\n * The returned decorator stores {@link ExplicitRouteMetadata} on the method and\n * tracks the method name under {@link ROUTE_METADATA_KEYS.DECORATED_METHODS}\n * on the controller prototype so they can be discovered at registration time.\n */\nfunction createHttpMethodDecorator(method: HttpMethod) {\n return function (path: string, config?: RouteConfig) {\n return function (\n target: object,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n const metadata: ExplicitRouteMetadata = {\n type: 'explicit',\n method,\n path,\n config: config ?? { response: z.any() },\n }\n\n Reflect.defineMetadata(\n ROUTE_METADATA_KEYS.ROUTE_CONFIG,\n metadata,\n target,\n propertyKey\n )\n\n // Track this method as decorated on the prototype\n const existing: string[] =\n (Reflect.getOwnMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, target) as string[] | undefined) ?? []\n existing.push(propertyKey)\n Reflect.defineMetadata(ROUTE_METADATA_KEYS.DECORATED_METHODS, existing, target)\n\n return descriptor\n }\n }\n}\n\n/**\n * Registers a GET route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (response schema, body, params, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/users')\n * class UsersController {\n * @Get('/', { response: z.array(userSchema), summary: 'List users' })\n * async list(ctx: RouterContext) { ... }\n *\n * @Get('/:id', { params: z.object({ id: z.string().uuid() }), response: userSchema })\n * async getUser(ctx: RouterContext) { ... }\n * }\n * ```\n */\nexport const Get = createHttpMethodDecorator('get')\n\n/**\n * Registers a POST route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (response schema, body, params, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/api/v1/users')\n * class UsersController {\n * @Post('/', { body: createUserSchema, response: userSchema, statusCode: 201 })\n * async createUser(ctx: RouterContext) { ... }\n * }\n * ```\n */\nexport const Post = createHttpMethodDecorator('post')\n\n/**\n * Registers a PUT route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Put = createHttpMethodDecorator('put')\n\n/**\n * Registers a PATCH route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Patch = createHttpMethodDecorator('patch')\n\n/**\n * Registers a DELETE route on the controller method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const Delete = createHttpMethodDecorator('delete')\n\n/**\n * Registers an ALL (any HTTP method) route on the controller method.\n * Routes using @All are registered without OpenAPI validation\n * since OpenAPI does not support a catch-all HTTP method.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration\n */\nexport const All = createHttpMethodDecorator('all')\n"],"mappings":";;;;;;;;;;AAWA,SAAS,0BAA0B,QAAoB;CACrD,OAAO,SAAU,MAAc,QAAsB;EACnD,OAAO,SACL,QACA,aACA,YACA;GACA,MAAM,WAAkC;IACtC,MAAM;IACN;IACA;IACA,QAAQ,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE;IACxC;GAED,QAAQ,eACN,oBAAoB,cACpB,UACA,QACA,YACD;GAGD,MAAM,WACH,QAAQ,eAAe,oBAAoB,mBAAmB,OAAO,IAA6B,EAAE;GACvG,SAAS,KAAK,YAAY;GAC1B,QAAQ,eAAe,oBAAoB,mBAAmB,UAAU,OAAO;GAE/E,OAAO;;;;;;;;;;;;;;;;;;;;;;AAuBb,MAAa,MAAM,0BAA0B,MAAM;;;;;;;;;;;;;;;;AAiBnD,MAAa,OAAO,0BAA0B,OAAO;;;;;;;AAQrD,MAAa,MAAM,0BAA0B,MAAM;;;;;;;AAQnD,MAAa,QAAQ,0BAA0B,QAAQ;;;;;;;AAQvD,MAAa,SAAS,0BAA0B,SAAS;;;;;;;;;AAUzD,MAAa,MAAM,0BAA0B,MAAM"}
@@ -1,3 +1,3 @@
1
- import { A as LocaleNotSupportedError, C as DetectionStrategy, D as buildDetectorOptions, E as ResolvedI18nOptions, O as resolveI18nOptions, S as I18nModule, T as LanguageDetectionOptions, _ as Messages, b as messages, g as MessageRegistry, h as MessageLoaderService, k as TranslationMissingError, m as I18nContextMiddleware, v as getLocales, w as I18nModuleOptions, x as I18N_TOKENS, y as getMessages } from "../index-D0US0X14.mjs";
1
+ import { A as LocaleNotSupportedError, C as DetectionStrategy, D as buildDetectorOptions, E as ResolvedI18nOptions, O as resolveI18nOptions, S as I18nModule, T as LanguageDetectionOptions, _ as Messages, b as messages, g as MessageRegistry, h as MessageLoaderService, k as TranslationMissingError, m as I18nContextMiddleware, v as getLocales, w as I18nModuleOptions, x as I18N_TOKENS, y as getMessages } from "../index-ByOyTmqf.mjs";
2
2
  import { C as MessageParams, S as MessageKeys, T as SystemMessageKeys, _ as AppMessageNamespaces, b as II18nService, g as AppMessageKeys, v as AppMessages, w as Prefixes, x as MessageKeyPrefix, y as DeepKeys } from "../index-Bnpfq6uk.mjs";
3
3
  export { AppMessageKeys, AppMessageNamespaces, AppMessages, DeepKeys, DetectionStrategy, I18N_TOKENS, I18nContextMiddleware, I18nModule, I18nModuleOptions, II18nService, LanguageDetectionOptions, LocaleNotSupportedError, MessageKeyPrefix, MessageKeys, MessageLoaderService, MessageParams, MessageRegistry, Messages, Prefixes, ResolvedI18nOptions, SystemMessageKeys, TranslationMissingError, buildDetectorOptions, getLocales, getMessages, messages, resolveI18nOptions };
@@ -1,3 +1,3 @@
1
- import { w as I18N_TOKENS } from "../errors-BdyV5PnY.mjs";
2
- import { B as LocaleNotSupportedError, F as getMessages, H as I18nContextMiddleware, I as messages, L as buildDetectorOptions, M as MessageRegistry, N as MessageLoaderService, P as getLocales, R as resolveI18nOptions, t as I18nModule, z as TranslationMissingError } from "../i18n.module-BBlNNlcG.mjs";
1
+ import { w as I18N_TOKENS } from "../errors-COW9-Mar.mjs";
2
+ import { B as LocaleNotSupportedError, F as getMessages, H as I18nContextMiddleware, I as messages, L as buildDetectorOptions, M as MessageRegistry, N as MessageLoaderService, P as getLocales, R as resolveI18nOptions, t as I18nModule, z as TranslationMissingError } from "../i18n.module-CzXLW9Hy.mjs";
3
3
  export { I18N_TOKENS, I18nContextMiddleware, I18nModule, LocaleNotSupportedError, MessageLoaderService, MessageRegistry, TranslationMissingError, buildDetectorOptions, getLocales, getMessages, messages, resolveI18nOptions };
@@ -1,14 +1,14 @@
1
- import { A as Scope, D as runWithContainer, E as getContainer, H as ApplicationError, V as ROUTER_TOKENS, a as createHttpExceptionContext, c as DEFAULT_CONTENT_TYPE, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext, u as METHOD_STATUS_CODES, w as I18N_TOKENS } from "./errors-BdyV5PnY.mjs";
2
- import { a as __decorate, d as CONTAINER_TOKEN, f as DI_TOKENS, g as getMethodInjections, o as __decorateParam, p as Transient, s as __decorateMetadata, u as LOGGER_TOKENS } from "./logger-V6Ms3QnQ.mjs";
3
- import { S as createThrottleMiddleware, b as ControllerRegistrationError, c as InvalidSignatureError, d as MissingRouteParamError, f as ResponseValidationError, g as RouteNotFoundError, h as SchemaValidationError, k as Module, l as MiddlewareNextCalledMultipleTimesError, o as DomainMismatchError, p as RouteNameNotFoundError, s as DuplicateRouteNameError, u as MissingEnvironmentVariableError, v as OpenAPIRouteRegistrationError, x as ControllerMethodNotFoundError, y as HonoAppAlreadyConfiguredError } from "./module-Dk2qTa77.mjs";
1
+ import { A as Scope, D as runWithContainer, E as getContainer, H as ApplicationError, V as ROUTER_TOKENS, a as createHttpExceptionContext, c as DEFAULT_CONTENT_TYPE, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, k as ERROR_CODES, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext, u as METHOD_STATUS_CODES, w as I18N_TOKENS } from "./errors-COW9-Mar.mjs";
2
+ import { a as __decorate, d as CONTAINER_TOKEN, f as DI_TOKENS, g as getMethodInjections, o as __decorateParam, p as Transient, s as __decorateMetadata, u as LOGGER_TOKENS } from "./logger-DlV7NtvD.mjs";
3
+ import { S as createThrottleMiddleware, b as ControllerRegistrationError, c as InvalidSignatureError, d as MissingRouteParamError, f as ResponseValidationError, g as RouteNotFoundError, h as SchemaValidationError, k as Module, l as MiddlewareNextCalledMultipleTimesError, o as DomainMismatchError, p as RouteNameNotFoundError, s as DuplicateRouteNameError, u as MissingEnvironmentVariableError, v as OpenAPIRouteRegistrationError, x as ControllerMethodNotFoundError, y as HonoAppAlreadyConfiguredError } from "./module-BzLg57FK.mjs";
4
4
  import { c as backendErrorMap, i as z, l as runWithErrorMapContext, r as validation_exports, t as OpenAPIHono } from "./validation-DtJwAv7O.mjs";
5
5
  import { n as OPENAPI_TOKENS } from "./openapi-tools.service-Zs-Ewv7F.mjs";
6
6
  import { t as en_exports } from "./en-DSH_bhh6.mjs";
7
- import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-DUk_Kzst.mjs";
8
- import { n as getControllerOptions, r as getControllerRoute } from "./controller.decorator-DQzenvSN.mjs";
9
- import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-CdJjpUCW.mjs";
10
- import "./http-method.decorator-DXwxAfb_.mjs";
11
- import { n as getRateLimits } from "./rate-limit.decorator--o6Q6p9w.mjs";
7
+ import { i as getMethodGuards, r as getControllerGuards, t as GuardExecutionService } from "./guards-DU1_J9YA.mjs";
8
+ import { n as getControllerOptions, r as getControllerRoute } from "./controller.decorator-B9vwn0zK.mjs";
9
+ import { c as getWsOnMessageMethod, d as isGateway, o as getWsOnCloseMethod, s as getWsOnErrorMethod, t as GatewayContext } from "./gateway-context-CXmXtaUP.mjs";
10
+ import "./http-method.decorator-BrgHMdLQ.mjs";
11
+ import { n as getRateLimits } from "./rate-limit.decorator-6qzNcSOt.mjs";
12
12
  import { n as verifySignedUrl, t as signUrl } from "./signed-url-BQPbv2In.mjs";
13
13
  import { t as setupI18nCompiler } from "./setup-CefZKV_e.mjs";
14
14
  import { inject } from "tsyringe";
@@ -23,6 +23,7 @@ import { languageDetector } from "hono/language";
23
23
  * Must run after LocaleExtractionMiddleware sets the locale.
24
24
  */
25
25
  let I18nContextMiddleware = class I18nContextMiddleware {
26
+ i18n;
26
27
  constructor(i18n) {
27
28
  this.i18n = i18n;
28
29
  }
@@ -41,6 +42,7 @@ I18nContextMiddleware = __decorate([
41
42
  //#endregion
42
43
  //#region src/openapi/services/openapi-config.service.ts
43
44
  let OpenAPIConfigService = class OpenAPIConfigService {
45
+ baseOptions;
44
46
  overrides = [];
45
47
  constructor(baseOptions) {
46
48
  this.baseOptions = baseOptions;
@@ -210,6 +212,8 @@ function deepMerge(target, source) {
210
212
  //#endregion
211
213
  //#region src/i18n/services/message-loader.service.ts
212
214
  let MessageLoaderService = class MessageLoaderService {
215
+ registry;
216
+ options;
213
217
  cache;
214
218
  contextCache;
215
219
  locales;
@@ -1050,10 +1054,34 @@ function toRoutingOpenAPIPath(path) {
1050
1054
  * Scoring: static = 0, `:param{constraint}` = 5, `:param` = 10, wildcard `{.+}` / `{.*}` = 100.
1051
1055
  *
1052
1056
  * Packed as: score * 10000 - segmentCount (negative segment count so more segments = lower key = higher priority)
1057
+ *
1058
+ * Locale variants score against the path with the leading `/:locale{…}` segment
1059
+ * stripped — the variant's score therefore matches its primary, but its larger
1060
+ * segment count makes it sort just before the primary. Without this, a primary
1061
+ * catch-all (e.g. `/:slug{.+}`) gobbles locale-prefixed URLs because Hono picks
1062
+ * whichever matching route was registered first.
1053
1063
  */
1054
- function getPathSpecificityKey(path) {
1064
+ function getPathSpecificityKey(route) {
1065
+ const segmentCount = countSegments(route.path);
1066
+ const scoringPath = route.isLocaleVariant ? route.path.replace(/^\/:locale\{[^}]*\}/, "") || "/" : route.path;
1055
1067
  let score = 0;
1056
- let segmentCount = 0;
1068
+ let i = 0;
1069
+ while (i < scoringPath.length) {
1070
+ if (scoringPath.charCodeAt(i) === 47) {
1071
+ i++;
1072
+ continue;
1073
+ }
1074
+ let end = scoringPath.indexOf("/", i);
1075
+ if (end === -1) end = scoringPath.length;
1076
+ const segment = scoringPath.substring(i, end);
1077
+ if (segment.includes("{.+}") || segment.includes("{.*}")) score += 100;
1078
+ else if (segment.charCodeAt(0) === 58) score += segment.includes("{") ? 5 : 10;
1079
+ i = end;
1080
+ }
1081
+ return score * 1e4 - segmentCount;
1082
+ }
1083
+ function countSegments(path) {
1084
+ let count = 0;
1057
1085
  let i = 0;
1058
1086
  while (i < path.length) {
1059
1087
  if (path.charCodeAt(i) === 47) {
@@ -1062,13 +1090,10 @@ function getPathSpecificityKey(path) {
1062
1090
  }
1063
1091
  let end = path.indexOf("/", i);
1064
1092
  if (end === -1) end = path.length;
1065
- segmentCount++;
1066
- const segment = path.substring(i, end);
1067
- if (segment.includes("{.+}") || segment.includes("{.*}")) score += 100;
1068
- else if (segment.charCodeAt(0) === 58) score += segment.includes("{") ? 5 : 10;
1093
+ count++;
1069
1094
  i = end;
1070
1095
  }
1071
- return score * 1e4 - segmentCount;
1096
+ return count;
1072
1097
  }
1073
1098
  /**
1074
1099
  * Compute a specificity score for route ordering.
@@ -1089,11 +1114,13 @@ function getPathSpecificityScore(path) {
1089
1114
  *
1090
1115
  * 1. Static paths before parameterized before wildcards
1091
1116
  * 2. More segments = more specific (tie-breaker)
1092
- * 3. Primary paths before locale-prefixed variants
1117
+ * 3. Locale-prefixed variants before their primary (so a locale-prefixed
1118
+ * request matches the variant first; a primary catch-all would otherwise
1119
+ * swallow the locale prefix into its param)
1093
1120
  */
1094
1121
  function sortRoutesBySpecificity(routes) {
1095
1122
  const keys = /* @__PURE__ */ new Map();
1096
- for (const route of routes) keys.set(route, getPathSpecificityKey(route.path));
1123
+ for (const route of routes) keys.set(route, getPathSpecificityKey(route));
1097
1124
  const copy = routes.slice();
1098
1125
  copy.sort((a, b) => keys.get(a) - keys.get(b));
1099
1126
  return copy;
@@ -1161,6 +1188,12 @@ const invokeHandler = (instance, method, ...args) => {
1161
1188
  }
1162
1189
  };
1163
1190
  let RouteRegistrationService = class RouteRegistrationService {
1191
+ logger;
1192
+ registry;
1193
+ routerResolver;
1194
+ localePathService;
1195
+ app;
1196
+ moduleRegistry;
1164
1197
  controllerClasses = /* @__PURE__ */ new Map();
1165
1198
  upgradeWebSocketFn = null;
1166
1199
  constructor(logger, registry, routerResolver, localePathService, app, moduleRegistry) {
@@ -1786,6 +1819,7 @@ HonoApp = __decorate([
1786
1819
  //#endregion
1787
1820
  //#region src/router/services/locale-path.service.ts
1788
1821
  let LocalePathService = class LocalePathService {
1822
+ honoApp;
1789
1823
  _config;
1790
1824
  _pathDetectionEnabled;
1791
1825
  _prefixDefaultLocale;
@@ -1941,6 +1975,8 @@ const CONCRETE_HTTP_METHODS = [
1941
1975
  "trace"
1942
1976
  ];
1943
1977
  let RouteRegistry = class RouteRegistry {
1978
+ versioningService;
1979
+ localePathService;
1944
1980
  routes = [];
1945
1981
  namedRoutes = /* @__PURE__ */ new Map();
1946
1982
  _sortedCache = null;
@@ -2025,7 +2061,7 @@ let RouteRegistry = class RouteRegistry {
2025
2061
  }
2026
2062
  return cache;
2027
2063
  }
2028
- /** Get all routes sorted by specificity (static > param > wildcard, primary before locale) */
2064
+ /** Get all routes sorted by specificity (static > param > wildcard, locale variant before its primary) */
2029
2065
  all() {
2030
2066
  this._sortedCache ??= sortRoutesBySpecificity(this.routes);
2031
2067
  return this._sortedCache;
@@ -2099,6 +2135,8 @@ function buildRouteUrl(route, name, params) {
2099
2135
  return url;
2100
2136
  }
2101
2137
  let Uri = class Uri {
2138
+ registry;
2139
+ routerContext;
2102
2140
  _defaults = {};
2103
2141
  trailingSlash;
2104
2142
  constructor(registry, routerContext, application) {
@@ -2352,6 +2390,8 @@ var VerifySignatureMiddleware = class {
2352
2390
  * Uses pre-built CoreContext from MessageLoaderService (singleton) for zero-cost lookups.
2353
2391
  */
2354
2392
  let I18nService = class I18nService {
2393
+ loader;
2394
+ routerContext;
2355
2395
  constructor(loader, routerContext) {
2356
2396
  this.loader = loader;
2357
2397
  this.routerContext = routerContext;
@@ -2489,4 +2529,4 @@ I18nModule = _I18nModule = __decorate([Module({ providers: [
2489
2529
  //#endregion
2490
2530
  export { OpenAPIModule as A, LocaleNotSupportedError as B, validationErrorResponseSchema as C, createMiddlewareChain as D, getRouteMetadata as E, getMessages as F, I18nContextMiddleware as H, messages as I, buildDetectorOptions as L, MessageRegistry as M, MessageLoaderService as N, createDomainMiddleware as O, getLocales as P, resolveI18nOptions as R, uuidParamSchema as S, getRouteDecoratedMethods as T, OpenAPIConfigService as V, commonErrorSchemas as _, buildRouteUrl as a, paginationQuerySchema as b, LocalePathService as c, extractDomainParamNames as d, extractParamNames as f, toOpenAPIPath as g, sortRoutesBySpecificity as h, Uri as i, OpenAPIService as j, parseDomainPattern as k, HonoApp as l, getPathSpecificityScore as m, VerifySignatureMiddleware as n, RouteRegistry as o, generateConventionRouteName as p, route as r, VersioningService as s, I18nModule as t, RouteRegistrationService as u, errorResponseSchema as v, Route as w, successMessageSchema as x, paginatedResponseSchema as y, TranslationMissingError as z };
2491
2531
 
2492
- //# sourceMappingURL=i18n.module-BBlNNlcG.mjs.map
2532
+ //# sourceMappingURL=i18n.module-CzXLW9Hy.mjs.map