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 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/config/config.tokens.ts","../../src/config/errors/config-module-not-initialized.error.ts","../../src/config/errors/config-not-initialized.error.ts","../../src/config/config.types.ts","../../src/config/services/config.service.ts","../../src/config/config.module.ts","../../src/config/register-as.ts"],"sourcesContent":["export const CONFIG_TOKENS = {\n\tConfigService: Symbol.for('stratal:config:service'),\n} as const\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * Error thrown when ConfigModule's onInitialize runs but forRoot() was never called.\n *\n * This means the module was imported without calling ConfigModule.forRoot({ load: [...] }).\n */\nexport class ConfigModuleNotInitializedError extends ApplicationError {\n constructor() {\n super(\n 'errors.configModuleNotInitialized',\n ERROR_CODES.SYSTEM.CONFIG_MODULE_NOT_INITIALIZED\n )\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * ConfigNotInitializedError\n *\n * Thrown when attempting to access ConfigService before it has been initialized.\n * This typically indicates that ConfigModule's onInitialize hook hasn't run yet,\n * or the module wasn't registered properly.\n */\nexport class ConfigNotInitializedError extends ApplicationError {\n constructor() {\n super(\n 'errors.configNotInitialized',\n ERROR_CODES.SYSTEM.CONFIG_NOT_INITIALIZED\n )\n }\n}\n","import type { z } from '../i18n/validation'\n\nexport class ConfigValidationError extends Error {\n constructor(\n message: string,\n public readonly errors: z.ZodError\n ) {\n super(message)\n this.name = 'ConfigValidationError'\n }\n}\n\n/**\n * Configuration that can be augmented by applications\n * Apps should augment this interface with their AppConfig type using module augmentation\n *\n * @example\n * ```typescript\n * // In your app (e.g., apps/backend/src/config/types.ts)\n * declare module 'stratal' {\n * interface ModuleConfig {\n * database: { url: string; maxConnections: number }\n * email: { provider: string; from: { name: string; email: string } }\n * }\n * }\n * ```\n */\nexport interface ModuleConfig { }\n\n/**\n * Generate all valid dot-notation paths from a config object type\n * @example ConfigPath<{ database: { url: string } }> = 'database' | 'database.url'\n */\nexport type ConfigPath<T> = {\n [K in keyof T & string]: T[K] extends Record<string, unknown>\n ? K | `${K}.${ConfigPath<T[K]>}`\n : K\n}[keyof T & string]\n\n/**\n * Get the value type at a dot-notation path\n * @example ConfigPathValue<{ database: { url: string } }, 'database.url'> = string\n */\nexport type ConfigPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}`\n ? K extends keyof T\n ? T[K] extends Record<string, unknown>\n ? ConfigPathValue<T[K], Rest>\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never\n\n/**\n * ConfigService interface with dot notation support\n */\nexport interface IConfigService<T extends object = ModuleConfig> {\n /**\n * Initialize the config service with validated configuration\n * Should be called once during application startup\n */\n initialize(config: T): void\n\n /**\n * Get config value using dot notation\n * @example config.get('database.url')\n */\n get<P extends ConfigPath<T>>(path: P): ConfigPathValue<T, P>\n\n /**\n * Set config value at runtime (for runtime overrides)\n * @example config.set('email.from.name', 'Custom Name')\n */\n set<P extends ConfigPath<T>>(path: P, value: ConfigPathValue<T, P>): void\n\n /**\n * Reset config to original value\n * @param path - Optional path to reset (resets entire config if omitted)\n */\n reset(path?: ConfigPath<T>): void\n\n /**\n * Get entire config object\n */\n all(): Readonly<T>\n\n /**\n * Check if a config path exists\n */\n has(path: ConfigPath<T>): boolean\n}\n","import { Transient } from '../../di/decorators'\nimport { CONFIG_TOKENS } from '../config.tokens'\nimport type { ConfigPath, ConfigPathValue, IConfigService, ModuleConfig } from '../config.types'\nimport { ConfigNotInitializedError } from '../errors'\n\n/**\n * ConfigService with dot notation support for get/set operations\n *\n * Supports runtime overrides via set() - useful for request-specific config overrides.\n * Use reset() to restore original values.\n *\n * @example\n * ```typescript\n * // Get with dot notation\n * const url = config.get('database.url')\n * const fromName = config.get('email.from.name')\n *\n * // Set at runtime (e.g., in middleware for runtime override)\n * config.set('email.from.name', 'Custom Name')\n *\n * // Reset to original\n * config.reset('email.from.name') // reset specific path\n * config.reset() // reset entire config\n * ```\n */\n@Transient(CONFIG_TOKENS.ConfigService)\nexport class ConfigService<T extends object = ModuleConfig> implements IConfigService<T> {\n private originalConfig: T | undefined\n private currentConfig: T | undefined\n\n /**\n * Initialize the config service with validated configuration\n * Called by ConfigModule during initialization\n */\n initialize(config: T): void {\n this.originalConfig = this.deepClone(config)\n this.currentConfig = this.deepClone(config)\n }\n\n /**\n * Get config value using dot notation\n * @example config.get('database.url')\n */\n get<P extends ConfigPath<T>>(path: P): ConfigPathValue<T, P> {\n this.ensureInitialized()\n return this.getByPath(this.currentConfig, path) as ConfigPathValue<T, P>\n }\n\n /**\n * Set config value at runtime (for runtime overrides)\n * @example config.set('email.from.name', 'Custom Name')\n */\n set<P extends ConfigPath<T>>(path: P, value: ConfigPathValue<T, P>): void {\n this.ensureInitialized()\n this.setByPath(this.currentConfig, path, value)\n }\n\n /**\n * Reset config to original value\n * @param path - Optional path to reset (resets entire config if omitted)\n */\n reset(path?: ConfigPath<T>): void {\n this.ensureInitialized()\n if (path) {\n const originalValue = this.getByPath(this.originalConfig, path)\n this.setByPath(this.currentConfig, path, this.deepClone(originalValue))\n } else {\n this.currentConfig = this.deepClone(this.originalConfig)\n }\n }\n\n /**\n * Get entire config object\n */\n all(): Readonly<T> {\n this.ensureInitialized()\n return this.currentConfig as Readonly<T>\n }\n\n /**\n * Check if a config path exists\n */\n has(path: ConfigPath<T>): boolean {\n this.ensureInitialized()\n return this.getByPath(this.currentConfig, path) !== undefined\n }\n\n private getByPath(obj: unknown, path: string): unknown {\n const keys = path.split('.')\n let current = obj\n for (const key of keys) {\n if (this.isDangerousKey(key)) return undefined\n if (current === null || current === undefined) return undefined\n current = (current as Record<string, unknown>)[key]\n }\n return current\n }\n\n private setByPath(obj: unknown, path: string, value: unknown): void {\n const keys = path.split('.')\n if (keys.some((key) => this.isDangerousKey(key))) return\n let current = obj as Record<string, unknown>\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i]\n if (!Object.hasOwn(current, key) || typeof current[key] !== 'object' || current[key] === null) {\n Object.defineProperty(current, key, {\n value: {},\n writable: true,\n enumerable: true,\n configurable: true,\n })\n }\n current = current[key] as Record<string, unknown>\n }\n Object.defineProperty(current, keys[keys.length - 1], {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n })\n }\n\n private isDangerousKey(key: string): boolean {\n return key === '__proto__' || key === 'constructor' || key === 'prototype'\n }\n\n private ensureInitialized(): void {\n if (!this.currentConfig) {\n throw new ConfigNotInitializedError()\n }\n }\n\n private deepClone<V>(obj: V): V {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n return JSON.parse(JSON.stringify(obj)) as V\n }\n}\n","import type { z } from '../i18n/validation'\nimport { ConfigModuleNotInitializedError } from './errors'\nimport { DI_TOKENS } from '../di/tokens'\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { DynamicModule, ModuleContext, OnInitialize, Provider } from '../module/types'\nimport { CONFIG_TOKENS } from './config.tokens'\nimport { ConfigValidationError, ModuleConfig } from './config.types'\nimport type { ConfigNamespace } from './register-as'\nimport { ConfigService } from './services/config.service'\n\n/**\n * Any config namespace - uses structural typing for flexibility\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyConfigNamespace = ConfigNamespace<string, any, object>\n\n/**\n * Options for ConfigModule.forRoot\n */\nexport interface ConfigModuleOptions {\n /**\n * Array of config namespaces created via registerAs()\n */\n load: AnyConfigNamespace[]\n\n /**\n * Optional Zod schema for validating the merged config\n * Validates the entire config object after all namespaces are merged\n */\n validateSchema?: z.ZodType<object>\n}\n\n// Store options for use in onInitialize\nlet moduleOptions: ConfigModuleOptions | null = null\n\n/**\n * ConfigModule\n *\n * Provides configuration management with namespace support.\n * Uses registerAs() to create typed config namespaces that can be injected.\n *\n * @example\n * ```typescript\n * // Define config namespaces\n * const databaseConfig = registerAs('database', (env) => ({\n * url: env.DATABASE_URL,\n * maxConnections: 10\n * }))\n *\n * const emailConfig = registerAs('email', (env) => ({\n * provider: env.EMAIL_PROVIDER,\n * from: { name: 'App', email: 'noreply@example.com' }\n * }))\n *\n * // Register in module\n * @Module({\n * imports: [\n * ConfigModule.forRoot({\n * load: [databaseConfig, emailConfig],\n * validateSchema: AppConfigSchema\n * })\n * ]\n * })\n * export class AppModule {}\n *\n * // Inject config\n * constructor(\n * @inject(CONFIG_TOKENS.ConfigService) private config: IConfigService,\n * @inject(databaseConfig.KEY) private dbConfig: DatabaseConfig\n * ) {\n * // Use dot notation\n * const url = this.config.get('database.url')\n *\n * // Or inject namespace directly\n * const url = this.dbConfig.url\n * }\n * ```\n */\n@Module({\n providers: [\n // Register the main ConfigService as Singleton so initialization persists\n {\n provide: CONFIG_TOKENS.ConfigService,\n useClass: ConfigService,\n scope: Scope.Singleton,\n },\n ],\n})\nexport class ConfigModule implements OnInitialize {\n /**\n * Configure ConfigModule with namespace loaders\n *\n * @param options - Configuration options\n * @returns Dynamic module with config infrastructure\n */\n static forRoot(options: ConfigModuleOptions): DynamicModule {\n moduleOptions = options\n\n const providers: Provider[] = []\n\n // Register each namespace config using asProvider()\n for (const namespace of options.load) {\n providers.push(namespace.asProvider())\n }\n\n return {\n module: ConfigModule,\n providers,\n }\n }\n\n /**\n * Initialize config service with merged namespaces\n * Called after all providers are registered\n */\n onInitialize(context: ModuleContext): void {\n if (!moduleOptions) {\n throw new ConfigModuleNotInitializedError()\n }\n\n const env = context.container.resolve<unknown>(DI_TOKENS.CloudflareEnv)\n const configService = context.container.resolve<ConfigService>(CONFIG_TOKENS.ConfigService)\n\n // Build merged config from all namespaces\n const mergedConfig: Record<string, unknown> = {}\n\n for (const namespace of moduleOptions.load) {\n mergedConfig[namespace.namespace] = namespace.factory(env)\n }\n\n // Validate if schema provided\n if (moduleOptions.validateSchema) {\n const result = moduleOptions.validateSchema.safeParse(mergedConfig)\n if (!result.success) {\n throw new ConfigValidationError(\n 'Configuration validation failed',\n result.error\n )\n }\n }\n\n // Initialize ConfigService with merged config\n configService.initialize(mergedConfig as ModuleConfig)\n\n context.logger.debug('ConfigModule initialized', {\n namespaces: moduleOptions.load.map((n) => n.namespace),\n })\n }\n}\n","import type { InjectionToken } from 'tsyringe'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { FactoryProvider } from '../module/types'\n\n/**\n * Configuration namespace registration result\n */\nexport interface ConfigNamespace<TKey extends string, TEnv, TConfig extends object> {\n /** Auto-derived injection token (e.g., 'database' -> Symbol('stratal:config:database')) */\n readonly KEY: InjectionToken<TConfig>\n /** The namespace key */\n readonly namespace: TKey\n /** The factory function that receives env and returns config */\n readonly factory: (env: TEnv) => TConfig\n /**\n * Returns a provider configuration for use in module registration\n * Automatically injects DI_TOKENS.CloudflareEnv\n */\n asProvider(): FactoryProvider<TConfig>\n}\n\n/**\n * Create a namespaced configuration factory\n * Similar to NestJS registerAs\n *\n * @param namespace - Configuration namespace (e.g., 'database', 'email')\n * @param factory - Factory function receiving env and returning config object\n * @returns ConfigNamespace with token, factory, and asProvider() method\n *\n * @example\n * ```typescript\n * // apps/backend/src/config/database.config.ts\n * export const databaseConfig = registerAs('database', (env: Env) => ({\n * url: env.DATABASE_URL,\n * maxConnections: parseInt(env.DATABASE_MAX_CONNECTIONS || '10'),\n * }))\n *\n * // Auto-generates: databaseConfig.KEY = Symbol('stratal:config:database')\n *\n * // Usage in module:\n * // Option 1: Manual provider\n * {\n * provide: databaseConfig.KEY,\n * useFactory: databaseConfig.factory,\n * inject: [DI_TOKENS.CloudflareEnv]\n * }\n *\n * // Option 2: asProvider() helper\n * databaseConfig.asProvider()\n * // Returns: { provide: databaseConfig.KEY, useFactory: ..., inject: [DI_TOKENS.CloudflareEnv] }\n * ```\n */\nexport function registerAs<TKey extends string, TEnv, TConfig extends object>(\n namespace: TKey,\n factory: (env: TEnv) => TConfig\n): ConfigNamespace<TKey, TEnv, TConfig> {\n const KEY = Symbol.for(`stratal:config:${namespace}`) as InjectionToken<TConfig>\n\n return {\n KEY,\n namespace,\n factory,\n asProvider(): FactoryProvider<TConfig> {\n return {\n provide: KEY,\n useFactory: factory,\n inject: [DI_TOKENS.CloudflareEnv],\n }\n },\n }\n}\n\n/**\n * Helper to derive config type from registerAs result\n *\n * @example\n * ```typescript\n * const databaseConfig = registerAs('database', (env) => ({ url: env.DATABASE_URL }))\n * type DatabaseConfig = InferConfigType<typeof databaseConfig>\n * // { url: string }\n * ```\n */\nexport type InferConfigType<T> = T extends ConfigNamespace<string, unknown, infer C> ? C : never\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/config/config.tokens.ts","../../src/config/errors/config-module-not-initialized.error.ts","../../src/config/errors/config-not-initialized.error.ts","../../src/config/config.types.ts","../../src/config/services/config.service.ts","../../src/config/config.module.ts","../../src/config/register-as.ts"],"sourcesContent":["export const CONFIG_TOKENS = {\n\tConfigService: Symbol.for('stratal:config:service'),\n} as const\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * Error thrown when ConfigModule's onInitialize runs but forRoot() was never called.\n *\n * This means the module was imported without calling ConfigModule.forRoot({ load: [...] }).\n */\nexport class ConfigModuleNotInitializedError extends ApplicationError {\n constructor() {\n super(\n 'errors.configModuleNotInitialized',\n ERROR_CODES.SYSTEM.CONFIG_MODULE_NOT_INITIALIZED\n )\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * ConfigNotInitializedError\n *\n * Thrown when attempting to access ConfigService before it has been initialized.\n * This typically indicates that ConfigModule's onInitialize hook hasn't run yet,\n * or the module wasn't registered properly.\n */\nexport class ConfigNotInitializedError extends ApplicationError {\n constructor() {\n super(\n 'errors.configNotInitialized',\n ERROR_CODES.SYSTEM.CONFIG_NOT_INITIALIZED\n )\n }\n}\n","import type { z } from '../i18n/validation'\n\nexport class ConfigValidationError extends Error {\n constructor(\n message: string,\n public readonly errors: z.ZodError\n ) {\n super(message)\n this.name = 'ConfigValidationError'\n }\n}\n\n/**\n * Configuration that can be augmented by applications\n * Apps should augment this interface with their AppConfig type using module augmentation\n *\n * @example\n * ```typescript\n * // In your app (e.g., apps/backend/src/config/types.ts)\n * declare module 'stratal' {\n * interface ModuleConfig {\n * database: { url: string; maxConnections: number }\n * email: { provider: string; from: { name: string; email: string } }\n * }\n * }\n * ```\n */\nexport interface ModuleConfig { }\n\n/**\n * Generate all valid dot-notation paths from a config object type\n * @example ConfigPath<{ database: { url: string } }> = 'database' | 'database.url'\n */\nexport type ConfigPath<T> = {\n [K in keyof T & string]: T[K] extends Record<string, unknown>\n ? K | `${K}.${ConfigPath<T[K]>}`\n : K\n}[keyof T & string]\n\n/**\n * Get the value type at a dot-notation path\n * @example ConfigPathValue<{ database: { url: string } }, 'database.url'> = string\n */\nexport type ConfigPathValue<T, P extends string> = P extends `${infer K}.${infer Rest}`\n ? K extends keyof T\n ? T[K] extends Record<string, unknown>\n ? ConfigPathValue<T[K], Rest>\n : never\n : never\n : P extends keyof T\n ? T[P]\n : never\n\n/**\n * ConfigService interface with dot notation support\n */\nexport interface IConfigService<T extends object = ModuleConfig> {\n /**\n * Initialize the config service with validated configuration\n * Should be called once during application startup\n */\n initialize(config: T): void\n\n /**\n * Get config value using dot notation\n * @example config.get('database.url')\n */\n get<P extends ConfigPath<T>>(path: P): ConfigPathValue<T, P>\n\n /**\n * Set config value at runtime (for runtime overrides)\n * @example config.set('email.from.name', 'Custom Name')\n */\n set<P extends ConfigPath<T>>(path: P, value: ConfigPathValue<T, P>): void\n\n /**\n * Reset config to original value\n * @param path - Optional path to reset (resets entire config if omitted)\n */\n reset(path?: ConfigPath<T>): void\n\n /**\n * Get entire config object\n */\n all(): Readonly<T>\n\n /**\n * Check if a config path exists\n */\n has(path: ConfigPath<T>): boolean\n}\n","import { Transient } from '../../di/decorators'\nimport { CONFIG_TOKENS } from '../config.tokens'\nimport type { ConfigPath, ConfigPathValue, IConfigService, ModuleConfig } from '../config.types'\nimport { ConfigNotInitializedError } from '../errors'\n\n/**\n * ConfigService with dot notation support for get/set operations\n *\n * Supports runtime overrides via set() - useful for request-specific config overrides.\n * Use reset() to restore original values.\n *\n * @example\n * ```typescript\n * // Get with dot notation\n * const url = config.get('database.url')\n * const fromName = config.get('email.from.name')\n *\n * // Set at runtime (e.g., in middleware for runtime override)\n * config.set('email.from.name', 'Custom Name')\n *\n * // Reset to original\n * config.reset('email.from.name') // reset specific path\n * config.reset() // reset entire config\n * ```\n */\n@Transient(CONFIG_TOKENS.ConfigService)\nexport class ConfigService<T extends object = ModuleConfig> implements IConfigService<T> {\n private originalConfig: T | undefined\n private currentConfig: T | undefined\n\n /**\n * Initialize the config service with validated configuration\n * Called by ConfigModule during initialization\n */\n initialize(config: T): void {\n this.originalConfig = this.deepClone(config)\n this.currentConfig = this.deepClone(config)\n }\n\n /**\n * Get config value using dot notation\n * @example config.get('database.url')\n */\n get<P extends ConfigPath<T>>(path: P): ConfigPathValue<T, P> {\n this.ensureInitialized()\n return this.getByPath(this.currentConfig, path) as ConfigPathValue<T, P>\n }\n\n /**\n * Set config value at runtime (for runtime overrides)\n * @example config.set('email.from.name', 'Custom Name')\n */\n set<P extends ConfigPath<T>>(path: P, value: ConfigPathValue<T, P>): void {\n this.ensureInitialized()\n this.setByPath(this.currentConfig, path, value)\n }\n\n /**\n * Reset config to original value\n * @param path - Optional path to reset (resets entire config if omitted)\n */\n reset(path?: ConfigPath<T>): void {\n this.ensureInitialized()\n if (path) {\n const originalValue = this.getByPath(this.originalConfig, path)\n this.setByPath(this.currentConfig, path, this.deepClone(originalValue))\n } else {\n this.currentConfig = this.deepClone(this.originalConfig)\n }\n }\n\n /**\n * Get entire config object\n */\n all(): Readonly<T> {\n this.ensureInitialized()\n return this.currentConfig as Readonly<T>\n }\n\n /**\n * Check if a config path exists\n */\n has(path: ConfigPath<T>): boolean {\n this.ensureInitialized()\n return this.getByPath(this.currentConfig, path) !== undefined\n }\n\n private getByPath(obj: unknown, path: string): unknown {\n const keys = path.split('.')\n let current = obj\n for (const key of keys) {\n if (this.isDangerousKey(key)) return undefined\n if (current === null || current === undefined) return undefined\n current = (current as Record<string, unknown>)[key]\n }\n return current\n }\n\n private setByPath(obj: unknown, path: string, value: unknown): void {\n const keys = path.split('.')\n if (keys.some((key) => this.isDangerousKey(key))) return\n let current = obj as Record<string, unknown>\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i]\n if (!Object.hasOwn(current, key) || typeof current[key] !== 'object' || current[key] === null) {\n Object.defineProperty(current, key, {\n value: {},\n writable: true,\n enumerable: true,\n configurable: true,\n })\n }\n current = current[key] as Record<string, unknown>\n }\n Object.defineProperty(current, keys[keys.length - 1], {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n })\n }\n\n private isDangerousKey(key: string): boolean {\n return key === '__proto__' || key === 'constructor' || key === 'prototype'\n }\n\n private ensureInitialized(): void {\n if (!this.currentConfig) {\n throw new ConfigNotInitializedError()\n }\n }\n\n private deepClone<V>(obj: V): V {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n return JSON.parse(JSON.stringify(obj)) as V\n }\n}\n","import type { z } from '../i18n/validation'\nimport { ConfigModuleNotInitializedError } from './errors'\nimport { DI_TOKENS } from '../di/tokens'\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { DynamicModule, ModuleContext, OnInitialize, Provider } from '../module/types'\nimport { CONFIG_TOKENS } from './config.tokens'\nimport { ConfigValidationError, ModuleConfig } from './config.types'\nimport type { ConfigNamespace } from './register-as'\nimport { ConfigService } from './services/config.service'\n\n/**\n * Any config namespace - uses structural typing for flexibility\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyConfigNamespace = ConfigNamespace<string, any, object>\n\n/**\n * Options for ConfigModule.forRoot\n */\nexport interface ConfigModuleOptions {\n /**\n * Array of config namespaces created via registerAs()\n */\n load: AnyConfigNamespace[]\n\n /**\n * Optional Zod schema for validating the merged config\n * Validates the entire config object after all namespaces are merged\n */\n validateSchema?: z.ZodType<object>\n}\n\n// Store options for use in onInitialize\nlet moduleOptions: ConfigModuleOptions | null = null\n\n/**\n * ConfigModule\n *\n * Provides configuration management with namespace support.\n * Uses registerAs() to create typed config namespaces that can be injected.\n *\n * @example\n * ```typescript\n * // Define config namespaces\n * const databaseConfig = registerAs('database', (env) => ({\n * url: env.DATABASE_URL,\n * maxConnections: 10\n * }))\n *\n * const emailConfig = registerAs('email', (env) => ({\n * provider: env.EMAIL_PROVIDER,\n * from: { name: 'App', email: 'noreply@example.com' }\n * }))\n *\n * // Register in module\n * @Module({\n * imports: [\n * ConfigModule.forRoot({\n * load: [databaseConfig, emailConfig],\n * validateSchema: AppConfigSchema\n * })\n * ]\n * })\n * export class AppModule {}\n *\n * // Inject config\n * constructor(\n * @inject(CONFIG_TOKENS.ConfigService) private config: IConfigService,\n * @inject(databaseConfig.KEY) private dbConfig: DatabaseConfig\n * ) {\n * // Use dot notation\n * const url = this.config.get('database.url')\n *\n * // Or inject namespace directly\n * const url = this.dbConfig.url\n * }\n * ```\n */\n@Module({\n providers: [\n // Register the main ConfigService as Singleton so initialization persists\n {\n provide: CONFIG_TOKENS.ConfigService,\n useClass: ConfigService,\n scope: Scope.Singleton,\n },\n ],\n})\nexport class ConfigModule implements OnInitialize {\n /**\n * Configure ConfigModule with namespace loaders\n *\n * @param options - Configuration options\n * @returns Dynamic module with config infrastructure\n */\n static forRoot(options: ConfigModuleOptions): DynamicModule {\n moduleOptions = options\n\n const providers: Provider[] = []\n\n // Register each namespace config using asProvider()\n for (const namespace of options.load) {\n providers.push(namespace.asProvider())\n }\n\n return {\n module: ConfigModule,\n providers,\n }\n }\n\n /**\n * Initialize config service with merged namespaces\n * Called after all providers are registered\n */\n onInitialize(context: ModuleContext): void {\n if (!moduleOptions) {\n throw new ConfigModuleNotInitializedError()\n }\n\n const env = context.container.resolve<unknown>(DI_TOKENS.CloudflareEnv)\n const configService = context.container.resolve<ConfigService>(CONFIG_TOKENS.ConfigService)\n\n // Build merged config from all namespaces\n const mergedConfig: Record<string, unknown> = {}\n\n for (const namespace of moduleOptions.load) {\n mergedConfig[namespace.namespace] = namespace.factory(env)\n }\n\n // Validate if schema provided\n if (moduleOptions.validateSchema) {\n const result = moduleOptions.validateSchema.safeParse(mergedConfig)\n if (!result.success) {\n throw new ConfigValidationError(\n 'Configuration validation failed',\n result.error\n )\n }\n }\n\n // Initialize ConfigService with merged config\n configService.initialize(mergedConfig as ModuleConfig)\n\n context.logger.debug('ConfigModule initialized', {\n namespaces: moduleOptions.load.map((n) => n.namespace),\n })\n }\n}\n","import type { InjectionToken } from 'tsyringe'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { FactoryProvider } from '../module/types'\n\n/**\n * Configuration namespace registration result\n */\nexport interface ConfigNamespace<TKey extends string, TEnv, TConfig extends object> {\n /** Auto-derived injection token (e.g., 'database' -> Symbol('stratal:config:database')) */\n readonly KEY: InjectionToken<TConfig>\n /** The namespace key */\n readonly namespace: TKey\n /** The factory function that receives env and returns config */\n readonly factory: (env: TEnv) => TConfig\n /**\n * Returns a provider configuration for use in module registration\n * Automatically injects DI_TOKENS.CloudflareEnv\n */\n asProvider(): FactoryProvider<TConfig>\n}\n\n/**\n * Create a namespaced configuration factory\n * Similar to NestJS registerAs\n *\n * @param namespace - Configuration namespace (e.g., 'database', 'email')\n * @param factory - Factory function receiving env and returning config object\n * @returns ConfigNamespace with token, factory, and asProvider() method\n *\n * @example\n * ```typescript\n * // apps/backend/src/config/database.config.ts\n * export const databaseConfig = registerAs('database', (env: Env) => ({\n * url: env.DATABASE_URL,\n * maxConnections: parseInt(env.DATABASE_MAX_CONNECTIONS || '10'),\n * }))\n *\n * // Auto-generates: databaseConfig.KEY = Symbol('stratal:config:database')\n *\n * // Usage in module:\n * // Option 1: Manual provider\n * {\n * provide: databaseConfig.KEY,\n * useFactory: databaseConfig.factory,\n * inject: [DI_TOKENS.CloudflareEnv]\n * }\n *\n * // Option 2: asProvider() helper\n * databaseConfig.asProvider()\n * // Returns: { provide: databaseConfig.KEY, useFactory: ..., inject: [DI_TOKENS.CloudflareEnv] }\n * ```\n */\nexport function registerAs<TKey extends string, TEnv, TConfig extends object>(\n namespace: TKey,\n factory: (env: TEnv) => TConfig\n): ConfigNamespace<TKey, TEnv, TConfig> {\n const KEY = Symbol.for(`stratal:config:${namespace}`) as InjectionToken<TConfig>\n\n return {\n KEY,\n namespace,\n factory,\n asProvider(): FactoryProvider<TConfig> {\n return {\n provide: KEY,\n useFactory: factory,\n inject: [DI_TOKENS.CloudflareEnv],\n }\n },\n }\n}\n\n/**\n * Helper to derive config type from registerAs result\n *\n * @example\n * ```typescript\n * const databaseConfig = registerAs('database', (env) => ({ url: env.DATABASE_URL }))\n * type DatabaseConfig = InferConfigType<typeof databaseConfig>\n * // { url: string }\n * ```\n */\nexport type InferConfigType<T> = T extends ConfigNamespace<string, unknown, infer C> ? C : never\n"],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ //#region src/queue/queue-consumer.d.ts
2
+ /**
3
+ * Queue message structure
4
+ *
5
+ * All messages dispatched to queues follow this structure.
6
+ * The `id` and `timestamp` are auto-generated by QueueSender.
7
+ */
8
+ interface QueueMessage<T = unknown> {
9
+ /** Unique message identifier (UUID) */
10
+ id: string;
11
+ /** Timestamp when message was dispatched (milliseconds since epoch) */
12
+ timestamp: number;
13
+ /** Message type for routing to consumers */
14
+ type: string;
15
+ /** Message payload */
16
+ payload: T;
17
+ /** Optional metadata including locale for i18n */
18
+ metadata?: {
19
+ locale?: string;
20
+ [key: string]: unknown;
21
+ };
22
+ }
23
+ /**
24
+ * Queue consumer interface
25
+ *
26
+ * Consumers handle messages based on their `messageTypes` declaration.
27
+ * A consumer receives messages of the declared types from ANY queue.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * @Transient()
32
+ * export class EmailConsumer implements IQueueConsumer<SendEmailInput> {
33
+ * readonly messageTypes = ['email.send', 'email.batch.send']
34
+ *
35
+ * async handle(message: QueueMessage<SendEmailInput>): Promise<void> {
36
+ * // Process email...
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ interface IQueueConsumer<T = unknown> {
42
+ /**
43
+ * Message types this consumer handles.
44
+ *
45
+ * The consumer receives messages matching these types from ANY queue.
46
+ * Use '*' to match all message types (wildcard consumer).
47
+ */
48
+ readonly messageTypes: string[];
49
+ /**
50
+ * Handle an incoming message
51
+ *
52
+ * @param message - The queue message to process
53
+ */
54
+ handle(message: QueueMessage<T>): Promise<void>;
55
+ /**
56
+ * Optional error handler for failed message processing
57
+ *
58
+ * @param error - The error that occurred
59
+ * @param message - The message that failed to process
60
+ */
61
+ onError?(error: Error, message: QueueMessage<T>): Promise<void>;
62
+ }
63
+ //#endregion
64
+ //#region src/queue/consumer-registry.d.ts
65
+ /**
66
+ * Consumer Registry
67
+ *
68
+ * Singleton service that holds all registered queue consumers indexed by message type.
69
+ * Consumers declare the message types they handle, and this registry routes messages
70
+ * to the appropriate consumers based on their types.
71
+ *
72
+ * **Message-Type Routing:**
73
+ * - Consumers declare `messageTypes` array (e.g., `['email.send', 'email.batch.send']`)
74
+ * - When a message arrives, consumers matching the message type are invoked
75
+ * - A consumer can handle messages from ANY queue (routing is by type, not queue)
76
+ * - Use `'*'` as a wildcard to handle all message types
77
+ *
78
+ * @example Consumer registration
79
+ * ```typescript
80
+ * // In consumer.ts
81
+ * @Transient()
82
+ * export class EmailConsumer implements IQueueConsumer {
83
+ * readonly messageTypes = ['email.send', 'email.batch.send']
84
+ * // ...
85
+ * }
86
+ *
87
+ * // In module.ts
88
+ * @Module({
89
+ * consumers: [EmailConsumer]
90
+ * })
91
+ *
92
+ * // Application auto-registers via ConsumerRegistry
93
+ * this.consumerRegistry.register(consumer)
94
+ * ```
95
+ */
96
+ declare class ConsumerRegistry {
97
+ /** Map from message type to consumers handling that type */
98
+ private consumersByType;
99
+ /** Set of all registered consumers (for iteration) */
100
+ private allConsumers;
101
+ /**
102
+ * Register a queue consumer
103
+ *
104
+ * Indexes the consumer by each of its declared message types.
105
+ *
106
+ * @param consumer - Queue consumer to register
107
+ */
108
+ register(consumer: IQueueConsumer): void;
109
+ /**
110
+ * Get all consumers that can handle a specific message type
111
+ *
112
+ * Returns consumers that either:
113
+ * - Explicitly declare the message type
114
+ * - Use '*' wildcard to handle all types
115
+ *
116
+ * @param messageType - The message type to find consumers for
117
+ * @returns Array of consumers that can handle this message type
118
+ */
119
+ getConsumers(messageType: string): IQueueConsumer[];
120
+ /**
121
+ * Check if any consumers can handle a message type
122
+ *
123
+ * @param messageType - The message type to check
124
+ * @returns true if at least one consumer can handle this type
125
+ */
126
+ hasConsumers(messageType: string): boolean;
127
+ /**
128
+ * Get all registered message types
129
+ *
130
+ * @returns Array of message types with registered consumers
131
+ */
132
+ getMessageTypes(): string[];
133
+ /**
134
+ * Get all registered consumers
135
+ *
136
+ * @returns Array of all registered consumers
137
+ */
138
+ getAllConsumers(): IQueueConsumer[];
139
+ }
140
+ //#endregion
141
+ export { IQueueConsumer as n, QueueMessage as r, ConsumerRegistry as t };
142
+ //# sourceMappingURL=consumer-registry-Bymm6ff4.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumer-registry-Bymm6ff4.d.mts","names":[],"sources":["../src/queue/queue-consumer.ts","../src/queue/consumer-registry.ts"],"mappings":";;AAMA;;;;;UAAiB,YAAA;EAIf;EAFA,EAAA;EAMA;EAJA,SAAA;EAMA;EAJA,IAAA;EAMG;EAJH,OAAA,EAAS,CAAA;EAIK;EAFd,QAAA;IACE,MAAA;IAAA,CACC,GAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;UAsBY,cAAA;EAsBQ;;;;;;EAAA,SAfd,YAAA;ECXE;;;;;EDkBX,MAAA,CAAO,OAAA,EAAS,YAAA,CAAa,CAAA,IAAK,OAAA;ECqDD;;;;;;ED7CjC,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,YAAA,CAAa,CAAA,IAAK,OAAA;AAAA;;;AAxDpD;;;;;;;;;;;;;;;AAkCA;;;;;;;;;;;;;;;;AAlCA,cC8Ba,gBAAA;EDkBJ;EAAA,QChBC,eAAA;EDwBR;EAAA,QCrBQ,YAAA;EDqBC;;;;;;;ECZT,QAAA,CAAS,QAAA,EAAU,cAAA;;;AAfrB;;;;;;;;EAuCE,YAAA,CAAa,WAAA,WAAsB,cAAA;EAjC3B;;;;;;EAgDR,YAAA,CAAa,WAAA;EAAb;;;;;EASA,eAAA,CAAA;EASiC;;;;;EAAjC,eAAA,CAAA,GAAmB,cAAA;AAAA"}
@@ -1,119 +1,6 @@
1
- import { s as ApplicationError } from "../index-BJWm863C.mjs";
2
- //#region src/cron/cron-job.d.ts
3
- /**
4
- * Interface for cron jobs that can be registered by modules
5
- *
6
- * Cron jobs are executed when Cloudflare triggers match their schedule.
7
- * Jobs are registered via the module's getCronJobs() method.
8
- *
9
- * @example
10
- * ```typescript
11
- * @Transient()
12
- * export class DataCleanupJob implements CronJob {
13
- * readonly schedule = '0 2 * * *' // Daily at 2 AM UTC
14
- *
15
- * constructor(
16
- * @inject(LOGGER_TOKENS.LoggerService) private logger: LoggerService,
17
- * ) {}
18
- *
19
- * async execute(controller: ScheduledController): Promise<void> {
20
- * this.logger.info('Running data cleanup')
21
- * await this.cleanupExpiredData()
22
- * }
23
- *
24
- * async onError(error: Error): Promise<void> {
25
- * this.logger.error('Data cleanup failed', { error: error.message })
26
- * }
27
- * }
28
- * ```
29
- */
30
- interface CronJob {
31
- /**
32
- * Cron expression that triggers this job
33
- *
34
- * Must match a cron trigger defined in wrangler.jsonc
35
- * @example '0 2 * * *' // Daily at 2 AM UTC
36
- * @example '* /15 * * * *' // Every 15 minutes
37
- */
38
- readonly schedule: string;
39
- /**
40
- * Execute the cron job
41
- *
42
- * @param controller - Cloudflare ScheduledController with scheduledTime and cron
43
- * @throws ApplicationError for expected errors
44
- */
45
- execute(controller: ScheduledController): Promise<void>;
46
- /**
47
- * Optional error handler for job execution failures
48
- *
49
- * If not provided, errors are logged via GlobalErrorHandler
50
- *
51
- * @param error - Error that occurred during execution
52
- * @param controller - Cloudflare ScheduledController
53
- */
54
- onError?(error: Error, controller: ScheduledController): Promise<void>;
55
- }
56
- //#endregion
57
- //#region src/cron/cron-manager.d.ts
58
- /**
59
- * Manages cron job registration and execution
60
- *
61
- * CronManager is a singleton service that:
62
- * - Registers cron jobs from modules
63
- * - Routes scheduled events to matching jobs
64
- * - Handles errors during job execution
65
- *
66
- * Jobs are grouped by their cron expression, allowing multiple jobs
67
- * to run on the same schedule.
68
- */
69
- declare class CronManager {
70
- /**
71
- * Map of cron expressions to jobs
72
- * Key: Cron expression (e.g., '0 2 * * *')
73
- * Value: Array of jobs matching that expression
74
- */
75
- private jobs;
76
- /**
77
- * Register a cron job
78
- *
79
- * Jobs with the same schedule are grouped together and executed
80
- * sequentially when the trigger fires.
81
- *
82
- * @param job - CronJob instance to register
83
- */
84
- registerJob(job: CronJob): void;
85
- /**
86
- * Execute all jobs matching the triggered cron expression
87
- *
88
- * Jobs are executed sequentially. If a job fails:
89
- * - Its onError() hook is called (if defined)
90
- * - Execution continues with the next job
91
- * - Errors are collected and logged
92
- *
93
- * @param controller - Cloudflare ScheduledController
94
- */
95
- executeScheduled(controller: ScheduledController): Promise<void>;
96
- /**
97
- * Get all registered jobs for a specific cron expression
98
- *
99
- * @param schedule - Cron expression
100
- * @returns Array of jobs for that schedule, or empty array if none
101
- */
102
- getJobsForSchedule(schedule: string): CronJob[];
103
- /**
104
- * Get all registered cron expressions
105
- *
106
- * @returns Array of unique cron expressions
107
- */
108
- getAllSchedules(): string[];
109
- /**
110
- * Get total number of registered jobs across all schedules
111
- *
112
- * @returns Total job count
113
- */
114
- getTotalJobCount(): number;
115
- }
116
- //#endregion
1
+ import { s as ApplicationError } from "../index-BFCxSp_f.mjs";
2
+ import { n as CronJob, t as CronManager } from "../cron-manager-D7imGwUT.mjs";
3
+
117
4
  //#region src/cron/errors/cron-execution.error.d.ts
118
5
  /**
119
6
  * Error thrown when one or more cron jobs fail execution
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cron/cron-job.ts","../../src/cron/cron-manager.ts","../../src/cron/errors/cron-execution.error.ts"],"mappings":";;;;;;AA2BA;;;;;;;;;;;;;;;;;;;;;;;UAAiB,OAAA;;;ACZjB;;;;;WDoBU,QAAA;EC6D6B;;;;;;EDrDtC,OAAA,CAAQ,UAAA,EAAY,mBAAA,GAAsB,OAAA;ECKpC;;;;;;;;EDKN,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,mBAAA,GAAsB,OAAA;AAAA;;;;;;AA1B1D;;;;;;;;cCXa,WAAA;EDqCoD;;;;;EAAA,QC/BxD,IAAA;ED+BR;;;;;;;;ECrBA,WAAA,CAAY,GAAA,EAAK,OAAA;;;AAjBlB;;;;;;;;EAiCO,gBAAA,CAAiB,UAAA,EAAY,mBAAA,GAAsB,OAAA;EA1BjD;;;;;;EA0ER,kBAAA,CAAmB,QAAA,WAAmB,OAAA;EAhDmB;;;;;EAyDzD,eAAA,CAAA;EASgB;;;;;EAAhB,gBAAA,CAAA;AAAA;;;;;;ADvFD;;cEnBa,kBAAA,SAA2B,gBAAA;cAEtC,QAAA,UACA,eAAA,UACA,QAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cron/errors/cron-execution.error.ts"],"mappings":";;;;;;;;AAQA;cAAa,kBAAA,SAA2B,gBAAA;cAEtC,QAAA,UACA,eAAA,UACA,QAAA;AAAA"}
@@ -1,5 +1,4 @@
1
- import "../errors-CtCi1wn6.mjs";
2
- import "../decorate-D5j-d9_z.mjs";
3
- import "../logger-BR1-s1Um.mjs";
4
- import { n as CronExecutionError, t as CronManager } from "../cron-manager-DR7fiG6o.mjs";
1
+ import "../errors-DSKapqD8.mjs";
2
+ import "../logger-CGT91VY6.mjs";
3
+ import { n as CronExecutionError, t as CronManager } from "../cron-manager-CFBamKKk.mjs";
5
4
  export { CronExecutionError, CronManager };
@@ -1,5 +1,5 @@
1
- import { S as ApplicationError, b as ERROR_CODES } from "./errors-CtCi1wn6.mjs";
2
- import { i as Transient, t as __decorate } from "./decorate-D5j-d9_z.mjs";
1
+ import { S as ApplicationError, b as ERROR_CODES } from "./errors-DSKapqD8.mjs";
2
+ import { a as __decorate, d as Transient } from "./logger-CGT91VY6.mjs";
3
3
  //#region src/cron/errors/cron-execution.error.ts
4
4
  /**
5
5
  * Error thrown when one or more cron jobs fail execution
@@ -104,4 +104,4 @@ CronManager = __decorate([Transient()], CronManager);
104
104
  //#endregion
105
105
  export { CronExecutionError as n, CronManager as t };
106
106
 
107
- //# sourceMappingURL=cron-manager-DR7fiG6o.mjs.map
107
+ //# sourceMappingURL=cron-manager-CFBamKKk.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cron-manager-DR7fiG6o.mjs","names":[],"sources":["../src/cron/errors/cron-execution.error.ts","../src/cron/cron-manager.ts"],"sourcesContent":["import { ApplicationError } from '../../errors'\nimport { ERROR_CODES } from '../../errors'\n\n/**\n * Error thrown when one or more cron jobs fail execution\n *\n * This error aggregates failures from multiple jobs that share the same schedule.\n */\nexport class CronExecutionError extends ApplicationError {\n\tconstructor(\n\t\tschedule: string,\n\t\tfailedJobsCount: number,\n\t\tjobNames: string\n\t) {\n\t\tsuper(\n\t\t\t'errors.cronExecutionFailed',\n\t\t\tERROR_CODES.SYSTEM.CRON_EXECUTION_FAILED,\n\t\t\t{\n\t\t\t\tschedule,\n\t\t\t\tcount: failedJobsCount,\n\t\t\t\tjobs: jobNames\n\t\t\t}\n\t\t)\n\t}\n}\n","import { Transient } from '../di/decorators'\nimport type { CronJob } from './cron-job'\nimport { CronExecutionError } from './errors/cron-execution.error'\n\n/**\n * Manages cron job registration and execution\n *\n * CronManager is a singleton service that:\n * - Registers cron jobs from modules\n * - Routes scheduled events to matching jobs\n * - Handles errors during job execution\n *\n * Jobs are grouped by their cron expression, allowing multiple jobs\n * to run on the same schedule.\n */\n@Transient()\nexport class CronManager {\n\t/**\n\t * Map of cron expressions to jobs\n\t * Key: Cron expression (e.g., '0 2 * * *')\n\t * Value: Array of jobs matching that expression\n\t */\n\tprivate jobs = new Map<string, CronJob[]>()\n\n\t/**\n\t * Register a cron job\n\t *\n\t * Jobs with the same schedule are grouped together and executed\n\t * sequentially when the trigger fires.\n\t *\n\t * @param job - CronJob instance to register\n\t */\n\tregisterJob(job: CronJob): void {\n\t\tconst existing = this.jobs.get(job.schedule) ?? []\n\t\texisting.push(job)\n\t\tthis.jobs.set(job.schedule, existing)\n\t}\n\n\t/**\n\t * Execute all jobs matching the triggered cron expression\n\t *\n\t * Jobs are executed sequentially. If a job fails:\n\t * - Its onError() hook is called (if defined)\n\t * - Execution continues with the next job\n\t * - Errors are collected and logged\n\t *\n\t * @param controller - Cloudflare ScheduledController\n\t */\n\tasync executeScheduled(controller: ScheduledController): Promise<void> {\n\t\tconst { cron } = controller\n\t\tconst matchingJobs = this.jobs.get(cron) ?? []\n\n\t\tif (matchingJobs.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst errors: { job: string; error: Error }[] = []\n\n\t\tfor (const job of matchingJobs) {\n\t\t\tconst jobName = job.constructor.name\n\n\t\t\ttry {\n\t\t\t\tawait job.execute(controller)\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error\n\t\t\t\terrors.push({ job: jobName, error: err })\n\n\t\t\t\t// Call job's error handler if defined\n\t\t\t\tif (job.onError) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait job.onError(err, controller)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If onError() itself fails, we just continue\n\t\t\t\t\t\t// The error will be logged by GlobalErrorHandler\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If any jobs failed, throw an aggregate error\n\t\t// This ensures the error is logged by GlobalErrorHandler\n\t\tif (errors.length > 0) {\n\t\t\tconst jobNames = errors\n\t\t\t\t.map(({ job, error }) => `${job}: ${error.message}`)\n\t\t\t\t.join('; ')\n\n\t\t\tthrow new CronExecutionError(cron, errors.length, jobNames)\n\t\t}\n\t}\n\n\t/**\n\t * Get all registered jobs for a specific cron expression\n\t *\n\t * @param schedule - Cron expression\n\t * @returns Array of jobs for that schedule, or empty array if none\n\t */\n\tgetJobsForSchedule(schedule: string): CronJob[] {\n\t\treturn this.jobs.get(schedule) ?? []\n\t}\n\n\t/**\n\t * Get all registered cron expressions\n\t *\n\t * @returns Array of unique cron expressions\n\t */\n\tgetAllSchedules(): string[] {\n\t\treturn Array.from(this.jobs.keys())\n\t}\n\n\t/**\n\t * Get total number of registered jobs across all schedules\n\t *\n\t * @returns Total job count\n\t */\n\tgetTotalJobCount(): number {\n\t\tlet count = 0\n\t\tfor (const jobs of this.jobs.values()) {\n\t\t\tcount += jobs.length\n\t\t}\n\t\treturn count\n\t}\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,qBAAb,cAAwC,iBAAiB;CACxD,YACC,UACA,iBACA,UACC;AACD,QACC,8BACA,YAAY,OAAO,uBACnB;GACC;GACA,OAAO;GACP,MAAM;GACN,CACD;;;;;ACNI,IAAA,cAAA,MAAM,YAAY;;;;;;CAMxB,uBAAe,IAAI,KAAwB;;;;;;;;;CAU3C,YAAY,KAAoB;EAC/B,MAAM,WAAW,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE;AAClD,WAAS,KAAK,IAAI;AAClB,OAAK,KAAK,IAAI,IAAI,UAAU,SAAS;;;;;;;;;;;;CAatC,MAAM,iBAAiB,YAAgD;EACtE,MAAM,EAAE,SAAS;EACjB,MAAM,eAAe,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAE9C,MAAI,aAAa,WAAW,EAC3B;EAGD,MAAM,SAA0C,EAAE;AAElD,OAAK,MAAM,OAAO,cAAc;GAC/B,MAAM,UAAU,IAAI,YAAY;AAEhC,OAAI;AACH,UAAM,IAAI,QAAQ,WAAW;YACrB,OAAO;IACf,MAAM,MAAM;AACZ,WAAO,KAAK;KAAE,KAAK;KAAS,OAAO;KAAK,CAAC;AAGzC,QAAI,IAAI,QACP,KAAI;AACH,WAAM,IAAI,QAAQ,KAAK,WAAW;YAC3B;;;AAUX,MAAI,OAAO,SAAS,GAAG;GACtB,MAAM,WAAW,OACf,KAAK,EAAE,KAAK,YAAY,GAAG,IAAI,IAAI,MAAM,UAAU,CACnD,KAAK,KAAK;AAEZ,SAAM,IAAI,mBAAmB,MAAM,OAAO,QAAQ,SAAS;;;;;;;;;CAU7D,mBAAmB,UAA6B;AAC/C,SAAO,KAAK,KAAK,IAAI,SAAS,IAAI,EAAE;;;;;;;CAQrC,kBAA4B;AAC3B,SAAO,MAAM,KAAK,KAAK,KAAK,MAAM,CAAC;;;;;;;CAQpC,mBAA2B;EAC1B,IAAI,QAAQ;AACZ,OAAK,MAAM,QAAQ,KAAK,KAAK,QAAQ,CACpC,UAAS,KAAK;AAEf,SAAO;;;0BAxGR,WAAW,CAAA,EAAA,YAAA"}
1
+ {"version":3,"file":"cron-manager-CFBamKKk.mjs","names":[],"sources":["../src/cron/errors/cron-execution.error.ts","../src/cron/cron-manager.ts"],"sourcesContent":["import { ApplicationError } from '../../errors'\nimport { ERROR_CODES } from '../../errors'\n\n/**\n * Error thrown when one or more cron jobs fail execution\n *\n * This error aggregates failures from multiple jobs that share the same schedule.\n */\nexport class CronExecutionError extends ApplicationError {\n\tconstructor(\n\t\tschedule: string,\n\t\tfailedJobsCount: number,\n\t\tjobNames: string\n\t) {\n\t\tsuper(\n\t\t\t'errors.cronExecutionFailed',\n\t\t\tERROR_CODES.SYSTEM.CRON_EXECUTION_FAILED,\n\t\t\t{\n\t\t\t\tschedule,\n\t\t\t\tcount: failedJobsCount,\n\t\t\t\tjobs: jobNames\n\t\t\t}\n\t\t)\n\t}\n}\n","import { Transient } from '../di/decorators'\nimport type { CronJob } from './cron-job'\nimport { CronExecutionError } from './errors/cron-execution.error'\n\n/**\n * Manages cron job registration and execution\n *\n * CronManager is a singleton service that:\n * - Registers cron jobs from modules\n * - Routes scheduled events to matching jobs\n * - Handles errors during job execution\n *\n * Jobs are grouped by their cron expression, allowing multiple jobs\n * to run on the same schedule.\n */\n@Transient()\nexport class CronManager {\n\t/**\n\t * Map of cron expressions to jobs\n\t * Key: Cron expression (e.g., '0 2 * * *')\n\t * Value: Array of jobs matching that expression\n\t */\n\tprivate jobs = new Map<string, CronJob[]>()\n\n\t/**\n\t * Register a cron job\n\t *\n\t * Jobs with the same schedule are grouped together and executed\n\t * sequentially when the trigger fires.\n\t *\n\t * @param job - CronJob instance to register\n\t */\n\tregisterJob(job: CronJob): void {\n\t\tconst existing = this.jobs.get(job.schedule) ?? []\n\t\texisting.push(job)\n\t\tthis.jobs.set(job.schedule, existing)\n\t}\n\n\t/**\n\t * Execute all jobs matching the triggered cron expression\n\t *\n\t * Jobs are executed sequentially. If a job fails:\n\t * - Its onError() hook is called (if defined)\n\t * - Execution continues with the next job\n\t * - Errors are collected and logged\n\t *\n\t * @param controller - Cloudflare ScheduledController\n\t */\n\tasync executeScheduled(controller: ScheduledController): Promise<void> {\n\t\tconst { cron } = controller\n\t\tconst matchingJobs = this.jobs.get(cron) ?? []\n\n\t\tif (matchingJobs.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst errors: { job: string; error: Error }[] = []\n\n\t\tfor (const job of matchingJobs) {\n\t\t\tconst jobName = job.constructor.name\n\n\t\t\ttry {\n\t\t\t\tawait job.execute(controller)\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as Error\n\t\t\t\terrors.push({ job: jobName, error: err })\n\n\t\t\t\t// Call job's error handler if defined\n\t\t\t\tif (job.onError) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait job.onError(err, controller)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If onError() itself fails, we just continue\n\t\t\t\t\t\t// The error will be logged by GlobalErrorHandler\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If any jobs failed, throw an aggregate error\n\t\t// This ensures the error is logged by GlobalErrorHandler\n\t\tif (errors.length > 0) {\n\t\t\tconst jobNames = errors\n\t\t\t\t.map(({ job, error }) => `${job}: ${error.message}`)\n\t\t\t\t.join('; ')\n\n\t\t\tthrow new CronExecutionError(cron, errors.length, jobNames)\n\t\t}\n\t}\n\n\t/**\n\t * Get all registered jobs for a specific cron expression\n\t *\n\t * @param schedule - Cron expression\n\t * @returns Array of jobs for that schedule, or empty array if none\n\t */\n\tgetJobsForSchedule(schedule: string): CronJob[] {\n\t\treturn this.jobs.get(schedule) ?? []\n\t}\n\n\t/**\n\t * Get all registered cron expressions\n\t *\n\t * @returns Array of unique cron expressions\n\t */\n\tgetAllSchedules(): string[] {\n\t\treturn Array.from(this.jobs.keys())\n\t}\n\n\t/**\n\t * Get total number of registered jobs across all schedules\n\t *\n\t * @returns Total job count\n\t */\n\tgetTotalJobCount(): number {\n\t\tlet count = 0\n\t\tfor (const jobs of this.jobs.values()) {\n\t\t\tcount += jobs.length\n\t\t}\n\t\treturn count\n\t}\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,qBAAb,cAAwC,iBAAiB;CACxD,YACC,UACA,iBACA,UACC;AACD,QACC,8BACA,YAAY,OAAO,uBACnB;GACC;GACA,OAAO;GACP,MAAM;GACN,CACD;;;;;ACNI,IAAA,cAAA,MAAM,YAAY;;;;;;CAMxB,uBAAe,IAAI,KAAwB;;;;;;;;;CAU3C,YAAY,KAAoB;EAC/B,MAAM,WAAW,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE;AAClD,WAAS,KAAK,IAAI;AAClB,OAAK,KAAK,IAAI,IAAI,UAAU,SAAS;;;;;;;;;;;;CAatC,MAAM,iBAAiB,YAAgD;EACtE,MAAM,EAAE,SAAS;EACjB,MAAM,eAAe,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAE9C,MAAI,aAAa,WAAW,EAC3B;EAGD,MAAM,SAA0C,EAAE;AAElD,OAAK,MAAM,OAAO,cAAc;GAC/B,MAAM,UAAU,IAAI,YAAY;AAEhC,OAAI;AACH,UAAM,IAAI,QAAQ,WAAW;YACrB,OAAO;IACf,MAAM,MAAM;AACZ,WAAO,KAAK;KAAE,KAAK;KAAS,OAAO;KAAK,CAAC;AAGzC,QAAI,IAAI,QACP,KAAI;AACH,WAAM,IAAI,QAAQ,KAAK,WAAW;YAC3B;;;AAUX,MAAI,OAAO,SAAS,GAAG;GACtB,MAAM,WAAW,OACf,KAAK,EAAE,KAAK,YAAY,GAAG,IAAI,IAAI,MAAM,UAAU,CACnD,KAAK,KAAK;AAEZ,SAAM,IAAI,mBAAmB,MAAM,OAAO,QAAQ,SAAS;;;;;;;;;CAU7D,mBAAmB,UAA6B;AAC/C,SAAO,KAAK,KAAK,IAAI,SAAS,IAAI,EAAE;;;;;;;CAQrC,kBAA4B;AAC3B,SAAO,MAAM,KAAK,KAAK,KAAK,MAAM,CAAC;;;;;;;CAQpC,mBAA2B;EAC1B,IAAI,QAAQ;AACZ,OAAK,MAAM,QAAQ,KAAK,KAAK,QAAQ,CACpC,UAAS,KAAK;AAEf,SAAO;;;0BAxGR,WAAW,CAAA,EAAA,YAAA"}
@@ -0,0 +1,117 @@
1
+ //#region src/cron/cron-job.d.ts
2
+ /**
3
+ * Interface for cron jobs that can be registered by modules
4
+ *
5
+ * Cron jobs are executed when Cloudflare triggers match their schedule.
6
+ * Jobs are registered via the module's getCronJobs() method.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * @Transient()
11
+ * export class DataCleanupJob implements CronJob {
12
+ * readonly schedule = '0 2 * * *' // Daily at 2 AM UTC
13
+ *
14
+ * constructor(
15
+ * @inject(LOGGER_TOKENS.LoggerService) private logger: LoggerService,
16
+ * ) {}
17
+ *
18
+ * async execute(controller: ScheduledController): Promise<void> {
19
+ * this.logger.info('Running data cleanup')
20
+ * await this.cleanupExpiredData()
21
+ * }
22
+ *
23
+ * async onError(error: Error): Promise<void> {
24
+ * this.logger.error('Data cleanup failed', { error: error.message })
25
+ * }
26
+ * }
27
+ * ```
28
+ */
29
+ interface CronJob {
30
+ /**
31
+ * Cron expression that triggers this job
32
+ *
33
+ * Must match a cron trigger defined in wrangler.jsonc
34
+ * @example '0 2 * * *' // Daily at 2 AM UTC
35
+ * @example '* /15 * * * *' // Every 15 minutes
36
+ */
37
+ readonly schedule: string;
38
+ /**
39
+ * Execute the cron job
40
+ *
41
+ * @param controller - Cloudflare ScheduledController with scheduledTime and cron
42
+ * @throws ApplicationError for expected errors
43
+ */
44
+ execute(controller: ScheduledController): Promise<void>;
45
+ /**
46
+ * Optional error handler for job execution failures
47
+ *
48
+ * If not provided, errors are logged via GlobalErrorHandler
49
+ *
50
+ * @param error - Error that occurred during execution
51
+ * @param controller - Cloudflare ScheduledController
52
+ */
53
+ onError?(error: Error, controller: ScheduledController): Promise<void>;
54
+ }
55
+ //#endregion
56
+ //#region src/cron/cron-manager.d.ts
57
+ /**
58
+ * Manages cron job registration and execution
59
+ *
60
+ * CronManager is a singleton service that:
61
+ * - Registers cron jobs from modules
62
+ * - Routes scheduled events to matching jobs
63
+ * - Handles errors during job execution
64
+ *
65
+ * Jobs are grouped by their cron expression, allowing multiple jobs
66
+ * to run on the same schedule.
67
+ */
68
+ declare class CronManager {
69
+ /**
70
+ * Map of cron expressions to jobs
71
+ * Key: Cron expression (e.g., '0 2 * * *')
72
+ * Value: Array of jobs matching that expression
73
+ */
74
+ private jobs;
75
+ /**
76
+ * Register a cron job
77
+ *
78
+ * Jobs with the same schedule are grouped together and executed
79
+ * sequentially when the trigger fires.
80
+ *
81
+ * @param job - CronJob instance to register
82
+ */
83
+ registerJob(job: CronJob): void;
84
+ /**
85
+ * Execute all jobs matching the triggered cron expression
86
+ *
87
+ * Jobs are executed sequentially. If a job fails:
88
+ * - Its onError() hook is called (if defined)
89
+ * - Execution continues with the next job
90
+ * - Errors are collected and logged
91
+ *
92
+ * @param controller - Cloudflare ScheduledController
93
+ */
94
+ executeScheduled(controller: ScheduledController): Promise<void>;
95
+ /**
96
+ * Get all registered jobs for a specific cron expression
97
+ *
98
+ * @param schedule - Cron expression
99
+ * @returns Array of jobs for that schedule, or empty array if none
100
+ */
101
+ getJobsForSchedule(schedule: string): CronJob[];
102
+ /**
103
+ * Get all registered cron expressions
104
+ *
105
+ * @returns Array of unique cron expressions
106
+ */
107
+ getAllSchedules(): string[];
108
+ /**
109
+ * Get total number of registered jobs across all schedules
110
+ *
111
+ * @returns Total job count
112
+ */
113
+ getTotalJobCount(): number;
114
+ }
115
+ //#endregion
116
+ export { CronJob as n, CronManager as t };
117
+ //# sourceMappingURL=cron-manager-D7imGwUT.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-manager-D7imGwUT.d.mts","names":[],"sources":["../src/cron/cron-job.ts","../src/cron/cron-manager.ts"],"mappings":";;AA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;UAAiB,OAAA;ECXO;;;;;;;EAAA,SDmBd,QAAA;ECbD;;;;;;EDqBR,OAAA,CAAQ,UAAA,EAAY,mBAAA,GAAsB,OAAA;ECKe;;;;;;;;EDKzD,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,mBAAA,GAAsB,OAAA;AAAA;;;AA1B1D;;;;;;;;;;;AAAA,cCXa,WAAA;ED2BQ;;;;;EAAA,QCrBZ,IAAA;ED+B2B;;;;;;;;ECrBnC,WAAA,CAAY,GAAA,EAAK,OAAA;EAhBM;;;;;;;;;;EAgCjB,gBAAA,CAAiB,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAhB7C;;;;;;EAgEZ,kBAAA,CAAmB,QAAA,WAAmB,OAAA;EAAA;;;;;EAStC,eAAA,CAAA;;;;;;EASA,gBAAA,CAAA;AAAA"}
@@ -1,2 +1,2 @@
1
- import { $t as Container, Gt as Transient, Jt as ParamInjection, Kt as INJECT_PARAM_METADATA_KEY, Qt as DI_TOKENS, Ut as RequestScopeOperationNotAllowedError, Wt as ConditionalBindingFallbackError, Xt as CONTAINER_TOKEN, Yt as getMethodInjections, Zt as DIToken, an as injectable, cn as ConditionalBindingBuilder, dn as ConditionalBindingUse, en as ContainerOptions, fn as PredicateContainer, gn as WhenOptions, hn as Scope, in as inject, ln as ConditionalBindingBuilderImpl, mn as ExtensionDecorator, nn as container, on as instancePerContainerCachingFactory, pn as ContainerLike, qt as InjectParam, rn as delay, sn as singleton, tn as DependencyContainer, un as ConditionalBindingGive } from "../index-BJWm863C.mjs";
1
+ import { $t as DI_TOKENS, Gt as ConditionalBindingFallbackError, Jt as InjectParam, Kt as Transient, Qt as DIToken, Wt as RequestScopeOperationNotAllowedError, Xt as getMethodInjections, Yt as ParamInjection, Zt as CONTAINER_TOKEN, _n as WhenOptions, an as inject, cn as singleton, dn as ConditionalBindingGive, en as Container, fn as ConditionalBindingUse, gn as Scope, hn as ExtensionDecorator, in as delay, ln as ConditionalBindingBuilder, mn as ContainerLike, nn as DependencyContainer, on as injectable, pn as PredicateContainer, qt as INJECT_PARAM_METADATA_KEY, rn as container, sn as instancePerContainerCachingFactory, tn as ContainerOptions, un as ConditionalBindingBuilderImpl } from "../index-BFCxSp_f.mjs";
2
2
  export { CONTAINER_TOKEN, ConditionalBindingBuilder, ConditionalBindingBuilderImpl, ConditionalBindingFallbackError, ConditionalBindingGive, ConditionalBindingUse, Container, ContainerLike, ContainerOptions, DIToken, DI_TOKENS, DependencyContainer, ExtensionDecorator, INJECT_PARAM_METADATA_KEY, InjectParam, ParamInjection, PredicateContainer, RequestScopeOperationNotAllowedError, Scope, Transient, WhenOptions, container, delay, getMethodInjections, inject, injectable, instancePerContainerCachingFactory, singleton };
package/dist/di/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { _ as ConditionalBindingFallbackError, c as Container, d as inject, f as injectable, g as RequestScopeOperationNotAllowedError, h as ConditionalBindingBuilderImpl, l as container, m as singleton, p as instancePerContainerCachingFactory, s as Scope, u as delay } from "../errors-CtCi1wn6.mjs";
2
- import { a as INJECT_PARAM_METADATA_KEY, c as CONTAINER_TOKEN, i as Transient, l as DI_TOKENS, o as InjectParam, s as getMethodInjections } from "../decorate-D5j-d9_z.mjs";
3
- import "../logger-BR1-s1Um.mjs";
1
+ import { _ as ConditionalBindingFallbackError, c as Container, d as inject, f as injectable, g as RequestScopeOperationNotAllowedError, h as ConditionalBindingBuilderImpl, l as container, m as singleton, p as instancePerContainerCachingFactory, s as Scope, u as delay } from "../errors-DSKapqD8.mjs";
2
+ import { d as Transient, f as INJECT_PARAM_METADATA_KEY, g as DI_TOKENS, h as CONTAINER_TOKEN, m as getMethodInjections, p as InjectParam } from "../logger-CGT91VY6.mjs";
4
3
  export { CONTAINER_TOKEN, ConditionalBindingBuilderImpl, ConditionalBindingFallbackError, Container, DI_TOKENS, INJECT_PARAM_METADATA_KEY, InjectParam, RequestScopeOperationNotAllowedError, Scope, Transient, container, delay, getMethodInjections, inject, injectable, instancePerContainerCachingFactory, singleton };
@@ -1,6 +1,6 @@
1
- import { it as DynamicModule, nt as AsyncModuleOptions, s as ApplicationError } from "../index-BJWm863C.mjs";
2
- import { o as z } from "../index-DVhdhLvE.mjs";
3
- import { g as QueueName, h as IQueueSender } from "../index-D9iYu2Yc.mjs";
1
+ import { at as DynamicModule, rt as AsyncModuleOptions, s as ApplicationError } from "../index-BFCxSp_f.mjs";
2
+ import { o as z } from "../index-NGxg-KP_.mjs";
3
+ import { g as QueueName, h as IQueueSender } from "../index-DGRe6Yoa.mjs";
4
4
  import { ReactElement } from "react";
5
5
 
6
6
  //#region src/email/email.module.d.ts
@@ -1,19 +1,18 @@
1
- import { S as ApplicationError, b as ERROR_CODES } from "../errors-CtCi1wn6.mjs";
2
- import { i as Transient, n as __decorateParam, r as __decorateMetadata, t as __decorate } from "../decorate-D5j-d9_z.mjs";
3
- import { s as LOGGER_TOKENS } from "../logger-BR1-s1Um.mjs";
4
- import { r as Module } from "../module-BgdxxzBe.mjs";
5
- import "../events-CXl-o1Ad.mjs";
6
- import "../colors-DJaRDXoS.mjs";
7
- import "../command-BvCOD6df.mjs";
8
- import "../is-command-BfCgWAcQ.mjs";
9
- import "../is-seeder-CebjZCDn.mjs";
10
- import "../middleware-C0Ebzswy.mjs";
11
- import "../router-context-BEJe9HEB.mjs";
12
- import { a as withI18n, i as z } from "../validation-Bh875Lyg.mjs";
13
- import { c as QUEUE_TOKENS } from "../queue.module-BZvmeAMj.mjs";
1
+ import { S as ApplicationError, b as ERROR_CODES } from "../errors-DSKapqD8.mjs";
2
+ import { a as __decorate, d as Transient, o as __decorateParam, s as __decorateMetadata, u as LOGGER_TOKENS } from "../logger-CGT91VY6.mjs";
3
+ import { r as Module } from "../module-tUtyVJ5E.mjs";
4
+ import "../events-CvUSgEuN.mjs";
5
+ import "../middleware-Bl-b5pkt.mjs";
6
+ import "../router-context-D9R1v2Ac.mjs";
7
+ import "../colors-Y7WIFXs7.mjs";
8
+ import "../command-B1CPgsrU.mjs";
9
+ import "../is-command-DJVI6wEJ.mjs";
10
+ import "../is-seeder-D5MIEcdz.mjs";
11
+ import { a as withI18n, i as z } from "../validation-DQTC259A.mjs";
12
+ import { c as QUEUE_TOKENS } from "../queue.module-BtI8f4Jo.mjs";
14
13
  import "../queue/index.mjs";
15
- import "../errors-H3TZnVeX.mjs";
16
- import { l as STORAGE_TOKENS } from "../storage-By_ow2o_.mjs";
14
+ import "../errors-DuAR5Wke.mjs";
15
+ import { l as STORAGE_TOKENS } from "../storage-CZKHOhci.mjs";
17
16
  import { inject } from "tsyringe";
18
17
  import { render } from "@react-email/render";
19
18
  //#region src/email/email.tokens.ts
@@ -264,11 +263,11 @@ let EmailProviderFactory = class EmailProviderFactory {
264
263
  async create() {
265
264
  switch (this.options.provider) {
266
265
  case "resend": {
267
- const { ResendProvider } = await import("../resend.provider-BCCACQAU.mjs");
266
+ const { ResendProvider } = await import("../resend.provider-bXMEkdRJ.mjs");
268
267
  return new ResendProvider(this.options);
269
268
  }
270
269
  case "smtp": {
271
- const { SmtpProvider } = await import("../smtp.provider-B8XtOcHU.mjs");
270
+ const { SmtpProvider } = await import("../smtp.provider-DrbHQztF.mjs");
272
271
  return new SmtpProvider(this.options);
273
272
  }
274
273
  default: throw new EmailProviderNotSupportedError(this.options.provider);