Skip to main content
Version: Current

delta

@rotorsoft/act-root


@rotorsoft/act-root / act-patch/src / delta

Function: delta()

delta<S>(before, after): Readonly<DeepPartial<S>>

Defined in: libs/act-patch/src/delta.ts:56

Compute the smallest DeepPartial<S> describing what changed between before and after. Designed for event payloads โ€” records what the caller put in after, rather than synthesizing patch instructions on the caller's behalf.

How delta reacts to what's in afterโ€‹

What the caller put in after for key kWhat delta returns for kWhat patch does on replay
(key omitted)(key omitted)leaves state[k] alone
explicit null (schema permits)nulldeletes state[k] (null is patch's deletion sentinel)
new valuethe new valuesets state[k]
same reference as before[k](key omitted)leaves state[k] alone

The asymmetry that matters: delta never synthesizes null for a missing key, but it always propagates null when the caller put one in after. So nullable-schema actions can express a deletion as { field: null }, and the deletion travels through delta โ†’ event โ†’ reducer-side patch(state, event_data) all the way to the aggregate. Non-nullable schemas (no .nullable() fields) never see null in the type or at runtime โ€” the konsult case.

Recursionโ€‹

  • Same reference (Object.is) โ†’ omit
  • Both plain objects โ†’ recurse
  • Any other diff (value, type, reference) โ†’ set to after[K]
  • Key in before only (missing in after) โ†’ omit
  • Key in after only, or explicit null in after โ†’ set to after[K] (so an explicit null flows through)

Round-trip propertyโ€‹

patch(before, delta(before, after)) deeply equals after when the key set in after is a superset of the key set in before (or the shrinkages are encoded as explicit nulls in after). If after silently omits keys that were present in before, the dropped key survives the round-trip โ€” delta reads the omission as "no change," not "delete." Express deletion in after (as null) or instruct patch directly (patch(before, { key: null })).

Equality is reference-based, matching patch's structural-sharing model. Two structurally-equal-but-distinct values (e.g. two Date instances with the same getTime(), or two arrays with the same elements) are treated as different and emit a replacement โ€” safe semantically, just slightly less compact.

Type Parametersโ€‹

Sโ€‹

S extends Schema

Parametersโ€‹

beforeโ€‹

Readonly<S>

The original state object

afterโ€‹

Readonly<S>

The desired state object

Returnsโ€‹

Readonly<DeepPartial<S>>

The smallest deep-partial describing what after says changed relative to before. May contain null only when after itself contained null at the same path.