@signaltree/core 6.2.2 → 6.2.3
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/dist/lib/entity-signal.js +292 -17
- package/package.json +1 -1
|
@@ -101,11 +101,55 @@ class EntitySignalImpl {
|
|
|
101
101
|
return id;
|
|
102
102
|
}
|
|
103
103
|
addMany(entities, opts) {
|
|
104
|
-
const
|
|
104
|
+
const idsToAdd = [];
|
|
105
105
|
for (const entity of entities) {
|
|
106
|
-
|
|
106
|
+
const id = opts?.selectId?.(entity) ?? this.selectId(entity);
|
|
107
|
+
if (this.storage.has(id)) {
|
|
108
|
+
throw new Error(`Entity with id ${String(id)} already exists`);
|
|
109
|
+
}
|
|
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
|
+
let transformedEntity = entity;
|
|
117
|
+
for (const handler of this.interceptHandlers) {
|
|
118
|
+
const ctx = {
|
|
119
|
+
block: reason => {
|
|
120
|
+
throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
|
|
121
|
+
},
|
|
122
|
+
transform: value => {
|
|
123
|
+
transformedEntity = value;
|
|
124
|
+
},
|
|
125
|
+
blocked: false,
|
|
126
|
+
blockReason: undefined
|
|
127
|
+
};
|
|
128
|
+
handler.onAdd?.(entity, ctx);
|
|
129
|
+
}
|
|
130
|
+
this.storage.set(id, transformedEntity);
|
|
131
|
+
this.nodeCache.delete(id);
|
|
132
|
+
addedEntities.push({
|
|
133
|
+
id,
|
|
134
|
+
entity: transformedEntity
|
|
135
|
+
});
|
|
107
136
|
}
|
|
108
|
-
|
|
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);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return idsToAdd;
|
|
109
153
|
}
|
|
110
154
|
updateOne(id, changes) {
|
|
111
155
|
const entity = this.storage.get(id);
|
|
@@ -140,19 +184,70 @@ class EntitySignalImpl {
|
|
|
140
184
|
}
|
|
141
185
|
}
|
|
142
186
|
updateMany(ids, changes) {
|
|
187
|
+
if (ids.length === 0) return;
|
|
188
|
+
const updatedEntities = [];
|
|
143
189
|
for (const id of ids) {
|
|
144
|
-
this.
|
|
190
|
+
const entity = this.storage.get(id);
|
|
191
|
+
if (!entity) {
|
|
192
|
+
throw new Error(`Entity with id ${String(id)} not found`);
|
|
193
|
+
}
|
|
194
|
+
const prev = entity;
|
|
195
|
+
let transformedChanges = changes;
|
|
196
|
+
for (const handler of this.interceptHandlers) {
|
|
197
|
+
const ctx = {
|
|
198
|
+
block: reason => {
|
|
199
|
+
throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
|
|
200
|
+
},
|
|
201
|
+
transform: value => {
|
|
202
|
+
transformedChanges = value;
|
|
203
|
+
},
|
|
204
|
+
blocked: false,
|
|
205
|
+
blockReason: undefined
|
|
206
|
+
};
|
|
207
|
+
handler.onUpdate?.(id, changes, ctx);
|
|
208
|
+
}
|
|
209
|
+
const finalUpdated = {
|
|
210
|
+
...entity,
|
|
211
|
+
...transformedChanges
|
|
212
|
+
};
|
|
213
|
+
this.storage.set(id, finalUpdated);
|
|
214
|
+
this.nodeCache.delete(id);
|
|
215
|
+
updatedEntities.push({
|
|
216
|
+
id,
|
|
217
|
+
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);
|
|
237
|
+
}
|
|
145
238
|
}
|
|
146
239
|
}
|
|
147
240
|
updateWhere(predicate, changes) {
|
|
148
|
-
|
|
241
|
+
const idsToUpdate = [];
|
|
149
242
|
for (const [id, entity] of this.storage) {
|
|
150
243
|
if (predicate(entity)) {
|
|
151
|
-
|
|
152
|
-
count++;
|
|
244
|
+
idsToUpdate.push(id);
|
|
153
245
|
}
|
|
154
246
|
}
|
|
155
|
-
|
|
247
|
+
if (idsToUpdate.length > 0) {
|
|
248
|
+
this.updateMany(idsToUpdate, changes);
|
|
249
|
+
}
|
|
250
|
+
return idsToUpdate.length;
|
|
156
251
|
}
|
|
157
252
|
removeOne(id) {
|
|
158
253
|
const entity = this.storage.get(id);
|
|
@@ -179,8 +274,49 @@ class EntitySignalImpl {
|
|
|
179
274
|
}
|
|
180
275
|
}
|
|
181
276
|
removeMany(ids) {
|
|
277
|
+
if (ids.length === 0) return;
|
|
278
|
+
const entitiesToRemove = [];
|
|
182
279
|
for (const id of ids) {
|
|
183
|
-
this.
|
|
280
|
+
const entity = this.storage.get(id);
|
|
281
|
+
if (!entity) {
|
|
282
|
+
throw new Error(`Entity with id ${String(id)} not found`);
|
|
283
|
+
}
|
|
284
|
+
for (const handler of this.interceptHandlers) {
|
|
285
|
+
const ctx = {
|
|
286
|
+
block: reason => {
|
|
287
|
+
throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
|
|
288
|
+
},
|
|
289
|
+
transform: () => {},
|
|
290
|
+
blocked: false,
|
|
291
|
+
blockReason: undefined
|
|
292
|
+
};
|
|
293
|
+
handler.onRemove?.(id, entity, ctx);
|
|
294
|
+
}
|
|
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) {
|
|
318
|
+
handler.onRemove?.(id, entity);
|
|
319
|
+
}
|
|
184
320
|
}
|
|
185
321
|
}
|
|
186
322
|
removeWhere(predicate) {
|
|
@@ -190,12 +326,10 @@ class EntitySignalImpl {
|
|
|
190
326
|
idsToRemove.push(id);
|
|
191
327
|
}
|
|
192
328
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.removeOne(id);
|
|
196
|
-
count++;
|
|
329
|
+
if (idsToRemove.length > 0) {
|
|
330
|
+
this.removeMany(idsToRemove);
|
|
197
331
|
}
|
|
198
|
-
return
|
|
332
|
+
return idsToRemove.length;
|
|
199
333
|
}
|
|
200
334
|
upsertOne(entity, opts) {
|
|
201
335
|
const id = opts?.selectId?.(entity) ?? this.selectId(entity);
|
|
@@ -207,7 +341,115 @@ class EntitySignalImpl {
|
|
|
207
341
|
return id;
|
|
208
342
|
}
|
|
209
343
|
upsertMany(entities, opts) {
|
|
210
|
-
|
|
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,
|
|
352
|
+
id,
|
|
353
|
+
prev: this.storage.get(id)
|
|
354
|
+
});
|
|
355
|
+
} else {
|
|
356
|
+
toAdd.push({
|
|
357
|
+
entity,
|
|
358
|
+
id
|
|
359
|
+
});
|
|
360
|
+
}
|
|
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
|
|
378
|
+
};
|
|
379
|
+
handler.onAdd?.(entity, ctx);
|
|
380
|
+
}
|
|
381
|
+
this.storage.set(id, transformedEntity);
|
|
382
|
+
this.nodeCache.delete(id);
|
|
383
|
+
addedEntities.push({
|
|
384
|
+
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);
|
|
407
|
+
}
|
|
408
|
+
const finalUpdated = {
|
|
409
|
+
...prev,
|
|
410
|
+
...transformedChanges
|
|
411
|
+
};
|
|
412
|
+
this.storage.set(id, finalUpdated);
|
|
413
|
+
this.nodeCache.delete(id);
|
|
414
|
+
updatedEntities.push({
|
|
415
|
+
id,
|
|
416
|
+
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);
|
|
441
|
+
}
|
|
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);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return [...toAdd.map(a => a.id), ...toUpdate.map(u => u.id)];
|
|
211
453
|
}
|
|
212
454
|
clear() {
|
|
213
455
|
this.storage.clear();
|
|
@@ -218,8 +460,41 @@ class EntitySignalImpl {
|
|
|
218
460
|
this.clear();
|
|
219
461
|
}
|
|
220
462
|
setAll(entities, opts) {
|
|
221
|
-
this.clear();
|
|
222
|
-
this.
|
|
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);
|
|
481
|
+
}
|
|
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);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
223
498
|
}
|
|
224
499
|
tap(handlers) {
|
|
225
500
|
this.tapHandlers.push(handlers);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaltree/core",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.3",
|
|
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,
|