mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(core,cli,engine,producer): add getVariables() helper and --variables render flag
Adds the parametrized-render primitive from hf#592 by reusing the existing
data-composition-variables schema as the source of declared defaults.
- Runtime helper window.__hyperframes.getVariables() (also exported from
@hyperframes/core) reads data-composition-variables defaults from the
document root and merges window.__hfVariables (CLI override) on top.
Returns Partial<T> for typed access; supports a generic for editor
ergonomics. Same code path runs in dev preview and at render time.
- CLI render --variables '<json>' / --variables-file <path> populates the
override. Mutually exclusive; fail-fast on conflicting flags, missing
file, unparseable JSON, or non-object payloads. parseVariablesArg is
exported as a pure function so validation paths stay unit-testable.
- Engine injects window.__hfVariables via evaluateOnNewDocument before
any page script runs, so the helper sees the merged values on its
first call. Empty payloads are skipped to avoid pointless init scripts.
- Producer threads variables through RenderConfig and into the engine's
CaptureOptions; Docker mode forwards --variables to the in-container
CLI invocation via dockerRunArgs.
Composition authors declare variables once on the root <html> element:
<html data-composition-variables='[
{"id":"title","type":"string","label":"Title","default":"Hello"}
]'>
and read them in any composition script:
const { title } = window.__hyperframes.getVariables();
A render with `--variables '{"title":"Q4 Report"}'` overrides the default
without modifying the composition source. Missing keys fall through to
the declared defaults, so dev preview and CLI renders without --variables
behave identically.
This is PR 1 of a 4-PR stack. Sub-comp per-instance scoping (carrying
host data-variable-values through the inlined sub-comp's getVariables()
call) lands in PR 2; schema validation and lint in PR 3; skill / scaffold
distribution in PR 4.
Tests: 9 new unit tests for getVariables() (jsdom), 11 new CLI tests
covering parseVariablesArg validation paths and Docker passthrough,
2 new dockerRunArgs assertions for the --variables flag. All existing
tests green (core 611, cli 208, engine 519).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Reads the resolved variables for the current composition.
|
||||
*
|
||||
* Resolves to declared defaults from `<html data-composition-variables="...">`
|
||||
* merged with `window.__hfVariables` (set at render time by the engine when
|
||||
* the user passes `hyperframes render --variables '<json>'`).
|
||||
*
|
||||
* Returns `Partial<T>` because not every declared variable is guaranteed to
|
||||
* have a default, and not every key in `__hfVariables` is guaranteed to be
|
||||
* declared. Callers are expected to destructure with their own fallbacks
|
||||
* where strictness matters:
|
||||
*
|
||||
* const { title = "Untitled", theme = "light" } = getVariables<MyVars>();
|
||||
*/
|
||||
export function getVariables<
|
||||
T extends Record<string, unknown> = Record<string, unknown>,
|
||||
>(): Partial<T> {
|
||||
if (typeof document === "undefined") return {} as Partial<T>;
|
||||
|
||||
const declaredDefaults = readDeclaredDefaults(document.documentElement);
|
||||
const overrides = readOverrides();
|
||||
|
||||
return { ...declaredDefaults, ...overrides } as Partial<T>;
|
||||
}
|
||||
|
||||
function readDeclaredDefaults(root: Element | null): Record<string, unknown> {
|
||||
if (!root) return {};
|
||||
const raw = root.getAttribute("data-composition-variables");
|
||||
if (!raw) return {};
|
||||
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
if (!Array.isArray(parsed)) return {};
|
||||
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const entry of parsed) {
|
||||
if (
|
||||
entry &&
|
||||
typeof entry === "object" &&
|
||||
typeof (entry as { id?: unknown }).id === "string" &&
|
||||
"default" in entry
|
||||
) {
|
||||
out[(entry as { id: string }).id] = (entry as { default: unknown }).default;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function readOverrides(): Record<string, unknown> {
|
||||
if (typeof window === "undefined") return {};
|
||||
const raw = (window as Window & { __hfVariables?: unknown }).__hfVariables;
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
||||
return raw as Record<string, unknown>;
|
||||
}
|
||||
Reference in New Issue
Block a user