@hitch42/applet 0.1.0-beta.10

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 (193) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_internal/client.d.ts +7 -0
  3. package/dist/_internal/client.js +169 -0
  4. package/dist/_internal/server.d.ts +92 -0
  5. package/dist/_internal/server.js +181 -0
  6. package/dist/applet.d.ts +1 -0
  7. package/dist/applet.js +184 -0
  8. package/dist/batch-Qa4B4-Yp.d.ts +30 -0
  9. package/dist/build-BlmLiKro.js +90 -0
  10. package/dist/build-CcT-wNN9.js +2 -0
  11. package/dist/ci-0Q2mUh53.js +68 -0
  12. package/dist/client.d.ts +48 -0
  13. package/dist/client.js +38 -0
  14. package/dist/contract-QUvEGiRI.d.ts +23 -0
  15. package/dist/deploy-BbL3GObV.js +2579 -0
  16. package/dist/email-DBTVhNB_.js +62 -0
  17. package/dist/generate-XB-72Fzj.js +537 -0
  18. package/dist/generate-wIN3IYqL.js +2 -0
  19. package/dist/index.d.ts +185 -0
  20. package/dist/index.js +251 -0
  21. package/dist/init-DwsXRUk-.js +244 -0
  22. package/dist/lazy-client-x0DCI98S.js +64 -0
  23. package/dist/load-config-OKZU3UKn.js +27 -0
  24. package/dist/login-BBO74hPC.js +12 -0
  25. package/dist/logs-XvK6Zs5Y.js +183 -0
  26. package/dist/manifest-4ZAc6HUk.js +191 -0
  27. package/dist/manifest-I-9s8R36.d.ts +88 -0
  28. package/dist/me-BW7Paary.js +32 -0
  29. package/dist/project-B0qNXnFu.js +536 -0
  30. package/dist/prompt-ChS-grYO.js +29 -0
  31. package/dist/query-cache-CXmAyLv3.js +82 -0
  32. package/dist/react.d.ts +37 -0
  33. package/dist/react.js +515 -0
  34. package/dist/schedules-BkWZsbSs.js +42 -0
  35. package/dist/server.d.ts +46 -0
  36. package/dist/server.js +104 -0
  37. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  38. package/dist/telephony.d.ts +77 -0
  39. package/dist/telephony.js +607 -0
  40. package/dist/upload-DKZzz5kW.d.ts +112 -0
  41. package/dist/upload-DRbVhuDm.js +114 -0
  42. package/dist/variables-0tjnAeut.js +108 -0
  43. package/dist/vite.d.ts +63 -0
  44. package/dist/vite.js +816 -0
  45. package/dist/workflow-CTo2kZF1.js +51 -0
  46. package/dist/workflow-DurM6Fwx.d.ts +28 -0
  47. package/dist/workflow-engine-MogejNfF.js +117 -0
  48. package/dist/workspace-DruAZPmV.js +390 -0
  49. package/package.json +112 -0
  50. package/scaffold/.agents/skills/hitch-applets/SKILL.md +163 -0
  51. package/scaffold/.agents/skills/hitch-applets/references/client.md +73 -0
  52. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  53. package/scaffold/.agents/skills/hitch-applets/references/files.md +109 -0
  54. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +198 -0
  55. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +37 -0
  56. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  57. package/scaffold/.agents/skills/hitch-applets/references/records.md +176 -0
  58. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +91 -0
  59. package/scaffold/.agents/skills/hitch-applets/references/server.md +95 -0
  60. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +81 -0
  61. package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
  62. package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
  63. package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
  64. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  65. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  66. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  116. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  117. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  118. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  119. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  120. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  121. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  122. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  146. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  147. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  148. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  149. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  150. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  151. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  152. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  161. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  162. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  163. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  164. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  165. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  166. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  167. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  168. package/scaffold/.github/workflows/hitch.yml +15 -0
  169. package/scaffold/AGENTS.md +38 -0
  170. package/scaffold/applet/components.json +25 -0
  171. package/scaffold/applet/src/client/components/ui/alert.tsx +73 -0
  172. package/scaffold/applet/src/client/components/ui/avatar.tsx +100 -0
  173. package/scaffold/applet/src/client/components/ui/badge.tsx +55 -0
  174. package/scaffold/applet/src/client/components/ui/button.tsx +90 -0
  175. package/scaffold/applet/src/client/components/ui/card.tsx +85 -0
  176. package/scaffold/applet/src/client/components/ui/checkbox.tsx +35 -0
  177. package/scaffold/applet/src/client/components/ui/dialog.tsx +164 -0
  178. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +235 -0
  179. package/scaffold/applet/src/client/components/ui/input-group.tsx +144 -0
  180. package/scaffold/applet/src/client/components/ui/input.tsx +22 -0
  181. package/scaffold/applet/src/client/components/ui/label.tsx +29 -0
  182. package/scaffold/applet/src/client/components/ui/popover.tsx +74 -0
  183. package/scaffold/applet/src/client/components/ui/select.tsx +243 -0
  184. package/scaffold/applet/src/client/components/ui/separator.tsx +25 -0
  185. package/scaffold/applet/src/client/components/ui/sheet.tsx +177 -0
  186. package/scaffold/applet/src/client/components/ui/skeleton.tsx +13 -0
  187. package/scaffold/applet/src/client/components/ui/spinner.tsx +17 -0
  188. package/scaffold/applet/src/client/components/ui/switch.tsx +41 -0
  189. package/scaffold/applet/src/client/components/ui/table.tsx +114 -0
  190. package/scaffold/applet/src/client/components/ui/tabs.tsx +78 -0
  191. package/scaffold/applet/src/client/components/ui/textarea.tsx +23 -0
  192. package/scaffold/applet/src/client/lib/utils.ts +1 -0
  193. package/scaffold/applet/src/client/styles/globals.css +136 -0
@@ -0,0 +1,156 @@
1
+ ---
2
+ name: compositions/query-data-and-forms
3
+ description: >
4
+ Use this when integrating TanStack Query data and mutations with forms:
5
+ initial server state, editable client state, dirty fields, background updates,
6
+ submit prevention, invalidation, reset-after-save, validation errors, and
7
+ avoiding blind copies from Query cache into local form state.
8
+ type: composition
9
+ library: TanStack Query
10
+ library_version: "5.101.0"
11
+ requires:
12
+ - core/write-mutations-and-invalidate-related-queries
13
+ - core/selectors-and-derived-state
14
+ sources:
15
+ - TanStack/query:docs/framework/react/guides/mutations.md
16
+ - TanStack/query:docs/framework/react/guides/invalidations-from-mutations.md
17
+ ---
18
+
19
+ ## Core Patterns
20
+
21
+ Treat form state as client state after the user starts editing. Server state can initialize the form, but it should not be copied blindly on every query update.
22
+
23
+ ### Initial data only
24
+
25
+ Use this when one user owns the form and background updates are not useful while editing.
26
+
27
+ ```tsx
28
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
29
+
30
+ function PersonDetail({ id }: { id: string }) {
31
+ const queryClient = useQueryClient();
32
+ const person = useQuery({
33
+ queryKey: ["person", id],
34
+ queryFn: () => fetchPerson(id),
35
+ staleTime: Infinity,
36
+ });
37
+
38
+ const updatePerson = useMutation({
39
+ mutationFn: savePerson,
40
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["person", id] }),
41
+ });
42
+
43
+ if (!person.data) return <p>Loading...</p>;
44
+ return (
45
+ <PersonForm
46
+ person={person.data}
47
+ isSaving={updatePerson.isPending}
48
+ onSubmit={updatePerson.mutate}
49
+ />
50
+ );
51
+ }
52
+ ```
53
+
54
+ ### Derived server plus client state
55
+
56
+ Use this when background updates should remain visible for untouched fields.
57
+
58
+ ```tsx
59
+ import * as React from "react";
60
+ import { useQuery } from "@tanstack/react-query";
61
+
62
+ function PersonName({ id }: { id: string }) {
63
+ const { data } = useQuery({
64
+ queryKey: ["person", id],
65
+ queryFn: () => fetchPerson(id),
66
+ });
67
+ const [draft, setDraft] = React.useState<{ firstName?: string }>({});
68
+
69
+ if (!data) return <p>Loading...</p>;
70
+
71
+ return (
72
+ <input
73
+ value={draft.firstName ?? data.firstName}
74
+ onChange={(event) => setDraft({ firstName: event.target.value })}
75
+ />
76
+ );
77
+ }
78
+ ```
79
+
80
+ ### Reset after awaited invalidation
81
+
82
+ ```tsx
83
+ const mutation = useMutation({
84
+ mutationFn: savePerson,
85
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["person", id] }),
86
+ });
87
+
88
+ function submit(values: PersonFormValues) {
89
+ mutation.mutate(values, { onSuccess: () => resetFormDraft() });
90
+ }
91
+ ```
92
+
93
+ Return the invalidation promise from `onSuccess` or `onSettled` when the saved server state must be back in the cache before the form resets.
94
+
95
+ ## Common Mistakes
96
+
97
+ ### HIGH Initializing form defaults before query data exists
98
+
99
+ Wrong:
100
+
101
+ ```tsx
102
+ const { data } = useQuery({
103
+ queryKey: ["person", id],
104
+ queryFn: () => fetchPerson(id),
105
+ });
106
+ const [draft, setDraft] = React.useState(data);
107
+ ```
108
+
109
+ Correct:
110
+
111
+ ```tsx
112
+ const { data } = useQuery({
113
+ queryKey: ["person", id],
114
+ queryFn: () => fetchPerson(id),
115
+ });
116
+ if (!data) return <p>Loading...</p>;
117
+ return <PersonForm initialPerson={data} />;
118
+ ```
119
+
120
+ The first render usually has no query data. Split the form boundary or derive field values from query data plus draft state.
121
+
122
+ ### HIGH Background refetch overwrites or hides dirty client state
123
+
124
+ Wrong:
125
+
126
+ ```tsx
127
+ React.useEffect(() => {
128
+ setDraft(personQuery.data);
129
+ }, [personQuery.data]);
130
+ ```
131
+
132
+ Correct:
133
+
134
+ ```tsx
135
+ const shownFirstName = draft.firstName ?? personQuery.data?.firstName ?? "";
136
+ ```
137
+
138
+ Do not synchronize every server update into the draft. Derive displayed values or intentionally opt out of background updates for initial-only forms.
139
+
140
+ ### MEDIUM Form submit can run twice
141
+
142
+ Wrong:
143
+
144
+ ```tsx
145
+ <button type="submit">Save</button>
146
+ ```
147
+
148
+ Correct:
149
+
150
+ ```tsx
151
+ <button type="submit" disabled={mutation.isPending}>
152
+ Save
153
+ </button>
154
+ ```
155
+
156
+ Use mutation state to block duplicate writes while a submission is in flight.
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: core/build-query-abstractions
3
+ description: >
4
+ Use this when creating TanStack Query abstractions: queryOptions factories,
5
+ feature-local key modules, custom hooks built on top of options factories,
6
+ TypeScript inference, avoiding wide UseQueryOptions wrappers, and sharing
7
+ query configuration across hooks, loaders, prefetches, and QueryClient calls.
8
+ type: core
9
+ library: TanStack Query
10
+ library_version: "5.101.0"
11
+ requires:
12
+ - core/design-query-keys-and-options
13
+ sources:
14
+ - TanStack/query:docs/framework/react/guides/query-options.md
15
+ - TanStack/query:docs/framework/react/typescript.md
16
+ ---
17
+
18
+ ## Core Patterns
19
+
20
+ Prefer a `queryOptions` factory as the base abstraction. Hooks, loaders, prefetches, suspense queries, and `QueryClient` calls can all consume it.
21
+
22
+ ```ts
23
+ import { queryOptions, useQuery, useSuspenseQuery } from "@tanstack/react-query";
24
+
25
+ export function invoiceOptions(id: number) {
26
+ return queryOptions({
27
+ queryKey: ["invoice", id],
28
+ queryFn: () => fetchInvoice(id),
29
+ staleTime: 60_000,
30
+ });
31
+ }
32
+
33
+ export function useInvoice(id: number) {
34
+ return useQuery(invoiceOptions(id));
35
+ }
36
+
37
+ export function useSuspenseInvoice(id: number) {
38
+ return useSuspenseQuery(invoiceOptions(id));
39
+ }
40
+ ```
41
+
42
+ Compose one-off options at the usage site:
43
+
44
+ ```ts
45
+ const invoice = useQuery({
46
+ ...invoiceOptions(id),
47
+ select: (data) => data.createdAt,
48
+ throwOnError: true,
49
+ });
50
+ ```
51
+
52
+ ## Common Mistakes
53
+
54
+ ### HIGH Custom hook is the only abstraction
55
+
56
+ Wrong:
57
+
58
+ ```ts
59
+ export function useInvoice(id: number) {
60
+ return useQuery({
61
+ queryKey: ["invoice", id],
62
+ queryFn: () => fetchInvoice(id),
63
+ });
64
+ }
65
+ ```
66
+
67
+ Correct:
68
+
69
+ ```ts
70
+ export function invoiceOptions(id: number) {
71
+ return queryOptions({
72
+ queryKey: ["invoice", id],
73
+ queryFn: () => fetchInvoice(id),
74
+ });
75
+ }
76
+
77
+ export function useInvoice(id: number) {
78
+ return useQuery(invoiceOptions(id));
79
+ }
80
+ ```
81
+
82
+ Custom hooks cannot run in route loaders, server prefetches, or event handlers. Options factories can.
83
+
84
+ ### HIGH Wide UseQueryOptions wrapper breaks inference
85
+
86
+ Wrong:
87
+
88
+ ```ts
89
+ function useInvoice(id: number, options?: Partial<UseQueryOptions<Invoice>>) {
90
+ return useQuery({
91
+ queryKey: ["invoice", id],
92
+ queryFn: () => fetchInvoice(id),
93
+ ...options,
94
+ });
95
+ }
96
+ ```
97
+
98
+ Correct:
99
+
100
+ ```ts
101
+ useQuery({
102
+ ...invoiceOptions(id),
103
+ select: (invoice) => invoice.createdAt,
104
+ });
105
+ ```
106
+
107
+ Let `queryOptions` and usage-site composition preserve `select` inference.
108
+
109
+ ### MEDIUM Wrapper hides Query result state
110
+
111
+ Wrong:
112
+
113
+ ```ts
114
+ export function useInvoice(id: number) {
115
+ const { data } = useQuery(invoiceOptions(id));
116
+ return data;
117
+ }
118
+ ```
119
+
120
+ Correct:
121
+
122
+ ```ts
123
+ export function useInvoice(id: number) {
124
+ return useQuery(invoiceOptions(id));
125
+ }
126
+ ```
127
+
128
+ Keep the Query result surface available unless the abstraction owns every loading, error, and refetch behavior.
129
+
130
+ Source: TanStack/query:docs/framework/react/guides/queries.md
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: core/cancel-queries-and-consume-abort-signals
3
+ description: >
4
+ Use this when handling query cancellation, AbortSignal, cancelQueries,
5
+ CancelledError, rollback after consumed signals, fetch cancellation, axios
6
+ cancellation, and optimistic update overwrite prevention.
7
+ type: core
8
+ library: TanStack Query
9
+ library_version: "5.101.0"
10
+ requires:
11
+ - core/fetch-and-observe-queries
12
+ sources:
13
+ - TanStack/query:docs/framework/react/guides/query-cancellation.md
14
+ - TanStack/query:docs/reference/QueryClient.md
15
+ - TanStack/query:packages/query-core/src/retryer.ts
16
+ ---
17
+
18
+ ## Setup
19
+
20
+ ```ts
21
+ import { useQuery } from "@tanstack/react-query";
22
+
23
+ export function useTodo(todoId: string) {
24
+ return useQuery({
25
+ queryKey: ["todo", todoId],
26
+ queryFn: async ({ signal }) => {
27
+ const response = await fetch(`/api/todos/${todoId}`, { signal });
28
+ return response.json() as Promise<{ id: string; title: string }>;
29
+ },
30
+ });
31
+ }
32
+ ```
33
+
34
+ ## Core Patterns
35
+
36
+ ### Cancel before optimistic writes
37
+
38
+ ```ts
39
+ import { QueryClient } from "@tanstack/react-query";
40
+
41
+ const queryClient = new QueryClient();
42
+
43
+ export async function prepareTodoWrite() {
44
+ await queryClient.cancelQueries({ queryKey: ["todos"] });
45
+ }
46
+ ```
47
+
48
+ ### Consume one signal across nested fetches
49
+
50
+ ```ts
51
+ import { queryOptions } from "@tanstack/react-query";
52
+
53
+ export const todosWithDetailsOptions = queryOptions({
54
+ queryKey: ["todos-with-details"],
55
+ queryFn: async ({ signal }) => {
56
+ const todos = await fetch("/api/todos", { signal }).then(
57
+ (response) => response.json() as Promise<Array<{ id: string }>>,
58
+ );
59
+ return Promise.all(
60
+ todos.map((todo) =>
61
+ fetch(`/api/todos/${todo.id}`, { signal }).then((response) => response.json()),
62
+ ),
63
+ );
64
+ },
65
+ });
66
+ ```
67
+
68
+ ### Cancel by query key
69
+
70
+ ```ts
71
+ import { QueryClient } from "@tanstack/react-query";
72
+
73
+ const queryClient = new QueryClient();
74
+
75
+ export function cancelTodos() {
76
+ return queryClient.cancelQueries({ queryKey: ["todos"] });
77
+ }
78
+ ```
79
+
80
+ ## Common Mistakes
81
+
82
+ ### HIGH Ignoring AbortSignal
83
+
84
+ Wrong:
85
+
86
+ ```ts
87
+ import { useQuery } from "@tanstack/react-query";
88
+
89
+ export function useTodo(id: string) {
90
+ return useQuery({
91
+ queryKey: ["todo", id],
92
+ queryFn: async () => fetch(`/api/todos/${id}`).then((r) => r.json()),
93
+ });
94
+ }
95
+ ```
96
+
97
+ Correct:
98
+
99
+ ```ts
100
+ import { useQuery } from "@tanstack/react-query";
101
+
102
+ export function useTodo(id: string) {
103
+ return useQuery({
104
+ queryKey: ["todo", id],
105
+ queryFn: async ({ signal }) => fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
106
+ });
107
+ }
108
+ ```
109
+
110
+ TanStack Query provides an AbortSignal; the request is only cancelled if the query function consumes it.
111
+
112
+ Source: TanStack/query:docs/framework/react/guides/query-cancellation.md
113
+
114
+ ### HIGH Assuming unmount cancels
115
+
116
+ Wrong:
117
+
118
+ ```ts
119
+ import { useQuery } from "@tanstack/react-query";
120
+
121
+ export function useReport() {
122
+ return useQuery({
123
+ queryKey: ["report"],
124
+ queryFn: async () => fetch("/api/report").then((r) => r.json()),
125
+ });
126
+ }
127
+ ```
128
+
129
+ Correct:
130
+
131
+ ```ts
132
+ import { useQuery } from "@tanstack/react-query";
133
+
134
+ export function useReport() {
135
+ return useQuery({
136
+ queryKey: ["report"],
137
+ queryFn: async ({ signal }) => fetch("/api/report", { signal }).then((r) => r.json()),
138
+ });
139
+ }
140
+ ```
141
+
142
+ Unused queries can continue and populate cache unless the signal is consumed.
143
+
144
+ Source: TanStack/query:docs/framework/react/guides/query-cancellation.md
145
+
146
+ ### HIGH Suspense cancellation expected
147
+
148
+ Wrong:
149
+
150
+ ```ts
151
+ import { useSuspenseQuery } from "@tanstack/react-query";
152
+
153
+ export function useTodo(id: string) {
154
+ return useSuspenseQuery({
155
+ queryKey: ["todo", id],
156
+ queryFn: async ({ signal }) => fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
157
+ });
158
+ }
159
+ ```
160
+
161
+ Correct:
162
+
163
+ ```ts
164
+ import { useQuery } from "@tanstack/react-query";
165
+
166
+ export function useTodo(id: string) {
167
+ return useQuery({
168
+ queryKey: ["todo", id],
169
+ queryFn: async ({ signal }) => fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
170
+ });
171
+ }
172
+ ```
173
+
174
+ Cancellation limitations apply to Suspense hooks; use non-suspense queries when cancellation behavior is required.
175
+
176
+ Source: TanStack/query:docs/framework/react/guides/query-cancellation.md
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: core/concurrent-optimistic-updates
3
+ description: >
4
+ Use this when multiple optimistic mutations can overlap: mutation variables,
5
+ submittedAt keys, scoped mutationKey filters, isMutating guards, cancellation,
6
+ rollback context, filtered list updates, and avoiding windows of inconsistent
7
+ UI during concurrent writes.
8
+ type: core
9
+ library: TanStack Query
10
+ library_version: "5.101.0"
11
+ requires:
12
+ - core/implement-optimistic-updates-and-cache-writes
13
+ - core/cancel-queries-and-consume-abort-signals
14
+ sources:
15
+ - TanStack/query:docs/framework/react/guides/optimistic-updates.md
16
+ - TanStack/query:docs/framework/react/reference/useMutationState.md
17
+ ---
18
+
19
+ ## Core Patterns
20
+
21
+ Concurrent optimistic updates need per-mutation identity and scoped invalidation. A rollback or invalidation from one mutation should not erase another mutation that is still pending.
22
+
23
+ ### Scope related mutations
24
+
25
+ ```tsx
26
+ const mutation = useMutation({
27
+ mutationKey: ["items"],
28
+ mutationFn: toggleItem,
29
+ onMutate: async ({ id }) => {
30
+ await queryClient.cancelQueries({ queryKey: ["items", "detail", id] });
31
+ const previousItem = queryClient.getQueryData<Item>(["items", "detail", id]);
32
+
33
+ queryClient.setQueryData<Item>(["items", "detail", id], (item) =>
34
+ item ? { ...item, isActive: !item.isActive } : item,
35
+ );
36
+
37
+ return { previousItem };
38
+ },
39
+ onError: (_error, variables, context) => {
40
+ queryClient.setQueryData(["items", "detail", variables.id], context?.previousItem);
41
+ },
42
+ onSettled: () => {
43
+ if (queryClient.isMutating({ mutationKey: ["items"] }) === 1) {
44
+ return queryClient.invalidateQueries({ queryKey: ["items"] });
45
+ }
46
+ },
47
+ });
48
+ ```
49
+
50
+ Use `queryClient.isMutating()` imperatively inside the callback so the count is current at the moment invalidation would run.
51
+
52
+ ### Render pending variables with stable keys
53
+
54
+ ```tsx
55
+ const pendingAdds = useMutationState<string>({
56
+ filters: { mutationKey: ["todos", "add"], status: "pending" },
57
+ select: (mutation) => `${mutation.state.submittedAt}:${mutation.state.variables}`,
58
+ });
59
+ ```
60
+
61
+ `submittedAt` distinguishes multiple pending mutations with the same variables.
62
+
63
+ ## Common Mistakes
64
+
65
+ ### CRITICAL One mutation invalidation reverts another optimistic update
66
+
67
+ Wrong:
68
+
69
+ ```ts
70
+ onSettled: () => queryClient.invalidateQueries({ queryKey: ["items"] });
71
+ ```
72
+
73
+ Correct:
74
+
75
+ ```ts
76
+ onSettled: () => {
77
+ if (queryClient.isMutating({ mutationKey: ["items"] }) === 1) {
78
+ return queryClient.invalidateQueries({ queryKey: ["items"] });
79
+ }
80
+ };
81
+ ```
82
+
83
+ Skip intermediate invalidations while related optimistic mutations are still in flight.
84
+
85
+ ### HIGH Optimistic list update ignores current filters
86
+
87
+ Wrong:
88
+
89
+ ```ts
90
+ queryClient.setQueryData(["items", "list", filters], (items) =>
91
+ items?.map((item) => (item.id === updated.id ? updated : item)),
92
+ );
93
+ ```
94
+
95
+ Correct:
96
+
97
+ ```ts
98
+ queryClient.setQueryData(["items", "list", filters], (items) =>
99
+ items
100
+ ?.map((item) => (item.id === updated.id ? updated : item))
101
+ .filter((item) => matchesFilters(item, filters)),
102
+ );
103
+ ```
104
+
105
+ If the server would remove the updated item from the filtered list, the optimistic cache update should do the same.
106
+
107
+ ### HIGH Pending optimistic rows share unstable keys
108
+
109
+ Wrong:
110
+
111
+ ```tsx
112
+ {
113
+ variables.map((title) => <li key={title}>{title}</li>);
114
+ }
115
+ ```
116
+
117
+ Correct:
118
+
119
+ ```tsx
120
+ const pending = useMutationState({
121
+ filters: { mutationKey: ["todos", "add"], status: "pending" },
122
+ select: (mutation) => ({
123
+ variables: mutation.state.variables,
124
+ submittedAt: mutation.state.submittedAt,
125
+ }),
126
+ });
127
+
128
+ {
129
+ pending.map((todo) => <li key={todo.submittedAt}>{todo.variables.title}</li>);
130
+ }
131
+ ```
132
+
133
+ Use mutation metadata to keep concurrent optimistic rows distinct.
134
+
135
+ Source: TanStack/query:docs/framework/react/guides/optimistic-updates.md