@coframe/sdk 1.1.9 → 1.1.11
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 +25 -0
- package/dist/pkg/cf.cjs.js +209 -272
- package/dist/pkg/cf.esm.mjs +209 -272
- package/dist/pkg/react.cjs.js +9 -1
- package/dist/pkg/react.esm.mjs +10 -2
- package/dist/pkg/server.cjs.js +11 -4
- package/dist/pkg/server.esm.mjs +11 -4
- package/dist/pkg/types/index.d.ts +4 -1
- package/dist/pkg/types/react.d.ts +5 -2
- package/dist/pkg/types/server.d.ts +2 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -40,6 +40,24 @@ await init({
|
|
|
40
40
|
|
|
41
41
|
`init()` accepts `config` and optional `projectData`, so npm consumers do not need to manually populate `window.CFQ` first.
|
|
42
42
|
|
|
43
|
+
### URL controls
|
|
44
|
+
|
|
45
|
+
The npm entry uses the same browser URL controls as the script-tag and Edge
|
|
46
|
+
installations:
|
|
47
|
+
|
|
48
|
+
- `cf_disable` skips initialization and reveals any antiflicker state.
|
|
49
|
+
- `coframe_variant_id` previews one or more comma-separated variant IDs.
|
|
50
|
+
- `coframe_should_send_events=true` allows events during preview for testing.
|
|
51
|
+
|
|
52
|
+
When `enableDebug: true` is set for the project, open the Launcher with
|
|
53
|
+
`cf_debug=true` and use **Keep across navigation** in the Preview Session
|
|
54
|
+
tool to keep the selected variants active for the current tab.
|
|
55
|
+
|
|
56
|
+
For SSR, pass the complete request URL, including its query string, as
|
|
57
|
+
`currentUrl`. This is required for query-sensitive targeting and allows
|
|
58
|
+
`getProjectData()` to short-circuit before any request when `cf_disable` is
|
|
59
|
+
present.
|
|
60
|
+
|
|
43
61
|
### React
|
|
44
62
|
|
|
45
63
|
```tsx
|
|
@@ -60,6 +78,8 @@ export default function App({ children }) {
|
|
|
60
78
|
```
|
|
61
79
|
|
|
62
80
|
Mount the provider once at the root of your app. It initializes the SDK on mount and keeps treatment state available to child components.
|
|
81
|
+
Keep it mounted across client-side route changes; runtime commands and the SDK's
|
|
82
|
+
navigation observer handle subsequent page changes without remounting it.
|
|
63
83
|
|
|
64
84
|
### Server prefetch
|
|
65
85
|
|
|
@@ -148,6 +168,10 @@ import { CoframeProvider } from '@coframe/sdk/react'
|
|
|
148
168
|
</CoframeProvider>
|
|
149
169
|
```
|
|
150
170
|
|
|
171
|
+
If your privacy policy permits treatment analytics before cookie consent, use
|
|
172
|
+
`onTreatmentAppliedBulkV2WithoutConsent` instead. It has the same callback
|
|
173
|
+
arguments and does not fire again when consent is later granted.
|
|
174
|
+
|
|
151
175
|
Outside React, you can also register callbacks from the main entry:
|
|
152
176
|
|
|
153
177
|
```ts
|
|
@@ -159,6 +183,7 @@ import { onLog, onTreatmentApplied } from '@coframe/sdk'
|
|
|
159
183
|
- `getProjectData()` is intended for SSR and edge runtimes that can provide a full request URL and request headers.
|
|
160
184
|
- In Pages Router or Remix, per-request server loaders are the most straightforward place to call it.
|
|
161
185
|
- In App Router, keep the provider mounted in a persistent shell, but be careful about request URL handling in `layout.tsx` if you need query-sensitive SSR targeting.
|
|
186
|
+
- The npm runtime reveals antiflicker styles but cannot insert them early enough to protect the initial paint. Render `antiflickerCSS` from `getProjectData()` in the server document head, or place equivalent static CSS and a timeout fallback there before application content.
|
|
162
187
|
|
|
163
188
|
## Package layout
|
|
164
189
|
|
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;
|
|
@@ -1823,6 +1911,7 @@ const waitForBody = (timeout = 10000) => {
|
|
|
1823
1911
|
function revealPage() {
|
|
1824
1912
|
document.dispatchEvent(new CustomEvent('coframe:show', {}));
|
|
1825
1913
|
document.documentElement.style.setProperty('--cf-show', '1');
|
|
1914
|
+
document.documentElement.style.setProperty('--cf-visibility', 'visible');
|
|
1826
1915
|
}
|
|
1827
1916
|
async function replaceVariants(variants, orgId, logger, nonce) {
|
|
1828
1917
|
const failedReplacements = [];
|
|
@@ -2215,44 +2304,8 @@ function debounce(func, wait) {
|
|
|
2215
2304
|
}, wait);
|
|
2216
2305
|
};
|
|
2217
2306
|
}
|
|
2218
|
-
function parseQueryString(query) {
|
|
2219
|
-
const params = {};
|
|
2220
|
-
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
|
|
2221
|
-
if (!cleanQuery)
|
|
2222
|
-
return params;
|
|
2223
|
-
for (const pair of cleanQuery.split('&')) {
|
|
2224
|
-
if (!pair)
|
|
2225
|
-
continue;
|
|
2226
|
-
const [key, value = ''] = pair.split('=').map(decodeURIComponent);
|
|
2227
|
-
if (!(key in params)) {
|
|
2228
|
-
params[key] = value;
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
return params;
|
|
2232
|
-
}
|
|
2233
|
-
function createURLSearchParams(query) {
|
|
2234
|
-
if (typeof URLSearchParams !== 'undefined') {
|
|
2235
|
-
const nativeParams = new URLSearchParams(query);
|
|
2236
|
-
return {
|
|
2237
|
-
get: (key) => nativeParams.get(key),
|
|
2238
|
-
};
|
|
2239
|
-
}
|
|
2240
|
-
const simpleParams = parseQueryString(query);
|
|
2241
|
-
return {
|
|
2242
|
-
get: (key) => { var _a; return (_a = simpleParams[key]) !== null && _a !== void 0 ? _a : null; },
|
|
2243
|
-
};
|
|
2244
|
-
}
|
|
2245
2307
|
function getCurrentUrlSearchParams() {
|
|
2246
|
-
|
|
2247
|
-
const searchIndex = url.indexOf('?');
|
|
2248
|
-
if (searchIndex === -1) {
|
|
2249
|
-
return createURLSearchParams('');
|
|
2250
|
-
}
|
|
2251
|
-
const hashIndex = url.indexOf('#', searchIndex);
|
|
2252
|
-
const search = hashIndex !== -1
|
|
2253
|
-
? url.substring(searchIndex, hashIndex)
|
|
2254
|
-
: url.substring(searchIndex);
|
|
2255
|
-
return createURLSearchParams(search);
|
|
2308
|
+
return getUrlSearchParams(getCurrentPageUrl());
|
|
2256
2309
|
}
|
|
2257
2310
|
|
|
2258
2311
|
const defaultScale = 100000;
|
|
@@ -2487,6 +2540,44 @@ async function getVariantsAndElements(personalizationHeaders, project, previewVa
|
|
|
2487
2540
|
});
|
|
2488
2541
|
}
|
|
2489
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
|
+
|
|
2490
2581
|
function buildSessionContext(isInitialApply) {
|
|
2491
2582
|
const context = {};
|
|
2492
2583
|
const viewport = getScreenSize();
|
|
@@ -2640,220 +2731,6 @@ function rebindMetrics(coframe, logger, metrics, nonce) {
|
|
|
2640
2731
|
}
|
|
2641
2732
|
}
|
|
2642
2733
|
|
|
2643
|
-
function getActivePreviewModes(sessionPreview) {
|
|
2644
|
-
if (!sessionPreview.isSessionPreviewActive ||
|
|
2645
|
-
!sessionPreview.previewVariantId) {
|
|
2646
|
-
return [];
|
|
2647
|
-
}
|
|
2648
|
-
return [
|
|
2649
|
-
{
|
|
2650
|
-
id: 'session-preview',
|
|
2651
|
-
label: 'Preview on',
|
|
2652
|
-
title: `Session preview is active for ${sessionPreview.previewVariantId}`,
|
|
2653
|
-
variantIds: sessionPreview.previewVariantId
|
|
2654
|
-
.split(',')
|
|
2655
|
-
.map((id) => id.trim())
|
|
2656
|
-
.filter(Boolean),
|
|
2657
|
-
},
|
|
2658
|
-
];
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
const PREVIEW_UI_PROTOCOL_VERSION = 1;
|
|
2662
|
-
|
|
2663
|
-
const SESSION_VARIANT_KEY = 'coframe.preview_variant';
|
|
2664
|
-
const PREVIEW_VARIANT_QUERY_PARAM = 'coframe_variant_id';
|
|
2665
|
-
const KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM = 'keep_preview_for_session';
|
|
2666
|
-
let previewDismissed = false;
|
|
2667
|
-
function dismissSessionPreview() {
|
|
2668
|
-
previewDismissed = true;
|
|
2669
|
-
clearPreviewVariantId();
|
|
2670
|
-
const url = new URL(window.location.href);
|
|
2671
|
-
url.searchParams.delete(PREVIEW_VARIANT_QUERY_PARAM);
|
|
2672
|
-
url.searchParams.delete(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM);
|
|
2673
|
-
window.history.replaceState(window.history.state, '', url);
|
|
2674
|
-
window.location.reload();
|
|
2675
|
-
}
|
|
2676
|
-
function normalizeVariantIds(raw) {
|
|
2677
|
-
if (!raw)
|
|
2678
|
-
return null;
|
|
2679
|
-
const ids = raw
|
|
2680
|
-
.split(',')
|
|
2681
|
-
.map((id) => id.trim())
|
|
2682
|
-
.filter(Boolean);
|
|
2683
|
-
return ids.length > 0 ? ids.join(',') : null;
|
|
2684
|
-
}
|
|
2685
|
-
function readPreviewVariantId() {
|
|
2686
|
-
try {
|
|
2687
|
-
return sessionStorage.getItem(SESSION_VARIANT_KEY);
|
|
2688
|
-
}
|
|
2689
|
-
catch (_a) {
|
|
2690
|
-
return null;
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
function persistPreviewVariantId(variantId) {
|
|
2694
|
-
const normalized = normalizeVariantIds(variantId);
|
|
2695
|
-
if (!normalized)
|
|
2696
|
-
return;
|
|
2697
|
-
try {
|
|
2698
|
-
sessionStorage.setItem(SESSION_VARIANT_KEY, normalized);
|
|
2699
|
-
}
|
|
2700
|
-
catch (_a) {
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
function clearPreviewVariantId() {
|
|
2704
|
-
try {
|
|
2705
|
-
sessionStorage.removeItem(SESSION_VARIANT_KEY);
|
|
2706
|
-
}
|
|
2707
|
-
catch (_a) {
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
function resolvePreviewSource(urlParams = getCurrentUrlSearchParams()) {
|
|
2711
|
-
return {
|
|
2712
|
-
urlVariantId: urlParams.get(PREVIEW_VARIANT_QUERY_PARAM),
|
|
2713
|
-
storedVariantId: readPreviewVariantId(),
|
|
2714
|
-
keepForSession: urlParams.get(KEEP_PREVIEW_FOR_SESSION_QUERY_PARAM) === 'true',
|
|
2715
|
-
};
|
|
2716
|
-
}
|
|
2717
|
-
function resolveSessionPreviewState() {
|
|
2718
|
-
if (previewDismissed) {
|
|
2719
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2720
|
-
}
|
|
2721
|
-
const previewSource = resolvePreviewSource();
|
|
2722
|
-
const urlVariantId = normalizeVariantIds(previewSource.urlVariantId);
|
|
2723
|
-
if (urlVariantId && previewSource.keepForSession) {
|
|
2724
|
-
return {
|
|
2725
|
-
isSessionPreviewActive: true,
|
|
2726
|
-
previewVariantId: urlVariantId,
|
|
2727
|
-
};
|
|
2728
|
-
}
|
|
2729
|
-
if (urlVariantId) {
|
|
2730
|
-
return { isSessionPreviewActive: false, previewVariantId: null };
|
|
2731
|
-
}
|
|
2732
|
-
const storedVariantId = normalizeVariantIds(previewSource.storedVariantId);
|
|
2733
|
-
return {
|
|
2734
|
-
isSessionPreviewActive: !!storedVariantId,
|
|
2735
|
-
previewVariantId: storedVariantId,
|
|
2736
|
-
};
|
|
2737
|
-
}
|
|
2738
|
-
|
|
2739
|
-
const PREVIEW_UI_SCRIPT_ID = 'coframe-preview-ui-script';
|
|
2740
|
-
const PREVIEW_UI_URL = "https://edge.cofra.me/preview-ui/latest.js";
|
|
2741
|
-
let loadPromise = null;
|
|
2742
|
-
let latestState = { modes: [] };
|
|
2743
|
-
let nonce;
|
|
2744
|
-
let logger;
|
|
2745
|
-
let mounted = false;
|
|
2746
|
-
function reportError(message, error) {
|
|
2747
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2748
|
-
if (logger) {
|
|
2749
|
-
logger.warn(message, 'preview_ui_unavailable', { error: errorMessage });
|
|
2750
|
-
return;
|
|
2751
|
-
}
|
|
2752
|
-
console.warn(`[Coframe] ${message}`, error);
|
|
2753
|
-
}
|
|
2754
|
-
function getPreviewUI() {
|
|
2755
|
-
const api = window.CoframePreviewUI;
|
|
2756
|
-
return (api === null || api === void 0 ? void 0 : api.protocolVersion) === PREVIEW_UI_PROTOCOL_VERSION ? api : undefined;
|
|
2757
|
-
}
|
|
2758
|
-
function loadPreviewUI() {
|
|
2759
|
-
const existing = getPreviewUI();
|
|
2760
|
-
if (existing)
|
|
2761
|
-
return Promise.resolve(existing);
|
|
2762
|
-
if (loadPromise)
|
|
2763
|
-
return loadPromise;
|
|
2764
|
-
loadPromise = new Promise((resolve, reject) => {
|
|
2765
|
-
const script = document.createElement('script');
|
|
2766
|
-
script.id = PREVIEW_UI_SCRIPT_ID;
|
|
2767
|
-
script.async = true;
|
|
2768
|
-
script.src = PREVIEW_UI_URL;
|
|
2769
|
-
if (nonce)
|
|
2770
|
-
script.nonce = nonce;
|
|
2771
|
-
script.onload = () => {
|
|
2772
|
-
const api = getPreviewUI();
|
|
2773
|
-
if (!api) {
|
|
2774
|
-
reject(new Error('Preview UI protocol is missing or incompatible'));
|
|
2775
|
-
return;
|
|
2776
|
-
}
|
|
2777
|
-
resolve(api);
|
|
2778
|
-
};
|
|
2779
|
-
script.onerror = () => {
|
|
2780
|
-
reject(new Error('Preview UI script failed to load'));
|
|
2781
|
-
};
|
|
2782
|
-
document.head.appendChild(script);
|
|
2783
|
-
});
|
|
2784
|
-
return loadPromise;
|
|
2785
|
-
}
|
|
2786
|
-
function renderPreviewUI(api) {
|
|
2787
|
-
try {
|
|
2788
|
-
if (latestState.modes.length === 0) {
|
|
2789
|
-
api.unmount();
|
|
2790
|
-
mounted = false;
|
|
2791
|
-
}
|
|
2792
|
-
else if (mounted) {
|
|
2793
|
-
api.update(latestState);
|
|
2794
|
-
}
|
|
2795
|
-
else {
|
|
2796
|
-
api.mount({ state: latestState, nonce, onDismiss: dismissPreviewPanel });
|
|
2797
|
-
mounted = true;
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2800
|
-
catch (error) {
|
|
2801
|
-
reportError('Preview UI could not be rendered', error);
|
|
2802
|
-
}
|
|
2803
|
-
}
|
|
2804
|
-
function dismissPreviewPanel() {
|
|
2805
|
-
dismissSessionPreview();
|
|
2806
|
-
latestState = { modes: [] };
|
|
2807
|
-
const api = getPreviewUI();
|
|
2808
|
-
if (api)
|
|
2809
|
-
renderPreviewUI(api);
|
|
2810
|
-
}
|
|
2811
|
-
function modesWithVariants(sessionPreview, variantsAndElements, applied = true) {
|
|
2812
|
-
const activeModes = getActivePreviewModes(sessionPreview);
|
|
2813
|
-
if (activeModes.length === 0)
|
|
2814
|
-
return [];
|
|
2815
|
-
const currentPageVariantIds = new Set((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => variant.id)) || []);
|
|
2816
|
-
const slugById = new Map((variantsAndElements === null || variantsAndElements === void 0 ? void 0 : variantsAndElements.map(({ variant }) => [variant.id, variant.slug])) || []);
|
|
2817
|
-
return activeModes.map((mode) => ({
|
|
2818
|
-
id: mode.id,
|
|
2819
|
-
label: mode.label,
|
|
2820
|
-
title: mode.title,
|
|
2821
|
-
variants: mode.variantIds.map((id) => ({
|
|
2822
|
-
id,
|
|
2823
|
-
slug: slugById.get(id) || null,
|
|
2824
|
-
status: variantsAndElements
|
|
2825
|
-
? applied && currentPageVariantIds.has(id)
|
|
2826
|
-
? 'active'
|
|
2827
|
-
: 'inactive'
|
|
2828
|
-
: 'pending',
|
|
2829
|
-
})),
|
|
2830
|
-
}));
|
|
2831
|
-
}
|
|
2832
|
-
function resolveSessionPreviewPanel(sessionPreview, previewNonce, previewLogger) {
|
|
2833
|
-
const modes = modesWithVariants(sessionPreview);
|
|
2834
|
-
if (modes.length === 0)
|
|
2835
|
-
return;
|
|
2836
|
-
nonce = previewNonce;
|
|
2837
|
-
logger = previewLogger;
|
|
2838
|
-
latestState = { modes };
|
|
2839
|
-
const existing = getPreviewUI();
|
|
2840
|
-
if (existing) {
|
|
2841
|
-
renderPreviewUI(existing);
|
|
2842
|
-
return;
|
|
2843
|
-
}
|
|
2844
|
-
void loadPreviewUI()
|
|
2845
|
-
.then(renderPreviewUI)
|
|
2846
|
-
.catch((error) => reportError('Preview UI could not be loaded', error));
|
|
2847
|
-
}
|
|
2848
|
-
function updatePreviewPanel(variantsAndElements, sessionPreview, applied = true) {
|
|
2849
|
-
latestState = {
|
|
2850
|
-
modes: modesWithVariants(sessionPreview, variantsAndElements, applied),
|
|
2851
|
-
};
|
|
2852
|
-
const api = getPreviewUI();
|
|
2853
|
-
if (api)
|
|
2854
|
-
renderPreviewUI(api);
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
2734
|
let lastUrl = null;
|
|
2858
2735
|
function observeUrlChanges(baseUrlMatch, onUrlChange) {
|
|
2859
2736
|
const tryTrack = () => {
|
|
@@ -3208,21 +3085,23 @@ function setupTracking(Coframe, projectResult, previewVariantId, executionStartT
|
|
|
3208
3085
|
}
|
|
3209
3086
|
|
|
3210
3087
|
function resolvePreview(pageUrl, previewVariantId, isSessionVariant) {
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3088
|
+
const previewSource = resolvePreviewSource(pageUrl);
|
|
3089
|
+
if (previewSource.urlVariantId) {
|
|
3090
|
+
return {
|
|
3091
|
+
previewVariantId: previewSource.urlVariantId,
|
|
3092
|
+
isSessionVariant: previewSource.isKeptForSession,
|
|
3093
|
+
};
|
|
3217
3094
|
}
|
|
3218
|
-
if (
|
|
3219
|
-
return {
|
|
3095
|
+
if (previewSource.storedVariantId) {
|
|
3096
|
+
return {
|
|
3097
|
+
previewVariantId: previewSource.storedVariantId,
|
|
3098
|
+
isSessionVariant: true,
|
|
3099
|
+
};
|
|
3220
3100
|
}
|
|
3221
3101
|
if (previewVariantId) {
|
|
3222
3102
|
return { previewVariantId, isSessionVariant };
|
|
3223
3103
|
}
|
|
3224
|
-
|
|
3225
|
-
return { previewVariantId: stored, isSessionVariant: !!stored };
|
|
3104
|
+
return { previewVariantId: null, isSessionVariant: false };
|
|
3226
3105
|
}
|
|
3227
3106
|
async function awaitHydration(config, Coframe, project) {
|
|
3228
3107
|
var _a, _b;
|
|
@@ -3355,8 +3234,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3355
3234
|
Coframe.experimentInfo = experimentsInfo;
|
|
3356
3235
|
Coframe.variantsAndElements = variantsAndElements;
|
|
3357
3236
|
setFrameActivated(hasActivatedFrames);
|
|
3358
|
-
const sessionPreview = resolveSessionPreviewState();
|
|
3359
|
-
updatePreviewPanel(variantsAndElements, sessionPreview, !config.trackingOnly);
|
|
3360
3237
|
if (Coframe.debug) {
|
|
3361
3238
|
Coframe.debug.project = pageProject;
|
|
3362
3239
|
Coframe.debug.frames = pageProject.frames;
|
|
@@ -3376,11 +3253,6 @@ function createApplyVariants(Coframe, config, previewVariantId, isSessionVariant
|
|
|
3376
3253
|
async function modifyPage(Coframe, configResult, projectResult, analytics) {
|
|
3377
3254
|
const { config, timeoutMs, previewVariantId, isSessionVariant, integrationStartTime, executionStartTime, } = configResult;
|
|
3378
3255
|
const { project: cdnProject, personalizationHeaders } = projectResult;
|
|
3379
|
-
if (resolveSessionPreviewState().isSessionPreviewActive) {
|
|
3380
|
-
waitForBody(timeoutMs)
|
|
3381
|
-
.then(() => resolveSessionPreviewPanel(resolveSessionPreviewState(), config.nonce, Coframe.logger))
|
|
3382
|
-
.catch(() => { });
|
|
3383
|
-
}
|
|
3384
3256
|
if (!(await awaitHydration(config, Coframe, cdnProject)))
|
|
3385
3257
|
return;
|
|
3386
3258
|
if (cdnProject.tags) {
|
|
@@ -3473,15 +3345,13 @@ async function resolveConfig(Coframe, revealPage) {
|
|
|
3473
3345
|
}
|
|
3474
3346
|
Coframe.projectId = projectId;
|
|
3475
3347
|
const urlParams = getCurrentUrlSearchParams();
|
|
3476
|
-
const previewSource = resolvePreviewSource(
|
|
3348
|
+
const previewSource = resolvePreviewSource();
|
|
3477
3349
|
const shouldSendEventsParam = urlParams.get('coframe_should_send_events');
|
|
3478
3350
|
let previewVariantId = null;
|
|
3479
3351
|
let isSessionVariant = false;
|
|
3480
3352
|
if (previewSource.urlVariantId) {
|
|
3481
3353
|
previewVariantId = previewSource.urlVariantId;
|
|
3482
|
-
|
|
3483
|
-
persistPreviewVariantId(previewSource.urlVariantId);
|
|
3484
|
-
}
|
|
3354
|
+
isSessionVariant = previewSource.isKeptForSession;
|
|
3485
3355
|
}
|
|
3486
3356
|
else if (previewSource.storedVariantId) {
|
|
3487
3357
|
previewVariantId = previewSource.storedVariantId;
|
|
@@ -3581,6 +3451,66 @@ async function resolveProject(Coframe, previewVariantId, isSessionVariant, confi
|
|
|
3581
3451
|
};
|
|
3582
3452
|
}
|
|
3583
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, 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
|
+
|
|
3584
3514
|
function injectOptions(options) {
|
|
3585
3515
|
var _a;
|
|
3586
3516
|
if (options.config) {
|
|
@@ -3646,6 +3576,12 @@ const Coframe = {
|
|
|
3646
3576
|
};
|
|
3647
3577
|
Coframe.logger = createLogger(() => Coframe);
|
|
3648
3578
|
async function init(options) {
|
|
3579
|
+
if (typeof window !== 'undefined' &&
|
|
3580
|
+
/cf_disable/.test(window.location.search)) {
|
|
3581
|
+
console.warn('[Coframe] Disabled via URL parameter');
|
|
3582
|
+
revealPage();
|
|
3583
|
+
return;
|
|
3584
|
+
}
|
|
3649
3585
|
if (options)
|
|
3650
3586
|
injectOptions(options);
|
|
3651
3587
|
const gracefulExit = (msg) => {
|
|
@@ -3656,6 +3592,7 @@ async function init(options) {
|
|
|
3656
3592
|
const configResult = await resolveConfig(Coframe, revealPage);
|
|
3657
3593
|
if (!configResult)
|
|
3658
3594
|
return;
|
|
3595
|
+
loadLauncherIfEnabled(configResult.config.enableDebug === true, configResult.config.nonce);
|
|
3659
3596
|
const projectResult = await resolveProject(Coframe, configResult.previewVariantId, configResult.isSessionVariant, configResult.config);
|
|
3660
3597
|
if (!projectResult)
|
|
3661
3598
|
return;
|