From 121d7d03e6529ebe6a7c3efd094a97923f6c77bc Mon Sep 17 00:00:00 2001 From: hqhq1025 <1506751656@qq.com> Date: Sun, 19 Apr 2026 05:06:30 +0800 Subject: [PATCH 1/2] fix(runtime): replace silent catches in overlay with console.warn diagnostics The 8 catch blocks in the overlay script silently swallowed errors, making it hard to diagnose iframe sandbox issues (postMessage failures, listener attach failures, setInterval failures). Replaced each with console.warn emitting a contextual tag so problems surface in the sandbox iframe DevTools console without affecting overlay behaviour. Signed-off-by: hqhq1025 <1506751656@qq.com> --- packages/runtime/src/overlay.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/runtime/src/overlay.ts b/packages/runtime/src/overlay.ts index 9e3fd424..c51b3a40 100644 --- a/packages/runtime/src/overlay.ts +++ b/packages/runtime/src/overlay.ts @@ -57,7 +57,7 @@ export const OVERLAY_SCRIPT = `(function() { outerHTML: (el.outerHTML || '').slice(0, 800), rect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height } }, '*'); - } catch (_) {} + } catch (err) { console.warn('[overlay] postMessage ELEMENT_SELECTED failed:', err); } } function onError(ev) { try { @@ -72,7 +72,7 @@ export const OVERLAY_SCRIPT = `(function() { stack: ev && ev.error && ev.error.stack ? String(ev.error.stack) : undefined, timestamp: Date.now() }, '*'); - } catch (_) {} + } catch (err) { console.warn('[overlay] postMessage IFRAME_ERROR (error) failed:', err); } } function onRejection(ev) { try { @@ -86,7 +86,7 @@ export const OVERLAY_SCRIPT = `(function() { stack: (reason && reason.stack) ? String(reason.stack) : undefined, timestamp: Date.now() }, '*'); - } catch (_) {} + } catch (err) { console.warn('[overlay] postMessage IFRAME_ERROR (unhandledrejection) failed:', err); } } // Install + reinstall every 200ms. User code may call removeEventListener @@ -100,18 +100,18 @@ export const OVERLAY_SCRIPT = `(function() { function reattach() { for (var i = 0; i < installs.length; i++) { var spec = installs[i]; - try { document.removeEventListener(spec.evt, spec.fn, true); } catch (_) {} - try { document.addEventListener(spec.evt, spec.fn, true); } catch (_) {} + try { document.removeEventListener(spec.evt, spec.fn, true); } catch (err) { console.warn('[overlay] removeEventListener failed for ' + spec.evt + ':', err); } + try { document.addEventListener(spec.evt, spec.fn, true); } catch (err) { console.warn('[overlay] addEventListener failed for ' + spec.evt + ':', err); } } if (!window.__cs_err) { - try { window.addEventListener('error', onError, true); window.__cs_err = true; } catch (_) {} + try { window.addEventListener('error', onError, true); window.__cs_err = true; } catch (err) { console.warn('[overlay] attach window error listener failed:', err); } } if (!window.__cs_rej) { - try { window.addEventListener('unhandledrejection', onRejection, true); window.__cs_rej = true; } catch (_) {} + try { window.addEventListener('unhandledrejection', onRejection, true); window.__cs_rej = true; } catch (err) { console.warn('[overlay] attach unhandledrejection listener failed:', err); } } } reattach(); - try { setInterval(reattach, 200); } catch (_) {} + try { setInterval(reattach, 200); } catch (err) { console.warn('[overlay] setInterval reattach failed:', err); } })();`; export interface OverlayMessage { From d05f00104babb0e256184dba95000a8e540604b4 Mon Sep 17 00:00:00 2001 From: hqhq1025 <1506751656@qq.com> Date: Sun, 19 Apr 2026 05:11:46 +0800 Subject: [PATCH 2/2] fix(runtime): dedupe reattach loop warnings to avoid console spam The overlay's 200ms setInterval reattach loop could flood the console when iframe content breaks add/removeEventListener (Codex Major on #61). Introduce a warnOnce helper keyed by error message so each unique failure warns at most once over the loop's lifetime. One-shot catches outside the loop keep their direct console.warn. Adds vitest coverage simulating 25 reattach ticks with throwing listeners and asserts warn count stays bounded by unique-key count, not tick count. Signed-off-by: hqhq1025 <1506751656@qq.com> --- packages/runtime/src/overlay.test.ts | 76 ++++++++++++++++++++++++++++ packages/runtime/src/overlay.ts | 14 +++-- 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 packages/runtime/src/overlay.test.ts diff --git a/packages/runtime/src/overlay.test.ts b/packages/runtime/src/overlay.test.ts new file mode 100644 index 00000000..2278cad5 --- /dev/null +++ b/packages/runtime/src/overlay.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it, vi } from 'vitest'; +import { OVERLAY_SCRIPT } from './overlay'; + +interface FakeWindow { + addEventListener: (type: string, fn: unknown, capture?: boolean) => void; + parent: { postMessage: (msg: unknown, target: string) => void }; + __cs_err?: boolean; + __cs_rej?: boolean; +} + +function runOverlay(opts: { + removeThrows?: boolean; + addThrows?: boolean; +}): { warn: ReturnType; tick: () => void } { + const warn = vi.fn(); + const fakeConsole = { warn }; + + const fakeDocument = { + body: {}, + addEventListener: () => { + if (opts.addThrows) throw new Error('add failed'); + }, + removeEventListener: () => { + if (opts.removeThrows) throw new Error('remove failed'); + }, + }; + + const fakeWindow: FakeWindow = { + addEventListener: () => {}, + parent: { postMessage: () => {} }, + }; + + let intervalFn: (() => void) | null = null; + const fakeSetInterval = (fn: () => void) => { + intervalFn = fn; + return 1; + }; + + const sandbox = new Function( + 'window', + 'document', + 'console', + 'setInterval', + `with (window) { ${OVERLAY_SCRIPT} }`, + ); + sandbox(fakeWindow, fakeDocument, fakeConsole, fakeSetInterval); + + return { + warn, + tick: () => { + if (intervalFn) intervalFn(); + }, + }; +} + +describe('OVERLAY_SCRIPT reattach loop warning throttle', () => { + it('dedupes repeated reattach failures across many ticks', () => { + const { warn, tick } = runOverlay({ removeThrows: true, addThrows: true }); + // Initial reattach already ran inside script; simulate 25 more interval fires (~5s @ 200ms). + for (let i = 0; i < 25; i++) tick(); + + // 3 install specs * 2 ops (remove+add) = 6 distinct keys at most. + // The point: it must not scale with tick count. + expect(warn.mock.calls.length).toBeLessThanOrEqual(6); + }); + + it('emits at most one warn per unique error key over the whole loop', () => { + const { warn, tick } = runOverlay({ removeThrows: true }); + for (let i = 0; i < 25; i++) tick(); + const keys = new Set(warn.mock.calls.map((c) => String(c[0]))); + // each warn call should be a unique key + expect(warn.mock.calls.length).toBe(keys.size); + // should be ≤ 3 (one per event type), well under the 25-tick spam ceiling + expect(warn.mock.calls.length).toBeLessThanOrEqual(3); + }); +}); diff --git a/packages/runtime/src/overlay.ts b/packages/runtime/src/overlay.ts index c51b3a40..f1cccc57 100644 --- a/packages/runtime/src/overlay.ts +++ b/packages/runtime/src/overlay.ts @@ -19,6 +19,12 @@ export const OVERLAY_SCRIPT = `(function() { 'use strict'; var hovered = null; + var warned = Object.create(null); + function warnOnce(key, err) { + if (warned[key]) return; + warned[key] = true; + try { console.warn('[overlay] ' + key, err); } catch (_) { /* noop */ } + } function getXPath(el) { if (el.dataset && el.dataset.codesignId) return '[data-codesign-id="' + el.dataset.codesignId + '"]'; @@ -100,14 +106,14 @@ export const OVERLAY_SCRIPT = `(function() { function reattach() { for (var i = 0; i < installs.length; i++) { var spec = installs[i]; - try { document.removeEventListener(spec.evt, spec.fn, true); } catch (err) { console.warn('[overlay] removeEventListener failed for ' + spec.evt + ':', err); } - try { document.addEventListener(spec.evt, spec.fn, true); } catch (err) { console.warn('[overlay] addEventListener failed for ' + spec.evt + ':', err); } + try { document.removeEventListener(spec.evt, spec.fn, true); } catch (err) { warnOnce('removeEventListener failed for ' + spec.evt, err); } + try { document.addEventListener(spec.evt, spec.fn, true); } catch (err) { warnOnce('addEventListener failed for ' + spec.evt, err); } } if (!window.__cs_err) { - try { window.addEventListener('error', onError, true); window.__cs_err = true; } catch (err) { console.warn('[overlay] attach window error listener failed:', err); } + try { window.addEventListener('error', onError, true); window.__cs_err = true; } catch (err) { warnOnce('attach window error listener failed', err); } } if (!window.__cs_rej) { - try { window.addEventListener('unhandledrejection', onRejection, true); window.__cs_rej = true; } catch (err) { console.warn('[overlay] attach unhandledrejection listener failed:', err); } + try { window.addEventListener('unhandledrejection', onRejection, true); window.__cs_rej = true; } catch (err) { warnOnce('attach unhandledrejection listener failed', err); } } } reattach();