@coframe/sdk 1.1.55 → 1.1.59

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 CHANGED
@@ -86,10 +86,11 @@ installations:
86
86
  production visitor would get.
87
87
  - `coframe_should_send_events=true` allows events during preview for testing.
88
88
 
89
- Debug tools are allowed by default. Open the Launcher with `cf_preview=true` and
90
- use **Keep across navigation** in the Preview Session tool to keep the selected
91
- variants active for the current tab. Set `enableDebug: false` to prevent URL or
92
- saved session state from loading the Launcher.
89
+ Debug tools are allowed by default. A preview link (`coframe_variant_id` with
90
+ `cf_preview=true`) keeps the selected variants active for the current tab as you
91
+ navigate; turn off **Keep across navigation** in the Preview Session tool to
92
+ stop. Set `enableDebug: false` to prevent URL or saved session state from
93
+ loading the Launcher (a preview link then applies to its own page only).
93
94
 
94
95
  For SSR, pass the complete request URL, including its query string, as
95
96
  `currentUrl`. This is required for query-sensitive targeting and allows
@@ -480,6 +480,10 @@ const getSelectorWithoutCache = (element, options) => {
480
480
  }
481
481
  return null;
482
482
  }
483
+ const ownId = (el) => {
484
+ const id = el.getAttribute('id');
485
+ return id && !isGeneratedIdentifier(id) ? id : undefined;
486
+ };
483
487
  const getParentNode = (element) => {
484
488
  return element.parentElement || getShadowHost(element);
485
489
  };
@@ -487,7 +491,7 @@ const getSelectorWithoutCache = (element, options) => {
487
491
  var _a;
488
492
  const selector = {
489
493
  t: el.tagName.toLowerCase(),
490
- id: el.getAttribute('id') || undefined,
494
+ id: ownId(el),
491
495
  text: getInnerText(el) || undefined,
492
496
  href: el.getAttribute('href') || undefined,
493
497
  dataTestId: el.getAttribute('data-test-id') || undefined,
@@ -572,12 +576,33 @@ const getElement = (selector, options) => {
572
576
  return null;
573
577
  };
574
578
  const resolvedPathCache = new Map();
579
+ const isGeneratedIdentifier = (value) => {
580
+ const token = value.trim();
581
+ if (!token)
582
+ return false;
583
+ const frameworkId = /^(?::r[0-9a-z]+:|«r[0-9a-z]+»|radix-:?r?[0-9a-z]+:?.*|headlessui-[a-z-]+-:?r?[0-9a-z]+:?|react-aria\d+.*|mui-\d+|ember\d+)$/;
584
+ const cssInJsClass = /^(?:css-[0-9a-z]{5,}(?:-[A-Za-z0-9]+)?|sc-[A-Za-z]{5,}|jsx-\d{6,}|[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9]+__[A-Za-z0-9_-]{5})$/;
585
+ if (frameworkId.test(token) || cssInJsClass.test(token))
586
+ return true;
587
+ return token.split(/[-_:.]+/).some((segment) => {
588
+ var _a, _b;
589
+ if (/^\d{8,}$/.test(segment))
590
+ return true;
591
+ if (/^[0-9a-f]{6,}$/.test(segment) &&
592
+ /\d/.test(segment) &&
593
+ /[a-f]/.test(segment))
594
+ return true;
595
+ return (/^[0-9a-zA-Z]{8,}$/.test(segment) &&
596
+ ((_b = (_a = segment.match(/\d+|[a-zA-Z]+/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) >= 5);
597
+ });
598
+ };
599
+ const stableId = (id) => id && !isGeneratedIdentifier(id) ? id : undefined;
575
600
  const selectorKey = (selector) => selector.fingerprint || JSON.stringify(selector);
576
601
  const keepsIdentity = (document, element, selector) => {
577
602
  if (element.tagName.toLowerCase() !== selector.t.toLowerCase())
578
603
  return false;
579
604
  const attrs = [
580
- ['id', selector.id],
605
+ ['id', stableId(selector.id)],
581
606
  ['data-test-id', selector.dataTestId],
582
607
  ['aria-label', selector.ariaLabel],
583
608
  ];
@@ -843,7 +868,7 @@ const generateRelativePaths = (sel, max) => {
843
868
  const uniqueQueries = [];
844
869
  const nonUniqQueries = [];
845
870
  const newWipPaths = [];
846
- if (currentSel.id) {
871
+ if (currentSel.id && !isGeneratedIdentifier(currentSel.id)) {
847
872
  uniqueQueries.push({
848
873
  xpath: `[@id="${currentSel.id}"]`,
849
874
  css: `#${currentSel.id}`,
@@ -2479,6 +2504,16 @@ function readStoredPreviewVariantId() {
2479
2504
  return null;
2480
2505
  }
2481
2506
  }
2507
+ function persistPreviewVariantId(variantId) {
2508
+ const normalized = normalizePreviewVariantId(variantId);
2509
+ if (!normalized)
2510
+ return;
2511
+ try {
2512
+ window.sessionStorage.setItem(COFRAME_PREVIEW_SESSION_STORAGE_KEY, normalized);
2513
+ }
2514
+ catch (_a) {
2515
+ }
2516
+ }
2482
2517
  function resolvePreviewSource(url = window.location.href) {
2483
2518
  const urlVariantId = readPreviewVariantIdFromUrl(url);
2484
2519
  const storedVariantId = readStoredPreviewVariantId();
@@ -2496,6 +2531,7 @@ function isElementVisible(el) {
2496
2531
  }
2497
2532
  const VALUE_UNKNOWN_YET = 'n/a';
2498
2533
  function checkActivationCondition(condition, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger, options) {
2534
+ var _a;
2499
2535
  if (condition.and) {
2500
2536
  const results = condition.and.map((c) => checkActivationCondition(c, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger));
2501
2537
  const hasUnknown = results.some((r) => r === VALUE_UNKNOWN_YET);
@@ -2602,12 +2638,13 @@ function checkActivationCondition(condition, personalizationHeaders, currentUrl,
2602
2638
  }
2603
2639
  }
2604
2640
  }
2605
- if (condition.device) {
2606
- const isMobileDevice = personalizationHeaders['x-is-mobile-viewer'] === 'true';
2607
- if (condition.device.includes('mobile') && !isMobileDevice) {
2608
- return false;
2641
+ if ((_a = condition.device) === null || _a === void 0 ? void 0 : _a.length) {
2642
+ let device = personalizationHeaders['x-device-type'];
2643
+ if (!['desktop', 'mobile', 'tablet'].includes(device)) {
2644
+ device =
2645
+ personalizationHeaders['x-is-mobile-viewer'] === 'true' ? 'mobile' : '';
2609
2646
  }
2610
- if (condition.device.includes('desktop') && isMobileDevice) {
2647
+ if (!condition.device.includes(device)) {
2611
2648
  return false;
2612
2649
  }
2613
2650
  }
@@ -5338,6 +5375,10 @@ async function resolveConfig(Coframe, revealActions) {
5338
5375
  if (previewSource.urlVariantId) {
5339
5376
  previewVariantId = previewSource.urlVariantId;
5340
5377
  isSessionVariant = previewSource.isKeptForSession;
5378
+ if (urlParams.get(COFRAME_PREVIEW_QUERY_PARAM) === 'true' &&
5379
+ config.enableDebug !== false) {
5380
+ persistPreviewVariantId(previewSource.urlVariantId);
5381
+ }
5341
5382
  }
5342
5383
  else if (previewSource.storedVariantId) {
5343
5384
  previewVariantId = previewSource.storedVariantId;
@@ -478,6 +478,10 @@ const getSelectorWithoutCache = (element, options) => {
478
478
  }
479
479
  return null;
480
480
  }
481
+ const ownId = (el) => {
482
+ const id = el.getAttribute('id');
483
+ return id && !isGeneratedIdentifier(id) ? id : undefined;
484
+ };
481
485
  const getParentNode = (element) => {
482
486
  return element.parentElement || getShadowHost(element);
483
487
  };
@@ -485,7 +489,7 @@ const getSelectorWithoutCache = (element, options) => {
485
489
  var _a;
486
490
  const selector = {
487
491
  t: el.tagName.toLowerCase(),
488
- id: el.getAttribute('id') || undefined,
492
+ id: ownId(el),
489
493
  text: getInnerText(el) || undefined,
490
494
  href: el.getAttribute('href') || undefined,
491
495
  dataTestId: el.getAttribute('data-test-id') || undefined,
@@ -570,12 +574,33 @@ const getElement = (selector, options) => {
570
574
  return null;
571
575
  };
572
576
  const resolvedPathCache = new Map();
577
+ const isGeneratedIdentifier = (value) => {
578
+ const token = value.trim();
579
+ if (!token)
580
+ return false;
581
+ const frameworkId = /^(?::r[0-9a-z]+:|«r[0-9a-z]+»|radix-:?r?[0-9a-z]+:?.*|headlessui-[a-z-]+-:?r?[0-9a-z]+:?|react-aria\d+.*|mui-\d+|ember\d+)$/;
582
+ const cssInJsClass = /^(?:css-[0-9a-z]{5,}(?:-[A-Za-z0-9]+)?|sc-[A-Za-z]{5,}|jsx-\d{6,}|[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9]+__[A-Za-z0-9_-]{5})$/;
583
+ if (frameworkId.test(token) || cssInJsClass.test(token))
584
+ return true;
585
+ return token.split(/[-_:.]+/).some((segment) => {
586
+ var _a, _b;
587
+ if (/^\d{8,}$/.test(segment))
588
+ return true;
589
+ if (/^[0-9a-f]{6,}$/.test(segment) &&
590
+ /\d/.test(segment) &&
591
+ /[a-f]/.test(segment))
592
+ return true;
593
+ return (/^[0-9a-zA-Z]{8,}$/.test(segment) &&
594
+ ((_b = (_a = segment.match(/\d+|[a-zA-Z]+/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) >= 5);
595
+ });
596
+ };
597
+ const stableId = (id) => id && !isGeneratedIdentifier(id) ? id : undefined;
573
598
  const selectorKey = (selector) => selector.fingerprint || JSON.stringify(selector);
574
599
  const keepsIdentity = (document, element, selector) => {
575
600
  if (element.tagName.toLowerCase() !== selector.t.toLowerCase())
576
601
  return false;
577
602
  const attrs = [
578
- ['id', selector.id],
603
+ ['id', stableId(selector.id)],
579
604
  ['data-test-id', selector.dataTestId],
580
605
  ['aria-label', selector.ariaLabel],
581
606
  ];
@@ -841,7 +866,7 @@ const generateRelativePaths = (sel, max) => {
841
866
  const uniqueQueries = [];
842
867
  const nonUniqQueries = [];
843
868
  const newWipPaths = [];
844
- if (currentSel.id) {
869
+ if (currentSel.id && !isGeneratedIdentifier(currentSel.id)) {
845
870
  uniqueQueries.push({
846
871
  xpath: `[@id="${currentSel.id}"]`,
847
872
  css: `#${currentSel.id}`,
@@ -2477,6 +2502,16 @@ function readStoredPreviewVariantId() {
2477
2502
  return null;
2478
2503
  }
2479
2504
  }
2505
+ function persistPreviewVariantId(variantId) {
2506
+ const normalized = normalizePreviewVariantId(variantId);
2507
+ if (!normalized)
2508
+ return;
2509
+ try {
2510
+ window.sessionStorage.setItem(COFRAME_PREVIEW_SESSION_STORAGE_KEY, normalized);
2511
+ }
2512
+ catch (_a) {
2513
+ }
2514
+ }
2480
2515
  function resolvePreviewSource(url = window.location.href) {
2481
2516
  const urlVariantId = readPreviewVariantIdFromUrl(url);
2482
2517
  const storedVariantId = readStoredPreviewVariantId();
@@ -2494,6 +2529,7 @@ function isElementVisible(el) {
2494
2529
  }
2495
2530
  const VALUE_UNKNOWN_YET = 'n/a';
2496
2531
  function checkActivationCondition(condition, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger, options) {
2532
+ var _a;
2497
2533
  if (condition.and) {
2498
2534
  const results = condition.and.map((c) => checkActivationCondition(c, personalizationHeaders, currentUrl, languages, browserPlatform, userToken, userVisitCount, runJsFunction, logger));
2499
2535
  const hasUnknown = results.some((r) => r === VALUE_UNKNOWN_YET);
@@ -2600,12 +2636,13 @@ function checkActivationCondition(condition, personalizationHeaders, currentUrl,
2600
2636
  }
2601
2637
  }
2602
2638
  }
2603
- if (condition.device) {
2604
- const isMobileDevice = personalizationHeaders['x-is-mobile-viewer'] === 'true';
2605
- if (condition.device.includes('mobile') && !isMobileDevice) {
2606
- return false;
2639
+ if ((_a = condition.device) === null || _a === void 0 ? void 0 : _a.length) {
2640
+ let device = personalizationHeaders['x-device-type'];
2641
+ if (!['desktop', 'mobile', 'tablet'].includes(device)) {
2642
+ device =
2643
+ personalizationHeaders['x-is-mobile-viewer'] === 'true' ? 'mobile' : '';
2607
2644
  }
2608
- if (condition.device.includes('desktop') && isMobileDevice) {
2645
+ if (!condition.device.includes(device)) {
2609
2646
  return false;
2610
2647
  }
2611
2648
  }
@@ -5336,6 +5373,10 @@ async function resolveConfig(Coframe, revealActions) {
5336
5373
  if (previewSource.urlVariantId) {
5337
5374
  previewVariantId = previewSource.urlVariantId;
5338
5375
  isSessionVariant = previewSource.isKeptForSession;
5376
+ if (urlParams.get(COFRAME_PREVIEW_QUERY_PARAM) === 'true' &&
5377
+ config.enableDebug !== false) {
5378
+ persistPreviewVariantId(previewSource.urlVariantId);
5379
+ }
5339
5380
  }
5340
5381
  else if (previewSource.storedVariantId) {
5341
5382
  previewVariantId = previewSource.storedVariantId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coframe/sdk",
3
- "version": "1.1.55",
3
+ "version": "1.1.59",
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",