mobx-keystone 1.25.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/dist/mobx-keystone.esm.js +3627 -1540
  3. package/dist/mobx-keystone.esm.mjs +3627 -1540
  4. package/dist/mobx-keystone.umd.js +3638 -1543
  5. package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
  6. package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
  7. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +17 -10
  8. package/dist/types/dataModel/dataModelInstanceCache.d.ts +1 -0
  9. package/dist/types/deepChange/onDeepChange.d.ts +9 -0
  10. package/dist/types/parent/arrayPaths.d.ts +1 -0
  11. package/dist/types/parent/detach.d.ts +1 -1
  12. package/dist/types/parent/findChildren.d.ts +1 -1
  13. package/dist/types/parent/treeOrder.d.ts +1 -0
  14. package/dist/types/patch/applyPatches.d.ts +3 -0
  15. package/dist/types/redux/connectReduxDevTools.d.ts +2 -1
  16. package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
  17. package/dist/types/ref/nodesById.d.ts +1 -0
  18. package/dist/types/ref/refIdIndex.d.ts +1 -0
  19. package/dist/types/snapshot/SnapshotOf.d.ts +2 -2
  20. package/dist/types/snapshot/applySnapshot.d.ts +1 -0
  21. package/dist/types/snapshot/fromSnapshot.d.ts +0 -3
  22. package/dist/types/standardActions/arrayActions.d.ts +1 -1
  23. package/dist/types/treeUtils/sandbox.d.ts +0 -4
  24. package/dist/types/tweaker/changeRollback.d.ts +1 -0
  25. package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
  26. package/dist/types/types/TypeCheckError.d.ts +1 -0
  27. package/dist/types/types/utility/typesRefinement.d.ts +2 -2
  28. package/dist/types/utils/ModelPool.d.ts +7 -1
  29. package/dist/types/utils/chunks.d.ts +1 -0
  30. package/dist/types/utils/forEachWithDelayedThrow.d.ts +1 -0
  31. package/dist/types/utils/index.d.ts +13 -0
  32. package/dist/types/utils/mapUtils.d.ts +2 -2
  33. package/dist/types/utils/setLike.d.ts +1 -0
  34. package/package.json +2 -2
  35. package/src/action/applyAction.ts +1 -1
  36. package/src/action/middleware.ts +17 -36
  37. package/src/action/modelFlow.ts +4 -5
  38. package/src/action/mutationBatch.ts +10 -10
  39. package/src/action/pendingActions.ts +3 -4
  40. package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +19 -14
  41. package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +29 -20
  42. package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +1 -1
  43. package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +4 -3
  44. package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +4 -12
  45. package/src/actionMiddlewares/actionSerialization/setSerializer.ts +4 -13
  46. package/src/actionMiddlewares/actionTrackingMiddleware.ts +11 -13
  47. package/src/actionMiddlewares/transactionMiddleware.ts +55 -25
  48. package/src/actionMiddlewares/undoMiddleware.ts +112 -59
  49. package/src/actionMiddlewares/utils.ts +2 -1
  50. package/src/computedTree/computedTree.ts +14 -3
  51. package/src/context/context.ts +17 -45
  52. package/src/dataModel/BaseDataModel.ts +20 -16
  53. package/src/dataModel/dataModelInstanceCache.ts +8 -0
  54. package/src/deepChange/onDeepChange.ts +179 -24
  55. package/src/frozen/Frozen.ts +50 -33
  56. package/src/model/BaseModel.ts +10 -13
  57. package/src/model/newModel.ts +21 -24
  58. package/src/model/utils.ts +2 -2
  59. package/src/modelShared/modelClassInitializer.ts +3 -2
  60. package/src/modelShared/modelDecorator.ts +39 -39
  61. package/src/modelShared/modelInfo.ts +1 -1
  62. package/src/modelShared/prop.ts +6 -9
  63. package/src/modelShared/sharedInternalModel.ts +16 -3
  64. package/src/parent/arrayPaths.ts +242 -0
  65. package/src/parent/coreObjectChildren.ts +300 -48
  66. package/src/parent/detach.ts +1 -1
  67. package/src/parent/findChildren.ts +4 -7
  68. package/src/parent/onChildAttachedTo.ts +157 -55
  69. package/src/parent/path.ts +15 -5
  70. package/src/parent/setParent.ts +34 -15
  71. package/src/parent/treeOrder.ts +53 -0
  72. package/src/parent/walkTree.ts +12 -101
  73. package/src/patch/applyPatches.ts +74 -34
  74. package/src/patch/emitPatch.ts +142 -26
  75. package/src/patch/jsonPatch.ts +5 -0
  76. package/src/patch/patchRecorder.ts +18 -14
  77. package/src/redux/connectReduxDevTools.ts +24 -18
  78. package/src/redux/redux.ts +5 -21
  79. package/src/ref/core.ts +81 -29
  80. package/src/ref/modelRefIdIndex.ts +131 -0
  81. package/src/ref/nodesById.ts +60 -0
  82. package/src/ref/refIdIndex.ts +209 -0
  83. package/src/rootStore/attachDetach.ts +27 -16
  84. package/src/rootStore/rootStore.ts +10 -6
  85. package/src/snapshot/SnapshotOf.ts +2 -2
  86. package/src/snapshot/applySnapshot.ts +27 -19
  87. package/src/snapshot/clone.ts +1 -2
  88. package/src/snapshot/cloneTreeValue.ts +1 -2
  89. package/src/snapshot/fromModelSnapshot.ts +6 -2
  90. package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
  91. package/src/snapshot/fromSnapshot.ts +2 -6
  92. package/src/snapshot/internal.ts +89 -19
  93. package/src/snapshot/onSnapshot.ts +12 -1
  94. package/src/snapshot/reconcileModelSnapshot.ts +6 -6
  95. package/src/snapshot/reconcilePlainObjectSnapshot.ts +5 -4
  96. package/src/standardActions/arrayActions.ts +12 -2
  97. package/src/standardActions/objectActions.ts +3 -2
  98. package/src/transforms/ImmutableDate.ts +4 -0
  99. package/src/transforms/asMap.ts +3 -1
  100. package/src/treeUtils/deepEquals.ts +15 -13
  101. package/src/treeUtils/draft.ts +8 -0
  102. package/src/treeUtils/sandbox.ts +31 -27
  103. package/src/tweaker/changeRollback.ts +218 -0
  104. package/src/tweaker/core.ts +33 -22
  105. package/src/tweaker/treeNodeMetadata.ts +12 -2
  106. package/src/tweaker/tweak.ts +183 -19
  107. package/src/tweaker/tweakArray.ts +176 -33
  108. package/src/tweaker/tweakPlainObject.ts +117 -39
  109. package/src/tweaker/tweakedChangeListeners.ts +61 -0
  110. package/src/tweaker/typeChecking.ts +25 -15
  111. package/src/types/TypeCheckError.ts +17 -19
  112. package/src/types/arrayBased/typesArray.ts +10 -11
  113. package/src/types/createCachedTypeCheck.ts +541 -88
  114. package/src/types/objectBased/typesDataModelData.ts +4 -2
  115. package/src/types/objectBased/typesModel.ts +4 -2
  116. package/src/types/objectBased/typesObject.ts +20 -16
  117. package/src/types/objectBased/typesRecord.ts +8 -18
  118. package/src/types/objectBased/typesRef.ts +2 -2
  119. package/src/types/primitiveBased/typesEnum.ts +4 -2
  120. package/src/types/primitiveBased/typesPrimitive.ts +9 -4
  121. package/src/types/typeCheckErrorUtils.ts +1 -0
  122. package/src/types/utility/typesCodecSupport.ts +97 -56
  123. package/src/types/utility/typesMap.ts +24 -41
  124. package/src/types/utility/typesRefinement.ts +3 -2
  125. package/src/types/utility/typesSet.ts +24 -0
  126. package/src/utils/ModelPool.ts +78 -22
  127. package/src/utils/chainFns.ts +4 -0
  128. package/src/utils/chunks.ts +271 -0
  129. package/src/utils/decorators.ts +1 -1
  130. package/src/utils/errorDiagnostics.ts +6 -2
  131. package/src/utils/forEachWithDelayedThrow.ts +83 -0
  132. package/src/utils/index.ts +71 -2
  133. package/src/utils/mapUtils.ts +3 -7
  134. package/src/utils/setIfDifferent.ts +3 -2
  135. package/src/utils/setLike.ts +134 -0
  136. package/src/wrappers/ArraySet.ts +5 -9
  137. package/src/wrappers/ObjectMap.ts +15 -11
  138. package/src/wrappers/asMap.ts +30 -18
  139. package/src/wrappers/asSet.ts +36 -12
  140. package/dist/types/action/modelFlowPromiseGenerator.d.ts +0 -1
  141. package/src/action/modelFlowPromiseGenerator.ts +0 -127
@@ -7,6 +7,7 @@ import { fastGetParent } from "../parent/path"
7
7
  import { internalApplyPatches } from "../patch/applyPatches"
8
8
  import type { InternalPatchRecorder } from "../patch/emitPatch"
9
9
  import { internalApplySnapshot } from "../snapshot/applySnapshot"
10
+ import { deferModelTypeCheckToBatch } from "./changeRollback"
10
11
  import { runWithoutSnapshotOrPatches } from "./core"
11
12
  import { isTypeCheckingAllowed } from "./withoutTypeChecking"
12
13
 
@@ -34,14 +35,15 @@ function isModelWithTypeChecker(obj: object): obj is AnyModel {
34
35
  * @internal
35
36
  *
36
37
  * Walks up parent chain from `obj`, calling `callback` for each typed model
37
- * ancestor (nearest-first / bottom-up). Stops early if the callback throws.
38
+ * including the target itself (nearest-first / bottom-up). Stops if the callback throws.
38
39
  */
39
- function forEachTypedModelAncestor(obj: object, callback: (model: AnyModel) => void): void {
40
+ export function forEachTypedModelAncestor(obj: object, callback: (model: AnyModel) => void): void {
40
41
  // obj might be a $ data object, so resolve to the model if applicable
41
42
  const start = dataToModelNode(obj)
42
43
 
43
- // If we started from a $ data object, check the model itself
44
- if (start !== obj && isModelWithTypeChecker(start)) {
44
+ // Both model-data mutations and whole-model reconciliation must validate
45
+ // the model itself before checking its ancestors.
46
+ if (isModelWithTypeChecker(start)) {
45
47
  callback(start)
46
48
  }
47
49
 
@@ -76,20 +78,19 @@ export function runTypeCheckingAfterChange(
76
78
  forEachTypedModelAncestor(obj, (model) => {
77
79
  const err = model.typeCheck()
78
80
  if (err) {
79
- // quietly apply inverse patches (do not generate patches, snapshots, actions, etc)
80
- // The re-entrancy guard prevents auto type checking during rollback:
81
- // rollback mutations trigger observe callbacks which call this function,
82
- // and with "all" scope those re-entrant checks could see partially-restored
83
- // intermediate state and attempt another rollback, causing infinite recursion.
81
+ // A rejected individual mutation has not updated its snapshot yet,
82
+ // so inverse patches must stay quiet. Snapshot reconciliation has
83
+ // already published changes: its rollback must update snapshots and
84
+ // emit compensating patches as well.
84
85
  isRollingBackTypeCheckFailure = true
85
86
  try {
86
- runWithoutSnapshotOrPatches(() => {
87
- if (patchRecorder) {
87
+ if (patchRecorder) {
88
+ runWithoutSnapshotOrPatches(() => {
88
89
  internalApplyPatches.call(obj, patchRecorder.invPatches, true)
89
- } else if (snapshotBeforeChanges) {
90
- internalApplySnapshot.call(obj, snapshotBeforeChanges)
91
- }
92
- })
90
+ })
91
+ } else if (snapshotBeforeChanges) {
92
+ internalApplySnapshot.call(obj, snapshotBeforeChanges)
93
+ }
93
94
  } finally {
94
95
  isRollingBackTypeCheckFailure = false
95
96
  }
@@ -98,3 +99,12 @@ export function runTypeCheckingAfterChange(
98
99
  })
99
100
  }
100
101
  }
102
+
103
+ /** @internal Called only when automatic type checking is enabled. */
104
+ export function typeCheckAfterCreation(model: AnyModel): void {
105
+ // a change batch validates it once it completes
106
+ if (!deferModelTypeCheckToBatch(model)) {
107
+ const error = model.typeCheck()
108
+ if (error) error.throw()
109
+ }
110
+ }
@@ -1,4 +1,5 @@
1
- import { fastGetRootPath } from "../parent/path"
1
+ import { dataToModelNode } from "../parent/core"
2
+ import { fastGetParent, fastGetRootPath } from "../parent/path"
2
3
  import type { Path } from "../parent/pathTypes"
3
4
  import { getSnapshot } from "../snapshot/getSnapshot"
4
5
  import { isTweakedObject } from "../tweaker/core"
@@ -10,6 +11,7 @@ import {
10
11
  } from "../utils/errorDiagnostics"
11
12
 
12
13
  export interface TypeCheckErrorData {
14
+ /** Path relative to the value being type checked. */
13
15
  path: Path
14
16
  expectedTypeName: string
15
17
  actualValue: any
@@ -71,8 +73,15 @@ export class TypeCheckError {
71
73
  */
72
74
  throw(): never {
73
75
  const diagnosticsPath = getErrorPathSnapshot()
76
+ const hasTreeParent =
77
+ isTweakedObject(this.typeCheckedValue, true) &&
78
+ fastGetParent(dataToModelNode(this.typeCheckedValue), false) !== undefined
79
+ // Attached values supply their location through the tree. Context supplies
80
+ // the location for detached values, such as models being hydrated.
74
81
  const path =
75
- diagnosticsPath && diagnosticsPath.length > 0 ? [...diagnosticsPath, ...this.path] : this.path
82
+ diagnosticsPath && diagnosticsPath.length > 0 && !hasTreeParent
83
+ ? [...diagnosticsPath, ...this.path]
84
+ : this.path
76
85
 
77
86
  throw new TypeCheckErrorFailure({
78
87
  path,
@@ -146,7 +155,7 @@ function resolveTypeCheckErrorData(data: TypeCheckErrorData): {
146
155
  const modelTrail = data.modelTrail ?? getErrorModelTrailSnapshot()
147
156
  const fullPath = resolveFullPath(data.path, data.typeCheckedValue)
148
157
  const actualValueSnapshot = isTweakedObject(data.actualValue, true)
149
- ? getSnapshot(data.actualValue)
158
+ ? getSnapshot(dataToModelNode(data.actualValue))
150
159
  : data.actualValue
151
160
 
152
161
  return {
@@ -162,22 +171,11 @@ function resolveTypeCheckErrorData(data: TypeCheckErrorData): {
162
171
  }
163
172
 
164
173
  function resolveFullPath(path: Path, typeCheckedValue: any): Path {
165
- let rootPath: Path = []
166
- if (typeCheckedValue && isTweakedObject(typeCheckedValue, true)) {
167
- rootPath = fastGetRootPath(typeCheckedValue, false).path
168
- }
169
- return rootPath.length > 0 && !pathStartsWith(path, rootPath) ? [...rootPath, ...path] : path
170
- }
171
-
172
- function pathStartsWith(path: Path, prefix: Path): boolean {
173
- if (prefix.length > path.length) {
174
- return false
175
- }
176
-
177
- for (let i = 0; i < prefix.length; i++) {
178
- if (path[i] !== prefix[i]) {
179
- return false
174
+ if (isTweakedObject(typeCheckedValue, true)) {
175
+ const rootPath = fastGetRootPath(dataToModelNode(typeCheckedValue), false).path
176
+ if (rootPath.length > 0) {
177
+ return [...rootPath, ...path]
180
178
  }
181
179
  }
182
- return true
180
+ return path
183
181
  }
@@ -1,7 +1,7 @@
1
1
  import type { Path } from "../../parent/pathTypes"
2
2
  import { isArray } from "../../utils"
3
3
  import { withErrorPathSegment } from "../../utils/errorDiagnostics"
4
- import { createWholeContainerCachedCheck } from "../createCachedTypeCheck"
4
+ import { createChunkedArrayCachedCheck } from "../createCachedTypeCheck"
5
5
  import { getTypeInfo } from "../getTypeInfo"
6
6
  import { resolveStandardType, resolveTypeChecker } from "../resolveTypeChecker"
7
7
  import type { AnyStandardType, AnyType, ArrayType } from "../schemas"
@@ -38,18 +38,17 @@ export function typesArray<T extends AnyType>(itemType: T): ArrayType<T[]> {
38
38
  const getTypeName = (...recursiveTypeCheckers: TypeChecker[]) =>
39
39
  `Array<${itemChecker.getTypeName(...recursiveTypeCheckers, itemChecker)}>`
40
40
 
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++) {
46
- const error = itemChecker.check(array[index], emptyChildPath, typeCheckedValue)
47
- if (error) {
48
- return prependPathElementToTypeCheckError(error, path, index, typeCheckedValue)
41
+ const checkArrayItems = createChunkedArrayCachedCheck(
42
+ (items, firstIndex, path, typeCheckedValue) => {
43
+ for (let i = 0; i < items.length; i++) {
44
+ const error = itemChecker.check(items[i], emptyChildPath, typeCheckedValue)
45
+ if (error) {
46
+ return prependPathElementToTypeCheckError(error, path, firstIndex + i, typeCheckedValue)
47
+ }
49
48
  }
49
+ return null
50
50
  }
51
- return null
52
- })
51
+ )
53
52
 
54
53
  const thisTc: TypeChecker = new TypeChecker(
55
54
  TypeCheckerBaseType.Array,