mobx-keystone 1.23.0 → 1.24.1

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.
@@ -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
  }
@@ -48,7 +48,8 @@ export function tweakPlainObject<T extends Record<string, any>>(
48
48
  isDataObject: boolean
49
49
  ): T {
50
50
  const originalObj: Record<string, any> = value
51
- const tweakedObj = isObservableObject(originalObj)
51
+ const originalIsObservableObject = isObservableObject(originalObj)
52
+ const tweakedObj = originalIsObservableObject
52
53
  ? originalObj
53
54
  : observable.object({}, undefined, observableOptions)
54
55
 
@@ -78,7 +79,7 @@ export function tweakPlainObject<T extends Record<string, any>>(
78
79
  const v = originalObj[k]
79
80
 
80
81
  if (isPrimitive(v)) {
81
- if (!doNotTweakChildren) {
82
+ if (!doNotTweakChildren && !originalIsObservableObject) {
82
83
  setIfDifferent(tweakedObj, k, v)
83
84
  }
84
85
  if (k === "__proto__") {
@@ -6,7 +6,7 @@ import type { TypeCheckError } from "./TypeCheckError"
6
6
 
7
7
  type CheckFunction = (value: any, path: Path, typeCheckedValue: any) => TypeCheckError | null
8
8
 
9
- /** @internal */
9
+ /** @ignore */
10
10
  export type SnapshotProcessor = (snapshot: any) => unknown
11
11
 
12
12
  /**
@@ -14,7 +14,7 @@ export type SnapshotProcessor = (snapshot: any) => unknown
14
14
  * TypeCheckers rather than processors so recursive graphs can be discovered
15
15
  * before any processor body is compiled.
16
16
  *
17
- * @internal
17
+ * @ignore
18
18
  */
19
19
  export interface SnapshotProcessorPlan {
20
20
  readonly intrinsic?: boolean
@@ -36,7 +36,7 @@ export function snapshotProcessorPlan(
36
36
  type SnapshotProcessorSource = SnapshotProcessor | SnapshotProcessorPlan | undefined
37
37
  type SnapshotProcessorDirection = "from" | "to"
38
38
 
39
- /** @internal */
39
+ /** @ignore */
40
40
  export class SnapshotProcessorPlanNode {
41
41
  private readonly owner: TypeChecker | undefined
42
42
  private readonly direction: SnapshotProcessorDirection
@@ -196,7 +196,7 @@ export class SnapshotProcessorPlanNode {
196
196
  const absentSnapshotProcessorPlanNode = new SnapshotProcessorPlanNode(undefined, "from", undefined)
197
197
 
198
198
  /**
199
- * @internal
199
+ * @ignore
200
200
  */
201
201
  export enum TypeCheckerBaseType {
202
202
  Object = "object",
@@ -243,7 +243,7 @@ export function invalidateCachedToSnapshotProcessorResult(obj: object) {
243
243
  }
244
244
 
245
245
  /**
246
- * @internal
246
+ * @ignore
247
247
  */
248
248
  export class TypeChecker {
249
249
  unchecked: boolean
@@ -443,6 +443,6 @@ export class TypeInfo {
443
443
  }
444
444
 
445
445
  /**
446
- * @internal
446
+ * @ignore
447
447
  */
448
448
  export type TypeInfoGen = (t: AnyStandardType) => TypeInfo
@@ -64,7 +64,7 @@ export interface ObjectOfTypes {
64
64
 
65
65
  /**
66
66
  * Name of the properties of an object that can be set to undefined, any or unknown.
67
- * @internal
67
+ * @ignore
68
68
  */
69
69
  export type UndefinablePropsNames<T> = {
70
70
  [K in keyof T]: IsOptionalValue<T[K], K, never>
@@ -72,7 +72,7 @@ export type UndefinablePropsNames<T> = {
72
72
 
73
73
  /**
74
74
  * Computes the optional keys for an object type schema.
75
- * @internal
75
+ * @ignore
76
76
  */
77
77
  export type ObjectOptionalKeys<S> = UndefinablePropsNames<{
78
78
  [k in keyof S]: TypeToDataOpt<S[k]> extends infer R ? R : never
@@ -1,5 +1,6 @@
1
1
  import * as mobx from "mobx"
2
2
  import {
3
+ type IComputedValue,
3
4
  type IObservableArray,
4
5
  isObservableArray,
5
6
  isObservableMap,
@@ -419,6 +420,127 @@ export const mobx6 = {
419
420
  ],
420
421
  }
421
422
 
423
+ /**
424
+ * MobX method action decorator compatible with both legacy decorators and
425
+ * MobX 7's standard decorator API.
426
+ *
427
+ * This is a decorator compatibility helper, not a replacement for other
428
+ * `action` overloads such as `action(fn)`. Field decorators are not supported.
429
+ *
430
+ * @hidden
431
+ */
432
+ export function mobxAction<This, Args extends unknown[], Return>(
433
+ value: (this: This, ...args: Args) => Return,
434
+ context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
435
+ ): (this: This, ...args: Args) => Return
436
+ export function mobxAction<This, Args extends unknown[], Return>(
437
+ target: object,
438
+ propertyKey: string | symbol,
439
+ descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Return>
440
+ ): TypedPropertyDescriptor<(this: This, ...args: Args) => Return> | void
441
+ export function mobxAction(...args: any[]): any {
442
+ if (isStandardDecoratorContext(args[1])) {
443
+ return (mobx.action as any)(args[0], args[1])
444
+ }
445
+
446
+ if (getMobxVersion() < 7) {
447
+ return (mobx.action as any)(...args)
448
+ }
449
+
450
+ const descriptor = args[2] as PropertyDescriptor | undefined
451
+ if (!descriptor?.value) {
452
+ return descriptor
453
+ }
454
+
455
+ if (mobx.isAction(descriptor.value)) {
456
+ return descriptor
457
+ }
458
+
459
+ return {
460
+ ...descriptor,
461
+ value: mobx.action(String(args[1]), descriptor.value),
462
+ }
463
+ }
464
+
465
+ /**
466
+ * MobX getter computed decorator compatible with both legacy decorators and
467
+ * MobX 7's standard decorator API.
468
+ *
469
+ * This is a decorator compatibility helper, not a replacement for other
470
+ * `computed` overloads. Field decorators and decorator options are not supported.
471
+ * Under MobX 7's legacy decorator transform the getter is backed by a computed
472
+ * value, but is not registered as a computed property for MobX introspection.
473
+ *
474
+ * @hidden
475
+ */
476
+ export function mobxComputed<This, Value>(
477
+ value: (this: This) => Value,
478
+ context: ClassGetterDecoratorContext<This, Value>
479
+ ): ((this: This) => Value) | void
480
+ export function mobxComputed<Value>(
481
+ target: object,
482
+ propertyKey: string | symbol,
483
+ descriptor: TypedPropertyDescriptor<Value>
484
+ ): TypedPropertyDescriptor<Value> | void
485
+ export function mobxComputed(...args: any[]): any {
486
+ if (isStandardDecoratorContext(args[1])) {
487
+ return (mobx.computed as any)(args[0], args[1])
488
+ }
489
+
490
+ if (getMobxVersion() < 7) {
491
+ return (mobx.computed as any)(...args)
492
+ }
493
+
494
+ const descriptor = args[2] as PropertyDescriptor | undefined
495
+ if (!descriptor?.get) {
496
+ return descriptor
497
+ }
498
+
499
+ const getter = descriptor.get
500
+ const computedValues = new WeakMap<object, IComputedValue<unknown>>()
501
+
502
+ return {
503
+ ...descriptor,
504
+ get(this: object) {
505
+ let computedValue = computedValues.get(this)
506
+ if (!computedValue) {
507
+ const newComputedValue = mobx.computed(() => getter.call(this), {
508
+ name: String(args[1]),
509
+ })
510
+ computedValues.set(this, newComputedValue)
511
+ computedValue = newComputedValue
512
+ }
513
+ return computedValue.get()
514
+ },
515
+ }
516
+ }
517
+
518
+ // Mirrors MobX's standard-decorator call detection. Legacy decorator calls use
519
+ // a string or symbol property key as their second argument, so checking `kind`
520
+ // distinguishes the call shapes without claiming to validate the full context.
521
+ function isStandardDecoratorContext(value: unknown): boolean {
522
+ return (
523
+ typeof value === "object" &&
524
+ value !== null &&
525
+ typeof (value as { kind?: unknown }).kind === "string"
526
+ )
527
+ }
528
+
529
+ /**
530
+ * Calls `makeObservable` across MobX versions. MobX 7 requires an annotations
531
+ * object, while MobX 6 and older versions use decorator metadata when no
532
+ * annotations are passed.
533
+ *
534
+ * @internal
535
+ */
536
+ export function makeObservableCompat(target: object, annotations?: object): void {
537
+ if (getMobxVersion() >= 7) {
538
+ mobx6.makeObservable(target, annotations ?? {})
539
+ } else {
540
+ mobx6.makeObservable(target)
541
+ }
542
+ }
543
+
422
544
  /**
423
545
  * @internal
424
546
  */
@@ -426,17 +548,26 @@ export function propNameToSetterName(propName: string): string {
426
548
  return `set${propName[0].toUpperCase()}${propName.slice(1)}`
427
549
  }
428
550
 
551
+ let cachedMobxVersion: number | undefined
552
+
429
553
  /**
430
554
  * @internal
431
555
  */
432
556
  export function getMobxVersion(): number {
557
+ if (cachedMobxVersion !== undefined) {
558
+ return cachedMobxVersion
559
+ }
560
+
433
561
  if (mobx6.makeObservable!) {
434
- return 6
562
+ const version = (mobx as any)._getGlobalState?.().version
563
+ cachedMobxVersion = typeof version === "number" && version >= 7 ? version : 6
435
564
  } else if ("extendShallowObservable" in mobx) {
436
- return 4
565
+ cachedMobxVersion = 4
437
566
  } else {
438
- return 5
567
+ cachedMobxVersion = 5
439
568
  }
569
+
570
+ return cachedMobxVersion
440
571
  }
441
572
 
442
573
  /**