fix: defer load() until connected to prevent metadata race on first Mux Data beacon (#1326)
## Problem
Fixes #1199
When `<mux-player>` (or `<mux-video>`) is created programmatically and
attributes are set before the element is appended to the DOM — with any
async yield between attribute setting and appendChild (e.g. await
Promise.resolve(), React's async reconciliation, Next.js rendering,
etc.) — the first Mux Data analytics beacon is sent with an incorrect
`video_id`.
Root cause: `mux-video.#requestLoad()` schedules `load()` in a microtask
via `await Promise.resolve()`. Under the following sequence, that
microtask fires before `mux-player.connectedCallback` has a chance to
propagate metadata-* attributes to the inner `<mux-video>`:
```
const el = document.createElement('mux-player');
el.setAttribute('playback-id', '...');
el.setAttribute('metadata-video-id', 'my-custom-video-id'); // not in observedAttributes
await Promise.resolve(); // ← load() microtask fires here — metadata is empty
document.body.appendChild(el); // connectedCallback propagates metadata too late
```
metadata-* attributes are not in mux-video's observedAttributes. They
are read lazily from the element's own DOM attributes by the metadata
getter. Since mux-player's template does not forward metadata-*
attributes to the inner <mux-video>, propagation only happens in
`mux-player.connectedCallback`. Any async gap before appendChild causes
the first `mux.monitor()` call — which sets `video_id` permanently for
the view — to receive empty metadata. Because video_id is fixed at view
start and cannot be corrected retroactively, the beacon is permanently
wrong.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches custom element lifecycle and overrides
`setAttribute`/`removeAttribute`, which can subtly affect attribute
semantics and timing-sensitive load/analytics behavior.
>
> **Overview**
> Fixes a race where `metadata-*` attributes set on `<mux-player>`
before DOM connection could be missed by the inner `<mux-video>` at
initial load time.
>
> `mux-player` now (1) seeds `mux-video.metadata` during `#init()` for
any pre-existing `metadata-*` attributes, and (2) overrides
`setAttribute`/`removeAttribute` to synchronously re-sync `metadata`
whenever any `metadata-*` attribute changes (since these attributes are
open-ended and not covered by `observedAttributes`).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8bca459e69d8c003ee379169528024cfb7fc4668. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY --> R
Renzo Delfino committed
1cca3ad431fa12ca08f2a707e1265ae2f1709928
Parent: 65d44fc
Committed by GitHub <noreply@github.com>
on 5/14/2026, 7:06:30 PM