From 23a3c6cf6b6dd493c7c3b43c8534b303486bf71d Mon Sep 17 00:00:00 2001 From: Sajid Mehmood Date: Tue, 25 Nov 2025 13:17:49 -0500 Subject: [PATCH] Fix dials HMR by using window-based singleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The static class property singleton pattern doesn't survive HMR when module boundaries get crossed. New dials would register with a fresh registry instance while DialsOverlay stayed subscribed to the old one. Fix: Store the singleton on `window.__NITESHIFT_DIALS_REGISTRY__` so it persists across module re-evaluations during hot reload. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/dials/src/registry.ts | 40 ++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/dials/src/registry.ts b/packages/dials/src/registry.ts index e24129dc..01e946c2 100644 --- a/packages/dials/src/registry.ts +++ b/packages/dials/src/registry.ts @@ -25,12 +25,13 @@ function getStorageKey(projectId?: string): string { : `${STORAGE_KEY_PREFIX}-v${STORAGE_VERSION}`; } +/** Key for storing registry on window to survive HMR */ +const REGISTRY_KEY = '__NITESHIFT_DIALS_REGISTRY__'; + /** * Singleton registry for all dials */ class DialRegistry { - private static instance: DialRegistry | null = null; - /** All registered dials */ private dials = new Map(); @@ -43,21 +44,11 @@ class DialRegistry { /** Project ID for storage scoping */ private projectId?: string; - private constructor() { + constructor() { // Load persisted values on initialization this.loadFromStorage(); } - /** - * Get the singleton instance - */ - static getInstance(): DialRegistry { - if (!DialRegistry.instance) { - DialRegistry.instance = new DialRegistry(); - } - return DialRegistry.instance; - } - /** * Set the project ID for storage scoping */ @@ -310,5 +301,26 @@ class DialRegistry { } } +/** + * Get the singleton registry instance + * Uses window storage to survive HMR module re-evaluation + */ +function getGlobalRegistry(): DialRegistry { + if (typeof window !== 'undefined') { + if (!(window as any)[REGISTRY_KEY]) { + (window as any)[REGISTRY_KEY] = new DialRegistry(); + } + return (window as any)[REGISTRY_KEY]; + } + // SSR fallback - module-level instance + if (!ssrInstance) { + ssrInstance = new DialRegistry(); + } + return ssrInstance; +} + +/** SSR fallback instance */ +let ssrInstance: DialRegistry | null = null; + // Export singleton instance getter -export const getDialRegistry = () => DialRegistry.getInstance(); +export const getDialRegistry = getGlobalRegistry;