muya 1.0.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/LICENSE ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+
2
+ # Muya 🌀
3
+ Welcome to Muya - Making state management a breeze, focused on simplicity and scalability for real-world scenarios.
4
+
5
+ [![Build](https://github.com/samuelgja/muya/actions/workflows/build.yml/badge.svg)](https://github.com/samuelgja/muya/actions/workflows/build.yml)
6
+ [![Code quality Check](https://github.com/samuelgja/muya/actions/workflows/code-check.yml/badge.svg)](https://github.com/samuelgja/muya/actions/workflows/code-check.yml)
7
+ [![Build Size](https://img.shields.io/bundlephobia/minzip/muya?label=Bundle%20size)](https://bundlephobia.com/result?p=muya)
8
+
9
+
10
+
11
+ ## 🚀 Features
12
+ - Easy State Creation: Kickstart your state management with simple and intuitive APIs.
13
+ - Selectors & Merges: Grab exactly what you need from your state and combine multiple states seamlessly.
14
+ - Deep Nesting Support: Handle complex state structures without breaking a sweat.
15
+ - Optimized Rendering: Prevent unnecessary re-renders
16
+ - TypeScript Ready: Fully typed for maximum developer sanity.
17
+ - Small Bundle Size: Lightweight and fast, no bloatware here.
18
+
19
+
20
+ ## 📦 Installation
21
+
22
+ ```bash
23
+ bun add muya
24
+ ```
25
+ or
26
+ ```bash
27
+ yarn add muya
28
+ ```
29
+ or
30
+ ```bash
31
+ npm install muya
32
+ ```
33
+
34
+ ## 📝 Quick Start
35
+
36
+ ```typescript
37
+ import { create } from 'muya'
38
+
39
+ const useCounter = create(0)
40
+
41
+ function App() {
42
+ const counter = useCounter()
43
+ return <div onClick={() => useCounter.setState((prev) => prev + 1)}>{counter}</div>
44
+ }
45
+ ```
46
+
47
+ ### Update
48
+ Sugar syntax above the `setState` method for partially updating the state.
49
+ ```typescript
50
+ import { create } from 'muya'
51
+
52
+ const useUser = create({ name: 'John', lastName: 'Doe' })
53
+
54
+ function App() {
55
+ const user = useUser()
56
+ // this will just partially update only the name field, it's sugar syntax for setState.
57
+ return <div onClick={() => useUser.updateState({ name: 'Nope' })}>{user.name}</div>
58
+ }
59
+ ```
60
+
61
+
62
+ ### Selecting parts of the state globally
63
+ ```tsx
64
+ import { create } from 'muya'
65
+
66
+ const useUser = create({ name: 'John', age: 30 })
67
+
68
+ // Selecting only the name part of the state
69
+ const useName = useUser.select((user) => user.name)
70
+
71
+ function App() {
72
+ const name = useName()
73
+ return <div onClick={() => useUser.setState((prev) => ({ ...prev, name: 'Jane' }))}>{name}</div>
74
+ }
75
+
76
+ ```
77
+
78
+ ### Merge two states
79
+ ```typescript
80
+ import { create, shallow } from 'muya'
81
+
82
+ const useName = create(() => 'John')
83
+ const useAge = create(() => 30)
84
+
85
+ const useFullName = useName.merge(useAge, (name, age) => ` ${name} and ${age}`, shallow)
86
+
87
+ function App() {
88
+ const fullName = useFullName()
89
+ return <div onClick={() => useName.setState((prev) => 'Jane')}>{fullName}</div>
90
+ }
91
+ ```
92
+
93
+
94
+ ### Promise based state and lifecycle management working with React Suspense
95
+ This methods are useful for handling async data fetching and lazy loading via React Suspense.
96
+
97
+ #### Immediate Promise resolution
98
+ ```typescript
99
+ import { create } from 'muya';
100
+ // state will try to resolve the promise immediately, can hit the suspense boundary
101
+ const counterState = create(Promise.resolve(0));
102
+
103
+ function Counter() {
104
+ const counter = counterState();
105
+ return (
106
+ <div onClick={() => counterState.setState((prev) => prev + 1)}>
107
+ {counter}
108
+ </div>
109
+ );
110
+ }
111
+ ```
112
+
113
+ #### Lazy Promise resolution
114
+ ```typescript
115
+ import { create } from 'muya';
116
+ // state will lazy resolve the promise on first access, this will hit the suspense boundary if the first access is from component and via `counterState.getState()` method
117
+ const counterState = create(() => Promise.resolve(0));
118
+
119
+ function Counter() {
120
+ const counter = counterState();
121
+ return (
122
+ <div onClick={() => counterState.setState((prev) => prev + 1)}>
123
+ {counter}
124
+ </div>
125
+ );
126
+ }
127
+ ```
128
+
129
+
130
+ ## 🔍 API Reference
131
+
132
+ ### `create`
133
+
134
+ Creates a basic atom state.
135
+
136
+ ```typescript
137
+ function create<T>(defaultState: T, options?: StateOptions<T>): StateSetter<T>;
138
+ ```
139
+
140
+ **Example:**
141
+
142
+ ```typescript
143
+ const userState = create({ name: 'John', age: 30 });
144
+ ```
145
+
146
+ ### `select`
147
+
148
+ Selects a slice of an existing state directly or via a selector function.
149
+
150
+ ```typescript
151
+ // userState is ready to use as hook, so you can name it with `use` prefix
152
+ const userState = create({ name: 'John', age: 30 });
153
+ // Direct selection outside the component, is useful for accessing the slices of the state in multiple components
154
+ const userAgeState = userState.select((user) => user.age);
155
+ ```
156
+
157
+ ### `merge`
158
+ Merges two states into a single state.
159
+ ```typescript
160
+ const useName = create(() => 'John');
161
+ const useAge = create(() => 30);
162
+ const useFullName = useName.merge(useAge, (name, age) => ` ${name} and ${age}`);
163
+ ```
164
+
165
+
166
+ ### `setState`
167
+ Sets the state to a new value or a function that returns a new value.
168
+
169
+ ```typescript
170
+ const userState = create({ name: 'John', age: 30 });
171
+ userState.setState({ name: 'Jane' });
172
+ ```
173
+
174
+ ### `updateState`
175
+ Partially updates the state with a new value.
176
+
177
+ ```typescript
178
+ const userState = create({ name: 'John', age: 30 });
179
+ userState.updateState({ name: 'Jane' });
180
+ ```
181
+
182
+ ### `getState`
183
+ Returns the current state value outside the component.
184
+
185
+ ```typescript
186
+ const userState = create({ name: 'John', age: 30 });
187
+ const user = userState.getState();
188
+ ```
189
+
190
+ ### `use`
191
+ Creates a hook for the state.
192
+
193
+ ```typescript
194
+ const useCounter = create(0);
195
+ // use inside the component
196
+ const counter = useCounter();
197
+ ```
198
+
199
+ ### `subscribe`
200
+ Subscribes to the state changes.
201
+
202
+ ```typescript
203
+ const userState = create({ name: 'John', age: 30 });
204
+ const unsubscribe = userState.subscribe((state) => console.log(state));
205
+ ```
206
+
207
+
208
+
209
+ ### Access from outside the component
210
+ :warning: Avoid using this method for state management in [React Server Components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md), especially in Next.js 13+. It may cause unexpected behavior or privacy concerns.
211
+ ```typescript
212
+ const userState = create({ name: 'John', age: 30 });
213
+ const user = userState.getState();
214
+ ```
215
+ ---
216
+
217
+
218
+ ### Slicing new references
219
+ :warning: Slicing data with new references can lead to maximum call stack exceeded error.
220
+ It's recommended to not use new references for the state slices, if you need so, use `shallow` or other custom equality checks.
221
+ ```typescript
222
+ import { state, shallow } from 'muya';
223
+ const userState = create({ name: 'John', age: 30 });
224
+ // this slice will create new reference object on each call
225
+ const useName = userState.select((user) => ({newUser: user.name }), shallow);
226
+ ```
227
+
228
+ ## 🤖 Contributing
229
+ Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) before submitting a pull request.
230
+
231
+ ## 🧪 Testing
232
+ Muya comes with a robust testing suite. Check out the state.test.tsx for examples on how to write your own tests.
233
+
234
+ ## 📜 License
235
+
236
+ Muya is [MIT licensed](LICENSE).
package/cjs/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var G=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var C=(t,e)=>{for(var r in e)G(t,r,{get:e[r],enumerable:!0})},L=(t,e,r,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of F(e))!M.call(t,n)&&n!==r&&G(t,n,{get:()=>e[n],enumerable:!(a=z(e,n))||a.enumerable});return t};var W=t=>L(G({},"__esModule",{value:!0}),t);var H={};C(H,{StateKeys:()=>_,create:()=>D,getDefaultValue:()=>y,merge:()=>b,select:()=>g,shallow:()=>v,useStateValue:()=>x});module.exports=W(H);function p(t){return t instanceof Promise}function q(t){return typeof t=="function"}function I(t){return typeof t=="function"}function R(t){return typeof t=="object"&&t!==null}function w(t){return t instanceof Map}function k(t){return t instanceof Set}function V(t){return Array.isArray(t)}function c(t,e){return t===e?!0:!!Object.is(t,e)}var _=(r=>(r.IS_STATE="isState",r.IS_SLICE="isSlice",r))(_||{});function y(t){return p(t)?t:q(t)?t():t}function T(t){let e=new Set;return{subscribe:r=>(e.add(r),()=>{e.delete(r)}),emit:(...r)=>{for(let a of e)a(...r)},getSnapshot:t}}var P=require("use-sync-external-store/shim/with-selector"),h=require("react");function O(t){return t}function j(t,e,r){let a=(0,P.useSyncExternalStoreWithSelector)(t.subscribe,t.getSnapshot,t.getSnapshot,e?n=>e(n):O,r);return(0,h.useDebugValue)(a),a}function x(t,e=a=>a,r){let a=j(t.__internal.emitter,n=>e(n),r);if(p(a))throw a;return a}function m(t){let{baseState:e}=t,r=(a,n)=>x(r,a,n);return r.__internal=e.__internal,r.getState=e.getState,r.reset=e.reset,r.select=e.select,r.merge=e.merge,r.subscribe=e.subscribe,r}function b(t,e,r,a=c){let n,o=T(()=>{let f=r(t.getState(),e.getState());return n!==void 0&&a(n,f)?n:(n=f,f)});t.__internal.emitter.subscribe(()=>{o.emit()}),e.__internal.emitter.subscribe(()=>{o.emit()});let s=l({emitter:o,getGetterState:()=>S,getState:()=>r(t.getState(),e.getState()),reset(){t.reset(),e.reset()}}),S=m({baseState:s});return S}function g(t,e,r=c){let a,n=T(()=>{let S=e(t.getState());return a!==void 0&&r(a,S)?a:(a=S,S)});t.__internal.emitter.subscribe(()=>{n.emit()});let o=l({emitter:n,getGetterState:()=>s,getState:()=>e(t.getState()),reset:t.reset}),s=m({baseState:o});return s}function l(t){let{emitter:e,getGetterState:r,reset:a,getState:n}=t;return{getState:n,reset:a,select(o,s){let S=r();return g(S,o,s)},merge(o,s,S){let f=r();return b(f,o,s,S)},__internal:{emitter:e},subscribe(o){return o(n()),e.subscribe(()=>{o(n())})}}}function D(t,e=c){function r(i,u){return I(u)?u(i):u}let a={updateVersion:0,value:void 0};function n(){return a.value===void 0&&(a.value=y(t)),a.value}function o(){let i=n();return p(i)&&i.then(u=>{a.value=u,f.emit()}),i}function s(i){let u=n(),E=r(u,i);e?.(u,E)||E===u||(a.updateVersion++,a.value=E,f.emit())}function S(i){if(R(i))return s(u=>({...u,...i}));s(i)}let f=T(o),U=l({emitter:f,getGetterState:()=>d,getState:o,reset(){let i=y(t);if(p(i)){i.then(u=>{s(u)});return}s(i)}}),d=m({baseState:U});return d.setState=s,d.updateState=S,d}function v(t,e){if(t==e||Object.is(t,e))return!0;if(typeof t!="object"||t==null||typeof e!="object"||e==null)return!1;if(w(t)&&w(e)){if(t.size!==e.size)return!1;for(let[n,o]of t)if(!Object.is(o,e.get(n)))return!1;return!0}if(k(t)&&k(e)){if(t.size!==e.size)return!1;for(let n of t)if(!e.has(n))return!1;return!0}if(V(t)&&V(e)){if(t.length!==e.length)return!1;for(let[n,o]of t.entries())if(!Object.is(o,e[n]))return!1;return!0}let r=Object.keys(t),a=Object.keys(e);if(r.length!==a.length)return!1;for(let n of r)if(!Object.prototype.hasOwnProperty.call(e,n)||!Object.is(t[n],e[n]))return!1;return!0}
package/esm/common.js ADDED
@@ -0,0 +1 @@
1
+ import{useSyncExternalStoreWithSelector as u}from"use-sync-external-store/shim/with-selector";import{useDebugValue as s}from"react";function S(e){return e}function i(e,t,r){const n=u(e.subscribe,e.getSnapshot,e.getSnapshot,t?o=>t(o):S,r);return s(n),n}export{S as toType,i as useSyncExternalStore};
@@ -0,0 +1 @@
1
+ import{merge as c}from"./merge";import{select as p}from"./select";function y(n){const{emitter:o,getGetterState:s,reset:i,getState:e}=n;return{getState:e,reset:i,select(t,r){const a=s();return p(a,t,r)},merge(t,r,a){const m=s();return c(m,t,r,a)},__internal:{emitter:o},subscribe(t){return t(e()),o.subscribe(()=>{t(e())})}}}export{y as createBaseState};
@@ -0,0 +1 @@
1
+ function n(r){const t=new Set;return{subscribe:e=>(t.add(e),()=>{t.delete(e)}),emit:(...e)=>{for(const i of t)i(...e)},getSnapshot:r}}export{n as createEmitter};
@@ -0,0 +1 @@
1
+ import{useStateValue as n}from"./use-state-value";function i(r){const{baseState:t}=r,e=(a,s)=>n(e,a,s);return e.__internal=t.__internal,e.getState=t.getState,e.reset=t.reset,e.select=t.select,e.merge=t.merge,e.subscribe=t.subscribe,e}export{i as createGetterState};
package/esm/create.js ADDED
@@ -0,0 +1 @@
1
+ import{createEmitter as d}from"./create-emitter";import{getDefaultValue as S}from"./types";import{isEqualBase as V,isObject as v,isPromise as c,isSetValueFunction as g}from"./is";import{createBaseState as D}from"./create-base-state";import{createGetterState as E}from"./create-getter-state";function y(s,f=V){function T(t,e){return g(e)?e(t):e}const a={updateVersion:0,value:void 0};function o(){return a.value===void 0&&(a.value=S(s)),a.value}function l(){const t=o();return c(t)&&t.then(e=>{a.value=e,n.emit()}),t}function r(t){const e=o(),i=T(e,t);f?.(e,i)||i===e||(a.updateVersion++,a.value=i,n.emit())}function m(t){if(v(t))return r(e=>({...e,...t}));r(t)}const n=d(l),p=D({emitter:n,getGetterState:()=>u,getState:l,reset(){const t=S(s);if(c(t)){t.then(e=>{r(e)});return}r(t)}}),u=E({baseState:p});return u.setState=r,u.updateState=m,u}export{y as create};
package/esm/index.js ADDED
@@ -0,0 +1 @@
1
+ export*from"./types";import{create as t}from"./create";import{select as f}from"./select";import{merge as x}from"./merge";import{useStateValue as l}from"./use-state-value";import{shallow as c}from"./shallow";export{t as create,x as merge,f as select,c as shallow,l as useStateValue};
package/esm/is.js ADDED
@@ -0,0 +1 @@
1
+ function o(n){return n instanceof Promise}function r(n){return typeof n=="function"}function u(n){return typeof n=="function"}function t(n){return typeof n=="object"&&n!==null}function i(n){return t(n)&&n.isRef===!0}function s(n){return n instanceof Map}function f(n){return n instanceof Set}function a(n){return Array.isArray(n)}function c(n,e){return n===e?!0:!!Object.is(n,e)}export{a as isArray,c as isEqualBase,r as isFunction,s as isMap,t as isObject,o as isPromise,i as isRef,f as isSet,u as isSetValueFunction};
package/esm/merge.js ADDED
@@ -0,0 +1 @@
1
+ import{createBaseState as u}from"./create-base-state";import{createEmitter as s}from"./create-emitter";import{createGetterState as c}from"./create-getter-state";import{isEqualBase as f}from"./is";function T(t,e,i,n=f){let r;const a=s(()=>{const S=i(t.getState(),e.getState());return r!==void 0&&n(r,S)?r:(r=S,S)});t.__internal.emitter.subscribe(()=>{a.emit()}),e.__internal.emitter.subscribe(()=>{a.emit()});const o=u({emitter:a,getGetterState:()=>m,getState:()=>i(t.getState(),e.getState()),reset(){t.reset(),e.reset()}}),m=c({baseState:o});return m}export{T as merge};
package/esm/select.js ADDED
@@ -0,0 +1 @@
1
+ import{createBaseState as m}from"./create-base-state";import{createEmitter as s}from"./create-emitter";import{createGetterState as u}from"./create-getter-state";import{isEqualBase as c}from"./is";function d(t,a,o=c){let e;const S=s(()=>{const r=a(t.getState());return e!==void 0&&o(e,r)?e:(e=r,r)});t.__internal.emitter.subscribe(()=>{S.emit()});const n=m({emitter:S,getGetterState:()=>i,getState:()=>a(t.getState()),reset:t.reset}),i=u({baseState:n});return i}export{d as select};
package/esm/shallow.js ADDED
@@ -0,0 +1 @@
1
+ import{isArray as o,isMap as f,isSet as i}from"./is";function b(t,r){if(t==r||Object.is(t,r))return!0;if(typeof t!="object"||t==null||typeof r!="object"||r==null)return!1;if(f(t)&&f(r)){if(t.size!==r.size)return!1;for(const[n,e]of t)if(!Object.is(e,r.get(n)))return!1;return!0}if(i(t)&&i(r)){if(t.size!==r.size)return!1;for(const n of t)if(!r.has(n))return!1;return!0}if(o(t)&&o(r)){if(t.length!==r.length)return!1;for(const[n,e]of t.entries())if(!Object.is(e,r[n]))return!1;return!0}const s=Object.keys(t),c=Object.keys(r);if(s.length!==c.length)return!1;for(const n of s)if(!Object.prototype.hasOwnProperty.call(r,n)||!Object.is(t[n],r[n]))return!1;return!0}export{b as shallow};
package/esm/types.js ADDED
@@ -0,0 +1 @@
1
+ import{isFunction as a,isPromise as r}from"./is";var T=(t=>(t.IS_STATE="isState",t.IS_SLICE="isSlice",t))(T||{});function o(e){return r(e)?e:a(e)?e():e}export{T as StateKeys,o as getDefaultValue};
@@ -0,0 +1 @@
1
+ import{useSyncExternalStore as n,toType as S}from"./common";import{isPromise as i}from"./is";function m(e,r=t=>S(t),o){const t=n(e.__internal.emitter,a=>r(a),o);if(i(t))throw t;return t}export{m as useStateValue};
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "muya",
3
+ "version": "1.0.1",
4
+ "author": "samuel.gjabel@gmail.com",
5
+ "description": "👀 Another React state management library",
6
+ "license": "MIT",
7
+ "main": "cjs/index.js",
8
+ "module": "esm/index.js",
9
+ "react-native": "src/index.ts",
10
+ "types": "types/index.d.ts",
11
+ "homepage": "https://github.com/samuelgjabel/muya",
12
+ "repository": "https://github.com/samuelgjabel/muya",
13
+ "sideEffects": false,
14
+ "keywords": [
15
+ "react",
16
+ "react-hooks",
17
+ "react-native",
18
+ "state",
19
+ "management",
20
+ "library",
21
+ "muya",
22
+ "redux",
23
+ "zustand"
24
+ ],
25
+ "peerDependencies": {
26
+ "use-sync-external-store": ">=1.2.0",
27
+ "react": ">=18.0.0"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "react": {
31
+ "optional": true
32
+ },
33
+ "use-sync-external-store": {
34
+ "optional": true
35
+ }
36
+ }
37
+ }
package/src/common.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { useSyncExternalStoreWithSelector as useSync } from 'use-sync-external-store/shim/with-selector'
2
+ import type { Emitter } from './create-emitter'
3
+ import type { IsEqual } from './types'
4
+ import { useDebugValue } from 'react'
5
+
6
+ /**
7
+ * Todo need to remove this
8
+ */
9
+ export function toType<T>(object?: unknown): T {
10
+ return object as T
11
+ }
12
+
13
+ export function useSyncExternalStore<T, S>(
14
+ emitter: Emitter<T>,
15
+ selector: (stateValue: T) => S,
16
+ isEqual?: IsEqual<S>,
17
+ ): undefined extends S ? T : S {
18
+ const value = useSync<T, S>(
19
+ emitter.subscribe,
20
+ emitter.getSnapshot,
21
+ emitter.getSnapshot,
22
+ selector ? (stateValue) => selector(stateValue) : toType,
23
+ isEqual,
24
+ ) as undefined extends S ? T : S
25
+
26
+ useDebugValue(value)
27
+ return value
28
+ }
@@ -0,0 +1,35 @@
1
+ import type { Emitter } from './create-emitter'
2
+ import { merge } from './merge'
3
+ import { select } from './select'
4
+ import type { BaseState, GetterState } from './types'
5
+
6
+ interface Options<T> {
7
+ readonly emitter: Emitter<T>
8
+ readonly reset: () => void
9
+ readonly getState: () => T
10
+ readonly getGetterState: () => GetterState<T>
11
+ }
12
+ export function createBaseState<T>(options: Options<T>): BaseState<T> {
13
+ const { emitter, getGetterState, reset, getState } = options
14
+ return {
15
+ getState,
16
+ reset,
17
+ select(selector, isSame) {
18
+ const state = getGetterState()
19
+ return select(state, selector, isSame)
20
+ },
21
+ merge(state2, selector, isEqualHook) {
22
+ const state = getGetterState()
23
+ return merge(state, state2, selector, isEqualHook)
24
+ },
25
+ __internal: {
26
+ emitter,
27
+ },
28
+ subscribe(listener) {
29
+ listener(getState())
30
+ return emitter.subscribe(() => {
31
+ listener(getState())
32
+ })
33
+ },
34
+ }
35
+ }
@@ -0,0 +1,24 @@
1
+ export type EmitterSubscribe<P = undefined> = (listener: (...params: P[]) => void) => () => void
2
+ export interface Emitter<T, R = T, P = undefined> {
3
+ subscribe: EmitterSubscribe<P>
4
+ getSnapshot: () => R
5
+ emit: (...params: P[]) => void
6
+ }
7
+
8
+ export function createEmitter<T, R = T, P = undefined>(getSnapshot: () => R): Emitter<T, R, P> {
9
+ const listeners = new Set<(...params: P[]) => void>()
10
+ return {
11
+ subscribe: (listener) => {
12
+ listeners.add(listener)
13
+ return () => {
14
+ listeners.delete(listener)
15
+ }
16
+ },
17
+ emit: (...params) => {
18
+ for (const listener of listeners) {
19
+ listener(...params)
20
+ }
21
+ },
22
+ getSnapshot,
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ import type { BaseState, GetterState } from './types'
2
+ import { useStateValue } from './use-state-value'
3
+
4
+ interface Options<T> {
5
+ readonly baseState: BaseState<T>
6
+ }
7
+ export function createGetterState<T>(options: Options<T>): GetterState<T> {
8
+ const { baseState } = options
9
+ const useSliceState: GetterState<T> = (useSelector, isEqualHook) => {
10
+ return useStateValue(useSliceState, useSelector, isEqualHook)
11
+ }
12
+ useSliceState.__internal = baseState.__internal
13
+ useSliceState.getState = baseState.getState
14
+ useSliceState.reset = baseState.reset
15
+ useSliceState.select = baseState.select
16
+ useSliceState.merge = baseState.merge
17
+ useSliceState.subscribe = baseState.subscribe
18
+ return useSliceState
19
+ }
package/src/create.ts ADDED
@@ -0,0 +1,102 @@
1
+ import { createEmitter } from './create-emitter'
2
+ import type { SetValue, SetterState, StateDataInternal, DefaultValue, GetterState, IsEqual, UpdateValue } from './types'
3
+ import { getDefaultValue } from './types'
4
+ import { isEqualBase, isObject, isPromise, isSetValueFunction } from './is'
5
+ import { createBaseState } from './create-base-state'
6
+ import { createGetterState } from './create-getter-state'
7
+
8
+ /**
9
+ * Creates a basic atom state.
10
+ * @param defaultValue - The initial state value.
11
+ * @param options - Optional settings for the state (e.g., isEqual, onSet).
12
+ * @returns A state object that can be used as a hook and provides state management methods.
13
+ * @example
14
+ * ```typescript
15
+ * // Global scope
16
+ * const counterState = state(0);
17
+ * const userState = state({ name: 'John', age: 20 });
18
+ *
19
+ * // React component
20
+ * const counter = counterState(); // Use as a hook
21
+ * const user = userState();
22
+ *
23
+ * // Access partial data from the state using slice
24
+ * const userAge = userState.slice((state) => state.age)();
25
+ * ```
26
+ */
27
+
28
+ export function create<T>(defaultValue: DefaultValue<T>, isEqual: IsEqual<T> = isEqualBase): SetterState<Awaited<T>> {
29
+ function resolveSetter(value: T, stateSetter: SetValue<T>): T {
30
+ if (isSetValueFunction(stateSetter)) {
31
+ return stateSetter(value)
32
+ }
33
+ return stateSetter
34
+ }
35
+
36
+ const stateData: StateDataInternal<T> = {
37
+ updateVersion: 0,
38
+ value: undefined,
39
+ }
40
+
41
+ function getValue(): T {
42
+ if (stateData.value === undefined) {
43
+ stateData.value = getDefaultValue(defaultValue)
44
+ }
45
+ return stateData.value
46
+ }
47
+ function get(): T {
48
+ const stateValue = getValue()
49
+ if (isPromise(stateValue)) {
50
+ stateValue.then((data) => {
51
+ stateData.value = data as Awaited<T>
52
+ emitter.emit()
53
+ })
54
+ }
55
+ return stateValue
56
+ }
57
+
58
+ function set(stateValue: SetValue<T>) {
59
+ const stateValueData = getValue()
60
+ const newState = resolveSetter(stateValueData, stateValue)
61
+ const isEqualResult = isEqual?.(stateValueData, newState)
62
+ if (isEqualResult || newState === stateValueData) {
63
+ return
64
+ }
65
+ stateData.updateVersion++
66
+ stateData.value = newState
67
+ emitter.emit()
68
+ }
69
+
70
+ function update(stateValue: UpdateValue<T>) {
71
+ if (isObject(stateValue)) {
72
+ return set((previousState) => {
73
+ return { ...previousState, ...stateValue }
74
+ })
75
+ }
76
+ set(stateValue as T)
77
+ }
78
+
79
+ const emitter = createEmitter<T>(get)
80
+
81
+ const baseState = createBaseState({
82
+ emitter,
83
+ getGetterState: () => setterState,
84
+ getState: get,
85
+ reset() {
86
+ const value = getDefaultValue(defaultValue)
87
+ if (isPromise(value)) {
88
+ value.then((data) => {
89
+ set(data as T)
90
+ })
91
+ return
92
+ }
93
+ set(value)
94
+ },
95
+ })
96
+
97
+ const getterState: GetterState<T> = createGetterState<T>({ baseState })
98
+ const setterState: SetterState<T> = getterState as SetterState<T>
99
+ setterState.setState = set
100
+ setterState.updateState = update
101
+ return setterState as SetterState<Awaited<T>>
102
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './types'
2
+ export { create } from './create'
3
+ export { select } from './select'
4
+ export { merge } from './merge'
5
+ export { useStateValue } from './use-state-value'
6
+ export { shallow } from './shallow'
package/src/is.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { Ref, Setter, SetValue } from './types'
2
+
3
+ export function isPromise(value: unknown): value is Promise<unknown> {
4
+ return value instanceof Promise
5
+ }
6
+ export function isFunction(value: unknown): value is (...args: unknown[]) => unknown {
7
+ return typeof value === 'function'
8
+ }
9
+ export function isSetValueFunction<T>(value: SetValue<T>): value is Setter<T> {
10
+ return typeof value === 'function'
11
+ }
12
+ export function isObject(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === 'object' && value !== null
14
+ }
15
+ export function isRef<T>(value: unknown): value is Ref<T> {
16
+ return isObject(value) && value.isRef === true
17
+ }
18
+
19
+ export function isMap(value: unknown): value is Map<unknown, unknown> {
20
+ return value instanceof Map
21
+ }
22
+
23
+ export function isSet(value: unknown): value is Set<unknown> {
24
+ return value instanceof Set
25
+ }
26
+
27
+ export function isArray(value: unknown): value is Array<unknown> {
28
+ return Array.isArray(value)
29
+ }
30
+
31
+ export function isEqualBase<T>(valueA: T, valueB: T): boolean {
32
+ if (valueA === valueB) {
33
+ return true
34
+ }
35
+ return !!Object.is(valueA, valueB)
36
+ }
package/src/merge.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { createBaseState } from './create-base-state'
2
+ import { createEmitter } from './create-emitter'
3
+ import { createGetterState } from './create-getter-state'
4
+ import { isEqualBase } from './is'
5
+ import type { IsEqual, GetterState } from './types'
6
+
7
+ export function merge<T1, T2, S>(
8
+ state1: GetterState<T1>,
9
+ state2: GetterState<T2>,
10
+ selector: (value1: T1, value2: T2) => S,
11
+ isEqual: IsEqual<S> = isEqualBase,
12
+ ): GetterState<S> {
13
+ let previousData: S | undefined
14
+ const emitter = createEmitter(() => {
15
+ const data = selector(state1.getState(), state2.getState())
16
+ if (previousData !== undefined && isEqual(previousData, data)) {
17
+ return previousData
18
+ }
19
+ previousData = data
20
+ return data
21
+ })
22
+ state1.__internal.emitter.subscribe(() => {
23
+ emitter.emit()
24
+ })
25
+ state2.__internal.emitter.subscribe(() => {
26
+ emitter.emit()
27
+ })
28
+
29
+ const baseState = createBaseState<S>({
30
+ emitter,
31
+ getGetterState: () => getterState,
32
+ getState: () => selector(state1.getState(), state2.getState()),
33
+ reset() {
34
+ state1.reset()
35
+ state2.reset()
36
+ },
37
+ })
38
+
39
+ const getterState: GetterState<S> = createGetterState<S>({ baseState })
40
+ return getterState
41
+ }
package/src/select.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { createBaseState } from './create-base-state'
2
+ import { createEmitter } from './create-emitter'
3
+ import { createGetterState } from './create-getter-state'
4
+ import { isEqualBase } from './is'
5
+ import type { IsEqual, GetterState } from './types'
6
+
7
+ export function select<T, S>(
8
+ state: GetterState<T>,
9
+ selector: (value: T) => S,
10
+ isEqual: IsEqual<S> = isEqualBase,
11
+ ): GetterState<S> {
12
+ let previousData: S | undefined
13
+ const emitter = createEmitter(() => {
14
+ const data = selector(state.getState())
15
+ if (previousData !== undefined && isEqual(previousData, data)) {
16
+ return previousData
17
+ }
18
+ previousData = data
19
+ return data
20
+ })
21
+ state.__internal.emitter.subscribe(() => {
22
+ emitter.emit()
23
+ })
24
+
25
+ const baseState = createBaseState<S>({
26
+ emitter,
27
+ getGetterState: () => getterState,
28
+ getState: () => selector(state.getState()),
29
+ reset: state.reset,
30
+ })
31
+ const getterState: GetterState<S> = createGetterState<S>({ baseState })
32
+ return getterState
33
+ }