Module: lib/core/reactive/journal

The write journal behind Avenx Rewind.

An action marked atomic runs inside a transaction. Every state write it makes — its own, and those of any bridge action it calls — is journaled, and if it fails the journal is played backwards until the state is what it was before the action ran.

Why this can live in the framework rather than in a library

Every mutation of Avenx state passes through one of a handful of proxy traps in proxyHandler.js, and each of those traps already knows the target, the key, the value before and the value after. A journal placed there sees a state.busy = true, a cart.items[2].qty += 1 reached through a bridge, and an items.push(row), without the developer listing any of them.

Cost when nothing is transactional

journal.active is a plain own property, read once per write and compared once. The pattern is copied deliberately from tracer.on: with no transaction open, no descriptor is allocated and nothing else in the reactive system changes shape.

Why a rewind restores through the handler

Putting a value back on the raw target would leave the UI showing the value that was rolled back, because nothing would wake. Restoring through the same ProxyHandlerFactory that recorded the write means a rewind is an ordinary write as far as watchers, the scheduler and the patcher are concerned — the DOM corrects itself with no special case anywhere.

What it deliberately does not do

The journal follows the dynamic extent of the action. A write made inside a .then() continuation runs after the action has already handed back its promise, so the journal never sees it. That is a real limit, it is reported at build time as AVX_W42, and it is not papered over here.

Source:

Classes

JournalFrame
Journal

Members

(static, constant) ConflictPolicy :string

What a rewind does when it finds a value the transaction did not write.

Type:
  • string
Source:

(static, constant) DEFAULT_MAX_SNAPSHOT_ITEMS :number

How many entries a collection may hold before the journal stops snapshotting it.

A push onto a ten-thousand row list would otherwise copy the list. The limit is not a silent truncation: a collection over it marks the frame partial, and the rewind reports what it could not restore.

Type:
  • number
Source:

(static, constant) journal :Journal

The journal every mutation site records through.

Type:
  • Journal
Source:

(inner, constant) ABSENT :symbol

Marks the absence of a value, so that restoring a key that did not exist can be told apart from restoring one whose value was undefined.

Type:
  • symbol
Source:

(inner, nullable) pathResolver :function

How the journal turns a target and key into a readable path.

Type:
  • function
Source:

Methods

(static) setPathResolver(resolver) → {void}

Installs the property-path resolver used in conflict reports.

Set by proxyHandler.js, which already imports getPropertyPath. Keeping it injected rather than imported means the journal does not have to reach into the watcher's naming rules to produce a diagnostic string.

Parameters:
Name Type Description
resolver function

The resolver.

Source:
Returns:
Type
void

(inner) collectionSize(target) → {number}

The size of a journaled collection.

Parameters:
Name Type Description
target object

An array, Map or Set.

Source:
Returns:

Its entry count.

Type
number

(inner) describe(target, key) → {string}

Names a target and key for a conflict report.

The full property path is recovered from the parent chain when it is still intact; a detached object falls back to the bare key rather than reporting a path that no longer describes anything.

Parameters:
Name Type Description
target object

The raw object.

key string | number

The key.

Source:
Returns:

A readable path.

Type
string

(inner) format(value) → {string}

Renders a value for a conflict report.

Parameters:
Name Type Description
value any

The value.

Source:
Returns:

A short, readable form.

Type
string

(inner) restoreCollection(record) → {void}

Puts a collection back and wakes whatever depended on it.

Parameters:
Name Type Description
record object

A collection savepoint.

Source:
Returns:
Type
void

(inner) unwrap(value) → {any}

Unwraps a reactive proxy without going through its get trap.

toRaw() would call track() and attribute the read to whichever watcher is running, which would make journaling change what the application depends on.

Parameters:
Name Type Description
value any

A possibly reactive value.

Source:
Returns:

The raw value.

Type
any