Skip to content

[haxcms-elements] Dispose theme wiring watchdog autorun and fix constructor __disposer orphaning (haxtheweb/issues#3106) - #836

Merged
btopro merged 9 commits into
masterfrom
fix/issue-3106
Oct 2, 2026
Merged

btopro merged 9 commits into
masterfrom
fix/issue-3106

Conversation

@btopro

@btopro btopro commented Oct 2, 2026

Copy link
Copy Markdown
Member

Summary

Fixes the theme teardown autorun leak family from haxtheweb/issues#3106 (deferred from the #3102 fix swarm; exemplar fix PR #832).

  • HAXCMSThemeWiring watchdog disposal (core): the wiring instance created per theme pushed its editor-reconnect watchdog autorun onto its own __disposer array, which nothing ever disposed — every removed theme leaked a live autorun still watching store.jwt / store.themeElement / store.isLoggedIn (one zombie per theme swap). The watchdog is now extracted into a guard-flagged establishDisposers() method, disposeDisposers() is added to the wiring class, the mixin's disconnectedCallback disposes it, and connectedCallback re-establishes it so reconnects keep editor wiring. No behavior change on the first-connect path (the guard prevents a double-push).
  • trayStatus reconnect gap (core): the mixin's connectedCallback re-pushes activeItemContent / editMode / isLoggedIn / manifest / location on every connect but never trayStatus, so a reconnected theme stopped syncing it. A trayStatus store-sync autorun is now pushed on every connect.
  • Constructor this.__disposer = [] reassignment (the simple-blog #18 pattern) dropped in:
    • haxcms-slide-theme — orphaned the 3 constructor-time autoruns from HAXCMSLitElementTheme; its redundant manual dispose loop (which double-disposed before the mixin's disposeDisposers) is also removed
    • terrible-themes + the 4 lib variants — each orphaned 6 constructor-time autoruns (3 HAXCMSLitElementTheme + 2 HAXCMSThemeParts + 1 HAXCMSRememberRoute)
    • outline-player — orphaned 3; the regenerated manifest drops the __disposer field CEM had inferred from that assignment
  • Redundant manual dispose blocks in disconnectedCallback simplified in haxma-theme, spacebook-theme (keydown listener removal kept), twenty-six-theme, and link-card-theme — the shared mixin's disposeDisposers() already handles disposal, and now the wiring watchdog too.

Tests

Mirrors the PR #832 patterns from simple-blog/test/simple-blog-suite.test.js: retain-count, full-disposal-on-disconnect, and no-zombie-reaction assertions for every touched theme; wiring lifecycle tests (establish once / dispose on theme disconnect / re-establish on reconnect) plus trayStatus reconnect tests; the directly instantiated wiring objects in the existing wiring tests are now disposed in afterEach so the suite stops leaking watchdog autoruns itself.

Validation

  • per-element suites: haxcms-elements (12 files, incl. the new haxcms-slide-theme.test.js), terrible-themes, outline-player, haxma-theme, spacebook-theme, twenty-six-theme, link-card-theme — all green
  • regressions: simple-blog, polaris-theme — green
  • four main apps: app-hax, hax-body, h-a-x, haxcms-elements — all green
  • per-element yarn run build (prettier + CEM) run for every touched package; custom-elements.json changes are tool-generated, not hand-edited

Follow-ups found during the audit (not addressed here)

  • haxcms-site-builder.js:820-849 — two bare autorun() calls in the constructor are never pushed to __disposer / never disposed, and its connectedCallback pushes autoruns inside setTimeout(0) that can land after a fast disconnect
  • LTIResizingMixin.js:43 — a bare LMS frame-resize autorun() is never disposed

Fixes haxtheweb/issues#3106

Co-Authored-By: Warp agent@warp.dev

btopro and others added 9 commits October 2, 2026 14:16
…heme disconnect and re-establish it on reconnect (haxtheweb/issues#3106)

The wiring instance created by the HAXCMSTheme mixin constructor pushed its editor-reconnect watchdog autorun onto its own __disposer array, which nothing ever disposed: every removed theme leaked a live autorun still watching store.jwt / store.themeElement / store.isLoggedIn (one zombie per theme swap). Extract the watchdog into a guard-flagged establishDisposers() method, add disposeDisposers() to the wiring class, dispose it from the mixin's disconnectedCallback, and re-establish it from connectedCallback so reconnects keep editor wiring. Also push a trayStatus store-sync autorun on every connect so a reconnected theme keeps syncing trayStatus, previously the one constructor-time sync that was never re-established.

Co-Authored-By: Warp <agent@warp.dev>
…ant dispose loop in haxcms-slide-theme (haxtheweb/issues#3106)

The constructor reassigned __disposer = [] right after super(), orphaning the three constructor-time autoruns pushed by HAXCMSLitElementTheme (editMode/trayStatus/activeItemContent) so removal never disposed them. The disconnectedCallback override also carried a manual dispose loop that duplicated (and double-ran) the mixin's disposeDisposers.

Co-Authored-By: Warp <agent@warp.dev>
…autoruns (haxtheweb/issues#3106)

Every terrible theme constructor reassigned __disposer = [] right after super(), orphaning the six constructor-time autoruns pushed by the class chain (3 from HAXCMSLitElementTheme + 2 from HAXCMSThemeParts + 1 from HAXCMSRememberRoute) so removal never disposed them (zombie autoruns on every removed theme, one per swap).

Co-Authored-By: Warp <agent@warp.dev>
…utoruns (haxtheweb/issues#3106)

The constructor reassigned __disposer = [] right after super(), orphaning the three constructor-time autoruns pushed by HAXCMSLitElementTheme (editMode/trayStatus/activeItemContent) so removal never disposed them. The regenerated manifest drops the __disposer field CEM inferred from that assignment.

Co-Authored-By: Warp <agent@warp.dev>
…eweb/issues#3106)

The manual dispose loop in disconnectedCallback duplicated the shared HAXCMSTheme mixin's disposeDisposers(), which now also disposes the wiring instance's watchdog autorun.

Co-Authored-By: Warp <agent@warp.dev>
…axtheweb/issues#3106)

The manual dispose loop in disconnectedCallback duplicated the shared HAXCMSTheme mixin's disposeDisposers(), which now also disposes the wiring instance's watchdog autorun; the keydown listener removal stays.

Co-Authored-By: Warp <agent@warp.dev>
…axtheweb/issues#3106)

The manual dispose loop in disconnectedCallback duplicated the shared HAXCMSTheme mixin's disposeDisposers(), which now also disposes the wiring instance's watchdog autorun.

Co-Authored-By: Warp <agent@warp.dev>
…axtheweb/issues#3106)

The manual dispose loop in disconnectedCallback duplicated the shared HAXCMSTheme mixin's disposeDisposers(), which now also disposes the wiring instance's watchdog autorun.

Co-Authored-By: Warp <agent@warp.dev>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 18:41
@vercel

vercel Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
webcomponents Error Error Oct 2, 2026 6:42pm UTC

Request Review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The two unrelated HAX browser styling changes should be removed or split into a documented, tested change.

Review effort: Balanced
Findings: 2 Low severity

Open (2)
What changed in this PR

Fixes HAXCMS theme teardown leaks and reconnect synchronization.

Changes:

  • Disposes and re-establishes theme wiring watchdogs.
  • Preserves inherited autorun disposers and removes redundant cleanup.
  • Adds lifecycle tests; also includes two unrelated heading-style changes.
File Description
elements/​hax-body/​lib/​hax-gizmo-browser.js Adds unrelated heading layout styling.
elements/​hax-body/​lib/​hax-stax-browser.js Adds unrelated heading layout styling.
elements/​haxcms-elements/​lib/​core/​HAXCMSThemeWiring.js Manages watchdog and reconnect disposers.
elements/​haxcms-elements/​lib/​core/​themes/​haxcms-slide-theme.js Preserves inherited disposers.
elements/​haxcms-elements/​test/​HAXCMSLitElementTheme.test.js Tests tray-status reconnection.
elements/​haxcms-elements/​test/​HAXCMSThemeWiring.test.js Tests watchdog lifecycle.
elements/​haxcms-elements/​test/​haxcms-slide-theme.test.js Tests slide-theme disposal.
elements/​haxma-theme/​haxma-theme.js Removes redundant cleanup.
elements/​haxma-theme/​test/​haxma-theme.test.js Tests complete disposal.
elements/​link-card-theme/​link-card-theme.js Removes redundant cleanup.
elements/​link-card-theme/​test/​link-card-theme.test.js Verifies wiring disposal.
elements/​outline-player/​custom-elements.json Regenerates component metadata.
elements/​outline-player/​outline-player.js Preserves inherited disposers.
elements/​outline-player/​test/​outline-player.test.js Tests retained and disposed autoruns.
elements/​spacebook-theme/​spacebook-theme.js Delegates cleanup while retaining listener removal.
elements/​spacebook-theme/​test/​spacebook-theme.test.js Tests teardown behavior.
elements/​terrible-themes/​lib/​terrible-best-themes.js Preserves constructor-chain disposers.
elements/​terrible-themes/​lib/​terrible-outlet-themes.js Preserves constructor-chain disposers.
elements/​terrible-themes/​lib/​terrible-productionz-themes.js Preserves constructor-chain disposers.
elements/​terrible-themes/​lib/​terrible-resume-themes.js Preserves constructor-chain disposers.
elements/​terrible-themes/​terrible-themes.js Preserves constructor-chain disposers.
elements/​terrible-themes/​test/​store-driven-suite.test.js Tests all theme disposal paths.
elements/​twenty-six-theme/​test/​twenty-six-theme.test.js Tests shared cleanup behavior.
elements/​twenty-six-theme/​twenty-six-theme.js Removes redundant cleanup.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

}
a11y-collapse::part(heading) {
margin: var(--ddd-spacing-2) 0;
display: block;
}
a11y-collapse::part(heading) {
margin: var(--ddd-spacing-2) 0;
display: block;
@btopro
btopro merged commit 9c96e5a into master Oct 2, 2026
4 of 6 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 2, 2026

This branch had an error being deployed

1 failed deployment
Preview — d5d8dcc6 Deployed Oct 2, 2026 by vercel[bot]
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Themes: constructor __disposer = [] reassignment orphans constructor-time autoruns (simple-blog #18 pattern) + HAXCMSThemeWiring autorun disposal

2 participants