muya 2.0.0-beta.2 → 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.
- package/README.md +124 -195
- package/cjs/index.js +1 -1
- package/esm/create-state.js +1 -0
- package/esm/create.js +1 -1
- package/esm/debug/development-tools.js +1 -1
- package/esm/index.js +1 -1
- package/esm/scheduler.js +1 -0
- package/esm/select.js +1 -0
- package/esm/use-value.js +1 -0
- package/esm/utils/__tests__/is.test.js +1 -1
- package/esm/utils/common.js +1 -1
- package/esm/utils/is.js +1 -1
- package/package.json +12 -12
- package/{packages/core → src}/__tests__/bench.test.tsx +3 -108
- package/src/__tests__/create.test.tsx +159 -0
- package/src/__tests__/scheduler.test.tsx +52 -0
- package/src/__tests__/select.test.tsx +127 -0
- package/src/__tests__/use-value.test.tsx +78 -0
- package/src/create-state.ts +50 -0
- package/src/create.ts +67 -0
- package/{packages/core → src}/debug/development-tools.ts +18 -3
- package/{packages/core → src}/index.ts +2 -1
- package/{packages/core/utils/global-scheduler.ts → src/scheduler.ts} +9 -3
- package/src/select.ts +69 -0
- package/src/types.ts +66 -0
- package/src/use-value.ts +22 -0
- package/{packages/core → src}/utils/__tests__/is.test.ts +24 -7
- package/{packages/core → src}/utils/common.ts +35 -10
- package/{packages/core → src}/utils/is.ts +5 -8
- package/types/create-state.d.ts +12 -0
- package/types/create.d.ts +6 -18
- package/types/debug/development-tools.d.ts +2 -9
- package/types/index.d.ts +2 -1
- package/types/{utils/scheduler.d.ts → scheduler.d.ts} +4 -1
- package/types/select.d.ts +10 -0
- package/types/types.d.ts +55 -5
- package/types/use-value.d.ts +2 -0
- package/types/utils/common.d.ts +6 -5
- package/types/utils/is.d.ts +3 -4
- package/esm/__tests__/create-async.test.js +0 -1
- package/esm/subscriber.js +0 -1
- package/esm/use.js +0 -1
- package/esm/utils/__tests__/context.test.js +0 -1
- package/esm/utils/__tests__/sub-memo.test.js +0 -1
- package/esm/utils/create-context.js +0 -1
- package/esm/utils/global-scheduler.js +0 -1
- package/esm/utils/scheduler.js +0 -1
- package/esm/utils/sub-memo.js +0 -1
- package/packages/core/__tests__/create-async.test.ts +0 -88
- package/packages/core/__tests__/create.test.tsx +0 -107
- package/packages/core/__tests__/subscriber.test.tsx +0 -89
- package/packages/core/__tests__/use-async.test.tsx +0 -45
- package/packages/core/__tests__/use.test.tsx +0 -125
- package/packages/core/create.ts +0 -98
- package/packages/core/subscriber.ts +0 -165
- package/packages/core/types.ts +0 -15
- package/packages/core/use.ts +0 -57
- package/packages/core/utils/__tests__/context.test.ts +0 -198
- package/packages/core/utils/__tests__/sub-memo.test.ts +0 -13
- package/packages/core/utils/create-context.ts +0 -60
- package/packages/core/utils/scheduler.ts +0 -59
- package/packages/core/utils/sub-memo.ts +0 -49
- package/types/subscriber.d.ts +0 -25
- package/types/use.d.ts +0 -2
- package/types/utils/create-context.d.ts +0 -5
- package/types/utils/global-scheduler.d.ts +0 -5
- package/types/utils/sub-memo.d.ts +0 -7
- /package/{packages/core → src}/__tests__/test-utils.ts +0 -0
- /package/{packages/core → src}/utils/__tests__/shallow.test.ts +0 -0
- /package/{packages/core → src}/utils/create-emitter.ts +0 -0
- /package/{packages/core → src}/utils/shallow.ts +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
# Muya
|
|
2
|
+
# **Muya 🌀**
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Muya is simple and lightweight react state management library.
|
|
5
|
+
|
|
6
|
+
---
|
|
5
7
|
|
|
6
8
|
[](https://github.com/samuelgja/muya/actions/workflows/build.yml)
|
|
7
9
|
[](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
+
bun add muya@latest
|
|
36
29
|
```
|
|
37
|
-
|
|
38
|
-
Or using Yarn:
|
|
39
|
-
|
|
40
30
|
```bash
|
|
41
|
-
|
|
31
|
+
npm install muya@latest
|
|
42
32
|
```
|
|
43
|
-
|
|
44
|
-
Or using Bun:
|
|
45
|
-
|
|
46
33
|
```bash
|
|
47
|
-
|
|
34
|
+
yarn add muya@latest
|
|
48
35
|
```
|
|
49
36
|
|
|
50
37
|
---
|
|
51
38
|
|
|
52
|
-
## 📝 Quick Start
|
|
39
|
+
## 📝 **Quick Start**
|
|
53
40
|
|
|
54
|
-
|
|
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 =
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
### Deriving / Selecting State with Functions
|
|
62
|
+
### **Select and Slice State**
|
|
89
63
|
|
|
90
|
-
|
|
64
|
+
Use `select` to derive a slice of the state:
|
|
91
65
|
|
|
92
66
|
```typescript
|
|
93
|
-
const
|
|
67
|
+
const state = create({ count: 0, value: 42 });
|
|
94
68
|
|
|
95
|
-
|
|
96
|
-
return counter() * 2;
|
|
97
|
-
}
|
|
98
|
-
```
|
|
69
|
+
const countSlice = state.select((s) => s.count);
|
|
99
70
|
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
function DoubledCounter() {
|
|
104
|
-
const value = use(doubled);
|
|
79
|
+
---
|
|
105
80
|
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
```
|
|
81
|
+
### **Combine Multiple States**
|
|
109
82
|
|
|
110
|
-
|
|
83
|
+
Combine multiple states into a derived state:
|
|
111
84
|
|
|
112
|
-
|
|
85
|
+
```typescript
|
|
86
|
+
const state1 = create(1);
|
|
87
|
+
const state2 = create(2);
|
|
113
88
|
|
|
114
|
-
|
|
115
|
-
|
|
89
|
+
const sum = select([state1, state2], (s1, s2) => s1 + s2);
|
|
90
|
+
```
|
|
116
91
|
|
|
117
|
-
|
|
118
|
-
const state2 = create(0);
|
|
119
|
-
const state3 = create(0);
|
|
92
|
+
### **Equality Check**
|
|
120
93
|
|
|
121
|
-
|
|
122
|
-
return state1() + state2() + state3();
|
|
123
|
-
}
|
|
94
|
+
Customize equality checks to prevent unnecessary updates:
|
|
124
95
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
96
|
+
```typescript
|
|
97
|
+
const state = create({ a: 1, b: 2 }, (prev, next) => prev.b === next.b);
|
|
128
98
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
99
|
+
// Updates only when `b` changes
|
|
100
|
+
state.set((prev) => ({ ...prev, a: prev.a + 1 }));
|
|
101
|
+
```
|
|
132
102
|
|
|
133
|
-
|
|
134
|
-
const isOddValue = use(isOdd);
|
|
103
|
+
Or in select methods:
|
|
135
104
|
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
152
|
-
import { create, use } from 'muya';
|
|
112
|
+
## 🖥️ **Using State in Components**
|
|
153
113
|
|
|
154
|
-
|
|
155
|
-
const counter2 = create(0);
|
|
114
|
+
Access state directly or through `useValue` hook:
|
|
156
115
|
|
|
157
|
-
|
|
158
|
-
return (counter1() + counter2()) * multiplier;
|
|
159
|
-
}
|
|
116
|
+
### **Option 1: Access State Directly**
|
|
160
117
|
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
###
|
|
127
|
+
### **Option 2: Use the Hook**
|
|
185
128
|
|
|
186
|
-
```
|
|
187
|
-
import {
|
|
188
|
-
|
|
189
|
-
const counter = create(1);
|
|
129
|
+
```typescript
|
|
130
|
+
import { useValue } from 'muya';
|
|
190
131
|
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
return
|
|
132
|
+
function App() {
|
|
133
|
+
const user = useValue(userState); // Access state via hook
|
|
134
|
+
return <p>User: {user}</p>;
|
|
194
135
|
}
|
|
136
|
+
```
|
|
195
137
|
|
|
196
|
-
|
|
197
|
-
function Component() {
|
|
198
|
-
const data = use(fetchData);
|
|
138
|
+
### **Option 3: Slice with Hook**
|
|
199
139
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
151
|
+
### **`create`**
|
|
216
152
|
|
|
217
|
-
|
|
153
|
+
Create a new state:
|
|
218
154
|
|
|
219
155
|
```typescript
|
|
220
|
-
|
|
221
|
-
```
|
|
156
|
+
const state = create(initialValue, isEqual?);
|
|
222
157
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
167
|
+
### **`select`**
|
|
168
|
+
|
|
169
|
+
Combine or derive new states:
|
|
170
|
+
|
|
231
171
|
```typescript
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
182
|
+
### **`useValue`**
|
|
238
183
|
|
|
239
|
-
|
|
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
|
-
|
|
187
|
+
const value = useValue(state, (s) => s.slice); // Optional selector
|
|
248
188
|
```
|
|
249
|
-
example
|
|
250
|
-
```tsx
|
|
251
|
-
const counter = create(0);
|
|
252
189
|
|
|
253
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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{
|
|
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};
|
package/esm/scheduler.js
ADDED
|
@@ -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};
|
package/esm/use-value.js
ADDED
|
@@ -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{
|
|
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)})});
|
package/esm/utils/common.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isUndefined as
|
|
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
|
|
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
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"author": "samuel.gjabel@gmail.com",
|
|
5
|
-
"
|
|
6
|
-
"license": "MIT",
|
|
5
|
+
"repository": "https://github.com/samuelgjabel/muya",
|
|
7
6
|
"main": "cjs/index.js",
|
|
8
7
|
"module": "esm/index.js",
|
|
9
|
-
"
|
|
10
|
-
|
|
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
|
-
"
|
|
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
|
}
|