@fun-tools/store 1.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 +437 -0
- package/dist/index.cjs +210 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +182 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# @ex/store
|
|
2
|
+
|
|
3
|
+
> A lightweight, type-safe external store library for React, React Native, and Next.js
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@ex/store)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## 📋 Overview
|
|
9
|
+
|
|
10
|
+
`@ex/store` is a minimal yet powerful state management solution built on React's `useSyncExternalStore` API. It provides automatic handler generation, TypeScript support, and works seamlessly across React, React Native, and Next.js applications.
|
|
11
|
+
|
|
12
|
+
## ✨ Features
|
|
13
|
+
|
|
14
|
+
- **🎯 Type-Safe**: Full TypeScript support with intelligent type inference
|
|
15
|
+
- **⚡ Lightweight**: Minimal bundle size with zero dependencies (except React peer dependency)
|
|
16
|
+
- **🔄 Auto-Generated Handlers**: Automatic CRUD operations for arrays, objects, and primitives
|
|
17
|
+
- **🪝 Custom Handlers**: Define your own sync and async state handlers
|
|
18
|
+
- **🎨 Flexible API**: Use as a global store or scoped with React Context
|
|
19
|
+
- **📦 Tree-Shakeable**: Only bundle what you use
|
|
20
|
+
- **🚀 Performance Optimized**: Built-in shallow equality checks and snapshot caching
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @ex/store
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add @ex/store
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add @ex/store
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 🚀 Quick Start
|
|
37
|
+
|
|
38
|
+
### Basic Usage (Global Store)
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { createStore } from "@ex/store";
|
|
42
|
+
|
|
43
|
+
// Define your store
|
|
44
|
+
const store = createStore({
|
|
45
|
+
states: {
|
|
46
|
+
count: 0,
|
|
47
|
+
user: { name: "John", age: 25 },
|
|
48
|
+
items: ["apple", "banana"],
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Use in components
|
|
53
|
+
function Counter() {
|
|
54
|
+
const { count } = store.useStore((state) => ({ count: state.count }));
|
|
55
|
+
const handlers = store.useHandlers();
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div>
|
|
59
|
+
<p>Count: {count}</p>
|
|
60
|
+
<button onClick={() => handlers.count.set((prev) => prev + 1)}>
|
|
61
|
+
Increment
|
|
62
|
+
</button>
|
|
63
|
+
<button onClick={() => handlers.count.reset()}>Reset</button>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Context-Based Store (Scoped)
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { createStoreProvider } from "@ex/store";
|
|
73
|
+
|
|
74
|
+
// Create provider
|
|
75
|
+
const { Provider, useStore, useHandlers } = createStoreProvider({
|
|
76
|
+
states: {
|
|
77
|
+
theme: "light",
|
|
78
|
+
settings: { notifications: true },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Wrap your app
|
|
83
|
+
function App() {
|
|
84
|
+
return (
|
|
85
|
+
<Provider>
|
|
86
|
+
<ThemeToggle />
|
|
87
|
+
</Provider>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Use in child components
|
|
92
|
+
function ThemeToggle() {
|
|
93
|
+
const { theme } = useStore((state) => ({ theme: state.theme }));
|
|
94
|
+
const handlers = useHandlers();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<button
|
|
98
|
+
onClick={() => handlers.theme.set(theme === "light" ? "dark" : "light")}
|
|
99
|
+
>
|
|
100
|
+
Current: {theme}
|
|
101
|
+
</button>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 📖 API Reference
|
|
107
|
+
|
|
108
|
+
### `createStore(config)`
|
|
109
|
+
|
|
110
|
+
Creates a global store instance.
|
|
111
|
+
|
|
112
|
+
**Parameters:**
|
|
113
|
+
|
|
114
|
+
- `states`: Initial state object
|
|
115
|
+
- `syncHandlers?`: Custom synchronous handlers (optional)
|
|
116
|
+
- `asyncHandlers?`: Custom asynchronous handlers (optional)
|
|
117
|
+
|
|
118
|
+
**Returns:**
|
|
119
|
+
|
|
120
|
+
- `useStore<T>(selector)`: Hook to select and subscribe to state
|
|
121
|
+
- `useHandlers()`: Hook to access all handlers
|
|
122
|
+
|
|
123
|
+
### `createStoreProvider(config)`
|
|
124
|
+
|
|
125
|
+
Creates a context-based store provider.
|
|
126
|
+
|
|
127
|
+
**Parameters:**
|
|
128
|
+
|
|
129
|
+
- Same as `createStore`
|
|
130
|
+
|
|
131
|
+
**Returns:**
|
|
132
|
+
|
|
133
|
+
- `Provider`: React component to wrap your app
|
|
134
|
+
- `useStore<T>(selector)`: Hook to select and subscribe to state
|
|
135
|
+
- `useHandlers()`: Hook to access all handlers
|
|
136
|
+
|
|
137
|
+
## 🎨 Auto-Generated Handlers
|
|
138
|
+
|
|
139
|
+
The library automatically generates handlers based on your state types:
|
|
140
|
+
|
|
141
|
+
### For All Types
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// Set value (supports callbacks)
|
|
145
|
+
handlers.count.set(10);
|
|
146
|
+
handlers.count.set((prev) => prev + 1);
|
|
147
|
+
|
|
148
|
+
// Reset to initial value
|
|
149
|
+
handlers.count.reset();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### For Arrays
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
const store = createStore({
|
|
156
|
+
states: { items: ["a", "b", "c"] },
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const handlers = store.useHandlers();
|
|
160
|
+
|
|
161
|
+
// Add items
|
|
162
|
+
handlers.items.push("d");
|
|
163
|
+
handlers.items.unshift("z");
|
|
164
|
+
|
|
165
|
+
// Remove items
|
|
166
|
+
handlers.items.pop();
|
|
167
|
+
handlers.items.shift();
|
|
168
|
+
|
|
169
|
+
// Transform
|
|
170
|
+
handlers.items.filter((item) => item !== "b");
|
|
171
|
+
handlers.items.map((item) => item.toUpperCase());
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### For Objects
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
const store = createStore({
|
|
178
|
+
states: {
|
|
179
|
+
user: {
|
|
180
|
+
name: "John",
|
|
181
|
+
address: { city: "NYC", zip: "10001" },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const handlers = store.useHandlers();
|
|
187
|
+
|
|
188
|
+
// Update single nested property
|
|
189
|
+
handlers.user.update("name", "Jane");
|
|
190
|
+
handlers.user.update("address.city", "LA");
|
|
191
|
+
|
|
192
|
+
// Update multiple properties (deep merge)
|
|
193
|
+
handlers.user.updateMany({
|
|
194
|
+
name: "Jane",
|
|
195
|
+
address: { city: "LA" },
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 🔧 Custom Handlers
|
|
200
|
+
|
|
201
|
+
### Sync Handlers
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
const store = createStore({
|
|
205
|
+
states: {
|
|
206
|
+
count: 0,
|
|
207
|
+
},
|
|
208
|
+
syncHandlers: {
|
|
209
|
+
increment: (states) => {
|
|
210
|
+
states.count++;
|
|
211
|
+
},
|
|
212
|
+
incrementBy: (states, amount: number) => {
|
|
213
|
+
states.count += amount;
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Usage
|
|
219
|
+
const handlers = store.useHandlers();
|
|
220
|
+
handlers.increment(); // count + 1
|
|
221
|
+
handlers.incrementBy(5); // count + 5
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Async Handlers
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
const store = createStore({
|
|
228
|
+
states: {
|
|
229
|
+
user: null,
|
|
230
|
+
loading: false,
|
|
231
|
+
},
|
|
232
|
+
asyncHandlers: {
|
|
233
|
+
fetchUser: async (states, userId: string) => {
|
|
234
|
+
states.loading = true;
|
|
235
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
236
|
+
states.user = await response.json();
|
|
237
|
+
states.loading = false;
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Usage
|
|
243
|
+
const handlers = store.useHandlers();
|
|
244
|
+
await handlers.fetchUser("123");
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## 💡 Advanced Usage
|
|
248
|
+
|
|
249
|
+
### Selective Subscriptions
|
|
250
|
+
|
|
251
|
+
The `useStore` selector ensures components only re-render when selected state changes:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// Component only re-renders when 'count' changes
|
|
255
|
+
function CountDisplay() {
|
|
256
|
+
const { count } = store.useStore((state) => ({ count: state.count }));
|
|
257
|
+
return <div>{count}</div>;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Component only re-renders when 'user.name' changes
|
|
261
|
+
function UserName() {
|
|
262
|
+
const { name } = store.useStore((state) => ({
|
|
263
|
+
name: state.user.name,
|
|
264
|
+
}));
|
|
265
|
+
return <div>{name}</div>;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Multiple Stores
|
|
270
|
+
|
|
271
|
+
You can create multiple independent stores:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
const authStore = createStore({
|
|
275
|
+
states: { user: null, isAuthenticated: false },
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const cartStore = createStore({
|
|
279
|
+
states: { items: [], total: 0 },
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
function App() {
|
|
283
|
+
const { user } = authStore.useStore((state) => ({ user: state.user }));
|
|
284
|
+
const { items } = cartStore.useStore((state) => ({ items: state.items }));
|
|
285
|
+
|
|
286
|
+
// ...
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### TypeScript Type Inference
|
|
291
|
+
|
|
292
|
+
The library provides full type safety with intelligent inference:
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
const store = createStore({
|
|
296
|
+
states: {
|
|
297
|
+
count: 0,
|
|
298
|
+
user: { name: "John", age: 25 },
|
|
299
|
+
},
|
|
300
|
+
syncHandlers: {
|
|
301
|
+
setUserAge: (states, age: number) => {
|
|
302
|
+
states.user.age = age;
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const handlers = store.useHandlers();
|
|
308
|
+
|
|
309
|
+
// ✅ TypeScript knows the types
|
|
310
|
+
handlers.count.set(10); // ✓
|
|
311
|
+
handlers.user.update("name", "Jane"); // ✓
|
|
312
|
+
handlers.setUserAge(30); // ✓
|
|
313
|
+
|
|
314
|
+
// ❌ TypeScript catches errors
|
|
315
|
+
handlers.count.set("invalid"); // Error: Argument of type 'string' is not assignable
|
|
316
|
+
handlers.setUserAge("invalid"); // Error: Argument of type 'string' is not assignable
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## 🏗️ Architecture
|
|
320
|
+
|
|
321
|
+
The library uses React's `useSyncExternalStore` API for optimal performance and compatibility:
|
|
322
|
+
|
|
323
|
+
- **Snapshot Caching**: Prevents unnecessary re-renders with shallow equality checks
|
|
324
|
+
- **WeakMap Storage**: Efficient snapshot management with automatic garbage collection
|
|
325
|
+
- **Immutable Updates**: State changes create new references for proper React updates
|
|
326
|
+
- **Subscription Management**: Automatic cleanup when components unmount
|
|
327
|
+
|
|
328
|
+
## 🎯 Use Cases
|
|
329
|
+
|
|
330
|
+
Perfect for:
|
|
331
|
+
|
|
332
|
+
- ✅ Small to medium-sized applications
|
|
333
|
+
- ✅ Quick prototyping and MVPs
|
|
334
|
+
- ✅ React Native mobile apps
|
|
335
|
+
- ✅ Next.js applications (SSR compatible)
|
|
336
|
+
- ✅ Projects needing simple global state
|
|
337
|
+
- ✅ Teams wanting minimal state management setup
|
|
338
|
+
|
|
339
|
+
## 📝 Examples
|
|
340
|
+
|
|
341
|
+
### Todo App
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
const todoStore = createStore({
|
|
345
|
+
states: {
|
|
346
|
+
todos: [] as Array<{ id: string; text: string; completed: boolean }>,
|
|
347
|
+
},
|
|
348
|
+
syncHandlers: {
|
|
349
|
+
addTodo: (states, text: string) => {
|
|
350
|
+
states.todos.push({
|
|
351
|
+
id: Date.now().toString(),
|
|
352
|
+
text,
|
|
353
|
+
completed: false,
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
toggleTodo: (states, id: string) => {
|
|
357
|
+
const todo = states.todos.find((t) => t.id === id);
|
|
358
|
+
if (todo) todo.completed = !todo.completed;
|
|
359
|
+
},
|
|
360
|
+
removeTodo: (states, id: string) => {
|
|
361
|
+
states.todos = states.todos.filter((t) => t.id !== id);
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
function TodoList() {
|
|
367
|
+
const { todos } = todoStore.useStore((state) => ({ todos: state.todos }));
|
|
368
|
+
const handlers = todoStore.useHandlers();
|
|
369
|
+
|
|
370
|
+
return (
|
|
371
|
+
<div>
|
|
372
|
+
{todos.map((todo) => (
|
|
373
|
+
<div key={todo.id}>
|
|
374
|
+
<input
|
|
375
|
+
type="checkbox"
|
|
376
|
+
checked={todo.completed}
|
|
377
|
+
onChange={() => handlers.toggleTodo(todo.id)}
|
|
378
|
+
/>
|
|
379
|
+
<span
|
|
380
|
+
style={{ textDecoration: todo.completed ? "line-through" : "none" }}
|
|
381
|
+
>
|
|
382
|
+
{todo.text}
|
|
383
|
+
</span>
|
|
384
|
+
<button onClick={() => handlers.removeTodo(todo.id)}>Delete</button>
|
|
385
|
+
</div>
|
|
386
|
+
))}
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Form Management
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
const formStore = createStore({
|
|
396
|
+
states: {
|
|
397
|
+
formData: {
|
|
398
|
+
username: "",
|
|
399
|
+
email: "",
|
|
400
|
+
bio: "",
|
|
401
|
+
},
|
|
402
|
+
errors: {} as Record<string, string>,
|
|
403
|
+
},
|
|
404
|
+
syncHandlers: {
|
|
405
|
+
updateField: (
|
|
406
|
+
states,
|
|
407
|
+
{ field, value }: { field: string; value: string }
|
|
408
|
+
) => {
|
|
409
|
+
states.formData = { ...states.formData, [field]: value };
|
|
410
|
+
},
|
|
411
|
+
validate: (states) => {
|
|
412
|
+
const errors: Record<string, string> = {};
|
|
413
|
+
if (!states.formData.username) errors.username = "Required";
|
|
414
|
+
if (!states.formData.email.includes("@")) errors.email = "Invalid email";
|
|
415
|
+
states.errors = errors;
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## 🤝 Contributing
|
|
422
|
+
|
|
423
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
424
|
+
|
|
425
|
+
## 📄 License
|
|
426
|
+
|
|
427
|
+
MIT © [Your Name]
|
|
428
|
+
|
|
429
|
+
## 🔗 Links
|
|
430
|
+
|
|
431
|
+
- [GitHub Repository](https://github.com/yourusername/ex-store)
|
|
432
|
+
- [Issues](https://github.com/yourusername/ex-store/issues)
|
|
433
|
+
- [NPM Package](https://www.npmjs.com/package/@ex/store)
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
Made with ❤️ by developers, for developers
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createStore: () => createStore,
|
|
24
|
+
createStoreProvider: () => createStoreProvider
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/createStore/index.ts
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
|
|
31
|
+
// src/createStore/utils.ts
|
|
32
|
+
function cookAutoBuildHandlers(states, cb) {
|
|
33
|
+
const withCallback = (fun) => {
|
|
34
|
+
return (...args) => {
|
|
35
|
+
const val = fun(...args);
|
|
36
|
+
cb();
|
|
37
|
+
return val;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const defaultStates = JSON.parse(JSON.stringify(states));
|
|
41
|
+
return Object.fromEntries(
|
|
42
|
+
Object.entries(states).map(([key, val]) => {
|
|
43
|
+
const handlers = {
|
|
44
|
+
set: withCallback((action) => {
|
|
45
|
+
states[key] = typeof action === "function" ? action(states[key]) : action;
|
|
46
|
+
}),
|
|
47
|
+
reset: withCallback(() => {
|
|
48
|
+
states[key] = defaultStates[key];
|
|
49
|
+
}),
|
|
50
|
+
...Array.isArray(val) ? {
|
|
51
|
+
push: withCallback((val2) => {
|
|
52
|
+
states[key] = [...states[key], val2];
|
|
53
|
+
}),
|
|
54
|
+
pop: withCallback(() => {
|
|
55
|
+
states[key] = states[key].slice(0, -1);
|
|
56
|
+
}),
|
|
57
|
+
shift: withCallback(() => {
|
|
58
|
+
states[key] = states[key].slice(1);
|
|
59
|
+
}),
|
|
60
|
+
unshift: withCallback((val2) => {
|
|
61
|
+
states[key] = [val2, ...states[key]];
|
|
62
|
+
}),
|
|
63
|
+
filter: withCallback((cb2) => {
|
|
64
|
+
states[key] = states[key].filter(cb2);
|
|
65
|
+
}),
|
|
66
|
+
map: withCallback((cb2) => {
|
|
67
|
+
states[key] = states[key].map(cb2);
|
|
68
|
+
})
|
|
69
|
+
} : val && typeof val === "object" ? {
|
|
70
|
+
update: withCallback((_path, val2) => {
|
|
71
|
+
let temp = states[key];
|
|
72
|
+
const path = _path.split(".");
|
|
73
|
+
const fn = (obj, index = 0) => {
|
|
74
|
+
var _a;
|
|
75
|
+
if (path.length === index) return val2;
|
|
76
|
+
const key2 = path[index];
|
|
77
|
+
return {
|
|
78
|
+
...obj,
|
|
79
|
+
[key2]: fn((_a = obj == null ? void 0 : obj[key2]) != null ? _a : {}, index + 1)
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
states[key] = fn(states[key]);
|
|
83
|
+
}),
|
|
84
|
+
updateMany: withCallback((val2) => {
|
|
85
|
+
const fn = (target, source) => {
|
|
86
|
+
if (typeof source !== "object" || source === null) return source;
|
|
87
|
+
if (Array.isArray(source)) return source;
|
|
88
|
+
const result = { ...target };
|
|
89
|
+
for (const key2 in source) {
|
|
90
|
+
if (Object.prototype.hasOwnProperty.call(target, key2)) {
|
|
91
|
+
result[key2] = fn(target[key2], source[key2]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
};
|
|
96
|
+
states[key] = fn(states[key], val2);
|
|
97
|
+
})
|
|
98
|
+
} : {}
|
|
99
|
+
};
|
|
100
|
+
return [key, handlers];
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function shallowEqual(a, b) {
|
|
105
|
+
if (a === b) return true;
|
|
106
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
107
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
108
|
+
const key1 = Object.keys(a);
|
|
109
|
+
const key2 = Object.keys(b);
|
|
110
|
+
if (key1.length !== key2.length) return false;
|
|
111
|
+
for (let key of key1) {
|
|
112
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
113
|
+
if (!Object.is(a[key], b[key])) return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/createStore/index.ts
|
|
119
|
+
function createStore({
|
|
120
|
+
states,
|
|
121
|
+
syncHandlers,
|
|
122
|
+
asyncHandlers
|
|
123
|
+
}) {
|
|
124
|
+
const allHandlers = Object.freeze(
|
|
125
|
+
Object.fromEntries([
|
|
126
|
+
...Object.entries(syncHandlers != null ? syncHandlers : {}).map(([key, cb]) => [
|
|
127
|
+
key,
|
|
128
|
+
(action) => {
|
|
129
|
+
cb(states, action);
|
|
130
|
+
notify();
|
|
131
|
+
}
|
|
132
|
+
]),
|
|
133
|
+
...Object.entries(asyncHandlers != null ? asyncHandlers : {}).map(([key, cb]) => [
|
|
134
|
+
key,
|
|
135
|
+
async (action) => {
|
|
136
|
+
await cb(states, action);
|
|
137
|
+
notify();
|
|
138
|
+
}
|
|
139
|
+
]),
|
|
140
|
+
...Object.entries(cookAutoBuildHandlers(states, notify))
|
|
141
|
+
])
|
|
142
|
+
);
|
|
143
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
144
|
+
function consume(consumer) {
|
|
145
|
+
consumers.add(consumer);
|
|
146
|
+
return () => consumers.delete(consumer);
|
|
147
|
+
}
|
|
148
|
+
function notify() {
|
|
149
|
+
consumers.forEach((con) => con());
|
|
150
|
+
}
|
|
151
|
+
const snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
152
|
+
function getSnapshot(selector) {
|
|
153
|
+
const newSnapshot = selector(states);
|
|
154
|
+
const cachedSnapshot = snapshotCache.get(selector);
|
|
155
|
+
if (cachedSnapshot && shallowEqual(newSnapshot, cachedSnapshot)) {
|
|
156
|
+
return cachedSnapshot;
|
|
157
|
+
}
|
|
158
|
+
snapshotCache.set(selector, newSnapshot);
|
|
159
|
+
return Object.freeze(newSnapshot);
|
|
160
|
+
}
|
|
161
|
+
function useStore(selector) {
|
|
162
|
+
return (0, import_react.useSyncExternalStore)(
|
|
163
|
+
consume,
|
|
164
|
+
() => getSnapshot(selector),
|
|
165
|
+
() => getSnapshot(selector)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function useHandlers() {
|
|
169
|
+
return allHandlers;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
useStore,
|
|
173
|
+
useHandlers
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/createStoreProvider/index.tsx
|
|
178
|
+
var import_react2 = require("react");
|
|
179
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
180
|
+
function createStoreProvider(props) {
|
|
181
|
+
const Context = (0, import_react2.createContext)(null);
|
|
182
|
+
function Provider({ children }) {
|
|
183
|
+
const store = (0, import_react2.useRef)(null);
|
|
184
|
+
if (!store.current) {
|
|
185
|
+
store.current = createStore(props);
|
|
186
|
+
}
|
|
187
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Context.Provider, { value: store.current, children });
|
|
188
|
+
}
|
|
189
|
+
function useStore(selector) {
|
|
190
|
+
const store = (0, import_react2.useContext)(Context);
|
|
191
|
+
if (!store) throw Error("Store Provider is missing !!");
|
|
192
|
+
return store.useStore(selector);
|
|
193
|
+
}
|
|
194
|
+
function useHandlers() {
|
|
195
|
+
const store = (0, import_react2.useContext)(Context);
|
|
196
|
+
if (!store) throw Error("Store Provider is missing !!");
|
|
197
|
+
return store.useHandlers();
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
Provider,
|
|
201
|
+
useStore,
|
|
202
|
+
useHandlers
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
206
|
+
0 && (module.exports = {
|
|
207
|
+
createStore,
|
|
208
|
+
createStoreProvider
|
|
209
|
+
});
|
|
210
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/createStore/index.ts","../src/createStore/utils.ts","../src/createStoreProvider/index.tsx"],"sourcesContent":["export { default as createStore } from \"./createStore\";\r\nexport { default as createStoreProvider } from \"./createStoreProvider\";\r\n\r\nexport * from \"./createStore/types\";","import { useSyncExternalStore } from \"react\";\r\nimport { cookAutoBuildHandlers, shallowEqual } from \"./utils\";\r\nimport { \r\n AsyncHandler, CreateStoreContextProps, \r\n CreateStoreReturn, \r\n HandlerRecord, States, SyncHandler \r\n} from \"./types\";\r\n\r\n\r\n\r\nexport default function createStore<\r\n S extends States,\r\n SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>,\r\n AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>\r\n>({\r\n states, syncHandlers, asyncHandlers\r\n}: CreateStoreContextProps<S, SH, AH>): CreateStoreReturn<S, SH, AH> {\r\n\r\n const allHandlers: HandlerRecord<S, SH, AH> = Object.freeze(\r\n Object.fromEntries([\r\n ...Object.entries(syncHandlers ?? {}).map(([key, cb]) => [\r\n key, (action?: any) => {cb(states, action); notify();}\r\n ]),\r\n ...Object.entries(asyncHandlers ?? {}).map(([key, cb]) => [\r\n key, async (action?: any) => {await cb(states, action); notify();}\r\n ]),\r\n ...Object.entries(cookAutoBuildHandlers(states, notify))\r\n ])\r\n ) as HandlerRecord<S, SH, AH>\r\n\r\n\r\n const consumers = new Set<() => void>();\r\n\r\n \r\n function consume(consumer: () => void) {\r\n consumers.add(consumer);\r\n return () => consumers.delete(consumer);\r\n }\r\n\r\n\r\n function notify() {\r\n consumers.forEach(con => con());\r\n }\r\n\r\n\r\n const snapshotCache = new WeakMap<(state: S) => object, object>();\r\n\r\n\r\n function getSnapshot<T extends object>(selector: (state: S) => T): T {\r\n const newSnapshot = selector(states);\r\n const cachedSnapshot = snapshotCache.get(selector);\r\n\r\n if(cachedSnapshot && shallowEqual(newSnapshot, cachedSnapshot)) {\r\n return cachedSnapshot as T;\r\n }\r\n \r\n snapshotCache.set(selector, newSnapshot);\r\n return Object.freeze(newSnapshot);\r\n }\r\n \r\n \r\n function useStore<T extends object>(selector: (state: S) => T): T {\r\n return useSyncExternalStore(\r\n consume,\r\n () => getSnapshot(selector),\r\n () => getSnapshot(selector)\r\n )\r\n }\r\n\r\n\r\n function useHandlers(): HandlerRecord<S, SH, AH> {\r\n return allHandlers;\r\n }\r\n\r\n\r\n return {\r\n useStore,\r\n useHandlers\r\n }\r\n}","import { AutoBuildHandler, AutoBuildHandlerRecord, States } from \"./types\";\r\n\r\nexport function cookAutoBuildHandlers<S extends States>(states: S, cb: () => void): AutoBuildHandlerRecord<S> {\r\n\r\n const withCallback = <R>(fun: (...args: any[]) => R) => {\r\n return (...args: any[]): R => {\r\n const val = fun(...args);\r\n cb();\r\n return val;\r\n }\r\n }\r\n\r\n\r\n const defaultStates: S = JSON.parse(JSON.stringify(states));\r\n\r\n \r\n return Object.fromEntries(\r\n Object.entries(states).map(([key, val]: [keyof S, S[keyof S]]) => {\r\n const handlers: AutoBuildHandler<S[keyof S]> = {\r\n set: withCallback((action) => {\r\n states[key] = typeof action === 'function' ? action(states[key]) : action;\r\n }),\r\n\r\n reset: withCallback(() => {\r\n states[key] = defaultStates[key];\r\n }),\r\n \r\n ...(\r\n Array.isArray(val) ? {\r\n push: withCallback((val) => {\r\n states[key] = [...states[key], val] as S[keyof S]\r\n }),\r\n \r\n pop: withCallback(() => {\r\n states[key] = states[key].slice(0, -1)\r\n }),\r\n\r\n shift: withCallback(() => {\r\n states[key] = states[key].slice(1)\r\n }),\r\n \r\n unshift: withCallback((val) => {\r\n states[key] = [val, ...states[key]] as S[keyof S]\r\n }),\r\n\r\n filter: withCallback((cb) => {\r\n states[key] = states[key].filter(cb)\r\n }),\r\n\r\n map: withCallback((cb) => {\r\n states[key] = states[key].map(cb)\r\n })\r\n\r\n } : val && typeof val === \"object\" ? {\r\n update: withCallback((_path, val) => {\r\n let temp: any = states[key];\r\n const path = _path.split('.');\r\n\r\n const fn = (obj: any, index = 0): S[keyof S] => {\r\n if (path.length === index) return val;\r\n const key = path[index];\r\n\r\n return {\r\n ...obj,\r\n [key]: fn(obj?.[key] ?? {}, index + 1)\r\n };\r\n }\r\n\r\n states[key] = fn(states[key]);\r\n }),\r\n\r\n updateMany: withCallback((val) => {\r\n const fn = (target: any, source: any) => {\r\n if (typeof source !== \"object\" || source === null) return source;\r\n if (Array.isArray(source)) return source;\r\n\r\n const result = { ...target };\r\n\r\n for (const key in source) {\r\n if (Object.prototype.hasOwnProperty.call(target, key)) {\r\n result[key] = fn(target[key], source[key]);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n states[key] = fn(states[key], val);\r\n })\r\n } : {}\r\n )\r\n } as unknown as AutoBuildHandler<S[keyof S]>\r\n \r\n return [key, handlers]\r\n })\r\n ) as unknown as AutoBuildHandlerRecord<S>\r\n}\r\n\r\n\r\nexport function shallowEqual(a: any, b: any) {\r\n if(a === b) return true;\r\n \r\n if(typeof a !== 'object' || typeof b !== 'object') return false;\r\n\r\n if(Array.isArray(a) !== Array.isArray(b)) return false;\r\n\r\n const key1 = Object.keys(a);\r\n const key2 = Object.keys(b);\r\n\r\n if(key1.length !== key2.length) return false;\r\n\r\n for(let key of key1) {\r\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false;\r\n if (!Object.is(a[key], b[key])) return false;\r\n }\r\n\r\n return true;\r\n}","import React, { createContext, ReactNode, useContext, useRef } from \"react\";\r\nimport createStore from \"../createStore\";\r\nimport { AsyncHandler, CreateStoreContextProps, CreateStoreReturn, HandlerRecord, States, SyncHandler } from \"../createStore/types\";\r\n\r\n\r\n\r\nexport default function createStoreProvider<\r\n S extends States,\r\n SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>,\r\n AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>\r\n>(props: CreateStoreContextProps<S, SH, AH>) { \r\n\r\n const Context = createContext<CreateStoreReturn<S, SH, AH> | null>(null);\r\n \r\n function Provider({children}: {children: ReactNode}): React.JSX.Element {\r\n const store = useRef<CreateStoreReturn<S, SH, AH> | null>(null);\r\n\r\n if(!store.current) {\r\n store.current = createStore(props);\r\n }\r\n\r\n return (\r\n <Context.Provider value={store.current}>\r\n {children}\r\n </Context.Provider>\r\n )\r\n }\r\n\r\n \r\n function useStore<T extends object>(selector: (states: S) => T): T {\r\n const store = useContext(Context);\r\n if(!store) throw Error('Store Provider is missing !!');\r\n return store.useStore<T>(selector);\r\n }\r\n\r\n\r\n function useHandlers(): HandlerRecord<S, SH, AH> {\r\n const store = useContext(Context);\r\n if(!store) throw Error('Store Provider is missing !!');\r\n return store.useHandlers();\r\n }\r\n\r\n\r\n return {\r\n Provider,\r\n useStore,\r\n useHandlers\r\n } \r\n}"],"mappings":"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","names":["val","cb","key","import_react"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type OBJECT_KEYS<T extends object> = {
|
|
4
|
+
[K in keyof T]: K extends string | number ? T[K] extends object ? K | `${K}.${OBJECT_KEYS<T[K]>}` : K : never;
|
|
5
|
+
}[keyof T];
|
|
6
|
+
type OBJECT_VALUE<O extends Record<string, any>, P extends string | number> = (P extends `${infer K}.${infer P}` ? K extends keyof O ? OBJECT_VALUE<O[K], P> : never : P extends keyof O ? O[P] : never);
|
|
7
|
+
|
|
8
|
+
type States = Record<string, any>;
|
|
9
|
+
type Action<T> = T | ((val: T) => T);
|
|
10
|
+
type Handler<S, V> = (V extends (state: S) => infer R ? ((state: S) => R) : V extends (state: S, action: infer A) => infer R ? ((state: S, action: A) => R) : never);
|
|
11
|
+
type SyncHandler<S> = Handler<S, (state: S, action: any) => any>;
|
|
12
|
+
type AsyncHandler<S> = Handler<S, (state: S, action: any) => Promise<any>>;
|
|
13
|
+
type SyncHandlerRecord<S, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>> = {
|
|
14
|
+
[K in keyof SH]: Handler<S, SH[K]>;
|
|
15
|
+
};
|
|
16
|
+
type AsyncHandlerRecord<S, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
17
|
+
[K in keyof AH]: Handler<S, AH[K]>;
|
|
18
|
+
};
|
|
19
|
+
type CreateStoreContextProps<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
20
|
+
states: S;
|
|
21
|
+
syncHandlers?: SH;
|
|
22
|
+
asyncHandlers?: AH;
|
|
23
|
+
};
|
|
24
|
+
type AutoBuildHandler<V> = {
|
|
25
|
+
set: (cb: Action<V>) => void;
|
|
26
|
+
reset: () => void;
|
|
27
|
+
} & (V extends Array<infer T> ? {
|
|
28
|
+
push: (val: T) => void;
|
|
29
|
+
pop: () => T | null;
|
|
30
|
+
shift: () => T | null;
|
|
31
|
+
unshift: (val: T) => void;
|
|
32
|
+
filter: (cb: (item: T) => boolean) => void;
|
|
33
|
+
map: (cb: (item: T) => T) => void;
|
|
34
|
+
} : V extends States ? {
|
|
35
|
+
update: (key: OBJECT_KEYS<V>, val: OBJECT_VALUE<V, OBJECT_KEYS<V>>) => void;
|
|
36
|
+
updateMany: (val: Partial<V>) => void;
|
|
37
|
+
} : {});
|
|
38
|
+
type AutoBuildHandlerRecord<S extends States> = {
|
|
39
|
+
[K in keyof S]: AutoBuildHandler<S[K]>;
|
|
40
|
+
};
|
|
41
|
+
type ExtractActionType<T> = T extends (state: States, action: infer A) => void ? A : never;
|
|
42
|
+
type ReturnHandlers<S, V> = (V extends (state: S) => infer R ? (() => R) : V extends (state: S, action: infer A) => infer R ? ((action: A) => R) : never);
|
|
43
|
+
type HandlerRecord<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = AutoBuildHandlerRecord<S> & {
|
|
44
|
+
[K in keyof SH]: ReturnHandlers<S, SH[K]>;
|
|
45
|
+
} & {
|
|
46
|
+
[K in keyof AH]: ReturnHandlers<S, AH[K]>;
|
|
47
|
+
};
|
|
48
|
+
type CreateStoreReturn<S extends States = States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
49
|
+
useStore: <T extends object>(selector: (state: S) => T) => T;
|
|
50
|
+
useHandlers: () => HandlerRecord<S, SH, AH>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
declare function createStore<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>>({ states, syncHandlers, asyncHandlers }: CreateStoreContextProps<S, SH, AH>): CreateStoreReturn<S, SH, AH>;
|
|
54
|
+
|
|
55
|
+
declare function createStoreProvider<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>>(props: CreateStoreContextProps<S, SH, AH>): {
|
|
56
|
+
Provider: ({ children }: {
|
|
57
|
+
children: ReactNode;
|
|
58
|
+
}) => React.JSX.Element;
|
|
59
|
+
useStore: <T extends object>(selector: (states: S) => T) => T;
|
|
60
|
+
useHandlers: () => HandlerRecord<S, SH, AH>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { type Action, type AsyncHandler, type AsyncHandlerRecord, type AutoBuildHandler, type AutoBuildHandlerRecord, type CreateStoreContextProps, type CreateStoreReturn, type ExtractActionType, type Handler, type HandlerRecord, type States, type SyncHandler, type SyncHandlerRecord, createStore, createStoreProvider };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type OBJECT_KEYS<T extends object> = {
|
|
4
|
+
[K in keyof T]: K extends string | number ? T[K] extends object ? K | `${K}.${OBJECT_KEYS<T[K]>}` : K : never;
|
|
5
|
+
}[keyof T];
|
|
6
|
+
type OBJECT_VALUE<O extends Record<string, any>, P extends string | number> = (P extends `${infer K}.${infer P}` ? K extends keyof O ? OBJECT_VALUE<O[K], P> : never : P extends keyof O ? O[P] : never);
|
|
7
|
+
|
|
8
|
+
type States = Record<string, any>;
|
|
9
|
+
type Action<T> = T | ((val: T) => T);
|
|
10
|
+
type Handler<S, V> = (V extends (state: S) => infer R ? ((state: S) => R) : V extends (state: S, action: infer A) => infer R ? ((state: S, action: A) => R) : never);
|
|
11
|
+
type SyncHandler<S> = Handler<S, (state: S, action: any) => any>;
|
|
12
|
+
type AsyncHandler<S> = Handler<S, (state: S, action: any) => Promise<any>>;
|
|
13
|
+
type SyncHandlerRecord<S, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>> = {
|
|
14
|
+
[K in keyof SH]: Handler<S, SH[K]>;
|
|
15
|
+
};
|
|
16
|
+
type AsyncHandlerRecord<S, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
17
|
+
[K in keyof AH]: Handler<S, AH[K]>;
|
|
18
|
+
};
|
|
19
|
+
type CreateStoreContextProps<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
20
|
+
states: S;
|
|
21
|
+
syncHandlers?: SH;
|
|
22
|
+
asyncHandlers?: AH;
|
|
23
|
+
};
|
|
24
|
+
type AutoBuildHandler<V> = {
|
|
25
|
+
set: (cb: Action<V>) => void;
|
|
26
|
+
reset: () => void;
|
|
27
|
+
} & (V extends Array<infer T> ? {
|
|
28
|
+
push: (val: T) => void;
|
|
29
|
+
pop: () => T | null;
|
|
30
|
+
shift: () => T | null;
|
|
31
|
+
unshift: (val: T) => void;
|
|
32
|
+
filter: (cb: (item: T) => boolean) => void;
|
|
33
|
+
map: (cb: (item: T) => T) => void;
|
|
34
|
+
} : V extends States ? {
|
|
35
|
+
update: (key: OBJECT_KEYS<V>, val: OBJECT_VALUE<V, OBJECT_KEYS<V>>) => void;
|
|
36
|
+
updateMany: (val: Partial<V>) => void;
|
|
37
|
+
} : {});
|
|
38
|
+
type AutoBuildHandlerRecord<S extends States> = {
|
|
39
|
+
[K in keyof S]: AutoBuildHandler<S[K]>;
|
|
40
|
+
};
|
|
41
|
+
type ExtractActionType<T> = T extends (state: States, action: infer A) => void ? A : never;
|
|
42
|
+
type ReturnHandlers<S, V> = (V extends (state: S) => infer R ? (() => R) : V extends (state: S, action: infer A) => infer R ? ((action: A) => R) : never);
|
|
43
|
+
type HandlerRecord<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = AutoBuildHandlerRecord<S> & {
|
|
44
|
+
[K in keyof SH]: ReturnHandlers<S, SH[K]>;
|
|
45
|
+
} & {
|
|
46
|
+
[K in keyof AH]: ReturnHandlers<S, AH[K]>;
|
|
47
|
+
};
|
|
48
|
+
type CreateStoreReturn<S extends States = States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>> = {
|
|
49
|
+
useStore: <T extends object>(selector: (state: S) => T) => T;
|
|
50
|
+
useHandlers: () => HandlerRecord<S, SH, AH>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
declare function createStore<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>>({ states, syncHandlers, asyncHandlers }: CreateStoreContextProps<S, SH, AH>): CreateStoreReturn<S, SH, AH>;
|
|
54
|
+
|
|
55
|
+
declare function createStoreProvider<S extends States, SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>, AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>>(props: CreateStoreContextProps<S, SH, AH>): {
|
|
56
|
+
Provider: ({ children }: {
|
|
57
|
+
children: ReactNode;
|
|
58
|
+
}) => React.JSX.Element;
|
|
59
|
+
useStore: <T extends object>(selector: (states: S) => T) => T;
|
|
60
|
+
useHandlers: () => HandlerRecord<S, SH, AH>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { type Action, type AsyncHandler, type AsyncHandlerRecord, type AutoBuildHandler, type AutoBuildHandlerRecord, type CreateStoreContextProps, type CreateStoreReturn, type ExtractActionType, type Handler, type HandlerRecord, type States, type SyncHandler, type SyncHandlerRecord, createStore, createStoreProvider };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// src/createStore/index.ts
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
|
|
4
|
+
// src/createStore/utils.ts
|
|
5
|
+
function cookAutoBuildHandlers(states, cb) {
|
|
6
|
+
const withCallback = (fun) => {
|
|
7
|
+
return (...args) => {
|
|
8
|
+
const val = fun(...args);
|
|
9
|
+
cb();
|
|
10
|
+
return val;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const defaultStates = JSON.parse(JSON.stringify(states));
|
|
14
|
+
return Object.fromEntries(
|
|
15
|
+
Object.entries(states).map(([key, val]) => {
|
|
16
|
+
const handlers = {
|
|
17
|
+
set: withCallback((action) => {
|
|
18
|
+
states[key] = typeof action === "function" ? action(states[key]) : action;
|
|
19
|
+
}),
|
|
20
|
+
reset: withCallback(() => {
|
|
21
|
+
states[key] = defaultStates[key];
|
|
22
|
+
}),
|
|
23
|
+
...Array.isArray(val) ? {
|
|
24
|
+
push: withCallback((val2) => {
|
|
25
|
+
states[key] = [...states[key], val2];
|
|
26
|
+
}),
|
|
27
|
+
pop: withCallback(() => {
|
|
28
|
+
states[key] = states[key].slice(0, -1);
|
|
29
|
+
}),
|
|
30
|
+
shift: withCallback(() => {
|
|
31
|
+
states[key] = states[key].slice(1);
|
|
32
|
+
}),
|
|
33
|
+
unshift: withCallback((val2) => {
|
|
34
|
+
states[key] = [val2, ...states[key]];
|
|
35
|
+
}),
|
|
36
|
+
filter: withCallback((cb2) => {
|
|
37
|
+
states[key] = states[key].filter(cb2);
|
|
38
|
+
}),
|
|
39
|
+
map: withCallback((cb2) => {
|
|
40
|
+
states[key] = states[key].map(cb2);
|
|
41
|
+
})
|
|
42
|
+
} : val && typeof val === "object" ? {
|
|
43
|
+
update: withCallback((_path, val2) => {
|
|
44
|
+
let temp = states[key];
|
|
45
|
+
const path = _path.split(".");
|
|
46
|
+
const fn = (obj, index = 0) => {
|
|
47
|
+
var _a;
|
|
48
|
+
if (path.length === index) return val2;
|
|
49
|
+
const key2 = path[index];
|
|
50
|
+
return {
|
|
51
|
+
...obj,
|
|
52
|
+
[key2]: fn((_a = obj == null ? void 0 : obj[key2]) != null ? _a : {}, index + 1)
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
states[key] = fn(states[key]);
|
|
56
|
+
}),
|
|
57
|
+
updateMany: withCallback((val2) => {
|
|
58
|
+
const fn = (target, source) => {
|
|
59
|
+
if (typeof source !== "object" || source === null) return source;
|
|
60
|
+
if (Array.isArray(source)) return source;
|
|
61
|
+
const result = { ...target };
|
|
62
|
+
for (const key2 in source) {
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(target, key2)) {
|
|
64
|
+
result[key2] = fn(target[key2], source[key2]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
states[key] = fn(states[key], val2);
|
|
70
|
+
})
|
|
71
|
+
} : {}
|
|
72
|
+
};
|
|
73
|
+
return [key, handlers];
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function shallowEqual(a, b) {
|
|
78
|
+
if (a === b) return true;
|
|
79
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
80
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
81
|
+
const key1 = Object.keys(a);
|
|
82
|
+
const key2 = Object.keys(b);
|
|
83
|
+
if (key1.length !== key2.length) return false;
|
|
84
|
+
for (let key of key1) {
|
|
85
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
86
|
+
if (!Object.is(a[key], b[key])) return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/createStore/index.ts
|
|
92
|
+
function createStore({
|
|
93
|
+
states,
|
|
94
|
+
syncHandlers,
|
|
95
|
+
asyncHandlers
|
|
96
|
+
}) {
|
|
97
|
+
const allHandlers = Object.freeze(
|
|
98
|
+
Object.fromEntries([
|
|
99
|
+
...Object.entries(syncHandlers != null ? syncHandlers : {}).map(([key, cb]) => [
|
|
100
|
+
key,
|
|
101
|
+
(action) => {
|
|
102
|
+
cb(states, action);
|
|
103
|
+
notify();
|
|
104
|
+
}
|
|
105
|
+
]),
|
|
106
|
+
...Object.entries(asyncHandlers != null ? asyncHandlers : {}).map(([key, cb]) => [
|
|
107
|
+
key,
|
|
108
|
+
async (action) => {
|
|
109
|
+
await cb(states, action);
|
|
110
|
+
notify();
|
|
111
|
+
}
|
|
112
|
+
]),
|
|
113
|
+
...Object.entries(cookAutoBuildHandlers(states, notify))
|
|
114
|
+
])
|
|
115
|
+
);
|
|
116
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
117
|
+
function consume(consumer) {
|
|
118
|
+
consumers.add(consumer);
|
|
119
|
+
return () => consumers.delete(consumer);
|
|
120
|
+
}
|
|
121
|
+
function notify() {
|
|
122
|
+
consumers.forEach((con) => con());
|
|
123
|
+
}
|
|
124
|
+
const snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
125
|
+
function getSnapshot(selector) {
|
|
126
|
+
const newSnapshot = selector(states);
|
|
127
|
+
const cachedSnapshot = snapshotCache.get(selector);
|
|
128
|
+
if (cachedSnapshot && shallowEqual(newSnapshot, cachedSnapshot)) {
|
|
129
|
+
return cachedSnapshot;
|
|
130
|
+
}
|
|
131
|
+
snapshotCache.set(selector, newSnapshot);
|
|
132
|
+
return Object.freeze(newSnapshot);
|
|
133
|
+
}
|
|
134
|
+
function useStore(selector) {
|
|
135
|
+
return useSyncExternalStore(
|
|
136
|
+
consume,
|
|
137
|
+
() => getSnapshot(selector),
|
|
138
|
+
() => getSnapshot(selector)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function useHandlers() {
|
|
142
|
+
return allHandlers;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
useStore,
|
|
146
|
+
useHandlers
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/createStoreProvider/index.tsx
|
|
151
|
+
import { createContext, useContext, useRef } from "react";
|
|
152
|
+
import { jsx } from "react/jsx-runtime";
|
|
153
|
+
function createStoreProvider(props) {
|
|
154
|
+
const Context = createContext(null);
|
|
155
|
+
function Provider({ children }) {
|
|
156
|
+
const store = useRef(null);
|
|
157
|
+
if (!store.current) {
|
|
158
|
+
store.current = createStore(props);
|
|
159
|
+
}
|
|
160
|
+
return /* @__PURE__ */ jsx(Context.Provider, { value: store.current, children });
|
|
161
|
+
}
|
|
162
|
+
function useStore(selector) {
|
|
163
|
+
const store = useContext(Context);
|
|
164
|
+
if (!store) throw Error("Store Provider is missing !!");
|
|
165
|
+
return store.useStore(selector);
|
|
166
|
+
}
|
|
167
|
+
function useHandlers() {
|
|
168
|
+
const store = useContext(Context);
|
|
169
|
+
if (!store) throw Error("Store Provider is missing !!");
|
|
170
|
+
return store.useHandlers();
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
Provider,
|
|
174
|
+
useStore,
|
|
175
|
+
useHandlers
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
createStore,
|
|
180
|
+
createStoreProvider
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createStore/index.ts","../src/createStore/utils.ts","../src/createStoreProvider/index.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\r\nimport { cookAutoBuildHandlers, shallowEqual } from \"./utils\";\r\nimport { \r\n AsyncHandler, CreateStoreContextProps, \r\n CreateStoreReturn, \r\n HandlerRecord, States, SyncHandler \r\n} from \"./types\";\r\n\r\n\r\n\r\nexport default function createStore<\r\n S extends States,\r\n SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>,\r\n AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>\r\n>({\r\n states, syncHandlers, asyncHandlers\r\n}: CreateStoreContextProps<S, SH, AH>): CreateStoreReturn<S, SH, AH> {\r\n\r\n const allHandlers: HandlerRecord<S, SH, AH> = Object.freeze(\r\n Object.fromEntries([\r\n ...Object.entries(syncHandlers ?? {}).map(([key, cb]) => [\r\n key, (action?: any) => {cb(states, action); notify();}\r\n ]),\r\n ...Object.entries(asyncHandlers ?? {}).map(([key, cb]) => [\r\n key, async (action?: any) => {await cb(states, action); notify();}\r\n ]),\r\n ...Object.entries(cookAutoBuildHandlers(states, notify))\r\n ])\r\n ) as HandlerRecord<S, SH, AH>\r\n\r\n\r\n const consumers = new Set<() => void>();\r\n\r\n \r\n function consume(consumer: () => void) {\r\n consumers.add(consumer);\r\n return () => consumers.delete(consumer);\r\n }\r\n\r\n\r\n function notify() {\r\n consumers.forEach(con => con());\r\n }\r\n\r\n\r\n const snapshotCache = new WeakMap<(state: S) => object, object>();\r\n\r\n\r\n function getSnapshot<T extends object>(selector: (state: S) => T): T {\r\n const newSnapshot = selector(states);\r\n const cachedSnapshot = snapshotCache.get(selector);\r\n\r\n if(cachedSnapshot && shallowEqual(newSnapshot, cachedSnapshot)) {\r\n return cachedSnapshot as T;\r\n }\r\n \r\n snapshotCache.set(selector, newSnapshot);\r\n return Object.freeze(newSnapshot);\r\n }\r\n \r\n \r\n function useStore<T extends object>(selector: (state: S) => T): T {\r\n return useSyncExternalStore(\r\n consume,\r\n () => getSnapshot(selector),\r\n () => getSnapshot(selector)\r\n )\r\n }\r\n\r\n\r\n function useHandlers(): HandlerRecord<S, SH, AH> {\r\n return allHandlers;\r\n }\r\n\r\n\r\n return {\r\n useStore,\r\n useHandlers\r\n }\r\n}","import { AutoBuildHandler, AutoBuildHandlerRecord, States } from \"./types\";\r\n\r\nexport function cookAutoBuildHandlers<S extends States>(states: S, cb: () => void): AutoBuildHandlerRecord<S> {\r\n\r\n const withCallback = <R>(fun: (...args: any[]) => R) => {\r\n return (...args: any[]): R => {\r\n const val = fun(...args);\r\n cb();\r\n return val;\r\n }\r\n }\r\n\r\n\r\n const defaultStates: S = JSON.parse(JSON.stringify(states));\r\n\r\n \r\n return Object.fromEntries(\r\n Object.entries(states).map(([key, val]: [keyof S, S[keyof S]]) => {\r\n const handlers: AutoBuildHandler<S[keyof S]> = {\r\n set: withCallback((action) => {\r\n states[key] = typeof action === 'function' ? action(states[key]) : action;\r\n }),\r\n\r\n reset: withCallback(() => {\r\n states[key] = defaultStates[key];\r\n }),\r\n \r\n ...(\r\n Array.isArray(val) ? {\r\n push: withCallback((val) => {\r\n states[key] = [...states[key], val] as S[keyof S]\r\n }),\r\n \r\n pop: withCallback(() => {\r\n states[key] = states[key].slice(0, -1)\r\n }),\r\n\r\n shift: withCallback(() => {\r\n states[key] = states[key].slice(1)\r\n }),\r\n \r\n unshift: withCallback((val) => {\r\n states[key] = [val, ...states[key]] as S[keyof S]\r\n }),\r\n\r\n filter: withCallback((cb) => {\r\n states[key] = states[key].filter(cb)\r\n }),\r\n\r\n map: withCallback((cb) => {\r\n states[key] = states[key].map(cb)\r\n })\r\n\r\n } : val && typeof val === \"object\" ? {\r\n update: withCallback((_path, val) => {\r\n let temp: any = states[key];\r\n const path = _path.split('.');\r\n\r\n const fn = (obj: any, index = 0): S[keyof S] => {\r\n if (path.length === index) return val;\r\n const key = path[index];\r\n\r\n return {\r\n ...obj,\r\n [key]: fn(obj?.[key] ?? {}, index + 1)\r\n };\r\n }\r\n\r\n states[key] = fn(states[key]);\r\n }),\r\n\r\n updateMany: withCallback((val) => {\r\n const fn = (target: any, source: any) => {\r\n if (typeof source !== \"object\" || source === null) return source;\r\n if (Array.isArray(source)) return source;\r\n\r\n const result = { ...target };\r\n\r\n for (const key in source) {\r\n if (Object.prototype.hasOwnProperty.call(target, key)) {\r\n result[key] = fn(target[key], source[key]);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n states[key] = fn(states[key], val);\r\n })\r\n } : {}\r\n )\r\n } as unknown as AutoBuildHandler<S[keyof S]>\r\n \r\n return [key, handlers]\r\n })\r\n ) as unknown as AutoBuildHandlerRecord<S>\r\n}\r\n\r\n\r\nexport function shallowEqual(a: any, b: any) {\r\n if(a === b) return true;\r\n \r\n if(typeof a !== 'object' || typeof b !== 'object') return false;\r\n\r\n if(Array.isArray(a) !== Array.isArray(b)) return false;\r\n\r\n const key1 = Object.keys(a);\r\n const key2 = Object.keys(b);\r\n\r\n if(key1.length !== key2.length) return false;\r\n\r\n for(let key of key1) {\r\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false;\r\n if (!Object.is(a[key], b[key])) return false;\r\n }\r\n\r\n return true;\r\n}","import React, { createContext, ReactNode, useContext, useRef } from \"react\";\r\nimport createStore from \"../createStore\";\r\nimport { AsyncHandler, CreateStoreContextProps, CreateStoreReturn, HandlerRecord, States, SyncHandler } from \"../createStore/types\";\r\n\r\n\r\n\r\nexport default function createStoreProvider<\r\n S extends States,\r\n SH extends Record<string, SyncHandler<S>> = Record<string, SyncHandler<S>>,\r\n AH extends Record<string, AsyncHandler<S>> = Record<string, AsyncHandler<S>>\r\n>(props: CreateStoreContextProps<S, SH, AH>) { \r\n\r\n const Context = createContext<CreateStoreReturn<S, SH, AH> | null>(null);\r\n \r\n function Provider({children}: {children: ReactNode}): React.JSX.Element {\r\n const store = useRef<CreateStoreReturn<S, SH, AH> | null>(null);\r\n\r\n if(!store.current) {\r\n store.current = createStore(props);\r\n }\r\n\r\n return (\r\n <Context.Provider value={store.current}>\r\n {children}\r\n </Context.Provider>\r\n )\r\n }\r\n\r\n \r\n function useStore<T extends object>(selector: (states: S) => T): T {\r\n const store = useContext(Context);\r\n if(!store) throw Error('Store Provider is missing !!');\r\n return store.useStore<T>(selector);\r\n }\r\n\r\n\r\n function useHandlers(): HandlerRecord<S, SH, AH> {\r\n const store = useContext(Context);\r\n if(!store) throw Error('Store Provider is missing !!');\r\n return store.useHandlers();\r\n }\r\n\r\n\r\n return {\r\n Provider,\r\n useStore,\r\n useHandlers\r\n } \r\n}"],"mappings":"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","names":["val","cb","key"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fun-tools/store",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lightweight external store for React, React Native and Next.js",
|
|
5
|
+
"author": "Mustak Khan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
|
|
8
|
+
"type": "module",
|
|
9
|
+
|
|
10
|
+
"main": "./dist/index.cjs",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"files": ["dist"],
|
|
23
|
+
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": ">=18"
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^19.2.7",
|
|
30
|
+
"eslint": "^9.39.2",
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"dev": "tsup --watch",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
}
|
|
40
|
+
}
|