fix(frontend): initializeStores dedup + retryAllMessages guard + as any cleanup
- index.ts: add _storesInitialized guard to prevent triple initialization - offlineStore.ts: add isRetrying mutex for retryAllMessages concurrency - PropertyPanel.tsx: replace 13x (data as any) with typed d accessor - chatStore.ts: replace window as any with Record<string, unknown> - kernel-*.ts: replace prototype as any with Record<string, unknown> - gateway-heartbeat.ts: delete dead code (9 as any, zero imports)
This commit is contained in:
@@ -354,8 +354,10 @@ if (import.meta.hot) {
|
||||
|
||||
// Dev-only: Expose stores to window for E2E testing
|
||||
if (import.meta.env.DEV && typeof window !== 'undefined') {
|
||||
(window as any).__ZCLAW_STORES__ = (window as any).__ZCLAW_STORES__ || {};
|
||||
(window as any).__ZCLAW_STORES__.chat = useChatStore;
|
||||
(window as any).__ZCLAW_STORES__.message = useMessageStore;
|
||||
(window as any).__ZCLAW_STORES__.stream = useStreamStore;
|
||||
const w = window as Record<string, unknown>;
|
||||
w.__ZCLAW_STORES__ = (w.__ZCLAW_STORES__ as Record<string, unknown>) || {};
|
||||
const stores = w.__ZCLAW_STORES__ as Record<string, unknown>;
|
||||
stores.chat = useChatStore;
|
||||
stores.message = useMessageStore;
|
||||
stores.stream = useStreamStore;
|
||||
}
|
||||
|
||||
@@ -84,11 +84,16 @@ import { setConfigStoreClient } from './configStore';
|
||||
import { setSecurityStoreClient } from './securityStore';
|
||||
import { setSessionStoreClient } from './sessionStore';
|
||||
|
||||
let _storesInitialized = false;
|
||||
|
||||
/**
|
||||
* Initialize all stores with the shared client.
|
||||
* Called once when the application mounts.
|
||||
* Guard ensures it only runs once even if called from multiple connection paths.
|
||||
*/
|
||||
export function initializeStores(): void {
|
||||
if (_storesInitialized) return;
|
||||
_storesInitialized = true;
|
||||
const client = getClient();
|
||||
|
||||
// Inject client into all stores
|
||||
|
||||
@@ -86,6 +86,7 @@ function generateMessageId(): string {
|
||||
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let healthCheckInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let isRetrying = false;
|
||||
|
||||
export const useOfflineStore = create<OfflineStore>()(
|
||||
persist(
|
||||
@@ -186,6 +187,12 @@ export const useOfflineStore = create<OfflineStore>()(
|
||||
},
|
||||
|
||||
retryAllMessages: async () => {
|
||||
if (isRetrying) {
|
||||
log.debug('retryAllMessages already in progress, skipping');
|
||||
return;
|
||||
}
|
||||
isRetrying = true;
|
||||
try {
|
||||
const state = get();
|
||||
const pending = state.queuedMessages.filter(
|
||||
(m) => m.status === 'pending' || m.status === 'failed'
|
||||
@@ -230,6 +237,9 @@ export const useOfflineStore = create<OfflineStore>()(
|
||||
log.warn(`Message ${msg.id} failed:`, errorMessage);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isRetrying = false;
|
||||
}
|
||||
},
|
||||
|
||||
// === Reconnection ===
|
||||
|
||||
Reference in New Issue
Block a user