Gathering detailed insights and metrics for resy
Gathering detailed insights and metrics for resy
Gathering detailed insights and metrics for resy
Gathering detailed insights and metrics for resy
npm install resy
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (99.7%)
CSS (0.11%)
JavaScript (0.1%)
HTML (0.1%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
14 Stars
1,024 Commits
2 Forks
1 Watchers
5 Branches
1 Contributors
Updated on Jun 12, 2025
Latest Version
11.1.0
Package Id
resy@11.1.0
Unpacked Size
207.87 kB
Size
53.41 kB
File Count
15
NPM Version
10.9.2
Node Version
22.14.0
Published on
Mar 02, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
1
34
1npm i resy 2 3# yarn add resy 4# pnpm add resy
1import React from "react"; 2import { defineStore } from "resy"; 3 4const useStore = defineStore({ 5 count: 0, 6 increase() { 7 this.count++; 8 }, 9}); 10 11const App = () => { 12 const { count, increase } = useStore(); 13 14 return ( 15 <> 16 <p>count:{count}</p> 17 <button onClick={increase}>increase</button> 18 </> 19 ); 20};
1import React from "react"; 2import { createStore, useStore, ComponentWithStore } from "resy"; 3 4const store = createStore({ count: 0 }); 5 6// for hook component 7function App() { 8 const { count } = useStore(store); // or store.useStore(); 9 return ( 10 <> 11 {count} 12 <button onClick={() => store.count++}>increase</button> 13 </> 14 ); 15} 16 17// for class component 18class AppClass extends ComponentWithStore { 19 20 store = this.connectStore(store); 21 22 render() { 23 const { count } = this.store; 24 return ( 25 <> 26 {count} 27 <button onClick={() => { store.count++; }}>increase</button> 28 </> 29 ); 30 } 31}
resy requires the version of React v >= 16.8
API | Description |
---|---|
defineStore | The macro definition method for createStore |
createStore | Create a store container for state |
useStore | Use state from the store container generated by createStore |
setState | Update data |
syncUpdate | Synchronously update data |
1const useUserStore = defineStore({ 2 userId: 666, 3 username: "wenmu", 4 updateUserInfo() { 5 this.username = "Shanbao Liu"; 6 }, 7}); 8const useThemeStore = defineStore({ 9 themeStyle: "dark", 10 changeTheme() { 11 this.themeStyle = "light"; 12 }, 13}); 14 15function App() { 16 const { username, updateUserInfo } = useUserStore(); 17 const { themeStyle, changeTheme } = useThemeStore(); 18 19 return ( 20 <> 21 <p onClick={updateUserInfo}>name:{username}</p> 22 <p onClick={changeTheme}>theme:{themeStyle}</p> 23 </> 24 ); 25}
1const useStore = defineStore({ 2 count: 0, 3 increase() { 4 this.count++; 5 }, 6}); 7 8function App() { 9 const { 10 count, increase, store, useSubscription, 11 } = useStore(); 12 13 useSubscription(({ effectState }) => { 14 console.log(effectState); 15 }, ["count"]); 16 17 return ( 18 <> 19 <p onClick={increase}>count:{count}</p> 20 <button onClick={() => store.count++}> 21 updateCount 22 </button> 23 </> 24 ); 25}
1const demoStore1 = createStore({ 2 count: 0, 3 text: "hello", 4});
1type DemoStateType = { count: number; text?: number | string }; 2// In this way, the type of text can be 3// more accurately identified as number or string or undefined 4const demoStore2 = createStore<DemoStateType>({ 5 count: 0, 6});
1// This is a very important feature for retrieving the latest time or other data. 2const demoStore3 = createStore(() => { 3 return { 4 count: 0, 5 time: Date.now(), 6 }; 7});
1const demoStore4 = createStore({ 2 count: 0, 3 increase() { 4 // this point store object, as follows example 5 // The updates and usage of these APIs will be detailed in subsequent chapters 6 this.count++; 7 // this.setState({ count: this.count + 1 }); 8 // this.restore(); 9 10 // demoStore4.count++; 11 // demoStore4.setState({ count: demoStore3.count + 1 }); 12 }, 13});
1import { createStore } from "resy"; 2 3type StateType = { 4 count: number; 5 text: string; 6 info: { name: string }; 7 ageList: { age: number }[]; 8 increase(): void; 9 inputValue?: string; 10}; 11 12// The generated store can be shared globally 13const store = createStore<StateType>({ 14 count: 0, 15 text: "hello", 16 info: { name: "Jack" }, 17 ageList: [{age: 12}, { age: 16 }], 18 increase() { 19 this.count++; 20 }, 21});
1// Store such as login and theme can set unmountRestore to false 2// so that it will not be reset globally. 3const userStore = createStore<{ userName: string; userId: number }>( 4 { 5 userName: "wenmu", 6 userId: 0, 7 }, 8 { 9 unmountRestore: false, 10 }, 11); 12const themeStore = createStore<{ themeStyle: "dark" | "light" }>( 13 { 14 themeStyle: "dark", 15 }, 16 { 17 unmountRestore: false, 18 }, 19);
1import { useStore } from "resy"; 2 3function App() { 4 const { count, text } = useStore(store); 5 // or 6 // const { count, text } = store.useStore(); 7 8 return ( 9 <> 10 <p>{count}</p> 11 <p>{text}</p> 12 </> 13 ); 14}
1import { useStore } from "resy"; 2 3function App() { 4 const { userName } = userStore.useStore(); 5 const { themeStyle } = themeStore.useStore(); 6 7 return ( 8 <> 9 <p>{userName}</p> 10 <p>{themeStyle}</p> 11 <button onClick={() => { userStore.userName = "LF" }}>nameChange</button> 12 <button onClick={() => { themeStore.setState({ themeStyle: "light" }) }}>themeChange</button> 13 </> 14 ); 15}
1import { useStore } from "resy"; 2 3function App() { 4 const state = store.useStore(); 5 6 return ( 7 <> 8 <p>{state.count}</p> 9 <p>{state.text}</p> 10 </> 11 ); 12}
1import { useStore } from "resy"; 2 3function App() { 4 const { 5 count, text, 6 // The use of these api will be described in detail later. 7 setState, syncUpdate, restore, subscribe, 8 } = store.useStore(); 9 10 return ( 11 <> 12 <p>{count}</p> 13 <p>{text}</p> 14 </> 15 ); 16}
1import { useStore } from "resy"; 2 3function App() { 4 const { count, text } = store.useStore(); 5 6 // Updates can be assigned directly 7 function btn2() { 8 store.count++; 9 store.text = "456asd"; 10 } 11 12 return ( 13 <> 14 <p>{count}</p> 15 <p>{text}</p> 16 </> 17 ); 18}
1import { ComponentWithStore, PureComponentWithStore } from "resy"; 2 3/** 4 * @description ComponentWithStore is inherited from React Component, 5 * PureComponentWithStore is inherited from React PureComponent; 6 */ 7class AppClass extends ComponentWithStore { 8 9 store = this.connectStore(store); 10 11 render() { 12 const { count } = this.store; 13 return ( 14 <> 15 {count} 16 <button onClick={() => { store.count++; }}>button +</button> 17 </> 18 ); 19 } 20} 21 22class PureAppClass extends PureComponentWithStore { 23 store = this.connectStore(store); 24 25 render() { 26 const { count } = this.store; 27 return ( 28 <> 29 {count} 30 <button onClick={() => { store.count++; }}>button +</button> 31 </> 32 ); 33 } 34}
1import { ComponentWithStore, createStore } from "resy"; 2 3/** 4 * @description The update methods of internal "this.userStore" and "this.themeStore" 5 * are the same as those of the connected store itself, and can be called directly. 6 */ 7class AppClass extends ComponentWithStore { 8 9 userStore = this.connectStore(userStore); 10 11 themeStore = this.connectStore(themeStore); 12 13 render() { 14 const { userName } = this.userStore; 15 const { theme } = this.themeStore; 16 return ( 17 <> 18 <span>{userName}</span> 19 <span>{theme}</span> 20 <button onClick={() => { userStore.userName = "LD" }}> 21 nameChange 22 </button> 23 <button onClick={() => { themeStore.setState({ theme: "light" }) }}> 24 themeChange 25 </button> 26 </> 27 ); 28 } 29}
1import { useStore } from "resy"; 2 3function App() { 4 const { 5 info: { name }, ageList, inputValue, 6 } = store.useStore(); 7 8 function btn2() { 9 // store.info.name = "Jack"; // Invalid update 10 // store.ageList[0] = { age: 7 }; // Invalid update 11 12 store.info = { name: "Jack" }; // Effective update 13 store.ageList = [{age: 7}]; // Effective update 14 } 15 16 return ( 17 <> 18 <p>{name}</p> 19 {ageList.map(item => `Age:${item}`)}<br/> 20 <button onClick={btn2}>btn2</button> 21 </> 22 ); 23}
1import { createStore } from "resy"; 2 3const store = createStore({ 4 count: 0, 5 text: "ok", 6 getTextPro(str?: number | string) { 7 /** 8 * @description Here, using `useStore` works fine even in class components, 9 * as we have made it compatible. 10 * In fact, if it's needed only in class components, 11 * you can directly use `this` to achieve the purpose of referencing data internally within the function for rendering. 12 * 🌟 The reason for using `this.useStore()` within `getTextPro` is because, 13 * in the demo below, the `App` component doesn't reference the `text` data within `store.useStore()`. 14 * Therefore, when updating the `text` state, if `useStore` isn't called inside `getTextPro`, 15 * it won't be possible to track the reference to the data for updates. Of course, 16 * this example is specific to hook components. As mentioned earlier, 17 * in class components, `this.useStore()` isn't actually necessary; you could directly use `this`. 18 * However, the usage of `this.useStore()` within `getTextPro` remains compatible even if `getTextPro` is placed inside a class component. 19 */ 20 const { text } = this.useStore(); 21 // Returns the final result for rendering. 22 return `${str ?? ""}_${text}_world`; 23 }, 24}); 25 26const App = () => { 27 const { count, getTextPro } = store.useStore(); 28 29 return ( 30 <div> 31 <div>count:{count}</div> 32 <div>textPro:{getTextPro("none")}</div> 33 <button 34 onClick={() => { 35 store.count++; 36 }} 37 > 38 countChange 39 </button> 40 <button 41 onClick={() => { 42 store.text = "hello"; 43 }} 44 > 45 textChange 46 </button> 47 </div> 48 ); 49}; 50
1import { createStore, ComponentWithStore } from "resy"; 2 3class App extends ComponentWithStore { 4 store = this.connectStore(store); 5 6 render() { 7 const { count, getTextPro } = this.store; 8 return ( 9 <div> 10 <div>count:{count}</div> 11 <div>textPro:{getTextPro("none")}</div> 12 <button onClick={() => store.count++}> 13 countChange 14 </button> 15 <button onClick={() => store.text = "hello"}> 16 textChange 17 </button> 18 </div> 19 ); 20 } 21}
1import { useStore } from "resy"; 2 3function App() { 4 const { count, text } = store.useStore(); 5 6 return ( 7 <> 8 <div>{count}</div> 9 <div>{text}</div> 10 <button 11 onClick={() => { 12 store.setState({ 13 text: "demo-setState", 14 count: count + 1, 15 }); 16 }} 17 > 18 btn 19 </button> 20 </> 21 ); 22}
1import { useStore } from "resy"; 2 3function App() { 4 const { text } = store.useStore(); 5 6 return ( 7 <button 8 onClick={() => { 9 store.setState({ 10 text: "cur-text", 11 }, nextState => { 12 console.log(nextState.text === "cur-text"); // true 13 }); 14 }} 15 > 16 {text} 17 </button> 18 ); 19}
the difference between the callback of setState and the callback of this.setState of class components
1import { Component } from "react"; 2 3class TestClassX extends Component { 4 constructor() { 5 super(); 6 this.state = { count: 0, text: "class-x" }; 7 } 8 9 render() { 10 const { count, text } = this.state; 11 return ( 12 <> 13 {count},{text} 14 <button 15 onClick={() => { 16 this.setState({ 17 text: "Try", 18 }, () => { 19 console.log(this.state.count === 9); // true 20 }); 21 this.setState({ count: 9 }); 22 }} 23 > 24 btn 25 </button> 26 </> 27 ); 28 } 29}
1import { useStore, createStore } from "resy"; 2 3const store = createStore({count: 0, text: "hello"}); 4 5function App() { 6 const { text } = store.useStore(); 7 8 return ( 9 <button 10 onClick={() => { 11 store.setState({ 12 text: "cur-text", 13 }, nextState => { 14 console.log(nextState.text === "cur-text"); // true 15 console.log(nextState.count === 0); // true 16 console.log(store.count === 9); // true 17 }); 18 store.setState({count: 9}); 19 }} 20 > 21 {text} 22 </button> 23 ); 24}
1import { useStore } from "resy"; 2 3const store = createStore({count: 0, text: "hello"}); 4 5function App() { 6 const { count, text } = store.useStore(); 7 8 function btnClick1() { 9 store.setState(() => { 10 // Returns the object that will eventually be updated 11 // through the calculation of complex business logic 12 return { 13 count: count + 1, 14 text: "B-Way-setState-with-function", 15 }; 16 }); 17 } 18 19 function btnClick2() { 20 store.count = 9; 21 // The prevState parameter of the function 22 store.setState(prevState => { 23 console.log(prevState.count === 9); // true 24 console.log(store.count === 9); // true 25 return { 26 text: "ok", 27 }; 28 }); 29 } 30 31 return ( 32 <> 33 <div>{count}</div> 34 <div>{text}</div> 35 <button onClick={btnClick1}>btn-1</button> 36 <button onClick={btnClick2}>btn-2</button> 37 </> 38 ); 39}
1import { useStore, syncUpdate } from "resy"; 2 3/** 4 * @description 🌟 The main purpose of syncUpdate is to solve the problem 5 * that input box updates such as input cannot be updated in an asynchronous environment. 6 */ 7function App() { 8 const { inputValue } = store.useStore(); 9 10 function inputChange(event: React.ChangeEvent<HTMLInputElement>) { 11 store.syncUpdate({ 12 inputValue: event.target.value, 13 }); 14 // @example B 15 // store.syncUpdate(prevState => { 16 // // prevState is same as setState's prevState. 17 // return { 18 // inputValue: event.target.value, 19 // }; 20 // }); 21 // @example C 22 // You can also use the callback function 23 // store.syncUpdate({ 24 // inputValue: event.target.value, 25 // }, nextState => { 26 // console.log(nextState); 27 // }); 28 } 29 30 return ( 31 <input value={inputValue} onChange={inputChange}/> 32 ); 33}
1import { useStore } from "resy"; 2 3// Updates to count data will not cause Text components to re-render 4function Text() { 5 const { text } = store.useStore(); 6 return <p>{text}</p>; 7} 8 9// Updates to text data will not cause Count components to re-render 10function Count() { 11 const { count } = store.useStore(); 12 return <p>{count}</p>; 13} 14 15function App() { 16 const { increase, name } = store.useStore(); 17 18 return ( 19 <> 20 <Text/> 21 <Count/> 22 <div>{name}</div> 23 <button onClick={() => { store.name = "app"; }}>btn-name</button> 24 <button onClick={increase}>btn+</button> 25 <button onClick={() => { store.count-- }}>btn-</button> 26 </> 27 ); 28}
1import { useStore, ComponentWithStore } from "resy"; 2 3// Updates to count data will not cause Text components to re-render 4class TextClass extends ComponentWithStore { 5 6 store = this.connectStore(store); 7 8 render() { 9 const { text } = this.store; 10 return ( 11 <p>{text}</p> 12 ); 13 } 14} 15 16// Updates to text data will not cause Count components to re-render 17class CountClass extends ComponentWithStore { 18 19 store = this.connectStore(store); 20 21 render() { 22 const { count } = this.store; 23 return ( 24 <p>{count}</p> 25 ); 26 } 27} 28 29class AppClass extends ComponentWithStore { 30 31 store = this.connectStore(store); 32 33 render() { 34 const { increase, name } = this.store; 35 return ( 36 <> 37 <Text/> 38 <Count/> 39 <div>{name}</div> 40 <button onClick={() => { store.name = "app" }}>btn-name</button> 41 <button onClick={increase}>btn+</button> 42 <button onClick={() => { store.count-- }}>btn-</button> 43 </> 44 ); 45 } 46}
API | Description |
---|---|
useConciseState | Concise version of useState |
subscribe | Subscribe for changes in store data generated by createStore |
useSubscription | Hook of subscribe |
restore | Restore data of store, with re-render effect |
setOptions | Set the options parameter of createStore |
getOptions | Get the options parameter of createStore |
The functionality of useConciseState is not limited to just a concise syntax on the surface. Its deeper capability is to deconstruct the store and provide sub-components with a doorway that allows for comprehensive control over the store's data, rendering, updates, and subscriptions.
1import { useConciseState } from "resy"; 2 3const initialState = { 4 count: 123, 5 text: "hello-consice", 6}; 7 8function App() { 9 const { count, text, store, setState } = useConciseState(initialState); 10 11 return ( 12 <> 13 <div 14 onClick={() => { 15 setState({ 16 count: count + 1, 17 text: "ASD", 18 }); 19 // or 20 // store.count++; 21 // store.text = "ASD"; 22 // or 23 // store.setState({ 24 // count: count + 1, 25 // text: "ASD", 26 // }); 27 // store has all the data of useConciseState 28 // and the restore, syncUpdate, and subscribe methods 29 }} 30 > 31 {count} 32 </div> 33 <div>{text}</div> 34 </> 35 ); 36}
restore、syncUpdate、subscribe these api can also be deconstructed and used directly.
1import { useEffect } from "react"; 2import { useConciseState } from "resy"; 3 4function App() { 5 const { count, text, restore, syncUpdate, subscribe } = useConciseState(initialState); 6 7 useEffect(() => { 8 return subscribe(({ effectState }) => { 9 console.log(effectState); 10 }, ["text"]); 11 }, []); 12 13 return ( 14 <> 15 <input 16 value={text} 17 onChange={(event: React.ChangeEvent<HTMLInputElement>) => { 18 syncUpdate({text: event.target.value}); 19 }} 20 /> 21 <div onClick={() => restore()}>reset-btn</div> 22 <div>{text}</div> 23 </> 24 ); 25}
1import { useConciseState, StoreType } from "resy"; 2 3type State = { 4 count: number; 5 text: string; 6}; 7 8function ChildOne(props: StoreType<State>) { 9 const { store } = props; 10 const { count, text } = useStore(store); 11 12 return ( 13 <> 14 <p>ChildOne-count:{count}</p> 15 <p>ChildOne-text:{text}</p> 16 <button 17 onClick={() => { 18 store.setState({ 19 count: 999, 20 text: "ChildOneSetStateNewText", 21 }); 22 }} 23 > 24 childOneBtn 25 </button> 26 </> 27 ); 28} 29 30function ChildTwo(props: StoreType<State>) { 31 const { store } = props; 32 const [data, setData] = useState({ count: 0, text: "hello" }); 33 34 store.useSubscription(({ nextState }) => { 35 setData(nextState); 36 }); 37 38 return ( 39 <> 40 <p>ChildTwo-count:{data.count}</p> 41 <p>ChildTwo-text:{data.text}</p> 42 </> 43 ); 44} 45 46const App = () => { 47 const { count, text, store } = useConciseState<State>({ 48 count: 0, 49 text: "hello", 50 }); 51 52 return ( 53 <> 54 <p>{count}</p> 55 <p>{text}</p> 56 <ChildOne store={store} /> 57 <ChildTwo store={store} /> 58 <button onClick={() => { 59 store.setState({ 60 count: 1, 61 text: "world", 62 }); 63 }}>change</button> 64 </> 65 ); 66};
1// You can also subscribe to a non-lifecycle data monitor directly. 2const unsub = store.subscribe(() => { 3 // ... to do anything 4}, ["count", "text"]); 5 6// cancel subscirbe 7// unsub();
1store.subscribe(() => { 2 // ... to do anything 3}, []); 4// [] or no state keys is equal 5// no state keys 6store.subscribe(() => { 7 // ... to do anything 8});
1import { useEffect } from "react"; 2import { useStore } from "resy"; 3 4function App() { 5 const { count } = store.useStore(); 6 7 // Here is an example of a function component. 8 // If it is a class component, it can be used in componentDidMount. 9 useEffect(() => { 10 /** 11 * @param listener: subscription monitoring callback function 12 * @param stateKeys: subscription listens for changes in certain data fields of a specific store. 13 * If empty, default listens for changes in any one of the data in store. 14 * @return Unsubscribe: unsubscribe to the function of listening 15 */ 16 const unsubscribe = store.subscribe(({ 17 effectState, prevState, nextState, 18 }) => { 19 /** 20 * effectState:Currently changing data 21 * nextState:Data after change 22 * prevState:Data before change 23 */ 24 console.log(effectState, prevState, nextState); 25 }, ["count", "text"]); 26 27 // unsubscribe(); 28 return () => { 29 unsubscribe(); 30 // ... to do else anything 31 }; 32 }, []); 33 34 function btnClickA() { 35 store.count++; 36 } 37 38 function btnClickB() { 39 store.text = "control btn-b click update text state value"; 40 } 41 42 function btnClickC() { 43 store.setState({ 44 count: count + 1, 45 text: "control btn-c click update text state value", 46 }); 47 } 48 49 return ( 50 <> 51 <p>{count}</p> 52 <button onClick={btnClickA}>btn-A</button><br/> 53 <button onClick={btnClickB}>btn-B</button><br/> 54 <button onClick={btnClickC}>btn-C</button> 55 </> 56 ); 57}
1import { useEffect } from "react"; 2import { useStore, useSubscription } from "resy"; 3 4function App() { 5 const { count } = store.useStore(); 6 7 useSubscription(store, ({ 8 effectState, prevState, nextState, 9 }) => { 10 console.log(effectState, prevState, nextState); 11 }, ["count"]); 12 13 function btnClick() { 14 store.count++; 15 } 16 17 return ( 18 <> 19 <p>{count}</p> 20 <button onClick={btnClick}>btn</button><br/> 21 </> 22 ); 23}
1import { useEffect } from "react"; 2import { useStore } from "resy"; 3 4function App() { 5 const { count } = store.useStore(); 6 7 store.useSubscription(({ 8 effectState, prevState, nextState, 9 }) => { 10 console.log(effectState, prevState, nextState); 11 }, ["count"]); 12 13 function btnClick() { 14 store.count++; 15 } 16 17 return ( 18 <> 19 <p>{count}</p> 20 <button onClick={btnClick}>btn</button><br/> 21 </> 22 ); 23}
1import { useStore } from "resy"; 2 3function App() { 4 const { count, text } = store.useStore(); 5 6 return ( 7 <> 8 <div>{count}-{text}</div> 9 <div 10 onClick={() => { 11 // data recover initial 12 store.restore(); 13 // You can also add callback functions in the restore function 14 // store.restore(nextState => { 15 // console.log(nextState); 16 // }); 17 }} 18 > 19 reset-btn 20 </div> 21 </> 22 ); 23}
1import { createStore, useStore } from "resy"; 2 3const timeStore = createStore(() => { 4 return { 5 now: Date.now(), 6 }; 7}); 8 9function App() { 10 const { now } = useStore(timeStore); 11 12 return ( 13 <> 14 <div>now:{now}</div> 15 <div 16 onClick={() => { 17 // time data now recover and also changed initial, 18 // because of initialState is function return. 19 store.restore(); 20 }} 21 > 22 reset-btn 23 </div> 24 </> 25 ); 26}
1function App() { 2 return ( 3 <button 4 onClick={() => { 5 // Use less scenes, use it with caution 6 // You can change the unmountRestore parameter setting of createStore 7 store.setOptions({ unmountRestore: false }); 8 }} 9 > 10 btn 11 </button> 12 ); 13}
1function App() { 2 return ( 3 <button 4 onClick={() => { 5 /** 6 * @description When executed in conjunction with "setOptions", 7 * it allows users to make different coding decisions based on various configurations, 8 * while being aware of the current settings. 9 * 🌟 Different from the considerations for the parameter types of "setOptions", 10 * "getOptions" returns a configuration object for all settings. 11 * This is because these read-only settings do not affect code security, 12 * and the parameters for "setOptions" are only aimed at the "unmountRestore" configuration item. 13 * Providing all configuration items may also be for the convenience of subsequent internal coding considerations. 14 */ 15 const currentOptions = store.getOptions(); 16 }} 17 > 18 btn 19 </button> 20 ); 21}
MIT License (c) 刘善保
No vulnerabilities found.
No security vulnerabilities found.