@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.esm.js CHANGED
@@ -76,74 +76,183 @@ function computeAggregateDimension(primitiveScores, aggregateDimension, allAggre
76
76
  // src/manifest/constants.ts
77
77
  var ENR_SEPARATOR = "_";
78
78
 
79
- // src/manifest/signals/criteria/util/isNumberMatch.ts
80
- function isNumberMatch(lhs, match) {
81
- var _a;
82
- if (typeof lhs === "undefined" || lhs === null) {
83
- return false;
84
- }
85
- const lhsValue = Number(lhs);
86
- if (isNaN(lhsValue)) {
87
- return false;
88
- }
89
- switch ((_a = match == null ? void 0 : match.op) != null ? _a : "=") {
90
- case "=":
91
- return lhsValue === match.rhs;
92
- case "!=":
93
- return lhsValue !== match.rhs;
94
- case ">":
95
- return lhsValue > match.rhs;
96
- case "<":
97
- return lhsValue < match.rhs;
98
- default:
99
- console.warn(`Unknown match type ${match.op} is false.`);
100
- return false;
101
- }
102
- }
103
- function explainNumberMatch(lhs, match) {
104
- return `${lhs} ${explainNumberMatchCriteria(match)}`;
105
- }
106
- function explainNumberMatchCriteria(match) {
107
- return `${match.op} ${match.rhs}`;
108
- }
109
-
110
- // src/manifest/utils/getEnrichmentVectorKey.ts
111
- function getEnrichmentVectorKey(category, value) {
112
- return `${category}${ENR_SEPARATOR}${value}`;
113
- }
114
-
115
- // src/manifest/goals/evaluators/EnrichmentGoalEvaluator.ts
116
- var _goal, _id;
117
- var EnrichmentGoalEvaluator = class {
79
+ // src/manifest/goals/evaluators/SignalGoalEvaluator.ts
80
+ var _signal, _id;
81
+ var SignalGoalEvaluator = class {
118
82
  constructor(options) {
119
- __privateAdd(this, _goal, void 0);
83
+ __privateAdd(this, _signal, void 0);
120
84
  __privateAdd(this, _id, void 0);
121
- __privateSet(this, _goal, options.goal);
122
85
  __privateSet(this, _id, options.id);
86
+ __privateSet(this, _signal, options.signal);
123
87
  }
124
88
  get id() {
125
89
  return __privateGet(this, _id);
126
90
  }
127
- evaluate({ scores }) {
128
- const name = getEnrichmentVectorKey(__privateGet(this, _goal).cat, __privateGet(this, _goal).value);
129
- const score = scores == null ? void 0 : scores[name];
130
- if (typeof score !== "number") {
131
- return {
132
- triggered: false
133
- };
134
- }
135
- const isMatch = isNumberMatch(score, {
136
- op: __privateGet(this, _goal).op,
137
- rhs: __privateGet(this, _goal).score
138
- });
91
+ evaluate({ scores, quirks }) {
92
+ const score = scores == null ? void 0 : scores[__privateGet(this, _id)];
93
+ const key = `goal_${__privateGet(this, _id)}_triggered`;
94
+ const hasGoalTriggered = (quirks == null ? void 0 : quirks[key]) === "1";
139
95
  return {
140
- triggered: isMatch
96
+ key,
97
+ triggered: typeof score === "number" && !hasGoalTriggered
141
98
  };
142
99
  }
143
100
  };
144
- _goal = new WeakMap();
101
+ _signal = new WeakMap();
145
102
  _id = new WeakMap();
146
103
 
104
+ // src/manifest/signals/SignalInstance.ts
105
+ var _evaluator, _onLogMessage;
106
+ var SignalInstance = class {
107
+ constructor(data, evaluator, onLogMessage) {
108
+ __privateAdd(this, _evaluator, void 0);
109
+ __privateAdd(this, _onLogMessage, void 0);
110
+ __publicField(this, "signal");
111
+ this.signal = data;
112
+ __privateSet(this, _evaluator, evaluator);
113
+ __privateSet(this, _onLogMessage, onLogMessage);
114
+ }
115
+ /** Computes storage update commands to take based on a state update and the signal's criteria */
116
+ computeSignal(update, commands) {
117
+ const isAtCap = update.scores[this.signal.id] >= this.signal.cap;
118
+ if (isAtCap && this.signal.dur !== "t") {
119
+ return;
120
+ }
121
+ const criteriaMatchUpdate = __privateGet(this, _evaluator).evaluate(
122
+ update,
123
+ this.signal.crit,
124
+ commands,
125
+ this.signal,
126
+ __privateGet(this, _onLogMessage)
127
+ );
128
+ const scoreCommand = this.signal.dur === "s" || this.signal.dur === "t" ? "modscoreS" : "modscore";
129
+ if (!criteriaMatchUpdate.changed) {
130
+ return;
131
+ }
132
+ if (criteriaMatchUpdate.result) {
133
+ commands.push({
134
+ type: scoreCommand,
135
+ data: { dimension: this.signal.id, delta: this.signal.str }
136
+ });
137
+ } else if (this.signal.dur === "t") {
138
+ const sessionScore = update.visitor.sessionScores[this.signal.id];
139
+ if (sessionScore) {
140
+ commands.push({
141
+ type: scoreCommand,
142
+ data: { dimension: this.signal.id, delta: -sessionScore }
143
+ });
144
+ }
145
+ }
146
+ }
147
+ };
148
+ _evaluator = new WeakMap();
149
+ _onLogMessage = new WeakMap();
150
+
151
+ // src/manifest/utils/control.ts
152
+ var rollForControlGroup = (value) => {
153
+ let control = value;
154
+ if (control >= 1) {
155
+ control = control / 100;
156
+ }
157
+ return Math.random() < control;
158
+ };
159
+
160
+ // src/manifest/ManifestInstance.ts
161
+ var _mf, _signalInstances, _goalEvaluators, _onLogMessage2;
162
+ var ManifestInstance = class {
163
+ constructor({
164
+ manifest,
165
+ evaluator = new GroupCriteriaEvaluator({}),
166
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
167
+ onLogMessage = () => {
168
+ }
169
+ }) {
170
+ __publicField(this, "data");
171
+ __privateAdd(this, _mf, void 0);
172
+ __privateAdd(this, _signalInstances, void 0);
173
+ __privateAdd(this, _goalEvaluators, []);
174
+ __privateAdd(this, _onLogMessage2, void 0);
175
+ var _a, _b, _c, _d, _e;
176
+ __privateSet(this, _mf, (_a = manifest.project) != null ? _a : {});
177
+ this.data = manifest;
178
+ __privateSet(this, _signalInstances, Object.entries((_c = (_b = __privateGet(this, _mf).pz) == null ? void 0 : _b.sig) != null ? _c : []).map(
179
+ ([id, signal]) => new SignalInstance({ ...signal, id }, evaluator, onLogMessage)
180
+ ));
181
+ Object.entries((_e = (_d = __privateGet(this, _mf).pz) == null ? void 0 : _d.sig) != null ? _e : []).forEach(([id, signal]) => {
182
+ if (signal.conversion) {
183
+ __privateGet(this, _goalEvaluators).push(new SignalGoalEvaluator({ id, signal }));
184
+ }
185
+ });
186
+ __privateSet(this, _onLogMessage2, onLogMessage);
187
+ }
188
+ rollForControlGroup() {
189
+ var _a;
190
+ return rollForControlGroup(((_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.control) || 0);
191
+ }
192
+ getTest(name) {
193
+ var _a;
194
+ return (_a = __privateGet(this, _mf).test) == null ? void 0 : _a[name];
195
+ }
196
+ computeSignals(update) {
197
+ const commands = [];
198
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "GROUP"]);
199
+ try {
200
+ __privateGet(this, _signalInstances).forEach((signal) => {
201
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "GROUP", signal.signal]);
202
+ try {
203
+ signal.computeSignal(update, commands);
204
+ } finally {
205
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "ENDGROUP"]);
206
+ }
207
+ });
208
+ } finally {
209
+ __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "ENDGROUP"]);
210
+ }
211
+ return commands;
212
+ }
213
+ computeGoals(data) {
214
+ const commands = [];
215
+ __privateGet(this, _goalEvaluators).forEach((evaluator) => {
216
+ const { triggered, key } = evaluator.evaluate(data);
217
+ if (triggered) {
218
+ commands.push({
219
+ type: "setgoal",
220
+ data: {
221
+ goal: evaluator.id
222
+ }
223
+ });
224
+ commands.push({
225
+ type: "setquirk",
226
+ data: {
227
+ key,
228
+ value: "1"
229
+ }
230
+ });
231
+ }
232
+ });
233
+ return commands;
234
+ }
235
+ /**
236
+ * Computes aggregated scores based on other dimensions
237
+ */
238
+ computeAggregateDimensions(primitiveScores) {
239
+ var _a, _b;
240
+ return computeAggregateDimensions(primitiveScores, (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.agg) != null ? _b : {});
241
+ }
242
+ getDimensionByKey(scoreKey) {
243
+ var _a, _b, _c, _d;
244
+ const enrichmentIndex = scoreKey.indexOf(ENR_SEPARATOR);
245
+ if (enrichmentIndex <= 0) {
246
+ return (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.sig) == null ? void 0 : _b[scoreKey];
247
+ }
248
+ return (_d = (_c = __privateGet(this, _mf).pz) == null ? void 0 : _c.enr) == null ? void 0 : _d[scoreKey.substring(0, enrichmentIndex)];
249
+ }
250
+ };
251
+ _mf = new WeakMap();
252
+ _signalInstances = new WeakMap();
253
+ _goalEvaluators = new WeakMap();
254
+ _onLogMessage2 = new WeakMap();
255
+
147
256
  // src/manifest/signals/criteria/evaluators/cookieEvaluator.ts
148
257
  import { dequal } from "dequal/lite";
149
258
 
@@ -261,6 +370,42 @@ var eventEvaluator = ({ update, criteria, onLogMessage }) => {
261
370
  return finalResult;
262
371
  };
263
372
 
373
+ // src/manifest/utils/getEnrichmentVectorKey.ts
374
+ function getEnrichmentVectorKey(category, value) {
375
+ return `${category}${ENR_SEPARATOR}${value}`;
376
+ }
377
+
378
+ // src/manifest/signals/criteria/util/isNumberMatch.ts
379
+ function isNumberMatch(lhs, match) {
380
+ var _a;
381
+ if (typeof lhs === "undefined" || lhs === null) {
382
+ return false;
383
+ }
384
+ const lhsValue = Number(lhs);
385
+ if (isNaN(lhsValue)) {
386
+ return false;
387
+ }
388
+ switch ((_a = match == null ? void 0 : match.op) != null ? _a : "=") {
389
+ case "=":
390
+ return lhsValue === match.rhs;
391
+ case "!=":
392
+ return lhsValue !== match.rhs;
393
+ case ">":
394
+ return lhsValue > match.rhs;
395
+ case "<":
396
+ return lhsValue < match.rhs;
397
+ default:
398
+ console.warn(`Unknown match type ${match.op} is false.`);
399
+ return false;
400
+ }
401
+ }
402
+ function explainNumberMatch(lhs, match) {
403
+ return `${lhs} ${explainNumberMatchCriteria(match)}`;
404
+ }
405
+ function explainNumberMatchCriteria(match) {
406
+ return `${match.op} ${match.rhs}`;
407
+ }
408
+
264
409
  // src/manifest/signals/criteria/evaluators/pageViewCountEvaluator.ts
265
410
  var pageViewCountDimension = getEnrichmentVectorKey("$pvc", "v");
266
411
  var pageViewCountEvaluator = ({ update, criteria, commands, onLogMessage }) => {
@@ -390,236 +535,6 @@ var GroupCriteriaEvaluator = class {
390
535
  };
391
536
  _evaluators = new WeakMap();
392
537
 
393
- // src/manifest/goals/evaluators/QuirkGoalEvaluator.ts
394
- var _goal2, _id2;
395
- var QuirkGoalEvaluator = class {
396
- constructor(options) {
397
- __privateAdd(this, _goal2, void 0);
398
- __privateAdd(this, _id2, void 0);
399
- __privateSet(this, _goal2, options.goal);
400
- __privateSet(this, _id2, options.id);
401
- }
402
- get id() {
403
- return __privateGet(this, _id2);
404
- }
405
- evaluate({ quirks }) {
406
- const quirkValue = quirks == null ? void 0 : quirks[__privateGet(this, _goal2).id];
407
- if (typeof quirkValue !== "string") {
408
- return {
409
- triggered: false
410
- };
411
- }
412
- const isMatch = isStringMatch(quirkValue, {
413
- op: __privateGet(this, _goal2).op,
414
- rhs: __privateGet(this, _goal2).value,
415
- cs: __privateGet(this, _goal2).cs
416
- });
417
- return {
418
- triggered: isMatch
419
- };
420
- }
421
- };
422
- _goal2 = new WeakMap();
423
- _id2 = new WeakMap();
424
-
425
- // src/manifest/goals/evaluators/SignalGoalEvaluator.ts
426
- var _goal3, _id3;
427
- var SignalGoalEvaluator = class {
428
- constructor(options) {
429
- __privateAdd(this, _goal3, void 0);
430
- __privateAdd(this, _id3, void 0);
431
- __privateSet(this, _id3, options.id);
432
- __privateSet(this, _goal3, options.goal);
433
- }
434
- get id() {
435
- return __privateGet(this, _id3);
436
- }
437
- evaluate({ scores }) {
438
- const score = scores == null ? void 0 : scores[__privateGet(this, _goal3).id];
439
- if (typeof score !== "number") {
440
- return {
441
- triggered: false
442
- };
443
- }
444
- const isMatch = isNumberMatch(score, {
445
- op: __privateGet(this, _goal3).op,
446
- rhs: __privateGet(this, _goal3).score
447
- });
448
- return {
449
- triggered: isMatch
450
- };
451
- }
452
- };
453
- _goal3 = new WeakMap();
454
- _id3 = new WeakMap();
455
-
456
- // src/manifest/goals/evaluators/index.ts
457
- var resolveGoalEvaluator = ({ id, goal }) => {
458
- let evaluator;
459
- if (goal.type === "sig") {
460
- evaluator = new SignalGoalEvaluator({
461
- id,
462
- goal
463
- });
464
- } else if (goal.type === "enr") {
465
- evaluator = new EnrichmentGoalEvaluator({
466
- id,
467
- goal
468
- });
469
- } else if (goal.type === "qrk") {
470
- evaluator = new QuirkGoalEvaluator({
471
- id,
472
- goal
473
- });
474
- }
475
- return evaluator;
476
- };
477
-
478
- // src/manifest/signals/SignalInstance.ts
479
- var _evaluator, _onLogMessage;
480
- var SignalInstance = class {
481
- constructor(data, evaluator, onLogMessage) {
482
- __privateAdd(this, _evaluator, void 0);
483
- __privateAdd(this, _onLogMessage, void 0);
484
- __publicField(this, "signal");
485
- this.signal = data;
486
- __privateSet(this, _evaluator, evaluator);
487
- __privateSet(this, _onLogMessage, onLogMessage);
488
- }
489
- /** Computes storage update commands to take based on a state update and the signal's criteria */
490
- computeSignal(update, commands) {
491
- const isAtCap = update.scores[this.signal.id] >= this.signal.cap;
492
- if (isAtCap && this.signal.dur !== "t") {
493
- return;
494
- }
495
- const criteriaMatchUpdate = __privateGet(this, _evaluator).evaluate(
496
- update,
497
- this.signal.crit,
498
- commands,
499
- this.signal,
500
- __privateGet(this, _onLogMessage)
501
- );
502
- const scoreCommand = this.signal.dur === "s" || this.signal.dur === "t" ? "modscoreS" : "modscore";
503
- if (!criteriaMatchUpdate.changed) {
504
- return;
505
- }
506
- if (criteriaMatchUpdate.result) {
507
- commands.push({
508
- type: scoreCommand,
509
- data: { dimension: this.signal.id, delta: this.signal.str }
510
- });
511
- } else if (this.signal.dur === "t") {
512
- const sessionScore = update.visitor.sessionScores[this.signal.id];
513
- if (sessionScore) {
514
- commands.push({
515
- type: scoreCommand,
516
- data: { dimension: this.signal.id, delta: -sessionScore }
517
- });
518
- }
519
- }
520
- }
521
- };
522
- _evaluator = new WeakMap();
523
- _onLogMessage = new WeakMap();
524
-
525
- // src/manifest/ManifestInstance.ts
526
- var _mf, _signalInstances, _goalEvaluators, _onLogMessage2;
527
- var ManifestInstance = class {
528
- constructor({
529
- manifest,
530
- evaluator = new GroupCriteriaEvaluator({}),
531
- // eslint-disable-next-line @typescript-eslint/no-empty-function
532
- onLogMessage = () => {
533
- }
534
- }) {
535
- __publicField(this, "data");
536
- __privateAdd(this, _mf, void 0);
537
- __privateAdd(this, _signalInstances, void 0);
538
- __privateAdd(this, _goalEvaluators, void 0);
539
- __privateAdd(this, _onLogMessage2, void 0);
540
- var _a, _b, _c, _d;
541
- __privateSet(this, _mf, (_a = manifest.project) != null ? _a : {});
542
- this.data = manifest;
543
- __privateSet(this, _signalInstances, Object.entries((_c = (_b = __privateGet(this, _mf).pz) == null ? void 0 : _b.sig) != null ? _c : []).map(
544
- ([id, signal]) => new SignalInstance({ ...signal, id }, evaluator, onLogMessage)
545
- ));
546
- __privateSet(this, _goalEvaluators, Object.entries((_d = __privateGet(this, _mf).goal) != null ? _d : {}).map(([id, goal]) => {
547
- const evaluator2 = resolveGoalEvaluator({
548
- id,
549
- goal
550
- });
551
- if (!evaluator2) {
552
- console.warn(`Unable to resolve goal evaluator for goal ${id}`);
553
- }
554
- return evaluator2;
555
- }).filter((evaluator2) => !!evaluator2));
556
- __privateSet(this, _onLogMessage2, onLogMessage);
557
- }
558
- rollForControlGroup() {
559
- var _a, _b;
560
- let control = (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.control) != null ? _b : 0;
561
- if (control >= 1) {
562
- control = control / 100;
563
- }
564
- return Math.random() < control;
565
- }
566
- getTest(name) {
567
- var _a;
568
- return (_a = __privateGet(this, _mf).test) == null ? void 0 : _a[name];
569
- }
570
- computeSignals(update) {
571
- const commands = [];
572
- __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "GROUP"]);
573
- try {
574
- __privateGet(this, _signalInstances).forEach((signal) => {
575
- __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "GROUP", signal.signal]);
576
- try {
577
- signal.computeSignal(update, commands);
578
- } finally {
579
- __privateGet(this, _onLogMessage2).call(this, ["debug", 201, "ENDGROUP"]);
580
- }
581
- });
582
- } finally {
583
- __privateGet(this, _onLogMessage2).call(this, ["debug", 200, "ENDGROUP"]);
584
- }
585
- return commands;
586
- }
587
- computeGoals(data) {
588
- const commands = [];
589
- __privateGet(this, _goalEvaluators).forEach((evaluator) => {
590
- const { triggered } = evaluator.evaluate(data);
591
- if (triggered) {
592
- commands.push({
593
- type: "setgoal",
594
- data: {
595
- goal: evaluator.id
596
- }
597
- });
598
- }
599
- });
600
- return commands;
601
- }
602
- /**
603
- * Computes aggregated scores based on other dimensions
604
- */
605
- computeAggregateDimensions(primitiveScores) {
606
- var _a, _b;
607
- return computeAggregateDimensions(primitiveScores, (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.agg) != null ? _b : {});
608
- }
609
- getDimensionByKey(scoreKey) {
610
- var _a, _b, _c, _d;
611
- const enrichmentIndex = scoreKey.indexOf(ENR_SEPARATOR);
612
- if (enrichmentIndex <= 0) {
613
- return (_b = (_a = __privateGet(this, _mf).pz) == null ? void 0 : _a.sig) == null ? void 0 : _b[scoreKey];
614
- }
615
- return (_d = (_c = __privateGet(this, _mf).pz) == null ? void 0 : _c.enr) == null ? void 0 : _d[scoreKey.substring(0, enrichmentIndex)];
616
- }
617
- };
618
- _mf = new WeakMap();
619
- _signalInstances = new WeakMap();
620
- _goalEvaluators = new WeakMap();
621
- _onLogMessage2 = new WeakMap();
622
-
623
538
  // src/placement/criteria/evaluateVariantMatch.ts
624
539
  function evaluateVariantMatch(variantId, match, vec, onLogMessage) {
625
540
  onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variantId, op: match == null ? void 0 : match.op }]);
@@ -643,7 +558,30 @@ function evaluateDimensionMatch(crit, vec, onLogMessage) {
643
558
  var _a, _b;
644
559
  const { op, l: lhs } = crit;
645
560
  const lhsScore = (_a = vec[lhs]) != null ? _a : 0;
646
- if (op === "+") {
561
+ if (op === "^") {
562
+ const [cat] = lhs.split(ENR_SEPARATOR);
563
+ let topVectorName = void 0;
564
+ let topScore = 0;
565
+ Object.keys(vec).forEach((vectorName) => {
566
+ if (vectorName.startsWith(`${cat}${ENR_SEPARATOR}`)) {
567
+ const score = vec[vectorName];
568
+ if (score > topScore) {
569
+ topVectorName = vectorName;
570
+ topScore = score;
571
+ }
572
+ }
573
+ });
574
+ const result = topVectorName === lhs;
575
+ onLogMessage == null ? void 0 : onLogMessage([
576
+ "info",
577
+ 302,
578
+ {
579
+ matched: result,
580
+ description: `${crit.l} has the highest score in the category`
581
+ }
582
+ ]);
583
+ return result;
584
+ } else if (op === "+") {
647
585
  const result = Math.max(...Object.values(vec)) === lhsScore && lhsScore > 0;
648
586
  onLogMessage == null ? void 0 : onLogMessage([
649
587
  "info",
@@ -730,31 +668,70 @@ function personalizeVariations({
730
668
  take = 1,
731
669
  onLogMessage
732
670
  }) {
733
- var _a, _b, _c;
671
+ var _a, _b, _c, _d;
734
672
  onLogMessage == null ? void 0 : onLogMessage(["info", 300, "GROUP", { name, take }]);
735
673
  try {
736
674
  const control = (_a = context.storage.data.controlGroup) != null ? _a : false;
737
675
  const results = [];
738
676
  let personalized = false;
739
677
  const scores = context.scores;
678
+ let index = 0;
679
+ const defaultVariants = [];
740
680
  for (const variant of variations) {
681
+ if (!((_b = variant.pz) == null ? void 0 : _b.crit.length)) {
682
+ defaultVariants.push(variant);
683
+ }
684
+ }
685
+ for (const variant of variations) {
686
+ const currentIndex = index++;
741
687
  if (results.length === take) {
742
688
  break;
743
689
  }
744
- if (!((_b = variant.pz) == null ? void 0 : _b.crit.length)) {
745
- onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variant.id, op: (_c = variant.pz) == null ? void 0 : _c.op }]);
690
+ if (!((_c = variant.pz) == null ? void 0 : _c.crit.length)) {
691
+ onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variant.id, op: (_d = variant.pz) == null ? void 0 : _d.op }]);
746
692
  onLogMessage == null ? void 0 : onLogMessage(["info", 302, { matched: true, description: "default variation" }]);
747
693
  onLogMessage == null ? void 0 : onLogMessage(["info", 303, true]);
748
694
  onLogMessage == null ? void 0 : onLogMessage(["info", 301, "ENDGROUP"]);
749
- results.push(variant);
695
+ results.push({
696
+ ...variant,
697
+ control: false
698
+ });
750
699
  continue;
751
700
  }
752
701
  if (control) {
753
702
  continue;
754
703
  }
755
704
  if (evaluateVariantMatch(variant.id, variant.pz, scores, onLogMessage)) {
756
- personalized = true;
757
- results.push(variant);
705
+ let variantToAdd = variant;
706
+ let isControl = false;
707
+ const isDefault = defaultVariants.find((v) => v.id === variant.id);
708
+ if (take === 1 && !isDefault && defaultVariants.length && typeof variant.pz.control === "number") {
709
+ isControl = context.getPersonalizeVariantControl(name, currentIndex);
710
+ if (typeof isControl === "undefined") {
711
+ isControl = rollForControlGroup(variant.pz.control);
712
+ context.storage.updateData([
713
+ {
714
+ type: "setpersonalizecontrol",
715
+ data: {
716
+ personlizationName: name,
717
+ index: currentIndex,
718
+ control: isControl
719
+ }
720
+ }
721
+ ]);
722
+ }
723
+ if (isControl) {
724
+ variantToAdd = {
725
+ ...defaultVariants[0],
726
+ id: variant.id
727
+ };
728
+ }
729
+ }
730
+ personalized = personalized || typeof variantToAdd.pz !== "undefined";
731
+ results.push({
732
+ ...variantToAdd,
733
+ control: isControl
734
+ });
758
735
  }
759
736
  }
760
737
  return {
@@ -1001,16 +978,19 @@ function parseScoreCookie(cookieValue) {
1001
978
  if (!cookieValue)
1002
979
  return;
1003
980
  const types = cookieValue.split(TYPE_SEP);
1004
- if (types.length > 3)
981
+ if (types.length > 5)
1005
982
  return;
1006
- const [abTestData, sessionScores, visitorScores] = types;
1007
- return {
983
+ const [abTestData, sessionScores, visitorScores, controlGroup, personalizeVariants] = types;
984
+ const data = {
1008
985
  // this is true since we're reading a cookie, which wouldn't exist if consent wasn't given
1009
986
  consent: true,
1010
987
  sessionScores: decodeCookieType(parseCookieType(sessionScores)),
1011
988
  scores: decodeCookieType(parseCookieType(visitorScores)),
1012
- tests: parseCookieType(abTestData)
989
+ tests: parseCookieType(abTestData),
990
+ controlGroup: controlGroup === "1",
991
+ personalizeVariants: decodePersonalizeVariants(personalizeVariants)
1013
992
  };
993
+ return data;
1014
994
  }
1015
995
  function parseCookieType(type) {
1016
996
  if (!type) {
@@ -1034,9 +1014,48 @@ function serializeCookie(data) {
1034
1014
  return [
1035
1015
  serializeCookieType(data.tests),
1036
1016
  serializeCookieType(encodeCookieType(data.sessionScores)),
1037
- serializeCookieType(encodeCookieType(data.scores))
1017
+ serializeCookieType(encodeCookieType(data.scores)),
1018
+ data.controlGroup ? "1" : "0",
1019
+ serializePersonalizeVariants(data)
1038
1020
  ].join(TYPE_SEP);
1039
1021
  }
1022
+ function serializePersonalizeVariants({
1023
+ personalizeVariants
1024
+ }) {
1025
+ const data = {};
1026
+ if (typeof personalizeVariants === "object") {
1027
+ Object.keys(personalizeVariants).forEach((personalizationName) => {
1028
+ const results = [];
1029
+ const variants = personalizeVariants[personalizationName];
1030
+ variants.forEach((variant) => {
1031
+ results.push(`${variant.index}:${variant.control ? "1" : "0"}`);
1032
+ });
1033
+ data[personalizationName] = results.join(",");
1034
+ });
1035
+ }
1036
+ const serialized = serializeCookieType(data);
1037
+ return serialized;
1038
+ }
1039
+ function decodePersonalizeVariants(data) {
1040
+ const parsed = parseCookieType(data);
1041
+ const keys = Object.keys(parsed);
1042
+ if (!keys.length) {
1043
+ return void 0;
1044
+ }
1045
+ const results = {};
1046
+ Object.keys(parsed).forEach((k) => {
1047
+ const variants = parsed[k].split(",");
1048
+ const key = decodeURIComponent(k);
1049
+ results[key] = variants.map((variant) => {
1050
+ const [index, control] = variant.split(":");
1051
+ return {
1052
+ index: parseInt(index, 10),
1053
+ control: control === "1"
1054
+ };
1055
+ });
1056
+ });
1057
+ return results;
1058
+ }
1040
1059
  function encodeCookieType(type) {
1041
1060
  return Object.entries(type).reduce((acc, [key, value]) => {
1042
1061
  acc[key] = ntob(value);
@@ -1125,7 +1144,8 @@ var emptyVisitorData = () => ({
1125
1144
  tests: {},
1126
1145
  goals: {},
1127
1146
  consent: false,
1128
- controlGroup: false
1147
+ controlGroup: false,
1148
+ personalizeVariants: {}
1129
1149
  });
1130
1150
 
1131
1151
  // src/storage/VisitorDataStore.ts
@@ -1174,6 +1194,25 @@ function applyCommandsToData(commands, state, inControlGroup) {
1174
1194
  case "setcontrol":
1175
1195
  newData.controlGroup = command.data;
1176
1196
  break;
1197
+ case "setpersonalizecontrol":
1198
+ if (!newData.personalizeVariants) {
1199
+ newData.personalizeVariants = {};
1200
+ }
1201
+ if (!newData.personalizeVariants[command.data.personlizationName]) {
1202
+ newData.personalizeVariants[command.data.personlizationName] = [];
1203
+ }
1204
+ const existingDef = newData.personalizeVariants[command.data.personlizationName].find(
1205
+ (i) => i.index === command.data.index
1206
+ );
1207
+ if (!existingDef) {
1208
+ newData.personalizeVariants[command.data.personlizationName].push({
1209
+ index: command.data.index,
1210
+ control: command.data.control
1211
+ });
1212
+ } else {
1213
+ console.warn("Overwriting existing control group definition is not allowed");
1214
+ }
1215
+ break;
1177
1216
  case "setgoal":
1178
1217
  newData.goals = (_c = newData.goals) != null ? _c : {};
1179
1218
  newData.goals[command.data.goal] = true;
@@ -1556,7 +1595,7 @@ var Context = class {
1556
1595
  * will NOT result in a recomputation of signal state.
1557
1596
  */
1558
1597
  async update(newData) {
1559
- var _a, _b, _c;
1598
+ var _a, _b, _c, _d;
1560
1599
  const commands = [];
1561
1600
  const newServerSideTests = {};
1562
1601
  if ((_a = __privateGet(this, _serverTransitionState)) == null ? void 0 : _a.quirks) {
@@ -1586,6 +1625,17 @@ var Context = class {
1586
1625
  );
1587
1626
  }
1588
1627
  }
1628
+ if ((_c = __privateGet(this, _serverTransitionState)) == null ? void 0 : _c.personalizeVariants) {
1629
+ Object.keys(__privateGet(this, _serverTransitionState).personalizeVariants).forEach((personalizationName) => {
1630
+ const variants = __privateGet(this, _serverTransitionState).personalizeVariants[personalizationName];
1631
+ variants.forEach((e) => {
1632
+ commands.push({
1633
+ type: "setpersonalizecontrol",
1634
+ data: { personlizationName: personalizationName, index: e.index, control: e.control }
1635
+ });
1636
+ });
1637
+ });
1638
+ }
1589
1639
  try {
1590
1640
  __privateGet(this, _mitt3).emit("log", [
1591
1641
  "info",
@@ -1595,7 +1645,7 @@ var Context = class {
1595
1645
  ...newData,
1596
1646
  // need to convert url to string so it can be json serialized
1597
1647
  // to go over postMessage to chrome extension
1598
- url: (_c = newData.url) == null ? void 0 : _c.toString()
1648
+ url: (_d = newData.url) == null ? void 0 : _d.toString()
1599
1649
  }
1600
1650
  ]);
1601
1651
  if (newData.quirks) {
@@ -1686,6 +1736,13 @@ var Context = class {
1686
1736
  }
1687
1737
  ]);
1688
1738
  }
1739
+ getPersonalizeVariantControl(name, index) {
1740
+ var _a, _b, _c;
1741
+ const source = (_b = (_a = __privateGet(this, _serverTransitionState)) == null ? void 0 : _a.personalizeVariants) != null ? _b : this.storage.data.personalizeVariants;
1742
+ const variants = (_c = source == null ? void 0 : source[name]) != null ? _c : [];
1743
+ const variant = variants.find((v) => v.index === index);
1744
+ return variant == null ? void 0 : variant.control;
1745
+ }
1689
1746
  /**
1690
1747
  * Writes a message to the Context log sink.
1691
1748
  * Used by Uniform internal SDK; not intended for public use.
@@ -1728,10 +1785,10 @@ var Context = class {
1728
1785
  const previousPlacement = __privateGet(this, _pzCache)[options.name];
1729
1786
  const eventData = {
1730
1787
  name: options.name,
1731
- variantIds: value.variations.map((variation) => {
1732
- var _a;
1733
- return (_a = variation.id) != null ? _a : "Unknown";
1734
- }),
1788
+ variantIds: value.variations.map((variation) => ({
1789
+ id: variation.id || "Unknown",
1790
+ control: variation.control
1791
+ })),
1735
1792
  control: this.storage.data.controlGroup,
1736
1793
  changed: true
1737
1794
  };
@@ -1768,7 +1825,8 @@ var Context = class {
1768
1825
  const transitionState = {
1769
1826
  quirks: this.storage.data.quirks,
1770
1827
  ssv: __privateGet(this, _scores),
1771
- tests: {}
1828
+ tests: {},
1829
+ personalizeVariants: this.storage.data.personalizeVariants
1772
1830
  };
1773
1831
  const allTests = this.storage.data.tests;
1774
1832
  Object.entries(allTests).map(([testName, testValue]) => {
@@ -2452,7 +2510,10 @@ var createInsightsClient = ({ endpoint }) => {
2452
2510
  };
2453
2511
  return sendMessage(message);
2454
2512
  },
2455
- testResult: (options) => {
2513
+ testResult: async (options) => {
2514
+ if (!options.variantAssigned) {
2515
+ return;
2516
+ }
2456
2517
  const message = {
2457
2518
  action: "test_result",
2458
2519
  version: "1",
@@ -2468,7 +2529,10 @@ var createInsightsClient = ({ endpoint }) => {
2468
2529
  return sendMessage(message);
2469
2530
  },
2470
2531
  personalizationResult: async (options) => {
2471
- const messages = options.variantIds.map((variantId) => {
2532
+ if (!options.changed) {
2533
+ return;
2534
+ }
2535
+ const messages = options.variantIds.map((variant) => {
2472
2536
  const message = {
2473
2537
  action: "personalization_result",
2474
2538
  version: "1",
@@ -2479,8 +2543,8 @@ var createInsightsClient = ({ endpoint }) => {
2479
2543
  payload: {
2480
2544
  ...getBasePayload(),
2481
2545
  name: options.name,
2482
- variantId,
2483
- control: options.control,
2546
+ variantId: variant.id,
2547
+ control: variant.control || options.control,
2484
2548
  changed: options.changed
2485
2549
  }
2486
2550
  };
@@ -2561,6 +2625,7 @@ var createInsights = ({
2561
2625
  const storage = createInsightsStorage();
2562
2626
  let storageData = void 0;
2563
2627
  let pageId = generatePageId();
2628
+ let previousUrl = void 0;
2564
2629
  return {
2565
2630
  init: () => {
2566
2631
  storageData = storage.get();
@@ -2588,6 +2653,11 @@ var createInsights = ({
2588
2653
  console.error("Insights not initialized");
2589
2654
  return;
2590
2655
  }
2656
+ if (previousUrl === window.location.href) {
2657
+ return;
2658
+ }
2659
+ previousUrl = window.location.href;
2660
+ pageId = generatePageId();
2591
2661
  client.pageHit({
2592
2662
  visitorId: storageData.visitorId,
2593
2663
  sessionId: storageData.sessionId,
@@ -2605,7 +2675,6 @@ var createInsights = ({
2605
2675
  sessionId: storageData.sessionId,
2606
2676
  pageId
2607
2677
  });
2608
- pageId = generatePageId();
2609
2678
  },
2610
2679
  personalizationResult: (result) => {
2611
2680
  if (!storageData) {