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.
Files changed (174) hide show
  1. package/CHANGELOG.md +864 -853
  2. package/README.md +61 -61
  3. package/dist/mobx-keystone.esm.js +11336 -11148
  4. package/dist/mobx-keystone.esm.mjs +11336 -11148
  5. package/dist/mobx-keystone.umd.js +12229 -11738
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
  7. package/dist/types/modelShared/prop.d.ts +5 -0
  8. package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
  9. package/dist/types/ref/Ref.d.ts +1 -1
  10. package/dist/types/treeUtils/sandbox.d.ts +1 -1
  11. package/dist/types/types/TypeChecker.d.ts +1 -1
  12. package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
  13. package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
  14. package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
  15. package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
  16. package/dist/types/types/objectBased/typesModel.d.ts +1 -1
  17. package/dist/types/types/objectBased/typesObject.d.ts +3 -3
  18. package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
  19. package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
  20. package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
  21. package/dist/types/types/types.d.ts +3 -3
  22. package/dist/types/types/utility/typesCodec.d.ts +1 -6
  23. package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
  24. package/dist/types/types/utility/typesOr.d.ts +1 -1
  25. package/dist/types/types/utility/typesRefinement.d.ts +1 -1
  26. package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
  27. package/dist/types/types/utility/typesTag.d.ts +1 -1
  28. package/dist/types/wrappers/ObjectMap.d.ts +2 -0
  29. package/dist/types/wrappers/asMap.d.ts +10 -6
  30. package/package.json +14 -12
  31. package/src/action/applyAction.ts +125 -114
  32. package/src/action/applyMethodCall.ts +37 -37
  33. package/src/action/isModelAction.ts +16 -16
  34. package/src/action/middleware.ts +137 -137
  35. package/src/action/modelFlow.ts +240 -240
  36. package/src/action/modelFlowPromiseGenerator.ts +127 -125
  37. package/src/action/pendingActions.ts +42 -42
  38. package/src/action/wrapInAction.ts +156 -156
  39. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
  40. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
  41. package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
  42. package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
  43. package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
  44. package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
  45. package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
  46. package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
  47. package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
  48. package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
  49. package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
  50. package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
  51. package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
  52. package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
  53. package/src/actionMiddlewares/undoMiddleware.ts +804 -796
  54. package/src/actionMiddlewares/utils.ts +2 -2
  55. package/src/computedTree/computedTree.ts +143 -143
  56. package/src/context/context.ts +1 -1
  57. package/src/dataModel/BaseDataModel.ts +194 -192
  58. package/src/dataModel/actions.ts +1 -1
  59. package/src/dataModel/newDataModel.ts +57 -57
  60. package/src/deepChange/index.ts +1 -1
  61. package/src/deepChange/onDeepChange.ts +244 -244
  62. package/src/frozen/Frozen.ts +146 -146
  63. package/src/globalConfig/globalConfig.ts +107 -107
  64. package/src/index.ts +24 -24
  65. package/src/model/BaseModel.ts +275 -273
  66. package/src/model/ModelConstructorOptions.ts +15 -15
  67. package/src/model/getModelMetadata.ts +51 -51
  68. package/src/model/index.ts +6 -6
  69. package/src/model/mixins.ts +288 -288
  70. package/src/model/newModel.ts +301 -301
  71. package/src/model/utils.ts +249 -249
  72. package/src/modelShared/modelDecorator.ts +260 -253
  73. package/src/modelShared/modelInfo.ts +50 -50
  74. package/src/modelShared/modelSymbols.ts +3 -3
  75. package/src/modelShared/newModel.ts +25 -25
  76. package/src/modelShared/prop.ts +713 -684
  77. package/src/modelShared/sharedInternalModel.ts +427 -417
  78. package/src/modelShared/utils.ts +24 -24
  79. package/src/parent/core.ts +65 -65
  80. package/src/parent/coreObjectChildren.ts +180 -180
  81. package/src/parent/detach.ts +47 -47
  82. package/src/parent/findParent.ts +1 -1
  83. package/src/parent/getChildrenObjects.ts +25 -25
  84. package/src/parent/path.ts +386 -381
  85. package/src/parent/setParent.ts +149 -149
  86. package/src/parent/walkTree.ts +1 -1
  87. package/src/patch/applyPatches.ts +187 -187
  88. package/src/patch/jsonPatch.ts +108 -108
  89. package/src/patch/patchRecorder.ts +6 -1
  90. package/src/redux/connectReduxDevTools.ts +39 -7
  91. package/src/redux/redux.ts +6 -2
  92. package/src/ref/Ref.ts +1 -1
  93. package/src/ref/core.ts +339 -345
  94. package/src/ref/customRef.ts +3 -3
  95. package/src/ref/rootRef.ts +100 -100
  96. package/src/rootStore/attachDetach.ts +89 -89
  97. package/src/rootStore/rootStore.ts +1 -1
  98. package/src/snapshot/SnapshotOf.ts +104 -104
  99. package/src/snapshot/SnapshotProcessingError.ts +45 -45
  100. package/src/snapshot/applySnapshot.ts +177 -181
  101. package/src/snapshot/cloneTreeValue.ts +30 -30
  102. package/src/snapshot/fromArraySnapshot.ts +27 -33
  103. package/src/snapshot/fromModelSnapshot.ts +70 -70
  104. package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
  105. package/src/snapshot/fromSnapshot.ts +198 -198
  106. package/src/snapshot/getSnapshot.ts +80 -79
  107. package/src/snapshot/index.ts +8 -8
  108. package/src/snapshot/internal.ts +230 -230
  109. package/src/snapshot/reconcileArraySnapshot.ts +70 -70
  110. package/src/snapshot/reconcileModelSnapshot.ts +155 -150
  111. package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
  112. package/src/snapshot/reconcileSnapshot.ts +88 -88
  113. package/src/standardActions/actions.ts +64 -64
  114. package/src/standardActions/objectActions.ts +60 -60
  115. package/src/transforms/ImmutableDate.ts +93 -93
  116. package/src/transforms/asMap.ts +50 -50
  117. package/src/transforms/asSet.ts +22 -22
  118. package/src/transforms/bigint.ts +20 -20
  119. package/src/transforms/date.ts +40 -40
  120. package/src/treeUtils/deepEquals.ts +55 -55
  121. package/src/treeUtils/sandbox.ts +6 -3
  122. package/src/tweaker/TweakerPriority.ts +9 -9
  123. package/src/tweaker/tweak.ts +4 -3
  124. package/src/tweaker/tweakArray.ts +530 -475
  125. package/src/tweaker/tweakFrozen.ts +42 -42
  126. package/src/tweaker/tweakModel.ts +1 -1
  127. package/src/tweaker/tweakPlainObject.ts +355 -355
  128. package/src/tweaker/typeChecking.ts +101 -101
  129. package/src/types/SnapshotTypeMismatchError.ts +46 -46
  130. package/src/types/TypeCheckError.ts +183 -184
  131. package/src/types/TypeChecker.ts +202 -185
  132. package/src/types/arrayBased/typesArray.ts +136 -137
  133. package/src/types/arrayBased/typesTuple.ts +138 -139
  134. package/src/types/createPerEntryCachedCheck.ts +166 -160
  135. package/src/types/index.ts +6 -6
  136. package/src/types/objectBased/typesArraySet.ts +133 -134
  137. package/src/types/objectBased/typesDataModelData.ts +203 -204
  138. package/src/types/objectBased/typesModel.ts +220 -221
  139. package/src/types/objectBased/typesObject.ts +308 -310
  140. package/src/types/objectBased/typesObjectMap.ts +142 -143
  141. package/src/types/objectBased/typesRecord.ts +156 -157
  142. package/src/types/objectBased/typesRef.ts +89 -89
  143. package/src/types/primitiveBased/typesEnum.ts +63 -63
  144. package/src/types/primitiveBased/typesPrimitive.ts +219 -220
  145. package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
  146. package/src/types/resolveTypeChecker.ts +78 -78
  147. package/src/types/schemas.ts +208 -208
  148. package/src/types/tProp.ts +277 -268
  149. package/src/types/typeCheck.ts +21 -21
  150. package/src/types/typeCheckErrorUtils.ts +23 -23
  151. package/src/types/types.ts +142 -142
  152. package/src/types/utility/typeToStoredData.ts +37 -37
  153. package/src/types/utility/typesBigInt.ts +15 -15
  154. package/src/types/utility/typesCodec.ts +41 -38
  155. package/src/types/utility/typesCodecCore.ts +178 -178
  156. package/src/types/utility/typesCodecSupport.ts +806 -806
  157. package/src/types/utility/typesDate.ts +27 -27
  158. package/src/types/utility/typesMap.ts +253 -222
  159. package/src/types/utility/typesOr.ts +263 -259
  160. package/src/types/utility/typesRefinement.ts +131 -125
  161. package/src/types/utility/typesSet.ts +110 -110
  162. package/src/types/utility/typesSkipCheck.ts +69 -70
  163. package/src/types/utility/typesTag.ts +88 -82
  164. package/src/types/utility/typesUnchecked.ts +40 -40
  165. package/src/utils/AnyFunction.ts +4 -4
  166. package/src/utils/ModelPool.ts +59 -59
  167. package/src/utils/chainFns.ts +18 -18
  168. package/src/utils/decorators.ts +274 -274
  169. package/src/utils/errorDiagnostics.ts +204 -204
  170. package/src/utils/index.ts +443 -372
  171. package/src/wrappers/ArraySet.ts +156 -156
  172. package/src/wrappers/ObjectMap.ts +155 -136
  173. package/src/wrappers/asMap.ts +65 -30
  174. 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 { Path } from "parent/pathTypes"
10
- import { assertCanWrite } from "../action/protection"
11
- import { DeepChangeType, emitDeepChange, getIsInInitPhase } from "../deepChange/onDeepChange"
12
- import type { AnyModel } from "../model/BaseModel"
13
- import { modelTypeKey } from "../model/metadata"
14
- import type { ModelClass } from "../modelShared/BaseModelShared"
15
- import { getModelInfoForName, getModelNotRegisteredErrorMessage } from "../modelShared/modelInfo"
16
- import { dataToModelNode } from "../parent/core"
17
- import type { ParentPath } from "../parent/path"
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[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
- untransformedSn[k] = valueSn.transformed
102
- }
103
- }
104
-
105
- let transformFn: SnapshotTransformFn | undefined
106
- if (snapshotModelType) {
107
- 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
- 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
- }
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
+ }