@warlock.js/core 5.34.2 → 5.35.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 (259) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/esm/cli/cli-commands.manager.mjs +1 -1
  3. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  4. package/esm/cli/commands/add.command.mjs +1 -0
  5. package/esm/cli/commands/add.command.mjs.map +1 -1
  6. package/esm/cli/commands/build.command.mjs +30 -3
  7. package/esm/cli/commands/build.command.mjs.map +1 -1
  8. package/esm/cli/commands/check/check-command.action.mjs +1 -0
  9. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  10. package/esm/cli/commands/check.command.mjs +5 -0
  11. package/esm/cli/commands/check.command.mjs.map +1 -1
  12. package/esm/cli/commands/explain/explain-command.action.mjs +21 -0
  13. package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -1
  14. package/esm/cli/commands/explain.command.mjs +14 -4
  15. package/esm/cli/commands/explain.command.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generate.command.mjs +19 -1
  17. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/endpoint.generator.mjs +1 -1
  19. package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generators/web.generator.mjs +169 -0
  21. package/esm/cli/commands/generate/generators/web.generator.mjs.map +1 -0
  22. package/esm/cli/commands/generate/templates/web-stubs.mjs +231 -0
  23. package/esm/cli/commands/generate/templates/web-stubs.mjs.map +1 -0
  24. package/esm/cli/commands/inspect/inspect-command.action.mjs +4 -3
  25. package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -1
  26. package/esm/cli/commands/inspect.command.mjs +13 -8
  27. package/esm/cli/commands/inspect.command.mjs.map +1 -1
  28. package/esm/cli/commands/mcp/mcp-command.action.mjs +1 -1
  29. package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -1
  30. package/esm/config/config-loader.d.mts.map +1 -1
  31. package/esm/config/config-loader.mjs +2 -0
  32. package/esm/config/config-loader.mjs.map +1 -1
  33. package/esm/config/load-config-files.mjs +4 -0
  34. package/esm/config/load-config-files.mjs.map +1 -1
  35. package/esm/dev-server/file-event-handler.mjs +11 -1
  36. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  37. package/esm/dev-server/file-manager.d.mts +1 -1
  38. package/esm/dev-server/file-manager.d.mts.map +1 -1
  39. package/esm/dev-server/file-manager.mjs.map +1 -1
  40. package/esm/dev-server/files-orchestrator.mjs +16 -1
  41. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  42. package/esm/dev-server/files-watcher.mjs +13 -4
  43. package/esm/dev-server/files-watcher.mjs.map +1 -1
  44. package/esm/dev-server/loader/loader-port-acks.mjs +21 -9
  45. package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
  46. package/esm/dev-server/loader/register-loader.mjs +31 -12
  47. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  48. package/esm/dev-server/module-loader.mjs +14 -0
  49. package/esm/dev-server/module-loader.mjs.map +1 -1
  50. package/esm/dev-server/special-file-patterns.mjs +4 -1
  51. package/esm/dev-server/special-file-patterns.mjs.map +1 -1
  52. package/esm/e2e/browser.d.mts.map +1 -1
  53. package/esm/e2e/browser.mjs +3 -1
  54. package/esm/e2e/browser.mjs.map +1 -1
  55. package/esm/e2e/index.d.mts +2 -2
  56. package/esm/e2e/page.d.mts +6 -1
  57. package/esm/e2e/page.d.mts.map +1 -1
  58. package/esm/e2e/page.mjs +36 -1
  59. package/esm/e2e/page.mjs.map +1 -1
  60. package/esm/e2e/types.d.mts +37 -0
  61. package/esm/e2e/types.d.mts.map +1 -1
  62. package/esm/e2e/types.mjs.map +1 -1
  63. package/esm/errors/error-codes.d.mts +48 -0
  64. package/esm/errors/error-codes.d.mts.map +1 -1
  65. package/esm/errors/error-codes.mjs +48 -0
  66. package/esm/errors/error-codes.mjs.map +1 -1
  67. package/esm/errors/index.d.mts +2 -2
  68. package/esm/errors/index.mjs +2 -2
  69. package/esm/generations/add-command.action.mjs +2 -0
  70. package/esm/generations/add-command.action.mjs.map +1 -1
  71. package/esm/generations/design-system-payload.mjs +53 -0
  72. package/esm/generations/design-system-payload.mjs.map +1 -0
  73. package/esm/generations/features/design-system.feature.mjs +38 -0
  74. package/esm/generations/features/design-system.feature.mjs.map +1 -0
  75. package/esm/generations/features/index.mjs +3 -1
  76. package/esm/generations/features/index.mjs.map +1 -1
  77. package/esm/generations/features/notifications.feature.mjs +13 -2
  78. package/esm/generations/features/notifications.feature.mjs.map +1 -1
  79. package/esm/generations/features/test.feature.mjs +48 -13
  80. package/esm/generations/features/test.feature.mjs.map +1 -1
  81. package/esm/generations/stubs.mjs +9 -5
  82. package/esm/generations/stubs.mjs.map +1 -1
  83. package/esm/http/error-mapper.d.mts +1 -1
  84. package/esm/http/error-mapper.mjs +3 -3
  85. package/esm/http/error-mapper.mjs.map +1 -1
  86. package/esm/http/error-response.d.mts +15 -0
  87. package/esm/http/error-response.d.mts.map +1 -0
  88. package/esm/http/error-response.mjs +47 -0
  89. package/esm/http/error-response.mjs.map +1 -0
  90. package/esm/http/errors/errors.d.mts +8 -0
  91. package/esm/http/errors/errors.d.mts.map +1 -1
  92. package/esm/http/errors/errors.mjs +13 -1
  93. package/esm/http/errors/errors.mjs.map +1 -1
  94. package/esm/http/index.d.mts +5 -3
  95. package/esm/http/index.mjs +3 -2
  96. package/esm/http/list-endpoint.d.mts +35 -0
  97. package/esm/http/list-endpoint.d.mts.map +1 -0
  98. package/esm/http/list-endpoint.mjs +28 -0
  99. package/esm/http/list-endpoint.mjs.map +1 -0
  100. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  101. package/esm/http/middleware/inject-request-context.mjs +12 -2
  102. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  103. package/esm/http/response.d.mts +2 -0
  104. package/esm/http/response.d.mts.map +1 -1
  105. package/esm/http/response.mjs +2 -0
  106. package/esm/http/response.mjs.map +1 -1
  107. package/esm/http/server.d.mts.map +1 -1
  108. package/esm/http/server.mjs +2 -0
  109. package/esm/http/server.mjs.map +1 -1
  110. package/esm/http/types.d.mts +7 -0
  111. package/esm/http/types.d.mts.map +1 -1
  112. package/esm/index.d.mts +10 -7
  113. package/esm/index.mjs +3 -2
  114. package/esm/openapi/security.mjs +12 -4
  115. package/esm/openapi/security.mjs.map +1 -1
  116. package/esm/openapi/types.d.mts +2 -0
  117. package/esm/openapi/types.d.mts.map +1 -1
  118. package/esm/production/build-budgets.d.mts +46 -0
  119. package/esm/production/build-budgets.d.mts.map +1 -0
  120. package/esm/production/build-budgets.mjs +109 -0
  121. package/esm/production/build-budgets.mjs.map +1 -0
  122. package/esm/production/production-builder.mjs +13 -1
  123. package/esm/production/production-builder.mjs.map +1 -1
  124. package/esm/production/resolve-build-config.d.mts +1 -1
  125. package/esm/production/resolve-build-config.d.mts.map +1 -1
  126. package/esm/production/resolve-build-config.mjs.map +1 -1
  127. package/esm/restful/restful.d.mts +1 -1
  128. package/esm/tests/fakes/storage-fake.d.mts +3 -1
  129. package/esm/tests/fakes/storage-fake.d.mts.map +1 -1
  130. package/esm/tests/fakes/storage-fake.mjs +3 -3
  131. package/esm/tests/fakes/storage-fake.mjs.map +1 -1
  132. package/esm/tests/fakes/test-fakes.d.mts +3 -1
  133. package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
  134. package/esm/tests/fakes/test-fakes.mjs +3 -2
  135. package/esm/tests/fakes/test-fakes.mjs.map +1 -1
  136. package/esm/tests/index.d.mts +4 -2
  137. package/esm/tests/index.mjs +3 -1
  138. package/esm/tests/load-unit-config.mjs +24 -0
  139. package/esm/tests/load-unit-config.mjs.map +1 -0
  140. package/esm/tests/response-contract.d.mts +32 -0
  141. package/esm/tests/response-contract.d.mts.map +1 -0
  142. package/esm/tests/response-contract.mjs +116 -0
  143. package/esm/tests/response-contract.mjs.map +1 -0
  144. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  145. package/esm/tests/start-http-development-server.mjs +10 -2
  146. package/esm/tests/start-http-development-server.mjs.map +1 -1
  147. package/esm/tests/test-lifecycle-state.mjs.map +1 -1
  148. package/esm/tests/test-locales.mjs +33 -0
  149. package/esm/tests/test-locales.mjs.map +1 -0
  150. package/esm/tests/test-runtime-scope.d.mts +15 -0
  151. package/esm/tests/test-runtime-scope.d.mts.map +1 -0
  152. package/esm/tests/test-runtime-scope.mjs +80 -0
  153. package/esm/tests/test-runtime-scope.mjs.map +1 -0
  154. package/esm/tests/vitest-setup.d.mts +13 -3
  155. package/esm/tests/vitest-setup.d.mts.map +1 -1
  156. package/esm/tests/vitest-setup.mjs +146 -18
  157. package/esm/tests/vitest-setup.mjs.map +1 -1
  158. package/esm/tooling/check/affected-files.d.mts +5 -0
  159. package/esm/tooling/check/affected-files.d.mts.map +1 -0
  160. package/esm/tooling/check/affected-files.mjs +56 -0
  161. package/esm/tooling/check/affected-files.mjs.map +1 -0
  162. package/esm/tooling/check/changed-files.d.mts +2 -0
  163. package/esm/tooling/check/changed-files.d.mts.map +1 -1
  164. package/esm/tooling/check/changed-files.mjs +2 -2
  165. package/esm/tooling/check/changed-files.mjs.map +1 -1
  166. package/esm/tooling/check/conventions-step.d.mts +2 -1
  167. package/esm/tooling/check/conventions-step.d.mts.map +1 -1
  168. package/esm/tooling/check/conventions-step.mjs +48 -6
  169. package/esm/tooling/check/conventions-step.mjs.map +1 -1
  170. package/esm/tooling/check/fixes.d.mts +8 -0
  171. package/esm/tooling/check/fixes.d.mts.map +1 -0
  172. package/esm/tooling/check/fixes.mjs +31 -0
  173. package/esm/tooling/check/fixes.mjs.map +1 -0
  174. package/esm/tooling/check/index.d.mts +12 -0
  175. package/esm/tooling/check/index.mjs +3 -2
  176. package/esm/tooling/check/run-check.d.mts +2 -0
  177. package/esm/tooling/check/run-check.d.mts.map +1 -1
  178. package/esm/tooling/check/run-check.mjs +20 -4
  179. package/esm/tooling/check/run-check.mjs.map +1 -1
  180. package/esm/tooling/check/run-tests.mjs +1 -1
  181. package/esm/tooling/check/source-ast.d.mts +1 -0
  182. package/esm/tooling/check/web-conventions.d.mts +10 -0
  183. package/esm/tooling/check/web-conventions.d.mts.map +1 -0
  184. package/esm/tooling/check/web-conventions.mjs +126 -0
  185. package/esm/tooling/check/web-conventions.mjs.map +1 -0
  186. package/esm/tooling/index.d.mts +7 -2
  187. package/esm/tooling/index.mjs +4 -2
  188. package/esm/tooling/inspect/collect-files.mjs +172 -0
  189. package/esm/tooling/inspect/collect-files.mjs.map +1 -0
  190. package/esm/tooling/inspect/collect-pages.mjs +3 -2
  191. package/esm/tooling/inspect/collect-pages.mjs.map +1 -1
  192. package/esm/tooling/inspect/collect-routes.mjs +1 -1
  193. package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
  194. package/esm/tooling/inspect/explain-file.d.mts +12 -0
  195. package/esm/tooling/inspect/explain-file.d.mts.map +1 -0
  196. package/esm/tooling/inspect/explain-file.mjs +45 -0
  197. package/esm/tooling/inspect/explain-file.mjs.map +1 -0
  198. package/esm/tooling/inspect/index.d.mts +3 -2
  199. package/esm/tooling/inspect/index.mjs +2 -1
  200. package/esm/tooling/inspect/inspect-app.d.mts.map +1 -1
  201. package/esm/tooling/inspect/inspect-app.mjs +9 -3
  202. package/esm/tooling/inspect/inspect-app.mjs.map +1 -1
  203. package/esm/tooling/inspect/inspect-report.schema.mjs +34 -1
  204. package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -1
  205. package/esm/tooling/inspect/inspect-report.types.d.mts +17 -1
  206. package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -1
  207. package/esm/tooling/inspect/inspect-report.types.mjs +2 -1
  208. package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -1
  209. package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -1
  210. package/esm/tooling/mcp/mcp-tools.mjs +8 -2
  211. package/esm/tooling/mcp/mcp-tools.mjs.map +1 -1
  212. package/esm/tooling/problem.d.mts +4 -0
  213. package/esm/tooling/problem.d.mts.map +1 -1
  214. package/esm/tooling/problem.mjs +1 -0
  215. package/esm/tooling/problem.mjs.map +1 -1
  216. package/esm/tooling/syntax/ast.d.mts +2 -0
  217. package/esm/tooling/typecheck/run-typecheck.mjs +5 -1
  218. package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -1
  219. package/esm/upgrade/codemods/toolchain-templates.mjs +3 -1
  220. package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -1
  221. package/esm/vite/index.d.mts +3 -1
  222. package/esm/vite/index.mjs +3 -1
  223. package/esm/vite/test-setup-reporter.d.mts +16 -0
  224. package/esm/vite/test-setup-reporter.d.mts.map +1 -0
  225. package/esm/vite/test-setup-reporter.mjs +55 -0
  226. package/esm/vite/test-setup-reporter.mjs.map +1 -0
  227. package/esm/vite/test-workers.d.mts +5 -0
  228. package/esm/vite/test-workers.d.mts.map +1 -0
  229. package/esm/vite/test-workers.mjs +38 -0
  230. package/esm/vite/test-workers.mjs.map +1 -0
  231. package/esm/warlock-config/index.d.mts +2 -1
  232. package/esm/warlock-config/types.d.mts +3 -0
  233. package/esm/warlock-config/types.d.mts.map +1 -1
  234. package/llms-full.txt +883 -70
  235. package/llms.txt +58 -56
  236. package/package.json +15 -14
  237. package/schemas/inspect-report.schema.json +47 -1
  238. package/skills/build-budgets/SKILL.md +51 -0
  239. package/skills/build-restful/SKILL.md +56 -0
  240. package/skills/configure-app/SKILL.md +34 -0
  241. package/skills/create-module/SKILL.md +9 -0
  242. package/skills/generate-openapi/SKILL.md +1 -1
  243. package/skills/generate-web-ui/SKILL.md +71 -0
  244. package/skills/index.md +3 -3
  245. package/skills/run-app/SKILL.md +1 -1
  246. package/skills/send-mail/SKILL.md +25 -0
  247. package/skills/send-response/SKILL.md +77 -0
  248. package/skills/test-browser/SKILL.md +54 -0
  249. package/skills/test-http/SKILL.md +91 -0
  250. package/skills/test-service/SKILL.md +161 -3
  251. package/skills/upgrade-from-5-29/SKILL.md +22 -0
  252. package/skills/use-localization/SKILL.md +21 -0
  253. package/skills/warlock-check/SKILL.md +72 -1
  254. package/skills/warlock-inspect/SKILL.md +45 -2
  255. package/skills/warlock-mcp/SKILL.md +4 -0
  256. package/skills/warlock-trace/SKILL.md +2 -2
  257. package/skills/write-cli-command/SKILL.md +20 -2
  258. package/esm/production/build-app-production.mjs +0 -14
  259. package/esm/production/build-app-production.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"error-codes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/errors/error-codes.ts"],"sourcesContent":["/**\n * The registry of stable Warlock error codes.\n *\n * Every framework error core throws, every `warlock doctor` finding and every\n * `warlock check` problem carries one of these codes, so a person or an agent\n * can look the code up instead of interpreting the message. `warlock explain\n * <CODE>` prints an entry; the docs page `cli/error-codes` lists all of them.\n *\n * THE SCHEME: `WL` followed by four digits. The first digit is the area:\n *\n * | Range | Area | Meaning |\n * | ------ | ----------- | ---------------------------------------------------- |\n * | WL1xxx | config | a configuration value is missing or invalid |\n * | WL2xxx | boot | the app could not boot: modules, connectors, services |\n * | WL3xxx | routing | route registration and lookup |\n * | WL4xxx | validation | request validation schemas |\n * | WL5xxx | http | the HTTP layer at runtime |\n * | WL6xxx | database | migrations, seeders and database code |\n * | WL7xxx | cli | CLI usage, and problems `warlock check` finds in code |\n * | WL8xxx | environment | this machine: Node, tools, services, ports |\n * | WL9xxx | convention | framework conventions and release hygiene |\n *\n * STABILITY: a code, once released, keeps its meaning forever. Its `title`,\n * `fix` and links may be reworded; a code is never reused for a different\n * problem and never renumbered. A retired code stays in this table with\n * `retired: true`.\n *\n * `category` splits \"your code\" from \"this machine\": `environment` problems are\n * the ones `warlock check` reports with exit code 2, so an agent stops editing\n * code to fix a missing Redis server.\n *\n * This module is part of the side-effect-free `@warlock.js/core/errors` entry:\n * it imports nothing.\n */\n\n/** The area a code belongs to, matching its first digit. */\nexport type ErrorCodeArea =\n | \"config\"\n | \"boot\"\n | \"routing\"\n | \"validation\"\n | \"http\"\n | \"database\"\n | \"cli\"\n | \"environment\"\n | \"convention\";\n\n/**\n * Who has to act: `code` problems are fixed in the project's source or config,\n * `environment` problems on the machine running it.\n */\nexport type ErrorCodeCategory = \"code\" | \"environment\";\n\n/** One registry entry. */\nexport type ErrorCodeDefinition = {\n /** The stable code, e.g. `\"WL3001\"`. */\n readonly code: string;\n /** The area, matching the first digit. */\n readonly area: ErrorCodeArea;\n /** Whether the project or the machine has to change. */\n readonly category: ErrorCodeCategory;\n /** One-line name of the problem. */\n readonly title: string;\n /** One-line \"what to do\". */\n readonly fix: string;\n /** Slug of the skill that explains the fix (`packages/<pkg>/skills/<slug>/SKILL.md`). */\n readonly skill: string;\n /** Names of the error classes that carry this code, when an error class does. */\n readonly errorNames?: readonly string[];\n /** Set when the code is no longer emitted. Retired codes are never reused. */\n readonly retired?: true;\n};\n\n/** Base URL of the error-code reference; each code is an anchor on that page. */\nexport const ERROR_CODES_DOCS_URL = \"https://warlock.js.org/v/latest/core/cli/error-codes/\";\n\n/** The registry, keyed by code. */\nexport const ERROR_CODES = {\n // ── WL1xxx config ────────────────────────────────────────────────────────\n WL1001: {\n code: \"WL1001\",\n area: \"config\",\n category: \"code\",\n title: \"A required config section is missing\",\n fix: \"Add the missing file under src/config/ (for example src/config/app.ts) exporting the section.\",\n skill: \"configure-app\",\n },\n WL1002: {\n code: \"WL1002\",\n area: \"config\",\n category: \"code\",\n title: \"No JWT signing secret is configured\",\n fix: \"Run `warlock jwt.generate` (writes JWT_SECRET to .env) and read it as auth.accessToken.secret in src/config/auth.ts.\",\n skill: \"configure-app\",\n },\n WL1003: {\n code: \"WL1003\",\n area: \"config\",\n category: \"code\",\n title: \"The rate-limit store cannot use the configured cache driver\",\n fix: 'Use a redis, pg or memory cache driver for http.rateLimit store \"cache\", or set store: \"memory\".',\n skill: \"configure-app\",\n errorNames: [\"UnsupportedRateLimitDriverError\"],\n },\n WL1004: {\n code: \"WL1004\",\n area: \"config\",\n category: \"code\",\n title: \"An http.csp directive value is invalid\",\n fix: \"Fix the directive value named in the message in your http config; quotes must balance and values may not contain ';'.\",\n skill: \"configure-app\",\n errorNames: [\"InvalidCspDirectiveError\"],\n },\n WL1005: {\n code: \"WL1005\",\n area: \"config\",\n category: \"code\",\n title: \"The uploads.images configuration is invalid\",\n fix: \"Correct the uploads.images variant settings named in the message.\",\n skill: \"upload-file\",\n errorNames: [\"ImageVariantsConfigError\"],\n },\n WL1006: {\n code: \"WL1006\",\n area: \"config\",\n category: \"code\",\n title: \"warlock.config.ts sets a build option the builder refuses\",\n fix: \"Remove the named option from the build block of warlock.config.ts; change tsconfig.json instead.\",\n skill: \"run-app\",\n errorNames: [\"ForbiddenBuildOptionError\"],\n },\n WL1007: {\n code: \"WL1007\",\n area: \"config\",\n category: \"code\",\n title: \"A locale dictionary file cannot be parsed\",\n fix: \"Fix the key named in the message in the locales.json file it names.\",\n skill: \"use-localization\",\n errorNames: [\"LocaleDictionaryError\"],\n },\n WL1008: {\n code: \"WL1008\",\n area: \"config\",\n category: \"code\",\n title: \"A locale outside app.localeCodes was requested\",\n fix: \"Add the locale to app.localeCodes in src/config/app.ts, or pass a configured locale.\",\n skill: \"use-localization\",\n errorNames: [\"UnknownLocaleError\"],\n },\n WL1009: {\n code: \"WL1009\",\n area: \"config\",\n category: \"code\",\n title: \"warlock.config.ts > connectors was refused\",\n fix: \"Give every connector in warlock.config.ts a unique name that is not a built-in connector name.\",\n skill: \"add-connector\",\n },\n\n // ── WL2xxx boot ──────────────────────────────────────────────────────────\n WL2001: {\n code: \"WL2001\",\n area: \"boot\",\n category: \"code\",\n title: \"An app module failed to load\",\n fix: \"Open the file named in the message and fix the error it throws on import; its routes and listeners are missing until then.\",\n skill: \"warlock-doctor\",\n errorNames: [\"ModuleLoadError\"],\n },\n WL2002: {\n code: \"WL2002\",\n area: \"boot\",\n category: \"code\",\n title: \"The app's source files could not be discovered\",\n fix: \"Run the command from the project root and check that src/ exists and is readable.\",\n skill: \"warlock-doctor\",\n },\n WL2003: {\n code: \"WL2003\",\n area: \"boot\",\n category: \"code\",\n title: \"A connector failed to boot\",\n fix: \"Read the connector's message; it usually names the config value or package it needs.\",\n skill: \"add-connector\",\n },\n WL2004: {\n code: \"WL2004\",\n area: \"boot\",\n category: \"code\",\n title: \"A container key was read before it was registered\",\n fix: \"Register the key (container.set) before reading it, or read it after the connector that sets it has booted.\",\n skill: \"use-app-context\",\n errorNames: [\"ContainerKeyMissingError\"],\n },\n WL2005: {\n code: \"WL2005\",\n area: \"boot\",\n category: \"code\",\n title: \"A boot precondition failed\",\n fix: \"Fix the condition named in the message; restarting does not help until it changes.\",\n skill: \"run-app\",\n errorNames: [\"BootPreconditionError\"],\n },\n WL2006: {\n code: \"WL2006\",\n area: \"boot\",\n category: \"code\",\n title: \"Storage was used before the storage connector ran\",\n fix: 'Add src/config/storage.ts; in a custom command add \"storage\" to preload.connectors.',\n skill: \"store-file\",\n errorNames: [\"StorageNotInitializedError\"],\n },\n WL2007: {\n code: \"WL2007\",\n area: \"boot\",\n category: \"code\",\n title: \"A configured driver's package is not installed\",\n fix: \"Install the package named in the message (for example `pnpm add sharp`), or stop configuring the driver.\",\n skill: \"update-packages\",\n },\n\n // ── WL3xxx routing ───────────────────────────────────────────────────────\n WL3001: {\n code: \"WL3001\",\n area: \"routing\",\n category: \"code\",\n title: \"A route name is already taken\",\n fix: 'Rename one of the two routes with the \"name\" route option; pages and API routes share one namespace.',\n skill: \"register-route\",\n errorNames: [\"RouteNameTakenError\"],\n },\n WL3002: {\n code: \"WL3002\",\n area: \"routing\",\n category: \"code\",\n title: \"A route points at a controller method that does not exist\",\n fix: \"Fix the [controller, action] pair: the action must be a method on the controller.\",\n skill: \"register-route\",\n },\n WL3003: {\n code: \"WL3003\",\n area: \"routing\",\n category: \"code\",\n title: \"A URL was built for a route name that is not registered\",\n fix: \"Check the name passed to router.route(); `warlock routes --name <part>` lists the registered names.\",\n skill: \"warlock-routes\",\n },\n WL3004: {\n code: \"WL3004\",\n area: \"routing\",\n category: \"code\",\n title: \"A URL was built without one of the route's path parameters\",\n fix: \"Pass every :param of the route path when building the URL.\",\n skill: \"warlock-routes\",\n },\n WL3005: {\n code: \"WL3005\",\n area: \"routing\",\n category: \"code\",\n title: \"Route modules loaded but registered no routes\",\n fix: \"Make sure routes.ts calls router.get/post/... at module level and is not wrapped in an unused function.\",\n skill: \"register-route\",\n },\n WL3006: {\n code: \"WL3006\",\n area: \"routing\",\n category: \"code\",\n title: \"A handler uses the v4 (request, response) signature\",\n fix: \"Rewrite the handler as async ({ request, response }) => { ... }.\",\n skill: \"create-controller\",\n },\n\n // ── WL4xxx validation ────────────────────────────────────────────────────\n WL4001: {\n code: \"WL4001\",\n area: \"validation\",\n category: \"code\",\n title: \"A validation schema cannot be described as JSON Schema\",\n fix: \"Use Seal validators with a static shape for validation.schema/params; custom validate middleware is not described.\",\n skill: \"validate-input\",\n },\n\n // ── WL5xxx http ──────────────────────────────────────────────────────────\n WL5001: {\n code: \"WL5001\",\n area: \"http\",\n category: \"code\",\n title: \"A cookie was read but the cookie plugin is not registered\",\n fix: \"Register @fastify/cookie on the server before reading cookies by name.\",\n skill: \"send-response\",\n errorNames: [\"CookieJarUnavailableError\"],\n },\n WL5002: {\n code: \"WL5002\",\n area: \"http\",\n category: \"code\",\n title: \"An app route claims a health-probe path\",\n fix: \"Move the app route, or rename the probe with http.health.path / http.health.readinessPath.\",\n skill: \"health-checks\",\n },\n WL5003: {\n code: \"WL5003\",\n area: \"http\",\n category: \"code\",\n title: \"Health probes are enabled but not registered\",\n fix: \"Check that the http connector booted and that nothing removed the probe routes.\",\n skill: \"health-checks\",\n },\n WL5004: {\n code: \"WL5004\",\n area: \"http\",\n category: \"code\",\n title: \"Devtools recorded no timeline for the traced request\",\n fix: \"Keep http.requestId enabled (its echoed header is how the request is found) and retry; a path under the devtools prefix is never recorded.\",\n skill: \"warlock-trace\",\n },\n\n // ── WL6xxx database ──────────────────────────────────────────────────────\n WL6001: {\n code: \"WL6001\",\n area: \"database\",\n category: \"code\",\n title: \"A seeder depends on a seeder that is not registered\",\n fix: \"Fix the dependsOn name, or register the missing seeder.\",\n skill: \"write-seeder\",\n errorNames: [\"UnknownSeederDependencyError\"],\n },\n WL6002: {\n code: \"WL6002\",\n area: \"database\",\n category: \"code\",\n title: \"Seeder dependencies form a cycle\",\n fix: \"Remove one dependsOn edge from the cycle printed in the message.\",\n skill: \"write-seeder\",\n errorNames: [\"SeederDependencyCycleError\"],\n },\n\n // ── WL7xxx cli and checked code ──────────────────────────────────────────\n WL7001: {\n code: \"WL7001\",\n area: \"cli\",\n category: \"code\",\n title: \"A boolean CLI option received a value it cannot read\",\n fix: \"Pass true/false/1/0/yes/no, or the flag on its own for true.\",\n skill: \"write-cli-command\",\n errorNames: [\"CliOptionValueError\"],\n },\n WL7002: {\n code: \"WL7002\",\n area: \"cli\",\n category: \"code\",\n title: \"Generated code imports a package the app does not declare\",\n fix: \"Add the named package to the app's dependencies, or import it through @warlock.js/core.\",\n skill: \"run-app\",\n errorNames: [\"UndeclaredGeneratedImportError\"],\n },\n WL7003: {\n code: \"WL7003\",\n area: \"cli\",\n category: \"code\",\n title: \"A command needs an optional package that is not installed\",\n fix: \"Install the packages named in the message as dev dependencies (warlock mcp needs @warlock.js/ai-tools and @warlock.js/ai).\",\n skill: \"warlock-mcp\",\n },\n WL7004: {\n code: \"WL7004\",\n area: \"cli\",\n category: \"code\",\n title: \"warlock mcp was asked to listen off loopback without a token\",\n fix: \"Pass --token (or set WARLOCK_MCP_TOKEN) when --host is not 127.0.0.1, ::1 or localhost, or keep the default loopback host.\",\n skill: \"warlock-mcp\",\n },\n WL7005: {\n code: \"WL7005\",\n area: \"cli\",\n category: \"code\",\n title: \"An MCP tool that writes files was called on a read-only server\",\n fix: \"Restart the server with --allow-writes to let generate tools write; without it they only return the diff.\",\n skill: \"warlock-mcp\",\n },\n WL7101: {\n code: \"WL7101\",\n area: \"cli\",\n category: \"code\",\n title: \"TypeScript reports a type error\",\n fix: \"Fix the type error at the reported position; the TypeScript code (TS1234) is in `rule`.\",\n skill: \"warlock-check\",\n },\n WL7102: {\n code: \"WL7102\",\n area: \"cli\",\n category: \"code\",\n title: \"tsconfig.json is missing or invalid\",\n fix: \"Fix tsconfig.json at the project root; the message names the problem.\",\n skill: \"warlock-check\",\n },\n WL7201: {\n code: \"WL7201\",\n area: \"cli\",\n category: \"code\",\n title: \"The linter reports an error\",\n fix: \"Fix the lint error at the reported position; the rule id is in `rule`.\",\n skill: \"warlock-check\",\n },\n WL7202: {\n code: \"WL7202\",\n area: \"cli\",\n category: \"code\",\n title: \"The linter reports a warning\",\n fix: \"Fix the warning, or change the rule's severity in the lint config if it does not apply.\",\n skill: \"warlock-check\",\n },\n WL7301: {\n code: \"WL7301\",\n area: \"cli\",\n category: \"code\",\n title: \"A test failed\",\n fix: \"Run the test file alone, read the assertion message and fix the code or the test.\",\n skill: \"warlock-check\",\n },\n WL7302: {\n code: \"WL7302\",\n area: \"cli\",\n category: \"code\",\n title: \"A test file failed before its tests ran\",\n fix: \"Fix the import or setup error named in the message; none of the file's tests ran.\",\n skill: \"warlock-check\",\n },\n WL7401: {\n code: \"WL7401\",\n area: \"cli\",\n category: \"code\",\n title: \"The agent board is not initialized\",\n fix: \"Run `warlock board init` at the project root first.\",\n skill: \"warlock-board\",\n },\n WL7402: {\n code: \"WL7402\",\n area: \"cli\",\n category: \"code\",\n title: \"A board item does not exist\",\n fix: \"Run `warlock board status` to list the item ids.\",\n skill: \"warlock-board\",\n },\n WL7403: {\n code: \"WL7403\",\n area: \"cli\",\n category: \"code\",\n title: \"A board item cannot move to the requested state\",\n fix: \"Use one of the next states listed in the message; `warlock board status <id>` shows the item's current state.\",\n skill: \"warlock-board\",\n },\n WL7404: {\n code: \"WL7404\",\n area: \"cli\",\n category: \"code\",\n title: \"No board message arrived before the watch timed out\",\n fix: \"Run `warlock board watch` again, or raise --timeout.\",\n skill: \"warlock-board\",\n },\n WL7501: {\n code: \"WL7501\",\n area: \"cli\",\n category: \"code\",\n title: \"AGENTS.md has malformed generated-section markers\",\n fix: \"Keep one `<!-- warlock:generated:start -->` line followed by one `<!-- warlock:generated:end -->` line, or remove both.\",\n skill: \"warlock-agents-md\",\n },\n\n // ── WL8xxx environment ───────────────────────────────────────────────────\n WL8001: {\n code: \"WL8001\",\n area: \"environment\",\n category: \"environment\",\n title: \"The Node.js version is not supported\",\n fix: \"Switch to a Node.js version that satisfies the engines range in the message (for example with nvm).\",\n skill: \"update-packages\",\n },\n WL8002: {\n code: \"WL8002\",\n area: \"environment\",\n category: \"environment\",\n title: \"A tool the project is configured to use is not installed\",\n fix: \"Install the tool named in the message (pnpm install), or remove its config file.\",\n skill: \"warlock-check\",\n },\n WL8003: {\n code: \"WL8003\",\n area: \"environment\",\n category: \"environment\",\n title: \"A service the code needs is unreachable\",\n fix: \"Start the service named in the message (database, Redis, ...) or point the config at a running one.\",\n skill: \"warlock-check\",\n },\n WL8004: {\n code: \"WL8004\",\n area: \"environment\",\n category: \"environment\",\n title: \"A port is already in use\",\n fix: \"Stop whatever listens on the port, or use another port (port 0 in tests).\",\n skill: \"run-app\",\n errorNames: [\"PortInUseError\"],\n },\n WL8005: {\n code: \"WL8005\",\n area: \"environment\",\n category: \"environment\",\n title: \"esbuild's native binary is not installed\",\n fix: \"Run `pnpm approve-builds`, then reinstall dependencies without skipping install scripts.\",\n skill: \"run-app\",\n errorNames: [\"EsbuildBinaryMissingError\"],\n },\n WL8006: {\n code: \"WL8006\",\n area: \"environment\",\n category: \"environment\",\n title: \"Git cannot list the changed files\",\n fix: \"Run inside a git repository with git on PATH, or run the command without --changed.\",\n skill: \"warlock-check\",\n },\n WL8007: {\n code: \"WL8007\",\n area: \"environment\",\n category: \"environment\",\n title: \"A tool crashed, timed out or ran out of memory\",\n fix: \"Re-run the step; if it repeats, raise the memory limit (NODE_OPTIONS=--max-old-space-size) or use --changed.\",\n skill: \"warlock-check\",\n },\n WL8008: {\n code: \"WL8008\",\n area: \"environment\",\n category: \"environment\",\n title: \"The dev server is not running\",\n fix: \"Start `warlock dev` in this project, or pass --url with the address of a running dev server.\",\n skill: \"warlock-trace\",\n },\n WL8009: {\n code: \"WL8009\",\n area: \"environment\",\n category: \"environment\",\n title: \"Devtools is not running in the dev server\",\n fix: \"Install it with `warlock add devtools` and restart `warlock dev`; devtools answers loopback requests in development only.\",\n skill: \"warlock-trace\",\n },\n\n // ── WL9xxx conventions and hygiene ───────────────────────────────────────\n WL9001: {\n code: \"WL9001\",\n area: \"convention\",\n category: \"code\",\n title: \"An auto-loaded file is imported\",\n fix: \"Remove the import: routes.ts, main.ts, worker.ts and events/ files are loaded by the framework.\",\n skill: \"warlock-conventions\",\n },\n WL9002: {\n code: \"WL9002\",\n area: \"convention\",\n category: \"code\",\n title: \"An import path ends in .js\",\n fix: \"Drop the .js extension from the import path.\",\n skill: \"warlock-conventions\",\n },\n WL9003: {\n code: \"WL9003\",\n area: \"convention\",\n category: \"code\",\n title: \"A relative import reaches into another module\",\n fix: \"Import across modules through the app/<module>/... alias instead of ../../.\",\n skill: \"warlock-conventions\",\n },\n WL9004: {\n code: \"WL9004\",\n area: \"convention\",\n category: \"code\",\n title: \"tsconfig.json enables legacy experimentalDecorators\",\n fix: 'Remove \"experimentalDecorators\": true from tsconfig.json; Cascade uses standard decorators.',\n skill: \"lower-stage3-decorators\",\n },\n WL9005: {\n code: \"WL9005\",\n area: \"convention\",\n category: \"code\",\n title: \"`request.all()` is passed to a query, a service or inApp()\",\n fix: \"Give the route a validation schema and pass `request.validated()`, which holds only the fields the schema accepted (mass assignment otherwise).\",\n skill: \"warlock-conventions\",\n },\n WL9006: {\n code: \"WL9006\",\n area: \"convention\",\n category: \"code\",\n title: \"A mutating route has no auth guard and is not marked public\",\n fix: \"Register the POST/PUT/PATCH/DELETE route inside `guarded()` (or with auth middleware), or pass `{ public: true }` if it is meant to be open.\",\n skill: \"warlock-conventions\",\n },\n WL9007: {\n code: \"WL9007\",\n area: \"convention\",\n category: \"code\",\n title: \"A redirect or link target comes from request input\",\n fix: \"Wrap the target in `safeRedirect()` (server, @warlock.js/core) or `safeRedirectTarget()` (pages, @warlock.js/web), or check it with `isSafeRedirect()` first.\",\n skill: \"warlock-conventions\",\n },\n WL9008: {\n code: \"WL9008\",\n area: \"convention\",\n category: \"code\",\n title: \"A raw email, credential or request body is logged\",\n fix: \"Pass the value through `maskEmail()`, `redact()`, `escapeLogValue()` or `request.toLogSnapshot()`, or log it under a key the logger censors (`token`, `password`, ...).\",\n skill: \"warlock-conventions\",\n },\n WL9009: {\n code: \"WL9009\",\n area: \"convention\",\n category: \"code\",\n title: \"A regex with an unbounded quantifier runs on request input\",\n fix: \"Bound the repetition (`{1,256}` instead of `+` or `*`) or cap the input length before matching.\",\n skill: \"warlock-conventions\",\n },\n WL9010: {\n code: \"WL9010\",\n area: \"convention\",\n category: \"code\",\n title: \"`await` on a synchronous log call\",\n fix: \"Remove the `await`: `log.info()` and its siblings return the logger; `await log.flush()` waits for delivery.\",\n skill: \"warlock-conventions\",\n },\n WL9011: {\n code: \"WL9011\",\n area: \"convention\",\n category: \"code\",\n title: \"A file defines more than one controller\",\n fix: \"Move each extra controller into its own `<name>.controller.ts` file.\",\n skill: \"warlock-conventions\",\n },\n WL9012: {\n code: \"WL9012\",\n area: \"convention\",\n category: \"code\",\n title: \"A tagged union uses `kind` as its discriminator\",\n fix: \"Rename the discriminator to `type` and update the code that narrows on it.\",\n skill: \"warlock-conventions\",\n },\n WL9101: {\n code: \"WL9101\",\n area: \"convention\",\n category: \"code\",\n title: \"A module still uses the deprecated utils/locales.ts\",\n fix: \"Run `warlock doctor --fix` to convert it to locales.json, or convert it by hand.\",\n skill: \"use-localization\",\n },\n WL9102: {\n code: \"WL9102\",\n area: \"convention\",\n category: \"code\",\n title: \"package.json version and CHANGELOG.md disagree\",\n fix: \"Add a CHANGELOG.md heading for the package.json version, or fix the version.\",\n skill: \"warlock-doctor\",\n },\n WL9103: {\n code: \"WL9103\",\n area: \"convention\",\n category: \"code\",\n title: \"CHANGELOG.md is missing or has no version heading\",\n fix: \"Add a CHANGELOG.md with a `## x.y.z` heading for the current version.\",\n skill: \"warlock-doctor\",\n },\n} as const satisfies Record<string, ErrorCodeDefinition>;\n\n/** Every registered code, as a union. */\nexport type WarlockErrorCode = keyof typeof ERROR_CODES;\n\n/** Whether `value` is a registered code. */\nexport function isWarlockErrorCode(value: unknown): value is WarlockErrorCode {\n return typeof value === \"string\" && Object.prototype.hasOwnProperty.call(ERROR_CODES, value);\n}\n\n/**\n * The registry entry for a code, or `undefined` for an unknown one. Case and\n * surrounding whitespace are ignored, so `explainErrorCode(\" wl3001 \")` works.\n */\nexport function explainErrorCode(code: string): ErrorCodeDefinition | undefined {\n const normalized = code.trim().toUpperCase();\n\n return isWarlockErrorCode(normalized) ? ERROR_CODES[normalized] : undefined;\n}\n\n/** The docs URL of a code: the reference page with the code as its anchor. */\nexport function errorCodeDocsUrl(code: WarlockErrorCode): string {\n return `${ERROR_CODES_DOCS_URL}#${code.toLowerCase()}`;\n}\n\n/** Node errno codes that mean \"the machine\", mapped to the Warlock code. */\nconst SYSTEM_ERROR_CODES: Readonly<Record<string, WarlockErrorCode>> = {\n ECONNREFUSED: \"WL8003\",\n ENOTFOUND: \"WL8003\",\n EHOSTUNREACH: \"WL8003\",\n ETIMEDOUT: \"WL8003\",\n EADDRINUSE: \"WL8004\",\n ERR_OUT_OF_MEMORY: \"WL8007\",\n};\n\nfunction readStringProperty(value: object, key: string): string | undefined {\n const property = Reflect.get(value, key);\n\n return typeof property === \"string\" ? property : undefined;\n}\n\n/**\n * The Warlock code of a thrown value, or `undefined` when it has none.\n *\n * Reads, in order: a registered `code` property (framework errors carry one);\n * the error's `name`, for classes whose `code` property already means\n * something else (`PortInUseError.code` is Node's `\"EADDRINUSE\"`); a Node\n * system errno such as `ECONNREFUSED`; then the same checks on `cause`. Reading\n * by name rather than `instanceof` keeps the answer right when two copies of\n * core are loaded.\n */\nexport function errorCodeOf(error: unknown): WarlockErrorCode | undefined {\n const seen = new Set<unknown>();\n let current: unknown = error;\n\n while (typeof current === \"object\" && current !== null && !seen.has(current)) {\n seen.add(current);\n\n const code = readStringProperty(current, \"code\");\n\n if (isWarlockErrorCode(code)) {\n return code;\n }\n\n const name = readStringProperty(current, \"name\");\n\n if (name) {\n for (const definition of Object.values(ERROR_CODES) as ErrorCodeDefinition[]) {\n if (definition.errorNames?.includes(name) && isWarlockErrorCode(definition.code)) {\n return definition.code;\n }\n }\n }\n\n if (code && SYSTEM_ERROR_CODES[code]) {\n return SYSTEM_ERROR_CODES[code];\n }\n\n current = Reflect.get(current, \"cause\");\n }\n\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"error-codes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/errors/error-codes.ts"],"sourcesContent":["/**\n * The registry of stable Warlock error codes.\n *\n * Every framework error core throws, every `warlock doctor` finding and every\n * `warlock check` problem carries one of these codes, so a person or an agent\n * can look the code up instead of interpreting the message. `warlock explain\n * <CODE>` prints an entry; the docs page `cli/error-codes` lists all of them.\n *\n * THE SCHEME: `WL` followed by four digits. The first digit is the area:\n *\n * | Range | Area | Meaning |\n * | ------ | ----------- | ---------------------------------------------------- |\n * | WL1xxx | config | a configuration value is missing or invalid |\n * | WL2xxx | boot | the app could not boot: modules, connectors, services |\n * | WL3xxx | routing | route registration and lookup |\n * | WL4xxx | validation | request validation schemas |\n * | WL5xxx | http | the HTTP layer at runtime |\n * | WL6xxx | database | migrations, seeders and database code |\n * | WL7xxx | cli | CLI usage, and problems `warlock check` finds in code |\n * | WL8xxx | environment | this machine: Node, tools, services, ports |\n * | WL9xxx | convention | framework conventions and release hygiene |\n *\n * STABILITY: a code, once released, keeps its meaning forever. Its `title`,\n * `fix` and links may be reworded; a code is never reused for a different\n * problem and never renumbered. A retired code stays in this table with\n * `retired: true`.\n *\n * `category` splits \"your code\" from \"this machine\": `environment` problems are\n * the ones `warlock check` reports with exit code 2, so an agent stops editing\n * code to fix a missing Redis server.\n *\n * This module is part of the side-effect-free `@warlock.js/core/errors` entry:\n * it imports nothing.\n */\n\n/** The area a code belongs to, matching its first digit. */\nexport type ErrorCodeArea =\n | \"config\"\n | \"boot\"\n | \"routing\"\n | \"validation\"\n | \"http\"\n | \"database\"\n | \"cli\"\n | \"environment\"\n | \"convention\";\n\n/**\n * Who has to act: `code` problems are fixed in the project's source or config,\n * `environment` problems on the machine running it.\n */\nexport type ErrorCodeCategory = \"code\" | \"environment\";\n\n/** One registry entry. */\nexport type ErrorCodeDefinition = {\n /** The stable code, e.g. `\"WL3001\"`. */\n readonly code: string;\n /** The area, matching the first digit. */\n readonly area: ErrorCodeArea;\n /** Whether the project or the machine has to change. */\n readonly category: ErrorCodeCategory;\n /** One-line name of the problem. */\n readonly title: string;\n /** One-line \"what to do\". */\n readonly fix: string;\n /** Slug of the skill that explains the fix (`packages/<pkg>/skills/<slug>/SKILL.md`). */\n readonly skill: string;\n /** Names of the error classes that carry this code, when an error class does. */\n readonly errorNames?: readonly string[];\n /** Set when the code is no longer emitted. Retired codes are never reused. */\n readonly retired?: true;\n};\n\n/** Base URL of the error-code reference; each code is an anchor on that page. */\nexport const ERROR_CODES_DOCS_URL = \"https://warlock.js.org/v/latest/core/cli/error-codes/\";\n\n/** The registry, keyed by code. */\nexport const ERROR_CODES = {\n // ── WL1xxx config ────────────────────────────────────────────────────────\n WL1001: {\n code: \"WL1001\",\n area: \"config\",\n category: \"code\",\n title: \"A required config section is missing\",\n fix: \"Add the missing file under src/config/ (for example src/config/app.ts) exporting the section.\",\n skill: \"configure-app\",\n },\n WL1002: {\n code: \"WL1002\",\n area: \"config\",\n category: \"code\",\n title: \"No JWT signing secret is configured\",\n fix: \"Run `warlock jwt.generate` (writes JWT_SECRET to .env) and read it as auth.accessToken.secret in src/config/auth.ts.\",\n skill: \"configure-app\",\n },\n WL1003: {\n code: \"WL1003\",\n area: \"config\",\n category: \"code\",\n title: \"The rate-limit store cannot use the configured cache driver\",\n fix: 'Use a redis, pg or memory cache driver for http.rateLimit store \"cache\", or set store: \"memory\".',\n skill: \"configure-app\",\n errorNames: [\"UnsupportedRateLimitDriverError\"],\n },\n WL1004: {\n code: \"WL1004\",\n area: \"config\",\n category: \"code\",\n title: \"An http.csp directive value is invalid\",\n fix: \"Fix the directive value named in the message in your http config; quotes must balance and values may not contain ';'.\",\n skill: \"configure-app\",\n errorNames: [\"InvalidCspDirectiveError\"],\n },\n WL1005: {\n code: \"WL1005\",\n area: \"config\",\n category: \"code\",\n title: \"The uploads.images configuration is invalid\",\n fix: \"Correct the uploads.images variant settings named in the message.\",\n skill: \"upload-file\",\n errorNames: [\"ImageVariantsConfigError\"],\n },\n WL1006: {\n code: \"WL1006\",\n area: \"config\",\n category: \"code\",\n title: \"warlock.config.ts sets a build option the builder refuses\",\n fix: \"Remove the named option from the build block of warlock.config.ts; change tsconfig.json instead.\",\n skill: \"run-app\",\n errorNames: [\"ForbiddenBuildOptionError\"],\n },\n WL1007: {\n code: \"WL1007\",\n area: \"config\",\n category: \"code\",\n title: \"A locale dictionary file cannot be parsed\",\n fix: \"Fix the key named in the message in the locales.json file it names.\",\n skill: \"use-localization\",\n errorNames: [\"LocaleDictionaryError\"],\n },\n WL1008: {\n code: \"WL1008\",\n area: \"config\",\n category: \"code\",\n title: \"A locale outside app.localeCodes was requested\",\n fix: \"Add the locale to app.localeCodes in src/config/app.ts, or pass a configured locale.\",\n skill: \"use-localization\",\n errorNames: [\"UnknownLocaleError\"],\n },\n WL1009: {\n code: \"WL1009\",\n area: \"config\",\n category: \"code\",\n title: \"warlock.config.ts > connectors was refused\",\n fix: \"Give every connector in warlock.config.ts a unique name that is not a built-in connector name.\",\n skill: \"add-connector\",\n },\n\n // ── WL2xxx boot ──────────────────────────────────────────────────────────\n WL2001: {\n code: \"WL2001\",\n area: \"boot\",\n category: \"code\",\n title: \"An app module failed to load\",\n fix: \"Open the file named in the message and fix the error it throws on import; its routes and listeners are missing until then.\",\n skill: \"warlock-doctor\",\n errorNames: [\"ModuleLoadError\"],\n },\n WL2002: {\n code: \"WL2002\",\n area: \"boot\",\n category: \"code\",\n title: \"The app's source files could not be discovered\",\n fix: \"Run the command from the project root and check that src/ exists and is readable.\",\n skill: \"warlock-doctor\",\n },\n WL2003: {\n code: \"WL2003\",\n area: \"boot\",\n category: \"code\",\n title: \"A connector failed to boot\",\n fix: \"Read the connector's message; it usually names the config value or package it needs.\",\n skill: \"add-connector\",\n },\n WL2004: {\n code: \"WL2004\",\n area: \"boot\",\n category: \"code\",\n title: \"A container key was read before it was registered\",\n fix: \"Register the key (container.set) before reading it, or read it after the connector that sets it has booted.\",\n skill: \"use-app-context\",\n errorNames: [\"ContainerKeyMissingError\"],\n },\n WL2005: {\n code: \"WL2005\",\n area: \"boot\",\n category: \"code\",\n title: \"A boot precondition failed\",\n fix: \"Fix the condition named in the message; restarting does not help until it changes.\",\n skill: \"run-app\",\n errorNames: [\"BootPreconditionError\"],\n },\n WL2006: {\n code: \"WL2006\",\n area: \"boot\",\n category: \"code\",\n title: \"Storage was used before the storage connector ran\",\n fix: 'Add src/config/storage.ts; in a custom command add \"storage\" to preload.connectors.',\n skill: \"store-file\",\n errorNames: [\"StorageNotInitializedError\"],\n },\n WL2007: {\n code: \"WL2007\",\n area: \"boot\",\n category: \"code\",\n title: \"A configured driver's package is not installed\",\n fix: \"Install the package named in the message (for example `pnpm add sharp`), or stop configuring the driver.\",\n skill: \"update-packages\",\n },\n\n // ── WL3xxx routing ───────────────────────────────────────────────────────\n WL3001: {\n code: \"WL3001\",\n area: \"routing\",\n category: \"code\",\n title: \"A route name is already taken\",\n fix: 'Rename one of the two routes with the \"name\" route option; pages and API routes share one namespace.',\n skill: \"register-route\",\n errorNames: [\"RouteNameTakenError\"],\n },\n WL3002: {\n code: \"WL3002\",\n area: \"routing\",\n category: \"code\",\n title: \"A route points at a controller method that does not exist\",\n fix: \"Fix the [controller, action] pair: the action must be a method on the controller.\",\n skill: \"register-route\",\n },\n WL3003: {\n code: \"WL3003\",\n area: \"routing\",\n category: \"code\",\n title: \"A URL was built for a route name that is not registered\",\n fix: \"Check the name passed to router.route(); `warlock routes --name <part>` lists the registered names.\",\n skill: \"warlock-routes\",\n },\n WL3004: {\n code: \"WL3004\",\n area: \"routing\",\n category: \"code\",\n title: \"A URL was built without one of the route's path parameters\",\n fix: \"Pass every :param of the route path when building the URL.\",\n skill: \"warlock-routes\",\n },\n WL3005: {\n code: \"WL3005\",\n area: \"routing\",\n category: \"code\",\n title: \"Route modules loaded but registered no routes\",\n fix: \"Make sure routes.ts calls router.get/post/... at module level and is not wrapped in an unused function.\",\n skill: \"register-route\",\n },\n WL3006: {\n code: \"WL3006\",\n area: \"routing\",\n category: \"code\",\n title: \"A handler uses the v4 (request, response) signature\",\n fix: \"Rewrite the handler as async ({ request, response }) => { ... }.\",\n skill: \"create-controller\",\n },\n\n // ── WL4xxx validation ────────────────────────────────────────────────────\n WL4001: {\n code: \"WL4001\",\n area: \"validation\",\n category: \"code\",\n title: \"A validation schema cannot be described as JSON Schema\",\n fix: \"Use Seal validators with a static shape for validation.schema/params; custom validate middleware is not described.\",\n skill: \"validate-input\",\n },\n\n // ── WL5xxx http ──────────────────────────────────────────────────────────\n WL5001: {\n code: \"WL5001\",\n area: \"http\",\n category: \"code\",\n title: \"A cookie was read but the cookie plugin is not registered\",\n fix: \"Register @fastify/cookie on the server before reading cookies by name.\",\n skill: \"send-response\",\n errorNames: [\"CookieJarUnavailableError\"],\n },\n WL5002: {\n code: \"WL5002\",\n area: \"http\",\n category: \"code\",\n title: \"An app route claims a health-probe path\",\n fix: \"Move the app route, or rename the probe with http.health.path / http.health.readinessPath.\",\n skill: \"health-checks\",\n },\n WL5003: {\n code: \"WL5003\",\n area: \"http\",\n category: \"code\",\n title: \"Health probes are enabled but not registered\",\n fix: \"Check that the http connector booted and that nothing removed the probe routes.\",\n skill: \"health-checks\",\n },\n WL5004: {\n code: \"WL5004\",\n area: \"http\",\n category: \"code\",\n title: \"Devtools recorded no timeline for the traced request\",\n fix: \"Keep http.requestId enabled (its echoed header is how the request is found) and retry; a path under the devtools prefix is never recorded.\",\n skill: \"warlock-trace\",\n },\n\n // ── WL6xxx database ──────────────────────────────────────────────────────\n WL6001: {\n code: \"WL6001\",\n area: \"database\",\n category: \"code\",\n title: \"A seeder depends on a seeder that is not registered\",\n fix: \"Fix the dependsOn name, or register the missing seeder.\",\n skill: \"write-seeder\",\n errorNames: [\"UnknownSeederDependencyError\"],\n },\n WL6002: {\n code: \"WL6002\",\n area: \"database\",\n category: \"code\",\n title: \"Seeder dependencies form a cycle\",\n fix: \"Remove one dependsOn edge from the cycle printed in the message.\",\n skill: \"write-seeder\",\n errorNames: [\"SeederDependencyCycleError\"],\n },\n\n // ── WL7xxx cli and checked code ──────────────────────────────────────────\n WL7001: {\n code: \"WL7001\",\n area: \"cli\",\n category: \"code\",\n title: \"A boolean CLI option received a value it cannot read\",\n fix: \"Pass true/false/1/0/yes/no, or the flag on its own for true.\",\n skill: \"write-cli-command\",\n errorNames: [\"CliOptionValueError\"],\n },\n WL7002: {\n code: \"WL7002\",\n area: \"cli\",\n category: \"code\",\n title: \"Generated code imports a package the app does not declare\",\n fix: \"Add the named package to the app's dependencies, or import it through @warlock.js/core.\",\n skill: \"run-app\",\n errorNames: [\"UndeclaredGeneratedImportError\"],\n },\n WL7003: {\n code: \"WL7003\",\n area: \"cli\",\n category: \"code\",\n title: \"A command needs an optional package that is not installed\",\n fix: \"Install the packages named in the message as dev dependencies (warlock mcp needs @warlock.js/ai-tools and @warlock.js/ai).\",\n skill: \"warlock-mcp\",\n },\n WL7004: {\n code: \"WL7004\",\n area: \"cli\",\n category: \"code\",\n title: \"warlock mcp was asked to listen off loopback without a token\",\n fix: \"Pass --token (or set WARLOCK_MCP_TOKEN) when --host is not 127.0.0.1, ::1 or localhost, or keep the default loopback host.\",\n skill: \"warlock-mcp\",\n },\n WL7005: {\n code: \"WL7005\",\n area: \"cli\",\n category: \"code\",\n title: \"An MCP tool that writes files was called on a read-only server\",\n fix: \"Restart the server with --allow-writes to let generate tools write; without it they only return the diff.\",\n skill: \"warlock-mcp\",\n },\n WL7101: {\n code: \"WL7101\",\n area: \"cli\",\n category: \"code\",\n title: \"TypeScript reports a type error\",\n fix: \"Fix the type error at the reported position; the TypeScript code (TS1234) is in `rule`.\",\n skill: \"warlock-check\",\n },\n WL7102: {\n code: \"WL7102\",\n area: \"cli\",\n category: \"code\",\n title: \"tsconfig.json is missing or invalid\",\n fix: \"Fix tsconfig.json at the project root; the message names the problem.\",\n skill: \"warlock-check\",\n },\n WL7201: {\n code: \"WL7201\",\n area: \"cli\",\n category: \"code\",\n title: \"The linter reports an error\",\n fix: \"Fix the lint error at the reported position; the rule id is in `rule`.\",\n skill: \"warlock-check\",\n },\n WL7202: {\n code: \"WL7202\",\n area: \"cli\",\n category: \"code\",\n title: \"The linter reports a warning\",\n fix: \"Fix the warning, or change the rule's severity in the lint config if it does not apply.\",\n skill: \"warlock-check\",\n },\n WL7301: {\n code: \"WL7301\",\n area: \"cli\",\n category: \"code\",\n title: \"A test failed\",\n fix: \"Run the test file alone, read the assertion message and fix the code or the test.\",\n skill: \"warlock-check\",\n },\n WL7302: {\n code: \"WL7302\",\n area: \"cli\",\n category: \"code\",\n title: \"A test file failed before its tests ran\",\n fix: \"Fix the import or setup error named in the message; none of the file's tests ran.\",\n skill: \"warlock-check\",\n },\n WL7401: {\n code: \"WL7401\",\n area: \"cli\",\n category: \"code\",\n title: \"The agent board is not initialized\",\n fix: \"Run `warlock board init` at the project root first.\",\n skill: \"warlock-board\",\n },\n WL7402: {\n code: \"WL7402\",\n area: \"cli\",\n category: \"code\",\n title: \"A board item does not exist\",\n fix: \"Run `warlock board status` to list the item ids.\",\n skill: \"warlock-board\",\n },\n WL7403: {\n code: \"WL7403\",\n area: \"cli\",\n category: \"code\",\n title: \"A board item cannot move to the requested state\",\n fix: \"Use one of the next states listed in the message; `warlock board status <id>` shows the item's current state.\",\n skill: \"warlock-board\",\n },\n WL7404: {\n code: \"WL7404\",\n area: \"cli\",\n category: \"code\",\n title: \"No board message arrived before the watch timed out\",\n fix: \"Run `warlock board watch` again, or raise --timeout.\",\n skill: \"warlock-board\",\n },\n WL7501: {\n code: \"WL7501\",\n area: \"cli\",\n category: \"code\",\n title: \"AGENTS.md has malformed generated-section markers\",\n fix: \"Keep one `<!-- warlock:generated:start -->` line followed by one `<!-- warlock:generated:end -->` line, or remove both.\",\n skill: \"warlock-agents-md\",\n },\n\n // ── WL8xxx environment ───────────────────────────────────────────────────\n WL8001: {\n code: \"WL8001\",\n area: \"environment\",\n category: \"environment\",\n title: \"The Node.js version is not supported\",\n fix: \"Switch to a Node.js version that satisfies the engines range in the message (for example with nvm).\",\n skill: \"update-packages\",\n },\n WL8002: {\n code: \"WL8002\",\n area: \"environment\",\n category: \"environment\",\n title: \"A tool the project is configured to use is not installed\",\n fix: \"Install the tool named in the message (pnpm install), or remove its config file.\",\n skill: \"warlock-check\",\n },\n WL8003: {\n code: \"WL8003\",\n area: \"environment\",\n category: \"environment\",\n title: \"A service the code needs is unreachable\",\n fix: \"Start the service named in the message (database, Redis, ...) or point the config at a running one.\",\n skill: \"warlock-check\",\n },\n WL8004: {\n code: \"WL8004\",\n area: \"environment\",\n category: \"environment\",\n title: \"A port is already in use\",\n fix: \"Stop whatever listens on the port, or use another port (port 0 in tests).\",\n skill: \"run-app\",\n errorNames: [\"PortInUseError\"],\n },\n WL8005: {\n code: \"WL8005\",\n area: \"environment\",\n category: \"environment\",\n title: \"esbuild's native binary is not installed\",\n fix: \"Run `pnpm approve-builds`, then reinstall dependencies without skipping install scripts.\",\n skill: \"run-app\",\n errorNames: [\"EsbuildBinaryMissingError\"],\n },\n WL8006: {\n code: \"WL8006\",\n area: \"environment\",\n category: \"environment\",\n title: \"Git cannot list the changed files\",\n fix: \"Run inside a git repository with git on PATH, or run the command without --changed.\",\n skill: \"warlock-check\",\n },\n WL8007: {\n code: \"WL8007\",\n area: \"environment\",\n category: \"environment\",\n title: \"A tool crashed, timed out or ran out of memory\",\n fix: \"Re-run the step; if it repeats, raise the memory limit (NODE_OPTIONS=--max-old-space-size) or use --changed.\",\n skill: \"warlock-check\",\n },\n WL8008: {\n code: \"WL8008\",\n area: \"environment\",\n category: \"environment\",\n title: \"The dev server is not running\",\n fix: \"Start `warlock dev` in this project, or pass --url with the address of a running dev server.\",\n skill: \"warlock-trace\",\n },\n WL8009: {\n code: \"WL8009\",\n area: \"environment\",\n category: \"environment\",\n title: \"Devtools is not running in the dev server\",\n fix: \"Install it with `warlock add devtools` and restart `warlock dev`; devtools answers loopback requests in development only.\",\n skill: \"warlock-trace\",\n },\n\n // ── WL9xxx conventions and hygiene ───────────────────────────────────────\n WL9001: {\n code: \"WL9001\",\n area: \"convention\",\n category: \"code\",\n title: \"An auto-loaded file is imported\",\n fix: \"Remove the import: routes.ts, main.ts, worker.ts and events/ files are loaded by the framework.\",\n skill: \"warlock-conventions\",\n },\n WL9002: {\n code: \"WL9002\",\n area: \"convention\",\n category: \"code\",\n title: \"An import path ends in .js\",\n fix: \"Drop the .js extension from the import path.\",\n skill: \"warlock-conventions\",\n },\n WL9003: {\n code: \"WL9003\",\n area: \"convention\",\n category: \"code\",\n title: \"A relative import reaches into another module\",\n fix: \"Import across modules through the app/<module>/... alias instead of ../../.\",\n skill: \"warlock-conventions\",\n },\n WL9004: {\n code: \"WL9004\",\n area: \"convention\",\n category: \"code\",\n title: \"tsconfig.json enables legacy experimentalDecorators\",\n fix: 'Remove \"experimentalDecorators\": true from tsconfig.json; Cascade uses standard decorators.',\n skill: \"lower-stage3-decorators\",\n },\n WL9005: {\n code: \"WL9005\",\n area: \"convention\",\n category: \"code\",\n title: \"`request.all()` is passed to a query, a service or inApp()\",\n fix: \"Give the route a validation schema and pass `request.validated()`, which holds only the fields the schema accepted (mass assignment otherwise).\",\n skill: \"warlock-conventions\",\n },\n WL9006: {\n code: \"WL9006\",\n area: \"convention\",\n category: \"code\",\n title: \"A mutating route has no auth guard and is not marked public\",\n fix: \"Register the POST/PUT/PATCH/DELETE route inside `guarded()` (or with auth middleware), or pass `{ public: true }` if it is meant to be open.\",\n skill: \"warlock-conventions\",\n },\n WL9007: {\n code: \"WL9007\",\n area: \"convention\",\n category: \"code\",\n title: \"A redirect or link target comes from request input\",\n fix: \"Wrap the target in `safeRedirect()` (server, @warlock.js/core) or `safeRedirectTarget()` (pages, @warlock.js/web), or check it with `isSafeRedirect()` first.\",\n skill: \"warlock-conventions\",\n },\n WL9008: {\n code: \"WL9008\",\n area: \"convention\",\n category: \"code\",\n title: \"A raw email, credential or request body is logged\",\n fix: \"Pass the value through `maskEmail()`, `redact()`, `escapeLogValue()` or `request.toLogSnapshot()`, or log it under a key the logger censors (`token`, `password`, ...).\",\n skill: \"warlock-conventions\",\n },\n WL9009: {\n code: \"WL9009\",\n area: \"convention\",\n category: \"code\",\n title: \"A regex with an unbounded quantifier runs on request input\",\n fix: \"Bound the repetition (`{1,256}` instead of `+` or `*`) or cap the input length before matching.\",\n skill: \"warlock-conventions\",\n },\n WL9010: {\n code: \"WL9010\",\n area: \"convention\",\n category: \"code\",\n title: \"`await` on a synchronous log call\",\n fix: \"Remove the `await`: `log.info()` and its siblings return the logger; `await log.flush()` waits for delivery.\",\n skill: \"warlock-conventions\",\n },\n WL9011: {\n code: \"WL9011\",\n area: \"convention\",\n category: \"code\",\n title: \"A file defines more than one controller\",\n fix: \"Move each extra controller into its own `<name>.controller.ts` file.\",\n skill: \"warlock-conventions\",\n },\n WL9012: {\n code: \"WL9012\",\n area: \"convention\",\n category: \"code\",\n title: \"A tagged union uses `kind` as its discriminator\",\n fix: \"Rename the discriminator to `type` and update the code that narrows on it.\",\n skill: \"warlock-conventions\",\n },\n WL9013: {\n code: \"WL9013\",\n area: \"convention\",\n category: \"code\",\n title: \"Component file exceeds the configured line limit\",\n fix: \"Split the component file; the default limit is 150 lines.\",\n skill: \"warlock-check\",\n },\n WL9014: {\n code: \"WL9014\",\n area: \"convention\",\n category: \"code\",\n title: \"A file defines more than one web component\",\n fix: \"Move each component to its own TSX file.\",\n skill: \"warlock-check\",\n },\n WL9015: {\n code: \"WL9015\",\n area: \"convention\",\n category: \"code\",\n title: \"Component state, effects or event handlers live in a component file\",\n fix: \"Move state, effects and event handlers into a use-*.ts hook.\",\n skill: \"warlock-check\",\n },\n WL9016: {\n code: \"WL9016\",\n area: \"convention\",\n category: \"code\",\n title: \"App code uses a native form control\",\n fix: \"Use a design-system control; native controls are allowed in src/web/design-system/.\",\n skill: \"warlock-check\",\n },\n WL9017: {\n code: \"WL9017\",\n area: \"convention\",\n category: \"code\",\n title: \"A DOM form element declares native validation\",\n fix: \"Use the shared validation schema and field error controls.\",\n skill: \"warlock-check\",\n },\n WL9018: {\n code: \"WL9018\",\n area: \"convention\",\n category: \"code\",\n title: \"An exported controller has no response schema\",\n fix: \"Assign controller.responseSchema with a contract for each response status.\",\n skill: \"warlock-check\",\n },\n WL9101: {\n code: \"WL9101\",\n area: \"convention\",\n category: \"code\",\n title: \"A module still uses the deprecated utils/locales.ts\",\n fix: \"Run `warlock doctor --fix` to convert it to locales.json, or convert it by hand.\",\n skill: \"use-localization\",\n },\n WL9102: {\n code: \"WL9102\",\n area: \"convention\",\n category: \"code\",\n title: \"package.json version and CHANGELOG.md disagree\",\n fix: \"Add a CHANGELOG.md heading for the package.json version, or fix the version.\",\n skill: \"warlock-doctor\",\n },\n WL9103: {\n code: \"WL9103\",\n area: \"convention\",\n category: \"code\",\n title: \"CHANGELOG.md is missing or has no version heading\",\n fix: \"Add a CHANGELOG.md with a `## x.y.z` heading for the current version.\",\n skill: \"warlock-doctor\",\n },\n} as const satisfies Record<string, ErrorCodeDefinition>;\n\n/** Every registered code, as a union. */\nexport type WarlockErrorCode = keyof typeof ERROR_CODES;\n\n/** Whether `value` is a registered code. */\nexport function isWarlockErrorCode(value: unknown): value is WarlockErrorCode {\n return typeof value === \"string\" && Object.prototype.hasOwnProperty.call(ERROR_CODES, value);\n}\n\n/**\n * The registry entry for a code, or `undefined` for an unknown one. Case and\n * surrounding whitespace are ignored, so `explainErrorCode(\" wl3001 \")` works.\n */\nexport function explainErrorCode(code: string): ErrorCodeDefinition | undefined {\n const normalized = code.trim().toUpperCase();\n\n return isWarlockErrorCode(normalized) ? ERROR_CODES[normalized] : undefined;\n}\n\n/** The docs URL of a code: the reference page with the code as its anchor. */\nexport function errorCodeDocsUrl(code: WarlockErrorCode): string {\n return `${ERROR_CODES_DOCS_URL}#${code.toLowerCase()}`;\n}\n\n/** Node errno codes that mean \"the machine\", mapped to the Warlock code. */\nconst SYSTEM_ERROR_CODES: Readonly<Record<string, WarlockErrorCode>> = {\n ECONNREFUSED: \"WL8003\",\n ENOTFOUND: \"WL8003\",\n EHOSTUNREACH: \"WL8003\",\n ETIMEDOUT: \"WL8003\",\n EADDRINUSE: \"WL8004\",\n ERR_OUT_OF_MEMORY: \"WL8007\",\n};\n\nfunction readStringProperty(value: object, key: string): string | undefined {\n const property = Reflect.get(value, key);\n\n return typeof property === \"string\" ? property : undefined;\n}\n\n/**\n * The Warlock code of a thrown value, or `undefined` when it has none.\n *\n * Reads, in order: a registered `code` property (framework errors carry one);\n * the error's `name`, for classes whose `code` property already means\n * something else (`PortInUseError.code` is Node's `\"EADDRINUSE\"`); a Node\n * system errno such as `ECONNREFUSED`; then the same checks on `cause`. Reading\n * by name rather than `instanceof` keeps the answer right when two copies of\n * core are loaded.\n */\nexport function errorCodeOf(error: unknown): WarlockErrorCode | undefined {\n const seen = new Set<unknown>();\n let current: unknown = error;\n\n while (typeof current === \"object\" && current !== null && !seen.has(current)) {\n seen.add(current);\n\n const code = readStringProperty(current, \"code\");\n\n if (isWarlockErrorCode(code)) {\n return code;\n }\n\n const name = readStringProperty(current, \"name\");\n\n if (name) {\n for (const definition of Object.values(ERROR_CODES) as ErrorCodeDefinition[]) {\n if (definition.errorNames?.includes(name) && isWarlockErrorCode(definition.code)) {\n return definition.code;\n }\n }\n }\n\n if (code && SYSTEM_ERROR_CODES[code]) {\n return SYSTEM_ERROR_CODES[code];\n }\n\n current = Reflect.get(current, \"cause\");\n }\n\n return undefined;\n}\n"],"mappings":"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T;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;CACT;AACF;;AAMA,SAAgB,mBAAmB,OAA2C;CAC5E,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,eAAe,KAAK,aAAa,KAAK;AAC7F;;;;;AAMA,SAAgB,iBAAiB,MAA+C;CAC9E,MAAM,aAAa,KAAK,KAAK,CAAC,CAAC,YAAY;CAE3C,OAAO,mBAAmB,UAAU,IAAI,YAAY,cAAc;AACpE;;AAGA,SAAgB,iBAAiB,MAAgC;CAC/D,OAAO,GAAG,qBAAqB,GAAG,KAAK,YAAY;AACrD;;AAGA,MAAM,qBAAiE;CACrE,cAAc;CACd,WAAW;CACX,cAAc;CACd,WAAW;CACX,YAAY;CACZ,mBAAmB;AACrB;AAEA,SAAS,mBAAmB,OAAe,KAAiC;CAC1E,MAAM,WAAW,QAAQ,IAAI,OAAO,GAAG;CAEvC,OAAO,OAAO,aAAa,WAAW,WAAW;AACnD;;;;;;;;;;;AAYA,SAAgB,YAAY,OAA8C;CACxE,MAAM,uBAAO,IAAI,IAAa;CAC9B,IAAI,UAAmB;CAEvB,OAAO,OAAO,YAAY,YAAY,YAAY,QAAQ,CAAC,KAAK,IAAI,OAAO,GAAG;EAC5E,KAAK,IAAI,OAAO;EAEhB,MAAM,OAAO,mBAAmB,SAAS,MAAM;EAE/C,IAAI,mBAAmB,IAAI,GACzB,OAAO;EAGT,MAAM,OAAO,mBAAmB,SAAS,MAAM;EAE/C,IAAI,MACF;QAAK,MAAM,cAAc,OAAO,OAAO,WAAW,GAChD,IAAI,WAAW,YAAY,SAAS,IAAI,KAAK,mBAAmB,WAAW,IAAI,GAC7E,OAAO,WAAW;EAEtB;EAGF,IAAI,QAAQ,mBAAmB,OAC7B,OAAO,mBAAmB;EAG5B,UAAU,QAAQ,IAAI,SAAS,OAAO;CACxC;AAGF"}
@@ -1,4 +1,4 @@
1
- import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "../http/errors/errors.mjs";
1
+ import { AppError, BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "../http/errors/errors.mjs";
2
2
  import { ERROR_CODES, ERROR_CODES_DOCS_URL, ErrorCodeArea, ErrorCodeCategory, ErrorCodeDefinition, WarlockErrorCode, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode } from "./error-codes.mjs";
3
3
  import { WarlockError } from "./warlock-error.mjs";
4
- export { BadRequestError, ConflictError, CookieJarUnavailableError, ERROR_CODES, ERROR_CODES_DOCS_URL, ErrorCodeArea, ErrorCodeCategory, ErrorCodeDefinition, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError, WarlockError, WarlockErrorCode, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode };
4
+ export { AppError, BadRequestError, ConflictError, CookieJarUnavailableError, ERROR_CODES, ERROR_CODES_DOCS_URL, ErrorCodeArea, ErrorCodeCategory, ErrorCodeDefinition, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError, WarlockError, WarlockErrorCode, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode };
@@ -1,5 +1,5 @@
1
- import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "../http/errors/errors.mjs";
1
+ import { AppError, BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "../http/errors/errors.mjs";
2
2
  import { WarlockError } from "./warlock-error.mjs";
3
3
  import { ERROR_CODES, ERROR_CODES_DOCS_URL, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode } from "./error-codes.mjs";
4
4
 
5
- export { BadRequestError, ConflictError, CookieJarUnavailableError, ERROR_CODES, ERROR_CODES_DOCS_URL, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError, WarlockError, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode };
5
+ export { AppError, BadRequestError, ConflictError, CookieJarUnavailableError, ERROR_CODES, ERROR_CODES_DOCS_URL, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError, WarlockError, errorCodeDocsUrl, errorCodeOf, explainErrorCode, isWarlockErrorCode };
@@ -4,6 +4,7 @@ import { getWarlockVersion } from "../utils/framework-vesion.mjs";
4
4
  import { isGenerationDryRun, recordSkippedFile, setGenerationDetail } from "./generation-run.mjs";
5
5
  import { fileExistsAsync, getFileAsync, getJsonFileAsync, putFileAsync, putJsonFileAsync } from "./generation-fs.mjs";
6
6
  import { detectPackageManager, getAddCommand, getExactAddCommand } from "../updater/package-manager.mjs";
7
+ import { assertDesignSystemTailwind } from "./features/design-system.feature.mjs";
7
8
  import { featuresMap } from "./features/index.mjs";
8
9
  import { getPnpmVersion, isInsideParentPnpmWorkspace, mergeOnlyBuiltDependencies, mergePackageJsonBuildApprovals, mergePnpmSharpApproval, pnpmBuildApprovalLayout } from "./pnpm-build-approvals.mjs";
9
10
  import { colors } from "@mongez/copper";
@@ -52,6 +53,7 @@ async function addCommandAction(options) {
52
53
  }
53
54
  validateFeatures(features);
54
55
  const resolvedFeatures = resolveFeatures(features);
56
+ if (resolvedFeatures.includes("design-system") && !resolvedFeatures.includes("tailwind")) await assertDesignSystemTailwind();
55
57
  const dependencies = {};
56
58
  const devDependencies = {};
57
59
  const ejectConfigs = {};
@@ -1 +1 @@
1
- {"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../../packages/core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n fileExistsAsync,\n getFileAsync,\n getJsonFileAsync,\n putFileAsync,\n putJsonFileAsync,\n recordSkippedFile,\n} from \"./generation-fs\";\nimport { isGenerationDryRun, setGenerationDetail } from \"./generation-run\";\nimport { execSync } from \"node:child_process\";\nimport type { CommandActionData } from \"../commands/types\";\nimport {\n detectPackageManager,\n getAddCommand,\n getExactAddCommand,\n type PackageManager,\n} from \"../updater/package-manager\";\nimport { rootPath, srcPath } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\nimport { featuresMap } from \"./features\";\nimport {\n getPnpmVersion,\n isInsideParentPnpmWorkspace,\n mergeOnlyBuiltDependencies,\n mergePackageJsonBuildApprovals,\n mergePnpmSharpApproval,\n pnpmBuildApprovalLayout,\n} from \"./pnpm-build-approvals\";\n\nexport { featuresMap };\nexport type { FeatureDefinition } from \"./features\";\n\n/**\n * The parts of a project `package.json` this action reads or writes. Deliberately\n * partial — it describes what we touch, not the whole manifest.\n */\ntype ProjectPackageJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n};\n\nexport const allowedFeatures = Object.keys(featuresMap);\n\nfunction isWarlockPackage(packageName: string): boolean {\n return packageName.startsWith(\"@warlock.js/\");\n}\n\n/**\n * Resolve the internal feature-map placeholder to the version of Core that is\n * actually executing the command. Every @warlock.js package is lockstep, so\n * Core is the single source of truth; non-Warlock dependencies are untouched.\n */\nexport function resolveWarlockDependencyVersions(\n dependencies: Record<string, string>,\n frameworkVersion: string,\n): void {\n for (const dependency of Object.keys(dependencies)) {\n if (isWarlockPackage(dependency)) {\n dependencies[dependency] = frameworkVersion;\n }\n }\n}\n\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\n const resolved: string[] = [];\n\n for (const feature of features) {\n if (visited.has(feature)) continue;\n visited.add(feature);\n\n const def = featuresMap[feature];\n\n // `addCommandAction` runs `validateFeatures()` before calling this, and the\n // recursive calls below pass `requires` entries that come out of the map\n // itself — so every lookup resolves. Skipping an unresolvable one keeps a\n // typo from crashing the command with `Cannot read properties of\n // undefined`, which names neither the feature nor the fact that it was the\n // ARGUMENT that was wrong.\n if (def === undefined) continue;\n\n if (def.requires?.length) {\n resolved.push(...resolveFeatures(def.requires, visited));\n }\n\n resolved.push(feature);\n }\n\n return resolved;\n}\n\nexport async function addCommandAction(options: CommandActionData) {\n const features = options.args;\n const { packageManager, list, noInstall } = options.options;\n\n if (list) {\n setGenerationDetail(\n \"features\",\n allowedFeatures.map((feature) => ({\n name: feature,\n description: featuresMap[feature]?.description ?? \"\",\n })),\n );\n\n console.log(\"Available Features:\");\n\n for (const feature of allowedFeatures) {\n const definition = featuresMap[feature];\n\n // `allowedFeatures` is derived from this same map, so this resolves.\n if (definition === undefined) continue;\n\n console.log(`- ${colors.yellowBright(feature)}: ${colors.green(definition.description)}`);\n }\n\n process.exit(0);\n }\n\n validateFeatures(features);\n\n const resolvedFeatures = resolveFeatures(features);\n\n const dependencies: Record<string, string> = {};\n const devDependencies: Record<string, string> = {};\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\n const scripts: Record<string, string> = {};\n\n for (const feature of resolvedFeatures) {\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\n\n // Validated upstream; see the note in resolveFeatures above.\n if (featurePackages === undefined) continue;\n\n Object.assign(dependencies, featurePackages.dependencies);\n if (featurePackages.devDependencies) {\n Object.assign(devDependencies, featurePackages.devDependencies);\n }\n\n if (featurePackages.ejectConfig) {\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\n }\n\n if (featurePackages.script) {\n Object.assign(scripts, featurePackages.script);\n }\n }\n\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\n // a scaffolded project's features match its core version instead of drifting to\n // the feature map's static range.\n const frameworkVersion = await getWarlockVersion();\n resolveWarlockDependencyVersions(dependencies, frameworkVersion);\n resolveWarlockDependencyVersions(devDependencies, frameworkVersion);\n\n const currentPackageJson = await getJsonFileAsync<ProjectPackageJson>(rootPath(\"package.json\"));\n\n // Fresh templates may omit one of the maps — guard before reading.\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\n\n // Skip anything already present so we never downgrade an existing pin.\n for (const dependency of Object.keys(dependencies)) {\n if (currentPackageJson.dependencies[dependency]) {\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\n delete dependencies[dependency];\n }\n }\n\n for (const devDependency of Object.keys(devDependencies)) {\n if (currentPackageJson.devDependencies[devDependency]) {\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\n delete devDependencies[devDependency];\n }\n }\n\n setGenerationDetail(\"features\", resolvedFeatures);\n setGenerationDetail(\"dependencies\", { ...dependencies });\n setGenerationDetail(\"devDependencies\", { ...devDependencies });\n\n if (isGenerationDryRun()) {\n // A preview installs nothing; package.json is \"edited\" in the overlay so\n // the report lists it, and later steps read the previewed manifest.\n console.log(\n `Dry run: would install ${colors.magenta(Object.keys({ ...dependencies, ...devDependencies }).join(\", \") || \"nothing\")}`,\n );\n await recordDependencies(dependencies, devDependencies);\n } else if (noInstall) {\n await recordDependencies(dependencies, devDependencies);\n } else {\n const resolvedPackageManager =\n (packageManager as PackageManager | undefined) ?? (await detectPackageManager());\n if (resolvedPackageManager === \"pnpm\" && dependencies.sharp !== undefined) {\n await approvePnpmSharpBuild();\n }\n await installDependencies(\n packageManager as PackageManager | undefined,\n dependencies,\n devDependencies,\n );\n }\n\n for (const [name, config] of Object.entries(ejectConfigs)) {\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\n recordSkippedFile(srcPath(`config/${name}.ts`), \"exists\");\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\n continue;\n }\n\n console.log(`Creating ${colors.magenta(name)} config...`);\n\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\n\n console.log(`${colors.green(name)} config created successfully`);\n }\n\n // now loop again over features to execute onExecuting\n for (const feature of resolvedFeatures) {\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\n\n if (featurePackages?.onExecuting) {\n await featurePackages.onExecuting(options);\n }\n }\n\n setGenerationDetail(\"scripts\", { ...scripts });\n\n if (Object.keys(scripts).length > 0) {\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\n await putJsonFileAsync(packageJsonPath, packageJson);\n\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\n }\n}\n\n/**\n * Let pnpm run sharp's install script, written where the INSTALLED pnpm reads\n * approvals (see `pnpmBuildApprovalLayout`). pnpm 9 and 10.0–10.4 reject a\n * settings-only `pnpm-workspace.yaml`, so for them the approval goes into\n * package.json and no workspace file is created.\n *\n * An app inside a parent pnpm workspace gets no per-app policy: the parent's\n * workspace owns it, and a nested workspace file would make a second root.\n */\nasync function approvePnpmSharpBuild(): Promise<void> {\n const workspacePath = rootPath(\"pnpm-workspace.yaml\");\n const hasWorkspaceFile = await fileExistsAsync(workspacePath);\n\n if (!hasWorkspaceFile && isInsideParentPnpmWorkspace(rootPath())) {\n console.log(\n `${colors.yellowBright(\"sharp\")} needs its install script approved in the parent pnpm workspace's settings.`,\n );\n return;\n }\n\n const layout = pnpmBuildApprovalLayout(getPnpmVersion(rootPath()));\n\n if (layout === \"packageJson\") {\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n\n await putJsonFileAsync(packageJsonPath, mergePackageJsonBuildApprovals(packageJson, [\"sharp\"]));\n return;\n }\n\n const current = hasWorkspaceFile ? await getFileAsync(workspacePath) : \"\";\n const updated =\n layout === \"onlyBuiltDependencies\"\n ? mergeOnlyBuiltDependencies(current, [\"sharp\"])\n : mergePnpmSharpApproval(current);\n\n if (updated !== current) await putFileAsync(workspacePath, updated);\n}\n\n/**\n * Install the resolved dependency sets through the project's package manager.\n * Runs two passes (prod then dev) so each lands in the correct section.\n */\nexport async function installDependencies(\n packageManager: PackageManager | undefined,\n dependencies: Record<string, string>,\n devDependencies: Record<string, string>,\n exec: (command: string, options: { cwd: string; stdio: \"inherit\" }) => unknown = execSync,\n) {\n // `--package-manager` is optional; without it, fall back to the lockfile.\n const resolvedPackageManager = packageManager ?? (await detectPackageManager());\n const packageManagerCommand = getAddCommand(resolvedPackageManager);\n const exactPackageManagerCommand = getExactAddCommand(resolvedPackageManager);\n\n const installDependencySet = (dependencySet: Record<string, string>, development: boolean) => {\n const regularSpecs: string[] = [];\n const exactWarlockSpecs: string[] = [];\n\n for (const [name, version] of Object.entries(dependencySet)) {\n const spec = `${name}@${version}`;\n (isWarlockPackage(name) ? exactWarlockSpecs : regularSpecs).push(spec);\n }\n\n const developmentFlag = development ? \" -D\" : \"\";\n const runInstall = (command: string, specs: string[]) => {\n if (specs.length === 0) return;\n\n exec(`${command} ${specs.join(\" \")}${developmentFlag}`, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n };\n\n runInstall(packageManagerCommand, regularSpecs);\n runInstall(exactPackageManagerCommand, exactWarlockSpecs);\n };\n\n if (Object.keys(dependencies).length > 0) {\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\n\n installDependencySet(dependencies, false);\n\n console.log(\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\n );\n }\n\n if (Object.keys(devDependencies).length > 0) {\n console.log(\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\n );\n\n installDependencySet(devDependencies, true);\n\n console.log(\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\n );\n }\n}\n\n/**\n * Write the resolved dependency sets into package.json without installing.\n * Used by `--no-install` so a scaffolder can batch every feature into one\n * install pass after the command returns. Versions come from the feature map.\n */\nasync function recordDependencies(\n dependencies: Record<string, string>,\n devDependencies: Record<string, string>,\n) {\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\n return;\n }\n\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n\n packageJson.dependencies = packageJson.dependencies ?? {};\n packageJson.devDependencies = packageJson.devDependencies ?? {};\n\n Object.assign(packageJson.dependencies, dependencies);\n Object.assign(packageJson.devDependencies, devDependencies);\n\n await putJsonFileAsync(packageJsonPath, packageJson);\n\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\n\n console.log(\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (${isGenerationDryRun() ? \"dry run: nothing installed\" : \"install skipped via --no-install\"})`,\n );\n}\n\nfunction validateFeatures(features: string[]) {\n for (const feature of features) {\n if (!allowedFeatures.includes(feature)) {\n console.log(\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\n );\n process.exit(1);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../../packages/core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n fileExistsAsync,\n getFileAsync,\n getJsonFileAsync,\n putFileAsync,\n putJsonFileAsync,\n recordSkippedFile,\n} from \"./generation-fs\";\nimport { isGenerationDryRun, setGenerationDetail } from \"./generation-run\";\nimport { execSync } from \"node:child_process\";\nimport type { CommandActionData } from \"../commands/types\";\nimport {\n detectPackageManager,\n getAddCommand,\n getExactAddCommand,\n type PackageManager,\n} from \"../updater/package-manager\";\nimport { rootPath, srcPath } from \"../utils\";\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\nimport { featuresMap } from \"./features\";\nimport { assertDesignSystemTailwind } from \"./features/design-system.feature\";\nimport {\n getPnpmVersion,\n isInsideParentPnpmWorkspace,\n mergeOnlyBuiltDependencies,\n mergePackageJsonBuildApprovals,\n mergePnpmSharpApproval,\n pnpmBuildApprovalLayout,\n} from \"./pnpm-build-approvals\";\n\nexport { featuresMap };\nexport type { FeatureDefinition } from \"./features\";\n\n/**\n * The parts of a project `package.json` this action reads or writes. Deliberately\n * partial — it describes what we touch, not the whole manifest.\n */\ntype ProjectPackageJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n};\n\nexport const allowedFeatures = Object.keys(featuresMap);\n\nfunction isWarlockPackage(packageName: string): boolean {\n return packageName.startsWith(\"@warlock.js/\");\n}\n\n/**\n * Resolve the internal feature-map placeholder to the version of Core that is\n * actually executing the command. Every @warlock.js package is lockstep, so\n * Core is the single source of truth; non-Warlock dependencies are untouched.\n */\nexport function resolveWarlockDependencyVersions(\n dependencies: Record<string, string>,\n frameworkVersion: string,\n): void {\n for (const dependency of Object.keys(dependencies)) {\n if (isWarlockPackage(dependency)) {\n dependencies[dependency] = frameworkVersion;\n }\n }\n}\n\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\n const resolved: string[] = [];\n\n for (const feature of features) {\n if (visited.has(feature)) continue;\n visited.add(feature);\n\n const def = featuresMap[feature];\n\n // `addCommandAction` runs `validateFeatures()` before calling this, and the\n // recursive calls below pass `requires` entries that come out of the map\n // itself — so every lookup resolves. Skipping an unresolvable one keeps a\n // typo from crashing the command with `Cannot read properties of\n // undefined`, which names neither the feature nor the fact that it was the\n // ARGUMENT that was wrong.\n if (def === undefined) continue;\n\n if (def.requires?.length) {\n resolved.push(...resolveFeatures(def.requires, visited));\n }\n\n resolved.push(feature);\n }\n\n return resolved;\n}\n\nexport async function addCommandAction(options: CommandActionData) {\n const features = options.args;\n const { packageManager, list, noInstall } = options.options;\n\n if (list) {\n setGenerationDetail(\n \"features\",\n allowedFeatures.map((feature) => ({\n name: feature,\n description: featuresMap[feature]?.description ?? \"\",\n })),\n );\n\n console.log(\"Available Features:\");\n\n for (const feature of allowedFeatures) {\n const definition = featuresMap[feature];\n\n // `allowedFeatures` is derived from this same map, so this resolves.\n if (definition === undefined) continue;\n\n console.log(`- ${colors.yellowBright(feature)}: ${colors.green(definition.description)}`);\n }\n\n process.exit(0);\n }\n\n validateFeatures(features);\n\n const resolvedFeatures = resolveFeatures(features);\n\n if (resolvedFeatures.includes(\"design-system\") && !resolvedFeatures.includes(\"tailwind\")) {\n await assertDesignSystemTailwind();\n }\n\n const dependencies: Record<string, string> = {};\n const devDependencies: Record<string, string> = {};\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\n const scripts: Record<string, string> = {};\n\n for (const feature of resolvedFeatures) {\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\n\n // Validated upstream; see the note in resolveFeatures above.\n if (featurePackages === undefined) continue;\n\n Object.assign(dependencies, featurePackages.dependencies);\n if (featurePackages.devDependencies) {\n Object.assign(devDependencies, featurePackages.devDependencies);\n }\n\n if (featurePackages.ejectConfig) {\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\n }\n\n if (featurePackages.script) {\n Object.assign(scripts, featurePackages.script);\n }\n }\n\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\n // a scaffolded project's features match its core version instead of drifting to\n // the feature map's static range.\n const frameworkVersion = await getWarlockVersion();\n resolveWarlockDependencyVersions(dependencies, frameworkVersion);\n resolveWarlockDependencyVersions(devDependencies, frameworkVersion);\n\n const currentPackageJson = await getJsonFileAsync<ProjectPackageJson>(rootPath(\"package.json\"));\n\n // Fresh templates may omit one of the maps — guard before reading.\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\n\n // Skip anything already present so we never downgrade an existing pin.\n for (const dependency of Object.keys(dependencies)) {\n if (currentPackageJson.dependencies[dependency]) {\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\n delete dependencies[dependency];\n }\n }\n\n for (const devDependency of Object.keys(devDependencies)) {\n if (currentPackageJson.devDependencies[devDependency]) {\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\n delete devDependencies[devDependency];\n }\n }\n\n setGenerationDetail(\"features\", resolvedFeatures);\n setGenerationDetail(\"dependencies\", { ...dependencies });\n setGenerationDetail(\"devDependencies\", { ...devDependencies });\n\n if (isGenerationDryRun()) {\n // A preview installs nothing; package.json is \"edited\" in the overlay so\n // the report lists it, and later steps read the previewed manifest.\n console.log(\n `Dry run: would install ${colors.magenta(Object.keys({ ...dependencies, ...devDependencies }).join(\", \") || \"nothing\")}`,\n );\n await recordDependencies(dependencies, devDependencies);\n } else if (noInstall) {\n await recordDependencies(dependencies, devDependencies);\n } else {\n const resolvedPackageManager =\n (packageManager as PackageManager | undefined) ?? (await detectPackageManager());\n if (resolvedPackageManager === \"pnpm\" && dependencies.sharp !== undefined) {\n await approvePnpmSharpBuild();\n }\n await installDependencies(\n packageManager as PackageManager | undefined,\n dependencies,\n devDependencies,\n );\n }\n\n for (const [name, config] of Object.entries(ejectConfigs)) {\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\n recordSkippedFile(srcPath(`config/${name}.ts`), \"exists\");\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\n continue;\n }\n\n console.log(`Creating ${colors.magenta(name)} config...`);\n\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\n\n console.log(`${colors.green(name)} config created successfully`);\n }\n\n // now loop again over features to execute onExecuting\n for (const feature of resolvedFeatures) {\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\n\n if (featurePackages?.onExecuting) {\n await featurePackages.onExecuting(options);\n }\n }\n\n setGenerationDetail(\"scripts\", { ...scripts });\n\n if (Object.keys(scripts).length > 0) {\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\n await putJsonFileAsync(packageJsonPath, packageJson);\n\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\n }\n}\n\n/**\n * Let pnpm run sharp's install script, written where the INSTALLED pnpm reads\n * approvals (see `pnpmBuildApprovalLayout`). pnpm 9 and 10.0–10.4 reject a\n * settings-only `pnpm-workspace.yaml`, so for them the approval goes into\n * package.json and no workspace file is created.\n *\n * An app inside a parent pnpm workspace gets no per-app policy: the parent's\n * workspace owns it, and a nested workspace file would make a second root.\n */\nasync function approvePnpmSharpBuild(): Promise<void> {\n const workspacePath = rootPath(\"pnpm-workspace.yaml\");\n const hasWorkspaceFile = await fileExistsAsync(workspacePath);\n\n if (!hasWorkspaceFile && isInsideParentPnpmWorkspace(rootPath())) {\n console.log(\n `${colors.yellowBright(\"sharp\")} needs its install script approved in the parent pnpm workspace's settings.`,\n );\n return;\n }\n\n const layout = pnpmBuildApprovalLayout(getPnpmVersion(rootPath()));\n\n if (layout === \"packageJson\") {\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n\n await putJsonFileAsync(packageJsonPath, mergePackageJsonBuildApprovals(packageJson, [\"sharp\"]));\n return;\n }\n\n const current = hasWorkspaceFile ? await getFileAsync(workspacePath) : \"\";\n const updated =\n layout === \"onlyBuiltDependencies\"\n ? mergeOnlyBuiltDependencies(current, [\"sharp\"])\n : mergePnpmSharpApproval(current);\n\n if (updated !== current) await putFileAsync(workspacePath, updated);\n}\n\n/**\n * Install the resolved dependency sets through the project's package manager.\n * Runs two passes (prod then dev) so each lands in the correct section.\n */\nexport async function installDependencies(\n packageManager: PackageManager | undefined,\n dependencies: Record<string, string>,\n devDependencies: Record<string, string>,\n exec: (command: string, options: { cwd: string; stdio: \"inherit\" }) => unknown = execSync,\n) {\n // `--package-manager` is optional; without it, fall back to the lockfile.\n const resolvedPackageManager = packageManager ?? (await detectPackageManager());\n const packageManagerCommand = getAddCommand(resolvedPackageManager);\n const exactPackageManagerCommand = getExactAddCommand(resolvedPackageManager);\n\n const installDependencySet = (dependencySet: Record<string, string>, development: boolean) => {\n const regularSpecs: string[] = [];\n const exactWarlockSpecs: string[] = [];\n\n for (const [name, version] of Object.entries(dependencySet)) {\n const spec = `${name}@${version}`;\n (isWarlockPackage(name) ? exactWarlockSpecs : regularSpecs).push(spec);\n }\n\n const developmentFlag = development ? \" -D\" : \"\";\n const runInstall = (command: string, specs: string[]) => {\n if (specs.length === 0) return;\n\n exec(`${command} ${specs.join(\" \")}${developmentFlag}`, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n };\n\n runInstall(packageManagerCommand, regularSpecs);\n runInstall(exactPackageManagerCommand, exactWarlockSpecs);\n };\n\n if (Object.keys(dependencies).length > 0) {\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\n\n installDependencySet(dependencies, false);\n\n console.log(\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\n );\n }\n\n if (Object.keys(devDependencies).length > 0) {\n console.log(\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\n );\n\n installDependencySet(devDependencies, true);\n\n console.log(\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\n );\n }\n}\n\n/**\n * Write the resolved dependency sets into package.json without installing.\n * Used by `--no-install` so a scaffolder can batch every feature into one\n * install pass after the command returns. Versions come from the feature map.\n */\nasync function recordDependencies(\n dependencies: Record<string, string>,\n devDependencies: Record<string, string>,\n) {\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\n return;\n }\n\n const packageJsonPath = rootPath(\"package.json\");\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\n\n packageJson.dependencies = packageJson.dependencies ?? {};\n packageJson.devDependencies = packageJson.devDependencies ?? {};\n\n Object.assign(packageJson.dependencies, dependencies);\n Object.assign(packageJson.devDependencies, devDependencies);\n\n await putJsonFileAsync(packageJsonPath, packageJson);\n\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\n\n console.log(\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (${isGenerationDryRun() ? \"dry run: nothing installed\" : \"install skipped via --no-install\"})`,\n );\n}\n\nfunction validateFeatures(features: string[]) {\n for (const feature of features) {\n if (!allowedFeatures.includes(feature)) {\n console.log(\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\n );\n process.exit(1);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ //#region ../../packages/core/src/generations/design-system-payload.json
2
+ var dependencies = {
3
+ "@mongez/react-form": "4.0.0",
4
+ "radix-ui": "1.7.0",
5
+ "cmdk": "1.1.1",
6
+ "react-day-picker": "9.14.0",
7
+ "date-fns": "4.4.0",
8
+ "cn": "0.4.0",
9
+ "lucide-react": "1.52.0",
10
+ "class-variance-authority": "0.7.1",
11
+ "clsx": "2.1.1",
12
+ "tailwind-merge": "3.7.0"
13
+ };
14
+ var files = {
15
+ "src/web/components/ui/button.tsx": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"cn\";\nimport { Slot } from \"radix-ui\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n",
16
+ "src/web/components/ui/calendar.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { useCalendarDayButton } from \"web/design-system/hooks/use-calendar-day-button\";\nimport { cn } from \"cn\";\nimport { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport { DayPicker, getDefaultClassNames, type DayButton } from \"react-day-picker\";\n\nimport { Button, buttonVariants } from \"web/components/ui/button\";\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n ...props\n}: React.ComponentProps<typeof DayPicker> & {\n buttonVariant?: React.ComponentProps<typeof Button>[\"variant\"];\n}) {\n const defaultClassNames = getDefaultClassNames();\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\n \"group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent\",\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className,\n )}\n captionLayout={captionLayout}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(\"default\", { month: \"short\" }),\n ...formatters,\n }}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\"relative flex flex-col gap-4 md:flex-row\", defaultClassNames.months),\n month: cn(\"flex w-full flex-col gap-4\", defaultClassNames.month),\n nav: cn(\n \"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1\",\n defaultClassNames.nav,\n ),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) p-0 select-none aria-disabled:opacity-50\",\n defaultClassNames.button_previous,\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) p-0 select-none aria-disabled:opacity-50\",\n defaultClassNames.button_next,\n ),\n month_caption: cn(\n \"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)\",\n defaultClassNames.month_caption,\n ),\n dropdowns: cn(\n \"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium\",\n defaultClassNames.dropdowns,\n ),\n dropdown_root: cn(\n \"relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50\",\n defaultClassNames.dropdown_root,\n ),\n dropdown: cn(\"absolute inset-0 bg-popover opacity-0\", defaultClassNames.dropdown),\n caption_label: cn(\n \"font-medium select-none\",\n captionLayout === \"label\"\n ? \"text-sm\"\n : \"flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground\",\n defaultClassNames.caption_label,\n ),\n month_grid: cn(\"w-full border-collapse\", defaultClassNames.month_grid),\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none\",\n defaultClassNames.weekday,\n ),\n week: cn(\"mt-2 flex w-full\", defaultClassNames.week),\n week_number_header: cn(\"w-(--cell-size) select-none\", defaultClassNames.week_number_header),\n week_number: cn(\n \"text-[0.8rem] text-muted-foreground select-none\",\n defaultClassNames.week_number,\n ),\n day: cn(\n \"group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md\",\n props.showWeekNumber\n ? \"[&:nth-child(2)[data-selected=true]_button]:rounded-l-md\"\n : \"[&:first-child[data-selected=true]_button]:rounded-l-md\",\n defaultClassNames.day,\n ),\n range_start: cn(\"rounded-l-md bg-accent\", defaultClassNames.range_start),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\"rounded-r-md bg-accent\", defaultClassNames.range_end),\n today: cn(\n \"rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none\",\n defaultClassNames.today,\n ),\n outside: cn(\n \"text-muted-foreground aria-selected:text-muted-foreground\",\n defaultClassNames.outside,\n ),\n disabled: cn(\"text-muted-foreground opacity-50\", defaultClassNames.disabled),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames,\n }}\n components={{\n Root: ({ className, rootRef, ...props }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(className)} {...props} />;\n },\n Chevron: ({ className, orientation, ...props }) => {\n if (orientation === \"left\") {\n return <ChevronLeftIcon className={cn(\"size-4\", className)} {...props} />;\n }\n\n if (orientation === \"right\") {\n return <ChevronRightIcon className={cn(\"size-4\", className)} {...props} />;\n }\n\n return <ChevronDownIcon className={cn(\"size-4\", className)} {...props} />;\n },\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, ...props }) => {\n return (\n <td {...props}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">\n {children}\n </div>\n </td>\n );\n },\n ...components,\n }}\n {...props}\n />\n );\n}\n\nfunction CalendarDayButton({\n className,\n day,\n modifiers,\n ...props\n}: React.ComponentProps<typeof DayButton>) {\n const defaultClassNames = getDefaultClassNames();\n\n const ref = useCalendarDayButton(modifiers.focused);\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString()}\n data-selected-single={\n modifiers.selected &&\n !modifiers.range_start &&\n !modifiers.range_end &&\n !modifiers.range_middle\n }\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n \"flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70\",\n defaultClassNames.day,\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Calendar, CalendarDayButton };\n",
17
+ "src/web/components/ui/checkbox.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { CheckIcon } from \"lucide-react\";\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\";\n\nfunction Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n className={cn(\n \"peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary\",\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"grid place-content-center text-current transition-none\"\n >\n <CheckIcon className=\"size-3.5\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n}\n\nexport { Checkbox };\n",
18
+ "src/web/components/ui/command.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { cn } from \"cn\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"web/components/ui/dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandDialog({\n title = \"Command Palette\",\n description = \"Search for a command to run...\",\n children,\n className,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof Dialog> & {\n title?: string;\n description?: string;\n className?: string;\n showCloseButton?: boolean;\n}) {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent\n className={cn(\"overflow-hidden p-0\", className)}\n showCloseButton={showCloseButton}\n >\n <Command className=\"**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div data-slot=\"command-input-wrapper\" className=\"flex h-9 items-center gap-2 border-b px-3\">\n <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n data-slot=\"command-empty\"\n className=\"py-6 text-center text-sm\"\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"-mx-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n",
19
+ "src/web/components/ui/dialog.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { XIcon } from \"lucide-react\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\n\nimport { Button } from \"web/components/ui/button\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean;\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n",
20
+ "src/web/components/ui/input.tsx": "import * as React from \"react\";\nimport { cn } from \"cn\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Input };\n",
21
+ "src/web/components/ui/label.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { Label as LabelPrimitive } from \"radix-ui\";\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Label };\n",
22
+ "src/web/components/ui/LICENSE.md": "MIT License\n\nCopyright (c) 2023 shadcn\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
23
+ "src/web/components/ui/popover.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\n\nfunction Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n}\n\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n}\n\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nfunction PopoverHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"popover-header\"\n className={cn(\"flex flex-col gap-1 text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction PopoverTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return <div data-slot=\"popover-title\" className={cn(\"font-medium\", className)} {...props} />;\n}\n\nfunction PopoverDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"popover-description\"\n className={cn(\"text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Popover,\n PopoverTrigger,\n PopoverContent,\n PopoverAnchor,\n PopoverHeader,\n PopoverTitle,\n PopoverDescription,\n};\n",
24
+ "src/web/components/ui/radio-group.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { CircleIcon } from \"lucide-react\";\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\n\nfunction RadioGroup({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {\n return (\n <RadioGroupPrimitive.Root\n data-slot=\"radio-group\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n />\n );\n}\n\nfunction RadioGroupItem({\n className,\n ...props\n}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"aspect-square size-4 shrink-0 rounded-full border border-input text-primary shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-primary\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n",
25
+ "src/web/components/ui/switch.tsx": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"cn\";\nimport { Switch as SwitchPrimitive } from \"radix-ui\";\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof SwitchPrimitive.Root> & {\n size?: \"sm\" | \"default\";\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80\",\n className,\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground\",\n )}\n />\n </SwitchPrimitive.Root>\n );\n}\n\nexport { Switch };\n",
26
+ "src/web/components/ui/textarea.tsx": "import * as React from \"react\";\nimport { cn } from \"cn\";\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n",
27
+ "src/web/design-system/components/checkbox-input.tsx": "import { Checkbox } from \"web/components/ui/checkbox\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { CheckProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function CheckboxInput(props: CheckProps) {\n const field = useControl(props, \"checkbox\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Checkbox\n {...field.binding}\n disabled={field.disabled || props.readOnly}\n checked={Boolean(field.checked)}\n onCheckedChange={(checked) => field.setChecked(checked === true)}\n aria-labelledby={`${props.id}-label`}\n />\n </FieldFrame>\n );\n}\n",
28
+ "src/web/design-system/components/date-picker-input.tsx": "import { Button } from \"web/components/ui/button\";\nimport { Calendar } from \"web/components/ui/calendar\";\n\nimport { Popover, PopoverContent, PopoverTrigger } from \"web/components/ui/popover\";\nimport { useDateControl } from \"../hooks/use-date-control\";\nimport type { DateProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function DatePickerInput(props: DateProps) {\n const field = useDateControl(props, false);\n return (\n <FieldFrame {...props} error={field.error}>\n <Popover modal open={field.open} onOpenChange={field.setOpen}>\n <PopoverTrigger asChild>\n <Button\n {...field.binding}\n type=\"button\"\n variant=\"outline\"\n disabled={field.disabled || props.readOnly}\n aria-haspopup=\"dialog\"\n aria-controls={`${props.id}-dialog`}\n aria-labelledby={`${props.id}-label ${props.id}-value`}\n >\n <span id={`${props.id}-value`}>\n {field.value || props.placeholder || \"Choose date\"}\n </span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n id={`${props.id}-dialog`}\n aria-modal=\"true\"\n dir={props.dir}\n align=\"start\"\n aria-labelledby={`${props.id}-label`}\n onCloseAutoFocus={field.onBlur}\n >\n <Calendar\n id={`${props.id}-calendar`}\n mode=\"single\"\n autoFocus\n dir={props.dir}\n selected={field.selected}\n defaultMonth={field.selected}\n onSelect={field.select}\n />\n\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"ghost\" onClick={field.clear}>\n {props.clearLabel ?? \"Clear\"}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => field.setOpen(false)}>\n {props.closeLabel ?? \"Close\"}\n </Button>\n </div>\n </PopoverContent>\n </Popover>\n </FieldFrame>\n );\n}\n",
29
+ "src/web/design-system/components/date-time-picker-input.tsx": "import { Button } from \"web/components/ui/button\";\nimport { Calendar } from \"web/components/ui/calendar\";\nimport { Input } from \"web/components/ui/input\";\nimport { Label } from \"web/components/ui/label\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"web/components/ui/popover\";\nimport { useDateControl } from \"../hooks/use-date-control\";\nimport type { DateProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function DateTimePickerInput(props: DateProps) {\n const field = useDateControl(props, true);\n return (\n <FieldFrame {...props} error={field.error}>\n <Popover modal open={field.open} onOpenChange={field.setOpen}>\n <PopoverTrigger asChild>\n <Button\n {...field.binding}\n type=\"button\"\n variant=\"outline\"\n disabled={field.disabled || props.readOnly}\n aria-haspopup=\"dialog\"\n aria-controls={`${props.id}-dialog`}\n aria-labelledby={`${props.id}-label ${props.id}-value`}\n >\n <span id={`${props.id}-value`}>\n {field.value || props.placeholder || \"Choose date\"}\n </span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n id={`${props.id}-dialog`}\n aria-modal=\"true\"\n dir={props.dir}\n align=\"start\"\n aria-labelledby={`${props.id}-label`}\n onCloseAutoFocus={field.onBlur}\n >\n <Calendar\n id={`${props.id}-calendar`}\n mode=\"single\"\n autoFocus\n dir={props.dir}\n selected={field.selected}\n defaultMonth={field.selected}\n onSelect={field.select}\n />\n <Label htmlFor={`${props.id}-time`}>{props.timeLabel ?? \"Time\"}</Label>\n <Input\n id={`${props.id}-time`}\n type=\"time\"\n value={field.time}\n disabled={!field.selected}\n onChange={(event) => field.changeTime(event.target.value)}\n />\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"ghost\" onClick={field.clear}>\n {props.clearLabel ?? \"Clear\"}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => field.setOpen(false)}>\n {props.closeLabel ?? \"Close\"}\n </Button>\n </div>\n </PopoverContent>\n </Popover>\n </FieldFrame>\n );\n}\n",
30
+ "src/web/design-system/components/email-input.tsx": "import { Input } from \"web/components/ui/input\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function EmailInput(props: ControlProps) {\n const field = useControl(props, \"email\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Input\n {...field.binding}\n type=\"email\"\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onChange={(event) => field.changeValue(event.target.value)}\n placeholder={props.placeholder}\n readOnly={props.readOnly}\n autoComplete={props.autoComplete}\n dir={props.dir}\n />\n </FieldFrame>\n );\n}\n",
31
+ "src/web/design-system/components/field-frame.tsx": "import type { ReactNode } from \"react\";\nimport { Label } from \"web/components/ui/label\";\nimport type { ControlProps } from \"../types/control-props\";\n\nexport default function FieldFrame({\n id,\n label,\n hint,\n error,\n children,\n dir,\n className,\n}: Pick<ControlProps, \"id\" | \"label\" | \"hint\" | \"error\" | \"dir\" | \"className\"> & {\n children: ReactNode;\n}) {\n return (\n <div dir={dir} className={className ?? \"grid gap-2 text-start\"}>\n {label != null ? (\n <Label id={`${id}-label`} htmlFor={id}>\n {label}\n </Label>\n ) : null}\n {children}\n {hint ? (\n <p id={`${id}-hint`} className=\"text-sm text-muted-foreground\">\n {hint}\n </p>\n ) : null}\n {error ? (\n <p id={`${id}-error`} role=\"alert\" className=\"text-sm text-destructive\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n",
32
+ "src/web/design-system/components/file-input.tsx": "import { Input } from \"web/components/ui/input\";\nimport { useFileControl } from \"../hooks/use-file-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function FileInput(props: ControlProps) {\n const field = useFileControl(props);\n return (\n <FieldFrame {...props} error={field.error}>\n <Input\n {...field.binding}\n type=\"file\"\n disabled={field.disabled || props.readOnly}\n accept={props.accept}\n multiple={props.multiple}\n onChange={(event) =>\n field.changeValue(\n props.multiple ? Array.from(event.target.files ?? []) : (event.target.files?.[0] ?? \"\"),\n )\n }\n />\n </FieldFrame>\n );\n}\n",
33
+ "src/web/design-system/components/multi-select-input.tsx": "import { Button } from \"web/components/ui/button\";\nimport { Input } from \"web/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"web/components/ui/popover\";\nimport { useSelectControl } from \"../hooks/use-select-control\";\nimport type { SelectProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function MultiSelectInput(props: SelectProps) {\n const field = useSelectControl(props, true);\n return (\n <FieldFrame {...props} error={field.error}>\n <Popover open={field.open} onOpenChange={field.setOpen}>\n <PopoverTrigger asChild>\n <Button\n {...field.binding}\n type=\"button\"\n variant=\"outline\"\n disabled={field.disabled || props.readOnly}\n aria-labelledby={`${props.id}-label ${props.id}-value`}\n aria-haspopup=\"dialog\"\n aria-controls={`${props.id}-dialog`}\n >\n <span id={`${props.id}-value`}>\n {props.options\n .filter((option) => field.selected.includes(option.value))\n .map((option) => option.label)\n .join(\", \") ||\n props.placeholder ||\n \"Choose\"}\n </span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n id={`${props.id}-dialog`}\n dir={props.dir}\n align=\"start\"\n aria-labelledby={`${props.id}-label`}\n onCloseAutoFocus={field.onBlur}\n >\n <Input\n id={`${props.id}-search`}\n role=\"combobox\"\n aria-label={props.searchLabel ?? \"Search options\"}\n aria-expanded={field.open}\n aria-controls={`${props.id}-listbox`}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n field.activeOption\n ? `${props.id}-option-${encodeURIComponent(field.activeOption.value)}`\n : undefined\n }\n value={field.search}\n onChange={(event) => field.setSearch(event.target.value)}\n onKeyDown={field.onKeyDown}\n />\n <div\n id={`${props.id}-listbox`}\n role=\"listbox\"\n aria-labelledby={`${props.id}-label`}\n aria-multiselectable={true}\n className=\"max-h-60 overflow-auto py-2\"\n >\n {field.filtered.map((option) => (\n <Button\n key={option.value}\n id={`${props.id}-option-${encodeURIComponent(option.value)}`}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={field.selected.includes(option.value)}\n aria-disabled={option.disabled}\n disabled={option.disabled}\n variant=\"ghost\"\n className=\"w-full justify-start aria-selected:bg-accent data-[active=true]:ring-2 data-[active=true]:ring-ring\"\n data-active={field.activeOption === option}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => field.select(option.value)}\n >\n {field.selected.includes(option.value) ? \"✓ \" : \"\"}\n {option.label}\n </Button>\n ))}\n </div>\n {field.filtered.length === 0 ? (\n <p role=\"status\">{props.emptyLabel ?? \"No options found\"}</p>\n ) : null}\n <Button type=\"button\" variant=\"ghost\" onClick={field.clear}>\n {props.clearLabel ?? \"Clear\"}\n </Button>\n </PopoverContent>\n </Popover>\n </FieldFrame>\n );\n}\n",
34
+ "src/web/design-system/components/password-input.tsx": "import { Input } from \"web/components/ui/input\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function PasswordInput(props: ControlProps) {\n const field = useControl(props, \"password\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Input\n {...field.binding}\n type=\"password\"\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onChange={(event) => field.changeValue(event.target.value)}\n placeholder={props.placeholder}\n readOnly={props.readOnly}\n autoComplete={props.autoComplete}\n dir={props.dir}\n />\n </FieldFrame>\n );\n}\n",
35
+ "src/web/design-system/components/radio-input.tsx": "import { RadioGroup, RadioGroupItem } from \"web/components/ui/radio-group\";\nimport { Label } from \"web/components/ui/label\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { RadioProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function RadioInput(props: RadioProps) {\n // Register the group once as a scalar; individual Radix items own keyboard focus.\n const field = useControl(props);\n return (\n <FieldFrame {...props} error={field.error}>\n <RadioGroup\n {...field.binding}\n ref={(element) => {\n field.visibleElementRef.current = element;\n }}\n dir={props.dir}\n disabled={field.disabled || props.readOnly}\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onValueChange={field.changeValue}\n aria-labelledby={`${props.id}-label`}\n >\n {props.options.map((option) => (\n <div key={option.value} className=\"flex items-center gap-2\">\n <RadioGroupItem\n ref={\n option === props.options.find((item) => !item.disabled) ? field.inputRef : undefined\n }\n id={`${props.id}-option-${encodeURIComponent(option.value)}`}\n value={option.value}\n disabled={option.disabled}\n />\n <Label htmlFor={`${props.id}-option-${encodeURIComponent(option.value)}`}>\n {option.label}\n </Label>\n </div>\n ))}\n </RadioGroup>\n </FieldFrame>\n );\n}\n",
36
+ "src/web/design-system/components/select-input.tsx": "import { Button } from \"web/components/ui/button\";\nimport { Input } from \"web/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"web/components/ui/popover\";\nimport { useSelectControl } from \"../hooks/use-select-control\";\nimport type { SelectProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function SelectInput(props: SelectProps) {\n const field = useSelectControl(props, false);\n return (\n <FieldFrame {...props} error={field.error}>\n <Popover open={field.open} onOpenChange={field.setOpen}>\n <PopoverTrigger asChild>\n <Button\n {...field.binding}\n type=\"button\"\n variant=\"outline\"\n disabled={field.disabled || props.readOnly}\n aria-labelledby={`${props.id}-label ${props.id}-value`}\n aria-haspopup=\"dialog\"\n aria-controls={`${props.id}-dialog`}\n >\n <span id={`${props.id}-value`}>\n {props.options\n .filter((option) => field.selected.includes(option.value))\n .map((option) => option.label)\n .join(\", \") ||\n props.placeholder ||\n \"Choose\"}\n </span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n id={`${props.id}-dialog`}\n dir={props.dir}\n align=\"start\"\n aria-labelledby={`${props.id}-label`}\n onCloseAutoFocus={field.onBlur}\n >\n <Input\n id={`${props.id}-search`}\n role=\"combobox\"\n aria-label={props.searchLabel ?? \"Search options\"}\n aria-expanded={field.open}\n aria-controls={`${props.id}-listbox`}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n field.activeOption\n ? `${props.id}-option-${encodeURIComponent(field.activeOption.value)}`\n : undefined\n }\n value={field.search}\n onChange={(event) => field.setSearch(event.target.value)}\n onKeyDown={field.onKeyDown}\n />\n <div\n id={`${props.id}-listbox`}\n role=\"listbox\"\n aria-labelledby={`${props.id}-label`}\n aria-multiselectable={false}\n className=\"max-h-60 overflow-auto py-2\"\n >\n {field.filtered.map((option) => (\n <Button\n key={option.value}\n id={`${props.id}-option-${encodeURIComponent(option.value)}`}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={field.selected.includes(option.value)}\n aria-disabled={option.disabled}\n disabled={option.disabled}\n variant=\"ghost\"\n className=\"w-full justify-start aria-selected:bg-accent data-[active=true]:ring-2 data-[active=true]:ring-ring\"\n data-active={field.activeOption === option}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => field.select(option.value)}\n >\n {field.selected.includes(option.value) ? \"✓ \" : \"\"}\n {option.label}\n </Button>\n ))}\n </div>\n {field.filtered.length === 0 ? (\n <p role=\"status\">{props.emptyLabel ?? \"No options found\"}</p>\n ) : null}\n <Button type=\"button\" variant=\"ghost\" onClick={field.clear}>\n {props.clearLabel ?? \"Clear\"}\n </Button>\n </PopoverContent>\n </Popover>\n </FieldFrame>\n );\n}\n",
37
+ "src/web/design-system/components/submit-button.tsx": "import { Button } from \"web/components/ui/button\";\nimport { useSubmitControl } from \"../hooks/use-submit-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function SubmitButton(props: ControlProps) {\n const field = useSubmitControl(props);\n return (\n <FieldFrame {...props} label={null} error={field.error}>\n <Button\n {...field.binding}\n type=\"submit\"\n disabled={field.disabled}\n aria-busy={field.isSubmitting}\n >\n {props.label}\n </Button>\n </FieldFrame>\n );\n}\n",
38
+ "src/web/design-system/components/switch-input.tsx": "import { Switch } from \"web/components/ui/switch\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { CheckProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function SwitchInput(props: CheckProps) {\n const field = useControl(props, \"checkbox\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Switch\n {...field.binding}\n disabled={field.disabled || props.readOnly}\n checked={Boolean(field.checked)}\n onCheckedChange={(checked) => field.setChecked(checked === true)}\n aria-labelledby={`${props.id}-label`}\n />\n </FieldFrame>\n );\n}\n",
39
+ "src/web/design-system/components/text-input.tsx": "import { Input } from \"web/components/ui/input\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function TextInput(props: ControlProps) {\n const field = useControl(props, \"text\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Input\n {...field.binding}\n type=\"text\"\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onChange={(event) => field.changeValue(event.target.value)}\n placeholder={props.placeholder}\n readOnly={props.readOnly}\n autoComplete={props.autoComplete}\n dir={props.dir}\n />\n </FieldFrame>\n );\n}\n",
40
+ "src/web/design-system/components/textarea-input.tsx": "import { Textarea } from \"web/components/ui/textarea\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function TextareaInput(props: ControlProps) {\n const field = useControl(props, \"textarea\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Textarea\n {...field.binding}\n rows={props.rows}\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onChange={(event) => field.changeValue(event.target.value)}\n placeholder={props.placeholder}\n readOnly={props.readOnly}\n autoComplete={props.autoComplete}\n dir={props.dir}\n />\n </FieldFrame>\n );\n}\n",
41
+ "src/web/design-system/components/time-picker-input.tsx": "import { Input } from \"web/components/ui/input\";\nimport { useControl } from \"../hooks/use-control\";\nimport type { ControlProps } from \"../types/control-props\";\nimport FieldFrame from \"./field-frame\";\n\nexport default function TimePickerInput(props: ControlProps) {\n const field = useControl(props, \"time\");\n return (\n <FieldFrame {...props} error={field.error}>\n <Input\n {...field.binding}\n type=\"time\"\n value={typeof field.value === \"string\" ? field.value : \"\"}\n onChange={(event) => field.changeValue(event.target.value)}\n placeholder={props.placeholder}\n readOnly={props.readOnly}\n autoComplete={props.autoComplete}\n dir={props.dir}\n />\n </FieldFrame>\n );\n}\n",
42
+ "src/web/design-system/hooks/use-calendar-day-button.ts": "import { useEffect, useRef } from \"react\";\n\nexport function useCalendarDayButton(focused: boolean) {\n const ref = useRef<HTMLButtonElement>(null);\n useEffect(() => {\n if (focused) ref.current?.focus();\n }, [focused]);\n return ref;\n}\n",
43
+ "src/web/design-system/hooks/use-control.ts": "import { useFormControl } from \"@mongez/react-form\";\nimport type { ControlProps } from \"../types/control-props\";\n\n/** Explicit ids keep SSR, hydration, portal labels and repeated forms aligned. */\nexport function useControl(props: ControlProps, type = \"text\", multiple = false) {\n const control = useFormControl(\n { ...props, type, multiple },\n { multiple, isCollectable: type === \"submit\" ? () => false : undefined },\n );\n const error = props.error ?? control.error;\n const describedBy =\n [props.hint ? `${props.id}-hint` : \"\", error ? `${props.id}-error` : \"\"]\n .filter(Boolean)\n .join(\" \") || undefined;\n const binding = {\n id: props.id,\n name: props.name,\n disabled: control.disabled,\n onBlur: control.onBlur,\n ref: (element: HTMLElement | null) => {\n control.inputRef.current = element;\n control.visibleElementRef.current = element;\n },\n \"aria-invalid\": Boolean(error),\n \"aria-required\": props.required || undefined,\n \"aria-describedby\": describedBy,\n };\n return { ...control, error, binding };\n}\n",
44
+ "src/web/design-system/hooks/use-date-control.ts": "import { useState } from \"react\";\nimport { useControl } from \"./use-control\";\nimport type { DateProps } from \"../types/control-props\";\n\nexport function calendarDate(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, \"0\")}-${String(date.getDate()).padStart(2, \"0\")}`;\n}\nexport function parseCalendarDate(value: string): Date | undefined {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return undefined;\n const [year = 0, month = 0, day = 0] = value.split(\"-\").map(Number);\n const date = new Date(0);\n date.setFullYear(year, month - 1, day);\n date.setHours(12, 0, 0, 0);\n return calendarDate(date) === value ? date : undefined;\n}\nexport function useDateControl(props: DateProps, withTime = false) {\n const field = useControl(props);\n const [open, setOpen] = useState(false);\n const value = typeof field.value === \"string\" ? field.value : \"\";\n const [datePart = \"\", time = \"\"] = value.split(\"T\");\n return {\n ...field,\n open,\n setOpen,\n value,\n time,\n selected: parseCalendarDate(datePart),\n select: (date: Date | undefined) => {\n if (field.disabled || props.readOnly) return;\n field.changeValue(date ? calendarDate(date) + (withTime ? `T${time || \"00:00\"}` : \"\") : \"\");\n if (!withTime) setOpen(false);\n },\n changeTime: (next: string) => {\n if (!field.disabled && !props.readOnly)\n field.changeValue(next && parseCalendarDate(datePart) ? `${datePart}T${next}` : \"\");\n },\n clear: () => {\n if (!field.disabled && !props.readOnly) field.changeValue(\"\");\n setOpen(false);\n },\n };\n}\n",
45
+ "src/web/design-system/hooks/use-file-control.ts": "import { useEffect } from \"react\";\nimport { useControl } from \"./use-control\";\nimport type { ControlProps } from \"../types/control-props\";\n\nexport function useFileControl(props: ControlProps) {\n const field = useControl(\n { ...props, value: undefined, defaultValue: props.multiple ? [] : \"\" },\n \"file\",\n props.multiple,\n );\n useEffect(() => {\n const clear = () => {\n const input: HTMLInputElement | undefined = field.inputRef.current;\n if (input) input.value = \"\";\n };\n const reset = field.formControl.onReset(clear);\n const cleared = field.formControl.onClear(clear);\n return () => {\n reset.unsubscribe();\n cleared.unsubscribe();\n };\n }, [field.formControl, field.inputRef]);\n return field;\n}\n",
46
+ "src/web/design-system/hooks/use-select-control.ts": "import { useState, type KeyboardEvent } from \"react\";\nimport { useControl } from \"./use-control\";\nimport type { SelectProps } from \"../types/control-props\";\n\nexport function selectedValues(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : typeof value === \"string\" && value !== \"\"\n ? [value]\n : [];\n}\nexport function toggleSelection(values: readonly string[], value: string): string[] {\n return values.includes(value) ? values.filter((item) => item !== value) : [...values, value];\n}\nexport function useSelectControl(props: SelectProps, multiple = false) {\n const field = useControl(props, \"text\", multiple);\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState(\"\");\n const [active, setActive] = useState(0);\n const selected = selectedValues(field.value);\n const filtered = props.options.filter((option) =>\n option.label.toLocaleLowerCase().includes(search.toLocaleLowerCase()),\n );\n const enabled = filtered.filter((option) => !option.disabled);\n const activeOption = enabled[Math.min(active, Math.max(0, enabled.length - 1))];\n const select = (value: string) => {\n if (field.disabled || props.readOnly) return;\n field.changeValue(multiple ? toggleSelection(selected, value) : value);\n if (!multiple) setOpen(false);\n };\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if ([\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(event.key)) {\n event.preventDefault();\n const current = activeOption ? enabled.indexOf(activeOption) : 0;\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? enabled.length - 1\n : (current + (event.key === \"ArrowDown\" ? 1 : -1) + enabled.length) %\n Math.max(1, enabled.length);\n setActive(Math.max(0, next));\n const option = enabled[Math.max(0, next)];\n if (option)\n document\n .getElementById(`${props.id}-option-${encodeURIComponent(option.value)}`)\n ?.scrollIntoView({ block: \"nearest\" });\n } else if (\n (event.key === \"Enter\" || (multiple && event.key === \" \" && search === \"\")) &&\n activeOption\n ) {\n event.preventDefault();\n select(activeOption.value);\n }\n };\n return {\n ...field,\n open,\n setOpen: (next: boolean) => {\n setOpen(next && !field.disabled && !props.readOnly);\n setSearch(\"\");\n setActive(0);\n },\n search,\n setSearch: (next: string) => {\n setSearch(next);\n setActive(0);\n },\n selected,\n filtered,\n activeOption,\n select,\n onKeyDown,\n clear: () => {\n if (!field.disabled && !props.readOnly) field.changeValue(multiple ? [] : \"\");\n },\n };\n}\n",
47
+ "src/web/design-system/hooks/use-submit-control.ts": "import { useSubmitButton } from \"@mongez/react-form\";\nimport { useControl } from \"./use-control\";\nimport type { ControlProps } from \"../types/control-props\";\n\nexport function useSubmitControl(props: ControlProps) {\n const field = useControl(props, \"submit\");\n const submit = useSubmitButton();\n return {\n ...field,\n disabled: field.disabled || submit.disabled || submit.isSubmitting,\n isSubmitting: submit.isSubmitting,\n };\n}\n",
48
+ "src/web/design-system/types/control-props.ts": "import type { FormControlProps } from \"@mongez/react-form\";\nimport type { ReactNode } from \"react\";\n\nexport type ControlProps = Pick<\n FormControlProps,\n | \"name\"\n | \"rules\"\n | \"schema\"\n | \"validate\"\n | \"validateOn\"\n | \"onChange\"\n | \"onError\"\n | \"errors\"\n | \"errorKeys\"\n | \"disabled\"\n | \"readOnly\"\n | \"required\"\n> & {\n id: string;\n label: ReactNode;\n hint?: ReactNode;\n error?: ReactNode;\n value?: unknown;\n defaultValue?: unknown;\n placeholder?: string;\n className?: string;\n dir?: \"ltr\" | \"rtl\";\n autoComplete?: string;\n rows?: number;\n accept?: string;\n multiple?: boolean;\n};\nexport type Choice = { value: string; label: string; disabled?: boolean };\nexport type SelectProps = ControlProps & {\n options: readonly Choice[];\n searchLabel?: string;\n emptyLabel?: string;\n clearLabel?: string;\n};\nexport type RadioProps = ControlProps & { options: readonly Choice[] };\nexport type CheckProps = ControlProps & { checked?: boolean; defaultChecked?: boolean };\nexport type DateProps = ControlProps & {\n clearLabel?: string;\n closeLabel?: string;\n timeLabel?: string;\n};\n"
49
+ };
50
+
51
+ //#endregion
52
+ export { dependencies, files };
53
+ //# sourceMappingURL=design-system-payload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-system-payload.mjs","names":[],"sources":["../../../../../../../../packages/core/src/generations/design-system-payload.json"],"sourcesContent":[""],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import { rootPath, srcPath } from "../../utils/paths.mjs";
2
+ import "../../utils/index.mjs";
3
+ import { recordSkippedFile } from "../generation-run.mjs";
4
+ import { ensureDirectoryAsync, fileExistsAsync, getFileAsync, getJsonFileAsync, putFileAsync } from "../generation-fs.mjs";
5
+ import { dependencies, files } from "../design-system-payload.mjs";
6
+ import { shadcnFeature } from "./shadcn.feature.mjs";
7
+ import { dirname } from "node:path";
8
+
9
+ //#region ../../packages/core/src/generations/features/design-system.feature.ts
10
+ /** Run before dependency installation so refusal leaves the app untouched. */
11
+ async function assertDesignSystemTailwind() {
12
+ const manifest = await getJsonFileAsync(rootPath("package.json"));
13
+ const css = await fileExistsAsync(srcPath("web/app.css")) ? await getFileAsync(srcPath("web/app.css")) : "";
14
+ if (!(manifest.dependencies?.tailwindcss ?? manifest.devDependencies?.tailwindcss) || !/@import\s+["']tailwindcss["']/.test(css.replace(/\/\*[\s\S]*?\*\//g, ""))) throw new Error("The design system needs Tailwind CSS in src/web/app.css. Please run `warlock add tailwind` first, then `warlock add design-system`.");
15
+ }
16
+ const designSystemFeature = {
17
+ description: "Accessible shadcn/ui form controls wired to Mongez React Form; requires an existing Tailwind setup.",
18
+ requires: ["web"],
19
+ dependencies,
20
+ async onExecuting(options) {
21
+ await assertDesignSystemTailwind();
22
+ await shadcnFeature.onExecuting?.(options);
23
+ for (const [relativePath, content] of Object.entries(files)) {
24
+ const target = rootPath(relativePath);
25
+ if (await fileExistsAsync(target)) {
26
+ recordSkippedFile(target, "exists");
27
+ continue;
28
+ }
29
+ await ensureDirectoryAsync(dirname(target));
30
+ await putFileAsync(target, content);
31
+ }
32
+ console.log("Design system controls are ready in src/web/design-system. Existing files were preserved.");
33
+ }
34
+ };
35
+
36
+ //#endregion
37
+ export { assertDesignSystemTailwind, designSystemFeature };
38
+ //# sourceMappingURL=design-system.feature.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-system.feature.mjs","names":["payload.dependencies","payload.files"],"sources":["../../../../../../../../../packages/core/src/generations/features/design-system.feature.ts"],"sourcesContent":["import payload from \"../design-system-payload.json\" with { type: \"json\" };\nimport {\n ensureDirectoryAsync,\n fileExistsAsync,\n getFileAsync,\n getJsonFileAsync,\n putFileAsync,\n recordSkippedFile,\n} from \"../generation-fs\";\nimport { dirname } from \"node:path\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport { shadcnFeature } from \"./shadcn.feature\";\nimport type { FeatureDefinition } from \"./types\";\n\n/** Run before dependency installation so refusal leaves the app untouched. */\nexport async function assertDesignSystemTailwind(): Promise<void> {\n const manifest = await getJsonFileAsync<{\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n }>(rootPath(\"package.json\"));\n const css = (await fileExistsAsync(srcPath(\"web/app.css\")))\n ? await getFileAsync(srcPath(\"web/app.css\"))\n : \"\";\n const hasPackage = manifest.dependencies?.tailwindcss ?? manifest.devDependencies?.tailwindcss;\n if (!hasPackage || !/@import\\s+[\"']tailwindcss[\"']/.test(css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\"))) {\n throw new Error(\n \"The design system needs Tailwind CSS in src/web/app.css. Please run `warlock add tailwind` first, then `warlock add design-system`.\",\n );\n }\n}\n\nexport const designSystemFeature: FeatureDefinition = {\n description:\n \"Accessible shadcn/ui form controls wired to Mongez React Form; requires an existing Tailwind setup.\",\n requires: [\"web\"],\n dependencies: payload.dependencies,\n async onExecuting(options) {\n await assertDesignSystemTailwind();\n await shadcnFeature.onExecuting?.(options);\n for (const [relativePath, content] of Object.entries(payload.files)) {\n const target = rootPath(relativePath);\n if (await fileExistsAsync(target)) {\n recordSkippedFile(target, \"exists\");\n continue;\n }\n await ensureDirectoryAsync(dirname(target));\n await putFileAsync(target, content);\n }\n console.log(\n \"Design system controls are ready in src/web/design-system. Existing files were preserved.\",\n );\n },\n};\n"],"mappings":";;;;;;;;;;AAeA,eAAsB,6BAA4C;CAChE,MAAM,WAAW,MAAM,iBAGpB,SAAS,cAAc,CAAC;CAC3B,MAAM,MAAO,MAAM,gBAAgB,QAAQ,aAAa,CAAC,IACrD,MAAM,aAAa,QAAQ,aAAa,CAAC,IACzC;CAEJ,IAAI,EADe,SAAS,cAAc,eAAe,SAAS,iBAAiB,gBAChE,CAAC,gCAAgC,KAAK,IAAI,QAAQ,qBAAqB,EAAE,CAAC,GAC3F,MAAM,IAAI,MACR,qIACF;AAEJ;AAEA,MAAa,sBAAyC;CACpD,aACE;CACF,UAAU,CAAC,KAAK;CACFA;CACd,MAAM,YAAY,SAAS;EACzB,MAAM,2BAA2B;EACjC,MAAM,cAAc,cAAc,OAAO;EACzC,KAAK,MAAM,CAAC,cAAc,YAAY,OAAO,QAAQC,KAAa,GAAG;GACnE,MAAM,SAAS,SAAS,YAAY;GACpC,IAAI,MAAM,gBAAgB,MAAM,GAAG;IACjC,kBAAkB,QAAQ,QAAQ;IAClC;GACF;GACA,MAAM,qBAAqB,QAAQ,MAAM,CAAC;GAC1C,MAAM,aAAa,QAAQ,OAAO;EACpC;EACA,QAAQ,IACN,2FACF;CACF;AACF"}