mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(sdk): variable CRUD (declare/remove/get/list) + export gaps
Closes the remaining Tier 2/3 gaps from the SDK surface audit that motivated #2092 — real, contained fixes short of the two genuinely architectural items (a live-DOM apply adapter, structural editing ops) that need their own design pass, not a quick patch. Variable CRUD was write-only and creation-blocked: setVariableValue existed, but there was no getVariableValue, listVariables, declareVariable, or removeVariable — and writeVariableDefault intentionally refuses to create an undeclared variable ("keep the schema authoritative"), so a variables panel (list what exists, read current values, let someone add one) could not be built against the SDK at all. - getVariableValue(id) / listVariables(): thin reads over the existing readVariableDefault / a new listVariableDecls. - declareVariable(decl) / removeVariable(id): new EditOps with full undo/redo support via a new patch path (/variable-decls/{id}, distinct from /variables/{id} which is default-only) — removeVariable's inverse bundles the original array index so undo reinserts at the same position instead of appending, mirroring handleRemoveElement's siblingIndex. Export gaps (same shape as #2092's fixes — the logic already existed, just wasn't reachable): resolveScoped, findById, escapeHfId from engine/model.ts; readVariableDefault from engine/variableModel.ts. 17 new tests across mutate.test.ts (declareVariable/removeVariable engine semantics + undo), session.test.ts (Composition-level API), and smoke.test.ts (export-surface import check). 439/439 sdk tests passing. Full workspace build (incl. studio) verified clean.
This commit is contained in:
@@ -556,3 +556,73 @@ describe("getAllAnimationIds", () => {
|
||||
expect(comp.getAllAnimationIds().has(realId)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── getVariableValue / listVariables / declareVariable / removeVariable ──────
|
||||
|
||||
const VARIABLES_HTML = `<!DOCTYPE html>
|
||||
<html data-composition-id="c1" data-composition-duration="5" data-composition-variables='${JSON.stringify(
|
||||
[{ id: "brand-color", type: "color", label: "Brand color", default: "#0066cc" }],
|
||||
)}'>
|
||||
<body>${BASE_HTML}</body>
|
||||
</html>`;
|
||||
|
||||
describe("variable declarations (Composition API)", () => {
|
||||
it("getVariableValue reads a declared variable's current default", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
expect(comp.getVariableValue("brand-color")).toBe("#0066cc");
|
||||
});
|
||||
|
||||
it("getVariableValue returns undefined for an undeclared id", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
expect(comp.getVariableValue("never-declared")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("listVariables returns every declared variable's full schema", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
expect(comp.listVariables()).toEqual([
|
||||
{ id: "brand-color", type: "color", label: "Brand color", default: "#0066cc" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("listVariables returns [] when the composition declares none", async () => {
|
||||
const comp = await openComposition(BASE_HTML);
|
||||
expect(comp.listVariables()).toEqual([]);
|
||||
});
|
||||
|
||||
it("declareVariable creates a new declaration a variables panel can list immediately", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
comp.declareVariable({ id: "brand-title", type: "string", label: "Title", default: "Hi" });
|
||||
expect(comp.getVariableValue("brand-title")).toBe("Hi");
|
||||
expect(comp.listVariables()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("declareVariable can create where setVariableValue's model write silently no-ops", async () => {
|
||||
const comp = await openComposition(BASE_HTML); // no data-composition-variables at all
|
||||
comp.setVariableValue("never-declared", "x");
|
||||
expect(comp.getVariableValue("never-declared")).toBeUndefined();
|
||||
comp.declareVariable({ id: "never-declared", type: "string", label: "New", default: "x" });
|
||||
expect(comp.getVariableValue("never-declared")).toBe("x");
|
||||
});
|
||||
|
||||
it("removeVariable removes the declaration; listVariables reflects it immediately", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
comp.removeVariable("brand-color");
|
||||
expect(comp.listVariables()).toEqual([]);
|
||||
expect(comp.getVariableValue("brand-color")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("declareVariable / removeVariable both support undo", async () => {
|
||||
const comp = await openComposition(VARIABLES_HTML);
|
||||
comp.declareVariable({ id: "brand-title", type: "string", label: "Title", default: "Hi" });
|
||||
expect(comp.listVariables()).toHaveLength(2);
|
||||
comp.undo();
|
||||
expect(comp.listVariables()).toHaveLength(1);
|
||||
|
||||
comp.removeVariable("brand-color");
|
||||
expect(comp.listVariables()).toEqual([]);
|
||||
comp.undo();
|
||||
expect(comp.listVariables()).toEqual([
|
||||
{ id: "brand-color", type: "color", label: "Brand color", default: "#0066cc" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user