* fix(core): a data script in a sub-composition no longer stops every timeline A sub-composition's <script type="application/json"> was wrapped and run as JavaScript, and because the bundle merges every composition script into one, its parse error stopped every scene's timeline. Data scripts now stay in the DOM as authored, and a script run that does not parse is left unmerged so a bad script fails alone. * fix(core): render and preview keep a broken composition script from stopping the rest The render path's script merge had the same gap as the bundler: one script that does not parse stopped every timeline. Both now share one parse check. A run left unmerged, and every scene-part script, is still escaped, so a string holding "</script>" can no longer end the script early in preview. * refactor(core): carry the bundler's script-merge reasons in names, not comments * fix(core): every composition path runs only the scripts a browser would A script check now matches the browser (esbuild accepted export and top-level await), so such a script can no longer stop every timeline. Inline template compositions keep data scripts and run module and import map scripts as such. A data script beside the composition root is mounted next to it, a nomodule script never runs, and the render path merges scripts on a page with no head. * fix(core): keep inline-template data scripts and run a script with a top-level return alone - parsesAsScript compiles with vm.Script, so a top-level return or new.target no longer passes as mergeable and stops every timeline. - The bundler's inline <template id="X-template"> path asks the shared assembly plan for its root and mounts the inert scripts authored beside it. - nomodule only stops classic scripts; type="module" nomodule runs, as in browsers. * refactor(core): the inline template path reads its root id from the shared assembly plan * fix(core): a broken composition script stays apart under Bun as well as Node parsesAsScript now parses with acorn in script mode instead of vm.Script. Bun 1.3's vm.Script compiles lazily and accepted any text, so the Studio dev server merged a broken script with the rest and every timeline stopped. acorn is already locked for parsers and lint; core now declares it. * test(core): name the parse-check test for what it pins * style(core): wrap the data-block comment under the line limit
@hyperframes/core
Types, parsers, generators, compiler, linter, runtime, and frame adapters for the Hyperframes video framework.
Install
npm install @hyperframes/core
Most users don't need to install core directly — the CLI, producer, and studio packages depend on it internally.
What's inside
| Module | Description |
|---|---|
| Types | TimelineElement, CompositionSpec, Asset, canvas dimensions, defaults |
| Parsers | parseHtml — extract timeline elements from HTML; parseGsapScript — parse GSAP animations |
| Generators | generateHyperframesHtml — produce valid Hyperframes HTML from a composition spec |
| Compiler | compileTimingAttrs — resolve data-start / data-duration into absolute times |
| Linter | lintHyperframeHtml — validate Hyperframes HTML (missing attributes, overlapping tracks, etc.) |
| Runtime | IIFE script injected into the browser — manages seek, media playback, and the window.__hf protocol |
| Frame Adapters | Pluggable animation drivers (GSAP, Lottie, CSS, or custom) |
Generated composition trust
Composition generators require trusted authors for code-bearing inputs. styles and
generateHyperframesStyles preserve authored CSS, which can load external resources.
animations may contain __raw: values that are emitted as JavaScript;
includeScripts: true includes executable timeline code. serializeGsapAnimations
also accepts raw preamble, postamble, and a code-bearing timelineVar. Never fill
these inputs with untrusted data. Attribute encoding and closing-tag containment
are not a sandbox; render untrusted compositions in an appropriately isolated
execution environment and never serve them on a privileged origin.
Text content retains the supported inline-formatting sanitizer contract. The clip parser intentionally flattens inner formatting to text, so parse/generate is not a lossless replacement for editing the source HTML.
Frame Adapters
A frame adapter tells the engine how to seek your animation to a specific frame:
import { createGSAPFrameAdapter } from "@hyperframes/core";
const adapter = createGSAPFrameAdapter({
getTimeline: () => gsap.timeline(),
compositionId: "my-video",
});
Implement FrameAdapter for custom animation runtimes:
import type { FrameAdapter } from "@hyperframes/core";
const myAdapter: FrameAdapter = {
id: "my-adapter",
getDurationFrames: () => 300,
seekFrame: (frame) => {
/* seek your animation */
},
};
Parsing and generating HTML
import { parseHtml, generateHyperframesHtml } from "@hyperframes/core";
const { elements, metadata } = parseHtml(htmlString);
const html = generateHyperframesHtml(spec);
Linting
import { lintHyperframeHtml } from "@hyperframes/core/lint";
const result = lintHyperframeHtml(htmlString);
// result.findings: { severity, message, elementId }[]
Documentation
Full documentation: hyperframes.heygen.com/packages/core
Related packages
@hyperframes/engine— rendering engine that drives the browser@hyperframes/producer— full render pipeline (capture + encode)hyperframes— CLI