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
package/src/parent/path.ts
CHANGED
|
@@ -1,381 +1,386 @@
|
|
|
1
|
-
import { modelIdKey } from "../model/metadata"
|
|
2
|
-
import { isModel } from "../model/utils"
|
|
3
|
-
import { assertTweakedObject } from "../tweaker/core"
|
|
4
|
-
import { isArray, isObject } from "../utils"
|
|
5
|
-
import {
|
|
6
|
-
dataObjectParent,
|
|
7
|
-
dataToModelNode,
|
|
8
|
-
modelToDataNode,
|
|
9
|
-
objectParents,
|
|
10
|
-
reportParentPathObserved,
|
|
11
|
-
} from "./core"
|
|
12
|
-
import type { Path, PathElement, WritablePath } from "./pathTypes"
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Path from an object to its immediate parent.
|
|
16
|
-
*
|
|
17
|
-
* @template T Parent object type.
|
|
18
|
-
*/
|
|
19
|
-
export interface ParentPath<T extends object> {
|
|
20
|
-
/**
|
|
21
|
-
* Parent object.
|
|
22
|
-
*/
|
|
23
|
-
readonly parent: T
|
|
24
|
-
/**
|
|
25
|
-
* Property name (if the parent is an object) or index number (if the parent is an array).
|
|
26
|
-
*/
|
|
27
|
-
readonly path: PathElement
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Path from an object to its root.
|
|
32
|
-
*
|
|
33
|
-
* @template T Root object type.
|
|
34
|
-
*/
|
|
35
|
-
export interface RootPath<T extends object> {
|
|
36
|
-
/**
|
|
37
|
-
* Root object.
|
|
38
|
-
*/
|
|
39
|
-
readonly root: T
|
|
40
|
-
/**
|
|
41
|
-
* Path from the root to the given target, as a string array.
|
|
42
|
-
* If the target is a root itself then the array will be empty.
|
|
43
|
-
*/
|
|
44
|
-
readonly path: Path
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Objects in the path, from root (included) until target (included).
|
|
48
|
-
* If the target is a root then only the target will be included.
|
|
49
|
-
*/
|
|
50
|
-
readonly pathObjects: ReadonlyArray<unknown>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Returns the parent of the target plus the path from the parent to the target, or undefined if it has no parent.
|
|
55
|
-
*
|
|
56
|
-
* @template T Parent object type.
|
|
57
|
-
* @param value Target object.
|
|
58
|
-
* @returns
|
|
59
|
-
*/
|
|
60
|
-
export function getParentPath<T extends object = any>(value: object): ParentPath<T> | undefined {
|
|
61
|
-
assertTweakedObject(value, "value")
|
|
62
|
-
|
|
63
|
-
return fastGetParentPath(value, true)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @internal
|
|
68
|
-
*/
|
|
69
|
-
export function fastGetParentPath<T extends object = any>(
|
|
70
|
-
value: object,
|
|
71
|
-
useAtom: boolean
|
|
72
|
-
): ParentPath<T> | undefined {
|
|
73
|
-
if (useAtom) {
|
|
74
|
-
reportParentPathObserved(value)
|
|
75
|
-
}
|
|
76
|
-
return objectParents.get(value) as ParentPath<T> | undefined
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @internal
|
|
81
|
-
*/
|
|
82
|
-
export function fastGetParentPathIncludingDataObjects<T extends object = any>(
|
|
83
|
-
value: object,
|
|
84
|
-
useAtom: boolean
|
|
85
|
-
): ParentPath<T> | undefined {
|
|
86
|
-
const parentModel = dataObjectParent.get(value)
|
|
87
|
-
if (parentModel) {
|
|
88
|
-
return { parent: parentModel as T, path: "$" }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const parentPath = fastGetParentPath(value, useAtom)
|
|
92
|
-
if (parentPath && isModel(parentPath.parent)) {
|
|
93
|
-
return { parent: parentPath.parent.$ as T, path: parentPath.path }
|
|
94
|
-
}
|
|
95
|
-
return parentPath
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Returns the parent object of the target object, or undefined if there's no parent.
|
|
100
|
-
*
|
|
101
|
-
* @template T Parent object type.
|
|
102
|
-
* @param value Target object.
|
|
103
|
-
* @returns
|
|
104
|
-
*/
|
|
105
|
-
export function getParent<T extends object = any>(value: object): T | undefined {
|
|
106
|
-
assertTweakedObject(value, "value")
|
|
107
|
-
|
|
108
|
-
return fastGetParent(value, true)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @internal
|
|
113
|
-
*/
|
|
114
|
-
export function fastGetParent<T extends object = any>(
|
|
115
|
-
value: object,
|
|
116
|
-
useAtom: boolean
|
|
117
|
-
): T | undefined {
|
|
118
|
-
return fastGetParentPath(value, useAtom)?.parent
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* @internal
|
|
123
|
-
*/
|
|
124
|
-
export function fastGetParentIncludingDataObjects<T extends object = any>(
|
|
125
|
-
value: object,
|
|
126
|
-
useAtom: boolean
|
|
127
|
-
): T | undefined {
|
|
128
|
-
return fastGetParentPathIncludingDataObjects(value, useAtom)?.parent
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Returns if a given object is a model interim data object (`$`).
|
|
133
|
-
*
|
|
134
|
-
* @param value Object to check.
|
|
135
|
-
* @returns true if it is, false otherwise.
|
|
136
|
-
*/
|
|
137
|
-
export function isModelDataObject(value: object): boolean {
|
|
138
|
-
assertTweakedObject(value, "value", true)
|
|
139
|
-
|
|
140
|
-
return fastIsModelDataObject(value)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* @internal
|
|
145
|
-
*/
|
|
146
|
-
export function fastIsModelDataObject(value: object): boolean {
|
|
147
|
-
return dataObjectParent.has(value)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Returns the root of the target plus the path from the root to get to the target.
|
|
152
|
-
*
|
|
153
|
-
* @template T Root object type.
|
|
154
|
-
* @param value Target object.
|
|
155
|
-
* @returns
|
|
156
|
-
*/
|
|
157
|
-
export function getRootPath<T extends object = any>(value: object): RootPath<T> {
|
|
158
|
-
assertTweakedObject(value, "value")
|
|
159
|
-
|
|
160
|
-
return fastGetRootPath(value, true)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* @internal
|
|
165
|
-
*/
|
|
166
|
-
export function fastGetRootPath<T extends object = any>(
|
|
167
|
-
value: object,
|
|
168
|
-
useAtom: boolean
|
|
169
|
-
): RootPath<T> {
|
|
170
|
-
let root = value
|
|
171
|
-
const path = [] as WritablePath
|
|
172
|
-
const pathObjects = [value] as unknown[]
|
|
173
|
-
|
|
174
|
-
let parentPath: ParentPath<any> | undefined
|
|
175
|
-
while ((parentPath = fastGetParentPath(root, useAtom))) {
|
|
176
|
-
root = parentPath.parent
|
|
177
|
-
path.unshift(parentPath.path)
|
|
178
|
-
pathObjects.unshift(parentPath.parent)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return { root, path, pathObjects } as RootPath<any>
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Returns the root of the target object, or itself if the target is a root.
|
|
186
|
-
*
|
|
187
|
-
* @template T Root object type.
|
|
188
|
-
* @param value Target object.
|
|
189
|
-
* @returns
|
|
190
|
-
*/
|
|
191
|
-
export function getRoot<T extends object = any>(value: object): T {
|
|
192
|
-
assertTweakedObject(value, "value")
|
|
193
|
-
|
|
194
|
-
return fastGetRoot(value, true)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* @internal
|
|
199
|
-
*/
|
|
200
|
-
export function fastGetRoot<T extends object = any>(value: object, useAtom: boolean): T {
|
|
201
|
-
let root = value
|
|
202
|
-
|
|
203
|
-
let parentPath: ParentPath<any> | undefined
|
|
204
|
-
while ((parentPath = fastGetParentPath(root, useAtom))) {
|
|
205
|
-
root = parentPath.parent
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return root as T
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Returns if a given object is a root object.
|
|
213
|
-
*
|
|
214
|
-
* @param value Target object.
|
|
215
|
-
* @returns
|
|
216
|
-
*/
|
|
217
|
-
export function isRoot(value: object): boolean {
|
|
218
|
-
assertTweakedObject(value, "value")
|
|
219
|
-
|
|
220
|
-
return !fastGetParent(value, true)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const unresolved = { resolved: false } as const
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Tries to resolve a path from an object.
|
|
227
|
-
*
|
|
228
|
-
* @template T Returned value type.
|
|
229
|
-
* @param pathRootObject Object that serves as path root.
|
|
230
|
-
* @param path Path as an string or number array.
|
|
231
|
-
* @returns An object with `{ resolved: true, value: T }` or `{ resolved: false }`.
|
|
232
|
-
*/
|
|
233
|
-
export function resolvePath<T = any>(
|
|
234
|
-
pathRootObject: object,
|
|
235
|
-
path: Path
|
|
236
|
-
):
|
|
237
|
-
| {
|
|
238
|
-
resolved: true
|
|
239
|
-
value: T
|
|
240
|
-
}
|
|
241
|
-
| {
|
|
242
|
-
resolved: false
|
|
243
|
-
value?: undefined
|
|
244
|
-
} {
|
|
245
|
-
// unit tests rely on this to work with any object
|
|
246
|
-
// assertTweakedObject(pathRootObject, "pathRootObject")
|
|
247
|
-
|
|
248
|
-
let current: any = pathRootObject
|
|
249
|
-
|
|
250
|
-
const len = path.length
|
|
251
|
-
for (let i = 0; i < len; i++) {
|
|
252
|
-
if (!isObject(current)) {
|
|
253
|
-
return unresolved
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const p = path[i]
|
|
257
|
-
|
|
258
|
-
// check just to avoid mobx warnings about trying to access out of bounds index
|
|
259
|
-
if (isArray(current) && +p >= current.length) {
|
|
260
|
-
return unresolved
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (isModel(current)) {
|
|
264
|
-
const dataNode = modelToDataNode(current)
|
|
265
|
-
if (p in dataNode) {
|
|
266
|
-
current = dataNode
|
|
267
|
-
} else if (!(p in current)) {
|
|
268
|
-
return unresolved
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
current = current[p]
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return { resolved: true, value: current }
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* @internal
|
|
280
|
-
*/
|
|
281
|
-
export const skipIdChecking = Symbol("skipIdChecking")
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* @internal
|
|
285
|
-
*
|
|
286
|
-
* Tries to resolve a path from an object while checking ids.
|
|
287
|
-
*
|
|
288
|
-
* @template T Returned value type.
|
|
289
|
-
* @param pathRootObject Object that serves as path root.
|
|
290
|
-
* @param path Path as an string or number array.
|
|
291
|
-
* @param pathIds An array of ids of the models that must be checked, null if not a model or `skipIdChecking` to skip it.
|
|
292
|
-
* @returns An object with `{ resolved: true, value: T }` or `{ resolved: false }`.
|
|
293
|
-
*/
|
|
294
|
-
export function resolvePathCheckingIds<T = any>(
|
|
295
|
-
pathRootObject: object,
|
|
296
|
-
path: Path,
|
|
297
|
-
pathIds: ReadonlyArray<string | null | typeof skipIdChecking>
|
|
298
|
-
):
|
|
299
|
-
| {
|
|
300
|
-
resolved: true
|
|
301
|
-
value: T
|
|
302
|
-
}
|
|
303
|
-
| {
|
|
304
|
-
resolved: false
|
|
305
|
-
value?: undefined
|
|
306
|
-
} {
|
|
307
|
-
// unit tests rely on this to work with any object
|
|
308
|
-
// assertTweakedObject(pathRootObject, "pathRootObject")
|
|
309
|
-
|
|
310
|
-
let current: any = modelToDataNode(pathRootObject)
|
|
311
|
-
// root id is never checked
|
|
312
|
-
|
|
313
|
-
const len = path.length
|
|
314
|
-
for (let i = 0; i < len; i++) {
|
|
315
|
-
if (!isObject(current)) {
|
|
316
|
-
return { resolved: false }
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const p = path[i]
|
|
320
|
-
|
|
321
|
-
// check just to avoid mobx warnings about trying to access out of bounds index
|
|
322
|
-
if (isArray(current)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
*
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
1
|
+
import { modelIdKey } from "../model/metadata"
|
|
2
|
+
import { isModel } from "../model/utils"
|
|
3
|
+
import { assertTweakedObject } from "../tweaker/core"
|
|
4
|
+
import { hasOwnProp, isArray, isObject } from "../utils"
|
|
5
|
+
import {
|
|
6
|
+
dataObjectParent,
|
|
7
|
+
dataToModelNode,
|
|
8
|
+
modelToDataNode,
|
|
9
|
+
objectParents,
|
|
10
|
+
reportParentPathObserved,
|
|
11
|
+
} from "./core"
|
|
12
|
+
import type { Path, PathElement, WritablePath } from "./pathTypes"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Path from an object to its immediate parent.
|
|
16
|
+
*
|
|
17
|
+
* @template T Parent object type.
|
|
18
|
+
*/
|
|
19
|
+
export interface ParentPath<T extends object> {
|
|
20
|
+
/**
|
|
21
|
+
* Parent object.
|
|
22
|
+
*/
|
|
23
|
+
readonly parent: T
|
|
24
|
+
/**
|
|
25
|
+
* Property name (if the parent is an object) or index number (if the parent is an array).
|
|
26
|
+
*/
|
|
27
|
+
readonly path: PathElement
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Path from an object to its root.
|
|
32
|
+
*
|
|
33
|
+
* @template T Root object type.
|
|
34
|
+
*/
|
|
35
|
+
export interface RootPath<T extends object> {
|
|
36
|
+
/**
|
|
37
|
+
* Root object.
|
|
38
|
+
*/
|
|
39
|
+
readonly root: T
|
|
40
|
+
/**
|
|
41
|
+
* Path from the root to the given target, as a string array.
|
|
42
|
+
* If the target is a root itself then the array will be empty.
|
|
43
|
+
*/
|
|
44
|
+
readonly path: Path
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Objects in the path, from root (included) until target (included).
|
|
48
|
+
* If the target is a root then only the target will be included.
|
|
49
|
+
*/
|
|
50
|
+
readonly pathObjects: ReadonlyArray<unknown>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns the parent of the target plus the path from the parent to the target, or undefined if it has no parent.
|
|
55
|
+
*
|
|
56
|
+
* @template T Parent object type.
|
|
57
|
+
* @param value Target object.
|
|
58
|
+
* @returns
|
|
59
|
+
*/
|
|
60
|
+
export function getParentPath<T extends object = any>(value: object): ParentPath<T> | undefined {
|
|
61
|
+
assertTweakedObject(value, "value")
|
|
62
|
+
|
|
63
|
+
return fastGetParentPath(value, true)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export function fastGetParentPath<T extends object = any>(
|
|
70
|
+
value: object,
|
|
71
|
+
useAtom: boolean
|
|
72
|
+
): ParentPath<T> | undefined {
|
|
73
|
+
if (useAtom) {
|
|
74
|
+
reportParentPathObserved(value)
|
|
75
|
+
}
|
|
76
|
+
return objectParents.get(value) as ParentPath<T> | undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
export function fastGetParentPathIncludingDataObjects<T extends object = any>(
|
|
83
|
+
value: object,
|
|
84
|
+
useAtom: boolean
|
|
85
|
+
): ParentPath<T> | undefined {
|
|
86
|
+
const parentModel = dataObjectParent.get(value)
|
|
87
|
+
if (parentModel) {
|
|
88
|
+
return { parent: parentModel as T, path: "$" }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parentPath = fastGetParentPath(value, useAtom)
|
|
92
|
+
if (parentPath && isModel(parentPath.parent)) {
|
|
93
|
+
return { parent: parentPath.parent.$ as T, path: parentPath.path }
|
|
94
|
+
}
|
|
95
|
+
return parentPath
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns the parent object of the target object, or undefined if there's no parent.
|
|
100
|
+
*
|
|
101
|
+
* @template T Parent object type.
|
|
102
|
+
* @param value Target object.
|
|
103
|
+
* @returns
|
|
104
|
+
*/
|
|
105
|
+
export function getParent<T extends object = any>(value: object): T | undefined {
|
|
106
|
+
assertTweakedObject(value, "value")
|
|
107
|
+
|
|
108
|
+
return fastGetParent(value, true)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function fastGetParent<T extends object = any>(
|
|
115
|
+
value: object,
|
|
116
|
+
useAtom: boolean
|
|
117
|
+
): T | undefined {
|
|
118
|
+
return fastGetParentPath(value, useAtom)?.parent
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export function fastGetParentIncludingDataObjects<T extends object = any>(
|
|
125
|
+
value: object,
|
|
126
|
+
useAtom: boolean
|
|
127
|
+
): T | undefined {
|
|
128
|
+
return fastGetParentPathIncludingDataObjects(value, useAtom)?.parent
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Returns if a given object is a model interim data object (`$`).
|
|
133
|
+
*
|
|
134
|
+
* @param value Object to check.
|
|
135
|
+
* @returns true if it is, false otherwise.
|
|
136
|
+
*/
|
|
137
|
+
export function isModelDataObject(value: object): boolean {
|
|
138
|
+
assertTweakedObject(value, "value", true)
|
|
139
|
+
|
|
140
|
+
return fastIsModelDataObject(value)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
export function fastIsModelDataObject(value: object): boolean {
|
|
147
|
+
return dataObjectParent.has(value)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Returns the root of the target plus the path from the root to get to the target.
|
|
152
|
+
*
|
|
153
|
+
* @template T Root object type.
|
|
154
|
+
* @param value Target object.
|
|
155
|
+
* @returns
|
|
156
|
+
*/
|
|
157
|
+
export function getRootPath<T extends object = any>(value: object): RootPath<T> {
|
|
158
|
+
assertTweakedObject(value, "value")
|
|
159
|
+
|
|
160
|
+
return fastGetRootPath(value, true)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @internal
|
|
165
|
+
*/
|
|
166
|
+
export function fastGetRootPath<T extends object = any>(
|
|
167
|
+
value: object,
|
|
168
|
+
useAtom: boolean
|
|
169
|
+
): RootPath<T> {
|
|
170
|
+
let root = value
|
|
171
|
+
const path = [] as WritablePath
|
|
172
|
+
const pathObjects = [value] as unknown[]
|
|
173
|
+
|
|
174
|
+
let parentPath: ParentPath<any> | undefined
|
|
175
|
+
while ((parentPath = fastGetParentPath(root, useAtom))) {
|
|
176
|
+
root = parentPath.parent
|
|
177
|
+
path.unshift(parentPath.path)
|
|
178
|
+
pathObjects.unshift(parentPath.parent)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { root, path, pathObjects } as RootPath<any>
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Returns the root of the target object, or itself if the target is a root.
|
|
186
|
+
*
|
|
187
|
+
* @template T Root object type.
|
|
188
|
+
* @param value Target object.
|
|
189
|
+
* @returns
|
|
190
|
+
*/
|
|
191
|
+
export function getRoot<T extends object = any>(value: object): T {
|
|
192
|
+
assertTweakedObject(value, "value")
|
|
193
|
+
|
|
194
|
+
return fastGetRoot(value, true)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @internal
|
|
199
|
+
*/
|
|
200
|
+
export function fastGetRoot<T extends object = any>(value: object, useAtom: boolean): T {
|
|
201
|
+
let root = value
|
|
202
|
+
|
|
203
|
+
let parentPath: ParentPath<any> | undefined
|
|
204
|
+
while ((parentPath = fastGetParentPath(root, useAtom))) {
|
|
205
|
+
root = parentPath.parent
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return root as T
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Returns if a given object is a root object.
|
|
213
|
+
*
|
|
214
|
+
* @param value Target object.
|
|
215
|
+
* @returns
|
|
216
|
+
*/
|
|
217
|
+
export function isRoot(value: object): boolean {
|
|
218
|
+
assertTweakedObject(value, "value")
|
|
219
|
+
|
|
220
|
+
return !fastGetParent(value, true)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const unresolved = { resolved: false } as const
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Tries to resolve a path from an object.
|
|
227
|
+
*
|
|
228
|
+
* @template T Returned value type.
|
|
229
|
+
* @param pathRootObject Object that serves as path root.
|
|
230
|
+
* @param path Path as an string or number array.
|
|
231
|
+
* @returns An object with `{ resolved: true, value: T }` or `{ resolved: false }`.
|
|
232
|
+
*/
|
|
233
|
+
export function resolvePath<T = any>(
|
|
234
|
+
pathRootObject: object,
|
|
235
|
+
path: Path
|
|
236
|
+
):
|
|
237
|
+
| {
|
|
238
|
+
resolved: true
|
|
239
|
+
value: T
|
|
240
|
+
}
|
|
241
|
+
| {
|
|
242
|
+
resolved: false
|
|
243
|
+
value?: undefined
|
|
244
|
+
} {
|
|
245
|
+
// unit tests rely on this to work with any object
|
|
246
|
+
// assertTweakedObject(pathRootObject, "pathRootObject")
|
|
247
|
+
|
|
248
|
+
let current: any = pathRootObject
|
|
249
|
+
|
|
250
|
+
const len = path.length
|
|
251
|
+
for (let i = 0; i < len; i++) {
|
|
252
|
+
if (!isObject(current)) {
|
|
253
|
+
return unresolved
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const p = path[i]
|
|
257
|
+
|
|
258
|
+
// check just to avoid mobx warnings about trying to access out of bounds index
|
|
259
|
+
if (isArray(current) && +p >= current.length) {
|
|
260
|
+
return unresolved
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (isModel(current)) {
|
|
264
|
+
const dataNode = modelToDataNode(current)
|
|
265
|
+
if (p in dataNode) {
|
|
266
|
+
current = dataNode
|
|
267
|
+
} else if (!(p in current)) {
|
|
268
|
+
return unresolved
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
current = current[p]
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return { resolved: true, value: current }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
export const skipIdChecking = Symbol("skipIdChecking")
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @internal
|
|
285
|
+
*
|
|
286
|
+
* Tries to resolve a path from an object while checking ids.
|
|
287
|
+
*
|
|
288
|
+
* @template T Returned value type.
|
|
289
|
+
* @param pathRootObject Object that serves as path root.
|
|
290
|
+
* @param path Path as an string or number array.
|
|
291
|
+
* @param pathIds An array of ids of the models that must be checked, null if not a model or `skipIdChecking` to skip it.
|
|
292
|
+
* @returns An object with `{ resolved: true, value: T }` or `{ resolved: false }`.
|
|
293
|
+
*/
|
|
294
|
+
export function resolvePathCheckingIds<T = any>(
|
|
295
|
+
pathRootObject: object,
|
|
296
|
+
path: Path,
|
|
297
|
+
pathIds: ReadonlyArray<string | null | typeof skipIdChecking>
|
|
298
|
+
):
|
|
299
|
+
| {
|
|
300
|
+
resolved: true
|
|
301
|
+
value: T
|
|
302
|
+
}
|
|
303
|
+
| {
|
|
304
|
+
resolved: false
|
|
305
|
+
value?: undefined
|
|
306
|
+
} {
|
|
307
|
+
// unit tests rely on this to work with any object
|
|
308
|
+
// assertTweakedObject(pathRootObject, "pathRootObject")
|
|
309
|
+
|
|
310
|
+
let current: any = modelToDataNode(pathRootObject)
|
|
311
|
+
// root id is never checked
|
|
312
|
+
|
|
313
|
+
const len = path.length
|
|
314
|
+
for (let i = 0; i < len; i++) {
|
|
315
|
+
if (!isObject(current)) {
|
|
316
|
+
return { resolved: false }
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const p = path[i]
|
|
320
|
+
|
|
321
|
+
// check just to avoid mobx warnings about trying to access out of bounds index
|
|
322
|
+
if (isArray(current)) {
|
|
323
|
+
if (+p >= current.length) {
|
|
324
|
+
return { resolved: false }
|
|
325
|
+
}
|
|
326
|
+
} else if (!hasOwnProp(current, p)) {
|
|
327
|
+
// we only check props for non-arrays
|
|
328
|
+
return { resolved: false }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const currentMaybeModel = current[p]
|
|
332
|
+
current = modelToDataNode(currentMaybeModel as object)
|
|
333
|
+
|
|
334
|
+
const expectedId = pathIds[i]
|
|
335
|
+
if (expectedId !== skipIdChecking) {
|
|
336
|
+
const currentId = isModel(currentMaybeModel) ? (currentMaybeModel[modelIdKey] ?? null) : null
|
|
337
|
+
if (expectedId !== currentId) {
|
|
338
|
+
return { resolved: false }
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return { resolved: true, value: dataToModelNode(current) }
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Gets the path to get from a parent to a given child.
|
|
348
|
+
* Returns an empty array if the child is actually the given parent or undefined if the child is not a child of the parent.
|
|
349
|
+
*
|
|
350
|
+
* @param fromParent
|
|
351
|
+
* @param toChild
|
|
352
|
+
* @returns
|
|
353
|
+
*/
|
|
354
|
+
export function getParentToChildPath(fromParent: object, toChild: object): Path | undefined {
|
|
355
|
+
assertTweakedObject(fromParent, "fromParent")
|
|
356
|
+
assertTweakedObject(toChild, "toChild")
|
|
357
|
+
|
|
358
|
+
return fastGetParentToChildPath(fromParent, toChild, true)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @internal
|
|
363
|
+
*/
|
|
364
|
+
export function fastGetParentToChildPath(
|
|
365
|
+
fromParent: object,
|
|
366
|
+
toChild: object,
|
|
367
|
+
useAtom: boolean
|
|
368
|
+
): Path | undefined {
|
|
369
|
+
if (fromParent === toChild) {
|
|
370
|
+
return []
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const path: WritablePath = []
|
|
374
|
+
|
|
375
|
+
let current = toChild
|
|
376
|
+
let parentPath: ParentPath<any> | undefined
|
|
377
|
+
while ((parentPath = fastGetParentPath(current, useAtom))) {
|
|
378
|
+
path.unshift(parentPath.path)
|
|
379
|
+
|
|
380
|
+
current = parentPath.parent
|
|
381
|
+
if (current === fromParent) {
|
|
382
|
+
return path
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return undefined
|
|
386
|
+
}
|