Fix dials HMR by using window-based singleton

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 <noreply@anthropic.com>
This commit is contained in:
Sajid Mehmood 2025-11-25 13:17:49 -05:00
parent 2727fd6dff
commit 23a3c6cf6b

View file

@ -25,12 +25,13 @@ function getStorageKey(projectId?: string): string {
: `${STORAGE_KEY_PREFIX}-v${STORAGE_VERSION}`; : `${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 * Singleton registry for all dials
*/ */
class DialRegistry { class DialRegistry {
private static instance: DialRegistry | null = null;
/** All registered dials */ /** All registered dials */
private dials = new Map<string, DialRegistration>(); private dials = new Map<string, DialRegistration>();
@ -43,21 +44,11 @@ class DialRegistry {
/** Project ID for storage scoping */ /** Project ID for storage scoping */
private projectId?: string; private projectId?: string;
private constructor() { constructor() {
// Load persisted values on initialization // Load persisted values on initialization
this.loadFromStorage(); 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 * 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 singleton instance getter
export const getDialRegistry = () => DialRegistry.getInstance(); export const getDialRegistry = getGlobalRegistry;