Skip to content

fix: AvalComponent failing to mount on Safari 26 - #6

Merged
lnikell merged 1 commit into
pixel-point:mainfrom
huntabyte:fix/element-definitioon
Sep 25, 2026
Merged

lnikell merged 1 commit into
pixel-point:mainfrom
huntabyte:fix/element-definitioon

Conversation

@huntabyte

@huntabyte huntabyte commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On Safari 26.x (iOS and macOS), mounting <AvalComponent> from @pixel-point/aval-svelte throws:

TypeError: Registered aval-player does not implement the required snapshot API

The adapter binding called defineAvalElement() lazily inside attach. By then Svelte had already created the <aval-player> host from a compiled template. Svelte creates custom-element hosts with document.importNode.

WebKit only assigns the global registry to template nodes after window.customElements has been accessed at least once. Nodes created from a template before that get customElementRegistry === null and never upgrade: not when define() runs, not when connected, and not after customElements.upgrade(node). In a production Svelte app, nothing touches customElements before the first render, so the host stays a plain HTMLElement and attach throws.

Minimal WebKit repro:

const t = document.createElement("template");
t.innerHTML = "<x-a></x-a>";
const el = document.importNode(t.content, true).firstChild;
document.body.append(el);
customElements.define("x-a", class extends HTMLElement { api() {} });
typeof el.api;                 // webkit: "undefined", chromium: "function"
el.customElementRegistry;      // webkit: null

Accessing window.customElements before creating the template makes the same code work in WebKit.

The bug doesn't show up under the Vite dev server. The Vite client defines vite-error-overlay before the app runs, which initializes the registry.

Fix

  • aval-svelte: createAval now calls defineAvalElement() when customElements is available. createAval runs in the component script, before Svelte builds any template, so the element is always defined before a host can exist. During SSR customElements is undefined and the call is skipped.
  • aval-element adapter binding: if a node still lacks the snapshot API after defineAvalElement(), upgrade() calls customElements.upgrade(node) once before throwing. This covers hosts that were created before define() and are still disconnected. Such hosts aren't upgraded by define() in any engine.

Tests

New Playwright specs in packages/svelte/test/browser/late-definition.spec.ts. They run in Chromium and in WebKit; the WebKit project runs against a production vite build + vite preview, so the dev client can't hide the bug.

  • Attaches a host imported from a template before registration: creates a controller, imports <aval-player> from a template, and calls attach. Fails on WebKit without the fix, with the exact reported error.
  • Attaches a disconnected host defined after it was imported: fails in both engines without the customElements.upgrade() fallback.
  • Mounts AvalComponent without the page registering aval-player: end-to-end sanity check in both engines.

@vercel

vercel Bot commented Sep 25, 2026

Copy link
Copy Markdown

@huntabyte is attempting to deploy a commit to the pixelpoint Team on Vercel.

A member of the Team first needs to authorize it.

@lnikell
lnikell merged commit e791915 into pixel-point:main Sep 25, 2026
1 check failed
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.

2 participants