@absmartly/sdk-plugins 1.0.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.
Files changed (177) hide show
  1. package/README.md +1101 -0
  2. package/dist/absmartly-cookie.min.js +1 -0
  3. package/dist/absmartly-dom-changes-core.min.js +1 -0
  4. package/dist/absmartly-dom-changes-lite.min.js +1 -0
  5. package/dist/absmartly-overrides-full.min.js +1 -0
  6. package/dist/absmartly-overrides-lite.min.js +1 -0
  7. package/dist/absmartly-sdk-plugins.min.js +1 -0
  8. package/dist/absmartly-vitals.min.js +1 -0
  9. package/es/cookies/CookiePlugin.js +410 -0
  10. package/es/cookies/CookiePlugin.js.map +1 -0
  11. package/es/cookies/index.js +2 -0
  12. package/es/cookies/index.js.map +1 -0
  13. package/es/core/DOMChangesPluginLite.js +1124 -0
  14. package/es/core/DOMChangesPluginLite.js.map +1 -0
  15. package/es/core/DOMManipulatorLite.js +410 -0
  16. package/es/core/DOMManipulatorLite.js.map +1 -0
  17. package/es/core/ExposureTracker.js +501 -0
  18. package/es/core/ExposureTracker.js.map +1 -0
  19. package/es/core/HTMLInjector.js +333 -0
  20. package/es/core/HTMLInjector.js.map +1 -0
  21. package/es/core/PendingChangeManager.js +440 -0
  22. package/es/core/PendingChangeManager.js.map +1 -0
  23. package/es/core/StyleSheetManager.js +78 -0
  24. package/es/core/StyleSheetManager.js.map +1 -0
  25. package/es/entries/cookie-only.js +10 -0
  26. package/es/entries/cookie-only.js.map +1 -0
  27. package/es/entries/dom-only.js +13 -0
  28. package/es/entries/dom-only.js.map +1 -0
  29. package/es/entries/dom-with-overrides-full.js +19 -0
  30. package/es/entries/dom-with-overrides-full.js.map +1 -0
  31. package/es/entries/dom-with-overrides-lite.js +15 -0
  32. package/es/entries/dom-with-overrides-lite.js.map +1 -0
  33. package/es/entries/overrides-full-only.js +15 -0
  34. package/es/entries/overrides-full-only.js.map +1 -0
  35. package/es/entries/overrides-lite-only.js +12 -0
  36. package/es/entries/overrides-lite-only.js.map +1 -0
  37. package/es/entries/vitals-only.js +10 -0
  38. package/es/entries/vitals-only.js.map +1 -0
  39. package/es/index.js +17 -0
  40. package/es/index.js.map +1 -0
  41. package/es/overrides/BrowserCookieAdapter.js +76 -0
  42. package/es/overrides/BrowserCookieAdapter.js.map +1 -0
  43. package/es/overrides/OverridesPlugin.js +903 -0
  44. package/es/overrides/OverridesPlugin.js.map +1 -0
  45. package/es/overrides/OverridesPluginFull.js +32 -0
  46. package/es/overrides/OverridesPluginFull.js.map +1 -0
  47. package/es/overrides/OverridesPluginLite.js +352 -0
  48. package/es/overrides/OverridesPluginLite.js.map +1 -0
  49. package/es/overrides/index.js +4 -0
  50. package/es/overrides/index.js.map +1 -0
  51. package/es/overrides/types.js +2 -0
  52. package/es/overrides/types.js.map +1 -0
  53. package/es/parsers/VariantExtractor.js +570 -0
  54. package/es/parsers/VariantExtractor.js.map +1 -0
  55. package/es/types/index.js +2 -0
  56. package/es/types/index.js.map +1 -0
  57. package/es/utils/URLMatcher.js +114 -0
  58. package/es/utils/URLMatcher.js.map +1 -0
  59. package/es/utils/debug.js +184 -0
  60. package/es/utils/debug.js.map +1 -0
  61. package/es/vitals/WebVitalsPlugin.js +379 -0
  62. package/es/vitals/WebVitalsPlugin.js.map +1 -0
  63. package/es/vitals/index.js +2 -0
  64. package/es/vitals/index.js.map +1 -0
  65. package/lib/cookies/CookiePlugin.d.ts +55 -0
  66. package/lib/cookies/CookiePlugin.js +413 -0
  67. package/lib/cookies/CookiePlugin.js.map +1 -0
  68. package/lib/cookies/index.d.ts +2 -0
  69. package/lib/cookies/index.js +6 -0
  70. package/lib/cookies/index.js.map +1 -0
  71. package/lib/core/DOMChangesPluginLite.d.ts +89 -0
  72. package/lib/core/DOMChangesPluginLite.js +1127 -0
  73. package/lib/core/DOMChangesPluginLite.js.map +1 -0
  74. package/lib/core/DOMManipulatorLite.d.ts +21 -0
  75. package/lib/core/DOMManipulatorLite.js +413 -0
  76. package/lib/core/DOMManipulatorLite.js.map +1 -0
  77. package/lib/core/ExposureTracker.d.ts +81 -0
  78. package/lib/core/ExposureTracker.js +504 -0
  79. package/lib/core/ExposureTracker.js.map +1 -0
  80. package/lib/core/HTMLInjector.d.ts +19 -0
  81. package/lib/core/HTMLInjector.js +336 -0
  82. package/lib/core/HTMLInjector.js.map +1 -0
  83. package/lib/core/PendingChangeManager.d.ts +31 -0
  84. package/lib/core/PendingChangeManager.js +443 -0
  85. package/lib/core/PendingChangeManager.js.map +1 -0
  86. package/lib/core/StyleSheetManager.d.ts +16 -0
  87. package/lib/core/StyleSheetManager.js +81 -0
  88. package/lib/core/StyleSheetManager.js.map +1 -0
  89. package/lib/entries/cookie-only.d.ts +8 -0
  90. package/lib/entries/cookie-only.js +12 -0
  91. package/lib/entries/cookie-only.js.map +1 -0
  92. package/lib/entries/dom-only.d.ts +9 -0
  93. package/lib/entries/dom-only.js +29 -0
  94. package/lib/entries/dom-only.js.map +1 -0
  95. package/lib/entries/dom-with-overrides-full.d.ts +15 -0
  96. package/lib/entries/dom-with-overrides-full.js +39 -0
  97. package/lib/entries/dom-with-overrides-full.js.map +1 -0
  98. package/lib/entries/dom-with-overrides-lite.d.ts +11 -0
  99. package/lib/entries/dom-with-overrides-lite.js +32 -0
  100. package/lib/entries/dom-with-overrides-lite.js.map +1 -0
  101. package/lib/entries/overrides-full-only.d.ts +11 -0
  102. package/lib/entries/overrides-full-only.js +33 -0
  103. package/lib/entries/overrides-full-only.js.map +1 -0
  104. package/lib/entries/overrides-lite-only.d.ts +8 -0
  105. package/lib/entries/overrides-lite-only.js +28 -0
  106. package/lib/entries/overrides-lite-only.js.map +1 -0
  107. package/lib/entries/vitals-only.d.ts +8 -0
  108. package/lib/entries/vitals-only.js +12 -0
  109. package/lib/entries/vitals-only.js.map +1 -0
  110. package/lib/index.d.ts +13 -0
  111. package/lib/index.js +34 -0
  112. package/lib/index.js.map +1 -0
  113. package/lib/overrides/BrowserCookieAdapter.d.ts +6 -0
  114. package/lib/overrides/BrowserCookieAdapter.js +79 -0
  115. package/lib/overrides/BrowserCookieAdapter.js.map +1 -0
  116. package/lib/overrides/OverridesPlugin.d.ts +33 -0
  117. package/lib/overrides/OverridesPlugin.js +906 -0
  118. package/lib/overrides/OverridesPlugin.js.map +1 -0
  119. package/lib/overrides/OverridesPluginFull.d.ts +9 -0
  120. package/lib/overrides/OverridesPluginFull.js +35 -0
  121. package/lib/overrides/OverridesPluginFull.js.map +1 -0
  122. package/lib/overrides/OverridesPluginLite.d.ts +27 -0
  123. package/lib/overrides/OverridesPluginLite.js +355 -0
  124. package/lib/overrides/OverridesPluginLite.js.map +1 -0
  125. package/lib/overrides/index.d.ts +3 -0
  126. package/lib/overrides/index.js +23 -0
  127. package/lib/overrides/index.js.map +1 -0
  128. package/lib/overrides/types.d.ts +43 -0
  129. package/lib/overrides/types.js +3 -0
  130. package/lib/overrides/types.js.map +1 -0
  131. package/lib/parsers/VariantExtractor.d.ts +49 -0
  132. package/lib/parsers/VariantExtractor.js +573 -0
  133. package/lib/parsers/VariantExtractor.js.map +1 -0
  134. package/lib/types/index.d.ts +177 -0
  135. package/lib/types/index.js +3 -0
  136. package/lib/types/index.js.map +1 -0
  137. package/lib/utils/URLMatcher.d.ts +20 -0
  138. package/lib/utils/URLMatcher.js +117 -0
  139. package/lib/utils/URLMatcher.js.map +1 -0
  140. package/lib/utils/debug.d.ts +41 -0
  141. package/lib/utils/debug.js +195 -0
  142. package/lib/utils/debug.js.map +1 -0
  143. package/lib/vitals/WebVitalsPlugin.d.ts +37 -0
  144. package/lib/vitals/WebVitalsPlugin.js +382 -0
  145. package/lib/vitals/WebVitalsPlugin.js.map +1 -0
  146. package/lib/vitals/index.d.ts +2 -0
  147. package/lib/vitals/index.js +6 -0
  148. package/lib/vitals/index.js.map +1 -0
  149. package/package.json +94 -0
  150. package/types/cookies/CookiePlugin.d.ts +55 -0
  151. package/types/cookies/index.d.ts +2 -0
  152. package/types/core/DOMChangesPluginLite.d.ts +89 -0
  153. package/types/core/DOMManipulatorLite.d.ts +21 -0
  154. package/types/core/ExposureTracker.d.ts +81 -0
  155. package/types/core/HTMLInjector.d.ts +19 -0
  156. package/types/core/PendingChangeManager.d.ts +31 -0
  157. package/types/core/StyleSheetManager.d.ts +16 -0
  158. package/types/entries/cookie-only.d.ts +8 -0
  159. package/types/entries/dom-only.d.ts +9 -0
  160. package/types/entries/dom-with-overrides-full.d.ts +15 -0
  161. package/types/entries/dom-with-overrides-lite.d.ts +11 -0
  162. package/types/entries/overrides-full-only.d.ts +11 -0
  163. package/types/entries/overrides-lite-only.d.ts +8 -0
  164. package/types/entries/vitals-only.d.ts +8 -0
  165. package/types/index.d.ts +13 -0
  166. package/types/overrides/BrowserCookieAdapter.d.ts +6 -0
  167. package/types/overrides/OverridesPlugin.d.ts +33 -0
  168. package/types/overrides/OverridesPluginFull.d.ts +9 -0
  169. package/types/overrides/OverridesPluginLite.d.ts +27 -0
  170. package/types/overrides/index.d.ts +3 -0
  171. package/types/overrides/types.d.ts +43 -0
  172. package/types/parsers/VariantExtractor.d.ts +49 -0
  173. package/types/types/index.d.ts +177 -0
  174. package/types/utils/URLMatcher.d.ts +20 -0
  175. package/types/utils/debug.d.ts +41 -0
  176. package/types/vitals/WebVitalsPlugin.d.ts +37 -0
  177. package/types/vitals/index.d.ts +2 -0
@@ -0,0 +1,1124 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
23
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ var __values = (this && this.__values) || function(o) {
49
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
50
+ if (m) return m.call(o);
51
+ if (o && typeof o.length === "number") return {
52
+ next: function () {
53
+ if (o && i >= o.length) o = void 0;
54
+ return { value: o && o[i++], done: !o };
55
+ }
56
+ };
57
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
58
+ };
59
+ var __read = (this && this.__read) || function (o, n) {
60
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
61
+ if (!m) return o;
62
+ var i = m.call(o), r, ar = [], e;
63
+ try {
64
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
65
+ }
66
+ catch (error) { e = { error: error }; }
67
+ finally {
68
+ try {
69
+ if (r && !r.done && (m = i["return"])) m.call(i);
70
+ }
71
+ finally { if (e) throw e.error; }
72
+ }
73
+ return ar;
74
+ };
75
+ import { DOMManipulatorLite } from './DOMManipulatorLite';
76
+ import { VariantExtractor } from '../parsers/VariantExtractor';
77
+ import { StyleSheetManager } from './StyleSheetManager';
78
+ import { ExposureTracker } from './ExposureTracker';
79
+ import { HTMLInjector } from './HTMLInjector';
80
+ import { logDebug, logExperimentSummary, logPerformance, DEBUG } from '../utils/debug';
81
+ import { URLMatcher } from '../utils/URLMatcher';
82
+ var DOMChangesPluginLite = /** @class */ (function () {
83
+ function DOMChangesPluginLite(config) {
84
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
85
+ this.mutationObserver = null;
86
+ this.exposedExperiments = new Set();
87
+ this.eventListeners = new Map();
88
+ this.styleManagers = new Map();
89
+ this.initialized = false;
90
+ this.watchedElements = new WeakMap();
91
+ this.persistenceObserver = null;
92
+ this.reapplyingElements = new Set();
93
+ this.reapplyLogThrottle = new Map();
94
+ this.appliedStyleChanges = new Map();
95
+ this.antiFlickerTimeout = null;
96
+ this.antiFlickerStyleId = 'absmartly-antiflicker';
97
+ this.config = {
98
+ context: config.context,
99
+ autoApply: (_a = config.autoApply) !== null && _a !== void 0 ? _a : true,
100
+ spa: (_b = config.spa) !== null && _b !== void 0 ? _b : true,
101
+ visibilityTracking: (_c = config.visibilityTracking) !== null && _c !== void 0 ? _c : true,
102
+ extensionBridge: false, // Always false for lite version
103
+ dataSource: (_d = config.dataSource) !== null && _d !== void 0 ? _d : 'variable',
104
+ dataFieldName: (_e = config.dataFieldName) !== null && _e !== void 0 ? _e : '__dom_changes',
105
+ debug: (_f = config.debug) !== null && _f !== void 0 ? _f : false,
106
+ hideUntilReady: (_g = config.hideUntilReady) !== null && _g !== void 0 ? _g : false,
107
+ hideTimeout: (_h = config.hideTimeout) !== null && _h !== void 0 ? _h : 3000,
108
+ hideSelector: (_j = config.hideSelector) !== null && _j !== void 0 ? _j : '[data-absmartly-hide]',
109
+ hideTransition: (_k = config.hideTransition) !== null && _k !== void 0 ? _k : false,
110
+ };
111
+ if (!this.config.context) {
112
+ throw new Error('[ABsmartly] Context is required');
113
+ }
114
+ // Apply anti-flicker hiding immediately if enabled
115
+ if (this.config.hideUntilReady) {
116
+ this.hideContent();
117
+ }
118
+ this.domManipulator = new DOMManipulatorLite(this.config.debug, this);
119
+ this.variantExtractor = new VariantExtractor(this.config.context, this.config.dataSource, this.config.dataFieldName, this.config.debug);
120
+ this.exposureTracker = new ExposureTracker(this.config.context, this.config.debug);
121
+ this.htmlInjector = new HTMLInjector(this.config.debug);
122
+ }
123
+ DOMChangesPluginLite.prototype.ready = function () {
124
+ return __awaiter(this, void 0, void 0, function () {
125
+ var startTime, duration, error_1;
126
+ return __generator(this, function (_a) {
127
+ switch (_a.label) {
128
+ case 0:
129
+ if (this.initialized) {
130
+ logDebug('Plugin already initialized');
131
+ return [2 /*return*/];
132
+ }
133
+ startTime = performance.now();
134
+ logDebug('Initializing ABsmartly DOM Changes Plugin Lite', {
135
+ version: DOMChangesPluginLite.VERSION,
136
+ config: {
137
+ autoApply: this.config.autoApply,
138
+ spa: this.config.spa,
139
+ visibilityTracking: this.config.visibilityTracking,
140
+ dataSource: this.config.dataSource,
141
+ DEBUG: DEBUG,
142
+ },
143
+ });
144
+ _a.label = 1;
145
+ case 1:
146
+ _a.trys.push([1, 4, , 5]);
147
+ if (this.config.spa) {
148
+ this.setupMutationObserver();
149
+ this.setupURLChangeListener();
150
+ }
151
+ if (!this.config.autoApply) return [3 /*break*/, 3];
152
+ // Apply HTML injections and DOM changes in parallel to minimize flickering
153
+ return [4 /*yield*/, this.applyInjectionsAndChanges()];
154
+ case 2:
155
+ // Apply HTML injections and DOM changes in parallel to minimize flickering
156
+ _a.sent();
157
+ _a.label = 3;
158
+ case 3:
159
+ this.initialized = true;
160
+ this.registerWithContext();
161
+ this.emit('initialized');
162
+ duration = performance.now() - startTime;
163
+ logPerformance('Plugin initialization', duration);
164
+ logDebug("[ABsmartly] DOM plugin lite loaded successfully (v".concat(DOMChangesPluginLite.VERSION, ")"));
165
+ if (this.config.debug) {
166
+ logDebug('[ABsmartly] DOM Changes Plugin Lite initialized with debug mode');
167
+ }
168
+ return [3 /*break*/, 5];
169
+ case 4:
170
+ error_1 = _a.sent();
171
+ logDebug('[ABsmartly] Failed to initialize plugin:', error_1);
172
+ throw error_1;
173
+ case 5: return [2 /*return*/];
174
+ }
175
+ });
176
+ });
177
+ };
178
+ DOMChangesPluginLite.prototype.initialize = function () {
179
+ return __awaiter(this, void 0, void 0, function () {
180
+ return __generator(this, function (_a) {
181
+ return [2 /*return*/, this.ready()];
182
+ });
183
+ });
184
+ };
185
+ DOMChangesPluginLite.prototype.setupMutationObserver = function () {
186
+ var observer = new MutationObserver(function () {
187
+ // Check for pending changes that might now be applicable
188
+ // Note: We don't have a centralized pending changes store in lite version,
189
+ // the PendingChangeManager handles this internally
190
+ });
191
+ observer.observe(document.body, {
192
+ childList: true,
193
+ subtree: true,
194
+ });
195
+ this.mutationObserver = observer;
196
+ };
197
+ /**
198
+ * Set up URL change listener for SPA mode
199
+ * Re-evaluates URL filters when URL changes and applies/removes changes accordingly
200
+ */
201
+ DOMChangesPluginLite.prototype.setupURLChangeListener = function () {
202
+ var _this = this;
203
+ var handleURLChange = function () { return __awaiter(_this, void 0, void 0, function () {
204
+ var newURL;
205
+ return __generator(this, function (_a) {
206
+ switch (_a.label) {
207
+ case 0:
208
+ newURL = window.location.href;
209
+ logDebug('[ABsmartly] URL changed, re-evaluating experiments:', newURL);
210
+ // Remove all current changes
211
+ return [4 /*yield*/, this.removeAllChanges()];
212
+ case 1:
213
+ // Remove all current changes
214
+ _a.sent();
215
+ // Re-apply changes and injections with new URL (in parallel)
216
+ return [4 /*yield*/, this.applyInjectionsAndChanges()];
217
+ case 2:
218
+ // Re-apply changes and injections with new URL (in parallel)
219
+ _a.sent();
220
+ return [2 /*return*/];
221
+ }
222
+ });
223
+ }); };
224
+ // Listen to popstate (back/forward navigation)
225
+ window.addEventListener('popstate', handleURLChange);
226
+ // Intercept pushState and replaceState
227
+ var originalPushState = history.pushState;
228
+ var originalReplaceState = history.replaceState;
229
+ history.pushState = function () {
230
+ var args = [];
231
+ for (var _i = 0; _i < arguments.length; _i++) {
232
+ args[_i] = arguments[_i];
233
+ }
234
+ originalPushState.apply(history, args);
235
+ handleURLChange();
236
+ };
237
+ history.replaceState = function () {
238
+ var args = [];
239
+ for (var _i = 0; _i < arguments.length; _i++) {
240
+ args[_i] = arguments[_i];
241
+ }
242
+ originalReplaceState.apply(history, args);
243
+ handleURLChange();
244
+ };
245
+ if (this.config.debug) {
246
+ logDebug('[ABsmartly] URL change listener set up for SPA mode');
247
+ }
248
+ };
249
+ /**
250
+ * Remove all currently applied changes
251
+ * Used when URL changes in SPA mode
252
+ */
253
+ DOMChangesPluginLite.prototype.removeAllChanges = function () {
254
+ return __awaiter(this, void 0, void 0, function () {
255
+ return __generator(this, function (_a) {
256
+ // Clear exposed experiments - ExposureTracker will re-register on next applyChanges
257
+ this.exposedExperiments.clear();
258
+ // Clear style managers - StyleSheetManager doesn't have a remove method
259
+ // The stylesheets will be reused or cleared on next applyChanges
260
+ this.styleManagers.clear();
261
+ // Clear applied style changes
262
+ this.appliedStyleChanges.clear();
263
+ // Clear HTML injections
264
+ this.htmlInjector.destroy();
265
+ // Clear variant extractor cache to force re-extraction
266
+ this.variantExtractor.clearCache();
267
+ if (this.config.debug) {
268
+ logDebug('[ABsmartly] All change tracking cleared for URL change');
269
+ }
270
+ return [2 /*return*/];
271
+ });
272
+ });
273
+ };
274
+ /**
275
+ * Apply HTML injections and DOM changes in parallel to minimize flickering
276
+ */
277
+ DOMChangesPluginLite.prototype.applyInjectionsAndChanges = function () {
278
+ return __awaiter(this, void 0, void 0, function () {
279
+ var startTime, currentURL, error_2, allInjectHTML, duration;
280
+ return __generator(this, function (_a) {
281
+ switch (_a.label) {
282
+ case 0:
283
+ startTime = performance.now();
284
+ currentURL = window.location.href;
285
+ _a.label = 1;
286
+ case 1:
287
+ _a.trys.push([1, 3, , 4]);
288
+ return [4 /*yield*/, this.config.context.ready()];
289
+ case 2:
290
+ _a.sent();
291
+ return [3 /*break*/, 4];
292
+ case 3:
293
+ error_2 = _a.sent();
294
+ logDebug('[ABsmartly] Failed to wait for context ready:', error_2);
295
+ return [2 /*return*/];
296
+ case 4:
297
+ allInjectHTML = this.variantExtractor.extractAllInjectHTML();
298
+ // Apply injections and DOM changes in parallel for minimal flickering
299
+ return [4 /*yield*/, Promise.all([this.applyHTMLInjections(allInjectHTML, currentURL), this.applyChanges()])];
300
+ case 5:
301
+ // Apply injections and DOM changes in parallel for minimal flickering
302
+ _a.sent();
303
+ duration = performance.now() - startTime;
304
+ logPerformance('Apply injections and changes (parallel)', duration);
305
+ if (this.config.debug) {
306
+ logDebug('[ABsmartly] HTML injections and DOM changes applied in parallel', {
307
+ duration: "".concat(duration.toFixed(2), "ms"),
308
+ currentURL: currentURL,
309
+ });
310
+ }
311
+ return [2 /*return*/];
312
+ }
313
+ });
314
+ });
315
+ };
316
+ /**
317
+ * Apply HTML injections from all variants
318
+ */
319
+ DOMChangesPluginLite.prototype.applyHTMLInjections = function (allInjectHTML_1) {
320
+ return __awaiter(this, arguments, void 0, function (allInjectHTML, currentUrl) {
321
+ var injectionsByLocation;
322
+ if (currentUrl === void 0) { currentUrl = window.location.href; }
323
+ return __generator(this, function (_a) {
324
+ if (allInjectHTML.size === 0) {
325
+ if (this.config.debug) {
326
+ logDebug('[ABsmartly] No HTML injections found');
327
+ }
328
+ return [2 /*return*/];
329
+ }
330
+ injectionsByLocation = this.htmlInjector.collectInjections(allInjectHTML, currentUrl);
331
+ this.htmlInjector.inject(injectionsByLocation);
332
+ if (this.config.debug) {
333
+ logDebug('[ABsmartly] HTML injections complete', {
334
+ experimentsWithInjections: allInjectHTML.size,
335
+ totalLocations: injectionsByLocation.size,
336
+ currentUrl: currentUrl,
337
+ });
338
+ }
339
+ return [2 /*return*/];
340
+ });
341
+ });
342
+ };
343
+ DOMChangesPluginLite.prototype.applyChanges = function (experimentName) {
344
+ return __awaiter(this, void 0, void 0, function () {
345
+ var startTime, currentURL, error_3, allExperiments, totalApplied, experimentStats, allExperiments_1, allExperiments_1_1, _a, expName, experimentData, currentVariant, variantData, urlFilter, globalDefaults, anyVariantMatchesURL, shouldApplyVisualChanges, changes, allVariantChanges, hasAnyChangesInAnyVariant, stats, hasImmediateTrigger, hasViewportTrigger, changes_1, changes_1_1, change, success, elements, changes_2, changes_2_1, change, hasAnyViewportTriggerInAnyVariant, hasAnyImmediateTriggerInAnyVariant, allVariantChanges_1, allVariantChanges_1_1, variantChanges, variantChanges_1, variantChanges_1_1, change, duration;
346
+ var e_1, _b, e_2, _c, e_3, _d, e_4, _e, e_5, _f;
347
+ return __generator(this, function (_g) {
348
+ switch (_g.label) {
349
+ case 0:
350
+ startTime = performance.now();
351
+ currentURL = window.location.href;
352
+ logDebug('Starting to apply changes', {
353
+ specificExperiment: experimentName || 'all',
354
+ url: currentURL,
355
+ action: 'apply_start',
356
+ });
357
+ if (this.config.debug) {
358
+ logDebug('[ABsmartly] === DOM Changes Application Starting ===');
359
+ logDebug('[ABsmartly] Target:', experimentName || 'all experiments');
360
+ logDebug('[ABsmartly] Current URL:', currentURL);
361
+ logDebug('[ABsmartly] Context ready:', true);
362
+ }
363
+ _g.label = 1;
364
+ case 1:
365
+ _g.trys.push([1, 3, , 4]);
366
+ return [4 /*yield*/, this.config.context.ready()];
367
+ case 2:
368
+ _g.sent();
369
+ return [3 /*break*/, 4];
370
+ case 3:
371
+ error_3 = _g.sent();
372
+ logDebug('[ABsmartly] Failed to wait for context ready:', error_3);
373
+ return [2 /*return*/];
374
+ case 4:
375
+ this.variantExtractor.clearCache();
376
+ allExperiments = this.getAllExperimentsData();
377
+ totalApplied = 0;
378
+ experimentStats = new Map();
379
+ logDebug('[ABsmartly] Experiments to process:', Array.from(allExperiments.keys()));
380
+ logDebug('[ABsmartly] Total experiments with changes:', allExperiments.size);
381
+ try {
382
+ for (allExperiments_1 = __values(allExperiments), allExperiments_1_1 = allExperiments_1.next(); !allExperiments_1_1.done; allExperiments_1_1 = allExperiments_1.next()) {
383
+ _a = __read(allExperiments_1_1.value, 2), expName = _a[0], experimentData = _a[1];
384
+ // Skip if filtering by specific experiment
385
+ if (experimentName && expName !== experimentName) {
386
+ continue;
387
+ }
388
+ currentVariant = this.config.context.peek(expName);
389
+ variantData = experimentData.variantData, urlFilter = experimentData.urlFilter, globalDefaults = experimentData.globalDefaults;
390
+ anyVariantMatchesURL = this.variantExtractor.anyVariantMatchesURL(expName, currentURL);
391
+ if (!anyVariantMatchesURL) {
392
+ logDebug("[ABsmartly] Skipping experiment '".concat(expName, "' - no variant matches URL: ").concat(currentURL));
393
+ continue;
394
+ }
395
+ shouldApplyVisualChanges = this.shouldApplyVisualChanges(variantData, urlFilter, currentURL);
396
+ changes = this.extractChangesFromData(variantData, globalDefaults);
397
+ allVariantChanges = this.extractAllVariantChanges(expName);
398
+ hasAnyChangesInAnyVariant = allVariantChanges.some(function (variantChanges) { return variantChanges && variantChanges.length > 0; });
399
+ if (!hasAnyChangesInAnyVariant) {
400
+ // If NO variant has ANY changes, skip the entire experiment
401
+ if (this.config.debug) {
402
+ logDebug("[ABsmartly] Skipping experiment '".concat(expName, "' - no variants have changes"));
403
+ }
404
+ continue;
405
+ }
406
+ stats = { total: (changes === null || changes === void 0 ? void 0 : changes.length) || 0, success: 0, pending: 0 };
407
+ if (this.config.debug) {
408
+ logDebug("[ABsmartly] Processing experiment '".concat(expName, "' (variant ").concat(currentVariant, "):"), {
409
+ urlMatches: shouldApplyVisualChanges,
410
+ changeCount: (changes === null || changes === void 0 ? void 0 : changes.length) || 0,
411
+ userVariantHasChanges: ((changes === null || changes === void 0 ? void 0 : changes.length) || 0) > 0,
412
+ changes: (changes === null || changes === void 0 ? void 0 : changes.map(function (c) { return ({
413
+ type: c.type,
414
+ selector: c.selector,
415
+ trigger: c.trigger_on_view ? 'viewport' : 'immediate',
416
+ }); })) || [],
417
+ });
418
+ }
419
+ hasImmediateTrigger = false;
420
+ hasViewportTrigger = false;
421
+ // Apply visual changes only if URL matches for user's variant AND user has changes
422
+ if (shouldApplyVisualChanges && changes && changes.length > 0) {
423
+ try {
424
+ 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()) {
425
+ change = changes_1_1.value;
426
+ success = this.domManipulator.applyChange(change, expName);
427
+ if (success) {
428
+ totalApplied++;
429
+ stats.success++;
430
+ if (change.trigger_on_view) {
431
+ hasViewportTrigger = true;
432
+ }
433
+ else {
434
+ hasImmediateTrigger = true;
435
+ }
436
+ }
437
+ else if (change.type !== 'create' && change.type !== 'styleRules') {
438
+ // Track pending changes for stats
439
+ try {
440
+ elements = document.querySelectorAll(change.selector);
441
+ if (elements.length === 0 && (this.config.spa || change.waitForElement)) {
442
+ stats.pending++;
443
+ if (change.trigger_on_view) {
444
+ hasViewportTrigger = true;
445
+ }
446
+ }
447
+ }
448
+ catch (error) {
449
+ if (this.config.debug) {
450
+ logDebug("[ABsmartly] Invalid selector: ".concat(change.selector), error);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
456
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
457
+ finally {
458
+ try {
459
+ if (changes_1_1 && !changes_1_1.done && (_c = changes_1.return)) _c.call(changes_1);
460
+ }
461
+ finally { if (e_2) throw e_2.error; }
462
+ }
463
+ }
464
+ else if (changes && changes.length > 0) {
465
+ try {
466
+ // URL doesn't match for user's variant OR user has no changes
467
+ // Check if user's changes have viewport trigger (if they exist)
468
+ // We still need to set up tracking for SRM prevention
469
+ for (changes_2 = (e_3 = void 0, __values(changes)), changes_2_1 = changes_2.next(); !changes_2_1.done; changes_2_1 = changes_2.next()) {
470
+ change = changes_2_1.value;
471
+ if (change.trigger_on_view) {
472
+ hasViewportTrigger = true;
473
+ }
474
+ else {
475
+ hasImmediateTrigger = true;
476
+ }
477
+ }
478
+ }
479
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
480
+ finally {
481
+ try {
482
+ if (changes_2_1 && !changes_2_1.done && (_d = changes_2.return)) _d.call(changes_2);
483
+ }
484
+ finally { if (e_3) throw e_3.error; }
485
+ }
486
+ logDebug("[ABsmartly] Experiment '".concat(expName, "' variant ").concat(currentVariant, " doesn't match URL filter or has no changes, but setting up tracking for SRM prevention"));
487
+ }
488
+ hasAnyViewportTriggerInAnyVariant = hasViewportTrigger;
489
+ hasAnyImmediateTriggerInAnyVariant = hasImmediateTrigger;
490
+ if (!hasViewportTrigger || !hasImmediateTrigger) {
491
+ try {
492
+ for (allVariantChanges_1 = (e_4 = void 0, __values(allVariantChanges)), allVariantChanges_1_1 = allVariantChanges_1.next(); !allVariantChanges_1_1.done; allVariantChanges_1_1 = allVariantChanges_1.next()) {
493
+ variantChanges = allVariantChanges_1_1.value;
494
+ if (variantChanges && variantChanges.length > 0) {
495
+ try {
496
+ for (variantChanges_1 = (e_5 = void 0, __values(variantChanges)), variantChanges_1_1 = variantChanges_1.next(); !variantChanges_1_1.done; variantChanges_1_1 = variantChanges_1.next()) {
497
+ change = variantChanges_1_1.value;
498
+ if (change.trigger_on_view) {
499
+ hasAnyViewportTriggerInAnyVariant = true;
500
+ }
501
+ else {
502
+ hasAnyImmediateTriggerInAnyVariant = true;
503
+ }
504
+ // Early exit if we found both types
505
+ if (hasAnyViewportTriggerInAnyVariant && hasAnyImmediateTriggerInAnyVariant) {
506
+ break;
507
+ }
508
+ }
509
+ }
510
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
511
+ finally {
512
+ try {
513
+ if (variantChanges_1_1 && !variantChanges_1_1.done && (_f = variantChanges_1.return)) _f.call(variantChanges_1);
514
+ }
515
+ finally { if (e_5) throw e_5.error; }
516
+ }
517
+ }
518
+ if (hasAnyViewportTriggerInAnyVariant && hasAnyImmediateTriggerInAnyVariant) {
519
+ break;
520
+ }
521
+ }
522
+ }
523
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
524
+ finally {
525
+ try {
526
+ if (allVariantChanges_1_1 && !allVariantChanges_1_1.done && (_e = allVariantChanges_1.return)) _e.call(allVariantChanges_1);
527
+ }
528
+ finally { if (e_4) throw e_4.error; }
529
+ }
530
+ }
531
+ // CRITICAL: Always register experiment for tracking if ANY variant has ANY trigger type
532
+ // This prevents SRM even when user's variant doesn't match URL filter
533
+ if (hasAnyViewportTriggerInAnyVariant || hasAnyImmediateTriggerInAnyVariant) {
534
+ this.exposureTracker.registerExperiment(expName, currentVariant || 0, changes || [], allVariantChanges);
535
+ }
536
+ // Note: We do NOT call treatment() here anymore to avoid duplicate calls.
537
+ // The ExposureTracker.registerExperiment() will call triggerExposure() internally
538
+ // when hasImmediateTrigger is true (see ExposureTracker line 169-174).
539
+ // This prevents double treatment() calls that were causing test failures.
540
+ experimentStats.set(expName, stats);
541
+ logExperimentSummary(expName, stats.total, stats.success, stats.pending);
542
+ }
543
+ }
544
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
545
+ finally {
546
+ try {
547
+ if (allExperiments_1_1 && !allExperiments_1_1.done && (_b = allExperiments_1.return)) _b.call(allExperiments_1);
548
+ }
549
+ finally { if (e_1) throw e_1.error; }
550
+ }
551
+ duration = performance.now() - startTime;
552
+ logPerformance('Apply changes', duration, {
553
+ totalApplied: totalApplied,
554
+ experiments: experimentStats.size,
555
+ });
556
+ if (this.config.debug) {
557
+ logDebug('[ABsmartly] === DOM Changes Application Complete ===');
558
+ logDebug('[ABsmartly] Summary:', {
559
+ totalChangesApplied: totalApplied,
560
+ experimentsProcessed: experimentStats.size,
561
+ duration: "".concat(duration.toFixed(2), "ms"),
562
+ experiments: Array.from(experimentStats.entries()).map(function (_a) {
563
+ var _b = __read(_a, 2), name = _b[0], stats = _b[1];
564
+ return ({
565
+ name: name,
566
+ total: stats.total,
567
+ success: stats.success,
568
+ pending: stats.pending,
569
+ pendingReason: stats.pending > 0 ? 'Elements not found yet (SPA mode will retry)' : undefined,
570
+ });
571
+ }),
572
+ });
573
+ }
574
+ // Show hidden content after changes are applied
575
+ if (this.config.hideUntilReady) {
576
+ this.showContent();
577
+ }
578
+ this.emit('changes-applied', { count: totalApplied, experimentName: experimentName });
579
+ return [2 /*return*/];
580
+ }
581
+ });
582
+ });
583
+ };
584
+ /**
585
+ * Get all experiments with their variant data and metadata
586
+ */
587
+ DOMChangesPluginLite.prototype.getAllExperimentsData = function () {
588
+ var e_6, _a;
589
+ var experiments = new Map();
590
+ var allVariants = this.variantExtractor.extractAllChanges();
591
+ try {
592
+ for (var allVariants_1 = __values(allVariants), allVariants_1_1 = allVariants_1.next(); !allVariants_1_1.done; allVariants_1_1 = allVariants_1.next()) {
593
+ var _b = __read(allVariants_1_1.value, 1), expName = _b[0];
594
+ var currentVariant = this.config.context.peek(expName);
595
+ if (currentVariant === undefined || currentVariant === null) {
596
+ continue;
597
+ }
598
+ var variantsData = this.variantExtractor.getAllVariantsData(expName);
599
+ var variantData = variantsData.get(currentVariant);
600
+ if (!variantData) {
601
+ continue;
602
+ }
603
+ // Extract URL filter and global defaults if using wrapped format
604
+ var urlFilter = null;
605
+ var globalDefaults = {};
606
+ if (variantData &&
607
+ typeof variantData === 'object' &&
608
+ !Array.isArray(variantData) &&
609
+ 'changes' in variantData) {
610
+ var config = variantData;
611
+ urlFilter = config.urlFilter;
612
+ globalDefaults = {
613
+ waitForElement: config.waitForElement,
614
+ persistStyle: config.persistStyle,
615
+ important: config.important,
616
+ observerRoot: config.observerRoot,
617
+ };
618
+ }
619
+ experiments.set(expName, { variantData: variantData, urlFilter: urlFilter, globalDefaults: globalDefaults });
620
+ }
621
+ }
622
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
623
+ finally {
624
+ try {
625
+ if (allVariants_1_1 && !allVariants_1_1.done && (_a = allVariants_1.return)) _a.call(allVariants_1);
626
+ }
627
+ finally { if (e_6) throw e_6.error; }
628
+ }
629
+ return experiments;
630
+ };
631
+ /**
632
+ * Extract changes from DOMChangesData and apply global defaults
633
+ */
634
+ DOMChangesPluginLite.prototype.extractChangesFromData = function (data, globalDefaults) {
635
+ var changes = null;
636
+ // Extract changes array
637
+ if (Array.isArray(data)) {
638
+ changes = data;
639
+ }
640
+ else if (data && typeof data === 'object' && 'changes' in data) {
641
+ changes = data.changes;
642
+ }
643
+ if (!changes || changes.length === 0) {
644
+ return null;
645
+ }
646
+ // Apply global defaults to each change
647
+ return changes.map(function (change) {
648
+ var _a, _b, _c, _d;
649
+ return (__assign(__assign({}, change), { waitForElement: (_a = change.waitForElement) !== null && _a !== void 0 ? _a : globalDefaults.waitForElement, persistStyle: (_b = change.persistStyle) !== null && _b !== void 0 ? _b : globalDefaults.persistStyle, important: (_c = change.important) !== null && _c !== void 0 ? _c : globalDefaults.important, observerRoot: (_d = change.observerRoot) !== null && _d !== void 0 ? _d : globalDefaults.observerRoot }));
650
+ });
651
+ };
652
+ /**
653
+ * Extract all variant changes for cross-variant tracking
654
+ */
655
+ DOMChangesPluginLite.prototype.extractAllVariantChanges = function (experimentName) {
656
+ var allVariantChanges = this.variantExtractor.getAllVariantChanges(experimentName);
657
+ var variantsData = this.variantExtractor.getAllVariantsData(experimentName);
658
+ // Apply global defaults to each variant's changes
659
+ var processedVariants = [];
660
+ var _loop_1 = function (i) {
661
+ var variantChanges = allVariantChanges[i];
662
+ var variantData = variantsData.get(i);
663
+ if (!variantChanges || variantChanges.length === 0) {
664
+ processedVariants.push([]);
665
+ return "continue";
666
+ }
667
+ // Extract global defaults if using wrapped format
668
+ var globalDefaults = {};
669
+ if (variantData &&
670
+ typeof variantData === 'object' &&
671
+ !Array.isArray(variantData) &&
672
+ 'changes' in variantData) {
673
+ var config = variantData;
674
+ globalDefaults = {
675
+ waitForElement: config.waitForElement,
676
+ persistStyle: config.persistStyle,
677
+ important: config.important,
678
+ observerRoot: config.observerRoot,
679
+ };
680
+ }
681
+ // Apply global defaults
682
+ var processedChanges = variantChanges.map(function (change) {
683
+ var _a, _b, _c, _d;
684
+ return (__assign(__assign({}, change), { waitForElement: (_a = change.waitForElement) !== null && _a !== void 0 ? _a : globalDefaults.waitForElement, persistStyle: (_b = change.persistStyle) !== null && _b !== void 0 ? _b : globalDefaults.persistStyle, important: (_c = change.important) !== null && _c !== void 0 ? _c : globalDefaults.important, observerRoot: (_d = change.observerRoot) !== null && _d !== void 0 ? _d : globalDefaults.observerRoot }));
685
+ });
686
+ processedVariants.push(processedChanges);
687
+ };
688
+ for (var i = 0; i < allVariantChanges.length; i++) {
689
+ _loop_1(i);
690
+ }
691
+ return processedVariants;
692
+ };
693
+ /**
694
+ * Determine if visual changes should be applied based on URL filter
695
+ */
696
+ DOMChangesPluginLite.prototype.shouldApplyVisualChanges = function (variantData, urlFilter, url) {
697
+ // Legacy array format has no URL filter - always apply
698
+ if (Array.isArray(variantData)) {
699
+ return true;
700
+ }
701
+ // Wrapped format without URL filter - always apply
702
+ if (!urlFilter) {
703
+ return true;
704
+ }
705
+ // Check URL filter
706
+ return URLMatcher.matches(urlFilter, url);
707
+ };
708
+ DOMChangesPluginLite.prototype.hasChanges = function (experimentName) {
709
+ return this.domManipulator.hasChanges(experimentName);
710
+ };
711
+ DOMChangesPluginLite.prototype.applyChange = function (change, experimentName) {
712
+ logDebug('Applying single change via public API', {
713
+ experimentName: experimentName,
714
+ selector: change.selector,
715
+ changeType: change.type,
716
+ });
717
+ try {
718
+ var success = this.domManipulator.applyChange(change, experimentName);
719
+ if (success) {
720
+ this.emit('change_applied', {
721
+ experimentName: experimentName,
722
+ change: change,
723
+ });
724
+ }
725
+ return success;
726
+ }
727
+ catch (error) {
728
+ if (this.config.debug) {
729
+ logDebug('[ABsmartly] Error applying change:', error);
730
+ }
731
+ return false;
732
+ }
733
+ };
734
+ DOMChangesPluginLite.prototype.on = function (event, callback) {
735
+ var listeners = this.eventListeners.get(event) || [];
736
+ listeners.push(callback);
737
+ this.eventListeners.set(event, listeners);
738
+ };
739
+ DOMChangesPluginLite.prototype.off = function (event, callback) {
740
+ if (!callback) {
741
+ this.eventListeners.delete(event);
742
+ }
743
+ else {
744
+ var listeners = this.eventListeners.get(event) || [];
745
+ var index = listeners.indexOf(callback);
746
+ if (index !== -1) {
747
+ listeners.splice(index, 1);
748
+ }
749
+ }
750
+ };
751
+ DOMChangesPluginLite.prototype.emit = function (event, data) {
752
+ var e_7, _a;
753
+ var listeners = this.eventListeners.get(event) || [];
754
+ try {
755
+ for (var listeners_1 = __values(listeners), listeners_1_1 = listeners_1.next(); !listeners_1_1.done; listeners_1_1 = listeners_1.next()) {
756
+ var callback = listeners_1_1.value;
757
+ try {
758
+ callback(data);
759
+ }
760
+ catch (error) {
761
+ logDebug("[ABsmartly] Error in event listener for ".concat(event, ":"), error);
762
+ }
763
+ }
764
+ }
765
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
766
+ finally {
767
+ try {
768
+ if (listeners_1_1 && !listeners_1_1.done && (_a = listeners_1.return)) _a.call(listeners_1);
769
+ }
770
+ finally { if (e_7) throw e_7.error; }
771
+ }
772
+ };
773
+ DOMChangesPluginLite.prototype.getStyleManager = function (experimentName) {
774
+ var id = "absmartly-styles-".concat(experimentName);
775
+ var manager = this.styleManagers.get(experimentName);
776
+ if (!manager) {
777
+ manager = new StyleSheetManager(id, this.config.debug);
778
+ this.styleManagers.set(experimentName, manager);
779
+ }
780
+ return manager;
781
+ };
782
+ DOMChangesPluginLite.prototype.buildCssRule = function (selector, properties, important) {
783
+ if (important === void 0) { important = true; }
784
+ var declarations = Object.entries(properties)
785
+ .map(function (_a) {
786
+ var _b = __read(_a, 2), prop = _b[0], value = _b[1];
787
+ var cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
788
+ var bang = important ? ' !important' : '';
789
+ return " ".concat(cssProp, ": ").concat(value).concat(bang, ";");
790
+ })
791
+ .join('\n');
792
+ return "".concat(selector, " {\n").concat(declarations, "\n}");
793
+ };
794
+ DOMChangesPluginLite.prototype.buildStateRules = function (selector, states, important) {
795
+ if (important === void 0) { important = true; }
796
+ var rules = [];
797
+ if (states.normal) {
798
+ rules.push(this.buildCssRule(selector, states.normal, important));
799
+ }
800
+ if (states.hover) {
801
+ rules.push(this.buildCssRule("".concat(selector, ":hover"), states.hover, important));
802
+ }
803
+ if (states.active) {
804
+ rules.push(this.buildCssRule("".concat(selector, ":active"), states.active, important));
805
+ }
806
+ if (states.focus) {
807
+ rules.push(this.buildCssRule("".concat(selector, ":focus"), states.focus, important));
808
+ }
809
+ return rules.join('\n\n');
810
+ };
811
+ DOMChangesPluginLite.prototype.refreshExperiments = function () {
812
+ if (this.config.debug) {
813
+ logDebug('[ABsmartly] Refreshing experiments and clearing cache');
814
+ }
815
+ this.variantExtractor.clearCache();
816
+ if (this.config.autoApply) {
817
+ this.applyInjectionsAndChanges();
818
+ }
819
+ };
820
+ DOMChangesPluginLite.prototype.destroy = function () {
821
+ this.domManipulator.destroy();
822
+ this.exposureTracker.destroy();
823
+ this.htmlInjector.destroy();
824
+ this.styleManagers.forEach(function (manager) { return manager.destroy(); });
825
+ this.styleManagers.clear();
826
+ if (this.mutationObserver) {
827
+ this.mutationObserver.disconnect();
828
+ this.mutationObserver = null;
829
+ }
830
+ if (this.persistenceObserver) {
831
+ this.persistenceObserver.disconnect();
832
+ this.persistenceObserver = null;
833
+ }
834
+ // Clean up anti-flicker timeout and style
835
+ if (this.antiFlickerTimeout !== null) {
836
+ clearTimeout(this.antiFlickerTimeout);
837
+ this.antiFlickerTimeout = null;
838
+ }
839
+ var antiFlickerStyle = document.getElementById(this.antiFlickerStyleId);
840
+ if (antiFlickerStyle) {
841
+ antiFlickerStyle.remove();
842
+ }
843
+ this.eventListeners.clear();
844
+ this.exposedExperiments.clear();
845
+ this.watchedElements = new WeakMap();
846
+ this.appliedStyleChanges.clear();
847
+ this.reapplyingElements.clear();
848
+ this.reapplyLogThrottle.clear();
849
+ this.unregisterFromContext();
850
+ this.initialized = false;
851
+ if (this.config.debug) {
852
+ logDebug('[ABsmartly] DOM Changes Plugin Lite destroyed');
853
+ }
854
+ };
855
+ DOMChangesPluginLite.prototype.registerWithContext = function () {
856
+ if (this.config.context) {
857
+ if (!this.config.context.__plugins) {
858
+ this.config.context.__plugins = {};
859
+ }
860
+ this.config.context.__plugins.domPlugin = {
861
+ name: 'DOMChangesPluginLite',
862
+ version: DOMChangesPluginLite.VERSION,
863
+ initialized: true,
864
+ capabilities: ['spa', 'visibility'],
865
+ instance: this,
866
+ timestamp: Date.now(),
867
+ };
868
+ this.config.context.__domPlugin = this.config.context.__plugins.domPlugin;
869
+ if (this.config.debug) {
870
+ logDebug('[ABsmartly] DOMChangesPluginLite registered with context at __plugins.domPlugin');
871
+ }
872
+ }
873
+ };
874
+ DOMChangesPluginLite.prototype.unregisterFromContext = function () {
875
+ var _a;
876
+ if (this.config.context) {
877
+ if ((_a = this.config.context.__plugins) === null || _a === void 0 ? void 0 : _a.domPlugin) {
878
+ delete this.config.context.__plugins.domPlugin;
879
+ }
880
+ if (this.config.context.__domPlugin) {
881
+ delete this.config.context.__domPlugin;
882
+ }
883
+ if (this.config.debug) {
884
+ logDebug('[ABsmartly] DOMChangesPluginLite unregistered from context');
885
+ }
886
+ }
887
+ };
888
+ /**
889
+ * Hide content to prevent flicker before experiments are applied
890
+ */
891
+ DOMChangesPluginLite.prototype.hideContent = function () {
892
+ var _this = this;
893
+ var mode = this.config.hideUntilReady;
894
+ if (!mode)
895
+ return;
896
+ // Check if style already exists (prevent duplicate injection)
897
+ if (document.getElementById(this.antiFlickerStyleId)) {
898
+ return;
899
+ }
900
+ var style = document.createElement('style');
901
+ style.id = this.antiFlickerStyleId;
902
+ var hasTransition = this.config.hideTransition !== false;
903
+ var selector = mode === 'body' ? 'body' : this.config.hideSelector || '[data-absmartly-hide]';
904
+ if (hasTransition) {
905
+ // Use both visibility:hidden and opacity:0 for smooth transition
906
+ style.textContent = "\n ".concat(selector, " {\n visibility: hidden !important;\n opacity: 0 !important;\n }\n ");
907
+ }
908
+ else {
909
+ // Use only visibility:hidden for instant reveal (no layout shift)
910
+ style.textContent = "\n ".concat(selector, " {\n visibility: hidden !important;\n }\n ");
911
+ }
912
+ document.head.appendChild(style);
913
+ // Set timeout to show content even if experiments fail to load
914
+ this.antiFlickerTimeout = window.setTimeout(function () {
915
+ if (_this.config.debug) {
916
+ logDebug("[ABsmartly] Anti-flicker timeout reached (".concat(_this.config.hideTimeout, "ms), showing content"));
917
+ }
918
+ _this.showContent();
919
+ }, this.config.hideTimeout);
920
+ if (this.config.debug) {
921
+ logDebug("[ABsmartly] Anti-flicker enabled (mode: ".concat(mode, ", transition: ").concat(hasTransition ? this.config.hideTransition : 'none', ", timeout: ").concat(this.config.hideTimeout, "ms)"));
922
+ }
923
+ };
924
+ /**
925
+ * Show hidden content after experiments are applied
926
+ */
927
+ DOMChangesPluginLite.prototype.showContent = function () {
928
+ var _this = this;
929
+ // Clear timeout if still pending
930
+ if (this.antiFlickerTimeout !== null) {
931
+ clearTimeout(this.antiFlickerTimeout);
932
+ this.antiFlickerTimeout = null;
933
+ }
934
+ var style = document.getElementById(this.antiFlickerStyleId);
935
+ if (!style)
936
+ return;
937
+ var hasTransition = this.config.hideTransition !== false;
938
+ if (hasTransition) {
939
+ // Smooth fade-in: remove visibility, add transition, then animate opacity
940
+ var mode = this.config.hideUntilReady;
941
+ var selector = mode === 'body' ? 'body' : this.config.hideSelector || '[data-absmartly-hide]';
942
+ // Step 1: Remove visibility:hidden, keep opacity:0, add transition
943
+ style.textContent = "\n ".concat(selector, " {\n opacity: 0 !important;\n transition: opacity ").concat(this.config.hideTransition, " !important;\n }\n ");
944
+ // Step 2: Force reflow to ensure transition applies
945
+ style.offsetHeight;
946
+ // Step 3: Trigger fade-in by setting opacity to 1
947
+ style.textContent = "\n ".concat(selector, " {\n opacity: 1 !important;\n transition: opacity ").concat(this.config.hideTransition, " !important;\n }\n ");
948
+ // Step 4: Remove style after transition completes
949
+ var transitionDuration = parseFloat(this.config.hideTransition) * 1000;
950
+ setTimeout(function () {
951
+ style.remove();
952
+ if (_this.config.debug) {
953
+ logDebug('[ABsmartly] Anti-flicker fade-in complete, style removed');
954
+ }
955
+ }, transitionDuration);
956
+ if (this.config.debug) {
957
+ logDebug("[ABsmartly] Anti-flicker fading in with transition: ".concat(this.config.hideTransition));
958
+ }
959
+ }
960
+ else {
961
+ // Instant reveal: just remove the style
962
+ style.remove();
963
+ if (this.config.debug) {
964
+ logDebug('[ABsmartly] Anti-flicker removed, content now visible');
965
+ }
966
+ }
967
+ };
968
+ DOMChangesPluginLite.prototype.watchElement = function (element, experimentName, change) {
969
+ var experiments = this.watchedElements.get(element);
970
+ if (!experiments) {
971
+ experiments = new Set();
972
+ this.watchedElements.set(element, experiments);
973
+ }
974
+ experiments.add(experimentName);
975
+ // Store the applied style change for this experiment
976
+ if (!this.appliedStyleChanges.has(experimentName)) {
977
+ this.appliedStyleChanges.set(experimentName, []);
978
+ }
979
+ var changes = this.appliedStyleChanges.get(experimentName);
980
+ var isNewWatch = !changes.includes(change);
981
+ if (isNewWatch) {
982
+ changes.push(change);
983
+ // Only log when actually adding a new watch, not on every reapply
984
+ if (this.config.debug) {
985
+ logDebug('[ABsmartly] Watching element for style persistence', {
986
+ experimentName: experimentName,
987
+ selector: change.selector,
988
+ element: element.tagName,
989
+ });
990
+ }
991
+ }
992
+ if (!this.persistenceObserver) {
993
+ this.setupPersistenceObserver();
994
+ }
995
+ };
996
+ DOMChangesPluginLite.prototype.unwatchElement = function (element, experimentName) {
997
+ var experiments = this.watchedElements.get(element);
998
+ if (experiments) {
999
+ experiments.delete(experimentName);
1000
+ if (experiments.size === 0) {
1001
+ this.watchedElements.delete(element);
1002
+ }
1003
+ }
1004
+ };
1005
+ DOMChangesPluginLite.prototype.setupPersistenceObserver = function () {
1006
+ var _this = this;
1007
+ if (this.persistenceObserver)
1008
+ return;
1009
+ this.persistenceObserver = new MutationObserver(function (mutations) {
1010
+ mutations.forEach(function (mutation) {
1011
+ var element = mutation.target;
1012
+ if (_this.reapplyingElements.has(element)) {
1013
+ return;
1014
+ }
1015
+ var experiments = _this.watchedElements.get(element);
1016
+ if (experiments) {
1017
+ // Throttle mutation detection logs to once per 5 seconds per element
1018
+ if (_this.config.debug) {
1019
+ var elementKey = "mutation:".concat(element.tagName, ":").concat(element.getAttribute('name') || element.className);
1020
+ var now = Date.now();
1021
+ var lastLogged = _this.reapplyLogThrottle.get(elementKey) || 0;
1022
+ if (now - lastLogged > 5000) {
1023
+ logDebug('[ABsmartly] Style mutation detected on watched element', {
1024
+ element: element.tagName,
1025
+ selector: element.getAttribute('name') || element.className,
1026
+ oldValue: mutation.oldValue,
1027
+ });
1028
+ _this.reapplyLogThrottle.set(elementKey, now);
1029
+ }
1030
+ }
1031
+ experiments.forEach(function (experimentName) {
1032
+ var appliedChanges = _this.appliedStyleChanges.get(experimentName);
1033
+ if (appliedChanges) {
1034
+ appliedChanges.forEach(function (change) {
1035
+ var e_8, _a;
1036
+ if (change.type === 'style' && element.matches(change.selector)) {
1037
+ var needsReapply = _this.checkStyleOverwritten(element, change.value);
1038
+ if (needsReapply) {
1039
+ _this.reapplyingElements.add(element);
1040
+ _this.domManipulator.applyChange(change, experimentName);
1041
+ var logKey = "".concat(experimentName, "-").concat(change.selector);
1042
+ var now = Date.now();
1043
+ var lastLogged = _this.reapplyLogThrottle.get(logKey) || 0;
1044
+ if (_this.config.debug && now - lastLogged > 5000) {
1045
+ logDebug('Reapplied style after mutation (React/framework conflict detected)', {
1046
+ experimentName: experimentName,
1047
+ selector: change.selector,
1048
+ note: 'This happens when the page framework (React/Vue/etc) fights with DOM changes',
1049
+ });
1050
+ _this.reapplyLogThrottle.set(logKey, now);
1051
+ // Cleanup old throttle entries to prevent memory leak
1052
+ if (_this.reapplyLogThrottle.size > 100) {
1053
+ var oldestAllowed = now - 60000; // 1 minute
1054
+ try {
1055
+ for (var _b = __values(_this.reapplyLogThrottle.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
1056
+ var _d = __read(_c.value, 2), key = _d[0], time = _d[1];
1057
+ if (time < oldestAllowed) {
1058
+ _this.reapplyLogThrottle.delete(key);
1059
+ }
1060
+ }
1061
+ }
1062
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
1063
+ finally {
1064
+ try {
1065
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1066
+ }
1067
+ finally { if (e_8) throw e_8.error; }
1068
+ }
1069
+ }
1070
+ }
1071
+ setTimeout(function () {
1072
+ _this.reapplyingElements.delete(element);
1073
+ }, 0);
1074
+ }
1075
+ else if (_this.config.debug) {
1076
+ logDebug('[ABsmartly] Style mutation detected but no reapply needed', {
1077
+ experimentName: experimentName,
1078
+ selector: change.selector,
1079
+ });
1080
+ }
1081
+ }
1082
+ });
1083
+ }
1084
+ });
1085
+ }
1086
+ });
1087
+ });
1088
+ this.persistenceObserver.observe(document.body, {
1089
+ attributes: true,
1090
+ attributeFilter: ['style'],
1091
+ subtree: true,
1092
+ attributeOldValue: true,
1093
+ });
1094
+ if (this.config.debug) {
1095
+ logDebug('[ABsmartly] Style persistence observer setup complete');
1096
+ }
1097
+ };
1098
+ DOMChangesPluginLite.prototype.checkStyleOverwritten = function (element, expectedStyles) {
1099
+ var e_9, _a;
1100
+ try {
1101
+ for (var _b = __values(Object.entries(expectedStyles)), _c = _b.next(); !_c.done; _c = _b.next()) {
1102
+ var _d = __read(_c.value, 2), prop = _d[0], value = _d[1];
1103
+ var cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
1104
+ var currentValue = element.style.getPropertyValue(cssProp);
1105
+ var currentPriority = element.style.getPropertyPriority(cssProp);
1106
+ if (currentValue !== value || (value.includes('!important') && !currentPriority)) {
1107
+ return true;
1108
+ }
1109
+ }
1110
+ }
1111
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
1112
+ finally {
1113
+ try {
1114
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1115
+ }
1116
+ finally { if (e_9) throw e_9.error; }
1117
+ }
1118
+ return false;
1119
+ };
1120
+ DOMChangesPluginLite.VERSION = '1.0.0-lite';
1121
+ return DOMChangesPluginLite;
1122
+ }());
1123
+ export { DOMChangesPluginLite };
1124
+ //# sourceMappingURL=DOMChangesPluginLite.js.map