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 CHANGED
@@ -1,8 +1,6 @@
1
1
  # Stunk
2
2
 
3
- **Stunk** is a **framework-agnostic** state management library that helps you manage your application's state in a clean and simple way. It uses a technique called **Atomic State**, breaking down state into smaller **chunks** that are easy to update, subscribe to, and manage.
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
- ### Why Stunk
16
+ ## Features
19
17
 
20
- - lightweight, framework-agnostic state management.
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
- ## 1 Features
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
- ### Usage:
35
+ ```typescript
36
+ import { chunk, select, batch } from "stunk";
45
37
 
46
- ```ts
47
- import { chunk } from "stunk";
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
- console.log(count.get()); // 0
42
+ // Select specific state - Selector
43
+ const nameSelector = select(userChunk, (user) => user.name);
52
44
 
53
- count.set(5);
45
+ // Subscribe to changes
46
+ nameSelector.subscribe((name) => console.log("Name:", name));
47
+ counter.subscribe((count) => console.log("Counter", counter));
54
48
 
55
- console.log(count.get()); // 5
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
- ### 1.1. **Subscription**
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
- ### Usage
63
-
64
- ```ts
65
- const count = chunk(0);
66
- const callback = (newValue: number) => console.log("Updated value:", newValue);
58
+ ### Chunks
67
59
 
68
- count.subscribe(callback);
60
+ Basic unit of state with get/set/subscribe functionality:
69
61
 
70
- count.set(10); // Will log: "Updated value: 10"
62
+ ```typescript
63
+ const counter = chunk(0);
64
+ counter.subscribe((value) => console.log(value));
65
+ counter.set(5);
71
66
  ```
72
67
 
73
- ### 1.1.0. **Unsubscribing**
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..** Well, would you wanna do that? 😂
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
- ### 1.2. **Deriving New Chunks**
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
- ### Usage
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
- ### 1.3. **Batch Updates**
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
- // Subscribe to changes
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
- // Without batch - triggers three separate updates
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
- firstName.set("Jane"); // Doesn't log yet
139
- lastName.set("Smith"); // Doesn't log yet
140
- age.set(26); // Doesn't log yet
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
- firstName.set("Jane");
118
+ chunk1.set("Tunde");
146
119
  batch(() => {
147
- lastName.set("Smith");
148
- age.set(26);
120
+ chunk1.set(26);
149
121
  });
150
122
  });
151
123
  ```
152
124
 
153
- Looks intresting right? There's more!
125
+ ### Selectors
154
126
 
155
- Batching is particularly useful when:
127
+ Efficiently access and react to specific state parts:
156
128
 
157
- - Updating multiple related pieces of state at once
158
- - Performing form updates
159
- - Handling complex state transitions
160
- - Optimizing performance in data-heavy applications
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
- - value: The value that is about to be set to the chunk.
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
- ### Usage
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
- ```ts
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
- ### 1.4.1. Middleware: Undo & Redo (withHistory)
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
- ## 2. **Atomic State Technique**
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
- The **Atomic State** technique is all about breaking down your state into small, manageable chunks. This allows you to:
244
+ ## License
244
245
 
245
- - Keep state changes focused and efficient
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.7.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