@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.
- package/README.md +85 -12
- package/dist/index.d.ts +2 -48
- package/dist/mobx-state-tree.cjs +144 -164
- package/dist/mobx-state-tree.cjs.map +1 -1
- package/dist/mobx-state-tree.mjs +145 -164
- package/dist/mobx-state-tree.mjs.map +1 -1
- package/package.json +6 -1
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)) {
|
|
@@ -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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1529
|
-
this._internalEventsClear(
|
|
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(
|
|
1561
|
+
return this._internalEventsRegister(InternalEvents.Snapshot, onChange);
|
|
1546
1562
|
}
|
|
1547
1563
|
emitSnapshot(snapshot) {
|
|
1548
|
-
this._internalEventsEmit(
|
|
1564
|
+
this._internalEventsEmit(InternalEvents.Snapshot, snapshot);
|
|
1549
1565
|
}
|
|
1550
1566
|
onPatch(handler) {
|
|
1551
|
-
return this._internalEventsRegister(
|
|
1567
|
+
return this._internalEventsRegister(InternalEvents.Patch, handler);
|
|
1552
1568
|
}
|
|
1553
1569
|
emitPatch(basePatch, source) {
|
|
1554
|
-
if (this._internalEventsHasSubscribers(
|
|
1570
|
+
if (this._internalEventsHasSubscribers(InternalEvents.Patch)) {
|
|
1555
1571
|
const localizedPatch = {
|
|
1556
1572
|
...basePatch,
|
|
1557
|
-
path: `${source.path.
|
|
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(
|
|
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(
|
|
1583
|
+
return this._internalEventsHas(InternalEvents.Dispose, disposer);
|
|
1568
1584
|
}
|
|
1569
1585
|
addDisposer(disposer) {
|
|
1570
1586
|
if (!this.hasDisposer(disposer)) {
|
|
1571
|
-
this._internalEventsRegister(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
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
|
|
4303
|
-
if (
|
|
4304
|
-
return
|
|
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
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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;
|