@coframe/sdk 1.1.21 → 1.1.23
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 +6 -0
- package/dist/pkg/cf.cjs.js +335 -52
- package/dist/pkg/cf.esm.mjs +335 -52
- package/dist/pkg/types/index.d.ts +2 -0
- package/dist/pkg/types/react.d.ts +2 -0
- package/dist/pkg/types/server.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -65,6 +65,12 @@ only has to forward `<ingestUrl>/*` to `https://ingest.app.coframe.com/ingest/v2
|
|
|
65
65
|
with the path unchanged. Both overrides are optional and retain the compiled
|
|
66
66
|
Coframe endpoints when omitted.
|
|
67
67
|
|
|
68
|
+
Configuring either `projectUrl` or `ingestUrl` makes CMAB experiments use
|
|
69
|
+
deterministic local assignment without calling the Edge assignment API.
|
|
70
|
+
Configure both overrides when the browser must avoid the SDK-compiled Coframe
|
|
71
|
+
project and ingest endpoints entirely. Debug tools remain controlled by
|
|
72
|
+
`enableDebug`.
|
|
73
|
+
|
|
68
74
|
### URL controls
|
|
69
75
|
|
|
70
76
|
The npm entry uses the same browser URL controls as the script-tag and Edge
|
package/dist/pkg/cf.cjs.js
CHANGED
|
@@ -1079,7 +1079,7 @@ function warnEventsNotSent() {
|
|
|
1079
1079
|
}
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
|
-
const SDK_VERSION = '2.
|
|
1082
|
+
const SDK_VERSION = '2.8.0';
|
|
1083
1083
|
const queuedEvents = [];
|
|
1084
1084
|
const queuedMonitoringEvents = [];
|
|
1085
1085
|
const queuedSessions = [];
|
|
@@ -2567,7 +2567,7 @@ function checkAllAudiences(metadata, ctx) {
|
|
|
2567
2567
|
}
|
|
2568
2568
|
return result;
|
|
2569
2569
|
}
|
|
2570
|
-
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false) {
|
|
2570
|
+
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false, chooseCmabVariants) {
|
|
2571
2571
|
var _a;
|
|
2572
2572
|
const experimentsInfo = [];
|
|
2573
2573
|
const cloakBot = !!((_a = project.settings) === null || _a === void 0 ? void 0 : _a.bot_cloaking_enabled) &&
|
|
@@ -2595,7 +2595,7 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2595
2595
|
const validatedClaims = validateClaims(laneClaims, project.lanes);
|
|
2596
2596
|
const previewVariantIds = splitPreviewVariantIds(previewVariantId);
|
|
2597
2597
|
if (previewVariantIds.length > 0) {
|
|
2598
|
-
const variantsAndElements = previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback);
|
|
2598
|
+
const variantsAndElements = await previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback);
|
|
2599
2599
|
if (variantsAndElements) {
|
|
2600
2600
|
return buildResponse({
|
|
2601
2601
|
variantsAndElements,
|
|
@@ -2635,7 +2635,7 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2635
2635
|
final_state: false,
|
|
2636
2636
|
level: GLOBAL_HOLDOUT,
|
|
2637
2637
|
});
|
|
2638
|
-
const assignment = assignFrames(activeFrames, validatedClaims, ctx);
|
|
2638
|
+
const assignment = await assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants);
|
|
2639
2639
|
return buildResponse({
|
|
2640
2640
|
...assignment,
|
|
2641
2641
|
experimentsInfo: [...experimentsInfo, ...assignment.experimentsInfo],
|
|
@@ -2651,7 +2651,8 @@ function activateFrames(ctx) {
|
|
|
2651
2651
|
return true;
|
|
2652
2652
|
});
|
|
2653
2653
|
}
|
|
2654
|
-
function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
2654
|
+
async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants) {
|
|
2655
|
+
var _a;
|
|
2655
2656
|
const { project, userToken, logger } = ctx;
|
|
2656
2657
|
const experimentsInfo = [];
|
|
2657
2658
|
const variantsAndElements = [];
|
|
@@ -2677,6 +2678,7 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2677
2678
|
}
|
|
2678
2679
|
const excludedExperimentIds = resolveLaneWinners(validatedClaims, reachableByLane, project.lanes, userToken);
|
|
2679
2680
|
const updatedClaims = { ...validatedClaims };
|
|
2681
|
+
const assignmentEntries = [];
|
|
2680
2682
|
let hasActivatedFrames = false;
|
|
2681
2683
|
for (const frame of activeFrames) {
|
|
2682
2684
|
let atLeastOneExperimentWasActivated = false;
|
|
@@ -2702,51 +2704,88 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2702
2704
|
const experimentHoldoutInfo = metadata.holdout_info;
|
|
2703
2705
|
const experimentUniverseVid = chooseVariant(userToken, experimentHoldoutInfo.variant_bins, experimentHoldoutInfo.seed, experimentHoldoutInfo.holdout_variant_id);
|
|
2704
2706
|
if (experimentUniverseVid === experimentHoldoutInfo.holdout_variant_id) {
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2707
|
+
assignmentEntries.push({
|
|
2708
|
+
kind: 'info',
|
|
2709
|
+
info: {
|
|
2710
|
+
variant_id: experimentHoldoutInfo.holdout_variant_id,
|
|
2711
|
+
variant_slug: experimentHoldoutInfo.holdout_variant_slug,
|
|
2712
|
+
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2713
|
+
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2714
|
+
iteration_id: '',
|
|
2715
|
+
level: EXPERIMENT_HOLDOUT,
|
|
2716
|
+
final_state: true,
|
|
2717
|
+
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2718
|
+
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2719
|
+
},
|
|
2720
|
+
});
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2723
|
+
assignmentEntries.push({
|
|
2724
|
+
kind: 'info',
|
|
2725
|
+
info: {
|
|
2726
|
+
variant_id: experimentHoldoutInfo.treatment_variant_id,
|
|
2727
|
+
variant_slug: experimentHoldoutInfo.treatment_variant_slug,
|
|
2708
2728
|
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2709
2729
|
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2710
2730
|
iteration_id: '',
|
|
2711
2731
|
level: EXPERIMENT_HOLDOUT,
|
|
2712
|
-
final_state:
|
|
2732
|
+
final_state: false,
|
|
2713
2733
|
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2714
2734
|
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2715
|
-
}
|
|
2716
|
-
continue;
|
|
2717
|
-
}
|
|
2718
|
-
experimentsInfo.push({
|
|
2719
|
-
variant_id: experimentHoldoutInfo.treatment_variant_id,
|
|
2720
|
-
variant_slug: experimentHoldoutInfo.treatment_variant_slug,
|
|
2721
|
-
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2722
|
-
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2723
|
-
iteration_id: '',
|
|
2724
|
-
level: EXPERIMENT_HOLDOUT,
|
|
2725
|
-
final_state: false,
|
|
2726
|
-
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2727
|
-
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2735
|
+
},
|
|
2728
2736
|
});
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2737
|
+
assignmentEntries.push({ kind: 'variant', metadata, variants, elements });
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
const cmabMetadataById = new Map();
|
|
2741
|
+
for (const entry of assignmentEntries) {
|
|
2742
|
+
if (entry.kind === 'variant' && entry.metadata.type === 'CMAB') {
|
|
2743
|
+
cmabMetadataById.set(entry.metadata.experiment_id, entry.metadata);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
let mlVariantIds = new Map();
|
|
2747
|
+
if (chooseCmabVariants && cmabMetadataById.size > 0) {
|
|
2748
|
+
try {
|
|
2749
|
+
mlVariantIds = await chooseCmabVariants(Array.from(cmabMetadataById.values()), userToken);
|
|
2750
|
+
}
|
|
2751
|
+
catch (_b) {
|
|
2752
|
+
mlVariantIds = new Map();
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
for (const entry of assignmentEntries) {
|
|
2756
|
+
if (entry.kind === 'info') {
|
|
2757
|
+
experimentsInfo.push(entry.info);
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
const { metadata, variants, elements } = entry;
|
|
2761
|
+
let mlVariantId = metadata.type === 'CMAB'
|
|
2762
|
+
? ((_a = mlVariantIds.get(metadata.experiment_id)) !== null && _a !== void 0 ? _a : null)
|
|
2763
|
+
: null;
|
|
2764
|
+
if (mlVariantId && !variants.some((v) => v.id === mlVariantId)) {
|
|
2765
|
+
logger.warn(`CMAB variant ${mlVariantId} not in experiment ${metadata.experiment_id}`, 'cmab_variant_not_found', { experiment_id: metadata.experiment_id });
|
|
2766
|
+
mlVariantId = null;
|
|
2767
|
+
}
|
|
2768
|
+
const variantId = mlVariantId ||
|
|
2769
|
+
chooseVariant(userToken, metadata.allocation, metadata.seed, '');
|
|
2770
|
+
const variant = variants.find((v) => v.id === variantId);
|
|
2771
|
+
if (!variant) {
|
|
2772
|
+
logger.error(`Variant not found for experiment ${metadata.experiment_id}`, 'variant_not_found', {
|
|
2740
2773
|
experiment_id: metadata.experiment_id,
|
|
2741
|
-
experiment_slug: metadata.slug,
|
|
2742
|
-
iteration_id: metadata.iteration_id,
|
|
2743
|
-
level: EXPERIMENT,
|
|
2744
|
-
final_state: true,
|
|
2745
|
-
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2746
|
-
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2747
2774
|
});
|
|
2748
|
-
|
|
2775
|
+
continue;
|
|
2749
2776
|
}
|
|
2777
|
+
experimentsInfo.push({
|
|
2778
|
+
variant_id: variant.id,
|
|
2779
|
+
variant_slug: variant.slug,
|
|
2780
|
+
experiment_id: metadata.experiment_id,
|
|
2781
|
+
experiment_slug: metadata.slug,
|
|
2782
|
+
iteration_id: metadata.iteration_id,
|
|
2783
|
+
level: EXPERIMENT,
|
|
2784
|
+
final_state: true,
|
|
2785
|
+
holdout_experiment_id: metadata.holdout_info.holdout_experiment_id,
|
|
2786
|
+
holdout_experiment_slug: metadata.holdout_info.holdout_experiment_slug,
|
|
2787
|
+
});
|
|
2788
|
+
variantsAndElements.push({ variant, elements });
|
|
2750
2789
|
}
|
|
2751
2790
|
return {
|
|
2752
2791
|
variantsAndElements,
|
|
@@ -2755,7 +2794,7 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2755
2794
|
laneClaims: updatedClaims,
|
|
2756
2795
|
};
|
|
2757
2796
|
}
|
|
2758
|
-
function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback) {
|
|
2797
|
+
async function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback) {
|
|
2759
2798
|
const previewRollouts = previewVariantIds.includes(ROLLOUTS_PREVIEW_ID);
|
|
2760
2799
|
const requestedVariantIds = previewVariantIds.filter((id) => !isPreviewSentinel(id));
|
|
2761
2800
|
const requestedVariants = findVariants(ctx.project.frames, requestedVariantIds);
|
|
@@ -2768,7 +2807,7 @@ function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback
|
|
|
2768
2807
|
window.alert(`[Coframe] Variant preview unavailable: ${previewVariantIds.join(',')}.`);
|
|
2769
2808
|
}
|
|
2770
2809
|
const rolledOutVariants = previewRollouts && !previewVariantIds.includes(GLOBAL_HOLDOUT)
|
|
2771
|
-
? assignFrames(keepRolledOutExperiments(activateFrames(ctx)), validatedClaims, ctx).variantsAndElements
|
|
2810
|
+
? (await assignFrames(keepRolledOutExperiments(activateFrames(ctx)), validatedClaims, ctx)).variantsAndElements
|
|
2772
2811
|
: [];
|
|
2773
2812
|
return uniqueByVariantId([...rolledOutVariants, ...requestedVariants]);
|
|
2774
2813
|
}
|
|
@@ -2846,6 +2885,226 @@ function getNavigationType(isInitialApply) {
|
|
|
2846
2885
|
return undefined;
|
|
2847
2886
|
}
|
|
2848
2887
|
|
|
2888
|
+
const CMAB_ASSIGNMENT_TIMEOUT_MS = 500;
|
|
2889
|
+
const CMAB_ASSIGNMENT_MAX_EXPERIMENTS = 64;
|
|
2890
|
+
const ASSIGNMENT_REQUEST_SCHEMA = 'coframe.edge.assign-request/2';
|
|
2891
|
+
const ASSIGNMENT_RESPONSE_SCHEMA = 'coframe.edge.assign-response/2';
|
|
2892
|
+
async function requestAssignmentChunk(options, metadata, userToken) {
|
|
2893
|
+
var _a, _b, _c, _d, _e;
|
|
2894
|
+
const assignments = new Map();
|
|
2895
|
+
const decisions = new Map();
|
|
2896
|
+
let fallbackReason = 'request_failed';
|
|
2897
|
+
let timeoutId;
|
|
2898
|
+
let cancel;
|
|
2899
|
+
try {
|
|
2900
|
+
if (options.disableRemoteAssignment) {
|
|
2901
|
+
fallbackReason = 'network_policy';
|
|
2902
|
+
return { assignments, decisions };
|
|
2903
|
+
}
|
|
2904
|
+
const controller = new AbortController();
|
|
2905
|
+
const budget = Date.now() + CMAB_ASSIGNMENT_TIMEOUT_MS;
|
|
2906
|
+
const deadline = options.deadline !== undefined && options.deadline < budget
|
|
2907
|
+
? options.deadline
|
|
2908
|
+
: budget;
|
|
2909
|
+
const remaining = deadline - Date.now();
|
|
2910
|
+
if (((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) || remaining <= 0) {
|
|
2911
|
+
fallbackReason = ((_b = options.signal) === null || _b === void 0 ? void 0 : _b.aborted)
|
|
2912
|
+
? 'cancelled'
|
|
2913
|
+
: 'deadline_exceeded';
|
|
2914
|
+
return { assignments, decisions };
|
|
2915
|
+
}
|
|
2916
|
+
const cancelled = new Promise((resolve) => {
|
|
2917
|
+
var _a;
|
|
2918
|
+
cancel = () => {
|
|
2919
|
+
var _a;
|
|
2920
|
+
fallbackReason = ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
|
|
2921
|
+
? 'cancelled'
|
|
2922
|
+
: 'deadline_exceeded';
|
|
2923
|
+
resolve(null);
|
|
2924
|
+
controller.abort();
|
|
2925
|
+
};
|
|
2926
|
+
timeoutId = setTimeout(cancel, remaining);
|
|
2927
|
+
(_a = options.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', cancel);
|
|
2928
|
+
});
|
|
2929
|
+
const userContext = sanitizeUserContext(options.userContext);
|
|
2930
|
+
const pending = fetch(`${"https://edge.cofra.me"}/api/v2/assign`, {
|
|
2931
|
+
method: 'POST',
|
|
2932
|
+
credentials: 'omit',
|
|
2933
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2934
|
+
signal: controller.signal,
|
|
2935
|
+
body: JSON.stringify({
|
|
2936
|
+
schema: ASSIGNMENT_REQUEST_SCHEMA,
|
|
2937
|
+
project_id: options.projectId,
|
|
2938
|
+
user_token: userToken,
|
|
2939
|
+
current_url: options.currentUrl,
|
|
2940
|
+
referrer: options.referrer,
|
|
2941
|
+
experiment_ids: metadata.map((item) => item.experiment_id),
|
|
2942
|
+
...(userContext ? { user_context: userContext } : {}),
|
|
2943
|
+
}),
|
|
2944
|
+
}).then((response) => {
|
|
2945
|
+
if (!response.ok)
|
|
2946
|
+
return null;
|
|
2947
|
+
fallbackReason = 'invalid_response';
|
|
2948
|
+
return response.json();
|
|
2949
|
+
});
|
|
2950
|
+
const payload = await Promise.race([pending, cancelled]);
|
|
2951
|
+
if (controller.signal.aborted ||
|
|
2952
|
+
((_c = options.signal) === null || _c === void 0 ? void 0 : _c.aborted) ||
|
|
2953
|
+
Date.now() >= deadline) {
|
|
2954
|
+
fallbackReason = ((_d = options.signal) === null || _d === void 0 ? void 0 : _d.aborted)
|
|
2955
|
+
? 'cancelled'
|
|
2956
|
+
: 'deadline_exceeded';
|
|
2957
|
+
return { assignments, decisions };
|
|
2958
|
+
}
|
|
2959
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.schema) !== ASSIGNMENT_RESPONSE_SCHEMA ||
|
|
2960
|
+
!Array.isArray(payload.assignments)) {
|
|
2961
|
+
return { assignments, decisions };
|
|
2962
|
+
}
|
|
2963
|
+
const requested = new Map(metadata.map((item) => [item.experiment_id, item]));
|
|
2964
|
+
const seen = new Set();
|
|
2965
|
+
for (const row of payload.assignments) {
|
|
2966
|
+
if (!row || typeof row.experiment_id !== 'string')
|
|
2967
|
+
continue;
|
|
2968
|
+
const id = row.experiment_id;
|
|
2969
|
+
const expected = requested.get(id);
|
|
2970
|
+
if (!expected)
|
|
2971
|
+
continue;
|
|
2972
|
+
if (seen.has(id)) {
|
|
2973
|
+
assignments.delete(id);
|
|
2974
|
+
decisions.delete(id);
|
|
2975
|
+
continue;
|
|
2976
|
+
}
|
|
2977
|
+
seen.add(id);
|
|
2978
|
+
if (typeof row.iteration_id !== 'string' ||
|
|
2979
|
+
row.iteration_id !== expected.iteration_id ||
|
|
2980
|
+
(row.variant_id !== null && typeof row.variant_id !== 'string'))
|
|
2981
|
+
continue;
|
|
2982
|
+
assignments.set(id, row.variant_id);
|
|
2983
|
+
const decision = {
|
|
2984
|
+
experiment_id: id,
|
|
2985
|
+
iteration_id: row.iteration_id,
|
|
2986
|
+
variant_id: row.variant_id,
|
|
2987
|
+
};
|
|
2988
|
+
for (const key of [
|
|
2989
|
+
'decision_source',
|
|
2990
|
+
'fallback_reason',
|
|
2991
|
+
'model_id',
|
|
2992
|
+
'feature_spec_version',
|
|
2993
|
+
'policy_id',
|
|
2994
|
+
'policy_version',
|
|
2995
|
+
'policy_route',
|
|
2996
|
+
'active_model_failure',
|
|
2997
|
+
'model_load_state',
|
|
2998
|
+
'scored_by',
|
|
2999
|
+
]) {
|
|
3000
|
+
if (typeof row[key] === 'string' && row[key].length <= 256)
|
|
3001
|
+
decision[key] = row[key];
|
|
3002
|
+
}
|
|
3003
|
+
if (typeof payload.assignment_request_id === 'string' &&
|
|
3004
|
+
payload.assignment_request_id.length <= 256) {
|
|
3005
|
+
decision.assignment_request_id = payload.assignment_request_id;
|
|
3006
|
+
}
|
|
3007
|
+
if (typeof row.assignment_probability === 'number' &&
|
|
3008
|
+
row.assignment_probability >= 0 &&
|
|
3009
|
+
row.assignment_probability <= 1) {
|
|
3010
|
+
decision.assignment_probability = row.assignment_probability;
|
|
3011
|
+
}
|
|
3012
|
+
if (typeof row.decision_latency_ms === 'number' &&
|
|
3013
|
+
row.decision_latency_ms >= 0 &&
|
|
3014
|
+
row.decision_latency_ms < Infinity) {
|
|
3015
|
+
decision.decision_latency_ms = row.decision_latency_ms;
|
|
3016
|
+
}
|
|
3017
|
+
if (Array.isArray(row.eligible_variant_ids) &&
|
|
3018
|
+
row.eligible_variant_ids.length <= 256 &&
|
|
3019
|
+
row.eligible_variant_ids.every((id) => typeof id === 'string' && id.length <= 256)) {
|
|
3020
|
+
decision.eligible_variant_ids = row.eligible_variant_ids;
|
|
3021
|
+
}
|
|
3022
|
+
if (row.action_probabilities &&
|
|
3023
|
+
typeof row.action_probabilities === 'object' &&
|
|
3024
|
+
!Array.isArray(row.action_probabilities)) {
|
|
3025
|
+
const keys = Object.keys(row.action_probabilities);
|
|
3026
|
+
if (keys.length <= 256 &&
|
|
3027
|
+
keys.every((key) => key.length <= 256 &&
|
|
3028
|
+
typeof row.action_probabilities[key] === 'number' &&
|
|
3029
|
+
row.action_probabilities[key] >= 0 &&
|
|
3030
|
+
row.action_probabilities[key] <= 1)) {
|
|
3031
|
+
decision.action_probabilities = row.action_probabilities;
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
decisions.set(id, decision);
|
|
3035
|
+
}
|
|
3036
|
+
return { assignments, decisions };
|
|
3037
|
+
}
|
|
3038
|
+
catch (_f) {
|
|
3039
|
+
return { assignments, decisions };
|
|
3040
|
+
}
|
|
3041
|
+
finally {
|
|
3042
|
+
if (timeoutId !== undefined)
|
|
3043
|
+
clearTimeout(timeoutId);
|
|
3044
|
+
if (cancel)
|
|
3045
|
+
(_e = options.signal) === null || _e === void 0 ? void 0 : _e.removeEventListener('abort', cancel);
|
|
3046
|
+
for (const item of metadata) {
|
|
3047
|
+
if (!decisions.has(item.experiment_id))
|
|
3048
|
+
decisions.set(item.experiment_id, {
|
|
3049
|
+
experiment_id: item.experiment_id,
|
|
3050
|
+
iteration_id: item.iteration_id,
|
|
3051
|
+
variant_id: null,
|
|
3052
|
+
decision_source: 'client_fallback',
|
|
3053
|
+
fallback_reason: fallbackReason,
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
function createCmabVariantBatchChooser(options) {
|
|
3059
|
+
const decisions = new Map();
|
|
3060
|
+
const choose = async (metadata, userToken) => {
|
|
3061
|
+
decisions.clear();
|
|
3062
|
+
const uniqueMetadata = Array.from(new Map(metadata.map((item) => [item.experiment_id, item])).values());
|
|
3063
|
+
const chunks = [];
|
|
3064
|
+
for (let index = 0; index < uniqueMetadata.length; index += CMAB_ASSIGNMENT_MAX_EXPERIMENTS) {
|
|
3065
|
+
chunks.push(uniqueMetadata.slice(index, index + CMAB_ASSIGNMENT_MAX_EXPERIMENTS));
|
|
3066
|
+
}
|
|
3067
|
+
const results = await Promise.all(chunks.map((chunk) => requestAssignmentChunk(options, chunk, userToken)));
|
|
3068
|
+
const assignments = new Map();
|
|
3069
|
+
for (const result of results) {
|
|
3070
|
+
result.assignments.forEach((value, id) => {
|
|
3071
|
+
assignments.set(id, value);
|
|
3072
|
+
});
|
|
3073
|
+
result.decisions.forEach((value, id) => {
|
|
3074
|
+
decisions.set(id, value);
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
return assignments;
|
|
3078
|
+
};
|
|
3079
|
+
choose.getDecisions = (experiments) => {
|
|
3080
|
+
const selected = [];
|
|
3081
|
+
for (const info of experiments) {
|
|
3082
|
+
const decision = decisions.get(info.experiment_id);
|
|
3083
|
+
if (!decision ||
|
|
3084
|
+
decision.iteration_id !== info.iteration_id ||
|
|
3085
|
+
!info.final_state)
|
|
3086
|
+
continue;
|
|
3087
|
+
if (decision.variant_id === info.variant_id)
|
|
3088
|
+
selected.push(decision);
|
|
3089
|
+
else
|
|
3090
|
+
selected.push({
|
|
3091
|
+
experiment_id: info.experiment_id,
|
|
3092
|
+
iteration_id: info.iteration_id,
|
|
3093
|
+
variant_id: info.variant_id,
|
|
3094
|
+
decision_source: 'client_fallback',
|
|
3095
|
+
fallback_reason: decision.variant_id === null
|
|
3096
|
+
? decision.fallback_reason || 'missing_choice'
|
|
3097
|
+
: 'unsupported_variant',
|
|
3098
|
+
...(decision.assignment_request_id
|
|
3099
|
+
? { assignment_request_id: decision.assignment_request_id }
|
|
3100
|
+
: {}),
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
return selected;
|
|
3104
|
+
};
|
|
3105
|
+
return choose;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
2849
3108
|
const CACHE_TTL_MS = 60 * 1000;
|
|
2850
3109
|
const cache = new Map();
|
|
2851
3110
|
function getCachedFrames(url) {
|
|
@@ -2972,7 +3231,7 @@ function observeUrlChanges(baseUrlMatch, onUrlChange) {
|
|
|
2972
3231
|
patchHistoryMethod('replaceState');
|
|
2973
3232
|
}
|
|
2974
3233
|
|
|
2975
|
-
function startSession(Coframe, cookiesEnabled, executionStartTime, personalizationHeaders, sessionContext) {
|
|
3234
|
+
function startSession(Coframe, cookiesEnabled, executionStartTime, personalizationHeaders, sessionContext, pageUrl = getCurrentPageUrl()) {
|
|
2976
3235
|
const sessionPayload = [
|
|
2977
3236
|
Coframe,
|
|
2978
3237
|
Coframe.orgId,
|
|
@@ -2985,7 +3244,7 @@ function startSession(Coframe, cookiesEnabled, executionStartTime, personalizati
|
|
|
2985
3244
|
getBrowserPlatform(),
|
|
2986
3245
|
getBrowserLanguages(),
|
|
2987
3246
|
getBrowserUserAgent(),
|
|
2988
|
-
|
|
3247
|
+
pageUrl,
|
|
2989
3248
|
getBrowserFignerprintInfo(),
|
|
2990
3249
|
];
|
|
2991
3250
|
{
|
|
@@ -3375,12 +3634,14 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3375
3634
|
let navigationAbortController = null;
|
|
3376
3635
|
observeUrlChanges(false, async (newPageUrl, _oldPageUrl) => {
|
|
3377
3636
|
var _a;
|
|
3378
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
3379
|
-
infiniteLoopProtection = 0;
|
|
3380
3637
|
if (navigationAbortController)
|
|
3381
3638
|
navigationAbortController.abort();
|
|
3382
3639
|
navigationAbortController = new AbortController();
|
|
3383
3640
|
const { signal } = navigationAbortController;
|
|
3641
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
3642
|
+
if (signal.aborted)
|
|
3643
|
+
return;
|
|
3644
|
+
infiniteLoopProtection = 0;
|
|
3384
3645
|
let pageProject = project;
|
|
3385
3646
|
let pagePersonalizationHeaders = Coframe.personalizationHeaders;
|
|
3386
3647
|
const cached = getCachedFrames(newPageUrl);
|
|
@@ -3408,7 +3669,7 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3408
3669
|
}
|
|
3409
3670
|
if (signal.aborted)
|
|
3410
3671
|
return;
|
|
3411
|
-
await applyVariants(newPageUrl, pageProject, pagePersonalizationHeaders);
|
|
3672
|
+
await applyVariants(newPageUrl, pageProject, pagePersonalizationHeaders, signal);
|
|
3412
3673
|
});
|
|
3413
3674
|
}
|
|
3414
3675
|
function readLaneClaims() {
|
|
@@ -3441,15 +3702,33 @@ function persistLaneClaims(claims, logger) {
|
|
|
3441
3702
|
}
|
|
3442
3703
|
function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore) {
|
|
3443
3704
|
let isFirstApply = true;
|
|
3444
|
-
|
|
3705
|
+
let applicationId = 0;
|
|
3706
|
+
return async (pageUrl, pageProject, personalizationHeaders, signal, deadline) => {
|
|
3707
|
+
var _a;
|
|
3708
|
+
const currentApplication = ++applicationId;
|
|
3709
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted)
|
|
3710
|
+
return;
|
|
3445
3711
|
const sessionId = isFirstApply ? Coframe.sessionId : generateUUID();
|
|
3446
3712
|
const sessionContext = buildSessionContext(isFirstApply);
|
|
3447
3713
|
isFirstApply = false;
|
|
3714
|
+
const preview = resolvePreview(pageUrl, previewVariantId, isSessionVariant);
|
|
3715
|
+
const chooseCmabVariants = createCmabVariantBatchChooser({
|
|
3716
|
+
projectId: Coframe.projectId,
|
|
3717
|
+
currentUrl: pageUrl,
|
|
3718
|
+
referrer: (_a = config.referrer) !== null && _a !== void 0 ? _a : getBrowserReferrer(),
|
|
3719
|
+
signal,
|
|
3720
|
+
deadline,
|
|
3721
|
+
disableRemoteAssignment: Boolean(config.projectUrl || config.ingestUrl),
|
|
3722
|
+
userContext: config.userContext,
|
|
3723
|
+
});
|
|
3724
|
+
const { variantsAndElements, experimentsInfo, hasActivatedFrames, laneClaims, } = await getVariantsAndElements(personalizationHeaders, pageProject, preview.previewVariantId, Coframe.userToken, pageUrl, getBrowserLanguages(), getBrowserPlatform(), Coframe.logger, config.nonce, laneStore.claims, preview.isSessionVariant, chooseCmabVariants);
|
|
3725
|
+
if ((signal === null || signal === void 0 ? void 0 : signal.aborted) ||
|
|
3726
|
+
currentApplication !== applicationId ||
|
|
3727
|
+
(signal && !urlsMatch(pageUrl, getCurrentPageUrl())))
|
|
3728
|
+
return;
|
|
3448
3729
|
Coframe.sessionId = sessionId;
|
|
3449
3730
|
Coframe.personalizationHeaders = personalizationHeaders;
|
|
3450
|
-
const preview = resolvePreview(pageUrl, previewVariantId, isSessionVariant);
|
|
3451
3731
|
setIsPreviewVariant(!!preview.previewVariantId);
|
|
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);
|
|
3453
3732
|
laneStore.claims = laneClaims;
|
|
3454
3733
|
persistLaneClaims(laneClaims, Coframe.logger);
|
|
3455
3734
|
Coframe.experimentInfo = experimentsInfo;
|
|
@@ -3466,7 +3745,11 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3466
3745
|
else {
|
|
3467
3746
|
replaceVariants(variantsAndElements, Coframe.orgId, Coframe.logger, config.nonce);
|
|
3468
3747
|
}
|
|
3469
|
-
|
|
3748
|
+
const cmabDecisions = chooseCmabVariants.getDecisions(experimentsInfo);
|
|
3749
|
+
startSession(Coframe, isCookiesEnabled, executionStartTime, personalizationHeaders, {
|
|
3750
|
+
...sessionContext,
|
|
3751
|
+
...(cmabDecisions.length ? { cmab_decisions: cmabDecisions } : {}),
|
|
3752
|
+
}, pageUrl);
|
|
3470
3753
|
analytics.resetBroadcasted();
|
|
3471
3754
|
analytics.sendAnalytics();
|
|
3472
3755
|
};
|
|
@@ -3484,7 +3767,7 @@ async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
|
3484
3767
|
return;
|
|
3485
3768
|
const laneStore = { claims: readLaneClaims() };
|
|
3486
3769
|
const applyVariants = createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore);
|
|
3487
|
-
await applyVariants(getCurrentPageUrl(), cdnProject, personalizationHeaders);
|
|
3770
|
+
await applyVariants(getCurrentPageUrl(), cdnProject, personalizationHeaders, undefined, (integrationStartTime || executionStartTime).getTime() + timeoutMs);
|
|
3488
3771
|
if (!Coframe.variantsAndElements ||
|
|
3489
3772
|
Coframe.variantsAndElements.length === 0) {
|
|
3490
3773
|
revealPage();
|
package/dist/pkg/cf.esm.mjs
CHANGED
|
@@ -1077,7 +1077,7 @@ function warnEventsNotSent() {
|
|
|
1077
1077
|
}
|
|
1078
1078
|
}
|
|
1079
1079
|
|
|
1080
|
-
const SDK_VERSION = '2.
|
|
1080
|
+
const SDK_VERSION = '2.8.0';
|
|
1081
1081
|
const queuedEvents = [];
|
|
1082
1082
|
const queuedMonitoringEvents = [];
|
|
1083
1083
|
const queuedSessions = [];
|
|
@@ -2565,7 +2565,7 @@ function checkAllAudiences(metadata, ctx) {
|
|
|
2565
2565
|
}
|
|
2566
2566
|
return result;
|
|
2567
2567
|
}
|
|
2568
|
-
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false) {
|
|
2568
|
+
async function getVariantsAndElements(personalizationHeaders, project, previewVariantId, userToken, currentPageUrl, languages, browserPlatform, logger, nonce, laneClaims = {}, silentFallback = false, chooseCmabVariants) {
|
|
2569
2569
|
var _a;
|
|
2570
2570
|
const experimentsInfo = [];
|
|
2571
2571
|
const cloakBot = !!((_a = project.settings) === null || _a === void 0 ? void 0 : _a.bot_cloaking_enabled) &&
|
|
@@ -2593,7 +2593,7 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2593
2593
|
const validatedClaims = validateClaims(laneClaims, project.lanes);
|
|
2594
2594
|
const previewVariantIds = splitPreviewVariantIds(previewVariantId);
|
|
2595
2595
|
if (previewVariantIds.length > 0) {
|
|
2596
|
-
const variantsAndElements = previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback);
|
|
2596
|
+
const variantsAndElements = await previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback);
|
|
2597
2597
|
if (variantsAndElements) {
|
|
2598
2598
|
return buildResponse({
|
|
2599
2599
|
variantsAndElements,
|
|
@@ -2633,7 +2633,7 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2633
2633
|
final_state: false,
|
|
2634
2634
|
level: GLOBAL_HOLDOUT,
|
|
2635
2635
|
});
|
|
2636
|
-
const assignment = assignFrames(activeFrames, validatedClaims, ctx);
|
|
2636
|
+
const assignment = await assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants);
|
|
2637
2637
|
return buildResponse({
|
|
2638
2638
|
...assignment,
|
|
2639
2639
|
experimentsInfo: [...experimentsInfo, ...assignment.experimentsInfo],
|
|
@@ -2649,7 +2649,8 @@ function activateFrames(ctx) {
|
|
|
2649
2649
|
return true;
|
|
2650
2650
|
});
|
|
2651
2651
|
}
|
|
2652
|
-
function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
2652
|
+
async function assignFrames(activeFrames, validatedClaims, ctx, chooseCmabVariants) {
|
|
2653
|
+
var _a;
|
|
2653
2654
|
const { project, userToken, logger } = ctx;
|
|
2654
2655
|
const experimentsInfo = [];
|
|
2655
2656
|
const variantsAndElements = [];
|
|
@@ -2675,6 +2676,7 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2675
2676
|
}
|
|
2676
2677
|
const excludedExperimentIds = resolveLaneWinners(validatedClaims, reachableByLane, project.lanes, userToken);
|
|
2677
2678
|
const updatedClaims = { ...validatedClaims };
|
|
2679
|
+
const assignmentEntries = [];
|
|
2678
2680
|
let hasActivatedFrames = false;
|
|
2679
2681
|
for (const frame of activeFrames) {
|
|
2680
2682
|
let atLeastOneExperimentWasActivated = false;
|
|
@@ -2700,51 +2702,88 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2700
2702
|
const experimentHoldoutInfo = metadata.holdout_info;
|
|
2701
2703
|
const experimentUniverseVid = chooseVariant(userToken, experimentHoldoutInfo.variant_bins, experimentHoldoutInfo.seed, experimentHoldoutInfo.holdout_variant_id);
|
|
2702
2704
|
if (experimentUniverseVid === experimentHoldoutInfo.holdout_variant_id) {
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2705
|
+
assignmentEntries.push({
|
|
2706
|
+
kind: 'info',
|
|
2707
|
+
info: {
|
|
2708
|
+
variant_id: experimentHoldoutInfo.holdout_variant_id,
|
|
2709
|
+
variant_slug: experimentHoldoutInfo.holdout_variant_slug,
|
|
2710
|
+
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2711
|
+
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2712
|
+
iteration_id: '',
|
|
2713
|
+
level: EXPERIMENT_HOLDOUT,
|
|
2714
|
+
final_state: true,
|
|
2715
|
+
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2716
|
+
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2717
|
+
},
|
|
2718
|
+
});
|
|
2719
|
+
continue;
|
|
2720
|
+
}
|
|
2721
|
+
assignmentEntries.push({
|
|
2722
|
+
kind: 'info',
|
|
2723
|
+
info: {
|
|
2724
|
+
variant_id: experimentHoldoutInfo.treatment_variant_id,
|
|
2725
|
+
variant_slug: experimentHoldoutInfo.treatment_variant_slug,
|
|
2706
2726
|
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2707
2727
|
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2708
2728
|
iteration_id: '',
|
|
2709
2729
|
level: EXPERIMENT_HOLDOUT,
|
|
2710
|
-
final_state:
|
|
2730
|
+
final_state: false,
|
|
2711
2731
|
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2712
2732
|
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2713
|
-
}
|
|
2714
|
-
continue;
|
|
2715
|
-
}
|
|
2716
|
-
experimentsInfo.push({
|
|
2717
|
-
variant_id: experimentHoldoutInfo.treatment_variant_id,
|
|
2718
|
-
variant_slug: experimentHoldoutInfo.treatment_variant_slug,
|
|
2719
|
-
experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2720
|
-
experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2721
|
-
iteration_id: '',
|
|
2722
|
-
level: EXPERIMENT_HOLDOUT,
|
|
2723
|
-
final_state: false,
|
|
2724
|
-
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2725
|
-
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2733
|
+
},
|
|
2726
2734
|
});
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2735
|
+
assignmentEntries.push({ kind: 'variant', metadata, variants, elements });
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
const cmabMetadataById = new Map();
|
|
2739
|
+
for (const entry of assignmentEntries) {
|
|
2740
|
+
if (entry.kind === 'variant' && entry.metadata.type === 'CMAB') {
|
|
2741
|
+
cmabMetadataById.set(entry.metadata.experiment_id, entry.metadata);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
let mlVariantIds = new Map();
|
|
2745
|
+
if (chooseCmabVariants && cmabMetadataById.size > 0) {
|
|
2746
|
+
try {
|
|
2747
|
+
mlVariantIds = await chooseCmabVariants(Array.from(cmabMetadataById.values()), userToken);
|
|
2748
|
+
}
|
|
2749
|
+
catch (_b) {
|
|
2750
|
+
mlVariantIds = new Map();
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
for (const entry of assignmentEntries) {
|
|
2754
|
+
if (entry.kind === 'info') {
|
|
2755
|
+
experimentsInfo.push(entry.info);
|
|
2756
|
+
continue;
|
|
2757
|
+
}
|
|
2758
|
+
const { metadata, variants, elements } = entry;
|
|
2759
|
+
let mlVariantId = metadata.type === 'CMAB'
|
|
2760
|
+
? ((_a = mlVariantIds.get(metadata.experiment_id)) !== null && _a !== void 0 ? _a : null)
|
|
2761
|
+
: null;
|
|
2762
|
+
if (mlVariantId && !variants.some((v) => v.id === mlVariantId)) {
|
|
2763
|
+
logger.warn(`CMAB variant ${mlVariantId} not in experiment ${metadata.experiment_id}`, 'cmab_variant_not_found', { experiment_id: metadata.experiment_id });
|
|
2764
|
+
mlVariantId = null;
|
|
2765
|
+
}
|
|
2766
|
+
const variantId = mlVariantId ||
|
|
2767
|
+
chooseVariant(userToken, metadata.allocation, metadata.seed, '');
|
|
2768
|
+
const variant = variants.find((v) => v.id === variantId);
|
|
2769
|
+
if (!variant) {
|
|
2770
|
+
logger.error(`Variant not found for experiment ${metadata.experiment_id}`, 'variant_not_found', {
|
|
2738
2771
|
experiment_id: metadata.experiment_id,
|
|
2739
|
-
experiment_slug: metadata.slug,
|
|
2740
|
-
iteration_id: metadata.iteration_id,
|
|
2741
|
-
level: EXPERIMENT,
|
|
2742
|
-
final_state: true,
|
|
2743
|
-
holdout_experiment_id: experimentHoldoutInfo.holdout_experiment_id,
|
|
2744
|
-
holdout_experiment_slug: experimentHoldoutInfo.holdout_experiment_slug,
|
|
2745
2772
|
});
|
|
2746
|
-
|
|
2773
|
+
continue;
|
|
2747
2774
|
}
|
|
2775
|
+
experimentsInfo.push({
|
|
2776
|
+
variant_id: variant.id,
|
|
2777
|
+
variant_slug: variant.slug,
|
|
2778
|
+
experiment_id: metadata.experiment_id,
|
|
2779
|
+
experiment_slug: metadata.slug,
|
|
2780
|
+
iteration_id: metadata.iteration_id,
|
|
2781
|
+
level: EXPERIMENT,
|
|
2782
|
+
final_state: true,
|
|
2783
|
+
holdout_experiment_id: metadata.holdout_info.holdout_experiment_id,
|
|
2784
|
+
holdout_experiment_slug: metadata.holdout_info.holdout_experiment_slug,
|
|
2785
|
+
});
|
|
2786
|
+
variantsAndElements.push({ variant, elements });
|
|
2748
2787
|
}
|
|
2749
2788
|
return {
|
|
2750
2789
|
variantsAndElements,
|
|
@@ -2753,7 +2792,7 @@ function assignFrames(activeFrames, validatedClaims, ctx) {
|
|
|
2753
2792
|
laneClaims: updatedClaims,
|
|
2754
2793
|
};
|
|
2755
2794
|
}
|
|
2756
|
-
function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback) {
|
|
2795
|
+
async function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback) {
|
|
2757
2796
|
const previewRollouts = previewVariantIds.includes(ROLLOUTS_PREVIEW_ID);
|
|
2758
2797
|
const requestedVariantIds = previewVariantIds.filter((id) => !isPreviewSentinel(id));
|
|
2759
2798
|
const requestedVariants = findVariants(ctx.project.frames, requestedVariantIds);
|
|
@@ -2766,7 +2805,7 @@ function previewVariants(previewVariantIds, validatedClaims, ctx, silentFallback
|
|
|
2766
2805
|
window.alert(`[Coframe] Variant preview unavailable: ${previewVariantIds.join(',')}.`);
|
|
2767
2806
|
}
|
|
2768
2807
|
const rolledOutVariants = previewRollouts && !previewVariantIds.includes(GLOBAL_HOLDOUT)
|
|
2769
|
-
? assignFrames(keepRolledOutExperiments(activateFrames(ctx)), validatedClaims, ctx).variantsAndElements
|
|
2808
|
+
? (await assignFrames(keepRolledOutExperiments(activateFrames(ctx)), validatedClaims, ctx)).variantsAndElements
|
|
2770
2809
|
: [];
|
|
2771
2810
|
return uniqueByVariantId([...rolledOutVariants, ...requestedVariants]);
|
|
2772
2811
|
}
|
|
@@ -2844,6 +2883,226 @@ function getNavigationType(isInitialApply) {
|
|
|
2844
2883
|
return undefined;
|
|
2845
2884
|
}
|
|
2846
2885
|
|
|
2886
|
+
const CMAB_ASSIGNMENT_TIMEOUT_MS = 500;
|
|
2887
|
+
const CMAB_ASSIGNMENT_MAX_EXPERIMENTS = 64;
|
|
2888
|
+
const ASSIGNMENT_REQUEST_SCHEMA = 'coframe.edge.assign-request/2';
|
|
2889
|
+
const ASSIGNMENT_RESPONSE_SCHEMA = 'coframe.edge.assign-response/2';
|
|
2890
|
+
async function requestAssignmentChunk(options, metadata, userToken) {
|
|
2891
|
+
var _a, _b, _c, _d, _e;
|
|
2892
|
+
const assignments = new Map();
|
|
2893
|
+
const decisions = new Map();
|
|
2894
|
+
let fallbackReason = 'request_failed';
|
|
2895
|
+
let timeoutId;
|
|
2896
|
+
let cancel;
|
|
2897
|
+
try {
|
|
2898
|
+
if (options.disableRemoteAssignment) {
|
|
2899
|
+
fallbackReason = 'network_policy';
|
|
2900
|
+
return { assignments, decisions };
|
|
2901
|
+
}
|
|
2902
|
+
const controller = new AbortController();
|
|
2903
|
+
const budget = Date.now() + CMAB_ASSIGNMENT_TIMEOUT_MS;
|
|
2904
|
+
const deadline = options.deadline !== undefined && options.deadline < budget
|
|
2905
|
+
? options.deadline
|
|
2906
|
+
: budget;
|
|
2907
|
+
const remaining = deadline - Date.now();
|
|
2908
|
+
if (((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) || remaining <= 0) {
|
|
2909
|
+
fallbackReason = ((_b = options.signal) === null || _b === void 0 ? void 0 : _b.aborted)
|
|
2910
|
+
? 'cancelled'
|
|
2911
|
+
: 'deadline_exceeded';
|
|
2912
|
+
return { assignments, decisions };
|
|
2913
|
+
}
|
|
2914
|
+
const cancelled = new Promise((resolve) => {
|
|
2915
|
+
var _a;
|
|
2916
|
+
cancel = () => {
|
|
2917
|
+
var _a;
|
|
2918
|
+
fallbackReason = ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
|
|
2919
|
+
? 'cancelled'
|
|
2920
|
+
: 'deadline_exceeded';
|
|
2921
|
+
resolve(null);
|
|
2922
|
+
controller.abort();
|
|
2923
|
+
};
|
|
2924
|
+
timeoutId = setTimeout(cancel, remaining);
|
|
2925
|
+
(_a = options.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', cancel);
|
|
2926
|
+
});
|
|
2927
|
+
const userContext = sanitizeUserContext(options.userContext);
|
|
2928
|
+
const pending = fetch(`${"https://edge.cofra.me"}/api/v2/assign`, {
|
|
2929
|
+
method: 'POST',
|
|
2930
|
+
credentials: 'omit',
|
|
2931
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2932
|
+
signal: controller.signal,
|
|
2933
|
+
body: JSON.stringify({
|
|
2934
|
+
schema: ASSIGNMENT_REQUEST_SCHEMA,
|
|
2935
|
+
project_id: options.projectId,
|
|
2936
|
+
user_token: userToken,
|
|
2937
|
+
current_url: options.currentUrl,
|
|
2938
|
+
referrer: options.referrer,
|
|
2939
|
+
experiment_ids: metadata.map((item) => item.experiment_id),
|
|
2940
|
+
...(userContext ? { user_context: userContext } : {}),
|
|
2941
|
+
}),
|
|
2942
|
+
}).then((response) => {
|
|
2943
|
+
if (!response.ok)
|
|
2944
|
+
return null;
|
|
2945
|
+
fallbackReason = 'invalid_response';
|
|
2946
|
+
return response.json();
|
|
2947
|
+
});
|
|
2948
|
+
const payload = await Promise.race([pending, cancelled]);
|
|
2949
|
+
if (controller.signal.aborted ||
|
|
2950
|
+
((_c = options.signal) === null || _c === void 0 ? void 0 : _c.aborted) ||
|
|
2951
|
+
Date.now() >= deadline) {
|
|
2952
|
+
fallbackReason = ((_d = options.signal) === null || _d === void 0 ? void 0 : _d.aborted)
|
|
2953
|
+
? 'cancelled'
|
|
2954
|
+
: 'deadline_exceeded';
|
|
2955
|
+
return { assignments, decisions };
|
|
2956
|
+
}
|
|
2957
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.schema) !== ASSIGNMENT_RESPONSE_SCHEMA ||
|
|
2958
|
+
!Array.isArray(payload.assignments)) {
|
|
2959
|
+
return { assignments, decisions };
|
|
2960
|
+
}
|
|
2961
|
+
const requested = new Map(metadata.map((item) => [item.experiment_id, item]));
|
|
2962
|
+
const seen = new Set();
|
|
2963
|
+
for (const row of payload.assignments) {
|
|
2964
|
+
if (!row || typeof row.experiment_id !== 'string')
|
|
2965
|
+
continue;
|
|
2966
|
+
const id = row.experiment_id;
|
|
2967
|
+
const expected = requested.get(id);
|
|
2968
|
+
if (!expected)
|
|
2969
|
+
continue;
|
|
2970
|
+
if (seen.has(id)) {
|
|
2971
|
+
assignments.delete(id);
|
|
2972
|
+
decisions.delete(id);
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
seen.add(id);
|
|
2976
|
+
if (typeof row.iteration_id !== 'string' ||
|
|
2977
|
+
row.iteration_id !== expected.iteration_id ||
|
|
2978
|
+
(row.variant_id !== null && typeof row.variant_id !== 'string'))
|
|
2979
|
+
continue;
|
|
2980
|
+
assignments.set(id, row.variant_id);
|
|
2981
|
+
const decision = {
|
|
2982
|
+
experiment_id: id,
|
|
2983
|
+
iteration_id: row.iteration_id,
|
|
2984
|
+
variant_id: row.variant_id,
|
|
2985
|
+
};
|
|
2986
|
+
for (const key of [
|
|
2987
|
+
'decision_source',
|
|
2988
|
+
'fallback_reason',
|
|
2989
|
+
'model_id',
|
|
2990
|
+
'feature_spec_version',
|
|
2991
|
+
'policy_id',
|
|
2992
|
+
'policy_version',
|
|
2993
|
+
'policy_route',
|
|
2994
|
+
'active_model_failure',
|
|
2995
|
+
'model_load_state',
|
|
2996
|
+
'scored_by',
|
|
2997
|
+
]) {
|
|
2998
|
+
if (typeof row[key] === 'string' && row[key].length <= 256)
|
|
2999
|
+
decision[key] = row[key];
|
|
3000
|
+
}
|
|
3001
|
+
if (typeof payload.assignment_request_id === 'string' &&
|
|
3002
|
+
payload.assignment_request_id.length <= 256) {
|
|
3003
|
+
decision.assignment_request_id = payload.assignment_request_id;
|
|
3004
|
+
}
|
|
3005
|
+
if (typeof row.assignment_probability === 'number' &&
|
|
3006
|
+
row.assignment_probability >= 0 &&
|
|
3007
|
+
row.assignment_probability <= 1) {
|
|
3008
|
+
decision.assignment_probability = row.assignment_probability;
|
|
3009
|
+
}
|
|
3010
|
+
if (typeof row.decision_latency_ms === 'number' &&
|
|
3011
|
+
row.decision_latency_ms >= 0 &&
|
|
3012
|
+
row.decision_latency_ms < Infinity) {
|
|
3013
|
+
decision.decision_latency_ms = row.decision_latency_ms;
|
|
3014
|
+
}
|
|
3015
|
+
if (Array.isArray(row.eligible_variant_ids) &&
|
|
3016
|
+
row.eligible_variant_ids.length <= 256 &&
|
|
3017
|
+
row.eligible_variant_ids.every((id) => typeof id === 'string' && id.length <= 256)) {
|
|
3018
|
+
decision.eligible_variant_ids = row.eligible_variant_ids;
|
|
3019
|
+
}
|
|
3020
|
+
if (row.action_probabilities &&
|
|
3021
|
+
typeof row.action_probabilities === 'object' &&
|
|
3022
|
+
!Array.isArray(row.action_probabilities)) {
|
|
3023
|
+
const keys = Object.keys(row.action_probabilities);
|
|
3024
|
+
if (keys.length <= 256 &&
|
|
3025
|
+
keys.every((key) => key.length <= 256 &&
|
|
3026
|
+
typeof row.action_probabilities[key] === 'number' &&
|
|
3027
|
+
row.action_probabilities[key] >= 0 &&
|
|
3028
|
+
row.action_probabilities[key] <= 1)) {
|
|
3029
|
+
decision.action_probabilities = row.action_probabilities;
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
decisions.set(id, decision);
|
|
3033
|
+
}
|
|
3034
|
+
return { assignments, decisions };
|
|
3035
|
+
}
|
|
3036
|
+
catch (_f) {
|
|
3037
|
+
return { assignments, decisions };
|
|
3038
|
+
}
|
|
3039
|
+
finally {
|
|
3040
|
+
if (timeoutId !== undefined)
|
|
3041
|
+
clearTimeout(timeoutId);
|
|
3042
|
+
if (cancel)
|
|
3043
|
+
(_e = options.signal) === null || _e === void 0 ? void 0 : _e.removeEventListener('abort', cancel);
|
|
3044
|
+
for (const item of metadata) {
|
|
3045
|
+
if (!decisions.has(item.experiment_id))
|
|
3046
|
+
decisions.set(item.experiment_id, {
|
|
3047
|
+
experiment_id: item.experiment_id,
|
|
3048
|
+
iteration_id: item.iteration_id,
|
|
3049
|
+
variant_id: null,
|
|
3050
|
+
decision_source: 'client_fallback',
|
|
3051
|
+
fallback_reason: fallbackReason,
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
function createCmabVariantBatchChooser(options) {
|
|
3057
|
+
const decisions = new Map();
|
|
3058
|
+
const choose = async (metadata, userToken) => {
|
|
3059
|
+
decisions.clear();
|
|
3060
|
+
const uniqueMetadata = Array.from(new Map(metadata.map((item) => [item.experiment_id, item])).values());
|
|
3061
|
+
const chunks = [];
|
|
3062
|
+
for (let index = 0; index < uniqueMetadata.length; index += CMAB_ASSIGNMENT_MAX_EXPERIMENTS) {
|
|
3063
|
+
chunks.push(uniqueMetadata.slice(index, index + CMAB_ASSIGNMENT_MAX_EXPERIMENTS));
|
|
3064
|
+
}
|
|
3065
|
+
const results = await Promise.all(chunks.map((chunk) => requestAssignmentChunk(options, chunk, userToken)));
|
|
3066
|
+
const assignments = new Map();
|
|
3067
|
+
for (const result of results) {
|
|
3068
|
+
result.assignments.forEach((value, id) => {
|
|
3069
|
+
assignments.set(id, value);
|
|
3070
|
+
});
|
|
3071
|
+
result.decisions.forEach((value, id) => {
|
|
3072
|
+
decisions.set(id, value);
|
|
3073
|
+
});
|
|
3074
|
+
}
|
|
3075
|
+
return assignments;
|
|
3076
|
+
};
|
|
3077
|
+
choose.getDecisions = (experiments) => {
|
|
3078
|
+
const selected = [];
|
|
3079
|
+
for (const info of experiments) {
|
|
3080
|
+
const decision = decisions.get(info.experiment_id);
|
|
3081
|
+
if (!decision ||
|
|
3082
|
+
decision.iteration_id !== info.iteration_id ||
|
|
3083
|
+
!info.final_state)
|
|
3084
|
+
continue;
|
|
3085
|
+
if (decision.variant_id === info.variant_id)
|
|
3086
|
+
selected.push(decision);
|
|
3087
|
+
else
|
|
3088
|
+
selected.push({
|
|
3089
|
+
experiment_id: info.experiment_id,
|
|
3090
|
+
iteration_id: info.iteration_id,
|
|
3091
|
+
variant_id: info.variant_id,
|
|
3092
|
+
decision_source: 'client_fallback',
|
|
3093
|
+
fallback_reason: decision.variant_id === null
|
|
3094
|
+
? decision.fallback_reason || 'missing_choice'
|
|
3095
|
+
: 'unsupported_variant',
|
|
3096
|
+
...(decision.assignment_request_id
|
|
3097
|
+
? { assignment_request_id: decision.assignment_request_id }
|
|
3098
|
+
: {}),
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
return selected;
|
|
3102
|
+
};
|
|
3103
|
+
return choose;
|
|
3104
|
+
}
|
|
3105
|
+
|
|
2847
3106
|
const CACHE_TTL_MS = 60 * 1000;
|
|
2848
3107
|
const cache = new Map();
|
|
2849
3108
|
function getCachedFrames(url) {
|
|
@@ -2970,7 +3229,7 @@ function observeUrlChanges(baseUrlMatch, onUrlChange) {
|
|
|
2970
3229
|
patchHistoryMethod('replaceState');
|
|
2971
3230
|
}
|
|
2972
3231
|
|
|
2973
|
-
function startSession(Coframe, cookiesEnabled, executionStartTime, personalizationHeaders, sessionContext) {
|
|
3232
|
+
function startSession(Coframe, cookiesEnabled, executionStartTime, personalizationHeaders, sessionContext, pageUrl = getCurrentPageUrl()) {
|
|
2974
3233
|
const sessionPayload = [
|
|
2975
3234
|
Coframe,
|
|
2976
3235
|
Coframe.orgId,
|
|
@@ -2983,7 +3242,7 @@ function startSession(Coframe, cookiesEnabled, executionStartTime, personalizati
|
|
|
2983
3242
|
getBrowserPlatform(),
|
|
2984
3243
|
getBrowserLanguages(),
|
|
2985
3244
|
getBrowserUserAgent(),
|
|
2986
|
-
|
|
3245
|
+
pageUrl,
|
|
2987
3246
|
getBrowserFignerprintInfo(),
|
|
2988
3247
|
];
|
|
2989
3248
|
{
|
|
@@ -3373,12 +3632,14 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3373
3632
|
let navigationAbortController = null;
|
|
3374
3633
|
observeUrlChanges(false, async (newPageUrl, _oldPageUrl) => {
|
|
3375
3634
|
var _a;
|
|
3376
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
3377
|
-
infiniteLoopProtection = 0;
|
|
3378
3635
|
if (navigationAbortController)
|
|
3379
3636
|
navigationAbortController.abort();
|
|
3380
3637
|
navigationAbortController = new AbortController();
|
|
3381
3638
|
const { signal } = navigationAbortController;
|
|
3639
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
3640
|
+
if (signal.aborted)
|
|
3641
|
+
return;
|
|
3642
|
+
infiniteLoopProtection = 0;
|
|
3382
3643
|
let pageProject = project;
|
|
3383
3644
|
let pagePersonalizationHeaders = Coframe.personalizationHeaders;
|
|
3384
3645
|
const cached = getCachedFrames(newPageUrl);
|
|
@@ -3406,7 +3667,7 @@ function setupSPANavigation(Coframe, project, config, previewVariantId, isSessio
|
|
|
3406
3667
|
}
|
|
3407
3668
|
if (signal.aborted)
|
|
3408
3669
|
return;
|
|
3409
|
-
await applyVariants(newPageUrl, pageProject, pagePersonalizationHeaders);
|
|
3670
|
+
await applyVariants(newPageUrl, pageProject, pagePersonalizationHeaders, signal);
|
|
3410
3671
|
});
|
|
3411
3672
|
}
|
|
3412
3673
|
function readLaneClaims() {
|
|
@@ -3439,15 +3700,33 @@ function persistLaneClaims(claims, logger) {
|
|
|
3439
3700
|
}
|
|
3440
3701
|
function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore) {
|
|
3441
3702
|
let isFirstApply = true;
|
|
3442
|
-
|
|
3703
|
+
let applicationId = 0;
|
|
3704
|
+
return async (pageUrl, pageProject, personalizationHeaders, signal, deadline) => {
|
|
3705
|
+
var _a;
|
|
3706
|
+
const currentApplication = ++applicationId;
|
|
3707
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted)
|
|
3708
|
+
return;
|
|
3443
3709
|
const sessionId = isFirstApply ? Coframe.sessionId : generateUUID();
|
|
3444
3710
|
const sessionContext = buildSessionContext(isFirstApply);
|
|
3445
3711
|
isFirstApply = false;
|
|
3712
|
+
const preview = resolvePreview(pageUrl, previewVariantId, isSessionVariant);
|
|
3713
|
+
const chooseCmabVariants = createCmabVariantBatchChooser({
|
|
3714
|
+
projectId: Coframe.projectId,
|
|
3715
|
+
currentUrl: pageUrl,
|
|
3716
|
+
referrer: (_a = config.referrer) !== null && _a !== void 0 ? _a : getBrowserReferrer(),
|
|
3717
|
+
signal,
|
|
3718
|
+
deadline,
|
|
3719
|
+
disableRemoteAssignment: Boolean(config.projectUrl || config.ingestUrl),
|
|
3720
|
+
userContext: config.userContext,
|
|
3721
|
+
});
|
|
3722
|
+
const { variantsAndElements, experimentsInfo, hasActivatedFrames, laneClaims, } = await getVariantsAndElements(personalizationHeaders, pageProject, preview.previewVariantId, Coframe.userToken, pageUrl, getBrowserLanguages(), getBrowserPlatform(), Coframe.logger, config.nonce, laneStore.claims, preview.isSessionVariant, chooseCmabVariants);
|
|
3723
|
+
if ((signal === null || signal === void 0 ? void 0 : signal.aborted) ||
|
|
3724
|
+
currentApplication !== applicationId ||
|
|
3725
|
+
(signal && !urlsMatch(pageUrl, getCurrentPageUrl())))
|
|
3726
|
+
return;
|
|
3446
3727
|
Coframe.sessionId = sessionId;
|
|
3447
3728
|
Coframe.personalizationHeaders = personalizationHeaders;
|
|
3448
|
-
const preview = resolvePreview(pageUrl, previewVariantId, isSessionVariant);
|
|
3449
3729
|
setIsPreviewVariant(!!preview.previewVariantId);
|
|
3450
|
-
const { variantsAndElements, experimentsInfo, hasActivatedFrames, laneClaims, } = await getVariantsAndElements(personalizationHeaders, pageProject, preview.previewVariantId, Coframe.userToken, pageUrl, getBrowserLanguages(), getBrowserPlatform(), Coframe.logger, config.nonce, laneStore.claims, preview.isSessionVariant);
|
|
3451
3730
|
laneStore.claims = laneClaims;
|
|
3452
3731
|
persistLaneClaims(laneClaims, Coframe.logger);
|
|
3453
3732
|
Coframe.experimentInfo = experimentsInfo;
|
|
@@ -3464,7 +3743,11 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3464
3743
|
else {
|
|
3465
3744
|
replaceVariants(variantsAndElements, Coframe.orgId, Coframe.logger, config.nonce);
|
|
3466
3745
|
}
|
|
3467
|
-
|
|
3746
|
+
const cmabDecisions = chooseCmabVariants.getDecisions(experimentsInfo);
|
|
3747
|
+
startSession(Coframe, isCookiesEnabled, executionStartTime, personalizationHeaders, {
|
|
3748
|
+
...sessionContext,
|
|
3749
|
+
...(cmabDecisions.length ? { cmab_decisions: cmabDecisions } : {}),
|
|
3750
|
+
}, pageUrl);
|
|
3468
3751
|
analytics.resetBroadcasted();
|
|
3469
3752
|
analytics.sendAnalytics();
|
|
3470
3753
|
};
|
|
@@ -3482,7 +3765,7 @@ async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
|
3482
3765
|
return;
|
|
3483
3766
|
const laneStore = { claims: readLaneClaims() };
|
|
3484
3767
|
const applyVariants = createApplyVariants(Coframe, config, previewVariantId, isSessionVariant, executionStartTime, analytics, laneStore);
|
|
3485
|
-
await applyVariants(getCurrentPageUrl(), cdnProject, personalizationHeaders);
|
|
3768
|
+
await applyVariants(getCurrentPageUrl(), cdnProject, personalizationHeaders, undefined, (integrationStartTime || executionStartTime).getTime() + timeoutMs);
|
|
3486
3769
|
if (!Coframe.variantsAndElements ||
|
|
3487
3770
|
Coframe.variantsAndElements.length === 0) {
|
|
3488
3771
|
revealPage();
|
|
@@ -163,9 +163,11 @@ interface Experiment {
|
|
|
163
163
|
}
|
|
164
164
|
interface ExperimentMetadata {
|
|
165
165
|
experiment_id: string;
|
|
166
|
+
source_experiment_id?: string;
|
|
166
167
|
slug: string;
|
|
167
168
|
iteration_id: string;
|
|
168
169
|
seed: number;
|
|
170
|
+
type?: 'CMAB';
|
|
169
171
|
holdout_info: HoldoutInfo;
|
|
170
172
|
allocation: Allocation;
|
|
171
173
|
activation_condition: {
|
|
@@ -166,9 +166,11 @@ interface Experiment {
|
|
|
166
166
|
}
|
|
167
167
|
interface ExperimentMetadata {
|
|
168
168
|
experiment_id: string;
|
|
169
|
+
source_experiment_id?: string;
|
|
169
170
|
slug: string;
|
|
170
171
|
iteration_id: string;
|
|
171
172
|
seed: number;
|
|
173
|
+
type?: 'CMAB';
|
|
172
174
|
holdout_info: HoldoutInfo;
|
|
173
175
|
allocation: Allocation;
|
|
174
176
|
activation_condition: {
|
|
@@ -129,9 +129,11 @@ interface Experiment {
|
|
|
129
129
|
}
|
|
130
130
|
interface ExperimentMetadata {
|
|
131
131
|
experiment_id: string;
|
|
132
|
+
source_experiment_id?: string;
|
|
132
133
|
slug: string;
|
|
133
134
|
iteration_id: string;
|
|
134
135
|
seed: number;
|
|
136
|
+
type?: 'CMAB';
|
|
135
137
|
holdout_info: HoldoutInfo;
|
|
136
138
|
allocation: Allocation;
|
|
137
139
|
activation_condition: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coframe/sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
4
4
|
"description": "Coframe optimizes your website using generative AI to deliver maximum impact to your audience and allow your site to improve itself as you sleep. Learn more at coframe.com.",
|
|
5
5
|
"main": "dist/pkg/cf.cjs.js",
|
|
6
6
|
"module": "dist/pkg/cf.esm.mjs",
|
|
@@ -131,4 +131,4 @@
|
|
|
131
131
|
"ua-parser-js": "^2.0.10"
|
|
132
132
|
},
|
|
133
133
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
134
|
-
}
|
|
134
|
+
}
|