@hitch42/applet 0.1.1-beta.1 → 0.1.1-beta.11
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/_internal/client.js +120 -11
- package/dist/_internal/log-context.d.ts +34 -0
- package/dist/_internal/log-context.js +2 -0
- package/dist/_internal/server.d.ts +14 -1
- package/dist/_internal/server.js +122 -118
- package/dist/applet.js +53 -29
- package/dist/build-Bh_S5BUv.js +2 -0
- package/dist/{build-Rk3xDZV7.js → build-CEfFoIZZ.js} +30 -14
- package/dist/ci-BKw14X2h.js +69 -0
- package/dist/client.d.ts +3 -3
- package/dist/{contract-DMrpbaLJ.d.ts → contract-CsUpTBe0.d.ts} +1 -1
- package/dist/{deploy-D3BGDqgC.js → deploy-wGYjwXrx.js} +249 -35
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/{generate-CH0VQmUi.js → generate-BEIYs17x.js} +79 -25
- package/dist/generate-Dox-OYt6.js +2 -0
- package/dist/headers-DkNr9MLJ.js +17 -0
- package/dist/index.d.ts +123 -11
- package/dist/index.js +142 -26
- package/dist/{init-BS1Y-eNL.js → init-6oGPM46R.js} +17 -18
- package/dist/load-config-xEdTXs0y.js +27 -0
- package/dist/log-context-zu8U9Sjm.js +73 -0
- package/dist/login-CUx3TmFQ.js +12 -0
- package/dist/{logs-m00pzr_O.js → logs-DSjuEN0Q.js} +20 -22
- package/dist/{manifest-BQCAxATS.d.ts → manifest-C6_F4bL8.d.ts} +9 -0
- package/dist/{manifest-DyoJ1MIL.js → manifest-CJSL9GuC.js} +25 -3
- package/dist/me-BW7Paary.js +32 -0
- package/dist/{project-CxN-Dtc7.js → project-BRZGd6xq.js} +227 -34
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/query-cache-CXmAyLv3.js +82 -0
- package/dist/react.d.ts +3 -2
- package/dist/react.js +98 -80
- package/dist/schedules-C8_Svr4h.js +42 -0
- package/dist/server.d.ts +9 -4
- package/dist/server.js +29 -8
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +97 -0
- package/dist/telephony.js +726 -0
- package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
- package/dist/{variables-CP7Ce3Np.js → variables-DT5RyHdH.js} +14 -39
- package/dist/vite.d.ts +3 -60
- package/dist/vite.js +503 -34
- package/dist/workflow-engine-BOTzURKM.js +111 -0
- package/dist/{auth-DaVK0k5R.js → workspace-D0dnnlxL.js} +75 -40
- package/package.json +19 -6
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +60 -15
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +29 -2
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
- package/scaffold/.agents/skills/hitch-applets/references/records.md +32 -8
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
- package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
- package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
- package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
- package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
- package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
- package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
- package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
- package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
- package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
- package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
- package/scaffold/.github/workflows/hitch.yml +15 -0
- package/scaffold/AGENTS.md +2 -2
- package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
- package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
- package/scaffold/applet/src/client/lib/utils.ts +1 -6
- package/scaffold/applet/src/client/styles/globals.css +2 -0
- package/dist/build-CEku1lyb.js +0 -2
- package/dist/generate-6Nbt7uii.js +0 -2
- package/dist/load-config-BnxocLAQ.js +0 -56
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/broadcast-realtime-and-multi-tab-synchronization
|
|
3
|
+
description: >
|
|
4
|
+
Use this when synchronizing TanStack Query caches with broadcastQueryClient,
|
|
5
|
+
BroadcastChannel, multi-tab cache sync, realtime invalidation, WebSocket
|
|
6
|
+
events, server/browser boundaries, and experimental broadcast behavior.
|
|
7
|
+
type: composition
|
|
8
|
+
library: TanStack Query
|
|
9
|
+
library_version: "5.101.0"
|
|
10
|
+
requires:
|
|
11
|
+
- lifecycle/setup-query-client-and-providers
|
|
12
|
+
- core/write-mutations-and-invalidate-related-queries
|
|
13
|
+
- compositions/persist-offline-and-restore-caches
|
|
14
|
+
sources:
|
|
15
|
+
- TanStack/query:docs/framework/react/plugins/broadcastQueryClient.md
|
|
16
|
+
- TanStack/query:docs/framework/react/guides/query-invalidation.md
|
|
17
|
+
- TanStack/query:examples/react/chat/package.json
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
24
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
25
|
+
|
|
26
|
+
export const queryClient = new QueryClient();
|
|
27
|
+
|
|
28
|
+
if (typeof window !== "undefined") {
|
|
29
|
+
broadcastQueryClient({ queryClient, broadcastChannel: "app-query-cache" });
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Core Integration Patterns
|
|
34
|
+
|
|
35
|
+
### Invalidate from realtime events
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
39
|
+
|
|
40
|
+
const queryClient = new QueryClient();
|
|
41
|
+
|
|
42
|
+
export function onTodoChanged(todoId: number) {
|
|
43
|
+
return queryClient.invalidateQueries({ queryKey: ["todo", todoId] });
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Update narrow cache slices
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
51
|
+
|
|
52
|
+
const queryClient = new QueryClient();
|
|
53
|
+
|
|
54
|
+
export function onTodoTitle(todo: { id: number; title: string }) {
|
|
55
|
+
queryClient.setQueryData(["todo", todo.id], todo);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Keep broadcast client-side
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
63
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
64
|
+
|
|
65
|
+
export function installBroadcast(queryClient: QueryClient) {
|
|
66
|
+
if (typeof window === "undefined") return;
|
|
67
|
+
broadcastQueryClient({ queryClient, broadcastChannel: "query-cache" });
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Common Mistakes
|
|
72
|
+
|
|
73
|
+
### MEDIUM Unlocked experimental broadcast
|
|
74
|
+
|
|
75
|
+
Wrong:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
79
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
80
|
+
|
|
81
|
+
broadcastQueryClient({ queryClient: new QueryClient() });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Correct:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
88
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
89
|
+
|
|
90
|
+
if (typeof window !== "undefined") {
|
|
91
|
+
broadcastQueryClient({
|
|
92
|
+
queryClient: new QueryClient(),
|
|
93
|
+
broadcastChannel: "query-cache-v1",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The broadcast package is experimental and should be deliberately isolated behind browser-only setup.
|
|
99
|
+
|
|
100
|
+
Source: TanStack/query:docs/framework/react/plugins/broadcastQueryClient.md
|
|
101
|
+
|
|
102
|
+
### MEDIUM Over-normalized realtime writes
|
|
103
|
+
|
|
104
|
+
Wrong:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
108
|
+
|
|
109
|
+
const queryClient = new QueryClient();
|
|
110
|
+
queryClient.setQueryData(["todos"], { byId: { 1: { id: 1 } }, allIds: [1] });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Correct:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
117
|
+
|
|
118
|
+
const queryClient = new QueryClient();
|
|
119
|
+
queryClient.invalidateQueries({ queryKey: ["todos"] });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Query is not a normalized cache; invalidation is often safer than duplicating server state models.
|
|
123
|
+
|
|
124
|
+
Source: TanStack/query:docs/framework/react/guides/query-invalidation.md
|
|
125
|
+
|
|
126
|
+
### HIGH Broadcast on server
|
|
127
|
+
|
|
128
|
+
Wrong:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
132
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
133
|
+
|
|
134
|
+
export const queryClient = new QueryClient();
|
|
135
|
+
broadcastQueryClient({ queryClient });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Correct:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
142
|
+
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
|
143
|
+
|
|
144
|
+
export const queryClient = new QueryClient();
|
|
145
|
+
if (typeof window !== "undefined") broadcastQueryClient({ queryClient });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
BroadcastChannel is a browser primitive; server setup should not install it.
|
|
149
|
+
|
|
150
|
+
Source: TanStack/query:docs/framework/react/plugins/broadcastQueryClient.md
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/compose-query-with-tanstack-router-and-start
|
|
3
|
+
description: >
|
|
4
|
+
Use this when combining TanStack Query with TanStack Router or TanStack Start:
|
|
5
|
+
router context QueryClient, createFileRoute loaders, ensureQueryData,
|
|
6
|
+
@tanstack/react-router-ssr-query, setupRouterSsrQueryIntegration, SSR hydration,
|
|
7
|
+
streaming, redirects, and Start server functions.
|
|
8
|
+
type: composition
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- lifecycle/setup-query-client-and-providers
|
|
13
|
+
- core/design-query-keys-and-options
|
|
14
|
+
- lifecycle/prefetch-and-remove-request-waterfalls
|
|
15
|
+
sources:
|
|
16
|
+
- TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
17
|
+
- TanStack/router:https://tanstack.com/router/latest/docs/how-to/setup-ssr
|
|
18
|
+
- TanStack/start:https://tanstack.com/start/latest/docs/framework/react/overview
|
|
19
|
+
- TanStack/query:docs/framework/react/guides/prefetching.md
|
|
20
|
+
- TanStack/query:docs/framework/react/guides/ssr.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { QueryClient, QueryClientProvider, useSuspenseQuery } from "@tanstack/react-query";
|
|
27
|
+
import { createFileRoute, createRouter, RouterProvider } from "@tanstack/react-router";
|
|
28
|
+
import { routeTree } from "./routeTree.gen";
|
|
29
|
+
|
|
30
|
+
const queryClient = new QueryClient();
|
|
31
|
+
|
|
32
|
+
const postsQuery = {
|
|
33
|
+
queryKey: ["posts"],
|
|
34
|
+
queryFn: async () => [{ id: 1, title: "Router first" }],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Route = createFileRoute("/posts")({
|
|
38
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
39
|
+
component: PostsPage,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function PostsPage() {
|
|
43
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
44
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
48
|
+
|
|
49
|
+
export function App() {
|
|
50
|
+
return (
|
|
51
|
+
<QueryClientProvider client={queryClient}>
|
|
52
|
+
<RouterProvider router={router} />
|
|
53
|
+
</QueryClientProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Core Integration Patterns
|
|
59
|
+
|
|
60
|
+
### Put QueryClient in router context
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
64
|
+
import { createRouter } from "@tanstack/react-router";
|
|
65
|
+
import { routeTree } from "./routeTree.gen";
|
|
66
|
+
|
|
67
|
+
const queryClient = new QueryClient();
|
|
68
|
+
|
|
69
|
+
export const router = createRouter({
|
|
70
|
+
routeTree,
|
|
71
|
+
context: { queryClient },
|
|
72
|
+
defaultPreloadStaleTime: 0,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Set `defaultPreloadStaleTime: 0` when Query owns server-state freshness. Router still preloads and runs loaders, but Query is the cache authority.
|
|
77
|
+
|
|
78
|
+
### Use loader ensureQueryData for route data
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
82
|
+
|
|
83
|
+
const todoQuery = (todoId: string) => ({
|
|
84
|
+
queryKey: ["todo", todoId],
|
|
85
|
+
queryFn: async () => ({ id: todoId, title: "Loaded" }),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export const Route = createFileRoute("/todos/$todoId")({
|
|
89
|
+
loader: ({ context, params }) => context.queryClient.ensureQueryData(todoQuery(params.todoId)),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Treat loaders as event handlers that prime the Query cache. Components should still call `useQuery` or `useSuspenseQuery` so the route has an active Query observer.
|
|
94
|
+
|
|
95
|
+
### Prefer Router SSR Query integration for Router SSR
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
99
|
+
import { createRouter } from "@tanstack/react-router";
|
|
100
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
101
|
+
import { routeTree } from "./routeTree.gen";
|
|
102
|
+
|
|
103
|
+
const queryClient = new QueryClient();
|
|
104
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
105
|
+
|
|
106
|
+
setupRouterSsrQueryIntegration({ router, queryClient });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Common Mistakes
|
|
110
|
+
|
|
111
|
+
### CRITICAL Component-only Query creates route waterfall
|
|
112
|
+
|
|
113
|
+
Wrong:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
117
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
118
|
+
|
|
119
|
+
export const Route = createFileRoute("/posts")({ component: PostsPage });
|
|
120
|
+
function PostsPage() {
|
|
121
|
+
const { data } = useSuspenseQuery({
|
|
122
|
+
queryKey: ["posts"],
|
|
123
|
+
queryFn: async () => [{ id: 1 }],
|
|
124
|
+
});
|
|
125
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Correct:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
133
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
134
|
+
|
|
135
|
+
const postsQuery = { queryKey: ["posts"], queryFn: async () => [{ id: 1 }] };
|
|
136
|
+
export const Route = createFileRoute("/posts")({
|
|
137
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
138
|
+
component: PostsPage,
|
|
139
|
+
});
|
|
140
|
+
function PostsPage() {
|
|
141
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
142
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The router can load route-critical data before component render.
|
|
147
|
+
|
|
148
|
+
Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
149
|
+
|
|
150
|
+
### CRITICAL Hand-rolled Router hydration
|
|
151
|
+
|
|
152
|
+
Wrong:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
156
|
+
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
|
157
|
+
import { routeTree } from "./routeTree.gen";
|
|
158
|
+
|
|
159
|
+
const queryClient = new QueryClient();
|
|
160
|
+
const router = createRouter({ routeTree });
|
|
161
|
+
export function App() {
|
|
162
|
+
return (
|
|
163
|
+
<QueryClientProvider client={queryClient}>
|
|
164
|
+
<RouterProvider router={router} />
|
|
165
|
+
</QueryClientProvider>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Correct:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
174
|
+
import { createRouter } from "@tanstack/react-router";
|
|
175
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
176
|
+
import { routeTree } from "./routeTree.gen";
|
|
177
|
+
|
|
178
|
+
const queryClient = new QueryClient();
|
|
179
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
180
|
+
setupRouterSsrQueryIntegration({ router, queryClient });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The Router SSR Query integration handles dehydration, hydration, streamed query results, redirects, and provider wrapping.
|
|
184
|
+
|
|
185
|
+
Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
186
|
+
|
|
187
|
+
### HIGH Next.js mental model copied into Start
|
|
188
|
+
|
|
189
|
+
Wrong:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
export default async function Page() {
|
|
193
|
+
const posts = await Promise.resolve([{ id: 1 }]);
|
|
194
|
+
return <pre>{JSON.stringify(posts)}</pre>;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Correct:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
202
|
+
|
|
203
|
+
export const Route = createFileRoute("/posts")({
|
|
204
|
+
loader: ({ context }) =>
|
|
205
|
+
context.queryClient.ensureQueryData({
|
|
206
|
+
queryKey: ["posts"],
|
|
207
|
+
queryFn: async () => [{ id: 1 }],
|
|
208
|
+
}),
|
|
209
|
+
component: () => <p>Posts loaded by the route</p>,
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Start is powered by TanStack Router; use file routes, loaders, server functions, and Router SSR before Next-specific app/pages APIs.
|
|
214
|
+
|
|
215
|
+
Source: TanStack/start:https://tanstack.com/start/latest/docs/framework/react/overview
|
|
216
|
+
|
|
217
|
+
### HIGH Reading loader data instead of observing Query
|
|
218
|
+
|
|
219
|
+
Wrong:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import { createFileRoute, useLoaderData } from "@tanstack/react-router";
|
|
223
|
+
|
|
224
|
+
export const Route = createFileRoute("/posts")({
|
|
225
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
226
|
+
component: PostsPage,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
function PostsPage() {
|
|
230
|
+
const posts = useLoaderData({ from: "/posts" });
|
|
231
|
+
return <pre>{JSON.stringify(posts)}</pre>;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Correct:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
239
|
+
|
|
240
|
+
function PostsPage() {
|
|
241
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
242
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The loader primes the cache. The component still needs an active Query observer for refetch triggers, invalidation, and garbage collection semantics.
|
|
247
|
+
|
|
248
|
+
See also: `lifecycle/ssr-hydration-and-streaming` for framework SSR recipes.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/enforce-query-best-practices-with-eslint
|
|
3
|
+
description: >
|
|
4
|
+
Use this when configuring @tanstack/eslint-plugin-query, flat/recommended,
|
|
5
|
+
flat/recommended-strict, exhaustive-deps, no-rest-destructuring,
|
|
6
|
+
no-unstable-deps, no-void-query-fn, stable-query-client, prefer-query-options,
|
|
7
|
+
infinite-query-property-order, and mutation-property-order.
|
|
8
|
+
type: composition
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- core/design-query-keys-and-options
|
|
13
|
+
- core/fetch-and-observe-queries
|
|
14
|
+
- framework/shape-data-and-render-efficiently
|
|
15
|
+
sources:
|
|
16
|
+
- TanStack/query:docs/eslint/eslint-plugin-query.md
|
|
17
|
+
- TanStack/query:docs/eslint/exhaustive-deps.md
|
|
18
|
+
- TanStack/query:docs/eslint/no-rest-destructuring.md
|
|
19
|
+
- TanStack/query:docs/eslint/no-unstable-deps.md
|
|
20
|
+
- TanStack/query:docs/eslint/no-void-query-fn.md
|
|
21
|
+
- TanStack/query:docs/eslint/prefer-query-options.md
|
|
22
|
+
- TanStack/query:docs/eslint/stable-query-client.md
|
|
23
|
+
- TanStack/query:docs/eslint/infinite-query-property-order.md
|
|
24
|
+
- TanStack/query:docs/eslint/mutation-property-order.md
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
31
|
+
|
|
32
|
+
export default [...pluginQuery.configs["flat/recommended"]];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Core Integration Patterns
|
|
36
|
+
|
|
37
|
+
### Use strict when generating Query-heavy code
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
41
|
+
|
|
42
|
+
export default [...pluginQuery.configs["flat/recommended-strict"]];
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Prefer option factories
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { queryOptions, useQuery } from "@tanstack/react-query";
|
|
49
|
+
|
|
50
|
+
const todosOptions = queryOptions({
|
|
51
|
+
queryKey: ["todos"],
|
|
52
|
+
queryFn: async () => [{ id: 1 }],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export function useTodos() {
|
|
56
|
+
return useQuery(todosOptions);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Keep inference-sensitive property order
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
64
|
+
|
|
65
|
+
export function useFeed() {
|
|
66
|
+
return useInfiniteQuery({
|
|
67
|
+
queryKey: ["feed"],
|
|
68
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
69
|
+
initialPageParam: 0,
|
|
70
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Common Mistakes
|
|
76
|
+
|
|
77
|
+
### MEDIUM Strict rule not enabled
|
|
78
|
+
|
|
79
|
+
Wrong:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
83
|
+
|
|
84
|
+
export default [...pluginQuery.configs["flat/recommended"]];
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Correct:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
91
|
+
|
|
92
|
+
export default [...pluginQuery.configs["flat/recommended-strict"]];
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Strict mode catches option-factory and inference patterns that agents commonly miss.
|
|
96
|
+
|
|
97
|
+
Source: TanStack/query:docs/eslint/eslint-plugin-query.md
|
|
98
|
+
|
|
99
|
+
### MEDIUM Infinite option property order
|
|
100
|
+
|
|
101
|
+
Wrong:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
105
|
+
|
|
106
|
+
export function useFeed() {
|
|
107
|
+
return useInfiniteQuery({
|
|
108
|
+
queryFn: async () => ({ nextCursor: 1 }),
|
|
109
|
+
queryKey: ["feed"],
|
|
110
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
111
|
+
initialPageParam: 0,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Correct:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
120
|
+
|
|
121
|
+
export function useFeed() {
|
|
122
|
+
return useInfiniteQuery({
|
|
123
|
+
queryKey: ["feed"],
|
|
124
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
125
|
+
initialPageParam: 0,
|
|
126
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Some infinite-query inference depends on stable option ordering.
|
|
132
|
+
|
|
133
|
+
Source: TanStack/query:docs/eslint/infinite-query-property-order.md
|
|
134
|
+
|
|
135
|
+
### MEDIUM Mutation option property order
|
|
136
|
+
|
|
137
|
+
Wrong:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { useMutation } from "@tanstack/react-query";
|
|
141
|
+
|
|
142
|
+
export function useSave() {
|
|
143
|
+
return useMutation({
|
|
144
|
+
onSuccess: () => console.log("saved"),
|
|
145
|
+
mutationFn: async (title: string) => title,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Correct:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { useMutation } from "@tanstack/react-query";
|
|
154
|
+
|
|
155
|
+
export function useSave() {
|
|
156
|
+
return useMutation({
|
|
157
|
+
mutationFn: async (title: string) => title,
|
|
158
|
+
onSuccess: () => console.log("saved"),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The mutation property order rule preserves inference for mutation options.
|
|
164
|
+
|
|
165
|
+
Source: TanStack/query:docs/eslint/mutation-property-order.md
|
|
166
|
+
|
|
167
|
+
See also: `core/design-query-keys-and-options` for the patterns these rules protect.
|