userlens-analytics-sdk 0.1.44 β†’ 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 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
+ ```
@@ -9,7 +9,7 @@ export default class EventCollector {
9
9
  private events;
10
10
  private userContext;
11
11
  private debug;
12
- constructor({ userId, userTraits, WRITE_CODE, callback, intervalTime, }: EventCollectorConfig);
12
+ constructor(config: EventCollectorConfig);
13
13
  pushEvent(event: {
14
14
  event: string;
15
15
  properties?: Record<string, any>;
@@ -1,10 +1,17 @@
1
- export type EventCollectorConfig = {
1
+ export type AutoUploadConfig = {
2
2
  userId: string;
3
- userTraits?: Record<string, any>;
4
3
  WRITE_CODE: string;
5
- callback?: (events: (PushedEvent | PageViewEvent | RawEvent)[]) => void;
4
+ userTraits?: Record<string, any>;
5
+ callback?: undefined;
6
+ intervalTime?: number;
7
+ };
8
+ export type CallbackModeConfig = {
9
+ callback: (events: (PushedEvent | PageViewEvent | RawEvent)[]) => void;
10
+ userId?: undefined;
11
+ WRITE_CODE?: undefined;
6
12
  intervalTime?: number;
7
13
  };
14
+ export type EventCollectorConfig = AutoUploadConfig | CallbackModeConfig;
8
15
  export interface UserContext {
9
16
  $ul_browser: string;
10
17
  $ul_browser_version: string;