@signaltree/core 6.2.3 → 6.2.4

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.
@@ -1,120 +1,91 @@
1
1
  import { signal, computed } from '@angular/core';
2
2
 
3
- class EntitySignalImpl {
4
- pathNotifier;
5
- basePath;
6
- storage = new Map();
7
- allSignal;
8
- countSignal;
9
- idsSignal;
10
- mapSignal;
11
- nodeCache = new Map();
12
- selectId;
13
- tapHandlers = [];
14
- interceptHandlers = [];
15
- constructor(config, pathNotifier, basePath) {
16
- this.pathNotifier = pathNotifier;
17
- this.basePath = basePath;
18
- this.selectId = config.selectId ?? (entity => entity['id']);
19
- this.allSignal = signal([]);
20
- this.countSignal = signal(0);
21
- this.idsSignal = signal([]);
22
- this.mapSignal = signal(new Map());
3
+ function createEntitySignal(config, pathNotifier, basePath) {
4
+ const storage = new Map();
5
+ const allSignal = signal([]);
6
+ const countSignal = signal(0);
7
+ const idsSignal = signal([]);
8
+ const mapSignal = signal(new Map());
9
+ const nodeCache = new Map();
10
+ const selectId = config.selectId ?? (entity => entity['id']);
11
+ const tapHandlers = [];
12
+ const interceptHandlers = [];
13
+ function updateSignals() {
14
+ const entities = Array.from(storage.values());
15
+ const ids = Array.from(storage.keys());
16
+ const map = new Map(storage);
17
+ allSignal.set(entities);
18
+ countSignal.set(entities.length);
19
+ idsSignal.set(ids);
20
+ mapSignal.set(map);
23
21
  }
24
- byId(id) {
25
- const entity = this.storage.get(id);
26
- if (!entity) return undefined;
27
- return this.getOrCreateNode(id, entity);
22
+ function createEntityNode(id, entity) {
23
+ const node = () => storage.get(id);
24
+ for (const key of Object.keys(entity)) {
25
+ Object.defineProperty(node, key, {
26
+ get: () => {
27
+ const current = storage.get(id);
28
+ const value = current?.[key];
29
+ return () => value;
30
+ },
31
+ enumerable: true,
32
+ configurable: true
33
+ });
34
+ }
35
+ return node;
28
36
  }
29
- byIdOrFail(id) {
30
- const node = this.byId(id);
37
+ function getOrCreateNode(id, entity) {
38
+ let node = nodeCache.get(id);
31
39
  if (!node) {
32
- throw new Error(`Entity with id ${String(id)} not found`);
40
+ node = createEntityNode(id, entity);
41
+ nodeCache.set(id, node);
33
42
  }
34
43
  return node;
35
44
  }
36
- get all() {
37
- return this.allSignal;
38
- }
39
- get count() {
40
- return this.countSignal;
41
- }
42
- get ids() {
43
- return this.idsSignal;
44
- }
45
- get map() {
46
- return this.mapSignal;
47
- }
48
- has(id) {
49
- return computed(() => this.storage.has(id));
50
- }
51
- get isEmpty() {
52
- return computed(() => this.storage.size === 0);
53
- }
54
- where(predicate) {
55
- return computed(() => {
56
- const result = [];
57
- for (const entity of this.storage.values()) {
58
- if (predicate(entity)) {
59
- result.push(entity);
60
- }
61
- }
62
- return result;
63
- });
64
- }
65
- find(predicate) {
66
- return computed(() => {
67
- for (const entity of this.storage.values()) {
68
- if (predicate(entity)) {
69
- return entity;
70
- }
45
+ const api = {
46
+ byId(id) {
47
+ const entity = storage.get(id);
48
+ if (!entity) return undefined;
49
+ return getOrCreateNode(id, entity);
50
+ },
51
+ byIdOrFail(id) {
52
+ const node = api.byId(id);
53
+ if (!node) {
54
+ throw new Error(`Entity with id ${String(id)} not found`);
71
55
  }
72
- return undefined;
73
- });
74
- }
75
- addOne(entity, opts) {
76
- const id = opts?.selectId?.(entity) ?? this.selectId(entity);
77
- if (this.storage.has(id)) {
78
- throw new Error(`Entity with id ${String(id)} already exists`);
79
- }
80
- let transformedEntity = entity;
81
- for (const handler of this.interceptHandlers) {
82
- const ctx = {
83
- block: reason => {
84
- throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
85
- },
86
- transform: value => {
87
- transformedEntity = value;
88
- },
89
- blocked: false,
90
- blockReason: undefined
91
- };
92
- handler.onAdd?.(entity, ctx);
93
- }
94
- this.storage.set(id, transformedEntity);
95
- this.nodeCache.delete(id);
96
- this.updateSignals();
97
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, transformedEntity, undefined);
98
- for (const handler of this.tapHandlers) {
99
- handler.onAdd?.(transformedEntity, id);
100
- }
101
- return id;
102
- }
103
- addMany(entities, opts) {
104
- const idsToAdd = [];
105
- for (const entity of entities) {
106
- const id = opts?.selectId?.(entity) ?? this.selectId(entity);
107
- if (this.storage.has(id)) {
56
+ return node;
57
+ },
58
+ get all() {
59
+ return allSignal;
60
+ },
61
+ get count() {
62
+ return countSignal;
63
+ },
64
+ get ids() {
65
+ return idsSignal;
66
+ },
67
+ get map() {
68
+ return mapSignal;
69
+ },
70
+ has(id) {
71
+ return computed(() => mapSignal().has(id));
72
+ },
73
+ get isEmpty() {
74
+ return computed(() => countSignal() === 0);
75
+ },
76
+ where(predicate) {
77
+ return computed(() => allSignal().filter(predicate));
78
+ },
79
+ find(predicate) {
80
+ return computed(() => allSignal().find(predicate));
81
+ },
82
+ addOne(entity, opts) {
83
+ const id = opts?.selectId?.(entity) ?? selectId(entity);
84
+ if (storage.has(id)) {
108
85
  throw new Error(`Entity with id ${String(id)} already exists`);
109
86
  }
110
- idsToAdd.push(id);
111
- }
112
- const addedEntities = [];
113
- for (let i = 0; i < entities.length; i++) {
114
- const entity = entities[i];
115
- const id = idsToAdd[i];
116
87
  let transformedEntity = entity;
117
- for (const handler of this.interceptHandlers) {
88
+ for (const handler of interceptHandlers) {
118
89
  const ctx = {
119
90
  block: reason => {
120
91
  throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
@@ -127,73 +98,74 @@ class EntitySignalImpl {
127
98
  };
128
99
  handler.onAdd?.(entity, ctx);
129
100
  }
130
- this.storage.set(id, transformedEntity);
131
- this.nodeCache.delete(id);
132
- addedEntities.push({
101
+ storage.set(id, transformedEntity);
102
+ nodeCache.delete(id);
103
+ updateSignals();
104
+ pathNotifier.notify(`${basePath}.${String(id)}`, transformedEntity, undefined);
105
+ for (const handler of tapHandlers) {
106
+ handler.onAdd?.(transformedEntity, id);
107
+ }
108
+ return id;
109
+ },
110
+ addMany(entities, opts) {
111
+ const idsToAdd = [];
112
+ for (const entity of entities) {
113
+ const id = opts?.selectId?.(entity) ?? selectId(entity);
114
+ if (storage.has(id)) {
115
+ throw new Error(`Entity with id ${String(id)} already exists`);
116
+ }
117
+ idsToAdd.push(id);
118
+ }
119
+ const addedEntities = [];
120
+ for (let i = 0; i < entities.length; i++) {
121
+ const entity = entities[i];
122
+ const id = idsToAdd[i];
123
+ let transformedEntity = entity;
124
+ for (const handler of interceptHandlers) {
125
+ const ctx = {
126
+ block: reason => {
127
+ throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
128
+ },
129
+ transform: value => {
130
+ transformedEntity = value;
131
+ },
132
+ blocked: false,
133
+ blockReason: undefined
134
+ };
135
+ handler.onAdd?.(entity, ctx);
136
+ }
137
+ storage.set(id, transformedEntity);
138
+ nodeCache.delete(id);
139
+ addedEntities.push({
140
+ id,
141
+ entity: transformedEntity
142
+ });
143
+ }
144
+ updateSignals();
145
+ for (const {
133
146
  id,
134
- entity: transformedEntity
135
- });
136
- }
137
- this.updateSignals();
138
- for (const {
139
- id,
140
- entity
141
- } of addedEntities) {
142
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, entity, undefined);
143
- }
144
- for (const {
145
- id,
146
- entity
147
- } of addedEntities) {
148
- for (const handler of this.tapHandlers) {
149
- handler.onAdd?.(entity, id);
147
+ entity
148
+ } of addedEntities) {
149
+ pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
150
150
  }
151
- }
152
- return idsToAdd;
153
- }
154
- updateOne(id, changes) {
155
- const entity = this.storage.get(id);
156
- if (!entity) {
157
- throw new Error(`Entity with id ${String(id)} not found`);
158
- }
159
- const prev = entity;
160
- let transformedChanges = changes;
161
- for (const handler of this.interceptHandlers) {
162
- const ctx = {
163
- block: reason => {
164
- throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
165
- },
166
- transform: value => {
167
- transformedChanges = value;
168
- },
169
- blocked: false,
170
- blockReason: undefined
171
- };
172
- handler.onUpdate?.(id, changes, ctx);
173
- }
174
- const finalUpdated = {
175
- ...entity,
176
- ...transformedChanges
177
- };
178
- this.storage.set(id, finalUpdated);
179
- this.nodeCache.delete(id);
180
- this.updateSignals();
181
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, finalUpdated, prev);
182
- for (const handler of this.tapHandlers) {
183
- handler.onUpdate?.(id, transformedChanges, finalUpdated);
184
- }
185
- }
186
- updateMany(ids, changes) {
187
- if (ids.length === 0) return;
188
- const updatedEntities = [];
189
- for (const id of ids) {
190
- const entity = this.storage.get(id);
151
+ for (const {
152
+ id,
153
+ entity
154
+ } of addedEntities) {
155
+ for (const handler of tapHandlers) {
156
+ handler.onAdd?.(entity, id);
157
+ }
158
+ }
159
+ return idsToAdd;
160
+ },
161
+ updateOne(id, changes) {
162
+ const entity = storage.get(id);
191
163
  if (!entity) {
192
164
  throw new Error(`Entity with id ${String(id)} not found`);
193
165
  }
194
166
  const prev = entity;
195
167
  let transformedChanges = changes;
196
- for (const handler of this.interceptHandlers) {
168
+ for (const handler of interceptHandlers) {
197
169
  const ctx = {
198
170
  block: reason => {
199
171
  throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
@@ -210,78 +182,86 @@ class EntitySignalImpl {
210
182
  ...entity,
211
183
  ...transformedChanges
212
184
  };
213
- this.storage.set(id, finalUpdated);
214
- this.nodeCache.delete(id);
215
- updatedEntities.push({
185
+ storage.set(id, finalUpdated);
186
+ nodeCache.delete(id);
187
+ updateSignals();
188
+ pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
189
+ for (const handler of tapHandlers) {
190
+ handler.onUpdate?.(id, transformedChanges, finalUpdated);
191
+ }
192
+ },
193
+ updateMany(ids, changes) {
194
+ if (ids.length === 0) return;
195
+ const updatedEntities = [];
196
+ for (const id of ids) {
197
+ const entity = storage.get(id);
198
+ if (!entity) {
199
+ throw new Error(`Entity with id ${String(id)} not found`);
200
+ }
201
+ const prev = entity;
202
+ let transformedChanges = changes;
203
+ for (const handler of interceptHandlers) {
204
+ const ctx = {
205
+ block: reason => {
206
+ throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
207
+ },
208
+ transform: value => {
209
+ transformedChanges = value;
210
+ },
211
+ blocked: false,
212
+ blockReason: undefined
213
+ };
214
+ handler.onUpdate?.(id, changes, ctx);
215
+ }
216
+ const finalUpdated = {
217
+ ...entity,
218
+ ...transformedChanges
219
+ };
220
+ storage.set(id, finalUpdated);
221
+ nodeCache.delete(id);
222
+ updatedEntities.push({
223
+ id,
224
+ prev,
225
+ finalUpdated,
226
+ transformedChanges
227
+ });
228
+ }
229
+ updateSignals();
230
+ for (const {
216
231
  id,
217
232
  prev,
218
- finalUpdated,
219
- transformedChanges
220
- });
221
- }
222
- this.updateSignals();
223
- for (const {
224
- id,
225
- prev,
226
- finalUpdated
227
- } of updatedEntities) {
228
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, finalUpdated, prev);
229
- }
230
- for (const {
231
- id,
232
- transformedChanges,
233
- finalUpdated
234
- } of updatedEntities) {
235
- for (const handler of this.tapHandlers) {
236
- handler.onUpdate?.(id, transformedChanges, finalUpdated);
233
+ finalUpdated
234
+ } of updatedEntities) {
235
+ pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
237
236
  }
238
- }
239
- }
240
- updateWhere(predicate, changes) {
241
- const idsToUpdate = [];
242
- for (const [id, entity] of this.storage) {
243
- if (predicate(entity)) {
244
- idsToUpdate.push(id);
237
+ for (const {
238
+ id,
239
+ transformedChanges,
240
+ finalUpdated
241
+ } of updatedEntities) {
242
+ for (const handler of tapHandlers) {
243
+ handler.onUpdate?.(id, transformedChanges, finalUpdated);
244
+ }
245
245
  }
246
- }
247
- if (idsToUpdate.length > 0) {
248
- this.updateMany(idsToUpdate, changes);
249
- }
250
- return idsToUpdate.length;
251
- }
252
- removeOne(id) {
253
- const entity = this.storage.get(id);
254
- if (!entity) {
255
- throw new Error(`Entity with id ${String(id)} not found`);
256
- }
257
- for (const handler of this.interceptHandlers) {
258
- const ctx = {
259
- block: reason => {
260
- throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
261
- },
262
- transform: () => {},
263
- blocked: false,
264
- blockReason: undefined
265
- };
266
- handler.onRemove?.(id, entity, ctx);
267
- }
268
- this.storage.delete(id);
269
- this.nodeCache.delete(id);
270
- this.updateSignals();
271
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, undefined, entity);
272
- for (const handler of this.tapHandlers) {
273
- handler.onRemove?.(id, entity);
274
- }
275
- }
276
- removeMany(ids) {
277
- if (ids.length === 0) return;
278
- const entitiesToRemove = [];
279
- for (const id of ids) {
280
- const entity = this.storage.get(id);
246
+ },
247
+ updateWhere(predicate, changes) {
248
+ const idsToUpdate = [];
249
+ for (const [id, entity] of storage) {
250
+ if (predicate(entity)) {
251
+ idsToUpdate.push(id);
252
+ }
253
+ }
254
+ if (idsToUpdate.length > 0) {
255
+ api.updateMany(idsToUpdate, changes);
256
+ }
257
+ return idsToUpdate.length;
258
+ },
259
+ removeOne(id) {
260
+ const entity = storage.get(id);
281
261
  if (!entity) {
282
262
  throw new Error(`Entity with id ${String(id)} not found`);
283
263
  }
284
- for (const handler of this.interceptHandlers) {
264
+ for (const handler of interceptHandlers) {
285
265
  const ctx = {
286
266
  block: reason => {
287
267
  throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
@@ -292,267 +272,263 @@ class EntitySignalImpl {
292
272
  };
293
273
  handler.onRemove?.(id, entity, ctx);
294
274
  }
295
- entitiesToRemove.push({
296
- id,
297
- entity
298
- });
299
- }
300
- for (const {
301
- id
302
- } of entitiesToRemove) {
303
- this.storage.delete(id);
304
- this.nodeCache.delete(id);
305
- }
306
- this.updateSignals();
307
- for (const {
308
- id,
309
- entity
310
- } of entitiesToRemove) {
311
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, undefined, entity);
312
- }
313
- for (const {
314
- id,
315
- entity
316
- } of entitiesToRemove) {
317
- for (const handler of this.tapHandlers) {
275
+ storage.delete(id);
276
+ nodeCache.delete(id);
277
+ updateSignals();
278
+ pathNotifier.notify(`${basePath}.${String(id)}`, undefined, entity);
279
+ for (const handler of tapHandlers) {
318
280
  handler.onRemove?.(id, entity);
319
281
  }
320
- }
321
- }
322
- removeWhere(predicate) {
323
- const idsToRemove = [];
324
- for (const [id, entity] of this.storage) {
325
- if (predicate(entity)) {
326
- idsToRemove.push(id);
327
- }
328
- }
329
- if (idsToRemove.length > 0) {
330
- this.removeMany(idsToRemove);
331
- }
332
- return idsToRemove.length;
333
- }
334
- upsertOne(entity, opts) {
335
- const id = opts?.selectId?.(entity) ?? this.selectId(entity);
336
- if (this.storage.has(id)) {
337
- this.updateOne(id, entity);
338
- } else {
339
- this.addOne(entity, opts);
340
- }
341
- return id;
342
- }
343
- upsertMany(entities, opts) {
344
- if (entities.length === 0) return [];
345
- const toAdd = [];
346
- const toUpdate = [];
347
- for (const entity of entities) {
348
- const id = opts?.selectId?.(entity) ?? this.selectId(entity);
349
- if (this.storage.has(id)) {
350
- toUpdate.push({
351
- entity,
282
+ },
283
+ removeMany(ids) {
284
+ if (ids.length === 0) return;
285
+ const entitiesToRemove = [];
286
+ for (const id of ids) {
287
+ const entity = storage.get(id);
288
+ if (!entity) {
289
+ throw new Error(`Entity with id ${String(id)} not found`);
290
+ }
291
+ for (const handler of interceptHandlers) {
292
+ const ctx = {
293
+ block: reason => {
294
+ throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
295
+ },
296
+ transform: () => {},
297
+ blocked: false,
298
+ blockReason: undefined
299
+ };
300
+ handler.onRemove?.(id, entity, ctx);
301
+ }
302
+ entitiesToRemove.push({
352
303
  id,
353
- prev: this.storage.get(id)
304
+ entity
354
305
  });
306
+ }
307
+ for (const {
308
+ id
309
+ } of entitiesToRemove) {
310
+ storage.delete(id);
311
+ nodeCache.delete(id);
312
+ }
313
+ updateSignals();
314
+ for (const {
315
+ id,
316
+ entity
317
+ } of entitiesToRemove) {
318
+ pathNotifier.notify(`${basePath}.${String(id)}`, undefined, entity);
319
+ }
320
+ for (const {
321
+ id,
322
+ entity
323
+ } of entitiesToRemove) {
324
+ for (const handler of tapHandlers) {
325
+ handler.onRemove?.(id, entity);
326
+ }
327
+ }
328
+ },
329
+ removeWhere(predicate) {
330
+ const idsToRemove = [];
331
+ for (const [id, entity] of storage) {
332
+ if (predicate(entity)) {
333
+ idsToRemove.push(id);
334
+ }
335
+ }
336
+ if (idsToRemove.length > 0) {
337
+ api.removeMany(idsToRemove);
338
+ }
339
+ return idsToRemove.length;
340
+ },
341
+ upsertOne(entity, opts) {
342
+ const id = opts?.selectId?.(entity) ?? selectId(entity);
343
+ if (storage.has(id)) {
344
+ api.updateOne(id, entity);
355
345
  } else {
356
- toAdd.push({
357
- entity,
358
- id
346
+ api.addOne(entity, opts);
347
+ }
348
+ return id;
349
+ },
350
+ upsertMany(entities, opts) {
351
+ if (entities.length === 0) return [];
352
+ const toAdd = [];
353
+ const toUpdate = [];
354
+ for (const entity of entities) {
355
+ const id = opts?.selectId?.(entity) ?? selectId(entity);
356
+ const existing = storage.get(id);
357
+ if (existing !== undefined) {
358
+ toUpdate.push({
359
+ entity,
360
+ id,
361
+ prev: existing
362
+ });
363
+ } else {
364
+ toAdd.push({
365
+ entity,
366
+ id
367
+ });
368
+ }
369
+ }
370
+ const addedEntities = [];
371
+ for (const {
372
+ entity,
373
+ id
374
+ } of toAdd) {
375
+ let transformedEntity = entity;
376
+ for (const handler of interceptHandlers) {
377
+ const ctx = {
378
+ block: reason => {
379
+ throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
380
+ },
381
+ transform: value => {
382
+ transformedEntity = value;
383
+ },
384
+ blocked: false,
385
+ blockReason: undefined
386
+ };
387
+ handler.onAdd?.(entity, ctx);
388
+ }
389
+ storage.set(id, transformedEntity);
390
+ nodeCache.delete(id);
391
+ addedEntities.push({
392
+ id,
393
+ entity: transformedEntity
359
394
  });
360
395
  }
361
- }
362
- const addedEntities = [];
363
- for (const {
364
- entity,
365
- id
366
- } of toAdd) {
367
- let transformedEntity = entity;
368
- for (const handler of this.interceptHandlers) {
369
- const ctx = {
370
- block: reason => {
371
- throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
372
- },
373
- transform: value => {
374
- transformedEntity = value;
375
- },
376
- blocked: false,
377
- blockReason: undefined
396
+ const updatedEntities = [];
397
+ for (const {
398
+ entity,
399
+ id,
400
+ prev
401
+ } of toUpdate) {
402
+ let transformedChanges = entity;
403
+ for (const handler of interceptHandlers) {
404
+ const ctx = {
405
+ block: reason => {
406
+ throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
407
+ },
408
+ transform: value => {
409
+ transformedChanges = value;
410
+ },
411
+ blocked: false,
412
+ blockReason: undefined
413
+ };
414
+ handler.onUpdate?.(id, entity, ctx);
415
+ }
416
+ const finalUpdated = {
417
+ ...prev,
418
+ ...transformedChanges
378
419
  };
379
- handler.onAdd?.(entity, ctx);
420
+ storage.set(id, finalUpdated);
421
+ nodeCache.delete(id);
422
+ updatedEntities.push({
423
+ id,
424
+ prev,
425
+ finalUpdated,
426
+ transformedChanges
427
+ });
380
428
  }
381
- this.storage.set(id, transformedEntity);
382
- this.nodeCache.delete(id);
383
- addedEntities.push({
429
+ updateSignals();
430
+ for (const {
384
431
  id,
385
- entity: transformedEntity
386
- });
387
- }
388
- const updatedEntities = [];
389
- for (const {
390
- entity,
391
- id,
392
- prev
393
- } of toUpdate) {
394
- let transformedChanges = entity;
395
- for (const handler of this.interceptHandlers) {
396
- const ctx = {
397
- block: reason => {
398
- throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
399
- },
400
- transform: value => {
401
- transformedChanges = value;
402
- },
403
- blocked: false,
404
- blockReason: undefined
405
- };
406
- handler.onUpdate?.(id, entity, ctx);
432
+ entity
433
+ } of addedEntities) {
434
+ pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
407
435
  }
408
- const finalUpdated = {
409
- ...prev,
410
- ...transformedChanges
411
- };
412
- this.storage.set(id, finalUpdated);
413
- this.nodeCache.delete(id);
414
- updatedEntities.push({
436
+ for (const {
415
437
  id,
416
438
  prev,
417
- finalUpdated,
418
- transformedChanges
419
- });
420
- }
421
- this.updateSignals();
422
- for (const {
423
- id,
424
- entity
425
- } of addedEntities) {
426
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, entity, undefined);
427
- }
428
- for (const {
429
- id,
430
- prev,
431
- finalUpdated
432
- } of updatedEntities) {
433
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, finalUpdated, prev);
434
- }
435
- for (const {
436
- id,
437
- entity
438
- } of addedEntities) {
439
- for (const handler of this.tapHandlers) {
440
- handler.onAdd?.(entity, id);
439
+ finalUpdated
440
+ } of updatedEntities) {
441
+ pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
441
442
  }
442
- }
443
- for (const {
444
- id,
445
- transformedChanges,
446
- finalUpdated
447
- } of updatedEntities) {
448
- for (const handler of this.tapHandlers) {
449
- handler.onUpdate?.(id, transformedChanges, finalUpdated);
443
+ for (const {
444
+ id,
445
+ entity
446
+ } of addedEntities) {
447
+ for (const handler of tapHandlers) {
448
+ handler.onAdd?.(entity, id);
449
+ }
450
450
  }
451
- }
452
- return [...toAdd.map(a => a.id), ...toUpdate.map(u => u.id)];
453
- }
454
- clear() {
455
- this.storage.clear();
456
- this.nodeCache.clear();
457
- this.updateSignals();
458
- }
459
- removeAll() {
460
- this.clear();
461
- }
462
- setAll(entities, opts) {
463
- this.storage.clear();
464
- this.nodeCache.clear();
465
- const addedIds = [];
466
- for (const entity of entities) {
467
- const id = opts?.selectId?.(entity) ?? this.selectId(entity);
468
- let transformedEntity = entity;
469
- for (const handler of this.interceptHandlers) {
470
- const ctx = {
471
- block: reason => {
472
- throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
473
- },
474
- transform: value => {
475
- transformedEntity = value;
476
- },
477
- blocked: false,
478
- blockReason: undefined
479
- };
480
- handler.onAdd?.(entity, ctx);
451
+ for (const {
452
+ id,
453
+ transformedChanges,
454
+ finalUpdated
455
+ } of updatedEntities) {
456
+ for (const handler of tapHandlers) {
457
+ handler.onUpdate?.(id, transformedChanges, finalUpdated);
458
+ }
481
459
  }
482
- this.storage.set(id, transformedEntity);
483
- addedIds.push(id);
484
- }
485
- this.updateSignals();
486
- for (let i = 0; i < addedIds.length; i++) {
487
- const id = addedIds[i];
488
- const entity = this.storage.get(id);
489
- this.pathNotifier.notify(`${this.basePath}.${String(id)}`, entity, undefined);
490
- }
491
- for (let i = 0; i < addedIds.length; i++) {
492
- const id = addedIds[i];
493
- const entity = this.storage.get(id);
494
- for (const handler of this.tapHandlers) {
495
- handler.onAdd?.(entity, id);
460
+ return [...toAdd.map(a => a.id), ...toUpdate.map(u => u.id)];
461
+ },
462
+ clear() {
463
+ storage.clear();
464
+ nodeCache.clear();
465
+ updateSignals();
466
+ },
467
+ removeAll() {
468
+ api.clear();
469
+ },
470
+ setAll(entities, opts) {
471
+ storage.clear();
472
+ nodeCache.clear();
473
+ const addedIds = [];
474
+ for (const entity of entities) {
475
+ const id = opts?.selectId?.(entity) ?? selectId(entity);
476
+ let transformedEntity = entity;
477
+ for (const handler of interceptHandlers) {
478
+ const ctx = {
479
+ block: reason => {
480
+ throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
481
+ },
482
+ transform: value => {
483
+ transformedEntity = value;
484
+ },
485
+ blocked: false,
486
+ blockReason: undefined
487
+ };
488
+ handler.onAdd?.(entity, ctx);
489
+ }
490
+ storage.set(id, transformedEntity);
491
+ addedIds.push(id);
496
492
  }
493
+ updateSignals();
494
+ for (let i = 0; i < addedIds.length; i++) {
495
+ const id = addedIds[i];
496
+ const entity = storage.get(id);
497
+ pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
498
+ }
499
+ for (let i = 0; i < addedIds.length; i++) {
500
+ const id = addedIds[i];
501
+ const entity = storage.get(id);
502
+ if (entity) {
503
+ for (const handler of tapHandlers) {
504
+ handler.onAdd?.(entity, id);
505
+ }
506
+ }
507
+ }
508
+ },
509
+ tap(handlers) {
510
+ tapHandlers.push(handlers);
511
+ return () => {
512
+ const idx = tapHandlers.indexOf(handlers);
513
+ if (idx > -1) tapHandlers.splice(idx, 1);
514
+ };
515
+ },
516
+ intercept(handlers) {
517
+ interceptHandlers.push(handlers);
518
+ return () => {
519
+ const idx = interceptHandlers.indexOf(handlers);
520
+ if (idx > -1) interceptHandlers.splice(idx, 1);
521
+ };
497
522
  }
498
- }
499
- tap(handlers) {
500
- this.tapHandlers.push(handlers);
501
- return () => {
502
- const idx = this.tapHandlers.indexOf(handlers);
503
- if (idx > -1) this.tapHandlers.splice(idx, 1);
504
- };
505
- }
506
- intercept(handlers) {
507
- this.interceptHandlers.push(handlers);
508
- return () => {
509
- const idx = this.interceptHandlers.indexOf(handlers);
510
- if (idx > -1) this.interceptHandlers.splice(idx, 1);
511
- };
512
- }
513
- updateSignals() {
514
- const entities = Array.from(this.storage.values());
515
- const ids = Array.from(this.storage.keys());
516
- const map = new Map(this.storage);
517
- this.allSignal.set(entities);
518
- this.countSignal.set(entities.length);
519
- this.idsSignal.set(ids);
520
- this.mapSignal.set(map);
521
- }
522
- getOrCreateNode(id, entity) {
523
- let node = this.nodeCache.get(id);
524
- if (!node) {
525
- node = this.createEntityNode(id, entity);
526
- this.nodeCache.set(id, node);
527
- }
528
- return node;
529
- }
530
- createEntityNode(id, entity) {
531
- const node = () => this.storage.get(id);
532
- for (const key of Object.keys(entity)) {
533
- Object.defineProperty(node, key, {
534
- get: () => {
535
- const current = this.storage.get(id);
536
- const value = current?.[key];
537
- return () => value;
538
- },
539
- enumerable: true,
540
- configurable: true
541
- });
542
- }
543
- return node;
544
- }
545
- }
546
- function createEntitySignal(config, pathNotifier, basePath) {
547
- const impl = new EntitySignalImpl(config, pathNotifier, basePath);
548
- return new Proxy(impl, {
523
+ };
524
+ return new Proxy(api, {
549
525
  get: (target, prop) => {
550
526
  if (typeof prop === 'string' && !isNaN(Number(prop))) {
551
- return target.byId(Number(prop));
527
+ return api.byId(Number(prop));
552
528
  }
553
529
  return target[prop];
554
530
  }
555
531
  });
556
532
  }
557
533
 
558
- export { EntitySignalImpl, createEntitySignal };
534
+ export { createEntitySignal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaltree/core",
3
- "version": "6.2.3",
3
+ "version": "6.2.4",
4
4
  "description": "Lightweight, type-safe signal-based state management for Angular. Core package providing hierarchical signal trees, basic entity management, and async actions.",
5
5
  "type": "module",
6
6
  "sideEffects": false,