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:
James
2026-05-03 00:18:13 +00:00
co-authored by Claude Opus 4.7
parent 4760afd3fc
commit c0d75a5268
14 changed files with 490 additions and 2 deletions
@@ -187,4 +187,27 @@ describe("buildDockerRunArgs", () => {
expect(args).toContain("10M");
expect(args).not.toContain("--crf");
});
it("forwards --variables JSON to the container when set", () => {
const args = buildDockerRunArgs({
...FIXED_INPUT,
options: { ...BASE, variables: { title: "Hello", n: 3 } },
});
const idx = args.indexOf("--variables");
expect(idx).toBeGreaterThan(-1);
expect(args[idx + 1]).toBe('{"title":"Hello","n":3}');
});
it("omits --variables when none provided", () => {
const args = buildDockerRunArgs({ ...FIXED_INPUT, options: BASE });
expect(args).not.toContain("--variables");
});
it("omits --variables when payload is empty", () => {
const args = buildDockerRunArgs({
...FIXED_INPUT,
options: { ...BASE, variables: {} },
});
expect(args).not.toContain("--variables");
});
});
+4
View File
@@ -29,6 +29,7 @@ export interface DockerRenderOptions {
crf?: number;
videoBitrate?: string;
quiet: boolean;
variables?: Record<string, unknown>;
}
export function buildDockerRunArgs(input: DockerRunArgsInput): string[] {
@@ -63,5 +64,8 @@ export function buildDockerRunArgs(input: DockerRunArgsInput): string[] {
...(options.browserGpu ? [] : ["--no-browser-gpu"]),
...(options.hdrMode === "force-hdr" ? ["--hdr"] : []),
...(options.hdrMode === "force-sdr" ? ["--sdr"] : []),
...(options.variables && Object.keys(options.variables).length > 0
? ["--variables", JSON.stringify(options.variables)]
: []),
];
}