@signaltree/core 1.0.0 → 1.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 +287 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,7 +1,290 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌳 SignalTree Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The foundation package for SignalTree - a powerful, type-safe, modular signal-based state management solution for Angular applications.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## ✨ What is @signaltree/core?
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
SignalTree Core is the lightweight (5KB) foundation that provides:
|
|
8
|
+
|
|
9
|
+
- **Hierarchical signal trees** for organized state management
|
|
10
|
+
- **Type-safe updates and access** with full TypeScript inference
|
|
11
|
+
- **Basic entity management** with CRUD operations
|
|
12
|
+
- **Simple async actions** with loading states
|
|
13
|
+
- **Form integration basics** for reactive forms
|
|
14
|
+
|
|
15
|
+
## 🚀 Quick Start
|
|
16
|
+
|
|
17
|
+
### Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @signaltree/core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Basic Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { signalTree } from '@signaltree/core';
|
|
27
|
+
|
|
28
|
+
// Create a reactive state tree
|
|
29
|
+
const tree = signalTree({
|
|
30
|
+
user: {
|
|
31
|
+
name: 'John Doe',
|
|
32
|
+
email: 'john@example.com',
|
|
33
|
+
},
|
|
34
|
+
settings: {
|
|
35
|
+
theme: 'dark',
|
|
36
|
+
notifications: true,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Full type-safe access to nested signals
|
|
41
|
+
console.log(tree.$.user.name()); // 'John Doe'
|
|
42
|
+
tree.$.settings.theme.set('light');
|
|
43
|
+
|
|
44
|
+
// Entity management always included (lightweight)
|
|
45
|
+
const users = tree.asCrud('users');
|
|
46
|
+
users.add({ id: '1', name: 'Alice', email: 'alice@example.com' });
|
|
47
|
+
|
|
48
|
+
// Basic async actions included
|
|
49
|
+
const loadUser = tree.asyncAction(async (id: string) => {
|
|
50
|
+
return await api.getUser(id);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 📦 Core Features
|
|
55
|
+
|
|
56
|
+
### Hierarchical Signal Trees
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const tree = signalTree({
|
|
60
|
+
user: { name: '', email: '' },
|
|
61
|
+
settings: { theme: 'dark', notifications: true },
|
|
62
|
+
todos: [] as Todo[],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Access nested signals with full type safety
|
|
66
|
+
tree.$.user.name(); // string
|
|
67
|
+
tree.$.settings.theme.set('light');
|
|
68
|
+
tree.$.todos.update((todos) => [...todos, newTodo]);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Basic Entity Management
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Built-in CRUD operations (lightweight)
|
|
75
|
+
const todos = tree.asCrud<Todo>('todos');
|
|
76
|
+
|
|
77
|
+
todos.add({ id: '1', text: 'Learn SignalTree', done: false });
|
|
78
|
+
todos.update('1', { done: true });
|
|
79
|
+
todos.remove('1');
|
|
80
|
+
todos.upsert({ id: '2', text: 'Build app', done: false });
|
|
81
|
+
|
|
82
|
+
// Basic queries
|
|
83
|
+
const todoById = todos.findById('1');
|
|
84
|
+
const allTodos = todos.selectAll();
|
|
85
|
+
const todoCount = todos.selectTotal();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Simple Async Actions
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const loadUsers = tree.asyncAction(async () => await api.getUsers(), {
|
|
92
|
+
onStart: () => ({ loading: true }),
|
|
93
|
+
onSuccess: (users) => ({ users, loading: false }),
|
|
94
|
+
onError: (error) => ({ loading: false, error: error.message }),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Use in components
|
|
98
|
+
async function handleLoadUsers() {
|
|
99
|
+
await loadUsers();
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Reactive Effects
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Create reactive effects
|
|
107
|
+
tree.effect((state) => {
|
|
108
|
+
console.log(`User: ${state.user.name}, Theme: ${state.settings.theme}`);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Manual subscriptions
|
|
112
|
+
const unsubscribe = tree.subscribe((state) => {
|
|
113
|
+
// Handle state changes
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 🎯 Core API Reference
|
|
118
|
+
|
|
119
|
+
### signalTree()
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const tree = signalTree(initialState, config?);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Tree Methods
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// State access
|
|
129
|
+
tree.$.property(); // Read signal value
|
|
130
|
+
tree.$.property.set(value); // Update signal
|
|
131
|
+
tree.unwrap(); // Get plain object
|
|
132
|
+
|
|
133
|
+
// Tree operations
|
|
134
|
+
tree.update(updater); // Update entire tree
|
|
135
|
+
tree.effect(fn); // Create reactive effects
|
|
136
|
+
tree.subscribe(fn); // Manual subscriptions
|
|
137
|
+
tree.destroy(); // Cleanup resources
|
|
138
|
+
|
|
139
|
+
// Entity management
|
|
140
|
+
tree.asCrud<T>(key); // Get entity helpers
|
|
141
|
+
|
|
142
|
+
// Async actions
|
|
143
|
+
tree.asyncAction(fn, config?); // Create async action
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 🔌 Extending with Optional Packages
|
|
147
|
+
|
|
148
|
+
SignalTree Core can be extended with additional features:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { signalTree } from '@signaltree/core';
|
|
152
|
+
import { withBatching } from '@signaltree/batching';
|
|
153
|
+
import { withMemoization } from '@signaltree/memoization';
|
|
154
|
+
import { withTimeTravel } from '@signaltree/time-travel';
|
|
155
|
+
|
|
156
|
+
// Compose features using pipe
|
|
157
|
+
const tree = signalTree(initialState).pipe(withBatching(), withMemoization(), withTimeTravel());
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Available Extensions
|
|
161
|
+
|
|
162
|
+
- **@signaltree/batching** (+1KB) - Batch multiple updates
|
|
163
|
+
- **@signaltree/memoization** (+2KB) - Intelligent caching & performance
|
|
164
|
+
- **@signaltree/middleware** (+1KB) - Middleware system & taps
|
|
165
|
+
- **@signaltree/async** (+2KB) - Advanced async actions & states
|
|
166
|
+
- **@signaltree/entities** (+2KB) - Advanced entity management
|
|
167
|
+
- **@signaltree/devtools** (+1KB) - Redux DevTools integration
|
|
168
|
+
- **@signaltree/time-travel** (+3KB) - Undo/redo functionality
|
|
169
|
+
- **@signaltree/ng-forms** (+3KB) - Complete Angular forms integration
|
|
170
|
+
- **@signaltree/presets** (+0.5KB) - Environment-based configurations
|
|
171
|
+
|
|
172
|
+
## 🎯 When to Use Core Only
|
|
173
|
+
|
|
174
|
+
Perfect for:
|
|
175
|
+
|
|
176
|
+
- ✅ Simple to medium applications
|
|
177
|
+
- ✅ Prototype and MVP development
|
|
178
|
+
- ✅ When bundle size is critical
|
|
179
|
+
- ✅ Learning signal-based state management
|
|
180
|
+
- ✅ Applications with basic state needs
|
|
181
|
+
|
|
182
|
+
Consider extensions when you need:
|
|
183
|
+
|
|
184
|
+
- ⚡ Performance optimization (batching, memoization)
|
|
185
|
+
- 🐛 Advanced debugging (devtools, time-travel)
|
|
186
|
+
- 📝 Complex forms (ng-forms)
|
|
187
|
+
- 🔧 Middleware patterns (middleware)
|
|
188
|
+
|
|
189
|
+
## 🔄 Migration from NgRx
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Step 1: Create parallel tree
|
|
193
|
+
const tree = signalTree(initialState);
|
|
194
|
+
|
|
195
|
+
// Step 2: Gradually migrate components
|
|
196
|
+
// Before (NgRx)
|
|
197
|
+
users$ = this.store.select(selectUsers);
|
|
198
|
+
|
|
199
|
+
// After (SignalTree)
|
|
200
|
+
users = this.tree.$.users;
|
|
201
|
+
|
|
202
|
+
// Step 3: Replace effects with async actions
|
|
203
|
+
// Before (NgRx)
|
|
204
|
+
loadUsers$ = createEffect(() =>
|
|
205
|
+
this.actions$.pipe(
|
|
206
|
+
ofType(loadUsers),
|
|
207
|
+
switchMap(() => this.api.getUsers())
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// After (SignalTree)
|
|
212
|
+
loadUsers = tree.asyncAction(() => api.getUsers(), {
|
|
213
|
+
onSuccess: (users, tree) => tree.$.users.set(users),
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 📖 Examples
|
|
218
|
+
|
|
219
|
+
### Simple Counter
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const counter = signalTree({ count: 0 });
|
|
223
|
+
|
|
224
|
+
// In component
|
|
225
|
+
@Component({
|
|
226
|
+
template: ` <button (click)="increment()">{{ counter.$.count() }}</button> `,
|
|
227
|
+
})
|
|
228
|
+
class CounterComponent {
|
|
229
|
+
counter = counter;
|
|
230
|
+
|
|
231
|
+
increment() {
|
|
232
|
+
this.counter.$.count.update((n) => n + 1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### User Management
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const userTree = signalTree({
|
|
241
|
+
users: [] as User[],
|
|
242
|
+
loading: false,
|
|
243
|
+
error: null as string | null,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const users = userTree.asCrud<User>('users');
|
|
247
|
+
|
|
248
|
+
const loadUsers = userTree.asyncAction(async () => await api.getUsers(), {
|
|
249
|
+
onStart: () => ({ loading: true }),
|
|
250
|
+
onSuccess: (users) => ({ users, loading: false, error: null }),
|
|
251
|
+
onError: (error) => ({ loading: false, error: error.message }),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// In component
|
|
255
|
+
@Component({
|
|
256
|
+
template: `
|
|
257
|
+
@if (userTree.$.loading()) {
|
|
258
|
+
<spinner />
|
|
259
|
+
} @else { @for (user of userTree.$.users(); track user.id) {
|
|
260
|
+
<user-card [user]="user" />
|
|
261
|
+
} }
|
|
262
|
+
`,
|
|
263
|
+
})
|
|
264
|
+
class UsersComponent {
|
|
265
|
+
userTree = userTree;
|
|
266
|
+
|
|
267
|
+
ngOnInit() {
|
|
268
|
+
loadUsers();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
addUser(userData: Partial<User>) {
|
|
272
|
+
users.add({ id: crypto.randomUUID(), ...userData });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## 🔗 Links
|
|
278
|
+
|
|
279
|
+
- [SignalTree Documentation](https://signaltree.io)
|
|
280
|
+
- [GitHub Repository](https://github.com/JBorgia/signaltree)
|
|
281
|
+
- [NPM Package](https://www.npmjs.com/package/@signaltree/core)
|
|
282
|
+
- [Interactive Examples](https://signaltree.io/examples)
|
|
283
|
+
|
|
284
|
+
## 📄 License
|
|
285
|
+
|
|
286
|
+
MIT License with AI Training Restriction - see the [LICENSE](../../LICENSE) file for details.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
**Ready to get started?** This core package provides everything you need for most applications. Add extensions only when you need them! 🚀
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaltree/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Lightweight, type-safe signal-based state management for Angular. Core package providing hierarchical signal trees, basic entity management, and async actions.",
|
|
4
5
|
"peerDependencies": {
|
|
5
6
|
"@angular/common": "^20.1.0",
|
|
6
7
|
"@angular/core": "^20.1.0"
|