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.
Files changed (53) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/mobx-keystone.esm.js +2341 -780
  3. package/dist/mobx-keystone.esm.mjs +2341 -780
  4. package/dist/mobx-keystone.umd.js +2346 -782
  5. package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +17 -10
  7. package/dist/types/parent/arrayPaths.d.ts +1 -0
  8. package/dist/types/parent/treeOrder.d.ts +1 -0
  9. package/dist/types/patch/applyPatches.d.ts +2 -1
  10. package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
  11. package/dist/types/ref/nodesById.d.ts +1 -0
  12. package/dist/types/ref/refIdIndex.d.ts +1 -0
  13. package/dist/types/snapshot/applySnapshot.d.ts +1 -0
  14. package/dist/types/snapshot/fromSnapshot.d.ts +0 -3
  15. package/dist/types/tweaker/changeRollback.d.ts +1 -0
  16. package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
  17. package/dist/types/utils/ModelPool.d.ts +7 -1
  18. package/dist/types/utils/chunks.d.ts +1 -0
  19. package/package.json +2 -2
  20. package/src/actionMiddlewares/transactionMiddleware.ts +40 -41
  21. package/src/actionMiddlewares/undoMiddleware.ts +47 -13
  22. package/src/model/BaseModel.ts +6 -4
  23. package/src/modelShared/modelDecorator.ts +5 -25
  24. package/src/parent/arrayPaths.ts +242 -0
  25. package/src/parent/coreObjectChildren.ts +300 -48
  26. package/src/parent/onChildAttachedTo.ts +130 -44
  27. package/src/parent/path.ts +10 -2
  28. package/src/parent/setParent.ts +27 -15
  29. package/src/parent/treeOrder.ts +53 -0
  30. package/src/parent/walkTree.ts +0 -73
  31. package/src/patch/applyPatches.ts +31 -26
  32. package/src/ref/core.ts +63 -27
  33. package/src/ref/modelRefIdIndex.ts +131 -0
  34. package/src/ref/nodesById.ts +60 -0
  35. package/src/ref/refIdIndex.ts +209 -0
  36. package/src/snapshot/applySnapshot.ts +26 -18
  37. package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
  38. package/src/snapshot/fromSnapshot.ts +2 -6
  39. package/src/snapshot/internal.ts +67 -10
  40. package/src/tweaker/changeRollback.ts +218 -0
  41. package/src/tweaker/core.ts +33 -20
  42. package/src/tweaker/treeNodeMetadata.ts +12 -2
  43. package/src/tweaker/tweak.ts +181 -18
  44. package/src/tweaker/tweakArray.ts +107 -27
  45. package/src/tweaker/tweakPlainObject.ts +56 -31
  46. package/src/tweaker/tweakedChangeListeners.ts +61 -0
  47. package/src/tweaker/typeChecking.ts +5 -104
  48. package/src/types/arrayBased/typesArray.ts +10 -11
  49. package/src/types/createCachedTypeCheck.ts +522 -94
  50. package/src/types/objectBased/typesRecord.ts +2 -5
  51. package/src/utils/ModelPool.ts +75 -19
  52. package/src/utils/chunks.ts +271 -0
  53. 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 by applying inverse patches.
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
- }, never, never>;
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 that creating it.
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 rejected batch is rolled back with compensating patches.
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.0.0",
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 { applyPatches } from "../patch"
6
- import { internalPatchRecorder, type PatchRecorder } from "../patch/patchRecorder"
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 by applying inverse patches.
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 patchRecorderSymbol = Symbol("patchRecorder")
40
- function initPatchRecorder(ctx: SimpleActionContext) {
41
- ctx.rootContext.data[patchRecorderSymbol] = {
42
- recorder: internalPatchRecorder(undefined, { recording: false }),
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 getPatchRecorderData(ctx: SimpleActionContext): {
47
- recorder: PatchRecorder
46
+ function getRollbackRecorderData(ctx: SimpleActionContext): {
47
+ recorder: ChangeRollbackRecorder
48
48
  activeCount: number
49
49
  } {
50
- return ctx.rootContext.data[patchRecorderSymbol]
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
- initPatchRecorder(ctx)
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 = getPatchRecorderData(ctx)
67
+ const data = getRollbackRecorderData(ctx)
68
68
  data.activeCount++
69
69
  data.recorder.recording = true
70
70
  },
71
71
  onSuspend(ctx) {
72
- const data = getPatchRecorderData(ctx)
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 patchRecorder = getPatchRecorderData(ctx).recorder
78
+ const recorder = getRollbackRecorderData(ctx).recorder
79
+ // stop recording, so the rollback is not recorded as well
80
+ recorder.dispose()
79
81
 
80
- try {
81
- if (ret.result === ActionTrackingResult.Throw) {
82
- // undo changes (backwards for inverse patches)
83
- const { events } = patchRecorder
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
- }
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
- 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
- )
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 that creating it.
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
- // TODO: add proper type checking to undo store
131
- undoEvents: tProp(typesArray(typesUnchecked<UndoEvent>()), () => []),
132
- redoEvents: tProp(typesArray(typesUnchecked<UndoEvent>()), () => []),
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.undoQueue.length
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.redoQueue.length
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 event = this.undoQueue[this.undoQueue.length - 1]
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 event = this.redoQueue[this.redoQueue.length - 1]
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) => {
@@ -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(initialData as any, undefined, {
154
- deep: false,
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
- // compatibility with mobx 6 and 7
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