@jbrowse/mobx-state-tree 5.13.0 → 6.1.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 -3
- package/dist/mobx-state-tree.cjs +182 -122
- package/dist/mobx-state-tree.cjs.map +1 -1
- package/dist/mobx-state-tree.mjs +183 -123
- package/dist/mobx-state-tree.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -945,7 +945,6 @@ declare abstract class BaseType<C, S, T, N extends BaseNode<any, any, any> = Bas
|
|
|
945
945
|
*/
|
|
946
946
|
declare abstract class ComplexType<C, S, T> extends BaseType<C, S, T, ObjectNode<C, S, T>> {
|
|
947
947
|
identifierAttribute?: string;
|
|
948
|
-
constructor(name: string);
|
|
949
948
|
create(snapshot?: C, environment?: any): any;
|
|
950
949
|
getValue(node: this["N"]): T;
|
|
951
950
|
abstract getDefaultSnapshot(): C;
|
|
@@ -953,7 +952,7 @@ declare abstract class ComplexType<C, S, T> extends BaseType<C, S, T, ObjectNode
|
|
|
953
952
|
abstract finalizeNewInstance(node: this["N"], instance: any): void;
|
|
954
953
|
abstract applySnapshot(node: this["N"], snapshot: C): void;
|
|
955
954
|
abstract applyPatchLocally(node: this["N"], subpath: string, patch: IJsonPatch): void;
|
|
956
|
-
abstract processInitialSnapshot(childNodes: IChildNodesMap
|
|
955
|
+
abstract processInitialSnapshot(childNodes: IChildNodesMap): S;
|
|
957
956
|
abstract getChildren(node: this["N"]): ReadonlyArray<AnyNode>;
|
|
958
957
|
abstract getChildNode(node: this["N"], key: string): AnyNode;
|
|
959
958
|
abstract getChildType(propertyName?: string): IAnyType;
|
|
@@ -1245,7 +1244,6 @@ declare class IdentifierCache {
|
|
|
1245
1244
|
private cacheId;
|
|
1246
1245
|
private cache;
|
|
1247
1246
|
private lastCacheModificationPerId;
|
|
1248
|
-
constructor();
|
|
1249
1247
|
private updateLastCacheModificationPerId;
|
|
1250
1248
|
getLastCacheModificationPerId(identifier: string): string;
|
|
1251
1249
|
addNodeToCache(node: AnyObjectNode, lastCacheUpdate?: boolean): void;
|
package/dist/mobx-state-tree.cjs
CHANGED
|
@@ -946,7 +946,6 @@ class BaseNode {
|
|
|
946
946
|
constructor(type, parent, subpath, environment) {
|
|
947
947
|
this.type = type;
|
|
948
948
|
this.environment = environment;
|
|
949
|
-
this.environment = environment;
|
|
950
949
|
this.baseSetParent(parent, subpath);
|
|
951
950
|
}
|
|
952
951
|
getReconciliationType() {
|
|
@@ -1414,10 +1413,7 @@ class ObjectNode extends BaseNode {
|
|
|
1414
1413
|
}
|
|
1415
1414
|
_getCachedInitialSnapshot() {
|
|
1416
1415
|
if (!this._cachedInitialSnapshotCreated) {
|
|
1417
|
-
|
|
1418
|
-
const childNodes = this._childNodes;
|
|
1419
|
-
const snapshot = this._initialSnapshot;
|
|
1420
|
-
this._cachedInitialSnapshot = type.processInitialSnapshot(childNodes, snapshot);
|
|
1416
|
+
this._cachedInitialSnapshot = this.type.processInitialSnapshot(this._childNodes);
|
|
1421
1417
|
this._cachedInitialSnapshotCreated = true;
|
|
1422
1418
|
}
|
|
1423
1419
|
return this._cachedInitialSnapshot;
|
|
@@ -1810,9 +1806,6 @@ BaseType.prototype.create = mobx.action(BaseType.prototype.create);
|
|
|
1810
1806
|
*/
|
|
1811
1807
|
class ComplexType extends BaseType {
|
|
1812
1808
|
identifierAttribute;
|
|
1813
|
-
constructor(name) {
|
|
1814
|
-
super(name);
|
|
1815
|
-
}
|
|
1816
1809
|
create(snapshot = this.getDefaultSnapshot(), environment) {
|
|
1817
1810
|
return super.create(snapshot, environment);
|
|
1818
1811
|
}
|
|
@@ -2714,13 +2707,6 @@ function typeCheckFailure(context, value, message) {
|
|
|
2714
2707
|
// Clone context since it may be mutated after this error is created
|
|
2715
2708
|
return [{ context: context.slice(), value, message }];
|
|
2716
2709
|
}
|
|
2717
|
-
/**
|
|
2718
|
-
* @internal
|
|
2719
|
-
* @hidden
|
|
2720
|
-
*/
|
|
2721
|
-
function flattenTypeErrors(errors) {
|
|
2722
|
-
return errors.flat();
|
|
2723
|
-
}
|
|
2724
2710
|
// TODO; doublecheck: typecheck should only needed to be invoked from: type.create and array / map / value.property will change
|
|
2725
2711
|
/**
|
|
2726
2712
|
* @internal
|
|
@@ -2782,7 +2768,6 @@ class IdentifierCache {
|
|
|
2782
2768
|
// last time the cache (array) for a given time changed
|
|
2783
2769
|
// n.b. it is not really the time, but just an integer that gets increased after each modification to the array
|
|
2784
2770
|
lastCacheModificationPerId = mobx.observable.map();
|
|
2785
|
-
constructor() { }
|
|
2786
2771
|
updateLastCacheModificationPerId(identifier) {
|
|
2787
2772
|
const lcm = this.lastCacheModificationPerId.get(identifier);
|
|
2788
2773
|
// we start at 1 since 0 means no update since cache creation
|
|
@@ -2823,7 +2808,7 @@ class IdentifierCache {
|
|
|
2823
2808
|
if (!set.length) {
|
|
2824
2809
|
this.cache.delete(id);
|
|
2825
2810
|
}
|
|
2826
|
-
this.updateLastCacheModificationPerId(
|
|
2811
|
+
this.updateLastCacheModificationPerId(id);
|
|
2827
2812
|
}
|
|
2828
2813
|
}
|
|
2829
2814
|
}
|
|
@@ -3055,15 +3040,8 @@ function convertChildNodesToArray(childNodes) {
|
|
|
3055
3040
|
if (!childNodes) {
|
|
3056
3041
|
return EMPTY_ARRAY;
|
|
3057
3042
|
}
|
|
3058
|
-
const
|
|
3059
|
-
|
|
3060
|
-
return EMPTY_ARRAY;
|
|
3061
|
-
}
|
|
3062
|
-
const result = new Array(keys.length);
|
|
3063
|
-
keys.forEach((key, index) => {
|
|
3064
|
-
result[index] = childNodes[key];
|
|
3065
|
-
});
|
|
3066
|
-
return result;
|
|
3043
|
+
const result = Object.values(childNodes);
|
|
3044
|
+
return result.length ? result : EMPTY_ARRAY;
|
|
3067
3045
|
}
|
|
3068
3046
|
|
|
3069
3047
|
const plainObjectString = Object.toString();
|
|
@@ -3081,9 +3059,7 @@ const EMPTY_OBJECT = Object.freeze({});
|
|
|
3081
3059
|
* @internal
|
|
3082
3060
|
* @hidden
|
|
3083
3061
|
*/
|
|
3084
|
-
const mobxShallow =
|
|
3085
|
-
? { deep: false }
|
|
3086
|
-
: { deep: false, proxy: false };
|
|
3062
|
+
const mobxShallow = { deep: false };
|
|
3087
3063
|
Object.freeze(mobxShallow);
|
|
3088
3064
|
/**
|
|
3089
3065
|
* @internal
|
|
@@ -3281,10 +3257,19 @@ class EventHandler {
|
|
|
3281
3257
|
this.handlers.length = 0;
|
|
3282
3258
|
}
|
|
3283
3259
|
emit(...args) {
|
|
3284
|
-
|
|
3285
|
-
// and
|
|
3286
|
-
|
|
3287
|
-
|
|
3260
|
+
const handlers = this.handlers;
|
|
3261
|
+
// 0 and 1 handlers are by far the common cases (a patch/snapshot listener, a
|
|
3262
|
+
// disposer) and need no copy: with a single handler, calling the value we
|
|
3263
|
+
// already read is exactly what iterating a copy of it would do.
|
|
3264
|
+
if (handlers.length <= 1) {
|
|
3265
|
+
handlers[0]?.(...args);
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
// otherwise iterate a copy so (un)registrations during emit don't disturb
|
|
3269
|
+
// this pass and reentrant emits stay correct
|
|
3270
|
+
for (const f of handlers.slice()) {
|
|
3271
|
+
f(...args);
|
|
3272
|
+
}
|
|
3288
3273
|
}
|
|
3289
3274
|
}
|
|
3290
3275
|
/**
|
|
@@ -3665,6 +3650,11 @@ function escapeJsonPath(path) {
|
|
|
3665
3650
|
* Unescape slashes and backslashes.
|
|
3666
3651
|
*/
|
|
3667
3652
|
function unescapeJsonPath(path) {
|
|
3653
|
+
// every escape sequence starts with `~`, so bail before the two regex passes
|
|
3654
|
+
// for the overwhelmingly common unescaped part (mirrors escapeJsonPath above)
|
|
3655
|
+
if (!path.includes("~")) {
|
|
3656
|
+
return path;
|
|
3657
|
+
}
|
|
3668
3658
|
return path.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
3669
3659
|
}
|
|
3670
3660
|
/**
|
|
@@ -3911,7 +3901,11 @@ var MapIdentifierMode;
|
|
|
3911
3901
|
})(MapIdentifierMode || (MapIdentifierMode = {}));
|
|
3912
3902
|
class MSTMap extends mobx.ObservableMap {
|
|
3913
3903
|
constructor(initialData, name) {
|
|
3914
|
-
|
|
3904
|
+
// mobx's default (deep) enhancer. The old `observable.ref.enhancer` arg
|
|
3905
|
+
// read undefined ever since mobx 6 renamed the field to `options_.enhancer`,
|
|
3906
|
+
// so deep has been the effective behavior all along; mobx 7 dropped
|
|
3907
|
+
// `observable.ref` entirely.
|
|
3908
|
+
super(initialData, undefined, name);
|
|
3915
3909
|
}
|
|
3916
3910
|
get(key) {
|
|
3917
3911
|
// maybe this is over-enthousiastic? normalize numeric keys to strings
|
|
@@ -4151,22 +4145,21 @@ class MapType extends ComplexType {
|
|
|
4151
4145
|
applySnapshot(node, snapshot) {
|
|
4152
4146
|
typecheckInternal(this, snapshot);
|
|
4153
4147
|
const target = node.storedValue;
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4148
|
+
// keys present before the apply, minus the ones the snapshot (re)sets — what
|
|
4149
|
+
// remains has to be deleted. A Set rather than a plain object: it needs no
|
|
4150
|
+
// sentinel values, no second pass to find the survivors, and it can hold keys
|
|
4151
|
+
// like "__proto__", which an object literal silently swallows.
|
|
4152
|
+
const staleKeys = new Set(target.keys());
|
|
4158
4153
|
if (snapshot) {
|
|
4159
4154
|
// Don't use target.replace, as it will throw away all existing items first
|
|
4160
4155
|
for (const key in snapshot) {
|
|
4161
4156
|
target.set(key, snapshot[key]);
|
|
4162
|
-
|
|
4157
|
+
staleKeys.delete(key);
|
|
4163
4158
|
}
|
|
4164
4159
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
}
|
|
4169
|
-
});
|
|
4160
|
+
for (const key of staleKeys) {
|
|
4161
|
+
target.delete(key);
|
|
4162
|
+
}
|
|
4170
4163
|
}
|
|
4171
4164
|
getChildType() {
|
|
4172
4165
|
return this._subType;
|
|
@@ -4306,7 +4299,7 @@ class ArrayType extends ComplexType {
|
|
|
4306
4299
|
if (change.newValue === change.object[change.index]) {
|
|
4307
4300
|
return null;
|
|
4308
4301
|
}
|
|
4309
|
-
const updatedNodes = reconcileArrayChildren(node, subType, [childNodes[change.index]], [change.newValue],
|
|
4302
|
+
const updatedNodes = reconcileArrayChildren(node, subType, [childNodes[change.index]], [change.newValue], change.index);
|
|
4310
4303
|
if (!updatedNodes) {
|
|
4311
4304
|
return null;
|
|
4312
4305
|
}
|
|
@@ -4316,7 +4309,7 @@ class ArrayType extends ComplexType {
|
|
|
4316
4309
|
case "splice":
|
|
4317
4310
|
{
|
|
4318
4311
|
const { index, removedCount, added } = change;
|
|
4319
|
-
const addedNodes = reconcileArrayChildren(node, subType, childNodes.slice(index, index + removedCount), added,
|
|
4312
|
+
const addedNodes = reconcileArrayChildren(node, subType, childNodes.slice(index, index + removedCount), added, index);
|
|
4320
4313
|
if (!addedNodes) {
|
|
4321
4314
|
return null;
|
|
4322
4315
|
}
|
|
@@ -4442,7 +4435,11 @@ function array(subtype) {
|
|
|
4442
4435
|
assertIsType(subtype, 1);
|
|
4443
4436
|
return new ArrayType(`${subtype.name}[]`, subtype);
|
|
4444
4437
|
}
|
|
4445
|
-
|
|
4438
|
+
/**
|
|
4439
|
+
* @param firstNewPath index the reconciled slice starts at; both call sites
|
|
4440
|
+
* hand over a contiguous run, so subpath `i` is simply `firstNewPath + i`
|
|
4441
|
+
*/
|
|
4442
|
+
function reconcileArrayChildren(parent, childType, oldNodes, newValues, firstNewPath) {
|
|
4446
4443
|
let nothingChanged = true;
|
|
4447
4444
|
// When the element type is a plain model with an identifier, index the old
|
|
4448
4445
|
// nodes by id so a moved / replaced child is matched in O(1) instead of the
|
|
@@ -4465,7 +4462,7 @@ function reconcileArrayChildren(parent, childType, oldNodes, newValues, newPaths
|
|
|
4465
4462
|
const hasNewNode = i <= newValues.length - 1;
|
|
4466
4463
|
const oldNode = oldNodes[i];
|
|
4467
4464
|
let newValue = hasNewNode ? newValues[i] : undefined;
|
|
4468
|
-
const newPath = `${
|
|
4465
|
+
const newPath = `${firstNewPath + i}`;
|
|
4469
4466
|
// for some reason, instead of newValue we got a node, fallback to the storedValue
|
|
4470
4467
|
// TODO: https://github.com/mobxjs/mobx-state-tree/issues/340#issuecomment-325581681
|
|
4471
4468
|
if (isNode(newValue)) {
|
|
@@ -4589,12 +4586,14 @@ function areSame(oldNode, newValue) {
|
|
|
4589
4586
|
if (!(oldNode instanceof ObjectNode)) {
|
|
4590
4587
|
return false;
|
|
4591
4588
|
}
|
|
4589
|
+
if (oldNode.identifier === null ||
|
|
4590
|
+
!oldNode.identifierAttribute ||
|
|
4591
|
+
!isPlainObject(newValue)) {
|
|
4592
|
+
return false;
|
|
4593
|
+
}
|
|
4592
4594
|
const oldNodeType = oldNode.getReconciliationType();
|
|
4593
4595
|
// new value is a snapshot with the correct identifier
|
|
4594
|
-
return (
|
|
4595
|
-
oldNode.identifierAttribute &&
|
|
4596
|
-
isPlainObject(newValue) &&
|
|
4597
|
-
oldNodeType.is(newValue) &&
|
|
4596
|
+
return (oldNodeType.is(newValue) &&
|
|
4598
4597
|
oldNodeType.isMatchingSnapshotId(oldNode, newValue));
|
|
4599
4598
|
}
|
|
4600
4599
|
/**
|
|
@@ -4609,6 +4608,9 @@ function isArrayType(type) {
|
|
|
4609
4608
|
|
|
4610
4609
|
const PRE_PROCESS_SNAPSHOT = "preProcessSnapshot";
|
|
4611
4610
|
const POST_PROCESS_SNAPSHOT = "postProcessSnapshot";
|
|
4611
|
+
function getPropObservable(storedValue, key) {
|
|
4612
|
+
return mobx.getAtom(storedValue, key);
|
|
4613
|
+
}
|
|
4612
4614
|
function objectTypeToString() {
|
|
4613
4615
|
return getStateTreeNode(this).toString();
|
|
4614
4616
|
}
|
|
@@ -4618,16 +4620,8 @@ const defaultObjectOptions = {
|
|
|
4618
4620
|
initializers: EMPTY_ARRAY
|
|
4619
4621
|
};
|
|
4620
4622
|
function toPropertiesObject(declaredProps) {
|
|
4621
|
-
const keysList = Object.keys(declaredProps);
|
|
4622
|
-
const alreadySeenKeys = new Set();
|
|
4623
|
-
keysList.forEach(key => {
|
|
4624
|
-
if (alreadySeenKeys.has(key)) {
|
|
4625
|
-
throw fail(`${key} is declared twice in the model. Model should not contain the same keys`);
|
|
4626
|
-
}
|
|
4627
|
-
alreadySeenKeys.add(key);
|
|
4628
|
-
});
|
|
4629
4623
|
// loop through properties and ensures that all items are types
|
|
4630
|
-
return
|
|
4624
|
+
return Object.keys(declaredProps).reduce((props, key) => {
|
|
4631
4625
|
// warn if user intended a HOOK
|
|
4632
4626
|
if (key in Hook) {
|
|
4633
4627
|
throw fail(`Hook '${key}' was defined as property. Hooks should be defined as part of the actions`);
|
|
@@ -4667,7 +4661,10 @@ function toPropertiesObject(declaredProps) {
|
|
|
4667
4661
|
throw fail(`Invalid type definition for property '${key}', cannot infer a type from a value like '${value}' (${typeof value})`);
|
|
4668
4662
|
}
|
|
4669
4663
|
return props;
|
|
4670
|
-
},
|
|
4664
|
+
},
|
|
4665
|
+
// seeded with the raw declaration; every key is either replaced by its
|
|
4666
|
+
// converted type below, left alone because it already is one, or throws
|
|
4667
|
+
{ ...declaredProps });
|
|
4671
4668
|
}
|
|
4672
4669
|
/**
|
|
4673
4670
|
* @internal
|
|
@@ -4949,7 +4946,8 @@ class ModelType extends ComplexType {
|
|
|
4949
4946
|
// success so a broken type keeps throwing on every create.
|
|
4950
4947
|
if (!this.duplicateKeysChecked) {
|
|
4951
4948
|
this.forAllProps(name => {
|
|
4952
|
-
if (mobx.isComputedProp(instance, name) ||
|
|
4949
|
+
if (mobx.isComputedProp(instance, name) ||
|
|
4950
|
+
!mobx.isObservableProp(instance, name)) {
|
|
4953
4951
|
throw fail(`${name} property is declared twice`);
|
|
4954
4952
|
}
|
|
4955
4953
|
});
|
|
@@ -4989,33 +4987,52 @@ class ModelType extends ComplexType {
|
|
|
4989
4987
|
}
|
|
4990
4988
|
}
|
|
4991
4989
|
getChildren(node) {
|
|
4992
|
-
const
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4990
|
+
const names = this.propertyNames;
|
|
4991
|
+
const res = new Array(names.length);
|
|
4992
|
+
for (let i = 0; i < names.length; i++) {
|
|
4993
|
+
res[i] = this.getPropertyNode(node, names[i]);
|
|
4994
|
+
}
|
|
4996
4995
|
return res;
|
|
4997
4996
|
}
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
4997
|
+
/**
|
|
4998
|
+
* Same as {@link getChildNode} but for a key already known to be a declared
|
|
4999
|
+
* property (callers iterating `propertyNames`), so the membership guard is
|
|
5000
|
+
* skipped.
|
|
5001
|
+
*/
|
|
5002
|
+
getPropertyNode(node, key) {
|
|
5003
|
+
const childNode = getPropObservable(node.storedValue, key).raw();
|
|
5004
5004
|
if (!childNode) {
|
|
5005
5005
|
throw fail(`Node not available for property ${key}`);
|
|
5006
5006
|
}
|
|
5007
5007
|
return childNode;
|
|
5008
5008
|
}
|
|
5009
|
+
getChildNode(node, key) {
|
|
5010
|
+
if (!(key in this.properties)) {
|
|
5011
|
+
throw fail(`Not a value property: ${key}`);
|
|
5012
|
+
}
|
|
5013
|
+
return this.getPropertyNode(node, key);
|
|
5014
|
+
}
|
|
5009
5015
|
getSnapshot(node, applyPostProcess = true) {
|
|
5010
5016
|
const res = {};
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5017
|
+
const storedValue = node.storedValue;
|
|
5018
|
+
const properties = this.properties;
|
|
5019
|
+
for (const name of this.propertyNames) {
|
|
5020
|
+
// One mobx lookup serves both purposes: reportObserved so the snapshot
|
|
5021
|
+
// computed recomputes when the child is reassigned (raw() below does not
|
|
5022
|
+
// track), and raw() to read the child node. Going through getChildNode
|
|
5023
|
+
// would repeat the same lookup for every property.
|
|
5024
|
+
const observable = getPropObservable(storedValue, name);
|
|
5025
|
+
observable.reportObserved();
|
|
5026
|
+
const childNode = observable.raw();
|
|
5027
|
+
if (!childNode) {
|
|
5028
|
+
throw fail(`Node not available for property ${name}`);
|
|
5029
|
+
}
|
|
5030
|
+
const snapshot = childNode.snapshot;
|
|
5014
5031
|
// strip-default optionals omit their key when equal to the default
|
|
5015
|
-
if (!shouldStripChildFromSnapshot(
|
|
5032
|
+
if (!shouldStripChildFromSnapshot(properties[name], snapshot)) {
|
|
5016
5033
|
res[name] = snapshot;
|
|
5017
5034
|
}
|
|
5018
|
-
}
|
|
5035
|
+
}
|
|
5019
5036
|
if (applyPostProcess) {
|
|
5020
5037
|
return this.applySnapshotPostProcessor(res);
|
|
5021
5038
|
}
|
|
@@ -5079,7 +5096,10 @@ class ModelType extends ComplexType {
|
|
|
5079
5096
|
return typeCheckSuccess();
|
|
5080
5097
|
}
|
|
5081
5098
|
forAllProps(fn) {
|
|
5082
|
-
|
|
5099
|
+
const properties = this.properties;
|
|
5100
|
+
for (const key of this.propertyNames) {
|
|
5101
|
+
fn(key, properties[key]);
|
|
5102
|
+
}
|
|
5083
5103
|
}
|
|
5084
5104
|
describe() {
|
|
5085
5105
|
// optimization: cache
|
|
@@ -5549,11 +5569,16 @@ class Union extends BaseType {
|
|
|
5549
5569
|
_types;
|
|
5550
5570
|
_dispatcher;
|
|
5551
5571
|
_eager = true;
|
|
5572
|
+
// Deliberately recomputed rather than memoized: a `types.late` member reports
|
|
5573
|
+
// 0 for its subtype until the definition resolves, so the fold is not stable
|
|
5574
|
+
// over the type's lifetime and a memo would need a carve-out for exactly the
|
|
5575
|
+
// case that motivates it. Measured at ~4% of union creation, which is not
|
|
5576
|
+
// worth another cache.
|
|
5552
5577
|
get flags() {
|
|
5553
5578
|
let result = TypeFlags.Union;
|
|
5554
|
-
this._types
|
|
5579
|
+
for (const type of this._types) {
|
|
5555
5580
|
result |= type.flags;
|
|
5556
|
-
}
|
|
5581
|
+
}
|
|
5557
5582
|
return result;
|
|
5558
5583
|
}
|
|
5559
5584
|
constructor(name, _types, options) {
|
|
@@ -5695,13 +5720,9 @@ class Union extends BaseType {
|
|
|
5695
5720
|
if (reconcileCurrentType.is(value)) {
|
|
5696
5721
|
return reconcileCurrentType;
|
|
5697
5722
|
}
|
|
5698
|
-
return this._types
|
|
5699
|
-
.filter(t => t !== reconcileCurrentType)
|
|
5700
|
-
.find(type => type.is(value));
|
|
5701
|
-
}
|
|
5702
|
-
else {
|
|
5703
|
-
return this._types.find(type => type.is(value));
|
|
5723
|
+
return this._types.find(type => type !== reconcileCurrentType && type.is(value));
|
|
5704
5724
|
}
|
|
5725
|
+
return this._types.find(type => type.is(value));
|
|
5705
5726
|
}
|
|
5706
5727
|
tryQuickMatch(value, reconcileCurrentType) {
|
|
5707
5728
|
// state tree nodes need full type compatibility checking
|
|
@@ -5713,15 +5734,17 @@ class Union extends BaseType {
|
|
|
5713
5734
|
if (!isPlainObject(value)) {
|
|
5714
5735
|
return this.tryMatchPrimitive(value);
|
|
5715
5736
|
}
|
|
5716
|
-
// for objects, try structural matching against model types
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5737
|
+
// for objects, try structural matching against model types, preferring the
|
|
5738
|
+
// reconciliation type (checked first, then skipped in the main pass) so a
|
|
5739
|
+
// reconcile keeps its current member when it still fits — without building
|
|
5740
|
+
// the reordered candidate list this used to allocate on every call
|
|
5741
|
+
if (reconcileCurrentType &&
|
|
5742
|
+
this.snapshotLooksLikeType(value, reconcileCurrentType)) {
|
|
5743
|
+
return reconcileCurrentType;
|
|
5744
|
+
}
|
|
5745
|
+
for (const type of this._types) {
|
|
5746
|
+
if (type !== reconcileCurrentType &&
|
|
5747
|
+
this.snapshotLooksLikeType(value, type)) {
|
|
5725
5748
|
return type;
|
|
5726
5749
|
}
|
|
5727
5750
|
}
|
|
@@ -5836,7 +5859,7 @@ class Union extends BaseType {
|
|
|
5836
5859
|
if (applicableTypes === 1) {
|
|
5837
5860
|
return typeCheckSuccess();
|
|
5838
5861
|
}
|
|
5839
|
-
return typeCheckFailure(context, value, "No type is applicable for the union").concat(
|
|
5862
|
+
return typeCheckFailure(context, value, "No type is applicable for the union").concat(allErrors.flat());
|
|
5840
5863
|
}
|
|
5841
5864
|
getSubTypes() {
|
|
5842
5865
|
return this._types;
|
|
@@ -5936,15 +5959,15 @@ class OptionalValue extends BaseType {
|
|
|
5936
5959
|
: this.getDefaultInstanceOrSnapshot(), parent, subpath);
|
|
5937
5960
|
}
|
|
5938
5961
|
getDefaultInstanceOrSnapshot() {
|
|
5939
|
-
const
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
// generator functions must always be rechecked just in case
|
|
5944
|
-
if (typeof this._defaultValue === "function") {
|
|
5945
|
-
typecheckInternal(this, defaultInstanceOrSnapshot);
|
|
5962
|
+
const defaultValue = this._defaultValue;
|
|
5963
|
+
if (typeof defaultValue !== "function") {
|
|
5964
|
+
// static values are already snapshots, checked once by types.optional
|
|
5965
|
+
return defaultValue;
|
|
5946
5966
|
}
|
|
5947
|
-
|
|
5967
|
+
const generated = defaultValue();
|
|
5968
|
+
// generator functions must always be rechecked just in case
|
|
5969
|
+
typecheckInternal(this, generated);
|
|
5970
|
+
return generated;
|
|
5948
5971
|
}
|
|
5949
5972
|
isValidSnapshot(value, context) {
|
|
5950
5973
|
// defaulted values can be skipped
|
|
@@ -6036,33 +6059,70 @@ function isOptionalType(type) {
|
|
|
6036
6059
|
return isType(type) && (type.flags & TypeFlags.Optional) > 0;
|
|
6037
6060
|
}
|
|
6038
6061
|
/**
|
|
6039
|
-
* Compare a child snapshot to a stripped-default's reference snapshot
|
|
6040
|
-
*
|
|
6041
|
-
*
|
|
6062
|
+
* Compare a child snapshot to a stripped-default's reference snapshot: identity
|
|
6063
|
+
* for primitives, structural for objects/arrays.
|
|
6064
|
+
*
|
|
6065
|
+
* Walks the two snapshots in parallel and bails at the first difference. The
|
|
6066
|
+
* previous implementation compared `JSON.stringify(a) === JSON.stringify(b)`
|
|
6067
|
+
* behind a size guard, which is O(whole snapshot) on both sides — plus two
|
|
6068
|
+
* string allocations — even when the very first key differs. That is the normal
|
|
6069
|
+
* case for an identified sub-model: its snapshot has exactly the same shape as
|
|
6070
|
+
* the default and differs only in the identifier, so the size guard never fires
|
|
6071
|
+
* and every `getSnapshot` of the parent serialized both trees just to answer
|
|
6072
|
+
* "no".
|
|
6073
|
+
*
|
|
6074
|
+
* Snapshots are frozen, acyclic, JSON-ish plain data, so a plain recursive walk
|
|
6075
|
+
* is safe. Key order is compared too, matching what stringify comparison did:
|
|
6076
|
+
* the point is to answer "is this still the default", and the only unsafe answer
|
|
6077
|
+
* is a false positive (a key gets dropped from the snapshot that shouldn't be),
|
|
6078
|
+
* so where this can't reproduce stringify exactly — `{a: undefined}` vs
|
|
6079
|
+
* `{b: undefined}`, or `NaN` nested in a frozen value, both of which stringify
|
|
6080
|
+
* flattened into equal text — it errs toward "not equal" and simply keeps the
|
|
6081
|
+
* key. Neither shape is reachable from a model snapshot, whose key set and order
|
|
6082
|
+
* are fixed by its type.
|
|
6042
6083
|
*/
|
|
6043
6084
|
function defaultSnapshotEquals(a, b) {
|
|
6044
6085
|
if (a === b) {
|
|
6045
6086
|
return true;
|
|
6046
6087
|
}
|
|
6047
|
-
if (typeof a
|
|
6048
|
-
a
|
|
6049
|
-
typeof b
|
|
6050
|
-
b
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6088
|
+
if (typeof a !== "object" ||
|
|
6089
|
+
a === null ||
|
|
6090
|
+
typeof b !== "object" ||
|
|
6091
|
+
b === null) {
|
|
6092
|
+
return false;
|
|
6093
|
+
}
|
|
6094
|
+
const aIsArray = Array.isArray(a);
|
|
6095
|
+
if (aIsArray !== Array.isArray(b)) {
|
|
6096
|
+
return false;
|
|
6097
|
+
}
|
|
6098
|
+
if (aIsArray) {
|
|
6099
|
+
const arrayA = a;
|
|
6100
|
+
const arrayB = b;
|
|
6101
|
+
if (arrayA.length !== arrayB.length) {
|
|
6056
6102
|
return false;
|
|
6057
6103
|
}
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6104
|
+
for (let i = 0; i < arrayA.length; i++) {
|
|
6105
|
+
if (!defaultSnapshotEquals(arrayA[i], arrayB[i])) {
|
|
6106
|
+
return false;
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
return true;
|
|
6110
|
+
}
|
|
6111
|
+
const objectA = a;
|
|
6112
|
+
const objectB = b;
|
|
6113
|
+
const keysA = Object.keys(objectA);
|
|
6114
|
+
const keysB = Object.keys(objectB);
|
|
6115
|
+
if (keysA.length !== keysB.length) {
|
|
6116
|
+
return false;
|
|
6117
|
+
}
|
|
6118
|
+
for (let i = 0; i < keysA.length; i++) {
|
|
6119
|
+
const key = keysA[i];
|
|
6120
|
+
if (key !== keysB[i] ||
|
|
6121
|
+
!defaultSnapshotEquals(objectA[key], objectB[key])) {
|
|
6061
6122
|
return false;
|
|
6062
6123
|
}
|
|
6063
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
6064
6124
|
}
|
|
6065
|
-
return
|
|
6125
|
+
return true;
|
|
6066
6126
|
}
|
|
6067
6127
|
/**
|
|
6068
6128
|
* An optional type that additionally omits its key from a parent model's
|