@uniformdev/context 19.79.1-alpha.18 → 19.79.1-alpha.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -147,74 +147,183 @@ function computeAggregateDimension(primitiveScores, aggregateDimension, allAggre
147
147
  // src/manifest/constants.ts
148
148
  var ENR_SEPARATOR = "_";
149
149
 
150
- // src/manifest/signals/criteria/util/isNumberMatch.ts
151
- function isNumberMatch(lhs, match) {
152
- var _a;
153
- if (typeof lhs === "undefined" || lhs === null) {
154
- return false;
155
- }
156
- const lhsValue = Number(lhs);
157
- if (isNaN(lhsValue)) {
158
- return false;
159
- }
160
- switch ((_a = match == null ? void 0 : match.op) != null ? _a : "=") {
161
- case "=":
162
- return lhsValue === match.rhs;
163
- case "!=":
164
- return lhsValue !== match.rhs;
165
- case ">":
166
- return lhsValue > match.rhs;
167
- case "<":
168
- return lhsValue < match.rhs;
169
- default:
170
- console.warn(`Unknown match type ${match.op} is false.`);
171
- return false;
172
- }
173
- }
174
- function explainNumberMatch(lhs, match) {
175
- return `${lhs} ${explainNumberMatchCriteria(match)}`;
176
- }
177
- function explainNumberMatchCriteria(match) {
178
- return `${match.op} ${match.rhs}`;
179
- }
180
-
181
- // src/manifest/utils/getEnrichmentVectorKey.ts
182
- function getEnrichmentVectorKey(category, value) {
183
- return `${category}${ENR_SEPARATOR}${value}`;
184
- }
185
-
186
- // src/manifest/goals/evaluators/EnrichmentGoalEvaluator.ts
187
- var _goal, _id;
188
- var EnrichmentGoalEvaluator = class {
150
+ // src/manifest/goals/evaluators/SignalGoalEvaluator.ts
151
+ var _signal, _id;
152
+ var SignalGoalEvaluator = class {
189
153
  constructor(options) {
190
- __privateAdd(this, _goal, void 0);
154
+ __privateAdd(this, _signal, void 0);
191
155
  __privateAdd(this, _id, void 0);
192
- __privateSet(this, _goal, options.goal);
193
156
  __privateSet(this, _id, options.id);
157
+ __privateSet(this, _signal, options.signal);
194
158
  }
195
159
  get id() {
196
160
  return __privateGet(this, _id);
197
161
  }
198
- evaluate({ scores }) {
199
- const name = getEnrichmentVectorKey(__privateGet(this, _goal).cat, __privateGet(this, _goal).value);
200
- const score = scores == null ? void 0 : scores[name];
201
- if (typeof score !== "number") {
202
- return {
203
- triggered: false
204
- };
205
- }
206
- const isMatch = isNumberMatch(score, {
207
- op: __privateGet(this, _goal).op,
208
- rhs: __privateGet(this, _goal).score
209
- });
162
+ evaluate({ scores, quirks }) {
163
+ const score = scores == null ? void 0 : scores[__privateGet(this, _id)];
164
+ const key = `goal_${__privateGet(this, _id)}_triggered`;
165
+ const hasGoalTriggered = (quirks == null ? void 0 : quirks[key]) === "1";
210
166
  return {
211
- triggered: isMatch
167
+ key,
168
+ triggered: typeof score === "number" && !hasGoalTriggered
212
169
  };
213
170
  }
214
171
  };
215
- _goal = new WeakMap();
172
+ _signal = new WeakMap();
216
173
  _id = new WeakMap();
217
174
 
175
+ // src/manifest/signals/SignalInstance.ts
176
+ var _evaluator, _onLogMessage;
177
+ var SignalInstance = class {
178
+ constructor(data, evaluator, onLogMessage) {
179
+ __privateAdd(this, _evaluator, void 0);
180
+ __privateAdd(this, _onLogMessage, void 0);
181
+ __publicField(this, "signal");
182
+ this.signal = data;
183
+ __privateSet(this, _evaluator, evaluator);
184
+ __privateSet(this, _onLogMessage, onLogMessage);
185
+ }
186
+ /** Computes storage update commands to take based on a state update and the signal's criteria */
187
+ computeSignal(update, commands) {
188
+ const isAtCap = update.scores[this.signal.id] >= this.signal.cap;
189
+ if (isAtCap && this.signal.dur !== "t") {
190
+ return;
191
+ }
192
+ const criteriaMatchUpdate = __privateGet(this, _evaluator).evaluate(
193
+ update,
194
+ this.signal.crit,
195
+ commands,
196
+ this.signal,
197
+ __privateGet(this, _onLogMessage)
198
+ );
199
+ const scoreCommand = this.signal.dur === "s" || this.signal.dur === "t" ? "modscoreS" : "modscore";
200
+ if (!criteriaMatchUpdate.changed) {
201
+ return;
202
+ }
203
+ if (criteriaMatchUpdate.result) {
204
+ commands.push({
205
+ type: scoreCommand,
206
+ data: { dimension: this.signal.id, delta: this.signal.str }
207
+ });
208
+ } else if (this.signal.dur === "t") {
209
+ const sessionScore = update.visitor.sessionScores[this.signal.id];
210
+ if (sessionScore) {
211
+ commands.push({
212
+ type: scoreCommand,
213
+ data: { dimension: this.signal.id, delta: -sessionScore }
214
+ });
215
+ }
216
+ }
217
+ }
218
+ };
219
+ _evaluator = new WeakMap();
220
+ _onLogMessage = new WeakMap();
221
+
222
+ // src/manifest/utils/control.ts
223
+ var rollForControlGroup = (value) => {
224
+ let control = value;
225
+ if (control >= 1) {
226
+ control = control / 100;
227
+ }
228
+ return Math.random() < control;
229
+ };
230
+
231
+ // src/manifest/ManifestInstance.ts
232
+ var _mf, _signalInstances, _goalEvaluators, _onLogMessage2;
233
+ var ManifestInstance = class {
234
+ constructor({
235
+ manifest,
236
+ evaluator = new GroupCriteriaEvaluator({}),
237
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
238
+ onLogMessage = () => {
239
+ }
240
+ }) {
241
+ __publicField(this, "data");
242
+ __privateAdd(this, _mf, void 0);
243
+ __privateAdd(this, _signalInstances, void 0);
244
+ __privateAdd(this, _goalEvaluators, []);
245
+ __privateAdd(this, _onLogMessage2, void 0);
246
+ var _a, _b, _c, _d, _e;
247
+ __privateSet(this, _mf, (_a = manifest.project) != null ? _a : {});
248
+ this.data = manifest;
249
+ __privateSet(this, _signalInstances, Object.entries((_c = (_b = __privateGet(this, _mf).pz) == null ? void 0 : _b.sig) != null ? _c : []).map(
250
+ ([id, signal]) => new SignalInstance({ ...signal, id }, evaluator, onLogMessage)
251
+ ));
252
+ Object.entries((_e = (_d = __privateGet(this, _mf).pz) == null ? void 0 : _d.sig) != null ? _e : []).forEach(([id, signal]) => {
253
+ if (signal.conversion) {
254
+ __privateGet(this, _goalEvaluators).push(new SignalGoalEvaluator({ id, signal }));
255
+ }
256
+ });
257
+ __privateSet(this, _onLogMessage2, onLogMessage);
258
+ }
259
+ rollForControlGroup() {
260
+ var _a;
261
+ return rollForControlGroup(((_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.control) || 0);
262
+ }
263
+ getTest(name) {
264
+ var _a;
265
+ return (_a = __privateGet(this, _mf).test) == null ? void 0 : _a[name];
266
+ }
267
+ computeSignals(update) {
268
+ const commands = [];
269
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "GROUP"]);
270
+ try {
271
+ __privateGet(this, _signalInstances).forEach((signal) => {
272
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "GROUP", signal.signal]);
273
+ try {
274
+ signal.computeSignal(update, commands);
275
+ } finally {
276
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "ENDGROUP"]);
277
+ }
278
+ });
279
+ } finally {
280
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "ENDGROUP"]);
281
+ }
282
+ return commands;
283
+ }
284
+ computeGoals(data) {
285
+ const commands = [];
286
+ __privateGet(this, _goalEvaluators).forEach((evaluator) => {
287
+ const { triggered, key } = evaluator.evaluate(data);
288
+ if (triggered) {
289
+ commands.push({
290
+ type: "setgoal",
291
+ data: {
292
+ goal: evaluator.id
293
+ }
294
+ });
295
+ commands.push({
296
+ type: "setquirk",
297
+ data: {
298
+ key,
299
+ value: "1"
300
+ }
301
+ });
302
+ }
303
+ });
304
+ return commands;
305
+ }
306
+ /**
307
+ * Computes aggregated scores based on other dimensions
308
+ */
309
+ computeAggregateDimensions(primitiveScores) {
310
+ var _a, _b;
311
+ return computeAggregateDimensions(primitiveScores, (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.agg) != null ? _b : {});
312
+ }
313
+ getDimensionByKey(scoreKey) {
314
+ var _a, _b, _c, _d;
315
+ const enrichmentIndex = scoreKey.indexOf(ENR_SEPARATOR);
316
+ if (enrichmentIndex <= 0) {
317
+ return (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.sig) == null ? void 0 : _b[scoreKey];
318
+ }
319
+ return (_d = (_c = __privateGet(this, _mf).pz) == null ? void 0 : _c.enr) == null ? void 0 : _d[scoreKey.substring(0, enrichmentIndex)];
320
+ }
321
+ };
322
+ _mf = new WeakMap();
323
+ _signalInstances = new WeakMap();
324
+ _goalEvaluators = new WeakMap();
325
+ _onLogMessage2 = new WeakMap();
326
+
218
327
  // src/manifest/signals/criteria/evaluators/cookieEvaluator.ts
219
328
  var import_lite = require("dequal/lite");
220
329
 
@@ -332,6 +441,42 @@ var eventEvaluator = ({ update, criteria, onLogMessage }) => {
332
441
  return finalResult;
333
442
  };
334
443
 
444
+ // src/manifest/utils/getEnrichmentVectorKey.ts
445
+ function getEnrichmentVectorKey(category, value) {
446
+ return `${category}${ENR_SEPARATOR}${value}`;
447
+ }
448
+
449
+ // src/manifest/signals/criteria/util/isNumberMatch.ts
450
+ function isNumberMatch(lhs, match) {
451
+ var _a;
452
+ if (typeof lhs === "undefined" || lhs === null) {
453
+ return false;
454
+ }
455
+ const lhsValue = Number(lhs);
456
+ if (isNaN(lhsValue)) {
457
+ return false;
458
+ }
459
+ switch ((_a = match == null ? void 0 : match.op) != null ? _a : "=") {
460
+ case "=":
461
+ return lhsValue === match.rhs;
462
+ case "!=":
463
+ return lhsValue !== match.rhs;
464
+ case ">":
465
+ return lhsValue > match.rhs;
466
+ case "<":
467
+ return lhsValue < match.rhs;
468
+ default:
469
+ console.warn(`Unknown match type ${match.op} is false.`);
470
+ return false;
471
+ }
472
+ }
473
+ function explainNumberMatch(lhs, match) {
474
+ return `${lhs} ${explainNumberMatchCriteria(match)}`;
475
+ }
476
+ function explainNumberMatchCriteria(match) {
477
+ return `${match.op} ${match.rhs}`;
478
+ }
479
+
335
480
  // src/manifest/signals/criteria/evaluators/pageViewCountEvaluator.ts
336
481
  var pageViewCountDimension = getEnrichmentVectorKey("$pvc", "v");
337
482
  var pageViewCountEvaluator = ({ update, criteria, commands, onLogMessage }) => {
@@ -461,236 +606,6 @@ var GroupCriteriaEvaluator = class {
461
606
  };
462
607
  _evaluators = new WeakMap();
463
608
 
464
- // src/manifest/goals/evaluators/QuirkGoalEvaluator.ts
465
- var _goal2, _id2;
466
- var QuirkGoalEvaluator = class {
467
- constructor(options) {
468
- __privateAdd(this, _goal2, void 0);
469
- __privateAdd(this, _id2, void 0);
470
- __privateSet(this, _goal2, options.goal);
471
- __privateSet(this, _id2, options.id);
472
- }
473
- get id() {
474
- return __privateGet(this, _id2);
475
- }
476
- evaluate({ quirks }) {
477
- const quirkValue = quirks == null ? void 0 : quirks[__privateGet(this, _goal2).id];
478
- if (typeof quirkValue !== "string") {
479
- return {
480
- triggered: false
481
- };
482
- }
483
- const isMatch = isStringMatch(quirkValue, {
484
- op: __privateGet(this, _goal2).op,
485
- rhs: __privateGet(this, _goal2).value,
486
- cs: __privateGet(this, _goal2).cs
487
- });
488
- return {
489
- triggered: isMatch
490
- };
491
- }
492
- };
493
- _goal2 = new WeakMap();
494
- _id2 = new WeakMap();
495
-
496
- // src/manifest/goals/evaluators/SignalGoalEvaluator.ts
497
- var _goal3, _id3;
498
- var SignalGoalEvaluator = class {
499
- constructor(options) {
500
- __privateAdd(this, _goal3, void 0);
501
- __privateAdd(this, _id3, void 0);
502
- __privateSet(this, _id3, options.id);
503
- __privateSet(this, _goal3, options.goal);
504
- }
505
- get id() {
506
- return __privateGet(this, _id3);
507
- }
508
- evaluate({ scores }) {
509
- const score = scores == null ? void 0 : scores[__privateGet(this, _goal3).id];
510
- if (typeof score !== "number") {
511
- return {
512
- triggered: false
513
- };
514
- }
515
- const isMatch = isNumberMatch(score, {
516
- op: __privateGet(this, _goal3).op,
517
- rhs: __privateGet(this, _goal3).score
518
- });
519
- return {
520
- triggered: isMatch
521
- };
522
- }
523
- };
524
- _goal3 = new WeakMap();
525
- _id3 = new WeakMap();
526
-
527
- // src/manifest/goals/evaluators/index.ts
528
- var resolveGoalEvaluator = ({ id, goal }) => {
529
- let evaluator;
530
- if (goal.type === "sig") {
531
- evaluator = new SignalGoalEvaluator({
532
- id,
533
- goal
534
- });
535
- } else if (goal.type === "enr") {
536
- evaluator = new EnrichmentGoalEvaluator({
537
- id,
538
- goal
539
- });
540
- } else if (goal.type === "qrk") {
541
- evaluator = new QuirkGoalEvaluator({
542
- id,
543
- goal
544
- });
545
- }
546
- return evaluator;
547
- };
548
-
549
- // src/manifest/signals/SignalInstance.ts
550
- var _evaluator, _onLogMessage;
551
- var SignalInstance = class {
552
- constructor(data, evaluator, onLogMessage) {
553
- __privateAdd(this, _evaluator, void 0);
554
- __privateAdd(this, _onLogMessage, void 0);
555
- __publicField(this, "signal");
556
- this.signal = data;
557
- __privateSet(this, _evaluator, evaluator);
558
- __privateSet(this, _onLogMessage, onLogMessage);
559
- }
560
- /** Computes storage update commands to take based on a state update and the signal's criteria */
561
- computeSignal(update, commands) {
562
- const isAtCap = update.scores[this.signal.id] >= this.signal.cap;
563
- if (isAtCap && this.signal.dur !== "t") {
564
- return;
565
- }
566
- const criteriaMatchUpdate = __privateGet(this, _evaluator).evaluate(
567
- update,
568
- this.signal.crit,
569
- commands,
570
- this.signal,
571
- __privateGet(this, _onLogMessage)
572
- );
573
- const scoreCommand = this.signal.dur === "s" || this.signal.dur === "t" ? "modscoreS" : "modscore";
574
- if (!criteriaMatchUpdate.changed) {
575
- return;
576
- }
577
- if (criteriaMatchUpdate.result) {
578
- commands.push({
579
- type: scoreCommand,
580
- data: { dimension: this.signal.id, delta: this.signal.str }
581
- });
582
- } else if (this.signal.dur === "t") {
583
- const sessionScore = update.visitor.sessionScores[this.signal.id];
584
- if (sessionScore) {
585
- commands.push({
586
- type: scoreCommand,
587
- data: { dimension: this.signal.id, delta: -sessionScore }
588
- });
589
- }
590
- }
591
- }
592
- };
593
- _evaluator = new WeakMap();
594
- _onLogMessage = new WeakMap();
595
-
596
- // src/manifest/ManifestInstance.ts
597
- var _mf, _signalInstances, _goalEvaluators, _onLogMessage2;
598
- var ManifestInstance = class {
599
- constructor({
600
- manifest,
601
- evaluator = new GroupCriteriaEvaluator({}),
602
- // eslint-disable-next-line @typescript-eslint/no-empty-function
603
- onLogMessage = () => {
604
- }
605
- }) {
606
- __publicField(this, "data");
607
- __privateAdd(this, _mf, void 0);
608
- __privateAdd(this, _signalInstances, void 0);
609
- __privateAdd(this, _goalEvaluators, void 0);
610
- __privateAdd(this, _onLogMessage2, void 0);
611
- var _a, _b, _c, _d;
612
- __privateSet(this, _mf, (_a = manifest.project) != null ? _a : {});
613
- this.data = manifest;
614
- __privateSet(this, _signalInstances, Object.entries((_c = (_b = __privateGet(this, _mf).pz) == null ? void 0 : _b.sig) != null ? _c : []).map(
615
- ([id, signal]) => new SignalInstance({ ...signal, id }, evaluator, onLogMessage)
616
- ));
617
- __privateSet(this, _goalEvaluators, Object.entries((_d = __privateGet(this, _mf).goal) != null ? _d : {}).map(([id, goal]) => {
618
- const evaluator2 = resolveGoalEvaluator({
619
- id,
620
- goal
621
- });
622
- if (!evaluator2) {
623
- console.warn(`Unable to resolve goal evaluator for goal ${id}`);
624
- }
625
- return evaluator2;
626
- }).filter((evaluator2) => !!evaluator2));
627
- __privateSet(this, _onLogMessage2, onLogMessage);
628
- }
629
- rollForControlGroup() {
630
- var _a, _b;
631
- let control = (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.control) != null ? _b : 0;
632
- if (control >= 1) {
633
- control = control / 100;
634
- }
635
- return Math.random() < control;
636
- }
637
- getTest(name) {
638
- var _a;
639
- return (_a = __privateGet(this, _mf).test) == null ? void 0 : _a[name];
640
- }
641
- computeSignals(update) {
642
- const commands = [];
643
- __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "GROUP"]);
644
- try {
645
- __privateGet(this, _signalInstances).forEach((signal) => {
646
- __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "GROUP", signal.signal]);
647
- try {
648
- signal.computeSignal(update, commands);
649
- } finally {
650
- __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "ENDGROUP"]);
651
- }
652
- });
653
- } finally {
654
- __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "ENDGROUP"]);
655
- }
656
- return commands;
657
- }
658
- computeGoals(data) {
659
- const commands = [];
660
- __privateGet(this, _goalEvaluators).forEach((evaluator) => {
661
- const { triggered } = evaluator.evaluate(data);
662
- if (triggered) {
663
- commands.push({
664
- type: "setgoal",
665
- data: {
666
- goal: evaluator.id
667
- }
668
- });
669
- }
670
- });
671
- return commands;
672
- }
673
- /**
674
- * Computes aggregated scores based on other dimensions
675
- */
676
- computeAggregateDimensions(primitiveScores) {
677
- var _a, _b;
678
- return computeAggregateDimensions(primitiveScores, (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.agg) != null ? _b : {});
679
- }
680
- getDimensionByKey(scoreKey) {
681
- var _a, _b, _c, _d;
682
- const enrichmentIndex = scoreKey.indexOf(ENR_SEPARATOR);
683
- if (enrichmentIndex <= 0) {
684
- return (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.sig) == null ? void 0 : _b[scoreKey];
685
- }
686
- return (_d = (_c = __privateGet(this, _mf).pz) == null ? void 0 : _c.enr) == null ? void 0 : _d[scoreKey.substring(0, enrichmentIndex)];
687
- }
688
- };
689
- _mf = new WeakMap();
690
- _signalInstances = new WeakMap();
691
- _goalEvaluators = new WeakMap();
692
- _onLogMessage2 = new WeakMap();
693
-
694
609
  // src/placement/criteria/evaluateVariantMatch.ts
695
610
  function evaluateVariantMatch(variantId, match, vec, onLogMessage) {
696
611
  onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variantId, op: match == null ? void 0 : match.op }]);
@@ -714,7 +629,30 @@ function evaluateDimensionMatch(crit, vec, onLogMessage) {
714
629
  var _a, _b;
715
630
  const { op, l: lhs } = crit;
716
631
  const lhsScore = (_a = vec[lhs]) != null ? _a : 0;
717
- if (op === "+") {
632
+ if (op === "^") {
633
+ const [cat] = lhs.split(ENR_SEPARATOR);
634
+ let topVectorName = void 0;
635
+ let topScore = 0;
636
+ Object.keys(vec).forEach((vectorName) => {
637
+ if (vectorName.startsWith(`${cat}${ENR_SEPARATOR}`)) {
638
+ const score = vec[vectorName];
639
+ if (score > topScore) {
640
+ topVectorName = vectorName;
641
+ topScore = score;
642
+ }
643
+ }
644
+ });
645
+ const result = topVectorName === lhs;
646
+ onLogMessage == null ? void 0 : onLogMessage([
647
+ "info",
648
+ 302,
649
+ {
650
+ matched: result,
651
+ description: `${crit.l} has the highest score in the category`
652
+ }
653
+ ]);
654
+ return result;
655
+ } else if (op === "+") {
718
656
  const result = Math.max(...Object.values(vec)) === lhsScore && lhsScore > 0;
719
657
  onLogMessage == null ? void 0 : onLogMessage([
720
658
  "info",
@@ -801,31 +739,70 @@ function personalizeVariations({
801
739
  take = 1,
802
740
  onLogMessage
803
741
  }) {
804
- var _a, _b, _c;
742
+ var _a, _b, _c, _d;
805
743
  onLogMessage == null ? void 0 : onLogMessage(["info", 300, "GROUP", { name, take }]);
806
744
  try {
807
745
  const control = (_a = context.storage.data.controlGroup) != null ? _a : false;
808
746
  const results = [];
809
747
  let personalized = false;
810
748
  const scores = context.scores;
749
+ let index = 0;
750
+ const defaultVariants = [];
811
751
  for (const variant of variations) {
752
+ if (!((_b = variant.pz) == null ? void 0 : _b.crit.length)) {
753
+ defaultVariants.push(variant);
754
+ }
755
+ }
756
+ for (const variant of variations) {
757
+ const currentIndex = index++;
812
758
  if (results.length === take) {
813
759
  break;
814
760
  }
815
- if (!((_b = variant.pz) == null ? void 0 : _b.crit.length)) {
816
- onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variant.id, op: (_c = variant.pz) == null ? void 0 : _c.op }]);
761
+ if (!((_c = variant.pz) == null ? void 0 : _c.crit.length)) {
762
+ onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variant.id, op: (_d = variant.pz) == null ? void 0 : _d.op }]);
817
763
  onLogMessage == null ? void 0 : onLogMessage(["info", 302, { matched: true, description: "default variation" }]);
818
764
  onLogMessage == null ? void 0 : onLogMessage(["info", 303, true]);
819
765
  onLogMessage == null ? void 0 : onLogMessage(["info", 301, "ENDGROUP"]);
820
- results.push(variant);
766
+ results.push({
767
+ ...variant,
768
+ control: false
769
+ });
821
770
  continue;
822
771
  }
823
772
  if (control) {
824
773
  continue;
825
774
  }
826
775
  if (evaluateVariantMatch(variant.id, variant.pz, scores, onLogMessage)) {
827
- personalized = true;
828
- results.push(variant);
776
+ let variantToAdd = variant;
777
+ let isControl = false;
778
+ const isDefault = defaultVariants.find((v) => v.id === variant.id);
779
+ if (take === 1 && !isDefault && defaultVariants.length && typeof variant.pz.control === "number") {
780
+ isControl = context.getPersonalizeVariantControl(name, currentIndex);
781
+ if (typeof isControl === "undefined") {
782
+ isControl = rollForControlGroup(variant.pz.control);
783
+ context.storage.updateData([
784
+ {
785
+ type: "setpersonalizecontrol",
786
+ data: {
787
+ personlizationName: name,
788
+ index: currentIndex,
789
+ control: isControl
790
+ }
791
+ }
792
+ ]);
793
+ }
794
+ if (isControl) {
795
+ variantToAdd = {
796
+ ...defaultVariants[0],
797
+ id: variant.id
798
+ };
799
+ }
800
+ }
801
+ personalized = personalized || typeof variantToAdd.pz !== "undefined";
802
+ results.push({
803
+ ...variantToAdd,
804
+ control: isControl
805
+ });
829
806
  }
830
807
  }
831
808
  return {
@@ -1072,16 +1049,19 @@ function parseScoreCookie(cookieValue) {
1072
1049
  if (!cookieValue)
1073
1050
  return;
1074
1051
  const types = cookieValue.split(TYPE_SEP);
1075
- if (types.length > 3)
1052
+ if (types.length > 5)
1076
1053
  return;
1077
- const [abTestData, sessionScores, visitorScores] = types;
1078
- return {
1054
+ const [abTestData, sessionScores, visitorScores, controlGroup, personalizeVariants] = types;
1055
+ const data = {
1079
1056
  // this is true since we're reading a cookie, which wouldn't exist if consent wasn't given
1080
1057
  consent: true,
1081
1058
  sessionScores: decodeCookieType(parseCookieType(sessionScores)),
1082
1059
  scores: decodeCookieType(parseCookieType(visitorScores)),
1083
- tests: parseCookieType(abTestData)
1060
+ tests: parseCookieType(abTestData),
1061
+ controlGroup: controlGroup === "1",
1062
+ personalizeVariants: decodePersonalizeVariants(personalizeVariants)
1084
1063
  };
1064
+ return data;
1085
1065
  }
1086
1066
  function parseCookieType(type) {
1087
1067
  if (!type) {
@@ -1105,9 +1085,48 @@ function serializeCookie(data) {
1105
1085
  return [
1106
1086
  serializeCookieType(data.tests),
1107
1087
  serializeCookieType(encodeCookieType(data.sessionScores)),
1108
- serializeCookieType(encodeCookieType(data.scores))
1088
+ serializeCookieType(encodeCookieType(data.scores)),
1089
+ data.controlGroup ? "1" : "0",
1090
+ serializePersonalizeVariants(data)
1109
1091
  ].join(TYPE_SEP);
1110
1092
  }
1093
+ function serializePersonalizeVariants({
1094
+ personalizeVariants
1095
+ }) {
1096
+ const data = {};
1097
+ if (typeof personalizeVariants === "object") {
1098
+ Object.keys(personalizeVariants).forEach((personalizationName) => {
1099
+ const results = [];
1100
+ const variants = personalizeVariants[personalizationName];
1101
+ variants.forEach((variant) => {
1102
+ results.push(`${variant.index}:${variant.control ? "1" : "0"}`);
1103
+ });
1104
+ data[personalizationName] = results.join(",");
1105
+ });
1106
+ }
1107
+ const serialized = serializeCookieType(data);
1108
+ return serialized;
1109
+ }
1110
+ function decodePersonalizeVariants(data) {
1111
+ const parsed = parseCookieType(data);
1112
+ const keys = Object.keys(parsed);
1113
+ if (!keys.length) {
1114
+ return void 0;
1115
+ }
1116
+ const results = {};
1117
+ Object.keys(parsed).forEach((k) => {
1118
+ const variants = parsed[k].split(",");
1119
+ const key = decodeURIComponent(k);
1120
+ results[key] = variants.map((variant) => {
1121
+ const [index, control] = variant.split(":");
1122
+ return {
1123
+ index: parseInt(index, 10),
1124
+ control: control === "1"
1125
+ };
1126
+ });
1127
+ });
1128
+ return results;
1129
+ }
1111
1130
  function encodeCookieType(type) {
1112
1131
  return Object.entries(type).reduce((acc, [key, value]) => {
1113
1132
  acc[key] = ntob(value);
@@ -1196,7 +1215,8 @@ var emptyVisitorData = () => ({
1196
1215
  tests: {},
1197
1216
  goals: {},
1198
1217
  consent: false,
1199
- controlGroup: false
1218
+ controlGroup: false,
1219
+ personalizeVariants: {}
1200
1220
  });
1201
1221
 
1202
1222
  // src/storage/VisitorDataStore.ts
@@ -1245,6 +1265,25 @@ function applyCommandsToData(commands, state, inControlGroup) {
1245
1265
  case "setcontrol":
1246
1266
  newData.controlGroup = command.data;
1247
1267
  break;
1268
+ case "setpersonalizecontrol":
1269
+ if (!newData.personalizeVariants) {
1270
+ newData.personalizeVariants = {};
1271
+ }
1272
+ if (!newData.personalizeVariants[command.data.personlizationName]) {
1273
+ newData.personalizeVariants[command.data.personlizationName] = [];
1274
+ }
1275
+ const existingDef = newData.personalizeVariants[command.data.personlizationName].find(
1276
+ (i) => i.index === command.data.index
1277
+ );
1278
+ if (!existingDef) {
1279
+ newData.personalizeVariants[command.data.personlizationName].push({
1280
+ index: command.data.index,
1281
+ control: command.data.control
1282
+ });
1283
+ } else {
1284
+ console.warn("Overwriting existing control group definition is not allowed");
1285
+ }
1286
+ break;
1248
1287
  case "setgoal":
1249
1288
  newData.goals = (_c = newData.goals) != null ? _c : {};
1250
1289
  newData.goals[command.data.goal] = true;
@@ -1627,7 +1666,7 @@ var Context = class {
1627
1666
  * will NOT result in a recomputation of signal state.
1628
1667
  */
1629
1668
  async update(newData) {
1630
- var _a, _b, _c;
1669
+ var _a, _b, _c, _d;
1631
1670
  const commands = [];
1632
1671
  const newServerSideTests = {};
1633
1672
  if ((_a = __privateGet(this, _serverTransitionState)) == null ? void 0 : _a.quirks) {
@@ -1657,6 +1696,17 @@ var Context = class {
1657
1696
  );
1658
1697
  }
1659
1698
  }
1699
+ if ((_c = __privateGet(this, _serverTransitionState)) == null ? void 0 : _c.personalizeVariants) {
1700
+ Object.keys(__privateGet(this, _serverTransitionState).personalizeVariants).forEach((personalizationName) => {
1701
+ const variants = __privateGet(this, _serverTransitionState).personalizeVariants[personalizationName];
1702
+ variants.forEach((e) => {
1703
+ commands.push({
1704
+ type: "setpersonalizecontrol",
1705
+ data: { personlizationName: personalizationName, index: e.index, control: e.control }
1706
+ });
1707
+ });
1708
+ });
1709
+ }
1660
1710
  try {
1661
1711
  __privateGet(this, _mitt3).emit("log", [
1662
1712
  "info",
@@ -1666,7 +1716,7 @@ var Context = class {
1666
1716
  ...newData,
1667
1717
  // need to convert url to string so it can be json serialized
1668
1718
  // to go over postMessage to chrome extension
1669
- url: (_c = newData.url) == null ? void 0 : _c.toString()
1719
+ url: (_d = newData.url) == null ? void 0 : _d.toString()
1670
1720
  }
1671
1721
  ]);
1672
1722
  if (newData.quirks) {
@@ -1757,6 +1807,13 @@ var Context = class {
1757
1807
  }
1758
1808
  ]);
1759
1809
  }
1810
+ getPersonalizeVariantControl(name, index) {
1811
+ var _a, _b, _c;
1812
+ const source = (_b = (_a = __privateGet(this, _serverTransitionState)) == null ? void 0 : _a.personalizeVariants) != null ? _b : this.storage.data.personalizeVariants;
1813
+ const variants = (_c = source == null ? void 0 : source[name]) != null ? _c : [];
1814
+ const variant = variants.find((v) => v.index === index);
1815
+ return variant == null ? void 0 : variant.control;
1816
+ }
1760
1817
  /**
1761
1818
  * Writes a message to the Context log sink.
1762
1819
  * Used by Uniform internal SDK; not intended for public use.
@@ -1799,10 +1856,10 @@ var Context = class {
1799
1856
  const previousPlacement = __privateGet(this, _pzCache)[options.name];
1800
1857
  const eventData = {
1801
1858
  name: options.name,
1802
- variantIds: value.variations.map((variation) => {
1803
- var _a;
1804
- return (_a = variation.id) != null ? _a : "Unknown";
1805
- }),
1859
+ variantIds: value.variations.map((variation) => ({
1860
+ id: variation.id || "Unknown",
1861
+ control: variation.control
1862
+ })),
1806
1863
  control: this.storage.data.controlGroup,
1807
1864
  changed: true
1808
1865
  };
@@ -1839,7 +1896,8 @@ var Context = class {
1839
1896
  const transitionState = {
1840
1897
  quirks: this.storage.data.quirks,
1841
1898
  ssv: __privateGet(this, _scores),
1842
- tests: {}
1899
+ tests: {},
1900
+ personalizeVariants: this.storage.data.personalizeVariants
1843
1901
  };
1844
1902
  const allTests = this.storage.data.tests;
1845
1903
  Object.entries(allTests).map(([testName, testValue]) => {
@@ -2523,7 +2581,10 @@ var createInsightsClient = ({ endpoint }) => {
2523
2581
  };
2524
2582
  return sendMessage(message);
2525
2583
  },
2526
- testResult: (options) => {
2584
+ testResult: async (options) => {
2585
+ if (!options.variantAssigned) {
2586
+ return;
2587
+ }
2527
2588
  const message = {
2528
2589
  action: "test_result",
2529
2590
  version: "1",
@@ -2539,7 +2600,10 @@ var createInsightsClient = ({ endpoint }) => {
2539
2600
  return sendMessage(message);
2540
2601
  },
2541
2602
  personalizationResult: async (options) => {
2542
- const messages = options.variantIds.map((variantId) => {
2603
+ if (!options.changed) {
2604
+ return;
2605
+ }
2606
+ const messages = options.variantIds.map((variant) => {
2543
2607
  const message = {
2544
2608
  action: "personalization_result",
2545
2609
  version: "1",
@@ -2550,8 +2614,8 @@ var createInsightsClient = ({ endpoint }) => {
2550
2614
  payload: {
2551
2615
  ...getBasePayload(),
2552
2616
  name: options.name,
2553
- variantId,
2554
- control: options.control,
2617
+ variantId: variant.id,
2618
+ control: variant.control || options.control,
2555
2619
  changed: options.changed
2556
2620
  }
2557
2621
  };
@@ -2632,6 +2696,7 @@ var createInsights = ({
2632
2696
  const storage = createInsightsStorage();
2633
2697
  let storageData = void 0;
2634
2698
  let pageId = generatePageId();
2699
+ let previousUrl = void 0;
2635
2700
  return {
2636
2701
  init: () => {
2637
2702
  storageData = storage.get();
@@ -2659,6 +2724,11 @@ var createInsights = ({
2659
2724
  console.error("Insights not initialized");
2660
2725
  return;
2661
2726
  }
2727
+ if (previousUrl === window.location.href) {
2728
+ return;
2729
+ }
2730
+ previousUrl = window.location.href;
2731
+ pageId = generatePageId();
2662
2732
  client.pageHit({
2663
2733
  visitorId: storageData.visitorId,
2664
2734
  sessionId: storageData.sessionId,
@@ -2676,7 +2746,6 @@ var createInsights = ({
2676
2746
  sessionId: storageData.sessionId,
2677
2747
  pageId
2678
2748
  });
2679
- pageId = generatePageId();
2680
2749
  },
2681
2750
  personalizationResult: (result) => {
2682
2751
  if (!storageData) {