fix(driver): evict animateExit listeners; install DevAPI closures once#256
Merged
Conversation
The animateExit opcode skipped the listener-eviction sweep its sibling destroyNode runs — and the Swift side suppresses destroyNode for the animated root, so every exit-animated unmount whose root carried handlers (every Toast dismissal) leaked a listeners-map entry and kept a stale DOM listener dispatching into evicted handler ids during the animation window. Both opcodes now share one detachListeners sweep; animateExit detaches immediately, matching the Swift-side eviction. DevAPI.installAll re-created its four JSClosures on every render/ unmount without releasing the prior set — each overwrite pinned four entries in JavaScriptKit's static sharedClosures table. The closures read the live renderers set at call time, so a single guarded install is equivalent (the HMRBridge pattern). js-driver: 96 tests, 0 fail (adds the first animateExit driver-layer coverage: immediate detach, post-window removal, parentNode fallback). Whole-package swift test: 1901 green. EmbeddedDriver + 22 example copies regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📦 Bundle size
✅ Within budget (≤5% growth allowed). Baseline: Swift 6.3, WASM SDK 6.3-RELEASE, measured 2026-06-18. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Audit backlog B1 + B3 (the two closure-lifecycle Highs from Parts 2/6).
animateExit listener leak (B1). The opcode skipped the listener-eviction sweep its sibling
destroyNoderuns — and Swift suppressesdestroyNodefor the animated root, so every exit-animated unmount whose root carried handlers (every Toast dismissal) leaked alisteners-map entry and kept a stale DOM listener dispatching into evicted handler ids during the animation window. Both opcodes now share onedetachListenerssweep;animateExitdetaches immediately, matching the Swift-side handler eviction. Adds the first driver-layeranimateExittests (it was the only opcode with zero coverage): immediate detach blocks in-window dispatch, node removed after the window,parentNodefallback.DevAPI closure pinning (B3).
DevAPI.installAll()re-created its fourJSClosures on everyrender(into:)/unmount(into:)and overwrote the fields withoutrelease()— four entries pinned insharedClosuresper cycle in every dev session. The closures read the liverenderersset at call time, so a single guarded install is equivalent (same rationale/pattern as HMRBridge).Verification. js-driver: 96 tests, 0 fail. Whole-package
swift test: 1901 tests green.EmbeddedDriver.swift+ 22 example runtime-JS copies regenerated viaswiflow-codegen driver(embed-freshness gate covers them).🤖 Generated with Claude Code