@oxyhq/auth 2.0.5 → 2.0.6

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.
@@ -6,3 +6,4 @@
6
6
  */
7
7
  export { useUpdateProfile, useUploadAvatar, useUpdateAccountSettings, useUpdatePrivacySettings, useUploadFile, } from './useAccountMutations';
8
8
  export { useSwitchSession, useLogoutSession, useLogoutAll, useUpdateDeviceName, useRemoveDevice, } from './useServicesMutations';
9
+ export { useSetAppData, useDeleteAppData } from './useAppData';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * App-Data Mutation Hooks
3
+ *
4
+ * Write side of the per-user JSON KV store. Both `useSetAppData` and
5
+ * `useDeleteAppData` apply optimistic updates against the two query keys
6
+ * that observe this data (`appDataQueryKeys.value` and the surrounding
7
+ * `appDataQueryKeys.namespace`) and roll back on error.
8
+ *
9
+ * When the underlying request fails because the endpoint isn't reachable
10
+ * (404 / network), the mutation still surfaces the error — write attempts
11
+ * are user-initiated and the caller may want to retry or fall back to
12
+ * local persistence. Reads are silent about missing endpoints; writes are
13
+ * not.
14
+ */
15
+ interface SetAppDataVariables<T> {
16
+ namespace: string;
17
+ key: string;
18
+ value: T;
19
+ }
20
+ interface SetAppDataContext<T> {
21
+ previousValue: T | null | undefined;
22
+ previousNamespace: Record<string, T> | undefined;
23
+ }
24
+ /**
25
+ * Upsert a per-user JSON value. Returns the value the server confirmed it
26
+ * stored — typically identical to the input but consumers should prefer the
27
+ * returned value (the server is the source of truth).
28
+ *
29
+ * Applies optimistic updates against both the single-value query key and
30
+ * the surrounding namespace query key, then rolls back on error.
31
+ */
32
+ export declare const useSetAppData: <T = unknown>() => import("@tanstack/react-query").UseMutationResult<T, Error, SetAppDataVariables<T>, SetAppDataContext<T>>;
33
+ interface DeleteAppDataVariables {
34
+ namespace: string;
35
+ key: string;
36
+ }
37
+ interface DeleteAppDataContext<T> {
38
+ previousValue: T | null | undefined;
39
+ previousNamespace: Record<string, T> | undefined;
40
+ }
41
+ /**
42
+ * Delete a per-user JSON value. Optimistically removes the entry from the
43
+ * single-value cache and from the surrounding namespace map, then rolls back
44
+ * on error.
45
+ */
46
+ export declare const useDeleteAppData: <T = unknown>() => import("@tanstack/react-query").UseMutationResult<void, Error, DeleteAppDataVariables, DeleteAppDataContext<T>>;
47
+ export {};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Query keys + error utilities for `useAppData` hooks.
3
+ *
4
+ * Lives next to the hook file so consumers can import the keys directly
5
+ * when they need to imperatively invalidate a value (e.g. after a non-React
6
+ * write through `oxyServices.setAppData`).
7
+ */
8
+ export declare const appDataQueryKeys: {
9
+ readonly all: readonly ["appData"];
10
+ readonly namespaces: () => readonly ["appData", "namespace"];
11
+ readonly namespace: (namespace: string) => readonly ["appData", "namespace", string];
12
+ readonly values: () => readonly ["appData", "value"];
13
+ readonly value: (namespace: string, key: string) => readonly ["appData", "value", string, string];
14
+ };
15
+ /**
16
+ * True when `error` indicates the app-data endpoint isn't reachable — either
17
+ * because the API deployment doesn't have it yet (404) or there's a network
18
+ * failure with no response. We treat these as "no value stored" so consumers
19
+ * fall back to local persistence without surfacing a user-facing error.
20
+ *
21
+ * Anything else (401, 403, 500) propagates normally — those are real bugs
22
+ * the auth or retry pipeline needs to see.
23
+ */
24
+ export declare function isMissingAppDataEndpointError(error: unknown): boolean;
@@ -8,3 +8,5 @@ export { useUserProfile, useUserProfiles, useCurrentUser, useUserById, useUserBy
8
8
  export { useSessions, useSession, useDeviceSessions, useUserDevices, useSecurityInfo, } from './useServicesQueries';
9
9
  export { useSecurityActivity, useRecentSecurityActivity, } from './useSecurityQueries';
10
10
  export { queryKeys, invalidateAccountQueries, invalidateUserQueries, invalidateSessionQueries } from './queryKeys';
11
+ export { useAppData, useAppDataNamespace } from './useAppData';
12
+ export { appDataQueryKeys, isMissingAppDataEndpointError } from './appDataQueryKeys';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * App-Data Query Hooks
3
+ *
4
+ * Read side of the `/users/me/app-data/...` per-user JSON KV store. Gated on
5
+ * `isAuthenticated` — when signed out the query stays `enabled: false` and
6
+ * `data` is `null`, so consumers can fall back to localStorage without ever
7
+ * issuing a doomed request.
8
+ *
9
+ * Errors from the network (404 because the endpoint isn't deployed yet,
10
+ * 401 because the session lapsed, etc.) are not user-facing here. Hooks
11
+ * return `data: null` on error so the calling component renders the
12
+ * "nothing yet" state and the consuming app can quietly fall back to local
13
+ * persistence. Mutations still propagate errors so write attempts surface
14
+ * a toast — only reads are silent.
15
+ */
16
+ import { type UseQueryResult } from '@tanstack/react-query';
17
+ interface AppDataQueryOptions {
18
+ /** Disable the query without unmounting the component. */
19
+ enabled?: boolean;
20
+ /** Override the default 1-minute stale time. */
21
+ staleTime?: number;
22
+ /** Override the default 30-minute gc time. */
23
+ gcTime?: number;
24
+ }
25
+ /**
26
+ * Read a single per-user JSON value.
27
+ *
28
+ * @param namespace - kebab/snake-case identifier (e.g. `"academy"`).
29
+ * @param key - kebab/snake-case identifier (e.g. course slug).
30
+ * @param options - optional `enabled`/`staleTime`/`gcTime` overrides.
31
+ *
32
+ * @returns A `useQuery` result with `data` of type `T | null`. The query
33
+ * stays disabled when the user is signed out; when enabled but the server
34
+ * has no stored value, `data` is `null`. Reads that fail because the
35
+ * endpoint isn't reachable also resolve to `null` so the consumer can
36
+ * fall back to local persistence.
37
+ */
38
+ export declare const useAppData: <T = unknown>(namespace: string, key: string, options?: AppDataQueryOptions) => UseQueryResult<T | null, Error>;
39
+ /**
40
+ * Read every value in a namespace.
41
+ *
42
+ * @returns A `useQuery` result with `data` as a `Record<string, T>`. Empty
43
+ * object when the namespace contains nothing (or when fetching failed).
44
+ */
45
+ export declare const useAppDataNamespace: <T = unknown>(namespace: string, options?: AppDataQueryOptions) => UseQueryResult<Record<string, T>, Error>;
46
+ export {};
@@ -29,8 +29,8 @@ export { useAssetStore, useAssets as useAssetsStore, useAsset, useUploadProgress
29
29
  export { useAccountStore, useAccounts, useAccountLoading, useAccountError, useAccountLoadingSession, } from './stores/accountStore';
30
30
  export type { QuickAccount } from './stores/accountStore';
31
31
  export { useFollowStore, } from './stores/followStore';
32
- export { useUserProfile, useUserProfiles, useCurrentUser, useUserById, useUserByUsername, useUsersBySessions, usePrivacySettings, useSessions, useSession, useDeviceSessions, useUserDevices, useSecurityInfo, useSecurityActivity, useRecentSecurityActivity, } from './hooks/queries';
33
- export { useUpdateProfile, useUploadAvatar, useUpdateAccountSettings, useUpdatePrivacySettings, useUploadFile, useSwitchSession, useLogoutSession, useLogoutAll, useUpdateDeviceName, useRemoveDevice, } from './hooks/mutations';
32
+ export { useUserProfile, useUserProfiles, useCurrentUser, useUserById, useUserByUsername, useUsersBySessions, usePrivacySettings, useSessions, useSession, useDeviceSessions, useUserDevices, useSecurityInfo, useSecurityActivity, useRecentSecurityActivity, useAppData, useAppDataNamespace, appDataQueryKeys, isMissingAppDataEndpointError, } from './hooks/queries';
33
+ export { useUpdateProfile, useUploadAvatar, useUpdateAccountSettings, useUpdatePrivacySettings, useUploadFile, useSwitchSession, useLogoutSession, useLogoutAll, useUpdateDeviceName, useRemoveDevice, useSetAppData, useDeleteAppData, } from './hooks/mutations';
34
34
  export { createProfileMutation, createGenericMutation, } from './hooks/mutations/mutationFactory';
35
35
  export type { ProfileMutationConfig, GenericMutationConfig, } from './hooks/mutations/mutationFactory';
36
36
  export { useWebSSO, isWebBrowser } from './hooks/useWebSSO';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/auth",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "OxyHQ Web Authentication SDK — headless auth with React hooks for Next.js, Vite, and web apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -23,3 +23,6 @@ export {
23
23
  useRemoveDevice,
24
24
  } from './useServicesMutations';
25
25
 
26
+ // App-data KV store mutation hooks
27
+ export { useSetAppData, useDeleteAppData } from './useAppData';
28
+
@@ -0,0 +1,167 @@
1
+ /**
2
+ * App-Data Mutation Hooks
3
+ *
4
+ * Write side of the per-user JSON KV store. Both `useSetAppData` and
5
+ * `useDeleteAppData` apply optimistic updates against the two query keys
6
+ * that observe this data (`appDataQueryKeys.value` and the surrounding
7
+ * `appDataQueryKeys.namespace`) and roll back on error.
8
+ *
9
+ * When the underlying request fails because the endpoint isn't reachable
10
+ * (404 / network), the mutation still surfaces the error — write attempts
11
+ * are user-initiated and the caller may want to retry or fall back to
12
+ * local persistence. Reads are silent about missing endpoints; writes are
13
+ * not.
14
+ */
15
+
16
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
17
+ import { authenticatedApiCall } from '@oxyhq/core';
18
+ import { useWebOxy } from '../../WebOxyProvider';
19
+ import { appDataQueryKeys } from '../queries/appDataQueryKeys';
20
+
21
+ interface SetAppDataVariables<T> {
22
+ namespace: string;
23
+ key: string;
24
+ value: T;
25
+ }
26
+
27
+ interface SetAppDataContext<T> {
28
+ previousValue: T | null | undefined;
29
+ previousNamespace: Record<string, T> | undefined;
30
+ }
31
+
32
+ /**
33
+ * Upsert a per-user JSON value. Returns the value the server confirmed it
34
+ * stored — typically identical to the input but consumers should prefer the
35
+ * returned value (the server is the source of truth).
36
+ *
37
+ * Applies optimistic updates against both the single-value query key and
38
+ * the surrounding namespace query key, then rolls back on error.
39
+ */
40
+ export const useSetAppData = <T = unknown>() => {
41
+ const { oxyServices, activeSessionId } = useWebOxy();
42
+ const queryClient = useQueryClient();
43
+
44
+ return useMutation<T, Error, SetAppDataVariables<T>, SetAppDataContext<T>>({
45
+ mutationKey: ['appData', 'set'],
46
+ mutationFn: async ({ namespace, key, value }) => {
47
+ return authenticatedApiCall(oxyServices, activeSessionId, () =>
48
+ oxyServices.setAppData<T>(namespace, key, value),
49
+ );
50
+ },
51
+ onMutate: async ({ namespace, key, value }) => {
52
+ const valueKey = appDataQueryKeys.value(namespace, key);
53
+ const namespaceKey = appDataQueryKeys.namespace(namespace);
54
+
55
+ await Promise.all([
56
+ queryClient.cancelQueries({ queryKey: valueKey }),
57
+ queryClient.cancelQueries({ queryKey: namespaceKey }),
58
+ ]);
59
+
60
+ const previousValue = queryClient.getQueryData<T | null>(valueKey);
61
+ const previousNamespace = queryClient.getQueryData<Record<string, T>>(namespaceKey);
62
+
63
+ queryClient.setQueryData<T | null>(valueKey, value);
64
+ if (previousNamespace) {
65
+ queryClient.setQueryData<Record<string, T>>(namespaceKey, {
66
+ ...previousNamespace,
67
+ [key]: value,
68
+ });
69
+ }
70
+
71
+ return { previousValue, previousNamespace };
72
+ },
73
+ onError: (_error, { namespace, key }, context) => {
74
+ if (!context) return;
75
+ const valueKey = appDataQueryKeys.value(namespace, key);
76
+ const namespaceKey = appDataQueryKeys.namespace(namespace);
77
+
78
+ // Restore exactly the snapshots we captured in onMutate. Don't merge
79
+ // with whatever's currently in the cache — that could splice in writes
80
+ // from concurrent mutations and undo their state.
81
+ queryClient.setQueryData(valueKey, context.previousValue ?? null);
82
+ if (context.previousNamespace !== undefined) {
83
+ queryClient.setQueryData(namespaceKey, context.previousNamespace);
84
+ }
85
+ },
86
+ onSuccess: (data, { namespace, key }) => {
87
+ const valueKey = appDataQueryKeys.value(namespace, key);
88
+ const namespaceKey = appDataQueryKeys.namespace(namespace);
89
+
90
+ queryClient.setQueryData(valueKey, data);
91
+ const existingNamespace = queryClient.getQueryData<Record<string, T>>(namespaceKey);
92
+ if (existingNamespace) {
93
+ queryClient.setQueryData<Record<string, T>>(namespaceKey, {
94
+ ...existingNamespace,
95
+ [key]: data,
96
+ });
97
+ }
98
+ },
99
+ });
100
+ };
101
+
102
+ interface DeleteAppDataVariables {
103
+ namespace: string;
104
+ key: string;
105
+ }
106
+
107
+ interface DeleteAppDataContext<T> {
108
+ previousValue: T | null | undefined;
109
+ previousNamespace: Record<string, T> | undefined;
110
+ }
111
+
112
+ /**
113
+ * Delete a per-user JSON value. Optimistically removes the entry from the
114
+ * single-value cache and from the surrounding namespace map, then rolls back
115
+ * on error.
116
+ */
117
+ export const useDeleteAppData = <T = unknown>() => {
118
+ const { oxyServices, activeSessionId } = useWebOxy();
119
+ const queryClient = useQueryClient();
120
+
121
+ return useMutation<void, Error, DeleteAppDataVariables, DeleteAppDataContext<T>>({
122
+ mutationKey: ['appData', 'delete'],
123
+ mutationFn: async ({ namespace, key }) => {
124
+ await authenticatedApiCall(oxyServices, activeSessionId, () =>
125
+ oxyServices.deleteAppData(namespace, key),
126
+ );
127
+ },
128
+ onMutate: async ({ namespace, key }) => {
129
+ const valueKey = appDataQueryKeys.value(namespace, key);
130
+ const namespaceKey = appDataQueryKeys.namespace(namespace);
131
+
132
+ await Promise.all([
133
+ queryClient.cancelQueries({ queryKey: valueKey }),
134
+ queryClient.cancelQueries({ queryKey: namespaceKey }),
135
+ ]);
136
+
137
+ const previousValue = queryClient.getQueryData<T | null>(valueKey);
138
+ const previousNamespace = queryClient.getQueryData<Record<string, T>>(namespaceKey);
139
+
140
+ queryClient.setQueryData<T | null>(valueKey, null);
141
+ if (previousNamespace && key in previousNamespace) {
142
+ const next: Record<string, T> = { ...previousNamespace };
143
+ delete next[key];
144
+ queryClient.setQueryData(namespaceKey, next);
145
+ }
146
+
147
+ return { previousValue, previousNamespace };
148
+ },
149
+ onError: (_error, { namespace, key }, context) => {
150
+ if (!context) return;
151
+ const valueKey = appDataQueryKeys.value(namespace, key);
152
+ const namespaceKey = appDataQueryKeys.namespace(namespace);
153
+
154
+ queryClient.setQueryData(valueKey, context.previousValue ?? null);
155
+ if (context.previousNamespace !== undefined) {
156
+ queryClient.setQueryData(namespaceKey, context.previousNamespace);
157
+ }
158
+ },
159
+ onSuccess: (_data, { namespace, key }) => {
160
+ queryClient.setQueryData(appDataQueryKeys.value(namespace, key), null);
161
+ // Confirm the value is gone from the namespace cache too. If the
162
+ // optimistic update wasn't applied (e.g. cache was empty), this is a
163
+ // no-op; if it was, we already removed it in onMutate, so this is also
164
+ // a no-op. The work happens in onMutate — onSuccess is the commit point.
165
+ },
166
+ });
167
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Query keys + error utilities for `useAppData` hooks.
3
+ *
4
+ * Lives next to the hook file so consumers can import the keys directly
5
+ * when they need to imperatively invalidate a value (e.g. after a non-React
6
+ * write through `oxyServices.setAppData`).
7
+ */
8
+
9
+ export const appDataQueryKeys = {
10
+ all: ['appData'] as const,
11
+ namespaces: () => [...appDataQueryKeys.all, 'namespace'] as const,
12
+ namespace: (namespace: string) =>
13
+ [...appDataQueryKeys.namespaces(), namespace] as const,
14
+ values: () => [...appDataQueryKeys.all, 'value'] as const,
15
+ value: (namespace: string, key: string) =>
16
+ [...appDataQueryKeys.values(), namespace, key] as const,
17
+ } as const;
18
+
19
+ /**
20
+ * True when `error` indicates the app-data endpoint isn't reachable — either
21
+ * because the API deployment doesn't have it yet (404) or there's a network
22
+ * failure with no response. We treat these as "no value stored" so consumers
23
+ * fall back to local persistence without surfacing a user-facing error.
24
+ *
25
+ * Anything else (401, 403, 500) propagates normally — those are real bugs
26
+ * the auth or retry pipeline needs to see.
27
+ */
28
+ export function isMissingAppDataEndpointError(error: unknown): boolean {
29
+ if (!error || typeof error !== 'object') {
30
+ return false;
31
+ }
32
+ const candidate = error as {
33
+ status?: number;
34
+ statusCode?: number;
35
+ response?: { status?: number };
36
+ code?: string;
37
+ message?: string;
38
+ };
39
+ const status =
40
+ candidate.status ?? candidate.statusCode ?? candidate.response?.status;
41
+
42
+ // 404: endpoint not deployed on this API instance yet.
43
+ if (status === 404) return true;
44
+
45
+ // Network errors: no response received at all. Common during local dev
46
+ // when the API server is down, or when offline.
47
+ if (candidate.code === 'NETWORK_ERROR') return true;
48
+ const message = typeof candidate.message === 'string' ? candidate.message : '';
49
+ if (message.includes('Network Error') || message.includes('Failed to fetch')) {
50
+ return true;
51
+ }
52
+ return false;
53
+ }
@@ -34,3 +34,7 @@ export {
34
34
  // Query keys and invalidation helpers (for advanced usage)
35
35
  export { queryKeys, invalidateAccountQueries, invalidateUserQueries, invalidateSessionQueries } from './queryKeys';
36
36
 
37
+ // App-data KV store query hooks
38
+ export { useAppData, useAppDataNamespace } from './useAppData';
39
+ export { appDataQueryKeys, isMissingAppDataEndpointError } from './appDataQueryKeys';
40
+
@@ -0,0 +1,105 @@
1
+ /**
2
+ * App-Data Query Hooks
3
+ *
4
+ * Read side of the `/users/me/app-data/...` per-user JSON KV store. Gated on
5
+ * `isAuthenticated` — when signed out the query stays `enabled: false` and
6
+ * `data` is `null`, so consumers can fall back to localStorage without ever
7
+ * issuing a doomed request.
8
+ *
9
+ * Errors from the network (404 because the endpoint isn't deployed yet,
10
+ * 401 because the session lapsed, etc.) are not user-facing here. Hooks
11
+ * return `data: null` on error so the calling component renders the
12
+ * "nothing yet" state and the consuming app can quietly fall back to local
13
+ * persistence. Mutations still propagate errors so write attempts surface
14
+ * a toast — only reads are silent.
15
+ */
16
+
17
+ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
18
+ import { authenticatedApiCall } from '@oxyhq/core';
19
+ import { useWebOxy } from '../../WebOxyProvider';
20
+ import { appDataQueryKeys, isMissingAppDataEndpointError } from './appDataQueryKeys';
21
+
22
+ interface AppDataQueryOptions {
23
+ /** Disable the query without unmounting the component. */
24
+ enabled?: boolean;
25
+ /** Override the default 1-minute stale time. */
26
+ staleTime?: number;
27
+ /** Override the default 30-minute gc time. */
28
+ gcTime?: number;
29
+ }
30
+
31
+ /**
32
+ * Read a single per-user JSON value.
33
+ *
34
+ * @param namespace - kebab/snake-case identifier (e.g. `"academy"`).
35
+ * @param key - kebab/snake-case identifier (e.g. course slug).
36
+ * @param options - optional `enabled`/`staleTime`/`gcTime` overrides.
37
+ *
38
+ * @returns A `useQuery` result with `data` of type `T | null`. The query
39
+ * stays disabled when the user is signed out; when enabled but the server
40
+ * has no stored value, `data` is `null`. Reads that fail because the
41
+ * endpoint isn't reachable also resolve to `null` so the consumer can
42
+ * fall back to local persistence.
43
+ */
44
+ export const useAppData = <T = unknown>(
45
+ namespace: string,
46
+ key: string,
47
+ options?: AppDataQueryOptions,
48
+ ): UseQueryResult<T | null, Error> => {
49
+ const { oxyServices, activeSessionId, isAuthenticated } = useWebOxy();
50
+
51
+ return useQuery<T | null, Error>({
52
+ queryKey: appDataQueryKeys.value(namespace, key),
53
+ queryFn: async () => {
54
+ try {
55
+ return await authenticatedApiCall(oxyServices, activeSessionId, () =>
56
+ oxyServices.getAppData<T>(namespace, key),
57
+ );
58
+ } catch (error) {
59
+ // Endpoint not deployed yet, no network, etc. — return null so the
60
+ // consumer falls back to localStorage rather than rendering a broken
61
+ // UI state. Authentication errors still bubble up so the auth retry
62
+ // pipeline can surface them at the provider level.
63
+ if (isMissingAppDataEndpointError(error)) {
64
+ return null;
65
+ }
66
+ throw error;
67
+ }
68
+ },
69
+ enabled: (options?.enabled !== false) && isAuthenticated,
70
+ staleTime: options?.staleTime ?? 60 * 1000,
71
+ gcTime: options?.gcTime ?? 30 * 60 * 1000,
72
+ });
73
+ };
74
+
75
+ /**
76
+ * Read every value in a namespace.
77
+ *
78
+ * @returns A `useQuery` result with `data` as a `Record<string, T>`. Empty
79
+ * object when the namespace contains nothing (or when fetching failed).
80
+ */
81
+ export const useAppDataNamespace = <T = unknown>(
82
+ namespace: string,
83
+ options?: AppDataQueryOptions,
84
+ ): UseQueryResult<Record<string, T>, Error> => {
85
+ const { oxyServices, activeSessionId, isAuthenticated } = useWebOxy();
86
+
87
+ return useQuery<Record<string, T>, Error>({
88
+ queryKey: appDataQueryKeys.namespace(namespace),
89
+ queryFn: async () => {
90
+ try {
91
+ return await authenticatedApiCall(oxyServices, activeSessionId, () =>
92
+ oxyServices.listAppData<T>(namespace),
93
+ );
94
+ } catch (error) {
95
+ if (isMissingAppDataEndpointError(error)) {
96
+ return {};
97
+ }
98
+ throw error;
99
+ }
100
+ },
101
+ enabled: (options?.enabled !== false) && isAuthenticated,
102
+ staleTime: options?.staleTime ?? 60 * 1000,
103
+ gcTime: options?.gcTime ?? 30 * 60 * 1000,
104
+ });
105
+ };
package/src/index.ts CHANGED
@@ -74,6 +74,10 @@ export {
74
74
  useSecurityInfo,
75
75
  useSecurityActivity,
76
76
  useRecentSecurityActivity,
77
+ useAppData,
78
+ useAppDataNamespace,
79
+ appDataQueryKeys,
80
+ isMissingAppDataEndpointError,
77
81
  } from './hooks/queries';
78
82
 
79
83
  // --- Mutation Hooks ---
@@ -88,6 +92,8 @@ export {
88
92
  useLogoutAll,
89
93
  useUpdateDeviceName,
90
94
  useRemoveDevice,
95
+ useSetAppData,
96
+ useDeleteAppData,
91
97
  } from './hooks/mutations';
92
98
 
93
99
  export {