stunk 0.7.0 → 0.8.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 +125 -126
- package/dist/core/asyncChunk.js +1 -0
- package/package.json +1 -1
- package/src/core/asyncChunk.ts +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Stunk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
---
|
|
3
|
+
A lightweight, framework-agnostic state management library using atomic state principles. Stunk breaks down state into manageable "chunks" for easy updates and subscriptions.
|
|
6
4
|
|
|
7
5
|
## Pronunciation and Meaning
|
|
8
6
|
|
|
@@ -15,64 +13,61 @@ Think of your application's state as a big jar of data. In traditional state man
|
|
|
15
13
|
|
|
16
14
|
**Stunk** is like dividing your jar into many smaller containers, each holding a single piece of state. These smaller containers are called **chunks**. Each **chunk** can be updated and accessed easily, and any part of your app can subscribe to changes in a chunk so it gets updated automatically.
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
## Features
|
|
19
17
|
|
|
20
|
-
-
|
|
21
|
-
- granular control over state updates and subscriptions.
|
|
22
|
-
- modular and composable state architecture.
|
|
18
|
+
<!-- - 🎯 Framework agnostic -->
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
- 🔄 Reactive updates with efficient subscription system
|
|
21
|
+
- 🎯 Granular state selection
|
|
22
|
+
- ⏳ Built-in undo/redo/getHistory/clearHistory
|
|
23
|
+
- 🔄 Batch updates for performance and nested batch updates
|
|
24
|
+
- 🛠️ Extensible middleware
|
|
25
|
+
- 🔍 Full TypeScript support
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
27
28
|
|
|
28
|
-
You can install **Stunk** from NPM:
|
|
29
|
-
|
|
30
29
|
```bash
|
|
31
30
|
npm install stunk
|
|
32
31
|
```
|
|
33
32
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
### 1.0 **Chunks**
|
|
37
|
-
|
|
38
|
-
A **chunk** is a small container of state. It holds a value, and you can do three things with it:
|
|
39
|
-
|
|
40
|
-
- **Get** the current value of the chunk
|
|
41
|
-
- **Set** a new value for the chunk
|
|
42
|
-
- **Subscribe** to the chunk to get notified whenever the value changes
|
|
33
|
+
## Quick Start
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
```typescript
|
|
36
|
+
import { chunk, select, batch } from "stunk";
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const count = chunk(0);
|
|
38
|
+
// Create a state chunk
|
|
39
|
+
const counter = chunk(0);
|
|
40
|
+
const userChunk = chunk({ name: "Olamide", age: 26 });
|
|
50
41
|
|
|
51
|
-
|
|
42
|
+
// Select specific state - Selector
|
|
43
|
+
const nameSelector = select(userChunk, (user) => user.name);
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
// Subscribe to changes
|
|
46
|
+
nameSelector.subscribe((name) => console.log("Name:", name));
|
|
47
|
+
counter.subscribe((count) => console.log("Counter", counter));
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
// Batch updates
|
|
50
|
+
batch(() => {
|
|
51
|
+
userChunk.set({ name: "Olalekan", age: 27 }); // Doesn't log yet
|
|
52
|
+
counter.set(5); // Doesn't log yet
|
|
53
|
+
}); // All logs happen here at once
|
|
56
54
|
```
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
You can **subscribe** to a **chunk**. This means you get notified whenever the value inside the chunk changes. This is super useful for updating your app automatically when **state** changes.
|
|
56
|
+
## Core Concepts
|
|
61
57
|
|
|
62
|
-
###
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
const count = chunk(0);
|
|
66
|
-
const callback = (newValue: number) => console.log("Updated value:", newValue);
|
|
58
|
+
### Chunks
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
Basic unit of state with get/set/subscribe functionality:
|
|
69
61
|
|
|
70
|
-
|
|
62
|
+
```typescript
|
|
63
|
+
const counter = chunk(0);
|
|
64
|
+
counter.subscribe((value) => console.log(value));
|
|
65
|
+
counter.set(5);
|
|
71
66
|
```
|
|
72
67
|
|
|
73
|
-
|
|
68
|
+
## Unsubscribing
|
|
74
69
|
|
|
75
|
-
You can **unsubscribe** from a **chunk**, which means you stop getting notifications when the **value changes**. You can do this by calling the function that's returned when you **subscribe..**
|
|
70
|
+
You can **unsubscribe** from a **chunk**, which means you stop getting notifications when the **value changes**. You can do this by calling the function that's returned when you **subscribe..**
|
|
76
71
|
|
|
77
72
|
### Usage
|
|
78
73
|
|
|
@@ -89,13 +84,11 @@ unsubscribe(); // Unsubscribe
|
|
|
89
84
|
count.set(20); // Nothing will happen now, because you unsubscribed
|
|
90
85
|
```
|
|
91
86
|
|
|
92
|
-
###
|
|
87
|
+
### Deriving New Chunks
|
|
93
88
|
|
|
94
89
|
With Stunk, you can create **derived chunks**. This means you can create a new **chunk** based on the value of another **chunk**. When the original **chunk** changes, the **derived chunk** will automatically update.
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```ts
|
|
91
|
+
```typescript
|
|
99
92
|
const count = chunk(5);
|
|
100
93
|
|
|
101
94
|
// Create a derived chunk that doubles the count
|
|
@@ -110,81 +103,41 @@ count.set(10);
|
|
|
110
103
|
// "Double count: 20"
|
|
111
104
|
```
|
|
112
105
|
|
|
113
|
-
###
|
|
114
|
-
|
|
115
|
-
Batch updates allow you to group multiple **state changes** together and notify **subscribers** only once at the end of the **batch**. This is particularly useful for **optimizing performance** when you need to **update multiple** chunks at the same time.
|
|
116
|
-
|
|
117
|
-
### Usage
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
import { chunk, batch } from "stunk";
|
|
121
|
-
|
|
122
|
-
const firstName = chunk("John");
|
|
123
|
-
const lastName = chunk("Doe");
|
|
124
|
-
const age = chunk(25);
|
|
106
|
+
### Batch Updates
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
firstName.subscribe((name) => console.log("First name changed:", name));
|
|
128
|
-
lastName.subscribe((name) => console.log("Last name changed:", name));
|
|
129
|
-
age.subscribe((age) => console.log("Age changed:", age));
|
|
108
|
+
Group multiple updates:
|
|
130
109
|
|
|
131
|
-
|
|
132
|
-
firstName.set("Jane"); // Logs immediately
|
|
133
|
-
lastName.set("Smith"); // Logs immediately
|
|
134
|
-
age.set(26); // Logs immediately
|
|
135
|
-
|
|
136
|
-
// With batch - triggers only one update per chunk at the end
|
|
110
|
+
```typescript
|
|
137
111
|
batch(() => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}); // All logs happen here at once
|
|
112
|
+
chunk1.set(newValue1);
|
|
113
|
+
chunk2.set(newValue2);
|
|
114
|
+
}); // Single notification
|
|
142
115
|
|
|
143
116
|
// Nested batches are also supported
|
|
144
117
|
batch(() => {
|
|
145
|
-
|
|
118
|
+
chunk1.set("Tunde");
|
|
146
119
|
batch(() => {
|
|
147
|
-
|
|
148
|
-
age.set(26);
|
|
120
|
+
chunk1.set(26);
|
|
149
121
|
});
|
|
150
122
|
});
|
|
151
123
|
```
|
|
152
124
|
|
|
153
|
-
|
|
125
|
+
### Selectors
|
|
154
126
|
|
|
155
|
-
|
|
127
|
+
Efficiently access and react to specific state parts:
|
|
156
128
|
|
|
157
|
-
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
The batch function ensures that:
|
|
163
|
-
|
|
164
|
-
- All updates within the batch are processed together
|
|
165
|
-
- Subscribers are notified only once with the final value
|
|
166
|
-
- Nested batches are handled correctly
|
|
167
|
-
- Updates are processed even if an error occurs (using try/finally)
|
|
168
|
-
|
|
169
|
-
### 1.4. **Middleware**
|
|
170
|
-
|
|
171
|
-
Middleware allows you to customize how values are set in a **chunk**. For example, you can add **logging**, **validation**, or any custom behavior when a chunk's value changes.
|
|
172
|
-
|
|
173
|
-
A middleware is a function with the following structure:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
export type Middleware<T> = (value: T, next: (newValue: T) => void) => void;
|
|
129
|
+
```typescript
|
|
130
|
+
// With selector - more specific, read-only
|
|
131
|
+
const userChunk = chunk({ name: "Olamide", score: 100 });
|
|
132
|
+
const scoreSelector = select(userChunk, (u) => u.score);
|
|
133
|
+
// scoreSelector.set(200); // This would throw an error
|
|
177
134
|
```
|
|
178
135
|
|
|
179
|
-
|
|
180
|
-
- next(value): A function you must call with the processed (or unaltered) value to continue the chain of middleware and eventually update the chunk's state.
|
|
136
|
+
## Middleware
|
|
181
137
|
|
|
182
|
-
|
|
138
|
+
Middleware allows you to customize how values are set in a **chunk**. For example, you can add **logging**, **validation**, or any custom behavior when a chunk's value changes.
|
|
183
139
|
|
|
184
|
-
```
|
|
185
|
-
import { chunk } from "stunk";
|
|
186
|
-
import { logger } from "stunk/middleware"; // native to stunk
|
|
187
|
-
import { nonNegativeValidator } from "stunk/middleware"; // native to stunk
|
|
140
|
+
```typescript
|
|
188
141
|
// You can also create yours and pass it []
|
|
189
142
|
|
|
190
143
|
// Use middleware for logging and validation
|
|
@@ -194,16 +147,9 @@ age.set(30); // Logs: "Setting value: 30"
|
|
|
194
147
|
age.set(-5); // Throws an error: "Value must be non-negative!"
|
|
195
148
|
```
|
|
196
149
|
|
|
197
|
-
###
|
|
198
|
-
|
|
199
|
-
The **withHistory** middleware extends a chunk to support undo and redo functionality. This allows you to navigate back and forth between previous **states**, making it useful for implementing features like **undo/redo**, form history, and state time travel.
|
|
200
|
-
|
|
201
|
-
### Usage
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
import { chunk } from "stunk";
|
|
205
|
-
import { withHistory } from "stunk/middleware"; // Import the history middleware
|
|
150
|
+
### History (Undo/Redo) - Time Travel
|
|
206
151
|
|
|
152
|
+
```typescript
|
|
207
153
|
const counter = withHistory(chunk(0));
|
|
208
154
|
|
|
209
155
|
counter.set(10);
|
|
@@ -218,17 +164,6 @@ counter.redo(); // Go forward one step
|
|
|
218
164
|
console.log(counter.get()); // 20
|
|
219
165
|
```
|
|
220
166
|
|
|
221
|
-
**Available Methods**
|
|
222
|
-
|
|
223
|
-
| Method | Description |
|
|
224
|
-
| ---------------- | ----------------------------------------------------------- |
|
|
225
|
-
| `undo()` | Reverts to the previous state (if available). |
|
|
226
|
-
| `redo()` | Moves forward to the next state (if available). |
|
|
227
|
-
| `canUndo()` | Returns `true` if there are past states available. |
|
|
228
|
-
| `canRedo()` | Returns `true` if there are future states available. |
|
|
229
|
-
| `getHistory()` | Returns an `array` of all past states. |
|
|
230
|
-
| `clearHistory()` | Clears all stored history and keeps only the current state. |
|
|
231
|
-
|
|
232
167
|
**Example: Limiting History Size (Optional)**
|
|
233
168
|
You can specify a max history size to prevent excessive memory usage.
|
|
234
169
|
|
|
@@ -238,10 +173,74 @@ const counter = withHistory(chunk(0), { maxHistory: 5 }); // Only keeps the last
|
|
|
238
173
|
|
|
239
174
|
This prevents the history from growing indefinitely and ensures efficient memory usage.
|
|
240
175
|
|
|
241
|
-
##
|
|
176
|
+
## API Reference
|
|
177
|
+
|
|
178
|
+
### `chunk<T>(initialValue: T, middleware?: Middleware<T>[])`
|
|
179
|
+
|
|
180
|
+
Creates a new state chunk.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
interface Chunk<T> {
|
|
184
|
+
get(): T;
|
|
185
|
+
set(value: T): void;
|
|
186
|
+
subscribe(callback: (value: T) => void): () => void;
|
|
187
|
+
derive<D>(fn: (value: T) => D): Chunk<D>;
|
|
188
|
+
destroy(): void;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `select<T, S>(sourceChunk: Chunk<T>, selector: (value: T) => S)`
|
|
193
|
+
|
|
194
|
+
Creates an optimized selector.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Returns a read-only chunk that updates only when selected value changes
|
|
198
|
+
const selector = select(userChunk, (user) => user.name);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `batch(callback: () => void)`
|
|
202
|
+
|
|
203
|
+
Batches multiple updates.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
batch(() => {
|
|
207
|
+
// Multiple updates here
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
batch(() => {
|
|
211
|
+
// Multiple updates here
|
|
212
|
+
batch(() => {
|
|
213
|
+
// Nested upddates here
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### `withHistory<T>(chunk: Chunk<T>, options?: { maxHistory?: number })`
|
|
219
|
+
|
|
220
|
+
Adds undo/redo capabilities.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface ChunkWithHistory<T> extends Chunk<T> {
|
|
224
|
+
undo(): void; // Reverts to the previous state (if available).
|
|
225
|
+
redo(): void; // Moves forward to the next state (if available).
|
|
226
|
+
canUndo(): boolean; // Returns `true` if there are past states available.
|
|
227
|
+
canRedo(): boolean; // Returns `true` if there are future states available.
|
|
228
|
+
getHistory(): T[]; // Returns an `array` of all past states.
|
|
229
|
+
clearHistory(): void; // Clears all stored history and keeps only the current state.
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `Middleware<T>`
|
|
234
|
+
|
|
235
|
+
Custom state processing:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
type Middleware<T> = (value: T, next: (newValue: T) => void) => void;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
- value: The value that is about to be set to the chunk.
|
|
242
|
+
- next(value): A function you must call with the processed (or unaltered) value to continue the chain of middleware and eventually update the chunk's state.
|
|
242
243
|
|
|
243
|
-
|
|
244
|
+
## License
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
- Update only the parts of your app that need to change
|
|
247
|
-
- Easily manage and subscribe to state changes
|
|
246
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stunk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Stunk - A framework-agnostic state management library implementing the Atomic State technique, utilizing chunk-based units for efficient state management.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
File without changes
|