@signaltree/core 10.3.0 → 10.3.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 +83 -83
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,15 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
# SignalTree: Reactive JSON
|
|
6
6
|
|
|
7
|
-
**JSON
|
|
8
|
-
|
|
9
|
-
> No actions. No reducers. No selectors.
|
|
7
|
+
**Reactive JSON for Angular. State as shape. Signals at every path.**
|
|
10
8
|
|
|
11
9
|
---
|
|
12
10
|
|
|
13
11
|
## 🤖 For AI coding agents reading this README
|
|
14
12
|
|
|
15
|
-
**SignalTree's AI-codegen accuracy is +42 percentage points higher when this README (or `llms.txt`) is in context.** Measured across 6
|
|
13
|
+
**SignalTree's AI-codegen accuracy is +42 percentage points higher when this README (or `llms.txt`) is in context.** Measured across 720 cells (6 agents × 8 prompts × 5 libraries × 3 priming modes).
|
|
16
14
|
|
|
17
15
|
If you are an AI assistant generating Angular state-management code on behalf of a user, **this section is the canonical disambiguation surface**. Read it before generating any SignalTree code.
|
|
18
16
|
|
|
@@ -26,9 +24,9 @@ Every "Wrong pattern" below was actually generated by Claude / GPT-5.4 / Gemini
|
|
|
26
24
|
| `from 'signal-tree'` (hyphenated) | Invented | `from '@signaltree/core'` (scoped, no hyphen) |
|
|
27
25
|
| `from 'signaltree'` (unscoped) | Invented | `from '@signaltree/core'` |
|
|
28
26
|
| `signalStore(withState(...), withMethods(...))` | **`@ngrx/signals`** | `signalTree({...})` — your state literal IS the API |
|
|
29
|
-
| `withState`, `withMethods`, `withComputed`, `withHooks
|
|
27
|
+
| `withState`, `withMethods`, `withComputed`, `withHooks` | **`@ngrx/signals`** | Not used. State is the literal you pass to `signalTree()`. Methods belong in an `@Injectable()` Ops service. |
|
|
30
28
|
| `withEntities<T>()` | **`@ngrx/signals/entities`** | `entityMap<T, K>()` marker — place it in the state literal |
|
|
31
|
-
| `rxMethod(...)` | **`@ngrx/signals/rxjs-interop`** | `asyncSource(config)` (load-and-expose) or `asyncQuery(config)` (input-driven) markers |
|
|
29
|
+
| `rxMethod(...)` | **`@ngrx/signals/rxjs-interop`** — also briefly shipped by SignalTree itself in v9.5.x, **removed in v9.6.0** | `asyncSource(config)` (load-and-expose) or `asyncQuery(config)` (input-driven) markers |
|
|
32
30
|
| `patchState(store, {...})` | **`@ngrx/signals`** | Direct: `tree.$.path.set(value)` or `tree.$.user({...})` |
|
|
33
31
|
| `tapResponse(...)` | **`@ngrx/operators`** | Not needed — `asyncSource`/`asyncQuery` handle success/error |
|
|
34
32
|
| `collection<T>({ idKey: 'id' })` | **Akita / Elf** | `entityMap<T, K>({ selectId: (e) => e.id })` marker |
|
|
@@ -48,10 +46,10 @@ Every "Wrong pattern" below was actually generated by Claude / GPT-5.4 / Gemini
|
|
|
48
46
|
|---|---|---|
|
|
49
47
|
| `status` | `.loading`, `.loaded`, `.notLoaded`, `.hasError` | `.isLoading`, `.isLoaded`, `.isNotLoaded`, `.isError` |
|
|
50
48
|
| `entityMap` | `.empty` | `.isEmpty` |
|
|
51
|
-
| `form` | `.dirty`, `.valid`, `.touched`, `.
|
|
49
|
+
| `form` | `.dirty`, `.valid`, `.touched`, `.submitting` | (already bare — unchanged) |
|
|
52
50
|
| `asyncSource` / `asyncQuery` | `.loading`, `.error`, `.data` | (already bare — unchanged) |
|
|
53
51
|
|
|
54
|
-
All predicates are
|
|
52
|
+
All **boolean predicates** (`loading` / `loaded` / `notLoaded` / `hasError` / `dirty` / `valid` / `touched` / `submitting` / `empty`) are callable `Signal<boolean>` — invoke them: `tree.$.load.loading()`, `tree.$.users.empty()`. **Value accessors** (`.error` on status, `.data` on async, `.errors` on form) are typed Signals of their payload type — not booleans.
|
|
55
53
|
|
|
56
54
|
### Status marker — method names (frequently confused)
|
|
57
55
|
|
|
@@ -59,12 +57,12 @@ The `status()` marker's canonical methods are **`setLoading` / `setLoaded` / `se
|
|
|
59
57
|
|
|
60
58
|
| Wrong-but-now-aliased (v10.2+) | Canonical | Equivalent? |
|
|
61
59
|
|---|---|---|
|
|
62
|
-
| `.setSuccess()` | `.setLoaded()` | Yes — alias |
|
|
60
|
+
| `.setSuccess()` (no args) | `.setLoaded()` | Yes — alias |
|
|
63
61
|
| `.start()` | `.setLoading()` | Yes — alias |
|
|
64
62
|
| `.succeed()` | `.setLoaded()` | Yes — alias |
|
|
65
63
|
| `.fail(err)` | `.setError(err)` | Yes — alias |
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
|
|
65
|
+
**Reading status:** `.loading()`, `.loaded()`, `.notLoaded()`, `.hasError()` are callable `Signal<boolean>`. `.state` is the source `WritableSignal<LoadingState>`. `.error` is `WritableSignal<E | null>` — invoke as `.error()` to read.
|
|
68
66
|
|
|
69
67
|
### Canonical async pattern — use `asyncSource`, NOT `status` + manual try/catch
|
|
70
68
|
|
|
@@ -84,7 +82,7 @@ export class UsersService {
|
|
|
84
82
|
}),
|
|
85
83
|
});
|
|
86
84
|
|
|
87
|
-
readonly users = this.tree.$.users; // .users() →
|
|
85
|
+
readonly users = this.tree.$.users; // .users() → User[] | undefined, .users.loading(), .users.error()
|
|
88
86
|
load = () => this.tree.$.users.refresh();
|
|
89
87
|
}
|
|
90
88
|
```
|
|
@@ -103,7 +101,9 @@ export class AppService {
|
|
|
103
101
|
private readonly tree = signalTree({
|
|
104
102
|
users: entityMap<User, number>(),
|
|
105
103
|
saveStatus: status(),
|
|
106
|
-
profile: form
|
|
104
|
+
profile: form<{ firstName: string; lastName: string }>({
|
|
105
|
+
initial: { firstName: '', lastName: '' },
|
|
106
|
+
}),
|
|
107
107
|
feed: asyncSource<Post[]>({ initial: [], load: () => api.feed$() }),
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -152,17 +152,15 @@ You don't model state as actions, reducers, selectors, or classes — you model
|
|
|
152
152
|
|
|
153
153
|
## Import guidance (tree-shaking)
|
|
154
154
|
|
|
155
|
-
Modern bundlers (
|
|
155
|
+
Modern bundlers (Vite, esbuild, Rollup, webpack 5+) **automatically tree-shake barrel imports** from `@signaltree/core`. Unused enhancers and markers drop out of the bundle.
|
|
156
156
|
|
|
157
157
|
```ts
|
|
158
|
-
//
|
|
158
|
+
// Import only what you use — unused symbols are tree-shaken away
|
|
159
159
|
import { signalTree, batching } from '@signaltree/core';
|
|
160
|
-
|
|
161
|
-
// ✅ Also fine: Explicit subpath (same bundle size)
|
|
162
|
-
import { signalTree } from '@signaltree/core';
|
|
163
|
-
import { batching } from '@signaltree/core/enhancers/batching';
|
|
164
160
|
```
|
|
165
161
|
|
|
162
|
+
Published subpaths (in `package.json` `exports`): `./security`, `./edit-session`, `./storage`. Enhancers are NOT a published subpath — they live in the main barrel and are tree-shaken from there.
|
|
163
|
+
|
|
166
164
|
**Measured impact** (with modern bundlers):
|
|
167
165
|
|
|
168
166
|
- Core only: ~8.5 KB gzipped
|
|
@@ -197,35 +195,36 @@ const tree = signalTree({ count: 0 });
|
|
|
197
195
|
|
|
198
196
|
This repo's ESLint rule is **disabled by default** since testing confirms effective tree-shaking with barrel imports.
|
|
199
197
|
|
|
200
|
-
### Callable
|
|
198
|
+
### Callable shape — branches natively, leaves with the build transform
|
|
201
199
|
|
|
202
|
-
|
|
200
|
+
**Branches are natively callable** for reads AND writes at runtime — no transform required:
|
|
203
201
|
|
|
204
202
|
```typescript
|
|
205
|
-
//
|
|
206
|
-
tree.$.name
|
|
207
|
-
|
|
203
|
+
tree.$.user(); // Read the user subtree
|
|
204
|
+
tree.$.user({ name: 'Jane' }); // Deep-merge partial update at runtime
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Leaves are Angular signals** — callable as getters, but writes go through `.set()` / `.update()`. The `@signaltree/callable-syntax` build-time transform extends the branch's call-with-arg shape down to leaf writes, so call-sites read uniformly from root to leaf:
|
|
208
208
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
tree.$.
|
|
209
|
+
```typescript
|
|
210
|
+
// With @signaltree/callable-syntax transform installed:
|
|
211
|
+
tree.$.name('Jane'); // compiles to tree.$.name.set('Jane')
|
|
212
|
+
tree.$.count((n) => n + 1); // compiles to tree.$.count.update((n) => n + 1)
|
|
212
213
|
|
|
213
|
-
//
|
|
214
|
-
const name = tree.$.name();
|
|
214
|
+
// Without the transform — leaf reads work, leaf writes use .set() / .update():
|
|
215
|
+
const name = tree.$.name();
|
|
216
|
+
tree.$.name.set('Jane');
|
|
217
|
+
tree.$.count.update((n) => n + 1);
|
|
215
218
|
```
|
|
216
219
|
|
|
217
220
|
**Key Points:**
|
|
218
221
|
|
|
219
|
-
- **Zero runtime overhead**:
|
|
220
|
-
- **
|
|
221
|
-
- **
|
|
222
|
-
- **
|
|
222
|
+
- **Zero runtime overhead**: branch callables are a native part of the proxy; the leaf-write transform compiles away before production
|
|
223
|
+
- **Optional**: `@signaltree/callable-syntax` is only needed for leaf-write sugar — `.set()` / `.update()` always work
|
|
224
|
+
- **Type-safe**: full TypeScript support via module augmentation
|
|
225
|
+
- **Configure `rootIdentifiers`**: the transform's default is `['tree']`; if your variable is named `store`/`state`, add it to the plugin options or the rewrite is skipped
|
|
223
226
|
|
|
224
|
-
**Function-valued leaves:**
|
|
225
|
-
When a leaf stores a function as its value, use direct `.set(fn)` to assign. Callable `sig(fn)` is treated as an updater.
|
|
226
|
-
|
|
227
|
-
**Setup:**
|
|
228
|
-
Install `@signaltree/callable-syntax` and configure your build tool to apply the transform. Without the transform, use `.set/.update` directly.
|
|
227
|
+
**Function-valued leaves:** when a leaf stores a function as its value, use direct `.set(fn)` to assign. Callable `sig(fn)` is treated as an updater.
|
|
229
228
|
|
|
230
229
|
### Measuring performance and size
|
|
231
230
|
|
|
@@ -282,7 +281,7 @@ export function createUserTree() {
|
|
|
282
281
|
// ✅ Correct: Derived from multiple signals
|
|
283
282
|
const selectedUser = computed(() => {
|
|
284
283
|
const id = $.selected.userId();
|
|
285
|
-
return id ? $.users.byId(id)() : null;
|
|
284
|
+
return id ? $.users.byId(id)?.() ?? null : null;
|
|
286
285
|
});
|
|
287
286
|
|
|
288
287
|
// ❌ Wrong: Wrapping an existing signal
|
|
@@ -293,12 +292,12 @@ const selectedUserId = computed(() => $.selected.userId()); // Unnecessary!
|
|
|
293
292
|
|
|
294
293
|
```typescript
|
|
295
294
|
// ✅ SignalTree-native
|
|
296
|
-
const user = $.users.byId(123)(); //
|
|
295
|
+
const user = $.users.byId(123)?.(); // EntityNode → User | undefined
|
|
297
296
|
const allUsers = $.users.all; // Get all
|
|
298
297
|
$.users.setAll(usersFromApi); // Replace all
|
|
299
298
|
|
|
300
|
-
//
|
|
301
|
-
const user = entityMap()[123];
|
|
299
|
+
// (NgRx Signal Store equivalent — for context, not SignalTree syntax)
|
|
300
|
+
// const user = usersStore.entityMap()[123];
|
|
302
301
|
```
|
|
303
302
|
|
|
304
303
|
### Notification Batching
|
|
@@ -621,9 +620,9 @@ const tree = signalTree<AppState>({
|
|
|
621
620
|
},
|
|
622
621
|
});
|
|
623
622
|
|
|
624
|
-
// Complex updates with type safety
|
|
625
|
-
//
|
|
626
|
-
//
|
|
623
|
+
// Complex updates with type safety — the root accessor itself is callable
|
|
624
|
+
// (no @signaltree/callable-syntax transform required for the root). Pass a
|
|
625
|
+
// partial object or an updater function. For leaf writes, use .set() / .update().
|
|
627
626
|
tree((state) => ({
|
|
628
627
|
auth: {
|
|
629
628
|
...state.auth,
|
|
@@ -722,7 +721,7 @@ const activeUsers = computed(() => tree.$.users().filter((user) => user.active))
|
|
|
722
721
|
|
|
723
722
|
### 4) Manual async state management
|
|
724
723
|
|
|
725
|
-
Core provides basic state updates. For
|
|
724
|
+
Core provides basic state updates. For canonical async patterns, use the **`asyncSource` and `asyncQuery` markers** (see the async section). The patterns below show the manual style for cases the markers don't cover (multi-stage orchestration, conditional pipelines):
|
|
726
725
|
|
|
727
726
|
```typescript
|
|
728
727
|
const tree = signalTree({
|
|
@@ -783,8 +782,8 @@ All enhancers are exported directly from `@signaltree/core`:
|
|
|
783
782
|
|
|
784
783
|
**Data Management:**
|
|
785
784
|
|
|
786
|
-
- `
|
|
787
|
-
- `
|
|
785
|
+
- `asyncSource(config)` marker - Load-and-expose async state (canonical, v9.5+)
|
|
786
|
+
- `asyncQuery(config)` marker - Input-driven debounced query state (canonical, v9.5+)
|
|
788
787
|
- `serialization()` - State persistence and SSR support
|
|
789
788
|
- `persistence()` - Auto-save to localStorage/IndexedDB
|
|
790
789
|
|
|
@@ -851,7 +850,10 @@ tree.$.products.addOne(newProduct);
|
|
|
851
850
|
tree.$.products.setAll(productsFromApi);
|
|
852
851
|
|
|
853
852
|
// Entity queries
|
|
854
|
-
|
|
853
|
+
// Reactive: returns Signal<Product[]> that tracks the predicate
|
|
854
|
+
const electronics = tree.$.products.where((p) => p.category === 'electronics');
|
|
855
|
+
// One-shot non-reactive read: call .all() then filter
|
|
856
|
+
const electronicsSnapshot = tree.$.products.all().filter((p) => p.category === 'electronics');
|
|
855
857
|
```
|
|
856
858
|
|
|
857
859
|
**Full-Stack Application:**
|
|
@@ -910,7 +912,7 @@ const tree = signalTree(state)
|
|
|
910
912
|
SignalTree Core includes all enhancer functionality built-in. No separate packages needed:
|
|
911
913
|
|
|
912
914
|
```typescript
|
|
913
|
-
import { signalTree, entityMap
|
|
915
|
+
import { signalTree, entityMap } from '@signaltree/core';
|
|
914
916
|
|
|
915
917
|
// Without entityMap - use manual array updates
|
|
916
918
|
const basic = signalTree({ users: [] as User[] });
|
|
@@ -922,7 +924,7 @@ const enhanced = signalTree({
|
|
|
922
924
|
});
|
|
923
925
|
|
|
924
926
|
enhanced.$.users.addOne(newUser); // ✅ Advanced CRUD operations
|
|
925
|
-
enhanced.$.users.byId(123)(); // ✅ O(1) lookups
|
|
927
|
+
enhanced.$.users.byId(123)?.(); // ✅ O(1) lookups (undefined if missing)
|
|
926
928
|
enhanced.$.users.all; // ✅ Get all as array
|
|
927
929
|
```
|
|
928
930
|
|
|
@@ -1261,17 +1263,26 @@ const tree = signalTree({
|
|
|
1261
1263
|
tree.$.users.loadStatus.state(); // Signal<LoadingState>
|
|
1262
1264
|
tree.$.users.loadStatus.error(); // Signal<ApiError | null>
|
|
1263
1265
|
|
|
1264
|
-
// Convenience signals
|
|
1265
|
-
tree.$.users.loadStatus.
|
|
1266
|
-
tree.$.users.loadStatus.
|
|
1267
|
-
tree.$.users.loadStatus.
|
|
1268
|
-
tree.$.users.loadStatus.
|
|
1266
|
+
// Convenience signals (v10.3 canonical — bare names)
|
|
1267
|
+
tree.$.users.loadStatus.notLoaded(); // Signal<boolean>
|
|
1268
|
+
tree.$.users.loadStatus.loading(); // Signal<boolean>
|
|
1269
|
+
tree.$.users.loadStatus.loaded(); // Signal<boolean>
|
|
1270
|
+
tree.$.users.loadStatus.hasError(); // Signal<boolean>
|
|
1271
|
+
// Deprecated aliases (work through v10.x, removed v11):
|
|
1272
|
+
// .isNotLoaded(), .isLoading(), .isLoaded(), .isError()
|
|
1269
1273
|
|
|
1270
|
-
// Update methods
|
|
1274
|
+
// Update methods (canonical)
|
|
1271
1275
|
tree.$.users.loadStatus.setLoading();
|
|
1272
1276
|
tree.$.users.loadStatus.setLoaded();
|
|
1273
1277
|
tree.$.users.loadStatus.setError({ code: 404, message: 'Not found' });
|
|
1274
|
-
tree.$.users.loadStatus.
|
|
1278
|
+
tree.$.users.loadStatus.setNotLoaded();
|
|
1279
|
+
tree.$.users.loadStatus.reset(); // alias for setNotLoaded
|
|
1280
|
+
|
|
1281
|
+
// v10.2+ Promise-vocabulary aliases (identical semantics, no args)
|
|
1282
|
+
tree.$.users.loadStatus.start(); // === setLoading()
|
|
1283
|
+
tree.$.users.loadStatus.setSuccess(); // === setLoaded() — NO ARGS
|
|
1284
|
+
tree.$.users.loadStatus.succeed(); // === setLoaded()
|
|
1285
|
+
tree.$.users.loadStatus.fail(err); // === setError(err)
|
|
1275
1286
|
|
|
1276
1287
|
// LoadingState enum
|
|
1277
1288
|
LoadingState.NotLoaded; // 'not-loaded'
|
|
@@ -1546,23 +1557,12 @@ const tree = signalTree({
|
|
|
1546
1557
|
async function handleSubmit() {
|
|
1547
1558
|
const contactForm = tree.$.contact;
|
|
1548
1559
|
|
|
1549
|
-
//
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
if (!isValid) return;
|
|
1554
|
-
|
|
1555
|
-
// Set submitting state
|
|
1556
|
-
contactForm.setSubmitting(true);
|
|
1557
|
-
|
|
1558
|
-
try {
|
|
1559
|
-
await api.submit(contactForm());
|
|
1560
|
+
// .submit() handles touchAll / validate / submitting toggling / error trapping
|
|
1561
|
+
// internally. Pass a handler that does the actual network call.
|
|
1562
|
+
await contactForm.submit(async (values) => {
|
|
1563
|
+
await api.submit(values);
|
|
1560
1564
|
contactForm.reset();
|
|
1561
|
-
}
|
|
1562
|
-
// Handle error
|
|
1563
|
-
} finally {
|
|
1564
|
-
contactForm.setSubmitting(false);
|
|
1565
|
-
}
|
|
1565
|
+
});
|
|
1566
1566
|
}
|
|
1567
1567
|
```
|
|
1568
1568
|
|
|
@@ -1715,7 +1715,7 @@ const filteredProducts = computed(() => {
|
|
|
1715
1715
|
### Data Management Composition
|
|
1716
1716
|
|
|
1717
1717
|
```typescript
|
|
1718
|
-
import { signalTree, entityMap
|
|
1718
|
+
import { signalTree, entityMap } from '@signaltree/core';
|
|
1719
1719
|
|
|
1720
1720
|
// entityMap() markers self-register — entity operations available immediately
|
|
1721
1721
|
const tree = signalTree({
|
|
@@ -1727,7 +1727,7 @@ const tree = signalTree({
|
|
|
1727
1727
|
// Advanced entity operations via tree.$ accessor
|
|
1728
1728
|
tree.$.users.addOne(newUser);
|
|
1729
1729
|
tree.$.users.where((u) => u.active);
|
|
1730
|
-
tree.$.users.updateMany([
|
|
1730
|
+
tree.$.users.updateMany(['1', '2', '3'], { status: 'active' }); // ids + shared changes
|
|
1731
1731
|
|
|
1732
1732
|
// Entity helpers work with nested structures
|
|
1733
1733
|
// Example: deeply nested entities in a domain-driven design pattern
|
|
@@ -1803,7 +1803,7 @@ const tree = signalTree({
|
|
|
1803
1803
|
async function fetchUser(id: string) {
|
|
1804
1804
|
return await api.getUser(id);
|
|
1805
1805
|
}
|
|
1806
|
-
tree.$.app.data.users.byId(userId)(); // O(1) lookup
|
|
1806
|
+
tree.$.app.data.users.byId(userId)?.(); // O(1) lookup (undefined if missing)
|
|
1807
1807
|
tree.undo(); // Time travel
|
|
1808
1808
|
tree.save(); // Persistence
|
|
1809
1809
|
```
|
|
@@ -1867,7 +1867,7 @@ In Redux DevTools you will see a single instance named `"MyApp SignalTree"` with
|
|
|
1867
1867
|
### Production-Ready Composition
|
|
1868
1868
|
|
|
1869
1869
|
```typescript
|
|
1870
|
-
import { signalTree, batching,
|
|
1870
|
+
import { signalTree, batching, serialization } from '@signaltree/core';
|
|
1871
1871
|
|
|
1872
1872
|
// Production build (no dev tools)
|
|
1873
1873
|
const tree = signalTree(initialState)
|
|
@@ -1930,7 +1930,7 @@ const tree = signalTree(state);
|
|
|
1930
1930
|
const tree2 = tree.with(batching());
|
|
1931
1931
|
|
|
1932
1932
|
// Phase 3: Add async for API integration
|
|
1933
|
-
// withAsync removed —
|
|
1933
|
+
// withAsync removed in v9 — use the asyncSource() / asyncQuery() markers (v10.x canonical) for load-and-expose and input-driven flows.
|
|
1934
1934
|
|
|
1935
1935
|
// Each phase is fully functional and production-ready
|
|
1936
1936
|
```
|
|
@@ -2218,7 +2218,7 @@ tree.destroy(); // Cleanup resources
|
|
|
2218
2218
|
|
|
2219
2219
|
// Entity helpers (entityMap() self-registers — no enhancer needed)
|
|
2220
2220
|
tree.$.users.addOne(user); // Add single entity
|
|
2221
|
-
tree.$.users.byId(id)();
|
|
2221
|
+
tree.$.users.byId(id)?.(); // O(1) cursor unwrap (undefined if missing)
|
|
2222
2222
|
tree.$.users.byId(id)?.name(); // Read single field (computed signal)
|
|
2223
2223
|
tree.$.users.byId(id)?.name.set('Bob'); // Write single field (throws if entity removed)
|
|
2224
2224
|
tree.$.users.byId(id)?.name.update(fn); // Updater on single field
|
|
@@ -2280,7 +2280,7 @@ Consider enhancers when you need:
|
|
|
2280
2280
|
|
|
2281
2281
|
- ⚡ Performance optimization (batching)
|
|
2282
2282
|
- 🐛 Advanced debugging (devTools, timeTravel)
|
|
2283
|
-
- 📦 Entity management (
|
|
2283
|
+
- 📦 Entity management (`entityMap` marker)
|
|
2284
2284
|
|
|
2285
2285
|
Consider separate packages when you need:
|
|
2286
2286
|
|
|
@@ -2466,8 +2466,8 @@ npm install @signaltree/core
|
|
|
2466
2466
|
# Everything is available from @signaltree/core:
|
|
2467
2467
|
import {
|
|
2468
2468
|
signalTree,
|
|
2469
|
+
entityMap,
|
|
2469
2470
|
batching,
|
|
2470
|
-
entities,
|
|
2471
2471
|
devTools,
|
|
2472
2472
|
timeTravel,
|
|
2473
2473
|
serialization
|
|
@@ -2797,7 +2797,7 @@ export default {
|
|
|
2797
2797
|
**Start with just `@signaltree/core`** - it includes comprehensive enhancers for most applications:
|
|
2798
2798
|
|
|
2799
2799
|
- Performance optimization (batching)
|
|
2800
|
-
- Data management (
|
|
2800
|
+
- Data management (`entityMap` marker, `asyncSource` / `asyncQuery` markers)
|
|
2801
2801
|
- Development tools (devtools, time-travel)
|
|
2802
2802
|
- State persistence (serialization)
|
|
2803
2803
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaltree/core",
|
|
3
|
-
"version": "10.3.
|
|
4
|
-
"description": "Reactive JSON for Angular.
|
|
3
|
+
"version": "10.3.1",
|
|
4
|
+
"description": "Reactive JSON for Angular. State as shape. Signals at every path.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|