@nextsparkjs/ai-workflow 0.1.0-beta.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,608 @@
1
+ ---
2
+ name: tanstack-query
3
+ description: |
4
+ TanStack Query (React Query) patterns for data fetching in this Next.js application.
5
+ Covers useQuery, useMutation, optimistic updates, cache invalidation, and anti-patterns.
6
+ Use this skill when implementing data fetching or state management with server data.
7
+ allowed-tools: Read, Glob, Grep
8
+ version: 1.0.0
9
+ ---
10
+
11
+ # TanStack Query Skill
12
+
13
+ Data fetching patterns and best practices with TanStack Query (React Query).
14
+
15
+ ## Architecture Overview
16
+
17
+ ```
18
+ core/providers/
19
+ └── query-provider.tsx # QueryClient configuration
20
+
21
+ core/hooks/
22
+ ├── useEntityQuery.ts # Generic entity query hook
23
+ ├── useEntityMutations.ts # CRUD mutations with optimistic updates
24
+ └── useUserProfile.ts # Example simple mutation
25
+ ```
26
+
27
+ ## When to Use This Skill
28
+
29
+ - Fetching data from API endpoints
30
+ - Implementing CRUD operations
31
+ - Managing server state and caching
32
+ - Implementing optimistic updates
33
+ - Avoiding useEffect anti-patterns
34
+
35
+ ## Query Client Setup
36
+
37
+ ```typescript
38
+ // core/providers/query-provider.tsx
39
+ export function QueryProvider({ children }: { children: React.ReactNode }) {
40
+ const [queryClient] = useState(
41
+ () =>
42
+ new QueryClient({
43
+ defaultOptions: {
44
+ queries: {
45
+ staleTime: 60 * 1000, // 1 minute default
46
+ refetchOnWindowFocus: false, // Disabled
47
+ },
48
+ },
49
+ })
50
+ )
51
+
52
+ return (
53
+ <QueryClientProvider client={queryClient}>
54
+ {children}
55
+ {process.env.NEXT_PUBLIC_RQ_DEVTOOLS === 'true' && (
56
+ <ReactQueryDevtools initialIsOpen={false} />
57
+ )}
58
+ </QueryClientProvider>
59
+ )
60
+ }
61
+ ```
62
+
63
+ **Key Configuration:**
64
+
65
+ | Setting | Value | Reason |
66
+ |---------|-------|--------|
67
+ | `staleTime` | 60 seconds | Prevents excessive refetching |
68
+ | `refetchOnWindowFocus` | false | Manual control over refetching |
69
+ | `gcTime` | 5 minutes (default) | Cache cleanup |
70
+
71
+ ## Query Key Conventions
72
+
73
+ Query keys should be hierarchical arrays for proper cache management:
74
+
75
+ ```typescript
76
+ // Pattern: ['domain', 'resource', filters, id]
77
+
78
+ // Entity list with filters
79
+ ['entity', 'tasks', { page: 1, search: 'test', status: 'active' }]
80
+
81
+ // Single entity
82
+ ['entity', 'tasks', 'task-123']
83
+
84
+ // Single entity with options
85
+ ['entity', 'tasks', 'task-123', { includeChildren: true }]
86
+
87
+ // User-specific data
88
+ ['user-profile']
89
+ ['user-settings', 'notifications']
90
+
91
+ // Admin data
92
+ ['superadmin-users', search, roleFilter, statusFilter, page]
93
+ ```
94
+
95
+ ## useQuery Patterns
96
+
97
+ ### Basic Query
98
+
99
+ ```typescript
100
+ import { useQuery } from '@tanstack/react-query'
101
+
102
+ function useTaskList(filters: TaskFilters) {
103
+ return useQuery({
104
+ queryKey: ['entity', 'tasks', filters],
105
+ queryFn: async () => {
106
+ const params = new URLSearchParams({
107
+ page: String(filters.page),
108
+ limit: String(filters.limit),
109
+ ...(filters.status && { status: filters.status }),
110
+ ...(filters.search && { search: filters.search }),
111
+ })
112
+
113
+ const response = await fetch(`/api/v1/tasks?${params}`)
114
+ if (!response.ok) {
115
+ throw new Error('Failed to fetch tasks')
116
+ }
117
+ return response.json()
118
+ },
119
+ })
120
+ }
121
+ ```
122
+
123
+ ### Query with Conditional Enabling
124
+
125
+ ```typescript
126
+ function useTask(id: string | null) {
127
+ return useQuery({
128
+ queryKey: ['entity', 'tasks', id],
129
+ queryFn: async () => {
130
+ const response = await fetch(`/api/v1/tasks/${id}`)
131
+ if (!response.ok) throw new Error('Failed to fetch task')
132
+ return response.json()
133
+ },
134
+ enabled: !!id, // Only fetch when id exists
135
+ })
136
+ }
137
+ ```
138
+
139
+ ### Query with Authentication Guard
140
+
141
+ ```typescript
142
+ function useEntityQuery(entityConfig: EntityConfig, filters: Filters) {
143
+ const { user } = useAuth()
144
+
145
+ return useQuery({
146
+ queryKey: ['entity', entityConfig.slug, filters],
147
+ queryFn: async () => {
148
+ const response = await fetch(`/api/v1/${entityConfig.slug}?${params}`)
149
+ if (!response.ok) throw new Error('Failed to fetch')
150
+ return response.json()
151
+ },
152
+ enabled: !!user, // Only fetch when authenticated
153
+ staleTime: 1000 * 60 * 5, // 5 minutes for entity lists
154
+ gcTime: 1000 * 60 * 60, // 1 hour garbage collection
155
+ })
156
+ }
157
+ ```
158
+
159
+ ### Query Options Summary
160
+
161
+ | Option | Type | Description |
162
+ |--------|------|-------------|
163
+ | `queryKey` | `unknown[]` | Cache key (required) |
164
+ | `queryFn` | `() => Promise<T>` | Fetch function (required) |
165
+ | `enabled` | `boolean` | Conditional fetching |
166
+ | `staleTime` | `number` | Time before data is stale (ms) |
167
+ | `gcTime` | `number` | Cache retention time (ms) |
168
+ | `retry` | `number \| boolean` | Retry attempts |
169
+ | `refetchOnWindowFocus` | `boolean` | Refetch on tab focus |
170
+ | `refetchInterval` | `number` | Polling interval (ms) |
171
+
172
+ ## useMutation Patterns
173
+
174
+ ### Simple Mutation
175
+
176
+ ```typescript
177
+ import { useMutation, useQueryClient } from '@tanstack/react-query'
178
+
179
+ function useCreateTask() {
180
+ const queryClient = useQueryClient()
181
+
182
+ return useMutation({
183
+ mutationFn: async (data: CreateTaskData) => {
184
+ const response = await fetch('/api/v1/tasks', {
185
+ method: 'POST',
186
+ headers: { 'Content-Type': 'application/json' },
187
+ body: JSON.stringify(data),
188
+ })
189
+ if (!response.ok) throw new Error('Failed to create task')
190
+ return response.json()
191
+ },
192
+ onSuccess: () => {
193
+ // Invalidate all task queries to refetch
194
+ queryClient.invalidateQueries({ queryKey: ['entity', 'tasks'] })
195
+ },
196
+ })
197
+ }
198
+
199
+ // Usage
200
+ function CreateTaskForm() {
201
+ const createTask = useCreateTask()
202
+
203
+ const handleSubmit = async (data: CreateTaskData) => {
204
+ try {
205
+ await createTask.mutateAsync(data)
206
+ toast.success('Task created!')
207
+ } catch (error) {
208
+ toast.error('Failed to create task')
209
+ }
210
+ }
211
+
212
+ return (
213
+ <form onSubmit={handleSubmit}>
214
+ {/* form fields */}
215
+ <Button disabled={createTask.isPending}>
216
+ {createTask.isPending ? 'Creating...' : 'Create'}
217
+ </Button>
218
+ </form>
219
+ )
220
+ }
221
+ ```
222
+
223
+ ### Mutation with Optimistic Updates
224
+
225
+ ```typescript
226
+ function useEntityMutations(entityConfig: EntityConfig) {
227
+ const queryClient = useQueryClient()
228
+ const baseQueryKey = ['entity', entityConfig.slug]
229
+
230
+ const createMutation = useMutation({
231
+ mutationFn: async (data: Record<string, unknown>) => {
232
+ const response = await fetch(`/api/v1/${entityConfig.slug}`, {
233
+ method: 'POST',
234
+ headers: { 'Content-Type': 'application/json' },
235
+ body: JSON.stringify(data),
236
+ })
237
+ if (!response.ok) throw new Error('Failed to create')
238
+ return response.json()
239
+ },
240
+
241
+ // OPTIMISTIC UPDATE
242
+ onMutate: async (newItem) => {
243
+ // 1. Cancel outgoing refetches to avoid overwriting optimistic update
244
+ await queryClient.cancelQueries({ queryKey: baseQueryKey })
245
+
246
+ // 2. Snapshot current data for rollback
247
+ const previousData = queryClient.getQueriesData({ queryKey: baseQueryKey })
248
+
249
+ // 3. Optimistically update all matching queries
250
+ queryClient.setQueriesData({ queryKey: baseQueryKey }, (old: any) => {
251
+ if (!old?.items) return old
252
+ return {
253
+ ...old,
254
+ items: [
255
+ { ...newItem, id: `temp-${Date.now()}` }, // Temporary ID
256
+ ...old.items
257
+ ],
258
+ total: old.total + 1,
259
+ }
260
+ })
261
+
262
+ // 4. Return context for rollback
263
+ return { previousData }
264
+ },
265
+
266
+ // ROLLBACK ON ERROR
267
+ onError: (error, variables, context) => {
268
+ if (context?.previousData) {
269
+ context.previousData.forEach(([queryKey, data]) => {
270
+ queryClient.setQueryData(queryKey, data)
271
+ })
272
+ }
273
+ toast.error('Failed to create item')
274
+ },
275
+
276
+ // SYNC WITH SERVER
277
+ onSettled: () => {
278
+ // Always refetch after mutation to sync with server
279
+ queryClient.invalidateQueries({ queryKey: baseQueryKey })
280
+ },
281
+ })
282
+
283
+ const updateMutation = useMutation({
284
+ mutationFn: async ({ id, data }: { id: string; data: Record<string, unknown> }) => {
285
+ const response = await fetch(`/api/v1/${entityConfig.slug}/${id}`, {
286
+ method: 'PATCH',
287
+ headers: { 'Content-Type': 'application/json' },
288
+ body: JSON.stringify(data),
289
+ })
290
+ if (!response.ok) throw new Error('Failed to update')
291
+ return response.json()
292
+ },
293
+
294
+ onMutate: async ({ id, data }) => {
295
+ await queryClient.cancelQueries({ queryKey: baseQueryKey })
296
+ const previousData = queryClient.getQueriesData({ queryKey: baseQueryKey })
297
+
298
+ // Update item in all matching queries
299
+ queryClient.setQueriesData({ queryKey: baseQueryKey }, (old: any) => {
300
+ if (!old?.items) return old
301
+ return {
302
+ ...old,
303
+ items: old.items.map((item: any) =>
304
+ item.id === id ? { ...item, ...data } : item
305
+ ),
306
+ }
307
+ })
308
+
309
+ return { previousData }
310
+ },
311
+
312
+ onError: (error, variables, context) => {
313
+ if (context?.previousData) {
314
+ context.previousData.forEach(([queryKey, data]) => {
315
+ queryClient.setQueryData(queryKey, data)
316
+ })
317
+ }
318
+ },
319
+
320
+ onSettled: () => {
321
+ queryClient.invalidateQueries({ queryKey: baseQueryKey })
322
+ },
323
+ })
324
+
325
+ const deleteMutation = useMutation({
326
+ mutationFn: async (id: string) => {
327
+ const response = await fetch(`/api/v1/${entityConfig.slug}/${id}`, {
328
+ method: 'DELETE',
329
+ })
330
+ if (!response.ok) throw new Error('Failed to delete')
331
+ return response.json()
332
+ },
333
+
334
+ onMutate: async (id) => {
335
+ await queryClient.cancelQueries({ queryKey: baseQueryKey })
336
+ const previousData = queryClient.getQueriesData({ queryKey: baseQueryKey })
337
+
338
+ // Remove item from all matching queries
339
+ queryClient.setQueriesData({ queryKey: baseQueryKey }, (old: any) => {
340
+ if (!old?.items) return old
341
+ return {
342
+ ...old,
343
+ items: old.items.filter((item: any) => item.id !== id),
344
+ total: old.total - 1,
345
+ }
346
+ })
347
+
348
+ return { previousData }
349
+ },
350
+
351
+ onError: (error, variables, context) => {
352
+ if (context?.previousData) {
353
+ context.previousData.forEach(([queryKey, data]) => {
354
+ queryClient.setQueryData(queryKey, data)
355
+ })
356
+ }
357
+ },
358
+
359
+ onSettled: () => {
360
+ queryClient.invalidateQueries({ queryKey: baseQueryKey })
361
+ },
362
+ })
363
+
364
+ return {
365
+ create: createMutation,
366
+ update: updateMutation,
367
+ delete: deleteMutation,
368
+ }
369
+ }
370
+ ```
371
+
372
+ ## Cache Invalidation Strategies
373
+
374
+ ### Broad Invalidation
375
+
376
+ Affects all queries with matching prefix:
377
+
378
+ ```typescript
379
+ // Invalidate all task queries (any filters)
380
+ queryClient.invalidateQueries({ queryKey: ['entity', 'tasks'] })
381
+
382
+ // Invalidate all entity queries
383
+ queryClient.invalidateQueries({ queryKey: ['entity'] })
384
+ ```
385
+
386
+ ### Specific Invalidation
387
+
388
+ Target specific queries:
389
+
390
+ ```typescript
391
+ // Invalidate single task
392
+ queryClient.invalidateQueries({
393
+ queryKey: ['entity', 'tasks', 'task-123']
394
+ })
395
+
396
+ // Invalidate list with specific filters
397
+ queryClient.invalidateQueries({
398
+ queryKey: ['entity', 'tasks', { status: 'active' }],
399
+ exact: true // Only exact match
400
+ })
401
+ ```
402
+
403
+ ### Direct Cache Update
404
+
405
+ Update without refetch:
406
+
407
+ ```typescript
408
+ // Update single item in cache
409
+ queryClient.setQueryData(
410
+ ['entity', 'tasks', 'task-123'],
411
+ (old) => ({ ...old, status: 'completed' })
412
+ )
413
+
414
+ // Update all matching queries
415
+ queryClient.setQueriesData(
416
+ { queryKey: ['entity', 'tasks'] },
417
+ (old: any) => ({
418
+ ...old,
419
+ items: old.items.map((item: any) =>
420
+ item.id === 'task-123' ? { ...item, status: 'completed' } : item
421
+ ),
422
+ })
423
+ )
424
+ ```
425
+
426
+ ## State Management Hierarchy
427
+
428
+ ```
429
+ 1. Server State → TanStack Query (useQuery, useMutation)
430
+ 2. URL State → Search params (shareable, bookmarkable)
431
+ 3. Component State → useState (local, ephemeral)
432
+ 4. Context API → Cross-component (theme, auth, user)
433
+ 5. External Stores → useSyncExternalStore (third-party)
434
+ ```
435
+
436
+ **Rule:** Use TanStack Query for ALL server data. Don't store server data in useState.
437
+
438
+ ## Loading & Error States
439
+
440
+ ```typescript
441
+ function TaskList() {
442
+ const { data, isLoading, isError, error, refetch } = useTaskList(filters)
443
+
444
+ if (isLoading) {
445
+ return <Skeleton count={5} />
446
+ }
447
+
448
+ if (isError) {
449
+ return (
450
+ <Alert variant="destructive">
451
+ <AlertTitle>Error</AlertTitle>
452
+ <AlertDescription>
453
+ {error.message}
454
+ <Button onClick={() => refetch()}>Retry</Button>
455
+ </AlertDescription>
456
+ </Alert>
457
+ )
458
+ }
459
+
460
+ return (
461
+ <ul>
462
+ {data.items.map((task) => (
463
+ <TaskItem key={task.id} task={task} />
464
+ ))}
465
+ </ul>
466
+ )
467
+ }
468
+ ```
469
+
470
+ ### State Properties
471
+
472
+ | Property | Description |
473
+ |----------|-------------|
474
+ | `isLoading` | First fetch, no data yet |
475
+ | `isFetching` | Any fetch (including background) |
476
+ | `isPending` | Mutation in progress |
477
+ | `isError` | Query/mutation failed |
478
+ | `isSuccess` | Query/mutation succeeded |
479
+ | `data` | Query result |
480
+ | `error` | Error object |
481
+
482
+ ## Anti-Patterns (CRITICAL)
483
+
484
+ ### FORBIDDEN: useEffect for Data Fetching
485
+
486
+ ```typescript
487
+ // ❌ NEVER DO THIS
488
+ function TaskList() {
489
+ const [tasks, setTasks] = useState([])
490
+ const [loading, setLoading] = useState(true)
491
+
492
+ useEffect(() => {
493
+ setLoading(true)
494
+ fetch('/api/v1/tasks')
495
+ .then(res => res.json())
496
+ .then(data => {
497
+ setTasks(data.items)
498
+ setLoading(false)
499
+ })
500
+ }, [])
501
+
502
+ // Problems: No caching, no error handling, no refetch, race conditions
503
+ }
504
+
505
+ // ✅ CORRECT
506
+ function TaskList() {
507
+ const { data, isLoading, error } = useQuery({
508
+ queryKey: ['entity', 'tasks'],
509
+ queryFn: () => fetch('/api/v1/tasks').then(res => res.json())
510
+ })
511
+
512
+ // Benefits: Caching, error handling, automatic refetch, deduplication
513
+ }
514
+ ```
515
+
516
+ ### FORBIDDEN: useEffect for Derived State
517
+
518
+ ```typescript
519
+ // ❌ NEVER DO THIS
520
+ function TaskStats({ tasks }) {
521
+ const [completedCount, setCompletedCount] = useState(0)
522
+
523
+ useEffect(() => {
524
+ setCompletedCount(tasks.filter(t => t.status === 'completed').length)
525
+ }, [tasks])
526
+ }
527
+
528
+ // ✅ CORRECT - Calculate during render
529
+ function TaskStats({ tasks }) {
530
+ const completedCount = useMemo(
531
+ () => tasks.filter(t => t.status === 'completed').length,
532
+ [tasks]
533
+ )
534
+ }
535
+ ```
536
+
537
+ ### FORBIDDEN: Storing Server Data in State
538
+
539
+ ```typescript
540
+ // ❌ NEVER DO THIS
541
+ function TaskPage() {
542
+ const { data } = useTaskList()
543
+ const [tasks, setTasks] = useState([])
544
+
545
+ useEffect(() => {
546
+ if (data) setTasks(data.items)
547
+ }, [data])
548
+
549
+ // Now have TWO sources of truth!
550
+ }
551
+
552
+ // ✅ CORRECT - Use query data directly
553
+ function TaskPage() {
554
+ const { data } = useTaskList()
555
+ const tasks = data?.items ?? []
556
+
557
+ // Single source of truth
558
+ }
559
+ ```
560
+
561
+ ### FORBIDDEN: Missing Query Keys for Filters
562
+
563
+ ```typescript
564
+ // ❌ WRONG - Same key regardless of filters
565
+ useQuery({
566
+ queryKey: ['tasks'],
567
+ queryFn: () => fetch(`/api/v1/tasks?status=${status}`)
568
+ })
569
+
570
+ // ✅ CORRECT - Include filters in key
571
+ useQuery({
572
+ queryKey: ['tasks', { status }],
573
+ queryFn: () => fetch(`/api/v1/tasks?status=${status}`)
574
+ })
575
+ ```
576
+
577
+ ## Key Conventions
578
+
579
+ | Aspect | Convention |
580
+ |--------|-----------|
581
+ | **Query Keys** | `['domain', 'resource', filters, id]` |
582
+ | **Stale Time** | 60s (global), 5min (entity lists) |
583
+ | **GC Time** | 1 hour |
584
+ | **Retry** | 2 attempts |
585
+ | **Window Refetch** | Disabled |
586
+ | **Enabled Guard** | `enabled: !!user && conditions` |
587
+ | **Optimistic IDs** | `temp-${Date.now()}` |
588
+ | **Error Handling** | Throw in queryFn, toast in onError |
589
+
590
+ ## Checklist
591
+
592
+ Before finalizing data fetching:
593
+
594
+ - [ ] Uses TanStack Query (not useEffect + useState)
595
+ - [ ] Query key includes all cache-affecting parameters
596
+ - [ ] Enabled guard for conditional queries
597
+ - [ ] Proper error handling with user feedback
598
+ - [ ] Loading states with Skeleton components
599
+ - [ ] Mutations invalidate related queries
600
+ - [ ] Optimistic updates for better UX (where appropriate)
601
+ - [ ] No server data stored in useState
602
+ - [ ] No derived state in useEffect
603
+
604
+ ## Related Skills
605
+
606
+ - `entity-api` - API endpoint patterns
607
+ - `shadcn-components` - Loading/error UI components
608
+ - `react-patterns` - React best practices