mobx-keystone 1.20.0 → 1.22.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 +864 -853
- package/README.md +61 -61
- package/dist/mobx-keystone.esm.js +11336 -11148
- package/dist/mobx-keystone.esm.mjs +11336 -11148
- package/dist/mobx-keystone.umd.js +12229 -11738
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
- package/dist/types/modelShared/prop.d.ts +5 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
- package/dist/types/ref/Ref.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +1 -1
- package/dist/types/types/TypeChecker.d.ts +1 -1
- package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
- package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
- package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
- package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
- package/dist/types/types/objectBased/typesModel.d.ts +1 -1
- package/dist/types/types/objectBased/typesObject.d.ts +3 -3
- package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
- package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
- package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
- package/dist/types/types/types.d.ts +3 -3
- package/dist/types/types/utility/typesCodec.d.ts +1 -6
- package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
- package/dist/types/types/utility/typesOr.d.ts +1 -1
- package/dist/types/types/utility/typesRefinement.d.ts +1 -1
- package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
- package/dist/types/types/utility/typesTag.d.ts +1 -1
- package/dist/types/wrappers/ObjectMap.d.ts +2 -0
- package/dist/types/wrappers/asMap.d.ts +10 -6
- package/package.json +14 -12
- package/src/action/applyAction.ts +125 -114
- package/src/action/applyMethodCall.ts +37 -37
- package/src/action/isModelAction.ts +16 -16
- package/src/action/middleware.ts +137 -137
- package/src/action/modelFlow.ts +240 -240
- package/src/action/modelFlowPromiseGenerator.ts +127 -125
- package/src/action/pendingActions.ts +42 -42
- package/src/action/wrapInAction.ts +156 -156
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
- package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
- package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
- package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
- package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
- package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
- package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
- package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
- package/src/actionMiddlewares/undoMiddleware.ts +804 -796
- package/src/actionMiddlewares/utils.ts +2 -2
- package/src/computedTree/computedTree.ts +143 -143
- package/src/context/context.ts +1 -1
- package/src/dataModel/BaseDataModel.ts +194 -192
- package/src/dataModel/actions.ts +1 -1
- package/src/dataModel/newDataModel.ts +57 -57
- package/src/deepChange/index.ts +1 -1
- package/src/deepChange/onDeepChange.ts +244 -244
- package/src/frozen/Frozen.ts +146 -146
- package/src/globalConfig/globalConfig.ts +107 -107
- package/src/index.ts +24 -24
- package/src/model/BaseModel.ts +275 -273
- package/src/model/ModelConstructorOptions.ts +15 -15
- package/src/model/getModelMetadata.ts +51 -51
- package/src/model/index.ts +6 -6
- package/src/model/mixins.ts +288 -288
- package/src/model/newModel.ts +301 -301
- package/src/model/utils.ts +249 -249
- package/src/modelShared/modelDecorator.ts +260 -253
- package/src/modelShared/modelInfo.ts +50 -50
- package/src/modelShared/modelSymbols.ts +3 -3
- package/src/modelShared/newModel.ts +25 -25
- package/src/modelShared/prop.ts +713 -684
- package/src/modelShared/sharedInternalModel.ts +427 -417
- package/src/modelShared/utils.ts +24 -24
- package/src/parent/core.ts +65 -65
- package/src/parent/coreObjectChildren.ts +180 -180
- package/src/parent/detach.ts +47 -47
- package/src/parent/findParent.ts +1 -1
- package/src/parent/getChildrenObjects.ts +25 -25
- package/src/parent/path.ts +386 -381
- package/src/parent/setParent.ts +149 -149
- package/src/parent/walkTree.ts +1 -1
- package/src/patch/applyPatches.ts +187 -187
- package/src/patch/jsonPatch.ts +108 -108
- package/src/patch/patchRecorder.ts +6 -1
- package/src/redux/connectReduxDevTools.ts +39 -7
- package/src/redux/redux.ts +6 -2
- package/src/ref/Ref.ts +1 -1
- package/src/ref/core.ts +339 -345
- package/src/ref/customRef.ts +3 -3
- package/src/ref/rootRef.ts +100 -100
- package/src/rootStore/attachDetach.ts +89 -89
- package/src/rootStore/rootStore.ts +1 -1
- package/src/snapshot/SnapshotOf.ts +104 -104
- package/src/snapshot/SnapshotProcessingError.ts +45 -45
- package/src/snapshot/applySnapshot.ts +177 -181
- package/src/snapshot/cloneTreeValue.ts +30 -30
- package/src/snapshot/fromArraySnapshot.ts +27 -33
- package/src/snapshot/fromModelSnapshot.ts +70 -70
- package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
- package/src/snapshot/fromSnapshot.ts +198 -198
- package/src/snapshot/getSnapshot.ts +80 -79
- package/src/snapshot/index.ts +8 -8
- package/src/snapshot/internal.ts +230 -230
- package/src/snapshot/reconcileArraySnapshot.ts +70 -70
- package/src/snapshot/reconcileModelSnapshot.ts +155 -150
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
- package/src/snapshot/reconcileSnapshot.ts +88 -88
- package/src/standardActions/actions.ts +64 -64
- package/src/standardActions/objectActions.ts +60 -60
- package/src/transforms/ImmutableDate.ts +93 -93
- package/src/transforms/asMap.ts +50 -50
- package/src/transforms/asSet.ts +22 -22
- package/src/transforms/bigint.ts +20 -20
- package/src/transforms/date.ts +40 -40
- package/src/treeUtils/deepEquals.ts +55 -55
- package/src/treeUtils/sandbox.ts +6 -3
- package/src/tweaker/TweakerPriority.ts +9 -9
- package/src/tweaker/tweak.ts +4 -3
- package/src/tweaker/tweakArray.ts +530 -475
- package/src/tweaker/tweakFrozen.ts +42 -42
- package/src/tweaker/tweakModel.ts +1 -1
- package/src/tweaker/tweakPlainObject.ts +355 -355
- package/src/tweaker/typeChecking.ts +101 -101
- package/src/types/SnapshotTypeMismatchError.ts +46 -46
- package/src/types/TypeCheckError.ts +183 -184
- package/src/types/TypeChecker.ts +202 -185
- package/src/types/arrayBased/typesArray.ts +136 -137
- package/src/types/arrayBased/typesTuple.ts +138 -139
- package/src/types/createPerEntryCachedCheck.ts +166 -160
- package/src/types/index.ts +6 -6
- package/src/types/objectBased/typesArraySet.ts +133 -134
- package/src/types/objectBased/typesDataModelData.ts +203 -204
- package/src/types/objectBased/typesModel.ts +220 -221
- package/src/types/objectBased/typesObject.ts +308 -310
- package/src/types/objectBased/typesObjectMap.ts +142 -143
- package/src/types/objectBased/typesRecord.ts +156 -157
- package/src/types/objectBased/typesRef.ts +89 -89
- package/src/types/primitiveBased/typesEnum.ts +63 -63
- package/src/types/primitiveBased/typesPrimitive.ts +219 -220
- package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
- package/src/types/resolveTypeChecker.ts +78 -78
- package/src/types/schemas.ts +208 -208
- package/src/types/tProp.ts +277 -268
- package/src/types/typeCheck.ts +21 -21
- package/src/types/typeCheckErrorUtils.ts +23 -23
- package/src/types/types.ts +142 -142
- package/src/types/utility/typeToStoredData.ts +37 -37
- package/src/types/utility/typesBigInt.ts +15 -15
- package/src/types/utility/typesCodec.ts +41 -38
- package/src/types/utility/typesCodecCore.ts +178 -178
- package/src/types/utility/typesCodecSupport.ts +806 -806
- package/src/types/utility/typesDate.ts +27 -27
- package/src/types/utility/typesMap.ts +253 -222
- package/src/types/utility/typesOr.ts +263 -259
- package/src/types/utility/typesRefinement.ts +131 -125
- package/src/types/utility/typesSet.ts +110 -110
- package/src/types/utility/typesSkipCheck.ts +69 -70
- package/src/types/utility/typesTag.ts +88 -82
- package/src/types/utility/typesUnchecked.ts +40 -40
- package/src/utils/AnyFunction.ts +4 -4
- package/src/utils/ModelPool.ts +59 -59
- package/src/utils/chainFns.ts +18 -18
- package/src/utils/decorators.ts +274 -274
- package/src/utils/errorDiagnostics.ts +204 -204
- package/src/utils/index.ts +443 -372
- package/src/wrappers/ArraySet.ts +156 -156
- package/src/wrappers/ObjectMap.ts +155 -136
- package/src/wrappers/asMap.ts +65 -30
- package/src/wrappers/asSet.ts +3 -3
|
@@ -1,355 +1,355 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IObjectDidChange,
|
|
3
|
-
IObjectWillChange,
|
|
4
|
-
intercept,
|
|
5
|
-
isObservableObject,
|
|
6
|
-
observable,
|
|
7
|
-
observe,
|
|
8
|
-
} from "mobx"
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import type {
|
|
18
|
-
import { setParent } from "../parent/setParent"
|
|
19
|
-
import { InternalPatchRecorder } from "../patch/emitPatch"
|
|
20
|
-
import {
|
|
21
|
-
freezeInternalSnapshot,
|
|
22
|
-
getInternalSnapshot,
|
|
23
|
-
SnapshotTransformFn,
|
|
24
|
-
setNewInternalSnapshot,
|
|
25
|
-
updateInternalSnapshot,
|
|
26
|
-
} from "../snapshot/internal"
|
|
27
|
-
import { failure, isPlainObject, isPrimitive } from "../utils"
|
|
28
|
-
import { setIfDifferent } from "../utils/setIfDifferent"
|
|
29
|
-
import { runningWithoutSnapshotOrPatches, tweakedObjects } from "./core"
|
|
30
|
-
import { TweakerPriority } from "./TweakerPriority"
|
|
31
|
-
import { registerTweaker, tweak } from "./tweak"
|
|
32
|
-
import { runTypeCheckingAfterChange } from "./typeChecking"
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
export function tweakPlainObject<T extends Record<string, any>>(
|
|
38
|
-
value: T,
|
|
39
|
-
parentPath: ParentPath<any> | undefined,
|
|
40
|
-
snapshotModelType: string | undefined,
|
|
41
|
-
doNotTweakChildren: boolean,
|
|
42
|
-
isDataObject: boolean
|
|
43
|
-
): T {
|
|
44
|
-
const originalObj: Record<string, any> = value
|
|
45
|
-
const tweakedObj = isObservableObject(originalObj)
|
|
46
|
-
? originalObj
|
|
47
|
-
: observable.object({}, undefined, observableOptions)
|
|
48
|
-
|
|
49
|
-
let interceptDisposer: () => void
|
|
50
|
-
let observeDisposer: () => void
|
|
51
|
-
|
|
52
|
-
const untweak = () => {
|
|
53
|
-
interceptDisposer()
|
|
54
|
-
observeDisposer()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
tweakedObjects.set(tweakedObj, untweak)
|
|
58
|
-
setParent(
|
|
59
|
-
tweakedObj, // value
|
|
60
|
-
parentPath,
|
|
61
|
-
false, // indexChangeAllowed
|
|
62
|
-
isDataObject,
|
|
63
|
-
// an object shouldn't be cloned
|
|
64
|
-
false // cloneIfApplicable
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
const untransformedSn: any = {}
|
|
68
|
-
|
|
69
|
-
// substitute initial values by tweaked values
|
|
70
|
-
const originalObjKeys = Object.keys(originalObj)
|
|
71
|
-
const originalObjKeysLen = originalObjKeys.length
|
|
72
|
-
for (let i = 0; i < originalObjKeysLen; i++) {
|
|
73
|
-
const k = originalObjKeys[i]
|
|
74
|
-
const v = originalObj[k]
|
|
75
|
-
|
|
76
|
-
if (isPrimitive(v)) {
|
|
77
|
-
if (!doNotTweakChildren) {
|
|
78
|
-
setIfDifferent(tweakedObj, k, v)
|
|
79
|
-
}
|
|
80
|
-
untransformedSn
|
|
81
|
-
} else {
|
|
82
|
-
const path = { parent: tweakedObj, path: k }
|
|
83
|
-
|
|
84
|
-
let tweakedValue: any
|
|
85
|
-
if (doNotTweakChildren) {
|
|
86
|
-
tweakedValue = v
|
|
87
|
-
setParent(
|
|
88
|
-
tweakedValue, // value
|
|
89
|
-
path, // parentPath
|
|
90
|
-
false, // indexChangeAllowed
|
|
91
|
-
false, // isDataObject
|
|
92
|
-
// the value is already a new value (the result of a fromSnapshot)
|
|
93
|
-
false // cloneIfApplicable
|
|
94
|
-
)
|
|
95
|
-
} else {
|
|
96
|
-
tweakedValue = tweak(v, path)
|
|
97
|
-
setIfDifferent(tweakedObj, k, tweakedValue)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const valueSn = getInternalSnapshot(tweakedValue)!
|
|
101
|
-
untransformedSn
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let transformFn: SnapshotTransformFn | undefined
|
|
106
|
-
if (snapshotModelType) {
|
|
107
|
-
untransformedSn
|
|
108
|
-
|
|
109
|
-
const modelInfo = getModelInfoForName(snapshotModelType)
|
|
110
|
-
if (!modelInfo) {
|
|
111
|
-
throw failure(getModelNotRegisteredErrorMessage(snapshotModelType))
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const originalTransformFn = (modelInfo.class as ModelClass<AnyModel>).toSnapshotProcessor
|
|
115
|
-
if (originalTransformFn) {
|
|
116
|
-
transformFn = (sn) => originalTransformFn(sn, dataToModelNode(tweakedObj))
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
setNewInternalSnapshot(
|
|
121
|
-
isDataObject ? dataToModelNode(tweakedObj) : tweakedObj,
|
|
122
|
-
untransformedSn,
|
|
123
|
-
transformFn
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
interceptDisposer = intercept(tweakedObj, interceptObjectMutation)
|
|
127
|
-
observeDisposer = observe(tweakedObj, objectDidChange)
|
|
128
|
-
|
|
129
|
-
return tweakedObj as any
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const observableOptions = {
|
|
133
|
-
deep: false,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function mutateSet(k: PropertyKey, v: unknown, sn: Record<PropertyKey, unknown>) {
|
|
137
|
-
sn
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function mutateDelete(k: PropertyKey, sn: Record<PropertyKey, unknown>) {
|
|
141
|
-
delete sn[k]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const patchRecorder = new InternalPatchRecorder()
|
|
145
|
-
|
|
146
|
-
const emptyPath: Path = Object.freeze([])
|
|
147
|
-
|
|
148
|
-
function objectDidChange(change: IObjectDidChange): void {
|
|
149
|
-
if (typeof change.name !== "string") {
|
|
150
|
-
throw failure("change.name is not a string")
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const obj = change.object
|
|
154
|
-
const actualNode = dataToModelNode(obj)
|
|
155
|
-
const oldUntransformedSn = getInternalSnapshot(actualNode)!.untransformed
|
|
156
|
-
|
|
157
|
-
patchRecorder.reset()
|
|
158
|
-
|
|
159
|
-
let mutate: ((sn: any) => void) | undefined
|
|
160
|
-
|
|
161
|
-
switch (change.type) {
|
|
162
|
-
case "add": {
|
|
163
|
-
mutate = objectDidChangeAddOrUpdate(change, oldUntransformedSn)
|
|
164
|
-
// Emit deep change for add
|
|
165
|
-
emitDeepChange(actualNode, {
|
|
166
|
-
type: DeepChangeType.ObjectAdd,
|
|
167
|
-
target: obj as object,
|
|
168
|
-
path: emptyPath,
|
|
169
|
-
key: change.name,
|
|
170
|
-
newValue: change.newValue,
|
|
171
|
-
isInit: getIsInInitPhase(),
|
|
172
|
-
})
|
|
173
|
-
break
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
case "update": {
|
|
177
|
-
mutate = objectDidChangeAddOrUpdate(change, oldUntransformedSn)
|
|
178
|
-
// Emit deep change for update
|
|
179
|
-
emitDeepChange(actualNode, {
|
|
180
|
-
type: DeepChangeType.ObjectUpdate,
|
|
181
|
-
target: obj as object,
|
|
182
|
-
path: emptyPath,
|
|
183
|
-
key: change.name,
|
|
184
|
-
newValue: change.newValue,
|
|
185
|
-
oldValue: change.oldValue,
|
|
186
|
-
isInit: getIsInInitPhase(),
|
|
187
|
-
})
|
|
188
|
-
break
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
case "remove": {
|
|
192
|
-
mutate = objectDidChangeRemove(change, oldUntransformedSn)
|
|
193
|
-
// Emit deep change for remove
|
|
194
|
-
emitDeepChange(actualNode, {
|
|
195
|
-
type: DeepChangeType.ObjectRemove,
|
|
196
|
-
target: obj as object,
|
|
197
|
-
path: emptyPath,
|
|
198
|
-
key: change.name,
|
|
199
|
-
oldValue: change.oldValue,
|
|
200
|
-
isInit: getIsInInitPhase(),
|
|
201
|
-
})
|
|
202
|
-
break
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
default:
|
|
206
|
-
throw failure("assertion error: unsupported object change type")
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
runTypeCheckingAfterChange(obj, patchRecorder)
|
|
210
|
-
|
|
211
|
-
if (!runningWithoutSnapshotOrPatches) {
|
|
212
|
-
updateInternalSnapshot(actualNode, mutate)
|
|
213
|
-
patchRecorder.emit(actualNode)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function objectDidChangeRemove(
|
|
218
|
-
change: IObjectDidChange & { type: "remove" },
|
|
219
|
-
oldUntransformedSn: any
|
|
220
|
-
) {
|
|
221
|
-
const k = change.name
|
|
222
|
-
const oldVal = oldUntransformedSn[k]
|
|
223
|
-
const mutate = mutateDelete.bind(undefined, k)
|
|
224
|
-
|
|
225
|
-
const path = [k as string]
|
|
226
|
-
|
|
227
|
-
patchRecorder.record(
|
|
228
|
-
[
|
|
229
|
-
{
|
|
230
|
-
op: "remove",
|
|
231
|
-
path,
|
|
232
|
-
},
|
|
233
|
-
],
|
|
234
|
-
[
|
|
235
|
-
{
|
|
236
|
-
op: "add",
|
|
237
|
-
path,
|
|
238
|
-
value: freezeInternalSnapshot(oldVal),
|
|
239
|
-
},
|
|
240
|
-
]
|
|
241
|
-
)
|
|
242
|
-
return mutate
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function objectDidChangeAddOrUpdate(
|
|
246
|
-
change: IObjectWillChange & { type: "add" | "update" },
|
|
247
|
-
oldUntransformedSn: any
|
|
248
|
-
) {
|
|
249
|
-
const k = change.name
|
|
250
|
-
const val = change.newValue
|
|
251
|
-
|
|
252
|
-
const oldVal = oldUntransformedSn[k]
|
|
253
|
-
|
|
254
|
-
let newVal: any
|
|
255
|
-
if (isPrimitive(val)) {
|
|
256
|
-
newVal = val
|
|
257
|
-
} else {
|
|
258
|
-
const valueSn = getInternalSnapshot(val)!
|
|
259
|
-
newVal = valueSn.transformed
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const mutate = mutateSet.bind(undefined, k, newVal)
|
|
263
|
-
|
|
264
|
-
const path = [k as string]
|
|
265
|
-
if (change.type === "add") {
|
|
266
|
-
patchRecorder.record(
|
|
267
|
-
[
|
|
268
|
-
{
|
|
269
|
-
op: "add",
|
|
270
|
-
path,
|
|
271
|
-
value: freezeInternalSnapshot(newVal),
|
|
272
|
-
},
|
|
273
|
-
],
|
|
274
|
-
[
|
|
275
|
-
{
|
|
276
|
-
op: "remove",
|
|
277
|
-
path,
|
|
278
|
-
},
|
|
279
|
-
]
|
|
280
|
-
)
|
|
281
|
-
} else {
|
|
282
|
-
patchRecorder.record(
|
|
283
|
-
[
|
|
284
|
-
{
|
|
285
|
-
op: "replace",
|
|
286
|
-
path,
|
|
287
|
-
value: freezeInternalSnapshot(newVal),
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
[
|
|
291
|
-
{
|
|
292
|
-
op: "replace",
|
|
293
|
-
path,
|
|
294
|
-
value: freezeInternalSnapshot(oldVal),
|
|
295
|
-
},
|
|
296
|
-
]
|
|
297
|
-
)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return mutate
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function interceptObjectMutation(change: IObjectWillChange) {
|
|
304
|
-
assertCanWrite()
|
|
305
|
-
|
|
306
|
-
if (typeof change.name === "symbol") {
|
|
307
|
-
throw failure("symbol properties are not supported")
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
switch (change.type) {
|
|
311
|
-
case "add":
|
|
312
|
-
change.newValue = tweak(change.newValue, {
|
|
313
|
-
parent: change.object,
|
|
314
|
-
path: String(change.name),
|
|
315
|
-
})
|
|
316
|
-
break
|
|
317
|
-
|
|
318
|
-
case "remove": {
|
|
319
|
-
tweak(change.object[change.name], undefined)
|
|
320
|
-
break
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
case "update": {
|
|
324
|
-
const oldVal = change.object[change.name]
|
|
325
|
-
const newVal = change.newValue
|
|
326
|
-
if (newVal !== oldVal) {
|
|
327
|
-
tweak(oldVal, undefined)
|
|
328
|
-
|
|
329
|
-
change.newValue = tweak(newVal, {
|
|
330
|
-
parent: change.object,
|
|
331
|
-
path: String(change.name),
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
break
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
default:
|
|
338
|
-
throw failure("assertion error: unsupported object change type")
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return change
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* @internal
|
|
346
|
-
*/
|
|
347
|
-
export function registerPlainObjectTweaker() {
|
|
348
|
-
registerTweaker(TweakerPriority.PlainObject, (value, parentPath) => {
|
|
349
|
-
// plain object
|
|
350
|
-
if (isObservableObject(value) || isPlainObject(value)) {
|
|
351
|
-
return tweakPlainObject(value as Record<string, any>, parentPath, undefined, false, false)
|
|
352
|
-
}
|
|
353
|
-
return undefined
|
|
354
|
-
})
|
|
355
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
type IObjectDidChange,
|
|
3
|
+
type IObjectWillChange,
|
|
4
|
+
intercept,
|
|
5
|
+
isObservableObject,
|
|
6
|
+
observable,
|
|
7
|
+
observe,
|
|
8
|
+
} from "mobx"
|
|
9
|
+
import { assertCanWrite } from "../action/protection"
|
|
10
|
+
import { DeepChangeType, emitDeepChange, getIsInInitPhase } from "../deepChange/onDeepChange"
|
|
11
|
+
import type { AnyModel } from "../model/BaseModel"
|
|
12
|
+
import { modelTypeKey } from "../model/metadata"
|
|
13
|
+
import type { ModelClass } from "../modelShared/BaseModelShared"
|
|
14
|
+
import { getModelInfoForName, getModelNotRegisteredErrorMessage } from "../modelShared/modelInfo"
|
|
15
|
+
import { dataToModelNode } from "../parent/core"
|
|
16
|
+
import type { ParentPath } from "../parent/path"
|
|
17
|
+
import type { Path } from "../parent/pathTypes"
|
|
18
|
+
import { setParent } from "../parent/setParent"
|
|
19
|
+
import { InternalPatchRecorder } from "../patch/emitPatch"
|
|
20
|
+
import {
|
|
21
|
+
freezeInternalSnapshot,
|
|
22
|
+
getInternalSnapshot,
|
|
23
|
+
type SnapshotTransformFn,
|
|
24
|
+
setNewInternalSnapshot,
|
|
25
|
+
updateInternalSnapshot,
|
|
26
|
+
} from "../snapshot/internal"
|
|
27
|
+
import { failure, isPlainObject, isPrimitive, setOwnProp } from "../utils"
|
|
28
|
+
import { setIfDifferent } from "../utils/setIfDifferent"
|
|
29
|
+
import { runningWithoutSnapshotOrPatches, tweakedObjects } from "./core"
|
|
30
|
+
import { TweakerPriority } from "./TweakerPriority"
|
|
31
|
+
import { registerTweaker, tweak } from "./tweak"
|
|
32
|
+
import { runTypeCheckingAfterChange } from "./typeChecking"
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export function tweakPlainObject<T extends Record<string, any>>(
|
|
38
|
+
value: T,
|
|
39
|
+
parentPath: ParentPath<any> | undefined,
|
|
40
|
+
snapshotModelType: string | undefined,
|
|
41
|
+
doNotTweakChildren: boolean,
|
|
42
|
+
isDataObject: boolean
|
|
43
|
+
): T {
|
|
44
|
+
const originalObj: Record<string, any> = value
|
|
45
|
+
const tweakedObj = isObservableObject(originalObj)
|
|
46
|
+
? originalObj
|
|
47
|
+
: observable.object({}, undefined, observableOptions)
|
|
48
|
+
|
|
49
|
+
let interceptDisposer: () => void
|
|
50
|
+
let observeDisposer: () => void
|
|
51
|
+
|
|
52
|
+
const untweak = () => {
|
|
53
|
+
interceptDisposer()
|
|
54
|
+
observeDisposer()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
tweakedObjects.set(tweakedObj, untweak)
|
|
58
|
+
setParent(
|
|
59
|
+
tweakedObj, // value
|
|
60
|
+
parentPath,
|
|
61
|
+
false, // indexChangeAllowed
|
|
62
|
+
isDataObject,
|
|
63
|
+
// an object shouldn't be cloned
|
|
64
|
+
false // cloneIfApplicable
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const untransformedSn: any = {}
|
|
68
|
+
|
|
69
|
+
// substitute initial values by tweaked values
|
|
70
|
+
const originalObjKeys = Object.keys(originalObj)
|
|
71
|
+
const originalObjKeysLen = originalObjKeys.length
|
|
72
|
+
for (let i = 0; i < originalObjKeysLen; i++) {
|
|
73
|
+
const k = originalObjKeys[i]
|
|
74
|
+
const v = originalObj[k]
|
|
75
|
+
|
|
76
|
+
if (isPrimitive(v)) {
|
|
77
|
+
if (!doNotTweakChildren) {
|
|
78
|
+
setIfDifferent(tweakedObj, k, v)
|
|
79
|
+
}
|
|
80
|
+
setOwnProp(untransformedSn, k, v)
|
|
81
|
+
} else {
|
|
82
|
+
const path = { parent: tweakedObj, path: k }
|
|
83
|
+
|
|
84
|
+
let tweakedValue: any
|
|
85
|
+
if (doNotTweakChildren) {
|
|
86
|
+
tweakedValue = v
|
|
87
|
+
setParent(
|
|
88
|
+
tweakedValue, // value
|
|
89
|
+
path, // parentPath
|
|
90
|
+
false, // indexChangeAllowed
|
|
91
|
+
false, // isDataObject
|
|
92
|
+
// the value is already a new value (the result of a fromSnapshot)
|
|
93
|
+
false // cloneIfApplicable
|
|
94
|
+
)
|
|
95
|
+
} else {
|
|
96
|
+
tweakedValue = tweak(v, path)
|
|
97
|
+
setIfDifferent(tweakedObj, k, tweakedValue)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const valueSn = getInternalSnapshot(tweakedValue)!
|
|
101
|
+
setOwnProp(untransformedSn, k, valueSn.transformed)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let transformFn: SnapshotTransformFn | undefined
|
|
106
|
+
if (snapshotModelType) {
|
|
107
|
+
setOwnProp(untransformedSn, modelTypeKey, snapshotModelType)
|
|
108
|
+
|
|
109
|
+
const modelInfo = getModelInfoForName(snapshotModelType)
|
|
110
|
+
if (!modelInfo) {
|
|
111
|
+
throw failure(getModelNotRegisteredErrorMessage(snapshotModelType))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const originalTransformFn = (modelInfo.class as ModelClass<AnyModel>).toSnapshotProcessor
|
|
115
|
+
if (originalTransformFn) {
|
|
116
|
+
transformFn = (sn) => originalTransformFn(sn, dataToModelNode(tweakedObj))
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setNewInternalSnapshot(
|
|
121
|
+
isDataObject ? dataToModelNode(tweakedObj) : tweakedObj,
|
|
122
|
+
untransformedSn,
|
|
123
|
+
transformFn
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
interceptDisposer = intercept(tweakedObj, interceptObjectMutation)
|
|
127
|
+
observeDisposer = observe(tweakedObj, objectDidChange)
|
|
128
|
+
|
|
129
|
+
return tweakedObj as any
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const observableOptions = {
|
|
133
|
+
deep: false,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function mutateSet(k: PropertyKey, v: unknown, sn: Record<PropertyKey, unknown>) {
|
|
137
|
+
setOwnProp(sn, k, v)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function mutateDelete(k: PropertyKey, sn: Record<PropertyKey, unknown>) {
|
|
141
|
+
delete sn[k]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const patchRecorder = new InternalPatchRecorder()
|
|
145
|
+
|
|
146
|
+
const emptyPath: Path = Object.freeze([])
|
|
147
|
+
|
|
148
|
+
function objectDidChange(change: IObjectDidChange): void {
|
|
149
|
+
if (typeof change.name !== "string") {
|
|
150
|
+
throw failure("change.name is not a string")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const obj = change.object
|
|
154
|
+
const actualNode = dataToModelNode(obj)
|
|
155
|
+
const oldUntransformedSn = getInternalSnapshot(actualNode)!.untransformed
|
|
156
|
+
|
|
157
|
+
patchRecorder.reset()
|
|
158
|
+
|
|
159
|
+
let mutate: ((sn: any) => void) | undefined
|
|
160
|
+
|
|
161
|
+
switch (change.type) {
|
|
162
|
+
case "add": {
|
|
163
|
+
mutate = objectDidChangeAddOrUpdate(change, oldUntransformedSn)
|
|
164
|
+
// Emit deep change for add
|
|
165
|
+
emitDeepChange(actualNode, {
|
|
166
|
+
type: DeepChangeType.ObjectAdd,
|
|
167
|
+
target: obj as object,
|
|
168
|
+
path: emptyPath,
|
|
169
|
+
key: change.name,
|
|
170
|
+
newValue: change.newValue,
|
|
171
|
+
isInit: getIsInInitPhase(),
|
|
172
|
+
})
|
|
173
|
+
break
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case "update": {
|
|
177
|
+
mutate = objectDidChangeAddOrUpdate(change, oldUntransformedSn)
|
|
178
|
+
// Emit deep change for update
|
|
179
|
+
emitDeepChange(actualNode, {
|
|
180
|
+
type: DeepChangeType.ObjectUpdate,
|
|
181
|
+
target: obj as object,
|
|
182
|
+
path: emptyPath,
|
|
183
|
+
key: change.name,
|
|
184
|
+
newValue: change.newValue,
|
|
185
|
+
oldValue: change.oldValue,
|
|
186
|
+
isInit: getIsInInitPhase(),
|
|
187
|
+
})
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case "remove": {
|
|
192
|
+
mutate = objectDidChangeRemove(change, oldUntransformedSn)
|
|
193
|
+
// Emit deep change for remove
|
|
194
|
+
emitDeepChange(actualNode, {
|
|
195
|
+
type: DeepChangeType.ObjectRemove,
|
|
196
|
+
target: obj as object,
|
|
197
|
+
path: emptyPath,
|
|
198
|
+
key: change.name,
|
|
199
|
+
oldValue: change.oldValue,
|
|
200
|
+
isInit: getIsInInitPhase(),
|
|
201
|
+
})
|
|
202
|
+
break
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
default:
|
|
206
|
+
throw failure("assertion error: unsupported object change type")
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
runTypeCheckingAfterChange(obj, patchRecorder)
|
|
210
|
+
|
|
211
|
+
if (!runningWithoutSnapshotOrPatches) {
|
|
212
|
+
updateInternalSnapshot(actualNode, mutate)
|
|
213
|
+
patchRecorder.emit(actualNode)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function objectDidChangeRemove(
|
|
218
|
+
change: IObjectDidChange & { type: "remove" },
|
|
219
|
+
oldUntransformedSn: any
|
|
220
|
+
) {
|
|
221
|
+
const k = change.name
|
|
222
|
+
const oldVal = oldUntransformedSn[k]
|
|
223
|
+
const mutate = mutateDelete.bind(undefined, k)
|
|
224
|
+
|
|
225
|
+
const path = [k as string]
|
|
226
|
+
|
|
227
|
+
patchRecorder.record(
|
|
228
|
+
[
|
|
229
|
+
{
|
|
230
|
+
op: "remove",
|
|
231
|
+
path,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
{
|
|
236
|
+
op: "add",
|
|
237
|
+
path,
|
|
238
|
+
value: freezeInternalSnapshot(oldVal),
|
|
239
|
+
},
|
|
240
|
+
]
|
|
241
|
+
)
|
|
242
|
+
return mutate
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function objectDidChangeAddOrUpdate(
|
|
246
|
+
change: IObjectWillChange & { type: "add" | "update" },
|
|
247
|
+
oldUntransformedSn: any
|
|
248
|
+
) {
|
|
249
|
+
const k = change.name
|
|
250
|
+
const val = change.newValue
|
|
251
|
+
|
|
252
|
+
const oldVal = oldUntransformedSn[k]
|
|
253
|
+
|
|
254
|
+
let newVal: any
|
|
255
|
+
if (isPrimitive(val)) {
|
|
256
|
+
newVal = val
|
|
257
|
+
} else {
|
|
258
|
+
const valueSn = getInternalSnapshot(val)!
|
|
259
|
+
newVal = valueSn.transformed
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const mutate = mutateSet.bind(undefined, k, newVal)
|
|
263
|
+
|
|
264
|
+
const path = [k as string]
|
|
265
|
+
if (change.type === "add") {
|
|
266
|
+
patchRecorder.record(
|
|
267
|
+
[
|
|
268
|
+
{
|
|
269
|
+
op: "add",
|
|
270
|
+
path,
|
|
271
|
+
value: freezeInternalSnapshot(newVal),
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
[
|
|
275
|
+
{
|
|
276
|
+
op: "remove",
|
|
277
|
+
path,
|
|
278
|
+
},
|
|
279
|
+
]
|
|
280
|
+
)
|
|
281
|
+
} else {
|
|
282
|
+
patchRecorder.record(
|
|
283
|
+
[
|
|
284
|
+
{
|
|
285
|
+
op: "replace",
|
|
286
|
+
path,
|
|
287
|
+
value: freezeInternalSnapshot(newVal),
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
[
|
|
291
|
+
{
|
|
292
|
+
op: "replace",
|
|
293
|
+
path,
|
|
294
|
+
value: freezeInternalSnapshot(oldVal),
|
|
295
|
+
},
|
|
296
|
+
]
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return mutate
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function interceptObjectMutation(change: IObjectWillChange) {
|
|
304
|
+
assertCanWrite()
|
|
305
|
+
|
|
306
|
+
if (typeof change.name === "symbol") {
|
|
307
|
+
throw failure("symbol properties are not supported")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
switch (change.type) {
|
|
311
|
+
case "add":
|
|
312
|
+
change.newValue = tweak(change.newValue, {
|
|
313
|
+
parent: change.object,
|
|
314
|
+
path: String(change.name),
|
|
315
|
+
})
|
|
316
|
+
break
|
|
317
|
+
|
|
318
|
+
case "remove": {
|
|
319
|
+
tweak(change.object[change.name], undefined)
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
case "update": {
|
|
324
|
+
const oldVal = change.object[change.name]
|
|
325
|
+
const newVal = change.newValue
|
|
326
|
+
if (newVal !== oldVal) {
|
|
327
|
+
tweak(oldVal, undefined)
|
|
328
|
+
|
|
329
|
+
change.newValue = tweak(newVal, {
|
|
330
|
+
parent: change.object,
|
|
331
|
+
path: String(change.name),
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
break
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
default:
|
|
338
|
+
throw failure("assertion error: unsupported object change type")
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return change
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @internal
|
|
346
|
+
*/
|
|
347
|
+
export function registerPlainObjectTweaker() {
|
|
348
|
+
registerTweaker(TweakerPriority.PlainObject, (value, parentPath) => {
|
|
349
|
+
// plain object
|
|
350
|
+
if (isObservableObject(value) || isPlainObject(value)) {
|
|
351
|
+
return tweakPlainObject(value as Record<string, any>, parentPath, undefined, false, false)
|
|
352
|
+
}
|
|
353
|
+
return undefined
|
|
354
|
+
})
|
|
355
|
+
}
|