swetrix 4.1.0 → 4.3.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 +245 -14
- package/dist/esnext/Lib.d.ts +79 -11
- package/dist/esnext/Lib.js +417 -12
- package/dist/esnext/Lib.js.map +1 -1
- package/dist/esnext/index.d.ts +18 -15
- package/dist/esnext/index.js +23 -15
- package/dist/esnext/index.js.map +1 -1
- package/dist/esnext/utils.d.ts +9 -0
- package/dist/esnext/utils.js +20 -0
- package/dist/esnext/utils.js.map +1 -1
- package/dist/replaylibrary.min.js +173 -0
- package/dist/swetrix.cjs.js +463 -25
- package/dist/swetrix.cjs.js.map +1 -1
- package/dist/swetrix.es5.js +463 -26
- package/dist/swetrix.es5.js.map +1 -1
- package/dist/swetrix.js +1 -1
- package/dist/swetrix.js.map +1 -1
- package/jest.config.js +3 -1
- package/package.json +43 -40
- package/rollup.config.mjs +20 -0
- package/src/Lib.ts +589 -12
- package/src/index.ts +29 -14
- package/src/types/rrweb-shim.d.ts +11 -0
- package/src/utils.ts +22 -0
- package/tests/errors.test.ts +2 -9
- package/tests/events.test.ts +2 -9
- package/tests/experiments.test.ts +2 -9
- package/tests/initialisation.test.ts +5 -18
- package/tests/jsdomEnvironment.ts +20 -0
- package/tests/pageview.test.ts +3 -9
- package/tests/sessionReplay.test.ts +389 -0
- package/tests/testUtils.ts +27 -0
- package/tests/utils.test.ts +37 -114
- package/tsconfig.esnext.json +5 -1
- package/tsconfig.json +6 -1
- package/tsconfig.test.json +7 -0
- package/.github/funding.yml +0 -2
- package/.github/workflows/test.yml +0 -32
package/src/index.ts
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
IPageViewPayload,
|
|
12
12
|
FeatureFlagsOptions,
|
|
13
13
|
ExperimentOptions,
|
|
14
|
+
SessionReplayOptions,
|
|
15
|
+
SessionReplayActions,
|
|
16
|
+
defaultSessionReplayActions,
|
|
14
17
|
} from './Lib.js'
|
|
15
18
|
|
|
16
19
|
export let LIB_INSTANCE: Lib | null = null
|
|
@@ -82,6 +85,16 @@ export function trackErrors(options?: ErrorOptions): ErrorActions {
|
|
|
82
85
|
return LIB_INSTANCE.trackErrors(options)
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
export function startSessionReplay(
|
|
89
|
+
options?: SessionReplayOptions,
|
|
90
|
+
): Promise<SessionReplayActions> {
|
|
91
|
+
if (!LIB_INSTANCE) {
|
|
92
|
+
return Promise.resolve(defaultSessionReplayActions)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return LIB_INSTANCE.startSessionReplay(options)
|
|
96
|
+
}
|
|
97
|
+
|
|
85
98
|
/**
|
|
86
99
|
* This function is used to manually track an error event.
|
|
87
100
|
* It's useful if you want to track specific errors in your application.
|
|
@@ -124,17 +137,16 @@ export function pageview(options: IPageviewOptions): void {
|
|
|
124
137
|
|
|
125
138
|
/**
|
|
126
139
|
* Fetches all feature flags for the project.
|
|
127
|
-
* Results are cached for 5 minutes by default.
|
|
140
|
+
* Results are cached for 5 minutes by default and share a cache with experiments.
|
|
128
141
|
*
|
|
129
|
-
* @param options - Options for evaluating feature flags (
|
|
142
|
+
* @param options - Options for evaluating feature flags (`profileId` only).
|
|
130
143
|
* @param forceRefresh - If true, bypasses the cache and fetches fresh flags.
|
|
131
144
|
* @returns A promise that resolves to a record of flag keys to boolean values.
|
|
132
145
|
*
|
|
133
146
|
* @example
|
|
134
147
|
* ```typescript
|
|
135
148
|
* const flags = await getFeatureFlags({
|
|
136
|
-
*
|
|
137
|
-
* attributes: { cc: 'US', dv: 'desktop' }
|
|
149
|
+
* profileId: 'user-123'
|
|
138
150
|
* })
|
|
139
151
|
*
|
|
140
152
|
* if (flags['new-checkout']) {
|
|
@@ -155,13 +167,13 @@ export async function getFeatureFlags(
|
|
|
155
167
|
* Gets the value of a single feature flag.
|
|
156
168
|
*
|
|
157
169
|
* @param key - The feature flag key.
|
|
158
|
-
* @param options - Options for evaluating the feature flag (
|
|
159
|
-
* @param defaultValue -
|
|
170
|
+
* @param options - Options for evaluating the feature flag (`profileId` only).
|
|
171
|
+
* @param defaultValue - Optional default value to return if the flag is not found. Defaults to false.
|
|
160
172
|
* @returns A promise that resolves to the boolean value of the flag.
|
|
161
173
|
*
|
|
162
174
|
* @example
|
|
163
175
|
* ```typescript
|
|
164
|
-
* const isEnabled = await getFeatureFlag('dark-mode', {
|
|
176
|
+
* const isEnabled = await getFeatureFlag('dark-mode', { profileId: 'user-123' })
|
|
165
177
|
*
|
|
166
178
|
* if (isEnabled) {
|
|
167
179
|
* // Enable dark mode
|
|
@@ -179,8 +191,8 @@ export async function getFeatureFlag(
|
|
|
179
191
|
}
|
|
180
192
|
|
|
181
193
|
/**
|
|
182
|
-
* Clears the cached feature flags, forcing a fresh fetch on the next call.
|
|
183
|
-
* Useful when you know the user's
|
|
194
|
+
* Clears the cached feature flags and experiments, forcing a fresh fetch on the next call.
|
|
195
|
+
* Useful when you know the user's profile has changed.
|
|
184
196
|
*/
|
|
185
197
|
export function clearFeatureFlagsCache(): void {
|
|
186
198
|
if (!LIB_INSTANCE) return
|
|
@@ -189,10 +201,10 @@ export function clearFeatureFlagsCache(): void {
|
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
/**
|
|
192
|
-
* Fetches
|
|
204
|
+
* Fetches variant assignments for running A/B test experiments returned by feature flag evaluation.
|
|
193
205
|
* Results are cached for 5 minutes by default (shared cache with feature flags).
|
|
194
206
|
*
|
|
195
|
-
* @param options - Options for evaluating experiments.
|
|
207
|
+
* @param options - Options for evaluating experiments (`profileId` only).
|
|
196
208
|
* @param forceRefresh - If true, bypasses the cache and fetches fresh data.
|
|
197
209
|
* @returns A promise that resolves to a record of experiment IDs to variant keys.
|
|
198
210
|
*
|
|
@@ -223,13 +235,16 @@ export async function getExperiments(
|
|
|
223
235
|
* Gets the variant key for a specific A/B test experiment.
|
|
224
236
|
*
|
|
225
237
|
* @param experimentId - The experiment ID.
|
|
226
|
-
* @param options - Options for evaluating the experiment.
|
|
227
|
-
* @param defaultVariant -
|
|
238
|
+
* @param options - Options for evaluating the experiment (`profileId` only).
|
|
239
|
+
* @param defaultVariant - Optional default variant key to return if the experiment is not found. Defaults to null.
|
|
228
240
|
* @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
|
|
229
241
|
*
|
|
230
242
|
* @example
|
|
231
243
|
* ```typescript
|
|
232
|
-
* const variant = await getExperiment('checkout-redesign-experiment-id')
|
|
244
|
+
* const variant = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' })
|
|
245
|
+
*
|
|
246
|
+
* // Optional fallback variant:
|
|
247
|
+
* const variantWithFallback = await getExperiment('checkout-redesign-experiment-id', undefined, 'control')
|
|
233
248
|
*
|
|
234
249
|
* if (variant === 'new-checkout') {
|
|
235
250
|
* // Show new checkout flow
|
package/src/utils.ts
CHANGED
|
@@ -48,6 +48,28 @@ export const getReferrer = (): string | undefined => {
|
|
|
48
48
|
return document.referrer || undefined
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Returns the URL query string (without the leading `?`) of the current
|
|
53
|
+
* page, or `undefined` if there is none.
|
|
54
|
+
*
|
|
55
|
+
* Falls back to a query string embedded in `location.hash` (e.g. when a
|
|
56
|
+
* hash router uses `/#/path?foo=bar`) so we still capture click IDs in
|
|
57
|
+
* SPA hash-routed setups.
|
|
58
|
+
*/
|
|
59
|
+
export const getQueryString = (): string | undefined => {
|
|
60
|
+
if (location.search && location.search.length > 1) {
|
|
61
|
+
return location.search.slice(1)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hashIndex = location.hash.indexOf('?')
|
|
65
|
+
if (hashIndex > -1) {
|
|
66
|
+
const hashQuery = location.hash.slice(hashIndex + 1)
|
|
67
|
+
if (hashQuery) return hashQuery
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
export const getUTMSource = () => findInSearch(utmSourceRegex)
|
|
52
74
|
|
|
53
75
|
export const getUTMMedium = () => findInSearch(utmMediumRegex) || getGclid()
|
package/tests/errors.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { init, trackError, trackErrors } from '../src/index'
|
|
2
2
|
import { Lib } from '../src/Lib'
|
|
3
|
+
import { setLocation } from './testUtils'
|
|
3
4
|
|
|
4
5
|
jest.mock('../src/Lib', () => {
|
|
5
6
|
const originalModule = jest.requireActual('../src/Lib')
|
|
@@ -71,15 +72,7 @@ describe('Error Tracking', () => {
|
|
|
71
72
|
|
|
72
73
|
libInstance = init(PROJECT_ID, { devMode: true }) as Lib
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
value: {
|
|
76
|
-
hostname: 'example.com',
|
|
77
|
-
pathname: '/test-page',
|
|
78
|
-
hash: '',
|
|
79
|
-
search: '',
|
|
80
|
-
},
|
|
81
|
-
writable: true,
|
|
82
|
-
})
|
|
75
|
+
setLocation({ hostname: 'example.com', pathname: '/test-page' })
|
|
83
76
|
|
|
84
77
|
window.addEventListener = jest.fn()
|
|
85
78
|
window.removeEventListener = jest.fn()
|
package/tests/events.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { init, track } from '../src/index'
|
|
2
2
|
import { Lib } from '../src/Lib'
|
|
3
|
+
import { setLocation } from './testUtils'
|
|
3
4
|
|
|
4
5
|
jest.mock('../src/Lib', () => {
|
|
5
6
|
const originalModule = jest.requireActual('../src/Lib')
|
|
@@ -21,15 +22,7 @@ describe('Custom Event Tracking', () => {
|
|
|
21
22
|
|
|
22
23
|
libInstance = init(PROJECT_ID, { devMode: true }) as Lib
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
value: {
|
|
26
|
-
hostname: 'example.com',
|
|
27
|
-
pathname: '/test-page',
|
|
28
|
-
hash: '',
|
|
29
|
-
search: '',
|
|
30
|
-
},
|
|
31
|
-
writable: true,
|
|
32
|
-
})
|
|
25
|
+
setLocation({ hostname: 'example.com', pathname: '/test-page' })
|
|
33
26
|
})
|
|
34
27
|
|
|
35
28
|
test('track function should track a custom event', async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { init, getExperiment, getExperiments, clearExperimentsCache } from '../src/index'
|
|
2
2
|
import { Lib } from '../src/Lib'
|
|
3
|
+
import { setLocation } from './testUtils'
|
|
3
4
|
|
|
4
5
|
// Mock fetch globally
|
|
5
6
|
const mockFetch = jest.fn()
|
|
@@ -16,15 +17,7 @@ describe('A/B Testing Experiments', () => {
|
|
|
16
17
|
// Reset fetch mock
|
|
17
18
|
mockFetch.mockReset()
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
value: {
|
|
21
|
-
hostname: 'example.com',
|
|
22
|
-
pathname: '/test-page',
|
|
23
|
-
hash: '',
|
|
24
|
-
search: '',
|
|
25
|
-
},
|
|
26
|
-
writable: true,
|
|
27
|
-
})
|
|
20
|
+
setLocation({ hostname: 'example.com', pathname: '/test-page' })
|
|
28
21
|
})
|
|
29
22
|
|
|
30
23
|
describe('getExperiments', () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { init } from '../src/index'
|
|
2
2
|
import { Lib } from '../src/Lib'
|
|
3
|
+
import { setLocation } from './testUtils'
|
|
3
4
|
|
|
4
5
|
jest.mock('../src/Lib', () => {
|
|
5
6
|
const originalModule = jest.requireActual('../src/Lib')
|
|
@@ -19,19 +20,12 @@ describe('Library Initialisation', () => {
|
|
|
19
20
|
|
|
20
21
|
jest.resetModules()
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
value: {
|
|
24
|
-
hostname: 'example.com',
|
|
25
|
-
pathname: '/test-page',
|
|
26
|
-
hash: '',
|
|
27
|
-
search: '',
|
|
28
|
-
},
|
|
29
|
-
writable: true,
|
|
30
|
-
})
|
|
23
|
+
setLocation({ hostname: 'example.com', pathname: '/test-page' })
|
|
31
24
|
|
|
32
25
|
Object.defineProperty(navigator, 'doNotTrack', {
|
|
33
26
|
value: null,
|
|
34
27
|
writable: true,
|
|
28
|
+
configurable: true,
|
|
35
29
|
})
|
|
36
30
|
})
|
|
37
31
|
|
|
@@ -45,15 +39,7 @@ describe('Library Initialisation', () => {
|
|
|
45
39
|
|
|
46
40
|
test('init with devMode should work on localhost', () => {
|
|
47
41
|
// Arrange
|
|
48
|
-
|
|
49
|
-
value: {
|
|
50
|
-
hostname: 'localhost',
|
|
51
|
-
pathname: '/',
|
|
52
|
-
hash: '',
|
|
53
|
-
search: '',
|
|
54
|
-
},
|
|
55
|
-
writable: true,
|
|
56
|
-
})
|
|
42
|
+
setLocation({ hostname: 'localhost', pathname: '/' })
|
|
57
43
|
|
|
58
44
|
// Act
|
|
59
45
|
const instance = init('test-project-id', { devMode: true })
|
|
@@ -68,6 +54,7 @@ describe('Library Initialisation', () => {
|
|
|
68
54
|
Object.defineProperty(navigator, 'doNotTrack', {
|
|
69
55
|
value: '1',
|
|
70
56
|
writable: true,
|
|
57
|
+
configurable: true,
|
|
71
58
|
})
|
|
72
59
|
|
|
73
60
|
// Act
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import JSDOMEnvironment from 'jest-environment-jsdom'
|
|
2
|
+
import type { EnvironmentContext, JestEnvironmentConfig } from '@jest/environment'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extends jest-environment-jsdom to expose a `__setLocation` global so tests
|
|
6
|
+
* can change `window.location` via `JSDOM#reconfigure`. Direct assignment or
|
|
7
|
+
* `Object.defineProperty(window, 'location', ...)` no longer works in jsdom 22+
|
|
8
|
+
* because the property is defined as non-configurable.
|
|
9
|
+
*/
|
|
10
|
+
export default class CustomJSDOMEnvironment extends JSDOMEnvironment {
|
|
11
|
+
constructor(config: JestEnvironmentConfig, context: EnvironmentContext) {
|
|
12
|
+
super(config, context)
|
|
13
|
+
|
|
14
|
+
const dom = (this as any).dom
|
|
15
|
+
|
|
16
|
+
;(this.global as any).__setLocation = (url: string) => {
|
|
17
|
+
dom.reconfigure({ url })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
package/tests/pageview.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { init, pageview, trackViews } from '../src/index'
|
|
2
2
|
import { Lib } from '../src/Lib'
|
|
3
|
+
import { setLocation } from './testUtils'
|
|
3
4
|
|
|
4
5
|
jest.mock('../src/Lib', () => {
|
|
5
6
|
const originalModule = jest.requireActual('../src/Lib')
|
|
@@ -21,19 +22,12 @@ describe('Pageview Tracking', () => {
|
|
|
21
22
|
|
|
22
23
|
libInstance = init(PROJECT_ID, { devMode: true }) as Lib
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
value: {
|
|
26
|
-
hostname: 'example.com',
|
|
27
|
-
pathname: '/test-page',
|
|
28
|
-
hash: '',
|
|
29
|
-
search: '',
|
|
30
|
-
},
|
|
31
|
-
writable: true,
|
|
32
|
-
})
|
|
25
|
+
setLocation({ hostname: 'example.com', pathname: '/test-page' })
|
|
33
26
|
|
|
34
27
|
Object.defineProperty(document, 'referrer', {
|
|
35
28
|
value: 'https://google.com',
|
|
36
29
|
writable: true,
|
|
30
|
+
configurable: true,
|
|
37
31
|
})
|
|
38
32
|
})
|
|
39
33
|
|