@coframe/sdk 1.1.14 → 1.1.21
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/README.md +28 -1
- package/dist/pkg/cf.cjs.js +283 -113
- package/dist/pkg/cf.esm.mjs +283 -113
- package/dist/pkg/react.cjs.js +2 -1
- package/dist/pkg/react.esm.mjs +2 -1
- package/dist/pkg/server.cjs.js +26 -5
- package/dist/pkg/server.esm.mjs +26 -5
- package/dist/pkg/types/index.d.ts +7 -0
- package/dist/pkg/types/react.d.ts +7 -0
- package/dist/pkg/types/server.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,13 +40,40 @@ await init({
|
|
|
40
40
|
|
|
41
41
|
`init()` accepts `config` and optional `projectData`, so npm consumers do not need to manually populate `window.CFQ` first.
|
|
42
42
|
|
|
43
|
+
### Runtime endpoint overrides
|
|
44
|
+
|
|
45
|
+
Private-CDN integrations can override the browser SDK's project and ingest
|
|
46
|
+
endpoints:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
await init({
|
|
50
|
+
config: {
|
|
51
|
+
projectId: 'your-project-id',
|
|
52
|
+
projectUrl: 'https://cdn.example.com/coframe/project.json',
|
|
53
|
+
ingestUrl: 'https://www.example.com/ingest/v2',
|
|
54
|
+
enableDebug: false,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`projectUrl` must return a raw, complete `Project` JSON object. `ingestUrl`
|
|
60
|
+
replaces the ingest base URL (`https://ingest.app.coframe.com/ingest/v2`); the
|
|
61
|
+
SDK still appends the category and project path, so beacons go to
|
|
62
|
+
`<ingestUrl>/batched_events/<projectId>/`, `<ingestUrl>/batched_monitoring/<projectId>/`,
|
|
63
|
+
`<ingestUrl>/session_result/`, and `<ingestUrl>/identify/<projectId>/`. A proxy
|
|
64
|
+
only has to forward `<ingestUrl>/*` to `https://ingest.app.coframe.com/ingest/v2/*`
|
|
65
|
+
with the path unchanged. Both overrides are optional and retain the compiled
|
|
66
|
+
Coframe endpoints when omitted.
|
|
67
|
+
|
|
43
68
|
### URL controls
|
|
44
69
|
|
|
45
70
|
The npm entry uses the same browser URL controls as the script-tag and Edge
|
|
46
71
|
installations:
|
|
47
72
|
|
|
48
73
|
- `cf_disable` skips initialization and reveals any antiflicker state.
|
|
49
|
-
- `coframe_variant_id` previews one or more comma-separated variant IDs.
|
|
74
|
+
- `coframe_variant_id` previews one or more comma-separated variant IDs. Add
|
|
75
|
+
`rollouts` to the list to apply them on top of the rolled-out winners a
|
|
76
|
+
production visitor would get.
|
|
50
77
|
- `coframe_should_send_events=true` allows events during preview for testing.
|
|
51
78
|
|
|
52
79
|
Debug tools are allowed by default. Open the Launcher with `cf_preview=true` and
|
package/dist/pkg/cf.cjs.js
CHANGED
|
@@ -24,6 +24,19 @@ if (!Array.prototype.find) {
|
|
|
24
24
|
enumerable: false,
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
+
if (!Array.prototype.includes) {
|
|
28
|
+
Object.defineProperty(Array.prototype, 'includes', {
|
|
29
|
+
value: function (searchElement) {
|
|
30
|
+
if (this == null) {
|
|
31
|
+
throw new TypeError('Array.prototype.includes called on null or undefined');
|
|
32
|
+
}
|
|
33
|
+
return Array.prototype.indexOf.call(this, searchElement) !== -1;
|
|
34
|
+
},
|
|
35
|
+
writable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
enumerable: false,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
27
40
|
if (!Array.prototype.flatMap) {
|
|
28
41
|
Object.defineProperty(Array.prototype, 'flatMap', {
|
|
29
42
|
value: function (callback, thisArg) {
|
|
@@ -1066,10 +1079,41 @@ function warnEventsNotSent() {
|
|
|
1066
1079
|
}
|
|
1067
1080
|
}
|
|
1068
1081
|
|
|
1069
|
-
const SDK_VERSION = '2.
|
|
1082
|
+
const SDK_VERSION = '2.7.0';
|
|
1070
1083
|
const queuedEvents = [];
|
|
1071
1084
|
const queuedMonitoringEvents = [];
|
|
1072
1085
|
const queuedSessions = [];
|
|
1086
|
+
let ingestUrl;
|
|
1087
|
+
const COMPILED_INGEST_BASE = "https://ingest.app.coframe.com/ingest/v2";
|
|
1088
|
+
function configureIngestUrl(url) {
|
|
1089
|
+
const trimmed = url === null || url === void 0 ? void 0 : url.trim().replace(/\/+$/, '');
|
|
1090
|
+
ingestUrl = trimmed || undefined;
|
|
1091
|
+
}
|
|
1092
|
+
function resolveIngestUrl(defaultUrl) {
|
|
1093
|
+
if (!ingestUrl || !defaultUrl.startsWith(COMPILED_INGEST_BASE))
|
|
1094
|
+
return defaultUrl;
|
|
1095
|
+
return ingestUrl + defaultUrl.slice(COMPILED_INGEST_BASE.length);
|
|
1096
|
+
}
|
|
1097
|
+
const MAX_BEACON_BATCH_BYTES = 48 * 1024;
|
|
1098
|
+
function getBeaconPayloadBytes(data) {
|
|
1099
|
+
return new Blob([JSON.stringify(data)]).size;
|
|
1100
|
+
}
|
|
1101
|
+
function chunkEventsByByteLimit(events) {
|
|
1102
|
+
const chunks = [];
|
|
1103
|
+
let current = [];
|
|
1104
|
+
for (const event of events) {
|
|
1105
|
+
if (current.length > 0 &&
|
|
1106
|
+
getBeaconPayloadBytes([...current, event]) > MAX_BEACON_BATCH_BYTES) {
|
|
1107
|
+
chunks.push(current);
|
|
1108
|
+
current = [];
|
|
1109
|
+
}
|
|
1110
|
+
current.push(event);
|
|
1111
|
+
}
|
|
1112
|
+
if (current.length > 0) {
|
|
1113
|
+
chunks.push(current);
|
|
1114
|
+
}
|
|
1115
|
+
return chunks;
|
|
1116
|
+
}
|
|
1073
1117
|
function queueMonitoringEvent(coframe, data) {
|
|
1074
1118
|
queuedMonitoringEvents.push(data);
|
|
1075
1119
|
if (queuedMonitoringEvents.length >= coframe.batchSize) {
|
|
@@ -1122,11 +1166,15 @@ function flushQueuedEvents(projectId) {
|
|
|
1122
1166
|
return;
|
|
1123
1167
|
}
|
|
1124
1168
|
if (queuedEvents.length > 0) {
|
|
1125
|
-
|
|
1169
|
+
for (const chunk of chunkEventsByByteLimit(queuedEvents)) {
|
|
1170
|
+
sendEventToBackend(projectId, chunk, 'batched_events');
|
|
1171
|
+
}
|
|
1126
1172
|
queuedEvents.length = 0;
|
|
1127
1173
|
}
|
|
1128
1174
|
if (queuedMonitoringEvents.length > 0) {
|
|
1129
|
-
|
|
1175
|
+
for (const chunk of chunkEventsByByteLimit(queuedMonitoringEvents)) {
|
|
1176
|
+
sendEventToBackend(projectId, chunk, 'batched_monitoring');
|
|
1177
|
+
}
|
|
1130
1178
|
queuedMonitoringEvents.length = 0;
|
|
1131
1179
|
}
|
|
1132
1180
|
if (queuedSessions.length > 0) {
|
|
@@ -1228,7 +1276,7 @@ function sendPageEngagementEvent(coframe, eventType, event, { projectId, session
|
|
|
1228
1276
|
}
|
|
1229
1277
|
}
|
|
1230
1278
|
function sendEventToBackend(projectId, data, eventCategory = 'events') {
|
|
1231
|
-
const eventsEndpoint = `${"https://ingest.app.coframe.com/ingest/v2"}/${eventCategory}/${projectId}
|
|
1279
|
+
const eventsEndpoint = resolveIngestUrl(`${"https://ingest.app.coframe.com/ingest/v2"}/${eventCategory}/${projectId}/`);
|
|
1232
1280
|
if (navigator.sendBeacon) {
|
|
1233
1281
|
const textData = JSON.stringify(data);
|
|
1234
1282
|
const blob = new Blob([textData], { type: 'text/plain' });
|
|
@@ -1241,7 +1289,7 @@ function sendEventToBackend(projectId, data, eventCategory = 'events') {
|
|
|
1241
1289
|
}
|
|
1242
1290
|
}
|
|
1243
1291
|
function createSession(data) {
|
|
1244
|
-
const sessionApiUrl = `${"https://ingest.app.coframe.com/ingest/v2"}/session_result
|
|
1292
|
+
const sessionApiUrl = resolveIngestUrl(`${"https://ingest.app.coframe.com/ingest/v2"}/session_result/`);
|
|
1245
1293
|
if (navigator.sendBeacon) {
|
|
1246
1294
|
const textData = JSON.stringify(data);
|
|
1247
1295
|
const blob = new Blob([textData], { type: 'text/plain' });
|
|
@@ -1253,11 +1301,14 @@ function createSession(data) {
|
|
|
1253
1301
|
console.warn('[Coframe] cannot send event beacon, feature unavailable');
|
|
1254
1302
|
}
|
|
1255
1303
|
}
|
|
1256
|
-
async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers) {
|
|
1304
|
+
async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer) {
|
|
1257
1305
|
const payload = { projectId, currentUrl };
|
|
1258
1306
|
if (previewVariantId) {
|
|
1259
1307
|
payload.previewVariantId = previewVariantId;
|
|
1260
1308
|
}
|
|
1309
|
+
if (referrer !== undefined) {
|
|
1310
|
+
payload.referrer = referrer;
|
|
1311
|
+
}
|
|
1261
1312
|
const safe = sanitizeUserContext(userContext);
|
|
1262
1313
|
if (safe) {
|
|
1263
1314
|
payload.userContext = safe;
|
|
@@ -1278,6 +1329,26 @@ async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext,
|
|
|
1278
1329
|
return null;
|
|
1279
1330
|
}
|
|
1280
1331
|
}
|
|
1332
|
+
async function fetchProject(projectUrl, projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer) {
|
|
1333
|
+
if (!projectUrl) {
|
|
1334
|
+
return fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer);
|
|
1335
|
+
}
|
|
1336
|
+
try {
|
|
1337
|
+
const response = await fetch(projectUrl, { headers, signal });
|
|
1338
|
+
if (!response.ok)
|
|
1339
|
+
return null;
|
|
1340
|
+
return {
|
|
1341
|
+
project: (await response.json()),
|
|
1342
|
+
personalizationHeaders: {},
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
catch (error) {
|
|
1346
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
1347
|
+
return null;
|
|
1348
|
+
}
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1281
1352
|
|
|
1282
1353
|
const isPageElement = (element) => {
|
|
1283
1354
|
return (element === document.body ||
|
|
@@ -2129,6 +2200,78 @@ function resolveLaneWinners(claims, reachableByLane, lanes, userToken) {
|
|
|
2129
2200
|
return excluded;
|
|
2130
2201
|
}
|
|
2131
2202
|
|
|
2203
|
+
var CoInspectorMessageType;
|
|
2204
|
+
(function (CoInspectorMessageType) {
|
|
2205
|
+
CoInspectorMessageType["onReady"] = "onReady";
|
|
2206
|
+
CoInspectorMessageType["onHoverElement"] = "onHoverElement";
|
|
2207
|
+
CoInspectorMessageType["onClickElement"] = "onClickElement";
|
|
2208
|
+
CoInspectorMessageType["onPageUrlChange"] = "onPageUrlChange";
|
|
2209
|
+
CoInspectorMessageType["onCreateNewElement"] = "onCreateNewElement";
|
|
2210
|
+
CoInspectorMessageType["onCancelNewElement"] = "onCancelNewElement";
|
|
2211
|
+
CoInspectorMessageType["onCopy"] = "onCopy";
|
|
2212
|
+
CoInspectorMessageType["onCloseDomInspectorPanel"] = "onCloseDomInspectorPanel";
|
|
2213
|
+
CoInspectorMessageType["onEditElement"] = "onEditElement";
|
|
2214
|
+
CoInspectorMessageType["onDeleteElement"] = "onDeleteElement";
|
|
2215
|
+
CoInspectorMessageType["onScroll"] = "onScroll";
|
|
2216
|
+
CoInspectorMessageType["setAllRects"] = "setAllRects";
|
|
2217
|
+
CoInspectorMessageType["onUpdateRects"] = "onUpdateRects";
|
|
2218
|
+
CoInspectorMessageType["DisableMouseTracking"] = "DisableMouseTracking";
|
|
2219
|
+
CoInspectorMessageType["EnableMouseTracking"] = "EnableMouseTracking";
|
|
2220
|
+
CoInspectorMessageType["onLeaveElement"] = "onLeaveElement";
|
|
2221
|
+
})(CoInspectorMessageType || (CoInspectorMessageType = {}));
|
|
2222
|
+
var CoMainSiteMessageType;
|
|
2223
|
+
(function (CoMainSiteMessageType) {
|
|
2224
|
+
CoMainSiteMessageType["ActiveInspect"] = "ActiveInspect";
|
|
2225
|
+
CoMainSiteMessageType["FreezeInspect"] = "FreezeInspect";
|
|
2226
|
+
CoMainSiteMessageType["SetElementChanges"] = "SetElementChanges";
|
|
2227
|
+
CoMainSiteMessageType["ShowDomInspector"] = "ShowDomInspector";
|
|
2228
|
+
CoMainSiteMessageType["RestoreOriginal"] = "RestoreOriginal";
|
|
2229
|
+
CoMainSiteMessageType["CreateNewElement"] = "CreateNewElement";
|
|
2230
|
+
CoMainSiteMessageType["ClickHoverElement"] = "ClickHoverElement";
|
|
2231
|
+
CoMainSiteMessageType["ReloadIframe"] = "ReloadIframe";
|
|
2232
|
+
})(CoMainSiteMessageType || (CoMainSiteMessageType = {}));
|
|
2233
|
+
|
|
2234
|
+
const COFRAME_PREVIEW_SESSION_STORAGE_KEY = 'coframe.preview_variant';
|
|
2235
|
+
const COFRAME_PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2236
|
+
const ROLLOUTS_PREVIEW_ID = 'rollouts';
|
|
2237
|
+
function splitPreviewVariantIds(raw) {
|
|
2238
|
+
var _a;
|
|
2239
|
+
return ((_a = raw === null || raw === void 0 ? void 0 : raw.split(',').map((id) => id.trim()).filter(Boolean)) !== null && _a !== void 0 ? _a : []);
|
|
2240
|
+
}
|
|
2241
|
+
function isPreviewSentinel(id) {
|
|
2242
|
+
return id === GLOBAL_HOLDOUT || id === ROLLOUTS_PREVIEW_ID;
|
|
2243
|
+
}
|
|
2244
|
+
function normalizePreviewVariantId(raw) {
|
|
2245
|
+
const ids = splitPreviewVariantIds(raw);
|
|
2246
|
+
return ids.length > 0 ? ids.join(',') : null;
|
|
2247
|
+
}
|
|
2248
|
+
function readPreviewVariantIdFromUrl(url = window.location.href) {
|
|
2249
|
+
try {
|
|
2250
|
+
return normalizePreviewVariantId(getUrlSearchParams(url).get(COFRAME_PREVIEW_VARIANT_QUERY_PARAM));
|
|
2251
|
+
}
|
|
2252
|
+
catch (_a) {
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
function readStoredPreviewVariantId() {
|
|
2257
|
+
try {
|
|
2258
|
+
return normalizePreviewVariantId(window.sessionStorage.getItem(COFRAME_PREVIEW_SESSION_STORAGE_KEY));
|
|
2259
|
+
}
|
|
2260
|
+
catch (_a) {
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
function resolvePreviewSource(url = window.location.href) {
|
|
2265
|
+
const urlVariantId = readPreviewVariantIdFromUrl(url);
|
|
2266
|
+
const storedVariantId = readStoredPreviewVariantId();
|
|
2267
|
+
return {
|
|
2268
|
+
effectiveVariantId: urlVariantId || storedVariantId,
|
|
2269
|
+
isKeptForSession: Boolean(storedVariantId && storedVariantId === (urlVariantId || storedVariantId)),
|
|
2270
|
+
storedVariantId,
|
|
2271
|
+
urlVariantId,
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2132
2275
|
function isElementVisible(el) {
|
|
2133
2276
|
const rect = el.getBoundingClientRect();
|
|
2134
2277
|
return rect.top >= 0 && rect.bottom <= window.innerHeight;
|
|
@@ -2168,11 +2311,11 @@ function checkActivationCondition(condition, personalizationHeaders, currentUrl,
|
|
|
2168
2311
|
}
|
|
2169
2312
|
if (condition.url &&
|
|
2170
2313
|
condition.url !== '*' &&
|
|
2171
|
-
!
|
|
2314
|
+
!urlConditionMatches(condition.url, currentUrl, false, logger)) {
|
|
2172
2315
|
return false;
|
|
2173
2316
|
}
|
|
2174
2317
|
if (condition.base_url_matches &&
|
|
2175
|
-
!
|
|
2318
|
+
!urlConditionMatches(condition.base_url_matches, currentUrl, true, logger)) {
|
|
2176
2319
|
return false;
|
|
2177
2320
|
}
|
|
2178
2321
|
if (condition.url_regex) {
|
|
@@ -2286,6 +2429,18 @@ const normalizeUrl = (url, baseUrlMatch) => {
|
|
|
2286
2429
|
}
|
|
2287
2430
|
return `${hostname}${pathname}${parsedUrl.search}${parsedUrl.hash}`;
|
|
2288
2431
|
};
|
|
2432
|
+
function urlConditionMatches(conditionUrl, currentUrl, baseUrlMatch, logger) {
|
|
2433
|
+
try {
|
|
2434
|
+
return urlsMatch(conditionUrl, currentUrl, baseUrlMatch);
|
|
2435
|
+
}
|
|
2436
|
+
catch (_a) {
|
|
2437
|
+
logger.error('Error parsing URL condition', 'url_condition_error', {
|
|
2438
|
+
conditionUrl,
|
|
2439
|
+
currentUrl,
|
|
2440
|
+
});
|
|
2441
|
+
return false;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2289
2444
|
function urlsMatch(conditionUrl, currentUrl, baseUrlMatch) {
|
|
2290
2445
|
if (currentUrl.includes('localhost') || currentUrl.includes('0.0.0.0')) {
|
|
2291
2446
|
const conditionUrlObj = new URL(conditionUrl);
|
|
@@ -2396,15 +2551,15 @@ const inRange = (bins, scale, seed, uid) => {
|
|
|
2396
2551
|
}
|
|
2397
2552
|
return false;
|
|
2398
2553
|
};
|
|
2399
|
-
function checkAllAudiences(metadata,
|
|
2554
|
+
function checkAllAudiences(metadata, ctx) {
|
|
2400
2555
|
var _a;
|
|
2401
2556
|
const audienceIds = (_a = metadata.activation_condition.audience_ids) !== null && _a !== void 0 ? _a : [];
|
|
2402
2557
|
let result = true;
|
|
2403
2558
|
for (const audienceId of audienceIds) {
|
|
2404
|
-
const audience = project.audiences[audienceId];
|
|
2559
|
+
const audience = ctx.project.audiences[audienceId];
|
|
2405
2560
|
if (!audience)
|
|
2406
2561
|
continue;
|
|
2407
|
-
const activated = checkActivationCondition(audience.selector, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce }), logger);
|
|
2562
|
+
const activated = checkActivationCondition(audience.selector, ctx.personalizationHeaders, ctx.currentPageUrl, ctx.languages, ctx.browserPlatform, ctx.userToken, ctx.userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce: ctx.nonce }), ctx.logger);
|
|
2408
2563
|
if (activated === false)
|
|
2409
2564
|
return false;
|
|
2410
2565
|
if (activated !== true)
|
|
@@ -2413,9 +2568,8 @@ function checkAllAudiences(metadata, project, personalizationHeaders, currentPag
|
|
|
2413
2568
|
return result;
|
|
2414
2569
|
}
|
|
2415
2570
|
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false) {
|
|
2416
|
-
var _a
|
|
2571
|
+
var _a;
|
|
2417
2572
|
const experimentsInfo = [];
|
|
2418
|
-
const variantsAndElements = [];
|
|
2419
2573
|
const cloakBot = !!((_a = project.settings) === null || _a === void 0 ? void 0 : _a.bot_cloaking_enabled) &&
|
|
2420
2574
|
personalizationHeaders['x-is-bot'] === 'true';
|
|
2421
2575
|
const buildResponse = ({ variantsAndElements, experimentsInfo, hasActivatedFrames: frameActivated = false, laneClaims: meClaims = laneClaims, }) => {
|
|
@@ -2426,39 +2580,30 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2426
2580
|
laneClaims: meClaims,
|
|
2427
2581
|
};
|
|
2428
2582
|
};
|
|
2429
|
-
const
|
|
2583
|
+
const userVisitCount = Number.parseInt(getCookie(VISIT_COUNT_COOKIE_KEY) || '0', 10);
|
|
2584
|
+
const ctx = {
|
|
2585
|
+
project,
|
|
2586
|
+
personalizationHeaders,
|
|
2587
|
+
userToken,
|
|
2588
|
+
currentPageUrl,
|
|
2589
|
+
languages,
|
|
2590
|
+
browserPlatform,
|
|
2591
|
+
userVisitCount,
|
|
2592
|
+
nonce,
|
|
2593
|
+
logger,
|
|
2594
|
+
};
|
|
2595
|
+
const validatedClaims = validateClaims(laneClaims, project.lanes);
|
|
2596
|
+
const previewVariantIds = splitPreviewVariantIds(previewVariantId);
|
|
2430
2597
|
if (previewVariantIds.length > 0) {
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2433
|
-
variant,
|
|
2434
|
-
elements: frame.elements,
|
|
2435
|
-
}))))
|
|
2436
|
-
.filter((item) => previewVariantIds.includes(item.variant.id));
|
|
2437
|
-
const skipAlert = previewVariantIds.some((id) => id === project.holdout_info.holdout_variant_id || id === GLOBAL_HOLDOUT);
|
|
2438
|
-
if (filteredVariantsAndElements.length > 0) {
|
|
2598
|
+
const variantsAndElements = previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback);
|
|
2599
|
+
if (variantsAndElements) {
|
|
2439
2600
|
return buildResponse({
|
|
2440
|
-
variantsAndElements
|
|
2441
|
-
hasActivatedFrames:
|
|
2601
|
+
variantsAndElements,
|
|
2602
|
+
hasActivatedFrames: variantsAndElements.length > 0,
|
|
2442
2603
|
});
|
|
2443
2604
|
}
|
|
2444
|
-
if (!skipAlert && !silentFallback) {
|
|
2445
|
-
window.alert(`[Coframe] Variant preview unavailable: ${previewVariantId}.`);
|
|
2446
|
-
}
|
|
2447
|
-
if (silentFallback) ;
|
|
2448
|
-
else {
|
|
2449
|
-
return buildResponse({});
|
|
2450
|
-
}
|
|
2451
2605
|
}
|
|
2452
|
-
const
|
|
2453
|
-
const validatedClaims = validateClaims(laneClaims, project.lanes);
|
|
2454
|
-
const activeFrames = project.frames.filter((frame) => {
|
|
2455
|
-
if (!frame.experiments)
|
|
2456
|
-
return false;
|
|
2457
|
-
const frameActivated = checkActivationCondition(frame.activation_condition, personalizationHeaders, currentPageUrl, languages, browserPlatform, userToken, userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce }), logger);
|
|
2458
|
-
if (!frameActivated)
|
|
2459
|
-
return false;
|
|
2460
|
-
return true;
|
|
2461
|
-
});
|
|
2606
|
+
const activeFrames = activateFrames(ctx);
|
|
2462
2607
|
if (activeFrames.length === 0) {
|
|
2463
2608
|
return buildResponse({ laneClaims: validatedClaims });
|
|
2464
2609
|
}
|
|
@@ -2476,7 +2621,6 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2476
2621
|
level: GLOBAL_HOLDOUT,
|
|
2477
2622
|
});
|
|
2478
2623
|
return buildResponse({
|
|
2479
|
-
variantsAndElements,
|
|
2480
2624
|
experimentsInfo,
|
|
2481
2625
|
hasActivatedFrames: true,
|
|
2482
2626
|
laneClaims: validatedClaims,
|
|
@@ -2491,7 +2635,27 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2491
2635
|
final_state: false,
|
|
2492
2636
|
level: GLOBAL_HOLDOUT,
|
|
2493
2637
|
});
|
|
2494
|
-
const
|
|
2638
|
+
const assignment = assignFrames(activeFrames, validatedClaims, ctx);
|
|
2639
|
+
return buildResponse({
|
|
2640
|
+
...assignment,
|
|
2641
|
+
experimentsInfo: [...experimentsInfo, ...assignment.experimentsInfo],
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
function activateFrames(ctx) {
|
|
2645
|
+
return ctx.project.frames.filter((frame) => {
|
|
2646
|
+
if (!frame.experiments)
|
|
2647
|
+
return false;
|
|
2648
|
+
const frameActivated = checkActivationCondition(frame.activation_condition, ctx.personalizationHeaders, ctx.currentPageUrl, ctx.languages, ctx.browserPlatform, ctx.userToken, ctx.userVisitCount, (js, args) => executeJs(js, args, { isExpression: true, nonce: ctx.nonce }), ctx.logger);
|
|
2649
|
+
if (!frameActivated)
|
|
2650
|
+
return false;
|
|
2651
|
+
return true;
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
2655
|
+
const { project, userToken, logger } = ctx;
|
|
2656
|
+
const experimentsInfo = [];
|
|
2657
|
+
const variantsAndElements = [];
|
|
2658
|
+
const evalAudiences = (metadata) => checkAllAudiences(metadata, ctx) !== false;
|
|
2495
2659
|
const audiencePass = new Map();
|
|
2496
2660
|
const reachableByLane = {};
|
|
2497
2661
|
for (const frame of activeFrames) {
|
|
@@ -2584,50 +2748,49 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2584
2748
|
variantsAndElements.push({ variant, elements });
|
|
2585
2749
|
}
|
|
2586
2750
|
}
|
|
2587
|
-
return
|
|
2751
|
+
return {
|
|
2588
2752
|
variantsAndElements,
|
|
2589
2753
|
experimentsInfo,
|
|
2590
2754
|
hasActivatedFrames,
|
|
2591
2755
|
laneClaims: updatedClaims,
|
|
2592
|
-
}
|
|
2756
|
+
};
|
|
2593
2757
|
}
|
|
2594
|
-
|
|
2595
|
-
const
|
|
2596
|
-
const
|
|
2597
|
-
|
|
2598
|
-
|
|
2758
|
+
function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback) {
|
|
2759
|
+
const previewRollouts = previewVariantIds.includes(ROLLOUTS_PREVIEW_ID);
|
|
2760
|
+
const requestedVariantIds = previewVariantIds.filter((id) => !isPreviewSentinel(id));
|
|
2761
|
+
const requestedVariants = findVariants(ctx.project.frames, requestedVariantIds);
|
|
2762
|
+
const requestedVariantsMissing = requestedVariantIds.length > 0 && requestedVariants.length === 0;
|
|
2763
|
+
if (requestedVariantsMissing && !previewRollouts && silentFallback) {
|
|
2599
2764
|
return null;
|
|
2600
|
-
const ids = raw
|
|
2601
|
-
.split(',')
|
|
2602
|
-
.map((id) => id.trim())
|
|
2603
|
-
.filter(Boolean);
|
|
2604
|
-
return ids.length > 0 ? ids.join(',') : null;
|
|
2605
|
-
}
|
|
2606
|
-
function readPreviewVariantIdFromUrl(url = window.location.href) {
|
|
2607
|
-
try {
|
|
2608
|
-
return normalizePreviewVariantId(getUrlSearchParams(url).get(COFRAME_PREVIEW_VARIANT_QUERY_PARAM));
|
|
2609
2765
|
}
|
|
2610
|
-
|
|
2611
|
-
|
|
2766
|
+
const requestedHoldout = requestedVariantIds.includes(ctx.project.holdout_info.holdout_variant_id);
|
|
2767
|
+
if (requestedVariantsMissing && !requestedHoldout && !silentFallback) {
|
|
2768
|
+
window.alert(`[Coframe] Variant preview unavailable: ${previewVariantIds.join(',')}.`);
|
|
2612
2769
|
}
|
|
2770
|
+
const rolledOutVariants = previewRollouts && !previewVariantIds.includes(GLOBAL_HOLDOUT)
|
|
2771
|
+
? assignFrames(keepRolledOutExperiments(activateFrames(ctx)), validatedClaims, ctx).variantsAndElements
|
|
2772
|
+
: [];
|
|
2773
|
+
return uniqueByVariantId([...rolledOutVariants, ...requestedVariants]);
|
|
2613
2774
|
}
|
|
2614
|
-
function
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
catch (_a) {
|
|
2619
|
-
return null;
|
|
2620
|
-
}
|
|
2775
|
+
function findVariants(frames, variantIds) {
|
|
2776
|
+
return frames.flatMap((frame) => frame.experiments.flatMap((experiment) => experiment.variants
|
|
2777
|
+
.filter((variant) => variantIds.includes(variant.id))
|
|
2778
|
+
.map((variant) => ({ variant, elements: frame.elements }))));
|
|
2621
2779
|
}
|
|
2622
|
-
function
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2780
|
+
function uniqueByVariantId(items) {
|
|
2781
|
+
const seen = {};
|
|
2782
|
+
return items.filter(({ variant }) => {
|
|
2783
|
+
if (seen[variant.id])
|
|
2784
|
+
return false;
|
|
2785
|
+
seen[variant.id] = true;
|
|
2786
|
+
return true;
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
function keepRolledOutExperiments(frames) {
|
|
2790
|
+
return frames.map((frame) => ({
|
|
2791
|
+
...frame,
|
|
2792
|
+
experiments: frame.experiments.filter((experiment) => experiment.metadata.serving_mode === 'winner'),
|
|
2793
|
+
}));
|
|
2631
2794
|
}
|
|
2632
2795
|
|
|
2633
2796
|
function buildSessionContext(isInitialApply) {
|
|
@@ -2913,29 +3076,27 @@ function createTreatmentAnalytics(Coframe, previewVariantId) {
|
|
|
2913
3076
|
});
|
|
2914
3077
|
}
|
|
2915
3078
|
}
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
});
|
|
2938
|
-
}
|
|
3079
|
+
const previewIds = splitPreviewVariantIds(previewVariantId).filter((id) => !isPreviewSentinel(id));
|
|
3080
|
+
for (const id of previewIds) {
|
|
3081
|
+
treatmentsData.push({
|
|
3082
|
+
impressionId: sessionId,
|
|
3083
|
+
userToken,
|
|
3084
|
+
experimentId: `preview_id_${id}`,
|
|
3085
|
+
holdoutExperimentId: `preview_holdout_id_${id}`,
|
|
3086
|
+
variantId: id,
|
|
3087
|
+
inGlobalHoldout: false,
|
|
3088
|
+
isBaseline: false,
|
|
3089
|
+
});
|
|
3090
|
+
treatmentsDataV2.push({
|
|
3091
|
+
coframe_experiment_id: `preview_id_${id}`,
|
|
3092
|
+
coframe_experiment_slug: `preview_id_${id}`,
|
|
3093
|
+
coframe_variant_id: id,
|
|
3094
|
+
coframe_variant_slug: id,
|
|
3095
|
+
coframe_user_token: userToken,
|
|
3096
|
+
coframe_impression_id: sessionId,
|
|
3097
|
+
coframe_in_global_holdout: false,
|
|
3098
|
+
coframe_in_exp_baseline: false,
|
|
3099
|
+
});
|
|
2939
3100
|
}
|
|
2940
3101
|
Coframe.treatmentsData = treatmentsData;
|
|
2941
3102
|
Coframe.treatmentsDataV2 = treatmentsDataV2;
|
|
@@ -3180,12 +3341,12 @@ async function ensureBody(Coframe, cdnProject, timeoutMs, nonce) {
|
|
|
3180
3341
|
}
|
|
3181
3342
|
const FLUSH_EVENTS_TIME = 1000 * 3;
|
|
3182
3343
|
let flushInterval = null;
|
|
3183
|
-
function setupFlushInterval(
|
|
3184
|
-
flushQueuedEvents(projectId);
|
|
3344
|
+
function setupFlushInterval(Coframe) {
|
|
3345
|
+
flushQueuedEvents(Coframe.projectId);
|
|
3185
3346
|
if (flushInterval !== null) {
|
|
3186
3347
|
clearInterval(flushInterval);
|
|
3187
3348
|
}
|
|
3188
|
-
flushInterval = setInterval(() => flushQueuedEvents(projectId), FLUSH_EVENTS_TIME);
|
|
3349
|
+
flushInterval = setInterval(() => flushQueuedEvents(Coframe.projectId), FLUSH_EVENTS_TIME);
|
|
3189
3350
|
}
|
|
3190
3351
|
function setupSPANavigation(Coframe, project, config, previewVariantId, isSessionVariant, applyVariants) {
|
|
3191
3352
|
var _a;
|
|
@@ -3213,6 +3374,7 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3213
3374
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
3214
3375
|
let navigationAbortController = null;
|
|
3215
3376
|
observeUrlChanges(false, async (newPageUrl, _oldPageUrl) => {
|
|
3377
|
+
var _a;
|
|
3216
3378
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
3217
3379
|
infiniteLoopProtection = 0;
|
|
3218
3380
|
if (navigationAbortController)
|
|
@@ -3220,27 +3382,33 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3220
3382
|
navigationAbortController = new AbortController();
|
|
3221
3383
|
const { signal } = navigationAbortController;
|
|
3222
3384
|
let pageProject = project;
|
|
3385
|
+
let pagePersonalizationHeaders = Coframe.personalizationHeaders;
|
|
3223
3386
|
const cached = getCachedFrames(newPageUrl);
|
|
3224
3387
|
if (cached) {
|
|
3225
3388
|
pageProject = cached.project;
|
|
3389
|
+
pagePersonalizationHeaders = {
|
|
3390
|
+
...cached.personalizationHeaders,
|
|
3391
|
+
'x-visit-url': newPageUrl,
|
|
3392
|
+
};
|
|
3226
3393
|
}
|
|
3227
3394
|
else {
|
|
3228
3395
|
try {
|
|
3229
3396
|
const preview = resolvePreview(newPageUrl, previewVariantId, isSessionVariant);
|
|
3230
|
-
const result = await
|
|
3397
|
+
const result = await fetchProject(config.projectUrl, Coframe.projectId, newPageUrl, signal, config.userContext, preview.previewVariantId || undefined, undefined, (_a = config.referrer) !== null && _a !== void 0 ? _a : getBrowserReferrer());
|
|
3231
3398
|
if (signal.aborted)
|
|
3232
3399
|
return;
|
|
3233
3400
|
if (result) {
|
|
3234
3401
|
pageProject = result.project;
|
|
3402
|
+
pagePersonalizationHeaders = result.personalizationHeaders;
|
|
3235
3403
|
cacheFrames(newPageUrl, result);
|
|
3236
3404
|
}
|
|
3237
3405
|
}
|
|
3238
|
-
catch (
|
|
3406
|
+
catch (_b) {
|
|
3239
3407
|
}
|
|
3240
3408
|
}
|
|
3241
3409
|
if (signal.aborted)
|
|
3242
3410
|
return;
|
|
3243
|
-
await applyVariants(newPageUrl, pageProject);
|
|
3411
|
+
await applyVariants(newPageUrl, pageProject, pagePersonalizationHeaders);
|
|
3244
3412
|
});
|
|
3245
3413
|
}
|
|
3246
3414
|
function readLaneClaims() {
|
|
@@ -3271,13 +3439,14 @@ function persistLaneClaims(claims, logger) {
|
|
|
3271
3439
|
setCookie(LANE_COOKIE_KEY, encodeURIComponent(serialized), logger);
|
|
3272
3440
|
}
|
|
3273
3441
|
}
|
|
3274
|
-
function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics,
|
|
3442
|
+
function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore) {
|
|
3275
3443
|
let isFirstApply = true;
|
|
3276
|
-
return async (pageUrl, pageProject) => {
|
|
3444
|
+
return async (pageUrl, pageProject, personalizationHeaders) => {
|
|
3277
3445
|
const sessionId = isFirstApply ? Coframe.sessionId : generateUUID();
|
|
3278
3446
|
const sessionContext = buildSessionContext(isFirstApply);
|
|
3279
3447
|
isFirstApply = false;
|
|
3280
3448
|
Coframe.sessionId = sessionId;
|
|
3449
|
+
Coframe.personalizationHeaders = personalizationHeaders;
|
|
3281
3450
|
const preview = resolvePreview(pageUrl, previewVariantId, isSessionVariant);
|
|
3282
3451
|
setIsPreviewVariant(!!preview.previewVariantId);
|
|
3283
3452
|
const { variantsAndElements, experimentsInfo, hasActivatedFrames, laneClaims, } = await getVariantsAndElements(personalizationHeaders, pageProject, preview.previewVariantId, Coframe.userToken, pageUrl, getBrowserLanguages(), getBrowserPlatform(), Coframe.logger, config.nonce, laneStore.claims, preview.isSessionVariant);
|
|
@@ -3314,8 +3483,8 @@ async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
|
3314
3483
|
if (!(await ensureBody(Coframe, cdnProject, timeoutMs, config.nonce)))
|
|
3315
3484
|
return;
|
|
3316
3485
|
const laneStore = { claims: readLaneClaims() };
|
|
3317
|
-
const applyVariants = createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics,
|
|
3318
|
-
await applyVariants(getCurrentPageUrl(), cdnProject);
|
|
3486
|
+
const applyVariants = createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore);
|
|
3487
|
+
await applyVariants(getCurrentPageUrl(), cdnProject, personalizationHeaders);
|
|
3319
3488
|
if (!Coframe.variantsAndElements ||
|
|
3320
3489
|
Coframe.variantsAndElements.length === 0) {
|
|
3321
3490
|
revealPage();
|
|
@@ -3323,7 +3492,7 @@ async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
|
3323
3492
|
}
|
|
3324
3493
|
reportInitTiming(Coframe, cdnProject, integrationStartTime, executionStartTime);
|
|
3325
3494
|
setupSPANavigation(Coframe, cdnProject, config, previewVariantId, isSessionVariant, applyVariants);
|
|
3326
|
-
setupFlushInterval(Coframe
|
|
3495
|
+
setupFlushInterval(Coframe);
|
|
3327
3496
|
}
|
|
3328
3497
|
|
|
3329
3498
|
const TIMEOUT_MAX_MS = 5000;
|
|
@@ -3359,6 +3528,7 @@ async function resolveConfig(Coframe, revealPage) {
|
|
|
3359
3528
|
: null;
|
|
3360
3529
|
const executionStartTime = new Date();
|
|
3361
3530
|
Coframe.batchSize = config.batchSize || 32;
|
|
3531
|
+
configureIngestUrl(config.ingestUrl);
|
|
3362
3532
|
const gracefulExit = (msg) => {
|
|
3363
3533
|
console.warn(`[Coframe] ${msg}`);
|
|
3364
3534
|
revealPage();
|
|
@@ -3480,13 +3650,13 @@ async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit) {
|
|
|
3480
3650
|
}
|
|
3481
3651
|
|
|
3482
3652
|
async function resolveProject(Coframe, previewVariantId, isSessionVariant, config) {
|
|
3483
|
-
var _a;
|
|
3653
|
+
var _a, _b;
|
|
3484
3654
|
const logger = Coframe.logger;
|
|
3485
3655
|
const projectData = (_a = findLastCmd((c) => !!c.projectData)) === null || _a === void 0 ? void 0 : _a.projectData;
|
|
3486
3656
|
let personalizationHeaders = (projectData === null || projectData === void 0 ? void 0 : projectData.personalizationHeaders) || null;
|
|
3487
3657
|
let project = isSessionVariant ? undefined : projectData === null || projectData === void 0 ? void 0 : projectData.project;
|
|
3488
3658
|
if (!project) {
|
|
3489
|
-
const edgeResult = await
|
|
3659
|
+
const edgeResult = await fetchProject(config.projectUrl, Coframe.projectId, config.currentUrl || getCurrentPageUrl(), undefined, config.userContext, previewVariantId || undefined, undefined, (_b = config.referrer) !== null && _b !== void 0 ? _b : getBrowserReferrer());
|
|
3490
3660
|
personalizationHeaders = (edgeResult === null || edgeResult === void 0 ? void 0 : edgeResult.personalizationHeaders) || null;
|
|
3491
3661
|
project = (edgeResult === null || edgeResult === void 0 ? void 0 : edgeResult.project) || undefined;
|
|
3492
3662
|
}
|