@aurostack/stacks 0.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 (601) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/cli/src/extract.mjs +165 -0
  4. package/cli/src/fsx.mjs +197 -0
  5. package/cli/src/generate.mjs +235 -0
  6. package/cli/src/hooks.mjs +55 -0
  7. package/cli/src/manifest.mjs +163 -0
  8. package/cli/src/strip.mjs +208 -0
  9. package/cli/src/tokens.mjs +102 -0
  10. package/cli/stack.mjs +672 -0
  11. package/package.json +37 -0
  12. package/templates/nest-api/files/.bin/db.ts +123 -0
  13. package/templates/nest-api/files/.bin/dc.ts +74 -0
  14. package/templates/nest-api/files/.bin/helpers.ts +49 -0
  15. package/templates/nest-api/files/.bin/secrets.ts +68 -0
  16. package/templates/nest-api/files/.bin/start.ts +47 -0
  17. package/templates/nest-api/files/.dockerignore +7 -0
  18. package/templates/nest-api/files/.env.example +91 -0
  19. package/templates/nest-api/files/.env.test.example +75 -0
  20. package/templates/nest-api/files/.forgejo/workflows/build-and-push.yml +41 -0
  21. package/templates/nest-api/files/.forgejo/workflows/test-e2e.yml +83 -0
  22. package/templates/nest-api/files/.forgejo/workflows/test.yml +53 -0
  23. package/templates/nest-api/files/.npmrc.example +1 -0
  24. package/templates/nest-api/files/.nvmrc +1 -0
  25. package/templates/nest-api/files/.prettierrc +8 -0
  26. package/templates/nest-api/files/.swcrc +15 -0
  27. package/templates/nest-api/files/.watchmanconfig +3 -0
  28. package/templates/nest-api/files/.yarnrc.yml +1 -0
  29. package/templates/nest-api/files/Dockerfile +40 -0
  30. package/templates/nest-api/files/__mocks__/activation-twofa.js +7 -0
  31. package/templates/nest-api/files/__mocks__/activation.js +7 -0
  32. package/templates/nest-api/files/__mocks__/forgot-password.js +7 -0
  33. package/templates/nest-api/files/__mocks__/main-layout.js +7 -0
  34. package/templates/nest-api/files/__mocks__/password-changed.js +7 -0
  35. package/templates/nest-api/files/__mocks__/reset-password.js +7 -0
  36. package/templates/nest-api/files/__mocks__/set-password.js +7 -0
  37. package/templates/nest-api/files/__mocks__/welcome.js +7 -0
  38. package/templates/nest-api/files/_gitignore +62 -0
  39. package/templates/nest-api/files/compose.test.yml +33 -0
  40. package/templates/nest-api/files/compose.yml +39 -0
  41. package/templates/nest-api/files/emails/activation-twofa.tsx +58 -0
  42. package/templates/nest-api/files/emails/activation.tsx +36 -0
  43. package/templates/nest-api/files/emails/forgot-password.tsx +29 -0
  44. package/templates/nest-api/files/emails/main-layout.tsx +134 -0
  45. package/templates/nest-api/files/emails/password-changed.tsx +24 -0
  46. package/templates/nest-api/files/emails/reset-password.tsx +24 -0
  47. package/templates/nest-api/files/emails/set-password.tsx +25 -0
  48. package/templates/nest-api/files/emails/welcome.tsx +39 -0
  49. package/templates/nest-api/files/eslint.config.mjs +28 -0
  50. package/templates/nest-api/files/global.d.ts +5 -0
  51. package/templates/nest-api/files/nest-cli.json +8 -0
  52. package/templates/nest-api/files/package.json +150 -0
  53. package/templates/nest-api/files/prisma/schema/auth.prisma +75 -0
  54. package/templates/nest-api/files/prisma/schema/base.prisma +17 -0
  55. package/templates/nest-api/files/prisma/schema/enum.prisma +17 -0
  56. package/templates/nest-api/files/prisma/schema/notification.prisma +14 -0
  57. package/templates/nest-api/files/prisma.config.ts +13 -0
  58. package/templates/nest-api/files/public/.gitkeep +0 -0
  59. package/templates/nest-api/files/seeders/index.ts +27 -0
  60. package/templates/nest-api/files/seeders/util.ts +88 -0
  61. package/templates/nest-api/files/src/__tests__/app.controller.spec.ts +29 -0
  62. package/templates/nest-api/files/src/app.controller.ts +40 -0
  63. package/templates/nest-api/files/src/app.module.ts +268 -0
  64. package/templates/nest-api/files/src/app.resolver.ts +32 -0
  65. package/templates/nest-api/files/src/app.setup.ts +178 -0
  66. package/templates/nest-api/files/src/common/__tests__/auth.service.spec.ts +31 -0
  67. package/templates/nest-api/files/src/common/__tests__/cache.service.spec.ts +214 -0
  68. package/templates/nest-api/files/src/common/__tests__/config.service.spec.ts +56 -0
  69. package/templates/nest-api/files/src/common/__tests__/generator.service.spec.ts +114 -0
  70. package/templates/nest-api/files/src/common/__tests__/health.controller.spec.ts +72 -0
  71. package/templates/nest-api/files/src/common/__tests__/helpers.spec.ts +124 -0
  72. package/templates/nest-api/files/src/common/__tests__/mail.service.spec.ts +75 -0
  73. package/templates/nest-api/files/src/common/__tests__/prisma.indicator.spec.ts +39 -0
  74. package/templates/nest-api/files/src/common/__tests__/prisma.service.spec.ts +15 -0
  75. package/templates/nest-api/files/src/common/__tests__/records.spec.ts +87 -0
  76. package/templates/nest-api/files/src/common/__tests__/schemas.spec.ts +142 -0
  77. package/templates/nest-api/files/src/common/__tests__/throttler-storage.service.spec.ts +80 -0
  78. package/templates/nest-api/files/src/common/common.module.ts +72 -0
  79. package/templates/nest-api/files/src/common/constants/index.ts +2 -0
  80. package/templates/nest-api/files/src/common/constants/options.ts +52 -0
  81. package/templates/nest-api/files/src/common/constants/pino.ts +24 -0
  82. package/templates/nest-api/files/src/common/controllers/health.controller.ts +54 -0
  83. package/templates/nest-api/files/src/common/controllers/index.ts +2 -0
  84. package/templates/nest-api/files/src/common/controllers/metrics.controller.ts +21 -0
  85. package/templates/nest-api/files/src/common/decorators/index.ts +2 -0
  86. package/templates/nest-api/files/src/common/decorators/misc.decorator.ts +49 -0
  87. package/templates/nest-api/files/src/common/decorators/multi-part.decorator.ts +6 -0
  88. package/templates/nest-api/files/src/common/dto/filters.dto.ts +25 -0
  89. package/templates/nest-api/files/src/common/dto/index.ts +1 -0
  90. package/templates/nest-api/files/src/common/entity/index.ts +2 -0
  91. package/templates/nest-api/files/src/common/entity/ok.entity.ts +11 -0
  92. package/templates/nest-api/files/src/common/entity/pagination.entity.ts +24 -0
  93. package/templates/nest-api/files/src/common/exceptions/index.ts +1 -0
  94. package/templates/nest-api/files/src/common/exceptions/prisma.exception.ts +57 -0
  95. package/templates/nest-api/files/src/common/guards/feature-flag.guard.ts +51 -0
  96. package/templates/nest-api/files/src/common/guards/index.ts +1 -0
  97. package/templates/nest-api/files/src/common/interceptors/index.ts +1 -0
  98. package/templates/nest-api/files/src/common/interceptors/metrics.interceptor.ts +37 -0
  99. package/templates/nest-api/files/src/common/misc/helpers.ts +26 -0
  100. package/templates/nest-api/files/src/common/misc/index.ts +3 -0
  101. package/templates/nest-api/files/src/common/misc/origin-builder.ts +56 -0
  102. package/templates/nest-api/files/src/common/misc/prisma.indicator.ts +24 -0
  103. package/templates/nest-api/files/src/common/modules/index.ts +3 -0
  104. package/templates/nest-api/files/src/common/modules/logger.module.ts +104 -0
  105. package/templates/nest-api/files/src/common/modules/metrics.module.ts +28 -0
  106. package/templates/nest-api/files/src/common/modules/queue.module.ts +23 -0
  107. package/templates/nest-api/files/src/common/processors/index.ts +1 -0
  108. package/templates/nest-api/files/src/common/processors/mail/index.ts +47 -0
  109. package/templates/nest-api/files/src/common/processors/mail/process.ts +225 -0
  110. package/templates/nest-api/files/src/common/schemas/fields.ts +76 -0
  111. package/templates/nest-api/files/src/common/schemas/index.ts +1 -0
  112. package/templates/nest-api/files/src/common/services/auth-rate-limit.storage.ts +79 -0
  113. package/templates/nest-api/files/src/common/services/auth.service.ts +16 -0
  114. package/templates/nest-api/files/src/common/services/cache.service.ts +118 -0
  115. package/templates/nest-api/files/src/common/services/config.service.ts +388 -0
  116. package/templates/nest-api/files/src/common/services/feature-flag.service.ts +117 -0
  117. package/templates/nest-api/files/src/common/services/generator.service.ts +54 -0
  118. package/templates/nest-api/files/src/common/services/index.ts +10 -0
  119. package/templates/nest-api/files/src/common/services/logger.service.ts +14 -0
  120. package/templates/nest-api/files/src/common/services/mail.service.ts +44 -0
  121. package/templates/nest-api/files/src/common/services/prisma.service.ts +106 -0
  122. package/templates/nest-api/files/src/common/services/throttler-storage.service.ts +112 -0
  123. package/templates/nest-api/files/src/common/types/email.ts +55 -0
  124. package/templates/nest-api/files/src/common/types/index.ts +3 -0
  125. package/templates/nest-api/files/src/common/types/misc.ts +20 -0
  126. package/templates/nest-api/files/src/common/types/prisma.ts +18 -0
  127. package/templates/nest-api/files/src/common/utils/enum.ts +39 -0
  128. package/templates/nest-api/files/src/common/utils/helpers.ts +139 -0
  129. package/templates/nest-api/files/src/common/utils/index.ts +5 -0
  130. package/templates/nest-api/files/src/common/utils/ip.ts +22 -0
  131. package/templates/nest-api/files/src/common/utils/records.ts +66 -0
  132. package/templates/nest-api/files/src/common/utils/redis.keys.ts +31 -0
  133. package/templates/nest-api/files/src/lib/__tests__/access.spec.ts +33 -0
  134. package/templates/nest-api/files/src/lib/access.ts +114 -0
  135. package/templates/nest-api/files/src/lib/auth.ts +76 -0
  136. package/templates/nest-api/files/src/lib/rate-limit.ts +20 -0
  137. package/templates/nest-api/files/src/main.ts +28 -0
  138. package/templates/nest-api/files/src/media/__tests__/media.service.spec.ts +60 -0
  139. package/templates/nest-api/files/src/media/__tests__/s3.service.spec.ts +159 -0
  140. package/templates/nest-api/files/src/media/entity/file.entity.ts +7 -0
  141. package/templates/nest-api/files/src/media/entity/index.ts +1 -0
  142. package/templates/nest-api/files/src/media/media.module.ts +20 -0
  143. package/templates/nest-api/files/src/media/processors/index.ts +44 -0
  144. package/templates/nest-api/files/src/media/services/index.ts +2 -0
  145. package/templates/nest-api/files/src/media/services/media.service.ts +29 -0
  146. package/templates/nest-api/files/src/media/services/s3.service.ts +168 -0
  147. package/templates/nest-api/files/src/notifications/controllers/index.ts +1 -0
  148. package/templates/nest-api/files/src/notifications/controllers/notifications.controller.ts +72 -0
  149. package/templates/nest-api/files/src/notifications/dto/index.ts +1 -0
  150. package/templates/nest-api/files/src/notifications/dto/notification.dto.ts +11 -0
  151. package/templates/nest-api/files/src/notifications/entity/index.ts +1 -0
  152. package/templates/nest-api/files/src/notifications/entity/notification.entity.ts +37 -0
  153. package/templates/nest-api/files/src/notifications/notifications.module.ts +11 -0
  154. package/templates/nest-api/files/src/notifications/services/index.ts +1 -0
  155. package/templates/nest-api/files/src/notifications/services/notifications.service.ts +98 -0
  156. package/templates/nest-api/files/src/realtime/__tests__/events.gateway.spec.ts +88 -0
  157. package/templates/nest-api/files/src/realtime/__tests__/rooms.spec.ts +29 -0
  158. package/templates/nest-api/files/src/realtime/__tests__/ws-permissions.guard.spec.ts +54 -0
  159. package/templates/nest-api/files/src/realtime/adapters/redis-io.adapter.ts +57 -0
  160. package/templates/nest-api/files/src/realtime/constants/index.ts +68 -0
  161. package/templates/nest-api/files/src/realtime/decorators/index.ts +1 -0
  162. package/templates/nest-api/files/src/realtime/decorators/ws-permissions.decorator.ts +14 -0
  163. package/templates/nest-api/files/src/realtime/gateways/events.gateway.ts +187 -0
  164. package/templates/nest-api/files/src/realtime/guards/index.ts +2 -0
  165. package/templates/nest-api/files/src/realtime/guards/ws-auth.guard.ts +20 -0
  166. package/templates/nest-api/files/src/realtime/guards/ws-permissions.guard.ts +35 -0
  167. package/templates/nest-api/files/src/realtime/misc/session-from-socket.ts +54 -0
  168. package/templates/nest-api/files/src/realtime/realtime.module.ts +26 -0
  169. package/templates/nest-api/files/src/realtime/services/presence.service.ts +56 -0
  170. package/templates/nest-api/files/src/realtime/services/realtime.service.ts +72 -0
  171. package/templates/nest-api/files/src/users/__tests__/current-user.controller.spec.ts +77 -0
  172. package/templates/nest-api/files/src/users/__tests__/current-user.service.spec.ts +159 -0
  173. package/templates/nest-api/files/src/users/__tests__/user.entity.spec.ts +175 -0
  174. package/templates/nest-api/files/src/users/__tests__/users.controller.spec.ts +89 -0
  175. package/templates/nest-api/files/src/users/__tests__/users.service.spec.ts +398 -0
  176. package/templates/nest-api/files/src/users/controllers/current-user.controller.ts +82 -0
  177. package/templates/nest-api/files/src/users/controllers/index.ts +2 -0
  178. package/templates/nest-api/files/src/users/controllers/users.controller.ts +40 -0
  179. package/templates/nest-api/files/src/users/decorators/index.ts +1 -0
  180. package/templates/nest-api/files/src/users/decorators/permissions.decorator.ts +24 -0
  181. package/templates/nest-api/files/src/users/dto/avatar.dto.ts +13 -0
  182. package/templates/nest-api/files/src/users/dto/filters.dto.ts +8 -0
  183. package/templates/nest-api/files/src/users/dto/index.ts +2 -0
  184. package/templates/nest-api/files/src/users/entity/index.ts +1 -0
  185. package/templates/nest-api/files/src/users/entity/user.entity.ts +53 -0
  186. package/templates/nest-api/files/src/users/services/current-user.service.ts +46 -0
  187. package/templates/nest-api/files/src/users/services/index.ts +2 -0
  188. package/templates/nest-api/files/src/users/services/users.service.ts +88 -0
  189. package/templates/nest-api/files/src/users/users.module.ts +10 -0
  190. package/templates/nest-api/files/test/app.spec.ts +102 -0
  191. package/templates/nest-api/files/test/auth.spec.ts +108 -0
  192. package/templates/nest-api/files/test/current-user.spec.ts +154 -0
  193. package/templates/nest-api/files/test/factory/app.ts +172 -0
  194. package/templates/nest-api/files/test/factory/constants.ts +22 -0
  195. package/templates/nest-api/files/test/factory/dto.ts +27 -0
  196. package/templates/nest-api/files/test/factory/entity.ts +70 -0
  197. package/templates/nest-api/files/test/factory/gen.ts +75 -0
  198. package/templates/nest-api/files/test/factory/util.ts +21 -0
  199. package/templates/nest-api/files/test/notifications.spec.ts +176 -0
  200. package/templates/nest-api/files/test/rate-limit.spec.ts +180 -0
  201. package/templates/nest-api/files/test/setup.ts +1 -0
  202. package/templates/nest-api/files/test/users.spec.ts +437 -0
  203. package/templates/nest-api/files/tsconfig.build.json +13 -0
  204. package/templates/nest-api/files/tsconfig.json +35 -0
  205. package/templates/nest-api/files/vitest.config.ts +53 -0
  206. package/templates/nest-api/template.json +452 -0
  207. package/templates/node-worker/files/.bin/db.ts +123 -0
  208. package/templates/node-worker/files/.bin/helpers.ts +49 -0
  209. package/templates/node-worker/files/.bin/secrets.ts +68 -0
  210. package/templates/node-worker/files/.dockerignore +5 -0
  211. package/templates/node-worker/files/.env.example +21 -0
  212. package/templates/node-worker/files/.forgejo/workflows/build-and-push.yml +41 -0
  213. package/templates/node-worker/files/.nvmrc +1 -0
  214. package/templates/node-worker/files/.prettierignore +4 -0
  215. package/templates/node-worker/files/.prettierrc +8 -0
  216. package/templates/node-worker/files/.yarnrc.yml +5 -0
  217. package/templates/node-worker/files/Dockerfile +63 -0
  218. package/templates/node-worker/files/Dockerfile.dev +59 -0
  219. package/templates/node-worker/files/_gitignore +48 -0
  220. package/templates/node-worker/files/ecosystem.config.cjs +10 -0
  221. package/templates/node-worker/files/eslint.config.js +37 -0
  222. package/templates/node-worker/files/package.json +67 -0
  223. package/templates/node-worker/files/prisma/_gitignore +1 -0
  224. package/templates/node-worker/files/prisma/schema/base.prisma +36 -0
  225. package/templates/node-worker/files/prisma.config.ts +13 -0
  226. package/templates/node-worker/files/src/config.ts +55 -0
  227. package/templates/node-worker/files/src/db.ts +118 -0
  228. package/templates/node-worker/files/src/index.ts +7 -0
  229. package/templates/node-worker/files/src/logger.ts +45 -0
  230. package/templates/node-worker/files/src/types.ts +23 -0
  231. package/templates/node-worker/files/src/utils/browser.ts +16 -0
  232. package/templates/node-worker/files/src/utils/date.ts +68 -0
  233. package/templates/node-worker/files/src/utils/helpers.ts +17 -0
  234. package/templates/node-worker/files/src/utils/number.ts +47 -0
  235. package/templates/node-worker/files/src/utils/worker.ts +29 -0
  236. package/templates/node-worker/files/src/workers/constants.ts +6 -0
  237. package/templates/node-worker/files/src/workers/example/index.ts +27 -0
  238. package/templates/node-worker/files/src/workers/index.ts +1 -0
  239. package/templates/node-worker/files/tsconfig.json +30 -0
  240. package/templates/node-worker/template.json +135 -0
  241. package/templates/py-worker/files/.dockerignore +176 -0
  242. package/templates/py-worker/files/.env.example +20 -0
  243. package/templates/py-worker/files/.forgejo/workflows/build-amd-push.yml +39 -0
  244. package/templates/py-worker/files/Dockerfile +10 -0
  245. package/templates/py-worker/files/README.md +3 -0
  246. package/templates/py-worker/files/_gitignore +176 -0
  247. package/templates/py-worker/files/config.py +89 -0
  248. package/templates/py-worker/files/models/decorators.py +42 -0
  249. package/templates/py-worker/files/models/enums.py +32 -0
  250. package/templates/py-worker/files/models/mixins.py +61 -0
  251. package/templates/py-worker/files/requirements.txt +18 -0
  252. package/templates/py-worker/files/utils/db.py +67 -0
  253. package/templates/py-worker/files/utils/logger.py +97 -0
  254. package/templates/py-worker/files/utils/misc.py +45 -0
  255. package/templates/py-worker/files/utils/worker.py +80 -0
  256. package/templates/py-worker/files/worker.py +41 -0
  257. package/templates/py-worker/files/workers/hello/process.py +22 -0
  258. package/templates/py-worker/template.json +87 -0
  259. package/templates/react-app/README.md +78 -0
  260. package/templates/react-app/derive.sh +128 -0
  261. package/templates/react-app/files/.bin/openapi.js +32 -0
  262. package/templates/react-app/files/.bin/secrets.js +47 -0
  263. package/templates/react-app/files/.dockerignore +16 -0
  264. package/templates/react-app/files/.editorconfig +12 -0
  265. package/templates/react-app/files/.env.example +34 -0
  266. package/templates/react-app/files/.forgejo/workflows/build-and-push.yml +55 -0
  267. package/templates/react-app/files/.forgejo/workflows/ci.yml +39 -0
  268. package/templates/react-app/files/.husky/pre-commit +1 -0
  269. package/templates/react-app/files/.nvmrc +1 -0
  270. package/templates/react-app/files/.prettierignore +6 -0
  271. package/templates/react-app/files/.prettierrc +9 -0
  272. package/templates/react-app/files/.yarnrc.yml +2 -0
  273. package/templates/react-app/files/Dockerfile +52 -0
  274. package/templates/react-app/files/_gitignore +33 -0
  275. package/templates/react-app/files/build.sh +53 -0
  276. package/templates/react-app/files/docker/nginx.conf +134 -0
  277. package/templates/react-app/files/eslint.config.mjs +75 -0
  278. package/templates/react-app/files/index.html +62 -0
  279. package/templates/react-app/files/openapi-ts.config.ts +22 -0
  280. package/templates/react-app/files/package.json +80 -0
  281. package/templates/react-app/files/src/app/nav.tsx +32 -0
  282. package/templates/react-app/files/src/app/providers.tsx +39 -0
  283. package/templates/react-app/files/src/app/router.tsx +142 -0
  284. package/templates/react-app/files/src/app/store.ts +20 -0
  285. package/templates/react-app/files/src/features/auth/components/auth-card.tsx +23 -0
  286. package/templates/react-app/files/src/features/auth/components/checkbox-field.tsx +32 -0
  287. package/templates/react-app/files/src/features/auth/components/google-button.tsx +41 -0
  288. package/templates/react-app/files/src/features/auth/components/or-divider.tsx +9 -0
  289. package/templates/react-app/files/src/features/auth/components/password-field.tsx +70 -0
  290. package/templates/react-app/files/src/features/auth/components/submit-button.tsx +16 -0
  291. package/templates/react-app/files/src/features/auth/lib/auth-actions.ts +68 -0
  292. package/templates/react-app/files/src/features/auth/lib/password-strength.ts +20 -0
  293. package/templates/react-app/files/src/features/auth/lib/redirect.ts +6 -0
  294. package/templates/react-app/files/src/features/auth/lib/schemas.ts +37 -0
  295. package/templates/react-app/files/src/features/seo/routes.ts +65 -0
  296. package/templates/react-app/files/src/lib/env.ts +28 -0
  297. package/templates/react-app/files/src/lib/paths.ts +58 -0
  298. package/templates/react-app/files/src/main.tsx +19 -0
  299. package/templates/react-app/files/src/routes/admin/home.tsx +24 -0
  300. package/templates/react-app/files/src/routes/auth/forgot-password.tsx +93 -0
  301. package/templates/react-app/files/src/routes/auth/login.tsx +87 -0
  302. package/templates/react-app/files/src/routes/auth/reset-password.tsx +83 -0
  303. package/templates/react-app/files/src/routes/auth/signup.tsx +117 -0
  304. package/templates/react-app/files/src/routes/auth/verified.tsx +31 -0
  305. package/templates/react-app/files/src/routes/auth/verify-email.tsx +97 -0
  306. package/templates/react-app/files/src/routes/home.tsx +17 -0
  307. package/templates/react-app/files/src/routes/legal/privacy.tsx +26 -0
  308. package/templates/react-app/files/src/routes/legal/terms.tsx +28 -0
  309. package/templates/react-app/files/src/routes/marketing/home.tsx +18 -0
  310. package/templates/react-app/files/src/routes/settings.tsx +10 -0
  311. package/templates/react-app/files/src/shared/api/base-api.ts +24 -0
  312. package/templates/react-app/files/src/shared/api/base-query.ts +29 -0
  313. package/templates/react-app/files/src/shared/api/config.ts +21 -0
  314. package/templates/react-app/files/src/shared/api/index.ts +15 -0
  315. package/templates/react-app/files/src/shared/api/realtime-events.ts +34 -0
  316. package/templates/react-app/files/src/shared/api/socket.ts +41 -0
  317. package/templates/react-app/files/src/shared/api/store.ts +39 -0
  318. package/templates/react-app/files/src/shared/api/upload-query.ts +115 -0
  319. package/templates/react-app/files/src/shared/auth/client.ts +27 -0
  320. package/templates/react-app/files/src/shared/auth/config.ts +28 -0
  321. package/templates/react-app/files/src/shared/auth/guards.tsx +75 -0
  322. package/templates/react-app/files/src/shared/auth/hooks.ts +13 -0
  323. package/templates/react-app/files/src/shared/auth/index.ts +20 -0
  324. package/templates/react-app/files/src/shared/auth/logout.ts +23 -0
  325. package/templates/react-app/files/src/shared/auth/provider.tsx +52 -0
  326. package/templates/react-app/files/src/shared/auth/redirect.ts +65 -0
  327. package/templates/react-app/files/src/shared/auth/slice.ts +54 -0
  328. package/templates/react-app/files/src/shared/hooks/index.ts +6 -0
  329. package/templates/react-app/files/src/shared/layouts/app-shell/app-shell.tsx +225 -0
  330. package/templates/react-app/files/src/shared/layouts/app-shell/user-menu.tsx +74 -0
  331. package/templates/react-app/files/src/shared/layouts/auth-layout.tsx +137 -0
  332. package/templates/react-app/files/src/shared/layouts/error-boundary.tsx +38 -0
  333. package/templates/react-app/files/src/shared/layouts/index.ts +23 -0
  334. package/templates/react-app/files/src/shared/layouts/marketing/marketing-layout.tsx +29 -0
  335. package/templates/react-app/files/src/shared/layouts/marketing/section-heading.tsx +38 -0
  336. package/templates/react-app/files/src/shared/layouts/marketing/section.tsx +30 -0
  337. package/templates/react-app/files/src/shared/layouts/states/app-shell-skeleton.tsx +47 -0
  338. package/templates/react-app/files/src/shared/layouts/states/empty.tsx +22 -0
  339. package/templates/react-app/files/src/shared/layouts/states/forbidden.tsx +14 -0
  340. package/templates/react-app/files/src/shared/layouts/states/loading.tsx +11 -0
  341. package/templates/react-app/files/src/shared/layouts/states/not-found.tsx +21 -0
  342. package/templates/react-app/files/src/shared/layouts/states/page-skeleton.tsx +39 -0
  343. package/templates/react-app/files/src/shared/layouts/states/route-fallback.tsx +26 -0
  344. package/templates/react-app/files/src/shared/layouts/states/state-screen.tsx +22 -0
  345. package/templates/react-app/files/src/shared/types/README.md +28 -0
  346. package/templates/react-app/files/src/shared/types/api/index.ts +9 -0
  347. package/templates/react-app/files/src/shared/types/api/zod.gen.ts +5 -0
  348. package/templates/react-app/files/src/shared/types/index.ts +4 -0
  349. package/templates/react-app/files/src/shared/ui/components/brand/logo.tsx +51 -0
  350. package/templates/react-app/files/src/shared/ui/components/brand/social-icons.tsx +37 -0
  351. package/templates/react-app/files/src/shared/ui/components/form/form.tsx +120 -0
  352. package/templates/react-app/files/src/shared/ui/components/ui/alert.tsx +25 -0
  353. package/templates/react-app/files/src/shared/ui/components/ui/button.tsx +43 -0
  354. package/templates/react-app/files/src/shared/ui/components/ui/calendar.tsx +78 -0
  355. package/templates/react-app/files/src/shared/ui/components/ui/card.tsx +44 -0
  356. package/templates/react-app/files/src/shared/ui/components/ui/checkbox.tsx +16 -0
  357. package/templates/react-app/files/src/shared/ui/components/ui/date-picker.tsx +76 -0
  358. package/templates/react-app/files/src/shared/ui/components/ui/input.tsx +22 -0
  359. package/templates/react-app/files/src/shared/ui/components/ui/label.tsx +19 -0
  360. package/templates/react-app/files/src/shared/ui/components/ui/modal.tsx +94 -0
  361. package/templates/react-app/files/src/shared/ui/components/ui/popover.tsx +31 -0
  362. package/templates/react-app/files/src/shared/ui/components/ui/progress.tsx +33 -0
  363. package/templates/react-app/files/src/shared/ui/components/ui/search-input.tsx +69 -0
  364. package/templates/react-app/files/src/shared/ui/components/ui/select.tsx +80 -0
  365. package/templates/react-app/files/src/shared/ui/components/ui/skeleton.tsx +12 -0
  366. package/templates/react-app/files/src/shared/ui/components/ui/sonner.tsx +23 -0
  367. package/templates/react-app/files/src/shared/ui/components/ui/spinner.tsx +8 -0
  368. package/templates/react-app/files/src/shared/ui/components/ui/textarea.tsx +22 -0
  369. package/templates/react-app/files/src/shared/ui/index.ts +45 -0
  370. package/templates/react-app/files/src/shared/ui/lib/utils.ts +7 -0
  371. package/templates/react-app/files/src/shared/ui/lib/zod-setup.ts +28 -0
  372. package/templates/react-app/files/src/shared/ui/meta/brand.ts +18 -0
  373. package/templates/react-app/files/src/shared/ui/meta/meta-context.ts +30 -0
  374. package/templates/react-app/files/src/shared/ui/meta/meta-provider.tsx +24 -0
  375. package/templates/react-app/files/src/shared/ui/meta/page-meta.tsx +69 -0
  376. package/templates/react-app/files/src/shared/ui/meta/use-meta-config.ts +11 -0
  377. package/templates/react-app/files/src/shared/ui/styles/globals.css +285 -0
  378. package/templates/react-app/files/src/shared/ui/styles/theme.css +48 -0
  379. package/templates/react-app/files/src/shared/ui/theme/init-script.ts +23 -0
  380. package/templates/react-app/files/src/shared/ui/theme/theme-colors.ts +14 -0
  381. package/templates/react-app/files/src/shared/ui/theme/theme-context.ts +60 -0
  382. package/templates/react-app/files/src/shared/ui/theme/theme-provider.tsx +101 -0
  383. package/templates/react-app/files/src/shared/ui/theme/theme-toggle.tsx +40 -0
  384. package/templates/react-app/files/src/shared/ui/theme/use-theme.ts +11 -0
  385. package/templates/react-app/files/src/vite-env.d.ts +13 -0
  386. package/templates/react-app/files/tsconfig.json +26 -0
  387. package/templates/react-app/files/tsconfig.node.json +25 -0
  388. package/templates/react-app/files/vite.config.ts +344 -0
  389. package/templates/react-app/overrides/src/routes/auth/forgot-password.tsx +93 -0
  390. package/templates/react-app/overrides/src/routes/auth/login.tsx +87 -0
  391. package/templates/react-app/overrides/src/routes/auth/signup.tsx +117 -0
  392. package/templates/react-app/overrides/src/routes/auth/verified.tsx +31 -0
  393. package/templates/react-app/overrides/src/routes/auth/verify-email.tsx +97 -0
  394. package/templates/react-app/overrides/src/shared/auth/config.ts +28 -0
  395. package/templates/react-app/overrides/src/shared/auth/guards.tsx +75 -0
  396. package/templates/react-app/overrides/src/shared/auth/index.ts +20 -0
  397. package/templates/react-app/overrides/src/shared/auth/redirect.ts +65 -0
  398. package/templates/react-app/overrides/src/shared/hooks/index.ts +6 -0
  399. package/templates/react-app/overrides/src/shared/types/README.md +28 -0
  400. package/templates/react-app/template.json +222 -0
  401. package/templates/react-monorepo/files/.dockerignore +19 -0
  402. package/templates/react-monorepo/files/.editorconfig +12 -0
  403. package/templates/react-monorepo/files/.forgejo/workflows/build-and-push.yml +98 -0
  404. package/templates/react-monorepo/files/.forgejo/workflows/ci.yml +35 -0
  405. package/templates/react-monorepo/files/.husky/pre-commit +1 -0
  406. package/templates/react-monorepo/files/.nvmrc +1 -0
  407. package/templates/react-monorepo/files/.prettierignore +6 -0
  408. package/templates/react-monorepo/files/.prettierrc +9 -0
  409. package/templates/react-monorepo/files/.yarnrc.yml +2 -0
  410. package/templates/react-monorepo/files/Dockerfile +90 -0
  411. package/templates/react-monorepo/files/_gitignore +35 -0
  412. package/templates/react-monorepo/files/apps/admin/.bin/secrets.js +57 -0
  413. package/templates/react-monorepo/files/apps/admin/.env.example +16 -0
  414. package/templates/react-monorepo/files/apps/admin/eslint.config.mjs +3 -0
  415. package/templates/react-monorepo/files/apps/admin/index.html +57 -0
  416. package/templates/react-monorepo/files/apps/admin/package.json +33 -0
  417. package/templates/react-monorepo/files/apps/admin/src/App.tsx +3 -0
  418. package/templates/react-monorepo/files/apps/admin/src/app/nav.tsx +14 -0
  419. package/templates/react-monorepo/files/apps/admin/src/app/providers.tsx +42 -0
  420. package/templates/react-monorepo/files/apps/admin/src/app/router.tsx +50 -0
  421. package/templates/react-monorepo/files/apps/admin/src/app/store.ts +15 -0
  422. package/templates/react-monorepo/files/apps/admin/src/lib/env.ts +12 -0
  423. package/templates/react-monorepo/files/apps/admin/src/main.tsx +14 -0
  424. package/templates/react-monorepo/files/apps/admin/src/routes/home.tsx +17 -0
  425. package/templates/react-monorepo/files/apps/admin/src/routes/settings.tsx +10 -0
  426. package/templates/react-monorepo/files/apps/admin/src/vite-env.d.ts +14 -0
  427. package/templates/react-monorepo/files/apps/admin/tsconfig.json +10 -0
  428. package/templates/react-monorepo/files/apps/admin/vite.config.ts +13 -0
  429. package/templates/react-monorepo/files/apps/auth/.bin/secrets.js +57 -0
  430. package/templates/react-monorepo/files/apps/auth/.env.example +16 -0
  431. package/templates/react-monorepo/files/apps/auth/eslint.config.mjs +3 -0
  432. package/templates/react-monorepo/files/apps/auth/index.html +57 -0
  433. package/templates/react-monorepo/files/apps/auth/package.json +32 -0
  434. package/templates/react-monorepo/files/apps/auth/src/App.tsx +3 -0
  435. package/templates/react-monorepo/files/apps/auth/src/app/providers.tsx +42 -0
  436. package/templates/react-monorepo/files/apps/auth/src/app/router.tsx +19 -0
  437. package/templates/react-monorepo/files/apps/auth/src/app/store.ts +15 -0
  438. package/templates/react-monorepo/files/apps/auth/src/components/auth-card.tsx +23 -0
  439. package/templates/react-monorepo/files/apps/auth/src/components/checkbox-field.tsx +32 -0
  440. package/templates/react-monorepo/files/apps/auth/src/components/google-button.tsx +41 -0
  441. package/templates/react-monorepo/files/apps/auth/src/components/or-divider.tsx +9 -0
  442. package/templates/react-monorepo/files/apps/auth/src/components/password-field.tsx +70 -0
  443. package/templates/react-monorepo/files/apps/auth/src/components/submit-button.tsx +16 -0
  444. package/templates/react-monorepo/files/apps/auth/src/lib/auth-actions.ts +68 -0
  445. package/templates/react-monorepo/files/apps/auth/src/lib/env.ts +8 -0
  446. package/templates/react-monorepo/files/apps/auth/src/lib/password-strength.ts +20 -0
  447. package/templates/react-monorepo/files/apps/auth/src/lib/redirect.ts +6 -0
  448. package/templates/react-monorepo/files/apps/auth/src/lib/schemas.ts +37 -0
  449. package/templates/react-monorepo/files/apps/auth/src/main.tsx +14 -0
  450. package/templates/react-monorepo/files/apps/auth/src/routes/forgot-password.tsx +91 -0
  451. package/templates/react-monorepo/files/apps/auth/src/routes/login.tsx +82 -0
  452. package/templates/react-monorepo/files/apps/auth/src/routes/reset-password.tsx +83 -0
  453. package/templates/react-monorepo/files/apps/auth/src/routes/signup.tsx +117 -0
  454. package/templates/react-monorepo/files/apps/auth/src/routes/verified.tsx +30 -0
  455. package/templates/react-monorepo/files/apps/auth/src/routes/verify-email.tsx +96 -0
  456. package/templates/react-monorepo/files/apps/auth/src/vite-env.d.ts +14 -0
  457. package/templates/react-monorepo/files/apps/auth/tsconfig.json +10 -0
  458. package/templates/react-monorepo/files/apps/auth/vite.config.ts +13 -0
  459. package/templates/react-monorepo/files/apps/client/.bin/secrets.js +57 -0
  460. package/templates/react-monorepo/files/apps/client/.env.example +16 -0
  461. package/templates/react-monorepo/files/apps/client/eslint.config.mjs +3 -0
  462. package/templates/react-monorepo/files/apps/client/index.html +57 -0
  463. package/templates/react-monorepo/files/apps/client/package.json +35 -0
  464. package/templates/react-monorepo/files/apps/client/src/App.tsx +3 -0
  465. package/templates/react-monorepo/files/apps/client/src/app/nav.tsx +14 -0
  466. package/templates/react-monorepo/files/apps/client/src/app/providers.tsx +42 -0
  467. package/templates/react-monorepo/files/apps/client/src/app/router.tsx +50 -0
  468. package/templates/react-monorepo/files/apps/client/src/app/store.ts +15 -0
  469. package/templates/react-monorepo/files/apps/client/src/lib/env.ts +12 -0
  470. package/templates/react-monorepo/files/apps/client/src/main.tsx +19 -0
  471. package/templates/react-monorepo/files/apps/client/src/routes/home.tsx +17 -0
  472. package/templates/react-monorepo/files/apps/client/src/routes/settings.tsx +10 -0
  473. package/templates/react-monorepo/files/apps/client/src/vite-env.d.ts +15 -0
  474. package/templates/react-monorepo/files/apps/client/tsconfig.json +10 -0
  475. package/templates/react-monorepo/files/apps/client/vite.config.ts +120 -0
  476. package/templates/react-monorepo/files/apps/landing/.bin/secrets.js +57 -0
  477. package/templates/react-monorepo/files/apps/landing/.env.example +20 -0
  478. package/templates/react-monorepo/files/apps/landing/eslint.config.mjs +3 -0
  479. package/templates/react-monorepo/files/apps/landing/index.html +51 -0
  480. package/templates/react-monorepo/files/apps/landing/package.json +33 -0
  481. package/templates/react-monorepo/files/apps/landing/src/App.tsx +3 -0
  482. package/templates/react-monorepo/files/apps/landing/src/app/providers.tsx +42 -0
  483. package/templates/react-monorepo/files/apps/landing/src/app/router.tsx +21 -0
  484. package/templates/react-monorepo/files/apps/landing/src/app/store.ts +18 -0
  485. package/templates/react-monorepo/files/apps/landing/src/features/seo/routes.ts +65 -0
  486. package/templates/react-monorepo/files/apps/landing/src/lib/env.ts +10 -0
  487. package/templates/react-monorepo/files/apps/landing/src/main.tsx +14 -0
  488. package/templates/react-monorepo/files/apps/landing/src/routes/home.tsx +18 -0
  489. package/templates/react-monorepo/files/apps/landing/src/vite-env.d.ts +15 -0
  490. package/templates/react-monorepo/files/apps/landing/tsconfig.json +10 -0
  491. package/templates/react-monorepo/files/apps/landing/vite.config.ts +169 -0
  492. package/templates/react-monorepo/files/build.sh +61 -0
  493. package/templates/react-monorepo/files/docker/nginx.conf +136 -0
  494. package/templates/react-monorepo/files/package.json +37 -0
  495. package/templates/react-monorepo/files/packages/.gitkeep +0 -0
  496. package/templates/react-monorepo/files/packages/api/eslint.config.mjs +3 -0
  497. package/templates/react-monorepo/files/packages/api/package.json +30 -0
  498. package/templates/react-monorepo/files/packages/api/src/base-api.ts +24 -0
  499. package/templates/react-monorepo/files/packages/api/src/base-query.ts +29 -0
  500. package/templates/react-monorepo/files/packages/api/src/config.ts +21 -0
  501. package/templates/react-monorepo/files/packages/api/src/index.ts +15 -0
  502. package/templates/react-monorepo/files/packages/api/src/realtime-events.ts +34 -0
  503. package/templates/react-monorepo/files/packages/api/src/socket.ts +41 -0
  504. package/templates/react-monorepo/files/packages/api/src/store.ts +39 -0
  505. package/templates/react-monorepo/files/packages/api/src/upload-query.ts +115 -0
  506. package/templates/react-monorepo/files/packages/api/tsconfig.json +4 -0
  507. package/templates/react-monorepo/files/packages/auth/eslint.config.mjs +3 -0
  508. package/templates/react-monorepo/files/packages/auth/package.json +33 -0
  509. package/templates/react-monorepo/files/packages/auth/src/client.ts +27 -0
  510. package/templates/react-monorepo/files/packages/auth/src/config.ts +22 -0
  511. package/templates/react-monorepo/files/packages/auth/src/guards.tsx +61 -0
  512. package/templates/react-monorepo/files/packages/auth/src/hooks.ts +13 -0
  513. package/templates/react-monorepo/files/packages/auth/src/index.ts +20 -0
  514. package/templates/react-monorepo/files/packages/auth/src/logout.ts +23 -0
  515. package/templates/react-monorepo/files/packages/auth/src/provider.tsx +52 -0
  516. package/templates/react-monorepo/files/packages/auth/src/redirect.ts +37 -0
  517. package/templates/react-monorepo/files/packages/auth/src/slice.ts +54 -0
  518. package/templates/react-monorepo/files/packages/auth/tsconfig.json +4 -0
  519. package/templates/react-monorepo/files/packages/config/README.md +47 -0
  520. package/templates/react-monorepo/files/packages/config/eslint/base.mjs +83 -0
  521. package/templates/react-monorepo/files/packages/config/package.json +37 -0
  522. package/templates/react-monorepo/files/packages/config/tailwind/theme.css +48 -0
  523. package/templates/react-monorepo/files/packages/config/tsconfig/base.json +22 -0
  524. package/templates/react-monorepo/files/packages/config/tsconfig/node.json +9 -0
  525. package/templates/react-monorepo/files/packages/config/tsconfig/react.json +9 -0
  526. package/templates/react-monorepo/files/packages/config/vite/index.d.ts +18 -0
  527. package/templates/react-monorepo/files/packages/config/vite/index.mjs +129 -0
  528. package/templates/react-monorepo/files/packages/hooks/eslint.config.mjs +3 -0
  529. package/templates/react-monorepo/files/packages/hooks/package.json +16 -0
  530. package/templates/react-monorepo/files/packages/hooks/src/index.ts +3 -0
  531. package/templates/react-monorepo/files/packages/hooks/tsconfig.json +4 -0
  532. package/templates/react-monorepo/files/packages/layouts/eslint.config.mjs +3 -0
  533. package/templates/react-monorepo/files/packages/layouts/package.json +30 -0
  534. package/templates/react-monorepo/files/packages/layouts/src/app-shell/app-shell.tsx +225 -0
  535. package/templates/react-monorepo/files/packages/layouts/src/app-shell/user-menu.tsx +74 -0
  536. package/templates/react-monorepo/files/packages/layouts/src/auth-layout.tsx +137 -0
  537. package/templates/react-monorepo/files/packages/layouts/src/error-boundary.tsx +38 -0
  538. package/templates/react-monorepo/files/packages/layouts/src/index.ts +23 -0
  539. package/templates/react-monorepo/files/packages/layouts/src/marketing/marketing-layout.tsx +29 -0
  540. package/templates/react-monorepo/files/packages/layouts/src/marketing/section-heading.tsx +38 -0
  541. package/templates/react-monorepo/files/packages/layouts/src/marketing/section.tsx +30 -0
  542. package/templates/react-monorepo/files/packages/layouts/src/states/app-shell-skeleton.tsx +47 -0
  543. package/templates/react-monorepo/files/packages/layouts/src/states/empty.tsx +22 -0
  544. package/templates/react-monorepo/files/packages/layouts/src/states/forbidden.tsx +14 -0
  545. package/templates/react-monorepo/files/packages/layouts/src/states/loading.tsx +11 -0
  546. package/templates/react-monorepo/files/packages/layouts/src/states/not-found.tsx +21 -0
  547. package/templates/react-monorepo/files/packages/layouts/src/states/page-skeleton.tsx +39 -0
  548. package/templates/react-monorepo/files/packages/layouts/src/states/route-fallback.tsx +26 -0
  549. package/templates/react-monorepo/files/packages/layouts/src/states/state-screen.tsx +22 -0
  550. package/templates/react-monorepo/files/packages/layouts/tsconfig.json +4 -0
  551. package/templates/react-monorepo/files/packages/types/.bin/openapi.js +32 -0
  552. package/templates/react-monorepo/files/packages/types/.bin/secrets.js +57 -0
  553. package/templates/react-monorepo/files/packages/types/README.md +28 -0
  554. package/templates/react-monorepo/files/packages/types/eslint.config.mjs +3 -0
  555. package/templates/react-monorepo/files/packages/types/openapi-ts.config.ts +22 -0
  556. package/templates/react-monorepo/files/packages/types/package.json +29 -0
  557. package/templates/react-monorepo/files/packages/types/src/index.ts +4 -0
  558. package/templates/react-monorepo/files/packages/types/src/types/api/index.ts +9 -0
  559. package/templates/react-monorepo/files/packages/types/src/types/api/zod.gen.ts +5 -0
  560. package/templates/react-monorepo/files/packages/types/tsconfig.json +7 -0
  561. package/templates/react-monorepo/files/packages/ui/components.json +21 -0
  562. package/templates/react-monorepo/files/packages/ui/eslint.config.mjs +13 -0
  563. package/templates/react-monorepo/files/packages/ui/package.json +44 -0
  564. package/templates/react-monorepo/files/packages/ui/src/components/brand/logo.tsx +51 -0
  565. package/templates/react-monorepo/files/packages/ui/src/components/brand/social-icons.tsx +37 -0
  566. package/templates/react-monorepo/files/packages/ui/src/components/form/form.tsx +120 -0
  567. package/templates/react-monorepo/files/packages/ui/src/components/ui/alert.tsx +25 -0
  568. package/templates/react-monorepo/files/packages/ui/src/components/ui/button.tsx +43 -0
  569. package/templates/react-monorepo/files/packages/ui/src/components/ui/calendar.tsx +78 -0
  570. package/templates/react-monorepo/files/packages/ui/src/components/ui/card.tsx +44 -0
  571. package/templates/react-monorepo/files/packages/ui/src/components/ui/checkbox.tsx +16 -0
  572. package/templates/react-monorepo/files/packages/ui/src/components/ui/date-picker.tsx +76 -0
  573. package/templates/react-monorepo/files/packages/ui/src/components/ui/input.tsx +22 -0
  574. package/templates/react-monorepo/files/packages/ui/src/components/ui/label.tsx +19 -0
  575. package/templates/react-monorepo/files/packages/ui/src/components/ui/modal.tsx +94 -0
  576. package/templates/react-monorepo/files/packages/ui/src/components/ui/popover.tsx +31 -0
  577. package/templates/react-monorepo/files/packages/ui/src/components/ui/progress.tsx +33 -0
  578. package/templates/react-monorepo/files/packages/ui/src/components/ui/search-input.tsx +69 -0
  579. package/templates/react-monorepo/files/packages/ui/src/components/ui/select.tsx +80 -0
  580. package/templates/react-monorepo/files/packages/ui/src/components/ui/skeleton.tsx +12 -0
  581. package/templates/react-monorepo/files/packages/ui/src/components/ui/sonner.tsx +23 -0
  582. package/templates/react-monorepo/files/packages/ui/src/components/ui/spinner.tsx +8 -0
  583. package/templates/react-monorepo/files/packages/ui/src/components/ui/textarea.tsx +22 -0
  584. package/templates/react-monorepo/files/packages/ui/src/index.ts +45 -0
  585. package/templates/react-monorepo/files/packages/ui/src/lib/utils.ts +7 -0
  586. package/templates/react-monorepo/files/packages/ui/src/lib/zod-setup.ts +28 -0
  587. package/templates/react-monorepo/files/packages/ui/src/meta/brand.ts +18 -0
  588. package/templates/react-monorepo/files/packages/ui/src/meta/meta-context.ts +30 -0
  589. package/templates/react-monorepo/files/packages/ui/src/meta/meta-provider.tsx +24 -0
  590. package/templates/react-monorepo/files/packages/ui/src/meta/page-meta.tsx +69 -0
  591. package/templates/react-monorepo/files/packages/ui/src/meta/use-meta-config.ts +11 -0
  592. package/templates/react-monorepo/files/packages/ui/src/styles/globals.css +285 -0
  593. package/templates/react-monorepo/files/packages/ui/src/theme/init-script.ts +23 -0
  594. package/templates/react-monorepo/files/packages/ui/src/theme/theme-colors.ts +14 -0
  595. package/templates/react-monorepo/files/packages/ui/src/theme/theme-context.ts +60 -0
  596. package/templates/react-monorepo/files/packages/ui/src/theme/theme-provider.tsx +101 -0
  597. package/templates/react-monorepo/files/packages/ui/src/theme/theme-toggle.tsx +40 -0
  598. package/templates/react-monorepo/files/packages/ui/src/theme/use-theme.ts +11 -0
  599. package/templates/react-monorepo/files/packages/ui/tsconfig.json +9 -0
  600. package/templates/react-monorepo/files/turbo.json +24 -0
  601. package/templates/react-monorepo/template.json +230 -0
@@ -0,0 +1,136 @@
1
+ # Static host for a built Vite SPA. One config serves every app in the repo —
2
+ # the Dockerfile copies it into whichever image it is building.
3
+ #
4
+ # The cache rules are load-bearing, not cosmetic: get them wrong and returning
5
+ # users are pinned to a stale build. Rule of thumb — anything with a content
6
+ # hash in its name is immutable forever; anything with a stable name must be
7
+ # revalidated every time.
8
+
9
+ server {
10
+ listen 80;
11
+ server_name _;
12
+ root /usr/share/nginx/html;
13
+ index index.html;
14
+
15
+ # Compose/orchestrator health probe. Must not be logged or it floods access.log.
16
+ location = /healthz {
17
+ access_log off;
18
+ add_header Content-Type text/plain;
19
+ return 200 "ok\n";
20
+ }
21
+
22
+ gzip on;
23
+ gzip_vary on;
24
+ gzip_min_length 1024;
25
+ gzip_proxied any;
26
+ gzip_types text/plain text/css application/javascript application/json
27
+ image/svg+xml application/manifest+json;
28
+
29
+ # Vite emits content-hashed filenames here, so the content can never change
30
+ # under a given URL — safe to cache for a year.
31
+ # NB: no `expires` directive. `expires` emits its own Cache-Control, so
32
+ # combining it with add_header sends the header twice and clients disagree on
33
+ # which wins — `immutable` can be dropped. One explicit header instead.
34
+ location /assets/ {
35
+ add_header Cache-Control "public, max-age=31536000, immutable" always;
36
+ try_files $uri =404;
37
+ }
38
+
39
+ # The service worker and its Workbox runtime keep STABLE names across builds.
40
+ # If these are cached the browser keeps running the old worker, which keeps
41
+ # serving the old precached app shell — users get stuck on a previous version
42
+ # with no way out but a hard reload. Only a PWA build ships them; the rule is
43
+ # harmless in the images that don't.
44
+ location ~* ^/(sw\.js|registerSW\.js|workbox-[^/]+\.js)$ {
45
+ add_header Cache-Control "no-cache, must-revalidate" always;
46
+ expires off;
47
+ try_files $uri =404;
48
+ }
49
+
50
+ # Re-checked on every load so a deploy is picked up immediately, and so the
51
+ # manifest's start_url/icons stay in step with the installed PWA.
52
+ location ~* ^/(manifest\.webmanifest|manifest\.json)$ {
53
+ add_header Cache-Control "no-cache, must-revalidate" always;
54
+ expires off;
55
+ try_files $uri =404;
56
+ }
57
+
58
+ # Long-lived but not immutable: stable names, so they must stay revalidatable,
59
+ # but they rarely change.
60
+ location ~* \.(png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ {
61
+ add_header Cache-Control "public, max-age=604800" always;
62
+ try_files $uri =404;
63
+ }
64
+
65
+ # SPA fallback. index.html is never cached — it references the hashed bundles,
66
+ # so a cached copy would keep pointing at assets from the previous deploy.
67
+ #
68
+ # `$uri/index.html` comes BEFORE `$uri/` deliberately. If you add a prerender
69
+ # pass that emits `about/index.html`, matching `$uri/` on a directory makes
70
+ # nginx 301 to the trailing-slash form — verified: `/about` redirected to
71
+ # `/about/`. That fights the canonical tag and the sitemap, which both use the
72
+ # slashless `/about`, and adds a hop for every crawler. Serving the file
73
+ # directly returns 200 at the canonical URL. Harmless for a pure SPA: it ships
74
+ # no such directories, so the extra probe never matches.
75
+ location / {
76
+ try_files $uri $uri/index.html $uri/ @fallback;
77
+ add_header Cache-Control "no-cache, must-revalidate" always;
78
+ expires off;
79
+ }
80
+
81
+ # What an unknown path gets — and it has to differ per app, so it is decided
82
+ # by what the image actually contains rather than by a second config file the
83
+ # cache rules above would have to be duplicated into.
84
+ #
85
+ # A prerendered marketing site should emit `404.html` and answer 404. Without
86
+ # one it falls through to `index.html`, and if that is a fully prerendered
87
+ # homepage then every typo'd or stale URL returns 200 with a complete copy of
88
+ # `/` that canonicalises to `/`. A soft 404 with a body is worse than one
89
+ # without: there is something for a crawler to index.
90
+ #
91
+ # A pure SPA ships no `404.html` and keeps the index.html fallback. Every route
92
+ # it owns is resolved client-side, so answering 404 would break the app rather
93
+ # than describe it.
94
+ #
95
+ # `if` is limited here to `return` and `rewrite ... last`, the two directives
96
+ # documented as safe inside a location.
97
+ location @fallback {
98
+ error_page 404 /404.html;
99
+ if (-f $document_root/404.html) {
100
+ return 404;
101
+ }
102
+ rewrite ^ /index.html last;
103
+ }
104
+
105
+ # Reachable only through error_page, so the page cannot also be served as a
106
+ # regular 200 at /404.html and be indexed on its own.
107
+ location = /404.html {
108
+ internal;
109
+ add_header Cache-Control "no-cache, must-revalidate" always;
110
+ expires off;
111
+ }
112
+
113
+ # Modest hardening. Deliberately NO Content-Security-Policy header: the apps
114
+ # ship an inline <script> in index.html that applies the saved theme before
115
+ # first paint, and a default CSP without 'unsafe-inline' would block it and
116
+ # reintroduce the light-mode flash. Add CSP only with a nonce/hash for that
117
+ # script. HSTS belongs on the TLS-terminating proxy in front of this, not here.
118
+ add_header X-Content-Type-Options "nosniff" always;
119
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
120
+ add_header X-Frame-Options "SAMEORIGIN" always;
121
+
122
+ # RFC 9116 puts security.txt under /.well-known/, which the deny below would
123
+ # otherwise 403 — that is the whole point of this block. `^~` is what makes the
124
+ # exception stick: it tells nginx that once this prefix matches, regex
125
+ # locations are skipped entirely, so `location ~ /\.` never gets a look at it.
126
+ # Deliberately adds no headers of its own, because `add_header` in a location
127
+ # REPLACES the inherited set rather than extending it — the server-level
128
+ # hardening headers above would silently stop applying here.
129
+ location ^~ /.well-known/ {
130
+ try_files $uri =404;
131
+ }
132
+
133
+ location ~ /\. {
134
+ deny all;
135
+ }
136
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "acme-frontend",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "packageManager": "yarn@4.5.3",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "workspaces": [
10
+ "apps/*",
11
+ "packages/*"
12
+ ],
13
+ "scripts": {
14
+ "build": "turbo run build",
15
+ "dev": "turbo run dev",
16
+ "lint": "turbo run lint",
17
+ "typecheck": "turbo run typecheck",
18
+ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,mjs,json,md,css}\"",
19
+ "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mjs,json,md,css}\"",
20
+ "prepare": "husky",
21
+ "secrets": "turbo run secrets"
22
+ },
23
+ "lint-staged": {
24
+ "*.{ts,tsx,js,jsx,mjs,json,md,css}": "prettier --write"
25
+ },
26
+ "devDependencies": {
27
+ "eslint": "^10.3.0",
28
+ "husky": "^9.1.7",
29
+ "lint-staged": "^15.3.0",
30
+ "prettier": "^3.4.2",
31
+ "turbo": "^2.5.3",
32
+ "typescript": "~6.0.2"
33
+ },
34
+ "resolutions": {
35
+ "typescript": "~6.0.2"
36
+ }
37
+ }
@@ -0,0 +1,3 @@
1
+ import config from '@acme/config/eslint';
2
+
3
+ export default config;
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@acme/api",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "scripts": {
10
+ "lint": "eslint .",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@acme/types": "workspace:*",
15
+ "@reduxjs/toolkit": "^2.12.0",
16
+ "react-redux": "^9.3.0",
17
+ "socket.io-client": "^4.8.1"
18
+ },
19
+ "peerDependencies": {
20
+ "react": ">=19",
21
+ "react-dom": ">=19"
22
+ },
23
+ "devDependencies": {
24
+ "@acme/config": "workspace:*",
25
+ "@types/react": "^19.2.14",
26
+ "@types/react-dom": "^19.2.3",
27
+ "react": "^19.2.6",
28
+ "react-dom": "^19.2.6"
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ import { createApi } from '@reduxjs/toolkit/query/react';
2
+ import { baseQueryWithReauth } from './base-query';
3
+
4
+ /**
5
+ * The single shared RTK Query API. Feature endpoint slices extend it via
6
+ * `baseApi.injectEndpoints(...)` (code-split per feature), and realtime socket
7
+ * events feed cache updates through `baseApi.util.updateQueryData` / `invalidateTags`.
8
+ */
9
+ export const baseApi = createApi({
10
+ reducerPath: 'api',
11
+ baseQuery: baseQueryWithReauth,
12
+ tagTypes: [
13
+ // Cache tags, one per resource kind. A query lists what it `provides`, a
14
+ // mutation lists what it `invalidates`, and RTK Query refetches the
15
+ // intersection — so these names are the whole invalidation contract.
16
+ //
17
+ // One flat namespace shared by every feature slice: two features that both
18
+ // invent `Items` will invalidate each other's caches, silently. Add yours
19
+ // here rather than locally, and keep them specific.
20
+ 'CurrentUser',
21
+ 'Sessions'
22
+ ],
23
+ endpoints: () => ({})
24
+ });
@@ -0,0 +1,29 @@
1
+ import { fetchBaseQuery, type BaseQueryFn, type FetchArgs, type FetchBaseQueryError } from '@reduxjs/toolkit/query';
2
+ import { getApiBaseUrl, notifyUnauthorized } from './config';
3
+
4
+ const rawBaseQuery = fetchBaseQuery({
5
+ baseUrl: '',
6
+ credentials: 'include'
7
+ });
8
+
9
+ /**
10
+ * Shared base query: sends the Better Auth session cookie (credentials:'include'),
11
+ * prefixes the configured API base URL, and routes 401s to the app's re-auth
12
+ * handler (registered via configureApi) — the single chokepoint for session expiry.
13
+ */
14
+ export const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
15
+ args,
16
+ api,
17
+ extraOptions
18
+ ) => {
19
+ const base = getApiBaseUrl();
20
+ const withBase = typeof args === 'string' ? `${base}${args}` : { ...args, url: `${base}${args.url}` };
21
+
22
+ const result = await rawBaseQuery(withBase, api, extraOptions);
23
+
24
+ if (result.error && result.error.status === 401) {
25
+ notifyUnauthorized();
26
+ }
27
+
28
+ return result;
29
+ };
@@ -0,0 +1,21 @@
1
+ // Runtime configuration for @acme/api. The consuming app calls configureApi()
2
+ // once at startup (before creating the store or issuing requests) to inject the
3
+ // backend base URL and the 401 -> re-auth redirect handler (from @acme/auth).
4
+
5
+ let apiBaseUrl = '';
6
+ let onUnauthorized: () => void = () => {};
7
+
8
+ export function configureApi(options: { baseUrl: string; onUnauthorized?: () => void }): void {
9
+ apiBaseUrl = options.baseUrl.replace(/\/$/, '');
10
+ if (options.onUnauthorized) {
11
+ onUnauthorized = options.onUnauthorized;
12
+ }
13
+ }
14
+
15
+ export function getApiBaseUrl(): string {
16
+ return apiBaseUrl;
17
+ }
18
+
19
+ export function notifyUnauthorized(): void {
20
+ onUnauthorized();
21
+ }
@@ -0,0 +1,15 @@
1
+ // @acme/api — shared Redux store + RTK Query base + Socket.IO client.
2
+ export { baseApi } from './base-api';
3
+ export { baseQueryWithReauth } from './base-query';
4
+ export { uploadBaseQuery } from './upload-query';
5
+ export type { UploadArgs } from './upload-query';
6
+ export { configureApi, getApiBaseUrl, notifyUnauthorized } from './config';
7
+ export { makeStore } from './store';
8
+ export type { AppStore } from './store';
9
+ // @feature:start realtime
10
+ export { connectSocket, getSocket, disconnectSocket, joinRoom, leaveRoom } from './socket';
11
+ export { ServerEvent, ClientEvent, FEED_ROOM, channelRoom } from './realtime-events';
12
+ // @feature:end
13
+
14
+ // Convenience re-exports so apps get their Redux bindings from one place.
15
+ export { Provider, useDispatch, useSelector, useStore } from 'react-redux';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Frontend mirror of the backend realtime wire contract
3
+ * (backend: `src/realtime/constants`). Keep these names in lockstep with the
4
+ * server — they are the single source of truth for what the socket speaks, and
5
+ * a rename on one side that is not mirrored on the other fails silently: the
6
+ * listener simply never fires.
7
+ */
8
+
9
+ /** Server → client event names. */
10
+ export const ServerEvent = {
11
+ /** Presence roster changed. */
12
+ PRESENCE_UPDATE: 'presence:update',
13
+ /** Someone started/stopped typing in a room. */
14
+ TYPING: 'activity:typing',
15
+ /** Someone is viewing a resource. */
16
+ VIEWING: 'activity:viewing',
17
+ /** A user-directed notification. Scoped to `user:<id>`. */
18
+ NOTIFICATION: 'notification',
19
+ /** Something in a channel changed; payload is app-defined. */
20
+ CHANNEL_UPDATE: 'channel:update'
21
+ } as const;
22
+
23
+ /** Client → server event names. */
24
+ export const ClientEvent = {
25
+ JOIN_ROOM: 'room:join',
26
+ LEAVE_ROOM: 'room:leave',
27
+ TYPING: 'activity:typing',
28
+ VIEWING: 'activity:viewing'
29
+ } as const;
30
+
31
+ /** Firehose room — members see every broadcast on the default channel. */
32
+ export const FEED_ROOM = 'feed';
33
+ /** Room scoped to a single resource. */
34
+ export const channelRoom = (channelId: string) => `channel:${channelId}`;
@@ -0,0 +1,41 @@
1
+ import { io, type Socket } from 'socket.io-client';
2
+ import { ClientEvent } from './realtime-events';
3
+
4
+ let socket: Socket | null = null;
5
+
6
+ /**
7
+ * Connect the realtime Socket.IO client. The Better Auth session cookie is
8
+ * carried on the handshake via withCredentials. Feature event wiring + the RTK
9
+ * Query cache bridge land in Phase 2+.
10
+ */
11
+ export function connectSocket(url: string): Socket {
12
+ if (socket) {
13
+ if (!socket.connected) socket.connect();
14
+ return socket;
15
+ }
16
+ socket = io(url, {
17
+ withCredentials: true,
18
+ path: '/realtime',
19
+ autoConnect: true
20
+ });
21
+ return socket;
22
+ }
23
+
24
+ export function getSocket(): Socket | null {
25
+ return socket;
26
+ }
27
+
28
+ /** Join a server-side room (forum feed / thread). No-op until connected. */
29
+ export function joinRoom(room: string): void {
30
+ socket?.emit(ClientEvent.JOIN_ROOM, { room });
31
+ }
32
+
33
+ /** Leave a previously joined room. */
34
+ export function leaveRoom(room: string): void {
35
+ socket?.emit(ClientEvent.LEAVE_ROOM, { room });
36
+ }
37
+
38
+ export function disconnectSocket(): void {
39
+ socket?.disconnect();
40
+ socket = null;
41
+ }
@@ -0,0 +1,39 @@
1
+ import { configureStore, type Middleware, type Reducer } from '@reduxjs/toolkit';
2
+ import { setupListeners } from '@reduxjs/toolkit/query';
3
+ import { baseApi } from './base-api';
4
+
5
+ type MakeStoreOptions = {
6
+ /** App-specific slice reducers (e.g. authSlice, feature UI slices). */
7
+ reducer?: Record<string, Reducer>;
8
+ /** Extra middleware (e.g. the socket middleware). */
9
+ middleware?: Middleware[];
10
+ };
11
+
12
+ /**
13
+ * Create an app store wired with the shared RTK Query API + any app slices.
14
+ * Each app derives its own typed hooks from the returned store, e.g.:
15
+ * export const useAppDispatch = useDispatch.withTypes<typeof store.dispatch>();
16
+ * export const useAppSelector = useSelector.withTypes<ReturnType<typeof store.getState>>();
17
+ */
18
+ export function makeStore(options: MakeStoreOptions = {}) {
19
+ const store = configureStore({
20
+ reducer: {
21
+ [baseApi.reducerPath]: baseApi.reducer,
22
+ ...options.reducer
23
+ },
24
+ middleware: (getDefaultMiddleware) =>
25
+ getDefaultMiddleware({
26
+ serializableCheck: {
27
+ // Upload endpoints (see `uploadBaseQuery`) carry a FormData body and an
28
+ // `onProgress` callback in their args. Neither is serializable, and
29
+ // neither is ever persisted or replayed — only read once, in flight.
30
+ ignoredActionPaths: ['meta.arg.originalArgs.body', 'meta.arg.originalArgs.onProgress']
31
+ }
32
+ }).concat(baseApi.middleware, ...(options.middleware ?? []))
33
+ });
34
+
35
+ setupListeners(store.dispatch);
36
+ return store;
37
+ }
38
+
39
+ export type AppStore = ReturnType<typeof makeStore>;
@@ -0,0 +1,115 @@
1
+ import type { BaseQueryFn, FetchBaseQueryError } from '@reduxjs/toolkit/query';
2
+ import { getApiBaseUrl, notifyUnauthorized } from './config';
3
+
4
+ /** How long an upload may stall (no bytes acknowledged) before we give up. */
5
+ const DEFAULT_STALL_TIMEOUT_MS = 45_000;
6
+
7
+ export type UploadArgs = {
8
+ url: string;
9
+ body: FormData;
10
+ method?: 'POST' | 'PUT' | 'PATCH';
11
+ /** Called with 0-100 as the request body is written to the socket. */
12
+ onProgress?: (percent: number) => void;
13
+ /**
14
+ * Abort if no upload progress is observed for this many ms. Guards against a
15
+ * connection that opens, stalls mid-body and never errors on its own — the
16
+ * common failure on slow mobile uplinks.
17
+ */
18
+ stallTimeoutMs?: number;
19
+ };
20
+
21
+ /** RTK Query's own marker for an aborted-by-timeout request. */
22
+ const TIMEOUT_ERROR: FetchBaseQueryError = {
23
+ status: 'TIMEOUT_ERROR',
24
+ error: 'The upload stalled. Check your connection and try again.'
25
+ };
26
+
27
+ function parseBody(xhr: XMLHttpRequest): unknown {
28
+ const text = xhr.responseText;
29
+ if (!text) return undefined;
30
+ try {
31
+ return JSON.parse(text);
32
+ } catch {
33
+ return text;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Multipart uploads over XMLHttpRequest instead of `fetch`.
39
+ *
40
+ * `fetchBaseQuery` cannot report request-body progress (the Fetch API exposes no
41
+ * upload stream events), so a large file on a slow connection is indistinguishable
42
+ * from a hung request — the UI can only show an indefinite spinner. XHR still
43
+ * emits `upload.onprogress`, which lets callers render real progress and lets us
44
+ * abort a stalled transfer rather than waiting on the OS socket timeout.
45
+ *
46
+ * Mirrors `baseQueryWithReauth`: prefixes the configured API base URL, sends the
47
+ * session cookie, and routes 401s to the shared re-auth handler.
48
+ */
49
+ export const uploadBaseQuery: BaseQueryFn<UploadArgs, unknown, FetchBaseQueryError> = (args) =>
50
+ new Promise((resolve) => {
51
+ const { url, body, method = 'POST', onProgress, stallTimeoutMs = DEFAULT_STALL_TIMEOUT_MS } = args;
52
+ const xhr = new XMLHttpRequest();
53
+
54
+ let stallTimer: ReturnType<typeof setTimeout> | undefined;
55
+ let settled = false;
56
+
57
+ const cleanup = () => {
58
+ if (stallTimer) clearTimeout(stallTimer);
59
+ };
60
+
61
+ const settle = (result: { data: unknown } | { error: FetchBaseQueryError }) => {
62
+ if (settled) return;
63
+ settled = true;
64
+ cleanup();
65
+ resolve(result);
66
+ };
67
+
68
+ // Restarted on every progress event; only fires when the socket goes quiet.
69
+ const armStallTimer = () => {
70
+ if (stallTimer) clearTimeout(stallTimer);
71
+ stallTimer = setTimeout(() => {
72
+ xhr.abort();
73
+ settle({ error: TIMEOUT_ERROR });
74
+ }, stallTimeoutMs);
75
+ };
76
+
77
+ xhr.open(method, `${getApiBaseUrl()}${url}`, true);
78
+ // Matches fetchBaseQuery's `credentials: 'include'` — Better Auth session cookie.
79
+ xhr.withCredentials = true;
80
+
81
+ xhr.upload.onprogress = (event) => {
82
+ armStallTimer();
83
+ if (!event.lengthComputable || !onProgress) return;
84
+ onProgress(Math.round((event.loaded / event.total) * 100));
85
+ };
86
+
87
+ // Body is fully written; the server is now working. Keep the stall guard
88
+ // running so a server that accepts the bytes and then hangs still fails.
89
+ xhr.upload.onload = () => {
90
+ onProgress?.(100);
91
+ armStallTimer();
92
+ };
93
+
94
+ xhr.onload = () => {
95
+ const data = parseBody(xhr);
96
+ if (xhr.status >= 200 && xhr.status < 300) {
97
+ settle({ data });
98
+ return;
99
+ }
100
+ if (xhr.status === 401) notifyUnauthorized();
101
+ settle({ error: { status: xhr.status, data } as FetchBaseQueryError });
102
+ };
103
+
104
+ xhr.onerror = () => {
105
+ settle({
106
+ error: { status: 'FETCH_ERROR', error: 'The upload failed. Check your connection and try again.' }
107
+ });
108
+ };
109
+
110
+ // Fires when the transfer is cut mid-body — the truncated-upload case.
111
+ xhr.onabort = () => settle({ error: TIMEOUT_ERROR });
112
+
113
+ armStallTimer();
114
+ xhr.send(body);
115
+ });
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@acme/config/tsconfig/react.json",
3
+ "include": ["src"]
4
+ }
@@ -0,0 +1,3 @@
1
+ import config from '@acme/config/eslint';
2
+
3
+ export default config;
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@acme/auth",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "scripts": {
10
+ "lint": "eslint .",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "better-auth": "^1.6.2"
15
+ },
16
+ "peerDependencies": {
17
+ "@reduxjs/toolkit": "^2",
18
+ "react": ">=19",
19
+ "react-dom": ">=19",
20
+ "react-redux": "^9",
21
+ "react-router": ">=7"
22
+ },
23
+ "devDependencies": {
24
+ "@acme/config": "workspace:*",
25
+ "@reduxjs/toolkit": "^2.12.0",
26
+ "@types/react": "^19.2.14",
27
+ "@types/react-dom": "^19.2.3",
28
+ "react": "^19.2.6",
29
+ "react-dom": "^19.2.6",
30
+ "react-redux": "^9.3.0",
31
+ "react-router": "^8.2.0"
32
+ }
33
+ }
@@ -0,0 +1,27 @@
1
+ import { createAuthClient } from 'better-auth/react';
2
+ import { adminClient } from 'better-auth/client/plugins';
3
+
4
+ function makeClient(baseURL: string) {
5
+ // The admin plugin adds `authClient.admin.*` (list/ban/unban/setRole). The
6
+ // methods are always present but the backend enforces the caller's role, so
7
+ // including them here is harmless for non-admin apps.
8
+ return createAuthClient({ baseURL, plugins: [adminClient()] });
9
+ }
10
+
11
+ let client: ReturnType<typeof makeClient> | null = null;
12
+
13
+ /** Initialize the Better Auth client singleton (idempotent). */
14
+ export function initAuthClient(baseURL: string) {
15
+ client ??= makeClient(baseURL);
16
+ return client;
17
+ }
18
+
19
+ /** The Better Auth client. Throws if not initialized (call configureAuth first). */
20
+ export function getAuthClient() {
21
+ if (!client) {
22
+ throw new Error('Auth client not initialized. Call configureAuth({ authApiBaseUrl, ... }) at app startup.');
23
+ }
24
+ return client;
25
+ }
26
+
27
+ export type AuthClient = ReturnType<typeof makeClient>;
@@ -0,0 +1,22 @@
1
+ import { initAuthClient } from './client';
2
+ import { configureRedirects } from './redirect';
3
+
4
+ /**
5
+ * One-shot auth wiring, called by each app at startup (before rendering).
6
+ * - authApiBaseUrl: the backend Better Auth base URL (e.g. https://api…/api/auth)
7
+ * - authAppUrl: the Auth app origin unauthenticated users are sent to
8
+ * - trustedOrigins: origins allowed as post-login redirect targets
9
+ */
10
+ export function configureAuth(options: {
11
+ authApiBaseUrl: string;
12
+ authAppUrl: string;
13
+ trustedOrigins: string[];
14
+ defaultRedirect?: string;
15
+ }): void {
16
+ initAuthClient(options.authApiBaseUrl);
17
+ configureRedirects({
18
+ authAppUrl: options.authAppUrl,
19
+ trustedOrigins: options.trustedOrigins,
20
+ defaultRedirect: options.defaultRedirect
21
+ });
22
+ }