@signaltree/core 7.1.3 → 7.1.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.
- package/README.md +28 -13
- package/package.json +2 -2
- package/dist/constants.js +0 -6
- package/dist/deep-equal.js +0 -41
- package/dist/enhancers/batching/batching.js +0 -230
- package/dist/enhancers/devtools/devtools.js +0 -318
- package/dist/enhancers/effects/effects.js +0 -66
- package/dist/enhancers/entities/entities.js +0 -7
- package/dist/enhancers/index.js +0 -72
- package/dist/enhancers/memoization/memoization.js +0 -420
- package/dist/enhancers/presets/lib/presets.js +0 -27
- package/dist/enhancers/serialization/constants.js +0 -15
- package/dist/enhancers/serialization/serialization.js +0 -656
- package/dist/enhancers/time-travel/time-travel.js +0 -283
- package/dist/enhancers/time-travel/utils.js +0 -11
- package/dist/enhancers/utils/copy-tree-properties.js +0 -20
- package/dist/index.js +0 -26
- package/dist/is-built-in-object.js +0 -23
- package/dist/lib/async-helpers.js +0 -77
- package/dist/lib/constants.js +0 -56
- package/dist/lib/edit-session.js +0 -84
- package/dist/lib/entity-signal.js +0 -544
- package/dist/lib/internals/batch-scope.js +0 -8
- package/dist/lib/internals/derived-types.js +0 -5
- package/dist/lib/internals/materialize-markers.js +0 -72
- package/dist/lib/internals/merge-derived.js +0 -59
- package/dist/lib/markers/derived.js +0 -6
- package/dist/lib/markers/entity-map.js +0 -20
- package/dist/lib/markers/status.js +0 -71
- package/dist/lib/markers/stored.js +0 -111
- package/dist/lib/memory/memory-manager.js +0 -164
- package/dist/lib/path-notifier.js +0 -178
- package/dist/lib/presets.js +0 -20
- package/dist/lib/security/security-validator.js +0 -121
- package/dist/lib/signal-tree.js +0 -415
- package/dist/lib/types.js +0 -3
- package/dist/lib/utils.js +0 -264
- package/dist/lru-cache.js +0 -64
- package/dist/parse-path.js +0 -13
- package/src/enhancers/batching/batching.d.ts +0 -10
- package/src/enhancers/batching/batching.types.d.ts +0 -1
- package/src/enhancers/batching/index.d.ts +0 -1
- package/src/enhancers/batching/test-setup.d.ts +0 -3
- package/src/enhancers/devtools/devtools.d.ts +0 -68
- package/src/enhancers/devtools/devtools.types.d.ts +0 -1
- package/src/enhancers/devtools/index.d.ts +0 -1
- package/src/enhancers/devtools/test-setup.d.ts +0 -3
- package/src/enhancers/effects/effects.d.ts +0 -9
- package/src/enhancers/effects/effects.types.d.ts +0 -1
- package/src/enhancers/effects/index.d.ts +0 -1
- package/src/enhancers/entities/entities.d.ts +0 -7
- package/src/enhancers/entities/entities.types.d.ts +0 -1
- package/src/enhancers/entities/index.d.ts +0 -1
- package/src/enhancers/entities/test-setup.d.ts +0 -3
- package/src/enhancers/index.d.ts +0 -3
- package/src/enhancers/memoization/index.d.ts +0 -1
- package/src/enhancers/memoization/memoization.d.ts +0 -54
- package/src/enhancers/memoization/memoization.types.d.ts +0 -1
- package/src/enhancers/memoization/test-setup.d.ts +0 -3
- package/src/enhancers/presets/index.d.ts +0 -1
- package/src/enhancers/presets/lib/presets.d.ts +0 -8
- package/src/enhancers/serialization/constants.d.ts +0 -14
- package/src/enhancers/serialization/index.d.ts +0 -2
- package/src/enhancers/serialization/serialization.d.ts +0 -68
- package/src/enhancers/serialization/test-setup.d.ts +0 -3
- package/src/enhancers/test-helpers/types-equals.d.ts +0 -2
- package/src/enhancers/time-travel/index.d.ts +0 -1
- package/src/enhancers/time-travel/test-setup.d.ts +0 -3
- package/src/enhancers/time-travel/time-travel.d.ts +0 -10
- package/src/enhancers/time-travel/time-travel.types.d.ts +0 -1
- package/src/enhancers/time-travel/utils.d.ts +0 -2
- package/src/enhancers/types.d.ts +0 -1
- package/src/enhancers/typing/helpers-types.d.ts +0 -2
- package/src/enhancers/utils/copy-tree-properties.d.ts +0 -1
- package/src/index.d.ts +0 -26
- package/src/lib/async-helpers.d.ts +0 -8
- package/src/lib/constants.d.ts +0 -41
- package/src/lib/dev-proxy.d.ts +0 -3
- package/src/lib/edit-session.d.ts +0 -21
- package/src/lib/entity-signal.d.ts +0 -1
- package/src/lib/internals/batch-scope.d.ts +0 -3
- package/src/lib/internals/builder-types.d.ts +0 -13
- package/src/lib/internals/derived-types.d.ts +0 -18
- package/src/lib/internals/materialize-markers.d.ts +0 -5
- package/src/lib/internals/merge-derived.d.ts +0 -4
- package/src/lib/markers/derived.d.ts +0 -9
- package/src/lib/markers/entity-map.d.ts +0 -4
- package/src/lib/markers/index.d.ts +0 -3
- package/src/lib/markers/status.d.ts +0 -32
- package/src/lib/markers/stored.d.ts +0 -23
- package/src/lib/memory/memory-manager.d.ts +0 -30
- package/src/lib/path-notifier.d.ts +0 -34
- package/src/lib/performance/diff-engine.d.ts +0 -33
- package/src/lib/performance/path-index.d.ts +0 -25
- package/src/lib/performance/update-engine.d.ts +0 -32
- package/src/lib/presets.d.ts +0 -34
- package/src/lib/security/security-validator.d.ts +0 -33
- package/src/lib/signal-tree.d.ts +0 -6
- package/src/lib/types.d.ts +0 -300
- package/src/lib/utils.d.ts +0 -25
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
import { signal, computed } from '@angular/core';
|
|
2
|
-
|
|
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);
|
|
21
|
-
}
|
|
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;
|
|
36
|
-
}
|
|
37
|
-
function getOrCreateNode(id, entity) {
|
|
38
|
-
let node = nodeCache.get(id);
|
|
39
|
-
if (!node) {
|
|
40
|
-
node = createEntityNode(id, entity);
|
|
41
|
-
nodeCache.set(id, node);
|
|
42
|
-
}
|
|
43
|
-
return node;
|
|
44
|
-
}
|
|
45
|
-
const whereCache = new WeakMap();
|
|
46
|
-
const findCache = new WeakMap();
|
|
47
|
-
const api = {
|
|
48
|
-
byId(id) {
|
|
49
|
-
const entity = storage.get(id);
|
|
50
|
-
if (!entity) return undefined;
|
|
51
|
-
return getOrCreateNode(id, entity);
|
|
52
|
-
},
|
|
53
|
-
byIdOrFail(id) {
|
|
54
|
-
const node = api.byId(id);
|
|
55
|
-
if (!node) {
|
|
56
|
-
throw new Error(`Entity with id ${String(id)} not found`);
|
|
57
|
-
}
|
|
58
|
-
return node;
|
|
59
|
-
},
|
|
60
|
-
get all() {
|
|
61
|
-
return allSignal;
|
|
62
|
-
},
|
|
63
|
-
get count() {
|
|
64
|
-
return countSignal;
|
|
65
|
-
},
|
|
66
|
-
get ids() {
|
|
67
|
-
return idsSignal;
|
|
68
|
-
},
|
|
69
|
-
get map() {
|
|
70
|
-
return mapSignal;
|
|
71
|
-
},
|
|
72
|
-
has(id) {
|
|
73
|
-
return computed(() => mapSignal().has(id));
|
|
74
|
-
},
|
|
75
|
-
get isEmpty() {
|
|
76
|
-
return computed(() => countSignal() === 0);
|
|
77
|
-
},
|
|
78
|
-
where(predicate) {
|
|
79
|
-
const cached = whereCache.get(predicate);
|
|
80
|
-
if (cached) return cached;
|
|
81
|
-
const s = computed(() => allSignal().filter(predicate));
|
|
82
|
-
whereCache.set(predicate, s);
|
|
83
|
-
return s;
|
|
84
|
-
},
|
|
85
|
-
find(predicate) {
|
|
86
|
-
const cached = findCache.get(predicate);
|
|
87
|
-
if (cached) return cached;
|
|
88
|
-
const s = computed(() => allSignal().find(predicate));
|
|
89
|
-
findCache.set(predicate, s);
|
|
90
|
-
return s;
|
|
91
|
-
},
|
|
92
|
-
addOne(entity, opts) {
|
|
93
|
-
const id = opts?.selectId?.(entity) ?? selectId(entity);
|
|
94
|
-
if (storage.has(id)) {
|
|
95
|
-
throw new Error(`Entity with id ${String(id)} already exists`);
|
|
96
|
-
}
|
|
97
|
-
let transformedEntity = entity;
|
|
98
|
-
for (const handler of interceptHandlers) {
|
|
99
|
-
const ctx = {
|
|
100
|
-
block: reason => {
|
|
101
|
-
throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
|
|
102
|
-
},
|
|
103
|
-
transform: value => {
|
|
104
|
-
transformedEntity = value;
|
|
105
|
-
},
|
|
106
|
-
blocked: false,
|
|
107
|
-
blockReason: undefined
|
|
108
|
-
};
|
|
109
|
-
handler.onAdd?.(entity, ctx);
|
|
110
|
-
}
|
|
111
|
-
storage.set(id, transformedEntity);
|
|
112
|
-
nodeCache.delete(id);
|
|
113
|
-
updateSignals();
|
|
114
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, transformedEntity, undefined);
|
|
115
|
-
for (const handler of tapHandlers) {
|
|
116
|
-
handler.onAdd?.(transformedEntity, id);
|
|
117
|
-
}
|
|
118
|
-
return id;
|
|
119
|
-
},
|
|
120
|
-
addMany(entities, opts) {
|
|
121
|
-
const idsToAdd = [];
|
|
122
|
-
for (const entity of entities) {
|
|
123
|
-
const id = opts?.selectId?.(entity) ?? selectId(entity);
|
|
124
|
-
if (storage.has(id)) {
|
|
125
|
-
throw new Error(`Entity with id ${String(id)} already exists`);
|
|
126
|
-
}
|
|
127
|
-
idsToAdd.push(id);
|
|
128
|
-
}
|
|
129
|
-
const addedEntities = [];
|
|
130
|
-
for (let i = 0; i < entities.length; i++) {
|
|
131
|
-
const entity = entities[i];
|
|
132
|
-
const id = idsToAdd[i];
|
|
133
|
-
let transformedEntity = entity;
|
|
134
|
-
for (const handler of interceptHandlers) {
|
|
135
|
-
const ctx = {
|
|
136
|
-
block: reason => {
|
|
137
|
-
throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
|
|
138
|
-
},
|
|
139
|
-
transform: value => {
|
|
140
|
-
transformedEntity = value;
|
|
141
|
-
},
|
|
142
|
-
blocked: false,
|
|
143
|
-
blockReason: undefined
|
|
144
|
-
};
|
|
145
|
-
handler.onAdd?.(entity, ctx);
|
|
146
|
-
}
|
|
147
|
-
storage.set(id, transformedEntity);
|
|
148
|
-
nodeCache.delete(id);
|
|
149
|
-
addedEntities.push({
|
|
150
|
-
id,
|
|
151
|
-
entity: transformedEntity
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
updateSignals();
|
|
155
|
-
for (const {
|
|
156
|
-
id,
|
|
157
|
-
entity
|
|
158
|
-
} of addedEntities) {
|
|
159
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
|
|
160
|
-
}
|
|
161
|
-
for (const {
|
|
162
|
-
id,
|
|
163
|
-
entity
|
|
164
|
-
} of addedEntities) {
|
|
165
|
-
for (const handler of tapHandlers) {
|
|
166
|
-
handler.onAdd?.(entity, id);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return idsToAdd;
|
|
170
|
-
},
|
|
171
|
-
updateOne(id, changes) {
|
|
172
|
-
const entity = storage.get(id);
|
|
173
|
-
if (!entity) {
|
|
174
|
-
throw new Error(`Entity with id ${String(id)} not found`);
|
|
175
|
-
}
|
|
176
|
-
const prev = entity;
|
|
177
|
-
let transformedChanges = changes;
|
|
178
|
-
for (const handler of interceptHandlers) {
|
|
179
|
-
const ctx = {
|
|
180
|
-
block: reason => {
|
|
181
|
-
throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
|
|
182
|
-
},
|
|
183
|
-
transform: value => {
|
|
184
|
-
transformedChanges = value;
|
|
185
|
-
},
|
|
186
|
-
blocked: false,
|
|
187
|
-
blockReason: undefined
|
|
188
|
-
};
|
|
189
|
-
handler.onUpdate?.(id, changes, ctx);
|
|
190
|
-
}
|
|
191
|
-
const finalUpdated = {
|
|
192
|
-
...entity,
|
|
193
|
-
...transformedChanges
|
|
194
|
-
};
|
|
195
|
-
storage.set(id, finalUpdated);
|
|
196
|
-
nodeCache.delete(id);
|
|
197
|
-
updateSignals();
|
|
198
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
|
|
199
|
-
for (const handler of tapHandlers) {
|
|
200
|
-
handler.onUpdate?.(id, transformedChanges, finalUpdated);
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
updateMany(ids, changes) {
|
|
204
|
-
if (ids.length === 0) return;
|
|
205
|
-
const updatedEntities = [];
|
|
206
|
-
for (const id of ids) {
|
|
207
|
-
const entity = storage.get(id);
|
|
208
|
-
if (!entity) {
|
|
209
|
-
throw new Error(`Entity with id ${String(id)} not found`);
|
|
210
|
-
}
|
|
211
|
-
const prev = entity;
|
|
212
|
-
let transformedChanges = changes;
|
|
213
|
-
for (const handler of interceptHandlers) {
|
|
214
|
-
const ctx = {
|
|
215
|
-
block: reason => {
|
|
216
|
-
throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
|
|
217
|
-
},
|
|
218
|
-
transform: value => {
|
|
219
|
-
transformedChanges = value;
|
|
220
|
-
},
|
|
221
|
-
blocked: false,
|
|
222
|
-
blockReason: undefined
|
|
223
|
-
};
|
|
224
|
-
handler.onUpdate?.(id, changes, ctx);
|
|
225
|
-
}
|
|
226
|
-
const finalUpdated = {
|
|
227
|
-
...entity,
|
|
228
|
-
...transformedChanges
|
|
229
|
-
};
|
|
230
|
-
storage.set(id, finalUpdated);
|
|
231
|
-
nodeCache.delete(id);
|
|
232
|
-
updatedEntities.push({
|
|
233
|
-
id,
|
|
234
|
-
prev,
|
|
235
|
-
finalUpdated,
|
|
236
|
-
transformedChanges
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
updateSignals();
|
|
240
|
-
for (const {
|
|
241
|
-
id,
|
|
242
|
-
prev,
|
|
243
|
-
finalUpdated
|
|
244
|
-
} of updatedEntities) {
|
|
245
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
|
|
246
|
-
}
|
|
247
|
-
for (const {
|
|
248
|
-
id,
|
|
249
|
-
transformedChanges,
|
|
250
|
-
finalUpdated
|
|
251
|
-
} of updatedEntities) {
|
|
252
|
-
for (const handler of tapHandlers) {
|
|
253
|
-
handler.onUpdate?.(id, transformedChanges, finalUpdated);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
updateWhere(predicate, changes) {
|
|
258
|
-
const idsToUpdate = [];
|
|
259
|
-
for (const [id, entity] of storage) {
|
|
260
|
-
if (predicate(entity)) {
|
|
261
|
-
idsToUpdate.push(id);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
if (idsToUpdate.length > 0) {
|
|
265
|
-
api.updateMany(idsToUpdate, changes);
|
|
266
|
-
}
|
|
267
|
-
return idsToUpdate.length;
|
|
268
|
-
},
|
|
269
|
-
removeOne(id) {
|
|
270
|
-
const entity = storage.get(id);
|
|
271
|
-
if (!entity) {
|
|
272
|
-
throw new Error(`Entity with id ${String(id)} not found`);
|
|
273
|
-
}
|
|
274
|
-
for (const handler of interceptHandlers) {
|
|
275
|
-
const ctx = {
|
|
276
|
-
block: reason => {
|
|
277
|
-
throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
|
|
278
|
-
},
|
|
279
|
-
transform: () => {},
|
|
280
|
-
blocked: false,
|
|
281
|
-
blockReason: undefined
|
|
282
|
-
};
|
|
283
|
-
handler.onRemove?.(id, entity, ctx);
|
|
284
|
-
}
|
|
285
|
-
storage.delete(id);
|
|
286
|
-
nodeCache.delete(id);
|
|
287
|
-
updateSignals();
|
|
288
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, undefined, entity);
|
|
289
|
-
for (const handler of tapHandlers) {
|
|
290
|
-
handler.onRemove?.(id, entity);
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
removeMany(ids) {
|
|
294
|
-
if (ids.length === 0) return;
|
|
295
|
-
const entitiesToRemove = [];
|
|
296
|
-
for (const id of ids) {
|
|
297
|
-
const entity = storage.get(id);
|
|
298
|
-
if (!entity) {
|
|
299
|
-
throw new Error(`Entity with id ${String(id)} not found`);
|
|
300
|
-
}
|
|
301
|
-
for (const handler of interceptHandlers) {
|
|
302
|
-
const ctx = {
|
|
303
|
-
block: reason => {
|
|
304
|
-
throw new Error(`Cannot remove entity: ${reason || 'blocked by interceptor'}`);
|
|
305
|
-
},
|
|
306
|
-
transform: () => {},
|
|
307
|
-
blocked: false,
|
|
308
|
-
blockReason: undefined
|
|
309
|
-
};
|
|
310
|
-
handler.onRemove?.(id, entity, ctx);
|
|
311
|
-
}
|
|
312
|
-
entitiesToRemove.push({
|
|
313
|
-
id,
|
|
314
|
-
entity
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
for (const {
|
|
318
|
-
id
|
|
319
|
-
} of entitiesToRemove) {
|
|
320
|
-
storage.delete(id);
|
|
321
|
-
nodeCache.delete(id);
|
|
322
|
-
}
|
|
323
|
-
updateSignals();
|
|
324
|
-
for (const {
|
|
325
|
-
id,
|
|
326
|
-
entity
|
|
327
|
-
} of entitiesToRemove) {
|
|
328
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, undefined, entity);
|
|
329
|
-
}
|
|
330
|
-
for (const {
|
|
331
|
-
id,
|
|
332
|
-
entity
|
|
333
|
-
} of entitiesToRemove) {
|
|
334
|
-
for (const handler of tapHandlers) {
|
|
335
|
-
handler.onRemove?.(id, entity);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
removeWhere(predicate) {
|
|
340
|
-
const idsToRemove = [];
|
|
341
|
-
for (const [id, entity] of storage) {
|
|
342
|
-
if (predicate(entity)) {
|
|
343
|
-
idsToRemove.push(id);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (idsToRemove.length > 0) {
|
|
347
|
-
api.removeMany(idsToRemove);
|
|
348
|
-
}
|
|
349
|
-
return idsToRemove.length;
|
|
350
|
-
},
|
|
351
|
-
upsertOne(entity, opts) {
|
|
352
|
-
const id = opts?.selectId?.(entity) ?? selectId(entity);
|
|
353
|
-
if (storage.has(id)) {
|
|
354
|
-
api.updateOne(id, entity);
|
|
355
|
-
} else {
|
|
356
|
-
api.addOne(entity, opts);
|
|
357
|
-
}
|
|
358
|
-
return id;
|
|
359
|
-
},
|
|
360
|
-
upsertMany(entities, opts) {
|
|
361
|
-
if (entities.length === 0) return [];
|
|
362
|
-
const toAdd = [];
|
|
363
|
-
const toUpdate = [];
|
|
364
|
-
for (const entity of entities) {
|
|
365
|
-
const id = opts?.selectId?.(entity) ?? selectId(entity);
|
|
366
|
-
const existing = storage.get(id);
|
|
367
|
-
if (existing !== undefined) {
|
|
368
|
-
toUpdate.push({
|
|
369
|
-
entity,
|
|
370
|
-
id,
|
|
371
|
-
prev: existing
|
|
372
|
-
});
|
|
373
|
-
} else {
|
|
374
|
-
toAdd.push({
|
|
375
|
-
entity,
|
|
376
|
-
id
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
const addedEntities = [];
|
|
381
|
-
for (const {
|
|
382
|
-
entity,
|
|
383
|
-
id
|
|
384
|
-
} of toAdd) {
|
|
385
|
-
let transformedEntity = entity;
|
|
386
|
-
for (const handler of interceptHandlers) {
|
|
387
|
-
const ctx = {
|
|
388
|
-
block: reason => {
|
|
389
|
-
throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
|
|
390
|
-
},
|
|
391
|
-
transform: value => {
|
|
392
|
-
transformedEntity = value;
|
|
393
|
-
},
|
|
394
|
-
blocked: false,
|
|
395
|
-
blockReason: undefined
|
|
396
|
-
};
|
|
397
|
-
handler.onAdd?.(entity, ctx);
|
|
398
|
-
}
|
|
399
|
-
storage.set(id, transformedEntity);
|
|
400
|
-
nodeCache.delete(id);
|
|
401
|
-
addedEntities.push({
|
|
402
|
-
id,
|
|
403
|
-
entity: transformedEntity
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
const updatedEntities = [];
|
|
407
|
-
for (const {
|
|
408
|
-
entity,
|
|
409
|
-
id,
|
|
410
|
-
prev
|
|
411
|
-
} of toUpdate) {
|
|
412
|
-
let transformedChanges = entity;
|
|
413
|
-
for (const handler of interceptHandlers) {
|
|
414
|
-
const ctx = {
|
|
415
|
-
block: reason => {
|
|
416
|
-
throw new Error(`Cannot update entity: ${reason || 'blocked by interceptor'}`);
|
|
417
|
-
},
|
|
418
|
-
transform: value => {
|
|
419
|
-
transformedChanges = value;
|
|
420
|
-
},
|
|
421
|
-
blocked: false,
|
|
422
|
-
blockReason: undefined
|
|
423
|
-
};
|
|
424
|
-
handler.onUpdate?.(id, entity, ctx);
|
|
425
|
-
}
|
|
426
|
-
const finalUpdated = {
|
|
427
|
-
...prev,
|
|
428
|
-
...transformedChanges
|
|
429
|
-
};
|
|
430
|
-
storage.set(id, finalUpdated);
|
|
431
|
-
nodeCache.delete(id);
|
|
432
|
-
updatedEntities.push({
|
|
433
|
-
id,
|
|
434
|
-
prev,
|
|
435
|
-
finalUpdated,
|
|
436
|
-
transformedChanges
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
updateSignals();
|
|
440
|
-
for (const {
|
|
441
|
-
id,
|
|
442
|
-
entity
|
|
443
|
-
} of addedEntities) {
|
|
444
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
|
|
445
|
-
}
|
|
446
|
-
for (const {
|
|
447
|
-
id,
|
|
448
|
-
prev,
|
|
449
|
-
finalUpdated
|
|
450
|
-
} of updatedEntities) {
|
|
451
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, finalUpdated, prev);
|
|
452
|
-
}
|
|
453
|
-
for (const {
|
|
454
|
-
id,
|
|
455
|
-
entity
|
|
456
|
-
} of addedEntities) {
|
|
457
|
-
for (const handler of tapHandlers) {
|
|
458
|
-
handler.onAdd?.(entity, id);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
for (const {
|
|
462
|
-
id,
|
|
463
|
-
transformedChanges,
|
|
464
|
-
finalUpdated
|
|
465
|
-
} of updatedEntities) {
|
|
466
|
-
for (const handler of tapHandlers) {
|
|
467
|
-
handler.onUpdate?.(id, transformedChanges, finalUpdated);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return [...toAdd.map(a => a.id), ...toUpdate.map(u => u.id)];
|
|
471
|
-
},
|
|
472
|
-
clear() {
|
|
473
|
-
storage.clear();
|
|
474
|
-
nodeCache.clear();
|
|
475
|
-
updateSignals();
|
|
476
|
-
},
|
|
477
|
-
removeAll() {
|
|
478
|
-
api.clear();
|
|
479
|
-
},
|
|
480
|
-
setAll(entities, opts) {
|
|
481
|
-
storage.clear();
|
|
482
|
-
nodeCache.clear();
|
|
483
|
-
const addedIds = [];
|
|
484
|
-
for (const entity of entities) {
|
|
485
|
-
const id = opts?.selectId?.(entity) ?? selectId(entity);
|
|
486
|
-
let transformedEntity = entity;
|
|
487
|
-
for (const handler of interceptHandlers) {
|
|
488
|
-
const ctx = {
|
|
489
|
-
block: reason => {
|
|
490
|
-
throw new Error(`Cannot add entity: ${reason || 'blocked by interceptor'}`);
|
|
491
|
-
},
|
|
492
|
-
transform: value => {
|
|
493
|
-
transformedEntity = value;
|
|
494
|
-
},
|
|
495
|
-
blocked: false,
|
|
496
|
-
blockReason: undefined
|
|
497
|
-
};
|
|
498
|
-
handler.onAdd?.(entity, ctx);
|
|
499
|
-
}
|
|
500
|
-
storage.set(id, transformedEntity);
|
|
501
|
-
addedIds.push(id);
|
|
502
|
-
}
|
|
503
|
-
updateSignals();
|
|
504
|
-
for (let i = 0; i < addedIds.length; i++) {
|
|
505
|
-
const id = addedIds[i];
|
|
506
|
-
const entity = storage.get(id);
|
|
507
|
-
pathNotifier.notify(`${basePath}.${String(id)}`, entity, undefined);
|
|
508
|
-
}
|
|
509
|
-
for (let i = 0; i < addedIds.length; i++) {
|
|
510
|
-
const id = addedIds[i];
|
|
511
|
-
const entity = storage.get(id);
|
|
512
|
-
if (entity) {
|
|
513
|
-
for (const handler of tapHandlers) {
|
|
514
|
-
handler.onAdd?.(entity, id);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
tap(handlers) {
|
|
520
|
-
tapHandlers.push(handlers);
|
|
521
|
-
return () => {
|
|
522
|
-
const idx = tapHandlers.indexOf(handlers);
|
|
523
|
-
if (idx > -1) tapHandlers.splice(idx, 1);
|
|
524
|
-
};
|
|
525
|
-
},
|
|
526
|
-
intercept(handlers) {
|
|
527
|
-
interceptHandlers.push(handlers);
|
|
528
|
-
return () => {
|
|
529
|
-
const idx = interceptHandlers.indexOf(handlers);
|
|
530
|
-
if (idx > -1) interceptHandlers.splice(idx, 1);
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
return new Proxy(api, {
|
|
535
|
-
get: (target, prop) => {
|
|
536
|
-
if (typeof prop === 'string' && !isNaN(Number(prop))) {
|
|
537
|
-
return api.byId(Number(prop));
|
|
538
|
-
}
|
|
539
|
-
return target[prop];
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
export { createEntitySignal };
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { isSignal } from '@angular/core';
|
|
2
|
-
import { getPathNotifier } from '../path-notifier.js';
|
|
3
|
-
import { isNodeAccessor } from '../utils.js';
|
|
4
|
-
|
|
5
|
-
const MARKER_PROCESSORS = [];
|
|
6
|
-
function isRegisteredMarker(value) {
|
|
7
|
-
if (value === null || typeof value !== 'object') {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
if (Object.getOwnPropertySymbols(value).length === 0) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
for (const processor of MARKER_PROCESSORS) {
|
|
14
|
-
if (processor.check(value)) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
function registerMarkerProcessor(check, create) {
|
|
21
|
-
const alreadyRegistered = MARKER_PROCESSORS.some(p => p.check === check);
|
|
22
|
-
if (alreadyRegistered) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
MARKER_PROCESSORS.push({
|
|
26
|
-
check,
|
|
27
|
-
create: create
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
function materializeMarkers(node, notifier, path = []) {
|
|
31
|
-
if (node == null) return;
|
|
32
|
-
if (typeof node !== 'object' && typeof node !== 'function') return;
|
|
33
|
-
if (isSignal(node)) return;
|
|
34
|
-
const isAccessor = typeof node === 'function' && isNodeAccessor(node);
|
|
35
|
-
if (typeof node === 'function' && !isAccessor) return;
|
|
36
|
-
const getNotifier = () => {
|
|
37
|
-
if (!notifier) {
|
|
38
|
-
notifier = getPathNotifier();
|
|
39
|
-
}
|
|
40
|
-
return notifier;
|
|
41
|
-
};
|
|
42
|
-
const keys = Object.keys(node);
|
|
43
|
-
for (const key of keys) {
|
|
44
|
-
const value = node[key];
|
|
45
|
-
const currentPath = [...path, key];
|
|
46
|
-
const pathString = currentPath.join('.');
|
|
47
|
-
let processed = false;
|
|
48
|
-
for (const processor of MARKER_PROCESSORS) {
|
|
49
|
-
if (processor.check(value)) {
|
|
50
|
-
try {
|
|
51
|
-
const materialized = processor.create(value, getNotifier(), pathString);
|
|
52
|
-
node[key] = materialized;
|
|
53
|
-
processed = true;
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
56
|
-
console.error(`SignalTree: Failed to materialize marker at "${pathString}"`, err);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (!processed && value != null) {
|
|
63
|
-
if (isNodeAccessor(value)) {
|
|
64
|
-
materializeMarkers(value, notifier, currentPath);
|
|
65
|
-
} else if (typeof value === 'object' && !Array.isArray(value) && !isSignal(value)) {
|
|
66
|
-
materializeMarkers(value, notifier, currentPath);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export { isRegisteredMarker, materializeMarkers, registerMarkerProcessor };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { computed, isSignal } from '@angular/core';
|
|
2
|
-
import { isDerivedMarker } from '../markers/derived.js';
|
|
3
|
-
|
|
4
|
-
function isSignalLike(value) {
|
|
5
|
-
return isSignal(value);
|
|
6
|
-
}
|
|
7
|
-
function ensurePathAndGetTarget($, path) {
|
|
8
|
-
if (!path) return $;
|
|
9
|
-
const parts = path.split('.');
|
|
10
|
-
let current = $;
|
|
11
|
-
for (const part of parts) {
|
|
12
|
-
if (!(part in current)) {
|
|
13
|
-
current[part] = {};
|
|
14
|
-
}
|
|
15
|
-
current = current[part];
|
|
16
|
-
}
|
|
17
|
-
return current;
|
|
18
|
-
}
|
|
19
|
-
function mergeDerivedState($, derivedDef, path = '') {
|
|
20
|
-
if (!derivedDef || typeof derivedDef !== 'object') {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
for (const [key, value] of Object.entries(derivedDef)) {
|
|
24
|
-
const currentPath = path ? `${path}.${key}` : key;
|
|
25
|
-
if (isDerivedMarker(value)) {
|
|
26
|
-
const target = ensurePathAndGetTarget($, path);
|
|
27
|
-
if (key in target && isSignalLike(target[key])) {
|
|
28
|
-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
29
|
-
console.warn(`SignalTree: Derived "${currentPath}" overwrites source signal. ` + `Consider using a different key to avoid confusion.`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
target[key] = computed(value.factory);
|
|
33
|
-
} else if (isSignalLike(value)) {
|
|
34
|
-
const target = ensurePathAndGetTarget($, path);
|
|
35
|
-
if (key in target && isSignalLike(target[key])) {
|
|
36
|
-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
37
|
-
console.warn(`SignalTree: Derived signal "${currentPath}" overwrites source signal. ` + `Consider using a different key to avoid confusion.`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
target[key] = value;
|
|
41
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
42
|
-
const target = ensurePathAndGetTarget($, path);
|
|
43
|
-
if (!(key in target)) {
|
|
44
|
-
target[key] = {};
|
|
45
|
-
} else if (isSignalLike(target[key])) {
|
|
46
|
-
throw new Error(`SignalTree: Cannot merge derived object into "${currentPath}" ` + `because source is a signal. Either make source an object or use a different key.`);
|
|
47
|
-
}
|
|
48
|
-
mergeDerivedState($, value, currentPath);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function applyDerivedFactories($, factories) {
|
|
53
|
-
for (const factory of factories) {
|
|
54
|
-
const derivedDef = factory($);
|
|
55
|
-
mergeDerivedState($, derivedDef);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export { applyDerivedFactories, mergeDerivedState };
|