Skip to content

fix(sdk): attachSync re-syncs the override snapshot on iframe load#2500

Merged
vanceingalls merged 1 commit into
mainfrom
task2-iframe-load-resync
Jul 16, 2026
Merged

fix(sdk): attachSync re-syncs the override snapshot on iframe load#2500
vanceingalls merged 1 commit into
mainfrom
task2-iframe-load-resync

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Part 2/4 of the SDK gap-closure stack. Makes IframePreviewAdapter.attachSync reapply the composition override snapshot whenever the iframe fires load.

Why

srcdoc and src assignments navigate asynchronously. An attach or patch can target the outgoing document and be lost when the new document arrives.

Implementation

  • Extract the immediate override sync into a reusable callback.
  • Register it on the iframe load event.
  • Remove the listener from the returned detach function.
  • Preserve the existing patch subscription and script-tag filtering behavior.
  • Document the navigation convergence behavior.

Validation

  • src/adapters/iframe.sync.test.ts: 15 tests passing, including load re-sync and detach coverage.
  • SDK full suite: 503 tests passing.
  • oxlint and oxfmt passing.

vanceingalls commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — #2500 fix(sdk): attachSync re-syncs the override snapshot on iframe load

Verdict: LGTM

SSOT check

Single syncOverrides function for both initial and load-time sync. The load listener is correctly added to the iframe element (not the document) and removed on detach. The try/catch with console.warn correctly prevents a failed sync from breaking the ongoing patch subscription.

The race condition this fixes is well-documented: srcdoc assignment is async, so an attachSync call in the same tick snapshots the outgoing document. Patches committed during the load window mirror into the dying document. Re-syncing on load converges the new document.

Tests: 2 cases — override re-applied after load, no re-sync after detach. No blockers.

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 4682da14. Layering on Miga's LGTM.

Blockers

(none)

Concerns

(none)

Nits

(none)

Green notes

🟢 Race characterization is precise. srcdoc/src assignment fires the navigation asynchronously; an attachSync in the same tick captures the OUTGOING document. Patches committed during the load window mirror into a document that's about to be torn down. Re-syncing on load converges the new document with the composition state regardless of attach/navigation ordering — captured cleanly in the docstring comment on line 771-775.

🟢 Listener lifecycle is symmetric. addEventListener("load", syncOverrides) on attach, removeEventListener("load", syncOverrides) on detach — same closure reference so the removal actually takes. Re-attaching via attachSync in the same session first invokes this._syncDetach?.() (line 753), which removes the previous listener + patch subscription, THEN a new syncOverrides closure is created and installed — no listener leak on repeated attach.

🟢 applyOverrideSet is idempotent on repeat loads. Firing load multiple times with the same override set re-establishes the same live-DOM state each time, so the "srcdoc reset mid-edit" path (where the iframe fires load twice without navigation) doesn't accumulate stale writes.

🟢 try/catch around each syncOverrides call is scoped correctly. A bad override snapshot no longer prevents the patch subscription from attaching, and now doesn't prevent the load-driven re-sync from continuing to run either — errors are per-call, not per-attach. Warn message updated from "initial override sync failed" → "override sync failed" reflects that.

🟢 Detach test is genuine. expect(liveTitle.style.getPropertyValue("color")).toBe("#fff") — post-detach, load fires, and the composition's #f00 override is NOT re-applied. Would fail against a naive fix that forgot the removeEventListener call.

Verdict framing

Clean fix for a real navigation race. LGTM from my side.

Review by Rames D Jusso

Base automatically changed from task1-base-variable-defaults to main July 16, 2026 01:10
@vanceingalls
vanceingalls merged commit a2dd403 into main Jul 16, 2026
19 checks passed
@vanceingalls
vanceingalls deleted the task2-iframe-load-resync branch July 16, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants