Skip to content

fix(player): detect the runtime by __player and honour runtime-src for src embeds - #4246

Open
srikarsunchu wants to merge 1 commit into
heygen-com:mainfrom
srikarsunchu:fix/player-src-runtime-bridge-and-runtime-src
Open

srikarsunchu wants to merge 1 commit into
heygen-com:mainfrom
srikarsunchu:fix/player-src-runtime-bridge-and-runtime-src

Conversation

@srikarsunchu

Copy link
Copy Markdown

Closes #4002
Closes #4003

Problem

Two related defects in how <hyperframes-player src="..."> discovers and injects the core runtime:

  1. window.__hf was treated as the runtime bridge. @hyperframes/shader-transitions also creates that global (window.__hf = window.__hf || {}) to publish shaderTransitionsReady. Any authored composition that uses shader transitions and registers its own __timelines therefore looked like it already had the runtime: the probe never injected it, hasRuntimeBridge() refused the direct-timeline adapter, and the element emitted error: "Composition timeline not found after 8s" with a black frame (player: window.__hf from @hyperframes/shader-transitions makes an src embed reject its own composition #4002).
  2. runtime-src only applied to srcdoc. The probe's late injection closed over the module-level CDN constant, so an src embed always fetched the runtime from jsDelivr even when the element carried a valid runtime-src. Offline, air-gapped, or behind a script-src 'self' CSP that silently fails and the embed times out (player: runtime-src is honoured for srcdoc only - an src embed always fetches the runtime from jsDelivr #4003).

Fix

  • hasRuntimeBridge(win) now checks for window.__player only. The core runtime installs __player synchronously in the same task as __hf (initSandboxRuntimeModular has no awaits between the two), so there is no tick on which a real runtime has __hf but not __player. Both the probe tick's hasRuntime and the adapter path use the same helper.
  • The runtime-src resolver in shader-options.ts is exported as resolveRuntimeUrlFromElement and the player hands it to the probe via a new resolveRuntimeUrl callback. It is read at injection time, so a runtime-src set after construction is still honoured. The existing origin/loopback validation is unchanged and now covers both paths.
  • The injected <script> gets an onerror handler: a blocked or 404 runtime now stops the probe and reports HyperFrames runtime failed to load from <url> instead of waiting out the 8 s timeout with a message about the timeline.

Tests

New unit tests in composition-probe.test.ts cover: the shader-transitions __hf namespace next to __timelines resolving via the direct-timeline adapter; __player still winning over __timelines; injection into a nested composition despite a pre-existing __hf; injection using resolveRuntimeUrl; and the script error path stopping the probe without a second timeout error. hyperframes-player.test.ts gains an element-level test that an src embed with runtime-src injects the configured URL.

bun run --filter @hyperframes/player test (excluding the pre-existing slideshow presenter-mode failures, which fail identically on main in this environment), tsc --noEmit, oxlint, oxfmt --check, the lefthook pre-commit gate, and the player build with verify-runtime-pin.mjs all pass.

Repro from the issues (both host.html variants in the linked gist) is expected to report ready for the shader-transitions composition and to request /local-runtime.js instead of jsDelivr for the src embed.

🤖 Generated with Claude Code

…r src embeds

`<hyperframes-player src="...">` treated the existence of `window.__hf` in
the composition frame as "the core runtime is present". `__hf` is a shared
namespace, not a bridge: `@hyperframes/shader-transitions` creates it too
(`window.__hf = window.__hf || {}`) to publish `shaderTransitionsReady`. An
authored composition that used shader transitions and registered its own
`__timelines` therefore never had the runtime injected and had its
direct-timeline adapter refused, and the embed failed 8 s later with
"Composition timeline not found". The only global the player ever drives is
`__player`, which the runtime installs synchronously in the same task as
`__hf`, so that is now the bridge check on both the probe tick and the
adapter path.

The probe's injection also always loaded the runtime from jsDelivr: the
`runtime-src` attribute was resolved for srcdoc only. The resolver is now
shared, and the player passes it to the probe, so an `src` embed can serve
its own pinned copy offline or under a `script-src 'self'` CSP. A runtime
that fails to load reports its URL on the script's own error event instead
of being indistinguishable from a slow one until the timeout.

Closes heygen-com#4002
Closes heygen-com#4003

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants