@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/dist/pkg/cf.esm.mjs
CHANGED
|
@@ -1661,6 +1661,60 @@ const executeTags = (tags, logger, nonce) => {
|
|
|
1661
1661
|
}
|
|
1662
1662
|
};
|
|
1663
1663
|
|
|
1664
|
+
function isRecord(value) {
|
|
1665
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
const SCOPED = new Set(['update', 'delete', 'insert_into', 'insert_by']);
|
|
1669
|
+
const ADDED = new Set(['add_modal', 'add_banner']);
|
|
1670
|
+
function safeSelector(css) {
|
|
1671
|
+
return css.trim() !== '' && !/[<{}]/.test(css) && !css.includes('/*');
|
|
1672
|
+
}
|
|
1673
|
+
function modificationScope(modifications, elements) {
|
|
1674
|
+
const selectors = [];
|
|
1675
|
+
if (!isRecord(modifications) || !isRecord(elements))
|
|
1676
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1677
|
+
for (const [id, changes] of Object.entries(modifications)) {
|
|
1678
|
+
if (!Array.isArray(changes))
|
|
1679
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1680
|
+
let scoped = false;
|
|
1681
|
+
for (const change of changes) {
|
|
1682
|
+
const type = isRecord(change) ? change.type : undefined;
|
|
1683
|
+
if (typeof type === 'string' && SCOPED.has(type))
|
|
1684
|
+
scoped = true;
|
|
1685
|
+
else if (type === 'execute_js' &&
|
|
1686
|
+
isRecord(change) &&
|
|
1687
|
+
change.css !== undefined) {
|
|
1688
|
+
const { css: list } = change;
|
|
1689
|
+
const declared = Array.isArray(list) ? list : [list];
|
|
1690
|
+
if (declared.length === 0)
|
|
1691
|
+
return { kind: 'page', reason: `unscoped_change:${type}` };
|
|
1692
|
+
for (const css of declared) {
|
|
1693
|
+
if (typeof css !== 'string' || !safeSelector(css))
|
|
1694
|
+
return { kind: 'page', reason: `unscoped_selector:${id}` };
|
|
1695
|
+
selectors.push(css.trim());
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
else if (typeof type !== 'string' || !ADDED.has(type))
|
|
1699
|
+
return { kind: 'page', reason: `unscoped_change:${String(type)}` };
|
|
1700
|
+
}
|
|
1701
|
+
if (!scoped)
|
|
1702
|
+
continue;
|
|
1703
|
+
const conditions = elements[id];
|
|
1704
|
+
if (!Array.isArray(conditions) || conditions.length === 0)
|
|
1705
|
+
return { kind: 'page', reason: 'invalid_variant' };
|
|
1706
|
+
const css = [];
|
|
1707
|
+
for (const condition of conditions) {
|
|
1708
|
+
const value = isRecord(condition) ? condition.css : undefined;
|
|
1709
|
+
if (typeof value !== 'string' || !safeSelector(value))
|
|
1710
|
+
return { kind: 'page', reason: `unscoped_selector:${id}` };
|
|
1711
|
+
css.push(value.trim());
|
|
1712
|
+
}
|
|
1713
|
+
selectors.push(css.join(', '));
|
|
1714
|
+
}
|
|
1715
|
+
return selectors.length ? { kind: 'elements', selectors } : { kind: 'none' };
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1664
1718
|
function getLogCallback() {
|
|
1665
1719
|
const queue = window.CFQ;
|
|
1666
1720
|
if (!queue) {
|
|
@@ -1972,6 +2026,75 @@ function subscribeToCmd(predicate, callback, queueName = QueueName.CFQ) {
|
|
|
1972
2026
|
return __addListener(predicate, callback, queueName);
|
|
1973
2027
|
}
|
|
1974
2028
|
|
|
2029
|
+
function scopeOwners(variants, selectors) {
|
|
2030
|
+
const matches = new Map();
|
|
2031
|
+
const select = (css) => {
|
|
2032
|
+
if (!matches.has(css)) {
|
|
2033
|
+
try {
|
|
2034
|
+
matches.set(css, document.querySelectorAll(css));
|
|
2035
|
+
}
|
|
2036
|
+
catch (_a) {
|
|
2037
|
+
matches.set(css, null);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return matches.get(css);
|
|
2041
|
+
};
|
|
2042
|
+
const overlaps = (hidden, changed) => {
|
|
2043
|
+
if (hidden === changed)
|
|
2044
|
+
return true;
|
|
2045
|
+
const left = select(hidden);
|
|
2046
|
+
const right = select(changed);
|
|
2047
|
+
if (!(left === null || left === void 0 ? void 0 : left.length) || !(right === null || right === void 0 ? void 0 : right.length))
|
|
2048
|
+
return true;
|
|
2049
|
+
for (let i = 0; i < left.length; i++) {
|
|
2050
|
+
for (let j = 0; j < right.length; j++) {
|
|
2051
|
+
if (left[i].contains(right[j]) || right[j].contains(left[i]))
|
|
2052
|
+
return true;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
return false;
|
|
2056
|
+
};
|
|
2057
|
+
const scopes = variants.map(({ variant, elements }) => modificationScope(variant.modifications, elements));
|
|
2058
|
+
return selectors.map((selector) => {
|
|
2059
|
+
const owners = [];
|
|
2060
|
+
scopes.forEach((scope, index) => {
|
|
2061
|
+
if (scope.kind === 'page' ||
|
|
2062
|
+
(scope.kind === 'elements' &&
|
|
2063
|
+
scope.selectors.some((changed) => overlaps(selector, changed)))) {
|
|
2064
|
+
owners.push(index);
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
return owners;
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
function beginProxyBatch(variants) {
|
|
2071
|
+
const request = findLastCmd((cmd) => cmd.proxyReveal === true);
|
|
2072
|
+
if (!request)
|
|
2073
|
+
return undefined;
|
|
2074
|
+
const queue = ensureQueueExists();
|
|
2075
|
+
const batchId = queue.length + 1;
|
|
2076
|
+
const keys = variants.map(({ variant }, index) => JSON.stringify([variant.experiment_id, variant.id, index]));
|
|
2077
|
+
const owners = request.proxySelectors
|
|
2078
|
+
? scopeOwners(variants, request.proxySelectors)
|
|
2079
|
+
: undefined;
|
|
2080
|
+
queue.push({ proxyBatch: { batchId, keys, ...(owners ? { owners } : {}) } });
|
|
2081
|
+
return (index) => {
|
|
2082
|
+
let completed = false;
|
|
2083
|
+
const complete = (() => {
|
|
2084
|
+
if (completed)
|
|
2085
|
+
return;
|
|
2086
|
+
completed = true;
|
|
2087
|
+
queue.push({ proxyVariantRendered: { batchId, key: keys[index] } });
|
|
2088
|
+
});
|
|
2089
|
+
complete.silent = () => {
|
|
2090
|
+
if (completed)
|
|
2091
|
+
return;
|
|
2092
|
+
queue.push({ proxyVariantSilent: { batchId, key: keys[index] } });
|
|
2093
|
+
};
|
|
2094
|
+
return complete;
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
|
|
1975
2098
|
function describeError(error) {
|
|
1976
2099
|
if (error && (typeof error === 'object' || typeof error === 'function')) {
|
|
1977
2100
|
const stack = readString(() => error.stack);
|
|
@@ -2110,13 +2233,15 @@ function variantRendered() {
|
|
|
2110
2233
|
const queue = ensureQueueExists();
|
|
2111
2234
|
queue.push({
|
|
2112
2235
|
emit: 'variantRendered',
|
|
2236
|
+
sdkVariantRendered: true,
|
|
2113
2237
|
});
|
|
2114
2238
|
}
|
|
2239
|
+
const SIGNALS_COMPLETION = /variantRendered|variantSession/;
|
|
2115
2240
|
function hasJavascriptModification(variantElements) {
|
|
2116
2241
|
const modifications = variantElements.variant.modifications || {};
|
|
2117
2242
|
return Object.keys(modifications).some((elementId) => (modifications[elementId] || []).some((modification) => modification.type === 'execute_js'));
|
|
2118
2243
|
}
|
|
2119
|
-
const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecycleContext) => {
|
|
2244
|
+
const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecycleContext, proxyComplete) => {
|
|
2120
2245
|
var _a;
|
|
2121
2246
|
const { variant, elements } = variantElements;
|
|
2122
2247
|
const variantMetadata = {
|
|
@@ -2124,6 +2249,35 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2124
2249
|
experiment_id: variant.experiment_id,
|
|
2125
2250
|
};
|
|
2126
2251
|
setApplyVariantChangesStarted(true);
|
|
2252
|
+
const javascriptPending = [];
|
|
2253
|
+
const javascriptSilent = [];
|
|
2254
|
+
let applying = true;
|
|
2255
|
+
const maybeComplete = () => {
|
|
2256
|
+
if (applying)
|
|
2257
|
+
return;
|
|
2258
|
+
if (javascriptPending.every((pending) => !pending))
|
|
2259
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete();
|
|
2260
|
+
else if (javascriptPending.every((pending, i) => !pending || javascriptSilent[i]))
|
|
2261
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete.silent();
|
|
2262
|
+
};
|
|
2263
|
+
const scriptCompletion = () => {
|
|
2264
|
+
const index = javascriptPending.push(true) - 1;
|
|
2265
|
+
javascriptSilent.push(false);
|
|
2266
|
+
const complete = (() => {
|
|
2267
|
+
var _a;
|
|
2268
|
+
if (!javascriptPending[index])
|
|
2269
|
+
return;
|
|
2270
|
+
javascriptPending[index] = false;
|
|
2271
|
+
(_a = complete.settled) === null || _a === void 0 ? void 0 : _a.call(complete);
|
|
2272
|
+
complete.settled = undefined;
|
|
2273
|
+
maybeComplete();
|
|
2274
|
+
});
|
|
2275
|
+
complete.silent = () => {
|
|
2276
|
+
if (javascriptPending[index])
|
|
2277
|
+
javascriptSilent[index] = true;
|
|
2278
|
+
};
|
|
2279
|
+
return complete;
|
|
2280
|
+
};
|
|
2127
2281
|
const session = lifecycleContext && hasJavascriptModification(variantElements)
|
|
2128
2282
|
? lifecycleContext.runtime.createVariantSession({
|
|
2129
2283
|
batchId: lifecycleContext.batchId,
|
|
@@ -2134,6 +2288,7 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2134
2288
|
if (!variant.modifications ||
|
|
2135
2289
|
Object.keys(variant.modifications).length === 0) {
|
|
2136
2290
|
variantRendered();
|
|
2291
|
+
proxyComplete === null || proxyComplete === void 0 ? void 0 : proxyComplete();
|
|
2137
2292
|
return true;
|
|
2138
2293
|
}
|
|
2139
2294
|
for (const elementId of Object.keys(variant.modifications)) {
|
|
@@ -2157,7 +2312,7 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2157
2312
|
}
|
|
2158
2313
|
continue;
|
|
2159
2314
|
}
|
|
2160
|
-
const edited = editElement(selectedElement, variant.modifications, elementId, logger, nonce, variantMetadata, session === null || session === void 0 ? void 0 : session.facade);
|
|
2315
|
+
const edited = editElement(selectedElement, variant.modifications, elementId, logger, nonce, variantMetadata, session === null || session === void 0 ? void 0 : session.facade, proxyComplete ? scriptCompletion : undefined);
|
|
2161
2316
|
if (!edited) {
|
|
2162
2317
|
logger.error('element not edited', 'element_not_edited', {
|
|
2163
2318
|
...variantMetadata,
|
|
@@ -2175,6 +2330,8 @@ const applyVariantChanges = (variantElements, currentUrl, logger, nonce, lifecyc
|
|
|
2175
2330
|
}
|
|
2176
2331
|
}
|
|
2177
2332
|
}
|
|
2333
|
+
applying = false;
|
|
2334
|
+
maybeComplete();
|
|
2178
2335
|
return true;
|
|
2179
2336
|
};
|
|
2180
2337
|
function describeElement(element) {
|
|
@@ -2186,7 +2343,7 @@ function describeElement(element) {
|
|
|
2186
2343
|
const className = target.getAttribute('class');
|
|
2187
2344
|
return `<${String(target.tagName).toLowerCase()}${id ? ` id=${JSON.stringify(id)}` : ''}${className ? ` class=${JSON.stringify(className)}` : ''}>`.slice(0, 200);
|
|
2188
2345
|
}
|
|
2189
|
-
const editElement = (element, modifications, elementId, logger, nonce, logMetadata = {}, variantSession) => {
|
|
2346
|
+
const editElement = (element, modifications, elementId, logger, nonce, logMetadata = {}, variantSession, scriptCompletion) => {
|
|
2190
2347
|
if (!modifications[elementId]) {
|
|
2191
2348
|
logger.warn('no modifications for element', 'no_modifications_for_element', {
|
|
2192
2349
|
...logMetadata,
|
|
@@ -2197,13 +2354,37 @@ const editElement = (element, modifications, elementId, logger, nonce, logMetada
|
|
|
2197
2354
|
}
|
|
2198
2355
|
for (const content of modifications[elementId]) {
|
|
2199
2356
|
switch (content.type) {
|
|
2200
|
-
case 'execute_js':
|
|
2201
|
-
|
|
2202
|
-
|
|
2357
|
+
case 'execute_js': {
|
|
2358
|
+
const complete = scriptCompletion === null || scriptCompletion === void 0 ? void 0 : scriptCompletion();
|
|
2359
|
+
const session = variantSession && complete
|
|
2360
|
+
? {
|
|
2361
|
+
...variantSession,
|
|
2362
|
+
complete() {
|
|
2363
|
+
variantSession.complete();
|
|
2364
|
+
complete();
|
|
2365
|
+
},
|
|
2366
|
+
error(error) {
|
|
2367
|
+
variantSession.error(error);
|
|
2368
|
+
complete();
|
|
2369
|
+
},
|
|
2370
|
+
}
|
|
2371
|
+
: variantSession;
|
|
2372
|
+
const stop = complete &&
|
|
2373
|
+
subscribeToCmd((cmd) => !cmd.sdkVariantRendered &&
|
|
2374
|
+
(cmd.variantRendered === true || cmd.emit === 'variantRendered'), () => complete());
|
|
2375
|
+
if (complete && stop)
|
|
2376
|
+
complete.settled = stop;
|
|
2377
|
+
const applied = applyJavascriptModification(element, content, logger, nonce, logMetadata, session ? { variantSession: session } : undefined);
|
|
2378
|
+
if (!applied)
|
|
2379
|
+
complete === null || complete === void 0 ? void 0 : complete();
|
|
2380
|
+
if (applied && complete && !SIGNALS_COMPLETION.test(content.js))
|
|
2381
|
+
complete.silent();
|
|
2382
|
+
if (!applied && variantSession) {
|
|
2203
2383
|
variantSession.error(new Error('JavaScript variant modification did not run'));
|
|
2204
2384
|
return false;
|
|
2205
2385
|
}
|
|
2206
2386
|
break;
|
|
2387
|
+
}
|
|
2207
2388
|
case 'update':
|
|
2208
2389
|
applyUpdateModification(element, content, logger, logMetadata);
|
|
2209
2390
|
variantRendered();
|
|
@@ -2262,13 +2443,14 @@ function revealPage() {
|
|
|
2262
2443
|
async function replaceVariants(variants, orgId, logger, nonce, runtime) {
|
|
2263
2444
|
const failedReplacements = [];
|
|
2264
2445
|
let currentVariant;
|
|
2446
|
+
const proxyCompletion = beginProxyBatch(variants);
|
|
2265
2447
|
const batch = runtime === null || runtime === void 0 ? void 0 : runtime.beginBatch({
|
|
2266
2448
|
expectedSessionCount: variants.filter(hasJavascriptModification).length,
|
|
2267
2449
|
});
|
|
2268
2450
|
try {
|
|
2269
|
-
for (const v of variants) {
|
|
2451
|
+
for (const [index, v] of variants.entries()) {
|
|
2270
2452
|
currentVariant = v.variant;
|
|
2271
|
-
if (!applyVariantChanges(v, getCurrentPageUrl(), logger, nonce, batch && runtime ? { runtime, batchId: batch.batchId } : undefined)) {
|
|
2453
|
+
if (!applyVariantChanges(v, getCurrentPageUrl(), logger, nonce, batch && runtime ? { runtime, batchId: batch.batchId } : undefined, proxyCompletion === null || proxyCompletion === void 0 ? void 0 : proxyCompletion(index))) {
|
|
2272
2454
|
failedReplacements.push({
|
|
2273
2455
|
message: 'Original element not found',
|
|
2274
2456
|
metadata: {
|
|
@@ -2821,8 +3003,7 @@ function checkAllAudiences(metadata, ctx) {
|
|
|
2821
3003
|
}
|
|
2822
3004
|
return result;
|
|
2823
3005
|
}
|
|
2824
|
-
function audiencesPass(
|
|
2825
|
-
const result = checkAllAudiences(metadata, ctx);
|
|
3006
|
+
function audiencesPass(result, ctx) {
|
|
2826
3007
|
return ctx.unknownIsInactive ? result === true : result !== false;
|
|
2827
3008
|
}
|
|
2828
3009
|
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false, chooseCmabVariants, userVisitCountOverride, runtime = {}) {
|
|
@@ -2904,57 +3085,89 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2904
3085
|
experimentsInfo: [...experimentsInfo, ...assignment.experimentsInfo],
|
|
2905
3086
|
});
|
|
2906
3087
|
}
|
|
3088
|
+
function frameActivation(frame, ctx) {
|
|
3089
|
+
return checkActivationCondition(frame.activation_condition, ctx.personalizationHeaders, ctx.currentPageUrl, ctx.languages, ctx.browserPlatform, ctx.userToken, ctx.userVisitCount, ctx.runJsFunction, ctx.logger);
|
|
3090
|
+
}
|
|
2907
3091
|
function activateFrames(ctx) {
|
|
2908
3092
|
return ctx.project.frames.filter((frame) => {
|
|
2909
3093
|
if (!frame.experiments)
|
|
2910
3094
|
return false;
|
|
2911
|
-
const frameActivated =
|
|
3095
|
+
const frameActivated = frameActivation(frame, ctx);
|
|
2912
3096
|
return ctx.unknownIsInactive ? frameActivated === true : !!frameActivated;
|
|
2913
3097
|
});
|
|
2914
3098
|
}
|
|
3099
|
+
function undecidedFrames(ctx) {
|
|
3100
|
+
if (!ctx.unknownIsInactive)
|
|
3101
|
+
return [];
|
|
3102
|
+
return ctx.project.frames.filter((frame) => {
|
|
3103
|
+
if (!frame.experiments)
|
|
3104
|
+
return false;
|
|
3105
|
+
const activated = frameActivation(frame, ctx);
|
|
3106
|
+
return activated !== true && activated !== false;
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
2915
3109
|
async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants) {
|
|
2916
|
-
var _a, _b, _c;
|
|
3110
|
+
var _a, _b, _c, _d;
|
|
2917
3111
|
const { project, userToken, logger } = ctx;
|
|
2918
3112
|
const experimentsInfo = [];
|
|
2919
3113
|
const variantsAndElements = [];
|
|
2920
|
-
const
|
|
3114
|
+
const audienceResult = new Map();
|
|
2921
3115
|
const reachableByLane = {};
|
|
3116
|
+
const undecidableLanes = new Set();
|
|
2922
3117
|
for (const frame of activeFrames) {
|
|
2923
3118
|
for (const experiment of (_a = frame.experiments) !== null && _a !== void 0 ? _a : []) {
|
|
2924
3119
|
const metadata = experiment.metadata;
|
|
2925
3120
|
const laneIds = experimentLaneIds(metadata);
|
|
2926
3121
|
if (laneIds.length === 0)
|
|
2927
3122
|
continue;
|
|
2928
|
-
const
|
|
2929
|
-
|
|
2930
|
-
if (
|
|
3123
|
+
const result = checkAllAudiences(metadata, ctx);
|
|
3124
|
+
audienceResult.set(metadata.experiment_id, result);
|
|
3125
|
+
if (audiencesPass(result, ctx)) {
|
|
2931
3126
|
for (const laneId of laneIds) {
|
|
2932
3127
|
if (!reachableByLane[laneId])
|
|
2933
3128
|
reachableByLane[laneId] = [];
|
|
2934
3129
|
reachableByLane[laneId].push(metadata.experiment_id);
|
|
2935
3130
|
}
|
|
2936
3131
|
}
|
|
3132
|
+
else if (ctx.unknownIsInactive && result !== false) {
|
|
3133
|
+
for (const laneId of laneIds)
|
|
3134
|
+
if (!validatedClaims[laneId])
|
|
3135
|
+
undecidableLanes.add(laneId);
|
|
3136
|
+
}
|
|
2937
3137
|
}
|
|
2938
3138
|
}
|
|
3139
|
+
for (const frame of undecidedFrames(ctx))
|
|
3140
|
+
for (const experiment of (_b = frame.experiments) !== null && _b !== void 0 ? _b : [])
|
|
3141
|
+
for (const laneId of experimentLaneIds(experiment.metadata))
|
|
3142
|
+
if (!validatedClaims[laneId])
|
|
3143
|
+
undecidableLanes.add(laneId);
|
|
2939
3144
|
const excludedExperimentIds = resolveLaneWinners(validatedClaims, reachableByLane, project.lanes, userToken);
|
|
2940
3145
|
const updatedClaims = { ...validatedClaims };
|
|
2941
3146
|
const assignmentEntries = [];
|
|
2942
3147
|
let hasActivatedFrames = false;
|
|
2943
3148
|
for (const frame of activeFrames) {
|
|
2944
3149
|
let atLeastOneExperimentWasActivated = false;
|
|
2945
|
-
for (const experiment of (
|
|
3150
|
+
for (const experiment of (_c = frame.experiments) !== null && _c !== void 0 ? _c : []) {
|
|
2946
3151
|
if (atLeastOneExperimentWasActivated)
|
|
2947
3152
|
break;
|
|
2948
3153
|
const metadata = experiment.metadata;
|
|
2949
3154
|
const variants = experiment.variants;
|
|
2950
3155
|
const elements = frame.elements;
|
|
3156
|
+
if (ctx.unknownIsInactive &&
|
|
3157
|
+
experimentLaneIds(metadata).some((id) => undecidableLanes.has(id))) {
|
|
3158
|
+
logger.warn('Leaving the frame to the browser: a lane member needs the page', 'undecidable_lane', { experiment_id: metadata.experiment_id });
|
|
3159
|
+
break;
|
|
3160
|
+
}
|
|
2951
3161
|
if (excludedExperimentIds.has(metadata.experiment_id))
|
|
2952
3162
|
continue;
|
|
2953
|
-
let
|
|
2954
|
-
if (
|
|
2955
|
-
|
|
3163
|
+
let result = audienceResult.get(metadata.experiment_id);
|
|
3164
|
+
if (result === undefined)
|
|
3165
|
+
result = checkAllAudiences(metadata, ctx);
|
|
3166
|
+
if (result !== true && result !== false && ctx.unknownIsInactive) {
|
|
3167
|
+
logger.warn('Leaving the frame to the browser: an audience needs the page', 'undecidable_audience', { experiment_id: metadata.experiment_id });
|
|
3168
|
+
break;
|
|
2956
3169
|
}
|
|
2957
|
-
if (!
|
|
3170
|
+
if (!audiencesPass(result, ctx))
|
|
2958
3171
|
continue;
|
|
2959
3172
|
atLeastOneExperimentWasActivated = true;
|
|
2960
3173
|
hasActivatedFrames = true;
|
|
@@ -3008,7 +3221,7 @@ async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVarian
|
|
|
3008
3221
|
try {
|
|
3009
3222
|
mlVariantIds = await chooseCmabVariants(Array.from(cmabMetadataById.values()), userToken);
|
|
3010
3223
|
}
|
|
3011
|
-
catch (
|
|
3224
|
+
catch (_e) {
|
|
3012
3225
|
mlVariantIds = new Map();
|
|
3013
3226
|
}
|
|
3014
3227
|
}
|
|
@@ -3019,7 +3232,7 @@ async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVarian
|
|
|
3019
3232
|
}
|
|
3020
3233
|
const { metadata, variants, elements } = entry;
|
|
3021
3234
|
let mlVariantId = metadata.type === 'CMAB'
|
|
3022
|
-
? ((
|
|
3235
|
+
? ((_d = mlVariantIds.get(metadata.experiment_id)) !== null && _d !== void 0 ? _d : null)
|
|
3023
3236
|
: null;
|
|
3024
3237
|
if (mlVariantId && !variants.some((v) => v.id === mlVariantId)) {
|
|
3025
3238
|
logger.warn(`CMAB variant ${mlVariantId} not in experiment ${metadata.experiment_id}`, 'cmab_variant_not_found', { experiment_id: metadata.experiment_id });
|
|
@@ -3562,7 +3775,7 @@ function shouldSdkWaitForHydration(config) {
|
|
|
3562
3775
|
!isVariantRuntimeLifecycleEnabled(config));
|
|
3563
3776
|
}
|
|
3564
3777
|
|
|
3565
|
-
function createRevealCoordinator(revealPage, reportError = () => { }) {
|
|
3778
|
+
function createRevealCoordinator(revealPage, reportError = () => { }, onForced = () => { }) {
|
|
3566
3779
|
const notify = (listener, reason) => {
|
|
3567
3780
|
try {
|
|
3568
3781
|
listener(reason);
|
|
@@ -3614,6 +3827,18 @@ function createRevealCoordinator(revealPage, reportError = () => { }) {
|
|
|
3614
3827
|
reveal(reason);
|
|
3615
3828
|
},
|
|
3616
3829
|
forceReveal(reason) {
|
|
3830
|
+
if (pageRevealed)
|
|
3831
|
+
return;
|
|
3832
|
+
try {
|
|
3833
|
+
onForced(reason);
|
|
3834
|
+
}
|
|
3835
|
+
catch (error) {
|
|
3836
|
+
try {
|
|
3837
|
+
reportError(error);
|
|
3838
|
+
}
|
|
3839
|
+
catch (_a) {
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3617
3842
|
reveal(reason);
|
|
3618
3843
|
},
|
|
3619
3844
|
block(batchId) {
|
|
@@ -3801,10 +4026,6 @@ function notifyEventsFlushed() {
|
|
|
3801
4026
|
}
|
|
3802
4027
|
}
|
|
3803
4028
|
|
|
3804
|
-
function isRecord(value) {
|
|
3805
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
3806
|
-
}
|
|
3807
|
-
|
|
3808
4029
|
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 ' +
|
|
3809
4030
|
'AX GF GP MF MQ RE YT ' +
|
|
3810
4031
|
'IS LI NO ' +
|
|
@@ -5008,6 +5229,7 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
5008
5229
|
Coframe.debug.laneClaims = laneClaims;
|
|
5009
5230
|
}
|
|
5010
5231
|
if (config.trackingOnly) {
|
|
5232
|
+
beginProxyBatch([]);
|
|
5011
5233
|
variantRendered();
|
|
5012
5234
|
}
|
|
5013
5235
|
else {
|
|
@@ -5030,7 +5252,7 @@ async function modifyPage(Coframe, configResult, projectResult, analytics, lifec
|
|
|
5030
5252
|
var _a;
|
|
5031
5253
|
const { config, timeoutMs, previewVariantId, isSessionVariant, integrationStartTime, executionStartTime, } = configResult;
|
|
5032
5254
|
const { project: cdnProject, personalizationHeaders, assignmentContext, } = projectResult;
|
|
5033
|
-
const revealCoordinator = (_a = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.revealCoordinator) !== null && _a !== void 0 ? _a : createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger));
|
|
5255
|
+
const revealCoordinator = (_a = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.revealCoordinator) !== null && _a !== void 0 ? _a : createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger), () => beginProxyBatch([]));
|
|
5034
5256
|
const runtime = lifecycle === null || lifecycle === void 0 ? void 0 : lifecycle.runtime;
|
|
5035
5257
|
if (runtime && config.waitForHydration) {
|
|
5036
5258
|
if (findLastCmd(isPageHydratedCmd)) {
|
|
@@ -5397,6 +5619,7 @@ async function resolveConfig(Coframe, revealActions) {
|
|
|
5397
5619
|
}
|
|
5398
5620
|
|
|
5399
5621
|
async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit, previewVariantId) {
|
|
5622
|
+
var _a;
|
|
5400
5623
|
const logger = Coframe.logger;
|
|
5401
5624
|
let userToken;
|
|
5402
5625
|
if (config.waitForUserToken) {
|
|
@@ -5414,7 +5637,8 @@ async function resolveIdentity(config, Coframe, timeoutMs, gracefulExit, preview
|
|
|
5414
5637
|
}
|
|
5415
5638
|
}
|
|
5416
5639
|
else {
|
|
5417
|
-
|
|
5640
|
+
const proxied = (_a = findLastCmd((cmd) => typeof cmd.proxyUserToken === 'string' && !!cmd.proxyUserToken)) === null || _a === void 0 ? void 0 : _a.proxyUserToken;
|
|
5641
|
+
userToken = proxied || getOrGenerateUserToken();
|
|
5418
5642
|
}
|
|
5419
5643
|
Coframe.userToken = userToken;
|
|
5420
5644
|
const persistCookies = () => {
|
|
@@ -6407,7 +6631,7 @@ const Coframe = {
|
|
|
6407
6631
|
},
|
|
6408
6632
|
};
|
|
6409
6633
|
Coframe.logger = createLogger(() => Coframe);
|
|
6410
|
-
const revealCoordinator = createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger));
|
|
6634
|
+
const revealCoordinator = createRevealCoordinator(revealPage, logRevealListenerError(() => Coframe.logger), () => beginProxyBatch([]));
|
|
6411
6635
|
let activeRuntime = null;
|
|
6412
6636
|
function clearActiveRuntime() {
|
|
6413
6637
|
activeRuntime === null || activeRuntime === void 0 ? void 0 : activeRuntime.destroy();
|
package/dist/pkg/server.cjs.js
CHANGED
|
@@ -236,6 +236,8 @@ const ID_FIELDS = ['projectId', 'experimentId', 'userToken'];
|
|
|
236
236
|
function invalidAssignmentIdField(body) {
|
|
237
237
|
for (const field of ID_FIELDS) {
|
|
238
238
|
const value = body[field];
|
|
239
|
+
if (field === 'experimentId' && value === undefined)
|
|
240
|
+
continue;
|
|
239
241
|
if (typeof value !== 'string' || !value) {
|
|
240
242
|
return { field, message: 'is required' };
|
|
241
243
|
}
|
|
@@ -515,6 +517,25 @@ async function flushFeatureFlagEvents() {
|
|
|
515
517
|
}
|
|
516
518
|
}
|
|
517
519
|
|
|
520
|
+
const IDLE_MS = 60000;
|
|
521
|
+
let dispatcher;
|
|
522
|
+
function runsOnNode() {
|
|
523
|
+
var _a;
|
|
524
|
+
return (typeof process !== 'undefined' &&
|
|
525
|
+
typeof ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node) === 'string' &&
|
|
526
|
+
!('EdgeRuntime' in globalThis));
|
|
527
|
+
}
|
|
528
|
+
function edgeDispatcher() {
|
|
529
|
+
if (!runsOnNode())
|
|
530
|
+
return Promise.resolve(undefined);
|
|
531
|
+
dispatcher !== null && dispatcher !== void 0 ? dispatcher : (dispatcher = import('undici').then(({ Agent }) => new Agent({ keepAliveTimeout: IDLE_MS, keepAliveMaxTimeout: IDLE_MS }), () => undefined));
|
|
532
|
+
return dispatcher;
|
|
533
|
+
}
|
|
534
|
+
async function edgeFetch(url, init = {}) {
|
|
535
|
+
const agent = await edgeDispatcher();
|
|
536
|
+
return fetch(url, agent ? { ...init, dispatcher: agent } : init);
|
|
537
|
+
}
|
|
538
|
+
|
|
518
539
|
const DEFAULT_ASSIGNMENT_TIMEOUT_MS = 1500;
|
|
519
540
|
function normalizeRequestHeaders(headers) {
|
|
520
541
|
const normalized = new Headers();
|
|
@@ -601,6 +622,13 @@ function effectiveVisitCount(headers) {
|
|
|
601
622
|
const next = nextVisitCount(existing, lastVisitTime, Date.now());
|
|
602
623
|
return Number.isSafeInteger(next) ? next : existing;
|
|
603
624
|
}
|
|
625
|
+
function isAssignedVariant(value) {
|
|
626
|
+
return (isRecord(value) &&
|
|
627
|
+
typeof value.id === 'string' &&
|
|
628
|
+
typeof value.slug === 'string' &&
|
|
629
|
+
typeof value.experimentId === 'string' &&
|
|
630
|
+
typeof value.iterationId === 'string');
|
|
631
|
+
}
|
|
604
632
|
function isExperimentAssignmentResult(value, experimentId) {
|
|
605
633
|
if (!isRecord(value) || !isRecord(value.projectData))
|
|
606
634
|
return false;
|
|
@@ -617,22 +645,32 @@ function isExperimentAssignmentResult(value, experimentId) {
|
|
|
617
645
|
context.userVisitCount < 0) {
|
|
618
646
|
return false;
|
|
619
647
|
}
|
|
648
|
+
if (value.variants !== undefined && !Array.isArray(value.variants))
|
|
649
|
+
return false;
|
|
650
|
+
if (Array.isArray(value.variants) && !value.variants.every(isAssignedVariant))
|
|
651
|
+
return false;
|
|
652
|
+
if (experimentId === undefined) {
|
|
653
|
+
return value.variant === null && Array.isArray(value.variants);
|
|
654
|
+
}
|
|
620
655
|
if (value.variant === null) {
|
|
621
656
|
return typeof value.reason === 'string';
|
|
622
657
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return (typeof value.variant.id === 'string' &&
|
|
626
|
-
typeof value.variant.slug === 'string' &&
|
|
627
|
-
value.variant.experimentId === experimentId &&
|
|
628
|
-
typeof value.variant.iterationId === 'string');
|
|
658
|
+
return (isAssignedVariant(value.variant) &&
|
|
659
|
+
value.variant.experimentId === experimentId);
|
|
629
660
|
}
|
|
630
661
|
function generateUserToken() {
|
|
631
662
|
return generateUUID();
|
|
632
663
|
}
|
|
633
|
-
|
|
664
|
+
function getExperimentAssignment(options) {
|
|
665
|
+
var _a;
|
|
666
|
+
return fetchAssignment(options, (_a = options.experimentId) !== null && _a !== void 0 ? _a : '');
|
|
667
|
+
}
|
|
668
|
+
function getAssignments(options) {
|
|
669
|
+
return fetchAssignment(options, undefined);
|
|
670
|
+
}
|
|
671
|
+
async function fetchAssignment(options, experimentId) {
|
|
634
672
|
var _a, _b, _c, _d, _e, _f;
|
|
635
|
-
const idProblem = invalidAssignmentIdField(options);
|
|
673
|
+
const idProblem = invalidAssignmentIdField({ ...options, experimentId });
|
|
636
674
|
if (idProblem)
|
|
637
675
|
throw new Error(`${idProblem.field} ${idProblem.message}`);
|
|
638
676
|
if (options.timeoutMs !== undefined &&
|
|
@@ -658,7 +696,7 @@ async function getExperimentAssignment(options) {
|
|
|
658
696
|
: clampHeader(incoming.get('referer'), ASSIGNMENT_FIELD_LIMITS.referrer);
|
|
659
697
|
const requestBody = {
|
|
660
698
|
projectId: options.projectId,
|
|
661
|
-
experimentId:
|
|
699
|
+
...(experimentId !== undefined ? { experimentId } : {}),
|
|
662
700
|
userToken: options.userToken,
|
|
663
701
|
context: {
|
|
664
702
|
currentUrl: options.currentUrl,
|
|
@@ -672,7 +710,7 @@ async function getExperimentAssignment(options) {
|
|
|
672
710
|
userVisitCount,
|
|
673
711
|
},
|
|
674
712
|
};
|
|
675
|
-
const response = await
|
|
713
|
+
const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
|
|
676
714
|
method: 'POST',
|
|
677
715
|
headers: createAssignmentHeaders(incoming),
|
|
678
716
|
signal: controller.signal,
|
|
@@ -681,7 +719,7 @@ async function getExperimentAssignment(options) {
|
|
|
681
719
|
if (!response.ok)
|
|
682
720
|
return undefined;
|
|
683
721
|
const result = await response.json();
|
|
684
|
-
return isExperimentAssignmentResult(result,
|
|
722
|
+
return isExperimentAssignmentResult(result, experimentId)
|
|
685
723
|
? result
|
|
686
724
|
: undefined;
|
|
687
725
|
}
|
|
@@ -697,7 +735,7 @@ async function requestFeatureFlags(options) {
|
|
|
697
735
|
const controller = new AbortController();
|
|
698
736
|
const timeout = setTimeout(() => controller.abort(), (_a = options.requestTimeoutMs) !== null && _a !== void 0 ? _a : 3000);
|
|
699
737
|
try {
|
|
700
|
-
const response = await
|
|
738
|
+
const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
|
|
701
739
|
method: 'POST',
|
|
702
740
|
headers: {
|
|
703
741
|
'Content-Type': 'application/json',
|
|
@@ -784,7 +822,7 @@ async function fetchAntiflickerFromEdge(projectId, currentUrl, referrer, userCon
|
|
|
784
822
|
const config = encodeURIComponent(JSON.stringify(payload));
|
|
785
823
|
const edgeUrl = new URL(`${"https://edge.cofra.me"}/cf.css`);
|
|
786
824
|
edgeUrl.searchParams.set('config', config);
|
|
787
|
-
const response = await
|
|
825
|
+
const response = await edgeFetch(edgeUrl, {
|
|
788
826
|
headers,
|
|
789
827
|
});
|
|
790
828
|
if (!response.ok)
|
|
@@ -836,6 +874,7 @@ async function getProjectData(options) {
|
|
|
836
874
|
exports.evaluateFlags = evaluateFlags;
|
|
837
875
|
exports.flushFeatureFlagEvents = flushFeatureFlagEvents;
|
|
838
876
|
exports.generateUserToken = generateUserToken;
|
|
877
|
+
exports.getAssignments = getAssignments;
|
|
839
878
|
exports.getExperimentAssignment = getExperimentAssignment;
|
|
840
879
|
exports.getFeatureFlag = getFeatureFlag;
|
|
841
880
|
exports.getFeatureFlagPayload = getFeatureFlagPayload;
|