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