stratal 0.0.23 → 0.0.25

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 (123) hide show
  1. package/dist/cache/index.d.mts +1 -1
  2. package/dist/cache/index.mjs +2 -2
  3. package/dist/{command-CPhFHjG3.d.mts → command-4HnKnC-G.d.mts} +2 -2
  4. package/dist/{command-CPhFHjG3.d.mts.map → command-4HnKnC-G.d.mts.map} +1 -1
  5. package/dist/config/index.d.mts +1 -1
  6. package/dist/config/index.mjs +2 -2
  7. package/dist/{controller.decorator-C5UVeJS3.mjs → controller.decorator-DKeZ71aP.mjs} +3 -3
  8. package/dist/{controller.decorator-C5UVeJS3.mjs.map → controller.decorator-DKeZ71aP.mjs.map} +1 -1
  9. package/dist/cron/index.d.mts +1 -1
  10. package/dist/cron/index.mjs +1 -1
  11. package/dist/{cron.module-Bgzq5hiT.mjs → cron.module-DgxLe8Xy.mjs} +3 -3
  12. package/dist/{cron.module-Bgzq5hiT.mjs.map → cron.module-DgxLe8Xy.mjs.map} +1 -1
  13. package/dist/di/index.d.mts +1 -1
  14. package/dist/di/index.mjs +1 -1
  15. package/dist/{di-DseMn-z9.mjs → di-B0NXIdRF.mjs} +17 -1
  16. package/dist/di-B0NXIdRF.mjs.map +1 -0
  17. package/dist/email/index.d.mts +2 -2
  18. package/dist/email/index.mjs +3 -3
  19. package/dist/errors/index.d.mts +1 -1
  20. package/dist/errors/index.mjs +2 -2
  21. package/dist/{errors-mXYxG0XB.mjs → errors-EUtwVfNm.mjs} +3 -3
  22. package/dist/{errors-mXYxG0XB.mjs.map → errors-EUtwVfNm.mjs.map} +1 -1
  23. package/dist/events/index.d.mts +8 -0
  24. package/dist/events/index.d.mts.map +1 -1
  25. package/dist/events/index.mjs +1 -1
  26. package/dist/{events-BXJGZjpG.mjs → events-zbCMY_Gf.mjs} +5 -2
  27. package/dist/events-zbCMY_Gf.mjs.map +1 -0
  28. package/dist/{exception-context-kEoMFwze.mjs → exception-context-Bd5Hk2c_.mjs} +2 -2
  29. package/dist/{exception-context-kEoMFwze.mjs.map → exception-context-Bd5Hk2c_.mjs.map} +1 -1
  30. package/dist/{gateway-context-TMu_AlJt.mjs → gateway-context-I0tKgGfh.mjs} +4 -4
  31. package/dist/{gateway-context-TMu_AlJt.mjs.map → gateway-context-I0tKgGfh.mjs.map} +1 -1
  32. package/dist/guards/index.d.mts +1 -1
  33. package/dist/{hono-app-CvV3hOfT.mjs → hono-app-C7-1m9eK.mjs} +6 -6
  34. package/dist/{hono-app-CvV3hOfT.mjs.map → hono-app-C7-1m9eK.mjs.map} +1 -1
  35. package/dist/{http-method.decorator-ByWZb9DO.mjs → http-method.decorator-xCuGoZPC.mjs} +2 -2
  36. package/dist/{http-method.decorator-ByWZb9DO.mjs.map → http-method.decorator-xCuGoZPC.mjs.map} +1 -1
  37. package/dist/i18n/index.d.mts +1 -1
  38. package/dist/i18n/index.mjs +3 -3
  39. package/dist/{i18n.module-DRQAZoSZ.mjs → i18n.module-JOPUNpvi.mjs} +3 -3
  40. package/dist/{i18n.module-DRQAZoSZ.mjs.map → i18n.module-JOPUNpvi.mjs.map} +1 -1
  41. package/dist/{index-B5JBRcWD.d.mts → index-BPJSBHhq.d.mts} +2 -2
  42. package/dist/{index-B5JBRcWD.d.mts.map → index-BPJSBHhq.d.mts.map} +1 -1
  43. package/dist/{index-B_JoEl3V.d.mts → index-C_t8VVQD.d.mts} +12 -1
  44. package/dist/index-C_t8VVQD.d.mts.map +1 -0
  45. package/dist/index.d.mts +3 -3
  46. package/dist/index.mjs +2 -2
  47. package/dist/{lazy-module-loader-Ib383jH_.d.mts → lazy-module-loader-DZQxOgRZ.d.mts} +2 -2
  48. package/dist/{lazy-module-loader-Ib383jH_.d.mts.map → lazy-module-loader-DZQxOgRZ.d.mts.map} +1 -1
  49. package/dist/{locale-path.service-D-dHiIPc.mjs → locale-path.service-DzA01Kvc.mjs} +3 -3
  50. package/dist/{locale-path.service-D-dHiIPc.mjs.map → locale-path.service-DzA01Kvc.mjs.map} +1 -1
  51. package/dist/{locale-url.service-C2EWmGdq.mjs → locale-url.service-DwTXZid3.mjs} +2 -2
  52. package/dist/{locale-url.service-C2EWmGdq.mjs.map → locale-url.service-DwTXZid3.mjs.map} +1 -1
  53. package/dist/logger/index.mjs +1 -1
  54. package/dist/module/index.d.mts +2 -2
  55. package/dist/module/index.mjs +2 -2
  56. package/dist/{module-registry-Dm-pqHd3.mjs → module-registry-MfWmniZ7.mjs} +4 -4
  57. package/dist/{module-registry-Dm-pqHd3.mjs.map → module-registry-MfWmniZ7.mjs.map} +1 -1
  58. package/dist/openapi/index.d.mts +2 -2
  59. package/dist/openapi/index.mjs +1 -1
  60. package/dist/{openapi-CstuTM8S.mjs → openapi-BKDwQKh-.mjs} +3 -3
  61. package/dist/{openapi-CstuTM8S.mjs.map → openapi-BKDwQKh-.mjs.map} +1 -1
  62. package/dist/{openapi.service-YhTiJ1bO.d.mts → openapi.service-CM40bnBB.d.mts} +2 -2
  63. package/dist/{openapi.service-YhTiJ1bO.d.mts.map → openapi.service-CM40bnBB.d.mts.map} +1 -1
  64. package/dist/quarry/index.d.mts +3 -3
  65. package/dist/quarry/index.mjs +1 -1
  66. package/dist/quarry/runner.d.mts +6 -6
  67. package/dist/quarry/runner.mjs +5 -5
  68. package/dist/{quarry-registry-CXg0RFXq.d.mts → quarry-registry-CsStq0DB.d.mts} +3 -3
  69. package/dist/{quarry-registry-CXg0RFXq.d.mts.map → quarry-registry-CsStq0DB.d.mts.map} +1 -1
  70. package/dist/{quarry.module-BuRPGMDm.mjs → quarry.module-BOgcNJMU.mjs} +3 -3
  71. package/dist/{quarry.module-BuRPGMDm.mjs.map → quarry.module-BOgcNJMU.mjs.map} +1 -1
  72. package/dist/queue/index.d.mts +1 -1
  73. package/dist/queue/index.mjs +2 -2
  74. package/dist/{queue.module-nddvxzCB.mjs → queue.module-DSAH88gZ.mjs} +3 -3
  75. package/dist/{queue.module-nddvxzCB.mjs.map → queue.module-DSAH88gZ.mjs.map} +1 -1
  76. package/dist/{r2-storage.provider-DCxQt9dD.mjs → r2-storage.provider-BJ4j23W6.mjs} +2 -2
  77. package/dist/{r2-storage.provider-DCxQt9dD.mjs.map → r2-storage.provider-BJ4j23W6.mjs.map} +1 -1
  78. package/dist/{rate-limit.decorator-BPAie_p3.mjs → rate-limit.decorator-B35jBEVD.mjs} +2 -2
  79. package/dist/{rate-limit.decorator-BPAie_p3.mjs.map → rate-limit.decorator-B35jBEVD.mjs.map} +1 -1
  80. package/dist/rate-limiter/index.d.mts +1 -1
  81. package/dist/rate-limiter/index.mjs +3 -3
  82. package/dist/{route-registration.service-D6vSwiKP.mjs → route-registration.service-HN8WgIis.mjs} +9 -9
  83. package/dist/{route-registration.service-D6vSwiKP.mjs.map → route-registration.service-HN8WgIis.mjs.map} +1 -1
  84. package/dist/{route-registry-CYqLp2Nj.mjs → route-registry-CtW26Suv.mjs} +3 -3
  85. package/dist/{route-registry-CYqLp2Nj.mjs.map → route-registry-CtW26Suv.mjs.map} +1 -1
  86. package/dist/router/index.d.mts +1 -1
  87. package/dist/router/index.mjs +13 -13
  88. package/dist/{router-CWGBD-Bg.mjs → router-OCnf4VB6.mjs} +13 -13
  89. package/dist/{router-CWGBD-Bg.mjs.map → router-OCnf4VB6.mjs.map} +1 -1
  90. package/dist/{router-resolver-D4YlPNlm.mjs → router-resolver-CMNuw-s0.mjs} +2 -2
  91. package/dist/{router-resolver-D4YlPNlm.mjs.map → router-resolver-CMNuw-s0.mjs.map} +1 -1
  92. package/dist/seeder/index.d.mts +2 -2
  93. package/dist/seeder/index.mjs +3 -3
  94. package/dist/{seeder-7ubkms-Y.mjs → seeder-DI-rUoZx.mjs} +4 -4
  95. package/dist/{seeder-7ubkms-Y.mjs.map → seeder-DI-rUoZx.mjs.map} +1 -1
  96. package/dist/{seeder-registry-CyUmKsJq.mjs → seeder-registry-B5FwBeCU.mjs} +3 -3
  97. package/dist/{seeder-registry-CyUmKsJq.mjs.map → seeder-registry-B5FwBeCU.mjs.map} +1 -1
  98. package/dist/{seeder.module-CYYwk3Qk.mjs → seeder.module-DaY0RYoB.mjs} +2 -2
  99. package/dist/{seeder.module-CYYwk3Qk.mjs.map → seeder.module-DaY0RYoB.mjs.map} +1 -1
  100. package/dist/storage/index.d.mts +1 -1
  101. package/dist/storage/index.mjs +2 -2
  102. package/dist/storage/providers/index.mjs +1 -1
  103. package/dist/{storage-MDZypIE9.mjs → storage-BE6-kzaN.mjs} +7 -7
  104. package/dist/{storage-MDZypIE9.mjs.map → storage-BE6-kzaN.mjs.map} +1 -1
  105. package/dist/{storage.error-Dnib4VHc.mjs → storage.error-Dai3rShJ.mjs} +2 -2
  106. package/dist/{storage.error-Dnib4VHc.mjs.map → storage.error-Dai3rShJ.mjs.map} +1 -1
  107. package/dist/{stratal-DwDJPY9N.d.mts → stratal-Bmc6WT3k.d.mts} +2 -2
  108. package/dist/{stratal-DwDJPY9N.d.mts.map → stratal-Bmc6WT3k.d.mts.map} +1 -1
  109. package/dist/{stratal-DL9M38_s.mjs → stratal-DtAj21uF.mjs} +21 -20
  110. package/dist/stratal-DtAj21uF.mjs.map +1 -0
  111. package/dist/{uri-h7Q8Jug9.mjs → uri-h6ITRpqr.mjs} +3 -3
  112. package/dist/{uri-h7Q8Jug9.mjs.map → uri-h6ITRpqr.mjs.map} +1 -1
  113. package/dist/{versioning.service-C6aHky8-.mjs → versioning.service-B4rtmgMQ.mjs} +3 -3
  114. package/dist/{versioning.service-C6aHky8-.mjs.map → versioning.service-B4rtmgMQ.mjs.map} +1 -1
  115. package/dist/websocket/index.d.mts +1 -1
  116. package/dist/websocket/index.mjs +1 -1
  117. package/dist/workers/index.d.mts +1 -1
  118. package/dist/workers/index.mjs +2 -2
  119. package/package.json +1 -1
  120. package/dist/di-DseMn-z9.mjs.map +0 -1
  121. package/dist/events-BXJGZjpG.mjs.map +0 -1
  122. package/dist/index-B_JoEl3V.d.mts.map +0 -1
  123. package/dist/stratal-DL9M38_s.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { n as SEEDER_INTERNALS, r as Seeder, t as isSeeder } from "../is-seeder-Gvh_AM71.mjs";
2
- import { n as SeederRegistry, r as SeederError, t as SEEDER_TOKENS } from "../seeder-registry-CyUmKsJq.mjs";
3
- import { n as DbSeedListCommand, t as DbSeedCommand } from "../seeder-7ubkms-Y.mjs";
4
- import { t as SeederModule } from "../seeder.module-CYYwk3Qk.mjs";
2
+ import { n as SeederRegistry, r as SeederError, t as SEEDER_TOKENS } from "../seeder-registry-B5FwBeCU.mjs";
3
+ import { n as DbSeedListCommand, t as DbSeedCommand } from "../seeder-DI-rUoZx.mjs";
4
+ import { t as SeederModule } from "../seeder.module-DaY0RYoB.mjs";
5
5
  export { DbSeedCommand, DbSeedListCommand, SEEDER_INTERNALS, SEEDER_TOKENS, Seeder, SeederError, SeederModule, SeederRegistry, isSeeder };
@@ -1,8 +1,8 @@
1
- import { d as inject } from "./di-DseMn-z9.mjs";
1
+ import { d as inject } from "./di-B0NXIdRF.mjs";
2
2
  import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
3
3
  import { t as Command } from "./command-BvmUAPPQ.mjs";
4
- import { t as SEEDER_TOKENS } from "./seeder-registry-CyUmKsJq.mjs";
5
- import "./seeder.module-CYYwk3Qk.mjs";
4
+ import { t as SEEDER_TOKENS } from "./seeder-registry-B5FwBeCU.mjs";
5
+ import "./seeder.module-DaY0RYoB.mjs";
6
6
  //#region src/seeder/commands/db-seed-list.command.ts
7
7
  let DbSeedListCommand = class DbSeedListCommand extends Command {
8
8
  seeders;
@@ -78,4 +78,4 @@ DbSeedCommand = __decorate([__decorateParam(0, inject(SEEDER_TOKENS.SeederRegist
78
78
  //#endregion
79
79
  export { DbSeedListCommand as n, DbSeedCommand as t };
80
80
 
81
- //# sourceMappingURL=seeder-7ubkms-Y.mjs.map
81
+ //# sourceMappingURL=seeder-DI-rUoZx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"seeder-7ubkms-Y.mjs","names":[],"sources":["../src/seeder/commands/db-seed-list.command.ts","../src/seeder/commands/db-seed.command.ts"],"sourcesContent":["import { inject } from '../../di'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedListCommand extends Command {\n static command = 'db:seed:list'\n static description = 'List available database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n handle(): undefined | number {\n const list = this.seeders.list()\n if (list.length === 0) {\n this.info('No seeders found')\n return 0\n }\n this.table(['Class'], list.map(s => [s.className]))\n\n return undefined\n }\n}\n","import { inject } from '../../di'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedCommand extends Command {\n static command = 'db:seed {names* : Seeder class names} {--a|all : Run all seeders} {--dry-run : Preview without executing}'\n static description = 'Run database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n async handle(): Promise<number | undefined> {\n const names = this.array('names')\n const all = this.boolean('all')\n const dryRun = this.boolean('dry-run')\n\n if (names.length > 0 && all) {\n this.warn(`Ignoring \"${names.join(', ')}\" because --all takes precedence`)\n }\n\n if (names.length === 0 && !all) {\n this.fail('Specify one or more seeder class names or use --all')\n return 1\n }\n\n if (dryRun) {\n if (all) {\n const list = this.seeders.list()\n this.info('Dry run — would execute:')\n for (const s of list) {\n this.info(` ${s.className}`)\n }\n } else {\n this.info('Dry run — would execute:')\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n this.info(` ${SeederClass.name}`)\n }\n }\n return 0\n }\n\n if (all) {\n await this.seeders.runAll()\n this.success('All seeders completed')\n } else {\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n await this.seeders.run(SeederClass)\n this.success(`Seeder \"${name}\" completed`)\n }\n }\n\n return 0\n }\n}\n"],"mappings":";;;;;;AAIO,IAAA,oBAAA,MAAM,0BAA0B,QAAQ;CAIa;CAH1D,OAAO,UAAU;CACjB,OAAO,cAAc;CAErB,YAAY,SAAuE;EACjF,MAAM;EADkD,KAAA,UAAA;CAE1D;CAEA,SAA6B;EAC3B,MAAM,OAAO,KAAK,QAAQ,KAAK;EAC/B,IAAI,KAAK,WAAW,GAAG;GACrB,KAAK,KAAK,kBAAkB;GAC5B,OAAO;EACT;EACA,KAAK,MAAM,CAAC,OAAO,GAAG,KAAK,KAAI,MAAK,CAAC,EAAE,SAAS,CAAC,CAAC;CAGpD;AACF;mDAde,OAAO,cAAc,cAAc,CAAA,CAAA,GAAA,iBAAA;;;ACJ3C,IAAA,gBAAA,MAAM,sBAAsB,QAAQ;CAIiB;CAH1D,OAAO,UAAU;CACjB,OAAO,cAAc;CAErB,YAAY,SAAuE;EACjF,MAAM;EADkD,KAAA,UAAA;CAE1D;CAEA,MAAM,SAAsC;EAC1C,MAAM,QAAQ,KAAK,MAAM,OAAO;EAChC,MAAM,MAAM,KAAK,QAAQ,KAAK;EAC9B,MAAM,SAAS,KAAK,QAAQ,SAAS;EAErC,IAAI,MAAM,SAAS,KAAK,KACtB,KAAK,KAAK,aAAa,MAAM,KAAK,IAAI,EAAE,iCAAiC;EAG3E,IAAI,MAAM,WAAW,KAAK,CAAC,KAAK;GAC9B,KAAK,KAAK,qDAAqD;GAC/D,OAAO;EACT;EAEA,IAAI,QAAQ;GACV,IAAI,KAAK;IACP,MAAM,OAAO,KAAK,QAAQ,KAAK;IAC/B,KAAK,KAAK,0BAA0B;IACpC,KAAK,MAAM,KAAK,MACd,KAAK,KAAK,KAAK,EAAE,WAAW;GAEhC,OAAO;IACL,KAAK,KAAK,0BAA0B;IACpC,KAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,cAAc,KAAK,QAAQ,KAAK,IAAI;KAC1C,IAAI,CAAC,aAAa;MAChB,KAAK,KAAK,WAAW,KAAK,YAAY;MACtC,OAAO;KACT;KACA,KAAK,KAAK,KAAK,YAAY,MAAM;IACnC;GACF;GACA,OAAO;EACT;EAEA,IAAI,KAAK;GACP,MAAM,KAAK,QAAQ,OAAO;GAC1B,KAAK,QAAQ,uBAAuB;EACtC,OACE,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,cAAc,KAAK,QAAQ,KAAK,IAAI;GAC1C,IAAI,CAAC,aAAa;IAChB,KAAK,KAAK,WAAW,KAAK,YAAY;IACtC,OAAO;GACT;GACA,MAAM,KAAK,QAAQ,IAAI,WAAW;GAClC,KAAK,QAAQ,WAAW,KAAK,YAAY;EAC3C;EAGF,OAAO;CACT;AACF;+CAxDe,OAAO,cAAc,cAAc,CAAA,CAAA,GAAA,aAAA"}
1
+ {"version":3,"file":"seeder-DI-rUoZx.mjs","names":[],"sources":["../src/seeder/commands/db-seed-list.command.ts","../src/seeder/commands/db-seed.command.ts"],"sourcesContent":["import { inject } from '../../di'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedListCommand extends Command {\n static command = 'db:seed:list'\n static description = 'List available database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n handle(): undefined | number {\n const list = this.seeders.list()\n if (list.length === 0) {\n this.info('No seeders found')\n return 0\n }\n this.table(['Class'], list.map(s => [s.className]))\n\n return undefined\n }\n}\n","import { inject } from '../../di'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedCommand extends Command {\n static command = 'db:seed {names* : Seeder class names} {--a|all : Run all seeders} {--dry-run : Preview without executing}'\n static description = 'Run database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n async handle(): Promise<number | undefined> {\n const names = this.array('names')\n const all = this.boolean('all')\n const dryRun = this.boolean('dry-run')\n\n if (names.length > 0 && all) {\n this.warn(`Ignoring \"${names.join(', ')}\" because --all takes precedence`)\n }\n\n if (names.length === 0 && !all) {\n this.fail('Specify one or more seeder class names or use --all')\n return 1\n }\n\n if (dryRun) {\n if (all) {\n const list = this.seeders.list()\n this.info('Dry run — would execute:')\n for (const s of list) {\n this.info(` ${s.className}`)\n }\n } else {\n this.info('Dry run — would execute:')\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n this.info(` ${SeederClass.name}`)\n }\n }\n return 0\n }\n\n if (all) {\n await this.seeders.runAll()\n this.success('All seeders completed')\n } else {\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n await this.seeders.run(SeederClass)\n this.success(`Seeder \"${name}\" completed`)\n }\n }\n\n return 0\n }\n}\n"],"mappings":";;;;;;AAIO,IAAA,oBAAA,MAAM,0BAA0B,QAAQ;CAIa;CAH1D,OAAO,UAAU;CACjB,OAAO,cAAc;CAErB,YAAY,SAAuE;EACjF,MAAM;EADkD,KAAA,UAAA;CAE1D;CAEA,SAA6B;EAC3B,MAAM,OAAO,KAAK,QAAQ,KAAK;EAC/B,IAAI,KAAK,WAAW,GAAG;GACrB,KAAK,KAAK,kBAAkB;GAC5B,OAAO;EACT;EACA,KAAK,MAAM,CAAC,OAAO,GAAG,KAAK,KAAI,MAAK,CAAC,EAAE,SAAS,CAAC,CAAC;CAGpD;AACF;mDAde,OAAO,cAAc,cAAc,CAAA,CAAA,GAAA,iBAAA;;;ACJ3C,IAAA,gBAAA,MAAM,sBAAsB,QAAQ;CAIiB;CAH1D,OAAO,UAAU;CACjB,OAAO,cAAc;CAErB,YAAY,SAAuE;EACjF,MAAM;EADkD,KAAA,UAAA;CAE1D;CAEA,MAAM,SAAsC;EAC1C,MAAM,QAAQ,KAAK,MAAM,OAAO;EAChC,MAAM,MAAM,KAAK,QAAQ,KAAK;EAC9B,MAAM,SAAS,KAAK,QAAQ,SAAS;EAErC,IAAI,MAAM,SAAS,KAAK,KACtB,KAAK,KAAK,aAAa,MAAM,KAAK,IAAI,EAAE,iCAAiC;EAG3E,IAAI,MAAM,WAAW,KAAK,CAAC,KAAK;GAC9B,KAAK,KAAK,qDAAqD;GAC/D,OAAO;EACT;EAEA,IAAI,QAAQ;GACV,IAAI,KAAK;IACP,MAAM,OAAO,KAAK,QAAQ,KAAK;IAC/B,KAAK,KAAK,0BAA0B;IACpC,KAAK,MAAM,KAAK,MACd,KAAK,KAAK,KAAK,EAAE,WAAW;GAEhC,OAAO;IACL,KAAK,KAAK,0BAA0B;IACpC,KAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,cAAc,KAAK,QAAQ,KAAK,IAAI;KAC1C,IAAI,CAAC,aAAa;MAChB,KAAK,KAAK,WAAW,KAAK,YAAY;MACtC,OAAO;KACT;KACA,KAAK,KAAK,KAAK,YAAY,MAAM;IACnC;GACF;GACA,OAAO;EACT;EAEA,IAAI,KAAK;GACP,MAAM,KAAK,QAAQ,OAAO;GAC1B,KAAK,QAAQ,uBAAuB;EACtC,OACE,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,cAAc,KAAK,QAAQ,KAAK,IAAI;GAC1C,IAAI,CAAC,aAAa;IAChB,KAAK,KAAK,WAAW,KAAK,YAAY;IACtC,OAAO;GACT;GACA,MAAM,KAAK,QAAQ,IAAI,WAAW;GAClC,KAAK,QAAQ,WAAW,KAAK,YAAY;EAC3C;EAGF,OAAO;CACT;AACF;+CAxDe,OAAO,cAAc,cAAc,CAAA,CAAA,GAAA,aAAA"}
@@ -1,7 +1,7 @@
1
- import { d as inject, r as DI_TOKENS, s as Singleton } from "./di-DseMn-z9.mjs";
1
+ import { d as inject, r as DI_TOKENS, s as Singleton } from "./di-B0NXIdRF.mjs";
2
2
  import { a as ApplicationError } from "./container-storage-BmOJ4_Na.mjs";
3
3
  import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
4
- import "./errors-mXYxG0XB.mjs";
4
+ import "./errors-EUtwVfNm.mjs";
5
5
  import { n as SEEDER_INTERNALS } from "./is-seeder-Gvh_AM71.mjs";
6
6
  //#region src/seeder/seeder.error.ts
7
7
  var SeederError = class extends ApplicationError {};
@@ -54,4 +54,4 @@ SeederRegistry = __decorate([Singleton(SEEDER_TOKENS.SeederRegistry), __decorate
54
54
  //#endregion
55
55
  export { SeederRegistry as n, SeederError as r, SEEDER_TOKENS as t };
56
56
 
57
- //# sourceMappingURL=seeder-registry-CyUmKsJq.mjs.map
57
+ //# sourceMappingURL=seeder-registry-B5FwBeCU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"seeder-registry-CyUmKsJq.mjs","names":[],"sources":["../src/seeder/seeder.error.ts","../src/seeder/seeder-registry.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class SeederError extends ApplicationError {}\n","import type { Application } from '../application'\nimport type { Container } from '../di/container'\nimport { inject, Singleton } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { Constructor } from '../types'\nimport { SeederError } from './seeder.error'\nimport { type Seeder, SEEDER_INTERNALS } from './seeder'\n\nexport const SEEDER_TOKENS = {\n SeederRegistry: Symbol.for('stratal:seeders:registry'),\n} as const\n\n@Singleton(SEEDER_TOKENS.SeederRegistry)\nexport class SeederRegistry {\n private seeders = new Set<Constructor<Seeder>>()\n private nameIndex = new Map<string, Constructor<Seeder>>()\n\n constructor(@inject(DI_TOKENS.Application) private app: Application) { }\n\n register(SeederClass: Constructor<Seeder>): void {\n const existing = this.nameIndex.get(SeederClass.name)\n if (existing && existing !== SeederClass) {\n throw new SeederError(`Seeder name collision: \"${SeederClass.name}\" is already registered`)\n }\n this.seeders.add(SeederClass)\n this.nameIndex.set(SeederClass.name, SeederClass)\n }\n\n async run(SeederClass: Constructor<Seeder>, options?: { container?: Container }): Promise<void> {\n if (!this.seeders.has(SeederClass)) {\n throw new SeederError(`Seeder \"${SeederClass.name}\" is not registered`)\n }\n\n const execute = async (container: Container) => {\n const seeder = container.resolve<Seeder>(SeederClass)\n seeder[SEEDER_INTERNALS] = {\n run: (cls) => this.run(cls, { container }),\n container,\n }\n await seeder.run()\n }\n\n if (options?.container) {\n await execute(options.container)\n } else {\n const mockContext = this.app.createMockRouterContext('en')\n await this.app.container.runInRequestScope(mockContext, execute)\n }\n }\n\n async runAll(options?: { container?: Container }): Promise<void> {\n for (const SeederClass of this.seeders) {\n await this.run(SeederClass, options)\n }\n }\n\n find(name: string): Constructor<Seeder> | undefined {\n return this.nameIndex.get(name)\n }\n\n has(SeederClass: Constructor<Seeder>): boolean {\n return this.seeders.has(SeederClass)\n }\n\n list(): { className: string }[] {\n return [...this.seeders].map(cls => ({ className: cls.name }))\n }\n}\n"],"mappings":";;;;;;AAEA,IAAa,cAAb,cAAiC,iBAAiB,CAAC;;;ACMnD,MAAa,gBAAgB,EAC3B,gBAAgB,OAAO,IAAI,0BAA0B,EACvD;AAGO,IAAA,iBAAA,MAAM,eAAe;CAIyB;CAHnD,0BAAkB,IAAI,IAAyB;CAC/C,4BAAoB,IAAI,IAAiC;CAEzD,YAAY,KAAyD;EAAlB,KAAA,MAAA;CAAoB;CAEvE,SAAS,aAAwC;EAC/C,MAAM,WAAW,KAAK,UAAU,IAAI,YAAY,IAAI;EACpD,IAAI,YAAY,aAAa,aAC3B,MAAM,IAAI,YAAY,2BAA2B,YAAY,KAAK,wBAAwB;EAE5F,KAAK,QAAQ,IAAI,WAAW;EAC5B,KAAK,UAAU,IAAI,YAAY,MAAM,WAAW;CAClD;CAEA,MAAM,IAAI,aAAkC,SAAoD;EAC9F,IAAI,CAAC,KAAK,QAAQ,IAAI,WAAW,GAC/B,MAAM,IAAI,YAAY,WAAW,YAAY,KAAK,oBAAoB;EAGxE,MAAM,UAAU,OAAO,cAAyB;GAC9C,MAAM,SAAS,UAAU,QAAgB,WAAW;GACpD,OAAO,oBAAoB;IACzB,MAAM,QAAQ,KAAK,IAAI,KAAK,EAAE,UAAU,CAAC;IACzC;GACF;GACA,MAAM,OAAO,IAAI;EACnB;EAEA,IAAI,SAAS,WACX,MAAM,QAAQ,QAAQ,SAAS;OAC1B;GACL,MAAM,cAAc,KAAK,IAAI,wBAAwB,IAAI;GACzD,MAAM,KAAK,IAAI,UAAU,kBAAkB,aAAa,OAAO;EACjE;CACF;CAEA,MAAM,OAAO,SAAoD;EAC/D,KAAK,MAAM,eAAe,KAAK,SAC7B,MAAM,KAAK,IAAI,aAAa,OAAO;CAEvC;CAEA,KAAK,MAA+C;EAClD,OAAO,KAAK,UAAU,IAAI,IAAI;CAChC;CAEA,IAAI,aAA2C;EAC7C,OAAO,KAAK,QAAQ,IAAI,WAAW;CACrC;CAEA,OAAgC;EAC9B,OAAO,CAAC,GAAG,KAAK,OAAO,EAAE,KAAI,SAAQ,EAAE,WAAW,IAAI,KAAK,EAAE;CAC/D;AACF;6BAvDC,UAAU,cAAc,cAAc,GAAA,gBAAA,GAKxB,OAAO,UAAU,WAAW,CAAA,CAAA,GAAA,cAAA"}
1
+ {"version":3,"file":"seeder-registry-B5FwBeCU.mjs","names":[],"sources":["../src/seeder/seeder.error.ts","../src/seeder/seeder-registry.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class SeederError extends ApplicationError {}\n","import type { Application } from '../application'\nimport type { Container } from '../di/container'\nimport { inject, Singleton } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { Constructor } from '../types'\nimport { SeederError } from './seeder.error'\nimport { type Seeder, SEEDER_INTERNALS } from './seeder'\n\nexport const SEEDER_TOKENS = {\n SeederRegistry: Symbol.for('stratal:seeders:registry'),\n} as const\n\n@Singleton(SEEDER_TOKENS.SeederRegistry)\nexport class SeederRegistry {\n private seeders = new Set<Constructor<Seeder>>()\n private nameIndex = new Map<string, Constructor<Seeder>>()\n\n constructor(@inject(DI_TOKENS.Application) private app: Application) { }\n\n register(SeederClass: Constructor<Seeder>): void {\n const existing = this.nameIndex.get(SeederClass.name)\n if (existing && existing !== SeederClass) {\n throw new SeederError(`Seeder name collision: \"${SeederClass.name}\" is already registered`)\n }\n this.seeders.add(SeederClass)\n this.nameIndex.set(SeederClass.name, SeederClass)\n }\n\n async run(SeederClass: Constructor<Seeder>, options?: { container?: Container }): Promise<void> {\n if (!this.seeders.has(SeederClass)) {\n throw new SeederError(`Seeder \"${SeederClass.name}\" is not registered`)\n }\n\n const execute = async (container: Container) => {\n const seeder = container.resolve<Seeder>(SeederClass)\n seeder[SEEDER_INTERNALS] = {\n run: (cls) => this.run(cls, { container }),\n container,\n }\n await seeder.run()\n }\n\n if (options?.container) {\n await execute(options.container)\n } else {\n const mockContext = this.app.createMockRouterContext('en')\n await this.app.container.runInRequestScope(mockContext, execute)\n }\n }\n\n async runAll(options?: { container?: Container }): Promise<void> {\n for (const SeederClass of this.seeders) {\n await this.run(SeederClass, options)\n }\n }\n\n find(name: string): Constructor<Seeder> | undefined {\n return this.nameIndex.get(name)\n }\n\n has(SeederClass: Constructor<Seeder>): boolean {\n return this.seeders.has(SeederClass)\n }\n\n list(): { className: string }[] {\n return [...this.seeders].map(cls => ({ className: cls.name }))\n }\n}\n"],"mappings":";;;;;;AAEA,IAAa,cAAb,cAAiC,iBAAiB,CAAC;;;ACMnD,MAAa,gBAAgB,EAC3B,gBAAgB,OAAO,IAAI,0BAA0B,EACvD;AAGO,IAAA,iBAAA,MAAM,eAAe;CAIyB;CAHnD,0BAAkB,IAAI,IAAyB;CAC/C,4BAAoB,IAAI,IAAiC;CAEzD,YAAY,KAAyD;EAAlB,KAAA,MAAA;CAAoB;CAEvE,SAAS,aAAwC;EAC/C,MAAM,WAAW,KAAK,UAAU,IAAI,YAAY,IAAI;EACpD,IAAI,YAAY,aAAa,aAC3B,MAAM,IAAI,YAAY,2BAA2B,YAAY,KAAK,wBAAwB;EAE5F,KAAK,QAAQ,IAAI,WAAW;EAC5B,KAAK,UAAU,IAAI,YAAY,MAAM,WAAW;CAClD;CAEA,MAAM,IAAI,aAAkC,SAAoD;EAC9F,IAAI,CAAC,KAAK,QAAQ,IAAI,WAAW,GAC/B,MAAM,IAAI,YAAY,WAAW,YAAY,KAAK,oBAAoB;EAGxE,MAAM,UAAU,OAAO,cAAyB;GAC9C,MAAM,SAAS,UAAU,QAAgB,WAAW;GACpD,OAAO,oBAAoB;IACzB,MAAM,QAAQ,KAAK,IAAI,KAAK,EAAE,UAAU,CAAC;IACzC;GACF;GACA,MAAM,OAAO,IAAI;EACnB;EAEA,IAAI,SAAS,WACX,MAAM,QAAQ,QAAQ,SAAS;OAC1B;GACL,MAAM,cAAc,KAAK,IAAI,wBAAwB,IAAI;GACzD,MAAM,KAAK,IAAI,UAAU,kBAAkB,aAAa,OAAO;EACjE;CACF;CAEA,MAAM,OAAO,SAAoD;EAC/D,KAAK,MAAM,eAAe,KAAK,SAC7B,MAAM,KAAK,IAAI,aAAa,OAAO;CAEvC;CAEA,KAAK,MAA+C;EAClD,OAAO,KAAK,UAAU,IAAI,IAAI;CAChC;CAEA,IAAI,aAA2C;EAC7C,OAAO,KAAK,QAAQ,IAAI,WAAW;CACrC;CAEA,OAAgC;EAC9B,OAAO,CAAC,GAAG,KAAK,OAAO,EAAE,KAAI,SAAQ,EAAE,WAAW,IAAI,KAAK,EAAE;CAC/D;AACF;6BAvDC,UAAU,cAAc,cAAc,GAAA,gBAAA,GAKxB,OAAO,UAAU,WAAW,CAAA,CAAA,GAAA,cAAA"}
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./chunk-BBjsoOtd.mjs";
2
2
  import { t as __decorate } from "./decorate-CuAoSZvs.mjs";
3
3
  import { n as Module } from "./module.decorator-CYHY6pG5.mjs";
4
- import { n as SeederRegistry, t as SEEDER_TOKENS } from "./seeder-registry-CyUmKsJq.mjs";
4
+ import { n as SeederRegistry, t as SEEDER_TOKENS } from "./seeder-registry-B5FwBeCU.mjs";
5
5
  //#region src/seeder/seeder.module.ts
6
6
  var seeder_module_exports = /* @__PURE__ */ __exportAll({ SeederModule: () => SeederModule });
7
7
  let SeederModule = class SeederModule {};
@@ -12,4 +12,4 @@ SeederModule = __decorate([Module({ providers: [{
12
12
  //#endregion
13
13
  export { seeder_module_exports as n, SeederModule as t };
14
14
 
15
- //# sourceMappingURL=seeder.module-CYYwk3Qk.mjs.map
15
+ //# sourceMappingURL=seeder.module-DaY0RYoB.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"seeder.module-CYYwk3Qk.mjs","names":[],"sources":["../src/seeder/seeder.module.ts"],"sourcesContent":["import { Module } from '../module/module.decorator'\nimport { SEEDER_TOKENS, SeederRegistry } from './seeder-registry'\n\n/**\n * Registers the seeder registry (`SEEDER_TOKENS.SeederRegistry`).\n *\n * Eager: resolved synchronously at bootstrap (`registerSeeders`) and by the\n * test harness (`@stratal/testing`), so it cannot be lazily loaded.\n * {@link SeederRegistry} injects the `Application` via `DI_TOKENS.Application`.\n */\n@Module({\n providers: [\n { provide: SEEDER_TOKENS.SeederRegistry, useClass: SeederRegistry },\n ],\n})\nexport class SeederModule { }\n"],"mappings":";;;;;;AAeO,IAAA,eAAA,MAAM,aAAa,CAAE;2BAL3B,OAAO,EACN,WAAW,CACT;CAAE,SAAS,cAAc;CAAgB,UAAU;AAAe,CACpE,EACF,CAAC,CAAA,GAAA,YAAA"}
1
+ {"version":3,"file":"seeder.module-DaY0RYoB.mjs","names":[],"sources":["../src/seeder/seeder.module.ts"],"sourcesContent":["import { Module } from '../module/module.decorator'\nimport { SEEDER_TOKENS, SeederRegistry } from './seeder-registry'\n\n/**\n * Registers the seeder registry (`SEEDER_TOKENS.SeederRegistry`).\n *\n * Eager: resolved synchronously at bootstrap (`registerSeeders`) and by the\n * test harness (`@stratal/testing`), so it cannot be lazily loaded.\n * {@link SeederRegistry} injects the `Application` via `DI_TOKENS.Application`.\n */\n@Module({\n providers: [\n { provide: SEEDER_TOKENS.SeederRegistry, useClass: SeederRegistry },\n ],\n})\nexport class SeederModule { }\n"],"mappings":";;;;;;AAeO,IAAA,eAAA,MAAM,aAAa,CAAE;2BAL3B,OAAO,EACN,WAAW,CACT;CAAE,SAAS,cAAc;CAAgB,UAAU;AAAe,CACpE,EACF,CAAC,CAAA,GAAA,YAAA"}
@@ -1,4 +1,4 @@
1
- import { Dn as AsyncModuleOptions, Dr as ApplicationError, c as HttpException, kn as DynamicModule, rt as RouterContext } from "../index-B_JoEl3V.mjs";
1
+ import { Dn as AsyncModuleOptions, Dr as ApplicationError, c as HttpException, kn as DynamicModule, rt as RouterContext } from "../index-C_t8VVQD.mjs";
2
2
  import { t as StratalEnv } from "../env-ug22bJj7.mjs";
3
3
  import { _ as StorageEntry, a as uploadResultSchema, c as getPresignedUrlInputSchema, d as fileExistsInputSchema, f as DownloadResult, g as StorageConfig, h as PresignedUrlConfig, i as UploadResult, l as presignedUrlResultSchema, m as deleteFileInputSchema, n as StreamingBlobPayloadInputTypes, o as GetPresignedUrlInput, p as DeleteFileInput, r as UploadOptions, s as PresignedUrlResult, t as IStorageProvider, u as FileExistsInput, v as StorageRouteConfig } from "../storage-provider.interface-ClUwxz4S.mjs";
4
4
 
@@ -1,3 +1,3 @@
1
- import { t as StorageError } from "../storage.error-Dnib4VHc.mjs";
2
- import { a as presignedUrlResultSchema, c as StorageController, d as StorageService, f as StorageManagerService, i as getPresignedUrlInputSchema, l as FileNotFoundError, n as FileTooLargeError, o as fileExistsInputSchema, p as STORAGE_TOKENS, r as uploadResultSchema, s as deleteFileInputSchema, t as InvalidFileTypeError, u as StorageModule } from "../storage-MDZypIE9.mjs";
1
+ import { t as StorageError } from "../storage.error-Dai3rShJ.mjs";
2
+ import { a as presignedUrlResultSchema, c as StorageController, d as StorageService, f as StorageManagerService, i as getPresignedUrlInputSchema, l as FileNotFoundError, n as FileTooLargeError, o as fileExistsInputSchema, p as STORAGE_TOKENS, r as uploadResultSchema, s as deleteFileInputSchema, t as InvalidFileTypeError, u as StorageModule } from "../storage-BE6-kzaN.mjs";
3
3
  export { FileNotFoundError, FileTooLargeError, InvalidFileTypeError, STORAGE_TOKENS, StorageController, StorageError, StorageManagerService, StorageModule, StorageService, deleteFileInputSchema, fileExistsInputSchema, getPresignedUrlInputSchema, presignedUrlResultSchema, uploadResultSchema };
@@ -1,2 +1,2 @@
1
- import { t as R2StorageProvider } from "../../r2-storage.provider-DCxQt9dD.mjs";
1
+ import { t as R2StorageProvider } from "../../r2-storage.provider-BJ4j23W6.mjs";
2
2
  export { R2StorageProvider };
@@ -1,13 +1,13 @@
1
- import { d as inject, o as Request, r as DI_TOKENS, s as Singleton } from "./di-DseMn-z9.mjs";
1
+ import { d as inject, o as Request, r as DI_TOKENS, s as Singleton } from "./di-B0NXIdRF.mjs";
2
2
  import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
3
- import { u as HttpException } from "./errors-mXYxG0XB.mjs";
3
+ import { u as HttpException } from "./errors-EUtwVfNm.mjs";
4
4
  import { n as Module } from "./module.decorator-CYHY6pG5.mjs";
5
5
  import "./module/index.mjs";
6
- import { t as StorageError } from "./storage.error-Dnib4VHc.mjs";
6
+ import { t as StorageError } from "./storage.error-Dai3rShJ.mjs";
7
7
  import { r as z } from "./zod-eKqqhZ5_.mjs";
8
8
  import { t as withZodI18n } from "./validation-CpOjviyT.mjs";
9
- import { t as Controller } from "./controller.decorator-C5UVeJS3.mjs";
10
- import { n as Delete, o as Put, r as Get } from "./http-method.decorator-ByWZb9DO.mjs";
9
+ import { t as Controller } from "./controller.decorator-DKeZ71aP.mjs";
10
+ import { n as Delete, o as Put, r as Get } from "./http-method.decorator-xCuGoZPC.mjs";
11
11
  //#region src/storage/storage.tokens.ts
12
12
  /**
13
13
  * Dependency injection tokens for the Storage module
@@ -68,7 +68,7 @@ let StorageManagerService = class StorageManagerService {
68
68
  * @returns Storage provider instance
69
69
  */
70
70
  async createProvider(config) {
71
- const { R2StorageProvider } = await import("./r2-storage.provider-DCxQt9dD.mjs").then((n) => n.n);
71
+ const { R2StorageProvider } = await import("./r2-storage.provider-BJ4j23W6.mjs").then((n) => n.n);
72
72
  const bucket = this.env[config.binding];
73
73
  if (!bucket) throw new StorageError(`R2 binding "${config.binding}" was not found in the environment`);
74
74
  return new R2StorageProvider(config, bucket, this.env, this.options.route);
@@ -493,4 +493,4 @@ var InvalidFileTypeError = class extends HttpException {
493
493
  //#endregion
494
494
  export { presignedUrlResultSchema as a, StorageController as c, StorageService as d, StorageManagerService as f, getPresignedUrlInputSchema as i, FileNotFoundError as l, FileTooLargeError as n, fileExistsInputSchema as o, STORAGE_TOKENS as p, uploadResultSchema as r, deleteFileInputSchema as s, InvalidFileTypeError as t, StorageModule as u };
495
495
 
496
- //# sourceMappingURL=storage-MDZypIE9.mjs.map
496
+ //# sourceMappingURL=storage-BE6-kzaN.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-MDZypIE9.mjs","names":[],"sources":["../src/storage/storage.tokens.ts","../src/storage/services/storage-manager.service.ts","../src/storage/services/storage.service.ts","../src/storage/storage.module.ts","../src/storage/errors/file-not-found.error.ts","../src/storage/controllers/storage.controller.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts","../src/storage/errors/file-too-large.error.ts","../src/storage/errors/invalid-file-type.error.ts"],"sourcesContent":["/**\n * Dependency injection tokens for the Storage module\n * Using Symbol-based tokens to avoid magic strings\n */\nexport const STORAGE_TOKENS = {\n Options: Symbol.for('stratal:storage:options'),\n StorageService: Symbol.for('stratal:storage:service'),\n StorageManager: Symbol.for('stratal:storage:manager'),\n} as const\n","import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { StorageError } from '../storage.error'\nimport type { IStorageProvider } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig, StorageEntry } from '../types'\n\n/**\n * Storage Manager Service\n * Manages multiple storage providers (one per disk)\n * Handles lazy initialization and caching of R2 providers\n */\n@Singleton(STORAGE_TOKENS.StorageManager)\nexport class StorageManagerService {\n private readonly providers = new Map<string, IStorageProvider>()\n private readonly creationPromises = new Map<string, Promise<IStorageProvider>>()\n private readonly diskConfigs = new Map<string, StorageEntry>()\n\n constructor(\n @inject(STORAGE_TOKENS.Options)\n private readonly options: StorageConfig,\n @inject(DI_TOKENS.CloudflareEnv)\n private readonly env: StratalEnv\n ) {\n this.initializeDiskConfigs()\n }\n\n /**\n * Initialize disk configurations from options\n */\n private initializeDiskConfigs(): void {\n for (const entry of this.options.storage) {\n this.diskConfigs.set(entry.disk, entry)\n }\n }\n\n /**\n * Get provider for a specific disk\n * Lazily initializes provider on first access\n * @param diskName - Name of the disk\n * @returns Storage provider instance\n */\n async getProvider(diskName: string): Promise<IStorageProvider> {\n // Return cached provider if exists\n const cached = this.providers.get(diskName)\n if (cached) {\n return cached\n }\n\n // Return in-flight creation promise to deduplicate concurrent calls\n const inflight = this.creationPromises.get(diskName)\n if (inflight) {\n return inflight\n }\n\n // Get disk configuration\n const diskConfig = this.diskConfigs.get(diskName)\n if (!diskConfig) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n // Create provider and deduplicate concurrent calls\n const promise = this.createProvider(diskConfig).then((provider) => {\n this.providers.set(diskName, provider)\n this.creationPromises.delete(diskName)\n return provider\n }).catch((error: unknown) => {\n this.creationPromises.delete(diskName)\n throw error\n })\n\n this.creationPromises.set(diskName, promise)\n\n return promise\n }\n\n /**\n * Create an R2 provider instance\n * Dynamically imports R2StorageProvider to support code splitting\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n const { R2StorageProvider } = await import('../providers/r2-storage.provider')\n const bucket = this.env[config.binding as keyof StratalEnv] as unknown as R2Bucket | undefined\n if (!bucket) {\n throw new StorageError(`R2 binding \"${config.binding}\" was not found in the environment`)\n }\n return new R2StorageProvider(config, bucket, this.env, this.options.route)\n }\n\n /**\n * Get disk configuration\n * @param diskName - Name of the disk\n * @returns Storage entry configuration\n */\n getDiskConfig(diskName: string): StorageEntry {\n const config = this.diskConfigs.get(diskName)\n if (!config) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n return config\n }\n\n /**\n * Check if a disk exists\n * @param diskName - Name of the disk\n * @returns True if disk exists, false otherwise\n */\n hasDisk(diskName: string): boolean {\n return this.diskConfigs.has(diskName)\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return Array.from(this.diskConfigs.keys())\n }\n}\n","import { inject } from '../../di'\nimport { Request } from '../../di/decorators'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { StorageError } from '../storage.error'\nimport type { StreamingBlobPayloadInputTypes } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig } from '../types'\nimport { type StorageManagerService } from './storage-manager.service'\n\n/**\n * Storage Service\n *\n * Main facade for storage operations.\n * Request-scoped for proper isolation.\n *\n * @example\n * ```typescript\n * @inject(STORAGE_TOKENS.StorageService)\n * private readonly storage: StorageService\n *\n * await this.storage.upload(file, 'documents/report.pdf')\n * ```\n */\n@Request(STORAGE_TOKENS.StorageService)\nexport class StorageService {\n constructor(\n @inject(STORAGE_TOKENS.StorageManager)\n protected readonly storageManager: StorageManagerService,\n @inject(STORAGE_TOKENS.Options)\n protected readonly options: StorageConfig\n ) { }\n\n /**\n * Upload content to storage\n * @param body - Content to upload (stream, buffer, or string)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options including size and mime type\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async upload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: UploadOptions,\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.upload(body, fullPath, options)\n }\n\n /**\n * Download a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Download result with stream and metadata\n */\n async download(relativePath: string, disk?: string): Promise<DownloadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.download(fullPath)\n }\n\n /**\n * Delete a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n */\n async delete(relativePath: string, disk?: string): Promise<void> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n await provider.delete(fullPath)\n }\n\n /**\n * Check if a file exists in storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns True if file exists, false otherwise\n */\n async exists(relativePath: string, disk?: string): Promise<boolean> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.exists(fullPath)\n }\n\n /**\n * Generate a presigned download URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDownloadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'GET', expiresIn, disk)\n }\n\n /**\n * Generate a presigned upload URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedUploadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'PUT', expiresIn, disk)\n }\n\n /**\n * Generate a presigned delete URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDeleteUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'DELETE', expiresIn, disk)\n }\n\n /**\n * Generate a presigned URL for any method\n * @param relativePath - Relative path within the disk\n * @param method - HTTP method (GET, PUT, DELETE, HEAD)\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n protected async getPresignedUrl(\n relativePath: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n const validatedExpiresIn = this.validateExpiresIn(expiresIn)\n\n return provider.getPresignedUrl(fullPath, method, validatedExpiresIn)\n }\n\n /**\n * Resolve disk name (use default if not provided)\n * @param disk - Optional disk name\n * @returns Resolved disk name\n */\n protected resolveDisk(disk?: string): string {\n const diskName = disk ?? this.options.defaultStorageDisk\n\n if (!this.storageManager.hasDisk(diskName)) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n return diskName\n }\n\n /**\n * Build full path with disk root and path template substitution\n * @param relativePath - Relative path within the disk\n * @param diskName - Name of the disk\n * @returns Full path including disk root\n */\n protected buildFullPath(relativePath: string, diskName: string): string {\n const diskConfig = this.storageManager.getDiskConfig(diskName)\n let root = diskConfig.root || ''\n\n // Substitute template variables\n root = this.substituteTemplateVariables(root)\n\n // Combine root and relative path\n const fullPath = `${root}/${relativePath}`.replace(/\\/+/g, '/').replace(/^\\//, '')\n\n return fullPath\n }\n\n /**\n * Substitute template variables in path\n * Override this method in subclasses to add custom substitutions\n *\n * @param path - Path with template variables\n * @returns Path with substituted variables\n */\n protected substituteTemplateVariables(path: string): string {\n let result = path\n\n // Substitute {date}, {year}, {month}\n const now = new Date()\n result = result.replace(/{date}/g, now.toISOString().split('T')[0])\n result = result.replace(/{year}/g, now.getFullYear().toString())\n result = result.replace(/{month}/g, (now.getMonth() + 1).toString().padStart(2, '0'))\n\n return result\n }\n\n /**\n * Validate expiry time for presigned URLs\n * @param expiresIn - Optional expiry time in seconds\n * @returns Validated expiry time\n */\n protected validateExpiresIn(expiresIn?: number): number {\n const presignedUrlConfig = this.options.presignedUrl\n const validatedExpiresIn = expiresIn ?? presignedUrlConfig.defaultExpiry\n\n const minExpiry = 1\n const maxExpiry = presignedUrlConfig.maxExpiry\n\n if (validatedExpiresIn < minExpiry || validatedExpiresIn > maxExpiry) {\n throw new StorageError(`Presigned URL expiry ${validatedExpiresIn}s is out of range (${minExpiry}–${maxExpiry}s)`)\n }\n\n return validatedExpiresIn\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return this.storageManager.getAvailableDisks()\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses multipart upload under the hood - handles retries and large files\n *\n * Use this method when:\n * - Content-Length is unknown or unreliable\n * - Uploading from streams that can't be rewound\n * - Need automatic retry handling for transient failures\n *\n * @param body - Content to upload (stream or buffer)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options (mimeType required, size optional)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: Omit<UploadOptions, 'size'> & { size?: number },\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.chunkedUpload(body, fullPath, options)\n }\n}\n","/**\n * Storage Module\n * Provides file storage capabilities using Cloudflare R2\n * Supports multiple disk configurations with dynamic path templates\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { StorageManagerService } from './services/storage-manager.service'\nimport { StorageService } from './services/storage.service'\nimport { STORAGE_TOKENS } from './storage.tokens'\nimport type { StorageConfig } from './types'\n\n/**\n * Storage module options\n * Same as StorageConfig from types.ts\n */\nexport type StorageModuleOptions = StorageConfig\n\n@Module({\n providers: [\n { provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },\n ],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],\n * defaultStorageDisk: 'uploads',\n * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }\n * })\n * ```\n */\n static forRoot(options: StorageModuleOptions): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n { provide: STORAGE_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Configure StorageModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @example\n * ```typescript\n * StorageModule.forRootAsync({\n * inject: [storageConfig.KEY],\n * useFactory: (storage) => ({\n * storage: storage.storage,\n * defaultStorageDisk: storage.defaultStorageDisk,\n * presignedUrl: storage.presignedUrl\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<StorageModuleOptions>): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n {\n provide: STORAGE_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class FileNotFoundError extends HttpException {\n constructor(path?: string) {\n super(404, path ? `File not found: \"${path}\"` : 'File not found')\n }\n}\n","import { inject } from '../../di'\nimport { z } from '../../i18n/validation'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { Delete, Get, Put } from '../../router/decorators/http-method.decorator'\nimport { type RouterContext } from '../../router/router-context'\nimport { FileNotFoundError } from '../errors/file-not-found.error'\nimport type { StorageService } from '../services/storage.service'\nimport { STORAGE_TOKENS } from '../storage.tokens'\n\nconst diskParam = z.object({\n disk: z.string(),\n})\n\n/**\n * Storage Controller\n *\n * Auto-registered controller that proxies R2 operations behind signed URLs.\n * Signature verification is applied via VerifySignatureMiddleware on the module's\n * configureRoutes() method.\n *\n * Routes:\n * - GET /storage/:disk/* → download file\n * - PUT /storage/:disk/* → upload file\n * - DELETE /storage/:disk/* → delete file\n */\n@Controller('/storage', { hideFromDocs: true })\nexport class StorageController {\n constructor(\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) {}\n\n @Get('/:disk/*', { hideFromDocs: true, params: diskParam })\n async download(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n const result = await this.storage.download(path, disk)\n\n const stream = result.toStream()\n if (!stream) {\n throw new FileNotFoundError(path)\n }\n\n return new Response(stream, {\n headers: {\n 'Content-Type': result.contentType,\n 'Content-Length': String(result.size),\n 'Content-Disposition': 'inline',\n },\n })\n }\n\n @Put('/:disk/*', { hideFromDocs: true, params: diskParam })\n async upload(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n const body = ctx.c.req.raw.body\n const contentType = ctx.header('content-type') ?? 'application/octet-stream'\n const contentLength = ctx.header('content-length')\n\n await this.storage.upload(body, path, {\n mimeType: contentType,\n size: contentLength ? parseInt(contentLength, 10) : 0,\n }, disk)\n\n return ctx.json({ path, disk }, 200)\n }\n\n @Delete('/:disk/*', { hideFromDocs: true, params: diskParam })\n async destroy(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n await this.storage.delete(path, disk)\n\n return ctx.c.body(null, 204)\n }\n}\n\n/**\n * Extract the wildcard path from the Hono context.\n * Hono stores wildcard params under the key matching the path pattern.\n */\nfunction extractWildcardPath(ctx: RouterContext): string {\n // Hono exposes wildcard capture as the raw path after the matched prefix\n const url = new URL(ctx.c.req.url)\n const fullPath = url.pathname\n // Remove /storage/:disk/ prefix to get the file path\n const parts = fullPath.split('/')\n // ['', 'storage', 'disk', ...rest]\n return parts.slice(3).join('/')\n}\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const deleteFileInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type DeleteFileInput = z.infer<typeof deleteFileInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const fileExistsInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type FileExistsInput = z.infer<typeof fileExistsInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const getPresignedUrlInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']).default('GET'),\n expiresIn: z.number().int().min(1).max(604800).optional(),\n disk: z.string().optional(),\n})\n\nexport type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>\n\nexport const presignedUrlResultSchema = z.object({\n url: z.string().url(),\n expiresIn: z.number(),\n expiresAt: z.date(),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']),\n})\n\nexport type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>\n","import { z } from '../../i18n/validation'\n\n/**\n * Upload options for streaming uploads\n */\nexport interface UploadOptions {\n /**\n * Size of the content in bytes\n */\n size: number\n /**\n * MIME type of the content\n */\n mimeType?: string\n /**\n * Custom metadata to store with the object (S3-specific)\n * Stored as S3 object metadata headers\n */\n metadata?: Record<string, string>\n /**\n * Object tagging for lifecycle policies (S3-specific)\n * Format: key=value (e.g., \"Tus-Completed=true\")\n */\n tagging?: string\n}\n\nexport const uploadResultSchema = z.object({\n path: z.string(),\n disk: z.string(),\n fullPath: z.string(),\n size: z.number(),\n mimeType: z.string(),\n uploadedAt: z.date(),\n})\n\nexport type UploadResult = z.infer<typeof uploadResultSchema>\n","import { HttpException } from '../../errors'\n\nexport class FileTooLargeError extends HttpException {\n constructor(public readonly size?: number, public readonly maxSize?: number) {\n super(413, 'File too large')\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class InvalidFileTypeError extends HttpException {\n constructor(public readonly mimeType?: string) {\n super(422, 'Invalid file type')\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage-BE6-kzaN.mjs","names":[],"sources":["../src/storage/storage.tokens.ts","../src/storage/services/storage-manager.service.ts","../src/storage/services/storage.service.ts","../src/storage/storage.module.ts","../src/storage/errors/file-not-found.error.ts","../src/storage/controllers/storage.controller.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts","../src/storage/errors/file-too-large.error.ts","../src/storage/errors/invalid-file-type.error.ts"],"sourcesContent":["/**\n * Dependency injection tokens for the Storage module\n * Using Symbol-based tokens to avoid magic strings\n */\nexport const STORAGE_TOKENS = {\n Options: Symbol.for('stratal:storage:options'),\n StorageService: Symbol.for('stratal:storage:service'),\n StorageManager: Symbol.for('stratal:storage:manager'),\n} as const\n","import { inject } from '../../di'\nimport { Singleton } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { StorageError } from '../storage.error'\nimport type { IStorageProvider } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig, StorageEntry } from '../types'\n\n/**\n * Storage Manager Service\n * Manages multiple storage providers (one per disk)\n * Handles lazy initialization and caching of R2 providers\n */\n@Singleton(STORAGE_TOKENS.StorageManager)\nexport class StorageManagerService {\n private readonly providers = new Map<string, IStorageProvider>()\n private readonly creationPromises = new Map<string, Promise<IStorageProvider>>()\n private readonly diskConfigs = new Map<string, StorageEntry>()\n\n constructor(\n @inject(STORAGE_TOKENS.Options)\n private readonly options: StorageConfig,\n @inject(DI_TOKENS.CloudflareEnv)\n private readonly env: StratalEnv\n ) {\n this.initializeDiskConfigs()\n }\n\n /**\n * Initialize disk configurations from options\n */\n private initializeDiskConfigs(): void {\n for (const entry of this.options.storage) {\n this.diskConfigs.set(entry.disk, entry)\n }\n }\n\n /**\n * Get provider for a specific disk\n * Lazily initializes provider on first access\n * @param diskName - Name of the disk\n * @returns Storage provider instance\n */\n async getProvider(diskName: string): Promise<IStorageProvider> {\n // Return cached provider if exists\n const cached = this.providers.get(diskName)\n if (cached) {\n return cached\n }\n\n // Return in-flight creation promise to deduplicate concurrent calls\n const inflight = this.creationPromises.get(diskName)\n if (inflight) {\n return inflight\n }\n\n // Get disk configuration\n const diskConfig = this.diskConfigs.get(diskName)\n if (!diskConfig) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n // Create provider and deduplicate concurrent calls\n const promise = this.createProvider(diskConfig).then((provider) => {\n this.providers.set(diskName, provider)\n this.creationPromises.delete(diskName)\n return provider\n }).catch((error: unknown) => {\n this.creationPromises.delete(diskName)\n throw error\n })\n\n this.creationPromises.set(diskName, promise)\n\n return promise\n }\n\n /**\n * Create an R2 provider instance\n * Dynamically imports R2StorageProvider to support code splitting\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n const { R2StorageProvider } = await import('../providers/r2-storage.provider')\n const bucket = this.env[config.binding as keyof StratalEnv] as unknown as R2Bucket | undefined\n if (!bucket) {\n throw new StorageError(`R2 binding \"${config.binding}\" was not found in the environment`)\n }\n return new R2StorageProvider(config, bucket, this.env, this.options.route)\n }\n\n /**\n * Get disk configuration\n * @param diskName - Name of the disk\n * @returns Storage entry configuration\n */\n getDiskConfig(diskName: string): StorageEntry {\n const config = this.diskConfigs.get(diskName)\n if (!config) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n return config\n }\n\n /**\n * Check if a disk exists\n * @param diskName - Name of the disk\n * @returns True if disk exists, false otherwise\n */\n hasDisk(diskName: string): boolean {\n return this.diskConfigs.has(diskName)\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return Array.from(this.diskConfigs.keys())\n }\n}\n","import { inject } from '../../di'\nimport { Request } from '../../di/decorators'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { StorageError } from '../storage.error'\nimport type { StreamingBlobPayloadInputTypes } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig } from '../types'\nimport { type StorageManagerService } from './storage-manager.service'\n\n/**\n * Storage Service\n *\n * Main facade for storage operations.\n * Request-scoped for proper isolation.\n *\n * @example\n * ```typescript\n * @inject(STORAGE_TOKENS.StorageService)\n * private readonly storage: StorageService\n *\n * await this.storage.upload(file, 'documents/report.pdf')\n * ```\n */\n@Request(STORAGE_TOKENS.StorageService)\nexport class StorageService {\n constructor(\n @inject(STORAGE_TOKENS.StorageManager)\n protected readonly storageManager: StorageManagerService,\n @inject(STORAGE_TOKENS.Options)\n protected readonly options: StorageConfig\n ) { }\n\n /**\n * Upload content to storage\n * @param body - Content to upload (stream, buffer, or string)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options including size and mime type\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async upload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: UploadOptions,\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.upload(body, fullPath, options)\n }\n\n /**\n * Download a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Download result with stream and metadata\n */\n async download(relativePath: string, disk?: string): Promise<DownloadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.download(fullPath)\n }\n\n /**\n * Delete a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n */\n async delete(relativePath: string, disk?: string): Promise<void> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n await provider.delete(fullPath)\n }\n\n /**\n * Check if a file exists in storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns True if file exists, false otherwise\n */\n async exists(relativePath: string, disk?: string): Promise<boolean> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.exists(fullPath)\n }\n\n /**\n * Generate a presigned download URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDownloadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'GET', expiresIn, disk)\n }\n\n /**\n * Generate a presigned upload URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedUploadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'PUT', expiresIn, disk)\n }\n\n /**\n * Generate a presigned delete URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDeleteUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'DELETE', expiresIn, disk)\n }\n\n /**\n * Generate a presigned URL for any method\n * @param relativePath - Relative path within the disk\n * @param method - HTTP method (GET, PUT, DELETE, HEAD)\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n protected async getPresignedUrl(\n relativePath: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n const validatedExpiresIn = this.validateExpiresIn(expiresIn)\n\n return provider.getPresignedUrl(fullPath, method, validatedExpiresIn)\n }\n\n /**\n * Resolve disk name (use default if not provided)\n * @param disk - Optional disk name\n * @returns Resolved disk name\n */\n protected resolveDisk(disk?: string): string {\n const diskName = disk ?? this.options.defaultStorageDisk\n\n if (!this.storageManager.hasDisk(diskName)) {\n throw new StorageError(`Disk \"${diskName}\" is not configured`)\n }\n\n return diskName\n }\n\n /**\n * Build full path with disk root and path template substitution\n * @param relativePath - Relative path within the disk\n * @param diskName - Name of the disk\n * @returns Full path including disk root\n */\n protected buildFullPath(relativePath: string, diskName: string): string {\n const diskConfig = this.storageManager.getDiskConfig(diskName)\n let root = diskConfig.root || ''\n\n // Substitute template variables\n root = this.substituteTemplateVariables(root)\n\n // Combine root and relative path\n const fullPath = `${root}/${relativePath}`.replace(/\\/+/g, '/').replace(/^\\//, '')\n\n return fullPath\n }\n\n /**\n * Substitute template variables in path\n * Override this method in subclasses to add custom substitutions\n *\n * @param path - Path with template variables\n * @returns Path with substituted variables\n */\n protected substituteTemplateVariables(path: string): string {\n let result = path\n\n // Substitute {date}, {year}, {month}\n const now = new Date()\n result = result.replace(/{date}/g, now.toISOString().split('T')[0])\n result = result.replace(/{year}/g, now.getFullYear().toString())\n result = result.replace(/{month}/g, (now.getMonth() + 1).toString().padStart(2, '0'))\n\n return result\n }\n\n /**\n * Validate expiry time for presigned URLs\n * @param expiresIn - Optional expiry time in seconds\n * @returns Validated expiry time\n */\n protected validateExpiresIn(expiresIn?: number): number {\n const presignedUrlConfig = this.options.presignedUrl\n const validatedExpiresIn = expiresIn ?? presignedUrlConfig.defaultExpiry\n\n const minExpiry = 1\n const maxExpiry = presignedUrlConfig.maxExpiry\n\n if (validatedExpiresIn < minExpiry || validatedExpiresIn > maxExpiry) {\n throw new StorageError(`Presigned URL expiry ${validatedExpiresIn}s is out of range (${minExpiry}–${maxExpiry}s)`)\n }\n\n return validatedExpiresIn\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return this.storageManager.getAvailableDisks()\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses multipart upload under the hood - handles retries and large files\n *\n * Use this method when:\n * - Content-Length is unknown or unreliable\n * - Uploading from streams that can't be rewound\n * - Need automatic retry handling for transient failures\n *\n * @param body - Content to upload (stream or buffer)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options (mimeType required, size optional)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: Omit<UploadOptions, 'size'> & { size?: number },\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.chunkedUpload(body, fullPath, options)\n }\n}\n","/**\n * Storage Module\n * Provides file storage capabilities using Cloudflare R2\n * Supports multiple disk configurations with dynamic path templates\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { StorageManagerService } from './services/storage-manager.service'\nimport { StorageService } from './services/storage.service'\nimport { STORAGE_TOKENS } from './storage.tokens'\nimport type { StorageConfig } from './types'\n\n/**\n * Storage module options\n * Same as StorageConfig from types.ts\n */\nexport type StorageModuleOptions = StorageConfig\n\n@Module({\n providers: [\n { provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },\n ],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', binding: 'MY_BUCKET', root: 'uploads' }],\n * defaultStorageDisk: 'uploads',\n * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }\n * })\n * ```\n */\n static forRoot(options: StorageModuleOptions): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n { provide: STORAGE_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Configure StorageModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @example\n * ```typescript\n * StorageModule.forRootAsync({\n * inject: [storageConfig.KEY],\n * useFactory: (storage) => ({\n * storage: storage.storage,\n * defaultStorageDisk: storage.defaultStorageDisk,\n * presignedUrl: storage.presignedUrl\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<StorageModuleOptions>): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n {\n provide: STORAGE_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class FileNotFoundError extends HttpException {\n constructor(path?: string) {\n super(404, path ? `File not found: \"${path}\"` : 'File not found')\n }\n}\n","import { inject } from '../../di'\nimport { z } from '../../i18n/validation'\nimport { Controller } from '../../router/decorators/controller.decorator'\nimport { Delete, Get, Put } from '../../router/decorators/http-method.decorator'\nimport { type RouterContext } from '../../router/router-context'\nimport { FileNotFoundError } from '../errors/file-not-found.error'\nimport type { StorageService } from '../services/storage.service'\nimport { STORAGE_TOKENS } from '../storage.tokens'\n\nconst diskParam = z.object({\n disk: z.string(),\n})\n\n/**\n * Storage Controller\n *\n * Auto-registered controller that proxies R2 operations behind signed URLs.\n * Signature verification is applied via VerifySignatureMiddleware on the module's\n * configureRoutes() method.\n *\n * Routes:\n * - GET /storage/:disk/* → download file\n * - PUT /storage/:disk/* → upload file\n * - DELETE /storage/:disk/* → delete file\n */\n@Controller('/storage', { hideFromDocs: true })\nexport class StorageController {\n constructor(\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) {}\n\n @Get('/:disk/*', { hideFromDocs: true, params: diskParam })\n async download(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n const result = await this.storage.download(path, disk)\n\n const stream = result.toStream()\n if (!stream) {\n throw new FileNotFoundError(path)\n }\n\n return new Response(stream, {\n headers: {\n 'Content-Type': result.contentType,\n 'Content-Length': String(result.size),\n 'Content-Disposition': 'inline',\n },\n })\n }\n\n @Put('/:disk/*', { hideFromDocs: true, params: diskParam })\n async upload(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n const body = ctx.c.req.raw.body\n const contentType = ctx.header('content-type') ?? 'application/octet-stream'\n const contentLength = ctx.header('content-length')\n\n await this.storage.upload(body, path, {\n mimeType: contentType,\n size: contentLength ? parseInt(contentLength, 10) : 0,\n }, disk)\n\n return ctx.json({ path, disk }, 200)\n }\n\n @Delete('/:disk/*', { hideFromDocs: true, params: diskParam })\n async destroy(ctx: RouterContext): Promise<Response> {\n const disk = ctx.param('disk')\n const path = extractWildcardPath(ctx)\n\n await this.storage.delete(path, disk)\n\n return ctx.c.body(null, 204)\n }\n}\n\n/**\n * Extract the wildcard path from the Hono context.\n * Hono stores wildcard params under the key matching the path pattern.\n */\nfunction extractWildcardPath(ctx: RouterContext): string {\n // Hono exposes wildcard capture as the raw path after the matched prefix\n const url = new URL(ctx.c.req.url)\n const fullPath = url.pathname\n // Remove /storage/:disk/ prefix to get the file path\n const parts = fullPath.split('/')\n // ['', 'storage', 'disk', ...rest]\n return parts.slice(3).join('/')\n}\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const deleteFileInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type DeleteFileInput = z.infer<typeof deleteFileInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const fileExistsInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type FileExistsInput = z.infer<typeof fileExistsInputSchema>\n","import { z, withZodI18n } from '../../i18n/validation'\n\nexport const getPresignedUrlInputSchema = z.object({\n path: z.string().min(1, withZodI18n('zodI18n.errors.custom.filePathRequired')),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']).default('GET'),\n expiresIn: z.number().int().min(1).max(604800).optional(),\n disk: z.string().optional(),\n})\n\nexport type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>\n\nexport const presignedUrlResultSchema = z.object({\n url: z.string().url(),\n expiresIn: z.number(),\n expiresAt: z.date(),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']),\n})\n\nexport type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>\n","import { z } from '../../i18n/validation'\n\n/**\n * Upload options for streaming uploads\n */\nexport interface UploadOptions {\n /**\n * Size of the content in bytes\n */\n size: number\n /**\n * MIME type of the content\n */\n mimeType?: string\n /**\n * Custom metadata to store with the object (S3-specific)\n * Stored as S3 object metadata headers\n */\n metadata?: Record<string, string>\n /**\n * Object tagging for lifecycle policies (S3-specific)\n * Format: key=value (e.g., \"Tus-Completed=true\")\n */\n tagging?: string\n}\n\nexport const uploadResultSchema = z.object({\n path: z.string(),\n disk: z.string(),\n fullPath: z.string(),\n size: z.number(),\n mimeType: z.string(),\n uploadedAt: z.date(),\n})\n\nexport type UploadResult = z.infer<typeof uploadResultSchema>\n","import { HttpException } from '../../errors'\n\nexport class FileTooLargeError extends HttpException {\n constructor(public readonly size?: number, public readonly maxSize?: number) {\n super(413, 'File too large')\n }\n}\n","import { HttpException } from '../../errors'\n\nexport class InvalidFileTypeError extends HttpException {\n constructor(public readonly mimeType?: string) {\n super(422, 'Invalid file type')\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { a as ApplicationError } from "./container-storage-BmOJ4_Na.mjs";
2
- import "./errors-mXYxG0XB.mjs";
2
+ import "./errors-EUtwVfNm.mjs";
3
3
  //#region src/storage/storage.error.ts
4
4
  var StorageError = class extends ApplicationError {};
5
5
  //#endregion
6
6
  export { StorageError as t };
7
7
 
8
- //# sourceMappingURL=storage.error-Dnib4VHc.mjs.map
8
+ //# sourceMappingURL=storage.error-Dai3rShJ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.error-Dnib4VHc.mjs","names":[],"sources":["../src/storage/storage.error.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class StorageError extends ApplicationError {}\n"],"mappings":";;;AAEA,IAAa,eAAb,cAAkC,iBAAiB,CAAC"}
1
+ {"version":3,"file":"storage.error-Dai3rShJ.mjs","names":[],"sources":["../src/storage/storage.error.ts"],"sourcesContent":["import { ApplicationError } from '../errors'\n\nexport class StorageError extends ApplicationError {}\n"],"mappings":";;;AAEA,IAAa,eAAb,cAAkC,iBAAiB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ct as Application, gn as HonoApp, lt as ApplicationConfig } from "./index-B_JoEl3V.mjs";
1
+ import { ct as Application, gn as HonoApp, lt as ApplicationConfig } from "./index-C_t8VVQD.mjs";
2
2
  import { t as StratalEnv } from "./env-ug22bJj7.mjs";
3
3
 
4
4
  //#region src/stratal.d.ts
@@ -40,4 +40,4 @@ declare class Stratal<Env extends StratalEnv = StratalEnv> {
40
40
  }
41
41
  //#endregion
42
42
  export { Stratal as t };
43
- //# sourceMappingURL=stratal-DwDJPY9N.d.mts.map
43
+ //# sourceMappingURL=stratal-Bmc6WT3k.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stratal-DwDJPY9N.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,iBAAA;cAEH,MAAA,EAAQ,iBAAA;EAiBd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,IAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,IAAY,OAAA;EA5CV;;;;;;EAAA,OA0DD,kBAAA,IAAsB,OAAA,CAAQ,WAAA;EAAA,QAOvB,WAAA;EAAA,QAKA,UAAA;AAAA"}
1
+ {"version":3,"file":"stratal-Bmc6WT3k.d.mts","names":[],"sources":["../src/stratal.ts"],"mappings":";;;;;AAmBA;;;;;;;;;;;;;cAAa,OAAA,aAAoB,UAAA,GAAa,UAAA;EAAA,QACpC,GAAA;EAAA,QACA,WAAA;EAAA,eAEO,YAAA;EAAA,eACA,WAAA;EAAA,eACA,iBAAA;cAEH,MAAA,EAAQ,iBAAA;EAiBd,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EAMlE,KAAA,CAAM,KAAA,EAAO,YAAA,GAAe,OAAA;EAK5B,SAAA,CAAU,UAAA,EAAY,mBAAA,GAAsB,OAAA;EAAA,IAK9C,IAAA,IAAQ,OAAA,CAAQ,OAAA;EAId,QAAA,IAAY,OAAA;EA5CV;;;;;;EAAA,OA0DD,kBAAA,IAAsB,OAAA,CAAQ,WAAA;EAAA,QAOvB,WAAA;EAAA,QAKA,UAAA;AAAA"}
@@ -1,11 +1,11 @@
1
- import { r as DI_TOKENS, t as Container, v as ROUTER_TOKENS } from "./di-DseMn-z9.mjs";
1
+ import { r as DI_TOKENS, t as Container, v as ROUTER_TOKENS } from "./di-B0NXIdRF.mjs";
2
2
  import { n as getContainer, r as runWithContainer, t as containerStorage } from "./container-storage-BmOJ4_Na.mjs";
3
3
  import { JsonFormatter, LOGGER_TOKENS, LoggerService, PrettyFormatter } from "./logger/index.mjs";
4
- import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-mXYxG0XB.mjs";
5
- import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-kEoMFwze.mjs";
6
- import { a as getListenerHandlers } from "./events-BXJGZjpG.mjs";
7
- import { d as LazyModuleLoader, t as ModuleRegistry } from "./module-registry-Dm-pqHd3.mjs";
8
- import { t as SEEDER_TOKENS } from "./seeder-registry-CyUmKsJq.mjs";
4
+ import { a as DefaultExceptionHandler, r as StratalNotInitializedError } from "./errors-EUtwVfNm.mjs";
5
+ import { i as createQueueExceptionContext, n as createCronExceptionContext, t as createCliExceptionContext } from "./exception-context-Bd5Hk2c_.mjs";
6
+ import { a as getListenerHandlers } from "./events-zbCMY_Gf.mjs";
7
+ import { d as LazyModuleLoader, t as ModuleRegistry } from "./module-registry-MfWmniZ7.mjs";
8
+ import { t as SEEDER_TOKENS } from "./seeder-registry-B5FwBeCU.mjs";
9
9
  //#region src/application.ts
10
10
  var Application = class {
11
11
  _container;
@@ -51,7 +51,7 @@ var Application = class {
51
51
  await runWithContainer(this._container, () => this.initializeInternal());
52
52
  }
53
53
  async initializeInternal() {
54
- const [{ QuarryModule }, { SeederModule }] = await Promise.all([import("./quarry.module-BuRPGMDm.mjs").then((n) => n.n), import("./seeder.module-CYYwk3Qk.mjs").then((n) => n.n)]);
54
+ const [{ QuarryModule }, { SeederModule }] = await Promise.all([import("./quarry.module-BOgcNJMU.mjs").then((n) => n.n), import("./seeder.module-DaY0RYoB.mjs").then((n) => n.n)]);
55
55
  this.moduleRegistry.registerAll([QuarryModule, SeederModule]);
56
56
  this.moduleRegistry.register(this.appConfig.module);
57
57
  await this.moduleRegistry.initialize();
@@ -64,14 +64,14 @@ var Application = class {
64
64
  }
65
65
  async registerRoutingServices() {
66
66
  const [{ HonoApp }, { RouteRegistry }, { RouterResolver }, { VersioningService }, { LocalePathService }, { LocaleUrlService }, { RouteRegistrationService }, { Uri }] = await Promise.all([
67
- import("./hono-app-CvV3hOfT.mjs").then((n) => n.n),
68
- import("./route-registry-CYqLp2Nj.mjs").then((n) => n.n),
69
- import("./router-resolver-D4YlPNlm.mjs"),
70
- import("./versioning.service-C6aHky8-.mjs").then((n) => n.n),
71
- import("./locale-path.service-D-dHiIPc.mjs").then((n) => n.n),
72
- import("./locale-url.service-C2EWmGdq.mjs").then((n) => n.n),
73
- import("./route-registration.service-D6vSwiKP.mjs").then((n) => n.n),
74
- import("./uri-h7Q8Jug9.mjs").then((n) => n.r)
67
+ import("./hono-app-C7-1m9eK.mjs").then((n) => n.n),
68
+ import("./route-registry-CtW26Suv.mjs").then((n) => n.n),
69
+ import("./router-resolver-CMNuw-s0.mjs"),
70
+ import("./versioning.service-B4rtmgMQ.mjs").then((n) => n.n),
71
+ import("./locale-path.service-DzA01Kvc.mjs").then((n) => n.n),
72
+ import("./locale-url.service-DwTXZid3.mjs").then((n) => n.n),
73
+ import("./route-registration.service-HN8WgIis.mjs").then((n) => n.n),
74
+ import("./uri-h6ITRpqr.mjs").then((n) => n.r)
75
75
  ]);
76
76
  this._container.register(ROUTER_TOKENS.VersioningService, VersioningService);
77
77
  this._container.register(ROUTER_TOKENS.HonoApp, HonoApp);
@@ -92,7 +92,7 @@ var Application = class {
92
92
  */
93
93
  initializeEventListeners() {
94
94
  this.eventsInitPromise ??= runWithContainer(this._container, async () => {
95
- const { EventsModule } = await import("./events-BXJGZjpG.mjs").then((n) => n.n);
95
+ const { EventsModule } = await import("./events-zbCMY_Gf.mjs").then((n) => n.n);
96
96
  await this.moduleRegistry.registerLazy(EventsModule);
97
97
  this.registerEventListeners();
98
98
  });
@@ -122,7 +122,7 @@ var Application = class {
122
122
  initializeQueue() {
123
123
  this.queueInitPromise ??= runWithContainer(this._container, async () => {
124
124
  await this.ensureI18n();
125
- const { QueueModule } = await import("./queue.module-nddvxzCB.mjs").then((n) => n.n);
125
+ const { QueueModule } = await import("./queue.module-DSAH88gZ.mjs").then((n) => n.n);
126
126
  this.moduleRegistry.register(QueueModule);
127
127
  this.consumerRegistry = this._container.resolve(DI_TOKENS.ConsumerRegistry);
128
128
  await this.registerQueueConsumers();
@@ -137,7 +137,7 @@ var Application = class {
137
137
  */
138
138
  ensureI18n() {
139
139
  this.i18nInitPromise ??= runWithContainer(this._container, async () => {
140
- const { I18nModule } = await import("./i18n.module-DRQAZoSZ.mjs").then((n) => n.n);
140
+ const { I18nModule } = await import("./i18n.module-JOPUNpvi.mjs").then((n) => n.n);
141
141
  await this.moduleRegistry.registerLazy(I18nModule);
142
142
  });
143
143
  return this.i18nInitPromise;
@@ -148,7 +148,7 @@ var Application = class {
148
148
  */
149
149
  ensureCron() {
150
150
  this.cronInitPromise ??= runWithContainer(this._container, async () => {
151
- const { CronModule } = await import("./cron.module-Bgzq5hiT.mjs").then((n) => n.n);
151
+ const { CronModule } = await import("./cron.module-DgxLe8Xy.mjs").then((n) => n.n);
152
152
  this.moduleRegistry.register(CronModule);
153
153
  this.cronManager = this._container.resolve(DI_TOKENS.Cron);
154
154
  this.registerCronJobs(this.cronManager);
@@ -196,6 +196,7 @@ var Application = class {
196
196
  return {
197
197
  getLocale: () => locale,
198
198
  setLocale: () => {},
199
+ header: () => void 0,
199
200
  getContainer: () => containerStorage.getStore() ?? this._container
200
201
  };
201
202
  }
@@ -380,4 +381,4 @@ var Stratal = class Stratal {
380
381
  //#endregion
381
382
  export { Application as n, Stratal as t };
382
383
 
383
- //# sourceMappingURL=stratal-DL9M38_s.mjs.map
384
+ //# sourceMappingURL=stratal-DtAj21uF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stratal-DtAj21uF.mjs","names":[],"sources":["../src/application.ts","../src/stratal.ts"],"sourcesContent":["import type { CronJob } from './cron/cron-job'\nimport type { CronManager } from './cron/cron-manager'\nimport { Container } from './di/container'\nimport { containerStorage, getContainer, runWithContainer } from './di/container-storage'\nimport { DI_TOKENS } from './di/tokens'\nimport { type StratalEnv } from './env'\nimport { DefaultExceptionHandler } from './errors/default-exception-handler'\nimport { createCliExceptionContext, createCronExceptionContext, createQueueExceptionContext } from './errors/exception-context'\nimport type { ExceptionHandler } from './errors/exception-handler'\nimport type { EventHandler, EventRegistry } from './events'\nimport { getListenerHandlers } from './events'\nimport type { StratalExecutionContext } from './execution-context'\nimport { JsonFormatter, LOGGER_TOKENS, LoggerService, LogLevel, PrettyFormatter } from './logger'\nimport { LazyModuleLoader } from './module/lazy-module-loader'\nimport { ModuleRegistry } from './module/module-registry'\nimport type { DynamicModule, ModuleClass } from './module/types'\nimport type { Command } from './quarry/command'\nimport type { QuarryRegistry } from './quarry/quarry-registry'\nimport type { CommandInput, CommandResult } from './quarry/types'\nimport type { ConsumerRegistry } from './queue/consumer-registry'\nimport type { IQueueConsumer, QueueMessage } from './queue/queue-consumer'\nimport type { QueueManager } from './queue/queue-manager'\nimport type { RouterContext } from './router'\nimport type { HonoApp } from './router/hono-app'\nimport { ROUTER_TOKENS } from './router/router.tokens'\nimport type { TrailingSlashMode, VersioningOptions } from './router/types'\nimport type { Seeder } from './seeder/seeder'\nimport { SEEDER_TOKENS } from './seeder/seeder-registry'\nimport type { SeederRegistry } from './seeder/seeder-registry'\nimport type { Constructor } from './types'\n\nexport interface ApplicationConfig {\n module: ModuleClass | DynamicModule\n logging?: {\n level?: LogLevel\n formatter?: 'json' | 'pretty'\n }\n versioning?: VersioningOptions\n trailingSlash?: TrailingSlashMode\n exceptionHandler?: Constructor<ExceptionHandler>\n}\n\nexport interface ApplicationOptions extends ApplicationConfig {\n env: StratalEnv\n ctx: StratalExecutionContext\n}\n\nexport class Application {\n private _container: Container\n private honoApp!: HonoApp\n private moduleRegistry: ModuleRegistry\n private consumerRegistry!: ConsumerRegistry\n private cronManager?: CronManager\n private quarry!: QuarryRegistry\n private initialized = false\n\n // Independently memoized lazy-init promises — each built-in subsystem is\n // loaded on demand at its trigger point to keep cold start lean.\n private routingInitPromise: Promise<void> | null = null\n private eventsInitPromise: Promise<void> | null = null\n private queueInitPromise: Promise<void> | null = null\n private i18nInitPromise: Promise<void> | null = null\n private cronInitPromise: Promise<void> | null = null\n\n readonly env: StratalEnv\n private readonly appConfig: ApplicationConfig\n\n constructor({ env, ctx, ...config }: ApplicationOptions) {\n this.env = env\n this.appConfig = config\n\n this._container = new Container()\n\n this._container.registerValue(DI_TOKENS.Application, this)\n this._container.registerValue(DI_TOKENS.CloudflareEnv, env)\n this._container.registerValue(DI_TOKENS.ExecutionContext, ctx)\n this._container.registerValue(ROUTER_TOKENS.RouterContext, null)\n\n this.registerLoggerService()\n this.registerCoreServices()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n this.moduleRegistry = new ModuleRegistry(this._container, logger)\n\n this._container.registerValue(DI_TOKENS.ModuleRegistry, this.moduleRegistry)\n }\n\n get container(): Container {\n return this._container\n }\n\n async ensureHono(): Promise<HonoApp> {\n await this.initializeRouting()\n return this.honoApp\n }\n\n get config(): ApplicationConfig {\n return this.appConfig\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) {\n return\n }\n\n await runWithContainer(this._container, () => this.initializeInternal())\n }\n\n private async initializeInternal(): Promise<void> {\n // Eager subsystem modules — Quarry/Seeder are resolved synchronously\n // post-init (CLI runner, test harness), so they must be registered before\n // initialize() completes. Dynamic import keeps application.ts free of static\n // subsystem imports (uniform with the other built-ins). They load at boot\n // regardless, so this is for consistency, not cold-start deferral.\n const [{ QuarryModule }, { SeederModule }] = await Promise.all([\n import('./quarry/quarry.module'),\n import('./seeder/seeder.module'),\n ])\n this.moduleRegistry.registerAll([QuarryModule, SeederModule])\n\n // Register the user's root module (traverses imports). Other built-in\n // subsystems (i18n, queue, cache, openapi, cron, events, router) load on\n // demand at their trigger points.\n this.moduleRegistry.register(this.appConfig.module)\n\n // Initialize all modules (only those with lifecycle hooks)\n await this.moduleRegistry.initialize()\n\n // Initialize ExceptionHandler\n this.initializeExceptionHandler()\n\n // Register CLI commands + seeders (class refs only; no instantiation)\n this.registerCommands()\n this.registerSeeders()\n\n // Wire event listeners as part of initialization whenever the application\n // declares any, so emitted events reach their handlers regardless of which\n // entry point drives the app. Listener wiring otherwise happens lazily only\n // on the HTTP router (`initializeRouting`) and queue/scheduled/command\n // (`ensureScopedHandlers`) paths; any code that emits events without going\n // through one of those — a direct service or repository call, an RPC\n // entrypoint, or a Durable Object — would dispatch into a registry with no\n // handlers attached, silently dropping the event. Guarded on there being\n // listeners so applications with none still skip loading the events\n // subsystem; `initializeEventListeners` dedups, so any later lazy trigger\n // becomes a no-op.\n if (this.moduleRegistry.getAllListeners().length > 0) {\n await this.initializeEventListeners()\n }\n\n // Cron only loads when the app actually declares scheduled jobs\n if (this.moduleRegistry.getAllJobs().length > 0) {\n await this.ensureCron()\n }\n\n this.initialized = true\n }\n\n private async registerRoutingServices(): Promise<void> {\n const [\n { HonoApp },\n { RouteRegistry },\n { RouterResolver },\n { VersioningService },\n { LocalePathService },\n { LocaleUrlService },\n { RouteRegistrationService },\n { Uri },\n ] = await Promise.all([\n import('./router/hono-app'),\n import('./router/route-registry'),\n import('./router/router-resolver'),\n import('./router/services/versioning.service'),\n import('./router/services/locale-path.service'),\n import('./router/services/locale-url.service'),\n import('./router/services/route-registration.service'),\n import('./router/uri'),\n ])\n\n this._container.register(ROUTER_TOKENS.VersioningService, VersioningService)\n this._container.register(ROUTER_TOKENS.HonoApp, HonoApp)\n this._container.register(ROUTER_TOKENS.LocalePathService, LocalePathService)\n this._container.register(ROUTER_TOKENS.LocaleUrlService, LocaleUrlService)\n this._container.register(ROUTER_TOKENS.RouteRegistry, RouteRegistry)\n this._container.register(ROUTER_TOKENS.Uri, Uri)\n\n const routerConfigs = this.moduleRegistry.getAllRouterConfigs()\n if (routerConfigs.length > 0) {\n this._container.registerValue(ROUTER_TOKENS.RouterResolver, new RouterResolver(routerConfigs))\n }\n\n this._container.register(RouteRegistrationService, RouteRegistrationService)\n }\n\n /**\n * Load the events subsystem and wire listeners. Needed by the HTTP, queue,\n * and scheduled paths (handlers emit events). Independent of the queue\n * subsystem so HTTP-only apps never load queue code. EventsModule is\n * registered before the (possibly empty) listener wiring so `emit()` works\n * even with zero listeners; dedups against the framework's own load.\n */\n private initializeEventListeners(): Promise<void> {\n this.eventsInitPromise ??= runWithContainer(this._container, async () => {\n const { EventsModule } = await import('./events/events.module')\n await this.moduleRegistry.registerLazy(EventsModule)\n this.registerEventListeners()\n })\n return this.eventsInitPromise\n }\n\n /**\n * Wire event and queue handlers for any non-HTTP request scope — queue\n * batches, scheduled/cron runs, CLI commands, Durable Objects, Workflows,\n * WorkerEntrypoints — all of which may emit events or dispatch to queues from\n * arbitrary user code. This is the single source of truth for that wiring:\n * every non-HTTP entry point routes through it rather than open-coding which\n * subsystems to init, so a subsystem can't be silently dropped by a future\n * refactor (the queue half was once lost from the command path that way).\n *\n * HTTP (`fetch`) deliberately does NOT use this: a fetch worker only enqueues\n * to the async Cloudflare queue and never processes consumers inline, so it\n * skips queue init via `initializeRouting`.\n */\n async ensureScopedHandlers(): Promise<void> {\n await this.initializeEventListeners()\n await this.initializeQueue()\n }\n\n /**\n * Load the queue subsystem on demand (first queue/scheduled trigger). i18n is\n * ensured first because the queue registry depends on it.\n */\n private initializeQueue(): Promise<void> {\n this.queueInitPromise ??= runWithContainer(this._container, async () => {\n await this.ensureI18n()\n const { QueueModule } = await import('./queue/queue.module')\n this.moduleRegistry.register(QueueModule as unknown as ModuleClass)\n this.consumerRegistry = this._container.resolve<ConsumerRegistry>(DI_TOKENS.ConsumerRegistry)\n await this.registerQueueConsumers()\n })\n return this.queueInitPromise\n }\n\n /**\n * Load i18n on demand. Coupled to the request path (Zod validation error maps,\n * OpenAPI descriptions, queue registry), so it loads before routing/queue\n * handling. Uses `registerLazy` because I18nModule has an `onInitialize` hook\n * (configures the Zod error map). Dedups if the app already imported i18n.\n */\n private ensureI18n(): Promise<void> {\n this.i18nInitPromise ??= runWithContainer(this._container, async () => {\n const { I18nModule } = await import('./i18n/i18n.module')\n await this.moduleRegistry.registerLazy(I18nModule as unknown as ModuleClass)\n })\n return this.i18nInitPromise\n }\n\n /**\n * Load the cron subsystem on demand (first scheduled trigger, or at bootstrap\n * when the app declares jobs).\n */\n private ensureCron(): Promise<CronManager> {\n this.cronInitPromise ??= runWithContainer(this._container, async () => {\n const { CronModule } = await import('./cron/cron.module')\n this.moduleRegistry.register(CronModule)\n this.cronManager = this._container.resolve<CronManager>(DI_TOKENS.Cron)\n this.registerCronJobs(this.cronManager)\n })\n return this.cronInitPromise.then(() => this.cronManager!)\n }\n\n resolve<T>(token: symbol): T {\n try {\n return this._container.resolve(token)\n } catch (error) {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n const ctx = createCliExceptionContext('resolve')\n void handler.handle(error, ctx)\n throw error\n }\n }\n\n async handleQueue(batch: MessageBatch, queueName: string): Promise<void> {\n await this.ensureScopedHandlers()\n\n const firstMessage = batch.messages[0]?.body as QueueMessage | undefined\n const locale = firstMessage?.metadata?.locale ?? 'en'\n const mockRouterContext = this.createMockRouterContext(locale)\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n const queueManager = requestContainer.resolve<QueueManager>(DI_TOKENS.Queue)\n await queueManager.processBatch(queueName, batch)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createQueueExceptionContext(queueName))\n throw error\n }\n })\n }\n\n async handleScheduled(controller: ScheduledController): Promise<void> {\n const cronManager = await this.ensureCron()\n // A scheduled job is a non-HTTP scope that can emit events and dispatch to\n // queues (timeout/reminder/digest emails) — wire both so dispatches aren't\n // silently dropped by the sync provider. i18n is ensured independently for\n // the job execution context. All inits are memoized, so this is idempotent.\n await this.ensureScopedHandlers()\n await this.ensureI18n()\n\n const mockRouterContext = this.createMockRouterContext('en')\n\n await this._container.runInRequestScope(mockRouterContext, async (requestContainer) => {\n try {\n await cronManager.executeScheduled(controller, requestContainer)\n } catch (error) {\n const handler = requestContainer.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n await handler.handle(error, createCronExceptionContext())\n throw error\n }\n })\n }\n\n createMockRouterContext(locale = 'en'): RouterContext {\n return {\n getLocale: () => locale,\n setLocale: () => { /* no-op */ },\n // Non-HTTP scopes have no request headers\n header: () => undefined,\n // Inside runInRequestScope the active container is the request-scoped child\n // (set in AsyncLocalStorage); return it so request-scoped providers resolve\n // against the scope, not the global container.\n getContainer: () => containerStorage.getStore() ?? this._container,\n } as unknown as RouterContext\n }\n\n async shutdown(): Promise<void> {\n if (!this.initialized) return\n this.initialized = false\n\n await this.moduleRegistry.shutdown()\n\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.info('Disposing container...')\n\n this._container.dispose()\n }\n\n async handleCommand(name: string, input?: CommandInput): Promise<CommandResult> {\n // Routing first: commands generate URLs via route() (e.g. tenant:bootstrap\n // prints tenant URLs / builds email links).\n await this.initializeRouting()\n // A CLI command is a non-HTTP scope that can dispatch to queues and emit\n // events from arbitrary user code (e.g. tenant:bootstrap → email.send /\n // tenant.geo.seed). Without this the dev/CLI sync provider finds zero\n // consumers and silently drops every dispatched message. All inits are\n // memoized, so the events half (already wired by routing) is a no-op here.\n await this.ensureScopedHandlers()\n // Resolve QuarryRegistry lazily (deferred from bootstrap)\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const mockContext = this.createMockRouterContext('en')\n return this._container.runInRequestScope(mockContext, async () => {\n return this.quarry.call(name, input)\n })\n }\n\n private initializeRouting(): Promise<void> {\n this.routingInitPromise ??= runWithContainer(this._container, async () => {\n await this.initializeEventListeners()\n await this.ensureI18n()\n const { OpenAPIModule } = await import('./openapi')\n this.moduleRegistry.register(OpenAPIModule as unknown as ModuleClass)\n await this.registerRoutingServices()\n this.honoApp = this._container.resolve<HonoApp>(ROUTER_TOKENS.HonoApp)\n await this.honoApp.configure()\n })\n return this.routingInitPromise\n }\n\n private registerCommands(): void {\n this.quarry ??= this._container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n const commands = this.moduleRegistry.getAllCommands()\n for (const CommandClass of commands) {\n this.quarry.register(CommandClass as Constructor<Command>)\n }\n }\n\n private registerSeeders(): void {\n const seeders = this.moduleRegistry.getAllSeeders()\n if (seeders.length === 0) return\n const registry = this._container.resolve<SeederRegistry>(SEEDER_TOKENS.SeederRegistry)\n for (const SeederClass of seeders) {\n registry.register(SeederClass as Constructor<Seeder>)\n }\n }\n\n private async registerQueueConsumers(): Promise<void> {\n const consumerClasses = this.moduleRegistry.getAllConsumers()\n if (consumerClasses.length === 0) return\n\n // Consumers are resolved fresh per message at dispatch time (see\n // QueueManager / SyncQueueProvider). Here we only need each consumer's\n // declared message types; read them from a throwaway instance built inside a\n // request scope so a consumer that injects request-scoped providers\n // (@InjectQueue, i18n, auth context, …) doesn't fail to instantiate at boot.\n const mockContext = this.createMockRouterContext('en')\n await this._container.runInRequestScope(mockContext, (requestContainer) => {\n for (const ConsumerClass of consumerClasses) {\n const consumer = requestContainer.resolve(ConsumerClass) as IQueueConsumer\n this.consumerRegistry.register(\n ConsumerClass as Constructor<IQueueConsumer>,\n consumer.messageTypes,\n )\n }\n })\n }\n\n private registerCronJobs(cronManager: CronManager): void {\n for (const JobClass of this.moduleRegistry.getAllJobs()) {\n const schedule = (JobClass as unknown as { schedule: string }).schedule\n if (schedule) {\n cronManager.registerJob(schedule, JobClass as Constructor<CronJob>)\n } else {\n const logger = this._container.resolve<LoggerService>(LOGGER_TOKENS.LoggerService)\n logger.warn(`Cron job \"${JobClass.name}\" has no static schedule property — skipped`)\n }\n }\n }\n\n private registerEventListeners(): void {\n const listeners = this.moduleRegistry.getAllListeners()\n if (listeners.length === 0) {\n return\n }\n\n const eventRegistry = this._container.resolve<EventRegistry>(DI_TOKENS.EventRegistry)\n\n for (const ListenerClass of listeners) {\n const handlers = getListenerHandlers(ListenerClass)\n\n for (const { methodName, event, options } of handlers) {\n // Resolve the listener fresh per event from the active scope (events are\n // emitted inside an HTTP request or runInScope), so request-scoped\n // listener dependencies bind to the emitting request rather than being\n // frozen at boot. Listener metadata is read from the class statically —\n // no instance is created until an event actually fires.\n const handler: EventHandler = ((...args: unknown[]) => {\n const instance = getContainer().resolve(ListenerClass) as Record<\n string,\n (...a: unknown[]) => unknown\n >\n return instance[methodName](...args)\n }) as EventHandler\n\n eventRegistry.on(event, handler, options)\n }\n }\n }\n\n private registerLoggerService(): void {\n const logLevel = this.appConfig.logging?.level ?? LogLevel.INFO\n const formatter = this.appConfig.logging?.formatter ?? 'json'\n\n this._container.registerValue(LOGGER_TOKENS.LogLevelOptions, logLevel)\n\n this._container\n .when(() => formatter === 'pretty')\n .use(LOGGER_TOKENS.Formatter)\n .give(PrettyFormatter)\n .otherwise(JsonFormatter)\n\n this._container.registerSingleton(LOGGER_TOKENS.LoggerService, LoggerService)\n }\n\n /**\n * Bootstrap kernel — registered imperatively because they cannot be expressed\n * as ordinary module providers: ExceptionHandler is a user-overridable forced\n * singleton (a module ClassProvider derives scope from the class decorator,\n * which a user handler may not carry), and LazyModuleLoader is the loader\n * itself. Subsystem registries (events/cron/quarry/seeder) are modules.\n */\n private registerCoreServices(): void {\n this._container.registerSingleton(\n DI_TOKENS.ExceptionHandler,\n (this.appConfig.exceptionHandler ?? DefaultExceptionHandler) as Constructor,\n )\n this._container.registerSingleton(DI_TOKENS.LazyModuleLoader, LazyModuleLoader)\n }\n\n private initializeExceptionHandler(): void {\n const handler = this._container.resolve<ExceptionHandler>(DI_TOKENS.ExceptionHandler)\n handler.register()\n this.moduleRegistry.configureExceptionHandlers(handler)\n }\n}\n","import { Application, type ApplicationConfig } from './application'\nimport type { StratalEnv } from './env'\nimport { StratalNotInitializedError } from './errors'\nimport type { HonoApp } from './router/hono-app'\n\n/**\n * Stratal — Hono-style entry point for Cloudflare Workers.\n *\n * Eagerly bootstraps the Application at construction time, dynamically\n * importing `cloudflare:workers` for env and waitUntil.\n *\n * @example\n * ```typescript\n * import { Stratal } from 'stratal'\n * import { AppModule } from './app.module'\n *\n * export default new Stratal({ module: AppModule })\n * ```\n */\nexport class Stratal<Env extends StratalEnv = StratalEnv> {\n private app: Application | null = null\n private initPromise: Promise<Application>\n\n private static _application: Promise<Application> | null = null\n private static _generation = 0\n private static _previousInstance: Stratal | null = null\n\n constructor(config: ApplicationConfig) {\n this.fetch = this.fetch.bind(this)\n this.queue = this.queue.bind(this)\n this.scheduled = this.scheduled.bind(this)\n\n // Invalidate any in-flight initialization from a previous instance (Vite HMR reload)\n const generation = ++Stratal._generation\n\n if (Stratal._previousInstance) {\n void Stratal._previousInstance.shutdown()\n }\n Stratal._previousInstance = this\n\n this.initPromise = this.prepareApp(config, generation)\n Stratal._application = this.initPromise\n }\n\n async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {\n const app = await this.ensureReady()\n const hono = await app.ensureHono()\n return hono.fetch(request, env, ctx)\n }\n\n async queue(batch: MessageBatch): Promise<void> {\n const app = await this.ensureReady()\n return app.handleQueue(batch, batch.queue)\n }\n\n async scheduled(controller: ScheduledController): Promise<void> {\n const app = await this.ensureReady()\n return app.handleScheduled(controller)\n }\n\n get hono(): Promise<HonoApp> {\n return this.initPromise.then(app => app.ensureHono())\n }\n\n async shutdown(): Promise<void> {\n try { this.app = await this.initPromise } catch { /* ignore */ }\n if (this.app) {\n await this.app.shutdown()\n this.app = null\n }\n }\n\n /**\n * @internal\n * Resolves the Application instance from the static singleton.\n * Used by worker base classes (DurableObject, Workflow, WorkerEntrypoint)\n * to access the DI container without going through Cloudflare RPC.\n */\n static resolveApplication(): Promise<Application> {\n if (!Stratal._application) {\n throw new StratalNotInitializedError()\n }\n return Stratal._application\n }\n\n private async ensureReady(): Promise<Application> {\n this.app ??= await this.initPromise;\n return this.app\n }\n\n private async prepareApp(config: ApplicationConfig, generation: number): Promise<Application> {\n const { env, waitUntil } = await import('cloudflare:workers')\n\n // After async import, check if a newer instance has replaced us (Vite HMR reload)\n if (generation !== Stratal._generation) {\n return new Promise<Application>(() => {\n //\n }) // Never resolves — avoids cross-request promise warning\n }\n\n const app = new Application({ ...config, env: env as Env, ctx: { waitUntil } })\n await app.initialize()\n\n // Check again after initialization completes\n if (generation !== Stratal._generation) {\n await app.shutdown()\n return new Promise<Application>(() => {\n //\n })\n }\n\n return app\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./chunk-BBjsoOtd.mjs";
2
- import { d as inject, o as Request, r as DI_TOKENS, v as ROUTER_TOKENS } from "./di-DseMn-z9.mjs";
2
+ import { d as inject, o as Request, r as DI_TOKENS, v as ROUTER_TOKENS } from "./di-B0NXIdRF.mjs";
3
3
  import { n as __decorateParam, t as __decorate } from "./decorate-CuAoSZvs.mjs";
4
- import { o as RouterError } from "./module-registry-Dm-pqHd3.mjs";
4
+ import { o as RouterError } from "./module-registry-MfWmniZ7.mjs";
5
5
  import { t as applyTrailingSlash } from "./trailing-slash-CFyw8nYu.mjs";
6
6
  import { t as applyLocalePrefix } from "./locale-url-nZrZxqJP.mjs";
7
7
  import { n as verifySignedUrl, t as signUrl } from "./signed-url-DIU0sK_6.mjs";
@@ -248,4 +248,4 @@ Uri = __decorate([
248
248
  //#endregion
249
249
  export { buildRouteUrl as n, uri_exports as r, Uri as t };
250
250
 
251
- //# sourceMappingURL=uri-h7Q8Jug9.mjs.map
251
+ //# sourceMappingURL=uri-h6ITRpqr.mjs.map