better-ts-stack 2.1.1 → 2.1.2
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.
- package/LICENSE +21 -21
- package/dist/args.js +6 -0
- package/dist/builder/moduleSelector.js +6 -1
- package/dist/builder/readmeGenerator.js +6 -0
- package/dist/builder/templateContext.js +1 -0
- package/dist/prompts/stack.js +7 -0
- package/dist/types/index.js +2 -0
- package/dist/ui/messages.js +2 -0
- package/package.json +71 -71
- package/templates/backend/express/.prettierignore +52 -52
- package/templates/backend/express/.prettierrc +16 -16
- package/templates/backend/express/gitignore +3 -1
- package/templates/backend/express/tsconfig.eslint.json +9 -9
- package/templates/backend/nestjs/.prettierignore +52 -52
- package/templates/backend/nestjs/gitignore +3 -1
- package/templates/backend/nestjs/tsconfig.eslint.json +9 -9
- package/templates/frontend/nextjs/app/globals.css +89 -89
- package/templates/frontend/nextjs/app/layout.tsx +34 -34
- package/templates/frontend/nextjs/components/ui/button.tsx +51 -51
- package/templates/frontend/nextjs/components/ui/card.tsx +60 -60
- package/templates/frontend/nextjs/components/ui/field.tsx +67 -67
- package/templates/frontend/nextjs/components/ui/input.tsx +18 -18
- package/templates/frontend/nextjs/config.json +35 -35
- package/templates/frontend/nextjs/eslint.config.mjs +11 -11
- package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
- package/templates/frontend/nextjs/postcss.config.mjs +7 -7
- package/templates/frontend/nextjs/public/file.svg +1 -1
- package/templates/frontend/nextjs/public/globe.svg +1 -1
- package/templates/frontend/nextjs/public/window.svg +1 -1
- package/templates/frontend/tanstack/components.json +18 -18
- package/templates/frontend/tanstack/eslint.config.mjs +23 -23
- package/templates/frontend/tanstack/gitignore +39 -39
- package/templates/frontend/tanstack/public/favicon.svg +7 -7
- package/templates/frontend/tanstack/src/components/ui/button.tsx +51 -51
- package/templates/frontend/tanstack/src/components/ui/card.tsx +60 -60
- package/templates/frontend/tanstack/src/components/ui/field.tsx +67 -67
- package/templates/frontend/tanstack/src/components/ui/input.tsx +18 -18
- package/templates/frontend/tanstack/src/lib/utils.ts +6 -6
- package/templates/frontend/tanstack/src/router.tsx +31 -31
- package/templates/frontend/tanstack/src/routes/__root.tsx +69 -69
- package/templates/frontend/tanstack/tsconfig.json +22 -22
- package/templates/modules/agents/AGENTS.md.hbs +237 -0
- package/templates/modules/agents/CLAUDE.md +1 -0
- package/templates/modules/agents/config.json +10 -0
- package/templates/modules/auth/express/src/lib/jwt.ts.hbs +27 -27
- package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -4
- package/templates/modules/auth/nextjs/config.json +18 -18
- package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -3
- package/templates/modules/auth/tanstack/config.json +18 -18
- package/templates/modules/auth/tanstack/src/lib/auth-client.ts +2 -2
- package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +8 -8
- package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +37 -37
- package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +15 -15
- package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +110 -110
- package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +36 -36
- package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +36 -36
- package/templates/modules/docker/.dockerignore +2 -0
- package/templates/modules/docker/config.json +15 -15
- package/templates/modules/drizzle/express/config.json +26 -26
- package/templates/modules/drizzle/nestjs/config.json +26 -26
- package/templates/modules/drizzle/nextjs/config.json +26 -26
- package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -13
- package/templates/modules/drizzle/tanstack/config.json +26 -26
- package/templates/modules/drizzle/tanstack/drizzle.config.ts +12 -12
- package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -13
- package/templates/modules/mongoose/express/config.json +15 -15
- package/templates/modules/mongoose/express/src/lib/db.ts +43 -43
- package/templates/modules/mongoose/express/src/models/User.ts +48 -48
- package/templates/modules/mongoose/nestjs/config.json +15 -15
- package/templates/modules/mongoose/nextjs/config.json +15 -15
- package/templates/modules/mongoose/nextjs/models/User.ts +48 -48
- package/templates/modules/mongoose/tanstack/config.json +16 -16
- package/templates/modules/mongoose/tanstack/src/models/User.ts +48 -48
- package/templates/modules/prisma/express/config.json +27 -27
- package/templates/modules/prisma/express/prisma/schema.prisma +23 -23
- package/templates/modules/prisma/nestjs/config.json +27 -27
- package/templates/modules/prisma/nextjs/config.json +28 -28
- package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -18
- package/templates/modules/prisma/nextjs/prisma.config.ts +14 -14
- package/templates/modules/prisma/tanstack/config.json +28 -28
- package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -91
- package/templates/modules/prisma/tanstack/prisma.config.ts +14 -14
- package/templates/modules/prisma/tanstack/src/lib/prisma.ts +17 -17
- package/templates/modules/vscode/.vscode/extensions.json.hbs +29 -0
- package/templates/modules/vscode/.vscode/settings.json.hbs +22 -0
- package/templates/modules/vscode/config.json +10 -0
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "~/lib/utils";
|
|
4
|
-
|
|
5
|
-
function FieldGroup({
|
|
6
|
-
className,
|
|
7
|
-
...props
|
|
8
|
-
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
-
return <div className={cn("space-y-5", className)} {...props} />;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type FieldProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
13
|
-
"data-invalid"?: boolean;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
function Field({
|
|
17
|
-
className,
|
|
18
|
-
"data-invalid": dataInvalid,
|
|
19
|
-
...props
|
|
20
|
-
}: FieldProps) {
|
|
21
|
-
return (
|
|
22
|
-
<div
|
|
23
|
-
data-invalid={dataInvalid}
|
|
24
|
-
className={cn("space-y-2", className)}
|
|
25
|
-
{...props}
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function FieldLabel({
|
|
31
|
-
className,
|
|
32
|
-
...props
|
|
33
|
-
}: React.LabelHTMLAttributes<HTMLLabelElement>) {
|
|
34
|
-
return (
|
|
35
|
-
<label
|
|
36
|
-
className={cn("text-sm font-medium text-slate-900", className)}
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function FieldDescription({
|
|
43
|
-
className,
|
|
44
|
-
...props
|
|
45
|
-
}: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
46
|
-
return <p className={cn("text-sm text-slate-500", className)} {...props} />;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type FieldErrorProps = React.HTMLAttributes<HTMLParagraphElement> & {
|
|
50
|
-
errors?: Array<{ message?: string } | undefined>;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
function FieldError({ className, errors, ...props }: FieldErrorProps) {
|
|
54
|
-
const message = errors?.find((error) => error?.message)?.message;
|
|
55
|
-
|
|
56
|
-
if (!message) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<p className={cn("text-sm font-medium text-rose-600", className)} {...props}>
|
|
62
|
-
{message}
|
|
63
|
-
</p>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { Field, FieldDescription, FieldError, FieldGroup, FieldLabel };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "~/lib/utils";
|
|
4
|
+
|
|
5
|
+
function FieldGroup({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return <div className={cn("space-y-5", className)} {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type FieldProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
13
|
+
"data-invalid"?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function Field({
|
|
17
|
+
className,
|
|
18
|
+
"data-invalid": dataInvalid,
|
|
19
|
+
...props
|
|
20
|
+
}: FieldProps) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
data-invalid={dataInvalid}
|
|
24
|
+
className={cn("space-y-2", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function FieldLabel({
|
|
31
|
+
className,
|
|
32
|
+
...props
|
|
33
|
+
}: React.LabelHTMLAttributes<HTMLLabelElement>) {
|
|
34
|
+
return (
|
|
35
|
+
<label
|
|
36
|
+
className={cn("text-sm font-medium text-slate-900", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function FieldDescription({
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
46
|
+
return <p className={cn("text-sm text-slate-500", className)} {...props} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type FieldErrorProps = React.HTMLAttributes<HTMLParagraphElement> & {
|
|
50
|
+
errors?: Array<{ message?: string } | undefined>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function FieldError({ className, errors, ...props }: FieldErrorProps) {
|
|
54
|
+
const message = errors?.find((error) => error?.message)?.message;
|
|
55
|
+
|
|
56
|
+
if (!message) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<p className={cn("text-sm font-medium text-rose-600", className)} {...props}>
|
|
62
|
+
{message}
|
|
63
|
+
</p>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { Field, FieldDescription, FieldError, FieldGroup, FieldLabel };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "~/lib/utils";
|
|
4
|
-
|
|
5
|
-
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
-
return (
|
|
7
|
-
<input
|
|
8
|
-
type={type}
|
|
9
|
-
className={cn(
|
|
10
|
-
"flex h-11 w-full rounded-2xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-950 shadow-sm transition-[color,box-shadow] outline-none placeholder:text-slate-400 focus-visible:border-sky-500 focus-visible:ring-[3px] focus-visible:ring-sky-500/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
|
-
className
|
|
12
|
-
)}
|
|
13
|
-
{...props}
|
|
14
|
-
/>
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export { Input };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "~/lib/utils";
|
|
4
|
+
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
+
return (
|
|
7
|
+
<input
|
|
8
|
+
type={type}
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex h-11 w-full rounded-2xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-950 shadow-sm transition-[color,box-shadow] outline-none placeholder:text-slate-400 focus-visible:border-sky-500 focus-visible:ring-[3px] focus-visible:ring-sky-500/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Input };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from "clsx";
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs));
|
|
6
|
-
}
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
-
import { createRouter } from "@tanstack/react-router";
|
|
3
|
-
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
4
|
-
|
|
5
|
-
import { routeTree } from "~/routeTree.gen";
|
|
6
|
-
|
|
7
|
-
export function getRouter() {
|
|
8
|
-
const queryClient = new QueryClient({
|
|
9
|
-
defaultOptions: {
|
|
10
|
-
queries: {
|
|
11
|
-
staleTime: 30_000,
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const router = createRouter({
|
|
17
|
-
routeTree,
|
|
18
|
-
context: {
|
|
19
|
-
queryClient,
|
|
20
|
-
},
|
|
21
|
-
defaultPreload: "intent",
|
|
22
|
-
defaultPreloadStaleTime: 0,
|
|
23
|
-
scrollRestoration: true,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
setupRouterSsrQueryIntegration({
|
|
27
|
-
router,
|
|
28
|
-
queryClient,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return router;
|
|
1
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { createRouter } from "@tanstack/react-router";
|
|
3
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
4
|
+
|
|
5
|
+
import { routeTree } from "~/routeTree.gen";
|
|
6
|
+
|
|
7
|
+
export function getRouter() {
|
|
8
|
+
const queryClient = new QueryClient({
|
|
9
|
+
defaultOptions: {
|
|
10
|
+
queries: {
|
|
11
|
+
staleTime: 30_000,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const router = createRouter({
|
|
17
|
+
routeTree,
|
|
18
|
+
context: {
|
|
19
|
+
queryClient,
|
|
20
|
+
},
|
|
21
|
+
defaultPreload: "intent",
|
|
22
|
+
defaultPreloadStaleTime: 0,
|
|
23
|
+
scrollRestoration: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
setupRouterSsrQueryIntegration({
|
|
27
|
+
router,
|
|
28
|
+
queryClient,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return router;
|
|
32
32
|
}
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
2
|
-
import {
|
|
3
|
-
HeadContent,
|
|
4
|
-
Outlet,
|
|
5
|
-
Scripts,
|
|
6
|
-
createRootRouteWithContext,
|
|
7
|
-
} from "@tanstack/react-router";
|
|
8
|
-
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
9
|
-
import type { QueryClient } from "@tanstack/react-query";
|
|
10
|
-
import * as React from "react";
|
|
11
|
-
|
|
12
|
-
import appCss from "~/styles/app.css?url";
|
|
13
|
-
|
|
14
|
-
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
|
15
|
-
head: () => ({
|
|
16
|
-
meta: [
|
|
17
|
-
{
|
|
18
|
-
charSet: "utf-8",
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: "viewport",
|
|
22
|
-
content: "width=device-width, initial-scale=1",
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
links: [
|
|
26
|
-
{
|
|
27
|
-
rel: "icon",
|
|
28
|
-
type: "image/svg+xml",
|
|
29
|
-
href: "/favicon.svg",
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
}),
|
|
33
|
-
component: RootComponent,
|
|
34
|
-
notFoundComponent: () => <NotFound />,
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
function RootComponent() {
|
|
38
|
-
return (
|
|
39
|
-
<RootDocument>
|
|
40
|
-
<Outlet />
|
|
41
|
-
</RootDocument>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
46
|
-
return (
|
|
47
|
-
<html lang="en">
|
|
48
|
-
<head>
|
|
49
|
-
<HeadContent />
|
|
50
|
-
<link rel="stylesheet" href={appCss} />
|
|
51
|
-
</head>
|
|
52
|
-
<body>
|
|
53
|
-
{children}
|
|
54
|
-
<TanStackRouterDevtools />
|
|
55
|
-
<Scripts />
|
|
56
|
-
</body>
|
|
57
|
-
</html>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function NotFound() {
|
|
62
|
-
return (
|
|
63
|
-
<div className="flex min-h-[50vh] items-center justify-center">
|
|
64
|
-
<div className="text-center">
|
|
65
|
-
<h1 className="text-4xl font-bold">404</h1>
|
|
66
|
-
<p className="mt-2 text-muted-foreground">Page not found</p>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
)
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
import {
|
|
3
|
+
HeadContent,
|
|
4
|
+
Outlet,
|
|
5
|
+
Scripts,
|
|
6
|
+
createRootRouteWithContext,
|
|
7
|
+
} from "@tanstack/react-router";
|
|
8
|
+
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
9
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
import appCss from "~/styles/app.css?url";
|
|
13
|
+
|
|
14
|
+
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
|
15
|
+
head: () => ({
|
|
16
|
+
meta: [
|
|
17
|
+
{
|
|
18
|
+
charSet: "utf-8",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "viewport",
|
|
22
|
+
content: "width=device-width, initial-scale=1",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
links: [
|
|
26
|
+
{
|
|
27
|
+
rel: "icon",
|
|
28
|
+
type: "image/svg+xml",
|
|
29
|
+
href: "/favicon.svg",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}),
|
|
33
|
+
component: RootComponent,
|
|
34
|
+
notFoundComponent: () => <NotFound />,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
function RootComponent() {
|
|
38
|
+
return (
|
|
39
|
+
<RootDocument>
|
|
40
|
+
<Outlet />
|
|
41
|
+
</RootDocument>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
46
|
+
return (
|
|
47
|
+
<html lang="en">
|
|
48
|
+
<head>
|
|
49
|
+
<HeadContent />
|
|
50
|
+
<link rel="stylesheet" href={appCss} />
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
{children}
|
|
54
|
+
<TanStackRouterDevtools />
|
|
55
|
+
<Scripts />
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function NotFound() {
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex min-h-[50vh] items-center justify-center">
|
|
64
|
+
<div className="text-center">
|
|
65
|
+
<h1 className="text-4xl font-bold">404</h1>
|
|
66
|
+
<p className="mt-2 text-muted-foreground">Page not found</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
70
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"strict": true,
|
|
5
|
-
"esModuleInterop": true,
|
|
6
|
-
"jsx": "react-jsx",
|
|
7
|
-
"module": "ESNext",
|
|
8
|
-
"moduleResolution": "Bundler",
|
|
9
|
-
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
10
|
-
"isolatedModules": true,
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"target": "ES2022",
|
|
14
|
-
"allowJs": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
"forceConsistentCasingInFileNames": true,
|
|
17
|
-
"paths": {
|
|
18
|
-
"~/*": ["./src/*"]
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"exclude": ["node_modules"]
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"target": "ES2022",
|
|
14
|
+
"allowJs": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"paths": {
|
|
18
|
+
"~/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"exclude": ["node_modules"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for AI coding agents working in {{projectName}}. Read this before making changes.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
{{#if (eq framework 'express')}}
|
|
8
|
+
- Express 5 REST API in TypeScript, compiled to CommonJS with `tsc`
|
|
9
|
+
{{/if}}
|
|
10
|
+
{{#if (eq framework 'nestjs')}}
|
|
11
|
+
- NestJS 12 REST API in TypeScript, using ES modules
|
|
12
|
+
{{/if}}
|
|
13
|
+
{{#if (eq framework 'nextjs')}}
|
|
14
|
+
- Next.js 16 with the App Router, React 19, Tailwind CSS 4, and shadcn/ui
|
|
15
|
+
{{/if}}
|
|
16
|
+
{{#if (eq framework 'tanstack')}}
|
|
17
|
+
- TanStack Start with Vite, Nitro, React 19, Tailwind CSS 4, and shadcn/ui
|
|
18
|
+
{{/if}}
|
|
19
|
+
{{#if (eq database 'prisma')}}
|
|
20
|
+
- PostgreSQL through Prisma 7
|
|
21
|
+
{{/if}}
|
|
22
|
+
{{#if (eq database 'drizzle')}}
|
|
23
|
+
- PostgreSQL through Drizzle ORM
|
|
24
|
+
{{/if}}
|
|
25
|
+
{{#if (eq database 'mongoose')}}
|
|
26
|
+
- MongoDB through Mongoose
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{#if useAuth}}
|
|
29
|
+
{{#if (eq framework 'express')}}
|
|
30
|
+
- JWT auth: `POST /auth/register`, `POST /auth/login`, and `GET /auth/me`
|
|
31
|
+
{{/if}}
|
|
32
|
+
{{#if (eq framework 'nestjs')}}
|
|
33
|
+
- JWT auth: `POST /auth/register`, `POST /auth/login`, and `GET /auth/me`
|
|
34
|
+
{{/if}}
|
|
35
|
+
{{#if (eq framework 'nextjs')}}
|
|
36
|
+
- Better Auth with email and password
|
|
37
|
+
{{/if}}
|
|
38
|
+
{{#if (eq framework 'tanstack')}}
|
|
39
|
+
- Better Auth with email and password
|
|
40
|
+
{{/if}}
|
|
41
|
+
{{/if}}
|
|
42
|
+
{{#if useRateLimit}}
|
|
43
|
+
- Rate limiting on every route except `/health`
|
|
44
|
+
{{/if}}
|
|
45
|
+
{{#if useDocker}}
|
|
46
|
+
- Docker: `Dockerfile` and `docker-compose.yml`
|
|
47
|
+
{{/if}}
|
|
48
|
+
{{#if useCi}}
|
|
49
|
+
- GitHub Actions CI in `.github/workflows/ci.yml`
|
|
50
|
+
{{/if}}
|
|
51
|
+
- Package manager: {{packageManager}}. Do not use another one or add its lockfile
|
|
52
|
+
- Node.js 24 or later
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
- Install: `{{packageManager}} install`
|
|
57
|
+
- Dev server: `{{docker.run}} dev`
|
|
58
|
+
- Build: `{{docker.run}} build`
|
|
59
|
+
- Type-check: `{{docker.run}} type:check`
|
|
60
|
+
- Lint: `{{docker.run}} lint`
|
|
61
|
+
{{#if (eq database 'prisma')}}
|
|
62
|
+
- After changing `prisma/schema.prisma`: `{{docker.run}} prisma:migrate` creates and applies a migration and regenerates the client
|
|
63
|
+
{{/if}}
|
|
64
|
+
{{#if (eq database 'drizzle')}}
|
|
65
|
+
- After changing the schema: `{{docker.run}} db:generate` writes a migration, then `{{docker.run}} db:migrate` applies it
|
|
66
|
+
{{/if}}
|
|
67
|
+
|
|
68
|
+
## Before you finish
|
|
69
|
+
|
|
70
|
+
Run type-check and lint, and fix every error they report. Run the build too when you changed config, dependencies, or anything the build reads. Do not report a task as done while any of them fails.
|
|
71
|
+
|
|
72
|
+
## Layout
|
|
73
|
+
|
|
74
|
+
{{#if (eq framework 'express')}}
|
|
75
|
+
- `src/index.ts`: creates the app, adds middleware, and mounts the routers
|
|
76
|
+
- `src/env.ts`: checks the environment variables with Zod at startup, so a missing one stops the server with a clear message
|
|
77
|
+
- `src/routes/`: one router per resource, mounted in `src/index.ts`
|
|
78
|
+
{{#if useAuth}}
|
|
79
|
+
- `src/controllers/`, `src/services/`: request handlers and data access for auth
|
|
80
|
+
- `src/middleware/requireAuth.ts`: add it to a route to require a valid JWT
|
|
81
|
+
{{/if}}
|
|
82
|
+
{{#if useRateLimit}}
|
|
83
|
+
- `src/middleware/rateLimit.ts`: the rate limiters
|
|
84
|
+
{{/if}}
|
|
85
|
+
{{#if (eq database 'prisma')}}
|
|
86
|
+
- `src/lib/prisma.ts`: the shared Prisma client. Do not create another one
|
|
87
|
+
{{/if}}
|
|
88
|
+
{{#if (eq database 'drizzle')}}
|
|
89
|
+
- `src/lib/db.ts`, `src/lib/schema.ts`: the database client and the tables
|
|
90
|
+
{{/if}}
|
|
91
|
+
{{#if (eq database 'mongoose')}}
|
|
92
|
+
- `src/lib/db.ts`, `src/models/`: the connection and the Mongoose models
|
|
93
|
+
{{/if}}
|
|
94
|
+
{{/if}}
|
|
95
|
+
{{#if (eq framework 'nestjs')}}
|
|
96
|
+
- `src/main.ts`: starts the app
|
|
97
|
+
- `src/app.module.ts`: the root module. Register new modules and controllers here
|
|
98
|
+
- `src/env.ts`: checks the environment variables with Zod at startup, so a missing one stops the server with a clear message
|
|
99
|
+
- `src/health/`: `GET /health`
|
|
100
|
+
{{#if useAuth}}
|
|
101
|
+
- `src/auth/`: the auth module. Put `@UseGuards(AuthGuard)` on a route to require a valid JWT
|
|
102
|
+
{{/if}}
|
|
103
|
+
{{#if (eq database 'prisma')}}
|
|
104
|
+
- `src/prisma/`: `PrismaService`, injected wherever the database is needed
|
|
105
|
+
{{/if}}
|
|
106
|
+
{{#if (eq database 'drizzle')}}
|
|
107
|
+
- `src/database/`: the database module and the tables in `schema.ts`
|
|
108
|
+
{{/if}}
|
|
109
|
+
{{#if (eq database 'mongoose')}}
|
|
110
|
+
- `src/users/user.schema.ts`: the Mongoose schema for users
|
|
111
|
+
{{/if}}
|
|
112
|
+
{{/if}}
|
|
113
|
+
{{#if (eq framework 'nextjs')}}
|
|
114
|
+
- `app/`: routes, layouts, and route handlers. Pages are Server Components unless they need state, effects, or event handlers
|
|
115
|
+
- `components/ui/`: shadcn/ui components. Add new ones with the shadcn CLI instead of writing them by hand
|
|
116
|
+
- `lib/utils.ts`: the `cn()` class name helper
|
|
117
|
+
{{#if useAuth}}
|
|
118
|
+
- `lib/auth.ts`: the Better Auth server. `lib/auth-client.ts`: the client for components
|
|
119
|
+
- `app/api/auth/[...all]/route.ts`: the Better Auth endpoints. Do not rename it
|
|
120
|
+
- `proxy.ts`: sends signed-out visitors from `/dashboard` to `/sign-in`. Add other protected paths to its `matcher`
|
|
121
|
+
{{/if}}
|
|
122
|
+
{{#if (eq database 'prisma')}}
|
|
123
|
+
- `lib/prisma.ts`: the shared Prisma client. Do not create another one
|
|
124
|
+
{{/if}}
|
|
125
|
+
{{#if (eq database 'drizzle')}}
|
|
126
|
+
- `lib/db.ts`, `lib/schema.ts`: the database client and the tables
|
|
127
|
+
{{/if}}
|
|
128
|
+
{{#if (eq database 'mongoose')}}
|
|
129
|
+
- `lib/db.ts`, `models/`: the connection and the Mongoose models
|
|
130
|
+
{{/if}}
|
|
131
|
+
{{/if}}
|
|
132
|
+
{{#if (eq framework 'tanstack')}}
|
|
133
|
+
- `src/routes/`: file-based routes. A new file becomes a route
|
|
134
|
+
- `src/routeTree.gen.ts`: generated by the dev server and the build. Never edit it
|
|
135
|
+
- `src/router.tsx`, `src/routes/__root.tsx`: the router and the root layout
|
|
136
|
+
- `src/components/ui/`: shadcn/ui components. Add new ones with the shadcn CLI instead of writing them by hand
|
|
137
|
+
- `src/lib/utils.ts`: the `cn()` class name helper
|
|
138
|
+
{{#if useAuth}}
|
|
139
|
+
- `src/lib/auth.ts`: the Better Auth server. `src/lib/auth-client.ts`: the client for components
|
|
140
|
+
- `src/routes/api/auth/$.ts`: the Better Auth endpoints. Do not rename it
|
|
141
|
+
- `src/routes/dashboard.tsx`: an example of a protected route that redirects in `beforeLoad`
|
|
142
|
+
{{/if}}
|
|
143
|
+
{{#if (eq database 'prisma')}}
|
|
144
|
+
- `src/lib/prisma.ts`: the shared Prisma client. Do not create another one
|
|
145
|
+
{{/if}}
|
|
146
|
+
{{#if (eq database 'drizzle')}}
|
|
147
|
+
- `src/lib/db.ts`, `src/lib/schema.ts`: the database client and the tables
|
|
148
|
+
{{/if}}
|
|
149
|
+
{{#if (eq database 'mongoose')}}
|
|
150
|
+
- `src/lib/db.ts`, `src/models/`: the connection and the Mongoose models
|
|
151
|
+
{{/if}}
|
|
152
|
+
{{/if}}
|
|
153
|
+
|
|
154
|
+
## Rules
|
|
155
|
+
|
|
156
|
+
- TypeScript is strict. Do not use `any` or `@ts-ignore` to silence an error; fix the type
|
|
157
|
+
{{#if (eq framework 'nestjs')}}
|
|
158
|
+
- Relative imports end in `.js`, as ES modules require: `import { AuthModule } from "./auth/auth.module.js"`
|
|
159
|
+
{{/if}}
|
|
160
|
+
- Keep `.env` out of git. When you add an environment variable, add it to `.env.example` too{{#if (eq framework 'express')}}, and to `src/env.ts`{{/if}}{{#if (eq framework 'nestjs')}}, and to `src/env.ts`{{/if}}
|
|
161
|
+
{{#if (eq database 'prisma')}}
|
|
162
|
+
- The Prisma client is generated into {{#if (eq framework 'express')}}`src/generated/`{{/if}}{{#if (eq framework 'nestjs')}}`src/generated/`{{/if}}{{#if (eq framework 'nextjs')}}`generated/`{{/if}}{{#if (eq framework 'tanstack')}}`generated/`{{/if}}. Never edit or commit it
|
|
163
|
+
{{/if}}
|
|
164
|
+
- Match the existing code style. ESLint settings are in `eslint.config.mjs`{{#if (eq framework 'express')}} and Prettier settings in `.prettierrc`{{/if}}{{#if (eq framework 'nestjs')}} and Prettier settings in `.prettierrc`{{/if}}
|
|
165
|
+
- Add a dependency only when the task needs it, and install it with {{packageManager}}
|
|
166
|
+
|
|
167
|
+
## Production rules
|
|
168
|
+
|
|
169
|
+
- Validate every input from outside the app (request bodies, query strings, form data) with Zod before you use it{{#unless useAuth}}{{#if (eq framework 'nextjs')}} (add `zod` with {{packageManager}} first){{/if}}{{#if (eq framework 'tanstack')}} (add `zod` with {{packageManager}} first){{/if}}{{/unless}}
|
|
170
|
+
- Never log or return secrets, tokens, or passwords. Error responses get a short message and the right status code, never a stack trace
|
|
171
|
+
{{#unless (eq database 'none')}}
|
|
172
|
+
- Query the database through {{#if (eq database 'prisma')}}Prisma{{/if}}{{#if (eq database 'drizzle')}}Drizzle{{/if}}{{#if (eq database 'mongoose')}}Mongoose{{/if}}, never with SQL or queries built from strings
|
|
173
|
+
{{/unless}}
|
|
174
|
+
{{#if (eq framework 'express')}}
|
|
175
|
+
- Keep the security middleware in `src/index.ts` (`helmet`, `cors`{{#if useRateLimit}}, the rate limiter{{/if}}) in place. Set `CORS_ORIGIN` to your real origins in production
|
|
176
|
+
{{/if}}
|
|
177
|
+
{{#if (eq framework 'nestjs')}}
|
|
178
|
+
- Keep the security setup in `src/main.ts` and `src/app.module.ts` in place. Set `CORS_ORIGIN` to your real origins in production
|
|
179
|
+
{{/if}}
|
|
180
|
+
{{#if (eq framework 'nextjs')}}
|
|
181
|
+
- Code that reads secrets or the database stays on the server. Only variables prefixed with `NEXT_PUBLIC_` reach the browser, so never put a secret in one
|
|
182
|
+
{{/if}}
|
|
183
|
+
{{#if (eq framework 'tanstack')}}
|
|
184
|
+
- Code that reads secrets or the database runs in server functions or server routes. Only variables prefixed with `VITE_` reach the browser, so never put a secret in one
|
|
185
|
+
{{/if}}
|
|
186
|
+
{{#if useAuth}}
|
|
187
|
+
{{#if (eq framework 'nextjs')}}
|
|
188
|
+
- Check the session on the server in every protected page, route handler, and server action. Hiding a button in the UI does not protect anything
|
|
189
|
+
{{/if}}
|
|
190
|
+
{{#if (eq framework 'tanstack')}}
|
|
191
|
+
- Check the session on the server in every protected route and server function. Hiding a button in the UI does not protect anything
|
|
192
|
+
{{/if}}
|
|
193
|
+
{{/if}}
|
|
194
|
+
{{#if (eq framework 'express')}}
|
|
195
|
+
- Keep `GET /health` fast and free of auth{{#if useRateLimit}} and rate limiting{{/if}}, so hosts can check that the app is up
|
|
196
|
+
{{/if}}
|
|
197
|
+
{{#if (eq framework 'nestjs')}}
|
|
198
|
+
- Keep `GET /health` fast and free of auth{{#if useRateLimit}} and rate limiting{{/if}}, so hosts can check that the app is up
|
|
199
|
+
{{/if}}
|
|
200
|
+
- Keep changes small and focused on the task. Do not add layers, abstractions, or files the task does not need
|
|
201
|
+
|
|
202
|
+
## When you are unsure
|
|
203
|
+
|
|
204
|
+
The libraries here may be newer than what you know. When you are not sure how an API works, read the official docs for the version in `package.json` instead of guessing:
|
|
205
|
+
|
|
206
|
+
{{#if (eq framework 'express')}}
|
|
207
|
+
- Express 5: https://expressjs.com/en/5x/api.html
|
|
208
|
+
{{/if}}
|
|
209
|
+
{{#if (eq framework 'nestjs')}}
|
|
210
|
+
- NestJS: https://docs.nestjs.com
|
|
211
|
+
{{/if}}
|
|
212
|
+
{{#if (eq framework 'nextjs')}}
|
|
213
|
+
- Next.js: https://nextjs.org/docs
|
|
214
|
+
{{/if}}
|
|
215
|
+
{{#if (eq framework 'tanstack')}}
|
|
216
|
+
- TanStack Start: https://tanstack.com/start/latest/docs
|
|
217
|
+
{{/if}}
|
|
218
|
+
{{#if (eq database 'prisma')}}
|
|
219
|
+
- Prisma: https://www.prisma.io/docs
|
|
220
|
+
{{/if}}
|
|
221
|
+
{{#if (eq database 'drizzle')}}
|
|
222
|
+
- Drizzle: https://orm.drizzle.team/docs/overview
|
|
223
|
+
{{/if}}
|
|
224
|
+
{{#if (eq database 'mongoose')}}
|
|
225
|
+
- Mongoose: https://mongoosejs.com/docs
|
|
226
|
+
{{/if}}
|
|
227
|
+
{{#if useAuth}}
|
|
228
|
+
{{#if (eq framework 'nextjs')}}
|
|
229
|
+
- Better Auth: https://www.better-auth.com/docs
|
|
230
|
+
{{/if}}
|
|
231
|
+
{{#if (eq framework 'tanstack')}}
|
|
232
|
+
- Better Auth: https://www.better-auth.com/docs
|
|
233
|
+
{{/if}}
|
|
234
|
+
{{/if}}
|
|
235
|
+
- Zod: https://zod.dev
|
|
236
|
+
|
|
237
|
+
If the docs and this file disagree, follow the docs and say so.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|