@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 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
- const baselineComponent = experience.components.find(component => component.baseline.id === baseline.id);
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
- Object.assign({}, window.ninetailed, {
236
- plugins: Object.assign(Object.assign({}, (_b = window.ninetailed) === null || _b === void 0 ? void 0 : _b.plugins), {
237
- preview: this.windowApi
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(`You cannot active a variant for an unknown experience (id: ${experienceId})`);
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(`You cannot active a variant for an experience (id: ${experienceId}), which is not in the active audiences.`);
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.12.0" ,
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
- const baselineComponent = experience.components.find(component => component.baseline.id === baseline.id);
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
- Object.assign({}, window.ninetailed, {
207
- plugins: Object.assign({}, (_window$ninetailed2 = window.ninetailed) == null ? void 0 : _window$ninetailed2.plugins, {
208
- preview: this.windowApi
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(`You cannot active a variant for an unknown experience (id: ${experienceId})`);
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(`You cannot active a variant for an experience (id: ${experienceId}), which is not in the active audiences.`);
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.12.0" ,
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.12.0",
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.12.0",
19
- "@ninetailed/experience.js": "7.12.0",
20
- "@ninetailed/experience.js-preview-bridge": "7.12.0",
21
- "@ninetailed/experience.js-plugin-analytics": "7.12.0"
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 {};