swetrix 3.7.1 → 4.0.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/dist/esnext/Lib.d.ts +148 -0
- package/dist/esnext/Lib.js +269 -2
- package/dist/esnext/Lib.js.map +1 -1
- package/dist/esnext/index.d.ts +146 -2
- package/dist/esnext/index.js +176 -0
- package/dist/esnext/index.js.map +1 -1
- package/dist/swetrix.cjs.js +585 -205
- package/dist/swetrix.cjs.js.map +1 -1
- package/dist/swetrix.es5.js +579 -207
- package/dist/swetrix.es5.js.map +1 -1
- package/dist/swetrix.js +1 -1
- package/dist/swetrix.js.map +1 -1
- package/package.json +2 -1
- package/rollup.config.mjs +5 -0
- package/src/Lib.ts +353 -4
- package/src/index.ts +202 -0
- package/tests/experiments.test.ts +330 -0
- package/tsconfig.esnext.json +3 -6
- package/tsconfig.json +4 -8
package/dist/swetrix.cjs.js
CHANGED
|
@@ -1,98 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
-
purpose with or without fee is hereby granted.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
-
***************************************************************************** */
|
|
17
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var __assign = function() {
|
|
21
|
-
__assign = Object.assign || function __assign(t) {
|
|
22
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
23
|
-
s = arguments[i];
|
|
24
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
25
|
-
}
|
|
26
|
-
return t;
|
|
27
|
-
};
|
|
28
|
-
return __assign.apply(this, arguments);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
32
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
33
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
34
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
35
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
36
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
37
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function __generator(thisArg, body) {
|
|
42
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
43
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
44
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
45
|
-
function step(op) {
|
|
46
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
47
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
48
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
49
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
50
|
-
switch (op[0]) {
|
|
51
|
-
case 0: case 1: t = op; break;
|
|
52
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
53
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
54
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
55
|
-
default:
|
|
56
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
57
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
58
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
59
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
60
|
-
if (t[2]) _.ops.pop();
|
|
61
|
-
_.trys.pop(); continue;
|
|
62
|
-
}
|
|
63
|
-
op = body.call(thisArg, _);
|
|
64
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
65
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
70
|
-
var e = new Error(message);
|
|
71
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
var findInSearch = function (exp) {
|
|
75
|
-
var res = location.search.match(exp);
|
|
3
|
+
const findInSearch = (exp) => {
|
|
4
|
+
const res = location.search.match(exp);
|
|
76
5
|
return (res && res[2]) || undefined;
|
|
77
6
|
};
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
7
|
+
const utmSourceRegex = /[?&](ref|source|utm_source)=([^?&]+)/;
|
|
8
|
+
const utmCampaignRegex = /[?&](utm_campaign)=([^?&]+)/;
|
|
9
|
+
const utmMediumRegex = /[?&](utm_medium)=([^?&]+)/;
|
|
10
|
+
const utmTermRegex = /[?&](utm_term)=([^?&]+)/;
|
|
11
|
+
const utmContentRegex = /[?&](utm_content)=([^?&]+)/;
|
|
12
|
+
const isInBrowser = () => {
|
|
84
13
|
return typeof window !== 'undefined';
|
|
85
14
|
};
|
|
86
|
-
|
|
15
|
+
const isLocalhost = () => {
|
|
87
16
|
return (location === null || location === void 0 ? void 0 : location.hostname) === 'localhost' || (location === null || location === void 0 ? void 0 : location.hostname) === '127.0.0.1' || (location === null || location === void 0 ? void 0 : location.hostname) === '';
|
|
88
17
|
};
|
|
89
|
-
|
|
18
|
+
const isAutomated = () => {
|
|
90
19
|
return navigator === null || navigator === void 0 ? void 0 : navigator.webdriver;
|
|
91
20
|
};
|
|
92
|
-
|
|
21
|
+
const getLocale = () => {
|
|
93
22
|
return typeof navigator.languages !== 'undefined' ? navigator.languages[0] : navigator.language;
|
|
94
23
|
};
|
|
95
|
-
|
|
24
|
+
const getTimezone = () => {
|
|
96
25
|
try {
|
|
97
26
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
98
27
|
}
|
|
@@ -100,14 +29,14 @@ var getTimezone = function () {
|
|
|
100
29
|
return;
|
|
101
30
|
}
|
|
102
31
|
};
|
|
103
|
-
|
|
32
|
+
const getReferrer = () => {
|
|
104
33
|
return document.referrer || undefined;
|
|
105
34
|
};
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
35
|
+
const getUTMSource = () => findInSearch(utmSourceRegex);
|
|
36
|
+
const getUTMMedium = () => findInSearch(utmMediumRegex);
|
|
37
|
+
const getUTMCampaign = () => findInSearch(utmCampaignRegex);
|
|
38
|
+
const getUTMTerm = () => findInSearch(utmTermRegex);
|
|
39
|
+
const getUTMContent = () => findInSearch(utmContentRegex);
|
|
111
40
|
/**
|
|
112
41
|
* Function used to track the current page (path) of the application.
|
|
113
42
|
* Will work in cases where the path looks like:
|
|
@@ -122,27 +51,30 @@ var getUTMContent = function () { return findInSearch(utmContentRegex); };
|
|
|
122
51
|
* @param options.search - Whether to trigger on search change.
|
|
123
52
|
* @returns The path of the current page.
|
|
124
53
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
54
|
+
const getPath = (options) => {
|
|
55
|
+
let result = location.pathname || '';
|
|
127
56
|
if (options.hash) {
|
|
128
|
-
|
|
129
|
-
|
|
57
|
+
const hashIndex = location.hash.indexOf('?');
|
|
58
|
+
const hashString = hashIndex > -1 ? location.hash.substring(0, hashIndex) : location.hash;
|
|
130
59
|
result += hashString;
|
|
131
60
|
}
|
|
132
61
|
if (options.search) {
|
|
133
|
-
|
|
134
|
-
|
|
62
|
+
const hashIndex = location.hash.indexOf('?');
|
|
63
|
+
const searchString = location.search || (hashIndex > -1 ? location.hash.substring(hashIndex) : '');
|
|
135
64
|
result += searchString;
|
|
136
65
|
}
|
|
137
66
|
return result;
|
|
138
67
|
};
|
|
139
68
|
|
|
140
|
-
|
|
141
|
-
stop
|
|
69
|
+
const defaultActions = {
|
|
70
|
+
stop() { },
|
|
142
71
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
72
|
+
const DEFAULT_API_HOST = 'https://api.swetrix.com/log';
|
|
73
|
+
const DEFAULT_API_BASE = 'https://api.swetrix.com';
|
|
74
|
+
// Default cache duration: 5 minutes
|
|
75
|
+
const DEFAULT_CACHE_DURATION = 5 * 60 * 1000;
|
|
76
|
+
class Lib {
|
|
77
|
+
constructor(projectID, options) {
|
|
146
78
|
this.projectID = projectID;
|
|
147
79
|
this.options = options;
|
|
148
80
|
this.pageData = null;
|
|
@@ -151,13 +83,14 @@ var Lib = /** @class */ (function () {
|
|
|
151
83
|
this.perfStatsCollected = false;
|
|
152
84
|
this.activePage = null;
|
|
153
85
|
this.errorListenerExists = false;
|
|
86
|
+
this.cachedData = null;
|
|
154
87
|
this.trackPathChange = this.trackPathChange.bind(this);
|
|
155
88
|
this.heartbeat = this.heartbeat.bind(this);
|
|
156
89
|
this.captureError = this.captureError.bind(this);
|
|
157
90
|
}
|
|
158
|
-
|
|
91
|
+
captureError(event) {
|
|
159
92
|
var _a, _b, _c, _d;
|
|
160
|
-
if (typeof ((_a = this.errorsOptions) === null || _a === void 0 ? void 0 : _a.sampleRate) === 'number' && this.errorsOptions.sampleRate
|
|
93
|
+
if (typeof ((_a = this.errorsOptions) === null || _a === void 0 ? void 0 : _a.sampleRate) === 'number' && this.errorsOptions.sampleRate >= Math.random()) {
|
|
161
94
|
return;
|
|
162
95
|
}
|
|
163
96
|
this.submitError({
|
|
@@ -176,9 +109,8 @@ var Lib = /** @class */ (function () {
|
|
|
176
109
|
// Stack trace of the error, if available.
|
|
177
110
|
stackTrace: (_d = event.error) === null || _d === void 0 ? void 0 : _d.stack,
|
|
178
111
|
}, true);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
var _this = this;
|
|
112
|
+
}
|
|
113
|
+
trackErrors(options) {
|
|
182
114
|
if (this.errorListenerExists || !this.canTrack()) {
|
|
183
115
|
return defaultActions;
|
|
184
116
|
}
|
|
@@ -186,23 +118,29 @@ var Lib = /** @class */ (function () {
|
|
|
186
118
|
window.addEventListener('error', this.captureError);
|
|
187
119
|
this.errorListenerExists = true;
|
|
188
120
|
return {
|
|
189
|
-
stop:
|
|
190
|
-
window.removeEventListener('error',
|
|
121
|
+
stop: () => {
|
|
122
|
+
window.removeEventListener('error', this.captureError);
|
|
123
|
+
this.errorListenerExists = false;
|
|
191
124
|
},
|
|
192
125
|
};
|
|
193
|
-
}
|
|
194
|
-
|
|
126
|
+
}
|
|
127
|
+
submitError(payload, evokeCallback) {
|
|
195
128
|
var _a, _b, _c;
|
|
196
|
-
|
|
129
|
+
const privateData = {
|
|
197
130
|
pid: this.projectID,
|
|
198
131
|
};
|
|
199
|
-
|
|
132
|
+
const errorPayload = {
|
|
133
|
+
pg: this.activePage ||
|
|
200
134
|
getPath({
|
|
201
135
|
hash: (_a = this.pageViewsOptions) === null || _a === void 0 ? void 0 : _a.hash,
|
|
202
136
|
search: (_b = this.pageViewsOptions) === null || _b === void 0 ? void 0 : _b.search,
|
|
203
|
-
}),
|
|
137
|
+
}),
|
|
138
|
+
lc: getLocale(),
|
|
139
|
+
tz: getTimezone(),
|
|
140
|
+
...payload,
|
|
141
|
+
};
|
|
204
142
|
if (evokeCallback && ((_c = this.errorsOptions) === null || _c === void 0 ? void 0 : _c.callback)) {
|
|
205
|
-
|
|
143
|
+
const callbackResult = this.errorsOptions.callback(errorPayload);
|
|
206
144
|
if (callbackResult === false) {
|
|
207
145
|
return;
|
|
208
146
|
}
|
|
@@ -212,26 +150,33 @@ var Lib = /** @class */ (function () {
|
|
|
212
150
|
}
|
|
213
151
|
Object.assign(errorPayload, privateData);
|
|
214
152
|
this.sendRequest('error', errorPayload);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
153
|
+
}
|
|
154
|
+
async track(event) {
|
|
155
|
+
var _a, _b, _c, _d;
|
|
156
|
+
if (!this.canTrack()) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const data = {
|
|
160
|
+
...event,
|
|
161
|
+
pid: this.projectID,
|
|
162
|
+
pg: this.activePage ||
|
|
163
|
+
getPath({
|
|
164
|
+
hash: (_a = this.pageViewsOptions) === null || _a === void 0 ? void 0 : _a.hash,
|
|
165
|
+
search: (_b = this.pageViewsOptions) === null || _b === void 0 ? void 0 : _b.search,
|
|
166
|
+
}),
|
|
167
|
+
lc: getLocale(),
|
|
168
|
+
tz: getTimezone(),
|
|
169
|
+
ref: getReferrer(),
|
|
170
|
+
so: getUTMSource(),
|
|
171
|
+
me: getUTMMedium(),
|
|
172
|
+
ca: getUTMCampaign(),
|
|
173
|
+
te: getUTMTerm(),
|
|
174
|
+
co: getUTMContent(),
|
|
175
|
+
profileId: (_c = event.profileId) !== null && _c !== void 0 ? _c : (_d = this.options) === null || _d === void 0 ? void 0 : _d.profileId,
|
|
176
|
+
};
|
|
177
|
+
await this.sendRequest('custom', data);
|
|
178
|
+
}
|
|
179
|
+
trackPageViews(options) {
|
|
235
180
|
if (!this.canTrack()) {
|
|
236
181
|
return defaultActions;
|
|
237
182
|
}
|
|
@@ -239,20 +184,20 @@ var Lib = /** @class */ (function () {
|
|
|
239
184
|
return this.pageData.actions;
|
|
240
185
|
}
|
|
241
186
|
this.pageViewsOptions = options;
|
|
242
|
-
|
|
187
|
+
let interval;
|
|
243
188
|
if (!(options === null || options === void 0 ? void 0 : options.unique)) {
|
|
244
189
|
interval = setInterval(this.trackPathChange, 2000);
|
|
245
190
|
}
|
|
246
191
|
setTimeout(this.heartbeat, 3000);
|
|
247
|
-
|
|
248
|
-
|
|
192
|
+
const hbInterval = setInterval(this.heartbeat, 28000);
|
|
193
|
+
const path = getPath({
|
|
249
194
|
hash: options === null || options === void 0 ? void 0 : options.hash,
|
|
250
195
|
search: options === null || options === void 0 ? void 0 : options.search,
|
|
251
196
|
});
|
|
252
197
|
this.pageData = {
|
|
253
|
-
path
|
|
198
|
+
path,
|
|
254
199
|
actions: {
|
|
255
|
-
stop:
|
|
200
|
+
stop: () => {
|
|
256
201
|
clearInterval(interval);
|
|
257
202
|
clearInterval(hbInterval);
|
|
258
203
|
},
|
|
@@ -260,13 +205,13 @@ var Lib = /** @class */ (function () {
|
|
|
260
205
|
};
|
|
261
206
|
this.trackPage(path, options === null || options === void 0 ? void 0 : options.unique);
|
|
262
207
|
return this.pageData.actions;
|
|
263
|
-
}
|
|
264
|
-
|
|
208
|
+
}
|
|
209
|
+
getPerformanceStats() {
|
|
265
210
|
var _a;
|
|
266
211
|
if (!this.canTrack() || this.perfStatsCollected || !((_a = window.performance) === null || _a === void 0 ? void 0 : _a.getEntriesByType)) {
|
|
267
212
|
return {};
|
|
268
213
|
}
|
|
269
|
-
|
|
214
|
+
const perf = window.performance.getEntriesByType('navigation')[0];
|
|
270
215
|
if (!perf) {
|
|
271
216
|
return {};
|
|
272
217
|
}
|
|
@@ -286,50 +231,323 @@ var Lib = /** @class */ (function () {
|
|
|
286
231
|
// Backend
|
|
287
232
|
ttfb: perf.responseStart - perf.requestStart,
|
|
288
233
|
};
|
|
289
|
-
}
|
|
290
|
-
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Fetches all feature flags and experiments for the project.
|
|
237
|
+
* Results are cached for 5 minutes by default.
|
|
238
|
+
*
|
|
239
|
+
* @param options - Options for evaluating feature flags.
|
|
240
|
+
* @param forceRefresh - If true, bypasses the cache and fetches fresh data.
|
|
241
|
+
* @returns A promise that resolves to a record of flag keys to boolean values.
|
|
242
|
+
*/
|
|
243
|
+
async getFeatureFlags(options, forceRefresh) {
|
|
244
|
+
var _a, _b, _c, _d;
|
|
245
|
+
if (!isInBrowser()) {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
const requestedProfileId = (_a = options === null || options === void 0 ? void 0 : options.profileId) !== null && _a !== void 0 ? _a : (_b = this.options) === null || _b === void 0 ? void 0 : _b.profileId;
|
|
249
|
+
// Check cache first - must match profileId and not be expired
|
|
250
|
+
if (!forceRefresh && this.cachedData) {
|
|
251
|
+
const now = Date.now();
|
|
252
|
+
const isSameProfile = this.cachedData.profileId === requestedProfileId;
|
|
253
|
+
if (isSameProfile && now - this.cachedData.timestamp < DEFAULT_CACHE_DURATION) {
|
|
254
|
+
return this.cachedData.flags;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
await this.fetchFlagsAndExperiments(options);
|
|
259
|
+
return ((_c = this.cachedData) === null || _c === void 0 ? void 0 : _c.flags) || {};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.warn('[Swetrix] Error fetching feature flags:', error);
|
|
263
|
+
return ((_d = this.cachedData) === null || _d === void 0 ? void 0 : _d.flags) || {};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Internal method to fetch both feature flags and experiments from the API.
|
|
268
|
+
*/
|
|
269
|
+
async fetchFlagsAndExperiments(options) {
|
|
270
|
+
var _a, _b, _c, _d;
|
|
271
|
+
const apiBase = this.getApiBase();
|
|
272
|
+
const body = {
|
|
273
|
+
pid: this.projectID,
|
|
274
|
+
};
|
|
275
|
+
// Use profileId from options, or fall back to global profileId
|
|
276
|
+
const profileId = (_a = options === null || options === void 0 ? void 0 : options.profileId) !== null && _a !== void 0 ? _a : (_b = this.options) === null || _b === void 0 ? void 0 : _b.profileId;
|
|
277
|
+
if (profileId) {
|
|
278
|
+
body.profileId = profileId;
|
|
279
|
+
}
|
|
280
|
+
const response = await fetch(`${apiBase}/feature-flag/evaluate`, {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
headers: {
|
|
283
|
+
'Content-Type': 'application/json',
|
|
284
|
+
},
|
|
285
|
+
body: JSON.stringify(body),
|
|
286
|
+
});
|
|
287
|
+
if (!response.ok) {
|
|
288
|
+
console.warn('[Swetrix] Failed to fetch feature flags and experiments:', response.status);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const data = (await response.json());
|
|
292
|
+
// Use profileId from options, or fall back to global profileId
|
|
293
|
+
const cachedProfileId = (_c = options === null || options === void 0 ? void 0 : options.profileId) !== null && _c !== void 0 ? _c : (_d = this.options) === null || _d === void 0 ? void 0 : _d.profileId;
|
|
294
|
+
// Update cache with both flags and experiments
|
|
295
|
+
this.cachedData = {
|
|
296
|
+
flags: data.flags || {},
|
|
297
|
+
experiments: data.experiments || {},
|
|
298
|
+
timestamp: Date.now(),
|
|
299
|
+
profileId: cachedProfileId,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Gets the value of a single feature flag.
|
|
304
|
+
*
|
|
305
|
+
* @param key - The feature flag key.
|
|
306
|
+
* @param options - Options for evaluating the feature flag.
|
|
307
|
+
* @param defaultValue - Default value to return if the flag is not found. Defaults to false.
|
|
308
|
+
* @returns A promise that resolves to the boolean value of the flag.
|
|
309
|
+
*/
|
|
310
|
+
async getFeatureFlag(key, options, defaultValue = false) {
|
|
311
|
+
var _a;
|
|
312
|
+
const flags = await this.getFeatureFlags(options);
|
|
313
|
+
return (_a = flags[key]) !== null && _a !== void 0 ? _a : defaultValue;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Clears the cached feature flags and experiments, forcing a fresh fetch on the next call.
|
|
317
|
+
*/
|
|
318
|
+
clearFeatureFlagsCache() {
|
|
319
|
+
this.cachedData = null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Fetches all A/B test experiments for the project.
|
|
323
|
+
* Results are cached for 5 minutes by default (shared cache with feature flags).
|
|
324
|
+
*
|
|
325
|
+
* @param options - Options for evaluating experiments.
|
|
326
|
+
* @param forceRefresh - If true, bypasses the cache and fetches fresh data.
|
|
327
|
+
* @returns A promise that resolves to a record of experiment IDs to variant keys.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* const experiments = await getExperiments()
|
|
332
|
+
* // experiments = { 'exp-123': 'variant-a', 'exp-456': 'control' }
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
async getExperiments(options, forceRefresh) {
|
|
336
|
+
var _a, _b, _c, _d;
|
|
337
|
+
if (!isInBrowser()) {
|
|
338
|
+
return {};
|
|
339
|
+
}
|
|
340
|
+
const requestedProfileId = (_a = options === null || options === void 0 ? void 0 : options.profileId) !== null && _a !== void 0 ? _a : (_b = this.options) === null || _b === void 0 ? void 0 : _b.profileId;
|
|
341
|
+
// Check cache first - must match profileId and not be expired
|
|
342
|
+
if (!forceRefresh && this.cachedData) {
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
const isSameProfile = this.cachedData.profileId === requestedProfileId;
|
|
345
|
+
if (isSameProfile && now - this.cachedData.timestamp < DEFAULT_CACHE_DURATION) {
|
|
346
|
+
return this.cachedData.experiments;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
await this.fetchFlagsAndExperiments(options);
|
|
351
|
+
return ((_c = this.cachedData) === null || _c === void 0 ? void 0 : _c.experiments) || {};
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
console.warn('[Swetrix] Error fetching experiments:', error);
|
|
355
|
+
return ((_d = this.cachedData) === null || _d === void 0 ? void 0 : _d.experiments) || {};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Gets the variant key for a specific A/B test experiment.
|
|
360
|
+
*
|
|
361
|
+
* @param experimentId - The experiment ID.
|
|
362
|
+
* @param options - Options for evaluating the experiment.
|
|
363
|
+
* @param defaultVariant - Default variant key to return if the experiment is not found. Defaults to null.
|
|
364
|
+
* @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```typescript
|
|
368
|
+
* const variant = await getExperiment('checkout-redesign')
|
|
369
|
+
*
|
|
370
|
+
* if (variant === 'new-checkout') {
|
|
371
|
+
* // Show new checkout flow
|
|
372
|
+
* } else {
|
|
373
|
+
* // Show control (original) checkout
|
|
374
|
+
* }
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
async getExperiment(experimentId, options, defaultVariant = null) {
|
|
378
|
+
var _a;
|
|
379
|
+
const experiments = await this.getExperiments(options);
|
|
380
|
+
return (_a = experiments[experimentId]) !== null && _a !== void 0 ? _a : defaultVariant;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Clears the cached experiments (alias for clearFeatureFlagsCache since they share the same cache).
|
|
384
|
+
*/
|
|
385
|
+
clearExperimentsCache() {
|
|
386
|
+
this.cachedData = null;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Gets the anonymous profile ID for the current visitor.
|
|
390
|
+
* If profileId was set via init options, returns that.
|
|
391
|
+
* Otherwise, requests server to generate one from IP/UA hash.
|
|
392
|
+
*
|
|
393
|
+
* This ID can be used for revenue attribution with payment providers.
|
|
394
|
+
*
|
|
395
|
+
* @returns A promise that resolves to the profile ID string, or null on error.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* const profileId = await swetrix.getProfileId()
|
|
400
|
+
*
|
|
401
|
+
* // Pass to Paddle Checkout for revenue attribution
|
|
402
|
+
* Paddle.Checkout.open({
|
|
403
|
+
* items: [{ priceId: 'pri_01234567890', quantity: 1 }],
|
|
404
|
+
* customData: {
|
|
405
|
+
* swetrix_profile_id: profileId,
|
|
406
|
+
* swetrix_session_id: await swetrix.getSessionId()
|
|
407
|
+
* }
|
|
408
|
+
* })
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
async getProfileId() {
|
|
412
|
+
var _a;
|
|
413
|
+
// If profileId is already set in options, return it
|
|
414
|
+
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.profileId) {
|
|
415
|
+
return this.options.profileId;
|
|
416
|
+
}
|
|
417
|
+
if (!isInBrowser()) {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
const apiBase = this.getApiBase();
|
|
422
|
+
const response = await fetch(`${apiBase}/log/profile-id`, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
headers: {
|
|
425
|
+
'Content-Type': 'application/json',
|
|
426
|
+
},
|
|
427
|
+
body: JSON.stringify({ pid: this.projectID }),
|
|
428
|
+
});
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
const data = (await response.json());
|
|
433
|
+
return data.profileId;
|
|
434
|
+
}
|
|
435
|
+
catch (_b) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Gets the current session ID for the visitor.
|
|
441
|
+
* Session IDs are generated server-side based on IP and user agent.
|
|
442
|
+
*
|
|
443
|
+
* This ID can be used for revenue attribution with payment providers.
|
|
444
|
+
*
|
|
445
|
+
* @returns A promise that resolves to the session ID string, or null on error.
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* const sessionId = await swetrix.getSessionId()
|
|
450
|
+
*
|
|
451
|
+
* // Pass to Paddle Checkout for revenue attribution
|
|
452
|
+
* Paddle.Checkout.open({
|
|
453
|
+
* items: [{ priceId: 'pri_01234567890', quantity: 1 }],
|
|
454
|
+
* customData: {
|
|
455
|
+
* swetrix_profile_id: await swetrix.getProfileId(),
|
|
456
|
+
* swetrix_session_id: sessionId
|
|
457
|
+
* }
|
|
458
|
+
* })
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
async getSessionId() {
|
|
462
|
+
if (!isInBrowser()) {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const apiBase = this.getApiBase();
|
|
467
|
+
const response = await fetch(`${apiBase}/log/session-id`, {
|
|
468
|
+
method: 'POST',
|
|
469
|
+
headers: {
|
|
470
|
+
'Content-Type': 'application/json',
|
|
471
|
+
},
|
|
472
|
+
body: JSON.stringify({ pid: this.projectID }),
|
|
473
|
+
});
|
|
474
|
+
if (!response.ok) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
const data = (await response.json());
|
|
478
|
+
return data.sessionId;
|
|
479
|
+
}
|
|
480
|
+
catch (_a) {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Gets the API base URL (without /log suffix).
|
|
486
|
+
*/
|
|
487
|
+
getApiBase() {
|
|
291
488
|
var _a;
|
|
489
|
+
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.apiURL) {
|
|
490
|
+
// Remove trailing /log if present
|
|
491
|
+
return this.options.apiURL.replace(/\/log\/?$/, '');
|
|
492
|
+
}
|
|
493
|
+
return DEFAULT_API_BASE;
|
|
494
|
+
}
|
|
495
|
+
heartbeat() {
|
|
496
|
+
var _a, _b;
|
|
292
497
|
if (!((_a = this.pageViewsOptions) === null || _a === void 0 ? void 0 : _a.heartbeatOnBackground) && document.visibilityState === 'hidden') {
|
|
293
498
|
return;
|
|
294
499
|
}
|
|
295
|
-
|
|
500
|
+
const data = {
|
|
296
501
|
pid: this.projectID,
|
|
297
502
|
};
|
|
503
|
+
if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.profileId) {
|
|
504
|
+
data.profileId = this.options.profileId;
|
|
505
|
+
}
|
|
298
506
|
this.sendRequest('hb', data);
|
|
299
|
-
}
|
|
507
|
+
}
|
|
300
508
|
// Tracking path changes. If path changes -> calling this.trackPage method
|
|
301
|
-
|
|
509
|
+
trackPathChange() {
|
|
302
510
|
var _a, _b;
|
|
303
511
|
if (!this.pageData)
|
|
304
512
|
return;
|
|
305
|
-
|
|
513
|
+
const newPath = getPath({
|
|
306
514
|
hash: (_a = this.pageViewsOptions) === null || _a === void 0 ? void 0 : _a.hash,
|
|
307
515
|
search: (_b = this.pageViewsOptions) === null || _b === void 0 ? void 0 : _b.search,
|
|
308
516
|
});
|
|
309
|
-
|
|
517
|
+
const { path } = this.pageData;
|
|
310
518
|
if (path !== newPath) {
|
|
311
519
|
this.trackPage(newPath, false);
|
|
312
520
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (unique === void 0) { unique = false; }
|
|
521
|
+
}
|
|
522
|
+
trackPage(pg, unique = false) {
|
|
316
523
|
if (!this.pageData)
|
|
317
524
|
return;
|
|
318
525
|
this.pageData.path = pg;
|
|
319
|
-
|
|
526
|
+
const perf = this.getPerformanceStats();
|
|
320
527
|
this.activePage = pg;
|
|
321
|
-
this.submitPageView({ pg
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
var _a;
|
|
325
|
-
|
|
528
|
+
this.submitPageView({ pg }, unique, perf, true);
|
|
529
|
+
}
|
|
530
|
+
submitPageView(payload, unique, perf, evokeCallback) {
|
|
531
|
+
var _a, _b;
|
|
532
|
+
const privateData = {
|
|
326
533
|
pid: this.projectID,
|
|
327
|
-
perf
|
|
328
|
-
unique
|
|
534
|
+
perf,
|
|
535
|
+
unique,
|
|
329
536
|
};
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
537
|
+
const pvPayload = {
|
|
538
|
+
lc: getLocale(),
|
|
539
|
+
tz: getTimezone(),
|
|
540
|
+
ref: getReferrer(),
|
|
541
|
+
so: getUTMSource(),
|
|
542
|
+
me: getUTMMedium(),
|
|
543
|
+
ca: getUTMCampaign(),
|
|
544
|
+
te: getUTMTerm(),
|
|
545
|
+
co: getUTMContent(),
|
|
546
|
+
profileId: (_a = this.options) === null || _a === void 0 ? void 0 : _a.profileId,
|
|
547
|
+
...payload,
|
|
548
|
+
};
|
|
549
|
+
if (evokeCallback && ((_b = this.pageViewsOptions) === null || _b === void 0 ? void 0 : _b.callback)) {
|
|
550
|
+
const callbackResult = this.pageViewsOptions.callback(pvPayload);
|
|
333
551
|
if (callbackResult === false) {
|
|
334
552
|
return;
|
|
335
553
|
}
|
|
@@ -339,8 +557,8 @@ var Lib = /** @class */ (function () {
|
|
|
339
557
|
}
|
|
340
558
|
Object.assign(pvPayload, privateData);
|
|
341
559
|
this.sendRequest('', pvPayload);
|
|
342
|
-
}
|
|
343
|
-
|
|
560
|
+
}
|
|
561
|
+
canTrack() {
|
|
344
562
|
var _a, _b, _c, _d;
|
|
345
563
|
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.disabled) ||
|
|
346
564
|
!isInBrowser() ||
|
|
@@ -350,31 +568,19 @@ var Lib = /** @class */ (function () {
|
|
|
350
568
|
return false;
|
|
351
569
|
}
|
|
352
570
|
return true;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
method: 'POST',
|
|
364
|
-
headers: {
|
|
365
|
-
'Content-Type': 'application/json',
|
|
366
|
-
},
|
|
367
|
-
body: JSON.stringify(body),
|
|
368
|
-
})];
|
|
369
|
-
case 1:
|
|
370
|
-
_b.sent();
|
|
371
|
-
return [2 /*return*/];
|
|
372
|
-
}
|
|
373
|
-
});
|
|
571
|
+
}
|
|
572
|
+
async sendRequest(path, body) {
|
|
573
|
+
var _a;
|
|
574
|
+
const host = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.apiURL) || DEFAULT_API_HOST;
|
|
575
|
+
await fetch(`${host}/${path}`, {
|
|
576
|
+
method: 'POST',
|
|
577
|
+
headers: {
|
|
578
|
+
'Content-Type': 'application/json',
|
|
579
|
+
},
|
|
580
|
+
body: JSON.stringify(body),
|
|
374
581
|
});
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
}());
|
|
582
|
+
}
|
|
583
|
+
}
|
|
378
584
|
|
|
379
585
|
exports.LIB_INSTANCE = null;
|
|
380
586
|
/**
|
|
@@ -397,20 +603,10 @@ function init(pid, options) {
|
|
|
397
603
|
*
|
|
398
604
|
* @param {TrackEventOptions} event The options related to the custom event.
|
|
399
605
|
*/
|
|
400
|
-
function track(event) {
|
|
401
|
-
|
|
402
|
-
return
|
|
403
|
-
|
|
404
|
-
case 0:
|
|
405
|
-
if (!exports.LIB_INSTANCE)
|
|
406
|
-
return [2 /*return*/];
|
|
407
|
-
return [4 /*yield*/, exports.LIB_INSTANCE.track(event)];
|
|
408
|
-
case 1:
|
|
409
|
-
_a.sent();
|
|
410
|
-
return [2 /*return*/];
|
|
411
|
-
}
|
|
412
|
-
});
|
|
413
|
-
});
|
|
606
|
+
async function track(event) {
|
|
607
|
+
if (!exports.LIB_INSTANCE)
|
|
608
|
+
return;
|
|
609
|
+
await exports.LIB_INSTANCE.track(event);
|
|
414
610
|
}
|
|
415
611
|
/**
|
|
416
612
|
* With this function you are able to automatically track pageviews across your application.
|
|
@@ -419,7 +615,7 @@ function track(event) {
|
|
|
419
615
|
* @returns {PageActions} The actions related to the tracking. Used to stop tracking pages.
|
|
420
616
|
*/
|
|
421
617
|
function trackViews(options) {
|
|
422
|
-
return new Promise(
|
|
618
|
+
return new Promise((resolve) => {
|
|
423
619
|
if (!exports.LIB_INSTANCE) {
|
|
424
620
|
resolve(defaultActions);
|
|
425
621
|
return;
|
|
@@ -429,7 +625,7 @@ function trackViews(options) {
|
|
|
429
625
|
resolve(exports.LIB_INSTANCE.trackPageViews(options));
|
|
430
626
|
}
|
|
431
627
|
else {
|
|
432
|
-
window.addEventListener('load',
|
|
628
|
+
window.addEventListener('load', () => {
|
|
433
629
|
// @ts-ignore
|
|
434
630
|
resolve(exports.LIB_INSTANCE.trackPageViews(options));
|
|
435
631
|
});
|
|
@@ -473,14 +669,198 @@ function trackError(payload) {
|
|
|
473
669
|
function trackPageview(pg, _prev, unique) {
|
|
474
670
|
if (!exports.LIB_INSTANCE)
|
|
475
671
|
return;
|
|
476
|
-
exports.LIB_INSTANCE.submitPageView({ pg
|
|
672
|
+
exports.LIB_INSTANCE.submitPageView({ pg }, Boolean(unique), {});
|
|
477
673
|
}
|
|
478
674
|
function pageview(options) {
|
|
479
675
|
if (!exports.LIB_INSTANCE)
|
|
480
676
|
return;
|
|
481
677
|
exports.LIB_INSTANCE.submitPageView(options.payload, Boolean(options.unique), {});
|
|
482
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* Fetches all feature flags for the project.
|
|
681
|
+
* Results are cached for 5 minutes by default.
|
|
682
|
+
*
|
|
683
|
+
* @param options - Options for evaluating feature flags (visitorId, attributes).
|
|
684
|
+
* @param forceRefresh - If true, bypasses the cache and fetches fresh flags.
|
|
685
|
+
* @returns A promise that resolves to a record of flag keys to boolean values.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```typescript
|
|
689
|
+
* const flags = await getFeatureFlags({
|
|
690
|
+
* visitorId: 'user-123',
|
|
691
|
+
* attributes: { cc: 'US', dv: 'desktop' }
|
|
692
|
+
* })
|
|
693
|
+
*
|
|
694
|
+
* if (flags['new-checkout']) {
|
|
695
|
+
* // Show new checkout flow
|
|
696
|
+
* }
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
async function getFeatureFlags(options, forceRefresh) {
|
|
700
|
+
if (!exports.LIB_INSTANCE)
|
|
701
|
+
return {};
|
|
702
|
+
return exports.LIB_INSTANCE.getFeatureFlags(options, forceRefresh);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Gets the value of a single feature flag.
|
|
706
|
+
*
|
|
707
|
+
* @param key - The feature flag key.
|
|
708
|
+
* @param options - Options for evaluating the feature flag (visitorId, attributes).
|
|
709
|
+
* @param defaultValue - Default value to return if the flag is not found. Defaults to false.
|
|
710
|
+
* @returns A promise that resolves to the boolean value of the flag.
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* ```typescript
|
|
714
|
+
* const isEnabled = await getFeatureFlag('dark-mode', { visitorId: 'user-123' })
|
|
715
|
+
*
|
|
716
|
+
* if (isEnabled) {
|
|
717
|
+
* // Enable dark mode
|
|
718
|
+
* }
|
|
719
|
+
* ```
|
|
720
|
+
*/
|
|
721
|
+
async function getFeatureFlag(key, options, defaultValue = false) {
|
|
722
|
+
if (!exports.LIB_INSTANCE)
|
|
723
|
+
return defaultValue;
|
|
724
|
+
return exports.LIB_INSTANCE.getFeatureFlag(key, options, defaultValue);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Clears the cached feature flags, forcing a fresh fetch on the next call.
|
|
728
|
+
* Useful when you know the user's context has changed significantly.
|
|
729
|
+
*/
|
|
730
|
+
function clearFeatureFlagsCache() {
|
|
731
|
+
if (!exports.LIB_INSTANCE)
|
|
732
|
+
return;
|
|
733
|
+
exports.LIB_INSTANCE.clearFeatureFlagsCache();
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Fetches all A/B test experiments for the project.
|
|
737
|
+
* Results are cached for 5 minutes by default (shared cache with feature flags).
|
|
738
|
+
*
|
|
739
|
+
* @param options - Options for evaluating experiments.
|
|
740
|
+
* @param forceRefresh - If true, bypasses the cache and fetches fresh data.
|
|
741
|
+
* @returns A promise that resolves to a record of experiment IDs to variant keys.
|
|
742
|
+
*
|
|
743
|
+
* @example
|
|
744
|
+
* ```typescript
|
|
745
|
+
* const experiments = await getExperiments()
|
|
746
|
+
* // experiments = { 'exp-123': 'variant-a', 'exp-456': 'control' }
|
|
747
|
+
*
|
|
748
|
+
* // Use the assigned variant
|
|
749
|
+
* const checkoutVariant = experiments['checkout-experiment-id']
|
|
750
|
+
* if (checkoutVariant === 'new-checkout') {
|
|
751
|
+
* showNewCheckout()
|
|
752
|
+
* } else {
|
|
753
|
+
* showOriginalCheckout()
|
|
754
|
+
* }
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
757
|
+
async function getExperiments(options, forceRefresh) {
|
|
758
|
+
if (!exports.LIB_INSTANCE)
|
|
759
|
+
return {};
|
|
760
|
+
return exports.LIB_INSTANCE.getExperiments(options, forceRefresh);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Gets the variant key for a specific A/B test experiment.
|
|
764
|
+
*
|
|
765
|
+
* @param experimentId - The experiment ID.
|
|
766
|
+
* @param options - Options for evaluating the experiment.
|
|
767
|
+
* @param defaultVariant - Default variant key to return if the experiment is not found. Defaults to null.
|
|
768
|
+
* @returns A promise that resolves to the variant key assigned to this user, or defaultVariant if not found.
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* ```typescript
|
|
772
|
+
* const variant = await getExperiment('checkout-redesign-experiment-id')
|
|
773
|
+
*
|
|
774
|
+
* if (variant === 'new-checkout') {
|
|
775
|
+
* // Show new checkout flow
|
|
776
|
+
* showNewCheckout()
|
|
777
|
+
* } else if (variant === 'control') {
|
|
778
|
+
* // Show original checkout (control group)
|
|
779
|
+
* showOriginalCheckout()
|
|
780
|
+
* } else {
|
|
781
|
+
* // Experiment not running or user not included
|
|
782
|
+
* showOriginalCheckout()
|
|
783
|
+
* }
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
async function getExperiment(experimentId, options, defaultVariant = null) {
|
|
787
|
+
if (!exports.LIB_INSTANCE)
|
|
788
|
+
return defaultVariant;
|
|
789
|
+
return exports.LIB_INSTANCE.getExperiment(experimentId, options, defaultVariant);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Clears the cached experiments, forcing a fresh fetch on the next call.
|
|
793
|
+
* This is an alias for clearFeatureFlagsCache since experiments and flags share the same cache.
|
|
794
|
+
*/
|
|
795
|
+
function clearExperimentsCache() {
|
|
796
|
+
if (!exports.LIB_INSTANCE)
|
|
797
|
+
return;
|
|
798
|
+
exports.LIB_INSTANCE.clearExperimentsCache();
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Gets the anonymous profile ID for the current visitor.
|
|
802
|
+
* If profileId was set via init options, returns that.
|
|
803
|
+
* Otherwise, requests server to generate one from IP/UA hash.
|
|
804
|
+
*
|
|
805
|
+
* This ID can be used for revenue attribution with payment providers like Paddle.
|
|
806
|
+
*
|
|
807
|
+
* @returns A promise that resolves to the profile ID string, or null on error.
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* ```typescript
|
|
811
|
+
* const profileId = await getProfileId()
|
|
812
|
+
*
|
|
813
|
+
* // Pass to Paddle Checkout for revenue attribution
|
|
814
|
+
* Paddle.Checkout.open({
|
|
815
|
+
* items: [{ priceId: 'pri_01234567890', quantity: 1 }],
|
|
816
|
+
* customData: {
|
|
817
|
+
* swetrix_profile_id: profileId,
|
|
818
|
+
* swetrix_session_id: await getSessionId()
|
|
819
|
+
* }
|
|
820
|
+
* })
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
async function getProfileId() {
|
|
824
|
+
if (!exports.LIB_INSTANCE)
|
|
825
|
+
return null;
|
|
826
|
+
return exports.LIB_INSTANCE.getProfileId();
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Gets the current session ID for the visitor.
|
|
830
|
+
* Session IDs are generated server-side based on IP and user agent.
|
|
831
|
+
*
|
|
832
|
+
* This ID can be used for revenue attribution with payment providers like Paddle.
|
|
833
|
+
*
|
|
834
|
+
* @returns A promise that resolves to the session ID string, or null on error.
|
|
835
|
+
*
|
|
836
|
+
* @example
|
|
837
|
+
* ```typescript
|
|
838
|
+
* const sessionId = await getSessionId()
|
|
839
|
+
*
|
|
840
|
+
* // Pass to Paddle Checkout for revenue attribution
|
|
841
|
+
* Paddle.Checkout.open({
|
|
842
|
+
* items: [{ priceId: 'pri_01234567890', quantity: 1 }],
|
|
843
|
+
* customData: {
|
|
844
|
+
* swetrix_profile_id: await getProfileId(),
|
|
845
|
+
* swetrix_session_id: sessionId
|
|
846
|
+
* }
|
|
847
|
+
* })
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
async function getSessionId() {
|
|
851
|
+
if (!exports.LIB_INSTANCE)
|
|
852
|
+
return null;
|
|
853
|
+
return exports.LIB_INSTANCE.getSessionId();
|
|
854
|
+
}
|
|
483
855
|
|
|
856
|
+
exports.clearExperimentsCache = clearExperimentsCache;
|
|
857
|
+
exports.clearFeatureFlagsCache = clearFeatureFlagsCache;
|
|
858
|
+
exports.getExperiment = getExperiment;
|
|
859
|
+
exports.getExperiments = getExperiments;
|
|
860
|
+
exports.getFeatureFlag = getFeatureFlag;
|
|
861
|
+
exports.getFeatureFlags = getFeatureFlags;
|
|
862
|
+
exports.getProfileId = getProfileId;
|
|
863
|
+
exports.getSessionId = getSessionId;
|
|
484
864
|
exports.init = init;
|
|
485
865
|
exports.pageview = pageview;
|
|
486
866
|
exports.track = track;
|