Files
884d8690af perf(core): reuse fonts, asset paths and scene parses across a page build (#5954)
* perf(core): a project with many composition variables builds without a style scan per variable

* perf(core): a build checks each project path once, however often a scene is mounted

* perf(core): collecting a page's characters for fonts no longer copies the page into arrays

* perf(core): a warm font cache builds the page without asking Google Fonts again

The woff2 files were cached on disk, but the CSS lookup that names them was
only cached in memory. Each new process asked fonts.googleapis.com first, and
offline the lookup failed before the cached woff2 files were ever reached.
The lookup is now kept on disk beside them, keyed by its request URL. Only a
lookup that named faces is kept, written aside and renamed into place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(core): a build resolves and reads each inlined asset once, however often a scene is mounted

Inlining a relative asset as a data: URL ran isSafePath on it, which realpaths
the path and every ancestor, then statted, read and base64-encoded the file.
It did so for every url() of every mount, so a scene mounted 160 times paid
for the same few files 160 times. The answers are now kept for the build: a
path's safety is shared with the entry-path checks, and an asset's data: URL
(or its too-large warning) is kept by file path.

The bundler also tells onRead about each path once per build. The preview's
read tracker realpaths and readlinks every path it is told about, and it was
told again for every mount.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(core): a scene mounted many times is read, parsed and scoped once per build

Every mount of a sub-composition re-read its file, re-stamped its hf ids,
parsed it four times (twice to check it is usable, twice to mount it) and
re-ran postcss over each of its stylesheets, though only the scope selector,
ids and variables differ between mounts.

- The bundler reads and stamps each scene file once per build.
- inlineSubCompositions parses each file once, answers the usability check
  from that parse, and gives each mount its own clone to edit.
- createCompositionCssScoper scopes a sheet once around a placeholder scope
  shaped like the real ones, then substitutes each mount's scope. It falls
  back to scopeCssToComposition for any scope or id outside [\w.:-], a scope
  equal to the authored id, a sheet holding the placeholder, or a selector
  kept raw for a comment.

A golden test pins the preview and render pages of a representative project
(goldens written by the build before this change), and a fuzz test compares
the scoper with scopeCssToComposition over random sheets and scopes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 13:45:08 +00:00
..
2026-10-21 11:04:37 +00:00

@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