@absmartly/sdk-plugins 1.1.2 → 1.2.1
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-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/es/core/DOMChangesPluginLite.js +192 -220
- package/es/core/DOMChangesPluginLite.js.map +1 -1
- package/es/core/DOMManipulatorLite.js +66 -9
- 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/entries/dom-with-overrides-lite.js +3 -0
- package/es/entries/dom-with-overrides-lite.js.map +1 -1
- 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 +115 -0
- package/es/url-redirect/URLRedirectMatcher.js.map +1 -0
- package/es/url-redirect/URLRedirectPlugin.js +350 -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/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/core/DOMChangesPluginLite.d.ts +10 -7
- package/lib/core/DOMChangesPluginLite.js +192 -220
- package/lib/core/DOMChangesPluginLite.js.map +1 -1
- package/lib/core/DOMManipulatorLite.js +66 -9
- 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/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/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 +118 -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 +353 -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/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 +59 -3
- package/types/core/DOMChangesPluginLite.d.ts +10 -7
- package/types/core/ExposureTracker.d.ts +6 -0
- package/types/entries/dom-with-overrides-lite.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/persistence.d.ts +23 -0
- package/types/utils/plugin-registry.d.ts +40 -0
|
@@ -79,6 +79,8 @@ 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';
|
|
82
84
|
var DOMChangesPluginLite = /** @class */ (function () {
|
|
83
85
|
function DOMChangesPluginLite(config) {
|
|
84
86
|
var _this = this;
|
|
@@ -88,11 +90,7 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
88
90
|
this.eventListeners = new Map();
|
|
89
91
|
this.styleManagers = new Map();
|
|
90
92
|
this.initialized = false;
|
|
91
|
-
this.
|
|
92
|
-
this.persistenceObserver = null;
|
|
93
|
-
this.reapplyingElements = new Set();
|
|
94
|
-
this.reapplyLogThrottle = new Map();
|
|
95
|
-
this.appliedChanges = new Map();
|
|
93
|
+
this.persistenceManager = null;
|
|
96
94
|
this.antiFlickerTimeout = null;
|
|
97
95
|
this.antiFlickerStyleId = 'absmartly-antiflicker';
|
|
98
96
|
this.config = {
|
|
@@ -117,6 +115,17 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
117
115
|
this.variantExtractor = new VariantExtractor(this.config.context, this.config.variableName, this.config.debug);
|
|
118
116
|
this.exposureTracker = new ExposureTracker(this.config.context, this.config.debug);
|
|
119
117
|
this.htmlInjector = new HTMLInjector(this.config.debug);
|
|
118
|
+
this.persistenceManager = new DOMPersistenceManager({
|
|
119
|
+
debug: this.config.debug,
|
|
120
|
+
onReapply: function (change, experimentName) {
|
|
121
|
+
logDebug('[DOMChangesPluginLite] Re-applying change due to mutation', {
|
|
122
|
+
experimentName: experimentName,
|
|
123
|
+
selector: change.selector,
|
|
124
|
+
type: change.type,
|
|
125
|
+
});
|
|
126
|
+
_this.domManipulator.applyChange(change, experimentName);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
120
129
|
// Auto-initialize when context is ready
|
|
121
130
|
this.readyPromise = this.config.context
|
|
122
131
|
.ready()
|
|
@@ -160,9 +169,14 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
160
169
|
case 1:
|
|
161
170
|
_a.trys.push([1, 4, , 5]);
|
|
162
171
|
if (this.config.spa) {
|
|
172
|
+
// SPA mode: observe body for React/Vue hydration recovery
|
|
173
|
+
// and listen for URL changes to re-apply changes on navigation
|
|
163
174
|
this.setupMutationObserver();
|
|
164
175
|
this.setupURLChangeListener();
|
|
165
176
|
}
|
|
177
|
+
else {
|
|
178
|
+
logDebug('[DOMChangesPluginLite] SPA mode disabled - skipping body observer');
|
|
179
|
+
}
|
|
166
180
|
if (!this.config.autoApply) return [3 /*break*/, 3];
|
|
167
181
|
// Apply HTML injections and DOM changes in parallel to minimize flickering
|
|
168
182
|
return [4 /*yield*/, this.applyInjectionsAndChanges()];
|
|
@@ -173,6 +187,7 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
173
187
|
case 3:
|
|
174
188
|
this.initialized = true;
|
|
175
189
|
this.registerWithContext();
|
|
190
|
+
this.registerGlobally();
|
|
176
191
|
this.emit('initialized');
|
|
177
192
|
duration = performance.now() - startTime;
|
|
178
193
|
logPerformance('Plugin initialization', duration);
|
|
@@ -198,10 +213,12 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
198
213
|
if (mutation.type === 'childList') {
|
|
199
214
|
// Check if any added nodes match our applied change selectors
|
|
200
215
|
mutation.addedNodes.forEach(function (node) {
|
|
216
|
+
var _a;
|
|
201
217
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
202
218
|
var element_1 = node;
|
|
203
219
|
// Check all experiments with applied changes (not just style changes)
|
|
204
|
-
_this.
|
|
220
|
+
var appliedChanges = ((_a = _this.persistenceManager) === null || _a === void 0 ? void 0 : _a.getAppliedChanges()) || new Map();
|
|
221
|
+
appliedChanges.forEach(function (changes, experimentName) {
|
|
205
222
|
changes.forEach(function (change) {
|
|
206
223
|
try {
|
|
207
224
|
// Check if this new element or any of its descendants match the selector
|
|
@@ -231,11 +248,30 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
231
248
|
}
|
|
232
249
|
});
|
|
233
250
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
251
|
+
// Wait for document.body to exist before observing
|
|
252
|
+
// This prevents "parameter 1 is not of type 'Node'" errors when SDK loads in <head>
|
|
253
|
+
var startObserving = function () {
|
|
254
|
+
observer.observe(document.body, {
|
|
255
|
+
childList: true,
|
|
256
|
+
subtree: true,
|
|
257
|
+
});
|
|
258
|
+
_this.mutationObserver = observer;
|
|
259
|
+
logDebug('[DOMChangesPluginLite] MutationObserver started on document.body');
|
|
260
|
+
};
|
|
261
|
+
if (document.body) {
|
|
262
|
+
startObserving();
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// Use MutationObserver on documentElement to detect when body is added
|
|
266
|
+
var bodyObserver = new MutationObserver(function (_mutations, obs) {
|
|
267
|
+
if (document.body) {
|
|
268
|
+
obs.disconnect();
|
|
269
|
+
startObserving();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
bodyObserver.observe(document.documentElement, { childList: true });
|
|
273
|
+
logDebug('[DOMChangesPluginLite] Waiting for document.body...');
|
|
274
|
+
}
|
|
239
275
|
};
|
|
240
276
|
/**
|
|
241
277
|
* Set up URL change listener for SPA mode
|
|
@@ -302,7 +338,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
302
338
|
// The stylesheets will be reused or cleared on next applyChanges
|
|
303
339
|
this.styleManagers.clear();
|
|
304
340
|
// Clear applied changes
|
|
305
|
-
this.
|
|
341
|
+
if (this.persistenceManager) {
|
|
342
|
+
this.persistenceManager.clearAll();
|
|
343
|
+
}
|
|
306
344
|
// Clear HTML injections
|
|
307
345
|
this.htmlInjector.destroy();
|
|
308
346
|
// Clear variant extractor cache to force re-extraction
|
|
@@ -419,22 +457,46 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
419
457
|
allExperiments = this.getAllExperimentsData();
|
|
420
458
|
totalApplied = 0;
|
|
421
459
|
experimentStats = new Map();
|
|
422
|
-
logDebug('[
|
|
423
|
-
|
|
460
|
+
logDebug('[APPLY-CHANGES] Experiments found with DOM changes:', {
|
|
461
|
+
experimentNames: Array.from(allExperiments.keys()),
|
|
462
|
+
totalCount: allExperiments.size,
|
|
463
|
+
currentURL: currentURL,
|
|
464
|
+
});
|
|
465
|
+
if (allExperiments.size === 0) {
|
|
466
|
+
logDebug('[APPLY-CHANGES] ⚠️ No experiments found with DOM changes - nothing to process');
|
|
467
|
+
}
|
|
424
468
|
try {
|
|
425
469
|
for (allExperiments_1 = __values(allExperiments), allExperiments_1_1 = allExperiments_1.next(); !allExperiments_1_1.done; allExperiments_1_1 = allExperiments_1.next()) {
|
|
426
470
|
_a = __read(allExperiments_1_1.value, 2), expName = _a[0], experimentData = _a[1];
|
|
471
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] Starting to process experiment"), {
|
|
472
|
+
experimentName: expName,
|
|
473
|
+
currentURL: currentURL,
|
|
474
|
+
});
|
|
427
475
|
// Skip if filtering by specific experiment
|
|
428
476
|
if (experimentName && expName !== experimentName) {
|
|
477
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - filtering for different experiment"), {
|
|
478
|
+
filteringFor: experimentName,
|
|
479
|
+
});
|
|
429
480
|
continue;
|
|
430
481
|
}
|
|
431
482
|
currentVariant = this.config.context.peek(expName);
|
|
483
|
+
if (currentVariant === undefined || currentVariant === null) {
|
|
484
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - no variant assigned (peek returned ").concat(currentVariant, ")"), {
|
|
485
|
+
experimentName: expName,
|
|
486
|
+
});
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] User assigned to variant ").concat(currentVariant));
|
|
432
490
|
variantData = experimentData.variantData, urlFilter = experimentData.urlFilter, globalDefaults = experimentData.globalDefaults;
|
|
433
491
|
anyVariantMatchesURL = this.variantExtractor.anyVariantMatchesURL(expName, currentURL);
|
|
434
492
|
if (!anyVariantMatchesURL) {
|
|
435
|
-
logDebug("[
|
|
493
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u23ED\uFE0F Skipping - no variant matches URL: ").concat(currentURL), {
|
|
494
|
+
experimentName: expName,
|
|
495
|
+
currentURL: currentURL,
|
|
496
|
+
});
|
|
436
497
|
continue;
|
|
437
498
|
}
|
|
499
|
+
logDebug("[APPLY-CHANGES] [".concat(expName, "] \u2713 At least one variant matches current URL"));
|
|
438
500
|
shouldApplyVisualChanges = this.shouldApplyVisualChanges(variantData, urlFilter, currentURL);
|
|
439
501
|
changes = this.extractChangesFromData(variantData, globalDefaults);
|
|
440
502
|
allVariantChanges = this.extractAllVariantChanges(expName);
|
|
@@ -448,7 +510,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
448
510
|
}
|
|
449
511
|
stats = { total: (changes === null || changes === void 0 ? void 0 : changes.length) || 0, success: 0, pending: 0 };
|
|
450
512
|
if (this.config.debug) {
|
|
451
|
-
logDebug("[ABsmartly] Processing experiment '".concat(expName, "'
|
|
513
|
+
logDebug("[ABsmartly] Processing experiment '".concat(expName, "' - User is in variant ").concat(currentVariant, ":"), {
|
|
514
|
+
experimentName: expName,
|
|
515
|
+
userVariant: currentVariant,
|
|
452
516
|
urlMatches: shouldApplyVisualChanges,
|
|
453
517
|
changeCount: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
454
518
|
userVariantHasChanges: ((changes === null || changes === void 0 ? void 0 : changes.length) || 0) > 0,
|
|
@@ -459,11 +523,28 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
459
523
|
}); })) || [],
|
|
460
524
|
});
|
|
461
525
|
}
|
|
526
|
+
// Log if user is in control variant (no changes)
|
|
527
|
+
if (!changes || changes.length === 0) {
|
|
528
|
+
if (this.config.debug) {
|
|
529
|
+
logDebug("[ABsmartly] User is in control variant ".concat(currentVariant, " with no changes, but will still track for SRM prevention"), {
|
|
530
|
+
experimentName: expName,
|
|
531
|
+
currentVariant: currentVariant,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
462
535
|
// Apply visual changes only if URL matches for user's variant AND user has changes
|
|
463
536
|
if (shouldApplyVisualChanges && changes && changes.length > 0) {
|
|
464
537
|
try {
|
|
465
538
|
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
539
|
change = changes_1_1.value;
|
|
540
|
+
// Skip delete changes with viewport triggers - they must be applied AFTER exposure
|
|
541
|
+
// Otherwise the element is removed before it can be observed
|
|
542
|
+
if (change.type === 'delete' && change.trigger_on_view) {
|
|
543
|
+
if (this.config.debug) {
|
|
544
|
+
logDebug("[ABsmartly] Deferring delete change until after viewport trigger for '".concat(expName, "'"), { selector: change.selector });
|
|
545
|
+
}
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
467
548
|
success = this.domManipulator.applyChange(change, expName);
|
|
468
549
|
if (success) {
|
|
469
550
|
totalApplied++;
|
|
@@ -581,14 +662,41 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
581
662
|
finally { if (e_3) throw e_3.error; }
|
|
582
663
|
}
|
|
583
664
|
if (this.config.debug) {
|
|
584
|
-
logDebug("[
|
|
665
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] Final trigger decision for '").concat(expName, "':"), {
|
|
666
|
+
experimentName: expName,
|
|
667
|
+
userVariant: currentVariant,
|
|
668
|
+
hasImmediateTrigger: hasAnyImmediateTriggerInAnyVariant,
|
|
669
|
+
hasViewportTrigger: hasAnyViewportTriggerInAnyVariant,
|
|
670
|
+
willRegisterExperiment: hasAnyViewportTriggerInAnyVariant || hasAnyImmediateTriggerInAnyVariant,
|
|
671
|
+
userChangesCount: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
672
|
+
allVariantsChangesCount: allVariantChanges.map(function (vc) { return vc.length; }),
|
|
673
|
+
});
|
|
585
674
|
}
|
|
586
675
|
// CRITICAL: Always register experiment for tracking if ANY variant has ANY trigger type
|
|
587
676
|
// This prevents SRM even when user's variant doesn't match URL filter
|
|
588
677
|
// Pass the URL-filtered trigger flags to ExposureTracker
|
|
589
678
|
if (hasAnyViewportTriggerInAnyVariant || hasAnyImmediateTriggerInAnyVariant) {
|
|
679
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] \u2713 REGISTERING EXPERIMENT - Calling exposureTracker.registerExperiment()"), {
|
|
680
|
+
experimentName: expName,
|
|
681
|
+
userVariant: currentVariant,
|
|
682
|
+
hasImmediateTrigger: hasAnyImmediateTriggerInAnyVariant,
|
|
683
|
+
hasViewportTrigger: hasAnyViewportTriggerInAnyVariant,
|
|
684
|
+
changesForUser: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
|
|
685
|
+
allVariantCounts: allVariantChanges.map(function (vc, idx) { return ({
|
|
686
|
+
variant: idx,
|
|
687
|
+
count: vc.length,
|
|
688
|
+
}); }),
|
|
689
|
+
});
|
|
590
690
|
this.exposureTracker.registerExperiment(expName, currentVariant || 0, changes || [], allVariantChanges, hasAnyImmediateTriggerInAnyVariant, hasAnyViewportTriggerInAnyVariant);
|
|
591
691
|
}
|
|
692
|
+
else {
|
|
693
|
+
logDebug("[TRIGGER-DETECTION] [".concat(expName, "] \u26A0\uFE0F No triggers detected - experiment will NOT be registered!"), {
|
|
694
|
+
experimentName: expName,
|
|
695
|
+
userVariant: currentVariant,
|
|
696
|
+
hasAnyChangesInAnyVariant: hasAnyChangesInAnyVariant,
|
|
697
|
+
allVariantsChangesCount: allVariantChanges.map(function (vc) { return vc.length; }),
|
|
698
|
+
});
|
|
699
|
+
}
|
|
592
700
|
// Note: We do NOT call treatment() here anymore to avoid duplicate calls.
|
|
593
701
|
// The ExposureTracker.registerExperiment() will call triggerExposure() internally
|
|
594
702
|
// when hasImmediateTrigger is true (see ExposureTracker line 169-174).
|
|
@@ -644,18 +752,43 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
644
752
|
var e_5, _a;
|
|
645
753
|
var experiments = new Map();
|
|
646
754
|
var allVariants = this.variantExtractor.extractAllChanges();
|
|
755
|
+
logDebug('[GET-EXPERIMENTS] Extracting experiment data from context', {
|
|
756
|
+
experimentsWithChanges: Array.from(allVariants.keys()),
|
|
757
|
+
totalCount: allVariants.size,
|
|
758
|
+
});
|
|
647
759
|
try {
|
|
648
760
|
for (var allVariants_1 = __values(allVariants), allVariants_1_1 = allVariants_1.next(); !allVariants_1_1.done; allVariants_1_1 = allVariants_1.next()) {
|
|
649
761
|
var _b = __read(allVariants_1_1.value, 1), expName = _b[0];
|
|
650
762
|
var currentVariant = this.config.context.peek(expName);
|
|
763
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] Checking experiment"), {
|
|
764
|
+
experimentName: expName,
|
|
765
|
+
currentVariant: currentVariant,
|
|
766
|
+
variantIsValid: currentVariant !== undefined && currentVariant !== null,
|
|
767
|
+
});
|
|
651
768
|
if (currentVariant === undefined || currentVariant === null) {
|
|
769
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u23ED\uFE0F Skipping - context.peek() returned ").concat(currentVariant));
|
|
652
770
|
continue;
|
|
653
771
|
}
|
|
654
772
|
var variantsData = this.variantExtractor.getAllVariantsData(expName);
|
|
655
773
|
var variantData = variantsData.get(currentVariant);
|
|
774
|
+
if (this.config.debug) {
|
|
775
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] Variant data lookup:"), {
|
|
776
|
+
experimentName: expName,
|
|
777
|
+
userVariant: currentVariant,
|
|
778
|
+
variantDataExists: !!variantData,
|
|
779
|
+
allVariantsInMap: Array.from(variantsData.keys()),
|
|
780
|
+
userVariantInMap: variantsData.has(currentVariant),
|
|
781
|
+
});
|
|
782
|
+
}
|
|
656
783
|
if (!variantData) {
|
|
657
|
-
|
|
784
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u26A0\uFE0F User's variant ").concat(currentVariant, " has no DOM changes (control variant)"), {
|
|
785
|
+
availableVariants: Array.from(variantsData.keys()),
|
|
786
|
+
note: 'Will still track for SRM prevention',
|
|
787
|
+
});
|
|
788
|
+
// Don't skip! Include experiments even if user's variant has no changes
|
|
789
|
+
// This is critical for SRM prevention - we need to track ALL experiments
|
|
658
790
|
}
|
|
791
|
+
logDebug("[GET-EXPERIMENTS] [".concat(expName, "] \u2713 Including experiment (variant ").concat(currentVariant, ")"));
|
|
659
792
|
// Extract URL filter and global defaults if using wrapped format
|
|
660
793
|
var urlFilter = null;
|
|
661
794
|
var globalDefaults = {};
|
|
@@ -688,12 +821,15 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
688
821
|
* Extract changes from DOMChangesData and apply global defaults
|
|
689
822
|
*/
|
|
690
823
|
DOMChangesPluginLite.prototype.extractChangesFromData = function (data, globalDefaults) {
|
|
824
|
+
if (!data) {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
691
827
|
var changes = null;
|
|
692
828
|
// Extract changes array
|
|
693
829
|
if (Array.isArray(data)) {
|
|
694
830
|
changes = data;
|
|
695
831
|
}
|
|
696
|
-
else if (
|
|
832
|
+
else if (typeof data === 'object' && 'changes' in data) {
|
|
697
833
|
changes = data.changes;
|
|
698
834
|
}
|
|
699
835
|
if (!changes || changes.length === 0) {
|
|
@@ -750,6 +886,10 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
750
886
|
* Determine if visual changes should be applied based on URL filter
|
|
751
887
|
*/
|
|
752
888
|
DOMChangesPluginLite.prototype.shouldApplyVisualChanges = function (variantData, urlFilter, url) {
|
|
889
|
+
// No data for this variant - no changes to apply
|
|
890
|
+
if (!variantData) {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
753
893
|
// Legacy array format has no URL filter - always apply
|
|
754
894
|
if (Array.isArray(variantData)) {
|
|
755
895
|
return true;
|
|
@@ -883,9 +1023,9 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
883
1023
|
this.mutationObserver.disconnect();
|
|
884
1024
|
this.mutationObserver = null;
|
|
885
1025
|
}
|
|
886
|
-
if (this.
|
|
887
|
-
this.
|
|
888
|
-
this.
|
|
1026
|
+
if (this.persistenceManager) {
|
|
1027
|
+
this.persistenceManager.destroy();
|
|
1028
|
+
this.persistenceManager = null;
|
|
889
1029
|
}
|
|
890
1030
|
// Clean up anti-flicker timeout and style
|
|
891
1031
|
if (this.antiFlickerTimeout !== null) {
|
|
@@ -898,11 +1038,8 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
898
1038
|
}
|
|
899
1039
|
this.eventListeners.clear();
|
|
900
1040
|
this.exposedExperiments.clear();
|
|
901
|
-
this.watchedElements = new WeakMap();
|
|
902
|
-
this.appliedChanges.clear();
|
|
903
|
-
this.reapplyingElements.clear();
|
|
904
|
-
this.reapplyLogThrottle.clear();
|
|
905
1041
|
this.unregisterFromContext();
|
|
1042
|
+
this.unregisterGlobally();
|
|
906
1043
|
this.initialized = false;
|
|
907
1044
|
if (this.config.debug) {
|
|
908
1045
|
logDebug('[ABsmartly] DOM Changes Plugin Lite destroyed');
|
|
@@ -941,6 +1078,31 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
941
1078
|
}
|
|
942
1079
|
}
|
|
943
1080
|
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Register plugin in global registry for detection
|
|
1083
|
+
*/
|
|
1084
|
+
DOMChangesPluginLite.prototype.registerGlobally = function () {
|
|
1085
|
+
registerPlugin('dom', {
|
|
1086
|
+
name: 'DOMChangesPluginLite',
|
|
1087
|
+
version: DOMChangesPluginLite.VERSION,
|
|
1088
|
+
initialized: true,
|
|
1089
|
+
timestamp: Date.now(),
|
|
1090
|
+
capabilities: ['spa', 'visibility', 'auto-apply'],
|
|
1091
|
+
instance: this,
|
|
1092
|
+
});
|
|
1093
|
+
if (this.config.debug) {
|
|
1094
|
+
logDebug('[ABsmartly] DOMChangesPluginLite registered in global window.__ABSMARTLY_PLUGINS__');
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* Unregister plugin from global registry
|
|
1099
|
+
*/
|
|
1100
|
+
DOMChangesPluginLite.prototype.unregisterGlobally = function () {
|
|
1101
|
+
unregisterPlugin('dom');
|
|
1102
|
+
if (this.config.debug) {
|
|
1103
|
+
logDebug('[ABsmartly] DOMChangesPluginLite unregistered from global registry');
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
944
1106
|
/**
|
|
945
1107
|
* Hide content to prevent flicker before experiments are applied
|
|
946
1108
|
*/
|
|
@@ -1020,204 +1182,14 @@ var DOMChangesPluginLite = /** @class */ (function () {
|
|
|
1020
1182
|
}
|
|
1021
1183
|
};
|
|
1022
1184
|
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();
|
|
1185
|
+
if (this.persistenceManager) {
|
|
1186
|
+
this.persistenceManager.watchElement(element, experimentName, change);
|
|
1059
1187
|
}
|
|
1060
1188
|
};
|
|
1061
1189
|
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; }
|
|
1190
|
+
if (this.persistenceManager) {
|
|
1191
|
+
this.persistenceManager.unwatchElement(element, experimentName);
|
|
1219
1192
|
}
|
|
1220
|
-
return false;
|
|
1221
1193
|
};
|
|
1222
1194
|
DOMChangesPluginLite.VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : '1.1.2';
|
|
1223
1195
|
return DOMChangesPluginLite;
|