stratal 0.0.21 → 0.0.23

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 (296) hide show
  1. package/README.md +2 -2
  2. package/dist/bin/cloudflare-workers-loader.mjs +80 -7
  3. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  4. package/dist/bin/quarry.mjs +84 -160
  5. package/dist/bin/quarry.mjs.map +1 -1
  6. package/dist/cache/index.d.mts +8 -46
  7. package/dist/cache/index.d.mts.map +1 -1
  8. package/dist/cache/index.mjs +134 -97
  9. package/dist/cache/index.mjs.map +1 -1
  10. package/dist/{cache.service-DsnKuNyO.d.mts → cache.service-uElmBtdS.d.mts} +29 -39
  11. package/dist/cache.service-uElmBtdS.d.mts.map +1 -0
  12. package/dist/{command-BgSlsS4M.mjs → command-BvmUAPPQ.mjs} +15 -4
  13. package/dist/command-BvmUAPPQ.mjs.map +1 -0
  14. package/dist/{command-Cmmf0oHX.d.mts → command-CPhFHjG3.d.mts} +3 -2
  15. package/dist/command-CPhFHjG3.d.mts.map +1 -0
  16. package/dist/command-not-found.error-ONAZ2Bpk.mjs +14 -0
  17. package/dist/command-not-found.error-ONAZ2Bpk.mjs.map +1 -0
  18. package/dist/config/index.d.mts +24 -11
  19. package/dist/config/index.d.mts.map +1 -1
  20. package/dist/config/index.mjs +32 -57
  21. package/dist/config/index.mjs.map +1 -1
  22. package/dist/{consumer-registry-B7yUNh0q.d.mts → consumer-registry-D3iMTSdy.d.mts} +54 -22
  23. package/dist/consumer-registry-D3iMTSdy.d.mts.map +1 -0
  24. package/dist/container-storage-BmOJ4_Na.mjs +52 -0
  25. package/dist/container-storage-BmOJ4_Na.mjs.map +1 -0
  26. package/dist/{controller.decorator-B9vwn0zK.mjs → controller.decorator-C5UVeJS3.mjs} +8 -8
  27. package/dist/controller.decorator-C5UVeJS3.mjs.map +1 -0
  28. package/dist/cron/index.d.mts +103 -7
  29. package/dist/cron/index.d.mts.map +1 -1
  30. package/dist/cron/index.mjs +2 -2
  31. package/dist/cron-job-NesZRk8F.d.mts +58 -0
  32. package/dist/cron-job-NesZRk8F.d.mts.map +1 -0
  33. package/dist/{cron-manager-DQSK8uoV.mjs → cron.module-Bgzq5hiT.mjs} +47 -17
  34. package/dist/cron.module-Bgzq5hiT.mjs.map +1 -0
  35. package/dist/decorate-CuAoSZvs.mjs +16 -0
  36. package/dist/deep-merge-ByiAOZ3r.mjs +18 -0
  37. package/dist/deep-merge-ByiAOZ3r.mjs.map +1 -0
  38. package/dist/di/index.d.mts +2 -2
  39. package/dist/di/index.mjs +4 -3
  40. package/dist/di-DseMn-z9.mjs +524 -0
  41. package/dist/di-DseMn-z9.mjs.map +1 -0
  42. package/dist/email/index.d.mts +40 -122
  43. package/dist/email/index.d.mts.map +1 -1
  44. package/dist/email/index.mjs +446 -131
  45. package/dist/email/index.mjs.map +1 -1
  46. package/dist/en-CDZBMcc1.mjs +202 -0
  47. package/dist/en-CDZBMcc1.mjs.map +1 -0
  48. package/dist/{env-D1rcZ8_r.d.mts → env-ug22bJj7.d.mts} +1 -1
  49. package/dist/env-ug22bJj7.d.mts.map +1 -0
  50. package/dist/errors/index.d.mts +2 -2
  51. package/dist/errors/index.mjs +4 -2
  52. package/dist/errors-mXYxG0XB.mjs +333 -0
  53. package/dist/errors-mXYxG0XB.mjs.map +1 -0
  54. package/dist/events/index.d.mts +14 -3
  55. package/dist/events/index.d.mts.map +1 -1
  56. package/dist/events/index.mjs +2 -2
  57. package/dist/{events-CzCV8jI8.mjs → events-BXJGZjpG.mjs} +23 -13
  58. package/dist/events-BXJGZjpG.mjs.map +1 -0
  59. package/dist/exception-context-kEoMFwze.mjs +429 -0
  60. package/dist/exception-context-kEoMFwze.mjs.map +1 -0
  61. package/dist/{gateway-context-CXmXtaUP.mjs → gateway-context-TMu_AlJt.mjs} +38 -31
  62. package/dist/gateway-context-TMu_AlJt.mjs.map +1 -0
  63. package/dist/guards/index.d.mts +3 -3
  64. package/dist/guards/index.d.mts.map +1 -1
  65. package/dist/guards/index.mjs +1 -1
  66. package/dist/{guards-DU1_J9YA.mjs → guards-DALPXy3_.mjs} +6 -5
  67. package/dist/guards-DALPXy3_.mjs.map +1 -0
  68. package/dist/hono-app-CvV3hOfT.mjs +161 -0
  69. package/dist/hono-app-CvV3hOfT.mjs.map +1 -0
  70. package/dist/{http-method.decorator-BrgHMdLQ.mjs → http-method.decorator-ByWZb9DO.mjs} +7 -6
  71. package/dist/http-method.decorator-ByWZb9DO.mjs.map +1 -0
  72. package/dist/i18n/index.d.mts +238 -3
  73. package/dist/i18n/index.d.mts.map +1 -0
  74. package/dist/i18n/index.mjs +39 -3
  75. package/dist/i18n/index.mjs.map +1 -0
  76. package/dist/i18n/messages/en/index.d.mts +2 -2
  77. package/dist/i18n/messages/en/index.mjs +2 -2
  78. package/dist/i18n/utils/index.d.mts +4 -26
  79. package/dist/i18n/utils/index.d.mts.map +1 -1
  80. package/dist/i18n/utils/index.mjs +2 -2
  81. package/dist/i18n/validation/index.d.mts +3 -2
  82. package/dist/i18n/validation/index.mjs +4 -2
  83. package/dist/i18n.module-DRQAZoSZ.mjs +222 -0
  84. package/dist/i18n.module-DRQAZoSZ.mjs.map +1 -0
  85. package/dist/i18n.tokens-CZ_v8oyS.mjs +19 -0
  86. package/dist/i18n.tokens-CZ_v8oyS.mjs.map +1 -0
  87. package/dist/{index-7-hU3GTV.d.mts → index-0ItCjaqw.d.mts} +1 -1
  88. package/dist/index-0ItCjaqw.d.mts.map +1 -0
  89. package/dist/index-B5JBRcWD.d.mts +544 -0
  90. package/dist/index-B5JBRcWD.d.mts.map +1 -0
  91. package/dist/index-BUt92sAE.d.mts +124 -0
  92. package/dist/index-BUt92sAE.d.mts.map +1 -0
  93. package/dist/{index-ByOyTmqf.d.mts → index-B_JoEl3V.d.mts} +751 -2229
  94. package/dist/index-B_JoEl3V.d.mts.map +1 -0
  95. package/dist/index-DtBNIFuP.d.mts +42 -0
  96. package/dist/index-DtBNIFuP.d.mts.map +1 -0
  97. package/dist/{index-C1KvMncZ.d.mts → index-HgOLNruQ.d.mts} +3 -108
  98. package/dist/index-HgOLNruQ.d.mts.map +1 -0
  99. package/dist/index.d.mts +6 -43
  100. package/dist/index.mjs +3 -2
  101. package/dist/{is-command-C6a7WTPw.mjs → is-command-CEPO9n8c.mjs} +2 -2
  102. package/dist/{is-command-C6a7WTPw.mjs.map → is-command-CEPO9n8c.mjs.map} +1 -1
  103. package/dist/{is-seeder-CebjZCDn.mjs → is-seeder-Gvh_AM71.mjs} +1 -1
  104. package/dist/{is-seeder-CebjZCDn.mjs.map → is-seeder-Gvh_AM71.mjs.map} +1 -1
  105. package/dist/lazy-module-loader-Ib383jH_.d.mts +60 -0
  106. package/dist/lazy-module-loader-Ib383jH_.d.mts.map +1 -0
  107. package/dist/locale-path.service-D-dHiIPc.mjs +165 -0
  108. package/dist/locale-path.service-D-dHiIPc.mjs.map +1 -0
  109. package/dist/locale-url-nZrZxqJP.mjs +44 -0
  110. package/dist/locale-url-nZrZxqJP.mjs.map +1 -0
  111. package/dist/locale-url.service-C2EWmGdq.mjs +41 -0
  112. package/dist/locale-url.service-C2EWmGdq.mjs.map +1 -0
  113. package/dist/logger/index.d.mts +2 -2
  114. package/dist/logger/index.mjs +170 -2
  115. package/dist/logger/index.mjs.map +1 -0
  116. package/dist/macroable/index.d.mts +2 -2
  117. package/dist/macroable/index.mjs +1 -1
  118. package/dist/{macroable-BmufBshB.mjs → macroable-cvDTFZ_A.mjs} +1 -1
  119. package/dist/{macroable-BmufBshB.mjs.map → macroable-cvDTFZ_A.mjs.map} +1 -1
  120. package/dist/metadata-DzzprcID.mjs +39 -0
  121. package/dist/metadata-DzzprcID.mjs.map +1 -0
  122. package/dist/module/index.d.mts +7 -24
  123. package/dist/module/index.d.mts.map +1 -1
  124. package/dist/module/index.mjs +10 -2
  125. package/dist/module/index.mjs.map +1 -0
  126. package/dist/module-registry-Dm-pqHd3.mjs +554 -0
  127. package/dist/module-registry-Dm-pqHd3.mjs.map +1 -0
  128. package/dist/module.decorator-CYHY6pG5.mjs +19 -0
  129. package/dist/module.decorator-CYHY6pG5.mjs.map +1 -0
  130. package/dist/openapi/index.d.mts +44 -8
  131. package/dist/openapi/index.d.mts.map +1 -1
  132. package/dist/openapi/index.mjs +3 -3
  133. package/dist/openapi-CstuTM8S.mjs +309 -0
  134. package/dist/openapi-CstuTM8S.mjs.map +1 -0
  135. package/dist/{openapi-tools.service-Zs-Ewv7F.mjs → openapi-tools.service-BC5EC3R3.mjs} +8 -2
  136. package/dist/openapi-tools.service-BC5EC3R3.mjs.map +1 -0
  137. package/dist/{openapi.service-Bt9bCIrd.d.mts → openapi.service-YhTiJ1bO.d.mts} +3 -3
  138. package/dist/openapi.service-YhTiJ1bO.d.mts.map +1 -0
  139. package/dist/quarry/index.d.mts +14 -163
  140. package/dist/quarry/index.d.mts.map +1 -1
  141. package/dist/quarry/index.mjs +6 -5
  142. package/dist/quarry/runner.d.mts +184 -0
  143. package/dist/quarry/runner.d.mts.map +1 -0
  144. package/dist/quarry/runner.mjs +945 -0
  145. package/dist/quarry/runner.mjs.map +1 -0
  146. package/dist/quarry-registry-CXg0RFXq.d.mts +69 -0
  147. package/dist/quarry-registry-CXg0RFXq.d.mts.map +1 -0
  148. package/dist/quarry.module-BuRPGMDm.mjs +312 -0
  149. package/dist/quarry.module-BuRPGMDm.mjs.map +1 -0
  150. package/dist/queue/index.d.mts +3 -3
  151. package/dist/queue/index.mjs +57 -48
  152. package/dist/queue/index.mjs.map +1 -1
  153. package/dist/queue.module-nddvxzCB.mjs +613 -0
  154. package/dist/queue.module-nddvxzCB.mjs.map +1 -0
  155. package/dist/queue.tokens-DjHnFmre.mjs +11 -0
  156. package/dist/queue.tokens-DjHnFmre.mjs.map +1 -0
  157. package/dist/{r2-storage.provider-DuonKeYm.mjs → r2-storage.provider-DCxQt9dD.mjs} +6 -6
  158. package/dist/r2-storage.provider-DCxQt9dD.mjs.map +1 -0
  159. package/dist/{rate-limit.decorator-6qzNcSOt.mjs → rate-limit.decorator-BPAie_p3.mjs} +6 -11
  160. package/dist/rate-limit.decorator-BPAie_p3.mjs.map +1 -0
  161. package/dist/rate-limiter/index.d.mts +11 -50
  162. package/dist/rate-limiter/index.d.mts.map +1 -1
  163. package/dist/rate-limiter/index.mjs +33 -42
  164. package/dist/rate-limiter/index.mjs.map +1 -1
  165. package/dist/route-name-DGoBOfPg.mjs +171 -0
  166. package/dist/route-name-DGoBOfPg.mjs.map +1 -0
  167. package/dist/route-registration.service-D6vSwiKP.mjs +918 -0
  168. package/dist/route-registration.service-D6vSwiKP.mjs.map +1 -0
  169. package/dist/route-registry-CYqLp2Nj.mjs +123 -0
  170. package/dist/route-registry-CYqLp2Nj.mjs.map +1 -0
  171. package/dist/router/index.d.mts +2 -2
  172. package/dist/router/index.mjs +18 -7
  173. package/dist/router-CWGBD-Bg.mjs +78 -0
  174. package/dist/router-CWGBD-Bg.mjs.map +1 -0
  175. package/dist/router-resolver-D4YlPNlm.mjs +88 -0
  176. package/dist/router-resolver-D4YlPNlm.mjs.map +1 -0
  177. package/dist/seeder/index.d.mts +16 -11
  178. package/dist/seeder/index.d.mts.map +1 -1
  179. package/dist/seeder/index.mjs +5 -3
  180. package/dist/seeder-7ubkms-Y.mjs +81 -0
  181. package/dist/seeder-7ubkms-Y.mjs.map +1 -0
  182. package/dist/seeder-registry-CyUmKsJq.mjs +57 -0
  183. package/dist/seeder-registry-CyUmKsJq.mjs.map +1 -0
  184. package/dist/seeder.module-CYYwk3Qk.mjs +15 -0
  185. package/dist/seeder.module-CYYwk3Qk.mjs.map +1 -0
  186. package/dist/{signed-url-BQPbv2In.mjs → signed-url-DIU0sK_6.mjs} +1 -1
  187. package/dist/{signed-url-BQPbv2In.mjs.map → signed-url-DIU0sK_6.mjs.map} +1 -1
  188. package/dist/storage/index.d.mts +15 -39
  189. package/dist/storage/index.d.mts.map +1 -1
  190. package/dist/storage/index.mjs +3 -3
  191. package/dist/storage/providers/index.d.mts +2 -2
  192. package/dist/storage/providers/index.d.mts.map +1 -1
  193. package/dist/storage/providers/index.mjs +1 -1
  194. package/dist/{storage-D8CBP72Z.mjs → storage-MDZypIE9.mjs} +66 -59
  195. package/dist/storage-MDZypIE9.mjs.map +1 -0
  196. package/dist/{storage-provider.interface-Bd6vA4ak.d.mts → storage-provider.interface-ClUwxz4S.d.mts} +2 -3
  197. package/dist/storage-provider.interface-ClUwxz4S.d.mts.map +1 -0
  198. package/dist/storage.error-Dnib4VHc.mjs +8 -0
  199. package/dist/storage.error-Dnib4VHc.mjs.map +1 -0
  200. package/dist/stratal-DL9M38_s.mjs +383 -0
  201. package/dist/stratal-DL9M38_s.mjs.map +1 -0
  202. package/dist/stratal-DwDJPY9N.d.mts +43 -0
  203. package/dist/stratal-DwDJPY9N.d.mts.map +1 -0
  204. package/dist/tiered-cache.service-Dv3BhxxE.d.mts +79 -0
  205. package/dist/tiered-cache.service-Dv3BhxxE.d.mts.map +1 -0
  206. package/dist/trailing-slash-CFyw8nYu.mjs +34 -0
  207. package/dist/trailing-slash-CFyw8nYu.mjs.map +1 -0
  208. package/dist/{types-cySNS_lp.d.mts → types-CmV_9xBD.d.mts} +1 -1
  209. package/dist/types-CmV_9xBD.d.mts.map +1 -0
  210. package/dist/uri-h7Q8Jug9.mjs +251 -0
  211. package/dist/uri-h7Q8Jug9.mjs.map +1 -0
  212. package/dist/{usage-generator-BUdlhnCK.mjs → usage-generator-DAWYasuP.mjs} +7 -4
  213. package/dist/usage-generator-DAWYasuP.mjs.map +1 -0
  214. package/dist/validation-CpOjviyT.mjs +49 -0
  215. package/dist/validation-CpOjviyT.mjs.map +1 -0
  216. package/dist/validation.context-CRvmrhq7.mjs +117 -0
  217. package/dist/validation.context-CRvmrhq7.mjs.map +1 -0
  218. package/dist/versioning.service-C6aHky8-.mjs +36 -0
  219. package/dist/versioning.service-C6aHky8-.mjs.map +1 -0
  220. package/dist/websocket/index.d.mts +16 -14
  221. package/dist/websocket/index.d.mts.map +1 -1
  222. package/dist/websocket/index.mjs +2 -2
  223. package/dist/workers/index.d.mts +2 -2
  224. package/dist/workers/index.d.mts.map +1 -1
  225. package/dist/workers/index.mjs +3 -2
  226. package/dist/workers/index.mjs.map +1 -1
  227. package/dist/zod-eKqqhZ5_.mjs +72 -0
  228. package/dist/zod-eKqqhZ5_.mjs.map +1 -0
  229. package/dist/{index-Bnpfq6uk.d.mts → zod-wecrEVAs.d.mts} +63 -133
  230. package/dist/zod-wecrEVAs.d.mts.map +1 -0
  231. package/package.json +28 -39
  232. package/dist/base-email.provider-CfQCA08m.mjs +0 -42
  233. package/dist/base-email.provider-CfQCA08m.mjs.map +0 -1
  234. package/dist/cache.service-DsnKuNyO.d.mts.map +0 -1
  235. package/dist/cache.tokens-B7Rw1C9Q.mjs +0 -6
  236. package/dist/cache.tokens-B7Rw1C9Q.mjs.map +0 -1
  237. package/dist/colors-DJaRDXoS.mjs +0 -16
  238. package/dist/colors-DJaRDXoS.mjs.map +0 -1
  239. package/dist/command-BgSlsS4M.mjs.map +0 -1
  240. package/dist/command-Cmmf0oHX.d.mts.map +0 -1
  241. package/dist/consumer-registry-B7yUNh0q.d.mts.map +0 -1
  242. package/dist/controller.decorator-B9vwn0zK.mjs.map +0 -1
  243. package/dist/cron-manager-CmTimEjf.d.mts +0 -131
  244. package/dist/cron-manager-CmTimEjf.d.mts.map +0 -1
  245. package/dist/cron-manager-DQSK8uoV.mjs.map +0 -1
  246. package/dist/en-DSH_bhh6.mjs +0 -308
  247. package/dist/en-DSH_bhh6.mjs.map +0 -1
  248. package/dist/env-D1rcZ8_r.d.mts.map +0 -1
  249. package/dist/errors-COW9-Mar.mjs +0 -1739
  250. package/dist/errors-COW9-Mar.mjs.map +0 -1
  251. package/dist/errors-ORxu1-Bb.mjs +0 -74
  252. package/dist/errors-ORxu1-Bb.mjs.map +0 -1
  253. package/dist/events-CzCV8jI8.mjs.map +0 -1
  254. package/dist/gateway-context-CXmXtaUP.mjs.map +0 -1
  255. package/dist/guards-DU1_J9YA.mjs.map +0 -1
  256. package/dist/http-method.decorator-BrgHMdLQ.mjs.map +0 -1
  257. package/dist/i18n.module-CzXLW9Hy.mjs +0 -2532
  258. package/dist/i18n.module-CzXLW9Hy.mjs.map +0 -1
  259. package/dist/index-7-hU3GTV.d.mts.map +0 -1
  260. package/dist/index-Bnpfq6uk.d.mts.map +0 -1
  261. package/dist/index-ByOyTmqf.d.mts.map +0 -1
  262. package/dist/index-C1KvMncZ.d.mts.map +0 -1
  263. package/dist/index-DBd_2wv8.d.mts +0 -263
  264. package/dist/index-DBd_2wv8.d.mts.map +0 -1
  265. package/dist/index-DUzWs0z7.d.mts +0 -494
  266. package/dist/index-DUzWs0z7.d.mts.map +0 -1
  267. package/dist/index.d.mts.map +0 -1
  268. package/dist/logger-DlV7NtvD.mjs +0 -440
  269. package/dist/logger-DlV7NtvD.mjs.map +0 -1
  270. package/dist/module-BzLg57FK.mjs +0 -866
  271. package/dist/module-BzLg57FK.mjs.map +0 -1
  272. package/dist/openapi-tools.service-Zs-Ewv7F.mjs.map +0 -1
  273. package/dist/openapi.service-Bt9bCIrd.d.mts.map +0 -1
  274. package/dist/quarry-registry-BwY2hOxm.mjs +0 -699
  275. package/dist/quarry-registry-BwY2hOxm.mjs.map +0 -1
  276. package/dist/queue.module-BhCjZp6H.mjs +0 -409
  277. package/dist/queue.module-BhCjZp6H.mjs.map +0 -1
  278. package/dist/r2-storage.provider-DuonKeYm.mjs.map +0 -1
  279. package/dist/rate-limit.decorator-6qzNcSOt.mjs.map +0 -1
  280. package/dist/resend.provider-DB4IlFjG.mjs +0 -68
  281. package/dist/resend.provider-DB4IlFjG.mjs.map +0 -1
  282. package/dist/seeder-zoEfEw9i.mjs +0 -138
  283. package/dist/seeder-zoEfEw9i.mjs.map +0 -1
  284. package/dist/setup-CefZKV_e.mjs +0 -37
  285. package/dist/setup-CefZKV_e.mjs.map +0 -1
  286. package/dist/smtp.provider-B6D7zuWX.mjs +0 -76
  287. package/dist/smtp.provider-B6D7zuWX.mjs.map +0 -1
  288. package/dist/storage-D8CBP72Z.mjs.map +0 -1
  289. package/dist/storage-provider.interface-Bd6vA4ak.d.mts.map +0 -1
  290. package/dist/stratal-CNwpbSZl.mjs +0 -535
  291. package/dist/stratal-CNwpbSZl.mjs.map +0 -1
  292. package/dist/types-cySNS_lp.d.mts.map +0 -1
  293. package/dist/usage-generator-BUdlhnCK.mjs.map +0 -1
  294. package/dist/validation-DtJwAv7O.mjs +0 -248
  295. package/dist/validation-DtJwAv7O.mjs.map +0 -1
  296. /package/dist/{chunk-D1SwGrFN.mjs → chunk-BBjsoOtd.mjs} +0 -0
package/README.md CHANGED
@@ -20,14 +20,14 @@ Full guides and examples are available at **[stratal.dev](https://stratal.dev)**
20
20
 
21
21
  ## Features
22
22
 
23
- - **Dependency Injection** — Two-tier DI container (global + request-scoped) powered by tsyringe
23
+ - **Dependency Injection** — Two-tier DI container (global + request-scoped) with built-in decorator support
24
24
  - **OpenAPI Documentation** — Define Zod schemas once and get a full OpenAPI 3.0 spec with interactive docs
25
25
  - **Modular Architecture** — NestJS-style modules with lifecycle hooks, dynamic configuration, and middleware
26
26
  - **Hono Routing** — Convention-based RESTful controllers with automatic HTTP method mapping
27
27
  - **Queue Consumers** — Typed Cloudflare Queue consumers with message-type filtering
28
28
  - **Cron Jobs** — Scheduled tasks via Cloudflare Workers cron triggers
29
29
  - **Storage** — S3-compatible file storage with presigned URLs and TUS upload support
30
- - **Email** — Resend and SMTP providers with React Email template support
30
+ - **Email** — SMTP provider with React Email template support
31
31
  - **i18n** — Type-safe internationalization with locale detection from request headers
32
32
  - **Guards and Middleware** — Route protection and per-module middleware configuration
33
33
 
@@ -3,18 +3,24 @@ import { readFileSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  //#region src/bin/cloudflare-workers-loader.ts
5
5
  /**
6
- * ESM loader hook that provides a virtual `cloudflare:workers` module
6
+ * ESM loader hook that provides virtual modules for Cloudflare-specific imports
7
7
  * and handles Vite-style `?raw` imports (returning file contents as a string).
8
8
  *
9
- * When registered via `node --import` or `register()`, this intercepts
10
- * `import('cloudflare:workers')` and returns a module that reads `env`
11
- * and `waitUntil` from `globalThis.__stratalPlatformProxy`.
9
+ * When registered via `node --import` or `register()`, this intercepts:
10
+ * - `cloudflare:workers` virtual env/waitUntil from `globalThis.__stratalPlatformProxy`
11
+ * - `cloudflare:sockets` Node.js TCP/TLS implementation of the CF connect() API
12
+ * - `?raw` imports — returns file contents as a default string export
12
13
  */
13
- const VIRTUAL_URL = "cloudflare-workers:virtual";
14
+ const VIRTUAL_WORKERS_URL = "cloudflare-workers:virtual";
15
+ const VIRTUAL_SOCKETS_URL = "cloudflare-sockets:virtual";
14
16
  const RAW_SUFFIX = "?raw";
15
17
  async function resolve(specifier, context, nextResolve) {
16
18
  if (specifier === "cloudflare:workers") return {
17
- url: VIRTUAL_URL,
19
+ url: VIRTUAL_WORKERS_URL,
20
+ shortCircuit: true
21
+ };
22
+ if (specifier === "cloudflare:sockets") return {
23
+ url: VIRTUAL_SOCKETS_URL,
18
24
  shortCircuit: true
19
25
  };
20
26
  if (specifier.endsWith(RAW_SUFFIX)) return {
@@ -32,7 +38,7 @@ async function load(url, context, nextLoad) {
32
38
  source: `export default ${JSON.stringify(content)}`
33
39
  };
34
40
  }
35
- if (url === VIRTUAL_URL) return {
41
+ if (url === VIRTUAL_WORKERS_URL) return {
36
42
  format: "module",
37
43
  shortCircuit: true,
38
44
  source: `
@@ -56,6 +62,73 @@ export const RpcStub = function(value) { return value; };
56
62
  export function withEnv(newEnv, fn) { return fn(); }
57
63
  export function withExports(newExports, fn) { return fn(); }
58
64
  export function withEnvAndExports(newEnv, newExports, fn) { return fn(); }
65
+ `
66
+ };
67
+ if (url === VIRTUAL_SOCKETS_URL) return {
68
+ format: "module",
69
+ shortCircuit: true,
70
+ source: `
71
+ import { connect as netConnect } from 'node:net';
72
+ import { connect as tlsConnect } from 'node:tls';
73
+
74
+ export function connect(address, options) {
75
+ const hostname = typeof address === 'string' ? address : address.hostname;
76
+ const port = typeof address === 'string' ? 443 : address.port;
77
+ const secureTransport = options?.secureTransport ?? 'off';
78
+
79
+ let currentSocket;
80
+ if (secureTransport === 'on') {
81
+ currentSocket = tlsConnect({ host: hostname, port, servername: hostname });
82
+ } else {
83
+ currentSocket = netConnect({ host: hostname, port });
84
+ }
85
+
86
+ let dataHandler, endHandler, errorHandler;
87
+
88
+ const readable = new ReadableStream({
89
+ start(controller) {
90
+ dataHandler = (chunk) => {
91
+ try { controller.enqueue(new Uint8Array(chunk)); } catch {}
92
+ };
93
+ endHandler = () => {
94
+ try { controller.close(); } catch {}
95
+ };
96
+ errorHandler = (err) => {
97
+ try { controller.error(err); } catch {}
98
+ };
99
+ currentSocket.on('data', dataHandler);
100
+ currentSocket.on('end', endHandler);
101
+ currentSocket.on('error', errorHandler);
102
+ }
103
+ });
104
+
105
+ const writable = new WritableStream({
106
+ write(chunk) {
107
+ return new Promise((resolve, reject) => {
108
+ currentSocket.write(Buffer.from(chunk), (err) => err ? reject(err) : resolve());
109
+ });
110
+ }
111
+ });
112
+
113
+ const closedPromise = new Promise((resolve) => currentSocket.on('close', resolve));
114
+
115
+ return {
116
+ readable,
117
+ writable,
118
+ startTls() {
119
+ currentSocket.removeListener('data', dataHandler);
120
+ currentSocket.removeListener('end', endHandler);
121
+ currentSocket.removeListener('error', errorHandler);
122
+ const tlsSocket = tlsConnect({ socket: currentSocket, servername: hostname });
123
+ currentSocket = tlsSocket;
124
+ tlsSocket.on('data', dataHandler);
125
+ tlsSocket.on('end', endHandler);
126
+ tlsSocket.on('error', errorHandler);
127
+ },
128
+ close() { currentSocket.destroy(); },
129
+ closed: closedPromise,
130
+ };
131
+ }
59
132
  `
60
133
  };
61
134
  return nextLoad(url, context);
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-workers-loader.mjs","names":[],"sources":["../../src/bin/cloudflare-workers-loader.ts"],"sourcesContent":["/**\n * ESM loader hook that provides a virtual `cloudflare:workers` module\n * and handles Vite-style `?raw` imports (returning file contents as a string).\n *\n * When registered via `node --import` or `register()`, this intercepts\n * `import('cloudflare:workers')` and returns a module that reads `env`\n * and `waitUntil` from `globalThis.__stratalPlatformProxy`.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nconst VIRTUAL_URL = 'cloudflare-workers:virtual'\nconst RAW_SUFFIX = '?raw'\n\ninterface ResolveContext {\n parentURL?: string\n conditions: string[]\n}\n\ninterface ResolveResult {\n url: string\n shortCircuit?: boolean\n}\n\ntype NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>\n\nexport async function resolve(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve,\n): Promise<ResolveResult> {\n if (specifier === 'cloudflare:workers') {\n return { url: VIRTUAL_URL, shortCircuit: true }\n }\n if (specifier.endsWith(RAW_SUFFIX)) {\n const base = specifier.slice(0, -RAW_SUFFIX.length)\n const resolved = await nextResolve(base, context)\n return { url: resolved.url + RAW_SUFFIX, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n}\n\ninterface LoadContext {\n format?: string\n conditions: string[]\n}\n\ninterface LoadResult {\n format: string\n source: string\n shortCircuit?: boolean\n}\n\ntype NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>\n\nexport async function load(\n url: string,\n context: LoadContext,\n nextLoad: NextLoad,\n): Promise<LoadResult> {\n if (url.endsWith(RAW_SUFFIX)) {\n const fileUrl = url.slice(0, -RAW_SUFFIX.length)\n const filePath = fileURLToPath(fileUrl)\n const content = readFileSync(filePath, 'utf-8')\n return {\n format: 'module',\n shortCircuit: true,\n source: `export default ${JSON.stringify(content)}`,\n }\n }\n if (url === VIRTUAL_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nconst proxy = globalThis.__stratalPlatformProxy;\nif (!proxy) throw new Error('globalThis.__stratalPlatformProxy not set — Quarry CLI must initialize it before importing the app entry.');\nexport const env = proxy.env;\nexport const waitUntil = proxy.waitUntil;\nexport const exports = {}\nexport class DurableObject {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkerEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowStep {}\nexport class RpcTarget {}\nexport const RpcStub = function(value) { return value; };\nexport function withEnv(newEnv, fn) { return fn(); }\nexport function withExports(newExports, fn) { return fn(); }\nexport function withEnvAndExports(newEnv, newExports, fn) { return fn(); }\n`,\n }\n }\n return nextLoad(url, context)\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,cAAc;AACpB,MAAM,aAAa;AAcnB,eAAsB,QACpB,WACA,SACA,aACwB;CACxB,IAAI,cAAc,sBAChB,OAAO;EAAE,KAAK;EAAa,cAAc;EAAM;CAEjD,IAAI,UAAU,SAAS,WAAW,EAGhC,OAAO;EAAE,MAAK,MADS,YADV,UAAU,MAAM,GAAG,GACO,EAAE,QAAQ,EAC1B,MAAM;EAAY,cAAc;EAAM;CAE/D,OAAO,YAAY,WAAW,QAAQ;;AAgBxC,eAAsB,KACpB,KACA,SACA,UACqB;CACrB,IAAI,IAAI,SAAS,WAAW,EAAE;EAG5B,MAAM,UAAU,aADC,cADD,IAAI,MAAM,GAAG,GACS,CACD,EAAE,QAAQ;EAC/C,OAAO;GACL,QAAQ;GACR,cAAc;GACd,QAAQ,kBAAkB,KAAK,UAAU,QAAQ;GAClD;;CAEH,IAAI,QAAQ,aACV,OAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;EAsBT;CAEH,OAAO,SAAS,KAAK,QAAQ"}
1
+ {"version":3,"file":"cloudflare-workers-loader.mjs","names":[],"sources":["../../src/bin/cloudflare-workers-loader.ts"],"sourcesContent":["/**\n * ESM loader hook that provides virtual modules for Cloudflare-specific imports\n * and handles Vite-style `?raw` imports (returning file contents as a string).\n *\n * When registered via `node --import` or `register()`, this intercepts:\n * - `cloudflare:workers` virtual env/waitUntil from `globalThis.__stratalPlatformProxy`\n * - `cloudflare:sockets` Node.js TCP/TLS implementation of the CF connect() API\n * - `?raw` imports — returns file contents as a default string export\n */\n\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\n\nconst VIRTUAL_WORKERS_URL = 'cloudflare-workers:virtual'\nconst VIRTUAL_SOCKETS_URL = 'cloudflare-sockets:virtual'\nconst RAW_SUFFIX = '?raw'\n\ninterface ResolveContext {\n parentURL?: string\n conditions: string[]\n}\n\ninterface ResolveResult {\n url: string\n shortCircuit?: boolean\n}\n\ntype NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>\n\nexport async function resolve(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve,\n): Promise<ResolveResult> {\n if (specifier === 'cloudflare:workers') {\n return { url: VIRTUAL_WORKERS_URL, shortCircuit: true }\n }\n if (specifier === 'cloudflare:sockets') {\n return { url: VIRTUAL_SOCKETS_URL, shortCircuit: true }\n }\n if (specifier.endsWith(RAW_SUFFIX)) {\n const base = specifier.slice(0, -RAW_SUFFIX.length)\n const resolved = await nextResolve(base, context)\n return { url: resolved.url + RAW_SUFFIX, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n}\n\ninterface LoadContext {\n format?: string\n conditions: string[]\n}\n\ninterface LoadResult {\n format: string\n source: string\n shortCircuit?: boolean\n}\n\ntype NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>\n\nexport async function load(\n url: string,\n context: LoadContext,\n nextLoad: NextLoad,\n): Promise<LoadResult> {\n if (url.endsWith(RAW_SUFFIX)) {\n const fileUrl = url.slice(0, -RAW_SUFFIX.length)\n const filePath = fileURLToPath(fileUrl)\n const content = readFileSync(filePath, 'utf-8')\n return {\n format: 'module',\n shortCircuit: true,\n source: `export default ${JSON.stringify(content)}`,\n }\n }\n if (url === VIRTUAL_WORKERS_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nconst proxy = globalThis.__stratalPlatformProxy;\nif (!proxy) throw new Error('globalThis.__stratalPlatformProxy not set — Quarry CLI must initialize it before importing the app entry.');\nexport const env = proxy.env;\nexport const waitUntil = proxy.waitUntil;\nexport const exports = {}\nexport class DurableObject {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkerEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowEntrypoint {\n constructor(ctx, env) { this.ctx = ctx; this.env = env; }\n}\nexport class WorkflowStep {}\nexport class RpcTarget {}\nexport const RpcStub = function(value) { return value; };\nexport function withEnv(newEnv, fn) { return fn(); }\nexport function withExports(newExports, fn) { return fn(); }\nexport function withEnvAndExports(newEnv, newExports, fn) { return fn(); }\n`,\n }\n }\n if (url === VIRTUAL_SOCKETS_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nimport { connect as netConnect } from 'node:net';\nimport { connect as tlsConnect } from 'node:tls';\n\nexport function connect(address, options) {\n const hostname = typeof address === 'string' ? address : address.hostname;\n const port = typeof address === 'string' ? 443 : address.port;\n const secureTransport = options?.secureTransport ?? 'off';\n\n let currentSocket;\n if (secureTransport === 'on') {\n currentSocket = tlsConnect({ host: hostname, port, servername: hostname });\n } else {\n currentSocket = netConnect({ host: hostname, port });\n }\n\n let dataHandler, endHandler, errorHandler;\n\n const readable = new ReadableStream({\n start(controller) {\n dataHandler = (chunk) => {\n try { controller.enqueue(new Uint8Array(chunk)); } catch {}\n };\n endHandler = () => {\n try { controller.close(); } catch {}\n };\n errorHandler = (err) => {\n try { controller.error(err); } catch {}\n };\n currentSocket.on('data', dataHandler);\n currentSocket.on('end', endHandler);\n currentSocket.on('error', errorHandler);\n }\n });\n\n const writable = new WritableStream({\n write(chunk) {\n return new Promise((resolve, reject) => {\n currentSocket.write(Buffer.from(chunk), (err) => err ? reject(err) : resolve());\n });\n }\n });\n\n const closedPromise = new Promise((resolve) => currentSocket.on('close', resolve));\n\n return {\n readable,\n writable,\n startTls() {\n currentSocket.removeListener('data', dataHandler);\n currentSocket.removeListener('end', endHandler);\n currentSocket.removeListener('error', errorHandler);\n const tlsSocket = tlsConnect({ socket: currentSocket, servername: hostname });\n currentSocket = tlsSocket;\n tlsSocket.on('data', dataHandler);\n tlsSocket.on('end', endHandler);\n tlsSocket.on('error', errorHandler);\n },\n close() { currentSocket.destroy(); },\n closed: closedPromise,\n };\n}\n`,\n }\n }\n return nextLoad(url, context)\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAC5B,MAAM,aAAa;AAcnB,eAAsB,QACpB,WACA,SACA,aACwB;CACxB,IAAI,cAAc,sBAChB,OAAO;EAAE,KAAK;EAAqB,cAAc;CAAK;CAExD,IAAI,cAAc,sBAChB,OAAO;EAAE,KAAK;EAAqB,cAAc;CAAK;CAExD,IAAI,UAAU,SAAS,UAAU,GAG/B,OAAO;EAAE,MAAK,MADS,YADV,UAAU,MAAM,GAAG,EACM,GAAG,OAAO,GACzB,MAAM;EAAY,cAAc;CAAK;CAE9D,OAAO,YAAY,WAAW,OAAO;AACvC;AAeA,eAAsB,KACpB,KACA,SACA,UACqB;CACrB,IAAI,IAAI,SAAS,UAAU,GAAG;EAG5B,MAAM,UAAU,aADC,cADD,IAAI,MAAM,GAAG,EACQ,CACD,GAAG,OAAO;EAC9C,OAAO;GACL,QAAQ;GACR,cAAc;GACd,QAAQ,kBAAkB,KAAK,UAAU,OAAO;EAClD;CACF;CACA,IAAI,QAAQ,qBACV,OAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;CAsBV;CAEF,IAAI,QAAQ,qBACV,OAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+DV;CAEF,OAAO,SAAS,KAAK,OAAO;AAC9B"}
@@ -1,115 +1,41 @@
1
1
  #!/usr/bin/env -S node --no-warnings
2
2
  import { createRequire, register } from "node:module";
3
- import "reflect-metadata";
4
- import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { tmpdir } from "node:os";
3
+ import { existsSync } from "node:fs";
6
4
  import { dirname, join, resolve } from "node:path";
7
5
  import { URL, pathToFileURL } from "node:url";
8
6
  import { Command, Option } from "clipanion";
9
- //#region src/i18n/messages/en/errors.ts
10
- /**
11
- * System Error Messages - English
12
- *
13
- * Error messages used by packages/modules infrastructure.
14
- * These are automatically merged with application-specific messages.
15
- */
16
- const errors = {
17
- internalError: "An internal error occurred",
18
- notFound: "Resource not found",
19
- unauthorized: "Unauthorized. Please sign in.",
20
- forbidden: "Access denied",
21
- honoAppAlreadyConfigured: "HonoApp has already been configured and can only be configured once",
22
- routeNotFound: "Route not found: {method} {path}",
23
- routeAccessDenied: "Resource not found",
24
- controllerMethodNotFound: "Method {methodName} not found on {controllerName}",
25
- controllerRegistration: "Failed to register controller {controllerName}: {reason}",
26
- duplicateRouteName: "Duplicate route name \"{name}\". Already registered by {existingHandler}, cannot register {newHandler}.",
27
- routeNameNotFound: "Route \"{name}\" not found in registry.",
28
- missingRouteParam: "Missing required parameter \"{param}\" for route \"{name}\" (path: {path}).",
29
- routerUseScopeViolation: "router.use() can only be called on the root Router, not inside group() callbacks. Use router.middleware() for scoped middleware.",
30
- middlewareNextCalledMultipleTimes: "next() was called multiple times in \"{middlewareName}\" middleware. Ensure each middleware calls next() at most once.",
31
- missingEnvironmentVariable: "Environment variable \"{variable}\" is required but not set.",
32
- websocketBodyNotAvailable: "body() is not available in WebSocket gateways. Use WebSocket messages instead.",
33
- websocketDuplicateEventHandler: "@{decorator}() is already applied to '{existingMethod}'. Only one method per gateway can handle this event.",
34
- contextNotInitialized: "Context has not been initialized",
35
- userNotAuthenticated: "User is not authenticated",
36
- insufficientPermissions: "Insufficient permissions to perform this action",
37
- requestContainerNotInitialized: "Request container has not been initialized",
38
- requestScopeOperationNotAllowed: "{methodName}() cannot be called on this container scope. Check if you are calling it on the correct container (global vs request-scoped).",
39
- conditionalBindingFallback: "Conditional binding predicate returned false for token \"{token}\" but no fallback was provided and no existing registration exists.",
40
- configNotInitialized: "Configuration service has not been initialized",
41
- configModuleNotInitialized: "ConfigModule.forRoot() was not called before module initialization",
42
- stratalNotInitialized: "Stratal has not been instantiated. Ensure you export a Stratal instance as the default export.",
43
- moduleAlreadyRegistered: "Module {moduleName} is already registered",
44
- moduleDependencyNotFound: "Module dependency {dependency} not found for module {moduleName}",
45
- moduleCircularDependency: "Circular dependency detected: {cycle}",
46
- invalidModuleProvider: "Invalid module provider configuration: {provider}",
47
- databaseGeneric: "Database error occurred",
48
- databaseRecordNotFound: "Record not found in database",
49
- databaseUniqueConstraint: "Record already exists",
50
- databaseForeignKeyConstraint: "Related record not found",
51
- databaseConnectionFailed: "Failed to connect to database",
52
- databaseTimeout: "Database query timeout",
53
- databaseNullConstraint: "Required field is missing",
54
- databaseTooManyConnections: "Too many database connections",
55
- databaseTransactionConflict: "Transaction conflict or deadlock",
56
- databaseConstraintFailed: "A database constraint was violated",
57
- databaseTableNotFound: "The specified table does not exist in the database",
58
- databaseColumnNotFound: "The specified column does not exist in the table",
59
- databaseInvalidQuery: "The database query is invalid or malformed",
60
- invalidErrorCodeRange: "Invalid error code range: {code}",
61
- queueBindingNotFound: "Queue binding {queueName} not found in environment",
62
- queueProviderNotSupported: "Queue provider \"{provider}\" is not supported. Valid providers: cloudflare, sync",
63
- cronExecutionFailed: "{count} cron job(s) failed for schedule \"{schedule}\": {jobs}",
64
- localeNotSupported: "Locale '{locale}' is not supported. Supported locales: {supportedLocales}",
65
- translationMissing: "Translation missing for key '{key}' in locale '{locale}'",
66
- containerNotInitialized: "Application container has not been initialized. Ensure Application.initialize() has been called.",
67
- domainMismatch: "The requested domain does not match any configured route",
68
- invalidSignature: "The URL signature is invalid or has expired",
69
- schemaValidation: "Schema validation failed",
70
- responseValidation: "Response validation failed",
71
- openapiValidation: "OpenAPI validation failed: {details}",
72
- openapiRouteRegistration: "Failed to register OpenAPI route {path}: {reason}",
73
- email: {
74
- resendApiKeyMissing: "Resend API key not configured. Set RESEND_EMAIL_API_KEY environment variable.",
75
- smtpConfigurationMissing: "SMTP configuration missing. Set SMTP_URL environment variable.",
76
- smtpHostMissing: "SMTP host not configured. Check SMTP_URL format (smtp://user:pass@host:port).",
77
- smtpConnectionFailed: "Failed to connect to SMTP server {smtpHost}:{smtpPort}",
78
- resendApiFailed: "Resend API error",
79
- providerNotSupported: "Unsupported email provider: {provider}. Supported providers: resend, smtp"
80
- },
81
- storage: {
82
- fileNotFound: "File at path \"{path}\" was not found",
83
- invalidDisk: "Storage disk \"{disk}\" is not configured",
84
- invalidFileType: "File type \"{mimeType}\" is not allowed",
85
- fileTooLarge: "File size {size} exceeds maximum allowed size of {maxSize}",
86
- presignedUrlInvalidExpiry: "Expiry must be between {min} and {max} seconds",
87
- diskNotConfigured: "Disk \"{disk}\" is not configured",
88
- responseBodyMissing: "No body in storage response for path: {path}",
89
- r2BindingNotFound: "R2 binding \"{binding}\" was not found in the environment",
90
- r2PresignedUrlSecretMissing: "APP_SECRET environment variable is required for presigned URLs"
91
- },
92
- cache: {
93
- getFailed: "Failed to retrieve value from cache for key '{key}'",
94
- putFailed: "Failed to store value in cache for key '{key}'",
95
- deleteFailed: "Failed to delete value from cache for key '{key}'",
96
- listFailed: "Failed to list cache keys"
97
- },
98
- rateLimit: {
99
- tooManyRequests: "Too Many Requests",
100
- notDefined: "Rate limiter \"{name}\" is not defined. Register it via RateLimiterRegistry.for(\"{name}\", ...) inside a module's onInitialize hook.",
101
- notConfigured: "RateLimiterModule.forRoot() was not called. Pass { store: \"kv\" | \"memory\" | { useClass } } to enable rate limiting.",
102
- moduleNotImported: "Rate limiter \"{name}\" was used (router.throttle / @RateLimit) but RateLimiterModule is not imported in your AppModule. Add RateLimiterModule.forRoot({ store: ... }) to imports."
103
- },
104
- seederNameCollision: "Seeder name collision: \"{name}\" is already registered. Use distinct class names for each seeder.",
105
- seederNotRegistered: "Seeder \"{name}\" is not registered",
106
- migration: {
107
- failed: "Migration {migrationName} failed: {error}",
108
- checksumMismatch: "Migration {migrationName} checksum mismatch (expected: {expected}, actual: {actual})",
109
- alreadyApplied: "Migration {migrationName} has already been applied",
110
- notFound: "Migration {migrationName} not found"
7
+ //#region src/bin/argv.ts
8
+ function extractEnvFlag(argv) {
9
+ let env;
10
+ const rest = [];
11
+ for (let i = 0; i < argv.length; i++) {
12
+ const tok = argv[i];
13
+ if (tok === "--") {
14
+ rest.push(...argv.slice(i));
15
+ break;
16
+ }
17
+ const eqMatch = tok.match(/^(?:--env|-e)=(.*)$/);
18
+ if (eqMatch) {
19
+ if (!eqMatch[1]) throw new Error("--env requires a value (e.g. --env staging)");
20
+ if (env !== void 0) throw new Error("--env specified more than once");
21
+ env = eqMatch[1];
22
+ continue;
23
+ }
24
+ if (tok === "--env" || tok === "-e") {
25
+ const next = argv[i + 1];
26
+ if (!next || next.startsWith("-")) throw new Error("--env requires a value (e.g. --env staging)");
27
+ if (env !== void 0) throw new Error("--env specified more than once");
28
+ env = next;
29
+ i++;
30
+ continue;
31
+ }
32
+ rest.push(tok);
111
33
  }
112
- };
34
+ return {
35
+ env,
36
+ rest
37
+ };
38
+ }
113
39
  //#endregion
114
40
  //#region src/bin/commands/dynamic-command.ts
115
41
  /** Create Clipanion command classes from Quarry-registered commands. */
@@ -170,7 +96,16 @@ function createDynamicCommands(quarry, parseSignature, app) {
170
96
  const require = createRequire(import.meta.url);
171
97
  register(pathToFileURL(join(dirname(require.resolve("@swc-node/register")), "esm/esm.mjs")), pathToFileURL("./"));
172
98
  register(new URL("./cloudflare-workers-loader.mjs", import.meta.url), pathToFileURL("./"));
173
- const DEFAULT_ENTRY = "./src/index.ts";
99
+ const DEFAULT_ENTRY = "./src/quarry.ts";
100
+ let environment;
101
+ try {
102
+ const parsed = extractEnvFlag(process.argv.slice(2));
103
+ environment = parsed.env;
104
+ process.argv.splice(2, process.argv.length - 2, ...parsed.rest);
105
+ } catch (e) {
106
+ console.error(`Error: ${e.message}`);
107
+ process.exit(1);
108
+ }
174
109
  const firstArg = process.argv[2];
175
110
  let entryFile = DEFAULT_ENTRY;
176
111
  if (firstArg && (firstArg.includes("/") || firstArg.includes("\\") || /\.(ts|js|mts|mjs)$/.test(firstArg))) {
@@ -181,69 +116,62 @@ const entryPath = resolve(process.cwd(), entryFile);
181
116
  if (!existsSync(entryPath)) {
182
117
  console.error(`Error: Entry file not found: ${entryFile}`);
183
118
  console.error("");
184
- console.error("Create src/index.ts with a default Stratal export, or specify a custom path:");
119
+ console.error("Create src/quarry.ts that exports `QuarryRunner.run({ module, seeders })`, or specify a custom path:");
185
120
  console.error(" npx quarry ./path/to/entry.ts <command> [options]");
186
121
  process.exit(1);
187
122
  }
188
- function stripDurableObjects(config) {
189
- delete config.durable_objects;
190
- delete config.migrations;
191
- if (config.env && typeof config.env === "object") for (const envConfig of Object.values(config.env)) {
192
- delete envConfig.durable_objects;
193
- delete envConfig.migrations;
194
- }
195
- }
196
- async function createStrippedConfig(cwdRequire) {
123
+ async function main() {
124
+ const cwdRequire = createRequire(join(process.cwd(), "package.json"));
125
+ const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev } = await import(cwdRequire.resolve("wrangler"));
126
+ const { Miniflare, getDefaultDevRegistryPath } = await import(cwdRequire.resolve("miniflare"));
197
127
  const configName = [
198
128
  "wrangler.jsonc",
199
129
  "wrangler.json",
200
130
  "wrangler.toml"
201
131
  ].find((c) => existsSync(resolve(process.cwd(), c)));
202
- if (!configName) return void 0;
203
- const raw = readFileSync(resolve(process.cwd(), configName), "utf-8");
204
- let config;
205
- if (configName.endsWith(".toml")) {
206
- const { parse } = await import(cwdRequire.resolve("smol-toml"));
207
- config = parse(raw);
208
- } else {
209
- const { parse: parseJsonc } = await import(cwdRequire.resolve("jsonc-parser"));
210
- config = parseJsonc(raw);
211
- }
212
- if (typeof config.name === "string") config.name = `quarry-${config.name}-${process.pid}`;
213
- if (config.env && typeof config.env === "object") {
214
- for (const envConfig of Object.values(config.env)) if (typeof envConfig.name === "string") envConfig.name = `quarry-${envConfig.name}-${process.pid}`;
132
+ const configPath = configName ? resolve(process.cwd(), configName) : void 0;
133
+ const envFiles = [
134
+ ".env",
135
+ ".env.local",
136
+ environment ? `.env.${environment}` : null,
137
+ environment ? `.env.${environment}.local` : null
138
+ ];
139
+ for (const envFile of envFiles) {
140
+ if (!envFile) continue;
141
+ const envPath = resolve(process.cwd(), envFile);
142
+ if (existsSync(envPath)) process.loadEnvFile(envPath);
215
143
  }
216
- stripDurableObjects(config);
217
- const tmpPath = resolve(tmpdir(), `quarry-wrangler-${Date.now()}.json`);
218
- writeFileSync(tmpPath, JSON.stringify(config, null, 2));
219
- return tmpPath;
220
- }
221
- function discoverEnvFiles() {
222
- const cwd = process.cwd();
223
- return readdirSync(cwd).filter((file) => (/^\.dev\.vars($|\.)/.test(file) || /^\.env($|\.)/.test(file)) && !file.endsWith(".example") && !file.endsWith(".sample")).sort((a, b) => {
224
- const aIsDevVars = a.startsWith(".dev.vars");
225
- if (aIsDevVars !== b.startsWith(".dev.vars")) return aIsDevVars ? 1 : -1;
226
- const aIsLocal = a.endsWith(".local");
227
- if (aIsLocal !== b.endsWith(".local")) return aIsLocal ? 1 : -1;
228
- return a.localeCompare(b);
229
- }).map((file) => join(cwd, file));
230
- }
231
- async function main() {
232
- const cwdRequire = createRequire(join(process.cwd(), "package.json"));
233
- const { getPlatformProxy } = await import(cwdRequire.resolve("wrangler"));
234
- const strippedConfigPath = await createStrippedConfig(cwdRequire);
235
- const { env, ctx, dispose } = await getPlatformProxy({
236
- envFiles: discoverEnvFiles(),
237
- configPath: strippedConfigPath
144
+ if (process.env.WRANGLER_REGISTRY_PATH && !process.env.MINIFLARE_REGISTRY_PATH) process.env.MINIFLARE_REGISTRY_PATH = process.env.WRANGLER_REGISTRY_PATH;
145
+ const config = readConfig({
146
+ config: configPath,
147
+ env: environment
148
+ });
149
+ const { workerOptions } = getMiniflareWorkerOptions(config, environment);
150
+ const vars = getVarsForDev(configPath, void 0, config.vars, environment);
151
+ const varsRecord = {};
152
+ for (const [key, binding] of Object.entries(vars)) varsRecord[key] = binding.value;
153
+ workerOptions.bindings = {
154
+ ...workerOptions.bindings ?? {},
155
+ ...varsRecord,
156
+ QUEUE_PROVIDER: "sync"
157
+ };
158
+ workerOptions.name = config.name ? `quarry-${config.name}-${process.pid}` : `quarry-${process.pid}`;
159
+ const registryPath = getDefaultDevRegistryPath();
160
+ const mf = new Miniflare({
161
+ ...workerOptions,
162
+ script: "",
163
+ modules: true,
164
+ unsafeDevRegistryPath: registryPath,
165
+ defaultPersistRoot: join(process.cwd(), ".wrangler/state/v3")
238
166
  });
167
+ await mf.ready;
168
+ const env = await mf.getBindings();
239
169
  const pendingPromises = [];
240
170
  const trackedWaitUntil = (promise) => {
241
171
  pendingPromises.push(promise);
242
- ctx.waitUntil(promise);
243
172
  };
244
173
  let app;
245
174
  try {
246
- env.QUEUE_PROVIDER = "sync";
247
175
  globalThis.__stratalPlatformProxy = {
248
176
  env,
249
177
  waitUntil: trackedWaitUntil
@@ -267,16 +195,12 @@ async function main() {
267
195
  } finally {
268
196
  await Promise.allSettled(pendingPromises);
269
197
  await app?.shutdown();
270
- await dispose();
271
- if (strippedConfigPath) try {
272
- unlinkSync(strippedConfigPath);
273
- } catch {}
198
+ await mf.dispose();
274
199
  }
275
200
  }
276
201
  main().catch(async (error) => {
277
202
  const { ConfigValidationError } = await import("stratal/config");
278
- const { StratalNotInitializedError } = await import("stratal/errors");
279
- const message = error instanceof StratalNotInitializedError ? errors.stratalNotInitialized : error instanceof Error ? error.message : String(error);
203
+ const message = error instanceof Error ? error.message : String(error);
280
204
  console.error("Fatal error:", message);
281
205
  if (error instanceof ConfigValidationError) console.error(error.errors.message);
282
206
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"quarry.mjs","names":["errorMessages"],"sources":["../../src/i18n/messages/en/errors.ts","../../src/bin/commands/dynamic-command.ts","../../src/bin/quarry.ts"],"sourcesContent":["/**\n * System Error Messages - English\n *\n * Error messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const errors = {\n // Generic errors\n internalError: 'An internal error occurred',\n notFound: 'Resource not found',\n unauthorized: 'Unauthorized. Please sign in.',\n forbidden: 'Access denied',\n\n // Router errors\n honoAppAlreadyConfigured: 'HonoApp has already been configured and can only be configured once',\n routeNotFound: 'Route not found: {method} {path}',\n routeAccessDenied: 'Resource not found',\n controllerMethodNotFound: 'Method {methodName} not found on {controllerName}',\n controllerRegistration: 'Failed to register controller {controllerName}: {reason}',\n duplicateRouteName: 'Duplicate route name \"{name}\". Already registered by {existingHandler}, cannot register {newHandler}.',\n routeNameNotFound: 'Route \"{name}\" not found in registry.',\n missingRouteParam: 'Missing required parameter \"{param}\" for route \"{name}\" (path: {path}).',\n routerUseScopeViolation: 'router.use() can only be called on the root Router, not inside group() callbacks. Use router.middleware() for scoped middleware.',\n middlewareNextCalledMultipleTimes: 'next() was called multiple times in \"{middlewareName}\" middleware. Ensure each middleware calls next() at most once.',\n missingEnvironmentVariable: 'Environment variable \"{variable}\" is required but not set.',\n\n // WebSocket errors\n websocketBodyNotAvailable: 'body() is not available in WebSocket gateways. Use WebSocket messages instead.',\n websocketDuplicateEventHandler: '@{decorator}() is already applied to \\'{existingMethod}\\'. Only one method per gateway can handle this event.',\n\n // Context errors\n contextNotInitialized: 'Context has not been initialized',\n userNotAuthenticated: 'User is not authenticated',\n insufficientPermissions: 'Insufficient permissions to perform this action',\n requestContainerNotInitialized: 'Request container has not been initialized',\n requestScopeOperationNotAllowed: '{methodName}() cannot be called on this container scope. Check if you are calling it on the correct container (global vs request-scoped).',\n conditionalBindingFallback: 'Conditional binding predicate returned false for token \"{token}\" but no fallback was provided and no existing registration exists.',\n\n // Configuration errors\n configNotInitialized: 'Configuration service has not been initialized',\n configModuleNotInitialized: 'ConfigModule.forRoot() was not called before module initialization',\n stratalNotInitialized: 'Stratal has not been instantiated. Ensure you export a Stratal instance as the default export.',\n\n // Module errors\n moduleAlreadyRegistered: 'Module {moduleName} is already registered',\n moduleDependencyNotFound: 'Module dependency {dependency} not found for module {moduleName}',\n moduleCircularDependency: 'Circular dependency detected: {cycle}',\n invalidModuleProvider: 'Invalid module provider configuration: {provider}',\n\n // Database errors\n databaseGeneric: 'Database error occurred',\n databaseRecordNotFound: 'Record not found in database',\n databaseUniqueConstraint: 'Record already exists',\n databaseForeignKeyConstraint: 'Related record not found',\n databaseConnectionFailed: 'Failed to connect to database',\n databaseTimeout: 'Database query timeout',\n databaseNullConstraint: 'Required field is missing',\n databaseTooManyConnections: 'Too many database connections',\n databaseTransactionConflict: 'Transaction conflict or deadlock',\n databaseConstraintFailed: 'A database constraint was violated',\n databaseTableNotFound: 'The specified table does not exist in the database',\n databaseColumnNotFound: 'The specified column does not exist in the table',\n databaseInvalidQuery: 'The database query is invalid or malformed',\n invalidErrorCodeRange: 'Invalid error code range: {code}',\n\n // Queue errors\n queueBindingNotFound: 'Queue binding {queueName} not found in environment',\n queueProviderNotSupported: 'Queue provider \"{provider}\" is not supported. Valid providers: cloudflare, sync',\n\n // Cron errors\n cronExecutionFailed: '{count} cron job(s) failed for schedule \"{schedule}\": {jobs}',\n\n // i18n errors\n localeNotSupported: \"Locale '{locale}' is not supported. Supported locales: {supportedLocales}\",\n translationMissing: \"Translation missing for key '{key}' in locale '{locale}'\",\n\n // Container errors\n containerNotInitialized: 'Application container has not been initialized. Ensure Application.initialize() has been called.',\n\n // Domain routing errors\n domainMismatch: 'The requested domain does not match any configured route',\n\n // Signature errors\n invalidSignature: 'The URL signature is invalid or has expired',\n\n // Schema validation errors\n schemaValidation: 'Schema validation failed',\n responseValidation: 'Response validation failed',\n\n // OpenAPI errors\n openapiValidation: 'OpenAPI validation failed: {details}',\n openapiRouteRegistration: 'Failed to register OpenAPI route {path}: {reason}',\n\n // Email errors\n email: {\n resendApiKeyMissing: 'Resend API key not configured. Set RESEND_EMAIL_API_KEY environment variable.',\n smtpConfigurationMissing: 'SMTP configuration missing. Set SMTP_URL environment variable.',\n smtpHostMissing: 'SMTP host not configured. Check SMTP_URL format (smtp://user:pass@host:port).',\n smtpConnectionFailed: 'Failed to connect to SMTP server {smtpHost}:{smtpPort}',\n resendApiFailed: 'Resend API error',\n providerNotSupported: 'Unsupported email provider: {provider}. Supported providers: resend, smtp'\n },\n\n // Storage errors\n storage: {\n fileNotFound: 'File at path \"{path}\" was not found',\n invalidDisk: 'Storage disk \"{disk}\" is not configured',\n invalidFileType: 'File type \"{mimeType}\" is not allowed',\n fileTooLarge: 'File size {size} exceeds maximum allowed size of {maxSize}',\n presignedUrlInvalidExpiry: 'Expiry must be between {min} and {max} seconds',\n diskNotConfigured: 'Disk \"{disk}\" is not configured',\n responseBodyMissing: 'No body in storage response for path: {path}',\n r2BindingNotFound: 'R2 binding \"{binding}\" was not found in the environment',\n r2PresignedUrlSecretMissing: 'APP_SECRET environment variable is required for presigned URLs',\n },\n\n // Cache errors\n cache: {\n getFailed: \"Failed to retrieve value from cache for key '{key}'\",\n putFailed: \"Failed to store value in cache for key '{key}'\",\n deleteFailed: \"Failed to delete value from cache for key '{key}'\",\n listFailed: 'Failed to list cache keys'\n },\n\n // Rate limiter errors\n rateLimit: {\n tooManyRequests: 'Too Many Requests',\n notDefined: 'Rate limiter \"{name}\" is not defined. Register it via RateLimiterRegistry.for(\"{name}\", ...) inside a module\\'s onInitialize hook.',\n notConfigured: 'RateLimiterModule.forRoot() was not called. Pass { store: \"kv\" | \"memory\" | { useClass } } to enable rate limiting.',\n moduleNotImported: 'Rate limiter \"{name}\" was used (router.throttle / @RateLimit) but RateLimiterModule is not imported in your AppModule. Add RateLimiterModule.forRoot({ store: ... }) to imports.',\n },\n\n // Seeder errors\n seederNameCollision: 'Seeder name collision: \"{name}\" is already registered. Use distinct class names for each seeder.',\n seederNotRegistered: 'Seeder \"{name}\" is not registered',\n\n // Migration errors\n migration: {\n failed: 'Migration {migrationName} failed: {error}',\n checksumMismatch: 'Migration {migrationName} checksum mismatch (expected: {expected}, actual: {actual})',\n alreadyApplied: 'Migration {migrationName} has already been applied',\n notFound: 'Migration {migrationName} not found',\n },\n} as const\n","import { Command, type CommandClass, Option, type Usage } from 'clipanion'\n\nimport type { Application } from 'stratal'\nimport type { ParsedSignature, QuarryRegistry } from 'stratal/quarry'\n\n/** Create Clipanion command classes from Quarry-registered commands. */\nexport function createDynamicCommands(\n quarry: QuarryRegistry,\n parseSignature: (command: string) => ParsedSignature,\n app: Application,\n) {\n const commands: CommandClass[] = []\n\n for (const entry of quarry.list()) {\n const commandClass = quarry.get(entry.name)! as unknown as { command: string; description?: string; aliases?: string[] }\n const signature = parseSignature(commandClass.command)\n\n const paths: string[][] = [entry.name.split(' ')]\n if (commandClass.aliases) {\n for (const alias of commandClass.aliases) {\n paths.push(alias.split(' '))\n }\n }\n\n // Allow bare `npx quarry` (no arguments) to invoke the help command\n if (entry.name === 'help') {\n paths.push([])\n }\n\n class DynCmd extends Command {\n static override paths = paths\n static override usage: Usage | undefined = commandClass.description\n ? Command.Usage({ description: commandClass.description })\n : undefined\n\n async execute(): Promise<number> {\n const input: Record<string, unknown> = {}\n\n for (const arg of signature.arguments) {\n const value = (this as Record<string, unknown>)[arg.name]\n if (value !== undefined) input[arg.name] = value\n }\n\n for (const opt of signature.options) {\n const value = (this as Record<string, unknown>)[opt.name]\n if (value !== undefined) input[opt.name] = value\n }\n\n const result = await app.handleCommand(entry.name, input)\n\n for (const line of result.output) {\n this.context.stdout.write(line + '\\n')\n }\n\n for (const err of result.errors) {\n this.context.stderr.write(err + '\\n')\n }\n\n return result.exitCode\n }\n }\n\n // Define Clipanion options/arguments as class property defaults\n const proto = DynCmd.prototype as unknown as Record<string, unknown>\n for (const arg of signature.arguments) {\n if (arg.isArray) {\n proto[arg.name] = Option.Rest({ name: arg.name, required: arg.required ? 1 : 0 })\n } else {\n proto[arg.name] = Option.String({ name: arg.name, required: arg.required })\n }\n }\n\n for (const opt of signature.options) {\n const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`\n const optDescParts: string[] = []\n if (opt.description) optDescParts.push(opt.description)\n if (opt.default !== undefined) optDescParts.push(`(default: ${opt.default})`)\n const optDesc = optDescParts.length > 0 ? optDescParts.join(' ') : undefined\n\n if (opt.isFlag) {\n proto[opt.name] = Option.Boolean(optName, { description: optDesc })\n } else if (opt.isArray) {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.Array(optName, [opt.default], { description: optDesc })\n } else {\n proto[opt.name] = Option.Array(optName, { description: optDesc })\n }\n } else {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.String(optName, opt.default, { description: optDesc })\n } else {\n proto[opt.name] = Option.String(optName, { description: optDesc })\n }\n }\n }\n\n commands.push(DynCmd)\n }\n\n return commands\n}\n","import 'reflect-metadata'\n\nimport { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { createRequire, register } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { URL, pathToFileURL } from 'node:url'\nimport type { QuarryRegistry } from 'stratal/quarry'\nimport { type Application } from '../application'\nimport { errors as errorMessages } from '../i18n/messages/en/errors'\nimport { createDynamicCommands } from './commands/dynamic-command'\n\nconst require = createRequire(import.meta.url)\n\n// Register @swc-node/register for TypeScript + decorator support\nconst swcRegisterPath = join(dirname(require.resolve('@swc-node/register')), 'esm/esm.mjs')\nregister(pathToFileURL(swcRegisterPath), pathToFileURL('./'))\n\n// Register cloudflare:workers virtual module loader\nregister(new URL('./cloudflare-workers-loader.mjs', import.meta.url), pathToFileURL('./'))\n\nconst DEFAULT_ENTRY = './src/index.ts'\n\n// Determine entry file: if first arg looks like a file path, use it; otherwise use default\nconst firstArg = process.argv[2]\nlet entryFile = DEFAULT_ENTRY\n\nif (firstArg && (firstArg.includes('/') || firstArg.includes('\\\\') || /\\.(ts|js|mts|mjs)$/.test(firstArg))) {\n entryFile = firstArg\n // Remove the entry file from argv so Clipanion sees: [node, script, command, ...options]\n process.argv.splice(2, 1)\n}\n\n// Resolve and validate the entry file\nconst entryPath = resolve(process.cwd(), entryFile)\n\nif (!existsSync(entryPath)) {\n console.error(`Error: Entry file not found: ${entryFile}`)\n console.error('')\n console.error('Create src/index.ts with a default Stratal export, or specify a custom path:')\n console.error(' npx quarry ./path/to/entry.ts <command> [options]')\n process.exit(1)\n}\n\nfunction stripDurableObjects(config: Record<string, unknown>): void {\n delete config.durable_objects\n delete config.migrations\n if (config.env && typeof config.env === 'object') {\n for (const envConfig of Object.values(config.env as Record<string, Record<string, unknown>>)) {\n delete envConfig.durable_objects\n delete envConfig.migrations\n }\n }\n}\n\nasync function createStrippedConfig(cwdRequire: NodeRequire): Promise<string | undefined> {\n const candidates = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']\n const configName = candidates.find(c => existsSync(resolve(process.cwd(), c)))\n if (!configName) return undefined\n\n const configPath = resolve(process.cwd(), configName)\n const raw = readFileSync(configPath, 'utf-8')\n\n let config: Record<string, unknown>\n if (configName.endsWith('.toml')) {\n const { parse } = await import(cwdRequire.resolve('smol-toml')) as { parse: (input: string) => Record<string, unknown> }\n config = parse(raw)\n } else {\n const { parse: parseJsonc } = await import(cwdRequire.resolve('jsonc-parser')) as { parse: (input: string) => Record<string, unknown> }\n config = parseJsonc(raw)\n }\n\n // Rename so quarry's ephemeral miniflare doesn't claim the running\n // `wrangler dev` session's dev-registry slot. getPlatformProxy registers\n // its worker with empty entrypointAddresses (no direct sockets), and that\n // overwrite makes peer workers route TenantsRpc/BrandingRpc/etc. calls to\n // their fallback service (\"couldn't find a local dev session for the X\n // entrypoint\"). A unique name keeps the running session's entry untouched.\n if (typeof config.name === 'string') {\n config.name = `quarry-${config.name}-${process.pid}`\n }\n if (config.env && typeof config.env === 'object') {\n for (const envConfig of Object.values(config.env as Record<string, Record<string, unknown>>)) {\n if (typeof envConfig.name === 'string') {\n envConfig.name = `quarry-${envConfig.name}-${process.pid}`\n }\n }\n }\n\n stripDurableObjects(config)\n\n const tmpPath = resolve(tmpdir(), `quarry-wrangler-${Date.now()}.json`)\n writeFileSync(tmpPath, JSON.stringify(config, null, 2))\n return tmpPath\n}\n\nfunction discoverEnvFiles(): string[] {\n const cwd = process.cwd()\n const files = readdirSync(cwd)\n return files\n .filter(file => (/^\\.dev\\.vars($|\\.)/.test(file) || /^\\.env($|\\.)/.test(file)) && !file.endsWith('.example') && !file.endsWith('.sample'))\n .sort((a, b) => {\n // Load .env files before .dev.vars so .dev.vars takes precedence\n const aIsDevVars = a.startsWith('.dev.vars')\n const bIsDevVars = b.startsWith('.dev.vars')\n if (aIsDevVars !== bIsDevVars) return aIsDevVars ? 1 : -1\n // Within each group, .local files load last (highest precedence)\n const aIsLocal = a.endsWith('.local')\n const bIsLocal = b.endsWith('.local')\n if (aIsLocal !== bIsLocal) return aIsLocal ? 1 : -1\n return a.localeCompare(b)\n })\n .map(file => join(cwd, file))\n}\n\nasync function main(): Promise<void> {\n const cwdRequire = createRequire(join(process.cwd(), 'package.json'))\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n const { getPlatformProxy } = await import(cwdRequire.resolve('wrangler')) as typeof import('wrangler')\n\n const strippedConfigPath = await createStrippedConfig(cwdRequire)\n\n const envFiles = discoverEnvFiles()\n const { env, ctx, dispose } = await getPlatformProxy({\n envFiles, configPath: strippedConfigPath,\n })\n\n // Track waitUntil promises so we can drain them before shutdown.\n // In Workers runtime, waitUntil keeps the isolate alive. In Quarry (miniflare),\n // dispose() tears down without awaiting pending promises — so we track and drain them.\n const pendingPromises: Promise<unknown>[] = []\n const trackedWaitUntil = (promise: Promise<unknown>) => {\n pendingPromises.push(promise)\n ctx.waitUntil(promise)\n }\n\n let app: Application | undefined\n try {\n env.QUEUE_PROVIDER = 'sync';\n\n // Store platform proxy on globalThis so the cloudflare:workers virtual module can read it\n (globalThis as Record<string, unknown>).__stratalPlatformProxy = {\n env,\n waitUntil: trackedWaitUntil,\n }\n\n // Import user's entry file — triggers `new Stratal(...)` + full Application init\n await import(pathToFileURL(entryPath).href)\n\n // Parallel import of stratal modules\n const [\n { Stratal },\n { DI_TOKENS },\n { parseSignature },\n ] = await Promise.all([\n import('stratal'),\n import('stratal/di'),\n import('stratal/quarry'),\n ])\n\n app = await Stratal.resolveApplication()\n const quarry = app.container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n\n // Build Clipanion CLI\n const { Cli } = await import('clipanion')\n const pkg = require('../../package.json') as { version: string }\n\n const cli = new Cli({\n binaryName: 'quarry',\n binaryLabel: 'Quarry CLI',\n binaryVersion: pkg.version,\n })\n\n for (const cmd of createDynamicCommands(quarry, parseSignature, app)) {\n cli.register(cmd)\n }\n\n await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext })\n } finally {\n await Promise.allSettled(pendingPromises);\n\n await app?.shutdown()\n await dispose()\n\n if (strippedConfigPath) {\n try { unlinkSync(strippedConfigPath) } catch {\n //\n }\n }\n }\n}\n\nmain().catch(async (error: unknown) => {\n const { ConfigValidationError } = await import('stratal/config')\n const { StratalNotInitializedError } = await import('stratal/errors')\n\n const message = error instanceof StratalNotInitializedError\n ? errorMessages.stratalNotInitialized\n : error instanceof Error ? error.message : String(error)\n console.error('Fatal error:', message)\n if (error instanceof ConfigValidationError) {\n console.error(error.errors.message)\n }\n process.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"quarry.mjs","names":[],"sources":["../../src/bin/argv.ts","../../src/bin/commands/dynamic-command.ts","../../src/bin/quarry.ts"],"sourcesContent":["export function extractEnvFlag(argv: string[]): { env: string | undefined; rest: string[] } {\n let env: string | undefined\n const rest: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]\n if (tok === '--') {\n rest.push(...argv.slice(i))\n break\n }\n const eqMatch = tok.match(/^(?:--env|-e)=(.*)$/)\n if (eqMatch) {\n if (!eqMatch[1]) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = eqMatch[1]\n continue\n }\n if (tok === '--env' || tok === '-e') {\n const next = argv[i + 1]\n if (!next || next.startsWith('-')) throw new Error('--env requires a value (e.g. --env staging)')\n if (env !== undefined) throw new Error('--env specified more than once')\n env = next\n i++\n continue\n }\n rest.push(tok)\n }\n return { env, rest }\n}\n","import { Command, type CommandClass, Option, type Usage } from 'clipanion'\n\nimport type { Application } from 'stratal'\nimport type { ParsedSignature, QuarryRegistry } from 'stratal/quarry'\n\n/** Create Clipanion command classes from Quarry-registered commands. */\nexport function createDynamicCommands(\n quarry: QuarryRegistry,\n parseSignature: (command: string) => ParsedSignature,\n app: Application,\n) {\n const commands: CommandClass[] = []\n\n for (const entry of quarry.list()) {\n const commandClass = quarry.get(entry.name)! as unknown as { command: string; description?: string; aliases?: string[] }\n const signature = parseSignature(commandClass.command)\n\n const paths: string[][] = [entry.name.split(' ')]\n if (commandClass.aliases) {\n for (const alias of commandClass.aliases) {\n paths.push(alias.split(' '))\n }\n }\n\n // Allow bare `npx quarry` (no arguments) to invoke the help command\n if (entry.name === 'help') {\n paths.push([])\n }\n\n class DynCmd extends Command {\n static override paths = paths\n static override usage: Usage | undefined = commandClass.description\n ? Command.Usage({ description: commandClass.description })\n : undefined\n\n async execute(): Promise<number> {\n const input: Record<string, unknown> = {}\n\n for (const arg of signature.arguments) {\n const value = (this as Record<string, unknown>)[arg.name]\n if (value !== undefined) input[arg.name] = value\n }\n\n for (const opt of signature.options) {\n const value = (this as Record<string, unknown>)[opt.name]\n if (value !== undefined) input[opt.name] = value\n }\n\n const result = await app.handleCommand(entry.name, input)\n\n for (const line of result.output) {\n this.context.stdout.write(line + '\\n')\n }\n\n for (const err of result.errors) {\n this.context.stderr.write(err + '\\n')\n }\n\n return result.exitCode\n }\n }\n\n // Define Clipanion options/arguments as class property defaults\n const proto = DynCmd.prototype as unknown as Record<string, unknown>\n for (const arg of signature.arguments) {\n if (arg.isArray) {\n proto[arg.name] = Option.Rest({ name: arg.name, required: arg.required ? 1 : 0 })\n } else {\n proto[arg.name] = Option.String({ name: arg.name, required: arg.required })\n }\n }\n\n for (const opt of signature.options) {\n const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`\n const optDescParts: string[] = []\n if (opt.description) optDescParts.push(opt.description)\n if (opt.default !== undefined) optDescParts.push(`(default: ${opt.default})`)\n const optDesc = optDescParts.length > 0 ? optDescParts.join(' ') : undefined\n\n if (opt.isFlag) {\n proto[opt.name] = Option.Boolean(optName, { description: optDesc })\n } else if (opt.isArray) {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.Array(optName, [opt.default], { description: optDesc })\n } else {\n proto[opt.name] = Option.Array(optName, { description: optDesc })\n }\n } else {\n if (opt.default !== undefined) {\n proto[opt.name] = Option.String(optName, opt.default, { description: optDesc })\n } else {\n proto[opt.name] = Option.String(optName, { description: optDesc })\n }\n }\n }\n\n commands.push(DynCmd)\n }\n\n return commands\n}\n","import type { MiniflareOptions } from 'miniflare';\nimport { existsSync } from 'node:fs';\nimport { createRequire, register } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { URL, pathToFileURL } from 'node:url';\nimport type { QuarryRegistry } from 'stratal/quarry';\nimport { type Application } from '../application';\nimport { extractEnvFlag } from './argv';\nimport { createDynamicCommands } from './commands/dynamic-command';\n\ninterface WranglerConfig {\n name?: string\n vars?: Record<string, unknown>\n}\n\ninterface MiniflareWorkerResult {\n workerOptions: Record<string, unknown>\n}\n\ninterface WranglerModule {\n unstable_readConfig: (args: { config?: string; env?: string }) => WranglerConfig\n unstable_getMiniflareWorkerOptions: (config: WranglerConfig, env?: string) => MiniflareWorkerResult\n unstable_getVarsForDev: (configPath: string | undefined, envFiles: undefined, vars: unknown, env: string | undefined) => Record<string, { value: string }>\n}\n\ninterface MiniflareModule {\n Miniflare: new (opts: MiniflareOptions) => { ready: Promise<URL>; getBindings: (name?: string) => Promise<Record<string, unknown>>; dispose: () => Promise<void> }\n getDefaultDevRegistryPath: () => string\n}\n\nconst require = createRequire(import.meta.url)\n\n// Register @swc-node/register for TypeScript + decorator support\nconst swcRegisterPath = join(dirname(require.resolve('@swc-node/register')), 'esm/esm.mjs')\nregister(pathToFileURL(swcRegisterPath), pathToFileURL('./'))\n\n// Register cloudflare:workers virtual module loader\nregister(new URL('./cloudflare-workers-loader.mjs', import.meta.url), pathToFileURL('./'))\n\nconst DEFAULT_ENTRY = './src/quarry.ts'\n\nlet environment: string | undefined\ntry {\n const parsed = extractEnvFlag(process.argv.slice(2))\n environment = parsed.env\n process.argv.splice(2, process.argv.length - 2, ...parsed.rest)\n} catch (e) {\n console.error(`Error: ${(e as Error).message}`)\n process.exit(1)\n}\n\n// Determine entry file: if first arg looks like a file path, use it; otherwise use default\nconst firstArg = process.argv[2]\nlet entryFile = DEFAULT_ENTRY\n\nif (firstArg && (firstArg.includes('/') || firstArg.includes('\\\\') || /\\.(ts|js|mts|mjs)$/.test(firstArg))) {\n entryFile = firstArg\n // Remove the entry file from argv so Clipanion sees: [node, script, command, ...options]\n process.argv.splice(2, 1)\n}\n\n// Resolve and validate the entry file\nconst entryPath = resolve(process.cwd(), entryFile)\n\nif (!existsSync(entryPath)) {\n console.error(`Error: Entry file not found: ${entryFile}`)\n console.error('')\n console.error('Create src/quarry.ts that exports `QuarryRunner.run({ module, seeders })`, or specify a custom path:')\n console.error(' npx quarry ./path/to/entry.ts <command> [options]')\n process.exit(1)\n}\n\nasync function main(): Promise<void> {\n const cwdRequire = createRequire(join(process.cwd(), 'package.json'))\n\n const { unstable_readConfig: readConfig, unstable_getMiniflareWorkerOptions: getMiniflareWorkerOptions, unstable_getVarsForDev: getVarsForDev } = await import(cwdRequire.resolve('wrangler')) as WranglerModule\n const { Miniflare, getDefaultDevRegistryPath } = await import(cwdRequire.resolve('miniflare')) as MiniflareModule\n\n const candidates = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']\n const configName = candidates.find(c => existsSync(resolve(process.cwd(), c)))\n const configPath = configName ? resolve(process.cwd(), configName) : undefined\n\n // Load .env into process.env before building Miniflare options, mirroring\n // `wrangler dev`'s precedence: base `.env`, then `.env.local`, then the\n // env-specific `.env.<environment>` and `.env.<environment>.local` (later\n // load wins).\n const envFiles = [\n '.env',\n '.env.local',\n environment ? `.env.${environment}` : null,\n environment ? `.env.${environment}.local` : null,\n ]\n for (const envFile of envFiles) {\n if (!envFile) continue\n const envPath = resolve(process.cwd(), envFile)\n if (existsSync(envPath)) process.loadEnvFile(envPath)\n }\n\n // Bridge the documented `WRANGLER_REGISTRY_PATH` to `MINIFLARE_REGISTRY_PATH`,\n // the variable Miniflare's `getDefaultDevRegistryPath()` actually reads.\n // `wrangler dev` performs the same translation internally; mirroring it here\n // lets a single documented env var redirect the dev service registry for BOTH\n // this CLI's Miniflare (below) AND any vite dev server a command launches\n // (`@cloudflare/vite-plugin` also resolves its registry via\n // `getDefaultDevRegistryPath()`). That allows several isolated dev environments\n // to run in parallel without sharing the global `~/.wrangler/registry`, where\n // their identically-named workers would otherwise overwrite each other and\n // break cross-worker service-binding resolution. Only set when unset so an\n // explicit override still wins.\n if (process.env.WRANGLER_REGISTRY_PATH && !process.env.MINIFLARE_REGISTRY_PATH) {\n process.env.MINIFLARE_REGISTRY_PATH = process.env.WRANGLER_REGISTRY_PATH\n }\n\n const config = readConfig({ config: configPath, env: environment })\n const { workerOptions } = getMiniflareWorkerOptions(config, environment)\n\n const vars = getVarsForDev(configPath, undefined, config.vars, environment)\n const varsRecord: Record<string, string> = {}\n for (const [key, binding] of Object.entries(vars)) {\n varsRecord[key] = binding.value\n }\n\n const existingBindings = workerOptions.bindings as Record<string, unknown> ?? {}\n workerOptions.bindings = {\n ...existingBindings,\n ...varsRecord,\n QUEUE_PROVIDER: 'sync',\n }\n\n // Rename so quarry doesn't overwrite a running `wrangler dev` session's\n // dev-registry entry. The registry is how Miniflare discovers peer workers\n // for service binding resolution — a collision would break the running session.\n const workerName = config.name ? `quarry-${config.name}-${process.pid}` : `quarry-${process.pid}`\n workerOptions.name = workerName\n\n // Resolve the dev-registry path so Miniflare can discover running\n // `wrangler dev` sessions for service binding resolution.\n const registryPath = getDefaultDevRegistryPath()\n\n const mf = new Miniflare({\n ...workerOptions,\n script: '',\n modules: true,\n unsafeDevRegistryPath: registryPath,\n // Persist every durable plugin (KV, D1, R2, Durable Objects, cache) under\n // the same root `wrangler dev` uses, so state survives across `quarry`\n // invocations and is shared with a running `wrangler dev` session.\n defaultPersistRoot: join(process.cwd(), '.wrangler/state/v3'),\n })\n\n await mf.ready\n const env = await mf.getBindings()\n\n const pendingPromises: Promise<unknown>[] = []\n const trackedWaitUntil = (promise: Promise<unknown>) => {\n pendingPromises.push(promise)\n }\n\n let app: Application | undefined\n try {\n (globalThis as Record<string, unknown>).__stratalPlatformProxy = {\n env,\n waitUntil: trackedWaitUntil,\n }\n\n await import(pathToFileURL(entryPath).href)\n\n const [\n { Stratal },\n { DI_TOKENS },\n { parseSignature },\n ] = await Promise.all([\n import('stratal'),\n import('stratal/di'),\n import('stratal/quarry'),\n ])\n\n app = await Stratal.resolveApplication()\n const quarry = app.container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n\n const { Cli } = await import('clipanion')\n const pkg = require('../../package.json') as { version: string }\n\n const cli = new Cli({\n binaryName: 'quarry',\n binaryLabel: 'Quarry CLI',\n binaryVersion: pkg.version,\n })\n\n for (const cmd of createDynamicCommands(quarry, parseSignature, app)) {\n cli.register(cmd)\n }\n\n await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext })\n } finally {\n await Promise.allSettled(pendingPromises)\n await app?.shutdown()\n await mf.dispose()\n }\n}\n\nmain().catch(async (error: unknown) => {\n const { ConfigValidationError } = await import('stratal/config')\n\n const message = error instanceof Error ? error.message : String(error)\n console.error('Fatal error:', message)\n if (error instanceof ConfigValidationError) {\n console.error(error.errors.message)\n }\n process.exit(1)\n})\n"],"mappings":"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"}