@explorins/pers-sdk-react-native 2.3.6 → 2.3.8

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1266 -1244
  3. package/dist/__tests__/PersSDKProvider.test.js +33 -0
  4. package/dist/hooks/useBusiness.d.ts +2 -2
  5. package/dist/hooks/useBusiness.d.ts.map +1 -1
  6. package/dist/hooks/useBusiness.js +2 -2
  7. package/dist/hooks/useIPFSUrl.d.ts +3 -3
  8. package/dist/hooks/useIPFSUrl.d.ts.map +1 -1
  9. package/dist/hooks/useIPFSUrl.js +7 -7
  10. package/dist/hooks/useTransactions.d.ts.map +1 -1
  11. package/dist/hooks/useTransactions.js +13 -5
  12. package/dist/hooks/useWeb3.js +4 -4
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2412 -492
  16. package/dist/index.js.map +1 -1
  17. package/dist/providers/PersSDKProvider.d.ts +4 -0
  18. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  19. package/dist/providers/PersSDKProvider.js +14 -0
  20. package/dist/providers/react-native-http-client.d.ts +2 -0
  21. package/dist/providers/react-native-http-client.d.ts.map +1 -1
  22. package/dist/providers/react-native-http-client.js +5 -2
  23. package/dist/storage/rn-secure-storage.d.ts.map +1 -1
  24. package/dist/storage/rn-secure-storage.js +21 -8
  25. package/package.json +3 -3
  26. package/src/__tests__/PersSDKProvider.test.tsx +484 -420
  27. package/src/__tests__/setup.ts +40 -40
  28. package/src/hooks/index.ts +60 -60
  29. package/src/hooks/useAnalytics.ts +327 -327
  30. package/src/hooks/useAuth.ts +265 -265
  31. package/src/hooks/useBookings.ts +191 -191
  32. package/src/hooks/useBusiness.ts +251 -251
  33. package/src/hooks/useCampaigns.ts +500 -500
  34. package/src/hooks/useDonations.ts +66 -66
  35. package/src/hooks/useEvents.ts +372 -372
  36. package/src/hooks/useFiles.ts +155 -155
  37. package/src/hooks/useIPFSUrl.ts +192 -192
  38. package/src/hooks/usePurchases.ts +65 -65
  39. package/src/hooks/useRedemptions.ts +467 -467
  40. package/src/hooks/useTenants.ts +72 -72
  41. package/src/hooks/useTokenBalances.ts +385 -385
  42. package/src/hooks/useTokens.ts +313 -313
  43. package/src/hooks/useTransactionSigner.ts +611 -611
  44. package/src/hooks/useTransactions.ts +299 -290
  45. package/src/hooks/useTriggerSources.ts +301 -301
  46. package/src/hooks/useUserStatus.ts +61 -61
  47. package/src/hooks/useUsers.ts +167 -167
  48. package/src/hooks/useWeb3.ts +355 -355
  49. package/src/index.ts +777 -774
  50. package/src/polyfills/index.ts +160 -160
  51. package/src/polyfills/web3-polyfills.ts +97 -97
  52. package/src/providers/PersSDKProvider.tsx +393 -374
  53. package/src/providers/react-native-auth-provider.ts +110 -110
  54. package/src/providers/react-native-http-client.ts +150 -145
  55. package/src/providers/rn-dpop-provider.ts +190 -190
  56. package/src/storage/async-storage-token-storage.ts +185 -185
  57. package/src/storage/rn-secure-storage.ts +182 -170
  58. package/src/types/react-native-globals.d.ts +45 -45
  59. package/src/utils/index.ts +8 -8
  60. package/src/utils/platform-detection.ts +86 -86
  61. package/dist/index-CzLxqePZ.js +0 -7717
  62. package/dist/index-CzLxqePZ.js.map +0 -1
@@ -1,372 +1,372 @@
1
- import { useCallback, useEffect, useRef } from 'react';
2
- import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type {
4
- PersEvent,
5
- EventHandler,
6
- EventFilter,
7
- Unsubscribe
8
- } from '@explorins/pers-sdk/core';
9
-
10
- // Re-export event types for convenience
11
- export type { PersEvent, EventHandler, EventFilter, Unsubscribe };
12
-
13
- /**
14
- * Return interface for the useEvents hook
15
- *
16
- * Provides event subscription capabilities for the PERS SDK event system.
17
- * Use this to display notifications, update UI, or trigger side effects.
18
- *
19
- * @interface EventsHook
20
- * @property {Function} subscribe - Subscribe to SDK events with optional filtering
21
- * @property {Function} once - Subscribe to a single event (auto-unsubscribes)
22
- * @property {Function} clear - Clear all subscriptions
23
- * @property {boolean} isAvailable - Whether the event system is available
24
- * @property {number} subscriberCount - Current number of active subscribers
25
- */
26
- export interface EventsHook {
27
- subscribe: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
28
- once: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
29
- clear: () => void;
30
- isAvailable: boolean;
31
- subscriberCount: number;
32
- }
33
-
34
- /**
35
- * React Native hook for PERS SDK event system
36
- *
37
- * This hook provides access to the platform-agnostic event system for subscribing to
38
- * transaction, authentication, campaign, blockchain, and system events. All events
39
- * include a `userMessage` field ready for display to end users.
40
- *
41
- * **Event Domains:**
42
- * - `auth` - Authentication events (login, logout, token refresh)
43
- * - `user` - User profile events (update, create)
44
- * - `transaction` - Transaction events (created, submitted, confirmed)
45
- * - `campaign` - Campaign events (claimed, activated)
46
- * - `redemption` - Redemption events (redeemed, expired)
47
- * - `business` - Business events (created, updated, membership)
48
- * - `wallet` - Real-time blockchain events (Transfer, Approval, etc.) - Auto-enabled on auth
49
- * - `api` - API error events (network, validation, server errors)
50
- *
51
- * **Blockchain Events (Wallet Domain):**
52
- * When `sdk.connectWalletEvents()` is called (or auto-enabled via `captureWalletEvents: true`),
53
- * real-time blockchain events are automatically routed through the same event system:
54
- * ```typescript
55
- * const { subscribe } = useEvents();
56
- *
57
- * subscribe((event) => {
58
- * if (event.domain === 'wallet') {
59
- * console.log('Blockchain event:', event.type); // wallet_transfer, wallet_approval, etc.
60
- * }
61
- * });
62
- * ```
63
- *
64
- * **Notification Levels:**
65
- * - `success` - Operation completed successfully
66
- * - `error` - Operation failed
67
- *
68
- * **Cleanup:**
69
- * All subscriptions created through this hook are automatically cleaned up when
70
- * the component unmounts, preventing memory leaks and stale event handlers.
71
- *
72
- * @returns {EventsHook} Hook interface with event subscription methods
73
- *
74
- * @example
75
- * **Basic Usage - Show All Notifications**
76
- * ```typescript
77
- * import { useEvents } from '@explorins/pers-sdk-react-native';
78
- *
79
- * function NotificationComponent() {
80
- * const { subscribe, isAvailable } = useEvents();
81
- *
82
- * useEffect(() => {
83
- * if (!isAvailable) return;
84
- *
85
- * const unsubscribe = subscribe((event) => {
86
- * // userMessage is always present and UI-ready
87
- * showNotification(event.userMessage, event.level);
88
- * });
89
- *
90
- * return () => unsubscribe();
91
- * }, [subscribe, isAvailable]);
92
- *
93
- * return <View />;
94
- * }
95
- * ```
96
- *
97
- * @example
98
- * **Filter by Domain and Level**
99
- * ```typescript
100
- * function TransactionListener() {
101
- * const { subscribe, isAvailable } = useEvents();
102
- *
103
- * useEffect(() => {
104
- * if (!isAvailable) return;
105
- *
106
- * // Only listen to successful transactions
107
- * const unsubscribe = subscribe(
108
- * (event) => {
109
- * if (event.level === 'success') {
110
- * playSuccessSound();
111
- * showConfetti();
112
- * }
113
- * },
114
- * { domain: 'transaction' } // Filter
115
- * );
116
- *
117
- * return () => unsubscribe();
118
- * }, [subscribe, isAvailable]);
119
- * }
120
- * ```
121
- *
122
- * @example
123
- * **Filter Errors for Logging**
124
- * ```typescript
125
- * function ErrorLogger() {
126
- * const { subscribe, isAvailable } = useEvents();
127
- *
128
- * useEffect(() => {
129
- * if (!isAvailable) return;
130
- *
131
- * const unsubscribe = subscribe(
132
- * (event) => {
133
- * // Log errors to crash reporting
134
- * logToSentry({
135
- * message: event.userMessage,
136
- * domain: event.domain,
137
- * type: event.type,
138
- * });
139
- * },
140
- * { level: 'error' } // Only errors
141
- * );
142
- *
143
- * return () => unsubscribe();
144
- * }, [subscribe, isAvailable]);
145
- * }
146
- * ```
147
- *
148
- * @example
149
- * **One-time Event**
150
- * ```typescript
151
- * function FirstTransactionHandler() {
152
- * const { once, isAvailable } = useEvents();
153
- *
154
- * useEffect(() => {
155
- * if (!isAvailable) return;
156
- *
157
- * // Auto-unsubscribe after first transaction event
158
- * const unsubscribe = once(
159
- * (event) => {
160
- * console.log('First transaction event:', event.type);
161
- * showOnboardingComplete();
162
- * },
163
- * { domain: 'transaction', level: 'success' }
164
- * );
165
- *
166
- * return () => unsubscribe();
167
- * }, [once, isAvailable]);
168
- * }
169
- * ```
170
- *
171
- * @example
172
- * **Combined Domain and Level Filter**
173
- * ```typescript
174
- * function AuthErrorHandler() {
175
- * const { subscribe, isAvailable } = useEvents();
176
- *
177
- * useEffect(() => {
178
- * if (!isAvailable) return;
179
- *
180
- * const unsubscribe = subscribe(
181
- * (event) => {
182
- * Alert.alert('Authentication Error', event.userMessage);
183
- * navigation.navigate('Login');
184
- * },
185
- * { domain: 'auth', level: 'error' }
186
- * );
187
- *
188
- * return () => unsubscribe();
189
- * }, [subscribe, isAvailable]);
190
- * }
191
- * ```
192
- *
193
- * @see {@link PersEvent} for event structure
194
- * @see {@link EventFilter} for filtering options
195
- * @see {@link EventHandler} for handler function type
196
- *
197
- * @since 1.6.49
198
- */
199
- export const useEvents = (): EventsHook => {
200
- const { sdk, isInitialized } = usePersSDK();
201
-
202
- // Track subscriptions for cleanup
203
- const subscriptionsRef = useRef<Unsubscribe[]>([]);
204
-
205
- // Cleanup all subscriptions on unmount
206
- useEffect(() => {
207
- return () => {
208
- subscriptionsRef.current.forEach(unsubscribe => {
209
- try {
210
- unsubscribe();
211
- } catch (e) {
212
- // Ignore cleanup errors
213
- }
214
- });
215
- subscriptionsRef.current = [];
216
- };
217
- }, []);
218
-
219
- /**
220
- * Subscribe to SDK events with optional filtering
221
- *
222
- * Creates a subscription that will be called for every matching event.
223
- * The subscription is automatically tracked for cleanup when the component unmounts.
224
- *
225
- * @param handler - Callback function invoked for each matching event
226
- * @param filter - Optional filter to limit events (by domain, level, or both)
227
- * @returns Unsubscribe function to manually cancel the subscription
228
- *
229
- * @example
230
- * ```typescript
231
- * const unsubscribe = subscribe((event) => {
232
- * console.log('Event:', event.type, event.userMessage);
233
- * });
234
- *
235
- * // Later: manual cleanup
236
- * unsubscribe();
237
- * ```
238
- */
239
- const subscribe = useCallback((handler: EventHandler, filter?: EventFilter): Unsubscribe => {
240
- if (!isInitialized || !sdk) {
241
- console.warn('[useEvents] SDK not initialized. Cannot subscribe to events.');
242
- return () => {}; // Return no-op unsubscribe
243
- }
244
-
245
- const unsubscribe = sdk.events.subscribe(handler, filter);
246
-
247
- // Track for cleanup
248
- subscriptionsRef.current.push(unsubscribe);
249
-
250
- // Return wrapped unsubscribe that also removes from tracking
251
- return () => {
252
- unsubscribe();
253
- const index = subscriptionsRef.current.indexOf(unsubscribe);
254
- if (index > -1) {
255
- subscriptionsRef.current.splice(index, 1);
256
- }
257
- };
258
- }, [sdk, isInitialized]);
259
-
260
- /**
261
- * Subscribe to a single event (auto-unsubscribes after first match)
262
- *
263
- * Useful for one-time event handling, like waiting for a specific action to complete.
264
- *
265
- * @param handler - Callback function invoked for the first matching event
266
- * @param filter - Optional filter to limit events (by domain, level, or both)
267
- * @returns Unsubscribe function to manually cancel before event fires
268
- *
269
- * @example
270
- * ```typescript
271
- * once((event) => {
272
- * console.log('First event:', event.type);
273
- * }, { domain: 'transaction', level: 'success' });
274
- * ```
275
- */
276
- const once = useCallback((handler: EventHandler, filter?: EventFilter): Unsubscribe => {
277
- if (!isInitialized || !sdk) {
278
- console.warn('[useEvents] SDK not initialized. Cannot subscribe to events.');
279
- return () => {};
280
- }
281
-
282
- const unsubscribe = sdk.events.once(handler, filter);
283
-
284
- // Track for cleanup
285
- subscriptionsRef.current.push(unsubscribe);
286
-
287
- return () => {
288
- unsubscribe();
289
- const index = subscriptionsRef.current.indexOf(unsubscribe);
290
- if (index > -1) {
291
- subscriptionsRef.current.splice(index, 1);
292
- }
293
- };
294
- }, [sdk, isInitialized]);
295
-
296
- /**
297
- * Clear all event subscriptions from this hook instance
298
- *
299
- * Useful when you want to reset all event listeners without unmounting the component.
300
- * Note: This only clears subscriptions created through this hook instance.
301
- *
302
- * @example
303
- * ```typescript
304
- * const { clear, subscribe } = useEvents();
305
- *
306
- * // Create some subscriptions
307
- * subscribe((event) => console.log(event));
308
- * subscribe((event) => logToServer(event));
309
- *
310
- * // Later: clear all subscriptions from this hook
311
- * clear();
312
- * ```
313
- */
314
- const clear = useCallback((): void => {
315
- subscriptionsRef.current.forEach(unsubscribe => {
316
- try {
317
- unsubscribe();
318
- } catch (e) {
319
- // Ignore cleanup errors
320
- }
321
- });
322
- subscriptionsRef.current = [];
323
- }, []);
324
-
325
- /**
326
- * Get current subscriber count from the event emitter
327
- *
328
- * Useful for debugging or showing active listener count in dev tools.
329
- */
330
- const subscriberCount = sdk?.events?.subscriberCount ?? 0;
331
-
332
- return {
333
- /**
334
- * Subscribe to SDK events with optional filtering
335
- *
336
- * @param handler - Callback function for each matching event
337
- * @param filter - Optional filter by domain and/or level
338
- * @returns Unsubscribe function
339
- */
340
- subscribe,
341
-
342
- /**
343
- * Subscribe to a single event (auto-unsubscribes after first match)
344
- *
345
- * @param handler - Callback function for the first matching event
346
- * @param filter - Optional filter by domain and/or level
347
- * @returns Unsubscribe function
348
- */
349
- once,
350
-
351
- /**
352
- * Clear all subscriptions created through this hook instance
353
- */
354
- clear,
355
-
356
- /**
357
- * Whether the event system is available
358
- *
359
- * Returns `true` when SDK is initialized and events can be subscribed to.
360
- */
361
- isAvailable: isInitialized && !!sdk?.events,
362
-
363
- /**
364
- * Current number of active subscribers across all components
365
- *
366
- * Useful for debugging event system usage.
367
- */
368
- subscriberCount,
369
- };
370
- };
371
-
372
- export type EventsHook_ = ReturnType<typeof useEvents>;
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { usePersSDK } from '../providers/PersSDKProvider';
3
+ import type {
4
+ PersEvent,
5
+ EventHandler,
6
+ EventFilter,
7
+ Unsubscribe
8
+ } from '@explorins/pers-sdk/core';
9
+
10
+ // Re-export event types for convenience
11
+ export type { PersEvent, EventHandler, EventFilter, Unsubscribe };
12
+
13
+ /**
14
+ * Return interface for the useEvents hook
15
+ *
16
+ * Provides event subscription capabilities for the PERS SDK event system.
17
+ * Use this to display notifications, update UI, or trigger side effects.
18
+ *
19
+ * @interface EventsHook
20
+ * @property {Function} subscribe - Subscribe to SDK events with optional filtering
21
+ * @property {Function} once - Subscribe to a single event (auto-unsubscribes)
22
+ * @property {Function} clear - Clear all subscriptions
23
+ * @property {boolean} isAvailable - Whether the event system is available
24
+ * @property {number} subscriberCount - Current number of active subscribers
25
+ */
26
+ export interface EventsHook {
27
+ subscribe: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
28
+ once: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
29
+ clear: () => void;
30
+ isAvailable: boolean;
31
+ subscriberCount: number;
32
+ }
33
+
34
+ /**
35
+ * React Native hook for PERS SDK event system
36
+ *
37
+ * This hook provides access to the platform-agnostic event system for subscribing to
38
+ * transaction, authentication, campaign, blockchain, and system events. All events
39
+ * include a `userMessage` field ready for display to end users.
40
+ *
41
+ * **Event Domains:**
42
+ * - `auth` - Authentication events (login, logout, token refresh)
43
+ * - `user` - User profile events (update, create)
44
+ * - `transaction` - Transaction events (created, submitted, confirmed)
45
+ * - `campaign` - Campaign events (claimed, activated)
46
+ * - `redemption` - Redemption events (redeemed, expired)
47
+ * - `business` - Business events (created, updated, membership)
48
+ * - `wallet` - Real-time blockchain events (Transfer, Approval, etc.) - Auto-enabled on auth
49
+ * - `api` - API error events (network, validation, server errors)
50
+ *
51
+ * **Blockchain Events (Wallet Domain):**
52
+ * When `sdk.connectWalletEvents()` is called (or auto-enabled via `captureWalletEvents: true`),
53
+ * real-time blockchain events are automatically routed through the same event system:
54
+ * ```typescript
55
+ * const { subscribe } = useEvents();
56
+ *
57
+ * subscribe((event) => {
58
+ * if (event.domain === 'wallet') {
59
+ * console.log('Blockchain event:', event.type); // wallet_transfer, wallet_approval, etc.
60
+ * }
61
+ * });
62
+ * ```
63
+ *
64
+ * **Notification Levels:**
65
+ * - `success` - Operation completed successfully
66
+ * - `error` - Operation failed
67
+ *
68
+ * **Cleanup:**
69
+ * All subscriptions created through this hook are automatically cleaned up when
70
+ * the component unmounts, preventing memory leaks and stale event handlers.
71
+ *
72
+ * @returns {EventsHook} Hook interface with event subscription methods
73
+ *
74
+ * @example
75
+ * **Basic Usage - Show All Notifications**
76
+ * ```typescript
77
+ * import { useEvents } from '@explorins/pers-sdk-react-native';
78
+ *
79
+ * function NotificationComponent() {
80
+ * const { subscribe, isAvailable } = useEvents();
81
+ *
82
+ * useEffect(() => {
83
+ * if (!isAvailable) return;
84
+ *
85
+ * const unsubscribe = subscribe((event) => {
86
+ * // userMessage is always present and UI-ready
87
+ * showNotification(event.userMessage, event.level);
88
+ * });
89
+ *
90
+ * return () => unsubscribe();
91
+ * }, [subscribe, isAvailable]);
92
+ *
93
+ * return <View />;
94
+ * }
95
+ * ```
96
+ *
97
+ * @example
98
+ * **Filter by Domain and Level**
99
+ * ```typescript
100
+ * function TransactionListener() {
101
+ * const { subscribe, isAvailable } = useEvents();
102
+ *
103
+ * useEffect(() => {
104
+ * if (!isAvailable) return;
105
+ *
106
+ * // Only listen to successful transactions
107
+ * const unsubscribe = subscribe(
108
+ * (event) => {
109
+ * if (event.level === 'success') {
110
+ * playSuccessSound();
111
+ * showConfetti();
112
+ * }
113
+ * },
114
+ * { domain: 'transaction' } // Filter
115
+ * );
116
+ *
117
+ * return () => unsubscribe();
118
+ * }, [subscribe, isAvailable]);
119
+ * }
120
+ * ```
121
+ *
122
+ * @example
123
+ * **Filter Errors for Logging**
124
+ * ```typescript
125
+ * function ErrorLogger() {
126
+ * const { subscribe, isAvailable } = useEvents();
127
+ *
128
+ * useEffect(() => {
129
+ * if (!isAvailable) return;
130
+ *
131
+ * const unsubscribe = subscribe(
132
+ * (event) => {
133
+ * // Log errors to crash reporting
134
+ * logToSentry({
135
+ * message: event.userMessage,
136
+ * domain: event.domain,
137
+ * type: event.type,
138
+ * });
139
+ * },
140
+ * { level: 'error' } // Only errors
141
+ * );
142
+ *
143
+ * return () => unsubscribe();
144
+ * }, [subscribe, isAvailable]);
145
+ * }
146
+ * ```
147
+ *
148
+ * @example
149
+ * **One-time Event**
150
+ * ```typescript
151
+ * function FirstTransactionHandler() {
152
+ * const { once, isAvailable } = useEvents();
153
+ *
154
+ * useEffect(() => {
155
+ * if (!isAvailable) return;
156
+ *
157
+ * // Auto-unsubscribe after first transaction event
158
+ * const unsubscribe = once(
159
+ * (event) => {
160
+ * console.log('First transaction event:', event.type);
161
+ * showOnboardingComplete();
162
+ * },
163
+ * { domain: 'transaction', level: 'success' }
164
+ * );
165
+ *
166
+ * return () => unsubscribe();
167
+ * }, [once, isAvailable]);
168
+ * }
169
+ * ```
170
+ *
171
+ * @example
172
+ * **Combined Domain and Level Filter**
173
+ * ```typescript
174
+ * function AuthErrorHandler() {
175
+ * const { subscribe, isAvailable } = useEvents();
176
+ *
177
+ * useEffect(() => {
178
+ * if (!isAvailable) return;
179
+ *
180
+ * const unsubscribe = subscribe(
181
+ * (event) => {
182
+ * Alert.alert('Authentication Error', event.userMessage);
183
+ * navigation.navigate('Login');
184
+ * },
185
+ * { domain: 'auth', level: 'error' }
186
+ * );
187
+ *
188
+ * return () => unsubscribe();
189
+ * }, [subscribe, isAvailable]);
190
+ * }
191
+ * ```
192
+ *
193
+ * @see {@link PersEvent} for event structure
194
+ * @see {@link EventFilter} for filtering options
195
+ * @see {@link EventHandler} for handler function type
196
+ *
197
+ * @since 1.6.49
198
+ */
199
+ export const useEvents = (): EventsHook => {
200
+ const { sdk, isInitialized } = usePersSDK();
201
+
202
+ // Track subscriptions for cleanup
203
+ const subscriptionsRef = useRef<Unsubscribe[]>([]);
204
+
205
+ // Cleanup all subscriptions on unmount
206
+ useEffect(() => {
207
+ return () => {
208
+ subscriptionsRef.current.forEach(unsubscribe => {
209
+ try {
210
+ unsubscribe();
211
+ } catch (e) {
212
+ // Ignore cleanup errors
213
+ }
214
+ });
215
+ subscriptionsRef.current = [];
216
+ };
217
+ }, []);
218
+
219
+ /**
220
+ * Subscribe to SDK events with optional filtering
221
+ *
222
+ * Creates a subscription that will be called for every matching event.
223
+ * The subscription is automatically tracked for cleanup when the component unmounts.
224
+ *
225
+ * @param handler - Callback function invoked for each matching event
226
+ * @param filter - Optional filter to limit events (by domain, level, or both)
227
+ * @returns Unsubscribe function to manually cancel the subscription
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const unsubscribe = subscribe((event) => {
232
+ * console.log('Event:', event.type, event.userMessage);
233
+ * });
234
+ *
235
+ * // Later: manual cleanup
236
+ * unsubscribe();
237
+ * ```
238
+ */
239
+ const subscribe = useCallback((handler: EventHandler, filter?: EventFilter): Unsubscribe => {
240
+ if (!isInitialized || !sdk) {
241
+ console.warn('[useEvents] SDK not initialized. Cannot subscribe to events.');
242
+ return () => {}; // Return no-op unsubscribe
243
+ }
244
+
245
+ const unsubscribe = sdk.events.subscribe(handler, filter);
246
+
247
+ // Track for cleanup
248
+ subscriptionsRef.current.push(unsubscribe);
249
+
250
+ // Return wrapped unsubscribe that also removes from tracking
251
+ return () => {
252
+ unsubscribe();
253
+ const index = subscriptionsRef.current.indexOf(unsubscribe);
254
+ if (index > -1) {
255
+ subscriptionsRef.current.splice(index, 1);
256
+ }
257
+ };
258
+ }, [sdk, isInitialized]);
259
+
260
+ /**
261
+ * Subscribe to a single event (auto-unsubscribes after first match)
262
+ *
263
+ * Useful for one-time event handling, like waiting for a specific action to complete.
264
+ *
265
+ * @param handler - Callback function invoked for the first matching event
266
+ * @param filter - Optional filter to limit events (by domain, level, or both)
267
+ * @returns Unsubscribe function to manually cancel before event fires
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * once((event) => {
272
+ * console.log('First event:', event.type);
273
+ * }, { domain: 'transaction', level: 'success' });
274
+ * ```
275
+ */
276
+ const once = useCallback((handler: EventHandler, filter?: EventFilter): Unsubscribe => {
277
+ if (!isInitialized || !sdk) {
278
+ console.warn('[useEvents] SDK not initialized. Cannot subscribe to events.');
279
+ return () => {};
280
+ }
281
+
282
+ const unsubscribe = sdk.events.once(handler, filter);
283
+
284
+ // Track for cleanup
285
+ subscriptionsRef.current.push(unsubscribe);
286
+
287
+ return () => {
288
+ unsubscribe();
289
+ const index = subscriptionsRef.current.indexOf(unsubscribe);
290
+ if (index > -1) {
291
+ subscriptionsRef.current.splice(index, 1);
292
+ }
293
+ };
294
+ }, [sdk, isInitialized]);
295
+
296
+ /**
297
+ * Clear all event subscriptions from this hook instance
298
+ *
299
+ * Useful when you want to reset all event listeners without unmounting the component.
300
+ * Note: This only clears subscriptions created through this hook instance.
301
+ *
302
+ * @example
303
+ * ```typescript
304
+ * const { clear, subscribe } = useEvents();
305
+ *
306
+ * // Create some subscriptions
307
+ * subscribe((event) => console.log(event));
308
+ * subscribe((event) => logToServer(event));
309
+ *
310
+ * // Later: clear all subscriptions from this hook
311
+ * clear();
312
+ * ```
313
+ */
314
+ const clear = useCallback((): void => {
315
+ subscriptionsRef.current.forEach(unsubscribe => {
316
+ try {
317
+ unsubscribe();
318
+ } catch (e) {
319
+ // Ignore cleanup errors
320
+ }
321
+ });
322
+ subscriptionsRef.current = [];
323
+ }, []);
324
+
325
+ /**
326
+ * Get current subscriber count from the event emitter
327
+ *
328
+ * Useful for debugging or showing active listener count in dev tools.
329
+ */
330
+ const subscriberCount = sdk?.events?.subscriberCount ?? 0;
331
+
332
+ return {
333
+ /**
334
+ * Subscribe to SDK events with optional filtering
335
+ *
336
+ * @param handler - Callback function for each matching event
337
+ * @param filter - Optional filter by domain and/or level
338
+ * @returns Unsubscribe function
339
+ */
340
+ subscribe,
341
+
342
+ /**
343
+ * Subscribe to a single event (auto-unsubscribes after first match)
344
+ *
345
+ * @param handler - Callback function for the first matching event
346
+ * @param filter - Optional filter by domain and/or level
347
+ * @returns Unsubscribe function
348
+ */
349
+ once,
350
+
351
+ /**
352
+ * Clear all subscriptions created through this hook instance
353
+ */
354
+ clear,
355
+
356
+ /**
357
+ * Whether the event system is available
358
+ *
359
+ * Returns `true` when SDK is initialized and events can be subscribed to.
360
+ */
361
+ isAvailable: isInitialized && !!sdk?.events,
362
+
363
+ /**
364
+ * Current number of active subscribers across all components
365
+ *
366
+ * Useful for debugging event system usage.
367
+ */
368
+ subscriberCount,
369
+ };
370
+ };
371
+
372
+ export type EventsHook_ = ReturnType<typeof useEvents>;