@newt-app/templates 0.15.2 → 0.17.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.
package/dist/index.js CHANGED
@@ -241,7 +241,7 @@ var package_json_default2 = {
241
241
  "@tanstack/react-form": "^1.28.5",
242
242
  "@tanstack/react-query": "^5.90.21",
243
243
  "better-auth": "^1.2.8",
244
- "next": "16.2.1",
244
+ "next": "16.2.10",
245
245
  "react": "^19.2.4",
246
246
  "react-dom": "^19.2.4",
247
247
  "tailwindcss": "^4.2.1"
@@ -459,9 +459,9 @@ var page_default = {
459
459
  import { useQuery } from '@tanstack/react-query';
460
460
  import { authClient } from '@/lib/auth-client';
461
461
  import { AuthForm } from '@/app/auth-form';
462
+ import { Button } from '@<%= projectName %>/ui/button';
462
463
  import { Link } from '@<%= projectName %>/ui/link';
463
464
  import { Logo } from '@<%= projectName %>/ui/logo';
464
- import { TodoList } from '@/app/todo-list';
465
465
 
466
466
  export default function Home() {
467
467
  const { data: session, isPending } = authClient.useSession();
@@ -507,7 +507,12 @@ export default function Home() {
507
507
  {isPending ? (
508
508
  <p className="text-sm text-gray-400">Loading\u2026</p>
509
509
  ) : session ? (
510
- <TodoList session={session} />
510
+ <div className="space-y-3">
511
+ <p className="text-sm">Signed in as {session.user.name}</p>
512
+ <Button onClick={() => void authClient.signOut()}>
513
+ Sign out
514
+ </Button>
515
+ </div>
511
516
  ) : (
512
517
  <AuthForm />
513
518
  )}
@@ -674,149 +679,6 @@ export function AuthForm() {
674
679
  }`
675
680
  };
676
681
 
677
- // src/web/templates/todo-list.ts
678
- var todo_list_default = {
679
- filename: "apps/web/app/todo-list.tsx",
680
- template: `'use client';
681
-
682
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
683
- import { useForm } from '@tanstack/react-form';
684
- import { authClient } from '@/lib/auth-client';
685
- import { Button } from '@<%= projectName %>/ui/button';
686
-
687
- interface Todo {
688
- id: number;
689
- title: string;
690
- done: boolean;
691
- }
692
-
693
- const api = {
694
- getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
695
- createTodo: (title: string): Promise<Todo> =>
696
- fetch('/api/todos', {
697
- method: 'POST',
698
- headers: { 'Content-Type': 'application/json' },
699
- body: JSON.stringify({ title }),
700
- }).then((r) => r.json()),
701
- toggleTodo: (id: number): Promise<Todo> =>
702
- fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
703
- deleteTodo: (id: number): Promise<void> =>
704
- fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
705
- };
706
-
707
- export function TodoList({
708
- session,
709
- }: {
710
- session: { user: { email: string } };
711
- }) {
712
- const queryClient = useQueryClient();
713
-
714
- const { data: todos = [], isPending } = useQuery({
715
- queryKey: ['todos'],
716
- queryFn: api.getTodos,
717
- });
718
-
719
- const createMutation = useMutation({
720
- mutationFn: api.createTodo,
721
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
722
- });
723
-
724
- const toggleMutation = useMutation({
725
- mutationFn: api.toggleTodo,
726
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
727
- });
728
-
729
- const deleteMutation = useMutation({
730
- mutationFn: api.deleteTodo,
731
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
732
- });
733
-
734
- const form = useForm({
735
- defaultValues: { title: '' },
736
- onSubmit: async ({ value }) => {
737
- if (!value.title.trim()) return;
738
- createMutation.mutate(value.title.trim());
739
- form.reset();
740
- },
741
- });
742
-
743
- return (
744
- <>
745
- <div className="flex items-center justify-between mb-8">
746
- <h1 className="text-2xl font-semibold">Todos</h1>
747
- <div className="flex items-center gap-3 text-sm text-gray-400">
748
- <span>{session.user.email}</span>
749
- <button
750
- className="hover:text-gray-100"
751
- onClick={() => authClient.signOut()}
752
- >
753
- Sign out
754
- </button>
755
- </div>
756
- </div>
757
-
758
- <form
759
- onSubmit={(e) => {
760
- e.preventDefault();
761
- form.handleSubmit();
762
- }}
763
- className="flex gap-2 mb-6"
764
- >
765
- <form.Field name="title">
766
- {(field) => (
767
- <input
768
- value={field.state.value}
769
- onChange={(e) => field.handleChange(e.target.value)}
770
- placeholder="New todo\u2026"
771
- className="flex h-9 flex-1 rounded-md border border-neutral-700 bg-neutral-800/50 px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-neutral-500 focus-visible:border-neutral-500 focus-visible:ring-2 focus-visible:ring-neutral-500/20"
772
- />
773
- )}
774
- </form.Field>
775
-
776
- <form.Subscribe selector={(s) => s.isSubmitting}>
777
- {(isSubmitting) => (
778
- <Button type="submit" disabled={isSubmitting || createMutation.isPending}>
779
- Add
780
- </Button>
781
- )}
782
- </form.Subscribe>
783
- </form>
784
-
785
- {isPending ? (
786
- <p className="text-sm text-gray-400">Loading\u2026</p>
787
- ) : (
788
- <ul className="divide-y divide-neutral-700">
789
- {todos.map((todo) => (
790
- <li key={todo.id} className="flex items-center gap-3 py-3">
791
- <input
792
- type="checkbox"
793
- checked={todo.done}
794
- onChange={() => toggleMutation.mutate(todo.id)}
795
- />
796
- <span
797
- className={\`flex-1 text-sm \${todo.done ? 'line-through text-gray-400' : ''}\`}
798
- >
799
- {todo.title}
800
- </span>
801
- <button
802
- onClick={() => deleteMutation.mutate(todo.id)}
803
- className="text-gray-600 hover:text-red-400 text-lg leading-none"
804
- >
805
- \xD7
806
- </button>
807
- </li>
808
- ))}
809
- </ul>
810
- )}
811
-
812
- {!isPending && todos.length === 0 && (
813
- <p className="text-sm text-gray-400">No todos yet.</p>
814
- )}
815
- </>
816
- );
817
- }`
818
- };
819
-
820
682
  // src/web/templates/auth-route.ts
821
683
  var auth_route_default = {
822
684
  filename: "apps/web/app/api/auth/[...all]/route.ts",
@@ -874,7 +736,6 @@ var web = {
874
736
  page_default,
875
737
  providers_default,
876
738
  auth_form_default,
877
- todo_list_default,
878
739
  auth_route_default,
879
740
  auth_client_default,
880
741
  manifest_default
@@ -1047,10 +908,8 @@ var app_module_default = {
1047
908
  filename: "apps/api/src/app.module.ts",
1048
909
  template: `import { Module } from '@nestjs/common';
1049
910
  import { AppService } from './app.service';
1050
- import { TodosModule } from './todos/todos.module';
1051
911
 
1052
912
  @Module({
1053
- imports: [TodosModule],
1054
913
  providers: [AppService],
1055
914
  })
1056
915
  export class AppModule {}`
@@ -1092,83 +951,6 @@ describe('AppService', () => {
1092
951
  });`
1093
952
  };
1094
953
 
1095
- // src/api/templates/todos-module.ts
1096
- var todos_module_default = {
1097
- filename: "apps/api/src/todos/todos.module.ts",
1098
- template: `import { Module } from '@nestjs/common';
1099
- import { TodosService } from './todos.service';
1100
-
1101
- @Module({
1102
- providers: [TodosService],
1103
- exports: [TodosService],
1104
- })
1105
- export class TodosModule {}`
1106
- };
1107
-
1108
- // src/api/templates/todos-service.ts
1109
- var todos_service_default = {
1110
- filename: "apps/api/src/todos/todos.service.ts",
1111
- template: `import { Injectable, NotFoundException } from '@nestjs/common';
1112
-
1113
- export interface Todo {
1114
- id: number;
1115
- title: string;
1116
- done: boolean;
1117
- }
1118
-
1119
- @Injectable()
1120
- export class TodosService {
1121
- private todos: Todo[] = [];
1122
- private nextId = 1;
1123
-
1124
- findAll(): Todo[] {
1125
- return this.todos;
1126
- }
1127
-
1128
- create(title: string): Todo {
1129
- const todo: Todo = { id: this.nextId++, title, done: false };
1130
- this.todos.push(todo);
1131
- return todo;
1132
- }
1133
-
1134
- toggle(id: number): Todo {
1135
- const todo = this.todos.find((t) => t.id === id);
1136
- if (!todo) throw new NotFoundException(\`Todo \${id} not found\`);
1137
- todo.done = !todo.done;
1138
- return todo;
1139
- }
1140
-
1141
- remove(id: number): void {
1142
- const index = this.todos.findIndex((t) => t.id === id);
1143
- if (index === -1) throw new NotFoundException(\`Todo \${id} not found\`);
1144
- this.todos.splice(index, 1);
1145
- }
1146
- }`
1147
- };
1148
-
1149
- // src/api/templates/todos-service-spec.ts
1150
- var todos_service_spec_default = {
1151
- filename: "apps/api/src/todos/todos.service.spec.ts",
1152
- template: `import { Test, TestingModule } from '@nestjs/testing';
1153
- import { TodosService } from './todos.service';
1154
-
1155
- describe('TodosService', () => {
1156
- let service: TodosService;
1157
-
1158
- beforeEach(async () => {
1159
- const module: TestingModule = await Test.createTestingModule({
1160
- providers: [TodosService],
1161
- }).compile();
1162
-
1163
- service = module.get<TodosService>(TodosService);
1164
- });
1165
-
1166
- it('should be defined', () => {
1167
- expect(service).toBeDefined();
1168
- });
1169
- });`
1170
- };
1171
-
1172
954
  // src/api/templates/readme.ts
1173
955
  var readme_default3 = {
1174
956
  filename: "apps/api/README.md",
@@ -1197,9 +979,6 @@ var api = {
1197
979
  app_module_default,
1198
980
  app_service_default,
1199
981
  app_service_spec_default,
1200
- todos_module_default,
1201
- todos_service_default,
1202
- todos_service_spec_default,
1203
982
  readme_default3
1204
983
  ]
1205
984
  };
@@ -1943,9 +1722,9 @@ var page_default2 = {
1943
1722
  import { useQuery } from '@tanstack/react-query';
1944
1723
  import { authClient } from '@/lib/auth-client';
1945
1724
  import { AuthForm } from '@/app/auth-form';
1725
+ import { Button } from '@<%= projectName %>/ui/button';
1946
1726
  import { Link } from '@<%= projectName %>/ui/link';
1947
1727
  import { Logo } from '@<%= projectName %>/ui/logo';
1948
- import { TodoList } from '@/app/todo-list';
1949
1728
  import { ModeToggle } from '@<%= projectName %>/ui/mode-toggle';
1950
1729
  import { Card, CardContent, CardHeader, CardTitle } from '@<%= projectName %>/ui/card';
1951
1730
 
@@ -2013,7 +1792,12 @@ export default function Home() {
2013
1792
  {isPending ? (
2014
1793
  <p className="text-sm text-muted-foreground">Loading\u2026</p>
2015
1794
  ) : session ? (
2016
- <TodoList session={session} />
1795
+ <div className="space-y-3">
1796
+ <p className="text-sm">Signed in as {session.user.name}</p>
1797
+ <Button variant="outline" onClick={() => authClient.signOut()}>
1798
+ Sign out
1799
+ </Button>
1800
+ </div>
2017
1801
  ) : (
2018
1802
  <AuthForm />
2019
1803
  )}
@@ -2185,169 +1969,22 @@ export function AuthForm() {
2185
1969
  }`
2186
1970
  };
2187
1971
 
2188
- // src/shadcn-ui/templates/todo-list.ts
2189
- var todo_list_default2 = {
2190
- filename: "apps/web/app/todo-list.tsx",
2191
- template: `'use client';
1972
+ // src/shadcn-ui/templates/accordion.ts
1973
+ var accordion_default = {
1974
+ filename: "packages/ui/src/components/accordion.tsx",
1975
+ template: `import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
2192
1976
 
2193
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2194
- import { useForm } from '@tanstack/react-form';
2195
- import { authClient } from '@/lib/auth-client';
2196
- import { Button } from '@<%= projectName %>/ui/button';
2197
- import { Input } from '@<%= projectName %>/ui/input';
2198
- import { Checkbox } from '@<%= projectName %>/ui/checkbox';
1977
+ import { cn } from "@<%= projectName %>/ui/lib/utils"
1978
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
2199
1979
 
2200
- interface Todo {
2201
- id: number;
2202
- title: string;
2203
- done: boolean;
2204
- }
2205
-
2206
- const api = {
2207
- getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
2208
- createTodo: (title: string): Promise<Todo> =>
2209
- fetch('/api/todos', {
2210
- method: 'POST',
2211
- headers: { 'Content-Type': 'application/json' },
2212
- body: JSON.stringify({ title }),
2213
- }).then((r) => r.json()),
2214
- toggleTodo: (id: number): Promise<Todo> =>
2215
- fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
2216
- deleteTodo: (id: number): Promise<void> =>
2217
- fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
2218
- };
2219
-
2220
- export function TodoList({
2221
- session,
2222
- }: {
2223
- session: { user: { email: string } };
2224
- }) {
2225
- const queryClient = useQueryClient();
2226
-
2227
- const { data: todos = [], isPending } = useQuery({
2228
- queryKey: ['todos'],
2229
- queryFn: api.getTodos,
2230
- });
2231
-
2232
- const createMutation = useMutation({
2233
- mutationFn: api.createTodo,
2234
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
2235
- });
2236
-
2237
- const toggleMutation = useMutation({
2238
- mutationFn: api.toggleTodo,
2239
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
2240
- });
2241
-
2242
- const deleteMutation = useMutation({
2243
- mutationFn: api.deleteTodo,
2244
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
2245
- });
2246
-
2247
- const form = useForm({
2248
- defaultValues: { title: '' },
2249
- onSubmit: async ({ value }) => {
2250
- if (!value.title.trim()) return;
2251
- createMutation.mutate(value.title.trim());
2252
- form.reset();
2253
- },
2254
- });
2255
-
2256
- return (
2257
- <>
2258
- <div className="flex items-center justify-between mb-6">
2259
- <h1 className="text-2xl font-semibold">Todos</h1>
2260
- <div className="flex items-center gap-3 text-sm text-muted-foreground">
2261
- <span>{session.user.email}</span>
2262
- <Button
2263
- variant="ghost"
2264
- size="sm"
2265
- onClick={() => authClient.signOut()}
2266
- >
2267
- Sign out
2268
- </Button>
2269
- </div>
2270
- </div>
2271
-
2272
- <form
2273
- onSubmit={(e) => {
2274
- e.preventDefault();
2275
- form.handleSubmit();
2276
- }}
2277
- className="flex gap-2 mb-6"
2278
- >
2279
- <form.Field name="title">
2280
- {(field) => (
2281
- <Input
2282
- value={field.state.value}
2283
- onChange={(e) => field.handleChange(e.target.value)}
2284
- placeholder="New todo\u2026"
2285
- className="flex-1"
2286
- />
2287
- )}
2288
- </form.Field>
2289
-
2290
- <form.Subscribe selector={(s) => s.isSubmitting}>
2291
- {(isSubmitting) => (
2292
- <Button type="submit" disabled={isSubmitting || createMutation.isPending}>
2293
- Add
2294
- </Button>
2295
- )}
2296
- </form.Subscribe>
2297
- </form>
2298
-
2299
- {isPending ? (
2300
- <p className="text-sm text-muted-foreground">Loading\u2026</p>
2301
- ) : (
2302
- <ul className="divide-y divide-border">
2303
- {todos.map((todo) => (
2304
- <li key={todo.id} className="flex items-center gap-3 py-3">
2305
- <Checkbox
2306
- checked={todo.done}
2307
- onCheckedChange={() => toggleMutation.mutate(todo.id)}
2308
- />
2309
- <span
2310
- className={\`flex-1 text-sm \${todo.done ? 'line-through text-muted-foreground' : ''}\`}
2311
- >
2312
- {todo.title}
2313
- </span>
2314
- <Button
2315
- variant="ghost"
2316
- size="sm"
2317
- onClick={() => deleteMutation.mutate(todo.id)}
2318
- className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
2319
- >
2320
- \xD7
2321
- </Button>
2322
- </li>
2323
- ))}
2324
- </ul>
2325
- )}
2326
-
2327
- {!isPending && todos.length === 0 && (
2328
- <p className="text-sm text-muted-foreground">No todos yet.</p>
2329
- )}
2330
- </>
2331
- );
2332
- }`
2333
- };
2334
-
2335
- // src/shadcn-ui/templates/accordion.ts
2336
- var accordion_default = {
2337
- filename: "packages/ui/src/components/accordion.tsx",
2338
- template: `import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
2339
-
2340
- import { cn } from "@<%= projectName %>/ui/lib/utils"
2341
- import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
2342
-
2343
- function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
2344
- return (
2345
- <AccordionPrimitive.Root
2346
- data-slot="accordion"
2347
- className={cn("flex w-full flex-col", className)}
2348
- {...props}
2349
- />
2350
- )
1980
+ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
1981
+ return (
1982
+ <AccordionPrimitive.Root
1983
+ data-slot="accordion"
1984
+ className={cn("flex w-full flex-col", className)}
1985
+ {...props}
1986
+ />
1987
+ )
2351
1988
  }
2352
1989
 
2353
1990
  function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
@@ -9333,7 +8970,6 @@ var shadcnUi = {
9333
8970
  page_default2,
9334
8971
  providers_default2,
9335
8972
  auth_form_default2,
9336
- todo_list_default2,
9337
8973
  accordion_default,
9338
8974
  alert_dialog_default,
9339
8975
  alert_default,
@@ -9412,7 +9048,7 @@ var package_json_default7 = {
9412
9048
  },
9413
9049
  "devDependencies": {
9414
9050
  "@eslint/js": "^9.39.1",
9415
- "@next/eslint-plugin-next": "^15.5.0",
9051
+ "@next/eslint-plugin-next": "^16.2.10",
9416
9052
  "eslint": "^9.39.1",
9417
9053
  "eslint-config-prettier": "^10.1.1",
9418
9054
  "eslint-plugin-only-warn": "^1.1.0",
@@ -10144,7 +9780,7 @@ var web_package_json_default = {
10144
9780
  "@tanstack/react-form": "^1.28.5",
10145
9781
  "@tanstack/react-query": "^5.90.21",
10146
9782
  "better-auth": "^1.2.8",
10147
- "next": "16.2.1",
9783
+ "next": "16.2.10",
10148
9784
  "react": "^19.2.4",
10149
9785
  "react-dom": "^19.2.4",
10150
9786
  "tailwindcss": "^4.2.1"
@@ -10444,54 +10080,6 @@ export async function inject<T>(token: Type<T> | Abstract<T> | string | symbol):
10444
10080
  }`
10445
10081
  };
10446
10082
 
10447
- // src/nest-di-only/templates/web-todos-route.ts
10448
- var web_todos_route_default = {
10449
- filename: "apps/web/app/api/todos/route.ts",
10450
- template: `import { NextResponse } from 'next/server';
10451
- import { inject } from '@/lib/nest';
10452
- import { TodosService } from '@<%= projectName %>/api';
10453
-
10454
- export async function GET() {
10455
- const todos = await inject(TodosService);
10456
- return NextResponse.json(todos.findAll());
10457
- }
10458
-
10459
- export async function POST(req: Request) {
10460
- const { title } = await req.json();
10461
- const todos = await inject(TodosService);
10462
- return NextResponse.json(todos.create(title), { status: 201 });
10463
- }`
10464
- };
10465
-
10466
- // src/nest-di-only/templates/web-todos-id-route.ts
10467
- var web_todos_id_route_default = {
10468
- filename: "apps/web/app/api/todos/[id]/route.ts",
10469
- template: `import { NextResponse } from 'next/server';
10470
- import { inject } from '@/lib/nest';
10471
- import { TodosService } from '@<%= projectName %>/api';
10472
-
10473
- export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
10474
- const { id } = await params;
10475
- const todos = await inject(TodosService);
10476
- todos.remove(Number(id));
10477
- return new NextResponse(null, { status: 204 });
10478
- }`
10479
- };
10480
-
10481
- // src/nest-di-only/templates/web-todos-toggle-route.ts
10482
- var web_todos_toggle_route_default = {
10483
- filename: "apps/web/app/api/todos/[id]/toggle/route.ts",
10484
- template: `import { NextResponse } from 'next/server';
10485
- import { inject } from '@/lib/nest';
10486
- import { TodosService } from '@<%= projectName %>/api';
10487
-
10488
- export async function PATCH(_req: Request, { params }: { params: Promise<{ id: string }> }) {
10489
- const { id } = await params;
10490
- const todos = await inject(TodosService);
10491
- return NextResponse.json(todos.toggle(Number(id)));
10492
- }`
10493
- };
10494
-
10495
10083
  // src/nest-di-only/templates/web-package-json.ts
10496
10084
  var web_package_json_default2 = {
10497
10085
  filename: "apps/web/package.json",
@@ -10520,7 +10108,7 @@ var web_package_json_default2 = {
10520
10108
  "@tanstack/react-form": "^1.28.5",
10521
10109
  "@tanstack/react-query": "^5.90.21",
10522
10110
  "better-auth": "^1.2.8",
10523
- "next": "16.2.1",
10111
+ "next": "16.2.10",
10524
10112
  "react": "^19.2.4",
10525
10113
  "react-dom": "^19.2.4",
10526
10114
  "tailwindcss": "^4.2.1"
@@ -10613,9 +10201,6 @@ var nestDiOnly = {
10613
10201
  api_index_default,
10614
10202
  web_nest_context_default,
10615
10203
  web_hello_route_default,
10616
- web_todos_route_default,
10617
- web_todos_id_route_default,
10618
- web_todos_toggle_route_default,
10619
10204
  web_package_json_default2,
10620
10205
  web_next_config_default3,
10621
10206
  web_tsconfig_default
@@ -10682,10 +10267,9 @@ import { AuthGuard, AuthModule } from '@thallesp/nestjs-better-auth';
10682
10267
  import { auth } from '@<%= projectName %>/auth';
10683
10268
  import { AppController } from './app.controller';
10684
10269
  import { AppService } from './app.service';
10685
- import { TodosModule } from './todos/todos.module';
10686
10270
 
10687
10271
  @Module({
10688
- imports: [AuthModule.forRoot({ auth }), TodosModule],
10272
+ imports: [AuthModule.forRoot({ auth })],
10689
10273
  controllers: [AppController],
10690
10274
  providers: [AppService, { provide: APP_GUARD, useClass: AuthGuard }],
10691
10275
  })
@@ -10717,72 +10301,720 @@ async function bootstrap() {
10717
10301
  void bootstrap();`
10718
10302
  };
10719
10303
 
10720
- // src/api-controllers/templates/todos-controller.ts
10721
- var todos_controller_default = {
10722
- filename: "apps/api/src/todos/todos.controller.ts",
10723
- template: `import {
10724
- Body,
10725
- Controller,
10726
- Delete,
10727
- Get,
10728
- Param,
10729
- Patch,
10730
- Post,
10731
- } from '@nestjs/common';
10732
- import { TodosService } from './todos.service';
10733
-
10734
- @Controller('todos')
10735
- export class TodosController {
10736
- constructor(private readonly todosService: TodosService) {}
10737
-
10738
- @Get()
10739
- findAll() {
10740
- return this.todosService.findAll();
10741
- }
10742
-
10743
- @Post()
10744
- create(@Body('title') title: string) {
10745
- return this.todosService.create(title);
10746
- }
10747
-
10748
- @Patch(':id/toggle')
10749
- toggle(@Param('id') id: string) {
10750
- return this.todosService.toggle(Number(id));
10751
- }
10752
-
10753
- @Delete(':id')
10754
- remove(@Param('id') id: string) {
10755
- return this.todosService.remove(Number(id));
10756
- }
10757
- }`
10304
+ // src/api-controllers/index.ts
10305
+ var apiControllers = {
10306
+ templates: [
10307
+ app_controller_default,
10308
+ app_controller_spec_default,
10309
+ app_module_default2,
10310
+ main_default
10311
+ ]
10758
10312
  };
10313
+ var api_controllers_default = apiControllers;
10759
10314
 
10760
- // src/api-controllers/templates/todos-module.ts
10761
- var todos_module_default2 = {
10315
+ // src/todo-example/templates/todos-module.ts
10316
+ var todos_module_default = {
10762
10317
  filename: "apps/api/src/todos/todos.module.ts",
10763
10318
  template: `import { Module } from '@nestjs/common';
10764
- import { TodosController } from './todos.controller';
10765
10319
  import { TodosService } from './todos.service';
10766
10320
 
10767
10321
  @Module({
10768
- controllers: [TodosController],
10769
10322
  providers: [TodosService],
10323
+ exports: [TodosService],
10770
10324
  })
10771
10325
  export class TodosModule {}`
10772
10326
  };
10773
10327
 
10774
- // src/api-controllers/index.ts
10775
- var apiControllers = {
10776
- templates: [
10777
- app_controller_default,
10778
- app_controller_spec_default,
10779
- app_module_default2,
10780
- main_default,
10781
- todos_controller_default,
10782
- todos_module_default2
10783
- ]
10784
- };
10785
- var api_controllers_default = apiControllers;
10328
+ // src/todo-example/templates/todos-service.ts
10329
+ var todos_service_default = {
10330
+ filename: "apps/api/src/todos/todos.service.ts",
10331
+ template: `import { Injectable, NotFoundException } from '@nestjs/common';
10332
+
10333
+ export interface Todo {
10334
+ id: number;
10335
+ title: string;
10336
+ done: boolean;
10337
+ }
10338
+
10339
+ @Injectable()
10340
+ export class TodosService {
10341
+ private todos: Todo[] = [];
10342
+ private nextId = 1;
10343
+
10344
+ findAll(): Todo[] {
10345
+ return this.todos;
10346
+ }
10347
+
10348
+ create(title: string): Todo {
10349
+ const todo: Todo = { id: this.nextId++, title, done: false };
10350
+ this.todos.push(todo);
10351
+ return todo;
10352
+ }
10353
+
10354
+ toggle(id: number): Todo {
10355
+ const todo = this.todos.find((t) => t.id === id);
10356
+ if (!todo) throw new NotFoundException(\`Todo \${id} not found\`);
10357
+ todo.done = !todo.done;
10358
+ return todo;
10359
+ }
10360
+
10361
+ remove(id: number): void {
10362
+ const index = this.todos.findIndex((t) => t.id === id);
10363
+ if (index === -1) throw new NotFoundException(\`Todo \${id} not found\`);
10364
+ this.todos.splice(index, 1);
10365
+ }
10366
+ }`
10367
+ };
10368
+
10369
+ // src/todo-example/templates/todos-service-spec.ts
10370
+ var todos_service_spec_default = {
10371
+ filename: "apps/api/src/todos/todos.service.spec.ts",
10372
+ template: `import { Test, TestingModule } from '@nestjs/testing';
10373
+ import { TodosService } from './todos.service';
10374
+
10375
+ describe('TodosService', () => {
10376
+ let service: TodosService;
10377
+
10378
+ beforeEach(async () => {
10379
+ const module: TestingModule = await Test.createTestingModule({
10380
+ providers: [TodosService],
10381
+ }).compile();
10382
+
10383
+ service = module.get<TodosService>(TodosService);
10384
+ });
10385
+
10386
+ it('should be defined', () => {
10387
+ expect(service).toBeDefined();
10388
+ });
10389
+ });`
10390
+ };
10391
+
10392
+ // src/todo-example/templates/todos-controller.ts
10393
+ var todos_controller_default = {
10394
+ filename: "apps/api/src/todos/todos.controller.ts",
10395
+ template: `import {
10396
+ Body,
10397
+ Controller,
10398
+ Delete,
10399
+ Get,
10400
+ Param,
10401
+ Patch,
10402
+ Post,
10403
+ } from '@nestjs/common';
10404
+ import { TodosService } from './todos.service';
10405
+
10406
+ @Controller('todos')
10407
+ export class TodosController {
10408
+ constructor(private readonly todosService: TodosService) {}
10409
+
10410
+ @Get()
10411
+ findAll() {
10412
+ return this.todosService.findAll();
10413
+ }
10414
+
10415
+ @Post()
10416
+ create(@Body('title') title: string) {
10417
+ return this.todosService.create(title);
10418
+ }
10419
+
10420
+ @Patch(':id/toggle')
10421
+ toggle(@Param('id') id: string) {
10422
+ return this.todosService.toggle(Number(id));
10423
+ }
10424
+
10425
+ @Delete(':id')
10426
+ remove(@Param('id') id: string) {
10427
+ return this.todosService.remove(Number(id));
10428
+ }
10429
+ }`
10430
+ };
10431
+
10432
+ // src/todo-example/templates/todos-module-controllers.ts
10433
+ var todos_module_controllers_default = {
10434
+ filename: "apps/api/src/todos/todos.module.ts",
10435
+ template: `import { Module } from '@nestjs/common';
10436
+ import { TodosController } from './todos.controller';
10437
+ import { TodosService } from './todos.service';
10438
+
10439
+ @Module({
10440
+ controllers: [TodosController],
10441
+ providers: [TodosService],
10442
+ })
10443
+ export class TodosModule {}`
10444
+ };
10445
+
10446
+ // src/todo-example/templates/app-module-controllers.ts
10447
+ var app_module_controllers_default = {
10448
+ filename: "apps/api/src/app.module.ts",
10449
+ template: `import { Module } from '@nestjs/common';
10450
+ import { APP_GUARD } from '@nestjs/core';
10451
+ import { AuthGuard, AuthModule } from '@thallesp/nestjs-better-auth';
10452
+ import { auth } from '@<%= projectName %>/auth';
10453
+ import { AppController } from './app.controller';
10454
+ import { AppService } from './app.service';
10455
+ import { TodosModule } from './todos/todos.module';
10456
+
10457
+ @Module({
10458
+ imports: [AuthModule.forRoot({ auth }), TodosModule],
10459
+ controllers: [AppController],
10460
+ providers: [AppService, { provide: APP_GUARD, useClass: AuthGuard }],
10461
+ })
10462
+ export class AppModule {}`
10463
+ };
10464
+
10465
+ // src/todo-example/templates/app-module-di.ts
10466
+ var app_module_di_default = {
10467
+ filename: "apps/api/src/app.module.ts",
10468
+ template: `import { Module } from '@nestjs/common';
10469
+ import { AppService } from './app.service';
10470
+ import { TodosModule } from './todos/todos.module';
10471
+
10472
+ @Module({
10473
+ imports: [TodosModule],
10474
+ providers: [AppService],
10475
+ })
10476
+ export class AppModule {}`
10477
+ };
10478
+
10479
+ // src/todo-example/templates/web-todos-route.ts
10480
+ var web_todos_route_default = {
10481
+ filename: "apps/web/app/api/todos/route.ts",
10482
+ template: `import { NextResponse } from 'next/server';
10483
+ import { inject } from '@/lib/nest';
10484
+ import { TodosService } from '@<%= projectName %>/api';
10485
+
10486
+ export async function GET() {
10487
+ const todos = await inject(TodosService);
10488
+ return NextResponse.json(todos.findAll());
10489
+ }
10490
+
10491
+ export async function POST(req: Request) {
10492
+ const { title } = await req.json();
10493
+ const todos = await inject(TodosService);
10494
+ return NextResponse.json(todos.create(title), { status: 201 });
10495
+ }`
10496
+ };
10497
+
10498
+ // src/todo-example/templates/web-todos-id-route.ts
10499
+ var web_todos_id_route_default = {
10500
+ filename: "apps/web/app/api/todos/[id]/route.ts",
10501
+ template: `import { NextResponse } from 'next/server';
10502
+ import { inject } from '@/lib/nest';
10503
+ import { TodosService } from '@<%= projectName %>/api';
10504
+
10505
+ export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
10506
+ const { id } = await params;
10507
+ const todos = await inject(TodosService);
10508
+ todos.remove(Number(id));
10509
+ return new NextResponse(null, { status: 204 });
10510
+ }`
10511
+ };
10512
+
10513
+ // src/todo-example/templates/web-todos-toggle-route.ts
10514
+ var web_todos_toggle_route_default = {
10515
+ filename: "apps/web/app/api/todos/[id]/toggle/route.ts",
10516
+ template: `import { NextResponse } from 'next/server';
10517
+ import { inject } from '@/lib/nest';
10518
+ import { TodosService } from '@<%= projectName %>/api';
10519
+
10520
+ export async function PATCH(_req: Request, { params }: { params: Promise<{ id: string }> }) {
10521
+ const { id } = await params;
10522
+ const todos = await inject(TodosService);
10523
+ return NextResponse.json(todos.toggle(Number(id)));
10524
+ }`
10525
+ };
10526
+
10527
+ // src/todo-example/templates/web-todo-list.ts
10528
+ var web_todo_list_default = {
10529
+ filename: "apps/web/app/todo-list.tsx",
10530
+ template: `'use client';
10531
+
10532
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
10533
+ import { useForm } from '@tanstack/react-form';
10534
+ import { authClient } from '@/lib/auth-client';
10535
+ import { Button } from '@<%= projectName %>/ui/button';
10536
+
10537
+ interface Todo {
10538
+ id: number;
10539
+ title: string;
10540
+ done: boolean;
10541
+ }
10542
+
10543
+ const api = {
10544
+ getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
10545
+ createTodo: (title: string): Promise<Todo> =>
10546
+ fetch('/api/todos', {
10547
+ method: 'POST',
10548
+ headers: { 'Content-Type': 'application/json' },
10549
+ body: JSON.stringify({ title }),
10550
+ }).then((r) => r.json()),
10551
+ toggleTodo: (id: number): Promise<Todo> =>
10552
+ fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
10553
+ deleteTodo: (id: number): Promise<void> =>
10554
+ fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
10555
+ };
10556
+
10557
+ export function TodoList({
10558
+ session,
10559
+ }: {
10560
+ session: { user: { email: string } };
10561
+ }) {
10562
+ const queryClient = useQueryClient();
10563
+
10564
+ const { data: todos = [], isPending } = useQuery({
10565
+ queryKey: ['todos'],
10566
+ queryFn: api.getTodos,
10567
+ });
10568
+
10569
+ const createMutation = useMutation({
10570
+ mutationFn: api.createTodo,
10571
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10572
+ });
10573
+
10574
+ const toggleMutation = useMutation({
10575
+ mutationFn: api.toggleTodo,
10576
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10577
+ });
10578
+
10579
+ const deleteMutation = useMutation({
10580
+ mutationFn: api.deleteTodo,
10581
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10582
+ });
10583
+
10584
+ const form = useForm({
10585
+ defaultValues: { title: '' },
10586
+ onSubmit: async ({ value }) => {
10587
+ if (!value.title.trim()) return;
10588
+ createMutation.mutate(value.title.trim());
10589
+ form.reset();
10590
+ },
10591
+ });
10592
+
10593
+ return (
10594
+ <>
10595
+ <div className="flex items-center justify-between mb-8">
10596
+ <h1 className="text-2xl font-semibold">Todos</h1>
10597
+ <div className="flex items-center gap-3 text-sm text-gray-400">
10598
+ <span>{session.user.email}</span>
10599
+ <button
10600
+ className="hover:text-gray-100"
10601
+ onClick={() => authClient.signOut()}
10602
+ >
10603
+ Sign out
10604
+ </button>
10605
+ </div>
10606
+ </div>
10607
+
10608
+ <form
10609
+ onSubmit={(e) => {
10610
+ e.preventDefault();
10611
+ form.handleSubmit();
10612
+ }}
10613
+ className="flex gap-2 mb-6"
10614
+ >
10615
+ <form.Field name="title">
10616
+ {(field) => (
10617
+ <input
10618
+ value={field.state.value}
10619
+ onChange={(e) => field.handleChange(e.target.value)}
10620
+ placeholder="New todo\u2026"
10621
+ className="flex h-9 flex-1 rounded-md border border-neutral-700 bg-neutral-800/50 px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-neutral-500 focus-visible:border-neutral-500 focus-visible:ring-2 focus-visible:ring-neutral-500/20"
10622
+ />
10623
+ )}
10624
+ </form.Field>
10625
+
10626
+ <form.Subscribe selector={(s) => s.isSubmitting}>
10627
+ {(isSubmitting) => (
10628
+ <Button type="submit" disabled={isSubmitting || createMutation.isPending}>
10629
+ Add
10630
+ </Button>
10631
+ )}
10632
+ </form.Subscribe>
10633
+ </form>
10634
+
10635
+ {isPending ? (
10636
+ <p className="text-sm text-gray-400">Loading\u2026</p>
10637
+ ) : (
10638
+ <ul className="divide-y divide-neutral-700">
10639
+ {todos.map((todo) => (
10640
+ <li key={todo.id} className="flex items-center gap-3 py-3">
10641
+ <input
10642
+ type="checkbox"
10643
+ checked={todo.done}
10644
+ onChange={() => toggleMutation.mutate(todo.id)}
10645
+ />
10646
+ <span
10647
+ className={\`flex-1 text-sm \${todo.done ? 'line-through text-gray-400' : ''}\`}
10648
+ >
10649
+ {todo.title}
10650
+ </span>
10651
+ <button
10652
+ onClick={() => deleteMutation.mutate(todo.id)}
10653
+ className="text-gray-600 hover:text-red-400 text-lg leading-none"
10654
+ >
10655
+ \xD7
10656
+ </button>
10657
+ </li>
10658
+ ))}
10659
+ </ul>
10660
+ )}
10661
+
10662
+ {!isPending && todos.length === 0 && (
10663
+ <p className="text-sm text-gray-400">No todos yet.</p>
10664
+ )}
10665
+ </>
10666
+ );
10667
+ }`
10668
+ };
10669
+
10670
+ // src/todo-example/templates/web-page.ts
10671
+ var web_page_default = {
10672
+ filename: "apps/web/app/page.tsx",
10673
+ template: `'use client';
10674
+
10675
+ import { useQuery } from '@tanstack/react-query';
10676
+ import { authClient } from '@/lib/auth-client';
10677
+ import { AuthForm } from '@/app/auth-form';
10678
+ import { Link } from '@<%= projectName %>/ui/link';
10679
+ import { Logo } from '@<%= projectName %>/ui/logo';
10680
+ import { TodoList } from '@/app/todo-list';
10681
+
10682
+ export default function Home() {
10683
+ const { data: session, isPending } = authClient.useSession();
10684
+
10685
+ const { data: hello } = useQuery({
10686
+ queryKey: ['hello'],
10687
+ queryFn: () => fetch('/api/hello').then((r) => r.json()),
10688
+ });
10689
+
10690
+ return (
10691
+ <main className="max-w-lg mx-auto min-h-full px-4 py-8 space-y-4">
10692
+ <div className="pb-4 border-b">
10693
+ <p className="font-mono text-sm text-gray-400">apps/web/page.tsx</p>
10694
+ <p className="text-sm text-gray-400">Delete me to get started!</p>
10695
+ </div>
10696
+
10697
+ <div className="flex items-center gap-3 py-2">
10698
+ <Logo className="w-10 h-auto text-foreground" />
10699
+ <div>
10700
+ <h1 className="text-4xl font-black tracking-tight"><%= projectName %></h1>
10701
+ <p className="text-sm text-gray-400 tracking-widest uppercase">
10702
+ Next + Nest = Newt \u{1F49C}
10703
+ </p>
10704
+ </div>
10705
+ </div>
10706
+
10707
+ <div className="rounded-xl border p-6 space-y-1">
10708
+ <p className="text-xs font-mono uppercase tracking-widest text-gray-400">next.js</p>
10709
+ <p className="font-mono text-sm text-gray-400">apps/web/layout.tsx</p>
10710
+ <p className="text-sm text-gray-400">Next.js rendering</p>
10711
+ </div>
10712
+
10713
+ <div className="rounded-xl border p-6 space-y-1">
10714
+ <p className="text-xs font-mono uppercase tracking-widest text-gray-400">nest.js</p>
10715
+ <p className="font-mono text-sm text-gray-400">GET /api/hello</p>
10716
+ <pre className="mt-2 border rounded-md p-3 text-sm bg-neutral-800">
10717
+ <code>{JSON.stringify(hello, null, 2)}</code>
10718
+ </pre>
10719
+ </div>
10720
+
10721
+ <div className="rounded-xl border p-6">
10722
+ <p className="text-xs font-mono uppercase tracking-widest text-gray-400 mb-4">better-auth</p>
10723
+ {isPending ? (
10724
+ <p className="text-sm text-gray-400">Loading\u2026</p>
10725
+ ) : session ? (
10726
+ <TodoList session={session} />
10727
+ ) : (
10728
+ <AuthForm />
10729
+ )}
10730
+ </div>
10731
+
10732
+ <div className="px-2 py-4 text-sm">
10733
+ <p className="mb-2 text-gray-400">Learn more</p>
10734
+ <ul className="list-disc list-inside space-y-2 mt-2">
10735
+ <li>
10736
+ <Link href="https://github.com">GitHub</Link>
10737
+ </li>
10738
+ <li>
10739
+ <Link href="https://nextjs.org">Next.js</Link>
10740
+ </li>
10741
+ <li>
10742
+ <Link href="https://nestjs.com">NestJS</Link>
10743
+ </li>
10744
+ </ul>
10745
+ </div>
10746
+ </main>
10747
+ );
10748
+ }`
10749
+ };
10750
+
10751
+ // src/todo-example/templates/shadcn-todo-list.ts
10752
+ var shadcn_todo_list_default = {
10753
+ filename: "apps/web/app/todo-list.tsx",
10754
+ template: `'use client';
10755
+
10756
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
10757
+ import { useForm } from '@tanstack/react-form';
10758
+ import { authClient } from '@/lib/auth-client';
10759
+ import { Button } from '@<%= projectName %>/ui/button';
10760
+ import { Input } from '@<%= projectName %>/ui/input';
10761
+ import { Checkbox } from '@<%= projectName %>/ui/checkbox';
10762
+
10763
+ interface Todo {
10764
+ id: number;
10765
+ title: string;
10766
+ done: boolean;
10767
+ }
10768
+
10769
+ const api = {
10770
+ getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
10771
+ createTodo: (title: string): Promise<Todo> =>
10772
+ fetch('/api/todos', {
10773
+ method: 'POST',
10774
+ headers: { 'Content-Type': 'application/json' },
10775
+ body: JSON.stringify({ title }),
10776
+ }).then((r) => r.json()),
10777
+ toggleTodo: (id: number): Promise<Todo> =>
10778
+ fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
10779
+ deleteTodo: (id: number): Promise<void> =>
10780
+ fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
10781
+ };
10782
+
10783
+ export function TodoList({
10784
+ session,
10785
+ }: {
10786
+ session: { user: { email: string } };
10787
+ }) {
10788
+ const queryClient = useQueryClient();
10789
+
10790
+ const { data: todos = [], isPending } = useQuery({
10791
+ queryKey: ['todos'],
10792
+ queryFn: api.getTodos,
10793
+ });
10794
+
10795
+ const createMutation = useMutation({
10796
+ mutationFn: api.createTodo,
10797
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10798
+ });
10799
+
10800
+ const toggleMutation = useMutation({
10801
+ mutationFn: api.toggleTodo,
10802
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10803
+ });
10804
+
10805
+ const deleteMutation = useMutation({
10806
+ mutationFn: api.deleteTodo,
10807
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
10808
+ });
10809
+
10810
+ const form = useForm({
10811
+ defaultValues: { title: '' },
10812
+ onSubmit: async ({ value }) => {
10813
+ if (!value.title.trim()) return;
10814
+ createMutation.mutate(value.title.trim());
10815
+ form.reset();
10816
+ },
10817
+ });
10818
+
10819
+ return (
10820
+ <>
10821
+ <div className="flex items-center justify-between mb-6">
10822
+ <h1 className="text-2xl font-semibold">Todos</h1>
10823
+ <div className="flex items-center gap-3 text-sm text-muted-foreground">
10824
+ <span>{session.user.email}</span>
10825
+ <Button
10826
+ variant="ghost"
10827
+ size="sm"
10828
+ onClick={() => authClient.signOut()}
10829
+ >
10830
+ Sign out
10831
+ </Button>
10832
+ </div>
10833
+ </div>
10834
+
10835
+ <form
10836
+ onSubmit={(e) => {
10837
+ e.preventDefault();
10838
+ form.handleSubmit();
10839
+ }}
10840
+ className="flex gap-2 mb-6"
10841
+ >
10842
+ <form.Field name="title">
10843
+ {(field) => (
10844
+ <Input
10845
+ value={field.state.value}
10846
+ onChange={(e) => field.handleChange(e.target.value)}
10847
+ placeholder="New todo\u2026"
10848
+ className="flex-1"
10849
+ />
10850
+ )}
10851
+ </form.Field>
10852
+
10853
+ <form.Subscribe selector={(s) => s.isSubmitting}>
10854
+ {(isSubmitting) => (
10855
+ <Button type="submit" disabled={isSubmitting || createMutation.isPending}>
10856
+ Add
10857
+ </Button>
10858
+ )}
10859
+ </form.Subscribe>
10860
+ </form>
10861
+
10862
+ {isPending ? (
10863
+ <p className="text-sm text-muted-foreground">Loading\u2026</p>
10864
+ ) : (
10865
+ <ul className="divide-y divide-border">
10866
+ {todos.map((todo) => (
10867
+ <li key={todo.id} className="flex items-center gap-3 py-3">
10868
+ <Checkbox
10869
+ checked={todo.done}
10870
+ onCheckedChange={() => toggleMutation.mutate(todo.id)}
10871
+ />
10872
+ <span
10873
+ className={\`flex-1 text-sm \${todo.done ? 'line-through text-muted-foreground' : ''}\`}
10874
+ >
10875
+ {todo.title}
10876
+ </span>
10877
+ <Button
10878
+ variant="ghost"
10879
+ size="sm"
10880
+ onClick={() => deleteMutation.mutate(todo.id)}
10881
+ className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
10882
+ >
10883
+ \xD7
10884
+ </Button>
10885
+ </li>
10886
+ ))}
10887
+ </ul>
10888
+ )}
10889
+
10890
+ {!isPending && todos.length === 0 && (
10891
+ <p className="text-sm text-muted-foreground">No todos yet.</p>
10892
+ )}
10893
+ </>
10894
+ );
10895
+ }`
10896
+ };
10897
+
10898
+ // src/todo-example/templates/shadcn-page.ts
10899
+ var shadcn_page_default = {
10900
+ filename: "apps/web/app/page.tsx",
10901
+ template: `'use client';
10902
+
10903
+ import { useQuery } from '@tanstack/react-query';
10904
+ import { authClient } from '@/lib/auth-client';
10905
+ import { AuthForm } from '@/app/auth-form';
10906
+ import { Link } from '@<%= projectName %>/ui/link';
10907
+ import { Logo } from '@<%= projectName %>/ui/logo';
10908
+ import { TodoList } from '@/app/todo-list';
10909
+ import { ModeToggle } from '@<%= projectName %>/ui/mode-toggle';
10910
+ import { Card, CardContent, CardHeader, CardTitle } from '@<%= projectName %>/ui/card';
10911
+
10912
+ export default function Home() {
10913
+ const { data: session, isPending } = authClient.useSession();
10914
+
10915
+ const { data: hello } = useQuery({
10916
+ queryKey: ['hello'],
10917
+ queryFn: () => fetch('/api/hello').then((r) => r.json()),
10918
+ });
10919
+
10920
+ return (
10921
+ <main className="max-w-lg mx-auto min-h-full px-4 py-8 space-y-4">
10922
+ <div className="pb-4 border-b flex items-start justify-between">
10923
+ <div>
10924
+ <p className="font-mono text-sm text-muted-foreground">apps/web/page.tsx</p>
10925
+ <p className="text-sm text-muted-foreground">Delete me to get started!</p>
10926
+ </div>
10927
+ <ModeToggle />
10928
+ </div>
10929
+
10930
+ <div className="flex items-center gap-3 py-2">
10931
+ <Logo className="w-10 h-auto text-foreground" />
10932
+ <div>
10933
+ <h1 className="text-4xl font-black tracking-tight"><%= projectName %></h1>
10934
+ <p className="text-sm text-muted-foreground tracking-widest uppercase">
10935
+ Next + Nest = Newt \u{1F49C}
10936
+ </p>
10937
+ </div>
10938
+ </div>
10939
+
10940
+ <Card>
10941
+ <CardHeader>
10942
+ <CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
10943
+ next.js
10944
+ </CardTitle>
10945
+ </CardHeader>
10946
+ <CardContent>
10947
+ <p className="font-mono text-sm text-muted-foreground">apps/web/layout.tsx</p>
10948
+ <p className="text-sm text-muted-foreground">Next.js rendering</p>
10949
+ </CardContent>
10950
+ </Card>
10951
+
10952
+ <Card>
10953
+ <CardHeader>
10954
+ <CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
10955
+ nest.js
10956
+ </CardTitle>
10957
+ </CardHeader>
10958
+ <CardContent>
10959
+ <p className="font-mono text-sm text-muted-foreground">GET /api/hello</p>
10960
+ <pre className="mt-2 rounded-md border p-3 text-sm bg-muted/50">
10961
+ <code>{JSON.stringify(hello, null, 2)}</code>
10962
+ </pre>
10963
+ </CardContent>
10964
+ </Card>
10965
+
10966
+ <Card>
10967
+ <CardHeader>
10968
+ <CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
10969
+ better-auth
10970
+ </CardTitle>
10971
+ </CardHeader>
10972
+ <CardContent>
10973
+ {isPending ? (
10974
+ <p className="text-sm text-muted-foreground">Loading\u2026</p>
10975
+ ) : session ? (
10976
+ <TodoList session={session} />
10977
+ ) : (
10978
+ <AuthForm />
10979
+ )}
10980
+ </CardContent>
10981
+ </Card>
10982
+
10983
+ <div className="px-2 py-4 text-sm">
10984
+ <p className="mb-2 text-muted-foreground">Learn more</p>
10985
+ <ul className="list-disc list-inside space-y-2 mt-2">
10986
+ <li>
10987
+ <Link href="https://github.com">GitHub</Link>
10988
+ </li>
10989
+ <li>
10990
+ <Link href="https://nextjs.org">Next.js</Link>
10991
+ </li>
10992
+ <li>
10993
+ <Link href="https://nestjs.com">NestJS</Link>
10994
+ </li>
10995
+ </ul>
10996
+ </div>
10997
+ </main>
10998
+ );
10999
+ }`
11000
+ };
11001
+
11002
+ // src/todo-example/index.ts
11003
+ var todoExampleApi = {
11004
+ templates: [todos_module_default, todos_service_default, todos_service_spec_default]
11005
+ };
11006
+ var todoExampleControllers = {
11007
+ templates: [todos_controller_default, todos_module_controllers_default, app_module_controllers_default]
11008
+ };
11009
+ var todoExampleDi = {
11010
+ templates: [web_todos_route_default, web_todos_id_route_default, web_todos_toggle_route_default, app_module_di_default]
11011
+ };
11012
+ var todoExampleWeb = {
11013
+ templates: [web_todo_list_default, web_page_default]
11014
+ };
11015
+ var todoExampleShadcn = {
11016
+ templates: [shadcn_todo_list_default, shadcn_page_default]
11017
+ };
10786
11018
 
10787
11019
  // src/index.ts
10788
11020
  var staticDir = new URL("./static/", import.meta.url);
@@ -10805,7 +11037,12 @@ var templates = {
10805
11037
  deploymentCustomServer: single_process_custom_server_default,
10806
11038
  deploymentSpa: single_process_static_export_default,
10807
11039
  nestDiOnly: nest_di_only_default,
10808
- apiControllers: api_controllers_default
11040
+ apiControllers: api_controllers_default,
11041
+ todoExampleApi,
11042
+ todoExampleControllers,
11043
+ todoExampleDi,
11044
+ todoExampleWeb,
11045
+ todoExampleShadcn
10809
11046
  };
10810
11047
  export {
10811
11048
  getStaticFilePath,