mobx-keystone 1.20.0 → 1.22.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 +864 -853
- package/README.md +61 -61
- package/dist/mobx-keystone.esm.js +11336 -11148
- package/dist/mobx-keystone.esm.mjs +11336 -11148
- package/dist/mobx-keystone.umd.js +12229 -11738
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
- package/dist/types/modelShared/prop.d.ts +5 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
- package/dist/types/ref/Ref.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +1 -1
- package/dist/types/types/TypeChecker.d.ts +1 -1
- package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
- package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
- package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
- package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
- package/dist/types/types/objectBased/typesModel.d.ts +1 -1
- package/dist/types/types/objectBased/typesObject.d.ts +3 -3
- package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
- package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
- package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
- package/dist/types/types/types.d.ts +3 -3
- package/dist/types/types/utility/typesCodec.d.ts +1 -6
- package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
- package/dist/types/types/utility/typesOr.d.ts +1 -1
- package/dist/types/types/utility/typesRefinement.d.ts +1 -1
- package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
- package/dist/types/types/utility/typesTag.d.ts +1 -1
- package/dist/types/wrappers/ObjectMap.d.ts +2 -0
- package/dist/types/wrappers/asMap.d.ts +10 -6
- package/package.json +14 -12
- package/src/action/applyAction.ts +125 -114
- package/src/action/applyMethodCall.ts +37 -37
- package/src/action/isModelAction.ts +16 -16
- package/src/action/middleware.ts +137 -137
- package/src/action/modelFlow.ts +240 -240
- package/src/action/modelFlowPromiseGenerator.ts +127 -125
- package/src/action/pendingActions.ts +42 -42
- package/src/action/wrapInAction.ts +156 -156
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
- package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
- package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
- package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
- package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
- package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
- package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
- package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
- package/src/actionMiddlewares/undoMiddleware.ts +804 -796
- package/src/actionMiddlewares/utils.ts +2 -2
- package/src/computedTree/computedTree.ts +143 -143
- package/src/context/context.ts +1 -1
- package/src/dataModel/BaseDataModel.ts +194 -192
- package/src/dataModel/actions.ts +1 -1
- package/src/dataModel/newDataModel.ts +57 -57
- package/src/deepChange/index.ts +1 -1
- package/src/deepChange/onDeepChange.ts +244 -244
- package/src/frozen/Frozen.ts +146 -146
- package/src/globalConfig/globalConfig.ts +107 -107
- package/src/index.ts +24 -24
- package/src/model/BaseModel.ts +275 -273
- package/src/model/ModelConstructorOptions.ts +15 -15
- package/src/model/getModelMetadata.ts +51 -51
- package/src/model/index.ts +6 -6
- package/src/model/mixins.ts +288 -288
- package/src/model/newModel.ts +301 -301
- package/src/model/utils.ts +249 -249
- package/src/modelShared/modelDecorator.ts +260 -253
- package/src/modelShared/modelInfo.ts +50 -50
- package/src/modelShared/modelSymbols.ts +3 -3
- package/src/modelShared/newModel.ts +25 -25
- package/src/modelShared/prop.ts +713 -684
- package/src/modelShared/sharedInternalModel.ts +427 -417
- package/src/modelShared/utils.ts +24 -24
- package/src/parent/core.ts +65 -65
- package/src/parent/coreObjectChildren.ts +180 -180
- package/src/parent/detach.ts +47 -47
- package/src/parent/findParent.ts +1 -1
- package/src/parent/getChildrenObjects.ts +25 -25
- package/src/parent/path.ts +386 -381
- package/src/parent/setParent.ts +149 -149
- package/src/parent/walkTree.ts +1 -1
- package/src/patch/applyPatches.ts +187 -187
- package/src/patch/jsonPatch.ts +108 -108
- package/src/patch/patchRecorder.ts +6 -1
- package/src/redux/connectReduxDevTools.ts +39 -7
- package/src/redux/redux.ts +6 -2
- package/src/ref/Ref.ts +1 -1
- package/src/ref/core.ts +339 -345
- package/src/ref/customRef.ts +3 -3
- package/src/ref/rootRef.ts +100 -100
- package/src/rootStore/attachDetach.ts +89 -89
- package/src/rootStore/rootStore.ts +1 -1
- package/src/snapshot/SnapshotOf.ts +104 -104
- package/src/snapshot/SnapshotProcessingError.ts +45 -45
- package/src/snapshot/applySnapshot.ts +177 -181
- package/src/snapshot/cloneTreeValue.ts +30 -30
- package/src/snapshot/fromArraySnapshot.ts +27 -33
- package/src/snapshot/fromModelSnapshot.ts +70 -70
- package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
- package/src/snapshot/fromSnapshot.ts +198 -198
- package/src/snapshot/getSnapshot.ts +80 -79
- package/src/snapshot/index.ts +8 -8
- package/src/snapshot/internal.ts +230 -230
- package/src/snapshot/reconcileArraySnapshot.ts +70 -70
- package/src/snapshot/reconcileModelSnapshot.ts +155 -150
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
- package/src/snapshot/reconcileSnapshot.ts +88 -88
- package/src/standardActions/actions.ts +64 -64
- package/src/standardActions/objectActions.ts +60 -60
- package/src/transforms/ImmutableDate.ts +93 -93
- package/src/transforms/asMap.ts +50 -50
- package/src/transforms/asSet.ts +22 -22
- package/src/transforms/bigint.ts +20 -20
- package/src/transforms/date.ts +40 -40
- package/src/treeUtils/deepEquals.ts +55 -55
- package/src/treeUtils/sandbox.ts +6 -3
- package/src/tweaker/TweakerPriority.ts +9 -9
- package/src/tweaker/tweak.ts +4 -3
- package/src/tweaker/tweakArray.ts +530 -475
- package/src/tweaker/tweakFrozen.ts +42 -42
- package/src/tweaker/tweakModel.ts +1 -1
- package/src/tweaker/tweakPlainObject.ts +355 -355
- package/src/tweaker/typeChecking.ts +101 -101
- package/src/types/SnapshotTypeMismatchError.ts +46 -46
- package/src/types/TypeCheckError.ts +183 -184
- package/src/types/TypeChecker.ts +202 -185
- package/src/types/arrayBased/typesArray.ts +136 -137
- package/src/types/arrayBased/typesTuple.ts +138 -139
- package/src/types/createPerEntryCachedCheck.ts +166 -160
- package/src/types/index.ts +6 -6
- package/src/types/objectBased/typesArraySet.ts +133 -134
- package/src/types/objectBased/typesDataModelData.ts +203 -204
- package/src/types/objectBased/typesModel.ts +220 -221
- package/src/types/objectBased/typesObject.ts +308 -310
- package/src/types/objectBased/typesObjectMap.ts +142 -143
- package/src/types/objectBased/typesRecord.ts +156 -157
- package/src/types/objectBased/typesRef.ts +89 -89
- package/src/types/primitiveBased/typesEnum.ts +63 -63
- package/src/types/primitiveBased/typesPrimitive.ts +219 -220
- package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
- package/src/types/resolveTypeChecker.ts +78 -78
- package/src/types/schemas.ts +208 -208
- package/src/types/tProp.ts +277 -268
- package/src/types/typeCheck.ts +21 -21
- package/src/types/typeCheckErrorUtils.ts +23 -23
- package/src/types/types.ts +142 -142
- package/src/types/utility/typeToStoredData.ts +37 -37
- package/src/types/utility/typesBigInt.ts +15 -15
- package/src/types/utility/typesCodec.ts +41 -38
- package/src/types/utility/typesCodecCore.ts +178 -178
- package/src/types/utility/typesCodecSupport.ts +806 -806
- package/src/types/utility/typesDate.ts +27 -27
- package/src/types/utility/typesMap.ts +253 -222
- package/src/types/utility/typesOr.ts +263 -259
- package/src/types/utility/typesRefinement.ts +131 -125
- package/src/types/utility/typesSet.ts +110 -110
- package/src/types/utility/typesSkipCheck.ts +69 -70
- package/src/types/utility/typesTag.ts +88 -82
- package/src/types/utility/typesUnchecked.ts +40 -40
- package/src/utils/AnyFunction.ts +4 -4
- package/src/utils/ModelPool.ts +59 -59
- package/src/utils/chainFns.ts +18 -18
- package/src/utils/decorators.ts +274 -274
- package/src/utils/errorDiagnostics.ts +204 -204
- package/src/utils/index.ts +443 -372
- package/src/wrappers/ArraySet.ts +156 -156
- package/src/wrappers/ObjectMap.ts +155 -136
- package/src/wrappers/asMap.ts +65 -30
- package/src/wrappers/asSet.ts +3 -3
|
@@ -1,253 +1,260 @@
|
|
|
1
|
-
import { HookAction } from "../action/hookActions"
|
|
2
|
-
import { wrapModelMethodInActionIfNeeded } from "../action/wrapInAction"
|
|
3
|
-
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
4
|
-
import { isDataModelClass } from "../dataModel/utils"
|
|
5
|
-
import { enterInitPhase, exitInitPhase } from "../deepChange/onDeepChange"
|
|
6
|
-
import { getGlobalConfig } from "../globalConfig"
|
|
7
|
-
import type { AnyModel } from "../model/BaseModel"
|
|
8
|
-
import { modelTypeKey } from "../model/metadata"
|
|
9
|
-
import { isModelClass } from "../model/utils"
|
|
10
|
-
import { ModelClass, modelInitializedSymbol } from "../modelShared/BaseModelShared"
|
|
11
|
-
import { modelInfoByClass, modelInfoByName } from "../modelShared/modelInfo"
|
|
12
|
-
import {
|
|
13
|
-
modelUnwrappedClassSymbol,
|
|
14
|
-
runAfterModelDecoratorSymbol,
|
|
15
|
-
} from "../modelShared/modelSymbols"
|
|
16
|
-
import { refreshInternalSnapshot } from "../snapshot/internal"
|
|
17
|
-
import {
|
|
18
|
-
failure,
|
|
19
|
-
getMobxVersion,
|
|
20
|
-
logWarning,
|
|
21
|
-
mobx6,
|
|
22
|
-
runAfterNewSymbol,
|
|
23
|
-
runBeforeOnInitSymbol,
|
|
24
|
-
runLateInitializationFunctions,
|
|
25
|
-
} from "../utils"
|
|
26
|
-
import { AnyFunction } from "../utils/AnyFunction"
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Decorator that marks this class (which MUST inherit from the `Model` or `DataModel` abstract classes)
|
|
30
|
-
* as a model.
|
|
31
|
-
*
|
|
32
|
-
* @param name Unique name for the model type. Note that this name must be unique for your whole
|
|
33
|
-
* application, so it is usually a good idea to use some prefix unique to your application domain.
|
|
34
|
-
*/
|
|
35
|
-
export const model =
|
|
36
|
-
(name: string) =>
|
|
37
|
-
<MC extends ModelClass<AnyModel | AnyDataModel>>(clazz: MC, ...args: any[]): MC => {
|
|
38
|
-
const ctx = typeof args[1] === "object" ? (args[1] as ClassDecoratorContext) : undefined
|
|
39
|
-
|
|
40
|
-
return internalModel(name, clazz, ctx?.addInitializer) as any
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const afterClassInitializationData = new WeakMap<
|
|
44
|
-
ModelClass<AnyModel | AnyDataModel>,
|
|
45
|
-
{
|
|
46
|
-
needsMakeObservable: boolean | undefined
|
|
47
|
-
type: "class" | "data"
|
|
48
|
-
}
|
|
49
|
-
>()
|
|
50
|
-
|
|
51
|
-
const runAfterClassInitialization = (
|
|
52
|
-
target: ModelClass<AnyModel | AnyDataModel>,
|
|
53
|
-
instance: any
|
|
54
|
-
) => {
|
|
55
|
-
runLateInitializationFunctions(instance, runAfterNewSymbol)
|
|
56
|
-
|
|
57
|
-
const tag = afterClassInitializationData.get(target)!
|
|
58
|
-
|
|
59
|
-
// compatibility with mobx 6
|
|
60
|
-
if (tag.needsMakeObservable) {
|
|
61
|
-
// we know it can be done and shouldn't fail
|
|
62
|
-
mobx6.makeObservable(instance)
|
|
63
|
-
} else if (tag.needsMakeObservable === undefined) {
|
|
64
|
-
if (getMobxVersion() >= 6) {
|
|
65
|
-
try {
|
|
66
|
-
mobx6.makeObservable(instance)
|
|
67
|
-
tag.needsMakeObservable = true
|
|
68
|
-
} catch (e) {
|
|
69
|
-
const err = e as Error
|
|
70
|
-
if (
|
|
71
|
-
err.message !==
|
|
72
|
-
"[MobX] No annotations were passed to makeObservable, but no decorator members have been found either" &&
|
|
73
|
-
err.message !==
|
|
74
|
-
"[MobX] No annotations were passed to makeObservable, but no decorated members have been found either"
|
|
75
|
-
) {
|
|
76
|
-
throw err
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// sadly we need to use this hack since the PR to do this the proper way
|
|
80
|
-
// was rejected on the mobx side
|
|
81
|
-
tag.needsMakeObservable = false
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
tag.needsMakeObservable = false
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// the object is ready
|
|
89
|
-
instance[modelInitializedSymbol] = true
|
|
90
|
-
refreshInternalSnapshot(instance)
|
|
91
|
-
|
|
92
|
-
enterInitPhase()
|
|
93
|
-
try {
|
|
94
|
-
runLateInitializationFunctions(instance, runBeforeOnInitSymbol)
|
|
95
|
-
|
|
96
|
-
if (tag.type === "class" && instance.onInit) {
|
|
97
|
-
wrapModelMethodInActionIfNeeded(instance, "onInit", HookAction.OnInit)
|
|
98
|
-
|
|
99
|
-
instance.onInit()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (tag.type === "data" && instance.onLazyInit) {
|
|
103
|
-
wrapModelMethodInActionIfNeeded(instance, "onLazyInit", HookAction.OnLazyInit)
|
|
104
|
-
|
|
105
|
-
instance.onLazyInit()
|
|
106
|
-
}
|
|
107
|
-
} finally {
|
|
108
|
-
exitInitPhase()
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const proxyClassHandler: ProxyHandler<ModelClass<AnyModel | AnyDataModel>> = {
|
|
113
|
-
construct(clazz, args) {
|
|
114
|
-
const instance = new (clazz as any)(...args)
|
|
115
|
-
|
|
116
|
-
runAfterClassInitialization(clazz, instance)
|
|
117
|
-
|
|
118
|
-
return instance
|
|
119
|
-
},
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const internalModel = <MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
123
|
-
name: string,
|
|
124
|
-
clazz: MC,
|
|
125
|
-
addInitializer: ((initializer: (this: any) => void) => void) | undefined
|
|
126
|
-
): MC | void => {
|
|
127
|
-
const type = isModelClass(clazz) ? "class" : isDataModelClass(clazz) ? "data" : undefined
|
|
128
|
-
if (!type) {
|
|
129
|
-
throw failure(`clazz must be a class that extends from Model/DataModel`)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (modelInfoByName[name]) {
|
|
133
|
-
if (getGlobalConfig().showDuplicateModelNameWarnings) {
|
|
134
|
-
logWarning(
|
|
135
|
-
"warn",
|
|
136
|
-
`a model with name "${name}" already exists (if you are using hot-reloading you may safely ignore this warning)`,
|
|
137
|
-
`duplicateModelName - ${name}`
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (modelUnwrappedClassSymbol in clazz && clazz[modelUnwrappedClassSymbol] === clazz) {
|
|
143
|
-
throw failure("a class already decorated with `@model` cannot be re-decorated")
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
clazz.toString = () => `class ${clazz.name}#${name}`
|
|
147
|
-
if (type === "class") {
|
|
148
|
-
;(clazz as any)[modelTypeKey] = name
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// track if we fail so we only try it once per class
|
|
152
|
-
afterClassInitializationData.set(clazz, { needsMakeObservable: undefined, type })
|
|
153
|
-
|
|
154
|
-
if (addInitializer) {
|
|
155
|
-
// standard decorator API, avoid proxies
|
|
156
|
-
addInitializer(function (this: any) {
|
|
157
|
-
runAfterClassInitialization(clazz, this)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
const modelInfo = {
|
|
161
|
-
name,
|
|
162
|
-
class: clazz,
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
modelInfoByName[name] = modelInfo
|
|
166
|
-
|
|
167
|
-
modelInfoByClass.set(clazz, modelInfo)
|
|
168
|
-
|
|
169
|
-
runLateInitializationFunctions(clazz, runAfterModelDecoratorSymbol)
|
|
170
|
-
|
|
171
|
-
return undefined // use same class
|
|
172
|
-
} else {
|
|
173
|
-
// non-standard decorator API, use proxies
|
|
174
|
-
|
|
175
|
-
// trick so plain new works
|
|
176
|
-
const proxyClass = new Proxy<MC>(clazz, proxyClassHandler)
|
|
177
|
-
|
|
178
|
-
// set or else it points to the undecorated class
|
|
179
|
-
proxyClass.prototype.constructor = proxyClass
|
|
180
|
-
;(proxyClass as any)[modelUnwrappedClassSymbol] = clazz
|
|
181
|
-
|
|
182
|
-
const modelInfo = {
|
|
183
|
-
name,
|
|
184
|
-
class: proxyClass,
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
modelInfoByName[name] = modelInfo
|
|
188
|
-
|
|
189
|
-
modelInfoByClass.set(proxyClass, modelInfo)
|
|
190
|
-
modelInfoByClass.set(clazz, modelInfo)
|
|
191
|
-
|
|
192
|
-
runLateInitializationFunctions(clazz, runAfterModelDecoratorSymbol)
|
|
193
|
-
|
|
194
|
-
return proxyClass
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// basically taken from TS
|
|
199
|
-
function tsDecorate(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
1
|
+
import { HookAction } from "../action/hookActions"
|
|
2
|
+
import { wrapModelMethodInActionIfNeeded } from "../action/wrapInAction"
|
|
3
|
+
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
4
|
+
import { isDataModelClass } from "../dataModel/utils"
|
|
5
|
+
import { enterInitPhase, exitInitPhase } from "../deepChange/onDeepChange"
|
|
6
|
+
import { getGlobalConfig } from "../globalConfig"
|
|
7
|
+
import type { AnyModel } from "../model/BaseModel"
|
|
8
|
+
import { modelTypeKey } from "../model/metadata"
|
|
9
|
+
import { isModelClass } from "../model/utils"
|
|
10
|
+
import { type ModelClass, modelInitializedSymbol } from "../modelShared/BaseModelShared"
|
|
11
|
+
import { modelInfoByClass, modelInfoByName } from "../modelShared/modelInfo"
|
|
12
|
+
import {
|
|
13
|
+
modelUnwrappedClassSymbol,
|
|
14
|
+
runAfterModelDecoratorSymbol,
|
|
15
|
+
} from "../modelShared/modelSymbols"
|
|
16
|
+
import { refreshInternalSnapshot } from "../snapshot/internal"
|
|
17
|
+
import {
|
|
18
|
+
failure,
|
|
19
|
+
getMobxVersion,
|
|
20
|
+
logWarning,
|
|
21
|
+
mobx6,
|
|
22
|
+
runAfterNewSymbol,
|
|
23
|
+
runBeforeOnInitSymbol,
|
|
24
|
+
runLateInitializationFunctions,
|
|
25
|
+
} from "../utils"
|
|
26
|
+
import type { AnyFunction } from "../utils/AnyFunction"
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Decorator that marks this class (which MUST inherit from the `Model` or `DataModel` abstract classes)
|
|
30
|
+
* as a model.
|
|
31
|
+
*
|
|
32
|
+
* @param name Unique name for the model type. Note that this name must be unique for your whole
|
|
33
|
+
* application, so it is usually a good idea to use some prefix unique to your application domain.
|
|
34
|
+
*/
|
|
35
|
+
export const model =
|
|
36
|
+
(name: string) =>
|
|
37
|
+
<MC extends ModelClass<AnyModel | AnyDataModel>>(clazz: MC, ...args: any[]): MC => {
|
|
38
|
+
const ctx = typeof args[1] === "object" ? (args[1] as ClassDecoratorContext) : undefined
|
|
39
|
+
|
|
40
|
+
return internalModel(name, clazz, ctx?.addInitializer) as any
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const afterClassInitializationData = new WeakMap<
|
|
44
|
+
ModelClass<AnyModel | AnyDataModel>,
|
|
45
|
+
{
|
|
46
|
+
needsMakeObservable: boolean | undefined
|
|
47
|
+
type: "class" | "data"
|
|
48
|
+
}
|
|
49
|
+
>()
|
|
50
|
+
|
|
51
|
+
const runAfterClassInitialization = (
|
|
52
|
+
target: ModelClass<AnyModel | AnyDataModel>,
|
|
53
|
+
instance: any
|
|
54
|
+
) => {
|
|
55
|
+
runLateInitializationFunctions(instance, runAfterNewSymbol)
|
|
56
|
+
|
|
57
|
+
const tag = afterClassInitializationData.get(target)!
|
|
58
|
+
|
|
59
|
+
// compatibility with mobx 6
|
|
60
|
+
if (tag.needsMakeObservable) {
|
|
61
|
+
// we know it can be done and shouldn't fail
|
|
62
|
+
mobx6.makeObservable(instance)
|
|
63
|
+
} else if (tag.needsMakeObservable === undefined) {
|
|
64
|
+
if (getMobxVersion() >= 6) {
|
|
65
|
+
try {
|
|
66
|
+
mobx6.makeObservable(instance)
|
|
67
|
+
tag.needsMakeObservable = true
|
|
68
|
+
} catch (e) {
|
|
69
|
+
const err = e as Error
|
|
70
|
+
if (
|
|
71
|
+
err.message !==
|
|
72
|
+
"[MobX] No annotations were passed to makeObservable, but no decorator members have been found either" &&
|
|
73
|
+
err.message !==
|
|
74
|
+
"[MobX] No annotations were passed to makeObservable, but no decorated members have been found either"
|
|
75
|
+
) {
|
|
76
|
+
throw err
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// sadly we need to use this hack since the PR to do this the proper way
|
|
80
|
+
// was rejected on the mobx side
|
|
81
|
+
tag.needsMakeObservable = false
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
tag.needsMakeObservable = false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// the object is ready
|
|
89
|
+
instance[modelInitializedSymbol] = true
|
|
90
|
+
refreshInternalSnapshot(instance)
|
|
91
|
+
|
|
92
|
+
enterInitPhase()
|
|
93
|
+
try {
|
|
94
|
+
runLateInitializationFunctions(instance, runBeforeOnInitSymbol)
|
|
95
|
+
|
|
96
|
+
if (tag.type === "class" && instance.onInit) {
|
|
97
|
+
wrapModelMethodInActionIfNeeded(instance, "onInit", HookAction.OnInit)
|
|
98
|
+
|
|
99
|
+
instance.onInit()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (tag.type === "data" && instance.onLazyInit) {
|
|
103
|
+
wrapModelMethodInActionIfNeeded(instance, "onLazyInit", HookAction.OnLazyInit)
|
|
104
|
+
|
|
105
|
+
instance.onLazyInit()
|
|
106
|
+
}
|
|
107
|
+
} finally {
|
|
108
|
+
exitInitPhase()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const proxyClassHandler: ProxyHandler<ModelClass<AnyModel | AnyDataModel>> = {
|
|
113
|
+
construct(clazz, args) {
|
|
114
|
+
const instance = new (clazz as any)(...args)
|
|
115
|
+
|
|
116
|
+
runAfterClassInitialization(clazz, instance)
|
|
117
|
+
|
|
118
|
+
return instance
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const internalModel = <MC extends ModelClass<AnyModel | AnyDataModel>>(
|
|
123
|
+
name: string,
|
|
124
|
+
clazz: MC,
|
|
125
|
+
addInitializer: ((initializer: (this: any) => void) => void) | undefined
|
|
126
|
+
): MC | void => {
|
|
127
|
+
const type = isModelClass(clazz) ? "class" : isDataModelClass(clazz) ? "data" : undefined
|
|
128
|
+
if (!type) {
|
|
129
|
+
throw failure(`clazz must be a class that extends from Model/DataModel`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (modelInfoByName[name]) {
|
|
133
|
+
if (getGlobalConfig().showDuplicateModelNameWarnings) {
|
|
134
|
+
logWarning(
|
|
135
|
+
"warn",
|
|
136
|
+
`a model with name "${name}" already exists (if you are using hot-reloading you may safely ignore this warning)`,
|
|
137
|
+
`duplicateModelName - ${name}`
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (modelUnwrappedClassSymbol in clazz && clazz[modelUnwrappedClassSymbol] === clazz) {
|
|
143
|
+
throw failure("a class already decorated with `@model` cannot be re-decorated")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
clazz.toString = () => `class ${clazz.name}#${name}`
|
|
147
|
+
if (type === "class") {
|
|
148
|
+
;(clazz as any)[modelTypeKey] = name
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// track if we fail so we only try it once per class
|
|
152
|
+
afterClassInitializationData.set(clazz, { needsMakeObservable: undefined, type })
|
|
153
|
+
|
|
154
|
+
if (addInitializer) {
|
|
155
|
+
// standard decorator API, avoid proxies
|
|
156
|
+
addInitializer(function (this: any) {
|
|
157
|
+
runAfterClassInitialization(clazz, this)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const modelInfo = {
|
|
161
|
+
name,
|
|
162
|
+
class: clazz,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
modelInfoByName[name] = modelInfo
|
|
166
|
+
|
|
167
|
+
modelInfoByClass.set(clazz, modelInfo)
|
|
168
|
+
|
|
169
|
+
runLateInitializationFunctions(clazz, runAfterModelDecoratorSymbol)
|
|
170
|
+
|
|
171
|
+
return undefined // use same class
|
|
172
|
+
} else {
|
|
173
|
+
// non-standard decorator API, use proxies
|
|
174
|
+
|
|
175
|
+
// trick so plain new works
|
|
176
|
+
const proxyClass = new Proxy<MC>(clazz, proxyClassHandler)
|
|
177
|
+
|
|
178
|
+
// set or else it points to the undecorated class
|
|
179
|
+
proxyClass.prototype.constructor = proxyClass
|
|
180
|
+
;(proxyClass as any)[modelUnwrappedClassSymbol] = clazz
|
|
181
|
+
|
|
182
|
+
const modelInfo = {
|
|
183
|
+
name,
|
|
184
|
+
class: proxyClass,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
modelInfoByName[name] = modelInfo
|
|
188
|
+
|
|
189
|
+
modelInfoByClass.set(proxyClass, modelInfo)
|
|
190
|
+
modelInfoByClass.set(clazz, modelInfo)
|
|
191
|
+
|
|
192
|
+
runLateInitializationFunctions(clazz, runAfterModelDecoratorSymbol)
|
|
193
|
+
|
|
194
|
+
return proxyClass
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// basically taken from TS
|
|
199
|
+
function tsDecorate(
|
|
200
|
+
...args:
|
|
201
|
+
| [decorators: any, target: any]
|
|
202
|
+
| [decorators: any, target: any, key: any]
|
|
203
|
+
| [decorators: any, target: any, key: any, desc: any]
|
|
204
|
+
) {
|
|
205
|
+
const [decorators, target, key] = args
|
|
206
|
+
let desc = args[3]
|
|
207
|
+
const c = args.length
|
|
208
|
+
let r =
|
|
209
|
+
c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc
|
|
210
|
+
let d: any
|
|
211
|
+
if (typeof Reflect === "object" && typeof (Reflect as any).decorate === "function") {
|
|
212
|
+
r = (Reflect as any).decorate(decorators, target, key, desc)
|
|
213
|
+
} else {
|
|
214
|
+
for (
|
|
215
|
+
// biome-ignore lint/correctness/noInnerDeclarations: minified file
|
|
216
|
+
var i = decorators.length - 1;
|
|
217
|
+
i >= 0;
|
|
218
|
+
i--
|
|
219
|
+
) {
|
|
220
|
+
if ((d = decorators[i])) {
|
|
221
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// biome-ignore lint/complexity/noCommaOperator: minified file
|
|
226
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Marks a class (which MUST inherit from the `Model` abstract class)
|
|
231
|
+
* as a model and decorates some of its methods/properties.
|
|
232
|
+
*
|
|
233
|
+
* @param name Unique name for the model type. Note that this name must be unique for your whole
|
|
234
|
+
* application, so it is usually a good idea to use some prefix unique to your application domain.
|
|
235
|
+
* If you don't want to assign a name yet (e.g. for a base model) pass `undefined`.
|
|
236
|
+
* @param clazz Model class.
|
|
237
|
+
* @param decorators Decorators.
|
|
238
|
+
*/
|
|
239
|
+
export function decoratedModel<M, MC extends abstract new (...ags: any) => M>(
|
|
240
|
+
name: string | undefined,
|
|
241
|
+
clazz: MC,
|
|
242
|
+
decorators: {
|
|
243
|
+
[k in keyof M]?: AnyFunction | ReadonlyArray<AnyFunction>
|
|
244
|
+
}
|
|
245
|
+
): MC {
|
|
246
|
+
// decorate class members
|
|
247
|
+
for (const [k, decorator] of Object.entries(decorators)) {
|
|
248
|
+
const prototypeValueDesc = Object.getOwnPropertyDescriptor(clazz.prototype, k)
|
|
249
|
+
// TS seems to send null for methods in the prototype
|
|
250
|
+
// (which we substitute for the descriptor to avoid a double look-up) and void 0 (undefined) for props
|
|
251
|
+
tsDecorate(
|
|
252
|
+
Array.isArray(decorator) ? decorator : [decorator],
|
|
253
|
+
clazz.prototype,
|
|
254
|
+
k,
|
|
255
|
+
prototypeValueDesc ? prototypeValueDesc : void 0
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (name ? model(name)(clazz as unknown as ModelClass<AnyModel>) : clazz) as MC
|
|
260
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
2
|
-
import type { AnyModel } from "../model/BaseModel"
|
|
3
|
-
import type { ModelClass } from "./BaseModelShared"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Information about a registered model.
|
|
7
|
-
*/
|
|
8
|
-
export interface ModelInfo {
|
|
9
|
-
name: string
|
|
10
|
-
class: ModelClass<AnyModel | AnyDataModel>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
export const modelInfoByName: {
|
|
17
|
-
[name: string]: ModelInfo
|
|
18
|
-
} = {}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
export const modelInfoByClass = new WeakMap<ModelClass<AnyModel | AnyDataModel>, ModelInfo>()
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Returns the model info for a model type name, or `undefined` if not found.
|
|
27
|
-
*
|
|
28
|
-
* @param name The model type name (from `$modelType`).
|
|
29
|
-
* @returns The model info or `undefined`.
|
|
30
|
-
*/
|
|
31
|
-
export function getModelInfoForName(name: string): ModelInfo | undefined {
|
|
32
|
-
return modelInfoByName[name]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Builds a helpful error message when a model type cannot be resolved from the runtime registry.
|
|
37
|
-
*
|
|
38
|
-
* @param name The model type name (from `$modelType`).
|
|
39
|
-
* @returns A diagnostic message with common remediation steps.
|
|
40
|
-
*/
|
|
41
|
-
export function getModelNotRegisteredErrorMessage(name: string): string {
|
|
42
|
-
return (
|
|
43
|
-
`model with name "${name}" not found in the registry. ` +
|
|
44
|
-
"This usually means the model module was not imported at runtime " +
|
|
45
|
-
"(for example, due to type-only imports or import elision). " +
|
|
46
|
-
"Import the model module for side effects, use runtime model references " +
|
|
47
|
-
"(for example `tProp(types.model(MyModel))` or `fromSnapshot(MyModel, snapshot)`), " +
|
|
48
|
-
"or call `registerModels(MyModel)` during startup."
|
|
49
|
-
)
|
|
50
|
-
}
|
|
1
|
+
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
2
|
+
import type { AnyModel } from "../model/BaseModel"
|
|
3
|
+
import type { ModelClass } from "./BaseModelShared"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Information about a registered model.
|
|
7
|
+
*/
|
|
8
|
+
export interface ModelInfo {
|
|
9
|
+
name: string
|
|
10
|
+
class: ModelClass<AnyModel | AnyDataModel>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export const modelInfoByName: {
|
|
17
|
+
[name: string]: ModelInfo
|
|
18
|
+
} = {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export const modelInfoByClass = new WeakMap<ModelClass<AnyModel | AnyDataModel>, ModelInfo>()
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the model info for a model type name, or `undefined` if not found.
|
|
27
|
+
*
|
|
28
|
+
* @param name The model type name (from `$modelType`).
|
|
29
|
+
* @returns The model info or `undefined`.
|
|
30
|
+
*/
|
|
31
|
+
export function getModelInfoForName(name: string): ModelInfo | undefined {
|
|
32
|
+
return modelInfoByName[name]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Builds a helpful error message when a model type cannot be resolved from the runtime registry.
|
|
37
|
+
*
|
|
38
|
+
* @param name The model type name (from `$modelType`).
|
|
39
|
+
* @returns A diagnostic message with common remediation steps.
|
|
40
|
+
*/
|
|
41
|
+
export function getModelNotRegisteredErrorMessage(name: string): string {
|
|
42
|
+
return (
|
|
43
|
+
`model with name "${name}" not found in the registry. ` +
|
|
44
|
+
"This usually means the model module was not imported at runtime " +
|
|
45
|
+
"(for example, due to type-only imports or import elision). " +
|
|
46
|
+
"Import the model module for side effects, use runtime model references " +
|
|
47
|
+
"(for example `tProp(types.model(MyModel))` or `fromSnapshot(MyModel, snapshot)`), " +
|
|
48
|
+
"or call `registerModels(MyModel)` during startup."
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const modelMetadataSymbol = Symbol("modelMetadata")
|
|
2
|
-
export const modelUnwrappedClassSymbol = Symbol("modelUnwrappedClass")
|
|
3
|
-
export const runAfterModelDecoratorSymbol = Symbol("runAfterModelDecorator")
|
|
1
|
+
export const modelMetadataSymbol = Symbol("modelMetadata")
|
|
2
|
+
export const modelUnwrappedClassSymbol = Symbol("modelUnwrappedClass")
|
|
3
|
+
export const runAfterModelDecoratorSymbol = Symbol("runAfterModelDecorator")
|