mobx-keystone 1.24.1 → 2.0.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 +137 -0
- package/README.md +5 -1
- package/dist/mobx-keystone.esm.js +1608 -1072
- package/dist/mobx-keystone.esm.mjs +1608 -1072
- package/dist/mobx-keystone.umd.js +1613 -1072
- package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
- package/dist/types/dataModel/dataModelInstanceCache.d.ts +1 -0
- package/dist/types/deepChange/onDeepChange.d.ts +9 -0
- package/dist/types/parent/detach.d.ts +1 -1
- package/dist/types/parent/findChildren.d.ts +1 -1
- package/dist/types/patch/applyPatches.d.ts +2 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +2 -1
- package/dist/types/snapshot/SnapshotOf.d.ts +2 -2
- package/dist/types/standardActions/arrayActions.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +0 -4
- package/dist/types/types/TypeCheckError.d.ts +1 -0
- package/dist/types/types/utility/typesRefinement.d.ts +2 -2
- package/dist/types/utils/forEachWithDelayedThrow.d.ts +1 -0
- package/dist/types/utils/index.d.ts +13 -0
- package/dist/types/utils/mapUtils.d.ts +2 -2
- package/dist/types/utils/setLike.d.ts +1 -0
- package/package.json +7 -7
- package/src/action/applyAction.ts +1 -1
- package/src/action/middleware.ts +17 -36
- package/src/action/modelFlow.ts +4 -5
- package/src/action/mutationBatch.ts +10 -10
- package/src/action/pendingActions.ts +3 -4
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +19 -14
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +29 -20
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +4 -3
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +4 -12
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +4 -13
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +11 -13
- package/src/actionMiddlewares/transactionMiddleware.ts +42 -11
- package/src/actionMiddlewares/undoMiddleware.ts +76 -60
- package/src/actionMiddlewares/utils.ts +2 -1
- package/src/computedTree/computedTree.ts +14 -3
- package/src/context/context.ts +17 -45
- package/src/dataModel/BaseDataModel.ts +20 -16
- package/src/dataModel/dataModelInstanceCache.ts +8 -0
- package/src/deepChange/onDeepChange.ts +179 -24
- package/src/frozen/Frozen.ts +50 -33
- package/src/model/BaseModel.ts +15 -14
- package/src/model/newModel.ts +32 -28
- package/src/model/utils.ts +2 -2
- package/src/modelShared/modelClassInitializer.ts +3 -2
- package/src/modelShared/modelDecorator.ts +34 -14
- package/src/modelShared/modelInfo.ts +1 -1
- package/src/modelShared/prop.ts +6 -9
- package/src/modelShared/sharedInternalModel.ts +16 -3
- package/src/parent/detach.ts +1 -1
- package/src/parent/findChildren.ts +4 -7
- package/src/parent/findParent.ts +14 -9
- package/src/parent/onChildAttachedTo.ts +45 -40
- package/src/parent/path.ts +11 -7
- package/src/parent/setParent.ts +33 -14
- package/src/parent/walkTree.ts +21 -37
- package/src/patch/applyPatches.ts +75 -60
- package/src/patch/emitPatch.ts +142 -26
- package/src/patch/jsonPatch.ts +5 -0
- package/src/patch/patchRecorder.ts +18 -14
- package/src/redux/connectReduxDevTools.ts +24 -18
- package/src/redux/redux.ts +5 -21
- package/src/ref/core.ts +18 -2
- package/src/rootStore/attachDetach.ts +27 -16
- package/src/rootStore/rootStore.ts +10 -6
- package/src/snapshot/SnapshotOf.ts +2 -2
- package/src/snapshot/applySnapshot.ts +10 -1
- package/src/snapshot/clone.ts +1 -2
- package/src/snapshot/cloneTreeValue.ts +1 -2
- package/src/snapshot/fromModelSnapshot.ts +6 -2
- package/src/snapshot/fromSnapshot.ts +3 -1
- package/src/snapshot/internal.ts +22 -9
- package/src/snapshot/modelInitialData.ts +14 -19
- package/src/snapshot/onSnapshot.ts +12 -1
- package/src/snapshot/reconcileModelSnapshot.ts +16 -8
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +5 -4
- package/src/snapshot/reconcileSnapshot.ts +13 -4
- package/src/standardActions/arrayActions.ts +12 -2
- package/src/standardActions/objectActions.ts +3 -2
- package/src/transforms/ImmutableDate.ts +4 -0
- package/src/transforms/asMap.ts +3 -1
- package/src/treeUtils/deepEquals.ts +15 -13
- package/src/treeUtils/draft.ts +8 -0
- package/src/treeUtils/sandbox.ts +31 -27
- package/src/tweaker/core.ts +2 -4
- package/src/tweaker/tweak.ts +8 -8
- package/src/tweaker/tweakArray.ts +86 -37
- package/src/tweaker/tweakFrozen.ts +0 -1
- package/src/tweaker/tweakModel.ts +0 -1
- package/src/tweaker/tweakPlainObject.ts +78 -29
- package/src/tweaker/typeChecking.ts +132 -33
- package/src/types/TypeCheckError.ts +17 -19
- package/src/types/createCachedTypeCheck.ts +27 -2
- package/src/types/objectBased/typesDataModelData.ts +4 -2
- package/src/types/objectBased/typesModel.ts +4 -2
- package/src/types/objectBased/typesObject.ts +20 -16
- package/src/types/objectBased/typesRecord.ts +6 -13
- package/src/types/objectBased/typesRef.ts +2 -2
- package/src/types/primitiveBased/typesEnum.ts +4 -2
- package/src/types/primitiveBased/typesPrimitive.ts +9 -4
- package/src/types/typeCheckErrorUtils.ts +1 -0
- package/src/types/utility/typesCodecSupport.ts +97 -56
- package/src/types/utility/typesMap.ts +24 -41
- package/src/types/utility/typesRefinement.ts +3 -2
- package/src/types/utility/typesSet.ts +24 -0
- package/src/utils/ModelPool.ts +3 -3
- package/src/utils/chainFns.ts +4 -0
- package/src/utils/decorators.ts +1 -1
- package/src/utils/errorDiagnostics.ts +6 -2
- package/src/utils/forEachWithDelayedThrow.ts +83 -0
- package/src/utils/index.ts +34 -2
- package/src/utils/mapUtils.ts +3 -7
- package/src/utils/setIfDifferent.ts +3 -2
- package/src/utils/setLike.ts +134 -0
- package/src/wrappers/ArraySet.ts +5 -9
- package/src/wrappers/ObjectMap.ts +15 -11
- package/src/wrappers/asMap.ts +30 -18
- package/src/wrappers/asSet.ts +36 -12
- package/dist/types/action/modelFlowPromiseGenerator.d.ts +0 -1
- package/src/action/modelFlowPromiseGenerator.ts +0 -127
|
@@ -253,15 +253,13 @@ export function actionTrackingMiddleware(
|
|
|
253
253
|
state: State.Finished,
|
|
254
254
|
})
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
256
|
+
try {
|
|
257
|
+
return hooks.onFinish?.(simpleCtx, ret) || ret
|
|
258
|
+
} finally {
|
|
259
|
+
if (parentResumed) {
|
|
260
|
+
suspend(parentCtx!)
|
|
261
|
+
}
|
|
262
262
|
}
|
|
263
|
-
|
|
264
|
-
return ret
|
|
265
263
|
}
|
|
266
264
|
|
|
267
265
|
const resume = (simpleCtx: SimpleActionContext, real: boolean) => {
|
|
@@ -323,10 +321,11 @@ export function actionTrackingMiddleware(
|
|
|
323
321
|
retObj = finish(simpleCtx, retObj)
|
|
324
322
|
} else {
|
|
325
323
|
try {
|
|
326
|
-
retObj =
|
|
324
|
+
retObj = { result: ActionTrackingResult.Return, value: next() }
|
|
327
325
|
} catch (err) {
|
|
328
|
-
retObj =
|
|
326
|
+
retObj = { result: ActionTrackingResult.Throw, value: err }
|
|
329
327
|
}
|
|
328
|
+
retObj = finish(simpleCtx, retObj)
|
|
330
329
|
}
|
|
331
330
|
|
|
332
331
|
return returnOrThrowActionTrackingReturn(retObj)
|
|
@@ -409,9 +408,8 @@ const simpleDataContextSymbol = Symbol("simpleDataContext")
|
|
|
409
408
|
* @returns Simplified action context.
|
|
410
409
|
*/
|
|
411
410
|
export function simplifyActionContext(ctx: ActionContext): SimpleActionContext {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
411
|
+
// every async step points to the step that spawned its flow (the spawn step points to itself)
|
|
412
|
+
ctx = ctx.spawnAsyncStepContext ?? ctx
|
|
415
413
|
|
|
416
414
|
let simpleCtx = ctx.data[simpleDataContextSymbol]
|
|
417
415
|
if (!simpleCtx) {
|
|
@@ -4,8 +4,10 @@ import { assertIsModel } from "../model/utils"
|
|
|
4
4
|
import { addModelClassInitializer } from "../modelShared/modelClassInitializer"
|
|
5
5
|
import { applyPatches } from "../patch"
|
|
6
6
|
import { internalPatchRecorder, type PatchRecorder } from "../patch/patchRecorder"
|
|
7
|
-
import {
|
|
7
|
+
import { withTypeCheckingBatch } from "../tweaker/typeChecking"
|
|
8
|
+
import { assertIsObject, failure, MobxKeystoneAggregateError } from "../utils"
|
|
8
9
|
import { checkDecoratorContext } from "../utils/decorators"
|
|
10
|
+
import { pushDelayedError } from "../utils/forEachWithDelayedThrow"
|
|
9
11
|
import {
|
|
10
12
|
ActionTrackingResult,
|
|
11
13
|
actionTrackingMiddleware,
|
|
@@ -36,11 +38,15 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
36
38
|
|
|
37
39
|
const patchRecorderSymbol = Symbol("patchRecorder")
|
|
38
40
|
function initPatchRecorder(ctx: SimpleActionContext) {
|
|
39
|
-
ctx.rootContext.data[patchRecorderSymbol] =
|
|
40
|
-
recording: false,
|
|
41
|
-
|
|
41
|
+
ctx.rootContext.data[patchRecorderSymbol] = {
|
|
42
|
+
recorder: internalPatchRecorder(undefined, { recording: false }),
|
|
43
|
+
activeCount: 0,
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
|
-
function
|
|
46
|
+
function getPatchRecorderData(ctx: SimpleActionContext): {
|
|
47
|
+
recorder: PatchRecorder
|
|
48
|
+
activeCount: number
|
|
49
|
+
} {
|
|
44
50
|
return ctx.rootContext.data[patchRecorderSymbol]
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -55,23 +61,48 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
55
61
|
initPatchRecorder(ctx)
|
|
56
62
|
}
|
|
57
63
|
},
|
|
64
|
+
// Actions nest, so recording must only stop once every action of the transaction
|
|
65
|
+
// has been suspended, not as soon as the innermost one returns.
|
|
58
66
|
onResume(ctx) {
|
|
59
|
-
|
|
67
|
+
const data = getPatchRecorderData(ctx)
|
|
68
|
+
data.activeCount++
|
|
69
|
+
data.recorder.recording = true
|
|
60
70
|
},
|
|
61
71
|
onSuspend(ctx) {
|
|
62
|
-
|
|
72
|
+
const data = getPatchRecorderData(ctx)
|
|
73
|
+
data.activeCount--
|
|
74
|
+
data.recorder.recording = data.activeCount > 0
|
|
63
75
|
},
|
|
64
76
|
onFinish(ctx, ret) {
|
|
65
77
|
if (ctx === ctx.rootContext) {
|
|
66
|
-
const patchRecorder =
|
|
78
|
+
const patchRecorder = getPatchRecorderData(ctx).recorder
|
|
67
79
|
|
|
68
80
|
try {
|
|
69
81
|
if (ret.result === ActionTrackingResult.Throw) {
|
|
70
82
|
// undo changes (backwards for inverse patches)
|
|
71
83
|
const { events } = patchRecorder
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
const rollbackErrors: unknown[] = []
|
|
85
|
+
withTypeCheckingBatch(() => {
|
|
86
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
87
|
+
const event = events[i]
|
|
88
|
+
// A listener may throw after a patch has already been applied.
|
|
89
|
+
// Continue at patch granularity so one failure cannot skip the
|
|
90
|
+
// remaining restoration, including patches from the same edit.
|
|
91
|
+
for (let j = event.inversePatches.length - 1; j >= 0; j--) {
|
|
92
|
+
try {
|
|
93
|
+
applyPatches(event.target, [event.inversePatches[j]])
|
|
94
|
+
} catch (error) {
|
|
95
|
+
pushDelayedError(rollbackErrors, error)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
if (rollbackErrors.length > 0) {
|
|
101
|
+
// Keep the action error first so the reason for the rollback is not lost.
|
|
102
|
+
throw new MobxKeystoneAggregateError(
|
|
103
|
+
[ret.value, ...rollbackErrors],
|
|
104
|
+
"transaction rollback callbacks failed"
|
|
105
|
+
)
|
|
75
106
|
}
|
|
76
107
|
}
|
|
77
108
|
} finally {
|
|
@@ -19,6 +19,16 @@ import {
|
|
|
19
19
|
} from "../utils"
|
|
20
20
|
import { actionTrackingMiddleware, type SimpleActionContext } from "./actionTrackingMiddleware"
|
|
21
21
|
|
|
22
|
+
function checkMaxLevels(value: number | undefined, name: string): void {
|
|
23
|
+
if (
|
|
24
|
+
value !== undefined &&
|
|
25
|
+
value !== Number.POSITIVE_INFINITY &&
|
|
26
|
+
(!Number.isInteger(value) || value < 0)
|
|
27
|
+
) {
|
|
28
|
+
throw failure(`${name} must be a non-negative integer or Infinity`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
/**
|
|
23
33
|
* An undo/redo event without attached state.
|
|
24
34
|
*/
|
|
@@ -99,21 +109,16 @@ export interface UndoEventGroup {
|
|
|
99
109
|
|
|
100
110
|
function toSingleEvents(
|
|
101
111
|
event: UndoEventWithoutAttachedState,
|
|
102
|
-
|
|
103
|
-
):
|
|
112
|
+
array: UndoSingleEvent[] = []
|
|
113
|
+
): UndoSingleEvent[] {
|
|
104
114
|
if (event.type === UndoEventType.Single) {
|
|
105
|
-
|
|
115
|
+
array.push(event)
|
|
106
116
|
} else {
|
|
107
|
-
const array: UndoSingleEvent[] = []
|
|
108
117
|
for (const e of event.events) {
|
|
109
|
-
|
|
110
|
-
array.unshift(...toSingleEvents(e, true))
|
|
111
|
-
} else {
|
|
112
|
-
array.push(...toSingleEvents(e, false))
|
|
113
|
-
}
|
|
118
|
+
toSingleEvents(e, array)
|
|
114
119
|
}
|
|
115
|
-
return array
|
|
116
120
|
}
|
|
121
|
+
return array
|
|
117
122
|
}
|
|
118
123
|
|
|
119
124
|
/**
|
|
@@ -156,15 +161,13 @@ export class UndoStore extends Model({
|
|
|
156
161
|
maxUndoLevels?: number
|
|
157
162
|
maxRedoLevels?: number
|
|
158
163
|
}) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
checkMaxLevels(maxUndoLevels, "maxUndoLevels")
|
|
165
|
+
checkMaxLevels(maxRedoLevels, "maxRedoLevels")
|
|
166
|
+
if (maxUndoLevels !== undefined && this.undoEvents.length > maxUndoLevels) {
|
|
167
|
+
this.undoEvents.splice(0, this.undoEvents.length - maxUndoLevels)
|
|
163
168
|
}
|
|
164
|
-
if (maxRedoLevels !== undefined) {
|
|
165
|
-
|
|
166
|
-
this.redoEvents.shift()
|
|
167
|
-
}
|
|
169
|
+
if (maxRedoLevels !== undefined && this.redoEvents.length > maxRedoLevels) {
|
|
170
|
+
this.redoEvents.splice(0, this.redoEvents.length - maxRedoLevels)
|
|
168
171
|
}
|
|
169
172
|
}
|
|
170
173
|
|
|
@@ -266,10 +269,16 @@ export class UndoStore extends Model({
|
|
|
266
269
|
running = true
|
|
267
270
|
},
|
|
268
271
|
end: () => {
|
|
272
|
+
if (ended) {
|
|
273
|
+
throw failure("cannot end a group when it is already ended")
|
|
274
|
+
}
|
|
269
275
|
if (running) {
|
|
270
276
|
api.pause()
|
|
271
277
|
}
|
|
272
278
|
ended = true
|
|
279
|
+
if (group.events.length === 0) {
|
|
280
|
+
return
|
|
281
|
+
}
|
|
273
282
|
if (parentGroup) {
|
|
274
283
|
this._addUndoToParentGroup(parentGroup, group)
|
|
275
284
|
} else {
|
|
@@ -386,12 +395,14 @@ export class UndoManager {
|
|
|
386
395
|
const event = this.undoQueue[this.undoQueue.length - 1]
|
|
387
396
|
|
|
388
397
|
withoutUndo(() => {
|
|
389
|
-
toSingleEvents(event
|
|
390
|
-
|
|
391
|
-
|
|
398
|
+
toSingleEvents(event)
|
|
399
|
+
.reverse()
|
|
400
|
+
.forEach((e) => {
|
|
401
|
+
applyPatches(this.subtreeRoot, e.inversePatches, true)
|
|
402
|
+
})
|
|
392
403
|
|
|
393
404
|
// restore the attached state before the operation was made
|
|
394
|
-
if (event.attachedState
|
|
405
|
+
if (event.attachedState) {
|
|
395
406
|
this.options?.attachedState?.restore(event.attachedState.beforeEvent)
|
|
396
407
|
}
|
|
397
408
|
})
|
|
@@ -411,12 +422,12 @@ export class UndoManager {
|
|
|
411
422
|
const event = this.redoQueue[this.redoQueue.length - 1]
|
|
412
423
|
|
|
413
424
|
withoutUndo(() => {
|
|
414
|
-
toSingleEvents(event
|
|
425
|
+
toSingleEvents(event).forEach((e) => {
|
|
415
426
|
applyPatches(this.subtreeRoot, e.patches)
|
|
416
427
|
})
|
|
417
428
|
|
|
418
429
|
// restore the attached state after the operation was made
|
|
419
|
-
if (event.attachedState
|
|
430
|
+
if (event.attachedState) {
|
|
420
431
|
this.options?.attachedState?.restore(event.attachedState.afterEvent)
|
|
421
432
|
}
|
|
422
433
|
})
|
|
@@ -589,14 +600,14 @@ export class UndoManager {
|
|
|
589
600
|
function next(ret: any): void {
|
|
590
601
|
if (ret && typeof ret.then === "function") {
|
|
591
602
|
// an async iterator
|
|
592
|
-
ret.then(next, reject)
|
|
603
|
+
Promise.resolve(ret).then(next, reject).catch(reject)
|
|
593
604
|
} else if (ret.done) {
|
|
594
605
|
// done
|
|
595
606
|
group.end()
|
|
596
607
|
resolve(ret.value)
|
|
597
608
|
} else {
|
|
598
609
|
// continue
|
|
599
|
-
Promise.resolve(ret.value).then(onFulfilled, onRejected)
|
|
610
|
+
Promise.resolve(ret.value).then(onFulfilled, onRejected).catch(reject)
|
|
600
611
|
}
|
|
601
612
|
}
|
|
602
613
|
|
|
@@ -678,6 +689,8 @@ export function undoMiddleware<S>(
|
|
|
678
689
|
options?: UndoMiddlewareOptions<S>
|
|
679
690
|
): UndoManager {
|
|
680
691
|
assertTweakedObject(subtreeRoot, "subtreeRoot")
|
|
692
|
+
checkMaxLevels(options?.maxUndoLevels, "maxUndoLevels")
|
|
693
|
+
checkMaxLevels(options?.maxRedoLevels, "maxRedoLevels")
|
|
681
694
|
|
|
682
695
|
let manager: UndoManager
|
|
683
696
|
|
|
@@ -694,6 +707,7 @@ export function undoMiddleware<S>(
|
|
|
694
707
|
function initPatchRecorder(ctx: SimpleActionContext) {
|
|
695
708
|
const group = manager.store._currentGroup
|
|
696
709
|
|
|
710
|
+
const attachedStateBeforeEvent = options?.attachedState?.save()
|
|
697
711
|
const patchRecorderData: PatchRecorderData = {
|
|
698
712
|
recorder: patchRecorder(subtreeRoot, {
|
|
699
713
|
recording: false,
|
|
@@ -705,7 +719,7 @@ export function undoMiddleware<S>(
|
|
|
705
719
|
undoRootContext: ctx,
|
|
706
720
|
group,
|
|
707
721
|
|
|
708
|
-
attachedStateBeforeEvent
|
|
722
|
+
attachedStateBeforeEvent,
|
|
709
723
|
}
|
|
710
724
|
|
|
711
725
|
ctx.rootContext.data[patchRecorderSymbol] = patchRecorderData
|
|
@@ -736,42 +750,44 @@ export function undoMiddleware<S>(
|
|
|
736
750
|
if (patchRecorderData && patchRecorderData.undoRootContext === ctx) {
|
|
737
751
|
const patchRecorder = patchRecorderData.recorder
|
|
738
752
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
753
|
+
try {
|
|
754
|
+
if (patchRecorder.events.length > 0) {
|
|
755
|
+
const patches: Patch[] = []
|
|
756
|
+
const inversePatches: Patch[] = []
|
|
757
|
+
|
|
758
|
+
for (const event of patchRecorder.events) {
|
|
759
|
+
for (const patch of event.patches) patches.push(patch)
|
|
760
|
+
for (const patch of event.inversePatches) inversePatches.push(patch)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const event = {
|
|
764
|
+
type: UndoEventType.Single,
|
|
765
|
+
targetPath: fastGetRootPath(ctx.target, false).path,
|
|
766
|
+
actionName: ctx.actionName,
|
|
767
|
+
patches,
|
|
768
|
+
inversePatches,
|
|
769
|
+
} as const
|
|
770
|
+
|
|
771
|
+
const parentGroup = patchRecorderData.group
|
|
772
|
+
|
|
773
|
+
if (parentGroup) {
|
|
774
|
+
manager.store._addUndoToParentGroup(parentGroup, event)
|
|
775
|
+
} else {
|
|
776
|
+
manager.store._addUndo({
|
|
777
|
+
event: {
|
|
778
|
+
...event,
|
|
779
|
+
attachedState: {
|
|
780
|
+
beforeEvent: patchRecorderData.attachedStateBeforeEvent,
|
|
781
|
+
afterEvent: options?.attachedState?.save(),
|
|
782
|
+
},
|
|
767
783
|
},
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
}
|
|
784
|
+
maxUndoLevels: options?.maxUndoLevels,
|
|
785
|
+
})
|
|
786
|
+
}
|
|
771
787
|
}
|
|
788
|
+
} finally {
|
|
789
|
+
patchRecorder.dispose()
|
|
772
790
|
}
|
|
773
|
-
|
|
774
|
-
patchRecorder.dispose()
|
|
775
791
|
}
|
|
776
792
|
},
|
|
777
793
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { modelIdKey } from "../model/metadata"
|
|
2
2
|
import { isModel } from "../model/utils"
|
|
3
|
+
import { modelToDataNode } from "../parent/core"
|
|
3
4
|
import type { RootPath } from "../parent/path"
|
|
4
5
|
import type { Path } from "../parent/pathTypes"
|
|
5
6
|
|
|
@@ -26,7 +27,7 @@ export function pathToTargetPathIds(root: any, path: Path): (string | null)[] {
|
|
|
26
27
|
let current = root // we don't care about the root ID
|
|
27
28
|
|
|
28
29
|
for (let i = 0; i < path.length; i++) {
|
|
29
|
-
current = current[path[i]]
|
|
30
|
+
current = modelToDataNode(current)[path[i]]
|
|
30
31
|
const targetObjId = isModel(current) ? (current[modelIdKey] ?? null) : null
|
|
31
32
|
targetPathIds.push(targetObjId)
|
|
32
33
|
}
|
|
@@ -5,7 +5,12 @@ import { isDataModelClass } from "../dataModel/utils"
|
|
|
5
5
|
import { isModelClass } from "../model/utils"
|
|
6
6
|
import { isTreeNode } from "../tweaker/core"
|
|
7
7
|
import { tweak } from "../tweaker/tweak"
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
addLateInitializationFunction,
|
|
10
|
+
failure,
|
|
11
|
+
isEqualOrBothNaN,
|
|
12
|
+
runBeforeOnInitSymbol,
|
|
13
|
+
} from "../utils"
|
|
9
14
|
import { checkDecoratorContext, copyFunctionMetadata } from "../utils/decorators"
|
|
10
15
|
import { getOrCreate } from "../utils/mapUtils"
|
|
11
16
|
|
|
@@ -54,14 +59,20 @@ export function computedTree(...args: any[]): any {
|
|
|
54
59
|
const oldValue = entry.value
|
|
55
60
|
const newValue = entry.computed.get()
|
|
56
61
|
|
|
57
|
-
if (oldValue
|
|
62
|
+
if (isEqualOrBothNaN(oldValue, newValue)) {
|
|
58
63
|
return entry.tweakedValue
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
const oldTweakedValue = entry.tweakedValue
|
|
62
67
|
tweak(oldTweakedValue, undefined)
|
|
63
68
|
|
|
64
|
-
|
|
69
|
+
let tweakedValue: unknown
|
|
70
|
+
try {
|
|
71
|
+
tweakedValue = tweakComputedTreeNode(newValue, this, propertyKey)
|
|
72
|
+
} catch (error) {
|
|
73
|
+
entry.tweakedValue = tweakComputedTreeNode(oldTweakedValue, this, propertyKey)
|
|
74
|
+
throw error
|
|
75
|
+
}
|
|
65
76
|
entry.value = newValue
|
|
66
77
|
entry.tweakedValue = tweakedValue
|
|
67
78
|
return tweakedValue
|
package/src/context/context.ts
CHANGED
|
@@ -120,56 +120,28 @@ class ContextClass<T> implements Context<T> {
|
|
|
120
120
|
this.nodeAtom.get(node)?.reportChanged()
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
get(node: object): T {
|
|
124
|
+
const provider = this.getProviderNode(node)
|
|
125
|
+
if (provider) {
|
|
126
|
+
return resolveContextValue(this.nodeContextValue.get(provider)!)
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
return resolveContextValue(obsForNode)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const parent = fastGetParent(node, useAtom)
|
|
134
|
-
if (!parent) {
|
|
135
|
-
const overrideValue = this.overrideContextValue.get()
|
|
136
|
-
if (overrideValue) {
|
|
137
|
-
return resolveContextValue(overrideValue)
|
|
138
|
-
}
|
|
139
|
-
return this.getDefault()
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return this.fastGet(parent, useAtom)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
get(node: object) {
|
|
146
|
-
assertTweakedObject(node, "node")
|
|
147
|
-
|
|
148
|
-
return this.fastGet(node, true)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private fastGetProviderNode(node: object, useAtom: boolean): object | undefined {
|
|
152
|
-
if (useAtom) {
|
|
153
|
-
this.reportNodeAtomObserved(node)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const obsForNode = this.nodeContextValue.get(node)
|
|
157
|
-
if (obsForNode) {
|
|
158
|
-
return node
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const parent = fastGetParent(node, useAtom)
|
|
162
|
-
if (!parent) {
|
|
163
|
-
return undefined
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return this.fastGetProviderNode(parent, useAtom)
|
|
129
|
+
const overrideValue = this.overrideContextValue.get()
|
|
130
|
+
return overrideValue ? resolveContextValue(overrideValue) : this.getDefault()
|
|
167
131
|
}
|
|
168
132
|
|
|
169
133
|
getProviderNode(node: object): object | undefined {
|
|
170
134
|
assertTweakedObject(node, "node")
|
|
171
135
|
|
|
172
|
-
|
|
136
|
+
let current: object | undefined = node
|
|
137
|
+
while (current) {
|
|
138
|
+
this.reportNodeAtomObserved(current)
|
|
139
|
+
if (this.nodeContextValue.has(current)) {
|
|
140
|
+
return current
|
|
141
|
+
}
|
|
142
|
+
current = fastGetParent(current, true)
|
|
143
|
+
}
|
|
144
|
+
return undefined
|
|
173
145
|
}
|
|
174
146
|
|
|
175
147
|
getDefault(): T {
|
|
@@ -227,7 +199,7 @@ class ContextClass<T> implements Context<T> {
|
|
|
227
199
|
|
|
228
200
|
try {
|
|
229
201
|
const ret = fn()
|
|
230
|
-
if (isTweakedObject(ret,
|
|
202
|
+
if (isTweakedObject(ret, false)) {
|
|
231
203
|
this.set(ret, value)
|
|
232
204
|
}
|
|
233
205
|
return ret
|
|
@@ -247,7 +219,7 @@ class ContextClass<T> implements Context<T> {
|
|
|
247
219
|
|
|
248
220
|
try {
|
|
249
221
|
const ret = fn()
|
|
250
|
-
if (isTweakedObject(ret,
|
|
222
|
+
if (isTweakedObject(ret, false)) {
|
|
251
223
|
this._setComputed(ret, computedValueFn)
|
|
252
224
|
}
|
|
253
225
|
return ret
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { propsTypeSymbol } from "../modelShared/BaseModelShared"
|
|
2
2
|
import { modelInfoByClass } from "../modelShared/modelInfo"
|
|
3
3
|
import { getInternalModelClassPropsInfo } from "../modelShared/modelPropsInfo"
|
|
4
4
|
import {
|
|
@@ -15,14 +15,13 @@ import { toTreeNode } from "../tweaker/tweak"
|
|
|
15
15
|
import { typesDataModelData } from "../types/objectBased/typesDataModelData"
|
|
16
16
|
import type { TypeCheckError } from "../types/TypeCheckError"
|
|
17
17
|
import { typeCheck } from "../types/typeCheck"
|
|
18
|
-
import { clonePlainObject, failure, isObject } from "../utils"
|
|
18
|
+
import { clonePlainObject, failure, hasOwnProp, isObject, setProtoProp } from "../utils"
|
|
19
19
|
import { getOrCreate } from "../utils/mapUtils"
|
|
20
20
|
import type { DataModelConstructorOptions } from "./DataModelConstructorOptions"
|
|
21
|
+
import { dataModelInstanceCache } from "./dataModelInstanceCache"
|
|
21
22
|
import { getDataModelMetadata } from "./getDataModelMetadata"
|
|
22
23
|
import { internalNewDataModel } from "./newDataModel"
|
|
23
24
|
|
|
24
|
-
const dataModelInstanceCache = new WeakMap<ModelClass<AnyDataModel>, WeakMap<any, AnyDataModel>>()
|
|
25
|
-
|
|
26
25
|
/**
|
|
27
26
|
* Base abstract class for data models. Use `DataModel` instead when extending.
|
|
28
27
|
*
|
|
@@ -101,7 +100,8 @@ export abstract class BaseDataModel<TProps extends ModelProps> {
|
|
|
101
100
|
const k = modelPropsKeys[i]
|
|
102
101
|
const propData = modelProps[k]
|
|
103
102
|
|
|
104
|
-
|
|
103
|
+
const hasInitialValue = hasOwnProp(initialData, k)
|
|
104
|
+
let newValue = hasInitialValue ? initialData[k] : undefined
|
|
105
105
|
let changed = false
|
|
106
106
|
|
|
107
107
|
// apply untransform (if any)
|
|
@@ -117,11 +117,15 @@ export abstract class BaseDataModel<TProps extends ModelProps> {
|
|
|
117
117
|
if (defaultValue !== noDefaultValue) {
|
|
118
118
|
changed = true
|
|
119
119
|
newValue = defaultValue
|
|
120
|
+
} else if (!hasInitialValue) {
|
|
121
|
+
// for mobx4, we need to set up properties even if they are undefined
|
|
122
|
+
changed = true
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
if (changed) {
|
|
124
|
-
|
|
127
|
+
if (k === "__proto__") setProtoProp(initialData, newValue)
|
|
128
|
+
else initialData[k] = newValue
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
@@ -144,24 +148,24 @@ export abstract class BaseDataModel<TProps extends ModelProps> {
|
|
|
144
148
|
|
|
145
149
|
Object.setPrototypeOf(this, modelClass.prototype)
|
|
146
150
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
try {
|
|
152
|
+
internalNewDataModel(this, tweakedData as any, {
|
|
153
|
+
modelClass,
|
|
154
|
+
})
|
|
155
|
+
} catch (error) {
|
|
156
|
+
instancesForModelClass.delete(tweakedData)
|
|
157
|
+
throw error
|
|
158
|
+
}
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
toString(options?: { withData?: boolean }) {
|
|
153
|
-
const
|
|
154
|
-
withData: true,
|
|
155
|
-
...options,
|
|
156
|
-
}
|
|
162
|
+
const { withData = true } = options ?? {}
|
|
157
163
|
|
|
158
164
|
const modelInfo = modelInfoByClass.get(this.constructor as any)
|
|
159
165
|
|
|
160
166
|
const firstPart = `${this.constructor.name}#${modelInfo!.name}`
|
|
161
167
|
|
|
162
|
-
return
|
|
163
|
-
? `[${firstPart} ${JSON.stringify(getSnapshot(this))}]`
|
|
164
|
-
: `[${firstPart}]`
|
|
168
|
+
return withData ? `[${firstPart} ${JSON.stringify(getSnapshot(this.$))}]` : `[${firstPart}]`
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
171
|
|