userlens-analytics-sdk 0.1.45 β 0.1.46
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 +354 -0
- package/dist/userlens.cjs.js +2 -2
- package/dist/userlens.cjs.js.map +1 -1
- package/dist/userlens.esm.js +2 -2
- package/dist/userlens.esm.js.map +1 -1
- package/dist/userlens.umd.js +2 -2
- package/dist/userlens.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/utils.ts +1 -1
- package/tsconfig.json +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# π userlens.js
|
|
2
|
+
|
|
3
|
+
Powerful and lightweight event tracking + session replay SDK for web apps. Works standalone or with React. Built for modern frontend teams.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## π Table of Contents
|
|
8
|
+
|
|
9
|
+
## π Introduction
|
|
10
|
+
|
|
11
|
+
`userlens-analytics-sdk` is a lightweight, framework-agnostic JavaScript SDK for collecting user interaction events and recording session replays directly in the browser.
|
|
12
|
+
|
|
13
|
+
It supports two main features:
|
|
14
|
+
|
|
15
|
+
- π **Event tracking** β Capture clicks and page views, complete with DOM snapshots and context. You can also push your own custom events manually.
|
|
16
|
+
- π₯ **Session replay** β Record full user sessions.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install userlens-analytics-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
You can use `userlens-analytics-sdk` in two main ways:
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### π§ SDK Overview
|
|
31
|
+
|
|
32
|
+
There are **two layers** to this SDK:
|
|
33
|
+
|
|
34
|
+
1. **EventCollector** β Tracks user interactions like clicks and page views.
|
|
35
|
+
2. **SessionRecorder** β Captures full user session replays.
|
|
36
|
+
|
|
37
|
+
Both can be used:
|
|
38
|
+
|
|
39
|
+
- With the React provider (recommended for React apps)
|
|
40
|
+
- Manually via class instances (non-React or custom setups)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### βοΈ EventCollector β Two Modes
|
|
45
|
+
|
|
46
|
+
There are **two ways to configure** `EventCollector`:
|
|
47
|
+
|
|
48
|
+
#### 1. Manual Upload Mode (RECOMMENDED)
|
|
49
|
+
|
|
50
|
+
This is the most flexible and production-safe setup. You **receive events via a callback** and forward them through your own backend to the Userlens API.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const collector = new EventCollector({
|
|
54
|
+
callback: (events) => {
|
|
55
|
+
// send events to your backend
|
|
56
|
+
fetch("/api/forward-events", {
|
|
57
|
+
method: "POST",
|
|
58
|
+
body: JSON.stringify(events),
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
intervalTime: 5000, // optional
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If you're using **manual upload mode**, you're responsible for sending the collected events to the Userlens API. Here's how to do that properly in a Node.js/Express setup.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// server.js or routes/track.js
|
|
69
|
+
|
|
70
|
+
import express from "express";
|
|
71
|
+
import fetch from "node-fetch"; // or global fetch in newer Node versions
|
|
72
|
+
|
|
73
|
+
const router = express.Router();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Your WRITE_CODE β retrieve it from:
|
|
77
|
+
* π https://app.userlens.io/settings/userlens-sdk
|
|
78
|
+
*
|
|
79
|
+
* Before using it as an Authorization header:
|
|
80
|
+
* 1. Append a colon (`:`) at the end of the string
|
|
81
|
+
* 2. Base64 encode the result
|
|
82
|
+
*
|
|
83
|
+
* Example:
|
|
84
|
+
* const raw = "your_write_code:";
|
|
85
|
+
* const encoded = Buffer.from(raw).toString("base64");
|
|
86
|
+
* β use that as the value for Authorization: `Basic ${encoded}`
|
|
87
|
+
*/
|
|
88
|
+
const WRITE_CODE = process.env.USERLENS_WRITE_CODE!;
|
|
89
|
+
|
|
90
|
+
const MAIN_BASE_URL = "https://events.userlens.io";
|
|
91
|
+
const RAW_BASE_URL = "https://raw.userlens.io";
|
|
92
|
+
|
|
93
|
+
// Step 1: optional user traits sync
|
|
94
|
+
async function identify(userId, traits) {
|
|
95
|
+
if (!userId || !traits) return;
|
|
96
|
+
|
|
97
|
+
const body = {
|
|
98
|
+
type: "identify",
|
|
99
|
+
userId,
|
|
100
|
+
source: "userlens-js-analytics-sdk",
|
|
101
|
+
traits,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const res = await fetch(`${MAIN_BASE_URL}/event`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
Authorization: `Basic ${WRITE_CODE}`,
|
|
109
|
+
},
|
|
110
|
+
body: JSON.stringify(body),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!res.ok) throw new Error("Failed to identify user");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Step 2: send the events array
|
|
117
|
+
async function track(events) {
|
|
118
|
+
const body = { events };
|
|
119
|
+
|
|
120
|
+
const res = await fetch(`${RAW_BASE_URL}/raw/event`, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
Authorization: `Basic ${WRITE_CODE}`,
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify(body),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (!res.ok) throw new Error("Failed to track events");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Your actual POST endpoint
|
|
133
|
+
router.post("/forward-events", async (req, res) => {
|
|
134
|
+
const events = req.body;
|
|
135
|
+
if (!Array.isArray(events)) return res.status(400).send("Invalid body");
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const first = events[0];
|
|
139
|
+
|
|
140
|
+
// Optional: keep traits in sync
|
|
141
|
+
if (first?.userId && first?.properties) {
|
|
142
|
+
await identify(first.userId, first.properties);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await track(events);
|
|
146
|
+
res.status(200).send("ok");
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error("Userlens forwarding error:", err);
|
|
149
|
+
res.status(500).send("Tracking failed");
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export default router;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
<!-- MENTION HERE HOW TO RECEIVE THE EVENTS AND FORWARD THEM TO UL API -->
|
|
157
|
+
|
|
158
|
+
β
Pros:
|
|
159
|
+
|
|
160
|
+
- Works around adblockers
|
|
161
|
+
- You can batch, modify, or encrypt events
|
|
162
|
+
|
|
163
|
+
#### 2. Auto-Upload Mode
|
|
164
|
+
|
|
165
|
+
This mode sends events directly to the Userlens API from the frontend.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const collector = new EventCollector({
|
|
169
|
+
userId: "user-123", // β
required
|
|
170
|
+
WRITE_CODE: "your-public-write-code", // β
required
|
|
171
|
+
userTraits: { plan: "starter" }, // optional
|
|
172
|
+
intervalTime: 5000, // optional
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
β
Pros:
|
|
177
|
+
|
|
178
|
+
- Easy to set up
|
|
179
|
+
|
|
180
|
+
β οΈ Cons:
|
|
181
|
+
|
|
182
|
+
- May be blocked by adblockers
|
|
183
|
+
- You lose control over event delivery
|
|
184
|
+
|
|
185
|
+
βΉοΈ Use this only if youβre okay with events being sent directly from the browser.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### π₯ SessionRecorder
|
|
190
|
+
|
|
191
|
+
SessionRecorder captures full user sessions. It's enabled by default if WRITE_CODE and userId are passed in config when using the React provider. Alternatively, you can instantiate it manually.
|
|
192
|
+
|
|
193
|
+
It always requires:
|
|
194
|
+
|
|
195
|
+
- userId
|
|
196
|
+
- WRITE_CODE
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
const recorder = new SessionRecorder({
|
|
200
|
+
userId: "user-123",
|
|
201
|
+
WRITE_CODE: "your-public-write-code",
|
|
202
|
+
recordingOptions: {
|
|
203
|
+
maskingOptions: ["passwords"],
|
|
204
|
+
BUFFER_SIZE: 10,
|
|
205
|
+
TIMEOUT: 30 * 60 * 1000, // 30 mins
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
β If either userId or WRITE_CODE is missing, the recorder will not start and will log a warning.
|
|
211
|
+
|
|
212
|
+
### React Wrapper
|
|
213
|
+
|
|
214
|
+
The `UserlensProvider` is a React context wrapper that **automatically initializes** both:
|
|
215
|
+
|
|
216
|
+
<!-- - [`EventCollector`](#eventcollector-methods) β for capturing user events
|
|
217
|
+
- [`SessionRecorder`](#sessionrecorder-methods) β for recording user sessions -->
|
|
218
|
+
|
|
219
|
+
- `EventCollector` - for capturing user events
|
|
220
|
+
- `SessionRecorder` - for recording user sessions
|
|
221
|
+
|
|
222
|
+
This is the **recommended way** to integrate `userlens-analytics-sdk` into React projects.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
#### β
What It Does
|
|
227
|
+
|
|
228
|
+
Under the hood, the React wrapper:
|
|
229
|
+
|
|
230
|
+
- Instantiates `EventCollector` based on the mode (`callback` or `auto-upload`)
|
|
231
|
+
- Optionally starts a `SessionRecorder` if not disabled
|
|
232
|
+
- Manages lifecycle + cleanup for both
|
|
233
|
+
- Exposes both instances via the `useUserlens()` hook
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
#### π Usage Example
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { UserlensProvider } from "userlens-analytics-sdk";
|
|
241
|
+
|
|
242
|
+
const config = useMemo(
|
|
243
|
+
() => ({
|
|
244
|
+
// Required only if you're enabling session recording
|
|
245
|
+
// or using auto-upload mode for EventCollector
|
|
246
|
+
WRITE_CODE: "your-public-write-code",
|
|
247
|
+
// Required only if you're enabling session recording
|
|
248
|
+
// or using auto-upload mode for EventCollector
|
|
249
|
+
userId: "user-123",
|
|
250
|
+
// Optional β used when letting the SDK handle event uploads automatically
|
|
251
|
+
userTraits: { email: "jane@example.com" },
|
|
252
|
+
eventCollector: {
|
|
253
|
+
// Required when you want to manually handle event forwarding
|
|
254
|
+
callback: (events) => {
|
|
255
|
+
fetch("/api/track", {
|
|
256
|
+
method: "POST",
|
|
257
|
+
body: JSON.stringify(events),
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
// Set to false if you don't want to enable session replay
|
|
262
|
+
enableSessionReplay: true,
|
|
263
|
+
// Optional β fine-tunes session replay behavior
|
|
264
|
+
sessionRecorder: {
|
|
265
|
+
// Masks inputs like <input type="password" />
|
|
266
|
+
maskingOptions: ["passwords"],
|
|
267
|
+
// Controls how many events to buffer before flushing to backend
|
|
268
|
+
// Recommended: 10
|
|
269
|
+
BUFFER_SIZE: 10,
|
|
270
|
+
},
|
|
271
|
+
}),
|
|
272
|
+
[userId] // π Prevents unnecessary reinitialization
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<UserlensProvider config={config}>
|
|
277
|
+
<App />
|
|
278
|
+
</UserlensProvider>
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Then, you can access the SDK instances anywhere using the `useUserlens()` hook:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
import { useUserlens } from "userlens-analytics-sdk";
|
|
286
|
+
|
|
287
|
+
const { collector, sessionRecorder } = useUserlens();
|
|
288
|
+
|
|
289
|
+
collector?.pushEvent({
|
|
290
|
+
event: "Clicked CTA",
|
|
291
|
+
properties: { location: "hero" },
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
π Heads up: Always wrap your config in useMemo() to avoid re-instantiating the SDK on every render. Even though the provider has guards, you'll avoid subtle bugs and unnecessary warnings.
|
|
296
|
+
|
|
297
|
+
#### π Behavior Details
|
|
298
|
+
|
|
299
|
+
If enableSessionReplay: false is passed, the wrapper skips session recording.
|
|
300
|
+
|
|
301
|
+
If you call UserlensProvider with the same userId, it wonβt reinitialize anything.
|
|
302
|
+
|
|
303
|
+
If either WRITE_CODE or userId is missing, session replay will not start and a warning will be logged.
|
|
304
|
+
|
|
305
|
+
### π Tracking Custom Events
|
|
306
|
+
|
|
307
|
+
In addition to auto-tracked clicks and page views, you can manually push your own custom events using `collector.pushEvent()`.
|
|
308
|
+
|
|
309
|
+
This is useful for tracking things like:
|
|
310
|
+
|
|
311
|
+
- Form submissions
|
|
312
|
+
- In-app interactions (e.g. modal opened, tab switched)
|
|
313
|
+
- Feature usage
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
#### βοΈ Example
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { useUserlens } from "userlens-analytics-sdk";
|
|
321
|
+
|
|
322
|
+
const { collector } = useUserlens();
|
|
323
|
+
|
|
324
|
+
collector?.pushEvent({
|
|
325
|
+
event: "Upgraded Plan",
|
|
326
|
+
properties: {
|
|
327
|
+
plan: "pro",
|
|
328
|
+
source: "pricing_modal",
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
#### π§ How it works
|
|
336
|
+
|
|
337
|
+
The event will be stored as a `PushedEvent` (not a raw click or page view).
|
|
338
|
+
|
|
339
|
+
The `properties` object is merged with the user's full environment/context automatically (OS, browser, timezone, etc).
|
|
340
|
+
|
|
341
|
+
#### β οΈ TypeError: Cannot read properties of undefined
|
|
342
|
+
|
|
343
|
+
When using `UserlensProvider`, keep this in mind:
|
|
344
|
+
|
|
345
|
+
**Donβt call `pushEvent()` before the provider is mounted.**
|
|
346
|
+
The `collector` instance is created inside the provider on mount. If you try to access it too early (e.g. before the first render completes), `useUserlens()` will return `null`, and your call will silently do nothing β or worse, throw an error if you donβt check.
|
|
347
|
+
|
|
348
|
+
β
Always check that `collector` exists before using it:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
if (collector) {
|
|
352
|
+
collector.pushEvent({ event: "Something" });
|
|
353
|
+
}
|
|
354
|
+
```
|