@alfredmouelle/create-stack 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 (206) hide show
  1. package/README.md +56 -0
  2. package/_stack/apps/next-base/.dockerignore +10 -0
  3. package/_stack/apps/next-base/Dockerfile +34 -0
  4. package/_stack/apps/next-base/README.md +32 -0
  5. package/_stack/apps/next-base/components.json +25 -0
  6. package/_stack/apps/next-base/drizzle.config.ts +16 -0
  7. package/_stack/apps/next-base/next.config.ts +8 -0
  8. package/_stack/apps/next-base/package.json +70 -0
  9. package/_stack/apps/next-base/postcss.config.mjs +7 -0
  10. package/_stack/apps/next-base/src/app/api/auth/[...all]/route.ts +4 -0
  11. package/_stack/apps/next-base/src/app/api/trpc/[trpc]/route.ts +23 -0
  12. package/_stack/apps/next-base/src/app/auth/_components/forgot-password-form.tsx +92 -0
  13. package/_stack/apps/next-base/src/app/auth/_components/reset-password-form.tsx +105 -0
  14. package/_stack/apps/next-base/src/app/auth/_components/sign-in-form.tsx +126 -0
  15. package/_stack/apps/next-base/src/app/auth/_components/sign-up-form.tsx +139 -0
  16. package/_stack/apps/next-base/src/app/auth/_components/verify-email-actions.tsx +45 -0
  17. package/_stack/apps/next-base/src/app/auth/forgot-password/page.tsx +19 -0
  18. package/_stack/apps/next-base/src/app/auth/layout.tsx +9 -0
  19. package/_stack/apps/next-base/src/app/auth/reset-password/page.tsx +26 -0
  20. package/_stack/apps/next-base/src/app/auth/sign-in/page.tsx +27 -0
  21. package/_stack/apps/next-base/src/app/auth/sign-up/page.tsx +27 -0
  22. package/_stack/apps/next-base/src/app/auth/verify-email/page.tsx +30 -0
  23. package/_stack/apps/next-base/src/app/dashboard/page.tsx +12 -0
  24. package/_stack/apps/next-base/src/app/globals.css +171 -0
  25. package/_stack/apps/next-base/src/app/layout.tsx +23 -0
  26. package/_stack/apps/next-base/src/app/page.tsx +15 -0
  27. package/_stack/apps/next-base/src/components/data-table.tsx +77 -0
  28. package/_stack/apps/next-base/src/components/infinite-data-table.tsx +102 -0
  29. package/_stack/apps/next-base/src/components/sortable-header.tsx +37 -0
  30. package/_stack/apps/next-base/src/components/theme-provider.tsx +8 -0
  31. package/_stack/apps/next-base/src/components/theme-toggle.tsx +37 -0
  32. package/_stack/apps/next-base/src/components/ui/button.tsx +64 -0
  33. package/_stack/apps/next-base/src/components/ui/calendar.tsx +185 -0
  34. package/_stack/apps/next-base/src/components/ui/card.tsx +84 -0
  35. package/_stack/apps/next-base/src/components/ui/date-picker.tsx +85 -0
  36. package/_stack/apps/next-base/src/components/ui/date-range-picker.tsx +138 -0
  37. package/_stack/apps/next-base/src/components/ui/dropdown-menu.tsx +246 -0
  38. package/_stack/apps/next-base/src/components/ui/form.tsx +149 -0
  39. package/_stack/apps/next-base/src/components/ui/input-group.tsx +97 -0
  40. package/_stack/apps/next-base/src/components/ui/input.tsx +18 -0
  41. package/_stack/apps/next-base/src/components/ui/label.tsx +18 -0
  42. package/_stack/apps/next-base/src/components/ui/popover.tsx +76 -0
  43. package/_stack/apps/next-base/src/components/ui/skeleton.tsx +13 -0
  44. package/_stack/apps/next-base/src/components/ui/spinner.tsx +8 -0
  45. package/_stack/apps/next-base/src/components/ui/table.tsx +87 -0
  46. package/_stack/apps/next-base/src/emails/components/components.tsx +199 -0
  47. package/_stack/apps/next-base/src/emails/components/context.tsx +18 -0
  48. package/_stack/apps/next-base/src/emails/components/index.ts +23 -0
  49. package/_stack/apps/next-base/src/emails/components/theme.ts +65 -0
  50. package/_stack/apps/next-base/src/emails/reset-password.tsx +16 -0
  51. package/_stack/apps/next-base/src/emails/verify-email.tsx +15 -0
  52. package/_stack/apps/next-base/src/env.ts +41 -0
  53. package/_stack/apps/next-base/src/features/auth/auth-card.tsx +30 -0
  54. package/_stack/apps/next-base/src/features/auth/form-alert.tsx +27 -0
  55. package/_stack/apps/next-base/src/features/auth/google-button.tsx +66 -0
  56. package/_stack/apps/next-base/src/features/auth/schemas.ts +35 -0
  57. package/_stack/apps/next-base/src/lib/date.ts +4 -0
  58. package/_stack/apps/next-base/src/lib/utils.ts +6 -0
  59. package/_stack/apps/next-base/src/server/api/root.ts +10 -0
  60. package/_stack/apps/next-base/src/server/api/routers/health.router.ts +8 -0
  61. package/_stack/apps/next-base/src/server/api/trpc.ts +56 -0
  62. package/_stack/apps/next-base/src/server/auth/guards.ts +10 -0
  63. package/_stack/apps/next-base/src/server/better-auth/client.ts +9 -0
  64. package/_stack/apps/next-base/src/server/better-auth/config.ts +60 -0
  65. package/_stack/apps/next-base/src/server/better-auth/emails.tsx +25 -0
  66. package/_stack/apps/next-base/src/server/better-auth/index.ts +1 -0
  67. package/_stack/apps/next-base/src/server/better-auth/server.ts +14 -0
  68. package/_stack/apps/next-base/src/server/db/index.ts +6 -0
  69. package/_stack/apps/next-base/src/server/db/keyset.ts +63 -0
  70. package/_stack/apps/next-base/src/server/db/schemas/auth.schema.ts +71 -0
  71. package/_stack/apps/next-base/src/server/db/schemas/index.ts +2 -0
  72. package/_stack/apps/next-base/src/server/db/seed.ts +27 -0
  73. package/_stack/apps/next-base/src/server/email/adapters/resend/config.ts +7 -0
  74. package/_stack/apps/next-base/src/server/email/adapters/resend/index.ts +75 -0
  75. package/_stack/apps/next-base/src/server/email/core/address.ts +21 -0
  76. package/_stack/apps/next-base/src/server/email/core/port.ts +89 -0
  77. package/_stack/apps/next-base/src/server/email/core/render.ts +16 -0
  78. package/_stack/apps/next-base/src/server/email/factory.ts +47 -0
  79. package/_stack/apps/next-base/src/server/email/index.ts +36 -0
  80. package/_stack/apps/next-base/src/trpc/query-client.ts +19 -0
  81. package/_stack/apps/next-base/src/trpc/react.tsx +62 -0
  82. package/_stack/apps/next-base/src/trpc/server.ts +23 -0
  83. package/_stack/apps/next-base/tsconfig.json +37 -0
  84. package/_stack/apps/tanstack-base/.dockerignore +13 -0
  85. package/_stack/apps/tanstack-base/Dockerfile +28 -0
  86. package/_stack/apps/tanstack-base/README.md +31 -0
  87. package/_stack/apps/tanstack-base/components.json +25 -0
  88. package/_stack/apps/tanstack-base/drizzle.config.ts +16 -0
  89. package/_stack/apps/tanstack-base/package.json +85 -0
  90. package/_stack/apps/tanstack-base/public/favicon.ico +0 -0
  91. package/_stack/apps/tanstack-base/public/logo192.png +0 -0
  92. package/_stack/apps/tanstack-base/public/logo512.png +0 -0
  93. package/_stack/apps/tanstack-base/public/manifest.json +25 -0
  94. package/_stack/apps/tanstack-base/public/robots.txt +3 -0
  95. package/_stack/apps/tanstack-base/src/components/data-table.tsx +77 -0
  96. package/_stack/apps/tanstack-base/src/components/form/field-error.tsx +18 -0
  97. package/_stack/apps/tanstack-base/src/components/form/text-field.tsx +47 -0
  98. package/_stack/apps/tanstack-base/src/components/infinite-data-table.tsx +102 -0
  99. package/_stack/apps/tanstack-base/src/components/sortable-header.tsx +37 -0
  100. package/_stack/apps/tanstack-base/src/components/theme-provider.tsx +69 -0
  101. package/_stack/apps/tanstack-base/src/components/theme-toggle.tsx +35 -0
  102. package/_stack/apps/tanstack-base/src/components/ui/button.tsx +64 -0
  103. package/_stack/apps/tanstack-base/src/components/ui/calendar.tsx +185 -0
  104. package/_stack/apps/tanstack-base/src/components/ui/card.tsx +84 -0
  105. package/_stack/apps/tanstack-base/src/components/ui/date-picker.tsx +83 -0
  106. package/_stack/apps/tanstack-base/src/components/ui/date-range-picker.tsx +136 -0
  107. package/_stack/apps/tanstack-base/src/components/ui/dropdown-menu.tsx +246 -0
  108. package/_stack/apps/tanstack-base/src/components/ui/input-group.tsx +97 -0
  109. package/_stack/apps/tanstack-base/src/components/ui/input.tsx +18 -0
  110. package/_stack/apps/tanstack-base/src/components/ui/label.tsx +18 -0
  111. package/_stack/apps/tanstack-base/src/components/ui/popover.tsx +74 -0
  112. package/_stack/apps/tanstack-base/src/components/ui/skeleton.tsx +13 -0
  113. package/_stack/apps/tanstack-base/src/components/ui/spinner.tsx +8 -0
  114. package/_stack/apps/tanstack-base/src/components/ui/table.tsx +87 -0
  115. package/_stack/apps/tanstack-base/src/emails/components/components.tsx +199 -0
  116. package/_stack/apps/tanstack-base/src/emails/components/context.tsx +18 -0
  117. package/_stack/apps/tanstack-base/src/emails/components/index.ts +23 -0
  118. package/_stack/apps/tanstack-base/src/emails/components/theme.ts +65 -0
  119. package/_stack/apps/tanstack-base/src/emails/reset-password.tsx +16 -0
  120. package/_stack/apps/tanstack-base/src/emails/verify-email.tsx +15 -0
  121. package/_stack/apps/tanstack-base/src/env.ts +41 -0
  122. package/_stack/apps/tanstack-base/src/features/auth/auth-card.tsx +30 -0
  123. package/_stack/apps/tanstack-base/src/features/auth/form-alert.tsx +27 -0
  124. package/_stack/apps/tanstack-base/src/features/auth/google-button.tsx +64 -0
  125. package/_stack/apps/tanstack-base/src/features/auth/schemas.ts +35 -0
  126. package/_stack/apps/tanstack-base/src/lib/date.ts +4 -0
  127. package/_stack/apps/tanstack-base/src/lib/utils.ts +6 -0
  128. package/_stack/apps/tanstack-base/src/router.tsx +40 -0
  129. package/_stack/apps/tanstack-base/src/routes/__root.tsx +73 -0
  130. package/_stack/apps/tanstack-base/src/routes/_authed/dashboard.tsx +12 -0
  131. package/_stack/apps/tanstack-base/src/routes/_authed.tsx +21 -0
  132. package/_stack/apps/tanstack-base/src/routes/api/auth/$.ts +14 -0
  133. package/_stack/apps/tanstack-base/src/routes/api.trpc.$.tsx +31 -0
  134. package/_stack/apps/tanstack-base/src/routes/auth/forgot-password.tsx +89 -0
  135. package/_stack/apps/tanstack-base/src/routes/auth/reset-password.tsx +111 -0
  136. package/_stack/apps/tanstack-base/src/routes/auth/sign-in.tsx +117 -0
  137. package/_stack/apps/tanstack-base/src/routes/auth/sign-up.tsx +119 -0
  138. package/_stack/apps/tanstack-base/src/routes/auth/verify-email.tsx +72 -0
  139. package/_stack/apps/tanstack-base/src/routes/auth.tsx +22 -0
  140. package/_stack/apps/tanstack-base/src/routes/index.tsx +18 -0
  141. package/_stack/apps/tanstack-base/src/server/api/root.ts +10 -0
  142. package/_stack/apps/tanstack-base/src/server/api/routers/health.router.ts +8 -0
  143. package/_stack/apps/tanstack-base/src/server/api/trpc.ts +61 -0
  144. package/_stack/apps/tanstack-base/src/server/better-auth/client.ts +9 -0
  145. package/_stack/apps/tanstack-base/src/server/better-auth/config.ts +68 -0
  146. package/_stack/apps/tanstack-base/src/server/better-auth/emails.tsx +25 -0
  147. package/_stack/apps/tanstack-base/src/server/better-auth/index.ts +1 -0
  148. package/_stack/apps/tanstack-base/src/server/better-auth/session.ts +9 -0
  149. package/_stack/apps/tanstack-base/src/server/db/index.ts +6 -0
  150. package/_stack/apps/tanstack-base/src/server/db/keyset.ts +63 -0
  151. package/_stack/apps/tanstack-base/src/server/db/schemas/auth.schema.ts +71 -0
  152. package/_stack/apps/tanstack-base/src/server/db/schemas/index.ts +2 -0
  153. package/_stack/apps/tanstack-base/src/server/db/seed.ts +27 -0
  154. package/_stack/apps/tanstack-base/src/server/email/adapters/resend/config.ts +7 -0
  155. package/_stack/apps/tanstack-base/src/server/email/adapters/resend/index.ts +75 -0
  156. package/_stack/apps/tanstack-base/src/server/email/core/address.ts +21 -0
  157. package/_stack/apps/tanstack-base/src/server/email/core/port.ts +89 -0
  158. package/_stack/apps/tanstack-base/src/server/email/core/render.ts +16 -0
  159. package/_stack/apps/tanstack-base/src/server/email/factory.ts +47 -0
  160. package/_stack/apps/tanstack-base/src/server/email/index.ts +36 -0
  161. package/_stack/apps/tanstack-base/src/styles.css +171 -0
  162. package/_stack/apps/tanstack-base/src/trpc/devtools.tsx +6 -0
  163. package/_stack/apps/tanstack-base/src/trpc/query-client.ts +19 -0
  164. package/_stack/apps/tanstack-base/src/trpc/react.tsx +49 -0
  165. package/_stack/apps/tanstack-base/src/trpc/server.ts +11 -0
  166. package/_stack/apps/tanstack-base/tsconfig.json +27 -0
  167. package/_stack/apps/tanstack-base/tsr.config.json +3 -0
  168. package/_stack/apps/tanstack-base/vite.config.ts +15 -0
  169. package/_stack/packages/analytics/capability.json +26 -0
  170. package/_stack/packages/cache/capability.json +21 -0
  171. package/_stack/packages/error-tracking/capability.json +21 -0
  172. package/_stack/packages/jobs/capability.json +26 -0
  173. package/_stack/packages/logger/capability.json +21 -0
  174. package/_stack/packages/mailer/capability.json +28 -0
  175. package/_stack/packages/mailer/package.json +37 -0
  176. package/_stack/packages/mailer/src/adapters/brevo/config.ts +7 -0
  177. package/_stack/packages/mailer/src/adapters/brevo/index.ts +90 -0
  178. package/_stack/packages/mailer/src/adapters/resend/config.ts +7 -0
  179. package/_stack/packages/mailer/src/adapters/resend/index.ts +75 -0
  180. package/_stack/packages/mailer/src/adapters/ses/config.ts +13 -0
  181. package/_stack/packages/mailer/src/adapters/ses/index.ts +103 -0
  182. package/_stack/packages/storage/capability.json +32 -0
  183. package/_stack/patterns/README.md +58 -0
  184. package/_stack/patterns/_baseline/README-author.md +10 -0
  185. package/_stack/patterns/_baseline/biome.jsonc +119 -0
  186. package/_stack/patterns/_baseline/env.ts +31 -0
  187. package/_stack/patterns/_baseline/tsconfig.json +27 -0
  188. package/_stack/patterns/better-auth/pattern.json +73 -0
  189. package/_stack/patterns/better-auth-next/pattern.json +76 -0
  190. package/_stack/patterns/data-table/pattern.json +43 -0
  191. package/_stack/patterns/drizzle/pattern.json +61 -0
  192. package/_stack/patterns/trpc/pattern.json +61 -0
  193. package/_stack/patterns/trpc-next/pattern.json +64 -0
  194. package/index.mjs +216 -0
  195. package/lib/build.mjs +64 -0
  196. package/lib/env.mjs +56 -0
  197. package/lib/identity.mjs +33 -0
  198. package/lib/mailer.mjs +95 -0
  199. package/lib/manifests.mjs +61 -0
  200. package/lib/scaffold.mjs +49 -0
  201. package/lib/strip.mjs +132 -0
  202. package/lib/util.mjs +82 -0
  203. package/package.json +51 -0
  204. package/templates/next/layout.no-trpc.tsx +22 -0
  205. package/templates/tanstack/__root.no-trpc.tsx +63 -0
  206. package/templates/tanstack/router.no-trpc.tsx +24 -0
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "better-auth",
4
+ "description": "better-auth v1 with the Drizzle adapter (Postgres). Email+password, email verification, optional Google OAuth, rate limiting and TanStack Start cookies. Ships auth tables, server/client instances, session helpers and a route guard.",
5
+ "framework": "tanstack-start",
6
+ "detect": {
7
+ "deps": [
8
+ "better-auth"
9
+ ],
10
+ "files": []
11
+ },
12
+ "dependsOn": [
13
+ "drizzle"
14
+ ],
15
+ "integratesWith": [
16
+ "trpc"
17
+ ],
18
+ "capabilities": [
19
+ "mailer",
20
+ "email-kit"
21
+ ],
22
+ "deps": [
23
+ "better-auth"
24
+ ],
25
+ "env": [
26
+ "BETTER_AUTH_URL",
27
+ "BETTER_AUTH_SECRET",
28
+ "BETTER_AUTH_GOOGLE_CLIENT_ID",
29
+ "BETTER_AUTH_GOOGLE_CLIENT_SECRET"
30
+ ],
31
+ "files": [
32
+ {
33
+ "from": "apps/tanstack-base/src/server/better-auth/config.ts",
34
+ "to": "<srcRoot>/server/better-auth/config.ts"
35
+ },
36
+ {
37
+ "from": "apps/tanstack-base/src/server/better-auth/client.ts",
38
+ "to": "<srcRoot>/server/better-auth/client.ts"
39
+ },
40
+ {
41
+ "from": "apps/tanstack-base/src/server/better-auth/session.ts",
42
+ "to": "<srcRoot>/server/better-auth/session.ts"
43
+ },
44
+ {
45
+ "from": "apps/tanstack-base/src/server/better-auth/emails.tsx",
46
+ "to": "<srcRoot>/server/better-auth/emails.tsx"
47
+ },
48
+ {
49
+ "from": "apps/tanstack-base/src/server/better-auth/index.ts",
50
+ "to": "<srcRoot>/server/better-auth/index.ts"
51
+ },
52
+ {
53
+ "from": "apps/tanstack-base/src/server/db/schemas/auth.schema.ts",
54
+ "to": "<srcRoot>/server/db/schemas/auth.schema.ts"
55
+ },
56
+ {
57
+ "from": "apps/tanstack-base/src/routes/api/auth/$.ts",
58
+ "to": "<srcRoot>/routes/api/auth/$.ts"
59
+ },
60
+ {
61
+ "from": "apps/tanstack-base/src/routes/_authed.tsx",
62
+ "to": "<srcRoot>/routes/_authed.tsx"
63
+ }
64
+ ],
65
+ "wiring": [
66
+ "Append `export * from './auth.schema'` to <srcRoot>/server/db/schemas/index.ts (created by the drizzle pattern).",
67
+ "emails.tsx imports ~/server/email (mailer capability) and ~/emails/{reset-password,verify-email} (email-kit). Ensure both capabilities are added; create the two templates if missing."
68
+ ],
69
+ "notes": [
70
+ "Auth tables (user/session/account/verification) live in auth.schema.ts; better-auth manages them via the Drizzle adapter. Add custom user columns via `additionalFields` in config.ts mirrored in auth.schema.ts.",
71
+ "BETTER_AUTH_SECRET is read implicitly from env by better-auth; the rest are validated in env.ts."
72
+ ]
73
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "better-auth-next",
4
+ "description": "better-auth v1 for Next.js App Router with the Drizzle adapter. Email+password + verification, optional Google OAuth, session via next/headers (react cache), toNextJsHandler catch-all route, and server-component guards. The Next counterpart of the `better-auth` pattern.",
5
+ "framework": "next",
6
+ "detect": {
7
+ "deps": [
8
+ "better-auth"
9
+ ],
10
+ "files": [
11
+ "src/app/api/auth/[...all]/route.ts"
12
+ ]
13
+ },
14
+ "dependsOn": [
15
+ "drizzle"
16
+ ],
17
+ "integratesWith": [
18
+ "trpc-next"
19
+ ],
20
+ "capabilities": [
21
+ "mailer",
22
+ "email-kit"
23
+ ],
24
+ "deps": [
25
+ "better-auth"
26
+ ],
27
+ "env": [
28
+ "BETTER_AUTH_URL",
29
+ "BETTER_AUTH_SECRET",
30
+ "BETTER_AUTH_GOOGLE_CLIENT_ID",
31
+ "BETTER_AUTH_GOOGLE_CLIENT_SECRET"
32
+ ],
33
+ "files": [
34
+ {
35
+ "from": "apps/next-base/src/server/better-auth/config.ts",
36
+ "to": "<srcRoot>/server/better-auth/config.ts"
37
+ },
38
+ {
39
+ "from": "apps/next-base/src/server/better-auth/client.ts",
40
+ "to": "<srcRoot>/server/better-auth/client.ts"
41
+ },
42
+ {
43
+ "from": "apps/next-base/src/server/better-auth/server.ts",
44
+ "to": "<srcRoot>/server/better-auth/server.ts"
45
+ },
46
+ {
47
+ "from": "apps/next-base/src/server/better-auth/emails.tsx",
48
+ "to": "<srcRoot>/server/better-auth/emails.tsx"
49
+ },
50
+ {
51
+ "from": "apps/next-base/src/server/better-auth/index.ts",
52
+ "to": "<srcRoot>/server/better-auth/index.ts"
53
+ },
54
+ {
55
+ "from": "apps/next-base/src/server/auth/guards.ts",
56
+ "to": "<srcRoot>/server/auth/guards.ts"
57
+ },
58
+ {
59
+ "from": "apps/next-base/src/server/db/schemas/auth.schema.ts",
60
+ "to": "<srcRoot>/server/db/schemas/auth.schema.ts"
61
+ },
62
+ {
63
+ "from": "apps/next-base/src/app/api/auth/[...all]/route.ts",
64
+ "to": "<srcRoot>/app/api/auth/[...all]/route.ts"
65
+ }
66
+ ],
67
+ "wiring": [
68
+ "Append `export * from './auth.schema'` to <srcRoot>/server/db/schemas/index.ts (created by the drizzle pattern).",
69
+ "emails.tsx imports ~/server/email (mailer) and ~/emails/{reset-password,verify-email} (email-kit) — add both capabilities and the two templates.",
70
+ "Auth pages live in app/auth/* (server pages) with forms in app/auth/_components/* ('use client', shadcn Form + react-hook-form + valibot)."
71
+ ],
72
+ "notes": [
73
+ "No tanstackStartCookies — cookies are handled by toNextJsHandler. getSession uses next/headers + react cache (server.ts). Protect Server Components/pages with requireAuth() from server/auth/guards.",
74
+ "Add custom user columns via `additionalFields` in config.ts mirrored in auth.schema.ts."
75
+ ]
76
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "data-table",
4
+ "description": "Headless data tables with TanStack Table: shadcn table + skeleton primitives, a DataTable (loading + empty states), an InfiniteDataTable and a SortableHeader.",
5
+ "framework": "agnostic",
6
+ "detect": {
7
+ "deps": [
8
+ "@tanstack/react-table"
9
+ ],
10
+ "files": []
11
+ },
12
+ "dependsOn": [],
13
+ "deps": [
14
+ "@tanstack/react-table"
15
+ ],
16
+ "env": [],
17
+ "files": [
18
+ {
19
+ "from": "apps/tanstack-base/src/components/ui/table.tsx",
20
+ "to": "<srcRoot>/components/ui/table.tsx"
21
+ },
22
+ {
23
+ "from": "apps/tanstack-base/src/components/ui/skeleton.tsx",
24
+ "to": "<srcRoot>/components/ui/skeleton.tsx"
25
+ },
26
+ {
27
+ "from": "apps/tanstack-base/src/components/data-table.tsx",
28
+ "to": "<srcRoot>/components/data-table.tsx"
29
+ },
30
+ {
31
+ "from": "apps/tanstack-base/src/components/infinite-data-table.tsx",
32
+ "to": "<srcRoot>/components/infinite-data-table.tsx"
33
+ },
34
+ {
35
+ "from": "apps/tanstack-base/src/components/sortable-header.tsx",
36
+ "to": "<srcRoot>/components/sortable-header.tsx"
37
+ }
38
+ ],
39
+ "notes": [
40
+ "Pure React + TanStack Table — works in both Next App Router and TanStack Start. Needs shadcn (cn util + Button); the table/skeleton primitives are vendored here.",
41
+ "On Next.js, the component that builds the table (calls useReactTable) must be a client component ('use client')."
42
+ ]
43
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "drizzle",
4
+ "description": "Drizzle ORM + drizzle-kit (Postgres / node-postgres). DB client, schema barrel, cursor (keyset) pagination helpers and a seed harness.",
5
+ "framework": "tanstack-start",
6
+ "detect": {
7
+ "deps": [
8
+ "drizzle-orm"
9
+ ],
10
+ "files": [
11
+ "drizzle.config.ts"
12
+ ]
13
+ },
14
+ "dependsOn": [],
15
+ "deps": [
16
+ "drizzle-orm",
17
+ "pg"
18
+ ],
19
+ "devDeps": [
20
+ "drizzle-kit",
21
+ "dotenv",
22
+ "tsx",
23
+ "@types/pg",
24
+ "@faker-js/faker"
25
+ ],
26
+ "env": [
27
+ "DATABASE_URL"
28
+ ],
29
+ "scripts": {
30
+ "db:generate": "drizzle-kit generate",
31
+ "db:migrate": "drizzle-kit migrate",
32
+ "db:push": "drizzle-kit push",
33
+ "db:studio": "drizzle-kit studio",
34
+ "db:seed": "tsx src/server/db/seed.ts"
35
+ },
36
+ "files": [
37
+ {
38
+ "from": "apps/tanstack-base/drizzle.config.ts",
39
+ "to": "drizzle.config.ts"
40
+ },
41
+ {
42
+ "from": "apps/tanstack-base/src/server/db/index.ts",
43
+ "to": "<srcRoot>/server/db/index.ts"
44
+ },
45
+ {
46
+ "from": "apps/tanstack-base/src/server/db/keyset.ts",
47
+ "to": "<srcRoot>/server/db/keyset.ts"
48
+ },
49
+ {
50
+ "from": "apps/tanstack-base/src/server/db/seed.ts",
51
+ "to": "<srcRoot>/server/db/seed.ts"
52
+ },
53
+ {
54
+ "from": "apps/tanstack-base/src/server/db/schemas/index.ts",
55
+ "to": "<srcRoot>/server/db/schemas/index.ts"
56
+ }
57
+ ],
58
+ "notes": [
59
+ "drizzle.config.ts globs ./src/server/db/schemas/*.schema.ts — adjust the path if the project's source root is not ./src."
60
+ ]
61
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "trpc",
4
+ "description": "tRPC v11 + TanStack React Query. Context with session injection, public/protected procedures, valibot error formatter, batch-stream client, SSR caller and a fetch route handler.",
5
+ "framework": "tanstack-start",
6
+ "detect": {
7
+ "deps": [
8
+ "@trpc/server"
9
+ ],
10
+ "files": []
11
+ },
12
+ "dependsOn": [
13
+ "drizzle"
14
+ ],
15
+ "integratesWith": [
16
+ "better-auth"
17
+ ],
18
+ "deps": [
19
+ "@trpc/server",
20
+ "@trpc/client",
21
+ "@trpc/tanstack-react-query",
22
+ "@tanstack/react-query",
23
+ "superjson",
24
+ "valibot"
25
+ ],
26
+ "env": [],
27
+ "files": [
28
+ {
29
+ "from": "apps/tanstack-base/src/server/api/trpc.ts",
30
+ "to": "<srcRoot>/server/api/trpc.ts"
31
+ },
32
+ {
33
+ "from": "apps/tanstack-base/src/server/api/root.ts",
34
+ "to": "<srcRoot>/server/api/root.ts"
35
+ },
36
+ {
37
+ "from": "apps/tanstack-base/src/server/api/routers/health.router.ts",
38
+ "to": "<srcRoot>/server/api/routers/health.router.ts"
39
+ },
40
+ {
41
+ "from": "apps/tanstack-base/src/routes/api.trpc.$.tsx",
42
+ "to": "<srcRoot>/routes/api.trpc.$.tsx"
43
+ },
44
+ {
45
+ "from": "apps/tanstack-base/src/trpc/react.tsx",
46
+ "to": "<srcRoot>/trpc/react.tsx"
47
+ },
48
+ {
49
+ "from": "apps/tanstack-base/src/trpc/query-client.ts",
50
+ "to": "<srcRoot>/trpc/query-client.ts"
51
+ },
52
+ {
53
+ "from": "apps/tanstack-base/src/trpc/server.ts",
54
+ "to": "<srcRoot>/trpc/server.ts"
55
+ }
56
+ ],
57
+ "notes": [
58
+ "trpc.ts integrates with better-auth: it imports `auth` and injects `session` into the context. If the better-auth pattern is NOT opt-in, strip those imports, the `session` from the context, and protectedProcedure — keep publicProcedure + the timing middleware.",
59
+ "Wire TRPCReactProvider + createQueryClient into the app's root route, and getServerHelpers for SSR preloading (see tanstack-base's router for the exact root wiring)."
60
+ ]
61
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "../../pattern.schema.json",
3
+ "name": "trpc-next",
4
+ "description": "tRPC v11 for Next.js App Router — the classic `api.x.useQuery` / `useMutation` hooks (createTRPCReact) plus RSC hydration (createHydrationHelpers). The Next counterpart of the `trpc` pattern.",
5
+ "framework": "next",
6
+ "detect": {
7
+ "deps": [
8
+ "@trpc/react-query"
9
+ ],
10
+ "files": []
11
+ },
12
+ "dependsOn": [
13
+ "drizzle"
14
+ ],
15
+ "integratesWith": [
16
+ "better-auth-next"
17
+ ],
18
+ "deps": [
19
+ "@trpc/server",
20
+ "@trpc/client",
21
+ "@trpc/react-query",
22
+ "@tanstack/react-query",
23
+ "superjson",
24
+ "valibot"
25
+ ],
26
+ "env": [],
27
+ "files": [
28
+ {
29
+ "from": "apps/next-base/src/server/api/trpc.ts",
30
+ "to": "<srcRoot>/server/api/trpc.ts"
31
+ },
32
+ {
33
+ "from": "apps/next-base/src/server/api/root.ts",
34
+ "to": "<srcRoot>/server/api/root.ts"
35
+ },
36
+ {
37
+ "from": "apps/next-base/src/server/api/routers/health.router.ts",
38
+ "to": "<srcRoot>/server/api/routers/health.router.ts"
39
+ },
40
+ {
41
+ "from": "apps/next-base/src/trpc/react.tsx",
42
+ "to": "<srcRoot>/trpc/react.tsx"
43
+ },
44
+ {
45
+ "from": "apps/next-base/src/trpc/query-client.ts",
46
+ "to": "<srcRoot>/trpc/query-client.ts"
47
+ },
48
+ {
49
+ "from": "apps/next-base/src/trpc/server.ts",
50
+ "to": "<srcRoot>/trpc/server.ts"
51
+ },
52
+ {
53
+ "from": "apps/next-base/src/app/api/trpc/[trpc]/route.ts",
54
+ "to": "<srcRoot>/app/api/trpc/[trpc]/route.ts"
55
+ }
56
+ ],
57
+ "wiring": [
58
+ "Wrap the app in <TRPCReactProvider> in app/layout.tsx (it's a client component)."
59
+ ],
60
+ "notes": [
61
+ "Client usage: `api.<router>.<proc>.useQuery()` / `.useMutation()` from ~/trpc/react. RSC usage: `import { api, HydrateClient } from '~/trpc/server'` for prefetch + hydrate.",
62
+ "trpc.ts context calls better-auth getSession; if better-auth-next is NOT opt-in, strip the auth import, `session` from the context, and protectedProcedure."
63
+ ]
64
+ }
package/index.mjs ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ // create-stack — deterministic installer for the personal reference stack.
3
+ // The non-LLM counterpart of the bootstrap skill's CREATE mode: fork a base
4
+ // app, strip it to the selection, stamp identity, verify.
5
+ //
6
+ // Interactive by default. Non-interactive when any selection flag (or --yes) is
7
+ // passed — useful for scripts/CI and for headless end-to-end testing:
8
+ // create-stack my-app --framework next --foundations drizzle,trpc --mailer ses --no-install
9
+
10
+ import { resolve } from 'node:path'
11
+ import * as p from '@clack/prompts'
12
+ import { buildProject } from './lib/build.mjs'
13
+ import { loadCapabilities, loadPatterns } from './lib/manifests.mjs'
14
+ import { isDirEmpty, run } from './lib/util.mjs'
15
+
16
+ // Capabilities baked into the base apps vs. the ones add-capability supplies.
17
+ const BAKED_CAPS = new Set(['mailer', 'email-kit'])
18
+ const ALL_FOUNDATIONS = ['drizzle', 'trpc', 'better-auth', 'data-table']
19
+
20
+ const cancelled = (v) => {
21
+ if (p.isCancel(v)) {
22
+ p.cancel('Aborted.')
23
+ process.exit(0)
24
+ }
25
+ return v
26
+ }
27
+
28
+ /** Minimal flag parser: positional dir + --key value / --flag. */
29
+ function parseArgs(argv) {
30
+ const out = { _: [], flags: {} }
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const a = argv[i]
33
+ if (a.startsWith('--')) {
34
+ const key = a.slice(2)
35
+ const next = argv[i + 1]
36
+ if (next && !next.startsWith('--')) {
37
+ out.flags[key] = next
38
+ i++
39
+ } else {
40
+ out.flags[key] = true
41
+ }
42
+ } else {
43
+ out._.push(a)
44
+ }
45
+ }
46
+ return out
47
+ }
48
+
49
+ const csv = (v) =>
50
+ typeof v === 'string'
51
+ ? v
52
+ .split(',')
53
+ .map((s) => s.trim())
54
+ .filter(Boolean)
55
+ : []
56
+
57
+ /** Resolve hard deps + the mailer's better-auth requirement. */
58
+ function normalize(picked, mailer) {
59
+ const kept = new Set(picked.filter((f) => ALL_FOUNDATIONS.includes(f)))
60
+ if (kept.has('trpc') || kept.has('better-auth')) kept.add('drizzle')
61
+ let mailerProvider = mailer ?? 'resend'
62
+ if (kept.has('better-auth') && mailerProvider === 'none') mailerProvider = 'resend'
63
+ return { kept, mailerProvider }
64
+ }
65
+
66
+ function collectFromFlags(args, capabilities) {
67
+ const argDir = args._[0]
68
+ if (!argDir) throw new Error('Project name is required (positional) in non-interactive mode')
69
+ const framework = args.flags.framework === 'next' ? 'next' : 'tanstack'
70
+ const picked = args.flags.foundations ? csv(args.flags.foundations) : [...ALL_FOUNDATIONS]
71
+ const { kept, mailerProvider } = normalize(picked, args.flags.mailer)
72
+ const validCaps = new Set(Object.keys(capabilities).filter((c) => !BAKED_CAPS.has(c)))
73
+ const extraCaps = csv(args.flags.caps).filter((c) => validCaps.has(c))
74
+ const doInstall = !args.flags['no-install']
75
+ return { argDir, projectName: argDir, framework, kept, mailerProvider, extraCaps, doInstall }
76
+ }
77
+
78
+ async function collectFromPrompts(argDir, capabilities) {
79
+ p.intro('create-stack — fork a base app, strip it to your selection')
80
+
81
+ const name = cancelled(
82
+ await p.text({
83
+ message: 'Project name',
84
+ placeholder: 'my-app',
85
+ initialValue: argDir ?? '',
86
+ validate: (v) => (v?.trim() ? undefined : 'Required'),
87
+ }),
88
+ )
89
+ const projectName = name.trim()
90
+
91
+ const framework = cancelled(
92
+ await p.select({
93
+ message: 'Framework',
94
+ options: [
95
+ { value: 'tanstack', label: 'TanStack Start' },
96
+ { value: 'next', label: 'Next.js (App Router)' },
97
+ ],
98
+ }),
99
+ )
100
+
101
+ const picked = cancelled(
102
+ await p.multiselect({
103
+ message: 'Foundations (space to toggle)',
104
+ required: false,
105
+ initialValues: [...ALL_FOUNDATIONS],
106
+ options: [
107
+ { value: 'drizzle', label: 'Drizzle ORM', hint: 'Postgres + seed' },
108
+ { value: 'trpc', label: 'tRPC', hint: 'needs Drizzle' },
109
+ { value: 'better-auth', label: 'better-auth', hint: 'needs Drizzle + mailer' },
110
+ { value: 'data-table', label: 'Data tables', hint: 'TanStack Table' },
111
+ ],
112
+ }),
113
+ )
114
+ const authKept = new Set(picked).has('better-auth')
115
+
116
+ const mailerOpts = [
117
+ { value: 'resend', label: 'Resend' },
118
+ { value: 'brevo', label: 'Brevo' },
119
+ { value: 'ses', label: 'Amazon SES' },
120
+ ]
121
+ if (!authKept) mailerOpts.push({ value: 'none', label: 'None' })
122
+ const mailer = cancelled(
123
+ await p.select({
124
+ message: authKept ? 'Mailer provider (required by better-auth)' : 'Mailer provider',
125
+ options: mailerOpts,
126
+ initialValue: 'resend',
127
+ }),
128
+ )
129
+
130
+ const extraCaps = cancelled(
131
+ await p.multiselect({
132
+ message: 'Extra capabilities (optional — added via add-capability)',
133
+ required: false,
134
+ initialValues: [],
135
+ options: Object.keys(capabilities)
136
+ .filter((c) => !BAKED_CAPS.has(c))
137
+ .map((c) => ({ value: c, label: c, hint: capabilities[c].description?.slice(0, 40) })),
138
+ }),
139
+ )
140
+
141
+ const doInstall = cancelled(
142
+ await p.confirm({ message: 'Install dependencies and verify now?', initialValue: true }),
143
+ )
144
+
145
+ const { kept, mailerProvider } = normalize(picked, mailer)
146
+ return { argDir, projectName, framework, kept, mailerProvider, extraCaps, doInstall }
147
+ }
148
+
149
+ function execute(a, patterns) {
150
+ const projectDir = resolve(process.cwd(), a.argDir ?? a.projectName)
151
+ if (!isDirEmpty(projectDir)) {
152
+ p.cancel(`Target directory is not empty: ${projectDir}`)
153
+ process.exit(1)
154
+ }
155
+
156
+ const s = p.spinner()
157
+ s.start('Forking + stripping the base app')
158
+ buildProject({ ...a, projectDir, patterns })
159
+ s.stop('Project scaffolded')
160
+
161
+ if (a.doInstall) {
162
+ p.log.step('pnpm install')
163
+ run('pnpm', ['install'], { cwd: projectDir })
164
+ p.log.step('Verifying (typecheck + biome)')
165
+ const tc = run('pnpm', ['--config.verify-deps-before-run=false', 'run', 'typecheck'], {
166
+ cwd: projectDir,
167
+ })
168
+ const lint = run('pnpm', ['--config.verify-deps-before-run=false', 'run', 'check'], {
169
+ cwd: projectDir,
170
+ })
171
+ p.log[tc && lint ? 'success' : 'warn'](
172
+ tc && lint ? 'typecheck + biome clean' : 'verify reported issues (see output above)',
173
+ )
174
+ }
175
+
176
+ const keptMailer = a.mailerProvider !== 'none'
177
+ const lines = [
178
+ `Framework: ${a.framework === 'next' ? 'Next.js' : 'TanStack Start'}`,
179
+ `Foundations: ${[...a.kept].sort().join(', ') || '(none)'}`,
180
+ `Mailer: ${keptMailer ? a.mailerProvider : '(none)'}`,
181
+ ]
182
+ if (a.extraCaps.length) {
183
+ lines.push(
184
+ '',
185
+ 'Add the extra capabilities with the add-capability skill:',
186
+ ...a.extraCaps.map((c) => ` • ${c}`),
187
+ )
188
+ }
189
+ lines.push('', 'Next:', ` cd ${a.argDir ?? a.projectName}`)
190
+ if (!a.doInstall) lines.push(' pnpm install')
191
+ lines.push(' cp .env.example .env # fill in the values', ' pnpm dev')
192
+ p.note(lines.join('\n'), 'Done')
193
+ p.outro(`Created ${a.projectName}`)
194
+ }
195
+
196
+ async function main() {
197
+ const patterns = loadPatterns()
198
+ const capabilities = loadCapabilities()
199
+ const args = parseArgs(process.argv.slice(2))
200
+
201
+ const nonInteractive =
202
+ args.flags.yes ||
203
+ args.flags.y ||
204
+ ['framework', 'foundations', 'mailer', 'caps', 'no-install'].some((k) => k in args.flags)
205
+
206
+ const answers = nonInteractive
207
+ ? collectFromFlags(args, capabilities)
208
+ : await collectFromPrompts(args._[0], capabilities)
209
+
210
+ execute(answers, patterns)
211
+ }
212
+
213
+ main().catch((err) => {
214
+ p.log.error(String(err?.stack || err))
215
+ process.exit(1)
216
+ })
package/lib/build.mjs ADDED
@@ -0,0 +1,64 @@
1
+ // Pure build phase (no prompts, no install) — fork → strip → mailer → env →
2
+ // identity. Shared by index.mjs (after the wizard) and the test harness.
3
+
4
+ import { writeEnv } from './env.mjs'
5
+ import { stampIdentity } from './identity.mjs'
6
+ import { swapMailer } from './mailer.mjs'
7
+ import { forkBase, makeStandalone } from './scaffold.mjs'
8
+ import { stripFoundations } from './strip.mjs'
9
+ import { join, pkgAddDeps, pkgRemoveDeps, pkgRemoveScripts, readJSON, writeJSON } from './util.mjs'
10
+
11
+ /**
12
+ * @param {object} o
13
+ * @param {string} o.projectDir absolute target dir (must be empty)
14
+ * @param {string} o.projectName
15
+ * @param {'next'|'tanstack'} o.framework
16
+ * @param {Set<string>} o.kept logical foundations to keep (deps pre-resolved)
17
+ * @param {'resend'|'brevo'|'ses'|'none'} o.mailerProvider
18
+ * @param {object} o.patterns loadPatterns()
19
+ * @returns {{ kept: string[], keptMailer: boolean, mailerProvider: string, envKeys: string[] }}
20
+ */
21
+ export function buildProject({
22
+ projectDir,
23
+ projectName,
24
+ framework,
25
+ kept,
26
+ mailerProvider,
27
+ patterns,
28
+ }) {
29
+ const authKept = kept.has('better-auth')
30
+ const keptMailer = mailerProvider !== 'none'
31
+
32
+ forkBase(framework, projectDir)
33
+ makeStandalone(projectDir, projectName)
34
+
35
+ const strip = stripFoundations({ projectDir, framework, kept, keptMailer, patterns })
36
+ const mailer = keptMailer
37
+ ? swapMailer(projectDir, mailerProvider)
38
+ : { addDeps: {}, removeDeps: [], envKeys: [] }
39
+
40
+ const pkgPath = join(projectDir, 'package.json')
41
+ const pkg = readJSON(pkgPath)
42
+ pkg.description = `${projectName} — bootstrapped from the personal reference stack.`
43
+ pkgRemoveDeps(pkg, [...strip.removeDeps, ...mailer.removeDeps])
44
+ pkgRemoveScripts(pkg, strip.removeScripts)
45
+ pkgAddDeps(pkg, mailer.addDeps)
46
+ writeJSON(pkgPath, pkg)
47
+
48
+ const envKeys = []
49
+ if (kept.has('drizzle')) envKeys.push('DATABASE_URL')
50
+ if (authKept) {
51
+ envKeys.push(
52
+ 'BETTER_AUTH_URL',
53
+ 'BETTER_AUTH_SECRET',
54
+ 'BETTER_AUTH_GOOGLE_CLIENT_ID',
55
+ 'BETTER_AUTH_GOOGLE_CLIENT_SECRET',
56
+ )
57
+ }
58
+ envKeys.push(...mailer.envKeys)
59
+ writeEnv(projectDir, envKeys)
60
+
61
+ stampIdentity(projectDir, projectName, framework)
62
+
63
+ return { kept: [...kept], keptMailer, mailerProvider, envKeys }
64
+ }