@absmartly/sdk-plugins 1.1.2 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/absmartly-cookie.min.js +1 -1
- package/dist/absmartly-dom-changes-core.min.js +1 -1
- package/dist/absmartly-dom-changes-lite.min.js +1 -1
- package/dist/absmartly-overrides-full.min.js +1 -1
- package/dist/absmartly-overrides-lite.min.js +1 -1
- package/dist/absmartly-sdk-plugins.min.js +1 -1
- package/dist/absmartly-vitals.min.js +1 -1
- package/es/cookies/cookieUtils.js +2 -1
- package/es/cookies/cookieUtils.js.map +1 -1
- package/es/core/DOMChangesPluginLite.js +195 -221
- package/es/core/DOMChangesPluginLite.js.map +1 -1
- package/es/core/DOMManipulatorLite.js +76 -10
- package/es/core/DOMManipulatorLite.js.map +1 -1
- package/es/core/ExposureTracker.js +136 -11
- package/es/core/ExposureTracker.js.map +1 -1
- package/es/core/PendingChangeManager.js +14 -14
- package/es/core/PendingChangeManager.js.map +1 -1
- package/es/entries/dom-with-overrides-lite.js +3 -0
- package/es/entries/dom-with-overrides-lite.js.map +1 -1
- package/es/generated/buildInfo.js +3 -0
- package/es/generated/buildInfo.js.map +1 -0
- package/es/index.js +9 -2
- package/es/index.js.map +1 -1
- package/es/overrides/OverridesPlugin.js +2 -2
- package/es/overrides/OverridesPlugin.js.map +1 -1
- package/es/overrides/OverridesPluginLite.js +29 -1
- package/es/overrides/OverridesPluginLite.js.map +1 -1
- package/es/overrides/overridesUtils.js +27 -18
- package/es/overrides/overridesUtils.js.map +1 -1
- package/es/parsers/VariantExtractor.js +14 -7
- package/es/parsers/VariantExtractor.js.map +1 -1
- package/es/url-redirect/URLRedirectExtractor.js +185 -0
- package/es/url-redirect/URLRedirectExtractor.js.map +1 -0
- package/es/url-redirect/URLRedirectMatcher.js +116 -0
- package/es/url-redirect/URLRedirectMatcher.js.map +1 -0
- package/es/url-redirect/URLRedirectPlugin.js +351 -0
- package/es/url-redirect/URLRedirectPlugin.js.map +1 -0
- package/es/url-redirect/index.js +5 -0
- package/es/url-redirect/index.js.map +1 -0
- package/es/url-redirect/types.js +2 -0
- package/es/url-redirect/types.js.map +1 -0
- package/es/utils/URLMatcher.js +4 -3
- package/es/utils/URLMatcher.js.map +1 -1
- package/es/utils/debug.js +4 -3
- package/es/utils/debug.js.map +1 -1
- package/es/utils/persistence.js +278 -0
- package/es/utils/persistence.js.map +1 -0
- package/es/utils/plugin-registry.js +47 -0
- package/es/utils/plugin-registry.js.map +1 -0
- package/lib/cookies/cookieUtils.d.ts +0 -4
- package/lib/cookies/cookieUtils.js +6 -5
- package/lib/cookies/cookieUtils.js.map +1 -1
- package/lib/core/DOMChangesPluginLite.d.ts +10 -7
- package/lib/core/DOMChangesPluginLite.js +195 -221
- package/lib/core/DOMChangesPluginLite.js.map +1 -1
- package/lib/core/DOMManipulatorLite.js +76 -10
- package/lib/core/DOMManipulatorLite.js.map +1 -1
- package/lib/core/ExposureTracker.d.ts +6 -0
- package/lib/core/ExposureTracker.js +136 -11
- package/lib/core/ExposureTracker.js.map +1 -1
- package/lib/core/PendingChangeManager.d.ts +0 -1
- package/lib/core/PendingChangeManager.js +14 -14
- package/lib/core/PendingChangeManager.js.map +1 -1
- package/lib/entries/dom-with-overrides-lite.d.ts +2 -0
- package/lib/entries/dom-with-overrides-lite.js +3 -1
- package/lib/entries/dom-with-overrides-lite.js.map +1 -1
- package/lib/generated/buildInfo.d.ts +2 -0
- package/lib/generated/buildInfo.js +6 -0
- package/lib/generated/buildInfo.js.map +1 -0
- package/lib/index.d.ts +8 -2
- package/lib/index.js +17 -1
- package/lib/index.js.map +1 -1
- package/lib/overrides/OverridesPlugin.js +2 -2
- package/lib/overrides/OverridesPlugin.js.map +1 -1
- package/lib/overrides/OverridesPluginLite.d.ts +8 -0
- package/lib/overrides/OverridesPluginLite.js +29 -1
- package/lib/overrides/OverridesPluginLite.js.map +1 -1
- package/lib/overrides/overridesUtils.d.ts +10 -5
- package/lib/overrides/overridesUtils.js +28 -18
- package/lib/overrides/overridesUtils.js.map +1 -1
- package/lib/parsers/VariantExtractor.js +14 -7
- package/lib/parsers/VariantExtractor.js.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/url-redirect/URLRedirectExtractor.d.ts +16 -0
- package/lib/url-redirect/URLRedirectExtractor.js +188 -0
- package/lib/url-redirect/URLRedirectExtractor.js.map +1 -0
- package/lib/url-redirect/URLRedirectMatcher.d.ts +8 -0
- package/lib/url-redirect/URLRedirectMatcher.js +119 -0
- package/lib/url-redirect/URLRedirectMatcher.js.map +1 -0
- package/lib/url-redirect/URLRedirectPlugin.d.ts +21 -0
- package/lib/url-redirect/URLRedirectPlugin.js +354 -0
- package/lib/url-redirect/URLRedirectPlugin.js.map +1 -0
- package/lib/url-redirect/index.d.ts +4 -0
- package/lib/url-redirect/index.js +25 -0
- package/lib/url-redirect/index.js.map +1 -0
- package/lib/url-redirect/types.d.ts +29 -0
- package/lib/url-redirect/types.js +3 -0
- package/lib/url-redirect/types.js.map +1 -0
- package/lib/utils/URLMatcher.js +4 -3
- package/lib/utils/URLMatcher.js.map +1 -1
- package/lib/utils/debug.d.ts +1 -1
- package/lib/utils/debug.js +4 -3
- package/lib/utils/debug.js.map +1 -1
- package/lib/utils/persistence.d.ts +23 -0
- package/lib/utils/persistence.js +281 -0
- package/lib/utils/persistence.js.map +1 -0
- package/lib/utils/plugin-registry.d.ts +40 -0
- package/lib/utils/plugin-registry.js +53 -0
- package/lib/utils/plugin-registry.js.map +1 -0
- package/package.json +64 -6
- package/types/cookies/cookieUtils.d.ts +0 -4
- package/types/core/DOMChangesPluginLite.d.ts +10 -7
- package/types/core/ExposureTracker.d.ts +6 -0
- package/types/core/PendingChangeManager.d.ts +0 -1
- package/types/entries/dom-with-overrides-lite.d.ts +2 -0
- package/types/generated/buildInfo.d.ts +2 -0
- package/types/index.d.ts +8 -2
- package/types/overrides/OverridesPluginLite.d.ts +8 -0
- package/types/overrides/overridesUtils.d.ts +10 -5
- package/types/types/index.d.ts +1 -0
- package/types/url-redirect/URLRedirectExtractor.d.ts +16 -0
- package/types/url-redirect/URLRedirectMatcher.d.ts +8 -0
- package/types/url-redirect/URLRedirectPlugin.d.ts +21 -0
- package/types/url-redirect/index.d.ts +4 -0
- package/types/url-redirect/types.d.ts +29 -0
- package/types/utils/debug.d.ts +1 -1
- package/types/utils/persistence.d.ts +23 -0
- package/types/utils/plugin-registry.d.ts +40 -0
|
@@ -79,6 +79,9 @@ import { ExposureTracker } from './ExposureTracker';
|
|
|
79
79
|
import { HTMLInjector } from './HTMLInjector';
|
|
80
80
|
import { logDebug, logExperimentSummary, logPerformance, DEBUG } from '../utils/debug';
|
|
81
81
|
import { URLMatcher } from '../utils/URLMatcher';
|
|
82
|
+
import { DOMPersistenceManager } from '../utils/persistence';
|
|
83
|
+
import { registerPlugin, unregisterPlugin } from '../utils/plugin-registry';
|
|
84
|
+
import { BUILD_VERSION } from '../generated/buildInfo';
|
|
82
85
|
var DOMChangesPluginLite = /** @class */ (function () {
|
|
83
86
|
function DOMChangesPluginLite(config) {
|
|
84
87
|
var _this = this;
|
|
@@ -88,11 +91,7 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
88
91
|
this.eventListeners = new Map();
|
|
89
92
|
this.styleManagers = new Map();
|
|
90
93
|
this.initialized = false;
|
|
91
|
-
this.
|
|
92
|
-
this.persistenceObserver = null;
|
|
93
|
-
this.reapplyingElements = new Set();
|
|
94
|
-
this.reapplyLogThrottle = new Map();
|
|
95
|
-
this.appliedChanges = new Map();
|
|
94
|
+
this.persistenceManager = null;
|
|
96
95
|
this.antiFlickerTimeout = null;
|
|
97
96
|
this.antiFlickerStyleId = 'absmartly-antiflicker';
|
|
98
97
|
this.config = {
|
|
@@ -117,6 +116,17 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
117
116
|
this.variantExtractor = new VariantExtractor(this.config.context, this.config.variableName, this.config.debug);
|
|
118
117
|
this.exposureTracker = new ExposureTracker(this.config.context, this.config.debug);
|
|
119
118
|
this.htmlInjector = new HTMLInjector(this.config.debug);
|
|
119
|
+
this.persistenceManager = new DOMPersistenceManager({
|
|
120
|
+
debug: this.config.debug,
|
|
121
|
+
onReapply: function (change, experimentName) {
|
|
122
|
+
logDebug('[DOMChangesPluginLite] Re-applying change due to mutation', {
|
|
123
|
+
experimentName: experimentName,
|
|
124
|
+
selector: change.selector,
|
|
125
|
+
type: change.type,
|
|
126
|
+
});
|
|
127
|
+
_this.domManipulator.applyChange(change, experimentName);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
120
130
|
// Auto-initialize when context is ready
|
|
121
131
|
this.readyPromise = this.config.context
|
|
122
132
|
.ready()
|
|
@@ -160,9 +170,14 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
160
170
|
case 1:
|
|
161
171
|
_a.trys.push([1, 4, , 5]);
|
|
162
172
|
if (this.config.spa) {
|
|
173
|
+
// SPA mode: observe body for React/Vue hydration recovery
|
|
174
|
+
// and listen for URL changes to re-apply changes on navigation
|
|
163
175
|
this.setupMutationObserver();
|
|
164
176
|
this.setupURLChangeListener();
|
|
165
177
|
}
|
|
178
|
+
else {
|
|
179
|
+
logDebug('[DOMChangesPluginLite] SPA mode disabled - skipping body observer');
|
|
180
|
+
}
|
|
166
181
|
if (!this.config.autoApply) return [3 /*break*/, 3];
|
|
167
182
|
// Apply HTML injections and DOM changes in parallel to minimize flickering
|
|
168
183
|
return [4 /*yield*/, this.applyInjectionsAndChanges()];
|
|
@@ -173,6 +188,7 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
173
188
|
case 3:
|
|
174
189
|
this.initialized = true;
|
|
175
190
|
this.registerWithContext();
|
|
191
|
+
this.registerGlobally();
|
|
176
192
|
this.emit('initialized');
|
|
177
193
|
duration = performance.now() - startTime;
|
|
178
194
|
logPerformance('Plugin initialization', duration);
|
|
@@ -198,10 +214,12 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
198
214
|
if (mutation.type === 'childList') {
|
|
199
215
|
// Check if any added nodes match our applied change selectors
|
|
200
216
|
mutation.addedNodes.forEach(function (node) {
|
|
217
|
+
var _a;
|
|
201
218
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
202
219
|
var element_1 = node;
|
|
203
220
|
// Check all experiments with applied changes (not just style changes)
|
|
204
|
-
_this.
|
|
221
|
+
var appliedChanges = ((_a = _this.persistenceManager) === null || _a === void 0 ? void 0 : _a.getAppliedChanges()) || new Map();
|
|
222
|
+
appliedChanges.forEach(function (changes, experimentName) {
|
|
205
223
|
changes.forEach(function (change) {
|
|
206
224
|
try {
|
|
207
225
|
// Check if this new element or any of its descendants match the selector
|
|
@@ -231,11 +249,30 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
231
249
|
}
|
|
232
250
|
});
|
|
233
251
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
// Wait for document.body to exist before observing
|
|
253
|
+
// This prevents "parameter 1 is not of type 'Node'" errors when SDK loads in <head>
|
|
254
|
+
var startObserving = function () {
|
|
255
|
+
observer.observe(document.body, {
|
|
256
|
+
childList: true,
|
|
257
|
+
subtree: true,
|
|
258
|
+
});
|
|
259
|
+
_this.mutationObserver = observer;
|
|
260
|
+
logDebug('[DOMChangesPluginLite] MutationObserver started on document.body');
|
|
261
|
+
};
|
|
262
|
+
if (document.body) {
|
|
263
|
+
startObserving();
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Use MutationObserver on documentElement to detect when body is added
|
|
267
|
+
var bodyObserver = new MutationObserver(function (_mutations, obs) {
|
|
268
|
+
if (document.body) {
|
|
269
|
+
obs.disconnect();
|
|
270
|
+
startObserving();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
bodyObserver.observe(document.documentElement, { childList: true });
|
|
274
|
+
logDebug('[DOMChangesPluginLite] Waiting for document.body...');
|
|
275
|
+
}
|
|
239
276
|
};
|
|
240
277
|
/**
|
|
241
278
|
* Set up URL change listener for SPA mode
|
|
@@ -302,7 +339,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
302
339
|
// The stylesheets will be reused or cleared on next applyChanges
|
|
303
340
|
this.styleManagers.clear();
|
|
304
341
|
// Clear applied changes
|
|
305
|
-
this.
|
|
342
|
+
if (this.persistenceManager) {
|
|
343
|
+
this.persistenceManager.clearAll();
|
|
344
|
+
}
|
|
306
345
|
// Clear HTML injections
|
|
307
346
|
this.htmlInjector.destroy();
|
|
308
347
|
// Clear variant extractor cache to force re-extraction
|
|
@@ -419,22 +458,46 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
419
458
|
allExperiments = this.getAllExperimentsData();
|
|
420
459
|
totalApplied = 0;
|
|
421
460
|
experimentStats = new Map();
|
|
422
|
-
logDebug('[
|
|
423
|
-
|
|
461
|
+
logDebug('[APPLY-CHANGES] Experiments found with DOM changes:', {
|
|
462
|
+
experimentNames: Array.from(allExperiments.keys()),
|
|
463
|
+
totalCount: allExperiments.size,
|
|
464
|
+
currentURL: currentURL,
|
|
465
|
+
});
|
|
466
|
+
if (allExperiments.size === 0) {
|
|
467
|
+
logDebug('[APPLY-CHANGES] ⚠️ No experiments found with DOM changes - nothing to process');
|
|
468
|
+
}
|
|
424
469
|
try {
|
|
425
470
|
for (allExperiments_1 = __values(allExperiments), allExperiments_1_1 = allExperiments_1.next(); !allExperiments_1_1.done; allExperiments_1_1 = allExperiments_1.next()) {
|
|
426
471
|
_a = __read(allExperiments_1_1.value, 2), expName = _a[0], experimentData = _a[1];
|
|
472
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] Starting to process experiment"), {
|
|
473
|
+
experimentName: expName,
|
|
474
|
+
currentURL: currentURL,
|
|
475
|
+
});
|
|
427
476
|
// Skip if filtering by specific experiment
|
|
428
477
|
if (experimentName && expName !== experimentName) {
|
|
478
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - filtering for different experiment"), {
|
|
479
|
+
filteringFor: experimentName,
|
|
480
|
+
});
|
|
429
481
|
continue;
|
|
430
482
|
}
|
|
431
483
|
currentVariant = this.config.context.peek(expName);
|
|
484
|
+
if (currentVariant === undefined || currentVariant === null) {
|
|
485
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - no variant assigned (peek returned ").concat(currentVariant, ")"), {
|
|
486
|
+
experimentName: expName,
|
|
487
|
+
});
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] User assigned to variant ").concat(currentVariant));
|
|
432
491
|
variantData = experimentData.variantData, urlFilter = experimentData.urlFilter, globalDefaults = experimentData.globalDefaults;
|
|
433
492
|
anyVariantMatchesURL = this.variantExtractor.anyVariantMatchesURL(expName, currentURL);
|
|
434
493
|
if (!anyVariantMatchesURL) {
|
|
435
|
-
logDebug("[
|
|
494
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - no variant matches URL: ").concat(currentURL), {
|
|
495
|
+
experimentName: expName,
|
|
496
|
+
currentURL: currentURL,
|
|
497
|
+
});
|
|
436
498
|
continue;
|
|
437
499
|
}
|
|
500
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u2713 At least one variant matches current URL"));
|
|
438
501
|
shouldApplyVisualChanges = this.shouldApplyVisualChanges(variantData, urlFilter, currentURL);
|
|
439
502
|
changes = this.extractChangesFromData(variantData, globalDefaults);
|
|
440
503
|
allVariantChanges = this.extractAllVariantChanges(expName);
|
|
@@ -448,7 +511,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
448
511
|
}
|
|
449
512
|
stats = { total: (changes === null || changes === void 0 ? void 0 : changes.length) || 0, success: 0, pending: 0 };
|
|
450
513
|
if (this.config.debug) {
|
|
451
|
-
logDebug("[ABsmartly] Processing experiment '".concat(expName, "'
|
|
514
|
+
logDebug("[ABsmartly] Processing experiment '".concat(expName, "' - User is in variant ").concat(currentVariant, ":"), {
|
|
515
|
+
experimentName: expName,
|
|
516
|
+
userVariant: currentVariant,
|
|
452
517
|
urlMatches: shouldApplyVisualChanges,
|
|
453
518
|
changeCount: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
454
519
|
userVariantHasChanges: ((changes === null || changes === void 0 ? void 0 : changes.length) || 0) > 0,
|
|
@@ -459,11 +524,28 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
459
524
|
}); })) || [],
|
|
460
525
|
});
|
|
461
526
|
}
|
|
527
|
+
// Log if user is in control variant (no changes)
|
|
528
|
+
if (!changes || changes.length === 0) {
|
|
529
|
+
if (this.config.debug) {
|
|
530
|
+
logDebug("[ABsmartly] User is in control variant ".concat(currentVariant, " with no changes, but will still track for SRM prevention"), {
|
|
531
|
+
experimentName: expName,
|
|
532
|
+
currentVariant: currentVariant,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
462
536
|
// Apply visual changes only if URL matches for user's variant AND user has changes
|
|
463
537
|
if (shouldApplyVisualChanges && changes && changes.length > 0) {
|
|
464
538
|
try {
|
|
465
539
|
for (changes_1 = (e_2 = void 0, __values(changes)), changes_1_1 = changes_1.next(); !changes_1_1.done; changes_1_1 = changes_1.next()) {
|
|
466
540
|
change = changes_1_1.value;
|
|
541
|
+
// Skip delete changes with viewport triggers - they must be applied AFTER exposure
|
|
542
|
+
// Otherwise the element is removed before it can be observed
|
|
543
|
+
if (change.type === 'delete' && change.trigger_on_view) {
|
|
544
|
+
if (this.config.debug) {
|
|
545
|
+
logDebug("[ABsmartly] Deferring delete change until after viewport trigger for '".concat(expName, "'"), { selector: change.selector });
|
|
546
|
+
}
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
467
549
|
success = this.domManipulator.applyChange(change, expName);
|
|
468
550
|
if (success) {
|
|
469
551
|
totalApplied++;
|
|
@@ -581,14 +663,41 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
581
663
|
finally { if (e_3) throw e_3.error; }
|
|
582
664
|
}
|
|
583
665
|
if (this.config.debug) {
|
|
584
|
-
logDebug("[
|
|
666
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] Final trigger decision for '").concat(expName, "':"), {
|
|
667
|
+
experimentName: expName,
|
|
668
|
+
userVariant: currentVariant,
|
|
669
|
+
hasImmediateTrigger: hasAnyImmediateTriggerInAnyVariant,
|
|
670
|
+
hasViewportTrigger: hasAnyViewportTriggerInAnyVariant,
|
|
671
|
+
willRegisterExperiment: hasAnyViewportTriggerInAnyVariant || hasAnyImmediateTriggerInAnyVariant,
|
|
672
|
+
userChangesCount: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
673
|
+
allVariantsChangesCount: allVariantChanges.map(function (vc) { return vc.length; }),
|
|
674
|
+
});
|
|
585
675
|
}
|
|
586
676
|
// CRITICAL: Always register experiment for tracking if ANY variant has ANY trigger type
|
|
587
677
|
// This prevents SRM even when user's variant doesn't match URL filter
|
|
588
678
|
// Pass the URL-filtered trigger flags to ExposureTracker
|
|
589
679
|
if (hasAnyViewportTriggerInAnyVariant || hasAnyImmediateTriggerInAnyVariant) {
|
|
680
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] \u2713 REGISTERING EXPERIMENT - Calling exposureTracker.registerExperiment()"), {
|
|
681
|
+
experimentName: expName,
|
|
682
|
+
userVariant: currentVariant,
|
|
683
|
+
hasImmediateTrigger: hasAnyImmediateTriggerInAnyVariant,
|
|
684
|
+
hasViewportTrigger: hasAnyViewportTriggerInAnyVariant,
|
|
685
|
+
changesForUser: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
686
|
+
allVariantCounts: allVariantChanges.map(function (vc, idx) { return ({
|
|
687
|
+
variant: idx,
|
|
688
|
+
count: vc.length,
|
|
689
|
+
}); }),
|
|
690
|
+
});
|
|
590
691
|
this.exposureTracker.registerExperiment(expName, currentVariant || 0, changes || [], allVariantChanges, hasAnyImmediateTriggerInAnyVariant, hasAnyViewportTriggerInAnyVariant);
|
|
591
692
|
}
|
|
693
|
+
else {
|
|
694
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] \u26A0\uFE0F No triggers detected - experiment will NOT be registered!"), {
|
|
695
|
+
experimentName: expName,
|
|
696
|
+
userVariant: currentVariant,
|
|
697
|
+
hasAnyChangesInAnyVariant: hasAnyChangesInAnyVariant,
|
|
698
|
+
allVariantsChangesCount: allVariantChanges.map(function (vc) { return vc.length; }),
|
|
699
|
+
});
|
|
700
|
+
}
|
|
592
701
|
// Note: We do NOT call treatment() here anymore to avoid duplicate calls.
|
|
593
702
|
// The ExposureTracker.registerExperiment() will call triggerExposure() internally
|
|
594
703
|
// when hasImmediateTrigger is true (see ExposureTracker line 169-174).
|
|
@@ -631,6 +740,7 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
631
740
|
if (this.config.hideUntilReady) {
|
|
632
741
|
this.showContent();
|
|
633
742
|
}
|
|
743
|
+
logDebug('[ABsmartly] DOM changes applied');
|
|
634
744
|
this.emit('changes-applied', { count: totalApplied, experimentName: experimentName });
|
|
635
745
|
return [2 /*return*/];
|
|
636
746
|
}
|
|
@@ -644,18 +754,43 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
644
754
|
var e_5, _a;
|
|
645
755
|
var experiments = new Map();
|
|
646
756
|
var allVariants = this.variantExtractor.extractAllChanges();
|
|
757
|
+
logDebug('[GET-EXPERIMENTS] Extracting experiment data from context', {
|
|
758
|
+
experimentsWithChanges: Array.from(allVariants.keys()),
|
|
759
|
+
totalCount: allVariants.size,
|
|
760
|
+
});
|
|
647
761
|
try {
|
|
648
762
|
for (var allVariants_1 = __values(allVariants), allVariants_1_1 = allVariants_1.next(); !allVariants_1_1.done; allVariants_1_1 = allVariants_1.next()) {
|
|
649
763
|
var _b = __read(allVariants_1_1.value, 1), expName = _b[0];
|
|
650
764
|
var currentVariant = this.config.context.peek(expName);
|
|
765
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] Checking experiment"), {
|
|
766
|
+
experimentName: expName,
|
|
767
|
+
currentVariant: currentVariant,
|
|
768
|
+
variantIsValid: currentVariant !== undefined && currentVariant !== null,
|
|
769
|
+
});
|
|
651
770
|
if (currentVariant === undefined || currentVariant === null) {
|
|
771
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u23ED\uFE0F Skipping - context.peek() returned ").concat(currentVariant));
|
|
652
772
|
continue;
|
|
653
773
|
}
|
|
654
774
|
var variantsData = this.variantExtractor.getAllVariantsData(expName);
|
|
655
775
|
var variantData = variantsData.get(currentVariant);
|
|
776
|
+
if (this.config.debug) {
|
|
777
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] Variant data lookup:"), {
|
|
778
|
+
experimentName: expName,
|
|
779
|
+
userVariant: currentVariant,
|
|
780
|
+
variantDataExists: !!variantData,
|
|
781
|
+
allVariantsInMap: Array.from(variantsData.keys()),
|
|
782
|
+
userVariantInMap: variantsData.has(currentVariant),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
656
785
|
if (!variantData) {
|
|
657
|
-
|
|
786
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u26A0\uFE0F User's variant ").concat(currentVariant, " has no DOM changes (control variant)"), {
|
|
787
|
+
availableVariants: Array.from(variantsData.keys()),
|
|
788
|
+
note: 'Will still track for SRM prevention',
|
|
789
|
+
});
|
|
790
|
+
// Don't skip! Include experiments even if user's variant has no changes
|
|
791
|
+
// This is critical for SRM prevention - we need to track ALL experiments
|
|
658
792
|
}
|
|
793
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u2713 Including experiment (variant ").concat(currentVariant, ")"));
|
|
659
794
|
// Extract URL filter and global defaults if using wrapped format
|
|
660
795
|
var urlFilter = null;
|
|
661
796
|
var globalDefaults = {};
|
|
@@ -688,12 +823,15 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
688
823
|
* Extract changes from DOMChangesData and apply global defaults
|
|
689
824
|
*/
|
|
690
825
|
DOMChangesPluginLite.prototype.extractChangesFromData = function (data, globalDefaults) {
|
|
826
|
+
if (!data) {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
691
829
|
var changes = null;
|
|
692
830
|
// Extract changes array
|
|
693
831
|
if (Array.isArray(data)) {
|
|
694
832
|
changes = data;
|
|
695
833
|
}
|
|
696
|
-
else if (
|
|
834
|
+
else if (typeof data === 'object' && 'changes' in data) {
|
|
697
835
|
changes = data.changes;
|
|
698
836
|
}
|
|
699
837
|
if (!changes || changes.length === 0) {
|
|
@@ -750,6 +888,10 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
750
888
|
* Determine if visual changes should be applied based on URL filter
|
|
751
889
|
*/
|
|
752
890
|
DOMChangesPluginLite.prototype.shouldApplyVisualChanges = function (variantData, urlFilter, url) {
|
|
891
|
+
// No data for this variant - no changes to apply
|
|
892
|
+
if (!variantData) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
753
895
|
// Legacy array format has no URL filter - always apply
|
|
754
896
|
if (Array.isArray(variantData)) {
|
|
755
897
|
return true;
|
|
@@ -883,9 +1025,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
883
1025
|
this.mutationObserver.disconnect();
|
|
884
1026
|
this.mutationObserver = null;
|
|
885
1027
|
}
|
|
886
|
-
if (this.
|
|
887
|
-
this.
|
|
888
|
-
this.
|
|
1028
|
+
if (this.persistenceManager) {
|
|
1029
|
+
this.persistenceManager.destroy();
|
|
1030
|
+
this.persistenceManager = null;
|
|
889
1031
|
}
|
|
890
1032
|
// Clean up anti-flicker timeout and style
|
|
891
1033
|
if (this.antiFlickerTimeout !== null) {
|
|
@@ -898,11 +1040,8 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
898
1040
|
}
|
|
899
1041
|
this.eventListeners.clear();
|
|
900
1042
|
this.exposedExperiments.clear();
|
|
901
|
-
this.watchedElements = new WeakMap();
|
|
902
|
-
this.appliedChanges.clear();
|
|
903
|
-
this.reapplyingElements.clear();
|
|
904
|
-
this.reapplyLogThrottle.clear();
|
|
905
1043
|
this.unregisterFromContext();
|
|
1044
|
+
this.unregisterGlobally();
|
|
906
1045
|
this.initialized = false;
|
|
907
1046
|
if (this.config.debug) {
|
|
908
1047
|
logDebug('[ABsmartly] DOM Changes Plugin Lite destroyed');
|
|
@@ -941,6 +1080,31 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
941
1080
|
}
|
|
942
1081
|
}
|
|
943
1082
|
};
|
|
1083
|
+
/**
|
|
1084
|
+
* Register plugin in global registry for detection
|
|
1085
|
+
*/
|
|
1086
|
+
DOMChangesPluginLite.prototype.registerGlobally = function () {
|
|
1087
|
+
registerPlugin('dom', {
|
|
1088
|
+
name: 'DOMChangesPluginLite',
|
|
1089
|
+
version: DOMChangesPluginLite.VERSION,
|
|
1090
|
+
initialized: true,
|
|
1091
|
+
timestamp: Date.now(),
|
|
1092
|
+
capabilities: ['spa', 'visibility', 'auto-apply'],
|
|
1093
|
+
instance: this,
|
|
1094
|
+
});
|
|
1095
|
+
if (this.config.debug) {
|
|
1096
|
+
logDebug('[ABsmartly] DOMChangesPluginLite registered in global window.__ABSMARTLY_PLUGINS__');
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
/**
|
|
1100
|
+
* Unregister plugin from global registry
|
|
1101
|
+
*/
|
|
1102
|
+
DOMChangesPluginLite.prototype.unregisterGlobally = function () {
|
|
1103
|
+
unregisterPlugin('dom');
|
|
1104
|
+
if (this.config.debug) {
|
|
1105
|
+
logDebug('[ABsmartly] DOMChangesPluginLite unregistered from global registry');
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
944
1108
|
/**
|
|
945
1109
|
* Hide content to prevent flicker before experiments are applied
|
|
946
1110
|
*/
|
|
@@ -1020,206 +1184,16 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
1020
1184
|
}
|
|
1021
1185
|
};
|
|
1022
1186
|
DOMChangesPluginLite.prototype.watchElement = function (element, experimentName, change) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
experiments = new Set();
|
|
1026
|
-
this.watchedElements.set(element, experiments);
|
|
1027
|
-
}
|
|
1028
|
-
experiments.add(experimentName);
|
|
1029
|
-
// Store the applied change for this experiment (ALL types: style, class, attribute, html, text, etc.)
|
|
1030
|
-
if (!this.appliedChanges.has(experimentName)) {
|
|
1031
|
-
this.appliedChanges.set(experimentName, []);
|
|
1032
|
-
}
|
|
1033
|
-
var changes = this.appliedChanges.get(experimentName);
|
|
1034
|
-
var isNewWatch = !changes.includes(change);
|
|
1035
|
-
if (isNewWatch) {
|
|
1036
|
-
changes.push(change);
|
|
1037
|
-
// Only log when actually adding a new watch, not on every reapply
|
|
1038
|
-
if (this.config.debug) {
|
|
1039
|
-
var currentStyles_1 = {};
|
|
1040
|
-
if (change.value && typeof change.value === 'object') {
|
|
1041
|
-
Object.keys(change.value).forEach(function (prop) {
|
|
1042
|
-
var cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
1043
|
-
currentStyles_1[cssProp] = element.style.getPropertyValue(cssProp);
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
logDebug('[WATCH-ELEMENT] Started watching element for persistence/hydration recovery', {
|
|
1047
|
-
experimentName: experimentName,
|
|
1048
|
-
selector: change.selector,
|
|
1049
|
-
element: element.tagName,
|
|
1050
|
-
changeType: change.type,
|
|
1051
|
-
value: change.value,
|
|
1052
|
-
currentStyles: change.type === 'style' ? currentStyles_1 : undefined,
|
|
1053
|
-
timestamp: Date.now(),
|
|
1054
|
-
});
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
if (!this.persistenceObserver) {
|
|
1058
|
-
this.setupPersistenceObserver();
|
|
1187
|
+
if (this.persistenceManager) {
|
|
1188
|
+
this.persistenceManager.watchElement(element, experimentName, change);
|
|
1059
1189
|
}
|
|
1060
1190
|
};
|
|
1061
1191
|
DOMChangesPluginLite.prototype.unwatchElement = function (element, experimentName) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
experiments.delete(experimentName);
|
|
1065
|
-
if (experiments.size === 0) {
|
|
1066
|
-
this.watchedElements.delete(element);
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
DOMChangesPluginLite.prototype.setupPersistenceObserver = function () {
|
|
1071
|
-
var _this = this;
|
|
1072
|
-
if (this.persistenceObserver)
|
|
1073
|
-
return;
|
|
1074
|
-
if (this.config.debug) {
|
|
1075
|
-
logDebug('[PERSISTENCE-OBSERVER] Setting up persistence observer', {
|
|
1076
|
-
timestamp: Date.now(),
|
|
1077
|
-
watchedElementsCount: this.watchedElements ? 'has WeakMap' : 'no WeakMap',
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
this.persistenceObserver = new MutationObserver(function (mutations) {
|
|
1081
|
-
if (_this.config.debug) {
|
|
1082
|
-
logDebug('[MUTATION-DETECTED] Persistence observer detected mutations', {
|
|
1083
|
-
mutationCount: mutations.length,
|
|
1084
|
-
timestamp: Date.now(),
|
|
1085
|
-
mutations: mutations.map(function (m) {
|
|
1086
|
-
var _a;
|
|
1087
|
-
return ({
|
|
1088
|
-
target: m.target.tagName,
|
|
1089
|
-
oldValue: (_a = m.oldValue) === null || _a === void 0 ? void 0 : _a.substring(0, 100),
|
|
1090
|
-
});
|
|
1091
|
-
}),
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
mutations.forEach(function (mutation) {
|
|
1095
|
-
var element = mutation.target;
|
|
1096
|
-
if (_this.reapplyingElements.has(element)) {
|
|
1097
|
-
if (_this.config.debug) {
|
|
1098
|
-
logDebug('[MUTATION-SKIP] Skipping mutation - currently reapplying', {
|
|
1099
|
-
element: element.tagName,
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1104
|
-
var experiments = _this.watchedElements.get(element);
|
|
1105
|
-
if (experiments) {
|
|
1106
|
-
// Throttle mutation detection logs to once per 5 seconds per element
|
|
1107
|
-
if (_this.config.debug) {
|
|
1108
|
-
var elementKey = "mutation:".concat(element.tagName, ":").concat(element.getAttribute('name') || element.className);
|
|
1109
|
-
var now = Date.now();
|
|
1110
|
-
var lastLogged = _this.reapplyLogThrottle.get(elementKey) || 0;
|
|
1111
|
-
if (now - lastLogged > 5000) {
|
|
1112
|
-
logDebug('[MUTATION-ON-WATCHED] Style mutation detected on watched element', {
|
|
1113
|
-
element: element.tagName,
|
|
1114
|
-
selector: element.getAttribute('name') || element.className,
|
|
1115
|
-
oldValue: mutation.oldValue,
|
|
1116
|
-
newValue: element.getAttribute('style'),
|
|
1117
|
-
experiments: Array.from(experiments),
|
|
1118
|
-
});
|
|
1119
|
-
_this.reapplyLogThrottle.set(elementKey, now);
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
experiments.forEach(function (experimentName) {
|
|
1123
|
-
var appliedChanges = _this.appliedChanges.get(experimentName);
|
|
1124
|
-
if (appliedChanges) {
|
|
1125
|
-
appliedChanges.forEach(function (change) {
|
|
1126
|
-
var e_7, _a;
|
|
1127
|
-
// Skip the .matches() check - we already know this element should have this change
|
|
1128
|
-
// because we added it to watchedElements when we applied it successfully.
|
|
1129
|
-
// The .matches() check fails for complex selectors with parent relationships (e.g., "div > p")
|
|
1130
|
-
if (change.type === 'style') {
|
|
1131
|
-
var needsReapply = _this.checkStyleOverwritten(element, change.value);
|
|
1132
|
-
if (needsReapply) {
|
|
1133
|
-
_this.reapplyingElements.add(element);
|
|
1134
|
-
var logKey = "".concat(experimentName, "-").concat(change.selector);
|
|
1135
|
-
var now = Date.now();
|
|
1136
|
-
var lastLogged = _this.reapplyLogThrottle.get(logKey) || 0;
|
|
1137
|
-
if (_this.config.debug && now - lastLogged > 5000) {
|
|
1138
|
-
logDebug('[REAPPLY-TRIGGERED] Reapplying style after mutation (React/framework conflict detected)', {
|
|
1139
|
-
experimentName: experimentName,
|
|
1140
|
-
selector: change.selector,
|
|
1141
|
-
element: element.tagName,
|
|
1142
|
-
timestamp: now,
|
|
1143
|
-
note: 'This happens when the page framework (React/Vue/etc) fights with DOM changes',
|
|
1144
|
-
});
|
|
1145
|
-
_this.reapplyLogThrottle.set(logKey, now);
|
|
1146
|
-
// Cleanup old throttle entries to prevent memory leak
|
|
1147
|
-
if (_this.reapplyLogThrottle.size > 100) {
|
|
1148
|
-
var oldestAllowed = now - 60000; // 1 minute
|
|
1149
|
-
try {
|
|
1150
|
-
for (var _b = __values(_this.reapplyLogThrottle.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
1151
|
-
var _d = __read(_c.value, 2), key = _d[0], time = _d[1];
|
|
1152
|
-
if (time < oldestAllowed) {
|
|
1153
|
-
_this.reapplyLogThrottle.delete(key);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
1158
|
-
finally {
|
|
1159
|
-
try {
|
|
1160
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
1161
|
-
}
|
|
1162
|
-
finally { if (e_7) throw e_7.error; }
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
_this.domManipulator.applyChange(change, experimentName);
|
|
1167
|
-
setTimeout(function () {
|
|
1168
|
-
_this.reapplyingElements.delete(element);
|
|
1169
|
-
}, 0);
|
|
1170
|
-
}
|
|
1171
|
-
else if (_this.config.debug) {
|
|
1172
|
-
logDebug('[MUTATION-NO-REAPPLY] Style mutation detected but no reapply needed', {
|
|
1173
|
-
experimentName: experimentName,
|
|
1174
|
-
selector: change.selector,
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
});
|
|
1183
|
-
});
|
|
1184
|
-
this.persistenceObserver.observe(document.body, {
|
|
1185
|
-
attributes: true,
|
|
1186
|
-
attributeFilter: ['style'],
|
|
1187
|
-
subtree: true,
|
|
1188
|
-
attributeOldValue: true,
|
|
1189
|
-
});
|
|
1190
|
-
if (this.config.debug) {
|
|
1191
|
-
logDebug('[PERSISTENCE-OBSERVER] Setup complete - now observing style mutations', {
|
|
1192
|
-
target: 'document.body',
|
|
1193
|
-
attributeFilter: ['style'],
|
|
1194
|
-
subtree: true,
|
|
1195
|
-
attributeOldValue: true,
|
|
1196
|
-
timestamp: Date.now(),
|
|
1197
|
-
});
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
DOMChangesPluginLite.prototype.checkStyleOverwritten = function (element, expectedStyles) {
|
|
1201
|
-
var e_8, _a;
|
|
1202
|
-
try {
|
|
1203
|
-
for (var _b = __values(Object.entries(expectedStyles)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
1204
|
-
var _d = __read(_c.value, 2), prop = _d[0], value = _d[1];
|
|
1205
|
-
var cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
1206
|
-
var currentValue = element.style.getPropertyValue(cssProp);
|
|
1207
|
-
var currentPriority = element.style.getPropertyPriority(cssProp);
|
|
1208
|
-
if (currentValue !== value || (value.includes('!important') && !currentPriority)) {
|
|
1209
|
-
return true;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
1214
|
-
finally {
|
|
1215
|
-
try {
|
|
1216
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
1217
|
-
}
|
|
1218
|
-
finally { if (e_8) throw e_8.error; }
|
|
1192
|
+
if (this.persistenceManager) {
|
|
1193
|
+
this.persistenceManager.unwatchElement(element, experimentName);
|
|
1219
1194
|
}
|
|
1220
|
-
return false;
|
|
1221
1195
|
};
|
|
1222
|
-
DOMChangesPluginLite.VERSION =
|
|
1196
|
+
DOMChangesPluginLite.VERSION = BUILD_VERSION;
|
|
1223
1197
|
return DOMChangesPluginLite;
|
|
1224
1198
|
}());
|
|
1225
1199
|
export { DOMChangesPluginLite };
|