import { v4 as uuidV4 } from "uuid";
import seedrandom from "seedrandom";
const AB_TESTS = {
itemcount: [
{ value: "10", weight: 50 },
{ value: "15", weight: 50 },
],
buttonsize: [
{ value: "small", weight: 50 },
{ value: "medium", weight: 25 },
{ value: "large", weight: 25 },
],
};
function applyTestToVisitor(visitorId) {
const results = {};
for (const [testId, entries] of Object.entries(AB_TESTS)) {
const rng = seedrandom(visitorId + testId);
const weightsTotal = entries.reduce((acc, entry) => acc + entry.weight, 0);
let random = rng() * weightsTotal;
const bucket = entries.find((entry) => {
random = random - entry.weight;
return random < 0;
});
results[testId] = bucket.value;
}
return results;
}
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
async function handleRequest(event) {
const req = event.request;
const cookies = (req.headers.get("Cookie") ?? "")
.split(";")
.reduce((acc, val) => {
const segment = val.trim();
const pos = segment.indexOf("=");
if (pos !== -1) {
const key = segment.slice(0, pos);
const value = decodeURIComponent(segment.slice(pos + 1));
acc[key] = value;
}
return acc;
}, {});
let newCookie = false;
let abTestVisitorId = cookies["ab_cid"];
if (abTestVisitorId == null) {
abTestVisitorId = uuidV4();
newCookie = true;
}
if (cookies["ab_cid"] != null) {
delete cookies["ab_cid"];
const reqCookie = Object.entries(cookies)
.map(([name, value]) => {
return name + "=" + encodeURIComponent(value);
})
.join("; ");
if (reqCookie !== "") {
req.headers.set("Cookie", reqCookie);
} else {
req.headers.delete("Cookie");
}
}
const testResults = applyTestToVisitor(abTestVisitorId);
for (const [testId, result] of Object.entries(testResults)) {
req.headers.set(`Fastly-ABTest-${testId}`, result);
}
const backendResponse = await fetch(req, {
backend: "origin_0",
});
if (newCookie) {
backendResponse.headers.append(
"Set-Cookie",
`ab_cid=${encodeURIComponent(
abTestVisitorId
)}; Max-Age: 31536000; Path=/; Secure; HttpOnly`
);
backendResponse.headers.set("Cache-Control", "no-store");
}
return backendResponse;
}