Commit Graph
153 Commits
Author SHA1 Message Date
James Russo 21cb722ebd refactor(core): unify composition contract (#2157)
* refactor(core): unify composition contract

* fix(parsers): parse start expressions linearly
2026-07-16 02:44:22 -04:00
Miguel Ángel 0f287ee0b6 chore: release v0.7.60 2026-07-16 05:26:57 +00:00
Vance Ingalls 4682da14f1 fix(sdk): attachSync re-syncs the override snapshot on iframe load 2026-07-15 14:50:41 -07:00
Vance Ingalls db5e062211 feat(sdk): getVariableValue({ base: true }) reads pre-override declared defaults 2026-07-15 14:50:33 -07:00
Vance Ingalls ff3b1541e5 chore: release v0.7.59 2026-07-15 11:57:49 -07:00
Miguel Ángel 4b0b89e8b1 chore: release v0.7.58 (#2446) 2026-07-14 16:15:59 -04:00
Miguel Ángel 90be05019b chore: release v0.7.57 (#2393) 2026-07-14 00:29:29 -04:00
Miguel Ángel 94c2e0f6eb chore: release v0.7.56 2026-07-13 07:04:42 +00:00
Vance Ingalls c830aa83c5 chore: release v0.7.55 2026-07-12 11:52:53 -07:00
Vance Ingalls ae4946e624 chore: release v0.7.54 2026-07-11 17:15:24 -07:00
Miguel Angel Simon Sierra 3b081a44ae chore: release v0.7.53 2026-07-11 15:59:07 -04:00
Vance Ingalls 7498eb4a3a chore: release v0.7.52 2026-07-10 19:47:59 -07:00
Vance Ingalls 2d17e34b90 Merge pull request #2189 from heygen-com/fix/sdk-template-gsap-resolver-parity
fix(sdk): include template GSAP scripts in resolver parity
2026-07-10 18:22:34 -07:00
Vance Ingalls a61f7de8d7 fix(sdk): align template script traversal 2026-07-10 17:36:09 -07:00
Miguel Ángel 598dd8b350 chore: release v0.7.51 (#2188) 2026-07-10 20:16:15 -04:00
Vance Ingalls 46602d75f4 fix(sdk): include template GSAP scripts in resolver parity 2026-07-10 16:55:19 -07:00
Miguel Angel Simon Sierra 1d97ddaf8c chore: release v0.7.50 2026-07-10 18:48:40 -04:00
Vance Ingalls 6152437d2a chore: release v0.7.49 2026-07-10 09:57:48 -07:00
Miguel Ángel 3fd340f6ba chore: release v0.7.48 (#2118) 2026-07-09 21:49:58 -04:00
Miguel Angel Simon Sierra 3aa1cf0d83 chore: release v0.7.47 2026-07-09 20:34:59 -04:00
James 8de80bf369 feat(sdk,studio): editable template sub-compositions + promote sub-comp element properties 2026-07-09 13:31:04 -07:00
James f7ee0768ae feat(core): declarative variable bindings — data-var-src, data-var-text, css custom props 2026-07-09 13:31:03 -07:00
James bc0e0b314b fix(studio): code-review and live-test fixes for the variables stack 2026-07-09 13:31:03 -07:00
James b34ad85165 feat(studio): variables inspector panel with live preview values 2026-07-09 13:31:03 -07:00
James 91831f3f8e feat(sdk): variable usage scan + preview-values adapter seam 2026-07-09 13:31:02 -07:00
James Russo 839881a98e feat(sdk): variable declaration edit ops (declare/update/remove) (#2047) 2026-07-09 13:29:24 -07:00
James Russo fcbd4cb0f6 feat(sdk): variable declaration read apis + browser-safe variables entry (#2046) 2026-07-09 13:15:21 -07:00
Vance Ingalls 030fded71d chore: release v0.7.46 2026-07-09 12:01:33 -07:00
Vance Ingalls c94de6034e fix(sdk): address PR #2100 review feedback on attachSync
- Script-mirror filter changed from an exact "/script/gsap" match to
  path.startsWith("/script/") — the documented contract is "never mirror
  script-tag rewrites," not just today's one known path; startsWith covers
  any future script-kind patch under the same intent.
- _syncDetach is now cleared when the caller invokes the returned detach
  function directly, not only on the next attachSync call — avoids holding
  a stale (already-unsubscribed) reference between an explicit detach() and
  a later attachSync(other).
- The initial applyOverrideSet call is now wrapped in try/catch: a bad
  initial snapshot no longer prevents the ongoing patch subscription from
  attaching, matching the SDK's existing swallow-and-warn precedent for
  silent-failure paths (adapters/iframe.ts's tainted-canvas warning).
- Added a test proving declareVariable/removeVariable (the /variable-decls/
  patches PR #2098 introduces) mirror onto the live document's
  data-composition-variables attribute — the existing suite only covered
  setVariableValue's CSS-custom-property path, not the schema-metadata path.
2026-07-09 11:52:14 -07:00
Vance Ingalls fbb6a33c91 test(sdk): cover attachSync mirroring for setVariableValue and setTiming
setVariableValue is the headline case the sync spec was built for and
had no coverage; setTiming (data-start/data-end mirroring) was also
untested. Both regression-checked by temporarily breaking the
underlying mutate/apply-patches code paths and confirming the new
assertions fail.
2026-07-09 11:52:14 -07:00
Vance Ingalls 9c691d2438 test(sdk): cover attachSync re-attach teardown and manual detach 2026-07-09 11:52:13 -07:00
Vance Ingalls 0e08543561 test(sdk): cover attachSync against a detached iframe 2026-07-09 11:52:13 -07:00
Vance Ingalls ed9df3f57d test(sdk): cover script-patch skip and stylesheet-patch mirroring in attachSync 2026-07-09 11:52:13 -07:00
Vance Ingalls 91ddf9e196 feat(sdk): attachSync mirrors composition edits onto a live document
Adds attachSync(comp) to PreviewAdapter/IframePreviewAdapter — does an
immediate full sync via the existing applyOverrideSet, then subscribes to
comp.on('patch', ...) and replays every future patch (forward or inverse —
undo/redo included) via the existing applyPatchesToDocument, pointed at the
iframe's live document instead of the offscreen linkedom one. No new
mutation logic; both functions already work against any
{document, wrapped, stamped}-shaped object.

Also adds a no-op attachSync stub to HeadlessPreviewAdapter, required to
keep it satisfying the widened PreviewAdapter interface.

Closes the gap that made pacific's canvas-react hand-roll its own
override-application code (applyOverrideToIframe.ts) with two separate
mechanisms (diffing for normal edits, verbatim op-replay for undo/redo) —
subscribing to the patch stream directly needs only one.
2026-07-09 11:52:13 -07:00
Vance Ingalls bcda11aa7c fix(sdk): address PR #2098 review feedback on variable CRUD
- validateOp now handles declareVariable/removeVariable (E_NO_ROOT when no
  composition root), matching setVariableValue's existing case — previously
  comp.can() returned E_UNKNOWN_OP for both.
- removeVariable's undo-inverse now tags its {decl, index} reinsert payload
  with __kind: "reinsert" instead of relying on structural "decl"/"index"
  key presence to disambiguate it from a plain declareVariable patch.
  VariableDecl has an open index signature, so a real variable schema could
  legally declare its own "decl"/"index" fields and be misinterpreted by the
  old structural check; a regression test pins the exact collision.
- getVariableValue's return type tightened from `unknown` to
  `string | number | boolean | FontValue | ImageValue | undefined`, matching
  setVariableValue's parameter type for round-trip symmetry. The underlying
  unknown-typed read is cast once at this SDK boundary.
- Added a redo test for declareVariable/removeVariable (existing tests only
  covered undo).
2026-07-09 11:52:12 -07:00
Vance Ingalls 19756faa5d 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.
2026-07-09 11:52:12 -07:00
Vance Ingalls 31f0810be8 chore: release v0.7.45 2026-07-08 22:15:49 -07:00
Vance Ingalls 4fb6068fa1 fix(sdk): address review feedback on getRootElements/serialize/getElementTimings PR
Blocker (flagged by all three reviewers, still open after the CI fix):
- Composition.serialize() interface in types.ts never got the { stripRuntime? }
  param the implementation already accepts, so a consumer holding a
  Composition-typed ref (exactly pacific's case) got a strict-TS arity error
  calling comp.serialize({ stripRuntime: true }). Widened the interface.

Also addresses:
- getRootElements() now cached like elementsCache (same 3 invalidation sites) —
  cheap insurance if a layer panel calls it every render tick.
- getElementTimings' resolver now uses the already-parsed expr.value for the
  absolute-number case instead of silently re-parsing via parseFloat, via a
  small resolveReferenceStart helper split out to keep resolveStart's own
  branching low.
- bareId's `?? scopedId` fallback gets a comment: it's unreachable at runtime
  (split() always returns >=1 element) but required by noUncheckedIndexedAccess.
- serialize({ stripRuntime }) docblock generalized past "the editing iframe" —
  it's for any host driving its own clock.
- Documented (and pinned with a test) the bare-id reference resolution's
  cross-scope behavior: a sub-composition element referencing a colliding bare
  id resolves to the canonical top-level match, same as the runtime's own
  resolver — consistent, but a real authoring footgun worth calling out.
- New tests: chained (A->B->C) references, a direct self-reference cycle, a
  mutual A<->B cycle, the cross-scope bare-id collision above, and an import
  assertion that RUNTIME_BOOTSTRAP_ATTR is actually reachable from
  @hyperframes/core and matches the marker generators stamp.

422/422 sdk tests passing (417 + 5 new). Full workspace build (incl. studio)
verified clean.
2026-07-08 21:59:52 -07:00
Vance Ingalls c1b8815cb2 feat(sdk): export getRootElements/isNewHostBoundary/bareId, fix relative data-start
Closes gaps surfaced by pacific#30298 (hyperframes layer panel), where consumer
code had to hand-roll fixes for things the SDK/core already solve or nearly solve:

- getRootElements(): getElements() flattens the tree, so every descendant also
  appears as its own top-level entry. buildRoots() already computes true roots
  internally; this exposes it directly instead of making consumers re-derive
  roots by filtering out descendant ids.
- Export isNewHostBoundary + bareId from @hyperframes/sdk: both already existed
  internally (engine/model.ts) but weren't exported, so consumers were
  duplicating sub-composition-boundary detection and scoped-id-to-DOM-leaf
  conversion by hand.
- Export stripEmbeddedRuntimeScripts + RUNTIME_BOOTSTRAP_ATTR from
  @hyperframes/core, and wire serialize({ stripRuntime: true }) on the SDK
  session: a proper tokenizing implementation already existed in
  compiler/htmlDocument.ts (handles more runtime-script marker variants than a
  naive regex), just never exported. The SDK itself imports these via narrow
  subpaths (./runtime/start-expression, ./compiler/html-document) rather than
  the wide ./compiler barrel, matching the SDK's existing import convention and
  avoiding pulling Node-only compiler code (fs/path) into browser bundles.
- Fix getElementTimings(): data-start can be a relative-reference expression
  ("intro", "intro + 2" — see parseStartExpression's grammar), not just an
  absolute number. The old code did a raw parseFloat() on it, which silently
  resolved any reference expression to 0. Now resolves references recursively
  against the target element's own resolved start + duration, Node-safe (no
  live GSAP timeline needed for this case).

14 new tests (session.timings.test.ts, session.subcomp.test.ts). Full sdk
suite: 417/417 passing. Full workspace build (incl. studio) verified clean.
2026-07-08 21:29:52 -07:00
Vance Ingalls 1e8dd29815 chore: release v0.7.44 2026-07-08 16:21:05 -07:00
Vance Ingalls f8f945d42e chore: release v0.7.43 2026-07-08 15:51:39 -07:00
Vance IngallsandClaude Fable 5 f6cd711bf0 chore: release v0.7.42
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:04:26 -07:00
Miguel Ángel 60463d0bd5 chore: release v0.7.41 2026-07-07 20:30:24 +00:00
Miguel Ángel 2bd9bb6f69 chore: release v0.7.40 (#2024) 2026-07-07 12:30:13 -04:00
Vance Ingalls 229e88eac5 chore: release v0.7.39 2026-07-06 22:48:42 -07:00
Vance Ingalls d8d3a93b0d chore: release v0.7.38 2026-07-06 17:47:34 -07:00
Vance IngallsandClaude Fable 5 3a717fa719 fix(parsers,sdk,studio-server,studio): unify hf-id space across preview, disk, and SDK session (#1981)
* fix(parsers,sdk,studio-server,studio): unify hf-id space across preview, disk, and SDK session

Root-causes the setTiming element_not_found resolver-shadow divergence class:
timeline edits carry hf-ids read from the live preview DOM, but the preview
minted ids AFTER rewriting attributes (and never persisted them for sub-comps),
while the SDK session mints from the raw file — content-keyed minting then
yields different ids for the same element. Template-based comps were worse:
the SDK excluded the whole <template> subtree, so the session had zero
elements and every edit diverged.

- parsers: ensureHfIds now descends into <template> subtrees (linkedom's
  querySelectorAll does not), minting and pinning inner ids
- sdk: buildRoots/buildElement treat <template> as a transparent container,
  and resolution (resolveScoped, animation-id map) searches template subtrees
  via querySelectorAllDeep — template comps now model, resolve, and edit
- studio-server: the sub-comp preview route persists hf-ids to the raw file
  BEFORE the rewrite pipeline (mirrors the main route), pinning one id space
  across served DOM, disk, and SDK session
- studio: resolver-shadow skips structurally-empty sessions (no event, no
  attempt) and tags fail-open emissions with sourceReadFailed so read errors
  are distinguishable from unwired readers in telemetry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(parsers,sdk,studio-server,studio): scope template descent, guard persist route

Addresses the 10 verified findings from the PR #1981 review:

- Restrict template transparency to COMPOSITION templates
  (<template data-composition-id>) everywhere — ensureHfIds, SDK
  buildChildren, querySelectorAllDeep. A plain <template> (runtime
  clone-source) keeps its old fully-excluded behavior: stamping its
  interior would duplicate one persisted id across every runtime clone,
  and modeling it would show phantom timeline clips.
- Guard the sub-comp persist: only .html files (the wildcard route can
  serve any project path — stamping an SVG corrupted it on disk),
  try/catch the read (file-removed race becomes 404, not 500), salt the
  etag (v2) so pre-fix cached clients don't 304 past the id pin, and
  thread the stamped content into buildSubCompositionHtml so served ids
  match the mint even when the disk write is skipped.
- Rewrite querySelectorAllDeep as a document-order DOM walk — appending
  template matches after top-level matches made duplicate-id tiebreaks
  disagree with the preview's unwrapped DOM (wrong-element edits).
- Recurse sourceMutation.querySelectorAllWithTemplates so server-side
  ops resolve ids at any template depth, matching SDK resolution.
- Replace the empty-session silent skip with ONE tagged session_empty
  event per session — silence would blind the tripwire to exactly the
  modeling-gap class that exposed the template bug. Attempts stay
  uncounted (an unmodelable comp can't cut over).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(studio-server): close TOCTOU in sub-comp hf-id persist (CodeQL js/file-system-race)

Replace the route-level stat/read/persist sequence with stampFileHfIds:
validation (fstat), read, mint, and write-back all go through ONE open
file descriptor (O_NOFOLLOW where supported), so the path cannot be
swapped between validation and write. Falls back to read-only stamping
when the file isn't writable — content-keyed minting means the SDK
derives the same ids from the same bytes even without the disk write.

Addresses miguel-heygen's blocking review on PR #1981.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(studio-server): linear-time template-attr match (CodeQL js/polynomial-redos)

promoteTemplateCompositionId's single-pattern regex backtracked
polynomially on crafted input. Two-step match: grab each <template>
open tag linearly, then find data-composition-id within that short
tag text. Same semantics (first template carrying the attr wins).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 00:16:07 -07:00
James 9de41ce316 chore: release v0.7.37 2026-07-06 05:59:36 +01:00
Vance Ingalls c5d725abd0 fix(sdk,studio): resolve animation ids from parsed script, not just DOM-matched elements (#1957)
recordAnimationResolverParity reported a false animation_not_found divergence
for any tween whose selector doesn't currently CSS-match a live DOM element,
because it only checked el.animationIds (DOM-gated). The real server-side op
it shadows resolves purely from the parsed script. Adds
Composition.getAllAnimationIds() as a DOM-independent id set and checks it
too, matching the server's actual resolution behavior.
2026-07-05 20:11:49 -07:00
Miguel Ángel e8d19b1b43 chore: release v0.7.36 2026-07-05 19:25:42 +00:00