diff --git a/packages/omniviewdev-runtime/src/hooks/data/usePluginData.ts b/packages/omniviewdev-runtime/src/hooks/data/usePluginData.ts index 71cbe45e..c45cb3e7 100644 --- a/packages/omniviewdev-runtime/src/hooks/data/usePluginData.ts +++ b/packages/omniviewdev-runtime/src/hooks/data/usePluginData.ts @@ -8,6 +8,21 @@ type UsePluginDataResult = { isLoading: boolean; }; +/** + * Check whether a value from the data store structurally matches the expected + * type indicated by the default value. This catches cases where the Go backend + * returns a JSON type that doesn't match the TypeScript generic (e.g. an object + * was stored but the caller expects an array). + */ +function matchesShape(value: unknown, defaultValue: T): value is T { + if (defaultValue === null) return value === null; + if (Array.isArray(defaultValue)) return Array.isArray(value); + if (typeof defaultValue === 'object') { + return typeof value === 'object' && value !== null && !Array.isArray(value); + } + return typeof value === typeof defaultValue; +} + /** * Generic hook for reading/writing plugin data from the Plugin Data Store. * Uses React Query for caching and optimistic updates. @@ -28,6 +43,14 @@ export function usePluginData( if (result === null || result === undefined) { return defaultValue; } + if (!matchesShape(result, defaultValue)) { + console.warn( + `[usePluginData] stored value for "${key}" has unexpected type ` + + `(expected ${Array.isArray(defaultValue) ? 'array' : typeof defaultValue}, ` + + `got ${Array.isArray(result) ? 'array' : typeof result}). Using default.`, + ); + return defaultValue; + } return result as T; }, });