mobx 6.1.8 → 6.3.2

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 (45) hide show
  1. package/CHANGELOG.md +57 -5
  2. package/README.md +2 -1
  3. package/dist/api/annotation.d.ts +6 -12
  4. package/dist/api/flow.d.ts +1 -0
  5. package/dist/api/object-api.d.ts +2 -0
  6. package/dist/errors.d.ts +2 -0
  7. package/dist/internal.d.ts +1 -0
  8. package/dist/mobx.cjs.development.js +322 -310
  9. package/dist/mobx.cjs.development.js.map +1 -1
  10. package/dist/mobx.cjs.production.min.js +1 -1
  11. package/dist/mobx.cjs.production.min.js.map +1 -1
  12. package/dist/mobx.d.ts +1 -1
  13. package/dist/mobx.esm.development.js +320 -311
  14. package/dist/mobx.esm.development.js.map +1 -1
  15. package/dist/mobx.esm.js +320 -311
  16. package/dist/mobx.esm.js.map +1 -1
  17. package/dist/mobx.esm.production.min.js +1 -1
  18. package/dist/mobx.esm.production.min.js.map +1 -1
  19. package/dist/mobx.umd.development.js +322 -310
  20. package/dist/mobx.umd.development.js.map +1 -1
  21. package/dist/mobx.umd.production.min.js +1 -1
  22. package/dist/mobx.umd.production.min.js.map +1 -1
  23. package/dist/types/actionannotation.d.ts +7 -1
  24. package/dist/types/autoannotation.d.ts +3 -0
  25. package/dist/types/observableobject.d.ts +4 -7
  26. package/package.json +1 -1
  27. package/src/api/annotation.ts +13 -42
  28. package/src/api/flow.ts +6 -1
  29. package/src/api/makeObservable.ts +25 -34
  30. package/src/api/object-api.ts +14 -0
  31. package/src/api/observable.ts +3 -8
  32. package/src/api/tojs.ts +10 -7
  33. package/src/core/observable.ts +7 -6
  34. package/src/errors.ts +14 -1
  35. package/src/internal.ts +1 -0
  36. package/src/mobx.ts +4 -0
  37. package/src/types/actionannotation.ts +27 -51
  38. package/src/types/autoannotation.ts +107 -0
  39. package/src/types/computedannotation.ts +7 -37
  40. package/src/types/dynamicobject.ts +1 -1
  41. package/src/types/flowannotation.ts +31 -42
  42. package/src/types/modifiers.ts +13 -2
  43. package/src/types/observableannotation.ts +7 -34
  44. package/src/types/observableobject.ts +34 -63
  45. package/src/types/overrideannotation.ts +4 -2
@@ -1,2 +1,8 @@
1
- import { Annotation } from "../internal";
1
+ import { ObservableObjectAdministration, Annotation } from "../internal";
2
2
  export declare function createActionAnnotation(name: string, options?: object): Annotation;
3
+ export declare function createActionDescriptor(adm: ObservableObjectAdministration, annotation: Annotation, key: PropertyKey, descriptor: PropertyDescriptor, safeDescriptors?: boolean): {
4
+ value: Function;
5
+ configurable: boolean;
6
+ enumerable: boolean;
7
+ writable: boolean;
8
+ };
@@ -0,0 +1,3 @@
1
+ import { Annotation } from "../internal";
2
+ export declare const autoAnnotation: Annotation;
3
+ export declare function createAutoAnnotation(options?: object): Annotation;
@@ -1,5 +1,4 @@
1
1
  import { CreateObservableOptions, Annotation, ComputedValue, IAtom, IComputedValueOptions, IEnhancer, IInterceptable, IListenable, Lambda, ObservableValue } from "../internal";
2
- export declare const inferredAnnotationsSymbol: unique symbol;
3
2
  export declare type IObjectDidChange<T = any> = {
4
3
  observableKind: "object";
5
4
  name: PropertyKey;
@@ -31,7 +30,6 @@ export declare class ObservableObjectAdministration implements IInterceptable<IO
31
30
  values_: Map<string | number | symbol, ObservableValue<any> | ComputedValue<any>>;
32
31
  name_: string;
33
32
  defaultAnnotation_: Annotation;
34
- autoBind_: boolean;
35
33
  keysAtom_: IAtom;
36
34
  changeListeners_: any;
37
35
  interceptors_: any;
@@ -39,14 +37,14 @@ export declare class ObservableObjectAdministration implements IInterceptable<IO
39
37
  isPlainObject_: boolean;
40
38
  appliedAnnotations_?: object;
41
39
  private pendingKeys_;
42
- constructor(target_: any, values_: Map<string | number | symbol, ObservableValue<any> | ComputedValue<any>>, name_: string, defaultAnnotation_?: Annotation, autoBind_?: boolean);
40
+ constructor(target_: any, values_: Map<string | number | symbol, ObservableValue<any> | ComputedValue<any>>, name_: string, defaultAnnotation_?: Annotation);
43
41
  getObservablePropValue_(key: PropertyKey): any;
44
42
  setObservablePropValue_(key: PropertyKey, newValue: any): boolean | null;
45
43
  get_(key: PropertyKey): any;
46
44
  /**
47
45
  * @param {PropertyKey} key
48
46
  * @param {any} value
49
- * @param {Annotation|boolean} annotation true - infer from descriptor, false - copy as is
47
+ * @param {Annotation|boolean} annotation true - use default annotation, false - copy as is
50
48
  * @param {boolean} proxyTrap whether it's called from proxy trap
51
49
  * @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
52
50
  */
@@ -54,18 +52,17 @@ export declare class ObservableObjectAdministration implements IInterceptable<IO
54
52
  has_(key: PropertyKey): boolean;
55
53
  /**
56
54
  * @param {PropertyKey} key
57
- * @param {Annotation|boolean} annotation true - infer from object or it's prototype, false - ignore
55
+ * @param {Annotation|boolean} annotation true - use default annotation, false - ignore prop
58
56
  */
59
57
  make_(key: PropertyKey, annotation: Annotation | boolean): void;
60
58
  /**
61
59
  * @param {PropertyKey} key
62
60
  * @param {PropertyDescriptor} descriptor
63
- * @param {Annotation|boolean} annotation true - infer from descriptor, false - copy as is
61
+ * @param {Annotation|boolean} annotation true - use default annotation, false - copy as is
64
62
  * @param {boolean} proxyTrap whether it's called from proxy trap
65
63
  * @returns {boolean|null} true on success, false on failure (proxyTrap + non-configurable), null when cancelled by interceptor
66
64
  */
67
65
  extend_(key: PropertyKey, descriptor: PropertyDescriptor, annotation: Annotation | boolean, proxyTrap?: boolean): boolean | null;
68
- inferAnnotation_(key: PropertyKey): Annotation | false;
69
66
  /**
70
67
  * @param {PropertyKey} key
71
68
  * @param {PropertyDescriptor} descriptor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx",
3
- "version": "6.1.8",
3
+ "version": "6.3.2",
4
4
  "description": "Simple, scalable state management.",
5
5
  "source": "src/mobx.ts",
6
6
  "main": "dist/index.js",
@@ -1,17 +1,19 @@
1
- import {
2
- ObservableObjectAdministration,
3
- isGenerator,
4
- isFunction,
5
- autoAction,
6
- isAction,
7
- flow,
8
- computed,
9
- isFlow
10
- } from "../internal"
1
+ import { ObservableObjectAdministration, isFunction } from "../internal"
2
+
3
+ export const enum MakeResult {
4
+ Cancel,
5
+ Break,
6
+ Continue
7
+ }
11
8
 
12
9
  export type Annotation = {
13
10
  annotationType_: string
14
- make_(adm: ObservableObjectAdministration, key: PropertyKey): void
11
+ make_(
12
+ adm: ObservableObjectAdministration,
13
+ key: PropertyKey,
14
+ descriptor: PropertyDescriptor,
15
+ source: object
16
+ ): MakeResult
15
17
  extend_(
16
18
  adm: ObservableObjectAdministration,
17
19
  key: PropertyKey,
@@ -33,37 +35,6 @@ export type AnnotationsMap<T, AdditionalFields extends PropertyKey> = {
33
35
  } &
34
36
  Record<AdditionalFields, AnnotationMapEntry>
35
37
 
36
- /**
37
- * Infers the best fitting annotation from property descriptor or false if the field shoudn't be annotated
38
- * - getter(+setter) -> computed
39
- * - setter w/o getter -> false (ignore)
40
- * - flow -> false (ignore)
41
- * - generator -> flow
42
- * - action -> false (ignore)
43
- * - function -> action (optionally bound)
44
- * - other -> defaultAnnotation
45
- */
46
- export function inferAnnotationFromDescriptor(
47
- desc: PropertyDescriptor,
48
- defaultAnnotation: Annotation,
49
- autoBind: boolean
50
- ): Annotation | false {
51
- if (desc.get) return computed
52
- if (desc.set) return false // ignore lone setter
53
- // If already wrapped in action/flow, don't do that another time, but assume it is already set up properly.
54
- return isFunction(desc.value)
55
- ? isGenerator(desc.value)
56
- ? isFlow(desc.value)
57
- ? false
58
- : flow
59
- : isAction(desc.value)
60
- ? false
61
- : autoBind
62
- ? autoAction.bound
63
- : autoAction
64
- : defaultAnnotation
65
- }
66
-
67
38
  export function isAnnotation(thing: any) {
68
39
  return (
69
40
  // Can be function
package/src/api/flow.ts CHANGED
@@ -6,7 +6,8 @@ import {
6
6
  Annotation,
7
7
  isStringish,
8
8
  storeAnnotation,
9
- createFlowAnnotation
9
+ createFlowAnnotation,
10
+ createDecoratorAnnotation
10
11
  } from "../internal"
11
12
 
12
13
  export const FLOW = "flow"
@@ -28,9 +29,11 @@ interface Flow extends Annotation, PropertyDecorator {
28
29
  <R, Args extends any[]>(
29
30
  generator: (...args: Args) => Generator<any, R, any> | AsyncGenerator<any, R, any>
30
31
  ): (...args: Args) => CancellablePromise<R>
32
+ bound: Annotation & PropertyDecorator
31
33
  }
32
34
 
33
35
  const flowAnnotation = createFlowAnnotation("flow")
36
+ const flowBoundAnnotation = createFlowAnnotation("flow.bound", { bound: true })
34
37
 
35
38
  export const flow: Flow = Object.assign(
36
39
  function flow(arg1, arg2?) {
@@ -123,6 +126,8 @@ export const flow: Flow = Object.assign(
123
126
  flowAnnotation
124
127
  )
125
128
 
129
+ flow.bound = createDecoratorAnnotation(flowBoundAnnotation)
130
+
126
131
  function cancelPromise(promise) {
127
132
  if (isFunction(promise.cancel)) promise.cancel()
128
133
  }
@@ -11,9 +11,8 @@ import {
11
11
  isObservableObject,
12
12
  die,
13
13
  ownKeys,
14
- objectPrototype,
15
- inferredAnnotationsSymbol,
16
- extendObservable
14
+ extendObservable,
15
+ addHiddenProp
17
16
  } from "../internal"
18
17
 
19
18
  // Hack based on https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-322267089
@@ -42,6 +41,9 @@ export function makeObservable<T extends object, AdditionalKeys extends Property
42
41
  return target
43
42
  }
44
43
 
44
+ // proto[keysSymbol] = new Set<PropertyKey>()
45
+ const keysSymbol = Symbol("mobx-keys")
46
+
45
47
  export function makeAutoObservable<T extends object, AdditionalKeys extends PropertyKey = never>(
46
48
  target: T,
47
49
  overrides?: AnnotationsMap<T, NoInfer<AdditionalKeys>>,
@@ -54,44 +56,33 @@ export function makeAutoObservable<T extends object, AdditionalKeys extends Prop
54
56
  die(`makeAutoObservable can only be used on objects not already made observable`)
55
57
  }
56
58
 
57
- // Optimization (avoids visiting protos)
58
- // assumes that annotation.make_/.extend_ works the same for plain objects
59
+ // Optimization: avoid visiting protos
60
+ // Assumes that annotation.make_/.extend_ works the same for plain objects
59
61
  if (isPlainObject(target)) {
60
62
  return extendObservable(target, target, overrides, options)
61
63
  }
62
64
 
63
65
  const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]
66
+
67
+ // Optimization: cache keys on proto
68
+ // Assumes makeAutoObservable can be called only once per object and can't be used in subclass
69
+ if (!target[keysSymbol]) {
70
+ const proto = Object.getPrototypeOf(target)
71
+ const keys = new Set([...ownKeys(target), ...ownKeys(proto)])
72
+ keys.delete("constructor")
73
+ keys.delete($mobx)
74
+ addHiddenProp(proto, keysSymbol, keys)
75
+ }
76
+
64
77
  startBatch()
65
78
  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
- }
79
+ target[keysSymbol].forEach(key =>
80
+ adm.make_(
81
+ key,
82
+ // must pass "undefined" for { key: undefined }
83
+ !overrides ? true : key in overrides ? overrides[key] : true
84
+ )
85
+ )
95
86
  } finally {
96
87
  endBatch()
97
88
  }
@@ -158,3 +158,17 @@ export function get(obj: any, key: any): any {
158
158
  }
159
159
  die(11)
160
160
  }
161
+
162
+ export function apiDefineProperty(obj: Object, key: PropertyKey, descriptor: PropertyDescriptor) {
163
+ if (isObservableObject(obj)) {
164
+ return ((obj as any) as IIsObservableObject)[$mobx].defineProperty_(key, descriptor)
165
+ }
166
+ die(39)
167
+ }
168
+
169
+ export function apiOwnKeys(obj: Object) {
170
+ if (isObservableObject(obj)) {
171
+ return ((obj as any) as IIsObservableObject)[$mobx].ownKeys_()
172
+ }
173
+ die(38)
174
+ }
@@ -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"
@@ -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> {
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 {
@@ -23,7 +24,8 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
23
24
  )
24
25
  return source
25
26
 
26
- if (isObservableValue(source)) return toJSHelper(source.get(), __alreadySeen)
27
+ if (isObservableValue(source) || isComputedValue(source))
28
+ return toJSHelper(source.get(), __alreadySeen)
27
29
  if (__alreadySeen.has(source)) {
28
30
  return __alreadySeen.get(source)
29
31
  }
@@ -49,10 +51,11 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
49
51
  return res
50
52
  } else {
51
53
  // must be observable object
52
- keys(source) // make sure keys are observed
53
54
  const res = cache(__alreadySeen, source, {})
54
- getPlainObjectKeys(source).forEach((key: any) => {
55
- res[key] = toJSHelper(source[key], __alreadySeen)
55
+ apiOwnKeys(source).forEach((key: any) => {
56
+ if (objectPrototype.propertyIsEnumerable.call(source, key)) {
57
+ res[key] = toJSHelper(source[key], __alreadySeen)
58
+ }
56
59
  })
57
60
  return res
58
61
  }
@@ -206,12 +206,16 @@ export function propagateChangeConfirmed(observable: IObservable) {
206
206
  observable.lowestObserverState_ = IDerivationState_.STALE_
207
207
 
208
208
  observable.observers_.forEach(d => {
209
- if (d.dependenciesState_ === IDerivationState_.POSSIBLY_STALE_)
209
+ if (d.dependenciesState_ === IDerivationState_.POSSIBLY_STALE_) {
210
210
  d.dependenciesState_ = IDerivationState_.STALE_
211
- else if (
211
+ if (__DEV__ && d.isTracing_ !== TraceMode.NONE) {
212
+ logTraceInfo(d, observable)
213
+ }
214
+ } else if (
212
215
  d.dependenciesState_ === IDerivationState_.UP_TO_DATE_ // this happens during computing of `d`, just keep lowestObserverState up to date.
213
- )
216
+ ) {
214
217
  observable.lowestObserverState_ = IDerivationState_.UP_TO_DATE_
218
+ }
215
219
  })
216
220
  // invariantLOS(observable, "confirmed end");
217
221
  }
@@ -225,9 +229,6 @@ export function propagateMaybeChanged(observable: IObservable) {
225
229
  observable.observers_.forEach(d => {
226
230
  if (d.dependenciesState_ === IDerivationState_.UP_TO_DATE_) {
227
231
  d.dependenciesState_ = IDerivationState_.POSSIBLY_STALE_
228
- if (__DEV__ && d.isTracing_ !== TraceMode.NONE) {
229
- logTraceInfo(d, observable)
230
- }
231
232
  d.onBecomeStale_()
232
233
  }
233
234
  })
package/src/errors.ts CHANGED
@@ -3,6 +3,17 @@ const niceErrors = {
3
3
  1(annotationType, key: PropertyKey) {
4
4
  return `Cannot apply '${annotationType}' to '${key.toString()}': Field not found.`
5
5
  },
6
+ /*
7
+ 2(prop) {
8
+ return `invalid decorator for '${prop.toString()}'`
9
+ },
10
+ 3(prop) {
11
+ return `Cannot decorate '${prop.toString()}': action can only be used on properties with a function value.`
12
+ },
13
+ 4(prop) {
14
+ return `Cannot decorate '${prop.toString()}': computed can only be used on getter properties.`
15
+ },
16
+ */
6
17
  5: "'keys()' can only be used on observable objects, arrays, sets and maps",
7
18
  6: "'values()' can only be used on observable objects, arrays, sets and maps",
8
19
  7: "'entries()' can only be used on observable objects, arrays and maps",
@@ -59,7 +70,9 @@ const niceErrors = {
59
70
  36: "isolateGlobalState should be called before MobX is running any reactions",
60
71
  37(method) {
61
72
  return `[mobx] \`observableArray.${method}()\` mutates the array in-place, which is not allowed inside a derivation. Use \`array.slice().${method}()\` instead`
62
- }
73
+ },
74
+ 38: "'ownKeys()' can only be used on observable objects",
75
+ 39: "'defineProperty()' can only be used on observable objects"
63
76
  } as const
64
77
 
65
78
  const errors: typeof niceErrors = __DEV__ ? niceErrors : ({} as any)
package/src/internal.ts CHANGED
@@ -17,6 +17,7 @@ export * from "./types/actionannotation"
17
17
  export * from "./types/flowannotation"
18
18
  export * from "./types/computedannotation"
19
19
  export * from "./types/observableannotation"
20
+ export * from "./types/autoannotation"
20
21
  export * from "./api/observable"
21
22
  export * from "./api/computed"
22
23
  export * from "./core/action"
package/src/mobx.ts CHANGED
@@ -75,6 +75,7 @@ export {
75
75
  IObservableFactory,
76
76
  CreateObservableOptions,
77
77
  computed,
78
+ IComputedFactory,
78
79
  isObservable,
79
80
  isObservableProp,
80
81
  isComputed,
@@ -99,10 +100,13 @@ export {
99
100
  remove,
100
101
  has,
101
102
  get,
103
+ apiOwnKeys as ownKeys,
104
+ apiDefineProperty as defineProperty,
102
105
  configure,
103
106
  onBecomeObserved,
104
107
  onBecomeUnobserved,
105
108
  flow,
109
+ isFlow,
106
110
  flowResult,
107
111
  FlowCancellationError,
108
112
  isFlowCancellationError,
@@ -3,14 +3,11 @@ import {
3
3
  createAction,
4
4
  isAction,
5
5
  defineProperty,
6
- getDescriptor,
7
- objectPrototype,
8
6
  die,
9
7
  isFunction,
10
8
  Annotation,
11
- recordAnnotationApplied,
12
9
  globalState,
13
- storedAnnotationsSymbol
10
+ MakeResult
14
11
  } from "../internal"
15
12
 
16
13
  export function createActionAnnotation(name: string, options?: object): Annotation {
@@ -22,54 +19,33 @@ export function createActionAnnotation(name: string, options?: object): Annotati
22
19
  }
23
20
  }
24
21
 
25
- function make_(adm: ObservableObjectAdministration, key: PropertyKey): void {
26
- let annotated = false
27
- let source = adm.target_
28
- let bound = this.options_?.bound ?? false
29
- while (source && source !== objectPrototype) {
30
- const descriptor = getDescriptor(source, key)
31
- if (descriptor) {
32
- // Instance or bound
33
- // Keep first because the operation can be intercepted
34
- // and we don't want to end up with partially annotated proto chain
35
- if (source === adm.target_ || bound) {
36
- const actionDescriptor = createActionDescriptor(adm, this, key, descriptor)
37
- const definePropertyOutcome = adm.defineProperty_(key, actionDescriptor)
38
- if (!definePropertyOutcome) {
39
- // Intercepted
40
- return
41
- }
42
- annotated = true
43
- // Don't annotate protos if bound
44
- if (bound) {
45
- break
46
- }
47
- }
48
- // Prototype
49
- if (source !== adm.target_) {
50
- if (isAction(descriptor.value)) {
51
- // A prototype could have been annotated already by other constructor,
52
- // rest of the proto chain must be annotated already
53
- annotated = true
54
- break
55
- }
56
- const actionDescriptor = createActionDescriptor(adm, this, key, descriptor, false)
57
- defineProperty(source, key, actionDescriptor)
58
- annotated = true
59
- }
60
- }
61
- source = Object.getPrototypeOf(source)
22
+ function make_(
23
+ adm: ObservableObjectAdministration,
24
+ key: PropertyKey,
25
+ descriptor: PropertyDescriptor,
26
+ source: object
27
+ ): MakeResult {
28
+ // bound
29
+ if (this.options_?.bound) {
30
+ return this.extend_(adm, key, descriptor, false) === null
31
+ ? MakeResult.Cancel
32
+ : MakeResult.Break
33
+ }
34
+ // own
35
+ if (source === adm.target_) {
36
+ return this.extend_(adm, key, descriptor, false) === null
37
+ ? MakeResult.Cancel
38
+ : MakeResult.Continue
62
39
  }
63
- if (annotated) {
64
- recordAnnotationApplied(adm, this, key)
65
- } else if (!adm.target_[storedAnnotationsSymbol]?.[key]) {
66
- // Throw on missing key, except for decorators:
67
- // Decorator annotations are collected from whole prototype chain.
68
- // When called from super() some props may not exist yet.
69
- // However we don't have to worry about missing prop,
70
- // because the decorator must have been applied to something.
71
- die(1, this.annotationType_, `${adm.name_}.${key.toString()}`)
40
+ // prototype
41
+ if (isAction(descriptor.value)) {
42
+ // A prototype could have been annotated already by other constructor,
43
+ // rest of the proto chain must be annotated already
44
+ return MakeResult.Break
72
45
  }
46
+ const actionDescriptor = createActionDescriptor(adm, this, key, descriptor, false)
47
+ defineProperty(source, key, actionDescriptor)
48
+ return MakeResult.Continue
73
49
  }
74
50
 
75
51
  function extend_(
@@ -96,7 +72,7 @@ function assertActionDescriptor(
96
72
  }
97
73
  }
98
74
 
99
- function createActionDescriptor(
75
+ export function createActionDescriptor(
100
76
  adm: ObservableObjectAdministration,
101
77
  annotation: Annotation,
102
78
  key: PropertyKey,
@@ -0,0 +1,107 @@
1
+ import {
2
+ ObservableObjectAdministration,
3
+ observable,
4
+ Annotation,
5
+ defineProperty,
6
+ createAction,
7
+ globalState,
8
+ flow,
9
+ computed,
10
+ autoAction,
11
+ isGenerator,
12
+ MakeResult
13
+ } from "../internal"
14
+
15
+ const AUTO = "true"
16
+
17
+ export const autoAnnotation: Annotation = createAutoAnnotation()
18
+
19
+ export function createAutoAnnotation(options?: object): Annotation {
20
+ return {
21
+ annotationType_: AUTO,
22
+ options_: options,
23
+ make_,
24
+ extend_
25
+ }
26
+ }
27
+
28
+ function make_(
29
+ adm: ObservableObjectAdministration,
30
+ key: PropertyKey,
31
+ descriptor: PropertyDescriptor,
32
+ source: object
33
+ ): MakeResult {
34
+ // getter -> computed
35
+ if (descriptor.get) {
36
+ return computed.make_(adm, key, descriptor, source)
37
+ }
38
+ // lone setter -> action setter
39
+ if (descriptor.set) {
40
+ // TODO make action applicable to setter and delegate to action.make_
41
+ const set = createAction(key.toString(), descriptor.set) as (v: any) => void
42
+ // own
43
+ if (source === adm.target_) {
44
+ return adm.defineProperty_(key, {
45
+ configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
46
+ set
47
+ }) === null
48
+ ? MakeResult.Cancel
49
+ : MakeResult.Continue
50
+ }
51
+ // proto
52
+ defineProperty(source, key, {
53
+ configurable: true,
54
+ set
55
+ })
56
+ return MakeResult.Continue
57
+ }
58
+ // function on proto -> autoAction/flow
59
+ if (source !== adm.target_ && typeof descriptor.value === "function") {
60
+ if (isGenerator(descriptor.value)) {
61
+ const flowAnnotation = this.options_?.autoBind ? flow.bound : flow
62
+ return flowAnnotation.make_(adm, key, descriptor, source)
63
+ }
64
+ const actionAnnotation = this.options_?.autoBind ? autoAction.bound : autoAction
65
+ return actionAnnotation.make_(adm, key, descriptor, source)
66
+ }
67
+ // other -> observable
68
+ // Copy props from proto as well, see test:
69
+ // "decorate should work with Object.create"
70
+ let observableAnnotation = this.options_?.deep === false ? observable.ref : observable
71
+ // if function respect autoBind option
72
+ if (typeof descriptor.value === "function" && this.options_?.autoBind) {
73
+ descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_)
74
+ }
75
+ return observableAnnotation.make_(adm, key, descriptor, source)
76
+ }
77
+
78
+ function extend_(
79
+ adm: ObservableObjectAdministration,
80
+ key: PropertyKey,
81
+ descriptor: PropertyDescriptor,
82
+ proxyTrap: boolean
83
+ ): boolean | null {
84
+ // getter -> computed
85
+ if (descriptor.get) {
86
+ return computed.extend_(adm, key, descriptor, proxyTrap)
87
+ }
88
+ // lone setter -> action setter
89
+ if (descriptor.set) {
90
+ // TODO make action applicable to setter and delegate to action.extend_
91
+ return adm.defineProperty_(
92
+ key,
93
+ {
94
+ configurable: globalState.safeDescriptors ? adm.isPlainObject_ : true,
95
+ set: createAction(key.toString(), descriptor.set) as (v: any) => void
96
+ },
97
+ proxyTrap
98
+ )
99
+ }
100
+ // other -> observable
101
+ // if function respect autoBind option
102
+ if (typeof descriptor.value === "function" && this.options_?.autoBind) {
103
+ descriptor.value = descriptor.value.bind(adm.proxy_ ?? adm.target_)
104
+ }
105
+ let observableAnnotation = this.options_?.deep === false ? observable.ref : observable
106
+ return observableAnnotation.extend_(adm, key, descriptor, proxyTrap)
107
+ }