mobx-keystone 2.0.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 +22 -0
- package/dist/mobx-keystone.esm.js +2341 -780
- package/dist/mobx-keystone.esm.mjs +2341 -780
- package/dist/mobx-keystone.umd.js +2346 -782
- package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +17 -10
- package/dist/types/parent/arrayPaths.d.ts +1 -0
- package/dist/types/parent/treeOrder.d.ts +1 -0
- package/dist/types/patch/applyPatches.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/applySnapshot.d.ts +1 -0
- package/dist/types/snapshot/fromSnapshot.d.ts +0 -3
- package/dist/types/tweaker/changeRollback.d.ts +1 -0
- package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
- package/dist/types/utils/ModelPool.d.ts +7 -1
- package/dist/types/utils/chunks.d.ts +1 -0
- package/package.json +2 -2
- package/src/actionMiddlewares/transactionMiddleware.ts +40 -41
- package/src/actionMiddlewares/undoMiddleware.ts +47 -13
- package/src/model/BaseModel.ts +6 -4
- package/src/modelShared/modelDecorator.ts +5 -25
- package/src/parent/arrayPaths.ts +242 -0
- package/src/parent/coreObjectChildren.ts +300 -48
- package/src/parent/onChildAttachedTo.ts +130 -44
- package/src/parent/path.ts +10 -2
- package/src/parent/setParent.ts +27 -15
- package/src/parent/treeOrder.ts +53 -0
- package/src/parent/walkTree.ts +0 -73
- package/src/patch/applyPatches.ts +31 -26
- package/src/ref/core.ts +63 -27
- package/src/ref/modelRefIdIndex.ts +131 -0
- package/src/ref/nodesById.ts +60 -0
- package/src/ref/refIdIndex.ts +209 -0
- package/src/snapshot/applySnapshot.ts +26 -18
- package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
- package/src/snapshot/fromSnapshot.ts +2 -6
- package/src/snapshot/internal.ts +67 -10
- package/src/tweaker/changeRollback.ts +218 -0
- package/src/tweaker/core.ts +33 -20
- package/src/tweaker/treeNodeMetadata.ts +12 -2
- package/src/tweaker/tweak.ts +181 -18
- package/src/tweaker/tweakArray.ts +107 -27
- package/src/tweaker/tweakPlainObject.ts +56 -31
- package/src/tweaker/tweakedChangeListeners.ts +61 -0
- package/src/tweaker/typeChecking.ts +5 -104
- package/src/types/arrayBased/typesArray.ts +10 -11
- package/src/types/createCachedTypeCheck.ts +522 -94
- package/src/types/objectBased/typesRecord.ts +2 -5
- package/src/utils/ModelPool.ts +75 -19
- package/src/utils/chunks.ts +271 -0
- package/src/utils/index.ts +37 -0
|
@@ -2,7 +2,7 @@ import { ActionMiddlewareDisposer } from '../action/middleware';
|
|
|
2
2
|
import { AnyModel } from '../model/BaseModel';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a transaction middleware, which reverts changes made by an action / child
|
|
5
|
-
* actions when the root action throws an exception
|
|
5
|
+
* actions when the root action throws an exception, restoring the values those changes replaced.
|
|
6
6
|
*
|
|
7
7
|
* @template M Model
|
|
8
8
|
* @param target Object with the root target model object (`model`) and root action name (`actionName`).
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ActionMiddlewareDisposer } from '../action/middleware';
|
|
2
|
+
import { Frozen } from '../frozen';
|
|
2
3
|
import { Path } from '../parent/pathTypes';
|
|
3
4
|
import { Patch } from '../patch';
|
|
5
|
+
import { FrozenData } from '../snapshot';
|
|
4
6
|
/**
|
|
5
7
|
* An undo/redo event without attached state.
|
|
6
8
|
*/
|
|
@@ -75,20 +77,25 @@ export interface UndoEventGroup {
|
|
|
75
77
|
readonly events: ReadonlyArray<UndoEventWithoutAttachedState>;
|
|
76
78
|
}
|
|
77
79
|
declare const UndoStore_base: import('..')._Model<unknown, {
|
|
78
|
-
undoEvents: import('..').OptionalModelProp<UndoEvent[]> & {
|
|
79
|
-
$storedValueType: UndoEvent[];
|
|
80
|
-
$storedCreationValueType: UndoEvent[] | null | undefined;
|
|
81
|
-
$typedFromSnapshotOverride: UndoEvent[] | null | undefined;
|
|
80
|
+
undoEvents: import('..').OptionalModelProp<Frozen<UndoEvent>[]> & {
|
|
81
|
+
$storedValueType: Frozen<UndoEvent>[];
|
|
82
|
+
$storedCreationValueType: Frozen<UndoEvent>[] | null | undefined;
|
|
83
|
+
$typedFromSnapshotOverride: import('..').SnapshotInOfFrozen<Frozen<UndoEvent>>[] | null | undefined;
|
|
82
84
|
};
|
|
83
|
-
redoEvents: import('..').OptionalModelProp<UndoEvent[]> & {
|
|
84
|
-
$storedValueType: UndoEvent[];
|
|
85
|
-
$storedCreationValueType: UndoEvent[] | null | undefined;
|
|
86
|
-
$typedFromSnapshotOverride: UndoEvent[] | null | undefined;
|
|
85
|
+
redoEvents: import('..').OptionalModelProp<Frozen<UndoEvent>[]> & {
|
|
86
|
+
$storedValueType: Frozen<UndoEvent>[];
|
|
87
|
+
$storedCreationValueType: Frozen<UndoEvent>[] | null | undefined;
|
|
88
|
+
$typedFromSnapshotOverride: import('..').SnapshotInOfFrozen<Frozen<UndoEvent>>[] | null | undefined;
|
|
87
89
|
};
|
|
88
|
-
},
|
|
90
|
+
}, {
|
|
91
|
+
undoEvents?: ReadonlyArray<UndoEvent | FrozenData<UndoEvent>>;
|
|
92
|
+
redoEvents?: ReadonlyArray<UndoEvent | FrozenData<UndoEvent>>;
|
|
93
|
+
}, never>;
|
|
89
94
|
/**
|
|
90
95
|
* Store model instance for undo/redo actions.
|
|
91
|
-
* Do not manipulate directly, other
|
|
96
|
+
* Do not manipulate directly, other than creating it.
|
|
97
|
+
*
|
|
98
|
+
* Events are stored as frozen data, since they never change once recorded.
|
|
92
99
|
*/
|
|
93
100
|
export declare class UndoStore extends UndoStore_base {
|
|
94
101
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,7 +2,8 @@ import { Patch } from '../patch/Patch';
|
|
|
2
2
|
/**
|
|
3
3
|
* Applies the given patches to the given target object.
|
|
4
4
|
* Automatic type checking validates the completed batch, including nested lists.
|
|
5
|
-
* A
|
|
5
|
+
* A batch that throws or is rejected is rolled back with compensating patches.
|
|
6
|
+
* Errors thrown by listeners do not roll it back.
|
|
6
7
|
*
|
|
7
8
|
* @param node Target object.
|
|
8
9
|
* @param patches List of patches to apply.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SnapshotInOf, SnapshotOutOf } from './SnapshotOf';
|
|
2
2
|
/**
|
|
3
3
|
* Applies a full snapshot over an object, reconciling it with the current contents of the object.
|
|
4
|
+
* If it throws, the changes it made are rolled back (errors thrown by listeners do not roll them back).
|
|
4
5
|
*
|
|
5
6
|
* @template T Object type.
|
|
6
7
|
* @param node Target object (model object, object or array).
|
|
@@ -28,6 +28,3 @@ export declare function fromSnapshot<TType extends AnyType>(type: TType, snapsho
|
|
|
28
28
|
* @returns The deserialized object.
|
|
29
29
|
*/
|
|
30
30
|
export declare function fromSnapshot<T>(snapshot: SnapshotInOf<T> | SnapshotOutOf<T>, options?: Partial<FromSnapshotOptions>): T;
|
|
31
|
-
export declare const observableOptions: {
|
|
32
|
-
deep: boolean;
|
|
33
|
-
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { AnyModel } from '../model/BaseModel';
|
|
2
|
+
/**
|
|
3
|
+
* A view of the models under a node, as they were when the pool was created.
|
|
4
|
+
* Must be released once no longer used.
|
|
5
|
+
*/
|
|
2
6
|
export declare class ModelPool {
|
|
3
|
-
private pool;
|
|
7
|
+
private readonly pool;
|
|
8
|
+
private readonly pinned;
|
|
4
9
|
constructor(root: object);
|
|
10
|
+
release(): void;
|
|
5
11
|
findModelByTypeAndId(modelType: string, modelId: string | undefined): AnyModel | undefined;
|
|
6
12
|
findModelForSnapshot(sn: any): AnyModel | undefined;
|
|
7
13
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobx-keystone",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mobx",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"@types/node": "^26.4.1",
|
|
79
79
|
"@typescript/typescript6": "^6.0.2",
|
|
80
80
|
"@vitest/coverage-v8": "^5.0.0",
|
|
81
|
+
"expect-type": "^1.4.0",
|
|
81
82
|
"mobx-v4": "npm:mobx@^4.15.7",
|
|
82
83
|
"mobx-v5": "npm:mobx@^5.15.7",
|
|
83
84
|
"mobx-v6": "npm:mobx@^6.0.0",
|
|
84
85
|
"mobx-v7": "npm:mobx@^7.0.0",
|
|
85
86
|
"shx": "^0.4.0",
|
|
86
|
-
"spec.ts": "^1.1.3",
|
|
87
87
|
"ts-node": "^10.9.2",
|
|
88
88
|
"typedoc": "^0.28.20",
|
|
89
89
|
"typescript": "^7.0.2",
|
|
@@ -1,10 +1,10 @@
|
|
|
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 { withTypeCheckingBatch } from "../tweaker/typeChecking"
|
|
6
|
+
import { ChangeRollbackRecorder } from "../tweaker/changeRollback"
|
|
7
|
+
import { withoutTypeChecking } from "../tweaker/withoutTypeChecking"
|
|
8
8
|
import { assertIsObject, failure, MobxKeystoneAggregateError } from "../utils"
|
|
9
9
|
import { checkDecoratorContext } from "../utils/decorators"
|
|
10
10
|
import { pushDelayedError } from "../utils/forEachWithDelayedThrow"
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Creates a transaction middleware, which reverts changes made by an action / child
|
|
19
|
-
* actions when the root action throws an exception
|
|
19
|
+
* actions when the root action throws an exception, restoring the values those changes replaced.
|
|
20
20
|
*
|
|
21
21
|
* @template M Model
|
|
22
22
|
* @param target Object with the root target model object (`model`) and root action name (`actionName`).
|
|
@@ -36,18 +36,18 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
36
36
|
throw failure("target.actionName must be a string")
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
function
|
|
41
|
-
ctx.rootContext.data[
|
|
42
|
-
recorder:
|
|
39
|
+
const rollbackRecorderSymbol = Symbol("rollbackRecorder")
|
|
40
|
+
function initRollbackRecorder(ctx: SimpleActionContext) {
|
|
41
|
+
ctx.rootContext.data[rollbackRecorderSymbol] = {
|
|
42
|
+
recorder: new ChangeRollbackRecorder(),
|
|
43
43
|
activeCount: 0,
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
recorder:
|
|
46
|
+
function getRollbackRecorderData(ctx: SimpleActionContext): {
|
|
47
|
+
recorder: ChangeRollbackRecorder
|
|
48
48
|
activeCount: number
|
|
49
49
|
} {
|
|
50
|
-
return ctx.rootContext.data[
|
|
50
|
+
return ctx.rootContext.data[rollbackRecorderSymbol]
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return actionTrackingMiddleware(model, {
|
|
@@ -58,55 +58,54 @@ export function transactionMiddleware<M extends AnyModel>(target: {
|
|
|
58
58
|
},
|
|
59
59
|
onStart(ctx) {
|
|
60
60
|
if (ctx === ctx.rootContext) {
|
|
61
|
-
|
|
61
|
+
initRollbackRecorder(ctx)
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
// Actions nest, so recording must only stop once every action of the transaction
|
|
65
65
|
// has been suspended, not as soon as the innermost one returns.
|
|
66
66
|
onResume(ctx) {
|
|
67
|
-
const data =
|
|
67
|
+
const data = getRollbackRecorderData(ctx)
|
|
68
68
|
data.activeCount++
|
|
69
69
|
data.recorder.recording = true
|
|
70
70
|
},
|
|
71
71
|
onSuspend(ctx) {
|
|
72
|
-
const data =
|
|
72
|
+
const data = getRollbackRecorderData(ctx)
|
|
73
73
|
data.activeCount--
|
|
74
74
|
data.recorder.recording = data.activeCount > 0
|
|
75
75
|
},
|
|
76
76
|
onFinish(ctx, ret) {
|
|
77
77
|
if (ctx === ctx.rootContext) {
|
|
78
|
-
const
|
|
78
|
+
const recorder = getRollbackRecorderData(ctx).recorder
|
|
79
|
+
// stop recording, so the rollback is not recorded as well
|
|
80
|
+
recorder.dispose()
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
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)
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
})
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
)
|
|
107
108
|
}
|
|
108
|
-
} finally {
|
|
109
|
-
patchRecorder.dispose()
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
},
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { ActionMiddlewareDisposer } from "../action/middleware"
|
|
2
2
|
import { modelAction } from "../action/modelAction"
|
|
3
|
+
import { type Frozen, FrozenCheckMode, frozen, isFrozenSnapshot, toFrozenSnapshot } from "../frozen"
|
|
3
4
|
import { Model } from "../model/Model"
|
|
4
5
|
import { model } from "../modelShared/modelDecorator"
|
|
5
6
|
import { fastGetRootPath } from "../parent/path"
|
|
6
7
|
import type { Path } from "../parent/pathTypes"
|
|
7
8
|
import { applyPatches, type Patch, type PatchRecorder, patchRecorder } from "../patch"
|
|
9
|
+
import type { FrozenData } from "../snapshot"
|
|
8
10
|
import { assertTweakedObject } from "../tweaker/core"
|
|
9
11
|
import { typesArray } from "../types/arrayBased/typesArray"
|
|
12
|
+
import { typesFrozen } from "../types/objectBased/typesObject"
|
|
10
13
|
import { tProp } from "../types/tProp"
|
|
11
14
|
import { typesUnchecked } from "../types/utility/typesUnchecked"
|
|
12
15
|
import {
|
|
@@ -121,16 +124,45 @@ function toSingleEvents(
|
|
|
121
124
|
return array
|
|
122
125
|
}
|
|
123
126
|
|
|
127
|
+
// snapshots from before events were frozen have them as plain objects
|
|
128
|
+
function toFrozenEventSnapshots(
|
|
129
|
+
events: ReadonlyArray<UndoEvent | FrozenData<UndoEvent>> | undefined
|
|
130
|
+
): FrozenData<UndoEvent>[] | undefined {
|
|
131
|
+
return events?.map((e) => (isFrozenSnapshot<UndoEvent>(e) ? e : toFrozenSnapshot(e)))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function freezeEvent(event: UndoEvent): Frozen<UndoEvent> {
|
|
135
|
+
// events are only read once recorded, so they are not checked or deeply
|
|
136
|
+
// frozen (that would also freeze the attached states given by the user)
|
|
137
|
+
return frozen(event, FrozenCheckMode.Off)
|
|
138
|
+
}
|
|
139
|
+
|
|
124
140
|
/**
|
|
125
141
|
* Store model instance for undo/redo actions.
|
|
126
|
-
* Do not manipulate directly, other
|
|
142
|
+
* Do not manipulate directly, other than creating it.
|
|
143
|
+
*
|
|
144
|
+
* Events are stored as frozen data, since they never change once recorded.
|
|
127
145
|
*/
|
|
128
146
|
@model(`${namespace}/UndoStore`)
|
|
129
|
-
export class UndoStore extends Model(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
export class UndoStore extends Model(
|
|
148
|
+
{
|
|
149
|
+
// TODO: add proper type checking to undo store
|
|
150
|
+
undoEvents: tProp(typesArray(typesFrozen(typesUnchecked<UndoEvent>())), () => []),
|
|
151
|
+
redoEvents: tProp(typesArray(typesFrozen(typesUnchecked<UndoEvent>())), () => []),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
fromSnapshotProcessor(sn: {
|
|
155
|
+
undoEvents?: ReadonlyArray<UndoEvent | FrozenData<UndoEvent>>
|
|
156
|
+
redoEvents?: ReadonlyArray<UndoEvent | FrozenData<UndoEvent>>
|
|
157
|
+
}) {
|
|
158
|
+
return {
|
|
159
|
+
...sn,
|
|
160
|
+
undoEvents: toFrozenEventSnapshots(sn.undoEvents),
|
|
161
|
+
redoEvents: toFrozenEventSnapshots(sn.redoEvents),
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
) {
|
|
134
166
|
/**
|
|
135
167
|
* @ignore
|
|
136
168
|
*/
|
|
@@ -201,7 +233,7 @@ export class UndoStore extends Model({
|
|
|
201
233
|
@modelAction
|
|
202
234
|
_addUndo({ event, maxUndoLevels }: { event: UndoEvent; maxUndoLevels: number | undefined }) {
|
|
203
235
|
withoutUndo(() => {
|
|
204
|
-
this.undoEvents.push(event)
|
|
236
|
+
this.undoEvents.push(freezeEvent(event))
|
|
205
237
|
// once an undo event is added redo queue is no longer valid
|
|
206
238
|
this.redoEvents.length = 0
|
|
207
239
|
this.enforceMaxLevels({ maxUndoLevels })
|
|
@@ -323,7 +355,7 @@ export class UndoManager {
|
|
|
323
355
|
*/
|
|
324
356
|
@mobxComputed
|
|
325
357
|
get undoQueue(): ReadonlyArray<UndoEvent> {
|
|
326
|
-
return this.store.undoEvents
|
|
358
|
+
return this.store.undoEvents.map((e) => e.data)
|
|
327
359
|
}
|
|
328
360
|
|
|
329
361
|
/**
|
|
@@ -332,7 +364,7 @@ export class UndoManager {
|
|
|
332
364
|
*/
|
|
333
365
|
@mobxComputed
|
|
334
366
|
get redoQueue(): ReadonlyArray<UndoEvent> {
|
|
335
|
-
return this.store.redoEvents
|
|
367
|
+
return this.store.redoEvents.map((e) => e.data)
|
|
336
368
|
}
|
|
337
369
|
|
|
338
370
|
/**
|
|
@@ -340,7 +372,7 @@ export class UndoManager {
|
|
|
340
372
|
*/
|
|
341
373
|
@mobxComputed
|
|
342
374
|
get undoLevels() {
|
|
343
|
-
return this.
|
|
375
|
+
return this.store.undoEvents.length
|
|
344
376
|
}
|
|
345
377
|
|
|
346
378
|
/**
|
|
@@ -364,7 +396,7 @@ export class UndoManager {
|
|
|
364
396
|
*/
|
|
365
397
|
@mobxComputed
|
|
366
398
|
get redoLevels() {
|
|
367
|
-
return this.
|
|
399
|
+
return this.store.redoEvents.length
|
|
368
400
|
}
|
|
369
401
|
|
|
370
402
|
/**
|
|
@@ -392,7 +424,8 @@ export class UndoManager {
|
|
|
392
424
|
if (!this.canUndo) {
|
|
393
425
|
throw failure("nothing to undo")
|
|
394
426
|
}
|
|
395
|
-
const
|
|
427
|
+
const events = this.store.undoEvents
|
|
428
|
+
const event = events[events.length - 1].data
|
|
396
429
|
|
|
397
430
|
withoutUndo(() => {
|
|
398
431
|
toSingleEvents(event)
|
|
@@ -419,7 +452,8 @@ export class UndoManager {
|
|
|
419
452
|
if (!this.canRedo) {
|
|
420
453
|
throw failure("nothing to redo")
|
|
421
454
|
}
|
|
422
|
-
const
|
|
455
|
+
const events = this.store.redoEvents
|
|
456
|
+
const event = events[events.length - 1].data
|
|
423
457
|
|
|
424
458
|
withoutUndo(() => {
|
|
425
459
|
toSingleEvents(event).forEach((e) => {
|
package/src/model/BaseModel.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type { SnapshotInOfModel, SnapshotOutOfModel } from "../snapshot/Snapshot
|
|
|
17
17
|
import { typesModel } from "../types/objectBased/typesModel"
|
|
18
18
|
import type { TypeCheckError } from "../types/TypeCheckError"
|
|
19
19
|
import { typeCheck } from "../types/typeCheck"
|
|
20
|
-
import { assertIsObject, failure } from "../utils"
|
|
20
|
+
import { assertIsObject, failure, nodeObservableOptions } from "../utils"
|
|
21
21
|
import { getModelIdPropertyName } from "./getModelMetadata"
|
|
22
22
|
import type { ModelConstructorOptions } from "./ModelConstructorOptions"
|
|
23
23
|
import { modelIdKey, modelTypeKey } from "./metadata"
|
|
@@ -150,9 +150,11 @@ export abstract class BaseModel<
|
|
|
150
150
|
// Empty input is filled by the constructor below. Track its primitive
|
|
151
151
|
// defaults directly instead of rereading observable fields for the snapshot.
|
|
152
152
|
const useInitialDataSnapshot = Reflect.ownKeys(initialData).length === 0
|
|
153
|
-
const observableInitialData = observable.object(
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
const observableInitialData = observable.object(
|
|
154
|
+
initialData as any,
|
|
155
|
+
undefined,
|
|
156
|
+
nodeObservableOptions
|
|
157
|
+
)
|
|
156
158
|
if (useInitialDataSnapshot) {
|
|
157
159
|
setModelInitialDataSnapshot(observableInitialData, {})
|
|
158
160
|
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
addLateInitializationFunction,
|
|
20
20
|
failure,
|
|
21
21
|
getMobxVersion,
|
|
22
|
+
hasStoredMobxAnnotations,
|
|
22
23
|
logWarning,
|
|
23
24
|
makeObservableCompat,
|
|
24
25
|
runAfterNewSymbol,
|
|
@@ -55,33 +56,12 @@ const afterClassInitializationData = new WeakMap<
|
|
|
55
56
|
const runAfterClassInitialization = (tag: AfterClassInitializationData, instance: any) => {
|
|
56
57
|
runLateInitializationFunctions(instance, runAfterNewSymbol)
|
|
57
58
|
|
|
58
|
-
//
|
|
59
|
+
// MobX 6 legacy decorators need makeObservable, while MobX 7 annotations (see
|
|
60
|
+
// decoratedModel) and 2022.3 decorators initialize themselves. Otherwise it
|
|
61
|
+
// would only add an empty observable administration to every instance.
|
|
62
|
+
tag.needsMakeObservable ??= getMobxVersion() === 6 && hasStoredMobxAnnotations(instance)
|
|
59
63
|
if (tag.needsMakeObservable) {
|
|
60
|
-
// we know it can be done and shouldn't fail
|
|
61
64
|
makeObservableCompat(instance)
|
|
62
|
-
} else if (tag.needsMakeObservable === undefined) {
|
|
63
|
-
if (getMobxVersion() >= 6) {
|
|
64
|
-
try {
|
|
65
|
-
makeObservableCompat(instance)
|
|
66
|
-
tag.needsMakeObservable = true
|
|
67
|
-
} catch (e) {
|
|
68
|
-
const err = e as Error
|
|
69
|
-
if (
|
|
70
|
-
err.message !==
|
|
71
|
-
"[MobX] No annotations were passed to makeObservable, but no decorator members have been found either" &&
|
|
72
|
-
err.message !==
|
|
73
|
-
"[MobX] No annotations were passed to makeObservable, but no decorated members have been found either"
|
|
74
|
-
) {
|
|
75
|
-
throw err
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// sadly we need to use this hack since the PR to do this the proper way
|
|
79
|
-
// was rejected on the mobx side
|
|
80
|
-
tag.needsMakeObservable = false
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
tag.needsMakeObservable = false
|
|
84
|
-
}
|
|
85
65
|
}
|
|
86
66
|
|
|
87
67
|
// the object is ready
|