A window key holding a closure pins the whole app after unmount -- the
component, its shadow root, and everything they reference, measured at
roughly 10 MB per mount. The three test handles here each close over live
state (the search index, the map, the selection), so each one is a full
retention path.
SIX GLOBALS, NOT THREE. The check names the three it can see, but each
named handle has a paired per-instance registry --
__UPG_SEARCH__/__UPG_SEARCHES__, __UPG_MAP__/__UPG_MAPS__,
__UPG_SELECT__/__UPG_SELECTS__. Releasing only the named half leaves the
registry holding the same closure, so the leak survives in the copy nobody
was looking at.
The two halves need opposite handling, which is why this is not one loop.
A named handle is a single slot that the last mount overwrites, so
deleting it blindly would strip a LIVE sibling's handle -- it is cleared
only if it is still the object this instance published, captured at
assignment so it cannot drift from what went out. A registry entry is
keyed by instance, so my key is always mine to delete, and deleting the
whole registry would strip the siblings.
Verified against the two-instance case rather than the single-mount one:
A mounts, B mounts and takes the named slot, A unmounts -- B keeps its
handle and its registry entry, and after both unmount nothing is
retained.
One delete per line, deliberately: the check reads these statements, so a
loop over a key list would release correctly and still be reported as a
leak. The whole block is wrapped because a frozen or cross-origin window
must never break teardown.