@warlock.js/core 5.2.3 → 5.3.0

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 (261) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/esm/application/application.d.mts.map +1 -1
  3. package/esm/application/application.mjs.map +1 -1
  4. package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
  5. package/esm/benchmark/benchmark.d.mts.map +1 -1
  6. package/esm/benchmark/channels/console.channel.mjs.map +1 -1
  7. package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
  8. package/esm/benchmark/profiler.d.mts.map +1 -1
  9. package/esm/benchmark/profiler.mjs.map +1 -1
  10. package/esm/bootstrap/setup.d.mts.map +1 -1
  11. package/esm/bootstrap/setup.mjs.map +1 -1
  12. package/esm/bootstrap.d.mts.map +1 -1
  13. package/esm/cache/database-cache-driver.d.mts.map +1 -1
  14. package/esm/cache/database-cache-driver.mjs.map +1 -1
  15. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  16. package/esm/cli/cli-commands.utils.mjs.map +1 -1
  17. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
  18. package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
  19. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
  20. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
  21. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  22. package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
  23. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  24. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
  25. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  27. package/esm/cli/commands/generate/templates/stubs.mjs +13 -14
  28. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  29. package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
  30. package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
  31. package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -1
  32. package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
  33. package/esm/cli/commands/routes/route-row.mjs.map +1 -1
  34. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  35. package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -1
  36. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  37. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  38. package/esm/cli/commands-loader.mjs.map +1 -1
  39. package/esm/cli/start.mjs.map +1 -1
  40. package/esm/cli/string-similarity.mjs.map +1 -1
  41. package/esm/commands/cli-command.mjs.map +1 -1
  42. package/esm/config/config-loader.mjs.map +1 -1
  43. package/esm/config/config-setter.d.mts.map +1 -1
  44. package/esm/config/load-config-files.mjs.map +1 -1
  45. package/esm/connectors/access-connector.d.mts.map +1 -1
  46. package/esm/connectors/ai-connector.d.mts.map +1 -1
  47. package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
  48. package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
  49. package/esm/connectors/base-connector.d.mts.map +1 -1
  50. package/esm/connectors/base-connector.mjs.map +1 -1
  51. package/esm/connectors/cache-connector.d.mts.map +1 -1
  52. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  53. package/esm/connectors/connectors-manager.mjs.map +1 -1
  54. package/esm/connectors/database-connector.d.mts.map +1 -1
  55. package/esm/connectors/describe-server-address.mjs.map +1 -1
  56. package/esm/connectors/herald-connector.d.mts.map +1 -1
  57. package/esm/connectors/http-connector.d.mts.map +1 -1
  58. package/esm/connectors/http-connector.mjs.map +1 -1
  59. package/esm/connectors/logger-connector.d.mts.map +1 -1
  60. package/esm/connectors/mail-connector.d.mts.map +1 -1
  61. package/esm/connectors/notifications-connector.d.mts.map +1 -1
  62. package/esm/connectors/register-configured-connectors.mjs.map +1 -1
  63. package/esm/connectors/socket-connector.d.mts.map +1 -1
  64. package/esm/connectors/socket-connector.mjs.map +1 -1
  65. package/esm/connectors/storage.connector.d.mts.map +1 -1
  66. package/esm/container/index.d.mts.map +1 -1
  67. package/esm/database/create-database-action.mjs.map +1 -1
  68. package/esm/database/drop-tables-action.mjs.map +1 -1
  69. package/esm/database/migrate-action.mjs.map +1 -1
  70. package/esm/database/seed-command-action.mjs.map +1 -1
  71. package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
  72. package/esm/database/seeds/seeders.manager.mjs.map +1 -1
  73. package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
  74. package/esm/dev-server/check-for-updates.mjs.map +1 -1
  75. package/esm/dev-server/dependency-graph.d.mts.map +1 -1
  76. package/esm/dev-server/dependency-graph.mjs.map +1 -1
  77. package/esm/dev-server/dev-logger.mjs.map +1 -1
  78. package/esm/dev-server/development-server.mjs.map +1 -1
  79. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  80. package/esm/dev-server/file-manager.d.mts.map +1 -1
  81. package/esm/dev-server/file-manager.mjs.map +1 -1
  82. package/esm/dev-server/file-operations.d.mts.map +1 -1
  83. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  84. package/esm/dev-server/files-watcher.mjs.map +1 -1
  85. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
  86. package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
  87. package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
  88. package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
  89. package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
  90. package/esm/dev-server/layer-executor.mjs.map +1 -1
  91. package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
  92. package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
  93. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  94. package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
  95. package/esm/dev-server/loader/source-slug.mjs.map +1 -1
  96. package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
  97. package/esm/dev-server/loader/version-registry.mjs.map +1 -1
  98. package/esm/dev-server/manifest-manager.d.mts.map +1 -1
  99. package/esm/dev-server/manifest-manager.mjs.map +1 -1
  100. package/esm/dev-server/module-loader.mjs.map +1 -1
  101. package/esm/dev-server/package-json-manager.mjs.map +1 -1
  102. package/esm/dev-server/parse-imports.mjs.map +1 -1
  103. package/esm/dev-server/ready-block.mjs.map +1 -1
  104. package/esm/dev-server/special-files-collector.d.mts.map +1 -1
  105. package/esm/dev-server/special-files-collector.mjs.map +1 -1
  106. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  107. package/esm/dev-server/type-generator.mjs.map +1 -1
  108. package/esm/dev-server/utils.mjs.map +1 -1
  109. package/esm/encryption/encrypt.mjs.map +1 -1
  110. package/esm/encryption/hash.mjs.map +1 -1
  111. package/esm/generations/add-command.action.mjs.map +1 -1
  112. package/esm/generations/features/shadcn.feature.mjs.map +1 -1
  113. package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
  114. package/esm/generations/features/tailwind.feature.mjs.map +1 -1
  115. package/esm/generations/features/web.feature.mjs +2 -1
  116. package/esm/generations/features/web.feature.mjs.map +1 -1
  117. package/esm/generations/stubs.mjs +53 -54
  118. package/esm/generations/stubs.mjs.map +1 -1
  119. package/esm/http/boot-port-preflight.d.mts.map +1 -1
  120. package/esm/http/context/request-context.d.mts.map +1 -1
  121. package/esm/http/context/request-context.mjs.map +1 -1
  122. package/esm/http/context/request-memo.d.mts.map +1 -1
  123. package/esm/http/context/request-memo.mjs.map +1 -1
  124. package/esm/http/createHttpApplication.d.mts.map +1 -1
  125. package/esm/http/createHttpApplication.mjs.map +1 -1
  126. package/esm/http/health.d.mts.map +1 -1
  127. package/esm/http/index.d.mts +1 -1
  128. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  129. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  130. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  131. package/esm/http/middleware/inject-request-context.mjs +2 -21
  132. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  133. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  134. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  135. package/esm/http/ready-report.d.mts.map +1 -1
  136. package/esm/http/request-controller.d.mts.map +1 -1
  137. package/esm/http/request.d.mts +25 -6
  138. package/esm/http/request.d.mts.map +1 -1
  139. package/esm/http/request.mjs +48 -0
  140. package/esm/http/request.mjs.map +1 -1
  141. package/esm/http/response.d.mts +3 -3
  142. package/esm/http/response.d.mts.map +1 -1
  143. package/esm/http/response.mjs.map +1 -1
  144. package/esm/http/server.d.mts.map +1 -1
  145. package/esm/http/server.mjs.map +1 -1
  146. package/esm/http/types.d.mts +36 -5
  147. package/esm/http/types.d.mts.map +1 -1
  148. package/esm/http/uploaded-file.d.mts.map +1 -1
  149. package/esm/http/uploaded-file.mjs.map +1 -1
  150. package/esm/http/uploads-config.d.mts.map +1 -1
  151. package/esm/image/image.d.mts.map +1 -1
  152. package/esm/image/image.mjs.map +1 -1
  153. package/esm/index.d.mts +2 -2
  154. package/esm/logger/logger.mjs.map +1 -1
  155. package/esm/mail/config.d.mts.map +1 -1
  156. package/esm/mail/events.d.mts.map +1 -1
  157. package/esm/mail/mail.d.mts.map +1 -1
  158. package/esm/mail/mail.mjs.map +1 -1
  159. package/esm/mail/mailer-pool.d.mts.map +1 -1
  160. package/esm/mail/mailer-pool.mjs.map +1 -1
  161. package/esm/mail/send-mail.mjs.map +1 -1
  162. package/esm/mail/test-mailbox.d.mts.map +1 -1
  163. package/esm/mail/test-mailbox.mjs.map +1 -1
  164. package/esm/production/assert-generated-imports.mjs.map +1 -1
  165. package/esm/production/build-app-production.mjs.map +1 -1
  166. package/esm/production/dist-build-manifest.mjs.map +1 -1
  167. package/esm/production/production-builder.mjs.map +1 -1
  168. package/esm/production/production-supervisor.mjs.map +1 -1
  169. package/esm/production/promote-dist.mjs.map +1 -1
  170. package/esm/production/resolve-build-config.mjs.map +1 -1
  171. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
  172. package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
  173. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
  174. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
  175. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  176. package/esm/repositories/repository.manager.d.mts.map +1 -1
  177. package/esm/repositories/repository.manager.mjs.map +1 -1
  178. package/esm/resource/register-resource.d.mts.map +1 -1
  179. package/esm/resource/resource-field-builder.d.mts.map +1 -1
  180. package/esm/resource/resource-field-builder.mjs.map +1 -1
  181. package/esm/resource/resource.d.mts.map +1 -1
  182. package/esm/resource/resource.mjs.map +1 -1
  183. package/esm/restful/restful.d.mts.map +1 -1
  184. package/esm/restful/restful.mjs.map +1 -1
  185. package/esm/router/log-request-lifecycle.mjs.map +1 -1
  186. package/esm/router/normalize-route-path.d.mts.map +1 -1
  187. package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
  188. package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
  189. package/esm/router/route-registry.d.mts.map +1 -1
  190. package/esm/router/route-registry.mjs.map +1 -1
  191. package/esm/router/router.d.mts.map +1 -1
  192. package/esm/router/router.mjs.map +1 -1
  193. package/esm/socket/utils.d.mts.map +1 -1
  194. package/esm/storage/config.d.mts.map +1 -1
  195. package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
  196. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  197. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  198. package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
  199. package/esm/storage/drivers/local-driver.d.mts.map +1 -1
  200. package/esm/storage/drivers/local-driver.mjs.map +1 -1
  201. package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
  202. package/esm/storage/scoped-storage.d.mts.map +1 -1
  203. package/esm/storage/scoped-storage.mjs.map +1 -1
  204. package/esm/storage/storage-file.d.mts.map +1 -1
  205. package/esm/storage/storage-file.mjs.map +1 -1
  206. package/esm/storage/storage.d.mts.map +1 -1
  207. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  208. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  209. package/esm/tests/test-connectors-selection.mjs.map +1 -1
  210. package/esm/tests/test-helpers.d.mts.map +1 -1
  211. package/esm/tests/test-setup-timeout.mjs.map +1 -1
  212. package/esm/tests/vitest-setup.d.mts.map +1 -1
  213. package/esm/tests/vitest-setup.mjs.map +1 -1
  214. package/esm/updater/update-warlock-packages.mjs.map +1 -1
  215. package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
  216. package/esm/use-cases/use-case-events.d.mts.map +1 -1
  217. package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
  218. package/esm/use-cases/use-case.d.mts.map +1 -1
  219. package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
  220. package/esm/use-cases/use-cases-registry.mjs.map +1 -1
  221. package/esm/utils/database-log.d.mts.map +1 -1
  222. package/esm/utils/database-log.mjs.map +1 -1
  223. package/esm/utils/environment.d.mts.map +1 -1
  224. package/esm/utils/framework-vesion.mjs.map +1 -1
  225. package/esm/utils/get-localized.mjs.map +1 -1
  226. package/esm/utils/load-environment.mjs.map +1 -1
  227. package/esm/utils/normalized-path.d.mts.map +1 -1
  228. package/esm/utils/paths.d.mts.map +1 -1
  229. package/esm/utils/promise-all-object.d.mts.map +1 -1
  230. package/esm/utils/promise-all-object.mjs.map +1 -1
  231. package/esm/utils/sluggable.mjs.map +1 -1
  232. package/esm/utils/version-compare.mjs.map +1 -1
  233. package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
  234. package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
  235. package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
  236. package/esm/validation/plugins/file-plugin.mjs.map +1 -1
  237. package/esm/validation/plugins/localized-plugin.mjs +2 -2
  238. package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
  239. package/esm/validation/types.d.mts +17 -4
  240. package/esm/validation/types.d.mts.map +1 -1
  241. package/esm/validation/validators/file-validator.mjs.map +1 -1
  242. package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
  243. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  244. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  245. package/llms-full.txt +113 -87
  246. package/llms.txt +2 -2
  247. package/package.json +12 -12
  248. package/skills/README.md +2 -2
  249. package/skills/create-controller/SKILL.md +9 -9
  250. package/skills/send-response/SKILL.md +51 -37
  251. package/skills/store-file/SKILL.md +8 -3
  252. package/skills/upload-file/SKILL.md +9 -7
  253. package/skills/use-app-context/SKILL.md +2 -2
  254. package/skills/use-localization/SKILL.md +6 -2
  255. package/skills/use-repository/SKILL.md +2 -2
  256. package/skills/use-request-locals/SKILL.md +3 -3
  257. package/skills/validate-input/SKILL.md +2 -2
  258. package/skills/warlock-conventions/SKILL.md +2 -2
  259. package/skills/wire-socket/SKILL.md +2 -2
  260. package/skills/write-cli-command/SKILL.md +4 -1
  261. package/skills/write-middleware/SKILL.md +13 -15
@@ -1 +1 @@
1
- {"version":3,"file":"herald-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/herald-connector.ts"],"mappings":";;;;;;AASA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,YAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAwCL;;;EAAA,mBAnCN,YAAA;EARgB;;;EAatB,KAAA,CAAA,GAAS,OAAA;EAXE;;;EAyCX,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"herald-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/herald-connector.ts"],"mappings":";;;;;;AASA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,YAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAwCL;;;EAAA,mBAnCN,YAAA;EARgB;;;EAatB,KAAA,IAAS,OAAA;EAXE;;;EAyCX,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"mappings":";;;;;;;AAyCA;;cAAa,aAAA,SAAsB,aAAA;EAAA,SACjB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,IAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EAwCb;;;;;EAAA,mBAjCE,YAAA;EAV2B;;;EAAA,UAepC,IAAA,GAAO,eAAA;EAbO;;;;;;;;;;;;;;;;;EAAA,cAgCV,iBAAA,CAAA;EAoOP;;;AAAoC;;EA3N9B,IAAA,CAAA,GAAI,OAAA;;;;;;;;UAmCT,oBAAA;;;;;;EAgCK,KAAA,CAAA,GAAS,OAAA;;;;;;EAmHT,OAAA,CAAA,GAAW,OAAA;;;;EASX,QAAA,CAAA,GAAY,OAAA;;;;;;EA4BlB,aAAA,CAAc,YAAA;AAAA"}
1
+ {"version":3,"file":"http-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"mappings":";;;;;;;AAyCA;;cAAa,aAAA,SAAsB,aAAA;EAAA,SACjB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,IAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EAwCb;;;;;EAAA,mBAjCE,YAAA;EAV2B;;;EAAA,UAepC,IAAA,GAAO,eAAA;EAbO;;;;;;;;;;;;;;;;;EAAA,cAgCV,iBAAA;EAoOP;;;AAAoC;;EA3N9B,IAAA,IAAI,OAAA;;;;;;;;UAmCT,oBAAA;;;;;;EAgCK,KAAA,IAAS,OAAA;;;;;;EAmHT,OAAA,IAAW,OAAA;;;;EASX,QAAA,IAAY,OAAA;;;;;;EA4BlB,aAAA,CAAc,YAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { colors } from \"@mongez/copper\";\nimport { log } from \"@warlock.js/logger\";\nimport { Application } from \"../application\";\nimport { health } from \"../http/health\";\nimport { assertPortIsAvailable } from \"../http/port-preflight\";\nimport { registerHttpPlugins } from \"../http/plugins\";\nimport { setHttpReadyReport } from \"../http/ready-report\";\nimport { closeServerWithTimeout, type FastifyInstance, getHttpServer, startHttpServer } from \"../http/server\";\nimport { ensureFatalIsVisible } from \"../logger/fatal-visibility\";\nimport { router } from \"../router/router\";\nimport { type Environment } from \"../utils\";\nimport { setBaseUrl } from \"../utils/urls\";\nimport { container } from \"./../container\";\nimport { BaseConnector } from \"./base-connector\";\nimport { describeServerAddress } from \"./describe-server-address\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\nfunction environmentColor(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.magentaBright(environment);\n case \"test\":\n return colors.yellowBright(environment);\n case \"production\":\n return colors.greenBright(environment);\n default:\n return colors.white(environment);\n }\n}\n\n/**\n * Default time to wait for in-flight requests to drain on shutdown before the\n * HTTP server is force-closed. Override via `http.gracefulShutdown.timeout`.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\n\n/**\n * HTTP Connector\n * Manages HTTP server (Fastify) lifecycle\n */\nexport class HttpConnector extends BaseConnector {\n public readonly name = \"http\";\n public readonly priority = ConnectorPriority.HTTP;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\n\n /**\n * Files that trigger HTTP server restart\n * Note: routes.ts changes will be handled by HMR with wildcard routing\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\n */\n protected readonly watchedFiles = [\"src/config/http.ts\", \"src/config/http.tsx\"];\n\n /**\n * Fastify Server instance\n */\n protected http?: FastifyInstance;\n\n /**\n * Whether someone downstream will render the combined ready block.\n *\n * Only the dev server does (`dev-server/ready-block.ts`), and only it can:\n * the block reports facts — boot duration, the web surface — that this\n * connector does not own and must not wait for. When it will, this connector\n * records and stays quiet; otherwise it narrates as it always has.\n *\n * Keyed on the runtime strategy rather than on `NODE_ENV`, because that is\n * what actually distinguishes \"a dev server is driving this boot\" from \"an\n * app happens to be running with development config\".\n *\n * `!isBooted` is the second half and it is not a detail. The block summarises\n * a BOOT. A `restart()` after boot — an edited `src/config/http.ts` — is an\n * EVENT, and there is no block coming to carry it, so suppressing the lines\n * there would rebind the port in total silence.\n */\n protected get rendersReadyBlock(): boolean {\n return Application.runtimeStrategy === \"development\" && !Application.isBooted;\n }\n\n /**\n * Boot the connector — construction only (create Fastify, register\n * plugins, populate container). Route scanning is deferred to\n * `start()` so it reads the router after app code has registered.\n */\n public async boot() {\n const httpConfig = config.get(\"http\");\n\n if (!httpConfig) return;\n\n const port = httpConfig.port;\n log.info(\n `http`,\n \"connection\",\n `Starting http server on port ${port} in ${environmentColor(Application.environment)} mode`,\n );\n\n this.http = startHttpServer(httpConfig.serverOptions);\n\n container.set(\"http.server\", this.http);\n\n await registerHttpPlugins(this.http);\n\n const baseUrl = config.get(\"app.baseUrl\");\n\n // update base url\n setBaseUrl(baseUrl);\n\n // Liveness/readiness endpoints — registered on Fastify before start() scans\n // the app router, so they exist by the time the server listens.\n this.registerHealthRoutes();\n }\n\n /**\n * Register the built-in liveness (`/health`) and readiness (`/ready`)\n * endpoints directly on the Fastify instance — infra routes, kept off the app\n * router so they're immune to HMR and never collide with route scanning. Opt\n * out via `http.health.enabled = false`; rename via `http.health.path` /\n * `http.health.readinessPath`.\n */\n private registerHealthRoutes(): void {\n if (!this.http) {\n return;\n }\n\n const healthConfig = config.get(\"http.health\");\n\n if (healthConfig?.enabled === false) {\n return;\n }\n\n const livenessPath = healthConfig?.path ?? \"/health\";\n const readinessPath = healthConfig?.readinessPath ?? \"/ready\";\n\n this.http.get(livenessPath, async (_request: any, reply: any) => {\n const result = health.liveness();\n\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\n });\n\n this.http.get(readinessPath, async (_request: any, reply: any) => {\n const result = await health.readiness();\n\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\n });\n }\n\n /**\n * Initialize HTTP server — bind app-registered routes to Fastify\n * then listen. Scanning here (not in `boot`) lets HTTP boot before\n * app code without losing routes.\n */\n public async start(): Promise<void> {\n const httpConfig = config.get(\"http\");\n\n if (!httpConfig || !this.http) return;\n\n // Read once. `Application.isBooted` flips during the same boot this method\n // belongs to, and a predicate that answered differently before and after\n // `listen()` would print half a report.\n const deferToReadyBlock = this.rendersReadyBlock;\n\n if (Application.runtimeStrategy === \"development\") {\n router.scanDevServer(this.http);\n } else {\n router.scan(this.http);\n }\n\n // Surface the route table as a readiness signal + a boot log, so an empty\n // or partial route surface (e.g. a route module that failed to register) is\n // detectable instead of silently 404ing. `addRoutesRegisteredCheck` is\n // keyed by name, so re-running on restart is idempotent.\n health.addRoutesRegisteredCheck(() => router.routeCount());\n\n const routeCount = router.routeCount();\n\n // In development this fact is one line of the single ready block rendered\n // after boot completes (`dev-server/ready-block.ts`); printing it here as\n // well would put the same number on screen twice, in two formats. Outside\n // development the line is load-bearing — a supervisor or CI log greps it —\n // so it stays exactly as it was.\n if (!deferToReadyBlock) {\n log.info(\"http\", \"routes\", `${routeCount} route(s) registered`);\n }\n\n try {\n // Preflight the bind before `listen()` so a collision surfaces as the\n // instruction in `PortInUseError.message` (which names the port) instead\n // of a raw `EADDRINUSE` from deep inside Fastify/libuv.\n await assertPortIsAvailable(httpConfig.port, httpConfig.host || \"localhost\");\n\n // `listen()` RESOLVES with the address it actually bound — which is the\n // only address worth announcing. See `describe-server-address.ts` for why\n // announcing `app.baseUrl` here instead was a defect rather than a\n // shortcut.\n const boundAddress = await this.http.listen({\n port: httpConfig.port,\n host: httpConfig.host || \"localhost\",\n });\n\n Application.setServedPort(httpConfig.port);\n\n const address = describeServerAddress(\n boundAddress,\n config.get(\"app.baseUrl\"),\n // Passed because `listen()` does not report it back: Fastify resolves a\n // `0.0.0.0` bind to `http://127.0.0.1:<port>`, so the wildcard is only\n // recoverable from what we ASKED for.\n httpConfig.host || \"localhost\",\n );\n\n // Recorded unconditionally: the ready block reads it in development, and\n // in every other mode it is simply the one place that knows what was\n // bound, which is worth having whether or not anything renders it.\n setHttpReadyReport({\n boundAddress: address.boundAddress,\n url: address.url,\n wildcardBind: address.wildcardBind,\n port: httpConfig.port,\n routeCount,\n publicUrl: address.publicUrl,\n });\n\n if (!deferToReadyBlock) {\n log.success(`http`, \"connection\", address.ready);\n\n if (address.publicUrl) {\n log.info(`http`, \"connection\", address.publicUrl);\n }\n }\n\n // NEVER conditional on the block. A base URL pointing at a port nothing\n // is listening on is the single most misleading state this connector can\n // be in, and a warning a status block swallows is a warning nobody reads\n // — so it is logged where it lands ABOVE the block, in both modes.\n if (address.warning) {\n log.warn(`http`, \"connection\", address.warning);\n }\n } catch (error) {\n // A failed listen()/port-bind at boot means the app can't serve — fatal.\n // `log.fatal` reaches Sentry/file; `await log.flush()` drains buffered and\n // async channels before the process exits.\n await log.fatal(\"http\", \"connection\", error);\n\n // …and `log.fatal` reaches NOBODY when the app's log config declares no\n // terminal channel — the ordinary production shape (`production: {\n // channels: [new FileLog()] }`), and the shape of every app with no `log`\n // config at all, since `LoggerConnector.start()` then returns before\n // `log.configure`. That is how an `EADDRINUSE` at boot became a process\n // that exited 1 in total silence: the error was constructed, thrown and\n // caught exactly as designed, and then written to zero channels. stderr\n // is the one destination that is always there.\n ensureFatalIsVisible(error);\n\n await log.flush();\n\n process.exit(1); // stop the process, exit with error\n }\n\n this.active = true;\n }\n\n /**\n * Restart — needs a fresh Fastify instance since `start()` now\n * re-runs `router.scan()`, and re-scanning the same Fastify would\n * register duplicate route handlers.\n */\n public async restart(): Promise<void> {\n await this.shutdown();\n await this.boot();\n await this.start();\n }\n\n /**\n * Shutdown HTTP server\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n const server = getHttpServer();\n\n if (server) {\n const timeout = config.get(\"http.gracefulShutdown.timeout\", DEFAULT_SHUTDOWN_TIMEOUT);\n const drained = await closeServerWithTimeout(server, timeout);\n\n if (!drained) {\n log.warn(\n \"http\",\n \"shutdown\",\n `In-flight requests did not drain within ${timeout}ms; closing anyway`,\n );\n }\n }\n\n this.active = false;\n }\n\n /**\n * Override shouldRestart to handle routes.ts specially\n * routes.ts changes should NOT restart the server (use HMR instead)\n * Now receives config file paths directly from layer-executor\n */\n public shouldRestart(changedFiles: string[]): boolean {\n // Only restart for config changes, not routes\n return changedFiles.some((file) => {\n const relativePath = file.replace(/\\\\/g, \"/\");\n return this.watchedFiles.includes(relativePath);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { colors } from \"@mongez/copper\";\nimport { log } from \"@warlock.js/logger\";\nimport { Application } from \"../application\";\nimport { health } from \"../http/health\";\nimport { assertPortIsAvailable } from \"../http/port-preflight\";\nimport { registerHttpPlugins } from \"../http/plugins\";\nimport { setHttpReadyReport } from \"../http/ready-report\";\nimport { closeServerWithTimeout, type FastifyInstance, getHttpServer, startHttpServer } from \"../http/server\";\nimport { ensureFatalIsVisible } from \"../logger/fatal-visibility\";\nimport { router } from \"../router/router\";\nimport { type Environment } from \"../utils\";\nimport { setBaseUrl } from \"../utils/urls\";\nimport { container } from \"./../container\";\nimport { BaseConnector } from \"./base-connector\";\nimport { describeServerAddress } from \"./describe-server-address\";\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\n\nfunction environmentColor(environment: Environment) {\n switch (environment) {\n case \"development\":\n return colors.magentaBright(environment);\n case \"test\":\n return colors.yellowBright(environment);\n case \"production\":\n return colors.greenBright(environment);\n default:\n return colors.white(environment);\n }\n}\n\n/**\n * Default time to wait for in-flight requests to drain on shutdown before the\n * HTTP server is force-closed. Override via `http.gracefulShutdown.timeout`.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\n\n/**\n * HTTP Connector\n * Manages HTTP server (Fastify) lifecycle\n */\nexport class HttpConnector extends BaseConnector {\n public readonly name = \"http\";\n public readonly priority = ConnectorPriority.HTTP;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\n\n /**\n * Files that trigger HTTP server restart\n * Note: routes.ts changes will be handled by HMR with wildcard routing\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\n */\n protected readonly watchedFiles = [\"src/config/http.ts\", \"src/config/http.tsx\"];\n\n /**\n * Fastify Server instance\n */\n protected http?: FastifyInstance;\n\n /**\n * Whether someone downstream will render the combined ready block.\n *\n * Only the dev server does (`dev-server/ready-block.ts`), and only it can:\n * the block reports facts — boot duration, the web surface — that this\n * connector does not own and must not wait for. When it will, this connector\n * records and stays quiet; otherwise it narrates as it always has.\n *\n * Keyed on the runtime strategy rather than on `NODE_ENV`, because that is\n * what actually distinguishes \"a dev server is driving this boot\" from \"an\n * app happens to be running with development config\".\n *\n * `!isBooted` is the second half and it is not a detail. The block summarises\n * a BOOT. A `restart()` after boot — an edited `src/config/http.ts` — is an\n * EVENT, and there is no block coming to carry it, so suppressing the lines\n * there would rebind the port in total silence.\n */\n protected get rendersReadyBlock(): boolean {\n return Application.runtimeStrategy === \"development\" && !Application.isBooted;\n }\n\n /**\n * Boot the connector — construction only (create Fastify, register\n * plugins, populate container). Route scanning is deferred to\n * `start()` so it reads the router after app code has registered.\n */\n public async boot() {\n const httpConfig = config.get(\"http\");\n\n if (!httpConfig) return;\n\n const port = httpConfig.port;\n log.info(\n `http`,\n \"connection\",\n `Starting http server on port ${port} in ${environmentColor(Application.environment)} mode`,\n );\n\n this.http = startHttpServer(httpConfig.serverOptions);\n\n container.set(\"http.server\", this.http);\n\n await registerHttpPlugins(this.http);\n\n const baseUrl = config.get(\"app.baseUrl\");\n\n // update base url\n setBaseUrl(baseUrl);\n\n // Liveness/readiness endpoints — registered on Fastify before start() scans\n // the app router, so they exist by the time the server listens.\n this.registerHealthRoutes();\n }\n\n /**\n * Register the built-in liveness (`/health`) and readiness (`/ready`)\n * endpoints directly on the Fastify instance — infra routes, kept off the app\n * router so they're immune to HMR and never collide with route scanning. Opt\n * out via `http.health.enabled = false`; rename via `http.health.path` /\n * `http.health.readinessPath`.\n */\n private registerHealthRoutes(): void {\n if (!this.http) {\n return;\n }\n\n const healthConfig = config.get(\"http.health\");\n\n if (healthConfig?.enabled === false) {\n return;\n }\n\n const livenessPath = healthConfig?.path ?? \"/health\";\n const readinessPath = healthConfig?.readinessPath ?? \"/ready\";\n\n this.http.get(livenessPath, async (_request: any, reply: any) => {\n const result = health.liveness();\n\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\n });\n\n this.http.get(readinessPath, async (_request: any, reply: any) => {\n const result = await health.readiness();\n\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\n });\n }\n\n /**\n * Initialize HTTP server — bind app-registered routes to Fastify\n * then listen. Scanning here (not in `boot`) lets HTTP boot before\n * app code without losing routes.\n */\n public async start(): Promise<void> {\n const httpConfig = config.get(\"http\");\n\n if (!httpConfig || !this.http) return;\n\n // Read once. `Application.isBooted` flips during the same boot this method\n // belongs to, and a predicate that answered differently before and after\n // `listen()` would print half a report.\n const deferToReadyBlock = this.rendersReadyBlock;\n\n if (Application.runtimeStrategy === \"development\") {\n router.scanDevServer(this.http);\n } else {\n router.scan(this.http);\n }\n\n // Surface the route table as a readiness signal + a boot log, so an empty\n // or partial route surface (e.g. a route module that failed to register) is\n // detectable instead of silently 404ing. `addRoutesRegisteredCheck` is\n // keyed by name, so re-running on restart is idempotent.\n health.addRoutesRegisteredCheck(() => router.routeCount());\n\n const routeCount = router.routeCount();\n\n // In development this fact is one line of the single ready block rendered\n // after boot completes (`dev-server/ready-block.ts`); printing it here as\n // well would put the same number on screen twice, in two formats. Outside\n // development the line is load-bearing — a supervisor or CI log greps it —\n // so it stays exactly as it was.\n if (!deferToReadyBlock) {\n log.info(\"http\", \"routes\", `${routeCount} route(s) registered`);\n }\n\n try {\n // Preflight the bind before `listen()` so a collision surfaces as the\n // instruction in `PortInUseError.message` (which names the port) instead\n // of a raw `EADDRINUSE` from deep inside Fastify/libuv.\n await assertPortIsAvailable(httpConfig.port, httpConfig.host || \"localhost\");\n\n // `listen()` RESOLVES with the address it actually bound — which is the\n // only address worth announcing. See `describe-server-address.ts` for why\n // announcing `app.baseUrl` here instead was a defect rather than a\n // shortcut.\n const boundAddress = await this.http.listen({\n port: httpConfig.port,\n host: httpConfig.host || \"localhost\",\n });\n\n Application.setServedPort(httpConfig.port);\n\n const address = describeServerAddress(\n boundAddress,\n config.get(\"app.baseUrl\"),\n // Passed because `listen()` does not report it back: Fastify resolves a\n // `0.0.0.0` bind to `http://127.0.0.1:<port>`, so the wildcard is only\n // recoverable from what we ASKED for.\n httpConfig.host || \"localhost\",\n );\n\n // Recorded unconditionally: the ready block reads it in development, and\n // in every other mode it is simply the one place that knows what was\n // bound, which is worth having whether or not anything renders it.\n setHttpReadyReport({\n boundAddress: address.boundAddress,\n url: address.url,\n wildcardBind: address.wildcardBind,\n port: httpConfig.port,\n routeCount,\n publicUrl: address.publicUrl,\n });\n\n if (!deferToReadyBlock) {\n log.success(`http`, \"connection\", address.ready);\n\n if (address.publicUrl) {\n log.info(`http`, \"connection\", address.publicUrl);\n }\n }\n\n // NEVER conditional on the block. A base URL pointing at a port nothing\n // is listening on is the single most misleading state this connector can\n // be in, and a warning a status block swallows is a warning nobody reads\n // — so it is logged where it lands ABOVE the block, in both modes.\n if (address.warning) {\n log.warn(`http`, \"connection\", address.warning);\n }\n } catch (error) {\n // A failed listen()/port-bind at boot means the app can't serve — fatal.\n // `log.fatal` reaches Sentry/file; `await log.flush()` drains buffered and\n // async channels before the process exits.\n await log.fatal(\"http\", \"connection\", error);\n\n // …and `log.fatal` reaches NOBODY when the app's log config declares no\n // terminal channel — the ordinary production shape (`production: {\n // channels: [new FileLog()] }`), and the shape of every app with no `log`\n // config at all, since `LoggerConnector.start()` then returns before\n // `log.configure`. That is how an `EADDRINUSE` at boot became a process\n // that exited 1 in total silence: the error was constructed, thrown and\n // caught exactly as designed, and then written to zero channels. stderr\n // is the one destination that is always there.\n ensureFatalIsVisible(error);\n\n await log.flush();\n\n process.exit(1); // stop the process, exit with error\n }\n\n this.active = true;\n }\n\n /**\n * Restart — needs a fresh Fastify instance since `start()` now\n * re-runs `router.scan()`, and re-scanning the same Fastify would\n * register duplicate route handlers.\n */\n public async restart(): Promise<void> {\n await this.shutdown();\n await this.boot();\n await this.start();\n }\n\n /**\n * Shutdown HTTP server\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n const server = getHttpServer();\n\n if (server) {\n const timeout = config.get(\"http.gracefulShutdown.timeout\", DEFAULT_SHUTDOWN_TIMEOUT);\n const drained = await closeServerWithTimeout(server, timeout);\n\n if (!drained) {\n log.warn(\n \"http\",\n \"shutdown\",\n `In-flight requests did not drain within ${timeout}ms; closing anyway`,\n );\n }\n }\n\n this.active = false;\n }\n\n /**\n * Override shouldRestart to handle routes.ts specially\n * routes.ts changes should NOT restart the server (use HMR instead)\n * Now receives config file paths directly from layer-executor\n */\n public shouldRestart(changedFiles: string[]): boolean {\n // Only restart for config changes, not routes\n return changedFiles.some((file) => {\n const relativePath = file.replace(/\\\\/g, \"/\");\n return this.watchedFiles.includes(relativePath);\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/logger-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA6BL;;;EAAA,mBAxBN,YAAA;EARgB;;;EAatB,KAAA,CAAA,GAAS,OAAA;EAXE;;;EA8BX,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"logger-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/logger-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA6BL;;;EAAA,mBAxBN,YAAA;EARgB;;;EAatB,KAAA,IAAS,OAAA;EAXE;;;EA8BX,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"mail-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/mail-connector.ts"],"mappings":";;;;;;AASA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA6BL;;;EAAA,mBAxBN,YAAA;EARgB;;;EAatB,KAAA,CAAA,GAAS,OAAA;EAXE;;;EA8BX,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"mail-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/mail-connector.ts"],"mappings":";;;;;;AASA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA6BL;;;EAAA,mBAxBN,YAAA;EARgB;;;EAatB,KAAA,IAAS,OAAA;EAXE;;;EA8BX,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"notifications-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/notifications-connector.ts"],"mappings":";;;;;;AAiBA;;;;;;;;;;cAAa,sBAAA,SAA+B,aAAA;EAAA,SAC1B,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,aAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EADN;;;EAAA,mBAML,YAAA;EAAA;;;EAKN,KAAA,CAAA,GAAS,OAAA;EAoBG;;AAAO;;;EAAnB,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"notifications-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/notifications-connector.ts"],"mappings":";;;;;;AAiBA;;;;;;;;;;cAAa,sBAAA,SAA+B,aAAA;EAAA,SAC1B,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,aAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EADN;;;EAAA,mBAML,YAAA;EAAA;;;EAKN,KAAA,IAAS,OAAA;EAoBG;;AAAO;;;EAAnB,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"register-configured-connectors.mjs","names":[],"sources":["../../../../../../../core/src/connectors/register-configured-connectors.ts"],"sourcesContent":["/**\n * Registration half of the `connectors` config key.\n *\n * ONE key drives both halves of a connector's life: `warlock build` reads the\n * same array to drain each connector's build contribution, and this function\n * registers that array with the connectors manager so the runtime boots it.\n * Reading them from one place is the whole point — a bundle \"built for\" web\n * that \"boots without\" web is a green build with a missing client bundle, and\n * the failure is silent. A second key would let the two drift; this cannot.\n *\n * Two callers, two ways in:\n *\n * - The generated production entry PASSES the array explicitly. Inside a\n * bundled artifact nothing ever loads `warlock.config.ts` — the build\n * imports it statically so esbuild bakes it into the bundle, then hands the\n * array straight here. No config manager, no runtime file read. It also\n * passes `expectedNames` — the list the build resolved — so a config that\n * resolves differently at startup is refused instead of booted.\n * - The dev preloader OMITS the array and lets this read the already-resolved\n * warlock config, which dev has loaded by then. It also omits\n * `expectedNames`, so dev gets NO drift check — deliberately. Drift is a\n * relationship between what a build baked and what a runtime registers, and\n * dev has no build: there is nothing to drift from, and inventing a second\n * source of truth to compare against would create the disagreement this\n * check exists to prevent. The absence of a check on this path is the\n * design, not an omission — do not harmonise it with the production path.\n * A conditional config that works in dev and is refused at production boot\n * is this working as intended, not a gap.\n *\n * Idempotent either way: a connector whose name an EARLIER CALL in the same\n * process already registered is skipped rather than stacked, since a duplicate\n * would boot and shut down twice. A built-in's name is not that case — it is\n * reserved, and an array claiming one is refused rather than skipped.\n */\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\nimport { assertNoReservedConnectorNames } from \"./assert-no-reserved-connector-names\";\nimport { assertUniqueConnectorNames } from \"./assert-unique-connector-names\";\nimport { connectorsManager } from \"./connectors-manager\";\nimport type { Connector } from \"./types\";\n\nexport type RegisterConfiguredConnectorsOptions = {\n /**\n * The connector names, in order, that the build resolved. The generated\n * production entry bakes this in; every other caller omits it.\n */\n expectedNames?: string[];\n};\n\nexport function registerConfiguredConnectors(\n connectors?: Connector[],\n options: RegisterConfiguredConnectorsOptions = {},\n): void {\n const registering = connectors ?? configuredConnectors();\n\n // First, and before the drift check: a name listed twice is registered once\n // here but was drained twice by the build, and the two lists the drift check\n // compares are identical in that case — so it would wave the array through\n // and the asymmetry would survive into the boot. Reported as the duplicate it\n // is, rather than as whatever the next check happens to make of it.\n assertUniqueConnectorNames(registering);\n\n // Then the names that were taken before this array was ever read. The skip\n // below treats an already-held name as idempotency, which is right for a name\n // an EARLIER CALL registered — the same array arriving twice in one process —\n // and wrong for a built-in: nothing the app configured put that name there, so\n // the skip would drop a connector the app asked for and the build already\n // prepared artifacts for. Same call the build makes, so neither half can be\n // reached with an array the other refuses.\n assertNoReservedConnectorNames(registering);\n\n // Before anything is registered: a half-registered manager is worse than a\n // refused boot, because the phases would start on top of it.\n if (options.expectedNames) {\n assertNoConnectorDrift(options.expectedNames, registering);\n }\n\n for (const connector of registering) {\n if (connectorsManager.has(connector.name)) continue;\n\n connectorsManager.register(connector);\n }\n}\n\n/**\n * Refuses a boot whose connector list is not the one the build was made from.\n *\n * `connectors` in warlock.config is a normal expression, so it may read the\n * environment — the build resolves it once, and the artifact re-evaluates the\n * same source at startup. Nothing forces the two results to agree, and when\n * they do not the failure is silent: the bundle carries what a connector\n * contributed at build time while the runtime never boots it, or boots one the\n * build never prepared for.\n *\n * Order counts as a difference. The build drains connector contributions\n * SEQUENTIALLY in config-array order — each one writing files and merging\n * esbuild options on top of what the previous ones left — so the same names in\n * another order describe an artifact produced by a drain sequence this build\n * never ran. Start order is NOT the reason: connectors start in PRIORITY order,\n * and the array's order only breaks ties between equal priorities.\n *\n * Names only — this compares WHICH connectors are present, never how any of\n * them is configured, so it cannot catch a connector pointed at the wrong\n * database. Saying so in the message is the point: a check that reads as\n * broader than it is buys false confidence.\n */\nfunction assertNoConnectorDrift(expectedNames: string[], connectors: Connector[]): void {\n const actualNames = connectors.map((connector) => connector.name);\n\n const matches =\n expectedNames.length === actualNames.length &&\n expectedNames.every((name, index) => name === actualNames[index]);\n\n if (matches) return;\n\n throw new Error(\n \"The connector list this server booted with does not match the list its build was made from.\\n\" +\n ` expected (resolved when the app was built): ${formatNames(expectedNames)}\\n` +\n ` actual (handed over at boot): ${formatNames(actualNames)}\\n` +\n \"This compares the set and order of connectors only, and says nothing about how any connector is configured. \" +\n \"It usually means `connectors` in warlock.config resolves differently here than it did at build time — \" +\n \"rebuild the app in the same configuration environment this server runs in, \" +\n \"or make the array resolve to the same connectors in both.\",\n );\n}\n\nfunction formatNames(names: string[]): string {\n return `[${names.map((name) => JSON.stringify(name)).join(\", \")}]`;\n}\n\n/**\n * The connectors the resolved warlock config declares.\n *\n * Reached only when the caller omitted the array. An unloaded config here is\n * not \"nothing to register\" — it is a caller asking this function to read a\n * source that does not exist yet, so it throws instead of returning empty.\n * Returning empty is how the production entry once registered NOTHING while\n * reporting success: the bundle booted none of the connectors it was built\n * for, and no line of output said so.\n */\nfunction configuredConnectors(): Connector[] {\n if (!warlockConfigManager.isLoaded) {\n throw new Error(\n \"registerConfiguredConnectors: warlock config is not loaded; pass the connectors array explicitly or load the config first\",\n );\n }\n\n return warlockConfigManager.get(\"connectors\") ?? [];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,6BACd,YACA,UAA+C,CAAC,GAC1C;CACN,MAAM,cAAc,cAAc,qBAAqB;CAOvD,2BAA2B,WAAW;CAStC,+BAA+B,WAAW;CAI1C,IAAI,QAAQ,eACV,uBAAuB,QAAQ,eAAe,WAAW;CAG3D,KAAK,MAAM,aAAa,aAAa;EACnC,IAAI,kBAAkB,IAAI,UAAU,IAAI,GAAG;EAE3C,kBAAkB,SAAS,SAAS;CACtC;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,uBAAuB,eAAyB,YAA+B;CACtF,MAAM,cAAc,WAAW,KAAK,cAAc,UAAU,IAAI;CAMhE,IAHE,cAAc,WAAW,YAAY,UACrC,cAAc,OAAO,MAAM,UAAU,SAAS,YAAY,MAAM,GAErD;CAEb,MAAM,IAAI,MACR;gDACmD,YAAY,aAAa,EAAE,oCACzC,YAAY,WAAW,EAAE,2VAKhE;AACF;AAEA,SAAS,YAAY,OAAyB;CAC5C,OAAO,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE;AAClE;;;;;;;;;;;AAYA,SAAS,uBAAoC;CAC3C,IAAI,CAAC,qBAAqB,UACxB,MAAM,IAAI,MACR,2HACF;CAGF,OAAO,qBAAqB,IAAI,YAAY,KAAK,CAAC;AACpD"}
1
+ {"version":3,"file":"register-configured-connectors.mjs","names":[],"sources":["../../../../../../../core/src/connectors/register-configured-connectors.ts"],"sourcesContent":["/**\n * Registration half of the `connectors` config key.\n *\n * ONE key drives both halves of a connector's life: `warlock build` reads the\n * same array to drain each connector's build contribution, and this function\n * registers that array with the connectors manager so the runtime boots it.\n * Reading them from one place is the whole point — a bundle \"built for\" web\n * that \"boots without\" web is a green build with a missing client bundle, and\n * the failure is silent. A second key would let the two drift; this cannot.\n *\n * Two callers, two ways in:\n *\n * - The generated production entry PASSES the array explicitly. Inside a\n * bundled artifact nothing ever loads `warlock.config.ts` — the build\n * imports it statically so esbuild bakes it into the bundle, then hands the\n * array straight here. No config manager, no runtime file read. It also\n * passes `expectedNames` — the list the build resolved — so a config that\n * resolves differently at startup is refused instead of booted.\n * - The dev preloader OMITS the array and lets this read the already-resolved\n * warlock config, which dev has loaded by then. It also omits\n * `expectedNames`, so dev gets NO drift check — deliberately. Drift is a\n * relationship between what a build baked and what a runtime registers, and\n * dev has no build: there is nothing to drift from, and inventing a second\n * source of truth to compare against would create the disagreement this\n * check exists to prevent. The absence of a check on this path is the\n * design, not an omission — do not harmonise it with the production path.\n * A conditional config that works in dev and is refused at production boot\n * is this working as intended, not a gap.\n *\n * Idempotent either way: a connector whose name an EARLIER CALL in the same\n * process already registered is skipped rather than stacked, since a duplicate\n * would boot and shut down twice. A built-in's name is not that case — it is\n * reserved, and an array claiming one is refused rather than skipped.\n */\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\nimport { assertNoReservedConnectorNames } from \"./assert-no-reserved-connector-names\";\nimport { assertUniqueConnectorNames } from \"./assert-unique-connector-names\";\nimport { connectorsManager } from \"./connectors-manager\";\nimport type { Connector } from \"./types\";\n\nexport type RegisterConfiguredConnectorsOptions = {\n /**\n * The connector names, in order, that the build resolved. The generated\n * production entry bakes this in; every other caller omits it.\n */\n expectedNames?: string[];\n};\n\nexport function registerConfiguredConnectors(\n connectors?: Connector[],\n options: RegisterConfiguredConnectorsOptions = {},\n): void {\n const registering = connectors ?? configuredConnectors();\n\n // First, and before the drift check: a name listed twice is registered once\n // here but was drained twice by the build, and the two lists the drift check\n // compares are identical in that case — so it would wave the array through\n // and the asymmetry would survive into the boot. Reported as the duplicate it\n // is, rather than as whatever the next check happens to make of it.\n assertUniqueConnectorNames(registering);\n\n // Then the names that were taken before this array was ever read. The skip\n // below treats an already-held name as idempotency, which is right for a name\n // an EARLIER CALL registered — the same array arriving twice in one process —\n // and wrong for a built-in: nothing the app configured put that name there, so\n // the skip would drop a connector the app asked for and the build already\n // prepared artifacts for. Same call the build makes, so neither half can be\n // reached with an array the other refuses.\n assertNoReservedConnectorNames(registering);\n\n // Before anything is registered: a half-registered manager is worse than a\n // refused boot, because the phases would start on top of it.\n if (options.expectedNames) {\n assertNoConnectorDrift(options.expectedNames, registering);\n }\n\n for (const connector of registering) {\n if (connectorsManager.has(connector.name)) continue;\n\n connectorsManager.register(connector);\n }\n}\n\n/**\n * Refuses a boot whose connector list is not the one the build was made from.\n *\n * `connectors` in warlock.config is a normal expression, so it may read the\n * environment — the build resolves it once, and the artifact re-evaluates the\n * same source at startup. Nothing forces the two results to agree, and when\n * they do not the failure is silent: the bundle carries what a connector\n * contributed at build time while the runtime never boots it, or boots one the\n * build never prepared for.\n *\n * Order counts as a difference. The build drains connector contributions\n * SEQUENTIALLY in config-array order — each one writing files and merging\n * esbuild options on top of what the previous ones left — so the same names in\n * another order describe an artifact produced by a drain sequence this build\n * never ran. Start order is NOT the reason: connectors start in PRIORITY order,\n * and the array's order only breaks ties between equal priorities.\n *\n * Names only — this compares WHICH connectors are present, never how any of\n * them is configured, so it cannot catch a connector pointed at the wrong\n * database. Saying so in the message is the point: a check that reads as\n * broader than it is buys false confidence.\n */\nfunction assertNoConnectorDrift(expectedNames: string[], connectors: Connector[]): void {\n const actualNames = connectors.map((connector) => connector.name);\n\n const matches =\n expectedNames.length === actualNames.length &&\n expectedNames.every((name, index) => name === actualNames[index]);\n\n if (matches) return;\n\n throw new Error(\n \"The connector list this server booted with does not match the list its build was made from.\\n\" +\n ` expected (resolved when the app was built): ${formatNames(expectedNames)}\\n` +\n ` actual (handed over at boot): ${formatNames(actualNames)}\\n` +\n \"This compares the set and order of connectors only, and says nothing about how any connector is configured. \" +\n \"It usually means `connectors` in warlock.config resolves differently here than it did at build time — \" +\n \"rebuild the app in the same configuration environment this server runs in, \" +\n \"or make the array resolve to the same connectors in both.\",\n );\n}\n\nfunction formatNames(names: string[]): string {\n return `[${names.map((name) => JSON.stringify(name)).join(\", \")}]`;\n}\n\n/**\n * The connectors the resolved warlock config declares.\n *\n * Reached only when the caller omitted the array. An unloaded config here is\n * not \"nothing to register\" — it is a caller asking this function to read a\n * source that does not exist yet, so it throws instead of returning empty.\n * Returning empty is how the production entry once registered NOTHING while\n * reporting success: the bundle booted none of the connectors it was built\n * for, and no line of output said so.\n */\nfunction configuredConnectors(): Connector[] {\n if (!warlockConfigManager.isLoaded) {\n throw new Error(\n \"registerConfiguredConnectors: warlock config is not loaded; pass the connectors array explicitly or load the config first\",\n );\n }\n\n return warlockConfigManager.get(\"connectors\") ?? [];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,6BACd,YACA,UAA+C,CAAC,GAC1C;CACN,MAAM,cAAc,cAAc,qBAAqB;CAOvD,2BAA2B,WAAW;CAStC,+BAA+B,WAAW;CAI1C,IAAI,QAAQ,eACV,uBAAuB,QAAQ,eAAe,WAAW;CAG3D,KAAK,MAAM,aAAa,aAAa;EACnC,IAAI,kBAAkB,IAAI,UAAU,IAAI,GAAG;EAE3C,kBAAkB,SAAS,SAAS;CACtC;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,uBAAuB,eAAyB,YAA+B;CACtF,MAAM,cAAc,WAAW,KAAK,cAAc,UAAU,IAAI;CAMhE,IAHE,cAAc,WAAW,YAAY,UACrC,cAAc,OAAO,MAAM,UAAU,SAAS,YAAY,MAAM,GAErD;CAEb,MAAM,IAAI,MACR;gDACmD,YAAY,aAAa,EAAE,oCACzC,YAAY,WAAW,EAAE,2VAKhE;AACF;AAEA,SAAS,YAAY,OAAyB;CAC5C,OAAO,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;AAClE;;;;;;;;;;;AAYA,SAAS,uBAAoC;CAC3C,IAAI,CAAC,qBAAqB,UACxB,MAAM,IAAI,MACR,2HACF;CAGF,OAAO,qBAAqB,IAAI,YAAY,KAAK,CAAC;AACpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"socket-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/socket-connector.ts"],"mappings":";;;;;;;AA8BA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA,EAAM,aAAA;EAAA,SACN,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EASX;;;;;EAAA,mBAFA,YAAA;EAAA,UAET,MAAA,GAAS,MAAA;EAZgB;;;;;;;;EAAA,UAsBzB,aAAA;EAZS;;;EAiBN,IAAA,CAAA,GAAI,OAAA;EAAJ;;;EAuDA,KAAA,CAAA,GAAS,OAAA;EAqBT;;;AAAmB;;;;;;EAAnB,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"socket-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/socket-connector.ts"],"mappings":";;;;;;;AA8BA;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA,EAAM,aAAA;EAAA,SACN,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,IAAA;EASX;;;;;EAAA,mBAFA,YAAA;EAAA,UAET,MAAA,GAAS,MAAA;EAZgB;;;;;;;;EAAA,UAsBzB,aAAA;EAZS;;;EAiBN,IAAA,IAAI,OAAA;EAAJ;;;EAuDA,KAAA,IAAS,OAAA;EAqBT;;;AAAmB;;;;;;EAAnB,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"socket-connector.mjs","names":["createHttpsServer","createHttpServer"],"sources":["../../../../../../../core/src/connectors/socket-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { log } from \"@warlock.js/logger\";\nimport { createServer as createHttpServer } from \"http\";\nimport { createServer as createHttpsServer } from \"https\";\nimport type { Server } from \"socket.io\";\nimport { container } from \"../container\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, type ConnectorName, ConnectorPriority } from \"./types\";\n\n/**\n * Shown when a project enables sockets (a `socket` config is present) but the\n * optional `socket.io` peer is not installed.\n */\nconst SOCKET_INSTALL_INSTRUCTIONS = `\nRealtime socket server requires the socket.io package.\nInstall it with:\n\n warlock add socket\n\nOr manually:\n\n npm install socket.io\n pnpm add socket.io\n yarn add socket.io\n`.trim();\n\n/**\n * Socket Connector\n * Manages Socket server (Socket.IO) lifecycle\n */\nexport class SocketConnector extends BaseConnector {\n public readonly name: ConnectorName = \"socket\";\n public readonly priority = ConnectorPriority.SOCKET;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\n\n /**\n * Files that trigger Socket server restart\n * Note: routes.ts changes will be handled by HMR with wildcard routing\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\n */\n protected readonly watchedFiles = [\"src/config/socket.ts\"];\n\n protected socket?: Server;\n\n /**\n * Whether this connector created the underlying node HTTP(S) server.\n *\n * When `http` is present, socket binds onto the HTTP connector's shared\n * `fastify.server` and must NOT close it on shutdown — the HTTP connector\n * owns the graceful drain. Socket only closes the node server when it\n * created one itself (http absent). See {@link shutdown}.\n */\n protected ownsRawServer = false;\n\n /**\n * Boot the connector\n */\n public async boot() {\n const socketConfig = config.get(\"socket\");\n\n if (!socketConfig) return;\n\n // socket.io is an optional peer — load it lazily so projects that don't use\n // realtime sockets never need it installed (mirrors the mail/storage drivers).\n let SocketServer: typeof import(\"socket.io\").Server;\n try {\n ({ Server: SocketServer } = await import(\"socket.io\"));\n } catch {\n throw new Error(SOCKET_INSTALL_INSTRUCTIONS);\n }\n\n log.info(\"socket\", \"connection\", \"Starting Socket.IO server\");\n\n // now we have two cases\n // 1. http is used, then use it (shared — the HTTP connector owns it)\n // 2. http is not used, then create a new server (we own it)\n let server;\n if (container.has(\"http.server\")) {\n const fastify = container.get(\"http.server\");\n server = fastify.server;\n this.ownsRawServer = false;\n } else {\n server = socketConfig.ssl ? createHttpsServer() : createHttpServer();\n server.listen(socketConfig.port);\n this.ownsRawServer = true;\n }\n\n container.set(\"socket.rawServer\", server);\n\n this.socket = new SocketServer(server, {\n // The shared raw server may carry OTHER `upgrade` consumers — Vite's HMR\n // websocket does exactly that when `WebConnector` is registered\n // (`web/src/server/web-connector.ts`, `server.hmr.server = fastify.server`).\n // engine.io's default `destroyUpgrade: true` schedules a destroy for every\n // upgrade whose path is not `/socket.io`\n // (`engine.io/build/server.js:676-695`: `setTimeout(… socket.end(), 1000)`),\n // and the only thing saving a foreign websocket today is that it wrote its\n // handshake bytes synchronously and so trips the `bytesWritten <= 0` guard\n // before the 1s timer fires. That is an undocumented race, not a contract.\n // Turning the destroy off removes the coupling outright and makes the\n // shared server safe for any app-owned websocket route.\n // A project can still override it via `socket.options`.\n destroyUpgrade: false,\n ...socketConfig.options,\n });\n\n container.set(\"socket\", this.socket);\n }\n\n /**\n * Initialize Socket server\n */\n public async start(): Promise<void> {\n const socketConfig = config.get(\"socket\");\n\n // `this.socket` is only set by boot() once a socket config is present —\n // use it (not a never-assigned field) to detect a successful boot.\n if (!socketConfig || !this.socket) return;\n\n log.success(\"socket\", \"connection\", \"Established Socket.IO server\");\n\n this.active = true;\n }\n\n /**\n * Shutdown Socket server\n *\n * Always closes the Socket.IO layer (and awaits the drain). The underlying\n * node HTTP(S) server is only closed here when this connector created it\n * ({@link ownsRawServer}); when `http` owns the shared server, we leave it\n * for the HTTP connector to drain and close, so the two connectors don't\n * race to `close()` the same socket.\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n if (container.has(\"socket\")) {\n const socket = container.get(\"socket\");\n // socket.io's close() takes a callback — await it so shutdown doesn't\n // report done while sockets are still draining.\n await new Promise<void>((resolve) => {\n socket.close(() => resolve());\n });\n }\n\n if (this.ownsRawServer && container.has(\"socket.rawServer\")) {\n const server = container.get(\"socket.rawServer\");\n await new Promise<void>((resolve, reject) => {\n server.close((error?: Error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,8BAA8B;;;;;;;;;;;EAWlC,KAAK;;;;;AAMP,IAAa,kBAAb,cAAqC,cAAc;;;cACX;;;sBASJ,CAAC,sBAAsB;uBAY/B;;;;;CAK1B,MAAa,OAAO;EAClB,MAAM,eAAe,OAAO,IAAI,QAAQ;EAExC,IAAI,CAAC,cAAc;EAInB,IAAI;EACJ,IAAI;GACF,CAAC,CAAE,QAAQ,gBAAiB,MAAM,OAAO;EAC3C,QAAQ;GACN,MAAM,IAAI,MAAM,2BAA2B;EAC7C;EAEA,IAAI,KAAK,UAAU,cAAc,2BAA2B;EAK5D,IAAI;EACJ,IAAI,UAAU,IAAI,aAAa,GAAG;GAEhC,SADgB,UAAU,IAAI,aACf,EAAE;GACjB,KAAK,gBAAgB;EACvB,OAAO;GACL,SAAS,aAAa,MAAMA,eAAkB,IAAIC,aAAiB;GACnE,OAAO,OAAO,aAAa,IAAI;GAC/B,KAAK,gBAAgB;EACvB;EAEA,UAAU,IAAI,oBAAoB,MAAM;EAExC,KAAK,SAAS,IAAI,aAAa,QAAQ;GAarC,gBAAgB;GAChB,GAAG,aAAa;EAClB,CAAC;EAED,UAAU,IAAI,UAAU,KAAK,MAAM;CACrC;;;;CAKA,MAAa,QAAuB;EAKlC,IAAI,CAJiB,OAAO,IAAI,QAIhB,KAAK,CAAC,KAAK,QAAQ;EAEnC,IAAI,QAAQ,UAAU,cAAc,8BAA8B;EAElE,KAAK,SAAS;CAChB;;;;;;;;;;CAWA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,IAAI,UAAU,IAAI,QAAQ,GAAG;GAC3B,MAAM,SAAS,UAAU,IAAI,QAAQ;GAGrC,MAAM,IAAI,SAAe,YAAY;IACnC,OAAO,YAAY,QAAQ,CAAC;GAC9B,CAAC;EACH;EAEA,IAAI,KAAK,iBAAiB,UAAU,IAAI,kBAAkB,GAAG;GAC3D,MAAM,SAAS,UAAU,IAAI,kBAAkB;GAC/C,MAAM,IAAI,SAAe,SAAS,WAAW;IAC3C,OAAO,OAAO,UAAkB;KAC9B,IAAI,OACF,OAAO,KAAK;UAEZ,QAAQ;IAEZ,CAAC;GACH,CAAC;EACH;EAEA,KAAK,SAAS;CAChB;AACF"}
1
+ {"version":3,"file":"socket-connector.mjs","names":["createHttpsServer","createHttpServer"],"sources":["../../../../../../../core/src/connectors/socket-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { log } from \"@warlock.js/logger\";\nimport { createServer as createHttpServer } from \"http\";\nimport { createServer as createHttpsServer } from \"https\";\nimport type { Server } from \"socket.io\";\nimport { container } from \"../container\";\nimport { BaseConnector } from \"./base-connector\";\nimport { ConnectorLifecyclePhase, type ConnectorName, ConnectorPriority } from \"./types\";\n\n/**\n * Shown when a project enables sockets (a `socket` config is present) but the\n * optional `socket.io` peer is not installed.\n */\nconst SOCKET_INSTALL_INSTRUCTIONS = `\nRealtime socket server requires the socket.io package.\nInstall it with:\n\n warlock add socket\n\nOr manually:\n\n npm install socket.io\n pnpm add socket.io\n yarn add socket.io\n`.trim();\n\n/**\n * Socket Connector\n * Manages Socket server (Socket.IO) lifecycle\n */\nexport class SocketConnector extends BaseConnector {\n public readonly name: ConnectorName = \"socket\";\n public readonly priority = ConnectorPriority.SOCKET;\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\n\n /**\n * Files that trigger Socket server restart\n * Note: routes.ts changes will be handled by HMR with wildcard routing\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\n */\n protected readonly watchedFiles = [\"src/config/socket.ts\"];\n\n protected socket?: Server;\n\n /**\n * Whether this connector created the underlying node HTTP(S) server.\n *\n * When `http` is present, socket binds onto the HTTP connector's shared\n * `fastify.server` and must NOT close it on shutdown — the HTTP connector\n * owns the graceful drain. Socket only closes the node server when it\n * created one itself (http absent). See {@link shutdown}.\n */\n protected ownsRawServer = false;\n\n /**\n * Boot the connector\n */\n public async boot() {\n const socketConfig = config.get(\"socket\");\n\n if (!socketConfig) return;\n\n // socket.io is an optional peer — load it lazily so projects that don't use\n // realtime sockets never need it installed (mirrors the mail/storage drivers).\n let SocketServer: typeof import(\"socket.io\").Server;\n try {\n ({ Server: SocketServer } = await import(\"socket.io\"));\n } catch {\n throw new Error(SOCKET_INSTALL_INSTRUCTIONS);\n }\n\n log.info(\"socket\", \"connection\", \"Starting Socket.IO server\");\n\n // now we have two cases\n // 1. http is used, then use it (shared — the HTTP connector owns it)\n // 2. http is not used, then create a new server (we own it)\n let server;\n if (container.has(\"http.server\")) {\n const fastify = container.get(\"http.server\");\n server = fastify.server;\n this.ownsRawServer = false;\n } else {\n server = socketConfig.ssl ? createHttpsServer() : createHttpServer();\n server.listen(socketConfig.port);\n this.ownsRawServer = true;\n }\n\n container.set(\"socket.rawServer\", server);\n\n this.socket = new SocketServer(server, {\n // The shared raw server may carry OTHER `upgrade` consumers — Vite's HMR\n // websocket does exactly that when `WebConnector` is registered\n // (`web/src/server/web-connector.ts`, `server.hmr.server = fastify.server`).\n // engine.io's default `destroyUpgrade: true` schedules a destroy for every\n // upgrade whose path is not `/socket.io`\n // (`engine.io/build/server.js:676-695`: `setTimeout(… socket.end(), 1000)`),\n // and the only thing saving a foreign websocket today is that it wrote its\n // handshake bytes synchronously and so trips the `bytesWritten <= 0` guard\n // before the 1s timer fires. That is an undocumented race, not a contract.\n // Turning the destroy off removes the coupling outright and makes the\n // shared server safe for any app-owned websocket route.\n // A project can still override it via `socket.options`.\n destroyUpgrade: false,\n ...socketConfig.options,\n });\n\n container.set(\"socket\", this.socket);\n }\n\n /**\n * Initialize Socket server\n */\n public async start(): Promise<void> {\n const socketConfig = config.get(\"socket\");\n\n // `this.socket` is only set by boot() once a socket config is present —\n // use it (not a never-assigned field) to detect a successful boot.\n if (!socketConfig || !this.socket) return;\n\n log.success(\"socket\", \"connection\", \"Established Socket.IO server\");\n\n this.active = true;\n }\n\n /**\n * Shutdown Socket server\n *\n * Always closes the Socket.IO layer (and awaits the drain). The underlying\n * node HTTP(S) server is only closed here when this connector created it\n * ({@link ownsRawServer}); when `http` owns the shared server, we leave it\n * for the HTTP connector to drain and close, so the two connectors don't\n * race to `close()` the same socket.\n */\n public async shutdown(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n if (container.has(\"socket\")) {\n const socket = container.get(\"socket\");\n // socket.io's close() takes a callback — await it so shutdown doesn't\n // report done while sockets are still draining.\n await new Promise<void>((resolve) => {\n socket.close(() => resolve());\n });\n }\n\n if (this.ownsRawServer && container.has(\"socket.rawServer\")) {\n const server = container.get(\"socket.rawServer\");\n await new Promise<void>((resolve, reject) => {\n server.close((error?: Error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n this.active = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,8BAA8B;;;;;;;;;;;EAWlC,KAAK;;;;;AAMP,IAAa,kBAAb,cAAqC,cAAc;;;cACX;;;sBASJ,CAAC,sBAAsB;uBAY/B;;;;;CAK1B,MAAa,OAAO;EAClB,MAAM,eAAe,OAAO,IAAI,QAAQ;EAExC,IAAI,CAAC,cAAc;EAInB,IAAI;EACJ,IAAI;GACF,CAAC,CAAE,QAAQ,gBAAiB,MAAM,OAAO;EAC3C,QAAQ;GACN,MAAM,IAAI,MAAM,2BAA2B;EAC7C;EAEA,IAAI,KAAK,UAAU,cAAc,2BAA2B;EAK5D,IAAI;EACJ,IAAI,UAAU,IAAI,aAAa,GAAG;GAEhC,SADgB,UAAU,IAAI,aACf,CAAC,CAAC;GACjB,KAAK,gBAAgB;EACvB,OAAO;GACL,SAAS,aAAa,MAAMA,eAAkB,IAAIC,aAAiB;GACnE,OAAO,OAAO,aAAa,IAAI;GAC/B,KAAK,gBAAgB;EACvB;EAEA,UAAU,IAAI,oBAAoB,MAAM;EAExC,KAAK,SAAS,IAAI,aAAa,QAAQ;GAarC,gBAAgB;GAChB,GAAG,aAAa;EAClB,CAAC;EAED,UAAU,IAAI,UAAU,KAAK,MAAM;CACrC;;;;CAKA,MAAa,QAAuB;EAKlC,IAAI,CAJiB,OAAO,IAAI,QAIhB,KAAK,CAAC,KAAK,QAAQ;EAEnC,IAAI,QAAQ,UAAU,cAAc,8BAA8B;EAElE,KAAK,SAAS;CAChB;;;;;;;;;;CAWA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,IAAI,UAAU,IAAI,QAAQ,GAAG;GAC3B,MAAM,SAAS,UAAU,IAAI,QAAQ;GAGrC,MAAM,IAAI,SAAe,YAAY;IACnC,OAAO,YAAY,QAAQ,CAAC;GAC9B,CAAC;EACH;EAEA,IAAI,KAAK,iBAAiB,UAAU,IAAI,kBAAkB,GAAG;GAC3D,MAAM,SAAS,UAAU,IAAI,kBAAkB;GAC/C,MAAM,IAAI,SAAe,SAAS,WAAW;IAC3C,OAAO,OAAO,UAAkB;KAC9B,IAAI,OACF,OAAO,KAAK;UAEZ,QAAQ;IAEZ,CAAC;GACH,CAAC;EACH;EAEA,KAAK,SAAS;CAChB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"mappings":";;;;;;AASA;;cAAa,gBAAA,SAAyB,aAAA;EAAA,SACpB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,OAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAiCL;;;EAAA,mBA5BN,YAAA;EARiB;;;;;;;;;;;;;;AAoCJ;;EAVnB,KAAA,CAAA,GAAS,OAAA;;;;EAUT,QAAA,CAAA,GAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"storage.connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/storage.connector.ts"],"mappings":";;;;;;AASA;;cAAa,gBAAA,SAAyB,aAAA;EAAA,SACpB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,OAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAiCL;;;EAAA,mBA5BN,YAAA;EARiB;;;;;;;;;;;;;;AAoCJ;;EAVnB,KAAA,IAAS,OAAA;;;;EAUT,QAAA,IAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"mappings":";;;;;;;;;KAUY,cAAA;EACV,MAAA,EAAQ,MAAA;EACR,aAAA,EAAe,eAAA;EACf,cAAA;EACA,MAAA,EAAQ,MAAA;EACR,iBAAA,EAAmB,UAAA;AAAA;AAAA,KAGhB,aAAA,SAAsB,cAAc;AAAA,cAEnC,SAAA;EATJ;;;EAaO,GAAA,iBAAoB,cAAA,CAAA,CAAgB,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;EAClE,GAAA,SAAA,CAAa,GAAA,UAAa,KAAA,EAAO,CAAA;EAXxC;;;EAmBO,GAAA,iBAAoB,cAAA,CAAA,CAAgB,GAAA,EAAK,CAAA,GAAI,cAAA,CAAe,CAAA;EAC5D,GAAA,SAAA,CAAa,GAAA,WAAc,CAAA;EAnBL;AAC7B;;EA0BO,GAAA,CAAI,GAAA,EAAK,aAAA;EAxBuB;AAAA;AAAA;EA+BhC,MAAA,CAAO,GAAA;AAAA;AAAA,cAKH,SAAA,EAAS,SAAkB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../core/src/container/index.ts"],"mappings":";;;;;;;;;KAUY,cAAA;EACV,MAAA,EAAQ,MAAA;EACR,aAAA,EAAe,eAAA;EACf,cAAA;EACA,MAAA,EAAQ,MAAA;EACR,iBAAA,EAAmB,UAAA;AAAA;AAAA,KAGhB,aAAA,SAAsB,cAAc;AAAA,cAEnC,SAAA;EATJ;;;EAaO,GAAA,iBAAoB,cAAA,EAAgB,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;EAClE,GAAA,UAAa,GAAA,UAAa,KAAA,EAAO,CAAA;EAXxC;;;EAmBO,GAAA,iBAAoB,cAAA,EAAgB,GAAA,EAAK,CAAA,GAAI,cAAA,CAAe,CAAA;EAC5D,GAAA,UAAa,GAAA,WAAc,CAAA;EAnBL;AAC7B;;EA0BO,GAAA,CAAI,GAAA,EAAK,aAAA;EAxBuB;AAAA;AAAA;EA+BhC,MAAA,CAAO,GAAA;AAAA;AAAA,cAKH,SAAA,EAAS,SAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-database-action.mjs","names":[],"sources":["../../../../../../../core/src/database/create-database-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { createDatabase } from \"@warlock.js/cascade\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { config } from \"../config\";\r\n\r\nexport async function createDatabaseAction(command: CommandActionData) {\r\n const name = command.args[0];\r\n const { connection = \"default\" } = command.options;\r\n\r\n const connectionName =\r\n connection === \"default\" || connection === true ? undefined : String(connection);\r\n\r\n let databaseName = name;\r\n\r\n if (!databaseName) {\r\n if (connection === \"default\") {\r\n databaseName = config.get(\"database\")?.database;\r\n }\r\n }\r\n\r\n if (!databaseName) {\r\n log.error(\r\n \"database\",\r\n \"create\",\r\n \"Database name is required. Please provide a name or configure it in the database config.\",\r\n );\r\n return;\r\n }\r\n\r\n log.info(\"database\", \"create\", `Creating database ${colors.cyan(databaseName)}...`);\r\n\r\n try {\r\n const { created } = await createDatabase(databaseName, { connection: connectionName });\r\n\r\n if (created) {\r\n log.success(\r\n \"database\",\r\n \"create\",\r\n `Database ${colors.green(databaseName)} created successfully.`,\r\n );\r\n } else {\r\n log.warn(\"database\", \"create\", `Database ${colors.yellow(databaseName)} already exists.`);\r\n }\r\n } catch (error: any) {\r\n log.error(\r\n \"database\",\r\n \"create\",\r\n `Failed to create database ${colors.red(databaseName)}: ${error.message}`,\r\n );\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAMA,eAAsB,qBAAqB,SAA4B;CACrE,MAAM,OAAO,QAAQ,KAAK;CAC1B,MAAM,EAAE,aAAa,cAAc,QAAQ;CAE3C,MAAM,iBACJ,eAAe,aAAa,eAAe,OAAO,SAAY,OAAO,UAAU;CAEjF,IAAI,eAAe;CAEnB,IAAI,CAAC,cACH;MAAI,eAAe,WACjB,eAAe,OAAO,IAAI,UAAU,GAAG;CACzC;CAGF,IAAI,CAAC,cAAc;EACjB,IAAI,MACF,YACA,UACA,0FACF;EACA;CACF;CAEA,IAAI,KAAK,YAAY,UAAU,qBAAqB,OAAO,KAAK,YAAY,EAAE,IAAI;CAElF,IAAI;EACF,MAAM,EAAE,YAAY,MAAM,eAAe,cAAc,EAAE,YAAY,eAAe,CAAC;EAErF,IAAI,SACF,IAAI,QACF,YACA,UACA,YAAY,OAAO,MAAM,YAAY,EAAE,uBACzC;OAEA,IAAI,KAAK,YAAY,UAAU,YAAY,OAAO,OAAO,YAAY,EAAE,iBAAiB;CAE5F,SAAS,OAAY;EACnB,IAAI,MACF,YACA,UACA,6BAA6B,OAAO,IAAI,YAAY,EAAE,IAAI,MAAM,SAClE;CACF;AACF"}
1
+ {"version":3,"file":"create-database-action.mjs","names":[],"sources":["../../../../../../../core/src/database/create-database-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { createDatabase } from \"@warlock.js/cascade\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { config } from \"../config\";\r\n\r\nexport async function createDatabaseAction(command: CommandActionData) {\r\n const name = command.args[0];\r\n const { connection = \"default\" } = command.options;\r\n\r\n const connectionName =\r\n connection === \"default\" || connection === true ? undefined : String(connection);\r\n\r\n let databaseName = name;\r\n\r\n if (!databaseName) {\r\n if (connection === \"default\") {\r\n databaseName = config.get(\"database\")?.database;\r\n }\r\n }\r\n\r\n if (!databaseName) {\r\n log.error(\r\n \"database\",\r\n \"create\",\r\n \"Database name is required. Please provide a name or configure it in the database config.\",\r\n );\r\n return;\r\n }\r\n\r\n log.info(\"database\", \"create\", `Creating database ${colors.cyan(databaseName)}...`);\r\n\r\n try {\r\n const { created } = await createDatabase(databaseName, { connection: connectionName });\r\n\r\n if (created) {\r\n log.success(\r\n \"database\",\r\n \"create\",\r\n `Database ${colors.green(databaseName)} created successfully.`,\r\n );\r\n } else {\r\n log.warn(\"database\", \"create\", `Database ${colors.yellow(databaseName)} already exists.`);\r\n }\r\n } catch (error: any) {\r\n log.error(\r\n \"database\",\r\n \"create\",\r\n `Failed to create database ${colors.red(databaseName)}: ${error.message}`,\r\n );\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAMA,eAAsB,qBAAqB,SAA4B;CACrE,MAAM,OAAO,QAAQ,KAAK;CAC1B,MAAM,EAAE,aAAa,cAAc,QAAQ;CAE3C,MAAM,iBACJ,eAAe,aAAa,eAAe,OAAO,SAAY,OAAO,UAAU;CAEjF,IAAI,eAAe;CAEnB,IAAI,CAAC,cACH;MAAI,eAAe,WACjB,eAAe,OAAO,IAAI,UAAU,CAAC,EAAE;CACzC;CAGF,IAAI,CAAC,cAAc;EACjB,IAAI,MACF,YACA,UACA,0FACF;EACA;CACF;CAEA,IAAI,KAAK,YAAY,UAAU,qBAAqB,OAAO,KAAK,YAAY,EAAE,IAAI;CAElF,IAAI;EACF,MAAM,EAAE,YAAY,MAAM,eAAe,cAAc,EAAE,YAAY,eAAe,CAAC;EAErF,IAAI,SACF,IAAI,QACF,YACA,UACA,YAAY,OAAO,MAAM,YAAY,EAAE,uBACzC;OAEA,IAAI,KAAK,YAAY,UAAU,YAAY,OAAO,OAAO,YAAY,EAAE,iBAAiB;CAE5F,SAAS,OAAY;EACnB,IAAI,MACF,YACA,UACA,6BAA6B,OAAO,IAAI,YAAY,EAAE,IAAI,MAAM,SAClE;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"drop-tables-action.mjs","names":[],"sources":["../../../../../../../core/src/database/drop-tables-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { dataSourceRegistry, dropAllTables } from \"@warlock.js/cascade\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { confirm } from \"../cli/commands/generate/utils/prompt\";\r\nimport { CommandActionData } from \"../commands/types\";\r\n\r\nexport async function dropTablesAction(command: CommandActionData) {\r\n const { force } = command.options;\r\n\r\n if (force) {\r\n const { dropped } = await dropAllTables();\r\n log.success(\r\n \"database\",\r\n \"drop\",\r\n `Dropped ${colors.yellowBright(dropped)} tables successfully.`,\r\n );\r\n return;\r\n }\r\n\r\n // Preview phase — we keep direct driver access so we can show per-table\r\n // row counts before asking for confirmation. The actual drop still goes\r\n // through the Cascade Operations API.\r\n const driver = dataSourceRegistry.get().driver;\r\n const tables = await driver.blueprint.listTables();\r\n\r\n if (tables.length === 0) {\r\n log.warn(\"database\", \"drop\", \"No tables found in the database.\");\r\n return;\r\n }\r\n\r\n log.info(\"database\", \"drop\", `Found ${colors.yellowBright(tables.length)} tables:`);\r\n\r\n for (const table of tables) {\r\n const count = await driver.queryBuilder(table).count();\r\n console.log(` - ${colors.cyan(table)}: ${colors.green(count)} rows`);\r\n }\r\n\r\n const confirmed = await confirm(\r\n `Are you sure you want to drop all ${colors.red(tables.length)} tables?`,\r\n );\r\n\r\n if (!confirmed) {\r\n log.info(\"database\", \"drop\", \"Operation cancelled.\");\r\n return;\r\n }\r\n\r\n const { dropped } = await dropAllTables();\r\n\r\n log.success(\r\n \"database\",\r\n \"drop\",\r\n `Dropped ${colors.yellowBright(dropped)} tables successfully.`,\r\n );\r\n}\r\n"],"mappings":";;;;;;AAMA,eAAsB,iBAAiB,SAA4B;CACjE,MAAM,EAAE,UAAU,QAAQ;CAE1B,IAAI,OAAO;EACT,MAAM,EAAE,YAAY,MAAM,cAAc;EACxC,IAAI,QACF,YACA,QACA,WAAW,OAAO,aAAa,OAAO,EAAE,sBAC1C;EACA;CACF;CAKA,MAAM,SAAS,mBAAmB,IAAI,EAAE;CACxC,MAAM,SAAS,MAAM,OAAO,UAAU,WAAW;CAEjD,IAAI,OAAO,WAAW,GAAG;EACvB,IAAI,KAAK,YAAY,QAAQ,kCAAkC;EAC/D;CACF;CAEA,IAAI,KAAK,YAAY,QAAQ,SAAS,OAAO,aAAa,OAAO,MAAM,EAAE,SAAS;CAElF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,MAAM,OAAO,aAAa,KAAK,EAAE,MAAM;EACrD,QAAQ,IAAI,OAAO,OAAO,KAAK,KAAK,EAAE,IAAI,OAAO,MAAM,KAAK,EAAE,MAAM;CACtE;CAMA,IAAI,CAAC,MAJmB,QACtB,qCAAqC,OAAO,IAAI,OAAO,MAAM,EAAE,SACjE,GAEgB;EACd,IAAI,KAAK,YAAY,QAAQ,sBAAsB;EACnD;CACF;CAEA,MAAM,EAAE,YAAY,MAAM,cAAc;CAExC,IAAI,QACF,YACA,QACA,WAAW,OAAO,aAAa,OAAO,EAAE,sBAC1C;AACF"}
1
+ {"version":3,"file":"drop-tables-action.mjs","names":[],"sources":["../../../../../../../core/src/database/drop-tables-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { dataSourceRegistry, dropAllTables } from \"@warlock.js/cascade\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { confirm } from \"../cli/commands/generate/utils/prompt\";\r\nimport { CommandActionData } from \"../commands/types\";\r\n\r\nexport async function dropTablesAction(command: CommandActionData) {\r\n const { force } = command.options;\r\n\r\n if (force) {\r\n const { dropped } = await dropAllTables();\r\n log.success(\r\n \"database\",\r\n \"drop\",\r\n `Dropped ${colors.yellowBright(dropped)} tables successfully.`,\r\n );\r\n return;\r\n }\r\n\r\n // Preview phase — we keep direct driver access so we can show per-table\r\n // row counts before asking for confirmation. The actual drop still goes\r\n // through the Cascade Operations API.\r\n const driver = dataSourceRegistry.get().driver;\r\n const tables = await driver.blueprint.listTables();\r\n\r\n if (tables.length === 0) {\r\n log.warn(\"database\", \"drop\", \"No tables found in the database.\");\r\n return;\r\n }\r\n\r\n log.info(\"database\", \"drop\", `Found ${colors.yellowBright(tables.length)} tables:`);\r\n\r\n for (const table of tables) {\r\n const count = await driver.queryBuilder(table).count();\r\n console.log(` - ${colors.cyan(table)}: ${colors.green(count)} rows`);\r\n }\r\n\r\n const confirmed = await confirm(\r\n `Are you sure you want to drop all ${colors.red(tables.length)} tables?`,\r\n );\r\n\r\n if (!confirmed) {\r\n log.info(\"database\", \"drop\", \"Operation cancelled.\");\r\n return;\r\n }\r\n\r\n const { dropped } = await dropAllTables();\r\n\r\n log.success(\r\n \"database\",\r\n \"drop\",\r\n `Dropped ${colors.yellowBright(dropped)} tables successfully.`,\r\n );\r\n}\r\n"],"mappings":";;;;;;AAMA,eAAsB,iBAAiB,SAA4B;CACjE,MAAM,EAAE,UAAU,QAAQ;CAE1B,IAAI,OAAO;EACT,MAAM,EAAE,YAAY,MAAM,cAAc;EACxC,IAAI,QACF,YACA,QACA,WAAW,OAAO,aAAa,OAAO,EAAE,sBAC1C;EACA;CACF;CAKA,MAAM,SAAS,mBAAmB,IAAI,CAAC,CAAC;CACxC,MAAM,SAAS,MAAM,OAAO,UAAU,WAAW;CAEjD,IAAI,OAAO,WAAW,GAAG;EACvB,IAAI,KAAK,YAAY,QAAQ,kCAAkC;EAC/D;CACF;CAEA,IAAI,KAAK,YAAY,QAAQ,SAAS,OAAO,aAAa,OAAO,MAAM,EAAE,SAAS;CAElF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,MAAM,OAAO,aAAa,KAAK,CAAC,CAAC,MAAM;EACrD,QAAQ,IAAI,OAAO,OAAO,KAAK,KAAK,EAAE,IAAI,OAAO,MAAM,KAAK,EAAE,MAAM;CACtE;CAMA,IAAI,CAAC,MAJmB,QACtB,qCAAqC,OAAO,IAAI,OAAO,MAAM,EAAE,SACjE,GAEgB;EACd,IAAI,KAAK,YAAY,QAAQ,sBAAsB;EACnD;CACF;CAEA,MAAM,EAAE,YAAY,MAAM,cAAc;CAExC,IAAI,QACF,YACA,QACA,WAAW,OAAO,aAAa,OAAO,EAAE,sBAC1C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-action.mjs","names":[],"sources":["../../../../../../../core/src/database/migrate-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n exportMigrationsSQL,\r\n freshMigrate,\r\n listExecutedMigrations,\r\n Migration,\n type MigrationConstructor,\r\n migrationRunner,\r\n rollbackMigrations,\r\n runMigrations,\r\n} from \"@warlock.js/cascade\";\r\nimport dayjs from \"dayjs\";\r\nimport path from \"path\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { exitCodeFor, PENDING_EXIT_CODE } from \"./pending-exit-code\";\r\nimport {\r\n resolvePendingMigrations,\r\n type PendingMigrationsResult,\r\n} from \"./resolve-pending-migrations\";\r\n\r\n/**\r\n * `--list` — print the migration state: what has run, then what will run next.\r\n *\r\n * The executed section is printed FIRST and unconditionally, before anything\r\n * touches disk or config. That ordering is the contract: reading migrations\r\n * from the table cannot fail because of a broken migration file, and `--list`\r\n * is the command an operator reaches for *during* an incident. The pending\r\n * section is best-effort on top of an answer that is already on screen.\r\n *\r\n * Always exits 0. It is a report; `--pending` is the gate.\r\n */\r\nasync function listMigrationsAction() {\r\n await printExecutedMigrations();\r\n\r\n const pending = await resolvePendingMigrations(loadAllMigrations);\r\n\r\n printPendingMigrations(pending);\r\n}\r\n\r\nasync function printExecutedMigrations() {\r\n const createdMigrations = await listExecutedMigrations();\r\n\r\n console.log(`\\nTotal Executed Migrations: ${colors.green(createdMigrations.length)}\\n`);\r\n\r\n if (createdMigrations.length === 0) {\r\n console.log(colors.gray(\" No migrations have been executed yet.\\n\"));\r\n return;\r\n }\r\n\r\n // Display each migration as a block\r\n for (const migration of createdMigrations) {\r\n const executedAt = dayjs(migration.executedAt).format(\"DD-MM-YYYY hh:mm:ss A\");\r\n const createdAt = migration.createdAt\r\n ? dayjs(migration.createdAt).format(\"DD-MM-YYYY hh:mm:ss A\")\r\n : null;\r\n\r\n // Migration name with checkmark icon\r\n console.log(` ${colors.green(\"✔\")} ${colors.cyanBright(migration.name)}`);\r\n\r\n // Executed date\r\n console.log(` ${colors.gray(\"Executed:\")} ${colors.white(executedAt)}`);\r\n\r\n // Created date (if available)\r\n if (createdAt) {\r\n console.log(` ${colors.gray(\"Created:\")} ${colors.yellow(createdAt)}`);\r\n }\r\n\r\n console.log(\"\"); // Empty line between migrations\r\n }\r\n}\r\n\r\n/**\r\n * Print the pending section shared by `--list` and `--pending`.\r\n *\r\n * An unavailable result prints an explicit line and NEVER a count. \"0 pending\"\r\n * over a tree we failed to read is the one output this whole feature exists to\r\n * prevent.\r\n */\r\nfunction printPendingMigrations(result: PendingMigrationsResult) {\r\n if (result.type === \"unavailable\") {\r\n console.log(\r\n `${colors.yellowBright(\"Pending: unavailable\")} — ${result.reason}\\n` +\r\n colors.gray(\" The executed list above is still accurate.\\n\"),\r\n );\r\n return;\r\n }\r\n\r\n const { migrations } = result;\r\n\r\n console.log(`Total Pending Migrations: ${colors.green(migrations.length)}\\n`);\r\n\r\n if (migrations.length === 0) {\r\n console.log(colors.gray(\" Nothing pending — the database is up to date.\\n\"));\r\n return;\r\n }\r\n\r\n // Numbered because the ORDER is the answer: this is what the next\r\n // `warlock migrate` will do, in the sequence it will do it.\r\n migrations.forEach((migration, index) => {\r\n console.log(` ${colors.gray(`${index + 1}.`)} ${colors.cyanBright(migration.name)}`);\r\n });\r\n\r\n console.log(\"\");\r\n}\r\n\r\n/**\r\n * `--pending` — the scriptable half. Same computation as `--list`'s pending\r\n * section, different exit contract: see {@link PENDING_EXIT_CODE}.\r\n */\r\nasync function pendingMigrationsAction() {\r\n const result = await resolvePendingMigrations(loadAllMigrations);\r\n\r\n printPendingMigrations(result);\r\n\r\n const exitCode = exitCodeFor(result);\r\n\r\n if (exitCode === PENDING_EXIT_CODE.unavailable) {\r\n console.error(\r\n colors.redBright(\"Could not determine the pending set — exiting 2 rather than reporting 0.\"),\r\n );\r\n }\r\n\r\n process.exit(exitCode);\r\n}\r\n\r\nasync function allMigrationsFilesAction() {\r\n // get all available migration files in the project\r\n const files = (await migrationFiles()).map((path) => Path.toRelative(path));\r\n console.log(`Total Migration Files: ${colors.green(files.length)}`);\r\n\r\n for (const file of files) {\r\n console.log(colors.yellowBright(file));\r\n }\r\n}\r\n\r\n/**\r\n * If path is provided, then run the migration runner against that file only\r\n * If fresh is provided, then rollback all migrations and run all migrations\r\n * If rollback is provided, then run the migration runner against all files in reverse order\r\n */\r\nexport async function migrateAction(options: CommandActionData) {\r\n const { fresh, path, rollback, all, list, pending, sql, pendingOnly, compact } = options.options;\r\n\r\n if (list) {\r\n return await listMigrationsAction();\r\n }\r\n\r\n if (pending) {\r\n return await pendingMigrationsAction();\r\n }\r\n\r\n if (all) {\r\n return await allMigrationsFilesAction();\r\n }\r\n\r\n if (path) {\r\n await loadMigrationFile(Path.toAbsolute(path as string));\r\n } else {\r\n await loadAllMigrations();\r\n }\r\n\r\n if (fresh && rollback) {\r\n console.log(colors.redBright(\"You can't use --fresh and --rollback together\"));\r\n process.exit(1);\r\n }\r\n\r\n if (rollback) {\r\n await rollbackMigrations({ all: true });\r\n return;\r\n }\r\n\r\n if (sql) {\r\n await exportMigrationsSQL({\r\n pendingOnly: pendingOnly as boolean,\r\n compact: compact as boolean,\r\n });\r\n return;\r\n }\r\n\r\n if (fresh) {\r\n await freshMigrate();\r\n return;\r\n }\r\n\r\n await runMigrations();\r\n}\r\n\r\nasync function loadMigrationFile(absPath: string) {\r\n const relativePath = Path.toRelative(absPath);\r\n\r\n // MigrationConstructor, not `typeof Migration`: a migration file default-exports a\r\n // CONCRETE subclass, while Migration itself is abstract and so cannot satisfy a\r\n // `new () => ...` parameter. The annotation described the base class rather than\r\n // what is actually loaded, and that is what migrationRunner.register() rejected.\r\n const loadedModule = await filesOrchestrator.load<{ default: MigrationConstructor }>(relativePath);\r\n\r\n if (!loadedModule?.default) {\r\n throw new Error(`${Path.toRelative(absPath)} must have a default export`);\r\n }\r\n\r\n const MigrationClass = loadedModule.default;\r\n\r\n if (!MigrationClass.migrationName) {\r\n MigrationClass.migrationName = path\r\n .basename(absPath)\r\n .split(\".\")[0]\r\n .replace(\"-migration\", \"\")\r\n .replace(\"_migration\", \"\");\r\n }\r\n\r\n // Extract createdAt timestamp from filename if not already set\r\n // Expected format: MM-DD-YYYY_HH-MM-SS-name.migration.ts\r\n if (!MigrationClass.createdAt) {\r\n const filename = path.basename(absPath);\r\n const timestampMatch = filename.match(/^(\\d{2}-\\d{2}-\\d{4}_\\d{2}-\\d{2}-\\d{2})/);\r\n if (timestampMatch) {\r\n MigrationClass.createdAt = timestampMatch[1];\r\n }\r\n }\r\n\r\n migrationRunner.register(MigrationClass);\r\n}\r\n\r\n/**\r\n *\r\n * @returns List of absolute paths to migration files\r\n */\r\nasync function migrationFiles() {\r\n const migrationFiles = await getFilesFromDirectory(srcPath(\"app\"), \"*/models/*/migrations/*\");\r\n const separateMigrationsFolderFIles = await getFilesFromDirectory(\r\n srcPath(\"app\"),\r\n \"*/migrations/*\",\r\n );\r\n\r\n const migrations = [...migrationFiles, ...separateMigrationsFolderFIles];\r\n\r\n return migrations;\r\n}\r\n\r\nasync function loadAllMigrations() {\r\n // Load config-registered migrations (from packages like @warlock.js/auth)\r\n const configMigrations = warlockConfigManager.get(\"database\")?.migrations || [];\r\n\r\n for (const MigrationClass of configMigrations) {\r\n // Use class name as migration name if not set\r\n if (!MigrationClass.migrationName) {\r\n MigrationClass.migrationName = MigrationClass.name;\r\n }\r\n migrationRunner.register(MigrationClass);\r\n }\r\n\r\n // Always load file-based migrations from src/app, regardless of config\r\n const migrations = await migrationFiles();\r\n for (const migrationFile of migrations) {\r\n await loadMigrationFile(migrationFile);\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"migrate-action.mjs","names":[],"sources":["../../../../../../../core/src/database/migrate-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n exportMigrationsSQL,\r\n freshMigrate,\r\n listExecutedMigrations,\r\n Migration,\n type MigrationConstructor,\r\n migrationRunner,\r\n rollbackMigrations,\r\n runMigrations,\r\n} from \"@warlock.js/cascade\";\r\nimport dayjs from \"dayjs\";\r\nimport path from \"path\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { exitCodeFor, PENDING_EXIT_CODE } from \"./pending-exit-code\";\r\nimport {\r\n resolvePendingMigrations,\r\n type PendingMigrationsResult,\r\n} from \"./resolve-pending-migrations\";\r\n\r\n/**\r\n * `--list` — print the migration state: what has run, then what will run next.\r\n *\r\n * The executed section is printed FIRST and unconditionally, before anything\r\n * touches disk or config. That ordering is the contract: reading migrations\r\n * from the table cannot fail because of a broken migration file, and `--list`\r\n * is the command an operator reaches for *during* an incident. The pending\r\n * section is best-effort on top of an answer that is already on screen.\r\n *\r\n * Always exits 0. It is a report; `--pending` is the gate.\r\n */\r\nasync function listMigrationsAction() {\r\n await printExecutedMigrations();\r\n\r\n const pending = await resolvePendingMigrations(loadAllMigrations);\r\n\r\n printPendingMigrations(pending);\r\n}\r\n\r\nasync function printExecutedMigrations() {\r\n const createdMigrations = await listExecutedMigrations();\r\n\r\n console.log(`\\nTotal Executed Migrations: ${colors.green(createdMigrations.length)}\\n`);\r\n\r\n if (createdMigrations.length === 0) {\r\n console.log(colors.gray(\" No migrations have been executed yet.\\n\"));\r\n return;\r\n }\r\n\r\n // Display each migration as a block\r\n for (const migration of createdMigrations) {\r\n const executedAt = dayjs(migration.executedAt).format(\"DD-MM-YYYY hh:mm:ss A\");\r\n const createdAt = migration.createdAt\r\n ? dayjs(migration.createdAt).format(\"DD-MM-YYYY hh:mm:ss A\")\r\n : null;\r\n\r\n // Migration name with checkmark icon\r\n console.log(` ${colors.green(\"✔\")} ${colors.cyanBright(migration.name)}`);\r\n\r\n // Executed date\r\n console.log(` ${colors.gray(\"Executed:\")} ${colors.white(executedAt)}`);\r\n\r\n // Created date (if available)\r\n if (createdAt) {\r\n console.log(` ${colors.gray(\"Created:\")} ${colors.yellow(createdAt)}`);\r\n }\r\n\r\n console.log(\"\"); // Empty line between migrations\r\n }\r\n}\r\n\r\n/**\r\n * Print the pending section shared by `--list` and `--pending`.\r\n *\r\n * An unavailable result prints an explicit line and NEVER a count. \"0 pending\"\r\n * over a tree we failed to read is the one output this whole feature exists to\r\n * prevent.\r\n */\r\nfunction printPendingMigrations(result: PendingMigrationsResult) {\r\n if (result.type === \"unavailable\") {\r\n console.log(\r\n `${colors.yellowBright(\"Pending: unavailable\")} — ${result.reason}\\n` +\r\n colors.gray(\" The executed list above is still accurate.\\n\"),\r\n );\r\n return;\r\n }\r\n\r\n const { migrations } = result;\r\n\r\n console.log(`Total Pending Migrations: ${colors.green(migrations.length)}\\n`);\r\n\r\n if (migrations.length === 0) {\r\n console.log(colors.gray(\" Nothing pending — the database is up to date.\\n\"));\r\n return;\r\n }\r\n\r\n // Numbered because the ORDER is the answer: this is what the next\r\n // `warlock migrate` will do, in the sequence it will do it.\r\n migrations.forEach((migration, index) => {\r\n console.log(` ${colors.gray(`${index + 1}.`)} ${colors.cyanBright(migration.name)}`);\r\n });\r\n\r\n console.log(\"\");\r\n}\r\n\r\n/**\r\n * `--pending` — the scriptable half. Same computation as `--list`'s pending\r\n * section, different exit contract: see {@link PENDING_EXIT_CODE}.\r\n */\r\nasync function pendingMigrationsAction() {\r\n const result = await resolvePendingMigrations(loadAllMigrations);\r\n\r\n printPendingMigrations(result);\r\n\r\n const exitCode = exitCodeFor(result);\r\n\r\n if (exitCode === PENDING_EXIT_CODE.unavailable) {\r\n console.error(\r\n colors.redBright(\"Could not determine the pending set — exiting 2 rather than reporting 0.\"),\r\n );\r\n }\r\n\r\n process.exit(exitCode);\r\n}\r\n\r\nasync function allMigrationsFilesAction() {\r\n // get all available migration files in the project\r\n const files = (await migrationFiles()).map((path) => Path.toRelative(path));\r\n console.log(`Total Migration Files: ${colors.green(files.length)}`);\r\n\r\n for (const file of files) {\r\n console.log(colors.yellowBright(file));\r\n }\r\n}\r\n\r\n/**\r\n * If path is provided, then run the migration runner against that file only\r\n * If fresh is provided, then rollback all migrations and run all migrations\r\n * If rollback is provided, then run the migration runner against all files in reverse order\r\n */\r\nexport async function migrateAction(options: CommandActionData) {\r\n const { fresh, path, rollback, all, list, pending, sql, pendingOnly, compact } = options.options;\r\n\r\n if (list) {\r\n return await listMigrationsAction();\r\n }\r\n\r\n if (pending) {\r\n return await pendingMigrationsAction();\r\n }\r\n\r\n if (all) {\r\n return await allMigrationsFilesAction();\r\n }\r\n\r\n if (path) {\r\n await loadMigrationFile(Path.toAbsolute(path as string));\r\n } else {\r\n await loadAllMigrations();\r\n }\r\n\r\n if (fresh && rollback) {\r\n console.log(colors.redBright(\"You can't use --fresh and --rollback together\"));\r\n process.exit(1);\r\n }\r\n\r\n if (rollback) {\r\n await rollbackMigrations({ all: true });\r\n return;\r\n }\r\n\r\n if (sql) {\r\n await exportMigrationsSQL({\r\n pendingOnly: pendingOnly as boolean,\r\n compact: compact as boolean,\r\n });\r\n return;\r\n }\r\n\r\n if (fresh) {\r\n await freshMigrate();\r\n return;\r\n }\r\n\r\n await runMigrations();\r\n}\r\n\r\nasync function loadMigrationFile(absPath: string) {\r\n const relativePath = Path.toRelative(absPath);\r\n\r\n // MigrationConstructor, not `typeof Migration`: a migration file default-exports a\r\n // CONCRETE subclass, while Migration itself is abstract and so cannot satisfy a\r\n // `new () => ...` parameter. The annotation described the base class rather than\r\n // what is actually loaded, and that is what migrationRunner.register() rejected.\r\n const loadedModule = await filesOrchestrator.load<{ default: MigrationConstructor }>(relativePath);\r\n\r\n if (!loadedModule?.default) {\r\n throw new Error(`${Path.toRelative(absPath)} must have a default export`);\r\n }\r\n\r\n const MigrationClass = loadedModule.default;\r\n\r\n if (!MigrationClass.migrationName) {\r\n MigrationClass.migrationName = path\r\n .basename(absPath)\r\n .split(\".\")[0]\r\n .replace(\"-migration\", \"\")\r\n .replace(\"_migration\", \"\");\r\n }\r\n\r\n // Extract createdAt timestamp from filename if not already set\r\n // Expected format: MM-DD-YYYY_HH-MM-SS-name.migration.ts\r\n if (!MigrationClass.createdAt) {\r\n const filename = path.basename(absPath);\r\n const timestampMatch = filename.match(/^(\\d{2}-\\d{2}-\\d{4}_\\d{2}-\\d{2}-\\d{2})/);\r\n if (timestampMatch) {\r\n MigrationClass.createdAt = timestampMatch[1];\r\n }\r\n }\r\n\r\n migrationRunner.register(MigrationClass);\r\n}\r\n\r\n/**\r\n *\r\n * @returns List of absolute paths to migration files\r\n */\r\nasync function migrationFiles() {\r\n const migrationFiles = await getFilesFromDirectory(srcPath(\"app\"), \"*/models/*/migrations/*\");\r\n const separateMigrationsFolderFIles = await getFilesFromDirectory(\r\n srcPath(\"app\"),\r\n \"*/migrations/*\",\r\n );\r\n\r\n const migrations = [...migrationFiles, ...separateMigrationsFolderFIles];\r\n\r\n return migrations;\r\n}\r\n\r\nasync function loadAllMigrations() {\r\n // Load config-registered migrations (from packages like @warlock.js/auth)\r\n const configMigrations = warlockConfigManager.get(\"database\")?.migrations || [];\r\n\r\n for (const MigrationClass of configMigrations) {\r\n // Use class name as migration name if not set\r\n if (!MigrationClass.migrationName) {\r\n MigrationClass.migrationName = MigrationClass.name;\r\n }\r\n migrationRunner.register(MigrationClass);\r\n }\r\n\r\n // Always load file-based migrations from src/app, regardless of config\r\n const migrations = await migrationFiles();\r\n for (const migrationFile of migrations) {\r\n await loadMigrationFile(migrationFile);\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"seed-command-action.mjs","names":["runInTransaction"],"sources":["../../../../../../../core/src/database/seed-command-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n DataSource,\r\n dataSourceRegistry,\r\n transaction as runInTransaction,\r\n} from \"@warlock.js/cascade\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport { Seeder } from \"./seeds/seeder\";\r\nimport { SeedersManager } from \"./seeds/seeders.manager\";\r\nimport { SeedClock, SeedRecordRef } from \"./seeds/types\";\r\nimport { seedRecordsTableName, seedsTableName } from \"./seeds/utils\";\r\n\r\n/**\r\n * Programmatic-only overrides for {@link seedCommandAction}.\r\n *\r\n * Not derived from CLI flags (the CLI passes only {@link CommandActionData}),\r\n * so a back-fill script or a test can inject behaviour the command line can't\r\n * express.\r\n */\r\nexport type SeedCommandOverrides = {\r\n /**\r\n * Clock the run reads for the `now` handed to each seeder and for the\r\n * seeds-log metadata timestamps. Defaults to `() => new Date()`. Forwarded to\r\n * {@link SeedersManager}'s `clock` option.\r\n */\r\n clock?: SeedClock;\r\n};\r\n\r\nasync function clearAllTables(datasource: DataSource) {\r\n const tables = await datasource.driver.blueprint.listTables();\r\n\r\n for (const table of tables) {\r\n await datasource.driver.truncateTable(table, { cascade: true });\r\n }\r\n}\r\n\r\ntype SeedRecordRow = SeedRecordRef & { id: number };\r\n\r\n/**\r\n * Undo seeded data by deleting the records a seeder tracked via `track`.\r\n *\r\n * Reads `seed_records` (all seeders, or a single `seederName`), then deletes\r\n * the referenced records in REVERSE seed-order and, within each seed, in\r\n * reverse insertion-order. Insertion id is strictly increasing across the\r\n * whole run, so deleting by descending `id` satisfies both at once. The whole\r\n * operation runs in a single transaction, and the matching seeds-log rows are\r\n * reset afterwards so `once: true` seeds re-run on the next `warlock seed`.\r\n *\r\n * @returns the number of tracked records deleted.\r\n */\r\nexport async function dropSeedRecords(\r\n datasource: DataSource,\r\n seederName?: string,\r\n): Promise<number> {\r\n const driver = datasource.driver;\r\n\r\n if (!(await driver.blueprint.tableExists(seedRecordsTableName))) {\r\n return 0;\r\n }\r\n\r\n const query = driver.queryBuilder<SeedRecordRow>(seedRecordsTableName);\r\n\r\n if (seederName) {\r\n query.where(\"seeder\", seederName);\r\n }\r\n\r\n const rows = await query.get();\r\n\r\n if (rows.length === 0) {\r\n return 0;\r\n }\r\n\r\n // Descending insertion id == reverse seed-order + reverse within-seed order.\r\n const ordered = [...rows].sort((a, b) => b.id - a.id);\r\n\r\n await runInTransaction(async () => {\r\n for (const row of ordered) {\r\n await driver.deleteMany(row.table, { id: row.recordId });\r\n }\r\n\r\n // Clear the tracked refs we just acted on.\r\n if (seederName) {\r\n await driver.deleteMany(seedRecordsTableName, { seeder: seederName });\r\n } else {\r\n await driver.deleteMany(seedRecordsTableName, {});\r\n }\r\n\r\n // Reset the seeds-log rows so `once: true` seeds re-run.\r\n const seederNames = seederName\r\n ? [seederName]\r\n : [...new Set(ordered.map((row) => row.seeder))];\r\n\r\n for (const name of seederNames) {\r\n await driver.deleteMany(seedsTableName, { name });\r\n }\r\n });\r\n\r\n return ordered.length;\r\n}\r\n\r\n/**\r\n * Run database seeds.\r\n *\r\n * @example\r\n * ```ts\r\n * import { seedCommandAction } from \"@warlock.js/core\";\r\n *\r\n * await seedCommandAction({\r\n * command: \"seed\",\r\n * options: { fresh: true, list: false }\r\n * });\r\n * ```\r\n *\r\n * @param overrides Programmatic-only overrides (e.g. an injected `clock`); the\r\n * CLI never passes this, so the command-line signature stays unchanged.\r\n */\r\nexport async function seedCommandAction(\r\n options: CommandActionData,\r\n overrides?: SeedCommandOverrides,\r\n) {\r\n const { path, fresh, transaction, list, drop } = options.options;\r\n\r\n const datasource = dataSourceRegistry.get();\r\n\r\n if (drop) {\r\n // `--drop` (boolean) undoes every tracked record; `--drop=<name>` scopes to\r\n // a single seeder.\r\n const seederName = typeof drop === \"string\" ? drop : undefined;\r\n\r\n const deleted = await dropSeedRecords(datasource, seederName);\r\n\r\n const scope = seederName ? colors.cyan(seederName) : \"all seeders\";\r\n console.log(\r\n `🗑️ Dropped ${colors.redBright(deleted)} tracked record(s) for ${scope}; matching seeds-log entries reset.`,\r\n );\r\n return;\r\n }\r\n\r\n if (fresh) {\r\n await clearAllTables(datasource);\r\n }\r\n\r\n if (list) {\r\n const seedFiles = await listSeedsFiles();\r\n\r\n if (seedFiles.length === 0) {\r\n console.log(\"No seeds found.\");\r\n return;\r\n }\r\n\r\n const seedersManager = new SeedersManager();\r\n\r\n seedersManager.register(...seedFiles);\r\n\r\n const seeds = seedersManager.sort().seeders.map((seed) => {\r\n return {\r\n name: seed.name,\r\n order: seed.order,\r\n enabled: seed.enabled,\r\n };\r\n });\r\n\r\n console.table(seeds);\r\n\r\n console.log(\r\n `Total Seeds: ${colors.blueBright(seeds.length)}, enabled: ${colors.greenBright(seeds.filter((seed) => seed.enabled !== false).length)}, disabled: ${colors.redBright(seeds.filter((seed) => seed.enabled === false).length)}`,\r\n );\r\n return;\r\n }\r\n\r\n const seeds = path\r\n ? [await loadSeedFile(Path.toAbsolute(path as string))]\r\n : await listSeedsFiles();\r\n\r\n const seedersManager = new SeedersManager({ clock: overrides?.clock });\r\n\r\n seedersManager.register(...seeds);\r\n\r\n await seedersManager.run(transaction as boolean);\r\n}\r\n\r\nasync function listSeedsFiles() {\r\n const seedsFiles = await getFilesFromDirectory(srcPath(\"app\"), \"*/seeds/*.ts\");\r\n\r\n const seeds = [];\r\n\r\n for (const seedFile of seedsFiles) {\r\n const seed = await loadSeedFile(seedFile);\r\n seeds.push(seed);\r\n }\r\n\r\n return seeds;\r\n}\r\n\r\nasync function loadSeedFile(absPath: string): Promise<Seeder> {\r\n const relativePath = Path.toRelative(absPath);\r\n const seedImport = await filesOrchestrator.load<{ default: Seeder }>(relativePath);\r\n\r\n if (!seedImport || !seedImport.default) {\r\n throw new Error(`Seeder file ${relativePath} does not export a default seeder.`);\r\n }\r\n\r\n return seedImport.default;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"seed-command-action.mjs","names":["runInTransaction"],"sources":["../../../../../../../core/src/database/seed-command-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n DataSource,\r\n dataSourceRegistry,\r\n transaction as runInTransaction,\r\n} from \"@warlock.js/cascade\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport { Seeder } from \"./seeds/seeder\";\r\nimport { SeedersManager } from \"./seeds/seeders.manager\";\r\nimport { SeedClock, SeedRecordRef } from \"./seeds/types\";\r\nimport { seedRecordsTableName, seedsTableName } from \"./seeds/utils\";\r\n\r\n/**\r\n * Programmatic-only overrides for {@link seedCommandAction}.\r\n *\r\n * Not derived from CLI flags (the CLI passes only {@link CommandActionData}),\r\n * so a back-fill script or a test can inject behaviour the command line can't\r\n * express.\r\n */\r\nexport type SeedCommandOverrides = {\r\n /**\r\n * Clock the run reads for the `now` handed to each seeder and for the\r\n * seeds-log metadata timestamps. Defaults to `() => new Date()`. Forwarded to\r\n * {@link SeedersManager}'s `clock` option.\r\n */\r\n clock?: SeedClock;\r\n};\r\n\r\nasync function clearAllTables(datasource: DataSource) {\r\n const tables = await datasource.driver.blueprint.listTables();\r\n\r\n for (const table of tables) {\r\n await datasource.driver.truncateTable(table, { cascade: true });\r\n }\r\n}\r\n\r\ntype SeedRecordRow = SeedRecordRef & { id: number };\r\n\r\n/**\r\n * Undo seeded data by deleting the records a seeder tracked via `track`.\r\n *\r\n * Reads `seed_records` (all seeders, or a single `seederName`), then deletes\r\n * the referenced records in REVERSE seed-order and, within each seed, in\r\n * reverse insertion-order. Insertion id is strictly increasing across the\r\n * whole run, so deleting by descending `id` satisfies both at once. The whole\r\n * operation runs in a single transaction, and the matching seeds-log rows are\r\n * reset afterwards so `once: true` seeds re-run on the next `warlock seed`.\r\n *\r\n * @returns the number of tracked records deleted.\r\n */\r\nexport async function dropSeedRecords(\r\n datasource: DataSource,\r\n seederName?: string,\r\n): Promise<number> {\r\n const driver = datasource.driver;\r\n\r\n if (!(await driver.blueprint.tableExists(seedRecordsTableName))) {\r\n return 0;\r\n }\r\n\r\n const query = driver.queryBuilder<SeedRecordRow>(seedRecordsTableName);\r\n\r\n if (seederName) {\r\n query.where(\"seeder\", seederName);\r\n }\r\n\r\n const rows = await query.get();\r\n\r\n if (rows.length === 0) {\r\n return 0;\r\n }\r\n\r\n // Descending insertion id == reverse seed-order + reverse within-seed order.\r\n const ordered = [...rows].sort((a, b) => b.id - a.id);\r\n\r\n await runInTransaction(async () => {\r\n for (const row of ordered) {\r\n await driver.deleteMany(row.table, { id: row.recordId });\r\n }\r\n\r\n // Clear the tracked refs we just acted on.\r\n if (seederName) {\r\n await driver.deleteMany(seedRecordsTableName, { seeder: seederName });\r\n } else {\r\n await driver.deleteMany(seedRecordsTableName, {});\r\n }\r\n\r\n // Reset the seeds-log rows so `once: true` seeds re-run.\r\n const seederNames = seederName\r\n ? [seederName]\r\n : [...new Set(ordered.map((row) => row.seeder))];\r\n\r\n for (const name of seederNames) {\r\n await driver.deleteMany(seedsTableName, { name });\r\n }\r\n });\r\n\r\n return ordered.length;\r\n}\r\n\r\n/**\r\n * Run database seeds.\r\n *\r\n * @example\r\n * ```ts\r\n * import { seedCommandAction } from \"@warlock.js/core\";\r\n *\r\n * await seedCommandAction({\r\n * command: \"seed\",\r\n * options: { fresh: true, list: false }\r\n * });\r\n * ```\r\n *\r\n * @param overrides Programmatic-only overrides (e.g. an injected `clock`); the\r\n * CLI never passes this, so the command-line signature stays unchanged.\r\n */\r\nexport async function seedCommandAction(\r\n options: CommandActionData,\r\n overrides?: SeedCommandOverrides,\r\n) {\r\n const { path, fresh, transaction, list, drop } = options.options;\r\n\r\n const datasource = dataSourceRegistry.get();\r\n\r\n if (drop) {\r\n // `--drop` (boolean) undoes every tracked record; `--drop=<name>` scopes to\r\n // a single seeder.\r\n const seederName = typeof drop === \"string\" ? drop : undefined;\r\n\r\n const deleted = await dropSeedRecords(datasource, seederName);\r\n\r\n const scope = seederName ? colors.cyan(seederName) : \"all seeders\";\r\n console.log(\r\n `🗑️ Dropped ${colors.redBright(deleted)} tracked record(s) for ${scope}; matching seeds-log entries reset.`,\r\n );\r\n return;\r\n }\r\n\r\n if (fresh) {\r\n await clearAllTables(datasource);\r\n }\r\n\r\n if (list) {\r\n const seedFiles = await listSeedsFiles();\r\n\r\n if (seedFiles.length === 0) {\r\n console.log(\"No seeds found.\");\r\n return;\r\n }\r\n\r\n const seedersManager = new SeedersManager();\r\n\r\n seedersManager.register(...seedFiles);\r\n\r\n const seeds = seedersManager.sort().seeders.map((seed) => {\r\n return {\r\n name: seed.name,\r\n order: seed.order,\r\n enabled: seed.enabled,\r\n };\r\n });\r\n\r\n console.table(seeds);\r\n\r\n console.log(\r\n `Total Seeds: ${colors.blueBright(seeds.length)}, enabled: ${colors.greenBright(seeds.filter((seed) => seed.enabled !== false).length)}, disabled: ${colors.redBright(seeds.filter((seed) => seed.enabled === false).length)}`,\r\n );\r\n return;\r\n }\r\n\r\n const seeds = path\r\n ? [await loadSeedFile(Path.toAbsolute(path as string))]\r\n : await listSeedsFiles();\r\n\r\n const seedersManager = new SeedersManager({ clock: overrides?.clock });\r\n\r\n seedersManager.register(...seeds);\r\n\r\n await seedersManager.run(transaction as boolean);\r\n}\r\n\r\nasync function listSeedsFiles() {\r\n const seedsFiles = await getFilesFromDirectory(srcPath(\"app\"), \"*/seeds/*.ts\");\r\n\r\n const seeds = [];\r\n\r\n for (const seedFile of seedsFiles) {\r\n const seed = await loadSeedFile(seedFile);\r\n seeds.push(seed);\r\n }\r\n\r\n return seeds;\r\n}\r\n\r\nasync function loadSeedFile(absPath: string): Promise<Seeder> {\r\n const relativePath = Path.toRelative(absPath);\r\n const seedImport = await filesOrchestrator.load<{ default: Seeder }>(relativePath);\r\n\r\n if (!seedImport || !seedImport.default) {\r\n throw new Error(`Seeder file ${relativePath} does not export a default seeder.`);\r\n }\r\n\r\n return seedImport.default;\r\n}\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"seed-records-table-migration.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seed-records-table-migration.ts"],"sourcesContent":["import { Migration } from \"@warlock.js/cascade\";\nimport { seedRecordsTableName } from \"./utils\";\n\n/**\n * Creates the `seed_records` table that backs `warlock seed --drop`.\n *\n * Each row is a reference to a single record a seeder created, captured via the\n * `track` helper inside the same transaction the seed ran in. `--drop` reads\n * these refs to delete exactly (and only) the records a seed produced, then\n * resets the matching seeds-log row so a `once: true` seed can re-run.\n */\nexport class SeedRecordsTableMigration extends Migration {\n public static migrationName = \"seed-records-table-migration\";\n public table = seedRecordsTableName;\n\n public up() {\n this.createTableIfNotExists();\n\n this.id();\n this.text(\"seeder\");\n this.text(\"table\");\n this.text(\"recordId\");\n this.dateTime(\"runAt\").useCurrent();\n }\n\n public down() {\n // No argument: dropTableIfExists() operates on `this.table`, set as a class\n // field above. The name passed here was silently ignored at runtime.\n this.dropTableIfExists();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAWA,IAAa,4BAAb,cAA+C,UAAU;;;eAExC;;;uBADe;;CAG9B,AAAO,KAAK;EACV,KAAK,uBAAuB;EAE5B,KAAK,GAAG;EACR,KAAK,KAAK,QAAQ;EAClB,KAAK,KAAK,OAAO;EACjB,KAAK,KAAK,UAAU;EACpB,KAAK,SAAS,OAAO,EAAE,WAAW;CACpC;CAEA,AAAO,OAAO;EAGZ,KAAK,kBAAkB;CACzB;AACF"}
1
+ {"version":3,"file":"seed-records-table-migration.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seed-records-table-migration.ts"],"sourcesContent":["import { Migration } from \"@warlock.js/cascade\";\nimport { seedRecordsTableName } from \"./utils\";\n\n/**\n * Creates the `seed_records` table that backs `warlock seed --drop`.\n *\n * Each row is a reference to a single record a seeder created, captured via the\n * `track` helper inside the same transaction the seed ran in. `--drop` reads\n * these refs to delete exactly (and only) the records a seed produced, then\n * resets the matching seeds-log row so a `once: true` seed can re-run.\n */\nexport class SeedRecordsTableMigration extends Migration {\n public static migrationName = \"seed-records-table-migration\";\n public table = seedRecordsTableName;\n\n public up() {\n this.createTableIfNotExists();\n\n this.id();\n this.text(\"seeder\");\n this.text(\"table\");\n this.text(\"recordId\");\n this.dateTime(\"runAt\").useCurrent();\n }\n\n public down() {\n // No argument: dropTableIfExists() operates on `this.table`, set as a class\n // field above. The name passed here was silently ignored at runtime.\n this.dropTableIfExists();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAWA,IAAa,4BAAb,cAA+C,UAAU;;;eAExC;;;uBADe;;CAG9B,AAAO,KAAK;EACV,KAAK,uBAAuB;EAE5B,KAAK,GAAG;EACR,KAAK,KAAK,QAAQ;EAClB,KAAK,KAAK,OAAO;EACjB,KAAK,KAAK,UAAU;EACpB,KAAK,SAAS,OAAO,CAAC,CAAC,WAAW;CACpC;CAEA,AAAO,OAAO;EAGZ,KAAK,kBAAkB;CACzB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"seeders.manager.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seeders.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n type DataSource,\n type DriverContract,\n DatabaseWriterValidationError,\n dataSourceRegistry,\n migrationRunner,\n transaction,\n} from \"@warlock.js/cascade\";\nimport { Seeder } from \"./seeder\";\nimport { SeederDependencyCycleError, UnknownSeederDependencyError } from \"./seeder.errors\";\nimport { SeedRecordsTableMigration } from \"./seed-records-table-migration\";\nimport { SeedsTableMigration } from \"./seeds-table-migration\";\nimport {\n SeedClock,\n SeederMetadata,\n SeedRecordRef,\n SeedResult,\n Track,\n TrackableModel,\n} from \"./types\";\nimport { seedRecordsTableName, seedsTableName } from \"./utils\";\n\nexport type SeedersManagerOptions = {\n datasource?: DataSource;\n /**\n * Clock the manager reads for the `now` it hands each seeder AND for its own\n * metadata timestamps (`createdAt` / `firstRunAt` / `lastRunAt` / a tracked\n * ref's `runAt`). Defaults to `() => new Date()`. Override it to make a\n * historical back-fill or a test deterministic — every timestamp a run\n * produces then comes from this one clock.\n */\n clock?: SeedClock;\n};\n\nexport class SeedersManager {\n public seeders: Seeder[] = [];\n\n // Not optional: the constructor assigns unconditionally, falling back to\n // dataSourceRegistry.get(), which itself returns a DataSource rather than\n // undefined. The `?` here described the OPTIONS field, not this one, and made\n // every read of `this.datasource.driver` a type error. The options field stays\n // optional, because that input genuinely is.\n protected datasource: DataSource;\n\n /**\n * The single clock every timestamp in a run reads from. Defaults to\n * `() => new Date()`; overridable via {@link SeedersManagerOptions.clock}.\n */\n protected readonly now: SeedClock;\n\n public constructor(protected options?: SeedersManagerOptions) {\n this.datasource = options?.datasource ?? dataSourceRegistry.get();\n this.now = options?.clock ?? (() => new Date());\n }\n\n /**\n * Register new seeder\n */\n public register(...seeders: Seeder[]) {\n this.seeders.push(...seeders);\n return this;\n }\n\n /**\n * Initialize before running\n */\n protected async init() {\n if (!(await this.driver.blueprint.tableExists(seedsTableName))) {\n await migrationRunner.run(SeedsTableMigration);\n }\n\n if (!(await this.driver.blueprint.tableExists(seedRecordsTableName))) {\n await migrationRunner.run(SeedRecordsTableMigration);\n }\n }\n\n /**\n * Run seeders\n */\n public async run(withTransaction = true) {\n await this.init();\n\n this.prepareSeeders();\n\n console.log(`🌱 Running ${this.seeders.length} seeder(s)...\\n`);\n\n let successCount = 0;\n let skippedCount = 0;\n let failedCount = 0;\n\n for (const seeder of this.seeders) {\n // Check if already executed\n if (seeder.once && (await this.seederIsExecutedBefore(seeder))) {\n console.log(`⏭️ Skipping ${colors.yellow(seeder.name)} (already executed)`);\n skippedCount++;\n continue;\n }\n\n try {\n console.log(`🔄 Running ${colors.green(seeder.name)}...`);\n const startTime = Date.now();\n\n const refs: SeedRecordRef[] = [];\n const track = this.createTracker(seeder, refs);\n\n const runSeeder = async () => {\n // Keep LAST-RUN refs only: drop the prior refs for this seeder before\n // writing the new ones, so `--drop` undo stays bounded.\n await this.driver.deleteMany(seedRecordsTableName, { seeder: seeder.name });\n\n const result = await seeder.run({\n track,\n now: this.now,\n batchSize: seeder.batchSize ?? 0,\n });\n\n if (refs.length > 0) {\n await this.driver.insertMany(\n seedRecordsTableName,\n refs.map((ref) => ({ ...ref })),\n );\n }\n\n return result;\n };\n\n const result = withTransaction ? await transaction(runSeeder) : await runSeeder();\n\n const duration = Date.now() - startTime;\n\n // Auto-derive recordsCreated from the track count; a returned\n // SeedResult is an optional fallback when nothing was tracked.\n const recordsCreated = refs.length > 0 ? refs.length : (result?.recordsCreated ?? 0);\n\n await this.storeSeedsResults(seeder, { recordsCreated });\n\n console.log(\n `✅ ${colors.green(seeder.name)} completed (${duration}ms, ${recordsCreated} records)\\n`,\n );\n successCount++;\n } catch (error: any) {\n const err = error as Error;\n console.error(`❌ ${colors.red(seeder.name)} failed:`, err.message);\n console.log(err);\n failedCount++;\n\n if (error instanceof DatabaseWriterValidationError) {\n console.log(error.errors);\n }\n\n // Re-throw to stop execution (or continue to next seed based on your preference)\n throw error;\n }\n }\n\n // Summary\n console.log(\"\\n\" + \"=\".repeat(50));\n console.log(`✅ Success: ${successCount}`);\n\n if (skippedCount > 0) console.log(`⏭️ Skipped: ${skippedCount}`);\n\n if (failedCount > 0) console.log(`❌ Failed: ${failedCount}`);\n\n console.log(\"=\".repeat(50));\n }\n\n /**\n * Build a `track` helper bound to the given seeder. Tracked refs are pushed\n * into `refs` and persisted (within the run transaction) by the caller.\n *\n * Every overload returns its first argument so it can be chained inline.\n */\n protected createTracker(seeder: Seeder, refs: SeedRecordRef[]): Track {\n const push = (table: string, recordId: number | string) => {\n refs.push({ seeder: seeder.name, table, recordId, runAt: this.now() });\n };\n\n function track<T extends TrackableModel>(model: T): T;\n function track<T extends TrackableModel>(models: T[]): T[];\n function track(table: string, id: number | string): string;\n function track(\n arg: TrackableModel | TrackableModel[] | string,\n id?: number | string,\n ): TrackableModel | TrackableModel[] | string {\n if (typeof arg === \"string\") {\n push(arg, id as number | string);\n\n return arg;\n }\n\n if (Array.isArray(arg)) {\n for (const model of arg) {\n push(model.getTableName(), model.id);\n }\n\n return arg;\n }\n\n push(arg.getTableName(), arg.id);\n\n return arg;\n }\n\n return track;\n }\n\n /**\n * Sort seeds\n */\n public sort() {\n this.seeders = this.seeders.sort((a, b) => {\n const orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n const orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n return orderA - orderB;\n });\n\n return this;\n }\n\n /**\n * Prepare seeders to order by the seeder order, then resolve `dependsOn`.\n *\n * Dependencies are honored via a topological sort layered over the existing\n * numeric `order` tie-break: among seeders whose dependencies are already\n * satisfied, the lowest `order` (then registration order) runs first. Throws\n * {@link UnknownSeederDependencyError} for a missing dependency and\n * {@link SeederDependencyCycleError} for a cycle.\n */\n public prepareSeeders() {\n this.seeders = this.seeders.filter((seeder) => seeder.enabled !== false);\n\n this.sort();\n\n this.seeders = this.resolveDependencies(this.seeders);\n\n return this;\n }\n\n /**\n * Topologically sort `seeders` by their `dependsOn` names. The input is\n * assumed pre-sorted by `order` (see {@link sort}); that ordering is used as\n * the tie-break so dependency-free siblings keep their `order` sequence.\n */\n protected resolveDependencies(seeders: Seeder[]): Seeder[] {\n const byName = new Map<string, Seeder>();\n\n for (const seeder of seeders) {\n byName.set(seeder.name, seeder);\n }\n\n // Validate dependencies up-front so an unknown name is a clear error rather\n // than a silently dropped edge.\n for (const seeder of seeders) {\n for (const dependency of seeder.dependsOn ?? []) {\n if (!byName.has(dependency)) {\n throw new UnknownSeederDependencyError(seeder.name, dependency);\n }\n }\n }\n\n const sorted: Seeder[] = [];\n const state = new Map<string, \"visiting\" | \"visited\">();\n\n const visit = (seeder: Seeder, path: string[]) => {\n const status = state.get(seeder.name);\n\n if (status === \"visited\") {\n return;\n }\n\n if (status === \"visiting\") {\n const cycleStart = path.indexOf(seeder.name);\n const cycle = path.slice(cycleStart >= 0 ? cycleStart : 0).concat(seeder.name);\n\n throw new SeederDependencyCycleError(cycle);\n }\n\n state.set(seeder.name, \"visiting\");\n\n for (const dependency of seeder.dependsOn ?? []) {\n // Presence already validated above; the `!` is safe.\n visit(byName.get(dependency)!, [...path, seeder.name]);\n }\n\n state.set(seeder.name, \"visited\");\n sorted.push(seeder);\n };\n\n // Iterate in the incoming (order-sorted, registration-stable) sequence so\n // dependency-free seeders preserve their `order` tie-break.\n for (const seeder of seeders) {\n visit(seeder, []);\n }\n\n return sorted;\n }\n\n /**\n * Store seed results in db\n */\n public async storeSeedsResults(seeder: Seeder, result: SeedResult) {\n const oldResult = await this.getMetadata(seeder);\n\n // log with an emoji icon at beginning of text no of total craeted records\n console.log(`📊 Total records created: ${result.recordsCreated}`);\n\n // Read the single overridable clock once so every timestamp in this write\n // is identical (and pinned in a deterministic / historical run).\n const now = this.now();\n\n if (oldResult) {\n // Update existing record - use query builder with WHERE clause\n await this.driver\n .queryBuilder(seedsTableName)\n .where(\"name\", seeder.name)\n .update({\n runCount: oldResult.runCount + 1,\n lastRunAt: now,\n totalRecordsCreated: oldResult.totalRecordsCreated + result.recordsCreated,\n lastRunRecordsCreated: result.recordsCreated,\n });\n\n // now display total created records so far\n console.log(\n `📊 Total records created so far: ${oldResult.totalRecordsCreated + result.recordsCreated}`,\n );\n } else {\n // Insert new record\n await this.driver.insert(seedsTableName, {\n name: seeder.name,\n createdAt: now,\n firstRunAt: now,\n lastRunAt: now,\n runCount: 1,\n totalRecordsCreated: result.recordsCreated,\n lastRunRecordsCreated: result.recordsCreated,\n });\n }\n }\n\n /**\n * Get seed info from database\n */\n // Returns null when the seeder has never run. first() has always resolved to\n // SeederMetadata | null; annotating it as non-null pushed that lie to every\n // caller, each of which already branches on the absent case.\n protected getMetadata(seeder: Seeder): Promise<SeederMetadata | null> {\n const driver = this.datasource.driver;\n\n return driver.queryBuilder(seedsTableName).where(\"name\", seeder.name).first();\n }\n\n /**\n * Get driver instance\n */\n protected get driver(): DriverContract {\n return this.datasource.driver;\n }\n\n /**\n * Check if seeder has been executed before\n */\n protected async seederIsExecutedBefore(seeder: Seeder) {\n return !!(await this.getMetadata(seeder));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"seeders.manager.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seeders.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n type DataSource,\n type DriverContract,\n DatabaseWriterValidationError,\n dataSourceRegistry,\n migrationRunner,\n transaction,\n} from \"@warlock.js/cascade\";\nimport { Seeder } from \"./seeder\";\nimport { SeederDependencyCycleError, UnknownSeederDependencyError } from \"./seeder.errors\";\nimport { SeedRecordsTableMigration } from \"./seed-records-table-migration\";\nimport { SeedsTableMigration } from \"./seeds-table-migration\";\nimport {\n SeedClock,\n SeederMetadata,\n SeedRecordRef,\n SeedResult,\n Track,\n TrackableModel,\n} from \"./types\";\nimport { seedRecordsTableName, seedsTableName } from \"./utils\";\n\nexport type SeedersManagerOptions = {\n datasource?: DataSource;\n /**\n * Clock the manager reads for the `now` it hands each seeder AND for its own\n * metadata timestamps (`createdAt` / `firstRunAt` / `lastRunAt` / a tracked\n * ref's `runAt`). Defaults to `() => new Date()`. Override it to make a\n * historical back-fill or a test deterministic — every timestamp a run\n * produces then comes from this one clock.\n */\n clock?: SeedClock;\n};\n\nexport class SeedersManager {\n public seeders: Seeder[] = [];\n\n // Not optional: the constructor assigns unconditionally, falling back to\n // dataSourceRegistry.get(), which itself returns a DataSource rather than\n // undefined. The `?` here described the OPTIONS field, not this one, and made\n // every read of `this.datasource.driver` a type error. The options field stays\n // optional, because that input genuinely is.\n protected datasource: DataSource;\n\n /**\n * The single clock every timestamp in a run reads from. Defaults to\n * `() => new Date()`; overridable via {@link SeedersManagerOptions.clock}.\n */\n protected readonly now: SeedClock;\n\n public constructor(protected options?: SeedersManagerOptions) {\n this.datasource = options?.datasource ?? dataSourceRegistry.get();\n this.now = options?.clock ?? (() => new Date());\n }\n\n /**\n * Register new seeder\n */\n public register(...seeders: Seeder[]) {\n this.seeders.push(...seeders);\n return this;\n }\n\n /**\n * Initialize before running\n */\n protected async init() {\n if (!(await this.driver.blueprint.tableExists(seedsTableName))) {\n await migrationRunner.run(SeedsTableMigration);\n }\n\n if (!(await this.driver.blueprint.tableExists(seedRecordsTableName))) {\n await migrationRunner.run(SeedRecordsTableMigration);\n }\n }\n\n /**\n * Run seeders\n */\n public async run(withTransaction = true) {\n await this.init();\n\n this.prepareSeeders();\n\n console.log(`🌱 Running ${this.seeders.length} seeder(s)...\\n`);\n\n let successCount = 0;\n let skippedCount = 0;\n let failedCount = 0;\n\n for (const seeder of this.seeders) {\n // Check if already executed\n if (seeder.once && (await this.seederIsExecutedBefore(seeder))) {\n console.log(`⏭️ Skipping ${colors.yellow(seeder.name)} (already executed)`);\n skippedCount++;\n continue;\n }\n\n try {\n console.log(`🔄 Running ${colors.green(seeder.name)}...`);\n const startTime = Date.now();\n\n const refs: SeedRecordRef[] = [];\n const track = this.createTracker(seeder, refs);\n\n const runSeeder = async () => {\n // Keep LAST-RUN refs only: drop the prior refs for this seeder before\n // writing the new ones, so `--drop` undo stays bounded.\n await this.driver.deleteMany(seedRecordsTableName, { seeder: seeder.name });\n\n const result = await seeder.run({\n track,\n now: this.now,\n batchSize: seeder.batchSize ?? 0,\n });\n\n if (refs.length > 0) {\n await this.driver.insertMany(\n seedRecordsTableName,\n refs.map((ref) => ({ ...ref })),\n );\n }\n\n return result;\n };\n\n const result = withTransaction ? await transaction(runSeeder) : await runSeeder();\n\n const duration = Date.now() - startTime;\n\n // Auto-derive recordsCreated from the track count; a returned\n // SeedResult is an optional fallback when nothing was tracked.\n const recordsCreated = refs.length > 0 ? refs.length : (result?.recordsCreated ?? 0);\n\n await this.storeSeedsResults(seeder, { recordsCreated });\n\n console.log(\n `✅ ${colors.green(seeder.name)} completed (${duration}ms, ${recordsCreated} records)\\n`,\n );\n successCount++;\n } catch (error: any) {\n const err = error as Error;\n console.error(`❌ ${colors.red(seeder.name)} failed:`, err.message);\n console.log(err);\n failedCount++;\n\n if (error instanceof DatabaseWriterValidationError) {\n console.log(error.errors);\n }\n\n // Re-throw to stop execution (or continue to next seed based on your preference)\n throw error;\n }\n }\n\n // Summary\n console.log(\"\\n\" + \"=\".repeat(50));\n console.log(`✅ Success: ${successCount}`);\n\n if (skippedCount > 0) console.log(`⏭️ Skipped: ${skippedCount}`);\n\n if (failedCount > 0) console.log(`❌ Failed: ${failedCount}`);\n\n console.log(\"=\".repeat(50));\n }\n\n /**\n * Build a `track` helper bound to the given seeder. Tracked refs are pushed\n * into `refs` and persisted (within the run transaction) by the caller.\n *\n * Every overload returns its first argument so it can be chained inline.\n */\n protected createTracker(seeder: Seeder, refs: SeedRecordRef[]): Track {\n const push = (table: string, recordId: number | string) => {\n refs.push({ seeder: seeder.name, table, recordId, runAt: this.now() });\n };\n\n function track<T extends TrackableModel>(model: T): T;\n function track<T extends TrackableModel>(models: T[]): T[];\n function track(table: string, id: number | string): string;\n function track(\n arg: TrackableModel | TrackableModel[] | string,\n id?: number | string,\n ): TrackableModel | TrackableModel[] | string {\n if (typeof arg === \"string\") {\n push(arg, id as number | string);\n\n return arg;\n }\n\n if (Array.isArray(arg)) {\n for (const model of arg) {\n push(model.getTableName(), model.id);\n }\n\n return arg;\n }\n\n push(arg.getTableName(), arg.id);\n\n return arg;\n }\n\n return track;\n }\n\n /**\n * Sort seeds\n */\n public sort() {\n this.seeders = this.seeders.sort((a, b) => {\n const orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n const orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n return orderA - orderB;\n });\n\n return this;\n }\n\n /**\n * Prepare seeders to order by the seeder order, then resolve `dependsOn`.\n *\n * Dependencies are honored via a topological sort layered over the existing\n * numeric `order` tie-break: among seeders whose dependencies are already\n * satisfied, the lowest `order` (then registration order) runs first. Throws\n * {@link UnknownSeederDependencyError} for a missing dependency and\n * {@link SeederDependencyCycleError} for a cycle.\n */\n public prepareSeeders() {\n this.seeders = this.seeders.filter((seeder) => seeder.enabled !== false);\n\n this.sort();\n\n this.seeders = this.resolveDependencies(this.seeders);\n\n return this;\n }\n\n /**\n * Topologically sort `seeders` by their `dependsOn` names. The input is\n * assumed pre-sorted by `order` (see {@link sort}); that ordering is used as\n * the tie-break so dependency-free siblings keep their `order` sequence.\n */\n protected resolveDependencies(seeders: Seeder[]): Seeder[] {\n const byName = new Map<string, Seeder>();\n\n for (const seeder of seeders) {\n byName.set(seeder.name, seeder);\n }\n\n // Validate dependencies up-front so an unknown name is a clear error rather\n // than a silently dropped edge.\n for (const seeder of seeders) {\n for (const dependency of seeder.dependsOn ?? []) {\n if (!byName.has(dependency)) {\n throw new UnknownSeederDependencyError(seeder.name, dependency);\n }\n }\n }\n\n const sorted: Seeder[] = [];\n const state = new Map<string, \"visiting\" | \"visited\">();\n\n const visit = (seeder: Seeder, path: string[]) => {\n const status = state.get(seeder.name);\n\n if (status === \"visited\") {\n return;\n }\n\n if (status === \"visiting\") {\n const cycleStart = path.indexOf(seeder.name);\n const cycle = path.slice(cycleStart >= 0 ? cycleStart : 0).concat(seeder.name);\n\n throw new SeederDependencyCycleError(cycle);\n }\n\n state.set(seeder.name, \"visiting\");\n\n for (const dependency of seeder.dependsOn ?? []) {\n // Presence already validated above; the `!` is safe.\n visit(byName.get(dependency)!, [...path, seeder.name]);\n }\n\n state.set(seeder.name, \"visited\");\n sorted.push(seeder);\n };\n\n // Iterate in the incoming (order-sorted, registration-stable) sequence so\n // dependency-free seeders preserve their `order` tie-break.\n for (const seeder of seeders) {\n visit(seeder, []);\n }\n\n return sorted;\n }\n\n /**\n * Store seed results in db\n */\n public async storeSeedsResults(seeder: Seeder, result: SeedResult) {\n const oldResult = await this.getMetadata(seeder);\n\n // log with an emoji icon at beginning of text no of total craeted records\n console.log(`📊 Total records created: ${result.recordsCreated}`);\n\n // Read the single overridable clock once so every timestamp in this write\n // is identical (and pinned in a deterministic / historical run).\n const now = this.now();\n\n if (oldResult) {\n // Update existing record - use query builder with WHERE clause\n await this.driver\n .queryBuilder(seedsTableName)\n .where(\"name\", seeder.name)\n .update({\n runCount: oldResult.runCount + 1,\n lastRunAt: now,\n totalRecordsCreated: oldResult.totalRecordsCreated + result.recordsCreated,\n lastRunRecordsCreated: result.recordsCreated,\n });\n\n // now display total created records so far\n console.log(\n `📊 Total records created so far: ${oldResult.totalRecordsCreated + result.recordsCreated}`,\n );\n } else {\n // Insert new record\n await this.driver.insert(seedsTableName, {\n name: seeder.name,\n createdAt: now,\n firstRunAt: now,\n lastRunAt: now,\n runCount: 1,\n totalRecordsCreated: result.recordsCreated,\n lastRunRecordsCreated: result.recordsCreated,\n });\n }\n }\n\n /**\n * Get seed info from database\n */\n // Returns null when the seeder has never run. first() has always resolved to\n // SeederMetadata | null; annotating it as non-null pushed that lie to every\n // caller, each of which already branches on the absent case.\n protected getMetadata(seeder: Seeder): Promise<SeederMetadata | null> {\n const driver = this.datasource.driver;\n\n return driver.queryBuilder(seedsTableName).where(\"name\", seeder.name).first();\n }\n\n /**\n * Get driver instance\n */\n protected get driver(): DriverContract {\n return this.datasource.driver;\n }\n\n /**\n * Check if seeder has been executed before\n */\n protected async seederIsExecutedBefore(seeder: Seeder) {\n return !!(await this.getMetadata(seeder));\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"seeds-table-migration.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seeds-table-migration.ts"],"sourcesContent":["import { Migration } from \"@warlock.js/cascade\";\nimport { seedsTableName } from \"./utils\";\n\nexport class SeedsTableMigration extends Migration {\n public static migrationName = \"seeds-table-migration\";\n public table = seedsTableName;\n\n public up() {\n this.createTableIfNotExists();\n\n this.id();\n this.text(\"name\").unique();\n this.int(\"runCount\").default(0);\n this.dateTime(\"createdAt\").useCurrent();\n this.dateTime(\"firstRunAt\").useCurrent();\n this.dateTime(\"lastRunAt\").useCurrent();\n this.int(\"totalRecordsCreated\").default(0);\n this.int(\"lastRunRecordsCreated\").default(0);\n }\n\n public down() {\n // No argument: dropTableIfExists() operates on `this.table`, set as a class\n // field above. The name passed here was silently ignored at runtime.\n this.dropTableIfExists();\n }\n}\n"],"mappings":";;;;AAGA,IAAa,sBAAb,cAAyC,UAAU;;;eAElC;;;uBADe;;CAG9B,AAAO,KAAK;EACV,KAAK,uBAAuB;EAE5B,KAAK,GAAG;EACR,KAAK,KAAK,MAAM,EAAE,OAAO;EACzB,KAAK,IAAI,UAAU,EAAE,QAAQ,CAAC;EAC9B,KAAK,SAAS,WAAW,EAAE,WAAW;EACtC,KAAK,SAAS,YAAY,EAAE,WAAW;EACvC,KAAK,SAAS,WAAW,EAAE,WAAW;EACtC,KAAK,IAAI,qBAAqB,EAAE,QAAQ,CAAC;EACzC,KAAK,IAAI,uBAAuB,EAAE,QAAQ,CAAC;CAC7C;CAEA,AAAO,OAAO;EAGZ,KAAK,kBAAkB;CACzB;AACF"}
1
+ {"version":3,"file":"seeds-table-migration.mjs","names":[],"sources":["../../../../../../../../core/src/database/seeds/seeds-table-migration.ts"],"sourcesContent":["import { Migration } from \"@warlock.js/cascade\";\nimport { seedsTableName } from \"./utils\";\n\nexport class SeedsTableMigration extends Migration {\n public static migrationName = \"seeds-table-migration\";\n public table = seedsTableName;\n\n public up() {\n this.createTableIfNotExists();\n\n this.id();\n this.text(\"name\").unique();\n this.int(\"runCount\").default(0);\n this.dateTime(\"createdAt\").useCurrent();\n this.dateTime(\"firstRunAt\").useCurrent();\n this.dateTime(\"lastRunAt\").useCurrent();\n this.int(\"totalRecordsCreated\").default(0);\n this.int(\"lastRunRecordsCreated\").default(0);\n }\n\n public down() {\n // No argument: dropTableIfExists() operates on `this.table`, set as a class\n // field above. The name passed here was silently ignored at runtime.\n this.dropTableIfExists();\n }\n}\n"],"mappings":";;;;AAGA,IAAa,sBAAb,cAAyC,UAAU;;;eAElC;;;uBADe;;CAG9B,AAAO,KAAK;EACV,KAAK,uBAAuB;EAE5B,KAAK,GAAG;EACR,KAAK,KAAK,MAAM,CAAC,CAAC,OAAO;EACzB,KAAK,IAAI,UAAU,CAAC,CAAC,QAAQ,CAAC;EAC9B,KAAK,SAAS,WAAW,CAAC,CAAC,WAAW;EACtC,KAAK,SAAS,YAAY,CAAC,CAAC,WAAW;EACvC,KAAK,SAAS,WAAW,CAAC,CAAC,WAAW;EACtC,KAAK,IAAI,qBAAqB,CAAC,CAAC,QAAQ,CAAC;EACzC,KAAK,IAAI,uBAAuB,CAAC,CAAC,QAAQ,CAAC;CAC7C;CAEA,AAAO,OAAO;EAGZ,KAAK,kBAAkB;CACzB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"check-for-updates.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/check-for-updates.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { updateWarlockPackages } from \"../updater/update-warlock-packages\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\nimport { fetchLatestVersion } from \"../utils/npm-registry\";\nimport { isNewerVersion } from \"../utils/version-compare\";\nimport { warlockConfigManager } from \"../warlock-config\";\nimport type { DevelopmentServer } from \"./development-server\";\nimport { restartDevServer } from \"./restart-dev-server\";\nimport { devServerShortcuts } from \"./shortcuts\";\nimport {\n clearCachedLatestVersion,\n readCachedLatestVersion,\n writeCachedLatestVersion,\n} from \"./update-check-cache\";\n\n/** The package whose version represents the whole (lockstep) family. */\nconst CORE_PACKAGE = \"@warlock.js/core\";\n\n/** Where developers can read what changed between releases. */\nconst CHANGELOG_URL = \"https://warlock.js.org/changelog/\";\n\n/** The key that applies the update without leaving the dev server. */\nconst UPDATE_SHORTCUT_KEY = \"u\";\n\n/**\n * Abort budget for the notice's registry lookup. Deliberately much shorter\n * than the `warlock update` command's: nobody is waiting on this answer, and\n * a network that hangs (captive portal, dead VPN, flaky DNS) must not leave a\n * pending request and a live timer sitting around behind a running dev server.\n */\nconst UPDATE_CHECK_TIMEOUT_MS = 5_000;\n\n/**\n * Check npm for a newer `@warlock.js/core` release and, if one exists, print\n * a short non-blocking notice to the terminal. Because the whole family is\n * released in lockstep, core's version stands in for every `@warlock.js/*`\n * package, so a single lookup is enough.\n *\n * When a `devServer` is passed and the terminal is interactive, the notice\n * also arms the `u` shortcut: one keypress updates every `@warlock.js/*`\n * dependency, installs, and restarts the server.\n *\n * Designed to be called fire-and-forget once the dev server is ready: it\n * never throws, never blocks startup, and stays silent unless there is a\n * genuinely newer version to report. Automatically skipped in CI, in\n * non-interactive (non-TTY) shells, and when `devServer.checkForUpdates` is\n * set to `false`.\n */\nexport async function checkForFrameworkUpdate(devServer?: DevelopmentServer): Promise<void> {\n try {\n if (!isUpdateCheckEnabled()) {\n return;\n }\n\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\n\n if (devServerConfig?.checkForUpdates === false) {\n return;\n }\n\n const currentVersion = await getWarlockVersion();\n const latestVersion = await resolveLatestVersion();\n\n if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {\n return;\n }\n\n printUpdateNotice(currentVersion, latestVersion, offerUpdateShortcut(devServer));\n } catch {\n // An update check is a convenience — never let it disrupt the dev server.\n }\n}\n\n/**\n * The latest published core version, preferring a recent cached answer over a\n * network round-trip. Every `warlock dev` boot would otherwise hit npm, which\n * is a lot of traffic for a question whose answer changes a few times a month.\n */\nasync function resolveLatestVersion(): Promise<string | undefined> {\n const cached = await readCachedLatestVersion();\n\n if (cached) {\n return cached;\n }\n\n const latestVersion = await fetchLatestVersion(CORE_PACKAGE, UPDATE_CHECK_TIMEOUT_MS);\n\n // A failed lookup is deliberately NOT cached as \"no version\" — that would\n // silence the notice for a day over one flaky moment. Only real answers are\n // remembered; offline simply re-asks next boot.\n if (latestVersion) {\n await writeCachedLatestVersion(latestVersion);\n }\n\n return latestVersion;\n}\n\n/**\n * Whether an update check should run at all in the current environment.\n * Mirrors npm's own update-notifier conventions: stay quiet in CI, in\n * non-interactive shells, and when explicitly opted out via env.\n */\nfunction isUpdateCheckEnabled(): boolean {\n if (process.env.CI) {\n return false;\n }\n\n if (process.env.NO_UPDATE_NOTIFIER) {\n return false;\n }\n\n if (!process.stdout.isTTY) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Arm the `u` shortcut, if there is a server to restart and a terminal that\n * can deliver keypresses. Returns whether the notice may advertise it.\n */\nfunction offerUpdateShortcut(devServer?: DevelopmentServer): boolean {\n if (!devServer) {\n return false;\n }\n\n return devServerShortcuts.register({\n key: UPDATE_SHORTCUT_KEY,\n description: \"update @warlock.js packages and restart\",\n handler: () => updateAndRestart(devServer),\n });\n}\n\n/**\n * The `u` handler: rewrite the `@warlock.js/*` versions, install them, and\n * come back up on the new ones.\n *\n * The shortcut is dropped and the terminal released first, so the package\n * manager child owns stdin (and a second `u` can't stack a parallel install).\n * Releasing has to be explicit: the standing shortcut bar keeps other keys\n * registered, so dropping `u` alone would leave us in raw mode.\n *\n * The terminal is taken back — and `u` re-armed — only for the cases worth\n * retrying: an unreachable registry, or a restart that could not be started.\n */\nasync function updateAndRestart(devServer: DevelopmentServer): Promise<void> {\n devServerShortcuts.unregister(UPDATE_SHORTCUT_KEY);\n devServerShortcuts.release();\n\n console.log();\n\n const result = await updateWarlockPackages();\n\n if (result.outcome === \"updated\") {\n // The remembered answer described the version we just replaced.\n await clearCachedLatestVersion();\n\n const restarted = await restartDevServer(devServer);\n\n if (!restarted) {\n devServerShortcuts.resume();\n console.log(` ${colors.dim(\"Restart the dev server manually to load the new version.\")}`);\n }\n\n return;\n }\n\n devServerShortcuts.resume();\n\n if (result.outcome === \"registry-unreachable\") {\n printShortcutRetryHint();\n offerUpdateShortcut(devServer);\n return;\n }\n\n // Anything else — a failed install, or a notice that turned out to be stale —\n // leaves the shortcut off on purpose: package.json is already rewritten, so a\n // second `u` would just report \"up to date\" and hide the real problem.\n}\n\n/**\n * Print the \"update available\" notice in the dev-logger's visual style.\n *\n * @param canPressKey Whether the `u` shortcut is armed. When it is not (piped\n * stdin, CI, a supervisor), fall back to the command the\n * developer can run by hand.\n */\nfunction printUpdateNotice(\n currentVersion: string,\n latestVersion: string,\n canPressKey: boolean,\n): void {\n console.log();\n console.log(\n ` ${colors.yellow(\"⚡\")} ${colors.bold(\"A new version of Warlock.js is available\")} ` +\n `${colors.dim(currentVersion)} ${colors.dim(\"→\")} ${colors.greenBright(latestVersion)}`,\n );\n\n if (canPressKey) {\n console.log(\n ` ${colors.dim(\"Press\")} ${colors.bold(colors.cyan(UPDATE_SHORTCUT_KEY))} ` +\n `${colors.dim(\"to update all @warlock.js packages and restart\")}`,\n );\n } else {\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"npx warlock update\")} ` +\n `${colors.dim(\"to update all @warlock.js packages\")}`,\n );\n }\n\n console.log(` ${colors.dim(\"Changelog\")} ${colors.cyan(CHANGELOG_URL)}`);\n console.log();\n}\n\n/** Tell the developer the shortcut is still available after a failed try. */\nfunction printShortcutRetryHint(): void {\n console.log(\n ` ${colors.dim(\"Press\")} ${colors.bold(colors.cyan(UPDATE_SHORTCUT_KEY))} ` +\n `${colors.dim(\"to try again once you are back online\")}`,\n );\n console.log();\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,eAAe;;AAGrB,MAAM,gBAAgB;;AAGtB,MAAM,sBAAsB;;;;;;;AAQ5B,MAAM,0BAA0B;;;;;;;;;;;;;;;;;AAkBhC,eAAsB,wBAAwB,WAA8C;CAC1F,IAAI;EACF,IAAI,CAAC,qBAAqB,GACxB;EAKF,KAAI,MAF0B,qBAAqB,IAAI,WAAW,IAE7C,oBAAoB,OACvC;EAGF,MAAM,iBAAiB,MAAM,kBAAkB;EAC/C,MAAM,gBAAgB,MAAM,qBAAqB;EAEjD,IAAI,CAAC,iBAAiB,CAAC,eAAe,eAAe,cAAc,GACjE;EAGF,kBAAkB,gBAAgB,eAAe,oBAAoB,SAAS,CAAC;CACjF,QAAQ,CAER;AACF;;;;;;AAOA,eAAe,uBAAoD;CACjE,MAAM,SAAS,MAAM,wBAAwB;CAE7C,IAAI,QACF,OAAO;CAGT,MAAM,gBAAgB,MAAM,mBAAmB,cAAc,uBAAuB;CAKpF,IAAI,eACF,MAAM,yBAAyB,aAAa;CAG9C,OAAO;AACT;;;;;;AAOA,SAAS,uBAAgC;CACvC,IAAI,QAAQ,IAAI,IACd,OAAO;CAGT,IAAI,QAAQ,IAAI,oBACd,OAAO;CAGT,IAAI,CAAC,QAAQ,OAAO,OAClB,OAAO;CAGT,OAAO;AACT;;;;;AAMA,SAAS,oBAAoB,WAAwC;CACnE,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,mBAAmB,SAAS;EACjC,KAAK;EACL,aAAa;EACb,eAAe,iBAAiB,SAAS;CAC3C,CAAC;AACH;;;;;;;;;;;;;AAcA,eAAe,iBAAiB,WAA6C;CAC3E,mBAAmB,WAAW,mBAAmB;CACjD,mBAAmB,QAAQ;CAE3B,QAAQ,IAAI;CAEZ,MAAM,SAAS,MAAM,sBAAsB;CAE3C,IAAI,OAAO,YAAY,WAAW;EAEhC,MAAM,yBAAyB;EAI/B,IAAI,CAAC,MAFmB,iBAAiB,SAAS,GAElC;GACd,mBAAmB,OAAO;GAC1B,QAAQ,IAAI,QAAQ,OAAO,IAAI,0DAA0D,GAAG;EAC9F;EAEA;CACF;CAEA,mBAAmB,OAAO;CAE1B,IAAI,OAAO,YAAY,wBAAwB;EAC7C,uBAAuB;EACvB,oBAAoB,SAAS;EAC7B;CACF;AAKF;;;;;;;;AASA,SAAS,kBACP,gBACA,eACA,aACM;CACN,QAAQ,IAAI;CACZ,QAAQ,IACN,KAAK,OAAO,OAAO,GAAG,EAAE,GAAG,OAAO,KAAK,0CAA0C,EAAE,IAC9E,OAAO,IAAI,cAAc,EAAE,GAAG,OAAO,IAAI,GAAG,EAAE,GAAG,OAAO,YAAY,aAAa,GACxF;CAEA,IAAI,aACF,QAAQ,IACN,QAAQ,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK,mBAAmB,CAAC,EAAE,GACxE,OAAO,IAAI,gDAAgD,GAClE;MAEA,QAAQ,IACN,QAAQ,OAAO,IAAI,KAAK,EAAE,GAAG,OAAO,KAAK,oBAAoB,EAAE,GAC1D,OAAO,IAAI,oCAAoC,GACtD;CAGF,QAAQ,IAAI,QAAQ,OAAO,IAAI,WAAW,EAAE,GAAG,OAAO,KAAK,aAAa,GAAG;CAC3E,QAAQ,IAAI;AACd;;AAGA,SAAS,yBAA+B;CACtC,QAAQ,IACN,QAAQ,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK,mBAAmB,CAAC,EAAE,GACxE,OAAO,IAAI,uCAAuC,GACzD;CACA,QAAQ,IAAI;AACd"}
1
+ {"version":3,"file":"check-for-updates.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/check-for-updates.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { updateWarlockPackages } from \"../updater/update-warlock-packages\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\nimport { fetchLatestVersion } from \"../utils/npm-registry\";\nimport { isNewerVersion } from \"../utils/version-compare\";\nimport { warlockConfigManager } from \"../warlock-config\";\nimport type { DevelopmentServer } from \"./development-server\";\nimport { restartDevServer } from \"./restart-dev-server\";\nimport { devServerShortcuts } from \"./shortcuts\";\nimport {\n clearCachedLatestVersion,\n readCachedLatestVersion,\n writeCachedLatestVersion,\n} from \"./update-check-cache\";\n\n/** The package whose version represents the whole (lockstep) family. */\nconst CORE_PACKAGE = \"@warlock.js/core\";\n\n/** Where developers can read what changed between releases. */\nconst CHANGELOG_URL = \"https://warlock.js.org/changelog/\";\n\n/** The key that applies the update without leaving the dev server. */\nconst UPDATE_SHORTCUT_KEY = \"u\";\n\n/**\n * Abort budget for the notice's registry lookup. Deliberately much shorter\n * than the `warlock update` command's: nobody is waiting on this answer, and\n * a network that hangs (captive portal, dead VPN, flaky DNS) must not leave a\n * pending request and a live timer sitting around behind a running dev server.\n */\nconst UPDATE_CHECK_TIMEOUT_MS = 5_000;\n\n/**\n * Check npm for a newer `@warlock.js/core` release and, if one exists, print\n * a short non-blocking notice to the terminal. Because the whole family is\n * released in lockstep, core's version stands in for every `@warlock.js/*`\n * package, so a single lookup is enough.\n *\n * When a `devServer` is passed and the terminal is interactive, the notice\n * also arms the `u` shortcut: one keypress updates every `@warlock.js/*`\n * dependency, installs, and restarts the server.\n *\n * Designed to be called fire-and-forget once the dev server is ready: it\n * never throws, never blocks startup, and stays silent unless there is a\n * genuinely newer version to report. Automatically skipped in CI, in\n * non-interactive (non-TTY) shells, and when `devServer.checkForUpdates` is\n * set to `false`.\n */\nexport async function checkForFrameworkUpdate(devServer?: DevelopmentServer): Promise<void> {\n try {\n if (!isUpdateCheckEnabled()) {\n return;\n }\n\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\n\n if (devServerConfig?.checkForUpdates === false) {\n return;\n }\n\n const currentVersion = await getWarlockVersion();\n const latestVersion = await resolveLatestVersion();\n\n if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {\n return;\n }\n\n printUpdateNotice(currentVersion, latestVersion, offerUpdateShortcut(devServer));\n } catch {\n // An update check is a convenience — never let it disrupt the dev server.\n }\n}\n\n/**\n * The latest published core version, preferring a recent cached answer over a\n * network round-trip. Every `warlock dev` boot would otherwise hit npm, which\n * is a lot of traffic for a question whose answer changes a few times a month.\n */\nasync function resolveLatestVersion(): Promise<string | undefined> {\n const cached = await readCachedLatestVersion();\n\n if (cached) {\n return cached;\n }\n\n const latestVersion = await fetchLatestVersion(CORE_PACKAGE, UPDATE_CHECK_TIMEOUT_MS);\n\n // A failed lookup is deliberately NOT cached as \"no version\" — that would\n // silence the notice for a day over one flaky moment. Only real answers are\n // remembered; offline simply re-asks next boot.\n if (latestVersion) {\n await writeCachedLatestVersion(latestVersion);\n }\n\n return latestVersion;\n}\n\n/**\n * Whether an update check should run at all in the current environment.\n * Mirrors npm's own update-notifier conventions: stay quiet in CI, in\n * non-interactive shells, and when explicitly opted out via env.\n */\nfunction isUpdateCheckEnabled(): boolean {\n if (process.env.CI) {\n return false;\n }\n\n if (process.env.NO_UPDATE_NOTIFIER) {\n return false;\n }\n\n if (!process.stdout.isTTY) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Arm the `u` shortcut, if there is a server to restart and a terminal that\n * can deliver keypresses. Returns whether the notice may advertise it.\n */\nfunction offerUpdateShortcut(devServer?: DevelopmentServer): boolean {\n if (!devServer) {\n return false;\n }\n\n return devServerShortcuts.register({\n key: UPDATE_SHORTCUT_KEY,\n description: \"update @warlock.js packages and restart\",\n handler: () => updateAndRestart(devServer),\n });\n}\n\n/**\n * The `u` handler: rewrite the `@warlock.js/*` versions, install them, and\n * come back up on the new ones.\n *\n * The shortcut is dropped and the terminal released first, so the package\n * manager child owns stdin (and a second `u` can't stack a parallel install).\n * Releasing has to be explicit: the standing shortcut bar keeps other keys\n * registered, so dropping `u` alone would leave us in raw mode.\n *\n * The terminal is taken back — and `u` re-armed — only for the cases worth\n * retrying: an unreachable registry, or a restart that could not be started.\n */\nasync function updateAndRestart(devServer: DevelopmentServer): Promise<void> {\n devServerShortcuts.unregister(UPDATE_SHORTCUT_KEY);\n devServerShortcuts.release();\n\n console.log();\n\n const result = await updateWarlockPackages();\n\n if (result.outcome === \"updated\") {\n // The remembered answer described the version we just replaced.\n await clearCachedLatestVersion();\n\n const restarted = await restartDevServer(devServer);\n\n if (!restarted) {\n devServerShortcuts.resume();\n console.log(` ${colors.dim(\"Restart the dev server manually to load the new version.\")}`);\n }\n\n return;\n }\n\n devServerShortcuts.resume();\n\n if (result.outcome === \"registry-unreachable\") {\n printShortcutRetryHint();\n offerUpdateShortcut(devServer);\n return;\n }\n\n // Anything else — a failed install, or a notice that turned out to be stale —\n // leaves the shortcut off on purpose: package.json is already rewritten, so a\n // second `u` would just report \"up to date\" and hide the real problem.\n}\n\n/**\n * Print the \"update available\" notice in the dev-logger's visual style.\n *\n * @param canPressKey Whether the `u` shortcut is armed. When it is not (piped\n * stdin, CI, a supervisor), fall back to the command the\n * developer can run by hand.\n */\nfunction printUpdateNotice(\n currentVersion: string,\n latestVersion: string,\n canPressKey: boolean,\n): void {\n console.log();\n console.log(\n ` ${colors.yellow(\"⚡\")} ${colors.bold(\"A new version of Warlock.js is available\")} ` +\n `${colors.dim(currentVersion)} ${colors.dim(\"→\")} ${colors.greenBright(latestVersion)}`,\n );\n\n if (canPressKey) {\n console.log(\n ` ${colors.dim(\"Press\")} ${colors.bold(colors.cyan(UPDATE_SHORTCUT_KEY))} ` +\n `${colors.dim(\"to update all @warlock.js packages and restart\")}`,\n );\n } else {\n console.log(\n ` ${colors.dim(\"Run\")} ${colors.cyan(\"npx warlock update\")} ` +\n `${colors.dim(\"to update all @warlock.js packages\")}`,\n );\n }\n\n console.log(` ${colors.dim(\"Changelog\")} ${colors.cyan(CHANGELOG_URL)}`);\n console.log();\n}\n\n/** Tell the developer the shortcut is still available after a failed try. */\nfunction printShortcutRetryHint(): void {\n console.log(\n ` ${colors.dim(\"Press\")} ${colors.bold(colors.cyan(UPDATE_SHORTCUT_KEY))} ` +\n `${colors.dim(\"to try again once you are back online\")}`,\n );\n console.log();\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,eAAe;;AAGrB,MAAM,gBAAgB;;AAGtB,MAAM,sBAAsB;;;;;;;AAQ5B,MAAM,0BAA0B;;;;;;;;;;;;;;;;;AAkBhC,eAAsB,wBAAwB,WAA8C;CAC1F,IAAI;EACF,IAAI,CAAC,qBAAqB,GACxB;EAKF,KAAI,MAF0B,qBAAqB,IAAI,WAAW,EAE/C,EAAE,oBAAoB,OACvC;EAGF,MAAM,iBAAiB,MAAM,kBAAkB;EAC/C,MAAM,gBAAgB,MAAM,qBAAqB;EAEjD,IAAI,CAAC,iBAAiB,CAAC,eAAe,eAAe,cAAc,GACjE;EAGF,kBAAkB,gBAAgB,eAAe,oBAAoB,SAAS,CAAC;CACjF,QAAQ,CAER;AACF;;;;;;AAOA,eAAe,uBAAoD;CACjE,MAAM,SAAS,MAAM,wBAAwB;CAE7C,IAAI,QACF,OAAO;CAGT,MAAM,gBAAgB,MAAM,mBAAmB,cAAc,uBAAuB;CAKpF,IAAI,eACF,MAAM,yBAAyB,aAAa;CAG9C,OAAO;AACT;;;;;;AAOA,SAAS,uBAAgC;CACvC,IAAI,QAAQ,IAAI,IACd,OAAO;CAGT,IAAI,QAAQ,IAAI,oBACd,OAAO;CAGT,IAAI,CAAC,QAAQ,OAAO,OAClB,OAAO;CAGT,OAAO;AACT;;;;;AAMA,SAAS,oBAAoB,WAAwC;CACnE,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,mBAAmB,SAAS;EACjC,KAAK;EACL,aAAa;EACb,eAAe,iBAAiB,SAAS;CAC3C,CAAC;AACH;;;;;;;;;;;;;AAcA,eAAe,iBAAiB,WAA6C;CAC3E,mBAAmB,WAAW,mBAAmB;CACjD,mBAAmB,QAAQ;CAE3B,QAAQ,IAAI;CAEZ,MAAM,SAAS,MAAM,sBAAsB;CAE3C,IAAI,OAAO,YAAY,WAAW;EAEhC,MAAM,yBAAyB;EAI/B,IAAI,CAAC,MAFmB,iBAAiB,SAAS,GAElC;GACd,mBAAmB,OAAO;GAC1B,QAAQ,IAAI,QAAQ,OAAO,IAAI,0DAA0D,GAAG;EAC9F;EAEA;CACF;CAEA,mBAAmB,OAAO;CAE1B,IAAI,OAAO,YAAY,wBAAwB;EAC7C,uBAAuB;EACvB,oBAAoB,SAAS;EAC7B;CACF;AAKF;;;;;;;;AASA,SAAS,kBACP,gBACA,eACA,aACM;CACN,QAAQ,IAAI;CACZ,QAAQ,IACN,KAAK,OAAO,OAAO,GAAG,EAAE,GAAG,OAAO,KAAK,0CAA0C,EAAE,IAC9E,OAAO,IAAI,cAAc,EAAE,GAAG,OAAO,IAAI,GAAG,EAAE,GAAG,OAAO,YAAY,aAAa,GACxF;CAEA,IAAI,aACF,QAAQ,IACN,QAAQ,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK,mBAAmB,CAAC,EAAE,GACxE,OAAO,IAAI,gDAAgD,GAClE;MAEA,QAAQ,IACN,QAAQ,OAAO,IAAI,KAAK,EAAE,GAAG,OAAO,KAAK,oBAAoB,EAAE,GAC1D,OAAO,IAAI,oCAAoC,GACtD;CAGF,QAAQ,IAAI,QAAQ,OAAO,IAAI,WAAW,EAAE,GAAG,OAAO,KAAK,aAAa,GAAG;CAC3E,QAAQ,IAAI;AACd;;AAGA,SAAS,yBAA+B;CACtC,QAAQ,IACN,QAAQ,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK,mBAAmB,CAAC,EAAE,GACxE,OAAO,IAAI,uCAAuC,GACzD;CACA,QAAQ,IAAI;AACd"}
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-graph.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/dependency-graph.ts"],"mappings":";;;;;AAYA;;;;;;;;cAAa,eAAA;EA0L4B;;;;;EAAA,QApL/B,YAAA;EAkBY;;;;;EAAA,QAXZ,UAAA;EAiD+C;;;;EAAA,QA3C/C,KAAA;EA8EU;;;EAzEX,KAAA,CAAM,KAAA,EAAO,GAAA,SAAY,WAAA;EA2G9B;;;;;;;;;EArEK,aAAA,CAAc,IAAA,UAAc,UAAA,UAAoB,UAAA;EA4HlC;;;;;EAlGd,gBAAA,CAAiB,IAAA,UAAc,UAAA;EAwM9B;;;;EA/LD,UAAA,CAAW,IAAA;;;;;;;;;;;;EAgCX,UAAA,CACL,IAAA,UACA,eAAA,EAAiB,GAAA,UACjB,oBAAA,GAAsB,GAAA;;;;;;EA+BjB,eAAA,CAAgB,IAAA,WAAe,GAAA;;;;;EAc/B,cAAA,CAAe,IAAA,UAAc,UAAA;;;;;;EAS7B,aAAA,CAAc,IAAA,WAAe,GAAA;;;;;;;;EAW7B,oBAAA,CAAqB,IAAA;;;;;;;;;;;;;EA+BrB,0BAAA,CAAA;;;;;;;UA2CC,cAAA;;;;;UAiBA,iCAAA;;;;EAgGD,QAAA,CAAA"}
1
+ {"version":3,"file":"dependency-graph.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/dependency-graph.ts"],"mappings":";;;;;AAYA;;;;;;;;cAAa,eAAA;EA0L4B;;;;;EAAA,QApL/B,YAAA;EAkBY;;;;;EAAA,QAXZ,UAAA;EAiD+C;;;;EAAA,QA3C/C,KAAA;EA8EU;;;EAzEX,KAAA,CAAM,KAAA,EAAO,GAAA,SAAY,WAAA;EA2G9B;;;;;;;;;EArEK,aAAA,CAAc,IAAA,UAAc,UAAA,UAAoB,UAAA;EA4HlC;;;;;EAlGd,gBAAA,CAAiB,IAAA,UAAc,UAAA;EAwM9B;;;;EA/LD,UAAA,CAAW,IAAA;;;;;;;;;;;;EAgCX,UAAA,CACL,IAAA,UACA,eAAA,EAAiB,GAAA,UACjB,oBAAA,GAAsB,GAAA;;;;;;EA+BjB,eAAA,CAAgB,IAAA,WAAe,GAAA;;;;;EAc/B,cAAA,CAAe,IAAA,UAAc,UAAA;;;;;;EAS7B,aAAA,CAAc,IAAA,WAAe,GAAA;;;;;;;;EAW7B,oBAAA,CAAqB,IAAA;;;;;;;;;;;;;EA+BrB,0BAAA;;;;;;;UA2CC,cAAA;;;;;UAiBA,iCAAA;;;;EAgGD,QAAA"}