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