@warlock.js/core 5.30.0 → 5.31.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 (797) hide show
  1. package/CHANGELOG.md +142 -36
  2. package/README.md +28 -23
  3. package/bin/source-load-hook.mjs +63 -0
  4. package/bin/warlock.js +102 -42
  5. package/esm/cli/commands/add.command.mjs +4 -2
  6. package/esm/cli/commands/add.command.mjs.map +1 -1
  7. package/esm/cli/commands/agents-md/agents-md-command.action.mjs +69 -0
  8. package/esm/cli/commands/agents-md/agents-md-command.action.mjs.map +1 -0
  9. package/esm/cli/commands/agents-md.command.mjs +38 -0
  10. package/esm/cli/commands/agents-md.command.mjs.map +1 -0
  11. package/esm/cli/commands/board/board-command.action.mjs +279 -0
  12. package/esm/cli/commands/board/board-command.action.mjs.map +1 -0
  13. package/esm/cli/commands/board.command.mjs +78 -0
  14. package/esm/cli/commands/board.command.mjs.map +1 -0
  15. package/esm/cli/commands/build.command.mjs +6 -1
  16. package/esm/cli/commands/build.command.mjs.map +1 -1
  17. package/esm/cli/commands/check/check-command.action.mjs +57 -0
  18. package/esm/cli/commands/check/check-command.action.mjs.map +1 -0
  19. package/esm/cli/commands/check/child-typecheck.mjs +66 -0
  20. package/esm/cli/commands/check/child-typecheck.mjs.map +1 -0
  21. package/esm/cli/commands/check/format-problems.mjs +43 -0
  22. package/esm/cli/commands/check/format-problems.mjs.map +1 -0
  23. package/esm/cli/commands/check/typecheck-command.action.mjs +61 -0
  24. package/esm/cli/commands/check/typecheck-command.action.mjs.map +1 -0
  25. package/esm/cli/commands/check.command.mjs +50 -0
  26. package/esm/cli/commands/check.command.mjs.map +1 -0
  27. package/esm/cli/commands/dev-server.command.mjs +9 -1
  28. package/esm/cli/commands/dev-server.command.mjs.map +1 -1
  29. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs +8 -3
  30. package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
  31. package/esm/cli/commands/doctor/check.types.d.mts +76 -0
  32. package/esm/cli/commands/doctor/check.types.d.mts.map +1 -0
  33. package/esm/cli/commands/doctor/checks/config.check.mjs +1 -0
  34. package/esm/cli/commands/doctor/checks/config.check.mjs.map +1 -1
  35. package/esm/cli/commands/doctor/checks/connectors.check.mjs +2 -0
  36. package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
  37. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +1 -0
  38. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
  39. package/esm/cli/commands/doctor/checks/health.check.mjs +3 -0
  40. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
  41. package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs +1 -0
  42. package/esm/cli/commands/doctor/checks/jwt-secret.check.mjs.map +1 -1
  43. package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs +1 -0
  44. package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs.map +1 -1
  45. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +1 -0
  46. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  47. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs +4 -0
  48. package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -1
  49. package/esm/cli/commands/doctor/checks/routes.check.mjs +3 -0
  50. package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
  51. package/esm/cli/commands/doctor/doctor-command.action.mjs +14 -6
  52. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
  53. package/esm/cli/commands/doctor/format-report.mjs +30 -2
  54. package/esm/cli/commands/doctor/format-report.mjs.map +1 -1
  55. package/esm/cli/commands/doctor.command.mjs +4 -0
  56. package/esm/cli/commands/doctor.command.mjs.map +1 -1
  57. package/esm/cli/commands/explain/explain-command.action.mjs +64 -0
  58. package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -0
  59. package/esm/cli/commands/explain.command.mjs +23 -0
  60. package/esm/cli/commands/explain.command.mjs.map +1 -0
  61. package/esm/cli/commands/generate/generate.command.mjs +124 -124
  62. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  63. package/esm/cli/commands/generate/generators/endpoint.generator.mjs +311 -0
  64. package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -0
  65. package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
  66. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  67. package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
  68. package/esm/cli/commands/generate/templates/endpoint-stubs.mjs +341 -0
  69. package/esm/cli/commands/generate/templates/endpoint-stubs.mjs.map +1 -0
  70. package/esm/cli/commands/generate/templates/stubs.mjs +19 -6
  71. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  72. package/esm/cli/commands/generate/utils/endpoint-schema.mjs +137 -0
  73. package/esm/cli/commands/generate/utils/endpoint-schema.mjs.map +1 -0
  74. package/esm/cli/commands/generate/utils/name-parser.mjs +1 -1
  75. package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
  76. package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
  77. package/esm/cli/commands/generate/utils/writer.mjs +38 -13
  78. package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
  79. package/esm/cli/commands/inspect/inspect-command.action.mjs +72 -0
  80. package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -0
  81. package/esm/cli/commands/inspect.command.mjs +39 -0
  82. package/esm/cli/commands/inspect.command.mjs.map +1 -0
  83. package/esm/cli/commands/mcp/mcp-command.action.mjs +103 -0
  84. package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -0
  85. package/esm/cli/commands/mcp.command.mjs +76 -0
  86. package/esm/cli/commands/mcp.command.mjs.map +1 -0
  87. package/esm/cli/commands/openapi-generator.action.mjs.map +1 -1
  88. package/esm/cli/commands/openapi-generator.command.mjs.map +1 -1
  89. package/esm/cli/commands/postman-generator.action.mjs.map +1 -1
  90. package/esm/cli/commands/postman-generator.command.mjs.map +1 -1
  91. package/esm/cli/commands/routes/routes-diff.action.mjs +1 -1
  92. package/esm/cli/commands/test.command.mjs +131 -0
  93. package/esm/cli/commands/test.command.mjs.map +1 -0
  94. package/esm/cli/commands/trace/trace-command.action.mjs +145 -0
  95. package/esm/cli/commands/trace/trace-command.action.mjs.map +1 -0
  96. package/esm/cli/commands/trace.command.mjs +60 -0
  97. package/esm/cli/commands/trace.command.mjs.map +1 -0
  98. package/esm/cli/commands/typecheck.command.mjs +40 -0
  99. package/esm/cli/commands/typecheck.command.mjs.map +1 -0
  100. package/esm/cli/commands/upgrade.command.mjs +46 -0
  101. package/esm/cli/commands/upgrade.command.mjs.map +1 -0
  102. package/esm/cli/flush-and-exit.mjs +34 -0
  103. package/esm/cli/flush-and-exit.mjs.map +1 -0
  104. package/esm/cli/framework-cli-commands.mjs +23 -1
  105. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  106. package/esm/cli/parse-cli-args.mjs +1 -0
  107. package/esm/cli/parse-cli-args.mjs.map +1 -1
  108. package/esm/config/config-loader.d.mts.map +1 -1
  109. package/esm/config/config-loader.mjs +34 -13
  110. package/esm/config/config-loader.mjs.map +1 -1
  111. package/esm/config/config-schema.d.mts +67 -0
  112. package/esm/config/config-schema.d.mts.map +1 -0
  113. package/esm/config/config-schema.mjs +77 -0
  114. package/esm/config/config-schema.mjs.map +1 -0
  115. package/esm/config/index.d.mts +6 -0
  116. package/esm/config/index.mjs +1 -0
  117. package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -1
  118. package/esm/connectors/assert-no-reserved-connector-names.mjs +2 -1
  119. package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
  120. package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -1
  121. package/esm/connectors/assert-unique-connector-names.mjs +26 -2
  122. package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
  123. package/esm/connectors/http-connector.d.mts.map +1 -1
  124. package/esm/connectors/http-connector.mjs +3 -2
  125. package/esm/connectors/http-connector.mjs.map +1 -1
  126. package/esm/connectors/single-server-warnings.mjs.map +1 -1
  127. package/esm/connectors/socket-connector.d.mts.map +1 -1
  128. package/esm/connectors/socket-connector.mjs.map +1 -1
  129. package/esm/container/index.d.mts.map +1 -1
  130. package/esm/container/index.mjs.map +1 -1
  131. package/esm/database/migrate-action.mjs.map +1 -1
  132. package/esm/database/models/database-log/database-log.d.mts +0 -28
  133. package/esm/database/models/database-log/database-log.d.mts.map +1 -1
  134. package/esm/database/seeds/seeder.errors.d.mts +4 -0
  135. package/esm/database/seeds/seeder.errors.d.mts.map +1 -1
  136. package/esm/database/seeds/seeder.errors.mjs +2 -0
  137. package/esm/database/seeds/seeder.errors.mjs.map +1 -1
  138. package/esm/dev-server/boot-precondition-error.mjs +1 -0
  139. package/esm/dev-server/boot-precondition-error.mjs.map +1 -1
  140. package/esm/dev-server/create-worker.mjs +15 -9
  141. package/esm/dev-server/create-worker.mjs.map +1 -1
  142. package/esm/dev-server/dev-agent-tooling.mjs +135 -0
  143. package/esm/dev-server/dev-agent-tooling.mjs.map +1 -0
  144. package/esm/dev-server/dev-logger.mjs.map +1 -1
  145. package/esm/dev-server/development-server.mjs +8 -0
  146. package/esm/dev-server/development-server.mjs.map +1 -1
  147. package/esm/dev-server/files-orchestrator.mjs +4 -5
  148. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  149. package/esm/dev-server/files-watcher.mjs +1 -1
  150. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +2 -3
  151. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
  152. package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +33 -0
  153. package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs.map +1 -0
  154. package/esm/dev-server/health-checker/default-health-checkers.mjs +31 -0
  155. package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -0
  156. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +9 -3
  157. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
  158. package/esm/dev-server/health-checker/files-healthcare.manager.mjs +15 -4
  159. package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
  160. package/esm/dev-server/health-checker/oxlint-runner.mjs +302 -0
  161. package/esm/dev-server/health-checker/oxlint-runner.mjs.map +1 -0
  162. package/esm/dev-server/health-checker/workers/oxlint-health.worker.d.mts +1 -0
  163. package/esm/dev-server/health-checker/workers/oxlint-health.worker.mjs +42 -0
  164. package/esm/dev-server/health-checker/workers/oxlint-health.worker.mjs.map +1 -0
  165. package/esm/dev-server/loader/loader-port-acks.mjs +1 -1
  166. package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
  167. package/esm/dev-server/loader/register-loader.mjs +1 -1
  168. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  169. package/esm/dev-server/loader/resolve-hook.mjs +1 -1
  170. package/esm/dev-server/loader/transpile-cache.mjs +1 -1
  171. package/esm/dev-server/model-resource-typings.mjs.map +1 -1
  172. package/esm/dev-server/module-loader.mjs +1 -0
  173. package/esm/dev-server/module-loader.mjs.map +1 -1
  174. package/esm/dev-server/parse-imports.mjs +1 -1
  175. package/esm/dev-server/parse-imports.mjs.map +1 -1
  176. package/esm/dev-server/route-types-publisher.mjs +1 -1
  177. package/esm/dev-server/start-development-server.mjs.map +1 -1
  178. package/esm/dev-server/translation-keys-sources.mjs +1 -1
  179. package/esm/dev-server/translation-keys-sources.mjs.map +1 -1
  180. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  181. package/esm/dev-server/type-generator.mjs +3 -3
  182. package/esm/dev-server/type-generator.mjs.map +1 -1
  183. package/esm/dev-server/types.d.mts.map +1 -1
  184. package/esm/errors/container-key-missing-error.mjs +1 -0
  185. package/esm/errors/container-key-missing-error.mjs.map +1 -1
  186. package/esm/errors/error-codes.d.mts +653 -0
  187. package/esm/errors/error-codes.d.mts.map +1 -0
  188. package/esm/errors/error-codes.mjs +634 -0
  189. package/esm/errors/error-codes.mjs.map +1 -0
  190. package/esm/errors/esbuild-binary-missing-error.mjs +1 -0
  191. package/esm/errors/esbuild-binary-missing-error.mjs.map +1 -1
  192. package/esm/errors/index.d.mts +3 -1
  193. package/esm/errors/index.mjs +3 -1
  194. package/esm/errors/unknown-locale-error.mjs +1 -0
  195. package/esm/errors/unknown-locale-error.mjs.map +1 -1
  196. package/esm/errors/warlock-error.d.mts +23 -0
  197. package/esm/errors/warlock-error.d.mts.map +1 -0
  198. package/esm/errors/warlock-error.mjs +22 -0
  199. package/esm/errors/warlock-error.mjs.map +1 -0
  200. package/esm/generations/add-command.action.mjs +19 -3
  201. package/esm/generations/add-command.action.mjs.map +1 -1
  202. package/esm/generations/features/access.feature.mjs +1 -1
  203. package/esm/generations/features/access.feature.mjs.map +1 -1
  204. package/esm/generations/features/bull-board.feature.mjs +1 -1
  205. package/esm/generations/features/bull-board.feature.mjs.map +1 -1
  206. package/esm/generations/features/mysql.feature.mjs.map +1 -1
  207. package/esm/generations/features/notifications.feature.mjs +5 -2
  208. package/esm/generations/features/notifications.feature.mjs.map +1 -1
  209. package/esm/generations/features/queue.feature.mjs +1 -1
  210. package/esm/generations/features/queue.feature.mjs.map +1 -1
  211. package/esm/generations/features/react-email.feature.mjs +1 -1
  212. package/esm/generations/features/react-email.feature.mjs.map +1 -1
  213. package/esm/generations/features/shadcn.feature.mjs +1 -1
  214. package/esm/generations/features/shadcn.feature.mjs.map +1 -1
  215. package/esm/generations/features/shared/link-ai-package-import.mjs +1 -1
  216. package/esm/generations/features/shared/link-ai-package-import.mjs.map +1 -1
  217. package/esm/generations/features/shared/merge-web-sitemap-config.mjs.map +1 -1
  218. package/esm/generations/features/shared/patch-tsconfig-include.mjs +1 -1
  219. package/esm/generations/features/shared/patch-tsconfig-include.mjs.map +1 -1
  220. package/esm/generations/features/shared/relocate-conflicting-home-route.mjs +1 -1
  221. package/esm/generations/features/shared/relocate-conflicting-home-route.mjs.map +1 -1
  222. package/esm/generations/features/sitemap.feature.mjs +1 -1
  223. package/esm/generations/features/sitemap.feature.mjs.map +1 -1
  224. package/esm/generations/features/tailwind.feature.mjs +1 -1
  225. package/esm/generations/features/tailwind.feature.mjs.map +1 -1
  226. package/esm/generations/features/test.feature.mjs +11 -6
  227. package/esm/generations/features/test.feature.mjs.map +1 -1
  228. package/esm/generations/features/web.feature.mjs +1 -1
  229. package/esm/generations/features/web.feature.mjs.map +1 -1
  230. package/esm/generations/generation-fs.mjs +73 -0
  231. package/esm/generations/generation-fs.mjs.map +1 -0
  232. package/esm/generations/generation-run.mjs +185 -0
  233. package/esm/generations/generation-run.mjs.map +1 -0
  234. package/esm/generations/stubs.mjs +155 -13
  235. package/esm/generations/stubs.mjs.map +1 -1
  236. package/esm/http/bounded-stream-queue.d.mts +10 -0
  237. package/esm/http/bounded-stream-queue.d.mts.map +1 -0
  238. package/esm/http/bounded-stream-queue.mjs +97 -0
  239. package/esm/http/bounded-stream-queue.mjs.map +1 -0
  240. package/esm/http/build-rate-limit-options.d.mts +13 -0
  241. package/esm/http/build-rate-limit-options.d.mts.map +1 -1
  242. package/esm/http/build-rate-limit-options.mjs.map +1 -1
  243. package/esm/http/conditional-request.d.mts +86 -0
  244. package/esm/http/conditional-request.d.mts.map +1 -0
  245. package/esm/http/conditional-request.mjs +121 -0
  246. package/esm/http/conditional-request.mjs.map +1 -0
  247. package/esm/http/csp.d.mts +2 -0
  248. package/esm/http/csp.d.mts.map +1 -1
  249. package/esm/http/csp.mjs +1 -0
  250. package/esm/http/csp.mjs.map +1 -1
  251. package/esm/http/csrf-origin-policy.mjs.map +1 -1
  252. package/esm/http/database/RequestLog.d.mts +24 -84
  253. package/esm/http/database/RequestLog.d.mts.map +1 -1
  254. package/esm/http/errors/errors.d.mts +2 -0
  255. package/esm/http/errors/errors.d.mts.map +1 -1
  256. package/esm/http/errors/errors.mjs +1 -0
  257. package/esm/http/errors/errors.mjs.map +1 -1
  258. package/esm/http/escape-header-value.d.mts +18 -0
  259. package/esm/http/escape-header-value.d.mts.map +1 -0
  260. package/esm/http/escape-header-value.mjs +27 -0
  261. package/esm/http/escape-header-value.mjs.map +1 -0
  262. package/esm/http/flush-pending-cookies.mjs.map +1 -1
  263. package/esm/http/index.d.mts +5 -2
  264. package/esm/http/index.mjs +4 -1
  265. package/esm/http/lazy-static.mjs +194 -0
  266. package/esm/http/lazy-static.mjs.map +1 -0
  267. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  268. package/esm/http/plugins.d.mts.map +1 -1
  269. package/esm/http/plugins.mjs +7 -4
  270. package/esm/http/plugins.mjs.map +1 -1
  271. package/esm/http/rate-limit-cache-store.mjs +2 -1
  272. package/esm/http/rate-limit-cache-store.mjs.map +1 -1
  273. package/esm/http/request-log-snapshot.d.mts +12 -5
  274. package/esm/http/request-log-snapshot.d.mts.map +1 -1
  275. package/esm/http/request-log-snapshot.mjs +83 -4
  276. package/esm/http/request-log-snapshot.mjs.map +1 -1
  277. package/esm/http/request.d.mts +2 -2
  278. package/esm/http/request.d.mts.map +1 -1
  279. package/esm/http/request.mjs +5 -4
  280. package/esm/http/request.mjs.map +1 -1
  281. package/esm/http/response.d.mts +11 -1
  282. package/esm/http/response.d.mts.map +1 -1
  283. package/esm/http/response.mjs +164 -76
  284. package/esm/http/response.mjs.map +1 -1
  285. package/esm/http/safe-redirect.d.mts +51 -0
  286. package/esm/http/safe-redirect.d.mts.map +1 -0
  287. package/esm/http/safe-redirect.mjs +80 -0
  288. package/esm/http/safe-redirect.mjs.map +1 -0
  289. package/esm/http/serialized.d.mts.map +1 -1
  290. package/esm/http/server.d.mts +3 -1
  291. package/esm/http/server.d.mts.map +1 -1
  292. package/esm/http/server.mjs +14 -4
  293. package/esm/http/server.mjs.map +1 -1
  294. package/esm/http/stream-transport.mjs +4 -2
  295. package/esm/http/stream-transport.mjs.map +1 -1
  296. package/esm/http/types.d.mts +44 -11
  297. package/esm/http/types.d.mts.map +1 -1
  298. package/esm/http/uploads/generate-image-variant.mjs +1 -1
  299. package/esm/http/uploads/generate-image-variants.d.mts +10 -7
  300. package/esm/http/uploads/generate-image-variants.d.mts.map +1 -1
  301. package/esm/http/uploads/generate-image-variants.mjs +61 -24
  302. package/esm/http/uploads/generate-image-variants.mjs.map +1 -1
  303. package/esm/http/uploads/image-variant-cache-entries.mjs +78 -0
  304. package/esm/http/uploads/image-variant-cache-entries.mjs.map +1 -0
  305. package/esm/http/uploads/image-variant-cache.mjs +72 -30
  306. package/esm/http/uploads/image-variant-cache.mjs.map +1 -1
  307. package/esm/http/uploads/image-variant-types.d.mts +2 -0
  308. package/esm/http/uploads/image-variant-types.d.mts.map +1 -1
  309. package/esm/http/uploads/image-variants-config-error.d.mts +3 -1
  310. package/esm/http/uploads/image-variants-config-error.d.mts.map +1 -1
  311. package/esm/http/uploads/image-variants-config-error.mjs +3 -2
  312. package/esm/http/uploads/image-variants-config-error.mjs.map +1 -1
  313. package/esm/http/uploads/load-variant-source.mjs +1 -1
  314. package/esm/http/uploads/load-variant-source.mjs.map +1 -1
  315. package/esm/http/uploads/purge-image-variants.d.mts +7 -2
  316. package/esm/http/uploads/purge-image-variants.d.mts.map +1 -1
  317. package/esm/http/uploads/purge-image-variants.mjs +16 -16
  318. package/esm/http/uploads/purge-image-variants.mjs.map +1 -1
  319. package/esm/http/uploads/remote-variant-source.mjs +115 -0
  320. package/esm/http/uploads/remote-variant-source.mjs.map +1 -0
  321. package/esm/http/uploads/resolve-image-variant.d.mts +3 -4
  322. package/esm/http/uploads/resolve-image-variant.d.mts.map +1 -1
  323. package/esm/http/uploads/resolve-image-variant.mjs +10 -18
  324. package/esm/http/uploads/resolve-image-variant.mjs.map +1 -1
  325. package/esm/http/uploads/resolve-image-variants-config.mjs +5 -3
  326. package/esm/http/uploads/resolve-image-variants-config.mjs.map +1 -1
  327. package/esm/http/uploads/resolve-remote-image-variant.mjs +89 -0
  328. package/esm/http/uploads/resolve-remote-image-variant.mjs.map +1 -0
  329. package/esm/http/uploads/trim-image-variant-cache.mjs +193 -0
  330. package/esm/http/uploads/trim-image-variant-cache.mjs.map +1 -0
  331. package/esm/http/uploads/uploaded-file.controller.d.mts +5 -3
  332. package/esm/http/uploads/uploaded-file.controller.d.mts.map +1 -1
  333. package/esm/http/uploads/uploaded-file.controller.mjs +16 -8
  334. package/esm/http/uploads/uploaded-file.controller.mjs.map +1 -1
  335. package/esm/http/uploads/variant-cache-key.mjs +1 -1
  336. package/esm/http/uploads-types.d.mts +6 -2
  337. package/esm/http/uploads-types.d.mts.map +1 -1
  338. package/esm/index.d.mts +17 -6
  339. package/esm/index.mjs +14 -3
  340. package/esm/localization/parse-locale-dictionary.d.mts +2 -0
  341. package/esm/localization/parse-locale-dictionary.d.mts.map +1 -1
  342. package/esm/localization/parse-locale-dictionary.mjs +1 -0
  343. package/esm/localization/parse-locale-dictionary.mjs.map +1 -1
  344. package/esm/mail/config.d.mts +12 -1
  345. package/esm/mail/config.d.mts.map +1 -1
  346. package/esm/mail/config.mjs +16 -1
  347. package/esm/mail/config.mjs.map +1 -1
  348. package/esm/mail/index.d.mts +1 -1
  349. package/esm/mail/index.mjs +1 -1
  350. package/esm/net/index.d.mts +2 -0
  351. package/esm/net/index.mjs +3 -0
  352. package/esm/openapi/build-openapi-document.d.mts.map +1 -1
  353. package/esm/openapi/build-openapi-document.mjs +8 -5
  354. package/esm/openapi/build-openapi-document.mjs.map +1 -1
  355. package/esm/openapi/error-responses.mjs +28 -2
  356. package/esm/openapi/error-responses.mjs.map +1 -1
  357. package/esm/openapi/postman/build-request-body.mjs.map +1 -1
  358. package/esm/openapi/postman/build-request-url.mjs.map +1 -1
  359. package/esm/openapi/postman/build-saved-responses.mjs.map +1 -1
  360. package/esm/openapi/postman/example-from-schema.d.mts.map +1 -1
  361. package/esm/openapi/postman/example-from-schema.mjs.map +1 -1
  362. package/esm/openapi/postman/openapi-to-postman-collection.d.mts.map +1 -1
  363. package/esm/openapi/postman/openapi-to-postman-collection.mjs.map +1 -1
  364. package/esm/openapi/postman/schema-refs.mjs.map +1 -1
  365. package/esm/openapi/postman/uuid-v5.mjs.map +1 -1
  366. package/esm/openapi/request-schemas.mjs.map +1 -1
  367. package/esm/openapi/response-schemas.mjs.map +1 -1
  368. package/esm/openapi/schema-registry.d.mts +1 -0
  369. package/esm/openapi/schema-registry.mjs.map +1 -1
  370. package/esm/openapi/security.mjs.map +1 -1
  371. package/esm/openapi/types.d.mts +3 -2
  372. package/esm/openapi/types.d.mts.map +1 -1
  373. package/esm/production/assert-generated-imports.mjs +1 -0
  374. package/esm/production/assert-generated-imports.mjs.map +1 -1
  375. package/esm/production/production-builder.mjs +12 -4
  376. package/esm/production/production-builder.mjs.map +1 -1
  377. package/esm/production/resolve-build-config.mjs +1 -0
  378. package/esm/production/resolve-build-config.mjs.map +1 -1
  379. package/esm/production/route-registration-child.mjs.map +1 -1
  380. package/esm/production/route-registration-snapshot.mjs.map +1 -1
  381. package/esm/repositories/contracts/query-builder.contract.d.mts.map +1 -1
  382. package/esm/repositories/contracts/types.d.mts.map +1 -1
  383. package/esm/repositories/repository.manager.mjs.map +1 -1
  384. package/esm/resource/resource.d.mts.map +1 -1
  385. package/esm/resource/resource.mjs.map +1 -1
  386. package/esm/resource/types.d.mts.map +1 -1
  387. package/esm/restful/restful.d.mts +5 -5
  388. package/esm/restful/restful.d.mts.map +1 -1
  389. package/esm/restful/restful.mjs.map +1 -1
  390. package/esm/router/build-route-rate-limit.mjs.map +1 -1
  391. package/esm/router/dev-dispatch.mjs +31 -12
  392. package/esm/router/dev-dispatch.mjs.map +1 -1
  393. package/esm/router/index.d.mts +4 -2
  394. package/esm/router/index.mjs +2 -0
  395. package/esm/router/log-request-lifecycle.mjs.map +1 -1
  396. package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
  397. package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
  398. package/esm/router/resolve-route-rate-limit.mjs +47 -0
  399. package/esm/router/resolve-route-rate-limit.mjs.map +1 -0
  400. package/esm/router/resolve-route-validation.mjs +19 -0
  401. package/esm/router/resolve-route-validation.mjs.map +1 -0
  402. package/esm/router/route-guard-names.mjs +30 -0
  403. package/esm/router/route-guard-names.mjs.map +1 -0
  404. package/esm/router/route-name-taken-error.d.mts +14 -0
  405. package/esm/router/route-name-taken-error.d.mts.map +1 -0
  406. package/esm/router/route-name-taken-error.mjs +18 -0
  407. package/esm/router/route-name-taken-error.mjs.map +1 -0
  408. package/esm/router/router.d.mts +32 -13
  409. package/esm/router/router.d.mts.map +1 -1
  410. package/esm/router/router.mjs +71 -38
  411. package/esm/router/router.mjs.map +1 -1
  412. package/esm/router/types.d.mts +137 -17
  413. package/esm/router/types.d.mts.map +1 -1
  414. package/esm/router/unguarded-route-diagnostics.d.mts +40 -0
  415. package/esm/router/unguarded-route-diagnostics.d.mts.map +1 -0
  416. package/esm/router/unguarded-route-diagnostics.mjs +95 -0
  417. package/esm/router/unguarded-route-diagnostics.mjs.map +1 -0
  418. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  419. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  420. package/esm/storage/drivers/local-driver.d.mts.map +1 -1
  421. package/esm/storage/drivers/local-driver.mjs.map +1 -1
  422. package/esm/storage/local-disk-roots.mjs +24 -0
  423. package/esm/storage/local-disk-roots.mjs.map +1 -0
  424. package/esm/storage/storage.d.mts +14 -0
  425. package/esm/storage/storage.d.mts.map +1 -1
  426. package/esm/storage/storage.mjs +22 -0
  427. package/esm/storage/storage.mjs.map +1 -1
  428. package/esm/storage/types.d.mts.map +1 -1
  429. package/esm/storage/utils/storage-not-initialized-error.d.mts +2 -0
  430. package/esm/storage/utils/storage-not-initialized-error.d.mts.map +1 -1
  431. package/esm/storage/utils/storage-not-initialized-error.mjs +1 -0
  432. package/esm/storage/utils/storage-not-initialized-error.mjs.map +1 -1
  433. package/esm/tests/authenticated-test-client.d.mts +32 -0
  434. package/esm/tests/authenticated-test-client.d.mts.map +1 -0
  435. package/esm/tests/authenticated-test-client.mjs +40 -0
  436. package/esm/tests/authenticated-test-client.mjs.map +1 -0
  437. package/esm/tests/factories.d.mts +59 -0
  438. package/esm/tests/factories.d.mts.map +1 -0
  439. package/esm/tests/factories.mjs +105 -0
  440. package/esm/tests/factories.mjs.map +1 -0
  441. package/esm/tests/fakes/cache-fake.d.mts +29 -0
  442. package/esm/tests/fakes/cache-fake.d.mts.map +1 -0
  443. package/esm/tests/fakes/cache-fake.mjs +79 -0
  444. package/esm/tests/fakes/cache-fake.mjs.map +1 -0
  445. package/esm/tests/fakes/clock-fake.d.mts +32 -0
  446. package/esm/tests/fakes/clock-fake.d.mts.map +1 -0
  447. package/esm/tests/fakes/clock-fake.mjs +69 -0
  448. package/esm/tests/fakes/clock-fake.mjs.map +1 -0
  449. package/esm/tests/fakes/fake-assertion-error.d.mts +13 -0
  450. package/esm/tests/fakes/fake-assertion-error.d.mts.map +1 -0
  451. package/esm/tests/fakes/fake-assertion-error.mjs +20 -0
  452. package/esm/tests/fakes/fake-assertion-error.mjs.map +1 -0
  453. package/esm/tests/fakes/ids-fake.d.mts +22 -0
  454. package/esm/tests/fakes/ids-fake.d.mts.map +1 -0
  455. package/esm/tests/fakes/ids-fake.mjs +57 -0
  456. package/esm/tests/fakes/ids-fake.mjs.map +1 -0
  457. package/esm/tests/fakes/index.d.mts +8 -0
  458. package/esm/tests/fakes/index.mjs +10 -0
  459. package/esm/tests/fakes/mail-fake.d.mts +41 -0
  460. package/esm/tests/fakes/mail-fake.d.mts.map +1 -0
  461. package/esm/tests/fakes/mail-fake.mjs +111 -0
  462. package/esm/tests/fakes/mail-fake.mjs.map +1 -0
  463. package/esm/tests/fakes/queue-fake.d.mts +48 -0
  464. package/esm/tests/fakes/queue-fake.d.mts.map +1 -0
  465. package/esm/tests/fakes/queue-fake.mjs +134 -0
  466. package/esm/tests/fakes/queue-fake.mjs.map +1 -0
  467. package/esm/tests/fakes/storage-fake.d.mts +31 -0
  468. package/esm/tests/fakes/storage-fake.d.mts.map +1 -0
  469. package/esm/tests/fakes/storage-fake.mjs +122 -0
  470. package/esm/tests/fakes/storage-fake.mjs.map +1 -0
  471. package/esm/tests/fakes/test-fakes.d.mts +39 -0
  472. package/esm/tests/fakes/test-fakes.d.mts.map +1 -0
  473. package/esm/tests/fakes/test-fakes.mjs +90 -0
  474. package/esm/tests/fakes/test-fakes.mjs.map +1 -0
  475. package/esm/tests/index.d.mts +14 -2
  476. package/esm/tests/index.mjs +15 -2
  477. package/esm/tests/snapshot-response.d.mts +34 -0
  478. package/esm/tests/snapshot-response.d.mts.map +1 -0
  479. package/esm/tests/snapshot-response.mjs +42 -0
  480. package/esm/tests/snapshot-response.mjs.map +1 -0
  481. package/esm/tests/start-http-development-server.d.mts +22 -0
  482. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  483. package/esm/tests/start-http-development-server.mjs +36 -3
  484. package/esm/tests/start-http-development-server.mjs.map +1 -1
  485. package/esm/tests/test-helpers.d.mts +20 -1
  486. package/esm/tests/test-helpers.d.mts.map +1 -1
  487. package/esm/tests/test-helpers.mjs +40 -27
  488. package/esm/tests/test-helpers.mjs.map +1 -1
  489. package/esm/tests/test-http-server.d.mts +62 -0
  490. package/esm/tests/test-http-server.d.mts.map +1 -0
  491. package/esm/tests/test-http-server.mjs +119 -0
  492. package/esm/tests/test-http-server.mjs.map +1 -0
  493. package/esm/tests/vitest-setup.d.mts +11 -0
  494. package/esm/tests/vitest-setup.d.mts.map +1 -1
  495. package/esm/tests/vitest-setup.mjs +12 -4
  496. package/esm/tests/vitest-setup.mjs.map +1 -1
  497. package/esm/tooling/agents-md/agents-md-markers.d.mts +27 -0
  498. package/esm/tooling/agents-md/agents-md-markers.d.mts.map +1 -0
  499. package/esm/tooling/agents-md/agents-md-markers.mjs +59 -0
  500. package/esm/tooling/agents-md/agents-md-markers.mjs.map +1 -0
  501. package/esm/tooling/agents-md/agents-md-refresher.d.mts +36 -0
  502. package/esm/tooling/agents-md/agents-md-refresher.d.mts.map +1 -0
  503. package/esm/tooling/agents-md/agents-md-refresher.mjs +72 -0
  504. package/esm/tooling/agents-md/agents-md-refresher.mjs.map +1 -0
  505. package/esm/tooling/agents-md/index.mjs +7 -0
  506. package/esm/tooling/agents-md/installed-skills.d.mts +30 -0
  507. package/esm/tooling/agents-md/installed-skills.d.mts.map +1 -0
  508. package/esm/tooling/agents-md/installed-skills.mjs +74 -0
  509. package/esm/tooling/agents-md/installed-skills.mjs.map +1 -0
  510. package/esm/tooling/agents-md/render-agents-md.d.mts +33 -0
  511. package/esm/tooling/agents-md/render-agents-md.d.mts.map +1 -0
  512. package/esm/tooling/agents-md/render-agents-md.mjs +195 -0
  513. package/esm/tooling/agents-md/render-agents-md.mjs.map +1 -0
  514. package/esm/tooling/agents-md/write-agents-md.d.mts +33 -0
  515. package/esm/tooling/agents-md/write-agents-md.d.mts.map +1 -0
  516. package/esm/tooling/agents-md/write-agents-md.mjs +49 -0
  517. package/esm/tooling/agents-md/write-agents-md.mjs.map +1 -0
  518. package/esm/tooling/board/board-message.d.mts +31 -0
  519. package/esm/tooling/board/board-message.d.mts.map +1 -0
  520. package/esm/tooling/board/board-message.mjs +86 -0
  521. package/esm/tooling/board/board-message.mjs.map +1 -0
  522. package/esm/tooling/board/board.d.mts +109 -0
  523. package/esm/tooling/board/board.d.mts.map +1 -0
  524. package/esm/tooling/board/board.mjs +406 -0
  525. package/esm/tooling/board/board.mjs.map +1 -0
  526. package/esm/tooling/board/board.types.d.mts +72 -0
  527. package/esm/tooling/board/board.types.d.mts.map +1 -0
  528. package/esm/tooling/board/board.types.mjs +54 -0
  529. package/esm/tooling/board/board.types.mjs.map +1 -0
  530. package/esm/tooling/check/changed-files.d.mts +33 -0
  531. package/esm/tooling/check/changed-files.d.mts.map +1 -0
  532. package/esm/tooling/check/changed-files.mjs +73 -0
  533. package/esm/tooling/check/changed-files.mjs.map +1 -0
  534. package/esm/tooling/check/check-report.types.d.mts +62 -0
  535. package/esm/tooling/check/check-report.types.d.mts.map +1 -0
  536. package/esm/tooling/check/check-report.types.mjs +37 -0
  537. package/esm/tooling/check/check-report.types.mjs.map +1 -0
  538. package/esm/tooling/check/code-style-conventions.mjs +106 -0
  539. package/esm/tooling/check/code-style-conventions.mjs.map +1 -0
  540. package/esm/tooling/check/conventions-step.d.mts +34 -0
  541. package/esm/tooling/check/conventions-step.d.mts.map +1 -0
  542. package/esm/tooling/check/conventions-step.mjs +184 -0
  543. package/esm/tooling/check/conventions-step.mjs.map +1 -0
  544. package/esm/tooling/check/environment-step.d.mts +18 -0
  545. package/esm/tooling/check/environment-step.d.mts.map +1 -0
  546. package/esm/tooling/check/environment-step.mjs +78 -0
  547. package/esm/tooling/check/environment-step.mjs.map +1 -0
  548. package/esm/tooling/check/index.d.mts +8 -0
  549. package/esm/tooling/check/index.mjs +10 -0
  550. package/esm/tooling/check/lint-step.d.mts +19 -0
  551. package/esm/tooling/check/lint-step.d.mts.map +1 -0
  552. package/esm/tooling/check/lint-step.mjs +111 -0
  553. package/esm/tooling/check/lint-step.mjs.map +1 -0
  554. package/esm/tooling/check/request-input.mjs +132 -0
  555. package/esm/tooling/check/request-input.mjs.map +1 -0
  556. package/esm/tooling/check/run-check.d.mts +35 -0
  557. package/esm/tooling/check/run-check.d.mts.map +1 -0
  558. package/esm/tooling/check/run-check.mjs +192 -0
  559. package/esm/tooling/check/run-check.mjs.map +1 -0
  560. package/esm/tooling/check/run-tests.d.mts +48 -0
  561. package/esm/tooling/check/run-tests.d.mts.map +1 -0
  562. package/esm/tooling/check/run-tests.mjs +93 -0
  563. package/esm/tooling/check/run-tests.mjs.map +1 -0
  564. package/esm/tooling/check/security-conventions.mjs +648 -0
  565. package/esm/tooling/check/security-conventions.mjs.map +1 -0
  566. package/esm/tooling/check/source-ast.mjs +195 -0
  567. package/esm/tooling/check/source-ast.mjs.map +1 -0
  568. package/esm/tooling/check/source-files.mjs +27 -0
  569. package/esm/tooling/check/source-files.mjs.map +1 -0
  570. package/esm/tooling/check/tests-step.d.mts +34 -0
  571. package/esm/tooling/check/tests-step.d.mts.map +1 -0
  572. package/esm/tooling/check/tests-step.mjs +177 -0
  573. package/esm/tooling/check/tests-step.mjs.map +1 -0
  574. package/esm/tooling/dev-state/dev-server-state.d.mts +39 -0
  575. package/esm/tooling/dev-state/dev-server-state.d.mts.map +1 -0
  576. package/esm/tooling/dev-state/dev-server-state.mjs +67 -0
  577. package/esm/tooling/dev-state/dev-server-state.mjs.map +1 -0
  578. package/esm/tooling/devtools/devtools-client.d.mts +91 -0
  579. package/esm/tooling/devtools/devtools-client.d.mts.map +1 -0
  580. package/esm/tooling/devtools/devtools-client.mjs +155 -0
  581. package/esm/tooling/devtools/devtools-client.mjs.map +1 -0
  582. package/esm/tooling/generate/generation.d.mts +78 -0
  583. package/esm/tooling/generate/generation.d.mts.map +1 -0
  584. package/esm/tooling/generate/generation.mjs +123 -0
  585. package/esm/tooling/generate/generation.mjs.map +1 -0
  586. package/esm/tooling/generate/generator-spec.d.mts +57 -0
  587. package/esm/tooling/generate/generator-spec.d.mts.map +1 -0
  588. package/esm/tooling/generate/generator-spec.mjs +102 -0
  589. package/esm/tooling/generate/generator-spec.mjs.map +1 -0
  590. package/esm/tooling/generate/index.mjs +6 -0
  591. package/esm/tooling/generate/project-files.mjs +121 -0
  592. package/esm/tooling/generate/project-files.mjs.map +1 -0
  593. package/esm/tooling/generate/run-cli.d.mts +40 -0
  594. package/esm/tooling/generate/run-cli.d.mts.map +1 -0
  595. package/esm/tooling/generate/run-cli.mjs +93 -0
  596. package/esm/tooling/generate/run-cli.mjs.map +1 -0
  597. package/esm/tooling/generate/unified-diff.d.mts +37 -0
  598. package/esm/tooling/generate/unified-diff.d.mts.map +1 -0
  599. package/esm/tooling/generate/unified-diff.mjs +161 -0
  600. package/esm/tooling/generate/unified-diff.mjs.map +1 -0
  601. package/esm/tooling/index.d.mts +41 -0
  602. package/esm/tooling/index.mjs +45 -0
  603. package/esm/tooling/inspect/boot-for-inspect.d.mts +23 -0
  604. package/esm/tooling/inspect/boot-for-inspect.d.mts.map +1 -0
  605. package/esm/tooling/inspect/boot-for-inspect.mjs +63 -0
  606. package/esm/tooling/inspect/boot-for-inspect.mjs.map +1 -0
  607. package/esm/tooling/inspect/collect-config.mjs +77 -0
  608. package/esm/tooling/inspect/collect-config.mjs.map +1 -0
  609. package/esm/tooling/inspect/collect-models.d.mts +9 -0
  610. package/esm/tooling/inspect/collect-models.d.mts.map +1 -0
  611. package/esm/tooling/inspect/collect-models.mjs +154 -0
  612. package/esm/tooling/inspect/collect-models.mjs.map +1 -0
  613. package/esm/tooling/inspect/collect-packages.d.mts +10 -0
  614. package/esm/tooling/inspect/collect-packages.d.mts.map +1 -0
  615. package/esm/tooling/inspect/collect-packages.mjs +157 -0
  616. package/esm/tooling/inspect/collect-packages.mjs.map +1 -0
  617. package/esm/tooling/inspect/collect-pages.mjs +108 -0
  618. package/esm/tooling/inspect/collect-pages.mjs.map +1 -0
  619. package/esm/tooling/inspect/collect-routes.mjs +168 -0
  620. package/esm/tooling/inspect/collect-routes.mjs.map +1 -0
  621. package/esm/tooling/inspect/event-recorder.d.mts +37 -0
  622. package/esm/tooling/inspect/event-recorder.d.mts.map +1 -0
  623. package/esm/tooling/inspect/event-recorder.mjs +91 -0
  624. package/esm/tooling/inspect/event-recorder.mjs.map +1 -0
  625. package/esm/tooling/inspect/index.d.mts +7 -0
  626. package/esm/tooling/inspect/index.mjs +7 -0
  627. package/esm/tooling/inspect/inspect-app.d.mts +53 -0
  628. package/esm/tooling/inspect/inspect-app.d.mts.map +1 -0
  629. package/esm/tooling/inspect/inspect-app.mjs +92 -0
  630. package/esm/tooling/inspect/inspect-app.mjs.map +1 -0
  631. package/esm/tooling/inspect/inspect-report.schema.d.mts +15 -0
  632. package/esm/tooling/inspect/inspect-report.schema.d.mts.map +1 -0
  633. package/esm/tooling/inspect/inspect-report.schema.mjs +463 -0
  634. package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -0
  635. package/esm/tooling/inspect/inspect-report.types.d.mts +196 -0
  636. package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -0
  637. package/esm/tooling/inspect/inspect-report.types.mjs +33 -0
  638. package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -0
  639. package/esm/tooling/mcp/create-mcp-context.d.mts +26 -0
  640. package/esm/tooling/mcp/create-mcp-context.d.mts.map +1 -0
  641. package/esm/tooling/mcp/create-mcp-context.mjs +75 -0
  642. package/esm/tooling/mcp/create-mcp-context.mjs.map +1 -0
  643. package/esm/tooling/mcp/db-schema.d.mts +49 -0
  644. package/esm/tooling/mcp/db-schema.d.mts.map +1 -0
  645. package/esm/tooling/mcp/db-schema.mjs +56 -0
  646. package/esm/tooling/mcp/db-schema.mjs.map +1 -0
  647. package/esm/tooling/mcp/docs-search.d.mts +72 -0
  648. package/esm/tooling/mcp/docs-search.d.mts.map +1 -0
  649. package/esm/tooling/mcp/docs-search.mjs +178 -0
  650. package/esm/tooling/mcp/docs-search.mjs.map +1 -0
  651. package/esm/tooling/mcp/index.mjs +9 -0
  652. package/esm/tooling/mcp/load-ai-tools.d.mts +62 -0
  653. package/esm/tooling/mcp/load-ai-tools.d.mts.map +1 -0
  654. package/esm/tooling/mcp/load-ai-tools.mjs +44 -0
  655. package/esm/tooling/mcp/load-ai-tools.mjs.map +1 -0
  656. package/esm/tooling/mcp/mcp-tools.d.mts +60 -0
  657. package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -0
  658. package/esm/tooling/mcp/mcp-tools.mjs +513 -0
  659. package/esm/tooling/mcp/mcp-tools.mjs.map +1 -0
  660. package/esm/tooling/mcp/start-mcp-server.d.mts +56 -0
  661. package/esm/tooling/mcp/start-mcp-server.d.mts.map +1 -0
  662. package/esm/tooling/mcp/start-mcp-server.mjs +126 -0
  663. package/esm/tooling/mcp/start-mcp-server.mjs.map +1 -0
  664. package/esm/tooling/mcp/tool-input.d.mts +75 -0
  665. package/esm/tooling/mcp/tool-input.d.mts.map +1 -0
  666. package/esm/tooling/mcp/tool-input.mjs +72 -0
  667. package/esm/tooling/mcp/tool-input.mjs.map +1 -0
  668. package/esm/tooling/problem.d.mts +45 -0
  669. package/esm/tooling/problem.d.mts.map +1 -0
  670. package/esm/tooling/problem.mjs +28 -0
  671. package/esm/tooling/problem.mjs.map +1 -0
  672. package/esm/tooling/trace/trace-report.types.d.mts +71 -0
  673. package/esm/tooling/trace/trace-report.types.d.mts.map +1 -0
  674. package/esm/tooling/trace/trace-report.types.mjs +7 -0
  675. package/esm/tooling/trace/trace-report.types.mjs.map +1 -0
  676. package/esm/tooling/trace/trace-request.d.mts +83 -0
  677. package/esm/tooling/trace/trace-request.d.mts.map +1 -0
  678. package/esm/tooling/trace/trace-request.mjs +241 -0
  679. package/esm/tooling/trace/trace-request.mjs.map +1 -0
  680. package/esm/tooling/typecheck/index.d.mts +1 -0
  681. package/esm/tooling/typecheck/index.mjs +3 -0
  682. package/esm/tooling/typecheck/run-typecheck.d.mts +59 -0
  683. package/esm/tooling/typecheck/run-typecheck.d.mts.map +1 -0
  684. package/esm/tooling/typecheck/run-typecheck.mjs +153 -0
  685. package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -0
  686. package/esm/upgrade/codemods/head-viewport.mjs +46 -0
  687. package/esm/upgrade/codemods/head-viewport.mjs.map +1 -0
  688. package/esm/upgrade/codemods/helpers.mjs +81 -0
  689. package/esm/upgrade/codemods/helpers.mjs.map +1 -0
  690. package/esm/upgrade/codemods/index.mjs +34 -0
  691. package/esm/upgrade/codemods/index.mjs.map +1 -0
  692. package/esm/upgrade/codemods/logger-sync-calls.mjs +57 -0
  693. package/esm/upgrade/codemods/logger-sync-calls.mjs.map +1 -0
  694. package/esm/upgrade/codemods/package-json.mjs +122 -0
  695. package/esm/upgrade/codemods/package-json.mjs.map +1 -0
  696. package/esm/upgrade/codemods/request-log-snapshot.mjs +82 -0
  697. package/esm/upgrade/codemods/request-log-snapshot.mjs.map +1 -0
  698. package/esm/upgrade/codemods/review-sites.mjs +262 -0
  699. package/esm/upgrade/codemods/review-sites.mjs.map +1 -0
  700. package/esm/upgrade/diff.mjs +119 -0
  701. package/esm/upgrade/diff.mjs.map +1 -0
  702. package/esm/upgrade/format-upgrade-report.mjs +32 -0
  703. package/esm/upgrade/format-upgrade-report.mjs.map +1 -0
  704. package/esm/upgrade/load-ts-morph.mjs +48 -0
  705. package/esm/upgrade/load-ts-morph.mjs.map +1 -0
  706. package/esm/upgrade/notes.mjs +16 -0
  707. package/esm/upgrade/notes.mjs.map +1 -0
  708. package/esm/upgrade/run-upgrade.mjs +246 -0
  709. package/esm/upgrade/run-upgrade.mjs.map +1 -0
  710. package/esm/upgrade/upgrade-command.action.mjs +37 -0
  711. package/esm/upgrade/upgrade-command.action.mjs.map +1 -0
  712. package/esm/upgrade/version.mjs +39 -0
  713. package/esm/upgrade/version.mjs.map +1 -0
  714. package/esm/use-cases/use-cases-registry.mjs.map +1 -1
  715. package/esm/utils/clock.d.mts +33 -0
  716. package/esm/utils/clock.d.mts.map +1 -0
  717. package/esm/utils/clock.mjs +27 -0
  718. package/esm/utils/clock.mjs.map +1 -0
  719. package/esm/utils/framework-vesion.mjs +1 -1
  720. package/esm/utils/framework-vesion.mjs.map +1 -1
  721. package/esm/utils/ids.d.mts +32 -0
  722. package/esm/utils/ids.d.mts.map +1 -0
  723. package/esm/utils/ids.mjs +33 -0
  724. package/esm/utils/ids.mjs.map +1 -0
  725. package/esm/utils/index.d.mts +3 -0
  726. package/esm/utils/index.mjs +3 -0
  727. package/esm/utils/runtime.d.mts +59 -0
  728. package/esm/utils/runtime.d.mts.map +1 -0
  729. package/esm/utils/runtime.mjs +85 -0
  730. package/esm/utils/runtime.mjs.map +1 -0
  731. package/esm/validation/index.d.mts +1 -1
  732. package/esm/validation/types.d.mts +57 -2
  733. package/esm/validation/types.d.mts.map +1 -1
  734. package/esm/validation/validateAll.mjs +19 -2
  735. package/esm/validation/validateAll.mjs.map +1 -1
  736. package/esm/validation/validation-failure-status.mjs +15 -0
  737. package/esm/validation/validation-failure-status.mjs.map +1 -0
  738. package/esm/validation/validators/file-validator.d.mts +0 -16
  739. package/esm/validation/validators/file-validator.d.mts.map +1 -1
  740. package/esm/warlock-config/bundle-config.mjs +1 -1
  741. package/esm/warlock-config/types.d.mts +27 -3
  742. package/esm/warlock-config/types.d.mts.map +1 -1
  743. package/llms-full.txt +3166 -736
  744. package/llms.txt +16 -6
  745. package/package.json +43 -13
  746. package/schemas/inspect-report.schema.json +554 -0
  747. package/skills/README.md +9 -0
  748. package/skills/add-connector/SKILL.md +26 -38
  749. package/skills/benchmark-code/SKILL.md +30 -24
  750. package/skills/build-restful/SKILL.md +14 -14
  751. package/skills/build-url/SKILL.md +18 -18
  752. package/skills/configure-app/SKILL.md +107 -14
  753. package/skills/create-controller/SKILL.md +25 -12
  754. package/skills/create-module/SKILL.md +3 -3
  755. package/skills/define-resource/SKILL.md +32 -29
  756. package/skills/encrypt-data/SKILL.md +21 -19
  757. package/skills/generate-openapi/SKILL.md +50 -24
  758. package/skills/hash-password/SKILL.md +2 -2
  759. package/skills/health-checks/SKILL.md +24 -7
  760. package/skills/index.md +9 -4
  761. package/skills/lower-stage3-decorators/SKILL.md +4 -4
  762. package/skills/make-endpoint/SKILL.md +110 -0
  763. package/skills/model-factories/SKILL.md +92 -0
  764. package/skills/process-image/SKILL.md +32 -30
  765. package/skills/register-route/SKILL.md +109 -10
  766. package/skills/request-memo/SKILL.md +1 -1
  767. package/skills/request-tracing/SKILL.md +4 -1
  768. package/skills/resolve-path/SKILL.md +45 -44
  769. package/skills/retry-operation/SKILL.md +20 -11
  770. package/skills/run-app/SKILL.md +106 -22
  771. package/skills/send-response/SKILL.md +179 -14
  772. package/skills/store-file/SKILL.md +93 -53
  773. package/skills/test-fakes/SKILL.md +123 -0
  774. package/skills/test-http/SKILL.md +125 -21
  775. package/skills/test-service/SKILL.md +41 -1
  776. package/skills/update-packages/SKILL.md +29 -18
  777. package/skills/upgrade-from-5-29/SKILL.md +413 -0
  778. package/skills/upload-file/SKILL.md +31 -33
  779. package/skills/use-app-context/SKILL.md +38 -31
  780. package/skills/use-middleware/SKILL.md +45 -25
  781. package/skills/use-model-transformers/SKILL.md +37 -28
  782. package/skills/use-repository/SKILL.md +2 -2
  783. package/skills/validate-input/SKILL.md +72 -28
  784. package/skills/warlock-agents-md/SKILL.md +96 -0
  785. package/skills/warlock-board/SKILL.md +123 -0
  786. package/skills/warlock-check/SKILL.md +132 -0
  787. package/skills/warlock-conventions/SKILL.md +115 -0
  788. package/skills/warlock-doctor/SKILL.md +55 -22
  789. package/skills/warlock-inspect/SKILL.md +134 -0
  790. package/skills/warlock-mcp/SKILL.md +120 -0
  791. package/skills/warlock-routes/SKILL.md +18 -11
  792. package/skills/warlock-trace/SKILL.md +81 -0
  793. package/skills/wire-socket/SKILL.md +8 -8
  794. package/skills/write-seeder/SKILL.md +46 -32
  795. package/skills/write-use-case/SKILL.md +40 -27
  796. package/esm/http/uploads/matches-if-none-match.mjs +0 -13
  797. package/esm/http/uploads/matches-if-none-match.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { rootPath, srcPath } from "../../utils/paths.mjs";
2
2
  import "../../utils/index.mjs";
3
+ import { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from "../generation-fs.mjs";
3
4
  import { webContactControllerStub, webContactRoutesStub, webHomePageStub, webHomeRegisterStub, webRootStub } from "../stubs.mjs";
4
5
  import { INSTALLED_WARLOCK_VERSION } from "./types.mjs";
5
6
  import { insertConnectorEntry } from "./shared/insert-connector-entry.mjs";
6
7
  import { relocateConflictingHomeRoute } from "./shared/relocate-conflicting-home-route.mjs";
7
8
  import { resolveContactScaffold } from "./shared/resolve-contact-scaffold.mjs";
8
9
  import { colors } from "@mongez/copper";
9
- import { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from "@warlock.js/fs";
10
10
 
11
11
  //#region ../../packages/core/src/generations/features/web.feature.ts
12
12
  /** The one vite range shared by every feature (web's peer range is `>=7.3.5 <8`). */
@@ -1 +1 @@
1
- {"version":3,"file":"web.feature.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/generations/features/web.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport type { CommandActionData } from \"../../commands/types\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport { insertConnectorEntry } from \"./shared/insert-connector-entry\";\nimport { relocateConflictingHomeRoute } from \"./shared/relocate-conflicting-home-route\";\nimport { resolveContactScaffold } from \"./shared/resolve-contact-scaffold\";\nimport {\n webContactControllerStub,\n webContactRoutesStub,\n webHomePageStub,\n webHomeRegisterStub,\n webRootStub,\n} from \"../stubs\";\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\n/** The one vite range shared by every feature (web's peer range is `>=7.3.5 <8`). */\nexport const VITE_RANGE = \"^7.3.5\";\n\n/**\n * Register the WebConnector in `warlock.config.ts`, and ONLY there.\n *\n * It belongs to the config array or to app code, never both. Both halves are\n * registered before app code loads — the CLI preloader in dev, the generated\n * entry in production — so also calling `connectorsManager.register(...)` in\n * `src/app/main.ts` boots the connector twice and installs every page route\n * twice. That surfaces at PRODUCTION boot as `Route name \"...\" is already\n * taken`, because pages and API routes share one route-name namespace.\n *\n * The config array is the half to prefer: `warlock build` reads the same array\n * to drain each connector's build contribution, so \"built for\" and \"boots with\"\n * cannot drift.\n *\n * String surgery rather than a TypeScript parse: `warlock.config.ts` is an\n * app-owned file that may carry any formatting, and a parse-and-print would\n * reformat the parts we did not come to change.\n */\nasync function registerWebConnector(): Promise<void> {\n const configPath = rootPath(\"warlock.config.ts\");\n\n if (!(await fileExistsAsync(configPath))) {\n console.log(\n `${colors.yellowBright(\"warlock.config.ts\")} not found — add this yourself:\\n` +\n ` import { webConnector } from \"@warlock.js/web/connector\";\\n` +\n ` export default defineConfig({ connectors: [webConnector()] });`,\n );\n\n return;\n }\n\n const current = await getFileAsync(configPath);\n\n if (current.includes(\"webConnector\")) {\n console.log(`${colors.yellowBright(\"webConnector\")} already registered, skipping...`);\n\n return;\n }\n\n const importLine = 'import { webConnector } from \"@warlock.js/web/connector\";';\n let next = current.includes(importLine) ? current : `${importLine}\\n${current}`;\n\n // An existing `connectors: [` gains one entry; otherwise the key is added to\n // the object `defineConfig` receives.\n const insertion = insertConnectorEntry(next, \"webConnector()\");\n\n if (insertion.status === \"already-present\") {\n return;\n }\n\n if (insertion.status === \"added\") {\n next = insertion.next;\n } else if (next.includes(\"defineConfig({\")) {\n next = next.replace(\"defineConfig({\", \"defineConfig({\\n connectors: [webConnector()],\");\n } else {\n console.log(\n `${colors.yellowBright(\"warlock.config.ts\")} has no recognisable defineConfig({...}) — ` +\n \"add `connectors: [webConnector()]` yourself.\",\n );\n\n return;\n }\n\n await putFileAsync(configPath, next);\n console.log(`${colors.green(\"✓\")} Registered webConnector in warlock.config.ts`);\n}\n\n/**\n * Scaffold the smallest page layer that renders, and register the connector.\n *\n * `src/web/root.tsx` is the sentinel for \"already scaffolded\" — the framework\n * ships a default root, so its presence means a human has been here.\n */\nasync function completeWebInstallation(_options: CommandActionData) {\n const rootFile = srcPath(\"web/root.tsx\");\n\n if (await fileExistsAsync(rootFile)) {\n console.log(`${colors.yellowBright(\"src/web\")} already scaffolded, skipping...`);\n } else {\n await ensureDirectoryAsync(srcPath(\"web\"));\n await putFileAsync(rootFile, webRootStub);\n console.log(`${colors.green(\"✓\")} Created src/web/root.tsx`);\n\n const collision = await relocateConflictingHomeRoute();\n\n if (collision.outcome === \"relocated\") {\n console.log(\n `${colors.green(\"✓\")} Moved the existing ${colors.yellowBright('GET \"/\"')} route to ` +\n `${colors.yellowBright('\"/welcome\"')} in ${colors.yellowBright(`src/${collision.relativePath}`)} — ` +\n \"the new page owns `/` now, and the JSON welcome route still answers at /welcome.\",\n );\n }\n\n // The page is written ONLY when `/` is provably free. Writing it while\n // another handler holds `/` produces a homepage that 500s on first request,\n // which is precisely the outcome a scaffolder must never hand back.\n if (collision.outcome === \"conflict\" || collision.outcome === \"failed\") {\n const verb =\n collision.outcome === \"failed\" ? colors.redBright(\"✗\") : colors.yellowBright(\"!\");\n\n console.log(\n `${verb} Did not create src/web/index.page.tsx: ${collision.reason}.\\n` +\n ` The page stub declares ${colors.yellowBright('route.path = \"/\"')}, and two handlers on one ` +\n \"path is a 500 at request time, not a startup error.\\n\" +\n ` Free up ${colors.yellowBright('GET \"/\"')} under src/app — move it to a path of its own, ` +\n \"or remove it — then create src/web/index.page.tsx yourself. Giving the page a `route` other \" +\n \"than `/` works too.\",\n );\n\n // Non-zero on BOTH branches. The page layer this command exists to\n // scaffold was not scaffolded, and a 0 here is the exact \"looked like it\n // worked\" signal that put `/` in this state to begin with — a conflict we\n // declined to guess at is still an incomplete install, not a success.\n //\n // `exitCode` rather than `exit(1)`: the connector below still has to be\n // registered, and any other feature in the same `warlock add` invocation\n // still has to install, or the project is left half-wired on top of this.\n process.exitCode = 1;\n } else {\n await putFileAsync(srcPath(\"web/index.page.tsx\"), webHomePageStub);\n await putFileAsync(srcPath(\"web/index.register.ts\"), webHomeRegisterStub);\n\n // Unlike root.tsx above, the contact controller and its routes file are\n // ordinary application files a project can already have — independently\n // of ever having run `warlock add web`. Each is guarded on its OWN\n // existence, not on the (already-consumed) root.tsx sentinel, so an\n // existing `contact` module is skipped rather than clobbered. See\n // resolveContactScaffold's doc comment for why.\n const contactPlan = resolveContactScaffold({\n controllerExists: await fileExistsAsync(\n srcPath(\"app/contact/controllers/contact.controller.ts\"),\n ),\n routesExists: await fileExistsAsync(srcPath(\"app/contact/routes.ts\")),\n });\n\n if (contactPlan.writeController) {\n await ensureDirectoryAsync(srcPath(\"app/contact/controllers\"));\n await putFileAsync(\n srcPath(\"app/contact/controllers/contact.controller.ts\"),\n webContactControllerStub,\n );\n }\n\n if (contactPlan.writeRoutes) {\n await ensureDirectoryAsync(srcPath(\"app/contact\"));\n await putFileAsync(srcPath(\"app/contact/routes.ts\"), webContactRoutesStub);\n }\n\n console.log(`${colors.green(\"✓\")} Created src/web/index.page.tsx`);\n\n for (const message of contactPlan.messages) {\n console.log(message);\n }\n }\n }\n\n await registerWebConnector();\n}\n\nexport const webFeature: FeatureDefinition = {\n description:\n \"Installs @warlock.js/web — SSR React pages served by the Warlock HTTP server. Scaffolds src/web (root.tsx + a home page) and registers the WebConnector in warlock.config.ts. Pages are opt-in: a Warlock app is an API until you add this.\",\n dependencies: {\n \"@warlock.js/web\": INSTALLED_WARLOCK_VERSION,\n \"@mongez/http\": \"^3.5.0\",\n \"@mongez/react-form\": \"^4.0.0\",\n \"@mongez/react-localization\": \"^3.4.7\",\n react: \"^19.2.3\",\n \"react-dom\": \"^19.2.3\",\n },\n devDependencies: {\n \"@types/react\": \"^19.2.7\",\n \"@types/react-dom\": \"^19.2.3\",\n // Loaded through `await import()` by the dev server only, so both are\n // optional peers of `web` rather than hard dependencies.\n vite: VITE_RANGE,\n \"@vitejs/plugin-react\": \"^5.2.0\",\n },\n onExecuting: completeWebInstallation,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"web.feature.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/generations/features/web.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n ensureDirectoryAsync,\n fileExistsAsync,\n getFileAsync,\n putFileAsync,\n} from \"../generation-fs\";\nimport type { CommandActionData } from \"../../commands/types\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport { insertConnectorEntry } from \"./shared/insert-connector-entry\";\nimport { relocateConflictingHomeRoute } from \"./shared/relocate-conflicting-home-route\";\nimport { resolveContactScaffold } from \"./shared/resolve-contact-scaffold\";\nimport {\n webContactControllerStub,\n webContactRoutesStub,\n webHomePageStub,\n webHomeRegisterStub,\n webRootStub,\n} from \"../stubs\";\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\n/** The one vite range shared by every feature (web's peer range is `>=7.3.5 <8`). */\nexport const VITE_RANGE = \"^7.3.5\";\n\n/**\n * Register the WebConnector in `warlock.config.ts`, and ONLY there.\n *\n * It belongs to the config array or to app code, never both. Both halves are\n * registered before app code loads — the CLI preloader in dev, the generated\n * entry in production — so also calling `connectorsManager.register(...)` in\n * `src/app/main.ts` boots the connector twice and installs every page route\n * twice. That surfaces at PRODUCTION boot as `Route name \"...\" is already\n * taken`, because pages and API routes share one route-name namespace.\n *\n * The config array is the half to prefer: `warlock build` reads the same array\n * to drain each connector's build contribution, so \"built for\" and \"boots with\"\n * cannot drift.\n *\n * String surgery rather than a TypeScript parse: `warlock.config.ts` is an\n * app-owned file that may carry any formatting, and a parse-and-print would\n * reformat the parts we did not come to change.\n */\nasync function registerWebConnector(): Promise<void> {\n const configPath = rootPath(\"warlock.config.ts\");\n\n if (!(await fileExistsAsync(configPath))) {\n console.log(\n `${colors.yellowBright(\"warlock.config.ts\")} not found — add this yourself:\\n` +\n ` import { webConnector } from \"@warlock.js/web/connector\";\\n` +\n ` export default defineConfig({ connectors: [webConnector()] });`,\n );\n\n return;\n }\n\n const current = await getFileAsync(configPath);\n\n if (current.includes(\"webConnector\")) {\n console.log(`${colors.yellowBright(\"webConnector\")} already registered, skipping...`);\n\n return;\n }\n\n const importLine = 'import { webConnector } from \"@warlock.js/web/connector\";';\n let next = current.includes(importLine) ? current : `${importLine}\\n${current}`;\n\n // An existing `connectors: [` gains one entry; otherwise the key is added to\n // the object `defineConfig` receives.\n const insertion = insertConnectorEntry(next, \"webConnector()\");\n\n if (insertion.status === \"already-present\") {\n return;\n }\n\n if (insertion.status === \"added\") {\n next = insertion.next;\n } else if (next.includes(\"defineConfig({\")) {\n next = next.replace(\"defineConfig({\", \"defineConfig({\\n connectors: [webConnector()],\");\n } else {\n console.log(\n `${colors.yellowBright(\"warlock.config.ts\")} has no recognisable defineConfig({...}) — ` +\n \"add `connectors: [webConnector()]` yourself.\",\n );\n\n return;\n }\n\n await putFileAsync(configPath, next);\n console.log(`${colors.green(\"✓\")} Registered webConnector in warlock.config.ts`);\n}\n\n/**\n * Scaffold the smallest page layer that renders, and register the connector.\n *\n * `src/web/root.tsx` is the sentinel for \"already scaffolded\" — the framework\n * ships a default root, so its presence means a human has been here.\n */\nasync function completeWebInstallation(_options: CommandActionData) {\n const rootFile = srcPath(\"web/root.tsx\");\n\n if (await fileExistsAsync(rootFile)) {\n console.log(`${colors.yellowBright(\"src/web\")} already scaffolded, skipping...`);\n } else {\n await ensureDirectoryAsync(srcPath(\"web\"));\n await putFileAsync(rootFile, webRootStub);\n console.log(`${colors.green(\"✓\")} Created src/web/root.tsx`);\n\n const collision = await relocateConflictingHomeRoute();\n\n if (collision.outcome === \"relocated\") {\n console.log(\n `${colors.green(\"✓\")} Moved the existing ${colors.yellowBright('GET \"/\"')} route to ` +\n `${colors.yellowBright('\"/welcome\"')} in ${colors.yellowBright(`src/${collision.relativePath}`)} — ` +\n \"the new page owns `/` now, and the JSON welcome route still answers at /welcome.\",\n );\n }\n\n // The page is written ONLY when `/` is provably free. Writing it while\n // another handler holds `/` produces a homepage that 500s on first request,\n // which is precisely the outcome a scaffolder must never hand back.\n if (collision.outcome === \"conflict\" || collision.outcome === \"failed\") {\n const verb =\n collision.outcome === \"failed\" ? colors.redBright(\"✗\") : colors.yellowBright(\"!\");\n\n console.log(\n `${verb} Did not create src/web/index.page.tsx: ${collision.reason}.\\n` +\n ` The page stub declares ${colors.yellowBright('route.path = \"/\"')}, and two handlers on one ` +\n \"path is a 500 at request time, not a startup error.\\n\" +\n ` Free up ${colors.yellowBright('GET \"/\"')} under src/app — move it to a path of its own, ` +\n \"or remove it — then create src/web/index.page.tsx yourself. Giving the page a `route` other \" +\n \"than `/` works too.\",\n );\n\n // Non-zero on BOTH branches. The page layer this command exists to\n // scaffold was not scaffolded, and a 0 here is the exact \"looked like it\n // worked\" signal that put `/` in this state to begin with — a conflict we\n // declined to guess at is still an incomplete install, not a success.\n //\n // `exitCode` rather than `exit(1)`: the connector below still has to be\n // registered, and any other feature in the same `warlock add` invocation\n // still has to install, or the project is left half-wired on top of this.\n process.exitCode = 1;\n } else {\n await putFileAsync(srcPath(\"web/index.page.tsx\"), webHomePageStub);\n await putFileAsync(srcPath(\"web/index.register.ts\"), webHomeRegisterStub);\n\n // Unlike root.tsx above, the contact controller and its routes file are\n // ordinary application files a project can already have — independently\n // of ever having run `warlock add web`. Each is guarded on its OWN\n // existence, not on the (already-consumed) root.tsx sentinel, so an\n // existing `contact` module is skipped rather than clobbered. See\n // resolveContactScaffold's doc comment for why.\n const contactPlan = resolveContactScaffold({\n controllerExists: await fileExistsAsync(\n srcPath(\"app/contact/controllers/contact.controller.ts\"),\n ),\n routesExists: await fileExistsAsync(srcPath(\"app/contact/routes.ts\")),\n });\n\n if (contactPlan.writeController) {\n await ensureDirectoryAsync(srcPath(\"app/contact/controllers\"));\n await putFileAsync(\n srcPath(\"app/contact/controllers/contact.controller.ts\"),\n webContactControllerStub,\n );\n }\n\n if (contactPlan.writeRoutes) {\n await ensureDirectoryAsync(srcPath(\"app/contact\"));\n await putFileAsync(srcPath(\"app/contact/routes.ts\"), webContactRoutesStub);\n }\n\n console.log(`${colors.green(\"✓\")} Created src/web/index.page.tsx`);\n\n for (const message of contactPlan.messages) {\n console.log(message);\n }\n }\n }\n\n await registerWebConnector();\n}\n\nexport const webFeature: FeatureDefinition = {\n description:\n \"Installs @warlock.js/web — SSR React pages served by the Warlock HTTP server. Scaffolds src/web (root.tsx + a home page) and registers the WebConnector in warlock.config.ts. Pages are opt-in: a Warlock app is an API until you add this.\",\n dependencies: {\n \"@warlock.js/web\": INSTALLED_WARLOCK_VERSION,\n \"@mongez/http\": \"^3.5.0\",\n \"@mongez/react-form\": \"^4.0.0\",\n \"@mongez/react-localization\": \"^3.4.7\",\n react: \"^19.2.3\",\n \"react-dom\": \"^19.2.3\",\n },\n devDependencies: {\n \"@types/react\": \"^19.2.7\",\n \"@types/react-dom\": \"^19.2.3\",\n // Loaded through `await import()` by the dev server only, so both are\n // optional peers of `web` rather than hard dependencies.\n vite: VITE_RANGE,\n \"@vitejs/plugin-react\": \"^5.2.0\",\n },\n onExecuting: completeWebInstallation,\n};\n"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { currentGenerationRun, hasPendingContentUnder, pendingGeneratedContent, recordGeneratedFile, recordSkippedFile, setPendingGeneratedContent } from "./generation-run.mjs";
2
+ import * as fs from "@warlock.js/fs";
3
+
4
+ //#region ../../packages/core/src/generations/generation-fs.ts
5
+ /**
6
+ * The filesystem generation commands write through.
7
+ *
8
+ * Same signatures as the `@warlock.js/fs` functions they wrap, so a feature
9
+ * hook or generator only changes its import. Outside a generation run they
10
+ * pass straight through. Inside one (see `generation-run.ts`) every write is
11
+ * recorded for the `--json` report, and in a `--dry-run` nothing reaches the
12
+ * disk: writes land in an in-memory overlay that later reads in the same run
13
+ * see, so a hook that writes a file and then edits it previews correctly.
14
+ */
15
+ /** Whether `target` exists on disk or in the dry-run overlay. */
16
+ async function fileExistsAsync$1(target) {
17
+ if (pendingGeneratedContent(target) !== void 0) return true;
18
+ return fs.fileExistsAsync(target);
19
+ }
20
+ /** Whether `directory` exists on disk or holds a file in the dry-run overlay. */
21
+ async function directoryExistsAsync$1(directory) {
22
+ if (hasPendingContentUnder(directory)) return true;
23
+ return fs.directoryExistsAsync(directory);
24
+ }
25
+ /** Read `target`, preferring what this dry run already "wrote". */
26
+ async function getFileAsync$1(target) {
27
+ const pending = pendingGeneratedContent(target);
28
+ if (pending !== void 0) return pending;
29
+ return fs.getFileAsync(target);
30
+ }
31
+ async function getJsonFileAsync$1(target) {
32
+ const pending = pendingGeneratedContent(target);
33
+ if (pending !== void 0) return JSON.parse(pending);
34
+ return fs.getJsonFileAsync(target);
35
+ }
36
+ /**
37
+ * Write `content` to `target`, recording `create`, `update` or `unchanged`.
38
+ * Identical content is not rewritten.
39
+ */
40
+ async function putFileAsync$1(target, content) {
41
+ const run = currentGenerationRun();
42
+ if (!run) {
43
+ await fs.putFileAsync(target, content);
44
+ return;
45
+ }
46
+ const exists = await fileExistsAsync$1(target);
47
+ if (exists && await getFileAsync$1(target) === content) {
48
+ recordGeneratedFile(target, "unchanged");
49
+ return;
50
+ }
51
+ recordGeneratedFile(target, exists ? "update" : "create");
52
+ if (run.dryRun) {
53
+ setPendingGeneratedContent(target, content);
54
+ return;
55
+ }
56
+ await fs.putFileAsync(target, content);
57
+ }
58
+ async function putJsonFileAsync$1(target, value) {
59
+ if (!currentGenerationRun()) {
60
+ await fs.putJsonFileAsync(target, value);
61
+ return;
62
+ }
63
+ await putFileAsync$1(target, JSON.stringify(value, null, 2));
64
+ }
65
+ /** Create `directory`; a dry run leaves no empty folders behind. */
66
+ async function ensureDirectoryAsync$1(directory) {
67
+ if (currentGenerationRun()?.dryRun) return;
68
+ await fs.ensureDirectoryAsync(directory);
69
+ }
70
+
71
+ //#endregion
72
+ export { directoryExistsAsync$1 as directoryExistsAsync, ensureDirectoryAsync$1 as ensureDirectoryAsync, fileExistsAsync$1 as fileExistsAsync, getFileAsync$1 as getFileAsync, getJsonFileAsync$1 as getJsonFileAsync, putFileAsync$1 as putFileAsync, putJsonFileAsync$1 as putJsonFileAsync };
73
+ //# sourceMappingURL=generation-fs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation-fs.mjs","names":["fileExistsAsync","directoryExistsAsync","getFileAsync","getJsonFileAsync","putFileAsync","putJsonFileAsync","ensureDirectoryAsync"],"sources":["../../../../../../../../packages/core/src/generations/generation-fs.ts"],"sourcesContent":["/**\n * The filesystem generation commands write through.\n *\n * Same signatures as the `@warlock.js/fs` functions they wrap, so a feature\n * hook or generator only changes its import. Outside a generation run they\n * pass straight through. Inside one (see `generation-run.ts`) every write is\n * recorded for the `--json` report, and in a `--dry-run` nothing reaches the\n * disk: writes land in an in-memory overlay that later reads in the same run\n * see, so a hook that writes a file and then edits it previews correctly.\n */\nimport * as fs from \"@warlock.js/fs\";\nimport {\n currentGenerationRun,\n hasPendingContentUnder,\n pendingGeneratedContent,\n recordGeneratedFile,\n recordSkippedFile,\n setPendingGeneratedContent,\n} from \"./generation-run\";\n\nexport { recordSkippedFile };\n\n/** Whether `target` exists on disk or in the dry-run overlay. */\nexport async function fileExistsAsync(target: string): Promise<boolean> {\n if (pendingGeneratedContent(target) !== undefined) return true;\n\n return fs.fileExistsAsync(target);\n}\n\n/** Whether `directory` exists on disk or holds a file in the dry-run overlay. */\nexport async function directoryExistsAsync(directory: string): Promise<boolean> {\n if (hasPendingContentUnder(directory)) return true;\n\n return fs.directoryExistsAsync(directory);\n}\n\n/** Read `target`, preferring what this dry run already \"wrote\". */\nexport async function getFileAsync(target: string): Promise<string> {\n const pending = pendingGeneratedContent(target);\n\n if (pending !== undefined) return pending;\n\n return fs.getFileAsync(target);\n}\n\nexport async function getJsonFileAsync<T = unknown>(target: string): Promise<T> {\n const pending = pendingGeneratedContent(target);\n\n if (pending !== undefined) return JSON.parse(pending) as T;\n\n return fs.getJsonFileAsync<T>(target);\n}\n\n/**\n * Write `content` to `target`, recording `create`, `update` or `unchanged`.\n * Identical content is not rewritten.\n */\nexport async function putFileAsync(target: string, content: string): Promise<void> {\n const run = currentGenerationRun();\n\n if (!run) {\n await fs.putFileAsync(target, content);\n return;\n }\n\n const exists = await fileExistsAsync(target);\n\n if (exists && (await getFileAsync(target)) === content) {\n recordGeneratedFile(target, \"unchanged\");\n return;\n }\n\n recordGeneratedFile(target, exists ? \"update\" : \"create\");\n\n if (run.dryRun) {\n setPendingGeneratedContent(target, content);\n return;\n }\n\n await fs.putFileAsync(target, content);\n}\n\nexport async function putJsonFileAsync(target: string, value: unknown): Promise<void> {\n if (!currentGenerationRun()) {\n await fs.putJsonFileAsync(target, value);\n return;\n }\n\n await putFileAsync(target, JSON.stringify(value, null, 2));\n}\n\n/** Create `directory`; a dry run leaves no empty folders behind. */\nexport async function ensureDirectoryAsync(directory: string): Promise<void> {\n if (currentGenerationRun()?.dryRun) return;\n\n await fs.ensureDirectoryAsync(directory);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuBA,eAAsBA,kBAAgB,QAAkC;CACtE,IAAI,wBAAwB,MAAM,MAAM,QAAW,OAAO;CAE1D,OAAO,GAAG,gBAAgB,MAAM;AAClC;;AAGA,eAAsBC,uBAAqB,WAAqC;CAC9E,IAAI,uBAAuB,SAAS,GAAG,OAAO;CAE9C,OAAO,GAAG,qBAAqB,SAAS;AAC1C;;AAGA,eAAsBC,eAAa,QAAiC;CAClE,MAAM,UAAU,wBAAwB,MAAM;CAE9C,IAAI,YAAY,QAAW,OAAO;CAElC,OAAO,GAAG,aAAa,MAAM;AAC/B;AAEA,eAAsBC,mBAA8B,QAA4B;CAC9E,MAAM,UAAU,wBAAwB,MAAM;CAE9C,IAAI,YAAY,QAAW,OAAO,KAAK,MAAM,OAAO;CAEpD,OAAO,GAAG,iBAAoB,MAAM;AACtC;;;;;AAMA,eAAsBC,eAAa,QAAgB,SAAgC;CACjF,MAAM,MAAM,qBAAqB;CAEjC,IAAI,CAAC,KAAK;EACR,MAAM,GAAG,aAAa,QAAQ,OAAO;EACrC;CACF;CAEA,MAAM,SAAS,MAAMJ,kBAAgB,MAAM;CAE3C,IAAI,UAAW,MAAME,eAAa,MAAM,MAAO,SAAS;EACtD,oBAAoB,QAAQ,WAAW;EACvC;CACF;CAEA,oBAAoB,QAAQ,SAAS,WAAW,QAAQ;CAExD,IAAI,IAAI,QAAQ;EACd,2BAA2B,QAAQ,OAAO;EAC1C;CACF;CAEA,MAAM,GAAG,aAAa,QAAQ,OAAO;AACvC;AAEA,eAAsBG,mBAAiB,QAAgB,OAA+B;CACpF,IAAI,CAAC,qBAAqB,GAAG;EAC3B,MAAM,GAAG,iBAAiB,QAAQ,KAAK;EACvC;CACF;CAEA,MAAMD,eAAa,QAAQ,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC3D;;AAGA,eAAsBE,uBAAqB,WAAkC;CAC3E,IAAI,qBAAqB,CAAC,EAAE,QAAQ;CAEpC,MAAM,GAAG,qBAAqB,SAAS;AACzC"}
@@ -0,0 +1,185 @@
1
+ import path from "node:path";
2
+ import { format, stripVTControlCharacters } from "node:util";
3
+
4
+ //#region ../../packages/core/src/generations/generation-run.ts
5
+ let activeRun;
6
+ /** Render an absolute path relative to the cwd, `/`-separated, for reports and logs. */
7
+ function toReportPath(target) {
8
+ return (path.relative(process.cwd(), path.resolve(target)) || target).split(path.sep).join("/");
9
+ }
10
+ /**
11
+ * Start recording a generation command. Every write made through
12
+ * `generation-fs` (and the `generate.*` writer) is recorded until
13
+ * {@link endGenerationRun}; in a dry run nothing reaches the disk.
14
+ */
15
+ function beginGenerationRun(options) {
16
+ activeRun = {
17
+ command: options.command,
18
+ dryRun: options.dryRun,
19
+ files: /* @__PURE__ */ new Map(),
20
+ pending: /* @__PURE__ */ new Map(),
21
+ details: {},
22
+ messages: []
23
+ };
24
+ }
25
+ /** The run being recorded, if any. */
26
+ function currentGenerationRun() {
27
+ return activeRun;
28
+ }
29
+ /** Whether the current generation command is a dry run. */
30
+ function isGenerationDryRun() {
31
+ return activeRun?.dryRun === true;
32
+ }
33
+ /**
34
+ * Record what happened to one file. A file first created and later edited in
35
+ * the same run stays `create`: the report describes the run's net effect.
36
+ */
37
+ function recordGeneratedFile(target, type, reason) {
38
+ if (!activeRun) return;
39
+ const key = toReportPath(target);
40
+ if (activeRun.files.get(key)?.type === "create" && type !== "skip") return;
41
+ activeRun.files.set(key, reason === void 0 ? {
42
+ path: key,
43
+ type
44
+ } : {
45
+ path: key,
46
+ type,
47
+ reason
48
+ });
49
+ }
50
+ /** Record a file the command deliberately left alone. */
51
+ function recordSkippedFile(target, reason) {
52
+ recordGeneratedFile(target, "skip", reason);
53
+ }
54
+ /** Attach a command-specific fact to the report, e.g. `dependencies`. */
55
+ function setGenerationDetail(key, value) {
56
+ if (!activeRun) return;
57
+ activeRun.details[key] = value;
58
+ }
59
+ /** The dry-run overlay content for `target`, when this run "wrote" it. */
60
+ function pendingGeneratedContent(target) {
61
+ return activeRun?.pending.get(path.resolve(target));
62
+ }
63
+ /** Keep `content` as what a dry run would have written to `target`. */
64
+ function setPendingGeneratedContent(target, content) {
65
+ activeRun?.pending.set(path.resolve(target), content);
66
+ }
67
+ /** Whether any pending dry-run file lives under `directory`. */
68
+ function hasPendingContentUnder(directory) {
69
+ if (!activeRun) return false;
70
+ const prefix = path.resolve(directory) + path.sep;
71
+ for (const file of activeRun.pending.keys()) if (file.startsWith(prefix)) return true;
72
+ return false;
73
+ }
74
+ /** Stop recording and build the report. */
75
+ function endGenerationRun(result) {
76
+ const run = activeRun;
77
+ activeRun = void 0;
78
+ const files = run ? [...run.files.values()] : [];
79
+ const report = {
80
+ schemaVersion: 1,
81
+ command: run?.command ?? "",
82
+ dryRun: run?.dryRun ?? false,
83
+ ok: result.ok,
84
+ written: files.filter((file) => file.type === "create" || file.type === "overwrite" || file.type === "update").map((file) => file.path),
85
+ skipped: files.filter((file) => file.type === "skip").map((file) => ({
86
+ path: file.path,
87
+ reason: file.reason ?? "exists"
88
+ })),
89
+ files,
90
+ details: run?.details ?? {},
91
+ messages: run?.messages ?? []
92
+ };
93
+ if (result.error !== void 0) report.error = result.error;
94
+ return report;
95
+ }
96
+ /**
97
+ * Pick the line that explains a failure from what the command printed: the
98
+ * first `Error…` line, else the last line.
99
+ */
100
+ function failureMessage(messages) {
101
+ return (messages.find((message) => /^\s*(?:✖\s*)?Error\b/.test(message)) ?? messages[messages.length - 1] ?? "The command failed.").trim();
102
+ }
103
+ /**
104
+ * Run a `make` / `generate.*` / `add` action under the recorder.
105
+ *
106
+ * `--dry-run` writes nothing. `--json` sends everything the command prints
107
+ * to stderr and prints exactly one {@link GenerationReport} on stdout — also
108
+ * when the command fails, including through `process.exit(1)`, so a caller
109
+ * parsing stdout always gets a document.
110
+ */
111
+ async function runGenerationCommand(command, data, action) {
112
+ const json = data.options.json === true || data.options.j === true;
113
+ beginGenerationRun({
114
+ command,
115
+ dryRun: data.options.dryRun === true
116
+ });
117
+ if (!json) {
118
+ try {
119
+ await action(data);
120
+ } finally {
121
+ endGenerationRun({ ok: true });
122
+ }
123
+ return;
124
+ }
125
+ const run = activeRun;
126
+ const originalLog = console.log;
127
+ const originalInfo = console.info;
128
+ const originalWarn = console.warn;
129
+ const originalError = console.error;
130
+ let printed = false;
131
+ const capture = (forward) => (...args) => {
132
+ run?.messages.push(stripVTControlCharacters(format(...args)));
133
+ forward(...args);
134
+ };
135
+ console.log = capture(originalError);
136
+ console.info = capture(originalError);
137
+ console.warn = capture(originalWarn);
138
+ console.error = capture(originalError);
139
+ const restoreConsole = () => {
140
+ console.log = originalLog;
141
+ console.info = originalInfo;
142
+ console.warn = originalWarn;
143
+ console.error = originalError;
144
+ };
145
+ const print = (report) => {
146
+ if (printed) return;
147
+ printed = true;
148
+ process.stdout.write(`${JSON.stringify(report)}\n`);
149
+ };
150
+ const onExit = (code) => {
151
+ const messages = run?.messages ?? [];
152
+ print(code === 0 ? endGenerationRun({ ok: true }) : endGenerationRun({
153
+ ok: false,
154
+ error: failureMessage(messages)
155
+ }));
156
+ };
157
+ process.once("exit", onExit);
158
+ try {
159
+ await action(data);
160
+ print(endGenerationRun({ ok: true }));
161
+ } catch (error) {
162
+ print(endGenerationRun({
163
+ ok: false,
164
+ error: error instanceof Error ? error.message : String(error)
165
+ }));
166
+ throw error;
167
+ } finally {
168
+ process.off("exit", onExit);
169
+ restoreConsole();
170
+ }
171
+ }
172
+ /** The options every generation command accepts, for `command({ options })`. */
173
+ const generationOutputOptions = [{
174
+ text: "--dry-run",
175
+ description: "Preview the files that would be written or skipped without writing anything",
176
+ type: "boolean"
177
+ }, {
178
+ text: "--json",
179
+ description: "Print one JSON report on stdout (files written, files skipped because they exist, messages)",
180
+ type: "boolean"
181
+ }];
182
+
183
+ //#endregion
184
+ export { currentGenerationRun, generationOutputOptions, hasPendingContentUnder, isGenerationDryRun, pendingGeneratedContent, recordGeneratedFile, recordSkippedFile, runGenerationCommand, setGenerationDetail, setPendingGeneratedContent, toReportPath };
185
+ //# sourceMappingURL=generation-run.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation-run.mjs","names":[],"sources":["../../../../../../../../packages/core/src/generations/generation-run.ts"],"sourcesContent":["import path from \"node:path\";\nimport { format, stripVTControlCharacters } from \"node:util\";\nimport type { CommandActionData } from \"../commands/types\";\n\n/**\n * What a generation command did (or, in a dry run, would do) to one file.\n *\n * - `create` — the file did not exist and is written.\n * - `overwrite` — the file existed and is replaced because `--force` was given.\n * - `update` — an existing file is edited in place (a route appended, a key merged).\n * - `unchanged` — the file already holds exactly the generated content.\n * - `skip` — the file exists and is left alone; `reason` says why.\n */\nexport type GenerationFileAction = \"create\" | \"overwrite\" | \"update\" | \"unchanged\" | \"skip\";\n\nexport type GenerationFileRecord = {\n /** Path relative to the current working directory, with `/` separators. */\n path: string;\n type: GenerationFileAction;\n reason?: string;\n};\n\n/**\n * The machine-readable result `--json` prints on stdout, one JSON document.\n *\n * `schemaVersion` changes only when a field is removed or changes meaning;\n * new fields may be added to version 1.\n */\nexport type GenerationReport = {\n schemaVersion: 1;\n /** The command as typed, e.g. `\"make endpoint\"` or `\"add\"`. */\n command: string;\n dryRun: boolean;\n ok: boolean;\n /** Files created, overwritten or updated (or, in a dry run, that would be). */\n written: string[];\n /** Files left alone because they already exist, with the reason. */\n skipped: Array<{ path: string; reason: string }>;\n /** Every file the command touched, in the order it touched them. */\n files: GenerationFileRecord[];\n /** Command-specific facts, e.g. the dependencies `add` installs. */\n details: Record<string, unknown>;\n /** Everything the command printed, without colours. */\n messages: string[];\n /** Why the command failed, when `ok` is false. */\n error?: string;\n};\n\ntype GenerationRunState = {\n command: string;\n dryRun: boolean;\n files: Map<string, GenerationFileRecord>;\n /** Dry-run overlay: content a real run would have written, keyed by absolute path. */\n pending: Map<string, string>;\n details: Record<string, unknown>;\n messages: string[];\n};\n\nlet activeRun: GenerationRunState | undefined;\n\n/** Render an absolute path relative to the cwd, `/`-separated, for reports and logs. */\nexport function toReportPath(target: string): string {\n const relative = path.relative(process.cwd(), path.resolve(target)) || target;\n\n return relative.split(path.sep).join(\"/\");\n}\n\n/**\n * Start recording a generation command. Every write made through\n * `generation-fs` (and the `generate.*` writer) is recorded until\n * {@link endGenerationRun}; in a dry run nothing reaches the disk.\n */\nexport function beginGenerationRun(options: { command: string; dryRun: boolean }): void {\n activeRun = {\n command: options.command,\n dryRun: options.dryRun,\n files: new Map(),\n pending: new Map(),\n details: {},\n messages: [],\n };\n}\n\n/** The run being recorded, if any. */\nexport function currentGenerationRun(): Readonly<GenerationRunState> | undefined {\n return activeRun;\n}\n\n/** Whether the current generation command is a dry run. */\nexport function isGenerationDryRun(): boolean {\n return activeRun?.dryRun === true;\n}\n\n/**\n * Record what happened to one file. A file first created and later edited in\n * the same run stays `create`: the report describes the run's net effect.\n */\nexport function recordGeneratedFile(\n target: string,\n type: GenerationFileAction,\n reason?: string,\n): void {\n if (!activeRun) return;\n\n const key = toReportPath(target);\n const previous = activeRun.files.get(key);\n\n if (previous?.type === \"create\" && type !== \"skip\") return;\n\n activeRun.files.set(\n key,\n reason === undefined ? { path: key, type } : { path: key, type, reason },\n );\n}\n\n/** Record a file the command deliberately left alone. */\nexport function recordSkippedFile(target: string, reason: string): void {\n recordGeneratedFile(target, \"skip\", reason);\n}\n\n/** Attach a command-specific fact to the report, e.g. `dependencies`. */\nexport function setGenerationDetail(key: string, value: unknown): void {\n if (!activeRun) return;\n\n activeRun.details[key] = value;\n}\n\n/** The dry-run overlay content for `target`, when this run \"wrote\" it. */\nexport function pendingGeneratedContent(target: string): string | undefined {\n return activeRun?.pending.get(path.resolve(target));\n}\n\n/** Keep `content` as what a dry run would have written to `target`. */\nexport function setPendingGeneratedContent(target: string, content: string): void {\n activeRun?.pending.set(path.resolve(target), content);\n}\n\n/** Whether any pending dry-run file lives under `directory`. */\nexport function hasPendingContentUnder(directory: string): boolean {\n if (!activeRun) return false;\n\n const prefix = path.resolve(directory) + path.sep;\n\n for (const file of activeRun.pending.keys()) {\n if (file.startsWith(prefix)) return true;\n }\n\n return false;\n}\n\n/** Stop recording and build the report. */\nexport function endGenerationRun(result: { ok: boolean; error?: string }): GenerationReport {\n const run = activeRun;\n\n activeRun = undefined;\n\n const files = run ? [...run.files.values()] : [];\n const report: GenerationReport = {\n schemaVersion: 1,\n command: run?.command ?? \"\",\n dryRun: run?.dryRun ?? false,\n ok: result.ok,\n written: files\n .filter(\n (file) => file.type === \"create\" || file.type === \"overwrite\" || file.type === \"update\",\n )\n .map((file) => file.path),\n skipped: files\n .filter((file) => file.type === \"skip\")\n .map((file) => ({ path: file.path, reason: file.reason ?? \"exists\" })),\n files,\n details: run?.details ?? {},\n messages: run?.messages ?? [],\n };\n\n if (result.error !== undefined) report.error = result.error;\n\n return report;\n}\n\n/**\n * Pick the line that explains a failure from what the command printed: the\n * first `Error…` line, else the last line.\n */\nfunction failureMessage(messages: readonly string[]): string {\n const errorLine = messages.find((message) => /^\\s*(?:✖\\s*)?Error\\b/.test(message));\n\n return (errorLine ?? messages[messages.length - 1] ?? \"The command failed.\").trim();\n}\n\n/**\n * Run a `make` / `generate.*` / `add` action under the recorder.\n *\n * `--dry-run` writes nothing. `--json` sends everything the command prints\n * to stderr and prints exactly one {@link GenerationReport} on stdout — also\n * when the command fails, including through `process.exit(1)`, so a caller\n * parsing stdout always gets a document.\n */\nexport async function runGenerationCommand(\n command: string,\n data: CommandActionData,\n action: (data: CommandActionData) => Promise<void>,\n): Promise<void> {\n const json = data.options.json === true || data.options.j === true;\n const dryRun = data.options.dryRun === true;\n\n beginGenerationRun({ command, dryRun });\n\n if (!json) {\n try {\n await action(data);\n } finally {\n endGenerationRun({ ok: true });\n }\n\n return;\n }\n\n const run = activeRun;\n const originalLog = console.log;\n const originalInfo = console.info;\n const originalWarn = console.warn;\n const originalError = console.error;\n let printed = false;\n\n const capture =\n (forward: (...args: unknown[]) => void) =>\n (...args: unknown[]): void => {\n run?.messages.push(stripVTControlCharacters(format(...args)));\n forward(...args);\n };\n\n // stdout carries the report and nothing else.\n console.log = capture(originalError);\n console.info = capture(originalError);\n console.warn = capture(originalWarn);\n console.error = capture(originalError);\n\n const restoreConsole = (): void => {\n console.log = originalLog;\n console.info = originalInfo;\n console.warn = originalWarn;\n console.error = originalError;\n };\n\n const print = (report: GenerationReport): void => {\n if (printed) return;\n\n printed = true;\n process.stdout.write(`${JSON.stringify(report)}\\n`);\n };\n\n // Generators stop with `process.exit(1)` on bad input. The report still has\n // to reach stdout, and `exit` listeners run synchronously before the process\n // ends (stdout writes to a pipe or file are synchronous on Linux and macOS).\n const onExit = (code: number): void => {\n const messages = run?.messages ?? [];\n\n print(\n code === 0\n ? endGenerationRun({ ok: true })\n : endGenerationRun({ ok: false, error: failureMessage(messages) }),\n );\n };\n\n process.once(\"exit\", onExit);\n\n try {\n await action(data);\n print(endGenerationRun({ ok: true }));\n } catch (error) {\n print(\n endGenerationRun({\n ok: false,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n\n throw error;\n } finally {\n process.off(\"exit\", onExit);\n restoreConsole();\n }\n}\n\n/** The options every generation command accepts, for `command({ options })`. */\nexport const generationOutputOptions = [\n {\n text: \"--dry-run\",\n description: \"Preview the files that would be written or skipped without writing anything\",\n type: \"boolean\" as const,\n },\n {\n text: \"--json\",\n description:\n \"Print one JSON report on stdout (files written, files skipped because they exist, messages)\",\n type: \"boolean\" as const,\n },\n];\n"],"mappings":"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"}
@@ -660,26 +660,168 @@ const notificationLocalesStub = `{
660
660
  "notFound": { "en": "Notification not found" }
661
661
  }
662
662
  `;
663
- /**
664
- * A controller spec for the notifications module, written only when the app
665
- * has the test feature. It checks the one thing true in every app — the route
666
- * refuses a request with no signed-in user — and leaves the signed-in cases to
667
- * the app, which owns its login helper.
668
- */
663
+ /** Shared fixtures for the generated signed-in notification controller specs. */
664
+ const notificationTestFixtureStub = `import type { Auth } from "@warlock.js/auth";
665
+ import type { ChildModel } from "@warlock.js/cascade";
666
+ import { config } from "@warlock.js/core";
667
+ import { actingAs, type AuthenticatedTestClient } from "@warlock.js/core/tests";
668
+ import { randomUUID } from "node:crypto";
669
+ import { Notification } from "../models/notification/notification.model";
670
+
671
+ /** Isolated recipients and rows; adjust user fields if your auth model requires more. */
672
+ export async function createNotificationTestFixture() {
673
+ const User = config.key<ChildModel<Auth>>("auth.userType.user");
674
+ if (!User) throw new Error("Notification specs need the configured auth.userType.user model.");
675
+ const users: Auth[] = [];
676
+ const clients: AuthenticatedTestClient[] = [];
677
+ const rows: Notification[] = [];
678
+
679
+ async function cleanup() {
680
+ const credentials = await Promise.allSettled(
681
+ clients.splice(0).map((client) => client.dispose()),
682
+ );
683
+ const notifications = await Promise.allSettled(
684
+ rows.splice(0).map((row) => row.destroy({ strategy: "permanent" })),
685
+ );
686
+ const recipients = await Promise.allSettled(
687
+ users.splice(0).map((user) => user.destroy({ strategy: "permanent" })),
688
+ );
689
+ const errors = [...credentials, ...notifications, ...recipients]
690
+ .filter((result): result is PromiseRejectedResult => result.status === "rejected")
691
+ .map((result) => result.reason);
692
+ if (errors.length) throw new AggregateError(errors, "Notification fixture cleanup failed.");
693
+ }
694
+
695
+ const createUser = async () => {
696
+ const user = await User.create({
697
+ name: "Notification test user",
698
+ email: "notifications-" + randomUUID() + "@example.test",
699
+ password: "test-password",
700
+ });
701
+ users.push(user);
702
+ return user;
703
+ };
704
+ const signIn = async (user: Auth) => {
705
+ const client = await actingAs(user);
706
+ clients.push(client);
707
+ return client;
708
+ };
709
+ const createNotification = async (recipientId: string | number, title: string, readAt?: Date) => {
710
+ const row = await Notification.create({
711
+ user_id: recipientId,
712
+ type: "test",
713
+ title,
714
+ read_at: readAt,
715
+ });
716
+ rows.push(row);
717
+ return row;
718
+ };
719
+
720
+ try {
721
+ const user = await createUser();
722
+ const otherUser = await createUser();
723
+ const client = await signIn(user);
724
+ const otherClient = await signIn(otherUser);
725
+ const own = await createNotification(user.id, "Own unread notification");
726
+ const alreadyRead = await createNotification(user.id, "Own read notification", new Date());
727
+ const foreign = await createNotification(otherUser.id, "Other user's notification");
728
+ await createNotification(otherUser.id, "Other user's second notification");
729
+ return { user, otherUser, client, otherClient, own, alreadyRead, foreign, cleanup };
730
+ } catch (error) {
731
+ // Preserve the setup failure after attempting every owned resource's cleanup.
732
+ await cleanup().catch(() => undefined);
733
+ throw error;
734
+ }
735
+ }
736
+ `;
737
+ /** A spec for each generated controller, including authenticated recipient isolation. */
669
738
  function notificationControllerSpecStub(method, path) {
670
739
  const helper = method === "GET" ? "testGet" : "testPatch";
671
- return `import { ${helper} } from "@warlock.js/core/tests";
672
- import { describe, expect, it } from "vitest";
740
+ let signedInCases;
741
+ if (path === "/notifications") signedInCases = ` it("lists only the signed-in recipient's rows, ignoring a forged recipient query", async () => {
742
+ const response = await fixture.client.get(
743
+ "/notifications?recipientId=" + fixture.otherUser.id,
744
+ );
745
+ const body = await expectJson<{ notifications: { id: string | number }[] }>(response);
746
+ expect(body.notifications.map((row) => row.id)).toEqual(
747
+ expect.arrayContaining([fixture.own.id, fixture.alreadyRead.id]),
748
+ );
749
+ expect(body.notifications).toHaveLength(2);
750
+ });
751
+
752
+ it("applies unread and paging query filters", async () => {
753
+ const response = await fixture.client.get("/notifications?unread=true&limit=1");
754
+ const body = await expectJson<{ notifications: { id: string | number }[] }>(response);
755
+ expect(body.notifications.map((row) => row.id)).toEqual([fixture.own.id]);
756
+ });
757
+
758
+ it("rejects a list limit above the schema's cap", async () => {
759
+ expect((await fixture.client.get("/notifications?limit=999")).status).toBe(422);
760
+ });`;
761
+ else if (path === "/notifications/unread-count") signedInCases = ` it("counts only this recipient's unread notifications", async () => {
762
+ const own = await expectJson<{ count: number }>(
763
+ await fixture.client.get("/notifications/unread-count"),
764
+ );
765
+ const other = await expectJson<{ count: number }>(
766
+ await fixture.otherClient.get("/notifications/unread-count"),
767
+ );
768
+ expect(own.count).toBe(1);
769
+ expect(other.count).toBe(2);
770
+ });`;
771
+ else if (path === "/notifications/read-all") signedInCases = ` it("marks only this recipient's unread rows and leaves another recipient unchanged", async () => {
772
+ const body = await expectJson<{ count: number }>(
773
+ await fixture.client.patch("/notifications/read-all"),
774
+ );
775
+ expect(body.count).toBe(1);
776
+ expect((await Notification.find(fixture.own.id))?.isRead).toBe(true);
777
+ expect((await Notification.find(fixture.foreign.id))?.isRead).toBe(false);
778
+ const other = await expectJson<{ count: number }>(
779
+ await fixture.otherClient.get("/notifications/unread-count"),
780
+ );
781
+ expect(other.count).toBe(2);
782
+ const again = await expectJson<{ count: number }>(
783
+ await fixture.client.patch("/notifications/read-all"),
784
+ );
785
+ expect(again.count).toBe(0);
786
+ });`;
787
+ else signedInCases = ` it("marks its own notification read and returns it", async () => {
788
+ const response = await fixture.client.patch("/notifications/" + fixture.own.id + "/read");
789
+ const body = await expectJson<{
790
+ notification: { id: string | number; readAt?: string | null };
791
+ }>(response);
792
+ expect(body.notification.id).toBe(fixture.own.id);
793
+ expect(body.notification.readAt).toBeTruthy();
794
+ expect((await Notification.find(fixture.own.id))?.isRead).toBe(true);
795
+ });
796
+
797
+ it("answers 404 for another recipient's notification without changing it", async () => {
798
+ const response = await fixture.client.patch("/notifications/" + fixture.foreign.id + "/read");
799
+ expect(response.status).toBe(404);
800
+ expect((await Notification.find(fixture.foreign.id))?.isRead).toBe(false);
801
+ });`;
802
+ return `import { expectJson, ${helper} } from "@warlock.js/core/tests";
803
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
804
+ ${method === "PATCH" ? "import { Notification } from \"../models/notification/notification.model\";\n" : ""}import { createNotificationTestFixture } from "../tests/notifications.fixture";
673
805
 
674
806
  describe("${method} ${path}", () => {
675
807
  it("answers 401 without a signed-in user", async () => {
676
808
  const response = await ${helper}("${path}");
677
-
678
809
  expect(response.status).toBe(401);
679
810
  });
680
811
 
681
- // Add the signed-in cases with your app's login helper, including that
682
- // another user's notifications are never returned or changed.
812
+ describe("signed in", () => {
813
+ let fixture: Awaited<ReturnType<typeof createNotificationTestFixture>> | undefined;
814
+ beforeEach(async () => {
815
+ fixture = await createNotificationTestFixture();
816
+ });
817
+ afterEach(async () => {
818
+ const current = fixture;
819
+ fixture = undefined;
820
+ await current?.cleanup();
821
+ });
822
+
823
+ ${signedInCases.replaceAll("fixture.", "fixture!.")}
824
+ });
683
825
  });
684
826
  `;
685
827
  }
@@ -781,7 +923,7 @@ contactController.validation = { schema: contactSchema };
781
923
  const webContactRoutesStub = `import { router } from "@warlock.js/core";
782
924
  import { contactController } from "./controllers/contact.controller";
783
925
 
784
- router.post("/api/contact", contactController);
926
+ router.post("/api/contact", contactController, { public: true });
785
927
  `;
786
928
  /**
787
929
  * `src/web/index.register.ts` — universal static setup for the starter page.
@@ -1047,5 +1189,5 @@ function ContactMessage() {
1047
1189
  `;
1048
1190
 
1049
1191
  //#endregion
1050
- export { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub, aiConfigStub, communicatorsConfigStub, listNotificationsControllerStub, listNotificationsSchemaStub, markAllNotificationsReadControllerStub, markNotificationReadControllerStub, markNotificationReadServiceStub, notificationControllerSpecStub, notificationLocalesStub, notificationMigrationStub, notificationModelStub, notificationResourceStub, notificationRoutesStub, notificationsConfigStub, notificationsRequestTypeStub, socketConfigStub, unreadNotificationsCountControllerStub, webContactControllerStub, webContactRoutesStub, webHomePageStub, webHomeRegisterStub, webRootStub };
1192
+ export { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub, aiConfigStub, communicatorsConfigStub, listNotificationsControllerStub, listNotificationsSchemaStub, markAllNotificationsReadControllerStub, markNotificationReadControllerStub, markNotificationReadServiceStub, notificationControllerSpecStub, notificationLocalesStub, notificationMigrationStub, notificationModelStub, notificationResourceStub, notificationRoutesStub, notificationTestFixtureStub, notificationsConfigStub, notificationsRequestTypeStub, socketConfigStub, unreadNotificationsCountControllerStub, webContactControllerStub, webContactRoutesStub, webHomePageStub, webHomeRegisterStub, webRootStub };
1051
1193
  //# sourceMappingURL=stubs.mjs.map