create-aron-app 0.1.6 → 0.1.8

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 (234) hide show
  1. package/README.md +24 -31
  2. package/dist/index.js +39 -115
  3. package/package.json +7 -4
  4. package/templates/.cursor/rules/backend.mdc +112 -0
  5. package/templates/.cursor/rules/coding_standards.mdc +145 -0
  6. package/templates/.cursor/rules/frontend_architecture.mdc +334 -0
  7. package/templates/.github/workflows/ci.yml +40 -0
  8. package/templates/apps/api/_generated/api.d.ts +57 -0
  9. package/templates/apps/api/_generated/api.js +23 -0
  10. package/templates/apps/api/_generated/dataModel.d.ts +60 -0
  11. package/templates/apps/api/_generated/server.d.ts +143 -0
  12. package/templates/apps/api/_generated/server.js +93 -0
  13. package/templates/apps/api/http.ts +16 -0
  14. package/templates/apps/web/.env.example +10 -0
  15. package/templates/apps/web/.react-router/types/+future.ts +9 -0
  16. package/templates/apps/web/.react-router/types/+routes.ts +76 -0
  17. package/templates/apps/web/.react-router/types/+server-build.d.ts +18 -0
  18. package/templates/apps/web/.react-router/types/src/+types/root.ts +59 -0
  19. package/templates/apps/web/.react-router/types/src/routes/(auth)/+types/layout.ts +62 -0
  20. package/templates/apps/web/.react-router/types/src/routes/(auth)/sign-in/+types/index.ts +65 -0
  21. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/[id].ts +68 -0
  22. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/index.ts +68 -0
  23. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/layout.ts +65 -0
  24. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/+types/index.ts +65 -0
  25. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/+types/layout.ts +62 -0
  26. package/templates/{react-router → apps/web}/project.json +9 -2
  27. package/templates/{react-router → apps/web}/react-router.config.ts +1 -1
  28. package/templates/apps/web/src/app.css +3 -0
  29. package/templates/{react-router → apps/web}/src/components/error_boundary.tsx +1 -1
  30. package/templates/apps/web/src/libs/convex_query_client.ts +11 -0
  31. package/templates/apps/web/src/libs/react_query_client.ts +17 -0
  32. package/templates/apps/web/src/libs/server/auth.ts +32 -0
  33. package/templates/apps/web/src/libs/server/protected.ts +17 -0
  34. package/templates/apps/web/src/providers/api_auth_provider.tsx +26 -0
  35. package/templates/apps/web/src/providers/global_provider.tsx +28 -0
  36. package/templates/apps/web/src/providers/navigation_loading_bar_provider.tsx +72 -0
  37. package/templates/apps/web/src/root.tsx +68 -0
  38. package/templates/{react-router/src/routes/auth → apps/web/src/routes/(auth)}/layout.tsx +1 -1
  39. package/templates/apps/web/src/routes/(dashboard)/(todos)/[id].tsx +33 -0
  40. package/templates/apps/web/src/routes/(dashboard)/(todos)/index.tsx +26 -0
  41. package/templates/apps/web/src/routes/(dashboard)/(todos)/layout.tsx +9 -0
  42. package/templates/apps/web/src/routes/(dashboard)/index.tsx +20 -0
  43. package/templates/apps/web/src/routes/(dashboard)/layout.tsx +20 -0
  44. package/templates/apps/web/src/routes.ts +14 -0
  45. package/templates/apps/web/src/surfaces/home/bootstrap.ts +9 -0
  46. package/templates/apps/web/src/surfaces/home/home.tsx +25 -0
  47. package/templates/apps/web/src/surfaces/home/install.tsx +17 -0
  48. package/templates/apps/web/src/surfaces/home/layout.tsx +35 -0
  49. package/templates/apps/web/src/surfaces/home/main/create.tsx +32 -0
  50. package/templates/apps/web/src/surfaces/sidebar/install.tsx +19 -0
  51. package/templates/apps/web/src/surfaces/sidebar/layout.tsx +119 -0
  52. package/templates/apps/web/src/surfaces/sidebar/nav_main/create.tsx +31 -0
  53. package/templates/apps/web/src/surfaces/sidebar/nav_main/nav_main.tsx +42 -0
  54. package/templates/apps/web/src/surfaces/sidebar/sidebar.tsx +18 -0
  55. package/templates/apps/web/src/surfaces/sidebar/user_menu/create.tsx +26 -0
  56. package/templates/{react-router/src/layouts/sidebar/sidebar_aside → apps/web/src/surfaces/sidebar/user_menu}/user_menu.tsx +13 -9
  57. package/templates/apps/web/src/surfaces/todos/all_todos/all_todos.tsx +25 -0
  58. package/templates/apps/web/src/surfaces/todos/all_todos/all_todos_controller.ts +47 -0
  59. package/templates/apps/web/src/surfaces/todos/all_todos/bootstrap.ts +21 -0
  60. package/templates/apps/web/src/surfaces/todos/all_todos/header/create.tsx +21 -0
  61. package/templates/apps/web/src/surfaces/todos/all_todos/install.tsx +20 -0
  62. package/templates/apps/web/src/surfaces/todos/all_todos/layout.tsx +35 -0
  63. package/templates/apps/web/src/surfaces/todos/all_todos/main/create.tsx +47 -0
  64. package/templates/apps/web/src/surfaces/todos/all_todos/main/main.tsx +68 -0
  65. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/create.tsx +54 -0
  66. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/new_todo_sheet.tsx +97 -0
  67. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/schema.ts +11 -0
  68. package/templates/apps/web/src/surfaces/todos/single_todo/bootstrap.ts +35 -0
  69. package/templates/apps/web/src/surfaces/todos/single_todo/header/create.tsx +24 -0
  70. package/templates/apps/web/src/surfaces/todos/single_todo/header/header.tsx +25 -0
  71. package/templates/apps/web/src/surfaces/todos/single_todo/install.tsx +27 -0
  72. package/templates/apps/web/src/surfaces/todos/single_todo/layout.tsx +45 -0
  73. package/templates/apps/web/src/surfaces/todos/single_todo/main/create.tsx +35 -0
  74. package/templates/apps/web/src/surfaces/todos/single_todo/main/main.tsx +47 -0
  75. package/templates/apps/web/src/surfaces/todos/single_todo/single_todo.tsx +27 -0
  76. package/templates/apps/web/src/surfaces/todos/single_todo/single_todo_controller.ts +16 -0
  77. package/templates/{react-router → apps/web}/vite.config.ts +27 -3
  78. package/templates/{_base/biome.json → biome.json} +12 -0
  79. package/templates/bun.lock +1917 -0
  80. package/templates/{_base/emails → emails}/project.json +1 -1
  81. package/templates/package.json +91 -0
  82. package/templates/{_base/shared → shared}/assets/src/styles/global.css +14 -8
  83. package/templates/shared/ui/src/base/collapsible.tsx +31 -0
  84. package/templates/shared/ui/src/base/hover-card.tsx +42 -0
  85. package/templates/shared/ui/src/base/input-group.tsx +168 -0
  86. package/templates/shared/ui/src/base/panel.tsx +93 -0
  87. package/templates/{_base/shared → shared}/ui/src/hooks/use_mobile.tsx +1 -1
  88. package/templates/{_base/shared → shared}/ui/src/hooks/use_query_params.tsx +6 -7
  89. package/templates/{_base/shared → shared}/ui/tsconfig.json +1 -1
  90. package/templates/shared/utils/src/convex.ts +4 -0
  91. package/templates/{_base/tsconfig.base.json → tsconfig.base.json} +2 -1
  92. package/templates/_base/.cursor/commands/builder.md +0 -0
  93. package/templates/_base/.cursor/rules/api_architecture.mdc +0 -268
  94. package/templates/_base/.cursor/rules/coding_standards.mdc +0 -64
  95. package/templates/_base/.cursor/rules/convex_rules.mdc +0 -675
  96. package/templates/_base/.cursor/rules/frontend_rules.mdc +0 -268
  97. package/templates/_base/.env.convex.example +0 -3
  98. package/templates/_base/.github/workflows/ci.yml +0 -29
  99. package/templates/_base/_gitignore +0 -58
  100. package/templates/_base/package.json +0 -73
  101. package/templates/_base/shared/utils/src/convex.ts +0 -3
  102. package/templates/nextjs/.env.example +0 -8
  103. package/templates/nextjs/index.d.ts +0 -6
  104. package/templates/nextjs/next-env.d.ts +0 -5
  105. package/templates/nextjs/next.config.js +0 -22
  106. package/templates/nextjs/postcss.config.js +0 -17
  107. package/templates/nextjs/project.json +0 -22
  108. package/templates/nextjs/src/app/(auth)/layout.tsx +0 -21
  109. package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +0 -22
  110. package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +0 -15
  111. package/templates/nextjs/src/app/(dashboard)/layout.tsx +0 -27
  112. package/templates/nextjs/src/app/(dashboard)/page.tsx +0 -5
  113. package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +0 -23
  114. package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +0 -16
  115. package/templates/nextjs/src/app/app.css +0 -3
  116. package/templates/nextjs/src/app/layout.tsx +0 -26
  117. package/templates/nextjs/src/convex.ts +0 -11
  118. package/templates/nextjs/src/middleware.ts +0 -18
  119. package/templates/nextjs/src/providers/convex_provider.tsx +0 -44
  120. package/templates/nextjs/src/surfaces/home_surface.tsx +0 -22
  121. package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +0 -97
  122. package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +0 -107
  123. package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +0 -90
  124. package/templates/nextjs/src/ui/sidebar/nav_link.tsx +0 -36
  125. package/templates/nextjs/src/ui/sidebar/sidebar.tsx +0 -125
  126. package/templates/nextjs/src/utils/font.ts +0 -9
  127. package/templates/nextjs/tsconfig.json +0 -42
  128. package/templates/react-router/.env.example +0 -8
  129. package/templates/react-router/src/app.css +0 -3
  130. package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +0 -76
  131. package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +0 -22
  132. package/templates/react-router/src/providers/api_auth_provider.tsx +0 -38
  133. package/templates/react-router/src/root.tsx +0 -37
  134. package/templates/react-router/src/routes/index.tsx +0 -9
  135. package/templates/react-router/src/routes/layout.tsx +0 -26
  136. package/templates/react-router/src/routes/todos/[id].tsx +0 -22
  137. package/templates/react-router/src/routes/todos/index.tsx +0 -13
  138. package/templates/react-router/src/routes.ts +0 -12
  139. package/templates/react-router/src/surfaces/home_surface.tsx +0 -20
  140. package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +0 -87
  141. package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +0 -102
  142. package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +0 -81
  143. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/SKILL.md +0 -0
  144. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/SKILL.md +0 -0
  145. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +0 -0
  146. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +0 -0
  147. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +0 -0
  148. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +0 -0
  149. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +0 -0
  150. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/SKILL.md +0 -0
  151. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +0 -0
  152. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +0 -0
  153. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +0 -0
  154. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +0 -0
  155. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +0 -0
  156. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +0 -0
  157. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +0 -0
  158. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +0 -0
  159. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +0 -0
  160. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +0 -0
  161. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +0 -0
  162. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-webhooks/SKILL.md +0 -0
  163. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/SKILL.md +0 -0
  164. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/agents/openai.yml +0 -0
  165. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn-small.png +0 -0
  166. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn.png +0 -0
  167. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/cli.md +0 -0
  168. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/customization.md +0 -0
  169. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/evals/evals.json +0 -0
  170. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/mcp.md +0 -0
  171. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/base-vs-radix.md +0 -0
  172. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/composition.md +0 -0
  173. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/forms.md +0 -0
  174. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/icons.md +0 -0
  175. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/styling.md +0 -0
  176. /package/templates/{_base/.cursor → .cursor}/commands/pr.md +0 -0
  177. /package/templates/{_base/.nvmrc → .nvmrc} +0 -0
  178. /package/templates/{_base/.vscode → .vscode}/settings.json +0 -0
  179. /package/templates/{_base/apps → apps}/api/auth.config.ts +0 -0
  180. /package/templates/{_base/apps → apps}/api/functions.ts +0 -0
  181. /package/templates/{_base/apps → apps}/api/project.json +0 -0
  182. /package/templates/{_base/apps → apps}/api/schema.ts +0 -0
  183. /package/templates/{_base/apps → apps}/api/todos/crud.ts +0 -0
  184. /package/templates/{_base/apps → apps}/api/todos/schema.ts +0 -0
  185. /package/templates/{_base/apps → apps}/api/todos/types.ts +0 -0
  186. /package/templates/{_base/apps → apps}/api/tsconfig.json +0 -0
  187. /package/templates/{_base/apps → apps}/api/types.ts +0 -0
  188. /package/templates/{react-router → apps/web}/postcss.config.js +0 -0
  189. /package/templates/{react-router → apps/web}/public/favicon.ico +0 -0
  190. /package/templates/{react-router/src/routes/auth/sign-in.tsx → apps/web/src/routes/(auth)/sign-in/index.tsx} +0 -0
  191. /package/templates/{react-router → apps/web}/tsconfig.json +0 -0
  192. /package/templates/{_base/convex.json → convex.json} +0 -0
  193. /package/templates/{_base/emails → emails}/tsconfig.json +0 -0
  194. /package/templates/{_base/emails → emails}/welcome_email.tsx +0 -0
  195. /package/templates/{_base/nx.json → nx.json} +0 -0
  196. /package/templates/{_base/scripts → scripts}/sync_convex_env.ts +0 -0
  197. /package/templates/{_base/shared → shared}/assets/image.d.ts +0 -0
  198. /package/templates/{_base/shared → shared}/assets/tsconfig.json +0 -0
  199. /package/templates/{_base/shared → shared}/ui/src/base/alert_dialog.tsx +0 -0
  200. /package/templates/{_base/shared → shared}/ui/src/base/badge.tsx +0 -0
  201. /package/templates/{_base/shared → shared}/ui/src/base/basic_data_table.tsx +0 -0
  202. /package/templates/{_base/shared → shared}/ui/src/base/button.tsx +0 -0
  203. /package/templates/{_base/shared → shared}/ui/src/base/button_group.tsx +0 -0
  204. /package/templates/{_base/shared → shared}/ui/src/base/card.tsx +0 -0
  205. /package/templates/{_base/shared → shared}/ui/src/base/checkbox.tsx +0 -0
  206. /package/templates/{_base/shared → shared}/ui/src/base/command.tsx +0 -0
  207. /package/templates/{_base/shared → shared}/ui/src/base/dialog.tsx +0 -0
  208. /package/templates/{_base/shared → shared}/ui/src/base/dropdown_menu.tsx +0 -0
  209. /package/templates/{_base/shared → shared}/ui/src/base/form.tsx +0 -0
  210. /package/templates/{_base/shared → shared}/ui/src/base/input.tsx +0 -0
  211. /package/templates/{_base/shared → shared}/ui/src/base/label.tsx +0 -0
  212. /package/templates/{_base/shared → shared}/ui/src/base/popover.tsx +0 -0
  213. /package/templates/{_base/shared → shared}/ui/src/base/radio_group.tsx +0 -0
  214. /package/templates/{_base/shared → shared}/ui/src/base/resizable.tsx +0 -0
  215. /package/templates/{_base/shared → shared}/ui/src/base/scroll_area.tsx +0 -0
  216. /package/templates/{_base/shared → shared}/ui/src/base/select.tsx +0 -0
  217. /package/templates/{_base/shared → shared}/ui/src/base/separator.tsx +0 -0
  218. /package/templates/{_base/shared → shared}/ui/src/base/sheet.tsx +0 -0
  219. /package/templates/{_base/shared → shared}/ui/src/base/side_bar.tsx +0 -0
  220. /package/templates/{_base/shared → shared}/ui/src/base/skeleton.tsx +0 -0
  221. /package/templates/{_base/shared → shared}/ui/src/base/spinner.tsx +0 -0
  222. /package/templates/{_base/shared → shared}/ui/src/base/switch.tsx +0 -0
  223. /package/templates/{_base/shared → shared}/ui/src/base/table.tsx +0 -0
  224. /package/templates/{_base/shared → shared}/ui/src/base/text_area.tsx +0 -0
  225. /package/templates/{_base/shared → shared}/ui/src/base/tooltip.tsx +0 -0
  226. /package/templates/{_base/shared → shared}/ui/src/base/utils.ts +0 -0
  227. /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_press.tsx +0 -0
  228. /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_release.tsx +0 -0
  229. /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_click.tsx +0 -0
  230. /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_location.tsx +0 -0
  231. /package/templates/{_base/shared → shared}/ui/src/hooks/use_outside_click.tsx +0 -0
  232. /package/templates/{_base/shared → shared}/utils/src/time.ts +0 -0
  233. /package/templates/{_base/shared → shared}/utils/tsconfig.json +0 -0
  234. /package/templates/{_base/skills-lock.json → skills-lock.json} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-aron-app",
3
- "version": "0.1.6",
4
- "description": "Scaffold a new Convex + Next.js + Clerk full-stack project",
3
+ "version": "0.1.8",
4
+ "description": "Scaffold a new Convex + React Router + Clerk full-stack project",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -12,8 +12,11 @@
12
12
  "templates"
13
13
  ],
14
14
  "scripts": {
15
- "build": "bun build src/index.ts --target=node --outfile=dist/index.js --minify && echo '#!/usr/bin/env node' | cat - dist/index.js > /tmp/create-tmp.js && mv /tmp/create-tmp.js dist/index.js && chmod +x dist/index.js",
15
+ "build": "bun build src/index.ts --target=node --outfile=dist/index.js --minify --external giget && echo '#!/usr/bin/env node' | cat - dist/index.js > /tmp/create-tmp.js && mv /tmp/create-tmp.js dist/index.js && chmod +x dist/index.js",
16
16
  "start": "bun run src/index.ts",
17
+ "start:local": "bun run src/index.ts --local",
18
+ "template": "bun run build && bun run start",
19
+ "template:local": "bun run build && bun run start:local",
17
20
  "release": "npm version patch && npm publish --access public",
18
21
  "prepublishOnly": "bun run build"
19
22
  },
@@ -33,7 +36,7 @@
33
36
  "create",
34
37
  "template",
35
38
  "convex",
36
- "nextjs",
39
+ "react-router",
37
40
  "clerk",
38
41
  "nx",
39
42
  "monorepo"
@@ -0,0 +1,112 @@
1
+ ---
2
+ description: Convex backend in apps/api — ents, zQuery/zMutation, CRUD layout, auth, and platform conventions that apply alongside project wrappers.
3
+ globs: templates/apps/api/**/*.ts,apps/api/**/*.ts
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Backend API (`apps/api`)
8
+
9
+ Convex backend using **convex-ents**, **`zQuery` / `zMutation`** from `@/api/functions`, and **Zod** (`z.*`, `zid()`) for public function args — not raw `v.*` validators on public handlers.
10
+
11
+ ---
12
+
13
+ ## Layout
14
+
15
+ ```
16
+ apps/api/
17
+ ├── <entity>/
18
+ │ ├── schema.ts # ent definition (table + edges + indexes)
19
+ │ ├── types.ts # Zod schemas + TS types
20
+ │ └── crud.ts # queries/mutations (GET → UPDATE → CREATE → DELETE)
21
+ ├── schema.ts # spreads all entity ents
22
+ ├── types.ts # shared context types
23
+ ├── functions.ts # zQuery / zMutation wrappers
24
+ └── auth.config.ts # JWT providers (required for ctx.auth)
25
+ ```
26
+
27
+ Example entity (`todos/`): `schema.ts`, `types.ts`, `crud.ts`.
28
+
29
+ ### New entity checklist
30
+
31
+ 1. Add `apps/api/<entity>/schema.ts` → export `<entity>Ents`
32
+ 2. Add `types.ts` — Zod input/output shapes
33
+ 3. Add `crud.ts` — `zQuery` / `zMutation` handlers
34
+ 4. Spread ents in `apps/api/schema.ts` via `defineEntSchema`
35
+
36
+ ---
37
+
38
+ ## Schema (`schema.ts` per entity)
39
+
40
+ - Export `{ tableName: defineEnt({...}).edge(...).index(...) }`
41
+ - Index names include all indexed fields, e.g. `by_user_id`
42
+ - Prefer **indexes** for lookups; avoid unbounded `filter` scans in production code
43
+
44
+ ---
45
+
46
+ ## Types (`types.ts`)
47
+
48
+ - Zod schemas and inferred types live here, not in ent schema files
49
+ - Use `zodToConvex` from convex-helpers when bridging Zod → Convex validators
50
+
51
+ ---
52
+
53
+ ## CRUD (`crud.ts`)
54
+
55
+ Order functions: **GET → UPDATE → CREATE → DELETE**.
56
+
57
+ - Authenticate with `ctx.auth.getUserIdentity()`; resolve the app user as your schema requires
58
+ - **Never** take `userId` from the client for authorization — derive from auth context
59
+ - Prefer **`identity.tokenIdentifier`** for stable auth-linked lookups (not `subject` alone)
60
+ - Use **`ConvexError`** for business errors; messages stay concise
61
+ - For expected present/missing entity semantics: **`getX` / `get`**, **`uniqueX` / `unique`** — no redundant null checks after `getX`
62
+
63
+ ### Ent helpers
64
+
65
+ | API | Use when |
66
+ |-----|----------|
67
+ | `getX(id)` | Row must exist |
68
+ | `get(id)` | Row may be missing |
69
+ | `uniqueX` / `firstX` | Index query must yield exactly one / at least one |
70
+
71
+ ### Edge patterns
72
+
73
+ ```typescript
74
+ const related = await entity.edge("relatedEntity");
75
+ return { ...entity, related };
76
+ ```
77
+
78
+ Standard update:
79
+
80
+ ```typescript
81
+ const { entityId, ...updates } = args;
82
+ const entity = await ctx.table("entities").getX(entityId);
83
+ return entity.patch(updates);
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Documentation style
89
+
90
+ - **No** JSDoc on every exported function — names should read clearly
91
+ - **Only** inline comments for non-obvious business rules
92
+
93
+ ---
94
+
95
+ ## Convex platform notes (template-wide)
96
+
97
+ These apply to any `query`/`mutation`/`action` code; **this repo’s public API should still use `zQuery`/`zMutation`** per above.
98
+
99
+ - Register **internal** functions with `internalQuery` / `internalMutation` / `internalAction` — never expose sensitive ops as public `query`/`mutation`
100
+ - **Actions**: add `"use node"` only in files that need Node builtins; **never** mix `"use node"` with queries/mutations in the same file
101
+ - **Pagination**: use `paginationOptsValidator` and `.paginate()` for large lists — avoid unbounded `.collect()` on big tables
102
+ - **Scheduling**: only schedule **internal** function references
103
+ - **Queries**: do not use `Date.now()` in query logic if it should be reactive/cached predictably
104
+ - **DB**: no `.collect().length` for counts at scale — denormalize or bound reads
105
+
106
+ For HTTP routes in Convex, use `httpRouter` + `httpAction` and register exact paths.
107
+
108
+ ---
109
+
110
+ ## Client imports
111
+
112
+ Frontend code imports **`Doc` / `Id`** only through per-entity **`types.ts` aliases** (e.g. `Todo`, `TodoId`). Do not use `Ent<T>` outside the backend.
@@ -0,0 +1,145 @@
1
+ ---
2
+ description: Project-wide coding standards covering import ordering, naming conventions, code style, and testing policy. Apply across all TypeScript/TSX files.
3
+ globs: **/*.ts,**/*.tsx
4
+ ---
5
+
6
+ # Coding Standards
7
+
8
+ ## Naming Conventions
9
+
10
+ ### Functions
11
+ - Use verb + noun: `getEntity`, `updateEntity`, `createEntity`, `deleteEntity`
12
+ - Be specific when needed: `getEntityByEmail`, `updateEntityField`
13
+ - Avoid abbreviations unless widely understood
14
+ - Avoid verbose qualifiers: prefer `updateEntity` over `updateEntityCoreDetails`
15
+
16
+ ### Fields
17
+ - `camelCase` for all field names
18
+ - Use `userId` — not `clerkId` or other provider-specific names
19
+
20
+ ### Variables
21
+ - Keep names concise and descriptive
22
+ - Use singular for single items (`todo`, `entity`) and plural for collections (`todos`, `entities`)
23
+ - Destructure to extract IDs: `const { entityId, ...updates } = args;`
24
+
25
+ ### Zod Schemas
26
+
27
+ The inferred type and the schema constant must share the same PascalCase name. Declare the type **above** the const. This applies to both API and web code:
28
+
29
+ ```typescript
30
+ // Good
31
+ export type CreateConversationInput = z.infer<typeof CreateConversationInput>;
32
+ export const CreateConversationInput = z.object({
33
+ title: z.string().min(1).max(120),
34
+ });
35
+
36
+ // Bad — mismatched names, type below const
37
+ export const createConversationSchema = z.object({ title: z.string() });
38
+ export type CreateConversationInput = z.infer<typeof createConversationSchema>;
39
+ ```
40
+
41
+ ## Code Style
42
+
43
+ ### Formatting
44
+ - Single blank line between functions
45
+ - No blank lines at the start or end of a function body
46
+ - 2-space indentation
47
+ - Trailing commas in multi-line arrays and objects
48
+
49
+ ### Class Spacing
50
+
51
+ Each member of a class (property, accessor, or method) must be separated by a blank line. Decorators go on their own line above the member. Never inline a method body — always expand to a full block:
52
+
53
+ ```typescript
54
+ // Good
55
+ export class FooController {
56
+ @observable.ref
57
+ accessor isOpen = false;
58
+
59
+ @observable.ref
60
+ accessor selectedId: string | undefined = undefined;
61
+
62
+ @computed
63
+ get label() {
64
+ return this.isOpen ? "Close" : "Open";
65
+ }
66
+
67
+ @action
68
+ open(id: string) {
69
+ this.selectedId = id;
70
+ this.isOpen = true;
71
+ }
72
+ }
73
+
74
+ // Bad — inlined bodies, missing blank lines, stacked decorator
75
+ export class FooController {
76
+ @observable.ref accessor isOpen = false;
77
+ @observable.ref accessor selectedId: string | undefined = undefined;
78
+ @computed get label() { return this.isOpen ? "Close" : "Open"; }
79
+ @action open(id: string) { this.selectedId = id; this.isOpen = true; }
80
+ }
81
+ ```
82
+
83
+ ### No Expression Nesting
84
+
85
+ Never pass an awaited call or a complex expression directly as an argument. Extract to a named variable first:
86
+
87
+ ```typescript
88
+ // Good
89
+ const user = await getUser(userId);
90
+ await sendWelcomeEmail(user);
91
+
92
+ // Bad
93
+ await sendWelcomeEmail(await getUser(userId));
94
+ ```
95
+
96
+ ### Block Statements
97
+
98
+ Always use block bodies (`{ }`). This is enforced by Biome (`useBlockStatements`). No single-line `if`/`throw`, no expression-body arrow callbacks:
99
+
100
+ ```typescript
101
+ // Good
102
+ if (!conversation) {
103
+ throw new Error("Not found");
104
+ }
105
+
106
+ onSubmit={() => {
107
+ controller.submit({ id: bootstrap.id });
108
+ }}
109
+
110
+ // Bad
111
+ if (!conversation) throw new Error("Not found");
112
+
113
+ onSubmit={() => controller.submit({ id: bootstrap.id })}
114
+ ```
115
+
116
+ ### Conditional Logic
117
+
118
+ Prefer concise conditionals — avoid unnecessary intermediate variables:
119
+
120
+ ```typescript
121
+ // Good
122
+ const count = entity.limit ?? 0;
123
+ if (count <= 0) {
124
+ throw new Error("Limit reached");
125
+ }
126
+
127
+ // Bad
128
+ const remaining = entity.limit ?? 0;
129
+ if (!entity.limit || entity.limit <= 0) {
130
+ throw new Error("Limit reached");
131
+ }
132
+ ```
133
+
134
+ ### Async / Await
135
+ - Always `await` async operations — never fire-and-forget unless intentional
136
+ - Use `Promise.all()` for independent parallel operations
137
+
138
+ ### Validation
139
+ - Validate business rules before any database operation
140
+ - Centralise reusable validation in `@/utils/`
141
+ - Keep CRUD files thin — validation logic belongs in utils
142
+
143
+ ## Testing
144
+
145
+ Test files **should not** be created unless explicitly requested. Focus on implementation quality over test coverage.
@@ -0,0 +1,334 @@
1
+ ---
2
+ description: Web frontend — surface architecture (MobX + React Router + Convex + Clerk SSR). Layers, routing, data loading, Convex/React Query wiring, and component conventions.
3
+ globs: templates/apps/web/**/*.tsx,templates/apps/web/**/*.ts,apps/web/**/*.tsx,apps/web/**/*.ts
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Frontend architecture — React Router + Convex
8
+
9
+ Audience: engineers working on `apps/web` (or `templates/apps/web` in this repo).
10
+
11
+ This stack uses **surfaces** (vertical slices), **MobX** for UI state, **Convex** for data, and **SSR loaders** with `ConvexHttpClient` + Clerk. An older **v2** ruleset documented a Hono BFF + `api` client; this project does **not** use that pattern — use Convex queries/mutations and the paths below instead.
12
+
13
+ ---
14
+
15
+ ## Philosophy
16
+
17
+ 1. **Display components are dumb** — props in, JSX out. No data fetching, no surface state, no controller imports.
18
+ 2. **`create.tsx` factories are smart closures** — they close over controller/bootstrap at wiring time and return an `observer()` component. This is the only layer that bridges MobX and React hooks.
19
+ 3. **Controllers are the brain** — a MobX class owns cross-section UI state (dialogs, selection). No React imports.
20
+
21
+ **Why explicit wiring instead of React Context?** Context hides the dependency graph. Here, access is traceable: surface → `install` → `create` → display. No surprise re-renders or ambiguous state ownership.
22
+
23
+ **Why not add Zustand?** MobX plus optional `GlobalProvider` already cover reactive UI state; Convex plus TanStack Query cover server state — avoid a third paradigm.
24
+
25
+ ---
26
+
27
+ ## Layer stack
28
+
29
+ ```
30
+ routes/.../[page].tsx → server loader + clientLoader; passes loaderData as bootstrap
31
+ bootstrap.ts → ConvexHttpClient query only (SSR); no browser APIs
32
+ [feature].tsx → surface entry: useRef install guard, useMemo(createLayout)
33
+ install.tsx → instantiate controller; dynamic import() of section creates
34
+ layout.tsx → LayoutController + observer shell + skeleton slots
35
+ [feature]_controller.ts → MobX — shared UI state; may call convex.mutation
36
+ [section]_presenter.ts → MobX — section-scoped logic (optional)
37
+ [section]/create.tsx → factory → observer; useQuery + convexQuery + initialData
38
+ [section]/[section].tsx → pure display (`useFormContext` exception for forms)
39
+ ui/ → dumb components scoped to this surface
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Folder and naming
45
+
46
+ ```
47
+ src/surfaces/todos/
48
+ all_todos/ # list surface
49
+ single_todo/ # id-scoped surface
50
+ ```
51
+
52
+ - File names: **snake_case**
53
+ - Factory/install args: `Opts` suffix — `InstallSingleTodoOpts`
54
+ - Component props: `Props` suffix — `MainProps`
55
+ - List surfaces: **`All`** prefix — `AllTodos`
56
+ - Id surfaces: **`Single`** prefix — `SingleTodo`
57
+ - CRUD UI folders: `new_`, `edit_`, `delete_` — never `create_` (conflicts with `create.tsx` pattern)
58
+ - Display component names must **not** repeat the parent path — inside `single_todo/header/`, export `Header`, not `SingleTodoHeader`
59
+ - Imports: **`@/...` only** — no relative imports within features
60
+
61
+ ---
62
+
63
+ ## Setup — MobX (Stage 3)
64
+
65
+ Requires TypeScript 5+ with `experimentalDecorators: false` and Babel decorators in Vite:
66
+
67
+ ```json
68
+ { "compilerOptions": { "experimentalDecorators": false } }
69
+ ```
70
+
71
+ ```typescript
72
+ react({ babel: { plugins: [["@babel/plugin-proposal-decorators", { version: "2023-05" }]] } })
73
+ ```
74
+
75
+ - Use `accessor` on `@observable` fields
76
+ - Prefer `@action` methods — mutations after `await` inside an `@action async` are safe without `runInAction`
77
+ - Use `@observable.ref` for component slot fields on layout controllers
78
+
79
+ ---
80
+
81
+ ## Setup — Convex and React Query
82
+
83
+ **Convex React client** (browser, `expectAuth: true`) and **ConvexQueryClient** live in `@/web/libs/convex_query_client`:
84
+
85
+ ```typescript
86
+ import { convex, convexQueryClient } from "@/web/libs/convex_query_client";
87
+ ```
88
+
89
+ **TanStack Query client** is `@/web/libs/react_query_client` — it connects `convexQueryClient` and sets default `queryFn`. Use this singleton anywhere you need a `QueryClient` on the client.
90
+
91
+ **Presenters and controllers** call mutations outside React via the same `convex` singleton:
92
+
93
+ ```typescript
94
+ import { convex } from "@/web/libs/convex_query_client";
95
+ import { api } from "@/api/_generated/api";
96
+
97
+ await convex.mutation(api.todos.crud.updateTodo, args);
98
+ ```
99
+
100
+ **Creates** use `useQuery` with `convexQuery` from `@convex-dev/react-query` and **`initialData` from bootstrap** so the first paint matches SSR.
101
+
102
+ ---
103
+
104
+ ## Global state (three tiers)
105
+
106
+ | Tier | Mechanism | When |
107
+ |------|-----------|------|
108
+ | Module `stores/` | Plain module variable | One-shot, non-reactive handoff across navigation |
109
+ | `GlobalProvider` | React context + `useGlobal()` | Reactive state across `<Outlet />` (use sparingly) |
110
+ | MobX controller | `[feature]_controller.ts` | All UI state inside a surface |
111
+
112
+ Module store rules: consume-once helpers, one concern per file, document lifetime at top, never for persistence alone.
113
+
114
+ ---
115
+
116
+ ## Routing and auth
117
+
118
+ **Route config** (`src/routes.ts`) uses route groups `(auth)` and `(dashboard)`. URLs come from `layout`/`prefix`/`route`, not folder names.
119
+
120
+ Example shape (adjust files to match your app):
121
+
122
+ ```typescript
123
+ import { index, layout, prefix, type RouteConfig, route } from "@react-router/dev/routes";
124
+
125
+ export default [
126
+ layout("./routes/(auth)/layout.tsx", [route("/sign-in/*", "./routes/(auth)/sign-in/index.tsx")]),
127
+ layout("./routes/(dashboard)/layout.tsx", [
128
+ index("./routes/(dashboard)/index.tsx"),
129
+ ...prefix("todos", [
130
+ layout("./routes/(dashboard)/(todos)/layout.tsx", [
131
+ index("./routes/(dashboard)/(todos)/index.tsx"),
132
+ route(":id", "./routes/(dashboard)/(todos)/[id].tsx"),
133
+ ]),
134
+ ]),
135
+ ]),
136
+ ] satisfies RouteConfig;
137
+ ```
138
+
139
+ **Middleware** (`root.tsx`): Clerk + a protected-route middleware enforce auth for non-public paths before loaders run.
140
+
141
+ **`root.tsx`**: providers (`ApiAuthProvider`, `GlobalProvider`, …) + `<Outlet />` — no feature sidebar here.
142
+
143
+ **Dashboard layout**: shell only (e.g. `SidebarProvider`, `Sidebar`, `Outlet`).
144
+
145
+ **Router types**: import `Route` from `./+types/...` adjacent to the route file (or the generated types path your tsconfig uses).
146
+
147
+ ---
148
+
149
+ ## SSR data loading (loaders + bootstrap)
150
+
151
+ Flow:
152
+
153
+ 1. **Server** — route `loader` builds an authenticated `ConvexHttpClient` and runs bootstrap.
154
+ 2. **Client** — `clientLoader` returns `serverLoader()` only — no second fetch.
155
+ 3. **Hydration** — `useQuery({ ...convexQuery(...), initialData: bootstrap.* })` so the live subscription takes over.
156
+
157
+ ### Auth helper — `src/libs/server/auth.ts`
158
+
159
+ Single place for loader-time Convex HTTP client + redirect if unauthenticated:
160
+
161
+ ```typescript
162
+ import { getAuth } from "@clerk/react-router/server";
163
+ import { ConvexHttpClient } from "convex/browser";
164
+ import type { LoaderFunctionArgs } from "react-router";
165
+ import { redirect } from "react-router";
166
+
167
+ export const createConvexClient = async (args: LoaderFunctionArgs): Promise<ConvexHttpClient> => {
168
+ const auth = await getAuth(args);
169
+ if (!auth.isAuthenticated) {
170
+ throw redirect(`/sign-in?redirect_url=${encodeURIComponent(args.request.url)}`);
171
+ }
172
+ const token = await auth.getToken({ template: "convex" });
173
+ const client = new ConvexHttpClient(process.env.VITE_CONVEX_URL!);
174
+ if (token) {
175
+ client.setAuth(token);
176
+ }
177
+ return client;
178
+ };
179
+ ```
180
+
181
+ Use `getToken({ template: "convex" })` (not the default session token). In server loaders use `process.env.VITE_CONVEX_URL` (not `import.meta.env`).
182
+
183
+ ### Bootstrap — `surfaces/.../bootstrap.ts`
184
+
185
+ - Accept `{ client: ConvexHttpClient }` from the route loader
186
+ - Use `client.query(api...)` only — no `ConvexReactClient`, `convexQuery`, or `ensureQueryData` in bootstrap
187
+ - May `throw redirect(...)` for invalid params
188
+
189
+ ### Route module
190
+
191
+ ```typescript
192
+ export async function loader(args: Route.LoaderArgs) {
193
+ const client = await createConvexClient(args);
194
+ return bootstrapAllTodos({ client });
195
+ }
196
+
197
+ export async function clientLoader({ serverLoader }: Route.ClientLoaderArgs) {
198
+ return serverLoader();
199
+ }
200
+
201
+ export default function Page({ loaderData: bootstrap }: Route.ComponentProps) {
202
+ return <AllTodos bootstrap={bootstrap} />;
203
+ }
204
+ ```
205
+
206
+ ### Client `create.tsx`
207
+
208
+ ```typescript
209
+ const { data: todos } = useQuery({
210
+ ...convexQuery(api.todos.crud.listTodos, {}),
211
+ initialData: bootstrap.todos as never,
212
+ });
213
+ ```
214
+
215
+ Spread `convexQuery` first, then `initialData`. The `as never` cast is often needed for serialized vs local types.
216
+
217
+ ### Anti-patterns
218
+
219
+ | Wrong | Right |
220
+ |-------|--------|
221
+ | `clientLoader` calling `ensureQueryData(convexQuery(...))` for SSR | Server `loader` + `clientLoader` → `serverLoader()` |
222
+ | Building `ConvexHttpClient` inside bootstrap | Receive `client` from `createConvexClient` |
223
+ | Duplicating Clerk redirects in every bootstrap | Centralize in `createConvexClient` / middleware |
224
+
225
+ ---
226
+
227
+ ## Shell vs pages
228
+
229
+ - **Layout-mounted** (e.g. sidebar): `useEffect(() => installSidebar(...), [])` once.
230
+ - **Route-mounted** pages: synchronous **`useRef` install guard** on first render (avoids empty flash; StrictMode-safe).
231
+
232
+ ---
233
+
234
+ ## Surface entry (`[feature].tsx`)
235
+
236
+ - Receives **`bootstrap` from the route** — never fetch the primary payload inside the surface
237
+ - `useMemo(() => createLayout(), [])` once
238
+ - `useRef` guard calls `installFeature({ layout, bootstrap, navigate })` synchronously on first run
239
+ - Obtain `useNavigate` (and similar) here and **pass into `install`**, not inside `install` itself
240
+
241
+ ---
242
+
243
+ ## `install.tsx`
244
+
245
+ - **New `Controller()` here** — exactly once; shared across all `create` closures
246
+ - Use dynamic `import()` per section for code splitting
247
+ - Plain function — not a hook, not a component
248
+ - Do not use React Context to replace explicit controller passing
249
+
250
+ ---
251
+
252
+ ## `layout.tsx`
253
+
254
+ - `LayoutController` holds `@observable.ref` slots for `ComponentType`
255
+ - `@action` setters for slots; observer shell renders skeletons until slots load
256
+
257
+ ---
258
+
259
+ ## Controllers and presenters
260
+
261
+ - **Controller**: cross-section UI state; `convex.mutation` / `convex.query` when needed; **no React**
262
+ - **Presenter**: one section’s logic; same rules — **no React**
263
+ - Prefer **`@action async`** without extra `runInAction` for Stage 3 decorators when using MobX’s supported pattern
264
+
265
+ ---
266
+
267
+ ## `create.tsx`
268
+
269
+ - Call child factories **outside** the returned `observer()` so identities stay stable
270
+ - Inside `observer`: `useQuery`, `useForm`, etc.
271
+ - Pass **`initialData: bootstrap.\*`** for the same Convex function used in SSR
272
+ - No `useMutation` for routine writes — use `convex.mutation` in controller/presenter and pass callbacks
273
+ - No `useState` for feature UI state — controller/presenter own that
274
+ - Inline JSX handlers: **block or multi-line** — not one-liner `onClick={() => foo()}`
275
+
276
+ ---
277
+
278
+ ## Display components
279
+
280
+ - Props + callbacks only; **no** `Doc<"todos">` / raw Convex types — import **`Todo`**, **`TodoId`** from `@/api/todos/types` (or the entity’s `types.ts`)
281
+ - Never import **`Ent<...>`** on the frontend
282
+ - **Forms**: `useForm` + `<FormProvider>` in `create.tsx`; display uses **`useFormContext()`** — never pass `UseFormReturn` as a prop
283
+ - **Thin UIs** can live inline in `create.tsx` without a separate display file
284
+ - Arrow functions, named `Props` type, explicit `return (...)` for JSX
285
+
286
+ ---
287
+
288
+ ## Schema (`schema.ts`)
289
+
290
+ Same PascalCase name for type and Zod value (see `coding_standards.mdc`):
291
+
292
+ ```typescript
293
+ export type NewTodoSchema = z.infer<typeof NewTodoSchema>;
294
+ export const NewTodoSchema = z.object({
295
+ title: z.string().min(1, "Title is required"),
296
+ });
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Factory opts vs observer props
302
+
303
+ - **Opts** — known when the factory runs (`controller`, `bootstrap`, child components created by parent). Closed over.
304
+ - **Props** on the observer — per-render / per-item values (`todoId`, list row props).
305
+
306
+ Component slots are **`ComponentType`**, not `ReactNode`.
307
+
308
+ ---
309
+
310
+ ## DI tree
311
+
312
+ Nested surfaces (sheets, dialogs) live **under** the section that constructs them so dependencies stay a subtree of that section’s `create.tsx`.
313
+
314
+ ---
315
+
316
+ ## Next.js variant
317
+
318
+ Adapt loader and client-entry patterns to your framework’s routing and data APIs — layer names and surface rules stay the same.
319
+
320
+ ---
321
+
322
+ ## Quick reference
323
+
324
+ | File | Hooks? | Controller |
325
+ |------|--------|------------|
326
+ | Route module | No in loader | No |
327
+ | `bootstrap.ts` | No | No |
328
+ | `[feature].tsx` | `useRef`, `useMemo`, router | No — passes to install |
329
+ | `install.tsx` | No | **Instantiates** |
330
+ | `layout.tsx` | No | No |
331
+ | `*_controller.ts` | No (class) | — |
332
+ | `*_presenter.ts` | No (class) | — |
333
+ | `create.tsx` | Yes, inside observer | Receives via closure |
334
+ | Display | `useFormContext` only | No |
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ permissions:
10
+ actions: read
11
+ contents: read
12
+
13
+ jobs:
14
+ build:
15
+ name: Build & Type-check
16
+ runs-on: ubuntu-latest
17
+ env:
18
+ SKIP_HOOKS: "1"
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+
25
+ - uses: oven-sh/setup-bun@v2
26
+ with:
27
+ bun-version: latest
28
+
29
+ - name: Install dependencies
30
+ run: bun install --frozen-lockfile
31
+
32
+ - name: Typecheck affected projects
33
+ run: bun run typecheck
34
+
35
+ - uses: nrwl/nx-set-shas@v4
36
+
37
+ - name: Build affected projects
38
+ run: bun nx affected -t build
39
+ # - name: Test affected projects
40
+ # run: bun nx affected -t test