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.
Files changed (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -21
  3. package/README.md +1 -1
  4. package/dist/mobx-keystone.esm.js +7198 -6242
  5. package/dist/mobx-keystone.esm.mjs +7198 -6242
  6. package/dist/mobx-keystone.umd.js +7217 -6243
  7. package/dist/types/action/runUnprotected.d.ts +8 -4
  8. package/dist/types/model/Model.d.ts +5 -0
  9. package/dist/types/modelShared/modelDecorator.d.ts +1 -1
  10. package/dist/types/modelShared/prop.d.ts +8 -4
  11. package/dist/types/snapshot/modelInitialData.d.ts +1 -0
  12. package/dist/types/tweaker/treeNodeMetadata.d.ts +1 -0
  13. package/dist/types/types/createCachedTypeCheck.d.ts +1 -0
  14. package/dist/types/utils/index.d.ts +24 -0
  15. package/package.json +15 -11
  16. package/src/action/middleware.ts +5 -18
  17. package/src/action/mutationBatch.ts +72 -0
  18. package/src/action/runUnprotected.ts +21 -8
  19. package/src/action/wrapInAction.ts +45 -15
  20. package/src/actionMiddlewares/undoMiddleware.ts +19 -13
  21. package/src/dataModel/BaseDataModel.ts +4 -8
  22. package/src/deepChange/onDeepChange.ts +246 -16
  23. package/src/model/BaseModel.ts +4 -12
  24. package/src/model/Model.ts +5 -0
  25. package/src/model/newModel.ts +68 -36
  26. package/src/modelShared/modelDecorator.ts +64 -53
  27. package/src/modelShared/prop.ts +105 -63
  28. package/src/modelShared/sharedInternalModel.ts +91 -60
  29. package/src/parent/core.ts +16 -10
  30. package/src/parent/coreObjectChildren.ts +219 -21
  31. package/src/parent/path.ts +6 -5
  32. package/src/parent/setParent.ts +5 -5
  33. package/src/patch/emitPatch.ts +102 -20
  34. package/src/ref/Ref.ts +4 -5
  35. package/src/ref/core.ts +2 -2
  36. package/src/snapshot/fromArraySnapshot.ts +11 -2
  37. package/src/snapshot/fromModelSnapshot.ts +10 -3
  38. package/src/snapshot/fromPlainObjectSnapshot.ts +18 -7
  39. package/src/snapshot/fromSnapshot.ts +31 -9
  40. package/src/snapshot/getSnapshot.ts +8 -3
  41. package/src/snapshot/internal.ts +243 -82
  42. package/src/snapshot/modelInitialData.ts +41 -0
  43. package/src/snapshot/reconcileArraySnapshot.ts +9 -3
  44. package/src/snapshot/reconcileModelSnapshot.ts +97 -17
  45. package/src/snapshot/reconcilePlainObjectSnapshot.ts +9 -3
  46. package/src/treeUtils/draft.ts +7 -8
  47. package/src/tweaker/core.ts +30 -5
  48. package/src/tweaker/treeNodeMetadata.ts +38 -0
  49. package/src/tweaker/tweak.ts +15 -4
  50. package/src/tweaker/tweakArray.ts +159 -121
  51. package/src/tweaker/tweakFrozen.ts +2 -2
  52. package/src/tweaker/tweakModel.ts +2 -2
  53. package/src/tweaker/tweakPlainObject.ts +117 -124
  54. package/src/tweaker/typeChecking.ts +10 -1
  55. package/src/types/TypeChecker.ts +261 -15
  56. package/src/types/arrayBased/typesArray.ts +38 -40
  57. package/src/types/arrayBased/typesTuple.ts +51 -34
  58. package/src/types/createCachedTypeCheck.ts +181 -0
  59. package/src/types/objectBased/typesArraySet.ts +42 -32
  60. package/src/types/objectBased/typesDataModelData.ts +18 -11
  61. package/src/types/objectBased/typesModel.ts +1 -1
  62. package/src/types/objectBased/typesObject.ts +57 -28
  63. package/src/types/objectBased/typesObjectMap.ts +51 -40
  64. package/src/types/objectBased/typesRecord.ts +59 -39
  65. package/src/types/objectBased/typesRef.ts +1 -1
  66. package/src/types/primitiveBased/typesPrimitive.ts +9 -9
  67. package/src/types/tProp.ts +80 -44
  68. package/src/types/utility/typesOr.ts +35 -24
  69. package/src/types/utility/typesRefinement.ts +21 -8
  70. package/src/types/utility/typesSkipCheck.ts +21 -8
  71. package/src/types/utility/typesTag.ts +21 -9
  72. package/src/types/utility/typesUnchecked.ts +2 -3
  73. package/src/utils/ModelPool.ts +34 -19
  74. package/src/utils/errorDiagnostics.ts +60 -18
  75. package/src/utils/index.ts +142 -9
  76. package/src/types/createPerEntryCachedCheck.ts +0 -166
  77. /package/dist/types/{types/createPerEntryCachedCheck.d.ts → action/mutationBatch.d.ts} +0 -0
@@ -1,5 +1,8 @@
1
1
  import { isObservableObject, remove } from "mobx"
2
- import { runTypeCheckingAfterChange } from "../tweaker/typeChecking"
2
+ import {
3
+ isTypeCheckingAfterChangeEnabled,
4
+ runTypeCheckingAfterChange,
5
+ } from "../tweaker/typeChecking"
3
6
  import { withoutTypeChecking } from "../tweaker/withoutTypeChecking"
4
7
  import { isPlainObject } from "../utils"
5
8
  import { withErrorPathSegment } from "../utils/errorDiagnostics"
@@ -23,7 +26,8 @@ function reconcilePlainObjectSnapshot(
23
26
  }
24
27
 
25
28
  const plainObj = value
26
- const snapshotBeforeChanges = getSnapshot(plainObj)
29
+ const typeCheckingAfterChangeEnabled = isTypeCheckingAfterChangeEnabled()
30
+ const snapshotBeforeChanges = typeCheckingAfterChangeEnabled ? getSnapshot(plainObj) : undefined
27
31
 
28
32
  withoutTypeChecking(() => {
29
33
  // remove excess props
@@ -54,7 +58,9 @@ function reconcilePlainObjectSnapshot(
54
58
  }
55
59
  })
56
60
 
57
- runTypeCheckingAfterChange(plainObj, undefined, snapshotBeforeChanges)
61
+ if (typeCheckingAfterChangeEnabled) {
62
+ runTypeCheckingAfterChange(plainObj, undefined, snapshotBeforeChanges)
63
+ }
58
64
 
59
65
  return plainObj
60
66
  }
@@ -1,4 +1,3 @@
1
- import { action, computed } from "mobx"
2
1
  import { pathToTargetPathIds } from "../actionMiddlewares/utils"
3
2
  import { resolvePath, resolvePathCheckingIds, skipIdChecking } from "../parent/path"
4
3
  import type { Path } from "../parent/pathTypes"
@@ -7,7 +6,7 @@ import { applySnapshot } from "../snapshot/applySnapshot"
7
6
  import { fromSnapshot } from "../snapshot/fromSnapshot"
8
7
  import { getSnapshot } from "../snapshot/getSnapshot"
9
8
  import { assertTweakedObject } from "../tweaker/core"
10
- import { failure } from "../utils"
9
+ import { failure, mobxAction, mobxComputed } from "../utils"
11
10
  import { deepEquals } from "./deepEquals"
12
11
 
13
12
  /**
@@ -25,7 +24,7 @@ export class Draft<T extends object> {
25
24
  /**
26
25
  * Commits current draft changes to the original object.
27
26
  */
28
- @action
27
+ @mobxAction
29
28
  commit(): void {
30
29
  applySnapshot(this.originalData, getSnapshot(this.data))
31
30
  }
@@ -37,7 +36,7 @@ export class Draft<T extends object> {
37
36
  *
38
37
  * @param path Path to commit.
39
38
  */
40
- @action
39
+ @mobxAction
41
40
  commitByPath(path: Path): void {
42
41
  const draftTarget = resolvePath(this.data, path)
43
42
  if (!draftTarget.resolved) {
@@ -63,7 +62,7 @@ export class Draft<T extends object> {
63
62
  /**
64
63
  * Resets the draft to be an exact copy of the current state of the original object.
65
64
  */
66
- @action
65
+ @mobxAction
67
66
  reset(): void {
68
67
  applySnapshot(this.data, this.originalSnapshot)
69
68
  }
@@ -75,7 +74,7 @@ export class Draft<T extends object> {
75
74
  *
76
75
  * @param path Path to reset.
77
76
  */
78
- @action
77
+ @mobxAction
79
78
  resetByPath(path: Path): void {
80
79
  const originalTarget = resolvePath(this.originalData, path)
81
80
  if (!originalTarget.resolved) {
@@ -101,7 +100,7 @@ export class Draft<T extends object> {
101
100
  /**
102
101
  * Returns `true` if the draft has changed compared to the original object, `false` otherwise.
103
102
  */
104
- @computed
103
+ @mobxComputed
105
104
  get isDirty(): boolean {
106
105
  return !deepEquals(getSnapshot(this.data), this.originalSnapshot)
107
106
  }
@@ -135,7 +134,7 @@ export class Draft<T extends object> {
135
134
  */
136
135
  readonly originalData: T
137
136
 
138
- @computed
137
+ @mobxComputed
139
138
  private get originalSnapshot() {
140
139
  return getSnapshot(this.originalData)
141
140
  }
@@ -1,20 +1,45 @@
1
1
  import { runInAction } from "mobx"
2
- import { dataObjectParent } from "../parent/core"
2
+ import { hasDataObjectParent } from "../parent/core"
3
3
  import { failure, isPrimitive } from "../utils"
4
+ import { treeNodeMetadata } from "./treeNodeMetadata"
5
+
6
+ type Untweaker = (() => void) & {
7
+ [secondaryUntweakerSymbol]?: () => void
8
+ }
9
+
10
+ const secondaryUntweakerSymbol = Symbol("secondaryUntweaker")
4
11
 
5
12
  /**
13
+ * Stores two existing cleanup functions without allocating a third function to
14
+ * close over them. The primary function is internal and never exposed.
15
+ *
6
16
  * @internal
7
17
  */
8
- export const tweakedObjects = new WeakMap<object, undefined | (() => void)>()
18
+ export function setTweakedObjectUntweakers(
19
+ value: object,
20
+ primaryUntweaker: Untweaker,
21
+ secondaryUntweaker: () => void
22
+ ): void {
23
+ primaryUntweaker[secondaryUntweakerSymbol] = secondaryUntweaker
24
+ treeNodeMetadata.get(value)!.untweaker = primaryUntweaker
25
+ }
9
26
 
10
27
  /**
11
28
  * @internal
12
29
  */
13
30
  export function isTweakedObject(value: unknown, canBeDataObject: boolean): value is object {
14
- if (!canBeDataObject && dataObjectParent.has(value as object)) {
31
+ if (!canBeDataObject && hasDataObjectParent(value as object)) {
15
32
  return false
16
33
  }
17
- return tweakedObjects.has(value as object)
34
+ return treeNodeMetadata.get(value as object)?.tweaked === true
35
+ }
36
+
37
+ /**
38
+ * @internal
39
+ */
40
+ export function runTweakedObjectUntweakers(untweaker: Untweaker): void {
41
+ untweaker()
42
+ untweaker[secondaryUntweakerSymbol]?.()
18
43
  }
19
44
 
20
45
  /**
@@ -35,7 +60,7 @@ export function assertTweakedObject(
35
60
  argName: string,
36
61
  canBeDataObject = false
37
62
  ): asserts treeNode is object {
38
- if (!canBeDataObject && dataObjectParent.has(treeNode as object)) {
63
+ if (!canBeDataObject && hasDataObjectParent(treeNode as object)) {
39
64
  throw failure(`${argName} must be the model object instance instead of the '$' sub-object`)
40
65
  }
41
66
  if (isPrimitive(treeNode) || !isTweakedObject(treeNode, true)) {
@@ -0,0 +1,38 @@
1
+ import type { ObjectChildrenData } from "../parent/coreObjectChildren"
2
+ import type { ParentPath } from "../parent/path"
3
+ import type { SnapshotData } from "../snapshot/internal"
4
+
5
+ /** @internal */
6
+ export interface TreeNodeMetadata {
7
+ tweaked: boolean
8
+ untweaker: (() => void) | undefined
9
+ parentPath: ParentPath<object> | undefined
10
+ dataObjectParent: object | undefined
11
+ objectChildren: ObjectChildrenData | undefined
12
+ snapshot: SnapshotData | undefined
13
+ }
14
+
15
+ /** @internal */
16
+ export const treeNodeMetadata = new WeakMap<object, TreeNodeMetadata>()
17
+
18
+ /** @internal */
19
+ export function getOrCreateTreeNodeMetadata(value: object): TreeNodeMetadata {
20
+ let metadata = treeNodeMetadata.get(value)
21
+ if (!metadata) {
22
+ metadata = {
23
+ tweaked: false,
24
+ untweaker: undefined,
25
+ parentPath: undefined,
26
+ dataObjectParent: undefined,
27
+ objectChildren: undefined,
28
+ snapshot: undefined,
29
+ }
30
+ treeNodeMetadata.set(value, metadata)
31
+ }
32
+ return metadata
33
+ }
34
+
35
+ /** @internal */
36
+ export function markAsTweakedObject(value: object): void {
37
+ getOrCreateTreeNodeMetadata(value).tweaked = true
38
+ }
@@ -8,8 +8,9 @@ import { unsetInternalSnapshot } from "../snapshot/internal"
8
8
  import type { AnyStandardType, TypeToData } from "../types/schemas"
9
9
  import { typeCheck } from "../types/typeCheck"
10
10
  import { failure, inDevMode, isMap, isObject, isPrimitive, isSet } from "../utils"
11
- import { isTweakedObject, tweakedObjects } from "./core"
11
+ import { isTweakedObject, runTweakedObjectUntweakers } from "./core"
12
12
  import { registerDefaultTweakers } from "./registerDefaultTweakers"
13
+ import { treeNodeMetadata } from "./treeNodeMetadata"
13
14
 
14
15
  /**
15
16
  * Turns an object (array, plain object) into a tree node,
@@ -153,7 +154,8 @@ export function tryUntweak(value: any): (() => void) | undefined {
153
154
  }
154
155
  }
155
156
 
156
- const untweaker = tweakedObjects.get(value)
157
+ const metadata = treeNodeMetadata.get(value)
158
+ const untweaker = metadata?.untweaker
157
159
  if (!untweaker) {
158
160
  return undefined
159
161
  }
@@ -176,9 +178,18 @@ export function tryUntweak(value: any): (() => void) | undefined {
176
178
 
177
179
  return () => {
178
180
  // post-untweaking, call the untweaker
179
- untweaker()
181
+ runTweakedObjectUntweakers(untweaker)
180
182
 
181
- tweakedObjects.delete(value)
183
+ // Match the former independent-map ordering: the node stops being tweaked
184
+ // before its snapshot is unset, but the shared record must stay alive until
185
+ // snapshot cleanup has consumed it.
186
+ metadata.tweaked = false
187
+ metadata.untweaker = undefined
182
188
  unsetInternalSnapshot(value)
189
+ // These associations previously lived in independent WeakMaps and remain
190
+ // valid across untweak/retweak, so keep their shared record when present.
191
+ if (metadata.dataObjectParent === undefined && metadata.objectChildren === undefined) {
192
+ treeNodeMetadata.delete(value)
193
+ }
183
194
  }
184
195
  }