@warlock.js/core 5.19.1 → 5.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +1136 -1073
  2. package/esm/benchmark/benchmark.mjs +34 -25
  3. package/esm/benchmark/benchmark.mjs.map +1 -1
  4. package/esm/benchmark/profiler.d.mts.map +1 -1
  5. package/esm/benchmark/profiler.mjs +8 -3
  6. package/esm/benchmark/profiler.mjs.map +1 -1
  7. package/esm/bootstrap.d.mts.map +1 -1
  8. package/esm/bootstrap.mjs +3 -0
  9. package/esm/bootstrap.mjs.map +1 -1
  10. package/esm/cache/database-cache-driver.d.mts +2 -2
  11. package/esm/cache/database-cache-driver.d.mts.map +1 -1
  12. package/esm/cache/database-cache-driver.mjs +3 -2
  13. package/esm/cache/database-cache-driver.mjs.map +1 -1
  14. package/esm/cli/cli-commands.manager.mjs +16 -14
  15. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  16. package/esm/cli/commands/add.command.mjs +1 -1
  17. package/esm/cli/commands/add.command.mjs.map +1 -1
  18. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +3 -2
  19. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generate.command.mjs +20 -0
  21. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  22. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +4 -2
  23. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
  24. package/esm/cli/commands/generate/generators/controller.generator.mjs +1 -1
  25. package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/migration.generator.mjs +22 -6
  27. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  28. package/esm/cli/commands/generate/generators/model.generator.mjs +22 -7
  29. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  30. package/esm/cli/commands/generate/generators/module.generator.mjs +8 -6
  31. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  32. package/esm/cli/commands/generate/generators/repository.generator.mjs +1 -1
  33. package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
  34. package/esm/cli/commands/generate/generators/resource.generator.mjs +1 -1
  35. package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
  36. package/esm/cli/commands/generate/generators/service.generator.mjs +1 -1
  37. package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
  38. package/esm/cli/commands/generate/templates/stubs.mjs +7 -7
  39. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  40. package/esm/cli/commands/generate/utils/writer.mjs +25 -1
  41. package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
  42. package/esm/cli/commands/routes/routes-command.action.mjs +5 -1
  43. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  44. package/esm/cli/commands/routes/with-console-on-stderr.mjs +24 -0
  45. package/esm/cli/commands/routes/with-console-on-stderr.mjs.map +1 -0
  46. package/esm/cli/commands/routes.command.mjs +3 -4
  47. package/esm/cli/commands/routes.command.mjs.map +1 -1
  48. package/esm/cli/commands/start-production.command.mjs +5 -5
  49. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  50. package/esm/cli/commands/storage-put.action.mjs +2 -1
  51. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  52. package/esm/cli/commands/update.command.mjs +1 -0
  53. package/esm/cli/commands/update.command.mjs.map +1 -1
  54. package/esm/cli/parse-cli-args.mjs +13 -1
  55. package/esm/cli/parse-cli-args.mjs.map +1 -1
  56. package/esm/commands/cli-command.d.mts.map +1 -1
  57. package/esm/commands/cli-command.mjs +4 -2
  58. package/esm/commands/cli-command.mjs.map +1 -1
  59. package/esm/commands/index.mjs +3 -0
  60. package/esm/config/config-key-from-path.mjs +20 -0
  61. package/esm/config/config-key-from-path.mjs.map +1 -0
  62. package/esm/config/config-loader.d.mts.map +1 -1
  63. package/esm/config/config-loader.mjs +2 -3
  64. package/esm/config/config-loader.mjs.map +1 -1
  65. package/esm/connectors/cache-connector.d.mts.map +1 -1
  66. package/esm/connectors/cache-connector.mjs +5 -0
  67. package/esm/connectors/cache-connector.mjs.map +1 -1
  68. package/esm/connectors/connectors-manager.d.mts +4 -0
  69. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  70. package/esm/connectors/connectors-manager.mjs +30 -2
  71. package/esm/connectors/connectors-manager.mjs.map +1 -1
  72. package/esm/connectors/single-server-warnings.mjs +33 -0
  73. package/esm/connectors/single-server-warnings.mjs.map +1 -0
  74. package/esm/connectors/socket-connector.d.mts +29 -0
  75. package/esm/connectors/socket-connector.d.mts.map +1 -1
  76. package/esm/connectors/socket-connector.mjs +77 -9
  77. package/esm/connectors/socket-connector.mjs.map +1 -1
  78. package/esm/connectors/storage.connector.d.mts.map +1 -1
  79. package/esm/connectors/storage.connector.mjs +6 -0
  80. package/esm/connectors/storage.connector.mjs.map +1 -1
  81. package/esm/connectors/types.d.mts +4 -2
  82. package/esm/connectors/types.d.mts.map +1 -1
  83. package/esm/connectors/types.mjs.map +1 -1
  84. package/esm/database/models/database-log/database-log.d.mts +47 -24
  85. package/esm/database/models/database-log/database-log.d.mts.map +1 -1
  86. package/esm/database/models/database-log/database-log.mjs +36 -4
  87. package/esm/database/models/database-log/database-log.mjs.map +1 -1
  88. package/esm/database/seed-command-action.mjs +10 -2
  89. package/esm/database/seed-command-action.mjs.map +1 -1
  90. package/esm/dev-server/development-server.mjs +15 -4
  91. package/esm/dev-server/development-server.mjs.map +1 -1
  92. package/esm/dev-server/file-event-handler.mjs +26 -6
  93. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  94. package/esm/dev-server/file-manager.d.mts.map +1 -1
  95. package/esm/dev-server/file-manager.mjs +2 -2
  96. package/esm/dev-server/file-manager.mjs.map +1 -1
  97. package/esm/dev-server/files-orchestrator.mjs +3 -1
  98. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  99. package/esm/dev-server/files-watcher.mjs +64 -5
  100. package/esm/dev-server/files-watcher.mjs.map +1 -1
  101. package/esm/dev-server/health-checker/files-healthcare.manager.mjs +31 -9
  102. package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
  103. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +11 -7
  104. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +1 -1
  105. package/esm/dev-server/loader/load-hook.mjs +1 -1
  106. package/esm/dev-server/loader/register-loader.mjs +1 -1
  107. package/esm/dev-server/manifest-manager.d.mts.map +1 -1
  108. package/esm/dev-server/manifest-manager.mjs +12 -6
  109. package/esm/dev-server/manifest-manager.mjs.map +1 -1
  110. package/esm/dev-server/parse-imports.mjs +9 -0
  111. package/esm/dev-server/parse-imports.mjs.map +1 -1
  112. package/esm/dev-server/register-dev-shortcuts.mjs +2 -1
  113. package/esm/dev-server/register-dev-shortcuts.mjs.map +1 -1
  114. package/esm/dev-server/route-types-publisher.mjs +60 -0
  115. package/esm/dev-server/route-types-publisher.mjs.map +1 -0
  116. package/esm/dev-server/shortcuts.mjs +2 -2
  117. package/esm/dev-server/shortcuts.mjs.map +1 -1
  118. package/esm/dev-server/special-file-patterns.mjs +18 -0
  119. package/esm/dev-server/special-file-patterns.mjs.map +1 -0
  120. package/esm/dev-server/special-files-collector.d.mts.map +1 -1
  121. package/esm/dev-server/special-files-collector.mjs +2 -5
  122. package/esm/dev-server/special-files-collector.mjs.map +1 -1
  123. package/esm/dev-server/start-development-server.mjs +1 -2
  124. package/esm/dev-server/start-development-server.mjs.map +1 -1
  125. package/esm/dev-server/supervisor.mjs +23 -3
  126. package/esm/dev-server/supervisor.mjs.map +1 -1
  127. package/esm/dev-server/tsconfig-manager.mjs +22 -12
  128. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  129. package/esm/dev-server/type-generator.mjs +10 -9
  130. package/esm/dev-server/type-generator.mjs.map +1 -1
  131. package/esm/encryption/encrypt.d.mts.map +1 -1
  132. package/esm/encryption/encrypt.mjs +16 -5
  133. package/esm/encryption/encrypt.mjs.map +1 -1
  134. package/esm/encryption/hash.d.mts.map +1 -1
  135. package/esm/encryption/hash.mjs +2 -0
  136. package/esm/encryption/hash.mjs.map +1 -1
  137. package/esm/encryption/password.d.mts.map +1 -1
  138. package/esm/encryption/password.mjs +3 -1
  139. package/esm/encryption/password.mjs.map +1 -1
  140. package/esm/generations/features/index.mjs +1 -1
  141. package/esm/generations/features/mysql.feature.mjs +9 -2
  142. package/esm/generations/features/mysql.feature.mjs.map +1 -1
  143. package/esm/generations/features/test.feature.mjs +2 -1
  144. package/esm/generations/features/test.feature.mjs.map +1 -1
  145. package/esm/generations/features/web.feature.mjs +4 -2
  146. package/esm/generations/features/web.feature.mjs.map +1 -1
  147. package/esm/http/build-rate-limit-options.d.mts +37 -0
  148. package/esm/http/build-rate-limit-options.d.mts.map +1 -0
  149. package/esm/http/build-rate-limit-options.mjs +37 -0
  150. package/esm/http/build-rate-limit-options.mjs.map +1 -0
  151. package/esm/http/csrf-origin-policy.d.mts.map +1 -1
  152. package/esm/http/csrf-origin-policy.mjs +5 -2
  153. package/esm/http/csrf-origin-policy.mjs.map +1 -1
  154. package/esm/http/flush-pending-cookies.mjs +52 -0
  155. package/esm/http/flush-pending-cookies.mjs.map +1 -0
  156. package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
  157. package/esm/http/middleware/cache-response-middleware.mjs +4 -2
  158. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  159. package/esm/http/middleware/idempotency.middleware.d.mts +5 -0
  160. package/esm/http/middleware/idempotency.middleware.d.mts.map +1 -1
  161. package/esm/http/middleware/idempotency.middleware.mjs +35 -3
  162. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  163. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  164. package/esm/http/middleware/inject-request-context.mjs +14 -3
  165. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  166. package/esm/http/middleware/rate-limit.middleware.d.mts +4 -3
  167. package/esm/http/middleware/rate-limit.middleware.d.mts.map +1 -1
  168. package/esm/http/middleware/rate-limit.middleware.mjs +4 -3
  169. package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
  170. package/esm/http/middleware/utils/cidr-match.d.mts +1 -1
  171. package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
  172. package/esm/http/middleware/utils/cidr-match.mjs +11 -2
  173. package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
  174. package/esm/http/middleware/utils/idempotency-key.mjs +1 -1
  175. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  176. package/esm/http/plugins.d.mts.map +1 -1
  177. package/esm/http/plugins.mjs +13 -5
  178. package/esm/http/plugins.mjs.map +1 -1
  179. package/esm/http/request.d.mts +12 -1
  180. package/esm/http/request.d.mts.map +1 -1
  181. package/esm/http/request.mjs +35 -7
  182. package/esm/http/request.mjs.map +1 -1
  183. package/esm/http/response.d.mts +16 -0
  184. package/esm/http/response.d.mts.map +1 -1
  185. package/esm/http/response.mjs +75 -37
  186. package/esm/http/response.mjs.map +1 -1
  187. package/esm/http/types.d.mts +26 -14
  188. package/esm/http/types.d.mts.map +1 -1
  189. package/esm/http/uploads/uploaded-file.controller.mjs +1 -1
  190. package/esm/http/uploads-config.mjs +2 -1
  191. package/esm/http/uploads-config.mjs.map +1 -1
  192. package/esm/http/uploads-types.d.mts +3 -3
  193. package/esm/image/image.d.mts +1 -1
  194. package/esm/image/image.d.mts.map +1 -1
  195. package/esm/image/image.mjs +25 -15
  196. package/esm/image/image.mjs.map +1 -1
  197. package/esm/index.d.mts +6 -4
  198. package/esm/index.mjs +7 -4
  199. package/esm/logger/logger.mjs +6 -1
  200. package/esm/logger/logger.mjs.map +1 -1
  201. package/esm/mail/config.d.mts.map +1 -1
  202. package/esm/mail/config.mjs +31 -21
  203. package/esm/mail/config.mjs.map +1 -1
  204. package/esm/mail/react-mail.mjs +6 -3
  205. package/esm/mail/react-mail.mjs.map +1 -1
  206. package/esm/mail/send-mail.d.mts.map +1 -1
  207. package/esm/mail/send-mail.mjs +40 -19
  208. package/esm/mail/send-mail.mjs.map +1 -1
  209. package/esm/production/assert-generated-imports.mjs +1 -0
  210. package/esm/production/assert-generated-imports.mjs.map +1 -1
  211. package/esm/production/build-contributions.mjs +3 -0
  212. package/esm/production/build-contributions.mjs.map +1 -1
  213. package/esm/production/esbuild-plugins.mjs +3 -2
  214. package/esm/production/esbuild-plugins.mjs.map +1 -1
  215. package/esm/production/production-builder.mjs +49 -37
  216. package/esm/production/production-builder.mjs.map +1 -1
  217. package/esm/production/production-supervisor.mjs +12 -5
  218. package/esm/production/production-supervisor.mjs.map +1 -1
  219. package/esm/production/route-registration-child.d.mts +1 -0
  220. package/esm/production/route-registration-child.mjs +65 -0
  221. package/esm/production/route-registration-child.mjs.map +1 -0
  222. package/esm/production/route-registration-snapshot.mjs +166 -0
  223. package/esm/production/route-registration-snapshot.mjs.map +1 -0
  224. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts +3 -1
  225. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
  226. package/esm/repositories/adapters/cascade/cascade-adapter.mjs +22 -12
  227. package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
  228. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +3 -3
  229. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
  230. package/esm/repositories/adapters/cascade/filter-applicator.mjs +88 -61
  231. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  232. package/esm/repositories/adapters/cascade/index.d.mts +1 -1
  233. package/esm/repositories/adapters/cascade/index.mjs +1 -1
  234. package/esm/repositories/contracts/query-builder.contract.d.mts +3 -3
  235. package/esm/repositories/contracts/types.d.mts +5 -0
  236. package/esm/repositories/contracts/types.d.mts.map +1 -1
  237. package/esm/repositories/index.d.mts +1 -1
  238. package/esm/repositories/index.mjs +1 -1
  239. package/esm/repositories/repository.manager.d.mts +65 -3
  240. package/esm/repositories/repository.manager.d.mts.map +1 -1
  241. package/esm/repositories/repository.manager.mjs +146 -57
  242. package/esm/repositories/repository.manager.mjs.map +1 -1
  243. package/esm/resource/resource-field-builder.d.mts.map +1 -1
  244. package/esm/resource/resource-field-builder.mjs +1 -0
  245. package/esm/resource/resource-field-builder.mjs.map +1 -1
  246. package/esm/resource/resource.d.mts +10 -3
  247. package/esm/resource/resource.d.mts.map +1 -1
  248. package/esm/resource/resource.mjs +19 -5
  249. package/esm/resource/resource.mjs.map +1 -1
  250. package/esm/restful/restful.d.mts +12 -5
  251. package/esm/restful/restful.d.mts.map +1 -1
  252. package/esm/restful/restful.mjs +47 -16
  253. package/esm/restful/restful.mjs.map +1 -1
  254. package/esm/router/build-route-rate-limit.mjs +22 -0
  255. package/esm/router/build-route-rate-limit.mjs.map +1 -0
  256. package/esm/router/dev-dispatch.mjs +60 -0
  257. package/esm/router/dev-dispatch.mjs.map +1 -0
  258. package/esm/router/route-builder.mjs +4 -1
  259. package/esm/router/route-builder.mjs.map +1 -1
  260. package/esm/router/route-registry.d.mts.map +1 -1
  261. package/esm/router/route-registry.mjs +2 -2
  262. package/esm/router/route-registry.mjs.map +1 -1
  263. package/esm/router/router.d.mts +1 -1
  264. package/esm/router/router.d.mts.map +1 -1
  265. package/esm/router/router.mjs +57 -39
  266. package/esm/router/router.mjs.map +1 -1
  267. package/esm/socket/index.d.mts +1 -1
  268. package/esm/socket/types.d.mts +25 -3
  269. package/esm/socket/types.d.mts.map +1 -1
  270. package/esm/storage/drivers/cloud-driver.d.mts +26 -0
  271. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  272. package/esm/storage/drivers/cloud-driver.mjs +100 -16
  273. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  274. package/esm/storage/drivers/do-spaces-driver.d.mts +7 -0
  275. package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
  276. package/esm/storage/drivers/do-spaces-driver.mjs +9 -2
  277. package/esm/storage/drivers/do-spaces-driver.mjs.map +1 -1
  278. package/esm/storage/drivers/local-driver.d.mts +16 -5
  279. package/esm/storage/drivers/local-driver.d.mts.map +1 -1
  280. package/esm/storage/drivers/local-driver.mjs +71 -28
  281. package/esm/storage/drivers/local-driver.mjs.map +1 -1
  282. package/esm/storage/index.d.mts +3 -1
  283. package/esm/storage/index.mjs +4 -2
  284. package/esm/storage/scoped-storage.d.mts +27 -1
  285. package/esm/storage/scoped-storage.d.mts.map +1 -1
  286. package/esm/storage/scoped-storage.mjs +40 -8
  287. package/esm/storage/scoped-storage.mjs.map +1 -1
  288. package/esm/storage/storage.d.mts +11 -5
  289. package/esm/storage/storage.d.mts.map +1 -1
  290. package/esm/storage/storage.mjs +39 -16
  291. package/esm/storage/storage.mjs.map +1 -1
  292. package/esm/storage/types.d.mts +43 -4
  293. package/esm/storage/types.d.mts.map +1 -1
  294. package/esm/storage/utils/classify-ip-address.d.mts +13 -0
  295. package/esm/storage/utils/classify-ip-address.d.mts.map +1 -0
  296. package/esm/storage/utils/classify-ip-address.mjs +98 -0
  297. package/esm/storage/utils/classify-ip-address.mjs.map +1 -0
  298. package/esm/storage/utils/safe-fetch.d.mts +3 -12
  299. package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
  300. package/esm/storage/utils/safe-fetch.mjs +3 -48
  301. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  302. package/esm/storage/utils/storage-capability-error.d.mts +25 -0
  303. package/esm/storage/utils/storage-capability-error.d.mts.map +1 -0
  304. package/esm/storage/utils/storage-capability-error.mjs +26 -0
  305. package/esm/storage/utils/storage-capability-error.mjs.map +1 -0
  306. package/esm/updater/update-warlock-packages.mjs +11 -1
  307. package/esm/updater/update-warlock-packages.mjs.map +1 -1
  308. package/esm/use-cases/use-case-broadcast.mjs +7 -1
  309. package/esm/use-cases/use-case-broadcast.mjs.map +1 -1
  310. package/esm/use-cases/use-cases-registry.mjs +21 -2
  311. package/esm/use-cases/use-cases-registry.mjs.map +1 -1
  312. package/esm/utils/database-log.mjs +0 -3
  313. package/esm/utils/database-log.mjs.map +1 -1
  314. package/esm/utils/queue.d.mts +6 -4
  315. package/esm/utils/queue.d.mts.map +1 -1
  316. package/esm/utils/queue.mjs +20 -9
  317. package/esm/utils/queue.mjs.map +1 -1
  318. package/esm/validation/database/exists-except-current-id.mjs +6 -2
  319. package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
  320. package/esm/validation/database/unique-except-current-id.mjs +6 -2
  321. package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
  322. package/esm/validation/init.mjs +6 -2
  323. package/esm/validation/init.mjs.map +1 -1
  324. package/esm/validation/validateAll.mjs +3 -2
  325. package/esm/validation/validateAll.mjs.map +1 -1
  326. package/esm/warlock-config/types.d.mts +14 -1
  327. package/esm/warlock-config/types.d.mts.map +1 -1
  328. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  329. package/esm/warlock-config/warlock-config.manager.mjs +3 -1
  330. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  331. package/esm/warlock-config/warn-deprecated-config.mjs +25 -0
  332. package/esm/warlock-config/warn-deprecated-config.mjs.map +1 -0
  333. package/llms-full.txt +71 -42
  334. package/llms.txt +41 -41
  335. package/package.json +21 -15
  336. package/skills/add-connector/SKILL.md +5 -1
  337. package/skills/benchmark-code/SKILL.md +1 -1
  338. package/skills/build-restful/SKILL.md +1 -1
  339. package/skills/build-url/SKILL.md +1 -1
  340. package/skills/configure-app/SKILL.md +7 -1
  341. package/skills/create-controller/SKILL.md +1 -1
  342. package/skills/create-module/SKILL.md +1 -1
  343. package/skills/define-resource/SKILL.md +1 -1
  344. package/skills/encrypt-data/SKILL.md +1 -1
  345. package/skills/hash-password/SKILL.md +1 -1
  346. package/skills/health-checks/SKILL.md +1 -1
  347. package/skills/index.md +30 -0
  348. package/skills/lower-stage3-decorators/SKILL.md +1 -1
  349. package/skills/process-image/SKILL.md +1 -1
  350. package/skills/register-route/SKILL.md +1 -1
  351. package/skills/request-memo/SKILL.md +1 -1
  352. package/skills/request-tracing/SKILL.md +1 -1
  353. package/skills/resolve-path/SKILL.md +1 -1
  354. package/skills/retry-operation/SKILL.md +1 -1
  355. package/skills/run-app/SKILL.md +1 -1
  356. package/skills/send-mail/SKILL.md +1 -1
  357. package/skills/send-response/SKILL.md +1 -1
  358. package/skills/store-file/SKILL.md +11 -1
  359. package/skills/test-http/SKILL.md +1 -1
  360. package/skills/test-service/SKILL.md +1 -1
  361. package/skills/update-packages/SKILL.md +1 -1
  362. package/skills/upload-file/SKILL.md +1 -1
  363. package/skills/use-app-context/SKILL.md +1 -1
  364. package/skills/use-localization/SKILL.md +1 -1
  365. package/skills/use-middleware/SKILL.md +7 -2
  366. package/skills/use-model-transformers/SKILL.md +1 -1
  367. package/skills/use-repository/SKILL.md +1 -1
  368. package/skills/use-request-locals/SKILL.md +1 -1
  369. package/skills/validate-input/SKILL.md +1 -1
  370. package/skills/warlock-conventions/SKILL.md +1 -1
  371. package/skills/warlock-doctor/SKILL.md +1 -1
  372. package/skills/warlock-routes/SKILL.md +1 -1
  373. package/skills/wire-socket/SKILL.md +5 -1
  374. package/skills/write-cli-command/SKILL.md +1 -1
  375. package/skills/write-middleware/SKILL.md +1 -1
  376. package/skills/write-seeder/SKILL.md +1 -1
  377. package/skills/write-use-case/SKILL.md +1 -1
  378. package/esm/_virtual/_rolldown/runtime.mjs +0 -7
@@ -19,7 +19,13 @@ async function broadcastUseCaseResult(params) {
19
19
  if (!channels || channels.length === 0) return;
20
20
  const projector = typeof broadcast === "object" ? broadcast.output : void 0;
21
21
  const event = typeof broadcast === "object" && broadcast.event ? broadcast.event : name;
22
- const payload = projector ? projector(output, result) : output;
22
+ let payload = output;
23
+ if (projector) try {
24
+ payload = projector(output, result);
25
+ } catch (error) {
26
+ log.error("use-cases", name, "broadcast projector failed", { error });
27
+ return;
28
+ }
23
29
  const broadcastEvent = {
24
30
  useCase: name,
25
31
  event,
@@ -1 +1 @@
1
- {"version":3,"file":"use-case-broadcast.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-case-broadcast.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\nimport type {\n UseCaseBroadcastEvent,\n UseCaseBroadcastOption,\n UseCaseConfigurations,\n UseCaseResult,\n} from \"./types\";\n\n/**\n * Broadcast a successful use-case result through the globally-configured channels.\n *\n * Fires only when the use case opted in (`broadcast`) and the global transport is\n * enabled with at least one channel. Fan-out is isolated via `Promise.allSettled` —\n * a failing channel is logged and never affects the use case or sibling channels.\n *\n * @example\n * await broadcastUseCaseResult({ name, id, output, result, broadcast, config });\n */\nexport async function broadcastUseCaseResult<Output>(params: {\n name: string;\n id: string;\n output: Output;\n result: UseCaseResult<Output>;\n broadcast?: UseCaseBroadcastOption<Output>;\n config?: UseCaseConfigurations[\"broadcast\"];\n}): Promise<void> {\n const { name, id, output, result, broadcast, config } = params;\n\n if (!broadcast) {\n return;\n }\n\n if (config?.enabled === false) {\n return;\n }\n\n const channels = config?.channels;\n\n if (!channels || channels.length === 0) {\n return;\n }\n\n const projector = typeof broadcast === \"object\" ? broadcast.output : undefined;\n const event = typeof broadcast === \"object\" && broadcast.event ? broadcast.event : name;\n const payload = projector ? projector(output, result) : output;\n\n const broadcastEvent: UseCaseBroadcastEvent = {\n useCase: name,\n event,\n id,\n at: new Date(),\n payload,\n };\n\n const outcomes = await Promise.allSettled(\n channels.map((channel) => channel.broadcast(broadcastEvent)),\n );\n\n for (const outcome of outcomes) {\n if (outcome.status === \"rejected\") {\n log.error(\"use-cases\", name, \"broadcast channel failed\", { error: outcome.reason });\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,eAAsB,uBAA+B,QAOnC;CAChB,MAAM,EAAE,MAAM,IAAI,QAAQ,QAAQ,WAAW,WAAW;CAExD,IAAI,CAAC,WACH;CAGF,IAAI,QAAQ,YAAY,OACtB;CAGF,MAAM,WAAW,QAAQ;CAEzB,IAAI,CAAC,YAAY,SAAS,WAAW,GACnC;CAGF,MAAM,YAAY,OAAO,cAAc,WAAW,UAAU,SAAS;CACrE,MAAM,QAAQ,OAAO,cAAc,YAAY,UAAU,QAAQ,UAAU,QAAQ;CACnF,MAAM,UAAU,YAAY,UAAU,QAAQ,MAAM,IAAI;CAExD,MAAM,iBAAwC;EAC5C,SAAS;EACT;EACA;EACA,oBAAI,IAAI,KAAK;EACb;CACF;CAEA,MAAM,WAAW,MAAM,QAAQ,WAC7B,SAAS,KAAK,YAAY,QAAQ,UAAU,cAAc,CAAC,CAC7D;CAEA,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,WAAW,YACrB,IAAI,MAAM,aAAa,MAAM,4BAA4B,EAAE,OAAO,QAAQ,OAAO,CAAC;AAGxF"}
1
+ {"version":3,"file":"use-case-broadcast.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-case-broadcast.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\nimport type {\n UseCaseBroadcastEvent,\n UseCaseBroadcastOption,\n UseCaseConfigurations,\n UseCaseResult,\n} from \"./types\";\n\n/**\n * Broadcast a successful use-case result through the globally-configured channels.\n *\n * Fires only when the use case opted in (`broadcast`) and the global transport is\n * enabled with at least one channel. Fan-out is isolated via `Promise.allSettled` —\n * a failing channel is logged and never affects the use case or sibling channels.\n *\n * @example\n * await broadcastUseCaseResult({ name, id, output, result, broadcast, config });\n */\nexport async function broadcastUseCaseResult<Output>(params: {\n name: string;\n id: string;\n output: Output;\n result: UseCaseResult<Output>;\n broadcast?: UseCaseBroadcastOption<Output>;\n config?: UseCaseConfigurations[\"broadcast\"];\n}): Promise<void> {\n const { name, id, output, result, broadcast, config } = params;\n\n if (!broadcast) {\n return;\n }\n\n if (config?.enabled === false) {\n return;\n }\n\n const channels = config?.channels;\n\n if (!channels || channels.length === 0) {\n return;\n }\n\n const projector = typeof broadcast === \"object\" ? broadcast.output : undefined;\n const event = typeof broadcast === \"object\" && broadcast.event ? broadcast.event : name;\n let payload: unknown = output;\n\n if (projector) {\n try {\n payload = projector(output, result);\n } catch (error) {\n log.error(\"use-cases\", name, \"broadcast projector failed\", { error });\n\n return;\n }\n }\n\n const broadcastEvent: UseCaseBroadcastEvent = {\n useCase: name,\n event,\n id,\n at: new Date(),\n payload,\n };\n\n const outcomes = await Promise.allSettled(\n channels.map((channel) => channel.broadcast(broadcastEvent)),\n );\n\n for (const outcome of outcomes) {\n if (outcome.status === \"rejected\") {\n log.error(\"use-cases\", name, \"broadcast channel failed\", { error: outcome.reason });\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,eAAsB,uBAA+B,QAOnC;CAChB,MAAM,EAAE,MAAM,IAAI,QAAQ,QAAQ,WAAW,WAAW;CAExD,IAAI,CAAC,WACH;CAGF,IAAI,QAAQ,YAAY,OACtB;CAGF,MAAM,WAAW,QAAQ;CAEzB,IAAI,CAAC,YAAY,SAAS,WAAW,GACnC;CAGF,MAAM,YAAY,OAAO,cAAc,WAAW,UAAU,SAAS;CACrE,MAAM,QAAQ,OAAO,cAAc,YAAY,UAAU,QAAQ,UAAU,QAAQ;CACnF,IAAI,UAAmB;CAEvB,IAAI,WACF,IAAI;EACF,UAAU,UAAU,QAAQ,MAAM;CACpC,SAAS,OAAO;EACd,IAAI,MAAM,aAAa,MAAM,8BAA8B,EAAE,MAAM,CAAC;EAEpE;CACF;CAGF,MAAM,iBAAwC;EAC5C,SAAS;EACT;EACA;EACA,oBAAI,IAAI,KAAK;EACb;CACF;CAEA,MAAM,WAAW,MAAM,QAAQ,WAC7B,SAAS,KAAK,YAAY,QAAQ,UAAU,cAAc,CAAC,CAC7D;CAEA,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,WAAW,YACrB,IAAI,MAAM,aAAa,MAAM,4BAA4B,EAAE,OAAO,QAAQ,OAAO,CAAC;AAGxF"}
@@ -99,13 +99,32 @@ async function getUseCaseHistory(name) {
99
99
  * unbounded — oldest entries (and their ids) are evicted past MAX_HISTORY_ENTRIES.
100
100
  */
101
101
  async function addUseCaseHistory(name, result) {
102
+ if (!(config.get("use-cases")?.history?.enabled ?? process.env.NODE_ENV !== "production")) return;
103
+ const current = (historyWrites.get(name) ?? Promise.resolve()).catch(() => void 0).then(() => writeHistory(name, result));
104
+ historyWrites.set(name, current);
105
+ try {
106
+ await current;
107
+ } finally {
108
+ if (historyWrites.get(name) === current) historyWrites.delete(name);
109
+ }
110
+ }
111
+ /**
112
+ * Per-use-case write chain so the list read-modify-write never interleaves
113
+ * within a process (keeps the cap enforced under concurrency).
114
+ */
115
+ const historyWrites = /* @__PURE__ */ new Map();
116
+ async function writeHistory(name, result) {
102
117
  const useCaseConfig = config.get("use-cases");
103
- if (useCaseConfig?.history?.enabled === false) return;
104
118
  const ttl = resolveHistoryTtl(useCaseConfig);
105
119
  const maxEntries = useCaseConfig?.history?.maxEntries ?? 100;
106
120
  const key = `use-case:history:${name}:${result.id}`;
107
121
  const listKey = `use-case:history:${name}:list`;
108
- await cache.set(key, result, ttl);
122
+ const projection = {
123
+ ...result,
124
+ output: void 0,
125
+ ctx: void 0
126
+ };
127
+ await cache.set(key, projection, ttl);
109
128
  const list = await cache.get(listKey) || [];
110
129
  list.push(result.id);
111
130
  while (list.length > maxEntries) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-cases-registry.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-cases-registry.ts"],"sourcesContent":["import { cache } from \"@warlock.js/cache\";\nimport { log } from \"@warlock.js/logger\";\nimport { config } from \"../config\";\nimport { type RegisteredUseCase, type UseCaseConfigurations, type UseCaseResult } from \"./types\";\n\n/**\n * Store registered use cases\n */\nconst useCaseRegister = new Map<string, RegisteredUseCase>();\n\n/**\n * Register a use case\n */\nexport function $registerUseCase<Output, Input>(\n name: string,\n useCase: RegisteredUseCase<Output, Input>,\n) {\n if (useCaseRegister.has(name) && process.env.NODE_ENV !== \"production\") {\n log.warn(\"use-cases\", \"registering\", `Use case \"${name}\" is already registered. Overwriting.`);\n }\n\n useCaseRegister.set(name, useCase);\n}\n\n/**\n * Unregister a use case.\n *\n * Dropping the history namespace is best-effort: `$cleanup` is synchronous, so the\n * async cache removal is fire-and-forget with a `.catch` guard — a rejecting cache\n * driver must never turn cleanup into an unhandled rejection.\n */\nexport function $unregisterUseCase(name: string) {\n useCaseRegister.delete(name);\n\n Promise.resolve(cache.removeNamespace(`use-case:history:${name}`)).catch((error) => {\n log.error(\"use-cases\", name, \"history namespace removal failed\", { error });\n });\n}\n\n/**\n * Get a use case.\n *\n * Returns the `RegisteredUseCase` shape — including the runtime `calls` counters\n * the registry actually stores — so consumers can read call stats without an\n * ad-hoc cast.\n */\nexport function getUseCase<Output, Input>(name: string) {\n return useCaseRegister.get(name) as RegisteredUseCase<Output, Input> | undefined;\n}\n\n/**\n * Get all use cases\n */\nexport function getUseCases() {\n return useCaseRegister;\n}\n\n/**\n * Increase use case success calls\n */\nexport function increaseUseCaseSuccessCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.success++;\n useCase.calls.total++;\n\n return useCase.calls.success;\n }\n\n return 0;\n}\n\n/**\n * Increase use case failed calls\n */\nexport function increaseUseCaseFailedCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.failed++;\n useCase.calls.total++;\n\n return useCase.calls.failed;\n }\n\n return 0;\n}\n\n/**\n * Resolve the history cache TTL (seconds) from config.\n * `false` means \"use the cache driver's default\", represented as `undefined`.\n */\nfunction resolveHistoryTtl(useCaseConfig?: UseCaseConfigurations): number | undefined {\n const ttlConfig = useCaseConfig?.history?.ttl ?? 3600; // 1 hour default\n\n return ttlConfig === false ? undefined : ttlConfig;\n}\n\n/**\n * Get use case history from cache.\n *\n * Prunes ids whose entries have expired so the list can't accumulate dead refs.\n */\nexport async function getUseCaseHistory(name: string): Promise<UseCaseResult<any>[]> {\n const listKey = `use-case:history:${name}:list`;\n const ids = (await cache.get<string[]>(listKey)) || [];\n\n const entries = await Promise.all(\n ids.map(async (id: string) => ({\n id,\n result: await cache.get<UseCaseResult<any>>(`use-case:history:${name}:${id}`),\n })),\n );\n\n const liveEntries = entries.filter((entry) => Boolean(entry.result));\n\n if (liveEntries.length !== ids.length) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n await cache.set(\n listKey,\n liveEntries.map((entry) => entry.id),\n resolveHistoryTtl(useCaseConfig),\n );\n }\n\n return liveEntries.map((entry) => entry.result) as UseCaseResult<any>[];\n}\n\n/**\n * Add use case history to cache, capping the per-use-case list so it can't grow\n * unbounded — oldest entries (and their ids) are evicted past MAX_HISTORY_ENTRIES.\n */\nexport async function addUseCaseHistory(name: string, result: UseCaseResult<any>) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n if (useCaseConfig?.history?.enabled === false) return;\n\n const ttl = resolveHistoryTtl(useCaseConfig);\n const maxEntries = useCaseConfig?.history?.maxEntries ?? 100;\n const key = `use-case:history:${name}:${result.id}`;\n const listKey = `use-case:history:${name}:list`;\n\n await cache.set(key, result, ttl);\n\n const list = (await cache.get<string[]>(listKey)) || [];\n\n list.push(result.id);\n\n while (list.length > maxEntries) {\n const evictedId = list.shift();\n\n if (evictedId) {\n await cache.remove(`use-case:history:${name}:${evictedId}`);\n }\n }\n\n await cache.set(listKey, list, ttl);\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,kCAAkB,IAAI,IAA+B;;;;AAK3D,SAAgB,iBACd,MACA,SACA;CACA,IAAI,gBAAgB,IAAI,IAAI,KAAK,QAAQ,IAAI,aAAa,cACxD,IAAI,KAAK,aAAa,eAAe,aAAa,KAAK,sCAAsC;CAG/F,gBAAgB,IAAI,MAAM,OAAO;AACnC;;;;;;;;AASA,SAAgB,mBAAmB,MAAc;CAC/C,gBAAgB,OAAO,IAAI;CAE3B,QAAQ,QAAQ,MAAM,gBAAgB,oBAAoB,MAAM,CAAC,CAAC,CAAC,OAAO,UAAU;EAClF,IAAI,MAAM,aAAa,MAAM,oCAAoC,EAAE,MAAM,CAAC;CAC5E,CAAC;AACH;;;;;;;;AASA,SAAgB,WAA0B,MAAc;CACtD,OAAO,gBAAgB,IAAI,IAAI;AACjC;;;;AAKA,SAAgB,cAAc;CAC5B,OAAO;AACT;;;;AAKA,SAAgB,4BAA4B,MAAsB;CAChE,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;AAKA,SAAgB,2BAA2B,MAAsB;CAC/D,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;;AAMA,SAAS,kBAAkB,eAA2D;CACpF,MAAM,YAAY,eAAe,SAAS,OAAO;CAEjD,OAAO,cAAc,QAAQ,SAAY;AAC3C;;;;;;AAOA,eAAsB,kBAAkB,MAA6C;CACnF,MAAM,UAAU,oBAAoB,KAAK;CACzC,MAAM,MAAO,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CASrD,MAAM,eAAc,MAPE,QAAQ,IAC5B,IAAI,IAAI,OAAO,QAAgB;EAC7B;EACA,QAAQ,MAAM,MAAM,IAAwB,oBAAoB,KAAK,GAAG,IAAI;CAC9E,EAAE,CACJ,EAE2B,CAAC,QAAQ,UAAU,QAAQ,MAAM,MAAM,CAAC;CAEnE,IAAI,YAAY,WAAW,IAAI,QAAQ;EACrC,MAAM,gBAAgB,OAAO,IAA2B,WAAW;EAEnE,MAAM,MAAM,IACV,SACA,YAAY,KAAK,UAAU,MAAM,EAAE,GACnC,kBAAkB,aAAa,CACjC;CACF;CAEA,OAAO,YAAY,KAAK,UAAU,MAAM,MAAM;AAChD;;;;;AAMA,eAAsB,kBAAkB,MAAc,QAA4B;CAChF,MAAM,gBAAgB,OAAO,IAA2B,WAAW;CAEnE,IAAI,eAAe,SAAS,YAAY,OAAO;CAE/C,MAAM,MAAM,kBAAkB,aAAa;CAC3C,MAAM,aAAa,eAAe,SAAS,cAAc;CACzD,MAAM,MAAM,oBAAoB,KAAK,GAAG,OAAO;CAC/C,MAAM,UAAU,oBAAoB,KAAK;CAEzC,MAAM,MAAM,IAAI,KAAK,QAAQ,GAAG;CAEhC,MAAM,OAAQ,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CAEtD,KAAK,KAAK,OAAO,EAAE;CAEnB,OAAO,KAAK,SAAS,YAAY;EAC/B,MAAM,YAAY,KAAK,MAAM;EAE7B,IAAI,WACF,MAAM,MAAM,OAAO,oBAAoB,KAAK,GAAG,WAAW;CAE9D;CAEA,MAAM,MAAM,IAAI,SAAS,MAAM,GAAG;AACpC"}
1
+ {"version":3,"file":"use-cases-registry.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-cases-registry.ts"],"sourcesContent":["import { cache } from \"@warlock.js/cache\";\nimport { log } from \"@warlock.js/logger\";\nimport { config } from \"../config\";\nimport { type RegisteredUseCase, type UseCaseConfigurations, type UseCaseResult } from \"./types\";\n\n/**\n * Store registered use cases\n */\nconst useCaseRegister = new Map<string, RegisteredUseCase>();\n\n/**\n * Register a use case\n */\nexport function $registerUseCase<Output, Input>(\n name: string,\n useCase: RegisteredUseCase<Output, Input>,\n) {\n if (useCaseRegister.has(name) && process.env.NODE_ENV !== \"production\") {\n log.warn(\"use-cases\", \"registering\", `Use case \"${name}\" is already registered. Overwriting.`);\n }\n\n useCaseRegister.set(name, useCase);\n}\n\n/**\n * Unregister a use case.\n *\n * Dropping the history namespace is best-effort: `$cleanup` is synchronous, so the\n * async cache removal is fire-and-forget with a `.catch` guard — a rejecting cache\n * driver must never turn cleanup into an unhandled rejection.\n */\nexport function $unregisterUseCase(name: string) {\n useCaseRegister.delete(name);\n\n Promise.resolve(cache.removeNamespace(`use-case:history:${name}`)).catch((error) => {\n log.error(\"use-cases\", name, \"history namespace removal failed\", { error });\n });\n}\n\n/**\n * Get a use case.\n *\n * Returns the `RegisteredUseCase` shape — including the runtime `calls` counters\n * the registry actually stores — so consumers can read call stats without an\n * ad-hoc cast.\n */\nexport function getUseCase<Output, Input>(name: string) {\n return useCaseRegister.get(name) as RegisteredUseCase<Output, Input> | undefined;\n}\n\n/**\n * Get all use cases\n */\nexport function getUseCases() {\n return useCaseRegister;\n}\n\n/**\n * Increase use case success calls\n */\nexport function increaseUseCaseSuccessCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.success++;\n useCase.calls.total++;\n\n return useCase.calls.success;\n }\n\n return 0;\n}\n\n/**\n * Increase use case failed calls\n */\nexport function increaseUseCaseFailedCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.failed++;\n useCase.calls.total++;\n\n return useCase.calls.failed;\n }\n\n return 0;\n}\n\n/**\n * Resolve the history cache TTL (seconds) from config.\n * `false` means \"use the cache driver's default\", represented as `undefined`.\n */\nfunction resolveHistoryTtl(useCaseConfig?: UseCaseConfigurations): number | undefined {\n const ttlConfig = useCaseConfig?.history?.ttl ?? 3600; // 1 hour default\n\n return ttlConfig === false ? undefined : ttlConfig;\n}\n\n/**\n * Get use case history from cache.\n *\n * Prunes ids whose entries have expired so the list can't accumulate dead refs.\n */\nexport async function getUseCaseHistory(name: string): Promise<UseCaseResult<any>[]> {\n const listKey = `use-case:history:${name}:list`;\n const ids = (await cache.get<string[]>(listKey)) || [];\n\n const entries = await Promise.all(\n ids.map(async (id: string) => ({\n id,\n result: await cache.get<UseCaseResult<any>>(`use-case:history:${name}:${id}`),\n })),\n );\n\n const liveEntries = entries.filter((entry) => Boolean(entry.result));\n\n if (liveEntries.length !== ids.length) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n await cache.set(\n listKey,\n liveEntries.map((entry) => entry.id),\n resolveHistoryTtl(useCaseConfig),\n );\n }\n\n return liveEntries.map((entry) => entry.result) as UseCaseResult<any>[];\n}\n\n/**\n * Add use case history to cache, capping the per-use-case list so it can't grow\n * unbounded — oldest entries (and their ids) are evicted past MAX_HISTORY_ENTRIES.\n */\nexport async function addUseCaseHistory(name: string, result: UseCaseResult<any>) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n // On by default outside production; opt in to production with `history.enabled: true`.\n const enabled = useCaseConfig?.history?.enabled ?? process.env.NODE_ENV !== \"production\";\n\n if (!enabled) return;\n\n const previous = historyWrites.get(name) ?? Promise.resolve();\n const current = previous.catch(() => undefined).then(() => writeHistory(name, result));\n\n historyWrites.set(name, current);\n\n try {\n await current;\n } finally {\n if (historyWrites.get(name) === current) {\n historyWrites.delete(name);\n }\n }\n}\n\n/**\n * Per-use-case write chain so the list read-modify-write never interleaves\n * within a process (keeps the cap enforced under concurrency).\n */\nconst historyWrites = new Map<string, Promise<unknown>>();\n\nasync function writeHistory(name: string, result: UseCaseResult<any>) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n const ttl = resolveHistoryTtl(useCaseConfig);\n const maxEntries = useCaseConfig?.history?.maxEntries ?? 100;\n const key = `use-case:history:${name}:${result.id}`;\n const listKey = `use-case:history:${name}:list`;\n\n // Store a projection only: `output` and `ctx` (tokens, payloads) never reach the cache.\n const projection = { ...result, output: undefined, ctx: undefined } as unknown as UseCaseResult<any>;\n\n await cache.set(key, projection, ttl);\n\n const list = (await cache.get<string[]>(listKey)) || [];\n\n list.push(result.id);\n\n while (list.length > maxEntries) {\n const evictedId = list.shift();\n\n if (evictedId) {\n await cache.remove(`use-case:history:${name}:${evictedId}`);\n }\n }\n\n await cache.set(listKey, list, ttl);\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,kCAAkB,IAAI,IAA+B;;;;AAK3D,SAAgB,iBACd,MACA,SACA;CACA,IAAI,gBAAgB,IAAI,IAAI,KAAK,QAAQ,IAAI,aAAa,cACxD,IAAI,KAAK,aAAa,eAAe,aAAa,KAAK,sCAAsC;CAG/F,gBAAgB,IAAI,MAAM,OAAO;AACnC;;;;;;;;AASA,SAAgB,mBAAmB,MAAc;CAC/C,gBAAgB,OAAO,IAAI;CAE3B,QAAQ,QAAQ,MAAM,gBAAgB,oBAAoB,MAAM,CAAC,CAAC,CAAC,OAAO,UAAU;EAClF,IAAI,MAAM,aAAa,MAAM,oCAAoC,EAAE,MAAM,CAAC;CAC5E,CAAC;AACH;;;;;;;;AASA,SAAgB,WAA0B,MAAc;CACtD,OAAO,gBAAgB,IAAI,IAAI;AACjC;;;;AAKA,SAAgB,cAAc;CAC5B,OAAO;AACT;;;;AAKA,SAAgB,4BAA4B,MAAsB;CAChE,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;AAKA,SAAgB,2BAA2B,MAAsB;CAC/D,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;;AAMA,SAAS,kBAAkB,eAA2D;CACpF,MAAM,YAAY,eAAe,SAAS,OAAO;CAEjD,OAAO,cAAc,QAAQ,SAAY;AAC3C;;;;;;AAOA,eAAsB,kBAAkB,MAA6C;CACnF,MAAM,UAAU,oBAAoB,KAAK;CACzC,MAAM,MAAO,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CASrD,MAAM,eAAc,MAPE,QAAQ,IAC5B,IAAI,IAAI,OAAO,QAAgB;EAC7B;EACA,QAAQ,MAAM,MAAM,IAAwB,oBAAoB,KAAK,GAAG,IAAI;CAC9E,EAAE,CACJ,EAE2B,CAAC,QAAQ,UAAU,QAAQ,MAAM,MAAM,CAAC;CAEnE,IAAI,YAAY,WAAW,IAAI,QAAQ;EACrC,MAAM,gBAAgB,OAAO,IAA2B,WAAW;EAEnE,MAAM,MAAM,IACV,SACA,YAAY,KAAK,UAAU,MAAM,EAAE,GACnC,kBAAkB,aAAa,CACjC;CACF;CAEA,OAAO,YAAY,KAAK,UAAU,MAAM,MAAM;AAChD;;;;;AAMA,eAAsB,kBAAkB,MAAc,QAA4B;CAMhF,IAAI,EALkB,OAAO,IAA2B,WAG5B,CAAC,EAAE,SAAS,WAAW,QAAQ,IAAI,aAAa,eAE9D;CAGd,MAAM,WADW,cAAc,IAAI,IAAI,KAAK,QAAQ,QAAQ,EACpC,CAAC,YAAY,MAAS,CAAC,CAAC,WAAW,aAAa,MAAM,MAAM,CAAC;CAErF,cAAc,IAAI,MAAM,OAAO;CAE/B,IAAI;EACF,MAAM;CACR,UAAU;EACR,IAAI,cAAc,IAAI,IAAI,MAAM,SAC9B,cAAc,OAAO,IAAI;CAE7B;AACF;;;;;AAMA,MAAM,gCAAgB,IAAI,IAA8B;AAExD,eAAe,aAAa,MAAc,QAA4B;CACpE,MAAM,gBAAgB,OAAO,IAA2B,WAAW;CACnE,MAAM,MAAM,kBAAkB,aAAa;CAC3C,MAAM,aAAa,eAAe,SAAS,cAAc;CACzD,MAAM,MAAM,oBAAoB,KAAK,GAAG,OAAO;CAC/C,MAAM,UAAU,oBAAoB,KAAK;CAGzC,MAAM,aAAa;EAAE,GAAG;EAAQ,QAAQ;EAAW,KAAK;CAAU;CAElE,MAAM,MAAM,IAAI,KAAK,YAAY,GAAG;CAEpC,MAAM,OAAQ,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CAEtD,KAAK,KAAK,OAAO,EAAE;CAEnB,OAAO,KAAK,SAAS,YAAY;EAC/B,MAAM,YAAY,KAAK,MAAM;EAE7B,IAAI,WACF,MAAM,MAAM,OAAO,oBAAoB,KAAK,GAAG,WAAW;CAE9D;CAEA,MAAM,MAAM,IAAI,SAAS,MAAM,GAAG;AACpC"}
@@ -31,9 +31,6 @@ var DatabaseLog = class extends LogChannel {
31
31
  if (message instanceof Error) {
32
32
  data.stack = message.stack;
33
33
  data.content = message.message;
34
- } else {
35
- data.content = message;
36
- data.stack = (/* @__PURE__ */ new Error()).stack;
37
34
  }
38
35
  try {
39
36
  await this.model.create(data);
@@ -1 +1 @@
1
- {"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"sourcesContent":["import type { ChildModel, Model } from \"@warlock.js/cascade\";\nimport type { BasicLogConfigurations, LoggingData } from \"@warlock.js/logger\";\nimport { LogChannel, type LogContract, type LogMessage } from \"@warlock.js/logger\";\nimport { DatabaseLogModel } from \"../database/models/database-log\";\n\nexport type DatabaseLogOptions = BasicLogConfigurations & {\n /**\n * Model to use for logging\n */\n model?: typeof DatabaseLogModel;\n};\n\nexport class DatabaseLog extends LogChannel<DatabaseLogOptions> implements LogContract {\n /**\n * {@inheritdoc}\n */\n public name = \"database\";\n\n /**\n * Database model\n */\n public get model(): ChildModel<Model> {\n return this.config(\"model\") ?? DatabaseLogModel;\n }\n\n /**\n * {@inheritdoc}\n */\n public async log(log: LoggingData) {\n const { module, action, message, type: level } = log;\n\n if (!this.model.getDataSource().driver?.isConnected) return;\n\n if (!this.shouldBeLogged(log)) return;\n\n const data: LogMessage = {\n module,\n action,\n content: message,\n level,\n date: new Date().toISOString(),\n };\n\n if (message instanceof Error) {\n data.stack = message.stack;\n data.content = message.message;\n } else {\n data.content = message;\n data.stack = new Error().stack;\n }\n\n try {\n await this.model.create(data);\n } catch (error) {\n console.log(\"Error\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,IAAa,cAAb,cAAiC,WAAsD;;;cAIvE;;;;;CAKd,IAAW,QAA2B;EACpC,OAAO,KAAK,OAAO,OAAO,KAAK;CACjC;;;;CAKA,MAAa,IAAI,KAAkB;EACjC,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC,QAAQ,aAAa;EAErD,IAAI,CAAC,KAAK,eAAe,GAAG,GAAG;EAE/B,MAAM,OAAmB;GACvB;GACA;GACA,SAAS;GACT;GACA,uBAAM,IAAI,KAAK,EAAC,CAAC,YAAY;EAC/B;EAEA,IAAI,mBAAmB,OAAO;GAC5B,KAAK,QAAQ,QAAQ;GACrB,KAAK,UAAU,QAAQ;EACzB,OAAO;GACL,KAAK,UAAU;GACf,KAAK,yBAAQ,IAAI,MAAM,EAAC,CAAC;EAC3B;EAEA,IAAI;GACF,MAAM,KAAK,MAAM,OAAO,IAAI;EAC9B,SAAS,OAAO;GACd,QAAQ,IAAI,SAAS,KAAK;EAC5B;CACF;AACF"}
1
+ {"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"sourcesContent":["import type { ChildModel, Model } from \"@warlock.js/cascade\";\nimport type { BasicLogConfigurations, LoggingData } from \"@warlock.js/logger\";\nimport { LogChannel, type LogContract, type LogMessage } from \"@warlock.js/logger\";\nimport { DatabaseLogModel } from \"../database/models/database-log\";\n\nexport type DatabaseLogOptions = BasicLogConfigurations & {\n /**\n * Model to use for logging\n */\n model?: typeof DatabaseLogModel;\n};\n\nexport class DatabaseLog extends LogChannel<DatabaseLogOptions> implements LogContract {\n /**\n * {@inheritdoc}\n */\n public name = \"database\";\n\n /**\n * Database model\n */\n public get model(): ChildModel<Model> {\n return this.config(\"model\") ?? DatabaseLogModel;\n }\n\n /**\n * {@inheritdoc}\n */\n public async log(log: LoggingData) {\n const { module, action, message, type: level } = log;\n\n if (!this.model.getDataSource().driver?.isConnected) return;\n\n if (!this.shouldBeLogged(log)) return;\n\n const data: LogMessage = {\n module,\n action,\n content: message,\n level,\n date: new Date().toISOString(),\n };\n\n if (message instanceof Error) {\n data.stack = message.stack;\n data.content = message.message;\n }\n\n try {\n await this.model.create(data);\n } catch (error) {\n console.log(\"Error\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,IAAa,cAAb,cAAiC,WAAsD;;;cAIvE;;;;;CAKd,IAAW,QAA2B;EACpC,OAAO,KAAK,OAAO,OAAO,KAAK;CACjC;;;;CAKA,MAAa,IAAI,KAAkB;EACjC,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC,QAAQ,aAAa;EAErD,IAAI,CAAC,KAAK,eAAe,GAAG,GAAG;EAE/B,MAAM,OAAmB;GACvB;GACA;GACA,SAAS;GACT;GACA,uBAAM,IAAI,KAAK,EAAC,CAAC,YAAY;EAC/B;EAEA,IAAI,mBAAmB,OAAO;GAC5B,KAAK,QAAQ,QAAQ;GACrB,KAAK,UAAU,QAAQ;EACzB;EAEA,IAAI;GACF,MAAM,KAAK,MAAM,OAAO,IAAI;EAC9B,SAAS,OAAO;GACd,QAAQ,IAAI,SAAS,KAAK;EAC5B;CACF;AACF"}
@@ -18,7 +18,9 @@ declare class Queue<T> {
18
18
  /** Flag to determine if execution should be parallel or sequential. */
19
19
  private readonly executeInParallel;
20
20
  /** The batch size for processing items. */
21
- private readonly batchSize;
21
+ private readonly batchSize?;
22
+ /** Called when the execute function rejects. */
23
+ private readonly onError;
22
24
  /** Whether the current queue is busy executing */
23
25
  private isExecuting;
24
26
  /**
@@ -29,7 +31,7 @@ declare class Queue<T> {
29
31
  * @param executeInParallel - Whether to execute the function in parallel or sequentially.
30
32
  * @param batchSize - The number of items to process in each batch.
31
33
  */
32
- constructor(executeFn: (items: T[]) => Promise<void>, executeInParallel: boolean | undefined, executeEvery: number | undefined, batchSize: number, maxSize?: number);
34
+ constructor(executeFn: (items: T[]) => Promise<void>, executeInParallel?: boolean, executeEvery?: number, batchSize?: number, maxSize?: number, onError?: (error: unknown) => void);
33
35
  /**
34
36
  * Adds an item to the queue.
35
37
  * Triggers execution if the queue reaches the maximum size.
@@ -42,8 +44,8 @@ declare class Queue<T> {
42
44
  */
43
45
  private startTimer;
44
46
  /**
45
- * Executes the function with the current items in the queue.
46
- * Processes items in batches and resets the timer.
47
+ * Executes the function with the queued items until the queue is drained.
48
+ * Rejections from the execute function are passed to `onError`.
47
49
  */
48
50
  private execute;
49
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.mts","names":[],"sources":["../../../../../../../core/src/utils/queue.ts"],"mappings":";;AAKA;;;;cAAa,KAAA;EAqDU;EAAA,QAnDb,KAAA;EAmDc;EAAA,iBAhDL,OAAA;EAHT;EAAA,iBAMS,QAAA;EAAA;EAAA,iBAGA,SAAA;EAGT;EAAA,QAAA,KAAA;EAMS;EAAA,iBAHA,iBAAA;;mBAGA,SAAA;EAcH;EAAA,QAXN,WAAA;EAWN;;;;;;;;cAAA,SAAA,GAAY,KAAA,EAAO,CAAA,OAAQ,OAAA,QAC3B,iBAAA,uBACA,YAAA,sBACA,SAAA,UACA,OAAA;EAyCY;;AAAO;;;;EA1Bd,OAAA,CAAQ,IAAA,EAAM,CAAA;;;;UAcb,UAAA;;;;;UAYM,OAAA;AAAA"}
1
+ {"version":3,"file":"queue.d.mts","names":[],"sources":["../../../../../../../core/src/utils/queue.ts"],"mappings":";;AAKA;;;;cAAa,KAAA;EA0DU;EAAA,QAxDb,KAAA;EAwDc;EAAA,iBArDL,OAAA;EAHT;EAAA,iBAMS,QAAA;EAAA;EAAA,iBAGA,SAAA;EAGT;EAAA,QAAA,KAAA;EAMS;EAAA,iBAHA,iBAAA;EAST;EAAA,iBANS,SAAA;EAiBI;EAAA,iBAdJ,OAAA;EAcY;EAAA,QAXrB,WAAA;EAYN;;;;;;;;cADA,SAAA,GAAY,KAAA,EAAO,CAAA,OAAQ,OAAA,QAC3B,iBAAA,YACA,YAAA,WACA,SAAA,WACA,OAAA,WACA,OAAA,IAAU,KAAA;EA8BJ;;;AAaa;;;EA3Bd,OAAA,CAAQ,IAAA,EAAM,CAAA;;;;UAcb,UAAA;;;;;UAaM,OAAA;AAAA"}
@@ -13,10 +13,11 @@ var Queue = class {
13
13
  * @param executeInParallel - Whether to execute the function in parallel or sequentially.
14
14
  * @param batchSize - The number of items to process in each batch.
15
15
  */
16
- constructor(executeFn, executeInParallel = true, executeEvery = 5e3, batchSize, maxSize) {
16
+ constructor(executeFn, executeInParallel = true, executeEvery = 5e3, batchSize, maxSize, onError = (error) => console.error("Queue execution failed", error)) {
17
17
  this.items = [];
18
18
  this.timer = null;
19
19
  this.isExecuting = false;
20
+ this.onError = onError;
20
21
  this.executeFn = executeFn;
21
22
  this.maxSize = maxSize;
22
23
  this.interval = executeEvery;
@@ -32,7 +33,7 @@ var Queue = class {
32
33
  enqueue(item) {
33
34
  this.items.push(item);
34
35
  if (this.maxSize && this.items.length >= this.maxSize) this.execute();
35
- if (!this.timer) this.startTimer();
36
+ if (!this.timer && !this.isExecuting) this.startTimer();
36
37
  }
37
38
  /**
38
39
  * Starts a timer to execute the queue after the specified interval.
@@ -41,23 +42,33 @@ var Queue = class {
41
42
  this.timer = setInterval(() => {
42
43
  if (this.items.length > 0) this.execute();
43
44
  }, this.interval);
45
+ this.timer.unref?.();
44
46
  }
45
47
  /**
46
- * Executes the function with the current items in the queue.
47
- * Processes items in batches and resets the timer.
48
+ * Executes the function with the queued items until the queue is drained.
49
+ * Rejections from the execute function are passed to `onError`.
48
50
  */
49
51
  async execute() {
50
52
  if (this.timer) {
51
53
  clearInterval(this.timer);
52
54
  this.timer = null;
53
55
  }
56
+ if (this.isExecuting) return;
54
57
  this.isExecuting = true;
55
- if (this.batchSize) {
56
- const itemsToProcess = this.items.splice(0, this.batchSize);
57
- if (this.executeInParallel) await Promise.all(itemsToProcess.map((item) => this.executeFn([item])));
58
- else for (const item of itemsToProcess) await this.executeFn([item]);
58
+ try {
59
+ while (this.items.length > 0) {
60
+ const batch = this.items.splice(0, this.batchSize || this.items.length);
61
+ try {
62
+ if (!this.batchSize) await this.executeFn(batch);
63
+ else if (this.executeInParallel) await Promise.all(batch.map((item) => this.executeFn([item])));
64
+ else for (const item of batch) await this.executeFn([item]);
65
+ } catch (error) {
66
+ this.onError(error);
67
+ }
68
+ }
69
+ } finally {
70
+ this.isExecuting = false;
59
71
  }
60
- this.isExecuting = false;
61
72
  }
62
73
  };
63
74
 
@@ -1 +1 @@
1
- {"version":3,"file":"queue.mjs","names":[],"sources":["../../../../../../../core/src/utils/queue.ts"],"sourcesContent":["/**\n * A utility class for managing a queue of operations.\n * Allows enqueuing values and executing a function when the queue reaches a certain size or after a specified interval.\n * Supports both parallel and sequential execution of the queued operations.\n */\nexport class Queue<T> {\n /** The items currently in the queue. */\n private items: T[] = [];\n\n /** The maximum size of the queue before triggering execution. */\n private readonly maxSize?: number;\n\n /** The interval in milliseconds after which the queue will be executed if not already triggered by size. */\n private readonly interval: number;\n\n /** The function to execute with the items in the queue. */\n private readonly executeFn: (items: T[]) => Promise<void>;\n\n /** Timer for managing the interval-based execution. */\n private timer: NodeJS.Timeout | null = null;\n\n /** Flag to determine if execution should be parallel or sequential. */\n private readonly executeInParallel: boolean;\n\n /** The batch size for processing items. */\n private readonly batchSize: number;\n\n /** Whether the current queue is busy executing */\n private isExecuting = false;\n\n /**\n * Constructs a new Queue instance.\n * @param executeFn - The function to execute with the items in the queue.\n * @param maxSize - The maximum number of items before the queue is executed.\n * @param executeEvery - The time in milliseconds after which the queue is executed if not already triggered.\n * @param executeInParallel - Whether to execute the function in parallel or sequentially.\n * @param batchSize - The number of items to process in each batch.\n */\n public constructor(\n executeFn: (items: T[]) => Promise<void>,\n executeInParallel: boolean = true,\n executeEvery: number = 5000,\n batchSize: number,\n maxSize?: number,\n ) {\n this.executeFn = executeFn;\n this.maxSize = maxSize;\n this.interval = executeEvery;\n this.executeInParallel = executeInParallel;\n this.batchSize = batchSize;\n }\n\n /**\n * Adds an item to the queue.\n * Triggers execution if the queue reaches the maximum size.\n * Starts a timer if not already running.\n * @param item - The item to add to the queue.\n */\n public enqueue(item: T): void {\n this.items.push(item);\n if (this.maxSize && this.items.length >= this.maxSize) {\n this.execute();\n }\n\n if (!this.timer) {\n this.startTimer();\n }\n }\n\n /**\n * Starts a timer to execute the queue after the specified interval.\n */\n private startTimer(): void {\n this.timer = setInterval(() => {\n if (this.items.length > 0) {\n this.execute();\n }\n }, this.interval);\n }\n\n /**\n * Executes the function with the current items in the queue.\n * Processes items in batches and resets the timer.\n */\n private async execute(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n this.isExecuting = true;\n\n // Now there are couple scenarios:\n // 1. Batch size has value, we need to check if its going to be executed in parallel or sequentially\n // 2. Batch size is not provided, we will execute all items in a single call\n if (this.batchSize) {\n const itemsToProcess = this.items.splice(0, this.batchSize);\n if (this.executeInParallel) {\n await Promise.all(itemsToProcess.map((item) => this.executeFn([item])));\n } else {\n for (const item of itemsToProcess) {\n await this.executeFn([item]);\n }\n }\n }\n\n this.isExecuting = false;\n }\n}\n"],"mappings":";;;;;;AAKA,IAAa,QAAb,MAAsB;;;;;;;;;CAiCpB,AAAO,YACL,WACA,oBAA6B,MAC7B,eAAuB,KACvB,WACA,SACA;eArCmB,CAAC;eAYiB;qBASjB;EAiBpB,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,oBAAoB;EACzB,KAAK,YAAY;CACnB;;;;;;;CAQA,AAAO,QAAQ,MAAe;EAC5B,KAAK,MAAM,KAAK,IAAI;EACpB,IAAI,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,SAC5C,KAAK,QAAQ;EAGf,IAAI,CAAC,KAAK,OACR,KAAK,WAAW;CAEpB;;;;CAKA,AAAQ,aAAmB;EACzB,KAAK,QAAQ,kBAAkB;GAC7B,IAAI,KAAK,MAAM,SAAS,GACtB,KAAK,QAAQ;EAEjB,GAAG,KAAK,QAAQ;CAClB;;;;;CAMA,MAAc,UAAyB;EACrC,IAAI,KAAK,OAAO;GACd,cAAc,KAAK,KAAK;GACxB,KAAK,QAAQ;EACf;EAEA,KAAK,cAAc;EAKnB,IAAI,KAAK,WAAW;GAClB,MAAM,iBAAiB,KAAK,MAAM,OAAO,GAAG,KAAK,SAAS;GAC1D,IAAI,KAAK,mBACP,MAAM,QAAQ,IAAI,eAAe,KAAK,SAAS,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAEtE,KAAK,MAAM,QAAQ,gBACjB,MAAM,KAAK,UAAU,CAAC,IAAI,CAAC;EAGjC;EAEA,KAAK,cAAc;CACrB;AACF"}
1
+ {"version":3,"file":"queue.mjs","names":[],"sources":["../../../../../../../core/src/utils/queue.ts"],"sourcesContent":["/**\n * A utility class for managing a queue of operations.\n * Allows enqueuing values and executing a function when the queue reaches a certain size or after a specified interval.\n * Supports both parallel and sequential execution of the queued operations.\n */\nexport class Queue<T> {\n /** The items currently in the queue. */\n private items: T[] = [];\n\n /** The maximum size of the queue before triggering execution. */\n private readonly maxSize?: number;\n\n /** The interval in milliseconds after which the queue will be executed if not already triggered by size. */\n private readonly interval: number;\n\n /** The function to execute with the items in the queue. */\n private readonly executeFn: (items: T[]) => Promise<void>;\n\n /** Timer for managing the interval-based execution. */\n private timer: NodeJS.Timeout | null = null;\n\n /** Flag to determine if execution should be parallel or sequential. */\n private readonly executeInParallel: boolean;\n\n /** The batch size for processing items. */\n private readonly batchSize?: number;\n\n /** Called when the execute function rejects. */\n private readonly onError: (error: unknown) => void;\n\n /** Whether the current queue is busy executing */\n private isExecuting = false;\n\n /**\n * Constructs a new Queue instance.\n * @param executeFn - The function to execute with the items in the queue.\n * @param maxSize - The maximum number of items before the queue is executed.\n * @param executeEvery - The time in milliseconds after which the queue is executed if not already triggered.\n * @param executeInParallel - Whether to execute the function in parallel or sequentially.\n * @param batchSize - The number of items to process in each batch.\n */\n public constructor(\n executeFn: (items: T[]) => Promise<void>,\n executeInParallel: boolean = true,\n executeEvery: number = 5000,\n batchSize?: number,\n maxSize?: number,\n onError: (error: unknown) => void = (error) => console.error(\"Queue execution failed\", error),\n ) {\n this.onError = onError;\n this.executeFn = executeFn;\n this.maxSize = maxSize;\n this.interval = executeEvery;\n this.executeInParallel = executeInParallel;\n this.batchSize = batchSize;\n }\n\n /**\n * Adds an item to the queue.\n * Triggers execution if the queue reaches the maximum size.\n * Starts a timer if not already running.\n * @param item - The item to add to the queue.\n */\n public enqueue(item: T): void {\n this.items.push(item);\n if (this.maxSize && this.items.length >= this.maxSize) {\n void this.execute();\n }\n\n if (!this.timer && !this.isExecuting) {\n this.startTimer();\n }\n }\n\n /**\n * Starts a timer to execute the queue after the specified interval.\n */\n private startTimer(): void {\n this.timer = setInterval(() => {\n if (this.items.length > 0) {\n void this.execute();\n }\n }, this.interval);\n this.timer.unref?.();\n }\n\n /**\n * Executes the function with the queued items until the queue is drained.\n * Rejections from the execute function are passed to `onError`.\n */\n private async execute(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n\n // the running drain loop will pick up newly enqueued items\n if (this.isExecuting) return;\n\n this.isExecuting = true;\n\n try {\n while (this.items.length > 0) {\n // Without a batch size, all items go in a single call\n const batch = this.items.splice(0, this.batchSize || this.items.length);\n\n try {\n if (!this.batchSize) {\n await this.executeFn(batch);\n } else if (this.executeInParallel) {\n await Promise.all(batch.map((item) => this.executeFn([item])));\n } else {\n for (const item of batch) {\n await this.executeFn([item]);\n }\n }\n } catch (error) {\n this.onError(error);\n }\n }\n } finally {\n this.isExecuting = false;\n }\n }\n}\n"],"mappings":";;;;;;AAKA,IAAa,QAAb,MAAsB;;;;;;;;;CAoCpB,AAAO,YACL,WACA,oBAA6B,MAC7B,eAAuB,KACvB,WACA,SACA,WAAqC,UAAU,QAAQ,MAAM,0BAA0B,KAAK,GAC5F;eAzCmB,CAAC;eAYiB;qBAYjB;EAkBpB,KAAK,UAAU;EACf,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,oBAAoB;EACzB,KAAK,YAAY;CACnB;;;;;;;CAQA,AAAO,QAAQ,MAAe;EAC5B,KAAK,MAAM,KAAK,IAAI;EACpB,IAAI,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,SAC5C,AAAK,KAAK,QAAQ;EAGpB,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,aACvB,KAAK,WAAW;CAEpB;;;;CAKA,AAAQ,aAAmB;EACzB,KAAK,QAAQ,kBAAkB;GAC7B,IAAI,KAAK,MAAM,SAAS,GACtB,AAAK,KAAK,QAAQ;EAEtB,GAAG,KAAK,QAAQ;EAChB,KAAK,MAAM,QAAQ;CACrB;;;;;CAMA,MAAc,UAAyB;EACrC,IAAI,KAAK,OAAO;GACd,cAAc,KAAK,KAAK;GACxB,KAAK,QAAQ;EACf;EAGA,IAAI,KAAK,aAAa;EAEtB,KAAK,cAAc;EAEnB,IAAI;GACF,OAAO,KAAK,MAAM,SAAS,GAAG;IAE5B,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,KAAK,aAAa,KAAK,MAAM,MAAM;IAEtE,IAAI;KACF,IAAI,CAAC,KAAK,WACR,MAAM,KAAK,UAAU,KAAK;UACrB,IAAI,KAAK,mBACd,MAAM,QAAQ,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;UAE7D,KAAK,MAAM,QAAQ,OACjB,MAAM,KAAK,UAAU,CAAC,IAAI,CAAC;IAGjC,SAAS,OAAO;KACd,KAAK,QAAQ,KAAK;IACpB;GACF;EACF,UAAU;GACR,KAAK,cAAc;EACrB;CACF;AACF"}
@@ -11,11 +11,15 @@ const existsExceptCurrentIdRule = {
11
11
  name: "existsExceptCurrentId",
12
12
  defaultErrorMessage: "The :input must exist",
13
13
  async validate(value, context) {
14
- const { Model, query, column = context.key, exceptCurrentIdColumn = "id" } = this.context.options;
14
+ const { Model, query, column = context.key, exceptCurrentIdColumn = "id", param = "id" } = this.context.options;
15
15
  const { request } = useRequestStore();
16
16
  const dbQuery = resolveModelClass(Model).query();
17
17
  dbQuery.where(column, value);
18
- dbQuery.where(exceptCurrentIdColumn, "!=", request.int("id"));
18
+ const rawId = request.input(param);
19
+ if (rawId !== void 0 && rawId !== null && rawId !== "") {
20
+ const currentId = typeof rawId === "string" && /^\d{1,15}$/.test(rawId) ? Number(rawId) : rawId;
21
+ dbQuery.where(exceptCurrentIdColumn, "!=", currentId);
22
+ }
19
23
  if (query) await query({
20
24
  query: dbQuery,
21
25
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"exists-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { ExistsExceptCurrentIdRuleOptions } from \"../types\";\n\n/**\n * Exists except current ID rule\n */\nexport const existsExceptCurrentIdRule: SchemaRule<ExistsExceptCurrentIdRuleOptions> = {\n name: \"existsExceptCurrentId\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.int(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,wBAAwB,SACtB,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAC3B,QAAQ,MAAM,uBAAuB,MAAM,QAAQ,IAAI,IAAI,CAAC;EAE5D,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,aAAa,YAAY,MAAM,OAAO;CAC1D;AACF"}
1
+ {"version":3,"file":"exists-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { ExistsExceptCurrentIdRuleOptions } from \"../types\";\n\n/**\n * Exists except current ID rule\n */\nexport const existsExceptCurrentIdRule: SchemaRule<ExistsExceptCurrentIdRuleOptions> = {\n name: \"existsExceptCurrentId\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n param = \"id\",\n } = this.context.options as ExistsExceptCurrentIdRuleOptions & { param?: string };\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n // Read the current id from the configured route param, keeping ObjectId/UUID strings intact\n const rawId = request.input(param);\n\n if (rawId !== undefined && rawId !== null && rawId !== \"\") {\n const currentId =\n typeof rawId === \"string\" && /^\\d{1,15}$/.test(rawId) ? Number(rawId) : rawId;\n\n dbQuery.where(exceptCurrentIdColumn, \"!=\", currentId);\n }\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,wBAAwB,MACxB,QAAQ,SACN,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAE3B,MAAM,QAAQ,QAAQ,MAAM,KAAK;EAEjC,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;GACzD,MAAM,YACJ,OAAO,UAAU,YAAY,aAAa,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI;GAE1E,QAAQ,MAAM,uBAAuB,MAAM,SAAS;EACtD;EAEA,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,aAAa,YAAY,MAAM,OAAO;CAC1D;AACF"}
@@ -11,11 +11,15 @@ const uniqueExceptCurrentIdRule = {
11
11
  name: "uniqueExceptCurrentId",
12
12
  defaultErrorMessage: "The :input must be unique",
13
13
  async validate(value, context) {
14
- const { Model, column = context.key, exceptCurrentIdColumn = "id", query } = this.context.options;
14
+ const { Model, column = context.key, exceptCurrentIdColumn = "id", query, param = "id" } = this.context.options;
15
15
  const { request } = useRequestStore();
16
16
  const dbQuery = resolveModelClass(Model).query();
17
17
  dbQuery.where(column, value);
18
- dbQuery.where(exceptCurrentIdColumn, "!=", request.input("id"));
18
+ const rawId = request.input(param);
19
+ if (rawId !== void 0 && rawId !== null && rawId !== "") {
20
+ const currentId = typeof rawId === "string" && /^\d{1,15}$/.test(rawId) ? Number(rawId) : rawId;
21
+ dbQuery.where(exceptCurrentIdColumn, "!=", currentId);
22
+ }
19
23
  if (query) await query({
20
24
  query: dbQuery,
21
25
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"unique-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/unique-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { UniqueExceptCurrentIdRuleOptions } from \"../types\";\n/**\n * Unique except current ID rule\n */\nexport const uniqueExceptCurrentIdRule: SchemaRule<UniqueExceptCurrentIdRuleOptions> = {\n name: \"uniqueExceptCurrentId\",\n defaultErrorMessage: \"The :input must be unique\",\n async validate(value: any, context) {\n const {\n Model,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n query,\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.input(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? invalidRule(this, context) : VALID_RULE;\n },\n};\n"],"mappings":";;;;;;;;;AAOA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,SAAS,QAAQ,KACjB,wBAAwB,MACxB,UACE,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAC3B,QAAQ,MAAM,uBAAuB,MAAM,QAAQ,MAAM,IAAI,CAAC;EAE9D,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,YAAY,MAAM,OAAO,IAAI;CACjD;AACF"}
1
+ {"version":3,"file":"unique-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/unique-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { UniqueExceptCurrentIdRuleOptions } from \"../types\";\n/**\n * Unique except current ID rule\n */\nexport const uniqueExceptCurrentIdRule: SchemaRule<UniqueExceptCurrentIdRuleOptions> = {\n name: \"uniqueExceptCurrentId\",\n defaultErrorMessage: \"The :input must be unique\",\n async validate(value: any, context) {\n const {\n Model,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n query,\n param = \"id\",\n } = this.context.options as UniqueExceptCurrentIdRuleOptions & { param?: string };\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n // Read the current id from the configured route param, keeping ObjectId/UUID strings intact\n const rawId = request.input(param);\n\n if (rawId !== undefined && rawId !== null && rawId !== \"\") {\n const currentId =\n typeof rawId === \"string\" && /^\\d{1,15}$/.test(rawId) ? Number(rawId) : rawId;\n\n dbQuery.where(exceptCurrentIdColumn, \"!=\", currentId);\n }\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? invalidRule(this, context) : VALID_RULE;\n },\n};\n"],"mappings":";;;;;;;;;AAOA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,SAAS,QAAQ,KACjB,wBAAwB,MACxB,OACA,QAAQ,SACN,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAE3B,MAAM,QAAQ,QAAQ,MAAM,KAAK;EAEjC,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;GACzD,MAAM,YACJ,OAAO,UAAU,YAAY,aAAa,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI;GAE1E,QAAQ,MAAM,uBAAuB,MAAM,SAAS;EACtD;EAEA,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,YAAY,MAAM,OAAO,IAAI;CACjD;AACF"}
@@ -5,7 +5,7 @@ import { databasePlugin } from "./plugins/database-plugin.mjs";
5
5
  import { filePlugin } from "./plugins/file-plugin.mjs";
6
6
  import { localizedPlugin } from "./plugins/localized-plugin.mjs";
7
7
  import "./plugins/index.mjs";
8
- import { configureSeal, registerPlugin } from "@warlock.js/seal";
8
+ import { configureSeal, getSealConfig, registerPlugin } from "@warlock.js/seal";
9
9
 
10
10
  //#region ../core/src/validation/init.ts
11
11
  /**
@@ -14,7 +14,6 @@ import { configureSeal, registerPlugin } from "@warlock.js/seal";
14
14
  * This file configures Seal to use Warlock's localization system
15
15
  */
16
16
  configureSeal({
17
- firstErrorOnly: config.key("validation.firstErrorOnly", true),
18
17
  translateRule({ rule, attributes }) {
19
18
  const translateRule = config.key("validation.translateRule");
20
19
  if (translateRule) return translateRule({
@@ -38,6 +37,11 @@ configureSeal({
38
37
  return output === translationKey ? attribute : output;
39
38
  }
40
39
  });
40
+ Object.defineProperty(getSealConfig(), "firstErrorOnly", {
41
+ configurable: true,
42
+ enumerable: true,
43
+ get: () => config.key("validation.firstErrorOnly", true)
44
+ });
41
45
  registerPlugin(databasePlugin);
42
46
  registerPlugin(filePlugin);
43
47
  registerPlugin(localizedPlugin);
@@ -1 +1 @@
1
- {"version":3,"file":"init.mjs","names":[],"sources":["../../../../../../../core/src/validation/init.ts"],"sourcesContent":["/**\n * Initialize Seal with Warlock Framework Settings\n *\n * This file configures Seal to use Warlock's localization system\n */\n\n// Auto-register framework plugins\nimport { configureSeal, registerPlugin } from \"@warlock.js/seal\";\nimport { config } from \"../config\";\nimport { t } from \"../http/middleware/inject-request-context\";\nimport { databasePlugin, filePlugin, localizedPlugin } from \"./plugins\";\n\n// Configure Seal to use Warlock's localization\nconfigureSeal({\n firstErrorOnly: config.key(\"validation.firstErrorOnly\", true),\n translateRule({ rule, attributes }) {\n const translateRule = config.key(\"validation.translateRule\");\n\n if (translateRule) {\n return translateRule({ rule, attributes });\n }\n\n const translationGroup = config.key(\"validation.translationGroup\", \"validation\");\n\n const translationKey = `${translationGroup}.${rule.name}`;\n const translation = t(translationKey, attributes);\n\n return translation === translationKey\n ? rule.errorMessage || rule.defaultErrorMessage\n : translation;\n },\n\n translateAttribute({ attribute, context, rule }) {\n const translateAttribute = config.key(\"validation.translateAttribute\");\n\n if (translateAttribute) {\n return translateAttribute({ attribute, context, rule });\n }\n\n const attributeGroup = config.key(\"validation.attributeGroup\") ?? \"attributes\";\n\n const translationKey = `${attributeGroup ? attributeGroup + \".\" : \"\"}${attribute}`;\n\n const output = t(translationKey, context.allValues);\n return output === translationKey ? attribute : output;\n },\n});\n\n// Register plugins to inject methods\nregisterPlugin(databasePlugin);\nregisterPlugin(filePlugin);\nregisterPlugin(localizedPlugin);\n"],"mappings":";;;;;;;;;;;;;;;AAaA,cAAc;CACZ,gBAAgB,OAAO,IAAI,6BAA6B,IAAI;CAC5D,cAAc,EAAE,MAAM,cAAc;EAClC,MAAM,gBAAgB,OAAO,IAAI,0BAA0B;EAE3D,IAAI,eACF,OAAO,cAAc;GAAE;GAAM;EAAW,CAAC;EAK3C,MAAM,iBAAiB,GAFE,OAAO,IAAI,+BAA+B,YAE1B,EAAE,GAAG,KAAK;EACnD,MAAM,cAAc,EAAE,gBAAgB,UAAU;EAEhD,OAAO,gBAAgB,iBACnB,KAAK,gBAAgB,KAAK,sBAC1B;CACN;CAEA,mBAAmB,EAAE,WAAW,SAAS,QAAQ;EAC/C,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;EAErE,IAAI,oBACF,OAAO,mBAAmB;GAAE;GAAW;GAAS;EAAK,CAAC;EAGxD,MAAM,iBAAiB,OAAO,IAAI,2BAA2B,KAAK;EAElE,MAAM,iBAAiB,GAAG,iBAAiB,iBAAiB,MAAM,KAAK;EAEvE,MAAM,SAAS,EAAE,gBAAgB,QAAQ,SAAS;EAClD,OAAO,WAAW,iBAAiB,YAAY;CACjD;AACF,CAAC;AAGD,eAAe,cAAc;AAC7B,eAAe,UAAU;AACzB,eAAe,eAAe"}
1
+ {"version":3,"file":"init.mjs","names":[],"sources":["../../../../../../../core/src/validation/init.ts"],"sourcesContent":["/**\n * Initialize Seal with Warlock Framework Settings\n *\n * This file configures Seal to use Warlock's localization system\n */\n\n// Auto-register framework plugins\nimport { configureSeal, getSealConfig, registerPlugin } from \"@warlock.js/seal\";\nimport { config } from \"../config\";\nimport { t } from \"../http/middleware/inject-request-context\";\nimport { databasePlugin, filePlugin, localizedPlugin } from \"./plugins\";\n\n// Configure Seal to use Warlock's localization\nconfigureSeal({\n translateRule({ rule, attributes }) {\n const translateRule = config.key(\"validation.translateRule\");\n\n if (translateRule) {\n return translateRule({ rule, attributes });\n }\n\n const translationGroup = config.key(\"validation.translationGroup\", \"validation\");\n\n const translationKey = `${translationGroup}.${rule.name}`;\n const translation = t(translationKey, attributes);\n\n return translation === translationKey\n ? rule.errorMessage || rule.defaultErrorMessage\n : translation;\n },\n\n translateAttribute({ attribute, context, rule }) {\n const translateAttribute = config.key(\"validation.translateAttribute\");\n\n if (translateAttribute) {\n return translateAttribute({ attribute, context, rule });\n }\n\n const attributeGroup = config.key(\"validation.attributeGroup\") ?? \"attributes\";\n\n const translationKey = `${attributeGroup ? attributeGroup + \".\" : \"\"}${attribute}`;\n\n const output = t(translationKey, context.allValues);\n return output === translationKey ? attribute : output;\n },\n});\n\n// App config is not loaded yet when this module is imported, so resolve\n// firstErrorOnly lazily on every read instead of capturing it once.\nObject.defineProperty(getSealConfig(), \"firstErrorOnly\", {\n configurable: true,\n enumerable: true,\n get: () => config.key(\"validation.firstErrorOnly\", true),\n});\n\n// Register plugins to inject methods\nregisterPlugin(databasePlugin);\nregisterPlugin(filePlugin);\nregisterPlugin(localizedPlugin);\n"],"mappings":";;;;;;;;;;;;;;;AAaA,cAAc;CACZ,cAAc,EAAE,MAAM,cAAc;EAClC,MAAM,gBAAgB,OAAO,IAAI,0BAA0B;EAE3D,IAAI,eACF,OAAO,cAAc;GAAE;GAAM;EAAW,CAAC;EAK3C,MAAM,iBAAiB,GAFE,OAAO,IAAI,+BAA+B,YAE1B,EAAE,GAAG,KAAK;EACnD,MAAM,cAAc,EAAE,gBAAgB,UAAU;EAEhD,OAAO,gBAAgB,iBACnB,KAAK,gBAAgB,KAAK,sBAC1B;CACN;CAEA,mBAAmB,EAAE,WAAW,SAAS,QAAQ;EAC/C,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;EAErE,IAAI,oBACF,OAAO,mBAAmB;GAAE;GAAW;GAAS;EAAK,CAAC;EAGxD,MAAM,iBAAiB,OAAO,IAAI,2BAA2B,KAAK;EAElE,MAAM,iBAAiB,GAAG,iBAAiB,iBAAiB,MAAM,KAAK;EAEvE,MAAM,SAAS,EAAE,gBAAgB,QAAQ,SAAS;EAClD,OAAO,WAAW,iBAAiB,YAAY;CACjD;AACF,CAAC;AAID,OAAO,eAAe,cAAc,GAAG,kBAAkB;CACvD,cAAc;CACd,YAAY;CACZ,WAAW,OAAO,IAAI,6BAA6B,IAAI;AACzD,CAAC;AAGD,eAAe,cAAc;AAC7B,eAAe,UAAU;AACzB,eAAe,eAAe"}
@@ -1,3 +1,4 @@
1
+ import { Response } from "../http/response.mjs";
1
2
  import { v } from "@warlock.js/seal";
2
3
  import config from "@mongez/config";
3
4
  import { log } from "@warlock.js/logger";
@@ -43,9 +44,9 @@ async function validateAll(validation, request, response) {
43
44
  response
44
45
  });
45
46
  if (result) {
46
- if (!response.statusCode) response.setStatusCode(config.get("validation.responseStatus", 400));
47
47
  log.info("validation", "failed", "Validation failed");
48
- return result;
48
+ if (result instanceof Response) return result;
49
+ return response.send(result, config.get("validation.responseStatus", 400));
49
50
  }
50
51
  log.info("validation", "passed", "Validation passed");
51
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validateAll.mjs","names":[],"sources":["../../../../../../../core/src/validation/validateAll.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { merge } from \"@mongez/reinforcements\";\nimport { log } from \"@warlock.js/logger\";\nimport { v } from \"@warlock.js/seal\";\nimport type { Request, Response } from \"../http\";\nimport type { RequestHandlerValidation, Route } from \"../router\";\n\nfunction resolveDataToParse(validating: RequestHandlerValidation[\"validating\"], request: Request) {\n if (!validating || validating.length === 0) return request.allExceptParams();\n\n let data: any = {};\n\n for (const validatingType of validating) {\n if (validatingType === \"body\") {\n data = merge(data, request.body);\n }\n\n if (validatingType === \"query\") {\n data = merge(data, request.query);\n }\n\n if (validatingType === \"params\") {\n data = merge(data, request.params);\n }\n\n if (validatingType === \"headers\") {\n data = merge(data, request.headers);\n }\n }\n\n return data;\n}\n\n/**\n * Validate the request route\n */\nexport async function validateAll(\n validation: Route[\"handler\"][\"validation\"],\n request: Request,\n response: Response,\n) {\n if (!validation) return;\n\n log.info(\"validation\", \"started\", \"Start validating the request\");\n\n if (validation.schema) {\n log.info(\"validation\", \"schema\", \"Validating request schema\");\n try {\n const data = resolveDataToParse(validation.validating, request);\n const result = await v.validate(validation.schema, data);\n\n if (result.data && result.isValid) {\n request.setValidatedData(result.data);\n }\n\n if (!result.isValid) {\n log.warn(\"validation\", \"schema\", \"Schema Validation failed\");\n return response.failedSchema(result);\n }\n\n log.success(\"validation\", \"schema\", \"Schema Validation passed\");\n } catch (error) {\n log.warn(\"app.validation\", \"error\", error);\n throw error;\n }\n }\n\n if (validation.validate) {\n const result = await validation.validate({ request, response });\n\n // if there is a result, it means it failed\n if (result) {\n // check if there is no response status code, then set it to config value or 400 as default\n if (!response.statusCode) {\n response.setStatusCode(config.get(\"validation.responseStatus\", 400));\n }\n\n log.info(\"validation\", \"failed\", \"Validation failed\");\n\n return result;\n }\n\n log.info(\"validation\", \"passed\", \"Validation passed\");\n }\n}\n"],"mappings":";;;;;;AAOA,SAAS,mBAAmB,YAAoD,SAAkB;CAChG,IAAI,CAAC,cAAc,WAAW,WAAW,GAAG,OAAO,QAAQ,gBAAgB;CAE3E,IAAI,OAAY,CAAC;CAEjB,KAAK,MAAM,kBAAkB,YAAY;EACvC,IAAI,mBAAmB,QACrB,OAAO,MAAM,MAAM,QAAQ,IAAI;EAGjC,IAAI,mBAAmB,SACrB,OAAO,MAAM,MAAM,QAAQ,KAAK;EAGlC,IAAI,mBAAmB,UACrB,OAAO,MAAM,MAAM,QAAQ,MAAM;EAGnC,IAAI,mBAAmB,WACrB,OAAO,MAAM,MAAM,QAAQ,OAAO;CAEtC;CAEA,OAAO;AACT;;;;AAKA,eAAsB,YACpB,YACA,SACA,UACA;CACA,IAAI,CAAC,YAAY;CAEjB,IAAI,KAAK,cAAc,WAAW,8BAA8B;CAEhE,IAAI,WAAW,QAAQ;EACrB,IAAI,KAAK,cAAc,UAAU,2BAA2B;EAC5D,IAAI;GACF,MAAM,OAAO,mBAAmB,WAAW,YAAY,OAAO;GAC9D,MAAM,SAAS,MAAM,EAAE,SAAS,WAAW,QAAQ,IAAI;GAEvD,IAAI,OAAO,QAAQ,OAAO,SACxB,QAAQ,iBAAiB,OAAO,IAAI;GAGtC,IAAI,CAAC,OAAO,SAAS;IACnB,IAAI,KAAK,cAAc,UAAU,0BAA0B;IAC3D,OAAO,SAAS,aAAa,MAAM;GACrC;GAEA,IAAI,QAAQ,cAAc,UAAU,0BAA0B;EAChE,SAAS,OAAO;GACd,IAAI,KAAK,kBAAkB,SAAS,KAAK;GACzC,MAAM;EACR;CACF;CAEA,IAAI,WAAW,UAAU;EACvB,MAAM,SAAS,MAAM,WAAW,SAAS;GAAE;GAAS;EAAS,CAAC;EAG9D,IAAI,QAAQ;GAEV,IAAI,CAAC,SAAS,YACZ,SAAS,cAAc,OAAO,IAAI,6BAA6B,GAAG,CAAC;GAGrE,IAAI,KAAK,cAAc,UAAU,mBAAmB;GAEpD,OAAO;EACT;EAEA,IAAI,KAAK,cAAc,UAAU,mBAAmB;CACtD;AACF"}
1
+ {"version":3,"file":"validateAll.mjs","names":[],"sources":["../../../../../../../core/src/validation/validateAll.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { merge } from \"@mongez/reinforcements\";\nimport { log } from \"@warlock.js/logger\";\nimport { v } from \"@warlock.js/seal\";\nimport type { Request } from \"../http\";\nimport { Response } from \"../http/response\";\nimport type { RequestHandlerValidation, Route } from \"../router\";\n\nfunction resolveDataToParse(validating: RequestHandlerValidation[\"validating\"], request: Request) {\n if (!validating || validating.length === 0) return request.allExceptParams();\n\n let data: any = {};\n\n for (const validatingType of validating) {\n if (validatingType === \"body\") {\n data = merge(data, request.body);\n }\n\n if (validatingType === \"query\") {\n data = merge(data, request.query);\n }\n\n if (validatingType === \"params\") {\n data = merge(data, request.params);\n }\n\n if (validatingType === \"headers\") {\n data = merge(data, request.headers);\n }\n }\n\n return data;\n}\n\n/**\n * Validate the request route\n */\nexport async function validateAll(\n validation: Route[\"handler\"][\"validation\"],\n request: Request,\n response: Response,\n) {\n if (!validation) return;\n\n log.info(\"validation\", \"started\", \"Start validating the request\");\n\n if (validation.schema) {\n log.info(\"validation\", \"schema\", \"Validating request schema\");\n try {\n const data = resolveDataToParse(validation.validating, request);\n const result = await v.validate(validation.schema, data);\n\n if (result.data && result.isValid) {\n request.setValidatedData(result.data);\n }\n\n if (!result.isValid) {\n log.warn(\"validation\", \"schema\", \"Schema Validation failed\");\n return response.failedSchema(result);\n }\n\n log.success(\"validation\", \"schema\", \"Schema Validation passed\");\n } catch (error) {\n log.warn(\"app.validation\", \"error\", error);\n throw error;\n }\n }\n\n if (validation.validate) {\n const result = await validation.validate({ request, response });\n\n // if there is a result, it means it failed\n if (result) {\n log.info(\"validation\", \"failed\", \"Validation failed\");\n\n // a returned Response already carries its own status; anything else is\n // sent with the configured failure status (400 by default)\n if (result instanceof Response) return result;\n\n return response.send(result, config.get(\"validation.responseStatus\", 400));\n }\n\n log.info(\"validation\", \"passed\", \"Validation passed\");\n }\n}\n"],"mappings":";;;;;;;AAQA,SAAS,mBAAmB,YAAoD,SAAkB;CAChG,IAAI,CAAC,cAAc,WAAW,WAAW,GAAG,OAAO,QAAQ,gBAAgB;CAE3E,IAAI,OAAY,CAAC;CAEjB,KAAK,MAAM,kBAAkB,YAAY;EACvC,IAAI,mBAAmB,QACrB,OAAO,MAAM,MAAM,QAAQ,IAAI;EAGjC,IAAI,mBAAmB,SACrB,OAAO,MAAM,MAAM,QAAQ,KAAK;EAGlC,IAAI,mBAAmB,UACrB,OAAO,MAAM,MAAM,QAAQ,MAAM;EAGnC,IAAI,mBAAmB,WACrB,OAAO,MAAM,MAAM,QAAQ,OAAO;CAEtC;CAEA,OAAO;AACT;;;;AAKA,eAAsB,YACpB,YACA,SACA,UACA;CACA,IAAI,CAAC,YAAY;CAEjB,IAAI,KAAK,cAAc,WAAW,8BAA8B;CAEhE,IAAI,WAAW,QAAQ;EACrB,IAAI,KAAK,cAAc,UAAU,2BAA2B;EAC5D,IAAI;GACF,MAAM,OAAO,mBAAmB,WAAW,YAAY,OAAO;GAC9D,MAAM,SAAS,MAAM,EAAE,SAAS,WAAW,QAAQ,IAAI;GAEvD,IAAI,OAAO,QAAQ,OAAO,SACxB,QAAQ,iBAAiB,OAAO,IAAI;GAGtC,IAAI,CAAC,OAAO,SAAS;IACnB,IAAI,KAAK,cAAc,UAAU,0BAA0B;IAC3D,OAAO,SAAS,aAAa,MAAM;GACrC;GAEA,IAAI,QAAQ,cAAc,UAAU,0BAA0B;EAChE,SAAS,OAAO;GACd,IAAI,KAAK,kBAAkB,SAAS,KAAK;GACzC,MAAM;EACR;CACF;CAEA,IAAI,WAAW,UAAU;EACvB,MAAM,SAAS,MAAM,WAAW,SAAS;GAAE;GAAS;EAAS,CAAC;EAG9D,IAAI,QAAQ;GACV,IAAI,KAAK,cAAc,UAAU,mBAAmB;GAIpD,IAAI,kBAAkB,UAAU,OAAO;GAEvC,OAAO,SAAS,KAAK,QAAQ,OAAO,IAAI,6BAA6B,GAAG,CAAC;EAC3E;EAEA,IAAI,KAAK,cAAc,UAAU,mBAAmB;CACtD;AACF"}
@@ -15,8 +15,17 @@ type WarlockConfig = {
15
15
  * Server configuration
16
16
  */
17
17
  server?: {
18
+ /**
19
+ * @deprecated Not read by the framework. Set the HTTP port in `config/http.ts` (`port`).
20
+ */
18
21
  port?: number;
22
+ /**
23
+ * @deprecated Not read by the framework. Set the HTTP host in `config/http.ts` (`host`).
24
+ */
19
25
  host?: string;
26
+ /**
27
+ * @deprecated Not read by the framework. Port fallback is not implemented.
28
+ */
20
29
  retryOtherPort?: boolean;
21
30
  };
22
31
  /**
@@ -237,12 +246,16 @@ type WarlockConfig = {
237
246
  * Added to the default patterns.
238
247
  *
239
248
  * @example ["src/shared/[ALL]/*.test.ts"]
249
+ *
250
+ * @deprecated Not read by the framework. Configure test discovery in `config/tests.ts`.
240
251
  */
241
252
  include?: string[];
242
253
  /**
243
254
  * Glob patterns to exclude from test discovery.
244
255
  *
245
- * @example [[ALL]/*.integration.test.ts"]
256
+ * @example ["[ALL]/*.integration.test.ts"]
257
+ *
258
+ * @deprecated Not read by the framework. Configure test discovery in `config/tests.ts`.
246
259
  */
247
260
  exclude?: string[];
248
261
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/types.ts"],"mappings":";;;;;;;;;AAWA;;;KAAY,aAAA;EAqBF;;;EAjBR,MAAA;IACE,IAAA;IACA,IAAA;IACA,cAAA;EAAA;EAHF;;;;;;;;;;;EAiBA,KAAA,GAAQ,IAAA,CAAK,YAAA;IA8DX;;;;;IAxDA,MAAA;IA+FW;;;;;;;;;;IApFX,YAAA;IA4JA;;;;;IAtJA,OAAA;IA8LA;;;AAOO;;;;;;;;;;;;;IApLP,YAAA;;;;;;;;;;;;;;;;IAgBA,OAAA;;;;;;IAMA,MAAA;;;;;;IAMA,SAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;EA2BF,UAAA,GAAa,SAAA;;;;EAKb,GAAA;IACE,QAAA,GAAW,UAAA;EAAA;;;;EAMb,SAAA;;;;IAIE,KAAA;;;;;;MAME,OAAA;;;;;;MAMA,OAAA;IAAA;;;;IAKF,cAAA,GAAiB,yBAAA;;;;;IAKjB,eAAA;;;;;;;;;IASA,eAAA;;;;;;;;;IASA,qBAAA;;;;;;;;;;IAUA,mBAAA;;;;;;;;;;;;IAYA,OAAA;EAAA;;;;EAMF,QAAA;;;;;;;;;;;;;;;;;;IAkBE,UAAA,GAAa,KAAA,CAAM,oBAAA;EAAA;;;;;;;EASrB,OAAA;;;;;;;IAOE,OAAA;;;;;;IAOA,OAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/types.ts"],"mappings":";;;;;;;;;AAWA;;;KAAY,aAAA;EA8BF;;;EA1BR,MAAA;IA+NqB;;;IA3NnB,IAAA;IAJF;;;IAQE,IAAA;IAkBF;;;IAdE,cAAA;EAAA;EAqCA;;;;;;;;;;;EAvBF,KAAA,GAAQ,IAAA,CAAK,YAAA;IAqHT;;;;;IA/GF,MAAA;IAiJA;;;;;;;;;;IAtIA,YAAA;IA+MO;;;;;IAzMP,OAAA;;;;;;;;;;;;;;;;;IAiBA,YAAA;;;;;;;;;;;;;;;;IAgBA,OAAA;;;;;;IAMA,MAAA;;;;;;IAMA,SAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;EA2BF,UAAA,GAAa,SAAA;;;;EAKb,GAAA;IACE,QAAA,GAAW,UAAA;EAAA;;;;EAMb,SAAA;;;;IAIE,KAAA;;;;;;MAME,OAAA;;;;;;MAMA,OAAA;IAAA;;;;IAKF,cAAA,GAAiB,yBAAA;;;;;IAKjB,eAAA;;;;;;;;;IASA,eAAA;;;;;;;;;IASA,qBAAA;;;;;;;;;;IAUA,mBAAA;;;;;;;;;;;;IAYA,OAAA;EAAA;;;;EAMF,QAAA;;;;;;;;;;;;;;;;;;IAkBE,UAAA,GAAa,KAAA,CAAM,oBAAA;EAAA;;;;;;;EASrB,OAAA;;;;;;;;;IASE,OAAA;;;;;;;;IASA,OAAA;EAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;AAeA;;;;AAAwD;AAaxD;iBAbgB,yBAAA,CAA0B,KAAc;;;;;;;cAa3C,oBAAA;EAyHR;;;EAAA,QArHK,MAAA;EAyIS;;;EAAA,QApIT,OAAA;EAqIL;;;;;EA9HU,IAAA,IAAQ,OAAA,CAAQ,aAAA;EAPrB;;;;;;EAAA,QAgCM,MAAA;EA6EH;;;;;;;;EAAA,QAtCG,cAAA;EA2DR;;;;;;;;;;;EArBC,GAAA,mBAAsB,aAAA,EAC3B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,aAAA,CAAc,GAAA;EAsCV;;;;;AAWuB;AAoBhC;;;;EAnDQ,OAAA,mBAA0B,aAAA,EAC9B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,OAAA,CAAQ,aAAA,CAAc,GAAA;;;;MAQd,QAAA;;;;;;EASJ,MAAA,IAAU,aAAA;;;;EAWJ,MAAA,IAAU,OAAA;AAAA;;;;;;;;;;;;;;cAoBZ,oBAAA,EAAoB,oBAA6B"}
1
+ {"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;AAgBA;;;;AAAwD;AAaxD;iBAbgB,yBAAA,CAA0B,KAAc;;;;;;;cAa3C,oBAAA;EAgIR;;;EAAA,QA5HK,MAAA;EAgJS;;;EAAA,QA3IT,OAAA;EA4IL;;;;;EArIU,IAAA,IAAQ,OAAA,CAAQ,aAAA;EAPrB;;;;;;EAAA,QAqCM,MAAA;EA+EH;;;;;;;;EAAA,QAtCG,cAAA;EA2DR;;;;;;;;;;;EArBC,GAAA,mBAAsB,aAAA,EAC3B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,aAAA,CAAc,GAAA;EAsCV;;;;;AAWuB;AAoBhC;;;;EAnDQ,OAAA,mBAA0B,aAAA,EAC9B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,OAAA,CAAQ,aAAA,CAAc,GAAA;;;;MAQd,QAAA;;;;;;EASJ,MAAA,IAAU,aAAA;;;;EAWJ,MAAA,IAAU,OAAA;AAAA;;;;;;;;;;;;;;cAoBZ,oBAAA,EAAoB,oBAA6B"}
@@ -1,5 +1,6 @@
1
1
  import { rootPath } from "../utils/paths.mjs";
2
2
  import "../utils/index.mjs";
3
+ import { getDeprecatedConfigWarnings } from "./warn-deprecated-config.mjs";
3
4
  import { get } from "@mongez/reinforcements";
4
5
  import { fileExistsAsync } from "@warlock.js/fs";
5
6
  import { readFile, unlink, writeFile } from "node:fs/promises";
@@ -35,6 +36,7 @@ var WarlockConfigManager = class {
35
36
  this.loading = this.doLoad();
36
37
  this.config = await this.loading;
37
38
  this.loading = void 0;
39
+ for (const warning of getDeprecatedConfigWarnings(this.config)) console.warn(warning);
38
40
  return this.config;
39
41
  }
40
42
  /**
@@ -46,7 +48,7 @@ var WarlockConfigManager = class {
46
48
  async doLoad() {
47
49
  const configPath = rootPath("warlock.config.ts");
48
50
  if (!await fileExistsAsync(configPath)) {
49
- console.warn("warlock.config.ts is missing — run `warlock init` to create it");
51
+ console.warn("warlock.config.ts is missing — create it at the project root (see https://warlock.js.org/docs for the config reference)");
50
52
  return;
51
53
  }
52
54
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport { rootPath } from \"../utils\";\r\nimport { type WarlockConfig } from \"./types\";\r\n\r\n/**\r\n * True when `error` is Node's \"this runtime can't import a `.ts` file\" error.\r\n *\r\n * Node only executes TypeScript natively from v22.18 / v23.6 onward (earlier\r\n * 22.x needs `--experimental-strip-types`). On older runtimes a bare\r\n * `import(\"warlock.config.ts\")` throws `ERR_UNKNOWN_FILE_EXTENSION` — which is\r\n * the signal to fall back to transpiling the config ourselves.\r\n */\r\nexport function isUnknownTsExtensionError(error: unknown): boolean {\r\n return (\r\n (error as { code?: string })?.code === \"ERR_UNKNOWN_FILE_EXTENSION\" ||\r\n /Unknown file extension \"\\.tsx?\"/.test(String((error as Error)?.message))\r\n );\r\n}\r\n\r\n/**\r\n * Warlock Config Manager\r\n *\r\n * Manages lazy loading of the pre-compiled warlock.config.js file\r\n * from the .warlock/cache directory.\r\n */\r\nexport class WarlockConfigManager {\r\n /**\r\n * Cached config instance\r\n */\r\n private config?: WarlockConfig;\r\n\r\n /**\r\n * Loading promise to prevent duplicate loads\r\n */\r\n private loading?: Promise<WarlockConfig | undefined>;\r\n\r\n /**\r\n * Load warlock.config.js (cached after first load)\r\n *\r\n * @returns The resolved Warlock configuration\r\n */\r\n public async load(): Promise<WarlockConfig | undefined> {\r\n // Already loaded\r\n if (this.config) {\r\n return this.config;\r\n }\r\n\r\n // Currently loading (prevent duplicate loads)\r\n if (this.loading) {\r\n return this.loading;\r\n }\r\n\r\n // Start loading\r\n this.loading = this.doLoad();\r\n this.config = await this.loading;\r\n this.loading = undefined;\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Internal load implementation\r\n *\r\n * The ESM loader hook transpiles `warlock.config.ts` on import — no\r\n * separate compile-to-disk step is needed.\r\n */\r\n private async doLoad(): Promise<WarlockConfig | undefined> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n // Written straight to the console, NOT through `@warlock.js/logger`.\r\n // This runs during CLI bootstrap, before `LoggerConnector.start()` has\r\n // configured a single channel, so a logger call here reaches nobody in\r\n // any application — the warning would be silently dropped exactly when\r\n // the user most needs it.\r\n console.warn(\"warlock.config.ts is missing — run `warlock init` to create it\");\r\n return;\r\n }\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(configPath).href);\r\n return configModule.default;\r\n } catch (error) {\r\n // `dev` registers a TS loader hook, and Node ≥ 22.18 / 23.6 strips types\r\n // natively — in both cases the direct import above works. But `build` /\r\n // `start` register no hook, so on an older Node the import throws\r\n // ERR_UNKNOWN_FILE_EXTENSION. Transpile the config with esbuild ourselves\r\n // so config loading is Node-version-independent rather than relying on an\r\n // experimental runtime feature.\r\n if (isUnknownTsExtensionError(error)) {\r\n return await this.loadViaEsbuild(configPath);\r\n }\r\n\r\n throw new Error(`Failed to load warlock.config.ts: ${error}`);\r\n }\r\n }\r\n\r\n /**\r\n * Fallback config loader for runtimes without native TypeScript support.\r\n *\r\n * Transpiles `warlock.config.ts` with esbuild (already a core dependency),\r\n * writes the result as a sibling `.mjs` — so the config's own imports (bare\r\n * `@warlock.js/*` and any relative paths) resolve exactly as they would from\r\n * the original location — imports it, then removes the temp file.\r\n */\r\n private async loadViaEsbuild(configPath: string): Promise<WarlockConfig | undefined> {\r\n const { transformSync } = await import(\"esbuild\");\r\n const source = await readFile(configPath, \"utf8\");\r\n\r\n const { code } = transformSync(source, {\r\n loader: \"ts\",\r\n format: \"esm\",\r\n target: `node${process.versions.node.split(\".\")[0]}`,\r\n sourcefile: configPath,\r\n });\r\n\r\n // Unique sibling name so concurrent processes never clobber each other.\r\n const compiledPath = configPath.replace(\r\n /warlock\\.config\\.ts$/,\r\n `.warlock.config.${process.pid}.mjs`,\r\n );\r\n\r\n await writeFile(compiledPath, code, \"utf8\");\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(compiledPath).href);\r\n return configModule.default;\r\n } finally {\r\n await unlink(compiledPath).catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * Get config value by key (dot notation supported)\r\n *\r\n * @example\r\n * config.get(\"server.port\") // Returns 3000\r\n * config.get(\"cli.commands\") // Returns array of commands\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @returns The config value\r\n * @throws Error if config is not loaded\r\n */\r\n public get<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): WarlockConfig[Key] {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return get(this.config, key as string, defaultValue);\r\n }\r\n\r\n /**\r\n * Lazy get - loads config if not already loaded\r\n *\r\n * @example\r\n * const port = await config.lazyGet(\"server\");\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @param defaultValue - Default value if config key is undefined\r\n * @returns The config value\r\n */\r\n async lazyGet<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): Promise<WarlockConfig[Key]> {\r\n await this.load();\r\n return this.get(key, defaultValue);\r\n }\r\n\r\n /**\r\n * Check if config is loaded\r\n */\r\n public get isLoaded(): boolean {\r\n return this.config !== undefined;\r\n }\r\n\r\n /**\r\n * Get the entire config object\r\n *\r\n * @throws Error if config is not loaded\r\n */\r\n public getAll(): WarlockConfig {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Reload config (useful for HMR/development)\r\n */\r\n public async reload(): Promise<void> {\r\n this.config = undefined;\r\n this.loading = undefined;\r\n await this.load();\r\n }\r\n}\r\n\r\n/**\r\n * Exported singleton instance\r\n *\r\n * @example\r\n * import { warlockConfig } from \"@warlock.js/core\";\r\n *\r\n * // Lazy load and get value\r\n * const port = await warlockConfig.lazyGet(\"server.port\");\r\n *\r\n * // Or load first, then get\r\n * await warlockConfig.load();\r\n * const commands = warlockConfig.get(\"cli.commands\");\r\n */\r\nexport const warlockConfigManager = new WarlockConfigManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,0BAA0B,OAAyB;CACjE,OACG,OAA6B,SAAS,gCACvC,kCAAkC,KAAK,OAAQ,OAAiB,OAAO,CAAC;AAE5E;;;;;;;AAQA,IAAa,uBAAb,MAAkC;;;;;;CAgBhC,MAAa,OAA2C;EAEtD,IAAI,KAAK,QACP,OAAO,KAAK;EAId,IAAI,KAAK,SACP,OAAO,KAAK;EAId,KAAK,UAAU,KAAK,OAAO;EAC3B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,UAAU;EAEf,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,SAA6C;EACzD,MAAM,aAAa,SAAS,mBAAmB;EAE/C,IAAI,CAAE,MAAM,gBAAgB,UAAU,GAAI;GAMxC,QAAQ,KAAK,gEAAgE;GAC7E;EACF;EAEA,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,UAAU,CAAC,CAAC,MACzC,CAAC;EACtB,SAAS,OAAO;GAOd,IAAI,0BAA0B,KAAK,GACjC,OAAO,MAAM,KAAK,eAAe,UAAU;GAG7C,MAAM,IAAI,MAAM,qCAAqC,OAAO;EAC9D;CACF;;;;;;;;;CAUA,MAAc,eAAe,YAAwD;EACnF,MAAM,EAAE,kBAAkB,MAAM,OAAO;EAGvC,MAAM,EAAE,SAAS,cAAc,MAFV,SAAS,YAAY,MAAM,GAET;GACrC,QAAQ;GACR,QAAQ;GACR,QAAQ,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC;GAChD,YAAY;EACd,CAAC;EAGD,MAAM,eAAe,WAAW,QAC9B,wBACA,mBAAmB,QAAQ,IAAI,KACjC;EAEA,MAAM,UAAU,cAAc,MAAM,MAAM;EAE1C,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,YAAY,CAAC,CAAC,MAC3C,CAAC;EACtB,UAAU;GACR,MAAM,OAAO,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC;EAC3C;CACF;;;;;;;;;;;;CAaA,AAAO,IACL,KACA,cACoB;EACpB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,IAAI,KAAK,QAAQ,KAAe,YAAY;CACrD;;;;;;;;;;;CAYA,MAAM,QACJ,KACA,cAC6B;EAC7B,MAAM,KAAK,KAAK;EAChB,OAAO,KAAK,IAAI,KAAK,YAAY;CACnC;;;;CAKA,IAAW,WAAoB;EAC7B,OAAO,KAAK,WAAW;CACzB;;;;;;CAOA,AAAO,SAAwB;EAC7B,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,KAAK;CACd;;;;CAKA,MAAa,SAAwB;EACnC,KAAK,SAAS;EACd,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;CAClB;AACF;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB,IAAI,qBAAqB"}
1
+ {"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport { rootPath } from \"../utils\";\r\nimport { type WarlockConfig } from \"./types\";\r\nimport { getDeprecatedConfigWarnings } from \"./warn-deprecated-config\";\r\n\r\n/**\r\n * True when `error` is Node's \"this runtime can't import a `.ts` file\" error.\r\n *\r\n * Node only executes TypeScript natively from v22.18 / v23.6 onward (earlier\r\n * 22.x needs `--experimental-strip-types`). On older runtimes a bare\r\n * `import(\"warlock.config.ts\")` throws `ERR_UNKNOWN_FILE_EXTENSION` — which is\r\n * the signal to fall back to transpiling the config ourselves.\r\n */\r\nexport function isUnknownTsExtensionError(error: unknown): boolean {\r\n return (\r\n (error as { code?: string })?.code === \"ERR_UNKNOWN_FILE_EXTENSION\" ||\r\n /Unknown file extension \"\\.tsx?\"/.test(String((error as Error)?.message))\r\n );\r\n}\r\n\r\n/**\r\n * Warlock Config Manager\r\n *\r\n * Manages lazy loading of the pre-compiled warlock.config.js file\r\n * from the .warlock/cache directory.\r\n */\r\nexport class WarlockConfigManager {\r\n /**\r\n * Cached config instance\r\n */\r\n private config?: WarlockConfig;\r\n\r\n /**\r\n * Loading promise to prevent duplicate loads\r\n */\r\n private loading?: Promise<WarlockConfig | undefined>;\r\n\r\n /**\r\n * Load warlock.config.js (cached after first load)\r\n *\r\n * @returns The resolved Warlock configuration\r\n */\r\n public async load(): Promise<WarlockConfig | undefined> {\r\n // Already loaded\r\n if (this.config) {\r\n return this.config;\r\n }\r\n\r\n // Currently loading (prevent duplicate loads)\r\n if (this.loading) {\r\n return this.loading;\r\n }\r\n\r\n // Start loading\r\n this.loading = this.doLoad();\r\n this.config = await this.loading;\r\n this.loading = undefined;\r\n\r\n // Console, not the logger: this runs before any log channel exists.\r\n for (const warning of getDeprecatedConfigWarnings(this.config)) {\r\n console.warn(warning);\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Internal load implementation\r\n *\r\n * The ESM loader hook transpiles `warlock.config.ts` on import — no\r\n * separate compile-to-disk step is needed.\r\n */\r\n private async doLoad(): Promise<WarlockConfig | undefined> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n // Written straight to the console, NOT through `@warlock.js/logger`.\r\n // This runs during CLI bootstrap, before `LoggerConnector.start()` has\r\n // configured a single channel, so a logger call here reaches nobody in\r\n // any application — the warning would be silently dropped exactly when\r\n // the user most needs it.\r\n console.warn(\r\n \"warlock.config.ts is missing — create it at the project root (see https://warlock.js.org/docs for the config reference)\",\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(configPath).href);\r\n return configModule.default;\r\n } catch (error) {\r\n // `dev` registers a TS loader hook, and Node ≥ 22.18 / 23.6 strips types\r\n // natively — in both cases the direct import above works. But `build` /\r\n // `start` register no hook, so on an older Node the import throws\r\n // ERR_UNKNOWN_FILE_EXTENSION. Transpile the config with esbuild ourselves\r\n // so config loading is Node-version-independent rather than relying on an\r\n // experimental runtime feature.\r\n if (isUnknownTsExtensionError(error)) {\r\n return await this.loadViaEsbuild(configPath);\r\n }\r\n\r\n throw new Error(`Failed to load warlock.config.ts: ${error}`);\r\n }\r\n }\r\n\r\n /**\r\n * Fallback config loader for runtimes without native TypeScript support.\r\n *\r\n * Transpiles `warlock.config.ts` with esbuild (already a core dependency),\r\n * writes the result as a sibling `.mjs` — so the config's own imports (bare\r\n * `@warlock.js/*` and any relative paths) resolve exactly as they would from\r\n * the original location — imports it, then removes the temp file.\r\n */\r\n private async loadViaEsbuild(configPath: string): Promise<WarlockConfig | undefined> {\r\n const { transformSync } = await import(\"esbuild\");\r\n const source = await readFile(configPath, \"utf8\");\r\n\r\n const { code } = transformSync(source, {\r\n loader: \"ts\",\r\n format: \"esm\",\r\n target: `node${process.versions.node.split(\".\")[0]}`,\r\n sourcefile: configPath,\r\n });\r\n\r\n // Unique sibling name so concurrent processes never clobber each other.\r\n const compiledPath = configPath.replace(\r\n /warlock\\.config\\.ts$/,\r\n `.warlock.config.${process.pid}.mjs`,\r\n );\r\n\r\n await writeFile(compiledPath, code, \"utf8\");\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(compiledPath).href);\r\n return configModule.default;\r\n } finally {\r\n await unlink(compiledPath).catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * Get config value by key (dot notation supported)\r\n *\r\n * @example\r\n * config.get(\"server.port\") // Returns 3000\r\n * config.get(\"cli.commands\") // Returns array of commands\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @returns The config value\r\n * @throws Error if config is not loaded\r\n */\r\n public get<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): WarlockConfig[Key] {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return get(this.config, key as string, defaultValue);\r\n }\r\n\r\n /**\r\n * Lazy get - loads config if not already loaded\r\n *\r\n * @example\r\n * const port = await config.lazyGet(\"server\");\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @param defaultValue - Default value if config key is undefined\r\n * @returns The config value\r\n */\r\n async lazyGet<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): Promise<WarlockConfig[Key]> {\r\n await this.load();\r\n return this.get(key, defaultValue);\r\n }\r\n\r\n /**\r\n * Check if config is loaded\r\n */\r\n public get isLoaded(): boolean {\r\n return this.config !== undefined;\r\n }\r\n\r\n /**\r\n * Get the entire config object\r\n *\r\n * @throws Error if config is not loaded\r\n */\r\n public getAll(): WarlockConfig {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Reload config (useful for HMR/development)\r\n */\r\n public async reload(): Promise<void> {\r\n this.config = undefined;\r\n this.loading = undefined;\r\n await this.load();\r\n }\r\n}\r\n\r\n/**\r\n * Exported singleton instance\r\n *\r\n * @example\r\n * import { warlockConfig } from \"@warlock.js/core\";\r\n *\r\n * // Lazy load and get value\r\n * const port = await warlockConfig.lazyGet(\"server.port\");\r\n *\r\n * // Or load first, then get\r\n * await warlockConfig.load();\r\n * const commands = warlockConfig.get(\"cli.commands\");\r\n */\r\nexport const warlockConfigManager = new WarlockConfigManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,0BAA0B,OAAyB;CACjE,OACG,OAA6B,SAAS,gCACvC,kCAAkC,KAAK,OAAQ,OAAiB,OAAO,CAAC;AAE5E;;;;;;;AAQA,IAAa,uBAAb,MAAkC;;;;;;CAgBhC,MAAa,OAA2C;EAEtD,IAAI,KAAK,QACP,OAAO,KAAK;EAId,IAAI,KAAK,SACP,OAAO,KAAK;EAId,KAAK,UAAU,KAAK,OAAO;EAC3B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,UAAU;EAGf,KAAK,MAAM,WAAW,4BAA4B,KAAK,MAAM,GAC3D,QAAQ,KAAK,OAAO;EAGtB,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,SAA6C;EACzD,MAAM,aAAa,SAAS,mBAAmB;EAE/C,IAAI,CAAE,MAAM,gBAAgB,UAAU,GAAI;GAMxC,QAAQ,KACN,yHACF;GACA;EACF;EAEA,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,UAAU,CAAC,CAAC,MACzC,CAAC;EACtB,SAAS,OAAO;GAOd,IAAI,0BAA0B,KAAK,GACjC,OAAO,MAAM,KAAK,eAAe,UAAU;GAG7C,MAAM,IAAI,MAAM,qCAAqC,OAAO;EAC9D;CACF;;;;;;;;;CAUA,MAAc,eAAe,YAAwD;EACnF,MAAM,EAAE,kBAAkB,MAAM,OAAO;EAGvC,MAAM,EAAE,SAAS,cAAc,MAFV,SAAS,YAAY,MAAM,GAET;GACrC,QAAQ;GACR,QAAQ;GACR,QAAQ,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC;GAChD,YAAY;EACd,CAAC;EAGD,MAAM,eAAe,WAAW,QAC9B,wBACA,mBAAmB,QAAQ,IAAI,KACjC;EAEA,MAAM,UAAU,cAAc,MAAM,MAAM;EAE1C,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,YAAY,CAAC,CAAC,MAC3C,CAAC;EACtB,UAAU;GACR,MAAM,OAAO,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC;EAC3C;CACF;;;;;;;;;;;;CAaA,AAAO,IACL,KACA,cACoB;EACpB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,IAAI,KAAK,QAAQ,KAAe,YAAY;CACrD;;;;;;;;;;;CAYA,MAAM,QACJ,KACA,cAC6B;EAC7B,MAAM,KAAK,KAAK;EAChB,OAAO,KAAK,IAAI,KAAK,YAAY;CACnC;;;;CAKA,IAAW,WAAoB;EAC7B,OAAO,KAAK,WAAW;CACzB;;;;;;CAOA,AAAO,SAAwB;EAC7B,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,KAAK;CACd;;;;CAKA,MAAa,SAAwB;EACnC,KAAK,SAAS;EACd,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;CAClB;AACF;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB,IAAI,qBAAqB"}
@@ -0,0 +1,25 @@
1
+ import { get } from "@mongez/reinforcements";
2
+
3
+ //#region ../core/src/warlock-config/warn-deprecated-config.ts
4
+ /**
5
+ * Keys of `warlock.config.ts` that are declared but never read, and where the
6
+ * setting lives instead.
7
+ */
8
+ const deprecatedConfigKeys = {
9
+ "server.port": "set the HTTP port in `config/http.ts` (`port`)",
10
+ "server.host": "set the HTTP host in `config/http.ts` (`host`)",
11
+ "server.retryOtherPort": "port fallback is not implemented, remove it",
12
+ "tests.include": "configure test discovery in `config/tests.ts`",
13
+ "tests.exclude": "configure test discovery in `config/tests.ts`"
14
+ };
15
+ /**
16
+ * Build one warning per deprecated key the app actually sets.
17
+ */
18
+ function getDeprecatedConfigWarnings(config) {
19
+ if (!config) return [];
20
+ return Object.entries(deprecatedConfigKeys).filter(([key]) => get(config, key) !== void 0).map(([key, instead]) => `warlock.config.ts: \`${key}\` is deprecated and ignored by the framework; ${instead}.`);
21
+ }
22
+
23
+ //#endregion
24
+ export { getDeprecatedConfigWarnings };
25
+ //# sourceMappingURL=warn-deprecated-config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warn-deprecated-config.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warn-deprecated-config.ts"],"sourcesContent":["import { get } from \"@mongez/reinforcements\";\nimport type { WarlockConfig } from \"./types\";\n\n/**\n * Keys of `warlock.config.ts` that are declared but never read, and where the\n * setting lives instead.\n */\nexport const deprecatedConfigKeys: Record<string, string> = {\n \"server.port\": \"set the HTTP port in `config/http.ts` (`port`)\",\n \"server.host\": \"set the HTTP host in `config/http.ts` (`host`)\",\n \"server.retryOtherPort\": \"port fallback is not implemented, remove it\",\n \"tests.include\": \"configure test discovery in `config/tests.ts`\",\n \"tests.exclude\": \"configure test discovery in `config/tests.ts`\",\n};\n\n/**\n * Build one warning per deprecated key the app actually sets.\n */\nexport function getDeprecatedConfigWarnings(config: WarlockConfig | undefined): string[] {\n if (!config) return [];\n\n return Object.entries(deprecatedConfigKeys)\n .filter(([key]) => get(config, key) !== undefined)\n .map(\n ([key, instead]) =>\n `warlock.config.ts: \\`${key}\\` is deprecated and ignored by the framework; ${instead}.`,\n );\n}\n"],"mappings":";;;;;;;AAOA,MAAa,uBAA+C;CAC1D,eAAe;CACf,eAAe;CACf,yBAAyB;CACzB,iBAAiB;CACjB,iBAAiB;AACnB;;;;AAKA,SAAgB,4BAA4B,QAA6C;CACvF,IAAI,CAAC,QAAQ,OAAO,CAAC;CAErB,OAAO,OAAO,QAAQ,oBAAoB,CAAC,CACxC,QAAQ,CAAC,SAAS,IAAI,QAAQ,GAAG,MAAM,MAAS,CAAC,CACjD,KACE,CAAC,KAAK,aACL,wBAAwB,IAAI,iDAAiD,QAAQ,EACzF;AACJ"}