mobx 6.1.8 → 6.9.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 (91) hide show
  1. package/CHANGELOG.md +224 -7
  2. package/README.md +54 -52
  3. package/dist/api/annotation.d.ts +6 -12
  4. package/dist/api/autorun.d.ts +4 -4
  5. package/dist/api/flow.d.ts +1 -0
  6. package/dist/api/intercept.d.ts +5 -5
  7. package/dist/api/object-api.d.ts +2 -0
  8. package/dist/api/observable.d.ts +5 -1
  9. package/dist/api/observe.d.ts +4 -4
  10. package/dist/api/tojs.d.ts +4 -1
  11. package/dist/api/when.d.ts +8 -0
  12. package/dist/core/atom.d.ts +1 -1
  13. package/dist/core/computedvalue.d.ts +0 -1
  14. package/dist/core/globalstate.d.ts +4 -0
  15. package/dist/core/reaction.d.ts +2 -2
  16. package/dist/errors.d.ts +4 -2
  17. package/dist/internal.d.ts +1 -0
  18. package/dist/mobx.cjs.development.js +1477 -1719
  19. package/dist/mobx.cjs.development.js.map +1 -1
  20. package/dist/mobx.cjs.production.min.js +1 -1
  21. package/dist/mobx.cjs.production.min.js.map +1 -1
  22. package/dist/mobx.d.ts +1 -1
  23. package/dist/mobx.esm.development.js +1475 -1720
  24. package/dist/mobx.esm.development.js.map +1 -1
  25. package/dist/mobx.esm.js +1492 -1726
  26. package/dist/mobx.esm.js.map +1 -1
  27. package/dist/mobx.esm.production.min.js +1 -1
  28. package/dist/mobx.esm.production.min.js.map +1 -1
  29. package/dist/mobx.umd.development.js +1477 -1719
  30. package/dist/mobx.umd.development.js.map +1 -1
  31. package/dist/mobx.umd.production.min.js +1 -1
  32. package/dist/mobx.umd.production.min.js.map +1 -1
  33. package/dist/types/actionannotation.d.ts +7 -1
  34. package/dist/types/autoannotation.d.ts +3 -0
  35. package/dist/types/observablemap.d.ts +5 -4
  36. package/dist/types/observableobject.d.ts +6 -9
  37. package/dist/types/observableset.d.ts +4 -4
  38. package/dist/types/observablevalue.d.ts +3 -5
  39. package/dist/utils/utils.d.ts +4 -4
  40. package/package.json +1 -1
  41. package/src/api/action.ts +8 -3
  42. package/src/api/annotation.ts +14 -44
  43. package/src/api/autorun.ts +38 -17
  44. package/src/api/computed.ts +5 -2
  45. package/src/api/configure.ts +6 -2
  46. package/src/api/decorators.ts +1 -1
  47. package/src/api/extendobservable.ts +12 -6
  48. package/src/api/extras.ts +4 -2
  49. package/src/api/flow.ts +17 -5
  50. package/src/api/intercept-read.ts +4 -2
  51. package/src/api/intercept.ts +10 -7
  52. package/src/api/iscomputed.ts +14 -8
  53. package/src/api/isobservable.ts +10 -4
  54. package/src/api/makeObservable.ts +35 -36
  55. package/src/api/object-api.ts +42 -14
  56. package/src/api/observable.ts +39 -25
  57. package/src/api/observe.ts +9 -6
  58. package/src/api/tojs.ts +20 -10
  59. package/src/api/trace.ts +6 -2
  60. package/src/api/when.ts +34 -9
  61. package/src/core/action.ts +11 -5
  62. package/src/core/atom.ts +9 -2
  63. package/src/core/computedvalue.ts +48 -27
  64. package/src/core/derivation.ts +34 -12
  65. package/src/core/globalstate.ts +25 -7
  66. package/src/core/observable.ts +23 -13
  67. package/src/core/reaction.ts +16 -7
  68. package/src/core/spy.ts +20 -7
  69. package/src/errors.ts +16 -3
  70. package/src/internal.ts +1 -0
  71. package/src/mobx.ts +6 -2
  72. package/src/types/actionannotation.ts +30 -52
  73. package/src/types/autoannotation.ts +107 -0
  74. package/src/types/computedannotation.ts +7 -37
  75. package/src/types/dynamicobject.ts +12 -6
  76. package/src/types/flowannotation.ts +40 -41
  77. package/src/types/intercept-utils.ts +9 -3
  78. package/src/types/legacyobservablearray.ts +26 -3
  79. package/src/types/listen-utils.ts +6 -2
  80. package/src/types/modifiers.ts +52 -16
  81. package/src/types/observableannotation.ts +7 -34
  82. package/src/types/observablearray.ts +106 -45
  83. package/src/types/observablemap.ts +71 -35
  84. package/src/types/observableobject.ts +88 -82
  85. package/src/types/observableset.ts +32 -13
  86. package/src/types/observablevalue.ts +16 -10
  87. package/src/types/overrideannotation.ts +4 -2
  88. package/src/types/type-utils.ts +34 -12
  89. package/src/utils/comparer.ts +5 -1
  90. package/src/utils/eq.ts +45 -15
  91. package/src/utils/utils.ts +39 -16
@@ -1,25 +1,31 @@
1
1
  import { $mobx, getAtom, isComputedValue, isObservableObject, die, isStringish } from "../internal"
2
2
 
3
3
  export function _isComputed(value, property?: PropertyKey): boolean {
4
- if (property !== undefined) {
5
- if (isObservableObject(value) === false) return false
6
- if (!value[$mobx].values_.has(property)) return false
7
- const atom = getAtom(value, property)
8
- return isComputedValue(atom)
4
+ if (property === undefined) {
5
+ return isComputedValue(value)
9
6
  }
10
- return isComputedValue(value)
7
+ if (isObservableObject(value) === false) {
8
+ return false
9
+ }
10
+ if (!value[$mobx].values_.has(property)) {
11
+ return false
12
+ }
13
+ const atom = getAtom(value, property)
14
+ return isComputedValue(atom)
11
15
  }
12
16
 
13
17
  export function isComputed(value: any): boolean {
14
- if (__DEV__ && arguments.length > 1)
18
+ if (__DEV__ && arguments.length > 1) {
15
19
  return die(
16
20
  `isComputed expects only 1 argument. Use isComputedProp to inspect the observability of a property`
17
21
  )
22
+ }
18
23
  return _isComputed(value)
19
24
  }
20
25
 
21
26
  export function isComputedProp(value: any, propName: PropertyKey): boolean {
22
- if (__DEV__ && !isStringish(propName))
27
+ if (__DEV__ && !isStringish(propName)) {
23
28
  return die(`isComputed expected a property name as second argument`)
29
+ }
24
30
  return _isComputed(value, propName)
25
31
  }
@@ -11,12 +11,15 @@ import {
11
11
  } from "../internal"
12
12
 
13
13
  function _isObservable(value, property?: PropertyKey): boolean {
14
- if (!value) return false
14
+ if (!value) {
15
+ return false
16
+ }
15
17
  if (property !== undefined) {
16
- if (__DEV__ && (isObservableMap(value) || isObservableArray(value)))
18
+ if (__DEV__ && (isObservableMap(value) || isObservableArray(value))) {
17
19
  return die(
18
20
  "isObservable(object, propertyName) is not supported for arrays and maps. Use map.has or array.length instead."
19
21
  )
22
+ }
20
23
  if (isObservableObject(value)) {
21
24
  return value[$mobx].values_.has(property)
22
25
  }
@@ -33,14 +36,17 @@ function _isObservable(value, property?: PropertyKey): boolean {
33
36
  }
34
37
 
35
38
  export function isObservable(value: any): boolean {
36
- if (__DEV__ && arguments.length !== 1)
39
+ if (__DEV__ && arguments.length !== 1) {
37
40
  die(
38
41
  `isObservable expects only 1 argument. Use isObservableProp to inspect the observability of a property`
39
42
  )
43
+ }
40
44
  return _isObservable(value)
41
45
  }
42
46
 
43
47
  export function isObservableProp(value: any, propName: PropertyKey): boolean {
44
- if (__DEV__ && !isStringish(propName)) return die(`expected a property name as second argument`)
48
+ if (__DEV__ && !isStringish(propName)) {
49
+ return die(`expected a property name as second argument`)
50
+ }
45
51
  return _isObservable(value, propName)
46
52
  }
@@ -11,9 +11,9 @@ import {
11
11
  isObservableObject,
12
12
  die,
13
13
  ownKeys,
14
- objectPrototype,
15
- inferredAnnotationsSymbol,
16
- extendObservable
14
+ extendObservable,
15
+ addHiddenProp,
16
+ storedAnnotationsSymbol
17
17
  } from "../internal"
18
18
 
19
19
  // Hack based on https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-322267089
@@ -31,6 +31,11 @@ export function makeObservable<T extends object, AdditionalKeys extends Property
31
31
  const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]
32
32
  startBatch()
33
33
  try {
34
+ if (__DEV__ && annotations && target[storedAnnotationsSymbol]) {
35
+ die(
36
+ `makeObservable second arg must be nullish when using decorators. Mixing @decorator syntax with annotations is not supported.`
37
+ )
38
+ }
34
39
  // Default to decorators
35
40
  annotations ??= collectStoredAnnotations(target)
36
41
 
@@ -42,56 +47,50 @@ export function makeObservable<T extends object, AdditionalKeys extends Property
42
47
  return target
43
48
  }
44
49
 
50
+ // proto[keysSymbol] = new Set<PropertyKey>()
51
+ const keysSymbol = Symbol("mobx-keys")
52
+
45
53
  export function makeAutoObservable<T extends object, AdditionalKeys extends PropertyKey = never>(
46
54
  target: T,
47
55
  overrides?: AnnotationsMap<T, NoInfer<AdditionalKeys>>,
48
56
  options?: CreateObservableOptions
49
57
  ): T {
50
58
  if (__DEV__) {
51
- if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target)))
59
+ if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target))) {
52
60
  die(`'makeAutoObservable' can only be used for classes that don't have a superclass`)
53
- if (isObservableObject(target))
61
+ }
62
+ if (isObservableObject(target)) {
54
63
  die(`makeAutoObservable can only be used on objects not already made observable`)
64
+ }
55
65
  }
56
66
 
57
- // Optimization (avoids visiting protos)
58
- // assumes that annotation.make_/.extend_ works the same for plain objects
67
+ // Optimization: avoid visiting protos
68
+ // Assumes that annotation.make_/.extend_ works the same for plain objects
59
69
  if (isPlainObject(target)) {
60
70
  return extendObservable(target, target, overrides, options)
61
71
  }
62
72
 
63
73
  const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]
74
+
75
+ // Optimization: cache keys on proto
76
+ // Assumes makeAutoObservable can be called only once per object and can't be used in subclass
77
+ if (!target[keysSymbol]) {
78
+ const proto = Object.getPrototypeOf(target)
79
+ const keys = new Set([...ownKeys(target), ...ownKeys(proto)])
80
+ keys.delete("constructor")
81
+ keys.delete($mobx)
82
+ addHiddenProp(proto, keysSymbol, keys)
83
+ }
84
+
64
85
  startBatch()
65
86
  try {
66
- // Use cached inferred annotations if available (only in classes)
67
- if (target[inferredAnnotationsSymbol]) {
68
- target[inferredAnnotationsSymbol].forEach((value, key) => adm.make_(key, value))
69
- // Overrides are not cached, unless `true`, see #2832
70
- if (overrides) {
71
- ownKeys(overrides).forEach(key => {
72
- const annotation = overrides[key]
73
- if (annotation !== true) {
74
- adm.make_(key, annotation)
75
- }
76
- })
77
- }
78
- } else {
79
- const ignoreKeys = { [$mobx]: 1, [inferredAnnotationsSymbol]: 1, constructor: 1 }
80
- const make = key => {
81
- if (ignoreKeys[key]) return
82
- ignoreKeys[key] = 1
83
- adm.make_(
84
- key,
85
- // must pass "undefined" for { key: undefined }
86
- !overrides ? true : key in overrides ? overrides[key] : true
87
- )
88
- }
89
- let current = target
90
- while (current && current !== objectPrototype) {
91
- ownKeys(current).forEach(make)
92
- current = Object.getPrototypeOf(current)
93
- }
94
- }
87
+ target[keysSymbol].forEach(key =>
88
+ adm.make_(
89
+ key,
90
+ // must pass "undefined" for { key: undefined }
91
+ !overrides ? true : key in overrides ? overrides[key] : true
92
+ )
93
+ )
95
94
  } finally {
96
95
  endBatch()
97
96
  }
@@ -20,9 +20,9 @@ export function keys<T>(set: ObservableSet<T>): ReadonlyArray<T>
20
20
  export function keys<T extends Object>(obj: T): ReadonlyArray<PropertyKey>
21
21
  export function keys(obj: any): any {
22
22
  if (isObservableObject(obj)) {
23
- return (((obj as any) as IIsObservableObject)[
24
- $mobx
25
- ] as ObservableObjectAdministration).keys_()
23
+ return (
24
+ (obj as any as IIsObservableObject)[$mobx] as ObservableObjectAdministration
25
+ ).keys_()
26
26
  }
27
27
  if (isObservableMap(obj) || isObservableSet(obj)) {
28
28
  return Array.from(obj.keys())
@@ -86,26 +86,36 @@ export function set(obj: any, key: any, value?: any): void {
86
86
  startBatch()
87
87
  const values = key
88
88
  try {
89
- for (let key in values) set(obj, key, values[key])
89
+ for (let key in values) {
90
+ set(obj, key, values[key])
91
+ }
90
92
  } finally {
91
93
  endBatch()
92
94
  }
93
95
  return
94
96
  }
95
97
  if (isObservableObject(obj)) {
96
- ;((obj as any) as IIsObservableObject)[$mobx].set_(key, value)
98
+ ;(obj as any as IIsObservableObject)[$mobx].set_(key, value)
97
99
  } else if (isObservableMap(obj)) {
98
100
  obj.set(key, value)
99
101
  } else if (isObservableSet(obj)) {
100
102
  obj.add(key)
101
103
  } else if (isObservableArray(obj)) {
102
- if (typeof key !== "number") key = parseInt(key, 10)
103
- if (key < 0) die(`Invalid index: '${key}'`)
104
+ if (typeof key !== "number") {
105
+ key = parseInt(key, 10)
106
+ }
107
+ if (key < 0) {
108
+ die(`Invalid index: '${key}'`)
109
+ }
104
110
  startBatch()
105
- if (key >= obj.length) obj.length = key + 1
111
+ if (key >= obj.length) {
112
+ obj.length = key + 1
113
+ }
106
114
  obj[key] = value
107
115
  endBatch()
108
- } else die(8)
116
+ } else {
117
+ die(8)
118
+ }
109
119
  }
110
120
 
111
121
  export function remove<K, V>(obj: ObservableMap<K, V>, key: K)
@@ -114,13 +124,15 @@ export function remove<T>(obj: IObservableArray<T>, index: number)
114
124
  export function remove<T extends Object>(obj: T, key: string)
115
125
  export function remove(obj: any, key: any): void {
116
126
  if (isObservableObject(obj)) {
117
- ;((obj as any) as IIsObservableObject)[$mobx].delete_(key)
127
+ ;(obj as any as IIsObservableObject)[$mobx].delete_(key)
118
128
  } else if (isObservableMap(obj)) {
119
129
  obj.delete(key)
120
130
  } else if (isObservableSet(obj)) {
121
131
  obj.delete(key)
122
132
  } else if (isObservableArray(obj)) {
123
- if (typeof key !== "number") key = parseInt(key, 10)
133
+ if (typeof key !== "number") {
134
+ key = parseInt(key, 10)
135
+ }
124
136
  obj.splice(key, 1)
125
137
  } else {
126
138
  die(9)
@@ -133,7 +145,7 @@ export function has<T>(obj: IObservableArray<T>, index: number): boolean
133
145
  export function has<T extends Object>(obj: T, key: string): boolean
134
146
  export function has(obj: any, key: any): boolean {
135
147
  if (isObservableObject(obj)) {
136
- return ((obj as any) as IIsObservableObject)[$mobx].has_(key)
148
+ return (obj as any as IIsObservableObject)[$mobx].has_(key)
137
149
  } else if (isObservableMap(obj)) {
138
150
  return obj.has(key)
139
151
  } else if (isObservableSet(obj)) {
@@ -148,9 +160,11 @@ export function get<K, V>(obj: ObservableMap<K, V>, key: K): V | undefined
148
160
  export function get<T>(obj: IObservableArray<T>, index: number): T | undefined
149
161
  export function get<T extends Object>(obj: T, key: string): any
150
162
  export function get(obj: any, key: any): any {
151
- if (!has(obj, key)) return undefined
163
+ if (!has(obj, key)) {
164
+ return undefined
165
+ }
152
166
  if (isObservableObject(obj)) {
153
- return ((obj as any) as IIsObservableObject)[$mobx].get_(key)
167
+ return (obj as any as IIsObservableObject)[$mobx].get_(key)
154
168
  } else if (isObservableMap(obj)) {
155
169
  return obj.get(key)
156
170
  } else if (isObservableArray(obj)) {
@@ -158,3 +172,17 @@ export function get(obj: any, key: any): any {
158
172
  }
159
173
  die(11)
160
174
  }
175
+
176
+ export function apiDefineProperty(obj: Object, key: PropertyKey, descriptor: PropertyDescriptor) {
177
+ if (isObservableObject(obj)) {
178
+ return (obj as any as IIsObservableObject)[$mobx].defineProperty_(key, descriptor)
179
+ }
180
+ die(39)
181
+ }
182
+
183
+ export function apiOwnKeys(obj: Object) {
184
+ if (isObservableObject(obj)) {
185
+ return (obj as any as IIsObservableObject)[$mobx].ownKeys_()
186
+ }
187
+ die(38)
188
+ }
@@ -28,7 +28,8 @@ import {
28
28
  globalState,
29
29
  assign,
30
30
  isStringish,
31
- createObservableAnnotation
31
+ createObservableAnnotation,
32
+ createAutoAnnotation
32
33
  } from "../internal"
33
34
 
34
35
  export const OBSERVABLE = "observable"
@@ -59,14 +60,14 @@ export function asCreateObservableOptions(thing: any): CreateObservableOptions {
59
60
  return thing || defaultCreateObservableOptions
60
61
  }
61
62
 
62
- const observableAnnotation = createObservableAnnotation("observable")
63
- const observableRefAnnotation = createObservableAnnotation("observable.ref", {
63
+ const observableAnnotation = createObservableAnnotation(OBSERVABLE)
64
+ const observableRefAnnotation = createObservableAnnotation(OBSERVABLE_REF, {
64
65
  enhancer: referenceEnhancer
65
66
  })
66
- const observableShallowAnnotation = createObservableAnnotation("observable.shallow", {
67
+ const observableShallowAnnotation = createObservableAnnotation(OBSERVABLE_SHALLOW, {
67
68
  enhancer: shallowEnhancer
68
69
  })
69
- const observableStructAnnotation = createObservableAnnotation("observable.struct", {
70
+ const observableStructAnnotation = createObservableAnnotation(OBSERVABLE_STRUCT, {
70
71
  enhancer: refStructEnhancer
71
72
  })
72
73
  const observableDecoratorAnnotation = createDecoratorAnnotation(observableAnnotation)
@@ -82,13 +83,7 @@ export function getEnhancerFromOptions(options: CreateObservableOptions): IEnhan
82
83
  export function getAnnotationFromOptions(
83
84
  options?: CreateObservableOptions
84
85
  ): Annotation | undefined {
85
- return options
86
- ? options.deep === true
87
- ? observableAnnotation
88
- : options.deep === false
89
- ? observableRefAnnotation
90
- : options.defaultDecorator
91
- : undefined
86
+ return options ? options.defaultDecorator ?? createAutoAnnotation(options) : undefined
92
87
  }
93
88
 
94
89
  export function getEnhancerFromAnnotation(annotation?: Annotation): IEnhancer<any> {
@@ -107,27 +102,44 @@ function createObservable(v: any, arg2?: any, arg3?: any) {
107
102
  }
108
103
 
109
104
  // already observable - ignore
110
- if (isObservable(v)) return v
105
+ if (isObservable(v)) {
106
+ return v
107
+ }
111
108
 
112
109
  // plain object
113
- if (isPlainObject(v)) return observable.object(v, arg2, arg3)
110
+ if (isPlainObject(v)) {
111
+ return observable.object(v, arg2, arg3)
112
+ }
114
113
 
115
114
  // Array
116
- if (Array.isArray(v)) return observable.array(v, arg2)
115
+ if (Array.isArray(v)) {
116
+ return observable.array(v, arg2)
117
+ }
117
118
 
118
119
  // Map
119
- if (isES6Map(v)) return observable.map(v, arg2)
120
+ if (isES6Map(v)) {
121
+ return observable.map(v, arg2)
122
+ }
120
123
 
121
124
  // Set
122
- if (isES6Set(v)) return observable.set(v, arg2)
125
+ if (isES6Set(v)) {
126
+ return observable.set(v, arg2)
127
+ }
123
128
 
124
129
  // other object - ignore
125
- if (typeof v === "object" && v !== null) return v
130
+ if (typeof v === "object" && v !== null) {
131
+ return v
132
+ }
126
133
 
127
134
  // anything else
128
135
  return observable.box(v, arg2)
129
136
  }
130
- Object.assign(createObservable, observableDecoratorAnnotation)
137
+ assign(createObservable, observableDecoratorAnnotation)
138
+
139
+ export interface IObservableValueFactory {
140
+ <T>(value: T, options?: CreateObservableOptions): IObservableValue<T>
141
+ <T>(value?: T, options?: CreateObservableOptions): IObservableValue<T | undefined>
142
+ }
131
143
 
132
144
  export interface IObservableFactory extends Annotation, PropertyDecorator {
133
145
  <T = any>(value: T[], options?: CreateObservableOptions): IObservableArray<T>
@@ -139,7 +151,7 @@ export interface IObservableFactory extends Annotation, PropertyDecorator {
139
151
  options?: CreateObservableOptions
140
152
  ): T
141
153
 
142
- box: <T = any>(value?: T, options?: CreateObservableOptions) => IObservableValue<T>
154
+ box: IObservableValueFactory
143
155
  array: <T = any>(initialValues?: T[], options?: CreateObservableOptions) => IObservableArray<T>
144
156
  set: <T = any>(
145
157
  initialValues?: IObservableSetInitialValues<T>,
@@ -168,15 +180,17 @@ export interface IObservableFactory extends Annotation, PropertyDecorator {
168
180
  }
169
181
 
170
182
  const observableFactories: IObservableFactory = {
171
- box<T = any>(value?: T, options?: CreateObservableOptions): IObservableValue<T> {
183
+ box<T = any>(value: T, options?: CreateObservableOptions): IObservableValue<T> {
172
184
  const o = asCreateObservableOptions(options)
173
185
  return new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
174
186
  },
175
187
  array<T = any>(initialValues?: T[], options?: CreateObservableOptions): IObservableArray<T> {
176
188
  const o = asCreateObservableOptions(options)
177
- return (globalState.useProxies === false || o.proxy === false
178
- ? createLegacyArray
179
- : createObservableArray)(initialValues, getEnhancerFromOptions(o), o.name)
189
+ return (
190
+ globalState.useProxies === false || o.proxy === false
191
+ ? createLegacyArray
192
+ : createObservableArray
193
+ )(initialValues, getEnhancerFromOptions(o), o.name)
180
194
  },
181
195
  map<K = any, V = any>(
182
196
  initialValues?: IObservableMapInitialValues<K, V>,
@@ -192,7 +206,7 @@ const observableFactories: IObservableFactory = {
192
206
  const o = asCreateObservableOptions(options)
193
207
  return new ObservableSet<T>(initialValues, getEnhancerFromOptions(o), o.name)
194
208
  },
195
- object<T = any>(
209
+ object<T extends object = any>(
196
210
  props: T,
197
211
  decorators?: AnnotationsMap<T, never>,
198
212
  options?: CreateObservableOptions
@@ -20,22 +20,23 @@ export function observe<T>(
20
20
  fireImmediately?: boolean
21
21
  ): Lambda
22
22
  export function observe<T>(
23
- observableArray: IObservableArray<T>,
23
+ observableArray: IObservableArray<T> | Array<T>,
24
24
  listener: (change: IArrayDidChange<T>) => void,
25
25
  fireImmediately?: boolean
26
26
  ): Lambda
27
27
  export function observe<V>(
28
- observableMap: ObservableSet<V>,
28
+ // ObservableSet/ObservableMap are required despite they implement Set/Map: https://github.com/mobxjs/mobx/pull/3180#discussion_r746542929
29
+ observableSet: ObservableSet<V> | Set<V>,
29
30
  listener: (change: ISetDidChange<V>) => void,
30
31
  fireImmediately?: boolean
31
32
  ): Lambda
32
33
  export function observe<K, V>(
33
- observableMap: ObservableMap<K, V>,
34
+ observableMap: ObservableMap<K, V> | Map<K, V>,
34
35
  listener: (change: IMapDidChange<K, V>) => void,
35
36
  fireImmediately?: boolean
36
37
  ): Lambda
37
38
  export function observe<K, V>(
38
- observableMap: ObservableMap<K, V>,
39
+ observableMap: ObservableMap<K, V> | Map<K, V>,
39
40
  property: K,
40
41
  listener: (change: IValueDidChange<V>) => void,
41
42
  fireImmediately?: boolean
@@ -52,9 +53,11 @@ export function observe<T, K extends keyof T>(
52
53
  fireImmediately?: boolean
53
54
  ): Lambda
54
55
  export function observe(thing, propOrCb?, cbOrFire?, fireImmediately?): Lambda {
55
- if (isFunction(cbOrFire))
56
+ if (isFunction(cbOrFire)) {
56
57
  return observeObservableProperty(thing, propOrCb, cbOrFire, fireImmediately)
57
- else return observeObservable(thing, propOrCb, cbOrFire)
58
+ } else {
59
+ return observeObservable(thing, propOrCb, cbOrFire)
60
+ }
58
61
  }
59
62
 
60
63
  function observeObservable(thing, listener, fireImmediately: boolean) {
package/src/api/tojs.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import {
2
- keys,
3
2
  isObservable,
4
3
  isObservableArray,
5
4
  isObservableValue,
6
5
  isObservableMap,
7
6
  isObservableSet,
8
- getPlainObjectKeys,
9
- die
7
+ isComputedValue,
8
+ die,
9
+ apiOwnKeys,
10
+ objectPrototype
10
11
  } from "../internal"
11
12
 
12
13
  function cache<K, V>(map: Map<any, any>, key: K, value: V): V {
@@ -20,10 +21,13 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
20
21
  typeof source !== "object" ||
21
22
  source instanceof Date ||
22
23
  !isObservable(source)
23
- )
24
+ ) {
24
25
  return source
26
+ }
25
27
 
26
- if (isObservableValue(source)) return toJSHelper(source.get(), __alreadySeen)
28
+ if (isObservableValue(source) || isComputedValue(source)) {
29
+ return toJSHelper(source.get(), __alreadySeen)
30
+ }
27
31
  if (__alreadySeen.has(source)) {
28
32
  return __alreadySeen.get(source)
29
33
  }
@@ -49,19 +53,25 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
49
53
  return res
50
54
  } else {
51
55
  // must be observable object
52
- keys(source) // make sure keys are observed
53
56
  const res = cache(__alreadySeen, source, {})
54
- getPlainObjectKeys(source).forEach((key: any) => {
55
- res[key] = toJSHelper(source[key], __alreadySeen)
57
+ apiOwnKeys(source).forEach((key: any) => {
58
+ if (objectPrototype.propertyIsEnumerable.call(source, key)) {
59
+ res[key] = toJSHelper(source[key], __alreadySeen)
60
+ }
56
61
  })
57
62
  return res
58
63
  }
59
64
  }
60
65
 
61
66
  /**
62
- * Basically, a deep clone, so that no reactive property will exist anymore.
67
+ * Recursively converts an observable to it's non-observable native counterpart.
68
+ * It does NOT recurse into non-observables, these are left as they are, even if they contain observables.
69
+ * Computed and other non-enumerable properties are completely ignored.
70
+ * Complex scenarios require custom solution, eg implementing `toJSON` or using `serializr` lib.
63
71
  */
64
72
  export function toJS<T>(source: T, options?: any): T {
65
- if (__DEV__ && options) die("toJS no longer supports options")
73
+ if (__DEV__ && options) {
74
+ die("toJS no longer supports options")
75
+ }
66
76
  return toJSHelper(source, new Map())
67
77
  }
package/src/api/trace.ts CHANGED
@@ -4,9 +4,13 @@ export function trace(thing?: any, prop?: string, enterBreakPoint?: boolean): vo
4
4
  export function trace(thing?: any, enterBreakPoint?: boolean): void
5
5
  export function trace(enterBreakPoint?: boolean): void
6
6
  export function trace(...args: any[]): void {
7
- if (!__DEV__) die(`trace() is not available in production builds`)
7
+ if (!__DEV__) {
8
+ die(`trace() is not available in production builds`)
9
+ }
8
10
  let enterBreakPoint = false
9
- if (typeof args[args.length - 1] === "boolean") enterBreakPoint = args.pop()
11
+ if (typeof args[args.length - 1] === "boolean") {
12
+ enterBreakPoint = args.pop()
13
+ }
10
14
  const derivation = getAtomFromArgs(args)
11
15
  if (!derivation) {
12
16
  return die(
package/src/api/when.ts CHANGED
@@ -9,10 +9,19 @@ import {
9
9
  allowStateChanges
10
10
  } from "../internal"
11
11
 
12
+ // https://github.com/mobxjs/mobx/issues/3582
13
+ interface GenericAbortSignal {
14
+ readonly aborted: boolean
15
+ onabort?: ((...args: any) => any) | null
16
+ addEventListener?: (...args: any) => any
17
+ removeEventListener?: (...args: any) => any
18
+ }
19
+
12
20
  export interface IWhenOptions {
13
21
  name?: string
14
22
  timeout?: number
15
23
  onError?: (error: any) => void
24
+ signal?: GenericAbortSignal
16
25
  }
17
26
 
18
27
  export function when(
@@ -25,20 +34,24 @@ export function when(
25
34
  opts?: IWhenOptions
26
35
  ): IReactionDisposer
27
36
  export function when(predicate: any, arg1?: any, arg2?: any): any {
28
- if (arguments.length === 1 || (arg1 && typeof arg1 === "object"))
37
+ if (arguments.length === 1 || (arg1 && typeof arg1 === "object")) {
29
38
  return whenPromise(predicate, arg1)
39
+ }
30
40
  return _when(predicate, arg1, arg2 || {})
31
41
  }
32
42
 
33
43
  function _when(predicate: () => boolean, effect: Lambda, opts: IWhenOptions): IReactionDisposer {
34
44
  let timeoutHandle: any
35
45
  if (typeof opts.timeout === "number") {
46
+ const error = new Error("WHEN_TIMEOUT")
36
47
  timeoutHandle = setTimeout(() => {
37
48
  if (!disposer[$mobx].isDisposed_) {
38
49
  disposer()
39
- const error = new Error("WHEN_TIMEOUT")
40
- if (opts.onError) opts.onError(error)
41
- else throw error
50
+ if (opts.onError) {
51
+ opts.onError(error)
52
+ } else {
53
+ throw error
54
+ }
42
55
  }
43
56
  }, opts.timeout)
44
57
  }
@@ -54,7 +67,9 @@ function _when(predicate: () => boolean, effect: Lambda, opts: IWhenOptions): IR
54
67
  let cond = allowStateChanges(false, predicate)
55
68
  if (cond) {
56
69
  r.dispose()
57
- if (timeoutHandle) clearTimeout(timeoutHandle)
70
+ if (timeoutHandle) {
71
+ clearTimeout(timeoutHandle)
72
+ }
58
73
  effectAction()
59
74
  }
60
75
  }, opts)
@@ -65,16 +80,26 @@ function whenPromise(
65
80
  predicate: () => boolean,
66
81
  opts?: IWhenOptions
67
82
  ): Promise<void> & { cancel(): void } {
68
- if (__DEV__ && opts && opts.onError)
83
+ if (__DEV__ && opts && opts.onError) {
69
84
  return die(`the options 'onError' and 'promise' cannot be combined`)
85
+ }
86
+ if (opts?.signal?.aborted) {
87
+ return Object.assign(Promise.reject(new Error("WHEN_ABORTED")), { cancel: () => null })
88
+ }
70
89
  let cancel
90
+ let abort
71
91
  const res = new Promise((resolve, reject) => {
72
- let disposer = _when(predicate, resolve, { ...opts, onError: reject })
92
+ let disposer = _when(predicate, resolve as Lambda, { ...opts, onError: reject })
73
93
  cancel = () => {
74
94
  disposer()
75
- reject("WHEN_CANCELLED")
95
+ reject(new Error("WHEN_CANCELLED"))
96
+ }
97
+ abort = () => {
98
+ disposer()
99
+ reject(new Error("WHEN_ABORTED"))
76
100
  }
77
- })
101
+ opts?.signal?.addEventListener?.("abort", abort)
102
+ }).finally(() => opts?.signal?.removeEventListener?.("abort", abort))
78
103
  ;(res as any).cancel = cancel
79
104
  return res as any
80
105
  }