Documentation / @etherfold/processor-entities / applyEventStream
Function: applyEventStream() ​
applyEventStream<
ABI,ProcessorConfig>(store,processor,eventStream,config,cursor?,report?):Promise<void>
Defined in: packages/processor-entities/src/apply.ts:119
Apply a whole stream to a store, reverting first if any of it is a retraction.
This is the backend-agnostic half of processing, and every backend gets it identically because it is written once, here, against StateStore. The stream is a flat list in which reorged-out events carry removed: true and are followed by the canonical replacements, if there are any. Three things about how that becomes storage calls are load-bearing:
Revert ONCE, at the fork point, not per removed event. The fork point is one below the LOWEST removed block in the stream, and it has to be computed over the whole stream before anything is applied. Reverting per event would issue N reverts where the second is a no-op at best and, if the events were ordered high-to-low, would revert to the wrong height.
A removed event is a removed event, whatever caused it. The engine emits retractions both when a height is replaced by a different hash (a contradiction) and when a block's logs simply vanish with no replacement (an absence: the transaction went back to the mempool). This reads
removedand never looks at what replaced anything, so the second case cannot be missed. Wiring the revert to "a new hash appeared at this height" would reproduced24872fone layer down, where the symptom is a row nobody notices instead of a state object somebody prints.Revert precedes apply, which is also what makes replay safe. A store records a block plainly and a re-applied block raises on purpose.
revertTo(fork)drops every block above the fork, and the canonical events in the same stream are all at or abovefork + 1, so the replacements cannot collide with the branch they replace.
One block is exactly one applyBlock, which is one atomic unit, which is why a handler never has to reason about more than the block it is in.
The cursor rides WITH the block, when one is given.
cursoris theLastSyncthis stream ends at, and each block is applied together with the cursor that describes THAT block (syncedThrough), in the store's own transaction. That is why the cursor lives behind the storage seam: nothing above the store can make the two atomic, and the gap between them is not self-healing in either direction (seecursor.ts). A caller that omits it -- a test, or the conformance workload -- gets the old behaviour and owns its own cursor.A block that was applied is REPORTED, when a reporter is given, and it is reported from HERE because this is where the mutations are. The
Mutationobjects carry the entity name and@etherfold/corehas no mutation vocabulary at all, so the touched-entity set is produced at this layer and RELAYED upward into the signal a reader learns from (ADR-0083). One report perapplyBlockthat RETURNED, after it returned: a block that threw was not applied, and reporting it would tell a reader to re-read for a change that is not there. A block whose handlers produced NO mutation is still reported, with an empty set -- it was applied, the cursor moved with it, and "applied" is the thing being reported.The REVERT is reported too, naming the fork point, and it is reported from here for the same reason: rule 1's fork point is derived HERE, from the
removedmarkers core emitted, and core is where the reader-facing signal is assembled. A reorg WITHDRAWS data, so a channel that reported only rule 5 would leave a reader rendering the branch the chain abandoned. It is reported AFTERrevertToreturned and BEFORE the replacements are applied, which is the order they happened in: a reader told the other way round would re-read the replacement and then be told to throw it away. What it names is the fork point and NOT the rows that moved --revertToanswersvoidat the seam, on purpose, and the token that rotates with this retraction already says invalidate everything.An applied block is reported WITH ITS HASH, in the store's spelling. A reader pins its re-read to the block it was told about (
block: {hash}), and a number cannot name that block across a reorg. The hash is normalised HERE (normalizeBlockHash, the rule every backend applies on write) and not in@etherfold/core, which relays it, because core cannot import the storage seam that owns the rule (ADR-0016). So it equals the hash the query layer reads back out of the store (extensions.blockHash) byte for byte.
Type Parameters ​
ABI ​
ABI extends Abi
ProcessorConfig ​
ProcessorConfig
Parameters ​
store ​
processor ​
EntityProcessor<ABI, ProcessorConfig>
eventStream ​
readonly LogEvent<ABI>[]
config ​
ProcessorConfig
cursor? ​
key ​
string
lastSync ​
LastSync<ABI>
report? ​
Returns ​
Promise<void>