mobx-keystone 1.22.0 → 1.24.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 +12 -0
- package/LICENSE +21 -21
- package/README.md +1 -1
- package/dist/mobx-keystone.esm.js +7198 -6242
- package/dist/mobx-keystone.esm.mjs +7198 -6242
- package/dist/mobx-keystone.umd.js +7217 -6243
- package/dist/types/action/runUnprotected.d.ts +8 -4
- package/dist/types/model/Model.d.ts +5 -0
- package/dist/types/modelShared/modelDecorator.d.ts +1 -1
- package/dist/types/modelShared/prop.d.ts +8 -4
- package/dist/types/snapshot/modelInitialData.d.ts +1 -0
- package/dist/types/tweaker/treeNodeMetadata.d.ts +1 -0
- package/dist/types/types/createCachedTypeCheck.d.ts +1 -0
- package/dist/types/utils/index.d.ts +24 -0
- package/package.json +15 -11
- package/src/action/middleware.ts +5 -18
- package/src/action/mutationBatch.ts +72 -0
- package/src/action/runUnprotected.ts +21 -8
- package/src/action/wrapInAction.ts +45 -15
- package/src/actionMiddlewares/undoMiddleware.ts +19 -13
- package/src/dataModel/BaseDataModel.ts +4 -8
- package/src/deepChange/onDeepChange.ts +246 -16
- package/src/model/BaseModel.ts +4 -12
- package/src/model/Model.ts +5 -0
- package/src/model/newModel.ts +68 -36
- package/src/modelShared/modelDecorator.ts +64 -53
- package/src/modelShared/prop.ts +105 -63
- package/src/modelShared/sharedInternalModel.ts +91 -60
- package/src/parent/core.ts +16 -10
- package/src/parent/coreObjectChildren.ts +219 -21
- package/src/parent/path.ts +6 -5
- package/src/parent/setParent.ts +5 -5
- package/src/patch/emitPatch.ts +102 -20
- package/src/ref/Ref.ts +4 -5
- package/src/ref/core.ts +2 -2
- package/src/snapshot/fromArraySnapshot.ts +11 -2
- package/src/snapshot/fromModelSnapshot.ts +10 -3
- package/src/snapshot/fromPlainObjectSnapshot.ts +18 -7
- package/src/snapshot/fromSnapshot.ts +31 -9
- package/src/snapshot/getSnapshot.ts +8 -3
- package/src/snapshot/internal.ts +243 -82
- package/src/snapshot/modelInitialData.ts +41 -0
- package/src/snapshot/reconcileArraySnapshot.ts +9 -3
- package/src/snapshot/reconcileModelSnapshot.ts +97 -17
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +9 -3
- package/src/treeUtils/draft.ts +7 -8
- package/src/tweaker/core.ts +30 -5
- package/src/tweaker/treeNodeMetadata.ts +38 -0
- package/src/tweaker/tweak.ts +15 -4
- package/src/tweaker/tweakArray.ts +159 -121
- package/src/tweaker/tweakFrozen.ts +2 -2
- package/src/tweaker/tweakModel.ts +2 -2
- package/src/tweaker/tweakPlainObject.ts +117 -124
- package/src/tweaker/typeChecking.ts +10 -1
- package/src/types/TypeChecker.ts +261 -15
- package/src/types/arrayBased/typesArray.ts +38 -40
- package/src/types/arrayBased/typesTuple.ts +51 -34
- package/src/types/createCachedTypeCheck.ts +181 -0
- package/src/types/objectBased/typesArraySet.ts +42 -32
- package/src/types/objectBased/typesDataModelData.ts +18 -11
- package/src/types/objectBased/typesModel.ts +1 -1
- package/src/types/objectBased/typesObject.ts +57 -28
- package/src/types/objectBased/typesObjectMap.ts +51 -40
- package/src/types/objectBased/typesRecord.ts +59 -39
- package/src/types/objectBased/typesRef.ts +1 -1
- package/src/types/primitiveBased/typesPrimitive.ts +9 -9
- package/src/types/tProp.ts +80 -44
- package/src/types/utility/typesOr.ts +35 -24
- package/src/types/utility/typesRefinement.ts +21 -8
- package/src/types/utility/typesSkipCheck.ts +21 -8
- package/src/types/utility/typesTag.ts +21 -9
- package/src/types/utility/typesUnchecked.ts +2 -3
- package/src/utils/ModelPool.ts +34 -19
- package/src/utils/errorDiagnostics.ts +60 -18
- package/src/utils/index.ts +142 -9
- package/src/types/createPerEntryCachedCheck.ts +0 -166
- /package/dist/types/{types/createPerEntryCachedCheck.d.ts → action/mutationBatch.d.ts} +0 -0
|
@@ -9,24 +9,25 @@ import {
|
|
|
9
9
|
observe,
|
|
10
10
|
} from "mobx"
|
|
11
11
|
import { assertCanWrite } from "../action/protection"
|
|
12
|
-
import {
|
|
12
|
+
import { emitArraySpliceDeepChange, emitArrayUpdateDeepChange } from "../deepChange/onDeepChange"
|
|
13
13
|
import { getGlobalConfig } from "../globalConfig"
|
|
14
14
|
import type { ParentPath } from "../parent/path"
|
|
15
|
-
import type { Path } from "../parent/pathTypes"
|
|
16
15
|
import { setParent } from "../parent/setParent"
|
|
17
|
-
import { InternalPatchRecorder } from "../patch/emitPatch"
|
|
16
|
+
import { hasPatchListenersFor, InternalPatchRecorder } from "../patch/emitPatch"
|
|
18
17
|
import type { Patch } from "../patch/Patch"
|
|
19
18
|
import {
|
|
19
|
+
flushInternalSnapshot,
|
|
20
20
|
freezeInternalSnapshot,
|
|
21
21
|
getInternalSnapshot,
|
|
22
22
|
setNewInternalSnapshot,
|
|
23
23
|
updateInternalSnapshot,
|
|
24
24
|
} from "../snapshot/internal"
|
|
25
25
|
import { failure, inDevMode, isArray, isPrimitive } from "../utils"
|
|
26
|
-
import { runningWithoutSnapshotOrPatches,
|
|
26
|
+
import { runningWithoutSnapshotOrPatches, setTweakedObjectUntweakers } from "./core"
|
|
27
27
|
import { TweakerPriority } from "./TweakerPriority"
|
|
28
|
+
import { markAsTweakedObject } from "./treeNodeMetadata"
|
|
28
29
|
import { registerTweaker, tweak } from "./tweak"
|
|
29
|
-
import { runTypeCheckingAfterChange } from "./typeChecking"
|
|
30
|
+
import { isTypeCheckingAfterChangeEnabled, runTypeCheckingAfterChange } from "./typeChecking"
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* @internal
|
|
@@ -55,15 +56,9 @@ export function tweakArray<T extends any[]>(
|
|
|
55
56
|
buffer = new Array(arrLn)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const untweak = () => {
|
|
62
|
-
interceptDisposer()
|
|
63
|
-
observeDisposer()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
tweakedObjects.set(tweakedArr, untweak)
|
|
59
|
+
// Mark it as tweaked before processing children. The cleanup functions are
|
|
60
|
+
// installed after the MobX handlers have been registered.
|
|
61
|
+
markAsTweakedObject(tweakedArr)
|
|
67
62
|
setParent(
|
|
68
63
|
tweakedArr, // value
|
|
69
64
|
parentPath,
|
|
@@ -76,37 +71,10 @@ export function tweakArray<T extends any[]>(
|
|
|
76
71
|
const untransformedSn: any[] = []
|
|
77
72
|
untransformedSn.length = arrLn
|
|
78
73
|
|
|
79
|
-
// externally
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const flushReplacementRange = () => {
|
|
85
|
-
if (replacementRangeStart >= 0) {
|
|
86
|
-
tweakedArr.spliceWithArray(
|
|
87
|
-
replacementRangeStart,
|
|
88
|
-
replacementRangeItems!.length,
|
|
89
|
-
replacementRangeItems!
|
|
90
|
-
)
|
|
91
|
-
replacementRangeStart = -1
|
|
92
|
-
replacementRangeItems = undefined
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const queueReplacementIfDifferent = (index: number, value: unknown) => {
|
|
97
|
-
if (tweakedArr[index] === value && index in tweakedArr) {
|
|
98
|
-
flushReplacementRange()
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (replacementRangeStart + (replacementRangeItems?.length ?? 0) === index) {
|
|
103
|
-
replacementRangeItems!.push(value)
|
|
104
|
-
} else {
|
|
105
|
-
flushReplacementRange()
|
|
106
|
-
replacementRangeStart = index
|
|
107
|
-
replacementRangeItems = [value]
|
|
108
|
-
}
|
|
109
|
-
}
|
|
74
|
+
// Only externally supplied observable arrays need replacement ranges. Fresh
|
|
75
|
+
// arrays use the bulk buffer path, while snapshot arrays are already populated.
|
|
76
|
+
const replacementQueue =
|
|
77
|
+
!buffer && !childrenAlreadyTweaked ? new ObservableArrayReplacementQueue(tweakedArr) : undefined
|
|
110
78
|
|
|
111
79
|
// substitute initial values by proxied values
|
|
112
80
|
for (let i = 0; i < arrLn; i++) {
|
|
@@ -116,7 +84,7 @@ export function tweakArray<T extends any[]>(
|
|
|
116
84
|
if (buffer) {
|
|
117
85
|
buffer[i] = v
|
|
118
86
|
} else if (!childrenAlreadyTweaked) {
|
|
119
|
-
|
|
87
|
+
replacementQueue!.queueIfDifferent(i, v)
|
|
120
88
|
}
|
|
121
89
|
|
|
122
90
|
untransformedSn[i] = v
|
|
@@ -141,7 +109,7 @@ export function tweakArray<T extends any[]>(
|
|
|
141
109
|
if (buffer) {
|
|
142
110
|
buffer[i] = tweakedValue
|
|
143
111
|
} else if (!childrenAlreadyTweaked) {
|
|
144
|
-
|
|
112
|
+
replacementQueue!.queueIfDifferent(i, tweakedValue)
|
|
145
113
|
}
|
|
146
114
|
|
|
147
115
|
const valueSn = getInternalSnapshot(tweakedValue)!
|
|
@@ -154,12 +122,16 @@ export function tweakArray<T extends any[]>(
|
|
|
154
122
|
// is a single plain mobx bulk insertion
|
|
155
123
|
tweakedArr.replace(buffer)
|
|
156
124
|
} else {
|
|
157
|
-
|
|
125
|
+
replacementQueue?.flush()
|
|
158
126
|
}
|
|
159
127
|
setNewInternalSnapshot(tweakedArr, untransformedSn, undefined)
|
|
160
128
|
|
|
161
|
-
interceptDisposer = intercept(
|
|
162
|
-
|
|
129
|
+
const interceptDisposer = intercept(
|
|
130
|
+
tweakedArr,
|
|
131
|
+
interceptArrayMutation.bind(undefined, tweakedArr)
|
|
132
|
+
)
|
|
133
|
+
const observeDisposer = observe(tweakedArr, arrayDidChange)
|
|
134
|
+
setTweakedObjectUntweakers(tweakedArr, interceptDisposer, observeDisposer)
|
|
163
135
|
|
|
164
136
|
return tweakedArr as any
|
|
165
137
|
}
|
|
@@ -174,43 +146,18 @@ function mutateSplice(index: number, removedCount: number, addedItems: any[], sn
|
|
|
174
146
|
|
|
175
147
|
const patchRecorder = new InternalPatchRecorder()
|
|
176
148
|
|
|
177
|
-
const emptyPath: Path = Object.freeze([])
|
|
178
|
-
|
|
179
149
|
function arrayDidChange(change: IArrayDidChange) {
|
|
180
150
|
const arr = change.object
|
|
181
|
-
const oldSnapshot = getInternalSnapshot(arr as Array<unknown>)!.untransformed
|
|
182
151
|
|
|
183
152
|
patchRecorder.reset()
|
|
184
153
|
|
|
185
|
-
let mutate: ((sn: any[]) => void) | undefined
|
|
186
|
-
|
|
187
154
|
switch (change.type) {
|
|
188
155
|
case "splice":
|
|
189
|
-
|
|
190
|
-
// Emit deep change for splice
|
|
191
|
-
emitDeepChange(arr, {
|
|
192
|
-
type: DeepChangeType.ArraySplice,
|
|
193
|
-
target: arr,
|
|
194
|
-
path: emptyPath,
|
|
195
|
-
index: change.index as number,
|
|
196
|
-
addedValues: change.added,
|
|
197
|
-
removedValues: change.removed,
|
|
198
|
-
isInit: getIsInInitPhase(),
|
|
199
|
-
})
|
|
156
|
+
emitArraySpliceDeepChange(arr, arr, change.index as number, change.added, change.removed)
|
|
200
157
|
break
|
|
201
158
|
|
|
202
159
|
case "update":
|
|
203
|
-
|
|
204
|
-
// Emit deep change for update
|
|
205
|
-
emitDeepChange(arr, {
|
|
206
|
-
type: DeepChangeType.ArrayUpdate,
|
|
207
|
-
target: arr,
|
|
208
|
-
path: emptyPath,
|
|
209
|
-
index: change.index as number,
|
|
210
|
-
newValue: change.newValue,
|
|
211
|
-
oldValue: change.oldValue,
|
|
212
|
-
isInit: getIsInInitPhase(),
|
|
213
|
-
})
|
|
160
|
+
emitArrayUpdateDeepChange(arr, arr, change.index as number, change.newValue, change.oldValue)
|
|
214
161
|
|
|
215
162
|
break
|
|
216
163
|
|
|
@@ -218,9 +165,38 @@ function arrayDidChange(change: IArrayDidChange) {
|
|
|
218
165
|
break
|
|
219
166
|
}
|
|
220
167
|
|
|
221
|
-
|
|
168
|
+
if (runningWithoutSnapshotOrPatches) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const oldSnapshot = getInternalSnapshot(arr as Array<unknown>)!.untransformed
|
|
173
|
+
const shouldEmitPatches = hasPatchListenersFor(arr)
|
|
174
|
+
const shouldBuildInversePatches = shouldEmitPatches || isTypeCheckingAfterChangeEnabled()
|
|
175
|
+
|
|
176
|
+
let mutate: ((sn: any[]) => void) | undefined
|
|
177
|
+
switch (change.type) {
|
|
178
|
+
case "splice":
|
|
179
|
+
mutate = arrayDidChangeSplice(
|
|
180
|
+
change,
|
|
181
|
+
oldSnapshot,
|
|
182
|
+
shouldEmitPatches,
|
|
183
|
+
shouldBuildInversePatches
|
|
184
|
+
)
|
|
185
|
+
break
|
|
186
|
+
case "update":
|
|
187
|
+
mutate = arrayDidChangeUpdate(
|
|
188
|
+
change,
|
|
189
|
+
oldSnapshot,
|
|
190
|
+
shouldEmitPatches,
|
|
191
|
+
shouldBuildInversePatches
|
|
192
|
+
)
|
|
193
|
+
break
|
|
194
|
+
default:
|
|
195
|
+
break
|
|
196
|
+
}
|
|
222
197
|
|
|
223
|
-
|
|
198
|
+
runTypeCheckingAfterChange(arr, patchRecorder)
|
|
199
|
+
if (mutate) {
|
|
224
200
|
updateInternalSnapshot(arr, mutate)
|
|
225
201
|
patchRecorder.emit(arr)
|
|
226
202
|
}
|
|
@@ -229,7 +205,12 @@ function arrayDidChange(change: IArrayDidChange) {
|
|
|
229
205
|
const undefinedInsideArrayErrorMsg =
|
|
230
206
|
"undefined is not supported inside arrays since it is not serializable in JSON, consider using null instead"
|
|
231
207
|
|
|
232
|
-
function arrayDidChangeUpdate(
|
|
208
|
+
function arrayDidChangeUpdate(
|
|
209
|
+
change: any /*IArrayDidChange*/,
|
|
210
|
+
oldSnapshot: any,
|
|
211
|
+
shouldEmitPatches: boolean,
|
|
212
|
+
shouldBuildInversePatches: boolean
|
|
213
|
+
) {
|
|
233
214
|
const k = change.index
|
|
234
215
|
const val = change.newValue
|
|
235
216
|
const oldVal = oldSnapshot[k]
|
|
@@ -242,28 +223,25 @@ function arrayDidChangeUpdate(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
242
223
|
}
|
|
243
224
|
const mutate = mutateSet.bind(undefined, k, newVal)
|
|
244
225
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
[
|
|
256
|
-
{
|
|
257
|
-
op: "replace",
|
|
258
|
-
path,
|
|
259
|
-
value: freezeInternalSnapshot(oldVal),
|
|
260
|
-
},
|
|
261
|
-
]
|
|
262
|
-
)
|
|
226
|
+
if (shouldEmitPatches || shouldBuildInversePatches) {
|
|
227
|
+
const path = [k]
|
|
228
|
+
const patches = shouldEmitPatches
|
|
229
|
+
? [{ op: "replace" as const, path, value: freezeInternalSnapshot(newVal) }]
|
|
230
|
+
: undefined
|
|
231
|
+
const invPatches = shouldBuildInversePatches
|
|
232
|
+
? [{ op: "replace" as const, path, value: freezeInternalSnapshot(oldVal) }]
|
|
233
|
+
: undefined
|
|
234
|
+
patchRecorder.record(patches, invPatches)
|
|
235
|
+
}
|
|
263
236
|
return mutate
|
|
264
237
|
}
|
|
265
238
|
|
|
266
|
-
function arrayDidChangeSplice(
|
|
239
|
+
function arrayDidChangeSplice(
|
|
240
|
+
change: any /*IArrayDidChange*/,
|
|
241
|
+
oldSnapshot: any,
|
|
242
|
+
shouldEmitPatches: boolean,
|
|
243
|
+
shouldBuildInversePatches: boolean
|
|
244
|
+
) {
|
|
267
245
|
const index = change.index as number
|
|
268
246
|
const addedCount = change.addedCount as number
|
|
269
247
|
const removedCount = change.removedCount as number
|
|
@@ -279,11 +257,19 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
279
257
|
}
|
|
280
258
|
}
|
|
281
259
|
|
|
282
|
-
const oldLen = oldSnapshot.length
|
|
283
260
|
const mutate = mutateSplice.bind(undefined, index, removedCount, addedItems)
|
|
284
261
|
|
|
285
|
-
|
|
286
|
-
|
|
262
|
+
// With neither patch listeners nor type checking, there is no inverse-patch
|
|
263
|
+
// data to build. The snapshot mutation above is all the post-change work we
|
|
264
|
+
// need to retain.
|
|
265
|
+
if (!shouldEmitPatches && !shouldBuildInversePatches) {
|
|
266
|
+
return mutate
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const oldLen = oldSnapshot.length
|
|
270
|
+
|
|
271
|
+
const patches: Patch[] | undefined = shouldEmitPatches ? [] : undefined
|
|
272
|
+
const invPatches: Patch[] | undefined = shouldBuildInversePatches ? [] : undefined
|
|
287
273
|
|
|
288
274
|
// optimization: if we add as many as we remove then remove/readd instead
|
|
289
275
|
|
|
@@ -294,8 +280,8 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
294
280
|
// validation might fail
|
|
295
281
|
|
|
296
282
|
if (addedCount === removedCount) {
|
|
297
|
-
const readdPatches: Patch[] = []
|
|
298
|
-
const readdInvPatches: Patch[] = []
|
|
283
|
+
const readdPatches: Patch[] | undefined = shouldEmitPatches ? [] : undefined
|
|
284
|
+
const readdInvPatches: Patch[] | undefined = shouldBuildInversePatches ? [] : undefined
|
|
299
285
|
let removed = 0
|
|
300
286
|
|
|
301
287
|
for (let i = 0; i < addedCount; i++) {
|
|
@@ -306,11 +292,11 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
306
292
|
|
|
307
293
|
if (newVal !== oldVal) {
|
|
308
294
|
const removePath = [realIndex - removed]
|
|
309
|
-
patches
|
|
295
|
+
patches?.push({
|
|
310
296
|
op: "remove",
|
|
311
297
|
path: removePath,
|
|
312
298
|
})
|
|
313
|
-
invPatches
|
|
299
|
+
invPatches?.push({
|
|
314
300
|
op: "remove",
|
|
315
301
|
path: removePath,
|
|
316
302
|
})
|
|
@@ -318,13 +304,13 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
318
304
|
removed++
|
|
319
305
|
|
|
320
306
|
const readdPath = [realIndex]
|
|
321
|
-
readdPatches
|
|
307
|
+
readdPatches?.push({
|
|
322
308
|
op: "add",
|
|
323
309
|
path: readdPath,
|
|
324
310
|
value: freezeInternalSnapshot(newVal),
|
|
325
311
|
})
|
|
326
312
|
|
|
327
|
-
readdInvPatches
|
|
313
|
+
readdInvPatches?.push({
|
|
328
314
|
op: "add",
|
|
329
315
|
path: readdPath,
|
|
330
316
|
value: freezeInternalSnapshot(oldVal),
|
|
@@ -332,10 +318,17 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
332
318
|
}
|
|
333
319
|
}
|
|
334
320
|
|
|
335
|
-
patches.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
invPatches.
|
|
321
|
+
if (patches && readdPatches && readdPatches.length > 0) {
|
|
322
|
+
patches.push(...readdPatches)
|
|
323
|
+
}
|
|
324
|
+
if (invPatches && readdInvPatches && readdInvPatches.length > 0) {
|
|
325
|
+
invPatches.push(...readdInvPatches)
|
|
326
|
+
}
|
|
327
|
+
// We need to reverse once since inverse patches are applied in reverse.
|
|
328
|
+
// Repeated unshift calls here would make a large splice quadratic.
|
|
329
|
+
if (invPatches && invPatches.length > 0) {
|
|
330
|
+
invPatches.reverse()
|
|
331
|
+
}
|
|
339
332
|
} else {
|
|
340
333
|
const interimLen = oldLen - removedCount
|
|
341
334
|
|
|
@@ -344,7 +337,7 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
344
337
|
// optimization, when removing from the end set the length instead
|
|
345
338
|
const removeUsingSetLength = index >= interimLen
|
|
346
339
|
if (removeUsingSetLength) {
|
|
347
|
-
patches
|
|
340
|
+
patches?.push({
|
|
348
341
|
op: "replace",
|
|
349
342
|
path: ["length"],
|
|
350
343
|
value: interimLen,
|
|
@@ -357,14 +350,14 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
357
350
|
|
|
358
351
|
if (!removeUsingSetLength) {
|
|
359
352
|
// remove ...2, 1, 0
|
|
360
|
-
patches
|
|
353
|
+
patches?.push({
|
|
361
354
|
op: "remove",
|
|
362
355
|
path,
|
|
363
356
|
})
|
|
364
357
|
}
|
|
365
358
|
|
|
366
359
|
// add 0, 1, 2... since inverse patches are applied in reverse
|
|
367
|
-
invPatches
|
|
360
|
+
invPatches?.push({
|
|
368
361
|
op: "add",
|
|
369
362
|
path,
|
|
370
363
|
value: freezeInternalSnapshot(oldSnapshot[realIndex]),
|
|
@@ -377,7 +370,7 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
377
370
|
// optimization, for inverse patches, when adding from the end set the length to restore instead
|
|
378
371
|
const restoreUsingSetLength = index >= interimLen
|
|
379
372
|
if (restoreUsingSetLength) {
|
|
380
|
-
invPatches
|
|
373
|
+
invPatches?.push({
|
|
381
374
|
op: "replace",
|
|
382
375
|
path: ["length"],
|
|
383
376
|
value: interimLen,
|
|
@@ -389,7 +382,7 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
389
382
|
const path = [realIndex]
|
|
390
383
|
|
|
391
384
|
// add 0, 1, 2...
|
|
392
|
-
patches
|
|
385
|
+
patches?.push({
|
|
393
386
|
op: "add",
|
|
394
387
|
path,
|
|
395
388
|
value: freezeInternalSnapshot(
|
|
@@ -399,7 +392,7 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
399
392
|
|
|
400
393
|
// remove ...2, 1, 0 since inverse patches are applied in reverse
|
|
401
394
|
if (!restoreUsingSetLength) {
|
|
402
|
-
invPatches
|
|
395
|
+
invPatches?.push({
|
|
403
396
|
op: "remove",
|
|
404
397
|
path,
|
|
405
398
|
})
|
|
@@ -408,7 +401,9 @@ function arrayDidChangeSplice(change: any /*IArrayDidChange*/, oldSnapshot: any)
|
|
|
408
401
|
}
|
|
409
402
|
}
|
|
410
403
|
|
|
411
|
-
|
|
404
|
+
if (patches || invPatches) {
|
|
405
|
+
patchRecorder.record(patches, invPatches)
|
|
406
|
+
}
|
|
412
407
|
return mutate
|
|
413
408
|
}
|
|
414
409
|
|
|
@@ -421,10 +416,16 @@ function interceptArrayMutation(
|
|
|
421
416
|
|
|
422
417
|
switch (change.type) {
|
|
423
418
|
case "splice":
|
|
419
|
+
validateArrayMutationSplice(change)
|
|
420
|
+
// Flush before removed values are untweaked and indexes are reassigned.
|
|
421
|
+
flushInternalSnapshot(array, false)
|
|
424
422
|
interceptArrayMutationSplice(change)
|
|
425
423
|
break
|
|
426
424
|
|
|
427
425
|
case "update":
|
|
426
|
+
validateArrayMutationUpdate(change)
|
|
427
|
+
// Flush before the old value is detached below.
|
|
428
|
+
flushInternalSnapshot(array, false)
|
|
428
429
|
interceptArrayMutationUpdate(change, array)
|
|
429
430
|
break
|
|
430
431
|
|
|
@@ -434,7 +435,7 @@ function interceptArrayMutation(
|
|
|
434
435
|
return change
|
|
435
436
|
}
|
|
436
437
|
|
|
437
|
-
function
|
|
438
|
+
function validateArrayMutationUpdate(change: IArrayWillChange) {
|
|
438
439
|
if (
|
|
439
440
|
inDevMode &&
|
|
440
441
|
!getGlobalConfig().allowUndefinedArrayElements &&
|
|
@@ -442,7 +443,9 @@ function interceptArrayMutationUpdate(change: IArrayWillChange, array: IObservab
|
|
|
442
443
|
) {
|
|
443
444
|
throw failure(undefinedInsideArrayErrorMsg)
|
|
444
445
|
}
|
|
446
|
+
}
|
|
445
447
|
|
|
448
|
+
function interceptArrayMutationUpdate(change: IArrayWillChange, array: IObservableArray) {
|
|
446
449
|
// TODO: should be change.object, but mobx is bugged and doesn't send the proxy
|
|
447
450
|
const oldVal = array[change.index]
|
|
448
451
|
tweak(oldVal, undefined) // set old prop obj parent to undefined
|
|
@@ -450,7 +453,7 @@ function interceptArrayMutationUpdate(change: IArrayWillChange, array: IObservab
|
|
|
450
453
|
change.newValue = tweak(change.newValue, { parent: array, path: change.index })
|
|
451
454
|
}
|
|
452
455
|
|
|
453
|
-
function
|
|
456
|
+
function validateArrayMutationSplice(change: IArrayWillSplice) {
|
|
454
457
|
if (inDevMode && !getGlobalConfig().allowUndefinedArrayElements) {
|
|
455
458
|
const len = change.added.length
|
|
456
459
|
for (let i = 0; i < len; i++) {
|
|
@@ -460,7 +463,9 @@ function interceptArrayMutationSplice(change: IArrayWillSplice) {
|
|
|
460
463
|
}
|
|
461
464
|
}
|
|
462
465
|
}
|
|
466
|
+
}
|
|
463
467
|
|
|
468
|
+
function interceptArrayMutationSplice(change: IArrayWillSplice) {
|
|
464
469
|
for (let i = 0; i < change.removedCount; i++) {
|
|
465
470
|
const removedValue = change.object[change.index + i]
|
|
466
471
|
tweak(removedValue, undefined)
|
|
@@ -510,6 +515,39 @@ const observableOptions = {
|
|
|
510
515
|
deep: false,
|
|
511
516
|
}
|
|
512
517
|
|
|
518
|
+
class ObservableArrayReplacementQueue {
|
|
519
|
+
private readonly array: IObservableArray<any>
|
|
520
|
+
private rangeStart = -1
|
|
521
|
+
private rangeItems: any[] | undefined
|
|
522
|
+
|
|
523
|
+
constructor(array: IObservableArray<any>) {
|
|
524
|
+
this.array = array
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
queueIfDifferent(index: number, value: unknown): void {
|
|
528
|
+
if (this.array[index] === value && index in this.array) {
|
|
529
|
+
this.flush()
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (this.rangeStart + (this.rangeItems?.length ?? 0) === index) {
|
|
534
|
+
this.rangeItems!.push(value)
|
|
535
|
+
} else {
|
|
536
|
+
this.flush()
|
|
537
|
+
this.rangeStart = index
|
|
538
|
+
this.rangeItems = [value]
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
flush(): void {
|
|
543
|
+
if (this.rangeStart >= 0) {
|
|
544
|
+
this.array.spliceWithArray(this.rangeStart, this.rangeItems!.length, this.rangeItems!)
|
|
545
|
+
this.rangeStart = -1
|
|
546
|
+
this.rangeItems = undefined
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
513
551
|
function getValueAfterSplice<T>(
|
|
514
552
|
array: readonly T[],
|
|
515
553
|
i: number,
|
|
@@ -2,8 +2,8 @@ import { Frozen, frozenKey } from "../frozen/Frozen"
|
|
|
2
2
|
import type { ParentPath } from "../parent/path"
|
|
3
3
|
import { setParent } from "../parent/setParent"
|
|
4
4
|
import { setNewInternalSnapshot } from "../snapshot/internal"
|
|
5
|
-
import { tweakedObjects } from "./core"
|
|
6
5
|
import { TweakerPriority } from "./TweakerPriority"
|
|
6
|
+
import { markAsTweakedObject } from "./treeNodeMetadata"
|
|
7
7
|
import { registerTweaker } from "./tweak"
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -13,7 +13,7 @@ export function tweakFrozen<T extends Frozen<any>>(
|
|
|
13
13
|
frozenObj: T,
|
|
14
14
|
parentPath: ParentPath<any> | undefined
|
|
15
15
|
): T {
|
|
16
|
-
|
|
16
|
+
markAsTweakedObject(frozenObj)
|
|
17
17
|
setParent(
|
|
18
18
|
frozenObj, // value
|
|
19
19
|
parentPath,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { isModel } from "../model/utils"
|
|
2
2
|
import type { ParentPath } from "../parent/path"
|
|
3
3
|
import { setParent } from "../parent/setParent"
|
|
4
|
-
import { tweakedObjects } from "./core"
|
|
5
4
|
import { TweakerPriority } from "./TweakerPriority"
|
|
5
|
+
import { markAsTweakedObject } from "./treeNodeMetadata"
|
|
6
6
|
import { registerTweaker } from "./tweak"
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @internal
|
|
10
10
|
*/
|
|
11
11
|
export function tweakModel<T extends object>(value: T, parentPath: ParentPath<any> | undefined): T {
|
|
12
|
-
|
|
12
|
+
markAsTweakedObject(value)
|
|
13
13
|
setParent(
|
|
14
14
|
value,
|
|
15
15
|
parentPath,
|