Skip to content

Commit

Permalink
feat: apply Object.freeze in patchState on state in dev mode
Browse files Browse the repository at this point in the history
  • Loading branch information
rainerhahnekamp committed Sep 18, 2024
1 parent 1f7f740 commit ed985d4
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
10 changes: 10 additions & 0 deletions modules/signals/spec/signal-state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,14 @@ describe('signalState', () => {
expect(stateCounter).toBe(3);
expect(userCounter).toBe(1);
}));

it('throws on a mutable change', () => {
const userState = signalState(initialState);
expect(() =>
patchState(userState, (state) => {
state.ngrx = 'mutable change';
return state;
})
).toThrowError("Cannot assign to read only property 'ngrx' of object");
});
});
14 changes: 13 additions & 1 deletion modules/signals/src/state-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import { SIGNAL } from '@angular/core/primitives/signals';
import { Prettify } from './ts-helpers';

declare const ngDevMode: boolean;

const STATE_WATCHERS = new WeakMap<object, Array<StateWatcher<any>>>();

export const STATE_SOURCE = Symbol('STATE_SOURCE');
Expand Down Expand Up @@ -40,7 +42,9 @@ export function patchState<State extends object>(
updaters.reduce(
(nextState: State, updater) => ({
...nextState,
...(typeof updater === 'function' ? updater(nextState) : updater),
...(typeof updater === 'function'
? updater(freezeInDevMode(nextState))
: updater),
}),
currentState
)
Expand All @@ -49,6 +53,14 @@ export function patchState<State extends object>(
notifyWatchers(stateSource);
}

function freezeInDevMode<State extends object>(value: State): State {
if (ngDevMode) {
Object.freeze(value);
}

return value;
}

export function getState<State extends object>(
stateSource: StateSource<State>
): State {
Expand Down

0 comments on commit ed985d4

Please sign in to comment.