mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
feat(lint): error on crossorigin on media (breaks preview) (#1793)
`crossorigin` on <video>/<audio> forces a CORS-checked fetch. The server-side renderer downloads media directly (no CORS) so renders always work, but Studio preview runs in the browser — a media host that omits Access-Control-Allow-Origin silently fails the load, so the media shows blank/black in preview while the render looks fine, hiding the bug. Plain displayed media never needs crossorigin; it's only required to read pixels/samples back (canvas/WebGL texture, WebAudio createMediaElementSource) and only when the host is known CORS-enabled. New rule media_crossorigin_breaks_preview flags it as an error with that guidance. Claude-Session: https://claude.ai/code/session_01NsmfF5FzhqXY6hZ8buXgUE Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
04700ea9c5
commit
1faeba4aa5
@@ -267,4 +267,32 @@ describe("media rules", () => {
|
||||
const finding = result.findings.find((f) => f.code === "media_in_subcomposition");
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
|
||||
it("reports error for media with crossorigin (breaks preview when host omits CORS)", async () => {
|
||||
const html = `
|
||||
<html><body>
|
||||
<div id="root" data-composition-id="c1" data-width="1920" data-height="1080">
|
||||
<video id="v1" crossorigin="anonymous" src="https://cdn.example.com/clip.mp4" data-start="0" data-duration="5" muted playsinline></video>
|
||||
</div>
|
||||
<script>window.__timelines = window.__timelines || {}; window.__timelines["c1"] = gsap.timeline({ paused: true });</script>
|
||||
</body></html>`;
|
||||
const result = await lintHyperframeHtml(html);
|
||||
const finding = result.findings.find((f) => f.code === "media_crossorigin_breaks_preview");
|
||||
expect(finding).toBeDefined();
|
||||
expect(finding?.severity).toBe("error");
|
||||
expect(finding?.elementId).toBe("v1");
|
||||
});
|
||||
|
||||
it("does not flag media without crossorigin", async () => {
|
||||
const html = `
|
||||
<html><body>
|
||||
<div id="root" data-composition-id="c1" data-width="1920" data-height="1080">
|
||||
<video id="v1" src="https://cdn.example.com/clip.mp4" data-start="0" data-duration="5" muted playsinline></video>
|
||||
</div>
|
||||
<script>window.__timelines = window.__timelines || {}; window.__timelines["c1"] = gsap.timeline({ paused: true });</script>
|
||||
</body></html>`;
|
||||
const result = await lintHyperframeHtml(html);
|
||||
const finding = result.findings.find((f) => f.code === "media_crossorigin_breaks_preview");
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -483,6 +483,33 @@ export const mediaRules: Array<(ctx: LintContext) => HyperframeLintFinding[]> =
|
||||
return findings;
|
||||
},
|
||||
|
||||
// media_crossorigin_breaks_preview — `crossorigin` on <video>/<audio> forces a
|
||||
// CORS-checked fetch. The server-side renderer downloads media directly (no CORS),
|
||||
// so it always works there; but Studio preview runs in the browser, where a media
|
||||
// host that omits Access-Control-Allow-Origin silently fails the load — the media
|
||||
// shows BLANK/black in preview while renders look fine, hiding the bug. Plain
|
||||
// displayed media never needs crossorigin; it's only required to read pixels/samples
|
||||
// back (canvas/WebGL texture, WebAudio createMediaElementSource) AND only when the
|
||||
// host is known CORS-enabled.
|
||||
({ tags }) => {
|
||||
const findings: HyperframeLintFinding[] = [];
|
||||
for (const tag of tags) {
|
||||
if (tag.name !== "video" && tag.name !== "audio") continue;
|
||||
if (!hasAttrName(tag.raw, "crossorigin")) continue;
|
||||
const elementId = readAttr(tag.raw, "id") || undefined;
|
||||
findings.push({
|
||||
code: "media_crossorigin_breaks_preview",
|
||||
severity: "error",
|
||||
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> has crossorigin, which forces a CORS-checked fetch. If the media host omits Access-Control-Allow-Origin, the load silently fails in Studio preview (media shows BLANK/black) while server-side renders still work — hiding the bug.`,
|
||||
elementId,
|
||||
fixHint:
|
||||
"Remove the crossorigin attribute unless you read the media back via canvas/WebGL/WebAudio AND the host is known to send CORS headers. Plain displayed media never needs it.",
|
||||
snippet: truncateSnippet(tag.raw),
|
||||
});
|
||||
}
|
||||
return findings;
|
||||
},
|
||||
|
||||
// video_audio_double_source — catches audible <video> paired with a separate
|
||||
// <audio> pointing to the same file, which causes double playback at runtime
|
||||
({ tags }) => {
|
||||
|
||||
Reference in New Issue
Block a user