@ninetailed/experience.js-plugin-preview 7.12.1 → 7.13.0-beta.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/index.cjs.js +172 -15
- package/index.esm.js +195 -17
- package/package.json +5 -5
- package/src/lib/plugin/NinetailedPreviewPlugin.d.ts +32 -3
package/index.cjs.js
CHANGED
|
@@ -128,9 +128,10 @@ 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
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
135
|
this.bridge = null;
|
|
135
136
|
/**
|
|
136
137
|
* Since several instances of the plugin can be created, we need to make sure only one is marked as active.
|
|
@@ -140,7 +141,7 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
140
141
|
this.clientId = null;
|
|
141
142
|
this.environment = null;
|
|
142
143
|
this.initialize = () => __awaiter(this, void 0, void 0, function* () {
|
|
143
|
-
var _b;
|
|
144
|
+
var _b, _c;
|
|
144
145
|
if (typeof window !== 'undefined') {
|
|
145
146
|
if (WidgetContainer.isContainerAttached()) {
|
|
146
147
|
experience_jsShared.logger.warn('Preview plugin is already attached.');
|
|
@@ -166,6 +167,9 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
166
167
|
this.bridge.updateProps({
|
|
167
168
|
props: this.pluginApi
|
|
168
169
|
});
|
|
170
|
+
if (!((_c = this.changes) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
171
|
+
this.onChange();
|
|
172
|
+
}
|
|
169
173
|
}
|
|
170
174
|
});
|
|
171
175
|
this.loaded = () => true;
|
|
@@ -176,9 +180,31 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
176
180
|
return;
|
|
177
181
|
}
|
|
178
182
|
if (payload === null || payload === void 0 ? void 0 : payload.profile) {
|
|
179
|
-
this.onProfileChange(payload.profile);
|
|
183
|
+
this.onProfileChange(payload.profile, payload.changes || []);
|
|
180
184
|
}
|
|
181
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
|
+
};
|
|
182
208
|
this.getExperienceSelectionMiddleware = ({
|
|
183
209
|
baseline,
|
|
184
210
|
experiences
|
|
@@ -198,7 +224,35 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
198
224
|
variantIndex: 0
|
|
199
225
|
};
|
|
200
226
|
}
|
|
201
|
-
|
|
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
|
|
202
256
|
if (!baselineComponent) {
|
|
203
257
|
return {
|
|
204
258
|
experience,
|
|
@@ -207,7 +261,6 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
207
261
|
};
|
|
208
262
|
}
|
|
209
263
|
const allVariants = [baseline, ...baselineComponent.variants];
|
|
210
|
-
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
211
264
|
if (allVariants.length <= variantIndex) {
|
|
212
265
|
return {
|
|
213
266
|
experience,
|
|
@@ -233,18 +286,42 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
233
286
|
this.onChange = () => {
|
|
234
287
|
var _b;
|
|
235
288
|
experience_jsShared.logger.debug('Ninetailed Preview Plugin onChange pluginApi:', this.pluginApi);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
+
}
|
|
241
296
|
this.bridge.updateProps({
|
|
242
297
|
props: this.pluginApi
|
|
243
298
|
});
|
|
244
299
|
this.onChangeEmitter.invokeListeners();
|
|
245
300
|
};
|
|
246
|
-
this.onProfileChange = profile => {
|
|
301
|
+
this.onProfileChange = (profile, changes) => {
|
|
247
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
|
|
248
325
|
this.onChange();
|
|
249
326
|
};
|
|
250
327
|
this.setCredentials = ({
|
|
@@ -370,23 +447,40 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
370
447
|
}
|
|
371
448
|
const experience = this.experiences.find(experience => experience.id === experienceId);
|
|
372
449
|
if (!experience) {
|
|
373
|
-
experience_jsShared.logger.warn(`
|
|
450
|
+
experience_jsShared.logger.warn(`Cannot activate a variant for an unknown experience (id: ${experienceId})`);
|
|
374
451
|
return;
|
|
375
452
|
}
|
|
376
453
|
if (experience.audience && !this.activeAudiences.some(id => {
|
|
377
454
|
var _b;
|
|
378
455
|
return id === ((_b = experience.audience) === null || _b === void 0 ? void 0 : _b.id);
|
|
379
456
|
})) {
|
|
380
|
-
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.`);
|
|
381
458
|
return;
|
|
382
459
|
}
|
|
383
460
|
const isValidIndex = experience.components.map(component => component.variants.length + 1).every(length => length > variantIndex);
|
|
384
461
|
if (!isValidIndex) {
|
|
385
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.`);
|
|
386
463
|
}
|
|
464
|
+
// Update the experience variant index
|
|
387
465
|
this.experienceVariantIndexOverwrites = Object.assign(Object.assign({}, this.experienceVariantIndexOverwrites), {
|
|
388
466
|
[experienceId]: variantIndex
|
|
389
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
|
|
390
484
|
this.onChange();
|
|
391
485
|
}
|
|
392
486
|
resetExperience(experienceId) {
|
|
@@ -408,6 +502,42 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
408
502
|
window.ninetailed.reset();
|
|
409
503
|
}
|
|
410
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
|
+
}
|
|
411
541
|
openExperienceEditor(experience) {
|
|
412
542
|
if (this.onOpenExperienceEditor && typeof this.onOpenExperienceEditor === 'function') return this.onOpenExperienceEditor(experience);
|
|
413
543
|
}
|
|
@@ -420,7 +550,7 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
420
550
|
get pluginApi() {
|
|
421
551
|
var _b;
|
|
422
552
|
return {
|
|
423
|
-
version: "7.
|
|
553
|
+
version: "7.13.0-beta.1" ,
|
|
424
554
|
open: this.open.bind(this),
|
|
425
555
|
close: this.close.bind(this),
|
|
426
556
|
toggle: this.toggle.bind(this),
|
|
@@ -456,7 +586,9 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
456
586
|
activeAudiences: this.activeAudiences,
|
|
457
587
|
setExperienceVariant: this.setExperienceVariant.bind(this),
|
|
458
588
|
resetExperience: this.resetExperience.bind(this),
|
|
459
|
-
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
|
|
460
592
|
};
|
|
461
593
|
}
|
|
462
594
|
isKnownAudience(id) {
|
|
@@ -503,6 +635,31 @@ class NinetailedPreviewPlugin extends experience_jsPluginAnalytics.NinetailedPlu
|
|
|
503
635
|
audiences: this.activeAudiences
|
|
504
636
|
}));
|
|
505
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
|
+
}
|
|
506
663
|
}
|
|
507
664
|
_a = experience_js.PROFILE_CHANGE;
|
|
508
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,9 +97,10 @@ 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
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
104
|
this.bridge = null;
|
|
104
105
|
/**
|
|
105
106
|
* Since several instances of the plugin can be created, we need to make sure only one is marked as active.
|
|
@@ -112,7 +113,7 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
112
113
|
this.environment = null;
|
|
113
114
|
this.initialize = async function () {
|
|
114
115
|
if (typeof window !== 'undefined') {
|
|
115
|
-
var _window$ninetailed;
|
|
116
|
+
var _window$ninetailed, _this$changes;
|
|
116
117
|
if (WidgetContainer.isContainerAttached()) {
|
|
117
118
|
logger.warn('Preview plugin is already attached.');
|
|
118
119
|
_this.isActiveInstance = false;
|
|
@@ -137,6 +138,9 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
137
138
|
_this.bridge.updateProps({
|
|
138
139
|
props: _this.pluginApi
|
|
139
140
|
});
|
|
141
|
+
if (!((_this$changes = _this.changes) != null && _this$changes.length)) {
|
|
142
|
+
_this.onChange();
|
|
143
|
+
}
|
|
140
144
|
}
|
|
141
145
|
};
|
|
142
146
|
this.loaded = () => true;
|
|
@@ -147,8 +151,31 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
147
151
|
return;
|
|
148
152
|
}
|
|
149
153
|
if (payload != null && payload.profile) {
|
|
150
|
-
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;
|
|
151
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
|
+
};
|
|
152
179
|
};
|
|
153
180
|
this.getExperienceSelectionMiddleware = ({
|
|
154
181
|
baseline,
|
|
@@ -169,7 +196,41 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
169
196
|
variantIndex: 0
|
|
170
197
|
};
|
|
171
198
|
}
|
|
172
|
-
|
|
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
|
|
173
234
|
if (!baselineComponent) {
|
|
174
235
|
return {
|
|
175
236
|
experience,
|
|
@@ -178,7 +239,6 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
178
239
|
};
|
|
179
240
|
}
|
|
180
241
|
const allVariants = [baseline, ...baselineComponent.variants];
|
|
181
|
-
const variantIndex = this.pluginApi.experienceVariantIndexes[experience.id];
|
|
182
242
|
if (allVariants.length <= variantIndex) {
|
|
183
243
|
return {
|
|
184
244
|
experience,
|
|
@@ -202,20 +262,47 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
202
262
|
};
|
|
203
263
|
};
|
|
204
264
|
this.onChange = () => {
|
|
205
|
-
var _window$ninetailed2;
|
|
206
265
|
logger.debug('Ninetailed Preview Plugin onChange pluginApi:', this.pluginApi);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
+
}
|
|
212
274
|
this.bridge.updateProps({
|
|
213
275
|
props: this.pluginApi
|
|
214
276
|
});
|
|
215
277
|
this.onChangeEmitter.invokeListeners();
|
|
216
278
|
};
|
|
217
|
-
this.onProfileChange = profile => {
|
|
279
|
+
this.onProfileChange = (profile, changes) => {
|
|
218
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
|
|
219
306
|
this.onChange();
|
|
220
307
|
};
|
|
221
308
|
this.setCredentials = ({
|
|
@@ -340,23 +427,44 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
340
427
|
}
|
|
341
428
|
const experience = this.experiences.find(experience => experience.id === experienceId);
|
|
342
429
|
if (!experience) {
|
|
343
|
-
logger.warn(`
|
|
430
|
+
logger.warn(`Cannot activate a variant for an unknown experience (id: ${experienceId})`);
|
|
344
431
|
return;
|
|
345
432
|
}
|
|
346
433
|
if (experience.audience && !this.activeAudiences.some(id => {
|
|
347
434
|
var _experience$audience4;
|
|
348
435
|
return id === ((_experience$audience4 = experience.audience) == null ? void 0 : _experience$audience4.id);
|
|
349
436
|
})) {
|
|
350
|
-
logger.warn(`
|
|
437
|
+
logger.warn(`Cannot activate a variant for an experience (id: ${experienceId}) which is not in the active audiences.`);
|
|
351
438
|
return;
|
|
352
439
|
}
|
|
353
440
|
const isValidIndex = experience.components.map(component => component.variants.length + 1).every(length => length > variantIndex);
|
|
354
441
|
if (!isValidIndex) {
|
|
355
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.`);
|
|
356
443
|
}
|
|
444
|
+
|
|
445
|
+
// Update the experience variant index
|
|
357
446
|
this.experienceVariantIndexOverwrites = Object.assign({}, this.experienceVariantIndexOverwrites, {
|
|
358
447
|
[experienceId]: variantIndex
|
|
359
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
|
|
360
468
|
this.onChange();
|
|
361
469
|
}
|
|
362
470
|
resetExperience(experienceId) {
|
|
@@ -376,6 +484,45 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
376
484
|
window.ninetailed.reset();
|
|
377
485
|
}
|
|
378
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
|
+
}
|
|
379
526
|
openExperienceEditor(experience) {
|
|
380
527
|
if (this.onOpenExperienceEditor && typeof this.onOpenExperienceEditor === 'function') return this.onOpenExperienceEditor(experience);
|
|
381
528
|
}
|
|
@@ -388,7 +535,7 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
388
535
|
get pluginApi() {
|
|
389
536
|
var _this$profile;
|
|
390
537
|
return {
|
|
391
|
-
version: "7.
|
|
538
|
+
version: "7.13.0-beta.1" ,
|
|
392
539
|
open: this.open.bind(this),
|
|
393
540
|
close: this.close.bind(this),
|
|
394
541
|
toggle: this.toggle.bind(this),
|
|
@@ -424,7 +571,9 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
424
571
|
activeAudiences: this.activeAudiences,
|
|
425
572
|
setExperienceVariant: this.setExperienceVariant.bind(this),
|
|
426
573
|
resetExperience: this.resetExperience.bind(this),
|
|
427
|
-
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
|
|
428
577
|
};
|
|
429
578
|
}
|
|
430
579
|
isKnownAudience(id) {
|
|
@@ -471,6 +620,35 @@ class NinetailedPreviewPlugin extends NinetailedPlugin {
|
|
|
471
620
|
audiences: this.activeAudiences
|
|
472
621
|
}));
|
|
473
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
|
+
}
|
|
474
652
|
}
|
|
475
653
|
|
|
476
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.1",
|
|
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.1",
|
|
19
|
+
"@ninetailed/experience.js": "7.13.0-beta.1",
|
|
20
|
+
"@ninetailed/experience.js-preview-bridge": "7.13.0-beta.1",
|
|
21
|
+
"@ninetailed/experience.js-plugin-analytics": "7.13.0-beta.1"
|
|
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 {};
|