@phygitallabs/tapquest-core 2.7.0 → 2.9.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.
- package/README.md +1 -1
- package/dist/index.cjs +1035 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -35
- package/dist/index.d.ts +29 -35
- package/dist/index.js +1017 -717
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
- package/src/modules/auth/constants/index.ts +6 -0
- package/src/modules/auth/helpers/index.ts +1 -4
- package/src/modules/auth/hooks/index.ts +2 -0
- package/src/modules/auth/hooks/useGoogleLogin.ts +169 -0
- package/src/modules/auth/hooks/useTokenRefresher.ts +39 -0
- package/src/modules/auth/index.ts +8 -2
- package/src/modules/auth/providers/AuthProvider.tsx +214 -186
- package/src/modules/auth/store/authStore.ts +577 -0
- package/src/modules/auth/types/auth.ts +29 -0
- package/src/modules/auth/types/user-data.ts +38 -0
- package/src/modules/auth/utils/user.ts +21 -0
- package/src/modules/data-tracking/hooks/index.ts +25 -1
- package/src/modules/generate-certificate/helpers/index.ts +3 -0
- package/src/modules/generate-certificate/hooks/index.ts +15 -6
- package/src/modules/generate-certificate/index.ts +3 -1
- package/src/modules/notification/providers/index.tsx +3 -3
- package/src/modules/reward/hooks/useRewardService.ts +6 -6
- package/src/modules/session-replay/README.md +334 -0
- package/src/modules/session-replay/hooks/useSessionReplay.ts +16 -0
- package/src/modules/session-replay/index.ts +10 -0
- package/src/modules/session-replay/providers/SessionReplayProvider.tsx +189 -0
- package/src/modules/session-replay/types/index.ts +147 -0
- package/src/modules/session-replay/utils/index.ts +12 -0
- package/src/providers/ServicesProvider.tsx +4 -76
- package/src/providers/TapquestCoreProvider.tsx +31 -36
- package/src/modules/auth/helpers/refreshToken.ts +0 -63
- package/src/modules/auth/store/authSlice.ts +0 -137
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
useGenerateThaocamvienCertificate,
|
|
3
|
+
useGenerateTemplateCertificate,
|
|
4
|
+
useGenerateFansipanCertificate,
|
|
5
|
+
useCreateCertificate,
|
|
6
|
+
useCreateCertificateAnonymous,
|
|
7
|
+
useCreateCertificateWithMask,
|
|
5
8
|
} from "@phygitallabs/generate-certificate";
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
export {
|
|
11
|
+
useGenerateThaocamvienCertificate,
|
|
12
|
+
useGenerateTemplateCertificate,
|
|
13
|
+
useGenerateFansipanCertificate,
|
|
14
|
+
useCreateCertificate,
|
|
15
|
+
useCreateCertificateAnonymous,
|
|
16
|
+
useCreateCertificateWithMask,
|
|
17
|
+
};
|
|
@@ -16,7 +16,7 @@ interface NotificationCallbacks {
|
|
|
16
16
|
interface NotificationProviderProps extends NotificationCallbacks {
|
|
17
17
|
children: React.ReactNode;
|
|
18
18
|
autoConnect?: boolean;
|
|
19
|
-
user: { id
|
|
19
|
+
user: { id?: string ; accessToken?: string };
|
|
20
20
|
environment?: EnvironmentType;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -34,8 +34,8 @@ export const NotificationProvider: React.FC<NotificationProviderProps> = ({
|
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
36
|
<NotificationProviderApi
|
|
37
|
-
userUid={user
|
|
38
|
-
accessToken={user
|
|
37
|
+
userUid={user?.id}
|
|
38
|
+
accessToken={user?.accessToken ?? null}
|
|
39
39
|
webSocketUrl={webSocketUrl}
|
|
40
40
|
autoConnect={autoConnect}
|
|
41
41
|
onWebSocketOpen={onWebSocketOpen}
|
|
@@ -3,12 +3,12 @@ export {
|
|
|
3
3
|
useManyUserRewards,
|
|
4
4
|
useGetUserRewards,
|
|
5
5
|
useClaimUserReward,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
useListRewardModels,
|
|
7
|
+
useGetRewardModel,
|
|
8
|
+
useCreateRewardModel,
|
|
9
|
+
useUpdateRewardModel,
|
|
10
|
+
useDeleteRewardModel,
|
|
11
11
|
useCreateModelGroupReward,
|
|
12
12
|
useClearUserRewardCache,
|
|
13
13
|
useV1ListRewards
|
|
14
|
-
} from "@phygitallabs/reward
|
|
14
|
+
} from "@phygitallabs/reward";
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# OpenReplay Session Replay Integration
|
|
2
|
+
|
|
3
|
+
This module provides a React context-based integration for OpenReplay session replay tracking, following the official Next.js tracker context pattern.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ React Context API with reducer pattern
|
|
8
|
+
- ✅ Dynamic user ID management with `setUserId` action
|
|
9
|
+
- ✅ SSR-safe with dynamic imports
|
|
10
|
+
- ✅ TypeScript support
|
|
11
|
+
- ✅ Automatic or custom user ID generation
|
|
12
|
+
- ✅ Flexible configuration options
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
The module is already part of `@tapquest/core`. Make sure you have the required dependencies:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @openreplay/tracker uuid
|
|
20
|
+
pnpm add -D @types/uuid
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Basic Usage
|
|
24
|
+
|
|
25
|
+
### 1. Wrap Your App with SessionReplayProvider
|
|
26
|
+
|
|
27
|
+
In your `_app.tsx` or root layout file:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { SessionReplayProvider } from '@tapquest/core';
|
|
31
|
+
|
|
32
|
+
function MyApp({ Component, pageProps }) {
|
|
33
|
+
return (
|
|
34
|
+
<SessionReplayProvider
|
|
35
|
+
config={{
|
|
36
|
+
projectKey: process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
|
|
37
|
+
userIdEnabled: false, // Set to true to enable automatic UUID generation
|
|
38
|
+
debug: process.env.NODE_ENV === 'development',
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
<Component {...pageProps} />
|
|
42
|
+
</SessionReplayProvider>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default MyApp;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Initialize Tracker in a Component
|
|
50
|
+
|
|
51
|
+
#### Option A: Using the `useSessionReplay` hook (Recommended)
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useEffect } from 'react';
|
|
55
|
+
import { useSessionReplay } from '@tapquest/core';
|
|
56
|
+
|
|
57
|
+
function MyComponent() {
|
|
58
|
+
const { initTracker, startTracking } = useSessionReplay();
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
initTracker();
|
|
62
|
+
startTracking();
|
|
63
|
+
}, [initTracker, startTracking]);
|
|
64
|
+
|
|
65
|
+
return <div>Your content</div>;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### Option B: Using `TrackerContext` directly
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { useContext, useEffect } from 'react';
|
|
73
|
+
import { TrackerContext } from '@tapquest/core';
|
|
74
|
+
|
|
75
|
+
function MyComponent() {
|
|
76
|
+
const tracker = useContext(TrackerContext);
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (tracker) {
|
|
80
|
+
tracker.initTracker();
|
|
81
|
+
tracker.startTracking();
|
|
82
|
+
}
|
|
83
|
+
}, [tracker]);
|
|
84
|
+
|
|
85
|
+
return <div>Your content</div>;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Advanced Usage
|
|
90
|
+
|
|
91
|
+
### Custom User ID Function
|
|
92
|
+
|
|
93
|
+
Provide a custom function to generate user IDs based on your authentication system:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { SessionReplayProvider } from '@tapquest/core';
|
|
97
|
+
import { useAuthStore } from './stores/authStore';
|
|
98
|
+
|
|
99
|
+
function MyApp({ Component, pageProps }) {
|
|
100
|
+
return (
|
|
101
|
+
<SessionReplayProvider
|
|
102
|
+
config={{
|
|
103
|
+
projectKey: process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
|
|
104
|
+
userIdEnabled: true,
|
|
105
|
+
getUserId: () => {
|
|
106
|
+
// Return the current user's ID from your auth system
|
|
107
|
+
const user = useAuthStore.getState().user;
|
|
108
|
+
return user?.id || user?.email || 'anonymous';
|
|
109
|
+
},
|
|
110
|
+
debug: process.env.NODE_ENV === 'development',
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<Component {...pageProps} />
|
|
114
|
+
</SessionReplayProvider>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Updating User ID After Login
|
|
120
|
+
|
|
121
|
+
Use the `setUserId` action to update the user ID when a user logs in:
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { useSessionReplay } from '@tapquest/core';
|
|
125
|
+
|
|
126
|
+
function LoginComponent() {
|
|
127
|
+
const { setUserId } = useSessionReplay();
|
|
128
|
+
|
|
129
|
+
const handleLogin = async (credentials) => {
|
|
130
|
+
const user = await loginUser(credentials);
|
|
131
|
+
|
|
132
|
+
// Update the tracker with the authenticated user's ID
|
|
133
|
+
setUserId(user.id);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return <LoginForm onSubmit={handleLogin} />;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Self-Hosted OpenReplay
|
|
141
|
+
|
|
142
|
+
For self-hosted OpenReplay instances, specify the ingest point:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
<SessionReplayProvider
|
|
146
|
+
config={{
|
|
147
|
+
projectKey: 'your-project-key',
|
|
148
|
+
ingestPoint: 'https://openreplay.yourdomain.com/ingest',
|
|
149
|
+
userIdEnabled: true,
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</SessionReplayProvider>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Full Configuration Example
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { SessionReplayProvider } from '@tapquest/core';
|
|
160
|
+
|
|
161
|
+
function MyApp({ Component, pageProps }) {
|
|
162
|
+
return (
|
|
163
|
+
<SessionReplayProvider
|
|
164
|
+
config={{
|
|
165
|
+
// Required
|
|
166
|
+
projectKey: process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
|
|
167
|
+
|
|
168
|
+
// User identification
|
|
169
|
+
userIdEnabled: true,
|
|
170
|
+
getUserId: () => getCurrentUser()?.id || 'anonymous',
|
|
171
|
+
|
|
172
|
+
// Custom ingest point (for self-hosted)
|
|
173
|
+
ingestPoint: 'https://openreplay.yourdomain.com/ingest',
|
|
174
|
+
|
|
175
|
+
// Capture options
|
|
176
|
+
captureExceptions: true,
|
|
177
|
+
capturePerformance: true,
|
|
178
|
+
|
|
179
|
+
// Network tracking
|
|
180
|
+
network: {
|
|
181
|
+
capturePayload: true,
|
|
182
|
+
sanitizer: (data) => {
|
|
183
|
+
// Redact sensitive information
|
|
184
|
+
if (data.request?.headers?.Authorization) {
|
|
185
|
+
data.request.headers.Authorization = '[REDACTED]';
|
|
186
|
+
}
|
|
187
|
+
return data;
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// Console tracking
|
|
192
|
+
console: {
|
|
193
|
+
levels: ['error', 'warn', 'log'],
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// Privacy settings
|
|
197
|
+
obscureTextEmails: true,
|
|
198
|
+
obscureTextNumbers: false,
|
|
199
|
+
obscureInputEmails: true,
|
|
200
|
+
|
|
201
|
+
// Development mode
|
|
202
|
+
debug: process.env.NODE_ENV === 'development',
|
|
203
|
+
__DISABLE_SECURE_MODE: process.env.NODE_ENV === 'development',
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
<Component {...pageProps} />
|
|
207
|
+
</SessionReplayProvider>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## API Reference
|
|
213
|
+
|
|
214
|
+
### SessionReplayProvider Props
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
interface SessionReplayProviderProps {
|
|
218
|
+
children: React.ReactNode;
|
|
219
|
+
config?: OpenReplayConfig;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### OpenReplayConfig
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
interface OpenReplayConfig {
|
|
227
|
+
projectKey?: string;
|
|
228
|
+
ingestPoint?: string;
|
|
229
|
+
userIdEnabled?: boolean;
|
|
230
|
+
getUserId?: () => string;
|
|
231
|
+
debug?: boolean;
|
|
232
|
+
captureExceptions?: boolean;
|
|
233
|
+
capturePerformance?: boolean;
|
|
234
|
+
network?: {
|
|
235
|
+
capturePayload?: boolean;
|
|
236
|
+
sanitizer?: (data: any) => any;
|
|
237
|
+
};
|
|
238
|
+
console?: {
|
|
239
|
+
levels?: Array<"error" | "warn" | "log" | "info" | "debug">;
|
|
240
|
+
};
|
|
241
|
+
obscureTextEmails?: boolean;
|
|
242
|
+
obscureTextNumbers?: boolean;
|
|
243
|
+
obscureInputEmails?: boolean;
|
|
244
|
+
__DISABLE_SECURE_MODE?: boolean;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### TrackerContextValue
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
interface TrackerContextValue {
|
|
252
|
+
initTracker: () => void;
|
|
253
|
+
startTracking: () => void;
|
|
254
|
+
setUserId: (userId: string) => void;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Environment Variables
|
|
259
|
+
|
|
260
|
+
Store your OpenReplay project key in an environment variable:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# .env.local
|
|
264
|
+
NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY=your_project_key_here
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Note:** Use the `NEXT_PUBLIC_` prefix to make the variable accessible in the browser.
|
|
268
|
+
|
|
269
|
+
## Best Practices
|
|
270
|
+
|
|
271
|
+
1. **Initialize Once**: Call `initTracker()` and `startTracking()` once when your app loads, typically in a root-level component or effect.
|
|
272
|
+
|
|
273
|
+
2. **Update User ID After Auth**: Use `setUserId()` after successful login to associate sessions with authenticated users.
|
|
274
|
+
|
|
275
|
+
3. **Sanitize Sensitive Data**: Use the `network.sanitizer` function to redact sensitive information like API keys, passwords, and PII.
|
|
276
|
+
|
|
277
|
+
4. **Use Environment Variables**: Never hardcode your project key. Use environment variables with the `NEXT_PUBLIC_` prefix.
|
|
278
|
+
|
|
279
|
+
5. **Enable Debug Mode in Development**: Set `debug: true` in development to see tracker logs in the console.
|
|
280
|
+
|
|
281
|
+
## Migration from Previous Implementation
|
|
282
|
+
|
|
283
|
+
If you're migrating from the old `useEffect`-based implementation:
|
|
284
|
+
|
|
285
|
+
**Before:**
|
|
286
|
+
```tsx
|
|
287
|
+
// Provider auto-initialized and started tracking
|
|
288
|
+
<SessionReplayProvider config={{ projectKey, userId: user.id }}>
|
|
289
|
+
{children}
|
|
290
|
+
</SessionReplayProvider>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**After:**
|
|
294
|
+
```tsx
|
|
295
|
+
// Provider requires explicit initialization
|
|
296
|
+
<SessionReplayProvider config={{ projectKey, userIdEnabled: true }}>
|
|
297
|
+
{children}
|
|
298
|
+
</SessionReplayProvider>
|
|
299
|
+
|
|
300
|
+
// In a component:
|
|
301
|
+
const tracker = useContext(TrackerContext);
|
|
302
|
+
useEffect(() => {
|
|
303
|
+
tracker?.initTracker();
|
|
304
|
+
tracker?.startTracking();
|
|
305
|
+
}, []);
|
|
306
|
+
|
|
307
|
+
// Update user ID dynamically:
|
|
308
|
+
tracker?.setUserId(user.id);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Troubleshooting
|
|
312
|
+
|
|
313
|
+
### Tracker not initializing
|
|
314
|
+
|
|
315
|
+
- Ensure you're calling `initTracker()` before `startTracking()`
|
|
316
|
+
- Check that your project key is set correctly
|
|
317
|
+
- Verify you're in a browser environment (not SSR)
|
|
318
|
+
|
|
319
|
+
### User ID not updating
|
|
320
|
+
|
|
321
|
+
- Make sure `userIdEnabled` is set to `true`
|
|
322
|
+
- Call `setUserId()` after the tracker is initialized
|
|
323
|
+
- Check debug logs to verify the user ID was set
|
|
324
|
+
|
|
325
|
+
### TypeScript errors
|
|
326
|
+
|
|
327
|
+
- Ensure you're importing types from `@tapquest/core`
|
|
328
|
+
- Verify that `TrackerContext` returns a non-null value before using it
|
|
329
|
+
|
|
330
|
+
## Links
|
|
331
|
+
|
|
332
|
+
- [OpenReplay Documentation](https://docs.openreplay.com/)
|
|
333
|
+
- [OpenReplay Next.js Integration](https://docs.openreplay.com/en/using-or/next/)
|
|
334
|
+
- [OpenReplay Tracker API](https://docs.openreplay.com/en/using-or/tracker-api/)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { TrackerContext } from "../providers/SessionReplayProvider";
|
|
3
|
+
import { TrackerContextValue } from "../types";
|
|
4
|
+
|
|
5
|
+
export function useSessionReplay(): TrackerContextValue {
|
|
6
|
+
const context = useContext(TrackerContext);
|
|
7
|
+
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"useSessionReplay must be used within a SessionReplayProvider. " +
|
|
11
|
+
"Make sure your component is wrapped with <SessionReplayProvider>."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { SessionReplayProvider, TrackerContext } from "./providers/SessionReplayProvider";
|
|
2
|
+
export { useSessionReplay } from "./hooks/useSessionReplay";
|
|
3
|
+
export type {
|
|
4
|
+
OpenReplayConfig,
|
|
5
|
+
SessionReplayProviderProps,
|
|
6
|
+
TrackerContextValue,
|
|
7
|
+
TrackerState,
|
|
8
|
+
TrackerAction,
|
|
9
|
+
} from "./types";
|
|
10
|
+
export * from "./utils";
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import React, { createContext, useEffect, useReducer } from "react";
|
|
2
|
+
import { v4 as uuidV4 } from "uuid";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SessionReplayProviderProps,
|
|
6
|
+
TrackerState,
|
|
7
|
+
TrackerAction,
|
|
8
|
+
TrackerContextValue,
|
|
9
|
+
OpenReplayConfig,
|
|
10
|
+
} from "../types";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
isBrowser
|
|
14
|
+
} from "../utils";
|
|
15
|
+
|
|
16
|
+
export const TrackerContext = createContext<TrackerContextValue | null>(null);
|
|
17
|
+
|
|
18
|
+
function defaultGetUserId(): string {
|
|
19
|
+
return uuidV4();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function newTracker(config: OpenReplayConfig) {
|
|
23
|
+
try {
|
|
24
|
+
// Dynamic import for SSR compatibility
|
|
25
|
+
const OpenReplay = (await import("@openreplay/tracker")).default;
|
|
26
|
+
|
|
27
|
+
// Get user ID function (custom or default UUID generator)
|
|
28
|
+
const getUserId =
|
|
29
|
+
config?.userIdEnabled && config?.getUserId
|
|
30
|
+
? config.getUserId
|
|
31
|
+
: defaultGetUserId;
|
|
32
|
+
|
|
33
|
+
// Build tracker configuration
|
|
34
|
+
const trackerConfig: any = {
|
|
35
|
+
projectKey:
|
|
36
|
+
config?.projectKey || process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
|
|
37
|
+
ingestPoint: config?.ingestPoint,
|
|
38
|
+
// Capture options
|
|
39
|
+
captureExceptions: config.captureExceptions ?? true,
|
|
40
|
+
capturePerformance: config.capturePerformance ?? true,
|
|
41
|
+
// Network tracking
|
|
42
|
+
network: config.network || {
|
|
43
|
+
capturePayload: true,
|
|
44
|
+
sanitizer: (data: any) => data,
|
|
45
|
+
},
|
|
46
|
+
// Console tracking
|
|
47
|
+
console: config.console || {
|
|
48
|
+
levels: ["error", "warn", "log"],
|
|
49
|
+
},
|
|
50
|
+
// Privacy settings
|
|
51
|
+
obscureTextEmails: config.obscureTextEmails ?? true,
|
|
52
|
+
obscureTextNumbers: config.obscureTextNumbers ?? false,
|
|
53
|
+
obscureInputEmails: config.obscureInputEmails ?? true,
|
|
54
|
+
// Development mode
|
|
55
|
+
__DISABLE_SECURE_MODE:
|
|
56
|
+
config.__DISABLE_SECURE_MODE ??
|
|
57
|
+
(typeof process !== "undefined" && process.env?.NODE_ENV === "development"),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Initialize tracker
|
|
61
|
+
const tracker = new OpenReplay(trackerConfig);
|
|
62
|
+
|
|
63
|
+
// Set user ID if enabled
|
|
64
|
+
if (config?.userIdEnabled) {
|
|
65
|
+
const userId = getUserId();
|
|
66
|
+
tracker.setUserID(userId);
|
|
67
|
+
console.log("User ID set:", userId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log("OpenReplay tracker initialized");
|
|
71
|
+
console.log("Project Key:", trackerConfig.projectKey);
|
|
72
|
+
console.log("Ingest Point:", trackerConfig.ingestPoint);
|
|
73
|
+
|
|
74
|
+
return tracker;
|
|
75
|
+
} catch (error: any) {
|
|
76
|
+
console.error("Failed to create tracker:", error);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function reducer(state: TrackerState, action: TrackerAction): TrackerState {
|
|
82
|
+
const { debug = false } = state.config;
|
|
83
|
+
|
|
84
|
+
switch (action.type) {
|
|
85
|
+
case "init":
|
|
86
|
+
// Only initialize if tracker doesn't exist and we're in browser
|
|
87
|
+
if (!state.tracker && isBrowser()) {
|
|
88
|
+
if (!state.config.projectKey && !process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY) {
|
|
89
|
+
console.warn(
|
|
90
|
+
debug,
|
|
91
|
+
"Project key not found. Skipping session replay initialization."
|
|
92
|
+
);
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Return state with tracker promise
|
|
97
|
+
// The tracker will be created asynchronously
|
|
98
|
+
return {
|
|
99
|
+
...state,
|
|
100
|
+
tracker: newTracker(state.config),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return state;
|
|
104
|
+
|
|
105
|
+
case "start":
|
|
106
|
+
// Start tracking if tracker exists
|
|
107
|
+
if (state.tracker) {
|
|
108
|
+
Promise.resolve(state.tracker)
|
|
109
|
+
.then((tracker) => {
|
|
110
|
+
tracker.start();
|
|
111
|
+
console.log(debug, "Session replay tracker started");
|
|
112
|
+
})
|
|
113
|
+
.catch((error) => {
|
|
114
|
+
console.error("Failed to start tracker:", error);
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
console.warn(debug, "Tracker not initialized. Call initTracker() first.");
|
|
118
|
+
}
|
|
119
|
+
return state;
|
|
120
|
+
|
|
121
|
+
case "setUserId":
|
|
122
|
+
// Set or update user ID
|
|
123
|
+
if (state.tracker) {
|
|
124
|
+
Promise.resolve(state.tracker)
|
|
125
|
+
.then((tracker) => {
|
|
126
|
+
tracker.setUserID(action.payload);
|
|
127
|
+
console.log(debug, "User ID updated:", action.payload);
|
|
128
|
+
})
|
|
129
|
+
.catch((error) => {
|
|
130
|
+
console.error("Failed to set user ID:", error);
|
|
131
|
+
});
|
|
132
|
+
} else {
|
|
133
|
+
console.warn(debug, "Tracker not initialized. Call initTracker() first.");
|
|
134
|
+
}
|
|
135
|
+
return state;
|
|
136
|
+
|
|
137
|
+
// Set metadata
|
|
138
|
+
case "setMetadata":
|
|
139
|
+
if (state.tracker) {
|
|
140
|
+
Promise.resolve(state.tracker)
|
|
141
|
+
.then((tracker) => {
|
|
142
|
+
Object.entries(action.payload || {})?.forEach(([key, value]) => {
|
|
143
|
+
tracker.setMetadata(key, value);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
console.log(debug, "Metadata updated:", action.payload.metadata);
|
|
147
|
+
})
|
|
148
|
+
.catch((error) => {
|
|
149
|
+
console.error("Failed to set metadata:", error);
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
console.warn(debug, "Tracker not initialized. Call initTracker() first.");
|
|
153
|
+
}
|
|
154
|
+
return state;
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
return state;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const SessionReplayProvider: React.FC<SessionReplayProviderProps> = ({
|
|
162
|
+
children,
|
|
163
|
+
config = {},
|
|
164
|
+
}) => {
|
|
165
|
+
const [, dispatch] = useReducer(reducer, {
|
|
166
|
+
tracker: null,
|
|
167
|
+
config,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const initTracker = () => dispatch({ type: "init" })
|
|
171
|
+
const startTracking = () => dispatch({ type: "start" })
|
|
172
|
+
const setUserId = (userId: string) => dispatch({ type: "setUserId", payload: userId })
|
|
173
|
+
const setMetadata = (metadata: Record<string, any>) => dispatch({ type: "setMetadata", payload: metadata })
|
|
174
|
+
|
|
175
|
+
// init and start tracker
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
initTracker();
|
|
178
|
+
startTracking();
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
181
|
+
return <TrackerContext.Provider value={{
|
|
182
|
+
initTracker,
|
|
183
|
+
startTracking,
|
|
184
|
+
setUserId,
|
|
185
|
+
setMetadata
|
|
186
|
+
}}>{children}</TrackerContext.Provider>;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export default SessionReplayProvider;
|