@nice-code/state 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/Store-B65MojT2.d.ts +201 -0
- package/build/Store-CI9N0P6I.js +366 -0
- package/build/Store-CI9N0P6I.js.map +1 -0
- package/build/Store-PjfFkZ2I.js +349 -0
- package/build/Store-PjfFkZ2I.js.map +1 -0
- package/build/devtools/browser/index.d.ts +120 -0
- package/build/devtools/browser/index.js +2750 -2357
- package/build/devtools/browser/index.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -244
- package/build/react/index.d.ts +58 -0
- package/build/react/index.js +59 -308
- package/build/react/index.js.map +1 -0
- package/package.json +29 -26
- package/build/types/core/Store.d.ts +0 -136
- package/build/types/core/index.d.ts +0 -1
- package/build/types/devtools/browser/NiceStateDevtools.d.ts +0 -10
- package/build/types/devtools/browser/components/ChangeDetailPanel.d.ts +0 -12
- package/build/types/devtools/browser/components/ChangeList.d.ts +0 -9
- package/build/types/devtools/browser/components/DiffView.d.ts +0 -13
- package/build/types/devtools/browser/components/JsonDiffView.d.ts +0 -24
- package/build/types/devtools/browser/components/JsonView.d.ts +0 -7
- package/build/types/devtools/browser/components/PanelChrome.d.ts +0 -54
- package/build/types/devtools/browser/components/SectionLabel.d.ts +0 -4
- package/build/types/devtools/browser/components/StateInspector.d.ts +0 -16
- package/build/types/devtools/browser/components/StoreTabs.d.ts +0 -12
- package/build/types/devtools/browser/components/utils.d.ts +0 -98
- package/build/types/devtools/browser/devtools_dock.d.ts +0 -54
- package/build/types/devtools/browser/index.d.ts +0 -3
- package/build/types/devtools/core/StateDevtools.types.d.ts +0 -43
- package/build/types/devtools/core/StateDevtoolsCore.d.ts +0 -56
- package/build/types/devtools/core/devtools_colors.d.ts +0 -26
- package/build/types/index.d.ts +0 -1
- package/build/types/react/InjectStoreState.d.ts +0 -18
- package/build/types/react/index.d.ts +0 -3
- package/build/types/react/useLocalStore.d.ts +0 -8
- package/build/types/react/useStoreState.d.ts +0 -23
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Draft, Patch, PatchListener } from "immer";
|
|
2
|
+
|
|
3
|
+
//#region src/core/Store.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A plain pub/sub listener. Fired (in the order added) every time the store's
|
|
6
|
+
* root state reference changes. This is the vanilla integration point that any
|
|
7
|
+
* ecosystem — including the React adapter's `useSyncExternalStore` — builds on.
|
|
8
|
+
*/
|
|
9
|
+
type TUpdateListener = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* @typeParam S The store's state
|
|
12
|
+
* @param draft The mutable draft to change during this update (Immer proxy).
|
|
13
|
+
* @param original A readonly snapshot of the state as it was before this update.
|
|
14
|
+
*/
|
|
15
|
+
type TUpdateFunction<S> = (draft: Draft<S>, original: S) => void;
|
|
16
|
+
/**
|
|
17
|
+
* A selector that derives a watched slice `T` from the full store state `S`.
|
|
18
|
+
*/
|
|
19
|
+
type TStoreWatch<S extends object, T> = (state: S) => T;
|
|
20
|
+
/**
|
|
21
|
+
* Fired by {@link Store.watch} whenever the watched slice changes structurally.
|
|
22
|
+
*/
|
|
23
|
+
type TStoreSubscriptionListener<S extends object, T> = (watched: T, allState: S, previousWatched: T) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Runs inside an Immer `produce` whenever a watched slice changes, letting the
|
|
26
|
+
* store derive further state from its own mutations.
|
|
27
|
+
*/
|
|
28
|
+
type TReactionFunction<S extends object, T> = (watched: T, draft: Draft<S>, original: S, previousWatched: T) => void;
|
|
29
|
+
type TStoreActionUpdate<S extends object> = (updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback) => void;
|
|
30
|
+
type TStoreAction<S extends object> = (update: TStoreActionUpdate<S>) => void;
|
|
31
|
+
type TPatchesCallback = (patches: Patch[], inversePatches: Patch[]) => void;
|
|
32
|
+
/**
|
|
33
|
+
* @internal
|
|
34
|
+
* Drives a registered reaction. `forceRun` bypasses the change-detection
|
|
35
|
+
* fast-path (used when a reaction is created with `runNow`).
|
|
36
|
+
*/
|
|
37
|
+
type TRunReactionFunction = (forceRun?: boolean) => void;
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
* Binds a reaction definition to a concrete store instance.
|
|
41
|
+
*/
|
|
42
|
+
type TReactionCreator<S extends object> = (store: Store<S>) => TRunReactionFunction;
|
|
43
|
+
interface IStoreInternalOptions<S extends object> {
|
|
44
|
+
ssr: boolean;
|
|
45
|
+
reactionCreators?: TReactionCreator<S>[];
|
|
46
|
+
}
|
|
47
|
+
interface ICreateReactionOptions {
|
|
48
|
+
runNow?: boolean;
|
|
49
|
+
runNowWithSideEffects?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A framework-agnostic, Immer-backed state container.
|
|
53
|
+
*
|
|
54
|
+
* The store owns a single immutable state value and a `Set` of plain
|
|
55
|
+
* listeners. Every mutation runs through Immer's `produce`, and listeners are
|
|
56
|
+
* only notified when the resulting root reference actually changes — making
|
|
57
|
+
* "no-op" updates genuinely free for subscribers.
|
|
58
|
+
*
|
|
59
|
+
* @typeParam S Your store's state interface.
|
|
60
|
+
*/
|
|
61
|
+
declare class Store<S extends object = object> {
|
|
62
|
+
private currentState;
|
|
63
|
+
private readonly createInitialState;
|
|
64
|
+
private ssr;
|
|
65
|
+
/** Plain pub/sub listeners — a Set both dedupes and dispatches fast. */
|
|
66
|
+
private readonly listeners;
|
|
67
|
+
private reactionCreators;
|
|
68
|
+
private reactions;
|
|
69
|
+
private clientSubscriptions;
|
|
70
|
+
/**
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
_patchListeners: PatchListener[];
|
|
74
|
+
constructor(initialState: S | (() => S));
|
|
75
|
+
get state(): S;
|
|
76
|
+
/**
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
_setInternalOptions({
|
|
80
|
+
ssr,
|
|
81
|
+
reactionCreators
|
|
82
|
+
}: IStoreInternalOptions<S>): void;
|
|
83
|
+
/**
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
_getReactionCreators(): TReactionCreator<S>[];
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
_instantiateReactions(): void;
|
|
91
|
+
/**
|
|
92
|
+
* @internal
|
|
93
|
+
*/
|
|
94
|
+
_getInitialState(): S;
|
|
95
|
+
/**
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
_hasPatchListeners(): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
_emitPatches(patches: Patch[], inversePatches: Patch[]): void;
|
|
103
|
+
/**
|
|
104
|
+
* @internal
|
|
105
|
+
*
|
|
106
|
+
* Writes a new state value without running reactions. Used by reactions
|
|
107
|
+
* themselves to avoid re-entrancy.
|
|
108
|
+
*/
|
|
109
|
+
_updateStateWithoutReaction(nextState: S): void;
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*
|
|
113
|
+
* Commits a new root state, runs reactions, then dispatches to all
|
|
114
|
+
* subscribers. Callers are responsible for the `nextState !== currentState`
|
|
115
|
+
* reference guard before invoking this.
|
|
116
|
+
*/
|
|
117
|
+
_updateState(nextState: S): void;
|
|
118
|
+
/**
|
|
119
|
+
* Returns the raw state object contained within this store at this moment.
|
|
120
|
+
*
|
|
121
|
+
* ---
|
|
122
|
+
* ** WARNING **
|
|
123
|
+
*
|
|
124
|
+
* Most of the time, if you're using this in your app, there's probably a
|
|
125
|
+
* better way to do it (a selector subscription or the React adapter).
|
|
126
|
+
* ---
|
|
127
|
+
*/
|
|
128
|
+
getRawState(): S;
|
|
129
|
+
/**
|
|
130
|
+
* Subscribe a plain listener to store mutations. The listener fires once per
|
|
131
|
+
* committed update (root reference change) and receives no arguments — read
|
|
132
|
+
* the latest value with {@link getRawState}.
|
|
133
|
+
*
|
|
134
|
+
* This is the low-level primitive that vanilla code and framework adapters
|
|
135
|
+
* (e.g. React's `useSyncExternalStore`) build upon.
|
|
136
|
+
*
|
|
137
|
+
* @returns An unsubscribe function.
|
|
138
|
+
*/
|
|
139
|
+
subscribe(listener: TUpdateListener): () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Subscribe to a derived slice of state. The `listener` only fires when the
|
|
142
|
+
* watched value changes structurally (per the Fast-Path rule).
|
|
143
|
+
*
|
|
144
|
+
* @returns An unsubscribe function.
|
|
145
|
+
*/
|
|
146
|
+
watch<T>(watch: TStoreWatch<S, T>, listener: TStoreSubscriptionListener<S, T>): () => void;
|
|
147
|
+
/**
|
|
148
|
+
* Register a reaction: when the watched slice changes, the `reaction` recipe
|
|
149
|
+
* runs inside Immer to derive further state on the same store.
|
|
150
|
+
*
|
|
151
|
+
* @returns A function that removes the reaction.
|
|
152
|
+
*/
|
|
153
|
+
createReaction<T>(watch: TStoreWatch<S, T>, reaction: TReactionFunction<S, T>, {
|
|
154
|
+
runNow,
|
|
155
|
+
runNowWithSideEffects
|
|
156
|
+
}?: ICreateReactionOptions): () => void;
|
|
157
|
+
/**
|
|
158
|
+
* Subscribe to the Immer patches produced by each update.
|
|
159
|
+
*
|
|
160
|
+
* @returns An unsubscribe function.
|
|
161
|
+
*/
|
|
162
|
+
listenToPatches(patchListener: PatchListener): () => void;
|
|
163
|
+
/**
|
|
164
|
+
* Mutate the store via one or more Immer update functions.
|
|
165
|
+
*
|
|
166
|
+
* @param updater A single update function, or an array applied in order.
|
|
167
|
+
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
168
|
+
*/
|
|
169
|
+
update(updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback): void;
|
|
170
|
+
/**
|
|
171
|
+
* Replace the store's state entirely with a new state value.
|
|
172
|
+
*/
|
|
173
|
+
replace(newState: S): void;
|
|
174
|
+
/**
|
|
175
|
+
* Replace the store's state by mapping from the current state.
|
|
176
|
+
*/
|
|
177
|
+
replaceFromCurrent(replacer: (state: S) => S): void;
|
|
178
|
+
/**
|
|
179
|
+
* Apply a set of Immer patches to the store.
|
|
180
|
+
*/
|
|
181
|
+
applyPatches(patches: Patch[]): void;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Apply Immer patches to a store, committing only if the root reference changes.
|
|
185
|
+
*/
|
|
186
|
+
declare function applyPatchesToStore<S extends object = object>(store: Store<S>, patches: Patch[]): void;
|
|
187
|
+
/**
|
|
188
|
+
* Mutate a store via one or more Immer update functions.
|
|
189
|
+
*
|
|
190
|
+
* Patches are only computed when there's a consumer for them (a registered
|
|
191
|
+
* patch listener or a `patchesCallback`), keeping the common path allocation-free.
|
|
192
|
+
* Listeners are notified only when the root reference actually changes.
|
|
193
|
+
*
|
|
194
|
+
* @param store The store to update.
|
|
195
|
+
* @param updater A single update function, or an array applied in order.
|
|
196
|
+
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
197
|
+
*/
|
|
198
|
+
declare function update<S extends object = object>(store: Store<S>, updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback): void;
|
|
199
|
+
//#endregion
|
|
200
|
+
export { TReactionFunction as a, TStoreSubscriptionListener as c, TUpdateListener as d, applyPatchesToStore as f, TPatchesCallback as i, TStoreWatch as l, IStoreInternalOptions as n, TStoreAction as o, update as p, Store as r, TStoreActionUpdate as s, ICreateReactionOptions as t, TUpdateFunction as u };
|
|
201
|
+
//# sourceMappingURL=Store-B65MojT2.d.ts.map
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
let fast_equals = require("fast-equals");
|
|
2
|
+
let immer = require("immer");
|
|
3
|
+
//#region src/core/Store.ts
|
|
4
|
+
(0, immer.enablePatches)();
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*
|
|
8
|
+
* Builds the per-emission runner for a selector subscription.
|
|
9
|
+
*
|
|
10
|
+
* Change detection follows the "Fast-Path" rule: Immer's structural sharing
|
|
11
|
+
* means an untouched branch keeps its exact reference, so a strict `===` check
|
|
12
|
+
* resolves the overwhelmingly common "nothing changed" case in 0ms. Only when
|
|
13
|
+
* the reference actually changes do we pay for a `deepEqual` traversal to
|
|
14
|
+
* confirm the change is structural and not just a new-but-equal reference.
|
|
15
|
+
*/
|
|
16
|
+
function makeSubscriptionFunction(store, watch, listener) {
|
|
17
|
+
let lastWatchState = watch(store.getRawState());
|
|
18
|
+
return () => {
|
|
19
|
+
const currentState = store.getRawState();
|
|
20
|
+
const nextWatchState = watch(currentState);
|
|
21
|
+
if (nextWatchState === lastWatchState) return;
|
|
22
|
+
if ((0, fast_equals.deepEqual)(nextWatchState, lastWatchState)) {
|
|
23
|
+
lastWatchState = nextWatchState;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const previousWatched = lastWatchState;
|
|
27
|
+
lastWatchState = nextWatchState;
|
|
28
|
+
listener(nextWatchState, currentState, previousWatched);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*
|
|
34
|
+
* Builds a reaction creator. Reactions follow the same Fast-Path rule as
|
|
35
|
+
* subscriptions, then run their recipe inside Immer to derive further state.
|
|
36
|
+
*/
|
|
37
|
+
function makeReactionFunctionCreator(watch, reaction) {
|
|
38
|
+
return (store) => {
|
|
39
|
+
let lastWatchState = watch(store.getRawState());
|
|
40
|
+
return (forceRun = false) => {
|
|
41
|
+
const currentState = store.getRawState();
|
|
42
|
+
const nextWatchState = watch(currentState);
|
|
43
|
+
if (!forceRun) {
|
|
44
|
+
if (nextWatchState === lastWatchState) return;
|
|
45
|
+
if ((0, fast_equals.deepEqual)(nextWatchState, lastWatchState)) {
|
|
46
|
+
lastWatchState = nextWatchState;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const previousWatched = lastWatchState;
|
|
51
|
+
lastWatchState = nextWatchState;
|
|
52
|
+
if (store._hasPatchListeners()) {
|
|
53
|
+
const [nextState, patches, inversePatches] = (0, immer.produceWithPatches)(currentState, (draft) => {
|
|
54
|
+
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
55
|
+
});
|
|
56
|
+
store._updateStateWithoutReaction(nextState);
|
|
57
|
+
if (patches.length > 0) store._emitPatches(patches, inversePatches);
|
|
58
|
+
} else {
|
|
59
|
+
const nextState = (0, immer.produce)(currentState, (draft) => {
|
|
60
|
+
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
61
|
+
});
|
|
62
|
+
store._updateStateWithoutReaction(nextState);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A framework-agnostic, Immer-backed state container.
|
|
69
|
+
*
|
|
70
|
+
* The store owns a single immutable state value and a `Set` of plain
|
|
71
|
+
* listeners. Every mutation runs through Immer's `produce`, and listeners are
|
|
72
|
+
* only notified when the resulting root reference actually changes — making
|
|
73
|
+
* "no-op" updates genuinely free for subscribers.
|
|
74
|
+
*
|
|
75
|
+
* @typeParam S Your store's state interface.
|
|
76
|
+
*/
|
|
77
|
+
var Store = class {
|
|
78
|
+
currentState;
|
|
79
|
+
createInitialState;
|
|
80
|
+
ssr = false;
|
|
81
|
+
/** Plain pub/sub listeners — a Set both dedupes and dispatches fast. */
|
|
82
|
+
listeners = /* @__PURE__ */ new Set();
|
|
83
|
+
reactionCreators = [];
|
|
84
|
+
reactions = [];
|
|
85
|
+
clientSubscriptions = [];
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
_patchListeners = [];
|
|
90
|
+
constructor(initialState) {
|
|
91
|
+
if (initialState instanceof Function) {
|
|
92
|
+
this.createInitialState = initialState;
|
|
93
|
+
this.currentState = initialState();
|
|
94
|
+
} else {
|
|
95
|
+
this.createInitialState = () => initialState;
|
|
96
|
+
this.currentState = initialState;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
get state() {
|
|
100
|
+
return Object.freeze(this.currentState);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
_setInternalOptions({ ssr, reactionCreators = [] }) {
|
|
106
|
+
this.ssr = ssr;
|
|
107
|
+
this.reactionCreators = reactionCreators;
|
|
108
|
+
this.reactions = reactionCreators.map((rc) => rc(this));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
_getReactionCreators() {
|
|
114
|
+
return this.reactionCreators;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
_instantiateReactions() {
|
|
120
|
+
this.reactions = this.reactionCreators.map((rc) => rc(this));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @internal
|
|
124
|
+
*/
|
|
125
|
+
_getInitialState() {
|
|
126
|
+
return this.createInitialState();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
_hasPatchListeners() {
|
|
132
|
+
return this._patchListeners.length > 0;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
137
|
+
_emitPatches(patches, inversePatches) {
|
|
138
|
+
for (const listener of this._patchListeners) listener(patches, inversePatches);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @internal
|
|
142
|
+
*
|
|
143
|
+
* Writes a new state value without running reactions. Used by reactions
|
|
144
|
+
* themselves to avoid re-entrancy.
|
|
145
|
+
*/
|
|
146
|
+
_updateStateWithoutReaction(nextState) {
|
|
147
|
+
this.currentState = nextState;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @internal
|
|
151
|
+
*
|
|
152
|
+
* Commits a new root state, runs reactions, then dispatches to all
|
|
153
|
+
* subscribers. Callers are responsible for the `nextState !== currentState`
|
|
154
|
+
* reference guard before invoking this.
|
|
155
|
+
*/
|
|
156
|
+
_updateState(nextState) {
|
|
157
|
+
this.currentState = nextState;
|
|
158
|
+
for (const runReaction of this.reactions) runReaction();
|
|
159
|
+
if (this.ssr) return;
|
|
160
|
+
for (const runSubscription of this.clientSubscriptions) runSubscription();
|
|
161
|
+
for (const listener of this.listeners) listener();
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns the raw state object contained within this store at this moment.
|
|
165
|
+
*
|
|
166
|
+
* ---
|
|
167
|
+
* ** WARNING **
|
|
168
|
+
*
|
|
169
|
+
* Most of the time, if you're using this in your app, there's probably a
|
|
170
|
+
* better way to do it (a selector subscription or the React adapter).
|
|
171
|
+
* ---
|
|
172
|
+
*/
|
|
173
|
+
getRawState() {
|
|
174
|
+
return this.currentState;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Subscribe a plain listener to store mutations. The listener fires once per
|
|
178
|
+
* committed update (root reference change) and receives no arguments — read
|
|
179
|
+
* the latest value with {@link getRawState}.
|
|
180
|
+
*
|
|
181
|
+
* This is the low-level primitive that vanilla code and framework adapters
|
|
182
|
+
* (e.g. React's `useSyncExternalStore`) build upon.
|
|
183
|
+
*
|
|
184
|
+
* @returns An unsubscribe function.
|
|
185
|
+
*/
|
|
186
|
+
subscribe(listener) {
|
|
187
|
+
this.listeners.add(listener);
|
|
188
|
+
return () => {
|
|
189
|
+
this.listeners.delete(listener);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Subscribe to a derived slice of state. The `listener` only fires when the
|
|
194
|
+
* watched value changes structurally (per the Fast-Path rule).
|
|
195
|
+
*
|
|
196
|
+
* @returns An unsubscribe function.
|
|
197
|
+
*/
|
|
198
|
+
watch(watch, listener) {
|
|
199
|
+
if (this.ssr) return () => {
|
|
200
|
+
console.warn(`@nice-code/state: Subscriptions made on the server side are not registered, so this unsubscribe call does nothing.`);
|
|
201
|
+
};
|
|
202
|
+
const run = makeSubscriptionFunction(this, watch, listener);
|
|
203
|
+
this.clientSubscriptions.push(run);
|
|
204
|
+
return () => {
|
|
205
|
+
this.clientSubscriptions = this.clientSubscriptions.filter((f) => f !== run);
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Register a reaction: when the watched slice changes, the `reaction` recipe
|
|
210
|
+
* runs inside Immer to derive further state on the same store.
|
|
211
|
+
*
|
|
212
|
+
* @returns A function that removes the reaction.
|
|
213
|
+
*/
|
|
214
|
+
createReaction(watch, reaction, { runNow = false, runNowWithSideEffects = false } = {}) {
|
|
215
|
+
const creator = makeReactionFunctionCreator(watch, reaction);
|
|
216
|
+
this.reactionCreators.push(creator);
|
|
217
|
+
const run = creator(this);
|
|
218
|
+
this.reactions.push(run);
|
|
219
|
+
if (runNow || runNowWithSideEffects) {
|
|
220
|
+
run(true);
|
|
221
|
+
if (runNowWithSideEffects && !this.ssr) this._updateState(this.currentState);
|
|
222
|
+
}
|
|
223
|
+
return () => {
|
|
224
|
+
this.reactions = this.reactions.filter((f) => f !== run);
|
|
225
|
+
this.reactionCreators = this.reactionCreators.filter((f) => f !== creator);
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Subscribe to the Immer patches produced by each update.
|
|
230
|
+
*
|
|
231
|
+
* @returns An unsubscribe function.
|
|
232
|
+
*/
|
|
233
|
+
listenToPatches(patchListener) {
|
|
234
|
+
this._patchListeners.push(patchListener);
|
|
235
|
+
return () => {
|
|
236
|
+
this._patchListeners = this._patchListeners.filter((f) => f !== patchListener);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Mutate the store via one or more Immer update functions.
|
|
241
|
+
*
|
|
242
|
+
* @param updater A single update function, or an array applied in order.
|
|
243
|
+
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
244
|
+
*/
|
|
245
|
+
update(updater, patchesCallback) {
|
|
246
|
+
update(this, updater, patchesCallback);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Replace the store's state entirely with a new state value.
|
|
250
|
+
*/
|
|
251
|
+
replace(newState) {
|
|
252
|
+
if (newState !== this.currentState) this._updateState(newState);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Replace the store's state by mapping from the current state.
|
|
256
|
+
*/
|
|
257
|
+
replaceFromCurrent(replacer) {
|
|
258
|
+
const nextState = replacer(this.currentState);
|
|
259
|
+
if (nextState !== this.currentState) this._updateState(nextState);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Apply a set of Immer patches to the store.
|
|
263
|
+
*/
|
|
264
|
+
applyPatches(patches) {
|
|
265
|
+
applyPatchesToStore(this, patches);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Apply Immer patches to a store, committing only if the root reference changes.
|
|
270
|
+
*/
|
|
271
|
+
function applyPatchesToStore(store, patches) {
|
|
272
|
+
const currentState = store.getRawState();
|
|
273
|
+
const nextState = (0, immer.applyPatches)(currentState, patches);
|
|
274
|
+
if (nextState !== currentState) store._updateState(nextState);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @internal
|
|
278
|
+
*
|
|
279
|
+
* Runs an updater (or array of updaters) through Immer, collecting patches.
|
|
280
|
+
*/
|
|
281
|
+
function applyUpdaterWithPatches(currentState, updater) {
|
|
282
|
+
if (typeof updater === "function") {
|
|
283
|
+
const [next, patches, inversePatches] = (0, immer.produceWithPatches)(currentState, (draft) => {
|
|
284
|
+
updater(draft, currentState);
|
|
285
|
+
});
|
|
286
|
+
return [
|
|
287
|
+
next,
|
|
288
|
+
patches,
|
|
289
|
+
inversePatches
|
|
290
|
+
];
|
|
291
|
+
}
|
|
292
|
+
let state = currentState;
|
|
293
|
+
const patches = [];
|
|
294
|
+
const inversePatches = [];
|
|
295
|
+
for (const single of updater) {
|
|
296
|
+
const [next, p, ip] = (0, immer.produceWithPatches)(state, (draft) => {
|
|
297
|
+
single(draft, state);
|
|
298
|
+
});
|
|
299
|
+
state = next;
|
|
300
|
+
patches.push(...p);
|
|
301
|
+
inversePatches.push(...ip);
|
|
302
|
+
}
|
|
303
|
+
return [
|
|
304
|
+
state,
|
|
305
|
+
patches,
|
|
306
|
+
inversePatches
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* @internal
|
|
311
|
+
*
|
|
312
|
+
* Runs an updater (or array of updaters) through Immer without tracking patches.
|
|
313
|
+
*/
|
|
314
|
+
function applyUpdater(currentState, updater) {
|
|
315
|
+
if (typeof updater === "function") return (0, immer.produce)(currentState, (draft) => {
|
|
316
|
+
updater(draft, currentState);
|
|
317
|
+
});
|
|
318
|
+
return updater.reduce((previousState, single) => (0, immer.produce)(previousState, (draft) => {
|
|
319
|
+
single(draft, previousState);
|
|
320
|
+
}), currentState);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Mutate a store via one or more Immer update functions.
|
|
324
|
+
*
|
|
325
|
+
* Patches are only computed when there's a consumer for them (a registered
|
|
326
|
+
* patch listener or a `patchesCallback`), keeping the common path allocation-free.
|
|
327
|
+
* Listeners are notified only when the root reference actually changes.
|
|
328
|
+
*
|
|
329
|
+
* @param store The store to update.
|
|
330
|
+
* @param updater A single update function, or an array applied in order.
|
|
331
|
+
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
332
|
+
*/
|
|
333
|
+
function update(store, updater, patchesCallback) {
|
|
334
|
+
const currentState = store.getRawState();
|
|
335
|
+
let nextState;
|
|
336
|
+
if (store._hasPatchListeners() || patchesCallback != null) {
|
|
337
|
+
const [next, patches, inversePatches] = applyUpdaterWithPatches(currentState, updater);
|
|
338
|
+
if (patches.length > 0) {
|
|
339
|
+
if (patchesCallback != null) patchesCallback(patches, inversePatches);
|
|
340
|
+
store._emitPatches(patches, inversePatches);
|
|
341
|
+
}
|
|
342
|
+
nextState = next;
|
|
343
|
+
} else nextState = applyUpdater(currentState, updater);
|
|
344
|
+
if (nextState !== currentState) store._updateState(nextState);
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
Object.defineProperty(exports, "Store", {
|
|
348
|
+
enumerable: true,
|
|
349
|
+
get: function() {
|
|
350
|
+
return Store;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
Object.defineProperty(exports, "applyPatchesToStore", {
|
|
354
|
+
enumerable: true,
|
|
355
|
+
get: function() {
|
|
356
|
+
return applyPatchesToStore;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
Object.defineProperty(exports, "update", {
|
|
360
|
+
enumerable: true,
|
|
361
|
+
get: function() {
|
|
362
|
+
return update;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
//# sourceMappingURL=Store-CI9N0P6I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Store-CI9N0P6I.js","names":[],"sources":["../src/core/Store.ts"],"sourcesContent":["import { deepEqual } from \"fast-equals\";\r\nimport {\r\n applyPatches,\r\n type Draft,\r\n enablePatches,\r\n type Patch,\r\n type PatchListener,\r\n produce,\r\n produceWithPatches,\r\n} from \"immer\";\r\n\r\n// Immer emits JSON patches for `produceWithPatches`, which the store exposes via\r\n// `listenToPatches`. This must run once, before any patch-producing update.\r\nenablePatches();\r\n\r\n/**\r\n * A plain pub/sub listener. Fired (in the order added) every time the store's\r\n * root state reference changes. This is the vanilla integration point that any\r\n * ecosystem — including the React adapter's `useSyncExternalStore` — builds on.\r\n */\r\nexport type TUpdateListener = () => void;\r\n\r\n/**\r\n * @typeParam S The store's state\r\n * @param draft The mutable draft to change during this update (Immer proxy).\r\n * @param original A readonly snapshot of the state as it was before this update.\r\n */\r\nexport type TUpdateFunction<S> = (draft: Draft<S>, original: S) => void;\r\n\r\n/**\r\n * A selector that derives a watched slice `T` from the full store state `S`.\r\n */\r\nexport type TStoreWatch<S extends object, T> = (state: S) => T;\r\n\r\n/**\r\n * Fired by {@link Store.watch} whenever the watched slice changes structurally.\r\n */\r\nexport type TStoreSubscriptionListener<S extends object, T> = (\r\n watched: T,\r\n allState: S,\r\n previousWatched: T,\r\n) => void;\r\n\r\n/**\r\n * Runs inside an Immer `produce` whenever a watched slice changes, letting the\r\n * store derive further state from its own mutations.\r\n */\r\nexport type TReactionFunction<S extends object, T> = (\r\n watched: T,\r\n draft: Draft<S>,\r\n original: S,\r\n previousWatched: T,\r\n) => void;\r\n\r\nexport type TStoreActionUpdate<S extends object> = (\r\n updater: TUpdateFunction<S> | TUpdateFunction<S>[],\r\n patchesCallback?: TPatchesCallback,\r\n) => void;\r\n\r\nexport type TStoreAction<S extends object> = (update: TStoreActionUpdate<S>) => void;\r\n\r\nexport type TPatchesCallback = (patches: Patch[], inversePatches: Patch[]) => void;\r\n\r\n/**\r\n * @internal\r\n * Drives a registered reaction. `forceRun` bypasses the change-detection\r\n * fast-path (used when a reaction is created with `runNow`).\r\n */\r\ntype TRunReactionFunction = (forceRun?: boolean) => void;\r\n\r\n/**\r\n * @internal\r\n * Drives a registered selector subscription on every store emission.\r\n */\r\ntype TRunSubscriptionFunction = () => void;\r\n\r\n/**\r\n * @internal\r\n * Binds a reaction definition to a concrete store instance.\r\n */\r\ntype TReactionCreator<S extends object> = (store: Store<S>) => TRunReactionFunction;\r\n\r\nexport interface IStoreInternalOptions<S extends object> {\r\n ssr: boolean;\r\n reactionCreators?: TReactionCreator<S>[];\r\n}\r\n\r\nexport interface ICreateReactionOptions {\r\n runNow?: boolean;\r\n runNowWithSideEffects?: boolean;\r\n}\r\n\r\n/**\r\n * @internal\r\n *\r\n * Builds the per-emission runner for a selector subscription.\r\n *\r\n * Change detection follows the \"Fast-Path\" rule: Immer's structural sharing\r\n * means an untouched branch keeps its exact reference, so a strict `===` check\r\n * resolves the overwhelmingly common \"nothing changed\" case in 0ms. Only when\r\n * the reference actually changes do we pay for a `deepEqual` traversal to\r\n * confirm the change is structural and not just a new-but-equal reference.\r\n */\r\nfunction makeSubscriptionFunction<S extends object, T>(\r\n store: Store<S>,\r\n watch: TStoreWatch<S, T>,\r\n listener: TStoreSubscriptionListener<S, T>,\r\n): TRunSubscriptionFunction {\r\n let lastWatchState: T = watch(store.getRawState());\r\n\r\n return () => {\r\n const currentState = store.getRawState();\r\n const nextWatchState = watch(currentState);\r\n\r\n // Fast-path: reference is identical → the watched branch was never touched.\r\n if (nextWatchState === lastWatchState) {\r\n return;\r\n }\r\n\r\n // Reference changed — fall back to deep equality to confirm real change.\r\n if (deepEqual(nextWatchState, lastWatchState)) {\r\n // Structurally equal: adopt the new reference so the fast-path stays hot,\r\n // but do not notify — nothing the watcher cares about actually changed.\r\n lastWatchState = nextWatchState;\r\n return;\r\n }\r\n\r\n const previousWatched = lastWatchState;\r\n lastWatchState = nextWatchState;\r\n listener(nextWatchState, currentState, previousWatched);\r\n };\r\n}\r\n\r\n/**\r\n * @internal\r\n *\r\n * Builds a reaction creator. Reactions follow the same Fast-Path rule as\r\n * subscriptions, then run their recipe inside Immer to derive further state.\r\n */\r\nfunction makeReactionFunctionCreator<S extends object, T>(\r\n watch: TStoreWatch<S, T>,\r\n reaction: TReactionFunction<S, T>,\r\n): TReactionCreator<S> {\r\n return (store) => {\r\n let lastWatchState: T = watch(store.getRawState());\r\n\r\n return (forceRun = false) => {\r\n const currentState = store.getRawState();\r\n const nextWatchState = watch(currentState);\r\n\r\n if (!forceRun) {\r\n // Fast-path: identical reference → skip entirely.\r\n if (nextWatchState === lastWatchState) {\r\n return;\r\n }\r\n\r\n // New reference but structurally equal → adopt and skip the recipe.\r\n if (deepEqual(nextWatchState, lastWatchState)) {\r\n lastWatchState = nextWatchState;\r\n return;\r\n }\r\n }\r\n\r\n const previousWatched = lastWatchState;\r\n lastWatchState = nextWatchState;\r\n\r\n // Reactions mutate via Immer but must not re-enter the reaction loop, so\r\n // they write back through the no-reaction update path.\r\n if (store._hasPatchListeners()) {\r\n const [nextState, patches, inversePatches] = produceWithPatches(\r\n currentState,\r\n (draft: Draft<S>) => {\r\n reaction(nextWatchState, draft, currentState, previousWatched);\r\n },\r\n );\r\n\r\n store._updateStateWithoutReaction(nextState);\r\n\r\n if (patches.length > 0) {\r\n store._emitPatches(patches, inversePatches);\r\n }\r\n } else {\r\n const nextState = produce(currentState, (draft: Draft<S>) => {\r\n reaction(nextWatchState, draft, currentState, previousWatched);\r\n });\r\n store._updateStateWithoutReaction(nextState);\r\n }\r\n };\r\n };\r\n}\r\n\r\n/**\r\n * A framework-agnostic, Immer-backed state container.\r\n *\r\n * The store owns a single immutable state value and a `Set` of plain\r\n * listeners. Every mutation runs through Immer's `produce`, and listeners are\r\n * only notified when the resulting root reference actually changes — making\r\n * \"no-op\" updates genuinely free for subscribers.\r\n *\r\n * @typeParam S Your store's state interface.\r\n */\r\nexport class Store<S extends object = object> {\r\n private currentState: S;\r\n private readonly createInitialState: () => S;\r\n private ssr = false;\r\n\r\n /** Plain pub/sub listeners — a Set both dedupes and dispatches fast. */\r\n private readonly listeners = new Set<TUpdateListener>();\r\n\r\n private reactionCreators: TReactionCreator<S>[] = [];\r\n private reactions: TRunReactionFunction[] = [];\r\n private clientSubscriptions: TRunSubscriptionFunction[] = [];\r\n\r\n /**\r\n * @internal\r\n */\r\n public _patchListeners: PatchListener[] = [];\r\n\r\n constructor(initialState: S | (() => S)) {\r\n if (initialState instanceof Function) {\r\n this.createInitialState = initialState;\r\n this.currentState = initialState();\r\n } else {\r\n this.createInitialState = () => initialState;\r\n this.currentState = initialState;\r\n }\r\n }\r\n\r\n get state(): S {\r\n return Object.freeze(this.currentState);\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _setInternalOptions({ ssr, reactionCreators = [] }: IStoreInternalOptions<S>) {\r\n this.ssr = ssr;\r\n this.reactionCreators = reactionCreators;\r\n this.reactions = reactionCreators.map((rc) => rc(this));\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _getReactionCreators(): TReactionCreator<S>[] {\r\n return this.reactionCreators;\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _instantiateReactions() {\r\n this.reactions = this.reactionCreators.map((rc) => rc(this));\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _getInitialState(): S {\r\n return this.createInitialState();\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _hasPatchListeners(): boolean {\r\n return this._patchListeners.length > 0;\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n _emitPatches(patches: Patch[], inversePatches: Patch[]) {\r\n for (const listener of this._patchListeners) {\r\n listener(patches, inversePatches);\r\n }\r\n }\r\n\r\n /**\r\n * @internal\r\n *\r\n * Writes a new state value without running reactions. Used by reactions\r\n * themselves to avoid re-entrancy.\r\n */\r\n _updateStateWithoutReaction(nextState: S) {\r\n this.currentState = nextState;\r\n }\r\n\r\n /**\r\n * @internal\r\n *\r\n * Commits a new root state, runs reactions, then dispatches to all\r\n * subscribers. Callers are responsible for the `nextState !== currentState`\r\n * reference guard before invoking this.\r\n */\r\n _updateState(nextState: S) {\r\n this.currentState = nextState;\r\n\r\n // Reactions run first and may derive further state (without re-triggering).\r\n for (const runReaction of this.reactions) {\r\n runReaction();\r\n }\r\n\r\n if (this.ssr) {\r\n return;\r\n }\r\n\r\n for (const runSubscription of this.clientSubscriptions) {\r\n runSubscription();\r\n }\r\n\r\n for (const listener of this.listeners) {\r\n listener();\r\n }\r\n }\r\n\r\n /**\r\n * Returns the raw state object contained within this store at this moment.\r\n *\r\n * ---\r\n * ** WARNING **\r\n *\r\n * Most of the time, if you're using this in your app, there's probably a\r\n * better way to do it (a selector subscription or the React adapter).\r\n * ---\r\n */\r\n getRawState(): S {\r\n return this.currentState;\r\n }\r\n\r\n /**\r\n * Subscribe a plain listener to store mutations. The listener fires once per\r\n * committed update (root reference change) and receives no arguments — read\r\n * the latest value with {@link getRawState}.\r\n *\r\n * This is the low-level primitive that vanilla code and framework adapters\r\n * (e.g. React's `useSyncExternalStore`) build upon.\r\n *\r\n * @returns An unsubscribe function.\r\n */\r\n subscribe(listener: TUpdateListener): () => void {\r\n this.listeners.add(listener);\r\n return () => {\r\n this.listeners.delete(listener);\r\n };\r\n }\r\n\r\n /**\r\n * Subscribe to a derived slice of state. The `listener` only fires when the\r\n * watched value changes structurally (per the Fast-Path rule).\r\n *\r\n * @returns An unsubscribe function.\r\n */\r\n watch<T>(watch: TStoreWatch<S, T>, listener: TStoreSubscriptionListener<S, T>): () => void {\r\n if (this.ssr) {\r\n return () => {\r\n console.warn(\r\n `@nice-code/state: Subscriptions made on the server side are not registered, so this unsubscribe call does nothing.`,\r\n );\r\n };\r\n }\r\n\r\n const run = makeSubscriptionFunction(this, watch, listener);\r\n this.clientSubscriptions.push(run);\r\n\r\n return () => {\r\n this.clientSubscriptions = this.clientSubscriptions.filter((f) => f !== run);\r\n };\r\n }\r\n\r\n /**\r\n * Register a reaction: when the watched slice changes, the `reaction` recipe\r\n * runs inside Immer to derive further state on the same store.\r\n *\r\n * @returns A function that removes the reaction.\r\n */\r\n createReaction<T>(\r\n watch: TStoreWatch<S, T>,\r\n reaction: TReactionFunction<S, T>,\r\n { runNow = false, runNowWithSideEffects = false }: ICreateReactionOptions = {},\r\n ): () => void {\r\n const creator = makeReactionFunctionCreator(watch, reaction);\r\n this.reactionCreators.push(creator);\r\n\r\n const run = creator(this);\r\n this.reactions.push(run);\r\n\r\n if (runNow || runNowWithSideEffects) {\r\n run(true);\r\n\r\n if (runNowWithSideEffects && !this.ssr) {\r\n this._updateState(this.currentState);\r\n }\r\n }\r\n\r\n return () => {\r\n this.reactions = this.reactions.filter((f) => f !== run);\r\n this.reactionCreators = this.reactionCreators.filter((f) => f !== creator);\r\n };\r\n }\r\n\r\n /**\r\n * Subscribe to the Immer patches produced by each update.\r\n *\r\n * @returns An unsubscribe function.\r\n */\r\n listenToPatches(patchListener: PatchListener): () => void {\r\n this._patchListeners.push(patchListener);\r\n return () => {\r\n this._patchListeners = this._patchListeners.filter((f) => f !== patchListener);\r\n };\r\n }\r\n\r\n /**\r\n * Mutate the store via one or more Immer update functions.\r\n *\r\n * @param updater A single update function, or an array applied in order.\r\n * @param patchesCallback Optional callback receiving the patches for this update.\r\n */\r\n update(updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback) {\r\n update(this, updater, patchesCallback);\r\n }\r\n\r\n /**\r\n * Replace the store's state entirely with a new state value.\r\n */\r\n replace(newState: S) {\r\n if (newState !== this.currentState) {\r\n this._updateState(newState);\r\n }\r\n }\r\n\r\n /**\r\n * Replace the store's state by mapping from the current state.\r\n */\r\n replaceFromCurrent(replacer: (state: S) => S) {\r\n const nextState = replacer(this.currentState);\r\n if (nextState !== this.currentState) {\r\n this._updateState(nextState);\r\n }\r\n }\r\n\r\n /**\r\n * Apply a set of Immer patches to the store.\r\n */\r\n applyPatches(patches: Patch[]) {\r\n applyPatchesToStore(this, patches);\r\n }\r\n}\r\n\r\n/**\r\n * Apply Immer patches to a store, committing only if the root reference changes.\r\n */\r\nexport function applyPatchesToStore<S extends object = object>(store: Store<S>, patches: Patch[]) {\r\n const currentState = store.getRawState();\r\n const nextState = applyPatches(currentState, patches);\r\n if (nextState !== currentState) {\r\n store._updateState(nextState);\r\n }\r\n}\r\n\r\n/**\r\n * @internal\r\n *\r\n * Runs an updater (or array of updaters) through Immer, collecting patches.\r\n */\r\nfunction applyUpdaterWithPatches<S extends object>(\r\n currentState: S,\r\n updater: TUpdateFunction<S> | TUpdateFunction<S>[],\r\n): [S, Patch[], Patch[]] {\r\n if (typeof updater === \"function\") {\r\n const [next, patches, inversePatches] = produceWithPatches(currentState, (draft: Draft<S>) => {\r\n updater(draft, currentState);\r\n });\r\n return [next, patches, inversePatches];\r\n }\r\n\r\n let state = currentState;\r\n const patches: Patch[] = [];\r\n const inversePatches: Patch[] = [];\r\n\r\n for (const single of updater) {\r\n const [next, p, ip] = produceWithPatches(state, (draft: Draft<S>) => {\r\n single(draft, state);\r\n });\r\n state = next;\r\n patches.push(...p);\r\n inversePatches.push(...ip);\r\n }\r\n\r\n return [state, patches, inversePatches];\r\n}\r\n\r\n/**\r\n * @internal\r\n *\r\n * Runs an updater (or array of updaters) through Immer without tracking patches.\r\n */\r\nfunction applyUpdater<S extends object>(\r\n currentState: S,\r\n updater: TUpdateFunction<S> | TUpdateFunction<S>[],\r\n): S {\r\n if (typeof updater === \"function\") {\r\n return produce(currentState, (draft: Draft<S>) => {\r\n updater(draft, currentState);\r\n });\r\n }\r\n\r\n return updater.reduce<S>(\r\n (previousState, single) =>\r\n produce(previousState, (draft: Draft<S>) => {\r\n single(draft, previousState);\r\n }),\r\n currentState,\r\n );\r\n}\r\n\r\n/**\r\n * Mutate a store via one or more Immer update functions.\r\n *\r\n * Patches are only computed when there's a consumer for them (a registered\r\n * patch listener or a `patchesCallback`), keeping the common path allocation-free.\r\n * Listeners are notified only when the root reference actually changes.\r\n *\r\n * @param store The store to update.\r\n * @param updater A single update function, or an array applied in order.\r\n * @param patchesCallback Optional callback receiving the patches for this update.\r\n */\r\nexport function update<S extends object = object>(\r\n store: Store<S>,\r\n updater: TUpdateFunction<S> | TUpdateFunction<S>[],\r\n patchesCallback?: TPatchesCallback,\r\n) {\r\n const currentState = store.getRawState();\r\n\r\n let nextState: S;\r\n\r\n if (store._hasPatchListeners() || patchesCallback != null) {\r\n const [next, patches, inversePatches] = applyUpdaterWithPatches(currentState, updater);\r\n\r\n if (patches.length > 0) {\r\n if (patchesCallback != null) {\r\n patchesCallback(patches, inversePatches);\r\n }\r\n store._emitPatches(patches, inversePatches);\r\n }\r\n\r\n nextState = next;\r\n } else {\r\n nextState = applyUpdater(currentState, updater);\r\n }\r\n\r\n // Reference guard: an updater that mutated nothing yields the same reference,\r\n // so we skip the dispatch entirely.\r\n if (nextState !== currentState) {\r\n store._updateState(nextState);\r\n }\r\n}\r\n"],"mappings":"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"}
|