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