@jbrowse/mobx-state-tree 6.6.1 → 6.7.0

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.
@@ -466,7 +466,7 @@ function resolveIdentifier(type, target, identifier) {
466
466
  assertIsType(type, 1);
467
467
  assertIsStateTreeNode(target, 2);
468
468
  assertIsValidIdentifier(identifier, 3);
469
- const node = getStateTreeNode(target).root.identifierCache.resolve(type, normalizeIdentifier(identifier));
469
+ const node = getStateTreeNode(target).root.identifierCache?.resolve(type, normalizeIdentifier(identifier));
470
470
  return node?.value;
471
471
  }
472
472
  /**
@@ -1209,7 +1209,7 @@ class ObjectNode extends BaseNode {
1209
1209
  // addNodeToCache no-ops without an identifier attribute, and reaching the
1210
1210
  // root to ask it is a walk up the whole parent chain
1211
1211
  if (this.identifierAttribute) {
1212
- const cache = parent ? parent.root.identifierCache : this.identifierCache;
1212
+ const cache = parent ? parent.assertAttachable() : this.identifierCache;
1213
1213
  cache.addNodeToCache(this);
1214
1214
  }
1215
1215
  }
@@ -1220,8 +1220,11 @@ class ObjectNode extends BaseNode {
1220
1220
  }
1221
1221
  }
1222
1222
  createObservableInstance(fireHooks = true) {
1223
+ const diedUncreated = this.state === NodeLifeCycle.DEAD &&
1224
+ this._observableInstanceState ===
1225
+ ObservableInstanceLifecycle.UNINITIALIZED;
1223
1226
  if (devMode()) {
1224
- if (this.state !== NodeLifeCycle.INITIALIZING) {
1227
+ if (!diedUncreated && this.state !== NodeLifeCycle.INITIALIZING) {
1225
1228
  // istanbul ignore next
1226
1229
  throw fail("assertion failed: the creation of the observable instance must be done on the initializing phase");
1227
1230
  }
@@ -1274,6 +1277,9 @@ class ObjectNode extends BaseNode {
1274
1277
  // subscribes to never pays for a full-tree snapshot at creation. keepAlive
1275
1278
  // on _snapshotComputed preserves snapshot referential stability without it.
1276
1279
  this._childNodes = EMPTY_OBJECT;
1280
+ if (diedUncreated) {
1281
+ return;
1282
+ }
1277
1283
  this.state = NodeLifeCycle.CREATED;
1278
1284
  if (fireHooks) {
1279
1285
  this.fireHook(Hook.afterCreate);
@@ -1328,6 +1334,19 @@ class ObjectNode extends BaseNode {
1328
1334
  child.notifyDetachOf(subject);
1329
1335
  }
1330
1336
  }
1337
+ /**
1338
+ * @internal
1339
+ * @hidden
1340
+ * A node that died inside a tree is its own root and has no identifier
1341
+ * cache, so attaching it, or attaching anything to it, cannot work.
1342
+ */
1343
+ assertAttachable() {
1344
+ const cache = this.root.identifierCache;
1345
+ if (!cache) {
1346
+ throw fail(`'${this}' is no longer part of a state tree, so it cannot be attached to one or have nodes attached to it.`);
1347
+ }
1348
+ return cache;
1349
+ }
1331
1350
  setParent(newParent, subpath) {
1332
1351
  const parentChanged = newParent !== this.parent;
1333
1352
  const subpathChanged = subpath !== this.subpath;
@@ -1356,9 +1375,9 @@ class ObjectNode extends BaseNode {
1356
1375
  }
1357
1376
  }
1358
1377
  if (parentChanged) {
1359
- // attach to new parent
1378
+ this.assertAttachable();
1360
1379
  this.environment = undefined; // will use root's
1361
- newParent.root.identifierCache.mergeCache(this);
1380
+ newParent.assertAttachable().mergeCache(this);
1362
1381
  this.baseSetParent(newParent, subpath);
1363
1382
  this.fireHook(Hook.afterAttach);
1364
1383
  }
@@ -1554,13 +1573,33 @@ class ObjectNode extends BaseNode {
1554
1573
  addHiddenFinalProp(this.storedValue, "toJSON", toJSON);
1555
1574
  }
1556
1575
  aboutToDie() {
1576
+ const wasUninitialized = this._observableInstanceState ===
1577
+ ObservableInstanceLifecycle.UNINITIALIZED;
1578
+ const children = this.getChildren();
1579
+ if (wasUninitialized &&
1580
+ (this.hasSnapshotPostProcessor ||
1581
+ children.some(child => child instanceof ObjectNode &&
1582
+ child._observableInstanceState !==
1583
+ ObservableInstanceLifecycle.UNINITIALIZED))) {
1584
+ this.createObservableInstanceIfNeeded();
1585
+ }
1586
+ for (const node of children) {
1587
+ if (node.isAlive) {
1588
+ node.aboutToDie();
1589
+ }
1590
+ }
1557
1591
  if (this._observableInstanceState ===
1558
1592
  ObservableInstanceLifecycle.UNINITIALIZED) {
1559
1593
  return;
1560
1594
  }
1561
- this.getChildren().forEach(node => {
1562
- node.aboutToDie();
1563
- });
1595
+ if (wasUninitialized) {
1596
+ const walked = new Set(children);
1597
+ for (const node of this.getChildren()) {
1598
+ if (node.isAlive && !walked.has(node)) {
1599
+ node.aboutToDie();
1600
+ }
1601
+ }
1602
+ }
1564
1603
  // beforeDestroy should run before the disposers since else we could end up in a situation where
1565
1604
  // a disposer added with addDisposer at this stage (beforeDestroy) is actually never released
1566
1605
  this.baseAboutToDie();
@@ -1857,12 +1896,16 @@ class ComplexType extends BaseType {
1857
1896
  current.setParent(parent, subpath);
1858
1897
  return current;
1859
1898
  }
1899
+ const newNode = getStateTreeNodeSafe(newValue);
1900
+ if (newNode) {
1901
+ newNode.assertAttachable();
1902
+ if (!current.isDetaching && newNode.isWithin(current)) {
1903
+ throw fail(`Cannot assign ${newNode} to '${parent.path}/${subpath}': it is part of ${current}, which it would replace.`);
1904
+ }
1905
+ }
1860
1906
  // current node cannot be recycled in any way
1861
1907
  current.die(); // noop if detaching
1862
- // attempt to reuse the new one
1863
- if (isStateTreeNode(newValue) && this.isAssignableFrom(getType(newValue))) {
1864
- // newValue is a Node as well, move it here..
1865
- const newNode = getStateTreeNode(newValue);
1908
+ if (newNode && this.isAssignableFrom(newNode.type)) {
1866
1909
  newNode.setParent(parent, subpath);
1867
1910
  return newNode;
1868
1911
  }