diff --git a/src/Squawk.ts b/src/Squawk.ts index 0355076..f97058d 100644 --- a/src/Squawk.ts +++ b/src/Squawk.ts @@ -131,8 +131,12 @@ export default function createStore(initialState: Required, useReduxDevToo // If Redux dev tools emitted a dispatch, and the state has a value if (message.type === "DISPATCH" && message.state) { // Deserialize the value and dispatch updates to all subscribers - globalState.set(JSON.parse(message.state)); - notifySubscribers(globalState.keys()); + try { + globalState.set(JSON.parse(message.state)); + notifySubscribers(globalState.keys()); + } catch { + // Ignore malformed JSON + } } }); } diff --git a/src/__tests__/Squawk.redux.test.ts b/src/__tests__/Squawk.redux.test.ts new file mode 100644 index 0000000..cc4c0c1 --- /dev/null +++ b/src/__tests__/Squawk.redux.test.ts @@ -0,0 +1,33 @@ +import createStore from "../Squawk"; + +describe("Redux DevTools integration", () => { + it("handles malformed JSON gracefully", () => { + const devToolsSubscribeCallbacks: Array<(msg: { type: string; state: string }) => void> = []; + // eslint-disable-next-line immutable/no-mutation, @typescript-eslint/no-explicit-any + (global as any).window = { + __REDUX_DEVTOOLS_EXTENSION__: { + connect: () => ({ + subscribe: (cb: (msg: { type: string; state: string }) => void) => { + devToolsSubscribeCallbacks.push(cb); + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + send: () => {}, + // eslint-disable-next-line @typescript-eslint/no-empty-function + init: () => {} + }) + } + }; + + const store = createStore({ foo: "bar" }, true); + + expect(() => { + devToolsSubscribeCallbacks[0]({ type: "DISPATCH", state: "invalid json" }); + }).not.toThrow(); + + expect(store.get().foo).toBe("bar"); + + // clean up + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (global as any).window; + }); +});