codeplay-common 4.5.5 → 4.5.7

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.
@@ -194,12 +194,16 @@ function applyFlowchartStoreApi(storeId) {
194
194
  if (String(_appUniqueId) !== String(FLOWCHART_API_APP_ID) || !flowchartSharedOriginal) return false;
195
195
  const apiBase = String(storeId) === '1' ? FLOWCHART_PLAY_API_BASE : FLOWCHART_OTHER_STORE_API_BASE;
196
196
  const accessHash = String(storeId) === '1' ? '' : FLOWCHART_OTHER_STORE_API_HASH;
197
- const updated = flowchartSharedOriginal
198
- .replace(/const FLOWCHART_API_BASE = "[^"]*";/, `const FLOWCHART_API_BASE = "${apiBase}";`)
199
- .replace(/const FLOWCHART_API_ACCESS_HASH = "[^"]*";/, `const FLOWCHART_API_ACCESS_HASH = "${accessHash}";`);
200
- if (updated === flowchartSharedOriginal) {
197
+ const apiBaseMarker = /const FLOWCHART_API_BASE = "[^"]*";/;
198
+ const accessHashMarker = /const FLOWCHART_API_ACCESS_HASH = "[^"]*";/;
199
+
200
+ if (!apiBaseMarker.test(flowchartSharedOriginal) || !accessHashMarker.test(flowchartSharedOriginal)) {
201
201
  throw new Error(`Flowchart API configuration markers were not found in ${FLOWCHART_SHARED_PATH}.`);
202
202
  }
203
+
204
+ const updated = flowchartSharedOriginal
205
+ .replace(apiBaseMarker, `const FLOWCHART_API_BASE = "${apiBase}";`)
206
+ .replace(accessHashMarker, `const FLOWCHART_API_ACCESS_HASH = "${accessHash}";`);
203
207
  writeFileSync(FLOWCHART_SHARED_PATH, updated, 'utf8');
204
208
  console.log(`✅ App 228 API endpoint configured for Store ID ${storeId}.`);
205
209
  return true;
@@ -590,13 +590,6 @@ async function getPageDetails(client) {
590
590
  async function capturePageSnapshot(client) {
591
591
  const evaluation = await client.send('Runtime.evaluate', {
592
592
  expression: `(async () => {
593
- const clonedRoot = document.documentElement.cloneNode(true);
594
- const originalElements = [
595
- document.documentElement,
596
- ...document.documentElement.querySelectorAll('*'),
597
- ];
598
- const clonedElements = [clonedRoot, ...clonedRoot.querySelectorAll('*')];
599
-
600
593
  const blobToDataUrl = (blob) => new Promise((resolve, reject) => {
601
594
  const reader = new FileReader();
602
595
  reader.onload = () => resolve(reader.result);
@@ -617,109 +610,147 @@ async function capturePageSnapshot(client) {
617
610
  return canvas.toDataURL('image/png');
618
611
  };
619
612
 
620
- for (let index = 0; index < originalElements.length; index += 1) {
621
- const original = originalElements[index];
622
- const clone = clonedElements[index];
613
+ const serializeDocument = async (sourceDocument) => {
614
+ const clonedRoot = sourceDocument.documentElement.cloneNode(true);
615
+ const originalElements = [
616
+ sourceDocument.documentElement,
617
+ ...sourceDocument.documentElement.querySelectorAll('*'),
618
+ ];
619
+ const clonedElements = [clonedRoot, ...clonedRoot.querySelectorAll('*')];
623
620
 
624
- if (!clone) continue;
621
+ clonedRoot.setAttribute(
622
+ 'data-responsive-window-scroll-y',
623
+ String(sourceDocument.defaultView?.scrollY || 0),
624
+ );
625
625
 
626
- if (original.scrollTop || original.scrollLeft) {
627
- clone.setAttribute('data-responsive-scroll-top', String(original.scrollTop));
628
- clone.setAttribute('data-responsive-scroll-left', String(original.scrollLeft));
629
- }
626
+ for (let index = 0; index < originalElements.length; index += 1) {
627
+ const original = originalElements[index];
628
+ const clone = clonedElements[index];
630
629
 
631
- if (original.classList.contains('swiper-initialized')) {
632
- clone.setAttribute('data-responsive-swiper-width', String(original.clientWidth));
633
- clone.setAttribute('data-responsive-swiper-height', String(original.clientHeight));
634
- }
630
+ if (!clone) continue;
635
631
 
636
- if (original instanceof HTMLInputElement) {
637
- clone.setAttribute('value', original.value);
638
- clone.toggleAttribute('checked', original.checked);
639
- } else if (original instanceof HTMLTextAreaElement) {
640
- clone.textContent = original.value;
641
- } else if (original instanceof HTMLSelectElement) {
642
- for (let optionIndex = 0; optionIndex < original.options.length; optionIndex += 1) {
643
- clone.options[optionIndex].selected = original.options[optionIndex].selected;
644
- }
645
- } else if (original instanceof HTMLCanvasElement) {
646
- try {
647
- clone.style.setProperty(
648
- 'background-image',
649
- 'url("' + original.toDataURL('image/png') + '")',
650
- 'important',
651
- );
652
- clone.style.setProperty('background-position', 'center', 'important');
653
- clone.style.setProperty('background-repeat', 'no-repeat', 'important');
654
- clone.style.setProperty('background-size', '100% 100%', 'important');
655
- } catch {
656
- // Keep the canvas element when its pixels cannot be exported.
632
+ if (original.scrollTop || original.scrollLeft) {
633
+ clone.setAttribute('data-responsive-scroll-top', String(original.scrollTop));
634
+ clone.setAttribute('data-responsive-scroll-left', String(original.scrollLeft));
657
635
  }
658
- } else if (original instanceof HTMLVideoElement && original.readyState >= 2) {
659
- try {
660
- const replacement = document.createElement('img');
661
- replacement.src = imageToDataUrl(original);
662
- replacement.alt = original.getAttribute('aria-label') || '';
663
- replacement.setAttribute('style', original.getAttribute('style') || '');
664
- clone.replaceWith(replacement);
665
- } catch {
666
- clone.setAttribute('poster', original.poster || '');
636
+
637
+ if (original.classList.contains('swiper-initialized')) {
638
+ clone.setAttribute('data-responsive-swiper-width', String(original.clientWidth));
639
+ clone.setAttribute('data-responsive-swiper-height', String(original.clientHeight));
667
640
  }
668
- } else if (original instanceof HTMLImageElement && original.complete) {
669
- try {
670
- clone.setAttribute('src', imageToDataUrl(original) || original.currentSrc || original.src);
671
- clone.removeAttribute('srcset');
672
- } catch {
673
- clone.setAttribute('src', original.currentSrc || original.src);
641
+
642
+ const tagName = original.tagName?.toLowerCase();
643
+
644
+ if (tagName === 'input') {
645
+ clone.setAttribute('value', original.value);
646
+ clone.toggleAttribute('checked', original.checked);
647
+ } else if (tagName === 'textarea') {
648
+ clone.textContent = original.value;
649
+ } else if (tagName === 'select') {
650
+ for (let optionIndex = 0; optionIndex < original.options.length; optionIndex += 1) {
651
+ clone.options[optionIndex].selected = original.options[optionIndex].selected;
652
+ }
653
+ } else if (tagName === 'canvas') {
654
+ try {
655
+ clone.style.setProperty(
656
+ 'background-image',
657
+ 'url("' + original.toDataURL('image/png') + '")',
658
+ 'important',
659
+ );
660
+ clone.style.setProperty('background-position', 'center', 'important');
661
+ clone.style.setProperty('background-repeat', 'no-repeat', 'important');
662
+ clone.style.setProperty('background-size', '100% 100%', 'important');
663
+ } catch {
664
+ // Keep the canvas element when its pixels cannot be exported.
665
+ }
666
+ } else if (tagName === 'video' && original.readyState >= 2) {
667
+ try {
668
+ const replacement = sourceDocument.createElement('img');
669
+ replacement.src = imageToDataUrl(original);
670
+ replacement.alt = original.getAttribute('aria-label') || '';
671
+ replacement.setAttribute('style', original.getAttribute('style') || '');
672
+ clone.replaceWith(replacement);
673
+ } catch {
674
+ clone.setAttribute('poster', original.poster || '');
675
+ }
676
+ } else if (tagName === 'img' && original.complete) {
677
+ try {
678
+ clone.setAttribute('src', imageToDataUrl(original) || original.currentSrc || original.src);
679
+ clone.removeAttribute('srcset');
680
+ } catch {
681
+ clone.setAttribute('src', original.currentSrc || original.src);
682
+ }
683
+ } else if (tagName === 'iframe') {
684
+ try {
685
+ const frameDocument = original.contentDocument;
686
+
687
+ if (frameDocument?.documentElement) {
688
+ const frameHtml = await serializeDocument(frameDocument);
689
+ clone.removeAttribute('src');
690
+ clone.setAttribute('srcdoc', '<!doctype html>' + frameHtml);
691
+ }
692
+ } catch {
693
+ // Cross-origin frames cannot be serialized and keep their original source.
694
+ }
674
695
  }
675
696
  }
676
- }
677
697
 
678
- const stylesheetLinks = [...document.querySelectorAll('link[rel="stylesheet"]')];
679
- const clonedStylesheetLinks = [...clonedRoot.querySelectorAll('link[rel="stylesheet"]')];
698
+ const stylesheetLinks = [...sourceDocument.querySelectorAll('link[rel="stylesheet"]')];
699
+ const clonedStylesheetLinks = [...clonedRoot.querySelectorAll('link[rel="stylesheet"]')];
680
700
 
681
- await Promise.all(stylesheetLinks.map(async (link, index) => {
682
- try {
683
- const response = await fetch(link.href);
684
- if (!response.ok) return;
685
-
686
- const cssText = (await response.text()).replace(
687
- /url\\(\\s*(['"]?)(?!data:|blob:|#)([^'"\\)]+)\\1\\s*\\)/gi,
688
- (match, quote, resourceUrl) => {
689
- try {
690
- return 'url("' + new URL(resourceUrl.trim(), link.href).href + '")';
691
- } catch {
692
- return match;
693
- }
694
- },
695
- );
696
- const style = document.createElement('style');
697
- style.textContent = cssText;
698
- clonedStylesheetLinks[index].replaceWith(style);
699
- } catch {
700
- clonedStylesheetLinks[index].href = link.href;
701
+ await Promise.all(stylesheetLinks.map(async (link, index) => {
702
+ try {
703
+ const response = await fetch(link.href);
704
+ if (!response.ok) return;
705
+
706
+ const cssText = (await response.text()).replace(
707
+ /url\\(\\s*(['"]?)(?!data:|blob:|#)([^'"\\)]+)\\1\\s*\\)/gi,
708
+ (match, quote, resourceUrl) => {
709
+ try {
710
+ return 'url("' + new URL(resourceUrl.trim(), link.href).href + '")';
711
+ } catch {
712
+ return match;
713
+ }
714
+ },
715
+ );
716
+ const style = sourceDocument.createElement('style');
717
+ style.textContent = cssText;
718
+ clonedStylesheetLinks[index].replaceWith(style);
719
+ } catch {
720
+ clonedStylesheetLinks[index].href = link.href;
721
+ }
722
+ }));
723
+
724
+ clonedRoot.querySelectorAll('script, link[rel="modulepreload"]').forEach(
725
+ (element) => element.remove(),
726
+ );
727
+ clonedRoot.querySelectorAll('meta[http-equiv="Content-Security-Policy"]').forEach(
728
+ (element) => element.remove(),
729
+ );
730
+
731
+ const head = clonedRoot.querySelector('head');
732
+
733
+ if (head) {
734
+ const base = sourceDocument.createElement('base');
735
+ base.href = sourceDocument.location.href;
736
+ head.prepend(base);
737
+
738
+ const replayStyle = sourceDocument.createElement('style');
739
+ replayStyle.textContent = [
740
+ '*,*::before,*::after{animation:none!important;transition:none!important;}',
741
+ '*{scrollbar-width:none!important;}',
742
+ '*::-webkit-scrollbar{width:0!important;height:0!important;display:none!important;}',
743
+ ].join('');
744
+ head.append(replayStyle);
701
745
  }
702
- }));
703
746
 
704
- clonedRoot.querySelectorAll('script, link[rel="modulepreload"]').forEach((element) => element.remove());
705
- clonedRoot.querySelectorAll('meta[http-equiv="Content-Security-Policy"]').forEach(
706
- (element) => element.remove(),
707
- );
708
-
709
- const base = document.createElement('base');
710
- base.href = location.href;
711
- clonedRoot.querySelector('head').prepend(base);
747
+ return clonedRoot.outerHTML;
748
+ };
712
749
 
713
- const replayStyle = document.createElement('style');
714
- replayStyle.textContent = [
715
- '*,*::before,*::after{animation-play-state:paused!important;}',
716
- '*{scrollbar-width:none!important;}',
717
- '*::-webkit-scrollbar{width:0!important;height:0!important;display:none!important;}',
718
- ].join('');
719
- clonedRoot.querySelector('head').append(replayStyle);
750
+ const html = await serializeDocument(document);
720
751
 
721
752
  return {
722
- html: '<!doctype html>' + clonedRoot.outerHTML,
753
+ html: '<!doctype html>' + html,
723
754
  scrollY,
724
755
  };
725
756
  })()`,
@@ -876,7 +907,43 @@ async function closeReplayBrowser(browser) {
876
907
  async function waitForReplayLayout(client, snapshot) {
877
908
  const evaluation = await client.send('Runtime.evaluate', {
878
909
  expression: `(async () => {
879
- const assetWait = Promise.all([...document.images].map((image) => {
910
+ const collectDocuments = (rootDocument) => {
911
+ const documents = [rootDocument];
912
+
913
+ rootDocument.querySelectorAll('iframe').forEach((frame) => {
914
+ try {
915
+ if (frame.contentDocument?.documentElement) {
916
+ documents.push(...collectDocuments(frame.contentDocument));
917
+ }
918
+ } catch {
919
+ // Cross-origin frames cannot participate in replay layout restoration.
920
+ }
921
+ });
922
+
923
+ return documents;
924
+ };
925
+
926
+ const frameDeadline = Date.now() + 5000;
927
+
928
+ while (Date.now() < frameDeadline) {
929
+ const frames = [...document.querySelectorAll('iframe')];
930
+ const framesReady = frames.every((frame) => {
931
+ try {
932
+ return frame.contentDocument?.readyState === 'complete';
933
+ } catch {
934
+ return true;
935
+ }
936
+ });
937
+
938
+ if (framesReady) break;
939
+ await new Promise((resolve) => setTimeout(resolve, 50));
940
+ }
941
+
942
+ const replayDocuments = collectDocuments(document);
943
+ const replayImages = replayDocuments.flatMap((replayDocument) => (
944
+ [...replayDocument.images]
945
+ ));
946
+ const assetWait = Promise.all(replayImages.map((image) => {
880
947
  if (image.complete) return Promise.resolve();
881
948
  return new Promise((resolve) => {
882
949
  image.addEventListener('load', resolve, { once: true });
@@ -885,11 +952,18 @@ async function waitForReplayLayout(client, snapshot) {
885
952
  }));
886
953
 
887
954
  await Promise.race([
888
- Promise.all([document.fonts.ready, assetWait]),
955
+ Promise.all([
956
+ ...replayDocuments.map((replayDocument) => replayDocument.fonts.ready),
957
+ assetWait,
958
+ ]),
889
959
  new Promise((resolve) => setTimeout(resolve, 5000)),
890
960
  ]);
891
961
 
892
- document.querySelectorAll('[data-responsive-swiper-width]').forEach((swiper) => {
962
+ const replayElements = (selector) => replayDocuments.flatMap((replayDocument) => (
963
+ [...replayDocument.querySelectorAll(selector)]
964
+ ));
965
+
966
+ replayElements('[data-responsive-swiper-width]').forEach((swiper) => {
893
967
  const wrapper = [...swiper.children].find((element) => (
894
968
  element.classList.contains('swiper-wrapper')
895
969
  ));
@@ -947,10 +1021,17 @@ async function waitForReplayLayout(client, snapshot) {
947
1021
  }
948
1022
  });
949
1023
 
950
- document.querySelectorAll('[data-responsive-scroll-top]').forEach((element) => {
1024
+ replayElements('[data-responsive-scroll-top]').forEach((element) => {
951
1025
  element.scrollTop = Number(element.dataset.responsiveScrollTop);
952
1026
  element.scrollLeft = Number(element.dataset.responsiveScrollLeft);
953
1027
  });
1028
+
1029
+ replayDocuments.forEach((replayDocument) => {
1030
+ replayDocument.defaultView?.scrollTo(
1031
+ 0,
1032
+ Number(replayDocument.documentElement.dataset.responsiveWindowScrollY) || 0,
1033
+ );
1034
+ });
954
1035
  scrollTo(0, ${Number(snapshot.scrollY) || 0});
955
1036
 
956
1037
  await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeplay-common",
3
- "version": "4.5.5",
3
+ "version": "4.5.7",
4
4
  "description": "Common build scripts and files",
5
5
  "scripts": {
6
6
  "postinstall": "node scripts/sync-files.js",