@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,96 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Link, useNavigate, useSearchParams } from 'react-router';
3
+ import { Mail } from 'lucide-react';
4
+ import { getAuthClient } from '@acme/auth';
5
+ import { AuthLayout } from '@acme/layouts';
6
+ import { Alert, Button, Card } from '@acme/ui';
7
+ import { SubmitButton } from '../components/submit-button';
8
+ import { resendVerification, verifyEmailToken } from '../lib/auth-actions';
9
+ import { AUTH_URL } from '../lib/env';
10
+
11
+ export function VerifyEmailRoute() {
12
+ const [params] = useSearchParams();
13
+ const navigate = useNavigate();
14
+ const email = params.get('email') ?? '';
15
+ const token = params.get('token');
16
+ const [error, setError] = useState<string | null>(null);
17
+ const [checking, setChecking] = useState(Boolean(token));
18
+ const [resent, setResent] = useState(false);
19
+
20
+ useEffect(() => {
21
+ if (!token) return;
22
+ let active = true;
23
+ void (async () => {
24
+ const { error: err } = await verifyEmailToken(token);
25
+ if (!active) return;
26
+ if (err) {
27
+ setError(err);
28
+ setChecking(false);
29
+ return;
30
+ }
31
+ navigate('/verified', { replace: true });
32
+ })();
33
+ return () => {
34
+ active = false;
35
+ };
36
+ }, [token, navigate]);
37
+
38
+ const handleClickedLink = async () => {
39
+ setError(null);
40
+ setChecking(true);
41
+ const { data } = await getAuthClient().getSession();
42
+ setChecking(false);
43
+ if (data?.user?.emailVerified) {
44
+ navigate('/verified');
45
+ } else {
46
+ setError("We can't confirm it yet — click the link in your email, then try again.");
47
+ }
48
+ };
49
+
50
+ const handleResend = async () => {
51
+ setError(null);
52
+ const { error: err } = await resendVerification({ email, callbackURL: `${AUTH_URL}/verified` });
53
+ if (err) {
54
+ setError(err);
55
+ return;
56
+ }
57
+ setResent(true);
58
+ };
59
+
60
+ return (
61
+ <AuthLayout>
62
+ <Card className="items-center text-center">
63
+ <span className="flex size-16 items-center justify-center rounded-full bg-accent text-ink-deep">
64
+ <Mail className="size-7" />
65
+ </span>
66
+ <div className="flex flex-col gap-2">
67
+ <h2 className="font-display text-2xl font-black text-foreground">Check your inbox</h2>
68
+ <p className="text-sm text-body">
69
+ We sent a verification link to{' '}
70
+ <strong className="font-semibold text-foreground">{email || 'your email'}</strong>.
71
+ </p>
72
+ <p className="text-sm text-mute">The link expires in 24 hours. Check spam if you don't see it.</p>
73
+ </div>
74
+ {error ? (
75
+ <Alert variant="destructive" className="w-full text-left">
76
+ {error}
77
+ </Alert>
78
+ ) : null}
79
+ <div className="flex w-full flex-col gap-2">
80
+ <SubmitButton type="button" loading={checking} onClick={handleClickedLink}>
81
+ I've clicked the link
82
+ </SubmitButton>
83
+ <Button type="button" variant="outline" className="w-full" onClick={handleResend} disabled={resent}>
84
+ {resent ? 'Verification email sent' : 'Resend email'}
85
+ </Button>
86
+ </div>
87
+ <p className="text-sm text-body">
88
+ Wrong address?{' '}
89
+ <Link to="/signup" className="font-semibold text-ink-deep">
90
+ Edit your email
91
+ </Link>
92
+ </p>
93
+ </Card>
94
+ </AuthLayout>
95
+ );
96
+ }
@@ -0,0 +1,14 @@
1
+ /// <reference types="vite/client" />
2
+ /// <reference types="vite-plugin-svgr/client" />
3
+
4
+ interface ImportMetaEnv {
5
+ VITE_APP_API_URL: string;
6
+ VITE_APP_ALLOWED_HOSTS: string;
7
+ VITE_APP_LANDING: string;
8
+ VITE_APP_CLIENT_HOST: string;
9
+ VITE_APP_ADMIN_HOST: string;
10
+ }
11
+
12
+ interface ImportMeta {
13
+ readonly env: ImportMetaEnv;
14
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@acme/config/tsconfig/react.json",
3
+ "compilerOptions": {
4
+ "types": ["vite/client"],
5
+ "paths": {
6
+ "@/*": ["./src/*"]
7
+ }
8
+ },
9
+ "include": ["src"]
10
+ }
@@ -0,0 +1,13 @@
1
+ import { defineAppConfig } from '@acme/config/vite';
2
+ import { BRAND } from '@acme/ui/brand';
3
+ import { THEME_COLORS } from '@acme/ui/theme-colors';
4
+
5
+ // `themeColors` substitutes %THEME_LIGHT%/%THEME_DARK% into index.html — the
6
+ // pre-paint theme script and the `html` background both need them, and HTML
7
+ // can't import. `appTitle` substitutes %APP_TITLE% for the same reason: the
8
+ // static <title> must match the suffix MetaProvider appends at runtime.
9
+ export default defineAppConfig({
10
+ rootDir: import.meta.dirname,
11
+ themeColors: THEME_COLORS,
12
+ appTitle: BRAND.name
13
+ });
@@ -0,0 +1,57 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ // Pulls this workspace's env from Infisical into the matching .env file.
7
+ // The Infisical path is derived from the package name, e.g.
8
+ // @scope/types -> /frontend/types, landing -> /frontend/landing
9
+ //
10
+ // The project ID comes from the environment rather than being hardcoded: it
11
+ // identifies one organisation's vault, so a value baked in here would follow
12
+ // every copy of this scaffold to projects it has nothing to do with. Put it in
13
+ // your shell profile, or in a committed `.infisical.json`.
14
+ const PROJECT_ID = process.env.INFISICAL_PROJECT_ID;
15
+ const SECRET_ROOT = process.env.INFISICAL_PATH_PREFIX ?? '/frontend';
16
+
17
+ const ENV_FILES = {
18
+ dev: '.env',
19
+ test: '.env.test',
20
+ staging: '.env.staging',
21
+ prod: '.env.production'
22
+ };
23
+
24
+ const normalizeEnv = (raw) => {
25
+ if (['dev', 'development'].includes(raw)) return 'dev';
26
+ if (['test', 'testing'].includes(raw)) return 'test';
27
+ if (['prod', 'production'].includes(raw)) return 'prod';
28
+ if (raw === 'staging') return 'staging';
29
+ throw new Error(`Unknown env '${raw}' (use one of: dev, test, staging, prod)`);
30
+ };
31
+
32
+ const parseEnvArg = () => {
33
+ const args = process.argv.slice(2);
34
+ const flagIndex = args.findIndex((a) => a === '-e' || a === '--env');
35
+ if (flagIndex !== -1 && args[flagIndex + 1]) return args[flagIndex + 1];
36
+ const inline = args.find((a) => a.startsWith('--env='));
37
+ if (inline) return inline.slice('--env='.length);
38
+ return 'development';
39
+ };
40
+
41
+ const __dirname = dirname(fileURLToPath(import.meta.url));
42
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
43
+ const slug = pkg.name.replace(/^@[^/]+\//, '');
44
+ const secretPath = `${SECRET_ROOT}/${slug}`;
45
+
46
+ if (!PROJECT_ID) {
47
+ console.error('INFISICAL_PROJECT_ID is not set — nothing to fetch from.');
48
+ process.exit(1);
49
+ }
50
+
51
+ const env = normalizeEnv(parseEnvArg());
52
+ const envFile = ENV_FILES[env];
53
+
54
+ console.log(`Fetching secrets for '${slug}' (${env}) -> ${envFile}...`);
55
+ execSync(`infisical export --projectId="${PROJECT_ID}" --path="${secretPath}" --env=${env} > ${envFile}`, {
56
+ stdio: 'inherit'
57
+ });
@@ -0,0 +1,16 @@
1
+ # Copy to .env. Vite only exposes variables prefixed VITE_, and inlines them at
2
+ # build time — never put a secret here, it ships to the browser.
3
+
4
+ # Backend base URL. Better Auth lives at ${VITE_APP_API_URL}/auth.
5
+ VITE_APP_API_URL='http://localhost:5000'
6
+
7
+ # Sibling app origins. All four share one session cookie across subdomains, so
8
+ # these must agree with the backend's BETTER_AUTH_COOKIE_DOMAIN and its
9
+ # trustedOrigins list, or login will appear to succeed and then bounce.
10
+ VITE_APP_LANDING='http://localhost:3000'
11
+ VITE_APP_AUTH_HOST='http://localhost:3001'
12
+ VITE_APP_CLIENT_HOST='http://localhost:3002'
13
+ VITE_APP_ADMIN_HOST='http://localhost:3003'
14
+
15
+ # This app's own dev port.
16
+ PORT='3002'
@@ -0,0 +1,3 @@
1
+ import config from '@acme/config/eslint';
2
+
3
+ export default config;
@@ -0,0 +1,57 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
7
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
8
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
9
+ <link rel="icon" href="/favicon.ico" sizes="any" />
10
+ <link rel="manifest" href="/site.webmanifest" />
11
+ <meta name="theme-color" content="%THEME_LIGHT%" />
12
+ <!--
13
+ Applies the saved (or system) theme before first paint, so the app never
14
+ flashes light then flips to dark. Mirrors `themeInitScript` in @acme/ui —
15
+ inlined because it must run before any module loads. Keep the two in sync.
16
+ %THEME_LIGHT/DARK% come from THEME_COLORS via the shared vite config.
17
+ -->
18
+ <script>
19
+ (function () {
20
+ try {
21
+ var m = localStorage.getItem('acme-theme');
22
+ var d =
23
+ m === 'dark' || ((m === null || m === 'system') && matchMedia('(prefers-color-scheme: dark)').matches);
24
+ var r = document.documentElement;
25
+ r.classList.toggle('dark', d);
26
+ r.style.colorScheme = d ? 'dark' : 'light';
27
+ var t = document.querySelector('meta[name="theme-color"]');
28
+ if (t) t.setAttribute('content', d ? '%THEME_DARK%' : '%THEME_LIGHT%');
29
+ } catch (e) {}
30
+ })();
31
+ </script>
32
+ <!--
33
+ globals.css only styles `body`, so without this the UA canvas paints white
34
+ for the moment before the stylesheet lands — a white flash on a dark load.
35
+ -->
36
+ <style>
37
+ html {
38
+ background: %THEME_LIGHT%;
39
+ }
40
+ html.dark {
41
+ background: %THEME_DARK%;
42
+ }
43
+ </style>
44
+ <meta name="description" content="Acme Corp." />
45
+ <!--
46
+ Private surface: never indexed. `robots.txt` only discourages crawling,
47
+ while `noindex` prevents listing, so both are present. This app's routes
48
+ include password-reset and email-verify URLs.
49
+ -->
50
+ <meta name="robots" content="noindex, nofollow" />
51
+ <title>%APP_TITLE%</title>
52
+ </head>
53
+ <body>
54
+ <div id="root"></div>
55
+ <script type="module" src="/src/main.tsx"></script>
56
+ </body>
57
+ </html>
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "client",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc --noEmit && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "eslint .",
11
+ "typecheck": "tsc --noEmit",
12
+ "secrets": "node .bin/secrets.js"
13
+ },
14
+ "dependencies": {
15
+ "@acme/api": "workspace:*",
16
+ "@acme/auth": "workspace:*",
17
+ "@acme/layouts": "workspace:*",
18
+ "@acme/ui": "workspace:*",
19
+ "@reduxjs/toolkit": "^2.12.0",
20
+ "apexcharts": "^6.5.0",
21
+ "lucide-react": "^1.17.0",
22
+ "react": "^19.2.6",
23
+ "react-apexcharts": "^2.1.1",
24
+ "react-dom": "^19.2.6",
25
+ "react-redux": "^9.3.0",
26
+ "react-router": "^8.2.0"
27
+ },
28
+ "devDependencies": {
29
+ "@acme/config": "workspace:*",
30
+ "@types/react": "^19.2.14",
31
+ "@types/react-dom": "^19.2.3",
32
+ "vite": "^8.0.12",
33
+ "vite-plugin-pwa": "^1.3.0"
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ // The Client app is driven by the router (see app/router.tsx); this module is
2
+ // intentionally empty and no longer rendered.
3
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Home, Settings } from 'lucide-react';
2
+ import type { NavItem } from '@acme/layouts';
3
+
4
+ /**
5
+ * Drives the app shell's navigation. Keep it in step with `router.tsx` — the
6
+ * shell derives the sidebar from this list, not from the router, so a route
7
+ * added in one place and not the other is simply unreachable or dead.
8
+ *
9
+ * `icon` is a rendered element, not a component reference.
10
+ */
11
+ export const NAV_ITEMS: NavItem[] = [
12
+ { to: '/', label: 'Home', icon: <Home className="size-4" />, end: true },
13
+ { to: '/settings', label: 'Settings', icon: <Settings className="size-4" /> }
14
+ ];
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Provider } from 'react-redux';
3
+ import { configureApi } from '@acme/api';
4
+ import { AuthProvider, configureAuth, redirectToLogin } from '@acme/auth';
5
+ import { MetaProvider, ThemeProvider, Toaster, installZodErrorMap } from '@acme/ui';
6
+ import { store } from './store';
7
+
8
+ const env = import.meta.env;
9
+ const apiBaseUrl = env.VITE_APP_API_URL;
10
+ const authAppUrl = env.VITE_APP_AUTH_HOST as string;
11
+
12
+ const trustedOrigins = [env.VITE_APP_LANDING, env.VITE_APP_CLIENT_HOST, env.VITE_APP_ADMIN_HOST, authAppUrl].filter(
13
+ (value): value is string => Boolean(value)
14
+ );
15
+
16
+ // Wire the shared API + auth clients once, before rendering. Unauthenticated users are sent to the Auth app and returned here.
17
+ configureApi({ baseUrl: apiBaseUrl, onUnauthorized: () => redirectToLogin() });
18
+ configureAuth({
19
+ authApiBaseUrl: `${apiBaseUrl}/auth`,
20
+ authAppUrl,
21
+ trustedOrigins,
22
+ defaultRedirect: (env.VITE_APP_CLIENT_HOST as string | undefined) ?? window.location.origin
23
+ });
24
+ installZodErrorMap();
25
+
26
+ export function Providers({ children }: { children: ReactNode }) {
27
+ return (
28
+ // ThemeProvider above the Redux Provider: it only touches the document
29
+ // (`dark` class, color-scheme, theme-color meta) and must not re-render
30
+ // through the store.
31
+ <ThemeProvider>
32
+ <MetaProvider>
33
+ <Provider store={store}>
34
+ <AuthProvider>
35
+ {children}
36
+ <Toaster />
37
+ </AuthProvider>
38
+ </Provider>
39
+ </MetaProvider>
40
+ </ThemeProvider>
41
+ );
42
+ }
@@ -0,0 +1,50 @@
1
+ import { Suspense, lazy, type ReactNode } from 'react';
2
+ import { createBrowserRouter } from 'react-router';
3
+ import { ProtectedRoute, signOut, useAuth } from '@acme/auth';
4
+ import { AppShell, AppShellSkeleton, ErrorBoundary, NotFound, RouteFallback } from '@acme/layouts';
5
+ import { NAV_ITEMS } from './nav';
6
+ import { HomeRoute } from '../routes/home';
7
+
8
+ /**
9
+ * Feature routes are lazy so the initial bundle stays the shell plus one page.
10
+ * Add a route here and a matching entry in `nav.tsx`.
11
+ */
12
+ const SettingsRoute = lazy(() => import('../routes/settings').then((m) => ({ default: m.SettingsRoute })));
13
+
14
+ const lazyRoute = (element: ReactNode) => <Suspense fallback={<RouteFallback />}>{element}</Suspense>;
15
+
16
+ /**
17
+ * The shell reads the session itself rather than taking it as a prop —
18
+ * `ProtectedRoute` has already established the user is authenticated by the
19
+ * time this renders.
20
+ *
21
+ * `ErrorBoundary` wraps the shell rather than being passed as the route's
22
+ * `errorElement`: it is a plain React error boundary taking `children`, so it
23
+ * catches render errors anywhere below, including inside a lazy chunk.
24
+ */
25
+ function Shell() {
26
+ const { user } = useAuth();
27
+ return (
28
+ <ErrorBoundary>
29
+ <AppShell nav={NAV_ITEMS} user={user ?? {}} onSignOut={() => void signOut()} />
30
+ </ErrorBoundary>
31
+ );
32
+ }
33
+
34
+ export const router = createBrowserRouter([
35
+ {
36
+ // `fallback` is required and must not be blank: the session round trip is
37
+ // cross-origin and takes most of a second, and rendering nothing for that
38
+ // long reads as a broken app.
39
+ element: (
40
+ <ProtectedRoute fallback={<AppShellSkeleton />}>
41
+ <Shell />
42
+ </ProtectedRoute>
43
+ ),
44
+ children: [
45
+ { index: true, element: <HomeRoute /> },
46
+ { path: 'settings', element: lazyRoute(<SettingsRoute />) }
47
+ ]
48
+ },
49
+ { path: '*', element: <NotFound /> }
50
+ ]);
@@ -0,0 +1,15 @@
1
+ import { makeStore } from '@acme/api';
2
+ import { authReducer } from '@acme/auth';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+
5
+ export const store = makeStore({
6
+ reducer: {
7
+ auth: authReducer
8
+ }
9
+ });
10
+
11
+ export type RootState = ReturnType<typeof store.getState>;
12
+ export type AppDispatch = typeof store.dispatch;
13
+
14
+ export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
15
+ export const useAppSelector = useSelector.withTypes<RootState>();
@@ -0,0 +1,12 @@
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
+ /** The Auth app users are redirected to for login. */
6
+ export const AUTH_HOST = env.VITE_APP_AUTH_HOST as string;
7
+ /** This client app's own origin. */
8
+ export const CLIENT_URL = env.VITE_APP_CLIENT_HOST as string;
9
+ /** The admin console origin. */
10
+ export const ADMIN_URL = env.VITE_APP_ADMIN_HOST as string;
11
+ /** The public marketing site (logout destination). */
12
+ export const LANDING_URL = env.VITE_APP_LANDING as string;
@@ -0,0 +1,19 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { RouterProvider } from 'react-router';
4
+ import '@acme/ui/globals.css';
5
+ import { Providers } from './app/providers';
6
+ import { router } from './app/router';
7
+
8
+ // If a landing view needs data immediately, kick its query off here rather than
9
+ // in a component effect: at 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
+ );
@@ -0,0 +1,17 @@
1
+ import { PageMeta } from '@acme/ui';
2
+
3
+ export function HomeRoute() {
4
+ return (
5
+ <>
6
+ <PageMeta title="Home" />
7
+ <div className="space-y-2">
8
+ <h1 className="text-2xl font-semibold">Home</h1>
9
+ <p className="text-muted-foreground">
10
+ Replace this with your first feature. Data calls belong in a
11
+ <code className="mx-1">features/&lt;name&gt;/api.ts</code>
12
+ slice that extends the shared <code>baseApi</code>.
13
+ </p>
14
+ </div>
15
+ </>
16
+ );
17
+ }
@@ -0,0 +1,10 @@
1
+ import { PageMeta } from '@acme/ui';
2
+
3
+ export function SettingsRoute() {
4
+ return (
5
+ <>
6
+ <PageMeta title="Settings" />
7
+ <h1 className="text-2xl font-semibold">Settings</h1>
8
+ </>
9
+ );
10
+ }
@@ -0,0 +1,15 @@
1
+ /// <reference types="vite/client" />
2
+ /// <reference types="vite-plugin-svgr/client" />
3
+ /// <reference types="vite-plugin-pwa/react" />
4
+
5
+ interface ImportMetaEnv {
6
+ VITE_APP_API_URL: string;
7
+ VITE_APP_ALLOWED_HOSTS: string;
8
+ VITE_APP_AUTH_HOST: string;
9
+ VITE_APP_LANDING: string;
10
+ VITE_APP_ADMIN_HOST: string;
11
+ }
12
+
13
+ interface ImportMeta {
14
+ readonly env: ImportMetaEnv;
15
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@acme/config/tsconfig/react.json",
3
+ "compilerOptions": {
4
+ "types": ["vite/client"],
5
+ "paths": {
6
+ "@/*": ["./src/*"]
7
+ }
8
+ },
9
+ "include": ["src"]
10
+ }
@@ -0,0 +1,120 @@
1
+ import { defineAppConfig } from '@acme/config/vite';
2
+ import { BRAND } from '@acme/ui/brand';
3
+ import { THEME_COLORS } from '@acme/ui/theme-colors';
4
+ import { VitePWA } from 'vite-plugin-pwa'; // @feature pwa
5
+
6
+ // `themeColors` substitutes %THEME_LIGHT%/%THEME_DARK% into index.html — here the
7
+ // pre-paint theme script and the boot splash both need them. The manifest below
8
+ // reads the same object, so there is one source of truth; a hardcoded copy is how
9
+ // the manifest once ended up #ffffff in both themes.
10
+ export default defineAppConfig({
11
+ rootDir: import.meta.dirname,
12
+ themeColors: THEME_COLORS,
13
+ appTitle: BRAND.name,
14
+ plugins: [
15
+ // @feature:start pwa
16
+ VitePWA({
17
+ // 'prompt', not 'autoUpdate': a new build waits until the user accepts it
18
+ // (see components/pwa-update-prompt.tsx for why taking over mid-session
19
+ // breaks code-split routes).
20
+ registerType: 'prompt',
21
+ injectRegister: 'auto',
22
+ // No `includeAssets`. It adds files to the precache manifest that the icon
23
+ // globs already matched, which is how every icon ended up listed twice.
24
+ //
25
+ // `includeManifestIcons: false` for the same reason the icon globs went:
26
+ // the install icons (32 KB of android-chrome + maskable) are fetched by the
27
+ // OS at install time and play no part in rendering the shell offline, so
28
+ // precaching them just re-downloads them on every deploy.
29
+ includeManifestIcons: false,
30
+ devOptions: {
31
+ enabled: true,
32
+ type: 'module',
33
+ suppressWarnings: true
34
+ },
35
+ manifest: {
36
+ id: '/',
37
+ name: BRAND.name,
38
+ // short_name is what shows under the home-screen icon.
39
+ short_name: BRAND.name,
40
+ description: 'Acme Corp.',
41
+ start_url: '/',
42
+ scope: '/',
43
+ display: 'standalone',
44
+ theme_color: THEME_COLORS.light,
45
+ background_color: THEME_COLORS.light,
46
+ icons: [
47
+ { src: '/android-chrome-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
48
+ { src: '/android-chrome-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
49
+ { src: '/maskable-icon-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
50
+ { src: '/maskable-icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }
51
+ ]
52
+ },
53
+ workbox: {
54
+ /*
55
+ * Precache = what's needed to boot the shell offline, and nothing else.
56
+ * Every entry here is re-downloaded whenever its hash changes, so this
57
+ * list is a recurring cost on every deploy, not a one-off install cost.
58
+ *
59
+ * Deliberately excluded:
60
+ * - `png`/`ico`. The only images in the build are launch/app icons, which
61
+ * the OS and browser fetch themselves at install time and keep in the
62
+ * HTTP cache. None are needed to render the shell offline.
63
+ * - The ApexCharts vendor chunk. At 763 KB it was 38% of the whole
64
+ * precache, for a `lazy()` import that only two routes reach. It's
65
+ * covered by the runtime cache below, so it still works offline —
66
+ * just from second use rather than before first use.
67
+ * `svg` stays: nothing matches it today, but an SVG that reaches
68
+ * `assets/` came from `src` and is therefore shell code.
69
+ */
70
+ globPatterns: ['**/*.{js,css,html,svg,woff,woff2}'],
71
+ globIgnores: ['**/splash/**', 'assets/react-apexcharts.esm-*.js'],
72
+ navigateFallback: '/index.html',
73
+ cleanupOutdatedCaches: true,
74
+ runtimeCaching: [
75
+ {
76
+ /*
77
+ * Anything hashed that isn't precached — the chart vendor chunk today,
78
+ * plus whatever else gets excluded later. Safe as CacheFirst precisely
79
+ * because the filenames are content-hashed: a changed file is a new
80
+ * URL, so a stale entry is unreachable rather than wrong.
81
+ */
82
+ urlPattern: ({ sameOrigin, url }) => sameOrigin && url.pathname.startsWith('/assets/'),
83
+ handler: 'CacheFirst',
84
+ options: {
85
+ cacheName: 'acme-assets',
86
+ expiration: { maxEntries: 60, maxAgeSeconds: 60 * 60 * 24 * 30 },
87
+ cacheableResponse: { statuses: [0, 200] }
88
+ }
89
+ },
90
+ {
91
+ /*
92
+ * Remote images — user-uploaded avatars and the like, off the media host.
93
+ * Matched
94
+ * by request destination rather than hostname so it doesn't hardcode
95
+ * the media origin, and bounded so an opaque cross-origin response
96
+ * (which counts fully against quota) can't grow without limit.
97
+ */
98
+ urlPattern: ({ request, sameOrigin }) => !sameOrigin && request.destination === 'image',
99
+ handler: 'StaleWhileRevalidate',
100
+ options: {
101
+ cacheName: 'acme-remote-images',
102
+ expiration: { maxEntries: 80, maxAgeSeconds: 60 * 60 * 24 * 7 },
103
+ cacheableResponse: { statuses: [0, 200] }
104
+ }
105
+ }
106
+ /*
107
+ * No rule for `/v1`, on purpose. Decide deliberately before adding
108
+ * one: a stale API response is worse than no response for anything
109
+ * the user acts on, and offline degrading to "no data" is usually the
110
+ * correct behaviour.
111
+ *
112
+ * Fonts need no rule either: they're self-hosted and same-origin, so
113
+ * they're precached above, which is strictly better.
114
+ */
115
+ ]
116
+ }
117
+ })
118
+ // @feature:end
119
+ ]
120
+ });