swetrix 4.2.0 → 4.4.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 CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![NPM](https://img.shields.io/npm/v/swetrix)](https://www.npmjs.com/package/swetrix)
8
8
  [![Package size](https://img.shields.io/bundlephobia/minzip/swetrix)](https://bundlephobia.com/package/swetrix)
9
- [![JSDelivr hits](https://data.jsdelivr.com/v1/package/npm/swetrix/badge?style=rounded)](https://data.jsdelivr.com/v1/package/npm/swetrix/badge/stats)
9
+ [![JSDelivr hits](https://data.jsdelivr.com/v1/package/npm/swetrix/badge?style=rounded)](https://www.jsdelivr.com/package/npm/swetrix)
10
10
  [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/swetrix/swetrix-js/issues)
11
11
 
12
12
  # Swetrix Tracking Script
@@ -65,6 +65,7 @@ init('YOUR_PROJECT_ID', {
65
65
  disabled: false,
66
66
  respectDNT: false,
67
67
  profileId: 'user-123',
68
+ preloadSessionReplay: false,
68
69
  })
69
70
  ```
70
71
 
@@ -75,6 +76,7 @@ init('YOUR_PROJECT_ID', {
75
76
  | `disabled` | When `true`, no data is sent. Useful for dev environments. | `false` |
76
77
  | `respectDNT` | When `true`, disables tracking for users with Do Not Track enabled. | `false` |
77
78
  | `profileId` | Profile ID for long-term user tracking (MAU/DAU). | `undefined` |
79
+ | `preloadSessionReplay` | Preload the session replay recorder after `init()`. Recording only starts after `startSessionReplay()`. | `undefined` |
78
80
 
79
81
  ### `trackViews(options?)`
80
82
 
@@ -110,6 +112,7 @@ track({
110
112
  ev: 'signup',
111
113
  unique: true,
112
114
  meta: { plan: 'pro', source: 'landing' },
115
+ profileId: 'user-123',
113
116
  })
114
117
  ```
115
118
 
@@ -118,6 +121,7 @@ track({
118
121
  | `ev` | Event name (max 256 chars). | **required** |
119
122
  | `unique` | Only count once per session. | `false` |
120
123
  | `meta` | Key-value metadata (max 20 keys, 1000 chars total). | `{}` |
124
+ | `profileId` | Optional profile ID. Overrides the global `profileId` for this event. | `undefined` |
121
125
 
122
126
  ### `trackErrors(options?)`
123
127
 
@@ -161,29 +165,101 @@ pageview({
161
165
  ### Feature Flags
162
166
 
163
167
  ```javascript
164
- // Get all flags
168
+ // Get all flags. Results are cached for 5 minutes.
165
169
  const flags = await getFeatureFlags({ profileId: 'user-123' })
166
170
 
167
- // Get a single flag
171
+ // Force a fresh fetch
172
+ const freshFlags = await getFeatureFlags({ profileId: 'user-123' }, true)
173
+
174
+ // Get a single flag. The third argument is an optional fallback value.
168
175
  const enabled = await getFeatureFlag('dark-mode', { profileId: 'user-123' })
176
+ const enabledWithFallback = await getFeatureFlag('dark-mode', { profileId: 'user-123' }, false)
169
177
 
170
- // Clear cache
178
+ // Clear the shared feature flag / experiment cache
171
179
  clearFeatureFlagsCache()
172
180
  ```
173
181
 
174
182
  ### A/B Experiments
175
183
 
176
184
  ```javascript
177
- // Get all experiments
185
+ // Get all running experiment assignments. Results are cached for 5 minutes.
178
186
  const experiments = await getExperiments({ profileId: 'user-123' })
179
187
 
180
- // Get a specific experiment variant
181
- const variant = await getExperiment('checkout-redesign', { profileId: 'user-123' })
188
+ // Force a fresh fetch
189
+ const freshExperiments = await getExperiments({ profileId: 'user-123' }, true)
182
190
 
183
- // Clear cache
191
+ // Get a specific experiment variant. The third argument is an optional fallback variant.
192
+ const variant = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' })
193
+ const variantWithFallback = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' }, 'control')
194
+
195
+ // Clear the shared feature flag / experiment cache
184
196
  clearExperimentsCache()
185
197
  ```
186
198
 
199
+ ### `startSessionReplay(options?)`
200
+
201
+ Start recording a session replay. Session replays use `total` privacy by default, which masks text and inputs and blocks media/canvas capture unless you explicitly choose another mode.
202
+
203
+ If you use the npm package, rrweb is dynamically imported from your installed dependencies only when the recorder is preloaded or started. If you use the CDN/script-tag build, the standalone replay recorder is loaded with an async script tag.
204
+
205
+ ```javascript
206
+ const replay = await startSessionReplay({
207
+ privacy: 'total',
208
+ maskAllText: true,
209
+ sampleRate: 0.25,
210
+ maxDurationMs: 10 * 60 * 1000,
211
+ idleTimeoutMs: 2 * 60 * 1000,
212
+ maxBytesPerChunk: 512 * 1024,
213
+ })
214
+
215
+ // Stop or flush manually when needed
216
+ await replay.flush()
217
+ await replay.stop()
218
+ ```
219
+
220
+ | Option | Description | Default |
221
+ |---|---|---|
222
+ | `privacy` | Privacy mode: `total`, `normal`, or `none`. | `'total'` |
223
+ | `maskAllText` | Mask all non-input text with asterisks. Defaults to `true` when `privacy` is `total`, otherwise `false`. | privacy-based |
224
+ | `sampleRate` | Fraction of sessions to record (`0` to `1`). | `1` |
225
+ | `maxDurationMs` | Stop recording after this duration. | `undefined` |
226
+ | `idleTimeoutMs` | Stop recording after this much visitor inactivity. | `undefined` |
227
+ | `flushIntervalMs` | Upload buffered replay events at this interval. | `5000` |
228
+ | `maxEventsPerChunk` | Upload once this many events are buffered. | `100` |
229
+ | `maxBytesPerChunk` | Upload once buffered replay events reach this approximate byte size. | `524288` |
230
+ | `maxBytesPerEvent` | Drop a single replay event if it is larger than this many bytes. | `5242880` |
231
+ | `recordIframes` | Allow iframe elements to be captured. Iframes are blocked by default to reduce replay size and avoid recording embedded third-party content. | `false` |
232
+ | `rrweb` | Additional rrweb record options. | `undefined` |
233
+
234
+ To mask text while keeping media less restricted than `total` privacy, combine `normal` privacy with `maskAllText`:
235
+
236
+ ```javascript
237
+ await startSessionReplay({
238
+ privacy: 'normal',
239
+ maskAllText: true,
240
+ })
241
+ ```
242
+
243
+ By default, Swetrix blocks iframe elements from replay snapshots. If you own the iframe content and need it in the replay, opt in explicitly:
244
+
245
+ ```javascript
246
+ await startSessionReplay({
247
+ privacy: 'normal',
248
+ recordIframes: true,
249
+ })
250
+ ```
251
+
252
+ Cross-origin iframe recording also requires rrweb's cross-origin iframe support and should only be enabled for domains you control:
253
+
254
+ ```javascript
255
+ await startSessionReplay({
256
+ recordIframes: true,
257
+ rrweb: {
258
+ recordCrossOriginIframes: true,
259
+ },
260
+ })
261
+ ```
262
+
187
263
  ### Session & Profile IDs
188
264
 
189
265
  ```javascript
@@ -1,3 +1,22 @@
1
+ type RrwebEvent = Record<string, unknown>;
2
+ type RrwebEmit = (event: RrwebEvent) => void;
3
+ interface RrwebRecordOptions {
4
+ emit?: RrwebEmit;
5
+ [key: string]: unknown;
6
+ }
7
+ interface RrwebGlobal {
8
+ record?: (options: RrwebRecordOptions) => (() => void) | undefined;
9
+ Replayer?: unknown;
10
+ }
11
+ type SessionReplayPreloadOption = boolean | {
12
+ rrwebUrl?: string;
13
+ };
14
+ declare global {
15
+ interface Window {
16
+ rrweb?: RrwebGlobal;
17
+ __SWETRIX_RRWEB_LOADING__?: Promise<void>;
18
+ }
19
+ }
1
20
  export interface LibOptions {
2
21
  /**
3
22
  * When set to `true`, localhost events will be sent to server.
@@ -19,6 +38,10 @@ export interface LibOptions {
19
38
  * If set, it will be used for all pageviews and events unless overridden per-call.
20
39
  */
21
40
  profileId?: string;
41
+ /**
42
+ * Preload session replay recorder code. Recording only starts after calling startSessionReplay().
43
+ */
44
+ preloadSessionReplay?: SessionReplayPreloadOption;
22
45
  }
23
46
  export interface TrackEventOptions {
24
47
  /** The custom event name. */
@@ -117,6 +140,25 @@ export interface ErrorActions {
117
140
  /** Stops the tracking of errors. */
118
141
  stop: () => void;
119
142
  }
143
+ declare const SESSION_REPLAY_PRIVACY_VALUES: readonly ["total", "normal", "none"];
144
+ export type SessionReplayPrivacy = (typeof SESSION_REPLAY_PRIVACY_VALUES)[number];
145
+ export interface SessionReplayOptions {
146
+ privacy?: SessionReplayPrivacy;
147
+ rrweb?: RrwebRecordOptions;
148
+ flushIntervalMs?: number;
149
+ maxEventsPerChunk?: number;
150
+ maxBytesPerChunk?: number;
151
+ maxBytesPerEvent?: number;
152
+ sampleRate?: number;
153
+ maxDurationMs?: number;
154
+ idleTimeoutMs?: number;
155
+ maskAllText?: boolean;
156
+ recordIframes?: boolean;
157
+ }
158
+ export interface SessionReplayActions {
159
+ stop: () => Promise<void>;
160
+ flush: () => Promise<void>;
161
+ }
120
162
  export interface PageData {
121
163
  /** Current URL path. */
122
164
  path: string;
@@ -169,6 +211,7 @@ export interface PageViewsOptions {
169
211
  export declare const defaultActions: {
170
212
  stop(): void;
171
213
  };
214
+ export declare const defaultSessionReplayActions: SessionReplayActions;
172
215
  export declare class Lib {
173
216
  private projectID;
174
217
  private options?;
@@ -179,6 +222,9 @@ export declare class Lib {
179
222
  private activePage;
180
223
  private errorListenerExists;
181
224
  private cachedData;
225
+ private rrwebLoader;
226
+ private sessionReplayActions;
227
+ private sessionReplayInitPromise;
182
228
  constructor(projectID: string, options?: LibOptions | undefined);
183
229
  captureError(event: ErrorEvent): void;
184
230
  trackErrors(options?: ErrorOptions): ErrorActions;
@@ -187,10 +233,10 @@ export declare class Lib {
187
233
  trackPageViews(options?: PageViewsOptions): PageActions;
188
234
  getPerformanceStats(): IPerfPayload | {};
189
235
  /**
190
- * Fetches all feature flags and experiments for the project.
191
- * Results are cached for 5 minutes by default.
236
+ * Fetches all feature flags for the project.
237
+ * Results are cached for 5 minutes by default and share a cache with experiments.
192
238
  *
193
- * @param options - Options for evaluating feature flags.
239
+ * @param options - Options for evaluating feature flags (`profileId` only).
194
240
  * @param forceRefresh - If true, bypasses the cache and fetches fresh data.
195
241
  * @returns A promise that resolves to a record of flag keys to boolean values.
196
242
  */
@@ -203,8 +249,8 @@ export declare class Lib {
203
249
  * Gets the value of a single feature flag.
204
250
  *
205
251
  * @param key - The feature flag key.
206
- * @param options - Options for evaluating the feature flag.
207
- * @param defaultValue - Default value to return if the flag is not found. Defaults to false.
252
+ * @param options - Options for evaluating the feature flag (`profileId` only).
253
+ * @param defaultValue - Optional default value to return if the flag is not found. Defaults to false.
208
254
  * @returns A promise that resolves to the boolean value of the flag.
209
255
  */
210
256
  getFeatureFlag(key: string, options?: FeatureFlagsOptions, defaultValue?: boolean): Promise<boolean>;
@@ -213,16 +259,16 @@ export declare class Lib {
213
259
  */
214
260
  clearFeatureFlagsCache(): void;
215
261
  /**
216
- * Fetches all A/B test experiments for the project.
262
+ * Fetches variant assignments for running A/B test experiments returned by feature flag evaluation.
217
263
  * Results are cached for 5 minutes by default (shared cache with feature flags).
218
264
  *
219
- * @param options - Options for evaluating experiments.
265
+ * @param options - Options for evaluating experiments (`profileId` only).
220
266
  * @param forceRefresh - If true, bypasses the cache and fetches fresh data.
221
267
  * @returns A promise that resolves to a record of experiment IDs to variant keys.
222
268
  *
223
269
  * @example
224
270
  * ```typescript
225
- * const experiments = await getExperiments()
271
+ * const experiments = await getExperiments({ profileId: 'user-123' })
226
272
  * // experiments = { 'exp-123': 'variant-a', 'exp-456': 'control' }
227
273
  * ```
228
274
  */
@@ -231,13 +277,16 @@ export declare class Lib {
231
277
  * Gets the variant key for a specific A/B test experiment.
232
278
  *
233
279
  * @param experimentId - The experiment ID.
234
- * @param options - Options for evaluating the experiment.
235
- * @param defaultVariant - Default variant key to return if the experiment is not found. Defaults to null.
280
+ * @param options - Options for evaluating the experiment (`profileId` only).
281
+ * @param defaultVariant - Optional default variant key to return if the experiment is not found. Defaults to null.
236
282
  * @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
237
283
  *
238
284
  * @example
239
285
  * ```typescript
240
- * const variant = await getExperiment('checkout-redesign')
286
+ * const variant = await getExperiment('checkout-redesign', { profileId: 'user-123' })
287
+ *
288
+ * // Optional fallback variant:
289
+ * const variantWithFallback = await getExperiment('checkout-redesign', undefined, 'control')
241
290
  *
242
291
  * if (variant === 'new-checkout') {
243
292
  * // Show new checkout flow
@@ -298,6 +347,10 @@ export declare class Lib {
298
347
  * ```
299
348
  */
300
349
  getSessionId(): Promise<string | null>;
350
+ startSessionReplay(options?: SessionReplayOptions): Promise<SessionReplayActions>;
351
+ private initialiseSessionReplay;
352
+ private shouldSampleSessionReplay;
353
+ private getSessionReplayPrivacy;
301
354
  /**
302
355
  * Gets the API base URL (without /log suffix).
303
356
  */
@@ -307,6 +360,19 @@ export declare class Lib {
307
360
  private trackPage;
308
361
  submitPageView(payload: Partial<IPageViewPayload>, unique: boolean, perf: IPerfPayload | {}, evokeCallback?: boolean): void;
309
362
  private canTrack;
363
+ private getSessionReplayUrl;
364
+ private getSessionReplayPreloadOption;
365
+ private getDefaultSessionReplayUrl;
366
+ private getTrackerScript;
367
+ private preloadSessionReplay;
368
+ private loadSessionReplayRecorder;
369
+ private loadSessionReplayPackage;
370
+ private loadSessionReplayScript;
371
+ private getSessionReplayRecordOptions;
372
+ private mergeSelectors;
373
+ private createReplayId;
374
+ private sendSessionReplayStart;
375
+ private sendSessionReplayChunk;
310
376
  private sendRequest;
311
377
  }
312
378
  export {};