mobx-keystone 2.0.0 → 2.2.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 (102) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/dist/mobx-keystone.esm.js +2561 -1214
  4. package/dist/mobx-keystone.esm.mjs +2561 -1214
  5. package/dist/mobx-keystone.umd.js +2566 -1220
  6. package/dist/types/actionMiddlewares/transactionMiddleware.d.ts +1 -1
  7. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +11 -12
  8. package/dist/types/modelShared/modelDecorator.d.ts +5 -2
  9. package/dist/types/modelShared/prop.d.ts +42 -29
  10. package/dist/types/parent/arrayPaths.d.ts +1 -0
  11. package/dist/types/parent/treeOrder.d.ts +1 -0
  12. package/dist/types/patch/applyPatches.d.ts +2 -1
  13. package/dist/types/ref/Ref.d.ts +1 -4
  14. package/dist/types/ref/modelRefIdIndex.d.ts +1 -0
  15. package/dist/types/ref/nodesById.d.ts +1 -0
  16. package/dist/types/ref/refIdIndex.d.ts +1 -0
  17. package/dist/types/snapshot/SnapshotOf.d.ts +4 -4
  18. package/dist/types/snapshot/applySnapshot.d.ts +2 -1
  19. package/dist/types/snapshot/fromSnapshot.d.ts +2 -5
  20. package/dist/types/snapshot/getSnapshot.d.ts +1 -1
  21. package/dist/types/tweaker/changeRollback.d.ts +1 -0
  22. package/dist/types/tweaker/tweakedChangeListeners.d.ts +1 -0
  23. package/dist/types/types/index.d.ts +1 -0
  24. package/dist/types/types/schemas.d.ts +41 -37
  25. package/dist/types/types/tProp.d.ts +12 -6
  26. package/dist/types/types/types.d.ts +6 -6
  27. package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
  28. package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
  29. package/dist/types/utils/ModelPool.d.ts +7 -2
  30. package/dist/types/utils/chunks.d.ts +1 -0
  31. package/dist/types/utils/types.d.ts +6 -0
  32. package/dist/types/wrappers/ArraySet.d.ts +1 -0
  33. package/package.json +5 -3
  34. package/src/action/modelFlow.ts +35 -66
  35. package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
  36. package/src/actionMiddlewares/transactionMiddleware.ts +41 -42
  37. package/src/actionMiddlewares/undoMiddleware.ts +63 -35
  38. package/src/context/context.ts +15 -36
  39. package/src/dataModel/utils.ts +5 -28
  40. package/src/deepChange/onDeepChange.ts +44 -126
  41. package/src/model/BaseModel.ts +7 -5
  42. package/src/model/utils.ts +5 -10
  43. package/src/modelShared/modelDecorator.ts +16 -30
  44. package/src/modelShared/prop.ts +115 -111
  45. package/src/modelShared/sharedInternalModel.ts +2 -2
  46. package/src/parent/arrayPaths.ts +239 -0
  47. package/src/parent/coreObjectChildren.ts +300 -48
  48. package/src/parent/onChildAttachedTo.ts +130 -44
  49. package/src/parent/path.ts +10 -12
  50. package/src/parent/setParent.ts +27 -15
  51. package/src/parent/treeOrder.ts +53 -0
  52. package/src/parent/walkTree.ts +0 -73
  53. package/src/patch/applyPatches.ts +31 -26
  54. package/src/patch/patchRecorder.ts +13 -28
  55. package/src/ref/core.ts +69 -46
  56. package/src/ref/modelRefIdIndex.ts +131 -0
  57. package/src/ref/nodesById.ts +60 -0
  58. package/src/ref/refIdIndex.ts +209 -0
  59. package/src/rootStore/rootStore.ts +1 -3
  60. package/src/snapshot/SnapshotOf.ts +24 -48
  61. package/src/snapshot/applySnapshot.ts +27 -19
  62. package/src/snapshot/fromPlainObjectSnapshot.ts +3 -8
  63. package/src/snapshot/fromSnapshot.ts +12 -9
  64. package/src/snapshot/getSnapshot.ts +1 -1
  65. package/src/snapshot/internal.ts +67 -12
  66. package/src/snapshot/reconcileModelSnapshot.ts +5 -3
  67. package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
  68. package/src/treeUtils/draft.ts +6 -1
  69. package/src/treeUtils/sandbox.ts +10 -7
  70. package/src/tweaker/changeRollback.ts +218 -0
  71. package/src/tweaker/core.ts +33 -20
  72. package/src/tweaker/treeNodeMetadata.ts +12 -2
  73. package/src/tweaker/tweak.ts +181 -18
  74. package/src/tweaker/tweakArray.ts +127 -77
  75. package/src/tweaker/tweakPlainObject.ts +65 -46
  76. package/src/tweaker/tweakedChangeListeners.ts +61 -0
  77. package/src/tweaker/typeChecking.ts +5 -104
  78. package/src/types/arrayBased/typesArray.ts +23 -30
  79. package/src/types/arrayBased/typesTuple.ts +16 -25
  80. package/src/types/createCachedTypeCheck.ts +509 -94
  81. package/src/types/index.ts +1 -0
  82. package/src/types/objectBased/typesDataModelData.ts +7 -8
  83. package/src/types/objectBased/typesObject.ts +11 -15
  84. package/src/types/objectBased/typesRecord.ts +12 -19
  85. package/src/types/schemas.ts +59 -71
  86. package/src/types/tProp.ts +29 -29
  87. package/src/types/utility/typeToStoredData.ts +22 -21
  88. package/src/types/utility/typesCodecCore.ts +5 -0
  89. package/src/types/utility/typesCodecSupport.ts +3 -3
  90. package/src/types/utility/typesMap.ts +43 -51
  91. package/src/types/utility/typesRefinement.ts +7 -8
  92. package/src/types/utility/typesSkipCheck.ts +7 -8
  93. package/src/types/utility/typesTag.ts +7 -8
  94. package/src/utils/ModelPool.ts +76 -37
  95. package/src/utils/chunks.ts +271 -0
  96. package/src/utils/decorators.ts +5 -5
  97. package/src/utils/index.ts +38 -20
  98. package/src/utils/types.ts +5 -0
  99. package/src/wrappers/ArraySet.ts +27 -14
  100. package/src/wrappers/ObjectMap.ts +1 -6
  101. package/src/wrappers/asMap.ts +1 -21
  102. package/src/wrappers/asSet.ts +0 -8
@@ -267,15 +267,6 @@ export function assertIsObject(value: unknown, argName: string): asserts value i
267
267
  }
268
268
  }
269
269
 
270
- /**
271
- * @internal
272
- */
273
- export function assertIsPlainObject(value: unknown, argName: string): asserts value is object {
274
- if (!isPlainObject(value)) {
275
- throw failure(`${argName} must be a plain object`)
276
- }
277
- }
278
-
279
270
  /**
280
271
  * @internal
281
272
  */
@@ -342,15 +333,6 @@ export function assertIsPrimitive(
342
333
  }
343
334
  }
344
335
 
345
- /**
346
- * @internal
347
- */
348
- export function assertIsString(value: unknown, argName: string): asserts value is string {
349
- if (typeof value !== "string") {
350
- throw failure(`${argName} must be a string`)
351
- }
352
- }
353
-
354
336
  /**
355
337
  * @internal
356
338
  */
@@ -372,8 +354,7 @@ export function addLateInitializationFunction(
372
354
  fn: (instance: any) => void
373
355
  ) {
374
356
  let array: LateInitializationFunctionsArray | undefined = target[symbol]
375
- // biome-ignore lint/suspicious/noPrototypeBuiltins: support old browsers
376
- if (!(array && Object.prototype.hasOwnProperty.call(target, symbol))) {
357
+ if (!(array && hasOwnProp(target, symbol))) {
377
358
  // leave base array unmodified, create new array in the derived class
378
359
  array = array ? array.slice() : []
379
360
  addHiddenProp(target, symbol, array)
@@ -558,6 +539,43 @@ function isStandardDecoratorContext(value: unknown): boolean {
558
539
  )
559
540
  }
560
541
 
542
+ /**
543
+ * Options for the observable objects of tree nodes. Their values are never
544
+ * functions nor getters, so the shared `observable.ref` annotation (MobX 7
545
+ * `observableRef`) is equivalent to `deep: false` alone, which MobX 6+ would
546
+ * allocate an annotation for on every object.
547
+ *
548
+ * @internal
549
+ */
550
+ export const nodeObservableOptions = {
551
+ deep: false,
552
+ defaultDecorator:
553
+ // (computed so bundlers do not warn about the export missing in MobX < 7)
554
+ (mobx as any)[["observable", "Ref"].join("")] ?? (mobx.observable as any).ref,
555
+ }
556
+
557
+ // the key MobX 6 stores legacy decorator annotations under (null if unknown)
558
+ let storedMobxAnnotationsKey: symbol | null | undefined
559
+
560
+ /**
561
+ * Whether an instance has legacy decorator annotations for MobX 6
562
+ * `makeObservable` to apply. When their key cannot be found it answers true,
563
+ * so `makeObservable` is still called.
564
+ *
565
+ * @internal
566
+ */
567
+ export function hasStoredMobxAnnotations(instance: object): boolean {
568
+ if (storedMobxAnnotationsKey === undefined) {
569
+ // a legacy decorator call stores its annotation under that key
570
+ const probe = {}
571
+ ;(mobx.observable as any)(probe, "probe")
572
+ storedMobxAnnotationsKey = Object.getOwnPropertySymbols(probe)[0] ?? null
573
+ }
574
+ return (
575
+ storedMobxAnnotationsKey === null || (instance as any)[storedMobxAnnotationsKey] !== undefined
576
+ )
577
+ }
578
+
561
579
  /**
562
580
  * Calls `makeObservable` across MobX versions. MobX 7 requires an annotations
563
581
  * object, while MobX 6 and older versions use decorator metadata when no
@@ -46,3 +46,8 @@ export type IsNeverType<T, IfNever, IfNotNever> = [T] extends [never] ? IfNever
46
46
  * @ignore
47
47
  */
48
48
  export type Flatten<T> = T extends Record<any, any> ? { [P in keyof T]: T[P] } : T
49
+
50
+ /**
51
+ * @ignore
52
+ */
53
+ export type SimplifyObject<T> = { [P in keyof T]: T[P] } & {}
@@ -8,6 +8,15 @@ import { typesArray } from "../types/arrayBased/typesArray"
8
8
  import { tProp } from "../types/tProp"
9
9
  import { typesUnchecked } from "../types/utility/typesUnchecked"
10
10
  import { namespace } from "../utils"
11
+ import {
12
+ isSetLikeDisjointFrom,
13
+ isSetLikeSubsetOf,
14
+ isSetLikeSupersetOf,
15
+ setLikeDifference,
16
+ setLikeIntersection,
17
+ setLikeSymmetricDifference,
18
+ setLikeUnion,
19
+ } from "../utils/setLike"
11
20
 
12
21
  const arraySetBase = Model({
13
22
  [modelIdKey]: idProp,
@@ -96,39 +105,43 @@ export class ArraySet<V> extends arraySetBase implements Set<V> {
96
105
 
97
106
  readonly [Symbol.toStringTag] = "ArraySet"
98
107
 
108
+ // `has` scans the items, so the set operations look values up in a set built
109
+ // on the first lookup instead (not before, since many of them never look up)
110
+ private toSetLike(): ReadonlySetLike<V> {
111
+ let lookup: Set<V> | undefined
112
+ return {
113
+ size: this.items.length,
114
+ has: (value) => (lookup ??= new Set(this.items)).has(value),
115
+ keys: () => this.values(),
116
+ }
117
+ }
118
+
99
119
  union<U>(other: ReadonlySetLike<U>): Set<V | U> {
100
- const s = new Set(this)
101
- return s.union(other)
120
+ return setLikeUnion(this.toSetLike(), other)
102
121
  }
103
122
 
104
123
  intersection<U>(other: ReadonlySetLike<U>): Set<V & U> {
105
- const s = new Set(this)
106
- return s.intersection(other)
124
+ return setLikeIntersection(this.toSetLike(), other)
107
125
  }
108
126
 
109
127
  difference<U>(other: ReadonlySetLike<U>): Set<V> {
110
- const s = new Set(this)
111
- return s.difference(other)
128
+ return setLikeDifference(this.toSetLike(), other)
112
129
  }
113
130
 
114
131
  symmetricDifference<U>(other: ReadonlySetLike<U>): Set<V | U> {
115
- const s = new Set(this)
116
- return s.symmetricDifference(other)
132
+ return setLikeSymmetricDifference(this.toSetLike(), other)
117
133
  }
118
134
 
119
135
  isSubsetOf(other: ReadonlySetLike<unknown>): boolean {
120
- const s = new Set(this)
121
- return s.isSubsetOf(other)
136
+ return isSetLikeSubsetOf(this.toSetLike(), other)
122
137
  }
123
138
 
124
139
  isSupersetOf(other: ReadonlySetLike<unknown>): boolean {
125
- const s = new Set(this)
126
- return s.isSupersetOf(other)
140
+ return isSetLikeSupersetOf(this.toSetLike(), other)
127
141
  }
128
142
 
129
143
  isDisjointFrom(other: ReadonlySetLike<unknown>): boolean {
130
- const s = new Set(this)
131
- return s.isDisjointFrom(other)
144
+ return isSetLikeDisjointFrom(this.toSetLike(), other)
132
145
  }
133
146
  }
134
147
 
@@ -70,12 +70,7 @@ export class ObjectMap<V> extends objectMapBase implements Map<string, V> {
70
70
  }
71
71
 
72
72
  getOrInsert(key: string, defaultValue: V): V {
73
- if (this.has(key)) {
74
- return this.get(key) as V
75
- }
76
-
77
- this.set(key, defaultValue)
78
- return this.get(key) as V
73
+ return this.getOrInsertComputed(key, () => defaultValue)
79
74
  }
80
75
 
81
76
  getOrInsertComputed(key: string, callback: (key: string) => V): V {
@@ -4,8 +4,6 @@ import {
4
4
  type IObjectDidChange,
5
5
  type IObservableArray,
6
6
  intercept,
7
- isObservableArray,
8
- isObservableObject,
9
7
  type ObservableMap,
10
8
  observable,
11
9
  observe,
@@ -21,7 +19,6 @@ import {
21
19
  failure,
22
20
  getMobxVersion,
23
21
  hasOwnProp,
24
- inDevMode,
25
22
  isArray,
26
23
  isEqualOrBothNaN,
27
24
  setProtoProp,
@@ -44,12 +41,7 @@ function addTs6MapExtras<K, V>(map: ObservableMap<K, V>): ObservableMap<K, V> &
44
41
 
45
42
  if (!mapWithExtras.getOrInsert) {
46
43
  mapWithExtras.getOrInsert = function (key: K, defaultValue: V): V {
47
- if (this.has(key)) {
48
- return this.get(key) as V
49
- }
50
-
51
- this.set(key, defaultValue)
52
- return this.get(key) as V
44
+ return this.getOrInsertComputed(key, () => defaultValue)
53
45
  }
54
46
  }
55
47
 
@@ -70,12 +62,6 @@ function addTs6MapExtras<K, V>(map: ObservableMap<K, V>): ObservableMap<K, V> &
70
62
 
71
63
  const observableMapBackedByObservableObject = action(
72
64
  <T>(obj: object): ObservableMapWithDataObject<string, T, typeof obj> => {
73
- if (inDevMode) {
74
- if (!isObservableObject(obj)) {
75
- throw failure("assertion failed: expected an observable object")
76
- }
77
- }
78
-
79
65
  const map = transaction(() =>
80
66
  untracked(() => {
81
67
  const map = observable.map<string, T>(undefined, { deep: false })
@@ -175,12 +161,6 @@ const observableMapBackedByObservableObject = action(
175
161
  const observableMapBackedByObservableArray = <K, T>(
176
162
  array: IObservableArray<[K, T]>
177
163
  ): ObservableMapWithDataObject<K, T, typeof array> => {
178
- if (inDevMode) {
179
- if (!isObservableArray(array)) {
180
- throw failure("assertion failed: expected an observable array")
181
- }
182
- }
183
-
184
164
  const map: ObservableMap<K, T> = untracked(() => {
185
165
  if (getMobxVersion() >= 6) {
186
166
  return observable.map<K, T>(array, { deep: false })
@@ -3,7 +3,6 @@ import {
3
3
  type IObservableArray,
4
4
  type ISetWillChange,
5
5
  intercept,
6
- isObservableArray,
7
6
  type ObservableSet,
8
7
  observable,
9
8
  observe,
@@ -16,7 +15,6 @@ import {
16
15
  assertIsSet,
17
16
  failure,
18
17
  getMobxVersion,
19
- inDevMode,
20
18
  isEqualOrBothNaN,
21
19
  } from "../utils"
22
20
  import { tag } from "../utils/tag"
@@ -24,12 +22,6 @@ import { tag } from "../utils/tag"
24
22
  const observableSetBackedByObservableArray = <T>(
25
23
  array: IObservableArray<T>
26
24
  ): ObservableSet<T> & { dataObject: typeof array } => {
27
- if (inDevMode) {
28
- if (!isObservableArray(array)) {
29
- throw failure("assertion failed: expected an observable array")
30
- }
31
- }
32
-
33
25
  const set = transaction(() =>
34
26
  untracked(() => {
35
27
  if (getMobxVersion() >= 6) {