mobx-keystone 2.0.0 → 2.2.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 (102) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/dist/mobx-keystone.esm.js +2561 -1214
  4. package/dist/mobx-keystone.esm.mjs +2561 -1214
  5. package/dist/mobx-keystone.umd.js +2566 -1220
  6. package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
  7. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +11 -12
  8. package/dist/types/modelShared/modelDecorator.d.ts +5 -2
  9. package/dist/types/modelShared/prop.d.ts +42 -29
  10. package/dist/types/parent/arrayPaths.d.ts +1 -0
  11. package/dist/types/parent/treeOrder.d.ts +1 -0
  12. package/dist/types/patch/applyPatches.d.ts +2 -1
  13. package/dist/types/ref/Ref.d.ts +1 -4
  14. package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
  15. package/dist/types/ref/nodesById.d.ts +1 -0
  16. package/dist/types/ref/refIdIndex.d.ts +1 -0
  17. package/dist/types/snapshot/SnapshotOf.d.ts +4 -4
  18. package/dist/types/snapshot/applySnapshot.d.ts +2 -1
  19. package/dist/types/snapshot/fromSnapshot.d.ts +2 -5
  20. package/dist/types/snapshot/getSnapshot.d.ts +1 -1
  21. package/dist/types/tweaker/changeRollback.d.ts +1 -0
  22. package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
  23. package/dist/types/types/index.d.ts +1 -0
  24. package/dist/types/types/schemas.d.ts +41 -37
  25. package/dist/types/types/tProp.d.ts +12 -6
  26. package/dist/types/types/types.d.ts +6 -6
  27. package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
  28. package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
  29. package/dist/types/utils/ModelPool.d.ts +7 -2
  30. package/dist/types/utils/chunks.d.ts +1 -0
  31. package/dist/types/utils/types.d.ts +6 -0
  32. package/dist/types/wrappers/ArraySet.d.ts +1 -0
  33. package/package.json +5 -3
  34. package/src/action/modelFlow.ts +35 -66
  35. package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
  36. package/src/actionMiddlewares/transactionMiddleware.ts +41 -42
  37. package/src/actionMiddlewares/undoMiddleware.ts +63 -35
  38. package/src/context/context.ts +15 -36
  39. package/src/dataModel/utils.ts +5 -28
  40. package/src/deepChange/onDeepChange.ts +44 -126
  41. package/src/model/BaseModel.ts +7 -5
  42. package/src/model/utils.ts +5 -10
  43. package/src/modelShared/modelDecorator.ts +16 -30
  44. package/src/modelShared/prop.ts +115 -111
  45. package/src/modelShared/sharedInternalModel.ts +2 -2
  46. package/src/parent/arrayPaths.ts +239 -0
  47. package/src/parent/coreObjectChildren.ts +300 -48
  48. package/src/parent/onChildAttachedTo.ts +130 -44
  49. package/src/parent/path.ts +10 -12
  50. package/src/parent/setParent.ts +27 -15
  51. package/src/parent/treeOrder.ts +53 -0
  52. package/src/parent/walkTree.ts +0 -73
  53. package/src/patch/applyPatches.ts +31 -26
  54. package/src/patch/patchRecorder.ts +13 -28
  55. package/src/ref/core.ts +69 -46
  56. package/src/ref/modelRefIdIndex.ts +131 -0
  57. package/src/ref/nodesById.ts +60 -0
  58. package/src/ref/refIdIndex.ts +209 -0
  59. package/src/rootStore/rootStore.ts +1 -3
  60. package/src/snapshot/SnapshotOf.ts +24 -48
  61. package/src/snapshot/applySnapshot.ts +27 -19
  62. package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
  63. package/src/snapshot/fromSnapshot.ts +12 -9
  64. package/src/snapshot/getSnapshot.ts +1 -1
  65. package/src/snapshot/internal.ts +67 -12
  66. package/src/snapshot/reconcileModelSnapshot.ts +5 -3
  67. package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
  68. package/src/treeUtils/draft.ts +6 -1
  69. package/src/treeUtils/sandbox.ts +10 -7
  70. package/src/tweaker/changeRollback.ts +218 -0
  71. package/src/tweaker/core.ts +33 -20
  72. package/src/tweaker/treeNodeMetadata.ts +12 -2
  73. package/src/tweaker/tweak.ts +181 -18
  74. package/src/tweaker/tweakArray.ts +127 -77
  75. package/src/tweaker/tweakPlainObject.ts +65 -46
  76. package/src/tweaker/tweakedChangeListeners.ts +61 -0
  77. package/src/tweaker/typeChecking.ts +5 -104
  78. package/src/types/arrayBased/typesArray.ts +23 -30
  79. package/src/types/arrayBased/typesTuple.ts +16 -25
  80. package/src/types/createCachedTypeCheck.ts +509 -94
  81. package/src/types/index.ts +1 -0
  82. package/src/types/objectBased/typesDataModelData.ts +7 -8
  83. package/src/types/objectBased/typesObject.ts +11 -15
  84. package/src/types/objectBased/typesRecord.ts +12 -19
  85. package/src/types/schemas.ts +59 -71
  86. package/src/types/tProp.ts +29 -29
  87. package/src/types/utility/typeToStoredData.ts +22 -21
  88. package/src/types/utility/typesCodecCore.ts +5 -0
  89. package/src/types/utility/typesCodecSupport.ts +3 -3
  90. package/src/types/utility/typesMap.ts +43 -51
  91. package/src/types/utility/typesRefinement.ts +7 -8
  92. package/src/types/utility/typesSkipCheck.ts +7 -8
  93. package/src/types/utility/typesTag.ts +7 -8
  94. package/src/utils/ModelPool.ts +76 -37
  95. package/src/utils/chunks.ts +271 -0
  96. package/src/utils/decorators.ts +5 -5
  97. package/src/utils/index.ts +38 -20
  98. package/src/utils/types.ts +5 -0
  99. package/src/wrappers/ArraySet.ts +27 -14
  100. package/src/wrappers/ObjectMap.ts +1 -6
  101. package/src/wrappers/asMap.ts +1 -21
  102. package/src/wrappers/asSet.ts +0 -8
@@ -162,25 +162,19 @@ class ContextClass<T> implements Context<T> {
162
162
  })
163
163
  })
164
164
 
165
- set = action((node: object, value: T) => {
166
- assertTweakedObject(node, "node")
167
-
168
- this.nodeContextValue.set(node, {
169
- type: "value",
170
- value,
171
- })
172
- this.reportNodeAtomChanged(node)
173
- })
174
-
175
- private _setComputed(node: object, computedValueFn: IComputedValue<T>) {
165
+ private setNodeContextValue(node: object, contextValue: ContextValue<T>) {
176
166
  assertTweakedObject(node, "node")
177
167
 
178
- this.nodeContextValue.set(node, { type: "computed", value: computedValueFn })
168
+ this.nodeContextValue.set(node, contextValue)
179
169
  this.reportNodeAtomChanged(node)
180
170
  }
181
171
 
172
+ set = action((node: object, value: T) => {
173
+ this.setNodeContextValue(node, { type: "value", value })
174
+ })
175
+
182
176
  setComputed = action((node: object, valueFn: () => T) => {
183
- this._setComputed(node, computed(valueFn))
177
+ this.setNodeContextValue(node, { type: "computed", value: computed(valueFn) })
184
178
  })
185
179
 
186
180
  unset = action((node: object) => {
@@ -190,42 +184,27 @@ class ContextClass<T> implements Context<T> {
190
184
  this.reportNodeAtomChanged(node)
191
185
  })
192
186
 
193
- apply = action(<R>(fn: () => R, value: T): R => {
187
+ private applyOverride<R>(fn: () => R, contextValue: ContextValue<T>): R {
194
188
  const old = this.overrideContextValue.get()
195
- this.overrideContextValue.set({
196
- type: "value",
197
- value,
198
- })
189
+ this.overrideContextValue.set(contextValue)
199
190
 
200
191
  try {
201
192
  const ret = fn()
202
193
  if (isTweakedObject(ret, false)) {
203
- this.set(ret, value)
194
+ this.setNodeContextValue(ret, contextValue)
204
195
  }
205
196
  return ret
206
197
  } finally {
207
198
  this.overrideContextValue.set(old)
208
199
  }
200
+ }
201
+
202
+ apply = action(<R>(fn: () => R, value: T): R => {
203
+ return this.applyOverride(fn, { type: "value", value })
209
204
  })
210
205
 
211
206
  applyComputed = action(<R>(fn: () => R, valueFn: () => T): R => {
212
- const computedValueFn = computed(valueFn)
213
-
214
- const old = this.overrideContextValue.get()
215
- this.overrideContextValue.set({
216
- type: "computed",
217
- value: computedValueFn,
218
- })
219
-
220
- try {
221
- const ret = fn()
222
- if (isTweakedObject(ret, false)) {
223
- this._setComputed(ret, computedValueFn)
224
- }
225
- return ret
226
- } finally {
227
- this.overrideContextValue.set(old)
228
- }
207
+ return this.applyOverride(fn, { type: "computed", value: computed(valueFn) })
229
208
  })
230
209
 
231
210
  constructor(defaultValue?: T) {
@@ -13,37 +13,14 @@ export function isDataModel(model: unknown): model is AnyDataModel {
13
13
  return model instanceof BaseDataModel
14
14
  }
15
15
 
16
- /**
17
- * @internal
18
- *
19
- * Asserts something is actually a data model.
20
- *
21
- * @param model
22
- * @param argName
23
- */
24
- export function assertIsDataModel(
25
- model: unknown,
26
- argName: string,
27
- customErrMsg = "must be a data model instance"
28
- ): asserts model is AnyDataModel {
29
- if (!isDataModel(model)) {
30
- throw failure(`${argName} ${customErrMsg}`)
31
- }
32
- }
33
-
34
16
  /**
35
17
  * @internal
36
18
  */
37
19
  export function isDataModelClass(modelClass: unknown): modelClass is ModelClass<AnyDataModel> {
38
- if (typeof modelClass !== "function") {
39
- return false
40
- }
41
-
42
- if (modelClass !== BaseDataModel && !(modelClass.prototype instanceof BaseDataModel)) {
43
- return false
44
- }
45
-
46
- return true
20
+ return (
21
+ typeof modelClass === "function" &&
22
+ (modelClass === BaseDataModel || modelClass.prototype instanceof BaseDataModel)
23
+ )
47
24
  }
48
25
 
49
26
  /**
@@ -57,7 +34,7 @@ export function assertIsDataModelClass(
57
34
  throw failure(`${argName} must be a class`)
58
35
  }
59
36
 
60
- if (modelClass !== BaseDataModel && !(modelClass.prototype instanceof BaseDataModel)) {
37
+ if (!isDataModelClass(modelClass)) {
61
38
  throw failure(`${argName} must extend DataModel`)
62
39
  }
63
40
  }
@@ -209,21 +209,9 @@ function changeWithPath(change: DeepChange, path: Path): DeepChange {
209
209
  * @internal
210
210
  */
211
211
  export function emitDeepChange(obj: object, change: DeepChange): void {
212
- if (!hasAnyDeepChangeListeners()) {
213
- return
212
+ if (hasAnyDeepChangeListeners()) {
213
+ emitLazyDeepChange(obj, () => change)
214
214
  }
215
-
216
- const emission = beginDeepChangeEmission()
217
- try {
218
- attachEmission(change)
219
- emitGlobalDeepChange(obj, change)
220
- if (deepChangeListenerCount > 0) {
221
- emitDeepChangeToListeners(obj, change)
222
- }
223
- } finally {
224
- currentEmission = emission.parent
225
- }
226
- rethrowDeepChangeListenerError(emission)
227
215
  }
228
216
 
229
217
  /**
@@ -308,98 +296,52 @@ function emitDeepChangeFromValues(
308
296
  addedValues?: unknown[],
309
297
  removedValues?: unknown[]
310
298
  ): void {
311
- if (!hasAnyDeepChangeListeners()) {
312
- return
313
- }
314
-
315
- const emission = beginDeepChangeEmission()
316
- try {
317
- emitDeepChangeFromValuesInEmission(
318
- node,
319
- type,
320
- target,
321
- keyOrIndex,
322
- newValue,
323
- oldValue,
324
- addedValues,
325
- removedValues
299
+ if (hasAnyDeepChangeListeners()) {
300
+ emitLazyDeepChange(node, () =>
301
+ buildDeepChange(type, target, keyOrIndex, newValue, oldValue, addedValues, removedValues)
326
302
  )
327
- } finally {
328
- currentEmission = emission.parent
329
303
  }
330
- rethrowDeepChangeListenerError(emission)
331
304
  }
332
305
 
333
- function emitDeepChangeFromValuesInEmission(
334
- node: object,
335
- type: DeepChangeType,
336
- target: object | unknown[],
337
- keyOrIndex: string | number,
338
- newValue: unknown,
339
- oldValue: unknown,
340
- addedValues?: unknown[],
341
- removedValues?: unknown[]
342
- ): void {
343
- let change: DeepChange | undefined
344
-
345
- if (globalDeepChangeListeners.length > 0) {
346
- change = createDeepChange(
347
- type,
348
- target,
349
- keyOrIndex,
350
- newValue,
351
- oldValue,
352
- addedValues,
353
- removedValues
354
- )
355
- emitGlobalDeepChange(node, change)
356
- }
357
-
358
- if (deepChangeListenerCount === 0) {
359
- return
360
- }
306
+ /**
307
+ * Delivers a change to the global listeners and to the listeners of the node
308
+ * and its ancestors. The change is only created if some listener gets it.
309
+ */
310
+ function emitLazyDeepChange(node: object, createChange: () => DeepChange): void {
311
+ const emission = beginDeepChangeEmission()
312
+ try {
313
+ let change: DeepChange | undefined
361
314
 
362
- // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
363
- // at the front (O(depth) each), keeping the whole walk O(depth) instead of
364
- // O(depth^2) on deep trees.
365
- const reversedPrefix: WritablePath = []
366
- let current: object | undefined = node
367
- while (current) {
368
- const listenersForObject = deepChangeListeners.get(current)
369
- if (listenersForObject && listenersForObject.length > 0) {
370
- change ??= createDeepChange(
371
- type,
372
- target,
373
- keyOrIndex,
374
- newValue,
375
- oldValue,
376
- addedValues,
377
- removedValues
378
- )
379
- emitDeepChangeForTarget(current, change, reversedPrefix, reversedPrefix.length)
315
+ if (globalDeepChangeListeners.length > 0) {
316
+ change = attachEmission(createChange())
317
+ emitGlobalDeepChange(node, change)
380
318
  }
381
319
 
382
- const parentPath: ParentPath<object> | undefined = fastGetParentPath(current, false)
383
- if (!parentPath) {
384
- break
320
+ if (deepChangeListenerCount > 0) {
321
+ // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
322
+ // at the front (O(depth) each), keeping the whole walk O(depth) instead of
323
+ // O(depth^2) on deep trees.
324
+ const reversedPrefix: WritablePath = []
325
+ let current: object | undefined = node
326
+ while (current) {
327
+ const listenersForObject = deepChangeListeners.get(current)
328
+ if (listenersForObject && listenersForObject.length > 0) {
329
+ change ??= attachEmission(createChange())
330
+ emitDeepChangeForTarget(listenersForObject, change, reversedPrefix)
331
+ }
332
+
333
+ const parentPath: ParentPath<object> | undefined = fastGetParentPath(current, false)
334
+ if (!parentPath) {
335
+ break
336
+ }
337
+ reversedPrefix.push(parentPath.path)
338
+ current = parentPath.parent
339
+ }
385
340
  }
386
- reversedPrefix.push(parentPath.path)
387
- current = parentPath.parent
341
+ } finally {
342
+ currentEmission = emission.parent
388
343
  }
389
- }
390
-
391
- function createDeepChange(
392
- type: DeepChangeType,
393
- target: object | unknown[],
394
- keyOrIndex: string | number,
395
- newValue: unknown,
396
- oldValue: unknown,
397
- addedValues: unknown[] | undefined,
398
- removedValues: unknown[] | undefined
399
- ): DeepChange {
400
- return attachEmission(
401
- buildDeepChange(type, target, keyOrIndex, newValue, oldValue, addedValues, removedValues)
402
- )
344
+ rethrowDeepChangeListenerError(emission)
403
345
  }
404
346
 
405
347
  function buildDeepChange(
@@ -489,39 +431,15 @@ function rememberDeepChangeListenerError(error: unknown): void {
489
431
  emission.error = addDelayedError(emission.error, error, "multiple deep change listeners failed")
490
432
  }
491
433
 
492
- function emitDeepChangeToListeners(obj: object, change: DeepChange): void {
493
- // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
494
- // at the front (O(depth) each), keeping the whole walk O(depth) instead of
495
- // O(depth^2) on deep trees.
496
- const reversedPrefix: WritablePath = []
497
-
498
- emitDeepChangeForTarget(obj, change, reversedPrefix, 0)
499
-
500
- // and also emit subtree listeners all the way to the root
501
- let parentPath = fastGetParentPath(obj, false)
502
- while (parentPath) {
503
- reversedPrefix.push(parentPath.path)
504
- emitDeepChangeForTarget(parentPath.parent, change, reversedPrefix, reversedPrefix.length)
505
-
506
- parentPath = fastGetParentPath(parentPath.parent, false)
507
- }
508
- }
509
-
510
434
  // `reversedPrefix` holds the path segments from the changed node up to (but not
511
- // including) `obj`, in child-to-root order; the emitted path prepends that slice
512
- // reversed (root-to-child). `prefixCount` is how many leading entries apply.
435
+ // including) the listened node, in child-to-root order; the emitted path
436
+ // prepends it reversed (root-to-child).
513
437
  function emitDeepChangeForTarget(
514
- obj: object,
438
+ listenersForObject: readonly ListenerSubscription<OnDeepChangeListener>[],
515
439
  change: DeepChange,
516
- reversedPrefix: readonly PathElement[],
517
- prefixCount: number
440
+ reversedPrefix: readonly PathElement[]
518
441
  ): void {
519
- const listenersForObject = deepChangeListeners.get(obj)
520
-
521
- if (!listenersForObject || listenersForObject.length === 0) {
522
- return
523
- }
524
-
442
+ const prefixCount = reversedPrefix.length
525
443
  const prefixedChange =
526
444
  prefixCount > 0
527
445
  ? changeWithPath(change, buildPrefixedPath(reversedPrefix, prefixCount, change.path))
@@ -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"
@@ -89,7 +89,7 @@ export abstract class BaseModel<
89
89
  * By default it will use the `idProp` if available or return `undefined` otherwise.
90
90
  */
91
91
  getRefId(): string | undefined {
92
- return this[modelIdKey]
92
+ return this[modelIdKey] as string | undefined
93
93
  }
94
94
 
95
95
  /**
@@ -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
  }
@@ -39,15 +39,10 @@ export function assertIsModel(
39
39
  * @internal
40
40
  */
41
41
  export function isModelClass(modelClass: unknown): modelClass is ModelClass<AnyModel> {
42
- if (typeof modelClass !== "function") {
43
- return false
44
- }
45
-
46
- if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
47
- return false
48
- }
49
-
50
- return true
42
+ return (
43
+ typeof modelClass === "function" &&
44
+ (modelClass === BaseModel || modelClass.prototype instanceof BaseModel)
45
+ )
51
46
  }
52
47
 
53
48
  /**
@@ -61,7 +56,7 @@ export function assertIsModelClass(
61
56
  throw failure(`${argName} must be a class`)
62
57
  }
63
58
 
64
- if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
59
+ if (!isModelClass(modelClass)) {
65
60
  throw failure(`${argName} must extend Model`)
66
61
  }
67
62
  }
@@ -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,
@@ -36,11 +37,17 @@ import type { AnyFunction } from "../utils/AnyFunction"
36
37
  */
37
38
  export const model =
38
39
  (name: string) =>
39
- <MC extends ModelClass<AnyModel | AnyDataModel>>(
40
- clazz: MC,
41
- _context?: ClassDecoratorContext<MC>
42
- ): MC =>
43
- internalModel(name, clazz) as any
40
+ <MC extends ModelDecoratorTarget>(clazz: MC, _context?: ClassDecoratorContext<MC>): MC =>
41
+ internalModel(name, clazz as unknown as ModelClass<AnyModel | AnyDataModel>) as any
42
+
43
+ // the model instance type is checked through `prototype` rather than through a typed constructor
44
+ // signature, since comparing constructor signatures would compute the (expensive) model creation data
45
+ // type of every decorated class
46
+ type ModelDecoratorTarget = (new (
47
+ ...args: any
48
+ ) => any) & {
49
+ prototype: AnyModel | AnyDataModel
50
+ }
44
51
 
45
52
  interface AfterClassInitializationData {
46
53
  needsMakeObservable: boolean | undefined
@@ -55,33 +62,12 @@ const afterClassInitializationData = new WeakMap<
55
62
  const runAfterClassInitialization = (tag: AfterClassInitializationData, instance: any) => {
56
63
  runLateInitializationFunctions(instance, runAfterNewSymbol)
57
64
 
58
- // compatibility with mobx 6 and 7
65
+ // MobX 6 legacy decorators need makeObservable, while MobX 7 annotations (see
66
+ // decoratedModel) and 2022.3 decorators initialize themselves. Otherwise it
67
+ // would only add an empty observable administration to every instance.
68
+ tag.needsMakeObservable ??= getMobxVersion() === 6 && hasStoredMobxAnnotations(instance)
59
69
  if (tag.needsMakeObservable) {
60
- // we know it can be done and shouldn't fail
61
70
  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
71
  }
86
72
 
87
73
  // the object is ready