create-better-t-stack 3.7.1 → 3.7.3-canary.53fc0031

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 (47) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +108 -250
  3. package/dist/index.js +1 -1
  4. package/dist/{src-BmyCNYPk.js → src-B1ogWZqk.js} +205 -368
  5. package/package.json +3 -3
  6. package/templates/api/orpc/server/package.json.hbs +0 -3
  7. package/templates/api/trpc/server/package.json.hbs +1 -4
  8. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +0 -2
  9. package/templates/auth/better-auth/server/base/package.json.hbs +1 -4
  10. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -2
  11. package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-in-form.tsx +117 -121
  12. package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-up-form.tsx +141 -145
  13. package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-in-form.tsx +117 -121
  14. package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-up-form.tsx +141 -145
  15. package/templates/auth/better-auth/web/solid/src/components/sign-in-form.tsx +3 -11
  16. package/templates/auth/better-auth/web/solid/src/components/sign-up-form.tsx +4 -14
  17. package/templates/backend/convex/packages/backend/convex/healthCheck.ts +2 -2
  18. package/templates/backend/convex/packages/backend/package.json.hbs +1 -2
  19. package/templates/backend/server/base/package.json.hbs +1 -3
  20. package/templates/db/base/package.json.hbs +1 -4
  21. package/templates/examples/ai/native/bare/polyfills.js +3 -7
  22. package/templates/examples/ai/native/unistyles/polyfills.js +3 -6
  23. package/templates/examples/ai/native/uniwind/polyfills.js +3 -7
  24. package/templates/examples/todo/server/drizzle/postgres/src/schema/todo.ts +1 -1
  25. package/templates/frontend/native/bare/package.json.hbs +1 -2
  26. package/templates/frontend/native/unistyles/package.json.hbs +1 -2
  27. package/templates/frontend/native/uniwind/package.json.hbs +1 -2
  28. package/templates/frontend/nuxt/package.json.hbs +1 -3
  29. package/templates/frontend/react/next/package.json.hbs +1 -3
  30. package/templates/frontend/react/react-router/package.json.hbs +1 -3
  31. package/templates/frontend/react/react-router/src/components/mode-toggle.tsx +3 -9
  32. package/templates/frontend/react/tanstack-router/package.json.hbs +1 -3
  33. package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx +3 -9
  34. package/templates/frontend/react/tanstack-start/package.json.hbs +1 -3
  35. package/templates/frontend/react/web-base/src/components/ui/button.tsx +13 -16
  36. package/templates/frontend/react/web-base/src/components/ui/card.tsx +13 -30
  37. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +8 -11
  38. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +37 -66
  39. package/templates/frontend/react/web-base/src/components/ui/input.tsx +5 -5
  40. package/templates/frontend/react/web-base/src/components/ui/label.tsx +7 -10
  41. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +3 -3
  42. package/templates/frontend/react/web-base/src/lib/utils.ts +3 -3
  43. package/templates/frontend/solid/package.json.hbs +1 -3
  44. package/templates/frontend/svelte/package.json.hbs +1 -3
  45. package/templates/frontend/svelte/src/app.d.ts +7 -7
  46. package/templates/frontend/svelte/src/lib/index.ts +1 -0
  47. package/templates/frontend/svelte/vite.config.ts +4 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.7.1",
3
+ "version": "3.7.3-canary.53fc0031",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -51,7 +51,6 @@
51
51
  "build": "tsdown --publint",
52
52
  "dev": "tsdown --watch",
53
53
  "check-types": "tsc --noEmit",
54
- "check": "biome check --write .",
55
54
  "test": "bun run build && vitest run; rm -rf .smoke || true",
56
55
  "test:ui": "bun run build && vitest --ui",
57
56
  "prepublishOnly": "npm run build"
@@ -66,6 +65,7 @@
66
65
  }
67
66
  },
68
67
  "dependencies": {
68
+ "@create-better-t-stack/types": "1.0.0",
69
69
  "@biomejs/js-api": "^3.0.0",
70
70
  "@biomejs/wasm-nodejs": "^2.2.6",
71
71
  "@clack/prompts": "^1.0.0-alpha.6",
@@ -81,7 +81,7 @@
81
81
  "trpc-cli": "^0.12.0",
82
82
  "ts-morph": "^27.0.2",
83
83
  "yaml": "^2.8.1",
84
- "zod": "^4.1.12"
84
+ "zod": "^4.1.13"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@types/fs-extra": "^11.0.4",
@@ -11,8 +11,5 @@
11
11
  "type": "module",
12
12
  "scripts": {},
13
13
  "devDependencies": {},
14
- "peerDependencies": {
15
- "typescript": "^5"
16
- },
17
14
  "dependencies": {}
18
15
  }
@@ -10,8 +10,5 @@
10
10
  },
11
11
  "type": "module",
12
12
  "scripts": {},
13
- "devDependencies": {},
14
- "peerDependencies": {
15
- "typescript": "^5"
16
- }
13
+ "devDependencies": {}
17
14
  }
@@ -1,5 +1,4 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
- import { anonymousClient } from "better-auth/client/plugins";
3
2
  import { convexClient } from "@convex-dev/better-auth/client/plugins";
4
3
  import { expoClient } from "@better-auth/expo/client";
5
4
  import Constants from "expo-constants";
@@ -8,7 +7,6 @@ import * as SecureStore from "expo-secure-store";
8
7
  export const authClient = createAuthClient({
9
8
  baseURL: process.env.EXPO_PUBLIC_CONVEX_SITE_URL,
10
9
  plugins: [
11
- anonymousClient(),
12
10
  expoClient({
13
11
  scheme: Constants.expoConfig?.scheme as string,
14
12
  storagePrefix: Constants.expoConfig?.scheme as string,
@@ -10,8 +10,5 @@
10
10
  },
11
11
  "type": "module",
12
12
  "scripts": {},
13
- "devDependencies": {},
14
- "peerDependencies": {
15
- "typescript": "^5"
16
- }
13
+ "devDependencies": {}
17
14
  }
@@ -1,6 +1,8 @@
1
1
  import type { auth } from "@{{projectName}}/auth";
2
2
  import { createAuthClient } from "better-auth/react";
3
- import { inferAdditionalFields } from "better-auth/client/plugins";
3
+ {{#if (eq payments "polar")}}
4
+ import { polarClient } from "@polar-sh/better-auth";
5
+ {{/if}}
4
6
 
5
7
  export const authClient = createAuthClient({
6
8
  {{#unless (eq backend "self")}}
@@ -11,5 +13,7 @@ export const authClient = createAuthClient({
11
13
  import.meta.env.VITE_SERVER_URL,
12
14
  {{/if}}
13
15
  {{/unless}}
14
- plugins: [inferAdditionalFields<typeof auth>()],
16
+ {{#if (eq payments "polar")}}
17
+ plugins: [polarClient()]
18
+ {{/if}}
15
19
  });
@@ -8,132 +8,128 @@ import { Button } from "./ui/button";
8
8
  import { Input } from "./ui/input";
9
9
  import { Label } from "./ui/label";
10
10
 
11
- export default function SignInForm({
12
- onSwitchToSignUp,
13
- }: {
14
- onSwitchToSignUp: () => void;
15
- }) {
16
- const navigate = useNavigate({
17
- from: "/",
18
- });
19
- const { isPending } = authClient.useSession();
11
+ export default function SignInForm({ onSwitchToSignUp }: { onSwitchToSignUp: () => void }) {
12
+ const navigate = useNavigate({
13
+ from: "/",
14
+ });
15
+ const { isPending } = authClient.useSession();
20
16
 
21
- const form = useForm({
22
- defaultValues: {
23
- email: "",
24
- password: "",
25
- },
26
- onSubmit: async ({ value }) => {
27
- await authClient.signIn.email(
28
- {
29
- email: value.email,
30
- password: value.password,
31
- },
32
- {
33
- onSuccess: () => {
34
- navigate({
35
- to: "/dashboard",
36
- });
37
- toast.success("Sign in successful");
38
- },
39
- onError: (error) => {
40
- toast.error(error.error.message || error.error.statusText);
41
- },
42
- },
43
- );
44
- },
45
- validators: {
46
- onSubmit: z.object({
47
- email: z.email("Invalid email address"),
48
- password: z.string().min(8, "Password must be at least 8 characters"),
49
- }),
50
- },
51
- });
17
+ const form = useForm({
18
+ defaultValues: {
19
+ email: "",
20
+ password: "",
21
+ },
22
+ onSubmit: async ({ value }) => {
23
+ await authClient.signIn.email(
24
+ {
25
+ email: value.email,
26
+ password: value.password,
27
+ },
28
+ {
29
+ onSuccess: () => {
30
+ navigate({
31
+ to: "/dashboard",
32
+ });
33
+ toast.success("Sign in successful");
34
+ },
35
+ onError: (error) => {
36
+ toast.error(error.error.message || error.error.statusText);
37
+ },
38
+ },
39
+ );
40
+ },
41
+ validators: {
42
+ onSubmit: z.object({
43
+ email: z.email("Invalid email address"),
44
+ password: z.string().min(8, "Password must be at least 8 characters"),
45
+ }),
46
+ },
47
+ });
52
48
 
53
- if (isPending) {
54
- return <Loader />;
55
- }
49
+ if (isPending) {
50
+ return <Loader />;
51
+ }
56
52
 
57
- return (
58
- <div className="mx-auto w-full mt-10 max-w-md p-6">
59
- <h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
53
+ return (
54
+ <div className="mx-auto w-full mt-10 max-w-md p-6">
55
+ <h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
60
56
 
61
- <form
62
- onSubmit={(e) => {
63
- e.preventDefault();
64
- e.stopPropagation();
65
- form.handleSubmit();
66
- }}
67
- className="space-y-4"
68
- >
69
- <div>
70
- <form.Field name="email">
71
- {(field) => (
72
- <div className="space-y-2">
73
- <Label htmlFor={field.name}>Email</Label>
74
- <Input
75
- id={field.name}
76
- name={field.name}
77
- type="email"
78
- value={field.state.value}
79
- onBlur={field.handleBlur}
80
- onChange={(e) => field.handleChange(e.target.value)}
81
- />
82
- {field.state.meta.errors.map((error) => (
83
- <p key={error?.message} className="text-red-500">
84
- {error?.message}
85
- </p>
86
- ))}
87
- </div>
88
- )}
89
- </form.Field>
90
- </div>
57
+ <form
58
+ onSubmit={(e) => {
59
+ e.preventDefault();
60
+ e.stopPropagation();
61
+ form.handleSubmit();
62
+ }}
63
+ className="space-y-4"
64
+ >
65
+ <div>
66
+ <form.Field name="email">
67
+ {(field) => (
68
+ <div className="space-y-2">
69
+ <Label htmlFor={field.name}>Email</Label>
70
+ <Input
71
+ id={field.name}
72
+ name={field.name}
73
+ type="email"
74
+ value={field.state.value}
75
+ onBlur={field.handleBlur}
76
+ onChange={(e) => field.handleChange(e.target.value)}
77
+ />
78
+ {field.state.meta.errors.map((error) => (
79
+ <p key={error?.message} className="text-red-500">
80
+ {error?.message}
81
+ </p>
82
+ ))}
83
+ </div>
84
+ )}
85
+ </form.Field>
86
+ </div>
91
87
 
92
- <div>
93
- <form.Field name="password">
94
- {(field) => (
95
- <div className="space-y-2">
96
- <Label htmlFor={field.name}>Password</Label>
97
- <Input
98
- id={field.name}
99
- name={field.name}
100
- type="password"
101
- value={field.state.value}
102
- onBlur={field.handleBlur}
103
- onChange={(e) => field.handleChange(e.target.value)}
104
- />
105
- {field.state.meta.errors.map((error) => (
106
- <p key={error?.message} className="text-red-500">
107
- {error?.message}
108
- </p>
109
- ))}
110
- </div>
111
- )}
112
- </form.Field>
113
- </div>
88
+ <div>
89
+ <form.Field name="password">
90
+ {(field) => (
91
+ <div className="space-y-2">
92
+ <Label htmlFor={field.name}>Password</Label>
93
+ <Input
94
+ id={field.name}
95
+ name={field.name}
96
+ type="password"
97
+ value={field.state.value}
98
+ onBlur={field.handleBlur}
99
+ onChange={(e) => field.handleChange(e.target.value)}
100
+ />
101
+ {field.state.meta.errors.map((error) => (
102
+ <p key={error?.message} className="text-red-500">
103
+ {error?.message}
104
+ </p>
105
+ ))}
106
+ </div>
107
+ )}
108
+ </form.Field>
109
+ </div>
114
110
 
115
- <form.Subscribe>
116
- {(state) => (
117
- <Button
118
- type="submit"
119
- className="w-full"
120
- disabled={!state.canSubmit || state.isSubmitting}
121
- >
122
- {state.isSubmitting ? "Submitting..." : "Sign In"}
123
- </Button>
124
- )}
125
- </form.Subscribe>
126
- </form>
111
+ <form.Subscribe>
112
+ {(state) => (
113
+ <Button
114
+ type="submit"
115
+ className="w-full"
116
+ disabled={!state.canSubmit || state.isSubmitting}
117
+ >
118
+ {state.isSubmitting ? "Submitting..." : "Sign In"}
119
+ </Button>
120
+ )}
121
+ </form.Subscribe>
122
+ </form>
127
123
 
128
- <div className="mt-4 text-center">
129
- <Button
130
- variant="link"
131
- onClick={onSwitchToSignUp}
132
- className="text-indigo-600 hover:text-indigo-800"
133
- >
134
- Need an account? Sign Up
135
- </Button>
136
- </div>
137
- </div>
138
- );
124
+ <div className="mt-4 text-center">
125
+ <Button
126
+ variant="link"
127
+ onClick={onSwitchToSignUp}
128
+ className="text-indigo-600 hover:text-indigo-800"
129
+ >
130
+ Need an account? Sign Up
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ );
139
135
  }
@@ -8,157 +8,153 @@ import { Button } from "./ui/button";
8
8
  import { Input } from "./ui/input";
9
9
  import { Label } from "./ui/label";
10
10
 
11
- export default function SignUpForm({
12
- onSwitchToSignIn,
13
- }: {
14
- onSwitchToSignIn: () => void;
15
- }) {
16
- const navigate = useNavigate({
17
- from: "/",
18
- });
19
- const { isPending } = authClient.useSession();
11
+ export default function SignUpForm({ onSwitchToSignIn }: { onSwitchToSignIn: () => void }) {
12
+ const navigate = useNavigate({
13
+ from: "/",
14
+ });
15
+ const { isPending } = authClient.useSession();
20
16
 
21
- const form = useForm({
22
- defaultValues: {
23
- email: "",
24
- password: "",
25
- name: "",
26
- },
27
- onSubmit: async ({ value }) => {
28
- await authClient.signUp.email(
29
- {
30
- email: value.email,
31
- password: value.password,
32
- name: value.name,
33
- },
34
- {
35
- onSuccess: () => {
36
- navigate({
37
- to: "/dashboard",
38
- });
39
- toast.success("Sign up successful");
40
- },
41
- onError: (error) => {
42
- toast.error(error.error.message || error.error.statusText);
43
- },
44
- },
45
- );
46
- },
47
- validators: {
48
- onSubmit: z.object({
49
- name: z.string().min(2, "Name must be at least 2 characters"),
50
- email: z.email("Invalid email address"),
51
- password: z.string().min(8, "Password must be at least 8 characters"),
52
- }),
53
- },
54
- });
17
+ const form = useForm({
18
+ defaultValues: {
19
+ email: "",
20
+ password: "",
21
+ name: "",
22
+ },
23
+ onSubmit: async ({ value }) => {
24
+ await authClient.signUp.email(
25
+ {
26
+ email: value.email,
27
+ password: value.password,
28
+ name: value.name,
29
+ },
30
+ {
31
+ onSuccess: () => {
32
+ navigate({
33
+ to: "/dashboard",
34
+ });
35
+ toast.success("Sign up successful");
36
+ },
37
+ onError: (error) => {
38
+ toast.error(error.error.message || error.error.statusText);
39
+ },
40
+ },
41
+ );
42
+ },
43
+ validators: {
44
+ onSubmit: z.object({
45
+ name: z.string().min(2, "Name must be at least 2 characters"),
46
+ email: z.email("Invalid email address"),
47
+ password: z.string().min(8, "Password must be at least 8 characters"),
48
+ }),
49
+ },
50
+ });
55
51
 
56
- if (isPending) {
57
- return <Loader />;
58
- }
52
+ if (isPending) {
53
+ return <Loader />;
54
+ }
59
55
 
60
- return (
61
- <div className="mx-auto w-full mt-10 max-w-md p-6">
62
- <h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
56
+ return (
57
+ <div className="mx-auto w-full mt-10 max-w-md p-6">
58
+ <h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
63
59
 
64
- <form
65
- onSubmit={(e) => {
66
- e.preventDefault();
67
- e.stopPropagation();
68
- form.handleSubmit();
69
- }}
70
- className="space-y-4"
71
- >
72
- <div>
73
- <form.Field name="name">
74
- {(field) => (
75
- <div className="space-y-2">
76
- <Label htmlFor={field.name}>Name</Label>
77
- <Input
78
- id={field.name}
79
- name={field.name}
80
- value={field.state.value}
81
- onBlur={field.handleBlur}
82
- onChange={(e) => field.handleChange(e.target.value)}
83
- />
84
- {field.state.meta.errors.map((error) => (
85
- <p key={error?.message} className="text-red-500">
86
- {error?.message}
87
- </p>
88
- ))}
89
- </div>
90
- )}
91
- </form.Field>
92
- </div>
60
+ <form
61
+ onSubmit={(e) => {
62
+ e.preventDefault();
63
+ e.stopPropagation();
64
+ form.handleSubmit();
65
+ }}
66
+ className="space-y-4"
67
+ >
68
+ <div>
69
+ <form.Field name="name">
70
+ {(field) => (
71
+ <div className="space-y-2">
72
+ <Label htmlFor={field.name}>Name</Label>
73
+ <Input
74
+ id={field.name}
75
+ name={field.name}
76
+ value={field.state.value}
77
+ onBlur={field.handleBlur}
78
+ onChange={(e) => field.handleChange(e.target.value)}
79
+ />
80
+ {field.state.meta.errors.map((error) => (
81
+ <p key={error?.message} className="text-red-500">
82
+ {error?.message}
83
+ </p>
84
+ ))}
85
+ </div>
86
+ )}
87
+ </form.Field>
88
+ </div>
93
89
 
94
- <div>
95
- <form.Field name="email">
96
- {(field) => (
97
- <div className="space-y-2">
98
- <Label htmlFor={field.name}>Email</Label>
99
- <Input
100
- id={field.name}
101
- name={field.name}
102
- type="email"
103
- value={field.state.value}
104
- onBlur={field.handleBlur}
105
- onChange={(e) => field.handleChange(e.target.value)}
106
- />
107
- {field.state.meta.errors.map((error) => (
108
- <p key={error?.message} className="text-red-500">
109
- {error?.message}
110
- </p>
111
- ))}
112
- </div>
113
- )}
114
- </form.Field>
115
- </div>
90
+ <div>
91
+ <form.Field name="email">
92
+ {(field) => (
93
+ <div className="space-y-2">
94
+ <Label htmlFor={field.name}>Email</Label>
95
+ <Input
96
+ id={field.name}
97
+ name={field.name}
98
+ type="email"
99
+ value={field.state.value}
100
+ onBlur={field.handleBlur}
101
+ onChange={(e) => field.handleChange(e.target.value)}
102
+ />
103
+ {field.state.meta.errors.map((error) => (
104
+ <p key={error?.message} className="text-red-500">
105
+ {error?.message}
106
+ </p>
107
+ ))}
108
+ </div>
109
+ )}
110
+ </form.Field>
111
+ </div>
116
112
 
117
- <div>
118
- <form.Field name="password">
119
- {(field) => (
120
- <div className="space-y-2">
121
- <Label htmlFor={field.name}>Password</Label>
122
- <Input
123
- id={field.name}
124
- name={field.name}
125
- type="password"
126
- value={field.state.value}
127
- onBlur={field.handleBlur}
128
- onChange={(e) => field.handleChange(e.target.value)}
129
- />
130
- {field.state.meta.errors.map((error) => (
131
- <p key={error?.message} className="text-red-500">
132
- {error?.message}
133
- </p>
134
- ))}
135
- </div>
136
- )}
137
- </form.Field>
138
- </div>
113
+ <div>
114
+ <form.Field name="password">
115
+ {(field) => (
116
+ <div className="space-y-2">
117
+ <Label htmlFor={field.name}>Password</Label>
118
+ <Input
119
+ id={field.name}
120
+ name={field.name}
121
+ type="password"
122
+ value={field.state.value}
123
+ onBlur={field.handleBlur}
124
+ onChange={(e) => field.handleChange(e.target.value)}
125
+ />
126
+ {field.state.meta.errors.map((error) => (
127
+ <p key={error?.message} className="text-red-500">
128
+ {error?.message}
129
+ </p>
130
+ ))}
131
+ </div>
132
+ )}
133
+ </form.Field>
134
+ </div>
139
135
 
140
- <form.Subscribe>
141
- {(state) => (
142
- <Button
143
- type="submit"
144
- className="w-full"
145
- disabled={!state.canSubmit || state.isSubmitting}
146
- >
147
- {state.isSubmitting ? "Submitting..." : "Sign Up"}
148
- </Button>
149
- )}
150
- </form.Subscribe>
151
- </form>
136
+ <form.Subscribe>
137
+ {(state) => (
138
+ <Button
139
+ type="submit"
140
+ className="w-full"
141
+ disabled={!state.canSubmit || state.isSubmitting}
142
+ >
143
+ {state.isSubmitting ? "Submitting..." : "Sign Up"}
144
+ </Button>
145
+ )}
146
+ </form.Subscribe>
147
+ </form>
152
148
 
153
- <div className="mt-4 text-center">
154
- <Button
155
- variant="link"
156
- onClick={onSwitchToSignIn}
157
- className="text-indigo-600 hover:text-indigo-800"
158
- >
159
- Already have an account? Sign In
160
- </Button>
161
- </div>
162
- </div>
163
- );
149
+ <div className="mt-4 text-center">
150
+ <Button
151
+ variant="link"
152
+ onClick={onSwitchToSignIn}
153
+ className="text-indigo-600 hover:text-indigo-800"
154
+ >
155
+ Already have an account? Sign In
156
+ </Button>
157
+ </div>
158
+ </div>
159
+ );
164
160
  }