@checkstack/frontend 0.0.5 → 0.2.0

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 (3) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/package.json +3 -1
  3. package/src/App.tsx +49 -27
package/CHANGELOG.md CHANGED
@@ -1,5 +1,153 @@
1
1
  # @checkstack/frontend
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7a23261: ## TanStack Query Integration
8
+
9
+ Migrated all frontend components to use `usePluginClient` hook with TanStack Query integration, replacing the legacy `forPlugin()` pattern.
10
+
11
+ ### New Features
12
+
13
+ - **`usePluginClient` hook**: Provides type-safe access to plugin APIs with `.useQuery()` and `.useMutation()` methods
14
+ - **Automatic request deduplication**: Multiple components requesting the same data share a single network request
15
+ - **Built-in caching**: Configurable stale time and cache duration per query
16
+ - **Loading/error states**: TanStack Query provides `isLoading`, `error`, `isRefetching` states automatically
17
+ - **Background refetching**: Stale data is automatically refreshed when components mount
18
+
19
+ ### Contract Changes
20
+
21
+ All RPC contracts now require `operationType: "query"` or `operationType: "mutation"` metadata:
22
+
23
+ ```typescript
24
+ const getItems = proc()
25
+ .meta({ operationType: "query", access: [access.read] })
26
+ .output(z.array(itemSchema))
27
+ .query();
28
+
29
+ const createItem = proc()
30
+ .meta({ operationType: "mutation", access: [access.manage] })
31
+ .input(createItemSchema)
32
+ .output(itemSchema)
33
+ .mutation();
34
+ ```
35
+
36
+ ### Migration
37
+
38
+ ```typescript
39
+ // Before (forPlugin pattern)
40
+ const api = useApi(myPluginApiRef);
41
+ const [items, setItems] = useState<Item[]>([]);
42
+ useEffect(() => {
43
+ api.getItems().then(setItems);
44
+ }, [api]);
45
+
46
+ // After (usePluginClient pattern)
47
+ const client = usePluginClient(MyPluginApi);
48
+ const { data: items, isLoading } = client.getItems.useQuery({});
49
+ ```
50
+
51
+ ### Bug Fixes
52
+
53
+ - Fixed `rpc.test.ts` test setup for middleware type inference
54
+ - Fixed `SearchDialog` to use `setQuery` instead of deprecated `search` method
55
+ - Fixed null→undefined warnings in notification and queue frontends
56
+
57
+ ### Patch Changes
58
+
59
+ - Updated dependencies [7a23261]
60
+ - @checkstack/frontend-api@0.2.0
61
+ - @checkstack/common@0.3.0
62
+ - @checkstack/auth-frontend@0.3.0
63
+ - @checkstack/catalog-frontend@0.3.0
64
+ - @checkstack/command-frontend@0.2.0
65
+ - @checkstack/ui@0.2.1
66
+ - @checkstack/signal-common@0.1.1
67
+ - @checkstack/signal-frontend@0.0.7
68
+
69
+ ## 0.1.0
70
+
71
+ ### Minor Changes
72
+
73
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
74
+
75
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
76
+
77
+ ## Changes
78
+
79
+ ### Core Infrastructure (`@checkstack/common`)
80
+
81
+ - Introduced `AccessRule` interface as the primary access control type
82
+ - Added `accessPair()` helper for creating read/manage access rule pairs
83
+ - Added `access()` builder for individual access rules
84
+ - Replaced `Permission` type with `AccessRule` throughout
85
+
86
+ ### API Changes
87
+
88
+ - `env.registerPermissions()` → `env.registerAccessRules()`
89
+ - `meta.permissions` → `meta.access` in RPC contracts
90
+ - `usePermission()` → `useAccess()` in frontend hooks
91
+ - Route `permission:` field → `accessRule:` field
92
+
93
+ ### UI Changes
94
+
95
+ - "Roles & Permissions" tab → "Roles & Access Rules"
96
+ - "You don't have permission..." → "You don't have access..."
97
+ - All permission-related UI text updated
98
+
99
+ ### Documentation & Templates
100
+
101
+ - Updated 18 documentation files with AccessRule terminology
102
+ - Updated 7 scaffolding templates with `accessPair()` pattern
103
+ - All code examples use new AccessRule API
104
+
105
+ ## Migration Guide
106
+
107
+ ### Backend Plugins
108
+
109
+ ```diff
110
+ - import { permissionList } from "./permissions";
111
+ - env.registerPermissions(permissionList);
112
+ + import { accessRules } from "./access";
113
+ + env.registerAccessRules(accessRules);
114
+ ```
115
+
116
+ ### RPC Contracts
117
+
118
+ ```diff
119
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
120
+ + .meta({ userType: "user", access: [access.read] })
121
+ ```
122
+
123
+ ### Frontend Hooks
124
+
125
+ ```diff
126
+ - const canRead = accessApi.usePermission(permissions.read.id);
127
+ + const canRead = accessApi.useAccess(access.read);
128
+ ```
129
+
130
+ ### Routes
131
+
132
+ ```diff
133
+ - permission: permissions.entityRead.id,
134
+ + accessRule: access.read,
135
+ ```
136
+
137
+ ### Patch Changes
138
+
139
+ - Updated dependencies [9faec1f]
140
+ - Updated dependencies [95eeec7]
141
+ - Updated dependencies [f533141]
142
+ - @checkstack/auth-frontend@0.2.0
143
+ - @checkstack/catalog-frontend@0.2.0
144
+ - @checkstack/command-frontend@0.1.0
145
+ - @checkstack/common@0.2.0
146
+ - @checkstack/frontend-api@0.1.0
147
+ - @checkstack/signal-common@0.1.0
148
+ - @checkstack/ui@0.2.0
149
+ - @checkstack/signal-frontend@0.0.6
150
+
3
151
  ## 0.0.5
4
152
 
5
153
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/frontend",
3
- "version": "0.0.5",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -21,6 +21,8 @@
21
21
  "@checkstack/signal-frontend": "workspace:*",
22
22
  "@checkstack/ui": "workspace:*",
23
23
  "@orpc/client": "^1.13.2",
24
+ "@tanstack/react-query": "^5.64.0",
25
+ "@tanstack/react-query-devtools": "^5.64.0",
24
26
  "better-auth": "^1.1.8",
25
27
  "class-variance-authority": "^0.7.0",
26
28
  "clsx": "^2.1.0",
package/src/App.tsx CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ApiProvider,
11
11
  ApiRegistryBuilder,
12
12
  loggerApiRef,
13
- permissionApiRef,
13
+ accessApiRef,
14
14
  fetchApiRef,
15
15
  rpcApiRef,
16
16
  useApi,
@@ -23,22 +23,36 @@ import {
23
23
  RuntimeConfigProvider,
24
24
  useRuntimeConfigLoading,
25
25
  useRuntimeConfig,
26
+ OrpcQueryProvider,
26
27
  } from "@checkstack/frontend-api";
28
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
29
+ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
27
30
  import { ConsoleLoggerApi } from "./apis/logger-api";
28
31
  import { CoreFetchApi } from "./apis/fetch-api";
29
32
  import { CoreRpcApi } from "./apis/rpc-api";
30
33
  import {
31
- PermissionDenied,
34
+ AccessDenied,
32
35
  LoadingSpinner,
33
36
  ToastProvider,
34
37
  AmbientBackground,
35
38
  } from "@checkstack/ui";
36
39
  import { SignalProvider } from "@checkstack/signal-frontend";
37
40
  import { usePluginLifecycle } from "./hooks/usePluginLifecycle";
38
- import {
39
- useCommands,
40
- useGlobalShortcuts,
41
- } from "@checkstack/command-frontend";
41
+ import { useCommands, useGlobalShortcuts } from "@checkstack/command-frontend";
42
+
43
+ // Create a stable query client instance
44
+ const queryClient = new QueryClient({
45
+ defaultOptions: {
46
+ queries: {
47
+ // Default stale time of 30 seconds for all queries
48
+ staleTime: 30_000,
49
+ // Keep unused data in cache for 5 minutes
50
+ gcTime: 5 * 60 * 1000,
51
+ // Don't refetch on window focus by default (can be overridden per query)
52
+ refetchOnWindowFocus: false,
53
+ },
54
+ },
55
+ });
42
56
 
43
57
  /**
44
58
  * Component that registers global keyboard shortcuts for all commands.
@@ -48,7 +62,7 @@ function GlobalShortcuts() {
48
62
  const { commands } = useCommands();
49
63
  const navigate = useNavigate();
50
64
 
51
- // Pass "*" as permission since backend already filters by permission
65
+ // Pass "*" as access since backend already filters by access
52
66
  useGlobalShortcuts(commands, navigate, ["*"]);
53
67
 
54
68
  // This component renders nothing - it only registers event listeners
@@ -57,10 +71,16 @@ function GlobalShortcuts() {
57
71
 
58
72
  const RouteGuard: React.FC<{
59
73
  children: React.ReactNode;
60
- permission?: string;
61
- }> = ({ children, permission }) => {
62
- const permissionApi = useApi(permissionApiRef);
63
- const { allowed, loading } = permissionApi.usePermission(permission || "");
74
+ accessRule?: string;
75
+ }> = ({ children, accessRule }) => {
76
+ const accessApi = useApi(accessApiRef);
77
+ // If there's an access rule requirement, use useAccess with a minimal AccessRule-like object
78
+ // the route.accessRule is already the qualified access rule ID string
79
+ const { allowed, loading } = accessRule
80
+ ? accessApi.useAccess({ id: accessRule } as Parameters<
81
+ typeof accessApi.useAccess
82
+ >[0])
83
+ : { allowed: true, loading: false };
64
84
 
65
85
  if (loading) {
66
86
  return (
@@ -70,10 +90,8 @@ const RouteGuard: React.FC<{
70
90
  );
71
91
  }
72
92
 
73
- const isAllowed = permission ? allowed : true;
74
-
75
- if (!isAllowed) {
76
- return <PermissionDenied />;
93
+ if (!allowed) {
94
+ return <AccessDenied />;
77
95
  }
78
96
 
79
97
  return <>{children}</>;
@@ -130,7 +148,7 @@ function AppContent() {
130
148
  key={route.path}
131
149
  path={route.path}
132
150
  element={
133
- <RouteGuard permission={route.permission}>
151
+ <RouteGuard accessRule={route.accessRule}>
134
152
  {route.element}
135
153
  </RouteGuard>
136
154
  }
@@ -154,10 +172,8 @@ function AppWithApis() {
154
172
  // Initialize API Registry with core apiRefs
155
173
  const registryBuilder = new ApiRegistryBuilder()
156
174
  .register(loggerApiRef, new ConsoleLoggerApi())
157
- .register(permissionApiRef, {
158
- usePermission: () => ({ loading: false, allowed: true }), // Default to allow all if no auth plugin present
159
- useResourcePermission: () => ({ loading: false, allowed: true }),
160
- useManagePermission: () => ({ loading: false, allowed: true }),
175
+ .register(accessApiRef, {
176
+ useAccess: () => ({ loading: false, allowed: true }), // Default to allow all if no auth plugin present
161
177
  })
162
178
  .registerFactory(fetchApiRef, (_registry) => {
163
179
  return new CoreFetchApi(baseUrl);
@@ -195,13 +211,19 @@ function AppWithApis() {
195
211
  }
196
212
 
197
213
  return (
198
- <ApiProvider registry={apiRegistry}>
199
- <SignalProvider backendUrl={baseUrl}>
200
- <ToastProvider>
201
- <AppContent />
202
- </ToastProvider>
203
- </SignalProvider>
204
- </ApiProvider>
214
+ <QueryClientProvider client={queryClient}>
215
+ <ApiProvider registry={apiRegistry}>
216
+ <OrpcQueryProvider>
217
+ <SignalProvider backendUrl={baseUrl}>
218
+ <ToastProvider>
219
+ <AppContent />
220
+ </ToastProvider>
221
+ </SignalProvider>
222
+ </OrpcQueryProvider>
223
+ </ApiProvider>
224
+ {/* DevTools only in development - toggle with keyboard or floating button */}
225
+ <ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
226
+ </QueryClientProvider>
205
227
  );
206
228
  }
207
229