mobx-keystone 1.25.0 → 2.1.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 +145 -0
- package/dist/mobx-keystone.esm.js +3627 -1540
- package/dist/mobx-keystone.esm.mjs +3627 -1540
- package/dist/mobx-keystone.umd.js +3638 -1543
- package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
- package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
- package/dist/types/actionMiddlewares/undoMiddleware.d.ts +17 -10
- package/dist/types/dataModel/dataModelInstanceCache.d.ts +1 -0
- package/dist/types/deepChange/onDeepChange.d.ts +9 -0
- package/dist/types/parent/arrayPaths.d.ts +1 -0
- package/dist/types/parent/detach.d.ts +1 -1
- package/dist/types/parent/findChildren.d.ts +1 -1
- package/dist/types/parent/treeOrder.d.ts +1 -0
- package/dist/types/patch/applyPatches.d.ts +3 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +2 -1
- package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
- package/dist/types/ref/nodesById.d.ts +1 -0
- package/dist/types/ref/refIdIndex.d.ts +1 -0
- package/dist/types/snapshot/SnapshotOf.d.ts +2 -2
- package/dist/types/snapshot/applySnapshot.d.ts +1 -0
- package/dist/types/snapshot/fromSnapshot.d.ts +0 -3
- package/dist/types/standardActions/arrayActions.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +0 -4
- package/dist/types/tweaker/changeRollback.d.ts +1 -0
- package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
- package/dist/types/types/TypeCheckError.d.ts +1 -0
- package/dist/types/types/utility/typesRefinement.d.ts +2 -2
- package/dist/types/utils/ModelPool.d.ts +7 -1
- package/dist/types/utils/chunks.d.ts +1 -0
- package/dist/types/utils/forEachWithDelayedThrow.d.ts +1 -0
- package/dist/types/utils/index.d.ts +13 -0
- package/dist/types/utils/mapUtils.d.ts +2 -2
- package/dist/types/utils/setLike.d.ts +1 -0
- package/package.json +2 -2
- package/src/action/applyAction.ts +1 -1
- package/src/action/middleware.ts +17 -36
- package/src/action/modelFlow.ts +4 -5
- package/src/action/mutationBatch.ts +10 -10
- package/src/action/pendingActions.ts +3 -4
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +19 -14
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +29 -20
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +4 -3
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +4 -12
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +4 -13
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +11 -13
- package/src/actionMiddlewares/transactionMiddleware.ts +55 -25
- package/src/actionMiddlewares/undoMiddleware.ts +112 -59
- package/src/actionMiddlewares/utils.ts +2 -1
- package/src/computedTree/computedTree.ts +14 -3
- package/src/context/context.ts +17 -45
- package/src/dataModel/BaseDataModel.ts +20 -16
- package/src/dataModel/dataModelInstanceCache.ts +8 -0
- package/src/deepChange/onDeepChange.ts +179 -24
- package/src/frozen/Frozen.ts +50 -33
- package/src/model/BaseModel.ts +10 -13
- package/src/model/newModel.ts +21 -24
- package/src/model/utils.ts +2 -2
- package/src/modelShared/modelClassInitializer.ts +3 -2
- package/src/modelShared/modelDecorator.ts +39 -39
- package/src/modelShared/modelInfo.ts +1 -1
- package/src/modelShared/prop.ts +6 -9
- package/src/modelShared/sharedInternalModel.ts +16 -3
- package/src/parent/arrayPaths.ts +242 -0
- package/src/parent/coreObjectChildren.ts +300 -48
- package/src/parent/detach.ts +1 -1
- package/src/parent/findChildren.ts +4 -7
- package/src/parent/onChildAttachedTo.ts +157 -55
- package/src/parent/path.ts +15 -5
- package/src/parent/setParent.ts +34 -15
- package/src/parent/treeOrder.ts +53 -0
- package/src/parent/walkTree.ts +12 -101
- package/src/patch/applyPatches.ts +74 -34
- package/src/patch/emitPatch.ts +142 -26
- package/src/patch/jsonPatch.ts +5 -0
- package/src/patch/patchRecorder.ts +18 -14
- package/src/redux/connectReduxDevTools.ts +24 -18
- package/src/redux/redux.ts +5 -21
- package/src/ref/core.ts +81 -29
- package/src/ref/modelRefIdIndex.ts +131 -0
- package/src/ref/nodesById.ts +60 -0
- package/src/ref/refIdIndex.ts +209 -0
- package/src/rootStore/attachDetach.ts +27 -16
- package/src/rootStore/rootStore.ts +10 -6
- package/src/snapshot/SnapshotOf.ts +2 -2
- package/src/snapshot/applySnapshot.ts +27 -19
- package/src/snapshot/clone.ts +1 -2
- package/src/snapshot/cloneTreeValue.ts +1 -2
- package/src/snapshot/fromModelSnapshot.ts +6 -2
- package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
- package/src/snapshot/fromSnapshot.ts +2 -6
- package/src/snapshot/internal.ts +89 -19
- package/src/snapshot/onSnapshot.ts +12 -1
- package/src/snapshot/reconcileModelSnapshot.ts +6 -6
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +5 -4
- package/src/standardActions/arrayActions.ts +12 -2
- package/src/standardActions/objectActions.ts +3 -2
- package/src/transforms/ImmutableDate.ts +4 -0
- package/src/transforms/asMap.ts +3 -1
- package/src/treeUtils/deepEquals.ts +15 -13
- package/src/treeUtils/draft.ts +8 -0
- package/src/treeUtils/sandbox.ts +31 -27
- package/src/tweaker/changeRollback.ts +218 -0
- package/src/tweaker/core.ts +33 -22
- package/src/tweaker/treeNodeMetadata.ts +12 -2
- package/src/tweaker/tweak.ts +183 -19
- package/src/tweaker/tweakArray.ts +176 -33
- package/src/tweaker/tweakPlainObject.ts +117 -39
- package/src/tweaker/tweakedChangeListeners.ts +61 -0
- package/src/tweaker/typeChecking.ts +25 -15
- package/src/types/TypeCheckError.ts +17 -19
- package/src/types/arrayBased/typesArray.ts +10 -11
- package/src/types/createCachedTypeCheck.ts +541 -88
- package/src/types/objectBased/typesDataModelData.ts +4 -2
- package/src/types/objectBased/typesModel.ts +4 -2
- package/src/types/objectBased/typesObject.ts +20 -16
- package/src/types/objectBased/typesRecord.ts +8 -18
- package/src/types/objectBased/typesRef.ts +2 -2
- package/src/types/primitiveBased/typesEnum.ts +4 -2
- package/src/types/primitiveBased/typesPrimitive.ts +9 -4
- package/src/types/typeCheckErrorUtils.ts +1 -0
- package/src/types/utility/typesCodecSupport.ts +97 -56
- package/src/types/utility/typesMap.ts +24 -41
- package/src/types/utility/typesRefinement.ts +3 -2
- package/src/types/utility/typesSet.ts +24 -0
- package/src/utils/ModelPool.ts +78 -22
- package/src/utils/chainFns.ts +4 -0
- package/src/utils/chunks.ts +271 -0
- package/src/utils/decorators.ts +1 -1
- package/src/utils/errorDiagnostics.ts +6 -2
- package/src/utils/forEachWithDelayedThrow.ts +83 -0
- package/src/utils/index.ts +71 -2
- package/src/utils/mapUtils.ts +3 -7
- package/src/utils/setIfDifferent.ts +3 -2
- package/src/utils/setLike.ts +134 -0
- package/src/wrappers/ArraySet.ts +5 -9
- package/src/wrappers/ObjectMap.ts +15 -11
- package/src/wrappers/asMap.ts +30 -18
- package/src/wrappers/asSet.ts +36 -12
- package/dist/types/action/modelFlowPromiseGenerator.d.ts +0 -1
- package/src/action/modelFlowPromiseGenerator.ts +0 -127
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
import { modelInfoByClass } from "../../modelShared/modelInfo"
|
|
11
11
|
import { getInternalModelClassPropsInfo } from "../../modelShared/modelPropsInfo"
|
|
12
12
|
import { noDefaultValue } from "../../modelShared/prop"
|
|
13
|
-
import { failure, lazy } from "../../utils"
|
|
13
|
+
import { failure, lazy, setProtoProp } from "../../utils"
|
|
14
14
|
import { getTypeInfo } from "../getTypeInfo"
|
|
15
15
|
import {
|
|
16
16
|
registerStandardTypeResolver,
|
|
@@ -168,12 +168,14 @@ export class DataModelDataTypeInfo extends TypeInfo {
|
|
|
168
168
|
hasDefault = true
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
const propInfo = {
|
|
172
172
|
type,
|
|
173
173
|
typeInfo,
|
|
174
174
|
hasDefault,
|
|
175
175
|
default: defaultValue,
|
|
176
176
|
}
|
|
177
|
+
if (propName === "__proto__") setProtoProp(propTypes, propInfo)
|
|
178
|
+
else propTypes[propName] = propInfo
|
|
177
179
|
})
|
|
178
180
|
return propTypes
|
|
179
181
|
})
|
|
@@ -7,7 +7,7 @@ import type { ModelClass } from "../../modelShared/BaseModelShared"
|
|
|
7
7
|
import { modelInfoByClass } from "../../modelShared/modelInfo"
|
|
8
8
|
import { getInternalModelClassPropsInfo } from "../../modelShared/modelPropsInfo"
|
|
9
9
|
import { noDefaultValue } from "../../modelShared/prop"
|
|
10
|
-
import { isObject, lazy } from "../../utils"
|
|
10
|
+
import { isObject, lazy, setProtoProp } from "../../utils"
|
|
11
11
|
import { getTypeInfo } from "../getTypeInfo"
|
|
12
12
|
import { registerStandardTypeResolver, resolveTypeChecker } from "../resolveTypeChecker"
|
|
13
13
|
import type { AnyStandardType, ModelType } from "../schemas"
|
|
@@ -178,12 +178,14 @@ export class ModelTypeInfo extends TypeInfo {
|
|
|
178
178
|
hasDefault = true
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
const propInfo = {
|
|
182
182
|
type,
|
|
183
183
|
typeInfo,
|
|
184
184
|
hasDefault,
|
|
185
185
|
default: defaultValue,
|
|
186
186
|
}
|
|
187
|
+
if (propName === "__proto__") setProtoProp(propTypes, propInfo)
|
|
188
|
+
else propTypes[propName] = propInfo
|
|
187
189
|
})
|
|
188
190
|
return propTypes
|
|
189
191
|
})
|
|
@@ -2,7 +2,16 @@ import { isObservableObject, keys } from "mobx"
|
|
|
2
2
|
import type { O } from "ts-toolbelt"
|
|
3
3
|
import { Frozen } from "../../frozen/Frozen"
|
|
4
4
|
import type { Path } from "../../parent/pathTypes"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
assertIsFunction,
|
|
7
|
+
assertIsObject,
|
|
8
|
+
copyOwnEnumerableProps,
|
|
9
|
+
getMobxVersion,
|
|
10
|
+
isArray,
|
|
11
|
+
isObject,
|
|
12
|
+
lazy,
|
|
13
|
+
setProtoProp,
|
|
14
|
+
} from "../../utils"
|
|
6
15
|
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
7
16
|
import { createIndexedPerEntryCachedCheck } from "../createCachedTypeCheck"
|
|
8
17
|
import { getTypeInfo } from "../getTypeInfo"
|
|
@@ -106,27 +115,20 @@ function typesObjectHelper<S>(objFn: S, frozen: boolean, typeInfoGen: TypeInfoGe
|
|
|
106
115
|
obj: Record<string, unknown>,
|
|
107
116
|
processors: ReadonlyArray<SnapshotProcessor | undefined>
|
|
108
117
|
) => {
|
|
109
|
-
const newObj: typeof obj = {}
|
|
110
|
-
|
|
111
118
|
// note: we allow excess properties when checking objects
|
|
112
|
-
|
|
113
|
-
for (let i = 0; i < keys.length; i++) {
|
|
114
|
-
const k = keys[i]
|
|
119
|
+
return copyOwnEnumerableProps({}, obj, (value, k) => {
|
|
115
120
|
const schemaEntry = schemaEntryByPropName[k]
|
|
116
121
|
if (schemaEntry) {
|
|
117
122
|
const processor = processors[schemaEntry.processorIndex]
|
|
118
|
-
|
|
119
|
-
} else {
|
|
120
|
-
// unknown prop, copy as is
|
|
121
|
-
newObj[k] = obj[k]
|
|
123
|
+
return processor ? withErrorPathSegment(k, () => processor(value)) : value
|
|
122
124
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
// unknown prop, copy as is
|
|
126
|
+
return value
|
|
127
|
+
})
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
const checkObjectType = (obj: any, path: Path, typeCheckedValue: any) => {
|
|
129
|
-
if (!isObject(obj) || (frozen && !(obj instanceof Frozen))) {
|
|
131
|
+
if (!isObject(obj) || isArray(obj) || (frozen && !(obj instanceof Frozen))) {
|
|
130
132
|
return new TypeCheckError({
|
|
131
133
|
path,
|
|
132
134
|
expectedTypeName: getTypeName(thisTc),
|
|
@@ -187,7 +189,7 @@ function typesObjectHelper<S>(objFn: S, frozen: boolean, typeInfoGen: TypeInfoGe
|
|
|
187
189
|
typeInfoGen,
|
|
188
190
|
|
|
189
191
|
(obj) => {
|
|
190
|
-
if (!isObject(obj)) {
|
|
192
|
+
if (!isObject(obj) || isArray(obj)) {
|
|
191
193
|
return null
|
|
192
194
|
}
|
|
193
195
|
|
|
@@ -278,7 +280,9 @@ export class ObjectTypeInfo extends TypeInfo {
|
|
|
278
280
|
const propTypes: O.Writable<ObjectTypeInfoProps> = {}
|
|
279
281
|
Object.keys(objSchema).forEach((propName) => {
|
|
280
282
|
const type = resolveStandardType(objSchema[propName])
|
|
281
|
-
|
|
283
|
+
const propInfo = { type, typeInfo: getTypeInfo(type) }
|
|
284
|
+
if (propName === "__proto__") setProtoProp(propTypes, propInfo)
|
|
285
|
+
else propTypes[propName] = propInfo
|
|
282
286
|
})
|
|
283
287
|
return propTypes
|
|
284
288
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isObservableObject, keys } from "mobx"
|
|
2
2
|
import type { Path } from "../../parent/pathTypes"
|
|
3
|
-
import { failure, isObject } from "../../utils"
|
|
3
|
+
import { copyOwnEnumerableProps, failure, isArray, isObject } from "../../utils"
|
|
4
4
|
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
5
|
-
import {
|
|
5
|
+
import { createChunkedRecordCachedCheck } from "../createCachedTypeCheck"
|
|
6
6
|
import { getTypeInfo } from "../getTypeInfo"
|
|
7
7
|
import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
|
|
8
8
|
import type { AnyStandardType, AnyType, RecordType } from "../schemas"
|
|
@@ -48,16 +48,9 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
|
|
|
48
48
|
return obj
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
for (let i = 0; i < keys.length; i++) {
|
|
55
|
-
const k = keys[i]
|
|
56
|
-
const v = withErrorPathSegment(k, () => processor(obj[k]))
|
|
57
|
-
newObj[k] = v
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return newObj
|
|
51
|
+
return copyOwnEnumerableProps({}, obj, (value, key) =>
|
|
52
|
+
withErrorPathSegment(key, () => processor(value))
|
|
53
|
+
)
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
const iterateRecordEntries = (
|
|
@@ -88,16 +81,13 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
|
|
|
88
81
|
return error ? prependPathElementToTypeCheckError(error, path, key, typeCheckedValue) : null
|
|
89
82
|
}
|
|
90
83
|
|
|
91
|
-
const checkRecordValues =
|
|
92
|
-
iterateRecordEntries,
|
|
93
|
-
checkRecordEntry
|
|
94
|
-
)
|
|
84
|
+
const checkRecordValues = createChunkedRecordCachedCheck(iterateRecordEntries, checkRecordEntry)
|
|
95
85
|
|
|
96
86
|
const thisTc: TypeChecker = new TypeChecker(
|
|
97
87
|
TypeCheckerBaseType.Object,
|
|
98
88
|
|
|
99
89
|
(obj, path, typeCheckedValue) => {
|
|
100
|
-
if (!isObject(obj)) {
|
|
90
|
+
if (!isObject(obj) || isArray(obj)) {
|
|
101
91
|
return new TypeCheckError({
|
|
102
92
|
path,
|
|
103
93
|
expectedTypeName: getTypeName(thisTc),
|
|
@@ -117,7 +107,7 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
|
|
|
117
107
|
typeInfoGen,
|
|
118
108
|
|
|
119
109
|
(obj) => {
|
|
120
|
-
if (!isObject(obj)) {
|
|
110
|
+
if (!isObject(obj) || isArray(obj)) {
|
|
121
111
|
return null
|
|
122
112
|
}
|
|
123
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { modelTypeKey } from "../../model/metadata"
|
|
2
2
|
import { modelInfoByClass } from "../../modelShared/modelInfo"
|
|
3
|
-
import { Ref,
|
|
3
|
+
import type { Ref, RefConstructor } from "../../ref/Ref"
|
|
4
4
|
import { isObject } from "../../utils"
|
|
5
5
|
import { typesString } from "../primitiveBased/typesPrimitive"
|
|
6
6
|
import { resolveTypeChecker } from "../resolveTypeChecker"
|
|
@@ -36,7 +36,7 @@ export function typesRef<O extends object>(refConstructor: RefConstructor<O>): M
|
|
|
36
36
|
TypeCheckerBaseType.Object,
|
|
37
37
|
|
|
38
38
|
(value, path, typeCheckedValue) => {
|
|
39
|
-
if (!(value instanceof
|
|
39
|
+
if (!(value instanceof refConstructor.refClass)) {
|
|
40
40
|
return new TypeCheckError({
|
|
41
41
|
path,
|
|
42
42
|
expectedTypeName: typeName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertIsObject } from "../../utils"
|
|
1
|
+
import { assertIsObject, isEqualOrBothNaN } from "../../utils"
|
|
2
2
|
import type { IdentityType } from "../schemas"
|
|
3
3
|
import { typesOr } from "../utility/typesOr"
|
|
4
4
|
import { typesLiteral } from "./typesPrimitive"
|
|
@@ -17,6 +17,7 @@ export interface EnumLike {
|
|
|
17
17
|
*/
|
|
18
18
|
export function enumValues(e: EnumLike): (string | number)[] {
|
|
19
19
|
const vals: (string | number)[] = []
|
|
20
|
+
const seen = new Set<string | number>()
|
|
20
21
|
for (const k of Object.keys(e)) {
|
|
21
22
|
const v = e[k]
|
|
22
23
|
// we have to do this since TS does something weird
|
|
@@ -24,7 +25,8 @@ export function enumValues(e: EnumLike): (string | number)[] {
|
|
|
24
25
|
// Hi = 0 -> { Hi: 0, 0: "Hi" }
|
|
25
26
|
// and SWC currently generates enum code inconsistent with TS/Babel
|
|
26
27
|
// https://github.com/swc-project/swc/issues/3711
|
|
27
|
-
if (!
|
|
28
|
+
if (!seen.has(v) && ((typeof v !== "string" && v !== +k) || !isEqualOrBothNaN(e[v], +k))) {
|
|
29
|
+
seen.add(v)
|
|
28
30
|
vals.push(v)
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertIsPrimitive } from "../../utils"
|
|
1
|
+
import { assertIsPrimitive, isEqualOrBothNaN } 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"
|
|
@@ -31,7 +31,12 @@ export function typesLiteral<T extends PrimitiveValue>(literal: T): IdentityType
|
|
|
31
31
|
typeName = "null"
|
|
32
32
|
break
|
|
33
33
|
default:
|
|
34
|
-
typeName =
|
|
34
|
+
typeName =
|
|
35
|
+
typeof literal === "bigint"
|
|
36
|
+
? `${literal}n`
|
|
37
|
+
: typeof literal === "number" && !Number.isFinite(literal)
|
|
38
|
+
? String(literal)
|
|
39
|
+
: JSON.stringify(literal)
|
|
35
40
|
break
|
|
36
41
|
}
|
|
37
42
|
|
|
@@ -41,7 +46,7 @@ export function typesLiteral<T extends PrimitiveValue>(literal: T): IdentityType
|
|
|
41
46
|
TypeCheckerBaseType.Primitive,
|
|
42
47
|
|
|
43
48
|
(value, path, typeCheckedValue) =>
|
|
44
|
-
value
|
|
49
|
+
isEqualOrBothNaN(value, literal)
|
|
45
50
|
? null
|
|
46
51
|
: new TypeCheckError({
|
|
47
52
|
path,
|
|
@@ -53,7 +58,7 @@ export function typesLiteral<T extends PrimitiveValue>(literal: T): IdentityType
|
|
|
53
58
|
() => typeName,
|
|
54
59
|
typeInfoGen,
|
|
55
60
|
|
|
56
|
-
(value) => (value
|
|
61
|
+
(value) => (isEqualOrBothNaN(value, literal) ? thisTc : null),
|
|
57
62
|
undefined,
|
|
58
63
|
undefined
|
|
59
64
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isObservableArray, isObservableObject, remove } from "mobx"
|
|
2
2
|
import type { ModelPropTransform } from "../../modelShared/prop"
|
|
3
|
-
import { isArray, isObject, lazy } from "../../utils"
|
|
3
|
+
import { copyOwnEnumerableProps, isArray, isObject, lazy } from "../../utils"
|
|
4
4
|
import { setIfDifferent } from "../../utils/setIfDifferent"
|
|
5
5
|
import { ArrayTypeInfo, typesArray } from "../arrayBased/typesArray"
|
|
6
6
|
import { TupleTypeInfo, typesTuple } from "../arrayBased/typesTuple"
|
|
@@ -19,23 +19,26 @@ import {
|
|
|
19
19
|
type RuntimeAdapter,
|
|
20
20
|
} from "./typesCodecCore"
|
|
21
21
|
import { OrTypeInfo, typesOr } from "./typesOr"
|
|
22
|
-
import { RefinementTypeInfo } from "./typesRefinement"
|
|
22
|
+
import { RefinementTypeInfo, typesRefinement } from "./typesRefinement"
|
|
23
23
|
import { SkipCheckTypeInfo, typesSkipCheck } from "./typesSkipCheck"
|
|
24
24
|
import { TagTypeInfo, typesTag } from "./typesTag"
|
|
25
25
|
|
|
26
26
|
const codecSupportCache = new WeakMap<AnyStandardType, ResolvedCodecSupport>()
|
|
27
27
|
|
|
28
28
|
function getArrayIndex(prop: PropertyKey): number | undefined {
|
|
29
|
-
// Proxy
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return
|
|
29
|
+
// Proxy traps receive string or symbol keys, including strings for numeric indices.
|
|
30
|
+
if (typeof prop !== "string") return undefined
|
|
31
|
+
const first = prop.charCodeAt(0)
|
|
32
|
+
if (first < 48 || first > 57) return undefined
|
|
33
|
+
|
|
34
|
+
const index = Number(prop)
|
|
35
|
+
return Number.isInteger(index) && index >= 0 && index < 0xffffffff && String(index) === prop
|
|
36
|
+
? index
|
|
37
|
+
: undefined
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function normalizeArrayIndex(index: number, length: number): number {
|
|
41
|
+
index = Math.trunc(index) || 0
|
|
39
42
|
if (index < 0) {
|
|
40
43
|
return Math.max(length + index, 0)
|
|
41
44
|
}
|
|
@@ -44,6 +47,7 @@ function normalizeArrayIndex(index: number, length: number): number {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
function resolveArrayAtIndex(index: number, length: number): number | undefined {
|
|
50
|
+
index = Math.trunc(index) || 0
|
|
47
51
|
const resolvedIndex = index < 0 ? length + index : index
|
|
48
52
|
|
|
49
53
|
if (resolvedIndex < 0 || resolvedIndex >= length) {
|
|
@@ -90,11 +94,7 @@ function objectLikeToStored(
|
|
|
90
94
|
return runtime as never
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
for (const key of Object.keys(runtime)) {
|
|
95
|
-
stored[key] = convertValue(key, runtime[key])
|
|
96
|
-
}
|
|
97
|
-
return stored
|
|
97
|
+
return copyOwnEnumerableProps({}, runtime, (value, key) => convertValue(key, value))
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
@@ -155,8 +155,7 @@ function createObjectRuntimeAdapter(
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
Reflect.set(target, prop, value)
|
|
159
|
-
return true
|
|
158
|
+
return Reflect.set(target, prop, value)
|
|
160
159
|
},
|
|
161
160
|
|
|
162
161
|
...objectRecordProxyTraps,
|
|
@@ -217,8 +216,7 @@ function createRecordRuntimeAdapter(
|
|
|
217
216
|
return true
|
|
218
217
|
}
|
|
219
218
|
|
|
220
|
-
Reflect.set(target, prop, value)
|
|
221
|
-
return true
|
|
219
|
+
return Reflect.set(target, prop, value)
|
|
222
220
|
},
|
|
223
221
|
|
|
224
222
|
...objectRecordProxyTraps,
|
|
@@ -250,7 +248,9 @@ function createArrayLikeRuntimeAdapter(
|
|
|
250
248
|
const storedByRuntime = new WeakMap<unknown[], unknown[]>()
|
|
251
249
|
|
|
252
250
|
const decodeIndex = (target: unknown[], index: number) =>
|
|
253
|
-
index < 0 ||
|
|
251
|
+
index < 0 ||
|
|
252
|
+
index >= target.length ||
|
|
253
|
+
(!isObservableArray(target) && !Reflect.has(target, index))
|
|
254
254
|
? undefined
|
|
255
255
|
: getItemAdapter(index).toRuntime(target[index], (newStoredValue) => {
|
|
256
256
|
target[index] = newStoredValue
|
|
@@ -265,13 +265,18 @@ function createArrayLikeRuntimeAdapter(
|
|
|
265
265
|
return Reflect.apply(method, receiver, args)
|
|
266
266
|
}
|
|
267
267
|
const materializeRuntimeValues = (target: unknown[]) =>
|
|
268
|
-
|
|
268
|
+
target.map((_value, index) => decodeIndex(target, index))
|
|
269
269
|
const replaceStoredValues = (target: unknown[], runtimeValues: unknown[]) => {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
target.
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
const storedValues = runtimeValues.map((value, index) => getItemAdapter(index).toStored(value))
|
|
271
|
+
if (isObservableArray(target)) {
|
|
272
|
+
target.replace(storedValues)
|
|
273
|
+
} else {
|
|
274
|
+
target.length = storedValues.length
|
|
275
|
+
for (let i = 0; i < storedValues.length; i++) {
|
|
276
|
+
if (i in storedValues) target[i] = storedValues[i]
|
|
277
|
+
else delete target[i]
|
|
278
|
+
}
|
|
279
|
+
}
|
|
275
280
|
}
|
|
276
281
|
const mutateViaRuntime = (
|
|
277
282
|
target: unknown[],
|
|
@@ -296,6 +301,11 @@ function createArrayLikeRuntimeAdapter(
|
|
|
296
301
|
}
|
|
297
302
|
|
|
298
303
|
const proxy: unknown[] = new Proxy(stored, {
|
|
304
|
+
has(target, prop) {
|
|
305
|
+
const index = getArrayIndex(prop)
|
|
306
|
+
if (index !== undefined && isObservableArray(target)) return index < target.length
|
|
307
|
+
return Reflect.has(target, prop)
|
|
308
|
+
},
|
|
299
309
|
get(target, prop, receiver) {
|
|
300
310
|
const index = getArrayIndex(prop)
|
|
301
311
|
if (index !== undefined) {
|
|
@@ -332,8 +342,9 @@ function createArrayLikeRuntimeAdapter(
|
|
|
332
342
|
}
|
|
333
343
|
|
|
334
344
|
const indexValue = target.length - 1
|
|
345
|
+
const present = isObservableArray(target) || indexValue in target
|
|
335
346
|
const value = target.pop()
|
|
336
|
-
return getItemAdapter(indexValue).toRuntime(value)
|
|
347
|
+
return present ? getItemAdapter(indexValue).toRuntime(value) : undefined
|
|
337
348
|
}
|
|
338
349
|
case "shift":
|
|
339
350
|
return () => {
|
|
@@ -341,8 +352,9 @@ function createArrayLikeRuntimeAdapter(
|
|
|
341
352
|
return undefined
|
|
342
353
|
}
|
|
343
354
|
|
|
355
|
+
const present = isObservableArray(target) || 0 in target
|
|
344
356
|
const value = target.shift()
|
|
345
|
-
return getItemAdapter(0).toRuntime(value)
|
|
357
|
+
return present ? getItemAdapter(0).toRuntime(value) : undefined
|
|
346
358
|
}
|
|
347
359
|
case "unshift":
|
|
348
360
|
return (...items: unknown[]) =>
|
|
@@ -350,12 +362,21 @@ function createArrayLikeRuntimeAdapter(
|
|
|
350
362
|
...items.map((item, indexValue) => getItemAdapter(indexValue).toStored(item))
|
|
351
363
|
)
|
|
352
364
|
case "splice":
|
|
353
|
-
return (
|
|
365
|
+
return (...args: [start?: number, deleteCount?: number, ...items: unknown[]]) => {
|
|
366
|
+
const [start = 0, deleteCount, ...items] = args
|
|
354
367
|
const normalizedStart = normalizeArrayIndex(start, target.length)
|
|
355
368
|
const actualDeleteCount =
|
|
356
|
-
|
|
357
|
-
?
|
|
358
|
-
:
|
|
369
|
+
args.length === 0
|
|
370
|
+
? 0
|
|
371
|
+
: args.length === 1
|
|
372
|
+
? target.length - normalizedStart
|
|
373
|
+
: Math.max(
|
|
374
|
+
0,
|
|
375
|
+
Math.min(
|
|
376
|
+
Math.trunc(deleteCount ?? 0) || 0,
|
|
377
|
+
target.length - normalizedStart
|
|
378
|
+
)
|
|
379
|
+
)
|
|
359
380
|
|
|
360
381
|
const removedValues = target
|
|
361
382
|
.slice(normalizedStart, normalizedStart + actualDeleteCount)
|
|
@@ -443,8 +464,7 @@ function createArrayLikeRuntimeAdapter(
|
|
|
443
464
|
return true
|
|
444
465
|
}
|
|
445
466
|
|
|
446
|
-
Reflect.set(target, prop, value)
|
|
447
|
-
return true
|
|
467
|
+
return Reflect.set(target, prop, value)
|
|
448
468
|
},
|
|
449
469
|
})
|
|
450
470
|
|
|
@@ -461,7 +481,7 @@ function createArrayLikeRuntimeAdapter(
|
|
|
461
481
|
return cachedStored
|
|
462
482
|
}
|
|
463
483
|
|
|
464
|
-
return
|
|
484
|
+
return runtime.map((value, index) => getItemAdapter(index).toStored(value))
|
|
465
485
|
}
|
|
466
486
|
|
|
467
487
|
return runtime as never
|
|
@@ -490,44 +510,55 @@ function createCodecLeafRuntimeAdapter(
|
|
|
490
510
|
toRuntime(stored, setStored) {
|
|
491
511
|
const cachedRuntime = isObject(stored) ? runtimeByStored.get(stored) : undefined
|
|
492
512
|
|
|
493
|
-
|
|
513
|
+
// Declared up front (rather than initialized from the call) so that a setter invoked
|
|
514
|
+
// while decoding can read it without hitting the temporal dead zone.
|
|
515
|
+
let runtime: unknown
|
|
516
|
+
let storedChanged = false
|
|
517
|
+
runtime = transform.transform({
|
|
494
518
|
originalValue: stored,
|
|
495
519
|
cachedTransformedValue: cachedRuntime,
|
|
496
520
|
setOriginalValue(newStoredValue) {
|
|
497
521
|
if (setStored) {
|
|
498
|
-
|
|
522
|
+
try {
|
|
523
|
+
setStored(newStoredValue)
|
|
524
|
+
} finally {
|
|
525
|
+
// A retained codec setter can replace the value after decoding. Neither
|
|
526
|
+
// direction of the cache may keep associating it with the old stored value.
|
|
527
|
+
storedChanged = true
|
|
528
|
+
if (isObject(runtime)) storedByRuntime.delete(runtime)
|
|
529
|
+
if (isObject(stored)) runtimeByStored.delete(stored)
|
|
530
|
+
}
|
|
499
531
|
}
|
|
500
532
|
},
|
|
501
533
|
})
|
|
502
534
|
|
|
503
|
-
if (
|
|
504
|
-
storedByRuntime.set(runtime, stored)
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (isObject(stored)) {
|
|
508
|
-
runtimeByStored.set(stored, runtime)
|
|
535
|
+
if (!storedChanged) {
|
|
536
|
+
if (isObject(runtime)) storedByRuntime.set(runtime, stored)
|
|
537
|
+
if (isObject(stored)) runtimeByStored.set(stored, runtime)
|
|
509
538
|
}
|
|
510
539
|
|
|
511
540
|
return runtime
|
|
512
541
|
},
|
|
513
542
|
|
|
514
543
|
toStored(runtime) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
544
|
+
if (isObject(runtime)) {
|
|
545
|
+
const cachedStored = storedByRuntime.get(runtime)
|
|
546
|
+
if (cachedStored !== undefined || storedByRuntime.has(runtime)) {
|
|
547
|
+
return cachedStored
|
|
548
|
+
}
|
|
518
549
|
}
|
|
519
550
|
|
|
551
|
+
let cacheResult = false
|
|
520
552
|
const stored = transform.untransform({
|
|
521
553
|
transformedValue: runtime,
|
|
522
|
-
cacheTransformedValue: () => {
|
|
554
|
+
cacheTransformedValue: () => {
|
|
555
|
+
cacheResult = true
|
|
556
|
+
},
|
|
523
557
|
})
|
|
524
558
|
|
|
525
|
-
if (
|
|
526
|
-
storedByRuntime.set(runtime, stored)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (isObject(stored)) {
|
|
530
|
-
runtimeByStored.set(stored, runtime)
|
|
559
|
+
if (cacheResult) {
|
|
560
|
+
if (isObject(runtime)) storedByRuntime.set(runtime, stored)
|
|
561
|
+
if (isObject(stored)) runtimeByStored.set(stored, runtime)
|
|
531
562
|
}
|
|
532
563
|
|
|
533
564
|
return stored
|
|
@@ -536,7 +567,7 @@ function createCodecLeafRuntimeAdapter(
|
|
|
536
567
|
toStoredIfCached(runtime) {
|
|
537
568
|
if (isObject(runtime)) {
|
|
538
569
|
const cachedStored = storedByRuntime.get(runtime)
|
|
539
|
-
if (cachedStored !== undefined) {
|
|
570
|
+
if (cachedStored !== undefined || storedByRuntime.has(runtime)) {
|
|
540
571
|
return { found: true, value: cachedStored }
|
|
541
572
|
}
|
|
542
573
|
}
|
|
@@ -675,7 +706,7 @@ function resolveCodecSupportForStandardType(type: AnyStandardType): ResolvedCode
|
|
|
675
706
|
}
|
|
676
707
|
} else if (typeInfo instanceof ObjectTypeInfo) {
|
|
677
708
|
const childSupports = lazy(() => {
|
|
678
|
-
const supports: Record<string, ResolvedCodecSupport> =
|
|
709
|
+
const supports: Record<string, ResolvedCodecSupport> = Object.create(null)
|
|
679
710
|
const props = typeInfo.props
|
|
680
711
|
for (const propName of Object.keys(props)) {
|
|
681
712
|
supports[propName] = resolveCodecSupportForStandardType(props[propName].type)
|
|
@@ -690,7 +721,7 @@ function resolveCodecSupportForStandardType(type: AnyStandardType): ResolvedCode
|
|
|
690
721
|
: {
|
|
691
722
|
hasCodec: true,
|
|
692
723
|
storedType: typesObject(() => {
|
|
693
|
-
const storedProps: Record<string, AnyStandardType> =
|
|
724
|
+
const storedProps: Record<string, AnyStandardType> = Object.create(null)
|
|
694
725
|
const props = typeInfo.props
|
|
695
726
|
for (const propName of Object.keys(props)) {
|
|
696
727
|
storedProps[propName] = childSupports()[propName].storedType
|
|
@@ -722,7 +753,17 @@ function resolveCodecSupportForStandardType(type: AnyStandardType): ResolvedCode
|
|
|
722
753
|
} else if (typeInfo instanceof RefinementTypeInfo) {
|
|
723
754
|
const baseSupport = resolveCodecSupportForStandardType(typeInfo.baseType)
|
|
724
755
|
|
|
725
|
-
resolvedSupport = baseSupport.hasCodec
|
|
756
|
+
resolvedSupport = !baseSupport.hasCodec
|
|
757
|
+
? noCodecSupport(type)
|
|
758
|
+
: {
|
|
759
|
+
hasCodec: true,
|
|
760
|
+
storedType: typesRefinement(
|
|
761
|
+
baseSupport.storedType,
|
|
762
|
+
(stored) => typeInfo.checkFunction(baseSupport.adapter.toRuntime(stored)),
|
|
763
|
+
typeInfo.typeName
|
|
764
|
+
),
|
|
765
|
+
adapter: baseSupport.adapter,
|
|
766
|
+
}
|
|
726
767
|
} else if (typeInfo instanceof SkipCheckTypeInfo) {
|
|
727
768
|
const baseSupport = resolveCodecSupportForStandardType(typeInfo.baseType)
|
|
728
769
|
|