@coframe/sdk 1.1.59 → 1.1.60
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 +34 -6
- package/dist/pkg/cf.cjs.js +255 -31
- package/dist/pkg/cf.esm.mjs +255 -31
- package/dist/pkg/server.cjs.js +52 -13
- package/dist/pkg/server.esm.mjs +52 -14
- package/dist/pkg/types/index.d.ts +1 -0
- package/dist/pkg/types/react.d.ts +1 -0
- package/dist/pkg/types/server.d.ts +8 -3
- package/package.json +12 -14
package/README.md
CHANGED
|
@@ -145,7 +145,11 @@ Pass `projectData` into `<CoframeProvider>` to avoid the extra client-side proje
|
|
|
145
145
|
|
|
146
146
|
Use `getExperimentAssignment()` when the server must know one experiment's
|
|
147
147
|
variant before rendering or calling an upstream service. Edge performs the full
|
|
148
|
-
assignment; the package never falls back to local bucketing.
|
|
148
|
+
assignment; the package never falls back to local bucketing. Use
|
|
149
|
+
`getAssignments()` with the same options minus `experimentId` to get every
|
|
150
|
+
experiment the visitor is assigned on `currentUrl` in one call; the result's
|
|
151
|
+
`variants` lists them in the order the browser applies them and `variant` is
|
|
152
|
+
`null` with reason `no_target_experiment`.
|
|
149
153
|
|
|
150
154
|
```ts
|
|
151
155
|
import {
|
|
@@ -273,14 +277,21 @@ kinds of input only exist in the page, so Edge cannot evaluate them:
|
|
|
273
277
|
on experiments you resolve on the server.
|
|
274
278
|
- Experiments in the same frame are evaluated in order and the first match
|
|
275
279
|
wins. If a JavaScript-targeted experiment sits **ahead of the target in
|
|
276
|
-
the same frame**, Edge
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
the same frame**, Edge cannot tell whether the browser would match it and
|
|
281
|
+
skip the target, so it leaves the whole frame to the browser: the result is
|
|
282
|
+
`variant: null` with reason `not_assigned` (logged on Edge as
|
|
283
|
+
`undecidable_audience`) and the browser renders the frame's pick. Keep
|
|
284
|
+
JavaScript-targeted experiments out of the target experiment's frame, or
|
|
285
|
+
place them after the target.
|
|
279
286
|
- The same applies to a JavaScript-targeted experiment that shares a
|
|
280
287
|
**lane** with the target, wherever it sits: lane winners are drawn from
|
|
281
288
|
the experiments the visitor can reach, and Edge cannot tell whether that
|
|
282
|
-
one is reachable
|
|
283
|
-
|
|
289
|
+
one is reachable, so it leaves every frame holding a member of that lane
|
|
290
|
+
to the browser (`variant: null`, reason `not_assigned`, logged as
|
|
291
|
+
`undecidable_lane`) unless an earlier visit already claimed the lane; a
|
|
292
|
+
lane member in a frame whose own activation needs the page counts the
|
|
293
|
+
same way. Keep
|
|
294
|
+
JavaScript-targeted experiments out of the target's lane.
|
|
284
295
|
- **CMAB experiments.** A target CMAB experiment returns `variant: null` with
|
|
285
296
|
reason `unsupported_experiment_type`.
|
|
286
297
|
- **URL fragments.** A server never sees the `#fragment` of the page URL, and
|
|
@@ -601,6 +612,22 @@ If your app uses an older TypeScript module resolution mode, prefer moving to on
|
|
|
601
612
|
|
|
602
613
|
Modern resolution modes handle package subpath exports more reliably.
|
|
603
614
|
|
|
615
|
+
## SDK development
|
|
616
|
+
|
|
617
|
+
When building this SDK from source, use the Node.js version pinned in `.nvmrc`.
|
|
618
|
+
SDK CI reads the same `.nvmrc` file through `actions/setup-node`.
|
|
619
|
+
|
|
620
|
+
From `sdk/sdk-js`, run:
|
|
621
|
+
|
|
622
|
+
```sh
|
|
623
|
+
nvm install
|
|
624
|
+
nvm use
|
|
625
|
+
yarn install --frozen-lockfile
|
|
626
|
+
yarn build:stage
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
SDK Tools use their own `sdk/tools/.nvmrc`.
|
|
630
|
+
|
|
604
631
|
## API overview
|
|
605
632
|
|
|
606
633
|
### `@coframe/sdk`
|
|
@@ -627,6 +654,7 @@ Modern resolution modes handle package subpath exports more reliably.
|
|
|
627
654
|
|
|
628
655
|
- `getProjectData(options)`
|
|
629
656
|
- `getExperimentAssignment(options)`
|
|
657
|
+
- `getAssignments(options)`
|
|
630
658
|
- `generateUserToken()`
|
|
631
659
|
- `evaluateFlags(options)`
|
|
632
660
|
- `getFeatureFlag(key, options)` / `isFeatureEnabled(key, options)`
|
package/dist/pkg/cf.cjs.js
CHANGED
|
@@ -1663,6 +1663,60 @@ const executeTags = (tags, logger, nonce) => {
|
|
|
1663
1663
|
}
|
|
1664
1664
|
};
|
|
1665
1665
|
|
|
1666
|
+
function isRecord(value) {
|
|
1667
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
const SCOPED = new Set(['update', 'delete', 'insert_into', 'insert_by']);
|
|
1671
|
+
const ADDED = new Set(['add_modal', 'add_banner']);
|
|
1672
|
+
function safeSelector(css) {
|
|
1673
|
+
return css.trim() !== '' && !/[<{}]/.test(css) && !css.includes('/*');
|
|
1674
|
+
}
|
|
1675
|
+
function modificationScope(modifications, elements) {
|
|
1676
|
+
const selectors = [];
|
|
1677
|
+
if (!isRecord(modifications) || !isRecord(elements))
|
|
1678
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1679
|
+
for (const [id, changes] of Object.entries(modifications)) {
|
|
1680
|
+
if (!Array.isArray(changes))
|
|
1681
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1682
|
+
let scoped = false;
|
|
1683
|
+
for (const change of changes) {
|
|
1684
|
+
const type = isRecord(change) ? change.type : undefined;
|
|
1685
|
+
if (typeof type === 'string' && SCOPED.has(type))
|
|
1686
|
+
scoped = true;
|
|
1687
|
+
else if (type === 'execute_js' &&
|
|
1688
|
+
isRecord(change) &&
|
|
1689
|
+
change.css !== undefined) {
|
|
1690
|
+
const { css: list } = change;
|
|
1691
|
+
const declared = Array.isArray(list) ? list : [list];
|
|
1692
|
+
if (declared.length === 0)
|
|
1693
|
+
return { kind: 'page', reason: `unscoped_change:${type}` };
|
|
1694
|
+
for (const css of declared) {
|
|
1695
|
+
if (typeof css !== 'string' || !safeSelector(css))
|
|
1696
|
+
return { kind: 'page', reason: `unscoped_selector:${id}` };
|
|
1697
|
+
selectors.push(css.trim());
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
else if (typeof type !== 'string' || !ADDED.has(type))
|
|
1701
|
+
return { kind: 'page', reason: `unscoped_change:${String(type)}` };
|
|
1702
|
+
}
|
|
1703
|
+
if (!scoped)
|
|
1704
|
+
continue;
|
|
1705
|
+
const conditions = elements[id];
|
|
1706
|
+
if (!Array.isArray(conditions) || conditions.length === 0)
|
|
1707
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1708
|
+
const css = [];
|
|
1709
|
+
for (const condition of conditions) {
|
|
1710
|
+
const value = isRecord(condition) ? condition.css : undefined;
|
|
1711
|
+
if (typeof value !== 'string' || !safeSelector(value))
|
|
1712
|
+
return { kind: 'page', reason: `unscoped_selector:${id}` };
|
|
1713
|
+
css.push(value.trim());
|
|
1714
|
+
}
|
|
1715
|
+
selectors.push(css.join(', '));
|
|
1716
|
+
}
|
|
1717
|
+
return selectors.length ? { kind: 'elements', selectors } : { kind: 'none' };
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1666
1720
|
function getLogCallback() {
|
|
1667
1721
|
const queue = window.CFQ;
|
|
1668
1722
|
if (!queue) {
|
|
@@ -1974,6 +2028,75 @@ function subscribeToCmd(predicate, callback, queueName = QueueName.CFQ) {
|
|
|
1974
2028
|
return __addListener(predicate, callback, queueName);
|
|
1975
2029
|
}
|
|
1976
2030
|
|
|
2031
|
+
function scopeOwners(variants, selectors) {
|
|
2032
|
+
const matches = new Map();
|
|
2033
|
+
const select = (css) => {
|
|
2034
|
+
if (!matches.has(css)) {
|
|
2035
|
+
try {
|
|
2036
|
+
matches.set(css, document.querySelectorAll(css));
|
|
2037
|
+
}
|
|
2038
|
+
catch (_a) {
|
|
2039
|
+
matches.set(css, null);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return matches.get(css);
|
|
2043
|
+
};
|
|
2044
|
+
const overlaps = (hidden, changed) => {
|
|
2045
|
+
if (hidden === changed)
|
|
2046
|
+
return true;
|
|
2047
|
+
const left = select(hidden);
|
|
2048
|
+
const right = select(changed);
|
|
2049
|
+
if (!(left === null || left === void 0 ? void 0 : left.length) || !(right === null || right === void 0 ? void 0 : right.length))
|
|
2050
|
+
return true;
|
|
2051
|
+
for (let i = 0; i < left.length; i++) {
|
|
2052
|
+
for (let j = 0; j < right.length; j++) {
|
|
2053
|
+
if (left[i].contains(right[j]) || right[j].contains(left[i]))
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
return false;
|
|
2058
|
+
};
|
|
2059
|
+
const scopes = variants.map(({ variant, elements }) => modificationScope(variant.modifications, elements));
|
|
2060
|
+
return selectors.map((selector) => {
|
|
2061
|
+
const owners = [];
|
|
2062
|
+
scopes.forEach((scope, index) => {
|
|
2063
|
+
if (scope.kind === 'page' ||
|
|
2064
|
+
(scope.kind === 'elements' &&
|
|
2065
|
+
scope.selectors.some((changed) => overlaps(selector, changed)))) {
|
|
2066
|
+
owners.push(index);
|
|
2067
|
+
}
|
|
2068
|
+
});
|
|
2069
|
+
return owners;
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
function beginProxyBatch(variants) {
|
|
2073
|
+
const request = findLastCmd((cmd) => cmd.proxyReveal === true);
|
|
2074
|
+
if (!request)
|
|
2075
|
+
return undefined;
|
|
2076
|
+
const queue = ensureQueueExists();
|
|
2077
|
+
const batchId = queue.length + 1;
|
|
2078
|
+
const keys = variants.map(({ variant }, index) => JSON.stringify([variant.experiment_id, variant.id, index]));
|
|
2079
|
+
const owners = request.proxySelectors
|
|
2080
|
+
? scopeOwners(variants, request.proxySelectors)
|
|
2081
|
+
: undefined;
|
|
2082
|
+
queue.push({ proxyBatch: { batchId, keys, ...(owners ? { owners } : {}) } });
|
|
2083
|
+
return (index) => {
|
|
2084
|
+
let completed = false;
|
|
2085
|
+
const complete = (() => {
|
|
2086
|
+
if (completed)
|
|
2087
|
+
return;
|
|
2088
|
+
completed = true;
|
|
2089
|
+
queue.push({ proxyVariantRendered: { batchId, key: keys[index] } });
|
|
2090
|
+
});
|
|
2091
|
+
complete.silent = () => {
|
|
2092
|
+
if (completed)
|
|
2093
|
+
return;
|
|
2094
|
+
queue.push({ proxyVariantSilent: { batchId, key: keys[index] } });
|
|
2095
|
+
};
|
|
2096
|
+
return complete;
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
|
|
1977
2100
|
function describeError(error) {
|
|
1978
2101
|
if (error && (typeof error === 'object' || typeof error === 'function')) {
|
|
1979
2102
|
const stack = readString(() => error.stack);
|
|
@@ -2112,13 +2235,15 @@ function variantRendered() {
|
|
|
2112
2235
|
const queue = ensureQueueExists();
|
|
2113
2236
|
queue.push({
|
|
2114
2237
|
emit: 'variantRendered',
|
|
2238
|
+
sdkVariantRendered: true,
|
|
2115
2239
|
});
|
|
2116
2240
|
}
|
|
2241
|
+
const SIGNALS_COMPLETION = /variantRendered|variantSession/;
|
|
2117
2242
|
function hasJavascriptModification(variantElements) {
|
|
2118
2243
|
const modifications = variantElements.variant.modifications || {};
|
|
2119
2244
|
return Object.keys(modifications).some((elementId) => (modifications[elementId] || []).some((modification) => modification.type === 'execute_js'));
|
|
2120
2245
|
}
|
|
2121
|
-
const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecycleContext) => {
|
|
2246
|
+
const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecycleContext, proxyComplete) => {
|
|
2122
2247
|
var _a;
|
|
2123
2248
|
const { variant, elements } = variantElements;
|
|
2124
2249
|
const variantMetadata = {
|
|
@@ -2126,6 +2251,35 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2126
2251
|
experiment_id: variant.experiment_id,
|
|
2127
2252
|
};
|
|
2128
2253
|
setApplyVariantChangesStarted(true);
|
|
2254
|
+
const javascriptPending = [];
|
|
2255
|
+
const javascriptSilent = [];
|
|
2256
|
+
let applying = true;
|
|
2257
|
+
const maybeComplete = () => {
|
|
2258
|
+
if (applying)
|
|
2259
|
+
return;
|
|
2260
|
+
if (javascriptPending.every((pending) => !pending))
|
|
2261
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete();
|
|
2262
|
+
else if (javascriptPending.every((pending, i) => !pending || javascriptSilent[i]))
|
|
2263
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete.silent();
|
|
2264
|
+
};
|
|
2265
|
+
const scriptCompletion = () => {
|
|
2266
|
+
const index = javascriptPending.push(true) - 1;
|
|
2267
|
+
javascriptSilent.push(false);
|
|
2268
|
+
const complete = (() => {
|
|
2269
|
+
var _a;
|
|
2270
|
+
if (!javascriptPending[index])
|
|
2271
|
+
return;
|
|
2272
|
+
javascriptPending[index] = false;
|
|
2273
|
+
(_a = complete.settled) === null || _a === void 0 ? void 0 : _a.call(complete);
|
|
2274
|
+
complete.settled = undefined;
|
|
2275
|
+
maybeComplete();
|
|
2276
|
+
});
|
|
2277
|
+
complete.silent = () => {
|
|
2278
|
+
if (javascriptPending[index])
|
|
2279
|
+
javascriptSilent[index] = true;
|
|
2280
|
+
};
|
|
2281
|
+
return complete;
|
|
2282
|
+
};
|
|
2129
2283
|
const session = lifecycleContext && hasJavascriptModification(variantElements)
|
|
2130
2284
|
? lifecycleContext.runtime.createVariantSession({
|
|
2131
2285
|
batchId: lifecycleContext.batchId,
|
|
@@ -2136,6 +2290,7 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2136
2290
|
if (!variant.modifications ||
|
|
2137
2291
|
Object.keys(variant.modifications).length === 0) {
|
|
2138
2292
|
variantRendered();
|
|
2293
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete();
|
|
2139
2294
|
return true;
|
|
2140
2295
|
}
|
|
2141
2296
|
for (const elementId of Object.keys(variant.modifications)) {
|
|
@@ -2159,7 +2314,7 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2159
2314
|
}
|
|
2160
2315
|
continue;
|
|
2161
2316
|
}
|
|
2162
|
-
const edited = editElement(selectedElement, variant.modifications, elementId, logger, nonce, variantMetadata, session === null || session === void 0 ? void 0 : session.facade);
|
|
2317
|
+
const edited = editElement(selectedElement, variant.modifications, elementId, logger, nonce, variantMetadata, session === null || session === void 0 ? void 0 : session.facade, proxyComplete ? scriptCompletion : undefined);
|
|
2163
2318
|
if (!edited) {
|
|
2164
2319
|
logger.error('element not edited', 'element_not_edited', {
|
|
2165
2320
|
...variantMetadata,
|
|
@@ -2177,6 +2332,8 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2177
2332
|
}
|
|
2178
2333
|
}
|
|
2179
2334
|
}
|
|
2335
|
+
applying = false;
|
|
2336
|
+
maybeComplete();
|
|
2180
2337
|
return true;
|
|
2181
2338
|
};
|
|
2182
2339
|
function describeElement(element) {
|
|
@@ -2188,7 +2345,7 @@ function describeElement(element) {
|
|
|
2188
2345
|
const className = target.getAttribute('class');
|
|
2189
2346
|
return `<${String(target.tagName).toLowerCase()}${id ? ` id=${JSON.stringify(id)}` : ''}${className ? ` class=${JSON.stringify(className)}` : ''}>`.slice(0, 200);
|
|
2190
2347
|
}
|
|
2191
|
-
const editElement = (element, modifications, elementId, logger, nonce, logMetadata = {}, variantSession) => {
|
|
2348
|
+
const editElement = (element, modifications, elementId, logger, nonce, logMetadata = {}, variantSession, scriptCompletion) => {
|
|
2192
2349
|
if (!modifications[elementId]) {
|
|
2193
2350
|
logger.warn('no modifications for element', 'no_modifications_for_element', {
|
|
2194
2351
|
...logMetadata,
|
|
@@ -2199,13 +2356,37 @@ const editElement = (element, modifications, elementId, logger, nonce, logMetada
|
|
|
2199
2356
|
}
|
|
2200
2357
|
for (const content of modifications[elementId]) {
|
|
2201
2358
|
switch (content.type) {
|
|
2202
|
-
case 'execute_js':
|
|
2203
|
-
|
|
2204
|
-
|
|
2359
|
+
case 'execute_js': {
|
|
2360
|
+
const complete = scriptCompletion === null || scriptCompletion === void 0 ? void 0 : scriptCompletion();
|
|
2361
|
+
const session = variantSession && complete
|
|
2362
|
+
? {
|
|
2363
|
+
...variantSession,
|
|
2364
|
+
complete() {
|
|
2365
|
+
variantSession.complete();
|
|
2366
|
+
complete();
|
|
2367
|
+
},
|
|
2368
|
+
error(error) {
|
|
2369
|
+
variantSession.error(error);
|
|
2370
|
+
complete();
|
|
2371
|
+
},
|
|
2372
|
+
}
|
|
2373
|
+
: variantSession;
|
|
2374
|
+
const stop = complete &&
|
|
2375
|
+
subscribeToCmd((cmd) => !cmd.sdkVariantRendered &&
|
|
2376
|
+
(cmd.variantRendered === true || cmd.emit === 'variantRendered'), () => complete());
|
|
2377
|
+
if (complete && stop)
|
|
2378
|
+
complete.settled = stop;
|
|
2379
|
+
const applied = applyJavascriptModification(element, content, logger, nonce, logMetadata, session ? { variantSession: session } : undefined);
|
|
2380
|
+
if (!applied)
|
|
2381
|
+
complete === null || complete === void 0 ? void 0 : complete();
|
|
2382
|
+
if (applied && complete && !SIGNALS_COMPLETION.test(content.js))
|
|
2383
|
+
complete.silent();
|
|
2384
|
+
if (!applied && variantSession) {
|
|
2205
2385
|
variantSession.error(new Error('JavaScript variant modification did not run'));
|
|
2206
2386
|
return false;
|
|
2207
2387
|
}
|
|
2208
2388
|
break;
|
|
2389
|
+
}
|
|
2209
2390
|
case 'update':
|
|
2210
2391
|
applyUpdateModification(element, content, logger, logMetadata);
|
|
2211
2392
|
variantRendered();
|
|
@@ -2264,13 +2445,14 @@ function revealPage() {
|
|
|
2264
2445
|
async function replaceVariants(variants, orgId, logger, nonce, runtime) {
|
|
2265
2446
|
const failedReplacements = [];
|
|
2266
2447
|
let currentVariant;
|
|
2448
|
+
const proxyCompletion = beginProxyBatch(variants);
|
|
2267
2449
|
const batch = runtime === null || runtime === void 0 ? void 0 : runtime.beginBatch({
|
|
2268
2450
|
expectedSessionCount: variants.filter(hasJavascriptModification).length,
|
|
2269
2451
|
});
|
|
2270
2452
|
try {
|
|
2271
|
-
for (const v of variants) {
|
|
2453
|
+
for (const [index, v] of variants.entries()) {
|
|
2272
2454
|
currentVariant = v.variant;
|
|
2273
|
-
if (!applyVariantChanges(v, getCurrentPageUrl(), logger, nonce, batch && runtime ? { runtime, batchId: batch.batchId } : undefined)) {
|
|
2455
|
+
if (!applyVariantChanges(v, getCurrentPageUrl(), logger, nonce, batch && runtime ? { runtime, batchId: batch.batchId } : undefined, proxyCompletion === null || proxyCompletion === void 0 ? void 0 : proxyCompletion(index))) {
|
|
2274
2456
|
failedReplacements.push({
|
|
2275
2457
|
message: 'Original element not found',
|
|
2276
2458
|
metadata: {
|
|
@@ -2823,8 +3005,7 @@ function checkAllAudiences(metadata, ctx) {
|
|
|
2823
3005
|
}
|
|
2824
3006
|
return result;
|
|
2825
3007
|
}
|
|
2826
|
-
function audiencesPass(
|
|
2827
|
-
const result = checkAllAudiences(metadata, ctx);
|
|
3008
|
+
function audiencesPass(result, ctx) {
|
|
2828
3009
|
return ctx.unknownIsInactive ? result === true : result !== false;
|
|
2829
3010
|
}
|
|
2830
3011
|
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false, chooseCmabVariants, userVisitCountOverride, runtime = {}) {
|
|
@@ -2906,57 +3087,89 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2906
3087
|
experimentsInfo: [...experimentsInfo, ...assignment.experimentsInfo],
|
|
2907
3088
|
});
|
|
2908
3089
|
}
|
|
3090
|
+
function frameActivation(frame, ctx) {
|
|
3091
|
+
return checkActivationCondition(frame.activation_condition, ctx.personalizationHeaders, ctx.currentPageUrl, ctx.languages, ctx.browserPlatform, ctx.userToken, ctx.userVisitCount, ctx.runJsFunction, ctx.logger);
|
|
3092
|
+
}
|
|
2909
3093
|
function activateFrames(ctx) {
|
|
2910
3094
|
return ctx.project.frames.filter((frame) => {
|
|
2911
3095
|
if (!frame.experiments)
|
|
2912
3096
|
return false;
|
|
2913
|
-
const frameActivated =
|
|
3097
|
+
const frameActivated = frameActivation(frame, ctx);
|
|
2914
3098
|
return ctx.unknownIsInactive ? frameActivated === true : !!frameActivated;
|
|
2915
3099
|
});
|
|
2916
3100
|
}
|
|
3101
|
+
function undecidedFrames(ctx) {
|
|
3102
|
+
if (!ctx.unknownIsInactive)
|
|
3103
|
+
return [];
|
|
3104
|
+
return ctx.project.frames.filter((frame) => {
|
|
3105
|
+
if (!frame.experiments)
|
|
3106
|
+
return false;
|
|
3107
|
+
const activated = frameActivation(frame, ctx);
|
|
3108
|
+
return activated !== true && activated !== false;
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
2917
3111
|
async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants) {
|
|
2918
|
-
var _a, _b, _c;
|
|
3112
|
+
var _a, _b, _c, _d;
|
|
2919
3113
|
const { project, userToken, logger } = ctx;
|
|
2920
3114
|
const experimentsInfo = [];
|
|
2921
3115
|
const variantsAndElements = [];
|
|
2922
|
-
const
|
|
3116
|
+
const audienceResult = new Map();
|
|
2923
3117
|
const reachableByLane = {};
|
|
3118
|
+
const undecidableLanes = new Set();
|
|
2924
3119
|
for (const frame of activeFrames) {
|
|
2925
3120
|
for (const experiment of (_a = frame.experiments) !== null && _a !== void 0 ? _a : []) {
|
|
2926
3121
|
const metadata = experiment.metadata;
|
|
2927
3122
|
const laneIds = experimentLaneIds(metadata);
|
|
2928
3123
|
if (laneIds.length === 0)
|
|
2929
3124
|
continue;
|
|
2930
|
-
const
|
|
2931
|
-
|
|
2932
|
-
if (
|
|
3125
|
+
const result = checkAllAudiences(metadata, ctx);
|
|
3126
|
+
audienceResult.set(metadata.experiment_id, result);
|
|
3127
|
+
if (audiencesPass(result, ctx)) {
|
|
2933
3128
|
for (const laneId of laneIds) {
|
|
2934
3129
|
if (!reachableByLane[laneId])
|
|
2935
3130
|
reachableByLane[laneId] = [];
|
|
2936
3131
|
reachableByLane[laneId].push(metadata.experiment_id);
|
|
2937
3132
|
}
|
|
2938
3133
|
}
|
|
3134
|
+
else if (ctx.unknownIsInactive && result !== false) {
|
|
3135
|
+
for (const laneId of laneIds)
|
|
3136
|
+
if (!validatedClaims[laneId])
|
|
3137
|
+
undecidableLanes.add(laneId);
|
|
3138
|
+
}
|
|
2939
3139
|
}
|
|
2940
3140
|
}
|
|
3141
|
+
for (const frame of undecidedFrames(ctx))
|
|
3142
|
+
for (const experiment of (_b = frame.experiments) !== null && _b !== void 0 ? _b : [])
|
|
3143
|
+
for (const laneId of experimentLaneIds(experiment.metadata))
|
|
3144
|
+
if (!validatedClaims[laneId])
|
|
3145
|
+
undecidableLanes.add(laneId);
|
|
2941
3146
|
const excludedExperimentIds = resolveLaneWinners(validatedClaims, reachableByLane, project.lanes, userToken);
|
|
2942
3147
|
const updatedClaims = { ...validatedClaims };
|
|
2943
3148
|
const assignmentEntries = [];
|
|
2944
3149
|
let hasActivatedFrames = false;
|
|
2945
3150
|
for (const frame of activeFrames) {
|
|
2946
3151
|
let atLeastOneExperimentWasActivated = false;
|
|
2947
|
-
for (const experiment of (
|
|
3152
|
+
for (const experiment of (_c = frame.experiments) !== null && _c !== void 0 ? _c : []) {
|
|
2948
3153
|
if (atLeastOneExperimentWasActivated)
|
|
2949
3154
|
break;
|
|
2950
3155
|
const metadata = experiment.metadata;
|
|
2951
3156
|
const variants = experiment.variants;
|
|
2952
3157
|
const elements = frame.elements;
|
|
3158
|
+
if (ctx.unknownIsInactive &&
|
|
3159
|
+
experimentLaneIds(metadata).some((id) => undecidableLanes.has(id))) {
|
|
3160
|
+
logger.warn('Leaving the frame to the browser: a lane member needs the page', 'undecidable_lane', { experiment_id: metadata.experiment_id });
|
|
3161
|
+
break;
|
|
3162
|
+
}
|
|
2953
3163
|
if (excludedExperimentIds.has(metadata.experiment_id))
|
|
2954
3164
|
continue;
|
|
2955
|
-
let
|
|
2956
|
-
if (
|
|
2957
|
-
|
|
3165
|
+
let result = audienceResult.get(metadata.experiment_id);
|
|
3166
|
+
if (result === undefined)
|
|
3167
|
+
result = checkAllAudiences(metadata, ctx);
|
|
3168
|
+
if (result !== true && result !== false && ctx.unknownIsInactive) {
|
|
3169
|
+
logger.warn('Leaving the frame to the browser: an audience needs the page', 'undecidable_audience', { experiment_id: metadata.experiment_id });
|
|
3170
|
+
break;
|
|
2958
3171
|
}
|
|
2959
|
-
if (!
|
|
3172
|
+
if (!audiencesPass(result, ctx))
|
|
2960
3173
|
continue;
|
|
2961
3174
|
atLeastOneExperimentWasActivated = true;
|
|
2962
3175
|
hasActivatedFrames = true;
|
|
@@ -3010,7 +3223,7 @@ async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVarian
|
|
|
3010
3223
|
try {
|
|
3011
3224
|
mlVariantIds = await chooseCmabVariants(Array.from(cmabMetadataById.values()), userToken);
|
|
3012
3225
|
}
|
|
3013
|
-
catch (
|
|
3226
|
+
catch (_e) {
|
|
3014
3227
|
mlVariantIds = new Map();
|
|
3015
3228
|
}
|
|
3016
3229
|
}
|
|
@@ -3021,7 +3234,7 @@ async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVarian
|
|
|
3021
3234
|
}
|
|
3022
3235
|
const { metadata, variants, elements } = entry;
|
|
3023
3236
|
let mlVariantId = metadata.type === 'CMAB'
|
|
3024
|
-
? ((
|
|
3237
|
+
? ((_d = mlVariantIds.get(metadata.experiment_id)) !== null && _d !== void 0 ? _d : null)
|
|
3025
3238
|
: null;
|
|
3026
3239
|
if (mlVariantId && !variants.some((v) => v.id === mlVariantId)) {
|
|
3027
3240
|
logger.warn(`CMAB variant ${mlVariantId} not in experiment ${metadata.experiment_id}`, 'cmab_variant_not_found', { experiment_id: metadata.experiment_id });
|
|
@@ -3564,7 +3777,7 @@ function shouldSdkWaitForHydration(config) {
|
|
|
3564
3777
|
!isVariantRuntimeLifecycleEnabled(config));
|
|
3565
3778
|
}
|
|
3566
3779
|
|
|
3567
|
-
function createRevealCoordinator(revealPage, reportError = () => { }) {
|
|
3780
|
+
function createRevealCoordinator(revealPage, reportError = () => { }, onForced = () => { }) {
|
|
3568
3781
|
const notify = (listener, reason) => {
|
|
3569
3782
|
try {
|
|
3570
3783
|
listener(reason);
|
|
@@ -3616,6 +3829,18 @@ function createRevealCoordinator(revealPage, reportError = () => { }) {
|
|
|
3616
3829
|
reveal(reason);
|
|
3617
3830
|
},
|
|
3618
3831
|
forceReveal(reason) {
|
|
3832
|
+
if (pageRevealed)
|
|
3833
|
+
return;
|
|
3834
|
+
try {
|
|
3835
|
+
onForced(reason);
|
|
3836
|
+
}
|
|
3837
|
+
catch (error) {
|
|
3838
|
+
try {
|
|
3839
|
+
reportError(error);
|
|
3840
|
+
}
|
|
3841
|
+
catch (_a) {
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3619
3844
|
reveal(reason);
|
|
3620
3845
|
},
|
|
3621
3846
|
block(batchId) {
|
|
@@ -3803,10 +4028,6 @@ function notifyEventsFlushed() {
|
|
|
3803
4028
|
}
|
|
3804
4029
|
}
|
|
3805
4030
|
|
|
3806
|
-
function isRecord(value) {
|
|
3807
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
3808
|
-
}
|
|
3809
|
-
|
|
3810
4031
|
const OPT_IN_COUNTRIES = new Set(('AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IT LV LT LU MT NL PL PT RO SK SI ES SE ' +
|
|
3811
4032
|
'AX GF GP MF MQ RE YT ' +
|
|
3812
4033
|
'IS LI NO ' +
|
|
@@ -5010,6 +5231,7 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
5010
5231
|
Coframe.debug.laneClaims = laneClaims;
|
|
5011
5232
|
}
|
|
5012
5233
|
if (config.trackingOnly) {
|
|
5234
|
+
beginProxyBatch([]);
|
|
5013
5235
|
variantRendered();
|
|
5014
5236
|
}
|
|
5015
5237
|
else {
|
|
@@ -5032,7 +5254,7 @@ async function modifyPage(Coframe, configResult, projectResult, analytics, lifec
|
|
|
5032
5254
|
var _a;
|
|
5033
5255
|
const { config, timeoutMs, previewVariantId, isSessionVariant, integrationStartTime, executionStartTime, } = configResult;
|
|
5034
5256
|
const { project: cdnProject, personalizationHeaders, assignmentContext, } = projectResult;
|
|
5035
|
-
const revealCoordinator = (_a = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.revealCoordinator) !== null && _a !== void 0 ? _a : createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger));
|
|
5257
|
+
const revealCoordinator = (_a = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.revealCoordinator) !== null && _a !== void 0 ? _a : createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger), () => beginProxyBatch([]));
|
|
5036
5258
|
const runtime = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.runtime;
|
|
5037
5259
|
if (runtime && config.waitForHydration) {
|
|
5038
5260
|
if (findLastCmd(isPageHydratedCmd)) {
|
|
@@ -5399,6 +5621,7 @@ async function resolveConfig(Coframe, revealActions) {
|
|
|
5399
5621
|
}
|
|
5400
5622
|
|
|
5401
5623
|
async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit, previewVariantId) {
|
|
5624
|
+
var _a;
|
|
5402
5625
|
const logger = Coframe.logger;
|
|
5403
5626
|
let userToken;
|
|
5404
5627
|
if (config.waitForUserToken) {
|
|
@@ -5416,7 +5639,8 @@ async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit, preview
|
|
|
5416
5639
|
}
|
|
5417
5640
|
}
|
|
5418
5641
|
else {
|
|
5419
|
-
|
|
5642
|
+
const proxied = (_a = findLastCmd((cmd) => typeof cmd.proxyUserToken === 'string' && !!cmd.proxyUserToken)) === null || _a === void 0 ? void 0 : _a.proxyUserToken;
|
|
5643
|
+
userToken = proxied || getOrGenerateUserToken();
|
|
5420
5644
|
}
|
|
5421
5645
|
Coframe.userToken = userToken;
|
|
5422
5646
|
const persistCookies = () => {
|
|
@@ -6409,7 +6633,7 @@ const Coframe = {
|
|
|
6409
6633
|
},
|
|
6410
6634
|
};
|
|
6411
6635
|
Coframe.logger = createLogger(() => Coframe);
|
|
6412
|
-
const revealCoordinator = createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger));
|
|
6636
|
+
const revealCoordinator = createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger), () => beginProxyBatch([]));
|
|
6413
6637
|
let activeRuntime = null;
|
|
6414
6638
|
function clearActiveRuntime() {
|
|
6415
6639
|
activeRuntime === null || activeRuntime === void 0 ? void 0 : activeRuntime.destroy();
|