@jbrowse/mobx-state-tree 6.3.0 → 6.5.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 +69 -114
- package/dist/mobx-state-tree.cjs +310 -211
- package/dist/mobx-state-tree.cjs.map +1 -1
- package/dist/mobx-state-tree.mjs +310 -212
- package/dist/mobx-state-tree.mjs.map +1 -1
- package/package.json +3 -1
package/dist/mobx-state-tree.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1741
|
-
*
|
|
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
|
-
|
|
2710
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4738
|
-
* indirect call
|
|
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
|
-
|
|
5058
|
-
result[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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
@@ -5776,7 +5725,7 @@ class Union extends BaseType {
|
|
|
5776
5725
|
return cached;
|
|
5777
5726
|
}
|
|
5778
5727
|
let result = TypeFlags.Union;
|
|
5779
|
-
for (const type of this.
|
|
5728
|
+
for (const type of this.members()) {
|
|
5780
5729
|
result |= type.flags;
|
|
5781
5730
|
}
|
|
5782
5731
|
if (!(result & TypeFlags.Late)) {
|
|
@@ -5785,12 +5734,14 @@ class Union extends BaseType {
|
|
|
5785
5734
|
return result;
|
|
5786
5735
|
}
|
|
5787
5736
|
computeName() {
|
|
5788
|
-
return `(${this.
|
|
5737
|
+
return `(${this.members()
|
|
5738
|
+
.map(type => type.name)
|
|
5739
|
+
.join(" | ")})`;
|
|
5789
5740
|
}
|
|
5790
5741
|
constructor(_types, options) {
|
|
5791
5742
|
super();
|
|
5792
5743
|
this._types = _types;
|
|
5793
|
-
// read the
|
|
5744
|
+
// read the options directly rather than spreading defaults into a fresh
|
|
5794
5745
|
// object: this constructor runs once per config slot in jbrowse, and the
|
|
5795
5746
|
// merged object was allocated only to be read twice and dropped
|
|
5796
5747
|
if (options !== undefined) {
|
|
@@ -5800,13 +5751,23 @@ class Union extends BaseType {
|
|
|
5800
5751
|
if (options.eager === false) {
|
|
5801
5752
|
this._eager = false;
|
|
5802
5753
|
}
|
|
5754
|
+
if (options.name !== undefined) {
|
|
5755
|
+
this.name = options.name;
|
|
5756
|
+
}
|
|
5803
5757
|
}
|
|
5804
5758
|
}
|
|
5759
|
+
// The one point membership is read through, so DynamicUnion can substitute
|
|
5760
|
+
// its thunk without duplicating any of the scanning logic below.
|
|
5761
|
+
members() {
|
|
5762
|
+
return this._types;
|
|
5763
|
+
}
|
|
5805
5764
|
isAssignableFrom(type) {
|
|
5806
|
-
return this.
|
|
5765
|
+
return this.members().some(subType => subType.isAssignableFrom(type));
|
|
5807
5766
|
}
|
|
5808
5767
|
describe() {
|
|
5809
|
-
return `(${this.
|
|
5768
|
+
return `(${this.members()
|
|
5769
|
+
.map(factory => factory.describe())
|
|
5770
|
+
.join(" | ")})`;
|
|
5810
5771
|
}
|
|
5811
5772
|
instantiate(parent, subpath, environment, initialValue) {
|
|
5812
5773
|
const type = this.determineType(initialValue, undefined);
|
|
@@ -5861,15 +5822,15 @@ class Union extends BaseType {
|
|
|
5861
5822
|
}
|
|
5862
5823
|
_scanForTypeDiscriminator(discriminator) {
|
|
5863
5824
|
let found;
|
|
5864
|
-
for (const t of this.
|
|
5825
|
+
for (const t of this.members()) {
|
|
5865
5826
|
const model = resolveModelType(t);
|
|
5866
5827
|
if (!model) {
|
|
5867
5828
|
continue;
|
|
5868
5829
|
}
|
|
5869
|
-
const typeProp = model.properties
|
|
5830
|
+
const typeProp = model.properties["type"];
|
|
5870
5831
|
if (!typeProp ||
|
|
5871
5832
|
!(typeProp.flags & TypeFlags.Literal) ||
|
|
5872
|
-
!typeProp
|
|
5833
|
+
!matchesLiteral(typeProp, discriminator)) {
|
|
5873
5834
|
continue;
|
|
5874
5835
|
}
|
|
5875
5836
|
if (found) {
|
|
@@ -5886,15 +5847,18 @@ class Union extends BaseType {
|
|
|
5886
5847
|
}
|
|
5887
5848
|
allMembersDiscriminated() {
|
|
5888
5849
|
if (this._allMembersDiscriminated === undefined) {
|
|
5889
|
-
this._allMembersDiscriminated = this.
|
|
5890
|
-
const model = resolveModelType(t);
|
|
5891
|
-
const typeProp = model &&
|
|
5892
|
-
model.properties.type;
|
|
5893
|
-
return !!typeProp && (typeProp.flags & TypeFlags.Literal) !== 0;
|
|
5894
|
-
});
|
|
5850
|
+
this._allMembersDiscriminated = this.computeAllMembersDiscriminated();
|
|
5895
5851
|
}
|
|
5896
5852
|
return this._allMembersDiscriminated;
|
|
5897
5853
|
}
|
|
5854
|
+
computeAllMembersDiscriminated() {
|
|
5855
|
+
return this.members().every(t => {
|
|
5856
|
+
const model = resolveModelType(t);
|
|
5857
|
+
const typeProp = model &&
|
|
5858
|
+
model.properties["type"];
|
|
5859
|
+
return !!typeProp && (typeProp.flags & TypeFlags.Literal) !== 0;
|
|
5860
|
+
});
|
|
5861
|
+
}
|
|
5898
5862
|
determineType(value, reconcileCurrentType) {
|
|
5899
5863
|
// try the dispatcher, if defined
|
|
5900
5864
|
if (this._dispatcher) {
|
|
@@ -5915,9 +5879,9 @@ class Union extends BaseType {
|
|
|
5915
5879
|
if (reconcileCurrentType.is(value)) {
|
|
5916
5880
|
return reconcileCurrentType;
|
|
5917
5881
|
}
|
|
5918
|
-
return this.
|
|
5882
|
+
return this.members().find(type => type !== reconcileCurrentType && type.is(value));
|
|
5919
5883
|
}
|
|
5920
|
-
return this.
|
|
5884
|
+
return this.members().find(type => type.is(value));
|
|
5921
5885
|
}
|
|
5922
5886
|
tryQuickMatch(value, reconcileCurrentType) {
|
|
5923
5887
|
// state tree nodes need full type compatibility checking
|
|
@@ -5937,7 +5901,7 @@ class Union extends BaseType {
|
|
|
5937
5901
|
this.snapshotLooksLikeType(value, reconcileCurrentType)) {
|
|
5938
5902
|
return reconcileCurrentType;
|
|
5939
5903
|
}
|
|
5940
|
-
for (const type of this.
|
|
5904
|
+
for (const type of this.members()) {
|
|
5941
5905
|
if (type !== reconcileCurrentType &&
|
|
5942
5906
|
this.snapshotLooksLikeType(value, type)) {
|
|
5943
5907
|
return type;
|
|
@@ -5947,7 +5911,7 @@ class Union extends BaseType {
|
|
|
5947
5911
|
}
|
|
5948
5912
|
tryMatchPrimitive(value) {
|
|
5949
5913
|
const valueType = typeof value;
|
|
5950
|
-
for (const type of this.
|
|
5914
|
+
for (const type of this.members()) {
|
|
5951
5915
|
const flags = type.flags;
|
|
5952
5916
|
if ((valueType === "string" && flags & TypeFlags.String) ||
|
|
5953
5917
|
(valueType === "number" &&
|
|
@@ -5963,7 +5927,7 @@ class Union extends BaseType {
|
|
|
5963
5927
|
}
|
|
5964
5928
|
// for literals, check exact value match
|
|
5965
5929
|
if (flags & TypeFlags.Literal) {
|
|
5966
|
-
if (type
|
|
5930
|
+
if (matchesLiteral(type, value)) {
|
|
5967
5931
|
return type;
|
|
5968
5932
|
}
|
|
5969
5933
|
}
|
|
@@ -5995,7 +5959,7 @@ class Union extends BaseType {
|
|
|
5995
5959
|
// for literal types, verify the value matches exactly
|
|
5996
5960
|
// this is critical for discriminated unions
|
|
5997
5961
|
if (flags & TypeFlags.Literal) {
|
|
5998
|
-
if (!propType
|
|
5962
|
+
if (!matchesLiteral(propType, propValue)) {
|
|
5999
5963
|
return false;
|
|
6000
5964
|
}
|
|
6001
5965
|
}
|
|
@@ -6034,10 +5998,11 @@ class Union extends BaseType {
|
|
|
6034
5998
|
// for plain-object snapshots, prefer union members whose literal-typed
|
|
6035
5999
|
// discriminator properties match the value (e.g. {type: "MsaView"})
|
|
6036
6000
|
// so error output is scoped to the intended branch instead of every member
|
|
6001
|
+
const members = this.members();
|
|
6037
6002
|
const candidates = isSnapshotObject
|
|
6038
|
-
?
|
|
6003
|
+
? members.filter(t => this.snapshotLooksLikeType(value, t))
|
|
6039
6004
|
: [];
|
|
6040
|
-
const typesToValidate = candidates.length > 0 ? candidates :
|
|
6005
|
+
const typesToValidate = candidates.length > 0 ? candidates : members;
|
|
6041
6006
|
const allErrors = [];
|
|
6042
6007
|
let applicableTypes = 0;
|
|
6043
6008
|
for (const type of typesToValidate) {
|
|
@@ -6060,7 +6025,7 @@ class Union extends BaseType {
|
|
|
6060
6025
|
return typeCheckFailure(context, value, "No type is applicable for the union").concat(allErrors.flat());
|
|
6061
6026
|
}
|
|
6062
6027
|
getSubTypes() {
|
|
6063
|
-
return this.
|
|
6028
|
+
return this.members();
|
|
6064
6029
|
}
|
|
6065
6030
|
}
|
|
6066
6031
|
// Defaults shared by every union; see the field declarations at the top of the
|
|
@@ -6071,9 +6036,82 @@ Object.assign(Union.prototype, {
|
|
|
6071
6036
|
_discriminatorCache: undefined,
|
|
6072
6037
|
_allMembersDiscriminated: undefined
|
|
6073
6038
|
});
|
|
6039
|
+
/**
|
|
6040
|
+
* @internal
|
|
6041
|
+
* @hidden
|
|
6042
|
+
*
|
|
6043
|
+
* A union whose membership is read fresh from a thunk on every operation, so
|
|
6044
|
+
* members that come into existence after the union was built — a registry of
|
|
6045
|
+
* pluggable types whose modules load on demand — still dispatch, validate, and
|
|
6046
|
+
* satisfy references. That last one is the reason this exists: reference
|
|
6047
|
+
* resolution asks the target type `isAssignableFrom(node.type)` through the
|
|
6048
|
+
* identifier cache, and a membership list frozen at construction would reject
|
|
6049
|
+
* every late-loaded member's instances.
|
|
6050
|
+
*
|
|
6051
|
+
* Every membership-derived cache the base class keeps is bypassed here: flags
|
|
6052
|
+
* and the folded name are recomputed per read, and the discriminator scan is
|
|
6053
|
+
* uncached so a miss stays retryable once the missing member registers. A hit
|
|
6054
|
+
* for a given discriminator is assumed stable — registering two members with
|
|
6055
|
+
* the same `type` literal at different times can leave a stale unambiguous
|
|
6056
|
+
* answer where the base class would have reported ambiguity.
|
|
6057
|
+
*/
|
|
6058
|
+
class DynamicUnion extends Union {
|
|
6059
|
+
_members;
|
|
6060
|
+
constructor(members, options) {
|
|
6061
|
+
super([], options);
|
|
6062
|
+
this._members = members;
|
|
6063
|
+
}
|
|
6064
|
+
members() {
|
|
6065
|
+
return this._members();
|
|
6066
|
+
}
|
|
6067
|
+
get flags() {
|
|
6068
|
+
let result = TypeFlags.Union;
|
|
6069
|
+
for (const type of this.members()) {
|
|
6070
|
+
result |= type.flags;
|
|
6071
|
+
}
|
|
6072
|
+
return result;
|
|
6073
|
+
}
|
|
6074
|
+
get name() {
|
|
6075
|
+
return this._explicitName !== undefined
|
|
6076
|
+
? this._explicitName
|
|
6077
|
+
: this.computeName();
|
|
6078
|
+
}
|
|
6079
|
+
set name(value) {
|
|
6080
|
+
this._explicitName = value;
|
|
6081
|
+
}
|
|
6082
|
+
_findCandidateByTypeDiscriminator(discriminator) {
|
|
6083
|
+
return this._scanForTypeDiscriminator(discriminator);
|
|
6084
|
+
}
|
|
6085
|
+
allMembersDiscriminated() {
|
|
6086
|
+
return this.computeAllMembersDiscriminated();
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
const unionInternRoot = {};
|
|
6090
|
+
function internUnion(types) {
|
|
6091
|
+
let node = unionInternRoot;
|
|
6092
|
+
for (const type of types) {
|
|
6093
|
+
const level = (node.next ??= new WeakMap());
|
|
6094
|
+
const existing = level.get(type);
|
|
6095
|
+
if (existing) {
|
|
6096
|
+
node = existing;
|
|
6097
|
+
}
|
|
6098
|
+
else {
|
|
6099
|
+
node = {};
|
|
6100
|
+
level.set(type, node);
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
return (node.union ??= new Union(types));
|
|
6104
|
+
}
|
|
6074
6105
|
/**
|
|
6075
6106
|
* `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
6107
|
*
|
|
6108
|
+
* Passing `members: () => Type[]` in the options (with no positional member
|
|
6109
|
+
* types) creates a union whose membership is re-read from the thunk on every
|
|
6110
|
+
* operation, so types registered after construction — e.g. from a lazily
|
|
6111
|
+
* loaded module — participate in dispatch, validation and reference
|
|
6112
|
+
* resolution. Pair it with a `dispatcher` for snapshot dispatch and a `name`
|
|
6113
|
+
* for stable error messages.
|
|
6114
|
+
*
|
|
6077
6115
|
* @param optionsOrType
|
|
6078
6116
|
* @param otherTypes
|
|
6079
6117
|
* @returns
|
|
@@ -6093,13 +6131,19 @@ function union(...args) {
|
|
|
6093
6131
|
// check all options
|
|
6094
6132
|
if (devMode()) {
|
|
6095
6133
|
if (options) {
|
|
6096
|
-
assertArg(options, o => isPlainObject(o), "object { eager?: boolean, dispatcher?: Function }", 1);
|
|
6134
|
+
assertArg(options, o => isPlainObject(o), "object { eager?: boolean, dispatcher?: Function, members?: Function, name?: string }", 1);
|
|
6097
6135
|
}
|
|
6098
6136
|
types.forEach((type, i) => {
|
|
6099
6137
|
assertIsType(type, options ? i + 2 : i + 1);
|
|
6100
6138
|
});
|
|
6101
6139
|
}
|
|
6102
|
-
|
|
6140
|
+
if (options?.members !== undefined) {
|
|
6141
|
+
if (types.length > 0) {
|
|
6142
|
+
throw fail("union(): the members option cannot be combined with positional member types");
|
|
6143
|
+
}
|
|
6144
|
+
return new DynamicUnion(options.members, options);
|
|
6145
|
+
}
|
|
6146
|
+
return options === undefined ? internUnion(types) : new Union(types, options);
|
|
6103
6147
|
}
|
|
6104
6148
|
/**
|
|
6105
6149
|
* Returns if a given value represents a union type.
|
|
@@ -6294,13 +6338,35 @@ const undefinedAsOptionalValues = [undefined];
|
|
|
6294
6338
|
* to compile. Guards with a distinct narrowing target (`isArrayType`,
|
|
6295
6339
|
* `isMapType`, `isModelType`) keep their predicate.
|
|
6296
6340
|
*
|
|
6297
|
-
* @template IT
|
|
6298
6341
|
* @param type
|
|
6299
6342
|
* @returns
|
|
6300
6343
|
*/
|
|
6301
6344
|
function isOptionalType(type) {
|
|
6302
6345
|
return isType(type) && (type.flags & TypeFlags.Optional) > 0;
|
|
6303
6346
|
}
|
|
6347
|
+
function isRecord(value) {
|
|
6348
|
+
return typeof value === "object" && value !== null;
|
|
6349
|
+
}
|
|
6350
|
+
/**
|
|
6351
|
+
* The identifier attribute of the model a stripDefault ultimately wraps, or
|
|
6352
|
+
* `null` when there is none. Drills through the single-subtype wrappers the way
|
|
6353
|
+
* `resolveModelType` in union.ts does — that one is module-private there, and
|
|
6354
|
+
* this file may not reach into it.
|
|
6355
|
+
*
|
|
6356
|
+
* `null` is also the answer for an unresolved `types.late`, which only costs the
|
|
6357
|
+
* short-circuit, never correctness: the full structural walk still runs.
|
|
6358
|
+
*/
|
|
6359
|
+
function resolveIdentifierAttribute(type) {
|
|
6360
|
+
let current = type;
|
|
6361
|
+
for (let depth = 0; current && depth < 20; depth++) {
|
|
6362
|
+
if (current instanceof ModelType) {
|
|
6363
|
+
return current.identifierAttribute ? current.identifierAttribute : null;
|
|
6364
|
+
}
|
|
6365
|
+
const wrapper = current;
|
|
6366
|
+
current = wrapper._subtype ?? wrapper.getSubType?.(false);
|
|
6367
|
+
}
|
|
6368
|
+
return null;
|
|
6369
|
+
}
|
|
6304
6370
|
/**
|
|
6305
6371
|
* Compare a child snapshot to a stripped-default's reference snapshot: identity
|
|
6306
6372
|
* for primitives, structural for objects/arrays.
|
|
@@ -6379,6 +6445,22 @@ function defaultSnapshotEquals(a, b) {
|
|
|
6379
6445
|
* @internal
|
|
6380
6446
|
*/
|
|
6381
6447
|
class StripDefaultValue extends OptionalValue {
|
|
6448
|
+
equalsDefault(snapshot, defaultSnapshot) {
|
|
6449
|
+
let identifierAttribute = this._identifierAttribute;
|
|
6450
|
+
if (identifierAttribute === undefined) {
|
|
6451
|
+
identifierAttribute = resolveIdentifierAttribute(this.getSubTypes());
|
|
6452
|
+
this._identifierAttribute = identifierAttribute;
|
|
6453
|
+
}
|
|
6454
|
+
// an identified model's snapshot normally has the same shape as the default
|
|
6455
|
+
// and differs only here, so answering from one key beats walking every key
|
|
6456
|
+
// (including nested objects) until the walk reaches it
|
|
6457
|
+
return identifierAttribute !== null &&
|
|
6458
|
+
isRecord(snapshot) &&
|
|
6459
|
+
isRecord(defaultSnapshot) &&
|
|
6460
|
+
snapshot[identifierAttribute] !== defaultSnapshot[identifierAttribute]
|
|
6461
|
+
? false
|
|
6462
|
+
: defaultSnapshotEquals(snapshot, defaultSnapshot);
|
|
6463
|
+
}
|
|
6382
6464
|
shouldStripFromSnapshot(snapshot) {
|
|
6383
6465
|
if (!this._defaultSnapshot) {
|
|
6384
6466
|
// instantiate the subtype detached with the default and read the node's
|
|
@@ -6387,11 +6469,35 @@ class StripDefaultValue extends OptionalValue {
|
|
|
6387
6469
|
const node = this.getSubTypes().instantiate(null, "", undefined, this.getDefaultInstanceOrSnapshot());
|
|
6388
6470
|
this._defaultSnapshot = { value: node.snapshot };
|
|
6389
6471
|
}
|
|
6390
|
-
|
|
6472
|
+
const defaultSnapshot = this._defaultSnapshot.value;
|
|
6473
|
+
let result;
|
|
6474
|
+
if (isRecord(snapshot)) {
|
|
6475
|
+
// a child node's snapshot is a keepAlive computed, so a stable reference
|
|
6476
|
+
// means unchanged content: the same object always gets the same answer
|
|
6477
|
+
let cache = this._equalsDefaultCache;
|
|
6478
|
+
if (!cache) {
|
|
6479
|
+
cache = new WeakMap();
|
|
6480
|
+
this._equalsDefaultCache = cache;
|
|
6481
|
+
}
|
|
6482
|
+
const cached = cache.get(snapshot);
|
|
6483
|
+
if (cached === undefined) {
|
|
6484
|
+
result = this.equalsDefault(snapshot, defaultSnapshot);
|
|
6485
|
+
cache.set(snapshot, result);
|
|
6486
|
+
}
|
|
6487
|
+
else {
|
|
6488
|
+
result = cached;
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
else {
|
|
6492
|
+
result = defaultSnapshotEquals(snapshot, defaultSnapshot);
|
|
6493
|
+
}
|
|
6494
|
+
return result;
|
|
6391
6495
|
}
|
|
6392
6496
|
}
|
|
6393
6497
|
Object.assign(StripDefaultValue.prototype, {
|
|
6394
|
-
_defaultSnapshot: undefined
|
|
6498
|
+
_defaultSnapshot: undefined,
|
|
6499
|
+
_identifierAttribute: undefined,
|
|
6500
|
+
_equalsDefaultCache: undefined
|
|
6395
6501
|
});
|
|
6396
6502
|
/**
|
|
6397
6503
|
* Whether `type` is a strip-default optional whose current child `snapshot`
|
|
@@ -6621,6 +6727,13 @@ class Lazy extends SimpleType {
|
|
|
6621
6727
|
}
|
|
6622
6728
|
reconcile(current, value, parent, subpath) {
|
|
6623
6729
|
if (this.loadedType) {
|
|
6730
|
+
// a node created after load carries the loaded type: let it reconcile so
|
|
6731
|
+
// the instance survives applySnapshot instead of dying on every write
|
|
6732
|
+
const loaded = this.loadedType;
|
|
6733
|
+
const currentType = current.type;
|
|
6734
|
+
if (currentType === loaded) {
|
|
6735
|
+
return this.loadedType.reconcile(current, value, parent, subpath);
|
|
6736
|
+
}
|
|
6624
6737
|
current.die();
|
|
6625
6738
|
return this.loadedType.instantiate(parent, subpath, parent.environment, value);
|
|
6626
6739
|
}
|
|
@@ -6810,9 +6923,6 @@ class BaseReferenceType extends SimpleType {
|
|
|
6810
6923
|
computeName() {
|
|
6811
6924
|
return `reference(${this.targetType.name})`;
|
|
6812
6925
|
}
|
|
6813
|
-
describe() {
|
|
6814
|
-
return this.name;
|
|
6815
|
-
}
|
|
6816
6926
|
isAssignableFrom(type) {
|
|
6817
6927
|
return this.targetType.isAssignableFrom(type);
|
|
6818
6928
|
}
|
|
@@ -7149,9 +7259,6 @@ class IdentifierType extends BaseIdentifierType {
|
|
|
7149
7259
|
constructor() {
|
|
7150
7260
|
super(`identifier`, "string");
|
|
7151
7261
|
}
|
|
7152
|
-
describe() {
|
|
7153
|
-
return `identifier`;
|
|
7154
|
-
}
|
|
7155
7262
|
}
|
|
7156
7263
|
/**
|
|
7157
7264
|
* @internal
|
|
@@ -7161,12 +7268,6 @@ class IdentifierNumberType extends BaseIdentifierType {
|
|
|
7161
7268
|
constructor() {
|
|
7162
7269
|
super("identifierNumber", "number");
|
|
7163
7270
|
}
|
|
7164
|
-
getSnapshot(node) {
|
|
7165
|
-
return node.storedValue;
|
|
7166
|
-
}
|
|
7167
|
-
describe() {
|
|
7168
|
-
return `identifierNumber`;
|
|
7169
|
-
}
|
|
7170
7271
|
}
|
|
7171
7272
|
/**
|
|
7172
7273
|
* `types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works.
|
|
@@ -7297,9 +7398,6 @@ class CustomType extends SimpleType {
|
|
|
7297
7398
|
super(options.name);
|
|
7298
7399
|
this.options = options;
|
|
7299
7400
|
}
|
|
7300
|
-
describe() {
|
|
7301
|
-
return this.name;
|
|
7302
|
-
}
|
|
7303
7401
|
isValidSnapshot(value, context) {
|
|
7304
7402
|
if (this.options.isTargetType(value)) {
|
|
7305
7403
|
return typeCheckSuccess();
|
|
@@ -7508,6 +7606,7 @@ exports.addMiddleware = addMiddleware;
|
|
|
7508
7606
|
exports.applyAction = applyAction;
|
|
7509
7607
|
exports.applyPatch = applyPatch;
|
|
7510
7608
|
exports.applySnapshot = applySnapshot;
|
|
7609
|
+
exports.cannotDetermineSubtype = cannotDetermineSubtype;
|
|
7511
7610
|
exports.cast = cast;
|
|
7512
7611
|
exports.castFlowReturn = castFlowReturn;
|
|
7513
7612
|
exports.castToReferenceSnapshot = castToReferenceSnapshot;
|