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.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +1 -1
- package/dist/mobx-keystone.esm.js +7198 -6242
- package/dist/mobx-keystone.esm.mjs +7198 -6242
- package/dist/mobx-keystone.umd.js +7217 -6243
- package/dist/types/action/runUnprotected.d.ts +8 -4
- package/dist/types/model/Model.d.ts +5 -0
- package/dist/types/modelShared/modelDecorator.d.ts +1 -1
- package/dist/types/modelShared/prop.d.ts +8 -4
- package/dist/types/snapshot/modelInitialData.d.ts +1 -0
- package/dist/types/tweaker/treeNodeMetadata.d.ts +1 -0
- package/dist/types/types/createCachedTypeCheck.d.ts +1 -0
- package/dist/types/utils/index.d.ts +24 -0
- package/package.json +15 -11
- package/src/action/middleware.ts +5 -18
- package/src/action/mutationBatch.ts +72 -0
- package/src/action/runUnprotected.ts +21 -8
- package/src/action/wrapInAction.ts +45 -15
- package/src/actionMiddlewares/undoMiddleware.ts +19 -13
- package/src/dataModel/BaseDataModel.ts +4 -8
- package/src/deepChange/onDeepChange.ts +246 -16
- package/src/model/BaseModel.ts +4 -12
- package/src/model/Model.ts +5 -0
- package/src/model/newModel.ts +68 -36
- package/src/modelShared/modelDecorator.ts +64 -53
- package/src/modelShared/prop.ts +105 -63
- package/src/modelShared/sharedInternalModel.ts +91 -60
- package/src/parent/core.ts +16 -10
- package/src/parent/coreObjectChildren.ts +219 -21
- package/src/parent/path.ts +6 -5
- package/src/parent/setParent.ts +5 -5
- package/src/patch/emitPatch.ts +102 -20
- package/src/ref/Ref.ts +4 -5
- package/src/ref/core.ts +2 -2
- package/src/snapshot/fromArraySnapshot.ts +11 -2
- package/src/snapshot/fromModelSnapshot.ts +10 -3
- package/src/snapshot/fromPlainObjectSnapshot.ts +18 -7
- package/src/snapshot/fromSnapshot.ts +31 -9
- package/src/snapshot/getSnapshot.ts +8 -3
- package/src/snapshot/internal.ts +243 -82
- package/src/snapshot/modelInitialData.ts +41 -0
- package/src/snapshot/reconcileArraySnapshot.ts +9 -3
- package/src/snapshot/reconcileModelSnapshot.ts +97 -17
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +9 -3
- package/src/treeUtils/draft.ts +7 -8
- package/src/tweaker/core.ts +30 -5
- package/src/tweaker/treeNodeMetadata.ts +38 -0
- package/src/tweaker/tweak.ts +15 -4
- package/src/tweaker/tweakArray.ts +159 -121
- package/src/tweaker/tweakFrozen.ts +2 -2
- package/src/tweaker/tweakModel.ts +2 -2
- package/src/tweaker/tweakPlainObject.ts +117 -124
- package/src/tweaker/typeChecking.ts +10 -1
- package/src/types/TypeChecker.ts +261 -15
- package/src/types/arrayBased/typesArray.ts +38 -40
- package/src/types/arrayBased/typesTuple.ts +51 -34
- package/src/types/createCachedTypeCheck.ts +181 -0
- package/src/types/objectBased/typesArraySet.ts +42 -32
- package/src/types/objectBased/typesDataModelData.ts +18 -11
- package/src/types/objectBased/typesModel.ts +1 -1
- package/src/types/objectBased/typesObject.ts +57 -28
- package/src/types/objectBased/typesObjectMap.ts +51 -40
- package/src/types/objectBased/typesRecord.ts +59 -39
- package/src/types/objectBased/typesRef.ts +1 -1
- package/src/types/primitiveBased/typesPrimitive.ts +9 -9
- package/src/types/tProp.ts +80 -44
- package/src/types/utility/typesOr.ts +35 -24
- package/src/types/utility/typesRefinement.ts +21 -8
- package/src/types/utility/typesSkipCheck.ts +21 -8
- package/src/types/utility/typesTag.ts +21 -9
- package/src/types/utility/typesUnchecked.ts +2 -3
- package/src/utils/ModelPool.ts +34 -19
- package/src/utils/errorDiagnostics.ts +60 -18
- package/src/utils/index.ts +142 -9
- package/src/types/createPerEntryCachedCheck.ts +0 -166
- /package/dist/types/{types/createPerEntryCachedCheck.d.ts → action/mutationBatch.d.ts} +0 -0
package/src/types/TypeChecker.ts
CHANGED
|
@@ -6,6 +6,195 @@ import type { TypeCheckError } from "./TypeCheckError"
|
|
|
6
6
|
|
|
7
7
|
type CheckFunction = (value: any, path: Path, typeCheckedValue: any) => TypeCheckError | null
|
|
8
8
|
|
|
9
|
+
/** @internal */
|
|
10
|
+
export type SnapshotProcessor = (snapshot: any) => unknown
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A lazily discovered snapshot-processor node. Dependencies are deliberately
|
|
14
|
+
* TypeCheckers rather than processors so recursive graphs can be discovered
|
|
15
|
+
* before any processor body is compiled.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export interface SnapshotProcessorPlan {
|
|
20
|
+
readonly intrinsic?: boolean
|
|
21
|
+
readonly getDependencies: () => ReadonlyArray<TypeChecker>
|
|
22
|
+
readonly compile: (
|
|
23
|
+
dependencyProcessors: ReadonlyArray<SnapshotProcessor | undefined>
|
|
24
|
+
) => SnapshotProcessor | undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @internal */
|
|
28
|
+
export function snapshotProcessorPlan(
|
|
29
|
+
getDependencies: () => ReadonlyArray<TypeChecker>,
|
|
30
|
+
compile: SnapshotProcessorPlan["compile"],
|
|
31
|
+
intrinsic = false
|
|
32
|
+
): SnapshotProcessorPlan {
|
|
33
|
+
return { intrinsic, getDependencies, compile }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type SnapshotProcessorSource = SnapshotProcessor | SnapshotProcessorPlan | undefined
|
|
37
|
+
type SnapshotProcessorDirection = "from" | "to"
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
export class SnapshotProcessorPlanNode {
|
|
41
|
+
private readonly owner: TypeChecker | undefined
|
|
42
|
+
private readonly direction: SnapshotProcessorDirection
|
|
43
|
+
private readonly source: SnapshotProcessorSource
|
|
44
|
+
private presence: boolean | undefined
|
|
45
|
+
private dependencies: SnapshotProcessorPlanNode[] | undefined
|
|
46
|
+
private discovering = false
|
|
47
|
+
private compiling = false
|
|
48
|
+
private compiled = false
|
|
49
|
+
private compiledProcessor: SnapshotProcessor | undefined
|
|
50
|
+
private forwardingProcessor: SnapshotProcessor | undefined
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
owner: TypeChecker | undefined,
|
|
54
|
+
direction: SnapshotProcessorDirection,
|
|
55
|
+
source: SnapshotProcessorSource
|
|
56
|
+
) {
|
|
57
|
+
this.owner = owner
|
|
58
|
+
this.direction = direction
|
|
59
|
+
this.source = source
|
|
60
|
+
if (typeof source === "function" || source === undefined) {
|
|
61
|
+
this.presence = !!source
|
|
62
|
+
this.compiled = true
|
|
63
|
+
this.compiledProcessor = source
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
resolve(): SnapshotProcessor | undefined {
|
|
68
|
+
if (this.compiled) {
|
|
69
|
+
return this.compiledProcessor
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const discoveredNodes: SnapshotProcessorPlanNode[] = []
|
|
73
|
+
const dependents = new Map<SnapshotProcessorPlanNode, SnapshotProcessorPlanNode[]>()
|
|
74
|
+
this.discover(discoveredNodes, dependents, new Set())
|
|
75
|
+
|
|
76
|
+
// Presence is the least fixed point of
|
|
77
|
+
// intrinsic || any(dependency is present)
|
|
78
|
+
// over the newly discovered graph. An identity-only recursive component
|
|
79
|
+
// therefore remains absent, while an intrinsic processor propagates
|
|
80
|
+
// through every parent that contains it.
|
|
81
|
+
const presentQueue: SnapshotProcessorPlanNode[] = []
|
|
82
|
+
for (let i = 0; i < discoveredNodes.length; i++) {
|
|
83
|
+
const node = discoveredNodes[i]
|
|
84
|
+
const plan = node.source as SnapshotProcessorPlan
|
|
85
|
+
if (plan.intrinsic || node.dependencies!.some((dependency) => dependency.presence === true)) {
|
|
86
|
+
node.presence = true
|
|
87
|
+
presentQueue.push(node)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (let queueIndex = 0; queueIndex < presentQueue.length; queueIndex++) {
|
|
92
|
+
const nodeDependents = dependents.get(presentQueue[queueIndex])
|
|
93
|
+
if (!nodeDependents) {
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < nodeDependents.length; i++) {
|
|
97
|
+
const dependent = nodeDependents[i]
|
|
98
|
+
if (dependent.presence === undefined) {
|
|
99
|
+
dependent.presence = true
|
|
100
|
+
presentQueue.push(dependent)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (let i = 0; i < discoveredNodes.length; i++) {
|
|
106
|
+
const node = discoveredNodes[i]
|
|
107
|
+
if (node.presence === undefined) {
|
|
108
|
+
node.presence = false
|
|
109
|
+
node.compiled = true
|
|
110
|
+
node.compiledProcessor = undefined
|
|
111
|
+
node.dependencies = undefined
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this.compile()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private discover(
|
|
119
|
+
discoveredNodes: SnapshotProcessorPlanNode[],
|
|
120
|
+
dependents: Map<SnapshotProcessorPlanNode, SnapshotProcessorPlanNode[]>,
|
|
121
|
+
visited: Set<SnapshotProcessorPlanNode>
|
|
122
|
+
): void {
|
|
123
|
+
if (this.presence !== undefined || this.discovering || visited.has(this)) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
visited.add(this)
|
|
128
|
+
this.discovering = true
|
|
129
|
+
discoveredNodes.push(this)
|
|
130
|
+
try {
|
|
131
|
+
const plan = this.source as SnapshotProcessorPlan
|
|
132
|
+
const dependencyCheckers = plan.getDependencies()
|
|
133
|
+
this.dependencies = new Array(dependencyCheckers.length)
|
|
134
|
+
for (let i = 0; i < dependencyCheckers.length; i++) {
|
|
135
|
+
const dependency = dependencyCheckers[i].getSnapshotProcessorPlanNode(this.direction)
|
|
136
|
+
this.dependencies[i] = dependency
|
|
137
|
+
if (dependency.presence === undefined) {
|
|
138
|
+
let dependencyDependents = dependents.get(dependency)
|
|
139
|
+
if (!dependencyDependents) {
|
|
140
|
+
dependencyDependents = []
|
|
141
|
+
dependents.set(dependency, dependencyDependents)
|
|
142
|
+
}
|
|
143
|
+
dependencyDependents.push(this)
|
|
144
|
+
}
|
|
145
|
+
dependency.discover(discoveredNodes, dependents, visited)
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
// A lazy schema may temporarily fail while a recursive declaration is
|
|
149
|
+
// still initializing. Leave this node discoverable for a later retry.
|
|
150
|
+
this.dependencies = undefined
|
|
151
|
+
throw error
|
|
152
|
+
} finally {
|
|
153
|
+
this.discovering = false
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private compile(): SnapshotProcessor | undefined {
|
|
158
|
+
if (this.compiled) {
|
|
159
|
+
return this.compiledProcessor
|
|
160
|
+
}
|
|
161
|
+
if (!this.presence) {
|
|
162
|
+
this.compiled = true
|
|
163
|
+
return undefined
|
|
164
|
+
}
|
|
165
|
+
if (this.compiling) {
|
|
166
|
+
if (!this.forwardingProcessor) {
|
|
167
|
+
this.forwardingProcessor = (snapshot) => this.compiledProcessor!(snapshot)
|
|
168
|
+
}
|
|
169
|
+
return this.forwardingProcessor
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.compiling = true
|
|
173
|
+
try {
|
|
174
|
+
const dependencies = this.dependencies!
|
|
175
|
+
const dependencyProcessors = new Array<SnapshotProcessor | undefined>(dependencies.length)
|
|
176
|
+
for (let i = 0; i < dependencies.length; i++) {
|
|
177
|
+
dependencyProcessors[i] = dependencies[i].compileForDependency()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const processor = (this.source as SnapshotProcessorPlan).compile(dependencyProcessors)
|
|
181
|
+
this.compiledProcessor = processor
|
|
182
|
+
this.compiled = true
|
|
183
|
+
this.dependencies = undefined
|
|
184
|
+
return processor
|
|
185
|
+
} finally {
|
|
186
|
+
this.compiling = false
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private compileForDependency(): SnapshotProcessor | undefined {
|
|
191
|
+
const processor = this.compile()
|
|
192
|
+
return processor && this.direction === "to" ? this.owner!.toSnapshotProcessor : processor
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const absentSnapshotProcessorPlanNode = new SnapshotProcessorPlanNode(undefined, "from", undefined)
|
|
197
|
+
|
|
9
198
|
/**
|
|
10
199
|
* @internal
|
|
11
200
|
*/
|
|
@@ -37,6 +226,7 @@ const typeCheckersWithCachedSnapshotProcessorResultsOfObject = new WeakMap<
|
|
|
37
226
|
object,
|
|
38
227
|
Set<TypeChecker>
|
|
39
228
|
>()
|
|
229
|
+
const cachedUndefinedSnapshotProcessorResult = Symbol("cachedUndefinedSnapshotProcessorResult")
|
|
40
230
|
|
|
41
231
|
/**
|
|
42
232
|
* @internal
|
|
@@ -63,8 +253,10 @@ export class TypeChecker {
|
|
|
63
253
|
readonly getTypeName: (...recursiveTypeCheckers: TypeChecker[]) => string
|
|
64
254
|
readonly typeInfoGen: TypeInfoGen
|
|
65
255
|
readonly snapshotType: (sn: unknown) => TypeChecker | null
|
|
66
|
-
private readonly
|
|
67
|
-
private readonly
|
|
256
|
+
private readonly fromSnapshotProcessorSource: SnapshotProcessorSource
|
|
257
|
+
private readonly toSnapshotProcessorSource: SnapshotProcessorSource
|
|
258
|
+
private fromSnapshotProcessorPlanNode: SnapshotProcessorPlanNode | undefined
|
|
259
|
+
private toSnapshotProcessorPlanNode: SnapshotProcessorPlanNode | undefined
|
|
68
260
|
|
|
69
261
|
check(value: any, path: Path, typeCheckedValue: any): TypeCheckError | null {
|
|
70
262
|
if (this.unchecked || this.skipCheck) {
|
|
@@ -86,24 +278,63 @@ export class TypeChecker {
|
|
|
86
278
|
getTypeName: (...recursiveTypeCheckers: TypeChecker[]) => string,
|
|
87
279
|
typeInfoGen: TypeInfoGen,
|
|
88
280
|
snapshotType: (sn: unknown) => TypeChecker | null,
|
|
89
|
-
|
|
90
|
-
|
|
281
|
+
fromSnapshotProcessor: SnapshotProcessorSource,
|
|
282
|
+
toSnapshotProcessor: SnapshotProcessorSource
|
|
91
283
|
) {
|
|
92
284
|
this.baseType = baseType
|
|
93
285
|
this._check = _check
|
|
94
286
|
this.getTypeName = getTypeName
|
|
95
287
|
this.typeInfoGen = typeInfoGen
|
|
96
288
|
this.snapshotType = snapshotType
|
|
97
|
-
this.
|
|
98
|
-
this.
|
|
289
|
+
this.fromSnapshotProcessorSource = fromSnapshotProcessor
|
|
290
|
+
this.toSnapshotProcessorSource = toSnapshotProcessor
|
|
99
291
|
this.unchecked = !_check
|
|
100
292
|
this._cachedTypeInfoGen = lazy(typeInfoGen)
|
|
101
293
|
}
|
|
102
294
|
|
|
295
|
+
getSnapshotProcessorPlanNode(direction: SnapshotProcessorDirection): SnapshotProcessorPlanNode {
|
|
296
|
+
const source =
|
|
297
|
+
direction === "from" ? this.fromSnapshotProcessorSource : this.toSnapshotProcessorSource
|
|
298
|
+
if (source === undefined) {
|
|
299
|
+
return absentSnapshotProcessorPlanNode
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let node =
|
|
303
|
+
direction === "from" ? this.fromSnapshotProcessorPlanNode : this.toSnapshotProcessorPlanNode
|
|
304
|
+
if (!node) {
|
|
305
|
+
node = new SnapshotProcessorPlanNode(this, direction, source)
|
|
306
|
+
if (direction === "from") {
|
|
307
|
+
this.fromSnapshotProcessorPlanNode = node
|
|
308
|
+
} else {
|
|
309
|
+
this.toSnapshotProcessorPlanNode = node
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return node
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private resolveSnapshotProcessor(
|
|
316
|
+
direction: SnapshotProcessorDirection
|
|
317
|
+
): SnapshotProcessor | undefined {
|
|
318
|
+
const source =
|
|
319
|
+
direction === "from" ? this.fromSnapshotProcessorSource : this.toSnapshotProcessorSource
|
|
320
|
+
return typeof source === "function"
|
|
321
|
+
? source
|
|
322
|
+
: source === undefined
|
|
323
|
+
? undefined
|
|
324
|
+
: this.getSnapshotProcessorPlanNode(direction).resolve()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
getFromSnapshotProcessor(): SnapshotProcessor | undefined {
|
|
328
|
+
return this.resolveSnapshotProcessor("from")
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
getToSnapshotProcessor(): SnapshotProcessor | undefined {
|
|
332
|
+
return this.resolveSnapshotProcessor("to") ? this.toSnapshotProcessor : undefined
|
|
333
|
+
}
|
|
334
|
+
|
|
103
335
|
fromSnapshotProcessor = (sn: any): unknown => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return this._fromSnapshotProcessor(sn)
|
|
336
|
+
const processor = this.getFromSnapshotProcessor()
|
|
337
|
+
return processor ? processor(sn) : sn
|
|
107
338
|
}
|
|
108
339
|
|
|
109
340
|
private readonly _toSnapshotProcessorCache = new WeakMap<object, unknown>()
|
|
@@ -113,17 +344,26 @@ export class TypeChecker {
|
|
|
113
344
|
}
|
|
114
345
|
|
|
115
346
|
toSnapshotProcessor = (sn: any): unknown => {
|
|
347
|
+
const processor = this.resolveSnapshotProcessor("to")
|
|
348
|
+
if (!processor) {
|
|
349
|
+
return sn
|
|
350
|
+
}
|
|
351
|
+
|
|
116
352
|
if (typeof sn !== "object" || sn === null) {
|
|
117
353
|
// not cacheable
|
|
118
|
-
return
|
|
354
|
+
return processor(sn)
|
|
119
355
|
}
|
|
120
356
|
|
|
121
|
-
|
|
122
|
-
|
|
357
|
+
const cachedResult = this._toSnapshotProcessorCache.get(sn)
|
|
358
|
+
if (cachedResult !== undefined) {
|
|
359
|
+
return cachedResult === cachedUndefinedSnapshotProcessorResult ? undefined : cachedResult
|
|
123
360
|
}
|
|
124
361
|
|
|
125
|
-
const val =
|
|
126
|
-
this._toSnapshotProcessorCache.set(
|
|
362
|
+
const val = processor(sn)
|
|
363
|
+
this._toSnapshotProcessorCache.set(
|
|
364
|
+
sn,
|
|
365
|
+
val === undefined ? cachedUndefinedSnapshotProcessorResult : val
|
|
366
|
+
)
|
|
127
367
|
|
|
128
368
|
// register this type checker as listener of that sn changes
|
|
129
369
|
const typeCheckerSet = getOrCreate(
|
|
@@ -146,6 +386,8 @@ const lateTypeCheckerSymbol = Symbol("lateTypeCheker")
|
|
|
146
386
|
export interface LateTypeChecker {
|
|
147
387
|
[lateTypeCheckerSymbol]: true
|
|
148
388
|
(): TypeChecker
|
|
389
|
+
getFromSnapshotProcessor(): SnapshotProcessor | undefined
|
|
390
|
+
getToSnapshotProcessor(): SnapshotProcessor | undefined
|
|
149
391
|
typeInfo: TypeInfo
|
|
150
392
|
}
|
|
151
393
|
|
|
@@ -162,7 +404,11 @@ export function lateTypeChecker(fn: () => TypeChecker, typeInfoGen: TypeInfoGen)
|
|
|
162
404
|
cached = fn()
|
|
163
405
|
return cached
|
|
164
406
|
}
|
|
165
|
-
|
|
407
|
+
Object.assign(ltc, {
|
|
408
|
+
[lateTypeCheckerSymbol]: true as const,
|
|
409
|
+
getFromSnapshotProcessor: () => ltc().getFromSnapshotProcessor(),
|
|
410
|
+
getToSnapshotProcessor: () => ltc().getToSnapshotProcessor(),
|
|
411
|
+
})
|
|
166
412
|
|
|
167
413
|
const cachedTypeInfoGen = lazy(typeInfoGen)
|
|
168
414
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Path } from "../../parent/pathTypes"
|
|
2
2
|
import { isArray } from "../../utils"
|
|
3
3
|
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
4
|
-
import {
|
|
4
|
+
import { createWholeContainerCachedCheck } from "../createCachedTypeCheck"
|
|
5
5
|
import { getTypeInfo } from "../getTypeInfo"
|
|
6
6
|
import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
|
|
7
7
|
import type { AnyStandardType, AnyType, ArrayType } from "../schemas"
|
|
8
8
|
import { TypeCheckError } from "../TypeCheckError"
|
|
9
9
|
import {
|
|
10
10
|
lateTypeChecker,
|
|
11
|
+
snapshotProcessorPlan,
|
|
11
12
|
TypeChecker,
|
|
12
13
|
TypeCheckerBaseType,
|
|
13
14
|
TypeInfo,
|
|
@@ -28,31 +29,27 @@ import { prependPathElementToTypeCheckError } from "../typeCheckErrorUtils"
|
|
|
28
29
|
* @returns
|
|
29
30
|
*/
|
|
30
31
|
export function typesArray<T extends AnyType>(itemType: T): ArrayType<T[]> {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const itemChecker = resolveTypeChecker(
|
|
32
|
+
const resolvedItemType = resolveStandardType(itemType)
|
|
33
|
+
const typeInfoGen: TypeInfoGen = (t) => new ArrayTypeInfo(t, resolvedItemType)
|
|
34
|
+
const typeChecker = lateTypeChecker(() => {
|
|
35
|
+
const itemChecker = resolveTypeChecker(resolvedItemType)
|
|
35
36
|
const emptyChildPath: Path = []
|
|
36
37
|
|
|
37
38
|
const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) =>
|
|
38
39
|
`Array<${itemChecker.getTypeName(...recursiveTypeCheckers, itemChecker)}>`
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
return null
|
|
47
|
-
},
|
|
48
|
-
(array, index, path, typeCheckedValue) => {
|
|
41
|
+
// MobX observable arrays invalidate reads at collection granularity. Per-index
|
|
42
|
+
// computeds would therefore all be invalidated by the same mutation while adding
|
|
43
|
+
// a computed and cache entry per item.
|
|
44
|
+
const checkArrayItems = createWholeContainerCachedCheck((array, path, typeCheckedValue) => {
|
|
45
|
+
for (let index = 0; index < array.length; index++) {
|
|
49
46
|
const error = itemChecker.check(array[index], emptyChildPath, typeCheckedValue)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
)
|
|
47
|
+
if (error) {
|
|
48
|
+
return prependPathElementToTypeCheckError(error, path, index, typeCheckedValue)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
})
|
|
56
53
|
|
|
57
54
|
const thisTc: TypeChecker = new TypeChecker(
|
|
58
55
|
TypeCheckerBaseType.Array,
|
|
@@ -93,29 +90,30 @@ export function typesArray<T extends AnyType>(itemType: T): ArrayType<T[]> {
|
|
|
93
90
|
return thisTc
|
|
94
91
|
},
|
|
95
92
|
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
93
|
+
snapshotProcessorPlan(
|
|
94
|
+
() => [itemChecker],
|
|
95
|
+
([itemProcessor]) =>
|
|
96
|
+
itemProcessor
|
|
97
|
+
? (sn: unknown[]) => {
|
|
98
|
+
return sn.map((item, i) => withErrorPathSegment(i, () => itemProcessor(item)))
|
|
99
|
+
}
|
|
100
|
+
: undefined
|
|
101
|
+
),
|
|
102
|
+
|
|
103
|
+
snapshotProcessorPlan(
|
|
104
|
+
() => [itemChecker],
|
|
105
|
+
([itemProcessor]) =>
|
|
106
|
+
itemProcessor
|
|
107
|
+
? (sn: unknown[]) => {
|
|
108
|
+
return sn.map((item, i) => withErrorPathSegment(i, () => itemProcessor(item)))
|
|
109
|
+
}
|
|
110
|
+
: undefined
|
|
111
|
+
)
|
|
115
112
|
)
|
|
116
113
|
|
|
117
114
|
return thisTc
|
|
118
|
-
}, typeInfoGen)
|
|
115
|
+
}, typeInfoGen)
|
|
116
|
+
return typeChecker as any
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
/**
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Path } from "../../parent/pathTypes"
|
|
2
2
|
import { isArray, lazy } from "../../utils"
|
|
3
3
|
import { withErrorPathSegment } from "../../utils/errorDiagnostics"
|
|
4
|
-
import {
|
|
4
|
+
import { createWholeContainerCachedCheck } from "../createCachedTypeCheck"
|
|
5
5
|
import { getTypeInfo } from "../getTypeInfo"
|
|
6
6
|
import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
|
|
7
7
|
import type { AnyStandardType, AnyType, ArrayType } from "../schemas"
|
|
8
8
|
import { TypeCheckError } from "../TypeCheckError"
|
|
9
9
|
import {
|
|
10
10
|
lateTypeChecker,
|
|
11
|
+
snapshotProcessorPlan,
|
|
11
12
|
TypeChecker,
|
|
12
13
|
TypeCheckerBaseType,
|
|
13
14
|
TypeInfo,
|
|
@@ -28,10 +29,10 @@ import { prependPathElementToTypeCheckError } from "../typeCheckErrorUtils"
|
|
|
28
29
|
* @returns
|
|
29
30
|
*/
|
|
30
31
|
export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const checkers =
|
|
32
|
+
const resolvedItemTypes = itemTypes.map(resolveStandardType)
|
|
33
|
+
const typeInfoGen: TypeInfoGen = (t) => new TupleTypeInfo(t, resolvedItemTypes)
|
|
34
|
+
const typeChecker = lateTypeChecker(() => {
|
|
35
|
+
const checkers = resolvedItemTypes.map(resolveTypeChecker)
|
|
35
36
|
const emptyChildPath: Path = []
|
|
36
37
|
|
|
37
38
|
const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) => {
|
|
@@ -45,26 +46,27 @@ export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
|
|
|
45
46
|
return "[" + typeNames.join(", ") + "]"
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return null
|
|
56
|
-
},
|
|
57
|
-
(array, index, path, typeCheckedValue) => {
|
|
58
|
-
const checker = checkers[index]
|
|
49
|
+
const checkTupleItemsImperatively = (
|
|
50
|
+
array: unknown[],
|
|
51
|
+
path: Path,
|
|
52
|
+
typeCheckedValue: any
|
|
53
|
+
): TypeCheckError | null => {
|
|
54
|
+
for (let i = 0; i < checkers.length; i++) {
|
|
55
|
+
const checker = checkers[i]
|
|
59
56
|
if (checker.unchecked || checker.skipCheck) {
|
|
60
|
-
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
const error = checker.check(array[i], emptyChildPath, typeCheckedValue)
|
|
60
|
+
if (error) {
|
|
61
|
+
return prependPathElementToTypeCheckError(error, path, i, typeCheckedValue)
|
|
61
62
|
}
|
|
62
|
-
const error = checker.check(array[index], emptyChildPath, typeCheckedValue)
|
|
63
|
-
return error
|
|
64
|
-
? prependPathElementToTypeCheckError(error, path, index, typeCheckedValue)
|
|
65
|
-
: null
|
|
66
63
|
}
|
|
67
|
-
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// MobX observable arrays invalidate reads at collection granularity, so indexed
|
|
68
|
+
// computeds cannot isolate tuple-item changes and only add allocation overhead.
|
|
69
|
+
const checkTupleItems = createWholeContainerCachedCheck(checkTupleItemsImperatively)
|
|
68
70
|
|
|
69
71
|
const thisTc: TypeChecker = new TypeChecker(
|
|
70
72
|
TypeCheckerBaseType.Array,
|
|
@@ -100,21 +102,36 @@ export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
|
|
|
100
102
|
return thisTc
|
|
101
103
|
},
|
|
102
104
|
|
|
103
|
-
(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
snapshotProcessorPlan(
|
|
106
|
+
() => checkers,
|
|
107
|
+
(processors) =>
|
|
108
|
+
processors.some(Boolean)
|
|
109
|
+
? (array: unknown[]) => {
|
|
110
|
+
return array.map((item, i) => {
|
|
111
|
+
const processor = processors[i]
|
|
112
|
+
return processor ? withErrorPathSegment(i, () => processor(item)) : item
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
: undefined
|
|
116
|
+
),
|
|
117
|
+
|
|
118
|
+
snapshotProcessorPlan(
|
|
119
|
+
() => checkers,
|
|
120
|
+
(processors) =>
|
|
121
|
+
processors.some(Boolean)
|
|
122
|
+
? (array: unknown[]) => {
|
|
123
|
+
return array.map((item, i) => {
|
|
124
|
+
const processor = processors[i]
|
|
125
|
+
return processor ? withErrorPathSegment(i, () => processor(item)) : item
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
: undefined
|
|
129
|
+
)
|
|
114
130
|
)
|
|
115
131
|
|
|
116
132
|
return thisTc
|
|
117
|
-
}, typeInfoGen)
|
|
133
|
+
}, typeInfoGen)
|
|
134
|
+
return typeChecker as any
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
/**
|