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
package/src/model/newModel.ts
CHANGED
|
@@ -1,301 +1,301 @@
|
|
|
1
|
-
import { action, set } from "mobx"
|
|
2
|
-
import type { O } from "ts-toolbelt"
|
|
3
|
-
import {
|
|
4
|
-
DeepChangeType,
|
|
5
|
-
emitDeepChange,
|
|
6
|
-
ObjectAddChange,
|
|
7
|
-
ObjectUpdateChange,
|
|
8
|
-
} from "../deepChange/onDeepChange"
|
|
9
|
-
import { isModelAutoTypeCheckingEnabled } from "../globalConfig/globalConfig"
|
|
10
|
-
import type { ModelClass, ModelCreationData } from "../modelShared/BaseModelShared"
|
|
11
|
-
import { modelInfoByClass } from "../modelShared/modelInfo"
|
|
12
|
-
import { getInternalModelClassPropsInfo } from "../modelShared/modelPropsInfo"
|
|
13
|
-
import { applyModelInitializers } from "../modelShared/newModel"
|
|
14
|
-
import {
|
|
15
|
-
import { createPatchForObjectValueChange, emitPatches } from "../patch/emitPatch"
|
|
16
|
-
import { Patch } from "../patch/Patch"
|
|
17
|
-
import { tweakModel } from "../tweaker/tweakModel"
|
|
18
|
-
import { tweakPlainObject } from "../tweaker/tweakPlainObject"
|
|
19
|
-
import { failure, inDevMode, makePropReadonly } from "../utils"
|
|
20
|
-
import { setIfDifferent, setIfDifferentWithReturn } from "../utils/setIfDifferent"
|
|
21
|
-
import type { AnyModel } from "./BaseModel"
|
|
22
|
-
import { getModelIdPropertyName } from "./getModelMetadata"
|
|
23
|
-
import type { ModelConstructorOptions } from "./ModelConstructorOptions"
|
|
24
|
-
import { modelTypeKey } from "./metadata"
|
|
25
|
-
import { assertIsModelClass } from "./utils"
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
export const internalNewModel = action(
|
|
31
|
-
"newModel",
|
|
32
|
-
<M extends AnyModel>(
|
|
33
|
-
origModelObj: M,
|
|
34
|
-
initialData: ModelCreationData<M>,
|
|
35
|
-
modelClass: ModelClass<AnyModel>
|
|
36
|
-
): void => {
|
|
37
|
-
if (inDevMode) {
|
|
38
|
-
assertIsModelClass(modelClass, "modelClass")
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { modelInfo, modelIdPropertyName, modelProps, modelIdPropData } =
|
|
42
|
-
getModelDetails(modelClass)
|
|
43
|
-
|
|
44
|
-
// use symbol if provided
|
|
45
|
-
if (modelIdPropertyName && modelIdPropData) {
|
|
46
|
-
let id: string | undefined
|
|
47
|
-
if (initialData[modelIdPropertyName]) {
|
|
48
|
-
id = initialData[modelIdPropertyName]
|
|
49
|
-
} else {
|
|
50
|
-
id = (modelIdPropData._defaultFn as () => string)()
|
|
51
|
-
}
|
|
52
|
-
setIfDifferent(initialData, modelIdPropertyName, id)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const modelObj = origModelObj as O.Writable<M>
|
|
56
|
-
modelObj[modelTypeKey] = modelInfo.name
|
|
57
|
-
|
|
58
|
-
// fill in defaults in initial data
|
|
59
|
-
const modelPropsKeys = Object.keys(modelProps)
|
|
60
|
-
for (let i = 0; i < modelPropsKeys.length; i++) {
|
|
61
|
-
const k = modelPropsKeys[i]
|
|
62
|
-
|
|
63
|
-
// id is already initialized above
|
|
64
|
-
if (k === modelIdPropertyName) {
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const propData = modelProps[k]
|
|
69
|
-
|
|
70
|
-
const initialValue = initialData[k]
|
|
71
|
-
let newValue = initialValue
|
|
72
|
-
let changed = false
|
|
73
|
-
|
|
74
|
-
// apply untransform (if any) if not in snapshot mode
|
|
75
|
-
if (propData._transform) {
|
|
76
|
-
changed = true
|
|
77
|
-
newValue = propData._transform.untransform(newValue, modelObj, k)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// apply default value (if needed)
|
|
81
|
-
if (newValue == null) {
|
|
82
|
-
const defaultValue =
|
|
83
|
-
if (defaultValue !== noDefaultValue) {
|
|
84
|
-
changed = true
|
|
85
|
-
newValue = defaultValue
|
|
86
|
-
} else if (!(k in initialData)) {
|
|
87
|
-
// for mobx4, we need to set up properties even if they are undefined
|
|
88
|
-
changed = true
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (changed) {
|
|
93
|
-
// setIfDifferent not required
|
|
94
|
-
set(initialData, k, newValue)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
finalizeNewModel(modelObj, initialData, modelClass)
|
|
99
|
-
|
|
100
|
-
// type check it if needed
|
|
101
|
-
if (isModelAutoTypeCheckingEnabled()) {
|
|
102
|
-
const err = modelObj.typeCheck()
|
|
103
|
-
if (err) {
|
|
104
|
-
err.throw()
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* @internal
|
|
112
|
-
*/
|
|
113
|
-
export const internalFromSnapshotModel = action(
|
|
114
|
-
"fromSnapshotModel",
|
|
115
|
-
<M extends AnyModel>(
|
|
116
|
-
origModelObj: M,
|
|
117
|
-
snapshotInitialData: NonNullable<ModelConstructorOptions["snapshotInitialData"]>,
|
|
118
|
-
modelClass: ModelClass<AnyModel>,
|
|
119
|
-
generateNewIds: boolean
|
|
120
|
-
): void => {
|
|
121
|
-
if (inDevMode) {
|
|
122
|
-
assertIsModelClass(modelClass, "modelClass")
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const { modelInfo, modelIdPropertyName, modelProps, modelIdPropData } =
|
|
126
|
-
getModelDetails(modelClass)
|
|
127
|
-
|
|
128
|
-
let id: string | undefined
|
|
129
|
-
let sn = snapshotInitialData.unprocessedSnapshot
|
|
130
|
-
|
|
131
|
-
if (modelIdPropData && modelIdPropertyName) {
|
|
132
|
-
if (generateNewIds) {
|
|
133
|
-
id = (modelIdPropData._defaultFn as () => string)()
|
|
134
|
-
} else {
|
|
135
|
-
id = sn[modelIdPropertyName]
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (modelClass.fromSnapshotProcessor) {
|
|
140
|
-
sn = modelClass.fromSnapshotProcessor(sn)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const initialData = snapshotInitialData.snapshotToInitialData(sn)
|
|
144
|
-
|
|
145
|
-
const modelObj = origModelObj as O.Writable<M>
|
|
146
|
-
modelObj[modelTypeKey] = modelInfo.name
|
|
147
|
-
|
|
148
|
-
const patches: Patch[] = []
|
|
149
|
-
const inversePatches: Patch[] = []
|
|
150
|
-
const defaultsApplied: { key: string; oldValue: unknown; newValue: unknown }[] = []
|
|
151
|
-
|
|
152
|
-
if (modelIdPropertyName) {
|
|
153
|
-
const initialValue = initialData[modelIdPropertyName]
|
|
154
|
-
const valueChanged = setIfDifferentWithReturn(initialData, modelIdPropertyName, id)
|
|
155
|
-
|
|
156
|
-
if (valueChanged) {
|
|
157
|
-
const modelIdPath = [modelIdPropertyName]
|
|
158
|
-
|
|
159
|
-
patches.push(createPatchForObjectValueChange(modelIdPath, initialValue, id))
|
|
160
|
-
inversePatches.push(createPatchForObjectValueChange(modelIdPath, id, initialValue))
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// fill in defaults in initial data
|
|
165
|
-
const modelPropsKeys = Object.keys(modelProps)
|
|
166
|
-
for (let i = 0; i < modelPropsKeys.length; i++) {
|
|
167
|
-
const k = modelPropsKeys[i]
|
|
168
|
-
|
|
169
|
-
// id is already initialized above
|
|
170
|
-
if (k === modelIdPropertyName) {
|
|
171
|
-
continue
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const propData = modelProps[k]
|
|
175
|
-
|
|
176
|
-
const initialValue = initialData[k]
|
|
177
|
-
let newValue = initialValue
|
|
178
|
-
let changed = false
|
|
179
|
-
|
|
180
|
-
// apply default value (if needed)
|
|
181
|
-
if (newValue == null) {
|
|
182
|
-
const defaultValue =
|
|
183
|
-
if (defaultValue !== noDefaultValue) {
|
|
184
|
-
changed = true
|
|
185
|
-
newValue = defaultValue
|
|
186
|
-
} else if (!(k in initialData!)) {
|
|
187
|
-
// for mobx4, we need to set up properties even if they are undefined
|
|
188
|
-
changed = true
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (changed) {
|
|
193
|
-
// setIfDifferent not required
|
|
194
|
-
set(initialData, k, newValue)
|
|
195
|
-
|
|
196
|
-
if (newValue !== initialValue) {
|
|
197
|
-
const propPath = [k]
|
|
198
|
-
|
|
199
|
-
patches.push(createPatchForObjectValueChange(propPath, initialValue, newValue))
|
|
200
|
-
inversePatches.push(createPatchForObjectValueChange(propPath, newValue, initialValue))
|
|
201
|
-
|
|
202
|
-
// Track defaults applied for deep change emission
|
|
203
|
-
defaultsApplied.push({ key: k, oldValue: initialValue, newValue })
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// also emit a patch for modelType, since it will get included in the snapshot
|
|
209
|
-
const initialModelType = snapshotInitialData?.unprocessedModelType
|
|
210
|
-
const newModelType = modelInfo.name
|
|
211
|
-
if (initialModelType !== newModelType) {
|
|
212
|
-
const modelTypePath = [modelTypeKey]
|
|
213
|
-
|
|
214
|
-
patches.push(createPatchForObjectValueChange(modelTypePath, initialModelType, newModelType))
|
|
215
|
-
inversePatches.push(
|
|
216
|
-
createPatchForObjectValueChange(modelTypePath, newModelType, initialModelType)
|
|
217
|
-
)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
finalizeNewModel(modelObj, initialData, modelClass)
|
|
221
|
-
|
|
222
|
-
emitPatches(modelObj, patches, inversePatches)
|
|
223
|
-
|
|
224
|
-
// Emit deep changes for defaults applied during fromSnapshot
|
|
225
|
-
// These are emitted with isInit: true so bindings can sync them to CRDTs
|
|
226
|
-
for (const { key, oldValue } of defaultsApplied) {
|
|
227
|
-
const target = modelObj.$
|
|
228
|
-
if (oldValue === undefined) {
|
|
229
|
-
// Key was added (didn't exist in snapshot)
|
|
230
|
-
const change: ObjectAddChange = {
|
|
231
|
-
type: DeepChangeType.ObjectAdd,
|
|
232
|
-
path: [],
|
|
233
|
-
target,
|
|
234
|
-
key,
|
|
235
|
-
newValue: target[key], // Use the tweaked value from $
|
|
236
|
-
isInit: true,
|
|
237
|
-
}
|
|
238
|
-
emitDeepChange(modelObj, change)
|
|
239
|
-
} else {
|
|
240
|
-
// Key was updated (had a different value in snapshot, e.g., null -> default)
|
|
241
|
-
const change: ObjectUpdateChange = {
|
|
242
|
-
type: DeepChangeType.ObjectUpdate,
|
|
243
|
-
path: [],
|
|
244
|
-
target,
|
|
245
|
-
key,
|
|
246
|
-
newValue: target[key], // Use the tweaked value from $
|
|
247
|
-
oldValue,
|
|
248
|
-
isInit: true,
|
|
249
|
-
}
|
|
250
|
-
emitDeepChange(modelObj, change)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// type check it if needed
|
|
255
|
-
if (isModelAutoTypeCheckingEnabled()) {
|
|
256
|
-
const err = modelObj.typeCheck()
|
|
257
|
-
if (err) {
|
|
258
|
-
err.throw()
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
function getModelDetails(modelClass: ModelClass<AnyModel>) {
|
|
265
|
-
const modelInfo = modelInfoByClass.get(modelClass)
|
|
266
|
-
if (!modelInfo) {
|
|
267
|
-
throw failure(
|
|
268
|
-
`no model info for class ${modelClass.name} could be found - did you forget to add the @model decorator?`
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const modelIdPropertyName = getModelIdPropertyName(modelClass)
|
|
273
|
-
const modelProps = getInternalModelClassPropsInfo(modelClass)
|
|
274
|
-
const modelIdPropData = modelIdPropertyName ? modelProps[modelIdPropertyName] : undefined
|
|
275
|
-
|
|
276
|
-
return { modelInfo, modelIdPropertyName, modelProps, modelIdPropData }
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function finalizeNewModel(
|
|
280
|
-
modelObj: O.Writable<AnyModel>,
|
|
281
|
-
initialData: any,
|
|
282
|
-
modelClass: ModelClass<AnyModel>
|
|
283
|
-
) {
|
|
284
|
-
tweakModel(modelObj, undefined)
|
|
285
|
-
|
|
286
|
-
// create observable data object with initial data
|
|
287
|
-
modelObj.$ = tweakPlainObject(
|
|
288
|
-
initialData,
|
|
289
|
-
{ parent: modelObj, path: "$" },
|
|
290
|
-
modelObj[modelTypeKey],
|
|
291
|
-
false,
|
|
292
|
-
true
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
if (inDevMode) {
|
|
296
|
-
makePropReadonly(modelObj, "$", true)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// run any extra initializers for the class as needed
|
|
300
|
-
applyModelInitializers(modelClass, modelObj)
|
|
301
|
-
}
|
|
1
|
+
import { action, set } from "mobx"
|
|
2
|
+
import type { O } from "ts-toolbelt"
|
|
3
|
+
import {
|
|
4
|
+
DeepChangeType,
|
|
5
|
+
emitDeepChange,
|
|
6
|
+
type ObjectAddChange,
|
|
7
|
+
type ObjectUpdateChange,
|
|
8
|
+
} from "../deepChange/onDeepChange"
|
|
9
|
+
import { isModelAutoTypeCheckingEnabled } from "../globalConfig/globalConfig"
|
|
10
|
+
import type { ModelClass, ModelCreationData } from "../modelShared/BaseModelShared"
|
|
11
|
+
import { modelInfoByClass } from "../modelShared/modelInfo"
|
|
12
|
+
import { getInternalModelClassPropsInfo } from "../modelShared/modelPropsInfo"
|
|
13
|
+
import { applyModelInitializers } from "../modelShared/newModel"
|
|
14
|
+
import { getModelPropStoredDefaultValue, noDefaultValue } from "../modelShared/prop"
|
|
15
|
+
import { createPatchForObjectValueChange, emitPatches } from "../patch/emitPatch"
|
|
16
|
+
import type { Patch } from "../patch/Patch"
|
|
17
|
+
import { tweakModel } from "../tweaker/tweakModel"
|
|
18
|
+
import { tweakPlainObject } from "../tweaker/tweakPlainObject"
|
|
19
|
+
import { failure, inDevMode, makePropReadonly } from "../utils"
|
|
20
|
+
import { setIfDifferent, setIfDifferentWithReturn } from "../utils/setIfDifferent"
|
|
21
|
+
import type { AnyModel } from "./BaseModel"
|
|
22
|
+
import { getModelIdPropertyName } from "./getModelMetadata"
|
|
23
|
+
import type { ModelConstructorOptions } from "./ModelConstructorOptions"
|
|
24
|
+
import { modelTypeKey } from "./metadata"
|
|
25
|
+
import { assertIsModelClass } from "./utils"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export const internalNewModel = action(
|
|
31
|
+
"newModel",
|
|
32
|
+
<M extends AnyModel>(
|
|
33
|
+
origModelObj: M,
|
|
34
|
+
initialData: ModelCreationData<M>,
|
|
35
|
+
modelClass: ModelClass<AnyModel>
|
|
36
|
+
): void => {
|
|
37
|
+
if (inDevMode) {
|
|
38
|
+
assertIsModelClass(modelClass, "modelClass")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { modelInfo, modelIdPropertyName, modelProps, modelIdPropData } =
|
|
42
|
+
getModelDetails(modelClass)
|
|
43
|
+
|
|
44
|
+
// use symbol if provided
|
|
45
|
+
if (modelIdPropertyName && modelIdPropData) {
|
|
46
|
+
let id: string | undefined
|
|
47
|
+
if (initialData[modelIdPropertyName]) {
|
|
48
|
+
id = initialData[modelIdPropertyName]
|
|
49
|
+
} else {
|
|
50
|
+
id = (modelIdPropData._defaultFn as () => string)()
|
|
51
|
+
}
|
|
52
|
+
setIfDifferent(initialData, modelIdPropertyName, id)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const modelObj = origModelObj as O.Writable<M>
|
|
56
|
+
modelObj[modelTypeKey] = modelInfo.name
|
|
57
|
+
|
|
58
|
+
// fill in defaults in initial data
|
|
59
|
+
const modelPropsKeys = Object.keys(modelProps)
|
|
60
|
+
for (let i = 0; i < modelPropsKeys.length; i++) {
|
|
61
|
+
const k = modelPropsKeys[i]
|
|
62
|
+
|
|
63
|
+
// id is already initialized above
|
|
64
|
+
if (k === modelIdPropertyName) {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const propData = modelProps[k]
|
|
69
|
+
|
|
70
|
+
const initialValue = initialData[k]
|
|
71
|
+
let newValue = initialValue
|
|
72
|
+
let changed = false
|
|
73
|
+
|
|
74
|
+
// apply untransform (if any) if not in snapshot mode
|
|
75
|
+
if (propData._transform) {
|
|
76
|
+
changed = true
|
|
77
|
+
newValue = propData._transform.untransform(newValue, modelObj, k)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// apply default value (if needed)
|
|
81
|
+
if (newValue == null) {
|
|
82
|
+
const defaultValue = getModelPropStoredDefaultValue(propData, modelObj, k)
|
|
83
|
+
if (defaultValue !== noDefaultValue) {
|
|
84
|
+
changed = true
|
|
85
|
+
newValue = defaultValue
|
|
86
|
+
} else if (!(k in initialData)) {
|
|
87
|
+
// for mobx4, we need to set up properties even if they are undefined
|
|
88
|
+
changed = true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (changed) {
|
|
93
|
+
// setIfDifferent not required
|
|
94
|
+
set(initialData, k, newValue)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
finalizeNewModel(modelObj, initialData, modelClass)
|
|
99
|
+
|
|
100
|
+
// type check it if needed
|
|
101
|
+
if (isModelAutoTypeCheckingEnabled()) {
|
|
102
|
+
const err = modelObj.typeCheck()
|
|
103
|
+
if (err) {
|
|
104
|
+
err.throw()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
export const internalFromSnapshotModel = action(
|
|
114
|
+
"fromSnapshotModel",
|
|
115
|
+
<M extends AnyModel>(
|
|
116
|
+
origModelObj: M,
|
|
117
|
+
snapshotInitialData: NonNullable<ModelConstructorOptions["snapshotInitialData"]>,
|
|
118
|
+
modelClass: ModelClass<AnyModel>,
|
|
119
|
+
generateNewIds: boolean
|
|
120
|
+
): void => {
|
|
121
|
+
if (inDevMode) {
|
|
122
|
+
assertIsModelClass(modelClass, "modelClass")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const { modelInfo, modelIdPropertyName, modelProps, modelIdPropData } =
|
|
126
|
+
getModelDetails(modelClass)
|
|
127
|
+
|
|
128
|
+
let id: string | undefined
|
|
129
|
+
let sn = snapshotInitialData.unprocessedSnapshot
|
|
130
|
+
|
|
131
|
+
if (modelIdPropData && modelIdPropertyName) {
|
|
132
|
+
if (generateNewIds) {
|
|
133
|
+
id = (modelIdPropData._defaultFn as () => string)()
|
|
134
|
+
} else {
|
|
135
|
+
id = sn[modelIdPropertyName]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (modelClass.fromSnapshotProcessor) {
|
|
140
|
+
sn = modelClass.fromSnapshotProcessor(sn)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const initialData = snapshotInitialData.snapshotToInitialData(sn)
|
|
144
|
+
|
|
145
|
+
const modelObj = origModelObj as O.Writable<M>
|
|
146
|
+
modelObj[modelTypeKey] = modelInfo.name
|
|
147
|
+
|
|
148
|
+
const patches: Patch[] = []
|
|
149
|
+
const inversePatches: Patch[] = []
|
|
150
|
+
const defaultsApplied: { key: string; oldValue: unknown; newValue: unknown }[] = []
|
|
151
|
+
|
|
152
|
+
if (modelIdPropertyName) {
|
|
153
|
+
const initialValue = initialData[modelIdPropertyName]
|
|
154
|
+
const valueChanged = setIfDifferentWithReturn(initialData, modelIdPropertyName, id)
|
|
155
|
+
|
|
156
|
+
if (valueChanged) {
|
|
157
|
+
const modelIdPath = [modelIdPropertyName]
|
|
158
|
+
|
|
159
|
+
patches.push(createPatchForObjectValueChange(modelIdPath, initialValue, id))
|
|
160
|
+
inversePatches.push(createPatchForObjectValueChange(modelIdPath, id, initialValue))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// fill in defaults in initial data
|
|
165
|
+
const modelPropsKeys = Object.keys(modelProps)
|
|
166
|
+
for (let i = 0; i < modelPropsKeys.length; i++) {
|
|
167
|
+
const k = modelPropsKeys[i]
|
|
168
|
+
|
|
169
|
+
// id is already initialized above
|
|
170
|
+
if (k === modelIdPropertyName) {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const propData = modelProps[k]
|
|
175
|
+
|
|
176
|
+
const initialValue = initialData[k]
|
|
177
|
+
let newValue = initialValue
|
|
178
|
+
let changed = false
|
|
179
|
+
|
|
180
|
+
// apply default value (if needed)
|
|
181
|
+
if (newValue == null) {
|
|
182
|
+
const defaultValue = getModelPropStoredDefaultValue(propData, modelObj, k)
|
|
183
|
+
if (defaultValue !== noDefaultValue) {
|
|
184
|
+
changed = true
|
|
185
|
+
newValue = defaultValue
|
|
186
|
+
} else if (!(k in initialData!)) {
|
|
187
|
+
// for mobx4, we need to set up properties even if they are undefined
|
|
188
|
+
changed = true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (changed) {
|
|
193
|
+
// setIfDifferent not required
|
|
194
|
+
set(initialData, k, newValue)
|
|
195
|
+
|
|
196
|
+
if (newValue !== initialValue) {
|
|
197
|
+
const propPath = [k]
|
|
198
|
+
|
|
199
|
+
patches.push(createPatchForObjectValueChange(propPath, initialValue, newValue))
|
|
200
|
+
inversePatches.push(createPatchForObjectValueChange(propPath, newValue, initialValue))
|
|
201
|
+
|
|
202
|
+
// Track defaults applied for deep change emission
|
|
203
|
+
defaultsApplied.push({ key: k, oldValue: initialValue, newValue })
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// also emit a patch for modelType, since it will get included in the snapshot
|
|
209
|
+
const initialModelType = snapshotInitialData?.unprocessedModelType
|
|
210
|
+
const newModelType = modelInfo.name
|
|
211
|
+
if (initialModelType !== newModelType) {
|
|
212
|
+
const modelTypePath = [modelTypeKey]
|
|
213
|
+
|
|
214
|
+
patches.push(createPatchForObjectValueChange(modelTypePath, initialModelType, newModelType))
|
|
215
|
+
inversePatches.push(
|
|
216
|
+
createPatchForObjectValueChange(modelTypePath, newModelType, initialModelType)
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
finalizeNewModel(modelObj, initialData, modelClass)
|
|
221
|
+
|
|
222
|
+
emitPatches(modelObj, patches, inversePatches)
|
|
223
|
+
|
|
224
|
+
// Emit deep changes for defaults applied during fromSnapshot
|
|
225
|
+
// These are emitted with isInit: true so bindings can sync them to CRDTs
|
|
226
|
+
for (const { key, oldValue } of defaultsApplied) {
|
|
227
|
+
const target = modelObj.$
|
|
228
|
+
if (oldValue === undefined) {
|
|
229
|
+
// Key was added (didn't exist in snapshot)
|
|
230
|
+
const change: ObjectAddChange = {
|
|
231
|
+
type: DeepChangeType.ObjectAdd,
|
|
232
|
+
path: [],
|
|
233
|
+
target,
|
|
234
|
+
key,
|
|
235
|
+
newValue: target[key], // Use the tweaked value from $
|
|
236
|
+
isInit: true,
|
|
237
|
+
}
|
|
238
|
+
emitDeepChange(modelObj, change)
|
|
239
|
+
} else {
|
|
240
|
+
// Key was updated (had a different value in snapshot, e.g., null -> default)
|
|
241
|
+
const change: ObjectUpdateChange = {
|
|
242
|
+
type: DeepChangeType.ObjectUpdate,
|
|
243
|
+
path: [],
|
|
244
|
+
target,
|
|
245
|
+
key,
|
|
246
|
+
newValue: target[key], // Use the tweaked value from $
|
|
247
|
+
oldValue,
|
|
248
|
+
isInit: true,
|
|
249
|
+
}
|
|
250
|
+
emitDeepChange(modelObj, change)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// type check it if needed
|
|
255
|
+
if (isModelAutoTypeCheckingEnabled()) {
|
|
256
|
+
const err = modelObj.typeCheck()
|
|
257
|
+
if (err) {
|
|
258
|
+
err.throw()
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
function getModelDetails(modelClass: ModelClass<AnyModel>) {
|
|
265
|
+
const modelInfo = modelInfoByClass.get(modelClass)
|
|
266
|
+
if (!modelInfo) {
|
|
267
|
+
throw failure(
|
|
268
|
+
`no model info for class ${modelClass.name} could be found - did you forget to add the @model decorator?`
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const modelIdPropertyName = getModelIdPropertyName(modelClass)
|
|
273
|
+
const modelProps = getInternalModelClassPropsInfo(modelClass)
|
|
274
|
+
const modelIdPropData = modelIdPropertyName ? modelProps[modelIdPropertyName] : undefined
|
|
275
|
+
|
|
276
|
+
return { modelInfo, modelIdPropertyName, modelProps, modelIdPropData }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function finalizeNewModel(
|
|
280
|
+
modelObj: O.Writable<AnyModel>,
|
|
281
|
+
initialData: any,
|
|
282
|
+
modelClass: ModelClass<AnyModel>
|
|
283
|
+
) {
|
|
284
|
+
tweakModel(modelObj, undefined)
|
|
285
|
+
|
|
286
|
+
// create observable data object with initial data
|
|
287
|
+
modelObj.$ = tweakPlainObject(
|
|
288
|
+
initialData,
|
|
289
|
+
{ parent: modelObj, path: "$" },
|
|
290
|
+
modelObj[modelTypeKey],
|
|
291
|
+
false,
|
|
292
|
+
true
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if (inDevMode) {
|
|
296
|
+
makePropReadonly(modelObj, "$", true)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// run any extra initializers for the class as needed
|
|
300
|
+
applyModelInitializers(modelClass, modelObj)
|
|
301
|
+
}
|