@jbrowse/mobx-state-tree 6.3.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.
@@ -272,6 +272,8 @@ function unprotect(target) {
272
272
  * Returns true if the object is in protected mode, @see protect
273
273
  */
274
274
  function isProtected(target) {
275
+ // check all arguments
276
+ assertIsStateTreeNode(target, 1);
275
277
  return getStateTreeNode(target).isProtected;
276
278
  }
277
279
  /**
@@ -303,6 +305,27 @@ function getSnapshot(target, applyPostProcess = true) {
303
305
  }
304
306
  return freeze(node.type.getSnapshot(node, false));
305
307
  }
308
+ function findParentAtDepth(node, depth) {
309
+ let remaining = depth;
310
+ let parent = node.parent;
311
+ while (parent) {
312
+ if (--remaining === 0) {
313
+ return parent;
314
+ }
315
+ parent = parent.parent;
316
+ }
317
+ return null;
318
+ }
319
+ function findParentOfType(node, type) {
320
+ let parent = node.parent;
321
+ while (parent) {
322
+ if (type.is(parent.storedValue)) {
323
+ return parent;
324
+ }
325
+ parent = parent.parent;
326
+ }
327
+ return null;
328
+ }
306
329
  /**
307
330
  * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array.
308
331
  *
@@ -314,14 +337,7 @@ function hasParent(target, depth = 1) {
314
337
  // check all arguments
315
338
  assertIsStateTreeNode(target, 1);
316
339
  assertIsNumber(depth, 2, 0);
317
- let parent = getStateTreeNode(target).parent;
318
- while (parent) {
319
- if (--depth === 0) {
320
- return true;
321
- }
322
- parent = parent.parent;
323
- }
324
- return false;
340
+ return findParentAtDepth(getStateTreeNode(target), depth) !== null;
325
341
  }
326
342
  /**
327
343
  * Returns the immediate parent of this object, or throws.
@@ -340,15 +356,11 @@ function getParent(target, depth = 1) {
340
356
  // check all arguments
341
357
  assertIsStateTreeNode(target, 1);
342
358
  assertIsNumber(depth, 2, 0);
343
- let d = depth;
344
- let parent = getStateTreeNode(target).parent;
345
- while (parent) {
346
- if (--d === 0) {
347
- return parent.storedValue;
348
- }
349
- parent = parent.parent;
359
+ const parent = findParentAtDepth(getStateTreeNode(target), depth);
360
+ if (!parent) {
361
+ throw fail(`Failed to find the parent of ${getStateTreeNode(target)} at depth ${depth}`);
350
362
  }
351
- throw fail(`Failed to find the parent of ${getStateTreeNode(target)} at depth ${depth}`);
363
+ return parent.storedValue;
352
364
  }
353
365
  /**
354
366
  * 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
@@ -361,14 +373,7 @@ function hasParentOfType(target, type) {
361
373
  // check all arguments
362
374
  assertIsStateTreeNode(target, 1);
363
375
  assertIsType(type, 2);
364
- let parent = getStateTreeNode(target).parent;
365
- while (parent) {
366
- if (type.is(parent.storedValue)) {
367
- return true;
368
- }
369
- parent = parent.parent;
370
- }
371
- return false;
376
+ return findParentOfType(getStateTreeNode(target), type) !== null;
372
377
  }
373
378
  /**
374
379
  * Returns the target's parent of a given type, or throws.
@@ -381,14 +386,11 @@ function getParentOfType(target, type) {
381
386
  // check all arguments
382
387
  assertIsStateTreeNode(target, 1);
383
388
  assertIsType(type, 2);
384
- let parent = getStateTreeNode(target).parent;
385
- while (parent) {
386
- if (type.is(parent.storedValue)) {
387
- return parent.storedValue;
388
- }
389
- parent = parent.parent;
389
+ const parent = findParentOfType(getStateTreeNode(target), type);
390
+ if (!parent) {
391
+ throw fail(`Failed to find the parent of ${getStateTreeNode(target)} of a given type`);
390
392
  }
391
- throw fail(`Failed to find the parent of ${getStateTreeNode(target)} of a given type`);
393
+ return parent.storedValue;
392
394
  }
393
395
  /**
394
396
  * Given an object in a model tree, returns the root object of that tree.
@@ -1197,11 +1199,11 @@ class ObjectNode extends BaseNode {
1197
1199
  this.identifier = normalizeIdentifier(id);
1198
1200
  this.unnormalizedIdentifier = id;
1199
1201
  }
1200
- if (!parent) {
1201
- this.identifierCache.addNodeToCache(this);
1202
- }
1203
- else {
1204
- parent.root.identifierCache.addNodeToCache(this);
1202
+ // addNodeToCache no-ops without an identifier attribute, and reaching the
1203
+ // root to ask it is a walk up the whole parent chain
1204
+ if (this.identifierAttribute) {
1205
+ const cache = parent ? parent.root.identifierCache : this.identifierCache;
1206
+ cache.addNodeToCache(this);
1205
1207
  }
1206
1208
  }
1207
1209
  createObservableInstanceIfNeeded(fireHooks = true) {
@@ -1349,15 +1351,9 @@ class ObjectNode extends BaseNode {
1349
1351
  typeof this.storedValue === "object" &&
1350
1352
  this.storedValue[name];
1351
1353
  if (typeof fn === "function") {
1352
- // we check for it to allow old mobx peer dependencies that don't have the method to work (even when still bugged)
1353
- if (mobx._allowStateChangesInsideComputed) {
1354
- mobx._allowStateChangesInsideComputed(() => {
1355
- fn.apply(this.storedValue);
1356
- });
1357
- }
1358
- else {
1354
+ mobx._allowStateChangesInsideComputed(() => {
1359
1355
  fn.apply(this.storedValue);
1360
- }
1356
+ });
1361
1357
  }
1362
1358
  }
1363
1359
  _snapshotUponDeath;
@@ -1737,8 +1733,8 @@ var TypeFlags;
1737
1733
  TypeFlags[TypeFlags["Float"] = 4194304] = "Float";
1738
1734
  })(TypeFlags || (TypeFlags = {}));
1739
1735
  /**
1740
- * @internal
1741
- * @hidden
1736
+ * The value `getSubTypes()` returns when a type cannot report its subtype,
1737
+ * which is distinct from reporting that it has none (`null`).
1742
1738
  */
1743
1739
  const cannotDetermineSubtype = "cannotDetermine";
1744
1740
  /**
@@ -1790,6 +1786,9 @@ class BaseType {
1790
1786
  // istanbul ignore next
1791
1787
  throw fail("unimplemented method");
1792
1788
  }
1789
+ describe() {
1790
+ return this.name;
1791
+ }
1793
1792
  isAssignableFrom(type) {
1794
1793
  return type === this;
1795
1794
  }
@@ -1935,7 +1934,7 @@ class SimpleType extends BaseType {
1935
1934
  * @returns `true` if the value is a type.
1936
1935
  */
1937
1936
  function isType(value) {
1938
- return typeof value === "object" && value?.isType === true;
1937
+ return (typeof value === "object" && value?.isType === true);
1939
1938
  }
1940
1939
  /**
1941
1940
  * @internal
@@ -2670,10 +2669,6 @@ function safeStringify(value) {
2670
2669
  return `<Unserializable: ${e}>`;
2671
2670
  }
2672
2671
  }
2673
- /**
2674
- * @internal
2675
- * @hidden
2676
- */
2677
2672
  function prettyPrintValue(value) {
2678
2673
  return typeof value === "function"
2679
2674
  ? `<function${value.name ? ` ${value.name}` : ""}>`
@@ -2706,12 +2701,8 @@ function toErrorString(error) {
2706
2701
  expectation = ".";
2707
2702
  }
2708
2703
  else {
2709
- // `isPrimitiveType` is declared `(type: IT) => type is IT`, so its negative
2710
- // branch narrows to `never` rather than to "some non-primitive type"; the
2711
- // annotation restores the type it actually has here.
2712
- const complexType = type;
2713
- const isSnapshotCompatible = isStateTreeNode(value) && complexType.is(getStateTreeNode(value).snapshot);
2714
- expectation = `, expected an instance of \`${complexType.name}\` or a snapshot like \`${shortenPrintValue(complexType.describe())}\` instead.${isSnapshotCompatible
2704
+ const isSnapshotCompatible = isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot);
2705
+ expectation = `, expected an instance of \`${type.name}\` or a snapshot like \`${shortenPrintValue(type.describe())}\` instead.${isSnapshotCompatible
2715
2706
  ? " (Note that a snapshot of the provided value is compatible with the targeted type)"
2716
2707
  : ""}`;
2717
2708
  }
@@ -2992,7 +2983,7 @@ function getStateTreeNode(value) {
2992
2983
  * @hidden
2993
2984
  */
2994
2985
  function getStateTreeNodeSafe(value) {
2995
- return value?.$treenode || null;
2986
+ return value?.["$treenode"] || null;
2996
2987
  }
2997
2988
  /**
2998
2989
  * @internal
@@ -3304,9 +3295,6 @@ class EventHandler {
3304
3295
  this.handlers.splice(index, 1);
3305
3296
  }
3306
3297
  }
3307
- clear() {
3308
- this.handlers.length = 0;
3309
- }
3310
3298
  emit(...args) {
3311
3299
  const handlers = this.handlers;
3312
3300
  // 0 and 1 handlers are by far the common cases (a patch/snapshot listener, a
@@ -3451,21 +3439,6 @@ function assertIsString(value, argNumber, canBeEmpty = true) {
3451
3439
  assertArg(value, s => s !== "", "not empty string", argNumber);
3452
3440
  }
3453
3441
  }
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
3442
 
3470
3443
  /**
3471
3444
  * See [asynchronous actions](concepts/async-actions.md).
@@ -3536,10 +3509,6 @@ function toGeneratorFunction(p) {
3536
3509
  function* toGenerator(p) {
3537
3510
  return (yield p);
3538
3511
  }
3539
- /**
3540
- * @internal
3541
- * @hidden
3542
- */
3543
3512
  function createFlowSpawner(name, generator) {
3544
3513
  const spawner = function flowSpawner(...flowArgs) {
3545
3514
  // Implementation based on https://github.com/tj/co/blob/master/index.js
@@ -3597,7 +3566,7 @@ function createFlowSpawner(name, generator) {
3597
3566
  }
3598
3567
  catch (e) {
3599
3568
  // prettier-ignore
3600
- setImmediateWithFallback(() => {
3569
+ queueMicrotask(() => {
3601
3570
  wrap((_r) => { reject(e); }, "flow_throw", e);
3602
3571
  });
3603
3572
  return;
@@ -3613,7 +3582,7 @@ function createFlowSpawner(name, generator) {
3613
3582
  }
3614
3583
  catch (e) {
3615
3584
  // prettier-ignore
3616
- setImmediateWithFallback(() => {
3585
+ queueMicrotask(() => {
3617
3586
  wrap((_r) => { reject(e); }, "flow_throw", e);
3618
3587
  });
3619
3588
  return;
@@ -3623,7 +3592,7 @@ function createFlowSpawner(name, generator) {
3623
3592
  function next(ret) {
3624
3593
  if (ret.done) {
3625
3594
  // prettier-ignore
3626
- setImmediateWithFallback(() => {
3595
+ queueMicrotask(() => {
3627
3596
  wrap((r) => { resolve(r); }, "flow_return", ret.value);
3628
3597
  });
3629
3598
  return;
@@ -3651,10 +3620,6 @@ function splitPatch(patch) {
3651
3620
  }
3652
3621
  return [stripPatch(patch), invertPatch(patch)];
3653
3622
  }
3654
- /**
3655
- * @internal
3656
- * @hidden
3657
- */
3658
3623
  function stripPatch(patch) {
3659
3624
  // strips `oldvalue` information from the patch, so that it becomes a patch conform the json-patch spec
3660
3625
  // this removes the ability to undo the patch
@@ -4001,13 +3966,6 @@ var MapIdentifierMode;
4001
3966
  MapIdentifierMode[MapIdentifierMode["NO"] = 2] = "NO";
4002
3967
  })(MapIdentifierMode || (MapIdentifierMode = {}));
4003
3968
  class MSTMap extends mobx.ObservableMap {
4004
- constructor(initialData, name) {
4005
- // mobx's default (deep) enhancer. The old `observable.ref.enhancer` arg
4006
- // read undefined ever since mobx 6 renamed the field to `options_.enhancer`,
4007
- // so deep has been the effective behavior all along; mobx 7 dropped
4008
- // `observable.ref` entirely.
4009
- super(initialData, undefined, name);
4010
- }
4011
3969
  get(key) {
4012
3970
  // maybe this is over-enthousiastic? normalize numeric keys to strings
4013
3971
  return super.get(`${key}`);
@@ -4120,7 +4078,8 @@ class MapType extends ComplexType {
4120
4078
  return result;
4121
4079
  }
4122
4080
  createNewInstance(childNodes) {
4123
- return new MSTMap(childNodes, this.name);
4081
+ // undefined is mobx's enhancer argument, left at its deep default
4082
+ return new MSTMap(childNodes, undefined, this.name);
4124
4083
  }
4125
4084
  finalizeNewInstance(node, instance) {
4126
4085
  mobx._interceptReads(instance, node.unbox);
@@ -4128,9 +4087,6 @@ class MapType extends ComplexType {
4128
4087
  mobx.intercept(instance, this.willChange);
4129
4088
  mobx.observe(instance, this.didChange);
4130
4089
  }
4131
- describe() {
4132
- return this.name;
4133
- }
4134
4090
  getChildren(node) {
4135
4091
  // return (node.storedValue as ObservableMap<any>).values()
4136
4092
  return mobx.values(node.storedValue);
@@ -4307,6 +4263,7 @@ MapType.prototype.applySnapshot = mobx.action(MapType.prototype.applySnapshot);
4307
4263
  * @returns
4308
4264
  */
4309
4265
  function map(subtype) {
4266
+ assertIsType(subtype, 1);
4310
4267
  return new MapType(subtype);
4311
4268
  }
4312
4269
  /**
@@ -4360,9 +4317,6 @@ class ArrayType extends ComplexType {
4360
4317
  mobx.intercept(instance, this.willChange);
4361
4318
  mobx.observe(instance, this.didChange);
4362
4319
  }
4363
- describe() {
4364
- return this.name;
4365
- }
4366
4320
  getChildren(node) {
4367
4321
  return node.storedValue.slice();
4368
4322
  }
@@ -4734,8 +4688,8 @@ function objectTypeToString() {
4734
4688
  }
4735
4689
  const ANONYMOUS_MODEL_NAME = "AnonymousModel";
4736
4690
  /**
4737
- * A plain loop rather than `forAllProps`, which allocated a closure and made an
4738
- * indirect call per property. This runs once per `types.model()` over every
4691
+ * A plain loop rather than a callback per property, which allocated a closure
4692
+ * and made an indirect call. This runs once per `types.model()` over every
4739
4693
  * declared property, and jbrowse builds a ~40-slot schema per track.
4740
4694
  */
4741
4695
  function findIdentifierAttribute(properties, propertyNames) {
@@ -4933,12 +4887,6 @@ class ModelType extends ComplexType {
4933
4887
  (!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(self, name, actionInvoker);
4934
4888
  });
4935
4889
  }
4936
- named = name => {
4937
- return this.cloneAndEnhance({ name });
4938
- };
4939
- props = properties => {
4940
- return this.cloneAndEnhance({ properties });
4941
- };
4942
4890
  volatile(fn) {
4943
4891
  if (typeof fn !== "function") {
4944
4892
  throw fail(`You passed an ${typeof fn} to volatile state as an argument, when function is expected`);
@@ -5021,28 +4969,6 @@ class ModelType extends ComplexType {
5021
4969
  }
5022
4970
  });
5023
4971
  }
5024
- preProcessSnapshot = preProcessor => {
5025
- const currentPreprocessor = this.preProcessor;
5026
- if (!currentPreprocessor) {
5027
- return this.cloneAndEnhance({ preProcessor });
5028
- }
5029
- else {
5030
- return this.cloneAndEnhance({
5031
- preProcessor: snapshot => currentPreprocessor(preProcessor(snapshot))
5032
- });
5033
- }
5034
- };
5035
- postProcessSnapshot = postProcessor => {
5036
- const currentPostprocessor = this.postProcessor;
5037
- if (!currentPostprocessor) {
5038
- return this.cloneAndEnhance({ postProcessor });
5039
- }
5040
- else {
5041
- return this.cloneAndEnhance({
5042
- postProcessor: snapshot => postProcessor(currentPostprocessor(snapshot))
5043
- });
5044
- }
5045
- };
5046
4972
  instantiate(parent, subpath, environment, initialValue) {
5047
4973
  const value = isStateTreeNode(initialValue)
5048
4974
  ? initialValue
@@ -5053,10 +4979,11 @@ class ModelType extends ComplexType {
5053
4979
  }
5054
4980
  initializeChildNodes(objNode, initialSnapshot = {}) {
5055
4981
  const type = objNode.type;
4982
+ const properties = type.properties;
5056
4983
  const result = {};
5057
- type.forAllProps((name, childType) => {
5058
- result[name] = childType.instantiate(objNode, name, undefined, initialSnapshot[name]);
5059
- });
4984
+ for (const name of type.propertyNames) {
4985
+ result[name] = properties[name].instantiate(objNode, name, undefined, initialSnapshot[name]);
4986
+ }
5060
4987
  return result;
5061
4988
  }
5062
4989
  createNewInstance(childNodes) {
@@ -5075,9 +5002,9 @@ class ModelType extends ComplexType {
5075
5002
  }
5076
5003
  }
5077
5004
  else {
5078
- this.forAllProps(name => {
5005
+ for (const name of this.propertyNames) {
5079
5006
  mobx._interceptReads(instance, name, node.unbox);
5080
- });
5007
+ }
5081
5008
  }
5082
5009
  this.initializers.reduce((self, fn) => fn(self), instance);
5083
5010
  // views, actions and volatile share the instance namespace with properties,
@@ -5088,12 +5015,12 @@ class ModelType extends ComplexType {
5088
5015
  // function/value stops being observable at all. The flag is only set on
5089
5016
  // success so a broken type keeps throwing on every create.
5090
5017
  if (!this.duplicateKeysChecked) {
5091
- this.forAllProps(name => {
5018
+ for (const name of this.propertyNames) {
5092
5019
  if (mobx.isComputedProp(instance, name) ||
5093
5020
  !mobx.isObservableProp(instance, name)) {
5094
5021
  throw fail(`${name} property is declared twice`);
5095
5022
  }
5096
- });
5023
+ }
5097
5024
  this.duplicateKeysChecked = true;
5098
5025
  }
5099
5026
  mobx.intercept(instance, this.willChange);
@@ -5146,24 +5073,16 @@ class ModelType extends ComplexType {
5146
5073
  }
5147
5074
  return res;
5148
5075
  }
5149
- /**
5150
- * Same as {@link getChildNode} but for a key already known to be a declared
5151
- * property (callers iterating `propertyNames`), so the membership guard is
5152
- * skipped.
5153
- */
5154
- getPropertyNode(node, key) {
5076
+ getChildNode(node, key) {
5077
+ if (!(key in this.properties)) {
5078
+ throw fail(`Not a value property: ${key}`);
5079
+ }
5155
5080
  const childNode = getPropObservable(node.storedValue, key).raw();
5156
5081
  if (!childNode) {
5157
5082
  throw fail(`Node not available for property ${key}`);
5158
5083
  }
5159
5084
  return childNode;
5160
5085
  }
5161
- getChildNode(node, key) {
5162
- if (!(key in this.properties)) {
5163
- throw fail(`Not a value property: ${key}`);
5164
- }
5165
- return this.getPropertyNode(node, key);
5166
- }
5167
5086
  getSnapshot(node, applyPostProcess = true) {
5168
5087
  const res = {};
5169
5088
  const storedValue = node.storedValue;
@@ -5215,9 +5134,9 @@ class ModelType extends ComplexType {
5215
5134
  applySnapshot(node, snapshot) {
5216
5135
  typecheckInternal(this, snapshot);
5217
5136
  const preProcessedSnapshot = this.applySnapshotPreProcessor(snapshot);
5218
- this.forAllProps(name => {
5137
+ for (const name of this.propertyNames) {
5219
5138
  node.storedValue[name] = preProcessedSnapshot[name];
5220
- });
5139
+ }
5221
5140
  }
5222
5141
  applySnapshotPreProcessor(snapshot) {
5223
5142
  const processor = this.preProcessor;
@@ -5250,12 +5169,6 @@ class ModelType extends ComplexType {
5250
5169
  }
5251
5170
  return typeCheckSuccess();
5252
5171
  }
5253
- forAllProps(fn) {
5254
- const properties = this.properties;
5255
- for (const key of this.propertyNames) {
5256
- fn(key, properties[key]);
5257
- }
5258
- }
5259
5172
  describe() {
5260
5173
  // optimization: cache
5261
5174
  return `{ ${this.propertyNames
@@ -5270,6 +5183,38 @@ class ModelType extends ComplexType {
5270
5183
  }
5271
5184
  }
5272
5185
  ModelType.prototype.applySnapshot = mobx.action(ModelType.prototype.applySnapshot);
5186
+ // Chain steps shared by every model type; see the field declarations in the
5187
+ // class for why they are not own slots.
5188
+ Object.assign(ModelType.prototype, {
5189
+ named(name) {
5190
+ return this.cloneAndEnhance({ name });
5191
+ },
5192
+ props(properties) {
5193
+ return this.cloneAndEnhance({ properties });
5194
+ },
5195
+ 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
+ }
5205
+ },
5206
+ 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
+ }
5216
+ }
5217
+ });
5273
5218
  /**
5274
5219
  * `types.model` - Creates a new model type by providing a name, properties, volatile state and actions.
5275
5220
  *
@@ -5380,9 +5325,6 @@ class CoreType extends SimpleType {
5380
5325
  this.checker = checker;
5381
5326
  this.initializer = initializer;
5382
5327
  }
5383
- describe() {
5384
- return this.name;
5385
- }
5386
5328
  instantiate(parent, subpath, environment, initialValue) {
5387
5329
  return createScalarNode(this, parent, subpath, environment, initialValue);
5388
5330
  }
@@ -5549,15 +5491,15 @@ class Literal extends SimpleType {
5549
5491
  value;
5550
5492
  flags = TypeFlags.Literal;
5551
5493
  constructor(value) {
5552
- super(JSON.stringify(value));
5494
+ super();
5553
5495
  this.value = value;
5554
5496
  }
5497
+ computeName() {
5498
+ return JSON.stringify(this.value);
5499
+ }
5555
5500
  instantiate(parent, subpath, environment, initialValue) {
5556
5501
  return createScalarNode(this, parent, subpath, environment, initialValue);
5557
5502
  }
5558
- describe() {
5559
- return JSON.stringify(this.value);
5560
- }
5561
5503
  isValidSnapshot(value, context) {
5562
5504
  if (isPrimitive(value) && value === this.value) {
5563
5505
  return typeCheckSuccess();
@@ -5615,9 +5557,6 @@ class Refinement extends BaseType {
5615
5557
  this._predicate = _predicate;
5616
5558
  this._message = _message;
5617
5559
  }
5618
- describe() {
5619
- return this.name;
5620
- }
5621
5560
  instantiate(parent, subpath, environment, initialValue) {
5622
5561
  // create the child type
5623
5562
  return this._subtype.instantiate(parent, subpath, environment, initialValue);
@@ -5749,6 +5688,16 @@ function resolveModelType(type) {
5749
5688
  }
5750
5689
  return undefined;
5751
5690
  }
5691
+ // The quick-match paths below already know a type carries TypeFlags.Literal,
5692
+ // but `is()` still routes through BaseType.validate — a context array, an entry
5693
+ // object and a `$treenode` probe per property, per candidate member. A real
5694
+ // `Literal` stores its primitive, and its isValidSnapshot is exactly value
5695
+ // equality against it, so compare directly. The `instanceof` gate is load
5696
+ // bearing: wrappers (optional, refinement, snapshotProcessor, late, union)
5697
+ // inherit the Literal flag from what they wrap and must keep the full check.
5698
+ function matchesLiteral(type, value) {
5699
+ return type instanceof Literal ? type.value === value : type.is(value);
5700
+ }
5752
5701
  /**
5753
5702
  * @internal
5754
5703
  * @hidden
@@ -5866,10 +5815,10 @@ class Union extends BaseType {
5866
5815
  if (!model) {
5867
5816
  continue;
5868
5817
  }
5869
- const typeProp = model.properties.type;
5818
+ const typeProp = model.properties["type"];
5870
5819
  if (!typeProp ||
5871
5820
  !(typeProp.flags & TypeFlags.Literal) ||
5872
- !typeProp.is(discriminator)) {
5821
+ !matchesLiteral(typeProp, discriminator)) {
5873
5822
  continue;
5874
5823
  }
5875
5824
  if (found) {
@@ -5889,7 +5838,7 @@ class Union extends BaseType {
5889
5838
  this._allMembersDiscriminated = this._types.every(t => {
5890
5839
  const model = resolveModelType(t);
5891
5840
  const typeProp = model &&
5892
- model.properties.type;
5841
+ model.properties["type"];
5893
5842
  return !!typeProp && (typeProp.flags & TypeFlags.Literal) !== 0;
5894
5843
  });
5895
5844
  }
@@ -5963,7 +5912,7 @@ class Union extends BaseType {
5963
5912
  }
5964
5913
  // for literals, check exact value match
5965
5914
  if (flags & TypeFlags.Literal) {
5966
- if (type.is(value)) {
5915
+ if (matchesLiteral(type, value)) {
5967
5916
  return type;
5968
5917
  }
5969
5918
  }
@@ -5995,7 +5944,7 @@ class Union extends BaseType {
5995
5944
  // for literal types, verify the value matches exactly
5996
5945
  // this is critical for discriminated unions
5997
5946
  if (flags & TypeFlags.Literal) {
5998
- if (!propType.is(propValue)) {
5947
+ if (!matchesLiteral(propType, propValue)) {
5999
5948
  return false;
6000
5949
  }
6001
5950
  }
@@ -6071,6 +6020,22 @@ Object.assign(Union.prototype, {
6071
6020
  _discriminatorCache: undefined,
6072
6021
  _allMembersDiscriminated: undefined
6073
6022
  });
6023
+ const unionInternRoot = {};
6024
+ function internUnion(types) {
6025
+ let node = unionInternRoot;
6026
+ for (const type of types) {
6027
+ const level = (node.next ??= new WeakMap());
6028
+ const existing = level.get(type);
6029
+ if (existing) {
6030
+ node = existing;
6031
+ }
6032
+ else {
6033
+ node = {};
6034
+ level.set(type, node);
6035
+ }
6036
+ }
6037
+ return (node.union ??= new Union(types));
6038
+ }
6074
6039
  /**
6075
6040
  * `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`.
6076
6041
  *
@@ -6099,7 +6064,7 @@ function union(...args) {
6099
6064
  assertIsType(type, options ? i + 2 : i + 1);
6100
6065
  });
6101
6066
  }
6102
- return new Union(types, options);
6067
+ return options === undefined ? internUnion(types) : new Union(types, options);
6103
6068
  }
6104
6069
  /**
6105
6070
  * Returns if a given value represents a union type.
@@ -6294,13 +6259,35 @@ const undefinedAsOptionalValues = [undefined];
6294
6259
  * to compile. Guards with a distinct narrowing target (`isArrayType`,
6295
6260
  * `isMapType`, `isModelType`) keep their predicate.
6296
6261
  *
6297
- * @template IT
6298
6262
  * @param type
6299
6263
  * @returns
6300
6264
  */
6301
6265
  function isOptionalType(type) {
6302
6266
  return isType(type) && (type.flags & TypeFlags.Optional) > 0;
6303
6267
  }
6268
+ function isRecord(value) {
6269
+ return typeof value === "object" && value !== null;
6270
+ }
6271
+ /**
6272
+ * The identifier attribute of the model a stripDefault ultimately wraps, or
6273
+ * `null` when there is none. Drills through the single-subtype wrappers the way
6274
+ * `resolveModelType` in union.ts does — that one is module-private there, and
6275
+ * this file may not reach into it.
6276
+ *
6277
+ * `null` is also the answer for an unresolved `types.late`, which only costs the
6278
+ * short-circuit, never correctness: the full structural walk still runs.
6279
+ */
6280
+ function resolveIdentifierAttribute(type) {
6281
+ let current = type;
6282
+ for (let depth = 0; current && depth < 20; depth++) {
6283
+ if (current instanceof ModelType) {
6284
+ return current.identifierAttribute ? current.identifierAttribute : null;
6285
+ }
6286
+ const wrapper = current;
6287
+ current = wrapper._subtype ?? wrapper.getSubType?.(false);
6288
+ }
6289
+ return null;
6290
+ }
6304
6291
  /**
6305
6292
  * Compare a child snapshot to a stripped-default's reference snapshot: identity
6306
6293
  * for primitives, structural for objects/arrays.
@@ -6379,6 +6366,22 @@ function defaultSnapshotEquals(a, b) {
6379
6366
  * @internal
6380
6367
  */
6381
6368
  class StripDefaultValue extends OptionalValue {
6369
+ equalsDefault(snapshot, defaultSnapshot) {
6370
+ let identifierAttribute = this._identifierAttribute;
6371
+ if (identifierAttribute === undefined) {
6372
+ identifierAttribute = resolveIdentifierAttribute(this.getSubTypes());
6373
+ this._identifierAttribute = identifierAttribute;
6374
+ }
6375
+ // an identified model's snapshot normally has the same shape as the default
6376
+ // and differs only here, so answering from one key beats walking every key
6377
+ // (including nested objects) until the walk reaches it
6378
+ return identifierAttribute !== null &&
6379
+ isRecord(snapshot) &&
6380
+ isRecord(defaultSnapshot) &&
6381
+ snapshot[identifierAttribute] !== defaultSnapshot[identifierAttribute]
6382
+ ? false
6383
+ : defaultSnapshotEquals(snapshot, defaultSnapshot);
6384
+ }
6382
6385
  shouldStripFromSnapshot(snapshot) {
6383
6386
  if (!this._defaultSnapshot) {
6384
6387
  // instantiate the subtype detached with the default and read the node's
@@ -6387,11 +6390,35 @@ class StripDefaultValue extends OptionalValue {
6387
6390
  const node = this.getSubTypes().instantiate(null, "", undefined, this.getDefaultInstanceOrSnapshot());
6388
6391
  this._defaultSnapshot = { value: node.snapshot };
6389
6392
  }
6390
- return defaultSnapshotEquals(snapshot, this._defaultSnapshot.value);
6393
+ const defaultSnapshot = this._defaultSnapshot.value;
6394
+ let result;
6395
+ if (isRecord(snapshot)) {
6396
+ // a child node's snapshot is a keepAlive computed, so a stable reference
6397
+ // means unchanged content: the same object always gets the same answer
6398
+ let cache = this._equalsDefaultCache;
6399
+ if (!cache) {
6400
+ cache = new WeakMap();
6401
+ this._equalsDefaultCache = cache;
6402
+ }
6403
+ const cached = cache.get(snapshot);
6404
+ if (cached === undefined) {
6405
+ result = this.equalsDefault(snapshot, defaultSnapshot);
6406
+ cache.set(snapshot, result);
6407
+ }
6408
+ else {
6409
+ result = cached;
6410
+ }
6411
+ }
6412
+ else {
6413
+ result = defaultSnapshotEquals(snapshot, defaultSnapshot);
6414
+ }
6415
+ return result;
6391
6416
  }
6392
6417
  }
6393
6418
  Object.assign(StripDefaultValue.prototype, {
6394
- _defaultSnapshot: undefined
6419
+ _defaultSnapshot: undefined,
6420
+ _identifierAttribute: undefined,
6421
+ _equalsDefaultCache: undefined
6395
6422
  });
6396
6423
  /**
6397
6424
  * Whether `type` is a strip-default optional whose current child `snapshot`
@@ -6810,9 +6837,6 @@ class BaseReferenceType extends SimpleType {
6810
6837
  computeName() {
6811
6838
  return `reference(${this.targetType.name})`;
6812
6839
  }
6813
- describe() {
6814
- return this.name;
6815
- }
6816
6840
  isAssignableFrom(type) {
6817
6841
  return this.targetType.isAssignableFrom(type);
6818
6842
  }
@@ -7149,9 +7173,6 @@ class IdentifierType extends BaseIdentifierType {
7149
7173
  constructor() {
7150
7174
  super(`identifier`, "string");
7151
7175
  }
7152
- describe() {
7153
- return `identifier`;
7154
- }
7155
7176
  }
7156
7177
  /**
7157
7178
  * @internal
@@ -7161,12 +7182,6 @@ class IdentifierNumberType extends BaseIdentifierType {
7161
7182
  constructor() {
7162
7183
  super("identifierNumber", "number");
7163
7184
  }
7164
- getSnapshot(node) {
7165
- return node.storedValue;
7166
- }
7167
- describe() {
7168
- return `identifierNumber`;
7169
- }
7170
7185
  }
7171
7186
  /**
7172
7187
  * `types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works.
@@ -7297,9 +7312,6 @@ class CustomType extends SimpleType {
7297
7312
  super(options.name);
7298
7313
  this.options = options;
7299
7314
  }
7300
- describe() {
7301
- return this.name;
7302
- }
7303
7315
  isValidSnapshot(value, context) {
7304
7316
  if (this.options.isTargetType(value)) {
7305
7317
  return typeCheckSuccess();
@@ -7508,6 +7520,7 @@ exports.addMiddleware = addMiddleware;
7508
7520
  exports.applyAction = applyAction;
7509
7521
  exports.applyPatch = applyPatch;
7510
7522
  exports.applySnapshot = applySnapshot;
7523
+ exports.cannotDetermineSubtype = cannotDetermineSubtype;
7511
7524
  exports.cast = cast;
7512
7525
  exports.castFlowReturn = castFlowReturn;
7513
7526
  exports.castToReferenceSnapshot = castToReferenceSnapshot;