dev-react-microstore 1.1.1 → 1.1.3

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.js CHANGED
@@ -1 +1 @@
1
- "use strict";var S=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames,j=Object.getOwnPropertySymbols;var g=Object.prototype.hasOwnProperty,v=Object.prototype.propertyIsEnumerable;var K=(r,e,t)=>e in r?S(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,k=(r,e)=>{for(var t in e||(e={}))g.call(e,t)&&K(r,t,e[t]);if(j)for(var t of j(e))v.call(e,t)&&K(r,t,e[t]);return r};var I=(r,e)=>{for(var t in e)S(r,t,{get:e[t],enumerable:!0})},C=(r,e,t,y)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of h(e))!g.call(r,a)&&a!==t&&S(r,a,{get:()=>e[a],enumerable:!(y=P(e,a))||y.enumerable});return r};var O=r=>C(S({},"__esModule",{value:!0}),r);var R={};I(R,{createStoreState:()=>V,useStoreSelector:()=>F});module.exports=O(R);var p=require("react");function V(r){let e=r,t=new Map;return{get:()=>e,set:s=>{var c;if(s==null||typeof s!="object"||Array.isArray(s)){console.error(`set() called with invalid value: ${s===null?"null":s===void 0?"undefined":typeof s}`);return}let o=!1,n=[];for(let d in s)if(Object.prototype.hasOwnProperty.call(s,d)){let i=d;i in e&&!Object.is(e[i],s[i])&&(o=!0,n.push(i))}if(o){e=k(k({},e),s);for(let d of n)(c=t.get(d))==null||c.forEach(i=>i())}},subscribe:(s,o)=>{for(let n of s)t.has(n)||t.set(n,new Set),t.get(n).add(o);return()=>{var n;for(let c of s)(n=t.get(c))==null||n.delete(o)}},select:s=>{let o={},n=e;for(let c of s)o[c]=n[c];return o}}}function F(r,e){let t=(0,p.useRef)(r.get()),y=(0,p.useRef)({}),a=e.flatMap(o=>typeof o=="string"?[{key:o}]:Object.entries(o).map(([n,c])=>({key:n,compare:c}))),b=()=>{let o=r.get(),n=t.current;if(!(!y.current||Object.keys(y.current).length===0||a.some(({key:u,compare:f})=>{let T=n[u],l=o[u];return f?f(T,l):!Object.is(T,l)})))return y.current;t.current=o;let i={};for(let{key:u,compare:f}of a){let T=y.current[u],l=o[u],x=f?f(T,l):!Object.is(T,l);i[u]=x?l:T}return y.current=i,i},m=(()=>{let o=r.get();return a.reduce((n,{key:c})=>(n[c]=o[c],n),{})})();return(0,p.useSyncExternalStore)(o=>r.subscribe(a.map(n=>n.key),o),b,()=>m)}0&&(module.exports={createStoreState,useStoreSelector});
1
+ "use strict";var m=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var g=(n,e)=>{for(var t in e)m(n,t,{get:e[t],enumerable:!0})},v=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let c of j(e))!P.call(n,c)&&c!==t&&m(n,c,{get:()=>e[c],enumerable:!(i=h(e,c))||i.enumerable});return n};var I=n=>v(m({},"__esModule",{value:!0}),n);var F={};g(F,{createStoreState:()=>R,useStoreSelector:()=>C});module.exports=I(F);var y=require("react");function R(n){let e=n,t=new Map;return{get:()=>e,set:o=>{var s;let r=[];for(let u in o){let d=u;Object.is(e[d],o[d])||r.push(d)}if(r.length!==0){for(let u in o)e[u]=o[u];for(let u of r)(s=t.get(u))==null||s.forEach(d=>d())}},subscribe:(o,r)=>{for(let s of o)t.has(s)||t.set(s,new Set),t.get(s).add(r);return()=>{var s;for(let u of o)(s=t.get(u))==null||s.delete(r)}},select:o=>{let r={},s=e;for(let u of o)r[u]=s[u];return r}}}function V(n,e){return n.length===e.length&&n.every((t,i)=>t===e[i])}function z(n,e,t){let i={};for(let{key:c,compare:p}of t){let f=e[c],o=n[c],r=f===void 0?!0:p?p(f,o):!Object.is(f,o);i[c]=r?o:f}return i}function C(n,e){let t=(0,y.useRef)({}),i=(0,y.useRef)(null),c=(0,y.useRef)(null),p=(0,y.useRef)(null),f=(0,y.useRef)(!0),o=(0,y.useRef)({});if(!i.current||!V(i.current,e)){let a=[],T=[];for(let S of e)if(typeof S=="string"){let l=S;a.push({key:l}),T.push(l)}else for(let[l,b]of Object.entries(S)){let k=l;a.push({key:k,compare:b}),T.push(k)}c.current=a,p.current=T,i.current=e}let r=c.current,s=p.current,u=()=>{let a=n.get(),T=f.current;if(!(T||r.some(({key:l,compare:b})=>{var K;let k=o.current[l],x=a[l];return k===void 0?!0:(K=b==null?void 0:b(k,x))!=null?K:!Object.is(k,x)})))return t.current;T&&(f.current=!1);for(let{key:l}of r)o.current[l]=a[l];return t.current=z(a,t.current,r),t.current},d=(()=>{let a=n.get();return s.reduce((T,S)=>(T[S]=a[S],T),{})})();return(0,y.useSyncExternalStore)(a=>n.subscribe(s,a),u,()=>d)}0&&(module.exports={createStoreState,useStoreSelector});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var g=Object.defineProperty;var b=Object.getOwnPropertySymbols;var x=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable;var m=(s,r,o)=>r in s?g(s,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):s[r]=o,p=(s,r)=>{for(var o in r||(r={}))x.call(r,o)&&m(s,o,r[o]);if(b)for(var o of b(r))P.call(r,o)&&m(s,o,r[o]);return s};import{useRef as j,useSyncExternalStore as h}from"react";function C(s){let r=s,o=new Map;return{get:()=>r,set:n=>{var c;if(n==null||typeof n!="object"||Array.isArray(n)){console.error(`set() called with invalid value: ${n===null?"null":n===void 0?"undefined":typeof n}`);return}let e=!1,t=[];for(let d in n)if(Object.prototype.hasOwnProperty.call(n,d)){let a=d;a in r&&!Object.is(r[a],n[a])&&(e=!0,t.push(a))}if(e){r=p(p({},r),n);for(let d of t)(c=o.get(d))==null||c.forEach(a=>a())}},subscribe:(n,e)=>{for(let t of n)o.has(t)||o.set(t,new Set),o.get(t).add(e);return()=>{var t;for(let c of n)(t=o.get(c))==null||t.delete(e)}},select:n=>{let e={},t=r;for(let c of n)e[c]=t[c];return e}}}function O(s,r){let o=j(s.get()),i=j({}),l=r.flatMap(e=>typeof e=="string"?[{key:e}]:Object.entries(e).map(([t,c])=>({key:t,compare:c}))),S=()=>{let e=s.get(),t=o.current;if(!(!i.current||Object.keys(i.current).length===0||l.some(({key:y,compare:f})=>{let u=t[y],T=e[y];return f?f(u,T):!Object.is(u,T)})))return i.current;o.current=e;let a={};for(let{key:y,compare:f}of l){let u=i.current[y],T=e[y],K=f?f(u,T):!Object.is(u,T);a[y]=K?T:u}return i.current=a,a},k=(()=>{let e=s.get();return l.reduce((t,{key:c})=>(t[c]=e[c],t),{})})();return h(e=>s.subscribe(l.map(t=>t.key),e),S,()=>k)}export{C as createStoreState,O as useStoreSelector};
1
+ import{useRef as k,useSyncExternalStore as K}from"react";function v(u){let r=u,n=new Map;return{get:()=>r,set:e=>{var o;let t=[];for(let s in e){let f=s;Object.is(r[f],e[f])||t.push(f)}if(t.length!==0){for(let s in e)r[s]=e[s];for(let s of t)(o=n.get(s))==null||o.forEach(f=>f())}},subscribe:(e,t)=>{for(let o of e)n.has(o)||n.set(o,new Set),n.get(o).add(t);return()=>{var o;for(let s of e)(o=n.get(s))==null||o.delete(t)}},select:e=>{let t={},o=r;for(let s of e)t[s]=o[s];return t}}}function h(u,r){return u.length===r.length&&u.every((n,a)=>n===r[a])}function j(u,r,n){let a={};for(let{key:y,compare:S}of n){let T=r[y],e=u[y],t=T===void 0?!0:S?S(T,e):!Object.is(T,e);a[y]=t?e:T}return a}function I(u,r){let n=k({}),a=k(null),y=k(null),S=k(null),T=k(!0),e=k({});if(!a.current||!h(a.current,r)){let c=[],l=[];for(let d of r)if(typeof d=="string"){let i=d;c.push({key:i}),l.push(i)}else for(let[i,b]of Object.entries(d)){let p=i;c.push({key:p,compare:b}),l.push(p)}y.current=c,S.current=l,a.current=r}let t=y.current,o=S.current,s=()=>{let c=u.get(),l=T.current;if(!(l||t.some(({key:i,compare:b})=>{var x;let p=e.current[i],m=c[i];return p===void 0?!0:(x=b==null?void 0:b(p,m))!=null?x:!Object.is(p,m)})))return n.current;l&&(T.current=!1);for(let{key:i}of t)e.current[i]=c[i];return n.current=j(c,n.current,t),n.current},f=(()=>{let c=u.get();return o.reduce((l,d)=>(l[d]=c[d],l),{})})();return K(c=>u.subscribe(o,c),s,()=>f)}export{v as createStoreState,I as useStoreSelector};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-react-microstore",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "A minimal global state manager for React with fine-grained subscriptions.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -5,34 +5,26 @@ type StoreListener = () => void;
5
5
 
6
6
  export function createStoreState<T extends object>(initialState: T) {
7
7
  let state = initialState;
8
-
9
- // Map from key to set of subscribers interested in that key
8
+
10
9
  const keyListeners = new Map<keyof T, Set<StoreListener>>();
11
10
 
12
11
  const get = () => state;
13
12
 
14
13
  const set = (next: Partial<T>) => {
15
- if (next === null || next === undefined || typeof next !== 'object' || Array.isArray(next)) {
16
- console.error(`set() called with invalid value: ${next === null ? 'null' : next === undefined ? 'undefined' : typeof next}`);
17
- return;
18
- }
19
-
20
- let changed = false;
21
14
  const updatedKeys: (keyof T)[] = [];
22
15
 
23
16
  for (const key in next) {
24
- if (Object.prototype.hasOwnProperty.call(next, key)) {
25
- const typedKey = key as keyof T;
26
- if (typedKey in state && !Object.is(state[typedKey], next[typedKey])) {
27
- changed = true;
28
- updatedKeys.push(typedKey);
29
- }
17
+ const typedKey = key as keyof T;
18
+ if (!Object.is(state[typedKey], next[typedKey])) {
19
+ updatedKeys.push(typedKey);
30
20
  }
31
21
  }
32
22
 
33
- if (!changed) return;
23
+ if (updatedKeys.length === 0) return;
34
24
 
35
- state = { ...state, ...next };
25
+ for (const key in next) {
26
+ state[key as keyof T] = next[key as keyof T]!;
27
+ }
36
28
 
37
29
  for (const key of updatedKeys) {
38
30
  keyListeners.get(key)?.forEach(listener => listener());
@@ -89,67 +81,101 @@ type NormalizedSelector<T extends object> = {
89
81
  compare?: CompareFn<T[keyof T]>;
90
82
  };
91
83
 
84
+ function shallowEqualSelector<T extends object>(
85
+ a: SelectorInput<T>,
86
+ b: SelectorInput<T>
87
+ ): boolean {
88
+ return a.length === b.length && a.every((item, i) => item === b[i]);
89
+ }
90
+
91
+ function pickKeys<T extends object>(
92
+ base: T,
93
+ prev: Partial<T>,
94
+ selectors: NormalizedSelector<T>[]
95
+ ): Partial<T> {
96
+ const out: Partial<T> = {};
97
+ for (const { key, compare } of selectors) {
98
+ const prevVal = prev[key];
99
+ const nextVal = base[key];
100
+ const changed = prevVal === undefined ? true : (compare ? compare(prevVal, nextVal) : !Object.is(prevVal, nextVal));
101
+ out[key] = changed ? nextVal : prevVal;
102
+ }
103
+ return out;
104
+ }
105
+
92
106
  export function useStoreSelector<T extends object, S extends SelectorInput<T>>(
93
107
  store: StoreType<T>,
94
108
  selector: S
95
109
  ): Picked<T, S> {
96
- const lastState = useRef(store.get());
97
110
  const lastSelected = useRef<Partial<T>>({});
98
-
99
- const normalized = selector.flatMap((item): NormalizedSelector<T>[] => {
100
- if (typeof item === 'string') {
101
- return [{ key: item as keyof T }];
111
+ const prevSelector = useRef<SelectorInput<T> | null>(null);
112
+ const normalizedRef = useRef<NormalizedSelector<T>[] | null>(null);
113
+ const keysRef = useRef<(keyof T)[] | null>(null);
114
+ const isFirstRunRef = useRef(true);
115
+ const lastValues = useRef<Partial<T>>({});
116
+
117
+ if (!prevSelector.current || !shallowEqualSelector(prevSelector.current, selector)) {
118
+ const normalized: NormalizedSelector<T>[] = [];
119
+ const keys: (keyof T)[] = [];
120
+
121
+ for (const item of selector) {
122
+ if (typeof item === 'string') {
123
+ const key = item as keyof T;
124
+ normalized.push({ key });
125
+ keys.push(key);
126
+ } else {
127
+ for (const [key, compare] of Object.entries(item)) {
128
+ const typedKey = key as keyof T;
129
+ normalized.push({ key: typedKey, compare: compare as CompareFn<T[keyof T]> });
130
+ keys.push(typedKey);
131
+ }
132
+ }
102
133
  }
103
- return Object.entries(item).map(([key, compare]) => ({
104
- key: key as keyof T,
105
- compare: compare as CompareFn<T[keyof T]>,
106
- }));
107
- });
134
+
135
+ normalizedRef.current = normalized;
136
+ keysRef.current = keys;
137
+ prevSelector.current = selector;
138
+ }
139
+
140
+ const normalized = normalizedRef.current!;
141
+ const keys = keysRef.current!;
108
142
 
109
143
  const getSnapshot = () => {
110
144
  const current = store.get();
111
- const prev = lastState.current;
112
-
113
- const isFirstRun = !lastSelected.current || Object.keys(lastSelected.current).length === 0;
145
+ const isFirstRun = isFirstRunRef.current;
114
146
 
115
147
  const changed = isFirstRun || normalized.some(({ key, compare }) => {
116
- const prevVal = prev[key];
148
+ const prevVal = lastValues.current[key];
117
149
  const nextVal = current[key];
118
- return compare ? compare(prevVal, nextVal) : !Object.is(prevVal, nextVal);
150
+ return prevVal === undefined ? true : (compare?.(prevVal, nextVal) ?? !Object.is(prevVal, nextVal));
119
151
  });
120
152
 
121
153
  if (!changed) {
122
154
  return lastSelected.current as Picked<T, S>;
123
155
  }
124
-
125
- lastState.current = current;
126
-
127
- const nextSelected: Partial<T> = {};
128
-
129
- for (const { key, compare } of normalized) {
130
- const prevVal = lastSelected.current[key];
131
- const nextVal = current[key];
132
- const hasChanged = compare
133
- ? compare(prevVal as T[keyof T], nextVal as T[keyof T])
134
- : !Object.is(prevVal, nextVal);
135
-
136
- nextSelected[key] = hasChanged ? nextVal : prevVal;
156
+
157
+ if (isFirstRun) {
158
+ isFirstRunRef.current = false;
137
159
  }
138
160
 
139
- lastSelected.current = nextSelected;
161
+ for (const { key } of normalized) {
162
+ lastValues.current[key] = current[key];
163
+ }
140
164
 
141
- return nextSelected as Picked<T, S>;
165
+ lastSelected.current = pickKeys(current, lastSelected.current, normalized);
166
+ return lastSelected.current as Picked<T, S>;
142
167
  };
143
168
 
144
169
  const staticSnapshot = (() => {
145
170
  const current = store.get();
146
- return normalized.reduce((acc, { key }) => {
171
+ return keys.reduce((acc, key) => {
147
172
  acc[key] = current[key];
148
173
  return acc;
149
174
  }, {} as Partial<T>) as Picked<T, S>;
150
175
  })();
151
-
176
+
152
177
  const subscribe = (onStoreChange: () => void) =>
153
- store.subscribe(normalized.map(sel => sel.key), onStoreChange);
178
+ store.subscribe(keys, onStoreChange);
179
+
154
180
  return useSyncExternalStore(subscribe, getSnapshot, () => staticSnapshot);
155
181
  }