muya 2.0.0-beta.3 → 2.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.
Files changed (64) hide show
  1. package/README.md +124 -195
  2. package/cjs/index.js +1 -1
  3. package/esm/create-state.js +1 -0
  4. package/esm/create.js +1 -1
  5. package/esm/debug/development-tools.js +1 -1
  6. package/esm/index.js +1 -1
  7. package/esm/scheduler.js +1 -0
  8. package/esm/select.js +1 -0
  9. package/esm/use-value.js +1 -0
  10. package/esm/utils/__tests__/is.test.js +1 -1
  11. package/esm/utils/common.js +1 -1
  12. package/esm/utils/is.js +1 -1
  13. package/package.json +12 -12
  14. package/src/__tests__/bench.test.tsx +3 -108
  15. package/src/__tests__/create.test.tsx +122 -70
  16. package/src/__tests__/scheduler.test.tsx +52 -0
  17. package/src/__tests__/select.test.tsx +127 -0
  18. package/src/__tests__/use-value.test.tsx +78 -0
  19. package/src/create-state.ts +50 -0
  20. package/src/create.ts +42 -73
  21. package/src/debug/development-tools.ts +18 -3
  22. package/src/index.ts +2 -1
  23. package/src/{utils/global-scheduler.ts → scheduler.ts} +9 -3
  24. package/src/select.ts +69 -0
  25. package/src/types.ts +57 -6
  26. package/src/use-value.ts +22 -0
  27. package/src/utils/__tests__/is.test.ts +24 -7
  28. package/src/utils/common.ts +35 -10
  29. package/src/utils/is.ts +5 -8
  30. package/types/create-state.d.ts +12 -0
  31. package/types/create.d.ts +6 -18
  32. package/types/debug/development-tools.d.ts +2 -9
  33. package/types/index.d.ts +2 -1
  34. package/types/{utils/scheduler.d.ts → scheduler.d.ts} +4 -1
  35. package/types/select.d.ts +10 -0
  36. package/types/types.d.ts +55 -5
  37. package/types/use-value.d.ts +2 -0
  38. package/types/utils/common.d.ts +6 -5
  39. package/types/utils/is.d.ts +3 -4
  40. package/esm/__tests__/create-async.test.js +0 -1
  41. package/esm/subscriber.js +0 -1
  42. package/esm/use.js +0 -1
  43. package/esm/utils/__tests__/context.test.js +0 -1
  44. package/esm/utils/__tests__/sub-memo.test.js +0 -1
  45. package/esm/utils/create-context.js +0 -1
  46. package/esm/utils/global-scheduler.js +0 -1
  47. package/esm/utils/scheduler.js +0 -1
  48. package/esm/utils/sub-memo.js +0 -1
  49. package/src/__tests__/create-async.test.ts +0 -88
  50. package/src/__tests__/subscriber.test.tsx +0 -89
  51. package/src/__tests__/use-async.test.tsx +0 -45
  52. package/src/__tests__/use.test.tsx +0 -125
  53. package/src/subscriber.ts +0 -165
  54. package/src/use.ts +0 -57
  55. package/src/utils/__tests__/context.test.ts +0 -198
  56. package/src/utils/__tests__/sub-memo.test.ts +0 -13
  57. package/src/utils/create-context.ts +0 -60
  58. package/src/utils/scheduler.ts +0 -59
  59. package/src/utils/sub-memo.ts +0 -49
  60. package/types/subscriber.d.ts +0 -25
  61. package/types/use.d.ts +0 -2
  62. package/types/utils/create-context.d.ts +0 -5
  63. package/types/utils/global-scheduler.d.ts +0 -5
  64. package/types/utils/sub-memo.d.ts +0 -7
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
 
2
- # Muya 🌀
2
+ # **Muya 🌀**
3
3
 
4
- Welcome to **Muya v2**—a state management library that makes managing state a breeze, focusing on simplicity and scalability for real-world applications.
4
+ Muya is simple and lightweight react state management library.
5
+
6
+ ---
5
7
 
6
8
  [![Build](https://github.com/samuelgja/muya/actions/workflows/build.yml/badge.svg)](https://github.com/samuelgja/muya/actions/workflows/build.yml)
7
9
  [![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)
@@ -9,72 +11,46 @@ Welcome to **Muya v2**—a state management library that makes managing state a
9
11
 
10
12
  ---
11
13
 
12
- ## 🚀 Features
13
-
14
- - **Simple State Creation**: Easily create global state with an intuitive API.
15
- - **Functional Derivations**: Derive / Compute new state using plain functions, embracing functional programming.
16
- - **Async Support**: Async derived states work out of the box, with support for React Suspense.
17
- - **Performance Optimizations**: WIP Batch state updates for optimized rendering.
18
- - **TypeScript Support**: Fully typed for a better developer experience.
19
- - **Small Bundle Size**: Lightweight and efficient—no unnecessary bloat.
20
-
21
- ---
22
-
23
- Info: [Muya v2](https://medium.com/p/106de3d04c43)
24
14
 
25
- ### 💡 How It Works
26
- Muya v2 uses its own custom context system to track dependencies and notify components when state changes. When you use a function that accesses state in your component, Muya tracks which states are used and re-renders the component when any of them change.
15
+ ## 🚀 **Key Features**
27
16
 
28
- This allows you to write derived state as plain functions, making your code more intuitive and closer to standard JavaScript. No need to define selectors or use special APIs—just use functions!
17
+ - **Simplified API**: Only `create` and `select`.
18
+ - **Batch Updates**: Built-in batching ensures efficient `muya` state updates internally.
19
+ - **TypeScript Support**: Type first.
20
+ - **Lightweight**: Minimal bundle size.
29
21
 
30
- ---
22
+ ---
31
23
 
32
- ## 📦 Installation
24
+ ## 📦 **Installation**
33
25
 
26
+ Install with your favorite package manager:
34
27
  ```bash
35
- npm install muya@2.0.0-beta.2
28
+ bun add muya@latest
36
29
  ```
37
-
38
- Or using Yarn:
39
-
40
30
  ```bash
41
- yarn add muya@2.0.0-beta.2
31
+ npm install muya@latest
42
32
  ```
43
-
44
- Or using Bun:
45
-
46
33
  ```bash
47
- bun add muya@2.0.0-beta.2
34
+ yarn add muya@latest
48
35
  ```
49
36
 
50
37
  ---
51
38
 
52
- ## 📝 Quick Start
39
+ ## 📝 **Quick Start**
53
40
 
54
- Here's how to get started with **Muya v2**:
55
-
56
- ### Creating State
41
+ ### **Create and Use State**
57
42
 
58
43
  ```typescript
59
44
  import { create } from 'muya';
60
45
 
61
46
  const counter = create(0);
62
- ```
63
-
64
- ### Using State in Components
65
-
66
- ```tsx
67
- import React from 'react';
68
- import { use } from 'muya';
69
47
 
48
+ // Access in a React component
70
49
  function Counter() {
71
- const count = use(counter);
72
-
50
+ const count = counter(); // Call state directly
73
51
  return (
74
52
  <div>
75
- <button onClick={() => counter.set((prev) => prev + 1)}>
76
- Increment
77
- </button>
53
+ <button onClick={() => counter.set((prev) => prev + 1)}>Increment</button>
78
54
  <p>Count: {count}</p>
79
55
  </div>
80
56
  );
@@ -83,217 +59,170 @@ function Counter() {
83
59
 
84
60
  ---
85
61
 
86
- ## 📚 Examples
87
-
88
- ### Deriving / Selecting State with Functions
62
+ ### **Select and Slice State**
89
63
 
90
- You can derive new state using plain functions:
64
+ Use `select` to derive a slice of the state:
91
65
 
92
66
  ```typescript
93
- const counter = create(0);
67
+ const state = create({ count: 0, value: 42 });
94
68
 
95
- function doubled() {
96
- return counter() * 2;
97
- }
98
- ```
69
+ const countSlice = state.select((s) => s.count);
99
70
 
100
- Use it in a component:
71
+ // Also async is possible, but Muya do not recommend
72
+ // It can lead to spaghetti re-renders code which is hard to maintain and debug
73
+ const asyncCountSlice = state.select(async (s) => {
74
+ const data = await fetchData();
75
+ return data.count;
76
+ });
77
+ ```
101
78
 
102
- ```tsx
103
- function DoubledCounter() {
104
- const value = use(doubled);
79
+ ---
105
80
 
106
- return <p>Doubled Count: {value}</p>;
107
- }
108
- ```
81
+ ### **Combine Multiple States**
109
82
 
110
- ### Complex Derivations
83
+ Combine multiple states into a derived state:
111
84
 
112
- Create more complex derived states by composing functions:
85
+ ```typescript
86
+ const state1 = create(1);
87
+ const state2 = create(2);
113
88
 
114
- ```tsx
115
- import { create, use } from 'muya';
89
+ const sum = select([state1, state2], (s1, s2) => s1 + s2);
90
+ ```
116
91
 
117
- const state1 = create(0);
118
- const state2 = create(0);
119
- const state3 = create(0);
92
+ ### **Equality Check**
120
93
 
121
- function sum() {
122
- return state1() + state2() + state3();
123
- }
94
+ Customize equality checks to prevent unnecessary updates:
124
95
 
125
- function multiply() {
126
- return sum() * 2; // Example multiplier
127
- }
96
+ ```typescript
97
+ const state = create({ a: 1, b: 2 }, (prev, next) => prev.b === next.b);
128
98
 
129
- function isOdd() {
130
- return multiply() % 2 === 1;
131
- }
99
+ // Updates only when `b` changes
100
+ state.set((prev) => ({ ...prev, a: prev.a + 1 }));
101
+ ```
132
102
 
133
- function App() {
134
- const isOddValue = use(isOdd);
103
+ Or in select methods:
135
104
 
136
- return (
137
- <div>
138
- <button onClick={() => state1.set((c) => c + 1)}>Increment Counter 1</button>
139
- <button onClick={() => state2.set((c) => c + 1)}>Increment Counter 2</button>
140
- <button onClick={() => state3.set((c) => c + 1)}>Increment Counter 3</button>
141
- <p>Is ODD: {isOddValue ? 'Yes' : 'No'}</p>
142
- </div>
143
- );
144
- }
105
+ ```typescript
106
+ const derived = select([state1, state2], (s1, s2) => s1 + s2, (prev, next) => prev === next);
145
107
  ```
146
108
 
109
+ ---
147
110
 
148
- ### Derive state with parameters
149
- ```tsx
150
111
 
151
- import React, { useCallback, useState } from 'react';
152
- import { create, use } from 'muya';
112
+ ## 🖥️ **Using State in Components**
153
113
 
154
- const counter1 = create(0);
155
- const counter2 = create(0);
114
+ Access state directly or through `useValue` hook:
156
115
 
157
- function multipliedSum(multiplier: number) {
158
- return (counter1() + counter2()) * multiplier;
159
- }
116
+ ### **Option 1: Access State Directly**
160
117
 
161
- function MultipliedCounter() {
162
- const [multiplier, setMultiplier] = useState(1);
163
- // make sure to use useCallback to memoize the function
164
- const multiply = useCallback(() => multipliedSum(multiplier), [multiplier]);
165
- const result = use(multiply);
118
+ ```typescript
119
+ const userState = create(0);
166
120
 
167
- return (
168
- <div>
169
- <button onClick={() => counter1.set((c) => c + 1)}>
170
- Increment Counter 1
171
- </button>
172
- <button onClick={() => counter2.set((c) => c + 1)}>
173
- Increment Counter 2
174
- </button>
175
- <button onClick={() => setMultiplier((m) => m + 1)}>
176
- Increment Multiplier
177
- </button>
178
- <p>Result: {result}</p>
179
- </div>
180
- );
121
+ function App() {
122
+ const user = userState(); // Directly call state
123
+ return <p>User: {user}</p>;
181
124
  }
182
125
  ```
183
126
 
184
- ### Async derives State
127
+ ### **Option 2: Use the Hook**
185
128
 
186
- ```tsx
187
- import { create } from 'muya';
188
-
189
- const counter = create(1);
129
+ ```typescript
130
+ import { useValue } from 'muya';
190
131
 
191
- async function fetchData() {
192
- const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${counter()}`);
193
- return response.json();
132
+ function App() {
133
+ const user = useValue(userState); // Access state via hook
134
+ return <p>User: {user}</p>;
194
135
  }
136
+ ```
195
137
 
196
- // make sure this component is wrapped in a <Suspense> component
197
- function Component() {
198
- const data = use(fetchData);
138
+ ### **Option 3: Slice with Hook**
199
139
 
200
- return (
201
- <div>
202
- <button onClick={() => counter.set((prev) => prev + 1)}>Increment</button>
203
- <p>{JSON.stringify(data)}</p>
204
- </div>
205
- );
140
+ ```typescript
141
+ function App() {
142
+ const count = useValue(state, (s) => s.count); // Use selector in hook
143
+ return <p>Count: {count}</p>;
206
144
  }
207
-
208
-
209
145
  ```
210
- ---
211
-
212
146
 
147
+ ---
213
148
 
149
+ ## 📖 **API Overview**
214
150
 
215
- ## 📖 Documentation
151
+ ### **`create`**
216
152
 
217
- #### `create`
153
+ Create a new state:
218
154
 
219
155
  ```typescript
220
- function create<T>(initialState: T): State<T>;
221
- ```
156
+ const state = create(initialValue, isEqual?);
222
157
 
223
- Create return state block or atom, setting a new value to the state will trigger a re-render of all components using the state.
224
- ```typescript
225
- const counter = create(0);
226
- // set new value
227
- counter.set(1);
158
+ // Methods:
159
+ state.get(); // Get current value
160
+ state.set(value); // Update value
161
+ state.listen(listener); // Subscribe to changes
162
+ state.select(selector, isEqual?); // Create derived state
163
+ state.destroy(); // Unsubscribe from changes, useful for dynamic state creation in components
164
+ state.withName(name); // Add a name for debugging, otherwise it will be auto generated number
228
165
  ```
229
166
 
230
- Get the state value outside of react
167
+ ### **`select`**
168
+
169
+ Combine or derive new states:
170
+
231
171
  ```typescript
232
- const value = counter();
233
- // call it like a function
234
- console.log(value()); // 1
172
+ const derived = select([state1, state2], (s1, s2) => s1 + s2);
173
+
174
+ // Methods:
175
+ derived.get(); // Get current value
176
+ derived.listen(listener); // Subscribe to changes
177
+ derived.select(selector, isEqual?); // Create nested derived state
178
+ derived.destroy(); // Unsubscribe from changes, useful for dynamic state creation in components
179
+ derived.withName(name); // Add a name for debugging, otherwise it will be auto generated number
235
180
  ```
236
181
 
237
- Each state created by create has the following properties and methods:
182
+ ### **`useValue`**
238
183
 
239
- - id: number: Unique identifier for the state.
240
- - (): T: The state is callable to get the current value.
241
- - set(value: T | ((prev: T) => T)): Update the state value.
242
- - listen(listener: (value: T) => void): () => void: Subscribe to state changes.
184
+ React hook to access state:
243
185
 
244
- #### `use` hook
245
- Use hook to get state value in react component
246
186
  ```typescript
247
- function use<T>(state: State<T> | (() => T)): T;
187
+ const value = useValue(state, (s) => s.slice); // Optional selector
248
188
  ```
249
- example
250
- ```tsx
251
- const counter = create(0);
252
189
 
253
- // in react component
254
- const count = use(counter);
255
- ```
256
-
257
- Also custom selector to avoid re-rendering the app
258
- ```tsx
259
- const doubled = create({doubled:true});
260
- // in react component
261
- const value = use(doubled, (state) => state.doubled);
262
- ```
190
+ ---
263
191
 
192
+ ## ⚠️ **Notes**
264
193
 
194
+ - **Equality Check**: Prevent unnecessary updates by passing a custom equality function to `create` or `select`.
195
+ - **Batch Updates**: Muya batches internal updates for better performance, reducing communication overhead similarly how react do.
196
+ - **Async Selectors / Derives**: Muya has support for async selectors / derives, but do not recommend to use as it can lead to spaghetti re-renders code which is hard to maintain and debug, if you want so, you can or maybe you should consider using other alternatives like `Jotai`.
265
197
 
266
198
 
267
199
 
268
- ### ⚠️ Notes
269
- Memoization: When using derived functions with parameters, you should memoize them using useCallback to prevent unnecessary re-renders and prevent function calls.
270
- ```tsx
271
- Copy code
272
- const multiply = useCallback(() => multipliedSum(multiplier), [multiplier]);
273
- ```
274
- Async Functions: Async functions used in use should handle errors appropriately and may need to use React's Suspense for loading states.
275
- Also keep in note, setting new state in async derives, will cancel the pending previous one in the queue.
276
-
277
- ```tsx
278
- Copy code
279
- function App() {
280
- return (
281
- <Suspense fallback={<div>Loading...</div>}>
282
- <DataComponent />
283
- </Suspense>
284
- );
200
+ `Muya` encourage use async updates withing sync state like this:
201
+ ```typescript
202
+ const state = create({ data: null });
203
+ async function update() {
204
+ const data = await fetchData();
205
+ state.set({ data });
285
206
  }
286
207
  ```
208
+ ---
287
209
 
288
- Error Handling: If an async function throws an error, you can catch it using React's error boundaries.
289
-
290
- ### 🤖 Contributing
291
- Contributions are welcome! Please read the contributing guidelines before submitting a pull request.
210
+ But of course you can do
292
211
 
293
- ### 🧪 Testing
294
- Muya comes with a robust testing suite. Check out the state.test.tsx for examples on how to write your own tests.
212
+ Note: Handling async updates for the state (`set`) will cancel the previous pending promise.
213
+ ```typescript
214
+ const state = create(0)
215
+ const asyncState = state.select(async (s) => {
216
+ await longPromise(100)
217
+ return s + 1
218
+ })
219
+ ```
220
+ ---
295
221
 
222
+ ### Debugging
223
+ `Muya` in dev mode automatically connects to the `redux` devtools extension if it is installed in the browser. For now devtool api is simple - state updates.
296
224
 
297
- ### 🙏 Acknowledgments
298
- Special thanks to reddit to motivate me for v2 :D
225
+ ## 🙏 **Acknowledgments**
299
226
 
227
+ This library is a fun, experimental project and not a replacement for robust state management tools. For more advanced use cases, consider libraries like `Zustand`, `Jotai`, or `Redux`.
228
+ If you enjoy `Muya`, please give it a ⭐️! :)
package/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var C=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var n in t)C(e,n,{get:t[n],enumerable:!0})},$=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of K(t))!Y.call(e,o)&&o!==n&&C(e,o,{get:()=>t[o],enumerable:!(s=X(t,o))||s.enumerable});return e};var J=e=>$(C({},"__esModule",{value:!0}),e);var ee={};W(ee,{EMPTY_SELECTOR:()=>F,create:()=>z,shallow:()=>q,use:()=>j});module.exports=J(ee);var F=e=>e;function p(e){return e instanceof Promise}function k(e){return typeof e=="function"}function P(e){return e instanceof Map}function g(e){return e instanceof Set}function R(e){return Array.isArray(e)}function b(e,t){return e===t?!0:!!Object.is(e,t)}function O(e){return typeof e=="function"}function v(e){return e instanceof DOMException&&e.name==="StateAbortError"}function A(e){return e instanceof Error&&e.name!=="StateAbortError"}function d(e){return e===void 0}function I(e,t){t&&t.abort();let n=new AbortController,{signal:s}=n;return{promise:new Promise((r,u)=>{s.addEventListener("abort",()=>{u(new DOMException("Promise was aborted","StateAbortError"))}),e.then(r).catch(u)}),controller:n}}var Q=0;function y(){return Q++}function S(e,t=(n,s)=>n===s){if(!d(e.current)){if(!d(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function h(e,t){let n=new Set,s=[];return{clear:()=>{for(let o of s)o();n.clear()},subscribe:o=>(n.add(o),()=>{n.delete(o)}),emit:(...o)=>{for(let r of n)r(...o)},contains:o=>n.has(o),getSnapshot:e,getInitialSnapshot:t,getSize:()=>n.size,subscribeToOtherEmitter(o){let r=o.subscribe(()=>{this.emit()});s.push(r)}}}var Z=Symbol("_");function D(e){let t=[];function n(){if(t.length===0)return e;let r=t.at(-1);return r===Z?e:r}function s(r,u){t.push(r);let i=u();return p(i)?(async()=>{try{return await i}finally{t.pop()}})():(t.pop(),i)}function o(r){let u=n();return()=>{t.push(u);let i=r();return p(i)?(async()=>{try{return await i}finally{t.pop()}})():(t.pop(),i)}}return{run:s,use:n,wrap:o}}function x(){let e=new Map,t=new Set,n=performance.now(),s=!1;function o(){let u=performance.now(),i=u-n,{size:c}=t;if(i<.2&&c>0&&c<10){n=u,r();return}s||(s=!0,Promise.resolve().then(()=>{s=!1,n=performance.now(),r()}))}function r(){if(t.size===0)return;let u=new Set;for(let i of t){if(e.has(i.id)){u.add(i.id);let{onResolveItem:c}=e.get(i.id);c&&c(i.value)}t.delete(i)}if(t.size>0){o();return}for(let i of u)e.get(i)?.onFinish()}return{add(u,i){return e.set(u,i),()=>{e.delete(u)}},schedule(u,i){t.add({value:i,id:u}),o()}}}var M=x(),E=D(void 0);function V(e){let t={},n=!1,s=[],o={},r=h(()=>n?t.current:(n=!0,f()),()=>(n=!0,f()));async function u(){S(t,b)&&(o.controller&&o.controller.abort(),M.schedule(i,null))}let i=y(),c=M.add(i,{onFinish(){t.current=f(),r.emit()}}),G={addEmitter(a){let l=a.subscribe(u);s.push(l)},id:i,sub:u};function H(a){let l=I(a,o.controller);return o.controller=l.controller,l.promise?.then(m=>{t.current=m,r.emit()}).catch(m=>{v(m)||(t.current=m,r.emit())}),l.promise}let f=function(){let a=E.run(G,e);if(!p(a))return t.current=a,a;let l=E.wrap(()=>H(a))();p(l)&&l.catch(()=>null);let m=l;return t.current=m,m};return f.emitter=r,f.destroy=function(){for(let a of s)a();r.clear(),c()},f.id=i,f.listen=function(a){return r.subscribe(()=>{let l=t.current;if(d(l))throw new Error("The value is undefined");a(l)})},f.abort=function(){o.controller&&o.controller.abort()},f}var L=x();function z(e,t=b){let n={};function s(){return d(n.current)&&(n.current=k(e)?e():e),n.current}function o(i){let c=s();n.current=O(i)?i(c):i}let r=function(){let i=s(),c=E.use();return c&&!r.emitter.contains(c.sub)&&c.addEmitter(r.emitter),i};r.listen=function(i){return r.emitter.subscribe(()=>{let c=n.current;if(d(c))throw new Error("The value is undefined");i(c)})},r.emitter=h(()=>r()),r.id=y();let u=L.add(r.id,{onFinish(){n.current=s(),S(n,t)&&r.emitter.emit()},onResolveItem:o});return r.set=function(i){L.schedule(r.id,i)},r.destroy=function(){n.current=void 0,s(),u(),r.emitter.clear()},r.withName=function(i){return r.stateName=i,r},r}var T=require("react");var N=require("react");var w=new WeakMap,_=0;function B(){_++}function U(e){return _=0,{call(){let t=w.get(e);if(t)return t.count++,t.returnType;B();let s={count:1,returnType:V(e)};return w.set(e,s),s.returnType},destroy(){let t=w.get(e);t&&(t.count--,t.count===0&&(t.returnType.destroy(),w.delete(e)))}}}function j(e,t=F){let n=U(e),s=n.call(),o=s.emitter.getInitialSnapshot??s.emitter.getSnapshot;(0,T.useEffect)(()=>n.destroy,[e,n.destroy]);let r=(0,N.useSyncExternalStore)(s.emitter.subscribe,()=>t(s.emitter.getSnapshot()),()=>t(o()));if((0,T.useDebugValue)(r),p(r))throw r;if(A(r))throw n.destroy(),r;return r}function q(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(P(e)&&P(t)){if(e.size!==t.size)return!1;for(let[o,r]of e)if(!Object.is(r,t.get(o)))return!1;return!0}if(g(e)&&g(t)){if(e.size!==t.size)return!1;for(let o of e)if(!t.has(o))return!1;return!0}if(R(e)&&R(t)){if(e.length!==t.length)return!1;for(let[o,r]of e.entries())if(!Object.is(r,t[o]))return!1;return!0}let n=Object.keys(e),s=Object.keys(t);if(n.length!==s.length)return!1;for(let o of n)if(!Object.prototype.hasOwnProperty.call(t,o)||!Object.is(e[o],t[o]))return!1;return!0}
1
+ "use strict";var x=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var L=(e,t)=>{for(var r in t)x(e,r,{get:t[r],enumerable:!0})},M=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of R(t))!F.call(e,i)&&i!==r&&x(e,i,{get:()=>t[i],enumerable:!(o=q(t,i))||o.enumerable});return e};var z=e=>M(x({},"__esModule",{value:!0}),e);var N={};L(N,{EMPTY_SELECTOR:()=>w,create:()=>G,select:()=>b,shallow:()=>U,useValue:()=>E});module.exports=z(N);var w=e=>e;function T(e){return e instanceof Promise}function O(e){return typeof e=="function"}function g(e){return e instanceof Map}function k(e){return e instanceof Set}function C(e){return Array.isArray(e)}function f(e,t){return e===t?!0:!!Object.is(e,t)}function V(e){return typeof e=="function"}function P(e){return e instanceof DOMException&&e.name==="StateAbortError"}function I(e){return e instanceof Error}function l(e){return e===void 0}function A(e,t){t&&t.abort();let r=new AbortController,{signal:o}=r;return{promise:new Promise((n,a)=>{o.addEventListener("abort",()=>{a(new DOMException("Promise was aborted","StateAbortError"))}),e.then(n).catch(a)}),controller:r}}function S(e,t=f){if(!l(e.current)){if(!l(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function p(e,t,r){if(!T(r))return r;e.abortController&&e.abortController.abort();let{promise:o,controller:i}=A(r,e.abortController);return e.abortController=i,o.then(n=>{e.current=n,t()}).catch(n=>{P(n)||(e.current=n,t())})}function D(){let e=new Map,t=new Set,r=performance.now(),o=!1;function i(){let a=performance.now(),s=a-r,{size:u}=t;if(s<.2&&u>0&&u<10){r=a,n();return}o||(o=!0,Promise.resolve().then(()=>{o=!1,r=performance.now(),n()}))}function n(){if(t.size===0)return;let a=new Set;for(let s of t){if(e.has(s.id)){a.add(s.id);let{onResolveItem:u}=e.get(s.id);u&&u(s.value)}t.delete(s)}if(t.size>0){i();return}for(let s of a)e.get(s)?.onFinish()}return{add(a,s){return e.set(a,s),()=>{e.delete(a)}},schedule(a,s){t.add({value:s,id:a}),i()}}}function b(e,t,r){let o={};function i(){let c=e.map(m=>m.get());return t(...c)}function n(){if(l(o.current)){let c=i();o.current=p(o,s.emitter.emit,c)}return o.current}let a=[];for(let c of e){let m=c.emitter.subscribe(()=>{d.schedule(s.id,null)});a.push(m)}let s=h({destroy(){for(let c of a)c();u(),s.emitter.clear(),o.current=void 0},get:n}),u=d.add(s.id,{onFinish(){let c=i();o.current=p(o,s.emitter.emit,c),S(o,r)&&s.emitter.emit()}});return s}var y=require("react");function E(e,t=w){let{emitter:r}=e,o=(0,y.useSyncExternalStore)(e.emitter.subscribe,()=>t(r.getSnapshot()),()=>t(r.getInitialSnapshot?r.getInitialSnapshot():r.getSnapshot()));if((0,y.useDebugValue)(o),T(o)||I(o))throw o;return o}function v(e,t){let r=new Set,o=[];return{clear:()=>{for(let i of o)i();r.clear()},subscribe:i=>(r.add(i),()=>{r.delete(i)}),emit:(...i)=>{for(let n of r)n(...i)},contains:i=>r.has(i),getSnapshot:e,getInitialSnapshot:t,getSize:()=>r.size,subscribeToOtherEmitter(i){let n=i.subscribe(()=>{this.emit()});o.push(n)}}}var H=0;function j(){return H++}function h(e){let{get:t,destroy:r,set:o}=e,i=!!o,n=function(a){return E(n,a)};return n.isSet=i,n.id=j(),n.emitter=v(t),n.destroy=r,n.listen=function(a){return this.emitter.subscribe(()=>{a(t())})},n.withName=function(a){return this.stateName=a,this},n.select=function(a,s=f){return b([n],a,s)},n.get=t,n.set=o,n}var d=D();function G(e,t=f){let r={};function o(){try{if(l(r.current)){let s=O(e)?e():e,u=p(r,n.emitter.emit,s);r.current=u}return r.current}catch(s){r.current=s}return r.current}function i(s){r.abortController&&r.abortController.abort();let u=o(),c=V(s)?s(u):s,m=p(r,n.emitter.emit,c);r.current=m}let n=h({get:o,destroy(){o(),a(),n.emitter.clear(),r.current=void 0},set(s){d.schedule(n.id,s)}}),a=d.add(n.id,{onFinish(){r.current=o(),S(r,t)&&n.emitter.emit()},onResolveItem:i});return n}function U(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(g(e)&&g(t)){if(e.size!==t.size)return!1;for(let[i,n]of e)if(!Object.is(n,t.get(i)))return!1;return!0}if(k(e)&&k(t)){if(e.size!==t.size)return!1;for(let i of e)if(!t.has(i))return!1;return!0}if(C(e)&&C(t)){if(e.length!==t.length)return!1;for(let[i,n]of e.entries())if(!Object.is(n,t[i]))return!1;return!0}let r=Object.keys(e),o=Object.keys(t);if(r.length!==o.length)return!1;for(let i of r)if(!Object.prototype.hasOwnProperty.call(t,i)||!Object.is(e[i],t[i]))return!1;return!0}
@@ -0,0 +1 @@
1
+ import{select as u}from"./select";import{useValue as l}from"./use-value";import{createEmitter as S}from"./utils/create-emitter";import{isEqualBase as c}from"./utils/is";let m=0;function d(){return m++}function G(a){const{get:r,destroy:n,set:s}=a,i=!!s,t=function(e){return l(t,e)};return t.isSet=i,t.id=d(),t.emitter=S(r),t.destroy=n,t.listen=function(e){return this.emitter.subscribe(()=>{e(r())})},t.withName=function(e){return this.stateName=e,this},t.select=function(e,o=c){return u([t],e,o)},t.get=r,t.set=s,t}export{G as createState};
package/esm/create.js CHANGED
@@ -1 +1 @@
1
- import{canUpdate as l,generateId as f}from"./utils/common";import{createEmitter as d}from"./utils/create-emitter";import{isEqualBase as T,isFunction as p,isSetValueFunction as S,isUndefined as o}from"./utils/is";import{context as h}from"./subscriber";import{createGlobalScheduler as w}from"./utils/global-scheduler";const u=w();function R(a,c=T){const r={};function i(){return o(r.current)&&(r.current=p(a)?a():a),r.current}function s(t){const n=i();r.current=S(t)?t(n):t}const e=function(){const t=i(),n=h.use();return n&&!e.emitter.contains(n.sub)&&n.addEmitter(e.emitter),t};e.listen=function(t){return e.emitter.subscribe(()=>{const n=r.current;if(o(n))throw new Error("The value is undefined");t(n)})},e.emitter=d(()=>e()),e.id=f();const m=u.add(e.id,{onFinish(){r.current=i(),l(r,c)&&e.emitter.emit()},onResolveItem:s});return e.set=function(t){u.schedule(e.id,t)},e.destroy=function(){r.current=void 0,i(),m(),e.emitter.clear()},e.withName=function(t){return e.stateName=t,e},e}export{R as create,u as createScheduler};
1
+ import{canUpdate as f,handleAsyncUpdate as u}from"./utils/common";import{isEqualBase as p,isFunction as T,isSetValueFunction as h,isUndefined as S}from"./utils/is";import{createScheduler as V}from"./scheduler";import{subscribeToDevelopmentTools as b}from"./debug/development-tools";import{createState as y}from"./create-state";const a=V();function F(n,s=p){const e={};function o(){try{if(S(e.current)){const t=T(n)?n():n,c=u(e,r.emitter.emit,t);e.current=c}return e.current}catch(t){e.current=t}return e.current}function i(t){e.abortController&&e.abortController.abort();const c=o(),m=h(t)?t(c):t,d=u(e,r.emitter.emit,m);e.current=d}const r=y({get:o,destroy(){o(),l(),r.emitter.clear(),e.current=void 0},set(t){a.schedule(r.id,t)}}),l=a.add(r.id,{onFinish(){e.current=o(),f(e,s)&&r.emitter.emit()},onResolveItem:i});return b(r),r}export{F as create,a as stateScheduler};
@@ -1 +1 @@
1
- import{isPromise as r}from"../utils/is";const t=window?.__REDUX_DEVTOOLS_EXTENSION__?.connect({name:"CustomState",trace:!0});t&&t.init({message:"Initial state"});function a(n){if(!t)return;const{message:o,type:e,value:s,name:i}=n;r(s)||t.send(i,{value:s,type:e,message:o},e)}function m(n,o){return e=>{a({name:n,type:o,value:e,message:"update"})}}export{m as developmentToolsListener,a as sendToDevelopmentTools};
1
+ import{isPromise as a,isState as p}from"../utils/is";const o=window?.__REDUX_DEVTOOLS_EXTENSION__?.connect({name:"CustomState",trace:!0});o&&o.init({message:"Initial state"});function s(e){if(!o)return;const{message:t,type:n,value:i,name:r}=e;a(i)||o.send(r,{value:i,type:n,message:t},n)}function m(e,t){return n=>{s({name:e,type:t,value:n,message:"update"})}}function l(e){}export{l as subscribeToDevelopmentTools};
package/esm/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./types";import{create as t}from"./create";import{use as m}from"./use";import{shallow as x}from"./utils/shallow";export{t as create,x as shallow,m as use};
1
+ export*from"./types";import{create as t}from"./create";import{select as m}from"./select";import{useValue as x}from"./use-value";import{shallow as a}from"./utils/shallow";export{t as create,m as select,a as shallow,x as useValue};
@@ -0,0 +1 @@
1
+ const l=.2,d=10,f=0;function a(){const o=new Map,t=new Set;let s=performance.now(),i=!1;function c(){const n=performance.now(),e=n-s,{size:r}=t;if(e<.2&&r>0&&r<10){s=n,u();return}i||(i=!0,Promise.resolve().then(()=>{i=!1,s=performance.now(),u()}))}function u(){if(t.size===0)return;const n=new Set;for(const e of t){if(o.has(e.id)){n.add(e.id);const{onResolveItem:r}=o.get(e.id);r&&r(e.value)}t.delete(e)}if(t.size>0){c();return}for(const e of n)o.get(e)?.onFinish()}return{add(n,e){return o.set(n,e),()=>{o.delete(n)}},schedule(n,e){t.add({value:e,id:n}),c()}}}export{f as RESCHEDULE_COUNT,l as THRESHOLD,d as THRESHOLD_ITEMS,a as createScheduler};
package/esm/select.js ADDED
@@ -0,0 +1 @@
1
+ import{stateScheduler as u}from"./create";import{createState as p}from"./create-state";import{subscribeToDevelopmentTools as f}from"./debug/development-tools";import{canUpdate as S,handleAsyncUpdate as s}from"./utils/common";import{isUndefined as T}from"./utils/is";function v(o,i,d){const t={};function c(){const e=o.map(r=>r.get());return i(...e)}function m(){if(T(t.current)){const e=c();t.current=s(t,n.emitter.emit,e)}return t.current}const a=[];for(const e of o){const r=e.emitter.subscribe(()=>{u.schedule(n.id,null)});a.push(r)}const n=p({destroy(){for(const e of a)e();l(),n.emitter.clear(),t.current=void 0},get:m}),l=u.add(n.id,{onFinish(){const e=c();t.current=s(t,n.emitter.emit,e),S(t,d)&&n.emitter.emit()}});return f(n),n}export{v as select};
@@ -0,0 +1 @@
1
+ import{useDebugValue as i,useSyncExternalStore as o}from"react";import{EMPTY_SELECTOR as s}from"./types";import{isError as S,isPromise as a}from"./utils/is";function f(n,r=s){const{emitter:e}=n,t=o(n.emitter.subscribe,()=>r(e.getSnapshot()),()=>r(e.getInitialSnapshot?e.getInitialSnapshot():e.getSnapshot()));if(i(t),a(t)||S(t))throw t;return t}export{f as useValue};
@@ -1 +1 @@
1
- import{Abort as a}from"../common";import{isPromise as e,isFunction as r,isSetValueFunction as t,isMap as o,isSet as s,isArray as u,isEqualBase as i,isAbortError as n}from"../is";describe("isPromise",()=>{it("should return true for a Promise",()=>{expect(e(Promise.resolve())).toBe(!0)}),it("should return false for a non-Promise",()=>{expect(e(123)).toBe(!1)})}),describe("isFunction",()=>{it("should return true for a function",()=>{expect(r(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(r(123)).toBe(!1)})}),describe("isSetValueFunction",()=>{it("should return true for a function",()=>{expect(t(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(t(123)).toBe(!1)})}),describe("isMap",()=>{it("should return true for a Map",()=>{expect(o(new Map)).toBe(!0)}),it("should return false for a non-Map",()=>{expect(o(123)).toBe(!1)})}),describe("isSet",()=>{it("should return true for a Set",()=>{expect(s(new Set)).toBe(!0)}),it("should return false for a non-Set",()=>{expect(s(123)).toBe(!1)})}),describe("isArray",()=>{it("should return true for an array",()=>{expect(u([])).toBe(!0)}),it("should return false for a non-array",()=>{expect(u(123)).toBe(!1)})}),describe("isEqualBase",()=>{it("should return true for equal values",()=>{expect(i(1,1)).toBe(!0)}),it("should return false for non-equal values",()=>{expect(i(1,2)).toBe(!1)})}),describe("isAbortError",()=>{it("should return true for an AbortError",()=>{expect(n(new DOMException("",a.Error))).toBe(!0)}),it("should return false for a non-AbortError",()=>{expect(n(new DOMException("","Error"))).toBe(!1)})});
1
+ import{create as r}from"../../create";import{isPromise as o,isFunction as s,isSetValueFunction as u,isMap as i,isSet as n,isArray as a,isEqualBase as f,isUndefined as l,isState as e}from"../is";describe("isPromise",()=>{it("should return true for a Promise",()=>{expect(o(Promise.resolve())).toBe(!0)}),it("should return false for a non-Promise",()=>{expect(o(123)).toBe(!1)})}),describe("isFunction",()=>{it("should return true for a function",()=>{expect(s(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(s(123)).toBe(!1)})}),describe("isSetValueFunction",()=>{it("should return true for a function",()=>{expect(u(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(u(123)).toBe(!1)})}),describe("isMap",()=>{it("should return true for a Map",()=>{expect(i(new Map)).toBe(!0)}),it("should return false for a non-Map",()=>{expect(i(123)).toBe(!1)})}),describe("isSet",()=>{it("should return true for a Set",()=>{expect(n(new Set)).toBe(!0)}),it("should return false for a non-Set",()=>{expect(n(123)).toBe(!1)})}),describe("isArray",()=>{it("should return true for an array",()=>{expect(a([])).toBe(!0)}),it("should return false for a non-array",()=>{expect(a(123)).toBe(!1)})}),describe("isEqualBase",()=>{it("should return true for equal values",()=>{expect(f(1,1)).toBe(!0)}),it("should return false for non-equal values",()=>{expect(f(1,2)).toBe(!1)})}),describe("isUndefined",()=>{it("should return true for undefined",()=>{expect(l(void 0)).toBe(!0)}),it("should return false for a non-undefined",()=>{expect(l(123)).toBe(!1)})}),describe("isState",()=>{it("should return true for a State real",()=>{const t=r(1);expect(e(t)).toBe(!0)}),it("should return true for a State with derived",()=>{const d=r(1).select(c=>c);expect(e(d)).toBe(!1)}),it("should return false for a non-State",()=>{expect(e(123)).toBe(!1)})});
@@ -1 +1 @@
1
- import{isUndefined as i}from"./is";var a=(e=>(e.Error="StateAbortError",e))(a||{});function m(r,e){e&&e.abort();const o=new AbortController,{signal:t}=o;return{promise:new Promise((l,n)=>{t.addEventListener("abort",()=>{n(new DOMException("Promise was aborted","StateAbortError"))}),r.then(l).catch(n)}),controller:o}}let s=0;function p(){return s++}function b(r,e=(o,t)=>o===t){if(!i(r.current)){if(!i(r.previous)&&e(r.current,r.previous))return!1;r.previous=r.current}return!0}export{a as Abort,b as canUpdate,m as cancelablePromise,p as generateId};
1
+ import{isAbortError as a,isEqualBase as b,isPromise as u,isUndefined as s}from"./is";var p=(o=>(o.Error="StateAbortError",o))(p||{});function T(r,o){o&&o.abort();const e=new AbortController,{signal:n}=e;return{promise:new Promise((t,l)=>{n.addEventListener("abort",()=>{l(new DOMException("Promise was aborted","StateAbortError"))}),r.then(t).catch(l)}),controller:e}}function f(r,o=b){if(!s(r.current)){if(!s(r.previous)&&o(r.current,r.previous))return!1;r.previous=r.current}return!0}function c(r,o,e){if(!u(e))return e;r.abortController&&r.abortController.abort();const{promise:n,controller:i}=T(e,r.abortController);return r.abortController=i,n.then(t=>{r.current=t,o()}).catch(t=>{a(t)||(r.current=t,o())})}export{p as Abort,f as canUpdate,c as handleAsyncUpdate};
package/esm/utils/is.js CHANGED
@@ -1 +1 @@
1
- import{Abort as t}from"./common";function i(n){return n instanceof Promise}function r(n){return typeof n=="function"}function u(n){return n instanceof Map}function s(n){return n instanceof Set}function a(n){return Array.isArray(n)}function f(n,e){return n===e?!0:!!Object.is(n,e)}function c(n){return typeof n=="function"}function p(n){return n instanceof DOMException&&n.name===t.Error}function k(n){return n instanceof Error&&n.name!==t.Error}function w(n){return n===void 0}function x(n){return r(n)&&n.set!==void 0}export{p as isAbortError,k as isAnyOtherError,a as isArray,x as isCreate,f as isEqualBase,r as isFunction,u as isMap,i as isPromise,s as isSet,c as isSetValueFunction,w as isUndefined};
1
+ import{Abort as e}from"./common";function i(n){return n instanceof Promise}function r(n){return typeof n=="function"}function u(n){return n instanceof Map}function s(n){return n instanceof Set}function a(n){return Array.isArray(n)}function f(n,t){return n===t?!0:!!Object.is(n,t)}function c(n){return typeof n=="function"}function p(n){return n instanceof DOMException&&n.name===e.Error}function k(n){return n instanceof Error}function w(n){return n===void 0}function S(n){return r(n)&&"get"in n&&"set"in n&&"isSet"in n&&n.isSet===!0}export{p as isAbortError,a as isArray,f as isEqualBase,k as isError,r as isFunction,u as isMap,i as isPromise,s as isSet,c as isSetValueFunction,S as isState,w as isUndefined};
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "muya",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0",
4
4
  "author": "samuel.gjabel@gmail.com",
5
- "description": "👀 Another React state management library",
6
- "license": "MIT",
5
+ "repository": "https://github.com/samuelgjabel/muya",
7
6
  "main": "cjs/index.js",
8
7
  "module": "esm/index.js",
9
- "react-native": "src/index.ts",
10
- "types": "types/index.d.ts",
8
+ "peerDependencies": {
9
+ "use-sync-external-store": ">=1.2.0",
10
+ "react": ">=18.0.0"
11
+ },
12
+ "description": "👀 Another React state management library",
11
13
  "homepage": "https://github.com/samuelgjabel/muya",
12
- "repository": "https://github.com/samuelgjabel/muya",
13
- "sideEffects": false,
14
14
  "keywords": [
15
15
  "react",
16
16
  "react-hooks",
@@ -22,10 +22,7 @@
22
22
  "redux",
23
23
  "zustand"
24
24
  ],
25
- "peerDependencies": {
26
- "use-sync-external-store": ">=1.2.0",
27
- "react": ">=18.0.0"
28
- },
25
+ "license": "MIT",
29
26
  "peerDependenciesMeta": {
30
27
  "react": {
31
28
  "optional": true
@@ -33,5 +30,8 @@
33
30
  "use-sync-external-store": {
34
31
  "optional": true
35
32
  }
36
- }
33
+ },
34
+ "react-native": "src/index.ts",
35
+ "sideEffects": false,
36
+ "types": "types/index.d.ts"
37
37
  }