mobx-keystone 2.1.0 → 2.2.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 (75) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -1
  3. package/dist/mobx-keystone.esm.js +275 -489
  4. package/dist/mobx-keystone.esm.mjs +275 -489
  5. package/dist/mobx-keystone.umd.js +274 -492
  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/index.d.ts +1 -0
  15. package/dist/types/types/schemas.d.ts +41 -37
  16. package/dist/types/types/tProp.d.ts +12 -6
  17. package/dist/types/types/types.d.ts +6 -6
  18. package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
  19. package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
  20. package/dist/types/utils/ModelPool.d.ts +0 -1
  21. package/dist/types/utils/types.d.ts +6 -0
  22. package/dist/types/wrappers/ArraySet.d.ts +1 -0
  23. package/package.json +4 -2
  24. package/src/action/modelFlow.ts +35 -66
  25. package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
  26. package/src/actionMiddlewares/transactionMiddleware.ts +1 -1
  27. package/src/actionMiddlewares/undoMiddleware.ts +16 -22
  28. package/src/context/context.ts +15 -36
  29. package/src/dataModel/utils.ts +5 -28
  30. package/src/deepChange/onDeepChange.ts +44 -126
  31. package/src/model/BaseModel.ts +1 -1
  32. package/src/model/utils.ts +5 -10
  33. package/src/modelShared/modelDecorator.ts +11 -5
  34. package/src/modelShared/prop.ts +115 -111
  35. package/src/modelShared/sharedInternalModel.ts +2 -2
  36. package/src/parent/arrayPaths.ts +1 -4
  37. package/src/parent/path.ts +0 -10
  38. package/src/patch/patchRecorder.ts +13 -28
  39. package/src/ref/core.ts +6 -19
  40. package/src/rootStore/rootStore.ts +1 -3
  41. package/src/snapshot/SnapshotOf.ts +24 -48
  42. package/src/snapshot/applySnapshot.ts +1 -1
  43. package/src/snapshot/fromSnapshot.ts +10 -3
  44. package/src/snapshot/getSnapshot.ts +1 -1
  45. package/src/snapshot/internal.ts +0 -2
  46. package/src/snapshot/reconcileModelSnapshot.ts +5 -3
  47. package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
  48. package/src/treeUtils/draft.ts +6 -1
  49. package/src/treeUtils/sandbox.ts +10 -7
  50. package/src/tweaker/tweakArray.ts +20 -50
  51. package/src/tweaker/tweakPlainObject.ts +9 -15
  52. package/src/types/arrayBased/typesArray.ts +13 -19
  53. package/src/types/arrayBased/typesTuple.ts +16 -25
  54. package/src/types/createCachedTypeCheck.ts +17 -30
  55. package/src/types/index.ts +1 -0
  56. package/src/types/objectBased/typesDataModelData.ts +7 -8
  57. package/src/types/objectBased/typesObject.ts +11 -15
  58. package/src/types/objectBased/typesRecord.ts +11 -15
  59. package/src/types/schemas.ts +59 -71
  60. package/src/types/tProp.ts +29 -29
  61. package/src/types/utility/typeToStoredData.ts +22 -21
  62. package/src/types/utility/typesCodecCore.ts +5 -0
  63. package/src/types/utility/typesCodecSupport.ts +3 -3
  64. package/src/types/utility/typesMap.ts +43 -51
  65. package/src/types/utility/typesRefinement.ts +7 -8
  66. package/src/types/utility/typesSkipCheck.ts +7 -8
  67. package/src/types/utility/typesTag.ts +7 -8
  68. package/src/utils/ModelPool.ts +1 -18
  69. package/src/utils/decorators.ts +5 -5
  70. package/src/utils/index.ts +1 -20
  71. package/src/utils/types.ts +5 -0
  72. package/src/wrappers/ArraySet.ts +27 -14
  73. package/src/wrappers/ObjectMap.ts +1 -6
  74. package/src/wrappers/asMap.ts +1 -21
  75. package/src/wrappers/asSet.ts +0 -8
@@ -209,21 +209,9 @@ function changeWithPath(change: DeepChange, path: Path): DeepChange {
209
209
  * @internal
210
210
  */
211
211
  export function emitDeepChange(obj: object, change: DeepChange): void {
212
- if (!hasAnyDeepChangeListeners()) {
213
- return
212
+ if (hasAnyDeepChangeListeners()) {
213
+ emitLazyDeepChange(obj, () => change)
214
214
  }
215
-
216
- const emission = beginDeepChangeEmission()
217
- try {
218
- attachEmission(change)
219
- emitGlobalDeepChange(obj, change)
220
- if (deepChangeListenerCount > 0) {
221
- emitDeepChangeToListeners(obj, change)
222
- }
223
- } finally {
224
- currentEmission = emission.parent
225
- }
226
- rethrowDeepChangeListenerError(emission)
227
215
  }
228
216
 
229
217
  /**
@@ -308,98 +296,52 @@ function emitDeepChangeFromValues(
308
296
  addedValues?: unknown[],
309
297
  removedValues?: unknown[]
310
298
  ): void {
311
- if (!hasAnyDeepChangeListeners()) {
312
- return
313
- }
314
-
315
- const emission = beginDeepChangeEmission()
316
- try {
317
- emitDeepChangeFromValuesInEmission(
318
- node,
319
- type,
320
- target,
321
- keyOrIndex,
322
- newValue,
323
- oldValue,
324
- addedValues,
325
- removedValues
299
+ if (hasAnyDeepChangeListeners()) {
300
+ emitLazyDeepChange(node, () =>
301
+ buildDeepChange(type, target, keyOrIndex, newValue, oldValue, addedValues, removedValues)
326
302
  )
327
- } finally {
328
- currentEmission = emission.parent
329
303
  }
330
- rethrowDeepChangeListenerError(emission)
331
304
  }
332
305
 
333
- function emitDeepChangeFromValuesInEmission(
334
- node: object,
335
- type: DeepChangeType,
336
- target: object | unknown[],
337
- keyOrIndex: string | number,
338
- newValue: unknown,
339
- oldValue: unknown,
340
- addedValues?: unknown[],
341
- removedValues?: unknown[]
342
- ): void {
343
- let change: DeepChange | undefined
344
-
345
- if (globalDeepChangeListeners.length > 0) {
346
- change = createDeepChange(
347
- type,
348
- target,
349
- keyOrIndex,
350
- newValue,
351
- oldValue,
352
- addedValues,
353
- removedValues
354
- )
355
- emitGlobalDeepChange(node, change)
356
- }
357
-
358
- if (deepChangeListenerCount === 0) {
359
- return
360
- }
306
+ /**
307
+ * Delivers a change to the global listeners and to the listeners of the node
308
+ * and its ancestors. The change is only created if some listener gets it.
309
+ */
310
+ function emitLazyDeepChange(node: object, createChange: () => DeepChange): void {
311
+ const emission = beginDeepChangeEmission()
312
+ try {
313
+ let change: DeepChange | undefined
361
314
 
362
- // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
363
- // at the front (O(depth) each), keeping the whole walk O(depth) instead of
364
- // O(depth^2) on deep trees.
365
- const reversedPrefix: WritablePath = []
366
- let current: object | undefined = node
367
- while (current) {
368
- const listenersForObject = deepChangeListeners.get(current)
369
- if (listenersForObject && listenersForObject.length > 0) {
370
- change ??= createDeepChange(
371
- type,
372
- target,
373
- keyOrIndex,
374
- newValue,
375
- oldValue,
376
- addedValues,
377
- removedValues
378
- )
379
- emitDeepChangeForTarget(current, change, reversedPrefix, reversedPrefix.length)
315
+ if (globalDeepChangeListeners.length > 0) {
316
+ change = attachEmission(createChange())
317
+ emitGlobalDeepChange(node, change)
380
318
  }
381
319
 
382
- const parentPath: ParentPath<object> | undefined = fastGetParentPath(current, false)
383
- if (!parentPath) {
384
- break
320
+ if (deepChangeListenerCount > 0) {
321
+ // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
322
+ // at the front (O(depth) each), keeping the whole walk O(depth) instead of
323
+ // O(depth^2) on deep trees.
324
+ const reversedPrefix: WritablePath = []
325
+ let current: object | undefined = node
326
+ while (current) {
327
+ const listenersForObject = deepChangeListeners.get(current)
328
+ if (listenersForObject && listenersForObject.length > 0) {
329
+ change ??= attachEmission(createChange())
330
+ emitDeepChangeForTarget(listenersForObject, change, reversedPrefix)
331
+ }
332
+
333
+ const parentPath: ParentPath<object> | undefined = fastGetParentPath(current, false)
334
+ if (!parentPath) {
335
+ break
336
+ }
337
+ reversedPrefix.push(parentPath.path)
338
+ current = parentPath.parent
339
+ }
385
340
  }
386
- reversedPrefix.push(parentPath.path)
387
- current = parentPath.parent
341
+ } finally {
342
+ currentEmission = emission.parent
388
343
  }
389
- }
390
-
391
- function createDeepChange(
392
- type: DeepChangeType,
393
- target: object | unknown[],
394
- keyOrIndex: string | number,
395
- newValue: unknown,
396
- oldValue: unknown,
397
- addedValues: unknown[] | undefined,
398
- removedValues: unknown[] | undefined
399
- ): DeepChange {
400
- return attachEmission(
401
- buildDeepChange(type, target, keyOrIndex, newValue, oldValue, addedValues, removedValues)
402
- )
344
+ rethrowDeepChangeListenerError(emission)
403
345
  }
404
346
 
405
347
  function buildDeepChange(
@@ -489,39 +431,15 @@ function rememberDeepChangeListenerError(error: unknown): void {
489
431
  emission.error = addDelayedError(emission.error, error, "multiple deep change listeners failed")
490
432
  }
491
433
 
492
- function emitDeepChangeToListeners(obj: object, change: DeepChange): void {
493
- // Segments are pushed in child-to-root order (O(1) each) rather than unshifted
494
- // at the front (O(depth) each), keeping the whole walk O(depth) instead of
495
- // O(depth^2) on deep trees.
496
- const reversedPrefix: WritablePath = []
497
-
498
- emitDeepChangeForTarget(obj, change, reversedPrefix, 0)
499
-
500
- // and also emit subtree listeners all the way to the root
501
- let parentPath = fastGetParentPath(obj, false)
502
- while (parentPath) {
503
- reversedPrefix.push(parentPath.path)
504
- emitDeepChangeForTarget(parentPath.parent, change, reversedPrefix, reversedPrefix.length)
505
-
506
- parentPath = fastGetParentPath(parentPath.parent, false)
507
- }
508
- }
509
-
510
434
  // `reversedPrefix` holds the path segments from the changed node up to (but not
511
- // including) `obj`, in child-to-root order; the emitted path prepends that slice
512
- // reversed (root-to-child). `prefixCount` is how many leading entries apply.
435
+ // including) the listened node, in child-to-root order; the emitted path
436
+ // prepends it reversed (root-to-child).
513
437
  function emitDeepChangeForTarget(
514
- obj: object,
438
+ listenersForObject: readonly ListenerSubscription<OnDeepChangeListener>[],
515
439
  change: DeepChange,
516
- reversedPrefix: readonly PathElement[],
517
- prefixCount: number
440
+ reversedPrefix: readonly PathElement[]
518
441
  ): void {
519
- const listenersForObject = deepChangeListeners.get(obj)
520
-
521
- if (!listenersForObject || listenersForObject.length === 0) {
522
- return
523
- }
524
-
442
+ const prefixCount = reversedPrefix.length
525
443
  const prefixedChange =
526
444
  prefixCount > 0
527
445
  ? changeWithPath(change, buildPrefixedPath(reversedPrefix, prefixCount, change.path))
@@ -89,7 +89,7 @@ export abstract class BaseModel<
89
89
  * By default it will use the `idProp` if available or return `undefined` otherwise.
90
90
  */
91
91
  getRefId(): string | undefined {
92
- return this[modelIdKey]
92
+ return this[modelIdKey] as string | undefined
93
93
  }
94
94
 
95
95
  /**
@@ -39,15 +39,10 @@ export function assertIsModel(
39
39
  * @internal
40
40
  */
41
41
  export function isModelClass(modelClass: unknown): modelClass is ModelClass<AnyModel> {
42
- if (typeof modelClass !== "function") {
43
- return false
44
- }
45
-
46
- if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
47
- return false
48
- }
49
-
50
- return true
42
+ return (
43
+ typeof modelClass === "function" &&
44
+ (modelClass === BaseModel || modelClass.prototype instanceof BaseModel)
45
+ )
51
46
  }
52
47
 
53
48
  /**
@@ -61,7 +56,7 @@ export function assertIsModelClass(
61
56
  throw failure(`${argName} must be a class`)
62
57
  }
63
58
 
64
- if (modelClass !== BaseModel && !(modelClass.prototype instanceof BaseModel)) {
59
+ if (!isModelClass(modelClass)) {
65
60
  throw failure(`${argName} must extend Model`)
66
61
  }
67
62
  }
@@ -37,11 +37,17 @@ import type { AnyFunction } from "../utils/AnyFunction"
37
37
  */
38
38
  export const model =
39
39
  (name: string) =>
40
- <MC extends ModelClass<AnyModel | AnyDataModel>>(
41
- clazz: MC,
42
- _context?: ClassDecoratorContext<MC>
43
- ): MC =>
44
- internalModel(name, clazz) as any
40
+ <MC extends ModelDecoratorTarget>(clazz: MC, _context?: ClassDecoratorContext<MC>): MC =>
41
+ internalModel(name, clazz as unknown as ModelClass<AnyModel | AnyDataModel>) as any
42
+
43
+ // the model instance type is checked through `prototype` rather than through a typed constructor
44
+ // signature, since comparing constructor signatures would compute the (expensive) model creation data
45
+ // type of every decorated class
46
+ type ModelDecoratorTarget = (new (
47
+ ...args: any
48
+ ) => any) & {
49
+ prototype: AnyModel | AnyDataModel
50
+ }
45
51
 
46
52
  interface AfterClassInitializationData {
47
53
  needsMakeObservable: boolean | undefined
@@ -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
  *