@jbrowse/mobx-state-tree 6.5.1 → 6.6.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.
@@ -1,4 +1,4 @@
1
- import { isComputedProp, isObservableProp, createAtom, action, computed, _allowStateChangesInsideComputed, reaction, runInAction, observable, values, entries, isObservableArray, isObservableObject, defineProperty as defineProperty$1, _interceptReads, intercept, observe, ObservableMap, _getAdministration, set, makeObservable, getAtom, when } from 'mobx';
1
+ import { isComputedProp, isObservableProp, action, createAtom, computed, _allowStateChangesInsideComputed, reaction, runInAction, observable, values, entries, isObservableArray, isObservableObject, defineProperty as defineProperty$1, _interceptReads, intercept, observe, ObservableMap, _getAdministration, set, makeObservable, getAtom, when } from 'mobx';
2
2
 
3
3
  let livelinessChecking = "warn";
4
4
  /**
@@ -565,7 +565,9 @@ function getRelativePath(base, target) {
565
565
  }
566
566
  /**
567
567
  * Returns a deep copy of the given state tree node as new tree.
568
- * Shorthand for `snapshot(x) = getType(x).create(getSnapshot(x))`
568
+ * Like `getType(x).create(getSnapshot(x))`, except that a node wrapped in a
569
+ * `types.snapshotProcessor` is recreated through the processor, which
570
+ * `getType(x)` (the wrapped type) would skip.
569
571
  *
570
572
  * _Tip: clone will create a literal copy, including the same identifiers. To modify identifiers etc. during cloning, don't use clone but take a snapshot of the tree, modify it, and create new instance_
571
573
  *
@@ -577,11 +579,11 @@ function clone(source, keepEnvironment = true) {
577
579
  // check all arguments
578
580
  assertIsStateTreeNode(source, 1);
579
581
  const node = getStateTreeNode(source);
580
- return node.type.create(node.snapshot, keepEnvironment === true
582
+ return (node.snapshotProcessorType ?? node.type).create(node.snapshot, keepEnvironment === true
581
583
  ? node.root.environment
582
584
  : keepEnvironment === false
583
585
  ? undefined
584
- : keepEnvironment); // it's an object or something else
586
+ : keepEnvironment);
585
587
  }
586
588
  /**
587
589
  * Removes a model element from the state tree, and let it live on as a new state tree
@@ -700,17 +702,15 @@ function walk(target, processor) {
700
702
  * @returns
701
703
  */
702
704
  function getPropertyMembers(typeOrNode) {
703
- let type;
704
- if (isStateTreeNode(typeOrNode)) {
705
- type = getType(typeOrNode);
706
- }
707
- else {
708
- type = typeOrNode;
709
- }
710
- assertArg(type, t => isModelType(t), "model type or model instance", 1);
705
+ const type = isStateTreeNode(typeOrNode)
706
+ ? getType(typeOrNode)
707
+ : typeOrNode;
708
+ assertArg(type, isModelType, "model type or model instance", 1);
709
+ // a wrapped model reports the model's properties; a union of models has no
710
+ // single set and reports none
711
711
  return {
712
712
  name: type.name,
713
- properties: { ...type.properties }
713
+ properties: { ...asModelType(type)?.properties }
714
714
  };
715
715
  }
716
716
  /**
@@ -912,16 +912,28 @@ class BaseNode {
912
912
  }
913
913
  }
914
914
  _hookSubscribers;
915
- fireInternalHook(name) {
916
- if (this._hookSubscribers) {
917
- this._hookSubscribers.emit(name, this, name);
915
+ fireInternalHook(name, subject = this) {
916
+ this._hookSubscribers?.emit(name, subject, name);
917
+ }
918
+ /**
919
+ * Detaching a node takes its whole subtree out of the tree, but only the
920
+ * detached node's parent changes, so only it fires `beforeDetach` for real.
921
+ * Internal subscribers below it (reference watchers on a target) still need
922
+ * to know their node is leaving.
923
+ */
924
+ notifyDetachOf(subject) {
925
+ this.fireInternalHook(Hook.beforeDetach, subject);
926
+ }
927
+ isWithin(ancestor) {
928
+ for (let node = this.parent; node; node = node.parent) {
929
+ if (node === ancestor) {
930
+ return true;
931
+ }
918
932
  }
933
+ return false;
919
934
  }
920
935
  registerHook(hook, hookHandler) {
921
- if (!this._hookSubscribers) {
922
- this._hookSubscribers = new EventHandlers();
923
- }
924
- return this._hookSubscribers.register(hook, hookHandler);
936
+ return (this._hookSubscribers ??= new EventHandlers()).register(hook, hookHandler);
925
937
  }
926
938
  _parent;
927
939
  get parent() {
@@ -982,6 +994,13 @@ class BaseNode {
982
994
  this.aliveAtom.reportObserved();
983
995
  return this.isAlive;
984
996
  }
997
+ die() {
998
+ if (!this.isAlive || this.isDetaching) {
999
+ return;
1000
+ }
1001
+ this.aboutToDie();
1002
+ this.finalizeDeath();
1003
+ }
985
1004
  baseFinalizeCreation(whenFinalized) {
986
1005
  if (devMode()) {
987
1006
  if (!this.isAlive) {
@@ -1017,6 +1036,7 @@ class BaseNode {
1017
1036
  this.fireHook(Hook.beforeDestroy);
1018
1037
  }
1019
1038
  }
1039
+ BaseNode.prototype.die = action(BaseNode.prototype.die);
1020
1040
 
1021
1041
  /**
1022
1042
  * @internal
@@ -1034,9 +1054,6 @@ class ScalarNode extends BaseNode {
1034
1054
  throw e;
1035
1055
  }
1036
1056
  this.state = NodeLifeCycle.CREATED;
1037
- // for scalar nodes there's no point in firing this event since it would fire on the constructor, before
1038
- // anybody can actually register for/listen to it
1039
- // this.fireHook(Hook.AfterCreate)
1040
1057
  this.finalizeCreation();
1041
1058
  }
1042
1059
  get root() {
@@ -1079,13 +1096,6 @@ class ScalarNode extends BaseNode {
1079
1096
  const path = (this.isAlive ? this.path : this.pathUponDeath) || "<root>";
1080
1097
  return `${this.type.name}@${path}${this.isAlive ? "" : " [dead]"}`;
1081
1098
  }
1082
- die() {
1083
- if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) {
1084
- return;
1085
- }
1086
- this.aboutToDie();
1087
- this.finalizeDeath();
1088
- }
1089
1099
  finalizeCreation() {
1090
1100
  this.baseFinalizeCreation();
1091
1101
  }
@@ -1099,7 +1109,6 @@ class ScalarNode extends BaseNode {
1099
1109
  this.fireInternalHook(name);
1100
1110
  }
1101
1111
  }
1102
- ScalarNode.prototype.die = action(ScalarNode.prototype.die);
1103
1112
 
1104
1113
  let nextNodeId = 1;
1105
1114
  var ObservableInstanceLifecycle;
@@ -1287,8 +1296,8 @@ class ObjectNode extends BaseNode {
1287
1296
  if (!this.parent) {
1288
1297
  return;
1289
1298
  }
1290
- // detach if attached
1291
1299
  this.fireHook(Hook.beforeDetach);
1300
+ this.notifyChildrenOfDetach(this);
1292
1301
  const previousState = this.state;
1293
1302
  this.state = NodeLifeCycle.DETACHING;
1294
1303
  const root = this.root;
@@ -1304,6 +1313,21 @@ class ObjectNode extends BaseNode {
1304
1313
  this.state = previousState;
1305
1314
  }
1306
1315
  }
1316
+ notifyDetachOf(subject) {
1317
+ super.notifyDetachOf(subject);
1318
+ this.notifyChildrenOfDetach(subject);
1319
+ }
1320
+ notifyChildrenOfDetach(subject) {
1321
+ // a handler notified earlier in the walk may have destroyed this node.
1322
+ // A node never read still has to be walked: an instance created elsewhere
1323
+ // can have been moved under it, subscribers and all.
1324
+ if (!this.isAlive) {
1325
+ return;
1326
+ }
1327
+ for (const child of this.getChildren()) {
1328
+ child.notifyDetachOf(subject);
1329
+ }
1330
+ }
1307
1331
  setParent(newParent, subpath) {
1308
1332
  const parentChanged = newParent !== this.parent;
1309
1333
  const subpathChanged = subpath !== this.subpath;
@@ -1508,37 +1532,27 @@ class ObjectNode extends BaseNode {
1508
1532
  this.clearParent();
1509
1533
  }
1510
1534
  preboot() {
1511
- // eslint-disable-next-line @typescript-eslint/no-this-alias
1512
- const self = this;
1513
1535
  this._applyPatches = createActionInvoker(this.storedValue, "@APPLY_PATCHES", (patches) => {
1514
1536
  patches.forEach(patch => {
1515
1537
  if (!patch.path) {
1516
- self.type.applySnapshot(self, patch.value);
1538
+ this.type.applySnapshot(this, patch.value);
1517
1539
  return;
1518
1540
  }
1519
1541
  const parts = splitJsonPath(patch.path);
1520
- const node = resolveNodeByPathParts(self, parts.slice(0, -1));
1542
+ const node = resolveNodeByPathParts(this, parts.slice(0, -1));
1521
1543
  node.applyPatchLocally(parts[parts.length - 1], patch);
1522
1544
  });
1523
1545
  });
1524
1546
  this._applySnapshot = createActionInvoker(this.storedValue, "@APPLY_SNAPSHOT", (snapshot) => {
1525
- // if the snapshot is the same as the current one, avoid performing a reconcile
1526
- if (snapshot === self.snapshot) {
1547
+ // the current snapshot needs no reconcile
1548
+ if (snapshot === this.snapshot) {
1527
1549
  return;
1528
1550
  }
1529
- // else, apply it by calling the type logic
1530
- return self.type.applySnapshot(self, snapshot);
1551
+ this.type.applySnapshot(this, snapshot);
1531
1552
  });
1532
1553
  addHiddenFinalProp(this.storedValue, "$treenode", this);
1533
1554
  addHiddenFinalProp(this.storedValue, "toJSON", toJSON);
1534
1555
  }
1535
- die() {
1536
- if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) {
1537
- return;
1538
- }
1539
- this.aboutToDie();
1540
- this.finalizeDeath();
1541
- }
1542
1556
  aboutToDie() {
1543
1557
  if (this._observableInstanceState ===
1544
1558
  ObservableInstanceLifecycle.UNINITIALIZED) {
@@ -1550,8 +1564,8 @@ class ObjectNode extends BaseNode {
1550
1564
  // beforeDestroy should run before the disposers since else we could end up in a situation where
1551
1565
  // a disposer added with addDisposer at this stage (beforeDestroy) is actually never released
1552
1566
  this.baseAboutToDie();
1553
- this._internalEventsEmit(InternalEvents.Dispose);
1554
- this._internalEventsClear(InternalEvents.Dispose);
1567
+ this._internalEvents?.emit(InternalEvents.Dispose);
1568
+ this._internalEvents?.clear(InternalEvents.Dispose);
1555
1569
  }
1556
1570
  finalizeDeath() {
1557
1571
  // invariant: not called directly but from "die"
@@ -1562,57 +1576,57 @@ class ObjectNode extends BaseNode {
1562
1576
  // "kill" the computed prop and just store the last snapshot
1563
1577
  const snapshot = this.snapshot;
1564
1578
  this._snapshotUponDeath = snapshot;
1565
- this._internalEventsClearAll();
1579
+ this._internalEvents?.clearAll();
1566
1580
  this.baseFinalizeDeath();
1567
1581
  }
1568
1582
  onSnapshot(onChange) {
1569
1583
  this._addSnapshotReaction();
1570
- const unregister = this._internalEventsRegister(InternalEvents.Snapshot, onChange);
1584
+ const unregister = this.internalEvents().register(InternalEvents.Snapshot, onChange);
1571
1585
  return () => {
1572
1586
  unregister();
1573
1587
  // The reaction re-serializes the whole subtree on every change, so leaving
1574
1588
  // it running once the last listener is gone would keep charging the node
1575
1589
  // for a snapshot nobody receives. Referential stability of getSnapshot()
1576
1590
  // does not depend on it — that comes from keepAlive on _snapshotComputed.
1577
- if (!this._internalEventsHasSubscribers(InternalEvents.Snapshot)) {
1591
+ if (!this._internalEvents?.hasSubscribers(InternalEvents.Snapshot)) {
1578
1592
  this._removeSnapshotReaction();
1579
1593
  }
1580
1594
  };
1581
1595
  }
1582
1596
  emitSnapshot(snapshot) {
1583
- this._internalEventsEmit(InternalEvents.Snapshot, snapshot);
1597
+ this._internalEvents?.emit(InternalEvents.Snapshot, snapshot);
1584
1598
  }
1585
1599
  onPatch(handler) {
1586
- return this._internalEventsRegister(InternalEvents.Patch, handler);
1600
+ return this.internalEvents().register(InternalEvents.Patch, handler);
1587
1601
  }
1588
1602
  emitPatch(basePatch, source) {
1589
- if (this._internalEventsHasSubscribers(InternalEvents.Patch)) {
1603
+ if (this._internalEvents?.hasSubscribers(InternalEvents.Patch)) {
1590
1604
  const localizedPatch = {
1591
1605
  ...basePatch,
1592
1606
  path: `${source.path.slice(this.path.length)}/${basePatch.path}` // calculate the relative path of the patch
1593
1607
  };
1594
1608
  const [patch, reversePatch] = splitPatch(localizedPatch);
1595
- this._internalEventsEmit(InternalEvents.Patch, patch, reversePatch);
1609
+ this._internalEvents?.emit(InternalEvents.Patch, patch, reversePatch);
1596
1610
  }
1597
1611
  if (this.parent) {
1598
1612
  this.parent.emitPatch(basePatch, source);
1599
1613
  }
1600
1614
  }
1601
1615
  hasDisposer(disposer) {
1602
- return this._internalEventsHas(InternalEvents.Dispose, disposer);
1616
+ return !!this._internalEvents?.has(InternalEvents.Dispose, disposer);
1603
1617
  }
1604
1618
  addDisposer(disposer) {
1605
1619
  if (!this.hasDisposer(disposer)) {
1606
- this._internalEventsRegister(InternalEvents.Dispose, disposer, true);
1620
+ this.internalEvents().register(InternalEvents.Dispose, disposer, true);
1607
1621
  return;
1608
1622
  }
1609
1623
  throw fail("cannot add a disposer when it is already registered for execution");
1610
1624
  }
1611
1625
  removeDisposer(disposer) {
1612
- if (!this._internalEventsHas(InternalEvents.Dispose, disposer)) {
1626
+ if (!this._internalEvents?.has(InternalEvents.Dispose, disposer)) {
1613
1627
  throw fail("cannot remove a disposer which was never registered for execution");
1614
1628
  }
1615
- this._internalEventsUnregister(InternalEvents.Dispose, disposer);
1629
+ this._internalEvents?.unregister(InternalEvents.Dispose, disposer);
1616
1630
  }
1617
1631
  removeMiddleware(middleware) {
1618
1632
  if (this.middlewares) {
@@ -1657,48 +1671,17 @@ class ObjectNode extends BaseNode {
1657
1671
  // not removeDisposer(), which throws when the registration is already gone —
1658
1672
  // the last listener can be disposed from within a disposer, i.e. after
1659
1673
  // aboutToDie has cleared them
1660
- this._internalEventsUnregister(InternalEvents.Dispose, disposer);
1674
+ this._internalEvents?.unregister(InternalEvents.Dispose, disposer);
1661
1675
  disposer();
1662
1676
  }
1663
- // #region internal event handling
1677
+ // created on first registration: most nodes never get a listener
1664
1678
  _internalEvents;
1665
- // we proxy the methods to avoid creating an EventHandlers instance when it is not needed
1666
- _internalEventsHasSubscribers(event) {
1667
- return !!this._internalEvents && this._internalEvents.hasSubscribers(event);
1668
- }
1669
- _internalEventsRegister(event, eventHandler, atTheBeginning = false) {
1670
- if (!this._internalEvents) {
1671
- this._internalEvents = new EventHandlers();
1672
- }
1673
- return this._internalEvents.register(event, eventHandler, atTheBeginning);
1674
- }
1675
- _internalEventsHas(event, eventHandler) {
1676
- return (!!this._internalEvents && this._internalEvents.has(event, eventHandler));
1677
- }
1678
- _internalEventsUnregister(event, eventHandler) {
1679
- if (this._internalEvents) {
1680
- this._internalEvents.unregister(event, eventHandler);
1681
- }
1682
- }
1683
- _internalEventsEmit(event, ...args) {
1684
- if (this._internalEvents) {
1685
- this._internalEvents.emit(event, ...args);
1686
- }
1687
- }
1688
- _internalEventsClear(event) {
1689
- if (this._internalEvents) {
1690
- this._internalEvents.clear(event);
1691
- }
1692
- }
1693
- _internalEventsClearAll() {
1694
- if (this._internalEvents) {
1695
- this._internalEvents.clearAll();
1696
- }
1679
+ internalEvents() {
1680
+ return (this._internalEvents ??= new EventHandlers());
1697
1681
  }
1698
1682
  }
1699
1683
  ObjectNode.prototype.createObservableInstance = action(ObjectNode.prototype.createObservableInstance);
1700
1684
  ObjectNode.prototype.detach = action(ObjectNode.prototype.detach);
1701
- ObjectNode.prototype.die = action(ObjectNode.prototype.die);
1702
1685
 
1703
1686
  /**
1704
1687
  * @internal
@@ -1890,7 +1873,6 @@ class ComplexType extends BaseType {
1890
1873
  return null;
1891
1874
  }
1892
1875
  }
1893
- ComplexType.prototype.create = action(ComplexType.prototype.create);
1894
1876
  /**
1895
1877
  * @internal
1896
1878
  * @hidden
@@ -1900,11 +1882,6 @@ class SimpleType extends BaseType {
1900
1882
  return snapshot;
1901
1883
  }
1902
1884
  getValue(node) {
1903
- // if we ever find a case where scalar nodes can be accessed without iterating through its parent
1904
- // uncomment this to make sure the parent chain is created when this is accessed
1905
- // if (node.parent) {
1906
- // node.parent.createObservableInstanceIfNeeded()
1907
- // }
1908
1885
  return node.storedValue;
1909
1886
  }
1910
1887
  getSnapshot(node) {
@@ -1934,6 +1911,30 @@ class SimpleType extends BaseType {
1934
1911
  function isType(value) {
1935
1912
  return (typeof value === "object" && value?.isType === true);
1936
1913
  }
1914
+ /**
1915
+ * The one type a wrapper hands everything to: what an `optional`,
1916
+ * `stripDefault`, `refinement`, `snapshotProcessor` or (resolved) `late`
1917
+ * wraps. `undefined` for any other type, including a union or a `resilient`,
1918
+ * which pick a type per value.
1919
+ *
1920
+ * Resolves a `late` type's definition if it has not been read yet.
1921
+ */
1922
+ function getWrappedType(type) {
1923
+ const subtype = type.getSubTypes();
1924
+ return isType(subtype) ? subtype : undefined;
1925
+ }
1926
+ /**
1927
+ * Strips every wrapper {@link getWrappedType} sees through, down to the type
1928
+ * that actually builds the value: for a complex type `T`,
1929
+ * `getType(T.create(snapshot)) === unwrapType(T)`.
1930
+ *
1931
+ * `types.model({ xs: types.array(X) }).properties.xs`, for instance, is an
1932
+ * `optional` whose unwrapped type is the array type.
1933
+ */
1934
+ function unwrapType(type) {
1935
+ const wrapped = getWrappedType(type);
1936
+ return wrapped ? unwrapType(wrapped) : type;
1937
+ }
1937
1938
  /**
1938
1939
  * @internal
1939
1940
  * @hidden
@@ -2932,7 +2933,7 @@ function createScalarNode(type, parent, subpath, environment, initialValue) {
2932
2933
  * @hidden
2933
2934
  */
2934
2935
  function isNode(value) {
2935
- return value instanceof ScalarNode || value instanceof ObjectNode;
2936
+ return value instanceof BaseNode;
2936
2937
  }
2937
2938
 
2938
2939
  /**
@@ -3111,25 +3112,6 @@ function fail(message = "Illegal state") {
3111
3112
  function identity(_) {
3112
3113
  return _;
3113
3114
  }
3114
- /**
3115
- * @internal
3116
- * @hidden
3117
- */
3118
- const isInteger = Number.isInteger;
3119
- /**
3120
- * @internal
3121
- * @hidden
3122
- */
3123
- function isFloat(val) {
3124
- return Number(val) === val && val % 1 !== 0;
3125
- }
3126
- /**
3127
- * @internal
3128
- * @hidden
3129
- */
3130
- function isFinite(val) {
3131
- return Number.isFinite(val);
3132
- }
3133
3115
  /**
3134
3116
  * @internal
3135
3117
  * @hidden
@@ -3777,11 +3759,8 @@ class SnapshotProcessor extends BaseType {
3777
3759
  return sn;
3778
3760
  }
3779
3761
  _fixNode(node) {
3780
- // the node's type is the *inner* type, so `getType(instance).create(...)`
3781
- // would bypass the processors — point it at ours instead
3782
- const nodeType = node.type;
3783
- nodeType.create = this.create.bind(this);
3784
3762
  if (node instanceof ObjectNode) {
3763
+ node.snapshotProcessorType = this;
3785
3764
  node.hasSnapshotPostProcessor = !!this._processors.postProcessor;
3786
3765
  }
3787
3766
  const oldGetSnapshot = node.getSnapshot;
@@ -4265,14 +4244,20 @@ function map(subtype) {
4265
4244
  return new MapType(subtype);
4266
4245
  }
4267
4246
  /**
4268
- * Returns if a given value represents a map type.
4269
- *
4270
- * @param type
4271
- * @returns `true` if it is a map type.
4247
+ * Returns if a type is a map type, or wraps or unions one. For the map type
4248
+ * itself, use {@link asMapType}.
4272
4249
  */
4273
4250
  function isMapType(type) {
4274
4251
  return isType(type) && (type.flags & TypeFlags.Map) > 0;
4275
4252
  }
4253
+ /**
4254
+ * The map type `type` builds its values with, seeing through the wrappers
4255
+ * {@link unwrapType} does, or `undefined` if that is not a map type.
4256
+ */
4257
+ function asMapType(type) {
4258
+ const unwrapped = unwrapType(type);
4259
+ return unwrapped instanceof MapType ? unwrapped : undefined;
4260
+ }
4276
4261
 
4277
4262
  /**
4278
4263
  * @internal
@@ -4642,14 +4627,20 @@ function areSame(oldNode, newValue) {
4642
4627
  oldNodeType.isMatchingSnapshotId(oldNode, newValue));
4643
4628
  }
4644
4629
  /**
4645
- * Returns if a given value represents an array type.
4646
- *
4647
- * @param type
4648
- * @returns `true` if the type is an array type.
4630
+ * Returns if a type is an array type, or wraps or unions one. For the array
4631
+ * type itself, use {@link asArrayType}.
4649
4632
  */
4650
4633
  function isArrayType(type) {
4651
4634
  return isType(type) && (type.flags & TypeFlags.Array) > 0;
4652
4635
  }
4636
+ /**
4637
+ * The array type `type` builds its values with, seeing through the wrappers
4638
+ * {@link unwrapType} does, or `undefined` if that is not an array type.
4639
+ */
4640
+ function asArrayType(type) {
4641
+ const unwrapped = unwrapType(type);
4642
+ return unwrapped instanceof ArrayType ? unwrapped : undefined;
4643
+ }
4653
4644
 
4654
4645
  const PRE_PROCESS_SNAPSHOT = "preProcessSnapshot";
4655
4646
  const POST_PROCESS_SNAPSHOT = "postProcessSnapshot";
@@ -4684,6 +4675,12 @@ function getPropObservables(storedValue) {
4684
4675
  function objectTypeToString() {
4685
4676
  return getStateTreeNode(this).toString();
4686
4677
  }
4678
+ /** `second` applied to `first`'s result, allocating only when both exist */
4679
+ function chainProcessors(first, second) {
4680
+ return first && second
4681
+ ? snapshot => second(first(snapshot))
4682
+ : (first ?? second);
4683
+ }
4687
4684
  const ANONYMOUS_MODEL_NAME = "AnonymousModel";
4688
4685
  /**
4689
4686
  * A plain loop rather than a callback per property, which allocated a closure
@@ -4972,8 +4969,6 @@ class ModelType extends ComplexType {
4972
4969
  ? initialValue
4973
4970
  : this.applySnapshotPreProcessor(initialValue);
4974
4971
  return createObjectNode(this, parent, subpath, environment, value);
4975
- // Optimization: record all prop- view- and action names after first construction, and generate an optimal base class
4976
- // that pre-reserves all these fields for fast object-member lookups
4977
4972
  }
4978
4973
  initializeChildNodes(objNode, initialSnapshot = {}) {
4979
4974
  const type = objNode.type;
@@ -5137,15 +5132,12 @@ class ModelType extends ComplexType {
5137
5132
  }
5138
5133
  }
5139
5134
  applySnapshotPreProcessor(snapshot) {
5140
- const processor = this.preProcessor;
5141
- return processor ? processor.call(null, snapshot) : snapshot;
5135
+ const preProcessor = this.preProcessor;
5136
+ return preProcessor ? preProcessor(snapshot) : snapshot;
5142
5137
  }
5143
5138
  applySnapshotPostProcessor(snapshot) {
5144
5139
  const postProcessor = this.postProcessor;
5145
- if (postProcessor) {
5146
- return postProcessor.call(null, snapshot);
5147
- }
5148
- return snapshot;
5140
+ return postProcessor ? postProcessor(snapshot) : snapshot;
5149
5141
  }
5150
5142
  getChildType(propertyName) {
5151
5143
  assertIsString(propertyName, 1);
@@ -5168,7 +5160,6 @@ class ModelType extends ComplexType {
5168
5160
  return typeCheckSuccess();
5169
5161
  }
5170
5162
  describe() {
5171
- // optimization: cache
5172
5163
  return `{ ${this.propertyNames
5173
5164
  .map(key => `${key}: ${this.properties[key].describe()}`)
5174
5165
  .join("; ")} }`;
@@ -5190,27 +5181,17 @@ Object.assign(ModelType.prototype, {
5190
5181
  props(properties) {
5191
5182
  return this.cloneAndEnhance({ properties });
5192
5183
  },
5184
+ // a newly added pre-processor sees the raw snapshot first; a newly added
5185
+ // post-processor sees what the existing one produced
5193
5186
  preProcessSnapshot(preProcessor) {
5194
- const currentPreprocessor = this.preProcessor;
5195
- if (!currentPreprocessor) {
5196
- return this.cloneAndEnhance({ preProcessor });
5197
- }
5198
- else {
5199
- return this.cloneAndEnhance({
5200
- preProcessor: snapshot => currentPreprocessor(preProcessor(snapshot))
5201
- });
5202
- }
5187
+ return this.cloneAndEnhance({
5188
+ preProcessor: chainProcessors(preProcessor, this.preProcessor)
5189
+ });
5203
5190
  },
5204
5191
  postProcessSnapshot(postProcessor) {
5205
- const currentPostprocessor = this.postProcessor;
5206
- if (!currentPostprocessor) {
5207
- return this.cloneAndEnhance({ postProcessor });
5208
- }
5209
- else {
5210
- return this.cloneAndEnhance({
5211
- postProcessor: snapshot => postProcessor(currentPostprocessor(snapshot))
5212
- });
5213
- }
5192
+ return this.cloneAndEnhance({
5193
+ postProcessor: chainProcessors(this.postProcessor, postProcessor)
5194
+ });
5214
5195
  }
5215
5196
  });
5216
5197
  /**
@@ -5234,39 +5215,44 @@ function model(...args) {
5234
5215
  * the types are composed into a new Type with the given name
5235
5216
  */
5236
5217
  function compose(...args) {
5237
- // TODO: just join the base type names if no name is provided
5238
5218
  const hasTypename = typeof args[0] === "string";
5239
5219
  const typeName = hasTypename ? args[0] : ANONYMOUS_MODEL_NAME;
5240
5220
  if (hasTypename) {
5241
5221
  args.shift();
5242
5222
  }
5243
- // check all parameters
5223
+ // exactly a model type: each part's own properties and initializers are
5224
+ // read below, and a wrapper around a model has neither
5244
5225
  if (devMode()) {
5245
5226
  args.forEach((type, i) => {
5246
- assertArg(type, isModelType, "mobx-state-tree model type", hasTypename ? i + 2 : i + 1);
5227
+ assertArg(type, t => t instanceof ModelType, "mobx-state-tree model type", hasTypename ? i + 2 : i + 1);
5247
5228
  });
5248
5229
  }
5249
5230
  return args
5250
5231
  .reduce((prev, cur) => prev.cloneAndEnhance({
5251
- name: `${prev.name}_${cur.name}`,
5252
5232
  properties: cur.properties,
5253
5233
  // cur.properties is another ModelType's already-converted+frozen bag
5254
5234
  propertiesAreConverted: true,
5255
5235
  initializers: cur.initializers,
5256
- preProcessor: (snapshot) => cur.applySnapshotPreProcessor(prev.applySnapshotPreProcessor(snapshot)),
5257
- postProcessor: (snapshot) => cur.applySnapshotPostProcessor(prev.applySnapshotPostProcessor(snapshot))
5236
+ preProcessor: chainProcessors(prev.preProcessor, cur.preProcessor),
5237
+ postProcessor: chainProcessors(prev.postProcessor, cur.postProcessor)
5258
5238
  }))
5259
5239
  .named(typeName);
5260
5240
  }
5261
5241
  /**
5262
- * Returns if a given value represents a model type.
5263
- *
5264
- * @param type
5265
- * @returns
5242
+ * Returns if a type is a model type, or wraps or unions one. For the model
5243
+ * type itself, use {@link asModelType}.
5266
5244
  */
5267
5245
  function isModelType(type) {
5268
5246
  return isType(type) && (type.flags & TypeFlags.Object) > 0;
5269
5247
  }
5248
+ /**
5249
+ * The model type `type` builds its values with, seeing through the wrappers
5250
+ * {@link unwrapType} does, or `undefined` if that is not a model type.
5251
+ */
5252
+ function asModelType(type) {
5253
+ const unwrapped = unwrapType(type);
5254
+ return unwrapped instanceof ModelType ? unwrapped : undefined;
5255
+ }
5270
5256
  /**
5271
5257
  * `extendInstance` - attaches additional actions, views and volatile state to an
5272
5258
  * already-created model instance, using the same instantiation machinery as
@@ -5292,7 +5278,7 @@ function extendInstance(instance, fn) {
5292
5278
  throw fail("extendInstance expects a mobx-state-tree node");
5293
5279
  }
5294
5280
  const type = getStateTreeNode(instance).type;
5295
- if (!isModelType(type)) {
5281
+ if (!(type instanceof ModelType)) {
5296
5282
  throw fail("extendInstance can only be used on model instances");
5297
5283
  }
5298
5284
  // Views/actions/volatile are installed via defineProperty + makeObservable — the
@@ -5300,9 +5286,8 @@ function extendInstance(instance, fn) {
5300
5286
  // write-protection interceptor is attached (see finalizeNewInstance). On a live
5301
5287
  // instance that interceptor is already active, so run the attach inside an action
5302
5288
  // context: isRunningAction() then short-circuits assertWritable.
5303
- const modelType = type;
5304
5289
  const attach = createActionInvoker(instance, "@@extendInstance", (() => {
5305
- modelType.applyExtensionToInstance(instance, fn(instance));
5290
+ type.applyExtensionToInstance(instance, fn(instance));
5306
5291
  }));
5307
5292
  attach();
5308
5293
  return instance;
@@ -5374,7 +5359,7 @@ const number = new CoreType("number", TypeFlags.Number, v => typeof v === "numbe
5374
5359
  * })
5375
5360
  * ```
5376
5361
  */
5377
- const integer = new CoreType("integer", TypeFlags.Integer, v => isInteger(v));
5362
+ const integer = new CoreType("integer", TypeFlags.Integer, Number.isInteger);
5378
5363
  /**
5379
5364
  * `types.float` - Creates a type that can only contain an float value.
5380
5365
  *
@@ -5386,7 +5371,7 @@ const integer = new CoreType("integer", TypeFlags.Integer, v => isInteger(v));
5386
5371
  * })
5387
5372
  * ```
5388
5373
  */
5389
- const float = new CoreType("float", TypeFlags.Float, v => isFloat(v));
5374
+ const float = new CoreType("float", TypeFlags.Float, v => Number(v) === v && v % 1 !== 0);
5390
5375
  /**
5391
5376
  * `types.finite` - Creates a type that can only contain an finite value.
5392
5377
  *
@@ -5398,7 +5383,7 @@ const float = new CoreType("float", TypeFlags.Float, v => isFloat(v));
5398
5383
  * })
5399
5384
  * ```
5400
5385
  */
5401
- const finite = new CoreType("finite", TypeFlags.Finite, v => isFinite(v));
5386
+ const finite = new CoreType("finite", TypeFlags.Finite, Number.isFinite);
5402
5387
  /**
5403
5388
  * `types.boolean` - Creates a type that can only contain a boolean value.
5404
5389
  * This type is used for boolean values by default
@@ -5459,11 +5444,9 @@ function getPrimitiveFactoryFromValue(value) {
5459
5444
  /**
5460
5445
  * Returns if a given value represents a primitive type.
5461
5446
  *
5462
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
5463
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
5464
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
5465
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
5466
- * `isMapType`, `isModelType`) keep their predicate.
5447
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
5448
+ * unions inherit from what they hold, so it is also true for a type that wraps
5449
+ * or unions one. Use {@link unwrapType} to get at the type itself.
5467
5450
  *
5468
5451
  * @param type
5469
5452
  * @returns
@@ -5529,11 +5512,9 @@ function literal(value) {
5529
5512
  /**
5530
5513
  * Returns if a given value represents a literal type.
5531
5514
  *
5532
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
5533
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
5534
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
5535
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
5536
- * `isMapType`, `isModelType`) keep their predicate.
5515
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
5516
+ * unions inherit from what they hold, so it is also true for a type that wraps
5517
+ * or unions one. Use {@link unwrapType} to get at the type itself.
5537
5518
  *
5538
5519
  * @param type
5539
5520
  * @returns
@@ -5611,11 +5592,9 @@ function refinement(...args) {
5611
5592
  /**
5612
5593
  * Returns if a given value is a refinement type.
5613
5594
  *
5614
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
5615
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
5616
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
5617
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
5618
- * `isMapType`, `isModelType`) keep their predicate.
5595
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
5596
+ * unions inherit from what they hold, so it is also true for a type that wraps
5597
+ * or unions one. Use {@link unwrapType} to get at the type itself.
5619
5598
  *
5620
5599
  * @param type
5621
5600
  * @returns
@@ -5628,6 +5607,10 @@ function isRefinementType(type) {
5628
5607
  * `types.enumeration` - Can be used to create an string based enumeration.
5629
5608
  * (note: this methods is just sugar for a union of string literals)
5630
5609
  *
5610
+ * The member type is inferred from the options, so a literal array, an
5611
+ * `as const` array and `Object.values(SomeStringEnum)` all produce the exact
5612
+ * union; only an array already typed `string[]` falls back to `string`.
5613
+ *
5631
5614
  * Example:
5632
5615
  * ```ts
5633
5616
  * const TrafficLight = types.model({
@@ -5639,52 +5622,47 @@ function isRefinementType(type) {
5639
5622
  * @param options possible values this enumeration can have
5640
5623
  * @returns
5641
5624
  */
5642
- function enumeration(name, options) {
5643
- const realOptions = typeof name === "string" ? options : name;
5644
- // check all options
5625
+ function enumeration(nameOrOptions, maybeOptions) {
5626
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : undefined;
5627
+ const options = typeof nameOrOptions === "string" ? maybeOptions : nameOrOptions;
5645
5628
  if (devMode()) {
5646
- realOptions.forEach((option, i) => {
5629
+ options.forEach((option, i) => {
5647
5630
  assertIsString(option, i + 1);
5648
5631
  });
5649
5632
  }
5650
- const type = union(...realOptions.map(option => literal(`${option}`)));
5651
- if (typeof name === "string") {
5652
- type.name = name;
5653
- }
5654
- return type;
5633
+ // built directly rather than through union(): its members are fresh
5634
+ // literals, so interning could never share it, and the name belongs to this
5635
+ // union alone
5636
+ return new Union(options.map(option => literal(`${option}`)), name === undefined ? undefined : { name });
5655
5637
  }
5656
5638
 
5657
- // Drill through single-subtype wrappers — optional(), refinement(),
5658
- // snapshotProcessor(), late() — to the underlying ModelType. Discriminated-
5659
- // union scoping keys on a member's literal `type` property, but real-world
5660
- // members are rarely bare models (jbrowse config schemas, for instance, are
5661
- // always optional(model) or optional(snapshotProcessor(model))). Without this
5662
- // the scoping never engages and every failure prints every member's full
5663
- // structure. Wrappers expose their child as `_subtype` (optional/refinement/
5664
- // snapshotProcessor) or via `getSubType()` (late); bounded to avoid cycles.
5665
- // Only *successful* resolutions are cached. A wrapper chain's shape is fixed at
5666
- // construction, so once a member resolves to a ModelType it always will; but a
5667
- // `late` member reports no subtype until its definition evaluates, and that
5668
- // miss must stay retryable.
5669
- const resolvedModelTypes = new WeakMap();
5639
+ // The model a member is meant for: the one under its wrappers, and for a
5640
+ // `resilient` member the one it tries first. Matching a snapshot to a member
5641
+ // (quick-match dispatch, discriminator scoping) keys on that model's props,
5642
+ // and real-world members are rarely bare models (jbrowse config schemas are
5643
+ // always optional(model) or optional(snapshotProcessor(model))).
5644
+ // Misses are cached too, unless the member holds a `late` type: that has no
5645
+ // subtype until its definition evaluates, so its miss must stay retryable.
5646
+ const intendedModelTypes = new WeakMap();
5670
5647
  function resolveModelType(type) {
5671
- if (!type) {
5672
- return undefined;
5648
+ const cached = intendedModelTypes.get(type);
5649
+ if (cached !== undefined) {
5650
+ return cached ?? undefined;
5673
5651
  }
5674
- const cached = resolvedModelTypes.get(type);
5675
- if (cached) {
5676
- return cached;
5652
+ const model = findIntendedModelType(type);
5653
+ if (model || !(type.flags & TypeFlags.Late)) {
5654
+ intendedModelTypes.set(type, model ?? null);
5677
5655
  }
5678
- let current = type;
5679
- for (let depth = 0; current && depth < 20; depth++) {
5680
- if (current instanceof ModelType) {
5681
- resolvedModelTypes.set(type, current);
5682
- return current;
5683
- }
5684
- const wrapper = current;
5685
- current = wrapper._subtype ?? wrapper.getSubType?.(false);
5656
+ return model;
5657
+ }
5658
+ function findIntendedModelType(type) {
5659
+ const unwrapped = unwrapType(type);
5660
+ if (unwrapped instanceof ModelType) {
5661
+ return unwrapped;
5686
5662
  }
5687
- return undefined;
5663
+ return unwrapped instanceof Resilient
5664
+ ? findIntendedModelType(unwrapped.primaryType)
5665
+ : undefined;
5688
5666
  }
5689
5667
  // The quick-match paths below already know a type carries TypeFlags.Literal,
5690
5668
  // but `is()` still routes through BaseType.validate — a context array, an entry
@@ -5722,13 +5700,17 @@ class Union extends BaseType {
5722
5700
  if (cached !== undefined) {
5723
5701
  return cached;
5724
5702
  }
5703
+ const result = this.foldFlags();
5704
+ if (!(result & TypeFlags.Late)) {
5705
+ this._flags = result;
5706
+ }
5707
+ return result;
5708
+ }
5709
+ foldFlags() {
5725
5710
  let result = TypeFlags.Union;
5726
5711
  for (const type of this.members()) {
5727
5712
  result |= type.flags;
5728
5713
  }
5729
- if (!(result & TypeFlags.Late)) {
5730
- this._flags = result;
5731
- }
5732
5714
  return result;
5733
5715
  }
5734
5716
  computeName() {
@@ -6063,11 +6045,7 @@ class DynamicUnion extends Union {
6063
6045
  return this._members();
6064
6046
  }
6065
6047
  get flags() {
6066
- let result = TypeFlags.Union;
6067
- for (const type of this.members()) {
6068
- result |= type.flags;
6069
- }
6070
- return result;
6048
+ return this.foldFlags();
6071
6049
  }
6072
6050
  get name() {
6073
6051
  return this._explicitName !== undefined
@@ -6146,11 +6124,9 @@ function union(...args) {
6146
6124
  /**
6147
6125
  * Returns if a given value represents a union type.
6148
6126
  *
6149
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6150
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6151
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6152
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
6153
- * `isMapType`, `isModelType`) keep their predicate.
6127
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
6128
+ * unions inherit from what they hold, so it is also true for a type that wraps
6129
+ * or unions one. Use {@link unwrapType} to get at the type itself.
6154
6130
  *
6155
6131
  * @param type
6156
6132
  * @returns
@@ -6159,13 +6135,9 @@ function isUnionType(type) {
6159
6135
  return isType(type) && (type.flags & TypeFlags.Union) > 0;
6160
6136
  }
6161
6137
  /**
6162
- * Returns the member types of a union.
6163
- *
6164
- * Wrapper types (`optional`, `refinement`, `late`) inherit the union flag from
6165
- * the type they wrap, so `isUnionType` is true for e.g. an optional-of-union,
6166
- * but their `getSubTypes()` reports the single wrapped type rather than the
6167
- * union's members. This drills through those wrappers until the union's member
6168
- * array surfaces.
6138
+ * Returns the member types of a union, seeing through the wrappers
6139
+ * {@link unwrapType} does: `isUnionType` is also true for e.g. an
6140
+ * optional-of-union.
6169
6141
  *
6170
6142
  * @param type a type for which `isUnionType` is true
6171
6143
  * @returns the array of member types of the underlying union
@@ -6174,16 +6146,11 @@ function getUnionSubtypes(type) {
6174
6146
  if (!isUnionType(type)) {
6175
6147
  throw fail("expected a union type");
6176
6148
  }
6177
- let subtypes = type.getSubTypes();
6178
- while (typeof subtypes === "object" &&
6179
- subtypes !== null &&
6180
- !Array.isArray(subtypes)) {
6181
- subtypes = subtypes.getSubTypes();
6182
- }
6183
- if (!Array.isArray(subtypes)) {
6149
+ const union = unwrapType(type);
6150
+ if (!(union instanceof Union)) {
6184
6151
  throw fail("could not extract subtypes from union type");
6185
6152
  }
6186
- return subtypes;
6153
+ return union.getSubTypes();
6187
6154
  }
6188
6155
 
6189
6156
  /**
@@ -6330,11 +6297,9 @@ const undefinedAsOptionalValues = [undefined];
6330
6297
  /**
6331
6298
  * Returns if a value represents an optional type.
6332
6299
  *
6333
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6334
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6335
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6336
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
6337
- * `isMapType`, `isModelType`) keep their predicate.
6300
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
6301
+ * unions inherit from what they hold, so it is also true for a type that wraps
6302
+ * or unions one. Use {@link unwrapType} to get at the type itself.
6338
6303
  *
6339
6304
  * @param type
6340
6305
  * @returns
@@ -6345,26 +6310,6 @@ function isOptionalType(type) {
6345
6310
  function isRecord(value) {
6346
6311
  return typeof value === "object" && value !== null;
6347
6312
  }
6348
- /**
6349
- * The identifier attribute of the model a stripDefault ultimately wraps, or
6350
- * `null` when there is none. Drills through the single-subtype wrappers the way
6351
- * `resolveModelType` in union.ts does — that one is module-private there, and
6352
- * this file may not reach into it.
6353
- *
6354
- * `null` is also the answer for an unresolved `types.late`, which only costs the
6355
- * short-circuit, never correctness: the full structural walk still runs.
6356
- */
6357
- function resolveIdentifierAttribute(type) {
6358
- let current = type;
6359
- for (let depth = 0; current && depth < 20; depth++) {
6360
- if (current instanceof ModelType) {
6361
- return current.identifierAttribute ? current.identifierAttribute : null;
6362
- }
6363
- const wrapper = current;
6364
- current = wrapper._subtype ?? wrapper.getSubType?.(false);
6365
- }
6366
- return null;
6367
- }
6368
6313
  /**
6369
6314
  * Compare a child snapshot to a stripped-default's reference snapshot: identity
6370
6315
  * for primitives, structural for objects/arrays.
@@ -6446,7 +6391,10 @@ class StripDefaultValue extends OptionalValue {
6446
6391
  equalsDefault(snapshot, defaultSnapshot) {
6447
6392
  let identifierAttribute = this._identifierAttribute;
6448
6393
  if (identifierAttribute === undefined) {
6449
- identifierAttribute = resolveIdentifierAttribute(this.getSubTypes());
6394
+ // null when not wrapping an identified model; an unresolved late member
6395
+ // also lands here, which only costs the short-circuit below
6396
+ identifierAttribute =
6397
+ asModelType(this.getSubTypes())?.identifierAttribute ?? null;
6450
6398
  this._identifierAttribute = identifierAttribute;
6451
6399
  }
6452
6400
  // an identified model's snapshot normally has the same shape as the default
@@ -6556,16 +6504,14 @@ class Late extends BaseType {
6556
6504
  }
6557
6505
  getSubType(mustSucceed) {
6558
6506
  if (!this._subType) {
6559
- let t = undefined;
6507
+ let t;
6560
6508
  try {
6561
6509
  t = this._definition();
6562
6510
  }
6563
6511
  catch (e) {
6564
- if (e instanceof ReferenceError) // can happen in strict ES5 code when a definition is self refering
6565
- {
6566
- t = undefined;
6567
- }
6568
- else {
6512
+ // a self-referencing definition can read its binding before it is
6513
+ // initialized; that is "not defined yet", like returning undefined
6514
+ if (!(e instanceof ReferenceError)) {
6569
6515
  throw e;
6570
6516
  }
6571
6517
  }
@@ -6581,10 +6527,15 @@ class Late extends BaseType {
6581
6527
  }
6582
6528
  return this._subType;
6583
6529
  }
6584
- constructor(name, _definition) {
6530
+ constructor(_definition, name) {
6585
6531
  super(name);
6586
6532
  this._definition = _definition;
6587
6533
  }
6534
+ // the unnamed form is named after the definition's source text, which is
6535
+ // only worth building if something reads it
6536
+ computeName() {
6537
+ return `late(${this._definition.toString()})`;
6538
+ }
6588
6539
  instantiate(parent, subpath, environment, initialValue) {
6589
6540
  return this.getSubType(true).instantiate(parent, subpath, environment, initialValue);
6590
6541
  }
@@ -6593,7 +6544,7 @@ class Late extends BaseType {
6593
6544
  }
6594
6545
  describe() {
6595
6546
  const t = this.getSubType(false);
6596
- return t ? t.name : "<uknown late type>";
6547
+ return t ? t.name : "<unknown late type>";
6597
6548
  }
6598
6549
  isValidSnapshot(value, context) {
6599
6550
  const t = this.getSubType(false);
@@ -6608,8 +6559,7 @@ class Late extends BaseType {
6608
6559
  return t ? t.isAssignableFrom(type) : false;
6609
6560
  }
6610
6561
  getSubTypes() {
6611
- const subtype = this.getSubType(false);
6612
- return subtype ? subtype : cannotDetermineSubtype;
6562
+ return this.getSubType(false) ?? cannotDetermineSubtype;
6613
6563
  }
6614
6564
  }
6615
6565
  /**
@@ -6629,26 +6579,21 @@ class Late extends BaseType {
6629
6579
  * @returns
6630
6580
  */
6631
6581
  function late(nameOrType, maybeType) {
6632
- const name = typeof nameOrType === "string"
6633
- ? nameOrType
6634
- : `late(${nameOrType.toString()})`;
6582
+ const name = typeof nameOrType === "string" ? nameOrType : undefined;
6635
6583
  const type = typeof nameOrType === "string" ? maybeType : nameOrType;
6636
- // checks that the type is actually a late type
6637
6584
  if (devMode()) {
6638
6585
  if (!(typeof type === "function" && type.length === 0)) {
6639
6586
  throw fail(`Invalid late type, expected a function with zero arguments that returns a type, got: ${type}`);
6640
6587
  }
6641
6588
  }
6642
- return new Late(name, type);
6589
+ return new Late(type, name);
6643
6590
  }
6644
6591
  /**
6645
6592
  * Returns if a given value represents a late type.
6646
6593
  *
6647
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6648
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6649
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6650
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
6651
- * `isMapType`, `isModelType`) keep their predicate.
6594
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
6595
+ * unions inherit from what they hold, so it is also true for a type that wraps
6596
+ * or unions one. Use {@link unwrapType} to get at the type itself.
6652
6597
  *
6653
6598
  * @param type
6654
6599
  * @returns
@@ -6658,7 +6603,8 @@ function isLateType(type) {
6658
6603
  }
6659
6604
 
6660
6605
  function lazy(name, options) {
6661
- // TODO: fix this unknown casting to be stricter
6606
+ // a lazy type stands in for the type it loads, down to that type's own
6607
+ // methods (a model's `.props`, say), which the placeholder does not have
6662
6608
  return new Lazy(name, options);
6663
6609
  }
6664
6610
  /**
@@ -6825,11 +6771,9 @@ function frozen(arg) {
6825
6771
  /**
6826
6772
  * Returns if a given value represents a frozen type.
6827
6773
  *
6828
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6829
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6830
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6831
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
6832
- * `isMapType`, `isModelType`) keep their predicate.
6774
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
6775
+ * unions inherit from what they hold, so it is also true for a type that wraps
6776
+ * or unions one. Use {@link unwrapType} to get at the type itself.
6833
6777
  *
6834
6778
  * @param type
6835
6779
  * @returns
@@ -6973,9 +6917,10 @@ class BaseReferenceType extends SimpleType {
6973
6917
  return undefined;
6974
6918
  }
6975
6919
  const refTargetNode = getStateTreeNode(refTargetValue);
6976
- const hookHandler = (_, refTargetNodeHook) => {
6920
+ const hookHandler = (subject, refTargetNodeHook) => {
6977
6921
  const cause = getInvalidationCause(refTargetNodeHook);
6978
- if (!cause) {
6922
+ // a reference detached along with its target still resolves it
6923
+ if (!cause || (cause === "detach" && storedRefNode.isWithin(subject))) {
6979
6924
  return;
6980
6925
  }
6981
6926
  this.fireInvalidated(cause, storedRefNode, referenceId, refTargetNode);
@@ -7173,11 +7118,9 @@ function reference(subType, options) {
7173
7118
  /**
7174
7119
  * Returns if a given value represents a reference type.
7175
7120
  *
7176
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
7177
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
7178
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
7179
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
7180
- * `isMapType`, `isModelType`) keep their predicate.
7121
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
7122
+ * unions inherit from what they hold, so it is also true for a type that wraps
7123
+ * or unions one. Use {@link unwrapType} to get at the type itself.
7181
7124
  *
7182
7125
  * @param type
7183
7126
  * @returns
@@ -7302,11 +7245,9 @@ const identifierNumber = new IdentifierNumberType();
7302
7245
  /**
7303
7246
  * Returns if a given value represents an identifier type.
7304
7247
  *
7305
- * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
7306
- * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
7307
- * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
7308
- * to compile. Guards with a distinct narrowing target (`isArrayType`,
7309
- * `isMapType`, `isModelType`) keep their predicate.
7248
+ * Like every `isXType` guard it reads the type's flags, which wrappers and
7249
+ * unions inherit from what they hold, so it is also true for a type that wraps
7250
+ * or unions one. Use {@link unwrapType} to get at the type itself.
7310
7251
  *
7311
7252
  * @param type
7312
7253
  * @returns
@@ -7437,6 +7378,10 @@ class CustomType extends SimpleType {
7437
7378
  }
7438
7379
  }
7439
7380
 
7381
+ /**
7382
+ * @internal
7383
+ * @hidden
7384
+ */
7440
7385
  class Resilient extends BaseType {
7441
7386
  _subtype;
7442
7387
  _fallbackType;
@@ -7444,6 +7389,10 @@ class Resilient extends BaseType {
7444
7389
  get flags() {
7445
7390
  return this._subtype.flags;
7446
7391
  }
7392
+ /** the type tried first; the fallback takes over only when it fails */
7393
+ get primaryType() {
7394
+ return this._subtype;
7395
+ }
7447
7396
  constructor(_subtype, _fallbackType, _createFallbackSnapshot) {
7448
7397
  super();
7449
7398
  this._subtype = _subtype;
@@ -7599,5 +7548,5 @@ const types = {
7599
7548
  resilient
7600
7549
  };
7601
7550
 
7602
- export { addDisposer, addMiddleware, applyAction, applyPatch, applySnapshot, cannotDetermineSubtype, cast, castFlowReturn, castToReferenceSnapshot, castToSnapshot, clone, createActionTrackingMiddleware, createActionTrackingMiddleware2, decorate, destroy, detach, escapeJsonPath, extendInstance, flow, getChildType, getEnv, getIdentifier, getLivelinessChecking, getMembers, getNodeId, getParent, getParentOfType, getPath, getPathParts, getPropertyMembers, getRelativePath, getRoot, getRunningActionContext, getSnapshot, getType, getUnionSubtypes, hasParent, hasParentOfType, isActionContextChildOf, isActionContextThisOrChildOf, isAlive, isArrayType, isFrozenType, isIdentifierType, isLateType, isLiteralType, isMapType, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRefinementType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, joinJsonPath, onAction, onPatch, onSnapshot, protect, recordActions, recordPatches, resolveIdentifier, resolvePath, setDevMode, setLivelinessChecking, setLivelynessChecking, setTypeChecking, splitJsonPath, types as t, toGenerator, toGeneratorFunction, tryReference, tryResolve, typecheck, types, unescapeJsonPath, unprotect, walk };
7551
+ export { addDisposer, addMiddleware, applyAction, applyPatch, applySnapshot, asArrayType, asMapType, asModelType, cannotDetermineSubtype, cast, castFlowReturn, castToReferenceSnapshot, castToSnapshot, clone, createActionTrackingMiddleware, createActionTrackingMiddleware2, decorate, destroy, detach, escapeJsonPath, extendInstance, flow, getChildType, getEnv, getIdentifier, getLivelinessChecking, getMembers, getNodeId, getParent, getParentOfType, getPath, getPathParts, getPropertyMembers, getRelativePath, getRoot, getRunningActionContext, getSnapshot, getType, getUnionSubtypes, getWrappedType, hasParent, hasParentOfType, isActionContextChildOf, isActionContextThisOrChildOf, isAlive, isArrayType, isFrozenType, isIdentifierType, isLateType, isLiteralType, isMapType, isModelType, isOptionalType, isPrimitiveType, isProtected, isReferenceType, isRefinementType, isRoot, isStateTreeNode, isType, isUnionType, isValidReference, joinJsonPath, onAction, onPatch, onSnapshot, protect, recordActions, recordPatches, resolveIdentifier, resolvePath, setDevMode, setLivelinessChecking, setLivelynessChecking, setTypeChecking, splitJsonPath, types as t, toGenerator, toGeneratorFunction, tryReference, tryResolve, typecheck, types, unescapeJsonPath, unprotect, unwrapType, walk };
7603
7552
  //# sourceMappingURL=mobx-state-tree.mjs.map