@ninetailed/experience.js-plugin-preview 7.12.0 → 7.13.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +172 -14
- package/index.esm.js +195 -16
- package/package.json +5 -5
- package/src/lib/plugin/NinetailedPreviewPlugin.d.ts +32 -3
package/index.cjs.js
CHANGED
|
@@ -128,7 +128,9 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
128
128
|
this.audienceDefinitions = [];
|
|
129
129
|
this.audienceOverwrites = {};
|
|
130
130
|
this.experienceVariantIndexOverwrites = {};
|
|
131
|
+
this.variableOverwrites = {};
|
|
131
132
|
this.profile = null;
|
|
133
|
+
this.changes = [];
|
|
132
134
|
this.container = null;
|
|
133
135
|
this.bridge = null;
|
|
134
136
|
/**
|
|
@@ -139,7 +141,7 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
139
141
|
this.clientId = null;
|
|
140
142
|
this.environment = null;
|
|
141
143
|
this.initialize = () => __awaiter(this, void 0, void 0, function* () {
|
|
142
|
-
var _b;
|
|
144
|
+
var _b, _c;
|
|
143
145
|
if (typeof window !== 'undefined') {
|
|
144
146
|
if (WidgetContainer.isContainerAttached()) {
|
|
145
147
|
experience_jsShared.logger.warn('Preview plugin is already attached.');
|
|
@@ -165,6 +167,9 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
165
167
|
this.bridge.updateProps({
|
|
166
168
|
props: this.pluginApi
|
|
167
169
|
});
|
|
170
|
+
if (!((_c = this.changes) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
171
|
+
this.onChange();
|
|
172
|
+
}
|
|
168
173
|
}
|
|
169
174
|
});
|
|
170
175
|
this.loaded = () => true;
|
|
@@ -175,9 +180,31 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
175
180
|
return;
|
|
176
181
|
}
|
|
177
182
|
if (payload === null || payload === void 0 ? void 0 : payload.profile) {
|
|
178
|
-
this.onProfileChange(payload.profile);
|
|
183
|
+
this.onProfileChange(payload.profile, payload.changes || []);
|
|
179
184
|
}
|
|
180
185
|
};
|
|
186
|
+
/**
|
|
187
|
+
* Implements the HasChangesModificationMiddleware interface
|
|
188
|
+
* Returns a middleware function that applies variable overwrites to changes
|
|
189
|
+
*/
|
|
190
|
+
this.getChangesModificationMiddleware = () => {
|
|
191
|
+
if (!this.isActiveInstance || Object.keys(this.variableOverwrites).length === 0) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
return ({
|
|
195
|
+
changes: inputChanges
|
|
196
|
+
}) => {
|
|
197
|
+
if (!inputChanges || inputChanges.length === 0) {
|
|
198
|
+
return {
|
|
199
|
+
changes: inputChanges
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// Calculate and return overridden changes on demand instead of storing them
|
|
203
|
+
return {
|
|
204
|
+
changes: this.getEffectiveChanges(inputChanges)
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
};
|
|
181
208
|
this.getExperienceSelectionMiddleware = ({
|
|
182
209
|
baseline,
|
|
183
210
|
experiences
|
|
@@ -197,7 +224,35 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
197
224
|
variantIndex: 0
|
|
198
225
|
};
|
|
199
226
|
}
|
|
200
|
-
|
|
227
|
+
// Handle entry replacements as before
|
|
228
|
+
const entryReplacementComponents = experience.components.filter(component => component.type === experience_jsShared.ComponentTypeEnum.EntryReplacement && 'id' in component.baseline);
|
|
229
|
+
const baselineComponent = entryReplacementComponents.find(component => component.baseline.id === baseline.id);
|
|
230
|
+
// Get the selected variant index
|
|
231
|
+
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
232
|
+
// Handle variable components for this experience (NEW CODE)
|
|
233
|
+
if (variantIndex !== undefined) {
|
|
234
|
+
// Process all variable components for this experience
|
|
235
|
+
const variableComponents = experience.components.filter(component => component.type === experience_jsShared.ComponentTypeEnum.InlineVariable);
|
|
236
|
+
// Set variable values based on the selected variant index
|
|
237
|
+
variableComponents.forEach(component => {
|
|
238
|
+
const key = component.key;
|
|
239
|
+
let value;
|
|
240
|
+
if (variantIndex === 0) {
|
|
241
|
+
value = component.baseline;
|
|
242
|
+
} else {
|
|
243
|
+
const variant = component.variants[variantIndex - 1];
|
|
244
|
+
value = variant && 'value' in variant ? variant.value : component.baseline;
|
|
245
|
+
}
|
|
246
|
+
// Set the variable in our changes system
|
|
247
|
+
this.setVariableValue({
|
|
248
|
+
experienceId: experience.id,
|
|
249
|
+
key,
|
|
250
|
+
value,
|
|
251
|
+
variantIndex
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
// Continue with entry replacement handling
|
|
201
256
|
if (!baselineComponent) {
|
|
202
257
|
return {
|
|
203
258
|
experience,
|
|
@@ -206,7 +261,6 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
206
261
|
};
|
|
207
262
|
}
|
|
208
263
|
const allVariants = [baseline, ...baselineComponent.variants];
|
|
209
|
-
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
210
264
|
if (allVariants.length <= variantIndex) {
|
|
211
265
|
return {
|
|
212
266
|
experience,
|
|
@@ -232,18 +286,42 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
232
286
|
this.onChange = () => {
|
|
233
287
|
var _b;
|
|
234
288
|
experience_jsShared.logger.debug('Ninetailed Preview Plugin onChange pluginApi:', this.pluginApi);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
289
|
+
if (typeof window !== 'undefined') {
|
|
290
|
+
window.ninetailed = Object.assign({}, window.ninetailed, {
|
|
291
|
+
plugins: Object.assign(Object.assign({}, (_b = window.ninetailed) === null || _b === void 0 ? void 0 : _b.plugins), {
|
|
292
|
+
preview: this.windowApi
|
|
293
|
+
})
|
|
294
|
+
});
|
|
295
|
+
}
|
|
240
296
|
this.bridge.updateProps({
|
|
241
297
|
props: this.pluginApi
|
|
242
298
|
});
|
|
243
299
|
this.onChangeEmitter.invokeListeners();
|
|
244
300
|
};
|
|
245
|
-
this.onProfileChange = profile => {
|
|
301
|
+
this.onProfileChange = (profile, changes) => {
|
|
246
302
|
this.profile = profile;
|
|
303
|
+
experience_jsShared.logger.debug('Profile changed:', {
|
|
304
|
+
profile,
|
|
305
|
+
changes
|
|
306
|
+
});
|
|
307
|
+
// If changes are provided, update them
|
|
308
|
+
if (changes) {
|
|
309
|
+
this.onChangesChange(changes);
|
|
310
|
+
}
|
|
311
|
+
this.onChange();
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Handles changes from the SDK and applies any variable overrides.
|
|
315
|
+
* This should be called whenever the original changes are updated.
|
|
316
|
+
*/
|
|
317
|
+
this.onChangesChange = incomingChanges => {
|
|
318
|
+
if (!this.isActiveInstance) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
experience_jsShared.logger.debug('Received changes:', incomingChanges);
|
|
322
|
+
// Store the original changes
|
|
323
|
+
this.changes = incomingChanges;
|
|
324
|
+
// Notify listeners and update UI
|
|
247
325
|
this.onChange();
|
|
248
326
|
};
|
|
249
327
|
this.setCredentials = ({
|
|
@@ -369,23 +447,40 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
369
447
|
}
|
|
370
448
|
const experience = this.experiences.find(experience => experience.id === experienceId);
|
|
371
449
|
if (!experience) {
|
|
372
|
-
experience_jsShared.logger.warn(`
|
|
450
|
+
experience_jsShared.logger.warn(`Cannot activate a variant for an unknown experience (id: ${experienceId})`);
|
|
373
451
|
return;
|
|
374
452
|
}
|
|
375
453
|
if (experience.audience && !this.activeAudiences.some(id => {
|
|
376
454
|
var _b;
|
|
377
455
|
return id === ((_b = experience.audience) === null || _b === void 0 ? void 0 : _b.id);
|
|
378
456
|
})) {
|
|
379
|
-
experience_jsShared.logger.warn(`
|
|
457
|
+
experience_jsShared.logger.warn(`Cannot activate a variant for an experience (id: ${experienceId}) which is not in the active audiences.`);
|
|
380
458
|
return;
|
|
381
459
|
}
|
|
382
460
|
const isValidIndex = experience.components.map(component => component.variants.length + 1).every(length => length > variantIndex);
|
|
383
461
|
if (!isValidIndex) {
|
|
384
462
|
experience_jsShared.logger.warn(`You activated a variant at index ${variantIndex} for the experience (id: ${experienceId}). Not all components have that many variants, you may see the baseline for some.`);
|
|
385
463
|
}
|
|
464
|
+
// Update the experience variant index
|
|
386
465
|
this.experienceVariantIndexOverwrites = Object.assign(Object.assign({}, this.experienceVariantIndexOverwrites), {
|
|
387
466
|
[experienceId]: variantIndex
|
|
388
467
|
});
|
|
468
|
+
// Process all components to extract variable values
|
|
469
|
+
experience.components.forEach(component => {
|
|
470
|
+
var _b, _c;
|
|
471
|
+
if (component.type === experience_jsShared.ComponentTypeEnum.InlineVariable) {
|
|
472
|
+
const key = component.key;
|
|
473
|
+
const value = variantIndex === 0 ? component.baseline.value : (_c = (_b = component.variants[variantIndex - 1]) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : component.baseline.value;
|
|
474
|
+
// Set the variable value
|
|
475
|
+
this.setVariableValue({
|
|
476
|
+
experienceId,
|
|
477
|
+
key,
|
|
478
|
+
value,
|
|
479
|
+
variantIndex
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
// Trigger change notification - this updates the middleware
|
|
389
484
|
this.onChange();
|
|
390
485
|
}
|
|
391
486
|
resetExperience(experienceId) {
|
|
@@ -407,6 +502,42 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
407
502
|
window.ninetailed.reset();
|
|
408
503
|
}
|
|
409
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Sets a variable value override for preview
|
|
507
|
+
*/
|
|
508
|
+
setVariableValue({
|
|
509
|
+
experienceId,
|
|
510
|
+
key,
|
|
511
|
+
value,
|
|
512
|
+
variantIndex
|
|
513
|
+
}) {
|
|
514
|
+
var _b;
|
|
515
|
+
if (!this.isActiveInstance) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const overrideKey = `${experienceId}:${key}`;
|
|
519
|
+
// Only create new object if actually changing
|
|
520
|
+
if (((_b = this.variableOverwrites[overrideKey]) === null || _b === void 0 ? void 0 : _b.value) === value) {
|
|
521
|
+
return; // No change needed
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const change = {
|
|
525
|
+
type: experience_jsShared.ChangeTypes.Variable,
|
|
526
|
+
key,
|
|
527
|
+
value,
|
|
528
|
+
meta: {
|
|
529
|
+
experienceId,
|
|
530
|
+
variantIndex
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
// Update variable overwrites
|
|
534
|
+
this.variableOverwrites = Object.assign(Object.assign({}, this.variableOverwrites), {
|
|
535
|
+
[overrideKey]: change
|
|
536
|
+
});
|
|
537
|
+
// Notify listeners
|
|
538
|
+
this.onChangeEmitter.invokeListeners();
|
|
539
|
+
this.onChange();
|
|
540
|
+
}
|
|
410
541
|
openExperienceEditor(experience) {
|
|
411
542
|
if (this.onOpenExperienceEditor && typeof this.onOpenExperienceEditor === 'function') return this.onOpenExperienceEditor(experience);
|
|
412
543
|
}
|
|
@@ -419,7 +550,7 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
419
550
|
get pluginApi() {
|
|
420
551
|
var _b;
|
|
421
552
|
return {
|
|
422
|
-
version: "7.
|
|
553
|
+
version: "7.13.0-beta.0" ,
|
|
423
554
|
open: this.open.bind(this),
|
|
424
555
|
close: this.close.bind(this),
|
|
425
556
|
toggle: this.toggle.bind(this),
|
|
@@ -455,7 +586,9 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
455
586
|
activeAudiences: this.activeAudiences,
|
|
456
587
|
setExperienceVariant: this.setExperienceVariant.bind(this),
|
|
457
588
|
resetExperience: this.resetExperience.bind(this),
|
|
458
|
-
experienceVariantIndexes: Object.assign(Object.assign({}, this.experienceVariantIndexes), this.experienceVariantIndexOverwrites)
|
|
589
|
+
experienceVariantIndexes: Object.assign(Object.assign({}, this.experienceVariantIndexes), this.experienceVariantIndexOverwrites),
|
|
590
|
+
setVariableValue: this.setVariableValue.bind(this),
|
|
591
|
+
variableOverwrites: this.variableOverwrites
|
|
459
592
|
};
|
|
460
593
|
}
|
|
461
594
|
isKnownAudience(id) {
|
|
@@ -502,6 +635,31 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
502
635
|
audiences: this.activeAudiences
|
|
503
636
|
}));
|
|
504
637
|
}
|
|
638
|
+
/**
|
|
639
|
+
* Get the override key for a variable
|
|
640
|
+
*/
|
|
641
|
+
getOverrideKey(experienceId, key) {
|
|
642
|
+
return `${experienceId}:${key}`;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Get effective changes by applying overrides - compute on demand
|
|
646
|
+
*/
|
|
647
|
+
getEffectiveChanges(inputChanges = this.changes) {
|
|
648
|
+
if (!inputChanges || Object.keys(this.variableOverwrites).length === 0) {
|
|
649
|
+
return inputChanges || [];
|
|
650
|
+
}
|
|
651
|
+
// Filter out changes that we're overriding
|
|
652
|
+
const filteredChanges = inputChanges.filter(change => {
|
|
653
|
+
var _b;
|
|
654
|
+
if (change.type !== experience_jsShared.ChangeTypes.Variable) return true;
|
|
655
|
+
const changeKey = ((_b = change.meta) === null || _b === void 0 ? void 0 : _b.experienceId) ? this.getOverrideKey(change.meta.experienceId, change.key) : change.key;
|
|
656
|
+
return !this.variableOverwrites[changeKey];
|
|
657
|
+
});
|
|
658
|
+
const effectiveChanges = [...filteredChanges, ...Object.values(this.variableOverwrites)];
|
|
659
|
+
experience_jsShared.logger.debug('Overridden changes after applying override:', effectiveChanges);
|
|
660
|
+
// Add our overrides to create the final result
|
|
661
|
+
return effectiveChanges;
|
|
662
|
+
}
|
|
505
663
|
}
|
|
506
664
|
_a = experience_js.PROFILE_CHANGE;
|
|
507
665
|
|
package/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger, unionBy } from '@ninetailed/experience.js-shared';
|
|
1
|
+
import { logger, ComponentTypeEnum, ChangeTypes, unionBy } from '@ninetailed/experience.js-shared';
|
|
2
2
|
import { OnChangeEmitter, PROFILE_CHANGE, isExperienceMatch, selectDistribution } from '@ninetailed/experience.js';
|
|
3
3
|
import { NinetailedPlugin } from '@ninetailed/experience.js-plugin-analytics';
|
|
4
4
|
|
|
@@ -97,7 +97,9 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
97
97
|
this.audienceDefinitions = [];
|
|
98
98
|
this.audienceOverwrites = {};
|
|
99
99
|
this.experienceVariantIndexOverwrites = {};
|
|
100
|
+
this.variableOverwrites = {};
|
|
100
101
|
this.profile = null;
|
|
102
|
+
this.changes = [];
|
|
101
103
|
this.container = null;
|
|
102
104
|
this.bridge = null;
|
|
103
105
|
/**
|
|
@@ -111,7 +113,7 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
111
113
|
this.environment = null;
|
|
112
114
|
this.initialize = async function () {
|
|
113
115
|
if (typeof window !== 'undefined') {
|
|
114
|
-
var _window$ninetailed;
|
|
116
|
+
var _window$ninetailed, _this$changes;
|
|
115
117
|
if (WidgetContainer.isContainerAttached()) {
|
|
116
118
|
logger.warn('Preview plugin is already attached.');
|
|
117
119
|
_this.isActiveInstance = false;
|
|
@@ -136,6 +138,9 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
136
138
|
_this.bridge.updateProps({
|
|
137
139
|
props: _this.pluginApi
|
|
138
140
|
});
|
|
141
|
+
if (!((_this$changes = _this.changes) != null && _this$changes.length)) {
|
|
142
|
+
_this.onChange();
|
|
143
|
+
}
|
|
139
144
|
}
|
|
140
145
|
};
|
|
141
146
|
this.loaded = () => true;
|
|
@@ -146,8 +151,31 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
146
151
|
return;
|
|
147
152
|
}
|
|
148
153
|
if (payload != null && payload.profile) {
|
|
149
|
-
this.onProfileChange(payload.profile);
|
|
154
|
+
this.onProfileChange(payload.profile, payload.changes || []);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Implements the HasChangesModificationMiddleware interface
|
|
159
|
+
* Returns a middleware function that applies variable overwrites to changes
|
|
160
|
+
*/
|
|
161
|
+
this.getChangesModificationMiddleware = () => {
|
|
162
|
+
if (!this.isActiveInstance || Object.keys(this.variableOverwrites).length === 0) {
|
|
163
|
+
return undefined;
|
|
150
164
|
}
|
|
165
|
+
return ({
|
|
166
|
+
changes: inputChanges
|
|
167
|
+
}) => {
|
|
168
|
+
if (!inputChanges || inputChanges.length === 0) {
|
|
169
|
+
return {
|
|
170
|
+
changes: inputChanges
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Calculate and return overridden changes on demand instead of storing them
|
|
175
|
+
return {
|
|
176
|
+
changes: this.getEffectiveChanges(inputChanges)
|
|
177
|
+
};
|
|
178
|
+
};
|
|
151
179
|
};
|
|
152
180
|
this.getExperienceSelectionMiddleware = ({
|
|
153
181
|
baseline,
|
|
@@ -168,7 +196,41 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
168
196
|
variantIndex: 0
|
|
169
197
|
};
|
|
170
198
|
}
|
|
171
|
-
|
|
199
|
+
|
|
200
|
+
// Handle entry replacements as before
|
|
201
|
+
const entryReplacementComponents = experience.components.filter(component => component.type === ComponentTypeEnum.EntryReplacement && 'id' in component.baseline);
|
|
202
|
+
const baselineComponent = entryReplacementComponents.find(component => component.baseline.id === baseline.id);
|
|
203
|
+
|
|
204
|
+
// Get the selected variant index
|
|
205
|
+
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
206
|
+
|
|
207
|
+
// Handle variable components for this experience (NEW CODE)
|
|
208
|
+
if (variantIndex !== undefined) {
|
|
209
|
+
// Process all variable components for this experience
|
|
210
|
+
const variableComponents = experience.components.filter(component => component.type === ComponentTypeEnum.InlineVariable);
|
|
211
|
+
|
|
212
|
+
// Set variable values based on the selected variant index
|
|
213
|
+
variableComponents.forEach(component => {
|
|
214
|
+
const key = component.key;
|
|
215
|
+
let value;
|
|
216
|
+
if (variantIndex === 0) {
|
|
217
|
+
value = component.baseline;
|
|
218
|
+
} else {
|
|
219
|
+
const variant = component.variants[variantIndex - 1];
|
|
220
|
+
value = variant && 'value' in variant ? variant.value : component.baseline;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Set the variable in our changes system
|
|
224
|
+
this.setVariableValue({
|
|
225
|
+
experienceId: experience.id,
|
|
226
|
+
key,
|
|
227
|
+
value,
|
|
228
|
+
variantIndex
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Continue with entry replacement handling
|
|
172
234
|
if (!baselineComponent) {
|
|
173
235
|
return {
|
|
174
236
|
experience,
|
|
@@ -177,7 +239,6 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
177
239
|
};
|
|
178
240
|
}
|
|
179
241
|
const allVariants = [baseline, ...baselineComponent.variants];
|
|
180
|
-
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
181
242
|
if (allVariants.length <= variantIndex) {
|
|
182
243
|
return {
|
|
183
244
|
experience,
|
|
@@ -201,20 +262,47 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
201
262
|
};
|
|
202
263
|
};
|
|
203
264
|
this.onChange = () => {
|
|
204
|
-
var _window$ninetailed2;
|
|
205
265
|
logger.debug('Ninetailed Preview Plugin onChange pluginApi:', this.pluginApi);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
266
|
+
if (typeof window !== 'undefined') {
|
|
267
|
+
var _window$ninetailed2;
|
|
268
|
+
window.ninetailed = Object.assign({}, window.ninetailed, {
|
|
269
|
+
plugins: Object.assign({}, (_window$ninetailed2 = window.ninetailed) == null ? void 0 : _window$ninetailed2.plugins, {
|
|
270
|
+
preview: this.windowApi
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
}
|
|
211
274
|
this.bridge.updateProps({
|
|
212
275
|
props: this.pluginApi
|
|
213
276
|
});
|
|
214
277
|
this.onChangeEmitter.invokeListeners();
|
|
215
278
|
};
|
|
216
|
-
this.onProfileChange = profile => {
|
|
279
|
+
this.onProfileChange = (profile, changes) => {
|
|
217
280
|
this.profile = profile;
|
|
281
|
+
logger.debug('Profile changed:', {
|
|
282
|
+
profile,
|
|
283
|
+
changes
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// If changes are provided, update them
|
|
287
|
+
if (changes) {
|
|
288
|
+
this.onChangesChange(changes);
|
|
289
|
+
}
|
|
290
|
+
this.onChange();
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Handles changes from the SDK and applies any variable overrides.
|
|
294
|
+
* This should be called whenever the original changes are updated.
|
|
295
|
+
*/
|
|
296
|
+
this.onChangesChange = incomingChanges => {
|
|
297
|
+
if (!this.isActiveInstance) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
logger.debug('Received changes:', incomingChanges);
|
|
301
|
+
|
|
302
|
+
// Store the original changes
|
|
303
|
+
this.changes = incomingChanges;
|
|
304
|
+
|
|
305
|
+
// Notify listeners and update UI
|
|
218
306
|
this.onChange();
|
|
219
307
|
};
|
|
220
308
|
this.setCredentials = ({
|
|
@@ -339,23 +427,44 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
339
427
|
}
|
|
340
428
|
const experience = this.experiences.find(experience => experience.id === experienceId);
|
|
341
429
|
if (!experience) {
|
|
342
|
-
logger.warn(`
|
|
430
|
+
logger.warn(`Cannot activate a variant for an unknown experience (id: ${experienceId})`);
|
|
343
431
|
return;
|
|
344
432
|
}
|
|
345
433
|
if (experience.audience && !this.activeAudiences.some(id => {
|
|
346
434
|
var _experience$audience4;
|
|
347
435
|
return id === ((_experience$audience4 = experience.audience) == null ? void 0 : _experience$audience4.id);
|
|
348
436
|
})) {
|
|
349
|
-
logger.warn(`
|
|
437
|
+
logger.warn(`Cannot activate a variant for an experience (id: ${experienceId}) which is not in the active audiences.`);
|
|
350
438
|
return;
|
|
351
439
|
}
|
|
352
440
|
const isValidIndex = experience.components.map(component => component.variants.length + 1).every(length => length > variantIndex);
|
|
353
441
|
if (!isValidIndex) {
|
|
354
442
|
logger.warn(`You activated a variant at index ${variantIndex} for the experience (id: ${experienceId}). Not all components have that many variants, you may see the baseline for some.`);
|
|
355
443
|
}
|
|
444
|
+
|
|
445
|
+
// Update the experience variant index
|
|
356
446
|
this.experienceVariantIndexOverwrites = Object.assign({}, this.experienceVariantIndexOverwrites, {
|
|
357
447
|
[experienceId]: variantIndex
|
|
358
448
|
});
|
|
449
|
+
|
|
450
|
+
// Process all components to extract variable values
|
|
451
|
+
experience.components.forEach(component => {
|
|
452
|
+
if (component.type === ComponentTypeEnum.InlineVariable) {
|
|
453
|
+
var _component$variants$v, _component$variants;
|
|
454
|
+
const key = component.key;
|
|
455
|
+
const value = variantIndex === 0 ? component.baseline.value : (_component$variants$v = (_component$variants = component.variants[variantIndex - 1]) == null ? void 0 : _component$variants.value) != null ? _component$variants$v : component.baseline.value;
|
|
456
|
+
|
|
457
|
+
// Set the variable value
|
|
458
|
+
this.setVariableValue({
|
|
459
|
+
experienceId,
|
|
460
|
+
key,
|
|
461
|
+
value,
|
|
462
|
+
variantIndex
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Trigger change notification - this updates the middleware
|
|
359
468
|
this.onChange();
|
|
360
469
|
}
|
|
361
470
|
resetExperience(experienceId) {
|
|
@@ -375,6 +484,45 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
375
484
|
window.ninetailed.reset();
|
|
376
485
|
}
|
|
377
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Sets a variable value override for preview
|
|
489
|
+
*/
|
|
490
|
+
setVariableValue({
|
|
491
|
+
experienceId,
|
|
492
|
+
key,
|
|
493
|
+
value,
|
|
494
|
+
variantIndex
|
|
495
|
+
}) {
|
|
496
|
+
var _this$variableOverwri;
|
|
497
|
+
if (!this.isActiveInstance) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const overrideKey = `${experienceId}:${key}`;
|
|
501
|
+
|
|
502
|
+
// Only create new object if actually changing
|
|
503
|
+
if (((_this$variableOverwri = this.variableOverwrites[overrideKey]) == null ? void 0 : _this$variableOverwri.value) === value) {
|
|
504
|
+
return; // No change needed
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const change = {
|
|
508
|
+
type: ChangeTypes.Variable,
|
|
509
|
+
key,
|
|
510
|
+
value,
|
|
511
|
+
meta: {
|
|
512
|
+
experienceId,
|
|
513
|
+
variantIndex
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// Update variable overwrites
|
|
518
|
+
this.variableOverwrites = Object.assign({}, this.variableOverwrites, {
|
|
519
|
+
[overrideKey]: change
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Notify listeners
|
|
523
|
+
this.onChangeEmitter.invokeListeners();
|
|
524
|
+
this.onChange();
|
|
525
|
+
}
|
|
378
526
|
openExperienceEditor(experience) {
|
|
379
527
|
if (this.onOpenExperienceEditor && typeof this.onOpenExperienceEditor === 'function') return this.onOpenExperienceEditor(experience);
|
|
380
528
|
}
|
|
@@ -387,7 +535,7 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
387
535
|
get pluginApi() {
|
|
388
536
|
var _this$profile;
|
|
389
537
|
return {
|
|
390
|
-
version: "7.
|
|
538
|
+
version: "7.13.0-beta.0" ,
|
|
391
539
|
open: this.open.bind(this),
|
|
392
540
|
close: this.close.bind(this),
|
|
393
541
|
toggle: this.toggle.bind(this),
|
|
@@ -423,7 +571,9 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
423
571
|
activeAudiences: this.activeAudiences,
|
|
424
572
|
setExperienceVariant: this.setExperienceVariant.bind(this),
|
|
425
573
|
resetExperience: this.resetExperience.bind(this),
|
|
426
|
-
experienceVariantIndexes: Object.assign({}, this.experienceVariantIndexes, this.experienceVariantIndexOverwrites)
|
|
574
|
+
experienceVariantIndexes: Object.assign({}, this.experienceVariantIndexes, this.experienceVariantIndexOverwrites),
|
|
575
|
+
setVariableValue: this.setVariableValue.bind(this),
|
|
576
|
+
variableOverwrites: this.variableOverwrites
|
|
427
577
|
};
|
|
428
578
|
}
|
|
429
579
|
isKnownAudience(id) {
|
|
@@ -470,6 +620,35 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
470
620
|
audiences: this.activeAudiences
|
|
471
621
|
}));
|
|
472
622
|
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Get the override key for a variable
|
|
626
|
+
*/
|
|
627
|
+
getOverrideKey(experienceId, key) {
|
|
628
|
+
return `${experienceId}:${key}`;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Get effective changes by applying overrides - compute on demand
|
|
633
|
+
*/
|
|
634
|
+
getEffectiveChanges(inputChanges = this.changes) {
|
|
635
|
+
if (!inputChanges || Object.keys(this.variableOverwrites).length === 0) {
|
|
636
|
+
return inputChanges || [];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Filter out changes that we're overriding
|
|
640
|
+
const filteredChanges = inputChanges.filter(change => {
|
|
641
|
+
var _change$meta;
|
|
642
|
+
if (change.type !== ChangeTypes.Variable) return true;
|
|
643
|
+
const changeKey = (_change$meta = change.meta) != null && _change$meta.experienceId ? this.getOverrideKey(change.meta.experienceId, change.key) : change.key;
|
|
644
|
+
return !this.variableOverwrites[changeKey];
|
|
645
|
+
});
|
|
646
|
+
const effectiveChanges = [...filteredChanges, ...Object.values(this.variableOverwrites)];
|
|
647
|
+
logger.debug('Overridden changes after applying override:', effectiveChanges);
|
|
648
|
+
|
|
649
|
+
// Add our overrides to create the final result
|
|
650
|
+
return effectiveChanges;
|
|
651
|
+
}
|
|
473
652
|
}
|
|
474
653
|
|
|
475
654
|
if (typeof window === 'object' && !('process' in window)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ninetailed/experience.js-plugin-preview",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.13.0-beta.0",
|
|
4
4
|
"description": "Ninetailed SDK plugin for preview",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"a/b testing"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@ninetailed/experience.js-shared": "7.
|
|
19
|
-
"@ninetailed/experience.js": "7.
|
|
20
|
-
"@ninetailed/experience.js-preview-bridge": "7.
|
|
21
|
-
"@ninetailed/experience.js-plugin-analytics": "7.
|
|
18
|
+
"@ninetailed/experience.js-shared": "7.13.0-beta.0",
|
|
19
|
+
"@ninetailed/experience.js": "7.13.0-beta.0",
|
|
20
|
+
"@ninetailed/experience.js-preview-bridge": "7.13.0-beta.0",
|
|
21
|
+
"@ninetailed/experience.js-plugin-analytics": "7.13.0-beta.0"
|
|
22
22
|
},
|
|
23
23
|
"module": "./index.esm.js",
|
|
24
24
|
"main": "./index.cjs.js"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Reference } from '@ninetailed/experience.js-shared';
|
|
2
|
-
import { ExperienceConfiguration, PROFILE_CHANGE, HasExperienceSelectionMiddleware, BuildExperienceSelectionMiddleware, type ProfileChangedPayload, type InterestedInProfileChange } from '@ninetailed/experience.js';
|
|
1
|
+
import { Reference, AllowedVariableType } from '@ninetailed/experience.js-shared';
|
|
2
|
+
import { ExperienceConfiguration, PROFILE_CHANGE, HasExperienceSelectionMiddleware, BuildExperienceSelectionMiddleware, HasChangesModificationMiddleware, BuildChangesModificationMiddleware, type ProfileChangedPayload, type InterestedInProfileChange } from '@ninetailed/experience.js';
|
|
3
3
|
import type { ExposedAudienceDefinition } from '@ninetailed/experience.js-preview-bridge';
|
|
4
4
|
import { type EventHandler, NinetailedPlugin } from '@ninetailed/experience.js-plugin-analytics';
|
|
5
5
|
export declare const NINETAILED_PREVIEW_EVENTS: {
|
|
@@ -18,7 +18,7 @@ type NinetailedPreviewPluginOptions = {
|
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
|
-
export declare class NinetailedPreviewPlugin extends NinetailedPlugin implements HasExperienceSelectionMiddleware<Reference, Reference>, InterestedInProfileChange {
|
|
21
|
+
export declare class NinetailedPreviewPlugin extends NinetailedPlugin implements HasExperienceSelectionMiddleware<Reference, Reference>, HasChangesModificationMiddleware, InterestedInProfileChange {
|
|
22
22
|
private readonly options;
|
|
23
23
|
name: string;
|
|
24
24
|
private isOpen;
|
|
@@ -26,7 +26,9 @@ export declare class NinetailedPreviewPlugin extends NinetailedPlugin implements
|
|
|
26
26
|
private readonly audienceDefinitions;
|
|
27
27
|
private audienceOverwrites;
|
|
28
28
|
private experienceVariantIndexOverwrites;
|
|
29
|
+
private variableOverwrites;
|
|
29
30
|
private profile;
|
|
31
|
+
private changes;
|
|
30
32
|
private container;
|
|
31
33
|
private bridge;
|
|
32
34
|
/**
|
|
@@ -54,6 +56,20 @@ export declare class NinetailedPreviewPlugin extends NinetailedPlugin implements
|
|
|
54
56
|
}): void;
|
|
55
57
|
resetExperience(experienceId: string): void;
|
|
56
58
|
reset(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Implements the HasChangesModificationMiddleware interface
|
|
61
|
+
* Returns a middleware function that applies variable overwrites to changes
|
|
62
|
+
*/
|
|
63
|
+
getChangesModificationMiddleware: BuildChangesModificationMiddleware;
|
|
64
|
+
/**
|
|
65
|
+
* Sets a variable value override for preview
|
|
66
|
+
*/
|
|
67
|
+
setVariableValue({ experienceId, key, value, variantIndex, }: {
|
|
68
|
+
experienceId: string;
|
|
69
|
+
key: string;
|
|
70
|
+
value: AllowedVariableType;
|
|
71
|
+
variantIndex: number;
|
|
72
|
+
}): void;
|
|
57
73
|
getExperienceSelectionMiddleware: BuildExperienceSelectionMiddleware<Reference, Reference>;
|
|
58
74
|
openExperienceEditor(experience: ExperienceConfiguration): void;
|
|
59
75
|
openExperienceAnalytics(experience: ExperienceConfiguration): void;
|
|
@@ -66,8 +82,21 @@ export declare class NinetailedPreviewPlugin extends NinetailedPlugin implements
|
|
|
66
82
|
private calculateExperienceVariantIndexes;
|
|
67
83
|
private get apiExperienceVariantIndexes();
|
|
68
84
|
private get experienceVariantIndexes();
|
|
85
|
+
/**
|
|
86
|
+
* Get the override key for a variable
|
|
87
|
+
*/
|
|
88
|
+
private getOverrideKey;
|
|
89
|
+
/**
|
|
90
|
+
* Get effective changes by applying overrides - compute on demand
|
|
91
|
+
*/
|
|
92
|
+
private getEffectiveChanges;
|
|
69
93
|
private onChange;
|
|
70
94
|
private onProfileChange;
|
|
95
|
+
/**
|
|
96
|
+
* Handles changes from the SDK and applies any variable overrides.
|
|
97
|
+
* This should be called whenever the original changes are updated.
|
|
98
|
+
*/
|
|
99
|
+
private onChangesChange;
|
|
71
100
|
private setCredentials;
|
|
72
101
|
}
|
|
73
102
|
export {};
|