dev-react-microstore 4.0.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ A minimal global state manager for React with fine-grained subscriptions.
8
8
  npm install react-microstore
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Basic Usage
12
12
 
13
13
  ```tsx
14
14
  import { createStoreState, useStoreSelector } from 'react-microstore';
@@ -29,6 +29,102 @@ function Counter() {
29
29
  }
30
30
  ```
31
31
 
32
+ ## Middleware Support
33
+
34
+ Add Express-style middleware to intercept and control state updates:
35
+
36
+ ```tsx
37
+ const store = createStoreState({ count: 0, user: null });
38
+
39
+ // Validation middleware - can block updates
40
+ store.addMiddleware(
41
+ (currentState, update, next) => {
42
+ if (update.count !== undefined && update.count < 0) {
43
+ // Don't call next() to block the update
44
+ console.log('Blocked negative count');
45
+ return;
46
+ }
47
+ next(); // Allow the update
48
+ },
49
+ ['count'] // Only run for count updates
50
+ );
51
+
52
+ // Transform middleware - can modify updates
53
+ store.addMiddleware(
54
+ (currentState, update, next) => {
55
+ if (update.user?.name) {
56
+ const modifiedUpdate = {
57
+ ...update,
58
+ user: {
59
+ ...update.user,
60
+ name: update.user.name.trim().toLowerCase()
61
+ }
62
+ };
63
+ next(modifiedUpdate); // Pass modified update
64
+ } else {
65
+ next(); // Pass original update
66
+ }
67
+ }
68
+ );
69
+
70
+ // Logging middleware
71
+ store.addMiddleware(
72
+ (currentState, update, next) => {
73
+ console.log('Processing update:', update);
74
+ next(); // Continue
75
+ }
76
+ );
77
+ ```
78
+
79
+ ## Persistence
80
+
81
+ The store supports automatic state persistence using middleware with per-key storage:
82
+
83
+ ```typescript
84
+ import { createStoreState, createPersistenceMiddleware, loadPersistedState } from '@ohad/react-microstore'
85
+
86
+ // Load persisted state during initialization
87
+ const persistedState = loadPersistedState<AppState>(
88
+ localStorage,
89
+ 'my-app-state',
90
+ ['theme', 'userName', 'isLoggedIn']
91
+ );
92
+
93
+ // Create store with merged initial + persisted state
94
+ const store = createStoreState<AppState>({
95
+ theme: 'light',
96
+ userName: '',
97
+ isLoggedIn: false,
98
+ tempData: { cache: [] },
99
+ ...persistedState // Apply persisted values
100
+ });
101
+
102
+ // Add persistence middleware - saves each key individually
103
+ store.addMiddleware(
104
+ createPersistenceMiddleware(localStorage, 'my-app-state', ['theme', 'userName', 'isLoggedIn'])
105
+ );
106
+ ```
107
+
108
+ **Key benefits:**
109
+
110
+ ✅ **Per-key storage** - Each key stored separately (e.g., `my-app-state:theme`, `my-app-state:userName`)
111
+ ✅ **Efficient writes** - Only writes to storage when specified keys actually change
112
+ ✅ **No state blobs** - Avoids serializing/storing entire state objects
113
+ ✅ **Composable** - Uses the same middleware system as validation/logging
114
+ ✅ **Flexible** - Easy to swap storage backends or add custom logic
115
+
116
+ ## Debouncing
117
+
118
+ Control when updates are applied:
119
+
120
+ ```tsx
121
+ // Debounce updates for 300ms
122
+ store.set({ searchQuery: 'new value' }, 300);
123
+
124
+ // Or use boolean for default debounce (0ms)
125
+ store.set({ count: count + 1 }, true);
126
+ ```
127
+
32
128
  ## Custom Comparison Function
33
129
 
34
130
  ```tsx
@@ -143,7 +239,10 @@ function UserProfile() {
143
239
  - Extremely lightweight (less than 2KB minified)
144
240
  - Fine-grained subscriptions to minimize re-renders
145
241
  - Custom comparison functions for complex state updates
146
- - Fully Type-safe
242
+ - Simple middleware support with `addMiddleware()`
243
+ - Automatic persistence to localStorage/sessionStorage
244
+ - Debouncing support to control update frequency
245
+ - Fully Type-safe with TypeScript
147
246
  - No dependencies other than React
148
247
  - Update store from anywhere in your application
149
248
 
package/dist/index.d.mts CHANGED
@@ -1,9 +1,24 @@
1
1
  type StoreListener = () => void;
2
+ type MiddlewareFunction<T extends object> = (currentState: T, update: Partial<T>, next: (modifiedUpdate?: Partial<T>) => void) => void;
3
+ /**
4
+ * Creates a new reactive store with fine-grained subscriptions and middleware support.
5
+ *
6
+ * @param initialState - The initial state object for the store
7
+ * @returns Store object with methods: get, set, subscribe, select, addMiddleware
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const store = createStoreState({ count: 0, name: 'John' });
12
+ * store.set({ count: 1 }); // Update state
13
+ * const { count } = useStoreSelector(store, ['count']); // Subscribe in React
14
+ * ```
15
+ */
2
16
  declare function createStoreState<T extends object>(initialState: T): {
3
17
  get: () => T;
4
- set: (next: Partial<T>, debounceDelay?: number | boolean) => void;
18
+ set: (update: Partial<T>, debounceDelay?: number | boolean) => void;
5
19
  subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
6
20
  select: <K extends keyof T>(keys: K[]) => Pick<T, K>;
21
+ addMiddleware: (callbackOrTuple: MiddlewareFunction<T> | [MiddlewareFunction<T>, (keyof T)[]], affectedKeys?: (keyof T)[] | null) => () => void;
7
22
  };
8
23
  type StoreType<T extends object> = ReturnType<typeof createStoreState<T>>;
9
24
  type PrimitiveKey<T extends object> = keyof T;
@@ -17,6 +32,74 @@ type ExtractSelectorKeys<T extends object, S extends SelectorInput<T>> = {
17
32
  [K in S[number] extends infer Item ? Item extends keyof T ? Item : keyof Item : never]: T[K];
18
33
  };
19
34
  type Picked<T extends object, S extends SelectorInput<T>> = ExtractSelectorKeys<T, S>;
35
+ /**
36
+ * React hook that subscribes to specific keys in a store with fine-grained re-renders.
37
+ * Only re-renders when the selected keys actually change (using Object.is comparison).
38
+ *
39
+ * @param store - The store created with createStoreState
40
+ * @param selector - Array of keys to subscribe to, or objects with custom compare functions
41
+ * @returns Selected state values from the store
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // Subscribe to specific keys
46
+ * const { count, name } = useStoreSelector(store, ['count', 'name']);
47
+ *
48
+ * // Custom comparison for complex objects
49
+ * const { tasks } = useStoreSelector(store, [
50
+ * { tasks: (prev, next) => prev.length === next.length }
51
+ * ]);
52
+ * ```
53
+ */
20
54
  declare function useStoreSelector<T extends object, S extends SelectorInput<T>>(store: StoreType<T>, selector: S): Picked<T, S>;
55
+ /**
56
+ * Interface for storage objects compatible with persistence middleware.
57
+ * Includes localStorage, sessionStorage, AsyncStorage, or any custom storage.
58
+ */
59
+ interface StorageSupportingInterface {
60
+ getItem(key: string): string | null;
61
+ setItem(key: string, value: string): void;
62
+ }
63
+ /**
64
+ * Creates a persistence middleware that saves individual keys to storage.
65
+ * Only writes when the specified keys actually change, using per-key storage.
66
+ * Storage format: `${persistKey}:${keyName}` for each persisted key.
67
+ *
68
+ * @param storage - Storage interface (localStorage, sessionStorage, AsyncStorage, etc.)
69
+ * @param persistKey - Base key prefix for storage (e.g., 'myapp' creates 'myapp:theme')
70
+ * @param keys - Array of state keys to persist
71
+ * @returns Tuple of [middleware function, affected keys] for use with addMiddleware
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * // Add persistence for theme and user settings
76
+ * store.addMiddleware(
77
+ * createPersistenceMiddleware(localStorage, 'myapp', ['theme', 'isLoggedIn'])
78
+ * );
79
+ * ```
80
+ */
81
+ declare function createPersistenceMiddleware<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): [MiddlewareFunction<T>, (keyof T)[]];
82
+ /**
83
+ * Loads persisted state from individual key storage during store initialization.
84
+ * Reads keys saved by createPersistenceMiddleware and returns them as partial state.
85
+ *
86
+ * @param storage - Storage interface to read from (same as used in middleware)
87
+ * @param persistKey - Base key prefix used for storage (same as used in middleware)
88
+ * @param keys - Array of keys to restore (should match middleware keys)
89
+ * @returns Partial state object with persisted values, or empty object if loading fails
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * // Load persisted state before creating store
94
+ * const persistedState = loadPersistedState(localStorage, 'myapp', ['theme', 'isLoggedIn']);
95
+ *
96
+ * const store = createStoreState({
97
+ * theme: 'light',
98
+ * isLoggedIn: false,
99
+ * ...persistedState // Apply persisted values
100
+ * });
101
+ * ```
102
+ */
103
+ declare function loadPersistedState<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): Partial<T>;
21
104
 
22
- export { createStoreState, useStoreSelector };
105
+ export { type StorageSupportingInterface, createPersistenceMiddleware, createStoreState, loadPersistedState, useStoreSelector };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,24 @@
1
1
  type StoreListener = () => void;
2
+ type MiddlewareFunction<T extends object> = (currentState: T, update: Partial<T>, next: (modifiedUpdate?: Partial<T>) => void) => void;
3
+ /**
4
+ * Creates a new reactive store with fine-grained subscriptions and middleware support.
5
+ *
6
+ * @param initialState - The initial state object for the store
7
+ * @returns Store object with methods: get, set, subscribe, select, addMiddleware
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const store = createStoreState({ count: 0, name: 'John' });
12
+ * store.set({ count: 1 }); // Update state
13
+ * const { count } = useStoreSelector(store, ['count']); // Subscribe in React
14
+ * ```
15
+ */
2
16
  declare function createStoreState<T extends object>(initialState: T): {
3
17
  get: () => T;
4
- set: (next: Partial<T>, debounceDelay?: number | boolean) => void;
18
+ set: (update: Partial<T>, debounceDelay?: number | boolean) => void;
5
19
  subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
6
20
  select: <K extends keyof T>(keys: K[]) => Pick<T, K>;
21
+ addMiddleware: (callbackOrTuple: MiddlewareFunction<T> | [MiddlewareFunction<T>, (keyof T)[]], affectedKeys?: (keyof T)[] | null) => () => void;
7
22
  };
8
23
  type StoreType<T extends object> = ReturnType<typeof createStoreState<T>>;
9
24
  type PrimitiveKey<T extends object> = keyof T;
@@ -17,6 +32,74 @@ type ExtractSelectorKeys<T extends object, S extends SelectorInput<T>> = {
17
32
  [K in S[number] extends infer Item ? Item extends keyof T ? Item : keyof Item : never]: T[K];
18
33
  };
19
34
  type Picked<T extends object, S extends SelectorInput<T>> = ExtractSelectorKeys<T, S>;
35
+ /**
36
+ * React hook that subscribes to specific keys in a store with fine-grained re-renders.
37
+ * Only re-renders when the selected keys actually change (using Object.is comparison).
38
+ *
39
+ * @param store - The store created with createStoreState
40
+ * @param selector - Array of keys to subscribe to, or objects with custom compare functions
41
+ * @returns Selected state values from the store
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // Subscribe to specific keys
46
+ * const { count, name } = useStoreSelector(store, ['count', 'name']);
47
+ *
48
+ * // Custom comparison for complex objects
49
+ * const { tasks } = useStoreSelector(store, [
50
+ * { tasks: (prev, next) => prev.length === next.length }
51
+ * ]);
52
+ * ```
53
+ */
20
54
  declare function useStoreSelector<T extends object, S extends SelectorInput<T>>(store: StoreType<T>, selector: S): Picked<T, S>;
55
+ /**
56
+ * Interface for storage objects compatible with persistence middleware.
57
+ * Includes localStorage, sessionStorage, AsyncStorage, or any custom storage.
58
+ */
59
+ interface StorageSupportingInterface {
60
+ getItem(key: string): string | null;
61
+ setItem(key: string, value: string): void;
62
+ }
63
+ /**
64
+ * Creates a persistence middleware that saves individual keys to storage.
65
+ * Only writes when the specified keys actually change, using per-key storage.
66
+ * Storage format: `${persistKey}:${keyName}` for each persisted key.
67
+ *
68
+ * @param storage - Storage interface (localStorage, sessionStorage, AsyncStorage, etc.)
69
+ * @param persistKey - Base key prefix for storage (e.g., 'myapp' creates 'myapp:theme')
70
+ * @param keys - Array of state keys to persist
71
+ * @returns Tuple of [middleware function, affected keys] for use with addMiddleware
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * // Add persistence for theme and user settings
76
+ * store.addMiddleware(
77
+ * createPersistenceMiddleware(localStorage, 'myapp', ['theme', 'isLoggedIn'])
78
+ * );
79
+ * ```
80
+ */
81
+ declare function createPersistenceMiddleware<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): [MiddlewareFunction<T>, (keyof T)[]];
82
+ /**
83
+ * Loads persisted state from individual key storage during store initialization.
84
+ * Reads keys saved by createPersistenceMiddleware and returns them as partial state.
85
+ *
86
+ * @param storage - Storage interface to read from (same as used in middleware)
87
+ * @param persistKey - Base key prefix used for storage (same as used in middleware)
88
+ * @param keys - Array of keys to restore (should match middleware keys)
89
+ * @returns Partial state object with persisted values, or empty object if loading fails
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * // Load persisted state before creating store
94
+ * const persistedState = loadPersistedState(localStorage, 'myapp', ['theme', 'isLoggedIn']);
95
+ *
96
+ * const store = createStoreState({
97
+ * theme: 'light',
98
+ * isLoggedIn: false,
99
+ * ...persistedState // Apply persisted values
100
+ * });
101
+ * ```
102
+ */
103
+ declare function loadPersistedState<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): Partial<T>;
21
104
 
22
- export { createStoreState, useStoreSelector };
105
+ export { type StorageSupportingInterface, createPersistenceMiddleware, createStoreState, loadPersistedState, useStoreSelector };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var K=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var I=(o,e)=>{for(var n in e)K(o,n,{get:e[n],enumerable:!0})},R=(o,e,n,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of v(e))!j.call(o,p)&&p!==n&&K(o,p,{get:()=>e[p],enumerable:!(c=P(e,p))||c.enumerable});return o};var C=o=>R(K({},"__esModule",{value:!0}),o);var w={};I(w,{createStoreState:()=>F,useStoreSelector:()=>E});module.exports=C(w);var f=require("react");function V(o,e){let n;return(...c)=>{clearTimeout(n),n=setTimeout(()=>o(...c),e)}}function F(o){let e=o,n=new Map,c=new Map;return{get:()=>e,set:(i,a=!1)=>{var S;if(!i)return;let r=[];for(let k in i){let t=k,l=e[t],T=i[t];l!==T&&(Object.is(l,T)||(e[t]=T,r.push(t)))}if(r.length!==0)for(let k of r)a!==!1?(c.has(k)||c.set(k,V(()=>{var t;(t=n.get(k))==null||t.forEach(l=>l())},typeof a=="number"?a:0)),c.get(k)()):(S=n.get(k))==null||S.forEach(t=>t())},subscribe:(i,a)=>{for(let r of i)n.has(r)||n.set(r,new Set),n.get(r).add(a);return()=>{var r;for(let S of i)(r=n.get(S))==null||r.delete(a)}},select:i=>{let a={},r=e;for(let S of i)a[S]=r[S];return a}}}function z(o,e){return o.length===e.length&&o.every((n,c)=>n===e[c])}function E(o,e){let n=(0,f.useRef)({}),c=(0,f.useRef)(null),p=(0,f.useRef)(null),x=(0,f.useRef)(null),h=(0,f.useRef)(!0),b=(0,f.useRef)({}),i=(0,f.useRef)(null);if(!c.current||!z(c.current,e)){let t=[],l=[];for(let T of e)if(typeof T=="string"){let d=T;t.push({key:d}),l.push(d)}else{let d=T;for(let s in d){let y=d[s],u=s;t.push({key:u,compare:y}),l.push(u)}}p.current=t,x.current=l,c.current=e,i.current=null}let a=p.current,r=x.current,S=()=>{let t=o.get();if(h.current){h.current=!1;let s={};for(let{key:y}of a){let u=t[y];b.current[y]=u,s[y]=u}return n.current=s,s}if(!(()=>{var s;for(let{key:y,compare:u}of a){let m=b.current[y],g=t[y];if(m===void 0||((s=u==null?void 0:u(m,g))!=null?s:!Object.is(m,g)))return!0}return!1})())return n.current;let d={};for(let{key:s,compare:y}of a){let u=b.current[s],m=t[s];u===void 0||(y?!y(u,m):!Object.is(u,m))?(b.current[s]=m,d[s]=m):d[s]=u}return n.current=d,d},k=(0,f.useMemo)(()=>{let t=o.get(),l={};for(let T of r)l[T]=t[T];return l},[r]);return i.current||(i.current=t=>o.subscribe(r,t)),(0,f.useSyncExternalStore)(i.current,S,()=>k)}0&&(module.exports={createStoreState,useStoreSelector});
1
+ "use strict";var w=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var K=(n,e)=>{for(var t in e)w(n,t,{get:e[t],enumerable:!0})},j=(n,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of I(e))!F.call(n,i)&&i!==t&&w(n,i,{get:()=>e[i],enumerable:!(a=v(e,i))||a.enumerable});return n};var M=n=>j(w({},"__esModule",{value:!0}),n);var $={};K($,{createPersistenceMiddleware:()=>E,createStoreState:()=>R,loadPersistedState:()=>N,useStoreSelector:()=>z});module.exports=M($);var S=require("react");function C(n,e){let t;return(...a)=>{clearTimeout(t),t=setTimeout(()=>n(...a),e)}}function R(n){let e=n,t=new Map,a=new Map,i=[],k=()=>e,g=(l,f=!1)=>{if(!l)return;let s=l,T=0,r=!1,o=y=>{if(y!==void 0&&(s=y),T>=i.length){r||x(s,f);return}let u=i[T++];if(!u.keys||u.keys.some(c=>c in s)){let c=!1,p=d=>{c||(c=!0,o(d))};try{u.callback(e,s,p)}catch(d){r=!0,console.error("Middleware error:",d);return}if(!c){r=!0;return}}else o()};o()},x=(l,f)=>{var T;let s=[];for(let r in l){let o=r,y=e[o],u=l[o];y!==u&&(Object.is(y,u)||(e[o]=u,s.push(o)))}if(s.length!==0)for(let r of s)f!==!1?(a.has(r)||a.set(r,C(()=>{var o;(o=t.get(r))==null||o.forEach(y=>y())},typeof f=="number"?f:0)),a.get(r)()):(T=t.get(r))==null||T.forEach(o=>o())};return{get:k,set:g,subscribe:(l,f)=>{for(let s of l)t.has(s)||t.set(s,new Set),t.get(s).add(f);return()=>{var s;for(let T of l)(s=t.get(T))==null||s.delete(f)}},select:l=>{let f={},s=e;for(let T of l)f[T]=s[T];return f},addMiddleware:(l,f=null)=>{let s,T;Array.isArray(l)?[s,T]=l:(s=l,T=f);let r={callback:s,keys:T};return i.push(r),()=>{let o=i.indexOf(r);o>-1&&i.splice(o,1)}}}}function V(n,e){return n.length===e.length&&n.every((t,a)=>t===e[a])}function z(n,e){let t=(0,S.useRef)({}),a=(0,S.useRef)(null),i=(0,S.useRef)(null),k=(0,S.useRef)(null),g=(0,S.useRef)(!0),x=(0,S.useRef)({}),m=(0,S.useRef)(null),b=(0,S.useRef)(n),P=b.current!==n;if(P&&(b.current=n,t.current={},a.current=null,i.current=null,k.current=null,g.current=!0,x.current={},m.current=null),!a.current||!V(a.current,e)){let r=[],o=[];for(let y of e)if(typeof y=="string"){let u=y;r.push({key:u}),o.push(u)}else{let u=y;for(let c in u){let p=u[c],d=c;r.push({key:d,compare:p}),o.push(d)}}i.current=r,k.current=o,a.current=e,m.current=null}let l=i.current,f=k.current,s=()=>{let r=n.get();if(g.current){g.current=!1;let c={};for(let{key:p}of l){let d=r[p];x.current[p]=d,c[p]=d}return t.current=c,c}if(!(()=>{for(let{key:c,compare:p}of l){let d=x.current[c],h=r[c];if(d===void 0||(p?!p(d,h):!Object.is(d,h)))return!0}return!1})())return t.current;let u={};for(let{key:c,compare:p}of l){let d=x.current[c],h=r[c];d===void 0||(p?!p(d,h):!Object.is(d,h))?(x.current[c]=h,u[c]=h):u[c]=d}return t.current=u,u},T=(0,S.useMemo)(()=>{let r=n.get(),o={};for(let y of f)o[y]=r[y];return o},[f]);return(!m.current||P)&&(m.current=r=>n.subscribe(f,r)),(0,S.useSyncExternalStore)(m.current,s,()=>T)}function E(n,e,t){return[(i,k,g)=>{let x=t.filter(m=>m in k);if(x.length===0)return g();for(let m of x)try{let b=k[m],P=`${e}:${String(m)}`;n.setItem(P,JSON.stringify(b))}catch(b){console.warn(`Failed to persist key ${String(m)}:`,b)}g()},t]}function N(n,e,t){let a={};for(let i of t)try{let k=`${e}:${String(i)}`,g=n.getItem(k);g!==null&&(a[i]=JSON.parse(g))}catch(k){console.warn(`Failed to load persisted key ${String(i)}:`,k)}return a}0&&(module.exports={createPersistenceMiddleware,createStoreState,loadPersistedState,useStoreSelector});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{useMemo as K,useRef as k,useSyncExternalStore as P}from"react";function v(T,s){let n;return(...a)=>{clearTimeout(n),n=setTimeout(()=>T(...a),s)}}function C(T){let s=T,n=new Map,a=new Map;return{get:()=>s,set:(c,u=!1)=>{var d;if(!c)return;let t=[];for(let p in c){let e=p,i=s[e],l=c[e];i!==l&&(Object.is(i,l)||(s[e]=l,t.push(e)))}if(t.length!==0)for(let p of t)u!==!1?(a.has(p)||a.set(p,v(()=>{var e;(e=n.get(p))==null||e.forEach(i=>i())},typeof u=="number"?u:0)),a.get(p)()):(d=n.get(p))==null||d.forEach(e=>e())},subscribe:(c,u)=>{for(let t of c)n.has(t)||n.set(t,new Set),n.get(t).add(u);return()=>{var t;for(let d of c)(t=n.get(d))==null||t.delete(u)}},select:c=>{let u={},t=s;for(let d of c)u[d]=t[d];return u}}}function j(T,s){return T.length===s.length&&T.every((n,a)=>n===s[a])}function V(T,s){let n=k({}),a=k(null),b=k(null),x=k(null),h=k(!0),m=k({}),c=k(null);if(!a.current||!j(a.current,s)){let e=[],i=[];for(let l of s)if(typeof l=="string"){let y=l;e.push({key:y}),i.push(y)}else{let y=l;for(let o in y){let f=y[o],r=o;e.push({key:r,compare:f}),i.push(r)}}b.current=e,x.current=i,a.current=s,c.current=null}let u=b.current,t=x.current,d=()=>{let e=T.get();if(h.current){h.current=!1;let o={};for(let{key:f}of u){let r=e[f];m.current[f]=r,o[f]=r}return n.current=o,o}if(!(()=>{var o;for(let{key:f,compare:r}of u){let S=m.current[f],g=e[f];if(S===void 0||((o=r==null?void 0:r(S,g))!=null?o:!Object.is(S,g)))return!0}return!1})())return n.current;let y={};for(let{key:o,compare:f}of u){let r=m.current[o],S=e[o];r===void 0||(f?!f(r,S):!Object.is(r,S))?(m.current[o]=S,y[o]=S):y[o]=r}return n.current=y,y},p=K(()=>{let e=T.get(),i={};for(let l of t)i[l]=e[l];return i},[t]);return c.current||(c.current=e=>T.subscribe(t,e)),P(c.current,d,()=>p)}export{C as createStoreState,V as useStoreSelector};
1
+ import{useMemo as w,useRef as x,useSyncExternalStore as v}from"react";function I(l,s){let n;return(...d)=>{clearTimeout(n),n=setTimeout(()=>l(...d),s)}}function C(l){let s=l,n=new Map,d=new Map,y=[],S=()=>s,m=(c,u=!1)=>{if(!c)return;let r=c,f=0,e=!1,t=T=>{if(T!==void 0&&(r=T),f>=y.length){e||g(r,u);return}let i=y[f++];if(!i.keys||i.keys.some(o=>o in r)){let o=!1,p=a=>{o||(o=!0,t(a))};try{i.callback(s,r,p)}catch(a){e=!0,console.error("Middleware error:",a);return}if(!o){e=!0;return}}else t()};t()},g=(c,u)=>{var f;let r=[];for(let e in c){let t=e,T=s[t],i=c[t];T!==i&&(Object.is(T,i)||(s[t]=i,r.push(t)))}if(r.length!==0)for(let e of r)u!==!1?(d.has(e)||d.set(e,I(()=>{var t;(t=n.get(e))==null||t.forEach(T=>T())},typeof u=="number"?u:0)),d.get(e)()):(f=n.get(e))==null||f.forEach(t=>t())};return{get:S,set:m,subscribe:(c,u)=>{for(let r of c)n.has(r)||n.set(r,new Set),n.get(r).add(u);return()=>{var r;for(let f of c)(r=n.get(f))==null||r.delete(u)}},select:c=>{let u={},r=s;for(let f of c)u[f]=r[f];return u},addMiddleware:(c,u=null)=>{let r,f;Array.isArray(c)?[r,f]=c:(r=c,f=u);let e={callback:r,keys:f};return y.push(e),()=>{let t=y.indexOf(e);t>-1&&y.splice(t,1)}}}}function F(l,s){return l.length===s.length&&l.every((n,d)=>n===s[d])}function R(l,s){let n=x({}),d=x(null),y=x(null),S=x(null),m=x(!0),g=x({}),k=x(null),b=x(l),P=b.current!==l;if(P&&(b.current=l,n.current={},d.current=null,y.current=null,S.current=null,m.current=!0,g.current={},k.current=null),!d.current||!F(d.current,s)){let e=[],t=[];for(let T of s)if(typeof T=="string"){let i=T;e.push({key:i}),t.push(i)}else{let i=T;for(let o in i){let p=i[o],a=o;e.push({key:a,compare:p}),t.push(a)}}y.current=e,S.current=t,d.current=s,k.current=null}let c=y.current,u=S.current,r=()=>{let e=l.get();if(m.current){m.current=!1;let o={};for(let{key:p}of c){let a=e[p];g.current[p]=a,o[p]=a}return n.current=o,o}if(!(()=>{for(let{key:o,compare:p}of c){let a=g.current[o],h=e[o];if(a===void 0||(p?!p(a,h):!Object.is(a,h)))return!0}return!1})())return n.current;let i={};for(let{key:o,compare:p}of c){let a=g.current[o],h=e[o];a===void 0||(p?!p(a,h):!Object.is(a,h))?(g.current[o]=h,i[o]=h):i[o]=a}return n.current=i,i},f=w(()=>{let e=l.get(),t={};for(let T of u)t[T]=e[T];return t},[u]);return(!k.current||P)&&(k.current=e=>l.subscribe(u,e)),v(k.current,r,()=>f)}function V(l,s,n){return[(y,S,m)=>{let g=n.filter(k=>k in S);if(g.length===0)return m();for(let k of g)try{let b=S[k],P=`${s}:${String(k)}`;l.setItem(P,JSON.stringify(b))}catch(b){console.warn(`Failed to persist key ${String(k)}:`,b)}m()},n]}function z(l,s,n){let d={};for(let y of n)try{let S=`${s}:${String(y)}`,m=l.getItem(S);m!==null&&(d[y]=JSON.parse(m))}catch(S){console.warn(`Failed to load persisted key ${String(y)}:`,S)}return d}export{V as createPersistenceMiddleware,C as createStoreState,z as loadPersistedState,R as useStoreSelector};
@@ -0,0 +1,54 @@
1
+ # React + TypeScript + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## Expanding the ESLint configuration
11
+
12
+ If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13
+
14
+ ```js
15
+ export default tseslint.config({
16
+ extends: [
17
+ // Remove ...tseslint.configs.recommended and replace with this
18
+ ...tseslint.configs.recommendedTypeChecked,
19
+ // Alternatively, use this for stricter rules
20
+ ...tseslint.configs.strictTypeChecked,
21
+ // Optionally, add this for stylistic rules
22
+ ...tseslint.configs.stylisticTypeChecked,
23
+ ],
24
+ languageOptions: {
25
+ // other options...
26
+ parserOptions: {
27
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
28
+ tsconfigRootDir: import.meta.dirname,
29
+ },
30
+ },
31
+ })
32
+ ```
33
+
34
+ You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
35
+
36
+ ```js
37
+ // eslint.config.js
38
+ import reactX from 'eslint-plugin-react-x'
39
+ import reactDom from 'eslint-plugin-react-dom'
40
+
41
+ export default tseslint.config({
42
+ plugins: {
43
+ // Add the react-x and react-dom plugins
44
+ 'react-x': reactX,
45
+ 'react-dom': reactDom,
46
+ },
47
+ rules: {
48
+ // other rules...
49
+ // Enable its recommended typescript rules
50
+ ...reactX.configs['recommended-typescript'].rules,
51
+ ...reactDom.configs.recommended.rules,
52
+ },
53
+ })
54
+ ```
@@ -0,0 +1,28 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ },
27
+ },
28
+ )
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Vite + React + TS</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>