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.
- package/CHANGELOG.md +33 -0
- package/README.md +1 -1
- package/dist/mobx-keystone.esm.js +2561 -1214
- package/dist/mobx-keystone.esm.mjs +2561 -1214
- package/dist/mobx-keystone.umd.js +2566 -1220
- package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +11 -12
- package/dist/types/modelShared/modelDecorator.d.ts +5 -2
- package/dist/types/modelShared/prop.d.ts +42 -29
- 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/Ref.d.ts +1 -4
- 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 +4 -4
- package/dist/types/snapshot/applySnapshot.d.ts +2 -1
- package/dist/types/snapshot/fromSnapshot.d.ts +2 -5
- package/dist/types/snapshot/getSnapshot.d.ts +1 -1
- package/dist/types/tweaker/changeRollback.d.ts +1 -0
- package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/schemas.d.ts +41 -37
- package/dist/types/types/tProp.d.ts +12 -6
- package/dist/types/types/types.d.ts +6 -6
- package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
- package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
- package/dist/types/utils/ModelPool.d.ts +7 -2
- package/dist/types/utils/chunks.d.ts +1 -0
- package/dist/types/utils/types.d.ts +6 -0
- package/dist/types/wrappers/ArraySet.d.ts +1 -0
- package/package.json +5 -3
- package/src/action/modelFlow.ts +35 -66
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
- package/src/actionMiddlewares/transactionMiddleware.ts +41 -42
- package/src/actionMiddlewares/undoMiddleware.ts +63 -35
- package/src/context/context.ts +15 -36
- package/src/dataModel/utils.ts +5 -28
- package/src/deepChange/onDeepChange.ts +44 -126
- package/src/model/BaseModel.ts +7 -5
- package/src/model/utils.ts +5 -10
- package/src/modelShared/modelDecorator.ts +16 -30
- package/src/modelShared/prop.ts +115 -111
- package/src/modelShared/sharedInternalModel.ts +2 -2
- package/src/parent/arrayPaths.ts +239 -0
- package/src/parent/coreObjectChildren.ts +300 -48
- package/src/parent/onChildAttachedTo.ts +130 -44
- package/src/parent/path.ts +10 -12
- 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/patch/patchRecorder.ts +13 -28
- package/src/ref/core.ts +69 -46
- package/src/ref/modelRefIdIndex.ts +131 -0
- package/src/ref/nodesById.ts +60 -0
- package/src/ref/refIdIndex.ts +209 -0
- package/src/rootStore/rootStore.ts +1 -3
- package/src/snapshot/SnapshotOf.ts +24 -48
- package/src/snapshot/applySnapshot.ts +27 -19
- package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
- package/src/snapshot/fromSnapshot.ts +12 -9
- package/src/snapshot/getSnapshot.ts +1 -1
- package/src/snapshot/internal.ts +67 -12
- package/src/snapshot/reconcileModelSnapshot.ts +5 -3
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
- package/src/treeUtils/draft.ts +6 -1
- package/src/treeUtils/sandbox.ts +10 -7
- 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 +127 -77
- package/src/tweaker/tweakPlainObject.ts +65 -46
- package/src/tweaker/tweakedChangeListeners.ts +61 -0
- package/src/tweaker/typeChecking.ts +5 -104
- package/src/types/arrayBased/typesArray.ts +23 -30
- package/src/types/arrayBased/typesTuple.ts +16 -25
- package/src/types/createCachedTypeCheck.ts +509 -94
- package/src/types/index.ts +1 -0
- package/src/types/objectBased/typesDataModelData.ts +7 -8
- package/src/types/objectBased/typesObject.ts +11 -15
- package/src/types/objectBased/typesRecord.ts +12 -19
- package/src/types/schemas.ts +59 -71
- package/src/types/tProp.ts +29 -29
- package/src/types/utility/typeToStoredData.ts +22 -21
- package/src/types/utility/typesCodecCore.ts +5 -0
- package/src/types/utility/typesCodecSupport.ts +3 -3
- package/src/types/utility/typesMap.ts +43 -51
- package/src/types/utility/typesRefinement.ts +7 -8
- package/src/types/utility/typesSkipCheck.ts +7 -8
- package/src/types/utility/typesTag.ts +7 -8
- package/src/utils/ModelPool.ts +76 -37
- package/src/utils/chunks.ts +271 -0
- package/src/utils/decorators.ts +5 -5
- package/src/utils/index.ts +38 -20
- package/src/utils/types.ts +5 -0
- package/src/wrappers/ArraySet.ts +27 -14
- package/src/wrappers/ObjectMap.ts +1 -6
- package/src/wrappers/asMap.ts +1 -21
- package/src/wrappers/asSet.ts +0 -8
package/src/context/context.ts
CHANGED
|
@@ -162,25 +162,19 @@ class ContextClass<T> implements Context<T> {
|
|
|
162
162
|
})
|
|
163
163
|
})
|
|
164
164
|
|
|
165
|
-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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) {
|
package/src/dataModel/utils.ts
CHANGED
|
@@ -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
|
-
|
|
39
|
-
|
|
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 (
|
|
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 (
|
|
213
|
-
|
|
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 (
|
|
312
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
387
|
-
|
|
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)
|
|
512
|
-
// reversed (root-to-child).
|
|
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
|
-
|
|
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
|
|
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))
|
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"
|
|
@@ -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(
|
|
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
|
}
|
package/src/model/utils.ts
CHANGED
|
@@ -39,15 +39,10 @@ export function assertIsModel(
|
|
|
39
39
|
* @internal
|
|
40
40
|
*/
|
|
41
41
|
export function isModelClass(modelClass: unknown): modelClass is ModelClass<AnyModel> {
|
|
42
|
-
|
|
43
|
-
|
|
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 (
|
|
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
|
|
40
|
-
clazz
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
//
|
|
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
|