@plasius/react-state 1.1.0 → 1.1.1
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/dist/index.cjs +110 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +102 -7
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -56,6 +66,14 @@ function deepFreeze(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
56
66
|
// src/store.ts
|
|
57
67
|
var import_meta = {};
|
|
58
68
|
var DEV = typeof import_meta !== "undefined" ? import_meta.env?.DEV : process.env.NODE_ENV !== "production";
|
|
69
|
+
function devTrack(name, props) {
|
|
70
|
+
if (!DEV) return;
|
|
71
|
+
try {
|
|
72
|
+
const t = globalThis?.track;
|
|
73
|
+
if (typeof t === "function") t(name, props);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
59
77
|
function createStore(reducer, initialState) {
|
|
60
78
|
let state = DEV ? deepFreeze(initialState) : initialState;
|
|
61
79
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -66,38 +84,55 @@ function createStore(reducer, initialState) {
|
|
|
66
84
|
const prevState = state;
|
|
67
85
|
const nextState = reducer(state, action);
|
|
68
86
|
if (DEV) deepFreeze(nextState);
|
|
87
|
+
devTrack("store:dispatch", { type: action?.type });
|
|
69
88
|
if (Object.is(prevState, nextState)) {
|
|
70
89
|
state = nextState;
|
|
90
|
+
devTrack("store:no-op", { type: action?.type });
|
|
71
91
|
return;
|
|
72
92
|
}
|
|
73
93
|
state = nextState;
|
|
74
|
-
|
|
94
|
+
let changedKeys;
|
|
95
|
+
if (DEV) {
|
|
96
|
+
changedKeys = Object.keys(nextState).filter((k) => !Object.is(prevState[k], nextState[k]));
|
|
97
|
+
devTrack("store:state-changed", { type: action?.type, changedKeys });
|
|
98
|
+
}
|
|
99
|
+
const globalSnapshot = [...listeners];
|
|
100
|
+
devTrack("store:notify:all", { listeners: globalSnapshot.length });
|
|
101
|
+
for (const listener of globalSnapshot) listener();
|
|
75
102
|
for (const [key, set] of keyListeners.entries()) {
|
|
76
103
|
if (!Object.is(prevState[key], state[key])) {
|
|
104
|
+
devTrack("store:notify:key", { key: String(key), listeners: set.size });
|
|
77
105
|
for (const listener of [...set]) listener(state[key]);
|
|
78
106
|
}
|
|
79
107
|
}
|
|
108
|
+
let selNotifies = 0;
|
|
80
109
|
selectorListeners.forEach((entry) => {
|
|
81
110
|
const nextValue = entry.selector(state);
|
|
82
111
|
if (!Object.is(entry.lastValue, nextValue)) {
|
|
83
112
|
entry.lastValue = nextValue;
|
|
84
113
|
entry.listener(nextValue);
|
|
114
|
+
selNotifies++;
|
|
85
115
|
}
|
|
86
116
|
});
|
|
117
|
+
devTrack("store:notify:selector", { listeners: selNotifies });
|
|
87
118
|
};
|
|
88
119
|
const subscribe = (listener) => {
|
|
89
120
|
listeners.add(listener);
|
|
121
|
+
devTrack("store:sub:all:add", { size: listeners.size });
|
|
90
122
|
return () => {
|
|
91
123
|
listeners.delete(listener);
|
|
124
|
+
devTrack("store:sub:all:remove", { size: listeners.size });
|
|
92
125
|
};
|
|
93
126
|
};
|
|
94
127
|
const subscribeToKey = (key, listener) => {
|
|
95
128
|
const set = keyListeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
96
129
|
set.add(listener);
|
|
97
130
|
keyListeners.set(key, set);
|
|
131
|
+
devTrack("store:sub:key:add", { key: String(key), size: set.size });
|
|
98
132
|
return () => {
|
|
99
133
|
set.delete(listener);
|
|
100
134
|
if (set.size === 0) keyListeners.delete(key);
|
|
135
|
+
devTrack("store:sub:key:remove", { key: String(key), size: set.size });
|
|
101
136
|
};
|
|
102
137
|
};
|
|
103
138
|
const subscribeWithSelector = (selector, listener) => {
|
|
@@ -107,8 +142,10 @@ function createStore(reducer, initialState) {
|
|
|
107
142
|
lastValue: selector(state)
|
|
108
143
|
};
|
|
109
144
|
selectorListeners.add(entry);
|
|
145
|
+
devTrack("store:sub:selector:add", { size: selectorListeners.size });
|
|
110
146
|
return () => {
|
|
111
147
|
selectorListeners.delete(entry);
|
|
148
|
+
devTrack("store:sub:selector:remove", { size: selectorListeners.size });
|
|
112
149
|
};
|
|
113
150
|
};
|
|
114
151
|
return {
|
|
@@ -122,6 +159,15 @@ function createStore(reducer, initialState) {
|
|
|
122
159
|
|
|
123
160
|
// src/create-scoped-store.tsx
|
|
124
161
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
162
|
+
var __DEV__ = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
163
|
+
function devTrack2(name, props) {
|
|
164
|
+
if (!__DEV__) return;
|
|
165
|
+
try {
|
|
166
|
+
const t = globalThis?.track;
|
|
167
|
+
if (typeof t === "function") t(name, props);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
125
171
|
var _queue = /* @__PURE__ */ new Set();
|
|
126
172
|
var _queued = false;
|
|
127
173
|
var _schedule = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => Promise.resolve().then(fn);
|
|
@@ -134,10 +180,22 @@ function enqueue(fn) {
|
|
|
134
180
|
const fns = Array.from(_queue);
|
|
135
181
|
_queue.clear();
|
|
136
182
|
for (const f of fns) f();
|
|
183
|
+
devTrack2("scoped:batch:flush", { size: fns.length });
|
|
137
184
|
});
|
|
138
185
|
}
|
|
139
186
|
function makeBatchedSubscribe(subscribe) {
|
|
140
|
-
return (onChange) =>
|
|
187
|
+
return (onChange) => {
|
|
188
|
+
devTrack2("scoped:sub:add");
|
|
189
|
+
const wrapped = () => {
|
|
190
|
+
devTrack2("scoped:notify:enqueue");
|
|
191
|
+
enqueue(onChange);
|
|
192
|
+
};
|
|
193
|
+
const unsubscribe = subscribe(wrapped);
|
|
194
|
+
return () => {
|
|
195
|
+
devTrack2("scoped:sub:remove");
|
|
196
|
+
unsubscribe();
|
|
197
|
+
};
|
|
198
|
+
};
|
|
141
199
|
}
|
|
142
200
|
function shallowEqual(a, b) {
|
|
143
201
|
if (Object.is(a, b)) return true;
|
|
@@ -161,12 +219,18 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
161
219
|
const storeRef = (0, import_react.useRef)(null);
|
|
162
220
|
if (!storeRef.current) {
|
|
163
221
|
storeRef.current = createStore(reducer, override ?? initialState);
|
|
222
|
+
devTrack2("scoped:store:create");
|
|
164
223
|
}
|
|
224
|
+
(0, import_react.useEffect)(() => {
|
|
225
|
+
devTrack2("scoped:provider:mount");
|
|
226
|
+
return () => devTrack2("scoped:provider:unmount");
|
|
227
|
+
}, []);
|
|
165
228
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Context.Provider, { value: storeRef.current, children });
|
|
166
229
|
};
|
|
167
230
|
const useStore2 = () => {
|
|
168
231
|
const ctx = (0, import_react.useContext)(Context);
|
|
169
232
|
if (!ctx) throw new Error("Store not found in context");
|
|
233
|
+
devTrack2("scoped:useStore");
|
|
170
234
|
return (0, import_react.useSyncExternalStore)(
|
|
171
235
|
makeBatchedSubscribe(ctx.subscribe),
|
|
172
236
|
ctx.getState,
|
|
@@ -190,8 +254,10 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
190
254
|
const last = lastRef.current;
|
|
191
255
|
const nextSelected = selector(state);
|
|
192
256
|
if (last && last.state === state && isEqual(last.selected, nextSelected)) {
|
|
257
|
+
devTrack2("scoped:selector:cache-hit");
|
|
193
258
|
return last.selected;
|
|
194
259
|
}
|
|
260
|
+
devTrack2("scoped:selector:cache-miss");
|
|
195
261
|
lastRef.current = { state, selected: nextSelected };
|
|
196
262
|
return nextSelected;
|
|
197
263
|
}
|
|
@@ -205,12 +271,22 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
205
271
|
}
|
|
206
272
|
|
|
207
273
|
// src/provider.tsx
|
|
208
|
-
var import_react2 = require("react");
|
|
274
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
209
275
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
276
|
+
var __DEV__2 = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
277
|
+
function devTrack3(name, props) {
|
|
278
|
+
if (!__DEV__2) return;
|
|
279
|
+
try {
|
|
280
|
+
const t = globalThis?.track;
|
|
281
|
+
if (typeof t === "function") t(name, props);
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
284
|
+
}
|
|
210
285
|
var StoreContext = (0, import_react2.createContext)(void 0);
|
|
211
286
|
function useStoreInstance() {
|
|
212
287
|
const store = (0, import_react2.useContext)(StoreContext);
|
|
213
288
|
if (!store) {
|
|
289
|
+
devTrack3("store:provider:missing");
|
|
214
290
|
throw new Error(
|
|
215
291
|
"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>."
|
|
216
292
|
);
|
|
@@ -221,16 +297,45 @@ function StoreProvider({
|
|
|
221
297
|
store,
|
|
222
298
|
children
|
|
223
299
|
}) {
|
|
300
|
+
(0, import_react2.useEffect)(() => {
|
|
301
|
+
devTrack3("store:provider:mount", { hasStore: !!store });
|
|
302
|
+
return () => devTrack3("store:provider:unmount");
|
|
303
|
+
}, [store]);
|
|
224
304
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoreContext.Provider, { value: store, children });
|
|
225
305
|
}
|
|
226
306
|
function useStore() {
|
|
227
307
|
const store = useStoreInstance();
|
|
228
308
|
const [state, setState] = (0, import_react2.useState)(() => store.getState());
|
|
309
|
+
const prevRef = import_react2.default.useRef(state);
|
|
229
310
|
(0, import_react2.useEffect)(() => {
|
|
311
|
+
devTrack3("store:react:subscribe");
|
|
230
312
|
const unsubscribe = store.subscribe(() => {
|
|
231
|
-
|
|
313
|
+
const next = store.getState();
|
|
314
|
+
if (!Object.is(prevRef.current, next)) {
|
|
315
|
+
if (__DEV__2) {
|
|
316
|
+
try {
|
|
317
|
+
const prev = prevRef.current;
|
|
318
|
+
const cur = next;
|
|
319
|
+
const changedKeys = Array.from(
|
|
320
|
+
/* @__PURE__ */ new Set([...Object.keys(prev || {}), ...Object.keys(cur || {})])
|
|
321
|
+
).filter((k) => !Object.is(prev?.[k], cur?.[k]));
|
|
322
|
+
devTrack3("store:react:update", {
|
|
323
|
+
changed: changedKeys,
|
|
324
|
+
count: changedKeys.length
|
|
325
|
+
});
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
prevRef.current = next;
|
|
330
|
+
setState(next);
|
|
331
|
+
} else {
|
|
332
|
+
devTrack3("store:react:no-op");
|
|
333
|
+
}
|
|
232
334
|
});
|
|
233
|
-
return
|
|
335
|
+
return () => {
|
|
336
|
+
devTrack3("store:react:unsubscribe");
|
|
337
|
+
unsubscribe();
|
|
338
|
+
};
|
|
234
339
|
}, [store]);
|
|
235
340
|
return state;
|
|
236
341
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/create-scoped-store.tsx","../src/freeze.ts","../src/store.ts","../src/provider.tsx","../src/metadata-store.ts"],"sourcesContent":["export * from \"./types.js\";\nexport * from \"./create-scoped-store.js\";\nexport * from \"./store.js\";\nexport * from \"./provider.js\";\nexport * from \"./metadata-store.js\";\n","export type Reducer<S, A> = (state: S, action: A) => S;\nexport type Listener = () => void;\nexport type Unsubscribe = () => void;\nexport const __noop = null;","import { createContext, useContext, useRef, useSyncExternalStore, useCallback } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\n\n// Local microtask-based batching (no react-dom dependency)\nconst _queue = new Set<() => void>();\nlet _queued = false;\nconst _schedule = typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (fn: () => void) => Promise.resolve().then(fn);\n\nfunction enqueue(fn: () => void) {\n _queue.add(fn);\n if (_queued) return;\n _queued = true;\n _schedule(() => {\n _queued = false;\n const fns = Array.from(_queue);\n _queue.clear();\n for (const f of fns) f();\n });\n}\n\nfunction makeBatchedSubscribe(subscribe: (l: () => void) => () => void) {\n return (onChange: () => void) => subscribe(() => enqueue(onChange));\n}\n\nfunction shallowEqual(a: any, b: any) {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== \"object\" ||\n a === null ||\n typeof b !== \"object\" ||\n b === null\n )\n return false;\n const ak = Object.keys(a),\n bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n const k = ak[i] as string;\n if (\n !Object.prototype.hasOwnProperty.call(b, k) ||\n !Object.is((a as any)[k], (b as any)[k])\n )\n return false;\n }\n return true;\n}\n\nexport function createScopedStoreContext<S extends IState, A extends IAction>(\n reducer: (state: S, action: A) => S,\n initialState: S\n) {\n const Context = createContext<Store<S, A> | null>(null);\n\n // Each Provider instance gets its own store.\n const Provider = ({\n children,\n initialState: override,\n }: {\n children: React.ReactNode;\n initialState?: S;\n }) => {\n const storeRef = useRef<Store<S, A> | null>(null);\n if (!storeRef.current) {\n storeRef.current = createStore(reducer, override ?? initialState);\n }\n return <Context.Provider value={storeRef.current}>{children}</Context.Provider>;\n };\n\n const useStore = (): S => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n return useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n };\n\n const useDispatch = (): ((action: A) => void) => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Dispatch not found in context\");\n return useCallback((action: A) => ctx.dispatch(action), [ctx]);\n };\n\n function useSelector<T>(\n selector: (state: S) => T,\n isEqual: (a: T, b: T) => boolean = shallowEqual\n ): T {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n\n // Subscribe to the raw state snapshot (stable reference until a dispatch)\n const state = useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n\n // Cache the selected slice per state snapshot to avoid returning fresh objects during render\n const lastRef = useRef<{ state: S; selected: T } | null>(null);\n const last = lastRef.current;\n const nextSelected = selector(state);\n\n if (last && last.state === state && isEqual(last.selected, nextSelected)) {\n return last.selected; // return cached reference to satisfy getSnapshot caching\n }\n\n lastRef.current = { state, selected: nextSelected };\n return nextSelected;\n }\n\n return {\n Context,\n Provider,\n useStore,\n useDispatch,\n useSelector,\n };\n}\n","// freeze.ts\nexport function deepFreeze<T>(obj: T, seen = new WeakSet<object>()): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const o = obj as unknown as object;\n if (seen.has(o)) return obj;\n seen.add(o);\n\n // Freeze children first\n for (const key of Object.getOwnPropertyNames(o)) {\n // @ts-expect-error index access\n const val = (o as any)[key];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n // Also handle symbols (rare but safe)\n for (const sym of Object.getOwnPropertySymbols(o)) {\n // @ts-expect-error index access\n const val = (o as any)[sym];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n\n return Object.freeze(obj);\n}\n","import type { Reducer, Listener } from \"./types.js\";\nimport { deepFreeze } from \"./freeze.js\";\n\nconst DEV =\n typeof import.meta !== \"undefined\"\n ? (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV\n : process.env.NODE_ENV !== \"production\";\n\n// Allow narrower parameter types for callbacks without fighting variance\ntype BivariantListener<T> = {\n bivarianceHack(value: T): void;\n}[\"bivarianceHack\"];\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IState {}\nexport interface IAction {\n type: string;\n}\n\nexport interface Store<S extends IState, A extends IAction> {\n getState(): S;\n dispatch(action: A): void;\n /**\n * Subscribe to all state changes.\n */\n subscribe(listener: Listener): () => void;\n /**\n * Subscribe to changes of a specific key in the state.\n */\n subscribeToKey<K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ): () => void;\n /**\n * Subscribe to changes in a selected value from the state.\n */\n subscribeWithSelector<T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ): () => void;\n}\n\nexport function createStore<S extends IState, A extends IAction>(\n reducer: Reducer<S, A>,\n initialState: S\n): Store<S, A> {\n let state: S = DEV ? deepFreeze(initialState) : initialState;\n const listeners = new Set<Listener>();\n const keyListeners = new Map<keyof S, Set<BivariantListener<S[keyof S]>>>();\n\n interface SelectorEntry<T> {\n selector: (state: S) => T;\n listener: BivariantListener<T>;\n lastValue: T;\n }\n const selectorListeners = new Set<SelectorEntry<unknown>>();\n\n const getState = () => state;\n\n const dispatch = (action: A) => {\n const prevState = state;\n const nextState = reducer(state, action);\n \n if (DEV) deepFreeze(nextState);\n \n // Distinct-until-changed: if the reducer returns the same reference,\n // skip all notifications (prevents unnecessary re-renders).\n if (Object.is(prevState, nextState)) {\n state = nextState; // keep any identity guarantees from reducer\n return;\n }\n\n state = nextState;\n\n // Notify global listeners (iterate over a snapshot so unsubscribe during\n // notify does not skip the next listener)\n for (const listener of [...listeners]) listener();\n\n // Notify key listeners only when that key actually changed (Object.is)\n for (const [key, set] of keyListeners.entries()) {\n if (!Object.is(prevState[key], state[key])) {\n for (const listener of [...set]) listener(state[key]);\n }\n }\n\n // Notify selector listeners only when selected value changed (Object.is)\n selectorListeners.forEach((entry) => {\n const nextValue = (entry.selector as (s: S) => unknown)(state);\n if (!Object.is(entry.lastValue, nextValue)) {\n entry.lastValue = nextValue as unknown;\n (entry.listener as (v: unknown) => void)(nextValue);\n }\n });\n };\n\n const subscribe = (listener: Listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n };\n\n const subscribeToKey = <K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ) => {\n const set =\n keyListeners.get(key) ?? new Set<BivariantListener<S[keyof S]>>();\n set.add(listener as unknown as BivariantListener<S[keyof S]>);\n keyListeners.set(key, set);\n return () => {\n set.delete(listener as unknown as BivariantListener<S[keyof S]>);\n if (set.size === 0) keyListeners.delete(key);\n };\n };\n\n const subscribeWithSelector = <T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ) => {\n const entry: SelectorEntry<T> = {\n selector,\n listener: listener as BivariantListener<T>,\n lastValue: selector(state),\n };\n selectorListeners.add(entry as unknown as SelectorEntry<unknown>);\n return () => {\n selectorListeners.delete(entry as unknown as SelectorEntry<unknown>);\n };\n };\n\n return {\n getState,\n dispatch,\n subscribe,\n subscribeToKey,\n subscribeWithSelector,\n };\n}\n","import React, { createContext, useContext, useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Store, IState, IAction } from \"./store.js\";\n\nconst StoreContext = createContext<Store<IState, IAction> | undefined>(undefined);\n\nfunction useStoreInstance<S extends IState, A extends IAction>(): Store<S, A> {\n const store = useContext(StoreContext) as Store<S, A> | undefined;\n if (!store) {\n throw new Error(\n \"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>.\"\n );\n }\n return store;\n}\n\ninterface StoreProviderProps<S extends IState, A extends IAction> {\n store: Store<S, A>;\n children: ReactNode;\n}\n\nexport function StoreProvider<S extends IState, A extends IAction>({\n store,\n children,\n}: StoreProviderProps<S, A>) {\n return (\n <StoreContext.Provider value={store as unknown as Store<IState, IAction>}>\n {children}\n </StoreContext.Provider>\n );\n}\n\nexport function useStore<S extends IState>(): S {\n const store = useStoreInstance<S, IAction>();\n const [state, setState] = useState<S>(() => store.getState());\n\n useEffect(() => {\n // Subscribe to store changes and update local state.\n const unsubscribe = store.subscribe(() => {\n setState(store.getState());\n });\n return unsubscribe;\n }, [store]);\n\n return state;\n}\n\nexport function useDispatch<A extends IAction>(): Store<IState, A>[\"dispatch\"] {\n const store = useStoreInstance<IState, A>();\n // Return the store's dispatch directly; consumers can call dispatch(action).\n return store.dispatch as Store<IState, A>[\"dispatch\"];\n}\n","// metadata-store.ts\nexport class MetadataStore<T extends object, Meta extends object> {\n private readonly symbol: symbol;\n\n constructor(description: string) {\n this.symbol = Symbol(description);\n }\n\n set(target: T, meta: Meta) {\n Object.defineProperty(target, this.symbol as PropertyKey, {\n value: meta,\n writable: false,\n enumerable: false,\n });\n }\n\n get(target: T): Meta | undefined {\n return (target as Record<PropertyKey, Meta>)[this.symbol as PropertyKey];\n }\n\n has(target: T): boolean {\n return (this.symbol as PropertyKey) in target;\n }\n}\n"],"mappings":"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","names":["useStore","useDispatch","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/create-scoped-store.tsx","../src/freeze.ts","../src/store.ts","../src/provider.tsx","../src/metadata-store.ts"],"sourcesContent":["export * from \"./types.js\";\nexport * from \"./create-scoped-store.js\";\nexport * from \"./store.js\";\nexport * from \"./provider.js\";\nexport * from \"./metadata-store.js\";\n","export type Reducer<S, A> = (state: S, action: A) => S;\nexport type Listener = () => void;\nexport type Unsubscribe = () => void;\nexport const __noop = null;","import { createContext, useContext, useRef, useSyncExternalStore, useCallback, useEffect } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\n\n// DEV-only tracking (no-op in production)\nconst __DEV__ = typeof process !== \"undefined\" ? process.env.NODE_ENV !== \"production\" : true;\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!__DEV__) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\n// Local microtask-based batching (no react-dom dependency)\nconst _queue = new Set<() => void>();\nlet _queued = false;\nconst _schedule = typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (fn: () => void) => Promise.resolve().then(fn);\n\nfunction enqueue(fn: () => void) {\n _queue.add(fn);\n if (_queued) return;\n _queued = true;\n _schedule(() => {\n _queued = false;\n const fns = Array.from(_queue);\n _queue.clear();\n for (const f of fns) f();\n devTrack(\"scoped:batch:flush\", { size: fns.length });\n });\n}\n\nfunction makeBatchedSubscribe(subscribe: (l: () => void) => () => void) {\n return (onChange: () => void) => {\n devTrack(\"scoped:sub:add\");\n const wrapped = () => {\n devTrack(\"scoped:notify:enqueue\");\n enqueue(onChange);\n };\n const unsubscribe = subscribe(wrapped);\n return () => {\n devTrack(\"scoped:sub:remove\");\n unsubscribe();\n };\n };\n}\n\nfunction shallowEqual(a: any, b: any) {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== \"object\" ||\n a === null ||\n typeof b !== \"object\" ||\n b === null\n )\n return false;\n const ak = Object.keys(a),\n bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n const k = ak[i] as string;\n if (\n !Object.prototype.hasOwnProperty.call(b, k) ||\n !Object.is((a as any)[k], (b as any)[k])\n )\n return false;\n }\n return true;\n}\n\nexport function createScopedStoreContext<S extends IState, A extends IAction>(\n reducer: (state: S, action: A) => S,\n initialState: S\n) {\n const Context = createContext<Store<S, A> | null>(null);\n\n // Each Provider instance gets its own store.\n const Provider = ({\n children,\n initialState: override,\n }: {\n children: React.ReactNode;\n initialState?: S;\n }) => {\n const storeRef = useRef<Store<S, A> | null>(null);\n if (!storeRef.current) {\n storeRef.current = createStore(reducer, override ?? initialState);\n devTrack(\"scoped:store:create\");\n }\n\n useEffect(() => {\n devTrack(\"scoped:provider:mount\");\n return () => devTrack(\"scoped:provider:unmount\");\n }, []);\n\n return <Context.Provider value={storeRef.current}>{children}</Context.Provider>;\n };\n\n const useStore = (): S => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n devTrack(\"scoped:useStore\");\n return useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n };\n\n const useDispatch = (): ((action: A) => void) => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Dispatch not found in context\");\n return useCallback((action: A) => ctx.dispatch(action), [ctx]);\n };\n\n function useSelector<T>(\n selector: (state: S) => T,\n isEqual: (a: T, b: T) => boolean = shallowEqual\n ): T {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n\n // Subscribe to the raw state snapshot (stable reference until a dispatch)\n const state = useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n\n // Cache the selected slice per state snapshot to avoid returning fresh objects during render\n const lastRef = useRef<{ state: S; selected: T } | null>(null);\n const last = lastRef.current;\n const nextSelected = selector(state);\n\n if (last && last.state === state && isEqual(last.selected, nextSelected)) {\n devTrack(\"scoped:selector:cache-hit\");\n return last.selected; // return cached reference to satisfy getSnapshot caching\n }\n\n devTrack(\"scoped:selector:cache-miss\");\n lastRef.current = { state, selected: nextSelected };\n return nextSelected;\n }\n\n return {\n Context,\n Provider,\n useStore,\n useDispatch,\n useSelector,\n };\n}\n","// freeze.ts\nexport function deepFreeze<T>(obj: T, seen = new WeakSet<object>()): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const o = obj as unknown as object;\n if (seen.has(o)) return obj;\n seen.add(o);\n\n // Freeze children first\n for (const key of Object.getOwnPropertyNames(o)) {\n // @ts-expect-error index access\n const val = (o as any)[key];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n // Also handle symbols (rare but safe)\n for (const sym of Object.getOwnPropertySymbols(o)) {\n // @ts-expect-error index access\n const val = (o as any)[sym];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n\n return Object.freeze(obj);\n}\n","import type { Reducer, Listener } from \"./types.js\";\nimport { deepFreeze } from \"./freeze.js\";\n\nconst DEV =\n typeof import.meta !== \"undefined\"\n ? (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV\n : process.env.NODE_ENV !== \"production\";\n\n// Lightweight DEV-only tracker\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!DEV) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\n// Allow narrower parameter types for callbacks without fighting variance\ntype BivariantListener<T> = {\n bivarianceHack(value: T): void;\n}[\"bivarianceHack\"];\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IState {}\nexport interface IAction {\n type: string;\n}\n\nexport interface Store<S extends IState, A extends IAction> {\n getState(): S;\n dispatch(action: A): void;\n /**\n * Subscribe to all state changes.\n */\n subscribe(listener: Listener): () => void;\n /**\n * Subscribe to changes of a specific key in the state.\n */\n subscribeToKey<K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ): () => void;\n /**\n * Subscribe to changes in a selected value from the state.\n */\n subscribeWithSelector<T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ): () => void;\n}\n\nexport function createStore<S extends IState, A extends IAction>(\n reducer: Reducer<S, A>,\n initialState: S\n): Store<S, A> {\n let state: S = DEV ? deepFreeze(initialState) : initialState;\n const listeners = new Set<Listener>();\n const keyListeners = new Map<keyof S, Set<BivariantListener<S[keyof S]>>>();\n\n interface SelectorEntry<T> {\n selector: (state: S) => T;\n listener: BivariantListener<T>;\n lastValue: T;\n }\n const selectorListeners = new Set<SelectorEntry<unknown>>();\n\n const getState = () => state;\n\n const dispatch = (action: A) => {\n const prevState = state;\n const nextState = reducer(state, action);\n\n if (DEV) deepFreeze(nextState);\n\n // Track the inbound action\n devTrack(\"store:dispatch\", { type: action?.type });\n\n // Distinct-until-changed: if the reducer returns the same reference,\n // skip all notifications (prevents unnecessary re-renders).\n if (Object.is(prevState, nextState)) {\n state = nextState; // keep any identity guarantees from reducer\n devTrack(\"store:no-op\", { type: action?.type });\n return;\n }\n\n state = nextState;\n\n // Compute changed keys (shallow) for diagnostics\n let changedKeys: (keyof S)[] | undefined;\n if (DEV) {\n changedKeys = Object.keys(nextState as Record<string, unknown>)\n .filter((k) => !Object.is((prevState as any)[k], (nextState as any)[k])) as (keyof S)[];\n devTrack(\"store:state-changed\", { type: action?.type, changedKeys });\n }\n\n // Notify global listeners (iterate over a snapshot so unsubscribe during\n // notify does not skip the next listener)\n const globalSnapshot = [...listeners];\n devTrack(\"store:notify:all\", { listeners: globalSnapshot.length });\n for (const listener of globalSnapshot) listener();\n\n // Notify key listeners only when that key actually changed (Object.is)\n for (const [key, set] of keyListeners.entries()) {\n if (!Object.is(prevState[key], state[key])) {\n devTrack(\"store:notify:key\", { key: String(key), listeners: set.size });\n for (const listener of [...set]) listener(state[key]);\n }\n }\n\n // Notify selector listeners only when selected value changed (Object.is)\n let selNotifies = 0;\n selectorListeners.forEach((entry) => {\n const nextValue = (entry.selector as (s: S) => unknown)(state);\n if (!Object.is(entry.lastValue, nextValue)) {\n entry.lastValue = nextValue as unknown;\n (entry.listener as (v: unknown) => void)(nextValue);\n selNotifies++;\n }\n });\n devTrack(\"store:notify:selector\", { listeners: selNotifies });\n };\n\n const subscribe = (listener: Listener) => {\n listeners.add(listener);\n devTrack(\"store:sub:all:add\", { size: listeners.size });\n return () => {\n listeners.delete(listener);\n devTrack(\"store:sub:all:remove\", { size: listeners.size });\n };\n };\n\n const subscribeToKey = <K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ) => {\n const set =\n keyListeners.get(key) ?? new Set<BivariantListener<S[keyof S]>>();\n set.add(listener as unknown as BivariantListener<S[keyof S]>);\n keyListeners.set(key, set);\n devTrack(\"store:sub:key:add\", { key: String(key), size: set.size });\n return () => {\n set.delete(listener as unknown as BivariantListener<S[keyof S]>);\n if (set.size === 0) keyListeners.delete(key);\n devTrack(\"store:sub:key:remove\", { key: String(key), size: set.size });\n };\n };\n\n const subscribeWithSelector = <T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ) => {\n const entry: SelectorEntry<T> = {\n selector,\n listener: listener as BivariantListener<T>,\n lastValue: selector(state),\n };\n selectorListeners.add(entry as unknown as SelectorEntry<unknown>);\n devTrack(\"store:sub:selector:add\", { size: selectorListeners.size });\n return () => {\n selectorListeners.delete(entry as unknown as SelectorEntry<unknown>);\n devTrack(\"store:sub:selector:remove\", { size: selectorListeners.size });\n };\n };\n\n return {\n getState,\n dispatch,\n subscribe,\n subscribeToKey,\n subscribeWithSelector,\n };\n}\n","import React, { createContext, useContext, useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Store, IState, IAction } from \"./store.js\";\n\n// DEV-only tracking (no-op in production)\nconst __DEV__ = typeof process !== \"undefined\" ? process.env.NODE_ENV !== \"production\" : true;\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!__DEV__) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\nconst StoreContext = createContext<Store<IState, IAction> | undefined>(undefined);\n\nfunction useStoreInstance<S extends IState, A extends IAction>(): Store<S, A> {\n const store = useContext(StoreContext) as Store<S, A> | undefined;\n if (!store) {\n devTrack(\"store:provider:missing\");\n throw new Error(\n \"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>.\"\n );\n }\n return store;\n}\n\ninterface StoreProviderProps<S extends IState, A extends IAction> {\n store: Store<S, A>;\n children: ReactNode;\n}\n\nexport function StoreProvider<S extends IState, A extends IAction>({\n store,\n children,\n}: StoreProviderProps<S, A>) {\n useEffect(() => {\n devTrack(\"store:provider:mount\", { hasStore: !!store });\n return () => devTrack(\"store:provider:unmount\");\n }, [store]);\n return (\n <StoreContext.Provider value={store as unknown as Store<IState, IAction>}>\n {children}\n </StoreContext.Provider>\n );\n}\n\nexport function useStore<S extends IState>(): S {\n const store = useStoreInstance<S, IAction>();\n const [state, setState] = useState<S>(() => store.getState());\n const prevRef = React.useRef<S>(state);\n\n useEffect(() => {\n devTrack(\"store:react:subscribe\");\n const unsubscribe = store.subscribe(() => {\n const next = store.getState();\n if (!Object.is(prevRef.current, next)) {\n if (__DEV__) {\n try {\n const prev = prevRef.current as unknown as Record<string, unknown>;\n const cur = next as unknown as Record<string, unknown>;\n const changedKeys = Array.from(\n new Set([...Object.keys(prev || {}), ...Object.keys(cur || {})])\n ).filter((k) => !Object.is(prev?.[k], cur?.[k]));\n devTrack(\"store:react:update\", {\n changed: changedKeys,\n count: changedKeys.length,\n });\n } catch {}\n }\n prevRef.current = next;\n setState(next);\n } else {\n devTrack(\"store:react:no-op\");\n }\n });\n return () => {\n devTrack(\"store:react:unsubscribe\");\n unsubscribe();\n };\n }, [store]);\n\n return state;\n}\n\nexport function useDispatch<A extends IAction>(): Store<IState, A>[\"dispatch\"] {\n const store = useStoreInstance<IState, A>();\n // Return the store's dispatch directly; consumers can call dispatch(action).\n return store.dispatch as Store<IState, A>[\"dispatch\"];\n}\n","// metadata-store.ts\nexport class MetadataStore<T extends object, Meta extends object> {\n private readonly symbol: symbol;\n\n constructor(description: string) {\n this.symbol = Symbol(description);\n }\n\n set(target: T, meta: Meta) {\n Object.defineProperty(target, this.symbol as PropertyKey, {\n value: meta,\n writable: false,\n enumerable: false,\n });\n }\n\n get(target: T): Meta | undefined {\n return (target as Record<PropertyKey, Meta>)[this.symbol as PropertyKey];\n }\n\n has(target: T): boolean {\n return (this.symbol as PropertyKey) in target;\n }\n}\n"],"mappings":"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","names":["devTrack","useStore","useDispatch","import_react","import_jsx_runtime","__DEV__","devTrack","React"]}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var __noop = null;
|
|
3
3
|
|
|
4
4
|
// src/create-scoped-store.tsx
|
|
5
|
-
import { createContext, useContext, useRef, useSyncExternalStore, useCallback } from "react";
|
|
5
|
+
import { createContext, useContext, useRef, useSyncExternalStore, useCallback, useEffect } from "react";
|
|
6
6
|
|
|
7
7
|
// src/freeze.ts
|
|
8
8
|
function deepFreeze(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
@@ -23,6 +23,14 @@ function deepFreeze(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
23
23
|
|
|
24
24
|
// src/store.ts
|
|
25
25
|
var DEV = typeof import.meta !== "undefined" ? import.meta.env?.DEV : process.env.NODE_ENV !== "production";
|
|
26
|
+
function devTrack(name, props) {
|
|
27
|
+
if (!DEV) return;
|
|
28
|
+
try {
|
|
29
|
+
const t = globalThis?.track;
|
|
30
|
+
if (typeof t === "function") t(name, props);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
function createStore(reducer, initialState) {
|
|
27
35
|
let state = DEV ? deepFreeze(initialState) : initialState;
|
|
28
36
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -33,38 +41,55 @@ function createStore(reducer, initialState) {
|
|
|
33
41
|
const prevState = state;
|
|
34
42
|
const nextState = reducer(state, action);
|
|
35
43
|
if (DEV) deepFreeze(nextState);
|
|
44
|
+
devTrack("store:dispatch", { type: action?.type });
|
|
36
45
|
if (Object.is(prevState, nextState)) {
|
|
37
46
|
state = nextState;
|
|
47
|
+
devTrack("store:no-op", { type: action?.type });
|
|
38
48
|
return;
|
|
39
49
|
}
|
|
40
50
|
state = nextState;
|
|
41
|
-
|
|
51
|
+
let changedKeys;
|
|
52
|
+
if (DEV) {
|
|
53
|
+
changedKeys = Object.keys(nextState).filter((k) => !Object.is(prevState[k], nextState[k]));
|
|
54
|
+
devTrack("store:state-changed", { type: action?.type, changedKeys });
|
|
55
|
+
}
|
|
56
|
+
const globalSnapshot = [...listeners];
|
|
57
|
+
devTrack("store:notify:all", { listeners: globalSnapshot.length });
|
|
58
|
+
for (const listener of globalSnapshot) listener();
|
|
42
59
|
for (const [key, set] of keyListeners.entries()) {
|
|
43
60
|
if (!Object.is(prevState[key], state[key])) {
|
|
61
|
+
devTrack("store:notify:key", { key: String(key), listeners: set.size });
|
|
44
62
|
for (const listener of [...set]) listener(state[key]);
|
|
45
63
|
}
|
|
46
64
|
}
|
|
65
|
+
let selNotifies = 0;
|
|
47
66
|
selectorListeners.forEach((entry) => {
|
|
48
67
|
const nextValue = entry.selector(state);
|
|
49
68
|
if (!Object.is(entry.lastValue, nextValue)) {
|
|
50
69
|
entry.lastValue = nextValue;
|
|
51
70
|
entry.listener(nextValue);
|
|
71
|
+
selNotifies++;
|
|
52
72
|
}
|
|
53
73
|
});
|
|
74
|
+
devTrack("store:notify:selector", { listeners: selNotifies });
|
|
54
75
|
};
|
|
55
76
|
const subscribe = (listener) => {
|
|
56
77
|
listeners.add(listener);
|
|
78
|
+
devTrack("store:sub:all:add", { size: listeners.size });
|
|
57
79
|
return () => {
|
|
58
80
|
listeners.delete(listener);
|
|
81
|
+
devTrack("store:sub:all:remove", { size: listeners.size });
|
|
59
82
|
};
|
|
60
83
|
};
|
|
61
84
|
const subscribeToKey = (key, listener) => {
|
|
62
85
|
const set = keyListeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
63
86
|
set.add(listener);
|
|
64
87
|
keyListeners.set(key, set);
|
|
88
|
+
devTrack("store:sub:key:add", { key: String(key), size: set.size });
|
|
65
89
|
return () => {
|
|
66
90
|
set.delete(listener);
|
|
67
91
|
if (set.size === 0) keyListeners.delete(key);
|
|
92
|
+
devTrack("store:sub:key:remove", { key: String(key), size: set.size });
|
|
68
93
|
};
|
|
69
94
|
};
|
|
70
95
|
const subscribeWithSelector = (selector, listener) => {
|
|
@@ -74,8 +99,10 @@ function createStore(reducer, initialState) {
|
|
|
74
99
|
lastValue: selector(state)
|
|
75
100
|
};
|
|
76
101
|
selectorListeners.add(entry);
|
|
102
|
+
devTrack("store:sub:selector:add", { size: selectorListeners.size });
|
|
77
103
|
return () => {
|
|
78
104
|
selectorListeners.delete(entry);
|
|
105
|
+
devTrack("store:sub:selector:remove", { size: selectorListeners.size });
|
|
79
106
|
};
|
|
80
107
|
};
|
|
81
108
|
return {
|
|
@@ -89,6 +116,15 @@ function createStore(reducer, initialState) {
|
|
|
89
116
|
|
|
90
117
|
// src/create-scoped-store.tsx
|
|
91
118
|
import { jsx } from "react/jsx-runtime";
|
|
119
|
+
var __DEV__ = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
120
|
+
function devTrack2(name, props) {
|
|
121
|
+
if (!__DEV__) return;
|
|
122
|
+
try {
|
|
123
|
+
const t = globalThis?.track;
|
|
124
|
+
if (typeof t === "function") t(name, props);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
92
128
|
var _queue = /* @__PURE__ */ new Set();
|
|
93
129
|
var _queued = false;
|
|
94
130
|
var _schedule = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => Promise.resolve().then(fn);
|
|
@@ -101,10 +137,22 @@ function enqueue(fn) {
|
|
|
101
137
|
const fns = Array.from(_queue);
|
|
102
138
|
_queue.clear();
|
|
103
139
|
for (const f of fns) f();
|
|
140
|
+
devTrack2("scoped:batch:flush", { size: fns.length });
|
|
104
141
|
});
|
|
105
142
|
}
|
|
106
143
|
function makeBatchedSubscribe(subscribe) {
|
|
107
|
-
return (onChange) =>
|
|
144
|
+
return (onChange) => {
|
|
145
|
+
devTrack2("scoped:sub:add");
|
|
146
|
+
const wrapped = () => {
|
|
147
|
+
devTrack2("scoped:notify:enqueue");
|
|
148
|
+
enqueue(onChange);
|
|
149
|
+
};
|
|
150
|
+
const unsubscribe = subscribe(wrapped);
|
|
151
|
+
return () => {
|
|
152
|
+
devTrack2("scoped:sub:remove");
|
|
153
|
+
unsubscribe();
|
|
154
|
+
};
|
|
155
|
+
};
|
|
108
156
|
}
|
|
109
157
|
function shallowEqual(a, b) {
|
|
110
158
|
if (Object.is(a, b)) return true;
|
|
@@ -128,12 +176,18 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
128
176
|
const storeRef = useRef(null);
|
|
129
177
|
if (!storeRef.current) {
|
|
130
178
|
storeRef.current = createStore(reducer, override ?? initialState);
|
|
179
|
+
devTrack2("scoped:store:create");
|
|
131
180
|
}
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
devTrack2("scoped:provider:mount");
|
|
183
|
+
return () => devTrack2("scoped:provider:unmount");
|
|
184
|
+
}, []);
|
|
132
185
|
return /* @__PURE__ */ jsx(Context.Provider, { value: storeRef.current, children });
|
|
133
186
|
};
|
|
134
187
|
const useStore2 = () => {
|
|
135
188
|
const ctx = useContext(Context);
|
|
136
189
|
if (!ctx) throw new Error("Store not found in context");
|
|
190
|
+
devTrack2("scoped:useStore");
|
|
137
191
|
return useSyncExternalStore(
|
|
138
192
|
makeBatchedSubscribe(ctx.subscribe),
|
|
139
193
|
ctx.getState,
|
|
@@ -157,8 +211,10 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
157
211
|
const last = lastRef.current;
|
|
158
212
|
const nextSelected = selector(state);
|
|
159
213
|
if (last && last.state === state && isEqual(last.selected, nextSelected)) {
|
|
214
|
+
devTrack2("scoped:selector:cache-hit");
|
|
160
215
|
return last.selected;
|
|
161
216
|
}
|
|
217
|
+
devTrack2("scoped:selector:cache-miss");
|
|
162
218
|
lastRef.current = { state, selected: nextSelected };
|
|
163
219
|
return nextSelected;
|
|
164
220
|
}
|
|
@@ -172,12 +228,22 @@ function createScopedStoreContext(reducer, initialState) {
|
|
|
172
228
|
}
|
|
173
229
|
|
|
174
230
|
// src/provider.tsx
|
|
175
|
-
import { createContext as createContext2, useContext as useContext2, useEffect, useState } from "react";
|
|
231
|
+
import React, { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState } from "react";
|
|
176
232
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
233
|
+
var __DEV__2 = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
234
|
+
function devTrack3(name, props) {
|
|
235
|
+
if (!__DEV__2) return;
|
|
236
|
+
try {
|
|
237
|
+
const t = globalThis?.track;
|
|
238
|
+
if (typeof t === "function") t(name, props);
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
}
|
|
177
242
|
var StoreContext = createContext2(void 0);
|
|
178
243
|
function useStoreInstance() {
|
|
179
244
|
const store = useContext2(StoreContext);
|
|
180
245
|
if (!store) {
|
|
246
|
+
devTrack3("store:provider:missing");
|
|
181
247
|
throw new Error(
|
|
182
248
|
"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>."
|
|
183
249
|
);
|
|
@@ -188,16 +254,45 @@ function StoreProvider({
|
|
|
188
254
|
store,
|
|
189
255
|
children
|
|
190
256
|
}) {
|
|
257
|
+
useEffect2(() => {
|
|
258
|
+
devTrack3("store:provider:mount", { hasStore: !!store });
|
|
259
|
+
return () => devTrack3("store:provider:unmount");
|
|
260
|
+
}, [store]);
|
|
191
261
|
return /* @__PURE__ */ jsx2(StoreContext.Provider, { value: store, children });
|
|
192
262
|
}
|
|
193
263
|
function useStore() {
|
|
194
264
|
const store = useStoreInstance();
|
|
195
265
|
const [state, setState] = useState(() => store.getState());
|
|
196
|
-
|
|
266
|
+
const prevRef = React.useRef(state);
|
|
267
|
+
useEffect2(() => {
|
|
268
|
+
devTrack3("store:react:subscribe");
|
|
197
269
|
const unsubscribe = store.subscribe(() => {
|
|
198
|
-
|
|
270
|
+
const next = store.getState();
|
|
271
|
+
if (!Object.is(prevRef.current, next)) {
|
|
272
|
+
if (__DEV__2) {
|
|
273
|
+
try {
|
|
274
|
+
const prev = prevRef.current;
|
|
275
|
+
const cur = next;
|
|
276
|
+
const changedKeys = Array.from(
|
|
277
|
+
/* @__PURE__ */ new Set([...Object.keys(prev || {}), ...Object.keys(cur || {})])
|
|
278
|
+
).filter((k) => !Object.is(prev?.[k], cur?.[k]));
|
|
279
|
+
devTrack3("store:react:update", {
|
|
280
|
+
changed: changedKeys,
|
|
281
|
+
count: changedKeys.length
|
|
282
|
+
});
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
prevRef.current = next;
|
|
287
|
+
setState(next);
|
|
288
|
+
} else {
|
|
289
|
+
devTrack3("store:react:no-op");
|
|
290
|
+
}
|
|
199
291
|
});
|
|
200
|
-
return
|
|
292
|
+
return () => {
|
|
293
|
+
devTrack3("store:react:unsubscribe");
|
|
294
|
+
unsubscribe();
|
|
295
|
+
};
|
|
201
296
|
}, [store]);
|
|
202
297
|
return state;
|
|
203
298
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/create-scoped-store.tsx","../src/freeze.ts","../src/store.ts","../src/provider.tsx","../src/metadata-store.ts"],"sourcesContent":["export type Reducer<S, A> = (state: S, action: A) => S;\nexport type Listener = () => void;\nexport type Unsubscribe = () => void;\nexport const __noop = null;","import { createContext, useContext, useRef, useSyncExternalStore, useCallback } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\n\n// Local microtask-based batching (no react-dom dependency)\nconst _queue = new Set<() => void>();\nlet _queued = false;\nconst _schedule = typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (fn: () => void) => Promise.resolve().then(fn);\n\nfunction enqueue(fn: () => void) {\n _queue.add(fn);\n if (_queued) return;\n _queued = true;\n _schedule(() => {\n _queued = false;\n const fns = Array.from(_queue);\n _queue.clear();\n for (const f of fns) f();\n });\n}\n\nfunction makeBatchedSubscribe(subscribe: (l: () => void) => () => void) {\n return (onChange: () => void) => subscribe(() => enqueue(onChange));\n}\n\nfunction shallowEqual(a: any, b: any) {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== \"object\" ||\n a === null ||\n typeof b !== \"object\" ||\n b === null\n )\n return false;\n const ak = Object.keys(a),\n bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n const k = ak[i] as string;\n if (\n !Object.prototype.hasOwnProperty.call(b, k) ||\n !Object.is((a as any)[k], (b as any)[k])\n )\n return false;\n }\n return true;\n}\n\nexport function createScopedStoreContext<S extends IState, A extends IAction>(\n reducer: (state: S, action: A) => S,\n initialState: S\n) {\n const Context = createContext<Store<S, A> | null>(null);\n\n // Each Provider instance gets its own store.\n const Provider = ({\n children,\n initialState: override,\n }: {\n children: React.ReactNode;\n initialState?: S;\n }) => {\n const storeRef = useRef<Store<S, A> | null>(null);\n if (!storeRef.current) {\n storeRef.current = createStore(reducer, override ?? initialState);\n }\n return <Context.Provider value={storeRef.current}>{children}</Context.Provider>;\n };\n\n const useStore = (): S => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n return useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n };\n\n const useDispatch = (): ((action: A) => void) => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Dispatch not found in context\");\n return useCallback((action: A) => ctx.dispatch(action), [ctx]);\n };\n\n function useSelector<T>(\n selector: (state: S) => T,\n isEqual: (a: T, b: T) => boolean = shallowEqual\n ): T {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n\n // Subscribe to the raw state snapshot (stable reference until a dispatch)\n const state = useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n\n // Cache the selected slice per state snapshot to avoid returning fresh objects during render\n const lastRef = useRef<{ state: S; selected: T } | null>(null);\n const last = lastRef.current;\n const nextSelected = selector(state);\n\n if (last && last.state === state && isEqual(last.selected, nextSelected)) {\n return last.selected; // return cached reference to satisfy getSnapshot caching\n }\n\n lastRef.current = { state, selected: nextSelected };\n return nextSelected;\n }\n\n return {\n Context,\n Provider,\n useStore,\n useDispatch,\n useSelector,\n };\n}\n","// freeze.ts\nexport function deepFreeze<T>(obj: T, seen = new WeakSet<object>()): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const o = obj as unknown as object;\n if (seen.has(o)) return obj;\n seen.add(o);\n\n // Freeze children first\n for (const key of Object.getOwnPropertyNames(o)) {\n // @ts-expect-error index access\n const val = (o as any)[key];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n // Also handle symbols (rare but safe)\n for (const sym of Object.getOwnPropertySymbols(o)) {\n // @ts-expect-error index access\n const val = (o as any)[sym];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n\n return Object.freeze(obj);\n}\n","import type { Reducer, Listener } from \"./types.js\";\nimport { deepFreeze } from \"./freeze.js\";\n\nconst DEV =\n typeof import.meta !== \"undefined\"\n ? (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV\n : process.env.NODE_ENV !== \"production\";\n\n// Allow narrower parameter types for callbacks without fighting variance\ntype BivariantListener<T> = {\n bivarianceHack(value: T): void;\n}[\"bivarianceHack\"];\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IState {}\nexport interface IAction {\n type: string;\n}\n\nexport interface Store<S extends IState, A extends IAction> {\n getState(): S;\n dispatch(action: A): void;\n /**\n * Subscribe to all state changes.\n */\n subscribe(listener: Listener): () => void;\n /**\n * Subscribe to changes of a specific key in the state.\n */\n subscribeToKey<K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ): () => void;\n /**\n * Subscribe to changes in a selected value from the state.\n */\n subscribeWithSelector<T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ): () => void;\n}\n\nexport function createStore<S extends IState, A extends IAction>(\n reducer: Reducer<S, A>,\n initialState: S\n): Store<S, A> {\n let state: S = DEV ? deepFreeze(initialState) : initialState;\n const listeners = new Set<Listener>();\n const keyListeners = new Map<keyof S, Set<BivariantListener<S[keyof S]>>>();\n\n interface SelectorEntry<T> {\n selector: (state: S) => T;\n listener: BivariantListener<T>;\n lastValue: T;\n }\n const selectorListeners = new Set<SelectorEntry<unknown>>();\n\n const getState = () => state;\n\n const dispatch = (action: A) => {\n const prevState = state;\n const nextState = reducer(state, action);\n \n if (DEV) deepFreeze(nextState);\n \n // Distinct-until-changed: if the reducer returns the same reference,\n // skip all notifications (prevents unnecessary re-renders).\n if (Object.is(prevState, nextState)) {\n state = nextState; // keep any identity guarantees from reducer\n return;\n }\n\n state = nextState;\n\n // Notify global listeners (iterate over a snapshot so unsubscribe during\n // notify does not skip the next listener)\n for (const listener of [...listeners]) listener();\n\n // Notify key listeners only when that key actually changed (Object.is)\n for (const [key, set] of keyListeners.entries()) {\n if (!Object.is(prevState[key], state[key])) {\n for (const listener of [...set]) listener(state[key]);\n }\n }\n\n // Notify selector listeners only when selected value changed (Object.is)\n selectorListeners.forEach((entry) => {\n const nextValue = (entry.selector as (s: S) => unknown)(state);\n if (!Object.is(entry.lastValue, nextValue)) {\n entry.lastValue = nextValue as unknown;\n (entry.listener as (v: unknown) => void)(nextValue);\n }\n });\n };\n\n const subscribe = (listener: Listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n };\n\n const subscribeToKey = <K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ) => {\n const set =\n keyListeners.get(key) ?? new Set<BivariantListener<S[keyof S]>>();\n set.add(listener as unknown as BivariantListener<S[keyof S]>);\n keyListeners.set(key, set);\n return () => {\n set.delete(listener as unknown as BivariantListener<S[keyof S]>);\n if (set.size === 0) keyListeners.delete(key);\n };\n };\n\n const subscribeWithSelector = <T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ) => {\n const entry: SelectorEntry<T> = {\n selector,\n listener: listener as BivariantListener<T>,\n lastValue: selector(state),\n };\n selectorListeners.add(entry as unknown as SelectorEntry<unknown>);\n return () => {\n selectorListeners.delete(entry as unknown as SelectorEntry<unknown>);\n };\n };\n\n return {\n getState,\n dispatch,\n subscribe,\n subscribeToKey,\n subscribeWithSelector,\n };\n}\n","import React, { createContext, useContext, useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Store, IState, IAction } from \"./store.js\";\n\nconst StoreContext = createContext<Store<IState, IAction> | undefined>(undefined);\n\nfunction useStoreInstance<S extends IState, A extends IAction>(): Store<S, A> {\n const store = useContext(StoreContext) as Store<S, A> | undefined;\n if (!store) {\n throw new Error(\n \"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>.\"\n );\n }\n return store;\n}\n\ninterface StoreProviderProps<S extends IState, A extends IAction> {\n store: Store<S, A>;\n children: ReactNode;\n}\n\nexport function StoreProvider<S extends IState, A extends IAction>({\n store,\n children,\n}: StoreProviderProps<S, A>) {\n return (\n <StoreContext.Provider value={store as unknown as Store<IState, IAction>}>\n {children}\n </StoreContext.Provider>\n );\n}\n\nexport function useStore<S extends IState>(): S {\n const store = useStoreInstance<S, IAction>();\n const [state, setState] = useState<S>(() => store.getState());\n\n useEffect(() => {\n // Subscribe to store changes and update local state.\n const unsubscribe = store.subscribe(() => {\n setState(store.getState());\n });\n return unsubscribe;\n }, [store]);\n\n return state;\n}\n\nexport function useDispatch<A extends IAction>(): Store<IState, A>[\"dispatch\"] {\n const store = useStoreInstance<IState, A>();\n // Return the store's dispatch directly; consumers can call dispatch(action).\n return store.dispatch as Store<IState, A>[\"dispatch\"];\n}\n","// metadata-store.ts\nexport class MetadataStore<T extends object, Meta extends object> {\n private readonly symbol: symbol;\n\n constructor(description: string) {\n this.symbol = Symbol(description);\n }\n\n set(target: T, meta: Meta) {\n Object.defineProperty(target, this.symbol as PropertyKey, {\n value: meta,\n writable: false,\n enumerable: false,\n });\n }\n\n get(target: T): Meta | undefined {\n return (target as Record<PropertyKey, Meta>)[this.symbol as PropertyKey];\n }\n\n has(target: T): boolean {\n return (this.symbol as PropertyKey) in target;\n }\n}\n"],"mappings":"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","names":["useStore","useDispatch","createContext","useContext","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/create-scoped-store.tsx","../src/freeze.ts","../src/store.ts","../src/provider.tsx","../src/metadata-store.ts"],"sourcesContent":["export type Reducer<S, A> = (state: S, action: A) => S;\nexport type Listener = () => void;\nexport type Unsubscribe = () => void;\nexport const __noop = null;","import { createContext, useContext, useRef, useSyncExternalStore, useCallback, useEffect } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\n\n// DEV-only tracking (no-op in production)\nconst __DEV__ = typeof process !== \"undefined\" ? process.env.NODE_ENV !== \"production\" : true;\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!__DEV__) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\n// Local microtask-based batching (no react-dom dependency)\nconst _queue = new Set<() => void>();\nlet _queued = false;\nconst _schedule = typeof queueMicrotask === \"function\"\n ? queueMicrotask\n : (fn: () => void) => Promise.resolve().then(fn);\n\nfunction enqueue(fn: () => void) {\n _queue.add(fn);\n if (_queued) return;\n _queued = true;\n _schedule(() => {\n _queued = false;\n const fns = Array.from(_queue);\n _queue.clear();\n for (const f of fns) f();\n devTrack(\"scoped:batch:flush\", { size: fns.length });\n });\n}\n\nfunction makeBatchedSubscribe(subscribe: (l: () => void) => () => void) {\n return (onChange: () => void) => {\n devTrack(\"scoped:sub:add\");\n const wrapped = () => {\n devTrack(\"scoped:notify:enqueue\");\n enqueue(onChange);\n };\n const unsubscribe = subscribe(wrapped);\n return () => {\n devTrack(\"scoped:sub:remove\");\n unsubscribe();\n };\n };\n}\n\nfunction shallowEqual(a: any, b: any) {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== \"object\" ||\n a === null ||\n typeof b !== \"object\" ||\n b === null\n )\n return false;\n const ak = Object.keys(a),\n bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n const k = ak[i] as string;\n if (\n !Object.prototype.hasOwnProperty.call(b, k) ||\n !Object.is((a as any)[k], (b as any)[k])\n )\n return false;\n }\n return true;\n}\n\nexport function createScopedStoreContext<S extends IState, A extends IAction>(\n reducer: (state: S, action: A) => S,\n initialState: S\n) {\n const Context = createContext<Store<S, A> | null>(null);\n\n // Each Provider instance gets its own store.\n const Provider = ({\n children,\n initialState: override,\n }: {\n children: React.ReactNode;\n initialState?: S;\n }) => {\n const storeRef = useRef<Store<S, A> | null>(null);\n if (!storeRef.current) {\n storeRef.current = createStore(reducer, override ?? initialState);\n devTrack(\"scoped:store:create\");\n }\n\n useEffect(() => {\n devTrack(\"scoped:provider:mount\");\n return () => devTrack(\"scoped:provider:unmount\");\n }, []);\n\n return <Context.Provider value={storeRef.current}>{children}</Context.Provider>;\n };\n\n const useStore = (): S => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n devTrack(\"scoped:useStore\");\n return useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n };\n\n const useDispatch = (): ((action: A) => void) => {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Dispatch not found in context\");\n return useCallback((action: A) => ctx.dispatch(action), [ctx]);\n };\n\n function useSelector<T>(\n selector: (state: S) => T,\n isEqual: (a: T, b: T) => boolean = shallowEqual\n ): T {\n const ctx = useContext(Context);\n if (!ctx) throw new Error(\"Store not found in context\");\n\n // Subscribe to the raw state snapshot (stable reference until a dispatch)\n const state = useSyncExternalStore(\n makeBatchedSubscribe(ctx.subscribe),\n ctx.getState,\n ctx.getState\n );\n\n // Cache the selected slice per state snapshot to avoid returning fresh objects during render\n const lastRef = useRef<{ state: S; selected: T } | null>(null);\n const last = lastRef.current;\n const nextSelected = selector(state);\n\n if (last && last.state === state && isEqual(last.selected, nextSelected)) {\n devTrack(\"scoped:selector:cache-hit\");\n return last.selected; // return cached reference to satisfy getSnapshot caching\n }\n\n devTrack(\"scoped:selector:cache-miss\");\n lastRef.current = { state, selected: nextSelected };\n return nextSelected;\n }\n\n return {\n Context,\n Provider,\n useStore,\n useDispatch,\n useSelector,\n };\n}\n","// freeze.ts\nexport function deepFreeze<T>(obj: T, seen = new WeakSet<object>()): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const o = obj as unknown as object;\n if (seen.has(o)) return obj;\n seen.add(o);\n\n // Freeze children first\n for (const key of Object.getOwnPropertyNames(o)) {\n // @ts-expect-error index access\n const val = (o as any)[key];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n // Also handle symbols (rare but safe)\n for (const sym of Object.getOwnPropertySymbols(o)) {\n // @ts-expect-error index access\n const val = (o as any)[sym];\n if (val && typeof val === \"object\") deepFreeze(val, seen);\n }\n\n return Object.freeze(obj);\n}\n","import type { Reducer, Listener } from \"./types.js\";\nimport { deepFreeze } from \"./freeze.js\";\n\nconst DEV =\n typeof import.meta !== \"undefined\"\n ? (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV\n : process.env.NODE_ENV !== \"production\";\n\n// Lightweight DEV-only tracker\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!DEV) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\n// Allow narrower parameter types for callbacks without fighting variance\ntype BivariantListener<T> = {\n bivarianceHack(value: T): void;\n}[\"bivarianceHack\"];\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IState {}\nexport interface IAction {\n type: string;\n}\n\nexport interface Store<S extends IState, A extends IAction> {\n getState(): S;\n dispatch(action: A): void;\n /**\n * Subscribe to all state changes.\n */\n subscribe(listener: Listener): () => void;\n /**\n * Subscribe to changes of a specific key in the state.\n */\n subscribeToKey<K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ): () => void;\n /**\n * Subscribe to changes in a selected value from the state.\n */\n subscribeWithSelector<T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ): () => void;\n}\n\nexport function createStore<S extends IState, A extends IAction>(\n reducer: Reducer<S, A>,\n initialState: S\n): Store<S, A> {\n let state: S = DEV ? deepFreeze(initialState) : initialState;\n const listeners = new Set<Listener>();\n const keyListeners = new Map<keyof S, Set<BivariantListener<S[keyof S]>>>();\n\n interface SelectorEntry<T> {\n selector: (state: S) => T;\n listener: BivariantListener<T>;\n lastValue: T;\n }\n const selectorListeners = new Set<SelectorEntry<unknown>>();\n\n const getState = () => state;\n\n const dispatch = (action: A) => {\n const prevState = state;\n const nextState = reducer(state, action);\n\n if (DEV) deepFreeze(nextState);\n\n // Track the inbound action\n devTrack(\"store:dispatch\", { type: action?.type });\n\n // Distinct-until-changed: if the reducer returns the same reference,\n // skip all notifications (prevents unnecessary re-renders).\n if (Object.is(prevState, nextState)) {\n state = nextState; // keep any identity guarantees from reducer\n devTrack(\"store:no-op\", { type: action?.type });\n return;\n }\n\n state = nextState;\n\n // Compute changed keys (shallow) for diagnostics\n let changedKeys: (keyof S)[] | undefined;\n if (DEV) {\n changedKeys = Object.keys(nextState as Record<string, unknown>)\n .filter((k) => !Object.is((prevState as any)[k], (nextState as any)[k])) as (keyof S)[];\n devTrack(\"store:state-changed\", { type: action?.type, changedKeys });\n }\n\n // Notify global listeners (iterate over a snapshot so unsubscribe during\n // notify does not skip the next listener)\n const globalSnapshot = [...listeners];\n devTrack(\"store:notify:all\", { listeners: globalSnapshot.length });\n for (const listener of globalSnapshot) listener();\n\n // Notify key listeners only when that key actually changed (Object.is)\n for (const [key, set] of keyListeners.entries()) {\n if (!Object.is(prevState[key], state[key])) {\n devTrack(\"store:notify:key\", { key: String(key), listeners: set.size });\n for (const listener of [...set]) listener(state[key]);\n }\n }\n\n // Notify selector listeners only when selected value changed (Object.is)\n let selNotifies = 0;\n selectorListeners.forEach((entry) => {\n const nextValue = (entry.selector as (s: S) => unknown)(state);\n if (!Object.is(entry.lastValue, nextValue)) {\n entry.lastValue = nextValue as unknown;\n (entry.listener as (v: unknown) => void)(nextValue);\n selNotifies++;\n }\n });\n devTrack(\"store:notify:selector\", { listeners: selNotifies });\n };\n\n const subscribe = (listener: Listener) => {\n listeners.add(listener);\n devTrack(\"store:sub:all:add\", { size: listeners.size });\n return () => {\n listeners.delete(listener);\n devTrack(\"store:sub:all:remove\", { size: listeners.size });\n };\n };\n\n const subscribeToKey = <K extends keyof S>(\n key: K,\n listener: (value: S[K]) => void\n ) => {\n const set =\n keyListeners.get(key) ?? new Set<BivariantListener<S[keyof S]>>();\n set.add(listener as unknown as BivariantListener<S[keyof S]>);\n keyListeners.set(key, set);\n devTrack(\"store:sub:key:add\", { key: String(key), size: set.size });\n return () => {\n set.delete(listener as unknown as BivariantListener<S[keyof S]>);\n if (set.size === 0) keyListeners.delete(key);\n devTrack(\"store:sub:key:remove\", { key: String(key), size: set.size });\n };\n };\n\n const subscribeWithSelector = <T>(\n selector: (state: S) => T,\n listener: (selected: T) => void\n ) => {\n const entry: SelectorEntry<T> = {\n selector,\n listener: listener as BivariantListener<T>,\n lastValue: selector(state),\n };\n selectorListeners.add(entry as unknown as SelectorEntry<unknown>);\n devTrack(\"store:sub:selector:add\", { size: selectorListeners.size });\n return () => {\n selectorListeners.delete(entry as unknown as SelectorEntry<unknown>);\n devTrack(\"store:sub:selector:remove\", { size: selectorListeners.size });\n };\n };\n\n return {\n getState,\n dispatch,\n subscribe,\n subscribeToKey,\n subscribeWithSelector,\n };\n}\n","import React, { createContext, useContext, useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Store, IState, IAction } from \"./store.js\";\n\n// DEV-only tracking (no-op in production)\nconst __DEV__ = typeof process !== \"undefined\" ? process.env.NODE_ENV !== \"production\" : true;\nfunction devTrack(name: string, props?: Record<string, unknown>) {\n if (!__DEV__) return;\n try {\n const t = (globalThis as any)?.track;\n if (typeof t === \"function\") t(name, props);\n } catch {}\n}\n\nconst StoreContext = createContext<Store<IState, IAction> | undefined>(undefined);\n\nfunction useStoreInstance<S extends IState, A extends IAction>(): Store<S, A> {\n const store = useContext(StoreContext) as Store<S, A> | undefined;\n if (!store) {\n devTrack(\"store:provider:missing\");\n throw new Error(\n \"StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>.\"\n );\n }\n return store;\n}\n\ninterface StoreProviderProps<S extends IState, A extends IAction> {\n store: Store<S, A>;\n children: ReactNode;\n}\n\nexport function StoreProvider<S extends IState, A extends IAction>({\n store,\n children,\n}: StoreProviderProps<S, A>) {\n useEffect(() => {\n devTrack(\"store:provider:mount\", { hasStore: !!store });\n return () => devTrack(\"store:provider:unmount\");\n }, [store]);\n return (\n <StoreContext.Provider value={store as unknown as Store<IState, IAction>}>\n {children}\n </StoreContext.Provider>\n );\n}\n\nexport function useStore<S extends IState>(): S {\n const store = useStoreInstance<S, IAction>();\n const [state, setState] = useState<S>(() => store.getState());\n const prevRef = React.useRef<S>(state);\n\n useEffect(() => {\n devTrack(\"store:react:subscribe\");\n const unsubscribe = store.subscribe(() => {\n const next = store.getState();\n if (!Object.is(prevRef.current, next)) {\n if (__DEV__) {\n try {\n const prev = prevRef.current as unknown as Record<string, unknown>;\n const cur = next as unknown as Record<string, unknown>;\n const changedKeys = Array.from(\n new Set([...Object.keys(prev || {}), ...Object.keys(cur || {})])\n ).filter((k) => !Object.is(prev?.[k], cur?.[k]));\n devTrack(\"store:react:update\", {\n changed: changedKeys,\n count: changedKeys.length,\n });\n } catch {}\n }\n prevRef.current = next;\n setState(next);\n } else {\n devTrack(\"store:react:no-op\");\n }\n });\n return () => {\n devTrack(\"store:react:unsubscribe\");\n unsubscribe();\n };\n }, [store]);\n\n return state;\n}\n\nexport function useDispatch<A extends IAction>(): Store<IState, A>[\"dispatch\"] {\n const store = useStoreInstance<IState, A>();\n // Return the store's dispatch directly; consumers can call dispatch(action).\n return store.dispatch as Store<IState, A>[\"dispatch\"];\n}\n","// metadata-store.ts\nexport class MetadataStore<T extends object, Meta extends object> {\n private readonly symbol: symbol;\n\n constructor(description: string) {\n this.symbol = Symbol(description);\n }\n\n set(target: T, meta: Meta) {\n Object.defineProperty(target, this.symbol as PropertyKey, {\n value: meta,\n writable: false,\n enumerable: false,\n });\n }\n\n get(target: T): Meta | undefined {\n return (target as Record<PropertyKey, Meta>)[this.symbol as PropertyKey];\n }\n\n has(target: T): boolean {\n return (this.symbol as PropertyKey) in target;\n }\n}\n"],"mappings":"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","names":["devTrack","useStore","useDispatch","createContext","useContext","useEffect","jsx","__DEV__","devTrack"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/react-state",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Tiny, testable, typesafe React Scoped Store helper.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"clean": "rimraf dist"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@types/react": "^19.1.13",
|
|
55
54
|
"@testing-library/jest-dom": "^6.8.0",
|
|
56
55
|
"@testing-library/react": "^16.3.0",
|
|
57
56
|
"@types/node": "^24.5.2",
|
|
57
|
+
"@types/react": "^19.1.13",
|
|
58
58
|
"@typescript-eslint/eslint-plugin": "^8.43.0",
|
|
59
59
|
"@typescript-eslint/parser": "^8.43.0",
|
|
60
60
|
"@vitest/coverage-v8": "^3.2.4",
|
|
@@ -80,5 +80,8 @@
|
|
|
80
80
|
"type": "github",
|
|
81
81
|
"url": "https://github.com/sponsors/Plasius-LTD"
|
|
82
82
|
}
|
|
83
|
-
]
|
|
83
|
+
],
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"@plasius/nfr": "^1.0.1"
|
|
86
|
+
}
|
|
84
87
|
}
|