@jbrowse/mobx-state-tree 6.2.0 → 6.4.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.
@@ -270,6 +270,8 @@ function unprotect(target) {
270
270
  * Returns true if the object is in protected mode, @see protect
271
271
  */
272
272
  function isProtected(target) {
273
+ // check all arguments
274
+ assertIsStateTreeNode(target, 1);
273
275
  return getStateTreeNode(target).isProtected;
274
276
  }
275
277
  /**
@@ -301,6 +303,27 @@ function getSnapshot(target, applyPostProcess = true) {
301
303
  }
302
304
  return freeze(node.type.getSnapshot(node, false));
303
305
  }
306
+ function findParentAtDepth(node, depth) {
307
+ let remaining = depth;
308
+ let parent = node.parent;
309
+ while (parent) {
310
+ if (--remaining === 0) {
311
+ return parent;
312
+ }
313
+ parent = parent.parent;
314
+ }
315
+ return null;
316
+ }
317
+ function findParentOfType(node, type) {
318
+ let parent = node.parent;
319
+ while (parent) {
320
+ if (type.is(parent.storedValue)) {
321
+ return parent;
322
+ }
323
+ parent = parent.parent;
324
+ }
325
+ return null;
326
+ }
304
327
  /**
305
328
  * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array.
306
329
  *
@@ -312,14 +335,7 @@ function hasParent(target, depth = 1) {
312
335
  // check all arguments
313
336
  assertIsStateTreeNode(target, 1);
314
337
  assertIsNumber(depth, 2, 0);
315
- let parent = getStateTreeNode(target).parent;
316
- while (parent) {
317
- if (--depth === 0) {
318
- return true;
319
- }
320
- parent = parent.parent;
321
- }
322
- return false;
338
+ return findParentAtDepth(getStateTreeNode(target), depth) !== null;
323
339
  }
324
340
  /**
325
341
  * Returns the immediate parent of this object, or throws.
@@ -338,15 +354,11 @@ function getParent(target, depth = 1) {
338
354
  // check all arguments
339
355
  assertIsStateTreeNode(target, 1);
340
356
  assertIsNumber(depth, 2, 0);
341
- let d = depth;
342
- let parent = getStateTreeNode(target).parent;
343
- while (parent) {
344
- if (--d === 0) {
345
- return parent.storedValue;
346
- }
347
- parent = parent.parent;
357
+ const parent = findParentAtDepth(getStateTreeNode(target), depth);
358
+ if (!parent) {
359
+ throw fail(`Failed to find the parent of ${getStateTreeNode(target)} at depth ${depth}`);
348
360
  }
349
- throw fail(`Failed to find the parent of ${getStateTreeNode(target)} at depth ${depth}`);
361
+ return parent.storedValue;
350
362
  }
351
363
  /**
352
364
  * Given a model instance, returns `true` if the object has a parent of given type, that is, is part of another object, map or array
@@ -359,14 +371,7 @@ function hasParentOfType(target, type) {
359
371
  // check all arguments
360
372
  assertIsStateTreeNode(target, 1);
361
373
  assertIsType(type, 2);
362
- let parent = getStateTreeNode(target).parent;
363
- while (parent) {
364
- if (type.is(parent.storedValue)) {
365
- return true;
366
- }
367
- parent = parent.parent;
368
- }
369
- return false;
374
+ return findParentOfType(getStateTreeNode(target), type) !== null;
370
375
  }
371
376
  /**
372
377
  * Returns the target's parent of a given type, or throws.
@@ -379,14 +384,11 @@ function getParentOfType(target, type) {
379
384
  // check all arguments
380
385
  assertIsStateTreeNode(target, 1);
381
386
  assertIsType(type, 2);
382
- let parent = getStateTreeNode(target).parent;
383
- while (parent) {
384
- if (type.is(parent.storedValue)) {
385
- return parent.storedValue;
386
- }
387
- parent = parent.parent;
387
+ const parent = findParentOfType(getStateTreeNode(target), type);
388
+ if (!parent) {
389
+ throw fail(`Failed to find the parent of ${getStateTreeNode(target)} of a given type`);
388
390
  }
389
- throw fail(`Failed to find the parent of ${getStateTreeNode(target)} of a given type`);
391
+ return parent.storedValue;
390
392
  }
391
393
  /**
392
394
  * Given an object in a model tree, returns the root object of that tree.
@@ -480,30 +482,20 @@ function getIdentifier(target) {
480
482
  return getStateTreeNode(target).identifier;
481
483
  }
482
484
  /**
483
- * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if the check passes,
484
- * else it returns undefined.
485
- *
486
- * @param getter Function to access the reference.
487
- * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check.
488
- * @returns
485
+ * Resolves a reference getter to the referenced node, or `undefined` when the
486
+ * reference is empty, dangling, or (with `checkIfAlive`) points at a dead node.
487
+ * Backs both {@link tryReference} and {@link isValidReference}.
489
488
  */
490
- function tryReference(getter, checkIfAlive = true) {
489
+ function resolveReference(getter, checkIfAlive) {
491
490
  try {
492
491
  const node = getter();
493
492
  if (node === undefined || node === null) {
494
493
  return undefined;
495
494
  }
496
- else if (isStateTreeNode(node)) {
497
- if (!checkIfAlive) {
498
- return node;
499
- }
500
- else {
501
- return isAlive(node) ? node : undefined;
502
- }
503
- }
504
- else {
495
+ if (!isStateTreeNode(node)) {
505
496
  throw fail("The reference to be checked is not one of node, null or undefined");
506
497
  }
498
+ return !checkIfAlive || isAlive(node) ? node : undefined;
507
499
  }
508
500
  catch (e) {
509
501
  if (e instanceof InvalidReferenceError) {
@@ -512,6 +504,17 @@ function tryReference(getter, checkIfAlive = true) {
512
504
  throw e;
513
505
  }
514
506
  }
507
+ /**
508
+ * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if the check passes,
509
+ * else it returns undefined.
510
+ *
511
+ * @param getter Function to access the reference.
512
+ * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check.
513
+ * @returns
514
+ */
515
+ function tryReference(getter, checkIfAlive = true) {
516
+ return resolveReference(getter, checkIfAlive);
517
+ }
515
518
  /**
516
519
  * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not.
517
520
  *
@@ -520,24 +523,7 @@ function tryReference(getter, checkIfAlive = true) {
520
523
  * @returns
521
524
  */
522
525
  function isValidReference(getter, checkIfAlive = true) {
523
- try {
524
- const node = getter();
525
- if (node === undefined || node === null) {
526
- return false;
527
- }
528
- else if (isStateTreeNode(node)) {
529
- return checkIfAlive ? isAlive(node) : true;
530
- }
531
- else {
532
- throw fail("The reference to be checked is not one of node, null or undefined");
533
- }
534
- }
535
- catch (e) {
536
- if (e instanceof InvalidReferenceError) {
537
- return false;
538
- }
539
- throw e;
540
- }
526
+ return resolveReference(getter, checkIfAlive) !== undefined;
541
527
  }
542
528
  /**
543
529
  * Try to resolve a given path relative to a given node.
@@ -1211,11 +1197,11 @@ class ObjectNode extends BaseNode {
1211
1197
  this.identifier = normalizeIdentifier(id);
1212
1198
  this.unnormalizedIdentifier = id;
1213
1199
  }
1214
- if (!parent) {
1215
- this.identifierCache.addNodeToCache(this);
1216
- }
1217
- else {
1218
- parent.root.identifierCache.addNodeToCache(this);
1200
+ // addNodeToCache no-ops without an identifier attribute, and reaching the
1201
+ // root to ask it is a walk up the whole parent chain
1202
+ if (this.identifierAttribute) {
1203
+ const cache = parent ? parent.root.identifierCache : this.identifierCache;
1204
+ cache.addNodeToCache(this);
1219
1205
  }
1220
1206
  }
1221
1207
  createObservableInstanceIfNeeded(fireHooks = true) {
@@ -1363,15 +1349,9 @@ class ObjectNode extends BaseNode {
1363
1349
  typeof this.storedValue === "object" &&
1364
1350
  this.storedValue[name];
1365
1351
  if (typeof fn === "function") {
1366
- // we check for it to allow old mobx peer dependencies that don't have the method to work (even when still bugged)
1367
- if (_allowStateChangesInsideComputed) {
1368
- _allowStateChangesInsideComputed(() => {
1369
- fn.apply(this.storedValue);
1370
- });
1371
- }
1372
- else {
1352
+ _allowStateChangesInsideComputed(() => {
1373
1353
  fn.apply(this.storedValue);
1374
- }
1354
+ });
1375
1355
  }
1376
1356
  }
1377
1357
  _snapshotUponDeath;
@@ -1751,8 +1731,8 @@ var TypeFlags;
1751
1731
  TypeFlags[TypeFlags["Float"] = 4194304] = "Float";
1752
1732
  })(TypeFlags || (TypeFlags = {}));
1753
1733
  /**
1754
- * @internal
1755
- * @hidden
1734
+ * The value `getSubTypes()` returns when a type cannot report its subtype,
1735
+ * which is distinct from reporting that it has none (`null`).
1756
1736
  */
1757
1737
  const cannotDetermineSubtype = "cannotDetermine";
1758
1738
  /**
@@ -1804,6 +1784,9 @@ class BaseType {
1804
1784
  // istanbul ignore next
1805
1785
  throw fail("unimplemented method");
1806
1786
  }
1787
+ describe() {
1788
+ return this.name;
1789
+ }
1807
1790
  isAssignableFrom(type) {
1808
1791
  return type === this;
1809
1792
  }
@@ -1949,7 +1932,7 @@ class SimpleType extends BaseType {
1949
1932
  * @returns `true` if the value is a type.
1950
1933
  */
1951
1934
  function isType(value) {
1952
- return typeof value === "object" && value?.isType === true;
1935
+ return (typeof value === "object" && value?.isType === true);
1953
1936
  }
1954
1937
  /**
1955
1938
  * @internal
@@ -1959,7 +1942,6 @@ function assertIsType(type, argNumber) {
1959
1942
  assertArg(type, isType, "mobx-state-tree type", argNumber);
1960
1943
  }
1961
1944
 
1962
- const runningActions = new Map();
1963
1945
  /**
1964
1946
  * Note: Consider migrating to `createActionTrackingMiddleware2`, it is easier to use.
1965
1947
  *
@@ -1975,6 +1957,12 @@ const runningActions = new Map();
1975
1957
  * @returns
1976
1958
  */
1977
1959
  function createActionTrackingMiddleware(hooks) {
1960
+ // per middleware, not per module: the entries hold the `context` this
1961
+ // middleware's own onStart returned, so two middlewares on one tree would
1962
+ // otherwise overwrite each other's context and then delete the shared entry —
1963
+ // whichever saw `flow_return` second dereferenced `undefined`.
1964
+ // createActionTrackingMiddleware2 scopes its map the same way.
1965
+ const runningActions = new Map();
1978
1966
  return function actionTrackingMiddleware(call, next, _abort) {
1979
1967
  switch (call.type) {
1980
1968
  case "action": {
@@ -2679,10 +2667,6 @@ function safeStringify(value) {
2679
2667
  return `<Unserializable: ${e}>`;
2680
2668
  }
2681
2669
  }
2682
- /**
2683
- * @internal
2684
- * @hidden
2685
- */
2686
2670
  function prettyPrintValue(value) {
2687
2671
  return typeof value === "function"
2688
2672
  ? `<function${value.name ? ` ${value.name}` : ""}>`
@@ -2697,6 +2681,8 @@ function shortenPrintValue(valueInString) {
2697
2681
  }
2698
2682
  function toErrorString(error) {
2699
2683
  const { value } = error;
2684
+ // every context entry carries a type: they are built by getContextForPath and
2685
+ // by typecheck's initial `[{ path: "", type }]`
2700
2686
  const type = error.context[error.context.length - 1].type;
2701
2687
  const fullPath = error.context
2702
2688
  .map(({ path }) => path)
@@ -2708,14 +2694,17 @@ function toErrorString(error) {
2708
2694
  : isPrimitive(value)
2709
2695
  ? "value"
2710
2696
  : "snapshot";
2711
- const isSnapshotCompatible = type && isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot);
2712
- return `${pathPrefix}${currentTypename} ${shortenPrintValue(prettyPrintValue(value))} is not assignable ${type ? `to type: \`${type.name}\`` : ``}${error.message ? ` (${error.message})` : ""}${type
2713
- ? isPrimitiveType(type) || isPrimitive(value)
2714
- ? `.`
2715
- : `, expected an instance of \`${type.name}\` or a snapshot like \`${shortenPrintValue(type.describe())}\` instead.${isSnapshotCompatible
2716
- ? " (Note that a snapshot of the provided value is compatible with the targeted type)"
2717
- : ""}`
2718
- : `.`}`;
2697
+ let expectation;
2698
+ if (isPrimitiveType(type) || isPrimitive(value)) {
2699
+ expectation = ".";
2700
+ }
2701
+ else {
2702
+ const isSnapshotCompatible = isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot);
2703
+ expectation = `, expected an instance of \`${type.name}\` or a snapshot like \`${shortenPrintValue(type.describe())}\` instead.${isSnapshotCompatible
2704
+ ? " (Note that a snapshot of the provided value is compatible with the targeted type)"
2705
+ : ""}`;
2706
+ }
2707
+ return `${pathPrefix}${currentTypename} ${shortenPrintValue(prettyPrintValue(value))} is not assignable to type: \`${type.name}\`${error.message ? ` (${error.message})` : ""}${expectation}`;
2719
2708
  }
2720
2709
  /**
2721
2710
  * @internal
@@ -2992,7 +2981,7 @@ function getStateTreeNode(value) {
2992
2981
  * @hidden
2993
2982
  */
2994
2983
  function getStateTreeNodeSafe(value) {
2995
- return value?.$treenode || null;
2984
+ return value?.["$treenode"] || null;
2996
2985
  }
2997
2986
  /**
2998
2987
  * @internal
@@ -3304,9 +3293,6 @@ class EventHandler {
3304
3293
  this.handlers.splice(index, 1);
3305
3294
  }
3306
3295
  }
3307
- clear() {
3308
- this.handlers.length = 0;
3309
- }
3310
3296
  emit(...args) {
3311
3297
  const handlers = this.handlers;
3312
3298
  // 0 and 1 handlers are by far the common cases (a patch/snapshot listener, a
@@ -3451,21 +3437,6 @@ function assertIsString(value, argNumber, canBeEmpty = true) {
3451
3437
  assertArg(value, s => s !== "", "not empty string", argNumber);
3452
3438
  }
3453
3439
  }
3454
- /**
3455
- * @internal
3456
- * @hidden
3457
- */
3458
- function setImmediateWithFallback(fn) {
3459
- if (typeof queueMicrotask === "function") {
3460
- queueMicrotask(fn);
3461
- }
3462
- else if (typeof setImmediate === "function") {
3463
- setImmediate(fn);
3464
- }
3465
- else {
3466
- setTimeout(fn, 1);
3467
- }
3468
- }
3469
3440
 
3470
3441
  /**
3471
3442
  * See [asynchronous actions](concepts/async-actions.md).
@@ -3536,14 +3507,12 @@ function toGeneratorFunction(p) {
3536
3507
  function* toGenerator(p) {
3537
3508
  return (yield p);
3538
3509
  }
3539
- /**
3540
- * @internal
3541
- * @hidden
3542
- */
3543
3510
  function createFlowSpawner(name, generator) {
3544
3511
  const spawner = function flowSpawner(...flowArgs) {
3545
3512
  // Implementation based on https://github.com/tj/co/blob/master/index.js
3546
3513
  const runId = getNextActionId();
3514
+ // no `!`: the guard below is the point, and asserting non-null first made it
3515
+ // dead to the checker while leaving it live at runtime
3547
3516
  const parentContext = getCurrentActionContext();
3548
3517
  if (!parentContext) {
3549
3518
  throw fail("a mst flow must always have a parent context");
@@ -3595,7 +3564,7 @@ function createFlowSpawner(name, generator) {
3595
3564
  }
3596
3565
  catch (e) {
3597
3566
  // prettier-ignore
3598
- setImmediateWithFallback(() => {
3567
+ queueMicrotask(() => {
3599
3568
  wrap((_r) => { reject(e); }, "flow_throw", e);
3600
3569
  });
3601
3570
  return;
@@ -3611,7 +3580,7 @@ function createFlowSpawner(name, generator) {
3611
3580
  }
3612
3581
  catch (e) {
3613
3582
  // prettier-ignore
3614
- setImmediateWithFallback(() => {
3583
+ queueMicrotask(() => {
3615
3584
  wrap((_r) => { reject(e); }, "flow_throw", e);
3616
3585
  });
3617
3586
  return;
@@ -3621,7 +3590,7 @@ function createFlowSpawner(name, generator) {
3621
3590
  function next(ret) {
3622
3591
  if (ret.done) {
3623
3592
  // prettier-ignore
3624
- setImmediateWithFallback(() => {
3593
+ queueMicrotask(() => {
3625
3594
  wrap((r) => { resolve(r); }, "flow_return", ret.value);
3626
3595
  });
3627
3596
  return;
@@ -3649,10 +3618,6 @@ function splitPatch(patch) {
3649
3618
  }
3650
3619
  return [stripPatch(patch), invertPatch(patch)];
3651
3620
  }
3652
- /**
3653
- * @internal
3654
- * @hidden
3655
- */
3656
3621
  function stripPatch(patch) {
3657
3622
  // strips `oldvalue` information from the patch, so that it becomes a patch conform the json-patch spec
3658
3623
  // this removes the ability to undo the patch
@@ -3812,8 +3777,10 @@ class SnapshotProcessor extends BaseType {
3812
3777
  return sn;
3813
3778
  }
3814
3779
  _fixNode(node) {
3815
- // the node has to use these methods rather than the original type ones
3816
- proxyNodeTypeMethods(node.type, this, "create");
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);
3817
3784
  if (node instanceof ObjectNode) {
3818
3785
  node.hasSnapshotPostProcessor = !!this._processors.postProcessor;
3819
3786
  }
@@ -3882,11 +3849,6 @@ class SnapshotProcessor extends BaseType {
3882
3849
  return this._subtype.isMatchingSnapshotId(current, processedSn);
3883
3850
  }
3884
3851
  }
3885
- function proxyNodeTypeMethods(nodeType, snapshotProcessorType, ...methods) {
3886
- for (const method of methods) {
3887
- nodeType[method] = snapshotProcessorType[method].bind(snapshotProcessorType);
3888
- }
3889
- }
3890
3852
  /**
3891
3853
  * `types.snapshotProcessor` - Runs a pre/post snapshot processor before/after serializing a given type.
3892
3854
  *
@@ -3937,6 +3899,41 @@ function snapshotProcessor(type, processors, name) {
3937
3899
  return new SnapshotProcessor(type, processors, name);
3938
3900
  }
3939
3901
 
3902
+ /**
3903
+ * Append `hooks` to a collection type's initializer list. `EMPTY_ARRAY` is the
3904
+ * shared default, so a type that never calls `.hooks()` allocates nothing.
3905
+ *
3906
+ * @internal
3907
+ * @hidden
3908
+ */
3909
+ function appendHookInitializer(current, hooks) {
3910
+ return current.length > 0 ? [...current, hooks] : [hooks];
3911
+ }
3912
+ /**
3913
+ * Install every hook the type's initializers produce onto `instance` as an MST
3914
+ * action, mirroring how a model's `.actions()` members are attached.
3915
+ *
3916
+ * @internal
3917
+ * @hidden
3918
+ */
3919
+ function installHookInitializers(hookInitializers, instance) {
3920
+ const addProp = !devMode() ? addHiddenFinalProp : addHiddenWritableProp;
3921
+ for (const initializer of hookInitializers) {
3922
+ const hooks = initializer(instance);
3923
+ for (const name of Object.keys(hooks)) {
3924
+ const hook = hooks[name];
3925
+ addProp(instance, name, createActionInvoker(instance, name, hook));
3926
+ }
3927
+ }
3928
+ }
3929
+ /**
3930
+ * The empty initializer list every collection type starts with.
3931
+ *
3932
+ * @internal
3933
+ * @hidden
3934
+ */
3935
+ const NO_HOOK_INITIALIZERS = EMPTY_ARRAY;
3936
+
3940
3937
  const needsIdentifierError = `Map.put can only be used to store complex values that have an identifier type attribute`;
3941
3938
  function tryCollectModelTypes(type, modelTypes) {
3942
3939
  const subtypes = type.getSubTypes();
@@ -3967,13 +3964,6 @@ var MapIdentifierMode;
3967
3964
  MapIdentifierMode[MapIdentifierMode["NO"] = 2] = "NO";
3968
3965
  })(MapIdentifierMode || (MapIdentifierMode = {}));
3969
3966
  class MSTMap extends ObservableMap {
3970
- constructor(initialData, name) {
3971
- // mobx's default (deep) enhancer. The old `observable.ref.enhancer` arg
3972
- // read undefined ever since mobx 6 renamed the field to `options_.enhancer`,
3973
- // so deep has been the effective behavior all along; mobx 7 dropped
3974
- // `observable.ref` entirely.
3975
- super(initialData, undefined, name);
3976
- }
3977
3967
  get(key) {
3978
3968
  // maybe this is over-enthousiastic? normalize numeric keys to strings
3979
3969
  return super.get(`${key}`);
@@ -4033,24 +4023,21 @@ class MSTMap extends ObservableMap {
4033
4023
  */
4034
4024
  class MapType extends ComplexType {
4035
4025
  _subType;
4026
+ hookInitializers;
4036
4027
  identifierMode = MapIdentifierMode.UNKNOWN;
4037
4028
  mapIdentifierAttribute = undefined;
4038
4029
  flags = TypeFlags.Map;
4039
- hookInitializers = [];
4040
- constructor(_subType, hookInitializers = []) {
4030
+ constructor(_subType, hookInitializers = NO_HOOK_INITIALIZERS) {
4041
4031
  super();
4042
4032
  this._subType = _subType;
4043
- this._determineIdentifierMode();
4044
4033
  this.hookInitializers = hookInitializers;
4034
+ this._determineIdentifierMode();
4045
4035
  }
4046
4036
  computeName() {
4047
4037
  return `Map<string, ${this._subType.name}>`;
4048
4038
  }
4049
4039
  hooks(hooks) {
4050
- const hookInitializers = this.hookInitializers.length > 0
4051
- ? this.hookInitializers.concat(hooks)
4052
- : [hooks];
4053
- return new MapType(this._subType, hookInitializers);
4040
+ return new MapType(this._subType, appendHookInitializer(this.hookInitializers, hooks));
4054
4041
  }
4055
4042
  instantiate(parent, subpath, environment, initialValue) {
4056
4043
  this._determineIdentifierMode();
@@ -4089,25 +4076,15 @@ class MapType extends ComplexType {
4089
4076
  return result;
4090
4077
  }
4091
4078
  createNewInstance(childNodes) {
4092
- return new MSTMap(childNodes, this.name);
4079
+ // undefined is mobx's enhancer argument, left at its deep default
4080
+ return new MSTMap(childNodes, undefined, this.name);
4093
4081
  }
4094
4082
  finalizeNewInstance(node, instance) {
4095
4083
  _interceptReads(instance, node.unbox);
4096
- const type = node.type;
4097
- type.hookInitializers.forEach(initializer => {
4098
- const hooks = initializer(instance);
4099
- Object.keys(hooks).forEach(name => {
4100
- const hook = hooks[name];
4101
- const actionInvoker = createActionInvoker(instance, name, hook);
4102
- (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(instance, name, actionInvoker);
4103
- });
4104
- });
4084
+ installHookInitializers(node.type.hookInitializers, instance);
4105
4085
  intercept(instance, this.willChange);
4106
4086
  observe(instance, this.didChange);
4107
4087
  }
4108
- describe() {
4109
- return this.name;
4110
- }
4111
4088
  getChildren(node) {
4112
4089
  // return (node.storedValue as ObservableMap<any>).values()
4113
4090
  return values(node.storedValue);
@@ -4284,6 +4261,7 @@ MapType.prototype.applySnapshot = action(MapType.prototype.applySnapshot);
4284
4261
  * @returns
4285
4262
  */
4286
4263
  function map(subtype) {
4264
+ assertIsType(subtype, 1);
4287
4265
  return new MapType(subtype);
4288
4266
  }
4289
4267
  /**
@@ -4302,9 +4280,9 @@ function isMapType(type) {
4302
4280
  */
4303
4281
  class ArrayType extends ComplexType {
4304
4282
  _subType;
4283
+ hookInitializers;
4305
4284
  flags = TypeFlags.Array;
4306
- hookInitializers = [];
4307
- constructor(_subType, hookInitializers = []) {
4285
+ constructor(_subType, hookInitializers = NO_HOOK_INITIALIZERS) {
4308
4286
  super();
4309
4287
  this._subType = _subType;
4310
4288
  this.hookInitializers = hookInitializers;
@@ -4313,10 +4291,7 @@ class ArrayType extends ComplexType {
4313
4291
  return `${this._subType.name}[]`;
4314
4292
  }
4315
4293
  hooks(hooks) {
4316
- const hookInitializers = this.hookInitializers.length > 0
4317
- ? this.hookInitializers.concat(hooks)
4318
- : [hooks];
4319
- return new ArrayType(this._subType, hookInitializers);
4294
+ return new ArrayType(this._subType, appendHookInitializer(this.hookInitializers, hooks));
4320
4295
  }
4321
4296
  instantiate(parent, subpath, environment, initialValue) {
4322
4297
  return createObjectNode(this, parent, subpath, environment, initialValue);
@@ -4336,21 +4311,10 @@ class ArrayType extends ComplexType {
4336
4311
  }
4337
4312
  finalizeNewInstance(node, instance) {
4338
4313
  _getAdministration(instance).dehancer = node.unbox;
4339
- const type = node.type;
4340
- type.hookInitializers.forEach(initializer => {
4341
- const hooks = initializer(instance);
4342
- Object.keys(hooks).forEach(name => {
4343
- const hook = hooks[name];
4344
- const actionInvoker = createActionInvoker(instance, name, hook);
4345
- (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(instance, name, actionInvoker);
4346
- });
4347
- });
4314
+ installHookInitializers(node.type.hookInitializers, instance);
4348
4315
  intercept(instance, this.willChange);
4349
4316
  observe(instance, this.didChange);
4350
4317
  }
4351
- describe() {
4352
- return this.name;
4353
- }
4354
4318
  getChildren(node) {
4355
4319
  return node.storedValue.slice();
4356
4320
  }
@@ -4720,11 +4684,10 @@ function getPropObservables(storedValue) {
4720
4684
  function objectTypeToString() {
4721
4685
  return getStateTreeNode(this).toString();
4722
4686
  }
4723
- const defaultObjectOptions = {
4724
- name: "AnonymousModel"};
4687
+ const ANONYMOUS_MODEL_NAME = "AnonymousModel";
4725
4688
  /**
4726
- * A plain loop rather than `forAllProps`, which allocated a closure and made an
4727
- * indirect call per property. This runs once per `types.model()` over every
4689
+ * A plain loop rather than a callback per property, which allocated a closure
4690
+ * and made an indirect call. This runs once per `types.model()` over every
4728
4691
  * declared property, and jbrowse builds a ~40-slot schema per track.
4729
4692
  */
4730
4693
  function findIdentifierAttribute(properties, propertyNames) {
@@ -4807,7 +4770,7 @@ class ModelType extends ComplexType {
4807
4770
  // `??`, not `||`: `types.model("", {})` names the model "". The old
4808
4771
  // `Object.assign(this, defaults, opts)` below overwrote the `||` fallback
4809
4772
  // with opts.name afterwards, so that only worked by accident.
4810
- super(opts.name ?? defaultObjectOptions.name);
4773
+ super(opts.name ?? ANONYMOUS_MODEL_NAME);
4811
4774
  // Every field is assigned here, unconditionally and in a fixed order,
4812
4775
  // rather than by `Object.assign(this, defaultObjectOptions, opts)`. `opts`
4813
4776
  // carries a different key set at each of the three call sites — `model()`,
@@ -4922,12 +4885,6 @@ class ModelType extends ComplexType {
4922
4885
  (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(self, name, actionInvoker);
4923
4886
  });
4924
4887
  }
4925
- named = name => {
4926
- return this.cloneAndEnhance({ name });
4927
- };
4928
- props = properties => {
4929
- return this.cloneAndEnhance({ properties });
4930
- };
4931
4888
  volatile(fn) {
4932
4889
  if (typeof fn !== "function") {
4933
4890
  throw fail(`You passed an ${typeof fn} to volatile state as an argument, when function is expected`);
@@ -4947,19 +4904,7 @@ class ModelType extends ComplexType {
4947
4904
  }
4948
4905
  extend(fn) {
4949
4906
  const initializer = (self) => {
4950
- const { actions, views, state, ...rest } = fn(self);
4951
- for (const key in rest) {
4952
- throw fail(`The \`extend\` function should return an object with a subset of the fields 'actions', 'views' and 'state'. Found invalid key '${key}'`);
4953
- }
4954
- if (state) {
4955
- this.instantiateVolatileState(self, state);
4956
- }
4957
- if (views) {
4958
- this.instantiateViews(self, views);
4959
- }
4960
- if (actions) {
4961
- this.instantiateActions(self, actions);
4962
- }
4907
+ this.applyExtension(self, fn(self), "The `extend` function");
4963
4908
  return self;
4964
4909
  };
4965
4910
  return this.cloneAndEnhance({ initializers: [initializer] });
@@ -4980,9 +4925,17 @@ class ModelType extends ComplexType {
4980
4925
  * are permitted (see extendInstance, which runs this in an action context).
4981
4926
  */
4982
4927
  applyExtensionToInstance(self, extension) {
4928
+ this.applyExtension(self, extension, "extendInstance");
4929
+ }
4930
+ /**
4931
+ * Materializes an `{ actions, views, state }` bundle onto an instance. Shared
4932
+ * by `.extend()` (at creation time) and `applyExtensionToInstance` (on a live
4933
+ * instance); `subject` only names the caller in the invalid-key error.
4934
+ */
4935
+ applyExtension(self, extension, subject) {
4983
4936
  const { actions, views, state, ...rest } = extension;
4984
4937
  for (const key in rest) {
4985
- throw fail(`extendInstance should return an object with a subset of the fields 'actions', 'views' and 'state'. Found invalid key '${key}'`);
4938
+ throw fail(`${subject} should return an object with a subset of the fields 'actions', 'views' and 'state'. Found invalid key '${key}'`);
4986
4939
  }
4987
4940
  if (state) {
4988
4941
  this.instantiateVolatileState(self, state);
@@ -5014,28 +4967,6 @@ class ModelType extends ComplexType {
5014
4967
  }
5015
4968
  });
5016
4969
  }
5017
- preProcessSnapshot = preProcessor => {
5018
- const currentPreprocessor = this.preProcessor;
5019
- if (!currentPreprocessor) {
5020
- return this.cloneAndEnhance({ preProcessor });
5021
- }
5022
- else {
5023
- return this.cloneAndEnhance({
5024
- preProcessor: snapshot => currentPreprocessor(preProcessor(snapshot))
5025
- });
5026
- }
5027
- };
5028
- postProcessSnapshot = postProcessor => {
5029
- const currentPostprocessor = this.postProcessor;
5030
- if (!currentPostprocessor) {
5031
- return this.cloneAndEnhance({ postProcessor });
5032
- }
5033
- else {
5034
- return this.cloneAndEnhance({
5035
- postProcessor: snapshot => postProcessor(currentPostprocessor(snapshot))
5036
- });
5037
- }
5038
- };
5039
4970
  instantiate(parent, subpath, environment, initialValue) {
5040
4971
  const value = isStateTreeNode(initialValue)
5041
4972
  ? initialValue
@@ -5046,10 +4977,11 @@ class ModelType extends ComplexType {
5046
4977
  }
5047
4978
  initializeChildNodes(objNode, initialSnapshot = {}) {
5048
4979
  const type = objNode.type;
4980
+ const properties = type.properties;
5049
4981
  const result = {};
5050
- type.forAllProps((name, childType) => {
5051
- result[name] = childType.instantiate(objNode, name, undefined, initialSnapshot[name]);
5052
- });
4982
+ for (const name of type.propertyNames) {
4983
+ result[name] = properties[name].instantiate(objNode, name, undefined, initialSnapshot[name]);
4984
+ }
5053
4985
  return result;
5054
4986
  }
5055
4987
  createNewInstance(childNodes) {
@@ -5068,9 +5000,9 @@ class ModelType extends ComplexType {
5068
5000
  }
5069
5001
  }
5070
5002
  else {
5071
- this.forAllProps(name => {
5003
+ for (const name of this.propertyNames) {
5072
5004
  _interceptReads(instance, name, node.unbox);
5073
- });
5005
+ }
5074
5006
  }
5075
5007
  this.initializers.reduce((self, fn) => fn(self), instance);
5076
5008
  // views, actions and volatile share the instance namespace with properties,
@@ -5081,12 +5013,12 @@ class ModelType extends ComplexType {
5081
5013
  // function/value stops being observable at all. The flag is only set on
5082
5014
  // success so a broken type keeps throwing on every create.
5083
5015
  if (!this.duplicateKeysChecked) {
5084
- this.forAllProps(name => {
5016
+ for (const name of this.propertyNames) {
5085
5017
  if (isComputedProp(instance, name) ||
5086
5018
  !isObservableProp(instance, name)) {
5087
5019
  throw fail(`${name} property is declared twice`);
5088
5020
  }
5089
- });
5021
+ }
5090
5022
  this.duplicateKeysChecked = true;
5091
5023
  }
5092
5024
  intercept(instance, this.willChange);
@@ -5139,24 +5071,16 @@ class ModelType extends ComplexType {
5139
5071
  }
5140
5072
  return res;
5141
5073
  }
5142
- /**
5143
- * Same as {@link getChildNode} but for a key already known to be a declared
5144
- * property (callers iterating `propertyNames`), so the membership guard is
5145
- * skipped.
5146
- */
5147
- getPropertyNode(node, key) {
5074
+ getChildNode(node, key) {
5075
+ if (!(key in this.properties)) {
5076
+ throw fail(`Not a value property: ${key}`);
5077
+ }
5148
5078
  const childNode = getPropObservable(node.storedValue, key).raw();
5149
5079
  if (!childNode) {
5150
5080
  throw fail(`Node not available for property ${key}`);
5151
5081
  }
5152
5082
  return childNode;
5153
5083
  }
5154
- getChildNode(node, key) {
5155
- if (!(key in this.properties)) {
5156
- throw fail(`Not a value property: ${key}`);
5157
- }
5158
- return this.getPropertyNode(node, key);
5159
- }
5160
5084
  getSnapshot(node, applyPostProcess = true) {
5161
5085
  const res = {};
5162
5086
  const storedValue = node.storedValue;
@@ -5208,9 +5132,9 @@ class ModelType extends ComplexType {
5208
5132
  applySnapshot(node, snapshot) {
5209
5133
  typecheckInternal(this, snapshot);
5210
5134
  const preProcessedSnapshot = this.applySnapshotPreProcessor(snapshot);
5211
- this.forAllProps(name => {
5135
+ for (const name of this.propertyNames) {
5212
5136
  node.storedValue[name] = preProcessedSnapshot[name];
5213
- });
5137
+ }
5214
5138
  }
5215
5139
  applySnapshotPreProcessor(snapshot) {
5216
5140
  const processor = this.preProcessor;
@@ -5243,12 +5167,6 @@ class ModelType extends ComplexType {
5243
5167
  }
5244
5168
  return typeCheckSuccess();
5245
5169
  }
5246
- forAllProps(fn) {
5247
- const properties = this.properties;
5248
- for (const key of this.propertyNames) {
5249
- fn(key, properties[key]);
5250
- }
5251
- }
5252
5170
  describe() {
5253
5171
  // optimization: cache
5254
5172
  return `{ ${this.propertyNames
@@ -5263,6 +5181,38 @@ class ModelType extends ComplexType {
5263
5181
  }
5264
5182
  }
5265
5183
  ModelType.prototype.applySnapshot = action(ModelType.prototype.applySnapshot);
5184
+ // Chain steps shared by every model type; see the field declarations in the
5185
+ // class for why they are not own slots.
5186
+ Object.assign(ModelType.prototype, {
5187
+ named(name) {
5188
+ return this.cloneAndEnhance({ name });
5189
+ },
5190
+ props(properties) {
5191
+ return this.cloneAndEnhance({ properties });
5192
+ },
5193
+ 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
+ }
5203
+ },
5204
+ 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
+ }
5214
+ }
5215
+ });
5266
5216
  /**
5267
5217
  * `types.model` - Creates a new model type by providing a name, properties, volatile state and actions.
5268
5218
  *
@@ -5272,7 +5222,7 @@ function model(...args) {
5272
5222
  if (devMode() && typeof args[0] !== "string" && args[1]) {
5273
5223
  throw fail("Model creation failed. First argument must be a string when two arguments are provided");
5274
5224
  }
5275
- const name = typeof args[0] === "string" ? args.shift() : "AnonymousModel";
5225
+ const name = typeof args[0] === "string" ? args.shift() : ANONYMOUS_MODEL_NAME;
5276
5226
  const properties = args.shift() || {};
5277
5227
  return new ModelType({ name, properties });
5278
5228
  }
@@ -5286,7 +5236,7 @@ function model(...args) {
5286
5236
  function compose(...args) {
5287
5237
  // TODO: just join the base type names if no name is provided
5288
5238
  const hasTypename = typeof args[0] === "string";
5289
- const typeName = hasTypename ? args[0] : "AnonymousModel";
5239
+ const typeName = hasTypename ? args[0] : ANONYMOUS_MODEL_NAME;
5290
5240
  if (hasTypename) {
5291
5241
  args.shift();
5292
5242
  }
@@ -5373,9 +5323,6 @@ class CoreType extends SimpleType {
5373
5323
  this.checker = checker;
5374
5324
  this.initializer = initializer;
5375
5325
  }
5376
- describe() {
5377
- return this.name;
5378
- }
5379
5326
  instantiate(parent, subpath, environment, initialValue) {
5380
5327
  return createScalarNode(this, parent, subpath, environment, initialValue);
5381
5328
  }
@@ -5512,6 +5459,12 @@ function getPrimitiveFactoryFromValue(value) {
5512
5459
  /**
5513
5460
  * Returns if a given value represents a primitive type.
5514
5461
  *
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.
5467
+ *
5515
5468
  * @param type
5516
5469
  * @returns
5517
5470
  */
@@ -5536,15 +5489,15 @@ class Literal extends SimpleType {
5536
5489
  value;
5537
5490
  flags = TypeFlags.Literal;
5538
5491
  constructor(value) {
5539
- super(JSON.stringify(value));
5492
+ super();
5540
5493
  this.value = value;
5541
5494
  }
5495
+ computeName() {
5496
+ return JSON.stringify(this.value);
5497
+ }
5542
5498
  instantiate(parent, subpath, environment, initialValue) {
5543
5499
  return createScalarNode(this, parent, subpath, environment, initialValue);
5544
5500
  }
5545
- describe() {
5546
- return JSON.stringify(this.value);
5547
- }
5548
5501
  isValidSnapshot(value, context) {
5549
5502
  if (isPrimitive(value) && value === this.value) {
5550
5503
  return typeCheckSuccess();
@@ -5576,6 +5529,12 @@ function literal(value) {
5576
5529
  /**
5577
5530
  * Returns if a given value represents a literal type.
5578
5531
  *
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.
5537
+ *
5579
5538
  * @param type
5580
5539
  * @returns
5581
5540
  */
@@ -5596,9 +5555,6 @@ class Refinement extends BaseType {
5596
5555
  this._predicate = _predicate;
5597
5556
  this._message = _message;
5598
5557
  }
5599
- describe() {
5600
- return this.name;
5601
- }
5602
5558
  instantiate(parent, subpath, environment, initialValue) {
5603
5559
  // create the child type
5604
5560
  return this._subtype.instantiate(parent, subpath, environment, initialValue);
@@ -5655,6 +5611,12 @@ function refinement(...args) {
5655
5611
  /**
5656
5612
  * Returns if a given value is a refinement type.
5657
5613
  *
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.
5619
+ *
5658
5620
  * @param type
5659
5621
  * @returns
5660
5622
  */
@@ -5724,6 +5686,16 @@ function resolveModelType(type) {
5724
5686
  }
5725
5687
  return undefined;
5726
5688
  }
5689
+ // The quick-match paths below already know a type carries TypeFlags.Literal,
5690
+ // but `is()` still routes through BaseType.validate — a context array, an entry
5691
+ // object and a `$treenode` probe per property, per candidate member. A real
5692
+ // `Literal` stores its primitive, and its isValidSnapshot is exactly value
5693
+ // equality against it, so compare directly. The `instanceof` gate is load
5694
+ // bearing: wrappers (optional, refinement, snapshotProcessor, late, union)
5695
+ // inherit the Literal flag from what they wrap and must keep the full check.
5696
+ function matchesLiteral(type, value) {
5697
+ return type instanceof Literal ? type.value === value : type.is(value);
5698
+ }
5727
5699
  /**
5728
5700
  * @internal
5729
5701
  * @hidden
@@ -5841,10 +5813,10 @@ class Union extends BaseType {
5841
5813
  if (!model) {
5842
5814
  continue;
5843
5815
  }
5844
- const typeProp = model.properties.type;
5816
+ const typeProp = model.properties["type"];
5845
5817
  if (!typeProp ||
5846
5818
  !(typeProp.flags & TypeFlags.Literal) ||
5847
- !typeProp.is(discriminator)) {
5819
+ !matchesLiteral(typeProp, discriminator)) {
5848
5820
  continue;
5849
5821
  }
5850
5822
  if (found) {
@@ -5864,7 +5836,7 @@ class Union extends BaseType {
5864
5836
  this._allMembersDiscriminated = this._types.every(t => {
5865
5837
  const model = resolveModelType(t);
5866
5838
  const typeProp = model &&
5867
- model.properties.type;
5839
+ model.properties["type"];
5868
5840
  return !!typeProp && (typeProp.flags & TypeFlags.Literal) !== 0;
5869
5841
  });
5870
5842
  }
@@ -5938,7 +5910,7 @@ class Union extends BaseType {
5938
5910
  }
5939
5911
  // for literals, check exact value match
5940
5912
  if (flags & TypeFlags.Literal) {
5941
- if (type.is(value)) {
5913
+ if (matchesLiteral(type, value)) {
5942
5914
  return type;
5943
5915
  }
5944
5916
  }
@@ -5970,7 +5942,7 @@ class Union extends BaseType {
5970
5942
  // for literal types, verify the value matches exactly
5971
5943
  // this is critical for discriminated unions
5972
5944
  if (flags & TypeFlags.Literal) {
5973
- if (!propType.is(propValue)) {
5945
+ if (!matchesLiteral(propType, propValue)) {
5974
5946
  return false;
5975
5947
  }
5976
5948
  }
@@ -5992,7 +5964,8 @@ class Union extends BaseType {
5992
5964
  // - A failure is the definitive, scoped error only when every member is
5993
5965
  // discriminated; otherwise a catch-all could still accept the value, so
5994
5966
  // fall through to full validation.
5995
- if (isPlainObject(value) && !isStateTreeNode(value)) {
5967
+ const isSnapshotObject = isPlainObject(value) && !isStateTreeNode(value);
5968
+ if (isSnapshotObject) {
5996
5969
  const discriminator = value.type;
5997
5970
  if (typeof discriminator === "string") {
5998
5971
  const candidate = this._findCandidateByTypeDiscriminator(discriminator);
@@ -6008,7 +5981,7 @@ class Union extends BaseType {
6008
5981
  // for plain-object snapshots, prefer union members whose literal-typed
6009
5982
  // discriminator properties match the value (e.g. {type: "MsaView"})
6010
5983
  // so error output is scoped to the intended branch instead of every member
6011
- const candidates = isPlainObject(value) && !isStateTreeNode(value)
5984
+ const candidates = isSnapshotObject
6012
5985
  ? this._types.filter(t => this.snapshotLooksLikeType(value, t))
6013
5986
  : [];
6014
5987
  const typesToValidate = candidates.length > 0 ? candidates : this._types;
@@ -6045,6 +6018,22 @@ Object.assign(Union.prototype, {
6045
6018
  _discriminatorCache: undefined,
6046
6019
  _allMembersDiscriminated: undefined
6047
6020
  });
6021
+ const unionInternRoot = {};
6022
+ function internUnion(types) {
6023
+ let node = unionInternRoot;
6024
+ for (const type of types) {
6025
+ const level = (node.next ??= new WeakMap());
6026
+ const existing = level.get(type);
6027
+ if (existing) {
6028
+ node = existing;
6029
+ }
6030
+ else {
6031
+ node = {};
6032
+ level.set(type, node);
6033
+ }
6034
+ }
6035
+ return (node.union ??= new Union(types));
6036
+ }
6048
6037
  /**
6049
6038
  * `types.union` - Create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function of the form `(snapshot) => Type`.
6050
6039
  *
@@ -6073,11 +6062,17 @@ function union(...args) {
6073
6062
  assertIsType(type, options ? i + 2 : i + 1);
6074
6063
  });
6075
6064
  }
6076
- return new Union(types, options);
6065
+ return options === undefined ? internUnion(types) : new Union(types, options);
6077
6066
  }
6078
6067
  /**
6079
6068
  * Returns if a given value represents a union type.
6080
6069
  *
6070
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6071
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6072
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6073
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
6074
+ * `isMapType`, `isModelType`) keep their predicate.
6075
+ *
6081
6076
  * @param type
6082
6077
  * @returns
6083
6078
  */
@@ -6256,13 +6251,41 @@ const undefinedAsOptionalValues = [undefined];
6256
6251
  /**
6257
6252
  * Returns if a value represents an optional type.
6258
6253
  *
6259
- * @template IT
6254
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6255
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6256
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6257
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
6258
+ * `isMapType`, `isModelType`) keep their predicate.
6259
+ *
6260
6260
  * @param type
6261
6261
  * @returns
6262
6262
  */
6263
6263
  function isOptionalType(type) {
6264
6264
  return isType(type) && (type.flags & TypeFlags.Optional) > 0;
6265
6265
  }
6266
+ function isRecord(value) {
6267
+ return typeof value === "object" && value !== null;
6268
+ }
6269
+ /**
6270
+ * The identifier attribute of the model a stripDefault ultimately wraps, or
6271
+ * `null` when there is none. Drills through the single-subtype wrappers the way
6272
+ * `resolveModelType` in union.ts does — that one is module-private there, and
6273
+ * this file may not reach into it.
6274
+ *
6275
+ * `null` is also the answer for an unresolved `types.late`, which only costs the
6276
+ * short-circuit, never correctness: the full structural walk still runs.
6277
+ */
6278
+ function resolveIdentifierAttribute(type) {
6279
+ let current = type;
6280
+ for (let depth = 0; current && depth < 20; depth++) {
6281
+ if (current instanceof ModelType) {
6282
+ return current.identifierAttribute ? current.identifierAttribute : null;
6283
+ }
6284
+ const wrapper = current;
6285
+ current = wrapper._subtype ?? wrapper.getSubType?.(false);
6286
+ }
6287
+ return null;
6288
+ }
6266
6289
  /**
6267
6290
  * Compare a child snapshot to a stripped-default's reference snapshot: identity
6268
6291
  * for primitives, structural for objects/arrays.
@@ -6341,6 +6364,22 @@ function defaultSnapshotEquals(a, b) {
6341
6364
  * @internal
6342
6365
  */
6343
6366
  class StripDefaultValue extends OptionalValue {
6367
+ equalsDefault(snapshot, defaultSnapshot) {
6368
+ let identifierAttribute = this._identifierAttribute;
6369
+ if (identifierAttribute === undefined) {
6370
+ identifierAttribute = resolveIdentifierAttribute(this.getSubTypes());
6371
+ this._identifierAttribute = identifierAttribute;
6372
+ }
6373
+ // an identified model's snapshot normally has the same shape as the default
6374
+ // and differs only here, so answering from one key beats walking every key
6375
+ // (including nested objects) until the walk reaches it
6376
+ return identifierAttribute !== null &&
6377
+ isRecord(snapshot) &&
6378
+ isRecord(defaultSnapshot) &&
6379
+ snapshot[identifierAttribute] !== defaultSnapshot[identifierAttribute]
6380
+ ? false
6381
+ : defaultSnapshotEquals(snapshot, defaultSnapshot);
6382
+ }
6344
6383
  shouldStripFromSnapshot(snapshot) {
6345
6384
  if (!this._defaultSnapshot) {
6346
6385
  // instantiate the subtype detached with the default and read the node's
@@ -6349,11 +6388,35 @@ class StripDefaultValue extends OptionalValue {
6349
6388
  const node = this.getSubTypes().instantiate(null, "", undefined, this.getDefaultInstanceOrSnapshot());
6350
6389
  this._defaultSnapshot = { value: node.snapshot };
6351
6390
  }
6352
- return defaultSnapshotEquals(snapshot, this._defaultSnapshot.value);
6391
+ const defaultSnapshot = this._defaultSnapshot.value;
6392
+ let result;
6393
+ if (isRecord(snapshot)) {
6394
+ // a child node's snapshot is a keepAlive computed, so a stable reference
6395
+ // means unchanged content: the same object always gets the same answer
6396
+ let cache = this._equalsDefaultCache;
6397
+ if (!cache) {
6398
+ cache = new WeakMap();
6399
+ this._equalsDefaultCache = cache;
6400
+ }
6401
+ const cached = cache.get(snapshot);
6402
+ if (cached === undefined) {
6403
+ result = this.equalsDefault(snapshot, defaultSnapshot);
6404
+ cache.set(snapshot, result);
6405
+ }
6406
+ else {
6407
+ result = cached;
6408
+ }
6409
+ }
6410
+ else {
6411
+ result = defaultSnapshotEquals(snapshot, defaultSnapshot);
6412
+ }
6413
+ return result;
6353
6414
  }
6354
6415
  }
6355
6416
  Object.assign(StripDefaultValue.prototype, {
6356
- _defaultSnapshot: undefined
6417
+ _defaultSnapshot: undefined,
6418
+ _identifierAttribute: undefined,
6419
+ _equalsDefaultCache: undefined
6357
6420
  });
6358
6421
  /**
6359
6422
  * Whether `type` is a strip-default optional whose current child `snapshot`
@@ -6502,6 +6565,12 @@ function late(nameOrType, maybeType) {
6502
6565
  /**
6503
6566
  * Returns if a given value represents a late type.
6504
6567
  *
6568
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6569
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6570
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6571
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
6572
+ * `isMapType`, `isModelType`) keep their predicate.
6573
+ *
6505
6574
  * @param type
6506
6575
  * @returns
6507
6576
  */
@@ -6670,6 +6739,12 @@ function frozen(arg) {
6670
6739
  /**
6671
6740
  * Returns if a given value represents a frozen type.
6672
6741
  *
6742
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
6743
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
6744
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
6745
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
6746
+ * `isMapType`, `isModelType`) keep their predicate.
6747
+ *
6673
6748
  * @param type
6674
6749
  * @returns
6675
6750
  */
@@ -6760,9 +6835,6 @@ class BaseReferenceType extends SimpleType {
6760
6835
  computeName() {
6761
6836
  return `reference(${this.targetType.name})`;
6762
6837
  }
6763
- describe() {
6764
- return this.name;
6765
- }
6766
6838
  isAssignableFrom(type) {
6767
6839
  return this.targetType.isAssignableFrom(type);
6768
6840
  }
@@ -6900,9 +6972,6 @@ class BaseReferenceType extends SimpleType {
6900
6972
  * @hidden
6901
6973
  */
6902
6974
  class IdentifierReferenceType extends BaseReferenceType {
6903
- constructor(targetType, onInvalidated) {
6904
- super(targetType, onInvalidated);
6905
- }
6906
6975
  getValue(storedRefNode) {
6907
6976
  if (!storedRefNode.isAlive) {
6908
6977
  return undefined;
@@ -7018,6 +7087,12 @@ function reference(subType, options) {
7018
7087
  /**
7019
7088
  * Returns if a given value represents a reference type.
7020
7089
  *
7090
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
7091
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
7092
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
7093
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
7094
+ * `isMapType`, `isModelType`) keep their predicate.
7095
+ *
7021
7096
  * @param type
7022
7097
  * @returns
7023
7098
  */
@@ -7096,9 +7171,6 @@ class IdentifierType extends BaseIdentifierType {
7096
7171
  constructor() {
7097
7172
  super(`identifier`, "string");
7098
7173
  }
7099
- describe() {
7100
- return `identifier`;
7101
- }
7102
7174
  }
7103
7175
  /**
7104
7176
  * @internal
@@ -7108,12 +7180,6 @@ class IdentifierNumberType extends BaseIdentifierType {
7108
7180
  constructor() {
7109
7181
  super("identifierNumber", "number");
7110
7182
  }
7111
- getSnapshot(node) {
7112
- return node.storedValue;
7113
- }
7114
- describe() {
7115
- return `identifierNumber`;
7116
- }
7117
7183
  }
7118
7184
  /**
7119
7185
  * `types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works.
@@ -7150,6 +7216,12 @@ const identifierNumber = new IdentifierNumberType();
7150
7216
  /**
7151
7217
  * Returns if a given value represents an identifier type.
7152
7218
  *
7219
+ * Returns a plain `boolean`, not a `type is IT` predicate: with the parameter
7220
+ * typed as `IT`, narrowing to `IT` was a no-op in the positive branch while
7221
+ * collapsing the negative one to `never`, so `if (!isX(t)) { t.name }` failed
7222
+ * to compile. Guards with a distinct narrowing target (`isArrayType`,
7223
+ * `isMapType`, `isModelType`) keep their predicate.
7224
+ *
7153
7225
  * @param type
7154
7226
  * @returns
7155
7227
  */
@@ -7238,9 +7310,6 @@ class CustomType extends SimpleType {
7238
7310
  super(options.name);
7239
7311
  this.options = options;
7240
7312
  }
7241
- describe() {
7242
- return this.name;
7243
- }
7244
7313
  isValidSnapshot(value, context) {
7245
7314
  if (this.options.isTargetType(value)) {
7246
7315
  return typeCheckSuccess();
@@ -7444,5 +7513,5 @@ const types = {
7444
7513
  resilient
7445
7514
  };
7446
7515
 
7447
- export { addDisposer, addMiddleware, applyAction, applyPatch, applySnapshot, 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 };
7516
+ 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 };
7448
7517
  //# sourceMappingURL=mobx-state-tree.mjs.map