muya 2.0.0-beta.3 → 2.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.
Files changed (64) hide show
  1. package/README.md +127 -200
  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 +56 -5
  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 +54 -4
  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,80 +1,52 @@
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)
8
10
  [![Build Size](https://img.shields.io/bundlephobia/minzip/muya?label=Bundle%20size)](https://bundlephobia.com/result?p=muya)
9
11
 
10
- ---
11
-
12
- ## 🚀 Features
13
12
 
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.
13
+ - **Simplified API**: Only `create` and `select`.
14
+ - **Batch Updates**: Built-in batching ensures efficient `muya` state updates internally.
15
+ - **TypeScript Support**: Type first.
16
+ - **Lightweight**: Minimal bundle size.
20
17
 
21
18
  ---
22
19
 
23
- Info: [Muya v2](https://medium.com/p/106de3d04c43)
24
-
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.
27
-
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!
29
-
30
- ---
31
-
32
- ## 📦 Installation
20
+ ## 📦 **Installation**
33
21
 
22
+ Install with your favorite package manager:
34
23
  ```bash
35
- npm install muya@2.0.0-beta.2
24
+ bun add muya@latest
36
25
  ```
37
-
38
- Or using Yarn:
39
-
40
26
  ```bash
41
- yarn add muya@2.0.0-beta.2
27
+ npm install muya@latest
42
28
  ```
43
-
44
- Or using Bun:
45
-
46
29
  ```bash
47
- bun add muya@2.0.0-beta.2
30
+ yarn add muya@latest
48
31
  ```
49
32
 
50
33
  ---
51
34
 
52
- ## 📝 Quick Start
53
-
54
- Here's how to get started with **Muya v2**:
35
+ ## 📝 **Quick Start**
55
36
 
56
- ### Creating State
37
+ ### **Create and Use State**
57
38
 
58
39
  ```typescript
59
40
  import { create } from 'muya';
60
41
 
61
- const counter = create(0);
62
- ```
63
-
64
- ### Using State in Components
65
-
66
- ```tsx
67
- import React from 'react';
68
- import { use } from 'muya';
42
+ const useCounter = create(0);
69
43
 
44
+ // Access in a React component
70
45
  function Counter() {
71
- const count = use(counter);
72
-
46
+ const count = useCounter(); // Call state directly
73
47
  return (
74
48
  <div>
75
- <button onClick={() => counter.set((prev) => prev + 1)}>
76
- Increment
77
- </button>
49
+ <button onClick={() => useCounter.set((prev) => prev + 1)}>Increment</button>
78
50
  <p>Count: {count}</p>
79
51
  </div>
80
52
  );
@@ -83,217 +55,172 @@ function Counter() {
83
55
 
84
56
  ---
85
57
 
86
- ## 📚 Examples
87
-
88
- ### Deriving / Selecting State with Functions
58
+ ### **Select and Slice State**
89
59
 
90
- You can derive new state using plain functions:
60
+ Use `select` to derive a slice of the state:
91
61
 
92
62
  ```typescript
93
- const counter = create(0);
63
+ const state = create({ count: 0, value: 42 });
94
64
 
95
- function doubled() {
96
- return counter() * 2;
97
- }
65
+ const countSlice = state.select((s) => s.count);
66
+
67
+ // Also async is possible, but Muya do not recommend
68
+ // It can lead to spaghetti re-renders code which is hard to maintain and debug
69
+ const asyncCountSlice = state.select(async (s) => {
70
+ const data = await fetchData();
71
+ return data.count;
72
+ });
98
73
  ```
99
74
 
100
- Use it in a component:
75
+ ---
101
76
 
102
- ```tsx
103
- function DoubledCounter() {
104
- const value = use(doubled);
77
+ ### **Combine Multiple States**
105
78
 
106
- return <p>Doubled Count: {value}</p>;
107
- }
108
- ```
79
+ Combine multiple states into a derived state via `select` method:
109
80
 
110
- ### Complex Derivations
81
+ ```typescript
82
+ import { create, select } from 'muya'
111
83
 
112
- Create more complex derived states by composing functions:
84
+ const state1 = create(1);
85
+ const state2 = create(2);
113
86
 
114
- ```tsx
115
- import { create, use } from 'muya';
87
+ const sum = select([state1, state2], (s1, s2) => s1 + s2);
88
+ ```
116
89
 
117
- const state1 = create(0);
118
- const state2 = create(0);
119
- const state3 = create(0);
90
+ ### **Equality Check**
120
91
 
121
- function sum() {
122
- return state1() + state2() + state3();
123
- }
92
+ Customize equality checks to prevent unnecessary updates:
124
93
 
125
- function multiply() {
126
- return sum() * 2; // Example multiplier
127
- }
94
+ ```typescript
95
+ const state = create({ a: 1, b: 2 }, (prev, next) => prev.b === next.b);
128
96
 
129
- function isOdd() {
130
- return multiply() % 2 === 1;
131
- }
97
+ // Updates only when `b` changes
98
+ state.set((prev) => ({ ...prev, a: prev.a + 1 }));
99
+ ```
132
100
 
133
- function App() {
134
- const isOddValue = use(isOdd);
101
+ Or in select methods:
135
102
 
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
- }
103
+ ```typescript
104
+ const derived = select([state1, state2], (s1, s2) => s1 + s2, (prev, next) => prev === next);
145
105
  ```
146
106
 
107
+ ---
147
108
 
148
- ### Derive state with parameters
149
- ```tsx
150
-
151
- import React, { useCallback, useState } from 'react';
152
- import { create, use } from 'muya';
153
109
 
154
- const counter1 = create(0);
155
- const counter2 = create(0);
110
+ ## 🖥️ **Using State in Components**
156
111
 
157
- function multipliedSum(multiplier: number) {
158
- return (counter1() + counter2()) * multiplier;
159
- }
112
+ Access state directly or through `useValue` hook:
160
113
 
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);
114
+ ### **Option 1: Access State Directly**
115
+ Each state can be called as the hook directly
116
+ ```typescript
117
+ const userState = create(0);
166
118
 
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
- );
119
+ function App() {
120
+ const user = userState(); // Directly call state
121
+ return <p>User: {user}</p>;
181
122
  }
182
123
  ```
183
124
 
184
- ### Async derives State
185
-
186
- ```tsx
187
- import { create } from 'muya';
188
-
189
- const counter = create(1);
125
+ ### **Option 2: Use the Hook**
126
+ Or for convenience, there is `useValue` method
127
+ ```typescript
128
+ import { useValue } from 'muya';
190
129
 
191
- async function fetchData() {
192
- const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${counter()}`);
193
- return response.json();
130
+ function App() {
131
+ const user = useValue(userState); // Access state via hook
132
+ return <p>User: {user}</p>;
194
133
  }
134
+ ```
195
135
 
196
- // make sure this component is wrapped in a <Suspense> component
197
- function Component() {
198
- const data = use(fetchData);
199
-
200
- return (
201
- <div>
202
- <button onClick={() => counter.set((prev) => prev + 1)}>Increment</button>
203
- <p>{JSON.stringify(data)}</p>
204
- </div>
205
- );
136
+ ### **Option 3: Slice with Hook**
137
+ For efficient re-renders, `useValue` provides a slicing method.
138
+ ```typescript
139
+ function App() {
140
+ const count = useValue(state, (s) => s.count); // Use selector in hook
141
+ return <p>Count: {count}</p>;
206
142
  }
207
-
208
-
209
143
  ```
210
- ---
211
-
212
144
 
145
+ ---
213
146
 
147
+ ## 📖 **API Overview**
214
148
 
215
- ## 📖 Documentation
149
+ ### **`create`**
216
150
 
217
- #### `create`
151
+ Create a new state:
218
152
 
219
153
  ```typescript
220
- function create<T>(initialState: T): State<T>;
221
- ```
154
+ const state = create(initialValue, isEqual?);
222
155
 
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);
156
+ // Methods:
157
+ state.get(); // Get current value
158
+ state.set(value); // Update value
159
+ state.listen(listener); // Subscribe to changes
160
+ state.select(selector, isEqual?); // Create derived state
161
+ state.destroy(); // Unsubscribe from changes, useful for dynamic state creation in components
162
+ state.withName(name); // Add a name for debugging, otherwise it will be auto generated number
228
163
  ```
229
164
 
230
- Get the state value outside of react
165
+ ### **`select`**
166
+
167
+ Combine or derive new states:
168
+
231
169
  ```typescript
232
- const value = counter();
233
- // call it like a function
234
- console.log(value()); // 1
170
+ const derived = select([state1, state2], (s1, s2) => s1 + s2);
171
+
172
+ // Methods:
173
+ derived.get(); // Get current value
174
+ derived.listen(listener); // Subscribe to changes
175
+ derived.select(selector, isEqual?); // Create nested derived state
176
+ derived.destroy(); // Unsubscribe from changes, useful for dynamic state creation in components
177
+ derived.withName(name); // Add a name for debugging, otherwise it will be auto generated number
235
178
  ```
236
179
 
237
- Each state created by create has the following properties and methods:
180
+ ### **`useValue`**
238
181
 
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.
182
+ React hook to access state:
243
183
 
244
- #### `use` hook
245
- Use hook to get state value in react component
246
184
  ```typescript
247
- function use<T>(state: State<T> | (() => T)): T;
185
+ const value = useValue(state, (s) => s.slice); // Optional selector
248
186
  ```
249
- example
250
- ```tsx
251
- const counter = create(0);
252
187
 
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
- ```
188
+ ---
263
189
 
190
+ ## ⚠️ **Notes**
264
191
 
192
+ - **Equality Check**: Prevent unnecessary updates by passing a custom equality function to `create` or `select`.
193
+ - **Batch Updates**: Muya batches internal updates for better performance, reducing communication overhead similarly how react do.
194
+ - **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
195
 
266
196
 
267
197
 
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
- );
198
+ `Muya` encourage use async updates withing sync state like this:
199
+ ```typescript
200
+ const state = create({ data: null });
201
+ async function update() {
202
+ const data = await fetchData();
203
+ state.set({ data });
285
204
  }
286
205
  ```
206
+ ---
287
207
 
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.
208
+ But of course you can do
292
209
 
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.
210
+ Note: Handling async updates for the state (`set`) will cancel the previous pending promise.
211
+ ```typescript
212
+ const state = create(0)
213
+ const asyncState = state.select(async (s) => {
214
+ await longPromise(100)
215
+ return s + 1
216
+ })
217
+ ```
218
+ ---
295
219
 
220
+ ### Debugging
221
+ `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
222
 
297
- ### 🙏 Acknowledgments
298
- Special thanks to reddit to motivate me for v2 :D
223
+ ## 🙏 **Acknowledgments**
299
224
 
225
+ 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`.
226
+ 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.1",
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
  }