mobx-keystone 1.24.0 → 1.25.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/CHANGELOG.md +18 -0
- package/LICENSE +21 -21
- package/README.md +16 -11
- package/dist/mobx-keystone.esm.js +204 -202
- package/dist/mobx-keystone.esm.mjs +204 -202
- package/dist/mobx-keystone.umd.js +204 -202
- package/dist/types/modelShared/BaseModelShared.d.ts +7 -1
- package/dist/types/modelShared/prop.d.ts +5 -0
- package/dist/types/types/TypeChecker.d.ts +79 -0
- package/dist/types/types/schemas.d.ts +15 -0
- package/package.json +10 -9
- package/src/actionMiddlewares/undoMiddleware.ts +11 -14
- package/src/model/BaseModel.ts +11 -5
- package/src/model/newModel.ts +11 -4
- package/src/modelShared/BaseModelShared.ts +1 -1
- package/src/modelShared/prop.ts +1 -1
- package/src/parent/findParent.ts +14 -9
- package/src/parent/onChildAttachedTo.ts +1 -12
- package/src/parent/path.ts +6 -4
- package/src/parent/setParent.ts +26 -14
- package/src/patch/applyPatches.ts +10 -30
- package/src/snapshot/applySnapshot.ts +9 -0
- package/src/snapshot/fromSnapshot.ts +3 -1
- package/src/snapshot/modelInitialData.ts +14 -19
- package/src/snapshot/reconcileModelSnapshot.ts +11 -3
- package/src/snapshot/reconcileSnapshot.ts +13 -4
- package/src/tweaker/tweak.ts +6 -7
- package/src/tweaker/tweakArray.ts +3 -17
- package/src/tweaker/tweakFrozen.ts +0 -1
- package/src/tweaker/tweakModel.ts +0 -1
- package/src/tweaker/tweakPlainObject.ts +2 -6
- package/src/tweaker/typeChecking.ts +9 -19
- package/src/types/TypeChecker.ts +6 -6
- package/src/types/schemas.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { remove } from "mobx"
|
|
1
|
+
import { remove, set } from "mobx"
|
|
2
2
|
import type { AnyModel } from "../model/BaseModel"
|
|
3
3
|
import { getModelIdPropertyName } from "../model/getModelMetadata"
|
|
4
4
|
import { isReservedModelKey, modelIdKey, modelTypeKey } from "../model/metadata"
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
runTypeCheckingAfterChange,
|
|
18
18
|
} from "../tweaker/typeChecking"
|
|
19
19
|
import { withoutTypeChecking } from "../tweaker/withoutTypeChecking"
|
|
20
|
-
import { isArray } from "../utils"
|
|
20
|
+
import { isArray, isPrimitive } from "../utils"
|
|
21
21
|
import { withErrorModelTrailEntry, withErrorPathSegment } from "../utils/errorDiagnostics"
|
|
22
22
|
import type { ModelPool } from "../utils/ModelPool"
|
|
23
23
|
import { setIfDifferent } from "../utils/setIfDifferent"
|
|
@@ -51,7 +51,7 @@ function reconcileModelSnapshot(
|
|
|
51
51
|
// the canonical output snapshot. A successful canonical no-op therefore
|
|
52
52
|
// cannot produce a diagnostic, so keep this hottest path outside the
|
|
53
53
|
// model-trail bookkeeping and defer id metadata until needed. The
|
|
54
|
-
// positional value was already flushed by the
|
|
54
|
+
// positional value was already flushed by the internal snapshot read in
|
|
55
55
|
// reconcileSnapshot, so its transformed snapshot is current.
|
|
56
56
|
if (isCanonicalSnapshotNoOp(value, type, sn)) {
|
|
57
57
|
return value
|
|
@@ -157,6 +157,14 @@ function reconcileModelSnapshot(
|
|
|
157
157
|
const v = processedSn[k]
|
|
158
158
|
|
|
159
159
|
const oldValue = data[k]
|
|
160
|
+
// Non-null primitives need neither reconciliation nor detachment.
|
|
161
|
+
// Nullish values must still pass through default handling below.
|
|
162
|
+
if (v != null && isPrimitive(v)) {
|
|
163
|
+
if (v !== oldValue) {
|
|
164
|
+
set(data, k, v)
|
|
165
|
+
}
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
160
168
|
let newValue = withErrorPathSegment(k, () =>
|
|
161
169
|
reconcileSnapshot(oldValue, v, modelPool, modelObj)
|
|
162
170
|
)
|
|
@@ -5,6 +5,7 @@ import { fastGetParentPathIncludingDataObjects } from "../parent"
|
|
|
5
5
|
import { isMap, isPrimitive, isSet } from "../utils"
|
|
6
6
|
import type { ModelPool } from "../utils/ModelPool"
|
|
7
7
|
import { getSnapshot } from "./getSnapshot"
|
|
8
|
+
import { flushInternalSnapshot, getInternalSnapshot } from "./internal"
|
|
8
9
|
import { registerDefaultReconcilers } from "./registerDefaultReconcilers"
|
|
9
10
|
import { SnapshotProcessingError } from "./SnapshotProcessingError"
|
|
10
11
|
|
|
@@ -28,10 +29,18 @@ export function reconcileSnapshot(value: any, sn: any, modelPool: ModelPool, par
|
|
|
28
29
|
return sn
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Unlike getSnapshot, this comparison never exposes the snapshot, so it needs
|
|
33
|
+
// neither freezing nor observation registration.
|
|
34
|
+
const currentSnapshot = getInternalSnapshot(value)
|
|
35
|
+
if (currentSnapshot) {
|
|
36
|
+
flushInternalSnapshot(value, false)
|
|
37
|
+
if (currentSnapshot.transformed === sn) {
|
|
38
|
+
return value
|
|
39
|
+
}
|
|
40
|
+
} else if (!isPrimitive(value)) {
|
|
41
|
+
// A non-primitive without an internal snapshot is not a valid reconciliation
|
|
42
|
+
// target (e.g. a non-node object); let getSnapshot raise the proper error.
|
|
43
|
+
getSnapshot(value)
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
registerDefaultReconcilers()
|
package/src/tweaker/tweak.ts
CHANGED
|
@@ -86,16 +86,11 @@ export function registerTweaker<T>(priority: number, tweaker: Tweaker<T>): void
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function internalTweak<T>(value: T, parentPath: ParentPath<any> | undefined): T {
|
|
89
|
-
if (isPrimitive(value)) {
|
|
90
|
-
return value
|
|
91
|
-
}
|
|
92
|
-
|
|
93
89
|
// already tweaked
|
|
94
90
|
if (isTweakedObject(value, true)) {
|
|
95
91
|
value = setParent(
|
|
96
92
|
value,
|
|
97
93
|
parentPath,
|
|
98
|
-
false, // indexChangeAllowed
|
|
99
94
|
false, // isDataObject
|
|
100
95
|
true // cloneIfApplicable
|
|
101
96
|
)
|
|
@@ -135,10 +130,15 @@ function internalTweak<T>(value: T, parentPath: ParentPath<any> | undefined): T
|
|
|
135
130
|
)
|
|
136
131
|
}
|
|
137
132
|
|
|
133
|
+
const tweakNonPrimitive = action("tweak", internalTweak)
|
|
134
|
+
|
|
138
135
|
/**
|
|
139
136
|
* @internal
|
|
140
137
|
*/
|
|
141
|
-
export
|
|
138
|
+
export function tweak<T>(value: T, parentPath: ParentPath<any> | undefined): T {
|
|
139
|
+
// Primitives need no tree bookkeeping or MobX action boundary.
|
|
140
|
+
return isPrimitive(value) ? value : tweakNonPrimitive(value, parentPath)
|
|
141
|
+
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* @internal
|
|
@@ -169,7 +169,6 @@ export function tryUntweak(value: any): (() => void) | undefined {
|
|
|
169
169
|
setParent(
|
|
170
170
|
children[i], // value
|
|
171
171
|
undefined, // parentPath
|
|
172
|
-
false, // indexChangeAllowed
|
|
173
172
|
false, // isDataObject
|
|
174
173
|
// no need to clone if unsetting the parent
|
|
175
174
|
false // cloneIfApplicable
|
|
@@ -12,7 +12,7 @@ import { assertCanWrite } from "../action/protection"
|
|
|
12
12
|
import { emitArraySpliceDeepChange, emitArrayUpdateDeepChange } from "../deepChange/onDeepChange"
|
|
13
13
|
import { getGlobalConfig } from "../globalConfig"
|
|
14
14
|
import type { ParentPath } from "../parent/path"
|
|
15
|
-
import { setParent } from "../parent/setParent"
|
|
15
|
+
import { reindexArrayChildren, setParent } from "../parent/setParent"
|
|
16
16
|
import { hasPatchListenersFor, InternalPatchRecorder } from "../patch/emitPatch"
|
|
17
17
|
import type { Patch } from "../patch/Patch"
|
|
18
18
|
import {
|
|
@@ -62,7 +62,6 @@ export function tweakArray<T extends any[]>(
|
|
|
62
62
|
setParent(
|
|
63
63
|
tweakedArr, // value
|
|
64
64
|
parentPath,
|
|
65
|
-
false, // indexChangeAllowed
|
|
66
65
|
false, // isDataObject
|
|
67
66
|
// arrays shouldn't be cloned anyway
|
|
68
67
|
false // cloneIfApplicable
|
|
@@ -97,7 +96,6 @@ export function tweakArray<T extends any[]>(
|
|
|
97
96
|
setParent(
|
|
98
97
|
tweakedValue, // value
|
|
99
98
|
path, // parentPath
|
|
100
|
-
false, // indexChangeAllowed
|
|
101
99
|
false, // isDataObject
|
|
102
100
|
// the value is already a new value (the result of a fromSnapshot)
|
|
103
101
|
false // cloneIfApplicable
|
|
@@ -482,20 +480,8 @@ function interceptArrayMutationSplice(change: IArrayWillSplice) {
|
|
|
482
480
|
const oldNextIndex = change.index + change.removedCount
|
|
483
481
|
const newNextIndex = change.index + change.added.length
|
|
484
482
|
|
|
485
|
-
if (oldNextIndex !== newNextIndex) {
|
|
486
|
-
|
|
487
|
-
setParent(
|
|
488
|
-
change.object[i], // value
|
|
489
|
-
{
|
|
490
|
-
parent: change.object,
|
|
491
|
-
path: j,
|
|
492
|
-
}, // parentPath
|
|
493
|
-
true, // indexChangeAllowed
|
|
494
|
-
false, // isDataObject
|
|
495
|
-
// just re-indexing
|
|
496
|
-
false // cloneIfApplicable
|
|
497
|
-
)
|
|
498
|
-
}
|
|
483
|
+
if (oldNextIndex !== newNextIndex && oldNextIndex < change.object.length) {
|
|
484
|
+
reindexArrayChildren(change.object, oldNextIndex, newNextIndex)
|
|
499
485
|
}
|
|
500
486
|
}
|
|
501
487
|
|
|
@@ -59,7 +59,6 @@ export function tweakPlainObject<T extends Record<string, any>>(
|
|
|
59
59
|
setParent(
|
|
60
60
|
tweakedObj, // value
|
|
61
61
|
parentPath,
|
|
62
|
-
false, // indexChangeAllowed
|
|
63
62
|
isDataObject,
|
|
64
63
|
// an object shouldn't be cloned
|
|
65
64
|
false // cloneIfApplicable
|
|
@@ -67,12 +66,10 @@ export function tweakPlainObject<T extends Record<string, any>>(
|
|
|
67
66
|
|
|
68
67
|
const initialDataSnapshot =
|
|
69
68
|
isDataObject && snapshotModelType ? takeModelInitialDataSnapshot(tweakedObj) : undefined
|
|
70
|
-
const
|
|
71
|
-
!!initialDataSnapshot?.reusable && initialDataSnapshot.allValuesPrimitive
|
|
72
|
-
const untransformedSn: any = reuseInitialDataSnapshot ? initialDataSnapshot.snapshot : {}
|
|
69
|
+
const untransformedSn: any = initialDataSnapshot ?? {}
|
|
73
70
|
|
|
74
71
|
// substitute initial values by tweaked values
|
|
75
|
-
const originalObjKeys =
|
|
72
|
+
const originalObjKeys = initialDataSnapshot ? [] : Object.keys(originalObj)
|
|
76
73
|
const originalObjKeysLen = originalObjKeys.length
|
|
77
74
|
for (let i = 0; i < originalObjKeysLen; i++) {
|
|
78
75
|
const k = originalObjKeys[i]
|
|
@@ -96,7 +93,6 @@ export function tweakPlainObject<T extends Record<string, any>>(
|
|
|
96
93
|
setParent(
|
|
97
94
|
tweakedValue, // value
|
|
98
95
|
path, // parentPath
|
|
99
|
-
false, // indexChangeAllowed
|
|
100
96
|
false, // isDataObject
|
|
101
97
|
// the value is already a new value (the result of a fromSnapshot)
|
|
102
98
|
false // cloneIfApplicable
|
|
@@ -3,8 +3,7 @@ import type { AnyModel } from "../model/BaseModel"
|
|
|
3
3
|
import { getModelMetadata } from "../model/getModelMetadata"
|
|
4
4
|
import { isModel } from "../model/utils"
|
|
5
5
|
import { dataToModelNode } from "../parent/core"
|
|
6
|
-
import {
|
|
7
|
-
import type { PathElement } from "../parent/pathTypes"
|
|
6
|
+
import { fastGetParent } from "../parent/path"
|
|
8
7
|
import { internalApplyPatches } from "../patch/applyPatches"
|
|
9
8
|
import type { InternalPatchRecorder } from "../patch/emitPatch"
|
|
10
9
|
import { internalApplySnapshot } from "../snapshot/applySnapshot"
|
|
@@ -39,29 +38,20 @@ function isModelWithTypeChecker(obj: object): obj is AnyModel {
|
|
|
39
38
|
*/
|
|
40
39
|
function forEachTypedModelAncestor(obj: object, callback: (model: AnyModel) => void): void {
|
|
41
40
|
// obj might be a $ data object, so resolve to the model if applicable
|
|
42
|
-
|
|
43
|
-
let lastAncestor: AnyModel | undefined
|
|
41
|
+
const start = dataToModelNode(obj)
|
|
44
42
|
|
|
45
43
|
// If we started from a $ data object, check the model itself
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
callback(current)
|
|
44
|
+
if (start !== obj && isModelWithTypeChecker(start)) {
|
|
45
|
+
callback(start)
|
|
49
46
|
}
|
|
50
47
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!parentPath) break
|
|
56
|
-
|
|
57
|
-
const parent: object = parentPath.parent
|
|
58
|
-
|
|
59
|
-
if (isModelWithTypeChecker(parent) && parent !== lastAncestor) {
|
|
60
|
-
lastAncestor = parent
|
|
48
|
+
// Raw parents never point to model $ objects, so each model is visited once.
|
|
49
|
+
let parent: object | undefined = fastGetParent(start, false)
|
|
50
|
+
while (parent) {
|
|
51
|
+
if (isModelWithTypeChecker(parent)) {
|
|
61
52
|
callback(parent)
|
|
62
53
|
}
|
|
63
|
-
|
|
64
|
-
current = parent
|
|
54
|
+
parent = fastGetParent(parent, false)
|
|
65
55
|
}
|
|
66
56
|
}
|
|
67
57
|
|
package/src/types/TypeChecker.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { TypeCheckError } from "./TypeCheckError"
|
|
|
6
6
|
|
|
7
7
|
type CheckFunction = (value: any, path: Path, typeCheckedValue: any) => TypeCheckError | null
|
|
8
8
|
|
|
9
|
-
/** @
|
|
9
|
+
/** @ignore */
|
|
10
10
|
export type SnapshotProcessor = (snapshot: any) => unknown
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -14,7 +14,7 @@ export type SnapshotProcessor = (snapshot: any) => unknown
|
|
|
14
14
|
* TypeCheckers rather than processors so recursive graphs can be discovered
|
|
15
15
|
* before any processor body is compiled.
|
|
16
16
|
*
|
|
17
|
-
* @
|
|
17
|
+
* @ignore
|
|
18
18
|
*/
|
|
19
19
|
export interface SnapshotProcessorPlan {
|
|
20
20
|
readonly intrinsic?: boolean
|
|
@@ -36,7 +36,7 @@ export function snapshotProcessorPlan(
|
|
|
36
36
|
type SnapshotProcessorSource = SnapshotProcessor | SnapshotProcessorPlan | undefined
|
|
37
37
|
type SnapshotProcessorDirection = "from" | "to"
|
|
38
38
|
|
|
39
|
-
/** @
|
|
39
|
+
/** @ignore */
|
|
40
40
|
export class SnapshotProcessorPlanNode {
|
|
41
41
|
private readonly owner: TypeChecker | undefined
|
|
42
42
|
private readonly direction: SnapshotProcessorDirection
|
|
@@ -196,7 +196,7 @@ export class SnapshotProcessorPlanNode {
|
|
|
196
196
|
const absentSnapshotProcessorPlanNode = new SnapshotProcessorPlanNode(undefined, "from", undefined)
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* @
|
|
199
|
+
* @ignore
|
|
200
200
|
*/
|
|
201
201
|
export enum TypeCheckerBaseType {
|
|
202
202
|
Object = "object",
|
|
@@ -243,7 +243,7 @@ export function invalidateCachedToSnapshotProcessorResult(obj: object) {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
/**
|
|
246
|
-
* @
|
|
246
|
+
* @ignore
|
|
247
247
|
*/
|
|
248
248
|
export class TypeChecker {
|
|
249
249
|
unchecked: boolean
|
|
@@ -443,6 +443,6 @@ export class TypeInfo {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
/**
|
|
446
|
-
* @
|
|
446
|
+
* @ignore
|
|
447
447
|
*/
|
|
448
448
|
export type TypeInfoGen = (t: AnyStandardType) => TypeInfo
|
package/src/types/schemas.ts
CHANGED
|
@@ -64,7 +64,7 @@ export interface ObjectOfTypes {
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Name of the properties of an object that can be set to undefined, any or unknown.
|
|
67
|
-
* @
|
|
67
|
+
* @ignore
|
|
68
68
|
*/
|
|
69
69
|
export type UndefinablePropsNames<T> = {
|
|
70
70
|
[K in keyof T]: IsOptionalValue<T[K], K, never>
|
|
@@ -72,7 +72,7 @@ export type UndefinablePropsNames<T> = {
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Computes the optional keys for an object type schema.
|
|
75
|
-
* @
|
|
75
|
+
* @ignore
|
|
76
76
|
*/
|
|
77
77
|
export type ObjectOptionalKeys<S> = UndefinablePropsNames<{
|
|
78
78
|
[k in keyof S]: TypeToDataOpt<S[k]> extends infer R ? R : never
|