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,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 {
|
|
@@ -19,6 +22,16 @@ import {
|
|
|
19
22
|
} from "../utils"
|
|
20
23
|
import { actionTrackingMiddleware, type SimpleActionContext } from "./actionTrackingMiddleware"
|
|
21
24
|
|
|
25
|
+
function checkMaxLevels(value: number | undefined, name: string): void {
|
|
26
|
+
if (
|
|
27
|
+
value !== undefined &&
|
|
28
|
+
value !== Number.POSITIVE_INFINITY &&
|
|
29
|
+
(!Number.isInteger(value) || value < 0)
|
|
30
|
+
) {
|
|
31
|
+
throw failure(`${name} must be a non-negative integer or Infinity`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
/**
|
|
23
36
|
* An undo/redo event without attached state.
|
|
24
37
|
*/
|
|
@@ -111,16 +124,45 @@ function toSingleEvents(
|
|
|
111
124
|
return array
|
|
112
125
|
}
|
|
113
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
|
+
|
|
114
140
|
/**
|
|
115
141
|
* Store model instance for undo/redo actions.
|
|
116
|
-
* 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.
|
|
117
145
|
*/
|
|
118
146
|
@model(`${namespace}/UndoStore`)
|
|
119
|
-
export class UndoStore extends Model(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
) {
|
|
124
166
|
/**
|
|
125
167
|
* @ignore
|
|
126
168
|
*/
|
|
@@ -151,15 +193,13 @@ export class UndoStore extends Model({
|
|
|
151
193
|
maxUndoLevels?: number
|
|
152
194
|
maxRedoLevels?: number
|
|
153
195
|
}) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
196
|
+
checkMaxLevels(maxUndoLevels, "maxUndoLevels")
|
|
197
|
+
checkMaxLevels(maxRedoLevels, "maxRedoLevels")
|
|
198
|
+
if (maxUndoLevels !== undefined && this.undoEvents.length > maxUndoLevels) {
|
|
199
|
+
this.undoEvents.splice(0, this.undoEvents.length - maxUndoLevels)
|
|
158
200
|
}
|
|
159
|
-
if (maxRedoLevels !== undefined) {
|
|
160
|
-
|
|
161
|
-
this.redoEvents.shift()
|
|
162
|
-
}
|
|
201
|
+
if (maxRedoLevels !== undefined && this.redoEvents.length > maxRedoLevels) {
|
|
202
|
+
this.redoEvents.splice(0, this.redoEvents.length - maxRedoLevels)
|
|
163
203
|
}
|
|
164
204
|
}
|
|
165
205
|
|
|
@@ -193,7 +233,7 @@ export class UndoStore extends Model({
|
|
|
193
233
|
@modelAction
|
|
194
234
|
_addUndo({ event, maxUndoLevels }: { event: UndoEvent; maxUndoLevels: number | undefined }) {
|
|
195
235
|
withoutUndo(() => {
|
|
196
|
-
this.undoEvents.push(event)
|
|
236
|
+
this.undoEvents.push(freezeEvent(event))
|
|
197
237
|
// once an undo event is added redo queue is no longer valid
|
|
198
238
|
this.redoEvents.length = 0
|
|
199
239
|
this.enforceMaxLevels({ maxUndoLevels })
|
|
@@ -261,10 +301,16 @@ export class UndoStore extends Model({
|
|
|
261
301
|
running = true
|
|
262
302
|
},
|
|
263
303
|
end: () => {
|
|
304
|
+
if (ended) {
|
|
305
|
+
throw failure("cannot end a group when it is already ended")
|
|
306
|
+
}
|
|
264
307
|
if (running) {
|
|
265
308
|
api.pause()
|
|
266
309
|
}
|
|
267
310
|
ended = true
|
|
311
|
+
if (group.events.length === 0) {
|
|
312
|
+
return
|
|
313
|
+
}
|
|
268
314
|
if (parentGroup) {
|
|
269
315
|
this._addUndoToParentGroup(parentGroup, group)
|
|
270
316
|
} else {
|
|
@@ -309,7 +355,7 @@ export class UndoManager {
|
|
|
309
355
|
*/
|
|
310
356
|
@mobxComputed
|
|
311
357
|
get undoQueue(): ReadonlyArray<UndoEvent> {
|
|
312
|
-
return this.store.undoEvents
|
|
358
|
+
return this.store.undoEvents.map((e) => e.data)
|
|
313
359
|
}
|
|
314
360
|
|
|
315
361
|
/**
|
|
@@ -318,7 +364,7 @@ export class UndoManager {
|
|
|
318
364
|
*/
|
|
319
365
|
@mobxComputed
|
|
320
366
|
get redoQueue(): ReadonlyArray<UndoEvent> {
|
|
321
|
-
return this.store.redoEvents
|
|
367
|
+
return this.store.redoEvents.map((e) => e.data)
|
|
322
368
|
}
|
|
323
369
|
|
|
324
370
|
/**
|
|
@@ -326,7 +372,7 @@ export class UndoManager {
|
|
|
326
372
|
*/
|
|
327
373
|
@mobxComputed
|
|
328
374
|
get undoLevels() {
|
|
329
|
-
return this.
|
|
375
|
+
return this.store.undoEvents.length
|
|
330
376
|
}
|
|
331
377
|
|
|
332
378
|
/**
|
|
@@ -350,7 +396,7 @@ export class UndoManager {
|
|
|
350
396
|
*/
|
|
351
397
|
@mobxComputed
|
|
352
398
|
get redoLevels() {
|
|
353
|
-
return this.
|
|
399
|
+
return this.store.redoEvents.length
|
|
354
400
|
}
|
|
355
401
|
|
|
356
402
|
/**
|
|
@@ -378,7 +424,8 @@ export class UndoManager {
|
|
|
378
424
|
if (!this.canUndo) {
|
|
379
425
|
throw failure("nothing to undo")
|
|
380
426
|
}
|
|
381
|
-
const
|
|
427
|
+
const events = this.store.undoEvents
|
|
428
|
+
const event = events[events.length - 1].data
|
|
382
429
|
|
|
383
430
|
withoutUndo(() => {
|
|
384
431
|
toSingleEvents(event)
|
|
@@ -388,7 +435,7 @@ export class UndoManager {
|
|
|
388
435
|
})
|
|
389
436
|
|
|
390
437
|
// restore the attached state before the operation was made
|
|
391
|
-
if (event.attachedState
|
|
438
|
+
if (event.attachedState) {
|
|
392
439
|
this.options?.attachedState?.restore(event.attachedState.beforeEvent)
|
|
393
440
|
}
|
|
394
441
|
})
|
|
@@ -405,7 +452,8 @@ export class UndoManager {
|
|
|
405
452
|
if (!this.canRedo) {
|
|
406
453
|
throw failure("nothing to redo")
|
|
407
454
|
}
|
|
408
|
-
const
|
|
455
|
+
const events = this.store.redoEvents
|
|
456
|
+
const event = events[events.length - 1].data
|
|
409
457
|
|
|
410
458
|
withoutUndo(() => {
|
|
411
459
|
toSingleEvents(event).forEach((e) => {
|
|
@@ -413,7 +461,7 @@ export class UndoManager {
|
|
|
413
461
|
})
|
|
414
462
|
|
|
415
463
|
// restore the attached state after the operation was made
|
|
416
|
-
if (event.attachedState
|
|
464
|
+
if (event.attachedState) {
|
|
417
465
|
this.options?.attachedState?.restore(event.attachedState.afterEvent)
|
|
418
466
|
}
|
|
419
467
|
})
|
|
@@ -586,14 +634,14 @@ export class UndoManager {
|
|
|
586
634
|
function next(ret: any): void {
|
|
587
635
|
if (ret && typeof ret.then === "function") {
|
|
588
636
|
// an async iterator
|
|
589
|
-
ret.then(next, reject)
|
|
637
|
+
Promise.resolve(ret).then(next, reject).catch(reject)
|
|
590
638
|
} else if (ret.done) {
|
|
591
639
|
// done
|
|
592
640
|
group.end()
|
|
593
641
|
resolve(ret.value)
|
|
594
642
|
} else {
|
|
595
643
|
// continue
|
|
596
|
-
Promise.resolve(ret.value).then(onFulfilled, onRejected)
|
|
644
|
+
Promise.resolve(ret.value).then(onFulfilled, onRejected).catch(reject)
|
|
597
645
|
}
|
|
598
646
|
}
|
|
599
647
|
|
|
@@ -675,6 +723,8 @@ export function undoMiddleware<S>(
|
|
|
675
723
|
options?: UndoMiddlewareOptions<S>
|
|
676
724
|
): UndoManager {
|
|
677
725
|
assertTweakedObject(subtreeRoot, "subtreeRoot")
|
|
726
|
+
checkMaxLevels(options?.maxUndoLevels, "maxUndoLevels")
|
|
727
|
+
checkMaxLevels(options?.maxRedoLevels, "maxRedoLevels")
|
|
678
728
|
|
|
679
729
|
let manager: UndoManager
|
|
680
730
|
|
|
@@ -691,6 +741,7 @@ export function undoMiddleware<S>(
|
|
|
691
741
|
function initPatchRecorder(ctx: SimpleActionContext) {
|
|
692
742
|
const group = manager.store._currentGroup
|
|
693
743
|
|
|
744
|
+
const attachedStateBeforeEvent = options?.attachedState?.save()
|
|
694
745
|
const patchRecorderData: PatchRecorderData = {
|
|
695
746
|
recorder: patchRecorder(subtreeRoot, {
|
|
696
747
|
recording: false,
|
|
@@ -702,7 +753,7 @@ export function undoMiddleware<S>(
|
|
|
702
753
|
undoRootContext: ctx,
|
|
703
754
|
group,
|
|
704
755
|
|
|
705
|
-
attachedStateBeforeEvent
|
|
756
|
+
attachedStateBeforeEvent,
|
|
706
757
|
}
|
|
707
758
|
|
|
708
759
|
ctx.rootContext.data[patchRecorderSymbol] = patchRecorderData
|
|
@@ -733,42 +784,44 @@ export function undoMiddleware<S>(
|
|
|
733
784
|
if (patchRecorderData && patchRecorderData.undoRootContext === ctx) {
|
|
734
785
|
const patchRecorder = patchRecorderData.recorder
|
|
735
786
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
787
|
+
try {
|
|
788
|
+
if (patchRecorder.events.length > 0) {
|
|
789
|
+
const patches: Patch[] = []
|
|
790
|
+
const inversePatches: Patch[] = []
|
|
791
|
+
|
|
792
|
+
for (const event of patchRecorder.events) {
|
|
793
|
+
for (const patch of event.patches) patches.push(patch)
|
|
794
|
+
for (const patch of event.inversePatches) inversePatches.push(patch)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const event = {
|
|
798
|
+
type: UndoEventType.Single,
|
|
799
|
+
targetPath: fastGetRootPath(ctx.target, false).path,
|
|
800
|
+
actionName: ctx.actionName,
|
|
801
|
+
patches,
|
|
802
|
+
inversePatches,
|
|
803
|
+
} as const
|
|
804
|
+
|
|
805
|
+
const parentGroup = patchRecorderData.group
|
|
806
|
+
|
|
807
|
+
if (parentGroup) {
|
|
808
|
+
manager.store._addUndoToParentGroup(parentGroup, event)
|
|
809
|
+
} else {
|
|
810
|
+
manager.store._addUndo({
|
|
811
|
+
event: {
|
|
812
|
+
...event,
|
|
813
|
+
attachedState: {
|
|
814
|
+
beforeEvent: patchRecorderData.attachedStateBeforeEvent,
|
|
815
|
+
afterEvent: options?.attachedState?.save(),
|
|
816
|
+
},
|
|
764
817
|
},
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
818
|
+
maxUndoLevels: options?.maxUndoLevels,
|
|
819
|
+
})
|
|
820
|
+
}
|
|
768
821
|
}
|
|
822
|
+
} finally {
|
|
823
|
+
patchRecorder.dispose()
|
|
769
824
|
}
|
|
770
|
-
|
|
771
|
-
patchRecorder.dispose()
|
|
772
825
|
}
|
|
773
826
|
},
|
|
774
827
|
})
|
|
@@ -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
|
|