mobx-keystone 1.22.0 → 1.24.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 (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -21
  3. package/README.md +1 -1
  4. package/dist/mobx-keystone.esm.js +7198 -6242
  5. package/dist/mobx-keystone.esm.mjs +7198 -6242
  6. package/dist/mobx-keystone.umd.js +7217 -6243
  7. package/dist/types/action/runUnprotected.d.ts +8 -4
  8. package/dist/types/model/Model.d.ts +5 -0
  9. package/dist/types/modelShared/modelDecorator.d.ts +1 -1
  10. package/dist/types/modelShared/prop.d.ts +8 -4
  11. package/dist/types/snapshot/modelInitialData.d.ts +1 -0
  12. package/dist/types/tweaker/treeNodeMetadata.d.ts +1 -0
  13. package/dist/types/types/createCachedTypeCheck.d.ts +1 -0
  14. package/dist/types/utils/index.d.ts +24 -0
  15. package/package.json +15 -11
  16. package/src/action/middleware.ts +5 -18
  17. package/src/action/mutationBatch.ts +72 -0
  18. package/src/action/runUnprotected.ts +21 -8
  19. package/src/action/wrapInAction.ts +45 -15
  20. package/src/actionMiddlewares/undoMiddleware.ts +19 -13
  21. package/src/dataModel/BaseDataModel.ts +4 -8
  22. package/src/deepChange/onDeepChange.ts +246 -16
  23. package/src/model/BaseModel.ts +4 -12
  24. package/src/model/Model.ts +5 -0
  25. package/src/model/newModel.ts +68 -36
  26. package/src/modelShared/modelDecorator.ts +64 -53
  27. package/src/modelShared/prop.ts +105 -63
  28. package/src/modelShared/sharedInternalModel.ts +91 -60
  29. package/src/parent/core.ts +16 -10
  30. package/src/parent/coreObjectChildren.ts +219 -21
  31. package/src/parent/path.ts +6 -5
  32. package/src/parent/setParent.ts +5 -5
  33. package/src/patch/emitPatch.ts +102 -20
  34. package/src/ref/Ref.ts +4 -5
  35. package/src/ref/core.ts +2 -2
  36. package/src/snapshot/fromArraySnapshot.ts +11 -2
  37. package/src/snapshot/fromModelSnapshot.ts +10 -3
  38. package/src/snapshot/fromPlainObjectSnapshot.ts +18 -7
  39. package/src/snapshot/fromSnapshot.ts +31 -9
  40. package/src/snapshot/getSnapshot.ts +8 -3
  41. package/src/snapshot/internal.ts +243 -82
  42. package/src/snapshot/modelInitialData.ts +41 -0
  43. package/src/snapshot/reconcileArraySnapshot.ts +9 -3
  44. package/src/snapshot/reconcileModelSnapshot.ts +97 -17
  45. package/src/snapshot/reconcilePlainObjectSnapshot.ts +9 -3
  46. package/src/treeUtils/draft.ts +7 -8
  47. package/src/tweaker/core.ts +30 -5
  48. package/src/tweaker/treeNodeMetadata.ts +38 -0
  49. package/src/tweaker/tweak.ts +15 -4
  50. package/src/tweaker/tweakArray.ts +159 -121
  51. package/src/tweaker/tweakFrozen.ts +2 -2
  52. package/src/tweaker/tweakModel.ts +2 -2
  53. package/src/tweaker/tweakPlainObject.ts +117 -124
  54. package/src/tweaker/typeChecking.ts +10 -1
  55. package/src/types/TypeChecker.ts +261 -15
  56. package/src/types/arrayBased/typesArray.ts +38 -40
  57. package/src/types/arrayBased/typesTuple.ts +51 -34
  58. package/src/types/createCachedTypeCheck.ts +181 -0
  59. package/src/types/objectBased/typesArraySet.ts +42 -32
  60. package/src/types/objectBased/typesDataModelData.ts +18 -11
  61. package/src/types/objectBased/typesModel.ts +1 -1
  62. package/src/types/objectBased/typesObject.ts +57 -28
  63. package/src/types/objectBased/typesObjectMap.ts +51 -40
  64. package/src/types/objectBased/typesRecord.ts +59 -39
  65. package/src/types/objectBased/typesRef.ts +1 -1
  66. package/src/types/primitiveBased/typesPrimitive.ts +9 -9
  67. package/src/types/tProp.ts +80 -44
  68. package/src/types/utility/typesOr.ts +35 -24
  69. package/src/types/utility/typesRefinement.ts +21 -8
  70. package/src/types/utility/typesSkipCheck.ts +21 -8
  71. package/src/types/utility/typesTag.ts +21 -9
  72. package/src/types/utility/typesUnchecked.ts +2 -3
  73. package/src/utils/ModelPool.ts +34 -19
  74. package/src/utils/errorDiagnostics.ts +60 -18
  75. package/src/utils/index.ts +142 -9
  76. package/src/types/createPerEntryCachedCheck.ts +0 -166
  77. /package/dist/types/{types/createPerEntryCachedCheck.d.ts → action/mutationBatch.d.ts} +0 -0
@@ -2,13 +2,14 @@ import { isObservableObject, keys } from "mobx"
2
2
  import type { Path } from "../../parent/pathTypes"
3
3
  import { failure, isObject } from "../../utils"
4
4
  import { withErrorPathSegment } from "../../utils/errorDiagnostics"
5
- import { createPerEntryCachedCheck, recordCachePruning } from "../createPerEntryCachedCheck"
5
+ import { createAdaptiveRecordCachedCheck } from "../createCachedTypeCheck"
6
6
  import { getTypeInfo } from "../getTypeInfo"
7
7
  import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
8
8
  import type { AnyStandardType, AnyType, RecordType } from "../schemas"
9
9
  import { TypeCheckError } from "../TypeCheckError"
10
10
  import {
11
11
  lateTypeChecker,
12
+ snapshotProcessorPlan,
12
13
  TypeChecker,
13
14
  TypeCheckerBaseType,
14
15
  TypeInfo,
@@ -30,16 +31,19 @@ import { prependPathElementToTypeCheckError } from "../typeCheckErrorUtils"
30
31
  * @returns
31
32
  */
32
33
  export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
33
- const typeInfoGen: TypeInfoGen = (tc) => new RecordTypeInfo(tc, resolveStandardType(valueType))
34
-
35
- return lateTypeChecker(() => {
36
- const valueChecker = resolveTypeChecker(valueType)
34
+ const resolvedValueType = resolveStandardType(valueType)
35
+ const typeInfoGen: TypeInfoGen = (tc) => new RecordTypeInfo(tc, resolvedValueType)
36
+ const typeChecker = lateTypeChecker(() => {
37
+ const valueChecker = resolveTypeChecker(resolvedValueType)
37
38
  const emptyChildPath: Path = []
38
39
 
39
40
  const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) =>
40
41
  `Record<${valueChecker.getTypeName(...recursiveTypeCheckers, valueChecker)}>`
41
42
 
42
- const applySnapshotProcessor = (obj: Record<string, unknown>, mode: "from" | "to") => {
43
+ const applySnapshotProcessor = (
44
+ obj: Record<string, unknown>,
45
+ processor: (snapshot: any) => unknown
46
+ ) => {
43
47
  if (valueChecker.unchecked) {
44
48
  return obj
45
49
  }
@@ -49,37 +53,44 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
49
53
  const keys = Object.keys(obj)
50
54
  for (let i = 0; i < keys.length; i++) {
51
55
  const k = keys[i]
52
- const v = withErrorPathSegment(k, () =>
53
- mode === "from"
54
- ? valueChecker.fromSnapshotProcessor(obj[k])
55
- : valueChecker.toSnapshotProcessor(obj[k])
56
- )
56
+ const v = withErrorPathSegment(k, () => processor(obj[k]))
57
57
  newObj[k] = v
58
58
  }
59
59
 
60
60
  return newObj
61
61
  }
62
62
 
63
- const checkRecordValues = createPerEntryCachedCheck(
64
- (obj, checkEntry) => {
65
- // Use keys() from MobX for observable objects to track key additions/removals
66
- // in MobX 4 (which lacks Proxy). Object.keys() doesn't establish MobX tracking
67
- // in MobX 4, so the aggregation computed would miss newly added keys.
68
- const objKeys = isObservableObject(obj) ? keys(obj) : Object.keys(obj)
69
- for (let i = 0; i < objKeys.length; i++) {
70
- const error = checkEntry(objKeys[i])
71
- if (error) return error
72
- }
73
- return null
74
- },
75
- (obj, key, path, typeCheckedValue) => {
76
- if (typeof key !== "string") {
77
- throw failure(`record type keys must be strings, got ${typeof key}`)
78
- }
79
- const error = valueChecker.check(obj[key], emptyChildPath, typeCheckedValue)
80
- return error ? prependPathElementToTypeCheckError(error, path, key, typeCheckedValue) : null
81
- },
82
- recordCachePruning
63
+ const iterateRecordEntries = (
64
+ obj: Record<string, unknown>,
65
+ checkEntry: (key: PropertyKey) => TypeCheckError | null
66
+ ): TypeCheckError | null => {
67
+ // Use keys() from MobX for observable objects to track key additions/removals
68
+ // in MobX 4 (which lacks Proxy). Object.keys() doesn't establish MobX tracking
69
+ // in MobX 4, so the aggregation computed would miss newly added keys.
70
+ const objKeys = isObservableObject(obj) ? keys(obj) : Object.keys(obj)
71
+ for (let i = 0; i < objKeys.length; i++) {
72
+ const error = checkEntry(objKeys[i])
73
+ if (error) return error
74
+ }
75
+ return null
76
+ }
77
+
78
+ const checkRecordEntry = (
79
+ obj: Record<string, unknown>,
80
+ key: PropertyKey,
81
+ path: Path,
82
+ typeCheckedValue: any
83
+ ): TypeCheckError | null => {
84
+ if (typeof key !== "string") {
85
+ throw failure(`record type keys must be strings, got ${typeof key}`)
86
+ }
87
+ const error = valueChecker.check(obj[key], emptyChildPath, typeCheckedValue)
88
+ return error ? prependPathElementToTypeCheckError(error, path, key, typeCheckedValue) : null
89
+ }
90
+
91
+ const checkRecordValues = createAdaptiveRecordCachedCheck(
92
+ iterateRecordEntries,
93
+ checkRecordEntry
83
94
  )
84
95
 
85
96
  const thisTc: TypeChecker = new TypeChecker(
@@ -125,17 +136,26 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
125
136
  return thisTc
126
137
  },
127
138
 
128
- (obj: Record<string, unknown>) => {
129
- return applySnapshotProcessor(obj, "from")
130
- },
131
-
132
- (obj: Record<string, unknown>) => {
133
- return applySnapshotProcessor(obj, "to")
134
- }
139
+ snapshotProcessorPlan(
140
+ () => [valueChecker],
141
+ ([processor]) =>
142
+ processor
143
+ ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processor)
144
+ : undefined
145
+ ),
146
+
147
+ snapshotProcessorPlan(
148
+ () => [valueChecker],
149
+ ([processor]) =>
150
+ processor
151
+ ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processor)
152
+ : undefined
153
+ )
135
154
  )
136
155
 
137
156
  return thisTc
138
- }, typeInfoGen) as any
157
+ }, typeInfoGen)
158
+ return typeChecker as any
139
159
  }
140
160
 
141
161
  /**
@@ -75,7 +75,7 @@ export function typesRef<O extends object>(refConstructor: RefConstructor<O>): M
75
75
  }
76
76
  },
77
77
 
78
- (sn) => sn
78
+ undefined
79
79
  )
80
80
 
81
81
  return thisTc as any
@@ -1,4 +1,4 @@
1
- import { assertIsPrimitive, identityFn } from "../../utils"
1
+ import { assertIsPrimitive } from "../../utils"
2
2
  import type { PrimitiveValue } from "../../utils/types"
3
3
  import { registerStandardTypeResolver, type StandardTypeResolverFn } from "../resolveTypeChecker"
4
4
  import type { AnyStandardType, IdentityType } from "../schemas"
@@ -54,8 +54,8 @@ export function typesLiteral<T extends PrimitiveValue>(literal: T): IdentityType
54
54
  typeInfoGen,
55
55
 
56
56
  (value) => (value === literal ? thisTc : null),
57
- identityFn,
58
- identityFn
57
+ undefined,
58
+ undefined
59
59
  )
60
60
 
61
61
  return thisTc as any
@@ -122,8 +122,8 @@ export const typesBoolean: IdentityType<boolean> = new TypeChecker(
122
122
  (t) => new BooleanTypeInfo(t),
123
123
 
124
124
  (value) => (typeof value === "boolean" ? (typesBoolean as any) : null),
125
- identityFn,
126
- identityFn
125
+ undefined,
126
+ undefined
127
127
  ) as any
128
128
 
129
129
  standardTypeResolvers.push((v) => (v === Boolean ? typesBoolean : undefined))
@@ -159,8 +159,8 @@ export const typesNumber: IdentityType<number> = new TypeChecker(
159
159
  (t) => new NumberTypeInfo(t),
160
160
 
161
161
  (value) => (typeof value === "number" ? (typesNumber as any) : null),
162
- identityFn,
163
- identityFn
162
+ undefined,
163
+ undefined
164
164
  ) as any
165
165
 
166
166
  standardTypeResolvers.push((v) => (v === Number ? typesNumber : undefined))
@@ -196,8 +196,8 @@ export const typesString: IdentityType<string> = new TypeChecker(
196
196
  (t) => new StringTypeInfo(t),
197
197
 
198
198
  (value) => (typeof value === "string" ? (typesString as any) : null),
199
- identityFn,
200
- identityFn
199
+ undefined,
200
+ undefined
201
201
  ) as any
202
202
 
203
203
  standardTypeResolvers.push((v) => (v === String ? typesString : undefined))
@@ -3,6 +3,7 @@ import {
3
3
  type MaybeOptionalModelProp,
4
4
  type OptionalModelProp,
5
5
  prop,
6
+ toFullModelPropTransform,
6
7
  } from "../modelShared/prop"
7
8
  import { lazy } from "../utils"
8
9
  import {
@@ -14,6 +15,7 @@ import {
14
15
  } from "./primitiveBased/typesPrimitive"
15
16
  import { resolveStandardType, resolveTypeChecker } from "./resolveTypeChecker"
16
17
  import type { AnyStandardType, AnyType, TypeToData, TypeToSnapshotIn } from "./schemas"
18
+ import { type SnapshotProcessor, TypeChecker } from "./TypeChecker"
17
19
  import { createCodecPropTransform, resolveCodecSupport } from "./utility/typesCodec"
18
20
  import { typesOr } from "./utility/typesOr"
19
21
  import type { TypeToStoredData } from "./utility/typeToStoredData"
@@ -22,6 +24,28 @@ const noDefaultValueSymbol = Symbol("noDefaultValue")
22
24
 
23
25
  const tPropCache = new WeakMap<AnyStandardType, Map<unknown, AnyModelProp>>()
24
26
 
27
+ function createFromSnapshotProcessorGetter(
28
+ getType: () => AnyStandardType,
29
+ eagerType: AnyStandardType | undefined
30
+ ): (() => SnapshotProcessor | undefined) | undefined {
31
+ if (eagerType instanceof TypeChecker) {
32
+ const processor = eagerType.getFromSnapshotProcessor()
33
+ return processor ? () => processor : undefined
34
+ }
35
+ return lazy(() => resolveTypeChecker(getType()).getFromSnapshotProcessor())
36
+ }
37
+
38
+ function createToSnapshotProcessorGetter(
39
+ getType: () => AnyStandardType,
40
+ eagerType: AnyStandardType | undefined
41
+ ): (() => SnapshotProcessor | undefined) | undefined {
42
+ if (eagerType instanceof TypeChecker) {
43
+ const processor = eagerType.getToSnapshotProcessor()
44
+ return processor ? () => processor : undefined
45
+ }
46
+ return lazy(() => resolveTypeChecker(getType()).getToSnapshotProcessor())
47
+ }
48
+
25
49
  type AnyTypeOrArray = AnyType | ReadonlyArray<AnyType>
26
50
 
27
51
  type StoredRequiredPropMetadata<TType extends AnyType> = {
@@ -40,26 +64,6 @@ type TypedOptionalModelProp<TType extends AnyType> = OptionalModelProp<TypeToDat
40
64
  $typedFromSnapshotOverride: TypeToSnapshotIn<TType> | null | undefined
41
65
  }
42
66
 
43
- function getOrCreateTProp(
44
- type: AnyStandardType,
45
- defKey: unknown,
46
- createTProp: () => AnyModelProp
47
- ): AnyModelProp {
48
- let defValueCache = tPropCache.get(type)
49
- if (!defValueCache) {
50
- defValueCache = new Map()
51
- tPropCache.set(type, defValueCache)
52
- }
53
-
54
- let prop = defValueCache.get(defKey)
55
- if (!prop) {
56
- prop = createTProp()
57
- defValueCache.set(defKey, prop)
58
- }
59
-
60
- return prop
61
- }
62
-
63
67
  /**
64
68
  * Defines a string model property with a default value.
65
69
  * Equivalent to `tProp(types.string, defaultValue)`.
@@ -244,34 +248,66 @@ export function tProp(
244
248
  : (typeOrArray as AnyType)
245
249
 
246
250
  const typeChecker = resolveStandardType(resolvedType)
247
- const codecSupport = resolveCodecSupport(typeChecker)
251
+ const defKey = hasDefaultValue ? def : noDefaultValueSymbol
248
252
 
249
- return getOrCreateTProp(typeChecker, hasDefaultValue ? def : noDefaultValueSymbol, () => {
250
- const fromSnapshotTypeChecker = hasDefaultValue
251
- ? typesOr(codecSupport.storedType, typesUndefined, typesNull)
252
- : codecSupport.storedType
253
- const getFromSnapshotProcessor = lazy(
254
- () => resolveTypeChecker(fromSnapshotTypeChecker).fromSnapshotProcessor
255
- )
256
- const getToSnapshotProcessor = lazy(
257
- () => resolveTypeChecker(codecSupport.storedType).toSnapshotProcessor
258
- )
253
+ let defValueCache = tPropCache.get(typeChecker)
254
+ if (!defValueCache) {
255
+ defValueCache = new Map()
256
+ tPropCache.set(typeChecker, defValueCache)
257
+ }
259
258
 
260
- // we use Object.create to avoid messing up with the prop cache
261
- const baseProp = hasDefaultValue ? prop(def) : prop()
262
- const newProp = codecSupport.hasCodec
263
- ? baseProp.withTransform(createCodecPropTransform(typeChecker))
264
- : Object.create(baseProp)
259
+ const cachedProp = defValueCache.get(defKey)
260
+ if (cachedProp) {
261
+ return cachedProp
262
+ }
265
263
 
266
- Object.assign(newProp, {
267
- _defaultValueIsTransformed: codecSupport.hasCodec,
268
- _typeChecker: typeChecker,
264
+ const eagerCodecSupport =
265
+ typeChecker instanceof TypeChecker ? resolveCodecSupport(typeChecker) : undefined
266
+ const getCodecSupport = eagerCodecSupport
267
+ ? () => eagerCodecSupport
268
+ : lazy(() => resolveCodecSupport(typeChecker))
269
+ const eagerStoredType = eagerCodecSupport?.storedType
270
+ const getStoredType = eagerStoredType
271
+ ? () => eagerStoredType
272
+ : lazy(() => getCodecSupport().storedType)
273
+ const eagerFromSnapshotType = eagerStoredType
274
+ ? hasDefaultValue
275
+ ? typesOr(eagerStoredType, typesUndefined, typesNull)
276
+ : eagerStoredType
277
+ : undefined
278
+ const getFromSnapshotType = eagerFromSnapshotType
279
+ ? () => eagerFromSnapshotType
280
+ : lazy(() => {
281
+ const storedType = getStoredType()
282
+ return hasDefaultValue ? typesOr(storedType, typesUndefined, typesNull) : storedType
283
+ })
269
284
 
270
- _fromSnapshotProcessor: (sn) => getFromSnapshotProcessor()(sn),
285
+ // we use Object.create to avoid messing up with the prop cache
286
+ const baseProp = hasDefaultValue ? prop(def) : prop()
287
+ const newProp = eagerCodecSupport?.hasCodec
288
+ ? baseProp.withTransform(createCodecPropTransform(typeChecker))
289
+ : Object.create(baseProp)
271
290
 
272
- _toSnapshotProcessor: (sn) => getToSnapshotProcessor()(sn),
273
- } satisfies Partial<AnyModelProp>)
291
+ Object.assign(newProp, {
292
+ _defaultValueIsTransformed: eagerCodecSupport?.hasCodec ?? false,
293
+ _getDefaultValueIsTransformed: eagerCodecSupport
294
+ ? undefined
295
+ : lazy(() => getCodecSupport().hasCodec),
296
+ _typeChecker: typeChecker,
297
+ _getTransform: eagerCodecSupport
298
+ ? undefined
299
+ : lazy(() =>
300
+ getCodecSupport().hasCodec
301
+ ? toFullModelPropTransform(createCodecPropTransform(typeChecker))
302
+ : undefined
303
+ ),
304
+ _getFromSnapshotProcessor: createFromSnapshotProcessorGetter(
305
+ getFromSnapshotType,
306
+ eagerFromSnapshotType
307
+ ),
308
+ _getToSnapshotProcessor: createToSnapshotProcessorGetter(getStoredType, eagerStoredType),
309
+ } satisfies Partial<AnyModelProp>)
274
310
 
275
- return newProp
276
- })
311
+ defValueCache.set(defKey, newProp)
312
+ return newProp
277
313
  }
@@ -11,6 +11,7 @@ import { TypeCheckError } from "../TypeCheckError"
11
11
  import {
12
12
  getTypeCheckerBaseTypeFromValue,
13
13
  lateTypeChecker,
14
+ snapshotProcessorPlan,
14
15
  TypeChecker,
15
16
  TypeCheckerBaseType,
16
17
  TypeInfo,
@@ -74,11 +75,11 @@ export function typesOr(
74
75
  const originalDispatcher = firstTypeChecker
75
76
  ? undefined
76
77
  : (dispatcherOrType as (sn: any) => AnyType)
77
- const typeInfoGen: TypeInfoGen = (t) =>
78
- new OrTypeInfo(t, orTypes.map(resolveStandardType), originalDispatcher)
78
+ const resolvedOrTypes = orTypes.map(resolveStandardType)
79
+ const typeInfoGen: TypeInfoGen = (t) => new OrTypeInfo(t, resolvedOrTypes, originalDispatcher)
79
80
 
80
81
  return lateTypeChecker(() => {
81
- const checkers = orTypes.map(resolveTypeChecker)
82
+ const checkers = resolvedOrTypes.map(resolveTypeChecker)
82
83
 
83
84
  // if the or includes unchecked then it is unchecked
84
85
  if (checkers.some((tc) => tc.unchecked)) {
@@ -207,29 +208,39 @@ export function typesOr(
207
208
  return null
208
209
  },
209
210
 
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
- },
211
+ snapshotProcessorPlan(
212
+ () => checkers,
213
+ () => (sn) => {
214
+ const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
215
+ if (!type) {
216
+ throw new SnapshotTypeMismatchError({
217
+ expectedTypeName: getTypeName(thisTc),
218
+ actualValue: sn,
219
+ })
220
+ }
221
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
- }
222
+ const processor = type.getFromSnapshotProcessor()
223
+ return processor ? processor(sn) : sn
224
+ },
225
+ true
226
+ ),
227
+
228
+ snapshotProcessorPlan(
229
+ () => checkers,
230
+ () => (sn) => {
231
+ const type = finalDispatcher ? finalDispatcher(sn) : thisTc.snapshotType(sn)
232
+ if (!type) {
233
+ throw new SnapshotTypeMismatchError({
234
+ expectedTypeName: getTypeName(thisTc),
235
+ actualValue: sn,
236
+ })
237
+ }
230
238
 
231
- return type.toSnapshotProcessor(sn)
232
- }
239
+ const processor = type.getToSnapshotProcessor()
240
+ return processor ? processor(sn) : sn
241
+ },
242
+ true
243
+ )
233
244
  )
234
245
 
235
246
  return thisTc
@@ -2,7 +2,13 @@ import { getTypeInfo } from "../getTypeInfo"
2
2
  import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
3
3
  import type { AnyStandardType, AnyType, TypeToData } from "../schemas"
4
4
  import { TypeCheckError } from "../TypeCheckError"
5
- import { lateTypeChecker, TypeChecker, TypeInfo, type TypeInfoGen } from "../TypeChecker"
5
+ import {
6
+ lateTypeChecker,
7
+ snapshotProcessorPlan,
8
+ TypeChecker,
9
+ TypeInfo,
10
+ type TypeInfoGen,
11
+ } from "../TypeChecker"
6
12
 
7
13
  /**
8
14
  * A refinement over a given type. This allows you to do extra checks
@@ -44,11 +50,11 @@ export function typesRefinement<T extends AnyType>(
44
50
  checkFn: (data: TypeToData<T>) => TypeCheckError | null | boolean,
45
51
  typeName?: string
46
52
  ): T {
53
+ const resolvedBaseType = resolveStandardType(baseType)
47
54
  const typeInfoGen: TypeInfoGen = (t) =>
48
- new RefinementTypeInfo(t, resolveStandardType(baseType), checkFn, typeName)
49
-
50
- return lateTypeChecker(() => {
51
- const baseChecker = resolveTypeChecker(baseType)
55
+ new RefinementTypeInfo(t, resolvedBaseType, checkFn, typeName)
56
+ const typeChecker = lateTypeChecker(() => {
57
+ const baseChecker = resolveTypeChecker(resolvedBaseType)
52
58
 
53
59
  const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
54
60
  const baseTypeName = baseChecker.getTypeName(...recursiveTypeCheckers, baseChecker)
@@ -96,12 +102,19 @@ export function typesRefinement<T extends AnyType>(
96
102
  // we cannot check refinement here since it checks data instances, not snapshots
97
103
  (sn) => baseChecker.snapshotType(sn),
98
104
 
99
- (sn) => baseChecker.fromSnapshotProcessor(sn),
100
- (sn) => baseChecker.toSnapshotProcessor(sn)
105
+ snapshotProcessorPlan(
106
+ () => [baseChecker],
107
+ ([processor]) => processor
108
+ ),
109
+ snapshotProcessorPlan(
110
+ () => [baseChecker],
111
+ ([processor]) => processor
112
+ )
101
113
  )
102
114
 
103
115
  return thisTc
104
- }, typeInfoGen) as any
116
+ }, typeInfoGen)
117
+ return typeChecker as any
105
118
  }
106
119
 
107
120
  /**
@@ -1,7 +1,13 @@
1
1
  import { getTypeInfo } from "../getTypeInfo"
2
2
  import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
3
3
  import type { AnyStandardType, AnyType } from "../schemas"
4
- import { lateTypeChecker, TypeChecker, TypeInfo, type TypeInfoGen } from "../TypeChecker"
4
+ import {
5
+ lateTypeChecker,
6
+ snapshotProcessorPlan,
7
+ TypeChecker,
8
+ TypeInfo,
9
+ type TypeInfoGen,
10
+ } from "../TypeChecker"
5
11
 
6
12
  /**
7
13
  * A type that wraps another type but skips runtime validation.
@@ -26,10 +32,10 @@ import { lateTypeChecker, TypeChecker, TypeInfo, type TypeInfoGen } from "../Typ
26
32
  * @returns A type with the same runtime/snapshot behavior but no validation.
27
33
  */
28
34
  export function typesSkipCheck<T extends AnyType>(baseType: T): T {
29
- const typeInfoGen: TypeInfoGen = (t) => new SkipCheckTypeInfo(t, resolveStandardType(baseType))
30
-
31
- return lateTypeChecker(() => {
32
- const baseChecker = resolveTypeChecker(baseType)
35
+ const resolvedBaseType = resolveStandardType(baseType)
36
+ const typeInfoGen: TypeInfoGen = (t) => new SkipCheckTypeInfo(t, resolvedBaseType)
37
+ const typeChecker = lateTypeChecker(() => {
38
+ const baseChecker = resolveTypeChecker(resolvedBaseType)
33
39
 
34
40
  const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
35
41
  const baseTypeName = baseChecker.getTypeName(...recursiveTypeCheckers, baseChecker)
@@ -42,13 +48,20 @@ export function typesSkipCheck<T extends AnyType>(baseType: T): T {
42
48
  getTypeName,
43
49
  typeInfoGen,
44
50
  (sn) => baseChecker.snapshotType(sn),
45
- (sn) => baseChecker.fromSnapshotProcessor(sn),
46
- (sn) => baseChecker.toSnapshotProcessor(sn)
51
+ snapshotProcessorPlan(
52
+ () => [baseChecker],
53
+ ([processor]) => processor
54
+ ),
55
+ snapshotProcessorPlan(
56
+ () => [baseChecker],
57
+ ([processor]) => processor
58
+ )
47
59
  )
48
60
  thisTc.skipCheck = true
49
61
 
50
62
  return thisTc
51
- }, typeInfoGen) as any
63
+ }, typeInfoGen)
64
+ return typeChecker as any
52
65
  }
53
66
 
54
67
  /**
@@ -1,7 +1,13 @@
1
1
  import { getTypeInfo } from "../getTypeInfo"
2
2
  import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
3
3
  import type { AnyStandardType, AnyType } from "../schemas"
4
- import { lateTypeChecker, TypeChecker, TypeInfo, type TypeInfoGen } from "../TypeChecker"
4
+ import {
5
+ lateTypeChecker,
6
+ snapshotProcessorPlan,
7
+ TypeChecker,
8
+ TypeInfo,
9
+ type TypeInfoGen,
10
+ } from "../TypeChecker"
5
11
 
6
12
  /**
7
13
  * Wrap a given type with tag information.
@@ -35,11 +41,10 @@ import { lateTypeChecker, TypeChecker, TypeInfo, type TypeInfoGen } from "../Typ
35
41
  * @returns
36
42
  */
37
43
  export function typesTag<T extends AnyType, A>(baseType: T, tag: A, typeName?: string): T {
38
- const typeInfoGen: TypeInfoGen = (t) =>
39
- new TagTypeInfo(t, resolveStandardType(baseType), tag, typeName)
40
-
41
- return lateTypeChecker(() => {
42
- const baseChecker = resolveTypeChecker(baseType)
44
+ const resolvedBaseType = resolveStandardType(baseType)
45
+ const typeInfoGen: TypeInfoGen = (t) => new TagTypeInfo(t, resolvedBaseType, tag, typeName)
46
+ const typeChecker = lateTypeChecker(() => {
47
+ const baseChecker = resolveTypeChecker(resolvedBaseType)
43
48
 
44
49
  const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
45
50
  const baseTypeName = baseChecker.getTypeName(...recursiveTypeCheckers, baseChecker)
@@ -53,12 +58,19 @@ export function typesTag<T extends AnyType, A>(baseType: T, tag: A, typeName?: s
53
58
  getTypeName,
54
59
  typeInfoGen,
55
60
  (sn) => baseChecker.snapshotType(sn),
56
- (sn) => baseChecker.fromSnapshotProcessor(sn),
57
- (sn) => baseChecker.toSnapshotProcessor(sn)
61
+ snapshotProcessorPlan(
62
+ () => [baseChecker],
63
+ ([processor]) => processor
64
+ ),
65
+ snapshotProcessorPlan(
66
+ () => [baseChecker],
67
+ ([processor]) => processor
68
+ )
58
69
  )
59
70
 
60
71
  return thisTc
61
- }, typeInfoGen) as any
72
+ }, typeInfoGen)
73
+ return typeChecker as any
62
74
  }
63
75
 
64
76
  /**
@@ -1,4 +1,3 @@
1
- import { identityFn } from "../../utils"
2
1
  import type { IdentityType } from "../schemas"
3
2
  import { TypeChecker, TypeCheckerBaseType, TypeInfo } from "../TypeChecker"
4
3
 
@@ -10,8 +9,8 @@ const unchecked: IdentityType<any> = new TypeChecker(
10
9
 
11
10
  () => unchecked as any,
12
11
 
13
- identityFn,
14
- identityFn
12
+ undefined,
13
+ undefined
15
14
  ) as any
16
15
 
17
16
  /**