Importing the package kicks off an async init (handleWhenReady in dist/index.js) that is fire-and-forget: it is never awaited or .catch()-ed, and it self-reschedules via setTimeout. Any throw in that background chain therefore surfaces as an unhandled promise rejection in the consuming app. This happens in a normal browser, not just in tests:
- Floating registry fetches. In the ready handler,
window.haNunjucks[registry].fetchRegistry(ha.hass) is called without await/.catch. fetchLabelRegistry does await connection.sendMessagePromise(...), so a transient WebSocket hiccup (e.g. an HA reconnect) rejects unhandled.
- Check/use mismatch. The readiness predicate guards with
ha?.hass?.connected, but the handler then reads ha.hass unguarded. If the home-assistant element or its hass goes away in the await gap (dashboard reload/re-render), it throws.
- Unguarded
document. Both the handler and the predicate call document.querySelector('home-assistant') with no guard, so import in any non-DOM/torn-down context (SSR, unit tests) throws document is not defined from the retry timer.
Repro
Import the module in a context where no connected <home-assistant> is present (a jsdom/node test is the simplest), then let the environment tear down. A pending handleWhenReady retry fires and throws:
ReferenceError: document is not defined
❯ dist/index.js (handler)
❯ handleWhenReady dist/helpers.js
❯ Timeout._onTimeout dist/helpers.js
Suggested fix (behavior-preserving)
.catch() the top-level handleWhenReady(...) call and the floating fetchRegistry(...) promises.
- Guard the DOM access (
typeof document !== 'undefined') and have the handler use the same optional chaining as the predicate.
- Optionally, expose an explicit/lazy init (or skip the global setup when
hass is passed to renderTemplate) so the library is safe to import outside a live browser.
Confirmed present and identical in the latest release (1.7.6).
Importing the package kicks off an async init (
handleWhenReadyindist/index.js) that is fire-and-forget: it is neverawaited or.catch()-ed, and it self-reschedules viasetTimeout. Any throw in that background chain therefore surfaces as an unhandled promise rejection in the consuming app. This happens in a normal browser, not just in tests:window.haNunjucks[registry].fetchRegistry(ha.hass)is called withoutawait/.catch.fetchLabelRegistrydoesawait connection.sendMessagePromise(...), so a transient WebSocket hiccup (e.g. an HA reconnect) rejects unhandled.ha?.hass?.connected, but the handler then readsha.hassunguarded. If thehome-assistantelement or itshassgoes away in theawaitgap (dashboard reload/re-render), it throws.document. Both the handler and the predicate calldocument.querySelector('home-assistant')with no guard, so import in any non-DOM/torn-down context (SSR, unit tests) throwsdocument is not definedfrom the retry timer.Repro
Import the module in a context where no connected
<home-assistant>is present (a jsdom/node test is the simplest), then let the environment tear down. A pendinghandleWhenReadyretry fires and throws:Suggested fix (behavior-preserving)
.catch()the top-levelhandleWhenReady(...)call and the floatingfetchRegistry(...)promises.typeof document !== 'undefined') and have the handler use the same optional chaining as the predicate.hassis passed torenderTemplate) so the library is safe to import outside a live browser.Confirmed present and identical in the latest release (
1.7.6).