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.
- package/README.md +127 -200
- 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/src/__tests__/bench.test.tsx +3 -108
- package/src/__tests__/create.test.tsx +122 -70
- 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 +42 -73
- package/src/debug/development-tools.ts +18 -3
- package/src/index.ts +2 -1
- package/src/{utils/global-scheduler.ts → scheduler.ts} +9 -3
- package/src/select.ts +69 -0
- package/src/types.ts +56 -5
- package/src/use-value.ts +22 -0
- package/src/utils/__tests__/is.test.ts +24 -7
- package/src/utils/common.ts +35 -10
- package/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 +54 -4
- 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/src/__tests__/create-async.test.ts +0 -88
- package/src/__tests__/subscriber.test.tsx +0 -89
- package/src/__tests__/use-async.test.tsx +0 -45
- package/src/__tests__/use.test.tsx +0 -125
- package/src/subscriber.ts +0 -165
- package/src/use.ts +0 -57
- package/src/utils/__tests__/context.test.ts +0 -198
- package/src/utils/__tests__/sub-memo.test.ts +0 -13
- package/src/utils/create-context.ts +0 -60
- package/src/utils/scheduler.ts +0 -59
- package/src/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/README.md
CHANGED
|
@@ -1,80 +1,52 @@
|
|
|
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)
|
|
8
10
|
[](https://bundlephobia.com/result?p=muya)
|
|
9
11
|
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 🚀 Features
|
|
13
12
|
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
bun add muya@latest
|
|
36
25
|
```
|
|
37
|
-
|
|
38
|
-
Or using Yarn:
|
|
39
|
-
|
|
40
26
|
```bash
|
|
41
|
-
|
|
27
|
+
npm install muya@latest
|
|
42
28
|
```
|
|
43
|
-
|
|
44
|
-
Or using Bun:
|
|
45
|
-
|
|
46
29
|
```bash
|
|
47
|
-
|
|
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
|
-
###
|
|
37
|
+
### **Create and Use State**
|
|
57
38
|
|
|
58
39
|
```typescript
|
|
59
40
|
import { create } from 'muya';
|
|
60
41
|
|
|
61
|
-
const
|
|
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 =
|
|
72
|
-
|
|
46
|
+
const count = useCounter(); // Call state directly
|
|
73
47
|
return (
|
|
74
48
|
<div>
|
|
75
|
-
<button onClick={() =>
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
### Deriving / Selecting State with Functions
|
|
58
|
+
### **Select and Slice State**
|
|
89
59
|
|
|
90
|
-
|
|
60
|
+
Use `select` to derive a slice of the state:
|
|
91
61
|
|
|
92
62
|
```typescript
|
|
93
|
-
const
|
|
63
|
+
const state = create({ count: 0, value: 42 });
|
|
94
64
|
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
75
|
+
---
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
function DoubledCounter() {
|
|
104
|
-
const value = use(doubled);
|
|
77
|
+
### **Combine Multiple States**
|
|
105
78
|
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
```
|
|
79
|
+
Combine multiple states into a derived state via `select` method:
|
|
109
80
|
|
|
110
|
-
|
|
81
|
+
```typescript
|
|
82
|
+
import { create, select } from 'muya'
|
|
111
83
|
|
|
112
|
-
|
|
84
|
+
const state1 = create(1);
|
|
85
|
+
const state2 = create(2);
|
|
113
86
|
|
|
114
|
-
|
|
115
|
-
|
|
87
|
+
const sum = select([state1, state2], (s1, s2) => s1 + s2);
|
|
88
|
+
```
|
|
116
89
|
|
|
117
|
-
|
|
118
|
-
const state2 = create(0);
|
|
119
|
-
const state3 = create(0);
|
|
90
|
+
### **Equality Check**
|
|
120
91
|
|
|
121
|
-
|
|
122
|
-
return state1() + state2() + state3();
|
|
123
|
-
}
|
|
92
|
+
Customize equality checks to prevent unnecessary updates:
|
|
124
93
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
94
|
+
```typescript
|
|
95
|
+
const state = create({ a: 1, b: 2 }, (prev, next) => prev.b === next.b);
|
|
128
96
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
97
|
+
// Updates only when `b` changes
|
|
98
|
+
state.set((prev) => ({ ...prev, a: prev.a + 1 }));
|
|
99
|
+
```
|
|
132
100
|
|
|
133
|
-
|
|
134
|
-
const isOddValue = use(isOdd);
|
|
101
|
+
Or in select methods:
|
|
135
102
|
|
|
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
|
-
}
|
|
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
|
-
|
|
155
|
-
const counter2 = create(0);
|
|
110
|
+
## 🖥️ **Using State in Components**
|
|
156
111
|
|
|
157
|
-
|
|
158
|
-
return (counter1() + counter2()) * multiplier;
|
|
159
|
-
}
|
|
112
|
+
Access state directly or through `useValue` hook:
|
|
160
113
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
);
|
|
119
|
+
function App() {
|
|
120
|
+
const user = userState(); // Directly call state
|
|
121
|
+
return <p>User: {user}</p>;
|
|
181
122
|
}
|
|
182
123
|
```
|
|
183
124
|
|
|
184
|
-
###
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
import {
|
|
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
|
-
|
|
192
|
-
const
|
|
193
|
-
return
|
|
130
|
+
function App() {
|
|
131
|
+
const user = useValue(userState); // Access state via hook
|
|
132
|
+
return <p>User: {user}</p>;
|
|
194
133
|
}
|
|
134
|
+
```
|
|
195
135
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
149
|
+
### **`create`**
|
|
216
150
|
|
|
217
|
-
|
|
151
|
+
Create a new state:
|
|
218
152
|
|
|
219
153
|
```typescript
|
|
220
|
-
|
|
221
|
-
```
|
|
154
|
+
const state = create(initialValue, isEqual?);
|
|
222
155
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
165
|
+
### **`select`**
|
|
166
|
+
|
|
167
|
+
Combine or derive new states:
|
|
168
|
+
|
|
231
169
|
```typescript
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
180
|
+
### **`useValue`**
|
|
238
181
|
|
|
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.
|
|
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
|
-
|
|
185
|
+
const value = useValue(state, (s) => s.slice); // Optional selector
|
|
248
186
|
```
|
|
249
|
-
example
|
|
250
|
-
```tsx
|
|
251
|
-
const counter = create(0);
|
|
252
187
|
|
|
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
|
-
```
|
|
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
|
-
|
|
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
|
-
);
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
3
|
+
"version": "2.0.1",
|
|
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
|
}
|