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
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