@jbrowse/mobx-state-tree 5.11.1 → 5.12.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.
@@ -766,10 +766,11 @@ function getMembers(target) {
766
766
  }
767
767
  return;
768
768
  }
769
- if (descriptor.value._isFlowAction === true) {
769
+ const value = descriptor.value;
770
+ if (value?._isFlowAction === true) {
770
771
  reflected.flowActions.push(key);
771
772
  }
772
- if (descriptor.value._isMSTAction === true) {
773
+ if (value?._isMSTAction === true) {
773
774
  reflected.actions.push(key);
774
775
  }
775
776
  else if (mobx.isObservableProp(target, key)) {
@@ -1118,6 +1119,21 @@ class ScalarNode extends BaseNode {
1118
1119
  ScalarNode.prototype.die = mobx.action(ScalarNode.prototype.die);
1119
1120
 
1120
1121
  let nextNodeId = 1;
1122
+ var ObservableInstanceLifecycle;
1123
+ (function (ObservableInstanceLifecycle) {
1124
+ // the actual observable instance has not been created yet
1125
+ ObservableInstanceLifecycle[ObservableInstanceLifecycle["UNINITIALIZED"] = 0] = "UNINITIALIZED";
1126
+ // the actual observable instance is being created
1127
+ ObservableInstanceLifecycle[ObservableInstanceLifecycle["CREATING"] = 1] = "CREATING";
1128
+ // the actual observable instance has been created
1129
+ ObservableInstanceLifecycle[ObservableInstanceLifecycle["CREATED"] = 2] = "CREATED";
1130
+ })(ObservableInstanceLifecycle || (ObservableInstanceLifecycle = {}));
1131
+ var InternalEvents;
1132
+ (function (InternalEvents) {
1133
+ InternalEvents["Dispose"] = "dispose";
1134
+ InternalEvents["Patch"] = "patch";
1135
+ InternalEvents["Snapshot"] = "snapshot";
1136
+ })(InternalEvents || (InternalEvents = {}));
1121
1137
  const snapshotReactionOptions = {
1122
1138
  onError(e) {
1123
1139
  throw e;
@@ -1149,7 +1165,7 @@ class ObjectNode extends BaseNode {
1149
1165
  _autoUnbox = true; // unboxing is disabled when reading child nodes
1150
1166
  _isRunningAction = false; // only relevant for root
1151
1167
  _hasSnapshotReaction = false;
1152
- _observableInstanceState = 0 /* ObservableInstanceLifecycle.UNINITIALIZED */;
1168
+ _observableInstanceState = ObservableInstanceLifecycle.UNINITIALIZED;
1153
1169
  _childNodes;
1154
1170
  _initialSnapshot;
1155
1171
  _cachedInitialSnapshot;
@@ -1194,7 +1210,7 @@ class ObjectNode extends BaseNode {
1194
1210
  }
1195
1211
  createObservableInstanceIfNeeded(fireHooks = true) {
1196
1212
  if (this._observableInstanceState ===
1197
- 0 /* ObservableInstanceLifecycle.UNINITIALIZED */) {
1213
+ ObservableInstanceLifecycle.UNINITIALIZED) {
1198
1214
  this.createObservableInstance(fireHooks);
1199
1215
  }
1200
1216
  }
@@ -1205,7 +1221,7 @@ class ObjectNode extends BaseNode {
1205
1221
  throw fail("assertion failed: the creation of the observable instance must be done on the initializing phase");
1206
1222
  }
1207
1223
  }
1208
- this._observableInstanceState = 1 /* ObservableInstanceLifecycle.CREATING */;
1224
+ this._observableInstanceState = ObservableInstanceLifecycle.CREATING;
1209
1225
  // make sure the parent chain is created as well
1210
1226
  // array with parent chain from parent to child
1211
1227
  const parentChain = [];
@@ -1215,7 +1231,7 @@ class ObjectNode extends BaseNode {
1215
1231
  // this is done to avoid traversing the whole tree to the root when using
1216
1232
  // the same reference again
1217
1233
  while (parent?._observableInstanceState ===
1218
- 0 /* ObservableInstanceLifecycle.UNINITIALIZED */) {
1234
+ ObservableInstanceLifecycle.UNINITIALIZED) {
1219
1235
  parentChain.unshift(parent);
1220
1236
  parent = parent.parent;
1221
1237
  }
@@ -1240,7 +1256,7 @@ class ObjectNode extends BaseNode {
1240
1256
  finally {
1241
1257
  this._isRunningAction = false;
1242
1258
  }
1243
- this._observableInstanceState = 2 /* ObservableInstanceLifecycle.CREATED */;
1259
+ this._observableInstanceState = ObservableInstanceLifecycle.CREATED;
1244
1260
  this._snapshotComputed.trackAndCompute();
1245
1261
  if (this.isRoot) {
1246
1262
  this._addSnapshotReaction();
@@ -1354,7 +1370,7 @@ class ObjectNode extends BaseNode {
1354
1370
  if (!this.isAlive) {
1355
1371
  return this._snapshotUponDeath;
1356
1372
  }
1357
- return this._observableInstanceState === 2 /* ObservableInstanceLifecycle.CREATED */
1373
+ return this._observableInstanceState === ObservableInstanceLifecycle.CREATED
1358
1374
  ? this._getActualSnapshot()
1359
1375
  : this._getCachedInitialSnapshot();
1360
1376
  }
@@ -1417,7 +1433,7 @@ class ObjectNode extends BaseNode {
1417
1433
  this._autoUnbox = false;
1418
1434
  try {
1419
1435
  return this._observableInstanceState ===
1420
- 2 /* ObservableInstanceLifecycle.CREATED */
1436
+ ObservableInstanceLifecycle.CREATED
1421
1437
  ? this.type.getChildNode(this, subpath)
1422
1438
  : this._childNodes[subpath];
1423
1439
  }
@@ -1430,7 +1446,7 @@ class ObjectNode extends BaseNode {
1430
1446
  this._autoUnbox = false;
1431
1447
  try {
1432
1448
  return this._observableInstanceState ===
1433
- 2 /* ObservableInstanceLifecycle.CREATED */
1449
+ ObservableInstanceLifecycle.CREATED
1434
1450
  ? this.type.getChildren(this)
1435
1451
  : convertChildNodesToArray(this._childNodes);
1436
1452
  }
@@ -1516,7 +1532,7 @@ class ObjectNode extends BaseNode {
1516
1532
  }
1517
1533
  aboutToDie() {
1518
1534
  if (this._observableInstanceState ===
1519
- 0 /* ObservableInstanceLifecycle.UNINITIALIZED */) {
1535
+ ObservableInstanceLifecycle.UNINITIALIZED) {
1520
1536
  return;
1521
1537
  }
1522
1538
  this.getChildren().forEach(node => {
@@ -1525,8 +1541,8 @@ class ObjectNode extends BaseNode {
1525
1541
  // beforeDestroy should run before the disposers since else we could end up in a situation where
1526
1542
  // a disposer added with addDisposer at this stage (beforeDestroy) is actually never released
1527
1543
  this.baseAboutToDie();
1528
- this._internalEventsEmit("dispose" /* InternalEvents.Dispose */);
1529
- this._internalEventsClear("dispose" /* InternalEvents.Dispose */);
1544
+ this._internalEventsEmit(InternalEvents.Dispose);
1545
+ this._internalEventsClear(InternalEvents.Dispose);
1530
1546
  }
1531
1547
  finalizeDeath() {
1532
1548
  // invariant: not called directly but from "die"
@@ -1542,42 +1558,42 @@ class ObjectNode extends BaseNode {
1542
1558
  }
1543
1559
  onSnapshot(onChange) {
1544
1560
  this._addSnapshotReaction();
1545
- return this._internalEventsRegister("snapshot" /* InternalEvents.Snapshot */, onChange);
1561
+ return this._internalEventsRegister(InternalEvents.Snapshot, onChange);
1546
1562
  }
1547
1563
  emitSnapshot(snapshot) {
1548
- this._internalEventsEmit("snapshot" /* InternalEvents.Snapshot */, snapshot);
1564
+ this._internalEventsEmit(InternalEvents.Snapshot, snapshot);
1549
1565
  }
1550
1566
  onPatch(handler) {
1551
- return this._internalEventsRegister("patch" /* InternalEvents.Patch */, handler);
1567
+ return this._internalEventsRegister(InternalEvents.Patch, handler);
1552
1568
  }
1553
1569
  emitPatch(basePatch, source) {
1554
- if (this._internalEventsHasSubscribers("patch" /* InternalEvents.Patch */)) {
1570
+ if (this._internalEventsHasSubscribers(InternalEvents.Patch)) {
1555
1571
  const localizedPatch = {
1556
1572
  ...basePatch,
1557
- path: `${source.path.substr(this.path.length)}/${basePatch.path}` // calculate the relative path of the patch
1573
+ path: `${source.path.slice(this.path.length)}/${basePatch.path}` // calculate the relative path of the patch
1558
1574
  };
1559
1575
  const [patch, reversePatch] = splitPatch(localizedPatch);
1560
- this._internalEventsEmit("patch" /* InternalEvents.Patch */, patch, reversePatch);
1576
+ this._internalEventsEmit(InternalEvents.Patch, patch, reversePatch);
1561
1577
  }
1562
1578
  if (this.parent) {
1563
1579
  this.parent.emitPatch(basePatch, source);
1564
1580
  }
1565
1581
  }
1566
1582
  hasDisposer(disposer) {
1567
- return this._internalEventsHas("dispose" /* InternalEvents.Dispose */, disposer);
1583
+ return this._internalEventsHas(InternalEvents.Dispose, disposer);
1568
1584
  }
1569
1585
  addDisposer(disposer) {
1570
1586
  if (!this.hasDisposer(disposer)) {
1571
- this._internalEventsRegister("dispose" /* InternalEvents.Dispose */, disposer, true);
1587
+ this._internalEventsRegister(InternalEvents.Dispose, disposer, true);
1572
1588
  return;
1573
1589
  }
1574
1590
  throw fail("cannot add a disposer when it is already registered for execution");
1575
1591
  }
1576
1592
  removeDisposer(disposer) {
1577
- if (!this._internalEventsHas("dispose" /* InternalEvents.Dispose */, disposer)) {
1593
+ if (!this._internalEventsHas(InternalEvents.Dispose, disposer)) {
1578
1594
  throw fail("cannot remove a disposer which was never registered for execution");
1579
1595
  }
1580
- this._internalEventsUnregister("dispose" /* InternalEvents.Dispose */, disposer);
1596
+ this._internalEventsUnregister(InternalEvents.Dispose, disposer);
1581
1597
  }
1582
1598
  removeMiddleware(middleware) {
1583
1599
  if (this.middlewares) {
@@ -1653,9 +1669,6 @@ ObjectNode.prototype.createObservableInstance = mobx.action(ObjectNode.prototype
1653
1669
  ObjectNode.prototype.detach = mobx.action(ObjectNode.prototype.detach);
1654
1670
  ObjectNode.prototype.die = mobx.action(ObjectNode.prototype.die);
1655
1671
 
1656
- // Cache for validation results to avoid re-validating the same object against the same type
1657
- // Uses WeakMap so cached objects can be garbage collected
1658
- const validationCache = new WeakMap();
1659
1672
  /**
1660
1673
  * @internal
1661
1674
  * @hidden
@@ -1731,27 +1744,7 @@ class BaseType {
1731
1744
  : typeCheckFailure(context, value);
1732
1745
  // it is tempting to compare snapshots, but in that case we should always clone on assignments...
1733
1746
  }
1734
- // check cache for object values (only at root level to avoid context mismatches)
1735
- if (typeof value === "object" && value !== null && context.length === 1) {
1736
- const typeCache = validationCache.get(value);
1737
- if (typeCache) {
1738
- const cached = typeCache.get(this);
1739
- if (cached !== undefined) {
1740
- return cached;
1741
- }
1742
- }
1743
- }
1744
- const result = this.isValidSnapshot(value, context);
1745
- // cache result for object values (only at root level)
1746
- if (typeof value === "object" && value !== null && context.length === 1) {
1747
- let typeCache = validationCache.get(value);
1748
- if (!typeCache) {
1749
- typeCache = new WeakMap();
1750
- validationCache.set(value, typeCache);
1751
- }
1752
- typeCache.set(this, result);
1753
- }
1754
- return result;
1747
+ return this.isValidSnapshot(value, context);
1755
1748
  }
1756
1749
  is(thing) {
1757
1750
  return this.validate(thing, [{ path: "", type: this }]).length === 0;
@@ -2567,22 +2560,47 @@ function isActionContextThisOrChildOf(actionContext, parentOrThis) {
2567
2560
  }
2568
2561
 
2569
2562
  const MAX_STRINGIFY_DEPTH = 3;
2563
+ const MAX_STRINGIFY_ARRAY_ITEMS = 10;
2564
+ const MAX_STRINGIFY_OBJECT_KEYS = 20;
2565
+ const MAX_STRINGIFY_STRING_LENGTH = 100;
2566
+ // Recursively cap depth, array length, object key count, and string length so a
2567
+ // huge offending snapshot degrades into readable, bounded context instead of a
2568
+ // wall of text that then gets sliced mid-structure. The depth cap also bounds
2569
+ // recursion on circular structures.
2570
+ function truncateForStringify(value, depth) {
2571
+ if (typeof value === "string") {
2572
+ return value.length > MAX_STRINGIFY_STRING_LENGTH
2573
+ ? `${value.substring(0, MAX_STRINGIFY_STRING_LENGTH)}… (${value.length - MAX_STRINGIFY_STRING_LENGTH} more characters)`
2574
+ : value;
2575
+ }
2576
+ if (value === null || typeof value !== "object") {
2577
+ return value;
2578
+ }
2579
+ if (depth >= MAX_STRINGIFY_DEPTH) {
2580
+ return Array.isArray(value) ? "[…]" : "{…}";
2581
+ }
2582
+ if (Array.isArray(value)) {
2583
+ const items = value
2584
+ .slice(0, MAX_STRINGIFY_ARRAY_ITEMS)
2585
+ .map(item => truncateForStringify(item, depth + 1));
2586
+ if (value.length > MAX_STRINGIFY_ARRAY_ITEMS) {
2587
+ items.push(`… ${value.length - MAX_STRINGIFY_ARRAY_ITEMS} more items`);
2588
+ }
2589
+ return items;
2590
+ }
2591
+ const keys = Object.keys(value);
2592
+ const result = {};
2593
+ for (const key of keys.slice(0, MAX_STRINGIFY_OBJECT_KEYS)) {
2594
+ result[key] = truncateForStringify(value[key], depth + 1);
2595
+ }
2596
+ if (keys.length > MAX_STRINGIFY_OBJECT_KEYS) {
2597
+ result["…"] = `${keys.length - MAX_STRINGIFY_OBJECT_KEYS} more keys`;
2598
+ }
2599
+ return result;
2600
+ }
2570
2601
  function safeStringify(value) {
2571
2602
  try {
2572
- const ancestors = [];
2573
- return JSON.stringify(value, function (_key, val) {
2574
- if (val !== null && typeof val === "object") {
2575
- while (ancestors.length > 0 &&
2576
- ancestors[ancestors.length - 1] !== this) {
2577
- ancestors.pop();
2578
- }
2579
- if (ancestors.length >= MAX_STRINGIFY_DEPTH) {
2580
- return Array.isArray(val) ? "[…]" : "{…}";
2581
- }
2582
- ancestors.push(val);
2583
- }
2584
- return val;
2585
- });
2603
+ return JSON.stringify(truncateForStringify(value, 0));
2586
2604
  }
2587
2605
  catch (e) {
2588
2606
  // istanbul ignore next
@@ -3013,31 +3031,6 @@ function convertChildNodesToArray(childNodes) {
3013
3031
  return result;
3014
3032
  }
3015
3033
 
3016
- // based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts
3017
- /*
3018
- All contents of this file are deprecated.
3019
-
3020
- The term `process` has been replaced with `flow` to avoid conflicts with the
3021
- global `process` object.
3022
-
3023
- Refer to `flow.ts` for any further changes to this implementation.
3024
- */
3025
- const DEPRECATION_MESSAGE = "See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information. " +
3026
- "Note that the middleware event types starting with `process` now start with `flow`.";
3027
- /**
3028
- * @hidden
3029
- *
3030
- * @deprecated has been renamed to `flow()`.
3031
- * See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information.
3032
- * Note that the middleware event types starting with `process` now start with `flow`.
3033
- *
3034
- * @returns {Promise}
3035
- */
3036
- function process$1(asyncAction) {
3037
- deprecated("process", `\`process()\` has been renamed to \`flow()\`. ${DEPRECATION_MESSAGE}`);
3038
- return flow(asyncAction);
3039
- }
3040
-
3041
3034
  const plainObjectString = Object.toString();
3042
3035
  /**
3043
3036
  * @internal
@@ -3226,8 +3219,6 @@ function addHiddenWritableProp(object, propName, value) {
3226
3219
  */
3227
3220
  class EventHandler {
3228
3221
  handlers = [];
3229
- emitting = false;
3230
- pendingUnregisters = null;
3231
3222
  get hasSubscribers() {
3232
3223
  return this.handlers.length > 0;
3233
3224
  }
@@ -3246,14 +3237,6 @@ class EventHandler {
3246
3237
  return this.handlers.includes(fn);
3247
3238
  }
3248
3239
  unregister(fn) {
3249
- if (this.emitting) {
3250
- // defer unregistration until emit is done
3251
- if (!this.pendingUnregisters) {
3252
- this.pendingUnregisters = [];
3253
- }
3254
- this.pendingUnregisters.push(fn);
3255
- return;
3256
- }
3257
3240
  const index = this.handlers.indexOf(fn);
3258
3241
  if (index >= 0) {
3259
3242
  this.handlers.splice(index, 1);
@@ -3263,26 +3246,10 @@ class EventHandler {
3263
3246
  this.handlers.length = 0;
3264
3247
  }
3265
3248
  emit(...args) {
3266
- // use emitting flag to defer unregistrations instead of copying array
3267
- this.emitting = true;
3268
- try {
3269
- for (const f of this.handlers) {
3270
- f(...args);
3271
- }
3272
- }
3273
- finally {
3274
- this.emitting = false;
3275
- // process any deferred unregistrations
3276
- if (this.pendingUnregisters) {
3277
- for (const fn of this.pendingUnregisters) {
3278
- const index = this.handlers.indexOf(fn);
3279
- if (index >= 0) {
3280
- this.handlers.splice(index, 1);
3281
- }
3282
- }
3283
- this.pendingUnregisters = null;
3284
- }
3285
- }
3249
+ // iterate a copy so (un)registrations during emit don't disturb this pass
3250
+ // and reentrant emits stay correct
3251
+ const handlers = this.handlers.slice();
3252
+ handlers.forEach(f => f(...args));
3286
3253
  }
3287
3254
  }
3288
3255
  /**
@@ -3328,25 +3295,6 @@ class EventHandlers {
3328
3295
  }
3329
3296
  }
3330
3297
  }
3331
- /**
3332
- * @internal
3333
- * @hidden
3334
- */
3335
- const deprecated = function (id, message) {
3336
- // skip if running production
3337
- if (!devMode()) {
3338
- return;
3339
- }
3340
- // warn if hasn't been warned before
3341
- if (deprecated.ids && !deprecated.ids.hasOwnProperty(id)) {
3342
- warnError(`Deprecation warning: ${message}`);
3343
- }
3344
- // mark as warned to avoid duplicate warn message
3345
- if (deprecated.ids) {
3346
- deprecated.ids[id] = true;
3347
- }
3348
- };
3349
- deprecated.ids = {};
3350
3298
  /**
3351
3299
  * @internal
3352
3300
  * @hidden
@@ -4299,9 +4247,9 @@ class ArrayType extends ComplexType {
4299
4247
  return node.storedValue.slice();
4300
4248
  }
4301
4249
  getChildNode(node, key) {
4302
- const index = Number(key);
4303
- if (index < node.storedValue.length) {
4304
- return node.storedValue[index];
4250
+ const child = node.storedValue[Number(key)];
4251
+ if (child) {
4252
+ return child;
4305
4253
  }
4306
4254
  throw fail(`Not a child: ${key}`);
4307
4255
  }
@@ -4454,6 +4402,23 @@ function array(subtype) {
4454
4402
  }
4455
4403
  function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths) {
4456
4404
  let nothingChanged = true;
4405
+ // When the element type is a plain model with an identifier, index the old
4406
+ // nodes by id so a moved / replaced child is matched in O(1) instead of the
4407
+ // linear scan in the reorder branch below. This turns a full array
4408
+ // replacement (the common "load new data" case) from O(n^2) into O(n). Types
4409
+ // whose id extraction needs type-specific preprocessing (union,
4410
+ // snapshotProcessor, late, ...) are intentionally excluded: areSame must run
4411
+ // `is()` before their id check, so they stay on the scan path.
4412
+ let idIndex;
4413
+ if (childType instanceof ModelType && childType.identifierAttribute) {
4414
+ const byId = new Map();
4415
+ for (const n of oldNodes) {
4416
+ if (n instanceof ObjectNode && n.identifier !== null) {
4417
+ byId.set(n.identifier, n);
4418
+ }
4419
+ }
4420
+ idIndex = { attr: childType.identifierAttribute, byId };
4421
+ }
4457
4422
  for (let i = 0;; i++) {
4458
4423
  const hasNewNode = i <= newValues.length - 1;
4459
4424
  const oldNode = oldNodes[i];
@@ -4468,7 +4433,7 @@ function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths
4468
4433
  // both are empty, end
4469
4434
  break;
4470
4435
  }
4471
- else if (!hasNewNode) {
4436
+ else if (oldNode && !hasNewNode) {
4472
4437
  // new one does not exists
4473
4438
  nothingChanged = false;
4474
4439
  oldNodes.splice(i, 1);
@@ -4497,13 +4462,26 @@ function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths
4497
4462
  oldNodes[i] = valueAsNode(childType, parent, newPath, newValue, oldNode);
4498
4463
  }
4499
4464
  else {
4500
- // nothing to do, try to reorder
4465
+ // nothing to do, try to reorder: find a candidate old node to reuse
4501
4466
  let oldMatch = undefined;
4502
- // find a possible candidate to reuse
4503
- for (let j = i; j < oldNodes.length; j++) {
4504
- if (areSame(oldNodes[j], newValue)) {
4467
+ if (idIndex && isPlainObject(newValue)) {
4468
+ // for an identified element type a plain-object snapshot can only match
4469
+ // by id (it is never a live node, and snapshot-reference equality still
4470
+ // implies the same id), so a miss means "nothing to reuse" without
4471
+ // scanning. areSame still verifies the single candidate.
4472
+ const candidate = idIndex.byId.get(normalizeIdentifier(newValue[idIndex.attr]));
4473
+ const j = candidate ? oldNodes.indexOf(candidate, i) : -1;
4474
+ if (j >= i && areSame(oldNodes[j], newValue)) {
4505
4475
  oldMatch = oldNodes.splice(j, 1)[0];
4506
- break;
4476
+ }
4477
+ }
4478
+ else {
4479
+ // find a possible candidate to reuse
4480
+ for (let j = i; j < oldNodes.length; j++) {
4481
+ if (areSame(oldNodes[j], newValue)) {
4482
+ oldMatch = oldNodes.splice(j, 1)[0];
4483
+ break;
4484
+ }
4507
4485
  }
4508
4486
  }
4509
4487
  nothingChanged = false;
@@ -4663,6 +4641,9 @@ class ModelType extends ComplexType {
4663
4641
  preProcessor;
4664
4642
  postProcessor;
4665
4643
  propertyNames;
4644
+ // member/property name collisions are a property of the type, so we only need
4645
+ // to check the first instance we finalize (see finalizeNewInstance)
4646
+ duplicateKeysChecked = false;
4666
4647
  constructor(opts) {
4667
4648
  super(opts.name || defaultObjectOptions.name);
4668
4649
  Object.assign(this, defaultObjectOptions, opts);
@@ -4852,6 +4833,21 @@ class ModelType extends ComplexType {
4852
4833
  mobx._interceptReads(instance, name, node.unbox);
4853
4834
  });
4854
4835
  this.initializers.reduce((self, fn) => fn(self), instance);
4836
+ // views, actions and volatile share the instance namespace with properties,
4837
+ // so a view/action reusing a property name silently clobbers that property's
4838
+ // observable value. It's a type-level mistake (identical for every instance),
4839
+ // so we check just the first one we finalize: a healthy property stays a
4840
+ // plain observable, a shadowing getter becomes a computed, and a shadowing
4841
+ // function/value stops being observable at all. The flag is only set on
4842
+ // success so a broken type keeps throwing on every create.
4843
+ if (!this.duplicateKeysChecked) {
4844
+ this.forAllProps(name => {
4845
+ if (mobx.isComputedProp(instance, name) || !mobx.isObservableProp(instance, name)) {
4846
+ throw fail(`${name} property is declared twice`);
4847
+ }
4848
+ });
4849
+ this.duplicateKeysChecked = true;
4850
+ }
4855
4851
  mobx.intercept(instance, this.willChange);
4856
4852
  mobx.observe(instance, this.didChange);
4857
4853
  }
@@ -4906,14 +4902,7 @@ class ModelType extends ComplexType {
4906
4902
  getSnapshot(node, applyPostProcess = true) {
4907
4903
  const res = {};
4908
4904
  this.forAllProps((name, type) => {
4909
- try {
4910
- // TODO: FIXME, make sure the observable ref is used!
4911
- const atom = mobx.getAtom(node.storedValue, name);
4912
- atom.reportObserved();
4913
- }
4914
- catch (_e) {
4915
- throw fail(`${name} property is declared twice`);
4916
- }
4905
+ mobx.getAtom(node.storedValue, name).reportObserved();
4917
4906
  const snapshot = this.getChildNode(node, name).snapshot;
4918
4907
  // strip-default optionals omit their key when equal to the default
4919
4908
  if (!shouldStripChildFromSnapshot(type, snapshot)) {
@@ -5525,22 +5514,14 @@ class Union extends BaseType {
5525
5514
  if (this._dispatcher) {
5526
5515
  return this._dispatcher(value);
5527
5516
  }
5528
- // fast path: when type checking is disabled, try quick structural matching first
5517
+ // fast path: when type checking is disabled, try quick structural matching
5518
+ // first. This skips full recursive validation of every property value, a
5519
+ // meaningful win for wide model members (e.g. jbrowse config schemas).
5529
5520
  if (!isTypeCheckingEnabled()) {
5530
5521
  const quickMatch = this.tryQuickMatch(value, reconcileCurrentType);
5531
5522
  if (quickMatch) {
5532
5523
  return quickMatch;
5533
5524
  }
5534
- // for plain object snapshots that didn't match via quick path, try all types
5535
- // with quick matching before falling back to full validation
5536
- // (state tree nodes must go through full validation for type identity checks)
5537
- if (isPlainObject(value) && !isStateTreeNode(value)) {
5538
- for (const type of this._types) {
5539
- if (this.snapshotLooksLikeType(value, type)) {
5540
- return type;
5541
- }
5542
- }
5543
- }
5544
5525
  }
5545
5526
  // find the most accomodating type
5546
5527
  // if we are using reconciliation try the current node type first (fix for #1045)
@@ -7068,7 +7049,6 @@ exports.joinJsonPath = joinJsonPath;
7068
7049
  exports.onAction = onAction;
7069
7050
  exports.onPatch = onPatch;
7070
7051
  exports.onSnapshot = onSnapshot;
7071
- exports.process = process$1;
7072
7052
  exports.protect = protect;
7073
7053
  exports.recordActions = recordActions;
7074
7054
  exports.recordPatches = recordPatches;