@coframe/sdk 1.1.10 → 1.1.12
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 +5 -1
- package/dist/pkg/cf.cjs.js +202 -272
- package/dist/pkg/cf.esm.mjs +202 -272
- package/dist/pkg/types/index.d.ts +1 -0
- package/dist/pkg/types/react.d.ts +1 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -47,9 +47,13 @@ installations:
|
|
|
47
47
|
|
|
48
48
|
- `cf_disable` skips initialization and reveals any antiflicker state.
|
|
49
49
|
- `coframe_variant_id` previews one or more comma-separated variant IDs.
|
|
50
|
-
- `keep_preview_for_session=true` keeps that preview active for the tab.
|
|
51
50
|
- `coframe_should_send_events=true` allows events during preview for testing.
|
|
52
51
|
|
|
52
|
+
Debug tools are allowed by default. Open the Launcher with `cf_debug=true` and
|
|
53
|
+
use **Keep across navigation** in the Preview Session tool to keep the selected
|
|
54
|
+
variants active for the current tab. Set `enableDebug: false` to prevent URL or
|
|
55
|
+
saved session state from loading the Launcher.
|
|
56
|
+
|
|
53
57
|
For SSR, pass the complete request URL, including its query string, as
|
|
54
58
|
`currentUrl`. This is required for query-sensitive targeting and allows
|
|
55
59
|
`getProjectData()` to short-circuit before any request when `cf_disable` is
|
package/dist/pkg/cf.cjs.js
CHANGED
|
@@ -859,6 +859,88 @@ const isSegmentEmpty = (segment) => {
|
|
|
859
859
|
return segment.xpath === '';
|
|
860
860
|
};
|
|
861
861
|
|
|
862
|
+
function parseQueryString(query) {
|
|
863
|
+
const params = {};
|
|
864
|
+
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
|
|
865
|
+
if (!cleanQuery)
|
|
866
|
+
return params;
|
|
867
|
+
for (const pair of cleanQuery.split('&')) {
|
|
868
|
+
if (!pair)
|
|
869
|
+
continue;
|
|
870
|
+
const [key, value = ''] = pair.split('=').map(decodeURIComponent);
|
|
871
|
+
if (!(key in params)) {
|
|
872
|
+
params[key] = value;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return params;
|
|
876
|
+
}
|
|
877
|
+
function createURLSearchParams(query) {
|
|
878
|
+
if (typeof URLSearchParams !== 'undefined') {
|
|
879
|
+
const nativeParams = new URLSearchParams(query);
|
|
880
|
+
return {
|
|
881
|
+
get: (key) => nativeParams.get(key),
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
const simpleParams = parseQueryString(query);
|
|
885
|
+
return {
|
|
886
|
+
get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function getUrlSearchParams(url) {
|
|
890
|
+
const searchIndex = url.indexOf('?');
|
|
891
|
+
const firstHashIndex = url.indexOf('#');
|
|
892
|
+
if (searchIndex === -1 ||
|
|
893
|
+
(firstHashIndex !== -1 && firstHashIndex < searchIndex)) {
|
|
894
|
+
return createURLSearchParams('');
|
|
895
|
+
}
|
|
896
|
+
const hashIndex = url.indexOf('#', searchIndex);
|
|
897
|
+
const search = hashIndex !== -1
|
|
898
|
+
? url.substring(searchIndex, hashIndex)
|
|
899
|
+
: url.substring(searchIndex);
|
|
900
|
+
return createURLSearchParams(search);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const COFRAME_DEBUG_QUERY_PARAM = 'cf_debug';
|
|
904
|
+
const COFRAME_TOOL_INTERACTION_ATTRIBUTE = 'data-cf-t';
|
|
905
|
+
const COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY = 'cf.tools.enabled';
|
|
906
|
+
const COFRAME_DEBUG_TOOLS_STATE_STORAGE_KEY = 'cf.tools.session';
|
|
907
|
+
const ENABLED_VALUE = 'true';
|
|
908
|
+
function enableDebugToolsSession() {
|
|
909
|
+
try {
|
|
910
|
+
window.sessionStorage.setItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY, ENABLED_VALUE);
|
|
911
|
+
}
|
|
912
|
+
catch (_a) {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function clearDebugToolsSession() {
|
|
916
|
+
try {
|
|
917
|
+
window.sessionStorage.removeItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY);
|
|
918
|
+
window.sessionStorage.removeItem(COFRAME_DEBUG_TOOLS_STATE_STORAGE_KEY);
|
|
919
|
+
}
|
|
920
|
+
catch (_a) {
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function isDebugToolsSessionEnabled(allowed) {
|
|
924
|
+
if (!allowed) {
|
|
925
|
+
clearDebugToolsSession();
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
const enabledByUrl = getUrlSearchParams(getCurrentPageUrl()).get(COFRAME_DEBUG_QUERY_PARAM) ===
|
|
929
|
+
ENABLED_VALUE;
|
|
930
|
+
if (enabledByUrl) {
|
|
931
|
+
enableDebugToolsSession();
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
try {
|
|
935
|
+
const enabled = window.sessionStorage.getItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY) ===
|
|
936
|
+
ENABLED_VALUE;
|
|
937
|
+
return enabled;
|
|
938
|
+
}
|
|
939
|
+
catch (_a) {
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
862
944
|
const USER_CONTEXT_KEY_PREFIX = 'x-ctx-';
|
|
863
945
|
const MAX_USER_CONTEXT_JSON_BYTES = 4096;
|
|
864
946
|
function userContextJsonByteLength(userContext) {
|
|
@@ -1037,6 +1119,12 @@ function queueVariantViewEvent(coframe, { projectId, sessionId, orgId, userToken
|
|
|
1037
1119
|
}
|
|
1038
1120
|
function sendPageEngagementEvent(coframe, eventType, event, { projectId, sessionId, duration = 0, userToken, personalizationHeaders, }) {
|
|
1039
1121
|
var _a;
|
|
1122
|
+
if (event
|
|
1123
|
+
.composedPath()
|
|
1124
|
+
.some((target) => target instanceof Element &&
|
|
1125
|
+
target.hasAttribute(COFRAME_TOOL_INTERACTION_ATTRIBUTE))) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1040
1128
|
const startTime = performance.now();
|
|
1041
1129
|
const x = event.clientX + window.scrollX;
|
|
1042
1130
|
const y = event.clientY + window.scrollY;
|
|
@@ -2216,44 +2304,8 @@ function debounce(func, wait) {
|
|
|
2216
2304
|
}, wait);
|
|
2217
2305
|
};
|
|
2218
2306
|
}
|
|
2219
|
-
function parseQueryString(query) {
|
|
2220
|
-
const params = {};
|
|
2221
|
-
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
|
|
2222
|
-
if (!cleanQuery)
|
|
2223
|
-
return params;
|
|
2224
|
-
for (const pair of cleanQuery.split('&')) {
|
|
2225
|
-
if (!pair)
|
|
2226
|
-
continue;
|
|
2227
|
-
const [key, value = ''] = pair.split('=').map(decodeURIComponent);
|
|
2228
|
-
if (!(key in params)) {
|
|
2229
|
-
params[key] = value;
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
return params;
|
|
2233
|
-
}
|
|
2234
|
-
function createURLSearchParams(query) {
|
|
2235
|
-
if (typeof URLSearchParams !== 'undefined') {
|
|
2236
|
-
const nativeParams = new URLSearchParams(query);
|
|
2237
|
-
return {
|
|
2238
|
-
get: (key) => nativeParams.get(key),
|
|
2239
|
-
};
|
|
2240
|
-
}
|
|
2241
|
-
const simpleParams = parseQueryString(query);
|
|
2242
|
-
return {
|
|
2243
|
-
get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
|
|
2244
|
-
};
|
|
2245
|
-
}
|
|
2246
2307
|
function getCurrentUrlSearchParams() {
|
|
2247
|
-
|
|
2248
|
-
const searchIndex = url.indexOf('?');
|
|
2249
|
-
if (searchIndex === -1) {
|
|
2250
|
-
return createURLSearchParams('');
|
|
2251
|
-
}
|
|
2252
|
-
const hashIndex = url.indexOf('#', searchIndex);
|
|
2253
|
-
const search = hashIndex !== -1
|
|
2254
|
-
? url.substring(searchIndex, hashIndex)
|
|
2255
|
-
: url.substring(searchIndex);
|
|
2256
|
-
return createURLSearchParams(search);
|
|
2308
|
+
return getUrlSearchParams(getCurrentPageUrl());
|
|
2257
2309
|
}
|
|
2258
2310
|
|
|
2259
2311
|
const defaultScale = 100000;
|
|
@@ -2488,6 +2540,44 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2488
2540
|
});
|
|
2489
2541
|
}
|
|
2490
2542
|
|
|
2543
|
+
const COFRAME_PREVIEW_SESSION_STORAGE_KEY = 'coframe.preview_variant';
|
|
2544
|
+
const COFRAME_PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2545
|
+
function normalizePreviewVariantId(raw) {
|
|
2546
|
+
if (!raw)
|
|
2547
|
+
return null;
|
|
2548
|
+
const ids = raw
|
|
2549
|
+
.split(',')
|
|
2550
|
+
.map((id) => id.trim())
|
|
2551
|
+
.filter(Boolean);
|
|
2552
|
+
return ids.length > 0 ? ids.join(',') : null;
|
|
2553
|
+
}
|
|
2554
|
+
function readPreviewVariantIdFromUrl(url = window.location.href) {
|
|
2555
|
+
try {
|
|
2556
|
+
return normalizePreviewVariantId(getUrlSearchParams(url).get(COFRAME_PREVIEW_VARIANT_QUERY_PARAM));
|
|
2557
|
+
}
|
|
2558
|
+
catch (_a) {
|
|
2559
|
+
return null;
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
function readStoredPreviewVariantId() {
|
|
2563
|
+
try {
|
|
2564
|
+
return normalizePreviewVariantId(window.sessionStorage.getItem(COFRAME_PREVIEW_SESSION_STORAGE_KEY));
|
|
2565
|
+
}
|
|
2566
|
+
catch (_a) {
|
|
2567
|
+
return null;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
function resolvePreviewSource(url = window.location.href) {
|
|
2571
|
+
const urlVariantId = readPreviewVariantIdFromUrl(url);
|
|
2572
|
+
const storedVariantId = readStoredPreviewVariantId();
|
|
2573
|
+
return {
|
|
2574
|
+
effectiveVariantId: urlVariantId || storedVariantId,
|
|
2575
|
+
isKeptForSession: Boolean(storedVariantId && storedVariantId === (urlVariantId || storedVariantId)),
|
|
2576
|
+
storedVariantId,
|
|
2577
|
+
urlVariantId,
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2491
2581
|
function buildSessionContext(isInitialApply) {
|
|
2492
2582
|
const context = {};
|
|
2493
2583
|
const viewport = getScreenSize();
|
|
@@ -2641,220 +2731,6 @@ function rebindMetrics(coframe, logger, metrics, nonce) {
|
|
|
2641
2731
|
}
|
|
2642
2732
|
}
|
|
2643
2733
|
|
|
2644
|
-
function getActivePreviewModes(sessionPreview) {
|
|
2645
|
-
if (!sessionPreview.isSessionPreviewActive ||
|
|
2646
|
-
!sessionPreview.previewVariantId) {
|
|
2647
|
-
return [];
|
|
2648
|
-
}
|
|
2649
|
-
return [
|
|
2650
|
-
{
|
|
2651
|
-
id: 'session-preview',
|
|
2652
|
-
label: 'Preview on',
|
|
2653
|
-
title: `Session preview is active for ${sessionPreview.previewVariantId}`,
|
|
2654
|
-
variantIds: sessionPreview.previewVariantId
|
|
2655
|
-
.split(',')
|
|
2656
|
-
.map((id) => id.trim())
|
|
2657
|
-
.filter(Boolean),
|
|
2658
|
-
},
|
|
2659
|
-
];
|
|
2660
|
-
}
|
|
2661
|
-
|
|
2662
|
-
const PREVIEW_UI_PROTOCOL_VERSION = 1;
|
|
2663
|
-
|
|
2664
|
-
const SESSION_VARIANT_KEY = 'coframe.preview_variant';
|
|
2665
|
-
const PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2666
|
-
const KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM = 'keep_preview_for_session';
|
|
2667
|
-
let previewDismissed = false;
|
|
2668
|
-
function dismissSessionPreview() {
|
|
2669
|
-
previewDismissed = true;
|
|
2670
|
-
clearPreviewVariantId();
|
|
2671
|
-
const url = new URL(window.location.href);
|
|
2672
|
-
url.searchParams.delete(PREVIEW_VARIANT_QUERY_PARAM);
|
|
2673
|
-
url.searchParams.delete(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM);
|
|
2674
|
-
window.history.replaceState(window.history.state, '', url);
|
|
2675
|
-
window.location.reload();
|
|
2676
|
-
}
|
|
2677
|
-
function normalizeVariantIds(raw) {
|
|
2678
|
-
if (!raw)
|
|
2679
|
-
return null;
|
|
2680
|
-
const ids = raw
|
|
2681
|
-
.split(',')
|
|
2682
|
-
.map((id) => id.trim())
|
|
2683
|
-
.filter(Boolean);
|
|
2684
|
-
return ids.length > 0 ? ids.join(',') : null;
|
|
2685
|
-
}
|
|
2686
|
-
function readPreviewVariantId() {
|
|
2687
|
-
try {
|
|
2688
|
-
return sessionStorage.getItem(SESSION_VARIANT_KEY);
|
|
2689
|
-
}
|
|
2690
|
-
catch (_a) {
|
|
2691
|
-
return null;
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
function persistPreviewVariantId(variantId) {
|
|
2695
|
-
const normalized = normalizeVariantIds(variantId);
|
|
2696
|
-
if (!normalized)
|
|
2697
|
-
return;
|
|
2698
|
-
try {
|
|
2699
|
-
sessionStorage.setItem(SESSION_VARIANT_KEY, normalized);
|
|
2700
|
-
}
|
|
2701
|
-
catch (_a) {
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
function clearPreviewVariantId() {
|
|
2705
|
-
try {
|
|
2706
|
-
sessionStorage.removeItem(SESSION_VARIANT_KEY);
|
|
2707
|
-
}
|
|
2708
|
-
catch (_a) {
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
function resolvePreviewSource(urlParams = getCurrentUrlSearchParams()) {
|
|
2712
|
-
return {
|
|
2713
|
-
urlVariantId: urlParams.get(PREVIEW_VARIANT_QUERY_PARAM),
|
|
2714
|
-
storedVariantId: readPreviewVariantId(),
|
|
2715
|
-
keepForSession: urlParams.get(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM) === 'true',
|
|
2716
|
-
};
|
|
2717
|
-
}
|
|
2718
|
-
function resolveSessionPreviewState() {
|
|
2719
|
-
if (previewDismissed) {
|
|
2720
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2721
|
-
}
|
|
2722
|
-
const previewSource = resolvePreviewSource();
|
|
2723
|
-
const urlVariantId = normalizeVariantIds(previewSource.urlVariantId);
|
|
2724
|
-
if (urlVariantId && previewSource.keepForSession) {
|
|
2725
|
-
return {
|
|
2726
|
-
isSessionPreviewActive: true,
|
|
2727
|
-
previewVariantId: urlVariantId,
|
|
2728
|
-
};
|
|
2729
|
-
}
|
|
2730
|
-
if (urlVariantId) {
|
|
2731
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2732
|
-
}
|
|
2733
|
-
const storedVariantId = normalizeVariantIds(previewSource.storedVariantId);
|
|
2734
|
-
return {
|
|
2735
|
-
isSessionPreviewActive: !!storedVariantId,
|
|
2736
|
-
previewVariantId: storedVariantId,
|
|
2737
|
-
};
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
const PREVIEW_UI_SCRIPT_ID = 'coframe-preview-ui-script';
|
|
2741
|
-
const PREVIEW_UI_URL = "https://edge.cofra.me/preview-ui/latest.js";
|
|
2742
|
-
let loadPromise = null;
|
|
2743
|
-
let latestState = { modes: [] };
|
|
2744
|
-
let nonce;
|
|
2745
|
-
let logger;
|
|
2746
|
-
let mounted = false;
|
|
2747
|
-
function reportError(message, error) {
|
|
2748
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2749
|
-
if (logger) {
|
|
2750
|
-
logger.warn(message, 'preview_ui_unavailable', { error: errorMessage });
|
|
2751
|
-
return;
|
|
2752
|
-
}
|
|
2753
|
-
console.warn(`[Coframe] ${message}`, error);
|
|
2754
|
-
}
|
|
2755
|
-
function getPreviewUI() {
|
|
2756
|
-
const api = window.CoframePreviewUI;
|
|
2757
|
-
return (api === null || api === void 0 ? void 0 : api.protocolVersion) === PREVIEW_UI_PROTOCOL_VERSION ? api : undefined;
|
|
2758
|
-
}
|
|
2759
|
-
function loadPreviewUI() {
|
|
2760
|
-
const existing = getPreviewUI();
|
|
2761
|
-
if (existing)
|
|
2762
|
-
return Promise.resolve(existing);
|
|
2763
|
-
if (loadPromise)
|
|
2764
|
-
return loadPromise;
|
|
2765
|
-
loadPromise = new Promise((resolve, reject) => {
|
|
2766
|
-
const script = document.createElement('script');
|
|
2767
|
-
script.id = PREVIEW_UI_SCRIPT_ID;
|
|
2768
|
-
script.async = true;
|
|
2769
|
-
script.src = PREVIEW_UI_URL;
|
|
2770
|
-
if (nonce)
|
|
2771
|
-
script.nonce = nonce;
|
|
2772
|
-
script.onload = () => {
|
|
2773
|
-
const api = getPreviewUI();
|
|
2774
|
-
if (!api) {
|
|
2775
|
-
reject(new Error('Preview UI protocol is missing or incompatible'));
|
|
2776
|
-
return;
|
|
2777
|
-
}
|
|
2778
|
-
resolve(api);
|
|
2779
|
-
};
|
|
2780
|
-
script.onerror = () => {
|
|
2781
|
-
reject(new Error('Preview UI script failed to load'));
|
|
2782
|
-
};
|
|
2783
|
-
document.head.appendChild(script);
|
|
2784
|
-
});
|
|
2785
|
-
return loadPromise;
|
|
2786
|
-
}
|
|
2787
|
-
function renderPreviewUI(api) {
|
|
2788
|
-
try {
|
|
2789
|
-
if (latestState.modes.length === 0) {
|
|
2790
|
-
api.unmount();
|
|
2791
|
-
mounted = false;
|
|
2792
|
-
}
|
|
2793
|
-
else if (mounted) {
|
|
2794
|
-
api.update(latestState);
|
|
2795
|
-
}
|
|
2796
|
-
else {
|
|
2797
|
-
api.mount({ state: latestState, nonce, onDismiss: dismissPreviewPanel });
|
|
2798
|
-
mounted = true;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
catch (error) {
|
|
2802
|
-
reportError('Preview UI could not be rendered', error);
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
function dismissPreviewPanel() {
|
|
2806
|
-
dismissSessionPreview();
|
|
2807
|
-
latestState = { modes: [] };
|
|
2808
|
-
const api = getPreviewUI();
|
|
2809
|
-
if (api)
|
|
2810
|
-
renderPreviewUI(api);
|
|
2811
|
-
}
|
|
2812
|
-
function modesWithVariants(sessionPreview, variantsAndElements, applied = true) {
|
|
2813
|
-
const activeModes = getActivePreviewModes(sessionPreview);
|
|
2814
|
-
if (activeModes.length === 0)
|
|
2815
|
-
return [];
|
|
2816
|
-
const currentPageVariantIds = new Set((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => variant.id)) || []);
|
|
2817
|
-
const slugById = new Map((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => [variant.id, variant.slug])) || []);
|
|
2818
|
-
return activeModes.map((mode) => ({
|
|
2819
|
-
id: mode.id,
|
|
2820
|
-
label: mode.label,
|
|
2821
|
-
title: mode.title,
|
|
2822
|
-
variants: mode.variantIds.map((id) => ({
|
|
2823
|
-
id,
|
|
2824
|
-
slug: slugById.get(id) || null,
|
|
2825
|
-
status: variantsAndElements
|
|
2826
|
-
? applied && currentPageVariantIds.has(id)
|
|
2827
|
-
? 'active'
|
|
2828
|
-
: 'inactive'
|
|
2829
|
-
: 'pending',
|
|
2830
|
-
})),
|
|
2831
|
-
}));
|
|
2832
|
-
}
|
|
2833
|
-
function resolveSessionPreviewPanel(sessionPreview, previewNonce, previewLogger) {
|
|
2834
|
-
const modes = modesWithVariants(sessionPreview);
|
|
2835
|
-
if (modes.length === 0)
|
|
2836
|
-
return;
|
|
2837
|
-
nonce = previewNonce;
|
|
2838
|
-
logger = previewLogger;
|
|
2839
|
-
latestState = { modes };
|
|
2840
|
-
const existing = getPreviewUI();
|
|
2841
|
-
if (existing) {
|
|
2842
|
-
renderPreviewUI(existing);
|
|
2843
|
-
return;
|
|
2844
|
-
}
|
|
2845
|
-
void loadPreviewUI()
|
|
2846
|
-
.then(renderPreviewUI)
|
|
2847
|
-
.catch((error) => reportError('Preview UI could not be loaded', error));
|
|
2848
|
-
}
|
|
2849
|
-
function updatePreviewPanel(variantsAndElements, sessionPreview, applied = true) {
|
|
2850
|
-
latestState = {
|
|
2851
|
-
modes: modesWithVariants(sessionPreview, variantsAndElements, applied),
|
|
2852
|
-
};
|
|
2853
|
-
const api = getPreviewUI();
|
|
2854
|
-
if (api)
|
|
2855
|
-
renderPreviewUI(api);
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
2734
|
let lastUrl = null;
|
|
2859
2735
|
function observeUrlChanges(baseUrlMatch, onUrlChange) {
|
|
2860
2736
|
const tryTrack = () => {
|
|
@@ -3209,21 +3085,23 @@ function setupTracking(Coframe, projectResult, previewVariantId, executionStartT
|
|
|
3209
3085
|
}
|
|
3210
3086
|
|
|
3211
3087
|
function resolvePreview(pageUrl, previewVariantId, isSessionVariant) {
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3088
|
+
const previewSource = resolvePreviewSource(pageUrl);
|
|
3089
|
+
if (previewSource.urlVariantId) {
|
|
3090
|
+
return {
|
|
3091
|
+
previewVariantId: previewSource.urlVariantId,
|
|
3092
|
+
isSessionVariant: previewSource.isKeptForSession,
|
|
3093
|
+
};
|
|
3218
3094
|
}
|
|
3219
|
-
if (
|
|
3220
|
-
return {
|
|
3095
|
+
if (previewSource.storedVariantId) {
|
|
3096
|
+
return {
|
|
3097
|
+
previewVariantId: previewSource.storedVariantId,
|
|
3098
|
+
isSessionVariant: true,
|
|
3099
|
+
};
|
|
3221
3100
|
}
|
|
3222
3101
|
if (previewVariantId) {
|
|
3223
3102
|
return { previewVariantId, isSessionVariant };
|
|
3224
3103
|
}
|
|
3225
|
-
|
|
3226
|
-
return { previewVariantId: stored, isSessionVariant: !!stored };
|
|
3104
|
+
return { previewVariantId: null, isSessionVariant: false };
|
|
3227
3105
|
}
|
|
3228
3106
|
async function awaitHydration(config, Coframe, project) {
|
|
3229
3107
|
var _a, _b;
|
|
@@ -3356,8 +3234,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3356
3234
|
Coframe.experimentInfo = experimentsInfo;
|
|
3357
3235
|
Coframe.variantsAndElements = variantsAndElements;
|
|
3358
3236
|
setFrameActivated(hasActivatedFrames);
|
|
3359
|
-
const sessionPreview = resolveSessionPreviewState();
|
|
3360
|
-
updatePreviewPanel(variantsAndElements, sessionPreview, !config.trackingOnly);
|
|
3361
3237
|
if (Coframe.debug) {
|
|
3362
3238
|
Coframe.debug.project = pageProject;
|
|
3363
3239
|
Coframe.debug.frames = pageProject.frames;
|
|
@@ -3377,11 +3253,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3377
3253
|
async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
3378
3254
|
const { config, timeoutMs, previewVariantId, isSessionVariant, integrationStartTime, executionStartTime, } = configResult;
|
|
3379
3255
|
const { project: cdnProject, personalizationHeaders } = projectResult;
|
|
3380
|
-
if (resolveSessionPreviewState().isSessionPreviewActive) {
|
|
3381
|
-
waitForBody(timeoutMs)
|
|
3382
|
-
.then(() => resolveSessionPreviewPanel(resolveSessionPreviewState(), config.nonce, Coframe.logger))
|
|
3383
|
-
.catch(() => { });
|
|
3384
|
-
}
|
|
3385
3256
|
if (!(await awaitHydration(config, Coframe, cdnProject)))
|
|
3386
3257
|
return;
|
|
3387
3258
|
if (cdnProject.tags) {
|
|
@@ -3474,15 +3345,13 @@ async function resolveConfig(Coframe, revealPage) {
|
|
|
3474
3345
|
}
|
|
3475
3346
|
Coframe.projectId = projectId;
|
|
3476
3347
|
const urlParams = getCurrentUrlSearchParams();
|
|
3477
|
-
const previewSource = resolvePreviewSource(
|
|
3348
|
+
const previewSource = resolvePreviewSource();
|
|
3478
3349
|
const shouldSendEventsParam = urlParams.get('coframe_should_send_events');
|
|
3479
3350
|
let previewVariantId = null;
|
|
3480
3351
|
let isSessionVariant = false;
|
|
3481
3352
|
if (previewSource.urlVariantId) {
|
|
3482
3353
|
previewVariantId = previewSource.urlVariantId;
|
|
3483
|
-
|
|
3484
|
-
persistPreviewVariantId(previewSource.urlVariantId);
|
|
3485
|
-
}
|
|
3354
|
+
isSessionVariant = previewSource.isKeptForSession;
|
|
3486
3355
|
}
|
|
3487
3356
|
else if (previewSource.storedVariantId) {
|
|
3488
3357
|
previewVariantId = previewSource.storedVariantId;
|
|
@@ -3582,6 +3451,66 @@ async function resolveProject(Coframe, previewVariantId, isSessionVariant, confi
|
|
|
3582
3451
|
};
|
|
3583
3452
|
}
|
|
3584
3453
|
|
|
3454
|
+
const COFRAME_LAUNCHER_PROTOCOL_VERSION = 1;
|
|
3455
|
+
|
|
3456
|
+
const LAUNCHER_SCRIPT_ID = 'coframe-tools-launcher-script';
|
|
3457
|
+
const LAUNCHER_URL = "https://edge.cofra.me/launcher/latest.js";
|
|
3458
|
+
let loading = false;
|
|
3459
|
+
function getLauncher() {
|
|
3460
|
+
const launcher = window.CoframeLauncher;
|
|
3461
|
+
return (launcher === null || launcher === void 0 ? void 0 : launcher.protocolVersion) === COFRAME_LAUNCHER_PROTOCOL_VERSION
|
|
3462
|
+
? launcher
|
|
3463
|
+
: undefined;
|
|
3464
|
+
}
|
|
3465
|
+
function exitDebugSession() {
|
|
3466
|
+
clearDebugToolsSession();
|
|
3467
|
+
const url = new URL(window.location.href);
|
|
3468
|
+
url.searchParams.delete(COFRAME_DEBUG_QUERY_PARAM);
|
|
3469
|
+
window.history.replaceState(window.history.state, '', url);
|
|
3470
|
+
}
|
|
3471
|
+
function mountLauncher(nonce) {
|
|
3472
|
+
const launcher = getLauncher();
|
|
3473
|
+
if (!launcher)
|
|
3474
|
+
return false;
|
|
3475
|
+
launcher.mount({ nonce, onExitDebugSession: exitDebugSession });
|
|
3476
|
+
return true;
|
|
3477
|
+
}
|
|
3478
|
+
function loadLauncherIfEnabled(enabled = true, nonce) {
|
|
3479
|
+
if (typeof window === 'undefined')
|
|
3480
|
+
return;
|
|
3481
|
+
let sessionEnabled = false;
|
|
3482
|
+
try {
|
|
3483
|
+
sessionEnabled = isDebugToolsSessionEnabled(enabled);
|
|
3484
|
+
}
|
|
3485
|
+
catch (error) {
|
|
3486
|
+
console.warn('[Coframe] Unable to determine Debug Tools session', error);
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3489
|
+
if (!sessionEnabled || mountLauncher(nonce) || loading) {
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3492
|
+
loading = true;
|
|
3493
|
+
const script = document.createElement('script');
|
|
3494
|
+
script.id = LAUNCHER_SCRIPT_ID;
|
|
3495
|
+
script.async = true;
|
|
3496
|
+
script.src = LAUNCHER_URL;
|
|
3497
|
+
if (nonce)
|
|
3498
|
+
script.nonce = nonce;
|
|
3499
|
+
script.onload = () => {
|
|
3500
|
+
loading = false;
|
|
3501
|
+
if (!mountLauncher(nonce)) {
|
|
3502
|
+
document.head.removeChild(script);
|
|
3503
|
+
console.warn('[Coframe] Launcher protocol is missing or incompatible');
|
|
3504
|
+
}
|
|
3505
|
+
};
|
|
3506
|
+
script.onerror = () => {
|
|
3507
|
+
loading = false;
|
|
3508
|
+
document.head.removeChild(script);
|
|
3509
|
+
console.warn('[Coframe] Launcher script failed to load');
|
|
3510
|
+
};
|
|
3511
|
+
document.head.appendChild(script);
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3585
3514
|
function injectOptions(options) {
|
|
3586
3515
|
var _a;
|
|
3587
3516
|
if (options.config) {
|
|
@@ -3663,6 +3592,7 @@ async function init(options) {
|
|
|
3663
3592
|
const configResult = await resolveConfig(Coframe, revealPage);
|
|
3664
3593
|
if (!configResult)
|
|
3665
3594
|
return;
|
|
3595
|
+
loadLauncherIfEnabled(configResult.config.enableDebug, configResult.config.nonce);
|
|
3666
3596
|
const projectResult = await resolveProject(Coframe, configResult.previewVariantId, configResult.isSessionVariant, configResult.config);
|
|
3667
3597
|
if (!projectResult)
|
|
3668
3598
|
return;
|
package/dist/pkg/cf.esm.mjs
CHANGED
|
@@ -857,6 +857,88 @@ const isSegmentEmpty = (segment) => {
|
|
|
857
857
|
return segment.xpath === '';
|
|
858
858
|
};
|
|
859
859
|
|
|
860
|
+
function parseQueryString(query) {
|
|
861
|
+
const params = {};
|
|
862
|
+
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
|
|
863
|
+
if (!cleanQuery)
|
|
864
|
+
return params;
|
|
865
|
+
for (const pair of cleanQuery.split('&')) {
|
|
866
|
+
if (!pair)
|
|
867
|
+
continue;
|
|
868
|
+
const [key, value = ''] = pair.split('=').map(decodeURIComponent);
|
|
869
|
+
if (!(key in params)) {
|
|
870
|
+
params[key] = value;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return params;
|
|
874
|
+
}
|
|
875
|
+
function createURLSearchParams(query) {
|
|
876
|
+
if (typeof URLSearchParams !== 'undefined') {
|
|
877
|
+
const nativeParams = new URLSearchParams(query);
|
|
878
|
+
return {
|
|
879
|
+
get: (key) => nativeParams.get(key),
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
const simpleParams = parseQueryString(query);
|
|
883
|
+
return {
|
|
884
|
+
get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
function getUrlSearchParams(url) {
|
|
888
|
+
const searchIndex = url.indexOf('?');
|
|
889
|
+
const firstHashIndex = url.indexOf('#');
|
|
890
|
+
if (searchIndex === -1 ||
|
|
891
|
+
(firstHashIndex !== -1 && firstHashIndex < searchIndex)) {
|
|
892
|
+
return createURLSearchParams('');
|
|
893
|
+
}
|
|
894
|
+
const hashIndex = url.indexOf('#', searchIndex);
|
|
895
|
+
const search = hashIndex !== -1
|
|
896
|
+
? url.substring(searchIndex, hashIndex)
|
|
897
|
+
: url.substring(searchIndex);
|
|
898
|
+
return createURLSearchParams(search);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const COFRAME_DEBUG_QUERY_PARAM = 'cf_debug';
|
|
902
|
+
const COFRAME_TOOL_INTERACTION_ATTRIBUTE = 'data-cf-t';
|
|
903
|
+
const COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY = 'cf.tools.enabled';
|
|
904
|
+
const COFRAME_DEBUG_TOOLS_STATE_STORAGE_KEY = 'cf.tools.session';
|
|
905
|
+
const ENABLED_VALUE = 'true';
|
|
906
|
+
function enableDebugToolsSession() {
|
|
907
|
+
try {
|
|
908
|
+
window.sessionStorage.setItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY, ENABLED_VALUE);
|
|
909
|
+
}
|
|
910
|
+
catch (_a) {
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function clearDebugToolsSession() {
|
|
914
|
+
try {
|
|
915
|
+
window.sessionStorage.removeItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY);
|
|
916
|
+
window.sessionStorage.removeItem(COFRAME_DEBUG_TOOLS_STATE_STORAGE_KEY);
|
|
917
|
+
}
|
|
918
|
+
catch (_a) {
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
function isDebugToolsSessionEnabled(allowed) {
|
|
922
|
+
if (!allowed) {
|
|
923
|
+
clearDebugToolsSession();
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
const enabledByUrl = getUrlSearchParams(getCurrentPageUrl()).get(COFRAME_DEBUG_QUERY_PARAM) ===
|
|
927
|
+
ENABLED_VALUE;
|
|
928
|
+
if (enabledByUrl) {
|
|
929
|
+
enableDebugToolsSession();
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
932
|
+
try {
|
|
933
|
+
const enabled = window.sessionStorage.getItem(COFRAME_DEBUG_TOOLS_SESSION_STORAGE_KEY) ===
|
|
934
|
+
ENABLED_VALUE;
|
|
935
|
+
return enabled;
|
|
936
|
+
}
|
|
937
|
+
catch (_a) {
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
860
942
|
const USER_CONTEXT_KEY_PREFIX = 'x-ctx-';
|
|
861
943
|
const MAX_USER_CONTEXT_JSON_BYTES = 4096;
|
|
862
944
|
function userContextJsonByteLength(userContext) {
|
|
@@ -1035,6 +1117,12 @@ function queueVariantViewEvent(coframe, { projectId, sessionId, orgId, userToken
|
|
|
1035
1117
|
}
|
|
1036
1118
|
function sendPageEngagementEvent(coframe, eventType, event, { projectId, sessionId, duration = 0, userToken, personalizationHeaders, }) {
|
|
1037
1119
|
var _a;
|
|
1120
|
+
if (event
|
|
1121
|
+
.composedPath()
|
|
1122
|
+
.some((target) => target instanceof Element &&
|
|
1123
|
+
target.hasAttribute(COFRAME_TOOL_INTERACTION_ATTRIBUTE))) {
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1038
1126
|
const startTime = performance.now();
|
|
1039
1127
|
const x = event.clientX + window.scrollX;
|
|
1040
1128
|
const y = event.clientY + window.scrollY;
|
|
@@ -2214,44 +2302,8 @@ function debounce(func, wait) {
|
|
|
2214
2302
|
}, wait);
|
|
2215
2303
|
};
|
|
2216
2304
|
}
|
|
2217
|
-
function parseQueryString(query) {
|
|
2218
|
-
const params = {};
|
|
2219
|
-
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
|
|
2220
|
-
if (!cleanQuery)
|
|
2221
|
-
return params;
|
|
2222
|
-
for (const pair of cleanQuery.split('&')) {
|
|
2223
|
-
if (!pair)
|
|
2224
|
-
continue;
|
|
2225
|
-
const [key, value = ''] = pair.split('=').map(decodeURIComponent);
|
|
2226
|
-
if (!(key in params)) {
|
|
2227
|
-
params[key] = value;
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
return params;
|
|
2231
|
-
}
|
|
2232
|
-
function createURLSearchParams(query) {
|
|
2233
|
-
if (typeof URLSearchParams !== 'undefined') {
|
|
2234
|
-
const nativeParams = new URLSearchParams(query);
|
|
2235
|
-
return {
|
|
2236
|
-
get: (key) => nativeParams.get(key),
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
const simpleParams = parseQueryString(query);
|
|
2240
|
-
return {
|
|
2241
|
-
get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
|
|
2242
|
-
};
|
|
2243
|
-
}
|
|
2244
2305
|
function getCurrentUrlSearchParams() {
|
|
2245
|
-
|
|
2246
|
-
const searchIndex = url.indexOf('?');
|
|
2247
|
-
if (searchIndex === -1) {
|
|
2248
|
-
return createURLSearchParams('');
|
|
2249
|
-
}
|
|
2250
|
-
const hashIndex = url.indexOf('#', searchIndex);
|
|
2251
|
-
const search = hashIndex !== -1
|
|
2252
|
-
? url.substring(searchIndex, hashIndex)
|
|
2253
|
-
: url.substring(searchIndex);
|
|
2254
|
-
return createURLSearchParams(search);
|
|
2306
|
+
return getUrlSearchParams(getCurrentPageUrl());
|
|
2255
2307
|
}
|
|
2256
2308
|
|
|
2257
2309
|
const defaultScale = 100000;
|
|
@@ -2486,6 +2538,44 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2486
2538
|
});
|
|
2487
2539
|
}
|
|
2488
2540
|
|
|
2541
|
+
const COFRAME_PREVIEW_SESSION_STORAGE_KEY = 'coframe.preview_variant';
|
|
2542
|
+
const COFRAME_PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2543
|
+
function normalizePreviewVariantId(raw) {
|
|
2544
|
+
if (!raw)
|
|
2545
|
+
return null;
|
|
2546
|
+
const ids = raw
|
|
2547
|
+
.split(',')
|
|
2548
|
+
.map((id) => id.trim())
|
|
2549
|
+
.filter(Boolean);
|
|
2550
|
+
return ids.length > 0 ? ids.join(',') : null;
|
|
2551
|
+
}
|
|
2552
|
+
function readPreviewVariantIdFromUrl(url = window.location.href) {
|
|
2553
|
+
try {
|
|
2554
|
+
return normalizePreviewVariantId(getUrlSearchParams(url).get(COFRAME_PREVIEW_VARIANT_QUERY_PARAM));
|
|
2555
|
+
}
|
|
2556
|
+
catch (_a) {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
function readStoredPreviewVariantId() {
|
|
2561
|
+
try {
|
|
2562
|
+
return normalizePreviewVariantId(window.sessionStorage.getItem(COFRAME_PREVIEW_SESSION_STORAGE_KEY));
|
|
2563
|
+
}
|
|
2564
|
+
catch (_a) {
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
function resolvePreviewSource(url = window.location.href) {
|
|
2569
|
+
const urlVariantId = readPreviewVariantIdFromUrl(url);
|
|
2570
|
+
const storedVariantId = readStoredPreviewVariantId();
|
|
2571
|
+
return {
|
|
2572
|
+
effectiveVariantId: urlVariantId || storedVariantId,
|
|
2573
|
+
isKeptForSession: Boolean(storedVariantId && storedVariantId === (urlVariantId || storedVariantId)),
|
|
2574
|
+
storedVariantId,
|
|
2575
|
+
urlVariantId,
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2489
2579
|
function buildSessionContext(isInitialApply) {
|
|
2490
2580
|
const context = {};
|
|
2491
2581
|
const viewport = getScreenSize();
|
|
@@ -2639,220 +2729,6 @@ function rebindMetrics(coframe, logger, metrics, nonce) {
|
|
|
2639
2729
|
}
|
|
2640
2730
|
}
|
|
2641
2731
|
|
|
2642
|
-
function getActivePreviewModes(sessionPreview) {
|
|
2643
|
-
if (!sessionPreview.isSessionPreviewActive ||
|
|
2644
|
-
!sessionPreview.previewVariantId) {
|
|
2645
|
-
return [];
|
|
2646
|
-
}
|
|
2647
|
-
return [
|
|
2648
|
-
{
|
|
2649
|
-
id: 'session-preview',
|
|
2650
|
-
label: 'Preview on',
|
|
2651
|
-
title: `Session preview is active for ${sessionPreview.previewVariantId}`,
|
|
2652
|
-
variantIds: sessionPreview.previewVariantId
|
|
2653
|
-
.split(',')
|
|
2654
|
-
.map((id) => id.trim())
|
|
2655
|
-
.filter(Boolean),
|
|
2656
|
-
},
|
|
2657
|
-
];
|
|
2658
|
-
}
|
|
2659
|
-
|
|
2660
|
-
const PREVIEW_UI_PROTOCOL_VERSION = 1;
|
|
2661
|
-
|
|
2662
|
-
const SESSION_VARIANT_KEY = 'coframe.preview_variant';
|
|
2663
|
-
const PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2664
|
-
const KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM = 'keep_preview_for_session';
|
|
2665
|
-
let previewDismissed = false;
|
|
2666
|
-
function dismissSessionPreview() {
|
|
2667
|
-
previewDismissed = true;
|
|
2668
|
-
clearPreviewVariantId();
|
|
2669
|
-
const url = new URL(window.location.href);
|
|
2670
|
-
url.searchParams.delete(PREVIEW_VARIANT_QUERY_PARAM);
|
|
2671
|
-
url.searchParams.delete(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM);
|
|
2672
|
-
window.history.replaceState(window.history.state, '', url);
|
|
2673
|
-
window.location.reload();
|
|
2674
|
-
}
|
|
2675
|
-
function normalizeVariantIds(raw) {
|
|
2676
|
-
if (!raw)
|
|
2677
|
-
return null;
|
|
2678
|
-
const ids = raw
|
|
2679
|
-
.split(',')
|
|
2680
|
-
.map((id) => id.trim())
|
|
2681
|
-
.filter(Boolean);
|
|
2682
|
-
return ids.length > 0 ? ids.join(',') : null;
|
|
2683
|
-
}
|
|
2684
|
-
function readPreviewVariantId() {
|
|
2685
|
-
try {
|
|
2686
|
-
return sessionStorage.getItem(SESSION_VARIANT_KEY);
|
|
2687
|
-
}
|
|
2688
|
-
catch (_a) {
|
|
2689
|
-
return null;
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
function persistPreviewVariantId(variantId) {
|
|
2693
|
-
const normalized = normalizeVariantIds(variantId);
|
|
2694
|
-
if (!normalized)
|
|
2695
|
-
return;
|
|
2696
|
-
try {
|
|
2697
|
-
sessionStorage.setItem(SESSION_VARIANT_KEY, normalized);
|
|
2698
|
-
}
|
|
2699
|
-
catch (_a) {
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
function clearPreviewVariantId() {
|
|
2703
|
-
try {
|
|
2704
|
-
sessionStorage.removeItem(SESSION_VARIANT_KEY);
|
|
2705
|
-
}
|
|
2706
|
-
catch (_a) {
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
function resolvePreviewSource(urlParams = getCurrentUrlSearchParams()) {
|
|
2710
|
-
return {
|
|
2711
|
-
urlVariantId: urlParams.get(PREVIEW_VARIANT_QUERY_PARAM),
|
|
2712
|
-
storedVariantId: readPreviewVariantId(),
|
|
2713
|
-
keepForSession: urlParams.get(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM) === 'true',
|
|
2714
|
-
};
|
|
2715
|
-
}
|
|
2716
|
-
function resolveSessionPreviewState() {
|
|
2717
|
-
if (previewDismissed) {
|
|
2718
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2719
|
-
}
|
|
2720
|
-
const previewSource = resolvePreviewSource();
|
|
2721
|
-
const urlVariantId = normalizeVariantIds(previewSource.urlVariantId);
|
|
2722
|
-
if (urlVariantId && previewSource.keepForSession) {
|
|
2723
|
-
return {
|
|
2724
|
-
isSessionPreviewActive: true,
|
|
2725
|
-
previewVariantId: urlVariantId,
|
|
2726
|
-
};
|
|
2727
|
-
}
|
|
2728
|
-
if (urlVariantId) {
|
|
2729
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2730
|
-
}
|
|
2731
|
-
const storedVariantId = normalizeVariantIds(previewSource.storedVariantId);
|
|
2732
|
-
return {
|
|
2733
|
-
isSessionPreviewActive: !!storedVariantId,
|
|
2734
|
-
previewVariantId: storedVariantId,
|
|
2735
|
-
};
|
|
2736
|
-
}
|
|
2737
|
-
|
|
2738
|
-
const PREVIEW_UI_SCRIPT_ID = 'coframe-preview-ui-script';
|
|
2739
|
-
const PREVIEW_UI_URL = "https://edge.cofra.me/preview-ui/latest.js";
|
|
2740
|
-
let loadPromise = null;
|
|
2741
|
-
let latestState = { modes: [] };
|
|
2742
|
-
let nonce;
|
|
2743
|
-
let logger;
|
|
2744
|
-
let mounted = false;
|
|
2745
|
-
function reportError(message, error) {
|
|
2746
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2747
|
-
if (logger) {
|
|
2748
|
-
logger.warn(message, 'preview_ui_unavailable', { error: errorMessage });
|
|
2749
|
-
return;
|
|
2750
|
-
}
|
|
2751
|
-
console.warn(`[Coframe] ${message}`, error);
|
|
2752
|
-
}
|
|
2753
|
-
function getPreviewUI() {
|
|
2754
|
-
const api = window.CoframePreviewUI;
|
|
2755
|
-
return (api === null || api === void 0 ? void 0 : api.protocolVersion) === PREVIEW_UI_PROTOCOL_VERSION ? api : undefined;
|
|
2756
|
-
}
|
|
2757
|
-
function loadPreviewUI() {
|
|
2758
|
-
const existing = getPreviewUI();
|
|
2759
|
-
if (existing)
|
|
2760
|
-
return Promise.resolve(existing);
|
|
2761
|
-
if (loadPromise)
|
|
2762
|
-
return loadPromise;
|
|
2763
|
-
loadPromise = new Promise((resolve, reject) => {
|
|
2764
|
-
const script = document.createElement('script');
|
|
2765
|
-
script.id = PREVIEW_UI_SCRIPT_ID;
|
|
2766
|
-
script.async = true;
|
|
2767
|
-
script.src = PREVIEW_UI_URL;
|
|
2768
|
-
if (nonce)
|
|
2769
|
-
script.nonce = nonce;
|
|
2770
|
-
script.onload = () => {
|
|
2771
|
-
const api = getPreviewUI();
|
|
2772
|
-
if (!api) {
|
|
2773
|
-
reject(new Error('Preview UI protocol is missing or incompatible'));
|
|
2774
|
-
return;
|
|
2775
|
-
}
|
|
2776
|
-
resolve(api);
|
|
2777
|
-
};
|
|
2778
|
-
script.onerror = () => {
|
|
2779
|
-
reject(new Error('Preview UI script failed to load'));
|
|
2780
|
-
};
|
|
2781
|
-
document.head.appendChild(script);
|
|
2782
|
-
});
|
|
2783
|
-
return loadPromise;
|
|
2784
|
-
}
|
|
2785
|
-
function renderPreviewUI(api) {
|
|
2786
|
-
try {
|
|
2787
|
-
if (latestState.modes.length === 0) {
|
|
2788
|
-
api.unmount();
|
|
2789
|
-
mounted = false;
|
|
2790
|
-
}
|
|
2791
|
-
else if (mounted) {
|
|
2792
|
-
api.update(latestState);
|
|
2793
|
-
}
|
|
2794
|
-
else {
|
|
2795
|
-
api.mount({ state: latestState, nonce, onDismiss: dismissPreviewPanel });
|
|
2796
|
-
mounted = true;
|
|
2797
|
-
}
|
|
2798
|
-
}
|
|
2799
|
-
catch (error) {
|
|
2800
|
-
reportError('Preview UI could not be rendered', error);
|
|
2801
|
-
}
|
|
2802
|
-
}
|
|
2803
|
-
function dismissPreviewPanel() {
|
|
2804
|
-
dismissSessionPreview();
|
|
2805
|
-
latestState = { modes: [] };
|
|
2806
|
-
const api = getPreviewUI();
|
|
2807
|
-
if (api)
|
|
2808
|
-
renderPreviewUI(api);
|
|
2809
|
-
}
|
|
2810
|
-
function modesWithVariants(sessionPreview, variantsAndElements, applied = true) {
|
|
2811
|
-
const activeModes = getActivePreviewModes(sessionPreview);
|
|
2812
|
-
if (activeModes.length === 0)
|
|
2813
|
-
return [];
|
|
2814
|
-
const currentPageVariantIds = new Set((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => variant.id)) || []);
|
|
2815
|
-
const slugById = new Map((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => [variant.id, variant.slug])) || []);
|
|
2816
|
-
return activeModes.map((mode) => ({
|
|
2817
|
-
id: mode.id,
|
|
2818
|
-
label: mode.label,
|
|
2819
|
-
title: mode.title,
|
|
2820
|
-
variants: mode.variantIds.map((id) => ({
|
|
2821
|
-
id,
|
|
2822
|
-
slug: slugById.get(id) || null,
|
|
2823
|
-
status: variantsAndElements
|
|
2824
|
-
? applied && currentPageVariantIds.has(id)
|
|
2825
|
-
? 'active'
|
|
2826
|
-
: 'inactive'
|
|
2827
|
-
: 'pending',
|
|
2828
|
-
})),
|
|
2829
|
-
}));
|
|
2830
|
-
}
|
|
2831
|
-
function resolveSessionPreviewPanel(sessionPreview, previewNonce, previewLogger) {
|
|
2832
|
-
const modes = modesWithVariants(sessionPreview);
|
|
2833
|
-
if (modes.length === 0)
|
|
2834
|
-
return;
|
|
2835
|
-
nonce = previewNonce;
|
|
2836
|
-
logger = previewLogger;
|
|
2837
|
-
latestState = { modes };
|
|
2838
|
-
const existing = getPreviewUI();
|
|
2839
|
-
if (existing) {
|
|
2840
|
-
renderPreviewUI(existing);
|
|
2841
|
-
return;
|
|
2842
|
-
}
|
|
2843
|
-
void loadPreviewUI()
|
|
2844
|
-
.then(renderPreviewUI)
|
|
2845
|
-
.catch((error) => reportError('Preview UI could not be loaded', error));
|
|
2846
|
-
}
|
|
2847
|
-
function updatePreviewPanel(variantsAndElements, sessionPreview, applied = true) {
|
|
2848
|
-
latestState = {
|
|
2849
|
-
modes: modesWithVariants(sessionPreview, variantsAndElements, applied),
|
|
2850
|
-
};
|
|
2851
|
-
const api = getPreviewUI();
|
|
2852
|
-
if (api)
|
|
2853
|
-
renderPreviewUI(api);
|
|
2854
|
-
}
|
|
2855
|
-
|
|
2856
2732
|
let lastUrl = null;
|
|
2857
2733
|
function observeUrlChanges(baseUrlMatch, onUrlChange) {
|
|
2858
2734
|
const tryTrack = () => {
|
|
@@ -3207,21 +3083,23 @@ function setupTracking(Coframe, projectResult, previewVariantId, executionStartT
|
|
|
3207
3083
|
}
|
|
3208
3084
|
|
|
3209
3085
|
function resolvePreview(pageUrl, previewVariantId, isSessionVariant) {
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3086
|
+
const previewSource = resolvePreviewSource(pageUrl);
|
|
3087
|
+
if (previewSource.urlVariantId) {
|
|
3088
|
+
return {
|
|
3089
|
+
previewVariantId: previewSource.urlVariantId,
|
|
3090
|
+
isSessionVariant: previewSource.isKeptForSession,
|
|
3091
|
+
};
|
|
3216
3092
|
}
|
|
3217
|
-
if (
|
|
3218
|
-
return {
|
|
3093
|
+
if (previewSource.storedVariantId) {
|
|
3094
|
+
return {
|
|
3095
|
+
previewVariantId: previewSource.storedVariantId,
|
|
3096
|
+
isSessionVariant: true,
|
|
3097
|
+
};
|
|
3219
3098
|
}
|
|
3220
3099
|
if (previewVariantId) {
|
|
3221
3100
|
return { previewVariantId, isSessionVariant };
|
|
3222
3101
|
}
|
|
3223
|
-
|
|
3224
|
-
return { previewVariantId: stored, isSessionVariant: !!stored };
|
|
3102
|
+
return { previewVariantId: null, isSessionVariant: false };
|
|
3225
3103
|
}
|
|
3226
3104
|
async function awaitHydration(config, Coframe, project) {
|
|
3227
3105
|
var _a, _b;
|
|
@@ -3354,8 +3232,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3354
3232
|
Coframe.experimentInfo = experimentsInfo;
|
|
3355
3233
|
Coframe.variantsAndElements = variantsAndElements;
|
|
3356
3234
|
setFrameActivated(hasActivatedFrames);
|
|
3357
|
-
const sessionPreview = resolveSessionPreviewState();
|
|
3358
|
-
updatePreviewPanel(variantsAndElements, sessionPreview, !config.trackingOnly);
|
|
3359
3235
|
if (Coframe.debug) {
|
|
3360
3236
|
Coframe.debug.project = pageProject;
|
|
3361
3237
|
Coframe.debug.frames = pageProject.frames;
|
|
@@ -3375,11 +3251,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3375
3251
|
async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
3376
3252
|
const { config, timeoutMs, previewVariantId, isSessionVariant, integrationStartTime, executionStartTime, } = configResult;
|
|
3377
3253
|
const { project: cdnProject, personalizationHeaders } = projectResult;
|
|
3378
|
-
if (resolveSessionPreviewState().isSessionPreviewActive) {
|
|
3379
|
-
waitForBody(timeoutMs)
|
|
3380
|
-
.then(() => resolveSessionPreviewPanel(resolveSessionPreviewState(), config.nonce, Coframe.logger))
|
|
3381
|
-
.catch(() => { });
|
|
3382
|
-
}
|
|
3383
3254
|
if (!(await awaitHydration(config, Coframe, cdnProject)))
|
|
3384
3255
|
return;
|
|
3385
3256
|
if (cdnProject.tags) {
|
|
@@ -3472,15 +3343,13 @@ async function resolveConfig(Coframe, revealPage) {
|
|
|
3472
3343
|
}
|
|
3473
3344
|
Coframe.projectId = projectId;
|
|
3474
3345
|
const urlParams = getCurrentUrlSearchParams();
|
|
3475
|
-
const previewSource = resolvePreviewSource(
|
|
3346
|
+
const previewSource = resolvePreviewSource();
|
|
3476
3347
|
const shouldSendEventsParam = urlParams.get('coframe_should_send_events');
|
|
3477
3348
|
let previewVariantId = null;
|
|
3478
3349
|
let isSessionVariant = false;
|
|
3479
3350
|
if (previewSource.urlVariantId) {
|
|
3480
3351
|
previewVariantId = previewSource.urlVariantId;
|
|
3481
|
-
|
|
3482
|
-
persistPreviewVariantId(previewSource.urlVariantId);
|
|
3483
|
-
}
|
|
3352
|
+
isSessionVariant = previewSource.isKeptForSession;
|
|
3484
3353
|
}
|
|
3485
3354
|
else if (previewSource.storedVariantId) {
|
|
3486
3355
|
previewVariantId = previewSource.storedVariantId;
|
|
@@ -3580,6 +3449,66 @@ async function resolveProject(Coframe, previewVariantId, isSessionVariant, confi
|
|
|
3580
3449
|
};
|
|
3581
3450
|
}
|
|
3582
3451
|
|
|
3452
|
+
const COFRAME_LAUNCHER_PROTOCOL_VERSION = 1;
|
|
3453
|
+
|
|
3454
|
+
const LAUNCHER_SCRIPT_ID = 'coframe-tools-launcher-script';
|
|
3455
|
+
const LAUNCHER_URL = "https://edge.cofra.me/launcher/latest.js";
|
|
3456
|
+
let loading = false;
|
|
3457
|
+
function getLauncher() {
|
|
3458
|
+
const launcher = window.CoframeLauncher;
|
|
3459
|
+
return (launcher === null || launcher === void 0 ? void 0 : launcher.protocolVersion) === COFRAME_LAUNCHER_PROTOCOL_VERSION
|
|
3460
|
+
? launcher
|
|
3461
|
+
: undefined;
|
|
3462
|
+
}
|
|
3463
|
+
function exitDebugSession() {
|
|
3464
|
+
clearDebugToolsSession();
|
|
3465
|
+
const url = new URL(window.location.href);
|
|
3466
|
+
url.searchParams.delete(COFRAME_DEBUG_QUERY_PARAM);
|
|
3467
|
+
window.history.replaceState(window.history.state, '', url);
|
|
3468
|
+
}
|
|
3469
|
+
function mountLauncher(nonce) {
|
|
3470
|
+
const launcher = getLauncher();
|
|
3471
|
+
if (!launcher)
|
|
3472
|
+
return false;
|
|
3473
|
+
launcher.mount({ nonce, onExitDebugSession: exitDebugSession });
|
|
3474
|
+
return true;
|
|
3475
|
+
}
|
|
3476
|
+
function loadLauncherIfEnabled(enabled = true, nonce) {
|
|
3477
|
+
if (typeof window === 'undefined')
|
|
3478
|
+
return;
|
|
3479
|
+
let sessionEnabled = false;
|
|
3480
|
+
try {
|
|
3481
|
+
sessionEnabled = isDebugToolsSessionEnabled(enabled);
|
|
3482
|
+
}
|
|
3483
|
+
catch (error) {
|
|
3484
|
+
console.warn('[Coframe] Unable to determine Debug Tools session', error);
|
|
3485
|
+
return;
|
|
3486
|
+
}
|
|
3487
|
+
if (!sessionEnabled || mountLauncher(nonce) || loading) {
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
loading = true;
|
|
3491
|
+
const script = document.createElement('script');
|
|
3492
|
+
script.id = LAUNCHER_SCRIPT_ID;
|
|
3493
|
+
script.async = true;
|
|
3494
|
+
script.src = LAUNCHER_URL;
|
|
3495
|
+
if (nonce)
|
|
3496
|
+
script.nonce = nonce;
|
|
3497
|
+
script.onload = () => {
|
|
3498
|
+
loading = false;
|
|
3499
|
+
if (!mountLauncher(nonce)) {
|
|
3500
|
+
document.head.removeChild(script);
|
|
3501
|
+
console.warn('[Coframe] Launcher protocol is missing or incompatible');
|
|
3502
|
+
}
|
|
3503
|
+
};
|
|
3504
|
+
script.onerror = () => {
|
|
3505
|
+
loading = false;
|
|
3506
|
+
document.head.removeChild(script);
|
|
3507
|
+
console.warn('[Coframe] Launcher script failed to load');
|
|
3508
|
+
};
|
|
3509
|
+
document.head.appendChild(script);
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3583
3512
|
function injectOptions(options) {
|
|
3584
3513
|
var _a;
|
|
3585
3514
|
if (options.config) {
|
|
@@ -3661,6 +3590,7 @@ async function init(options) {
|
|
|
3661
3590
|
const configResult = await resolveConfig(Coframe, revealPage);
|
|
3662
3591
|
if (!configResult)
|
|
3663
3592
|
return;
|
|
3593
|
+
loadLauncherIfEnabled(configResult.config.enableDebug, configResult.config.nonce);
|
|
3664
3594
|
const projectResult = await resolveProject(Coframe, configResult.previewVariantId, configResult.isSessionVariant, configResult.config);
|
|
3665
3595
|
if (!projectResult)
|
|
3666
3596
|
return;
|
|
@@ -251,6 +251,7 @@ type CoframeConfig = {
|
|
|
251
251
|
currentUrl?: string;
|
|
252
252
|
userContext?: Record<string, unknown>;
|
|
253
253
|
nonce?: string;
|
|
254
|
+
enableDebug?: boolean;
|
|
254
255
|
};
|
|
255
256
|
type BotReason = 'none' | 'cf_bot_score' | 'cf_verified_bot' | 'force_bot_param';
|
|
256
257
|
type PersonalizationHeaders = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coframe/sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
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",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"lint:fix!": "biome check --write --unsafe .",
|
|
43
43
|
"deploy:cdn": "./bin/publish.sh",
|
|
44
44
|
"deploy:cdn:prod": "SDK_ENV=prod ./bin/publish.sh",
|
|
45
|
-
"dev": "NODE_ENV=production EXTERNAL_PORT=3004 rollup -c -w"
|
|
45
|
+
"dev": "NODE_ENV=production EXTERNAL_PORT=3004 rollup -c -w",
|
|
46
|
+
"dev:local-edge": "COFRAME_EDGE_URL=http://localhost:8787 NODE_ENV=production EXTERNAL_PORT=3004 rollup -c -w"
|
|
46
47
|
},
|
|
47
48
|
"author": "Coframe",
|
|
48
49
|
"keywords": [
|
|
@@ -130,4 +131,4 @@
|
|
|
130
131
|
"ua-parser-js": "^2.0.10"
|
|
131
132
|
},
|
|
132
133
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
133
|
-
}
|
|
134
|
+
}
|