@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,142 @@
1
+ import { Suspense, lazy, type ReactNode } from 'react';
2
+ import { createBrowserRouter } from 'react-router';
3
+ import { ProtectedRoute, signOut, useAuth } from '@/shared/auth';
4
+ import { RoleRoute } from '@/shared/auth'; // @feature admin
5
+ import { AppShell, AppShellSkeleton, ErrorBoundary, MarketingLayout, NotFound, RouteFallback } from '@/shared/layouts';
6
+ import { Forbidden } from '@/shared/layouts'; // @feature admin
7
+ import { appPath } from '@/lib/paths';
8
+ import { ADMIN_BASE } from '@/lib/paths'; // @feature admin
9
+ import { NAV_ITEMS } from './nav';
10
+ import { ADMIN_NAV_ITEMS } from './nav'; // @feature admin
11
+ import { HomeRoute } from '@/routes/home';
12
+
13
+ /**
14
+ * Everything but the first page is lazy, so the initial download is the shell
15
+ * plus one route. The auth screens are lazy too: a logged-in user should never
16
+ * pay for the login form, and a logged-out one never pays for the app.
17
+ */
18
+ const SettingsRoute = lazy(() => import('@/routes/settings').then((m) => ({ default: m.SettingsRoute })));
19
+ const TermsRoute = lazy(() => import('@/routes/legal/terms').then((m) => ({ default: m.TermsRoute })));
20
+ const PrivacyRoute = lazy(() => import('@/routes/legal/privacy').then((m) => ({ default: m.PrivacyRoute })));
21
+ // @feature:start marketing
22
+ const LandingRoute = lazy(() => import('@/routes/marketing/home').then((m) => ({ default: m.HomeRoute })));
23
+ // @feature:end
24
+ // @feature:start auth-screens
25
+ const LoginRoute = lazy(() => import('@/routes/auth/login').then((m) => ({ default: m.LoginRoute })));
26
+ const SignupRoute = lazy(() => import('@/routes/auth/signup').then((m) => ({ default: m.SignupRoute })));
27
+ const ForgotPasswordRoute = lazy(() =>
28
+ import('@/routes/auth/forgot-password').then((m) => ({ default: m.ForgotPasswordRoute }))
29
+ );
30
+ const ResetPasswordRoute = lazy(() =>
31
+ import('@/routes/auth/reset-password').then((m) => ({ default: m.ResetPasswordRoute }))
32
+ );
33
+ const VerifyEmailRoute = lazy(() =>
34
+ import('@/routes/auth/verify-email').then((m) => ({ default: m.VerifyEmailRoute }))
35
+ );
36
+ const VerifiedRoute = lazy(() => import('@/routes/auth/verified').then((m) => ({ default: m.VerifiedRoute })));
37
+ // @feature:end
38
+ // @feature:start admin
39
+ const AdminHomeRoute = lazy(() => import('@/routes/admin/home').then((m) => ({ default: m.AdminHomeRoute })));
40
+ // @feature:end
41
+
42
+ const lazyRoute = (element: ReactNode) => <Suspense fallback={<RouteFallback />}>{element}</Suspense>;
43
+
44
+ /**
45
+ * The shell reads the session itself rather than taking it as a prop —
46
+ * `ProtectedRoute` has already established the user is authenticated by the time
47
+ * this renders.
48
+ *
49
+ * `ErrorBoundary` wraps the shell rather than being passed as the route's
50
+ * `errorElement`: it is a plain React error boundary taking `children`, so it
51
+ * catches render errors anywhere below, including inside a lazy chunk.
52
+ */
53
+ function Shell() {
54
+ const { user } = useAuth();
55
+ return (
56
+ <ErrorBoundary>
57
+ <AppShell nav={NAV_ITEMS} user={user ?? {}} onSignOut={() => void signOut()} />
58
+ </ErrorBoundary>
59
+ );
60
+ }
61
+
62
+ // @feature:start admin
63
+ function AdminShell() {
64
+ const { user } = useAuth();
65
+ return (
66
+ <ErrorBoundary>
67
+ <AppShell nav={ADMIN_NAV_ITEMS} user={user ?? {}} onSignOut={() => void signOut()} />
68
+ </ErrorBoundary>
69
+ );
70
+ }
71
+ // @feature:end
72
+
73
+ export const router = createBrowserRouter([
74
+ // @feature:start marketing
75
+ // The public marketing site. It owns `/`, which is the whole reason the
76
+ // authenticated app is mounted under a base path — see src/lib/paths.ts.
77
+ {
78
+ element: (
79
+ <ErrorBoundary>
80
+ <MarketingLayout />
81
+ </ErrorBoundary>
82
+ ),
83
+ children: [{ index: true, element: lazyRoute(<LandingRoute />) }]
84
+ },
85
+ // @feature:end
86
+ // Legal pages ship whether or not the marketing site does: the signup consent
87
+ // gate links to them, so they have to resolve either way.
88
+ {
89
+ element: (
90
+ <ErrorBoundary>
91
+ <MarketingLayout />
92
+ </ErrorBoundary>
93
+ ),
94
+ children: [
95
+ { path: 'terms', element: lazyRoute(<TermsRoute />) },
96
+ { path: 'privacy', element: lazyRoute(<PrivacyRoute />) }
97
+ ]
98
+ },
99
+ // @feature:start auth-screens
100
+ // Public, and deliberately flat: these paths are baked into verification and
101
+ // password-reset emails that are already in people's inboxes, so moving one
102
+ // breaks links that have already been sent.
103
+ { path: '/login', element: lazyRoute(<LoginRoute />) },
104
+ { path: '/signup', element: lazyRoute(<SignupRoute />) },
105
+ { path: '/forgot-password', element: lazyRoute(<ForgotPasswordRoute />) },
106
+ { path: '/reset-password', element: lazyRoute(<ResetPasswordRoute />) },
107
+ { path: '/verify-email', element: lazyRoute(<VerifyEmailRoute />) },
108
+ { path: '/verified', element: lazyRoute(<VerifiedRoute />) },
109
+ // @feature:end
110
+ {
111
+ // `/app` with the marketing site, `/` without it. Children stay relative so
112
+ // they follow it either way.
113
+ path: appPath(),
114
+ // `fallback` is required and must not be blank: the session round trip takes
115
+ // most of a second on a cold load, and rendering nothing for that long reads
116
+ // as a broken app.
117
+ element: (
118
+ <ProtectedRoute fallback={<AppShellSkeleton />}>
119
+ <Shell />
120
+ </ProtectedRoute>
121
+ ),
122
+ children: [
123
+ { index: true, element: <HomeRoute /> },
124
+ { path: 'settings', element: lazyRoute(<SettingsRoute />) }
125
+ ]
126
+ },
127
+ // @feature:start admin
128
+ {
129
+ path: ADMIN_BASE,
130
+ // The role gate is UI, not security: it decides what to render from the
131
+ // session the browser holds. Every endpoint behind it enforces the same
132
+ // role server-side, or the console is an honour system.
133
+ element: (
134
+ <RoleRoute allow={['admin', 'superuser']} fallback={<AppShellSkeleton />} forbidden={<Forbidden />}>
135
+ <AdminShell />
136
+ </RoleRoute>
137
+ ),
138
+ children: [{ index: true, element: lazyRoute(<AdminHomeRoute />) }]
139
+ },
140
+ // @feature:end
141
+ { path: '*', element: <NotFound /> }
142
+ ]);
@@ -0,0 +1,20 @@
1
+ import { makeStore } from '@/shared/api';
2
+ import { authReducer } from '@/shared/auth';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+
5
+ /**
6
+ * The one store. `makeStore` wires the shared RTK Query `baseApi`; slices are
7
+ * added here. Feature endpoint slices do *not* register themselves — they extend
8
+ * `baseApi` via `injectEndpoints`, which needs no change in this file.
9
+ */
10
+ export const store = makeStore({
11
+ reducer: {
12
+ auth: authReducer
13
+ }
14
+ });
15
+
16
+ export type RootState = ReturnType<typeof store.getState>;
17
+ export type AppDispatch = typeof store.dispatch;
18
+
19
+ export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
20
+ export const useAppSelector = useSelector.withTypes<RootState>();
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/ui';
3
+
4
+ type AuthCardProps = {
5
+ title: string;
6
+ description?: ReactNode;
7
+ children: ReactNode;
8
+ footer?: ReactNode;
9
+ };
10
+
11
+ /** Standard auth surface: white 24px card, 900-weight heading, optional footer. */
12
+ export function AuthCard({ title, description, children, footer }: AuthCardProps) {
13
+ return (
14
+ <Card>
15
+ <CardHeader>
16
+ <CardTitle className="font-black">{title}</CardTitle>
17
+ {description ? <CardDescription className="text-body">{description}</CardDescription> : null}
18
+ </CardHeader>
19
+ <CardContent>{children}</CardContent>
20
+ {footer ? <p className="text-center text-sm text-body">{footer}</p> : null}
21
+ </Card>
22
+ );
23
+ }
@@ -0,0 +1,32 @@
1
+ import type { ReactNode } from 'react';
2
+ import { useField } from 'formik';
3
+ import { Checkbox, FormError } from '@/shared/ui';
4
+
5
+ /**
6
+ * Formik-bound checkbox with the design's inline label treatment.
7
+ *
8
+ * `label` is a node, not a string, because the signup consent needs links to
9
+ * Terms and Privacy inside it. Clicking one does not toggle the box: HTML
10
+ * defines a label's activation behaviour as doing nothing for events targeted
11
+ * at interactive descendants.
12
+ *
13
+ * `items-start` rather than the design's `center`: the design only has this on
14
+ * the single-line "Remember me", and consent copy wraps to two or three lines on
15
+ * a phone — centring would then float the box against the middle of the block.
16
+ * `mt-0.5` puts it on the first line's optical centre, so the single-line case
17
+ * looks unchanged.
18
+ */
19
+ export function CheckboxField({ name, label }: { name: string; label: ReactNode }) {
20
+ const [field, meta] = useField({ name, type: 'checkbox' });
21
+ const showError = meta.touched && Boolean(meta.error);
22
+
23
+ return (
24
+ <div className="flex flex-col gap-1.5">
25
+ <label className="flex cursor-pointer items-start gap-2.5">
26
+ <Checkbox {...field} checked={field.value} aria-invalid={showError || undefined} className="mt-0.5" />
27
+ <span className="text-sm text-body">{label}</span>
28
+ </label>
29
+ {showError ? <FormError>{meta.error}</FormError> : null}
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,41 @@
1
+ import { Button } from '@/shared/ui';
2
+
3
+ function GoogleIcon() {
4
+ return (
5
+ <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
6
+ <path
7
+ fill="#4285F4"
8
+ d="M23.5 12.3c0-.9-.1-1.5-.3-2.2H12v4.1h6.5c-.1 1.1-.8 2.7-2.4 3.8l3.7 2.9c2.3-2.1 3.7-5.1 3.7-8.6z"
9
+ />
10
+ <path
11
+ fill="#34A853"
12
+ d="M12 24c3.2 0 6-1.1 7.9-2.9l-3.7-2.9c-1 .7-2.4 1.2-4.2 1.2-3.2 0-6-2.1-6.9-5.1L1.2 17.2C3.2 21.2 7.3 24 12 24z"
13
+ />
14
+ <path
15
+ fill="#FBBC05"
16
+ d="M5.1 14.3c-.2-.7-.4-1.5-.4-2.3s.2-1.6.4-2.3L1.2 6.8C.4 8.4 0 10.1 0 12s.4 3.6 1.2 5.2l3.9-2.9z"
17
+ />
18
+ <path
19
+ fill="#EA4335"
20
+ d="M12 4.6c1.8 0 3 .8 3.7 1.4l3.3-3.2C17 1 14.2 0 12 0 7.3 0 3.2 2.8 1.2 6.8l3.9 2.9c1-3 3.7-5.1 6.9-5.1z"
21
+ />
22
+ </svg>
23
+ );
24
+ }
25
+
26
+ export function GoogleButton({
27
+ onClick,
28
+ disabled,
29
+ label = 'Continue with Google'
30
+ }: {
31
+ onClick: () => void;
32
+ disabled?: boolean;
33
+ label?: string;
34
+ }) {
35
+ return (
36
+ <Button type="button" variant="outline" className="w-full" onClick={onClick} disabled={disabled}>
37
+ <GoogleIcon />
38
+ {label}
39
+ </Button>
40
+ );
41
+ }
@@ -0,0 +1,9 @@
1
+ export function OrDivider({ label = 'or continue with' }: { label?: string }) {
2
+ return (
3
+ <div className="flex items-center gap-3">
4
+ <span className="h-px flex-1 bg-border" />
5
+ <span className="text-xs font-medium text-mute">{label}</span>
6
+ <span className="h-px flex-1 bg-border" />
7
+ </div>
8
+ );
9
+ }
@@ -0,0 +1,70 @@
1
+ import { type ReactNode, useState } from 'react';
2
+ import { useField } from 'formik';
3
+ import { FormError, Input, Label, cn } from '@/shared/ui';
4
+ import { scorePassword } from '../lib/password-strength';
5
+
6
+ function StrengthMeter({ score }: { score: number }) {
7
+ const activeColor = ['bg-border', 'bg-negative', 'bg-warning', 'bg-positive'][score];
8
+ return (
9
+ <div className="mt-1 flex gap-1" aria-hidden="true">
10
+ {[1, 2, 3].map((bar) => (
11
+ <span key={bar} className={cn('h-1 flex-1 rounded-full', bar <= score ? activeColor : 'bg-border')} />
12
+ ))}
13
+ </div>
14
+ );
15
+ }
16
+
17
+ type PasswordFieldProps = {
18
+ name: string;
19
+ label?: string;
20
+ placeholder?: string;
21
+ autoComplete?: string;
22
+ showStrength?: boolean;
23
+ /** Replaces the show/hide toggle in the label row (e.g. a "Forgot password?" link). */
24
+ action?: ReactNode;
25
+ };
26
+
27
+ export function PasswordField({
28
+ name,
29
+ label = 'Password',
30
+ placeholder,
31
+ autoComplete = 'current-password',
32
+ showStrength = false,
33
+ action
34
+ }: PasswordFieldProps) {
35
+ const [field, meta] = useField(name);
36
+ const [visible, setVisible] = useState(false);
37
+ const showError = meta.touched && Boolean(meta.error);
38
+ const strength = showStrength ? scorePassword((field.value as string) ?? '') : null;
39
+
40
+ return (
41
+ <div className="flex flex-col gap-1.5">
42
+ <div className="flex items-center justify-between">
43
+ <Label htmlFor={name}>{label}</Label>
44
+ {action ?? (
45
+ <button
46
+ type="button"
47
+ onClick={() => setVisible((value) => !value)}
48
+ className="text-xs font-semibold text-ink-deep"
49
+ >
50
+ {visible ? 'Hide' : 'Show'}
51
+ </button>
52
+ )}
53
+ </div>
54
+ <Input
55
+ id={name}
56
+ type={visible ? 'text' : 'password'}
57
+ placeholder={placeholder}
58
+ autoComplete={autoComplete}
59
+ aria-invalid={showError || undefined}
60
+ {...field}
61
+ />
62
+ {strength ? <StrengthMeter score={strength.score} /> : null}
63
+ {showError ? (
64
+ <FormError>{meta.error}</FormError>
65
+ ) : strength ? (
66
+ <p className="text-xs text-mute">{strength.label}</p>
67
+ ) : null}
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,16 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import { Button, Spinner, cn } from '@/shared/ui';
3
+
4
+ type SubmitButtonProps = ComponentProps<typeof Button> & {
5
+ loading?: boolean;
6
+ children: ReactNode;
7
+ };
8
+
9
+ export function SubmitButton({ loading, children, className, disabled, ...props }: SubmitButtonProps) {
10
+ return (
11
+ <Button type="submit" className={cn('w-full', className)} disabled={disabled || loading} {...props}>
12
+ {loading ? <Spinner /> : null}
13
+ {children}
14
+ </Button>
15
+ );
16
+ }
@@ -0,0 +1,68 @@
1
+ import { getAuthClient } from '@/shared/auth';
2
+
3
+ type ActionResult = { error?: string };
4
+
5
+ function messageOf(error: { message?: string } | null | undefined, fallback: string): string {
6
+ return error?.message ?? fallback;
7
+ }
8
+
9
+ export async function loginWithEmail(input: {
10
+ email: string;
11
+ password: string;
12
+ remember?: boolean;
13
+ }): Promise<ActionResult> {
14
+ const { error } = await getAuthClient().signIn.email({
15
+ email: input.email,
16
+ password: input.password,
17
+ rememberMe: input.remember ?? true
18
+ });
19
+ return error ? { error: messageOf(error, 'Could not log in. Check your details and try again.') } : {};
20
+ }
21
+
22
+ export async function signupWithEmail(input: {
23
+ name: string;
24
+ email: string;
25
+ password: string;
26
+ callbackURL?: string;
27
+ }): Promise<ActionResult> {
28
+ const { error } = await getAuthClient().signUp.email({
29
+ name: input.name,
30
+ email: input.email,
31
+ password: input.password,
32
+ callbackURL: input.callbackURL
33
+ });
34
+ return error ? { error: messageOf(error, 'Could not create your account.') } : {};
35
+ }
36
+
37
+ export async function signInWithGoogle(options: {
38
+ /** Where existing users land after sign-in (usually the client app). */
39
+ callbackURL?: string;
40
+ /** Where first-time users land (the onboarding wizard). */
41
+ newUserCallbackURL?: string;
42
+ }): Promise<void> {
43
+ await getAuthClient().signIn.social({
44
+ provider: 'google',
45
+ callbackURL: options.callbackURL,
46
+ newUserCallbackURL: options.newUserCallbackURL
47
+ });
48
+ }
49
+
50
+ export async function requestPasswordReset(input: { email: string; redirectTo?: string }): Promise<ActionResult> {
51
+ const { error } = await getAuthClient().requestPasswordReset({ email: input.email, redirectTo: input.redirectTo });
52
+ return error ? { error: messageOf(error, 'Could not send the reset link.') } : {};
53
+ }
54
+
55
+ export async function resetPasswordWithToken(input: { newPassword: string; token: string }): Promise<ActionResult> {
56
+ const { error } = await getAuthClient().resetPassword({ newPassword: input.newPassword, token: input.token });
57
+ return error ? { error: messageOf(error, 'Could not reset your password. The link may have expired.') } : {};
58
+ }
59
+
60
+ export async function resendVerification(input: { email: string; callbackURL?: string }): Promise<ActionResult> {
61
+ const { error } = await getAuthClient().sendVerificationEmail({ email: input.email, callbackURL: input.callbackURL });
62
+ return error ? { error: messageOf(error, 'Could not resend the email.') } : {};
63
+ }
64
+
65
+ export async function verifyEmailToken(token: string): Promise<ActionResult> {
66
+ const { error } = await getAuthClient().verifyEmail({ query: { token } });
67
+ return error ? { error: messageOf(error, 'Could not verify your email. The link may have expired.') } : {};
68
+ }
@@ -0,0 +1,20 @@
1
+ export type PasswordStrength = {
2
+ /** 0 (empty/too short) … 3 (strong) */
3
+ score: 0 | 1 | 2 | 3;
4
+ label: string;
5
+ };
6
+
7
+ /** Lightweight strength heuristic driving the 3-bar meter on the signup screen. */
8
+ export function scorePassword(password: string): PasswordStrength {
9
+ if (!password) {
10
+ return { score: 0, label: 'Use 8+ characters with a mix of letters and numbers.' };
11
+ }
12
+ let points = 0;
13
+ if (password.length >= 8) points += 1;
14
+ if (/[a-z]/.test(password) && /[A-Z]/.test(password)) points += 1;
15
+ if (/\d/.test(password) || /[^A-Za-z0-9]/.test(password)) points += 1;
16
+
17
+ const score = Math.min(points, 3) as 0 | 1 | 2 | 3;
18
+ const labels = ['Too short', 'Weak — add more variety', 'Getting there', 'Strong password'];
19
+ return { score, label: labels[score] };
20
+ }
@@ -0,0 +1,6 @@
1
+ /** Append the current `redirect` param to an in-app path (threads it across screens). */
2
+ export function withRedirect(path: string, redirect: string | null): string {
3
+ if (!redirect) return path;
4
+ const separator = path.includes('?') ? '&' : '?';
5
+ return `${path}${separator}redirect=${encodeURIComponent(redirect)}`;
6
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+
3
+ export const loginSchema = z.object({
4
+ email: z.string().min(1, 'Email is required').email('Enter a valid email'),
5
+ password: z.string().min(1, 'Password is required'),
6
+ remember: z.boolean().optional()
7
+ });
8
+ export type LoginValues = z.infer<typeof loginSchema>;
9
+
10
+ export const signupSchema = z.object({
11
+ name: z.string().min(1, 'Enter your name'),
12
+ email: z.string().min(1, 'Email is required').email('Enter a valid email'),
13
+ password: z.string().min(8, 'At least 8 characters'),
14
+ // Consent is a gate, not a preference, so it is validated rather than merely
15
+ // displayed. It is stripped before the payload reaches the API — see
16
+ // `SignupRoute`; nothing on the server takes it.
17
+ acceptedTerms: z.boolean().refine((accepted) => accepted, {
18
+ message: 'Please accept the terms to continue'
19
+ })
20
+ });
21
+ export type SignupValues = z.infer<typeof signupSchema>;
22
+
23
+ export const forgotSchema = z.object({
24
+ email: z.string().min(1, 'Email is required').email('Enter a valid email')
25
+ });
26
+ export type ForgotValues = z.infer<typeof forgotSchema>;
27
+
28
+ export const resetSchema = z
29
+ .object({
30
+ password: z.string().min(8, 'At least 8 characters'),
31
+ confirm: z.string().min(1, 'Confirm your password')
32
+ })
33
+ .refine((values) => values.password === values.confirm, {
34
+ message: "Passwords don't match yet.",
35
+ path: ['confirm']
36
+ });
37
+ export type ResetValues = z.infer<typeof resetSchema>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Every indexable route, with the metadata that describes it.
3
+ *
4
+ * **This is the single source.** `PageSeo` renders per-route tags from it and
5
+ * the build emits `sitemap.xml` from it (see `vite.config.ts`), so the sitemap
6
+ * cannot list a route that does not exist or miss one that does — the classic
7
+ * way a sitemap rots. Add a route here on the same commit that adds it to
8
+ * `app/router.tsx`, and remove it the same way; a sitemap advertising a 404
9
+ * stays invisible until a crawler finds it.
10
+ *
11
+ * Kept free of React imports: `vite.config.ts` imports this directly, and the
12
+ * config is bundled with esbuild.
13
+ */
14
+ export type SeoRoute = {
15
+ /** Path as it appears in the router, leading slash, no trailing slash. */
16
+ path: string;
17
+ /** Page segment only — `MetaProvider` appends the brand suffix. */
18
+ title: string;
19
+ description: string;
20
+ /** Relative priority for the sitemap; the homepage leads. */
21
+ priority: string;
22
+ changefreq: 'daily' | 'weekly' | 'monthly' | 'yearly';
23
+ };
24
+
25
+ export const SEO_ROUTES: SeoRoute[] = [
26
+ {
27
+ path: '/',
28
+ title: 'Home',
29
+ description: 'Replace with the description search results should show.',
30
+ priority: '1.0',
31
+ changefreq: 'weekly'
32
+ }
33
+ ];
34
+
35
+ /**
36
+ * The one origin that may be indexed. Staging and preview builds compare
37
+ * unequal and are served `noindex`, so a preview deploy cannot outrank
38
+ * production for its own content.
39
+ */
40
+ export const PRODUCTION_ORIGIN = 'https://example.com';
41
+
42
+ export const seoRouteFor = (path: string) => SEO_ROUTES.find((route) => route.path === path);
43
+
44
+ /**
45
+ * Absolute URL for a path — canonical, `og:url` and sitemap entries all need one.
46
+ *
47
+ * `origin` is passed in rather than read here so this file stays free of
48
+ * `import.meta.env`: it is imported by `vite.config.ts`, where `import.meta.env`
49
+ * does not exist. App code gets the origin from its env, the build from its own
50
+ * `loadEnv`.
51
+ */
52
+ export const absoluteUrl = (path: string, origin: string) => (path === '/' ? `${origin}/` : `${origin}${path}`);
53
+
54
+ /** Strip a trailing slash so joins don't produce `//about`. */
55
+ export const normalizeOrigin = (origin: string) => origin.replace(/\/$/, '');
56
+
57
+ /*
58
+ * Not included here: **prerendering**. `vite build` emits a single SPA shell,
59
+ * so a crawler that does not execute JavaScript sees an empty `#root` for every
60
+ * route. `robots.txt` and `sitemap.xml` are emitted from this table at build
61
+ * time (see `vite.config.ts`), which is the half that matters for discovery —
62
+ * but if these pages need to rank on their content, add a post-build pass that
63
+ * renders each `SEO_ROUTES` entry to `dist/<path>/index.html`. Drive it from
64
+ * this table, not a second list, or the sitemap and the prerendered set drift.
65
+ */
@@ -0,0 +1,28 @@
1
+ const env = import.meta.env;
2
+
3
+ /** Backend base URL. Better Auth lives at `${API_URL}/auth`. */
4
+ export const API_URL = env.VITE_APP_API_URL as string;
5
+
6
+ /**
7
+ * This app's own origin, read from the browser rather than from a build-time
8
+ * variable.
9
+ *
10
+ * One deployment means one origin, so there is nothing a `VITE_APP_URL` could
11
+ * add here and plenty it could get wrong: Vite inlines env at build time, so a
12
+ * stale value baked into an image sends every verification link to the previous
13
+ * environment — and that failure is invisible until a user clicks one.
14
+ *
15
+ * The only place an absolute URL is genuinely needed is a link that lands in an
16
+ * email, and by then the browser has already told us where we are.
17
+ */
18
+ export const APP_ORIGIN = window.location.origin;
19
+
20
+ // @feature:start marketing
21
+ /**
22
+ * Canonical public origin, for SEO tags only — the one origin that may be
23
+ * indexed. Unset (previews, staging) means "not production", and the build
24
+ * emits `noindex` plus a disallowing robots.txt rather than letting a preview
25
+ * deploy outrank production for its own content. See `vite.config.ts`.
26
+ */
27
+ export const SITE_URL = (env.VITE_APP_SITE_URL as string | undefined) ?? '';
28
+ // @feature:end
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Where things live in the URL space.
3
+ *
4
+ * One deployment serves the marketing pages, the auth screens and the
5
+ * authenticated app, so unlike a multi-app build these cannot each own `/` on
6
+ * their own host — the split has to happen in the path, and it has to happen in
7
+ * one place or the router, the nav and the post-login redirect will disagree.
8
+ */
9
+
10
+ /**
11
+ * The mount point of the authenticated app, as a path split on `/`.
12
+ *
13
+ * A list rather than two competing `const` declarations because a generated
14
+ * project has to compile in *every* feature combination, and only one of two
15
+ * declarations of the same name can survive the strip. Drop `marketing` and only
16
+ * the leading empty segment is left, putting the app back at the root.
17
+ *
18
+ * The `''` is not filler: an absolute path's first segment *is* empty —
19
+ * `'/app'.split('/')` is `['', 'app']`. It also keeps this array non-empty in
20
+ * every build, which matters because a literal that strips down to `[\n]` is no
21
+ * longer formatted the way Prettier wants it, and `format:check` runs in CI.
22
+ */
23
+ const BASE_SEGMENTS: string[] = [
24
+ '', // the leading slash
25
+ 'app' // @feature marketing
26
+ ];
27
+
28
+ /** `/app` when the marketing site owns `/`; otherwise `''`. */
29
+ export const APP_BASE = BASE_SEGMENTS.join('/');
30
+
31
+ /**
32
+ * Absolute path to a route inside the authenticated app.
33
+ *
34
+ * appPath() -> '/app' (or '/')
35
+ * appPath('settings') -> '/app/settings' (or '/settings')
36
+ *
37
+ * Use it for anything that has to name a route from outside the router — nav
38
+ * items, redirect targets, links in emails. Routes *inside* the app shell can
39
+ * keep using relative paths and need none of this.
40
+ */
41
+ export const appPath = (path = '') => `${APP_BASE}/${path}`.replace(/\/+/g, '/').replace(/(.)\/$/, '$1');
42
+
43
+ /**
44
+ * Legal pages ship whether or not the marketing site does: the signup consent
45
+ * gate links to them, and a consent checkbox pointing at a 404 is worse than no
46
+ * checkbox at all.
47
+ */
48
+ export const TERMS_PATH = '/terms';
49
+ export const PRIVACY_PATH = '/privacy';
50
+
51
+ // @feature:start admin
52
+ /**
53
+ * The admin console. A separate branch of the router rather than a route inside
54
+ * the app shell, so the role check wraps the whole section instead of being
55
+ * repeated per page — and so an admin-only nav never renders for anyone else.
56
+ */
57
+ export const ADMIN_BASE = '/admin';
58
+ // @feature:end
@@ -0,0 +1,19 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { RouterProvider } from 'react-router';
4
+ import '@/shared/ui/styles/globals.css';
5
+ import { Providers } from './app/providers';
6
+ import { router } from './app/router';
7
+
8
+ // If the first screen needs data immediately, kick its query off here rather
9
+ // than in a component effect: by this point every module above has finished
10
+ // evaluating and `providers` has already called `configureApi` at module scope,
11
+ // so the fetch races the session check instead of queueing behind it.
12
+
13
+ createRoot(document.getElementById('root')!).render(
14
+ <StrictMode>
15
+ <Providers>
16
+ <RouterProvider router={router} />
17
+ </Providers>
18
+ </StrictMode>
19
+ );