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.
Files changed (174) hide show
  1. package/CHANGELOG.md +864 -853
  2. package/README.md +61 -61
  3. package/dist/mobx-keystone.esm.js +11336 -11148
  4. package/dist/mobx-keystone.esm.mjs +11336 -11148
  5. package/dist/mobx-keystone.umd.js +12229 -11738
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +1 -1
  7. package/dist/types/modelShared/prop.d.ts +5 -0
  8. package/dist/types/redux/connectReduxDevTools.d.ts +17 -4
  9. package/dist/types/ref/Ref.d.ts +1 -1
  10. package/dist/types/treeUtils/sandbox.d.ts +1 -1
  11. package/dist/types/types/TypeChecker.d.ts +1 -1
  12. package/dist/types/types/arrayBased/typesArray.d.ts +1 -1
  13. package/dist/types/types/arrayBased/typesTuple.d.ts +1 -1
  14. package/dist/types/types/objectBased/typesArraySet.d.ts +1 -1
  15. package/dist/types/types/objectBased/typesDataModelData.d.ts +1 -1
  16. package/dist/types/types/objectBased/typesModel.d.ts +1 -1
  17. package/dist/types/types/objectBased/typesObject.d.ts +3 -3
  18. package/dist/types/types/objectBased/typesObjectMap.d.ts +1 -1
  19. package/dist/types/types/objectBased/typesRecord.d.ts +1 -1
  20. package/dist/types/types/primitiveBased/typesPrimitive.d.ts +1 -1
  21. package/dist/types/types/types.d.ts +3 -3
  22. package/dist/types/types/utility/typesCodec.d.ts +1 -6
  23. package/dist/types/types/utility/typesCodecCore.d.ts +1 -1
  24. package/dist/types/types/utility/typesOr.d.ts +1 -1
  25. package/dist/types/types/utility/typesRefinement.d.ts +1 -1
  26. package/dist/types/types/utility/typesSkipCheck.d.ts +1 -1
  27. package/dist/types/types/utility/typesTag.d.ts +1 -1
  28. package/dist/types/wrappers/ObjectMap.d.ts +2 -0
  29. package/dist/types/wrappers/asMap.d.ts +10 -6
  30. package/package.json +14 -12
  31. package/src/action/applyAction.ts +125 -114
  32. package/src/action/applyMethodCall.ts +37 -37
  33. package/src/action/isModelAction.ts +16 -16
  34. package/src/action/middleware.ts +137 -137
  35. package/src/action/modelFlow.ts +240 -240
  36. package/src/action/modelFlowPromiseGenerator.ts +127 -125
  37. package/src/action/pendingActions.ts +42 -42
  38. package/src/action/wrapInAction.ts +156 -156
  39. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +1 -1
  40. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +172 -172
  41. package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +20 -20
  42. package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +17 -17
  43. package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +42 -42
  44. package/src/actionMiddlewares/actionSerialization/objectPathSerializer.ts +50 -50
  45. package/src/actionMiddlewares/actionSerialization/objectSnapshotSerializer.ts +21 -21
  46. package/src/actionMiddlewares/actionSerialization/plainObjectSerializer.ts +24 -32
  47. package/src/actionMiddlewares/actionSerialization/primitiveSerializer.ts +51 -51
  48. package/src/actionMiddlewares/actionSerialization/setSerializer.ts +37 -37
  49. package/src/actionMiddlewares/actionTrackingMiddleware.ts +3 -3
  50. package/src/actionMiddlewares/onActionMiddleware.ts +2 -2
  51. package/src/actionMiddlewares/readonlyMiddleware.ts +2 -2
  52. package/src/actionMiddlewares/transactionMiddleware.ts +4 -4
  53. package/src/actionMiddlewares/undoMiddleware.ts +804 -796
  54. package/src/actionMiddlewares/utils.ts +2 -2
  55. package/src/computedTree/computedTree.ts +143 -143
  56. package/src/context/context.ts +1 -1
  57. package/src/dataModel/BaseDataModel.ts +194 -192
  58. package/src/dataModel/actions.ts +1 -1
  59. package/src/dataModel/newDataModel.ts +57 -57
  60. package/src/deepChange/index.ts +1 -1
  61. package/src/deepChange/onDeepChange.ts +244 -244
  62. package/src/frozen/Frozen.ts +146 -146
  63. package/src/globalConfig/globalConfig.ts +107 -107
  64. package/src/index.ts +24 -24
  65. package/src/model/BaseModel.ts +275 -273
  66. package/src/model/ModelConstructorOptions.ts +15 -15
  67. package/src/model/getModelMetadata.ts +51 -51
  68. package/src/model/index.ts +6 -6
  69. package/src/model/mixins.ts +288 -288
  70. package/src/model/newModel.ts +301 -301
  71. package/src/model/utils.ts +249 -249
  72. package/src/modelShared/modelDecorator.ts +260 -253
  73. package/src/modelShared/modelInfo.ts +50 -50
  74. package/src/modelShared/modelSymbols.ts +3 -3
  75. package/src/modelShared/newModel.ts +25 -25
  76. package/src/modelShared/prop.ts +713 -684
  77. package/src/modelShared/sharedInternalModel.ts +427 -417
  78. package/src/modelShared/utils.ts +24 -24
  79. package/src/parent/core.ts +65 -65
  80. package/src/parent/coreObjectChildren.ts +180 -180
  81. package/src/parent/detach.ts +47 -47
  82. package/src/parent/findParent.ts +1 -1
  83. package/src/parent/getChildrenObjects.ts +25 -25
  84. package/src/parent/path.ts +386 -381
  85. package/src/parent/setParent.ts +149 -149
  86. package/src/parent/walkTree.ts +1 -1
  87. package/src/patch/applyPatches.ts +187 -187
  88. package/src/patch/jsonPatch.ts +108 -108
  89. package/src/patch/patchRecorder.ts +6 -1
  90. package/src/redux/connectReduxDevTools.ts +39 -7
  91. package/src/redux/redux.ts +6 -2
  92. package/src/ref/Ref.ts +1 -1
  93. package/src/ref/core.ts +339 -345
  94. package/src/ref/customRef.ts +3 -3
  95. package/src/ref/rootRef.ts +100 -100
  96. package/src/rootStore/attachDetach.ts +89 -89
  97. package/src/rootStore/rootStore.ts +1 -1
  98. package/src/snapshot/SnapshotOf.ts +104 -104
  99. package/src/snapshot/SnapshotProcessingError.ts +45 -45
  100. package/src/snapshot/applySnapshot.ts +177 -181
  101. package/src/snapshot/cloneTreeValue.ts +30 -30
  102. package/src/snapshot/fromArraySnapshot.ts +27 -33
  103. package/src/snapshot/fromModelSnapshot.ts +70 -70
  104. package/src/snapshot/fromPlainObjectSnapshot.ts +47 -42
  105. package/src/snapshot/fromSnapshot.ts +198 -198
  106. package/src/snapshot/getSnapshot.ts +80 -79
  107. package/src/snapshot/index.ts +8 -8
  108. package/src/snapshot/internal.ts +230 -230
  109. package/src/snapshot/reconcileArraySnapshot.ts +70 -70
  110. package/src/snapshot/reconcileModelSnapshot.ts +155 -150
  111. package/src/snapshot/reconcilePlainObjectSnapshot.ts +72 -72
  112. package/src/snapshot/reconcileSnapshot.ts +88 -88
  113. package/src/standardActions/actions.ts +64 -64
  114. package/src/standardActions/objectActions.ts +60 -60
  115. package/src/transforms/ImmutableDate.ts +93 -93
  116. package/src/transforms/asMap.ts +50 -50
  117. package/src/transforms/asSet.ts +22 -22
  118. package/src/transforms/bigint.ts +20 -20
  119. package/src/transforms/date.ts +40 -40
  120. package/src/treeUtils/deepEquals.ts +55 -55
  121. package/src/treeUtils/sandbox.ts +6 -3
  122. package/src/tweaker/TweakerPriority.ts +9 -9
  123. package/src/tweaker/tweak.ts +4 -3
  124. package/src/tweaker/tweakArray.ts +530 -475
  125. package/src/tweaker/tweakFrozen.ts +42 -42
  126. package/src/tweaker/tweakModel.ts +1 -1
  127. package/src/tweaker/tweakPlainObject.ts +355 -355
  128. package/src/tweaker/typeChecking.ts +101 -101
  129. package/src/types/SnapshotTypeMismatchError.ts +46 -46
  130. package/src/types/TypeCheckError.ts +183 -184
  131. package/src/types/TypeChecker.ts +202 -185
  132. package/src/types/arrayBased/typesArray.ts +136 -137
  133. package/src/types/arrayBased/typesTuple.ts +138 -139
  134. package/src/types/createPerEntryCachedCheck.ts +166 -160
  135. package/src/types/index.ts +6 -6
  136. package/src/types/objectBased/typesArraySet.ts +133 -134
  137. package/src/types/objectBased/typesDataModelData.ts +203 -204
  138. package/src/types/objectBased/typesModel.ts +220 -221
  139. package/src/types/objectBased/typesObject.ts +308 -310
  140. package/src/types/objectBased/typesObjectMap.ts +142 -143
  141. package/src/types/objectBased/typesRecord.ts +156 -157
  142. package/src/types/objectBased/typesRef.ts +89 -89
  143. package/src/types/primitiveBased/typesEnum.ts +63 -63
  144. package/src/types/primitiveBased/typesPrimitive.ts +219 -220
  145. package/src/types/registerDefaultStandardTypeResolvers.ts +21 -21
  146. package/src/types/resolveTypeChecker.ts +78 -78
  147. package/src/types/schemas.ts +208 -208
  148. package/src/types/tProp.ts +277 -268
  149. package/src/types/typeCheck.ts +21 -21
  150. package/src/types/typeCheckErrorUtils.ts +23 -23
  151. package/src/types/types.ts +142 -142
  152. package/src/types/utility/typeToStoredData.ts +37 -37
  153. package/src/types/utility/typesBigInt.ts +15 -15
  154. package/src/types/utility/typesCodec.ts +41 -38
  155. package/src/types/utility/typesCodecCore.ts +178 -178
  156. package/src/types/utility/typesCodecSupport.ts +806 -806
  157. package/src/types/utility/typesDate.ts +27 -27
  158. package/src/types/utility/typesMap.ts +253 -222
  159. package/src/types/utility/typesOr.ts +263 -259
  160. package/src/types/utility/typesRefinement.ts +131 -125
  161. package/src/types/utility/typesSet.ts +110 -110
  162. package/src/types/utility/typesSkipCheck.ts +69 -70
  163. package/src/types/utility/typesTag.ts +88 -82
  164. package/src/types/utility/typesUnchecked.ts +40 -40
  165. package/src/utils/AnyFunction.ts +4 -4
  166. package/src/utils/ModelPool.ts +59 -59
  167. package/src/utils/chainFns.ts +18 -18
  168. package/src/utils/decorators.ts +274 -274
  169. package/src/utils/errorDiagnostics.ts +204 -204
  170. package/src/utils/index.ts +443 -372
  171. package/src/wrappers/ArraySet.ts +156 -156
  172. package/src/wrappers/ObjectMap.ts +155 -136
  173. package/src/wrappers/asMap.ts +65 -30
  174. package/src/wrappers/asSet.ts +3 -3
@@ -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) && +p >= current.length) {
323
- return { resolved: false }
324
- }
325
-
326
- const currentMaybeModel = current[p]
327
- current = modelToDataNode(currentMaybeModel as object)
328
-
329
- const expectedId = pathIds[i]
330
- if (expectedId !== skipIdChecking) {
331
- const currentId = isModel(currentMaybeModel) ? (currentMaybeModel[modelIdKey] ?? null) : null
332
- if (expectedId !== currentId) {
333
- return { resolved: false }
334
- }
335
- }
336
- }
337
-
338
- return { resolved: true, value: dataToModelNode(current) }
339
- }
340
-
341
- /**
342
- * Gets the path to get from a parent to a given child.
343
- * Returns an empty array if the child is actually the given parent or undefined if the child is not a child of the parent.
344
- *
345
- * @param fromParent
346
- * @param toChild
347
- * @returns
348
- */
349
- export function getParentToChildPath(fromParent: object, toChild: object): Path | undefined {
350
- assertTweakedObject(fromParent, "fromParent")
351
- assertTweakedObject(toChild, "toChild")
352
-
353
- return fastGetParentToChildPath(fromParent, toChild, true)
354
- }
355
-
356
- /**
357
- * @internal
358
- */
359
- export function fastGetParentToChildPath(
360
- fromParent: object,
361
- toChild: object,
362
- useAtom: boolean
363
- ): Path | undefined {
364
- if (fromParent === toChild) {
365
- return []
366
- }
367
-
368
- const path: WritablePath = []
369
-
370
- let current = toChild
371
- let parentPath: ParentPath<any> | undefined
372
- while ((parentPath = fastGetParentPath(current, useAtom))) {
373
- path.unshift(parentPath.path)
374
-
375
- current = parentPath.parent
376
- if (current === fromParent) {
377
- return path
378
- }
379
- }
380
- return undefined
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
+ }