feat(web): add TanStack Query infrastructure (Phase 0)
- Install @tanstack/react-query v5 + devtools - Create core/query-client.ts with WS-optimized defaults (staleTime: Infinity) - Create QueryProvider and wire into root layout - Add @core/* path alias to tsconfig + vitest - Add useWorkspaceId() bridge hook for query key scoping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5fe1ec806d
commit
2be9f6cd2f
9 changed files with 108 additions and 6 deletions
|
|
@ -4,6 +4,7 @@ import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { QueryProvider } from "@core/provider";
|
||||||
import { AuthInitializer } from "@/features/auth";
|
import { AuthInitializer } from "@/features/auth";
|
||||||
import { WSProvider } from "@/features/realtime";
|
import { WSProvider } from "@/features/realtime";
|
||||||
import { ModalRegistry } from "@/features/modals";
|
import { ModalRegistry } from "@/features/modals";
|
||||||
|
|
@ -67,11 +68,13 @@ export default async function RootLayout({
|
||||||
>
|
>
|
||||||
<body className="h-full overflow-hidden">
|
<body className="h-full overflow-hidden">
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
|
<QueryProvider>
|
||||||
<AuthInitializer>
|
<AuthInitializer>
|
||||||
<WSProvider>{children}</WSProvider>
|
<WSProvider>{children}</WSProvider>
|
||||||
</AuthInitializer>
|
</AuthInitializer>
|
||||||
<ModalRegistry />
|
<ModalRegistry />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
</QueryProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
17
apps/web/core/hooks.ts
Normal file
17
apps/web/core/hooks.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useWorkspaceStore } from "@/features/workspace";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current workspace ID.
|
||||||
|
*
|
||||||
|
* Bridge hook: reads from Zustand workspace store now.
|
||||||
|
* Phase 3 will switch to core/workspace/store.ts — signature stays the same.
|
||||||
|
*/
|
||||||
|
export function useWorkspaceId(): string {
|
||||||
|
const workspaceId = useWorkspaceStore((s) => s.workspace?.id);
|
||||||
|
if (!workspaceId) {
|
||||||
|
throw new Error("useWorkspaceId: no workspace selected");
|
||||||
|
}
|
||||||
|
return workspaceId;
|
||||||
|
}
|
||||||
3
apps/web/core/index.ts
Normal file
3
apps/web/core/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { createQueryClient } from "./query-client";
|
||||||
|
export { QueryProvider } from "./provider";
|
||||||
|
export { useWorkspaceId } from "./hooks";
|
||||||
17
apps/web/core/provider.tsx
Normal file
17
apps/web/core/provider.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||||
|
import { createQueryClient } from "./query-client";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function QueryProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [queryClient] = useState(createQueryClient);
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
18
apps/web/core/query-client.ts
Normal file
18
apps/web/core/query-client.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { QueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export function createQueryClient(): QueryClient {
|
||||||
|
return new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: Infinity,
|
||||||
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
retry: 1,
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
retry: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -18,11 +18,12 @@
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@emoji-mart/data": "^1.2.1",
|
"@emoji-mart/data": "^1.2.1",
|
||||||
"@floating-ui/dom": "^1.7.6",
|
"@floating-ui/dom": "^1.7.6",
|
||||||
|
"@tanstack/react-query": "^5.96.2",
|
||||||
|
"@tanstack/react-query-devtools": "^5.96.2",
|
||||||
"@tiptap/extension-code-block-lowlight": "^3.22.1",
|
"@tiptap/extension-code-block-lowlight": "^3.22.1",
|
||||||
"@tiptap/extension-image": "^3.22.1",
|
"@tiptap/extension-image": "^3.22.1",
|
||||||
"@tiptap/extension-link": "^3.22.1",
|
"@tiptap/extension-link": "^3.22.1",
|
||||||
"@tiptap/extension-mention": "^3.22.1",
|
"@tiptap/extension-mention": "^3.22.1",
|
||||||
"@tiptap/suggestion": "^3.22.1",
|
|
||||||
"@tiptap/extension-placeholder": "^3.22.1",
|
"@tiptap/extension-placeholder": "^3.22.1",
|
||||||
"@tiptap/extension-table": "^3.22.1",
|
"@tiptap/extension-table": "^3.22.1",
|
||||||
"@tiptap/extension-table-cell": "^3.22.1",
|
"@tiptap/extension-table-cell": "^3.22.1",
|
||||||
|
|
@ -33,6 +34,7 @@
|
||||||
"@tiptap/pm": "^3.22.1",
|
"@tiptap/pm": "^3.22.1",
|
||||||
"@tiptap/react": "^3.22.1",
|
"@tiptap/react": "^3.22.1",
|
||||||
"@tiptap/starter-kit": "^3.22.1",
|
"@tiptap/starter-kit": "^3.22.1",
|
||||||
|
"@tiptap/suggestion": "^3.22.1",
|
||||||
"@types/linkify-it": "^5.0.0",
|
"@types/linkify-it": "^5.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
"./*"
|
"./*"
|
||||||
|
],
|
||||||
|
"@core/*": [
|
||||||
|
"./core/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "."),
|
"@": path.resolve(__dirname, "."),
|
||||||
|
"@core": path.resolve(__dirname, "core"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
|
|
@ -75,6 +75,12 @@ importers:
|
||||||
'@floating-ui/dom':
|
'@floating-ui/dom':
|
||||||
specifier: ^1.7.6
|
specifier: ^1.7.6
|
||||||
version: 1.7.6
|
version: 1.7.6
|
||||||
|
'@tanstack/react-query':
|
||||||
|
specifier: ^5.96.2
|
||||||
|
version: 5.96.2(react@19.2.3)
|
||||||
|
'@tanstack/react-query-devtools':
|
||||||
|
specifier: ^5.96.2
|
||||||
|
version: 5.96.2(@tanstack/react-query@5.96.2(react@19.2.3))(react@19.2.3)
|
||||||
'@tiptap/extension-code-block-lowlight':
|
'@tiptap/extension-code-block-lowlight':
|
||||||
specifier: ^3.22.1
|
specifier: ^3.22.1
|
||||||
version: 3.22.1(@tiptap/core@3.22.1(@tiptap/pm@3.22.1))(@tiptap/extension-code-block@3.22.1(@tiptap/core@3.22.1(@tiptap/pm@3.22.1))(@tiptap/pm@3.22.1))(@tiptap/pm@3.22.1)(highlight.js@11.11.1)(lowlight@3.3.0)
|
version: 3.22.1(@tiptap/core@3.22.1(@tiptap/pm@3.22.1))(@tiptap/extension-code-block@3.22.1(@tiptap/core@3.22.1(@tiptap/pm@3.22.1))(@tiptap/pm@3.22.1))(@tiptap/pm@3.22.1)(highlight.js@11.11.1)(lowlight@3.3.0)
|
||||||
|
|
@ -1288,6 +1294,23 @@ packages:
|
||||||
'@tailwindcss/postcss@4.2.2':
|
'@tailwindcss/postcss@4.2.2':
|
||||||
resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==}
|
resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==}
|
||||||
|
|
||||||
|
'@tanstack/query-core@5.96.2':
|
||||||
|
resolution: {integrity: sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA==}
|
||||||
|
|
||||||
|
'@tanstack/query-devtools@5.96.2':
|
||||||
|
resolution: {integrity: sha512-vBTB1Qhbm3nHSbEUtQwks/EdcAtFfEapr1WyBW4w2ExYKuXVi3jIxUIHf5MlSltiHuL7zNyUuanqT/7sI2sb6g==}
|
||||||
|
|
||||||
|
'@tanstack/react-query-devtools@5.96.2':
|
||||||
|
resolution: {integrity: sha512-nTFKLGuTOFvmFRvcyZ3ArWC/DnMNPoBh6h/2yD6rsf7TCTJCQt+oUWOp2uKPTIuEPtF/vN9Kw5tl5mD1Kbposw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tanstack/react-query': ^5.96.2
|
||||||
|
react: ^18 || ^19
|
||||||
|
|
||||||
|
'@tanstack/react-query@5.96.2':
|
||||||
|
resolution: {integrity: sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18 || ^19
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
@ -4917,6 +4940,21 @@ snapshots:
|
||||||
postcss: 8.5.8
|
postcss: 8.5.8
|
||||||
tailwindcss: 4.2.2
|
tailwindcss: 4.2.2
|
||||||
|
|
||||||
|
'@tanstack/query-core@5.96.2': {}
|
||||||
|
|
||||||
|
'@tanstack/query-devtools@5.96.2': {}
|
||||||
|
|
||||||
|
'@tanstack/react-query-devtools@5.96.2(@tanstack/react-query@5.96.2(react@19.2.3))(react@19.2.3)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-devtools': 5.96.2
|
||||||
|
'@tanstack/react-query': 5.96.2(react@19.2.3)
|
||||||
|
react: 19.2.3
|
||||||
|
|
||||||
|
'@tanstack/react-query@5.96.2(react@19.2.3)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-core': 5.96.2
|
||||||
|
react: 19.2.3
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.29.0
|
'@babel/code-frame': 7.29.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue