@plasius/react-state 1.0.13 → 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 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
- for (const listener of [...listeners]) listener();
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,44 @@ 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
+ }
171
+ var _queue = /* @__PURE__ */ new Set();
172
+ var _queued = false;
173
+ var _schedule = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => Promise.resolve().then(fn);
174
+ function enqueue(fn) {
175
+ _queue.add(fn);
176
+ if (_queued) return;
177
+ _queued = true;
178
+ _schedule(() => {
179
+ _queued = false;
180
+ const fns = Array.from(_queue);
181
+ _queue.clear();
182
+ for (const f of fns) f();
183
+ devTrack2("scoped:batch:flush", { size: fns.length });
184
+ });
185
+ }
186
+ function makeBatchedSubscribe(subscribe) {
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
+ };
199
+ }
125
200
  function shallowEqual(a, b) {
126
201
  if (Object.is(a, b)) return true;
127
202
  if (typeof a !== "object" || a === null || typeof b !== "object" || b === null)
@@ -137,23 +212,41 @@ function shallowEqual(a, b) {
137
212
  }
138
213
  function createScopedStoreContext(reducer, initialState) {
139
214
  const Context = (0, import_react.createContext)(null);
140
- const store = createStore(reducer, initialState);
141
- const Provider = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Context.Provider, { value: store, children });
215
+ const Provider = ({
216
+ children,
217
+ initialState: override
218
+ }) => {
219
+ const storeRef = (0, import_react.useRef)(null);
220
+ if (!storeRef.current) {
221
+ storeRef.current = createStore(reducer, override ?? initialState);
222
+ devTrack2("scoped:store:create");
223
+ }
224
+ (0, import_react.useEffect)(() => {
225
+ devTrack2("scoped:provider:mount");
226
+ return () => devTrack2("scoped:provider:unmount");
227
+ }, []);
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Context.Provider, { value: storeRef.current, children });
229
+ };
142
230
  const useStore2 = () => {
143
231
  const ctx = (0, import_react.useContext)(Context);
144
232
  if (!ctx) throw new Error("Store not found in context");
145
- return (0, import_react.useSyncExternalStore)(ctx.subscribe, ctx.getState, ctx.getState);
233
+ devTrack2("scoped:useStore");
234
+ return (0, import_react.useSyncExternalStore)(
235
+ makeBatchedSubscribe(ctx.subscribe),
236
+ ctx.getState,
237
+ ctx.getState
238
+ );
146
239
  };
147
240
  const useDispatch2 = () => {
148
241
  const ctx = (0, import_react.useContext)(Context);
149
242
  if (!ctx) throw new Error("Dispatch not found in context");
150
- return (action) => ctx.dispatch(action);
243
+ return (0, import_react.useCallback)((action) => ctx.dispatch(action), [ctx]);
151
244
  };
152
245
  function useSelector(selector, isEqual = shallowEqual) {
153
246
  const ctx = (0, import_react.useContext)(Context);
154
247
  if (!ctx) throw new Error("Store not found in context");
155
248
  const state = (0, import_react.useSyncExternalStore)(
156
- ctx.subscribe,
249
+ makeBatchedSubscribe(ctx.subscribe),
157
250
  ctx.getState,
158
251
  ctx.getState
159
252
  );
@@ -161,13 +254,14 @@ function createScopedStoreContext(reducer, initialState) {
161
254
  const last = lastRef.current;
162
255
  const nextSelected = selector(state);
163
256
  if (last && last.state === state && isEqual(last.selected, nextSelected)) {
257
+ devTrack2("scoped:selector:cache-hit");
164
258
  return last.selected;
165
259
  }
260
+ devTrack2("scoped:selector:cache-miss");
166
261
  lastRef.current = { state, selected: nextSelected };
167
262
  return nextSelected;
168
263
  }
169
264
  return {
170
- store,
171
265
  Context,
172
266
  Provider,
173
267
  useStore: useStore2,
@@ -177,12 +271,22 @@ function createScopedStoreContext(reducer, initialState) {
177
271
  }
178
272
 
179
273
  // src/provider.tsx
180
- var import_react2 = require("react");
274
+ var import_react2 = __toESM(require("react"), 1);
181
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
+ }
182
285
  var StoreContext = (0, import_react2.createContext)(void 0);
183
286
  function useStoreInstance() {
184
287
  const store = (0, import_react2.useContext)(StoreContext);
185
288
  if (!store) {
289
+ devTrack3("store:provider:missing");
186
290
  throw new Error(
187
291
  "StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>."
188
292
  );
@@ -193,16 +297,45 @@ function StoreProvider({
193
297
  store,
194
298
  children
195
299
  }) {
300
+ (0, import_react2.useEffect)(() => {
301
+ devTrack3("store:provider:mount", { hasStore: !!store });
302
+ return () => devTrack3("store:provider:unmount");
303
+ }, [store]);
196
304
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoreContext.Provider, { value: store, children });
197
305
  }
198
306
  function useStore() {
199
307
  const store = useStoreInstance();
200
308
  const [state, setState] = (0, import_react2.useState)(() => store.getState());
309
+ const prevRef = import_react2.default.useRef(state);
201
310
  (0, import_react2.useEffect)(() => {
311
+ devTrack3("store:react:subscribe");
202
312
  const unsubscribe = store.subscribe(() => {
203
- setState(store.getState());
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
+ }
204
334
  });
205
- return unsubscribe;
335
+ return () => {
336
+ devTrack3("store:react:unsubscribe");
337
+ unsubscribe();
338
+ };
206
339
  }, [store]);
207
340
  return state;
208
341
  }
@@ -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 } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\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 const store = createStore(reducer, initialState);\n\n const Provider = ({ children }: { children: React.ReactNode }) => (\n <Context.Provider value={store}>{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(ctx.subscribe, ctx.getState, ctx.getState);\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 (action: A) => ctx.dispatch(action);\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 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 store,\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.d.cts CHANGED
@@ -31,10 +31,10 @@ interface Store<S extends IState, A extends IAction> {
31
31
  declare function createStore<S extends IState, A extends IAction>(reducer: Reducer<S, A>, initialState: S): Store<S, A>;
32
32
 
33
33
  declare function createScopedStoreContext<S extends IState, A extends IAction>(reducer: (state: S, action: A) => S, initialState: S): {
34
- store: Store<S, A>;
35
34
  Context: react.Context<Store<S, A> | null>;
36
- Provider: ({ children }: {
35
+ Provider: ({ children, initialState: override, }: {
37
36
  children: React.ReactNode;
37
+ initialState?: S;
38
38
  }) => react_jsx_runtime.JSX.Element;
39
39
  useStore: () => S;
40
40
  useDispatch: () => ((action: A) => void);
package/dist/index.d.ts CHANGED
@@ -31,10 +31,10 @@ interface Store<S extends IState, A extends IAction> {
31
31
  declare function createStore<S extends IState, A extends IAction>(reducer: Reducer<S, A>, initialState: S): Store<S, A>;
32
32
 
33
33
  declare function createScopedStoreContext<S extends IState, A extends IAction>(reducer: (state: S, action: A) => S, initialState: S): {
34
- store: Store<S, A>;
35
34
  Context: react.Context<Store<S, A> | null>;
36
- Provider: ({ children }: {
35
+ Provider: ({ children, initialState: override, }: {
37
36
  children: React.ReactNode;
37
+ initialState?: S;
38
38
  }) => react_jsx_runtime.JSX.Element;
39
39
  useStore: () => S;
40
40
  useDispatch: () => ((action: A) => void);
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 } 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
- for (const listener of [...listeners]) listener();
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,44 @@ 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
+ }
128
+ var _queue = /* @__PURE__ */ new Set();
129
+ var _queued = false;
130
+ var _schedule = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => Promise.resolve().then(fn);
131
+ function enqueue(fn) {
132
+ _queue.add(fn);
133
+ if (_queued) return;
134
+ _queued = true;
135
+ _schedule(() => {
136
+ _queued = false;
137
+ const fns = Array.from(_queue);
138
+ _queue.clear();
139
+ for (const f of fns) f();
140
+ devTrack2("scoped:batch:flush", { size: fns.length });
141
+ });
142
+ }
143
+ function makeBatchedSubscribe(subscribe) {
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
+ };
156
+ }
92
157
  function shallowEqual(a, b) {
93
158
  if (Object.is(a, b)) return true;
94
159
  if (typeof a !== "object" || a === null || typeof b !== "object" || b === null)
@@ -104,23 +169,41 @@ function shallowEqual(a, b) {
104
169
  }
105
170
  function createScopedStoreContext(reducer, initialState) {
106
171
  const Context = createContext(null);
107
- const store = createStore(reducer, initialState);
108
- const Provider = ({ children }) => /* @__PURE__ */ jsx(Context.Provider, { value: store, children });
172
+ const Provider = ({
173
+ children,
174
+ initialState: override
175
+ }) => {
176
+ const storeRef = useRef(null);
177
+ if (!storeRef.current) {
178
+ storeRef.current = createStore(reducer, override ?? initialState);
179
+ devTrack2("scoped:store:create");
180
+ }
181
+ useEffect(() => {
182
+ devTrack2("scoped:provider:mount");
183
+ return () => devTrack2("scoped:provider:unmount");
184
+ }, []);
185
+ return /* @__PURE__ */ jsx(Context.Provider, { value: storeRef.current, children });
186
+ };
109
187
  const useStore2 = () => {
110
188
  const ctx = useContext(Context);
111
189
  if (!ctx) throw new Error("Store not found in context");
112
- return useSyncExternalStore(ctx.subscribe, ctx.getState, ctx.getState);
190
+ devTrack2("scoped:useStore");
191
+ return useSyncExternalStore(
192
+ makeBatchedSubscribe(ctx.subscribe),
193
+ ctx.getState,
194
+ ctx.getState
195
+ );
113
196
  };
114
197
  const useDispatch2 = () => {
115
198
  const ctx = useContext(Context);
116
199
  if (!ctx) throw new Error("Dispatch not found in context");
117
- return (action) => ctx.dispatch(action);
200
+ return useCallback((action) => ctx.dispatch(action), [ctx]);
118
201
  };
119
202
  function useSelector(selector, isEqual = shallowEqual) {
120
203
  const ctx = useContext(Context);
121
204
  if (!ctx) throw new Error("Store not found in context");
122
205
  const state = useSyncExternalStore(
123
- ctx.subscribe,
206
+ makeBatchedSubscribe(ctx.subscribe),
124
207
  ctx.getState,
125
208
  ctx.getState
126
209
  );
@@ -128,13 +211,14 @@ function createScopedStoreContext(reducer, initialState) {
128
211
  const last = lastRef.current;
129
212
  const nextSelected = selector(state);
130
213
  if (last && last.state === state && isEqual(last.selected, nextSelected)) {
214
+ devTrack2("scoped:selector:cache-hit");
131
215
  return last.selected;
132
216
  }
217
+ devTrack2("scoped:selector:cache-miss");
133
218
  lastRef.current = { state, selected: nextSelected };
134
219
  return nextSelected;
135
220
  }
136
221
  return {
137
- store,
138
222
  Context,
139
223
  Provider,
140
224
  useStore: useStore2,
@@ -144,12 +228,22 @@ function createScopedStoreContext(reducer, initialState) {
144
228
  }
145
229
 
146
230
  // src/provider.tsx
147
- 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";
148
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
+ }
149
242
  var StoreContext = createContext2(void 0);
150
243
  function useStoreInstance() {
151
244
  const store = useContext2(StoreContext);
152
245
  if (!store) {
246
+ devTrack3("store:provider:missing");
153
247
  throw new Error(
154
248
  "StoreProvider is missing in the React tree. Wrap your app with <StoreProvider store={...}>."
155
249
  );
@@ -160,16 +254,45 @@ function StoreProvider({
160
254
  store,
161
255
  children
162
256
  }) {
257
+ useEffect2(() => {
258
+ devTrack3("store:provider:mount", { hasStore: !!store });
259
+ return () => devTrack3("store:provider:unmount");
260
+ }, [store]);
163
261
  return /* @__PURE__ */ jsx2(StoreContext.Provider, { value: store, children });
164
262
  }
165
263
  function useStore() {
166
264
  const store = useStoreInstance();
167
265
  const [state, setState] = useState(() => store.getState());
168
- useEffect(() => {
266
+ const prevRef = React.useRef(state);
267
+ useEffect2(() => {
268
+ devTrack3("store:react:subscribe");
169
269
  const unsubscribe = store.subscribe(() => {
170
- setState(store.getState());
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
+ }
171
291
  });
172
- return unsubscribe;
292
+ return () => {
293
+ devTrack3("store:react:unsubscribe");
294
+ unsubscribe();
295
+ };
173
296
  }, [store]);
174
297
  return state;
175
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 } from \"react\";\nimport type { IState, IAction, Store } from \"./store.js\";\nimport { createStore } from \"./store.js\";\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 const store = createStore(reducer, initialState);\n\n const Provider = ({ children }: { children: React.ReactNode }) => (\n <Context.Provider value={store}>{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(ctx.subscribe, ctx.getState, ctx.getState);\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 (action: A) => ctx.dispatch(action);\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 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 store,\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.0.13",
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
  }