@kood/claude-code 0.3.6 → 0.3.8
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 +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +124 -124
- package/templates/.claude/agents/dependency-manager.md +85 -85
- package/templates/.claude/agents/deployment-validator.md +56 -56
- package/templates/.claude/agents/git-operator.md +64 -64
- package/templates/.claude/agents/implementation-executor.md +95 -95
- package/templates/.claude/agents/ko-to-en-translator.md +74 -0
- package/templates/.claude/agents/lint-fixer.md +78 -78
- package/templates/.claude/agents/refactor-advisor.md +122 -122
- package/templates/.claude/commands/agent-creator.md +185 -185
- package/templates/.claude/commands/bug-fix.md +193 -193
- package/templates/.claude/commands/command-creator.md +54 -54
- package/templates/.claude/commands/docs-creator.md +57 -57
- package/templates/.claude/commands/docs-refactor.md +26 -26
- package/templates/.claude/commands/execute.md +12 -12
- package/templates/.claude/commands/git-all.md +32 -32
- package/templates/.claude/commands/git-session.md +42 -42
- package/templates/.claude/commands/git.md +34 -34
- package/templates/.claude/commands/lint-fix.md +138 -138
- package/templates/.claude/commands/lint-init.md +61 -61
- package/templates/.claude/commands/plan.md +260 -260
- package/templates/.claude/commands/prd.md +24 -24
- package/templates/.claude/commands/pre-deploy.md +109 -109
- package/templates/.claude/commands/refactor.md +147 -147
- package/templates/.claude/commands/version-update.md +17 -17
- package/templates/hono/CLAUDE.md +27 -27
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +228 -0
- package/templates/nextjs/docs/design.md +558 -0
- package/templates/nextjs/docs/guides/conventions.md +343 -0
- package/templates/nextjs/docs/guides/getting-started.md +367 -0
- package/templates/nextjs/docs/guides/routes.md +342 -0
- package/templates/nextjs/docs/library/better-auth/index.md +541 -0
- package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
- package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
- package/templates/nextjs/docs/library/nextjs/index.md +291 -0
- package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
- package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
- package/templates/nextjs/docs/library/prisma/config.md +77 -0
- package/templates/nextjs/docs/library/prisma/crud.md +90 -0
- package/templates/nextjs/docs/library/prisma/index.md +73 -0
- package/templates/nextjs/docs/library/prisma/relations.md +69 -0
- package/templates/nextjs/docs/library/prisma/schema.md +98 -0
- package/templates/nextjs/docs/library/prisma/setup.md +49 -0
- package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
- package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
- package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
- package/templates/nextjs/docs/library/zod/index.md +56 -0
- package/templates/nextjs/docs/library/zod/transforms.md +51 -0
- package/templates/nextjs/docs/library/zod/validation.md +70 -0
- package/templates/npx/CLAUDE.md +37 -37
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +53 -49
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +63 -35
- package/templates/tanstack-start/docs/guides/routes.md +61 -42
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/better-auth/index.md +68 -68
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/index.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/t3-env/index.md +21 -160
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +15 -15
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/index.md +8 -8
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Prisma - Relation Queries
|
|
2
|
+
|
|
3
|
+
## Nested Create
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
const user = await prisma.user.create({
|
|
7
|
+
data: {
|
|
8
|
+
email: 'user@prisma.io',
|
|
9
|
+
posts: { create: [{ title: 'Post 1' }, { title: 'Post 2' }] },
|
|
10
|
+
},
|
|
11
|
+
include: { posts: true },
|
|
12
|
+
})
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Connect Relations
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// connect - connect existing
|
|
19
|
+
author: { connect: { id: 1 } }
|
|
20
|
+
|
|
21
|
+
// connectOrCreate - connect if exists, create if not
|
|
22
|
+
categories: { connectOrCreate: { where: { name: 'Tech' }, create: { name: 'Tech' } } }
|
|
23
|
+
|
|
24
|
+
// disconnect - disconnect relation
|
|
25
|
+
author: { disconnect: true }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Include Relations
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// include
|
|
32
|
+
const users = await prisma.user.findMany({ include: { posts: true, profile: true } })
|
|
33
|
+
|
|
34
|
+
// nested
|
|
35
|
+
include: { posts: { include: { categories: true } } }
|
|
36
|
+
|
|
37
|
+
// filter + sort
|
|
38
|
+
include: { posts: { where: { published: true }, orderBy: { createdAt: 'desc' }, take: 5 } }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Filter by Relations
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// some - at least one matches
|
|
45
|
+
where: { posts: { some: { published: true } } }
|
|
46
|
+
|
|
47
|
+
// every - all match
|
|
48
|
+
where: { posts: { every: { published: true } } }
|
|
49
|
+
|
|
50
|
+
// none - none match
|
|
51
|
+
where: { posts: { none: { published: false } } }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Count
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
include: { _count: { select: { posts: true } } }
|
|
58
|
+
// result: { _count: { posts: 5 } }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Nested Update/Delete
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// updateMany
|
|
65
|
+
posts: { updateMany: { where: { published: false }, data: { published: true } } }
|
|
66
|
+
|
|
67
|
+
// deleteMany
|
|
68
|
+
posts: { deleteMany: { where: { published: false } } }
|
|
69
|
+
```
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Prisma - Schema Definition (Multi-File)
|
|
2
|
+
|
|
3
|
+
## ⚠️ Required Rules
|
|
4
|
+
|
|
5
|
+
1. **Use Multi-File structure**
|
|
6
|
+
2. **Add comments to all elements** (files, models, fields, enums)
|
|
7
|
+
|
|
8
|
+
## Structure
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
prisma/schema/
|
|
12
|
+
├── +base.prisma # datasource, generator
|
|
13
|
+
├── +enum.prisma # all enums
|
|
14
|
+
├── user.prisma # User model
|
|
15
|
+
└── post.prisma # Post model
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
```prisma
|
|
21
|
+
// +base.prisma
|
|
22
|
+
datasource db {
|
|
23
|
+
provider = "postgresql"
|
|
24
|
+
url = env("DATABASE_URL")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
generator client {
|
|
28
|
+
provider = "prisma-client"
|
|
29
|
+
output = "../generated/prisma"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// +enum.prisma
|
|
33
|
+
enum Role {
|
|
34
|
+
USER // regular user
|
|
35
|
+
ADMIN // administrator
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// user.prisma
|
|
39
|
+
model User {
|
|
40
|
+
id Int @id @default(autoincrement())
|
|
41
|
+
email String @unique // login email
|
|
42
|
+
name String? // display name
|
|
43
|
+
role Role @default(USER)
|
|
44
|
+
posts Post[] // authored posts (1:N)
|
|
45
|
+
profile Profile? // user profile (1:1)
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
updatedAt DateTime @updatedAt
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Relation Types
|
|
52
|
+
|
|
53
|
+
```prisma
|
|
54
|
+
// 1:1
|
|
55
|
+
model Profile {
|
|
56
|
+
id Int @id @default(autoincrement())
|
|
57
|
+
user User @relation(fields: [userId], references: [id])
|
|
58
|
+
userId Int @unique
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 1:N
|
|
62
|
+
model Post {
|
|
63
|
+
author User @relation(fields: [authorId], references: [id])
|
|
64
|
+
authorId Int
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// M:N (implicit)
|
|
68
|
+
model Post { categories Category[] }
|
|
69
|
+
model Category { posts Post[] }
|
|
70
|
+
|
|
71
|
+
// M:N (explicit)
|
|
72
|
+
model CategoriesOnPosts {
|
|
73
|
+
post Post @relation(fields: [postId], references: [id])
|
|
74
|
+
postId Int
|
|
75
|
+
category Category @relation(fields: [categoryId], references: [id])
|
|
76
|
+
categoryId Int
|
|
77
|
+
@@id([postId, categoryId])
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Others
|
|
82
|
+
|
|
83
|
+
```prisma
|
|
84
|
+
// Optional relation
|
|
85
|
+
author User? @relation(fields: [authorId], references: [id])
|
|
86
|
+
authorId Int?
|
|
87
|
+
|
|
88
|
+
// Index
|
|
89
|
+
@@index([authorId])
|
|
90
|
+
@@index([createdAt])
|
|
91
|
+
|
|
92
|
+
// Composite key
|
|
93
|
+
@@id([postId, tagId])
|
|
94
|
+
|
|
95
|
+
// Mapping
|
|
96
|
+
@map("user_id")
|
|
97
|
+
@@map("users")
|
|
98
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Prisma - Installation and Setup
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
yarn add @prisma/client@7
|
|
7
|
+
yarn add -D prisma@7
|
|
8
|
+
npx prisma init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## v6 → v7 Upgrade
|
|
12
|
+
|
|
13
|
+
```prisma
|
|
14
|
+
// v6 (old)
|
|
15
|
+
generator client {
|
|
16
|
+
provider = "prisma-client-js"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// v7 (required)
|
|
20
|
+
generator client {
|
|
21
|
+
provider = "prisma-client"
|
|
22
|
+
output = "../generated/prisma"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Prisma Client
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// lib/prisma.ts
|
|
30
|
+
import { PrismaClient } from './generated/prisma'
|
|
31
|
+
|
|
32
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
33
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient({ log: ['query'] })
|
|
34
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## TanStack Start Integration
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
41
|
+
import { prisma } from '@/lib/prisma'
|
|
42
|
+
|
|
43
|
+
export const getUsers = createServerFn({ method: 'GET' })
|
|
44
|
+
.handler(async () => prisma.user.findMany({ include: { posts: true } }))
|
|
45
|
+
|
|
46
|
+
export const createUser = createServerFn({ method: 'POST' })
|
|
47
|
+
.inputValidator((data: { email: string; name: string }) => data)
|
|
48
|
+
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
49
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Prisma - Transactions
|
|
2
|
+
|
|
3
|
+
## Array-based Transaction
|
|
4
|
+
|
|
5
|
+
Rolls back all if any operation fails.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
const deletePosts = prisma.post.deleteMany({ where: { authorId: 7 } })
|
|
9
|
+
const deleteUser = prisma.user.delete({ where: { id: 7 } })
|
|
10
|
+
await prisma.$transaction([deletePosts, deleteUser])
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Interactive Transaction
|
|
14
|
+
|
|
15
|
+
For complex logic and conditional processing.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const result = await prisma.$transaction(async (tx) => {
|
|
19
|
+
const sender = await tx.account.findUnique({ where: { id: senderId } })
|
|
20
|
+
if (!sender || sender.balance < amount) throw new Error('Insufficient balance')
|
|
21
|
+
|
|
22
|
+
await tx.account.update({ where: { id: senderId }, data: { balance: { decrement: amount } } })
|
|
23
|
+
await tx.account.update({ where: { id: recipientId }, data: { balance: { increment: amount } } })
|
|
24
|
+
|
|
25
|
+
return { success: true }
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
await prisma.$transaction(async (tx) => { ... }, {
|
|
33
|
+
maxWait: 5000, // maximum wait time (ms)
|
|
34
|
+
timeout: 10000, // timeout (ms)
|
|
35
|
+
isolationLevel: 'Serializable', // ReadUncommitted | ReadCommitted | RepeatableRead | Serializable
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Error Handling
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
try {
|
|
43
|
+
await prisma.$transaction(async (tx) => {
|
|
44
|
+
await tx.user.create({ data: { email } })
|
|
45
|
+
if (someCondition) throw new Error('Rollback')
|
|
46
|
+
})
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// entire transaction rolled back
|
|
49
|
+
}
|
|
50
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# TanStack Query
|
|
2
|
+
|
|
3
|
+
> 5.x | React Data Fetching
|
|
4
|
+
|
|
5
|
+
@use-query.md
|
|
6
|
+
@use-mutation.md
|
|
7
|
+
@invalidation.md
|
|
8
|
+
@optimistic-updates.md
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<quick_reference>
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// useQuery
|
|
16
|
+
const { data, isLoading, error } = useQuery({
|
|
17
|
+
queryKey: ['users'],
|
|
18
|
+
queryFn: () => getUsers(),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// useMutation + invalidation
|
|
22
|
+
const queryClient = useQueryClient()
|
|
23
|
+
const mutation = useMutation({
|
|
24
|
+
mutationFn: createUser,
|
|
25
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
|
|
26
|
+
})
|
|
27
|
+
mutation.mutate({ email, name })
|
|
28
|
+
|
|
29
|
+
// Optimistic Update
|
|
30
|
+
const mutation = useMutation({
|
|
31
|
+
mutationFn: updateUser,
|
|
32
|
+
onMutate: async (newUser) => {
|
|
33
|
+
await queryClient.cancelQueries({ queryKey: ['users'] })
|
|
34
|
+
const previous = queryClient.getQueryData(['users'])
|
|
35
|
+
queryClient.setQueryData(['users'], (old) => [...old, newUser])
|
|
36
|
+
return { previous }
|
|
37
|
+
},
|
|
38
|
+
onError: (err, newUser, context) => {
|
|
39
|
+
queryClient.setQueryData(['users'], context.previous)
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Setup
|
|
44
|
+
const queryClient = new QueryClient({
|
|
45
|
+
defaultOptions: {
|
|
46
|
+
queries: {
|
|
47
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
48
|
+
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
49
|
+
retry: 3,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const App = () => (
|
|
55
|
+
<QueryClientProvider client={queryClient}>
|
|
56
|
+
<YourApp />
|
|
57
|
+
</QueryClientProvider>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// Query Keys
|
|
61
|
+
['todos'] // simple
|
|
62
|
+
['todo', { id: 5 }] // with parameter
|
|
63
|
+
['todos', 'list', { filters }] // hierarchical
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
</quick_reference>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# TanStack Query - Query Invalidation
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
const queryClient = useQueryClient()
|
|
7
|
+
|
|
8
|
+
// Single
|
|
9
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
10
|
+
|
|
11
|
+
// Multiple
|
|
12
|
+
await Promise.all([
|
|
13
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
14
|
+
queryClient.invalidateQueries({ queryKey: ['reminders'] }),
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
// All
|
|
18
|
+
queryClient.invalidateQueries()
|
|
19
|
+
|
|
20
|
+
// Options
|
|
21
|
+
queryClient.invalidateQueries({
|
|
22
|
+
queryKey: ['posts'],
|
|
23
|
+
exact: true, // exact key match only
|
|
24
|
+
refetchType: 'active', // 'active' | 'inactive' | 'all' | 'none'
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Query key matching
|
|
28
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] }) // prefix matching
|
|
29
|
+
queryClient.invalidateQueries({ queryKey: ['todos', 'list'], exact: true }) // exact matching
|
|
30
|
+
|
|
31
|
+
// With mutation
|
|
32
|
+
useMutation({
|
|
33
|
+
mutationFn: addTodo,
|
|
34
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
35
|
+
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), // regardless of success/failure
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Direct update vs invalidation
|
|
39
|
+
queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) // faster
|
|
40
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] }) // guarantees server data
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
</patterns>
|
|
44
|
+
|
|
45
|
+
<options>
|
|
46
|
+
|
|
47
|
+
| refetchType | Description |
|
|
48
|
+
|-------------|-------------|
|
|
49
|
+
| active | Refetch only queries currently being rendered (default) |
|
|
50
|
+
| inactive | Refetch only inactive queries |
|
|
51
|
+
| all | Refetch all matching queries |
|
|
52
|
+
| none | Invalidate only, no refetch |
|
|
53
|
+
|
|
54
|
+
</options>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# TanStack Query - Optimistic Updates
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
// Add
|
|
7
|
+
useMutation({
|
|
8
|
+
mutationFn: addTodo,
|
|
9
|
+
onMutate: async (newTodo) => {
|
|
10
|
+
await queryClient.cancelQueries({ queryKey: ['todos'] })
|
|
11
|
+
const previousTodos = queryClient.getQueryData(['todos'])
|
|
12
|
+
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
|
|
13
|
+
return { previousTodos }
|
|
14
|
+
},
|
|
15
|
+
onError: (err, newTodo, context) => {
|
|
16
|
+
queryClient.setQueryData(['todos'], context.previousTodos)
|
|
17
|
+
},
|
|
18
|
+
onSettled: () => {
|
|
19
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// Delete
|
|
24
|
+
useMutation({
|
|
25
|
+
mutationFn: deleteTodo,
|
|
26
|
+
onMutate: async (todoId) => {
|
|
27
|
+
await queryClient.cancelQueries({ queryKey: ['todos'] })
|
|
28
|
+
const previousTodos = queryClient.getQueryData(['todos'])
|
|
29
|
+
queryClient.setQueryData(['todos'], (old) =>
|
|
30
|
+
old.filter((todo) => todo.id !== todoId)
|
|
31
|
+
)
|
|
32
|
+
return { previousTodos }
|
|
33
|
+
},
|
|
34
|
+
onError: (err, todoId, context) => {
|
|
35
|
+
queryClient.setQueryData(['todos'], context.previousTodos)
|
|
36
|
+
},
|
|
37
|
+
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Toggle
|
|
41
|
+
useMutation({
|
|
42
|
+
mutationFn: toggleTodo,
|
|
43
|
+
onMutate: async (todoId) => {
|
|
44
|
+
await queryClient.cancelQueries({ queryKey: ['todos'] })
|
|
45
|
+
const previousTodos = queryClient.getQueryData(['todos'])
|
|
46
|
+
queryClient.setQueryData(['todos'], (old) =>
|
|
47
|
+
old.map((todo) =>
|
|
48
|
+
todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
return { previousTodos }
|
|
52
|
+
},
|
|
53
|
+
onError: (err, todoId, context) => {
|
|
54
|
+
queryClient.setQueryData(['todos'], context.previousTodos)
|
|
55
|
+
},
|
|
56
|
+
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Single item
|
|
60
|
+
useMutation({
|
|
61
|
+
mutationFn: updateTodo,
|
|
62
|
+
onMutate: async (newTodo) => {
|
|
63
|
+
await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] })
|
|
64
|
+
const previousTodo = queryClient.getQueryData(['todos', newTodo.id])
|
|
65
|
+
queryClient.setQueryData(['todos', newTodo.id], newTodo)
|
|
66
|
+
return { previousTodo, newTodo }
|
|
67
|
+
},
|
|
68
|
+
onError: (err, newTodo, context) => {
|
|
69
|
+
queryClient.setQueryData(['todos', context.newTodo.id], context.previousTodo)
|
|
70
|
+
},
|
|
71
|
+
onSettled: (newTodo) => {
|
|
72
|
+
queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] })
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
</patterns>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# TanStack Query - useMutation
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
// Basic
|
|
7
|
+
const queryClient = useQueryClient()
|
|
8
|
+
const mutation = useMutation({
|
|
9
|
+
mutationFn: postTodo,
|
|
10
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
11
|
+
})
|
|
12
|
+
mutation.mutate({ title: 'New Todo' })
|
|
13
|
+
|
|
14
|
+
// Callbacks
|
|
15
|
+
useMutation({
|
|
16
|
+
mutationFn: updateTodo,
|
|
17
|
+
onMutate: async (newTodo) => {
|
|
18
|
+
// before mutation starts (for optimistic updates)
|
|
19
|
+
return { previousData } // passed to context
|
|
20
|
+
},
|
|
21
|
+
onSuccess: (data, variables, context) => {},
|
|
22
|
+
onError: (error, variables, context) => {},
|
|
23
|
+
onSettled: (data, error, variables, context) => {
|
|
24
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// mutate vs mutateAsync
|
|
29
|
+
mutation.mutate(data, {
|
|
30
|
+
onSuccess: (result) => console.log(result),
|
|
31
|
+
onError: (error) => console.log(error),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const result = await mutation.mutateAsync(data)
|
|
36
|
+
} catch (error) { ... }
|
|
37
|
+
|
|
38
|
+
// Cache update
|
|
39
|
+
useMutation({
|
|
40
|
+
mutationFn: patchTodo,
|
|
41
|
+
onSuccess: (data) => {
|
|
42
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
43
|
+
queryClient.setQueryData(['todo', { id: data.id }], data)
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
</patterns>
|
|
49
|
+
|
|
50
|
+
<returns>
|
|
51
|
+
|
|
52
|
+
| Property | Description |
|
|
53
|
+
|----------|-------------|
|
|
54
|
+
| data | Mutation result |
|
|
55
|
+
| error | Error object |
|
|
56
|
+
| isPending | Execution in progress |
|
|
57
|
+
| isSuccess/isError | Status flags |
|
|
58
|
+
| mutate | Execute (async) |
|
|
59
|
+
| mutateAsync | Execute (Promise) |
|
|
60
|
+
| reset | Reset state |
|
|
61
|
+
| variables | Passed variables |
|
|
62
|
+
|
|
63
|
+
</returns>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# TanStack Query - useQuery
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
// Basic
|
|
7
|
+
const { data, isLoading, error } = useQuery({
|
|
8
|
+
queryKey: ['todos'],
|
|
9
|
+
queryFn: getTodos,
|
|
10
|
+
})
|
|
11
|
+
if (isLoading) return <div>Loading...</div>
|
|
12
|
+
if (error) return <div>Error: {error.message}</div>
|
|
13
|
+
|
|
14
|
+
// Options
|
|
15
|
+
useQuery({
|
|
16
|
+
queryKey: ['todos'],
|
|
17
|
+
queryFn: fetchTodos,
|
|
18
|
+
staleTime: 1000 * 60 * 5, // time to keep data fresh
|
|
19
|
+
gcTime: 1000 * 60 * 30, // garbage collection time
|
|
20
|
+
refetchOnWindowFocus: true, // refetch on window focus
|
|
21
|
+
refetchInterval: 1000 * 60, // auto-refetch interval
|
|
22
|
+
retry: 3, // retry attempts
|
|
23
|
+
enabled: !!userId, // conditional execution
|
|
24
|
+
initialData: [], // initial data
|
|
25
|
+
select: (data) => data.filter(t => t.done), // data transformation
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// With parameter
|
|
29
|
+
useQuery({
|
|
30
|
+
queryKey: ['todo', todoId],
|
|
31
|
+
queryFn: () => fetchTodoById(todoId),
|
|
32
|
+
enabled: !!todoId,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Dependent queries
|
|
36
|
+
const { data: user } = useQuery({ queryKey: ['user', userId], queryFn: ... })
|
|
37
|
+
const { data: posts } = useQuery({
|
|
38
|
+
queryKey: ['posts', user?.id],
|
|
39
|
+
queryFn: () => fetchPostsByUserId(user!.id),
|
|
40
|
+
enabled: !!user?.id,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Parallel
|
|
44
|
+
const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
|
|
45
|
+
const postsQuery = useQuery({ queryKey: ['posts'], queryFn: fetchPosts })
|
|
46
|
+
|
|
47
|
+
// Dynamic parallel
|
|
48
|
+
const userQueries = useQueries({
|
|
49
|
+
queries: userIds.map((id) => ({
|
|
50
|
+
queryKey: ['user', id],
|
|
51
|
+
queryFn: () => fetchUserById(id),
|
|
52
|
+
})),
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
</patterns>
|
|
57
|
+
|
|
58
|
+
<returns>
|
|
59
|
+
|
|
60
|
+
| Property | Description |
|
|
61
|
+
|----------|-------------|
|
|
62
|
+
| data | Query result |
|
|
63
|
+
| error | Error object |
|
|
64
|
+
| isLoading | First loading state |
|
|
65
|
+
| isFetching | Background fetching state |
|
|
66
|
+
| isError/isSuccess | Status flags |
|
|
67
|
+
| refetch | Manual refetch function |
|
|
68
|
+
| status | 'pending' \| 'error' \| 'success' |
|
|
69
|
+
|
|
70
|
+
</returns>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Zod - Complex Types
|
|
2
|
+
|
|
3
|
+
<patterns>
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Object
|
|
7
|
+
const UserSchema = z.object({
|
|
8
|
+
name: z.string(),
|
|
9
|
+
email: z.email(),
|
|
10
|
+
age: z.number().optional(),
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
UserSchema.partial() // all fields optional
|
|
14
|
+
UserSchema.required() // all fields required
|
|
15
|
+
UserSchema.pick({ name: true }) // specific fields only
|
|
16
|
+
UserSchema.omit({ email: true }) // exclude specific fields
|
|
17
|
+
UserSchema.extend({ role: z.enum(['admin', 'user']) })
|
|
18
|
+
UserSchema.merge(AnotherSchema)
|
|
19
|
+
|
|
20
|
+
z.strictObject({ name: z.string() }) // v4: additional keys throw error
|
|
21
|
+
z.looseObject({ name: z.string() }) // v4: additional keys pass through
|
|
22
|
+
|
|
23
|
+
// Array/Tuple
|
|
24
|
+
z.array(z.string())
|
|
25
|
+
z.array(z.number()).min(1).max(10).length(5).nonempty()
|
|
26
|
+
z.tuple([z.string(), z.number()]) // [string, number]
|
|
27
|
+
|
|
28
|
+
// Union
|
|
29
|
+
z.union([z.string(), z.number()])
|
|
30
|
+
z.string().or(z.number())
|
|
31
|
+
|
|
32
|
+
z.discriminatedUnion('type', [
|
|
33
|
+
z.object({ type: z.literal('circle'), radius: z.number() }),
|
|
34
|
+
z.object({ type: z.literal('rectangle'), width: z.number(), height: z.number() }),
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
// Enum
|
|
38
|
+
const Status = z.enum(['pending', 'done', 'cancelled'])
|
|
39
|
+
type Status = z.infer<typeof Status> // 'pending' | 'done' | 'cancelled'
|
|
40
|
+
|
|
41
|
+
enum Fruits { Apple, Banana }
|
|
42
|
+
z.nativeEnum(Fruits)
|
|
43
|
+
|
|
44
|
+
// Record/Map/Set
|
|
45
|
+
z.record(z.string(), z.object({ name: z.string() })) // { [key: string]: { name: string } }
|
|
46
|
+
z.map(z.string(), z.number()) // Map<string, number>
|
|
47
|
+
z.set(z.number()) // Set<number>
|
|
48
|
+
|
|
49
|
+
// Recursive
|
|
50
|
+
type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
|
|
51
|
+
|
|
52
|
+
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
|
53
|
+
z.union([
|
|
54
|
+
z.string(), z.number(), z.boolean(), z.null(),
|
|
55
|
+
z.array(jsonSchema),
|
|
56
|
+
z.record(jsonSchema)
|
|
57
|
+
])
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
</patterns>
|