@milaboratories/pl-tree 1.15.7 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","names":[],"sources":["../src/state.ts"],"mappings":";;;;;;;YA4BY,uBAAuB;EACjC,IAAI;;qBAGO,6BAA6B;EACxC,YAAY;;cAKR,uBAAuB;WAIT;EACT,MAAM;EACN,OAAO;EACP,OAAO;EACP,QAAQ;EACR;;EAEA;WAVA,QAAM;EAEf,YACkB,cACT,MAAM,WACN,OAAO,0BACP,OAAO,0BACP,QAAQ,aACR,uBAEA;;EAAA;MAGL,SAAS;;YAcH,6BAA6B;EACvC;;;;;;iBAOe;;EAEf;;EAEA;;EAEA;;EAEA;;;EAGA;;;EAGA;;;EAGA;;EAGA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;qBAIW,0BAA0B;;EAErC;;EAGA;;EAEA;WAES,WAAW,YAAY;WAEvB,IAAE,YAAA,WAAA;WAEF,iBAAe;EAIxB,eAAa;EAEb,sBAAoB;EAEpB,eAAa;EACb,kCAAgC;EAChC,yBAAuB;EACvB,0BAAwB;EAGxB,kBAAgB;WAEP,IAAI;EACb,oBAAoB;WAEX,MAAM;WACN,MAAM;WAEN,OAAO;UACR;UACA;EAER,OAAO;EAEP;EACA;EACA;EACA;;EAGA;mBAEiB;EAEjB,YAAY,cAAc,mBAAmB,SAAS;UAgB9C;UAIA;MAIJ;MAIA;MAIA,UAAU;EAIP,SACL,SAAS,SACT,QACK,KAAK;IAA0C;QAC/C,KAAK;IAAwC;MAClD,aAAa,0BACZ,cAAc;EACV,SACL,SAAS,SACT,gBAAgB,cAChB,aAAa,0BACZ,cAAc;EAsDV,gBAAgB,SAAS;EAOzB,iBAAiB,SAAS;MAOtB;EAQJ,WAAW,SAAS;EAKpB,kBAAkB,SAAS;EAO3B,SAAS,SAAS,UAAU;EAU5B,gBAAgB,SAAS;EAUzB,iBAAiB,SAAS;EAU1B,kBAAkB,SAAS;EAU3B,YAAY,SAAS,SAAS,cAAc;EAK5C,kBAAkB,SAAS,SAAS;EAMpC,kBAAkB,aAAa,SAAS,SAAS,cAAc;EAM/D;EAMA,cAAc,gBAAgB;EAMrC;MAOI,cAAc;MAed,iBAAiB;;EASrB;;EAgBA;;qBAcW;WAqBO,kBAAkB;;UAnB5B;mBACS;;;mBAGA;;;UAGT;UACA;;;;WAKQ,OAAO,IAAI;EAE3B,YAGE;;;EAAA,OAAO,mBAAmB,IAAI,mBACd,kBAAkB;;;;MAQzB,QAAQ;EAQZ,gBAAgB,KAAK,KAAK;;;;;MAQtB;UAIH;;;EAMD,SAAS,SAAS,UAAU;EAM5B,IAAI,SAAS,SAAS,KAAK,mBAAmB;EAarD,uBACE,cAAc,wBACd;IAAQ;IAA6B,OAAO;;;;;;UAuctC;;;;;;;;EAwCD,SAAS,UAAU,IAAI;;EA4CvB,SAAS,MAAK,mBAA+B;EAK7C,MAAM,MAAK,mBAA+B;EAK1C,eAAe;EASf,aAAa"}
1
+ {"version":3,"file":"state.d.ts","names":[],"sources":["../src/state.ts"],"mappings":";;;;;;;YA4BY,uBAAuB;EACjC,IAAI;;qBAGO,6BAA6B;EACxC,YAAY,iBAAiB,UAAU;;cAKnC,uBAAuB;WAIT;EACT,MAAM;EACN,OAAO;EACP,OAAO;EACP,QAAQ;EACR;;EAEA;WAVA,QAAM;EAEf,YACkB,cACT,MAAM,WACN,OAAO,0BACP,OAAO,0BACP,QAAQ,aACR,uBAEA;;EAAA;MAGL,SAAS;;YAyBH,6BAA6B;EACvC;;;;;;iBAOe;;EAEf;;EAEA;;EAEA;;EAEA;;;EAGA;;;EAGA;;;EAGA;;EAGA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;qBAIW,0BAA0B;;EAErC;;EAGA;;EAEA;WAES,WAAW,YAAY;WAEvB,IAAE,YAAA,WAAA;WAEF,iBAAe;EAIxB,eAAa;EAEb,sBAAoB;EAEpB,eAAa;EACb,kCAAgC;EAChC,yBAAuB;EACvB,0BAAwB;EAGxB,kBAAgB;WAEP,IAAI;EACb,oBAAoB;WAEX,MAAM;WACN,MAAM;WAEN,OAAO;UACR;UACA;EAER,OAAO;EAEP;EACA;EACA;EACA;;EAGA;mBAEiB;EAEjB,YAAY,cAAc,mBAAmB,SAAS;UAgB9C;UAIA;MAIJ;MAIA;MAIA,UAAU;EAIP,SACL,SAAS,SACT,QACK,KAAK;IAA0C;QAC/C,KAAK;IAAwC;MAClD,aAAa,0BACZ,cAAc;EACV,SACL,SAAS,SACT,gBAAgB,cAChB,aAAa,0BACZ,cAAc;EAsEV,gBAAgB,SAAS;EAOzB,iBAAiB,SAAS;MAOtB;EAQJ,WAAW,SAAS;EAKpB,kBAAkB,SAAS;EAO3B,SAAS,SAAS,UAAU;EAU5B,gBAAgB,SAAS;EAUzB,iBAAiB,SAAS;;;EAY1B,kBAAkB,SAAS;EAU3B,YAAY,SAAS,SAAS,cAAc;EAK5C,kBAAkB,SAAS,SAAS;EAMpC,kBAAkB,aAAa,SAAS,SAAS,cAAc;EAM/D;EAMA,cAAc,gBAAgB;EAMrC;MAOI,cAAc;MAed,iBAAiB;;;;;EAYrB;;EAyBA;;qBAcW;WAqBO,kBAAkB;;UAnB5B;mBACS;;;mBAGA;;;UAGT;UACA;;;;WAKQ,OAAO,IAAI;EAE3B,YAGE;;;EAAA,OAAO,mBAAmB,IAAI,mBACd,kBAAkB;;;;MAQzB,QAAQ;EAQZ,gBAAgB,KAAK,KAAK;;;;;MAQtB;UAIH;;;EAMD,SAAS,SAAS,UAAU;EAM5B,IAAI,SAAS,SAAS,KAAK,mBAAmB;;;;;;EAkBrD,uBACE,cAAc,wBACd;IAAQ;IAA6B,OAAO;;UAYtC;;;;;UA2cA;;;;;;;;EAwCD,SAAS,UAAU,IAAI;;;;EA+BvB,iBAAiB;;;;;;;EAejB,gBAAgB,KAAK;;;UAUpB;;EAeD,SAAS,MAAK,mBAA+B;EAK7C,MAAM,MAAK,mBAA+B;EAK1C,eAAe;EAWf,aAAa"}
package/dist/state.js CHANGED
@@ -4,8 +4,8 @@ import { ChangeSource, KeyedChangeSource } from "@milaboratories/computable";
4
4
  import { cachedDecode, cachedDeserialize, notEmpty } from "@milaboratories/ts-helpers";
5
5
  //#region src/state.ts
6
6
  var TreeStateUpdateError = class extends Error {
7
- constructor(message) {
8
- super(message);
7
+ constructor(message, options) {
8
+ super(message, options);
9
9
  }
10
10
  };
11
11
  var PlTreeField = class {
@@ -38,6 +38,15 @@ var PlTreeField = class {
38
38
  }
39
39
  };
40
40
  const InitialResourceVersion = 0;
41
+ /** Input and Service fields share one list and one lock: both are refused once inputs lock. */
42
+ function isInputLike(type) {
43
+ return type === "Input" || type === "Service";
44
+ }
45
+ /** The only field types the backend lets a client delete, so the only ones whose name can come
46
+ * back under another type between two polls. */
47
+ function isRecreatable(type) {
48
+ return type === "Dynamic" || type === "MTW";
49
+ }
41
50
  /** Never store instances of this class, always get fresh instance from {@link PlTreeState} */
42
51
  var PlTreeResource = class {
43
52
  /** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */
@@ -103,7 +112,13 @@ var PlTreeResource = class {
103
112
  const step = typeof _step === "string" ? { field: _step } : _step;
104
113
  const field = this.fieldsMap.get(step.field);
105
114
  if (field === void 0) {
106
- if (step.errorIfFieldNotFound || step.errorIfFieldNotSet) throw new Error(`Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`);
115
+ if (step.errorIfFieldNotFound || step.errorIfFieldNotSet) {
116
+ if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
117
+ if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
118
+ this.dynamicFieldListChanged?.attachWatcher(watcher);
119
+ throw new Error(`Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`);
120
+ }
121
+ if (step.assertFieldType !== void 0 && (isInputLike(step.assertFieldType) ? !this.inputsLocked : step.assertFieldType === "Output" && !this.outputsLocked)) this.lockedChange?.attachWatcher(watcher);
107
122
  if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
108
123
  else if (step.assertFieldType === "Service" || step.assertFieldType === "Input") if (step.allowPermanentAbsence) return void 0;
109
124
  else throw new Error(`Service or input field not found ${step.field}.`);
@@ -114,21 +129,21 @@ var PlTreeResource = class {
114
129
  if (!this._finalState && !step.stableIfNotFound) onUnstable("field_not_found:" + step.field);
115
130
  return;
116
131
  } else {
132
+ field.change.attachWatcher(watcher);
117
133
  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}`);
118
134
  const ret = {};
119
135
  if (isNotNullSignedResourceId(field.value)) ret.value = field.value;
120
136
  if (isNotNullSignedResourceId(field.error)) ret.error = field.error;
121
- if (ret.value === void 0 && ret.error === void 0) onUnstable("field_not_resolved:" + step.field);
122
- field.change.attachWatcher(watcher);
137
+ if (ret.value === void 0 && ret.error === void 0 && !this._finalState) onUnstable("field_not_resolved:" + step.field);
123
138
  return ret;
124
139
  }
125
140
  }
126
141
  getInputsLocked(watcher) {
127
- if (!this.inputsLocked) this.resourceStateChange?.attachWatcher(watcher);
142
+ if (!this.inputsLocked) this.lockedChange?.attachWatcher(watcher);
128
143
  return this.inputsLocked;
129
144
  }
130
145
  getOutputsLocked(watcher) {
131
- if (!this.outputsLocked) this.resourceStateChange?.attachWatcher(watcher);
146
+ if (!this.outputsLocked) this.lockedChange?.attachWatcher(watcher);
132
147
  return this.outputsLocked;
133
148
  }
134
149
  get isReadyOrError() {
@@ -164,10 +179,12 @@ var PlTreeResource = class {
164
179
  if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);
165
180
  return ret;
166
181
  }
182
+ /** Fields that are neither Input, Output nor Service. Service fields are input-like (the
183
+ * backend refuses them once inputs are locked) and are listed by {@link listInputFields}. */
167
184
  listDynamicFields(watcher) {
168
185
  const ret = [];
169
186
  this.fieldsMap.forEach((field, name) => {
170
- if (field.type !== "Input" && field.type !== "Output") ret.push(name);
187
+ if (!isInputLike(field.type) && field.type !== "Output") ret.push(name);
171
188
  });
172
189
  this.dynamicFieldListChanged?.attachWatcher(watcher);
173
190
  return ret;
@@ -223,11 +240,21 @@ var PlTreeResource = class {
223
240
  }))
224
241
  };
225
242
  }
226
- /** Called when {@link FinalResourceDataPredicate} returns true for the state. */
243
+ /** Called when {@link FinalResourceDataPredicate} returns true for the state.
244
+ *
245
+ * Every change source it retires is fired first: a reader attached to one of them read a
246
+ * state that could still change, and is re-run to read it as final. */
227
247
  markFinal() {
228
248
  if (this._finalState) return;
229
249
  this._finalState = true;
230
250
  notEmpty(this.finalChanged).markChanged("marked final");
251
+ this.fieldsMap.forEach((field) => field.change.markChanged("marked final"));
252
+ this.resourceStateChange?.markChanged("marked final");
253
+ this.lockedChange?.markChanged("marked final");
254
+ this.inputAndServiceFieldListChanged?.markChanged("marked final");
255
+ this.outputFieldListChanged?.markChanged("marked final");
256
+ this.dynamicFieldListChanged?.markChanged("marked final");
257
+ this.kvChangedPerKey?.markAllChanged("marked final");
231
258
  this.finalChanged = void 0;
232
259
  this.resourceStateChange = void 0;
233
260
  this.dynamicFieldListChanged = void 0;
@@ -306,7 +333,22 @@ var PlTreeState = class {
306
333
  res.resourceRemoved.attachWatcher(watcher);
307
334
  return res;
308
335
  }
336
+ /** Applies a batch of resource bodies to the mirror.
337
+ *
338
+ * Any error leaves the tree invalidated and surfaces as a {@link TreeStateUpdateError}: the
339
+ * batch is applied resource by resource, so a throw part way through has already mutated the
340
+ * mirror and fired watchers, and only a rebuild restores a state some poll confirmed. */
309
341
  updateFromResourceData(resourceData, opts = {}) {
342
+ try {
343
+ this.applyResourceData(resourceData, opts);
344
+ } catch (e) {
345
+ const message = e instanceof Error ? e.message : String(e);
346
+ if (this._isValid) this.invalidateTree(message);
347
+ if (e instanceof TreeStateUpdateError) throw e;
348
+ throw new TreeStateUpdateError(`tree update failed: ${message}`, { cause: e });
349
+ }
350
+ }
351
+ applyResourceData(resourceData, opts) {
310
352
  const { allowOrphanInputs = false, stat } = opts;
311
353
  this.checkValid();
312
354
  const incrementRefs = [];
@@ -382,7 +424,7 @@ var PlTreeState = class {
382
424
  field = new PlTreeField(fd.name, fd.type, fd.value, fd.error, fd.status, fd.valueIsFinal, resource.version);
383
425
  if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);
384
426
  if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);
385
- if (fd.type === "Input" || fd.type === "Service") {
427
+ if (isInputLike(fd.type)) {
386
428
  if (resource.inputsLocked) unexpectedTransitionError(`adding ${fd.type} (${fd.name}) field while inputs locked`);
387
429
  notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);
388
430
  } else if (fd.type === "Output") {
@@ -395,19 +437,20 @@ var PlTreeState = class {
395
437
  if (stat) stat.fieldsAdded++;
396
438
  } else {
397
439
  if (field.type !== fd.type) {
398
- if (field.type !== "Dynamic") unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);
399
- notEmpty(resource.dynamicFieldListChanged).markChanged(`field ${fd.name} changed type from Dynamic to ${fd.type} in ${resourceIdToString(resource.id)}`);
400
- if (field.type === "Input" || field.type === "Service") {
401
- if (resource.inputsLocked) unexpectedTransitionError(`adding input field "${fd.name}", while corresponding list is locked`);
402
- notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);
403
- }
404
- if (field.type === "Output") {
405
- if (resource.outputsLocked) unexpectedTransitionError(`adding output field "${fd.name}", while corresponding list is locked`);
406
- notEmpty(resource.outputFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);
440
+ if (!isRecreatable(field.type)) unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);
441
+ const reason = `field ${fd.name} changed type ${field.type} -> ${fd.type} in ${resourceIdToString(resource.id)}`;
442
+ notEmpty(resource.dynamicFieldListChanged).markChanged(reason);
443
+ if (isInputLike(fd.type)) {
444
+ if (resource.inputsLocked) unexpectedTransitionError(`adding ${fd.type} field "${fd.name}", while inputs are locked`);
445
+ notEmpty(resource.inputAndServiceFieldListChanged).markChanged(reason);
446
+ } else if (fd.type === "Output") {
447
+ if (resource.outputsLocked) unexpectedTransitionError(`adding output field "${fd.name}", while outputs are locked`);
448
+ notEmpty(resource.outputFieldListChanged).markChanged(reason);
407
449
  }
408
450
  field.type = fd.type;
409
- field.change.markChanged(`field ${fd.name} type changed to ${fd.type} in ${resourceIdToString(resource.id)}`);
451
+ field.change.markChanged(reason);
410
452
  changed = true;
453
+ metadataChanged = true;
411
454
  }
412
455
  if (field.value !== fd.value) {
413
456
  if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);
@@ -442,6 +485,7 @@ var PlTreeState = class {
442
485
  if (field.type === "Input" || field.type === "Service" || field.type === "Output") unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);
443
486
  field.change.markChanged(`dynamic field ${fieldName} removed from ${resourceIdToString(resource.id)}`);
444
487
  fields.delete(fieldName);
488
+ changed = true;
445
489
  metadataChanged = true;
446
490
  if (stat) stat.fieldsRemoved++;
447
491
  if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);
@@ -472,6 +516,10 @@ var PlTreeState = class {
472
516
  changed = true;
473
517
  if (stat) stat.readyFlips++;
474
518
  }
519
+ if (resource.finalFlag !== rd.final) {
520
+ resource.finalFlag = rd.final;
521
+ changed = true;
522
+ }
475
523
  let kvWalk = rd.kv.length > 0 ? resource.kv.entries() : void 0;
476
524
  for (const kv of rd.kv) {
477
525
  let current;
@@ -606,17 +654,46 @@ var PlTreeState = class {
606
654
  const res = this.resources.get(rid);
607
655
  if (res === void 0) continue;
608
656
  if (res.refCount > 0) continue;
609
- const seed = [];
610
- res.fieldsMap.forEach((field) => {
611
- if (isNotNullSignedResourceId(field.value)) seed.push(field.value);
612
- if (isNotNullSignedResourceId(field.error)) seed.push(field.error);
613
- field.change.markChanged(`field ${field.name} removed after root ${resourceIdToString(res.id)} left the root set`);
614
- });
615
- if (isNotNullSignedResourceId(res.error)) seed.push(res.error);
616
- res.resourceRemoved.markChanged(`resource removed after leaving the root set: ${resourceIdToString(res.id)}`);
617
- this.resources.delete(rid);
618
- this.collectGarbage(seed);
657
+ this.removeUnreferenced(res, `root ${resourceIdToString(res.id)} left the root set`);
658
+ }
659
+ }
660
+ /** Roots held in the heap and not final: the ones whose existence a poll must check, since
661
+ * a walk seeded at a deleted resource returns nothing rather than an error. A final root is
662
+ * never re-read, so it is not checked either. */
663
+ nonFinalRoots() {
664
+ const ret = [];
665
+ for (const rid of this.roots) {
666
+ const res = this.resources.get(rid);
667
+ if (res !== void 0 && !res.finalState) ret.push(rid);
619
668
  }
669
+ return ret;
670
+ }
671
+ /** Drops from the heap a root the backend has deleted, with the subtree only it held, and
672
+ * notifies its readers. The id stays in the root set: a resource id is never reused, so the
673
+ * root stays absent and readers see "not found". Returns false, changing nothing, if the
674
+ * root is not held or is still referenced from elsewhere in the heap; a root is protected
675
+ * from the refcount GC, so it is retried once its referrers are gone (the next call, or the
676
+ * caller's next pass). */
677
+ dropDeletedRoot(rid) {
678
+ this.checkValid();
679
+ const res = this.resources.get(rid);
680
+ if (res === void 0 || res.refCount > 0) return false;
681
+ this.removeUnreferenced(res, `root ${resourceIdToString(rid)} deleted on the backend`);
682
+ return true;
683
+ }
684
+ /** Removes a resource nothing in the heap references and cascades the refcount GC into
685
+ * what it referenced. */
686
+ removeUnreferenced(res, reason) {
687
+ const seed = [];
688
+ res.fieldsMap.forEach((field) => {
689
+ if (isNotNullSignedResourceId(field.value)) seed.push(field.value);
690
+ if (isNotNullSignedResourceId(field.error)) seed.push(field.error);
691
+ field.change.markChanged(`field ${field.name} removed: ${reason}`);
692
+ });
693
+ if (isNotNullSignedResourceId(res.error)) seed.push(res.error);
694
+ res.resourceRemoved.markChanged(`resource removed: ${reason}`);
695
+ this.resources.delete(res.id);
696
+ this.collectGarbage(seed);
620
697
  }
621
698
  /** @deprecated use "entry" instead */
622
699
  accessor(rid = this.root) {
@@ -631,6 +708,7 @@ var PlTreeState = class {
631
708
  this._isValid = false;
632
709
  this.invalidationMessage = msg;
633
710
  this.rootsChanged.markChanged("tree invalidated");
711
+ this.resourcesAdded.markChanged("tree invalidated");
634
712
  this.resources.forEach((res) => {
635
713
  res.markAllChanged();
636
714
  });
package/dist/state.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","names":[],"sources":["../src/state.ts"],"sourcesContent":["import type {\n BasicResourceData,\n FieldData,\n FieldStatus,\n FieldType,\n KeyValue,\n OptionalSignedResourceId,\n ResourceData,\n SignedResourceId,\n ResourceKind,\n ResourceType,\n} from \"@milaboratories/pl-client\";\nimport {\n isNotNullSignedResourceId,\n isNullSignedResourceId,\n NullSignedResourceId,\n resourceIdToString,\n stringifyWithResourceId,\n} from \"@milaboratories/pl-client\";\nimport type { Watcher } from \"@milaboratories/computable\";\nimport { ChangeSource, KeyedChangeSource } from \"@milaboratories/computable\";\nimport { PlTreeEntry } from \"./accessors\";\nimport type { ValueAndError } from \"./value_and_error\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { cachedDecode, cachedDeserialize, notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { FieldTraversalStep, GetFieldStep } from \"./traversal_ops\";\nimport type { FinalResourceDataPredicate } from \"@milaboratories/pl-client\";\n\nexport type ExtendedResourceData = ResourceData & {\n kv: KeyValue[];\n};\n\nexport class TreeStateUpdateError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nclass PlTreeField implements FieldData {\n readonly change = new ChangeSource();\n\n constructor(\n public readonly name: string,\n public type: FieldType,\n public value: OptionalSignedResourceId,\n public error: OptionalSignedResourceId,\n public status: FieldStatus,\n public valueIsFinal: boolean,\n /** Last version of resource this field was observed, used to garbage collect fields in tree patching procedure */\n public resourceVersion: number,\n ) {}\n\n get state(): FieldData {\n return {\n name: this.name,\n type: this.type,\n status: this.status,\n value: this.value,\n error: this.error,\n valueIsFinal: this.valueIsFinal,\n };\n }\n}\n\nconst InitialResourceVersion = 0;\n\nexport type ResourceDataWithFinalState = ResourceData & {\n finalState: boolean;\n};\n\n/** Cross-cycle re-fetch duplication counters, populated by {@link PlTreeState.updateFromResourceData}.\n * Separates genuinely new/changed resources from redundant re-fetches of unchanged ones\n * (the delta-skip opportunity: intra-cycle dedup already happens in the loader, so all waste here\n * is cross-cycle). */\nexport interface ResourceUpdateStat {\n /** Resources seen for the first time in this update. */\n resourcesNew: number;\n /** Resources already held whose state actually changed. */\n resourcesChanged: number;\n /** Resources already held that came back unchanged: a pure duplicate re-fetch. */\n resourcesUnchanged: number;\n /** data + KV bytes of the unchanged bucket: wasted downlink. */\n bytesUnchanged: number;\n /** Changed resources whose immutable metadata (type, kind, field set) did not change,\n * i.e. metadata re-streamed even though only a value or flag flipped. */\n metadataStableChanged: number;\n /** Per-resource fetches spent on unchanged resources (BFS only; streaming re-sends them\n * inside one stream, so this stays 0). */\n bfsRequestsWasted: number;\n /** Whether the current load used backend streaming; set by the loader, read here to\n * attribute {@link bfsRequestsWasted}. */\n usedStreaming: boolean;\n // Breakdown of what changed among resourcesChanged (a resource can bump several).\n /** New fields appeared on a held resource. */\n fieldsAdded: number;\n /** Dynamic fields disappeared from a held resource. */\n fieldsRemoved: number;\n /** Existing field value pointer changed (a new value/result was attached). */\n fieldsChanged: number;\n /** KV entries added, changed, or deleted. */\n kvChanged: number;\n /** resourceReady flipped. */\n readyFlips: number;\n /** inputsLocked or outputsLocked transitioned. */\n locksChanged: number;\n /** An error resource was attached. */\n errorsAttached: number;\n /** originalResourceId resolved (duplicate -> original). */\n duplicatesResolved: number;\n /** Held resources that transitioned to final this update. */\n resourcesMarkedFinal: number;\n}\n\n/** Never store instances of this class, always get fresh instance from {@link PlTreeState} */\nexport class PlTreeResource implements ResourceDataWithFinalState {\n /** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */\n refCount: number = 0;\n\n /** Increments each time resource is checked for difference with new state */\n version: number = InitialResourceVersion;\n /** Set to resource version when resource state, or it's fields have changed */\n dataVersion: number = InitialResourceVersion;\n\n readonly fieldsMap: Map<string, PlTreeField> = new Map();\n\n readonly kv = new Map<string, Uint8Array>();\n\n readonly resourceRemoved = new ChangeSource();\n\n // following change source are removed when resource is marked as final\n\n finalChanged? = new ChangeSource();\n\n resourceStateChange? = new ChangeSource();\n\n lockedChange? = new ChangeSource();\n inputAndServiceFieldListChanged? = new ChangeSource();\n outputFieldListChanged? = new ChangeSource();\n dynamicFieldListChanged? = new ChangeSource();\n\n // kvChangedGlobal? = new ChangeSource();\n kvChangedPerKey? = new KeyedChangeSource();\n\n readonly id: SignedResourceId;\n originalResourceId: OptionalSignedResourceId;\n\n readonly kind: ResourceKind;\n readonly type: ResourceType;\n\n readonly data?: Uint8Array;\n private dataAsString?: string;\n private dataAsJson?: unknown;\n\n error: OptionalSignedResourceId;\n\n inputsLocked: boolean;\n outputsLocked: boolean;\n resourceReady: boolean;\n finalFlag: boolean;\n\n /** Set externally by the tree, using {@link FinalResourceDataPredicate} */\n _finalState: boolean = false;\n\n private readonly logger?: MiLogger;\n\n constructor(initialState: BasicResourceData, logger?: MiLogger) {\n this.id = initialState.id;\n this.originalResourceId = initialState.originalResourceId;\n this.kind = initialState.kind;\n this.type = initialState.type;\n this.data = initialState.data;\n this.error = initialState.error;\n this.inputsLocked = initialState.inputsLocked;\n this.outputsLocked = initialState.outputsLocked;\n this.resourceReady = initialState.resourceReady;\n this.finalFlag = initialState.final;\n this.logger = logger;\n }\n\n // TODO add logging\n\n private info(msg: string) {\n if (this.logger !== undefined) this.logger.info(msg);\n }\n\n private warn(msg: string) {\n if (this.logger !== undefined) this.logger.warn(msg);\n }\n\n get final(): boolean {\n return this.finalFlag;\n }\n\n get finalState(): boolean {\n return this._finalState;\n }\n\n get fields(): FieldData[] {\n return [...this.fieldsMap.values()];\n }\n\n public getField(\n watcher: Watcher,\n _step:\n | (Omit<GetFieldStep, \"errorIfFieldNotFound\"> & { errorIfFieldNotFound: true })\n | (Omit<GetFieldStep, \"errorIfFieldNotSet\"> & { errorIfFieldNotSet: true }),\n onUnstable: (marker: string) => void,\n ): ValueAndError<SignedResourceId>;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void,\n ): ValueAndError<SignedResourceId> | undefined;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void = () => {},\n ): ValueAndError<SignedResourceId> | undefined {\n const step: FieldTraversalStep = typeof _step === \"string\" ? { field: _step } : _step;\n\n const field = this.fieldsMap.get(step.field);\n if (field === undefined) {\n if (step.errorIfFieldNotFound || step.errorIfFieldNotSet)\n throw new Error(\n `Field \"${step.field}\" not found in resource ${resourceIdToString(this.id)}`,\n );\n\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === \"Service\" || step.assertFieldType === \"Input\") {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Service or input field not found ${step.field}.`);\n }\n\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === \"Output\") {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Output field not found ${step.field}.`);\n }\n\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n if (!this._finalState && !step.stableIfNotFound) onUnstable(\"field_not_found:\" + step.field);\n\n return undefined;\n } else {\n if (step.assertFieldType !== undefined && field.type !== step.assertFieldType)\n throw new Error(\n `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`,\n );\n\n const ret = {} as ValueAndError<SignedResourceId>;\n if (isNotNullSignedResourceId(field.value)) ret.value = field.value;\n if (isNotNullSignedResourceId(field.error)) ret.error = field.error;\n if (ret.value === undefined && ret.error === undefined)\n // this method returns value and error of the field, thus those values are considered to be accessed;\n // any existing but not resolved field here is considered to be unstable, in the sense it is\n // considered to acquire some resolved value eventually\n onUnstable(\"field_not_resolved:\" + step.field);\n field.change.attachWatcher(watcher);\n return ret;\n }\n }\n\n public getInputsLocked(watcher: Watcher): boolean {\n if (!this.inputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.resourceStateChange?.attachWatcher(watcher);\n return this.inputsLocked;\n }\n\n public getOutputsLocked(watcher: Watcher): boolean {\n if (!this.outputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.resourceStateChange?.attachWatcher(watcher);\n return this.outputsLocked;\n }\n\n public get isReadyOrError(): boolean {\n return (\n this.error !== NullSignedResourceId ||\n this.resourceReady ||\n this.originalResourceId !== NullSignedResourceId\n );\n }\n\n public getIsFinal(watcher: Watcher): boolean {\n this.finalChanged?.attachWatcher(watcher);\n return this._finalState;\n }\n\n public getIsReadyOrError(watcher: Watcher): boolean {\n if (!this.isReadyOrError)\n // reverse transition can't happen, so there is no reason to wait for value to change if it is already true\n this.resourceStateChange?.attachWatcher(watcher);\n return this.isReadyOrError;\n }\n\n public getError(watcher: Watcher): SignedResourceId | undefined {\n if (isNullSignedResourceId(this.error)) {\n this.resourceStateChange?.attachWatcher(watcher);\n return undefined;\n } else {\n // reverse transition can't happen, so there is no reason to wait for value to change, if error already set\n return this.error;\n }\n }\n\n public listInputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === \"Input\" || field.type === \"Service\") ret.push(name);\n });\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public listOutputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === \"Output\") ret.push(name);\n });\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public listDynamicFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type !== \"Input\" && field.type !== \"Output\") ret.push(name);\n });\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public getKeyValue(watcher: Watcher, key: string): Uint8Array | undefined {\n this.kvChangedPerKey?.attachWatcher(key, watcher);\n return this.kv.get(key);\n }\n\n public getKeyValueString(watcher: Watcher, key: string): string | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDecode(bytes);\n }\n\n public getKeyValueAsJson<T = unknown>(watcher: Watcher, key: string): T | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDeserialize(bytes);\n }\n\n public getDataAsString(): string | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsString === undefined) this.dataAsString = cachedDecode(this.data);\n return this.dataAsString;\n }\n\n public getDataAsJson<T = unknown>(): T | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsJson === undefined) this.dataAsJson = cachedDeserialize(this.data);\n return this.dataAsJson as T;\n }\n\n verifyReadyState() {\n if (this.resourceReady && !this.inputsLocked)\n throw new Error(\n `ready without input or output lock: ${stringifyWithResourceId(this.basicState)}`,\n );\n }\n\n get basicState(): BasicResourceData {\n return {\n id: this.id,\n kind: this.kind,\n type: this.type,\n data: this.data,\n resourceReady: this.resourceReady,\n inputsLocked: this.inputsLocked,\n outputsLocked: this.outputsLocked,\n error: this.error,\n originalResourceId: this.originalResourceId,\n final: this.finalFlag,\n };\n }\n\n get extendedState(): ExtendedResourceData {\n return {\n ...this.basicState,\n fields: this.fields,\n kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value })),\n };\n }\n\n /** Called when {@link FinalResourceDataPredicate} returns true for the state. */\n markFinal() {\n if (this._finalState) return;\n\n this._finalState = true;\n notEmpty(this.finalChanged).markChanged(\"marked final\");\n this.finalChanged = undefined;\n this.resourceStateChange = undefined;\n this.dynamicFieldListChanged = undefined;\n this.inputAndServiceFieldListChanged = undefined;\n this.outputFieldListChanged = undefined;\n this.lockedChange = undefined;\n // this.kvChangedGlobal = undefined;\n this.kvChangedPerKey = undefined;\n }\n\n /** Used for invalidation */\n markAllChanged() {\n this.fieldsMap.forEach((field) => field.change.markChanged(\"marked all changed\"));\n this.finalChanged?.markChanged(\"marked all changed\");\n this.resourceStateChange?.markChanged(\"marked all changed\");\n this.lockedChange?.markChanged(\"marked all changed\");\n this.inputAndServiceFieldListChanged?.markChanged(\"marked all changed\");\n this.outputFieldListChanged?.markChanged(\"marked all changed\");\n this.dynamicFieldListChanged?.markChanged(\"marked all changed\");\n // this.kvChangedGlobal?.markChanged('marked all changed');\n this.kvChangedPerKey?.markAllChanged(\"marked all changed\");\n this.resourceRemoved.markChanged(\"marked all changed\");\n }\n}\n\nexport class PlTreeState {\n /** resource heap */\n private resources: Map<SignedResourceId, PlTreeResource> = new Map();\n private readonly resourcesAdded = new ChangeSource();\n /** Marked when the root set changes (roots discovered or removed), so multi-root\n * accessors that read the set recompute. */\n private readonly rootsChanged = new ChangeSource();\n /** Resets to false if any invalid state transitions are registered,\n * after that tree will produce errors for any read or write operations */\n private _isValid: boolean = true;\n private invalidationMessage?: string;\n\n /** The set of roots — resources protected from GC. Originally a single `root`,\n * generalized to a set so several explicit roots and dynamically-discovered shared\n * roots can coexist in one heap. Mutated through {@link setRoots}. */\n public readonly roots: Set<SignedResourceId>;\n\n constructor(\n /** Resources protected from GC. Accepts a single id (single-root, the original\n * contract) or a set of ids (multi-root). */\n roots: SignedResourceId | Set<SignedResourceId>,\n public readonly isFinalPredicate: FinalResourceDataPredicate,\n ) {\n this.roots = roots instanceof Set ? new Set(roots) : new Set([roots]);\n }\n\n /** Backward-compatible single-root accessor. Returns the sole root and THROWS if the\n * tree has zero or more than one root, so a multi-root tree can never be read as if it\n * had one. Mirrors the {@link entry} guard. */\n public get root(): SignedResourceId {\n if (this.roots.size !== 1)\n throw new Error(\n `single-root accessor used on a tree with ${this.roots.size} roots; use the multi-root API`,\n );\n return this.roots.values().next().value!;\n }\n\n public forEachResource(cb: (res: ResourceDataWithFinalState) => void): void {\n this.resources.forEach((v) => cb(v));\n }\n\n /** False once the tree has been invalidated (an inconsistent update, or termination of\n * its synchronization loop). {@link dumpState} deliberately reads through an invalid\n * tree, so anything persisting that dump must check this first: the contents of an\n * invalidated tree are not something to write to disk. */\n public get isValid(): boolean {\n return this._isValid;\n }\n\n private checkValid() {\n if (!this._isValid) throw new Error(this.invalidationMessage ?? \"tree is in invalid state\");\n }\n\n /** Reactive read of the current root set. Attaches the watcher to {@link rootsChanged}\n * so a Computable recomputes when discovered roots appear or disappear. */\n public getRoots(watcher: Watcher): SignedResourceId[] {\n this.checkValid();\n this.rootsChanged.attachWatcher(watcher);\n return [...this.roots];\n }\n\n public get(watcher: Watcher, rid: SignedResourceId): PlTreeResource {\n this.checkValid();\n const res = this.resources.get(rid);\n if (res === undefined) {\n // to make recovery from resource not found possible, considering some\n // race conditions, where computable is created before tree is updated\n this.resourcesAdded.attachWatcher(watcher);\n throw new Error(`resource ${resourceIdToString(rid)} not found in the tree`);\n }\n res.resourceRemoved.attachWatcher(watcher);\n return res;\n }\n\n updateFromResourceData(\n resourceData: ExtendedResourceData[],\n opts: { allowOrphanInputs?: boolean; stat?: ResourceUpdateStat } = {},\n ) {\n const { allowOrphanInputs = false, stat } = opts;\n this.checkValid();\n\n // All resources for which recount should be incremented, first are aggregated in this list\n const incrementRefs: SignedResourceId[] = [];\n const decrementRefs: SignedResourceId[] = [];\n\n // `resourcesAdded` is notified once, after the loop: markChanged detaches every watcher on\n // its first call and no watcher can attach in the middle of this synchronous loop, so a\n // single notification covers every resource added here. It also builds the marker string\n // just once, which is worth doing because resourceIdToString on a signed id parses a\n // BigInt and hex-decodes a Buffer.\n let addedCount = 0;\n let firstAdded: SignedResourceId | undefined;\n\n // Diagnostic context for unexpectedTransitionError, refreshed once per resource. It lives\n // out here so the loop body allocates neither a closure nor a snapshot per resource on the\n // path where nothing goes wrong. Only the mutable half of BasicResourceData needs\n // capturing: id, kind, type and data are readonly on PlTreeResource, so they are read back\n // from the resource when the message is built.\n let errRd: ExtendedResourceData;\n let errRes: PlTreeResource | undefined;\n let errOriginalResourceId: OptionalSignedResourceId = NullSignedResourceId;\n let errError: OptionalSignedResourceId = NullSignedResourceId;\n let errInputsLocked = false;\n let errOutputsLocked = false;\n let errResourceReady = false;\n let errFinal = false;\n const unexpectedTransitionError = (reason: string): never => {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { fields, ...rdWithoutFields } = errRd;\n const statBeforeMutation: BasicResourceData | undefined =\n errRes === undefined\n ? undefined\n : {\n id: errRes.id,\n kind: errRes.kind,\n type: errRes.type,\n data: errRes.data,\n resourceReady: errResourceReady,\n inputsLocked: errInputsLocked,\n outputsLocked: errOutputsLocked,\n error: errError,\n originalResourceId: errOriginalResourceId,\n final: errFinal,\n };\n this.invalidateTree();\n throw new TreeStateUpdateError(\n `Unexpected resource state transition (${reason}): ${stringifyWithResourceId(\n rdWithoutFields,\n )} -> ${stringifyWithResourceId(statBeforeMutation)}`,\n );\n };\n\n // patching / creating resources\n for (const rd of resourceData) {\n let resource = this.resources.get(rd.id);\n const held = resource !== undefined;\n let changed = false;\n // Structural/metadata change (new or removed field). type and kind are readonly, so\n // they never change; this flag isolates value/flag-only changes from real metadata churn.\n let metadataChanged = false;\n\n errRd = rd;\n errRes = resource;\n\n if (resource !== undefined) {\n // updating existing resource\n errOriginalResourceId = resource.originalResourceId;\n errError = resource.error;\n errInputsLocked = resource.inputsLocked;\n errOutputsLocked = resource.outputsLocked;\n errResourceReady = resource.resourceReady;\n errFinal = resource.finalFlag;\n\n if (resource.finalState)\n unexpectedTransitionError(\"resource state can\\t be updated after it is marked as final\");\n\n // updating resource version, even if it was not changed\n resource.version += 1;\n\n // duplicate / original\n if (resource.originalResourceId !== rd.originalResourceId) {\n if (resource.originalResourceId !== NullSignedResourceId)\n unexpectedTransitionError(\"originalResourceId can't change after it is set\");\n resource.originalResourceId = rd.originalResourceId;\n // duplicate status of the resource counts as ready for the external observer\n notEmpty(resource.resourceStateChange).markChanged(\n `originalResourceId changed for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.duplicatesResolved++;\n }\n\n // error\n if (isNotNullSignedResourceId(rd.error) && resource.error !== rd.error) {\n if (isNotNullSignedResourceId(resource.error))\n unexpectedTransitionError(\"resource can't change attached error after it is set\");\n resource.error = rd.error;\n incrementRefs.push(resource.error);\n notEmpty(resource.resourceStateChange).markChanged(\n `error changed for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.errorsAttached++;\n }\n\n // updating fields.\n //\n // Fields normally arrive in the order they are already stored, so the stored ones are\n // walked in lockstep with the incoming ones and matched by direct name comparison\n // rather than through a fieldsMap lookup: every field name in a poll response is\n // freshly decoded from protobuf, so V8 has no cached hash for any of them.\n //\n // Any divergence (reordering, an insertion, a removal) abandons the walk, and the rest\n // of the resource resolves through fieldsMap.get, which is always correct. The walk is\n // abandoned before any fieldsMap.set, so it never observes an insertion made through\n // the live iterator.\n let walk: MapIterator<PlTreeField> | undefined =\n rd.fields.length > 0 ? resource.fieldsMap.values() : undefined;\n for (const fd of rd.fields) {\n let field: PlTreeField | undefined;\n if (walk !== undefined) {\n const next = walk.next();\n if (next.done === true || next.value.name !== fd.name) walk = undefined;\n else field = next.value;\n }\n if (field === undefined) field = resource.fieldsMap.get(fd.name);\n\n if (!field) {\n // new field\n\n field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n resource.version,\n );\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n\n if (fd.type === \"Input\" || fd.type === \"Service\") {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while inputs locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n } else if (fd.type === \"Output\") {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while outputs locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n } else {\n notEmpty(resource.dynamicFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n }\n\n resource.fieldsMap.set(fd.name, field);\n\n changed = true;\n metadataChanged = true;\n if (stat) stat.fieldsAdded++;\n } else {\n // change of old field\n\n // in principle this transition is possible, see assertions below\n if (field.type !== fd.type) {\n if (field.type !== \"Dynamic\")\n unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);\n notEmpty(resource.dynamicFieldListChanged).markChanged(\n `field ${fd.name} changed type from Dynamic to ${fd.type} in ${resourceIdToString(resource.id)}`,\n );\n if (field.type === \"Input\" || field.type === \"Service\") {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding input field \"${fd.name}\", while corresponding list is locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(\n `field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`,\n );\n }\n if (field.type === \"Output\") {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding output field \"${fd.name}\", while corresponding list is locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(\n `field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`,\n );\n }\n field.type = fd.type;\n field.change.markChanged(\n `field ${fd.name} type changed to ${fd.type} in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n // field value\n if (field.value !== fd.value) {\n if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);\n field.value = fd.value;\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n field.change.markChanged(\n `field ${fd.name} value changed in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.fieldsChanged++;\n }\n\n // field error\n if (field.error !== fd.error) {\n if (isNotNullSignedResourceId(field.error)) decrementRefs.push(field.error);\n field.error = fd.error;\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n field.change.markChanged(\n `field ${fd.name} error changed in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n // field status\n if (field.status !== fd.status) {\n field.status = fd.status;\n field.change.markChanged(\n `field ${fd.name} status changed to ${fd.status} in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n // field valueIsFinal flag\n if (field.valueIsFinal !== fd.valueIsFinal) {\n field.valueIsFinal = fd.valueIsFinal;\n field.change.markChanged(\n `field ${fd.name} valueIsFinal changed to ${fd.valueIsFinal} in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n field.resourceVersion = resource.version;\n }\n }\n\n // detecting removed fields\n resource.fieldsMap.forEach((field, fieldName, fields) => {\n if (field.resourceVersion !== resource!.version) {\n if (field.type === \"Input\" || field.type === \"Service\" || field.type === \"Output\")\n unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);\n field.change.markChanged(\n `dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`,\n );\n fields.delete(fieldName);\n metadataChanged = true;\n if (stat) stat.fieldsRemoved++;\n\n if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);\n if (isNotNullSignedResourceId(field.error)) decrementRefs.push(field.error);\n\n notEmpty(resource!.dynamicFieldListChanged).markChanged(\n `dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`,\n );\n }\n });\n\n // inputsLocked\n if (resource.inputsLocked !== rd.inputsLocked) {\n if (resource.inputsLocked) unexpectedTransitionError(\"inputs unlocking is not permitted\");\n resource.inputsLocked = rd.inputsLocked;\n notEmpty(resource.lockedChange).markChanged(\n `inputs locked for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.locksChanged++;\n }\n\n // outputsLocked\n if (resource.outputsLocked !== rd.outputsLocked) {\n if (resource.outputsLocked)\n unexpectedTransitionError(\"outputs unlocking is not permitted\");\n resource.outputsLocked = rd.outputsLocked;\n notEmpty(resource.lockedChange).markChanged(\n `outputs locked for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.locksChanged++;\n }\n\n // ready flag\n if (resource.resourceReady !== rd.resourceReady) {\n const readyStateBefore = resource.resourceReady;\n resource.resourceReady = rd.resourceReady;\n resource.verifyReadyState();\n if (!resource.isReadyOrError)\n unexpectedTransitionError(\n `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`,\n );\n notEmpty(resource.resourceStateChange).markChanged(\n `ready flag changed to ${rd.resourceReady} for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.readyFlips++;\n }\n\n // syncing kv. Same lockstep walk as the fields above, for the same reason: kv keys\n // arrive in a stable order and are freshly decoded strings. The walk is only set up\n // when there is something to compare against, so a resource with no kv allocates no\n // iterator.\n let kvWalk: MapIterator<[string, Uint8Array]> | undefined =\n rd.kv.length > 0 ? resource.kv.entries() : undefined;\n for (const kv of rd.kv) {\n let current: Uint8Array | undefined;\n if (kvWalk !== undefined) {\n const next = kvWalk.next();\n if (next.done === true || next.value[0] !== kv.key) kvWalk = undefined;\n else current = next.value[1];\n }\n if (current === undefined) current = resource.kv.get(kv.key);\n if (current === undefined) {\n resource.kv.set(kv.key, kv.value);\n kvWalk = undefined;\n notEmpty(resource.kvChangedPerKey).markChanged(\n kv.key,\n `kv added for ${resourceIdToString(resource.id)}: ${kv.key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n } else if (Buffer.compare(current, kv.value) !== 0) {\n resource.kv.set(kv.key, kv.value);\n notEmpty(resource.kvChangedPerKey).markChanged(\n kv.key,\n `kv changed for ${resourceIdToString(resource.id)}: ${kv.key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n }\n }\n\n if (resource.kv.size > rd.kv.length) {\n // only it this case it makes sense to check for deletions\n const newStateKeys = new Set(rd.kv.map((kv) => kv.key));\n\n // deleting keys not present in resource anymore\n resource.kv.forEach((_value, key, map) => {\n if (!newStateKeys.has(key)) {\n map.delete(key);\n notEmpty(resource!.kvChangedPerKey).markChanged(\n key,\n `kv deleted for ${resourceIdToString(resource!.id)}: ${key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n }\n });\n }\n\n if (changed) {\n // if resource was changed, updating resource data version\n resource.dataVersion = resource.version;\n if (this.isFinalPredicate(resource)) {\n resource.markFinal();\n if (stat) stat.resourcesMarkedFinal++;\n }\n }\n } else {\n // creating new resource\n\n resource = new PlTreeResource(rd);\n resource.verifyReadyState();\n if (isNotNullSignedResourceId(resource.error)) incrementRefs.push(resource.error);\n for (const fd of rd.fields) {\n const field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n InitialResourceVersion,\n );\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n resource.fieldsMap.set(fd.name, field);\n }\n\n // adding kv\n for (const kv of rd.kv) resource.kv.set(kv.key, kv.value);\n\n // checking that resource is final, and if so, marking it\n if (this.isFinalPredicate(resource)) resource.markFinal();\n\n // adding the resource to the heap\n this.resources.set(resource.id, resource);\n if (addedCount === 0) firstAdded = resource.id;\n addedCount++;\n }\n\n if (stat) {\n if (!held) stat.resourcesNew++;\n else if (changed) {\n stat.resourcesChanged++;\n if (!metadataChanged) stat.metadataStableChanged++;\n } else {\n stat.resourcesUnchanged++;\n stat.bytesUnchanged += rd.data?.length ?? 0;\n for (const kv of rd.kv) stat.bytesUnchanged += kv.value.length;\n if (!stat.usedStreaming) stat.bfsRequestsWasted++;\n }\n }\n }\n\n if (firstAdded !== undefined)\n this.resourcesAdded.markChanged(\n addedCount === 1\n ? `new resource ${resourceIdToString(firstAdded)} added`\n : `${addedCount} new resources added, first ${resourceIdToString(firstAdded)}`,\n );\n\n // applying refCount increments\n for (const rid of incrementRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount++;\n }\n\n // recursively applying refCount decrements / doing garbage collection\n this.collectGarbage(decrementRefs);\n\n // checking for orphans (maybe removed in the future)\n if (!allowOrphanInputs) {\n for (const rd of resourceData) {\n if (!this.resources.has(rd.id)) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);\n }\n }\n }\n }\n\n /** Runs the refcount-decrement garbage-collection cascade over the given worklist.\n * A resource reaching refCount 0 that is not a root is removed, and its outgoing\n * references are decremented in turn, until the worklist drains. Factored out of\n * {@link updateFromResourceData} so {@link setRoots} can reuse the same cascade. */\n private collectGarbage(decrementRefs: SignedResourceId[]) {\n let currentRefs = decrementRefs;\n while (currentRefs.length > 0) {\n const nextRefs: SignedResourceId[] = [];\n for (const rid of currentRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount--;\n\n // garbage collection\n if (res.refCount === 0 && !this.roots.has(res.id)) {\n // removing fields\n res.fieldsMap.forEach((field) => {\n if (isNotNullSignedResourceId(field.value)) nextRefs.push(field.value);\n if (isNotNullSignedResourceId(field.error)) nextRefs.push(field.error);\n field.change.markChanged(\n `field ${field.name} removed during garbage collection of ${resourceIdToString(res.id)}`,\n );\n });\n if (isNotNullSignedResourceId(res.error)) nextRefs.push(res.error);\n res.resourceRemoved.markChanged(\n `resource removed during garbage collection: ${resourceIdToString(res.id)}`,\n );\n this.resources.delete(rid);\n }\n }\n currentRefs = nextRefs;\n }\n }\n\n /** Replaces the current root set with `newRoots`.\n *\n * Roots that leave the set lose their GC protection: each removed root present in the\n * heap is collected directly (a root sits at refCount 0, so it is never enqueued by an\n * ordinary decrement), and its subtree cascades to collection through the existing\n * refcount GC — the standard removal mechanic, applied to a root. Roots newly added to\n * the set are protected immediately; their resources arrive on the next refresh poll. */\n public setRoots(newRoots: Set<SignedResourceId>) {\n this.checkValid();\n\n const removed: SignedResourceId[] = [];\n for (const rid of this.roots) if (!newRoots.has(rid)) removed.push(rid);\n let added = false;\n for (const rid of newRoots) if (!this.roots.has(rid)) added = true;\n if (removed.length === 0 && !added) return; // no change\n\n // commit the new protected set before collecting, so a removed root is no longer\n // protected while its subtree cascades.\n this.roots.clear();\n for (const rid of newRoots) this.roots.add(rid);\n\n this.rootsChanged.markChanged(\"root set changed\");\n\n for (const rid of removed) {\n const res = this.resources.get(rid);\n if (res === undefined) continue; // root not yet materialized in the heap\n\n // if the (former) root is still referenced by another resource, dropping protection\n // is enough — ordinary refcounting keeps it alive and will collect it later.\n if (res.refCount > 0) continue;\n\n // collect the (now-unprotected) root itself and seed the cascade with the refs it holds\n const seed: SignedResourceId[] = [];\n res.fieldsMap.forEach((field) => {\n if (isNotNullSignedResourceId(field.value)) seed.push(field.value);\n if (isNotNullSignedResourceId(field.error)) seed.push(field.error);\n field.change.markChanged(\n `field ${field.name} removed after root ${resourceIdToString(res.id)} left the root set`,\n );\n });\n if (isNotNullSignedResourceId(res.error)) seed.push(res.error);\n res.resourceRemoved.markChanged(\n `resource removed after leaving the root set: ${resourceIdToString(res.id)}`,\n );\n this.resources.delete(rid);\n\n this.collectGarbage(seed);\n }\n }\n\n /** @deprecated use \"entry\" instead */\n public accessor(rid: SignedResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return this.entry(rid);\n }\n\n public entry(rid: SignedResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return new PlTreeEntry({ treeProvider: () => this }, rid);\n }\n\n public invalidateTree(msg?: string) {\n this._isValid = false;\n this.invalidationMessage = msg;\n this.rootsChanged.markChanged(\"tree invalidated\");\n this.resources.forEach((res) => {\n res.markAllChanged();\n });\n }\n\n public dumpState(): ExtendedResourceData[] {\n return Array.from(this.resources.values()).map((res) => res.extendedState);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"state.js","names":[],"sources":["../src/state.ts"],"sourcesContent":["import type {\n BasicResourceData,\n FieldData,\n FieldStatus,\n FieldType,\n KeyValue,\n OptionalSignedResourceId,\n ResourceData,\n SignedResourceId,\n ResourceKind,\n ResourceType,\n} from \"@milaboratories/pl-client\";\nimport {\n isNotNullSignedResourceId,\n isNullSignedResourceId,\n NullSignedResourceId,\n resourceIdToString,\n stringifyWithResourceId,\n} from \"@milaboratories/pl-client\";\nimport type { Watcher } from \"@milaboratories/computable\";\nimport { ChangeSource, KeyedChangeSource } from \"@milaboratories/computable\";\nimport { PlTreeEntry } from \"./accessors\";\nimport type { ValueAndError } from \"./value_and_error\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { cachedDecode, cachedDeserialize, notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { FieldTraversalStep, GetFieldStep } from \"./traversal_ops\";\nimport type { FinalResourceDataPredicate } from \"@milaboratories/pl-client\";\n\nexport type ExtendedResourceData = ResourceData & {\n kv: KeyValue[];\n};\n\nexport class TreeStateUpdateError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nclass PlTreeField implements FieldData {\n readonly change = new ChangeSource();\n\n constructor(\n public readonly name: string,\n public type: FieldType,\n public value: OptionalSignedResourceId,\n public error: OptionalSignedResourceId,\n public status: FieldStatus,\n public valueIsFinal: boolean,\n /** Last version of resource this field was observed, used to garbage collect fields in tree patching procedure */\n public resourceVersion: number,\n ) {}\n\n get state(): FieldData {\n return {\n name: this.name,\n type: this.type,\n status: this.status,\n value: this.value,\n error: this.error,\n valueIsFinal: this.valueIsFinal,\n };\n }\n}\n\nconst InitialResourceVersion = 0;\n\n/** Input and Service fields share one list and one lock: both are refused once inputs lock. */\nfunction isInputLike(type: FieldType): boolean {\n return type === \"Input\" || type === \"Service\";\n}\n\n/** The only field types the backend lets a client delete, so the only ones whose name can come\n * back under another type between two polls. */\nfunction isRecreatable(type: FieldType): boolean {\n return type === \"Dynamic\" || type === \"MTW\";\n}\n\nexport type ResourceDataWithFinalState = ResourceData & {\n finalState: boolean;\n};\n\n/** Cross-cycle re-fetch duplication counters, populated by {@link PlTreeState.updateFromResourceData}.\n * Separates genuinely new/changed resources from redundant re-fetches of unchanged ones\n * (the delta-skip opportunity: intra-cycle dedup already happens in the loader, so all waste here\n * is cross-cycle). */\nexport interface ResourceUpdateStat {\n /** Resources seen for the first time in this update. */\n resourcesNew: number;\n /** Resources already held whose state actually changed. */\n resourcesChanged: number;\n /** Resources already held that came back unchanged: a pure duplicate re-fetch. */\n resourcesUnchanged: number;\n /** data + KV bytes of the unchanged bucket: wasted downlink. */\n bytesUnchanged: number;\n /** Changed resources whose immutable metadata (type, kind, field set) did not change,\n * i.e. metadata re-streamed even though only a value or flag flipped. */\n metadataStableChanged: number;\n /** Per-resource fetches spent on unchanged resources (BFS only; streaming re-sends them\n * inside one stream, so this stays 0). */\n bfsRequestsWasted: number;\n /** Whether the current load used backend streaming; set by the loader, read here to\n * attribute {@link bfsRequestsWasted}. */\n usedStreaming: boolean;\n // Breakdown of what changed among resourcesChanged (a resource can bump several).\n /** New fields appeared on a held resource. */\n fieldsAdded: number;\n /** Dynamic fields disappeared from a held resource. */\n fieldsRemoved: number;\n /** Existing field value pointer changed (a new value/result was attached). */\n fieldsChanged: number;\n /** KV entries added, changed, or deleted. */\n kvChanged: number;\n /** resourceReady flipped. */\n readyFlips: number;\n /** inputsLocked or outputsLocked transitioned. */\n locksChanged: number;\n /** An error resource was attached. */\n errorsAttached: number;\n /** originalResourceId resolved (duplicate -> original). */\n duplicatesResolved: number;\n /** Held resources that transitioned to final this update. */\n resourcesMarkedFinal: number;\n}\n\n/** Never store instances of this class, always get fresh instance from {@link PlTreeState} */\nexport class PlTreeResource implements ResourceDataWithFinalState {\n /** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */\n refCount: number = 0;\n\n /** Increments each time resource is checked for difference with new state */\n version: number = InitialResourceVersion;\n /** Set to resource version when resource state, or it's fields have changed */\n dataVersion: number = InitialResourceVersion;\n\n readonly fieldsMap: Map<string, PlTreeField> = new Map();\n\n readonly kv = new Map<string, Uint8Array>();\n\n readonly resourceRemoved = new ChangeSource();\n\n // following change source are removed when resource is marked as final\n\n finalChanged? = new ChangeSource();\n\n resourceStateChange? = new ChangeSource();\n\n lockedChange? = new ChangeSource();\n inputAndServiceFieldListChanged? = new ChangeSource();\n outputFieldListChanged? = new ChangeSource();\n dynamicFieldListChanged? = new ChangeSource();\n\n // kvChangedGlobal? = new ChangeSource();\n kvChangedPerKey? = new KeyedChangeSource();\n\n readonly id: SignedResourceId;\n originalResourceId: OptionalSignedResourceId;\n\n readonly kind: ResourceKind;\n readonly type: ResourceType;\n\n readonly data?: Uint8Array;\n private dataAsString?: string;\n private dataAsJson?: unknown;\n\n error: OptionalSignedResourceId;\n\n inputsLocked: boolean;\n outputsLocked: boolean;\n resourceReady: boolean;\n finalFlag: boolean;\n\n /** Set externally by the tree, using {@link FinalResourceDataPredicate} */\n _finalState: boolean = false;\n\n private readonly logger?: MiLogger;\n\n constructor(initialState: BasicResourceData, logger?: MiLogger) {\n this.id = initialState.id;\n this.originalResourceId = initialState.originalResourceId;\n this.kind = initialState.kind;\n this.type = initialState.type;\n this.data = initialState.data;\n this.error = initialState.error;\n this.inputsLocked = initialState.inputsLocked;\n this.outputsLocked = initialState.outputsLocked;\n this.resourceReady = initialState.resourceReady;\n this.finalFlag = initialState.final;\n this.logger = logger;\n }\n\n // TODO add logging\n\n private info(msg: string) {\n if (this.logger !== undefined) this.logger.info(msg);\n }\n\n private warn(msg: string) {\n if (this.logger !== undefined) this.logger.warn(msg);\n }\n\n get final(): boolean {\n return this.finalFlag;\n }\n\n get finalState(): boolean {\n return this._finalState;\n }\n\n get fields(): FieldData[] {\n return [...this.fieldsMap.values()];\n }\n\n public getField(\n watcher: Watcher,\n _step:\n | (Omit<GetFieldStep, \"errorIfFieldNotFound\"> & { errorIfFieldNotFound: true })\n | (Omit<GetFieldStep, \"errorIfFieldNotSet\"> & { errorIfFieldNotSet: true }),\n onUnstable: (marker: string) => void,\n ): ValueAndError<SignedResourceId>;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void,\n ): ValueAndError<SignedResourceId> | undefined;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void = () => {},\n ): ValueAndError<SignedResourceId> | undefined {\n const step: FieldTraversalStep = typeof _step === \"string\" ? { field: _step } : _step;\n\n const field = this.fieldsMap.get(step.field);\n if (field === undefined) {\n if (step.errorIfFieldNotFound || step.errorIfFieldNotSet) {\n // Subscribed before throwing, so the error recomputes when the field appears.\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n throw new Error(\n `Field \"${step.field}\" not found in resource ${resourceIdToString(this.id)}`,\n );\n }\n\n // An asserted type's absence becomes permanent when its list locks, with no field added.\n if (\n step.assertFieldType !== undefined &&\n (isInputLike(step.assertFieldType)\n ? !this.inputsLocked\n : step.assertFieldType === \"Output\" && !this.outputsLocked)\n )\n this.lockedChange?.attachWatcher(watcher);\n\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === \"Service\" || step.assertFieldType === \"Input\") {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Service or input field not found ${step.field}.`);\n }\n\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === \"Output\") {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Output field not found ${step.field}.`);\n }\n\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n if (!this._finalState && !step.stableIfNotFound) onUnstable(\"field_not_found:\" + step.field);\n\n return undefined;\n } else {\n // Subscribed before the type check: a Dynamic or MTW field can come back under another\n // type, and the refused reader must then be re-run.\n field.change.attachWatcher(watcher);\n if (step.assertFieldType !== undefined && field.type !== step.assertFieldType)\n throw new Error(\n `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`,\n );\n\n const ret = {} as ValueAndError<SignedResourceId>;\n if (isNotNullSignedResourceId(field.value)) ret.value = field.value;\n if (isNotNullSignedResourceId(field.error)) ret.error = field.error;\n if (ret.value === undefined && ret.error === undefined && !this._finalState)\n // this method returns value and error of the field, thus those values are considered to be accessed;\n // any existing but not resolved field here is considered to be unstable, in the sense it is\n // considered to acquire some resolved value eventually; on a final resource it never will\n onUnstable(\"field_not_resolved:\" + step.field);\n return ret;\n }\n }\n\n public getInputsLocked(watcher: Watcher): boolean {\n if (!this.inputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.lockedChange?.attachWatcher(watcher);\n return this.inputsLocked;\n }\n\n public getOutputsLocked(watcher: Watcher): boolean {\n if (!this.outputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.lockedChange?.attachWatcher(watcher);\n return this.outputsLocked;\n }\n\n public get isReadyOrError(): boolean {\n return (\n this.error !== NullSignedResourceId ||\n this.resourceReady ||\n this.originalResourceId !== NullSignedResourceId\n );\n }\n\n public getIsFinal(watcher: Watcher): boolean {\n this.finalChanged?.attachWatcher(watcher);\n return this._finalState;\n }\n\n public getIsReadyOrError(watcher: Watcher): boolean {\n if (!this.isReadyOrError)\n // reverse transition can't happen, so there is no reason to wait for value to change if it is already true\n this.resourceStateChange?.attachWatcher(watcher);\n return this.isReadyOrError;\n }\n\n public getError(watcher: Watcher): SignedResourceId | undefined {\n if (isNullSignedResourceId(this.error)) {\n this.resourceStateChange?.attachWatcher(watcher);\n return undefined;\n } else {\n // reverse transition can't happen, so there is no reason to wait for value to change, if error already set\n return this.error;\n }\n }\n\n public listInputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === \"Input\" || field.type === \"Service\") ret.push(name);\n });\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public listOutputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === \"Output\") ret.push(name);\n });\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n /** Fields that are neither Input, Output nor Service. Service fields are input-like (the\n * backend refuses them once inputs are locked) and are listed by {@link listInputFields}. */\n public listDynamicFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (!isInputLike(field.type) && field.type !== \"Output\") ret.push(name);\n });\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public getKeyValue(watcher: Watcher, key: string): Uint8Array | undefined {\n this.kvChangedPerKey?.attachWatcher(key, watcher);\n return this.kv.get(key);\n }\n\n public getKeyValueString(watcher: Watcher, key: string): string | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDecode(bytes);\n }\n\n public getKeyValueAsJson<T = unknown>(watcher: Watcher, key: string): T | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDeserialize(bytes);\n }\n\n public getDataAsString(): string | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsString === undefined) this.dataAsString = cachedDecode(this.data);\n return this.dataAsString;\n }\n\n public getDataAsJson<T = unknown>(): T | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsJson === undefined) this.dataAsJson = cachedDeserialize(this.data);\n return this.dataAsJson as T;\n }\n\n verifyReadyState() {\n if (this.resourceReady && !this.inputsLocked)\n throw new Error(\n `ready without input or output lock: ${stringifyWithResourceId(this.basicState)}`,\n );\n }\n\n get basicState(): BasicResourceData {\n return {\n id: this.id,\n kind: this.kind,\n type: this.type,\n data: this.data,\n resourceReady: this.resourceReady,\n inputsLocked: this.inputsLocked,\n outputsLocked: this.outputsLocked,\n error: this.error,\n originalResourceId: this.originalResourceId,\n final: this.finalFlag,\n };\n }\n\n get extendedState(): ExtendedResourceData {\n return {\n ...this.basicState,\n fields: this.fields,\n kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value })),\n };\n }\n\n /** Called when {@link FinalResourceDataPredicate} returns true for the state.\n *\n * Every change source it retires is fired first: a reader attached to one of them read a\n * state that could still change, and is re-run to read it as final. */\n markFinal() {\n if (this._finalState) return;\n\n this._finalState = true;\n notEmpty(this.finalChanged).markChanged(\"marked final\");\n // Field sources are kept (a final resource's fields are still read), but a reader of an\n // unresolved one read it as unstable, and must be re-run to read it as permanent.\n this.fieldsMap.forEach((field) => field.change.markChanged(\"marked final\"));\n this.resourceStateChange?.markChanged(\"marked final\");\n this.lockedChange?.markChanged(\"marked final\");\n this.inputAndServiceFieldListChanged?.markChanged(\"marked final\");\n this.outputFieldListChanged?.markChanged(\"marked final\");\n this.dynamicFieldListChanged?.markChanged(\"marked final\");\n this.kvChangedPerKey?.markAllChanged(\"marked final\");\n this.finalChanged = undefined;\n this.resourceStateChange = undefined;\n this.dynamicFieldListChanged = undefined;\n this.inputAndServiceFieldListChanged = undefined;\n this.outputFieldListChanged = undefined;\n this.lockedChange = undefined;\n // this.kvChangedGlobal = undefined;\n this.kvChangedPerKey = undefined;\n }\n\n /** Used for invalidation */\n markAllChanged() {\n this.fieldsMap.forEach((field) => field.change.markChanged(\"marked all changed\"));\n this.finalChanged?.markChanged(\"marked all changed\");\n this.resourceStateChange?.markChanged(\"marked all changed\");\n this.lockedChange?.markChanged(\"marked all changed\");\n this.inputAndServiceFieldListChanged?.markChanged(\"marked all changed\");\n this.outputFieldListChanged?.markChanged(\"marked all changed\");\n this.dynamicFieldListChanged?.markChanged(\"marked all changed\");\n // this.kvChangedGlobal?.markChanged('marked all changed');\n this.kvChangedPerKey?.markAllChanged(\"marked all changed\");\n this.resourceRemoved.markChanged(\"marked all changed\");\n }\n}\n\nexport class PlTreeState {\n /** resource heap */\n private resources: Map<SignedResourceId, PlTreeResource> = new Map();\n private readonly resourcesAdded = new ChangeSource();\n /** Marked when the root set changes (roots discovered or removed), so multi-root\n * accessors that read the set recompute. */\n private readonly rootsChanged = new ChangeSource();\n /** Resets to false if any invalid state transitions are registered,\n * after that tree will produce errors for any read or write operations */\n private _isValid: boolean = true;\n private invalidationMessage?: string;\n\n /** The set of roots — resources protected from GC. Originally a single `root`,\n * generalized to a set so several explicit roots and dynamically-discovered shared\n * roots can coexist in one heap. Mutated through {@link setRoots}. */\n public readonly roots: Set<SignedResourceId>;\n\n constructor(\n /** Resources protected from GC. Accepts a single id (single-root, the original\n * contract) or a set of ids (multi-root). */\n roots: SignedResourceId | Set<SignedResourceId>,\n public readonly isFinalPredicate: FinalResourceDataPredicate,\n ) {\n this.roots = roots instanceof Set ? new Set(roots) : new Set([roots]);\n }\n\n /** Backward-compatible single-root accessor. Returns the sole root and THROWS if the\n * tree has zero or more than one root, so a multi-root tree can never be read as if it\n * had one. Mirrors the {@link entry} guard. */\n public get root(): SignedResourceId {\n if (this.roots.size !== 1)\n throw new Error(\n `single-root accessor used on a tree with ${this.roots.size} roots; use the multi-root API`,\n );\n return this.roots.values().next().value!;\n }\n\n public forEachResource(cb: (res: ResourceDataWithFinalState) => void): void {\n this.resources.forEach((v) => cb(v));\n }\n\n /** False once the tree has been invalidated (an inconsistent update, or termination of\n * its synchronization loop). {@link dumpState} deliberately reads through an invalid\n * tree, so anything persisting that dump must check this first: the contents of an\n * invalidated tree are not something to write to disk. */\n public get isValid(): boolean {\n return this._isValid;\n }\n\n private checkValid() {\n if (!this._isValid) throw new Error(this.invalidationMessage ?? \"tree is in invalid state\");\n }\n\n /** Reactive read of the current root set. Attaches the watcher to {@link rootsChanged}\n * so a Computable recomputes when discovered roots appear or disappear. */\n public getRoots(watcher: Watcher): SignedResourceId[] {\n this.checkValid();\n this.rootsChanged.attachWatcher(watcher);\n return [...this.roots];\n }\n\n public get(watcher: Watcher, rid: SignedResourceId): PlTreeResource {\n this.checkValid();\n const res = this.resources.get(rid);\n if (res === undefined) {\n // to make recovery from resource not found possible, considering some\n // race conditions, where computable is created before tree is updated\n this.resourcesAdded.attachWatcher(watcher);\n throw new Error(`resource ${resourceIdToString(rid)} not found in the tree`);\n }\n res.resourceRemoved.attachWatcher(watcher);\n return res;\n }\n\n /** Applies a batch of resource bodies to the mirror.\n *\n * Any error leaves the tree invalidated and surfaces as a {@link TreeStateUpdateError}: the\n * batch is applied resource by resource, so a throw part way through has already mutated the\n * mirror and fired watchers, and only a rebuild restores a state some poll confirmed. */\n updateFromResourceData(\n resourceData: ExtendedResourceData[],\n opts: { allowOrphanInputs?: boolean; stat?: ResourceUpdateStat } = {},\n ) {\n try {\n this.applyResourceData(resourceData, opts);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n if (this._isValid) this.invalidateTree(message);\n if (e instanceof TreeStateUpdateError) throw e;\n throw new TreeStateUpdateError(`tree update failed: ${message}`, { cause: e });\n }\n }\n\n private applyResourceData(\n resourceData: ExtendedResourceData[],\n opts: { allowOrphanInputs?: boolean; stat?: ResourceUpdateStat },\n ) {\n const { allowOrphanInputs = false, stat } = opts;\n this.checkValid();\n\n // All resources for which recount should be incremented, first are aggregated in this list\n const incrementRefs: SignedResourceId[] = [];\n const decrementRefs: SignedResourceId[] = [];\n\n // `resourcesAdded` is notified once, after the loop: markChanged detaches every watcher on\n // its first call and no watcher can attach in the middle of this synchronous loop, so a\n // single notification covers every resource added here. It also builds the marker string\n // just once, which is worth doing because resourceIdToString on a signed id parses a\n // BigInt and hex-decodes a Buffer.\n let addedCount = 0;\n let firstAdded: SignedResourceId | undefined;\n\n // Diagnostic context for unexpectedTransitionError, refreshed once per resource. It lives\n // out here so the loop body allocates neither a closure nor a snapshot per resource on the\n // path where nothing goes wrong. Only the mutable half of BasicResourceData needs\n // capturing: id, kind, type and data are readonly on PlTreeResource, so they are read back\n // from the resource when the message is built.\n let errRd: ExtendedResourceData;\n let errRes: PlTreeResource | undefined;\n let errOriginalResourceId: OptionalSignedResourceId = NullSignedResourceId;\n let errError: OptionalSignedResourceId = NullSignedResourceId;\n let errInputsLocked = false;\n let errOutputsLocked = false;\n let errResourceReady = false;\n let errFinal = false;\n const unexpectedTransitionError = (reason: string): never => {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { fields, ...rdWithoutFields } = errRd;\n const statBeforeMutation: BasicResourceData | undefined =\n errRes === undefined\n ? undefined\n : {\n id: errRes.id,\n kind: errRes.kind,\n type: errRes.type,\n data: errRes.data,\n resourceReady: errResourceReady,\n inputsLocked: errInputsLocked,\n outputsLocked: errOutputsLocked,\n error: errError,\n originalResourceId: errOriginalResourceId,\n final: errFinal,\n };\n this.invalidateTree();\n throw new TreeStateUpdateError(\n `Unexpected resource state transition (${reason}): ${stringifyWithResourceId(\n rdWithoutFields,\n )} -> ${stringifyWithResourceId(statBeforeMutation)}`,\n );\n };\n\n // patching / creating resources\n for (const rd of resourceData) {\n let resource = this.resources.get(rd.id);\n const held = resource !== undefined;\n let changed = false;\n // Structural/metadata change (a field added, removed or retyped). type and kind are readonly, so\n // they never change; this flag isolates value/flag-only changes from real metadata churn.\n let metadataChanged = false;\n\n errRd = rd;\n errRes = resource;\n\n if (resource !== undefined) {\n // updating existing resource\n errOriginalResourceId = resource.originalResourceId;\n errError = resource.error;\n errInputsLocked = resource.inputsLocked;\n errOutputsLocked = resource.outputsLocked;\n errResourceReady = resource.resourceReady;\n errFinal = resource.finalFlag;\n\n if (resource.finalState)\n unexpectedTransitionError(\"resource state can\\t be updated after it is marked as final\");\n\n // updating resource version, even if it was not changed\n resource.version += 1;\n\n // duplicate / original\n if (resource.originalResourceId !== rd.originalResourceId) {\n if (resource.originalResourceId !== NullSignedResourceId)\n unexpectedTransitionError(\"originalResourceId can't change after it is set\");\n resource.originalResourceId = rd.originalResourceId;\n // duplicate status of the resource counts as ready for the external observer\n notEmpty(resource.resourceStateChange).markChanged(\n `originalResourceId changed for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.duplicatesResolved++;\n }\n\n // error\n if (isNotNullSignedResourceId(rd.error) && resource.error !== rd.error) {\n if (isNotNullSignedResourceId(resource.error))\n unexpectedTransitionError(\"resource can't change attached error after it is set\");\n resource.error = rd.error;\n incrementRefs.push(resource.error);\n notEmpty(resource.resourceStateChange).markChanged(\n `error changed for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.errorsAttached++;\n }\n\n // updating fields.\n //\n // Fields normally arrive in the order they are already stored, so the stored ones are\n // walked in lockstep with the incoming ones and matched by direct name comparison\n // rather than through a fieldsMap lookup: every field name in a poll response is\n // freshly decoded from protobuf, so V8 has no cached hash for any of them.\n //\n // Any divergence (reordering, an insertion, a removal) abandons the walk, and the rest\n // of the resource resolves through fieldsMap.get, which is always correct. The walk is\n // abandoned before any fieldsMap.set, so it never observes an insertion made through\n // the live iterator.\n let walk: MapIterator<PlTreeField> | undefined =\n rd.fields.length > 0 ? resource.fieldsMap.values() : undefined;\n for (const fd of rd.fields) {\n let field: PlTreeField | undefined;\n if (walk !== undefined) {\n const next = walk.next();\n if (next.done === true || next.value.name !== fd.name) walk = undefined;\n else field = next.value;\n }\n if (field === undefined) field = resource.fieldsMap.get(fd.name);\n\n if (!field) {\n // new field\n\n field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n resource.version,\n );\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n\n if (isInputLike(fd.type)) {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while inputs locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n } else if (fd.type === \"Output\") {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while outputs locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n } else {\n notEmpty(resource.dynamicFieldListChanged).markChanged(\n `new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`,\n );\n }\n\n resource.fieldsMap.set(fd.name, field);\n\n changed = true;\n metadataChanged = true;\n if (stat) stat.fieldsAdded++;\n } else {\n // change of old field\n\n // A Dynamic or MTW field deleted and recreated under the same name between two\n // polls looks like a type change. Any other type is append-only on the backend.\n if (field.type !== fd.type) {\n if (!isRecreatable(field.type))\n unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);\n const reason = `field ${fd.name} changed type ${field.type} -> ${fd.type} in ${resourceIdToString(resource.id)}`;\n // the old list: both recreatable types are listed as dynamic\n notEmpty(resource.dynamicFieldListChanged).markChanged(reason);\n if (isInputLike(fd.type)) {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} field \"${fd.name}\", while inputs are locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(reason);\n } else if (fd.type === \"Output\") {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding output field \"${fd.name}\", while outputs are locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(reason);\n }\n field.type = fd.type;\n field.change.markChanged(reason);\n changed = true;\n metadataChanged = true;\n }\n\n // field value\n if (field.value !== fd.value) {\n if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);\n field.value = fd.value;\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n field.change.markChanged(\n `field ${fd.name} value changed in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.fieldsChanged++;\n }\n\n // field error\n if (field.error !== fd.error) {\n if (isNotNullSignedResourceId(field.error)) decrementRefs.push(field.error);\n field.error = fd.error;\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n field.change.markChanged(\n `field ${fd.name} error changed in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n // field status\n if (field.status !== fd.status) {\n field.status = fd.status;\n field.change.markChanged(\n `field ${fd.name} status changed to ${fd.status} in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n // field valueIsFinal flag\n if (field.valueIsFinal !== fd.valueIsFinal) {\n field.valueIsFinal = fd.valueIsFinal;\n field.change.markChanged(\n `field ${fd.name} valueIsFinal changed to ${fd.valueIsFinal} in ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n }\n\n field.resourceVersion = resource.version;\n }\n }\n\n // detecting removed fields\n resource.fieldsMap.forEach((field, fieldName, fields) => {\n if (field.resourceVersion !== resource!.version) {\n if (field.type === \"Input\" || field.type === \"Service\" || field.type === \"Output\")\n unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);\n field.change.markChanged(\n `dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`,\n );\n fields.delete(fieldName);\n changed = true;\n metadataChanged = true;\n if (stat) stat.fieldsRemoved++;\n\n if (isNotNullSignedResourceId(field.value)) decrementRefs.push(field.value);\n if (isNotNullSignedResourceId(field.error)) decrementRefs.push(field.error);\n\n notEmpty(resource!.dynamicFieldListChanged).markChanged(\n `dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`,\n );\n }\n });\n\n // inputsLocked\n if (resource.inputsLocked !== rd.inputsLocked) {\n if (resource.inputsLocked) unexpectedTransitionError(\"inputs unlocking is not permitted\");\n resource.inputsLocked = rd.inputsLocked;\n notEmpty(resource.lockedChange).markChanged(\n `inputs locked for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.locksChanged++;\n }\n\n // outputsLocked\n if (resource.outputsLocked !== rd.outputsLocked) {\n if (resource.outputsLocked)\n unexpectedTransitionError(\"outputs unlocking is not permitted\");\n resource.outputsLocked = rd.outputsLocked;\n notEmpty(resource.lockedChange).markChanged(\n `outputs locked for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.locksChanged++;\n }\n\n // ready flag\n if (resource.resourceReady !== rd.resourceReady) {\n const readyStateBefore = resource.resourceReady;\n resource.resourceReady = rd.resourceReady;\n resource.verifyReadyState();\n if (!resource.isReadyOrError)\n unexpectedTransitionError(\n `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`,\n );\n notEmpty(resource.resourceStateChange).markChanged(\n `ready flag changed to ${rd.resourceReady} for ${resourceIdToString(resource.id)}`,\n );\n changed = true;\n if (stat) stat.readyFlips++;\n }\n\n // backend final flag: informational, no reader watches it\n if (resource.finalFlag !== rd.final) {\n resource.finalFlag = rd.final;\n changed = true;\n }\n\n // syncing kv. Same lockstep walk as the fields above, for the same reason: kv keys\n // arrive in a stable order and are freshly decoded strings. The walk is only set up\n // when there is something to compare against, so a resource with no kv allocates no\n // iterator.\n let kvWalk: MapIterator<[string, Uint8Array]> | undefined =\n rd.kv.length > 0 ? resource.kv.entries() : undefined;\n for (const kv of rd.kv) {\n let current: Uint8Array | undefined;\n if (kvWalk !== undefined) {\n const next = kvWalk.next();\n if (next.done === true || next.value[0] !== kv.key) kvWalk = undefined;\n else current = next.value[1];\n }\n if (current === undefined) current = resource.kv.get(kv.key);\n if (current === undefined) {\n resource.kv.set(kv.key, kv.value);\n kvWalk = undefined;\n notEmpty(resource.kvChangedPerKey).markChanged(\n kv.key,\n `kv added for ${resourceIdToString(resource.id)}: ${kv.key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n } else if (Buffer.compare(current, kv.value) !== 0) {\n resource.kv.set(kv.key, kv.value);\n notEmpty(resource.kvChangedPerKey).markChanged(\n kv.key,\n `kv changed for ${resourceIdToString(resource.id)}: ${kv.key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n }\n }\n\n if (resource.kv.size > rd.kv.length) {\n // only it this case it makes sense to check for deletions\n const newStateKeys = new Set(rd.kv.map((kv) => kv.key));\n\n // deleting keys not present in resource anymore\n resource.kv.forEach((_value, key, map) => {\n if (!newStateKeys.has(key)) {\n map.delete(key);\n notEmpty(resource!.kvChangedPerKey).markChanged(\n key,\n `kv deleted for ${resourceIdToString(resource!.id)}: ${key}`,\n );\n changed = true;\n if (stat) stat.kvChanged++;\n }\n });\n }\n\n if (changed) {\n // if resource was changed, updating resource data version\n resource.dataVersion = resource.version;\n if (this.isFinalPredicate(resource)) {\n resource.markFinal();\n if (stat) stat.resourcesMarkedFinal++;\n }\n }\n } else {\n // creating new resource\n\n resource = new PlTreeResource(rd);\n resource.verifyReadyState();\n if (isNotNullSignedResourceId(resource.error)) incrementRefs.push(resource.error);\n for (const fd of rd.fields) {\n const field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n InitialResourceVersion,\n );\n if (isNotNullSignedResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullSignedResourceId(fd.error)) incrementRefs.push(fd.error);\n resource.fieldsMap.set(fd.name, field);\n }\n\n // adding kv\n for (const kv of rd.kv) resource.kv.set(kv.key, kv.value);\n\n // checking that resource is final, and if so, marking it\n if (this.isFinalPredicate(resource)) resource.markFinal();\n\n // adding the resource to the heap\n this.resources.set(resource.id, resource);\n if (addedCount === 0) firstAdded = resource.id;\n addedCount++;\n }\n\n if (stat) {\n if (!held) stat.resourcesNew++;\n else if (changed) {\n stat.resourcesChanged++;\n if (!metadataChanged) stat.metadataStableChanged++;\n } else {\n stat.resourcesUnchanged++;\n stat.bytesUnchanged += rd.data?.length ?? 0;\n for (const kv of rd.kv) stat.bytesUnchanged += kv.value.length;\n if (!stat.usedStreaming) stat.bfsRequestsWasted++;\n }\n }\n }\n\n if (firstAdded !== undefined)\n this.resourcesAdded.markChanged(\n addedCount === 1\n ? `new resource ${resourceIdToString(firstAdded)} added`\n : `${addedCount} new resources added, first ${resourceIdToString(firstAdded)}`,\n );\n\n // applying refCount increments\n for (const rid of incrementRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount++;\n }\n\n // recursively applying refCount decrements / doing garbage collection\n this.collectGarbage(decrementRefs);\n\n // checking for orphans (maybe removed in the future)\n if (!allowOrphanInputs) {\n for (const rd of resourceData) {\n if (!this.resources.has(rd.id)) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);\n }\n }\n }\n }\n\n /** Runs the refcount-decrement garbage-collection cascade over the given worklist.\n * A resource reaching refCount 0 that is not a root is removed, and its outgoing\n * references are decremented in turn, until the worklist drains. Factored out of\n * {@link updateFromResourceData} so {@link setRoots} can reuse the same cascade. */\n private collectGarbage(decrementRefs: SignedResourceId[]) {\n let currentRefs = decrementRefs;\n while (currentRefs.length > 0) {\n const nextRefs: SignedResourceId[] = [];\n for (const rid of currentRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount--;\n\n // garbage collection\n if (res.refCount === 0 && !this.roots.has(res.id)) {\n // removing fields\n res.fieldsMap.forEach((field) => {\n if (isNotNullSignedResourceId(field.value)) nextRefs.push(field.value);\n if (isNotNullSignedResourceId(field.error)) nextRefs.push(field.error);\n field.change.markChanged(\n `field ${field.name} removed during garbage collection of ${resourceIdToString(res.id)}`,\n );\n });\n if (isNotNullSignedResourceId(res.error)) nextRefs.push(res.error);\n res.resourceRemoved.markChanged(\n `resource removed during garbage collection: ${resourceIdToString(res.id)}`,\n );\n this.resources.delete(rid);\n }\n }\n currentRefs = nextRefs;\n }\n }\n\n /** Replaces the current root set with `newRoots`.\n *\n * Roots that leave the set lose their GC protection: each removed root present in the\n * heap is collected directly (a root sits at refCount 0, so it is never enqueued by an\n * ordinary decrement), and its subtree cascades to collection through the existing\n * refcount GC — the standard removal mechanic, applied to a root. Roots newly added to\n * the set are protected immediately; their resources arrive on the next refresh poll. */\n public setRoots(newRoots: Set<SignedResourceId>) {\n this.checkValid();\n\n const removed: SignedResourceId[] = [];\n for (const rid of this.roots) if (!newRoots.has(rid)) removed.push(rid);\n let added = false;\n for (const rid of newRoots) if (!this.roots.has(rid)) added = true;\n if (removed.length === 0 && !added) return; // no change\n\n // commit the new protected set before collecting, so a removed root is no longer\n // protected while its subtree cascades.\n this.roots.clear();\n for (const rid of newRoots) this.roots.add(rid);\n\n this.rootsChanged.markChanged(\"root set changed\");\n\n for (const rid of removed) {\n const res = this.resources.get(rid);\n if (res === undefined) continue; // root not yet materialized in the heap\n\n // if the (former) root is still referenced by another resource, dropping protection\n // is enough — ordinary refcounting keeps it alive and will collect it later.\n if (res.refCount > 0) continue;\n\n this.removeUnreferenced(res, `root ${resourceIdToString(res.id)} left the root set`);\n }\n }\n\n /** Roots held in the heap and not final: the ones whose existence a poll must check, since\n * a walk seeded at a deleted resource returns nothing rather than an error. A final root is\n * never re-read, so it is not checked either. */\n public nonFinalRoots(): SignedResourceId[] {\n const ret: SignedResourceId[] = [];\n for (const rid of this.roots) {\n const res = this.resources.get(rid);\n if (res !== undefined && !res.finalState) ret.push(rid);\n }\n return ret;\n }\n\n /** Drops from the heap a root the backend has deleted, with the subtree only it held, and\n * notifies its readers. The id stays in the root set: a resource id is never reused, so the\n * root stays absent and readers see \"not found\". Returns false, changing nothing, if the\n * root is not held or is still referenced from elsewhere in the heap; a root is protected\n * from the refcount GC, so it is retried once its referrers are gone (the next call, or the\n * caller's next pass). */\n public dropDeletedRoot(rid: SignedResourceId): boolean {\n this.checkValid();\n const res = this.resources.get(rid);\n if (res === undefined || res.refCount > 0) return false;\n this.removeUnreferenced(res, `root ${resourceIdToString(rid)} deleted on the backend`);\n return true;\n }\n\n /** Removes a resource nothing in the heap references and cascades the refcount GC into\n * what it referenced. */\n private removeUnreferenced(res: PlTreeResource, reason: string) {\n const seed: SignedResourceId[] = [];\n res.fieldsMap.forEach((field) => {\n if (isNotNullSignedResourceId(field.value)) seed.push(field.value);\n if (isNotNullSignedResourceId(field.error)) seed.push(field.error);\n field.change.markChanged(`field ${field.name} removed: ${reason}`);\n });\n if (isNotNullSignedResourceId(res.error)) seed.push(res.error);\n res.resourceRemoved.markChanged(`resource removed: ${reason}`);\n this.resources.delete(res.id);\n\n this.collectGarbage(seed);\n }\n\n /** @deprecated use \"entry\" instead */\n public accessor(rid: SignedResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return this.entry(rid);\n }\n\n public entry(rid: SignedResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return new PlTreeEntry({ treeProvider: () => this }, rid);\n }\n\n public invalidateTree(msg?: string) {\n this._isValid = false;\n this.invalidationMessage = msg;\n this.rootsChanged.markChanged(\"tree invalidated\");\n // readers whose last run found no resource wait on this, and must learn the tree is gone\n this.resourcesAdded.markChanged(\"tree invalidated\");\n this.resources.forEach((res) => {\n res.markAllChanged();\n });\n }\n\n public dumpState(): ExtendedResourceData[] {\n return Array.from(this.resources.values()).map((res) => res.extendedState);\n }\n}\n"],"mappings":"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"}
package/dist/sync.cjs CHANGED
@@ -73,6 +73,7 @@ function initialTreeLoadingStat() {
73
73
  deltaSeedsSent: 0,
74
74
  deltaResolutionRounds: 0,
75
75
  deltaSuspectedFullAnswers: 0,
76
+ rootsDropped: 0,
76
77
  resourcesNew: 0,
77
78
  resourcesChanged: 0,
78
79
  resourcesUnchanged: 0,
@@ -221,7 +222,7 @@ async function processResourceTreeStream(treeItems, finalResources, pruningFunct
221
222
  resourceReady: frame.resourceReady,
222
223
  error: frame.error,
223
224
  originalResourceId: frame.originalResourceId,
224
- final: frame.final || frame.traverseWasStopped,
225
+ final: frame.final,
225
226
  inputsLocked: frame.inputsLocked,
226
227
  outputsLocked: frame.outputsLocked,
227
228
  fields: frame.fields,