@dewtech/dare-cli 3.0.0 → 3.1.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 (453) hide show
  1. package/README.md +45 -39
  2. package/dist/bin/dare.js +1 -3
  3. package/dist/bin/dare.js.map +1 -1
  4. package/dist/commands/__tests__/init.integration.spec.d.ts +2 -0
  5. package/dist/commands/__tests__/init.integration.spec.d.ts.map +1 -0
  6. package/dist/commands/__tests__/init.integration.spec.js +134 -0
  7. package/dist/commands/__tests__/init.integration.spec.js.map +1 -0
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +84 -1
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/new.d.ts.map +1 -1
  12. package/dist/commands/new.js +2 -1
  13. package/dist/commands/new.js.map +1 -1
  14. package/dist/mcp-server/bin/server.js +0 -0
  15. package/dist/stacks/__tests__/dna-emitter.spec.d.ts +2 -0
  16. package/dist/stacks/__tests__/dna-emitter.spec.d.ts.map +1 -0
  17. package/dist/stacks/__tests__/dna-emitter.spec.js +207 -0
  18. package/dist/stacks/__tests__/dna-emitter.spec.js.map +1 -0
  19. package/dist/stacks/__tests__/dna.spec.d.ts +2 -0
  20. package/dist/stacks/__tests__/dna.spec.d.ts.map +1 -0
  21. package/dist/stacks/__tests__/dna.spec.js +211 -0
  22. package/dist/stacks/__tests__/dna.spec.js.map +1 -0
  23. package/dist/stacks/__tests__/parity-rails.fixture.json +228 -0
  24. package/dist/stacks/__tests__/parity-rails.spec.d.ts +2 -0
  25. package/dist/stacks/__tests__/parity-rails.spec.d.ts.map +1 -0
  26. package/dist/stacks/__tests__/parity-rails.spec.js +99 -0
  27. package/dist/stacks/__tests__/parity-rails.spec.js.map +1 -0
  28. package/dist/stacks/__tests__/registry.spec.d.ts +2 -0
  29. package/dist/stacks/__tests__/registry.spec.d.ts.map +1 -0
  30. package/dist/stacks/__tests__/registry.spec.js +101 -0
  31. package/dist/stacks/__tests__/registry.spec.js.map +1 -0
  32. package/dist/stacks/__tests__/template-engine.spec.d.ts +2 -0
  33. package/dist/stacks/__tests__/template-engine.spec.d.ts.map +1 -0
  34. package/dist/stacks/__tests__/template-engine.spec.js +149 -0
  35. package/dist/stacks/__tests__/template-engine.spec.js.map +1 -0
  36. package/dist/stacks/dna-emitter.d.ts +45 -0
  37. package/dist/stacks/dna-emitter.d.ts.map +1 -0
  38. package/dist/stacks/dna-emitter.js +267 -0
  39. package/dist/stacks/dna-emitter.js.map +1 -0
  40. package/dist/stacks/go-gin/__tests__/scaffold.spec.d.ts +2 -0
  41. package/dist/stacks/go-gin/__tests__/scaffold.spec.d.ts.map +1 -0
  42. package/dist/stacks/go-gin/__tests__/scaffold.spec.js +221 -0
  43. package/dist/stacks/go-gin/__tests__/scaffold.spec.js.map +1 -0
  44. package/dist/stacks/go-gin/scaffold.d.ts +3 -0
  45. package/dist/stacks/go-gin/scaffold.d.ts.map +1 -0
  46. package/dist/stacks/go-gin/scaffold.js +105 -0
  47. package/dist/stacks/go-gin/scaffold.js.map +1 -0
  48. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.d.ts +2 -0
  49. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.d.ts.map +1 -0
  50. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.js +215 -0
  51. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.js.map +1 -0
  52. package/dist/stacks/go-stdlib/scaffold.d.ts +3 -0
  53. package/dist/stacks/go-stdlib/scaffold.d.ts.map +1 -0
  54. package/dist/stacks/go-stdlib/scaffold.js +106 -0
  55. package/dist/stacks/go-stdlib/scaffold.js.map +1 -0
  56. package/dist/stacks/mcp-go/__tests__/scaffold.spec.d.ts +2 -0
  57. package/dist/stacks/mcp-go/__tests__/scaffold.spec.d.ts.map +1 -0
  58. package/dist/stacks/mcp-go/__tests__/scaffold.spec.js +203 -0
  59. package/dist/stacks/mcp-go/__tests__/scaffold.spec.js.map +1 -0
  60. package/dist/stacks/mcp-go/scaffold.d.ts +3 -0
  61. package/dist/stacks/mcp-go/scaffold.d.ts.map +1 -0
  62. package/dist/stacks/mcp-go/scaffold.js +94 -0
  63. package/dist/stacks/mcp-go/scaffold.js.map +1 -0
  64. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.d.ts +2 -0
  65. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.d.ts.map +1 -0
  66. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.js +236 -0
  67. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.js.map +1 -0
  68. package/dist/stacks/mcp-node-ts/scaffold.d.ts +3 -0
  69. package/dist/stacks/mcp-node-ts/scaffold.d.ts.map +1 -0
  70. package/dist/stacks/mcp-node-ts/scaffold.js +95 -0
  71. package/dist/stacks/mcp-node-ts/scaffold.js.map +1 -0
  72. package/dist/stacks/mcp-python/__tests__/scaffold.spec.d.ts +2 -0
  73. package/dist/stacks/mcp-python/__tests__/scaffold.spec.d.ts.map +1 -0
  74. package/dist/stacks/mcp-python/__tests__/scaffold.spec.js +228 -0
  75. package/dist/stacks/mcp-python/__tests__/scaffold.spec.js.map +1 -0
  76. package/dist/stacks/mcp-python/scaffold.d.ts +3 -0
  77. package/dist/stacks/mcp-python/scaffold.d.ts.map +1 -0
  78. package/dist/stacks/mcp-python/scaffold.js +98 -0
  79. package/dist/stacks/mcp-python/scaffold.js.map +1 -0
  80. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.d.ts +2 -0
  81. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.d.ts.map +1 -0
  82. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.js +213 -0
  83. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.js.map +1 -0
  84. package/dist/stacks/mcp-rust/scaffold.d.ts +3 -0
  85. package/dist/stacks/mcp-rust/scaffold.d.ts.map +1 -0
  86. package/dist/stacks/mcp-rust/scaffold.js +98 -0
  87. package/dist/stacks/mcp-rust/scaffold.js.map +1 -0
  88. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.d.ts +2 -0
  89. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.d.ts.map +1 -0
  90. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.js +172 -0
  91. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.js.map +1 -0
  92. package/dist/stacks/node-nestjs/scaffold.d.ts +3 -0
  93. package/dist/stacks/node-nestjs/scaffold.d.ts.map +1 -0
  94. package/dist/stacks/node-nestjs/scaffold.js +117 -0
  95. package/dist/stacks/node-nestjs/scaffold.js.map +1 -0
  96. package/dist/stacks/php-laravel/__tests__/scaffold.spec.d.ts +2 -0
  97. package/dist/stacks/php-laravel/__tests__/scaffold.spec.d.ts.map +1 -0
  98. package/dist/stacks/php-laravel/__tests__/scaffold.spec.js +205 -0
  99. package/dist/stacks/php-laravel/__tests__/scaffold.spec.js.map +1 -0
  100. package/dist/stacks/php-laravel/scaffold.d.ts +3 -0
  101. package/dist/stacks/php-laravel/scaffold.d.ts.map +1 -0
  102. package/dist/stacks/php-laravel/scaffold.js +109 -0
  103. package/dist/stacks/php-laravel/scaffold.js.map +1 -0
  104. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.d.ts +2 -0
  105. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.d.ts.map +1 -0
  106. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.js +168 -0
  107. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.js.map +1 -0
  108. package/dist/stacks/python-fastapi/scaffold.d.ts +3 -0
  109. package/dist/stacks/python-fastapi/scaffold.d.ts.map +1 -0
  110. package/dist/stacks/python-fastapi/scaffold.js +108 -0
  111. package/dist/stacks/python-fastapi/scaffold.js.map +1 -0
  112. package/dist/stacks/registry.d.ts +38 -0
  113. package/dist/stacks/registry.d.ts.map +1 -0
  114. package/dist/stacks/registry.js +153 -0
  115. package/dist/stacks/registry.js.map +1 -0
  116. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.d.ts +6 -0
  117. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.d.ts.map +1 -0
  118. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.js +604 -0
  119. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.js.map +1 -0
  120. package/dist/stacks/ruby-rails-8/scaffold.d.ts +91 -0
  121. package/dist/stacks/ruby-rails-8/scaffold.d.ts.map +1 -0
  122. package/dist/stacks/ruby-rails-8/scaffold.js +410 -0
  123. package/dist/stacks/ruby-rails-8/scaffold.js.map +1 -0
  124. package/dist/stacks/rust-axum/__tests__/scaffold.spec.d.ts +2 -0
  125. package/dist/stacks/rust-axum/__tests__/scaffold.spec.d.ts.map +1 -0
  126. package/dist/stacks/rust-axum/__tests__/scaffold.spec.js +203 -0
  127. package/dist/stacks/rust-axum/__tests__/scaffold.spec.js.map +1 -0
  128. package/dist/stacks/rust-axum/scaffold.d.ts +3 -0
  129. package/dist/stacks/rust-axum/scaffold.d.ts.map +1 -0
  130. package/dist/stacks/rust-axum/scaffold.js +105 -0
  131. package/dist/stacks/rust-axum/scaffold.js.map +1 -0
  132. package/dist/stacks/template-engine.d.ts +38 -0
  133. package/dist/stacks/template-engine.d.ts.map +1 -0
  134. package/dist/stacks/template-engine.js +134 -0
  135. package/dist/stacks/template-engine.js.map +1 -0
  136. package/dist/stacks/types.d.ts +69 -0
  137. package/dist/stacks/types.d.ts.map +1 -0
  138. package/dist/stacks/types.js +29 -0
  139. package/dist/stacks/types.js.map +1 -0
  140. package/dist/utils/project-generator.d.ts +1 -1
  141. package/dist/utils/project-generator.d.ts.map +1 -1
  142. package/dist/utils/project-generator.js +16 -7
  143. package/dist/utils/project-generator.js.map +1 -1
  144. package/dist/utils/stack-bootstrap.d.ts +3 -2
  145. package/dist/utils/stack-bootstrap.d.ts.map +1 -1
  146. package/dist/utils/stack-bootstrap.js +46 -16
  147. package/dist/utils/stack-bootstrap.js.map +1 -1
  148. package/package.json +91 -87
  149. package/templates/stacks/go-gin/.dare/skills.yml +11 -0
  150. package/templates/stacks/go-gin/.env.example +24 -0
  151. package/templates/stacks/go-gin/.github/workflows/dare-ci.yml +42 -0
  152. package/templates/stacks/go-gin/README.md.tpl +38 -0
  153. package/templates/stacks/go-gin/cmd/server/main.go.tpl +78 -0
  154. package/templates/stacks/go-gin/db/migrations/0001_create_users.down.sql +2 -0
  155. package/templates/stacks/go-gin/db/migrations/0001_create_users.up.sql +12 -0
  156. package/templates/stacks/go-gin/db/queries/users.sql +23 -0
  157. package/templates/stacks/go-gin/gitignore +7 -0
  158. package/templates/stacks/go-gin/go.mod.tpl +17 -0
  159. package/templates/stacks/go-gin/internal/config/config.go +41 -0
  160. package/templates/stacks/go-gin/internal/db/postgres.go.tpl +25 -0
  161. package/templates/stacks/go-gin/internal/handler/auth_handler.go.tpl +72 -0
  162. package/templates/stacks/go-gin/internal/handler/users_handler.go.tpl +72 -0
  163. package/templates/stacks/go-gin/internal/handler/ws_handler.go +37 -0
  164. package/templates/stacks/go-gin/internal/llm/dummy.go +14 -0
  165. package/templates/stacks/go-gin/internal/llm/provider.go +8 -0
  166. package/templates/stacks/go-gin/internal/middleware/jwt.go.tpl +58 -0
  167. package/templates/stacks/go-gin/internal/middleware/rate_limit.go +55 -0
  168. package/templates/stacks/go-gin/internal/model/user.go +17 -0
  169. package/templates/stacks/go-gin/internal/repository/users_repository.go.tpl +79 -0
  170. package/templates/stacks/go-gin/internal/service/auth_service.go.tpl +55 -0
  171. package/templates/stacks/go-gin/internal/service/users_service.go.tpl +53 -0
  172. package/templates/stacks/go-gin/llms.txt.tpl +54 -0
  173. package/templates/stacks/go-gin/openapi.json.tpl +46 -0
  174. package/templates/stacks/go-gin/sqlc.yaml +14 -0
  175. package/templates/stacks/go-gin/tests/smoke_test.go.tpl +22 -0
  176. package/templates/stacks/go-stdlib/.dare/skills.yml +11 -0
  177. package/templates/stacks/go-stdlib/.env.example +24 -0
  178. package/templates/stacks/go-stdlib/.github/workflows/dare-ci.yml +42 -0
  179. package/templates/stacks/go-stdlib/README.md.tpl +41 -0
  180. package/templates/stacks/go-stdlib/cmd/server/main.go.tpl +82 -0
  181. package/templates/stacks/go-stdlib/db/migrations/0001_create_users.down.sql +2 -0
  182. package/templates/stacks/go-stdlib/db/migrations/0001_create_users.up.sql +12 -0
  183. package/templates/stacks/go-stdlib/db/queries/users.sql +23 -0
  184. package/templates/stacks/go-stdlib/gitignore +6 -0
  185. package/templates/stacks/go-stdlib/go.mod.tpl +15 -0
  186. package/templates/stacks/go-stdlib/internal/config/config.go +41 -0
  187. package/templates/stacks/go-stdlib/internal/db/postgres.go.tpl +24 -0
  188. package/templates/stacks/go-stdlib/internal/handler/auth_handler.go.tpl +71 -0
  189. package/templates/stacks/go-stdlib/internal/handler/users_handler.go.tpl +84 -0
  190. package/templates/stacks/go-stdlib/internal/handler/ws_handler.go +36 -0
  191. package/templates/stacks/go-stdlib/internal/httpx/json.go +32 -0
  192. package/templates/stacks/go-stdlib/internal/llm/dummy.go +14 -0
  193. package/templates/stacks/go-stdlib/internal/llm/provider.go +8 -0
  194. package/templates/stacks/go-stdlib/internal/middleware/chain.go +21 -0
  195. package/templates/stacks/go-stdlib/internal/middleware/cors.go +27 -0
  196. package/templates/stacks/go-stdlib/internal/middleware/jwt.go.tpl +51 -0
  197. package/templates/stacks/go-stdlib/internal/middleware/rate_limit.go +81 -0
  198. package/templates/stacks/go-stdlib/internal/model/user.go +17 -0
  199. package/templates/stacks/go-stdlib/internal/repository/users_repository.go.tpl +75 -0
  200. package/templates/stacks/go-stdlib/internal/service/auth_service.go.tpl +55 -0
  201. package/templates/stacks/go-stdlib/internal/service/users_service.go.tpl +53 -0
  202. package/templates/stacks/go-stdlib/llms.txt.tpl +60 -0
  203. package/templates/stacks/go-stdlib/openapi.json.tpl +46 -0
  204. package/templates/stacks/go-stdlib/sqlc.yaml +14 -0
  205. package/templates/stacks/go-stdlib/tests/smoke_test.go.tpl +45 -0
  206. package/templates/stacks/mcp-go/.dare/skills.yml +8 -0
  207. package/templates/stacks/mcp-go/.env.example +14 -0
  208. package/templates/stacks/mcp-go/.github/workflows/dare-ci.yml +42 -0
  209. package/templates/stacks/mcp-go/README.md.tpl +50 -0
  210. package/templates/stacks/mcp-go/cmd/server/main.go.tpl +62 -0
  211. package/templates/stacks/mcp-go/gitignore +6 -0
  212. package/templates/stacks/mcp-go/go.mod.tpl +9 -0
  213. package/templates/stacks/mcp-go/internal/prompts/summarize.go +9 -0
  214. package/templates/stacks/mcp-go/internal/server/server.go.tpl +80 -0
  215. package/templates/stacks/mcp-go/internal/tools/echo.go +15 -0
  216. package/templates/stacks/mcp-go/internal/transports/http.go.tpl +21 -0
  217. package/templates/stacks/mcp-go/internal/transports/sse.go.tpl +17 -0
  218. package/templates/stacks/mcp-go/internal/transports/stdio.go.tpl +14 -0
  219. package/templates/stacks/mcp-go/llms.txt.tpl +60 -0
  220. package/templates/stacks/mcp-go/openapi.json.tpl +31 -0
  221. package/templates/stacks/mcp-go/tests/echo_test.go.tpl +37 -0
  222. package/templates/stacks/mcp-node-ts/.dare/skills.yml +8 -0
  223. package/templates/stacks/mcp-node-ts/.env.example +16 -0
  224. package/templates/stacks/mcp-node-ts/.github/workflows/dare-ci.yml +54 -0
  225. package/templates/stacks/mcp-node-ts/README.md.hbs +49 -0
  226. package/templates/stacks/mcp-node-ts/gitignore +7 -0
  227. package/templates/stacks/mcp-node-ts/llms.txt.hbs +61 -0
  228. package/templates/stacks/mcp-node-ts/openapi.json.hbs +39 -0
  229. package/templates/stacks/mcp-node-ts/package.json.hbs +35 -0
  230. package/templates/stacks/mcp-node-ts/src/cli.ts.hbs +71 -0
  231. package/templates/stacks/mcp-node-ts/src/prompts/index.ts +36 -0
  232. package/templates/stacks/mcp-node-ts/src/server.ts.hbs +45 -0
  233. package/templates/stacks/mcp-node-ts/src/tools/echo.ts +23 -0
  234. package/templates/stacks/mcp-node-ts/src/tools/index.ts +18 -0
  235. package/templates/stacks/mcp-node-ts/src/transports/http.ts +68 -0
  236. package/templates/stacks/mcp-node-ts/src/transports/sse.ts +58 -0
  237. package/templates/stacks/mcp-node-ts/src/transports/stdio.ts +5 -0
  238. package/templates/stacks/mcp-node-ts/tests/echo.test.ts +50 -0
  239. package/templates/stacks/mcp-node-ts/tsconfig.json +17 -0
  240. package/templates/stacks/mcp-python/.dare/skills.yml +8 -0
  241. package/templates/stacks/mcp-python/.env.example +14 -0
  242. package/templates/stacks/mcp-python/.github/workflows/dare-ci.yml +42 -0
  243. package/templates/stacks/mcp-python/README.md.j2 +49 -0
  244. package/templates/stacks/mcp-python/gitignore +12 -0
  245. package/templates/stacks/mcp-python/llms.txt.j2 +56 -0
  246. package/templates/stacks/mcp-python/openapi.json.j2 +33 -0
  247. package/templates/stacks/mcp-python/pyproject.toml.j2 +37 -0
  248. package/templates/stacks/mcp-python/src/__init__.py +0 -0
  249. package/templates/stacks/mcp-python/src/cli.py.j2 +68 -0
  250. package/templates/stacks/mcp-python/src/prompts/__init__.py +0 -0
  251. package/templates/stacks/mcp-python/src/prompts/summarize.py +10 -0
  252. package/templates/stacks/mcp-python/src/server.py.j2 +28 -0
  253. package/templates/stacks/mcp-python/src/tools/__init__.py +0 -0
  254. package/templates/stacks/mcp-python/src/tools/echo.py +12 -0
  255. package/templates/stacks/mcp-python/src/transports/__init__.py +0 -0
  256. package/templates/stacks/mcp-python/src/transports/http.py +12 -0
  257. package/templates/stacks/mcp-python/src/transports/sse.py +13 -0
  258. package/templates/stacks/mcp-python/src/transports/stdio.py +6 -0
  259. package/templates/stacks/mcp-python/tests/__init__.py +0 -0
  260. package/templates/stacks/mcp-python/tests/test_echo.py +28 -0
  261. package/templates/stacks/mcp-rust/.dare/skills.yml +8 -0
  262. package/templates/stacks/mcp-rust/.env.example +14 -0
  263. package/templates/stacks/mcp-rust/.github/workflows/dare-ci.yml +38 -0
  264. package/templates/stacks/mcp-rust/Cargo.toml.tera +35 -0
  265. package/templates/stacks/mcp-rust/README.md.tera +50 -0
  266. package/templates/stacks/mcp-rust/gitignore +5 -0
  267. package/templates/stacks/mcp-rust/llms.txt.tera +60 -0
  268. package/templates/stacks/mcp-rust/openapi.json.tera +31 -0
  269. package/templates/stacks/mcp-rust/src/cli.rs.tera +33 -0
  270. package/templates/stacks/mcp-rust/src/lib.rs +6 -0
  271. package/templates/stacks/mcp-rust/src/main.rs.tera +30 -0
  272. package/templates/stacks/mcp-rust/src/prompts/mod.rs +1 -0
  273. package/templates/stacks/mcp-rust/src/prompts/summarize.rs +5 -0
  274. package/templates/stacks/mcp-rust/src/server.rs.tera +38 -0
  275. package/templates/stacks/mcp-rust/src/tools/echo.rs +18 -0
  276. package/templates/stacks/mcp-rust/src/tools/mod.rs +22 -0
  277. package/templates/stacks/mcp-rust/src/transports/http.rs +27 -0
  278. package/templates/stacks/mcp-rust/src/transports/mod.rs +3 -0
  279. package/templates/stacks/mcp-rust/src/transports/sse.rs +33 -0
  280. package/templates/stacks/mcp-rust/src/transports/stdio.rs +14 -0
  281. package/templates/stacks/mcp-rust/tests/echo_test.rs.tera +27 -0
  282. package/templates/stacks/node-nestjs/.dare/skills.yml +11 -0
  283. package/templates/stacks/node-nestjs/.env.example +21 -0
  284. package/templates/stacks/node-nestjs/.github/workflows/dare-ci.yml +54 -0
  285. package/templates/stacks/node-nestjs/README.md.hbs +35 -0
  286. package/templates/stacks/node-nestjs/gitignore +7 -0
  287. package/templates/stacks/node-nestjs/llms.txt.hbs +47 -0
  288. package/templates/stacks/node-nestjs/nest-cli.json +16 -0
  289. package/templates/stacks/node-nestjs/openapi.json.hbs +75 -0
  290. package/templates/stacks/node-nestjs/package.json.hbs +57 -0
  291. package/templates/stacks/node-nestjs/prisma/schema.prisma +25 -0
  292. package/templates/stacks/node-nestjs/prisma/seed.ts.hbs +25 -0
  293. package/templates/stacks/node-nestjs/src/app.module.ts +39 -0
  294. package/templates/stacks/node-nestjs/src/auth/auth.controller.ts +29 -0
  295. package/templates/stacks/node-nestjs/src/auth/auth.module.ts +25 -0
  296. package/templates/stacks/node-nestjs/src/auth/auth.service.ts +36 -0
  297. package/templates/stacks/node-nestjs/src/auth/dto/login-response.dto.ts +9 -0
  298. package/templates/stacks/node-nestjs/src/auth/dto/login.dto.ts +17 -0
  299. package/templates/stacks/node-nestjs/src/auth/jwt.strategy.ts +25 -0
  300. package/templates/stacks/node-nestjs/src/common/filters/problem-details.filter.ts +38 -0
  301. package/templates/stacks/node-nestjs/src/common/interceptors/json-response.interceptor.ts +13 -0
  302. package/templates/stacks/node-nestjs/src/main.ts.hbs +44 -0
  303. package/templates/stacks/node-nestjs/src/prisma/prisma.module.ts +9 -0
  304. package/templates/stacks/node-nestjs/src/prisma/prisma.service.ts +9 -0
  305. package/templates/stacks/node-nestjs/src/users/dto/create-user.dto.ts +22 -0
  306. package/templates/stacks/node-nestjs/src/users/dto/user.dto.ts +15 -0
  307. package/templates/stacks/node-nestjs/src/users/users.controller.ts +41 -0
  308. package/templates/stacks/node-nestjs/src/users/users.module.ts +11 -0
  309. package/templates/stacks/node-nestjs/src/users/users.repository.ts +38 -0
  310. package/templates/stacks/node-nestjs/src/users/users.service.ts +38 -0
  311. package/templates/stacks/node-nestjs/tsconfig.build.json +4 -0
  312. package/templates/stacks/node-nestjs/tsconfig.json +28 -0
  313. package/templates/stacks/php-laravel/.dare/skills.yml +11 -0
  314. package/templates/stacks/php-laravel/.env.example +41 -0
  315. package/templates/stacks/php-laravel/.github/workflows/dare-ci.yml +43 -0
  316. package/templates/stacks/php-laravel/README.md.hbs +36 -0
  317. package/templates/stacks/php-laravel/app/Http/Controllers/Api/AuthController.php +36 -0
  318. package/templates/stacks/php-laravel/app/Http/Controllers/Api/UsersController.php +33 -0
  319. package/templates/stacks/php-laravel/app/Http/Requests/CreateUserRequest.php +26 -0
  320. package/templates/stacks/php-laravel/app/Http/Requests/LoginRequest.php +34 -0
  321. package/templates/stacks/php-laravel/app/Llm/Contracts/LlmProvider.php +12 -0
  322. package/templates/stacks/php-laravel/app/Llm/Providers/DummyProvider.php +13 -0
  323. package/templates/stacks/php-laravel/app/Llm/Providers/OpenAiProvider.php +33 -0
  324. package/templates/stacks/php-laravel/app/Models/User.php +44 -0
  325. package/templates/stacks/php-laravel/app/Repositories/UsersRepository.php +32 -0
  326. package/templates/stacks/php-laravel/app/Services/AuthService.php +37 -0
  327. package/templates/stacks/php-laravel/app/Services/UsersService.php +57 -0
  328. package/templates/stacks/php-laravel/artisan +12 -0
  329. package/templates/stacks/php-laravel/bootstrap/app.php +29 -0
  330. package/templates/stacks/php-laravel/bootstrap/providers.php +5 -0
  331. package/templates/stacks/php-laravel/composer.json.hbs +58 -0
  332. package/templates/stacks/php-laravel/config/l5-swagger.php +41 -0
  333. package/templates/stacks/php-laravel/config/reverb.php +34 -0
  334. package/templates/stacks/php-laravel/config/sanctum.php +15 -0
  335. package/templates/stacks/php-laravel/database/migrations/2026_06_01_000001_create_users_table.php +27 -0
  336. package/templates/stacks/php-laravel/database/seeders/DatabaseSeeder.php +21 -0
  337. package/templates/stacks/php-laravel/gitignore +23 -0
  338. package/templates/stacks/php-laravel/llms.txt.hbs +53 -0
  339. package/templates/stacks/php-laravel/openapi.json.hbs +43 -0
  340. package/templates/stacks/php-laravel/phpstan.neon +9 -0
  341. package/templates/stacks/php-laravel/routes/api.php +13 -0
  342. package/templates/stacks/php-laravel/routes/channels.php +7 -0
  343. package/templates/stacks/php-laravel/tests/Feature/AuthTest.php +35 -0
  344. package/templates/stacks/php-laravel/tests/Feature/UsersTest.php +30 -0
  345. package/templates/stacks/php-laravel/tests/Pest.php +5 -0
  346. package/templates/stacks/python-fastapi/.dare/skills.yml +11 -0
  347. package/templates/stacks/python-fastapi/.env.example +21 -0
  348. package/templates/stacks/python-fastapi/.github/workflows/dare-ci.yml +43 -0
  349. package/templates/stacks/python-fastapi/README.md.j2 +35 -0
  350. package/templates/stacks/python-fastapi/alembic/env.py +46 -0
  351. package/templates/stacks/python-fastapi/alembic/script.py.mako +26 -0
  352. package/templates/stacks/python-fastapi/alembic/versions/0001_create_users.py.j2 +37 -0
  353. package/templates/stacks/python-fastapi/alembic.ini.j2 +39 -0
  354. package/templates/stacks/python-fastapi/app/__init__.py +0 -0
  355. package/templates/stacks/python-fastapi/app/core/__init__.py +0 -0
  356. package/templates/stacks/python-fastapi/app/core/config.py +24 -0
  357. package/templates/stacks/python-fastapi/app/core/security.py +34 -0
  358. package/templates/stacks/python-fastapi/app/db/__init__.py +0 -0
  359. package/templates/stacks/python-fastapi/app/db/session.py +22 -0
  360. package/templates/stacks/python-fastapi/app/main.py.j2 +36 -0
  361. package/templates/stacks/python-fastapi/app/models/__init__.py +3 -0
  362. package/templates/stacks/python-fastapi/app/models/user.py +30 -0
  363. package/templates/stacks/python-fastapi/app/repositories/__init__.py +0 -0
  364. package/templates/stacks/python-fastapi/app/repositories/user_repository.py +34 -0
  365. package/templates/stacks/python-fastapi/app/routers/__init__.py +0 -0
  366. package/templates/stacks/python-fastapi/app/routers/auth.py +37 -0
  367. package/templates/stacks/python-fastapi/app/routers/users.py +46 -0
  368. package/templates/stacks/python-fastapi/app/schemas/__init__.py +0 -0
  369. package/templates/stacks/python-fastapi/app/schemas/user.py +56 -0
  370. package/templates/stacks/python-fastapi/app/services/__init__.py +0 -0
  371. package/templates/stacks/python-fastapi/app/services/auth_service.py +22 -0
  372. package/templates/stacks/python-fastapi/app/services/user_service.py +31 -0
  373. package/templates/stacks/python-fastapi/gitignore +12 -0
  374. package/templates/stacks/python-fastapi/llms.txt.j2 +53 -0
  375. package/templates/stacks/python-fastapi/openapi.json.j2 +43 -0
  376. package/templates/stacks/python-fastapi/pyproject.toml.j2 +45 -0
  377. package/templates/stacks/python-fastapi/tests/__init__.py +0 -0
  378. package/templates/stacks/python-fastapi/tests/test_auth.py +22 -0
  379. package/templates/stacks/ruby-rails-8/.dare/skills.yml +50 -0
  380. package/templates/stacks/ruby-rails-8/.env.example +20 -0
  381. package/templates/stacks/ruby-rails-8/.github/workflows/dare-ci.yml +112 -0
  382. package/templates/stacks/ruby-rails-8/Gemfile.erb +61 -0
  383. package/templates/stacks/ruby-rails-8/app/channels/application_cable/channel.rb +11 -0
  384. package/templates/stacks/ruby-rails-8/app/channels/application_cable/connection.rb +34 -0
  385. package/templates/stacks/ruby-rails-8/app/channels/dare_updates_channel.rb +18 -0
  386. package/templates/stacks/ruby-rails-8/app/channels/user_updates_channel.rb +23 -0
  387. package/templates/stacks/ruby-rails-8/app/controllers/application_controller.rb +44 -0
  388. package/templates/stacks/ruby-rails-8/app/controllers/concerns/problem_details.rb +93 -0
  389. package/templates/stacks/ruby-rails-8/app/handlers/summarize_handler.rb +33 -0
  390. package/templates/stacks/ruby-rails-8/app/handlers/users_handler.rb +68 -0
  391. package/templates/stacks/ruby-rails-8/app/llm/cache/llm_cache.rb +44 -0
  392. package/templates/stacks/ruby-rails-8/app/llm/prompts/prompt_loader.rb +54 -0
  393. package/templates/stacks/ruby-rails-8/app/llm/prompts/summarize_v1.jinja2 +12 -0
  394. package/templates/stacks/ruby-rails-8/app/llm/providers/dummy_provider.rb +35 -0
  395. package/templates/stacks/ruby-rails-8/app/llm/providers/llm_provider.rb +67 -0
  396. package/templates/stacks/ruby-rails-8/app/llm/providers/openai_provider.rb +62 -0
  397. package/templates/stacks/ruby-rails-8/app/llm/rate_limit/token_bucket.rb +82 -0
  398. package/templates/stacks/ruby-rails-8/app/llm/validators/summarize_output_schema.json +21 -0
  399. package/templates/stacks/ruby-rails-8/app/llm/validators/validator.rb +52 -0
  400. package/templates/stacks/ruby-rails-8/app/models/user.rb +36 -0
  401. package/templates/stacks/ruby-rails-8/app/presenters/user_presenter.rb +48 -0
  402. package/templates/stacks/ruby-rails-8/app/repositories/document_repository.rb +57 -0
  403. package/templates/stacks/ruby-rails-8/app/repositories/user_repository.rb +73 -0
  404. package/templates/stacks/ruby-rails-8/app/services/create_user_service.rb +67 -0
  405. package/templates/stacks/ruby-rails-8/app/services/realtime_service.rb +53 -0
  406. package/templates/stacks/ruby-rails-8/app/services/summarize_document_service.rb +57 -0
  407. package/templates/stacks/ruby-rails-8/config/dare.yml +42 -0
  408. package/templates/stacks/ruby-rails-8/config/initializers/dare.rb +31 -0
  409. package/templates/stacks/ruby-rails-8/config/initializers/rack_attack.rb +64 -0
  410. package/templates/stacks/ruby-rails-8/config/initializers/rswag_api.rb +12 -0
  411. package/templates/stacks/ruby-rails-8/lib/tasks/dare.rake +159 -0
  412. package/templates/stacks/ruby-rails-8/llms.txt.erb +69 -0
  413. package/templates/stacks/ruby-rails-8/spec/api/summarize_spec.rb +56 -0
  414. package/templates/stacks/ruby-rails-8/spec/api/users_spec.rb +72 -0
  415. package/templates/stacks/ruby-rails-8/spec/channels/dare_updates_channel_spec.rb +61 -0
  416. package/templates/stacks/ruby-rails-8/spec/channels/user_updates_channel_spec.rb +56 -0
  417. package/templates/stacks/ruby-rails-8/spec/factories/users.rb +27 -0
  418. package/templates/stacks/ruby-rails-8/spec/handlers/users_handler_spec.rb +88 -0
  419. package/templates/stacks/ruby-rails-8/spec/rails_helper.rb +31 -0
  420. package/templates/stacks/ruby-rails-8/spec/services/create_user_service_spec.rb +88 -0
  421. package/templates/stacks/ruby-rails-8/spec/services/summarize_document_service_spec.rb +142 -0
  422. package/templates/stacks/ruby-rails-8/spec/swagger_helper.rb +73 -0
  423. package/templates/stacks/rust-axum/.dare/skills.yml +11 -0
  424. package/templates/stacks/rust-axum/.env.example +26 -0
  425. package/templates/stacks/rust-axum/.github/workflows/dare-ci.yml +40 -0
  426. package/templates/stacks/rust-axum/Cargo.toml.tera +53 -0
  427. package/templates/stacks/rust-axum/README.md.tera +37 -0
  428. package/templates/stacks/rust-axum/gitignore +5 -0
  429. package/templates/stacks/rust-axum/llms.txt.tera +54 -0
  430. package/templates/stacks/rust-axum/migrations/0001_create_users.sql +13 -0
  431. package/templates/stacks/rust-axum/openapi.json.tera +46 -0
  432. package/templates/stacks/rust-axum/src/config.rs +45 -0
  433. package/templates/stacks/rust-axum/src/errors.rs +48 -0
  434. package/templates/stacks/rust-axum/src/handlers/auth.rs +48 -0
  435. package/templates/stacks/rust-axum/src/handlers/mod.rs +3 -0
  436. package/templates/stacks/rust-axum/src/handlers/users.rs +81 -0
  437. package/templates/stacks/rust-axum/src/handlers/ws.rs +24 -0
  438. package/templates/stacks/rust-axum/src/lib.rs +19 -0
  439. package/templates/stacks/rust-axum/src/llm/mod.rs +1 -0
  440. package/templates/stacks/rust-axum/src/llm/provider.rs +48 -0
  441. package/templates/stacks/rust-axum/src/main.rs.tera +64 -0
  442. package/templates/stacks/rust-axum/src/middleware/auth.rs +20 -0
  443. package/templates/stacks/rust-axum/src/middleware/mod.rs +2 -0
  444. package/templates/stacks/rust-axum/src/middleware/rate_limit.rs +27 -0
  445. package/templates/stacks/rust-axum/src/models/mod.rs +1 -0
  446. package/templates/stacks/rust-axum/src/models/user.rs +13 -0
  447. package/templates/stacks/rust-axum/src/repositories/mod.rs +1 -0
  448. package/templates/stacks/rust-axum/src/repositories/user_repository.rs +62 -0
  449. package/templates/stacks/rust-axum/src/services/auth_service.rs +50 -0
  450. package/templates/stacks/rust-axum/src/services/mod.rs +2 -0
  451. package/templates/stacks/rust-axum/src/services/user_service.rs +53 -0
  452. package/templates/stacks/rust-axum/tests/integration_test.rs.tera +13 -0
  453. package/LICENSE +0 -21
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — User domain model
4
+ # Convention (ADR-05): models are lean data objects — no callbacks, no fat model
5
+ # Business logic lives in app/services/; data access in app/repositories/
6
+
7
+ class User < ApplicationRecord
8
+ # ── Validations ─────────────────────────────────────────────────────────
9
+ validates :email, presence: true,
10
+ uniqueness: { case_sensitive: false },
11
+ format: { with: URI::MailTo::EMAIL_REGEXP }
12
+ validates :name, presence: true, length: { minimum: 2, maximum: 100 }
13
+
14
+ # ── Scopes ───────────────────────────────────────────────────────────────
15
+ scope :active, -> { where(active: true) }
16
+
17
+ # ── DARE: NO callbacks, NO fat model logic ───────────────────────────────
18
+ # Use Services for: password hashing, welcome emails, event publishing, etc.
19
+ # Use Repositories for: queries, bulk updates, custom SQL
20
+ #
21
+ # WRONG (fat model):
22
+ # before_create :send_welcome_email
23
+ # after_save :publish_event
24
+ #
25
+ # RIGHT (service):
26
+ # Services::CreateUserService.new(...).execute(params)
27
+
28
+ # ── Simple domain helpers (pure data concerns only) ──────────────────────
29
+ def display_name
30
+ name.presence || email.split("@").first
31
+ end
32
+
33
+ def can_view?(user_id)
34
+ id == user_id.to_i || admin?
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — UserPresenter
4
+ # Convention (ADR-05): serialization concern isolated from model and controller
5
+ # Returns plain Hash; handler renders JSON from it
6
+
7
+ class UserPresenter
8
+ def initialize(user)
9
+ @user = user
10
+ end
11
+
12
+ # Full representation — used in show/create responses
13
+ def as_json(*)
14
+ {
15
+ id: @user.id,
16
+ email: @user.email,
17
+ name: @user.name,
18
+ active: @user.active?,
19
+ created_at: @user.created_at&.iso8601,
20
+ updated_at: @user.updated_at&.iso8601,
21
+ }
22
+ end
23
+
24
+ def to_json(*)
25
+ as_json.to_json
26
+ end
27
+
28
+ # Collection representation — used in index responses
29
+ # Wraps an array of presenters into a standard envelope
30
+ def self.collection(users, meta: {})
31
+ {
32
+ data: users.map { |u| new(u).as_json },
33
+ meta: {
34
+ count: users.size,
35
+ **meta
36
+ }
37
+ }
38
+ end
39
+
40
+ # Minimal summary representation (e.g. for nested resources)
41
+ def summary
42
+ {
43
+ id: @user.id,
44
+ email: @user.email,
45
+ name: @user.name
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — DocumentRepository (interface + ActiveRecord implementation)
4
+ # Convention (ADR-05): data access is isolated here — services never call AR directly
5
+ # This enables easy mocking in unit tests and future DB swaps
6
+
7
+ module Repositories
8
+ class DocumentRepository
9
+ # ── Query ──────────────────────────────────────────────────────────────
10
+
11
+ def find(id)
12
+ Document.find_by(id: id)
13
+ end
14
+
15
+ def find!(id)
16
+ Document.find(id)
17
+ rescue ActiveRecord::RecordNotFound
18
+ raise ActiveRecord::RecordNotFound, "Document with id=#{id} was not found."
19
+ end
20
+
21
+ def all
22
+ Document.order(created_at: :desc)
23
+ end
24
+
25
+ def by_user(user_id)
26
+ Document.where(user_id: user_id).order(created_at: :desc)
27
+ end
28
+
29
+ # ── Persistence ────────────────────────────────────────────────────────
30
+
31
+ # Builds a new Document (not persisted).
32
+ def build(attrs = {})
33
+ Document.new(attrs)
34
+ end
35
+
36
+ # Persist (create or update). Raises ActiveRecord::RecordInvalid on failure.
37
+ def save!(document)
38
+ document.save!
39
+ document
40
+ end
41
+
42
+ # Update summary and timestamp after LLM processing.
43
+ def update_summary!(document, summary)
44
+ document.update!(summary: summary, summarized_at: Time.current)
45
+ document
46
+ end
47
+
48
+ def update!(document, attributes)
49
+ document.update!(attributes)
50
+ document
51
+ end
52
+
53
+ def destroy!(document)
54
+ document.destroy!
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — UserRepository
4
+ # Convention (ADR-05): data access is isolated here — services never call AR directly
5
+ # This enables easy mocking in unit tests and future DB swaps
6
+
7
+ module Repositories
8
+ class UserRepository
9
+ # ── Query ──────────────────────────────────────────────────────────────
10
+
11
+ def find(id)
12
+ User.find_by(id: id)
13
+ end
14
+
15
+ def find!(id)
16
+ User.find(id)
17
+ rescue ActiveRecord::RecordNotFound
18
+ raise ActiveRecord::RecordNotFound, "User with id=#{id} was not found."
19
+ end
20
+
21
+ def find_by_email(email)
22
+ User.find_by(email: email.downcase.strip)
23
+ end
24
+
25
+ def all_active
26
+ User.active.order(created_at: :desc)
27
+ end
28
+
29
+ def paginated(page: 1, per: 25)
30
+ User.active
31
+ .order(created_at: :desc)
32
+ .offset((page.to_i - 1) * per.to_i)
33
+ .limit(per.to_i)
34
+ end
35
+
36
+ def count
37
+ User.count
38
+ end
39
+
40
+ # ── Persistence ────────────────────────────────────────────────────────
41
+
42
+ # Builds a new User (not persisted). Let the service validate input first.
43
+ def build(attributes = {})
44
+ User.new(attributes)
45
+ end
46
+
47
+ # Persist (create or update). Raises ActiveRecord::RecordInvalid on failure.
48
+ def save!(user)
49
+ user.save!
50
+ user
51
+ end
52
+
53
+ # Persist without raising. Returns false on failure.
54
+ def save(user)
55
+ user.save
56
+ end
57
+
58
+ def update!(user, attributes)
59
+ user.update!(attributes)
60
+ user
61
+ end
62
+
63
+ def destroy!(user)
64
+ user.destroy!
65
+ end
66
+
67
+ # ── Bulk ───────────────────────────────────────────────────────────────
68
+
69
+ def deactivate_all_inactive(since:)
70
+ User.where("last_active_at < ?", since).update_all(active: false)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — CreateUserService
4
+ # Convention (ADR-01): one service per operation; injected dependencies for testability
5
+ #
6
+ # Usage:
7
+ # result = Services::CreateUserService.new(
8
+ # user_repository: Repositories::UserRepository.new,
9
+ # event_publisher: RealtimeService.instance
10
+ # ).execute(email: "user@example.com", name: "Alice")
11
+ #
12
+ # Returns: User (persisted)
13
+ # Raises: Services::CreateUserService::EmailTakenError
14
+ # ActiveRecord::RecordInvalid (validation failure)
15
+
16
+ module Services
17
+ class CreateUserService
18
+ # ── Domain errors ──────────────────────────────────────────────────────
19
+ class EmailTakenError < StandardError
20
+ def initialize(email)
21
+ super("Email '#{email}' is already registered.")
22
+ end
23
+ end
24
+
25
+ # ── Constructor ────────────────────────────────────────────────────────
26
+ def initialize(user_repository:, event_publisher:)
27
+ @user_repository = user_repository
28
+ @event_publisher = event_publisher
29
+ end
30
+
31
+ # ── Main operation ─────────────────────────────────────────────────────
32
+ def execute(email:, name:, password: nil, **extra_attrs)
33
+ email = email.to_s.downcase.strip
34
+
35
+ # Guard: check for duplicate email
36
+ if @user_repository.find_by_email(email)
37
+ raise EmailTakenError, email
38
+ end
39
+
40
+ # Build and persist
41
+ user = @user_repository.build(
42
+ email: email,
43
+ name: name.to_s.strip,
44
+ active: true,
45
+ **extra_attrs
46
+ )
47
+
48
+ # Hash password if provided (example using has_secure_password)
49
+ user.password = password if password.present?
50
+
51
+ @user_repository.save!(user)
52
+
53
+ # Publish real-time event
54
+ @event_publisher.publish(
55
+ type: "user.created",
56
+ data: {
57
+ user_id: user.id,
58
+ email: user.email,
59
+ name: user.name,
60
+ timestamp: Time.now.to_i
61
+ }
62
+ )
63
+
64
+ user
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — RealtimeService
4
+ # Convention (ADR-03): centralized broadcaster — services call this, not channels directly
5
+ # Singleton via .instance; injectable in tests via double
6
+ #
7
+ # Usage in a Service:
8
+ # RealtimeService.instance.publish(
9
+ # type: "user.created",
10
+ # data: { user_id: 42, email: "user@example.com" }
11
+ # )
12
+ #
13
+ # Frontend receives:
14
+ # { type: "user.created", data: { ... }, timestamp: 1234567890 }
15
+
16
+ class RealtimeService
17
+ include Singleton
18
+
19
+ # ── Public API ────────────────────────────────────────────────────────────
20
+
21
+ # Broadcast to all subscribers of a channel stream.
22
+ # channel_class: e.g. UserUpdatesChannel
23
+ # target: e.g. user.id (matched against stream_for in the channel)
24
+ # payload: Hash with :type and :data
25
+ def broadcast_to(channel_class, target, type:, data: {})
26
+ event = build_event(type: type, data: data)
27
+ channel_class.broadcast_to(target, event)
28
+ end
29
+
30
+ # Generic publish: broadcasts to `dare_updates:<user_id>` for all active users.
31
+ # Use for system-wide events.
32
+ def publish(type:, data: {}, user_id: nil)
33
+ event = build_event(type: type, data: data)
34
+ stream = user_id ? "dare_updates:#{user_id}" : "dare_updates:global"
35
+ ActionCable.server.broadcast(stream, event)
36
+ end
37
+
38
+ # Publish to a named stream directly (lower-level).
39
+ def broadcast_to_stream(stream_name, type:, data: {})
40
+ event = build_event(type: type, data: data)
41
+ ActionCable.server.broadcast(stream_name, event)
42
+ end
43
+
44
+ private
45
+
46
+ def build_event(type:, data: {})
47
+ {
48
+ type: type,
49
+ data: data,
50
+ timestamp: Time.now.to_i
51
+ }
52
+ end
53
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — SummarizeDocumentService
4
+ # Convention (ADR-01): single responsibility — load document, call LLM, persist summary, emit event
5
+ #
6
+ # Usage:
7
+ # result = Services::SummarizeDocumentService.new(
8
+ # document_repository: Repositories::DocumentRepository.new,
9
+ # llm_provider: LLM::Providers::LLMProvider.instance,
10
+ # event_publisher: RealtimeService.instance
11
+ # ).execute(document_id: "abc-123", user_id: 42)
12
+ #
13
+ # Returns: SummarizeDocumentService::Result (summary, document_id)
14
+ # Raises: Services::SummarizeDocumentService::DocumentNotFoundError
15
+ # Services::SummarizeDocumentService::SummarizationError
16
+
17
+ module Services
18
+ class SummarizeDocumentService
19
+ # ── Domain errors ──────────────────────────────────────────────────────
20
+ DocumentNotFoundError = Class.new(StandardError)
21
+ SummarizationError = Class.new(StandardError)
22
+
23
+ # ── Result ─────────────────────────────────────────────────────────────
24
+ Result = Struct.new(:summary, :document_id, keyword_init: true)
25
+
26
+ # ── Constructor ────────────────────────────────────────────────────────
27
+ def initialize(document_repository:, llm_provider:, event_publisher:)
28
+ @document_repository = document_repository
29
+ @llm_provider = llm_provider
30
+ @event_publisher = event_publisher
31
+ end
32
+
33
+ # ── Main operation ─────────────────────────────────────────────────────
34
+ def execute(document_id:, user_id:)
35
+ doc = @document_repository.find(document_id)
36
+ raise DocumentNotFoundError, "Document #{document_id} not found" if doc.nil?
37
+
38
+ prompt = LLM::Prompts::PromptLoader.load("summarize_v1", content: doc.content, language: "pt-BR")
39
+ summary = @llm_provider.complete(model: "gpt-4o-mini", prompt: prompt)
40
+
41
+ raise SummarizationError, "LLM returned empty summary" if summary.blank?
42
+
43
+ @document_repository.update_summary!(doc, summary)
44
+
45
+ @event_publisher.publish(
46
+ type: "document.summarized",
47
+ data: {
48
+ document_id: document_id,
49
+ user_id: user_id,
50
+ summary_length: summary.length
51
+ }
52
+ )
53
+
54
+ Result.new(summary: summary, document_id: document_id)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ # DARE v3.0 — Application configuration
2
+ # Access in Ruby via: Rails.configuration.dare
3
+ # Environment-specific settings override the "default" block.
4
+
5
+ default: &default
6
+ llm:
7
+ provider: dummy # dummy | openai | local_llama
8
+ model: gpt-4o
9
+ # api_key: set via OPENAI_API_KEY env var (never in source)
10
+
11
+ rate_limit:
12
+ rpm: 100 # requests per minute per IP (public API)
13
+ auth_rpm: 10 # requests per minute per IP (auth endpoints)
14
+ llm_rpm: 20 # requests per minute per user (LLM endpoints)
15
+
16
+ openapi:
17
+ enabled: true
18
+ output_path: public/openapi.json
19
+ title: "<%= app_name %> API"
20
+ version: "1.0.0"
21
+ description: "REST API built with Rails 8 + DARE v3.0"
22
+
23
+ realtime:
24
+ backend: action_cable # action_cable | sucker_punch
25
+ redis_url: <%= ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') %>
26
+
27
+ development:
28
+ <<: *default
29
+ llm:
30
+ provider: dummy
31
+
32
+ test:
33
+ <<: *default
34
+ llm:
35
+ provider: dummy
36
+ rate_limit:
37
+ rpm: 10000 # effectively unlimited in tests
38
+
39
+ production:
40
+ <<: *default
41
+ llm:
42
+ provider: openai # set OPENAI_API_KEY in environment
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — Central configuration initializer
4
+ # All DARE runtime settings are loaded from config/dare.yml
5
+ # Access via: Rails.configuration.dare
6
+
7
+ dare_config_path = Rails.root.join("config", "dare.yml")
8
+
9
+ if dare_config_path.exist?
10
+ raw = YAML.safe_load_file(dare_config_path, symbolize_names: false)
11
+ env_config = raw.dig(Rails.env) || raw["default"] || {}
12
+
13
+ Rails.configuration.dare = OpenStruct.new(
14
+ llm_provider: env_config.dig("llm", "provider") || "dummy",
15
+ llm_model: env_config.dig("llm", "model") || "gpt-4o",
16
+ llm_api_key: ENV.fetch("OPENAI_API_KEY", nil),
17
+ redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"),
18
+ rate_limit_rpm: env_config.dig("rate_limit", "rpm") || 100,
19
+ metrics_path: Rails.root.join("tmp", "dare_metrics.json")
20
+ )
21
+ else
22
+ Rails.logger.warn "[DARE] config/dare.yml not found — using defaults"
23
+ Rails.configuration.dare = OpenStruct.new(
24
+ llm_provider: "dummy",
25
+ llm_model: "gpt-4o",
26
+ llm_api_key: ENV.fetch("OPENAI_API_KEY", nil),
27
+ redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"),
28
+ rate_limit_rpm: 100,
29
+ metrics_path: Rails.root.join("tmp", "dare_metrics.json")
30
+ )
31
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — Rack::Attack configuration
4
+ # Satisfies metric M-04: 100% of public endpoints have rate limit configured
5
+ #
6
+ # D-006 (RFC 7807): throttle responses use Problem Details format
7
+
8
+ class Rack::Attack
9
+ # ── Safelist ───────────────────────────────────────────────────────────────
10
+ # Always allow localhost in development
11
+ safelist("allow-localhost") do |req|
12
+ req.ip == "127.0.0.1" || req.ip == "::1" if Rails.env.development?
13
+ end
14
+
15
+ # ── Public API throttle ───────────────────────────────────────────────────
16
+ # M-04: All public endpoints limited to 100 req/min per IP
17
+ throttle("public-api/ip", limit: 100, period: 1.minute) do |req|
18
+ req.ip if req.path.start_with?("/api/")
19
+ end
20
+
21
+ # ── Auth endpoints throttle ───────────────────────────────────────────────
22
+ # 10 req/min per IP for sensitive auth endpoints
23
+ throttle("auth/ip", limit: 10, period: 1.minute) do |req|
24
+ req.ip if req.path =~ %r{\A/api/(auth|sessions|passwords)}
25
+ end
26
+
27
+ # ── LLM endpoints throttle ───────────────────────────────────────────────
28
+ # 20 req/min per authenticated user for LLM-heavy endpoints
29
+ throttle("llm/user", limit: 20, period: 1.minute) do |req|
30
+ req.env["HTTP_X_USER_ID"] if req.path.start_with?("/api/") && req.env["HTTP_X_USER_ID"]
31
+ end
32
+
33
+ # ── Response — RFC 7807 Problem Details ───────────────────────────────────
34
+ self.throttled_responder = lambda do |env|
35
+ match_data = env["rack.attack.match_data"]
36
+ now = match_data[:epoch_time]
37
+ retry_at = now + (match_data[:period] - now % match_data[:period])
38
+
39
+ headers = {
40
+ "Content-Type" => "application/problem+json",
41
+ "Retry-After" => (retry_at - now).to_s,
42
+ "X-RateLimit-Limit" => match_data[:limit].to_s,
43
+ "X-RateLimit-Remaining" => "0",
44
+ "X-RateLimit-Reset" => retry_at.to_s,
45
+ }
46
+
47
+ body = {
48
+ type: "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429",
49
+ title: "Too Many Requests",
50
+ status: 429,
51
+ detail: "Rate limit exceeded. Please retry after #{retry_at - now} seconds.",
52
+ instance: env["PATH_INFO"],
53
+ }.to_json
54
+
55
+ [429, headers, [body]]
56
+ end
57
+ end
58
+
59
+ # Use Redis for distributed rate limiting (multi-server setups)
60
+ if defined?(Redis) && ENV["REDIS_URL"]
61
+ Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(
62
+ url: ENV["REDIS_URL"]
63
+ )
64
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — rswag API configuration
4
+ # Satisfies metric M-02: 100% of endpoints have OpenAPI documentation
5
+
6
+ Rswag::Api.configure do |c|
7
+ # Path to the generated openapi.json spec file
8
+ c.openapi_root = Rails.root.join("public").to_s
9
+
10
+ # Authorize all endpoints in Swagger UI with Bearer token by default
11
+ c.swagger_filter = lambda { |swagger, env| swagger }
12
+ end