@milaboratories/pl-tree 1.3.6 → 1.3.7

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/index.js CHANGED
@@ -1,979 +1,2 @@
1
- import { resourceIdToString, isNotNullResourceId, NullResourceId, isNullResourceId, stringifyWithResourceId, isTimeoutOrCancelError, resourceTypeToString, resourceTypesEqual } from '@milaboratories/pl-client';
2
- import { ChangeSource, PollingComputableHooks } from '@milaboratories/computable';
3
- import { notEmpty } from '@milaboratories/ts-helpers';
4
- import Denque from 'denque';
5
- import * as tp from 'node:timers/promises';
6
-
7
- // src/state.ts
8
-
9
- // src/value_and_error.ts
10
- function mapValueAndErrorIfDefined(input, mapping) {
11
- if (input === void 0) return void 0;
12
- else return mapValueAndError(input, mapping);
13
- }
14
- function mapValueAndError(input, mapping) {
15
- const ret = {};
16
- if (input.value !== void 0) ret.value = mapping(input.value);
17
- if (input.error !== void 0) ret.error = mapping(input.error);
18
- return ret;
19
- }
20
-
21
- // src/accessors.ts
22
- var PlError = class extends Error {
23
- constructor(message) {
24
- super(message);
25
- }
26
- };
27
- function isPlTreeEntry(obj) {
28
- return typeof obj === "object" && obj !== null && obj["__pl_tree_type_marker__"] === "PlTreeEntry";
29
- }
30
- function isPlTreeEntryAccessor(obj) {
31
- return typeof obj === "object" && obj !== null && obj["__pl_tree_type_marker__"] === "PlTreeEntryAccessor";
32
- }
33
- function isPlTreeNodeAccessor(obj) {
34
- return typeof obj === "object" && obj !== null && obj["__pl_tree_type_marker__"] === "PlTreeNodeAccessor";
35
- }
36
- var PlTreeEntry = class {
37
- constructor(accessorData, rid) {
38
- this.accessorData = accessorData;
39
- this.rid = rid;
40
- }
41
- __pl_tree_type_marker__ = "PlTreeEntry";
42
- createAccessor(ctx, guard) {
43
- return new PlTreeEntryAccessor(this.accessorData, this.accessorData.treeProvider(), this.rid, {
44
- ctx,
45
- guard
46
- });
47
- }
48
- toJSON() {
49
- return this.toString();
50
- }
51
- toString() {
52
- return `[ENTRY:${resourceIdToString(this.rid)}]`;
53
- }
54
- };
55
- function getResourceFromTree(accessorData, tree, instanceData, rid, ops) {
56
- const acc = new PlTreeNodeAccessor(
57
- accessorData,
58
- tree,
59
- tree.get(instanceData.ctx.watcher, rid),
60
- instanceData
61
- );
62
- if (!ops.ignoreError) {
63
- const err = acc.getError();
64
- if (err !== void 0)
65
- throw new PlError(
66
- `error encountered on resource ${resourceIdToString(acc.id)} (${resourceTypeToString(acc.resourceType)}): ${err.getDataAsString()}`
67
- );
68
- }
69
- if (ops.assertResourceType !== void 0 && (Array.isArray(ops.assertResourceType) ? ops.assertResourceType.findIndex((rt) => resourceTypesEqual(rt, acc.resourceType)) === -1 : !resourceTypesEqual(ops.assertResourceType, acc.resourceType)))
70
- throw new Error(
71
- `wrong resource type ${resourceTypeToString(acc.resourceType)} but expected ${ops.assertResourceType}`
72
- );
73
- return acc;
74
- }
75
- var PlTreeEntryAccessor = class {
76
- constructor(accessorData, tree, rid, instanceData) {
77
- this.accessorData = accessorData;
78
- this.tree = tree;
79
- this.rid = rid;
80
- this.instanceData = instanceData;
81
- }
82
- __pl_tree_type_marker__ = "PlTreeEntryAccessor";
83
- node(ops = {}) {
84
- this.instanceData.guard();
85
- if (this.accessorData.hooks !== void 0)
86
- this.instanceData.ctx.attacheHooks(this.accessorData.hooks);
87
- return getResourceFromTree(this.accessorData, this.tree, this.instanceData, this.rid, ops);
88
- }
89
- };
90
- function treeEntryToResourceInfo(res, ctx) {
91
- if (res instanceof PlTreeEntry) return ctx.accessor(res).node().resourceInfo;
92
- return res;
93
- }
94
- var PlTreeNodeAccessor = class {
95
- constructor(accessorData, tree, resource, instanceData) {
96
- this.accessorData = accessorData;
97
- this.tree = tree;
98
- this.resource = resource;
99
- this.instanceData = instanceData;
100
- }
101
- __pl_tree_type_marker__ = "PlTreeNodeAccessor";
102
- get id() {
103
- this.instanceData.guard();
104
- return this.resource.id;
105
- }
106
- get originalId() {
107
- this.instanceData.guard();
108
- return this.resource.originalResourceId;
109
- }
110
- get resourceType() {
111
- this.instanceData.guard();
112
- return this.resource.type;
113
- }
114
- get resourceInfo() {
115
- return { id: this.id, type: this.resourceType };
116
- }
117
- getResourceFromTree(rid, ops) {
118
- return getResourceFromTree(this.accessorData, this.tree, this.instanceData, rid, ops);
119
- }
120
- traverse(...steps) {
121
- return this.traverseWithCommon({}, ...steps);
122
- }
123
- traverseOrError(...steps) {
124
- return this.traverseOrErrorWithCommon({}, ...steps);
125
- }
126
- traverseWithCommon(commonOptions, ...steps) {
127
- const result = this.traverseOrErrorWithCommon(commonOptions, ...steps);
128
- if (result === void 0) return void 0;
129
- if (!result.ok) throw new PlError(result.error);
130
- return result.value;
131
- }
132
- traverseOrErrorWithCommon(commonOptions, ...steps) {
133
- let current = this;
134
- for (const _step of steps) {
135
- const step = typeof _step === "string" ? {
136
- ...commonOptions,
137
- field: _step
138
- } : { ...commonOptions, ..._step };
139
- const next = current.getField(_step);
140
- if (next === void 0) return void 0;
141
- if (step.pureFieldErrorToUndefined && next.value === void 0 && next.error !== void 0)
142
- return void 0;
143
- if ((!step.ignoreError || next.value === void 0) && next.error !== void 0)
144
- return {
145
- ok: false,
146
- error: `error in field ${step.field} of ${resourceIdToString(current.id)}: ${next.error.getDataAsString()}`
147
- };
148
- if (next.value === void 0) {
149
- if (step.errorIfFieldNotSet)
150
- return {
151
- ok: false,
152
- error: `field have no assigned value ${step.field} of ${resourceIdToString(current.id)}`
153
- };
154
- this.onUnstableLambda("unpopulated_field:" + step.field);
155
- return void 0;
156
- }
157
- current = next.value;
158
- }
159
- return { ok: true, value: current };
160
- }
161
- onUnstableLambda = (marker) => {
162
- this.instanceData.ctx.markUnstable(marker);
163
- };
164
- getField(_step) {
165
- this.instanceData.guard();
166
- const step = typeof _step === "string" ? { field: _step } : _step;
167
- const ve = this.resource.getField(this.instanceData.ctx.watcher, step, this.onUnstableLambda);
168
- if (ve === void 0) return void 0;
169
- return mapValueAndError(ve, (rid) => this.getResourceFromTree(rid, { ignoreError: true }));
170
- }
171
- getInputsLocked() {
172
- this.instanceData.guard();
173
- const result = this.resource.getInputsLocked(this.instanceData.ctx.watcher);
174
- if (!result) this.instanceData.ctx.markUnstable("inputs_unlocked:" + this.resourceType.name);
175
- return result;
176
- }
177
- getOutputsLocked() {
178
- this.instanceData.guard();
179
- const result = this.resource.getOutputsLocked(this.instanceData.ctx.watcher);
180
- if (!result) this.instanceData.ctx.markUnstable("outputs_unlocked:" + this.resourceType.name);
181
- return result;
182
- }
183
- getIsReadyOrError() {
184
- this.instanceData.guard();
185
- const result = this.resource.getIsReadyOrError(this.instanceData.ctx.watcher);
186
- if (!result) this.instanceData.ctx.markUnstable("not_ready:" + this.resourceType.name);
187
- return result;
188
- }
189
- getIsFinal() {
190
- this.instanceData.guard();
191
- return this.resource.getIsFinal(this.instanceData.ctx.watcher);
192
- }
193
- getError() {
194
- this.instanceData.guard();
195
- const rid = this.resource.getError(this.instanceData.ctx.watcher);
196
- if (rid === void 0)
197
- return void 0;
198
- return this.getResourceFromTree(rid, {});
199
- }
200
- getData() {
201
- return this.resource.data;
202
- }
203
- getDataAsString() {
204
- return this.resource.getDataAsString();
205
- }
206
- getDataAsJson() {
207
- return this.resource.getDataAsJson();
208
- }
209
- listInputFields() {
210
- this.instanceData.guard();
211
- this.getInputsLocked();
212
- return this.resource.listInputFields(this.instanceData.ctx.watcher);
213
- }
214
- listOutputFields() {
215
- this.instanceData.guard();
216
- this.getOutputsLocked();
217
- return this.resource.listOutputFields(this.instanceData.ctx.watcher);
218
- }
219
- listDynamicFields() {
220
- this.instanceData.guard();
221
- return this.resource.listDynamicFields(this.instanceData.ctx.watcher);
222
- }
223
- getKeyValue(key, unstableIfNotFound = false) {
224
- this.instanceData.guard();
225
- const result = this.resource.getKeyValue(this.instanceData.ctx.watcher, key);
226
- if (result === void 0 && unstableIfNotFound)
227
- this.instanceData.ctx.markUnstable("key_not_found_b:" + key);
228
- return result;
229
- }
230
- /** @deprecated */
231
- getKeyValueString(key) {
232
- return this.getKeyValueAsString(key);
233
- }
234
- getKeyValueAsString(key, unstableIfNotFound = false) {
235
- this.instanceData.guard();
236
- const result = this.resource.getKeyValueString(this.instanceData.ctx.watcher, key);
237
- if (result === void 0 && unstableIfNotFound)
238
- this.instanceData.ctx.markUnstable("key_not_found_s:" + key);
239
- return result;
240
- }
241
- getKeyValueAsJson(key, unstableIfNotFound = false) {
242
- const result = this.resource.getKeyValueString(this.instanceData.ctx.watcher, key);
243
- if (result === void 0) {
244
- if (unstableIfNotFound) this.instanceData.ctx.markUnstable("key_not_found_j:" + key);
245
- return void 0;
246
- }
247
- return JSON.parse(result);
248
- }
249
- /**
250
- * Can be used to passe a higher level accessor that will wrap the resource and throw its
251
- * errors on node resolution.
252
- * */
253
- toEntryAccessor() {
254
- return new PlTreeEntryAccessor(this.accessorData, this.tree, this.id, this.instanceData);
255
- }
256
- /** Can be passed to nested computable. */
257
- persist() {
258
- return new PlTreeEntry(this.accessorData, this.resource.id);
259
- }
260
- };
261
- var TreeStateUpdateError = class extends Error {
262
- constructor(message) {
263
- super(message);
264
- }
265
- };
266
- var PlTreeField = class {
267
- constructor(type, value, error, resourceVersion) {
268
- this.type = type;
269
- this.value = value;
270
- this.error = error;
271
- this.resourceVersion = resourceVersion;
272
- }
273
- change = new ChangeSource();
274
- };
275
- var InitialResourceVersion = 0;
276
- var decoder = new TextDecoder();
277
- var PlTreeResource = class {
278
- /** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */
279
- refCount = 0;
280
- /** Increments each time resource is checked for difference with new state */
281
- version = InitialResourceVersion;
282
- /** Set to resource version when resource state, or it's fields have changed */
283
- dataVersion = InitialResourceVersion;
284
- fields = /* @__PURE__ */ new Map();
285
- kv = /* @__PURE__ */ new Map();
286
- resourceRemoved = new ChangeSource();
287
- // following change source are removed when resource is marked as final
288
- finalChanged = new ChangeSource();
289
- resourceStateChange = new ChangeSource();
290
- lockedChange = new ChangeSource();
291
- inputAndServiceFieldListChanged = new ChangeSource();
292
- outputFieldListChanged = new ChangeSource();
293
- dynamicFieldListChanged = new ChangeSource();
294
- kvChanged = new ChangeSource();
295
- id;
296
- originalResourceId;
297
- kind;
298
- type;
299
- data;
300
- dataAsString;
301
- dataAsJson;
302
- error;
303
- inputsLocked;
304
- outputsLocked;
305
- resourceReady;
306
- finalFlag;
307
- /** Set externally by the tree, using {@link FinalPredicate} */
308
- _final = false;
309
- logger;
310
- constructor(initialState, logger) {
311
- this.id = initialState.id;
312
- this.originalResourceId = initialState.originalResourceId;
313
- this.kind = initialState.kind;
314
- this.type = initialState.type;
315
- this.data = initialState.data;
316
- this.error = initialState.error;
317
- this.inputsLocked = initialState.inputsLocked;
318
- this.outputsLocked = initialState.outputsLocked;
319
- this.resourceReady = initialState.resourceReady;
320
- this.finalFlag = initialState.final;
321
- this.logger = logger;
322
- }
323
- // TODO add logging
324
- info(msg) {
325
- if (this.logger !== void 0) this.logger.info(msg);
326
- }
327
- warn(msg) {
328
- if (this.logger !== void 0) this.logger.warn(msg);
329
- }
330
- get final() {
331
- return this._final;
332
- }
333
- getField(watcher, _step, onUnstable = () => {
334
- }) {
335
- const step = typeof _step === "string" ? { field: _step } : _step;
336
- const field = this.fields.get(step.field);
337
- if (field === void 0) {
338
- if (step.errorIfFieldNotFound || step.errorIfFieldNotSet)
339
- throw new Error(
340
- `Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`
341
- );
342
- if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
343
- else if (step.assertFieldType === "Service" || step.assertFieldType === "Input") {
344
- if (step.allowPermanentAbsence)
345
- return void 0;
346
- else throw new Error(`Service or input field not found ${step.field}.`);
347
- }
348
- if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
349
- else if (step.assertFieldType === "Output") {
350
- if (step.allowPermanentAbsence)
351
- return void 0;
352
- else throw new Error(`Output field not found ${step.field}.`);
353
- }
354
- this.dynamicFieldListChanged?.attachWatcher(watcher);
355
- if (!this._final && !step.stableIfNotFound) onUnstable("field_not_found:" + step.field);
356
- return void 0;
357
- } else {
358
- if (step.assertFieldType !== void 0 && field.type !== step.assertFieldType)
359
- throw new Error(
360
- `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`
361
- );
362
- const ret = {};
363
- if (isNotNullResourceId(field.value)) ret.value = field.value;
364
- if (isNotNullResourceId(field.error)) ret.error = field.error;
365
- if (ret.value === void 0 && ret.error === void 0)
366
- onUnstable("field_not_resolved:" + step.field);
367
- field.change.attachWatcher(watcher);
368
- return ret;
369
- }
370
- }
371
- getInputsLocked(watcher) {
372
- if (!this.inputsLocked)
373
- this.resourceStateChange?.attachWatcher(watcher);
374
- return this.inputsLocked;
375
- }
376
- getOutputsLocked(watcher) {
377
- if (!this.outputsLocked)
378
- this.resourceStateChange?.attachWatcher(watcher);
379
- return this.outputsLocked;
380
- }
381
- get isReadyOrError() {
382
- return this.error !== NullResourceId || this.resourceReady || this.originalResourceId !== NullResourceId;
383
- }
384
- getIsFinal(watcher) {
385
- this.finalChanged?.attachWatcher(watcher);
386
- return this._final;
387
- }
388
- getIsReadyOrError(watcher) {
389
- if (!this.isReadyOrError)
390
- this.resourceStateChange?.attachWatcher(watcher);
391
- return this.isReadyOrError;
392
- }
393
- getError(watcher) {
394
- if (isNullResourceId(this.error)) {
395
- this.resourceStateChange?.attachWatcher(watcher);
396
- return void 0;
397
- } else {
398
- return this.error;
399
- }
400
- }
401
- listInputFields(watcher) {
402
- const ret = [];
403
- this.fields.forEach((field, name) => {
404
- if (field.type === "Input" || field.type === "Service") ret.push(name);
405
- });
406
- if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
407
- return ret;
408
- }
409
- listOutputFields(watcher) {
410
- const ret = [];
411
- this.fields.forEach((field, name) => {
412
- if (field.type === "Output") ret.push(name);
413
- });
414
- if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
415
- return ret;
416
- }
417
- listDynamicFields(watcher) {
418
- const ret = [];
419
- this.fields.forEach((field, name) => {
420
- if (field.type !== "Input" && field.type !== "Output") ret.push(name);
421
- });
422
- this.dynamicFieldListChanged?.attachWatcher(watcher);
423
- return ret;
424
- }
425
- getKeyValue(watcher, key) {
426
- this.kvChanged?.attachWatcher(watcher);
427
- return this.kv.get(key);
428
- }
429
- getKeyValueString(watcher, key) {
430
- const bytes = this.getKeyValue(watcher, key);
431
- if (bytes === void 0) return void 0;
432
- return decoder.decode(bytes);
433
- }
434
- getDataAsString() {
435
- if (this.data === void 0) return void 0;
436
- if (this.dataAsString === void 0) this.dataAsString = decoder.decode(this.data);
437
- return this.dataAsString;
438
- }
439
- getDataAsJson() {
440
- if (this.data === void 0) return void 0;
441
- if (this.dataAsJson === void 0) this.dataAsJson = JSON.parse(this.getDataAsString());
442
- return this.dataAsJson;
443
- }
444
- verifyReadyState() {
445
- if (this.resourceReady && !this.inputsLocked)
446
- throw new Error(`ready without input or output lock: ${stringifyWithResourceId(this.state)}`);
447
- }
448
- get state() {
449
- return {
450
- id: this.id,
451
- kind: this.kind,
452
- type: this.type,
453
- data: this.data,
454
- resourceReady: this.resourceReady,
455
- inputsLocked: this.inputsLocked,
456
- outputsLocked: this.outputsLocked,
457
- error: this.error,
458
- originalResourceId: this.originalResourceId,
459
- final: this.finalFlag
460
- };
461
- }
462
- markFinal() {
463
- if (this._final) return;
464
- this._final = true;
465
- notEmpty(this.finalChanged).markChanged();
466
- this.finalChanged = void 0;
467
- this.resourceStateChange = void 0;
468
- this.dynamicFieldListChanged = void 0;
469
- this.inputAndServiceFieldListChanged = void 0;
470
- this.outputFieldListChanged = void 0;
471
- this.lockedChange = void 0;
472
- }
473
- /** Used for invalidation */
474
- markAllChanged() {
475
- this.fields.forEach((field) => field.change.markChanged());
476
- this.finalChanged?.markChanged();
477
- this.resourceStateChange?.markChanged();
478
- this.lockedChange?.markChanged();
479
- this.inputAndServiceFieldListChanged?.markChanged();
480
- this.outputFieldListChanged?.markChanged();
481
- this.dynamicFieldListChanged?.markChanged();
482
- this.kvChanged?.markChanged();
483
- this.resourceRemoved.markChanged();
484
- }
485
- };
486
- var PlTreeState = class {
487
- constructor(root, isFinalPredicate = (r) => false) {
488
- this.root = root;
489
- this.isFinalPredicate = isFinalPredicate;
490
- }
491
- /** resource heap */
492
- resources = /* @__PURE__ */ new Map();
493
- resourcesAdded = new ChangeSource();
494
- /** Resets to false if any invalid state transitions are registered,
495
- * after that tree will produce errors for any read or write operations */
496
- _isValid = true;
497
- invalidationMessage;
498
- forEachResource(cb) {
499
- this.resources.forEach((v) => cb(v));
500
- }
501
- checkValid() {
502
- if (!this._isValid) throw new Error(this.invalidationMessage ?? "tree is in invalid state");
503
- }
504
- get(watcher, rid) {
505
- this.checkValid();
506
- const res = this.resources.get(rid);
507
- if (res === void 0) {
508
- this.resourcesAdded.attachWatcher(watcher);
509
- throw new Error(`resource ${resourceIdToString(rid)} not found in the tree`);
510
- }
511
- res.resourceRemoved.attachWatcher(watcher);
512
- return res;
513
- }
514
- updateFromResourceData(resourceData, allowOrphanInputs = false) {
515
- this.checkValid();
516
- const incrementRefs = [];
517
- const decrementRefs = [];
518
- for (const rd of resourceData) {
519
- let resource = this.resources.get(rd.id);
520
- const statBeforeMutation = resource?.state;
521
- const unexpectedTransitionError = (reason) => {
522
- const { fields, ...rdWithoutFields } = rd;
523
- this.invalidateTree();
524
- throw new TreeStateUpdateError(
525
- `Unexpected resource state transition (${reason}): ${stringifyWithResourceId(
526
- rdWithoutFields
527
- )} -> ${stringifyWithResourceId(statBeforeMutation)}`
528
- );
529
- };
530
- if (resource !== void 0) {
531
- if (resource.final)
532
- unexpectedTransitionError("resource state can be updated after it is marked as final");
533
- let changed = false;
534
- resource.version += 1;
535
- if (resource.originalResourceId !== rd.originalResourceId) {
536
- if (resource.originalResourceId !== NullResourceId)
537
- unexpectedTransitionError("originalResourceId can't change after it is set");
538
- resource.originalResourceId = rd.originalResourceId;
539
- notEmpty(resource.resourceStateChange).markChanged();
540
- changed = true;
541
- }
542
- if (resource.error !== rd.error) {
543
- if (isNotNullResourceId(resource.error))
544
- unexpectedTransitionError("resource can't change attached error after it is set");
545
- resource.error = rd.error;
546
- incrementRefs.push(resource.error);
547
- notEmpty(resource.resourceStateChange).markChanged();
548
- changed = true;
549
- }
550
- for (const fd of rd.fields) {
551
- let field = resource.fields.get(fd.name);
552
- if (!field) {
553
- field = new PlTreeField(fd.type, fd.value, fd.error, resource.version);
554
- if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
555
- if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
556
- if (fd.type === "Input" || fd.type === "Service") {
557
- if (resource.inputsLocked)
558
- unexpectedTransitionError(
559
- `adding ${fd.type} (${fd.name}) field while inputs locked`
560
- );
561
- notEmpty(resource.inputAndServiceFieldListChanged).markChanged();
562
- } else if (fd.type === "Output") {
563
- if (resource.outputsLocked)
564
- unexpectedTransitionError(
565
- `adding ${fd.type} (${fd.name}) field while outputs locked`
566
- );
567
- notEmpty(resource.outputFieldListChanged).markChanged();
568
- } else {
569
- notEmpty(resource.dynamicFieldListChanged).markChanged();
570
- }
571
- resource.fields.set(fd.name, field);
572
- changed = true;
573
- } else {
574
- if (field.type !== fd.type) {
575
- if (field.type !== "Dynamic")
576
- unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);
577
- notEmpty(resource.dynamicFieldListChanged).markChanged();
578
- if (field.type === "Input" || field.type === "Service") {
579
- if (resource.inputsLocked)
580
- unexpectedTransitionError(
581
- `adding input field "${fd.name}", while corresponding list is locked`
582
- );
583
- notEmpty(resource.inputAndServiceFieldListChanged).markChanged();
584
- }
585
- if (field.type === "Output") {
586
- if (resource.outputsLocked)
587
- unexpectedTransitionError(
588
- `adding output field "${fd.name}", while corresponding list is locked`
589
- );
590
- notEmpty(resource.outputFieldListChanged).markChanged();
591
- }
592
- field.type = fd.type;
593
- field.change.markChanged();
594
- changed = true;
595
- }
596
- if (field.value !== fd.value) {
597
- if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);
598
- field.value = fd.value;
599
- if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
600
- field.change.markChanged();
601
- changed = true;
602
- }
603
- if (field.error !== fd.error) {
604
- if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);
605
- field.error = fd.error;
606
- if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
607
- field.change.markChanged();
608
- changed = true;
609
- }
610
- field.resourceVersion = resource.version;
611
- }
612
- }
613
- resource.fields.forEach((field, fieldName, fields) => {
614
- if (field.resourceVersion !== resource.version) {
615
- if (field.type === "Input" || field.type === "Service" || field.type === "Output")
616
- unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);
617
- field.change.markChanged();
618
- fields.delete(fieldName);
619
- if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);
620
- if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);
621
- notEmpty(resource.dynamicFieldListChanged).markChanged();
622
- }
623
- });
624
- if (resource.inputsLocked !== rd.inputsLocked) {
625
- if (resource.inputsLocked) unexpectedTransitionError("inputs unlocking is not permitted");
626
- resource.inputsLocked = rd.inputsLocked;
627
- notEmpty(resource.lockedChange).markChanged();
628
- changed = true;
629
- }
630
- if (resource.outputsLocked !== rd.outputsLocked) {
631
- if (resource.outputsLocked)
632
- unexpectedTransitionError("outputs unlocking is not permitted");
633
- resource.outputsLocked = rd.outputsLocked;
634
- notEmpty(resource.lockedChange).markChanged();
635
- changed = true;
636
- }
637
- if (resource.resourceReady !== rd.resourceReady) {
638
- const readyStateBefore = resource.resourceReady;
639
- resource.resourceReady = rd.resourceReady;
640
- resource.verifyReadyState();
641
- if (!resource.isReadyOrError)
642
- unexpectedTransitionError(
643
- `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`
644
- );
645
- notEmpty(resource.resourceStateChange).markChanged();
646
- changed = true;
647
- }
648
- let kvChanged = false;
649
- for (const kv of rd.kv) {
650
- const current = resource.kv.get(kv.key);
651
- if (current === void 0) {
652
- resource.kv.set(kv.key, kv.value);
653
- kvChanged = true;
654
- } else if (Buffer.compare(current, kv.value) !== 0) {
655
- resource.kv.set(kv.key, kv.value);
656
- kvChanged = true;
657
- }
658
- }
659
- if (resource.kv.size > rd.kv.length) {
660
- const newStateKeys = new Set(rd.kv.map((kv) => kv.key));
661
- resource.kv.forEach((value, key, map) => {
662
- if (!newStateKeys.has(key)) map.delete(key);
663
- });
664
- kvChanged = true;
665
- }
666
- if (kvChanged) notEmpty(resource.kvChanged).markChanged();
667
- if (changed) {
668
- resource.dataVersion = resource.version;
669
- if (this.isFinalPredicate(resource)) resource.markFinal();
670
- }
671
- } else {
672
- resource = new PlTreeResource(rd);
673
- resource.verifyReadyState();
674
- if (isNotNullResourceId(resource.error)) incrementRefs.push(resource.error);
675
- for (const fd of rd.fields) {
676
- const field = new PlTreeField(fd.type, fd.value, fd.error, InitialResourceVersion);
677
- if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
678
- if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
679
- resource.fields.set(fd.name, field);
680
- }
681
- for (const kv of rd.kv) resource.kv.set(kv.key, kv.value);
682
- this.resources.set(resource.id, resource);
683
- this.resourcesAdded.markChanged();
684
- }
685
- }
686
- for (const rid of incrementRefs) {
687
- const res = this.resources.get(rid);
688
- if (!res) {
689
- this.invalidateTree();
690
- throw new TreeStateUpdateError(`orphan resource ${rid}`);
691
- }
692
- res.refCount++;
693
- }
694
- let currentRefs = decrementRefs;
695
- while (currentRefs.length > 0) {
696
- const nextRefs = [];
697
- for (const rid of currentRefs) {
698
- const res = this.resources.get(rid);
699
- if (!res) {
700
- this.invalidateTree();
701
- throw new TreeStateUpdateError(`orphan resource ${rid}`);
702
- }
703
- res.refCount--;
704
- if (res.refCount === 0 && res.id !== this.root) {
705
- res.fields.forEach((field) => {
706
- if (isNotNullResourceId(field.value)) nextRefs.push(field.value);
707
- if (isNotNullResourceId(field.error)) nextRefs.push(field.error);
708
- field.change.markChanged();
709
- });
710
- if (isNotNullResourceId(res.error)) nextRefs.push(res.error);
711
- res.resourceRemoved.markChanged();
712
- this.resources.delete(rid);
713
- }
714
- }
715
- currentRefs = nextRefs;
716
- }
717
- if (!allowOrphanInputs) {
718
- for (const rd of resourceData) {
719
- if (!this.resources.has(rd.id)) {
720
- this.invalidateTree();
721
- throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);
722
- }
723
- }
724
- }
725
- }
726
- /** @deprecated use "entry" instead */
727
- accessor(rid = this.root) {
728
- this.checkValid();
729
- return this.entry(rid);
730
- }
731
- entry(rid = this.root) {
732
- this.checkValid();
733
- return new PlTreeEntry({ treeProvider: () => this }, rid);
734
- }
735
- invalidateTree(msg) {
736
- this._isValid = false;
737
- this.invalidationMessage = msg;
738
- this.resources.forEach((res) => {
739
- res.markAllChanged();
740
- });
741
- }
742
- };
743
- function constructTreeLoadingRequest(tree, pruningFunction) {
744
- const seedResources = [];
745
- const finalResources = /* @__PURE__ */ new Set();
746
- tree.forEachResource((res) => {
747
- if (res.final) finalResources.add(res.id);
748
- else seedResources.push(res.id);
749
- });
750
- if (seedResources.length === 0 && finalResources.size === 0) seedResources.push(tree.root);
751
- return { seedResources, finalResources, pruningFunction };
752
- }
753
- async function loadTreeState(tx, loadingRequest) {
754
- const { seedResources, finalResources, pruningFunction } = loadingRequest;
755
- const pending = new Denque();
756
- const requested = /* @__PURE__ */ new Set();
757
- const requestState = (rid) => {
758
- if (isNullResourceId(rid) || requested.has(rid) || finalResources.has(rid)) return;
759
- requested.add(rid);
760
- const resourceData = tx.getResourceDataIfExists(rid, true);
761
- const kvData = tx.listKeyValuesIfResourceExists(rid);
762
- pending.push(
763
- (async () => {
764
- const resource = await resourceData;
765
- const kv = await kvData;
766
- if (resource === void 0) return void 0;
767
- if (kv === void 0) throw new Error("Inconsistent replies");
768
- return { ...resource, kv };
769
- })()
770
- );
771
- };
772
- seedResources.forEach((rid) => requestState(rid));
773
- const result = [];
774
- while (true) {
775
- const nextResourcePromise = pending.shift();
776
- if (nextResourcePromise === void 0)
777
- break;
778
- let nextResource = await nextResourcePromise;
779
- if (nextResource === void 0)
780
- continue;
781
- if (pruningFunction !== void 0)
782
- nextResource = { ...nextResource, fields: pruningFunction(nextResource) };
783
- requestState(nextResource.error);
784
- for (const field of nextResource.fields) {
785
- requestState(field.value);
786
- requestState(field.error);
787
- }
788
- result.push(nextResource);
789
- }
790
- return result;
791
- }
792
- function rsSchema(schema) {
793
- return schema;
794
- }
795
- function makeResourceSnapshot(res, schema, ctx) {
796
- const node = res instanceof PlTreeEntry ? notEmpty(ctx).accessor(res).node() : res instanceof PlTreeEntryAccessor ? res.node() : res;
797
- const info = node.resourceInfo;
798
- const result = { ...info };
799
- if (schema.data !== void 0) {
800
- if (schema.data === "raw") result.data = node.getData();
801
- else result.data = schema.data.parse(node.getDataAsJson());
802
- }
803
- if (schema.fields !== void 0) {
804
- const fields = {};
805
- for (const [fieldName, required] of Object.entries(schema.fields))
806
- fields[fieldName] = node.traverse({
807
- field: fieldName,
808
- errorIfFieldNotSet: required,
809
- stableIfNotFound: !required
810
- })?.id;
811
- result.fields = fields;
812
- }
813
- if (schema.kv !== void 0) {
814
- const kv = {};
815
- for (const [fieldName, type] of Object.entries(schema.kv)) {
816
- const value = node.getKeyValue(fieldName);
817
- if (value === void 0) {
818
- throw new Error(`Key not found ${fieldName}`);
819
- } else if (type === "raw") {
820
- kv[fieldName] = value;
821
- } else {
822
- kv[fieldName] = type.parse(JSON.parse(Buffer.from(value).toString("utf-8")));
823
- }
824
- }
825
- result.kv = kv;
826
- }
827
- return result;
828
- }
829
- function treeEntryToResourceWithData(res, fields, ctx) {
830
- if (res instanceof PlTreeEntry) {
831
- const node = ctx.accessor(res).node();
832
- const info = node.resourceInfo;
833
- const fValues = fields.map((name) => [
834
- name,
835
- node.getField(name)?.value?.id
836
- ]);
837
- return {
838
- ...info,
839
- fields: new Map(fValues),
840
- data: node.getData() ?? new Uint8Array()
841
- };
842
- }
843
- return res;
844
- }
845
- function treeEntryToResourceWithMetadata(res, mdKeys, ctx) {
846
- if (!(res instanceof PlTreeEntry)) return res;
847
- const node = ctx.accessor(res).node();
848
- const info = node.resourceInfo;
849
- const mdEntries = mdKeys.map((k) => [k, node.getKeyValue(k)]);
850
- return {
851
- ...info,
852
- metadata: Object.fromEntries(mdEntries)
853
- };
854
- }
855
- var SynchronizedTreeState = class _SynchronizedTreeState {
856
- constructor(pl, root, ops, logger) {
857
- this.pl = pl;
858
- this.root = root;
859
- this.logger = logger;
860
- const { finalPredicate, pruning, pollingInterval, stopPollingDelay } = ops;
861
- this.pruning = pruning;
862
- this.pollingInterval = pollingInterval;
863
- this.finalPredicate = finalPredicate;
864
- this.state = new PlTreeState(root, finalPredicate);
865
- this.hooks = new PollingComputableHooks(
866
- () => this.startUpdating(),
867
- () => this.stopUpdating(),
868
- { stopDebounce: stopPollingDelay },
869
- (resolve, reject) => this.scheduleOnNextState(resolve, reject)
870
- );
871
- }
872
- finalPredicate;
873
- state;
874
- pollingInterval;
875
- pruning;
876
- hooks;
877
- abortController = new AbortController();
878
- /** @deprecated use "entry" instead */
879
- accessor(rid = this.root) {
880
- if (this.terminated) throw new Error("tree synchronization is terminated");
881
- return this.entry(rid);
882
- }
883
- entry(rid = this.root) {
884
- if (this.terminated) throw new Error("tree synchronization is terminated");
885
- return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);
886
- }
887
- /** Can be used to externally kick off the synchronization polling loop, and
888
- * await for the first synchronization to happen. */
889
- async refreshState() {
890
- if (this.terminated) throw new Error("tree synchronization is terminated");
891
- await this.hooks.refreshState();
892
- }
893
- scheduledOnNextState = [];
894
- scheduleOnNextState(resolve, reject) {
895
- if (this.terminated) reject(new Error("tree synchronization is terminated"));
896
- else this.scheduledOnNextState.push({ resolve, reject });
897
- }
898
- /** Called from observer */
899
- startUpdating() {
900
- if (this.terminated) return;
901
- this.keepRunning = true;
902
- if (this.currentLoop === void 0) this.currentLoop = this.mainLoop();
903
- }
904
- /** Called from observer */
905
- stopUpdating() {
906
- this.keepRunning = false;
907
- }
908
- /** If true, main loop will continue polling pl state. */
909
- keepRunning = false;
910
- /** Actual state of main loop. */
911
- currentLoop = void 0;
912
- /** Executed from the main loop, and initialization procedure. */
913
- async refresh() {
914
- if (this.terminated) throw new Error("tree synchronization is terminated");
915
- const request = constructTreeLoadingRequest(this.state, this.pruning);
916
- const data = await this.pl.withReadTx("ReadingTree", async (tx) => {
917
- return await loadTreeState(tx, request);
918
- });
919
- this.state.updateFromResourceData(data, true);
920
- }
921
- /** If true this tree state is permanently terminaed. */
922
- terminated = false;
923
- async mainLoop() {
924
- while (true) {
925
- if (!this.keepRunning) break;
926
- let toNotify = void 0;
927
- if (this.scheduledOnNextState.length > 0) {
928
- toNotify = this.scheduledOnNextState;
929
- this.scheduledOnNextState = [];
930
- }
931
- try {
932
- await this.refresh();
933
- if (toNotify !== void 0) for (const n of toNotify) n.resolve();
934
- } catch (e) {
935
- if (toNotify !== void 0) for (const n of toNotify) n.reject(e);
936
- if (e instanceof TreeStateUpdateError) {
937
- this.logger?.error(e);
938
- this.state.invalidateTree("stat update error");
939
- this.state = new PlTreeState(this.root, this.finalPredicate);
940
- continue;
941
- } else this.logger?.warn(e);
942
- }
943
- if (!this.keepRunning || this.terminated) break;
944
- try {
945
- await tp.setTimeout(this.pollingInterval, this.abortController.signal);
946
- } catch (e) {
947
- if (!isTimeoutOrCancelError(e)) throw new Error("Unexpected error", { cause: e });
948
- break;
949
- }
950
- }
951
- this.currentLoop = void 0;
952
- }
953
- /**
954
- * Terminates the internal loop, and permanently destoys all internal state, so
955
- * all computables using this state will resolve to errors.
956
- * */
957
- async terminate() {
958
- this.keepRunning = false;
959
- this.terminated = true;
960
- this.abortController.abort();
961
- if (this.currentLoop === void 0) return;
962
- await this.currentLoop;
963
- this.state.invalidateTree("synchronization terminated for the tree");
964
- }
965
- /** @deprecated */
966
- async awaitSyncLoopTermination() {
967
- if (this.currentLoop === void 0) return;
968
- await this.currentLoop;
969
- }
970
- static async init(pl, root, ops) {
971
- const tree = new _SynchronizedTreeState(pl, root, ops);
972
- await tree.refresh();
973
- return tree;
974
- }
975
- };
976
-
977
- export { PlError, PlTreeEntry, PlTreeEntryAccessor, PlTreeNodeAccessor, PlTreeResource, PlTreeState, SynchronizedTreeState, TreeStateUpdateError, constructTreeLoadingRequest, isPlTreeEntry, isPlTreeEntryAccessor, isPlTreeNodeAccessor, loadTreeState, makeResourceSnapshot, mapValueAndError, mapValueAndErrorIfDefined, rsSchema, treeEntryToResourceInfo, treeEntryToResourceWithData, treeEntryToResourceWithMetadata };
1
+ "use strict";var b=Object.defineProperty;var P=(o,e,t)=>e in o?b(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var u=(o,e,t)=>P(o,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("@milaboratories/pl-client"),v=require("@milaboratories/computable"),p=require("@milaboratories/ts-helpers"),$=require("denque"),V=require("node:timers/promises");function W(o){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const t in o)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(o,t);Object.defineProperty(e,t,r.get?r:{enumerable:!0,get:()=>o[t]})}}return e.default=o,Object.freeze(e)}const U=W(V);function K(o,e){if(o!==void 0)return S(o,e)}function S(o,e){const t={};return o.value!==void 0&&(t.value=e(o.value)),o.error!==void 0&&(t.error=e(o.error)),t}class T extends Error{constructor(e){super(e)}}function q(o){return typeof o=="object"&&o!==null&&o.__pl_tree_type_marker__==="PlTreeEntry"}function J(o){return typeof o=="object"&&o!==null&&o.__pl_tree_type_marker__==="PlTreeEntryAccessor"}function M(o){return typeof o=="object"&&o!==null&&o.__pl_tree_type_marker__==="PlTreeNodeAccessor"}class k{constructor(e,t){u(this,"__pl_tree_type_marker__","PlTreeEntry");this.accessorData=e,this.rid=t}createAccessor(e,t){return new R(this.accessorData,this.accessorData.treeProvider(),this.rid,{ctx:e,guard:t})}toJSON(){return this.toString()}toString(){return`[ENTRY:${l.resourceIdToString(this.rid)}]`}}function F(o,e,t,r,i){const c=new _(o,e,e.get(t.ctx.watcher,r),t);if(!i.ignoreError){const n=c.getError();if(n!==void 0)throw new T(`error encountered on resource ${l.resourceIdToString(c.id)} (${l.resourceTypeToString(c.resourceType)}): ${n.getDataAsString()}`)}if(i.assertResourceType!==void 0&&(Array.isArray(i.assertResourceType)?i.assertResourceType.findIndex(n=>l.resourceTypesEqual(n,c.resourceType))===-1:!l.resourceTypesEqual(i.assertResourceType,c.resourceType)))throw new Error(`wrong resource type ${l.resourceTypeToString(c.resourceType)} but expected ${i.assertResourceType}`);return c}class R{constructor(e,t,r,i){u(this,"__pl_tree_type_marker__","PlTreeEntryAccessor");this.accessorData=e,this.tree=t,this.rid=r,this.instanceData=i}node(e={}){return this.instanceData.guard(),this.accessorData.hooks!==void 0&&this.instanceData.ctx.attacheHooks(this.accessorData.hooks),F(this.accessorData,this.tree,this.instanceData,this.rid,e)}}function z(o,e){return o instanceof k?e.accessor(o).node().resourceInfo:o}class _{constructor(e,t,r,i){u(this,"__pl_tree_type_marker__","PlTreeNodeAccessor");u(this,"onUnstableLambda",e=>{this.instanceData.ctx.markUnstable(e)});this.accessorData=e,this.tree=t,this.resource=r,this.instanceData=i}get id(){return this.instanceData.guard(),this.resource.id}get originalId(){return this.instanceData.guard(),this.resource.originalResourceId}get resourceType(){return this.instanceData.guard(),this.resource.type}get resourceInfo(){return{id:this.id,type:this.resourceType}}getResourceFromTree(e,t){return F(this.accessorData,this.tree,this.instanceData,e,t)}traverse(...e){return this.traverseWithCommon({},...e)}traverseOrError(...e){return this.traverseOrErrorWithCommon({},...e)}traverseWithCommon(e,...t){const r=this.traverseOrErrorWithCommon(e,...t);if(r!==void 0){if(!r.ok)throw new T(r.error);return r.value}}traverseOrErrorWithCommon(e,...t){let r=this;for(const i of t){const c=typeof i=="string"?{...e,field:i}:{...e,...i},n=r.getField(i);if(n===void 0||c.pureFieldErrorToUndefined&&n.value===void 0&&n.error!==void 0)return;if((!c.ignoreError||n.value===void 0)&&n.error!==void 0)return{ok:!1,error:`error in field ${c.field} of ${l.resourceIdToString(r.id)}: ${n.error.getDataAsString()}`};if(n.value===void 0){if(c.errorIfFieldNotSet)return{ok:!1,error:`field have no assigned value ${c.field} of ${l.resourceIdToString(r.id)}`};this.onUnstableLambda("unpopulated_field:"+c.field);return}r=n.value}return{ok:!0,value:r}}getField(e){this.instanceData.guard();const t=typeof e=="string"?{field:e}:e,r=this.resource.getField(this.instanceData.ctx.watcher,t,this.onUnstableLambda);if(r!==void 0)return S(r,i=>this.getResourceFromTree(i,{ignoreError:!0}))}getInputsLocked(){this.instanceData.guard();const e=this.resource.getInputsLocked(this.instanceData.ctx.watcher);return e||this.instanceData.ctx.markUnstable("inputs_unlocked:"+this.resourceType.name),e}getOutputsLocked(){this.instanceData.guard();const e=this.resource.getOutputsLocked(this.instanceData.ctx.watcher);return e||this.instanceData.ctx.markUnstable("outputs_unlocked:"+this.resourceType.name),e}getIsReadyOrError(){this.instanceData.guard();const e=this.resource.getIsReadyOrError(this.instanceData.ctx.watcher);return e||this.instanceData.ctx.markUnstable("not_ready:"+this.resourceType.name),e}getIsFinal(){return this.instanceData.guard(),this.resource.getIsFinal(this.instanceData.ctx.watcher)}getError(){this.instanceData.guard();const e=this.resource.getError(this.instanceData.ctx.watcher);if(e!==void 0)return this.getResourceFromTree(e,{})}getData(){return this.resource.data}getDataAsString(){return this.resource.getDataAsString()}getDataAsJson(){return this.resource.getDataAsJson()}listInputFields(){return this.instanceData.guard(),this.getInputsLocked(),this.resource.listInputFields(this.instanceData.ctx.watcher)}listOutputFields(){return this.instanceData.guard(),this.getOutputsLocked(),this.resource.listOutputFields(this.instanceData.ctx.watcher)}listDynamicFields(){return this.instanceData.guard(),this.resource.listDynamicFields(this.instanceData.ctx.watcher)}getKeyValue(e,t=!1){this.instanceData.guard();const r=this.resource.getKeyValue(this.instanceData.ctx.watcher,e);return r===void 0&&t&&this.instanceData.ctx.markUnstable("key_not_found_b:"+e),r}getKeyValueString(e){return this.getKeyValueAsString(e)}getKeyValueAsString(e,t=!1){this.instanceData.guard();const r=this.resource.getKeyValueString(this.instanceData.ctx.watcher,e);return r===void 0&&t&&this.instanceData.ctx.markUnstable("key_not_found_s:"+e),r}getKeyValueAsJson(e,t=!1){const r=this.resource.getKeyValueString(this.instanceData.ctx.watcher,e);if(r===void 0){t&&this.instanceData.ctx.markUnstable("key_not_found_j:"+e);return}return JSON.parse(r)}toEntryAccessor(){return new R(this.accessorData,this.tree,this.id,this.instanceData)}persist(){return new k(this.accessorData,this.resource.id)}}class m extends Error{constructor(e){super(e)}}class D{constructor(e,t,r,i){u(this,"change",new v.ChangeSource);this.type=e,this.value=t,this.error=r,this.resourceVersion=i}}const C=0,L=new TextDecoder;class N{constructor(e,t){u(this,"refCount",0);u(this,"version",C);u(this,"dataVersion",C);u(this,"fields",new Map);u(this,"kv",new Map);u(this,"resourceRemoved",new v.ChangeSource);u(this,"finalChanged",new v.ChangeSource);u(this,"resourceStateChange",new v.ChangeSource);u(this,"lockedChange",new v.ChangeSource);u(this,"inputAndServiceFieldListChanged",new v.ChangeSource);u(this,"outputFieldListChanged",new v.ChangeSource);u(this,"dynamicFieldListChanged",new v.ChangeSource);u(this,"kvChanged",new v.ChangeSource);u(this,"id");u(this,"originalResourceId");u(this,"kind");u(this,"type");u(this,"data");u(this,"dataAsString");u(this,"dataAsJson");u(this,"error");u(this,"inputsLocked");u(this,"outputsLocked");u(this,"resourceReady");u(this,"finalFlag");u(this,"_final",!1);u(this,"logger");this.id=e.id,this.originalResourceId=e.originalResourceId,this.kind=e.kind,this.type=e.type,this.data=e.data,this.error=e.error,this.inputsLocked=e.inputsLocked,this.outputsLocked=e.outputsLocked,this.resourceReady=e.resourceReady,this.finalFlag=e.final,this.logger=t}info(e){this.logger!==void 0&&this.logger.info(e)}warn(e){this.logger!==void 0&&this.logger.warn(e)}get final(){return this._final}getField(e,t,r=()=>{}){var n,s,g;const i=typeof t=="string"?{field:t}:t,c=this.fields.get(i.field);if(c===void 0){if(i.errorIfFieldNotFound||i.errorIfFieldNotSet)throw new Error(`Field "${i.field}" not found in resource ${l.resourceIdToString(this.id)}`);if(!this.inputsLocked)(n=this.inputAndServiceFieldListChanged)==null||n.attachWatcher(e);else if(i.assertFieldType==="Service"||i.assertFieldType==="Input"){if(i.allowPermanentAbsence)return;throw new Error(`Service or input field not found ${i.field}.`)}if(!this.outputsLocked)(s=this.outputFieldListChanged)==null||s.attachWatcher(e);else if(i.assertFieldType==="Output"){if(i.allowPermanentAbsence)return;throw new Error(`Output field not found ${i.field}.`)}(g=this.dynamicFieldListChanged)==null||g.attachWatcher(e),!this._final&&!i.stableIfNotFound&&r("field_not_found:"+i.field);return}else{if(i.assertFieldType!==void 0&&c.type!==i.assertFieldType)throw new Error(`Unexpected field type: expected ${i.assertFieldType} but got ${c.type} for the field name ${i.field}`);const d={};return l.isNotNullResourceId(c.value)&&(d.value=c.value),l.isNotNullResourceId(c.error)&&(d.error=c.error),d.value===void 0&&d.error===void 0&&r("field_not_resolved:"+i.field),c.change.attachWatcher(e),d}}getInputsLocked(e){var t;return this.inputsLocked||(t=this.resourceStateChange)==null||t.attachWatcher(e),this.inputsLocked}getOutputsLocked(e){var t;return this.outputsLocked||(t=this.resourceStateChange)==null||t.attachWatcher(e),this.outputsLocked}get isReadyOrError(){return this.error!==l.NullResourceId||this.resourceReady||this.originalResourceId!==l.NullResourceId}getIsFinal(e){var t;return(t=this.finalChanged)==null||t.attachWatcher(e),this._final}getIsReadyOrError(e){var t;return this.isReadyOrError||(t=this.resourceStateChange)==null||t.attachWatcher(e),this.isReadyOrError}getError(e){var t;if(l.isNullResourceId(this.error)){(t=this.resourceStateChange)==null||t.attachWatcher(e);return}else return this.error}listInputFields(e){var r;const t=[];return this.fields.forEach((i,c)=>{(i.type==="Input"||i.type==="Service")&&t.push(c)}),this.inputsLocked||(r=this.inputAndServiceFieldListChanged)==null||r.attachWatcher(e),t}listOutputFields(e){var r;const t=[];return this.fields.forEach((i,c)=>{i.type==="Output"&&t.push(c)}),this.outputsLocked||(r=this.outputFieldListChanged)==null||r.attachWatcher(e),t}listDynamicFields(e){var r;const t=[];return this.fields.forEach((i,c)=>{i.type!=="Input"&&i.type!=="Output"&&t.push(c)}),(r=this.dynamicFieldListChanged)==null||r.attachWatcher(e),t}getKeyValue(e,t){var r;return(r=this.kvChanged)==null||r.attachWatcher(e),this.kv.get(t)}getKeyValueString(e,t){const r=this.getKeyValue(e,t);if(r!==void 0)return L.decode(r)}getDataAsString(){if(this.data!==void 0)return this.dataAsString===void 0&&(this.dataAsString=L.decode(this.data)),this.dataAsString}getDataAsJson(){if(this.data!==void 0)return this.dataAsJson===void 0&&(this.dataAsJson=JSON.parse(this.getDataAsString())),this.dataAsJson}verifyReadyState(){if(this.resourceReady&&!this.inputsLocked)throw new Error(`ready without input or output lock: ${l.stringifyWithResourceId(this.state)}`)}get state(){return{id:this.id,kind:this.kind,type:this.type,data:this.data,resourceReady:this.resourceReady,inputsLocked:this.inputsLocked,outputsLocked:this.outputsLocked,error:this.error,originalResourceId:this.originalResourceId,final:this.finalFlag}}markFinal(){this._final||(this._final=!0,p.notEmpty(this.finalChanged).markChanged(),this.finalChanged=void 0,this.resourceStateChange=void 0,this.dynamicFieldListChanged=void 0,this.inputAndServiceFieldListChanged=void 0,this.outputFieldListChanged=void 0,this.lockedChange=void 0)}markAllChanged(){var e,t,r,i,c,n,s;this.fields.forEach(g=>g.change.markChanged()),(e=this.finalChanged)==null||e.markChanged(),(t=this.resourceStateChange)==null||t.markChanged(),(r=this.lockedChange)==null||r.markChanged(),(i=this.inputAndServiceFieldListChanged)==null||i.markChanged(),(c=this.outputFieldListChanged)==null||c.markChanged(),(n=this.dynamicFieldListChanged)==null||n.markChanged(),(s=this.kvChanged)==null||s.markChanged(),this.resourceRemoved.markChanged()}}class E{constructor(e,t=r=>!1){u(this,"resources",new Map);u(this,"resourcesAdded",new v.ChangeSource);u(this,"_isValid",!0);u(this,"invalidationMessage");this.root=e,this.isFinalPredicate=t}forEachResource(e){this.resources.forEach(t=>e(t))}checkValid(){if(!this._isValid)throw new Error(this.invalidationMessage??"tree is in invalid state")}get(e,t){this.checkValid();const r=this.resources.get(t);if(r===void 0)throw this.resourcesAdded.attachWatcher(e),new Error(`resource ${l.resourceIdToString(t)} not found in the tree`);return r.resourceRemoved.attachWatcher(e),r}updateFromResourceData(e,t=!1){this.checkValid();const r=[],i=[];for(const n of e){let s=this.resources.get(n.id);const g=s==null?void 0:s.state,d=h=>{const{fields:y,...a}=n;throw this.invalidateTree(),new m(`Unexpected resource state transition (${h}): ${l.stringifyWithResourceId(a)} -> ${l.stringifyWithResourceId(g)}`)};if(s!==void 0){s.final&&d("resource state can be updated after it is marked as final");let h=!1;s.version+=1,s.originalResourceId!==n.originalResourceId&&(s.originalResourceId!==l.NullResourceId&&d("originalResourceId can't change after it is set"),s.originalResourceId=n.originalResourceId,p.notEmpty(s.resourceStateChange).markChanged(),h=!0),s.error!==n.error&&(l.isNotNullResourceId(s.error)&&d("resource can't change attached error after it is set"),s.error=n.error,r.push(s.error),p.notEmpty(s.resourceStateChange).markChanged(),h=!0);for(const a of n.fields){let f=s.fields.get(a.name);f?(f.type!==a.type&&(f.type!=="Dynamic"&&d(`field changed type ${f.type} -> ${a.type}`),p.notEmpty(s.dynamicFieldListChanged).markChanged(),(f.type==="Input"||f.type==="Service")&&(s.inputsLocked&&d(`adding input field "${a.name}", while corresponding list is locked`),p.notEmpty(s.inputAndServiceFieldListChanged).markChanged()),f.type==="Output"&&(s.outputsLocked&&d(`adding output field "${a.name}", while corresponding list is locked`),p.notEmpty(s.outputFieldListChanged).markChanged()),f.type=a.type,f.change.markChanged(),h=!0),f.value!==a.value&&(l.isNotNullResourceId(f.value)&&i.push(f.value),f.value=a.value,l.isNotNullResourceId(a.value)&&r.push(a.value),f.change.markChanged(),h=!0),f.error!==a.error&&(l.isNotNullResourceId(f.error)&&i.push(f.error),f.error=a.error,l.isNotNullResourceId(a.error)&&r.push(a.error),f.change.markChanged(),h=!0),f.resourceVersion=s.version):(f=new D(a.type,a.value,a.error,s.version),l.isNotNullResourceId(a.value)&&r.push(a.value),l.isNotNullResourceId(a.error)&&r.push(a.error),a.type==="Input"||a.type==="Service"?(s.inputsLocked&&d(`adding ${a.type} (${a.name}) field while inputs locked`),p.notEmpty(s.inputAndServiceFieldListChanged).markChanged()):a.type==="Output"?(s.outputsLocked&&d(`adding ${a.type} (${a.name}) field while outputs locked`),p.notEmpty(s.outputFieldListChanged).markChanged()):p.notEmpty(s.dynamicFieldListChanged).markChanged(),s.fields.set(a.name,f),h=!0)}if(s.fields.forEach((a,f,w)=>{a.resourceVersion!==s.version&&((a.type==="Input"||a.type==="Service"||a.type==="Output")&&d(`removal of ${a.type} field ${f}`),a.change.markChanged(),w.delete(f),l.isNotNullResourceId(a.value)&&i.push(a.value),l.isNotNullResourceId(a.error)&&i.push(a.error),p.notEmpty(s.dynamicFieldListChanged).markChanged())}),s.inputsLocked!==n.inputsLocked&&(s.inputsLocked&&d("inputs unlocking is not permitted"),s.inputsLocked=n.inputsLocked,p.notEmpty(s.lockedChange).markChanged(),h=!0),s.outputsLocked!==n.outputsLocked&&(s.outputsLocked&&d("outputs unlocking is not permitted"),s.outputsLocked=n.outputsLocked,p.notEmpty(s.lockedChange).markChanged(),h=!0),s.resourceReady!==n.resourceReady){const a=s.resourceReady;s.resourceReady=n.resourceReady,s.verifyReadyState(),s.isReadyOrError||d(`resource can't lose it's ready or error state (ready state before ${a})`),p.notEmpty(s.resourceStateChange).markChanged(),h=!0}let y=!1;for(const a of n.kv){const f=s.kv.get(a.key);(f===void 0||Buffer.compare(f,a.value)!==0)&&(s.kv.set(a.key,a.value),y=!0)}if(s.kv.size>n.kv.length){const a=new Set(n.kv.map(f=>f.key));s.kv.forEach((f,w,x)=>{a.has(w)||x.delete(w)}),y=!0}y&&p.notEmpty(s.kvChanged).markChanged(),h&&(s.dataVersion=s.version,this.isFinalPredicate(s)&&s.markFinal())}else{s=new N(n),s.verifyReadyState(),l.isNotNullResourceId(s.error)&&r.push(s.error);for(const h of n.fields){const y=new D(h.type,h.value,h.error,C);l.isNotNullResourceId(h.value)&&r.push(h.value),l.isNotNullResourceId(h.error)&&r.push(h.error),s.fields.set(h.name,y)}for(const h of n.kv)s.kv.set(h.key,h.value);this.resources.set(s.id,s),this.resourcesAdded.markChanged()}}for(const n of r){const s=this.resources.get(n);if(!s)throw this.invalidateTree(),new m(`orphan resource ${n}`);s.refCount++}let c=i;for(;c.length>0;){const n=[];for(const s of c){const g=this.resources.get(s);if(!g)throw this.invalidateTree(),new m(`orphan resource ${s}`);g.refCount--,g.refCount===0&&g.id!==this.root&&(g.fields.forEach(d=>{l.isNotNullResourceId(d.value)&&n.push(d.value),l.isNotNullResourceId(d.error)&&n.push(d.error),d.change.markChanged()}),l.isNotNullResourceId(g.error)&&n.push(g.error),g.resourceRemoved.markChanged(),this.resources.delete(s))}c=n}if(!t){for(const n of e)if(!this.resources.has(n.id))throw this.invalidateTree(),new m(`orphan input resource ${n.id}`)}}accessor(e=this.root){return this.checkValid(),this.entry(e)}entry(e=this.root){return this.checkValid(),new k({treeProvider:()=>this},e)}invalidateTree(e){this._isValid=!1,this.invalidationMessage=e,this.resources.forEach(t=>{t.markAllChanged()})}}function A(o,e){const t=[],r=new Set;return o.forEachResource(i=>{i.final?r.add(i.id):t.push(i.id)}),t.length===0&&r.size===0&&t.push(o.root),{seedResources:t,finalResources:r,pruningFunction:e}}async function O(o,e){const{seedResources:t,finalResources:r,pruningFunction:i}=e,c=new $,n=new Set,s=d=>{if(l.isNullResourceId(d)||n.has(d)||r.has(d))return;n.add(d);const h=o.getResourceDataIfExists(d,!0),y=o.listKeyValuesIfResourceExists(d);c.push((async()=>{const a=await h,f=await y;if(a!==void 0){if(f===void 0)throw new Error("Inconsistent replies");return{...a,kv:f}}})())};t.forEach(d=>s(d));const g=[];for(;;){const d=c.shift();if(d===void 0)break;let h=await d;if(h!==void 0){i!==void 0&&(h={...h,fields:i(h)}),s(h.error);for(const y of h.fields)s(y.value),s(y.error);g.push(h)}}return g}function j(o){return o}function B(o,e,t){var n;const r=o instanceof k?p.notEmpty(t).accessor(o).node():o instanceof R?o.node():o,c={...r.resourceInfo};if(e.data!==void 0&&(e.data==="raw"?c.data=r.getData():c.data=e.data.parse(r.getDataAsJson())),e.fields!==void 0){const s={};for(const[g,d]of Object.entries(e.fields))s[g]=(n=r.traverse({field:g,errorIfFieldNotSet:d,stableIfNotFound:!d}))==null?void 0:n.id;c.fields=s}if(e.kv!==void 0){const s={};for(const[g,d]of Object.entries(e.kv)){const h=r.getKeyValue(g);if(h===void 0)throw new Error(`Key not found ${g}`);d==="raw"?s[g]=h:s[g]=d.parse(JSON.parse(Buffer.from(h).toString("utf-8")))}c.kv=s}return c}function H(o,e,t){if(o instanceof k){const r=t.accessor(o).node(),i=r.resourceInfo,c=e.map(n=>{var s,g;return[n,(g=(s=r.getField(n))==null?void 0:s.value)==null?void 0:g.id]});return{...i,fields:new Map(c),data:r.getData()??new Uint8Array}}return o}function Y(o,e,t){if(!(o instanceof k))return o;const r=t.accessor(o).node(),i=r.resourceInfo,c=e.map(n=>[n,r.getKeyValue(n)]);return{...i,metadata:Object.fromEntries(c)}}class I{constructor(e,t,r,i){u(this,"finalPredicate");u(this,"state");u(this,"pollingInterval");u(this,"pruning");u(this,"hooks");u(this,"abortController",new AbortController);u(this,"scheduledOnNextState",[]);u(this,"keepRunning",!1);u(this,"currentLoop");u(this,"terminated",!1);this.pl=e,this.root=t,this.logger=i;const{finalPredicate:c,pruning:n,pollingInterval:s,stopPollingDelay:g}=r;this.pruning=n,this.pollingInterval=s,this.finalPredicate=c,this.state=new E(t,c),this.hooks=new v.PollingComputableHooks(()=>this.startUpdating(),()=>this.stopUpdating(),{stopDebounce:g},(d,h)=>this.scheduleOnNextState(d,h))}accessor(e=this.root){if(this.terminated)throw new Error("tree synchronization is terminated");return this.entry(e)}entry(e=this.root){if(this.terminated)throw new Error("tree synchronization is terminated");return new k({treeProvider:()=>this.state,hooks:this.hooks},e)}async refreshState(){if(this.terminated)throw new Error("tree synchronization is terminated");await this.hooks.refreshState()}scheduleOnNextState(e,t){this.terminated?t(new Error("tree synchronization is terminated")):this.scheduledOnNextState.push({resolve:e,reject:t})}startUpdating(){this.terminated||(this.keepRunning=!0,this.currentLoop===void 0&&(this.currentLoop=this.mainLoop()))}stopUpdating(){this.keepRunning=!1}async refresh(){if(this.terminated)throw new Error("tree synchronization is terminated");const e=A(this.state,this.pruning),t=await this.pl.withReadTx("ReadingTree",async r=>await O(r,e));this.state.updateFromResourceData(t,!0)}async mainLoop(){var e,t;for(;this.keepRunning;){let r;this.scheduledOnNextState.length>0&&(r=this.scheduledOnNextState,this.scheduledOnNextState=[]);try{if(await this.refresh(),r!==void 0)for(const i of r)i.resolve()}catch(i){if(r!==void 0)for(const c of r)c.reject(i);if(i instanceof m){(e=this.logger)==null||e.error(i),this.state.invalidateTree("stat update error"),this.state=new E(this.root,this.finalPredicate);continue}else(t=this.logger)==null||t.warn(i)}if(!this.keepRunning||this.terminated)break;try{await U.setTimeout(this.pollingInterval,this.abortController.signal)}catch(i){if(!l.isTimeoutOrCancelError(i))throw new Error("Unexpected error",{cause:i});break}}this.currentLoop=void 0}async terminate(){this.keepRunning=!1,this.terminated=!0,this.abortController.abort(),this.currentLoop!==void 0&&(await this.currentLoop,this.state.invalidateTree("synchronization terminated for the tree"))}async awaitSyncLoopTermination(){this.currentLoop!==void 0&&await this.currentLoop}static async init(e,t,r){const i=new I(e,t,r);return await i.refresh(),i}}exports.PlError=T;exports.PlTreeEntry=k;exports.PlTreeEntryAccessor=R;exports.PlTreeNodeAccessor=_;exports.PlTreeResource=N;exports.PlTreeState=E;exports.SynchronizedTreeState=I;exports.TreeStateUpdateError=m;exports.constructTreeLoadingRequest=A;exports.isPlTreeEntry=q;exports.isPlTreeEntryAccessor=J;exports.isPlTreeNodeAccessor=M;exports.loadTreeState=O;exports.makeResourceSnapshot=B;exports.mapValueAndError=S;exports.mapValueAndErrorIfDefined=K;exports.rsSchema=j;exports.treeEntryToResourceInfo=z;exports.treeEntryToResourceWithData=H;exports.treeEntryToResourceWithMetadata=Y;
978
2
  //# sourceMappingURL=index.js.map
979
- //# sourceMappingURL=index.js.map