1/* 2 * Copyright 2022 Adobe. All rights reserved. 3 * This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. You may obtain a copy 5 * of the License at http://www.apache.org/licenses/LICENSE-2.0 6 * 7 * Unless required by applicable law or agreed to in writing, software distributed under 8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 9 * OF ANY KIND, either express or implied. See the License for the specific language 10 * governing permissions and limitations under the License. 11 */ 12 13var storage = window.sessionStorage; 14 15function murmurhash3_32_gc(key, seed) { 16 var remainder = key.length & 3; 17 var bytes = key.length - remainder; 18 var c1 = 0xcc9e2d51; 19 var c2 = 0x1b873593; 20 var h1 = seed; 21 var k1; 22 var h1b; 23 var i = 0; 24 while (i < bytes) { 25 k1 = 26 ((key.charCodeAt(i) & 0xff)) | 27 ((key.charCodeAt(++i) & 0xff) << 8) | 28 ((key.charCodeAt(++i) & 0xff) << 16) | 29 ((key.charCodeAt(++i) & 0xff) << 24); 30 ++i; 31 k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; 32 k1 = (k1 << 15) | (k1 >>> 17); 33 k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; 34 h1 ^= k1; 35 h1 = (h1 << 13) | (h1 >>> 19); 36 h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; 37 h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); 38 } 39 k1 = 0; 40 switch (remainder) { 41 case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; 42 case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; 43 case 1: 44 k1 ^= (key.charCodeAt(i) & 0xff); 45 k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; 46 k1 = (k1 << 15) | (k1 >>> 17); 47 k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; 48 h1 ^= k1; 49 } 50 h1 ^= key.length; 51 h1 ^= h1 >>> 16; 52 h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; 53 h1 ^= h1 >>> 13; 54 h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; 55 h1 ^= h1 >>> 16; 56 return h1 >>> 0; 57} 58 59var TOTAL_BUCKETS = 10000; 60function getBucket(saltedId) { 61 var hash = murmurhash3_32_gc(saltedId, 0); 62 var hashFixedBucket = Math.abs(hash) % TOTAL_BUCKETS; 63 var bucket = hashFixedBucket / TOTAL_BUCKETS; 64 return bucket; 65} 66function pickWithWeightsBucket(allocationPercentages, treatments, bucket) { 67 var sum = allocationPercentages.reduce(function (partialSum, a) { return partialSum + a; }, 0); 68 var partialSum = 0.0; 69 for (var i = 0; i < treatments.length; i++) { 70 partialSum += Number(allocationPercentages[i].toFixed(2)) / sum; 71 if (bucket > partialSum) { 72 continue; 73 } 74 return treatments[i]; 75 } 76} 77function assignTreatmentByVisitor(experimentid, identityId, allocationPercentages, treatments) { 78 var saltedId = experimentid + '.' + identityId; 79 var bucketId = getBucket(saltedId); 80 var treatmentId = pickWithWeightsBucket(allocationPercentages, treatments, bucketId); 81 return { 82 treatmentId: treatmentId, 83 bucketId: bucketId 84 }; 85} 86 87var LOCAL_STORAGE_KEY = 'unified-decisioning-experiments'; 88function assignTreatment(allocationPercentages, treatments) { 89 var random = Math.random() * 100; 90 var i = treatments.length; 91 while (random > 0 && i > 0) { 92 i -= 1; 93 random -= +allocationPercentages[i]; 94 } 95 return treatments[i]; 96} 97function getLastExperimentTreatment(experimentId) { 98 var experimentsStr = storage.getItem(LOCAL_STORAGE_KEY); 99 if (experimentsStr) { 100 var experiments = JSON.parse(experimentsStr); 101 if (experiments[experimentId]) { 102 return experiments[experimentId].treatment; 103 } 104 } 105 return null; 106} 107function setLastExperimentTreatment(experimentId, treatment) { 108 var experimentsStr = storage.getItem(LOCAL_STORAGE_KEY); 109 var experiments = experimentsStr ? JSON.parse(experimentsStr) : {}; 110 var now = new Date(); 111 var expKeys = Object.keys(experiments); 112 expKeys.forEach(function (key) { 113 var date = new Date(experiments[key].date); 114 if ((now.getTime() - date.getTime()) > (1000 * 86400 * 30)) { 115 delete experiments[key]; 116 } 117 }); 118 var date = now.toISOString().split('T')[0]; 119 experiments[experimentId] = { treatment: treatment, date: date }; 120 storage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(experiments)); 121} 122function assignTreatmentByDevice(experimentId, allocationPercentages, treatments) { 123 var cachedTreatmentId = getLastExperimentTreatment(experimentId); 124 var treatmentIdResponse; 125 if (!cachedTreatmentId || !treatments.includes(cachedTreatmentId)) { 126 var assignedTreatmentId = assignTreatment(allocationPercentages, treatments); 127 setLastExperimentTreatment(experimentId, assignedTreatmentId); 128 treatmentIdResponse = assignedTreatmentId; 129 } 130 else { 131 treatmentIdResponse = cachedTreatmentId; 132 } 133 return { 134 treatmentId: treatmentIdResponse 135 }; 136} 137 138var RandomizationUnit = { 139 VISITOR: 'VISITOR', 140 DEVICE: 'DEVICE' 141}; 142function evaluateExperiment(context, experiment) { 143 var experimentId = experiment.id, identityNamespace = experiment.identityNamespace, _a = experiment.randomizationUnit, randomizationUnit = _a === void 0 ? RandomizationUnit.VISITOR : _a;
144 var identityMap = context.identityMap; 145 var treatments = experiment.treatments.map(function (item) { return item.id; }); 146 var allocationPercentages = experiment.treatments.map(function (item) { return item.allocationPercentage; }); 147 var treatmentAssignment = null; 148 switch (randomizationUnit) { 149 case RandomizationUnit.VISITOR: { 150 var identityId = identityMap[identityNamespace][0].id; 151 treatmentAssignment = assignTreatmentByVisitor(experimentId, identityId, allocationPercentages, treatments); 152 break; 153 } 154 case RandomizationUnit.DEVICE: { 155 treatmentAssignment = assignTreatmentByDevice(experimentId, allocationPercentages, treatments); 156 break; 157 } 158 default: 159 throw new Error("Unknow randomization unit"); 160 } 161 var evaluationResponse = { 162 experimentId: experimentId, 163 hashedBucket: treatmentAssignment.bucketId, 164 treatment: { 165 id: treatmentAssignment.treatmentId 166 } 167 }; 168 return evaluationResponse; 169} 170 171function traverseDecisionTree(decisionNodesMap, context, currentNodeId) { 172 var _a = decisionNodesMap[currentNodeId], experiment = _a.experiment, type = _a.type; 173 if (type === 'EXPERIMENTATION') { 174 var treatment = evaluateExperiment(context, experiment).treatment; 175 return [treatment]; 176 } 177} 178function evaluateDecisionPolicy(decisionPolicy, context) { 179 if (context.storage && context.storage instanceof Storage) { 180 storage = context.storage; 181 } 182 var decisionNodesMap = {}; 183 decisionPolicy.decisionNodes.forEach(function (item) { 184 decisionNodesMap[item['id']] = item; 185 }); 186 var items = traverseDecisionTree(decisionNodesMap, context, decisionPolicy.rootDecisionNodeId); 187 return { 188 items: items 189 }; 190} 191 192export const ued = { evaluateDecisionPolicy };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.