@warlock.js/core 5.1.0 → 5.2.1

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 (356) hide show
  1. package/CHANGELOG.md +152 -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 +16 -10
  17. package/esm/cli/cli-commands.utils.mjs.map +1 -1
  18. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
  19. package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
  20. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
  21. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
  22. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  23. package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
  24. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  25. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  27. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  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 +84 -0
  32. package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -0
  33. package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
  34. package/esm/cli/commands/routes/route-row.mjs.map +1 -1
  35. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  36. package/esm/cli/commands/routes/routes-diff.action.mjs +84 -0
  37. package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -0
  38. package/esm/cli/commands/routes.command.mjs +15 -1
  39. package/esm/cli/commands/routes.command.mjs.map +1 -1
  40. package/esm/cli/commands/start-production.command.mjs +8 -1
  41. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  42. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  43. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  44. package/esm/cli/commands-loader.mjs.map +1 -1
  45. package/esm/cli/framework-cli-commands.mjs +2 -1
  46. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  47. package/esm/cli/start.mjs.map +1 -1
  48. package/esm/cli/string-similarity.mjs.map +1 -1
  49. package/esm/commands/cli-command.mjs.map +1 -1
  50. package/esm/config/config-loader.d.mts +1 -1
  51. package/esm/config/config-loader.d.mts.map +1 -1
  52. package/esm/config/config-loader.mjs.map +1 -1
  53. package/esm/config/config-setter.d.mts.map +1 -1
  54. package/esm/config/config-special-handlers.d.mts +1 -1
  55. package/esm/config/config-special-handlers.mjs.map +1 -1
  56. package/esm/config/load-config-files.mjs.map +1 -1
  57. package/esm/connectors/access-connector.d.mts.map +1 -1
  58. package/esm/connectors/ai-connector.d.mts.map +1 -1
  59. package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
  60. package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
  61. package/esm/connectors/base-connector.d.mts.map +1 -1
  62. package/esm/connectors/base-connector.mjs.map +1 -1
  63. package/esm/connectors/cache-connector.d.mts.map +1 -1
  64. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  65. package/esm/connectors/connectors-manager.mjs.map +1 -1
  66. package/esm/connectors/database-connector.d.mts.map +1 -1
  67. package/esm/connectors/describe-server-address.mjs +20 -3
  68. package/esm/connectors/describe-server-address.mjs.map +1 -1
  69. package/esm/connectors/herald-connector.d.mts.map +1 -1
  70. package/esm/connectors/http-connector.d.mts.map +1 -1
  71. package/esm/connectors/http-connector.mjs +5 -1
  72. package/esm/connectors/http-connector.mjs.map +1 -1
  73. package/esm/connectors/logger-connector.d.mts.map +1 -1
  74. package/esm/connectors/mail-connector.d.mts.map +1 -1
  75. package/esm/connectors/notifications-connector.d.mts.map +1 -1
  76. package/esm/connectors/register-configured-connectors.mjs.map +1 -1
  77. package/esm/connectors/socket-connector.d.mts.map +1 -1
  78. package/esm/connectors/socket-connector.mjs.map +1 -1
  79. package/esm/connectors/storage.connector.d.mts.map +1 -1
  80. package/esm/container/index.d.mts.map +1 -1
  81. package/esm/database/create-database-action.mjs.map +1 -1
  82. package/esm/database/drop-tables-action.mjs.map +1 -1
  83. package/esm/database/migrate-action.mjs.map +1 -1
  84. package/esm/database/models/database-log/database-log.mjs.map +1 -1
  85. package/esm/database/seed-command-action.mjs.map +1 -1
  86. package/esm/database/seeds/seed-records-table-migration.mjs +1 -1
  87. package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
  88. package/esm/database/seeds/seeder.mjs.map +1 -1
  89. package/esm/database/seeds/seeders.manager.mjs.map +1 -1
  90. package/esm/database/seeds/seeds-table-migration.mjs +1 -1
  91. package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
  92. package/esm/database/utils.mjs.map +1 -1
  93. package/esm/dev-server/check-for-updates.mjs.map +1 -1
  94. package/esm/dev-server/dependency-graph.d.mts.map +1 -1
  95. package/esm/dev-server/dependency-graph.mjs.map +1 -1
  96. package/esm/dev-server/dev-logger.mjs.map +1 -1
  97. package/esm/dev-server/development-server.mjs.map +1 -1
  98. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  99. package/esm/dev-server/file-manager.d.mts.map +1 -1
  100. package/esm/dev-server/file-manager.mjs.map +1 -1
  101. package/esm/dev-server/file-operations.d.mts.map +1 -1
  102. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  103. package/esm/dev-server/files-watcher.mjs.map +1 -1
  104. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
  105. package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
  106. package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
  107. package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
  108. package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
  109. package/esm/dev-server/layer-executor.mjs +10 -3
  110. package/esm/dev-server/layer-executor.mjs.map +1 -1
  111. package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
  112. package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
  113. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  114. package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
  115. package/esm/dev-server/loader/source-slug.mjs.map +1 -1
  116. package/esm/dev-server/loader/transpile-cache.mjs +12 -3
  117. package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
  118. package/esm/dev-server/loader/version-registry.mjs.map +1 -1
  119. package/esm/dev-server/manifest-manager.d.mts.map +1 -1
  120. package/esm/dev-server/manifest-manager.mjs.map +1 -1
  121. package/esm/dev-server/module-loader.mjs.map +1 -1
  122. package/esm/dev-server/package-json-manager.mjs.map +1 -1
  123. package/esm/dev-server/parse-imports.mjs.map +1 -1
  124. package/esm/dev-server/ready-block.mjs.map +1 -1
  125. package/esm/dev-server/special-files-collector.d.mts.map +1 -1
  126. package/esm/dev-server/special-files-collector.mjs.map +1 -1
  127. package/esm/dev-server/supervisor.mjs +9 -6
  128. package/esm/dev-server/supervisor.mjs.map +1 -1
  129. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  130. package/esm/dev-server/type-generator.mjs.map +1 -1
  131. package/esm/dev-server/utils.mjs.map +1 -1
  132. package/esm/encryption/encrypt.mjs.map +1 -1
  133. package/esm/encryption/hash.mjs.map +1 -1
  134. package/esm/encryption/password.mjs.map +1 -1
  135. package/esm/generations/add-command.action.mjs +11 -4
  136. package/esm/generations/add-command.action.mjs.map +1 -1
  137. package/esm/generations/features/access.feature.mjs +2 -1
  138. package/esm/generations/features/access.feature.mjs.map +1 -1
  139. package/esm/generations/features/ai-anthropic.feature.mjs +3 -1
  140. package/esm/generations/features/ai-anthropic.feature.mjs.map +1 -1
  141. package/esm/generations/features/ai-bedrock.feature.mjs +3 -1
  142. package/esm/generations/features/ai-bedrock.feature.mjs.map +1 -1
  143. package/esm/generations/features/ai-google.feature.mjs +3 -1
  144. package/esm/generations/features/ai-google.feature.mjs.map +1 -1
  145. package/esm/generations/features/ai-ollama.feature.mjs +3 -1
  146. package/esm/generations/features/ai-ollama.feature.mjs.map +1 -1
  147. package/esm/generations/features/ai-openai.feature.mjs +3 -1
  148. package/esm/generations/features/ai-openai.feature.mjs.map +1 -1
  149. package/esm/generations/features/ai-panoptic.feature.mjs +2 -1
  150. package/esm/generations/features/ai-panoptic.feature.mjs.map +1 -1
  151. package/esm/generations/features/ai-tools.feature.mjs +2 -1
  152. package/esm/generations/features/ai-tools.feature.mjs.map +1 -1
  153. package/esm/generations/features/ai-workspace.feature.mjs +2 -1
  154. package/esm/generations/features/ai-workspace.feature.mjs.map +1 -1
  155. package/esm/generations/features/ai.feature.mjs +2 -1
  156. package/esm/generations/features/ai.feature.mjs.map +1 -1
  157. package/esm/generations/features/herald.feature.mjs +2 -1
  158. package/esm/generations/features/herald.feature.mjs.map +1 -1
  159. package/esm/generations/features/notifications.feature.mjs +3 -2
  160. package/esm/generations/features/notifications.feature.mjs.map +1 -1
  161. package/esm/generations/features/scheduler.feature.mjs +3 -1
  162. package/esm/generations/features/scheduler.feature.mjs.map +1 -1
  163. package/esm/generations/features/shadcn.feature.mjs.map +1 -1
  164. package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
  165. package/esm/generations/features/tailwind.feature.mjs.map +1 -1
  166. package/esm/generations/features/types.mjs +14 -0
  167. package/esm/generations/features/types.mjs.map +1 -0
  168. package/esm/generations/features/web.feature.mjs +10 -2
  169. package/esm/generations/features/web.feature.mjs.map +1 -1
  170. package/esm/generations/stubs.mjs +169 -28
  171. package/esm/generations/stubs.mjs.map +1 -1
  172. package/esm/http/boot-port-preflight.d.mts +46 -0
  173. package/esm/http/boot-port-preflight.d.mts.map +1 -0
  174. package/esm/http/boot-port-preflight.mjs +87 -0
  175. package/esm/http/boot-port-preflight.mjs.map +1 -0
  176. package/esm/http/context/request-context.d.mts.map +1 -1
  177. package/esm/http/context/request-context.mjs.map +1 -1
  178. package/esm/http/context/request-memo.d.mts.map +1 -1
  179. package/esm/http/context/request-memo.mjs.map +1 -1
  180. package/esm/http/createHttpApplication.d.mts.map +1 -1
  181. package/esm/http/createHttpApplication.mjs.map +1 -1
  182. package/esm/http/health.d.mts.map +1 -1
  183. package/esm/http/index.d.mts +1 -0
  184. package/esm/http/index.mjs +1 -0
  185. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  186. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  187. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  188. package/esm/http/middleware/ip-filter.middleware.d.mts +1 -1
  189. package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
  190. package/esm/http/middleware/ip-filter.middleware.mjs.map +1 -1
  191. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  192. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  193. package/esm/http/port-preflight.d.mts +8 -0
  194. package/esm/http/port-preflight.d.mts.map +1 -1
  195. package/esm/http/port-preflight.mjs +8 -1
  196. package/esm/http/port-preflight.mjs.map +1 -1
  197. package/esm/http/ready-report.d.mts.map +1 -1
  198. package/esm/http/request-controller.d.mts.map +1 -1
  199. package/esm/http/request.d.mts +14 -2
  200. package/esm/http/request.d.mts.map +1 -1
  201. package/esm/http/request.mjs +16 -2
  202. package/esm/http/request.mjs.map +1 -1
  203. package/esm/http/response.d.mts +3 -3
  204. package/esm/http/response.d.mts.map +1 -1
  205. package/esm/http/response.mjs.map +1 -1
  206. package/esm/http/server.d.mts.map +1 -1
  207. package/esm/http/server.mjs +26 -2
  208. package/esm/http/server.mjs.map +1 -1
  209. package/esm/http/types.d.mts +1 -3
  210. package/esm/http/types.d.mts.map +1 -1
  211. package/esm/http/uploaded-file.d.mts.map +1 -1
  212. package/esm/http/uploaded-file.mjs.map +1 -1
  213. package/esm/http/uploads-config.d.mts.map +1 -1
  214. package/esm/image/image.d.mts.map +1 -1
  215. package/esm/image/image.mjs.map +1 -1
  216. package/esm/index.d.mts +6 -1
  217. package/esm/index.mjs +6 -1
  218. package/esm/logger/fatal-visibility.d.mts +25 -0
  219. package/esm/logger/fatal-visibility.d.mts.map +1 -0
  220. package/esm/logger/fatal-visibility.mjs +31 -0
  221. package/esm/logger/fatal-visibility.mjs.map +1 -0
  222. package/esm/logger/index.d.mts +1 -0
  223. package/esm/logger/index.mjs +1 -0
  224. package/esm/logger/logger.mjs.map +1 -1
  225. package/esm/mail/config.d.mts.map +1 -1
  226. package/esm/mail/events.d.mts.map +1 -1
  227. package/esm/mail/mail.d.mts.map +1 -1
  228. package/esm/mail/mail.mjs.map +1 -1
  229. package/esm/mail/mailer-pool.d.mts.map +1 -1
  230. package/esm/mail/mailer-pool.mjs +2 -0
  231. package/esm/mail/mailer-pool.mjs.map +1 -1
  232. package/esm/mail/send-mail.mjs.map +1 -1
  233. package/esm/mail/test-mailbox.d.mts.map +1 -1
  234. package/esm/mail/test-mailbox.mjs.map +1 -1
  235. package/esm/production/assert-dist-ready-to-start.mjs +24 -0
  236. package/esm/production/assert-dist-ready-to-start.mjs.map +1 -0
  237. package/esm/production/assert-generated-imports.mjs.map +1 -1
  238. package/esm/production/build-app-production.mjs.map +1 -1
  239. package/esm/production/dist-build-manifest.mjs +47 -0
  240. package/esm/production/dist-build-manifest.mjs.map +1 -0
  241. package/esm/production/production-builder.mjs +68 -13
  242. package/esm/production/production-builder.mjs.map +1 -1
  243. package/esm/production/production-supervisor.mjs +21 -9
  244. package/esm/production/production-supervisor.mjs.map +1 -1
  245. package/esm/production/promote-dist.mjs +115 -0
  246. package/esm/production/promote-dist.mjs.map +1 -0
  247. package/esm/production/resolve-build-config.mjs.map +1 -1
  248. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
  249. package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
  250. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
  251. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
  252. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  253. package/esm/repositories/repository.manager.d.mts.map +1 -1
  254. package/esm/repositories/repository.manager.mjs.map +1 -1
  255. package/esm/resource/register-resource.d.mts.map +1 -1
  256. package/esm/resource/resource-field-builder.d.mts.map +1 -1
  257. package/esm/resource/resource-field-builder.mjs.map +1 -1
  258. package/esm/resource/resource.d.mts.map +1 -1
  259. package/esm/resource/resource.mjs.map +1 -1
  260. package/esm/restful/restful.d.mts.map +1 -1
  261. package/esm/restful/restful.mjs.map +1 -1
  262. package/esm/router/index.d.mts +3 -0
  263. package/esm/router/index.mjs +3 -0
  264. package/esm/router/log-request-lifecycle.mjs.map +1 -1
  265. package/esm/router/normalize-request-path.d.mts +14 -0
  266. package/esm/router/normalize-request-path.d.mts.map +1 -0
  267. package/esm/router/normalize-request-path.mjs +18 -0
  268. package/esm/router/normalize-request-path.mjs.map +1 -0
  269. package/esm/router/normalize-route-path.d.mts +28 -0
  270. package/esm/router/normalize-route-path.d.mts.map +1 -0
  271. package/esm/router/normalize-route-path.mjs +33 -0
  272. package/esm/router/normalize-route-path.mjs.map +1 -0
  273. package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
  274. package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
  275. package/esm/router/route-name-method-suffix.d.mts +33 -0
  276. package/esm/router/route-name-method-suffix.d.mts.map +1 -0
  277. package/esm/router/route-name-method-suffix.mjs +39 -0
  278. package/esm/router/route-name-method-suffix.mjs.map +1 -0
  279. package/esm/router/route-registry.d.mts.map +1 -1
  280. package/esm/router/route-registry.mjs +3 -2
  281. package/esm/router/route-registry.mjs.map +1 -1
  282. package/esm/router/router.d.mts +14 -0
  283. package/esm/router/router.d.mts.map +1 -1
  284. package/esm/router/router.mjs +50 -11
  285. package/esm/router/router.mjs.map +1 -1
  286. package/esm/socket/utils.d.mts.map +1 -1
  287. package/esm/storage/config.d.mts.map +1 -1
  288. package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
  289. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  290. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  291. package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
  292. package/esm/storage/drivers/local-driver.d.mts.map +1 -1
  293. package/esm/storage/drivers/local-driver.mjs.map +1 -1
  294. package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
  295. package/esm/storage/scoped-storage.d.mts.map +1 -1
  296. package/esm/storage/scoped-storage.mjs.map +1 -1
  297. package/esm/storage/storage-file.d.mts.map +1 -1
  298. package/esm/storage/storage-file.mjs.map +1 -1
  299. package/esm/storage/storage.d.mts.map +1 -1
  300. package/esm/storage/types.d.mts +14 -0
  301. package/esm/storage/types.d.mts.map +1 -1
  302. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  303. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  304. package/esm/tests/start-http-development-server.mjs +2 -1
  305. package/esm/tests/start-http-development-server.mjs.map +1 -1
  306. package/esm/tests/test-connectors-selection.mjs.map +1 -1
  307. package/esm/tests/test-helpers.d.mts.map +1 -1
  308. package/esm/tests/test-setup-timeout.mjs.map +1 -1
  309. package/esm/tests/vitest-setup.d.mts.map +1 -1
  310. package/esm/tests/vitest-setup.mjs.map +1 -1
  311. package/esm/updater/update-warlock-packages.mjs.map +1 -1
  312. package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
  313. package/esm/use-cases/use-case-events.d.mts.map +1 -1
  314. package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
  315. package/esm/use-cases/use-case.d.mts.map +1 -1
  316. package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
  317. package/esm/use-cases/use-cases-registry.mjs.map +1 -1
  318. package/esm/utils/database-log.d.mts.map +1 -1
  319. package/esm/utils/database-log.mjs.map +1 -1
  320. package/esm/utils/environment.d.mts.map +1 -1
  321. package/esm/utils/framework-vesion.mjs.map +1 -1
  322. package/esm/utils/get-localized.mjs.map +1 -1
  323. package/esm/utils/load-environment.mjs.map +1 -1
  324. package/esm/utils/normalized-path.d.mts.map +1 -1
  325. package/esm/utils/paths.d.mts.map +1 -1
  326. package/esm/utils/promise-all-object.d.mts.map +1 -1
  327. package/esm/utils/promise-all-object.mjs.map +1 -1
  328. package/esm/utils/sluggable.mjs.map +1 -1
  329. package/esm/utils/version-compare.mjs.map +1 -1
  330. package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
  331. package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
  332. package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
  333. package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
  334. package/esm/validation/validators/file-validator.mjs.map +1 -1
  335. package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
  336. package/esm/warlock-config/default-configurations.mjs.map +1 -1
  337. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  338. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  339. package/llms-full.txt +205 -90
  340. package/llms.txt +5 -5
  341. package/package.json +12 -12
  342. package/skills/add-connector/SKILL.md +35 -28
  343. package/skills/create-controller/SKILL.md +1 -1
  344. package/skills/create-module/SKILL.md +12 -12
  345. package/skills/hash-password/SKILL.md +4 -4
  346. package/skills/run-app/SKILL.md +77 -16
  347. package/skills/send-mail/SKILL.md +2 -2
  348. package/skills/use-middleware/SKILL.md +13 -4
  349. package/skills/use-model-transformers/SKILL.md +2 -2
  350. package/skills/use-repository/SKILL.md +1 -1
  351. package/skills/validate-input/SKILL.md +1 -1
  352. package/skills/warlock-conventions/SKILL.md +1 -1
  353. package/skills/warlock-doctor/SKILL.md +3 -3
  354. package/skills/warlock-routes/SKILL.md +47 -9
  355. package/skills/write-cli-command/SKILL.md +1 -1
  356. package/skills/write-seeder/SKILL.md +8 -8
@@ -6,7 +6,7 @@ import { doctorCommand } from "./commands/doctor.command.mjs";
6
6
  import { dropTablesCommand } from "./commands/drop-tables.command.mjs";
7
7
  import { generateCommand, generateControllerCommand, generateMigrationCommand, generateModelCommand, generateModuleCommand, generateRepositoryCommand, generateResourceCommand, generateServiceCommand } from "./commands/generate/generate.command.mjs";
8
8
  import { migrateCommand } from "./commands/migrate.command.mjs";
9
- import { routesCommand } from "./commands/routes.command.mjs";
9
+ import { routesCommand, routesDiffCommand } from "./commands/routes.command.mjs";
10
10
  import { seedCommand } from "./commands/seed.command.mjs";
11
11
  import { startProductionCommand } from "./commands/start-production.command.mjs";
12
12
  import { storagePutCommand } from "./commands/storage-put.command.mjs";
@@ -19,6 +19,7 @@ const frameworkCommands = [
19
19
  typingsGeneratorCommand,
20
20
  doctorCommand,
21
21
  routesCommand,
22
+ routesDiffCommand,
22
23
  buildCommand,
23
24
  startProductionCommand,
24
25
  migrateCommand,
@@ -1 +1 @@
1
- {"version":3,"file":"framework-cli-commands.mjs","names":[],"sources":["../../../../../../../core/src/cli/framework-cli-commands.ts"],"sourcesContent":["import { addCommand } from \"./commands/add.command\";\r\nimport { buildCommand } from \"./commands/build.command\";\r\nimport { createDatabaseCommand } from \"./commands/create-database.command\";\r\nimport { devServerCommand } from \"./commands/dev-server.command\";\r\nimport { doctorCommand } from \"./commands/doctor.command\";\r\nimport { dropTablesCommand } from \"./commands/drop-tables.command\";\r\nimport {\r\n generateCommand,\r\n generateControllerCommand,\r\n generateMigrationCommand,\r\n generateModelCommand,\r\n generateModuleCommand,\r\n generateRepositoryCommand,\r\n generateResourceCommand,\r\n generateServiceCommand,\r\n} from \"./commands/generate/generate.command\";\r\nimport { migrateCommand } from \"./commands/migrate.command\";\r\nimport { routesCommand } from \"./commands/routes.command\";\r\nimport { seedCommand } from \"./commands/seed.command\";\r\nimport { startProductionCommand } from \"./commands/start-production.command\";\r\nimport { storagePutCommand } from \"./commands/storage-put.command\";\r\nimport { typingsGeneratorCommand } from \"./commands/typings-generator.command\";\r\nimport { updateCommand } from \"./commands/update.command\";\r\n\r\nexport const frameworkCommands = [\r\n // development commands\r\n devServerCommand,\r\n typingsGeneratorCommand,\r\n\r\n // diagnostics commands\r\n doctorCommand,\r\n routesCommand,\r\n\r\n // production commands\r\n buildCommand,\r\n startProductionCommand,\r\n\r\n // database commands\r\n migrateCommand,\r\n seedCommand,\r\n createDatabaseCommand,\r\n dropTablesCommand,\r\n\r\n // generation/installation commands\r\n addCommand,\r\n updateCommand,\r\n\r\n // scaffolding commands\r\n generateCommand,\r\n generateModuleCommand,\r\n generateControllerCommand,\r\n generateServiceCommand,\r\n generateModelCommand,\r\n generateRepositoryCommand,\r\n generateResourceCommand,\r\n generateMigrationCommand,\r\n\r\n // storage commands\r\n storagePutCommand,\r\n];\r\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,MAAa,oBAAoB;CAE/B;CACA;CAGA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;AACF"}
1
+ {"version":3,"file":"framework-cli-commands.mjs","names":[],"sources":["../../../../../../../core/src/cli/framework-cli-commands.ts"],"sourcesContent":["import { addCommand } from \"./commands/add.command\";\r\nimport { buildCommand } from \"./commands/build.command\";\r\nimport { createDatabaseCommand } from \"./commands/create-database.command\";\r\nimport { devServerCommand } from \"./commands/dev-server.command\";\r\nimport { doctorCommand } from \"./commands/doctor.command\";\r\nimport { dropTablesCommand } from \"./commands/drop-tables.command\";\r\nimport {\r\n generateCommand,\r\n generateControllerCommand,\r\n generateMigrationCommand,\r\n generateModelCommand,\r\n generateModuleCommand,\r\n generateRepositoryCommand,\r\n generateResourceCommand,\r\n generateServiceCommand,\r\n} from \"./commands/generate/generate.command\";\r\nimport { migrateCommand } from \"./commands/migrate.command\";\r\nimport { routesCommand, routesDiffCommand } from \"./commands/routes.command\";\nimport { seedCommand } from \"./commands/seed.command\";\r\nimport { startProductionCommand } from \"./commands/start-production.command\";\r\nimport { storagePutCommand } from \"./commands/storage-put.command\";\r\nimport { typingsGeneratorCommand } from \"./commands/typings-generator.command\";\r\nimport { updateCommand } from \"./commands/update.command\";\r\n\r\nexport const frameworkCommands = [\r\n // development commands\r\n devServerCommand,\r\n typingsGeneratorCommand,\r\n\r\n // diagnostics commands\r\n doctorCommand,\n routesCommand,\n routesDiffCommand,\n\r\n // production commands\r\n buildCommand,\r\n startProductionCommand,\r\n\r\n // database commands\r\n migrateCommand,\r\n seedCommand,\r\n createDatabaseCommand,\r\n dropTablesCommand,\r\n\r\n // generation/installation commands\r\n addCommand,\r\n updateCommand,\r\n\r\n // scaffolding commands\r\n generateCommand,\r\n generateModuleCommand,\r\n generateControllerCommand,\r\n generateServiceCommand,\r\n generateModelCommand,\r\n generateRepositoryCommand,\r\n generateResourceCommand,\r\n generateMigrationCommand,\r\n\r\n // storage commands\r\n storagePutCommand,\r\n];\r\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,MAAa,oBAAoB;CAE/B;CACA;CAGA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"start.mjs","names":[],"sources":["../../../../../../../core/src/cli/start.ts"],"sourcesContent":["import { CLICommandsManager } from \"./cli-commands.manager\";\n\nconst commandsManager = new CLICommandsManager();\nawait commandsManager.start();\n"],"mappings":";;;AAGA,MAAM,IADsB,mBACR,CAAC,CAAC,MAAM"}
1
+ {"version":3,"file":"start.mjs","names":[],"sources":["../../../../../../../core/src/cli/start.ts"],"sourcesContent":["import { CLICommandsManager } from \"./cli-commands.manager\";\n\nconst commandsManager = new CLICommandsManager();\nawait commandsManager.start();\n"],"mappings":";;;AAGA,MAAM,IADsB,mBACR,EAAE,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"string-similarity.mjs","names":[],"sources":["../../../../../../../core/src/cli/string-similarity.ts"],"sourcesContent":["/**\n * String similarity utilities for CLI command suggestions\n * Uses Levenshtein distance algorithm for fuzzy matching\n */\n\n/**\n * Calculate the Levenshtein distance between two strings\n * Lower distance = more similar\n *\n * @param str1 - First string\n * @param str2 - Second string\n * @returns Distance (number of edits required to transform str1 to str2)\n */\nexport function levenshteinDistance(str1: string, str2: string): number {\n const s1 = str1.toLowerCase();\n const s2 = str2.toLowerCase();\n\n const len1 = s1.length;\n const len2 = s2.length;\n\n // Create matrix\n const matrix: number[][] = Array.from({ length: len1 + 1 }, (_, i) =>\n Array.from({ length: len2 + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),\n );\n\n // Fill matrix\n for (let i = 1; i <= len1; i++) {\n for (let j = 1; j <= len2; j++) {\n const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;\n matrix[i][j] = Math.min(\n matrix[i - 1][j] + 1, // deletion\n matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j - 1] + cost, // substitution\n );\n }\n }\n\n return matrix[len1][len2];\n}\n\n/**\n * Suggestion result with similarity score\n */\nexport type Suggestion = {\n /** The suggested value */\n value: string;\n /** Distance score (lower = more similar) */\n distance: number;\n};\n\n/**\n * Find similar strings from a list\n *\n * @param input - The input string to find matches for\n * @param candidates - List of candidate strings\n * @param maxDistance - Maximum distance to consider (default: 3)\n * @param maxResults - Maximum number of results to return (default: 3)\n * @returns Array of suggestions sorted by similarity\n *\n * @example\n * ```typescript\n * findSimilar(\"biuld\", [\"build\", \"dev\", \"start\", \"test\"])\n * // Returns: [{ value: \"build\", distance: 1 }]\n * ```\n */\nexport function findSimilar(\n input: string,\n candidates: string[],\n maxDistance: number = 3,\n maxResults: number = 3,\n): Suggestion[] {\n const results: Suggestion[] = [];\n\n for (const candidate of candidates) {\n const distance = levenshteinDistance(input, candidate);\n\n if (distance <= maxDistance && distance > 0) {\n results.push({ value: candidate, distance });\n }\n }\n\n // Sort by distance (most similar first)\n results.sort((a, b) => a.distance - b.distance);\n\n return results.slice(0, maxResults);\n}\n\n/**\n * Check if a string starts with any of the given prefixes\n * Useful for command name matching\n */\nexport function startsWithAny(str: string, prefixes: string[]): boolean {\n return prefixes.some((prefix) => str.toLowerCase().startsWith(prefix.toLowerCase()));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,oBAAoB,MAAc,MAAsB;CACtE,MAAM,KAAK,KAAK,YAAY;CAC5B,MAAM,KAAK,KAAK,YAAY;CAE5B,MAAM,OAAO,GAAG;CAChB,MAAM,OAAO,GAAG;CAGhB,MAAM,SAAqB,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,GAAG,MAC9D,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,GAAG,MAAO,MAAM,IAAI,IAAI,MAAM,IAAI,IAAI,CAAE,CAC5E;CAGA,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,KACzB,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,KAAK;EAC9B,MAAM,OAAO,GAAG,IAAI,OAAO,GAAG,IAAI,KAAK,IAAI;EAC3C,OAAO,EAAE,CAAC,KAAK,KAAK,IAClB,OAAO,IAAI,EAAE,CAAC,KAAK,GACnB,OAAO,EAAE,CAAC,IAAI,KAAK,GACnB,OAAO,IAAI,EAAE,CAAC,IAAI,KAAK,IACzB;CACF;CAGF,OAAO,OAAO,KAAK,CAAC;AACtB;;;;;;;;;;;;;;;;AA2BA,SAAgB,YACd,OACA,YACA,cAAsB,GACtB,aAAqB,GACP;CACd,MAAM,UAAwB,CAAC;CAE/B,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,WAAW,oBAAoB,OAAO,SAAS;EAErD,IAAI,YAAY,eAAe,WAAW,GACxC,QAAQ,KAAK;GAAE,OAAO;GAAW;EAAS,CAAC;CAE/C;CAGA,QAAQ,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CAE9C,OAAO,QAAQ,MAAM,GAAG,UAAU;AACpC"}
1
+ {"version":3,"file":"string-similarity.mjs","names":[],"sources":["../../../../../../../core/src/cli/string-similarity.ts"],"sourcesContent":["/**\n * String similarity utilities for CLI command suggestions\n * Uses Levenshtein distance algorithm for fuzzy matching\n */\n\n/**\n * Calculate the Levenshtein distance between two strings\n * Lower distance = more similar\n *\n * @param str1 - First string\n * @param str2 - Second string\n * @returns Distance (number of edits required to transform str1 to str2)\n */\nexport function levenshteinDistance(str1: string, str2: string): number {\n const s1 = str1.toLowerCase();\n const s2 = str2.toLowerCase();\n\n const len1 = s1.length;\n const len2 = s2.length;\n\n // Create matrix\n const matrix: number[][] = Array.from({ length: len1 + 1 }, (_, i) =>\n Array.from({ length: len2 + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),\n );\n\n // Fill matrix\n for (let i = 1; i <= len1; i++) {\n for (let j = 1; j <= len2; j++) {\n const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;\n matrix[i][j] = Math.min(\n matrix[i - 1][j] + 1, // deletion\n matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j - 1] + cost, // substitution\n );\n }\n }\n\n return matrix[len1][len2];\n}\n\n/**\n * Suggestion result with similarity score\n */\nexport type Suggestion = {\n /** The suggested value */\n value: string;\n /** Distance score (lower = more similar) */\n distance: number;\n};\n\n/**\n * Find similar strings from a list\n *\n * @param input - The input string to find matches for\n * @param candidates - List of candidate strings\n * @param maxDistance - Maximum distance to consider (default: 3)\n * @param maxResults - Maximum number of results to return (default: 3)\n * @returns Array of suggestions sorted by similarity\n *\n * @example\n * ```typescript\n * findSimilar(\"biuld\", [\"build\", \"dev\", \"start\", \"test\"])\n * // Returns: [{ value: \"build\", distance: 1 }]\n * ```\n */\nexport function findSimilar(\n input: string,\n candidates: string[],\n maxDistance: number = 3,\n maxResults: number = 3,\n): Suggestion[] {\n const results: Suggestion[] = [];\n\n for (const candidate of candidates) {\n const distance = levenshteinDistance(input, candidate);\n\n if (distance <= maxDistance && distance > 0) {\n results.push({ value: candidate, distance });\n }\n }\n\n // Sort by distance (most similar first)\n results.sort((a, b) => a.distance - b.distance);\n\n return results.slice(0, maxResults);\n}\n\n/**\n * Check if a string starts with any of the given prefixes\n * Useful for command name matching\n */\nexport function startsWithAny(str: string, prefixes: string[]): boolean {\n return prefixes.some((prefix) => str.toLowerCase().startsWith(prefix.toLowerCase()));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,oBAAoB,MAAc,MAAsB;CACtE,MAAM,KAAK,KAAK,YAAY;CAC5B,MAAM,KAAK,KAAK,YAAY;CAE5B,MAAM,OAAO,GAAG;CAChB,MAAM,OAAO,GAAG;CAGhB,MAAM,SAAqB,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,GAAG,MAC9D,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,GAAG,MAAO,MAAM,IAAI,IAAI,MAAM,IAAI,IAAI,CAAE,CAC5E;CAGA,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,KACzB,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,KAAK;EAC9B,MAAM,OAAO,GAAG,IAAI,OAAO,GAAG,IAAI,KAAK,IAAI;EAC3C,OAAO,GAAG,KAAK,KAAK,IAClB,OAAO,IAAI,GAAG,KAAK,GACnB,OAAO,GAAG,IAAI,KAAK,GACnB,OAAO,IAAI,GAAG,IAAI,KAAK,IACzB;CACF;CAGF,OAAO,OAAO,MAAM;AACtB;;;;;;;;;;;;;;;;AA2BA,SAAgB,YACd,OACA,YACA,cAAsB,GACtB,aAAqB,GACP;CACd,MAAM,UAAwB,CAAC;CAE/B,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,WAAW,oBAAoB,OAAO,SAAS;EAErD,IAAI,YAAY,eAAe,WAAW,GACxC,QAAQ,KAAK;GAAE,OAAO;GAAW;EAAS,CAAC;CAE/C;CAGA,QAAQ,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CAE9C,OAAO,QAAQ,MAAM,GAAG,UAAU;AACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cli-command.mjs","names":[],"sources":["../../../../../../../core/src/commands/cli-command.ts"],"sourcesContent":["import type {\n CLICommandAction,\n CLICommandOption,\n CLICommandOptions,\n CLICommandPreload,\n CLICommandSource,\n CommandActionData,\n ResolvedCLICommandOption,\n} from \"./types\";\n\nexport class CLICommand {\n /**\n * Command source\n */\n public commandSource?: CLICommandSource;\n\n /**\n * Command action\n */\n public commandAction?: CLICommandAction;\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public commandPreAction?: CLICommandAction;\n\n /**\n * Command preload\n */\n public commandPreload?: CLICommandPreload;\n\n /**\n * Command description\n */\n public commandDescription?: string;\n\n /**\n * Command options\n */\n public commandOptions: ResolvedCLICommandOption[] = [];\n\n /**\n * Command relative path\n * Available only for project commands\n * Auto injected by the framework itself\n */\n public commandRelativePath?: string;\n\n /**\n * Determine if the command is persistent\n */\n public isPersistent: boolean = false;\n\n /**\n * Command alias (short name)\n */\n public commandAlias?: string;\n\n /**\n * Constructor\n */\n public constructor(\n public name: string,\n description?: string,\n ) {\n if (description) {\n this.commandDescription = description;\n }\n\n return this;\n }\n\n /**\n * Add command source\n */\n public source(source: CLICommandSource): this {\n this.commandSource = source;\n return this;\n }\n\n /**\n * Set command description\n */\n public description(description: string): this {\n this.commandDescription = description;\n return this;\n }\n\n /**\n * Determine if the command is persistent\n */\n public persistent(isPersistent = true): this {\n this.isPersistent = isPersistent;\n return this;\n }\n\n /**\n * Set command alias (short name)\n * @example .alias(\"m\") for \"migrate\"\n */\n public alias(alias: string): this {\n this.commandAlias = alias;\n return this;\n }\n\n /**\n * Command action\n */\n public action(action: CLICommandAction): this {\n this.commandAction = action;\n return this;\n }\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public preAction(action: CLICommandAction): this {\n this.commandPreAction = action;\n return this;\n }\n\n /**\n * Add command options\n */\n public options(options: CLICommandOption[]): this {\n options.map((option) => this.option(option));\n\n return this;\n }\n\n /**\n * Add command relative path\n */\n public $relativePath(relativePath: string) {\n this.commandRelativePath = relativePath;\n return this;\n }\n\n /**\n * Add command option\n */\n public option(option: CLICommandOption): this;\n public option(\n name: string,\n description?: string,\n options?: Omit<CLICommandOption, \"name\" | \"text\" | \"description\">,\n ): this;\n public option(\n ...args:\n | [CLICommandOption]\n | [string, string?, Omit<CLICommandOption, \"name\" | \"text\" | \"description\">?]\n ): this {\n let option: CLICommandOption;\n if (args.length === 1) {\n option = args[0] as CLICommandOption;\n } else {\n option = {\n text: args[0],\n description: args[1],\n ...args[2],\n name: \"\",\n };\n }\n\n this.commandOptions.push(this.parseOption(option));\n\n return this;\n }\n\n /**\n * Parse option name and alias if exists\n *\n * Supports formats:\n * - \"--port, -p\" → name: \"port\", alias: \"p\"\n * - \"-p, --port\" → name: \"port\", alias: \"p\"\n * - \"--port\" → name: \"port\", alias: undefined\n * - \"-p\" → name: \"p\", alias: undefined\n */\n protected parseOption(option: CLICommandOption): ResolvedCLICommandOption {\n const text = option.text.trim();\n\n // Split by comma to check for alias\n const parts = text.split(\",\").map((part) => part.trim());\n\n let name = \"\";\n let alias = \"\";\n\n if (parts.length === 1) {\n // Single option: \"--port\" or \"-p\"\n name = this.extractOptionName(parts[0]);\n } else if (parts.length === 2) {\n // Two options: \"--port, -p\" or \"-p, --port\"\n const first = parts[0];\n const second = parts[1];\n\n // Determine which is the long form (name) and which is short (alias)\n if (first.startsWith(\"--\")) {\n name = this.extractOptionName(first);\n alias = this.extractOptionName(second);\n } else {\n name = this.extractOptionName(second);\n alias = this.extractOptionName(first);\n }\n }\n\n if (alias === \"h\" || name === \"help\") {\n throw new Error(\"Help option is not allowed, it's reserved for displaying command help\");\n }\n\n return {\n ...option,\n name,\n alias,\n };\n }\n\n /**\n * Extract option name from text (removes -- or -)\n *\n * @example\n * extractOptionName(\"--port\") → \"port\"\n * extractOptionName(\"-p\") → \"p\"\n * extractOptionName(\"--port=3000\") → \"port\"\n */\n private extractOptionName(text: string): string {\n // Remove leading dashes\n let name = text.replace(/^-+/, \"\");\n\n // Remove value assignment if exists (e.g., \"--port=3000\" → \"port\")\n const equalIndex = name.indexOf(\"=\");\n if (equalIndex !== -1) {\n name = name.slice(0, equalIndex);\n }\n\n // Remove angle brackets if exists (e.g., \"--port <number>\" → \"port\")\n const spaceIndex = name.indexOf(\" \");\n if (spaceIndex !== -1) {\n name = name.slice(0, spaceIndex);\n }\n\n return name.trim();\n }\n\n /**\n * Command preload\n */\n public preload(options: CLICommandPreload) {\n this.commandPreload = options;\n return this;\n }\n\n /**\n * Execute the command\n */\n public async execute(data: CommandActionData) {\n if (!this.commandAction) {\n throw new Error(`Command \"${this.name}\" has no action defined`);\n }\n\n await this.commandAction(data);\n }\n}\n\nexport function command(options: CLICommandOptions) {\n const commandInstnace = new CLICommand(options.name, options.description);\n\n if (options.preload) {\n commandInstnace.preload(options.preload);\n }\n\n if (options.persistent) {\n commandInstnace.persistent(options.persistent);\n }\n\n if (options.alias) {\n commandInstnace.alias(options.alias);\n }\n\n commandInstnace.action(options.action);\n\n if (options.options) {\n commandInstnace.options(options.options);\n }\n\n if (options.preAction) {\n commandInstnace.preAction(options.preAction);\n }\n\n return commandInstnace;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-command.mjs","names":[],"sources":["../../../../../../../core/src/commands/cli-command.ts"],"sourcesContent":["import type {\n CLICommandAction,\n CLICommandOption,\n CLICommandOptions,\n CLICommandPreload,\n CLICommandSource,\n CommandActionData,\n ResolvedCLICommandOption,\n} from \"./types\";\n\nexport class CLICommand {\n /**\n * Command source\n */\n public commandSource?: CLICommandSource;\n\n /**\n * Command action\n */\n public commandAction?: CLICommandAction;\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public commandPreAction?: CLICommandAction;\n\n /**\n * Command preload\n */\n public commandPreload?: CLICommandPreload;\n\n /**\n * Command description\n */\n public commandDescription?: string;\n\n /**\n * Command options\n */\n public commandOptions: ResolvedCLICommandOption[] = [];\n\n /**\n * Command relative path\n * Available only for project commands\n * Auto injected by the framework itself\n */\n public commandRelativePath?: string;\n\n /**\n * Determine if the command is persistent\n */\n public isPersistent: boolean = false;\n\n /**\n * Command alias (short name)\n */\n public commandAlias?: string;\n\n /**\n * Constructor\n */\n public constructor(\n public name: string,\n description?: string,\n ) {\n if (description) {\n this.commandDescription = description;\n }\n\n return this;\n }\n\n /**\n * Add command source\n */\n public source(source: CLICommandSource): this {\n this.commandSource = source;\n return this;\n }\n\n /**\n * Set command description\n */\n public description(description: string): this {\n this.commandDescription = description;\n return this;\n }\n\n /**\n * Determine if the command is persistent\n */\n public persistent(isPersistent = true): this {\n this.isPersistent = isPersistent;\n return this;\n }\n\n /**\n * Set command alias (short name)\n * @example .alias(\"m\") for \"migrate\"\n */\n public alias(alias: string): this {\n this.commandAlias = alias;\n return this;\n }\n\n /**\n * Command action\n */\n public action(action: CLICommandAction): this {\n this.commandAction = action;\n return this;\n }\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public preAction(action: CLICommandAction): this {\n this.commandPreAction = action;\n return this;\n }\n\n /**\n * Add command options\n */\n public options(options: CLICommandOption[]): this {\n options.map((option) => this.option(option));\n\n return this;\n }\n\n /**\n * Add command relative path\n */\n public $relativePath(relativePath: string) {\n this.commandRelativePath = relativePath;\n return this;\n }\n\n /**\n * Add command option\n */\n public option(option: CLICommandOption): this;\n public option(\n name: string,\n description?: string,\n options?: Omit<CLICommandOption, \"name\" | \"text\" | \"description\">,\n ): this;\n public option(\n ...args:\n | [CLICommandOption]\n | [string, string?, Omit<CLICommandOption, \"name\" | \"text\" | \"description\">?]\n ): this {\n let option: CLICommandOption;\n if (args.length === 1) {\n option = args[0] as CLICommandOption;\n } else {\n option = {\n text: args[0],\n description: args[1],\n ...args[2],\n name: \"\",\n };\n }\n\n this.commandOptions.push(this.parseOption(option));\n\n return this;\n }\n\n /**\n * Parse option name and alias if exists\n *\n * Supports formats:\n * - \"--port, -p\" → name: \"port\", alias: \"p\"\n * - \"-p, --port\" → name: \"port\", alias: \"p\"\n * - \"--port\" → name: \"port\", alias: undefined\n * - \"-p\" → name: \"p\", alias: undefined\n */\n protected parseOption(option: CLICommandOption): ResolvedCLICommandOption {\n const text = option.text.trim();\n\n // Split by comma to check for alias\n const parts = text.split(\",\").map((part) => part.trim());\n\n let name = \"\";\n let alias = \"\";\n\n if (parts.length === 1) {\n // Single option: \"--port\" or \"-p\"\n name = this.extractOptionName(parts[0]);\n } else if (parts.length === 2) {\n // Two options: \"--port, -p\" or \"-p, --port\"\n const first = parts[0];\n const second = parts[1];\n\n // Determine which is the long form (name) and which is short (alias)\n if (first.startsWith(\"--\")) {\n name = this.extractOptionName(first);\n alias = this.extractOptionName(second);\n } else {\n name = this.extractOptionName(second);\n alias = this.extractOptionName(first);\n }\n }\n\n if (alias === \"h\" || name === \"help\") {\n throw new Error(\"Help option is not allowed, it's reserved for displaying command help\");\n }\n\n return {\n ...option,\n name,\n alias,\n };\n }\n\n /**\n * Extract option name from text (removes -- or -)\n *\n * @example\n * extractOptionName(\"--port\") → \"port\"\n * extractOptionName(\"-p\") → \"p\"\n * extractOptionName(\"--port=3000\") → \"port\"\n */\n private extractOptionName(text: string): string {\n // Remove leading dashes\n let name = text.replace(/^-+/, \"\");\n\n // Remove value assignment if exists (e.g., \"--port=3000\" → \"port\")\n const equalIndex = name.indexOf(\"=\");\n if (equalIndex !== -1) {\n name = name.slice(0, equalIndex);\n }\n\n // Remove angle brackets if exists (e.g., \"--port <number>\" → \"port\")\n const spaceIndex = name.indexOf(\" \");\n if (spaceIndex !== -1) {\n name = name.slice(0, spaceIndex);\n }\n\n return name.trim();\n }\n\n /**\n * Command preload\n */\n public preload(options: CLICommandPreload) {\n this.commandPreload = options;\n return this;\n }\n\n /**\n * Execute the command\n */\n public async execute(data: CommandActionData) {\n if (!this.commandAction) {\n throw new Error(`Command \"${this.name}\" has no action defined`);\n }\n\n await this.commandAction(data);\n }\n}\n\nexport function command(options: CLICommandOptions) {\n const commandInstnace = new CLICommand(options.name, options.description);\n\n if (options.preload) {\n commandInstnace.preload(options.preload);\n }\n\n if (options.persistent) {\n commandInstnace.persistent(options.persistent);\n }\n\n if (options.alias) {\n commandInstnace.alias(options.alias);\n }\n\n commandInstnace.action(options.action);\n\n if (options.options) {\n commandInstnace.options(options.options);\n }\n\n if (options.preAction) {\n commandInstnace.preAction(options.preAction);\n }\n\n return commandInstnace;\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  * Special Config Handler
4
4
  * A function that handles special configuration loading
5
5
  */
6
- type SpecialConfigHandler = (configValue: any) => void | Promise<void>;
6
+ type SpecialConfigHandler = (configValue: any) => unknown | Promise<unknown>;
7
7
  //#endregion
8
8
  export { SpecialConfigHandler };
9
9
  //# sourceMappingURL=config-loader.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.mts","names":[],"sources":["../../../../../../../core/src/config/config-loader.ts"],"mappings":";AAUA;;;;AAAA,KAAY,oBAAA,IAAwB,WAAA,iBAA4B,OAAO"}
1
+ {"version":3,"file":"config-loader.d.mts","names":[],"sources":["../../../../../../../core/src/config/config-loader.ts"],"mappings":";AAcA;;;;AAAA,KAAY,oBAAA,IAAwB,WAAA,oBAA+B,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.mjs","names":[],"sources":["../../../../../../../core/src/config/config-loader.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { pathToFileURL } from \"node:url\";\r\nimport type { FileManager } from \"../dev-server/file-manager\";\r\nimport { configSpecialHandlers } from \"./config-special-handlers\";\r\n\r\n/**\r\n * Special Config Handler\r\n * A function that handles special configuration loading\r\n */\r\nexport type SpecialConfigHandler = (configValue: any) => void | Promise<void>;\r\n\r\n/**\r\n * Config Loader\r\n * Dynamically loads all configuration files and registers them with @mongez/config\r\n * Supports special handlers for configs that require additional processing\r\n */\r\nexport class ConfigLoader {\r\n public async loadAll(configFiles: FileManager[]): Promise<void> {\r\n for (const file of configFiles) {\r\n await this.loadConfig(file);\r\n }\r\n }\r\n\r\n /**\r\n * Load a single configuration file.\r\n *\r\n * The ESM loader hook stamps a version token onto the URL so that each HMR\r\n * cycle gets a fresh module — no manual cache-busting needed here.\r\n */\r\n public async loadConfig(file: FileManager): Promise<void> {\r\n const configName = this.getConfigName(file.relativePath);\r\n\r\n try {\r\n const fileUrl = pathToFileURL(file.absolutePath).href;\r\n const configModule = await import(fileUrl);\r\n const configValue = configModule.default;\r\n\r\n if (configValue === undefined) {\r\n console.log(\r\n colors.red(`config error: `),\r\n `Config file ${colors.yellow(file.relativePath)} does not have a default export`,\r\n );\r\n return;\r\n }\r\n\r\n config.set(configName, configValue);\r\n await configSpecialHandlers.execute(configName, configValue);\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n public async reloadConfig(file: FileManager): Promise<void> {\r\n await this.loadConfig(file);\r\n }\r\n\r\n private getConfigName(relativePath: string): string {\r\n const match = relativePath.match(/^src\\/config\\/(.+)\\.(ts|tsx)$/);\r\n\r\n if (!match) {\r\n throw new Error(`Invalid config file path: ${relativePath}`);\r\n }\r\n\r\n return match[1];\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AAiBA,IAAa,eAAb,MAA0B;CACxB,MAAa,QAAQ,aAA2C;EAC9D,KAAK,MAAM,QAAQ,aACjB,MAAM,KAAK,WAAW,IAAI;CAE9B;;;;;;;CAQA,MAAa,WAAW,MAAkC;EACxD,MAAM,aAAa,KAAK,cAAc,KAAK,YAAY;EAEvD,IAAI;GAGF,MAAM,eAAc,MADO,OADX,cAAc,KAAK,YAAY,CAAC,CAAC,MAEjB,CAAC;GAEjC,IAAI,gBAAgB,QAAW;IAC7B,QAAQ,IACN,OAAO,IAAI,gBAAgB,GAC3B,eAAe,OAAO,OAAO,KAAK,YAAY,EAAE,gCAClD;IACA;GACF;GAEA,OAAO,IAAI,YAAY,WAAW;GAClC,MAAM,sBAAsB,QAAQ,YAAY,WAAW;EAC7D,SAAS,OAAO;GACd,MAAM;EACR;CACF;CAEA,MAAa,aAAa,MAAkC;EAC1D,MAAM,KAAK,WAAW,IAAI;CAC5B;CAEA,AAAQ,cAAc,cAA8B;EAClD,MAAM,QAAQ,aAAa,MAAM,+BAA+B;EAEhE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,6BAA6B,cAAc;EAG7D,OAAO,MAAM;CACf;AACF"}
1
+ {"version":3,"file":"config-loader.mjs","names":[],"sources":["../../../../../../../core/src/config/config-loader.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { pathToFileURL } from \"node:url\";\r\nimport type { FileManager } from \"../dev-server/file-manager\";\r\nimport { configSpecialHandlers } from \"./config-special-handlers\";\r\n\r\n/**\r\n * Special Config Handler\r\n * A function that handles special configuration loading\r\n */\r\n// Returns unknown, not void: ConfigSpecialHandlers.execute() RETURNS handler(config)\r\n// rather than discarding it, and a test pins that (\"returns the handler's resolved\r\n// value\"). The implementation and the test agreed; only this type said handlers\r\n// produce nothing, which made any value-returning handler fail to typecheck.\r\nexport type SpecialConfigHandler = (configValue: any) => unknown | Promise<unknown>;\r\n\r\n/**\r\n * Config Loader\r\n * Dynamically loads all configuration files and registers them with @mongez/config\r\n * Supports special handlers for configs that require additional processing\r\n */\r\nexport class ConfigLoader {\r\n public async loadAll(configFiles: FileManager[]): Promise<void> {\r\n for (const file of configFiles) {\r\n await this.loadConfig(file);\r\n }\r\n }\r\n\r\n /**\r\n * Load a single configuration file.\r\n *\r\n * The ESM loader hook stamps a version token onto the URL so that each HMR\r\n * cycle gets a fresh module — no manual cache-busting needed here.\r\n */\r\n public async loadConfig(file: FileManager): Promise<void> {\r\n const configName = this.getConfigName(file.relativePath);\r\n\r\n try {\r\n const fileUrl = pathToFileURL(file.absolutePath).href;\r\n const configModule = await import(fileUrl);\r\n const configValue = configModule.default;\r\n\r\n if (configValue === undefined) {\r\n console.log(\r\n colors.red(`config error: `),\r\n `Config file ${colors.yellow(file.relativePath)} does not have a default export`,\r\n );\r\n return;\r\n }\r\n\r\n config.set(configName, configValue);\r\n await configSpecialHandlers.execute(configName, configValue);\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n public async reloadConfig(file: FileManager): Promise<void> {\r\n await this.loadConfig(file);\r\n }\r\n\r\n private getConfigName(relativePath: string): string {\r\n const match = relativePath.match(/^src\\/config\\/(.+)\\.(ts|tsx)$/);\r\n\r\n if (!match) {\r\n throw new Error(`Invalid config file path: ${relativePath}`);\r\n }\r\n\r\n return match[1];\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AAqBA,IAAa,eAAb,MAA0B;CACxB,MAAa,QAAQ,aAA2C;EAC9D,KAAK,MAAM,QAAQ,aACjB,MAAM,KAAK,WAAW,IAAI;CAE9B;;;;;;;CAQA,MAAa,WAAW,MAAkC;EACxD,MAAM,aAAa,KAAK,cAAc,KAAK,YAAY;EAEvD,IAAI;GAGF,MAAM,eAAc,MADO,OADX,cAAc,KAAK,YAAY,EAAE,OAEhB;GAEjC,IAAI,gBAAgB,QAAW;IAC7B,QAAQ,IACN,OAAO,IAAI,gBAAgB,GAC3B,eAAe,OAAO,OAAO,KAAK,YAAY,EAAE,gCAClD;IACA;GACF;GAEA,OAAO,IAAI,YAAY,WAAW;GAClC,MAAM,sBAAsB,QAAQ,YAAY,WAAW;EAC7D,SAAS,OAAO;GACd,MAAM;EACR;CACF;CAEA,MAAa,aAAa,MAAkC;EAC1D,MAAM,KAAK,WAAW,IAAI;CAC5B;CAEA,AAAQ,cAAc,cAA8B;EAClD,MAAM,QAAQ,aAAa,MAAM,+BAA+B;EAEhE,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,6BAA6B,cAAc;EAG7D,OAAO,MAAM;CACf;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-setter.d.mts","names":[],"sources":["../../../../../../../core/src/config/config-setter.ts"],"mappings":";;;;;AAsBA;;;;;;;;;;;;;;;;;iBAAgB,SAAA,iBAA0B,cAAA,EAAgB,IAAA,EAAM,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;AAAA,iBACzE,SAAA,CAAU,IAAA,UAAc,KAAc"}
1
+ {"version":3,"file":"config-setter.d.mts","names":[],"sources":["../../../../../../../core/src/config/config-setter.ts"],"mappings":";;;;;AAsBA;;;;;;;;;;;;;;;;;iBAAgB,SAAA,iBAA0B,cAAA,CAAA,CAAgB,IAAA,EAAM,CAAA,EAAG,KAAA,EAAO,cAAA,CAAe,CAAA;AAAA,iBACzE,SAAA,CAAU,IAAA,UAAc,KAAc"}
@@ -13,7 +13,7 @@ declare class ConfigSpecialHandlers {
13
13
  /**
14
14
  * Execute handler for the given config name
15
15
  */
16
- execute(configName: string, config: any): Promise<void>;
16
+ execute(configName: string, config: any): Promise<unknown>;
17
17
  }
18
18
  declare const configSpecialHandlers: ConfigSpecialHandlers;
19
19
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"config-special-handlers.mjs","names":[],"sources":["../../../../../../../core/src/config/config-special-handlers.ts"],"sourcesContent":["import { SpecialConfigHandler } from \"./config-loader\";\n\nexport class ConfigSpecialHandlers {\n /**\n * Handlers\n */\n protected handlers: Map<string, SpecialConfigHandler> = new Map();\n\n /**\n * Register a new handler\n */\n public register(configName: string, handler: SpecialConfigHandler) {\n this.handlers.set(configName, handler);\n }\n\n /**\n * Execute handler for the given config name\n */\n public async execute(configName: string, config: any) {\n const handler = this.handlers.get(configName);\n if (!handler) {\n return; // do nothing\n }\n\n return handler(config);\n }\n}\n\nexport const configSpecialHandlers = new ConfigSpecialHandlers();\n"],"mappings":";AAEA,IAAa,wBAAb,MAAmC;;kCAIuB,IAAI,IAAI;;;;;CAKhE,AAAO,SAAS,YAAoB,SAA+B;EACjE,KAAK,SAAS,IAAI,YAAY,OAAO;CACvC;;;;CAKA,MAAa,QAAQ,YAAoB,QAAa;EACpD,MAAM,UAAU,KAAK,SAAS,IAAI,UAAU;EAC5C,IAAI,CAAC,SACH;EAGF,OAAO,QAAQ,MAAM;CACvB;AACF;AAEA,MAAa,wBAAwB,IAAI,sBAAsB"}
1
+ {"version":3,"file":"config-special-handlers.mjs","names":[],"sources":["../../../../../../../core/src/config/config-special-handlers.ts"],"sourcesContent":["import { type SpecialConfigHandler } from \"./config-loader\";\n\nexport class ConfigSpecialHandlers {\n /**\n * Handlers\n */\n protected handlers: Map<string, SpecialConfigHandler> = new Map();\n\n /**\n * Register a new handler\n */\n public register(configName: string, handler: SpecialConfigHandler) {\n this.handlers.set(configName, handler);\n }\n\n /**\n * Execute handler for the given config name\n */\n public async execute(configName: string, config: any) {\n const handler = this.handlers.get(configName);\n if (!handler) {\n return; // do nothing\n }\n\n return handler(config);\n }\n}\n\nexport const configSpecialHandlers = new ConfigSpecialHandlers();\n"],"mappings":";AAEA,IAAa,wBAAb,MAAmC;;kCAIuB,IAAI,IAAI;;;;;CAKhE,AAAO,SAAS,YAAoB,SAA+B;EACjE,KAAK,SAAS,IAAI,YAAY,OAAO;CACvC;;;;CAKA,MAAa,QAAQ,YAAoB,QAAa;EACpD,MAAM,UAAU,KAAK,SAAS,IAAI,UAAU;EAC5C,IAAI,CAAC,SACH;EAGF,OAAO,QAAQ,MAAM;CACvB;AACF;AAEA,MAAa,wBAAwB,IAAI,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"load-config-files.mjs","names":[],"sources":["../../../../../../../core/src/config/load-config-files.ts"],"sourcesContent":["import { filesOrchestrator } from \"../dev-server/files-orchestrator\";\nimport { Path } from \"../utils/normalized-path\";\nimport { getCertainFilesFromDirectory, getFilesFromDirectory } from \"../dev-server/utils\";\nimport { srcPath } from \"../utils\";\nimport { configManager } from \"./config-manager\";\n\n/**\n * Load config files\n * either config file names (without extensions) or true to load all config files\n */\nexport async function loadConfigFiles(config: string[] | true) {\n let relativePaths: string[] = [];\n if (config === true) {\n const configFilesAbsulatePaths = await getFilesFromDirectory(srcPath(\"config\"));\n relativePaths = configFilesAbsulatePaths.map((path) => Path.toRelative(path));\n } else {\n // define only the given config files\n\n const configFilesAbsulatePaths = await getCertainFilesFromDirectory(srcPath(\"config\"), config);\n\n relativePaths = configFilesAbsulatePaths.map((path) => Path.toRelative(path));\n }\n\n const configFiles = await Promise.all(\n relativePaths.map((relativePath) => filesOrchestrator.add(relativePath)),\n );\n\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.files);\n\n await configManager.loadAll(configFiles);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,eAAsB,gBAAgB,QAAyB;CAC7D,IAAI,gBAA0B,CAAC;CAC/B,IAAI,WAAW,MAEb,iBAAgB,MADuB,sBAAsB,QAAQ,QAAQ,CAAC,EACtC,CAAC,KAAK,SAAS,KAAK,WAAW,IAAI,CAAC;MAM5E,iBAAgB,MAFuB,6BAA6B,QAAQ,QAAQ,GAAG,MAAM,EAErD,CAAC,KAAK,SAAS,KAAK,WAAW,IAAI,CAAC;CAG9E,MAAM,cAAc,MAAM,QAAQ,IAChC,cAAc,KAAK,iBAAiB,kBAAkB,IAAI,YAAY,CAAC,CACzE;CAEA,kBAAkB,sBAAsB,QAAQ,kBAAkB,KAAK;CAEvE,MAAM,cAAc,QAAQ,WAAW;AACzC"}
1
+ {"version":3,"file":"load-config-files.mjs","names":[],"sources":["../../../../../../../core/src/config/load-config-files.ts"],"sourcesContent":["import { filesOrchestrator } from \"../dev-server/files-orchestrator\";\nimport { Path } from \"../utils/normalized-path\";\nimport { getCertainFilesFromDirectory, getFilesFromDirectory } from \"../dev-server/utils\";\nimport { srcPath } from \"../utils\";\nimport { configManager } from \"./config-manager\";\n\n/**\n * Load config files\n * either config file names (without extensions) or true to load all config files\n */\nexport async function loadConfigFiles(config: string[] | true) {\n let relativePaths: string[] = [];\n if (config === true) {\n const configFilesAbsulatePaths = await getFilesFromDirectory(srcPath(\"config\"));\n relativePaths = configFilesAbsulatePaths.map((path) => Path.toRelative(path));\n } else {\n // define only the given config files\n\n const configFilesAbsulatePaths = await getCertainFilesFromDirectory(srcPath(\"config\"), config);\n\n relativePaths = configFilesAbsulatePaths.map((path) => Path.toRelative(path));\n }\n\n const configFiles = await Promise.all(\n relativePaths.map((relativePath) => filesOrchestrator.add(relativePath)),\n );\n\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.files);\n\n await configManager.loadAll(configFiles);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,eAAsB,gBAAgB,QAAyB;CAC7D,IAAI,gBAA0B,CAAC;CAC/B,IAAI,WAAW,MAEb,iBAAgB,MADuB,sBAAsB,QAAQ,QAAQ,CAAC,GACrC,KAAK,SAAS,KAAK,WAAW,IAAI,CAAC;MAM5E,iBAAgB,MAFuB,6BAA6B,QAAQ,QAAQ,GAAG,MAAM,GAEpD,KAAK,SAAS,KAAK,WAAW,IAAI,CAAC;CAG9E,MAAM,cAAc,MAAM,QAAQ,IAChC,cAAc,KAAK,iBAAiB,kBAAkB,IAAI,YAAY,CAAC,CACzE;CAEA,kBAAkB,sBAAsB,QAAQ,kBAAkB,KAAK;CAEvE,MAAM,cAAc,QAAQ,WAAW;AACzC"}
@@ -1 +1 @@
1
- {"version":3,"file":"access-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/access-connector.ts"],"mappings":";;;;;;AAkBA;;;;;;;;;;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAAd;;;EAAA,mBAKG,YAAA;EAKN;;;EAAA,KAAA,IAAS,OAAA;EAoBU;AAAA;;;;EAAnB,QAAA,IAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"access-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/access-connector.ts"],"mappings":";;;;;;AAkBA;;;;;;;;;;;cAAa,eAAA,SAAwB,aAAA;EAAA,SACnB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,MAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAAd;;;EAAA,mBAKG,YAAA;EAKN;;;EAAA,KAAA,CAAA,GAAS,OAAA;EAoBU;AAAA;;;;EAAnB,QAAA,CAAA,GAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ai-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/ai-connector.ts"],"mappings":";;;;;;AAsBA;;;;;;;;;;;;;;;cAAa,WAAA,SAAoB,aAAA;EAAA,SACf,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,EAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAUjB;;;EAAA,mBALM,YAAA;EAuBa;AAAA;;EAlBnB,KAAA,IAAS,OAAA;;;;;;EAkBT,QAAA,IAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"ai-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/ai-connector.ts"],"mappings":";;;;;;AAsBA;;;;;;;;;;;;;;;cAAa,WAAA,SAAoB,aAAA;EAAA,SACf,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,EAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EAUjB;;;EAAA,mBALM,YAAA;EAuBa;AAAA;;EAlBnB,KAAA,CAAA,GAAS,OAAA;;;;;;EAkBT,QAAA,CAAA,GAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"assert-no-reserved-connector-names.mjs","names":[],"sources":["../../../../../../../core/src/connectors/assert-no-reserved-connector-names.ts"],"sourcesContent":["/**\n * Refuses a `connectors` array that claims a name a framework built-in owns.\n *\n * The uniqueness check rejects a name repeated inside the array; this rejects\n * one already held by a BUILT-IN outside it. The manager registers its\n * built-ins in its own constructor, so those names are taken before any config\n * is read — and the two halves disagreed about what that meant. The build\n * drains per ENTRY and knows nothing of the manager, so it prepared the custom\n * connector's artifacts; the runtime registers per NAME and found the built-in\n * already there, so it skipped the entry. The result booted the built-in the\n * app did not ask for, never booted the connector it did, and reported success\n * from both halves.\n *\n * Refused rather than resolved in the app's favour: the built-ins are the\n * framework's own wiring — other framework code reaches for `http`, `cache`,\n * `database` by name — so letting a config replace one silently repoints\n * everything that looks it up. The name is the app's to change; the built-in's\n * is not.\n *\n * Scope, precisely: BUILT-IN names only. A name held by a PREVIOUSLY\n * CONFIGURED connector (an earlier call in the same process) is not this\n * check's business — that case is the registration skip's deliberate\n * idempotency, and stays out of both validators.\n *\n * Reads {@link ConnectorsManager.isBuiltInName}, which is the ONLY inventory of\n * built-in names, so this cannot fall out of step with what the manager\n * actually registered.\n */\nimport { connectorsManager } from \"./connectors-manager\";\nimport type { Connector } from \"./types\";\n\nexport function assertNoReservedConnectorNames(connectors: Connector[]): void {\n const reserved = connectors\n .map((connector) => connector.name)\n .filter((name) => connectorsManager.isBuiltInName(name));\n\n if (reserved.length === 0) return;\n\n const unique = [...new Set(reserved)];\n const names = unique.map((name) => JSON.stringify(name)).join(\", \");\n const subject = unique.length === 1 ? \"name is\" : \"names are\";\n const object = unique.length === 1 ? \"it\" : \"one of them\";\n\n throw new Error(\n `The following connector ${subject} reserved by a framework built-in connector: [${names}].\\n` +\n \"Every connector name must be unique, and the framework already registers a built-in connector under \" +\n `${unique.length === 1 ? \"that name\" : \"those names\"}, so a custom connector carrying ${object} ` +\n \"would be built for and then never booted. \" +\n \"Rename the custom connector in `warlock.config.ts > connectors` to a name of your own.\",\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,+BAA+B,YAA+B;CAC5E,MAAM,WAAW,WACd,KAAK,cAAc,UAAU,IAAI,CAAC,CAClC,QAAQ,SAAS,kBAAkB,cAAc,IAAI,CAAC;CAEzD,IAAI,SAAS,WAAW,GAAG;CAE3B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;CACpC,MAAM,QAAQ,OAAO,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;CAClE,MAAM,UAAU,OAAO,WAAW,IAAI,YAAY;CAClD,MAAM,SAAS,OAAO,WAAW,IAAI,OAAO;CAE5C,MAAM,IAAI,MACR,2BAA2B,QAAQ,gDAAgD,MAAM,0GAEpF,OAAO,WAAW,IAAI,cAAc,cAAc,mCAAmC,OAAO,oIAGnG;AACF"}
1
+ {"version":3,"file":"assert-no-reserved-connector-names.mjs","names":[],"sources":["../../../../../../../core/src/connectors/assert-no-reserved-connector-names.ts"],"sourcesContent":["/**\n * Refuses a `connectors` array that claims a name a framework built-in owns.\n *\n * The uniqueness check rejects a name repeated inside the array; this rejects\n * one already held by a BUILT-IN outside it. The manager registers its\n * built-ins in its own constructor, so those names are taken before any config\n * is read — and the two halves disagreed about what that meant. The build\n * drains per ENTRY and knows nothing of the manager, so it prepared the custom\n * connector's artifacts; the runtime registers per NAME and found the built-in\n * already there, so it skipped the entry. The result booted the built-in the\n * app did not ask for, never booted the connector it did, and reported success\n * from both halves.\n *\n * Refused rather than resolved in the app's favour: the built-ins are the\n * framework's own wiring — other framework code reaches for `http`, `cache`,\n * `database` by name — so letting a config replace one silently repoints\n * everything that looks it up. The name is the app's to change; the built-in's\n * is not.\n *\n * Scope, precisely: BUILT-IN names only. A name held by a PREVIOUSLY\n * CONFIGURED connector (an earlier call in the same process) is not this\n * check's business — that case is the registration skip's deliberate\n * idempotency, and stays out of both validators.\n *\n * Reads {@link ConnectorsManager.isBuiltInName}, which is the ONLY inventory of\n * built-in names, so this cannot fall out of step with what the manager\n * actually registered.\n */\nimport { connectorsManager } from \"./connectors-manager\";\nimport type { Connector } from \"./types\";\n\nexport function assertNoReservedConnectorNames(connectors: Connector[]): void {\n const reserved = connectors\n .map((connector) => connector.name)\n .filter((name) => connectorsManager.isBuiltInName(name));\n\n if (reserved.length === 0) return;\n\n const unique = [...new Set(reserved)];\n const names = unique.map((name) => JSON.stringify(name)).join(\", \");\n const subject = unique.length === 1 ? \"name is\" : \"names are\";\n const object = unique.length === 1 ? \"it\" : \"one of them\";\n\n throw new Error(\n `The following connector ${subject} reserved by a framework built-in connector: [${names}].\\n` +\n \"Every connector name must be unique, and the framework already registers a built-in connector under \" +\n `${unique.length === 1 ? \"that name\" : \"those names\"}, so a custom connector carrying ${object} ` +\n \"would be built for and then never booted. \" +\n \"Rename the custom connector in `warlock.config.ts > connectors` to a name of your own.\",\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,+BAA+B,YAA+B;CAC5E,MAAM,WAAW,WACd,KAAK,cAAc,UAAU,IAAI,EACjC,QAAQ,SAAS,kBAAkB,cAAc,IAAI,CAAC;CAEzD,IAAI,SAAS,WAAW,GAAG;CAE3B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;CACpC,MAAM,QAAQ,OAAO,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK,IAAI;CAClE,MAAM,UAAU,OAAO,WAAW,IAAI,YAAY;CAClD,MAAM,SAAS,OAAO,WAAW,IAAI,OAAO;CAE5C,MAAM,IAAI,MACR,2BAA2B,QAAQ,gDAAgD,MAAM,0GAEpF,OAAO,WAAW,IAAI,cAAc,cAAc,mCAAmC,OAAO,oIAGnG;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"assert-unique-connector-names.mjs","names":[],"sources":["../../../../../../../core/src/connectors/assert-unique-connector-names.ts"],"sourcesContent":["/**\n * Refuses a `connectors` array that lists the same name more than once.\n *\n * A name is a connector's identity across both halves of its life, and the two\n * halves treat a repeat differently. The build works per ARRAY ENTRY: it drains\n * the build contribution of every element, so a name listed twice contributes\n * twice — files written twice, esbuild patches merged twice. The runtime works\n * per NAME: the first entry registers and every later one with that name is\n * skipped, because registering it again would boot and shut down the same\n * connector twice.\n *\n * So a duplicate is half-applied: built for twice, booted once, with nothing\n * reported either way. The name-set the drift check compares is identical on\n * both sides, so it passes the array straight through — this asymmetry is\n * invisible to it, which is why it is caught here instead.\n *\n * Called by both halves (the build before it drains, the registration before it\n * registers) so neither can be reached with an array the other would refuse.\n * It rejects duplicates WITHIN the array it is handed. A name held OUTSIDE the\n * array is out of scope here: if a BUILT-IN holds it, the reserved-name check\n * refuses it; if an EARLIER CALL configured it, the registration skip treats it\n * as idempotency on purpose — neither validator claims that case.\n */\nimport type { Connector } from \"./types\";\n\nexport function assertUniqueConnectorNames(connectors: Connector[]): void {\n const seen = new Set<string>();\n const duplicated = new Set<string>();\n\n for (const connector of connectors) {\n if (seen.has(connector.name)) {\n duplicated.add(connector.name);\n continue;\n }\n\n seen.add(connector.name);\n }\n\n if (duplicated.size === 0) return;\n\n const names = [...duplicated].map((name) => JSON.stringify(name)).join(\", \");\n const subject = duplicated.size === 1 ? \"name is\" : \"names are\";\n\n throw new Error(\n `The same connector ${subject} listed more than once in \\`connectors\\`: [${names}].\\n` +\n \"Every connector in `warlock.config.ts > connectors` must have a unique name. \" +\n \"The build prepares artifacts per ENTRY — it drains the build contribution of each one, so a repeated name contributes twice — \" +\n \"while the runtime registers each NAME once and skips the rest. \" +\n \"A duplicate is therefore half-applied, and neither half reports it. \" +\n \"Remove the extra entry, or give the connectors distinct names.\",\n );\n}\n"],"mappings":";AAyBA,SAAgB,2BAA2B,YAA+B;CACxE,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,6BAAa,IAAI,IAAY;CAEnC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,KAAK,IAAI,UAAU,IAAI,GAAG;GAC5B,WAAW,IAAI,UAAU,IAAI;GAC7B;EACF;EAEA,KAAK,IAAI,UAAU,IAAI;CACzB;CAEA,IAAI,WAAW,SAAS,GAAG;CAE3B,MAAM,QAAQ,CAAC,GAAG,UAAU,CAAC,CAAC,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;CAC3E,MAAM,UAAU,WAAW,SAAS,IAAI,YAAY;CAEpD,MAAM,IAAI,MACR,sBAAsB,QAAQ,6CAA6C,MAAM,mZAMnF;AACF"}
1
+ {"version":3,"file":"assert-unique-connector-names.mjs","names":[],"sources":["../../../../../../../core/src/connectors/assert-unique-connector-names.ts"],"sourcesContent":["/**\n * Refuses a `connectors` array that lists the same name more than once.\n *\n * A name is a connector's identity across both halves of its life, and the two\n * halves treat a repeat differently. The build works per ARRAY ENTRY: it drains\n * the build contribution of every element, so a name listed twice contributes\n * twice — files written twice, esbuild patches merged twice. The runtime works\n * per NAME: the first entry registers and every later one with that name is\n * skipped, because registering it again would boot and shut down the same\n * connector twice.\n *\n * So a duplicate is half-applied: built for twice, booted once, with nothing\n * reported either way. The name-set the drift check compares is identical on\n * both sides, so it passes the array straight through — this asymmetry is\n * invisible to it, which is why it is caught here instead.\n *\n * Called by both halves (the build before it drains, the registration before it\n * registers) so neither can be reached with an array the other would refuse.\n * It rejects duplicates WITHIN the array it is handed. A name held OUTSIDE the\n * array is out of scope here: if a BUILT-IN holds it, the reserved-name check\n * refuses it; if an EARLIER CALL configured it, the registration skip treats it\n * as idempotency on purpose — neither validator claims that case.\n */\nimport type { Connector } from \"./types\";\n\nexport function assertUniqueConnectorNames(connectors: Connector[]): void {\n const seen = new Set<string>();\n const duplicated = new Set<string>();\n\n for (const connector of connectors) {\n if (seen.has(connector.name)) {\n duplicated.add(connector.name);\n continue;\n }\n\n seen.add(connector.name);\n }\n\n if (duplicated.size === 0) return;\n\n const names = [...duplicated].map((name) => JSON.stringify(name)).join(\", \");\n const subject = duplicated.size === 1 ? \"name is\" : \"names are\";\n\n throw new Error(\n `The same connector ${subject} listed more than once in \\`connectors\\`: [${names}].\\n` +\n \"Every connector in `warlock.config.ts > connectors` must have a unique name. \" +\n \"The build prepares artifacts per ENTRY — it drains the build contribution of each one, so a repeated name contributes twice — \" +\n \"while the runtime registers each NAME once and skips the rest. \" +\n \"A duplicate is therefore half-applied, and neither half reports it. \" +\n \"Remove the extra entry, or give the connectors distinct names.\",\n );\n}\n"],"mappings":";AAyBA,SAAgB,2BAA2B,YAA+B;CACxE,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,6BAAa,IAAI,IAAY;CAEnC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,KAAK,IAAI,UAAU,IAAI,GAAG;GAC5B,WAAW,IAAI,UAAU,IAAI;GAC7B;EACF;EAEA,KAAK,IAAI,UAAU,IAAI;CACzB;CAEA,IAAI,WAAW,SAAS,GAAG;CAE3B,MAAM,QAAQ,CAAC,GAAG,UAAU,EAAE,KAAK,SAAS,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK,IAAI;CAC3E,MAAM,UAAU,WAAW,SAAS,IAAI,YAAY;CAEpD,MAAM,IAAI,MACR,sBAAsB,QAAQ,6CAA6C,MAAM,mZAMnF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/base-connector.ts"],"mappings":";;;;AAQA;;;uBAAsB,aAAA,YAAyB,SAAA;EAgBb;;;EAAA,kBAZP,IAAA,EAAM,aAAA;EAuDH;;;EAAA,kBAlDH,QAAA;EAToB;;;;;EAAA,SAgB7B,cAAA,EAAgB,uBAAA;EAMJ;;;;EAAA,4BAAA,YAAA;EAwBZ;;;EAAA,UAnBN,MAAA;EAgCM;;;EA3BT,QAAA;EAwCG;;;EAjCG,IAAA,IAAQ,OAAA;;;;WAOL,KAAA,IAAS,OAAA;;;;EAKZ,OAAA,IAAW,OAAA;;;;WAQR,QAAA,IAAY,OAAA;;;;EAKrB,aAAA,CAAc,YAAA;;;;YAQX,aAAA,CAAc,IAAA;AAAA"}
1
+ {"version":3,"file":"base-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/base-connector.ts"],"mappings":";;;;AAQA;;;uBAAsB,aAAA,YAAyB,SAAA;EAgBb;;;EAAA,kBAZP,IAAA,EAAM,aAAA;EAuDH;;;EAAA,kBAlDH,QAAA;EAToB;;;;;EAAA,SAgB7B,cAAA,EAAgB,uBAAA;EAMJ;;;;EAAA,4BAAA,YAAA;EAwBZ;;;EAAA,UAnBN,MAAA;EAgCM;;;EA3BT,QAAA,CAAA;EAwCG;;;EAjCG,IAAA,CAAA,GAAQ,OAAA;;;;WAOL,KAAA,CAAA,GAAS,OAAA;;;;EAKZ,OAAA,CAAA,GAAW,OAAA;;;;WAQR,QAAA,CAAA,GAAY,OAAA;;;;EAKrB,aAAA,CAAc,YAAA;;;;YAQX,aAAA,CAAc,IAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/base-connector.ts"],"sourcesContent":["import { Path } from \"../utils/normalized-path\";\nimport { ConnectorLifecyclePhase } from \"./types\";\nimport type { Connector, ConnectorName } from \"./types\";\n\n/**\n * Base Connector Class\n * Provides common functionality for all connectors\n */\nexport abstract class BaseConnector implements Connector {\n /**\n * Connector name\n */\n public abstract readonly name: ConnectorName;\n\n /**\n * Initialization priority\n */\n public abstract readonly priority: number;\n\n /**\n * Lifecycle phase. Defaults to `Early`; override to `Late` if the\n * connector consumes state user code registers at import time\n * (routes, socket listeners).\n */\n public readonly lifecyclePhase: ConnectorLifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger restart when changed\n * Use relative paths\n */\n protected abstract readonly watchedFiles: string[];\n\n /**\n * Whether the connector is currently active\n */\n protected active: boolean = false;\n\n /**\n * Check if connector is active\n */\n public isActive(): boolean {\n return this.active;\n }\n\n /**\n * Boot the connector\n */\n public async boot(): Promise<void> {\n return;\n }\n\n /**\n * Initialize the connector\n */\n public abstract start(): Promise<void>;\n\n /**\n * Restart the connector\n */\n public async restart(): Promise<void> {\n await this.shutdown();\n await this.start();\n }\n\n /**\n * Shutdown the connector\n */\n public abstract shutdown(): Promise<void>;\n\n /**\n * Determine if connector should restart based on changed files\n */\n public shouldRestart(changedFiles: string[]): boolean {\n // Check if any changed file matches watched files\n return changedFiles.some((file) => this.isWatchedFile(file));\n }\n\n /**\n * Check if a file is watched by this connector\n */\n protected isWatchedFile(file: string): boolean {\n const relativePath = Path.toRelative(file);\n\n return this.watchedFiles.some((watchedFile) => {\n // Exact match\n if (watchedFile === relativePath) {\n return true;\n }\n\n // Pattern match (e.g., \"config/*.ts\")\n if (watchedFile.includes(\"*\")) {\n const pattern = new RegExp(\"^\" + watchedFile.replace(/\\*/g, \".*\") + \"$\");\n return pattern.test(relativePath);\n }\n\n return false;\n });\n }\n}\n"],"mappings":";;;;;;;;AAQA,IAAsB,gBAAtB,MAAyD;;;gBA2B3B;;;;;CAK5B,AAAO,WAAoB;EACzB,OAAO,KAAK;CACd;;;;CAKA,MAAa,OAAsB,CAEnC;;;;CAUA,MAAa,UAAyB;EACpC,MAAM,KAAK,SAAS;EACpB,MAAM,KAAK,MAAM;CACnB;;;;CAUA,AAAO,cAAc,cAAiC;EAEpD,OAAO,aAAa,MAAM,SAAS,KAAK,cAAc,IAAI,CAAC;CAC7D;;;;CAKA,AAAU,cAAc,MAAuB;EAC7C,MAAM,eAAe,KAAK,WAAW,IAAI;EAEzC,OAAO,KAAK,aAAa,MAAM,gBAAgB;GAE7C,IAAI,gBAAgB,cAClB,OAAO;GAIT,IAAI,YAAY,SAAS,GAAG,GAE1B,OAAO,IADa,OAAO,MAAM,YAAY,QAAQ,OAAO,IAAI,IAAI,GACvD,CAAC,CAAC,KAAK,YAAY;GAGlC,OAAO;EACT,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"base-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/base-connector.ts"],"sourcesContent":["import { Path } from \"../utils/normalized-path\";\nimport { ConnectorLifecyclePhase } from \"./types\";\nimport type { Connector, ConnectorName } from \"./types\";\n\n/**\n * Base Connector Class\n * Provides common functionality for all connectors\n */\nexport abstract class BaseConnector implements Connector {\n /**\n * Connector name\n */\n public abstract readonly name: ConnectorName;\n\n /**\n * Initialization priority\n */\n public abstract readonly priority: number;\n\n /**\n * Lifecycle phase. Defaults to `Early`; override to `Late` if the\n * connector consumes state user code registers at import time\n * (routes, socket listeners).\n */\n public readonly lifecyclePhase: ConnectorLifecyclePhase = ConnectorLifecyclePhase.Early;\n\n /**\n * Files that trigger restart when changed\n * Use relative paths\n */\n protected abstract readonly watchedFiles: string[];\n\n /**\n * Whether the connector is currently active\n */\n protected active: boolean = false;\n\n /**\n * Check if connector is active\n */\n public isActive(): boolean {\n return this.active;\n }\n\n /**\n * Boot the connector\n */\n public async boot(): Promise<void> {\n return;\n }\n\n /**\n * Initialize the connector\n */\n public abstract start(): Promise<void>;\n\n /**\n * Restart the connector\n */\n public async restart(): Promise<void> {\n await this.shutdown();\n await this.start();\n }\n\n /**\n * Shutdown the connector\n */\n public abstract shutdown(): Promise<void>;\n\n /**\n * Determine if connector should restart based on changed files\n */\n public shouldRestart(changedFiles: string[]): boolean {\n // Check if any changed file matches watched files\n return changedFiles.some((file) => this.isWatchedFile(file));\n }\n\n /**\n * Check if a file is watched by this connector\n */\n protected isWatchedFile(file: string): boolean {\n const relativePath = Path.toRelative(file);\n\n return this.watchedFiles.some((watchedFile) => {\n // Exact match\n if (watchedFile === relativePath) {\n return true;\n }\n\n // Pattern match (e.g., \"config/*.ts\")\n if (watchedFile.includes(\"*\")) {\n const pattern = new RegExp(\"^\" + watchedFile.replace(/\\*/g, \".*\") + \"$\");\n return pattern.test(relativePath);\n }\n\n return false;\n });\n }\n}\n"],"mappings":";;;;;;;;AAQA,IAAsB,gBAAtB,MAAyD;;;gBA2B3B;;;;;CAK5B,AAAO,WAAoB;EACzB,OAAO,KAAK;CACd;;;;CAKA,MAAa,OAAsB,CAEnC;;;;CAUA,MAAa,UAAyB;EACpC,MAAM,KAAK,SAAS;EACpB,MAAM,KAAK,MAAM;CACnB;;;;CAUA,AAAO,cAAc,cAAiC;EAEpD,OAAO,aAAa,MAAM,SAAS,KAAK,cAAc,IAAI,CAAC;CAC7D;;;;CAKA,AAAU,cAAc,MAAuB;EAC7C,MAAM,eAAe,KAAK,WAAW,IAAI;EAEzC,OAAO,KAAK,aAAa,MAAM,gBAAgB;GAE7C,IAAI,gBAAgB,cAClB,OAAO;GAIT,IAAI,YAAY,SAAS,GAAG,GAE1B,OAAO,IADa,OAAO,MAAM,YAAY,QAAQ,OAAO,IAAI,IAAI,GACvD,EAAE,KAAK,YAAY;GAGlC,OAAO;EACT,CAAC;CACH;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/cache-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,cAAA,SAAuB,aAAA;EAAA,SAClB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,KAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA8BL;;;EAAA,mBAzBN,YAAA;EARe;;;EAarB,KAAA,IAAS,OAAA;EAXE;;;;;;;;EA+BX,QAAA,IAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"cache-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/cache-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,cAAA,SAAuB,aAAA;EAAA,SAClB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,KAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA8BL;;;EAAA,mBAzBN,YAAA;EARe;;;EAarB,KAAA,CAAA,GAAS,OAAA;EAXE;;;;;;;;EA+BX,QAAA,CAAA,GAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"connectors-manager.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/connectors-manager.ts"],"mappings":";;;cAgBa,iBAAA;EAAA;;;EAAA,iBAIM,UAAA;EAwEF;;;;;;;;;;;;EAAA,iBA1DE,YAAA;;;;;EAiDV;;;;;;;EAAA,QAvBC,eAAA;EAkDK;;;;;;;;EAlCN,aAAA,CAAc,IAAA;EA0EyB;;;EAnEvC,QAAA,IAAY,UAAA,EAAY,SAAA;EA8EN;;;EArElB,IAAA,IAAQ,SAAA;EAmJJ;;;;AAA2C;;;EAxI/C,GAAA,CAAI,IAAA,EAAM,aAAA;;;;EAOJ,KAAA,CAAM,eAAA,GAAkB,aAAA,KAAkB,OAAA;;;;;;;;;;EAuB1C,UAAA,CAAW,KAAA,EAAO,uBAAA,GAA0B,OAAA;;;;EAiB5C,YAAA,CAAa,kBAAA,EAAoB,aAAA,KAAkB,OAAA;;;;EAWnD,QAAA,IAAY,OAAA;;;;;;;;;;;;;EAqDlB,qBAAA;AAAA;AAAA,cAyBI,iBAAA,EAAiB,iBAA0B"}
1
+ {"version":3,"file":"connectors-manager.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/connectors-manager.ts"],"mappings":";;;cAgBa,iBAAA;EAAA;;;EAAA,iBAIM,UAAA;EAwEF;;;;;;;;;;;;EAAA,iBA1DE,YAAA;;;;;EAiDV;;;;;;;EAAA,QAvBC,eAAA;EAkDK;;;;;;;;EAlCN,aAAA,CAAc,IAAA;EA0EyB;;;EAnEvC,QAAA,CAAA,GAAY,UAAA,EAAY,SAAA;EA8EN;;;EArElB,IAAA,CAAA,GAAQ,SAAA;EAmJJ;;;;AAA2C;;;EAxI/C,GAAA,CAAI,IAAA,EAAM,aAAA;;;;EAOJ,KAAA,CAAM,eAAA,GAAkB,aAAA,KAAkB,OAAA;;;;;;;;;;EAuB1C,UAAA,CAAW,KAAA,EAAO,uBAAA,GAA0B,OAAA;;;;EAiB5C,YAAA,CAAa,kBAAA,EAAoB,aAAA,KAAkB,OAAA;;;;EAWnD,QAAA,CAAA,GAAY,OAAA;;;;;;;;;;;;;EAqDlB,qBAAA,CAAA;AAAA;AAAA,cAyBI,iBAAA,EAAiB,iBAA0B"}
@@ -1 +1 @@
1
- {"version":3,"file":"connectors-manager.mjs","names":[],"sources":["../../../../../../../core/src/connectors/connectors-manager.ts"],"sourcesContent":["import { Application } from \"../application\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { AccessConnector } from \"./access-connector\";\r\nimport { AiConnector } from \"./ai-connector\";\r\nimport { CacheConnector } from \"./cache-connector\";\r\nimport { DatabaseConnector } from \"./database-connector\";\r\nimport { HeraldConnector } from \"./herald-connector\";\r\nimport { HttpConnector } from \"./http-connector\";\r\nimport { LoggerConnector } from \"./logger-connector\";\r\nimport { MailerConnector } from \"./mail-connector\";\r\nimport { NotificationsConnector } from \"./notifications-connector\";\r\nimport { SocketConnector } from \"./socket-connector\";\r\nimport { StorageConnector } from \"./storage.connector\";\r\nimport { ConnectorLifecyclePhase } from \"./types\";\r\nimport type { Connector, ConnectorName } from \"./types\";\r\n\r\nexport class ConnectorsManager {\r\n /**\r\n * Connectors list\r\n */\r\n private readonly connectors: Connector[] = [];\r\n\r\n /**\r\n * The names the framework's own connectors hold.\r\n *\r\n * Filled by the constructor's own registration calls rather than declared\r\n * beside them, so there is exactly ONE place a built-in name exists: the\r\n * connector that owns it. A second list — in the builder, in a config\r\n * validator, in a test double — is a list that drifts the moment a built-in\r\n * is added or renamed, and it drifts silently, because nothing compares the\r\n * two. Both halves of a connector's life read this one through\r\n * {@link isBuiltInName}: the build refuses a `connectors` array that claims\r\n * a built-in name, and so does registration.\r\n */\r\n private readonly builtInNames = new Set<string>();\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor() {\r\n this.registerBuiltIn(new LoggerConnector());\r\n this.registerBuiltIn(new MailerConnector());\r\n this.registerBuiltIn(new HttpConnector());\r\n this.registerBuiltIn(new DatabaseConnector());\r\n this.registerBuiltIn(new HeraldConnector());\r\n this.registerBuiltIn(new CacheConnector());\r\n this.registerBuiltIn(new StorageConnector());\r\n this.registerBuiltIn(new SocketConnector());\r\n this.registerBuiltIn(new NotificationsConnector());\r\n this.registerBuiltIn(new AccessConnector());\r\n this.registerBuiltIn(new AiConnector());\r\n }\r\n\r\n /**\r\n * Register a framework-owned connector and reserve its name.\r\n *\r\n * Private on purpose: an app connector must not be able to reserve a name,\r\n * and the set is only correct if every entry in it came from a connector\r\n * this class constructed itself.\r\n */\r\n private registerBuiltIn(...connectors: Connector[]): void {\r\n for (const connector of connectors) {\r\n this.builtInNames.add(connector.name);\r\n }\r\n\r\n this.register(...connectors);\r\n }\r\n\r\n /**\r\n * Whether the given name belongs to a framework built-in connector.\r\n *\r\n * Reserved, not merely taken: a configured connector carrying one of these\r\n * names would be skipped by {@link has} at boot while the build had already\r\n * drained its contribution, so the artifact would carry a connector the\r\n * server never runs. Callers refuse the name instead of skipping it.\r\n */\r\n public isBuiltInName(name: string): boolean {\r\n return this.builtInNames.has(name);\r\n }\r\n\r\n /**\r\n * Register a connector\r\n */\r\n public register(...connectors: Connector[]): void {\r\n this.connectors.push(...connectors);\r\n // sort connectors by priority\r\n this.connectors.sort((a, b) => a.priority - b.priority);\r\n }\r\n\r\n /**\r\n * Get all connectors\r\n */\r\n public list(): Connector[] {\r\n return this.connectors;\r\n }\r\n\r\n /**\r\n * Whether a connector with the given name is already registered.\r\n *\r\n * Names are the identity of a connector here, which is what lets a caller\r\n * that may run more than once per process register idempotently instead of\r\n * stacking duplicates that would each boot and shut down.\r\n */\r\n public has(name: ConnectorName): boolean {\r\n return this.connectors.some((connector) => connector.name === name);\r\n }\r\n\r\n /**\r\n * start all connectors\r\n */\r\n public async start(connectorsNames?: ConnectorName[]): Promise<void> {\r\n const connectorsList = connectorsNames\r\n ? this.connectors.filter((connector) => connectorsNames.includes(connector.name))\r\n : this.connectors;\r\n\r\n for (const connector of connectorsList) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of connectorsList) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors in the given lifecycle phase.\r\n *\r\n * The production builder and dev preload split startup around app\r\n * code: early phase before app imports, late phase after. Within a\r\n * phase, all connectors `boot()` first, then all `start()`, so\r\n * cross-connector wiring inside the phase still works (e.g. socket\r\n * reads http's instance during its own boot).\r\n */\r\n public async startPhase(phase: ConnectorLifecyclePhase): Promise<void> {\r\n const phaseConnectors = this.connectors.filter(\r\n (connector) => connector.lifecyclePhase === phase,\r\n );\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors except the given ones\r\n */\r\n public async startWithout(excludedConnectors: ConnectorName[]): Promise<void> {\r\n await this.start(\r\n this.connectors\r\n .filter((connector) => !excludedConnectors.includes(connector.name))\r\n .map((connector) => connector.name),\r\n );\r\n }\r\n\r\n /**\r\n * Shutdown all connectors\r\n */\r\n public async shutdown(): Promise<void> {\r\n // App-owned teardown runs first, while every connector (db, cache, http) is\r\n // still up, so cleanup hooks can use them. Idempotent + error-isolated.\r\n await Application.runShutdownHooks();\r\n\r\n // Shut down connectors in reverse priority order. Copy the list first —\r\n // `reverse()` mutates in place, so reversing the live array would corrupt\r\n // the order on a second shutdown pass.\r\n for (const connector of [...this.connectors].reverse()) {\r\n try {\r\n await connector.shutdown();\r\n } catch (error) {\r\n try {\r\n // Awaited deliberately: the caller (`shutdownOnProcessKill`) calls\r\n // `process.exit(0)` as soon as this resolves, so an un-awaited log is\r\n // a log that never happens.\r\n await log.error(\"connectors\", \"shutdown\", error as Error, { connector: connector.name });\r\n } catch {\r\n // Reporting a failure must never become a worse failure than the one\r\n // being reported. `Logger.log()` fans out to channels without\r\n // isolating them, so a channel that throws synchronously rejects this\r\n // call — and this call sits INSIDE the catch, so that rejection\r\n // escapes `shutdown()` entirely: the remaining connectors are never\r\n // torn down and `process.exit(0)` never runs, leaving the process\r\n // alive on the handles they still hold.\r\n //\r\n // Swallowed rather than re-reported: the only channel we could report\r\n // it through is the one that just threw.\r\n }\r\n }\r\n }\r\n\r\n // `Logger.log()` hands each entry to `channel.log()` without awaiting it, so\r\n // awaiting the calls above is not enough on its own — a buffered or async\r\n // channel (file, Sentry) still loses the entry when the process exits.\r\n // Drained once here rather than per failure: shutdown is exactly when\r\n // buffered logs must reach their destination, and re-draining every channel\r\n // once per failed connector buys nothing.\r\n await log.flush();\r\n }\r\n\r\n /**\r\n * Shutdown connectors on process kill\r\n *\r\n * Registers signal handlers that run an idempotent graceful shutdown:\r\n * - SIGINT: Ctrl+C on Unix, also caught on Windows but unreliable in child processes\r\n * - SIGTERM: Termination signal (Unix primarily)\r\n * - SIGHUP: registered on Windows (`win32`) only, to catch Ctrl+C / console close\r\n *\r\n * Note: no `beforeExit` handler is registered — `beforeExit` cannot reliably\r\n * await async teardown (the process exits once the callback returns), so\r\n * shutdown is driven entirely by the signal handlers above.\r\n */\r\n public shutdownOnProcessKill(): void {\r\n let isShuttingDown = false;\r\n\r\n const gracefulShutdown = async (signal: string) => {\r\n if (isShuttingDown) return;\r\n isShuttingDown = true;\r\n\r\n console.log(`\\nExiting...`);\r\n await this.shutdown();\r\n process.exit(0);\r\n };\r\n\r\n // Unix signals\r\n process.on(\"SIGINT\", () => gracefulShutdown(\"SIGINT\"));\r\n process.on(\"SIGTERM\", () => gracefulShutdown(\"SIGTERM\"));\r\n\r\n // Windows-specific: handle when process is about to exit\r\n // This is more reliable on Windows for spawned child processes\r\n if (process.platform === \"win32\") {\r\n // Handle Ctrl+C on Windows specifically\r\n process.on(\"SIGHUP\", () => gracefulShutdown(\"SIGHUP\"));\r\n }\r\n }\r\n}\r\n\r\nexport const connectorsManager = new ConnectorsManager();\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"connectors-manager.mjs","names":[],"sources":["../../../../../../../core/src/connectors/connectors-manager.ts"],"sourcesContent":["import { Application } from \"../application\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { AccessConnector } from \"./access-connector\";\r\nimport { AiConnector } from \"./ai-connector\";\r\nimport { CacheConnector } from \"./cache-connector\";\r\nimport { DatabaseConnector } from \"./database-connector\";\r\nimport { HeraldConnector } from \"./herald-connector\";\r\nimport { HttpConnector } from \"./http-connector\";\r\nimport { LoggerConnector } from \"./logger-connector\";\r\nimport { MailerConnector } from \"./mail-connector\";\r\nimport { NotificationsConnector } from \"./notifications-connector\";\r\nimport { SocketConnector } from \"./socket-connector\";\r\nimport { StorageConnector } from \"./storage.connector\";\r\nimport { ConnectorLifecyclePhase } from \"./types\";\r\nimport type { Connector, ConnectorName } from \"./types\";\r\n\r\nexport class ConnectorsManager {\r\n /**\r\n * Connectors list\r\n */\r\n private readonly connectors: Connector[] = [];\r\n\r\n /**\r\n * The names the framework's own connectors hold.\r\n *\r\n * Filled by the constructor's own registration calls rather than declared\r\n * beside them, so there is exactly ONE place a built-in name exists: the\r\n * connector that owns it. A second list — in the builder, in a config\r\n * validator, in a test double — is a list that drifts the moment a built-in\r\n * is added or renamed, and it drifts silently, because nothing compares the\r\n * two. Both halves of a connector's life read this one through\r\n * {@link isBuiltInName}: the build refuses a `connectors` array that claims\r\n * a built-in name, and so does registration.\r\n */\r\n private readonly builtInNames = new Set<string>();\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor() {\r\n this.registerBuiltIn(new LoggerConnector());\r\n this.registerBuiltIn(new MailerConnector());\r\n this.registerBuiltIn(new HttpConnector());\r\n this.registerBuiltIn(new DatabaseConnector());\r\n this.registerBuiltIn(new HeraldConnector());\r\n this.registerBuiltIn(new CacheConnector());\r\n this.registerBuiltIn(new StorageConnector());\r\n this.registerBuiltIn(new SocketConnector());\r\n this.registerBuiltIn(new NotificationsConnector());\r\n this.registerBuiltIn(new AccessConnector());\r\n this.registerBuiltIn(new AiConnector());\r\n }\r\n\r\n /**\r\n * Register a framework-owned connector and reserve its name.\r\n *\r\n * Private on purpose: an app connector must not be able to reserve a name,\r\n * and the set is only correct if every entry in it came from a connector\r\n * this class constructed itself.\r\n */\r\n private registerBuiltIn(...connectors: Connector[]): void {\r\n for (const connector of connectors) {\r\n this.builtInNames.add(connector.name);\r\n }\r\n\r\n this.register(...connectors);\r\n }\r\n\r\n /**\r\n * Whether the given name belongs to a framework built-in connector.\r\n *\r\n * Reserved, not merely taken: a configured connector carrying one of these\r\n * names would be skipped by {@link has} at boot while the build had already\r\n * drained its contribution, so the artifact would carry a connector the\r\n * server never runs. Callers refuse the name instead of skipping it.\r\n */\r\n public isBuiltInName(name: string): boolean {\r\n return this.builtInNames.has(name);\r\n }\r\n\r\n /**\r\n * Register a connector\r\n */\r\n public register(...connectors: Connector[]): void {\r\n this.connectors.push(...connectors);\r\n // sort connectors by priority\r\n this.connectors.sort((a, b) => a.priority - b.priority);\r\n }\r\n\r\n /**\r\n * Get all connectors\r\n */\r\n public list(): Connector[] {\r\n return this.connectors;\r\n }\r\n\r\n /**\r\n * Whether a connector with the given name is already registered.\r\n *\r\n * Names are the identity of a connector here, which is what lets a caller\r\n * that may run more than once per process register idempotently instead of\r\n * stacking duplicates that would each boot and shut down.\r\n */\r\n public has(name: ConnectorName): boolean {\r\n return this.connectors.some((connector) => connector.name === name);\r\n }\r\n\r\n /**\r\n * start all connectors\r\n */\r\n public async start(connectorsNames?: ConnectorName[]): Promise<void> {\r\n const connectorsList = connectorsNames\r\n ? this.connectors.filter((connector) => connectorsNames.includes(connector.name))\r\n : this.connectors;\r\n\r\n for (const connector of connectorsList) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of connectorsList) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors in the given lifecycle phase.\r\n *\r\n * The production builder and dev preload split startup around app\r\n * code: early phase before app imports, late phase after. Within a\r\n * phase, all connectors `boot()` first, then all `start()`, so\r\n * cross-connector wiring inside the phase still works (e.g. socket\r\n * reads http's instance during its own boot).\r\n */\r\n public async startPhase(phase: ConnectorLifecyclePhase): Promise<void> {\r\n const phaseConnectors = this.connectors.filter(\r\n (connector) => connector.lifecyclePhase === phase,\r\n );\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.boot();\r\n }\r\n\r\n for (const connector of phaseConnectors) {\r\n await connector.start();\r\n }\r\n }\r\n\r\n /**\r\n * Start all connectors except the given ones\r\n */\r\n public async startWithout(excludedConnectors: ConnectorName[]): Promise<void> {\r\n await this.start(\r\n this.connectors\r\n .filter((connector) => !excludedConnectors.includes(connector.name))\r\n .map((connector) => connector.name),\r\n );\r\n }\r\n\r\n /**\r\n * Shutdown all connectors\r\n */\r\n public async shutdown(): Promise<void> {\r\n // App-owned teardown runs first, while every connector (db, cache, http) is\r\n // still up, so cleanup hooks can use them. Idempotent + error-isolated.\r\n await Application.runShutdownHooks();\r\n\r\n // Shut down connectors in reverse priority order. Copy the list first —\r\n // `reverse()` mutates in place, so reversing the live array would corrupt\r\n // the order on a second shutdown pass.\r\n for (const connector of [...this.connectors].reverse()) {\r\n try {\r\n await connector.shutdown();\r\n } catch (error) {\r\n try {\r\n // Awaited deliberately: the caller (`shutdownOnProcessKill`) calls\r\n // `process.exit(0)` as soon as this resolves, so an un-awaited log is\r\n // a log that never happens.\r\n await log.error(\"connectors\", \"shutdown\", error as Error, { connector: connector.name });\r\n } catch {\r\n // Reporting a failure must never become a worse failure than the one\r\n // being reported. `Logger.log()` fans out to channels without\r\n // isolating them, so a channel that throws synchronously rejects this\r\n // call — and this call sits INSIDE the catch, so that rejection\r\n // escapes `shutdown()` entirely: the remaining connectors are never\r\n // torn down and `process.exit(0)` never runs, leaving the process\r\n // alive on the handles they still hold.\r\n //\r\n // Swallowed rather than re-reported: the only channel we could report\r\n // it through is the one that just threw.\r\n }\r\n }\r\n }\r\n\r\n // `Logger.log()` hands each entry to `channel.log()` without awaiting it, so\r\n // awaiting the calls above is not enough on its own — a buffered or async\r\n // channel (file, Sentry) still loses the entry when the process exits.\r\n // Drained once here rather than per failure: shutdown is exactly when\r\n // buffered logs must reach their destination, and re-draining every channel\r\n // once per failed connector buys nothing.\r\n await log.flush();\r\n }\r\n\r\n /**\r\n * Shutdown connectors on process kill\r\n *\r\n * Registers signal handlers that run an idempotent graceful shutdown:\r\n * - SIGINT: Ctrl+C on Unix, also caught on Windows but unreliable in child processes\r\n * - SIGTERM: Termination signal (Unix primarily)\r\n * - SIGHUP: registered on Windows (`win32`) only, to catch Ctrl+C / console close\r\n *\r\n * Note: no `beforeExit` handler is registered — `beforeExit` cannot reliably\r\n * await async teardown (the process exits once the callback returns), so\r\n * shutdown is driven entirely by the signal handlers above.\r\n */\r\n public shutdownOnProcessKill(): void {\r\n let isShuttingDown = false;\r\n\r\n const gracefulShutdown = async (signal: string) => {\r\n if (isShuttingDown) return;\r\n isShuttingDown = true;\r\n\r\n console.log(`\\nExiting...`);\r\n await this.shutdown();\r\n process.exit(0);\r\n };\r\n\r\n // Unix signals\r\n process.on(\"SIGINT\", () => gracefulShutdown(\"SIGINT\"));\r\n process.on(\"SIGTERM\", () => gracefulShutdown(\"SIGTERM\"));\r\n\r\n // Windows-specific: handle when process is about to exit\r\n // This is more reliable on Windows for spawned child processes\r\n if (process.platform === \"win32\") {\r\n // Handle Ctrl+C on Windows specifically\r\n process.on(\"SIGHUP\", () => gracefulShutdown(\"SIGHUP\"));\r\n }\r\n }\r\n}\r\n\r\nexport const connectorsManager = new ConnectorsManager();\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"database-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/database-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,iBAAA,SAA0B,aAAA;EAAA,SACrB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,QAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA8BL;;;EAAA,mBAzBN,YAAA;EARkB;;;EAaxB,KAAA,IAAS,OAAA;EAXE;;;EA+BX,QAAA,IAAY,OAAA;AAAA"}
1
+ {"version":3,"file":"database-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/database-connector.ts"],"mappings":";;;;;;AAUA;;cAAa,iBAAA,SAA0B,aAAA;EAAA,SACrB,IAAA;EAAA,SACA,QAAA,GAAQ,iBAAA,CAAA,QAAA;EAAA,SACR,cAAA,GAAc,uBAAA,CAAA,KAAA;EA8BL;;;EAAA,mBAzBN,YAAA;EARkB;;;EAaxB,KAAA,CAAA,GAAS,OAAA;EAXE;;;EA+BX,QAAA,CAAA,GAAY,OAAA;AAAA"}
@@ -92,6 +92,23 @@ function isWildcardBind(boundAddress) {
92
92
  return WILDCARD_HOSTS.has(url.hostname);
93
93
  }
94
94
  /**
95
+ * Whether the host a caller ASKED to bind is a wildcard.
96
+ *
97
+ * Needed because `isWildcardBind` alone cannot answer this. Fastify's
98
+ * `listen()` normalises a `0.0.0.0` bind and resolves back
99
+ * `http://127.0.0.1:<port>` — the loopback spelling — while `::` comes back
100
+ * as `http://[::]:<port>` untouched. So the returned address reports the
101
+ * wildcard for one of the two spellings and hides it for the other, and
102
+ * `0.0.0.0` is the one people actually write.
103
+ *
104
+ * The requested host is the ground truth for INTENT; the bound address is the
105
+ * ground truth for what happened. A wildcard in either is a wildcard bind.
106
+ */
107
+ function isWildcardHost(host) {
108
+ if (!host) return false;
109
+ return WILDCARD_HOSTS.has(host.trim().toLowerCase());
110
+ }
111
+ /**
95
112
  * The address as it should be SHOWN to a human. See the module doc above.
96
113
  *
97
114
  * Anything unparseable is returned untouched: a best-effort prettifier must
@@ -122,7 +139,7 @@ function effectivePort(url) {
122
139
  if (url.port) return url.port;
123
140
  return url.protocol === "https:" ? "443" : "80";
124
141
  }
125
- function describeServerAddress(boundAddress, baseUrl) {
142
+ function describeServerAddress(boundAddress, baseUrl, requestedHost) {
126
143
  const bound = parseUrl(boundAddress);
127
144
  const configured = parseUrl(baseUrl);
128
145
  if (!bound) {
@@ -131,7 +148,7 @@ function describeServerAddress(boundAddress, baseUrl) {
131
148
  ready: `Server ready at ${fallback}`,
132
149
  url: fallback,
133
150
  boundAddress,
134
- wildcardBind: false
151
+ wildcardBind: isWildcardHost(requestedHost)
135
152
  };
136
153
  }
137
154
  const url = toDisplayUrl(boundAddress);
@@ -139,7 +156,7 @@ function describeServerAddress(boundAddress, baseUrl) {
139
156
  ready: `Server ready at ${url}`,
140
157
  url,
141
158
  boundAddress,
142
- wildcardBind: isWildcardBind(boundAddress)
159
+ wildcardBind: isWildcardBind(boundAddress) || isWildcardHost(requestedHost)
143
160
  };
144
161
  if (!configured) return base;
145
162
  if (!isLoopback(configured)) return {
@@ -1 +1 @@
1
- {"version":3,"file":"describe-server-address.mjs","names":[],"sources":["../../../../../../../core/src/connectors/describe-server-address.ts"],"sourcesContent":["/**\n * What to say once the HTTP server has bound.\n *\n * ## Why this is not one template string\n *\n * `http-connector.ts` used to bind `httpConfig.port` and then announce\n * `config.get(\"app.baseUrl\")` — a different value, read from a different\n * environment variable (`PORT` vs `BASE_URL`), with nothing checking the two\n * agreed. On 2026-08-24 a server listening on 3000 printed\n * `Server ready at http://localhost:41900`. **Following the line the server\n * itself printed gave connection refused, on a server that was working.** The\n * reference app carried the rule as a comment in `.env` — \"must agree with the\n * port in BASE_URL above\" — and a rule a comment enforces is not enforced.\n *\n * So the ready line now states the address actually bound. `app.baseUrl` is a\n * separate claim, and it is reported separately.\n *\n * ## Why a disagreement is not fatal\n *\n * A reverse-proxied deployment legitimately binds `0.0.0.0:3000` and publishes\n * `https://example.com`. Refusing to boot on a mismatch would break the correct\n * case, and warning on it would fire on every correct production boot — a\n * warning that always fires is one nobody reads.\n *\n * The mismatch worth a warning is the ACTIONABLE one: a base URL that points at\n * **loopback on a port nothing is listening on**, because someone will paste it\n * into a browser and conclude the server is broken. A non-loopback base URL is\n * a deployment address, reported plainly and never flagged.\n *\n * ## Why the address bound and the address printed are not the same string\n *\n * `listen()` resolves `localhost` and hands back whichever address it actually\n * bound — on a dual-stack machine that is `http://[::1]:2030`. That literal is\n * correct and useless: most terminals will not linkify a bracketed IPv6 host,\n * and on a first run it reads as broken output. So {@link toDisplayUrl} maps\n * the *loopback and wildcard* spellings onto `localhost`, which resolves back\n * to the very socket that was bound.\n *\n * This is a DISPLAY transform and nothing else — `host` still reaches\n * `listen()` untouched (`http-connector.ts`), so the set of interfaces the\n * server accepts on is byte-for-byte what it was. A genuine routable IPv6\n * address is left exactly as it is: there is no synonym for it, and inventing\n * one would be the same class of lie as printing `app.baseUrl` was.\n */\n\n/** Hosts that all mean \"this machine\" for the purpose of comparing addresses. */\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\", \"0.0.0.0\", \"\"]);\n\n/**\n * Hosts for which `localhost` is a truthful, reachable synonym.\n *\n * The two loopback literals are exact synonyms. The two wildcards (`::`,\n * `0.0.0.0`) are not synonyms — they are strictly wider — but every one of them\n * *includes* loopback, so `localhost` always reaches the server. Callers that\n * need to tell the reader the bind is wider than loopback should say so\n * separately; see `dev-server/ready-block.ts`.\n *\n * The bracketed spellings are the ones that actually occur: WHATWG `hostname`\n * KEEPS the brackets for an IPv6 host (`new URL(\"http://[::1]:1\").hostname ===\n * \"[::1]\"`). The bare forms are listed too so a caller that hands over a host\n * rather than a URL is not silently missed.\n */\nconst DISPLAY_AS_LOCALHOST = new Set([\"127.0.0.1\", \"[::1]\", \"::1\", \"0.0.0.0\", \"[::]\", \"::\"]);\n\n/** The wildcard spellings — a bind wider than loopback. */\nconst WILDCARD_HOSTS = new Set([\"0.0.0.0\", \"[::]\", \"::\"]);\n\n/**\n * Whether the given bound address listens on more than loopback.\n *\n * Used to add \"(all interfaces)\" next to a URL that has been rewritten to\n * `localhost`, so the rewrite never hides a wider bind than the reader thinks.\n */\nexport function isWildcardBind(boundAddress: string | undefined): boolean {\n const url = parseUrl(boundAddress);\n\n if (!url) return false;\n\n return WILDCARD_HOSTS.has(url.hostname);\n}\n\n/**\n * The address as it should be SHOWN to a human. See the module doc above.\n *\n * Anything unparseable is returned untouched: a best-effort prettifier must\n * never be the reason a developer is shown something other than what the\n * server reported.\n */\nexport function toDisplayUrl(boundAddress: string | undefined): string {\n if (!boundAddress) return \"\";\n\n const url = parseUrl(boundAddress);\n\n if (!url) return boundAddress;\n\n if (!DISPLAY_AS_LOCALHOST.has(url.hostname)) return boundAddress;\n\n url.hostname = \"localhost\";\n\n // `URL.href` re-appends a trailing slash to a bare origin, which is noise in\n // a status line and is not what `listen()` returned.\n return url.href.replace(/\\/$/, \"\");\n}\n\nexport type ServerAddressReport = {\n /** Where the server is genuinely reachable, spelled for a human. Always present. */\n ready: string;\n /**\n * The same address as a bare URL, ready to paste into a browser — the value\n * `ready` embeds. Callers that render their own layout (the dev ready block)\n * use this instead of re-deriving it.\n */\n url: string;\n /**\n * Exactly what `listen()` returned, untransformed. Kept so a caller that\n * needs to reason about the BIND (which interfaces, which family) reads the\n * fact rather than the presentation of it.\n */\n boundAddress?: string;\n /** Whether the bind covers more than loopback (`0.0.0.0` / `::`). */\n wildcardBind: boolean;\n /** The configured public address, when it is a deployment URL rather than a local one. */\n publicUrl?: string;\n /** Set only when `app.baseUrl` points at loopback on a port nothing bound. */\n warning?: string;\n};\n\nfunction parseUrl(value: unknown): URL | undefined {\n if (typeof value !== \"string\" || value.length === 0) return undefined;\n\n try {\n return new URL(value);\n } catch {\n // An unparseable base URL is a separate problem, and it is NOT evidence of\n // a port mismatch — reporting one from it would be a guess.\n return undefined;\n }\n}\n\nfunction isLoopback(url: URL): boolean {\n return LOOPBACK_HOSTS.has(url.hostname);\n}\n\n/** `:3000` explicitly, or the protocol's default when the URL omits it. */\nfunction effectivePort(url: URL): string {\n if (url.port) return url.port;\n\n return url.protocol === \"https:\" ? \"443\" : \"80\";\n}\n\nexport function describeServerAddress(\n boundAddress: string | undefined,\n baseUrl: unknown,\n): ServerAddressReport {\n const bound = parseUrl(boundAddress);\n const configured = parseUrl(baseUrl);\n\n // No bound address to report means the caller could not obtain one; the\n // configured URL is then the only thing we know, and saying nothing would be\n // worse than saying the one thing we have.\n if (!bound) {\n const fallback = String(configured?.href ?? boundAddress ?? baseUrl ?? \"\");\n\n return {\n ready: `Server ready at ${fallback}`,\n url: fallback,\n boundAddress,\n wildcardBind: false,\n };\n }\n\n // The URL a human is shown. `boundAddress` (the literal `listen()` returned)\n // travels on the report untouched — see the module doc on why these differ.\n const url = toDisplayUrl(boundAddress);\n const base = {\n ready: `Server ready at ${url}`,\n url,\n boundAddress,\n wildcardBind: isWildcardBind(boundAddress),\n };\n\n if (!configured) return base;\n\n if (!isLoopback(configured)) {\n return { ...base, publicUrl: `Public URL (app.baseUrl): ${baseUrl}` };\n }\n\n if (effectivePort(configured) === effectivePort(bound)) {\n return base;\n }\n\n return {\n ...base,\n warning:\n `app.baseUrl is ${baseUrl}, but the server bound port ${effectivePort(bound)}. ` +\n `Nothing is listening on port ${effectivePort(configured)} — ` +\n `check BASE_URL and PORT, including anything exported in your shell.`,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;CAAS;CAAW;AAAE,CAAC;;;;;;;;;;;;;;;AAgBxF,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAa;CAAS;CAAO;CAAW;CAAQ;AAAI,CAAC;;AAG3F,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAW;CAAQ;AAAI,CAAC;;;;;;;AAQxD,SAAgB,eAAe,cAA2C;CACxE,MAAM,MAAM,SAAS,YAAY;CAEjC,IAAI,CAAC,KAAK,OAAO;CAEjB,OAAO,eAAe,IAAI,IAAI,QAAQ;AACxC;;;;;;;;AASA,SAAgB,aAAa,cAA0C;CACrE,IAAI,CAAC,cAAc,OAAO;CAE1B,MAAM,MAAM,SAAS,YAAY;CAEjC,IAAI,CAAC,KAAK,OAAO;CAEjB,IAAI,CAAC,qBAAqB,IAAI,IAAI,QAAQ,GAAG,OAAO;CAEpD,IAAI,WAAW;CAIf,OAAO,IAAI,KAAK,QAAQ,OAAO,EAAE;AACnC;AAyBA,SAAS,SAAS,OAAiC;CACjD,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG,OAAO;CAE5D,IAAI;EACF,OAAO,IAAI,IAAI,KAAK;CACtB,QAAQ;EAGN;CACF;AACF;AAEA,SAAS,WAAW,KAAmB;CACrC,OAAO,eAAe,IAAI,IAAI,QAAQ;AACxC;;AAGA,SAAS,cAAc,KAAkB;CACvC,IAAI,IAAI,MAAM,OAAO,IAAI;CAEzB,OAAO,IAAI,aAAa,WAAW,QAAQ;AAC7C;AAEA,SAAgB,sBACd,cACA,SACqB;CACrB,MAAM,QAAQ,SAAS,YAAY;CACnC,MAAM,aAAa,SAAS,OAAO;CAKnC,IAAI,CAAC,OAAO;EACV,MAAM,WAAW,OAAO,YAAY,QAAQ,gBAAgB,WAAW,EAAE;EAEzE,OAAO;GACL,OAAO,mBAAmB;GAC1B,KAAK;GACL;GACA,cAAc;EAChB;CACF;CAIA,MAAM,MAAM,aAAa,YAAY;CACrC,MAAM,OAAO;EACX,OAAO,mBAAmB;EAC1B;EACA;EACA,cAAc,eAAe,YAAY;CAC3C;CAEA,IAAI,CAAC,YAAY,OAAO;CAExB,IAAI,CAAC,WAAW,UAAU,GACxB,OAAO;EAAE,GAAG;EAAM,WAAW,6BAA6B;CAAU;CAGtE,IAAI,cAAc,UAAU,MAAM,cAAc,KAAK,GACnD,OAAO;CAGT,OAAO;EACL,GAAG;EACH,SACE,kBAAkB,QAAQ,8BAA8B,cAAc,KAAK,EAAE,iCAC7C,cAAc,UAAU,EAAE;CAE9D;AACF"}
1
+ {"version":3,"file":"describe-server-address.mjs","names":[],"sources":["../../../../../../../core/src/connectors/describe-server-address.ts"],"sourcesContent":["/**\n * What to say once the HTTP server has bound.\n *\n * ## Why this is not one template string\n *\n * `http-connector.ts` used to bind `httpConfig.port` and then announce\n * `config.get(\"app.baseUrl\")` — a different value, read from a different\n * environment variable (`PORT` vs `BASE_URL`), with nothing checking the two\n * agreed. On 2026-08-24 a server listening on 3000 printed\n * `Server ready at http://localhost:41900`. **Following the line the server\n * itself printed gave connection refused, on a server that was working.** The\n * reference app carried the rule as a comment in `.env` — \"must agree with the\n * port in BASE_URL above\" — and a rule a comment enforces is not enforced.\n *\n * So the ready line now states the address actually bound. `app.baseUrl` is a\n * separate claim, and it is reported separately.\n *\n * ## Why a disagreement is not fatal\n *\n * A reverse-proxied deployment legitimately binds `0.0.0.0:3000` and publishes\n * `https://example.com`. Refusing to boot on a mismatch would break the correct\n * case, and warning on it would fire on every correct production boot — a\n * warning that always fires is one nobody reads.\n *\n * The mismatch worth a warning is the ACTIONABLE one: a base URL that points at\n * **loopback on a port nothing is listening on**, because someone will paste it\n * into a browser and conclude the server is broken. A non-loopback base URL is\n * a deployment address, reported plainly and never flagged.\n *\n * ## Why the address bound and the address printed are not the same string\n *\n * `listen()` resolves `localhost` and hands back whichever address it actually\n * bound — on a dual-stack machine that is `http://[::1]:2030`. That literal is\n * correct and useless: most terminals will not linkify a bracketed IPv6 host,\n * and on a first run it reads as broken output. So {@link toDisplayUrl} maps\n * the *loopback and wildcard* spellings onto `localhost`, which resolves back\n * to the very socket that was bound.\n *\n * This is a DISPLAY transform and nothing else — `host` still reaches\n * `listen()` untouched (`http-connector.ts`), so the set of interfaces the\n * server accepts on is byte-for-byte what it was. A genuine routable IPv6\n * address is left exactly as it is: there is no synonym for it, and inventing\n * one would be the same class of lie as printing `app.baseUrl` was.\n */\n\n/** Hosts that all mean \"this machine\" for the purpose of comparing addresses. */\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\", \"0.0.0.0\", \"\"]);\n\n/**\n * Hosts for which `localhost` is a truthful, reachable synonym.\n *\n * The two loopback literals are exact synonyms. The two wildcards (`::`,\n * `0.0.0.0`) are not synonyms — they are strictly wider — but every one of them\n * *includes* loopback, so `localhost` always reaches the server. Callers that\n * need to tell the reader the bind is wider than loopback should say so\n * separately; see `dev-server/ready-block.ts`.\n *\n * The bracketed spellings are the ones that actually occur: WHATWG `hostname`\n * KEEPS the brackets for an IPv6 host (`new URL(\"http://[::1]:1\").hostname ===\n * \"[::1]\"`). The bare forms are listed too so a caller that hands over a host\n * rather than a URL is not silently missed.\n */\nconst DISPLAY_AS_LOCALHOST = new Set([\"127.0.0.1\", \"[::1]\", \"::1\", \"0.0.0.0\", \"[::]\", \"::\"]);\n\n/** The wildcard spellings — a bind wider than loopback. */\nconst WILDCARD_HOSTS = new Set([\"0.0.0.0\", \"[::]\", \"::\"]);\n\n/**\n * Whether the given bound address listens on more than loopback.\n *\n * Used to add \"(all interfaces)\" next to a URL that has been rewritten to\n * `localhost`, so the rewrite never hides a wider bind than the reader thinks.\n */\nexport function isWildcardBind(boundAddress: string | undefined): boolean {\n const url = parseUrl(boundAddress);\n\n if (!url) return false;\n\n return WILDCARD_HOSTS.has(url.hostname);\n}\n\n/**\n * Whether the host a caller ASKED to bind is a wildcard.\n *\n * Needed because `isWildcardBind` alone cannot answer this. Fastify's\n * `listen()` normalises a `0.0.0.0` bind and resolves back\n * `http://127.0.0.1:<port>` — the loopback spelling — while `::` comes back\n * as `http://[::]:<port>` untouched. So the returned address reports the\n * wildcard for one of the two spellings and hides it for the other, and\n * `0.0.0.0` is the one people actually write.\n *\n * The requested host is the ground truth for INTENT; the bound address is the\n * ground truth for what happened. A wildcard in either is a wildcard bind.\n */\nexport function isWildcardHost(host: string | undefined): boolean {\n if (!host) return false;\n\n return WILDCARD_HOSTS.has(host.trim().toLowerCase());\n}\n\n/**\n * The address as it should be SHOWN to a human. See the module doc above.\n *\n * Anything unparseable is returned untouched: a best-effort prettifier must\n * never be the reason a developer is shown something other than what the\n * server reported.\n */\nexport function toDisplayUrl(boundAddress: string | undefined): string {\n if (!boundAddress) return \"\";\n\n const url = parseUrl(boundAddress);\n\n if (!url) return boundAddress;\n\n if (!DISPLAY_AS_LOCALHOST.has(url.hostname)) return boundAddress;\n\n url.hostname = \"localhost\";\n\n // `URL.href` re-appends a trailing slash to a bare origin, which is noise in\n // a status line and is not what `listen()` returned.\n return url.href.replace(/\\/$/, \"\");\n}\n\nexport type ServerAddressReport = {\n /** Where the server is genuinely reachable, spelled for a human. Always present. */\n ready: string;\n /**\n * The same address as a bare URL, ready to paste into a browser — the value\n * `ready` embeds. Callers that render their own layout (the dev ready block)\n * use this instead of re-deriving it.\n */\n url: string;\n /**\n * Exactly what `listen()` returned, untransformed. Kept so a caller that\n * needs to reason about the BIND (which interfaces, which family) reads the\n * fact rather than the presentation of it.\n */\n boundAddress?: string;\n /** Whether the bind covers more than loopback (`0.0.0.0` / `::`). */\n wildcardBind: boolean;\n /** The configured public address, when it is a deployment URL rather than a local one. */\n publicUrl?: string;\n /** Set only when `app.baseUrl` points at loopback on a port nothing bound. */\n warning?: string;\n};\n\nfunction parseUrl(value: unknown): URL | undefined {\n if (typeof value !== \"string\" || value.length === 0) return undefined;\n\n try {\n return new URL(value);\n } catch {\n // An unparseable base URL is a separate problem, and it is NOT evidence of\n // a port mismatch — reporting one from it would be a guess.\n return undefined;\n }\n}\n\nfunction isLoopback(url: URL): boolean {\n return LOOPBACK_HOSTS.has(url.hostname);\n}\n\n/** `:3000` explicitly, or the protocol's default when the URL omits it. */\nfunction effectivePort(url: URL): string {\n if (url.port) return url.port;\n\n return url.protocol === \"https:\" ? \"443\" : \"80\";\n}\n\nexport function describeServerAddress(\n boundAddress: string | undefined,\n baseUrl: unknown,\n /**\n * The host passed to `listen()`. Optional so existing callers keep working,\n * but the connector passes it: without it a `0.0.0.0` bind is reported as\n * loopback-only, and the dev block then omits its \"(all interfaces)\" note in\n * exactly the case a developer most needs it.\n */\n requestedHost?: string,\n): ServerAddressReport {\n const bound = parseUrl(boundAddress);\n const configured = parseUrl(baseUrl);\n\n // No bound address to report means the caller could not obtain one; the\n // configured URL is then the only thing we know, and saying nothing would be\n // worse than saying the one thing we have.\n if (!bound) {\n const fallback = String(configured?.href ?? boundAddress ?? baseUrl ?? \"\");\n\n return {\n ready: `Server ready at ${fallback}`,\n url: fallback,\n boundAddress,\n wildcardBind: isWildcardHost(requestedHost),\n };\n }\n\n // The URL a human is shown. `boundAddress` (the literal `listen()` returned)\n // travels on the report untouched — see the module doc on why these differ.\n const url = toDisplayUrl(boundAddress);\n const base = {\n ready: `Server ready at ${url}`,\n url,\n boundAddress,\n wildcardBind: isWildcardBind(boundAddress) || isWildcardHost(requestedHost),\n };\n\n if (!configured) return base;\n\n if (!isLoopback(configured)) {\n return { ...base, publicUrl: `Public URL (app.baseUrl): ${baseUrl}` };\n }\n\n if (effectivePort(configured) === effectivePort(bound)) {\n return base;\n }\n\n return {\n ...base,\n warning:\n `app.baseUrl is ${baseUrl}, but the server bound port ${effectivePort(bound)}. ` +\n `Nothing is listening on port ${effectivePort(configured)} — ` +\n `check BASE_URL and PORT, including anything exported in your shell.`,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;CAAS;CAAW;AAAE,CAAC;;;;;;;;;;;;;;;AAgBxF,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAa;CAAS;CAAO;CAAW;CAAQ;AAAI,CAAC;;AAG3F,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAW;CAAQ;AAAI,CAAC;;;;;;;AAQxD,SAAgB,eAAe,cAA2C;CACxE,MAAM,MAAM,SAAS,YAAY;CAEjC,IAAI,CAAC,KAAK,OAAO;CAEjB,OAAO,eAAe,IAAI,IAAI,QAAQ;AACxC;;;;;;;;;;;;;;AAeA,SAAgB,eAAe,MAAmC;CAChE,IAAI,CAAC,MAAM,OAAO;CAElB,OAAO,eAAe,IAAI,KAAK,KAAK,EAAE,YAAY,CAAC;AACrD;;;;;;;;AASA,SAAgB,aAAa,cAA0C;CACrE,IAAI,CAAC,cAAc,OAAO;CAE1B,MAAM,MAAM,SAAS,YAAY;CAEjC,IAAI,CAAC,KAAK,OAAO;CAEjB,IAAI,CAAC,qBAAqB,IAAI,IAAI,QAAQ,GAAG,OAAO;CAEpD,IAAI,WAAW;CAIf,OAAO,IAAI,KAAK,QAAQ,OAAO,EAAE;AACnC;AAyBA,SAAS,SAAS,OAAiC;CACjD,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG,OAAO;CAE5D,IAAI;EACF,OAAO,IAAI,IAAI,KAAK;CACtB,QAAQ;EAGN;CACF;AACF;AAEA,SAAS,WAAW,KAAmB;CACrC,OAAO,eAAe,IAAI,IAAI,QAAQ;AACxC;;AAGA,SAAS,cAAc,KAAkB;CACvC,IAAI,IAAI,MAAM,OAAO,IAAI;CAEzB,OAAO,IAAI,aAAa,WAAW,QAAQ;AAC7C;AAEA,SAAgB,sBACd,cACA,SAOA,eACqB;CACrB,MAAM,QAAQ,SAAS,YAAY;CACnC,MAAM,aAAa,SAAS,OAAO;CAKnC,IAAI,CAAC,OAAO;EACV,MAAM,WAAW,OAAO,YAAY,QAAQ,gBAAgB,WAAW,EAAE;EAEzE,OAAO;GACL,OAAO,mBAAmB;GAC1B,KAAK;GACL;GACA,cAAc,eAAe,aAAa;EAC5C;CACF;CAIA,MAAM,MAAM,aAAa,YAAY;CACrC,MAAM,OAAO;EACX,OAAO,mBAAmB;EAC1B;EACA;EACA,cAAc,eAAe,YAAY,KAAK,eAAe,aAAa;CAC5E;CAEA,IAAI,CAAC,YAAY,OAAO;CAExB,IAAI,CAAC,WAAW,UAAU,GACxB,OAAO;EAAE,GAAG;EAAM,WAAW,6BAA6B;CAAU;CAGtE,IAAI,cAAc,UAAU,MAAM,cAAc,KAAK,GACnD,OAAO;CAGT,OAAO;EACL,GAAG;EACH,SACE,kBAAkB,QAAQ,8BAA8B,cAAc,KAAK,EAAE,iCAC7C,cAAc,UAAU,EAAE;CAE9D;AACF"}
@@ -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,IAAS,OAAA;EAXE;;;EAyCX,QAAA,IAAY,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,CAAA,GAAS,OAAA;EAXE;;;EAyCX,QAAA,CAAA,GAAY,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.d.mts","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"mappings":";;;;;;;AAuCA;;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;EA6MP;;;AAAoC;;EApM9B,IAAA,IAAI,OAAA;;;;;;;;UAmCT,oBAAA;;;;;;EAgCK,KAAA,IAAS,OAAA;;;;;;EA4FT,OAAA,IAAW,OAAA;;;;EASX,QAAA,IAAY,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,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"}
@@ -6,9 +6,11 @@ import { registerHttpPlugins } from "../http/plugins.mjs";
6
6
  import { closeServerWithTimeout, getHttpServer, startHttpServer } from "../http/server.mjs";
7
7
  import "../application/index.mjs";
8
8
  import { health } from "../http/health.mjs";
9
+ import { assertPortIsAvailable } from "../http/port-preflight.mjs";
9
10
  import { setHttpReadyReport } from "../http/ready-report.mjs";
10
11
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./types.mjs";
11
12
  import { BaseConnector } from "./base-connector.mjs";
13
+ import { ensureFatalIsVisible } from "../logger/fatal-visibility.mjs";
12
14
  import { describeServerAddress } from "./describe-server-address.mjs";
13
15
  import config from "@mongez/config";
14
16
  import { log } from "@warlock.js/logger";
@@ -113,12 +115,13 @@ var HttpConnector = class extends BaseConnector {
113
115
  const routeCount = router.routeCount();
114
116
  if (!deferToReadyBlock) log.info("http", "routes", `${routeCount} route(s) registered`);
115
117
  try {
118
+ await assertPortIsAvailable(httpConfig.port, httpConfig.host || "localhost");
116
119
  const boundAddress = await this.http.listen({
117
120
  port: httpConfig.port,
118
121
  host: httpConfig.host || "localhost"
119
122
  });
120
123
  Application.setServedPort(httpConfig.port);
121
- const address = describeServerAddress(boundAddress, config.get("app.baseUrl"));
124
+ const address = describeServerAddress(boundAddress, config.get("app.baseUrl"), httpConfig.host || "localhost");
122
125
  setHttpReadyReport({
123
126
  boundAddress: address.boundAddress,
124
127
  url: address.url,
@@ -134,6 +137,7 @@ var HttpConnector = class extends BaseConnector {
134
137
  if (address.warning) log.warn(`http`, "connection", address.warning);
135
138
  } catch (error) {
136
139
  await log.fatal("http", "connection", error);
140
+ ensureFatalIsVisible(error);
137
141
  await log.flush();
138
142
  process.exit(1);
139
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http-connector.mjs","names":[],"sources":["../../../../../../../core/src/connectors/http-connector.ts"],"sourcesContent":["import config from \"@mongez/config\";\r\nimport { colors } from \"@mongez/copper\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { Application } from \"../application\";\r\nimport { health } from \"../http/health\";\r\nimport { registerHttpPlugins } from \"../http/plugins\";\r\nimport { setHttpReadyReport } from \"../http/ready-report\";\r\nimport { closeServerWithTimeout, FastifyInstance, getHttpServer, startHttpServer } from \"../http/server\";\r\nimport { router } from \"../router/router\";\r\nimport { Environment } from \"../utils\";\r\nimport { setBaseUrl } from \"../utils/urls\";\r\nimport { container } from \"./../container\";\r\nimport { BaseConnector } from \"./base-connector\";\r\nimport { describeServerAddress } from \"./describe-server-address\";\r\nimport { ConnectorLifecyclePhase, ConnectorPriority } from \"./types\";\r\n\r\nfunction environmentColor(environment: Environment) {\r\n switch (environment) {\r\n case \"development\":\r\n return colors.magentaBright(environment);\r\n case \"test\":\r\n return colors.yellowBright(environment);\r\n case \"production\":\r\n return colors.greenBright(environment);\r\n default:\r\n return colors.white(environment);\r\n }\r\n}\r\n\r\n/**\r\n * Default time to wait for in-flight requests to drain on shutdown before the\r\n * HTTP server is force-closed. Override via `http.gracefulShutdown.timeout`.\r\n */\r\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\r\n\r\n/**\r\n * HTTP Connector\r\n * Manages HTTP server (Fastify) lifecycle\r\n */\r\nexport class HttpConnector extends BaseConnector {\r\n public readonly name = \"http\";\r\n public readonly priority = ConnectorPriority.HTTP;\r\n public readonly lifecyclePhase = ConnectorLifecyclePhase.Late;\r\n\r\n /**\r\n * Files that trigger HTTP server restart\r\n * Note: routes.ts changes will be handled by HMR with wildcard routing\r\n * Connectors receive config file paths directly (not .env) thanks to layer-executor\r\n */\r\n protected readonly watchedFiles = [\"src/config/http.ts\", \"src/config/http.tsx\"];\r\n\r\n /**\r\n * Fastify Server instance\r\n */\r\n protected http?: FastifyInstance;\r\n\r\n /**\r\n * Whether someone downstream will render the combined ready block.\r\n *\r\n * Only the dev server does (`dev-server/ready-block.ts`), and only it can:\r\n * the block reports facts — boot duration, the web surface — that this\r\n * connector does not own and must not wait for. When it will, this connector\r\n * records and stays quiet; otherwise it narrates as it always has.\r\n *\r\n * Keyed on the runtime strategy rather than on `NODE_ENV`, because that is\r\n * what actually distinguishes \"a dev server is driving this boot\" from \"an\r\n * app happens to be running with development config\".\r\n *\r\n * `!isBooted` is the second half and it is not a detail. The block summarises\r\n * a BOOT. A `restart()` after boot — an edited `src/config/http.ts` — is an\r\n * EVENT, and there is no block coming to carry it, so suppressing the lines\r\n * there would rebind the port in total silence.\r\n */\r\n protected get rendersReadyBlock(): boolean {\r\n return Application.runtimeStrategy === \"development\" && !Application.isBooted;\r\n }\r\n\r\n /**\r\n * Boot the connector — construction only (create Fastify, register\r\n * plugins, populate container). Route scanning is deferred to\r\n * `start()` so it reads the router after app code has registered.\r\n */\r\n public async boot() {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig) return;\r\n\r\n const port = httpConfig.port;\r\n log.info(\r\n `http`,\r\n \"connection\",\r\n `Starting http server on port ${port} in ${environmentColor(Application.environment)} mode`,\r\n );\r\n\r\n this.http = startHttpServer(httpConfig.serverOptions);\r\n\r\n container.set(\"http.server\", this.http);\r\n\r\n await registerHttpPlugins(this.http);\r\n\r\n const baseUrl = config.get(\"app.baseUrl\");\r\n\r\n // update base url\r\n setBaseUrl(baseUrl);\r\n\r\n // Liveness/readiness endpoints — registered on Fastify before start() scans\r\n // the app router, so they exist by the time the server listens.\r\n this.registerHealthRoutes();\r\n }\r\n\r\n /**\r\n * Register the built-in liveness (`/health`) and readiness (`/ready`)\r\n * endpoints directly on the Fastify instance — infra routes, kept off the app\r\n * router so they're immune to HMR and never collide with route scanning. Opt\r\n * out via `http.health.enabled = false`; rename via `http.health.path` /\r\n * `http.health.readinessPath`.\r\n */\r\n private registerHealthRoutes(): void {\r\n if (!this.http) {\r\n return;\r\n }\r\n\r\n const healthConfig = config.get(\"http.health\");\r\n\r\n if (healthConfig?.enabled === false) {\r\n return;\r\n }\r\n\r\n const livenessPath = healthConfig?.path ?? \"/health\";\r\n const readinessPath = healthConfig?.readinessPath ?? \"/ready\";\r\n\r\n this.http.get(livenessPath, async (_request: any, reply: any) => {\r\n const result = health.liveness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n\r\n this.http.get(readinessPath, async (_request: any, reply: any) => {\r\n const result = await health.readiness();\r\n\r\n return reply.code(result.status === \"ok\" ? 200 : 503).send(result);\r\n });\r\n }\r\n\r\n /**\r\n * Initialize HTTP server — bind app-registered routes to Fastify\r\n * then listen. Scanning here (not in `boot`) lets HTTP boot before\r\n * app code without losing routes.\r\n */\r\n public async start(): Promise<void> {\r\n const httpConfig = config.get(\"http\");\r\n\r\n if (!httpConfig || !this.http) return;\r\n\r\n // Read once. `Application.isBooted` flips during the same boot this method\r\n // belongs to, and a predicate that answered differently before and after\r\n // `listen()` would print half a report.\r\n const deferToReadyBlock = this.rendersReadyBlock;\r\n\r\n if (Application.runtimeStrategy === \"development\") {\r\n router.scanDevServer(this.http);\r\n } else {\r\n router.scan(this.http);\r\n }\r\n\r\n // Surface the route table as a readiness signal + a boot log, so an empty\r\n // or partial route surface (e.g. a route module that failed to register) is\r\n // detectable instead of silently 404ing. `addRoutesRegisteredCheck` is\r\n // keyed by name, so re-running on restart is idempotent.\r\n health.addRoutesRegisteredCheck(() => router.routeCount());\r\n\r\n const routeCount = router.routeCount();\r\n\r\n // In development this fact is one line of the single ready block rendered\r\n // after boot completes (`dev-server/ready-block.ts`); printing it here as\r\n // well would put the same number on screen twice, in two formats. Outside\r\n // development the line is load-bearing — a supervisor or CI log greps it —\r\n // so it stays exactly as it was.\r\n if (!deferToReadyBlock) {\r\n log.info(\"http\", \"routes\", `${routeCount} route(s) registered`);\r\n }\r\n\r\n try {\r\n // `listen()` RESOLVES with the address it actually bound — which is the\r\n // only address worth announcing. See `describe-server-address.ts` for why\r\n // announcing `app.baseUrl` here instead was a defect rather than a\r\n // shortcut.\r\n const boundAddress = await this.http.listen({\r\n port: httpConfig.port,\r\n host: httpConfig.host || \"localhost\",\r\n });\r\n\r\n Application.setServedPort(httpConfig.port);\r\n\r\n const address = describeServerAddress(boundAddress, config.get(\"app.baseUrl\"));\r\n\r\n // Recorded unconditionally: the ready block reads it in development, and\r\n // in every other mode it is simply the one place that knows what was\r\n // bound, which is worth having whether or not anything renders it.\r\n setHttpReadyReport({\r\n boundAddress: address.boundAddress,\r\n url: address.url,\r\n wildcardBind: address.wildcardBind,\r\n port: httpConfig.port,\r\n routeCount,\r\n publicUrl: address.publicUrl,\r\n });\r\n\r\n if (!deferToReadyBlock) {\r\n log.success(`http`, \"connection\", address.ready);\r\n\r\n if (address.publicUrl) {\r\n log.info(`http`, \"connection\", address.publicUrl);\r\n }\r\n }\r\n\r\n // NEVER conditional on the block. A base URL pointing at a port nothing\r\n // is listening on is the single most misleading state this connector can\r\n // be in, and a warning a status block swallows is a warning nobody reads\r\n // — so it is logged where it lands ABOVE the block, in both modes.\r\n if (address.warning) {\r\n log.warn(`http`, \"connection\", address.warning);\r\n }\r\n } catch (error) {\r\n // A failed listen()/port-bind at boot means the app can't serve — fatal.\r\n // `log.fatal` reaches Sentry/file; `await log.flush()` drains buffered and\r\n // async channels before the process exits.\r\n await log.fatal(\"http\", \"connection\", error);\r\n await log.flush();\r\n\r\n process.exit(1); // stop the process, exit with error\r\n }\r\n\r\n this.active = true;\r\n }\r\n\r\n /**\r\n * Restart — needs a fresh Fastify instance since `start()` now\r\n * re-runs `router.scan()`, and re-scanning the same Fastify would\r\n * register duplicate route handlers.\r\n */\r\n public async restart(): Promise<void> {\r\n await this.shutdown();\r\n await this.boot();\r\n await this.start();\r\n }\r\n\r\n /**\r\n * Shutdown HTTP server\r\n */\r\n public async shutdown(): Promise<void> {\r\n if (!this.active) {\r\n return;\r\n }\r\n\r\n const server = getHttpServer();\r\n\r\n if (server) {\r\n const timeout = config.get(\"http.gracefulShutdown.timeout\", DEFAULT_SHUTDOWN_TIMEOUT);\r\n const drained = await closeServerWithTimeout(server, timeout);\r\n\r\n if (!drained) {\r\n log.warn(\r\n \"http\",\r\n \"shutdown\",\r\n `In-flight requests did not drain within ${timeout}ms; closing anyway`,\r\n );\r\n }\r\n }\r\n\r\n this.active = false;\r\n }\r\n\r\n /**\r\n * Override shouldRestart to handle routes.ts specially\r\n * routes.ts changes should NOT restart the server (use HMR instead)\r\n * Now receives config file paths directly from layer-executor\r\n */\r\n public shouldRestart(changedFiles: string[]): boolean {\r\n // Only restart for config changes, not routes\r\n return changedFiles.some((file) => {\r\n const relativePath = file.replace(/\\\\/g, \"/\");\r\n return this.watchedFiles.includes(relativePath);\r\n });\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAS,iBAAiB,aAA0B;CAClD,QAAQ,aAAR;EACE,KAAK,eACH,OAAO,OAAO,cAAc,WAAW;EACzC,KAAK,QACH,OAAO,OAAO,aAAa,WAAW;EACxC,KAAK,cACH,OAAO,OAAO,YAAY,WAAW;EACvC,SACE,OAAO,OAAO,MAAM,WAAW;CACnC;AACF;;;;;AAMA,MAAM,2BAA2B;;;;;AAMjC,IAAa,gBAAb,cAAmC,cAAc;;;cACxB;;;sBASW,CAAC,sBAAsB,qBAAqB;;;;;;;;;;;;;;;;;;;CAwB9E,IAAc,oBAA6B;EACzC,OAAO,YAAY,oBAAoB,iBAAiB,CAAC,YAAY;CACvE;;;;;;CAOA,MAAa,OAAO;EAClB,MAAM,aAAa,OAAO,IAAI,MAAM;EAEpC,IAAI,CAAC,YAAY;EAEjB,MAAM,OAAO,WAAW;EACxB,IAAI,KACF,QACA,cACA,gCAAgC,KAAK,MAAM,iBAAiB,YAAY,WAAW,EAAE,MACvF;EAEA,KAAK,OAAO,gBAAgB,WAAW,aAAa;EAEpD,UAAU,IAAI,eAAe,KAAK,IAAI;EAEtC,MAAM,oBAAoB,KAAK,IAAI;EAKnC,WAHgB,OAAO,IAAI,aAGV,CAAC;EAIlB,KAAK,qBAAqB;CAC5B;;;;;;;;CASA,AAAQ,uBAA6B;EACnC,IAAI,CAAC,KAAK,MACR;EAGF,MAAM,eAAe,OAAO,IAAI,aAAa;EAE7C,IAAI,cAAc,YAAY,OAC5B;EAGF,MAAM,eAAe,cAAc,QAAQ;EAC3C,MAAM,gBAAgB,cAAc,iBAAiB;EAErD,KAAK,KAAK,IAAI,cAAc,OAAO,UAAe,UAAe;GAC/D,MAAM,SAAS,OAAO,SAAS;GAE/B,OAAO,MAAM,KAAK,OAAO,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM;EACnE,CAAC;EAED,KAAK,KAAK,IAAI,eAAe,OAAO,UAAe,UAAe;GAChE,MAAM,SAAS,MAAM,OAAO,UAAU;GAEtC,OAAO,MAAM,KAAK,OAAO,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM;EACnE,CAAC;CACH;;;;;;CAOA,MAAa,QAAuB;EAClC,MAAM,aAAa,OAAO,IAAI,MAAM;EAEpC,IAAI,CAAC,cAAc,CAAC,KAAK,MAAM;EAK/B,MAAM,oBAAoB,KAAK;EAE/B,IAAI,YAAY,oBAAoB,eAClC,OAAO,cAAc,KAAK,IAAI;OAE9B,OAAO,KAAK,KAAK,IAAI;EAOvB,OAAO,+BAA+B,OAAO,WAAW,CAAC;EAEzD,MAAM,aAAa,OAAO,WAAW;EAOrC,IAAI,CAAC,mBACH,IAAI,KAAK,QAAQ,UAAU,GAAG,WAAW,qBAAqB;EAGhE,IAAI;GAKF,MAAM,eAAe,MAAM,KAAK,KAAK,OAAO;IAC1C,MAAM,WAAW;IACjB,MAAM,WAAW,QAAQ;GAC3B,CAAC;GAED,YAAY,cAAc,WAAW,IAAI;GAEzC,MAAM,UAAU,sBAAsB,cAAc,OAAO,IAAI,aAAa,CAAC;GAK7E,mBAAmB;IACjB,cAAc,QAAQ;IACtB,KAAK,QAAQ;IACb,cAAc,QAAQ;IACtB,MAAM,WAAW;IACjB;IACA,WAAW,QAAQ;GACrB,CAAC;GAED,IAAI,CAAC,mBAAmB;IACtB,IAAI,QAAQ,QAAQ,cAAc,QAAQ,KAAK;IAE/C,IAAI,QAAQ,WACV,IAAI,KAAK,QAAQ,cAAc,QAAQ,SAAS;GAEpD;GAMA,IAAI,QAAQ,SACV,IAAI,KAAK,QAAQ,cAAc,QAAQ,OAAO;EAElD,SAAS,OAAO;GAId,MAAM,IAAI,MAAM,QAAQ,cAAc,KAAK;GAC3C,MAAM,IAAI,MAAM;GAEhB,QAAQ,KAAK,CAAC;EAChB;EAEA,KAAK,SAAS;CAChB;;;;;;CAOA,MAAa,UAAyB;EACpC,MAAM,KAAK,SAAS;EACpB,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,MAAM;CACnB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,MAAM,SAAS,cAAc;EAE7B,IAAI,QAAQ;GACV,MAAM,UAAU,OAAO,IAAI,iCAAiC,wBAAwB;GAGpF,IAAI,CAAC,MAFiB,uBAAuB,QAAQ,OAAO,GAG1D,IAAI,KACF,QACA,YACA,2CAA2C,QAAQ,mBACrD;EAEJ;EAEA,KAAK,SAAS;CAChB;;;;;;CAOA,AAAO,cAAc,cAAiC;EAEpD,OAAO,aAAa,MAAM,SAAS;GACjC,MAAM,eAAe,KAAK,QAAQ,OAAO,GAAG;GAC5C,OAAO,KAAK,aAAa,SAAS,YAAY;EAChD,CAAC;CACH;AACF"}
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":";;;;;;;;;;;;;;;;;;;AAkBA,SAAS,iBAAiB,aAA0B;CAClD,QAAQ,aAAR;EACE,KAAK,eACH,OAAO,OAAO,cAAc,WAAW;EACzC,KAAK,QACH,OAAO,OAAO,aAAa,WAAW;EACxC,KAAK,cACH,OAAO,OAAO,YAAY,WAAW;EACvC,SACE,OAAO,OAAO,MAAM,WAAW;CACnC;AACF;;;;;AAMA,MAAM,2BAA2B;;;;;AAMjC,IAAa,gBAAb,cAAmC,cAAc;;;cACxB;;;sBASW,CAAC,sBAAsB,qBAAqB;;;;;;;;;;;;;;;;;;;CAwB9E,IAAc,oBAA6B;EACzC,OAAO,YAAY,oBAAoB,iBAAiB,CAAC,YAAY;CACvE;;;;;;CAOA,MAAa,OAAO;EAClB,MAAM,aAAa,OAAO,IAAI,MAAM;EAEpC,IAAI,CAAC,YAAY;EAEjB,MAAM,OAAO,WAAW;EACxB,IAAI,KACF,QACA,cACA,gCAAgC,KAAK,MAAM,iBAAiB,YAAY,WAAW,EAAE,MACvF;EAEA,KAAK,OAAO,gBAAgB,WAAW,aAAa;EAEpD,UAAU,IAAI,eAAe,KAAK,IAAI;EAEtC,MAAM,oBAAoB,KAAK,IAAI;EAKnC,WAHgB,OAAO,IAAI,aAGV,CAAC;EAIlB,KAAK,qBAAqB;CAC5B;;;;;;;;CASA,AAAQ,uBAA6B;EACnC,IAAI,CAAC,KAAK,MACR;EAGF,MAAM,eAAe,OAAO,IAAI,aAAa;EAE7C,IAAI,cAAc,YAAY,OAC5B;EAGF,MAAM,eAAe,cAAc,QAAQ;EAC3C,MAAM,gBAAgB,cAAc,iBAAiB;EAErD,KAAK,KAAK,IAAI,cAAc,OAAO,UAAe,UAAe;GAC/D,MAAM,SAAS,OAAO,SAAS;GAE/B,OAAO,MAAM,KAAK,OAAO,WAAW,OAAO,MAAM,GAAG,EAAE,KAAK,MAAM;EACnE,CAAC;EAED,KAAK,KAAK,IAAI,eAAe,OAAO,UAAe,UAAe;GAChE,MAAM,SAAS,MAAM,OAAO,UAAU;GAEtC,OAAO,MAAM,KAAK,OAAO,WAAW,OAAO,MAAM,GAAG,EAAE,KAAK,MAAM;EACnE,CAAC;CACH;;;;;;CAOA,MAAa,QAAuB;EAClC,MAAM,aAAa,OAAO,IAAI,MAAM;EAEpC,IAAI,CAAC,cAAc,CAAC,KAAK,MAAM;EAK/B,MAAM,oBAAoB,KAAK;EAE/B,IAAI,YAAY,oBAAoB,eAClC,OAAO,cAAc,KAAK,IAAI;OAE9B,OAAO,KAAK,KAAK,IAAI;EAOvB,OAAO,+BAA+B,OAAO,WAAW,CAAC;EAEzD,MAAM,aAAa,OAAO,WAAW;EAOrC,IAAI,CAAC,mBACH,IAAI,KAAK,QAAQ,UAAU,GAAG,WAAW,qBAAqB;EAGhE,IAAI;GAIF,MAAM,sBAAsB,WAAW,MAAM,WAAW,QAAQ,WAAW;GAM3E,MAAM,eAAe,MAAM,KAAK,KAAK,OAAO;IAC1C,MAAM,WAAW;IACjB,MAAM,WAAW,QAAQ;GAC3B,CAAC;GAED,YAAY,cAAc,WAAW,IAAI;GAEzC,MAAM,UAAU,sBACd,cACA,OAAO,IAAI,aAAa,GAIxB,WAAW,QAAQ,WACrB;GAKA,mBAAmB;IACjB,cAAc,QAAQ;IACtB,KAAK,QAAQ;IACb,cAAc,QAAQ;IACtB,MAAM,WAAW;IACjB;IACA,WAAW,QAAQ;GACrB,CAAC;GAED,IAAI,CAAC,mBAAmB;IACtB,IAAI,QAAQ,QAAQ,cAAc,QAAQ,KAAK;IAE/C,IAAI,QAAQ,WACV,IAAI,KAAK,QAAQ,cAAc,QAAQ,SAAS;GAEpD;GAMA,IAAI,QAAQ,SACV,IAAI,KAAK,QAAQ,cAAc,QAAQ,OAAO;EAElD,SAAS,OAAO;GAId,MAAM,IAAI,MAAM,QAAQ,cAAc,KAAK;GAU3C,qBAAqB,KAAK;GAE1B,MAAM,IAAI,MAAM;GAEhB,QAAQ,KAAK,CAAC;EAChB;EAEA,KAAK,SAAS;CAChB;;;;;;CAOA,MAAa,UAAyB;EACpC,MAAM,KAAK,SAAS;EACpB,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,MAAM;CACnB;;;;CAKA,MAAa,WAA0B;EACrC,IAAI,CAAC,KAAK,QACR;EAGF,MAAM,SAAS,cAAc;EAE7B,IAAI,QAAQ;GACV,MAAM,UAAU,OAAO,IAAI,iCAAiC,wBAAwB;GAGpF,IAAI,CAAC,MAFiB,uBAAuB,QAAQ,OAAO,GAG1D,IAAI,KACF,QACA,YACA,2CAA2C,QAAQ,mBACrD;EAEJ;EAEA,KAAK,SAAS;CAChB;;;;;;CAOA,AAAO,cAAc,cAAiC;EAEpD,OAAO,aAAa,MAAM,SAAS;GACjC,MAAM,eAAe,KAAK,QAAQ,OAAO,GAAG;GAC5C,OAAO,KAAK,aAAa,SAAS,YAAY;EAChD,CAAC;CACH;AACF"}
@@ -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,IAAS,OAAA;EAXE;;;EA8BX,QAAA,IAAY,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,CAAA,GAAS,OAAA;EAXE;;;EA8BX,QAAA,CAAA,GAAY,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,IAAS,OAAA;EAXE;;;EA8BX,QAAA,IAAY,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,CAAA,GAAS,OAAA;EAXE;;;EA8BX,QAAA,CAAA,GAAY,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,IAAS,OAAA;EAoBG;;AAAO;;;EAAnB,QAAA,IAAY,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,CAAA,GAAS,OAAA;EAoBG;;AAAO;;;EAAnB,QAAA,CAAA,GAAY,OAAA;AAAA"}