mobx-keystone 1.20.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +864 -853
- package/README.md +61 -61
- package/dist/mobx-keystone.esm.js +11336 -11148
- package/dist/mobx-keystone.esm.mjs +11336 -11148
- package/dist/mobx-keystone.umd.js +12229 -11738
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
- package/dist/types/modelShared/prop.d.ts +5 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
- package/dist/types/ref/Ref.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +1 -1
- package/dist/types/types/TypeChecker.d.ts +1 -1
- package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
- package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
- package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
- package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
- package/dist/types/types/objectBased/typesModel.d.ts +1 -1
- package/dist/types/types/objectBased/typesObject.d.ts +3 -3
- package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
- package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
- package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
- package/dist/types/types/types.d.ts +3 -3
- package/dist/types/types/utility/typesCodec.d.ts +1 -6
- package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
- package/dist/types/types/utility/typesOr.d.ts +1 -1
- package/dist/types/types/utility/typesRefinement.d.ts +1 -1
- package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
- package/dist/types/types/utility/typesTag.d.ts +1 -1
- package/dist/types/wrappers/ObjectMap.d.ts +2 -0
- package/dist/types/wrappers/asMap.d.ts +10 -6
- package/package.json +14 -12
- package/src/action/applyAction.ts +125 -114
- package/src/action/applyMethodCall.ts +37 -37
- package/src/action/isModelAction.ts +16 -16
- package/src/action/middleware.ts +137 -137
- package/src/action/modelFlow.ts +240 -240
- package/src/action/modelFlowPromiseGenerator.ts +127 -125
- package/src/action/pendingActions.ts +42 -42
- package/src/action/wrapInAction.ts +156 -156
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
- package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
- package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
- package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
- package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
- package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
- package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
- package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
- package/src/actionMiddlewares/undoMiddleware.ts +804 -796
- package/src/actionMiddlewares/utils.ts +2 -2
- package/src/computedTree/computedTree.ts +143 -143
- package/src/context/context.ts +1 -1
- package/src/dataModel/BaseDataModel.ts +194 -192
- package/src/dataModel/actions.ts +1 -1
- package/src/dataModel/newDataModel.ts +57 -57
- package/src/deepChange/index.ts +1 -1
- package/src/deepChange/onDeepChange.ts +244 -244
- package/src/frozen/Frozen.ts +146 -146
- package/src/globalConfig/globalConfig.ts +107 -107
- package/src/index.ts +24 -24
- package/src/model/BaseModel.ts +275 -273
- package/src/model/ModelConstructorOptions.ts +15 -15
- package/src/model/getModelMetadata.ts +51 -51
- package/src/model/index.ts +6 -6
- package/src/model/mixins.ts +288 -288
- package/src/model/newModel.ts +301 -301
- package/src/model/utils.ts +249 -249
- package/src/modelShared/modelDecorator.ts +260 -253
- package/src/modelShared/modelInfo.ts +50 -50
- package/src/modelShared/modelSymbols.ts +3 -3
- package/src/modelShared/newModel.ts +25 -25
- package/src/modelShared/prop.ts +713 -684
- package/src/modelShared/sharedInternalModel.ts +427 -417
- package/src/modelShared/utils.ts +24 -24
- package/src/parent/core.ts +65 -65
- package/src/parent/coreObjectChildren.ts +180 -180
- package/src/parent/detach.ts +47 -47
- package/src/parent/findParent.ts +1 -1
- package/src/parent/getChildrenObjects.ts +25 -25
- package/src/parent/path.ts +386 -381
- package/src/parent/setParent.ts +149 -149
- package/src/parent/walkTree.ts +1 -1
- package/src/patch/applyPatches.ts +187 -187
- package/src/patch/jsonPatch.ts +108 -108
- package/src/patch/patchRecorder.ts +6 -1
- package/src/redux/connectReduxDevTools.ts +39 -7
- package/src/redux/redux.ts +6 -2
- package/src/ref/Ref.ts +1 -1
- package/src/ref/core.ts +339 -345
- package/src/ref/customRef.ts +3 -3
- package/src/ref/rootRef.ts +100 -100
- package/src/rootStore/attachDetach.ts +89 -89
- package/src/rootStore/rootStore.ts +1 -1
- package/src/snapshot/SnapshotOf.ts +104 -104
- package/src/snapshot/SnapshotProcessingError.ts +45 -45
- package/src/snapshot/applySnapshot.ts +177 -181
- package/src/snapshot/cloneTreeValue.ts +30 -30
- package/src/snapshot/fromArraySnapshot.ts +27 -33
- package/src/snapshot/fromModelSnapshot.ts +70 -70
- package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
- package/src/snapshot/fromSnapshot.ts +198 -198
- package/src/snapshot/getSnapshot.ts +80 -79
- package/src/snapshot/index.ts +8 -8
- package/src/snapshot/internal.ts +230 -230
- package/src/snapshot/reconcileArraySnapshot.ts +70 -70
- package/src/snapshot/reconcileModelSnapshot.ts +155 -150
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
- package/src/snapshot/reconcileSnapshot.ts +88 -88
- package/src/standardActions/actions.ts +64 -64
- package/src/standardActions/objectActions.ts +60 -60
- package/src/transforms/ImmutableDate.ts +93 -93
- package/src/transforms/asMap.ts +50 -50
- package/src/transforms/asSet.ts +22 -22
- package/src/transforms/bigint.ts +20 -20
- package/src/transforms/date.ts +40 -40
- package/src/treeUtils/deepEquals.ts +55 -55
- package/src/treeUtils/sandbox.ts +6 -3
- package/src/tweaker/TweakerPriority.ts +9 -9
- package/src/tweaker/tweak.ts +4 -3
- package/src/tweaker/tweakArray.ts +530 -475
- package/src/tweaker/tweakFrozen.ts +42 -42
- package/src/tweaker/tweakModel.ts +1 -1
- package/src/tweaker/tweakPlainObject.ts +355 -355
- package/src/tweaker/typeChecking.ts +101 -101
- package/src/types/SnapshotTypeMismatchError.ts +46 -46
- package/src/types/TypeCheckError.ts +183 -184
- package/src/types/TypeChecker.ts +202 -185
- package/src/types/arrayBased/typesArray.ts +136 -137
- package/src/types/arrayBased/typesTuple.ts +138 -139
- package/src/types/createPerEntryCachedCheck.ts +166 -160
- package/src/types/index.ts +6 -6
- package/src/types/objectBased/typesArraySet.ts +133 -134
- package/src/types/objectBased/typesDataModelData.ts +203 -204
- package/src/types/objectBased/typesModel.ts +220 -221
- package/src/types/objectBased/typesObject.ts +308 -310
- package/src/types/objectBased/typesObjectMap.ts +142 -143
- package/src/types/objectBased/typesRecord.ts +156 -157
- package/src/types/objectBased/typesRef.ts +89 -89
- package/src/types/primitiveBased/typesEnum.ts +63 -63
- package/src/types/primitiveBased/typesPrimitive.ts +219 -220
- package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
- package/src/types/resolveTypeChecker.ts +78 -78
- package/src/types/schemas.ts +208 -208
- package/src/types/tProp.ts +277 -268
- package/src/types/typeCheck.ts +21 -21
- package/src/types/typeCheckErrorUtils.ts +23 -23
- package/src/types/types.ts +142 -142
- package/src/types/utility/typeToStoredData.ts +37 -37
- package/src/types/utility/typesBigInt.ts +15 -15
- package/src/types/utility/typesCodec.ts +41 -38
- package/src/types/utility/typesCodecCore.ts +178 -178
- package/src/types/utility/typesCodecSupport.ts +806 -806
- package/src/types/utility/typesDate.ts +27 -27
- package/src/types/utility/typesMap.ts +253 -222
- package/src/types/utility/typesOr.ts +263 -259
- package/src/types/utility/typesRefinement.ts +131 -125
- package/src/types/utility/typesSet.ts +110 -110
- package/src/types/utility/typesSkipCheck.ts +69 -70
- package/src/types/utility/typesTag.ts +88 -82
- package/src/types/utility/typesUnchecked.ts +40 -40
- package/src/utils/AnyFunction.ts +4 -4
- package/src/utils/ModelPool.ts +59 -59
- package/src/utils/chainFns.ts +18 -18
- package/src/utils/decorators.ts +274 -274
- package/src/utils/errorDiagnostics.ts +204 -204
- package/src/utils/index.ts +443 -372
- package/src/wrappers/ArraySet.ts +156 -156
- package/src/wrappers/ObjectMap.ts +155 -136
- package/src/wrappers/asMap.ts +65 -30
- package/src/wrappers/asSet.ts +3 -3
|
@@ -1,139 +1,138 @@
|
|
|
1
|
-
import type { Path } from "../../parent/pathTypes"
|
|
2
|
-
import { isArray, lazy } from "../../utils"
|
|
3
|
-
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
4
|
-
import { createPerEntryCachedCheck } from "../createPerEntryCachedCheck"
|
|
5
|
-
import { getTypeInfo } from "../getTypeInfo"
|
|
6
|
-
import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
|
|
7
|
-
import type { AnyStandardType, AnyType, ArrayType } from "../schemas"
|
|
8
|
-
import { TypeCheckError } from "../TypeCheckError"
|
|
9
|
-
import {
|
|
10
|
-
lateTypeChecker,
|
|
11
|
-
TypeChecker,
|
|
12
|
-
TypeCheckerBaseType,
|
|
13
|
-
TypeInfo,
|
|
14
|
-
TypeInfoGen,
|
|
15
|
-
} from "../TypeChecker"
|
|
16
|
-
import { prependPathElementToTypeCheckError } from "../typeCheckErrorUtils"
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A type that represents an tuple of values of a given type.
|
|
20
|
-
*
|
|
21
|
-
* Example:
|
|
22
|
-
* ```ts
|
|
23
|
-
* const stringNumberTupleType = types.tuple(types.string, types.number)
|
|
24
|
-
* ```
|
|
25
|
-
*
|
|
26
|
-
* @template T Item types.
|
|
27
|
-
* @param itemTypes Type of inner items.
|
|
28
|
-
* @returns
|
|
29
|
-
*/
|
|
30
|
-
export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
|
|
31
|
-
const typeInfoGen: TypeInfoGen = (t) => new TupleTypeInfo(t, itemTypes.map(resolveStandardType))
|
|
32
|
-
|
|
33
|
-
return lateTypeChecker(() => {
|
|
34
|
-
const checkers = itemTypes.map(resolveTypeChecker)
|
|
35
|
-
const emptyChildPath: Path = []
|
|
36
|
-
|
|
37
|
-
const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
|
|
38
|
-
const typeNames = checkers.map((tc) => {
|
|
39
|
-
if (recursiveTypeCheckers.includes(tc)) {
|
|
40
|
-
return "..."
|
|
41
|
-
}
|
|
42
|
-
return tc.getTypeName(...recursiveTypeCheckers, tc)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
return "[" + typeNames.join(", ") + "]"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// No setupCachePruning needed: tuple length is fixed, so entries never become stale.
|
|
49
|
-
const checkTupleItems = createPerEntryCachedCheck<number>(
|
|
50
|
-
(_array, checkEntry) => {
|
|
51
|
-
for (let i = 0; i < checkers.length; i++) {
|
|
52
|
-
const error = checkEntry(i)
|
|
53
|
-
if (error) return error
|
|
54
|
-
}
|
|
55
|
-
return null
|
|
56
|
-
},
|
|
57
|
-
(array, index, path, typeCheckedValue) => {
|
|
58
|
-
const checker = checkers[index]
|
|
59
|
-
if (checker.unchecked || checker.skipCheck) {
|
|
60
|
-
return null
|
|
61
|
-
}
|
|
62
|
-
const error = checker.check(array[index], emptyChildPath, typeCheckedValue)
|
|
63
|
-
return error
|
|
64
|
-
? prependPathElementToTypeCheckError(error, path, index, typeCheckedValue)
|
|
65
|
-
: null
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
const thisTc: TypeChecker = new TypeChecker(
|
|
70
|
-
TypeCheckerBaseType.Array,
|
|
71
|
-
|
|
72
|
-
(array, path, typeCheckedValue) => {
|
|
73
|
-
if (!isArray(array) || array.length !== itemTypes.length) {
|
|
74
|
-
return new TypeCheckError({
|
|
75
|
-
path,
|
|
76
|
-
expectedTypeName: getTypeName(thisTc),
|
|
77
|
-
actualValue: array,
|
|
78
|
-
typeCheckedValue,
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return checkTupleItems(array, path, typeCheckedValue)
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
getTypeName,
|
|
86
|
-
typeInfoGen,
|
|
87
|
-
|
|
88
|
-
(array) => {
|
|
89
|
-
if (!isArray(array) || array.length !== itemTypes.length) {
|
|
90
|
-
return null
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
for (let i = 0; i < array.length; i++) {
|
|
94
|
-
const itemActualChecker = checkers[i].snapshotType(array[i])
|
|
95
|
-
if (!itemActualChecker) {
|
|
96
|
-
return null
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return thisTc
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
(array: unknown[]) => {
|
|
104
|
-
return array.map((item, i) => {
|
|
105
|
-
return withErrorPathSegment(i, () => checkers[i].fromSnapshotProcessor(item))
|
|
106
|
-
})
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
(array: unknown[]) => {
|
|
110
|
-
return array.map((item, i) => {
|
|
111
|
-
return withErrorPathSegment(i, () => checkers[i].toSnapshotProcessor(item))
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
return thisTc
|
|
117
|
-
}, typeInfoGen) as any
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* `types.tuple` type info.
|
|
122
|
-
*/
|
|
123
|
-
export class TupleTypeInfo extends TypeInfo {
|
|
124
|
-
readonly kind = "tuple"
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
1
|
+
import type { Path } from "../../parent/pathTypes"
|
|
2
|
+
import { isArray, lazy } from "../../utils"
|
|
3
|
+
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
4
|
+
import { createPerEntryCachedCheck } from "../createPerEntryCachedCheck"
|
|
5
|
+
import { getTypeInfo } from "../getTypeInfo"
|
|
6
|
+
import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
|
|
7
|
+
import type { AnyStandardType, AnyType, ArrayType } from "../schemas"
|
|
8
|
+
import { TypeCheckError } from "../TypeCheckError"
|
|
9
|
+
import {
|
|
10
|
+
lateTypeChecker,
|
|
11
|
+
TypeChecker,
|
|
12
|
+
TypeCheckerBaseType,
|
|
13
|
+
TypeInfo,
|
|
14
|
+
type TypeInfoGen,
|
|
15
|
+
} from "../TypeChecker"
|
|
16
|
+
import { prependPathElementToTypeCheckError } from "../typeCheckErrorUtils"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A type that represents an tuple of values of a given type.
|
|
20
|
+
*
|
|
21
|
+
* Example:
|
|
22
|
+
* ```ts
|
|
23
|
+
* const stringNumberTupleType = types.tuple(types.string, types.number)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @template T Item types.
|
|
27
|
+
* @param itemTypes Type of inner items.
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
|
|
31
|
+
const typeInfoGen: TypeInfoGen = (t) => new TupleTypeInfo(t, itemTypes.map(resolveStandardType))
|
|
32
|
+
|
|
33
|
+
return lateTypeChecker(() => {
|
|
34
|
+
const checkers = itemTypes.map(resolveTypeChecker)
|
|
35
|
+
const emptyChildPath: Path = []
|
|
36
|
+
|
|
37
|
+
const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
|
|
38
|
+
const typeNames = checkers.map((tc) => {
|
|
39
|
+
if (recursiveTypeCheckers.includes(tc)) {
|
|
40
|
+
return "..."
|
|
41
|
+
}
|
|
42
|
+
return tc.getTypeName(...recursiveTypeCheckers, tc)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return "[" + typeNames.join(", ") + "]"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// No setupCachePruning needed: tuple length is fixed, so entries never become stale.
|
|
49
|
+
const checkTupleItems = createPerEntryCachedCheck<number>(
|
|
50
|
+
(_array, checkEntry) => {
|
|
51
|
+
for (let i = 0; i < checkers.length; i++) {
|
|
52
|
+
const error = checkEntry(i)
|
|
53
|
+
if (error) return error
|
|
54
|
+
}
|
|
55
|
+
return null
|
|
56
|
+
},
|
|
57
|
+
(array, index, path, typeCheckedValue) => {
|
|
58
|
+
const checker = checkers[index]
|
|
59
|
+
if (checker.unchecked || checker.skipCheck) {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
const error = checker.check(array[index], emptyChildPath, typeCheckedValue)
|
|
63
|
+
return error
|
|
64
|
+
? prependPathElementToTypeCheckError(error, path, index, typeCheckedValue)
|
|
65
|
+
: null
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const thisTc: TypeChecker = new TypeChecker(
|
|
70
|
+
TypeCheckerBaseType.Array,
|
|
71
|
+
|
|
72
|
+
(array, path, typeCheckedValue) => {
|
|
73
|
+
if (!isArray(array) || array.length !== itemTypes.length) {
|
|
74
|
+
return new TypeCheckError({
|
|
75
|
+
path,
|
|
76
|
+
expectedTypeName: getTypeName(thisTc),
|
|
77
|
+
actualValue: array,
|
|
78
|
+
typeCheckedValue,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return checkTupleItems(array, path, typeCheckedValue)
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
getTypeName,
|
|
86
|
+
typeInfoGen,
|
|
87
|
+
|
|
88
|
+
(array) => {
|
|
89
|
+
if (!isArray(array) || array.length !== itemTypes.length) {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < array.length; i++) {
|
|
94
|
+
const itemActualChecker = checkers[i].snapshotType(array[i])
|
|
95
|
+
if (!itemActualChecker) {
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return thisTc
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
(array: unknown[]) => {
|
|
104
|
+
return array.map((item, i) => {
|
|
105
|
+
return withErrorPathSegment(i, () => checkers[i].fromSnapshotProcessor(item))
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
(array: unknown[]) => {
|
|
110
|
+
return array.map((item, i) => {
|
|
111
|
+
return withErrorPathSegment(i, () => checkers[i].toSnapshotProcessor(item))
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return thisTc
|
|
117
|
+
}, typeInfoGen) as any
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* `types.tuple` type info.
|
|
122
|
+
*/
|
|
123
|
+
export class TupleTypeInfo extends TypeInfo {
|
|
124
|
+
readonly kind = "tuple"
|
|
125
|
+
readonly itemTypes: ReadonlyArray<AnyStandardType>
|
|
126
|
+
|
|
127
|
+
// memoize to always return the same array on the getter
|
|
128
|
+
private _itemTypeInfos = lazy(() => this.itemTypes.map(getTypeInfo))
|
|
129
|
+
|
|
130
|
+
get itemTypeInfos(): ReadonlyArray<TypeInfo> {
|
|
131
|
+
return this._itemTypeInfos()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
constructor(thisType: AnyStandardType, itemTypes: ReadonlyArray<AnyStandardType>) {
|
|
135
|
+
super(thisType)
|
|
136
|
+
this.itemTypes = itemTypes
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -1,160 +1,166 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* @param
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
type IArrayDidChange,
|
|
4
|
+
type IComputedValue,
|
|
5
|
+
type IObjectDidChange,
|
|
6
|
+
observe,
|
|
7
|
+
} from "mobx"
|
|
8
|
+
import type { Path } from "../parent/pathTypes"
|
|
9
|
+
import { isTweakedObject } from "../tweaker/core"
|
|
10
|
+
import type { TypeCheckError } from "./TypeCheckError"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*
|
|
15
|
+
* Per-entry cache map type used by `createPerEntryCachedCheck`.
|
|
16
|
+
*/
|
|
17
|
+
export type PerEntryCache = Map<PropertyKey, IComputedValue<TypeCheckError | null>>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*
|
|
22
|
+
* Callback that sets up reactive pruning of stale per-entry cache entries.
|
|
23
|
+
* Called once per observable value when the aggregation is first created.
|
|
24
|
+
* Should install an observe listener that removes entries from the cache
|
|
25
|
+
* when the collection structure changes (e.g. key removal, array shrink).
|
|
26
|
+
*/
|
|
27
|
+
export type CachePruningSetup = (value: any, capturedEntries: PerEntryCache) => void
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*
|
|
32
|
+
* Pruning strategy for record / plain-object types.
|
|
33
|
+
* Removes a per-entry cached computed when an object key is removed.
|
|
34
|
+
*/
|
|
35
|
+
export const recordCachePruning: CachePruningSetup = (value, capturedEntries) => {
|
|
36
|
+
observe(value, (change: IObjectDidChange) => {
|
|
37
|
+
if (change.type === "remove") {
|
|
38
|
+
capturedEntries.delete(change.name)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
*
|
|
46
|
+
* Pruning strategy for array types.
|
|
47
|
+
* After a splice that shrinks the array, removes per-entry cached computeds
|
|
48
|
+
* for indices that no longer exist (i.e. index >= newLength).
|
|
49
|
+
*/
|
|
50
|
+
export const arrayCachePruning: CachePruningSetup = (value, capturedEntries) => {
|
|
51
|
+
observe(value, (change: IArrayDidChange) => {
|
|
52
|
+
if (change.type === "splice") {
|
|
53
|
+
const newLength = (value as unknown[]).length
|
|
54
|
+
if (capturedEntries.size > newLength) {
|
|
55
|
+
for (const key of capturedEntries.keys()) {
|
|
56
|
+
if ((key as number) >= newLength) {
|
|
57
|
+
capturedEntries.delete(key)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @internal
|
|
67
|
+
*
|
|
68
|
+
* Debug registry mapping observable values to their per-entry cache Maps.
|
|
69
|
+
* Used by tests to verify that stale entries are actually pruned.
|
|
70
|
+
*/
|
|
71
|
+
const __debugPerEntryCaches = new WeakMap<object, PerEntryCache>()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @internal
|
|
75
|
+
*
|
|
76
|
+
* Returns the number of entries in the per-entry cache for the given observable value,
|
|
77
|
+
* or `undefined` if the value has no cached entries. Used by tests to verify pruning.
|
|
78
|
+
*/
|
|
79
|
+
export function __getPerEntryCacheSize(value: object): number | undefined {
|
|
80
|
+
return __debugPerEntryCaches.get(value)?.size
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @internal
|
|
85
|
+
*
|
|
86
|
+
* Creates a check function that uses per-entry MobX computeds + an aggregation computed
|
|
87
|
+
* for fine-grained reactive type checking.
|
|
88
|
+
*
|
|
89
|
+
* For tweaked (observable) values:
|
|
90
|
+
* 1. Per-entry computeds — each reads only one entry's observable, so changing entry A
|
|
91
|
+
* only invalidates entry A's computed.
|
|
92
|
+
* 2. Aggregation computed — iterates per-entry computeds. Thanks to MobX's change propagation,
|
|
93
|
+
* if a per-entry computed re-evaluates but returns the same result (e.g. null → null),
|
|
94
|
+
* the aggregation body is skipped entirely.
|
|
95
|
+
* 3. Stale entries are pruned reactively via an optional `setupCachePruning` callback
|
|
96
|
+
* (e.g. MobX `observe` listener) that fires on structural mutations, preventing
|
|
97
|
+
* memory leaks for dynamic collections.
|
|
98
|
+
*
|
|
99
|
+
* For non-tweaked values (plain snapshots), falls back to imperative checking.
|
|
100
|
+
*
|
|
101
|
+
* @param iterateEntries Called both inside the aggregation computed (cached path) and
|
|
102
|
+
* for the imperative fallback. Receives a `checkEntry(key)` callback that either
|
|
103
|
+
* returns a cached computed result or performs a direct check, depending on the path.
|
|
104
|
+
* Should call `checkEntry(key)` for each entry and return the first error, or null.
|
|
105
|
+
* @param checkEntry Checks a single entry. For tweaked values, the utility wraps this
|
|
106
|
+
* in a non-keepAlive computed automatically. For non-tweaked values, called directly.
|
|
107
|
+
* @param setupCachePruning Optional callback to install reactive pruning of stale entries.
|
|
108
|
+
* Called once per observable value. Use `recordCachePruning` for records/objects,
|
|
109
|
+
* `arrayCachePruning` for arrays, or omit for fixed-schema types (objects, tuples).
|
|
110
|
+
*/
|
|
111
|
+
export function createPerEntryCachedCheck<K extends PropertyKey>(
|
|
112
|
+
iterateEntries: (
|
|
113
|
+
value: any,
|
|
114
|
+
checkEntry: (entryKey: K) => TypeCheckError | null
|
|
115
|
+
) => TypeCheckError | null,
|
|
116
|
+
checkEntry: (value: any, entryKey: K, path: Path, typeCheckedValue: any) => TypeCheckError | null,
|
|
117
|
+
setupCachePruning?: CachePruningSetup
|
|
118
|
+
): (value: any, path: Path, typeCheckedValue: any) => TypeCheckError | null {
|
|
119
|
+
const perEntryCache = new WeakMap<object, PerEntryCache>()
|
|
120
|
+
const aggregationCache = new WeakMap<object, IComputedValue<TypeCheckError | null>>()
|
|
121
|
+
|
|
122
|
+
// Mutable call context updated before each `.get()` so computed closures
|
|
123
|
+
// always use the latest path/typeCheckedValue. Safe because MobX computed
|
|
124
|
+
// evaluation is synchronous and single-threaded.
|
|
125
|
+
let currentPath: Path
|
|
126
|
+
let currentTypeCheckedValue: any
|
|
127
|
+
|
|
128
|
+
return (value: any, path: Path, typeCheckedValue: any): TypeCheckError | null => {
|
|
129
|
+
if (!isTweakedObject(value, true)) {
|
|
130
|
+
return iterateEntries(value, (key) => checkEntry(value, key, path, typeCheckedValue))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Update mutable scope before evaluation.
|
|
134
|
+
currentPath = path
|
|
135
|
+
currentTypeCheckedValue = typeCheckedValue
|
|
136
|
+
|
|
137
|
+
let aggr = aggregationCache.get(value)
|
|
138
|
+
if (!aggr) {
|
|
139
|
+
let entries = perEntryCache.get(value)
|
|
140
|
+
if (!entries) {
|
|
141
|
+
entries = new Map()
|
|
142
|
+
perEntryCache.set(value, entries)
|
|
143
|
+
__debugPerEntryCaches.set(value, entries)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const capturedEntries = entries
|
|
147
|
+
|
|
148
|
+
// Install reactive pruning if a strategy was provided.
|
|
149
|
+
setupCachePruning?.(value, capturedEntries)
|
|
150
|
+
|
|
151
|
+
const cachedCheckEntry = (key: K): TypeCheckError | null => {
|
|
152
|
+
let c = capturedEntries.get(key)
|
|
153
|
+
if (!c) {
|
|
154
|
+
c = computed(() => checkEntry(value, key, currentPath, currentTypeCheckedValue))
|
|
155
|
+
capturedEntries.set(key, c)
|
|
156
|
+
}
|
|
157
|
+
return c.get()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
aggr = computed(() => iterateEntries(value, cachedCheckEntry), { keepAlive: true })
|
|
161
|
+
aggregationCache.set(value, aggr)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return aggr.get()
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from "./SnapshotTypeMismatchError"
|
|
2
|
-
export * from "./schemas"
|
|
3
|
-
export * from "./TypeCheckError"
|
|
4
|
-
export * from "./tProp"
|
|
5
|
-
export { typeCheck } from "./typeCheck"
|
|
6
|
-
export * from "./types"
|
|
1
|
+
export * from "./SnapshotTypeMismatchError"
|
|
2
|
+
export * from "./schemas"
|
|
3
|
+
export * from "./TypeCheckError"
|
|
4
|
+
export * from "./tProp"
|
|
5
|
+
export { typeCheck } from "./typeCheck"
|
|
6
|
+
export * from "./types"
|