From 35eab94e69ad7c15d19101938c3b79094ec018dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 11 May 2026 19:37:06 -0700 Subject: [PATCH] fix(core): parent-frame proxy bypass, data-preload-eager opt-out, configurable threshold - Player: _adoptIframeMedia now skips media with preload="metadata" or "none", preventing parent-frame proxies from bypassing the preloader. MutationObserver extended to watch preload attribute changes so proxies are created just-in-time when the preloader promotes a clip. - init.ts: lazy-mode demotion loop skips elements with data-preload-eager, letting power users keep specific clips eagerly buffered. - mediaPreloader: reads window.__HF_LAZY_PRELOAD_THRESHOLD as an override, falling back to the default 6. --- packages/core/src/runtime/init.ts | 4 +++ .../core/src/runtime/mediaPreloader.test.ts | 33 ++++++++++++++++++ packages/core/src/runtime/mediaPreloader.ts | 6 +++- .../player/src/hyperframes-player.test.ts | 9 ++++- packages/player/src/hyperframes-player.ts | 34 +++++++++++++++++-- 5 files changed, 82 insertions(+), 4 deletions(-) diff --git a/packages/core/src/runtime/init.ts b/packages/core/src/runtime/init.ts index fcb4c6d91..65c19bc9f 100644 --- a/packages/core/src/runtime/init.ts +++ b/packages/core/src/runtime/init.ts @@ -1263,6 +1263,10 @@ export function initSandboxRuntimeModular(): void { // timed clips and would never promote them back. for (const mediaEl of mediaEls) { if (!mediaEl.hasAttribute("data-start")) continue; + // Power-user opt-out: data-preload-eager keeps a clip eagerly buffered + // even under lazy mode, useful when a specific clip must be instantly + // available regardless of playhead proximity. + if (mediaEl.hasAttribute("data-preload-eager")) continue; if (mediaEl.preload === "auto" || mediaEl.preload === "") { mediaEl.preload = "metadata"; // Kick off the metadata fetch explicitly — some browsers (Chrome Lite diff --git a/packages/core/src/runtime/mediaPreloader.test.ts b/packages/core/src/runtime/mediaPreloader.test.ts index 9353f3e56..792ed1fb7 100644 --- a/packages/core/src/runtime/mediaPreloader.test.ts +++ b/packages/core/src/runtime/mediaPreloader.test.ts @@ -322,4 +322,37 @@ describe("createMediaPreloadManager", () => { expect(onActivation).toHaveBeenCalledOnce(); }); + + it("respects window.__HF_LAZY_PRELOAD_THRESHOLD override", () => { + elements = Array.from({ length: 4 }, (_, i) => + mockMediaElement({ start: String(i * 5), duration: "5" }), + ); + setupDOM(elements); + + // 4 elements is below the default threshold (6) but at our custom one + (window as Record).__HF_LAZY_PRELOAD_THRESHOLD = 4; + + const manager = createMediaPreloadManager(); + manager.refresh(); + + expect(manager.isLazy()).toBe(true); + + // Clean up + delete (window as Record).__HF_LAZY_PRELOAD_THRESHOLD; + }); + + it("falls back to default threshold when __HF_LAZY_PRELOAD_THRESHOLD is not set", () => { + elements = Array.from({ length: 4 }, (_, i) => + mockMediaElement({ start: String(i * 5), duration: "5" }), + ); + setupDOM(elements); + + // Ensure it's not set + delete (window as Record).__HF_LAZY_PRELOAD_THRESHOLD; + + const manager = createMediaPreloadManager(); + manager.refresh(); + + expect(manager.isLazy()).toBe(false); + }); }); diff --git a/packages/core/src/runtime/mediaPreloader.ts b/packages/core/src/runtime/mediaPreloader.ts index fd8308631..6e4d2b4f6 100644 --- a/packages/core/src/runtime/mediaPreloader.ts +++ b/packages/core/src/runtime/mediaPreloader.ts @@ -38,7 +38,11 @@ export function createMediaPreloadManager(options?: { function refresh(): void { const cache = refreshRuntimeMediaCache(options); clips = cache.mediaClips; - lazy = clips.length >= LAZY_THRESHOLD; + const configuredThreshold = + typeof (window as Record).__HF_LAZY_PRELOAD_THRESHOLD === "number" + ? ((window as Record).__HF_LAZY_PRELOAD_THRESHOLD as number) + : LAZY_THRESHOLD; + lazy = clips.length >= configuredThreshold; if (lazy && !activationEmitted) { activationEmitted = true; options?.onActivation?.(clips.length); diff --git a/packages/player/src/hyperframes-player.test.ts b/packages/player/src/hyperframes-player.test.ts index 98b30762e..b8d9cae60 100644 --- a/packages/player/src/hyperframes-player.test.ts +++ b/packages/player/src/hyperframes-player.test.ts @@ -548,8 +548,15 @@ describe("HyperframesPlayer media MutationObserver scoping", () => { expect(observedTargets).not.toContain(fakeDoc.body); // Subtree is still required — sub-composition media can be deeply nested // inside the host (e.g. wrapper div around the `