@coframe/sdk 1.1.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.
@@ -0,0 +1,3344 @@
1
+ if (!Array.prototype.find) {
2
+ Object.defineProperty(Array.prototype, 'find', {
3
+ value: function (predicate, thisArg) {
4
+ if (this == null) {
5
+ throw new TypeError('Array.prototype.find called on null or undefined');
6
+ }
7
+ if (typeof predicate !== 'function') {
8
+ throw new TypeError('predicate must be a function');
9
+ }
10
+ const list = Object(this);
11
+ const length = list.length >>> 0;
12
+ for (let i = 0; i < length; i++) {
13
+ const value = list[i];
14
+ if (predicate.call(thisArg, value, i, list)) {
15
+ return value;
16
+ }
17
+ }
18
+ return undefined;
19
+ },
20
+ writable: true,
21
+ configurable: true,
22
+ enumerable: false,
23
+ });
24
+ }
25
+ if (!Array.prototype.flatMap) {
26
+ Object.defineProperty(Array.prototype, 'flatMap', {
27
+ value: function (callback, thisArg) {
28
+ if (this == null) {
29
+ throw new TypeError('Array.prototype.flatMap called on null or undefined');
30
+ }
31
+ if (typeof callback !== 'function') {
32
+ throw new TypeError('callback must be a function');
33
+ }
34
+ const result = [];
35
+ for (let i = 0; i < this.length; i++) {
36
+ if (i in this) {
37
+ const mappedValue = callback.call(thisArg, this[i], i, this);
38
+ if (Array.isArray(mappedValue)) {
39
+ for (const item of mappedValue) {
40
+ result.push(item);
41
+ }
42
+ }
43
+ else {
44
+ result.push(mappedValue);
45
+ }
46
+ }
47
+ }
48
+ return result;
49
+ },
50
+ configurable: true,
51
+ writable: true,
52
+ enumerable: false,
53
+ });
54
+ }
55
+ if (typeof Promise !== 'undefined' && !Promise.prototype.finally) {
56
+ Object.defineProperty(Promise.prototype, 'finally', {
57
+ value: function (callback) {
58
+ if (typeof this !== 'object' || this === null) {
59
+ throw new TypeError('"this" is not a Promise');
60
+ }
61
+ if (typeof callback !== 'function') {
62
+ return this;
63
+ }
64
+ const PromiseCtor = this.constructor;
65
+ const onFinally = () => {
66
+ try {
67
+ return callback();
68
+ }
69
+ catch (err) {
70
+ return PromiseCtor.reject(err);
71
+ }
72
+ };
73
+ return this.then((value) => PromiseCtor.resolve(onFinally()).then(() => value), (reason) => PromiseCtor.resolve(onFinally()).then(() => {
74
+ throw reason;
75
+ }));
76
+ },
77
+ writable: true,
78
+ configurable: true,
79
+ enumerable: false,
80
+ });
81
+ }
82
+ if (!Object.entries) {
83
+ Object.defineProperty(Object, 'entries', {
84
+ value: function (obj) {
85
+ if (obj === null || obj === undefined) {
86
+ throw new TypeError('Object.entries called on non-object');
87
+ }
88
+ const o = Object(obj);
89
+ return Object.keys(o).map((key) => [
90
+ key,
91
+ o[key],
92
+ ]);
93
+ },
94
+ writable: true,
95
+ configurable: true,
96
+ enumerable: false,
97
+ });
98
+ }
99
+ if (typeof Event !== 'undefined' && !Event.prototype.composedPath) {
100
+ Object.defineProperty(Event.prototype, 'composedPath', {
101
+ value: function () {
102
+ if (!(this instanceof Event)) {
103
+ throw new TypeError('Illegal invocation');
104
+ }
105
+ const path = [];
106
+ let current = this.target;
107
+ while (current) {
108
+ path.push(current);
109
+ if (typeof ShadowRoot !== 'undefined' &&
110
+ current instanceof ShadowRoot) {
111
+ current = current.host;
112
+ }
113
+ else if (current instanceof Document) {
114
+ current = current.defaultView;
115
+ }
116
+ else if (current instanceof Element) {
117
+ current = current.parentNode;
118
+ }
119
+ else {
120
+ current = null;
121
+ }
122
+ }
123
+ const win = window;
124
+ if (win && !path.includes(win)) {
125
+ path.push(win);
126
+ }
127
+ return path;
128
+ },
129
+ writable: true,
130
+ configurable: true,
131
+ enumerable: false,
132
+ });
133
+ }
134
+ if (!Array.from) {
135
+ Object.defineProperty(Array, 'from', {
136
+ value: function (arrayLike) {
137
+ if (arrayLike == null) {
138
+ throw new TypeError('Array.from requires an array-like or iterable object');
139
+ }
140
+ const items = Object(arrayLike);
141
+ const result = [];
142
+ let k = 0;
143
+ const isIterable = typeof items[Symbol.iterator] === 'function';
144
+ if (isIterable) {
145
+ const iterator = items[Symbol.iterator]();
146
+ let next = iterator.next();
147
+ while (!next.done) {
148
+ result[k++] = next.value;
149
+ next = iterator.next();
150
+ }
151
+ }
152
+ else {
153
+ const len = items.length >>> 0;
154
+ while (k < len) {
155
+ result[k] = items[k];
156
+ k++;
157
+ }
158
+ }
159
+ result.length = k;
160
+ return result;
161
+ },
162
+ writable: true,
163
+ configurable: true,
164
+ enumerable: false,
165
+ });
166
+ }
167
+
168
+ function getCurrentPageUrl() {
169
+ return window.location.href;
170
+ }
171
+ function getBrowserLanguages() {
172
+ var _a, _b;
173
+ return Array.from((_a = navigator === null || navigator === void 0 ? void 0 : navigator.languages) !== null && _a !== void 0 ? _a : [(_b = navigator === null || navigator === void 0 ? void 0 : navigator.language) !== null && _b !== void 0 ? _b : []]);
174
+ }
175
+ function getBrowserPlatform() {
176
+ var _a, _b, _c;
177
+ return ((_c = (_a = navigator === null || navigator === void 0 ? void 0 : navigator.platform) !== null && _a !== void 0 ? _a : (_b = navigator === null || navigator === void 0 ? void 0 : navigator.userAgentData) === null || _b === void 0 ? void 0 : _b.platform) !== null && _c !== void 0 ? _c : '');
178
+ }
179
+ function getBrowserReferrer() {
180
+ return document.referrer;
181
+ }
182
+ function getBrowserHostname() {
183
+ return window.location.hostname;
184
+ }
185
+ function getDocumentCookie() {
186
+ try {
187
+ return document.cookie;
188
+ }
189
+ catch (_error) {
190
+ return '';
191
+ }
192
+ }
193
+ function setDocumentCookie(cookie) {
194
+ document.cookie = cookie;
195
+ }
196
+ function getBrowserUserAgent() {
197
+ return (navigator && navigator.userAgent) || '';
198
+ }
199
+ function getBrowserFignerprintInfo() {
200
+ const cookieKeys = [];
201
+ const localStorageKeys = [];
202
+ const sessionStorageKeys = [];
203
+ try {
204
+ const cookieStr = getDocumentCookie();
205
+ if (cookieStr) {
206
+ const cookiePairs = cookieStr.split(';').map((c) => c.trim());
207
+ for (const cookie of cookiePairs) {
208
+ const [name] = cookie.split('=');
209
+ if (name) {
210
+ if (name.startsWith('coframe.')) {
211
+ continue;
212
+ }
213
+ cookieKeys.push(name);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ catch (_error) {
219
+ }
220
+ try {
221
+ if (typeof window !== 'undefined' && window.localStorage) {
222
+ const keys = Object.keys(window.localStorage);
223
+ for (const key of keys) {
224
+ localStorageKeys.push(key);
225
+ }
226
+ }
227
+ }
228
+ catch (_error) {
229
+ }
230
+ try {
231
+ if (typeof window !== 'undefined' && window.sessionStorage) {
232
+ const keys = Object.keys(window.sessionStorage);
233
+ for (const key of keys) {
234
+ sessionStorageKeys.push(key);
235
+ }
236
+ }
237
+ }
238
+ catch (_error) {
239
+ }
240
+ return {
241
+ cookie_keys: cookieKeys,
242
+ local_storage_keys: localStorageKeys,
243
+ session_storage_keys: sessionStorageKeys,
244
+ };
245
+ }
246
+
247
+ const USER_TOKEN_COOKIE_KEY = 'coframe.user.token';
248
+ const LAST_VISIT_TIME_KEY = 'coframe.lastVisitTime';
249
+ const VISIT_COUNT_COOKIE_KEY = 'coframe.cvc';
250
+ const LANE_COOKIE_KEY = 'coframe.lanes';
251
+ const COOKIE_DAYS = 400;
252
+ function getGlobalScope() {
253
+ if (typeof globalThis !== 'undefined') {
254
+ return globalThis;
255
+ }
256
+ if (typeof window !== 'undefined') {
257
+ return window;
258
+ }
259
+ if (typeof self !== 'undefined') {
260
+ return self;
261
+ }
262
+ }
263
+ const generateUUID = () => {
264
+ var _a;
265
+ const cryptoObject = (_a = getGlobalScope()) === null || _a === void 0 ? void 0 : _a.crypto;
266
+ if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID)
267
+ return cryptoObject.randomUUID();
268
+ return `${1e7}${ -1e3}${ -4e3}${ -8e3}${ -1e11}`.replace(/[018]/g, (c) => {
269
+ var _a, _b;
270
+ const randomValue = (_b = (_a = cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues) === null || _a === void 0 ? void 0 : _a.call(cryptoObject, new Uint8Array(1))[0]) !== null && _b !== void 0 ? _b : 0;
271
+ return (c ^
272
+ (randomValue & (15 >> (c / 4)))).toString(16);
273
+ });
274
+ };
275
+ const getCookie = (name) => {
276
+ const cookieStr = getDocumentCookie();
277
+ const regex = new RegExp(`(?:^| )${name}=([^;]*)`, 'g');
278
+ let match = null;
279
+ let lastValue = '';
280
+ while (true) {
281
+ match = regex.exec(cookieStr);
282
+ if (match === null)
283
+ break;
284
+ lastValue = match[1];
285
+ }
286
+ return lastValue;
287
+ };
288
+ let isCookiesEnabled = false;
289
+ const setCookiesEnabled = (allowed) => {
290
+ isCookiesEnabled = allowed;
291
+ };
292
+ const setCookie = (name, value, logger) => {
293
+ if (!isCookiesEnabled) {
294
+ logger.error('Cookies are not enabled', 'setting_cookie_when_disabled');
295
+ return;
296
+ }
297
+ const expirationDate = new Date();
298
+ expirationDate.setTime(expirationDate.getTime() + 24 * 60 * 60 * 1000 * COOKIE_DAYS);
299
+ const expires = expirationDate.toUTCString();
300
+ const setCookieForDomain = (domain) => {
301
+ const cookie = domain
302
+ ? `${name}=${value};path=/;domain=${domain};expires=${expires};secure`
303
+ : `${name}=${value};path=/;expires=${expires};secure`;
304
+ setDocumentCookie(cookie);
305
+ };
306
+ const hostname = getBrowserHostname();
307
+ if (hostname === 'localhost') {
308
+ setCookieForDomain();
309
+ }
310
+ else {
311
+ const domainParts = hostname.split('.');
312
+ for (let i = domainParts.length - 2; i >= 0; i--) {
313
+ let domain = `${domainParts.slice(i).join('.')}`;
314
+ if (i !== 0) {
315
+ domain = `.${domain}`;
316
+ }
317
+ setCookieForDomain(domain);
318
+ }
319
+ }
320
+ if (getCookie(name) !== value) {
321
+ logger.error(`Failed to set cookie ${name} for domain: ${hostname}`, 'failed_to_set_cookie', {
322
+ name,
323
+ hostname,
324
+ });
325
+ }
326
+ };
327
+ const getOrGenerateUserToken = () => {
328
+ let uuid = getCookie(USER_TOKEN_COOKIE_KEY);
329
+ if (!uuid) {
330
+ uuid = generateUUID();
331
+ }
332
+ return uuid;
333
+ };
334
+ const setUserTokenCookie = (uuid, logger) => {
335
+ setCookie(USER_TOKEN_COOKIE_KEY, uuid, logger);
336
+ };
337
+ const trackVisitCookie = (logger) => {
338
+ const visitCountString = getCookie(VISIT_COUNT_COOKIE_KEY);
339
+ const lastVisitTime = Number.parseInt(getCookie(LAST_VISIT_TIME_KEY), 10);
340
+ const currentTime = Date.now();
341
+ const twoHours = 2 * 60 * 60 * 1000;
342
+ if (!lastVisitTime || currentTime - lastVisitTime > twoHours) {
343
+ const visitCount = visitCountString
344
+ ? Number.parseInt(visitCountString, 10) + 1
345
+ : 1;
346
+ setCookie(VISIT_COUNT_COOKIE_KEY, visitCount.toString(), logger);
347
+ setCookie(LAST_VISIT_TIME_KEY, currentTime.toString(), logger);
348
+ }
349
+ };
350
+
351
+ const selectorCache = new WeakMap();
352
+ const getSelector = (element, options) => {
353
+ const opts = {
354
+ withDescendants: true,
355
+ cache: true,
356
+ ...options,
357
+ };
358
+ if (opts.cache === false) {
359
+ return getSelectorWithoutCache(element, options);
360
+ }
361
+ const cacheKey = JSON.stringify(opts);
362
+ if (!selectorCache.has(element)) {
363
+ selectorCache.set(element, new Map());
364
+ }
365
+ const elementCache = selectorCache.get(element);
366
+ if (elementCache === null || elementCache === void 0 ? void 0 : elementCache.has(cacheKey)) {
367
+ const cachedSelector = elementCache.get(cacheKey);
368
+ if (cachedSelector) {
369
+ return cachedSelector;
370
+ }
371
+ }
372
+ const selector = getSelectorWithoutCache(element, options);
373
+ if (elementCache) {
374
+ elementCache.set(cacheKey, selector);
375
+ }
376
+ return selector;
377
+ };
378
+ const getSelectorWithoutCache = (element, options) => {
379
+ const opts = {
380
+ withDescendants: true,
381
+ ...options,
382
+ };
383
+ const generateHash = (data) => {
384
+ let hash = 0;
385
+ if (data.length === 0)
386
+ return hash.toString();
387
+ for (let i = 0; i < data.length; i++) {
388
+ const char = data.charCodeAt(i);
389
+ hash = (hash << 5) - hash + char;
390
+ hash = hash & hash;
391
+ }
392
+ return Math.abs(hash).toString(16);
393
+ };
394
+ function getInnerText(element) {
395
+ const childNodes = element.childNodes;
396
+ for (let i = 0; i < childNodes.length; i++) {
397
+ const node = childNodes[i];
398
+ if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
399
+ return node.nodeValue.trim();
400
+ }
401
+ }
402
+ return null;
403
+ }
404
+ function getDescendants(element, max_depth) {
405
+ const descendants = [];
406
+ function collectDescendants(el, depth) {
407
+ if (depth > max_depth)
408
+ return;
409
+ for (let i = 0; i < el.childNodes.length; i++) {
410
+ const child = el.childNodes[i];
411
+ if (child.nodeType === Node.ELEMENT_NODE) {
412
+ descendants.push({ element: child, depth: depth });
413
+ collectDescendants(child, depth + 1);
414
+ }
415
+ }
416
+ }
417
+ collectDescendants(element, 1);
418
+ return descendants;
419
+ }
420
+ function getShadowHost(element) {
421
+ if (element === document.documentElement || element === document.body) {
422
+ return null;
423
+ }
424
+ const rootNode = element.getRootNode();
425
+ if (rootNode instanceof ShadowRoot) {
426
+ return rootNode.host;
427
+ }
428
+ return null;
429
+ }
430
+ const getParentNode = (element) => {
431
+ return element.parentElement || getShadowHost(element);
432
+ };
433
+ const buildSelector = (el, ascend) => {
434
+ var _a;
435
+ const selector = {
436
+ t: el.tagName.toLowerCase(),
437
+ id: el.getAttribute('id') || undefined,
438
+ text: getInnerText(el) || undefined,
439
+ href: el.getAttribute('href') || undefined,
440
+ dataTestId: el.getAttribute('data-test-id') || undefined,
441
+ ariaLabel: el.getAttribute('aria-label') || undefined,
442
+ };
443
+ for (const key of Object.keys(selector)) {
444
+ if (selector[key] === undefined) {
445
+ delete selector[key];
446
+ }
447
+ }
448
+ if (el.shadowRoot) {
449
+ selector.isShadowRoot = true;
450
+ }
451
+ let i = 0;
452
+ let totalSiblings = 0;
453
+ let sibling = (_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.firstElementChild;
454
+ while (sibling) {
455
+ if (sibling.tagName.toLowerCase() === selector.t) {
456
+ totalSiblings++;
457
+ if (sibling === el) {
458
+ i = totalSiblings;
459
+ }
460
+ }
461
+ sibling = sibling.nextElementSibling;
462
+ }
463
+ if (i > 0 && totalSiblings > 0) {
464
+ selector.n = { i, t: totalSiblings };
465
+ }
466
+ const parent = getParentNode(el);
467
+ if (parent && ascend) {
468
+ const parentSelector = buildSelector(parent, ascend);
469
+ if (parentSelector) {
470
+ selector.p = parentSelector;
471
+ }
472
+ }
473
+ return selector;
474
+ };
475
+ const selector = buildSelector(element, true);
476
+ const descendants = opts.withDescendants ? getDescendants(element, 3) : [];
477
+ for (const descendant of descendants) {
478
+ const descSelector = buildSelector(descendant.element, false);
479
+ if (descSelector.id ||
480
+ descSelector.dataTestId ||
481
+ descSelector.ariaLabel ||
482
+ descSelector.href ||
483
+ descSelector.text) {
484
+ selector.descendants = selector.descendants || [];
485
+ selector.descendants.push({
486
+ depth: descendant.depth,
487
+ sel: descSelector,
488
+ });
489
+ }
490
+ }
491
+ selector.fingerprint = generateHash(JSON.stringify(selector));
492
+ return selector;
493
+ };
494
+ const getElementsByHrefPrefix = (currentUrl, hrefStartsWith) => {
495
+ const elements = [];
496
+ const elementsFullUrl = document.querySelectorAll(`a[href^="${hrefStartsWith}"]`);
497
+ elements.push(...Array.from(elementsFullUrl));
498
+ const currentUrlObj = new URL(currentUrl);
499
+ const hrefStartsWithObj = new URL(hrefStartsWith);
500
+ if (currentUrlObj.hostname === hrefStartsWithObj.hostname) {
501
+ let relativePrefix = hrefStartsWithObj.pathname.replace(currentUrlObj.pathname, '');
502
+ if (relativePrefix) {
503
+ if (!relativePrefix.startsWith('/')) {
504
+ relativePrefix = `/${relativePrefix}`;
505
+ }
506
+ const elementsRelativeUrl = document.querySelectorAll(`a[href^="${relativePrefix}"]`);
507
+ elements.push(...Array.from(elementsRelativeUrl));
508
+ }
509
+ }
510
+ return elements;
511
+ };
512
+ const getElement = (selector, options) => {
513
+ if (selector.universal) {
514
+ return getElementUniversal((options === null || options === void 0 ? void 0 : options.context) || document, selector.universal, (options === null || options === void 0 ? void 0 : options.debug) || false).element;
515
+ }
516
+ if (selector.css) {
517
+ return ((options === null || options === void 0 ? void 0 : options.context) || document).querySelector(selector.css);
518
+ }
519
+ return null;
520
+ };
521
+ const getElementUniversal = (document, selector, debug) => {
522
+ var _a, _b, _c;
523
+ const debugList = [];
524
+ const debugLog = (message, xpath, element) => {
525
+ if (!debug)
526
+ return;
527
+ debugList.push({ xpath, message, element });
528
+ };
529
+ const tryXpath = (xpath) => {
530
+ if (!xpath)
531
+ return null;
532
+ let elements;
533
+ try {
534
+ elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
535
+ }
536
+ catch (e) {
537
+ console.error('[Coframe] Error evaluating xpath', xpath, e);
538
+ return null;
539
+ }
540
+ if (elements.snapshotLength === 1) {
541
+ const element = elements.snapshotItem(0);
542
+ if (xpath !== '/html') {
543
+ debugLog('Found unique', xpath, element);
544
+ }
545
+ return element;
546
+ }
547
+ if (elements.snapshotLength > 1) {
548
+ debugLog('Found more than one element for xpath:', xpath);
549
+ return null;
550
+ }
551
+ debugLog('No element found for xpath:', xpath);
552
+ return null;
553
+ };
554
+ const tryPath = (pathSegments) => {
555
+ const parentXpath = pathSegments[0].xpath;
556
+ let element = tryXpath(parentXpath);
557
+ if (!element) {
558
+ return null;
559
+ }
560
+ let currentShadowRoot = element.shadowRoot || element;
561
+ let accumulatedXpath = parentXpath;
562
+ for (let i = 1; i < pathSegments.length; i++) {
563
+ const segment = pathSegments[i];
564
+ if (segment.isInsideShadowRoot) {
565
+ const childElements = currentShadowRoot.querySelectorAll(segment.css);
566
+ if (childElements.length === 0) {
567
+ return null;
568
+ }
569
+ if (childElements.length > 1) {
570
+ debugLog(`Found more than one element for css: ${segment.css}`, accumulatedXpath);
571
+ return null;
572
+ }
573
+ const childElement = childElements[0];
574
+ if (!childElement) {
575
+ return null;
576
+ }
577
+ element = childElement;
578
+ currentShadowRoot = element.shadowRoot || element;
579
+ accumulatedXpath = `${accumulatedXpath}/${segment.xpath}`;
580
+ debugLog(`Found ${i} shadow element`, accumulatedXpath, element);
581
+ }
582
+ else {
583
+ let result;
584
+ try {
585
+ result = document.evaluate(segment.xpath, element, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
586
+ }
587
+ catch (e) {
588
+ console.error('[Coframe] Error evaluating xpath', segment.xpath, e);
589
+ return null;
590
+ }
591
+ const partElement = result.singleNodeValue;
592
+ if (!partElement) {
593
+ return null;
594
+ }
595
+ accumulatedXpath = `${accumulatedXpath}/${segment.xpath}`;
596
+ debugLog(`Found ${i} element`, accumulatedXpath, partElement);
597
+ element = partElement;
598
+ currentShadowRoot = element.shadowRoot || element;
599
+ }
600
+ }
601
+ return element;
602
+ };
603
+ const naivePaths = generateNaivePaths(selector);
604
+ let element = tryPath(naivePaths[0]);
605
+ if (element && !debug)
606
+ return { element };
607
+ const [relativePaths, partialRelativePaths] = generateRelativePaths(selector, 10);
608
+ for (const relativePath of relativePaths) {
609
+ element = tryPath(relativePath);
610
+ if (element && !debug)
611
+ return { element };
612
+ }
613
+ if (selector.text) {
614
+ const textPaths = generatePathsByText(selector.text, selector.t);
615
+ for (const textPath of textPaths) {
616
+ element = tryPath(textPath);
617
+ if (element && !debug)
618
+ return { element };
619
+ }
620
+ }
621
+ if (selector.descendants) {
622
+ for (const descendant of selector.descendants) {
623
+ const ancestorSelectors = {
624
+ xpath: `/ancestor::*[${descendant.depth}][self::${selector.t}]`,
625
+ css: `:nth-ancestor(${descendant.depth}):is(${selector.t})`,
626
+ };
627
+ const descPath = (_a = generateRelativePaths(descendant.sel, 1)[0]) === null || _a === void 0 ? void 0 : _a[0];
628
+ if (descPath && descPath.length > 0) {
629
+ const modifiedPath = appendToLastPathSegment(descPath, ancestorSelectors);
630
+ element = tryPath(modifiedPath);
631
+ if (element && !debug)
632
+ return { element };
633
+ }
634
+ if (descendant.sel.text) {
635
+ const textPaths = generatePathsByText(descendant.sel.text, descendant.sel.t);
636
+ for (const textPath of textPaths) {
637
+ if (textPath && textPath.length > 0) {
638
+ const textBasedPath = appendToLastPathSegment(textPath, ancestorSelectors);
639
+ element = tryPath(textBasedPath);
640
+ if (element && !debug)
641
+ return { element };
642
+ }
643
+ }
644
+ }
645
+ }
646
+ }
647
+ for (const partialPath of naivePaths.slice(1)) {
648
+ element = tryPath(partialPath);
649
+ if (element && !debug)
650
+ return { element };
651
+ }
652
+ for (const partialPath of partialRelativePaths) {
653
+ element = tryPath(partialPath);
654
+ if (element && !debug)
655
+ return { element };
656
+ }
657
+ if (debug) {
658
+ const lastNaivePath = naivePaths.length > 0 ? naivePaths[naivePaths.length - 1] : null;
659
+ const lastSegment = lastNaivePath && lastNaivePath.length > 0
660
+ ? lastNaivePath[lastNaivePath.length - 1]
661
+ : null;
662
+ const targetXpath = ((_b = lastSegment === null || lastSegment === void 0 ? void 0 : lastSegment.xpath) === null || _b === void 0 ? void 0 : _b.replace(/^\//, '')) || '';
663
+ const sortedDebugElements = debugList
664
+ .filter((log) => log.element)
665
+ .filter((log) => log.xpath.endsWith(targetXpath))
666
+ .sort((a, b) => {
667
+ const lengthDiff = b.xpath.length - a.xpath.length;
668
+ return lengthDiff !== 0 ? lengthDiff : 0;
669
+ });
670
+ const element = ((_c = sortedDebugElements[0]) === null || _c === void 0 ? void 0 : _c.element) || null;
671
+ return {
672
+ element,
673
+ debugList,
674
+ };
675
+ }
676
+ return { element: null };
677
+ };
678
+ const generateNaivePaths = (sel) => {
679
+ var _a, _b;
680
+ const paths = [];
681
+ const emptySegment = {
682
+ xpath: '',
683
+ css: '',
684
+ isInsideShadowRoot: false,
685
+ };
686
+ let path = [emptySegment];
687
+ let currentSel = sel;
688
+ while (currentSel) {
689
+ const tagName = currentSel.t;
690
+ if (!tagName)
691
+ break;
692
+ let subpath = `/${tagName}`;
693
+ let cssSegment = tagName.toLowerCase();
694
+ if (((_a = currentSel.n) === null || _a === void 0 ? void 0 : _a.i) && ((_b = currentSel.n) === null || _b === void 0 ? void 0 : _b.t)) {
695
+ subpath += `[count(../${tagName})=${currentSel.n.t}][${currentSel.n.i}]`;
696
+ cssSegment = `${cssSegment}:nth-of-type(${currentSel.n.i})`;
697
+ }
698
+ const isShadowBoundary = currentSel.isShadowRoot === true;
699
+ if (isShadowBoundary) {
700
+ path.push({
701
+ xpath: '',
702
+ css: '',
703
+ isInsideShadowRoot: true,
704
+ });
705
+ }
706
+ const currSegment = path[path.length - 1];
707
+ const restSegments = path.slice(0, -1);
708
+ const updatedSegment = {
709
+ xpath: `${subpath}${currSegment.xpath}`,
710
+ css: `${cssSegment} ${currSegment.css}`.trim(),
711
+ isInsideShadowRoot: hasShadowRoot(currentSel),
712
+ };
713
+ path = [...restSegments, updatedSegment];
714
+ currentSel = currentSel.p;
715
+ const absoluteXpath = (currentSel ? '/' : '') + updatedSegment.xpath;
716
+ const finalSegment = {
717
+ ...updatedSegment,
718
+ xpath: absoluteXpath,
719
+ };
720
+ const filteredSegments = [finalSegment, ...restSegments.reverse()].filter((segment) => !isSegmentEmpty(segment));
721
+ paths.unshift(filteredSegments);
722
+ }
723
+ return paths;
724
+ };
725
+ const generatePathsByText = (text, tagName) => {
726
+ const paths = [];
727
+ for (const tagQuery of [tagName, '*']) {
728
+ const parts = text.split("'");
729
+ const containsQuery = parts.length === 1
730
+ ?
731
+ `'${text}'`
732
+ :
733
+ `concat(${parts.map((part) => `'${part}'`).join(`, "'", `)})`;
734
+ const xpath = `//${tagQuery}[not(self::script or self::style) and contains(text(), ${containsQuery})]`;
735
+ const regularSegment = {
736
+ isInsideShadowRoot: false,
737
+ xpath,
738
+ css: `*:contains("${text}")`,
739
+ };
740
+ paths.push([regularSegment]);
741
+ }
742
+ return paths;
743
+ };
744
+ const generateRelativePaths = (sel, max) => {
745
+ var _a, _b, _c;
746
+ const relativePaths = [];
747
+ const partialRelativePaths = [];
748
+ let wipPaths = [{ xpath: '', css: '' }];
749
+ let currentSel = sel;
750
+ while (currentSel) {
751
+ const tagName = currentSel.t;
752
+ const uniqueQueries = [];
753
+ const nonUniqQueries = [];
754
+ const newWipPaths = [];
755
+ if (currentSel.id) {
756
+ uniqueQueries.push({
757
+ xpath: `[@id="${currentSel.id}"]`,
758
+ css: `#${currentSel.id}`,
759
+ });
760
+ }
761
+ else if (currentSel.dataTestId) {
762
+ uniqueQueries.push({
763
+ xpath: `[@data-test-id="${currentSel.dataTestId}"]`,
764
+ css: `${tagName}[data-test-id="${currentSel.dataTestId}"]`,
765
+ });
766
+ }
767
+ if (currentSel.ariaLabel) {
768
+ nonUniqQueries.push({
769
+ xpath: `[@aria-label="${currentSel.ariaLabel}"]`,
770
+ css: `${tagName}[aria-label="${currentSel.ariaLabel}"]`,
771
+ });
772
+ }
773
+ if (currentSel.href) {
774
+ nonUniqQueries.push({
775
+ xpath: `[@href="${currentSel.href}"]`,
776
+ css: `${tagName}[href="${currentSel.href}"]`,
777
+ });
778
+ }
779
+ const positionalQuery = {
780
+ xpath: ((_a = currentSel.n) === null || _a === void 0 ? void 0 : _a.i) && ((_b = currentSel.n) === null || _b === void 0 ? void 0 : _b.t)
781
+ ? `[count(../${tagName})=${currentSel.n.t}][${currentSel.n.i}]`
782
+ : '',
783
+ css: ((_c = currentSel.n) === null || _c === void 0 ? void 0 : _c.i)
784
+ ? `${tagName}:nth-of-type(${currentSel.n.i})`
785
+ : tagName,
786
+ };
787
+ for (const query of uniqueQueries) {
788
+ for (const path of wipPaths) {
789
+ const xpath = `//${tagName}${query.xpath}${path.xpath}`;
790
+ const css = query.css + (path.css ? ` ${path.css}` : '');
791
+ const regularSegment = {
792
+ xpath,
793
+ css,
794
+ isInsideShadowRoot: false,
795
+ };
796
+ relativePaths.push([regularSegment]);
797
+ if (relativePaths.length >= max)
798
+ return [relativePaths, partialRelativePaths];
799
+ }
800
+ }
801
+ for (const query of nonUniqQueries) {
802
+ for (const path of wipPaths) {
803
+ const xpath = `/${tagName}${query.xpath}${path.xpath}`;
804
+ const css = query.css + (path.css ? ` > ${path.css}` : '');
805
+ newWipPaths.push({ xpath, css });
806
+ const regularSegment = {
807
+ xpath: `/${xpath}`,
808
+ css,
809
+ isInsideShadowRoot: false,
810
+ };
811
+ partialRelativePaths.unshift([regularSegment]);
812
+ }
813
+ }
814
+ for (const path of wipPaths) {
815
+ const xpath = `/${tagName}${positionalQuery.xpath}${path.xpath}`;
816
+ const css = positionalQuery.css + (path.css ? ` > ${path.css}` : '');
817
+ newWipPaths.push({ xpath, css });
818
+ }
819
+ wipPaths = newWipPaths;
820
+ currentSel = currentSel.p;
821
+ }
822
+ return [relativePaths, partialRelativePaths];
823
+ };
824
+ const hasShadowRoot = (sel) => {
825
+ if (!sel)
826
+ return false;
827
+ if (sel.isShadowRoot === true) {
828
+ return true;
829
+ }
830
+ if (sel.p && hasShadowRoot(sel.p)) {
831
+ return true;
832
+ }
833
+ return false;
834
+ };
835
+ const appendToLastPathSegment = (path, selectors) => {
836
+ if (path.length === 0)
837
+ return path;
838
+ const allSegments = [...path];
839
+ const last = allSegments.pop();
840
+ const leading = allSegments;
841
+ return [
842
+ ...leading,
843
+ {
844
+ ...last,
845
+ xpath: `${last.xpath}${selectors.xpath}`,
846
+ css: `${last.css} ${selectors.css}`,
847
+ },
848
+ ];
849
+ };
850
+ const isSegmentEmpty = (segment) => {
851
+ return segment.xpath === '';
852
+ };
853
+
854
+ const USER_CONTEXT_KEY_PREFIX = 'x-ctx-';
855
+ const MAX_USER_CONTEXT_JSON_BYTES = 4096;
856
+ function userContextJsonByteLength(userContext) {
857
+ try {
858
+ return new TextEncoder().encode(JSON.stringify(userContext !== null && userContext !== void 0 ? userContext : null)).length;
859
+ }
860
+ catch (_a) {
861
+ return Number.POSITIVE_INFINITY;
862
+ }
863
+ }
864
+ function sanitizeUserContext(userContext) {
865
+ if (userContext === undefined ||
866
+ userContext === null ||
867
+ typeof userContext !== 'object' ||
868
+ Array.isArray(userContext)) {
869
+ return undefined;
870
+ }
871
+ if (userContextJsonByteLength(userContext) > MAX_USER_CONTEXT_JSON_BYTES) {
872
+ return undefined;
873
+ }
874
+ return userContext;
875
+ }
876
+
877
+ function canSendWithConsent() {
878
+ if (!waitingForCookies) {
879
+ return true;
880
+ }
881
+ return isCookiesEnabled;
882
+ }
883
+ function shouldSendEvents(options = {}) {
884
+ if (forceSendEvents) {
885
+ return true;
886
+ }
887
+ if (isPreviewVariant) {
888
+ warnEventsNotSent();
889
+ return false;
890
+ }
891
+ if (!options.ignoreConsent && !canSendWithConsent()) {
892
+ return false;
893
+ }
894
+ if (trackOnlyActivatedFrames && !frameActivated) {
895
+ return false;
896
+ }
897
+ return true;
898
+ }
899
+ let isPreviewVariant = false;
900
+ const setIsPreviewVariant = (value) => {
901
+ isPreviewVariant = value;
902
+ };
903
+ let waitingForCookies = false;
904
+ const setWaitingForCookies = (value) => {
905
+ waitingForCookies = value;
906
+ };
907
+ let frameActivated = false;
908
+ const setFrameActivated = (value) => {
909
+ frameActivated = value;
910
+ };
911
+ let trackOnlyActivatedFrames = false;
912
+ const setTrackOnlyActivatedFrames = (value) => {
913
+ trackOnlyActivatedFrames = value;
914
+ };
915
+ let forceSendEvents = false;
916
+ const setForceSendEvents = (value) => {
917
+ forceSendEvents = value;
918
+ };
919
+ let eventsNotSentWarned = false;
920
+ function warnEventsNotSent() {
921
+ if (!eventsNotSentWarned) {
922
+ eventsNotSentWarned = true;
923
+ console.warn('[Coframe] Events not sent. Use ?coframe_should_send_events=true to force sending events for testing');
924
+ }
925
+ }
926
+
927
+ const SDK_VERSION = '2.4.0';
928
+ const queuedEvents = [];
929
+ const queuedMonitoringEvents = [];
930
+ const queuedSessions = [];
931
+ function queueMonitoringEvent(coframe, data) {
932
+ queuedMonitoringEvents.push(data);
933
+ if (queuedMonitoringEvents.length >= coframe.batchSize) {
934
+ flushQueuedEvents(coframe.projectId);
935
+ }
936
+ }
937
+ function queueEventForBatchedSending(coframe, data, flushImmediately = false) {
938
+ queuedEvents.push(data);
939
+ if (flushImmediately || queuedEvents.length >= coframe.batchSize) {
940
+ flushQueuedEvents(coframe.projectId);
941
+ }
942
+ }
943
+ function queueCreateSession(coframe, orgId, projectId, sessionId, userToken, experimentsInfo, startTime, referrer, platform, languages, userAgent, url, storageInfo, gaSessionInfo, cfId, personalizationHeaders) {
944
+ queuedSessions.push({
945
+ org_id: orgId,
946
+ project_id: projectId,
947
+ session_id: sessionId,
948
+ experiments: experimentsInfo,
949
+ method: 'script',
950
+ sdk_version: SDK_VERSION,
951
+ metadata: {
952
+ local_time: startTime,
953
+ languages,
954
+ platform,
955
+ referrer: referrer,
956
+ user_token: userToken,
957
+ user_agent: userAgent,
958
+ session_metadata: JSON.stringify({
959
+ url,
960
+ storage_info: storageInfo,
961
+ }),
962
+ ga_session_id: gaSessionInfo === null || gaSessionInfo === void 0 ? void 0 : gaSessionInfo.ga_session_id,
963
+ ga_session_number: gaSessionInfo === null || gaSessionInfo === void 0 ? void 0 : gaSessionInfo.ga_session_number,
964
+ coframe_user_id: cfId,
965
+ url: url,
966
+ ...(personalizationHeaders
967
+ ? {
968
+ personalization_headers: { ...personalizationHeaders },
969
+ }
970
+ : {}),
971
+ },
972
+ });
973
+ if (queuedSessions.length >= coframe.batchSize) {
974
+ flushQueuedEvents(coframe.projectId);
975
+ }
976
+ }
977
+ function flushQueuedEvents(projectId) {
978
+ if (!shouldSendEvents()) {
979
+ return;
980
+ }
981
+ if (queuedEvents.length > 0) {
982
+ sendEventToBackend(projectId, queuedEvents, 'batched_events');
983
+ queuedEvents.length = 0;
984
+ }
985
+ if (queuedMonitoringEvents.length > 0) {
986
+ sendEventToBackend(projectId, queuedMonitoringEvents, 'batched_monitoring');
987
+ queuedMonitoringEvents.length = 0;
988
+ }
989
+ if (queuedSessions.length > 0) {
990
+ for (const session of queuedSessions) {
991
+ createSession(session);
992
+ }
993
+ queuedSessions.length = 0;
994
+ }
995
+ }
996
+ function queueEvent(coframe, sessionInfo) {
997
+ const { projectId, sessionId, eventType, orgId, userToken } = sessionInfo;
998
+ const data = {
999
+ type: eventType,
1000
+ project_id: projectId,
1001
+ session_id: sessionId,
1002
+ time: new Date(),
1003
+ url: getCurrentPageUrl(),
1004
+ org_id: orgId,
1005
+ user_token: userToken,
1006
+ };
1007
+ const seg = sessionInfo.personalizationHeaders;
1008
+ const metadata = seg ? { personalization_headers: { ...seg } } : {};
1009
+ if (eventType === 'page_view') {
1010
+ data.content = { universal_selector: {}, metadata };
1011
+ }
1012
+ else {
1013
+ data.content = { metadata };
1014
+ }
1015
+ queueEventForBatchedSending(coframe, data);
1016
+ }
1017
+ function sendPageEngagementEvent(coframe, eventType, event, { isEngaged, projectId, sessionId, orgId, duration = 0, userToken, personalizationHeaders, }) {
1018
+ var _a;
1019
+ const startTime = performance.now();
1020
+ if (!isEngaged && eventType === 'click') {
1021
+ isEngaged = true;
1022
+ queueEvent(coframe, {
1023
+ projectId,
1024
+ sessionId,
1025
+ eventType: 'session_start',
1026
+ orgId,
1027
+ userToken: userToken || '',
1028
+ personalizationHeaders,
1029
+ });
1030
+ }
1031
+ const x = event.clientX + window.scrollX;
1032
+ const y = event.clientY + window.scrollY;
1033
+ const screenWidth = window.innerWidth;
1034
+ const screenHeight = window.innerHeight;
1035
+ const realEventTarget = event.composedPath()[0];
1036
+ const conversionElement = realEventTarget === null || realEventTarget === void 0 ? void 0 : realEventTarget.closest('[coframe-metric-ids], a, button, input, [onclick], [href]');
1037
+ const targetElement = conversionElement || realEventTarget;
1038
+ const metricIds = (_a = targetElement.getAttribute('coframe-metric-ids')) === null || _a === void 0 ? void 0 : _a.split(',');
1039
+ const data = {
1040
+ type: eventType,
1041
+ project_id: projectId,
1042
+ session_id: sessionId,
1043
+ time: new Date(),
1044
+ url: getCurrentPageUrl(),
1045
+ user_token: userToken,
1046
+ content: {
1047
+ universal_selector: getSelector(targetElement, {
1048
+ withDescendants: false,
1049
+ }),
1050
+ metric_ids: metricIds,
1051
+ position: {
1052
+ x: Math.round(x),
1053
+ y: Math.round(y),
1054
+ },
1055
+ screen_size: {
1056
+ width: screenWidth,
1057
+ height: screenHeight,
1058
+ },
1059
+ 'duration-ms': Math.round(duration),
1060
+ ...(personalizationHeaders && {
1061
+ metadata: {
1062
+ personalization_headers: { ...personalizationHeaders },
1063
+ },
1064
+ }),
1065
+ },
1066
+ };
1067
+ queueEventForBatchedSending(coframe, data, Boolean(metricIds === null || metricIds === void 0 ? void 0 : metricIds.length));
1068
+ const processingDuration = performance.now() - startTime;
1069
+ if (coframe.status) {
1070
+ if (!coframe.status.events) {
1071
+ coframe.status.events = [];
1072
+ }
1073
+ coframe.status.events.push({
1074
+ ...data,
1075
+ processing_duration_ms: Math.round(processingDuration),
1076
+ timestamp: Date.now(),
1077
+ });
1078
+ }
1079
+ }
1080
+ function sendEventToBackend(projectId, data, eventCategory = 'events') {
1081
+ const eventsEndpoint = `${"https://ingest.app.coframe.com/ingest/v2"}/${eventCategory}/${projectId}/`;
1082
+ if (navigator.sendBeacon) {
1083
+ const textData = JSON.stringify(data);
1084
+ const blob = new Blob([textData], { type: 'text/plain' });
1085
+ if (!navigator.sendBeacon(eventsEndpoint, blob)) {
1086
+ console.warn('[Coframe] failed to send event beacon');
1087
+ }
1088
+ }
1089
+ else {
1090
+ console.warn('[Coframe] cannot send event beacon, feature unavailable');
1091
+ }
1092
+ }
1093
+ function createSession(data) {
1094
+ const sessionApiUrl = `${"https://ingest.app.coframe.com/ingest/v2"}/session_result/`;
1095
+ if (navigator.sendBeacon) {
1096
+ const textData = JSON.stringify(data);
1097
+ const blob = new Blob([textData], { type: 'text/plain' });
1098
+ if (!navigator.sendBeacon(sessionApiUrl, blob)) {
1099
+ console.warn('[Coframe] failed to send event beacon');
1100
+ }
1101
+ }
1102
+ else {
1103
+ console.warn('[Coframe] cannot send event beacon, feature unavailable');
1104
+ }
1105
+ }
1106
+ async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, headers) {
1107
+ const payload = { projectId, currentUrl };
1108
+ const safe = sanitizeUserContext(userContext);
1109
+ if (safe) {
1110
+ payload.userContext = safe;
1111
+ }
1112
+ const config = encodeURIComponent(JSON.stringify(payload));
1113
+ const edgeUrl = new URL(`${"https://edge.cofra.me"}/api/v1/frames`);
1114
+ edgeUrl.searchParams.set('config', config);
1115
+ try {
1116
+ const response = await fetch(edgeUrl, { headers, signal });
1117
+ if (!response.ok)
1118
+ return null;
1119
+ return await response.json();
1120
+ }
1121
+ catch (error) {
1122
+ if (error instanceof DOMException && error.name === 'AbortError') {
1123
+ return null;
1124
+ }
1125
+ return null;
1126
+ }
1127
+ }
1128
+
1129
+ const isPageElement = (element) => {
1130
+ return (element === document.body ||
1131
+ element === document ||
1132
+ element === document.documentElement);
1133
+ };
1134
+ const inputButtonTypes = ['submit', 'button', 'reset'];
1135
+ function isInputButton(el) {
1136
+ if (el.tagName.toLowerCase() !== 'input') {
1137
+ return false;
1138
+ }
1139
+ const type = el.type;
1140
+ if (!type) {
1141
+ return false;
1142
+ }
1143
+ return inputButtonTypes.includes(type.toLowerCase());
1144
+ }
1145
+ const convertToFrontendStyle = (style) => {
1146
+ const result = {};
1147
+ if (style) {
1148
+ for (const key in style) {
1149
+ const styleKey = key.replace(/_/g, '-');
1150
+ result[styleKey] = style[key];
1151
+ }
1152
+ }
1153
+ return result;
1154
+ };
1155
+ const applyUpdateModification = (element, args, logger, logMetadata = {}) => {
1156
+ const { innerHTML, textContent, src, styles, onClick, ...otherAttributes } = args.attributes;
1157
+ if (innerHTML) {
1158
+ element.innerHTML = innerHTML;
1159
+ }
1160
+ else if (textContent && typeof textContent === 'string') {
1161
+ if (isInputButton(element)) {
1162
+ element.value = textContent;
1163
+ }
1164
+ else {
1165
+ element.textContent = textContent;
1166
+ }
1167
+ }
1168
+ if (src) {
1169
+ element.setAttribute('src', src);
1170
+ }
1171
+ if (styles) {
1172
+ const convertedStyle = convertToFrontendStyle(styles);
1173
+ for (const [styleName, styleValue] of Object.entries(convertedStyle)) {
1174
+ if (!styleValue)
1175
+ continue;
1176
+ const currentStyleValue = element.style.getPropertyValue(styleName);
1177
+ if (currentStyleValue !== styleValue) {
1178
+ element.style.setProperty(styleName, styleValue, 'important');
1179
+ }
1180
+ }
1181
+ }
1182
+ if (otherAttributes && Object.keys(otherAttributes).length > 0) {
1183
+ if (logger === null || logger === void 0 ? void 0 : logger.warn) {
1184
+ logger.warn('Unknown attributes', 'unknown_attributes', {
1185
+ ...logMetadata,
1186
+ otherAttributes,
1187
+ });
1188
+ }
1189
+ else {
1190
+ console.warn('[Coframe] Unknown attributes', otherAttributes);
1191
+ }
1192
+ }
1193
+ return true;
1194
+ };
1195
+ const executeJs = (jsCode, args = {}, opts = {}) => {
1196
+ const key = `__cf_${Math.random().toString(36).slice(2, 12)}`;
1197
+ const w = window;
1198
+ w.Coframe = w.Coframe || {};
1199
+ w.Coframe.vars = w.Coframe.vars || {};
1200
+ const elKey = `${key}_el`;
1201
+ if (opts.elementRef) {
1202
+ w.Coframe.vars[elKey] = opts.elementRef;
1203
+ }
1204
+ const invocation = opts.isExpression
1205
+ ? `(${jsCode})(__args)`
1206
+ : opts.elementRef
1207
+ ? `(function(el){ ${jsCode} })(__args.el)`
1208
+ : `(function(){ ${jsCode} })()`;
1209
+ const ranKey = `${key}_ran`;
1210
+ const script = document.createElement('script');
1211
+ script.id = `coframe-script-${key}`;
1212
+ if (opts.nonce)
1213
+ script.nonce = opts.nonce;
1214
+ script.textContent = `
1215
+ (function() {
1216
+ try {
1217
+ var __args = ${JSON.stringify(args)};
1218
+ ${opts.elementRef ? `__args.el = window.Coframe.vars[${JSON.stringify(elKey)}];` : ''}
1219
+ window.Coframe.vars[${JSON.stringify(key)}] = ${invocation};
1220
+ } catch (e) {
1221
+ window.Coframe.vars[${JSON.stringify(`${key}_err`)}] = (e && (e.stack || e.message)) ? String(e.stack || e.message) : (String(e) || 'Unknown error');
1222
+ }
1223
+ window.Coframe.vars[${JSON.stringify(ranKey)}] = true;
1224
+ })();
1225
+ `;
1226
+ document.head.appendChild(script);
1227
+ document.head.removeChild(script);
1228
+ const value = w.Coframe.vars[key];
1229
+ const err = w.Coframe.vars[`${key}_err`];
1230
+ const ran = w.Coframe.vars[ranKey];
1231
+ delete w.Coframe.vars[key];
1232
+ delete w.Coframe.vars[`${key}_err`];
1233
+ delete w.Coframe.vars[ranKey];
1234
+ if (opts.elementRef)
1235
+ delete w.Coframe.vars[elKey];
1236
+ if (err !== undefined)
1237
+ throw new Error(err);
1238
+ if (!ran) {
1239
+ throw new SyntaxError('Failed to parse JavaScript (likely a syntax error in variant code)');
1240
+ }
1241
+ return value;
1242
+ };
1243
+ const applyJavascriptModification = (element, args, logger, nonce, logMetadata = {}) => {
1244
+ const javascript = args.js;
1245
+ try {
1246
+ if (!javascript) {
1247
+ if (logger === null || logger === void 0 ? void 0 : logger.warn) {
1248
+ logger.warn('No JavaScript to execute', 'no_javascript_to_execute', logMetadata);
1249
+ }
1250
+ else {
1251
+ console.warn('[Coframe] No JavaScript to execute');
1252
+ }
1253
+ return false;
1254
+ }
1255
+ executeJs(javascript, {}, {
1256
+ nonce,
1257
+ elementRef: isPageElement(element) ? undefined : element,
1258
+ });
1259
+ return true;
1260
+ }
1261
+ catch (error) {
1262
+ if (logger === null || logger === void 0 ? void 0 : logger.error) {
1263
+ logger.error('Error executing JavaScript:', 'error_execute_js', {
1264
+ ...logMetadata,
1265
+ error,
1266
+ javascript,
1267
+ element,
1268
+ });
1269
+ }
1270
+ else {
1271
+ console.error('[Coframe] Error executing JavaScript:', error, javascript, element);
1272
+ }
1273
+ return false;
1274
+ }
1275
+ };
1276
+ const executeTags = (tags, logger, nonce) => {
1277
+ for (const tag of tags) {
1278
+ if (tag.js) {
1279
+ applyJavascriptModification(document.body, { js: tag.js }, logger, nonce);
1280
+ }
1281
+ }
1282
+ };
1283
+
1284
+ function getLogCallback() {
1285
+ const queue = window.CFQ;
1286
+ if (!queue) {
1287
+ return;
1288
+ }
1289
+ for (let i = queue.length - 1; i >= 0; i--) {
1290
+ const cmd = queue[i];
1291
+ if (!cmd || typeof cmd !== 'object') {
1292
+ continue;
1293
+ }
1294
+ if (cmd.subscribe === 'log' && cmd.logCallback) {
1295
+ return cmd.logCallback;
1296
+ }
1297
+ }
1298
+ }
1299
+ function emitLogCallback(callback, payload) {
1300
+ if (!callback) {
1301
+ return;
1302
+ }
1303
+ try {
1304
+ callback(payload);
1305
+ }
1306
+ catch (e) {
1307
+ console.warn('[Coframe] Error in log callback:', e);
1308
+ }
1309
+ }
1310
+ function reportException({ err, orgId, projectId, sessionId, }) {
1311
+ let errorMessage = 'Unknown error';
1312
+ let stack = '';
1313
+ if (typeof err === 'string') {
1314
+ errorMessage = err;
1315
+ }
1316
+ else if (err && typeof err === 'object') {
1317
+ if ('message' in err && typeof err.message === 'string') {
1318
+ errorMessage = err.message;
1319
+ }
1320
+ if ('stack' in err && typeof err.stack === 'string') {
1321
+ stack = err.stack;
1322
+ }
1323
+ }
1324
+ if (!stack) {
1325
+ stack = new Error().stack || '';
1326
+ }
1327
+ try {
1328
+ const payload = {
1329
+ kind: 'exception',
1330
+ org_id: orgId,
1331
+ project_id: projectId,
1332
+ event_id: generateUUID(),
1333
+ variant_id: '',
1334
+ experiment_id: '',
1335
+ session_id: sessionId,
1336
+ url: window.location.href,
1337
+ monitoring_type: 'sdk_error',
1338
+ local_time: new Date().toISOString(),
1339
+ metadata: {
1340
+ error_message: errorMessage,
1341
+ stack,
1342
+ user_agent: navigator.userAgent,
1343
+ },
1344
+ };
1345
+ emitLogCallback(getLogCallback(), payload);
1346
+ if (canSendWithConsent()) {
1347
+ sendEventToBackend(projectId, payload, 'monitoring');
1348
+ }
1349
+ }
1350
+ catch (e) {
1351
+ console.warn('[Coframe] Error in reportException:', e);
1352
+ }
1353
+ }
1354
+ const LCP_ENTRY_TYPE = 'largest-contentful-paint';
1355
+ function reportLCP({ coframe, projectId, orgId, sessionId, }) {
1356
+ var _a;
1357
+ if (typeof PerformanceObserver === 'undefined' ||
1358
+ !((_a = PerformanceObserver.supportedEntryTypes) === null || _a === void 0 ? void 0 : _a.includes(LCP_ENTRY_TYPE))) {
1359
+ return;
1360
+ }
1361
+ let latestEntry;
1362
+ let hasReported = false;
1363
+ const observer = new PerformanceObserver((list) => {
1364
+ const entries = list.getEntries();
1365
+ if (entries.length > 0) {
1366
+ latestEntry = entries[entries.length - 1];
1367
+ }
1368
+ });
1369
+ try {
1370
+ observer.observe({ type: LCP_ENTRY_TYPE, buffered: true });
1371
+ }
1372
+ catch (err) {
1373
+ console.warn('[Coframe] Failed to observe LCP:', err);
1374
+ return;
1375
+ }
1376
+ document.addEventListener('visibilitychange', () => {
1377
+ var _a, _b, _c, _d;
1378
+ if (document.visibilityState === 'hidden' && !hasReported) {
1379
+ const pending = observer.takeRecords();
1380
+ if (pending.length > 0) {
1381
+ latestEntry = pending[pending.length - 1];
1382
+ }
1383
+ if (latestEntry) {
1384
+ try {
1385
+ const lcp = latestEntry;
1386
+ queueMonitoringEvent(coframe, {
1387
+ event_id: generateUUID(),
1388
+ monitoring_type: 'web_performance_lcp',
1389
+ local_time: new Date().toISOString(),
1390
+ project_id: projectId,
1391
+ session_id: sessionId,
1392
+ url: getCurrentPageUrl(),
1393
+ org_id: orgId,
1394
+ metadata: {
1395
+ lcp: lcp.startTime,
1396
+ size: (_a = lcp.size) !== null && _a !== void 0 ? _a : 0,
1397
+ element: (_c = (_b = lcp.element) === null || _b === void 0 ? void 0 : _b.tagName) !== null && _c !== void 0 ? _c : '',
1398
+ url: (_d = lcp.url) !== null && _d !== void 0 ? _d : '',
1399
+ },
1400
+ });
1401
+ hasReported = true;
1402
+ }
1403
+ catch (e) {
1404
+ console.warn('[Coframe] Error reporting LCP:', e);
1405
+ }
1406
+ }
1407
+ observer.disconnect();
1408
+ }
1409
+ });
1410
+ }
1411
+ function createLogger(getContext) {
1412
+ let totalLogCount = 0;
1413
+ const MAX_LOG_COUNT = 15;
1414
+ function logEvent(kind, getContext, message, type, metadata) {
1415
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1416
+ if (totalLogCount >= MAX_LOG_COUNT) {
1417
+ console.warn('[Coframe] Max log count reached, auto-mute');
1418
+ return;
1419
+ }
1420
+ totalLogCount++;
1421
+ const tag = '[Coframe]';
1422
+ const context = getContext();
1423
+ const trimMessage = (message !== null && message !== void 0 ? message : '').trim();
1424
+ const { org_id: _orgId, variant_id = '', experiment_id = '', ...restMetadata } = metadata;
1425
+ const now = Date.now();
1426
+ const localTime = new Date(now).toISOString();
1427
+ const variantIds = ((_a = context.variantsAndElements) !== null && _a !== void 0 ? _a : [])
1428
+ .map((v) => v.variant.id)
1429
+ .filter(Boolean);
1430
+ const singleVariant = ((_c = (_b = context.variantsAndElements) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) === 1
1431
+ ? (_e = (_d = context.variantsAndElements) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.variant
1432
+ : undefined;
1433
+ const data = {
1434
+ kind,
1435
+ org_id: context.orgId || '',
1436
+ variant_id: variant_id || (singleVariant === null || singleVariant === void 0 ? void 0 : singleVariant.id) || '',
1437
+ experiment_id: experiment_id || (singleVariant === null || singleVariant === void 0 ? void 0 : singleVariant.experiment_id) || '',
1438
+ variant_ids: variantIds,
1439
+ project_id: (_f = context.projectId) !== null && _f !== void 0 ? _f : '',
1440
+ event_id: generateUUID(),
1441
+ session_id: (_g = context.sessionId) !== null && _g !== void 0 ? _g : '',
1442
+ url: getCurrentPageUrl(),
1443
+ monitoring_type: `log_${type}`,
1444
+ local_time: localTime,
1445
+ metadata: {
1446
+ ...restMetadata,
1447
+ message: trimMessage,
1448
+ },
1449
+ };
1450
+ if (kind === 'warn') {
1451
+ console.warn(`${tag} ${trimMessage}`, { ...restMetadata });
1452
+ }
1453
+ else {
1454
+ console.error(`${tag} ${trimMessage}`, { ...restMetadata });
1455
+ }
1456
+ emitLogCallback(getLogCallback(), data);
1457
+ (_j = (_h = context.debug) === null || _h === void 0 ? void 0 : _h.logs) === null || _j === void 0 ? void 0 : _j.push({
1458
+ logger: kind,
1459
+ timestamp: now,
1460
+ message: trimMessage,
1461
+ type,
1462
+ metadata: { ...restMetadata },
1463
+ });
1464
+ queueMonitoringEvent(context, data);
1465
+ if (context.status) {
1466
+ if (!context.status.events) {
1467
+ context.status.events = [];
1468
+ }
1469
+ context.status.events.push({
1470
+ ...data,
1471
+ timestamp: now,
1472
+ });
1473
+ }
1474
+ }
1475
+ return {
1476
+ warn: (message, type = 'coframe_warn', metadata = {}) => logEvent('warn', getContext, message, type, metadata),
1477
+ error: (message, type = 'coframe_error', metadata = {}) => logEvent('error', getContext, message, type, metadata),
1478
+ };
1479
+ }
1480
+
1481
+ const GLOBAL_HOLDOUT = 'global_holdout';
1482
+ const EXPERIMENT_HOLDOUT = 'experiment_holdout';
1483
+ const EXPERIMENT = 'experiment';
1484
+ const QueueName = {
1485
+ CFQ: 'CFQ'};
1486
+
1487
+ function ensureQueueExists(queueName = QueueName.CFQ) {
1488
+ var _a;
1489
+ const descriptor = Object.getOwnPropertyDescriptor(window, queueName);
1490
+ if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) {
1491
+ return window[queueName];
1492
+ }
1493
+ const queue = (_a = window[queueName]) !== null && _a !== void 0 ? _a : [];
1494
+ Object.defineProperty(window, queueName, {
1495
+ get() {
1496
+ return queue;
1497
+ },
1498
+ set(val) {
1499
+ if (val !== queue) {
1500
+ console.warn(`[Coframe] Attempt to reassign ${queueName} queue — assignment ignored. Use .push() instead.`);
1501
+ }
1502
+ },
1503
+ configurable: false,
1504
+ });
1505
+ return queue;
1506
+ }
1507
+ function __ensureQueuePushListenerHooked(queueName = QueueName.CFQ) {
1508
+ const queue = ensureQueueExists(queueName);
1509
+ if (queue.__cfqHooked) {
1510
+ return queue;
1511
+ }
1512
+ queue.__cfqHooked = true;
1513
+ queue.__cfqListeners = [];
1514
+ const originalPush = queue.push.bind(queue);
1515
+ queue.push = (...items) => {
1516
+ const len = originalPush(...items);
1517
+ const listeners = queue.__cfqListeners;
1518
+ for (let i = 0; i < items.length; i++) {
1519
+ const cmd = items[i];
1520
+ const snapshot = listeners.slice();
1521
+ for (let j = 0; j < snapshot.length; j++) {
1522
+ const l = snapshot[j];
1523
+ if (l.predicate(cmd))
1524
+ l.callback(cmd);
1525
+ }
1526
+ }
1527
+ return len;
1528
+ };
1529
+ return queue;
1530
+ }
1531
+ function __addListener(predicate, callback, queueName = QueueName.CFQ) {
1532
+ const queue = __ensureQueuePushListenerHooked(queueName);
1533
+ const entry = { predicate, callback };
1534
+ queue.__cfqListeners.push(entry);
1535
+ let active = true;
1536
+ return () => {
1537
+ if (!active)
1538
+ return;
1539
+ active = false;
1540
+ const arr = queue.__cfqListeners;
1541
+ const idx = arr.indexOf(entry);
1542
+ if (idx !== -1)
1543
+ arr.splice(idx, 1);
1544
+ };
1545
+ }
1546
+ function findLastCmd(predicate, queueName = QueueName.CFQ) {
1547
+ const queue = ensureQueueExists(queueName);
1548
+ for (let i = queue.length - 1; i >= 0; i--) {
1549
+ if (predicate(queue[i])) {
1550
+ return queue[i];
1551
+ }
1552
+ }
1553
+ return undefined;
1554
+ }
1555
+ function waitForCmd(predicate, coframe, timeoutMs = 3600000, queueName = QueueName.CFQ) {
1556
+ return new Promise((resolve) => {
1557
+ const existing = findLastCmd(predicate, queueName);
1558
+ if (existing) {
1559
+ resolve(existing);
1560
+ return;
1561
+ }
1562
+ const stop = __addListener(predicate, (cmd) => {
1563
+ stop();
1564
+ clearTimeout(timer);
1565
+ resolve(cmd);
1566
+ }, queueName);
1567
+ const timer = setTimeout(() => {
1568
+ stop();
1569
+ const error = new Error(`Timeout waiting for command after ${timeoutMs}ms for:\n${predicate.toString()}`);
1570
+ if ((coframe === null || coframe === void 0 ? void 0 : coframe.projectId) && (coframe === null || coframe === void 0 ? void 0 : coframe.sessionId)) {
1571
+ reportException({
1572
+ err: error,
1573
+ orgId: coframe.orgId || '',
1574
+ projectId: coframe.projectId,
1575
+ sessionId: coframe.sessionId,
1576
+ });
1577
+ }
1578
+ resolve(undefined);
1579
+ }, timeoutMs);
1580
+ });
1581
+ }
1582
+ function listenForCmd(predicate, callback, queueName = QueueName.CFQ) {
1583
+ const queue = ensureQueueExists(queueName);
1584
+ for (let i = 0; i < queue.length; i++) {
1585
+ const cmd = queue[i];
1586
+ if (predicate(cmd))
1587
+ callback(cmd);
1588
+ }
1589
+ return __addListener(predicate, callback, queueName);
1590
+ }
1591
+ function subscribeToCmd(predicate, callback, queueName = QueueName.CFQ) {
1592
+ return __addListener(predicate, callback, queueName);
1593
+ }
1594
+
1595
+ function querySelector(config) {
1596
+ const result = findElementsRecursive(config);
1597
+ if (result !== null && result.length === 0) {
1598
+ return null;
1599
+ }
1600
+ return result;
1601
+ }
1602
+ function handleAndCondition(children, currentUrl, isQueryingAll) {
1603
+ if (children.length === 0)
1604
+ return null;
1605
+ const childResultsForAnd = [];
1606
+ for (const condition of children) {
1607
+ const currentChildResult = findElementsRecursive({
1608
+ selector: condition,
1609
+ currentUrl: currentUrl,
1610
+ isQueryingAll,
1611
+ });
1612
+ if (currentChildResult === null) {
1613
+ return null;
1614
+ }
1615
+ childResultsForAnd.push(currentChildResult);
1616
+ }
1617
+ for (let i = childResultsForAnd.length - 1; i >= 0; i--) {
1618
+ const res = childResultsForAnd[i];
1619
+ if (res && res.length > 0) {
1620
+ return res;
1621
+ }
1622
+ }
1623
+ return [];
1624
+ }
1625
+ function handleOrCondition(children, currentUrl, isQueryingAll) {
1626
+ let accumulatedElementsForOrAll = null;
1627
+ let orConditionMet = false;
1628
+ for (const condition of children) {
1629
+ const currentChildResult = findElementsRecursive({
1630
+ selector: condition,
1631
+ currentUrl: currentUrl,
1632
+ isQueryingAll,
1633
+ });
1634
+ if (currentChildResult !== null) {
1635
+ orConditionMet = true;
1636
+ if (!isQueryingAll) {
1637
+ if (currentChildResult.length > 0) {
1638
+ return currentChildResult;
1639
+ }
1640
+ }
1641
+ else {
1642
+ if (currentChildResult.length > 0) {
1643
+ if (accumulatedElementsForOrAll === null) {
1644
+ accumulatedElementsForOrAll = [];
1645
+ }
1646
+ accumulatedElementsForOrAll.push(...currentChildResult);
1647
+ }
1648
+ }
1649
+ }
1650
+ }
1651
+ if (!isQueryingAll) {
1652
+ return null;
1653
+ }
1654
+ if (orConditionMet) {
1655
+ return accumulatedElementsForOrAll !== null
1656
+ ? accumulatedElementsForOrAll
1657
+ : [];
1658
+ }
1659
+ return null;
1660
+ }
1661
+ function findElementsRecursive({ selector, currentUrl, isQueryingAll = false, }) {
1662
+ if (selector.url_starts_with) {
1663
+ return currentUrl.startsWith(selector.url_starts_with) ? [] : null;
1664
+ }
1665
+ if (selector.url) {
1666
+ const url = new URL(currentUrl);
1667
+ const targetUrl = new URL(selector.url);
1668
+ return url.hostname === targetUrl.hostname &&
1669
+ url.pathname === targetUrl.pathname
1670
+ ? []
1671
+ : null;
1672
+ }
1673
+ if (selector.universal || selector.css) {
1674
+ const element = getElement(selector);
1675
+ return element ? [element] : null;
1676
+ }
1677
+ if (selector.navigate_to_url_starts_with) {
1678
+ const elements = getElementsByHrefPrefix(currentUrl, selector.navigate_to_url_starts_with);
1679
+ return elements.length > 0 ? elements : null;
1680
+ }
1681
+ if (selector.and) {
1682
+ return handleAndCondition(selector.and, currentUrl, isQueryingAll);
1683
+ }
1684
+ if (selector.or) {
1685
+ return handleOrCondition(selector.or, currentUrl, isQueryingAll);
1686
+ }
1687
+ return null;
1688
+ }
1689
+ let applyVariantChangesStarted = false;
1690
+ function setApplyVariantChangesStarted(value) {
1691
+ applyVariantChangesStarted = value;
1692
+ }
1693
+ function variantRendered() {
1694
+ const queue = ensureQueueExists();
1695
+ queue.push({
1696
+ emit: 'variantRendered',
1697
+ });
1698
+ }
1699
+ const applyVariantChanges = (variantElements, currentUrl, logger, nonce) => {
1700
+ var _a;
1701
+ const { variant, elements } = variantElements;
1702
+ const variantMetadata = {
1703
+ variant_id: variant.id,
1704
+ experiment_id: variant.experiment_id,
1705
+ };
1706
+ setApplyVariantChangesStarted(true);
1707
+ if (!variant.modifications ||
1708
+ Object.keys(variant.modifications).length === 0) {
1709
+ variantRendered();
1710
+ return true;
1711
+ }
1712
+ for (const [elementId, _] of Object.entries(variant.modifications)) {
1713
+ const elementSelectors = elements[elementId];
1714
+ let selectedElement = null;
1715
+ for (const selector of elementSelectors || []) {
1716
+ selectedElement = (_a = querySelector({ selector, currentUrl })) === null || _a === void 0 ? void 0 : _a[0];
1717
+ if (selectedElement)
1718
+ break;
1719
+ }
1720
+ if (!selectedElement) {
1721
+ logger.error(`element not found: elementId ${elementId}`, 'element_not_found', {
1722
+ element_id: elementId,
1723
+ selectors: elementSelectors,
1724
+ variant_id: variant.id,
1725
+ experiment_id: variant.experiment_id,
1726
+ });
1727
+ continue;
1728
+ }
1729
+ const edited = editElement(selectedElement, variant.modifications, elementId, logger, nonce, variantMetadata);
1730
+ if (edited && selectedElement instanceof HTMLElement) {
1731
+ selectedElement.setAttribute('coframe-exp-id', variant.id);
1732
+ }
1733
+ else {
1734
+ logger.error('element not edited', 'element_not_edited', {
1735
+ ...variantMetadata,
1736
+ selectedElement,
1737
+ elementSelectors,
1738
+ });
1739
+ }
1740
+ }
1741
+ return true;
1742
+ };
1743
+ const editElement = (element, modifications, elementId, logger, nonce, logMetadata = {}) => {
1744
+ if (!modifications[elementId]) {
1745
+ logger.warn('no modifications for element', 'no_modifications_for_element', {
1746
+ ...logMetadata,
1747
+ elementId,
1748
+ modifications,
1749
+ });
1750
+ return false;
1751
+ }
1752
+ for (const content of modifications[elementId]) {
1753
+ switch (content.type) {
1754
+ case 'execute_js':
1755
+ applyJavascriptModification(element, content, logger, nonce, logMetadata);
1756
+ break;
1757
+ case 'update':
1758
+ applyUpdateModification(element, content, logger, logMetadata);
1759
+ variantRendered();
1760
+ break;
1761
+ case 'delete':
1762
+ deleteElement(element);
1763
+ variantRendered();
1764
+ break;
1765
+ default:
1766
+ logger.warn('unknown modification type', 'unknown_modification_type', {
1767
+ ...logMetadata,
1768
+ modificationType: content.type,
1769
+ elementId,
1770
+ });
1771
+ variantRendered();
1772
+ return false;
1773
+ }
1774
+ }
1775
+ return true;
1776
+ };
1777
+ const deleteElement = (element) => {
1778
+ var _a;
1779
+ (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(element);
1780
+ return true;
1781
+ };
1782
+ const waitForBody = (timeout = 10000) => {
1783
+ return new Promise((resolve, reject) => {
1784
+ if (document.body) {
1785
+ resolve(document.body);
1786
+ return;
1787
+ }
1788
+ let timeoutId;
1789
+ const observer = new MutationObserver(() => {
1790
+ if (document.body) {
1791
+ if (timeoutId)
1792
+ clearTimeout(timeoutId);
1793
+ observer.disconnect();
1794
+ resolve(document.body);
1795
+ }
1796
+ });
1797
+ timeoutId = setTimeout(() => {
1798
+ observer.disconnect();
1799
+ reject(new Error('Timeout waiting for document.body'));
1800
+ }, timeout);
1801
+ observer.observe(document.documentElement || document, {
1802
+ childList: true,
1803
+ subtree: true,
1804
+ });
1805
+ });
1806
+ };
1807
+ function revealPage() {
1808
+ document.dispatchEvent(new CustomEvent('coframe:show', {}));
1809
+ document.documentElement.style.setProperty('--cf-show', '1');
1810
+ }
1811
+ async function replaceVariants(variants, orgId, logger, nonce) {
1812
+ const failedReplacements = [];
1813
+ let currentVariant;
1814
+ try {
1815
+ for (const v of variants) {
1816
+ currentVariant = v.variant;
1817
+ if (!applyVariantChanges(v, getCurrentPageUrl(), logger, nonce)) {
1818
+ failedReplacements.push({
1819
+ message: 'Original element not found',
1820
+ metadata: {
1821
+ description: 'Original element not found',
1822
+ experiment_id: v.variant.experiment_id,
1823
+ variant_id: v.variant.id,
1824
+ org_id: orgId,
1825
+ },
1826
+ });
1827
+ }
1828
+ }
1829
+ }
1830
+ catch (error) {
1831
+ const errorMessage = error instanceof Error ? error.stack || error.message : String(error);
1832
+ failedReplacements.push({
1833
+ metadata: {
1834
+ description: error instanceof Error ? error.toString() : String(error),
1835
+ org_id: orgId,
1836
+ variant_id: currentVariant === null || currentVariant === void 0 ? void 0 : currentVariant.id,
1837
+ experiment_id: currentVariant === null || currentVariant === void 0 ? void 0 : currentVariant.experiment_id,
1838
+ },
1839
+ message: errorMessage,
1840
+ });
1841
+ }
1842
+ for (const event of failedReplacements) {
1843
+ logger.error(event.message, 'failed_replacement', event.metadata);
1844
+ }
1845
+ }
1846
+
1847
+ function _x86Multiply(m, n) {
1848
+ return (m & 0xffff) * n + ((((m >>> 16) * n) & 0xffff) << 16);
1849
+ }
1850
+ function _x86Rotl(m, n) {
1851
+ return (m << n) | (m >>> (32 - n));
1852
+ }
1853
+ function _x86Fmix(h) {
1854
+ h ^= h >>> 16;
1855
+ h = _x86Multiply(h, 0x85ebca6b);
1856
+ h ^= h >>> 13;
1857
+ h = _x86Multiply(h, 0xc2b2ae35);
1858
+ h ^= h >>> 16;
1859
+ return h;
1860
+ }
1861
+ function murmurHash3_x86_32(key, seed) {
1862
+ key = key || '';
1863
+ seed = seed || 0;
1864
+ const remainder = key.length % 4;
1865
+ const bytes = key.length - remainder;
1866
+ let h1 = seed;
1867
+ let k1 = 0;
1868
+ const c1 = 0xcc9e2d51;
1869
+ const c2 = 0x1b873593;
1870
+ let i = 0;
1871
+ for (i = 0; i < bytes; i = i + 4) {
1872
+ k1 =
1873
+ (key.charCodeAt(i) & 0xff) |
1874
+ ((key.charCodeAt(i + 1) & 0xff) << 8) |
1875
+ ((key.charCodeAt(i + 2) & 0xff) << 16) |
1876
+ ((key.charCodeAt(i + 3) & 0xff) << 24);
1877
+ k1 = _x86Multiply(k1, c1);
1878
+ k1 = _x86Rotl(k1, 15);
1879
+ k1 = _x86Multiply(k1, c2);
1880
+ h1 ^= k1;
1881
+ h1 = _x86Rotl(h1, 13);
1882
+ h1 = _x86Multiply(h1, 5) + 0xe6546b64;
1883
+ }
1884
+ k1 = 0;
1885
+ switch (remainder) {
1886
+ case 3:
1887
+ k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
1888
+ case 2:
1889
+ k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
1890
+ case 1:
1891
+ k1 ^= key.charCodeAt(i) & 0xff;
1892
+ k1 = _x86Multiply(k1, c1);
1893
+ k1 = _x86Rotl(k1, 15);
1894
+ k1 = _x86Multiply(k1, c2);
1895
+ h1 ^= k1;
1896
+ }
1897
+ h1 ^= key.length;
1898
+ h1 = _x86Fmix(h1);
1899
+ return h1 >>> 0;
1900
+ }
1901
+
1902
+ function parseClaims(raw) {
1903
+ if (!raw)
1904
+ return {};
1905
+ try {
1906
+ const parsed = JSON.parse(raw);
1907
+ const out = {};
1908
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
1909
+ for (const laneId of Object.keys(parsed)) {
1910
+ const c = parsed[laneId];
1911
+ if (c && typeof c.e === 'string' && typeof c.i === 'string') {
1912
+ out[laneId] = { e: c.e, i: c.i };
1913
+ }
1914
+ }
1915
+ }
1916
+ return out;
1917
+ }
1918
+ catch (_a) {
1919
+ return {};
1920
+ }
1921
+ }
1922
+ function serializeClaims(claims) {
1923
+ return JSON.stringify(claims);
1924
+ }
1925
+ function validateClaims(claims, lanes) {
1926
+ const valid = {};
1927
+ if (!lanes)
1928
+ return valid;
1929
+ for (const laneId of Object.keys(claims)) {
1930
+ const claim = claims[laneId];
1931
+ const lane = lanes[laneId];
1932
+ if (!lane)
1933
+ continue;
1934
+ const member = lane.members.find((m) => m.experiment_id === claim.e);
1935
+ if (!member)
1936
+ continue;
1937
+ if (member.iteration_id !== claim.i)
1938
+ continue;
1939
+ valid[laneId] = claim;
1940
+ }
1941
+ return valid;
1942
+ }
1943
+ function experimentLaneIds(metadata) {
1944
+ var _a;
1945
+ return (_a = metadata.lane_ids) !== null && _a !== void 0 ? _a : [];
1946
+ }
1947
+ function makeClaim(metadata) {
1948
+ return { e: metadata.experiment_id, i: metadata.iteration_id };
1949
+ }
1950
+ function pickWinner(reachableMemberIds, seed, userToken) {
1951
+ const sorted = [...reachableMemberIds].sort();
1952
+ if (sorted.length <= 1)
1953
+ return sorted[0];
1954
+ const hash = murmurHash3_x86_32(userToken, seed);
1955
+ return sorted[hash % sorted.length];
1956
+ }
1957
+ function resolveLaneWinners(claims, reachableByLane, lanes, userToken) {
1958
+ var _a, _b;
1959
+ const excluded = new Set();
1960
+ for (const laneId of Object.keys(reachableByLane)) {
1961
+ const reachable = reachableByLane[laneId];
1962
+ if (reachable.length === 0)
1963
+ continue;
1964
+ const claim = claims[laneId];
1965
+ const seed = (_b = (_a = lanes === null || lanes === void 0 ? void 0 : lanes[laneId]) === null || _a === void 0 ? void 0 : _a.seed) !== null && _b !== void 0 ? _b : 0;
1966
+ const winner = claim ? claim.e : pickWinner(reachable, seed, userToken);
1967
+ for (const experimentId of reachable) {
1968
+ if (experimentId !== winner)
1969
+ excluded.add(experimentId);
1970
+ }
1971
+ }
1972
+ return excluded;
1973
+ }
1974
+
1975
+ function isElementVisible(el) {
1976
+ const rect = el.getBoundingClientRect();
1977
+ return rect.top >= 0 && rect.bottom <= window.innerHeight;
1978
+ }
1979
+ const VALUE_UNKNOWN_YET = 'n/a';
1980
+ function checkActivationCondition(condition, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger) {
1981
+ if (condition.and) {
1982
+ const results = condition.and.map((c) => checkActivationCondition(c, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger));
1983
+ const hasUnknown = results.some((r) => r === VALUE_UNKNOWN_YET);
1984
+ if (hasUnknown) {
1985
+ const hasFalse = results.some((r) => r === false);
1986
+ if (hasUnknown && hasFalse) {
1987
+ return false;
1988
+ }
1989
+ if (hasUnknown && !hasFalse) {
1990
+ return VALUE_UNKNOWN_YET;
1991
+ }
1992
+ }
1993
+ return results.every((r) => r === true);
1994
+ }
1995
+ if (condition.or) {
1996
+ const results = condition.or.map((c) => checkActivationCondition(c, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger));
1997
+ const hasAtLeastOneTrue = results.some((r) => r === true);
1998
+ if (hasAtLeastOneTrue) {
1999
+ return true;
2000
+ }
2001
+ const hasUnknown = results.some((r) => r === VALUE_UNKNOWN_YET);
2002
+ return hasUnknown ? VALUE_UNKNOWN_YET : false;
2003
+ }
2004
+ if (condition.url ||
2005
+ condition.base_url_matches ||
2006
+ condition.url_regex ||
2007
+ condition.search) {
2008
+ if (currentUrl === VALUE_UNKNOWN_YET) {
2009
+ return VALUE_UNKNOWN_YET;
2010
+ }
2011
+ }
2012
+ if (condition.url &&
2013
+ condition.url !== '*' &&
2014
+ !urlsMatch(condition.url, currentUrl)) {
2015
+ return false;
2016
+ }
2017
+ if (condition.base_url_matches &&
2018
+ !urlsMatch(condition.base_url_matches, currentUrl, true)) {
2019
+ return false;
2020
+ }
2021
+ if (condition.url_regex) {
2022
+ try {
2023
+ const regex = new RegExp(condition.url_regex);
2024
+ return regex.test(currentUrl);
2025
+ }
2026
+ catch (error) {
2027
+ logger.error('Error parsing URL regex', 'url_regex_error', {
2028
+ error: error instanceof Error ? error.message : String(error),
2029
+ url_regex: condition.url_regex,
2030
+ currentUrl,
2031
+ });
2032
+ return false;
2033
+ }
2034
+ }
2035
+ if (condition.search && !queryParamsMatch(condition.search, currentUrl)) {
2036
+ return false;
2037
+ }
2038
+ if (condition.user_context &&
2039
+ !userContextConditionsMatch(condition.user_context, personalizationHeaders)) {
2040
+ return false;
2041
+ }
2042
+ if (condition.language) {
2043
+ if (languages === VALUE_UNKNOWN_YET) {
2044
+ return VALUE_UNKNOWN_YET;
2045
+ }
2046
+ for (const language of condition.language) {
2047
+ if (languages.some((l) => l.includes(language))) {
2048
+ return true;
2049
+ }
2050
+ }
2051
+ return false;
2052
+ }
2053
+ if (condition.time) {
2054
+ const currentTime = new Date();
2055
+ if (condition.time.before && currentTime >= condition.time.before) {
2056
+ return false;
2057
+ }
2058
+ if (condition.time.after && currentTime <= condition.time.after) {
2059
+ return false;
2060
+ }
2061
+ }
2062
+ if (condition.platform) {
2063
+ if (browserPlatform === VALUE_UNKNOWN_YET) {
2064
+ return VALUE_UNKNOWN_YET;
2065
+ }
2066
+ return isPlatformSupported(condition, browserPlatform);
2067
+ }
2068
+ if (condition.country &&
2069
+ !isCountrySupported(condition.country, personalizationHeaders)) {
2070
+ return false;
2071
+ }
2072
+ if (condition.first_time_vs_returning) {
2073
+ if (userVisitCount === VALUE_UNKNOWN_YET) {
2074
+ return true;
2075
+ }
2076
+ if (condition.first_time_vs_returning.length !== 2) {
2077
+ if (condition.first_time_vs_returning.includes('first_time') &&
2078
+ userVisitCount > 1) {
2079
+ return false;
2080
+ }
2081
+ if (condition.first_time_vs_returning.includes('returning') &&
2082
+ userVisitCount <= 1) {
2083
+ return false;
2084
+ }
2085
+ }
2086
+ }
2087
+ if (condition.device) {
2088
+ const isMobileDevice = personalizationHeaders['x-is-mobile-viewer'] === 'true';
2089
+ if (condition.device.includes('mobile') && !isMobileDevice) {
2090
+ return false;
2091
+ }
2092
+ if (condition.device.includes('desktop') && isMobileDevice) {
2093
+ return false;
2094
+ }
2095
+ }
2096
+ if (condition.allocation) {
2097
+ if (userToken === VALUE_UNKNOWN_YET) {
2098
+ return VALUE_UNKNOWN_YET;
2099
+ }
2100
+ const inAllocation = inRange(condition.allocation.allocation_bins, condition.allocation.allocation_scale, condition.allocation.seed, userToken);
2101
+ return inAllocation;
2102
+ }
2103
+ if (condition.js_function) {
2104
+ if (runJsFunction === undefined) {
2105
+ return VALUE_UNKNOWN_YET;
2106
+ }
2107
+ try {
2108
+ const result = runJsFunction(`(${condition.js_function})`, {});
2109
+ return Boolean(result);
2110
+ }
2111
+ catch (error) {
2112
+ logger.error('Error evaluating JS function', 'js_eval_error', {
2113
+ error: error instanceof Error ? error.message : String(error),
2114
+ js_function: condition.js_function,
2115
+ });
2116
+ return false;
2117
+ }
2118
+ }
2119
+ return true;
2120
+ }
2121
+ const normalizeUrl = (url, baseUrlMatch) => {
2122
+ const parsedUrl = new URL(url);
2123
+ const hostname = parsedUrl.hostname.replace(/^www\./, '');
2124
+ const pathname = parsedUrl.pathname.endsWith('/')
2125
+ ? parsedUrl.pathname.slice(0, -1)
2126
+ : parsedUrl.pathname;
2127
+ if (baseUrlMatch) {
2128
+ return `${hostname}${pathname}`;
2129
+ }
2130
+ return `${hostname}${pathname}${parsedUrl.search}${parsedUrl.hash}`;
2131
+ };
2132
+ function urlsMatch(conditionUrl, currentUrl, baseUrlMatch) {
2133
+ if (currentUrl.includes('localhost') || currentUrl.includes('0.0.0.0')) {
2134
+ const conditionUrlObj = new URL(conditionUrl);
2135
+ const currentUrlObj = new URL(currentUrl);
2136
+ const normalizePath = (path) => path.endsWith('/') ? path.slice(0, -1) : path;
2137
+ if (baseUrlMatch) {
2138
+ return (normalizePath(currentUrlObj.pathname) ===
2139
+ normalizePath(conditionUrlObj.pathname));
2140
+ }
2141
+ return (normalizePath(currentUrlObj.pathname) ===
2142
+ normalizePath(conditionUrlObj.pathname) &&
2143
+ currentUrlObj.search === conditionUrlObj.search);
2144
+ }
2145
+ return (normalizeUrl(conditionUrl, baseUrlMatch) ===
2146
+ normalizeUrl(currentUrl, baseUrlMatch));
2147
+ }
2148
+ function isPlatformSupported(condition, browserPlatform) {
2149
+ const conditionPlatforms = Array.isArray(condition.platform)
2150
+ ? condition.platform
2151
+ : [condition.platform];
2152
+ return conditionPlatforms.some((condPlatform) => condPlatform &&
2153
+ browserPlatform.toLowerCase().includes(condPlatform.toLowerCase()));
2154
+ }
2155
+ function isCountrySupported(countries, personalizationHeaders) {
2156
+ return personalizationHeaders
2157
+ ? countries.includes(personalizationHeaders['x-viewer-country'])
2158
+ : false;
2159
+ }
2160
+ function queryParamsMatch(conditions, currentUrl) {
2161
+ const search = new URL(currentUrl).search;
2162
+ return conditions.some((c) => {
2163
+ const q = decodeURIComponent(search).toLowerCase();
2164
+ const operand = c.operand.toLowerCase();
2165
+ if (c.operator === 'equals')
2166
+ return q === operand;
2167
+ if (c.operator === 'contains')
2168
+ return q.includes(operand);
2169
+ if (c.operator === 'not_contains')
2170
+ return !q.includes(operand);
2171
+ return false;
2172
+ });
2173
+ }
2174
+ function userContextConditionsMatch(conditions, personalizationHeaders) {
2175
+ const headers = personalizationHeaders;
2176
+ return conditions.some((c) => {
2177
+ var _a;
2178
+ if (!c.key)
2179
+ return false;
2180
+ const value = ((_a = headers[`${USER_CONTEXT_KEY_PREFIX}${c.key}`]) !== null && _a !== void 0 ? _a : '').toLowerCase();
2181
+ const operand = c.operand.toLowerCase();
2182
+ if (c.operator === 'equals')
2183
+ return value === operand;
2184
+ if (c.operator === 'contains')
2185
+ return value.includes(operand);
2186
+ if (c.operator === 'not_contains')
2187
+ return !value.includes(operand);
2188
+ return false;
2189
+ });
2190
+ }
2191
+ function debounce(func, wait) {
2192
+ let timeoutId = null;
2193
+ return function (...args) {
2194
+ if (timeoutId) {
2195
+ clearTimeout(timeoutId);
2196
+ }
2197
+ timeoutId = setTimeout(() => {
2198
+ func.apply(this, args);
2199
+ }, wait);
2200
+ };
2201
+ }
2202
+ function parseQueryString(query) {
2203
+ const params = {};
2204
+ const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
2205
+ if (!cleanQuery)
2206
+ return params;
2207
+ for (const pair of cleanQuery.split('&')) {
2208
+ if (!pair)
2209
+ continue;
2210
+ const [key, value = ''] = pair.split('=').map(decodeURIComponent);
2211
+ if (!(key in params)) {
2212
+ params[key] = value;
2213
+ }
2214
+ }
2215
+ return params;
2216
+ }
2217
+ function createURLSearchParams(query) {
2218
+ if (typeof URLSearchParams !== 'undefined') {
2219
+ const nativeParams = new URLSearchParams(query);
2220
+ return {
2221
+ get: (key) => nativeParams.get(key),
2222
+ };
2223
+ }
2224
+ const simpleParams = parseQueryString(query);
2225
+ return {
2226
+ get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
2227
+ };
2228
+ }
2229
+ function getCurrentUrlSearchParams() {
2230
+ const url = getCurrentPageUrl();
2231
+ const searchIndex = url.indexOf('?');
2232
+ if (searchIndex === -1) {
2233
+ return createURLSearchParams('');
2234
+ }
2235
+ const hashIndex = url.indexOf('#', searchIndex);
2236
+ const search = hashIndex !== -1
2237
+ ? url.substring(searchIndex, hashIndex)
2238
+ : url.substring(searchIndex);
2239
+ return createURLSearchParams(search);
2240
+ }
2241
+
2242
+ const defaultScale = 100000;
2243
+ const variantPartition = (seed, uid, scale) => {
2244
+ const hash = murmurHash3_x86_32(uid, seed);
2245
+ const maxUnsigned32BitInt = 0xffffffff;
2246
+ const scaledValue = hash / maxUnsigned32BitInt;
2247
+ const normalized = Math.floor(scaledValue * (scale - 1));
2248
+ return normalized;
2249
+ };
2250
+ const chooseVariant = (uid, allocation, seed, defaultId) => {
2251
+ const defaultVariant = defaultId;
2252
+ const assignment = variantPartition(seed, uid, defaultScale);
2253
+ let variantId = '';
2254
+ outerloop: for (const variant in allocation) {
2255
+ for (let j = 0; j < allocation[variant].length; j++) {
2256
+ const [start, end] = allocation[variant][j];
2257
+ if (assignment >= start && assignment <= end) {
2258
+ variantId = variant;
2259
+ break outerloop;
2260
+ }
2261
+ }
2262
+ }
2263
+ if (variantId === '') {
2264
+ variantId = defaultVariant;
2265
+ }
2266
+ return variantId;
2267
+ };
2268
+ const inRange = (bins, scale, seed, uid) => {
2269
+ const assignment = variantPartition(seed, uid, scale);
2270
+ for (let j = 0; j < bins.length; j++) {
2271
+ const [start, end] = bins[j];
2272
+ if (assignment >= start && assignment <= end) {
2273
+ return true;
2274
+ }
2275
+ }
2276
+ return false;
2277
+ };
2278
+ function checkAllAudiences(metadata, project, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, nonce, logger) {
2279
+ var _a;
2280
+ const audienceIds = (_a = metadata.activation_condition.audience_ids) !== null && _a !== void 0 ? _a : [];
2281
+ let result = true;
2282
+ for (const audienceId of audienceIds) {
2283
+ const audience = project.audiences[audienceId];
2284
+ if (!audience)
2285
+ continue;
2286
+ const activated = checkActivationCondition(audience.selector, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce }), logger);
2287
+ if (activated === false)
2288
+ return false;
2289
+ if (activated !== true)
2290
+ result = activated;
2291
+ }
2292
+ return result;
2293
+ }
2294
+ async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}) {
2295
+ var _a, _b;
2296
+ const experimentsInfo = [];
2297
+ const variantsAndElements = [];
2298
+ const cloakBot = !!((_a = project.settings) === null || _a === void 0 ? void 0 : _a.bot_cloaking_enabled) &&
2299
+ personalizationHeaders['x-is-bot'] === 'true';
2300
+ const buildResponse = ({ variantsAndElements, experimentsInfo, hasActivatedFrames: frameActivated = false, laneClaims: meClaims = laneClaims, }) => {
2301
+ return {
2302
+ variantsAndElements: variantsAndElements || [],
2303
+ experimentsInfo: experimentsInfo || [],
2304
+ hasActivatedFrames: frameActivated,
2305
+ laneClaims: meClaims,
2306
+ };
2307
+ };
2308
+ const previewVariantIds = (_b = previewVariantId === null || previewVariantId === void 0 ? void 0 : previewVariantId.split(',').map((id) => id.trim()).filter(Boolean)) !== null && _b !== void 0 ? _b : [];
2309
+ if (previewVariantIds.length > 0) {
2310
+ const filteredVariantsAndElements = project.frames
2311
+ .flatMap((frame) => frame.experiments.flatMap((experiment) => experiment.variants.map((variant) => ({
2312
+ variant,
2313
+ elements: frame.elements,
2314
+ }))))
2315
+ .filter((item) => previewVariantIds.includes(item.variant.id));
2316
+ const skipAlert = previewVariantIds.some((id) => id === project.holdout_info.holdout_variant_id || id === GLOBAL_HOLDOUT);
2317
+ if (filteredVariantsAndElements.length > 0) {
2318
+ return buildResponse({
2319
+ variantsAndElements: filteredVariantsAndElements,
2320
+ hasActivatedFrames: true,
2321
+ });
2322
+ }
2323
+ if (!skipAlert) {
2324
+ window.alert(`[Coframe] Variant preview unavailable: ${previewVariantId}.`);
2325
+ }
2326
+ return buildResponse({});
2327
+ }
2328
+ const userVisitCount = Number.parseInt(getCookie(VISIT_COUNT_COOKIE_KEY) || '0', 10);
2329
+ const validatedClaims = validateClaims(laneClaims, project.lanes);
2330
+ const activeFrames = project.frames.filter((frame) => {
2331
+ if (!frame.experiments)
2332
+ return false;
2333
+ const frameActivated = checkActivationCondition(frame.activation_condition, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce }), logger);
2334
+ if (!frameActivated)
2335
+ return false;
2336
+ return true;
2337
+ });
2338
+ if (activeFrames.length === 0) {
2339
+ return buildResponse({ laneClaims: validatedClaims });
2340
+ }
2341
+ const globalVariantId = cloakBot
2342
+ ? project.holdout_info.holdout_variant_id
2343
+ : chooseVariant(userToken, project.holdout_info.variant_bins, project.holdout_info.seed, project.holdout_info.holdout_variant_id);
2344
+ if (globalVariantId === project.holdout_info.holdout_variant_id) {
2345
+ experimentsInfo.push({
2346
+ variant_id: project.holdout_info.holdout_variant_id,
2347
+ variant_slug: project.holdout_info.holdout_variant_slug,
2348
+ experiment_id: project.holdout_info.holdout_experiment_id,
2349
+ experiment_slug: project.holdout_info.holdout_experiment_slug,
2350
+ iteration_id: '',
2351
+ final_state: true,
2352
+ level: GLOBAL_HOLDOUT,
2353
+ });
2354
+ return buildResponse({
2355
+ variantsAndElements,
2356
+ experimentsInfo,
2357
+ hasActivatedFrames: true,
2358
+ laneClaims: validatedClaims,
2359
+ });
2360
+ }
2361
+ experimentsInfo.push({
2362
+ variant_id: globalVariantId,
2363
+ variant_slug: project.holdout_info.treatment_variant_slug,
2364
+ experiment_id: project.holdout_info.holdout_experiment_id,
2365
+ experiment_slug: project.holdout_info.holdout_experiment_slug,
2366
+ iteration_id: '',
2367
+ final_state: false,
2368
+ level: GLOBAL_HOLDOUT,
2369
+ });
2370
+ const evalAudiences = (metadata) => checkAllAudiences(metadata, project, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, nonce, logger) !== false;
2371
+ const audiencePass = new Map();
2372
+ const reachableByLane = {};
2373
+ for (const frame of activeFrames) {
2374
+ for (const experiment of frame.experiments) {
2375
+ const metadata = experiment.metadata;
2376
+ const laneIds = experimentLaneIds(metadata);
2377
+ if (laneIds.length === 0)
2378
+ continue;
2379
+ const passes = evalAudiences(metadata);
2380
+ audiencePass.set(metadata.experiment_id, passes);
2381
+ if (passes) {
2382
+ for (const laneId of laneIds) {
2383
+ if (!reachableByLane[laneId])
2384
+ reachableByLane[laneId] = [];
2385
+ reachableByLane[laneId].push(metadata.experiment_id);
2386
+ }
2387
+ }
2388
+ }
2389
+ }
2390
+ const excludedExperimentIds = resolveLaneWinners(validatedClaims, reachableByLane, project.lanes, userToken);
2391
+ const updatedClaims = { ...validatedClaims };
2392
+ let hasActivatedFrames = false;
2393
+ for (const frame of activeFrames) {
2394
+ let atLeastOneExperimentWasActivated = false;
2395
+ for (const experiment of frame.experiments) {
2396
+ if (atLeastOneExperimentWasActivated)
2397
+ break;
2398
+ const metadata = experiment.metadata;
2399
+ const variants = experiment.variants;
2400
+ const elements = frame.elements;
2401
+ if (excludedExperimentIds.has(metadata.experiment_id))
2402
+ continue;
2403
+ let activated = audiencePass.get(metadata.experiment_id);
2404
+ if (activated === undefined) {
2405
+ activated = evalAudiences(metadata);
2406
+ }
2407
+ if (!activated)
2408
+ continue;
2409
+ atLeastOneExperimentWasActivated = true;
2410
+ hasActivatedFrames = true;
2411
+ for (const laneId of experimentLaneIds(metadata)) {
2412
+ updatedClaims[laneId] = makeClaim(metadata);
2413
+ }
2414
+ const experimentHoldoutInfo = metadata.holdout_info;
2415
+ const experimentUniverseVid = chooseVariant(userToken, experimentHoldoutInfo.variant_bins, experimentHoldoutInfo.seed, experimentHoldoutInfo.holdout_variant_id);
2416
+ if (experimentUniverseVid === experimentHoldoutInfo.holdout_variant_id) {
2417
+ experimentsInfo.push({
2418
+ variant_id: experimentHoldoutInfo.holdout_variant_id,
2419
+ variant_slug: experimentHoldoutInfo.holdout_variant_slug,
2420
+ experiment_id: experimentHoldoutInfo.holdout_experiment_id,
2421
+ experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
2422
+ iteration_id: '',
2423
+ level: EXPERIMENT_HOLDOUT,
2424
+ final_state: true,
2425
+ holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
2426
+ holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
2427
+ });
2428
+ continue;
2429
+ }
2430
+ experimentsInfo.push({
2431
+ variant_id: experimentHoldoutInfo.treatment_variant_id,
2432
+ variant_slug: experimentHoldoutInfo.treatment_variant_slug,
2433
+ experiment_id: experimentHoldoutInfo.holdout_experiment_id,
2434
+ experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
2435
+ iteration_id: '',
2436
+ level: EXPERIMENT_HOLDOUT,
2437
+ final_state: false,
2438
+ holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
2439
+ holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
2440
+ });
2441
+ const variantId = chooseVariant(userToken, metadata.allocation, metadata.seed, '');
2442
+ const variant = variants.find((v) => v.id === variantId);
2443
+ if (!variant) {
2444
+ logger.error(`Variant not found for experiment ${metadata.experiment_id}`, 'variant_not_found', {
2445
+ experiment_id: metadata.experiment_id,
2446
+ });
2447
+ break;
2448
+ }
2449
+ experimentsInfo.push({
2450
+ variant_id: variant.id,
2451
+ variant_slug: variant.slug,
2452
+ experiment_id: metadata.experiment_id,
2453
+ experiment_slug: metadata.slug,
2454
+ iteration_id: metadata.iteration_id,
2455
+ level: EXPERIMENT,
2456
+ final_state: true,
2457
+ holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
2458
+ holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
2459
+ });
2460
+ variantsAndElements.push({ variant, elements });
2461
+ }
2462
+ }
2463
+ return buildResponse({
2464
+ variantsAndElements,
2465
+ experimentsInfo,
2466
+ hasActivatedFrames,
2467
+ laneClaims: updatedClaims,
2468
+ });
2469
+ }
2470
+
2471
+ const CACHE_TTL_MS = 60 * 1000;
2472
+ const cache = new Map();
2473
+ function getCachedFrames(url) {
2474
+ const key = normalizeUrl(url);
2475
+ const entry = cache.get(key);
2476
+ if (!entry)
2477
+ return null;
2478
+ if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
2479
+ cache.delete(key);
2480
+ return null;
2481
+ }
2482
+ return entry.data;
2483
+ }
2484
+ function cacheFrames(url, data) {
2485
+ const key = normalizeUrl(url);
2486
+ cache.set(key, { data, timestamp: Date.now() });
2487
+ }
2488
+
2489
+ let conversionListenerStop = null;
2490
+ const activeJSBinders = new Set();
2491
+ function rebindMetrics(coframe, logger, metrics, nonce) {
2492
+ var _a, _b;
2493
+ if (!metrics || metrics.length === 0) {
2494
+ logger.warn('No metrics defined in the project config');
2495
+ return;
2496
+ }
2497
+ if (!conversionListenerStop) {
2498
+ conversionListenerStop = listenForCmd((cmd) => cmd.conversion !== undefined ||
2499
+ (cmd.emit === 'conversion' && cmd.payload !== undefined), (cmd) => {
2500
+ const payload = cmd.conversion ||
2501
+ (cmd.emit === 'conversion'
2502
+ ? cmd.payload
2503
+ : undefined);
2504
+ if (!payload || (!payload.metricId && !payload.metricName)) {
2505
+ logger.error('Conversion event payload must have metricId or metricName', 'conversion_event_payload_error', { payload });
2506
+ return;
2507
+ }
2508
+ const content = { metadata: payload.metadata };
2509
+ if (payload.metricId) {
2510
+ content.metric_ids = [payload.metricId];
2511
+ }
2512
+ if (payload.metricName) {
2513
+ content.metric_names = [payload.metricName];
2514
+ }
2515
+ if (payload.value) {
2516
+ content.value = payload.value;
2517
+ }
2518
+ const eventData = {
2519
+ type: 'conversion',
2520
+ project_id: coframe.projectId,
2521
+ session_id: coframe.sessionId,
2522
+ time: new Date(),
2523
+ url: getCurrentPageUrl(),
2524
+ user_token: coframe.userToken,
2525
+ content,
2526
+ };
2527
+ queueEventForBatchedSending(coframe, eventData, true);
2528
+ });
2529
+ }
2530
+ const elementsToClean = document.querySelectorAll('[coframe-metric-ids]');
2531
+ for (let i = 0; i < elementsToClean.length; i++) {
2532
+ elementsToClean[i].removeAttribute('coframe-metric-ids');
2533
+ }
2534
+ for (let i = 0; i < metrics.length; i++) {
2535
+ const metric = metrics[i];
2536
+ if (metric.js_binder) {
2537
+ if (activeJSBinders.has(metric.js_binder)) {
2538
+ continue;
2539
+ }
2540
+ activeJSBinders.add(metric.js_binder);
2541
+ try {
2542
+ executeJs(metric.js_binder, { metricId: metric.id, metricName: (_a = metric.selector) === null || _a === void 0 ? void 0 : _a.name }, { nonce, isExpression: true });
2543
+ }
2544
+ catch (error) {
2545
+ logger.error(`Failed to execute js_binder for metric ${metric.id}`, 'js_binder_execution_error', { error, metricId: metric.id });
2546
+ }
2547
+ continue;
2548
+ }
2549
+ if (metric.selector) {
2550
+ const elements = (_b = querySelector({
2551
+ selector: metric.selector,
2552
+ currentUrl: getCurrentPageUrl(),
2553
+ isQueryingAll: true,
2554
+ })) !== null && _b !== void 0 ? _b : [];
2555
+ for (let j = 0; j < elements.length; j++) {
2556
+ const e = elements[j];
2557
+ const metricIdsStr = e.getAttribute('coframe-metric-ids');
2558
+ if (metricIdsStr) {
2559
+ const metricIds = metricIdsStr.split(',');
2560
+ metricIds.push(metric.id);
2561
+ e.setAttribute('coframe-metric-ids', Array.from(new Set(metricIds)).join(','));
2562
+ }
2563
+ else {
2564
+ e.setAttribute('coframe-metric-ids', metric.id);
2565
+ }
2566
+ }
2567
+ }
2568
+ }
2569
+ }
2570
+
2571
+ let lastUrl = null;
2572
+ function observeUrlChanges(baseUrlMatch, onUrlChange) {
2573
+ const tryTrack = () => {
2574
+ const currentUrl = getCurrentPageUrl();
2575
+ const previousUrl = lastUrl;
2576
+ if (previousUrl && !urlsMatch(currentUrl, previousUrl, baseUrlMatch)) {
2577
+ lastUrl = currentUrl;
2578
+ onUrlChange(currentUrl, previousUrl);
2579
+ }
2580
+ };
2581
+ lastUrl = getCurrentPageUrl();
2582
+ tryTrack();
2583
+ window.addEventListener('popstate', tryTrack);
2584
+ window.addEventListener('hashchange', tryTrack);
2585
+ const patchHistoryMethod = (method) => {
2586
+ const original = history[method];
2587
+ history[method] = function (...args) {
2588
+ const result = original.apply(this, args);
2589
+ setTimeout(tryTrack, 0);
2590
+ return result;
2591
+ };
2592
+ };
2593
+ patchHistoryMethod('pushState');
2594
+ patchHistoryMethod('replaceState');
2595
+ }
2596
+
2597
+ function startSession(Coframe, cookiesEnabled, executionStartTime, personalizationHeaders) {
2598
+ const sessionPayload = [
2599
+ Coframe,
2600
+ Coframe.orgId,
2601
+ Coframe.projectId,
2602
+ Coframe.sessionId,
2603
+ Coframe.userToken,
2604
+ Coframe.experimentInfo,
2605
+ executionStartTime,
2606
+ getBrowserReferrer(),
2607
+ getBrowserPlatform(),
2608
+ getBrowserLanguages(),
2609
+ getBrowserUserAgent(),
2610
+ getCurrentPageUrl(),
2611
+ getBrowserFignerprintInfo(),
2612
+ ];
2613
+ {
2614
+ queueCreateSession(...sessionPayload, undefined, undefined, personalizationHeaders);
2615
+ }
2616
+ queueEvent(Coframe, {
2617
+ projectId: Coframe.projectId,
2618
+ sessionId: Coframe.sessionId,
2619
+ eventType: 'page_view',
2620
+ orgId: Coframe.orgId,
2621
+ userToken: Coframe.userToken,
2622
+ personalizationHeaders,
2623
+ });
2624
+ }
2625
+ function reportInitTiming(Coframe, project, integrationStartTime, executionStartTime) {
2626
+ var _a;
2627
+ reportLCP({
2628
+ coframe: Coframe,
2629
+ projectId: Coframe.projectId,
2630
+ orgId: Coframe.orgId,
2631
+ sessionId: Coframe.sessionId,
2632
+ });
2633
+ const now = new Date();
2634
+ const elapsedFromInstallation = integrationStartTime
2635
+ ? now.getTime() - integrationStartTime.getTime()
2636
+ : 0;
2637
+ const elapsedFromExecution = now.getTime() - executionStartTime.getTime();
2638
+ Coframe.status = {
2639
+ initialized: true,
2640
+ elapsedFromInstallation,
2641
+ elapsedFromExecution,
2642
+ trackOnlyActivatedFrames: !!((_a = project === null || project === void 0 ? void 0 : project.settings) === null || _a === void 0 ? void 0 : _a.track_only_activated_frames),
2643
+ };
2644
+ queueMonitoringEvent(Coframe, {
2645
+ event_id: generateUUID(),
2646
+ monitoring_type: 'init',
2647
+ local_time: new Date().toISOString(),
2648
+ project_id: Coframe.projectId,
2649
+ session_id: Coframe.sessionId,
2650
+ url: getCurrentPageUrl(),
2651
+ org_id: Coframe.orgId,
2652
+ metadata: {
2653
+ elapsedFromInstallation,
2654
+ elapsedFromExecution,
2655
+ },
2656
+ });
2657
+ }
2658
+ function createTreatmentAnalytics(Coframe, previewVariantId) {
2659
+ Coframe.treatmentsData = null;
2660
+ Coframe.treatmentsDataV2 = null;
2661
+ let treatmentDataCallback;
2662
+ let treatmentDataAllCallback;
2663
+ let treatmentDataAllCallbackV2;
2664
+ let treatmentDataAllCallbackV2WithoutConsent;
2665
+ let withConsentBroadcasted = false;
2666
+ let withoutConsentBroadcasted = false;
2667
+ const buildTreatmentData = () => {
2668
+ const activeExperiments = Coframe.experimentInfo;
2669
+ const sessionId = Coframe.sessionId;
2670
+ const userToken = Coframe.userToken;
2671
+ const treatmentsData = [];
2672
+ const treatmentsDataV2 = [];
2673
+ for (const experimentInfo of activeExperiments) {
2674
+ if (experimentInfo.final_state) {
2675
+ const data = {
2676
+ impressionId: sessionId,
2677
+ userToken,
2678
+ experimentId: experimentInfo.experiment_id,
2679
+ variantId: experimentInfo.variant_id,
2680
+ inGlobalHoldout: experimentInfo.level === GLOBAL_HOLDOUT,
2681
+ isBaseline: experimentInfo.level === EXPERIMENT_HOLDOUT,
2682
+ ...(experimentInfo.holdout_experiment_id
2683
+ ? { holdoutExperimentId: experimentInfo.holdout_experiment_id }
2684
+ : {}),
2685
+ };
2686
+ treatmentsData.push(data);
2687
+ treatmentsDataV2.push({
2688
+ coframe_experiment_id: experimentInfo.holdout_experiment_id ||
2689
+ experimentInfo.experiment_id,
2690
+ coframe_experiment_slug: experimentInfo.holdout_experiment_slug ||
2691
+ experimentInfo.experiment_slug,
2692
+ coframe_variant_id: experimentInfo.variant_id,
2693
+ coframe_variant_slug: experimentInfo.variant_slug,
2694
+ coframe_user_token: userToken,
2695
+ coframe_impression_id: sessionId,
2696
+ coframe_in_global_holdout: experimentInfo.level === GLOBAL_HOLDOUT,
2697
+ coframe_in_exp_baseline: experimentInfo.level === EXPERIMENT_HOLDOUT,
2698
+ });
2699
+ }
2700
+ }
2701
+ if (previewVariantId) {
2702
+ const previewIds = previewVariantId.split(',');
2703
+ for (const id of previewIds) {
2704
+ treatmentsData.push({
2705
+ impressionId: sessionId,
2706
+ userToken,
2707
+ experimentId: `preview_id_${id}`,
2708
+ holdoutExperimentId: `preview_holdout_id_${id}`,
2709
+ variantId: id,
2710
+ inGlobalHoldout: false,
2711
+ isBaseline: false,
2712
+ });
2713
+ treatmentsDataV2.push({
2714
+ coframe_experiment_id: `preview_id_${id}`,
2715
+ coframe_experiment_slug: `preview_id_${id}`,
2716
+ coframe_variant_id: id,
2717
+ coframe_variant_slug: id,
2718
+ coframe_user_token: userToken,
2719
+ coframe_impression_id: sessionId,
2720
+ coframe_in_global_holdout: false,
2721
+ coframe_in_exp_baseline: false,
2722
+ });
2723
+ }
2724
+ }
2725
+ Coframe.treatmentsData = treatmentsData;
2726
+ Coframe.treatmentsDataV2 = treatmentsDataV2;
2727
+ Coframe.experimentInfo = activeExperiments;
2728
+ };
2729
+ const broadcastWithConsentCallbacksOnce = () => {
2730
+ var _a, _b, _c, _d;
2731
+ const hasData = ((_b = (_a = Coframe.treatmentsData) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 ||
2732
+ ((_d = (_c = Coframe.treatmentsDataV2) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
2733
+ if ((treatmentDataCallback ||
2734
+ treatmentDataAllCallback ||
2735
+ treatmentDataAllCallbackV2) &&
2736
+ hasData &&
2737
+ !withConsentBroadcasted &&
2738
+ canSendWithConsent()) {
2739
+ if (Coframe.treatmentsData && treatmentDataCallback) {
2740
+ for (const treatmentData of Coframe.treatmentsData) {
2741
+ treatmentDataCallback(treatmentData);
2742
+ }
2743
+ withConsentBroadcasted = true;
2744
+ }
2745
+ if (Coframe.treatmentsData && treatmentDataAllCallback) {
2746
+ treatmentDataAllCallback(Coframe.treatmentsData);
2747
+ withConsentBroadcasted = true;
2748
+ }
2749
+ if (Coframe.treatmentsDataV2 && treatmentDataAllCallbackV2) {
2750
+ treatmentDataAllCallbackV2(Coframe.treatmentsDataV2, Coframe.experimentInfo);
2751
+ withConsentBroadcasted = true;
2752
+ }
2753
+ }
2754
+ };
2755
+ const broadcastWithoutConsentCallbacksOnce = () => {
2756
+ var _a, _b;
2757
+ const hasData = ((_b = (_a = Coframe.treatmentsDataV2) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
2758
+ if (treatmentDataAllCallbackV2WithoutConsent &&
2759
+ hasData &&
2760
+ !withoutConsentBroadcasted &&
2761
+ shouldSendEvents({ ignoreConsent: true })) {
2762
+ treatmentDataAllCallbackV2WithoutConsent(Coframe.treatmentsDataV2, Coframe.experimentInfo);
2763
+ withoutConsentBroadcasted = true;
2764
+ }
2765
+ };
2766
+ const sendAnalytics = () => {
2767
+ if (!shouldSendEvents({ ignoreConsent: true })) {
2768
+ return;
2769
+ }
2770
+ buildTreatmentData();
2771
+ broadcastWithoutConsentCallbacksOnce();
2772
+ broadcastWithConsentCallbacksOnce();
2773
+ };
2774
+ const registerCallbacks = (cmd) => {
2775
+ treatmentDataCallback = cmd.callback;
2776
+ treatmentDataAllCallback = cmd.bulkCallback;
2777
+ treatmentDataAllCallbackV2 = cmd.bulkCallbackV2;
2778
+ treatmentDataAllCallbackV2WithoutConsent = cmd.bulkCallbackV2WithoutConsent;
2779
+ broadcastWithoutConsentCallbacksOnce();
2780
+ broadcastWithConsentCallbacksOnce();
2781
+ };
2782
+ const resetBroadcasted = () => {
2783
+ withConsentBroadcasted = false;
2784
+ withoutConsentBroadcasted = false;
2785
+ };
2786
+ return { sendAnalytics, registerCallbacks, resetBroadcasted };
2787
+ }
2788
+ const ENGAGED_SESSION_THRESHOLD = 10000;
2789
+ function setupListeners(Coframe, executionStartTime) {
2790
+ const experiencedCoframeIds = new Set();
2791
+ let isEngaged = false;
2792
+ function measureEngagement() {
2793
+ const timeSpent = Date.now() - (executionStartTime ? executionStartTime.getTime() : 0);
2794
+ if (!isEngaged && timeSpent >= ENGAGED_SESSION_THRESHOLD) {
2795
+ isEngaged = true;
2796
+ queueEvent(Coframe, {
2797
+ projectId: Coframe.projectId,
2798
+ sessionId: Coframe.sessionId,
2799
+ eventType: 'session_start',
2800
+ orgId: Coframe.orgId,
2801
+ userToken: Coframe.userToken,
2802
+ personalizationHeaders: Coframe.personalizationHeaders,
2803
+ });
2804
+ }
2805
+ else {
2806
+ setTimeout(measureEngagement, ENGAGED_SESSION_THRESHOLD - timeSpent);
2807
+ }
2808
+ }
2809
+ function updateViewedVariants() {
2810
+ for (const variantElement of Coframe.variantsAndElements || []) {
2811
+ const variant = variantElement.variant;
2812
+ if (!experiencedCoframeIds.has(variant.id)) {
2813
+ const elements = document.querySelectorAll(`[coframe-exp-id="${variant.id}"]`);
2814
+ for (const el of Array.from(elements)) {
2815
+ if (isElementVisible(el)) {
2816
+ el.setAttribute('coframe-experienced', 'true');
2817
+ experiencedCoframeIds.add(variant.id);
2818
+ queueEventForBatchedSending(Coframe, {
2819
+ type: 'view',
2820
+ project_id: Coframe.projectId,
2821
+ session_id: Coframe.sessionId,
2822
+ experiment_id: variant.experiment_id,
2823
+ variant_id: variant.id,
2824
+ time: new Date(),
2825
+ orgId: Coframe.orgId,
2826
+ });
2827
+ }
2828
+ }
2829
+ }
2830
+ }
2831
+ }
2832
+ document.addEventListener('visibilitychange', measureEngagement);
2833
+ setTimeout(measureEngagement, ENGAGED_SESSION_THRESHOLD);
2834
+ document.addEventListener('coframe:dom-renderer:complete', updateViewedVariants);
2835
+ document.addEventListener('scroll', updateViewedVariants);
2836
+ document.dispatchEvent(new CustomEvent('coframe:dom-renderer:complete', {}));
2837
+ document.addEventListener('click', (event) => {
2838
+ sendPageEngagementEvent(Coframe, 'click', event, {
2839
+ projectId: Coframe.projectId,
2840
+ isEngaged,
2841
+ sessionId: Coframe.sessionId,
2842
+ orgId: Coframe.orgId,
2843
+ userToken: Coframe.userToken,
2844
+ personalizationHeaders: Coframe.personalizationHeaders,
2845
+ });
2846
+ }, true);
2847
+ const handleEventFlush = debounce((eventType) => {
2848
+ queueEvent(Coframe, {
2849
+ projectId: Coframe.projectId,
2850
+ sessionId: Coframe.sessionId,
2851
+ eventType: eventType,
2852
+ orgId: Coframe.orgId,
2853
+ userToken: Coframe.userToken,
2854
+ personalizationHeaders: Coframe.personalizationHeaders,
2855
+ });
2856
+ flushQueuedEvents(Coframe.projectId);
2857
+ }, 1);
2858
+ window.addEventListener('beforeunload', () => handleEventFlush('before_unload'));
2859
+ window.addEventListener('pagehide', () => handleEventFlush('page_hide'));
2860
+ window.addEventListener('visibilitychange', () => {
2861
+ const isPageHidden = document.hidden;
2862
+ const eventType = isPageHidden ? 'tab_hide' : 'tab_show';
2863
+ handleEventFlush(eventType);
2864
+ });
2865
+ }
2866
+ function setupTracking(Coframe, projectResult, previewVariantId, executionStartTime, nonce, waitForCookiesEnabled, onConsentChange) {
2867
+ var _a, _b;
2868
+ const logger = Coframe.logger;
2869
+ const { project } = projectResult;
2870
+ setTrackOnlyActivatedFrames(!!((_a = project === null || project === void 0 ? void 0 : project.settings) === null || _a === void 0 ? void 0 : _a.track_only_activated_frames));
2871
+ const analytics = createTreatmentAnalytics(Coframe, previewVariantId);
2872
+ setupListeners(Coframe, executionStartTime);
2873
+ const managedAnalyticsCallback = (_b = project === null || project === void 0 ? void 0 : project.settings) === null || _b === void 0 ? void 0 : _b.analytics_callback;
2874
+ if (managedAnalyticsCallback === null || managedAnalyticsCallback === void 0 ? void 0 : managedAnalyticsCallback.callback) {
2875
+ try {
2876
+ const cfg = executeJs(`function() { return (${managedAnalyticsCallback.callback.trim()}); }`, {}, { isExpression: true, nonce });
2877
+ analytics.registerCallbacks(cfg);
2878
+ }
2879
+ catch (_e) {
2880
+ logger.error('Failed to evaluate managed analytics callback', 'managed_callback_eval_error');
2881
+ }
2882
+ }
2883
+ else {
2884
+ waitForCmd((cmd) => !!(cmd.subscribe === 'treatmentApplied'), Coframe).then((cmd) => {
2885
+ if (cmd) {
2886
+ if (!cmd.callback &&
2887
+ !cmd.bulkCallback &&
2888
+ !cmd.bulkCallbackV2 &&
2889
+ !cmd.bulkCallbackV2WithoutConsent) {
2890
+ logger.warn('treatmentApplied callback is not set, skipping', 'treatment_applied_callback_not_set');
2891
+ return;
2892
+ }
2893
+ analytics.registerCallbacks(cmd);
2894
+ }
2895
+ });
2896
+ }
2897
+ if (waitForCookiesEnabled) {
2898
+ onConsentChange === null || onConsentChange === void 0 ? void 0 : onConsentChange((enabled) => {
2899
+ if (enabled) {
2900
+ flushQueuedEvents(Coframe.projectId);
2901
+ analytics.sendAnalytics();
2902
+ }
2903
+ });
2904
+ }
2905
+ return analytics;
2906
+ }
2907
+
2908
+ async function awaitHydration(config, Coframe) {
2909
+ if (!config.waitForHydration)
2910
+ return true;
2911
+ const cmd = await waitForCmd((cmd) => cmd.pageHydrated === true || cmd.emit === 'pageHydrated', Coframe);
2912
+ if (!cmd) {
2913
+ Coframe.logger.warn('Failed to wait for page hydrated command', 'wait_for_cmd_timeout');
2914
+ return false;
2915
+ }
2916
+ return true;
2917
+ }
2918
+ async function ensureBody(Coframe, cdnProject, timeoutMs, nonce) {
2919
+ const bodyPresent = await waitForBody(timeoutMs).catch(() => false);
2920
+ if (!bodyPresent) {
2921
+ Coframe.logger.error('Timeout waiting for document.body, exiting gracefully', 'body_timeout');
2922
+ variantRendered();
2923
+ return false;
2924
+ }
2925
+ rebindMetrics(Coframe, Coframe.logger, cdnProject === null || cdnProject === void 0 ? void 0 : cdnProject.metrics, nonce);
2926
+ return true;
2927
+ }
2928
+ const FLUSH_EVENTS_TIME = 1000 * 3;
2929
+ let flushInterval = null;
2930
+ function setupFlushInterval(projectId) {
2931
+ flushQueuedEvents(projectId);
2932
+ if (flushInterval !== null) {
2933
+ clearInterval(flushInterval);
2934
+ }
2935
+ flushInterval = setInterval(() => flushQueuedEvents(projectId), FLUSH_EVENTS_TIME);
2936
+ }
2937
+ function setupSPANavigation(Coframe, project, config, applyVariants) {
2938
+ var _a;
2939
+ if (!((_a = project === null || project === void 0 ? void 0 : project.settings) === null || _a === void 0 ? void 0 : _a.mutation_observer_active))
2940
+ return;
2941
+ const infiniteLoopProtectionMax = 5000;
2942
+ let infiniteLoopProtection = 0;
2943
+ const rebindMetricsOnChildListChange = debounce(() => {
2944
+ rebindMetrics(Coframe, Coframe.logger, project === null || project === void 0 ? void 0 : project.metrics, config.nonce);
2945
+ }, 100);
2946
+ const mutationCallback = (mutations) => {
2947
+ infiniteLoopProtection++;
2948
+ if (infiniteLoopProtection >= infiniteLoopProtectionMax) {
2949
+ Coframe.logger.error('Mutation callback infinite loop protection triggered, disconnecting mutation observer', 'mutation_observer_loop_protection', mutations);
2950
+ observer.disconnect();
2951
+ return;
2952
+ }
2953
+ for (const mutation of mutations) {
2954
+ if (mutation.type === 'childList') {
2955
+ rebindMetricsOnChildListChange();
2956
+ }
2957
+ }
2958
+ };
2959
+ const observer = new MutationObserver(mutationCallback);
2960
+ observer.observe(document.body, { childList: true, subtree: true });
2961
+ let navigationAbortController = null;
2962
+ observeUrlChanges(false, async (newPageUrl, _oldPageUrl) => {
2963
+ await new Promise((resolve) => requestAnimationFrame(resolve));
2964
+ infiniteLoopProtection = 0;
2965
+ if (navigationAbortController)
2966
+ navigationAbortController.abort();
2967
+ navigationAbortController = new AbortController();
2968
+ const { signal } = navigationAbortController;
2969
+ let pageProject = project;
2970
+ const cached = getCachedFrames(newPageUrl);
2971
+ if (cached) {
2972
+ pageProject = cached.project;
2973
+ }
2974
+ else {
2975
+ try {
2976
+ const result = await fetchProjectFromEdge(Coframe.projectId, newPageUrl, signal, config.userContext);
2977
+ if (signal.aborted)
2978
+ return;
2979
+ if (result) {
2980
+ pageProject = result.project;
2981
+ cacheFrames(newPageUrl, result);
2982
+ }
2983
+ }
2984
+ catch (_a) {
2985
+ }
2986
+ }
2987
+ if (signal.aborted)
2988
+ return;
2989
+ await applyVariants(newPageUrl, pageProject);
2990
+ });
2991
+ }
2992
+ function readLaneClaims() {
2993
+ const cookie = getCookie(LANE_COOKIE_KEY);
2994
+ if (cookie) {
2995
+ try {
2996
+ return parseClaims(decodeURIComponent(cookie));
2997
+ }
2998
+ catch (_a) {
2999
+ return parseClaims(cookie);
3000
+ }
3001
+ }
3002
+ try {
3003
+ return parseClaims(localStorage.getItem(LANE_COOKIE_KEY));
3004
+ }
3005
+ catch (_b) {
3006
+ return {};
3007
+ }
3008
+ }
3009
+ function persistLaneClaims(claims, logger) {
3010
+ const serialized = serializeClaims(claims);
3011
+ try {
3012
+ localStorage.setItem(LANE_COOKIE_KEY, serialized);
3013
+ }
3014
+ catch (_a) {
3015
+ }
3016
+ if (isCookiesEnabled) {
3017
+ setCookie(LANE_COOKIE_KEY, encodeURIComponent(serialized), logger);
3018
+ }
3019
+ }
3020
+ function createApplyVariants(Coframe, config, previewVariantId, executionStartTime, analytics, personalizationHeaders, laneStore) {
3021
+ return async (pageUrl, pageProject) => {
3022
+ const sessionId = generateUUID();
3023
+ Coframe.sessionId = sessionId;
3024
+ const urlPreviewVariantId = getCurrentUrlSearchParams().get('coframe_variant_id') || null;
3025
+ const updatedPreviewVariantId = urlPreviewVariantId !== previewVariantId
3026
+ ? urlPreviewVariantId
3027
+ : previewVariantId;
3028
+ const { variantsAndElements, experimentsInfo, hasActivatedFrames, laneClaims, } = await getVariantsAndElements(personalizationHeaders, pageProject, updatedPreviewVariantId, Coframe.userToken, pageUrl, getBrowserLanguages(), getBrowserPlatform(), Coframe.logger, config.nonce, laneStore.claims);
3029
+ laneStore.claims = laneClaims;
3030
+ persistLaneClaims(laneClaims, Coframe.logger);
3031
+ Coframe.experimentInfo = experimentsInfo;
3032
+ Coframe.variantsAndElements = variantsAndElements;
3033
+ setFrameActivated(hasActivatedFrames);
3034
+ if (Coframe.debug) {
3035
+ Coframe.debug.project = pageProject;
3036
+ Coframe.debug.frames = pageProject.frames;
3037
+ Coframe.debug.laneClaims = laneClaims;
3038
+ }
3039
+ if (config.trackingOnly) {
3040
+ variantRendered();
3041
+ }
3042
+ else {
3043
+ replaceVariants(variantsAndElements, Coframe.orgId, Coframe.logger, config.nonce);
3044
+ }
3045
+ startSession(Coframe, isCookiesEnabled, executionStartTime, personalizationHeaders);
3046
+ analytics.resetBroadcasted();
3047
+ analytics.sendAnalytics();
3048
+ };
3049
+ }
3050
+ async function modifyPage(Coframe, configResult, projectResult, analytics) {
3051
+ const { config, timeoutMs, previewVariantId, integrationStartTime, executionStartTime, } = configResult;
3052
+ const { project: cdnProject, personalizationHeaders } = projectResult;
3053
+ if (!(await awaitHydration(config, Coframe)))
3054
+ return;
3055
+ if (cdnProject.tags) {
3056
+ executeTags(cdnProject.tags, Coframe.logger, config.nonce);
3057
+ Coframe.status.tags = cdnProject.tags;
3058
+ }
3059
+ if (!(await ensureBody(Coframe, cdnProject, timeoutMs, config.nonce)))
3060
+ return;
3061
+ const laneStore = { claims: readLaneClaims() };
3062
+ const applyVariants = createApplyVariants(Coframe, config, previewVariantId, executionStartTime, analytics, personalizationHeaders, laneStore);
3063
+ await applyVariants(getCurrentPageUrl(), cdnProject);
3064
+ if (!Coframe.variantsAndElements ||
3065
+ Coframe.variantsAndElements.length === 0) {
3066
+ revealPage();
3067
+ variantRendered();
3068
+ }
3069
+ reportInitTiming(Coframe, cdnProject, integrationStartTime, executionStartTime);
3070
+ setupSPANavigation(Coframe, cdnProject, config, applyVariants);
3071
+ setupFlushInterval(Coframe.projectId);
3072
+ }
3073
+
3074
+ const TIMEOUT_MAX_MS = 5000;
3075
+ function getFeatureFlagValue(cmd) {
3076
+ if (cmd.featureFlagSet !== undefined)
3077
+ return cmd.featureFlagSet;
3078
+ if (typeof cmd.emit === 'object' &&
3079
+ cmd.emit !== null &&
3080
+ 'featureFlagSet' in cmd.emit) {
3081
+ return cmd.emit.featureFlagSet;
3082
+ }
3083
+ return undefined;
3084
+ }
3085
+ async function resolveFeatureFlag(Coframe, timeoutMs) {
3086
+ const cmd = await waitForCmd((cmd) => typeof getFeatureFlagValue(cmd) === 'boolean', Coframe, timeoutMs);
3087
+ if (!cmd)
3088
+ return undefined;
3089
+ return getFeatureFlagValue(cmd);
3090
+ }
3091
+ async function resolveConfig(Coframe, revealPage) {
3092
+ const logger = Coframe.logger;
3093
+ const configCmd = await waitForCmd((cmd) => !!cmd.config, Coframe);
3094
+ if (!configCmd) {
3095
+ logger.error('Failed to wait for config cmd', 'wait_for_cmd_timeout');
3096
+ return null;
3097
+ }
3098
+ const config = configCmd.config;
3099
+ const timeoutMs = (config === null || config === void 0 ? void 0 : config.timeoutMs)
3100
+ ? Math.min(config.timeoutMs, TIMEOUT_MAX_MS)
3101
+ : TIMEOUT_MAX_MS;
3102
+ const integrationStartTime = (config === null || config === void 0 ? void 0 : config.startTime)
3103
+ ? new Date(config.startTime)
3104
+ : null;
3105
+ const executionStartTime = new Date();
3106
+ Coframe.batchSize = config.batchSize || 32;
3107
+ const gracefulExit = (msg) => {
3108
+ console.warn(`[Coframe] ${msg}`);
3109
+ revealPage();
3110
+ return null;
3111
+ };
3112
+ if (config.waitForFeatureFlag) {
3113
+ const flag = await resolveFeatureFlag(Coframe, timeoutMs);
3114
+ if (flag === undefined) {
3115
+ return gracefulExit('Feature flag was not set after timeout, graceful exit');
3116
+ }
3117
+ if (flag === false) {
3118
+ return gracefulExit('Feature flag is set to false, graceful exit');
3119
+ }
3120
+ }
3121
+ const projectId = config === null || config === void 0 ? void 0 : config.projectId;
3122
+ const warnMissingVariant = () => {
3123
+ if (applyVariantChangesStarted) {
3124
+ logger.warn('Failed to wait for variant rendered command', 'missing_variant_rendered');
3125
+ }
3126
+ };
3127
+ waitForCmd((cmd) => cmd.variantRendered === true || cmd.emit === 'variantRendered', Coframe, timeoutMs)
3128
+ .then((cmd) => {
3129
+ if (!cmd) {
3130
+ warnMissingVariant();
3131
+ }
3132
+ })
3133
+ .finally(() => {
3134
+ revealPage();
3135
+ });
3136
+ if (!projectId) {
3137
+ logger.error('No project ID', 'no_project_id', {
3138
+ description: 'No Coframe Page ID',
3139
+ });
3140
+ variantRendered();
3141
+ return null;
3142
+ }
3143
+ Coframe.projectId = projectId;
3144
+ const urlParams = getCurrentUrlSearchParams();
3145
+ const previewVariantId = urlParams.get('coframe_variant_id');
3146
+ const shouldSendEventsParam = urlParams.get('coframe_should_send_events');
3147
+ setForceSendEvents(shouldSendEventsParam === 'true');
3148
+ setIsPreviewVariant(!!previewVariantId);
3149
+ setWaitingForCookies(!!config.waitForCookiesEnabled);
3150
+ return {
3151
+ config,
3152
+ timeoutMs,
3153
+ projectId,
3154
+ previewVariantId,
3155
+ integrationStartTime,
3156
+ executionStartTime,
3157
+ };
3158
+ }
3159
+
3160
+ async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit) {
3161
+ const logger = Coframe.logger;
3162
+ let userToken;
3163
+ if (config.waitForUserToken) {
3164
+ const tokenCmd = await waitForCmd((cmd) => !!cmd.setUserToken && typeof cmd.setUserToken === 'string', Coframe, timeoutMs);
3165
+ if (!tokenCmd || !tokenCmd.setUserToken) {
3166
+ gracefulExit('User token was not provided after timeout, graceful exit');
3167
+ return null;
3168
+ }
3169
+ userToken = tokenCmd.setUserToken;
3170
+ }
3171
+ else {
3172
+ userToken = getOrGenerateUserToken();
3173
+ }
3174
+ Coframe.userToken = userToken;
3175
+ const persistCookies = () => {
3176
+ if (config.waitForUserToken)
3177
+ return;
3178
+ setUserTokenCookie(userToken, logger);
3179
+ trackVisitCookie(logger);
3180
+ };
3181
+ const consentListeners = [];
3182
+ const onConsentChange = (cb) => {
3183
+ consentListeners.push(cb);
3184
+ return () => {
3185
+ const idx = consentListeners.indexOf(cb);
3186
+ if (idx !== -1)
3187
+ consentListeners.splice(idx, 1);
3188
+ };
3189
+ };
3190
+ const applyCookieConsent = (enabled) => {
3191
+ if (isCookiesEnabled === enabled) {
3192
+ return;
3193
+ }
3194
+ setCookiesEnabled(enabled);
3195
+ if (enabled) {
3196
+ persistCookies();
3197
+ }
3198
+ for (const cb of consentListeners.slice()) {
3199
+ cb(enabled);
3200
+ }
3201
+ };
3202
+ if (!(config === null || config === void 0 ? void 0 : config.waitForCookiesEnabled)) {
3203
+ applyCookieConsent(true);
3204
+ return { userToken, applyCookieConsent, onConsentChange };
3205
+ }
3206
+ setCookiesEnabled(false);
3207
+ variantRendered();
3208
+ const initial = findLastCmd((cmd) => typeof cmd.cookiesEnabled === 'boolean');
3209
+ if (initial) {
3210
+ applyCookieConsent(!!initial.cookiesEnabled);
3211
+ }
3212
+ subscribeToCmd((cmd) => typeof cmd.cookiesEnabled === 'boolean', (cmd) => applyCookieConsent(!!cmd.cookiesEnabled));
3213
+ return { userToken, applyCookieConsent, onConsentChange };
3214
+ }
3215
+
3216
+ async function resolveProject(Coframe, previewVariantId, config) {
3217
+ var _a;
3218
+ const logger = Coframe.logger;
3219
+ const projectData = (_a = findLastCmd((c) => !!c.projectData)) === null || _a === void 0 ? void 0 : _a.projectData;
3220
+ let personalizationHeaders = (projectData === null || projectData === void 0 ? void 0 : projectData.personalizationHeaders) || null;
3221
+ let project = projectData === null || projectData === void 0 ? void 0 : projectData.project;
3222
+ if (!project) {
3223
+ const edgeResult = await fetchProjectFromEdge(Coframe.projectId, config.currentUrl || getCurrentPageUrl(), undefined, config.userContext);
3224
+ personalizationHeaders = (edgeResult === null || edgeResult === void 0 ? void 0 : edgeResult.personalizationHeaders) || null;
3225
+ project = (edgeResult === null || edgeResult === void 0 ? void 0 : edgeResult.project) || undefined;
3226
+ }
3227
+ if (!project) {
3228
+ logger.error('No Coframe Project Config', 'no_project_config', {
3229
+ previewVariantId,
3230
+ });
3231
+ variantRendered();
3232
+ return null;
3233
+ }
3234
+ return {
3235
+ project,
3236
+ personalizationHeaders: personalizationHeaders,
3237
+ };
3238
+ }
3239
+
3240
+ function injectOptions(options) {
3241
+ var _a;
3242
+ if (options.config) {
3243
+ window.CFQ = window.CFQ || [];
3244
+ window.CFQ.push({ config: options.config });
3245
+ }
3246
+ if (options.projectData) {
3247
+ window.CFQ = window.CFQ || [];
3248
+ window.CFQ.push({
3249
+ projectData: {
3250
+ project: options.projectData.project,
3251
+ personalizationHeaders: (_a = options.projectData.personalizationHeaders) !== null && _a !== void 0 ? _a : {},
3252
+ },
3253
+ });
3254
+ }
3255
+ }
3256
+
3257
+ function push(cmd) {
3258
+ if (typeof window === 'undefined')
3259
+ return;
3260
+ window.CFQ = window.CFQ || [];
3261
+ window.CFQ.push(cmd);
3262
+ }
3263
+ function setConsent(enabled) {
3264
+ push({ cookiesEnabled: enabled });
3265
+ }
3266
+ function setUserToken(token) {
3267
+ push({ setUserToken: token });
3268
+ }
3269
+ function setFeatureFlag(enabled) {
3270
+ push({ featureFlagSet: enabled });
3271
+ }
3272
+ function setPageHydrated() {
3273
+ push({ pageHydrated: true });
3274
+ }
3275
+ function trackConversion(payload) {
3276
+ push({ conversion: payload });
3277
+ }
3278
+ function onTreatmentApplied(handlers) {
3279
+ push({ subscribe: 'treatmentApplied', ...handlers });
3280
+ }
3281
+ function onLog(cb) {
3282
+ push({ subscribe: 'log', logCallback: cb });
3283
+ }
3284
+
3285
+ const Coframe = {
3286
+ status: {
3287
+ initialized: false,
3288
+ },
3289
+ projectId: '',
3290
+ orgId: '',
3291
+ variantsAndElements: [],
3292
+ userToken: '',
3293
+ sessionId: generateUUID(),
3294
+ batchSize: 32,
3295
+ treatmentsData: null,
3296
+ treatmentsDataV2: null,
3297
+ experimentInfo: [],
3298
+ logger: {
3299
+ warn: () => { },
3300
+ error: () => { },
3301
+ },
3302
+ };
3303
+ Coframe.logger = createLogger(() => Coframe);
3304
+ async function init(options) {
3305
+ if (options)
3306
+ injectOptions(options);
3307
+ const gracefulExit = (msg) => {
3308
+ console.warn(`[Coframe] ${msg}`);
3309
+ revealPage();
3310
+ return;
3311
+ };
3312
+ const configResult = await resolveConfig(Coframe, revealPage);
3313
+ if (!configResult)
3314
+ return;
3315
+ const projectResult = await resolveProject(Coframe, configResult.previewVariantId, configResult.config);
3316
+ if (!projectResult)
3317
+ return;
3318
+ Coframe.orgId = projectResult.project.org_id || '';
3319
+ Coframe.personalizationHeaders = projectResult.personalizationHeaders;
3320
+ const identity = await resolveIdentity(configResult.config, Coframe, configResult.timeoutMs, gracefulExit);
3321
+ if (!identity)
3322
+ return;
3323
+ const analytics = setupTracking(Coframe, projectResult, configResult.previewVariantId, configResult.executionStartTime, configResult.config.nonce, configResult.config.waitForCookiesEnabled, identity.onConsentChange);
3324
+ await modifyPage(Coframe, configResult, projectResult, analytics);
3325
+ }
3326
+ Coframe.debug = {
3327
+ getSelector,
3328
+ getElement,
3329
+ getElementUniversal,
3330
+ waitForCmd,
3331
+ flushQueuedEvents: () => {
3332
+ flushQueuedEvents(Coframe.projectId);
3333
+ },
3334
+ queuedEvents,
3335
+ shouldSendEvents,
3336
+ querySelector,
3337
+ logs: [],
3338
+ };
3339
+ Coframe.init = init;
3340
+ if (typeof window !== 'undefined') {
3341
+ window.Coframe = Coframe;
3342
+ }
3343
+
3344
+ export { init, onLog, onTreatmentApplied, setConsent, setFeatureFlag, setPageHydrated, setUserToken, trackConversion };