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
@@ -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]) {
@@ -48,7 +48,9 @@ function reconcilePlainObjectSnapshot(
48
48
  const k = snKeys[i]
49
49
  const v = sn[k]
50
50
 
51
- const oldValue = plainObj[k]
51
+ // an inherited member such as `toString` is no old value (but `__proto__`
52
+ // still reads the prototype, which is rejected as it is no tree node)
53
+ const oldValue = k === "__proto__" || hasOwnProp(plainObj, k) ? plainObj[k] : undefined
52
54
  const newValue = withErrorPathSegment(k, () =>
53
55
  reconcileSnapshot(oldValue, v, modelPool, plainObj)
54
56
  )
@@ -6,7 +6,7 @@ import { applySnapshot } from "../snapshot/applySnapshot"
6
6
  import { fromSnapshot } from "../snapshot/fromSnapshot"
7
7
  import { getSnapshot } from "../snapshot/getSnapshot"
8
8
  import { assertTweakedObject } from "../tweaker/core"
9
- import { failure, mobxAction, mobxComputed } from "../utils"
9
+ import { failure, getMobxVersion, makeObservableCompat, mobxAction, mobxComputed } from "../utils"
10
10
  import { deepEquals } from "./deepEquals"
11
11
 
12
12
  /**
@@ -157,6 +157,11 @@ export class Draft<T extends object> {
157
157
  assertTweakedObject(original, "original")
158
158
 
159
159
  this.originalData = original
160
+
161
+ if (getMobxVersion() >= 6) {
162
+ makeObservableCompat(this)
163
+ }
164
+
160
165
  this.data = fromSnapshot(this.originalSnapshot, { generateNewIds: false })
161
166
  }
162
167
  }
@@ -233,9 +233,11 @@ export class SandboxManager {
233
233
  // see the reverted intermediate state.
234
234
  runInAction(() => {
235
235
  this.allowWrite(() => {
236
- for (let i = events.length - 1; i >= 0; i--) {
237
- applyPatches(this.subtreeRootClone, events[i].inversePatches, true)
238
- }
236
+ applyPatches(
237
+ this.subtreeRootClone,
238
+ events.map((event) => event.inversePatches),
239
+ true
240
+ )
239
241
  })
240
242
  applyPatches(
241
243
  this.subtreeRoot,
@@ -249,10 +251,11 @@ export class SandboxManager {
249
251
  const wasRecording = recorder.recording
250
252
  recorder.recording = false
251
253
  try {
252
- let i = recorder.events.length
253
- while (i-- > numRecorderEvents) {
254
- applyPatches(this.subtreeRootClone, recorder.events[i].inversePatches, true)
255
- }
254
+ applyPatches(
255
+ this.subtreeRootClone,
256
+ recorder.events.slice(numRecorderEvents).map((event) => event.inversePatches),
257
+ true
258
+ )
256
259
  recorder.events.splice(numRecorderEvents)
257
260
  } finally {
258
261
  recorder.recording = wasRecording
@@ -289,7 +289,6 @@ function arrayDidChangeUpdate(
289
289
  ) {
290
290
  const k = change.index
291
291
  const val = change.newValue
292
- const oldVal = oldSnapshot[k]
293
292
  let newVal: any
294
293
  if (isPrimitive(val)) {
295
294
  newVal = val
@@ -299,15 +298,14 @@ function arrayDidChangeUpdate(
299
298
  }
300
299
  const mutate = mutateSet.bind(undefined, k, newVal)
301
300
 
302
- if (shouldEmitPatches || shouldBuildInversePatches) {
301
+ if (shouldBuildInversePatches) {
303
302
  const path = [k]
304
303
  const patches = shouldEmitPatches
305
304
  ? [{ op: "replace" as const, path, value: freezeInternalSnapshot(newVal) }]
306
305
  : undefined
307
- const invPatches = shouldBuildInversePatches
308
- ? [{ op: "replace" as const, path, value: freezeInternalSnapshot(oldVal) }]
309
- : undefined
310
- patchRecorder.record(patches, invPatches)
306
+ patchRecorder.record(patches, [
307
+ { op: "replace" as const, path, value: freezeInternalSnapshot(oldSnapshot[k]) },
308
+ ])
311
309
  }
312
310
  return mutate
313
311
  }
@@ -335,17 +333,16 @@ function arrayDidChangeSplice(
335
333
 
336
334
  const mutate = mutateSplice.bind(undefined, index, removedCount, addedItems)
337
335
 
338
- // With neither patch listeners nor type checking, there is no inverse-patch
339
- // data to build. The snapshot mutation above is all the post-change work we
340
- // need to retain.
341
- if (!shouldEmitPatches && !shouldBuildInversePatches) {
336
+ // With neither patch listeners nor type checking, there is no patch data to
337
+ // build. The snapshot mutation above is all the post-change work we need.
338
+ if (!shouldBuildInversePatches) {
342
339
  return mutate
343
340
  }
344
341
 
345
342
  const oldLen = oldSnapshot.length
346
343
 
347
344
  const patches: Patch[] | undefined = shouldEmitPatches ? [] : undefined
348
- const invPatches: Patch[] | undefined = shouldBuildInversePatches ? [] : undefined
345
+ const invPatches: Patch[] = []
349
346
 
350
347
  // optimization: if we add as many as we remove then remove/readd instead
351
348
 
@@ -357,13 +354,13 @@ function arrayDidChangeSplice(
357
354
 
358
355
  if (addedCount === removedCount) {
359
356
  const readdPatches: Patch[] | undefined = shouldEmitPatches ? [] : undefined
360
- const readdInvPatches: Patch[] | undefined = shouldBuildInversePatches ? [] : undefined
357
+ const readdInvPatches: Patch[] = []
361
358
  let removed = 0
362
359
 
363
360
  for (let i = 0; i < addedCount; i++) {
364
361
  const realIndex = index + i
365
362
 
366
- const newVal = getValueAfterSplice(oldSnapshot, realIndex, index, removedCount, addedItems)
363
+ const newVal = addedItems[i]
367
364
  const oldVal = oldSnapshot[realIndex]
368
365
 
369
366
  if (newVal !== oldVal) {
@@ -372,7 +369,7 @@ function arrayDidChangeSplice(
372
369
  op: "remove",
373
370
  path: removePath,
374
371
  })
375
- invPatches?.push({
372
+ invPatches.push({
376
373
  op: "remove",
377
374
  path: removePath,
378
375
  })
@@ -386,7 +383,7 @@ function arrayDidChangeSplice(
386
383
  value: freezeInternalSnapshot(newVal),
387
384
  })
388
385
 
389
- readdInvPatches?.push({
386
+ readdInvPatches.push({
390
387
  op: "add",
391
388
  path: readdPath,
392
389
  value: freezeInternalSnapshot(oldVal),
@@ -394,17 +391,13 @@ function arrayDidChangeSplice(
394
391
  }
395
392
  }
396
393
 
397
- if (patches && readdPatches && readdPatches.length > 0) {
394
+ if (patches && readdPatches) {
398
395
  for (const patch of readdPatches) patches.push(patch)
399
396
  }
400
- if (invPatches && readdInvPatches && readdInvPatches.length > 0) {
401
- for (const patch of readdInvPatches) invPatches.push(patch)
402
- }
397
+ for (const patch of readdInvPatches) invPatches.push(patch)
403
398
  // We need to reverse once since inverse patches are applied in reverse.
404
399
  // Repeated unshift calls here would make a large splice quadratic.
405
- if (invPatches && invPatches.length > 0) {
406
- invPatches.reverse()
407
- }
400
+ invPatches.reverse()
408
401
  } else {
409
402
  const interimLen = oldLen - removedCount
410
403
 
@@ -433,7 +426,7 @@ function arrayDidChangeSplice(
433
426
  }
434
427
 
435
428
  // add 0, 1, 2... since inverse patches are applied in reverse
436
- invPatches?.push({
429
+ invPatches.push({
437
430
  op: "add",
438
431
  path,
439
432
  value: freezeInternalSnapshot(oldSnapshot[realIndex]),
@@ -446,7 +439,7 @@ function arrayDidChangeSplice(
446
439
  // optimization, for inverse patches, when adding from the end set the length to restore instead
447
440
  const restoreUsingSetLength = index >= interimLen
448
441
  if (restoreUsingSetLength) {
449
- invPatches?.push({
442
+ invPatches.push({
450
443
  op: "replace",
451
444
  path: ["length"],
452
445
  value: interimLen,
@@ -461,14 +454,12 @@ function arrayDidChangeSplice(
461
454
  patches?.push({
462
455
  op: "add",
463
456
  path,
464
- value: freezeInternalSnapshot(
465
- getValueAfterSplice(oldSnapshot, realIndex, index, removedCount, addedItems)
466
- ),
457
+ value: freezeInternalSnapshot(addedItems[i]),
467
458
  })
468
459
 
469
460
  // remove ...2, 1, 0 since inverse patches are applied in reverse
470
461
  if (!restoreUsingSetLength) {
471
- invPatches?.push({
462
+ invPatches.push({
472
463
  op: "remove",
473
464
  path,
474
465
  })
@@ -477,9 +468,7 @@ function arrayDidChangeSplice(
477
468
  }
478
469
  }
479
470
 
480
- if (patches || invPatches) {
481
- patchRecorder.record(patches, invPatches)
482
- }
471
+ patchRecorder.record(patches, invPatches)
483
472
  return mutate
484
473
  }
485
474
 
@@ -676,22 +665,3 @@ class ObservableArrayReplacementQueue {
676
665
  }
677
666
  }
678
667
  }
679
-
680
- function getValueAfterSplice<T>(
681
- array: readonly T[],
682
- i: number,
683
- index: number,
684
- remove: number,
685
- addedItems: readonly T[]
686
- ) {
687
- const base = i - index
688
- if (base < 0) {
689
- return array[i]
690
- }
691
-
692
- if (base < addedItems.length) {
693
- return addedItems[base]
694
- }
695
-
696
- return array[i - addedItems.length + remove]
697
- }
@@ -295,16 +295,14 @@ function objectDidChangeRemove(
295
295
  shouldBuildInversePatches: boolean
296
296
  ) {
297
297
  const k = change.name
298
- const oldVal = oldUntransformedSn[k]
299
298
  const mutate = mutateDelete.bind(undefined, k)
300
299
 
301
- if (shouldEmitPatches || shouldBuildInversePatches) {
300
+ if (shouldBuildInversePatches) {
302
301
  const path = [k as string]
303
302
  const patches = shouldEmitPatches ? [{ op: "remove" as const, path }] : undefined
304
- const invPatches = shouldBuildInversePatches
305
- ? [{ op: "add" as const, path, value: freezeInternalSnapshot(oldVal) }]
306
- : undefined
307
- patchRecorder.record(patches, invPatches)
303
+ patchRecorder.record(patches, [
304
+ { op: "add" as const, path, value: freezeInternalSnapshot(oldUntransformedSn[k]) },
305
+ ])
308
306
  }
309
307
  return mutate
310
308
  }
@@ -318,8 +316,6 @@ function objectDidChangeAddOrUpdate(
318
316
  const k = change.name
319
317
  const val = change.newValue
320
318
 
321
- const oldVal = oldUntransformedSn[k]
322
-
323
319
  let newVal: any
324
320
  if (isPrimitive(val)) {
325
321
  newVal = val
@@ -330,22 +326,20 @@ function objectDidChangeAddOrUpdate(
330
326
 
331
327
  const mutate = mutateSet.bind(undefined, k, newVal)
332
328
 
333
- if (shouldEmitPatches || shouldBuildInversePatches) {
329
+ if (shouldBuildInversePatches) {
334
330
  const path = [k as string]
335
331
  if (change.type === "add") {
336
332
  const patches = shouldEmitPatches
337
333
  ? [{ op: "add" as const, path, value: freezeInternalSnapshot(newVal) }]
338
334
  : undefined
339
- const invPatches = shouldBuildInversePatches ? [{ op: "remove" as const, path }] : undefined
340
- patchRecorder.record(patches, invPatches)
335
+ patchRecorder.record(patches, [{ op: "remove" as const, path }])
341
336
  } else {
342
337
  const patches = shouldEmitPatches
343
338
  ? [{ op: "replace" as const, path, value: freezeInternalSnapshot(newVal) }]
344
339
  : undefined
345
- const invPatches = shouldBuildInversePatches
346
- ? [{ op: "replace" as const, path, value: freezeInternalSnapshot(oldVal) }]
347
- : undefined
348
- patchRecorder.record(patches, invPatches)
340
+ patchRecorder.record(patches, [
341
+ { op: "replace" as const, path, value: freezeInternalSnapshot(oldUntransformedSn[k]) },
342
+ ])
349
343
  }
350
344
  }
351
345
 
@@ -50,6 +50,16 @@ export function typesArray<T extends AnyType>(itemType: T): ArrayType<T[]> {
50
50
  }
51
51
  )
52
52
 
53
+ const processorPlan = snapshotProcessorPlan(
54
+ () => [itemChecker],
55
+ ([itemProcessor]) =>
56
+ itemProcessor
57
+ ? (sn: unknown[]) => {
58
+ return sn.map((item, i) => withErrorPathSegment(i, () => itemProcessor(item)))
59
+ }
60
+ : undefined
61
+ )
62
+
53
63
  const thisTc: TypeChecker = new TypeChecker(
54
64
  TypeCheckerBaseType.Array,
55
65
 
@@ -89,25 +99,9 @@ export function typesArray<T extends AnyType>(itemType: T): ArrayType<T[]> {
89
99
  return thisTc
90
100
  },
91
101
 
92
- snapshotProcessorPlan(
93
- () => [itemChecker],
94
- ([itemProcessor]) =>
95
- itemProcessor
96
- ? (sn: unknown[]) => {
97
- return sn.map((item, i) => withErrorPathSegment(i, () => itemProcessor(item)))
98
- }
99
- : undefined
100
- ),
101
-
102
- snapshotProcessorPlan(
103
- () => [itemChecker],
104
- ([itemProcessor]) =>
105
- itemProcessor
106
- ? (sn: unknown[]) => {
107
- return sn.map((item, i) => withErrorPathSegment(i, () => itemProcessor(item)))
108
- }
109
- : undefined
110
- )
102
+ processorPlan,
103
+
104
+ processorPlan
111
105
  )
112
106
 
113
107
  return thisTc