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,259 +1,263 @@
1
- import { failure, lazy } from "../../utils"
2
- import { getTypeInfo } from "../getTypeInfo"
3
- import {
4
- resolveStandardType,
5
- resolveStandardTypeNoThrow,
6
- resolveTypeChecker,
7
- } from "../resolveTypeChecker"
8
- import { SnapshotTypeMismatchError } from "../SnapshotTypeMismatchError"
9
- import type { AnyStandardType, AnyType } from "../schemas"
10
- import { TypeCheckError } from "../TypeCheckError"
11
- import {
12
- getTypeCheckerBaseTypeFromValue,
13
- lateTypeChecker,
14
- TypeChecker,
15
- TypeCheckerBaseType,
16
- TypeInfo,
17
- TypeInfoGen,
18
- } from "../TypeChecker"
19
- import { typesUnchecked } from "./typesUnchecked"
20
-
21
- /**
22
- * A type that represents the union of several other types (a | b | c | ...).
23
- * Accepts a dispatcher that, given a snapshot, returns the type
24
- * that snapshot is.
25
- *
26
- * @template T Type.
27
- * @param dispatcher Function that given a snapshot returns the type.
28
- * @param orTypes Possible types.
29
- * @returns
30
- */
31
- export function typesOr<T extends AnyType[]>(
32
- dispatcher: (sn: any) => T[number],
33
- ...orTypes: T
34
- ): T[number]
35
-
36
- /**
37
- * A type that represents the union of several other types (a | b | c | ...).
38
- *
39
- * Example:
40
- * ```ts
41
- * const booleanOrNumberType = types.or(types.boolean, types.number)
42
- * ```
43
- *
44
- * @template T Type.
45
- * @param orTypes Possible types.
46
- * @returns
47
- */
48
- export function typesOr<T extends AnyType[]>(...orTypes: T): T[number]
49
-
50
- export function typesOr(
51
- dispatcherOrType: ((sn: any) => AnyType) | AnyType,
52
- ...moreOrTypes: AnyType[]
53
- ): AnyType {
54
- const orTypes = moreOrTypes.slice()
55
- let finalDispatcher: ((sn: any) => TypeChecker) | undefined
56
-
57
- const firstTypeChecker = resolveStandardTypeNoThrow(dispatcherOrType as AnyType)
58
-
59
- if (firstTypeChecker) {
60
- orTypes.unshift(firstTypeChecker)
61
- } else {
62
- const dispatcher = dispatcherOrType as (sn: any) => AnyType
63
- finalDispatcher = (sn: any) => {
64
- const type = dispatcher(sn)
65
- const typeChecker = resolveTypeChecker(type)
66
- return typeChecker
67
- }
68
- }
69
-
70
- if (orTypes.length <= 0) {
71
- throw failure("or type must have at least 1 possible type")
72
- }
73
-
74
- const originalDispatcher = firstTypeChecker
75
- ? undefined
76
- : (dispatcherOrType as (sn: any) => AnyType)
77
- const typeInfoGen: TypeInfoGen = (t) =>
78
- new OrTypeInfo(t, orTypes.map(resolveStandardType), originalDispatcher)
79
-
80
- return lateTypeChecker(() => {
81
- const checkers = orTypes.map(resolveTypeChecker)
82
-
83
- // if the or includes unchecked then it is unchecked
84
- if (checkers.some((tc) => tc.unchecked)) {
85
- return typesUnchecked() as any
86
- }
87
-
88
- const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
89
- const typeNames = checkers.map((tc) => {
90
- if (recursiveTypeCheckers.includes(tc)) {
91
- return "..."
92
- }
93
- return tc.getTypeName(...recursiveTypeCheckers, tc)
94
- })
95
-
96
- return typeNames.join(" | ")
97
- }
98
-
99
- let thisTcBaseType: TypeCheckerBaseType
100
- if (checkers.some((c) => c.baseType !== checkers[0].baseType)) {
101
- thisTcBaseType = TypeCheckerBaseType.Any
102
- } else {
103
- thisTcBaseType = checkers[0].baseType
104
- }
105
-
106
- const anyCheckers: TypeChecker[] = []
107
- const primitiveOrAnyCheckers: TypeChecker[] = []
108
- const objectOrAnyCheckers: TypeChecker[] = []
109
- const arrayOrAnyCheckers: TypeChecker[] = []
110
-
111
- for (let i = 0; i < checkers.length; i++) {
112
- const checker = checkers[i]
113
- switch (checker.baseType) {
114
- case TypeCheckerBaseType.Primitive:
115
- primitiveOrAnyCheckers.push(checker)
116
- break
117
- case TypeCheckerBaseType.Object:
118
- objectOrAnyCheckers.push(checker)
119
- break
120
- case TypeCheckerBaseType.Array:
121
- arrayOrAnyCheckers.push(checker)
122
- break
123
- case TypeCheckerBaseType.Any:
124
- anyCheckers.push(checker)
125
- primitiveOrAnyCheckers.push(checker)
126
- objectOrAnyCheckers.push(checker)
127
- arrayOrAnyCheckers.push(checker)
128
- break
129
- default:
130
- break
131
- }
132
- }
133
-
134
- const getCandidateCheckers = (
135
- valueBaseType: TypeCheckerBaseType
136
- ): ReadonlyArray<TypeChecker> => {
137
- switch (valueBaseType) {
138
- case TypeCheckerBaseType.Primitive:
139
- return primitiveOrAnyCheckers
140
- case TypeCheckerBaseType.Object:
141
- return objectOrAnyCheckers
142
- case TypeCheckerBaseType.Array:
143
- return arrayOrAnyCheckers
144
- case TypeCheckerBaseType.Any:
145
- return anyCheckers
146
- default:
147
- return anyCheckers
148
- }
149
- }
150
-
151
- const thisTc: TypeChecker = new TypeChecker(
152
- thisTcBaseType,
153
-
154
- (value, path, typeCheckedValue) => {
155
- // Union types must always full-check every alternative. Partial checking could
156
- // produce false positives when overlapping alternatives each pass on different
157
- // property subsets without any single alternative passing in full.
158
- const valueBaseType = getTypeCheckerBaseTypeFromValue(value)
159
- const candidateCheckers = getCandidateCheckers(valueBaseType)
160
-
161
- for (let i = 0; i < candidateCheckers.length; i++) {
162
- const tc = candidateCheckers[i]
163
- if (tc.skipCheck) {
164
- // For skipCheck branches, verify structural match without deep validation
165
- if (tc.snapshotType(value)) {
166
- return null
167
- }
168
- } else if (!tc.check(value, path, typeCheckedValue)) {
169
- return null
170
- }
171
- }
172
-
173
- return new TypeCheckError({
174
- path,
175
- expectedTypeName: getTypeName(thisTc),
176
- actualValue: value,
177
- typeCheckedValue,
178
- })
179
- },
180
-
181
- getTypeName,
182
- typeInfoGen,
183
-
184
- (value) => {
185
- const valueBaseType = getTypeCheckerBaseTypeFromValue(value)
186
- const checkerForBaseType = getCandidateCheckers(valueBaseType)
187
-
188
- if (
189
- checkerForBaseType.length === 1 &&
190
- checkerForBaseType[0].baseType === valueBaseType &&
191
- !checkerForBaseType[0].skipCheck
192
- ) {
193
- // when there is only one valid option accept it without asking
194
- // this is done because:
195
- // 1) performance (avoid checking structure if not needed)
196
- // 2) so we can accept untyped models when paired with undefined | null
197
- return checkerForBaseType[0]
198
- }
199
-
200
- for (let i = 0; i < checkerForBaseType.length; i++) {
201
- const matchingType = checkerForBaseType[i].snapshotType(value)
202
- if (matchingType) {
203
- return matchingType
204
- }
205
- }
206
-
207
- return null
208
- },
209
-
210
- (sn) => {
211
- const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
212
- if (!type) {
213
- throw new SnapshotTypeMismatchError({
214
- expectedTypeName: getTypeName(thisTc),
215
- actualValue: sn,
216
- })
217
- }
218
-
219
- return type.fromSnapshotProcessor(sn)
220
- },
221
-
222
- (sn) => {
223
- const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
224
- if (!type) {
225
- throw new SnapshotTypeMismatchError({
226
- expectedTypeName: getTypeName(thisTc),
227
- actualValue: sn,
228
- })
229
- }
230
-
231
- return type.toSnapshotProcessor(sn)
232
- }
233
- )
234
-
235
- return thisTc
236
- }, typeInfoGen) as any
237
- }
238
-
239
- /**
240
- * `types.or` type info.
241
- */
242
- export class OrTypeInfo extends TypeInfo {
243
- readonly kind = "or"
244
-
245
- // memoize to always return the same array on the getter
246
- private _orTypeInfos = lazy(() => this.orTypes.map(getTypeInfo))
247
-
248
- get orTypeInfos(): ReadonlyArray<TypeInfo> {
249
- return this._orTypeInfos()
250
- }
251
-
252
- constructor(
253
- thisType: AnyStandardType,
254
- readonly orTypes: ReadonlyArray<AnyStandardType>,
255
- readonly dispatcher: ((sn: any) => AnyType) | undefined
256
- ) {
257
- super(thisType)
258
- }
259
- }
1
+ import { failure, lazy } from "../../utils"
2
+ import { getTypeInfo } from "../getTypeInfo"
3
+ import {
4
+ resolveStandardType,
5
+ resolveStandardTypeNoThrow,
6
+ resolveTypeChecker,
7
+ } from "../resolveTypeChecker"
8
+ import { SnapshotTypeMismatchError } from "../SnapshotTypeMismatchError"
9
+ import type { AnyStandardType, AnyType } from "../schemas"
10
+ import { TypeCheckError } from "../TypeCheckError"
11
+ import {
12
+ getTypeCheckerBaseTypeFromValue,
13
+ lateTypeChecker,
14
+ TypeChecker,
15
+ TypeCheckerBaseType,
16
+ TypeInfo,
17
+ type TypeInfoGen,
18
+ } from "../TypeChecker"
19
+ import { typesUnchecked } from "./typesUnchecked"
20
+
21
+ /**
22
+ * A type that represents the union of several other types (a | b | c | ...).
23
+ * Accepts a dispatcher that, given a snapshot, returns the type
24
+ * that snapshot is.
25
+ *
26
+ * @template T Type.
27
+ * @param dispatcher Function that given a snapshot returns the type.
28
+ * @param orTypes Possible types.
29
+ * @returns
30
+ */
31
+ export function typesOr<T extends AnyType[]>(
32
+ dispatcher: (sn: any) => T[number],
33
+ ...orTypes: T
34
+ ): T[number]
35
+
36
+ /**
37
+ * A type that represents the union of several other types (a | b | c | ...).
38
+ *
39
+ * Example:
40
+ * ```ts
41
+ * const booleanOrNumberType = types.or(types.boolean, types.number)
42
+ * ```
43
+ *
44
+ * @template T Type.
45
+ * @param orTypes Possible types.
46
+ * @returns
47
+ */
48
+ export function typesOr<T extends AnyType[]>(...orTypes: T): T[number]
49
+
50
+ export function typesOr(
51
+ dispatcherOrType: ((sn: any) => AnyType) | AnyType,
52
+ ...moreOrTypes: AnyType[]
53
+ ): AnyType {
54
+ const orTypes = moreOrTypes.slice()
55
+ let finalDispatcher: ((sn: any) => TypeChecker) | undefined
56
+
57
+ const firstTypeChecker = resolveStandardTypeNoThrow(dispatcherOrType as AnyType)
58
+
59
+ if (firstTypeChecker) {
60
+ orTypes.unshift(firstTypeChecker)
61
+ } else {
62
+ const dispatcher = dispatcherOrType as (sn: any) => AnyType
63
+ finalDispatcher = (sn: any) => {
64
+ const type = dispatcher(sn)
65
+ const typeChecker = resolveTypeChecker(type)
66
+ return typeChecker
67
+ }
68
+ }
69
+
70
+ if (orTypes.length <= 0) {
71
+ throw failure("or type must have at least 1 possible type")
72
+ }
73
+
74
+ const originalDispatcher = firstTypeChecker
75
+ ? undefined
76
+ : (dispatcherOrType as (sn: any) => AnyType)
77
+ const typeInfoGen: TypeInfoGen = (t) =>
78
+ new OrTypeInfo(t, orTypes.map(resolveStandardType), originalDispatcher)
79
+
80
+ return lateTypeChecker(() => {
81
+ const checkers = orTypes.map(resolveTypeChecker)
82
+
83
+ // if the or includes unchecked then it is unchecked
84
+ if (checkers.some((tc) => tc.unchecked)) {
85
+ return typesUnchecked() as any
86
+ }
87
+
88
+ const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
89
+ const typeNames = checkers.map((tc) => {
90
+ if (recursiveTypeCheckers.includes(tc)) {
91
+ return "..."
92
+ }
93
+ return tc.getTypeName(...recursiveTypeCheckers, tc)
94
+ })
95
+
96
+ return typeNames.join(" | ")
97
+ }
98
+
99
+ let thisTcBaseType: TypeCheckerBaseType
100
+ if (checkers.some((c) => c.baseType !== checkers[0].baseType)) {
101
+ thisTcBaseType = TypeCheckerBaseType.Any
102
+ } else {
103
+ thisTcBaseType = checkers[0].baseType
104
+ }
105
+
106
+ const anyCheckers: TypeChecker[] = []
107
+ const primitiveOrAnyCheckers: TypeChecker[] = []
108
+ const objectOrAnyCheckers: TypeChecker[] = []
109
+ const arrayOrAnyCheckers: TypeChecker[] = []
110
+
111
+ for (let i = 0; i < checkers.length; i++) {
112
+ const checker = checkers[i]
113
+ switch (checker.baseType) {
114
+ case TypeCheckerBaseType.Primitive:
115
+ primitiveOrAnyCheckers.push(checker)
116
+ break
117
+ case TypeCheckerBaseType.Object:
118
+ objectOrAnyCheckers.push(checker)
119
+ break
120
+ case TypeCheckerBaseType.Array:
121
+ arrayOrAnyCheckers.push(checker)
122
+ break
123
+ case TypeCheckerBaseType.Any:
124
+ anyCheckers.push(checker)
125
+ primitiveOrAnyCheckers.push(checker)
126
+ objectOrAnyCheckers.push(checker)
127
+ arrayOrAnyCheckers.push(checker)
128
+ break
129
+ default:
130
+ break
131
+ }
132
+ }
133
+
134
+ const getCandidateCheckers = (
135
+ valueBaseType: TypeCheckerBaseType
136
+ ): ReadonlyArray<TypeChecker> => {
137
+ switch (valueBaseType) {
138
+ case TypeCheckerBaseType.Primitive:
139
+ return primitiveOrAnyCheckers
140
+ case TypeCheckerBaseType.Object:
141
+ return objectOrAnyCheckers
142
+ case TypeCheckerBaseType.Array:
143
+ return arrayOrAnyCheckers
144
+ case TypeCheckerBaseType.Any:
145
+ return anyCheckers
146
+ default:
147
+ return anyCheckers
148
+ }
149
+ }
150
+
151
+ const thisTc: TypeChecker = new TypeChecker(
152
+ thisTcBaseType,
153
+
154
+ (value, path, typeCheckedValue) => {
155
+ // Union types must always full-check every alternative. Partial checking could
156
+ // produce false positives when overlapping alternatives each pass on different
157
+ // property subsets without any single alternative passing in full.
158
+ const valueBaseType = getTypeCheckerBaseTypeFromValue(value)
159
+ const candidateCheckers = getCandidateCheckers(valueBaseType)
160
+
161
+ for (let i = 0; i < candidateCheckers.length; i++) {
162
+ const tc = candidateCheckers[i]
163
+ if (tc.skipCheck) {
164
+ // For skipCheck branches, verify structural match without deep validation
165
+ if (tc.snapshotType(value)) {
166
+ return null
167
+ }
168
+ } else if (!tc.check(value, path, typeCheckedValue)) {
169
+ return null
170
+ }
171
+ }
172
+
173
+ return new TypeCheckError({
174
+ path,
175
+ expectedTypeName: getTypeName(thisTc),
176
+ actualValue: value,
177
+ typeCheckedValue,
178
+ })
179
+ },
180
+
181
+ getTypeName,
182
+ typeInfoGen,
183
+
184
+ (value) => {
185
+ const valueBaseType = getTypeCheckerBaseTypeFromValue(value)
186
+ const checkerForBaseType = getCandidateCheckers(valueBaseType)
187
+
188
+ if (
189
+ checkerForBaseType.length === 1 &&
190
+ checkerForBaseType[0].baseType === valueBaseType &&
191
+ !checkerForBaseType[0].skipCheck
192
+ ) {
193
+ // when there is only one valid option accept it without asking
194
+ // this is done because:
195
+ // 1) performance (avoid checking structure if not needed)
196
+ // 2) so we can accept untyped models when paired with undefined | null
197
+ return checkerForBaseType[0]
198
+ }
199
+
200
+ for (let i = 0; i < checkerForBaseType.length; i++) {
201
+ const matchingType = checkerForBaseType[i].snapshotType(value)
202
+ if (matchingType) {
203
+ return matchingType
204
+ }
205
+ }
206
+
207
+ return null
208
+ },
209
+
210
+ (sn) => {
211
+ const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
212
+ if (!type) {
213
+ throw new SnapshotTypeMismatchError({
214
+ expectedTypeName: getTypeName(thisTc),
215
+ actualValue: sn,
216
+ })
217
+ }
218
+
219
+ return type.fromSnapshotProcessor(sn)
220
+ },
221
+
222
+ (sn) => {
223
+ const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
224
+ if (!type) {
225
+ throw new SnapshotTypeMismatchError({
226
+ expectedTypeName: getTypeName(thisTc),
227
+ actualValue: sn,
228
+ })
229
+ }
230
+
231
+ return type.toSnapshotProcessor(sn)
232
+ }
233
+ )
234
+
235
+ return thisTc
236
+ }, typeInfoGen) as any
237
+ }
238
+
239
+ /**
240
+ * `types.or` type info.
241
+ */
242
+ export class OrTypeInfo extends TypeInfo {
243
+ readonly kind = "or"
244
+ readonly orTypes: ReadonlyArray<AnyStandardType>
245
+ readonly dispatcher: ((sn: any) => AnyType) | undefined
246
+
247
+ // memoize to always return the same array on the getter
248
+ private _orTypeInfos = lazy(() => this.orTypes.map(getTypeInfo))
249
+
250
+ get orTypeInfos(): ReadonlyArray<TypeInfo> {
251
+ return this._orTypeInfos()
252
+ }
253
+
254
+ constructor(
255
+ thisType: AnyStandardType,
256
+ orTypes: ReadonlyArray<AnyStandardType>,
257
+ dispatcher: ((sn: any) => AnyType) | undefined
258
+ ) {
259
+ super(thisType)
260
+ this.orTypes = orTypes
261
+ this.dispatcher = dispatcher
262
+ }
263
+ }