@uniformdev/context 20.6.1 → 20.6.2-alpha.11

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
@@ -53,7 +53,9 @@ __export(src_exports, {
53
53
  PAIR_SEP: () => PAIR_SEP,
54
54
  QUIRK_SEP: () => QUIRK_SEP,
55
55
  SERVER_STATE_ID: () => SERVER_STATE_ID,
56
+ STRONGEST_SCORE_PERSONALIZATION_ALGORITHM: () => STRONGEST_SCORE_PERSONALIZATION_ALGORITHM,
56
57
  ScriptType: () => ScriptType,
58
+ TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM: () => TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM,
57
59
  TYPE_SEP: () => TYPE_SEP,
58
60
  TransitionDataStore: () => TransitionDataStore,
59
61
  UNIFORM_DEFAULT_COOKIE_NAME: () => UNIFORM_DEFAULT_COOKIE_NAME,
@@ -95,7 +97,9 @@ __export(src_exports, {
95
97
  serializePersonalizeVariants: () => serializePersonalizeVariants,
96
98
  serializeQuickConnect: () => serializeQuickConnect,
97
99
  serializeQuirks: () => serializeQuirks,
98
- testVariations: () => testVariations
100
+ strongestScorePersonalizationSelectionAlgorithm: () => strongestScorePersonalizationSelectionAlgorithm,
101
+ testVariations: () => testVariations,
102
+ topDownCriteriaPersonalizationSelectionAlgorithm: () => topDownCriteriaPersonalizationSelectionAlgorithm
99
103
  });
100
104
  module.exports = __toCommonJS(src_exports);
101
105
 
@@ -605,7 +609,7 @@ var GroupCriteriaEvaluator = class {
605
609
  _evaluators = new WeakMap();
606
610
 
607
611
  // src/placement/criteria/evaluateVariantMatch.ts
608
- function evaluateVariantMatch(variantId, match, vec, onLogMessage) {
612
+ function evaluateVariantMatch(variantId, match, vec, onLogMessage, quirks) {
609
613
  onLogMessage == null ? void 0 : onLogMessage(["info", 301, "GROUP", { id: variantId, op: match == null ? void 0 : match.op }]);
610
614
  let result;
611
615
  try {
@@ -613,9 +617,9 @@ function evaluateVariantMatch(variantId, match, vec, onLogMessage) {
613
617
  onLogMessage == null ? void 0 : onLogMessage(["info", 302, { matched: true, description: "default variation" }]);
614
618
  result = true;
615
619
  } else if (!match.op || match.op === "&") {
616
- result = match.crit.every((c) => evaluateDimensionMatch(c, vec, onLogMessage));
620
+ result = match.crit.every((c) => evaluateMatch(c, vec, quirks != null ? quirks : {}, onLogMessage));
617
621
  } else {
618
- result = match.crit.some((c) => evaluateDimensionMatch(c, vec, onLogMessage));
622
+ result = match.crit.some((c) => evaluateMatch(c, vec, quirks != null ? quirks : {}, onLogMessage));
619
623
  }
620
624
  onLogMessage == null ? void 0 : onLogMessage(["info", 303, result]);
621
625
  } finally {
@@ -623,9 +627,16 @@ function evaluateVariantMatch(variantId, match, vec, onLogMessage) {
623
627
  }
624
628
  return result;
625
629
  }
630
+ function evaluateMatch(crit, vec, quirks, onLogMessage) {
631
+ if ("t" in crit && crit.t === "q") {
632
+ return evaluateQuirkMatch(crit, quirks, onLogMessage);
633
+ } else {
634
+ return evaluateDimensionMatch(crit, vec, onLogMessage);
635
+ }
636
+ }
626
637
  function evaluateDimensionMatch(crit, vec, onLogMessage) {
627
638
  var _a, _b;
628
- const { op, l: lhs } = crit;
639
+ const { l: lhs, op } = crit;
629
640
  const lhsScore = (_a = vec[lhs]) != null ? _a : 0;
630
641
  if (op === "^") {
631
642
  const [cat] = lhs.split(ENR_SEPARATOR);
@@ -692,33 +703,65 @@ function evaluateDimensionMatch(crit, vec, onLogMessage) {
692
703
  }
693
704
  if (op === ">") {
694
705
  const result = lhsScore > rhsScore;
695
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
706
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
696
707
  return result;
697
708
  } else if (op === ">=") {
698
709
  const result = lhsScore >= rhsScore;
699
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
710
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
700
711
  return result;
701
712
  } else if (op === "<") {
702
713
  const result = lhsScore < rhsScore;
703
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
714
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
704
715
  return result;
705
716
  } else if (op === "<=") {
706
717
  const result = lhsScore <= rhsScore;
707
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
718
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
708
719
  return result;
709
720
  } else if (op === "=") {
710
721
  const result = lhsScore === rhsScore;
711
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
722
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
712
723
  return result;
713
724
  } else if (op === "!=") {
714
725
  const result = lhsScore !== rhsScore;
715
- explain(onLogMessage, result, crit, lhsScore, rhsScore);
726
+ explainScore(onLogMessage, result, crit, lhsScore, rhsScore);
716
727
  return result;
717
728
  } else {
718
- throw new Error(`Unknown op: ${op}`);
729
+ onLogMessage == null ? void 0 : onLogMessage([
730
+ "error",
731
+ 302,
732
+ {
733
+ matched: false,
734
+ description: `${crit.l} ${crit.op} ${crit.rDim ? `${crit.rDim}` : crit.r}: Unknown op ${crit.op}.`
735
+ }
736
+ ]);
737
+ return false;
738
+ }
739
+ }
740
+ function evaluateQuirkMatch(crit, quirks, onLogMessage) {
741
+ var _a;
742
+ const { l: targetQuirk, op, r: targetValue } = crit;
743
+ const targetQuirkValue = (_a = quirks[targetQuirk]) != null ? _a : "";
744
+ if (op === "=") {
745
+ const result = targetQuirkValue === targetValue;
746
+ explainQuirk(onLogMessage, result, crit, targetQuirkValue);
747
+ return result;
748
+ } else if (op === "!=") {
749
+ const result = targetQuirkValue !== targetValue;
750
+ explainQuirk(onLogMessage, result, crit, targetQuirkValue);
751
+ return result;
752
+ } else {
753
+ onLogMessage == null ? void 0 : onLogMessage([
754
+ "error",
755
+ 302,
756
+ {
757
+ matched: false,
758
+ description: `Quirk ${crit.l} ${crit.op} ${crit.r}: Unknown quirk op ${crit.op}.`
759
+ }
760
+ ]);
761
+ return false;
719
762
  }
720
763
  }
721
- function explain(onLogMessage, result, crit, lhsScore, rhsScore) {
764
+ function explainScore(onLogMessage, result, crit, lhsScore, rhsScore) {
722
765
  onLogMessage == null ? void 0 : onLogMessage([
723
766
  "info",
724
767
  302,
@@ -728,9 +771,20 @@ function explain(onLogMessage, result, crit, lhsScore, rhsScore) {
728
771
  }
729
772
  ]);
730
773
  }
774
+ function explainQuirk(onLogMessage, result, crit, lhs) {
775
+ onLogMessage == null ? void 0 : onLogMessage([
776
+ "info",
777
+ 302,
778
+ {
779
+ matched: result,
780
+ description: `Quirk ${crit.l}[${lhs}] ${crit.op} ${crit.r}`
781
+ }
782
+ ]);
783
+ }
731
784
 
732
- // src/placement/personalize.ts
733
- function personalizeVariations({
785
+ // src/placement/topDownCriteriaPersonalizationSelectionAlgorithm.ts
786
+ var TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM = "default";
787
+ function topDownCriteriaPersonalizationSelectionAlgorithm({
734
788
  name,
735
789
  context,
736
790
  variations,
@@ -740,27 +794,35 @@ function personalizeVariations({
740
794
  var _a, _b, _c;
741
795
  onLogMessage == null ? void 0 : onLogMessage(["info", 300, "GROUP", { name, take }]);
742
796
  try {
743
- const variantMatches = [];
744
- const defaultVariants = [];
745
- const needsConsent = context.requireConsentForPersonalization;
746
- const canEvaluate = !needsConsent || context.storage.data.consent;
747
- for (const variant of variations) {
748
- if ((_a = variant.pz) == null ? void 0 : _a.crit.length) {
749
- if (canEvaluate && variantMatches.length !== take && evaluateVariantMatch(variant.id, variant.pz, context.scores, onLogMessage)) {
750
- variantMatches.push(variant);
797
+ const variationMatches = [];
798
+ const defaultVariations = [];
799
+ const needsConsentToPersonalize = context.requireConsentForPersonalization;
800
+ const personalizationAllowed = !needsConsentToPersonalize || context.storage.data.consent;
801
+ for (const variation of variations) {
802
+ const isInvalidFormat = variation.pz && (typeof variation.pz !== "object" || variation.pz === null || !("crit" in variation.pz && Array.isArray(variation.pz.crit)));
803
+ let validVariation;
804
+ if (isInvalidFormat) {
805
+ const { pz, ...validParts } = variation;
806
+ validVariation = validParts;
807
+ } else {
808
+ validVariation = variation;
809
+ }
810
+ if ((_a = validVariation.pz) == null ? void 0 : _a.crit.length) {
811
+ if (personalizationAllowed && variationMatches.length !== take && evaluateVariantMatch(variation.id, validVariation.pz, context.scores, onLogMessage, context.quirks)) {
812
+ variationMatches.push(validVariation);
751
813
  }
752
814
  } else {
753
- defaultVariants.push(variant);
815
+ defaultVariations.push(validVariation);
754
816
  }
755
817
  }
756
818
  const result = [];
757
- for (let i = 0; i < variantMatches.length; i++) {
819
+ for (let i = 0; i < variationMatches.length; i++) {
758
820
  let isControl = (_b = context.storage.data.controlGroup) != null ? _b : false;
759
- const variant = variantMatches[i];
760
- if (!isControl && typeof ((_c = variant.pz) == null ? void 0 : _c.control) === "number") {
821
+ const variation = variationMatches[i];
822
+ if (!isControl && typeof ((_c = variation.pz) == null ? void 0 : _c.control) === "number") {
761
823
  isControl = context.getPersonalizeVariantControl(name, i);
762
824
  if (typeof isControl === "undefined") {
763
- isControl = rollForControlGroup(variant.pz.control);
825
+ isControl = rollForControlGroup(variation.pz.control);
764
826
  context.storage.updateData([
765
827
  {
766
828
  type: "setpersonalizecontrol",
@@ -773,13 +835,13 @@ function personalizeVariations({
773
835
  ]);
774
836
  }
775
837
  }
776
- let variantToAdd = variant;
838
+ let variantToAdd = variation;
777
839
  if (isControl) {
778
- const defaultReplacement = defaultVariants.shift();
840
+ const defaultReplacement = defaultVariations.shift();
779
841
  if (defaultReplacement) {
780
842
  variantToAdd = {
781
843
  ...defaultReplacement,
782
- id: variant.id
844
+ id: variation.id
783
845
  };
784
846
  } else {
785
847
  variantToAdd = void 0;
@@ -789,8 +851,8 @@ function personalizeVariations({
789
851
  result.push({ ...variantToAdd, control: isControl });
790
852
  }
791
853
  }
792
- while (result.length < take && defaultVariants.length) {
793
- result.push({ ...defaultVariants.shift(), control: false });
854
+ while (result.length < take && defaultVariations.length) {
855
+ result.push({ ...defaultVariations.shift(), control: false });
794
856
  }
795
857
  const personalized = result.some((v) => {
796
858
  var _a2;
@@ -805,7 +867,86 @@ function personalizeVariations({
805
867
  }
806
868
  }
807
869
 
808
- // src/placement/test.ts
870
+ // src/placement/personalizeVariations.ts
871
+ function personalizeVariations(options) {
872
+ return topDownCriteriaPersonalizationSelectionAlgorithm(options);
873
+ }
874
+
875
+ // src/placement/strongestScorePersonalizationSelectionAlgorithm.ts
876
+ var STRONGEST_SCORE_PERSONALIZATION_ALGORITHM = "ssc";
877
+ function strongestScorePersonalizationSelectionAlgorithm({
878
+ name,
879
+ context,
880
+ variations,
881
+ take = 1,
882
+ onLogMessage
883
+ }) {
884
+ var _a, _b;
885
+ onLogMessage == null ? void 0 : onLogMessage(["info", 300, "GROUP", { name, take }]);
886
+ try {
887
+ const variationMatches = [];
888
+ const defaultVariations = [];
889
+ const needsConsentToPersonalize = context.requireConsentForPersonalization;
890
+ const isInGlobalControlGroup = (_a = context.storage.data.controlGroup) != null ? _a : false;
891
+ const personalizationAllowed = !needsConsentToPersonalize || context.storage.data.consent;
892
+ for (const variation of variations) {
893
+ const isInvalidFormat = variation.pz && typeof variation.pz !== "object";
894
+ let validVariation;
895
+ if (isInvalidFormat) {
896
+ const { pz, ...validParts } = variation;
897
+ validVariation = validParts;
898
+ } else {
899
+ validVariation = variation;
900
+ }
901
+ if ((_b = validVariation.pz) == null ? void 0 : _b.dim) {
902
+ if (!personalizationAllowed) {
903
+ continue;
904
+ }
905
+ const score = context.scores[validVariation.pz.dim];
906
+ if (score === void 0 || score <= 0) {
907
+ continue;
908
+ }
909
+ variationMatches.push({ variation: validVariation, score });
910
+ } else {
911
+ defaultVariations.push(validVariation);
912
+ }
913
+ }
914
+ variationMatches.sort((a, b) => b.score - a.score);
915
+ const result = [];
916
+ for (let i = 0; i < variationMatches.length; i++) {
917
+ const variationMatch = variationMatches[i];
918
+ let variantToAdd = variationMatch.variation;
919
+ if (i >= take) {
920
+ continue;
921
+ }
922
+ if (isInGlobalControlGroup) {
923
+ const defaultReplacement = defaultVariations.shift();
924
+ if (defaultReplacement) {
925
+ variantToAdd = {
926
+ ...defaultReplacement,
927
+ id: variationMatch.variation.id
928
+ };
929
+ } else {
930
+ variantToAdd = void 0;
931
+ }
932
+ }
933
+ if (variantToAdd) {
934
+ result.push({ ...variantToAdd, control: isInGlobalControlGroup });
935
+ }
936
+ }
937
+ while (result.length < take && defaultVariations.length) {
938
+ result.push({ ...defaultVariations.shift(), control: false });
939
+ }
940
+ return {
941
+ personalized: variationMatches.length > 0 && !isInGlobalControlGroup,
942
+ variations: result
943
+ };
944
+ } finally {
945
+ onLogMessage == null ? void 0 : onLogMessage(["info", 300, "ENDGROUP"]);
946
+ }
947
+ }
948
+
949
+ // src/placement/normalizeVariationDistributions.ts
809
950
  var normalizeVariationDistributions = (variations) => {
810
951
  const { values, total, missingDistribution } = variations.reduce(
811
952
  (previous, current) => {
@@ -824,7 +965,12 @@ var normalizeVariationDistributions = (variations) => {
824
965
  }
825
966
  );
826
967
  if (total > 100) {
827
- throw new Error(`Total distribution ${total} is over the maximum 100.`);
968
+ const autoScaleFactor = 100 / total;
969
+ values.forEach((value, index) => {
970
+ if (typeof value === "number") {
971
+ values[index] = value * autoScaleFactor;
972
+ }
973
+ });
828
974
  } else if (total < 100) {
829
975
  const remainder = 100 - total;
830
976
  const missingSlice = remainder / missingDistribution;
@@ -836,6 +982,8 @@ var normalizeVariationDistributions = (variations) => {
836
982
  }
837
983
  return values;
838
984
  };
985
+
986
+ // src/placement/testVariations.ts
839
987
  var testVariations = ({
840
988
  name,
841
989
  context,
@@ -1602,11 +1750,12 @@ var import_lite5 = require("dequal/lite");
1602
1750
  var import_mitt3 = __toESM(require("mitt"));
1603
1751
  var CONTEXTUAL_EDITING_TEST_NAME = "contextual_editing_test";
1604
1752
  var CONTEXTUAL_EDITING_TEST_SELECTED_VARIANT_ID = "contextual_editing_test_selected_variant";
1605
- var _serverTransitionState, _scores, _state, _pzCache, _plugins, _commands, _requireConsentForPersonalization, _mitt3, _Context_instances, emitTest_fn, updateGoals_fn, updateComputedScores_fn, calculateScores_fn;
1753
+ var _personalizationSelectionAlgorithms, _serverTransitionState, _scores, _state, _pzCache, _plugins, _commands, _requireConsentForPersonalization, _mitt3, _Context_instances, emitTest_fn, updateGoals_fn, updateComputedScores_fn, calculateScores_fn;
1606
1754
  var Context = class {
1607
1755
  constructor(options) {
1608
1756
  __privateAdd(this, _Context_instances);
1609
1757
  __publicField(this, "manifest");
1758
+ __privateAdd(this, _personalizationSelectionAlgorithms);
1610
1759
  __privateAdd(this, _serverTransitionState);
1611
1760
  __privateAdd(this, _scores, {});
1612
1761
  __privateAdd(this, _state);
@@ -1623,7 +1772,7 @@ var Context = class {
1623
1772
  off: __privateGet(this, _mitt3).off
1624
1773
  });
1625
1774
  __publicField(this, "storage");
1626
- var _a, _b;
1775
+ var _a, _b, _c;
1627
1776
  const { manifest, ...storageOptions } = options;
1628
1777
  __privateSet(this, _state, {});
1629
1778
  __privateSet(this, _plugins, options.plugins);
@@ -1631,7 +1780,20 @@ var Context = class {
1631
1780
  if (typeof options.transitionStore !== "undefined") {
1632
1781
  __privateSet(this, _commands, []);
1633
1782
  }
1783
+ __privateSet(this, _personalizationSelectionAlgorithms, {
1784
+ [TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM]: topDownCriteriaPersonalizationSelectionAlgorithm,
1785
+ [STRONGEST_SCORE_PERSONALIZATION_ALGORITHM]: strongestScorePersonalizationSelectionAlgorithm
1786
+ });
1634
1787
  (_a = __privateGet(this, _plugins)) == null ? void 0 : _a.forEach((plugin) => {
1788
+ if (!plugin.personalizationSelectionAlgorithms) {
1789
+ return;
1790
+ }
1791
+ __privateSet(this, _personalizationSelectionAlgorithms, {
1792
+ ...__privateGet(this, _personalizationSelectionAlgorithms),
1793
+ ...plugin.personalizationSelectionAlgorithms
1794
+ });
1795
+ });
1796
+ (_b = __privateGet(this, _plugins)) == null ? void 0 : _b.forEach((plugin) => {
1635
1797
  if (!plugin.logDrain) {
1636
1798
  return;
1637
1799
  }
@@ -1682,7 +1844,7 @@ var Context = class {
1682
1844
  __privateGet(this, _mitt3).emit("quirksUpdated", quirks.quirks);
1683
1845
  __privateGet(this, _mitt3).emit("log", ["info", 4, quirks.quirks]);
1684
1846
  });
1685
- (_b = __privateGet(this, _plugins)) == null ? void 0 : _b.forEach((plugin) => {
1847
+ (_c = __privateGet(this, _plugins)) == null ? void 0 : _c.forEach((plugin) => {
1686
1848
  if (!plugin.init) {
1687
1849
  return;
1688
1850
  }
@@ -1932,7 +2094,17 @@ var Context = class {
1932
2094
  }
1933
2095
  /** Executes a personalized placement with a given set of variants */
1934
2096
  personalize(options) {
1935
- const value = personalizeVariations({
2097
+ var _a;
2098
+ const algorithmName = (_a = options.algorithm) != null ? _a : TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM;
2099
+ const algorithm = __privateGet(this, _personalizationSelectionAlgorithms)[algorithmName];
2100
+ if (!algorithm) {
2101
+ __privateGet(this, _mitt3).emit("log", ["warn", 304, { algorithm: algorithmName }]);
2102
+ return {
2103
+ personalized: false,
2104
+ variations: []
2105
+ };
2106
+ }
2107
+ const value = algorithm({
1936
2108
  ...options,
1937
2109
  context: this,
1938
2110
  onLogMessage: (message) => __privateGet(this, _mitt3).emit("log", message)
@@ -2006,6 +2178,7 @@ var Context = class {
2006
2178
  __privateGet(this, _mitt3).emit("personalizationResult", event);
2007
2179
  }
2008
2180
  };
2181
+ _personalizationSelectionAlgorithms = new WeakMap();
2009
2182
  _serverTransitionState = new WeakMap();
2010
2183
  _scores = new WeakMap();
2011
2184
  _state = new WeakMap();
@@ -2601,6 +2774,10 @@ var messageContent = {
2601
2774
  301: ({ id, op }) => ["personalization", `testing variation ${id} (${op === "|" ? "OR" : "AND"})`],
2602
2775
  302: ({ matched, description }) => ["personalization", `${description} is ${matched}`],
2603
2776
  303: (selected) => ["personalization", selected ? "selected variation" : "did not select variation"],
2777
+ 304: ({ algorithm }) => [
2778
+ "personalization",
2779
+ `personalization selection algorithm '${algorithm}' not found. Hiding placement.`
2780
+ ],
2604
2781
  // TESTING
2605
2782
  400: (name) => ["testing", `executing A/B test '${name}'`],
2606
2783
  401: (testName) => ["testing", `${testName} is not registered in the manifest; it will not be run.`],
@@ -2706,7 +2883,9 @@ function parseQuickConnect(serialized) {
2706
2883
  PAIR_SEP,
2707
2884
  QUIRK_SEP,
2708
2885
  SERVER_STATE_ID,
2886
+ STRONGEST_SCORE_PERSONALIZATION_ALGORITHM,
2709
2887
  ScriptType,
2888
+ TOP_DOWN_CRITERIA_PERSONALIZATION_ALGORITHM,
2710
2889
  TYPE_SEP,
2711
2890
  TransitionDataStore,
2712
2891
  UNIFORM_DEFAULT_COOKIE_NAME,
@@ -2748,5 +2927,7 @@ function parseQuickConnect(serialized) {
2748
2927
  serializePersonalizeVariants,
2749
2928
  serializeQuickConnect,
2750
2929
  serializeQuirks,
2751
- testVariations
2930
+ strongestScorePersonalizationSelectionAlgorithm,
2931
+ testVariations,
2932
+ topDownCriteriaPersonalizationSelectionAlgorithm
2752
2933
  });