@milaboratories/pl-tree 1.8.47 → 1.8.49
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/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/accessors.cjs +236 -273
- package/dist/accessors.cjs.map +1 -1
- package/dist/accessors.d.ts +93 -93
- package/dist/accessors.js +235 -271
- package/dist/accessors.js.map +1 -1
- package/dist/dump.cjs +79 -80
- package/dist/dump.cjs.map +1 -1
- package/dist/dump.d.ts +16 -18
- package/dist/dump.js +79 -79
- package/dist/dump.js.map +1 -1
- package/dist/index.cjs +31 -35
- package/dist/index.d.ts +10 -10
- package/dist/index.js +9 -8
- package/dist/snapshot.cjs +50 -71
- package/dist/snapshot.cjs.map +1 -1
- package/dist/snapshot.d.ts +36 -38
- package/dist/snapshot.js +49 -69
- package/dist/snapshot.js.map +1 -1
- package/dist/state.cjs +487 -624
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.ts +117 -116
- package/dist/state.js +486 -622
- package/dist/state.js.map +1 -1
- package/dist/sync.cjs +113 -133
- package/dist/sync.cjs.map +1 -1
- package/dist/sync.d.ts +37 -33
- package/dist/sync.js +111 -131
- package/dist/sync.js.map +1 -1
- package/dist/synchronized_tree.cjs +167 -232
- package/dist/synchronized_tree.cjs.map +1 -1
- package/dist/synchronized_tree.d.ts +66 -68
- package/dist/synchronized_tree.js +165 -211
- package/dist/synchronized_tree.js.map +1 -1
- package/dist/traversal_ops.d.ts +42 -42
- package/dist/value_and_error.cjs +9 -12
- package/dist/value_and_error.cjs.map +1 -1
- package/dist/value_and_error.d.ts +8 -5
- package/dist/value_and_error.js +9 -11
- package/dist/value_and_error.js.map +1 -1
- package/dist/value_or_error.d.ts +8 -5
- package/package.json +9 -9
- package/src/synchronized_tree.ts +10 -5
- package/dist/accessors.d.ts.map +0 -1
- package/dist/dump.d.ts.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/snapshot.d.ts.map +0 -1
- package/dist/state.d.ts.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/synchronized_tree.d.ts.map +0 -1
- package/dist/test_utils.d.ts +0 -25
- package/dist/test_utils.d.ts.map +0 -1
- package/dist/traversal_ops.d.ts.map +0 -1
- package/dist/value_and_error.d.ts.map +0 -1
- package/dist/value_or_error.d.ts.map +0 -1
package/dist/state.js
CHANGED
|
@@ -1,627 +1,491 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { cachedDecode, cachedDeserialize, notEmpty } from
|
|
1
|
+
import { PlTreeEntry } from "./accessors.js";
|
|
2
|
+
import { NullResourceId, isNotNullResourceId, isNullResourceId, resourceIdToString, stringifyWithResourceId } from "@milaboratories/pl-client";
|
|
3
|
+
import { ChangeSource, KeyedChangeSource } from "@milaboratories/computable";
|
|
4
|
+
import { cachedDecode, cachedDeserialize, notEmpty } from "@milaboratories/ts-helpers";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
type: this.type,
|
|
35
|
-
status: this.status,
|
|
36
|
-
value: this.value,
|
|
37
|
-
error: this.error,
|
|
38
|
-
valueIsFinal: this.valueIsFinal,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
6
|
+
//#region src/state.ts
|
|
7
|
+
var TreeStateUpdateError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var PlTreeField = class {
|
|
13
|
+
change = new ChangeSource();
|
|
14
|
+
constructor(name, type, value, error, status, valueIsFinal, resourceVersion) {
|
|
15
|
+
this.name = name;
|
|
16
|
+
this.type = type;
|
|
17
|
+
this.value = value;
|
|
18
|
+
this.error = error;
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.valueIsFinal = valueIsFinal;
|
|
21
|
+
this.resourceVersion = resourceVersion;
|
|
22
|
+
}
|
|
23
|
+
get state() {
|
|
24
|
+
return {
|
|
25
|
+
name: this.name,
|
|
26
|
+
type: this.type,
|
|
27
|
+
status: this.status,
|
|
28
|
+
value: this.value,
|
|
29
|
+
error: this.error,
|
|
30
|
+
valueIsFinal: this.valueIsFinal
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
42
34
|
const InitialResourceVersion = 0;
|
|
43
35
|
/** Never store instances of this class, always get fresh instance from {@link PlTreeState} */
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
resource.verifyReadyState();
|
|
497
|
-
if (!resource.isReadyOrError)
|
|
498
|
-
unexpectedTransitionError(`resource can't lose it's ready or error state (ready state before ${readyStateBefore})`);
|
|
499
|
-
notEmpty(resource.resourceStateChange).markChanged(`ready flag changed to ${rd.resourceReady} for ${resourceIdToString(resource.id)}`);
|
|
500
|
-
changed = true;
|
|
501
|
-
}
|
|
502
|
-
// syncing kv
|
|
503
|
-
for (const kv of rd.kv) {
|
|
504
|
-
const current = resource.kv.get(kv.key);
|
|
505
|
-
if (current === undefined) {
|
|
506
|
-
resource.kv.set(kv.key, kv.value);
|
|
507
|
-
notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv added for ${resourceIdToString(resource.id)}: ${kv.key}`);
|
|
508
|
-
}
|
|
509
|
-
else if (Buffer.compare(current, kv.value) !== 0) {
|
|
510
|
-
resource.kv.set(kv.key, kv.value);
|
|
511
|
-
notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv changed for ${resourceIdToString(resource.id)}: ${kv.key}`);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (resource.kv.size > rd.kv.length) {
|
|
515
|
-
// only it this case it makes sense to check for deletions
|
|
516
|
-
const newStateKeys = new Set(rd.kv.map((kv) => kv.key));
|
|
517
|
-
// deleting keys not present in resource anymore
|
|
518
|
-
resource.kv.forEach((_value, key, map) => {
|
|
519
|
-
if (!newStateKeys.has(key)) {
|
|
520
|
-
map.delete(key);
|
|
521
|
-
notEmpty(resource.kvChangedPerKey).markChanged(key, `kv deleted for ${resourceIdToString(resource.id)}: ${key}`);
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
if (changed) {
|
|
526
|
-
// if resource was changed, updating resource data version
|
|
527
|
-
resource.dataVersion = resource.version;
|
|
528
|
-
if (this.isFinalPredicate(resource))
|
|
529
|
-
resource.markFinal();
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
else {
|
|
533
|
-
// creating new resource
|
|
534
|
-
resource = new PlTreeResource(rd);
|
|
535
|
-
resource.verifyReadyState();
|
|
536
|
-
if (isNotNullResourceId(resource.error))
|
|
537
|
-
incrementRefs.push(resource.error);
|
|
538
|
-
for (const fd of rd.fields) {
|
|
539
|
-
const field = new PlTreeField(fd.name, fd.type, fd.value, fd.error, fd.status, fd.valueIsFinal, InitialResourceVersion);
|
|
540
|
-
if (isNotNullResourceId(fd.value))
|
|
541
|
-
incrementRefs.push(fd.value);
|
|
542
|
-
if (isNotNullResourceId(fd.error))
|
|
543
|
-
incrementRefs.push(fd.error);
|
|
544
|
-
resource.fieldsMap.set(fd.name, field);
|
|
545
|
-
}
|
|
546
|
-
// adding kv
|
|
547
|
-
for (const kv of rd.kv)
|
|
548
|
-
resource.kv.set(kv.key, kv.value);
|
|
549
|
-
// checking that resource is final, and if so, marking it
|
|
550
|
-
if (this.isFinalPredicate(resource))
|
|
551
|
-
resource.markFinal();
|
|
552
|
-
// adding the resource to the heap
|
|
553
|
-
this.resources.set(resource.id, resource);
|
|
554
|
-
this.resourcesAdded.markChanged(`new resource ${resourceIdToString(resource.id)} added`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
// applying refCount increments
|
|
558
|
-
for (const rid of incrementRefs) {
|
|
559
|
-
const res = this.resources.get(rid);
|
|
560
|
-
if (!res) {
|
|
561
|
-
this.invalidateTree();
|
|
562
|
-
throw new TreeStateUpdateError(`orphan resource ${rid}`);
|
|
563
|
-
}
|
|
564
|
-
res.refCount++;
|
|
565
|
-
}
|
|
566
|
-
// recursively applying refCount decrements / doing garbage collection
|
|
567
|
-
let currentRefs = decrementRefs;
|
|
568
|
-
while (currentRefs.length > 0) {
|
|
569
|
-
const nextRefs = [];
|
|
570
|
-
for (const rid of currentRefs) {
|
|
571
|
-
const res = this.resources.get(rid);
|
|
572
|
-
if (!res) {
|
|
573
|
-
this.invalidateTree();
|
|
574
|
-
throw new TreeStateUpdateError(`orphan resource ${rid}`);
|
|
575
|
-
}
|
|
576
|
-
res.refCount--;
|
|
577
|
-
// garbage collection
|
|
578
|
-
if (res.refCount === 0 && res.id !== this.root) {
|
|
579
|
-
// removing fields
|
|
580
|
-
res.fieldsMap.forEach((field) => {
|
|
581
|
-
if (isNotNullResourceId(field.value))
|
|
582
|
-
nextRefs.push(field.value);
|
|
583
|
-
if (isNotNullResourceId(field.error))
|
|
584
|
-
nextRefs.push(field.error);
|
|
585
|
-
field.change.markChanged(`field ${field.name} removed during garbage collection of ${resourceIdToString(res.id)}`);
|
|
586
|
-
});
|
|
587
|
-
if (isNotNullResourceId(res.error))
|
|
588
|
-
nextRefs.push(res.error);
|
|
589
|
-
res.resourceRemoved.markChanged(`resource removed during garbage collection: ${resourceIdToString(res.id)}`);
|
|
590
|
-
this.resources.delete(rid);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
currentRefs = nextRefs;
|
|
594
|
-
}
|
|
595
|
-
// checking for orphans (maybe removed in the future)
|
|
596
|
-
if (!allowOrphanInputs) {
|
|
597
|
-
for (const rd of resourceData) {
|
|
598
|
-
if (!this.resources.has(rd.id)) {
|
|
599
|
-
this.invalidateTree();
|
|
600
|
-
throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
/** @deprecated use "entry" instead */
|
|
606
|
-
accessor(rid = this.root) {
|
|
607
|
-
this.checkValid();
|
|
608
|
-
return this.entry(rid);
|
|
609
|
-
}
|
|
610
|
-
entry(rid = this.root) {
|
|
611
|
-
this.checkValid();
|
|
612
|
-
return new PlTreeEntry({ treeProvider: () => this }, rid);
|
|
613
|
-
}
|
|
614
|
-
invalidateTree(msg) {
|
|
615
|
-
this._isValid = false;
|
|
616
|
-
this.invalidationMessage = msg;
|
|
617
|
-
this.resources.forEach((res) => {
|
|
618
|
-
res.markAllChanged();
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
dumpState() {
|
|
622
|
-
return Array.from(this.resources.values()).map((res) => res.extendedState);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
36
|
+
var PlTreeResource = class {
|
|
37
|
+
/** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */
|
|
38
|
+
refCount = 0;
|
|
39
|
+
/** Increments each time resource is checked for difference with new state */
|
|
40
|
+
version = InitialResourceVersion;
|
|
41
|
+
/** Set to resource version when resource state, or it's fields have changed */
|
|
42
|
+
dataVersion = InitialResourceVersion;
|
|
43
|
+
fieldsMap = /* @__PURE__ */ new Map();
|
|
44
|
+
kv = /* @__PURE__ */ new Map();
|
|
45
|
+
resourceRemoved = new ChangeSource();
|
|
46
|
+
finalChanged = new ChangeSource();
|
|
47
|
+
resourceStateChange = new ChangeSource();
|
|
48
|
+
lockedChange = new ChangeSource();
|
|
49
|
+
inputAndServiceFieldListChanged = new ChangeSource();
|
|
50
|
+
outputFieldListChanged = new ChangeSource();
|
|
51
|
+
dynamicFieldListChanged = new ChangeSource();
|
|
52
|
+
kvChangedPerKey = new KeyedChangeSource();
|
|
53
|
+
id;
|
|
54
|
+
originalResourceId;
|
|
55
|
+
kind;
|
|
56
|
+
type;
|
|
57
|
+
data;
|
|
58
|
+
dataAsString;
|
|
59
|
+
dataAsJson;
|
|
60
|
+
error;
|
|
61
|
+
inputsLocked;
|
|
62
|
+
outputsLocked;
|
|
63
|
+
resourceReady;
|
|
64
|
+
finalFlag;
|
|
65
|
+
/** Set externally by the tree, using {@link FinalResourceDataPredicate} */
|
|
66
|
+
_finalState = false;
|
|
67
|
+
logger;
|
|
68
|
+
constructor(initialState, logger) {
|
|
69
|
+
this.id = initialState.id;
|
|
70
|
+
this.originalResourceId = initialState.originalResourceId;
|
|
71
|
+
this.kind = initialState.kind;
|
|
72
|
+
this.type = initialState.type;
|
|
73
|
+
this.data = initialState.data;
|
|
74
|
+
this.error = initialState.error;
|
|
75
|
+
this.inputsLocked = initialState.inputsLocked;
|
|
76
|
+
this.outputsLocked = initialState.outputsLocked;
|
|
77
|
+
this.resourceReady = initialState.resourceReady;
|
|
78
|
+
this.finalFlag = initialState.final;
|
|
79
|
+
this.logger = logger;
|
|
80
|
+
}
|
|
81
|
+
info(msg) {
|
|
82
|
+
if (this.logger !== void 0) this.logger.info(msg);
|
|
83
|
+
}
|
|
84
|
+
warn(msg) {
|
|
85
|
+
if (this.logger !== void 0) this.logger.warn(msg);
|
|
86
|
+
}
|
|
87
|
+
get final() {
|
|
88
|
+
return this.finalFlag;
|
|
89
|
+
}
|
|
90
|
+
get finalState() {
|
|
91
|
+
return this._finalState;
|
|
92
|
+
}
|
|
93
|
+
get fields() {
|
|
94
|
+
return [...this.fieldsMap.values()];
|
|
95
|
+
}
|
|
96
|
+
getField(watcher, _step, onUnstable = () => {}) {
|
|
97
|
+
const step = typeof _step === "string" ? { field: _step } : _step;
|
|
98
|
+
const field = this.fieldsMap.get(step.field);
|
|
99
|
+
if (field === void 0) {
|
|
100
|
+
if (step.errorIfFieldNotFound || step.errorIfFieldNotSet) throw new Error(`Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`);
|
|
101
|
+
if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
|
|
102
|
+
else if (step.assertFieldType === "Service" || step.assertFieldType === "Input") if (step.allowPermanentAbsence) return void 0;
|
|
103
|
+
else throw new Error(`Service or input field not found ${step.field}.`);
|
|
104
|
+
if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
|
|
105
|
+
else if (step.assertFieldType === "Output") if (step.allowPermanentAbsence) return void 0;
|
|
106
|
+
else throw new Error(`Output field not found ${step.field}.`);
|
|
107
|
+
this.dynamicFieldListChanged?.attachWatcher(watcher);
|
|
108
|
+
if (!this._finalState && !step.stableIfNotFound) onUnstable("field_not_found:" + step.field);
|
|
109
|
+
return;
|
|
110
|
+
} else {
|
|
111
|
+
if (step.assertFieldType !== void 0 && field.type !== step.assertFieldType) throw new Error(`Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`);
|
|
112
|
+
const ret = {};
|
|
113
|
+
if (isNotNullResourceId(field.value)) ret.value = field.value;
|
|
114
|
+
if (isNotNullResourceId(field.error)) ret.error = field.error;
|
|
115
|
+
if (ret.value === void 0 && ret.error === void 0) onUnstable("field_not_resolved:" + step.field);
|
|
116
|
+
field.change.attachWatcher(watcher);
|
|
117
|
+
return ret;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
getInputsLocked(watcher) {
|
|
121
|
+
if (!this.inputsLocked) this.resourceStateChange?.attachWatcher(watcher);
|
|
122
|
+
return this.inputsLocked;
|
|
123
|
+
}
|
|
124
|
+
getOutputsLocked(watcher) {
|
|
125
|
+
if (!this.outputsLocked) this.resourceStateChange?.attachWatcher(watcher);
|
|
126
|
+
return this.outputsLocked;
|
|
127
|
+
}
|
|
128
|
+
get isReadyOrError() {
|
|
129
|
+
return this.error !== NullResourceId || this.resourceReady || this.originalResourceId !== NullResourceId;
|
|
130
|
+
}
|
|
131
|
+
getIsFinal(watcher) {
|
|
132
|
+
this.finalChanged?.attachWatcher(watcher);
|
|
133
|
+
return this._finalState;
|
|
134
|
+
}
|
|
135
|
+
getIsReadyOrError(watcher) {
|
|
136
|
+
if (!this.isReadyOrError) this.resourceStateChange?.attachWatcher(watcher);
|
|
137
|
+
return this.isReadyOrError;
|
|
138
|
+
}
|
|
139
|
+
getError(watcher) {
|
|
140
|
+
if (isNullResourceId(this.error)) {
|
|
141
|
+
this.resourceStateChange?.attachWatcher(watcher);
|
|
142
|
+
return;
|
|
143
|
+
} else return this.error;
|
|
144
|
+
}
|
|
145
|
+
listInputFields(watcher) {
|
|
146
|
+
const ret = [];
|
|
147
|
+
this.fieldsMap.forEach((field, name) => {
|
|
148
|
+
if (field.type === "Input" || field.type === "Service") ret.push(name);
|
|
149
|
+
});
|
|
150
|
+
if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
|
|
151
|
+
return ret;
|
|
152
|
+
}
|
|
153
|
+
listOutputFields(watcher) {
|
|
154
|
+
const ret = [];
|
|
155
|
+
this.fieldsMap.forEach((field, name) => {
|
|
156
|
+
if (field.type === "Output") ret.push(name);
|
|
157
|
+
});
|
|
158
|
+
if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
|
|
159
|
+
return ret;
|
|
160
|
+
}
|
|
161
|
+
listDynamicFields(watcher) {
|
|
162
|
+
const ret = [];
|
|
163
|
+
this.fieldsMap.forEach((field, name) => {
|
|
164
|
+
if (field.type !== "Input" && field.type !== "Output") ret.push(name);
|
|
165
|
+
});
|
|
166
|
+
this.dynamicFieldListChanged?.attachWatcher(watcher);
|
|
167
|
+
return ret;
|
|
168
|
+
}
|
|
169
|
+
getKeyValue(watcher, key) {
|
|
170
|
+
this.kvChangedPerKey?.attachWatcher(key, watcher);
|
|
171
|
+
return this.kv.get(key);
|
|
172
|
+
}
|
|
173
|
+
getKeyValueString(watcher, key) {
|
|
174
|
+
const bytes = this.getKeyValue(watcher, key);
|
|
175
|
+
if (bytes === void 0) return void 0;
|
|
176
|
+
return cachedDecode(bytes);
|
|
177
|
+
}
|
|
178
|
+
getKeyValueAsJson(watcher, key) {
|
|
179
|
+
const bytes = this.getKeyValue(watcher, key);
|
|
180
|
+
if (bytes === void 0) return void 0;
|
|
181
|
+
return cachedDeserialize(bytes);
|
|
182
|
+
}
|
|
183
|
+
getDataAsString() {
|
|
184
|
+
if (this.data === void 0) return void 0;
|
|
185
|
+
if (this.dataAsString === void 0) this.dataAsString = cachedDecode(this.data);
|
|
186
|
+
return this.dataAsString;
|
|
187
|
+
}
|
|
188
|
+
getDataAsJson() {
|
|
189
|
+
if (this.data === void 0) return void 0;
|
|
190
|
+
if (this.dataAsJson === void 0) this.dataAsJson = cachedDeserialize(this.data);
|
|
191
|
+
return this.dataAsJson;
|
|
192
|
+
}
|
|
193
|
+
verifyReadyState() {
|
|
194
|
+
if (this.resourceReady && !this.inputsLocked) throw new Error(`ready without input or output lock: ${stringifyWithResourceId(this.basicState)}`);
|
|
195
|
+
}
|
|
196
|
+
get basicState() {
|
|
197
|
+
return {
|
|
198
|
+
id: this.id,
|
|
199
|
+
kind: this.kind,
|
|
200
|
+
type: this.type,
|
|
201
|
+
data: this.data,
|
|
202
|
+
resourceReady: this.resourceReady,
|
|
203
|
+
inputsLocked: this.inputsLocked,
|
|
204
|
+
outputsLocked: this.outputsLocked,
|
|
205
|
+
error: this.error,
|
|
206
|
+
originalResourceId: this.originalResourceId,
|
|
207
|
+
final: this.finalFlag
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
get extendedState() {
|
|
211
|
+
return {
|
|
212
|
+
...this.basicState,
|
|
213
|
+
fields: this.fields,
|
|
214
|
+
kv: Array.from(this.kv.entries()).map(([key, value]) => ({
|
|
215
|
+
key,
|
|
216
|
+
value
|
|
217
|
+
}))
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/** Called when {@link FinalResourceDataPredicate} returns true for the state. */
|
|
221
|
+
markFinal() {
|
|
222
|
+
if (this._finalState) return;
|
|
223
|
+
this._finalState = true;
|
|
224
|
+
notEmpty(this.finalChanged).markChanged("marked final");
|
|
225
|
+
this.finalChanged = void 0;
|
|
226
|
+
this.resourceStateChange = void 0;
|
|
227
|
+
this.dynamicFieldListChanged = void 0;
|
|
228
|
+
this.inputAndServiceFieldListChanged = void 0;
|
|
229
|
+
this.outputFieldListChanged = void 0;
|
|
230
|
+
this.lockedChange = void 0;
|
|
231
|
+
this.kvChangedPerKey = void 0;
|
|
232
|
+
}
|
|
233
|
+
/** Used for invalidation */
|
|
234
|
+
markAllChanged() {
|
|
235
|
+
this.fieldsMap.forEach((field) => field.change.markChanged("marked all changed"));
|
|
236
|
+
this.finalChanged?.markChanged("marked all changed");
|
|
237
|
+
this.resourceStateChange?.markChanged("marked all changed");
|
|
238
|
+
this.lockedChange?.markChanged("marked all changed");
|
|
239
|
+
this.inputAndServiceFieldListChanged?.markChanged("marked all changed");
|
|
240
|
+
this.outputFieldListChanged?.markChanged("marked all changed");
|
|
241
|
+
this.dynamicFieldListChanged?.markChanged("marked all changed");
|
|
242
|
+
this.kvChangedPerKey?.markAllChanged("marked all changed");
|
|
243
|
+
this.resourceRemoved.markChanged("marked all changed");
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var PlTreeState = class {
|
|
247
|
+
/** resource heap */
|
|
248
|
+
resources = /* @__PURE__ */ new Map();
|
|
249
|
+
resourcesAdded = new ChangeSource();
|
|
250
|
+
/** Resets to false if any invalid state transitions are registered,
|
|
251
|
+
* after that tree will produce errors for any read or write operations */
|
|
252
|
+
_isValid = true;
|
|
253
|
+
invalidationMessage;
|
|
254
|
+
constructor(root, isFinalPredicate) {
|
|
255
|
+
this.root = root;
|
|
256
|
+
this.isFinalPredicate = isFinalPredicate;
|
|
257
|
+
}
|
|
258
|
+
forEachResource(cb) {
|
|
259
|
+
this.resources.forEach((v) => cb(v));
|
|
260
|
+
}
|
|
261
|
+
checkValid() {
|
|
262
|
+
if (!this._isValid) throw new Error(this.invalidationMessage ?? "tree is in invalid state");
|
|
263
|
+
}
|
|
264
|
+
get(watcher, rid) {
|
|
265
|
+
this.checkValid();
|
|
266
|
+
const res = this.resources.get(rid);
|
|
267
|
+
if (res === void 0) {
|
|
268
|
+
this.resourcesAdded.attachWatcher(watcher);
|
|
269
|
+
throw new Error(`resource ${resourceIdToString(rid)} not found in the tree`);
|
|
270
|
+
}
|
|
271
|
+
res.resourceRemoved.attachWatcher(watcher);
|
|
272
|
+
return res;
|
|
273
|
+
}
|
|
274
|
+
updateFromResourceData(resourceData, allowOrphanInputs = false) {
|
|
275
|
+
this.checkValid();
|
|
276
|
+
const incrementRefs = [];
|
|
277
|
+
const decrementRefs = [];
|
|
278
|
+
for (const rd of resourceData) {
|
|
279
|
+
let resource = this.resources.get(rd.id);
|
|
280
|
+
const statBeforeMutation = resource?.basicState;
|
|
281
|
+
const unexpectedTransitionError = (reason) => {
|
|
282
|
+
const { fields, ...rdWithoutFields } = rd;
|
|
283
|
+
this.invalidateTree();
|
|
284
|
+
throw new TreeStateUpdateError(`Unexpected resource state transition (${reason}): ${stringifyWithResourceId(rdWithoutFields)} -> ${stringifyWithResourceId(statBeforeMutation)}`);
|
|
285
|
+
};
|
|
286
|
+
if (resource !== void 0) {
|
|
287
|
+
if (resource.finalState) unexpectedTransitionError("resource state can be updated after it is marked as final");
|
|
288
|
+
let changed = false;
|
|
289
|
+
resource.version += 1;
|
|
290
|
+
if (resource.originalResourceId !== rd.originalResourceId) {
|
|
291
|
+
if (resource.originalResourceId !== NullResourceId) unexpectedTransitionError("originalResourceId can't change after it is set");
|
|
292
|
+
resource.originalResourceId = rd.originalResourceId;
|
|
293
|
+
notEmpty(resource.resourceStateChange).markChanged(`originalResourceId changed for ${resourceIdToString(resource.id)}`);
|
|
294
|
+
changed = true;
|
|
295
|
+
}
|
|
296
|
+
if (resource.error !== rd.error) {
|
|
297
|
+
if (isNotNullResourceId(resource.error)) unexpectedTransitionError("resource can't change attached error after it is set");
|
|
298
|
+
resource.error = rd.error;
|
|
299
|
+
incrementRefs.push(resource.error);
|
|
300
|
+
notEmpty(resource.resourceStateChange).markChanged(`error changed for ${resourceIdToString(resource.id)}`);
|
|
301
|
+
changed = true;
|
|
302
|
+
}
|
|
303
|
+
for (const fd of rd.fields) {
|
|
304
|
+
let field = resource.fieldsMap.get(fd.name);
|
|
305
|
+
if (!field) {
|
|
306
|
+
field = new PlTreeField(fd.name, fd.type, fd.value, fd.error, fd.status, fd.valueIsFinal, resource.version);
|
|
307
|
+
if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
|
|
308
|
+
if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
|
|
309
|
+
if (fd.type === "Input" || fd.type === "Service") {
|
|
310
|
+
if (resource.inputsLocked) unexpectedTransitionError(`adding ${fd.type} (${fd.name}) field while inputs locked`);
|
|
311
|
+
notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);
|
|
312
|
+
} else if (fd.type === "Output") {
|
|
313
|
+
if (resource.outputsLocked) unexpectedTransitionError(`adding ${fd.type} (${fd.name}) field while outputs locked`);
|
|
314
|
+
notEmpty(resource.outputFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);
|
|
315
|
+
} else notEmpty(resource.dynamicFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);
|
|
316
|
+
resource.fieldsMap.set(fd.name, field);
|
|
317
|
+
changed = true;
|
|
318
|
+
} else {
|
|
319
|
+
if (field.type !== fd.type) {
|
|
320
|
+
if (field.type !== "Dynamic") unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);
|
|
321
|
+
notEmpty(resource.dynamicFieldListChanged).markChanged(`field ${fd.name} changed type from Dynamic to ${fd.type} in ${resourceIdToString(resource.id)}`);
|
|
322
|
+
if (field.type === "Input" || field.type === "Service") {
|
|
323
|
+
if (resource.inputsLocked) unexpectedTransitionError(`adding input field "${fd.name}", while corresponding list is locked`);
|
|
324
|
+
notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);
|
|
325
|
+
}
|
|
326
|
+
if (field.type === "Output") {
|
|
327
|
+
if (resource.outputsLocked) unexpectedTransitionError(`adding output field "${fd.name}", while corresponding list is locked`);
|
|
328
|
+
notEmpty(resource.outputFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);
|
|
329
|
+
}
|
|
330
|
+
field.type = fd.type;
|
|
331
|
+
field.change.markChanged(`field ${fd.name} type changed to ${fd.type} in ${resourceIdToString(resource.id)}`);
|
|
332
|
+
changed = true;
|
|
333
|
+
}
|
|
334
|
+
if (field.value !== fd.value) {
|
|
335
|
+
if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);
|
|
336
|
+
field.value = fd.value;
|
|
337
|
+
if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
|
|
338
|
+
field.change.markChanged(`field ${fd.name} value changed in ${resourceIdToString(resource.id)}`);
|
|
339
|
+
changed = true;
|
|
340
|
+
}
|
|
341
|
+
if (field.error !== fd.error) {
|
|
342
|
+
if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);
|
|
343
|
+
field.error = fd.error;
|
|
344
|
+
if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
|
|
345
|
+
field.change.markChanged(`field ${fd.name} error changed in ${resourceIdToString(resource.id)}`);
|
|
346
|
+
changed = true;
|
|
347
|
+
}
|
|
348
|
+
if (field.status !== fd.status) {
|
|
349
|
+
field.status = fd.status;
|
|
350
|
+
field.change.markChanged(`field ${fd.name} status changed to ${fd.status} in ${resourceIdToString(resource.id)}`);
|
|
351
|
+
changed = true;
|
|
352
|
+
}
|
|
353
|
+
if (field.valueIsFinal !== fd.valueIsFinal) {
|
|
354
|
+
field.valueIsFinal = fd.valueIsFinal;
|
|
355
|
+
field.change.markChanged(`field ${fd.name} valueIsFinal changed to ${fd.valueIsFinal} in ${resourceIdToString(resource.id)}`);
|
|
356
|
+
changed = true;
|
|
357
|
+
}
|
|
358
|
+
field.resourceVersion = resource.version;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
resource.fieldsMap.forEach((field, fieldName, fields) => {
|
|
362
|
+
if (field.resourceVersion !== resource.version) {
|
|
363
|
+
if (field.type === "Input" || field.type === "Service" || field.type === "Output") unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);
|
|
364
|
+
field.change.markChanged(`dynamic field ${fieldName} removed from ${resourceIdToString(resource.id)}`);
|
|
365
|
+
fields.delete(fieldName);
|
|
366
|
+
if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);
|
|
367
|
+
if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);
|
|
368
|
+
notEmpty(resource.dynamicFieldListChanged).markChanged(`dynamic field ${fieldName} removed from ${resourceIdToString(resource.id)}`);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
if (resource.inputsLocked !== rd.inputsLocked) {
|
|
372
|
+
if (resource.inputsLocked) unexpectedTransitionError("inputs unlocking is not permitted");
|
|
373
|
+
resource.inputsLocked = rd.inputsLocked;
|
|
374
|
+
notEmpty(resource.lockedChange).markChanged(`inputs locked for ${resourceIdToString(resource.id)}`);
|
|
375
|
+
changed = true;
|
|
376
|
+
}
|
|
377
|
+
if (resource.outputsLocked !== rd.outputsLocked) {
|
|
378
|
+
if (resource.outputsLocked) unexpectedTransitionError("outputs unlocking is not permitted");
|
|
379
|
+
resource.outputsLocked = rd.outputsLocked;
|
|
380
|
+
notEmpty(resource.lockedChange).markChanged(`outputs locked for ${resourceIdToString(resource.id)}`);
|
|
381
|
+
changed = true;
|
|
382
|
+
}
|
|
383
|
+
if (resource.resourceReady !== rd.resourceReady) {
|
|
384
|
+
const readyStateBefore = resource.resourceReady;
|
|
385
|
+
resource.resourceReady = rd.resourceReady;
|
|
386
|
+
resource.verifyReadyState();
|
|
387
|
+
if (!resource.isReadyOrError) unexpectedTransitionError(`resource can't lose it's ready or error state (ready state before ${readyStateBefore})`);
|
|
388
|
+
notEmpty(resource.resourceStateChange).markChanged(`ready flag changed to ${rd.resourceReady} for ${resourceIdToString(resource.id)}`);
|
|
389
|
+
changed = true;
|
|
390
|
+
}
|
|
391
|
+
for (const kv of rd.kv) {
|
|
392
|
+
const current = resource.kv.get(kv.key);
|
|
393
|
+
if (current === void 0) {
|
|
394
|
+
resource.kv.set(kv.key, kv.value);
|
|
395
|
+
notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv added for ${resourceIdToString(resource.id)}: ${kv.key}`);
|
|
396
|
+
} else if (Buffer.compare(current, kv.value) !== 0) {
|
|
397
|
+
resource.kv.set(kv.key, kv.value);
|
|
398
|
+
notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv changed for ${resourceIdToString(resource.id)}: ${kv.key}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (resource.kv.size > rd.kv.length) {
|
|
402
|
+
const newStateKeys = new Set(rd.kv.map((kv) => kv.key));
|
|
403
|
+
resource.kv.forEach((_value, key, map) => {
|
|
404
|
+
if (!newStateKeys.has(key)) {
|
|
405
|
+
map.delete(key);
|
|
406
|
+
notEmpty(resource.kvChangedPerKey).markChanged(key, `kv deleted for ${resourceIdToString(resource.id)}: ${key}`);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (changed) {
|
|
411
|
+
resource.dataVersion = resource.version;
|
|
412
|
+
if (this.isFinalPredicate(resource)) resource.markFinal();
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
resource = new PlTreeResource(rd);
|
|
416
|
+
resource.verifyReadyState();
|
|
417
|
+
if (isNotNullResourceId(resource.error)) incrementRefs.push(resource.error);
|
|
418
|
+
for (const fd of rd.fields) {
|
|
419
|
+
const field = new PlTreeField(fd.name, fd.type, fd.value, fd.error, fd.status, fd.valueIsFinal, InitialResourceVersion);
|
|
420
|
+
if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
|
|
421
|
+
if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
|
|
422
|
+
resource.fieldsMap.set(fd.name, field);
|
|
423
|
+
}
|
|
424
|
+
for (const kv of rd.kv) resource.kv.set(kv.key, kv.value);
|
|
425
|
+
if (this.isFinalPredicate(resource)) resource.markFinal();
|
|
426
|
+
this.resources.set(resource.id, resource);
|
|
427
|
+
this.resourcesAdded.markChanged(`new resource ${resourceIdToString(resource.id)} added`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
for (const rid of incrementRefs) {
|
|
431
|
+
const res = this.resources.get(rid);
|
|
432
|
+
if (!res) {
|
|
433
|
+
this.invalidateTree();
|
|
434
|
+
throw new TreeStateUpdateError(`orphan resource ${rid}`);
|
|
435
|
+
}
|
|
436
|
+
res.refCount++;
|
|
437
|
+
}
|
|
438
|
+
let currentRefs = decrementRefs;
|
|
439
|
+
while (currentRefs.length > 0) {
|
|
440
|
+
const nextRefs = [];
|
|
441
|
+
for (const rid of currentRefs) {
|
|
442
|
+
const res = this.resources.get(rid);
|
|
443
|
+
if (!res) {
|
|
444
|
+
this.invalidateTree();
|
|
445
|
+
throw new TreeStateUpdateError(`orphan resource ${rid}`);
|
|
446
|
+
}
|
|
447
|
+
res.refCount--;
|
|
448
|
+
if (res.refCount === 0 && res.id !== this.root) {
|
|
449
|
+
res.fieldsMap.forEach((field) => {
|
|
450
|
+
if (isNotNullResourceId(field.value)) nextRefs.push(field.value);
|
|
451
|
+
if (isNotNullResourceId(field.error)) nextRefs.push(field.error);
|
|
452
|
+
field.change.markChanged(`field ${field.name} removed during garbage collection of ${resourceIdToString(res.id)}`);
|
|
453
|
+
});
|
|
454
|
+
if (isNotNullResourceId(res.error)) nextRefs.push(res.error);
|
|
455
|
+
res.resourceRemoved.markChanged(`resource removed during garbage collection: ${resourceIdToString(res.id)}`);
|
|
456
|
+
this.resources.delete(rid);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
currentRefs = nextRefs;
|
|
460
|
+
}
|
|
461
|
+
if (!allowOrphanInputs) {
|
|
462
|
+
for (const rd of resourceData) if (!this.resources.has(rd.id)) {
|
|
463
|
+
this.invalidateTree();
|
|
464
|
+
throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/** @deprecated use "entry" instead */
|
|
469
|
+
accessor(rid = this.root) {
|
|
470
|
+
this.checkValid();
|
|
471
|
+
return this.entry(rid);
|
|
472
|
+
}
|
|
473
|
+
entry(rid = this.root) {
|
|
474
|
+
this.checkValid();
|
|
475
|
+
return new PlTreeEntry({ treeProvider: () => this }, rid);
|
|
476
|
+
}
|
|
477
|
+
invalidateTree(msg) {
|
|
478
|
+
this._isValid = false;
|
|
479
|
+
this.invalidationMessage = msg;
|
|
480
|
+
this.resources.forEach((res) => {
|
|
481
|
+
res.markAllChanged();
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
dumpState() {
|
|
485
|
+
return Array.from(this.resources.values()).map((res) => res.extendedState);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
625
488
|
|
|
489
|
+
//#endregion
|
|
626
490
|
export { PlTreeResource, PlTreeState, TreeStateUpdateError };
|
|
627
|
-
//# sourceMappingURL=state.js.map
|
|
491
|
+
//# sourceMappingURL=state.js.map
|