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.
Files changed (174) hide show
  1. package/CHANGELOG.md +864 -853
  2. package/README.md +61 -61
  3. package/dist/mobx-keystone.esm.js +11336 -11148
  4. package/dist/mobx-keystone.esm.mjs +11336 -11148
  5. package/dist/mobx-keystone.umd.js +12229 -11738
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
  7. package/dist/types/modelShared/prop.d.ts +5 -0
  8. package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
  9. package/dist/types/ref/Ref.d.ts +1 -1
  10. package/dist/types/treeUtils/sandbox.d.ts +1 -1
  11. package/dist/types/types/TypeChecker.d.ts +1 -1
  12. package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
  13. package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
  14. package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
  15. package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
  16. package/dist/types/types/objectBased/typesModel.d.ts +1 -1
  17. package/dist/types/types/objectBased/typesObject.d.ts +3 -3
  18. package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
  19. package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
  20. package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
  21. package/dist/types/types/types.d.ts +3 -3
  22. package/dist/types/types/utility/typesCodec.d.ts +1 -6
  23. package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
  24. package/dist/types/types/utility/typesOr.d.ts +1 -1
  25. package/dist/types/types/utility/typesRefinement.d.ts +1 -1
  26. package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
  27. package/dist/types/types/utility/typesTag.d.ts +1 -1
  28. package/dist/types/wrappers/ObjectMap.d.ts +2 -0
  29. package/dist/types/wrappers/asMap.d.ts +10 -6
  30. package/package.json +14 -12
  31. package/src/action/applyAction.ts +125 -114
  32. package/src/action/applyMethodCall.ts +37 -37
  33. package/src/action/isModelAction.ts +16 -16
  34. package/src/action/middleware.ts +137 -137
  35. package/src/action/modelFlow.ts +240 -240
  36. package/src/action/modelFlowPromiseGenerator.ts +127 -125
  37. package/src/action/pendingActions.ts +42 -42
  38. package/src/action/wrapInAction.ts +156 -156
  39. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
  40. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
  41. package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
  42. package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
  43. package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
  44. package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
  45. package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
  46. package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
  47. package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
  48. package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
  49. package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
  50. package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
  51. package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
  52. package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
  53. package/src/actionMiddlewares/undoMiddleware.ts +804 -796
  54. package/src/actionMiddlewares/utils.ts +2 -2
  55. package/src/computedTree/computedTree.ts +143 -143
  56. package/src/context/context.ts +1 -1
  57. package/src/dataModel/BaseDataModel.ts +194 -192
  58. package/src/dataModel/actions.ts +1 -1
  59. package/src/dataModel/newDataModel.ts +57 -57
  60. package/src/deepChange/index.ts +1 -1
  61. package/src/deepChange/onDeepChange.ts +244 -244
  62. package/src/frozen/Frozen.ts +146 -146
  63. package/src/globalConfig/globalConfig.ts +107 -107
  64. package/src/index.ts +24 -24
  65. package/src/model/BaseModel.ts +275 -273
  66. package/src/model/ModelConstructorOptions.ts +15 -15
  67. package/src/model/getModelMetadata.ts +51 -51
  68. package/src/model/index.ts +6 -6
  69. package/src/model/mixins.ts +288 -288
  70. package/src/model/newModel.ts +301 -301
  71. package/src/model/utils.ts +249 -249
  72. package/src/modelShared/modelDecorator.ts +260 -253
  73. package/src/modelShared/modelInfo.ts +50 -50
  74. package/src/modelShared/modelSymbols.ts +3 -3
  75. package/src/modelShared/newModel.ts +25 -25
  76. package/src/modelShared/prop.ts +713 -684
  77. package/src/modelShared/sharedInternalModel.ts +427 -417
  78. package/src/modelShared/utils.ts +24 -24
  79. package/src/parent/core.ts +65 -65
  80. package/src/parent/coreObjectChildren.ts +180 -180
  81. package/src/parent/detach.ts +47 -47
  82. package/src/parent/findParent.ts +1 -1
  83. package/src/parent/getChildrenObjects.ts +25 -25
  84. package/src/parent/path.ts +386 -381
  85. package/src/parent/setParent.ts +149 -149
  86. package/src/parent/walkTree.ts +1 -1
  87. package/src/patch/applyPatches.ts +187 -187
  88. package/src/patch/jsonPatch.ts +108 -108
  89. package/src/patch/patchRecorder.ts +6 -1
  90. package/src/redux/connectReduxDevTools.ts +39 -7
  91. package/src/redux/redux.ts +6 -2
  92. package/src/ref/Ref.ts +1 -1
  93. package/src/ref/core.ts +339 -345
  94. package/src/ref/customRef.ts +3 -3
  95. package/src/ref/rootRef.ts +100 -100
  96. package/src/rootStore/attachDetach.ts +89 -89
  97. package/src/rootStore/rootStore.ts +1 -1
  98. package/src/snapshot/SnapshotOf.ts +104 -104
  99. package/src/snapshot/SnapshotProcessingError.ts +45 -45
  100. package/src/snapshot/applySnapshot.ts +177 -181
  101. package/src/snapshot/cloneTreeValue.ts +30 -30
  102. package/src/snapshot/fromArraySnapshot.ts +27 -33
  103. package/src/snapshot/fromModelSnapshot.ts +70 -70
  104. package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
  105. package/src/snapshot/fromSnapshot.ts +198 -198
  106. package/src/snapshot/getSnapshot.ts +80 -79
  107. package/src/snapshot/index.ts +8 -8
  108. package/src/snapshot/internal.ts +230 -230
  109. package/src/snapshot/reconcileArraySnapshot.ts +70 -70
  110. package/src/snapshot/reconcileModelSnapshot.ts +155 -150
  111. package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
  112. package/src/snapshot/reconcileSnapshot.ts +88 -88
  113. package/src/standardActions/actions.ts +64 -64
  114. package/src/standardActions/objectActions.ts +60 -60
  115. package/src/transforms/ImmutableDate.ts +93 -93
  116. package/src/transforms/asMap.ts +50 -50
  117. package/src/transforms/asSet.ts +22 -22
  118. package/src/transforms/bigint.ts +20 -20
  119. package/src/transforms/date.ts +40 -40
  120. package/src/treeUtils/deepEquals.ts +55 -55
  121. package/src/treeUtils/sandbox.ts +6 -3
  122. package/src/tweaker/TweakerPriority.ts +9 -9
  123. package/src/tweaker/tweak.ts +4 -3
  124. package/src/tweaker/tweakArray.ts +530 -475
  125. package/src/tweaker/tweakFrozen.ts +42 -42
  126. package/src/tweaker/tweakModel.ts +1 -1
  127. package/src/tweaker/tweakPlainObject.ts +355 -355
  128. package/src/tweaker/typeChecking.ts +101 -101
  129. package/src/types/SnapshotTypeMismatchError.ts +46 -46
  130. package/src/types/TypeCheckError.ts +183 -184
  131. package/src/types/TypeChecker.ts +202 -185
  132. package/src/types/arrayBased/typesArray.ts +136 -137
  133. package/src/types/arrayBased/typesTuple.ts +138 -139
  134. package/src/types/createPerEntryCachedCheck.ts +166 -160
  135. package/src/types/index.ts +6 -6
  136. package/src/types/objectBased/typesArraySet.ts +133 -134
  137. package/src/types/objectBased/typesDataModelData.ts +203 -204
  138. package/src/types/objectBased/typesModel.ts +220 -221
  139. package/src/types/objectBased/typesObject.ts +308 -310
  140. package/src/types/objectBased/typesObjectMap.ts +142 -143
  141. package/src/types/objectBased/typesRecord.ts +156 -157
  142. package/src/types/objectBased/typesRef.ts +89 -89
  143. package/src/types/primitiveBased/typesEnum.ts +63 -63
  144. package/src/types/primitiveBased/typesPrimitive.ts +219 -220
  145. package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
  146. package/src/types/resolveTypeChecker.ts +78 -78
  147. package/src/types/schemas.ts +208 -208
  148. package/src/types/tProp.ts +277 -268
  149. package/src/types/typeCheck.ts +21 -21
  150. package/src/types/typeCheckErrorUtils.ts +23 -23
  151. package/src/types/types.ts +142 -142
  152. package/src/types/utility/typeToStoredData.ts +37 -37
  153. package/src/types/utility/typesBigInt.ts +15 -15
  154. package/src/types/utility/typesCodec.ts +41 -38
  155. package/src/types/utility/typesCodecCore.ts +178 -178
  156. package/src/types/utility/typesCodecSupport.ts +806 -806
  157. package/src/types/utility/typesDate.ts +27 -27
  158. package/src/types/utility/typesMap.ts +253 -222
  159. package/src/types/utility/typesOr.ts +263 -259
  160. package/src/types/utility/typesRefinement.ts +131 -125
  161. package/src/types/utility/typesSet.ts +110 -110
  162. package/src/types/utility/typesSkipCheck.ts +69 -70
  163. package/src/types/utility/typesTag.ts +88 -82
  164. package/src/types/utility/typesUnchecked.ts +40 -40
  165. package/src/utils/AnyFunction.ts +4 -4
  166. package/src/utils/ModelPool.ts +59 -59
  167. package/src/utils/chainFns.ts +18 -18
  168. package/src/utils/decorators.ts +274 -274
  169. package/src/utils/errorDiagnostics.ts +204 -204
  170. package/src/utils/index.ts +443 -372
  171. package/src/wrappers/ArraySet.ts +156 -156
  172. package/src/wrappers/ObjectMap.ts +155 -136
  173. package/src/wrappers/asMap.ts +65 -30
  174. package/src/wrappers/asSet.ts +3 -3
@@ -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
+ }