@jbrowse/mobx-state-tree 5.11.2 → 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.
- package/dist/index.d.ts +1 -1
- package/dist/mobx-state-tree.cjs +65 -80
- package/dist/mobx-state-tree.cjs.map +1 -1
- package/dist/mobx-state-tree.mjs +65 -80
- package/dist/mobx-state-tree.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -347,7 +347,7 @@ declare function getRelativePath(base: IAnyStateTreeNode, target: IAnyStateTreeN
|
|
|
347
347
|
* @param keepEnvironment indicates whether the clone should inherit the same environment (`true`, the default), or not have an environment (`false`). If an object is passed in as second argument, that will act as the environment for the cloned tree.
|
|
348
348
|
* @returns
|
|
349
349
|
*/
|
|
350
|
-
declare function clone<T extends IAnyStateTreeNode>(source: T, keepEnvironment?: boolean |
|
|
350
|
+
declare function clone<T extends IAnyStateTreeNode>(source: T, keepEnvironment?: boolean | object): T;
|
|
351
351
|
/**
|
|
352
352
|
* Removes a model element from the state tree, and let it live on as a new state tree
|
|
353
353
|
*/
|
package/dist/mobx-state-tree.cjs
CHANGED
|
@@ -766,10 +766,11 @@ function getMembers(target) {
|
|
|
766
766
|
}
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
|
-
|
|
769
|
+
const value = descriptor.value;
|
|
770
|
+
if (value?._isFlowAction === true) {
|
|
770
771
|
reflected.flowActions.push(key);
|
|
771
772
|
}
|
|
772
|
-
if (
|
|
773
|
+
if (value?._isMSTAction === true) {
|
|
773
774
|
reflected.actions.push(key);
|
|
774
775
|
}
|
|
775
776
|
else if (mobx.isObservableProp(target, key)) {
|
|
@@ -1668,9 +1669,6 @@ ObjectNode.prototype.createObservableInstance = mobx.action(ObjectNode.prototype
|
|
|
1668
1669
|
ObjectNode.prototype.detach = mobx.action(ObjectNode.prototype.detach);
|
|
1669
1670
|
ObjectNode.prototype.die = mobx.action(ObjectNode.prototype.die);
|
|
1670
1671
|
|
|
1671
|
-
// Cache for validation results to avoid re-validating the same object against the same type
|
|
1672
|
-
// Uses WeakMap so cached objects can be garbage collected
|
|
1673
|
-
const validationCache = new WeakMap();
|
|
1674
1672
|
/**
|
|
1675
1673
|
* @internal
|
|
1676
1674
|
* @hidden
|
|
@@ -1746,27 +1744,7 @@ class BaseType {
|
|
|
1746
1744
|
: typeCheckFailure(context, value);
|
|
1747
1745
|
// it is tempting to compare snapshots, but in that case we should always clone on assignments...
|
|
1748
1746
|
}
|
|
1749
|
-
|
|
1750
|
-
if (typeof value === "object" && value !== null && context.length === 1) {
|
|
1751
|
-
const typeCache = validationCache.get(value);
|
|
1752
|
-
if (typeCache) {
|
|
1753
|
-
const cached = typeCache.get(this);
|
|
1754
|
-
if (cached !== undefined) {
|
|
1755
|
-
return cached;
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
const result = this.isValidSnapshot(value, context);
|
|
1760
|
-
// cache result for object values (only at root level)
|
|
1761
|
-
if (typeof value === "object" && value !== null && context.length === 1) {
|
|
1762
|
-
let typeCache = validationCache.get(value);
|
|
1763
|
-
if (!typeCache) {
|
|
1764
|
-
typeCache = new WeakMap();
|
|
1765
|
-
validationCache.set(value, typeCache);
|
|
1766
|
-
}
|
|
1767
|
-
typeCache.set(this, result);
|
|
1768
|
-
}
|
|
1769
|
-
return result;
|
|
1747
|
+
return this.isValidSnapshot(value, context);
|
|
1770
1748
|
}
|
|
1771
1749
|
is(thing) {
|
|
1772
1750
|
return this.validate(thing, [{ path: "", type: this }]).length === 0;
|
|
@@ -3241,8 +3219,6 @@ function addHiddenWritableProp(object, propName, value) {
|
|
|
3241
3219
|
*/
|
|
3242
3220
|
class EventHandler {
|
|
3243
3221
|
handlers = [];
|
|
3244
|
-
emitting = false;
|
|
3245
|
-
pendingUnregisters = null;
|
|
3246
3222
|
get hasSubscribers() {
|
|
3247
3223
|
return this.handlers.length > 0;
|
|
3248
3224
|
}
|
|
@@ -3261,14 +3237,6 @@ class EventHandler {
|
|
|
3261
3237
|
return this.handlers.includes(fn);
|
|
3262
3238
|
}
|
|
3263
3239
|
unregister(fn) {
|
|
3264
|
-
if (this.emitting) {
|
|
3265
|
-
// defer unregistration until emit is done
|
|
3266
|
-
if (!this.pendingUnregisters) {
|
|
3267
|
-
this.pendingUnregisters = [];
|
|
3268
|
-
}
|
|
3269
|
-
this.pendingUnregisters.push(fn);
|
|
3270
|
-
return;
|
|
3271
|
-
}
|
|
3272
3240
|
const index = this.handlers.indexOf(fn);
|
|
3273
3241
|
if (index >= 0) {
|
|
3274
3242
|
this.handlers.splice(index, 1);
|
|
@@ -3278,26 +3246,10 @@ class EventHandler {
|
|
|
3278
3246
|
this.handlers.length = 0;
|
|
3279
3247
|
}
|
|
3280
3248
|
emit(...args) {
|
|
3281
|
-
//
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
f(...args);
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
finally {
|
|
3289
|
-
this.emitting = false;
|
|
3290
|
-
// process any deferred unregistrations
|
|
3291
|
-
if (this.pendingUnregisters) {
|
|
3292
|
-
for (const fn of this.pendingUnregisters) {
|
|
3293
|
-
const index = this.handlers.indexOf(fn);
|
|
3294
|
-
if (index >= 0) {
|
|
3295
|
-
this.handlers.splice(index, 1);
|
|
3296
|
-
}
|
|
3297
|
-
}
|
|
3298
|
-
this.pendingUnregisters = null;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
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));
|
|
3301
3253
|
}
|
|
3302
3254
|
}
|
|
3303
3255
|
/**
|
|
@@ -4450,6 +4402,23 @@ function array(subtype) {
|
|
|
4450
4402
|
}
|
|
4451
4403
|
function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths) {
|
|
4452
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
|
+
}
|
|
4453
4422
|
for (let i = 0;; i++) {
|
|
4454
4423
|
const hasNewNode = i <= newValues.length - 1;
|
|
4455
4424
|
const oldNode = oldNodes[i];
|
|
@@ -4493,13 +4462,26 @@ function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths
|
|
|
4493
4462
|
oldNodes[i] = valueAsNode(childType, parent, newPath, newValue, oldNode);
|
|
4494
4463
|
}
|
|
4495
4464
|
else {
|
|
4496
|
-
// nothing to do, try to reorder
|
|
4465
|
+
// nothing to do, try to reorder: find a candidate old node to reuse
|
|
4497
4466
|
let oldMatch = undefined;
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
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)) {
|
|
4501
4475
|
oldMatch = oldNodes.splice(j, 1)[0];
|
|
4502
|
-
|
|
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
|
+
}
|
|
4503
4485
|
}
|
|
4504
4486
|
}
|
|
4505
4487
|
nothingChanged = false;
|
|
@@ -4659,6 +4641,9 @@ class ModelType extends ComplexType {
|
|
|
4659
4641
|
preProcessor;
|
|
4660
4642
|
postProcessor;
|
|
4661
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;
|
|
4662
4647
|
constructor(opts) {
|
|
4663
4648
|
super(opts.name || defaultObjectOptions.name);
|
|
4664
4649
|
Object.assign(this, defaultObjectOptions, opts);
|
|
@@ -4848,6 +4833,21 @@ class ModelType extends ComplexType {
|
|
|
4848
4833
|
mobx._interceptReads(instance, name, node.unbox);
|
|
4849
4834
|
});
|
|
4850
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
|
+
}
|
|
4851
4851
|
mobx.intercept(instance, this.willChange);
|
|
4852
4852
|
mobx.observe(instance, this.didChange);
|
|
4853
4853
|
}
|
|
@@ -4902,14 +4902,7 @@ class ModelType extends ComplexType {
|
|
|
4902
4902
|
getSnapshot(node, applyPostProcess = true) {
|
|
4903
4903
|
const res = {};
|
|
4904
4904
|
this.forAllProps((name, type) => {
|
|
4905
|
-
|
|
4906
|
-
// TODO: FIXME, make sure the observable ref is used!
|
|
4907
|
-
const atom = mobx.getAtom(node.storedValue, name);
|
|
4908
|
-
atom.reportObserved();
|
|
4909
|
-
}
|
|
4910
|
-
catch (_e) {
|
|
4911
|
-
throw fail(`${name} property is declared twice`);
|
|
4912
|
-
}
|
|
4905
|
+
mobx.getAtom(node.storedValue, name).reportObserved();
|
|
4913
4906
|
const snapshot = this.getChildNode(node, name).snapshot;
|
|
4914
4907
|
// strip-default optionals omit their key when equal to the default
|
|
4915
4908
|
if (!shouldStripChildFromSnapshot(type, snapshot)) {
|
|
@@ -5521,22 +5514,14 @@ class Union extends BaseType {
|
|
|
5521
5514
|
if (this._dispatcher) {
|
|
5522
5515
|
return this._dispatcher(value);
|
|
5523
5516
|
}
|
|
5524
|
-
// fast path: when type checking is disabled, try quick structural matching
|
|
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).
|
|
5525
5520
|
if (!isTypeCheckingEnabled()) {
|
|
5526
5521
|
const quickMatch = this.tryQuickMatch(value, reconcileCurrentType);
|
|
5527
5522
|
if (quickMatch) {
|
|
5528
5523
|
return quickMatch;
|
|
5529
5524
|
}
|
|
5530
|
-
// for plain object snapshots that didn't match via quick path, try all types
|
|
5531
|
-
// with quick matching before falling back to full validation
|
|
5532
|
-
// (state tree nodes must go through full validation for type identity checks)
|
|
5533
|
-
if (isPlainObject(value) && !isStateTreeNode(value)) {
|
|
5534
|
-
for (const type of this._types) {
|
|
5535
|
-
if (this.snapshotLooksLikeType(value, type)) {
|
|
5536
|
-
return type;
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
}
|
|
5540
5525
|
}
|
|
5541
5526
|
// find the most accomodating type
|
|
5542
5527
|
// if we are using reconciliation try the current node type first (fix for #1045)
|