better-ts-stack 2.0.2 → 2.1.1

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 (68) hide show
  1. package/README.md +114 -113
  2. package/dist/args.js +27 -20
  3. package/dist/builder/buildPlanner.js +1 -1
  4. package/dist/builder/configGenerator.js +4 -6
  5. package/dist/builder/index.js +15 -3
  6. package/dist/builder/moduleSelector.js +6 -2
  7. package/dist/builder/readmeGenerator.js +107 -0
  8. package/dist/builder/templateContext.js +53 -0
  9. package/dist/index.js +7 -1
  10. package/dist/output/nextSteps.js +23 -22
  11. package/dist/prompts/index.js +5 -2
  12. package/dist/prompts/stack.js +23 -2
  13. package/dist/types/index.js +2 -0
  14. package/dist/ui/messages.js +3 -1
  15. package/dist/validators/index.js +5 -2
  16. package/package.json +3 -3
  17. package/templates/backend/express/config.json +42 -40
  18. package/templates/backend/express/gitignore +3 -0
  19. package/templates/backend/express/src/env.ts.hbs +39 -0
  20. package/templates/backend/express/src/index.ts.hbs +111 -75
  21. package/templates/backend/nestjs/config.json +43 -41
  22. package/templates/backend/nestjs/gitignore +3 -0
  23. package/templates/backend/nestjs/src/app.module.ts.hbs +13 -0
  24. package/templates/backend/nestjs/src/env.ts.hbs +39 -0
  25. package/templates/backend/nestjs/src/health/{health.controller.ts → health.controller.ts.hbs} +8 -0
  26. package/templates/backend/nestjs/src/main.ts +3 -2
  27. package/templates/frontend/nextjs/app/page.tsx.hbs +98 -97
  28. package/templates/frontend/nextjs/config.json +12 -14
  29. package/templates/frontend/nextjs/gitignore +3 -0
  30. package/templates/frontend/tanstack/config.json +16 -16
  31. package/templates/frontend/tanstack/gitignore +3 -0
  32. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +102 -101
  33. package/templates/frontend/tanstack/vite.config.ts +23 -14
  34. package/templates/modules/auth/express/config.json +20 -20
  35. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +1 -2
  36. package/templates/modules/auth/express/src/routes/auth.ts.hbs +27 -19
  37. package/templates/modules/auth/nestjs/config.json +20 -20
  38. package/templates/modules/auth/nestjs/src/auth/{auth.controller.ts → auth.controller.ts.hbs} +15 -0
  39. package/templates/modules/auth/nextjs/config.json +4 -4
  40. package/templates/{frontend/nextjs/proxy.ts.hbs → modules/auth/nextjs/proxy.ts} +1 -8
  41. package/templates/modules/auth/tanstack/config.json +4 -4
  42. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +132 -134
  43. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +46 -48
  44. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +150 -152
  45. package/templates/modules/ci/.github/workflows/ci.yml.hbs +43 -0
  46. package/templates/modules/ci/config.json +10 -0
  47. package/templates/modules/docker/.dockerignore +2 -0
  48. package/templates/modules/docker/Dockerfile.hbs +147 -136
  49. package/templates/modules/docker/docker-compose.yml.hbs +97 -2
  50. package/templates/modules/drizzle/express/config.json +3 -3
  51. package/templates/modules/drizzle/nestjs/config.json +3 -3
  52. package/templates/modules/drizzle/nextjs/config.json +3 -3
  53. package/templates/modules/drizzle/tanstack/config.json +3 -3
  54. package/templates/modules/mongoose/express/config.json +2 -2
  55. package/templates/modules/mongoose/nestjs/config.json +1 -1
  56. package/templates/modules/mongoose/nextjs/config.json +2 -2
  57. package/templates/modules/mongoose/tanstack/config.json +2 -2
  58. package/templates/modules/prisma/express/config.json +4 -4
  59. package/templates/modules/prisma/express/prisma.config.ts +4 -2
  60. package/templates/modules/prisma/nestjs/config.json +4 -4
  61. package/templates/modules/prisma/nestjs/prisma.config.ts +4 -2
  62. package/templates/modules/prisma/nextjs/config.json +4 -4
  63. package/templates/modules/prisma/nextjs/prisma.config.ts +4 -2
  64. package/templates/modules/prisma/tanstack/config.json +4 -4
  65. package/templates/modules/prisma/tanstack/prisma.config.ts +4 -2
  66. package/templates/modules/rate-limit/express/config.json +12 -0
  67. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +23 -0
  68. package/templates/modules/rate-limit/nestjs/config.json +12 -0
@@ -1,153 +1,151 @@
1
- "use client";
2
-
3
- import { Link, useNavigate } from "@tanstack/react-router";
4
- import { useState, useTransition } from "react";
5
- import { zodResolver } from "@hookform/resolvers/zod";
6
- import { LoaderCircle } from "lucide-react";
7
- import { useForm } from "react-hook-form";
8
-
9
- import { authClient } from "~/lib/auth-client";
10
- import {
11
- getAuthErrorMessage,
12
- signUpSchema,
13
- type SignUpValues,
14
- } from "~/lib/auth-schema";
15
- import { Button } from "~/components/ui/button";
16
- import {
17
- Card,
18
- CardContent,
19
- CardDescription,
20
- CardFooter,
21
- CardHeader,
22
- CardTitle,
23
- } from "~/components/ui/card";
24
- import {
25
- Field,
26
- FieldDescription,
27
- FieldError,
28
- FieldGroup,
29
- FieldLabel,
30
- } from "~/components/ui/field";
31
- import { Input } from "~/components/ui/input";
32
-
33
- export function SignUpForm() {
34
- const navigate = useNavigate();
35
- const [isPending, startTransition] = useTransition();
36
- const [authError, setAuthError] = useState<string | null>(null);
37
- const form = useForm<SignUpValues>({
38
- resolver: zodResolver(signUpSchema),
39
- defaultValues: {
40
- name: "",
41
- email: "",
42
- password: "",
43
- },
44
- });
45
-
46
- const onSubmit = form.handleSubmit((values) => {
47
- setAuthError(null);
48
-
49
- startTransition(async () => {
50
- const result = await authClient.signUp.email({
51
- name: values.name,
52
- email: values.email,
53
- password: values.password,
54
- });
55
-
56
- if (result.error) {
57
- setAuthError(getAuthErrorMessage(result.error));
58
- return;
59
- }
60
-
61
- navigate({ to: "/dashboard" });
62
- });
63
- });
64
-
65
- return (
66
- <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
67
- <CardHeader className="space-y-2">
68
- <CardTitle>Create your account</CardTitle>
69
- <CardDescription>
70
- Start with a simple email and password sign-up flow powered by Better
71
- Auth.
72
- </CardDescription>
73
- </CardHeader>
74
- <CardContent>
75
- <form className="space-y-6" onSubmit={onSubmit} noValidate>
76
- <FieldGroup>
77
- <Field data-invalid={Boolean(form.formState.errors.name)}>
78
- <FieldLabel htmlFor="name">Full name</FieldLabel>
79
- <Input
80
- id="name"
81
- autoComplete="name"
82
- aria-invalid={Boolean(form.formState.errors.name)}
83
- placeholder="Ada Lovelace"
84
- {...form.register("name")}
85
- />
86
- <FieldError errors={[form.formState.errors.name]} />
87
- </Field>
88
-
89
- <Field data-invalid={Boolean(form.formState.errors.email)}>
90
- <FieldLabel htmlFor="email">Email</FieldLabel>
91
- <Input
92
- id="email"
93
- type="email"
94
- autoComplete="email"
95
- aria-invalid={Boolean(form.formState.errors.email)}
96
- placeholder="you@example.com"
97
- {...form.register("email")}
98
- />
99
- <FieldDescription>
100
- This email will be used for future sign-ins.
101
- </FieldDescription>
102
- <FieldError errors={[form.formState.errors.email]} />
103
- </Field>
104
-
105
- <Field data-invalid={Boolean(form.formState.errors.password)}>
106
- <FieldLabel htmlFor="password">Password</FieldLabel>
107
- <Input
108
- id="password"
109
- type="password"
110
- autoComplete="new-password"
111
- aria-invalid={Boolean(form.formState.errors.password)}
112
- placeholder="Create a strong password"
113
- {...form.register("password")}
114
- />
115
- <FieldDescription>
116
- Use at least 8 characters for the generated starter flow.
117
- </FieldDescription>
118
- <FieldError errors={[form.formState.errors.password]} />
119
- </Field>
120
- </FieldGroup>
121
-
122
- {authError ? (
123
- <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
124
- {authError}
125
- </div>
126
- ) : null}
127
-
128
- <Button className="w-full" type="submit" disabled={isPending}>
129
- {isPending ? (
130
- <>
131
- <LoaderCircle className="mr-2 size-4 animate-spin" />
132
- Creating account
133
- </>
134
- ) : (
135
- "Signup"
136
- )}
137
- </Button>
138
- </form>
139
- </CardContent>
140
- <CardFooter className="justify-center">
141
- <p className="text-sm text-slate-600">
142
- Already have an account?{" "}
143
- <Link
144
- className="font-medium text-sky-700 hover:text-sky-800"
145
- to="/sign-in"
146
- >
147
- Login
148
- </Link>
149
- </p>
150
- </CardFooter>
151
- </Card>
152
- );
1
+ import { Link, useNavigate } from "@tanstack/react-router";
2
+ import { useState, useTransition } from "react";
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import { LoaderCircle } from "lucide-react";
5
+ import { useForm } from "react-hook-form";
6
+
7
+ import { authClient } from "~/lib/auth-client";
8
+ import {
9
+ getAuthErrorMessage,
10
+ signUpSchema,
11
+ type SignUpValues,
12
+ } from "~/lib/auth-schema";
13
+ import { Button } from "~/components/ui/button";
14
+ import {
15
+ Card,
16
+ CardContent,
17
+ CardDescription,
18
+ CardFooter,
19
+ CardHeader,
20
+ CardTitle,
21
+ } from "~/components/ui/card";
22
+ import {
23
+ Field,
24
+ FieldDescription,
25
+ FieldError,
26
+ FieldGroup,
27
+ FieldLabel,
28
+ } from "~/components/ui/field";
29
+ import { Input } from "~/components/ui/input";
30
+
31
+ export function SignUpForm() {
32
+ const navigate = useNavigate();
33
+ const [isPending, startTransition] = useTransition();
34
+ const [authError, setAuthError] = useState<string | null>(null);
35
+ const form = useForm<SignUpValues>({
36
+ resolver: zodResolver(signUpSchema),
37
+ defaultValues: {
38
+ name: "",
39
+ email: "",
40
+ password: "",
41
+ },
42
+ });
43
+
44
+ const onSubmit = form.handleSubmit((values) => {
45
+ setAuthError(null);
46
+
47
+ startTransition(async () => {
48
+ const result = await authClient.signUp.email({
49
+ name: values.name,
50
+ email: values.email,
51
+ password: values.password,
52
+ });
53
+
54
+ if (result.error) {
55
+ setAuthError(getAuthErrorMessage(result.error));
56
+ return;
57
+ }
58
+
59
+ navigate({ to: "/dashboard" });
60
+ });
61
+ });
62
+
63
+ return (
64
+ <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
65
+ <CardHeader className="space-y-2">
66
+ <CardTitle>Create your account</CardTitle>
67
+ <CardDescription>
68
+ Start with a simple email and password sign-up flow powered by Better
69
+ Auth.
70
+ </CardDescription>
71
+ </CardHeader>
72
+ <CardContent>
73
+ <form className="space-y-6" onSubmit={onSubmit} noValidate>
74
+ <FieldGroup>
75
+ <Field data-invalid={Boolean(form.formState.errors.name)}>
76
+ <FieldLabel htmlFor="name">Full name</FieldLabel>
77
+ <Input
78
+ id="name"
79
+ autoComplete="name"
80
+ aria-invalid={Boolean(form.formState.errors.name)}
81
+ placeholder="Ada Lovelace"
82
+ {...form.register("name")}
83
+ />
84
+ <FieldError errors={[form.formState.errors.name]} />
85
+ </Field>
86
+
87
+ <Field data-invalid={Boolean(form.formState.errors.email)}>
88
+ <FieldLabel htmlFor="email">Email</FieldLabel>
89
+ <Input
90
+ id="email"
91
+ type="email"
92
+ autoComplete="email"
93
+ aria-invalid={Boolean(form.formState.errors.email)}
94
+ placeholder="you@example.com"
95
+ {...form.register("email")}
96
+ />
97
+ <FieldDescription>
98
+ This email will be used for future sign-ins.
99
+ </FieldDescription>
100
+ <FieldError errors={[form.formState.errors.email]} />
101
+ </Field>
102
+
103
+ <Field data-invalid={Boolean(form.formState.errors.password)}>
104
+ <FieldLabel htmlFor="password">Password</FieldLabel>
105
+ <Input
106
+ id="password"
107
+ type="password"
108
+ autoComplete="new-password"
109
+ aria-invalid={Boolean(form.formState.errors.password)}
110
+ placeholder="Create a strong password"
111
+ {...form.register("password")}
112
+ />
113
+ <FieldDescription>
114
+ Use at least 8 characters for the generated starter flow.
115
+ </FieldDescription>
116
+ <FieldError errors={[form.formState.errors.password]} />
117
+ </Field>
118
+ </FieldGroup>
119
+
120
+ {authError ? (
121
+ <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
122
+ {authError}
123
+ </div>
124
+ ) : null}
125
+
126
+ <Button className="w-full" type="submit" disabled={isPending}>
127
+ {isPending ? (
128
+ <>
129
+ <LoaderCircle className="mr-2 size-4 animate-spin" />
130
+ Creating account
131
+ </>
132
+ ) : (
133
+ "Signup"
134
+ )}
135
+ </Button>
136
+ </form>
137
+ </CardContent>
138
+ <CardFooter className="justify-center">
139
+ <p className="text-sm text-slate-600">
140
+ Already have an account?{" "}
141
+ <Link
142
+ className="font-medium text-sky-700 hover:text-sky-800"
143
+ to="/sign-in"
144
+ >
145
+ Login
146
+ </Link>
147
+ </p>
148
+ </CardFooter>
149
+ </Card>
150
+ );
153
151
  }
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ check:
13
+ name: Build, type-check, lint
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+ {{#if (eq packageManager 'pnpm')}}
18
+ - uses: pnpm/action-setup@v6
19
+ with:
20
+ version: 11
21
+ {{/if}}
22
+ - uses: actions/setup-node@v7
23
+ with:
24
+ node-version: 24
25
+ {{#if (eq packageManager 'bun')}}
26
+ - uses: oven-sh/setup-bun@v2
27
+ {{/if}}
28
+ - name: Install
29
+ run: {{docker.install}}
30
+ - name: Build
31
+ {{#if ciBuildEnv.length}}
32
+ # Placeholders: the build never connects to a database. Add real values
33
+ # as repository secrets if your build needs them
34
+ env:
35
+ {{#each ciBuildEnv}}
36
+ {{name}}: "{{value}}"
37
+ {{/each}}
38
+ {{/if}}
39
+ run: {{docker.run}} build
40
+ - name: Type-check
41
+ run: {{docker.run}} type:check
42
+ - name: Lint
43
+ run: {{docker.run}} lint
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "ci",
3
+ "name": "GitHub Actions CI",
4
+ "description": "A GitHub Actions workflow that builds, type-checks, and lints every push and pull request",
5
+ "type": "feature",
6
+ "dependencies": {},
7
+ "devDependencies": {},
8
+ "scripts": {},
9
+ "envVars": {}
10
+ }
@@ -2,6 +2,7 @@ node_modules
2
2
  npm-debug.log
3
3
  .env*
4
4
  .git
5
+ .github
5
6
  .gitignore
6
7
  README.md
7
8
  .vscode
@@ -12,3 +13,4 @@ coverage
12
13
  *.log
13
14
 
14
15
  .next
16
+ .output