@oxyhq/auth 1.0.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 (119) hide show
  1. package/README.md +56 -0
  2. package/dist/cjs/WebOxyProvider.js +287 -0
  3. package/dist/cjs/hooks/mutations/index.js +23 -0
  4. package/dist/cjs/hooks/mutations/mutationFactory.js +126 -0
  5. package/dist/cjs/hooks/mutations/useAccountMutations.js +275 -0
  6. package/dist/cjs/hooks/mutations/useServicesMutations.js +149 -0
  7. package/dist/cjs/hooks/queries/index.js +35 -0
  8. package/dist/cjs/hooks/queries/queryKeys.js +82 -0
  9. package/dist/cjs/hooks/queries/useAccountQueries.js +141 -0
  10. package/dist/cjs/hooks/queries/useSecurityQueries.js +45 -0
  11. package/dist/cjs/hooks/queries/useServicesQueries.js +113 -0
  12. package/dist/cjs/hooks/queryClient.js +110 -0
  13. package/dist/cjs/hooks/useAssets.js +225 -0
  14. package/dist/cjs/hooks/useFileDownloadUrl.js +91 -0
  15. package/dist/cjs/hooks/useFileFiltering.js +81 -0
  16. package/dist/cjs/hooks/useFollow.js +159 -0
  17. package/dist/cjs/hooks/useFollow.types.js +4 -0
  18. package/dist/cjs/hooks/useQueryClient.js +16 -0
  19. package/dist/cjs/hooks/useSessionSocket.js +215 -0
  20. package/dist/cjs/hooks/useWebSSO.js +146 -0
  21. package/dist/cjs/index.js +115 -0
  22. package/dist/cjs/stores/accountStore.js +226 -0
  23. package/dist/cjs/stores/assetStore.js +192 -0
  24. package/dist/cjs/stores/authStore.js +47 -0
  25. package/dist/cjs/stores/followStore.js +154 -0
  26. package/dist/cjs/utils/authHelpers.js +154 -0
  27. package/dist/cjs/utils/avatarUtils.js +77 -0
  28. package/dist/cjs/utils/errorHandlers.js +128 -0
  29. package/dist/cjs/utils/sessionHelpers.js +90 -0
  30. package/dist/cjs/utils/storageHelpers.js +147 -0
  31. package/dist/esm/WebOxyProvider.js +282 -0
  32. package/dist/esm/hooks/mutations/index.js +10 -0
  33. package/dist/esm/hooks/mutations/mutationFactory.js +122 -0
  34. package/dist/esm/hooks/mutations/useAccountMutations.js +267 -0
  35. package/dist/esm/hooks/mutations/useServicesMutations.js +141 -0
  36. package/dist/esm/hooks/queries/index.js +14 -0
  37. package/dist/esm/hooks/queries/queryKeys.js +76 -0
  38. package/dist/esm/hooks/queries/useAccountQueries.js +131 -0
  39. package/dist/esm/hooks/queries/useSecurityQueries.js +40 -0
  40. package/dist/esm/hooks/queries/useServicesQueries.js +105 -0
  41. package/dist/esm/hooks/queryClient.js +104 -0
  42. package/dist/esm/hooks/useAssets.js +220 -0
  43. package/dist/esm/hooks/useFileDownloadUrl.js +86 -0
  44. package/dist/esm/hooks/useFileFiltering.js +78 -0
  45. package/dist/esm/hooks/useFollow.js +154 -0
  46. package/dist/esm/hooks/useFollow.types.js +3 -0
  47. package/dist/esm/hooks/useQueryClient.js +12 -0
  48. package/dist/esm/hooks/useSessionSocket.js +209 -0
  49. package/dist/esm/hooks/useWebSSO.js +143 -0
  50. package/dist/esm/index.js +48 -0
  51. package/dist/esm/stores/accountStore.js +219 -0
  52. package/dist/esm/stores/assetStore.js +180 -0
  53. package/dist/esm/stores/authStore.js +44 -0
  54. package/dist/esm/stores/followStore.js +151 -0
  55. package/dist/esm/utils/authHelpers.js +145 -0
  56. package/dist/esm/utils/avatarUtils.js +72 -0
  57. package/dist/esm/utils/errorHandlers.js +121 -0
  58. package/dist/esm/utils/sessionHelpers.js +84 -0
  59. package/dist/esm/utils/storageHelpers.js +108 -0
  60. package/dist/types/WebOxyProvider.d.ts +97 -0
  61. package/dist/types/hooks/mutations/index.d.ts +8 -0
  62. package/dist/types/hooks/mutations/mutationFactory.d.ts +75 -0
  63. package/dist/types/hooks/mutations/useAccountMutations.d.ts +68 -0
  64. package/dist/types/hooks/mutations/useServicesMutations.d.ts +22 -0
  65. package/dist/types/hooks/queries/index.d.ts +10 -0
  66. package/dist/types/hooks/queries/queryKeys.d.ts +64 -0
  67. package/dist/types/hooks/queries/useAccountQueries.d.ts +42 -0
  68. package/dist/types/hooks/queries/useSecurityQueries.d.ts +14 -0
  69. package/dist/types/hooks/queries/useServicesQueries.d.ts +31 -0
  70. package/dist/types/hooks/queryClient.d.ts +18 -0
  71. package/dist/types/hooks/useAssets.d.ts +34 -0
  72. package/dist/types/hooks/useFileDownloadUrl.d.ts +18 -0
  73. package/dist/types/hooks/useFileFiltering.d.ts +28 -0
  74. package/dist/types/hooks/useFollow.d.ts +61 -0
  75. package/dist/types/hooks/useFollow.types.d.ts +32 -0
  76. package/dist/types/hooks/useQueryClient.d.ts +6 -0
  77. package/dist/types/hooks/useSessionSocket.d.ts +13 -0
  78. package/dist/types/hooks/useWebSSO.d.ts +57 -0
  79. package/dist/types/index.d.ts +46 -0
  80. package/dist/types/stores/accountStore.d.ts +33 -0
  81. package/dist/types/stores/assetStore.d.ts +53 -0
  82. package/dist/types/stores/authStore.d.ts +16 -0
  83. package/dist/types/stores/followStore.d.ts +24 -0
  84. package/dist/types/utils/authHelpers.d.ts +98 -0
  85. package/dist/types/utils/avatarUtils.d.ts +33 -0
  86. package/dist/types/utils/errorHandlers.d.ts +34 -0
  87. package/dist/types/utils/sessionHelpers.d.ts +63 -0
  88. package/dist/types/utils/storageHelpers.d.ts +27 -0
  89. package/package.json +71 -0
  90. package/src/WebOxyProvider.tsx +372 -0
  91. package/src/global.d.ts +1 -0
  92. package/src/hooks/mutations/index.ts +25 -0
  93. package/src/hooks/mutations/mutationFactory.ts +215 -0
  94. package/src/hooks/mutations/useAccountMutations.ts +344 -0
  95. package/src/hooks/mutations/useServicesMutations.ts +164 -0
  96. package/src/hooks/queries/index.ts +36 -0
  97. package/src/hooks/queries/queryKeys.ts +88 -0
  98. package/src/hooks/queries/useAccountQueries.ts +152 -0
  99. package/src/hooks/queries/useSecurityQueries.ts +64 -0
  100. package/src/hooks/queries/useServicesQueries.ts +126 -0
  101. package/src/hooks/queryClient.ts +112 -0
  102. package/src/hooks/useAssets.ts +291 -0
  103. package/src/hooks/useFileDownloadUrl.ts +118 -0
  104. package/src/hooks/useFileFiltering.ts +115 -0
  105. package/src/hooks/useFollow.ts +175 -0
  106. package/src/hooks/useFollow.types.ts +33 -0
  107. package/src/hooks/useQueryClient.ts +17 -0
  108. package/src/hooks/useSessionSocket.ts +233 -0
  109. package/src/hooks/useWebSSO.ts +187 -0
  110. package/src/index.ts +144 -0
  111. package/src/stores/accountStore.ts +296 -0
  112. package/src/stores/assetStore.ts +281 -0
  113. package/src/stores/authStore.ts +63 -0
  114. package/src/stores/followStore.ts +181 -0
  115. package/src/utils/authHelpers.ts +183 -0
  116. package/src/utils/avatarUtils.ts +103 -0
  117. package/src/utils/errorHandlers.ts +194 -0
  118. package/src/utils/sessionHelpers.ts +151 -0
  119. package/src/utils/storageHelpers.ts +130 -0
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePrivacySettings = exports.useUsersBySessions = exports.useUserByUsername = exports.useUserById = exports.useCurrentUser = exports.useUserProfiles = exports.useUserProfile = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const queryKeys_1 = require("./queryKeys");
6
+ const WebOxyProvider_1 = require("../../WebOxyProvider");
7
+ const authHelpers_1 = require("../../utils/authHelpers");
8
+ /**
9
+ * Get user profile by session ID
10
+ */
11
+ const useUserProfile = (sessionId, options) => {
12
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
13
+ return (0, react_query_1.useQuery)({
14
+ queryKey: queryKeys_1.queryKeys.users.profile(sessionId || ''),
15
+ queryFn: async () => {
16
+ if (!sessionId) {
17
+ throw new Error('Session ID is required');
18
+ }
19
+ return await oxyServices.getUserBySession(sessionId);
20
+ },
21
+ enabled: (options?.enabled !== false) && !!sessionId,
22
+ staleTime: 5 * 60 * 1000, // 5 minutes
23
+ gcTime: 30 * 60 * 1000, // 30 minutes
24
+ });
25
+ };
26
+ exports.useUserProfile = useUserProfile;
27
+ /**
28
+ * Get multiple user profiles by session IDs (batch query)
29
+ */
30
+ const useUserProfiles = (sessionIds, options) => {
31
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
32
+ return (0, react_query_1.useQueries)({
33
+ queries: sessionIds.map((sessionId) => ({
34
+ queryKey: queryKeys_1.queryKeys.users.profile(sessionId),
35
+ queryFn: async () => {
36
+ const results = await oxyServices.getUsersBySessions([sessionId]);
37
+ return results[0]?.user || null;
38
+ },
39
+ enabled: (options?.enabled !== false) && !!sessionId,
40
+ staleTime: 5 * 60 * 1000,
41
+ gcTime: 30 * 60 * 1000,
42
+ })),
43
+ });
44
+ };
45
+ exports.useUserProfiles = useUserProfiles;
46
+ /**
47
+ * Get current authenticated user
48
+ */
49
+ const useCurrentUser = (options) => {
50
+ const { oxyServices, activeSessionId, isAuthenticated } = (0, WebOxyProvider_1.useWebOxy)();
51
+ return (0, react_query_1.useQuery)({
52
+ queryKey: queryKeys_1.queryKeys.accounts.current(),
53
+ queryFn: async () => {
54
+ if (!activeSessionId) {
55
+ throw new Error('No active session');
56
+ }
57
+ return await oxyServices.getUserBySession(activeSessionId);
58
+ },
59
+ enabled: (options?.enabled !== false) && isAuthenticated && !!activeSessionId,
60
+ staleTime: 1 * 60 * 1000, // 1 minute for current user
61
+ gcTime: 30 * 60 * 1000,
62
+ });
63
+ };
64
+ exports.useCurrentUser = useCurrentUser;
65
+ /**
66
+ * Get user by ID
67
+ */
68
+ const useUserById = (userId, options) => {
69
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
70
+ return (0, react_query_1.useQuery)({
71
+ queryKey: queryKeys_1.queryKeys.users.detail(userId || ''),
72
+ queryFn: async () => {
73
+ if (!userId) {
74
+ throw new Error('User ID is required');
75
+ }
76
+ return await oxyServices.getUserById(userId);
77
+ },
78
+ enabled: (options?.enabled !== false) && !!userId,
79
+ staleTime: 5 * 60 * 1000,
80
+ gcTime: 30 * 60 * 1000,
81
+ });
82
+ };
83
+ exports.useUserById = useUserById;
84
+ /**
85
+ * Get user profile by username
86
+ */
87
+ const useUserByUsername = (username, options) => {
88
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
89
+ return (0, react_query_1.useQuery)({
90
+ queryKey: [...queryKeys_1.queryKeys.users.details(), 'username', username || ''],
91
+ queryFn: async () => {
92
+ if (!username) {
93
+ throw new Error('Username is required');
94
+ }
95
+ return await oxyServices.getProfileByUsername(username);
96
+ },
97
+ enabled: (options?.enabled !== false) && !!username,
98
+ staleTime: 5 * 60 * 1000,
99
+ gcTime: 30 * 60 * 1000,
100
+ });
101
+ };
102
+ exports.useUserByUsername = useUserByUsername;
103
+ /**
104
+ * Batch get users by session IDs (optimized single API call)
105
+ */
106
+ const useUsersBySessions = (sessionIds, options) => {
107
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
108
+ return (0, react_query_1.useQuery)({
109
+ queryKey: queryKeys_1.queryKeys.accounts.list(sessionIds),
110
+ queryFn: async () => {
111
+ if (sessionIds.length === 0) {
112
+ return [];
113
+ }
114
+ return await oxyServices.getUsersBySessions(sessionIds);
115
+ },
116
+ enabled: (options?.enabled !== false) && sessionIds.length > 0,
117
+ staleTime: 5 * 60 * 1000,
118
+ gcTime: 30 * 60 * 1000,
119
+ });
120
+ };
121
+ exports.useUsersBySessions = useUsersBySessions;
122
+ /**
123
+ * Get privacy settings for a user
124
+ */
125
+ const usePrivacySettings = (userId, options) => {
126
+ const { oxyServices, activeSessionId, user } = (0, WebOxyProvider_1.useWebOxy)();
127
+ const targetUserId = userId || user?.id;
128
+ return (0, react_query_1.useQuery)({
129
+ queryKey: queryKeys_1.queryKeys.privacy.settings(targetUserId),
130
+ queryFn: async () => {
131
+ if (!targetUserId) {
132
+ throw new Error('User ID is required');
133
+ }
134
+ return (0, authHelpers_1.authenticatedApiCall)(oxyServices, activeSessionId, () => oxyServices.getPrivacySettings(targetUserId));
135
+ },
136
+ enabled: (options?.enabled !== false) && !!targetUserId,
137
+ staleTime: 2 * 60 * 1000, // 2 minutes
138
+ gcTime: 10 * 60 * 1000, // 10 minutes
139
+ });
140
+ };
141
+ exports.usePrivacySettings = usePrivacySettings;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useRecentSecurityActivity = exports.useSecurityActivity = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const queryKeys_1 = require("./queryKeys");
6
+ const WebOxyProvider_1 = require("../../WebOxyProvider");
7
+ /**
8
+ * Get user's security activity with pagination
9
+ */
10
+ const useSecurityActivity = (options) => {
11
+ const { oxyServices, activeSessionId } = (0, WebOxyProvider_1.useWebOxy)();
12
+ return (0, react_query_1.useQuery)({
13
+ queryKey: queryKeys_1.queryKeys.security.activity(options?.limit, options?.offset, options?.eventType),
14
+ queryFn: async () => {
15
+ if (!activeSessionId) {
16
+ throw new Error('No active session');
17
+ }
18
+ const response = await oxyServices.getSecurityActivity(options?.limit, options?.offset, options?.eventType);
19
+ return response;
20
+ },
21
+ enabled: (options?.enabled !== false) && !!activeSessionId,
22
+ staleTime: 5 * 60 * 1000, // 5 minutes
23
+ gcTime: 10 * 60 * 1000, // 10 minutes
24
+ });
25
+ };
26
+ exports.useSecurityActivity = useSecurityActivity;
27
+ /**
28
+ * Get recent security activity (convenience hook)
29
+ */
30
+ const useRecentSecurityActivity = (limit = 10) => {
31
+ const { oxyServices, activeSessionId } = (0, WebOxyProvider_1.useWebOxy)();
32
+ return (0, react_query_1.useQuery)({
33
+ queryKey: queryKeys_1.queryKeys.security.recent(limit),
34
+ queryFn: async () => {
35
+ if (!activeSessionId) {
36
+ throw new Error('No active session');
37
+ }
38
+ return await oxyServices.getRecentSecurityActivity(limit);
39
+ },
40
+ enabled: !!activeSessionId,
41
+ staleTime: 5 * 60 * 1000, // 5 minutes
42
+ gcTime: 10 * 60 * 1000, // 10 minutes
43
+ });
44
+ };
45
+ exports.useRecentSecurityActivity = useRecentSecurityActivity;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSecurityInfo = exports.useUserDevices = exports.useDeviceSessions = exports.useSession = exports.useSessions = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const queryKeys_1 = require("./queryKeys");
6
+ const WebOxyProvider_1 = require("../../WebOxyProvider");
7
+ const sessionHelpers_1 = require("../../utils/sessionHelpers");
8
+ const authHelpers_1 = require("../../utils/authHelpers");
9
+ /**
10
+ * Get all active sessions for the current user
11
+ */
12
+ const useSessions = (userId, options) => {
13
+ const { oxyServices, activeSessionId } = (0, WebOxyProvider_1.useWebOxy)();
14
+ return (0, react_query_1.useQuery)({
15
+ queryKey: queryKeys_1.queryKeys.sessions.list(userId),
16
+ queryFn: async () => {
17
+ if (!activeSessionId) {
18
+ throw new Error('No active session');
19
+ }
20
+ const sessions = await (0, sessionHelpers_1.fetchSessionsWithFallback)(oxyServices, activeSessionId, {
21
+ fallbackDeviceId: undefined,
22
+ fallbackUserId: userId,
23
+ });
24
+ return (0, sessionHelpers_1.mapSessionsToClient)(sessions, activeSessionId);
25
+ },
26
+ enabled: (options?.enabled !== false) && !!activeSessionId,
27
+ staleTime: 2 * 60 * 1000, // 2 minutes (sessions change frequently)
28
+ gcTime: 10 * 60 * 1000, // 10 minutes
29
+ });
30
+ };
31
+ exports.useSessions = useSessions;
32
+ /**
33
+ * Get specific session by ID
34
+ */
35
+ const useSession = (sessionId, options) => {
36
+ const { oxyServices } = (0, WebOxyProvider_1.useWebOxy)();
37
+ return (0, react_query_1.useQuery)({
38
+ queryKey: queryKeys_1.queryKeys.sessions.detail(sessionId || ''),
39
+ queryFn: async () => {
40
+ if (!sessionId) {
41
+ throw new Error('Session ID is required');
42
+ }
43
+ const validation = await oxyServices.validateSession(sessionId, { useHeaderValidation: true });
44
+ if (!validation?.valid || !validation.user) {
45
+ throw new Error('Session not found or invalid');
46
+ }
47
+ const now = new Date();
48
+ return {
49
+ sessionId,
50
+ deviceId: '', // Device ID not available from validation response
51
+ expiresAt: validation.expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
52
+ lastActive: validation.lastActivity || now.toISOString(),
53
+ userId: validation.user.id?.toString() ?? '',
54
+ isCurrent: false,
55
+ };
56
+ },
57
+ enabled: (options?.enabled !== false) && !!sessionId,
58
+ staleTime: 2 * 60 * 1000,
59
+ gcTime: 10 * 60 * 1000,
60
+ });
61
+ };
62
+ exports.useSession = useSession;
63
+ /**
64
+ * Get device sessions for the current active session
65
+ */
66
+ const useDeviceSessions = (options) => {
67
+ const { oxyServices, activeSessionId } = (0, WebOxyProvider_1.useWebOxy)();
68
+ return (0, react_query_1.useQuery)({
69
+ queryKey: queryKeys_1.queryKeys.sessions.active(),
70
+ queryFn: async () => {
71
+ if (!activeSessionId) {
72
+ throw new Error('No active session');
73
+ }
74
+ return await oxyServices.getDeviceSessions(activeSessionId);
75
+ },
76
+ enabled: (options?.enabled !== false) && !!activeSessionId,
77
+ staleTime: 2 * 60 * 1000,
78
+ gcTime: 10 * 60 * 1000,
79
+ });
80
+ };
81
+ exports.useDeviceSessions = useDeviceSessions;
82
+ /**
83
+ * Get user devices
84
+ */
85
+ const useUserDevices = (options) => {
86
+ const { oxyServices, isAuthenticated, activeSessionId } = (0, WebOxyProvider_1.useWebOxy)();
87
+ return (0, react_query_1.useQuery)({
88
+ queryKey: queryKeys_1.queryKeys.devices.list(),
89
+ queryFn: async () => {
90
+ return (0, authHelpers_1.authenticatedApiCall)(oxyServices, activeSessionId, () => oxyServices.getUserDevices());
91
+ },
92
+ enabled: (options?.enabled !== false) && isAuthenticated,
93
+ staleTime: 5 * 60 * 1000,
94
+ gcTime: 30 * 60 * 1000,
95
+ });
96
+ };
97
+ exports.useUserDevices = useUserDevices;
98
+ /**
99
+ * Get security information
100
+ */
101
+ const useSecurityInfo = (options) => {
102
+ const { oxyServices, isAuthenticated } = (0, WebOxyProvider_1.useWebOxy)();
103
+ return (0, react_query_1.useQuery)({
104
+ queryKey: [...queryKeys_1.queryKeys.devices.all, 'security'],
105
+ queryFn: async () => {
106
+ return await oxyServices.getSecurityInfo();
107
+ },
108
+ enabled: (options?.enabled !== false) && isAuthenticated,
109
+ staleTime: 5 * 60 * 1000,
110
+ gcTime: 30 * 60 * 1000,
111
+ });
112
+ };
113
+ exports.useSecurityInfo = useSecurityInfo;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clearQueryCache = exports.createQueryClient = exports.createPersistenceAdapter = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const QUERY_CACHE_KEY = 'oxy_query_cache';
6
+ const QUERY_CACHE_VERSION = '1';
7
+ /**
8
+ * Custom persistence adapter for TanStack Query using our StorageInterface
9
+ */
10
+ const createPersistenceAdapter = (storage) => {
11
+ return {
12
+ persistClient: async (client) => {
13
+ try {
14
+ const serialized = JSON.stringify({
15
+ clientState: client,
16
+ timestamp: Date.now(),
17
+ version: QUERY_CACHE_VERSION,
18
+ });
19
+ await storage.setItem(QUERY_CACHE_KEY, serialized);
20
+ }
21
+ catch (error) {
22
+ if (__DEV__) {
23
+ console.warn('[QueryClient] Failed to persist cache:', error);
24
+ }
25
+ }
26
+ },
27
+ restoreClient: async () => {
28
+ try {
29
+ const cached = await storage.getItem(QUERY_CACHE_KEY);
30
+ if (!cached)
31
+ return undefined;
32
+ const parsed = JSON.parse(cached);
33
+ // Check version compatibility
34
+ if (parsed.version !== QUERY_CACHE_VERSION) {
35
+ // Clear old cache on version mismatch
36
+ await storage.removeItem(QUERY_CACHE_KEY);
37
+ return undefined;
38
+ }
39
+ // Check if cache is too old (30 days)
40
+ const maxAge = 30 * 24 * 60 * 60 * 1000;
41
+ if (parsed.timestamp && Date.now() - parsed.timestamp > maxAge) {
42
+ await storage.removeItem(QUERY_CACHE_KEY);
43
+ return undefined;
44
+ }
45
+ return parsed.clientState;
46
+ }
47
+ catch (error) {
48
+ if (__DEV__) {
49
+ console.warn('[QueryClient] Failed to restore cache:', error);
50
+ }
51
+ return undefined;
52
+ }
53
+ },
54
+ removeClient: async () => {
55
+ try {
56
+ await storage.removeItem(QUERY_CACHE_KEY);
57
+ }
58
+ catch (error) {
59
+ if (__DEV__) {
60
+ console.warn('[QueryClient] Failed to remove cache:', error);
61
+ }
62
+ }
63
+ },
64
+ };
65
+ };
66
+ exports.createPersistenceAdapter = createPersistenceAdapter;
67
+ /**
68
+ * Create a QueryClient with offline-first configuration
69
+ */
70
+ const createQueryClient = (storage) => {
71
+ const client = new react_query_1.QueryClient({
72
+ defaultOptions: {
73
+ queries: {
74
+ // Data is fresh for 5 minutes
75
+ staleTime: 5 * 60 * 1000,
76
+ // Keep unused data in cache for 30 minutes
77
+ gcTime: 30 * 60 * 1000,
78
+ // Retry 3 times with exponential backoff
79
+ retry: 3,
80
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
81
+ // Refetch on reconnect
82
+ refetchOnReconnect: true,
83
+ // Don't refetch on window focus (better for mobile)
84
+ refetchOnWindowFocus: false,
85
+ // Offline-first: use cache when offline
86
+ networkMode: 'offlineFirst',
87
+ },
88
+ mutations: {
89
+ // Retry once for mutations
90
+ retry: 1,
91
+ // Offline-first: queue mutations when offline
92
+ networkMode: 'offlineFirst',
93
+ },
94
+ },
95
+ });
96
+ // Note: Persistence is handled by TanStack Query's built-in persistence
97
+ // For now, we rely on the query client's default behavior with networkMode: 'offlineFirst'
98
+ // The cache will be available in memory and queries will use cached data when offline
99
+ // Full persistence to AsyncStorage can be added later with @tanstack/react-query-persist-client if needed
100
+ return client;
101
+ };
102
+ exports.createQueryClient = createQueryClient;
103
+ /**
104
+ * Clear persisted query cache
105
+ */
106
+ const clearQueryCache = async (storage) => {
107
+ const adapter = (0, exports.createPersistenceAdapter)(storage);
108
+ await adapter.removeClient();
109
+ };
110
+ exports.clearQueryCache = clearQueryCache;
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAssets = exports.setOxyAssetInstance = void 0;
4
+ const react_1 = require("react");
5
+ const assetStore_1 = require("../stores/assetStore");
6
+ // Create a singleton instance for the hook
7
+ let oxyInstance = null;
8
+ const setOxyAssetInstance = (instance) => {
9
+ oxyInstance = instance;
10
+ };
11
+ exports.setOxyAssetInstance = setOxyAssetInstance;
12
+ /**
13
+ * Hook for managing assets with Zustand store integration
14
+ */
15
+ const useAssets = () => {
16
+ const { assets, uploadProgress, loading, errors, setAsset, setAssets, removeAsset, setUploadProgress, removeUploadProgress, addLink, removeLink, setUploading, setLinking, setDeleting, setUploadError, setLinkError, setDeleteError, clearErrors, getAssetsByApp, getAssetsByEntity, getAssetUsageCount, isAssetLinked, reset } = (0, assetStore_1.useAssetStore)();
17
+ // Upload asset with progress tracking
18
+ const upload = (0, react_1.useCallback)(async (file, metadata) => {
19
+ if (!oxyInstance) {
20
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
21
+ }
22
+ try {
23
+ clearErrors();
24
+ setUploading(true);
25
+ // Upload file (progress tracking simplified for now)
26
+ const result = await oxyInstance.assetUpload(file, undefined, metadata);
27
+ // Update progress with final status
28
+ if (result?.file) {
29
+ const fileId = result.file.id;
30
+ setUploadProgress(fileId, {
31
+ fileId,
32
+ uploaded: file.size,
33
+ total: file.size,
34
+ percentage: 100,
35
+ status: 'complete'
36
+ });
37
+ // Remove progress after a short delay
38
+ setTimeout(() => {
39
+ removeUploadProgress(fileId);
40
+ }, 2000);
41
+ }
42
+ // Add asset to store
43
+ if (result.file) {
44
+ setAsset(result.file);
45
+ return result.file;
46
+ }
47
+ return null;
48
+ }
49
+ catch (error) {
50
+ setUploadError(error.message || 'Upload failed');
51
+ throw error;
52
+ }
53
+ finally {
54
+ setUploading(false);
55
+ }
56
+ }, [
57
+ clearErrors,
58
+ setUploading,
59
+ setUploadProgress,
60
+ removeUploadProgress,
61
+ setAsset,
62
+ setUploadError
63
+ ]);
64
+ // Link asset to entity
65
+ const link = (0, react_1.useCallback)(async (assetId, app, entityType, entityId) => {
66
+ if (!oxyInstance) {
67
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
68
+ }
69
+ try {
70
+ clearErrors();
71
+ setLinking(true);
72
+ // Auto-detect visibility for avatars and profile banners
73
+ const visibility = (entityType === 'avatar' || entityType === 'profile-banner')
74
+ ? 'public'
75
+ : undefined;
76
+ const result = await oxyInstance.assetLink(assetId, app, entityType, entityId, visibility);
77
+ if (result.file) {
78
+ setAsset(result.file);
79
+ }
80
+ else {
81
+ // If API doesn't return full file, update store optimistically
82
+ addLink(assetId, {
83
+ app,
84
+ entityType,
85
+ entityId,
86
+ createdBy: '', // Will be filled by server
87
+ createdAt: new Date().toISOString()
88
+ });
89
+ }
90
+ }
91
+ catch (error) {
92
+ setLinkError(error.message || 'Link failed');
93
+ throw error;
94
+ }
95
+ finally {
96
+ setLinking(false);
97
+ }
98
+ }, [clearErrors, setLinking, setAsset, addLink, setLinkError]);
99
+ // Unlink asset from entity
100
+ const unlink = (0, react_1.useCallback)(async (assetId, app, entityType, entityId) => {
101
+ if (!oxyInstance) {
102
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
103
+ }
104
+ try {
105
+ clearErrors();
106
+ setLinking(true);
107
+ const result = await oxyInstance.assetUnlink(assetId, app, entityType, entityId);
108
+ if (result.file) {
109
+ setAsset(result.file);
110
+ }
111
+ else {
112
+ // Update store optimistically
113
+ removeLink(assetId, app, entityType, entityId);
114
+ }
115
+ }
116
+ catch (error) {
117
+ setLinkError(error.message || 'Unlink failed');
118
+ throw error;
119
+ }
120
+ finally {
121
+ setLinking(false);
122
+ }
123
+ }, [clearErrors, setLinking, setAsset, removeLink, setLinkError]);
124
+ // Get asset URL
125
+ const getUrl = (0, react_1.useCallback)(async (assetId, variant, expiresIn) => {
126
+ if (!oxyInstance) {
127
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
128
+ }
129
+ try {
130
+ const result = await oxyInstance.assetGetUrl(assetId, variant, expiresIn);
131
+ return result.url;
132
+ }
133
+ catch (error) {
134
+ throw error;
135
+ }
136
+ }, []);
137
+ // Get asset metadata
138
+ const getAsset = (0, react_1.useCallback)(async (assetId) => {
139
+ if (!oxyInstance) {
140
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
141
+ }
142
+ try {
143
+ const result = await oxyInstance.assetGet(assetId);
144
+ if (result.file) {
145
+ setAsset(result.file);
146
+ return result.file;
147
+ }
148
+ throw new Error('Asset not found');
149
+ }
150
+ catch (error) {
151
+ throw error;
152
+ }
153
+ }, [setAsset]);
154
+ // Delete asset
155
+ const deleteAsset = (0, react_1.useCallback)(async (assetId, force = false) => {
156
+ if (!oxyInstance) {
157
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
158
+ }
159
+ try {
160
+ clearErrors();
161
+ setDeleting(true);
162
+ await oxyInstance.assetDelete(assetId, force);
163
+ removeAsset(assetId);
164
+ }
165
+ catch (error) {
166
+ setDeleteError(error.message || 'Delete failed');
167
+ throw error;
168
+ }
169
+ finally {
170
+ setDeleting(false);
171
+ }
172
+ }, [clearErrors, setDeleting, removeAsset, setDeleteError]);
173
+ // Restore asset from trash
174
+ const restore = (0, react_1.useCallback)(async (assetId) => {
175
+ if (!oxyInstance) {
176
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
177
+ }
178
+ try {
179
+ const result = await oxyInstance.assetRestore(assetId);
180
+ if (result.file) {
181
+ setAsset(result.file);
182
+ }
183
+ }
184
+ catch (error) {
185
+ throw error;
186
+ }
187
+ }, [setAsset]);
188
+ // Get variants
189
+ const getVariants = (0, react_1.useCallback)(async (assetId) => {
190
+ if (!oxyInstance) {
191
+ throw new Error('OxyServices instance not configured. Call setOxyAssetInstance first.');
192
+ }
193
+ try {
194
+ return await oxyInstance.assetGetVariants(assetId);
195
+ }
196
+ catch (error) {
197
+ throw error;
198
+ }
199
+ }, []);
200
+ return {
201
+ // State
202
+ assets: Object.values(assets),
203
+ uploadProgress,
204
+ loading,
205
+ errors,
206
+ // Actions
207
+ upload,
208
+ link,
209
+ unlink,
210
+ getUrl,
211
+ getAsset,
212
+ deleteAsset,
213
+ restore,
214
+ getVariants,
215
+ // Utility methods
216
+ getAssetsByApp,
217
+ getAssetsByEntity,
218
+ getAssetUsageCount,
219
+ isAssetLinked,
220
+ // Store management
221
+ clearErrors,
222
+ reset
223
+ };
224
+ };
225
+ exports.useAssets = useAssets;