mobx-keystone 2.1.0 → 2.3.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 (83) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/dist/mobx-keystone.esm.js +555 -516
  4. package/dist/mobx-keystone.esm.mjs +555 -516
  5. package/dist/mobx-keystone.umd.js +558 -519
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +2 -10
  7. package/dist/types/modelShared/modelDecorator.d.ts +5 -2
  8. package/dist/types/modelShared/prop.d.ts +42 -29
  9. package/dist/types/ref/Ref.d.ts +1 -4
  10. package/dist/types/snapshot/SnapshotOf.d.ts +4 -4
  11. package/dist/types/snapshot/applySnapshot.d.ts +1 -1
  12. package/dist/types/snapshot/fromSnapshot.d.ts +2 -2
  13. package/dist/types/snapshot/getSnapshot.d.ts +1 -1
  14. package/dist/types/types/SnapshotTypeMismatchError.d.ts +3 -1
  15. package/dist/types/types/index.d.ts +1 -0
  16. package/dist/types/types/modelTypeMatching.d.ts +1 -0
  17. package/dist/types/types/schemas.d.ts +41 -37
  18. package/dist/types/types/tProp.d.ts +12 -6
  19. package/dist/types/types/types.d.ts +6 -6
  20. package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
  21. package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
  22. package/dist/types/utils/ModelPool.d.ts +0 -1
  23. package/dist/types/utils/types.d.ts +6 -0
  24. package/dist/types/wrappers/ArraySet.d.ts +1 -0
  25. package/package.json +13 -11
  26. package/src/action/modelFlow.ts +35 -66
  27. package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
  28. package/src/actionMiddlewares/transactionMiddleware.ts +1 -1
  29. package/src/actionMiddlewares/undoMiddleware.ts +16 -22
  30. package/src/context/context.ts +15 -36
  31. package/src/dataModel/utils.ts +5 -28
  32. package/src/deepChange/onDeepChange.ts +44 -126
  33. package/src/model/BaseModel.ts +1 -1
  34. package/src/model/utils.ts +5 -10
  35. package/src/modelShared/modelDecorator.ts +13 -10
  36. package/src/modelShared/modelInfo.ts +75 -0
  37. package/src/modelShared/prop.ts +115 -111
  38. package/src/modelShared/sharedInternalModel.ts +2 -2
  39. package/src/parent/arrayPaths.ts +1 -4
  40. package/src/parent/path.ts +0 -10
  41. package/src/patch/patchRecorder.ts +13 -28
  42. package/src/ref/core.ts +6 -19
  43. package/src/rootStore/rootStore.ts +1 -3
  44. package/src/snapshot/SnapshotOf.ts +24 -48
  45. package/src/snapshot/applySnapshot.ts +1 -1
  46. package/src/snapshot/fromSnapshot.ts +10 -3
  47. package/src/snapshot/getSnapshot.ts +1 -1
  48. package/src/snapshot/internal.ts +0 -2
  49. package/src/snapshot/reconcileModelSnapshot.ts +5 -3
  50. package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
  51. package/src/treeUtils/draft.ts +6 -1
  52. package/src/treeUtils/sandbox.ts +10 -7
  53. package/src/tweaker/tweakArray.ts +20 -50
  54. package/src/tweaker/tweakPlainObject.ts +9 -15
  55. package/src/types/SnapshotTypeMismatchError.ts +3 -1
  56. package/src/types/arrayBased/typesArray.ts +13 -19
  57. package/src/types/arrayBased/typesTuple.ts +16 -25
  58. package/src/types/createCachedTypeCheck.ts +17 -30
  59. package/src/types/index.ts +1 -0
  60. package/src/types/modelTypeMatching.ts +276 -0
  61. package/src/types/objectBased/typesDataModelData.ts +7 -8
  62. package/src/types/objectBased/typesModel.ts +23 -6
  63. package/src/types/objectBased/typesObject.ts +11 -15
  64. package/src/types/objectBased/typesRecord.ts +11 -15
  65. package/src/types/objectBased/typesRef.ts +23 -4
  66. package/src/types/schemas.ts +59 -71
  67. package/src/types/tProp.ts +29 -29
  68. package/src/types/utility/typeToStoredData.ts +22 -21
  69. package/src/types/utility/typesCodecCore.ts +8 -1
  70. package/src/types/utility/typesCodecSupport.ts +16 -10
  71. package/src/types/utility/typesMap.ts +43 -51
  72. package/src/types/utility/typesOr.ts +38 -8
  73. package/src/types/utility/typesRefinement.ts +7 -8
  74. package/src/types/utility/typesSkipCheck.ts +7 -8
  75. package/src/types/utility/typesTag.ts +7 -8
  76. package/src/utils/ModelPool.ts +1 -18
  77. package/src/utils/decorators.ts +5 -5
  78. package/src/utils/index.ts +1 -20
  79. package/src/utils/types.ts +5 -0
  80. package/src/wrappers/ArraySet.ts +27 -14
  81. package/src/wrappers/ObjectMap.ts +1 -6
  82. package/src/wrappers/asMap.ts +1 -21
  83. package/src/wrappers/asSet.ts +0 -8
@@ -3,7 +3,7 @@ import type { AnyStandardType } from "../types/schemas"
3
3
  import { isEqualOrBothNaN, lazy } from "../utils"
4
4
  import { runWithErrorDiagnosticsContext, withErrorPathSegment } from "../utils/errorDiagnostics"
5
5
  import { getOrCreate } from "../utils/mapUtils"
6
- import type { Flatten, IsNeverType, IsOptionalValue } from "../utils/types"
6
+ import type { IsNeverType, IsOptionalValue, SimplifyObject } from "../utils/types"
7
7
 
8
8
  /**
9
9
  * @ignore
@@ -71,50 +71,18 @@ export interface ModelProp<
71
71
  * Adds a setter to the property. The setter will be named `set${CapitalizedPropName}`
72
72
  * and will be available in the model instance.
73
73
  */
74
- withSetter(): ModelProp<
75
- TPropValue,
76
- TPropCreationValue,
77
- TTransformedValue,
78
- TTransformedCreationValue,
79
- TIsRequired,
80
- TIsId,
81
- string,
82
- TFromSnapshotOverride,
83
- TToSnapshotOverride
84
- >
74
+ withSetter(): ModelPropWithSetter<this>
85
75
  /**
86
76
  * Adds a setter with a transform to the property. The setter will be named `set${CapitalizedPropName}`
87
77
  * and will be available in the model instance.
88
78
  */
89
79
  withSetter(
90
80
  valueTransform: ModelPropSetterValueTransform<TTransformedValue>
91
- ): ModelProp<
92
- TPropValue,
93
- TPropCreationValue,
94
- TTransformedValue,
95
- TTransformedCreationValue,
96
- TIsRequired,
97
- TIsId,
98
- string,
99
- TFromSnapshotOverride,
100
- TToSnapshotOverride
101
- >
81
+ ): ModelPropWithSetter<this>
102
82
  /**
103
83
  * @deprecated Setter methods are preferred.
104
84
  */
105
- withSetter(
106
- mode: "assign"
107
- ): ModelProp<
108
- TPropValue,
109
- TPropCreationValue,
110
- TTransformedValue,
111
- TTransformedCreationValue,
112
- TIsRequired,
113
- TIsId,
114
- string,
115
- TFromSnapshotOverride,
116
- TToSnapshotOverride
117
- >
85
+ withSetter(mode: "assign"): ModelPropWithSetter<this>
118
86
 
119
87
  /**
120
88
  * Sets a transform for the property instance value.
@@ -130,17 +98,7 @@ export interface ModelProp<
130
98
  */
131
99
  withTransform<TTV>(
132
100
  transform: ModelPropTransform<NonNullable<TPropValue>, TTV>
133
- ): ModelProp<
134
- TPropValue,
135
- TPropCreationValue,
136
- TTV | Extract<TPropValue, null | undefined>,
137
- TTV | Extract<TPropCreationValue, null | undefined>,
138
- TIsRequired,
139
- TIsId,
140
- THasSetter,
141
- TFromSnapshotOverride,
142
- TToSnapshotOverride
143
- >
101
+ ): ModelPropWithTransform<this, TTV>
144
102
 
145
103
  /**
146
104
  * Sets snapshot processors for this property.
@@ -158,18 +116,69 @@ export interface ModelProp<
158
116
  >(processor: {
159
117
  fromSnapshot?: (sn: FS) => ModelPropFromSnapshot<This>
160
118
  toSnapshot?: (sn: ModelPropToSnapshot<This>) => TS
161
- }): ModelProp<
162
- TPropValue,
163
- TPropCreationValue,
164
- TTransformedValue,
165
- TTransformedCreationValue,
166
- TIsRequired,
167
- TIsId,
168
- THasSetter,
169
- FS,
170
- TS
171
- >
119
+ }): ModelPropWithSnapshotProcessor<this, FS, TS>
120
+ }
121
+
122
+ type ModelPropStoredMetadataKey = "$storedValueType" | "$storedCreationValueType"
123
+
124
+ // keeps the stored value metadata that `tProp` adds (e.g. the encoded type of a codec)
125
+ // when a model prop is derived through one of its modifiers
126
+ type ModelPropStoredMetadata<MP, K extends PropertyKey> = MP extends {
127
+ $storedValueType: unknown
172
128
  }
129
+ ? Pick<MP, Extract<keyof MP, K>>
130
+ : {}
131
+
132
+ // the modifier result types are exported so emitted declaration files can reference them by name
133
+ // instead of expanding them inline in every exported model class
134
+
135
+ /**
136
+ * The model property returned by `withSetter`.
137
+ */
138
+ export type ModelPropWithSetter<MP extends AnyModelProp> = ModelProp<
139
+ MP["$valueType"],
140
+ MP["$creationValueType"],
141
+ MP["$transformedValueType"],
142
+ MP["$transformedCreationValueType"],
143
+ MP["$isRequired"],
144
+ MP["$isId"],
145
+ string,
146
+ MP["$fromSnapshotOverride"],
147
+ MP["$toSnapshotOverride"]
148
+ > &
149
+ ModelPropStoredMetadata<MP, ModelPropStoredMetadataKey | "$typedFromSnapshotOverride">
150
+
151
+ /**
152
+ * The model property returned by `withTransform`.
153
+ */
154
+ export type ModelPropWithTransform<MP extends AnyModelProp, TTV> = ModelProp<
155
+ MP["$valueType"],
156
+ MP["$creationValueType"],
157
+ TTV | Extract<MP["$valueType"], null | undefined>,
158
+ TTV | Extract<MP["$creationValueType"], null | undefined>,
159
+ MP["$isRequired"],
160
+ MP["$isId"],
161
+ MP["$hasSetter"],
162
+ MP["$fromSnapshotOverride"],
163
+ MP["$toSnapshotOverride"]
164
+ > &
165
+ ModelPropStoredMetadata<MP, ModelPropStoredMetadataKey | "$typedFromSnapshotOverride">
166
+
167
+ /**
168
+ * The model property returned by `withSnapshotProcessor`.
169
+ */
170
+ export type ModelPropWithSnapshotProcessor<MP extends AnyModelProp, FS, TS> = ModelProp<
171
+ MP["$valueType"],
172
+ MP["$creationValueType"],
173
+ MP["$transformedValueType"],
174
+ MP["$transformedCreationValueType"],
175
+ MP["$isRequired"],
176
+ MP["$isId"],
177
+ MP["$hasSetter"],
178
+ FS,
179
+ TS
180
+ > &
181
+ ModelPropStoredMetadata<MP, ModelPropStoredMetadataKey>
173
182
 
174
183
  /**
175
184
  * The snapshot in type of a model property.
@@ -189,23 +198,19 @@ export type ModelPropToSnapshot<MP extends AnyModelProp> = IsNeverType<
189
198
  MP["$toSnapshotOverride"]
190
199
  >
191
200
 
192
- export type ModelPropStoredValue<MP extends AnyModelProp> = MP extends {
193
- $storedValueType: infer TStoredValue
194
- }
195
- ? TStoredValue
201
+ export type ModelPropStoredValue<MP extends AnyModelProp> = "$storedValueType" extends keyof MP
202
+ ? MP["$storedValueType" & keyof MP]
196
203
  : MP["$valueType"]
197
204
 
198
- export type ModelPropStoredCreationValue<MP extends AnyModelProp> = MP extends {
199
- $storedCreationValueType: infer TStoredCreationValue
200
- }
201
- ? TStoredCreationValue
202
- : MP["$creationValueType"]
205
+ export type ModelPropStoredCreationValue<MP extends AnyModelProp> =
206
+ "$storedCreationValueType" extends keyof MP
207
+ ? MP["$storedCreationValueType" & keyof MP]
208
+ : MP["$creationValueType"]
203
209
 
204
- export type ModelPropFromSnapshotOverride<MP extends AnyModelProp> = MP extends {
205
- $typedFromSnapshotOverride: infer TFromSnapshotOverride
206
- }
207
- ? TFromSnapshotOverride
208
- : MP["$fromSnapshotOverride"]
210
+ export type ModelPropFromSnapshotOverride<MP extends AnyModelProp> =
211
+ "$typedFromSnapshotOverride" extends keyof MP
212
+ ? MP["$typedFromSnapshotOverride" & keyof MP]
213
+ : MP["$fromSnapshotOverride"]
209
214
 
210
215
  /**
211
216
  * A model prop transform.
@@ -245,13 +250,13 @@ export type RequiredModelProps<MP extends ModelProps> = {
245
250
  [K in keyof MP]: MP[K]["$isRequired"] & K
246
251
  }[keyof MP]
247
252
 
248
- export type ModelPropsToUntransformedData<MP extends ModelProps> = Flatten<{
253
+ export type ModelPropsToUntransformedData<MP extends ModelProps> = {
249
254
  [k in keyof MP]: ModelPropStoredValue<MP[k]>
250
- }>
255
+ } & {}
251
256
 
252
- export type ModelPropsToSnapshotData<MP extends ModelProps> = Flatten<{
257
+ export type ModelPropsToSnapshotData<MP extends ModelProps> = {
253
258
  [k in keyof MP]: ModelPropToSnapshot<MP[k]> extends infer R ? R : never
254
- }>
259
+ } & {}
255
260
 
256
261
  // we don't use O.Optional anymore since it generates unions too heavy
257
262
  // also if we use Pick over the optional props we will loose the ability to infer generics
@@ -264,23 +269,29 @@ export type ModelPropsToUntransformedCreationData<MP extends ModelProps> = {
264
269
 
265
270
  // we don't use O.Optional anymore since it generates unions too heavy
266
271
  // also if we use Pick over the optional props we will loose the ability to infer generics
267
- export type ModelPropsToSnapshotCreationData<MP extends ModelProps> = Flatten<
272
+ export type ModelPropsToSnapshotCreationData<MP extends ModelProps> = SimplifyObject<
268
273
  {
269
274
  [k in keyof MP]?: ModelPropFromSnapshot<MP[k]> extends infer R ? R : never
270
275
  } & {
271
- [k in {
272
- [K in keyof MP]: IsNeverType<
273
- ModelPropFromSnapshotOverride<MP[K]>,
274
- MP[K]["$isRequired"] & K, // no override
275
- IsOptionalValue<ModelPropFromSnapshotOverride<MP[K]>, never, K> // with override
276
- >
277
- }[keyof MP]]: ModelPropFromSnapshot<MP[k]> extends infer R ? R : never
276
+ [k in SnapshotCreationRequiredKeys<MP>]: ModelPropFromSnapshot<MP[k]> extends infer R
277
+ ? R
278
+ : never
278
279
  }
279
280
  >
280
281
 
281
- export type ModelPropsToTransformedData<MP extends ModelProps> = Flatten<{
282
+ // without a snapshot processor a key is required when the prop is required,
283
+ // with one when the processor input does not accept undefined
284
+ type SnapshotCreationRequiredKeys<MP extends ModelProps> = {
285
+ [K in keyof MP]: IsNeverType<
286
+ ModelPropFromSnapshotOverride<MP[K]>,
287
+ MP[K]["$isRequired"] & K,
288
+ IsOptionalValue<ModelPropFromSnapshotOverride<MP[K]>, never, K>
289
+ >
290
+ }[keyof MP]
291
+
292
+ export type ModelPropsToTransformedData<MP extends ModelProps> = {
282
293
  [k in keyof MP]: MP[k]["$transformedValueType"]
283
- }>
294
+ } & {}
284
295
 
285
296
  // we don't use O.Optional anymore since it generates unions too heavy
286
297
  // also if we use Pick over the optional props we will loose the ability to infer generics
@@ -292,11 +303,11 @@ export type ModelPropsToTransformedCreationData<MP extends ModelProps> = {
292
303
  [k in RequiredModelProps<MP>]: MP[k]["$transformedCreationValueType"]
293
304
  }
294
305
 
295
- export type ModelPropsToSetter<MP extends ModelProps> = Flatten<{
306
+ export type ModelPropsToSetter<MP extends ModelProps> = {
296
307
  [k in keyof MP as MP[k]["$hasSetter"] & `set${Capitalize<k & string>}`]: (
297
308
  value: MP[k]["$transformedValueType"]
298
309
  ) => void
299
- }>
310
+ } & {}
300
311
 
301
312
  export type ModelIdProp<T extends string = string> = ModelProp<
302
313
  T,
@@ -307,7 +318,12 @@ export type ModelIdProp<T extends string = string> = ModelProp<
307
318
  true
308
319
  >
309
320
 
310
- type TypedModelIdProp<T extends string = string, THasSetter = never> = Omit<
321
+ // exported (and publicly re-exported) so emitted declaration files can reference it by name
322
+ // instead of expanding it inline in every exported model class, which is much slower and larger
323
+ /**
324
+ * The type of `idProp`.
325
+ */
326
+ export type TypedModelIdProp<T extends string = string, THasSetter = never> = Omit<
311
327
  ModelProp<T, T | undefined, T, T | undefined, never, true, THasSetter>,
312
328
  "withSetter"
313
329
  > & {
@@ -569,21 +585,17 @@ export function prop(...args: [] | [def: any]): AnyModelProp {
569
585
 
570
586
  const [def] = args
571
587
 
572
- let p = propCache.get(def)
573
-
574
- if (!p) {
575
- p = Object.create(baseProp)
588
+ return getOrCreate(propCache, def, () => {
589
+ const p: AnyModelProp = Object.create(baseProp)
576
590
 
577
591
  if (typeof def === "function") {
578
- p!._defaultFn = def
592
+ p._defaultFn = def
579
593
  } else {
580
- p!._defaultValue = def
594
+ p._defaultValue = def
581
595
  }
582
596
 
583
- propCache.set(def, p!)
584
- }
585
-
586
- return p!
597
+ return p
598
+ })
587
599
  }
588
600
 
589
601
  function parseSetterConfig(
@@ -599,19 +611,11 @@ function parseSetterConfig(
599
611
  }
600
612
  }
601
613
 
602
- if (
603
- modeOrValueTransform === undefined ||
604
- typeof modeOrValueTransform === "boolean" ||
605
- modeOrValueTransform === "assign"
606
- ) {
607
- return {
608
- mode: modeOrValueTransform ?? true,
609
- valueTransform: undefined,
610
- }
611
- }
612
-
613
614
  return {
614
- mode: true,
615
+ mode:
616
+ typeof modeOrValueTransform === "boolean" || modeOrValueTransform === "assign"
617
+ ? modeOrValueTransform
618
+ : true,
615
619
  valueTransform: undefined,
616
620
  }
617
621
  }
@@ -241,7 +241,7 @@ export function sharedInternalModel<
241
241
  const idPropGenerator = idProp._idGenerator
242
242
  const baseIdProp = baseModelProps?.[idKey]
243
243
  const baseIdPropGenerator = baseIdProp?._idGenerator
244
- const isOverridingBaseIdProp = !!baseIdProp?._isId && Object.hasOwn(modelProps, idKey)
244
+ const isOverridingBaseIdProp = !!baseIdProp?._isId && hasOwnProp(modelProps, idKey)
245
245
  if (isOverridingBaseIdProp && baseIdPropGenerator !== idPropGenerator) {
246
246
  throw failure(
247
247
  `expected same idProp.withGenerator function when overriding a base idProp, but got different references`
@@ -315,7 +315,7 @@ export function sharedInternalModel<
315
315
  // make sure abstract classes do not override prototype props
316
316
  if (!propsToDeleteFromBase) {
317
317
  propsToDeleteFromBase = Object.keys(modelProps).filter(
318
- (p) => !basePropNames.has(p as any) && Object.hasOwn(baseModel, p)
318
+ (p) => !basePropNames.has(p as any) && hasOwnProp(baseModel, p)
319
319
  )
320
320
  }
321
321
 
@@ -235,8 +235,5 @@ export function getArrayPaths(
235
235
  * @internal
236
236
  */
237
237
  export function setLazyArrayParentPath(entry: LazyArrayParentPath, node: object): void {
238
- const metadata = treeNodeMetadata.get(node)!
239
- if (metadata.parentPath !== entry) {
240
- metadata.parentPath = entry
241
- }
238
+ treeNodeMetadata.get(node)!.parentPath = entry
242
239
  }
@@ -127,16 +127,6 @@ export function fastGetParent<T extends object = any>(
127
127
  return fastGetParentPath(value, useAtom)?.parent
128
128
  }
129
129
 
130
- /**
131
- * @internal
132
- */
133
- export function fastGetParentIncludingDataObjects<T extends object = any>(
134
- value: object,
135
- useAtom: boolean
136
- ): T | undefined {
137
- return fastGetParentPathIncludingDataObjects(value, useAtom)?.parent
138
- }
139
-
140
130
  /**
141
131
  * Returns if a given object is a model interim data object (`$`).
142
132
  *
@@ -108,36 +108,21 @@ export function internalPatchRecorder(
108
108
  deep: false,
109
109
  })
110
110
 
111
- let onPatchesDisposer: OnPatchesDisposer
112
-
113
- if (subtreeRoot) {
114
- onPatchesDisposer = internalOnPatches(
115
- subtreeRoot,
116
- (p, invP) => {
117
- if (recording && filter(p, invP)) {
118
- events.push({
119
- target: subtreeRoot,
120
- patches: p,
121
- inversePatches: invP,
122
- })
123
- opts?.onPatches?.(p, invP)
124
- }
125
- },
126
- true
127
- )
128
- } else {
129
- onPatchesDisposer = internalOnGlobalPatches((target, p, invP) => {
130
- if (recording && filter(p, invP)) {
131
- events.push({
132
- target,
133
- patches: p,
134
- inversePatches: invP,
135
- })
136
- opts?.onPatches?.(p, invP)
137
- }
138
- }, true)
111
+ const record = (target: object, p: Patch[], invP: Patch[]) => {
112
+ if (recording && filter(p, invP)) {
113
+ events.push({
114
+ target,
115
+ patches: p,
116
+ inversePatches: invP,
117
+ })
118
+ opts?.onPatches?.(p, invP)
119
+ }
139
120
  }
140
121
 
122
+ const onPatchesDisposer: OnPatchesDisposer = subtreeRoot
123
+ ? internalOnPatches(subtreeRoot, (p, invP) => record(subtreeRoot, p, invP), true)
124
+ : internalOnGlobalPatches(record, true)
125
+
141
126
  return {
142
127
  get recording() {
143
128
  return recording
package/src/ref/core.ts CHANGED
@@ -220,29 +220,16 @@ export function resolveId<T extends object>(
220
220
  return resolveRefIdByIndex(root, id, getId) as T | undefined
221
221
  }
222
222
 
223
+ const createRefSet = () => observable.set<Ref<any>>(undefined, { deep: false })
224
+
225
+ const createBackRefs = (): BackRefs<any> => ({ all: createRefSet(), byType: new WeakMap() })
226
+
223
227
  function getBackRefs<T extends object>(
224
228
  target: T,
225
229
  refType?: RefConstructor<T>
226
230
  ): ObservableSet<Ref<T>> {
227
- let backRefs = objectBackRefs.get(target) as BackRefs<T> | undefined
228
- if (!backRefs) {
229
- backRefs = {
230
- all: observable.set(undefined, { deep: false }),
231
- byType: new WeakMap(),
232
- }
233
- objectBackRefs.set(target, backRefs)
234
- }
235
-
236
- if (refType) {
237
- let byType = backRefs.byType.get(refType)
238
- if (!byType) {
239
- byType = observable.set(undefined, { deep: false })
240
- backRefs.byType.set(refType, byType)
241
- }
242
- return byType
243
- } else {
244
- return backRefs.all
245
- }
231
+ const backRefs: BackRefs<T> = getOrCreate(objectBackRefs, target, createBackRefs)
232
+ return refType ? getOrCreate(backRefs.byType, refType, createRefSet) : backRefs.all
246
233
  }
247
234
 
248
235
  /**
@@ -84,9 +84,7 @@ export function isRootStore(node: object): boolean {
84
84
  assertTweakedObject(node, "node")
85
85
 
86
86
  const entry = getOrCreateRootStoreEntry(node)
87
- if (!entry.atom) {
88
- entry.atom = createAtom("rootStore")
89
- }
87
+ entry.atom ??= createAtom("rootStore")
90
88
  entry.atom.reportObserved()
91
89
  return entry.is
92
90
  }
@@ -13,10 +13,8 @@ export type FrozenData<D> = {
13
13
 
14
14
  // snapshot out
15
15
 
16
- // infer is there just to cache type generation
17
-
18
16
  export type SnapshotOutOfObject<T> = {
19
- [k in keyof T]: SnapshotOutOf<T[k]> extends infer R ? R : never
17
+ [k in keyof T]: SnapshotOutOf<T[k]>
20
18
  }
21
19
 
22
20
  export type SnapshotOutOfModel<M extends AnyModel> = ModelToSnapshot<M>
@@ -35,33 +33,22 @@ export interface SnapshotOutOfArraySet<V> {
35
33
  [modelIdKey]: string
36
34
  }
37
35
 
38
- export type SnapshotOutOf<T> =
39
- T extends ObjectMap<infer V>
40
- ? SnapshotOutOfObjectMap<V> extends infer R
41
- ? R
42
- : never
43
- : T extends ArraySet<infer V>
44
- ? SnapshotOutOfArraySet<V> extends infer R
45
- ? R
46
- : never
47
- : T extends AnyModel
48
- ? SnapshotOutOfModel<T> extends infer R
49
- ? R
50
- : never
51
- : T extends Frozen<any>
52
- ? SnapshotOutOfFrozen<T> extends infer R
53
- ? R
54
- : never
55
- : T extends object
56
- ? SnapshotOutOfObject<T> extends infer R
57
- ? R
58
- : never
59
- : T
36
+ export type SnapshotOutOf<T> = T extends object
37
+ ? T extends AnyModel
38
+ ? T extends ObjectMap<infer V>
39
+ ? SnapshotOutOfObjectMap<V>
40
+ : T extends ArraySet<infer V>
41
+ ? SnapshotOutOfArraySet<V>
42
+ : SnapshotOutOfModel<T>
43
+ : T extends Frozen<any>
44
+ ? SnapshotOutOfFrozen<T>
45
+ : SnapshotOutOfObject<T>
46
+ : T
60
47
 
61
48
  // snapshot in
62
49
 
63
50
  export type SnapshotInOfObject<T> = {
64
- [k in keyof T]: SnapshotInOf<T[k]> extends infer R ? R : never
51
+ [k in keyof T]: SnapshotInOf<T[k]>
65
52
  }
66
53
 
67
54
  export type SnapshotInOfModel<M extends AnyModel> = ModelFromSnapshot<M>
@@ -80,25 +67,14 @@ export interface SnapshotInOfArraySet<V> {
80
67
  [modelIdKey]: string
81
68
  }
82
69
 
83
- export type SnapshotInOf<T> =
84
- T extends ObjectMap<infer V>
85
- ? SnapshotInOfObjectMap<V> extends infer R
86
- ? R
87
- : never
88
- : T extends ArraySet<infer V>
89
- ? SnapshotInOfArraySet<V> extends infer R
90
- ? R
91
- : never
92
- : T extends AnyModel
93
- ? SnapshotInOfModel<T> extends infer R
94
- ? R
95
- : never
96
- : T extends Frozen<any>
97
- ? SnapshotInOfFrozen<T> extends infer R
98
- ? R
99
- : never
100
- : T extends object
101
- ? SnapshotInOfObject<T> extends infer R
102
- ? R
103
- : never
104
- : T
70
+ export type SnapshotInOf<T> = T extends object
71
+ ? T extends AnyModel
72
+ ? T extends ObjectMap<infer V>
73
+ ? SnapshotInOfObjectMap<V>
74
+ : T extends ArraySet<infer V>
75
+ ? SnapshotInOfArraySet<V>
76
+ : SnapshotInOfModel<T>
77
+ : T extends Frozen<any>
78
+ ? SnapshotInOfFrozen<T>
79
+ : SnapshotInOfObject<T>
80
+ : T
@@ -33,7 +33,7 @@ import { SnapshotProcessingError } from "./SnapshotProcessingError"
33
33
  */
34
34
  export function applySnapshot<T extends object>(
35
35
  node: T,
36
- snapshot: SnapshotInOf<T> | SnapshotOutOf<T>
36
+ snapshot: NoInfer<SnapshotInOf<T> | SnapshotOutOf<T>>
37
37
  ): void
38
38
 
39
39
  export function applySnapshot(node: object, snapshot: unknown): void {
@@ -1,8 +1,9 @@
1
1
  import { action, observable } from "mobx"
2
2
  import type { AnyModel } from "../model/BaseModel"
3
3
  import { isReservedModelKey } from "../model/metadata"
4
+ import { isTweakedObject } from "../tweaker/core"
4
5
  import { resolveStandardTypeNoThrow, resolveTypeChecker } from "../types/resolveTypeChecker"
5
- import type { AnyType, TypeToData, TypeToSnapshotIn } from "../types/schemas"
6
+ import type { AnyType, TypeToData, TypeToSnapshotIn, TypeToSnapshotOut } from "../types/schemas"
6
7
  import { isLateTypeChecker, TypeChecker } from "../types/TypeChecker"
7
8
  import { resolveCodecSupport } from "../types/utility/typesCodec"
8
9
  import { isMap, isPrimitive, isSet, nodeObservableOptions, setProtoProp } from "../utils"
@@ -60,7 +61,7 @@ export interface FromSnapshotContext {
60
61
  */
61
62
  export function fromSnapshot<TType extends AnyType>(
62
63
  type: TType,
63
- snapshot: TypeToSnapshotIn<TType>,
64
+ snapshot: NoInfer<TypeToSnapshotIn<TType> | TypeToSnapshotOut<TType>>,
64
65
  options?: Partial<FromSnapshotOptions>
65
66
  ): TypeToData<TType>
66
67
 
@@ -122,7 +123,6 @@ const fromSnapshotAction = action(
122
123
  ): T => {
123
124
  const opts = {
124
125
  generateNewIds: false,
125
- overrideRootModelId: undefined,
126
126
  ...options,
127
127
  }
128
128
 
@@ -172,6 +172,13 @@ export function internalFromSnapshot<T>(
172
172
  })
173
173
  }
174
174
 
175
+ if (isTweakedObject(sn, true)) {
176
+ throw new SnapshotProcessingError({
177
+ message: "a snapshot must not contain tree nodes (use getSnapshot to get their snapshots)",
178
+ actualSnapshot: sn,
179
+ })
180
+ }
181
+
175
182
  throw new SnapshotProcessingError({
176
183
  message: "unsupported snapshot",
177
184
  actualSnapshot: sn,
@@ -23,7 +23,7 @@ import type { SnapshotOutOf } from "./SnapshotOf"
23
23
  */
24
24
  export function getSnapshot<T extends AnyType>(
25
25
  type: T,
26
- nodeOrPrimitive: TypeToData<T>
26
+ nodeOrPrimitive: NoInfer<TypeToData<T>>
27
27
  ): TypeToSnapshotOut<T>
28
28
 
29
29
  /**
@@ -113,8 +113,6 @@ export const setNewInternalSnapshot = action(
113
113
  }
114
114
 
115
115
  getOrCreateTreeNodeMetadata(value).snapshot = sn
116
-
117
- sn.atom?.reportChanged()
118
116
  }
119
117
  )
120
118
 
@@ -156,7 +156,9 @@ function reconcileModelSnapshot(
156
156
  if (!isReservedModelKey(k)) {
157
157
  const v = processedSn[k]
158
158
 
159
- const oldValue = data[k]
159
+ // an inherited member such as `toString` is no old value (but `__proto__`
160
+ // still reads the prototype, which is rejected as it is no tree node)
161
+ const oldValue = k === "__proto__" || hasOwnProp(data, k) ? data[k] : undefined
160
162
  // Non-null primitives need neither reconciliation nor detachment.
161
163
  // Nullish values must still pass through default handling below.
162
164
  if (v != null && isPrimitive(v)) {
@@ -216,10 +218,10 @@ function snapshotSlotsAreEqual(
216
218
  for (let i = 0; i < currentKeys.length; i++) {
217
219
  const key = currentKeys[i]
218
220
  // a matching key at the same index proves the key is an own property of
219
- // the incoming snapshot without an Object.hasOwn call; canonical snapshots
221
+ // the incoming snapshot without an own-property check; canonical snapshots
220
222
  // keep key order, so the fallback is rare (never trust inherited values,
221
223
  // e.g. a crafted __proto__ chain, without an own-property proof)
222
- if (incomingKeys[i] !== key && !Object.hasOwn(incomingSnapshot, key)) {
224
+ if (incomingKeys[i] !== key && !hasOwnProp(incomingSnapshot, key)) {
223
225
  return false
224
226
  }
225
227
  if (currentSnapshot[key] !== incomingSnapshot[key]) {