mobx-keystone 1.24.1 → 2.0.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.
- package/CHANGELOG.md +137 -0
- package/README.md +5 -1
- package/dist/mobx-keystone.esm.js +1608 -1072
- package/dist/mobx-keystone.esm.mjs +1608 -1072
- package/dist/mobx-keystone.umd.js +1613 -1072
- package/dist/types/actionMiddlewares/actionSerialization/dateSerializer.d.ts +1 -1
- package/dist/types/dataModel/dataModelInstanceCache.d.ts +1 -0
- package/dist/types/deepChange/onDeepChange.d.ts +9 -0
- package/dist/types/parent/detach.d.ts +1 -1
- package/dist/types/parent/findChildren.d.ts +1 -1
- package/dist/types/patch/applyPatches.d.ts +2 -0
- package/dist/types/redux/connectReduxDevTools.d.ts +2 -1
- package/dist/types/snapshot/SnapshotOf.d.ts +2 -2
- package/dist/types/standardActions/arrayActions.d.ts +1 -1
- package/dist/types/treeUtils/sandbox.d.ts +0 -4
- package/dist/types/types/TypeCheckError.d.ts +1 -0
- package/dist/types/types/utility/typesRefinement.d.ts +2 -2
- package/dist/types/utils/forEachWithDelayedThrow.d.ts +1 -0
- package/dist/types/utils/index.d.ts +13 -0
- package/dist/types/utils/mapUtils.d.ts +2 -2
- package/dist/types/utils/setLike.d.ts +1 -0
- package/package.json +7 -7
- package/src/action/applyAction.ts +1 -1
- package/src/action/middleware.ts +17 -36
- package/src/action/modelFlow.ts +4 -5
- package/src/action/mutationBatch.ts +10 -10
- package/src/action/pendingActions.ts +3 -4
- package/src/actionMiddlewares/actionSerialization/actionSerialization.ts +19 -14
- package/src/actionMiddlewares/actionSerialization/applySerializedAction.ts +29 -20
- package/src/actionMiddlewares/actionSerialization/arraySerializer.ts +1 -1
- package/src/actionMiddlewares/actionSerialization/dateSerializer.ts +4 -3
- package/src/actionMiddlewares/actionSerialization/mapSerializer.ts +4 -12
- package/src/actionMiddlewares/actionSerialization/setSerializer.ts +4 -13
- package/src/actionMiddlewares/actionTrackingMiddleware.ts +11 -13
- package/src/actionMiddlewares/transactionMiddleware.ts +42 -11
- package/src/actionMiddlewares/undoMiddleware.ts +76 -60
- package/src/actionMiddlewares/utils.ts +2 -1
- package/src/computedTree/computedTree.ts +14 -3
- package/src/context/context.ts +17 -45
- package/src/dataModel/BaseDataModel.ts +20 -16
- package/src/dataModel/dataModelInstanceCache.ts +8 -0
- package/src/deepChange/onDeepChange.ts +179 -24
- package/src/frozen/Frozen.ts +50 -33
- package/src/model/BaseModel.ts +15 -14
- package/src/model/newModel.ts +32 -28
- package/src/model/utils.ts +2 -2
- package/src/modelShared/modelClassInitializer.ts +3 -2
- package/src/modelShared/modelDecorator.ts +34 -14
- package/src/modelShared/modelInfo.ts +1 -1
- package/src/modelShared/prop.ts +6 -9
- package/src/modelShared/sharedInternalModel.ts +16 -3
- package/src/parent/detach.ts +1 -1
- package/src/parent/findChildren.ts +4 -7
- package/src/parent/findParent.ts +14 -9
- package/src/parent/onChildAttachedTo.ts +45 -40
- package/src/parent/path.ts +11 -7
- package/src/parent/setParent.ts +33 -14
- package/src/parent/walkTree.ts +21 -37
- package/src/patch/applyPatches.ts +75 -60
- package/src/patch/emitPatch.ts +142 -26
- package/src/patch/jsonPatch.ts +5 -0
- package/src/patch/patchRecorder.ts +18 -14
- package/src/redux/connectReduxDevTools.ts +24 -18
- package/src/redux/redux.ts +5 -21
- package/src/ref/core.ts +18 -2
- package/src/rootStore/attachDetach.ts +27 -16
- package/src/rootStore/rootStore.ts +10 -6
- package/src/snapshot/SnapshotOf.ts +2 -2
- package/src/snapshot/applySnapshot.ts +10 -1
- package/src/snapshot/clone.ts +1 -2
- package/src/snapshot/cloneTreeValue.ts +1 -2
- package/src/snapshot/fromModelSnapshot.ts +6 -2
- package/src/snapshot/fromSnapshot.ts +3 -1
- package/src/snapshot/internal.ts +22 -9
- package/src/snapshot/modelInitialData.ts +14 -19
- package/src/snapshot/onSnapshot.ts +12 -1
- package/src/snapshot/reconcileModelSnapshot.ts +16 -8
- package/src/snapshot/reconcilePlainObjectSnapshot.ts +5 -4
- package/src/snapshot/reconcileSnapshot.ts +13 -4
- package/src/standardActions/arrayActions.ts +12 -2
- package/src/standardActions/objectActions.ts +3 -2
- package/src/transforms/ImmutableDate.ts +4 -0
- package/src/transforms/asMap.ts +3 -1
- package/src/treeUtils/deepEquals.ts +15 -13
- package/src/treeUtils/draft.ts +8 -0
- package/src/treeUtils/sandbox.ts +31 -27
- package/src/tweaker/core.ts +2 -4
- package/src/tweaker/tweak.ts +8 -8
- package/src/tweaker/tweakArray.ts +86 -37
- package/src/tweaker/tweakFrozen.ts +0 -1
- package/src/tweaker/tweakModel.ts +0 -1
- package/src/tweaker/tweakPlainObject.ts +78 -29
- package/src/tweaker/typeChecking.ts +132 -33
- package/src/types/TypeCheckError.ts +17 -19
- package/src/types/createCachedTypeCheck.ts +27 -2
- package/src/types/objectBased/typesDataModelData.ts +4 -2
- package/src/types/objectBased/typesModel.ts +4 -2
- package/src/types/objectBased/typesObject.ts +20 -16
- package/src/types/objectBased/typesRecord.ts +6 -13
- package/src/types/objectBased/typesRef.ts +2 -2
- package/src/types/primitiveBased/typesEnum.ts +4 -2
- package/src/types/primitiveBased/typesPrimitive.ts +9 -4
- package/src/types/typeCheckErrorUtils.ts +1 -0
- package/src/types/utility/typesCodecSupport.ts +97 -56
- package/src/types/utility/typesMap.ts +24 -41
- package/src/types/utility/typesRefinement.ts +3 -2
- package/src/types/utility/typesSet.ts +24 -0
- package/src/utils/ModelPool.ts +3 -3
- package/src/utils/chainFns.ts +4 -0
- package/src/utils/decorators.ts +1 -1
- package/src/utils/errorDiagnostics.ts +6 -2
- package/src/utils/forEachWithDelayedThrow.ts +83 -0
- package/src/utils/index.ts +34 -2
- package/src/utils/mapUtils.ts +3 -7
- package/src/utils/setIfDifferent.ts +3 -2
- package/src/utils/setLike.ts +134 -0
- package/src/wrappers/ArraySet.ts +5 -9
- package/src/wrappers/ObjectMap.ts +15 -11
- package/src/wrappers/asMap.ts +30 -18
- package/src/wrappers/asSet.ts +36 -12
- package/dist/types/action/modelFlowPromiseGenerator.d.ts +0 -1
- package/src/action/modelFlowPromiseGenerator.ts +0 -127
package/src/utils/index.ts
CHANGED
|
@@ -23,6 +23,24 @@ export class MobxKeystoneError extends Error {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* A mobx-keystone error that groups several failures that happened while delivering
|
|
28
|
+
* a single notification, such as multiple patch listeners or root store hooks throwing.
|
|
29
|
+
*
|
|
30
|
+
* It is only used when more than one callback failed; a lone failure is rethrown as is.
|
|
31
|
+
*/
|
|
32
|
+
export class MobxKeystoneAggregateError extends MobxKeystoneError {
|
|
33
|
+
/**
|
|
34
|
+
* The values thrown by the failing callbacks, in the order they were thrown.
|
|
35
|
+
*/
|
|
36
|
+
readonly errors: readonly unknown[]
|
|
37
|
+
|
|
38
|
+
constructor(errors: readonly unknown[], msg: string) {
|
|
39
|
+
super(msg)
|
|
40
|
+
this.errors = errors
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* @internal
|
|
28
46
|
*/
|
|
@@ -107,6 +125,18 @@ export let hasOwnProp: (object: object, propName: PropertyKey) => boolean = (
|
|
|
107
125
|
return impl(object, propName)
|
|
108
126
|
}
|
|
109
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Like `===`, except that `NaN` equals `NaN`.
|
|
130
|
+
* This is the `SameValueZero` equality used by `Map`/`Set` keys and by
|
|
131
|
+
* `Array.prototype.includes`, so `0` and `-0` are the same value.
|
|
132
|
+
*
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
export function isEqualOrBothNaN(a: unknown, b: unknown): boolean {
|
|
136
|
+
// biome-ignore lint/suspicious/noSelfCompare: NaN check
|
|
137
|
+
return a === b || (a !== a && b !== b)
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
/**
|
|
111
141
|
* @internal
|
|
112
142
|
*/
|
|
@@ -224,7 +254,9 @@ declare const process:
|
|
|
224
254
|
* @internal
|
|
225
255
|
*/
|
|
226
256
|
export const inDevMode =
|
|
227
|
-
process
|
|
257
|
+
typeof process !== "undefined" &&
|
|
258
|
+
process?.env?.NODE_ENV !== undefined &&
|
|
259
|
+
process.env.NODE_ENV !== "production"
|
|
228
260
|
|
|
229
261
|
/**
|
|
230
262
|
* @internal
|
|
@@ -545,7 +577,7 @@ export function makeObservableCompat(target: object, annotations?: object): void
|
|
|
545
577
|
* @internal
|
|
546
578
|
*/
|
|
547
579
|
export function propNameToSetterName(propName: string): string {
|
|
548
|
-
return `set${propName
|
|
580
|
+
return `set${propName.charAt(0).toUpperCase()}${propName.slice(1)}`
|
|
549
581
|
}
|
|
550
582
|
|
|
551
583
|
let cachedMobxVersion: number | undefined
|
package/src/utils/mapUtils.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
type AnyMap<V = any> = Map<any, V> | WeakMap<any, V>
|
|
2
2
|
|
|
3
|
-
export function getOrCreate<K, V
|
|
4
|
-
export function getOrCreate<K extends object, V,
|
|
5
|
-
map: WeakMap<K, V>,
|
|
6
|
-
key: K,
|
|
7
|
-
create: () => C
|
|
8
|
-
): V
|
|
3
|
+
export function getOrCreate<K, V>(map: Map<K, V>, key: K, create: () => V): V
|
|
4
|
+
export function getOrCreate<K extends object, V>(map: WeakMap<K, V>, key: K, create: () => V): V
|
|
9
5
|
|
|
10
6
|
export function getOrCreate<V>(map: AnyMap<V>, key: any, create: () => V) {
|
|
11
7
|
let value = map.get(key)
|
|
12
|
-
if (value === undefined) {
|
|
8
|
+
if (value === undefined && !map.has(key)) {
|
|
13
9
|
value = create()
|
|
14
10
|
map.set(key, value)
|
|
15
11
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { set } from "mobx"
|
|
2
|
+
import { isEqualOrBothNaN } from "./index"
|
|
2
3
|
|
|
3
4
|
export function setIfDifferent(target: any, key: PropertyKey, value: unknown): void {
|
|
4
|
-
if (target[key]
|
|
5
|
+
if (!isEqualOrBothNaN(target[key], value) || !(key in target)) {
|
|
5
6
|
set(target, key, value)
|
|
6
7
|
}
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export function setIfDifferentWithReturn(target: any, key: PropertyKey, value: unknown): boolean {
|
|
10
|
-
if (target[key]
|
|
11
|
+
if (!isEqualOrBothNaN(target[key], value) || !(key in target)) {
|
|
11
12
|
set(target, key, value)
|
|
12
13
|
return true
|
|
13
14
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { failure } from "./index"
|
|
2
|
+
|
|
3
|
+
// keys() need only return an iterator, not an iterable. Wrapping it in one also lets
|
|
4
|
+
// for-of close that iterator when a predicate exits early.
|
|
5
|
+
function keysOf<T>(set: ReadonlySetLike<T>): Iterable<T> {
|
|
6
|
+
return { [Symbol.iterator]: () => set.keys() }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Read these once, in protocol order, even when a size check can decide the result.
|
|
10
|
+
function getSetRecord<T>(set: ReadonlySetLike<T>) {
|
|
11
|
+
if ((typeof set !== "object" || set === null) && typeof set !== "function") {
|
|
12
|
+
throw failure("expected a set-like object")
|
|
13
|
+
}
|
|
14
|
+
const numberSize = +set.size
|
|
15
|
+
if (Number.isNaN(numberSize)) throw failure("set-like size must be a number")
|
|
16
|
+
const size = Math.trunc(numberSize)
|
|
17
|
+
if (size < 0) throw failure("set-like size must not be negative")
|
|
18
|
+
const has = set.has
|
|
19
|
+
if (typeof has !== "function") throw failure("set-like has must be a function")
|
|
20
|
+
const keys = set.keys
|
|
21
|
+
if (typeof keys !== "function") throw failure("set-like keys must be a function")
|
|
22
|
+
return {
|
|
23
|
+
size,
|
|
24
|
+
has: (value: T) => has.call(set, value),
|
|
25
|
+
keys: { [Symbol.iterator]: () => keys.call(set) } as Iterable<T>,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @internal */
|
|
30
|
+
export function isSetLikeSubsetOf<T>(
|
|
31
|
+
set: ReadonlySetLike<T>,
|
|
32
|
+
other: ReadonlySetLike<unknown>
|
|
33
|
+
): boolean {
|
|
34
|
+
const otherRecord = getSetRecord(other)
|
|
35
|
+
if (set.size > otherRecord.size) return false
|
|
36
|
+
for (const value of keysOf(set)) {
|
|
37
|
+
if (!otherRecord.has(value)) return false
|
|
38
|
+
}
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @internal */
|
|
43
|
+
export function isSetLikeSupersetOf<T>(
|
|
44
|
+
set: ReadonlySetLike<T>,
|
|
45
|
+
other: ReadonlySetLike<unknown>
|
|
46
|
+
): boolean {
|
|
47
|
+
const otherRecord = getSetRecord(other)
|
|
48
|
+
if (set.size < otherRecord.size) return false
|
|
49
|
+
for (const value of otherRecord.keys) {
|
|
50
|
+
if (!set.has(value as T)) return false
|
|
51
|
+
}
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @internal */
|
|
56
|
+
export function isSetLikeDisjointFrom<T>(
|
|
57
|
+
set: ReadonlySetLike<T>,
|
|
58
|
+
other: ReadonlySetLike<unknown>
|
|
59
|
+
): boolean {
|
|
60
|
+
const otherRecord = getSetRecord(other)
|
|
61
|
+
if (set.size <= otherRecord.size) {
|
|
62
|
+
for (const value of keysOf(set)) {
|
|
63
|
+
if (otherRecord.has(value)) return false
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
for (const value of otherRecord.keys) {
|
|
67
|
+
if (set.has(value as T)) return false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** @internal */
|
|
74
|
+
export function setLikeUnion<T, U>(set: ReadonlySetLike<T>, other: ReadonlySetLike<U>): Set<T | U> {
|
|
75
|
+
const otherRecord = getSetRecord(other)
|
|
76
|
+
const result = new Set<T | U>(keysOf(set))
|
|
77
|
+
for (const value of otherRecord.keys) result.add(value)
|
|
78
|
+
return result
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @internal */
|
|
82
|
+
export function setLikeIntersection<T, U>(
|
|
83
|
+
set: ReadonlySetLike<T>,
|
|
84
|
+
other: ReadonlySetLike<U>
|
|
85
|
+
): Set<T & U> {
|
|
86
|
+
const otherRecord = getSetRecord(other)
|
|
87
|
+
const result = new Set<T & U>()
|
|
88
|
+
if (set.size <= otherRecord.size) {
|
|
89
|
+
for (const value of keysOf(set)) {
|
|
90
|
+
if (otherRecord.has(value as unknown as U)) result.add(value as T & U)
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
for (const value of otherRecord.keys) {
|
|
94
|
+
if (set.has(value as unknown as T)) result.add(value as T & U)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @internal */
|
|
101
|
+
export function setLikeDifference<T>(
|
|
102
|
+
set: ReadonlySetLike<T>,
|
|
103
|
+
other: ReadonlySetLike<unknown>
|
|
104
|
+
): Set<T> {
|
|
105
|
+
const otherRecord = getSetRecord(other)
|
|
106
|
+
const result = new Set<T>()
|
|
107
|
+
if (set.size <= otherRecord.size) {
|
|
108
|
+
for (const value of keysOf(set)) {
|
|
109
|
+
if (!otherRecord.has(value)) result.add(value)
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
for (const value of keysOf(set)) result.add(value)
|
|
113
|
+
for (const value of otherRecord.keys) result.delete(value as T)
|
|
114
|
+
}
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** @internal */
|
|
119
|
+
export function setLikeSymmetricDifference<T, U>(
|
|
120
|
+
set: ReadonlySetLike<T>,
|
|
121
|
+
other: ReadonlySetLike<U>
|
|
122
|
+
): Set<T | U> {
|
|
123
|
+
const otherRecord = getSetRecord(other)
|
|
124
|
+
const result = new Set<T | U>(keysOf(set))
|
|
125
|
+
// Remember keys already processed: duplicate keys must not toggle membership.
|
|
126
|
+
// Use the decoded result's equality, just like the native Set result.
|
|
127
|
+
const seen = new Set<U>()
|
|
128
|
+
for (const value of otherRecord.keys) {
|
|
129
|
+
if (seen.has(value)) continue
|
|
130
|
+
seen.add(value)
|
|
131
|
+
if (!result.delete(value)) result.add(value)
|
|
132
|
+
}
|
|
133
|
+
return result
|
|
134
|
+
}
|
package/src/wrappers/ArraySet.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { values } from "mobx"
|
|
2
1
|
import { modelAction } from "../action/modelAction"
|
|
3
2
|
import type { AnyModel } from "../model/BaseModel"
|
|
4
3
|
import { Model } from "../model/Model"
|
|
@@ -48,7 +47,7 @@ export class ArraySet<V> extends arraySetBase implements Set<V> {
|
|
|
48
47
|
delete(value: V): boolean {
|
|
49
48
|
const items = this.items
|
|
50
49
|
|
|
51
|
-
const index = items.indexOf(value)
|
|
50
|
+
const index = Number.isNaN(value) ? items.findIndex(Number.isNaN) : items.indexOf(value)
|
|
52
51
|
if (index >= 0) {
|
|
53
52
|
items.splice(index, 1)
|
|
54
53
|
return true
|
|
@@ -75,15 +74,12 @@ export class ArraySet<V> extends arraySetBase implements Set<V> {
|
|
|
75
74
|
return this.items.length
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
for (const value of values(this.items)) {
|
|
81
|
-
yield value as V
|
|
82
|
-
}
|
|
77
|
+
keys(): ReturnType<Set<V>["keys"]> {
|
|
78
|
+
return this.values()
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
*values(): ReturnType<Set<V>["values"]> {
|
|
86
|
-
for (const value of
|
|
82
|
+
for (const value of this.items) {
|
|
87
83
|
yield value as V
|
|
88
84
|
}
|
|
89
85
|
}
|
|
@@ -150,7 +146,7 @@ export interface ArraySet<V> {
|
|
|
150
146
|
* @param [values] Optional initial values.
|
|
151
147
|
*/
|
|
152
148
|
export function arraySet<V>(values?: ReadonlyArray<V> | null): ArraySet<V> {
|
|
153
|
-
const initialArr: V[] = values ?
|
|
149
|
+
const initialArr: V[] = values ? Array.from(new Set(values)) : []
|
|
154
150
|
|
|
155
151
|
return new ArraySet({ items: initialArr })
|
|
156
152
|
}
|
|
@@ -8,7 +8,7 @@ import { idProp } from "../modelShared/prop"
|
|
|
8
8
|
import { typesRecord } from "../types/objectBased/typesRecord"
|
|
9
9
|
import { tProp } from "../types/tProp"
|
|
10
10
|
import { typesUnchecked } from "../types/utility/typesUnchecked"
|
|
11
|
-
import { namespace } from "../utils"
|
|
11
|
+
import { namespace, setProtoProp } from "../utils"
|
|
12
12
|
import { setIfDifferent } from "../utils/setIfDifferent"
|
|
13
13
|
|
|
14
14
|
const objectMapBase = Model({
|
|
@@ -34,11 +34,11 @@ export class ObjectMap<V> extends objectMapBase implements Map<string, V> {
|
|
|
34
34
|
clear(): void {
|
|
35
35
|
const items = this.items
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
const
|
|
37
|
+
// Object.keys is enough here: this is a write, so there is nothing to observe.
|
|
38
|
+
const itemKeys = Object.keys(items)
|
|
39
|
+
const len = itemKeys.length
|
|
39
40
|
for (let i = 0; i < len; i++) {
|
|
40
|
-
|
|
41
|
-
remove(items, k)
|
|
41
|
+
remove(items, itemKeys[i])
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -57,10 +57,10 @@ export class ObjectMap<V> extends objectMapBase implements Map<string, V> {
|
|
|
57
57
|
// we cannot use the map implementation since we need to pass this as map
|
|
58
58
|
const items = this.items
|
|
59
59
|
|
|
60
|
-
const
|
|
61
|
-
const len =
|
|
60
|
+
const itemKeys = keys(items)
|
|
61
|
+
const len = itemKeys.length
|
|
62
62
|
for (let i = 0; i < len; i++) {
|
|
63
|
-
const k =
|
|
63
|
+
const k = itemKeys[i] as string
|
|
64
64
|
callbackfn.call(thisArg, items[k], k, this)
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -75,7 +75,7 @@ export class ObjectMap<V> extends objectMapBase implements Map<string, V> {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
this.set(key, defaultValue)
|
|
78
|
-
return
|
|
78
|
+
return this.get(key) as V
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
getOrInsertComputed(key: string, callback: (key: string) => V): V {
|
|
@@ -85,7 +85,7 @@ export class ObjectMap<V> extends objectMapBase implements Map<string, V> {
|
|
|
85
85
|
|
|
86
86
|
const value = callback(key)
|
|
87
87
|
this.set(key, value)
|
|
88
|
-
return
|
|
88
|
+
return this.get(key) as V
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
has(key: string): boolean {
|
|
@@ -147,7 +147,11 @@ export function objectMap<V>(entries?: ReadonlyArray<readonly [string, V]> | nul
|
|
|
147
147
|
const len = entries.length
|
|
148
148
|
for (let i = 0; i < len; i++) {
|
|
149
149
|
const entry = entries[i]
|
|
150
|
-
|
|
150
|
+
if (entry[0] === "__proto__") {
|
|
151
|
+
setProtoProp(initialObj, entry[1])
|
|
152
|
+
} else {
|
|
153
|
+
initialObj[entry[0]] = entry[1]
|
|
154
|
+
}
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
|
package/src/wrappers/asMap.ts
CHANGED
|
@@ -20,8 +20,11 @@ import {
|
|
|
20
20
|
assertIsObservableObject,
|
|
21
21
|
failure,
|
|
22
22
|
getMobxVersion,
|
|
23
|
+
hasOwnProp,
|
|
23
24
|
inDevMode,
|
|
24
25
|
isArray,
|
|
26
|
+
isEqualOrBothNaN,
|
|
27
|
+
setProtoProp,
|
|
25
28
|
} from "../utils"
|
|
26
29
|
import { setIfDifferent } from "../utils/setIfDifferent"
|
|
27
30
|
import { tag } from "../utils/tag"
|
|
@@ -46,7 +49,7 @@ function addTs6MapExtras<K, V>(map: ObservableMap<K, V>): ObservableMap<K, V> &
|
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
this.set(key, defaultValue)
|
|
49
|
-
return
|
|
52
|
+
return this.get(key) as V
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -58,7 +61,7 @@ function addTs6MapExtras<K, V>(map: ObservableMap<K, V>): ObservableMap<K, V> &
|
|
|
58
61
|
|
|
59
62
|
const value = callback(key)
|
|
60
63
|
this.set(key, value)
|
|
61
|
-
return
|
|
64
|
+
return this.get(key) as V
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
|
|
@@ -75,7 +78,7 @@ const observableMapBackedByObservableObject = action(
|
|
|
75
78
|
|
|
76
79
|
const map = transaction(() =>
|
|
77
80
|
untracked(() => {
|
|
78
|
-
const map = observable.map<string, T>()
|
|
81
|
+
const map = observable.map<string, T>(undefined, { deep: false })
|
|
79
82
|
|
|
80
83
|
const keys = Object.keys(obj)
|
|
81
84
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -143,11 +146,14 @@ const observableMapBackedByObservableObject = action(
|
|
|
143
146
|
case "add":
|
|
144
147
|
case "update": {
|
|
145
148
|
setIfDifferent(obj, change.name, change.newValue)
|
|
149
|
+
if (!hasOwnProp(obj, change.name)) return null
|
|
150
|
+
change.newValue = (obj as Record<string, T>)[change.name]
|
|
146
151
|
break
|
|
147
152
|
}
|
|
148
153
|
|
|
149
154
|
case "delete": {
|
|
150
155
|
remove(obj, change.name)
|
|
156
|
+
if (hasOwnProp(obj, change.name)) return null
|
|
151
157
|
break
|
|
152
158
|
}
|
|
153
159
|
|
|
@@ -166,20 +172,20 @@ const observableMapBackedByObservableObject = action(
|
|
|
166
172
|
}
|
|
167
173
|
)
|
|
168
174
|
|
|
169
|
-
const observableMapBackedByObservableArray = <T>(
|
|
170
|
-
array: IObservableArray<[
|
|
171
|
-
): ObservableMapWithDataObject<
|
|
175
|
+
const observableMapBackedByObservableArray = <K, T>(
|
|
176
|
+
array: IObservableArray<[K, T]>
|
|
177
|
+
): ObservableMapWithDataObject<K, T, typeof array> => {
|
|
172
178
|
if (inDevMode) {
|
|
173
179
|
if (!isObservableArray(array)) {
|
|
174
180
|
throw failure("assertion failed: expected an observable array")
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
const map: ObservableMap<
|
|
184
|
+
const map: ObservableMap<K, T> = untracked(() => {
|
|
179
185
|
if (getMobxVersion() >= 6) {
|
|
180
|
-
return observable.map<
|
|
186
|
+
return observable.map<K, T>(array, { deep: false })
|
|
181
187
|
} else {
|
|
182
|
-
const map = observable.map<
|
|
188
|
+
const map = observable.map<K, T>(undefined, { deep: false })
|
|
183
189
|
runInAction(() => {
|
|
184
190
|
array.forEach(([k, v]) => {
|
|
185
191
|
map.set(k, v)
|
|
@@ -204,7 +210,7 @@ const observableMapBackedByObservableArray = <T>(
|
|
|
204
210
|
// when the array changes the map changes
|
|
205
211
|
observe(
|
|
206
212
|
array,
|
|
207
|
-
action((change: any /*IArrayDidChange<[
|
|
213
|
+
action((change: any /*IArrayDidChange<[K, T]>*/) => {
|
|
208
214
|
if (mapAlreadyChanged) {
|
|
209
215
|
return
|
|
210
216
|
}
|
|
@@ -248,7 +254,7 @@ const observableMapBackedByObservableArray = <T>(
|
|
|
248
254
|
// when the map changes also change the array
|
|
249
255
|
intercept(
|
|
250
256
|
map,
|
|
251
|
-
action((change: IMapWillChange<
|
|
257
|
+
action((change: IMapWillChange<K, T>) => {
|
|
252
258
|
if (mapAlreadyChanged) {
|
|
253
259
|
return null
|
|
254
260
|
}
|
|
@@ -263,21 +269,23 @@ const observableMapBackedByObservableArray = <T>(
|
|
|
263
269
|
switch (change.type) {
|
|
264
270
|
case "update": {
|
|
265
271
|
// replace the whole tuple to keep tuple immutability
|
|
266
|
-
const i = array.findIndex((
|
|
272
|
+
const i = array.findIndex((entry) => isEqualOrBothNaN(entry[0], change.name))
|
|
267
273
|
array[i] = [change.name, change.newValue!]
|
|
274
|
+
change.newValue = array[i][1]
|
|
268
275
|
break
|
|
269
276
|
}
|
|
270
277
|
|
|
271
278
|
case "add": {
|
|
279
|
+
const oldLength = array.length
|
|
272
280
|
array.push([change.name, change.newValue!])
|
|
281
|
+
if (array.length === oldLength) return null
|
|
282
|
+
change.newValue = array[array.length - 1][1]
|
|
273
283
|
break
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
case "delete": {
|
|
277
|
-
const i = array.findIndex((
|
|
278
|
-
if (i >= 0)
|
|
279
|
-
array.splice(i, 1)
|
|
280
|
-
}
|
|
287
|
+
const i = array.findIndex((entry) => isEqualOrBothNaN(entry[0], change.name))
|
|
288
|
+
if (i >= 0 && array.splice(i, 1).length === 0) return null
|
|
281
289
|
break
|
|
282
290
|
}
|
|
283
291
|
|
|
@@ -295,7 +303,7 @@ const observableMapBackedByObservableArray = <T>(
|
|
|
295
303
|
return mapWithExtras as any
|
|
296
304
|
}
|
|
297
305
|
|
|
298
|
-
const asMapTag = tag((objOrArray: Record<string,
|
|
306
|
+
const asMapTag = tag((objOrArray: Record<string, unknown> | Array<[unknown, unknown]>) => {
|
|
299
307
|
if (isArray(objOrArray)) {
|
|
300
308
|
assertIsObservableArray(objOrArray, "objOrArray")
|
|
301
309
|
return observableMapBackedByObservableArray(objOrArray)
|
|
@@ -347,7 +355,11 @@ export function mapToObject<T>(map: Pick<Map<string, T>, "forEach">): Record<str
|
|
|
347
355
|
|
|
348
356
|
const obj: Record<string, T> = {}
|
|
349
357
|
map.forEach((v, k) => {
|
|
350
|
-
|
|
358
|
+
if (k === "__proto__") {
|
|
359
|
+
setProtoProp(obj, v)
|
|
360
|
+
} else {
|
|
361
|
+
obj[k] = v
|
|
362
|
+
}
|
|
351
363
|
})
|
|
352
364
|
|
|
353
365
|
return obj
|
package/src/wrappers/asSet.ts
CHANGED
|
@@ -11,7 +11,14 @@ import {
|
|
|
11
11
|
transaction,
|
|
12
12
|
untracked,
|
|
13
13
|
} from "mobx"
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
assertIsObservableArray,
|
|
16
|
+
assertIsSet,
|
|
17
|
+
failure,
|
|
18
|
+
getMobxVersion,
|
|
19
|
+
inDevMode,
|
|
20
|
+
isEqualOrBothNaN,
|
|
21
|
+
} from "../utils"
|
|
15
22
|
import { tag } from "../utils/tag"
|
|
16
23
|
|
|
17
24
|
const observableSetBackedByObservableArray = <T>(
|
|
@@ -26,9 +33,9 @@ const observableSetBackedByObservableArray = <T>(
|
|
|
26
33
|
const set = transaction(() =>
|
|
27
34
|
untracked(() => {
|
|
28
35
|
if (getMobxVersion() >= 6) {
|
|
29
|
-
return observable.set(array)
|
|
36
|
+
return observable.set(array, { deep: false })
|
|
30
37
|
} else {
|
|
31
|
-
const set = observable.set()
|
|
38
|
+
const set = observable.set<T>(undefined, { deep: false })
|
|
32
39
|
runInAction(() => {
|
|
33
40
|
array.forEach((item) => {
|
|
34
41
|
set.add(item)
|
|
@@ -98,28 +105,45 @@ const observableSetBackedByObservableArray = <T>(
|
|
|
98
105
|
intercept(
|
|
99
106
|
set,
|
|
100
107
|
action((change: ISetWillChange<T>) => {
|
|
101
|
-
if (setAlreadyChanged) {
|
|
102
|
-
return null
|
|
103
|
-
}
|
|
104
|
-
|
|
105
108
|
if (arrayAlreadyChanged) {
|
|
106
109
|
return change
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
if (setAlreadyChanged) {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
setAlreadyChanged = true
|
|
110
117
|
|
|
111
118
|
try {
|
|
112
119
|
switch (change.type) {
|
|
113
120
|
case "add": {
|
|
114
|
-
|
|
121
|
+
if (!set.has(change.newValue)) {
|
|
122
|
+
const oldLength = array.length
|
|
123
|
+
array.push(change.newValue)
|
|
124
|
+
if (array.length === oldLength) return null
|
|
125
|
+
const storedValue = array[array.length - 1]
|
|
126
|
+
if (getMobxVersion() >= 6) {
|
|
127
|
+
change.newValue = storedValue
|
|
128
|
+
} else if (!isEqualOrBothNaN(storedValue, change.newValue)) {
|
|
129
|
+
// MobX 4/5 ignore replacement values returned by set interceptors.
|
|
130
|
+
arrayAlreadyChanged = true
|
|
131
|
+
try {
|
|
132
|
+
set.add(storedValue)
|
|
133
|
+
} finally {
|
|
134
|
+
arrayAlreadyChanged = false
|
|
135
|
+
}
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
}
|
|
115
139
|
break
|
|
116
140
|
}
|
|
117
141
|
|
|
118
142
|
case "delete": {
|
|
119
|
-
const i =
|
|
120
|
-
|
|
121
|
-
array.
|
|
122
|
-
|
|
143
|
+
const i = Number.isNaN(change.oldValue)
|
|
144
|
+
? array.findIndex(Number.isNaN)
|
|
145
|
+
: array.indexOf(change.oldValue)
|
|
146
|
+
if (i >= 0 && array.splice(i, 1).length === 0) return null
|
|
123
147
|
break
|
|
124
148
|
}
|
|
125
149
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function promiseGenerator(this: Promise<any>): any;
|