mobx-keystone 1.25.0 → 2.1.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 +145 -0
- package/dist/mobx-keystone.esm.js +3627 -1540
- package/dist/mobx-keystone.esm.mjs +3627 -1540
- package/dist/mobx-keystone.umd.js +3638 -1543
- package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
- package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +17 -10
- package/dist/types/dataModel/dataModelInstanceCache.d.ts +1 -0
- package/dist/types/deepChange/onDeepChange.d.ts +9 -0
- package/dist/types/parent/arrayPaths.d.ts +1 -0
- package/dist/types/parent/detach.d.ts +1 -1
- package/dist/types/parent/findChildren.d.ts +1 -1
- package/dist/types/parent/treeOrder.d.ts +1 -0
- package/dist/types/patch/applyPatches.d.ts +3 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +2 -1
- package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
- package/dist/types/ref/nodesById.d.ts +1 -0
- package/dist/types/ref/refIdIndex.d.ts +1 -0
- package/dist/types/snapshot/SnapshotOf.d.ts +2 -2
- package/dist/types/snapshot/applySnapshot.d.ts +1 -0
- package/dist/types/snapshot/fromSnapshot.d.ts +0 -3
- package/dist/types/standardActions/arrayActions.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +0 -4
- package/dist/types/tweaker/changeRollback.d.ts +1 -0
- package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
- package/dist/types/types/TypeCheckError.d.ts +1 -0
- package/dist/types/types/utility/typesRefinement.d.ts +2 -2
- package/dist/types/utils/ModelPool.d.ts +7 -1
- package/dist/types/utils/chunks.d.ts +1 -0
- 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 +2 -2
- 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 +55 -25
- package/src/actionMiddlewares/undoMiddleware.ts +112 -59
- 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 +10 -13
- package/src/model/newModel.ts +21 -24
- package/src/model/utils.ts +2 -2
- package/src/modelShared/modelClassInitializer.ts +3 -2
- package/src/modelShared/modelDecorator.ts +39 -39
- package/src/modelShared/modelInfo.ts +1 -1
- package/src/modelShared/prop.ts +6 -9
- package/src/modelShared/sharedInternalModel.ts +16 -3
- package/src/parent/arrayPaths.ts +242 -0
- package/src/parent/coreObjectChildren.ts +300 -48
- package/src/parent/detach.ts +1 -1
- package/src/parent/findChildren.ts +4 -7
- package/src/parent/onChildAttachedTo.ts +157 -55
- package/src/parent/path.ts +15 -5
- package/src/parent/setParent.ts +34 -15
- package/src/parent/treeOrder.ts +53 -0
- package/src/parent/walkTree.ts +12 -101
- package/src/patch/applyPatches.ts +74 -34
- 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 +81 -29
- package/src/ref/modelRefIdIndex.ts +131 -0
- package/src/ref/nodesById.ts +60 -0
- package/src/ref/refIdIndex.ts +209 -0
- 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 +27 -19
- package/src/snapshot/clone.ts +1 -2
- package/src/snapshot/cloneTreeValue.ts +1 -2
- package/src/snapshot/fromModelSnapshot.ts +6 -2
- package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
- package/src/snapshot/fromSnapshot.ts +2 -6
- package/src/snapshot/internal.ts +89 -19
- package/src/snapshot/onSnapshot.ts +12 -1
- package/src/snapshot/reconcileModelSnapshot.ts +6 -6
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +5 -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/changeRollback.ts +218 -0
- package/src/tweaker/core.ts +33 -22
- package/src/tweaker/treeNodeMetadata.ts +12 -2
- package/src/tweaker/tweak.ts +183 -19
- package/src/tweaker/tweakArray.ts +176 -33
- package/src/tweaker/tweakPlainObject.ts +117 -39
- package/src/tweaker/tweakedChangeListeners.ts +61 -0
- package/src/tweaker/typeChecking.ts +25 -15
- package/src/types/TypeCheckError.ts +17 -19
- package/src/types/arrayBased/typesArray.ts +10 -11
- package/src/types/createCachedTypeCheck.ts +541 -88
- 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 +8 -18
- 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 +78 -22
- package/src/utils/chainFns.ts +4 -0
- package/src/utils/chunks.ts +271 -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 +71 -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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { runInAction } from "mobx"
|
|
2
2
|
import { applyAction } from "../../action/applyAction"
|
|
3
3
|
import { frozenKey } from "../../frozen/Frozen"
|
|
4
|
+
import type { AnyModel } from "../../model/BaseModel"
|
|
4
5
|
import { getModelIdPropertyName } from "../../model/getModelMetadata"
|
|
5
6
|
import { isModel } from "../../model/utils"
|
|
6
|
-
import { resolvePath } from "../../parent/path"
|
|
7
|
+
import { fastGetParentToChildPath, resolvePath } from "../../parent/path"
|
|
7
8
|
import type { WritablePath } from "../../parent/pathTypes"
|
|
8
9
|
import { applyPatches } from "../../patch/applyPatches"
|
|
9
10
|
import { onPatches } from "../../patch/emitPatch"
|
|
@@ -53,16 +54,28 @@ export function applySerializedActionAndTrackNewModelIds<TRet = any>(
|
|
|
53
54
|
|
|
54
55
|
const deserializedCall = deserializeActionCall(call, subtreeRoot)
|
|
55
56
|
|
|
56
|
-
const
|
|
57
|
+
const modelsWithChangedIds = new Map<AnyModel, string>()
|
|
57
58
|
|
|
58
59
|
// set a patch listener to track changes to model ids
|
|
59
60
|
const patchDisposer = onPatches(subtreeRoot, (patches) => {
|
|
60
|
-
scanPatchesForModelIdChanges(subtreeRoot,
|
|
61
|
+
scanPatchesForModelIdChanges(subtreeRoot, modelsWithChangedIds, patches)
|
|
61
62
|
})
|
|
62
63
|
|
|
63
64
|
try {
|
|
64
65
|
const returnValue = applyAction(subtreeRoot, deserializedCall)
|
|
65
66
|
|
|
67
|
+
const modelIdOverrides: Patch[] = []
|
|
68
|
+
for (const [model, idProperty] of modelsWithChangedIds) {
|
|
69
|
+
const path = fastGetParentToChildPath(subtreeRoot, model, false)
|
|
70
|
+
if (path) {
|
|
71
|
+
modelIdOverrides.push({
|
|
72
|
+
op: "replace",
|
|
73
|
+
path: [...path, idProperty],
|
|
74
|
+
value: model.$[idProperty],
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
return {
|
|
67
80
|
returnValue,
|
|
68
81
|
serializedActionCall: {
|
|
@@ -75,24 +88,23 @@ export function applySerializedActionAndTrackNewModelIds<TRet = any>(
|
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
|
|
78
|
-
function scanPatchesForModelIdChanges(
|
|
91
|
+
function scanPatchesForModelIdChanges(
|
|
92
|
+
root: object,
|
|
93
|
+
modelsWithChangedIds: Map<AnyModel, string>,
|
|
94
|
+
patches: Patch[]
|
|
95
|
+
) {
|
|
79
96
|
const len = patches.length
|
|
80
97
|
for (let i = 0; i < len; i++) {
|
|
81
98
|
const patch = patches[i]
|
|
82
99
|
if (patch.op === "replace" || patch.op === "add") {
|
|
83
|
-
deepScanValueForModelIdChanges(
|
|
84
|
-
root,
|
|
85
|
-
modelIdOverrides,
|
|
86
|
-
patch.value,
|
|
87
|
-
patch.path as WritablePath
|
|
88
|
-
)
|
|
100
|
+
deepScanValueForModelIdChanges(root, modelsWithChangedIds, patch.value, patch.path.slice())
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
function deepScanValueForModelIdChanges(
|
|
94
106
|
root: object,
|
|
95
|
-
|
|
107
|
+
modelsWithChangedIds: Map<AnyModel, string>,
|
|
96
108
|
value: any,
|
|
97
109
|
path: WritablePath
|
|
98
110
|
) {
|
|
@@ -102,20 +114,17 @@ function deepScanValueForModelIdChanges(
|
|
|
102
114
|
|
|
103
115
|
if (isModel(parent)) {
|
|
104
116
|
const propertyName = path[path.length - 1]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
path: path.slice(),
|
|
110
|
-
value: value,
|
|
111
|
-
})
|
|
117
|
+
const idProperty = getModelIdPropertyName(parent.constructor as any)
|
|
118
|
+
if (idProperty !== undefined && propertyName === idProperty) {
|
|
119
|
+
// Resolve the final path and ID after the action, since this model may move or detach.
|
|
120
|
+
modelsWithChangedIds.set(parent, idProperty)
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
} else if (Array.isArray(value)) {
|
|
115
124
|
const len = value.length
|
|
116
125
|
for (let i = 0; i < len; i++) {
|
|
117
126
|
path.push(i)
|
|
118
|
-
deepScanValueForModelIdChanges(root,
|
|
127
|
+
deepScanValueForModelIdChanges(root, modelsWithChangedIds, value[i], path)
|
|
119
128
|
path.pop()
|
|
120
129
|
}
|
|
121
130
|
} else if (isObject(value)) {
|
|
@@ -128,7 +137,7 @@ function deepScanValueForModelIdChanges(
|
|
|
128
137
|
const propValue = value[propName]
|
|
129
138
|
|
|
130
139
|
path.push(propName)
|
|
131
|
-
deepScanValueForModelIdChanges(root,
|
|
140
|
+
deepScanValueForModelIdChanges(root, modelsWithChangedIds, propValue, path)
|
|
132
141
|
path.pop()
|
|
133
142
|
}
|
|
134
143
|
}
|
|
@@ -11,7 +11,7 @@ export const arraySerializer: ActionCallArgumentSerializer<any[] | IObservableAr
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// this will also transform observable arrays into non-observable ones
|
|
14
|
-
return
|
|
14
|
+
return Array.from(value, serialize)
|
|
15
15
|
},
|
|
16
16
|
|
|
17
17
|
deserialize(arr, deserialize) {
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { namespace } from "../../utils"
|
|
2
2
|
import { type ActionCallArgumentSerializer, cannotSerialize } from "./core"
|
|
3
3
|
|
|
4
|
-
export const dateSerializer: ActionCallArgumentSerializer<Date, number> = {
|
|
4
|
+
export const dateSerializer: ActionCallArgumentSerializer<Date, number | null> = {
|
|
5
5
|
id: `${namespace}/dateAsTimestamp`,
|
|
6
6
|
|
|
7
7
|
serialize(date) {
|
|
8
8
|
if (!(date instanceof Date)) {
|
|
9
9
|
return cannotSerialize
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
const timestamp = +date
|
|
12
|
+
return Number.isNaN(timestamp) ? null : timestamp
|
|
12
13
|
},
|
|
13
14
|
|
|
14
15
|
deserialize(timestamp) {
|
|
15
|
-
return new Date(timestamp)
|
|
16
|
+
return new Date(timestamp === null ? Number.NaN : timestamp)
|
|
16
17
|
},
|
|
17
18
|
}
|
|
@@ -15,13 +15,8 @@ export const mapSerializer: ActionCallArgumentSerializer<
|
|
|
15
15
|
|
|
16
16
|
const arr: [any, any][] = []
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
while (!cur.done) {
|
|
21
|
-
const k = cur.value
|
|
22
|
-
const v = map.get(k)
|
|
23
|
-
arr.push([serialize(k), serialize(v)])
|
|
24
|
-
cur = iter.next()
|
|
18
|
+
for (const [key, value] of map.entries()) {
|
|
19
|
+
arr.push([serialize(key), serialize(value)])
|
|
25
20
|
}
|
|
26
21
|
|
|
27
22
|
return arr
|
|
@@ -30,11 +25,8 @@ export const mapSerializer: ActionCallArgumentSerializer<
|
|
|
30
25
|
deserialize(arr, deserialize) {
|
|
31
26
|
const map = new Map()
|
|
32
27
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const k = arr[i][0]
|
|
36
|
-
const v = arr[i][1]
|
|
37
|
-
map.set(deserialize(k), deserialize(v))
|
|
28
|
+
for (const [key, value] of arr) {
|
|
29
|
+
map.set(deserialize(key), deserialize(value))
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
return map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { isObservableSet, type ObservableSet } from "mobx"
|
|
2
2
|
import { namespace } from "../../utils"
|
|
3
3
|
import { type ActionCallArgumentSerializer, cannotSerialize } from "./core"
|
|
4
4
|
|
|
@@ -6,21 +6,12 @@ export const setSerializer: ActionCallArgumentSerializer<Set<any> | ObservableSe
|
|
|
6
6
|
id: `${namespace}/setAsArray`,
|
|
7
7
|
|
|
8
8
|
serialize(set, serialize) {
|
|
9
|
-
if (!(set instanceof Set)) {
|
|
9
|
+
if (!(set instanceof Set || isObservableSet(set))) {
|
|
10
10
|
return cannotSerialize
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const iter = set.keys()
|
|
16
|
-
let cur = iter.next()
|
|
17
|
-
while (!cur.done) {
|
|
18
|
-
const k = cur.value
|
|
19
|
-
arr.push(serialize(k))
|
|
20
|
-
cur = iter.next()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return arr
|
|
13
|
+
// Array.from closes the iterator if an element cannot be serialized.
|
|
14
|
+
return Array.from(set.keys(), serialize)
|
|
24
15
|
},
|
|
25
16
|
|
|
26
17
|
deserialize(arr, deserialize) {
|
|
@@ -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) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { ActionMiddlewareDisposer } from "../action/middleware"
|
|
2
|
+
import { runUnprotected } from "../action/runUnprotected"
|
|
2
3
|
import type { AnyModel } from "../model/BaseModel"
|
|
3
4
|
import { assertIsModel } from "../model/utils"
|
|
4
5
|
import { addModelClassInitializer } from "../modelShared/modelClassInitializer"
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { assertIsObject, failure } from "../utils"
|
|
6
|
+
import { ChangeRollbackRecorder } from "../tweaker/changeRollback"
|
|
7
|
+
import { withoutTypeChecking } from "../tweaker/withoutTypeChecking"
|
|
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,
|
|
@@ -14,7 +16,7 @@ import {
|
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Creates a transaction middleware, which reverts changes made by an action / child
|
|
17
|
-
* actions when the root action throws an exception
|
|
19
|
+
* actions when the root action throws an exception, restoring the values those changes replaced.
|
|
18
20
|
*
|
|
19
21
|
* @template M Model
|
|
20
22
|
* @param target Object with the root target model object (`model`) and root action name (`actionName`).
|
|
@@ -34,14 +36,18 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
34
36
|
throw failure("target.actionName must be a string")
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const
|
|
38
|
-
function
|
|
39
|
-
ctx.rootContext.data[
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const rollbackRecorderSymbol = Symbol("rollbackRecorder")
|
|
40
|
+
function initRollbackRecorder(ctx: SimpleActionContext) {
|
|
41
|
+
ctx.rootContext.data[rollbackRecorderSymbol] = {
|
|
42
|
+
recorder: new ChangeRollbackRecorder(),
|
|
43
|
+
activeCount: 0,
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
46
|
+
function getRollbackRecorderData(ctx: SimpleActionContext): {
|
|
47
|
+
recorder: ChangeRollbackRecorder
|
|
48
|
+
activeCount: number
|
|
49
|
+
} {
|
|
50
|
+
return ctx.rootContext.data[rollbackRecorderSymbol]
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
return actionTrackingMiddleware(model, {
|
|
@@ -52,30 +58,54 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
52
58
|
},
|
|
53
59
|
onStart(ctx) {
|
|
54
60
|
if (ctx === ctx.rootContext) {
|
|
55
|
-
|
|
61
|
+
initRollbackRecorder(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 = getRollbackRecorderData(ctx)
|
|
68
|
+
data.activeCount++
|
|
69
|
+
data.recorder.recording = true
|
|
60
70
|
},
|
|
61
71
|
onSuspend(ctx) {
|
|
62
|
-
|
|
72
|
+
const data = getRollbackRecorderData(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
|
|
78
|
+
const recorder = getRollbackRecorderData(ctx).recorder
|
|
79
|
+
// stop recording, so the rollback is not recorded as well
|
|
80
|
+
recorder.dispose()
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
if (ret.result === ActionTrackingResult.Throw) {
|
|
83
|
+
// revert changes (backwards). Restoring the replaced values themselves
|
|
84
|
+
// (rather than applying inverse patches) keeps node identities, so
|
|
85
|
+
// nodes that were detached and then changed are restored too.
|
|
86
|
+
const { reverts } = recorder
|
|
87
|
+
const rollbackErrors: unknown[] = []
|
|
88
|
+
runUnprotected(() => {
|
|
89
|
+
// it restores the state before the action, which was already checked
|
|
90
|
+
withoutTypeChecking(() => {
|
|
91
|
+
for (let i = reverts.length - 1; i >= 0; i--) {
|
|
92
|
+
// A listener may throw after a change has already been reverted.
|
|
93
|
+
// Keep going so one failure cannot skip the remaining restoration.
|
|
94
|
+
try {
|
|
95
|
+
reverts[i]()
|
|
96
|
+
} catch (error) {
|
|
97
|
+
pushDelayedError(rollbackErrors, error)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
if (rollbackErrors.length > 0) {
|
|
103
|
+
// Keep the action error first so the reason for the rollback is not lost.
|
|
104
|
+
throw new MobxKeystoneAggregateError(
|
|
105
|
+
[ret.value, ...rollbackErrors],
|
|
106
|
+
"transaction rollback callbacks failed"
|
|
107
|
+
)
|
|
76
108
|
}
|
|
77
|
-
} finally {
|
|
78
|
-
patchRecorder.dispose()
|
|
79
109
|
}
|
|
80
110
|
}
|
|
81
111
|
},
|