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
package/src/model/utils.ts
CHANGED
|
@@ -1,249 +1,249 @@
|
|
|
1
|
-
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
2
|
-
import { isDataModel, isDataModelClass } from "../dataModel/utils"
|
|
3
|
-
import type { ModelClass } from "../modelShared/BaseModelShared"
|
|
4
|
-
import { getModelInfoForName, ModelInfo, modelInfoByClass } from "../modelShared/modelInfo"
|
|
5
|
-
import { failure, isPlainObject } from "../utils"
|
|
6
|
-
import { AnyModel, BaseModel } from "./BaseModel"
|
|
7
|
-
import { getModelIdPropertyName } from "./getModelMetadata"
|
|
8
|
-
import { modelIdKey, modelTypeKey } from "./metadata"
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Checks if an object is a model instance.
|
|
12
|
-
*
|
|
13
|
-
* @param model
|
|
14
|
-
* @returns
|
|
15
|
-
*/
|
|
16
|
-
export function isModel(model: unknown): model is AnyModel {
|
|
17
|
-
return model instanceof BaseModel
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @internal
|
|
22
|
-
*
|
|
23
|
-
* Asserts something is actually a model.
|
|
24
|
-
*
|
|
25
|
-
* @param model
|
|
26
|
-
* @param argName
|
|
27
|
-
*/
|
|
28
|
-
export function assertIsModel(
|
|
29
|
-
model: unknown,
|
|
30
|
-
argName: string,
|
|
31
|
-
customErrMsg = "must be a model instance"
|
|
32
|
-
): asserts model is AnyModel {
|
|
33
|
-
if (!isModel(model)) {
|
|
34
|
-
throw failure(`${argName} ${customErrMsg}`)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @internal
|
|
40
|
-
*/
|
|
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
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @internal
|
|
55
|
-
*/
|
|
56
|
-
export function assertIsModelClass(
|
|
57
|
-
modelClass: unknown,
|
|
58
|
-
argName: string
|
|
59
|
-
): asserts modelClass is ModelClass<AnyModel> {
|
|
60
|
-
if (typeof modelClass !== "function") {
|
|
61
|
-
throw failure(`${argName} must be a class`)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
|
|
65
|
-
throw failure(`${argName} must extend Model`)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Gets the model type name from a model snapshot.
|
|
71
|
-
*
|
|
72
|
-
* @param snapshot - The snapshot to get the model type from.
|
|
73
|
-
* @returns The model type name if the snapshot is a model snapshot, `undefined` otherwise.
|
|
74
|
-
*/
|
|
75
|
-
export function getSnapshotModelType(snapshot: unknown): string | undefined {
|
|
76
|
-
if (isPlainObject(snapshot) && modelTypeKey in snapshot) {
|
|
77
|
-
const modelType = (snapshot as any)[modelTypeKey]
|
|
78
|
-
return typeof modelType === "string" ? modelType : undefined
|
|
79
|
-
}
|
|
80
|
-
return undefined
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Gets the model ID from a model snapshot.
|
|
85
|
-
*
|
|
86
|
-
* This function extracts the model's unique identifier from a snapshot
|
|
87
|
-
* by looking up the model class using `$modelType` and then reading
|
|
88
|
-
* the value of the ID property (as declared with `idProp`).
|
|
89
|
-
*
|
|
90
|
-
* @param snapshot - A model snapshot (must have `$modelType`).
|
|
91
|
-
* @returns The model ID if the snapshot has an ID property, or `undefined` if not.
|
|
92
|
-
*/
|
|
93
|
-
export function getSnapshotModelId(snapshot: unknown): string | undefined {
|
|
94
|
-
return getSnapshotModelTypeAndId(snapshot)?.modelId
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Ensures the given model classes are referenced at runtime and already present in the model registry.
|
|
99
|
-
*
|
|
100
|
-
* This helper is mostly useful when your app primarily uses snapshots and your bundler/compiler
|
|
101
|
-
* may elide otherwise-unused model imports.
|
|
102
|
-
*
|
|
103
|
-
* @param modelClasses Model and/or data model classes to keep as runtime references.
|
|
104
|
-
*/
|
|
105
|
-
export function registerModels(
|
|
106
|
-
...modelClasses: Array<ModelClass<AnyModel> | ModelClass<AnyDataModel>>
|
|
107
|
-
): void {
|
|
108
|
-
const len = modelClasses.length
|
|
109
|
-
for (let i = 0; i < len; i++) {
|
|
110
|
-
const modelClass = modelClasses[i]
|
|
111
|
-
|
|
112
|
-
if (!(isModelClass(modelClass) || isDataModelClass(modelClass))) {
|
|
113
|
-
throw failure(`modelClasses[${i}] must be a model class or data model class`)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!modelInfoByClass.get(modelClass)) {
|
|
117
|
-
const modelClassName = modelClass.name || "<anonymous>"
|
|
118
|
-
throw failure(
|
|
119
|
-
`modelClasses[${i}] (${modelClassName}) is not registered. ` +
|
|
120
|
-
"Ensure the class is decorated with @model and that its module has been evaluated."
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Model type and ID information.
|
|
128
|
-
*/
|
|
129
|
-
export interface ModelTypeAndId {
|
|
130
|
-
/**
|
|
131
|
-
* The model type name (from `$modelType`).
|
|
132
|
-
*/
|
|
133
|
-
modelType: string
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* The model ID value, or `undefined` if the model has no ID property.
|
|
137
|
-
*/
|
|
138
|
-
modelId: string | undefined
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* The name of the property that holds the model ID (as declared with `idProp`),
|
|
142
|
-
* or `undefined` if the model has no ID property.
|
|
143
|
-
*/
|
|
144
|
-
modelIdPropertyName: string | undefined
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* The model info (contains the model class and name).
|
|
148
|
-
*/
|
|
149
|
-
modelInfo: ModelInfo
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Gets the model type and ID from a model snapshot.
|
|
154
|
-
*
|
|
155
|
-
* The result includes:
|
|
156
|
-
* - `modelType`: The model type name (from `$modelType`)
|
|
157
|
-
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
158
|
-
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
159
|
-
* - `modelInfo`: The model info (contains the model class and name)
|
|
160
|
-
*
|
|
161
|
-
* @param snapshot - A model snapshot (must have `$modelType`).
|
|
162
|
-
* @returns The model type and ID info, or `undefined` if not a valid model snapshot.
|
|
163
|
-
*/
|
|
164
|
-
export function getSnapshotModelTypeAndId(snapshot: unknown): ModelTypeAndId | undefined {
|
|
165
|
-
const modelType = getSnapshotModelType(snapshot)
|
|
166
|
-
if (modelType === undefined) {
|
|
167
|
-
return undefined
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const modelInfo = getModelInfoForName(modelType)
|
|
171
|
-
if (!modelInfo) {
|
|
172
|
-
return undefined
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const modelIdPropertyName = getModelIdPropertyName(modelInfo.class as ModelClass<AnyModel>)
|
|
176
|
-
|
|
177
|
-
let modelId: string | undefined
|
|
178
|
-
if (modelIdPropertyName) {
|
|
179
|
-
const id = (snapshot as Record<string, unknown>)[modelIdPropertyName]
|
|
180
|
-
modelId = typeof id === "string" ? id : undefined
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return { modelType, modelId, modelIdPropertyName, modelInfo }
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Gets the model type and ID from a Model or DataModel instance.
|
|
188
|
-
*
|
|
189
|
-
* The result includes:
|
|
190
|
-
* - `modelType`: The model type name (from `$modelType`)
|
|
191
|
-
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
192
|
-
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
193
|
-
* - `modelInfo`: The model info (contains the model class and name)
|
|
194
|
-
*
|
|
195
|
-
* @param model - A Model or DataModel instance.
|
|
196
|
-
* @returns The model type and ID info, or `undefined` if not a valid model.
|
|
197
|
-
*/
|
|
198
|
-
export function getModelTypeAndId(model: AnyModel | AnyDataModel): ModelTypeAndId | undefined {
|
|
199
|
-
// Handle null/undefined
|
|
200
|
-
if (!model || typeof model !== "object") {
|
|
201
|
-
return undefined
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const modelType = (model as any)[modelTypeKey] as string | undefined
|
|
205
|
-
if (typeof modelType !== "string") {
|
|
206
|
-
return undefined
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const modelInfo = getModelInfoForName(modelType)
|
|
210
|
-
if (!modelInfo) {
|
|
211
|
-
return undefined
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const modelIdPropertyName = getModelIdPropertyName(modelInfo.class as ModelClass<AnyModel>)
|
|
215
|
-
|
|
216
|
-
let modelId: string | undefined
|
|
217
|
-
if (modelIdPropertyName) {
|
|
218
|
-
const id = (model as any)[modelIdKey] as string | undefined
|
|
219
|
-
modelId = typeof id === "string" ? id : undefined
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return { modelType, modelId, modelIdPropertyName, modelInfo }
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Gets the model type and ID from a Model instance, DataModel instance, or model snapshot.
|
|
227
|
-
*
|
|
228
|
-
* The result includes:
|
|
229
|
-
* - `modelType`: The model type name (from `$modelType`)
|
|
230
|
-
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
231
|
-
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
232
|
-
* - `modelInfo`: The model info (contains the model class and name)
|
|
233
|
-
*
|
|
234
|
-
* @param value - A Model instance, DataModel instance, or model snapshot.
|
|
235
|
-
* @returns The model type and ID info, or `undefined` if not a valid model or snapshot.
|
|
236
|
-
*/
|
|
237
|
-
export function getModelOrSnapshotTypeAndId(value: unknown): ModelTypeAndId | undefined {
|
|
238
|
-
// Handle null/undefined/primitives
|
|
239
|
-
if (!value || typeof value !== "object") {
|
|
240
|
-
return undefined
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (isModel(value) || isDataModel(value)) {
|
|
244
|
-
return getModelTypeAndId(value)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Try as snapshot
|
|
248
|
-
return getSnapshotModelTypeAndId(value)
|
|
249
|
-
}
|
|
1
|
+
import type { AnyDataModel } from "../dataModel/BaseDataModel"
|
|
2
|
+
import { isDataModel, isDataModelClass } from "../dataModel/utils"
|
|
3
|
+
import type { ModelClass } from "../modelShared/BaseModelShared"
|
|
4
|
+
import { getModelInfoForName, type ModelInfo, modelInfoByClass } from "../modelShared/modelInfo"
|
|
5
|
+
import { failure, isPlainObject } from "../utils"
|
|
6
|
+
import { type AnyModel, BaseModel } from "./BaseModel"
|
|
7
|
+
import { getModelIdPropertyName } from "./getModelMetadata"
|
|
8
|
+
import { modelIdKey, modelTypeKey } from "./metadata"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if an object is a model instance.
|
|
12
|
+
*
|
|
13
|
+
* @param model
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export function isModel(model: unknown): model is AnyModel {
|
|
17
|
+
return model instanceof BaseModel
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @internal
|
|
22
|
+
*
|
|
23
|
+
* Asserts something is actually a model.
|
|
24
|
+
*
|
|
25
|
+
* @param model
|
|
26
|
+
* @param argName
|
|
27
|
+
*/
|
|
28
|
+
export function assertIsModel(
|
|
29
|
+
model: unknown,
|
|
30
|
+
argName: string,
|
|
31
|
+
customErrMsg = "must be a model instance"
|
|
32
|
+
): asserts model is AnyModel {
|
|
33
|
+
if (!isModel(model)) {
|
|
34
|
+
throw failure(`${argName} ${customErrMsg}`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
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
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export function assertIsModelClass(
|
|
57
|
+
modelClass: unknown,
|
|
58
|
+
argName: string
|
|
59
|
+
): asserts modelClass is ModelClass<AnyModel> {
|
|
60
|
+
if (typeof modelClass !== "function") {
|
|
61
|
+
throw failure(`${argName} must be a class`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
|
|
65
|
+
throw failure(`${argName} must extend Model`)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets the model type name from a model snapshot.
|
|
71
|
+
*
|
|
72
|
+
* @param snapshot - The snapshot to get the model type from.
|
|
73
|
+
* @returns The model type name if the snapshot is a model snapshot, `undefined` otherwise.
|
|
74
|
+
*/
|
|
75
|
+
export function getSnapshotModelType(snapshot: unknown): string | undefined {
|
|
76
|
+
if (isPlainObject(snapshot) && modelTypeKey in snapshot) {
|
|
77
|
+
const modelType = (snapshot as any)[modelTypeKey]
|
|
78
|
+
return typeof modelType === "string" ? modelType : undefined
|
|
79
|
+
}
|
|
80
|
+
return undefined
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Gets the model ID from a model snapshot.
|
|
85
|
+
*
|
|
86
|
+
* This function extracts the model's unique identifier from a snapshot
|
|
87
|
+
* by looking up the model class using `$modelType` and then reading
|
|
88
|
+
* the value of the ID property (as declared with `idProp`).
|
|
89
|
+
*
|
|
90
|
+
* @param snapshot - A model snapshot (must have `$modelType`).
|
|
91
|
+
* @returns The model ID if the snapshot has an ID property, or `undefined` if not.
|
|
92
|
+
*/
|
|
93
|
+
export function getSnapshotModelId(snapshot: unknown): string | undefined {
|
|
94
|
+
return getSnapshotModelTypeAndId(snapshot)?.modelId
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Ensures the given model classes are referenced at runtime and already present in the model registry.
|
|
99
|
+
*
|
|
100
|
+
* This helper is mostly useful when your app primarily uses snapshots and your bundler/compiler
|
|
101
|
+
* may elide otherwise-unused model imports.
|
|
102
|
+
*
|
|
103
|
+
* @param modelClasses Model and/or data model classes to keep as runtime references.
|
|
104
|
+
*/
|
|
105
|
+
export function registerModels(
|
|
106
|
+
...modelClasses: Array<ModelClass<AnyModel> | ModelClass<AnyDataModel>>
|
|
107
|
+
): void {
|
|
108
|
+
const len = modelClasses.length
|
|
109
|
+
for (let i = 0; i < len; i++) {
|
|
110
|
+
const modelClass = modelClasses[i]
|
|
111
|
+
|
|
112
|
+
if (!(isModelClass(modelClass) || isDataModelClass(modelClass))) {
|
|
113
|
+
throw failure(`modelClasses[${i}] must be a model class or data model class`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!modelInfoByClass.get(modelClass)) {
|
|
117
|
+
const modelClassName = modelClass.name || "<anonymous>"
|
|
118
|
+
throw failure(
|
|
119
|
+
`modelClasses[${i}] (${modelClassName}) is not registered. ` +
|
|
120
|
+
"Ensure the class is decorated with @model and that its module has been evaluated."
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Model type and ID information.
|
|
128
|
+
*/
|
|
129
|
+
export interface ModelTypeAndId {
|
|
130
|
+
/**
|
|
131
|
+
* The model type name (from `$modelType`).
|
|
132
|
+
*/
|
|
133
|
+
modelType: string
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The model ID value, or `undefined` if the model has no ID property.
|
|
137
|
+
*/
|
|
138
|
+
modelId: string | undefined
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The name of the property that holds the model ID (as declared with `idProp`),
|
|
142
|
+
* or `undefined` if the model has no ID property.
|
|
143
|
+
*/
|
|
144
|
+
modelIdPropertyName: string | undefined
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The model info (contains the model class and name).
|
|
148
|
+
*/
|
|
149
|
+
modelInfo: ModelInfo
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Gets the model type and ID from a model snapshot.
|
|
154
|
+
*
|
|
155
|
+
* The result includes:
|
|
156
|
+
* - `modelType`: The model type name (from `$modelType`)
|
|
157
|
+
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
158
|
+
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
159
|
+
* - `modelInfo`: The model info (contains the model class and name)
|
|
160
|
+
*
|
|
161
|
+
* @param snapshot - A model snapshot (must have `$modelType`).
|
|
162
|
+
* @returns The model type and ID info, or `undefined` if not a valid model snapshot.
|
|
163
|
+
*/
|
|
164
|
+
export function getSnapshotModelTypeAndId(snapshot: unknown): ModelTypeAndId | undefined {
|
|
165
|
+
const modelType = getSnapshotModelType(snapshot)
|
|
166
|
+
if (modelType === undefined) {
|
|
167
|
+
return undefined
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const modelInfo = getModelInfoForName(modelType)
|
|
171
|
+
if (!modelInfo) {
|
|
172
|
+
return undefined
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const modelIdPropertyName = getModelIdPropertyName(modelInfo.class as ModelClass<AnyModel>)
|
|
176
|
+
|
|
177
|
+
let modelId: string | undefined
|
|
178
|
+
if (modelIdPropertyName) {
|
|
179
|
+
const id = (snapshot as Record<string, unknown>)[modelIdPropertyName]
|
|
180
|
+
modelId = typeof id === "string" ? id : undefined
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { modelType, modelId, modelIdPropertyName, modelInfo }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Gets the model type and ID from a Model or DataModel instance.
|
|
188
|
+
*
|
|
189
|
+
* The result includes:
|
|
190
|
+
* - `modelType`: The model type name (from `$modelType`)
|
|
191
|
+
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
192
|
+
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
193
|
+
* - `modelInfo`: The model info (contains the model class and name)
|
|
194
|
+
*
|
|
195
|
+
* @param model - A Model or DataModel instance.
|
|
196
|
+
* @returns The model type and ID info, or `undefined` if not a valid model.
|
|
197
|
+
*/
|
|
198
|
+
export function getModelTypeAndId(model: AnyModel | AnyDataModel): ModelTypeAndId | undefined {
|
|
199
|
+
// Handle null/undefined
|
|
200
|
+
if (!model || typeof model !== "object") {
|
|
201
|
+
return undefined
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const modelType = (model as any)[modelTypeKey] as string | undefined
|
|
205
|
+
if (typeof modelType !== "string") {
|
|
206
|
+
return undefined
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const modelInfo = getModelInfoForName(modelType)
|
|
210
|
+
if (!modelInfo) {
|
|
211
|
+
return undefined
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const modelIdPropertyName = getModelIdPropertyName(modelInfo.class as ModelClass<AnyModel>)
|
|
215
|
+
|
|
216
|
+
let modelId: string | undefined
|
|
217
|
+
if (modelIdPropertyName) {
|
|
218
|
+
const id = (model as any)[modelIdKey] as string | undefined
|
|
219
|
+
modelId = typeof id === "string" ? id : undefined
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { modelType, modelId, modelIdPropertyName, modelInfo }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Gets the model type and ID from a Model instance, DataModel instance, or model snapshot.
|
|
227
|
+
*
|
|
228
|
+
* The result includes:
|
|
229
|
+
* - `modelType`: The model type name (from `$modelType`)
|
|
230
|
+
* - `modelId`: The model ID value (or `undefined` if no ID property)
|
|
231
|
+
* - `modelIdPropertyName`: The name of the property that holds the model ID (or `undefined` if no ID property)
|
|
232
|
+
* - `modelInfo`: The model info (contains the model class and name)
|
|
233
|
+
*
|
|
234
|
+
* @param value - A Model instance, DataModel instance, or model snapshot.
|
|
235
|
+
* @returns The model type and ID info, or `undefined` if not a valid model or snapshot.
|
|
236
|
+
*/
|
|
237
|
+
export function getModelOrSnapshotTypeAndId(value: unknown): ModelTypeAndId | undefined {
|
|
238
|
+
// Handle null/undefined/primitives
|
|
239
|
+
if (!value || typeof value !== "object") {
|
|
240
|
+
return undefined
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (isModel(value) || isDataModel(value)) {
|
|
244
|
+
return getModelTypeAndId(value)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Try as snapshot
|
|
248
|
+
return getSnapshotModelTypeAndId(value)
|
|
249
|
+
}
|