Documentation / @etherfold/processor-entities / syncedThrough
Variable: syncedThrough ​
constsyncedThrough: <ABI>(lastSync,blockNumber) =>LastSync<ABI> =cursorSyncedThrough
Defined in: packages/processor-entities/src/cursor.ts:141
The same cursor, as it stood when only blocks up to blockNumber had been applied.
Why an intermediate cursor exists at all ​
One process(eventStream, lastSync) call can carry MANY blocks, and each of them is its own atomic applyBlock. The lastSync the core hands over describes the END of that stream, so writing it with the FIRST block would put the cursor ahead of the state: a crash there and the restart resumes past blocks nothing ever applied, silently. Writing it only with the LAST block puts the cursor behind the state for the whole of the run, and that is the wedge sync-cursor-write-is-not-atomic-with-the-block-it-describes recorded -- the restart replays a block the store already holds, applyBlock refuses it as the caller bug it normally is, and no number of restarts clears it.
So every block gets the cursor that describes IT, and the state and the cursor move in lockstep, one atomic step per block.
What is truncated, and what deliberately is not ​
lastToBlockbecomesblockNumber, which is the whole point: it is whatgetFromBlockresumes from.unconfirmedBlocksis cut to the ones at or below it. They are ascending, so this is a prefix, and it has to be one: the engine treats the last unconfirmed block as the boundary above which events are new, and an unconfirmed block ABOVE the resume point would make the blocks between them invisible on the next round.latestBlockis NOT truncated. It is the chain tip that was observed, not progress through it, and lowering it would widen the re-fetch window for no reason.contextis not touched: it is the identity the core validates, and it is the same identity whichever block of the stream this is.
The last block of a stream needs none of this and gets the lastSync itself, so the common case (one block, or the tip of a backfill) costs no truncation at all. See applyEventStream.
Why this is a re-export and not an implementation ​
The engine needs the SAME narrowing one level up: promiseToFeed hands each BATCH a cursor the processor persists, and it has to be true on its own for exactly the reason above. Two copies of a rule this subtle is how the halves drift, so it lives once in @etherfold/core as cursorSyncedThrough and is re-exported here under the name this package's callers already use.
The cursor that describes a fold SYNCED THROUGH blockNumber, and the one rule for narrowing a cursor anywhere in this system.
A cursor is not a label, it is a claim that can be resumed from, so every cursor that is PUBLISHED or PERSISTED has to be true on its own. Narrowing one is therefore not "copy it and lower lastToBlock": the unconfirmed window is the set of blocks already folded, and the engine treats the top of it as the boundary above which events are NEW. A window reaching ABOVE lastToBlock makes the blocks in between invisible -- they are neither below the resume point nor above the window, so nothing ever delivers them, and the loss is silent and permanent.
What is truncated, and what deliberately is not ​
lastToBlockbecomesblockNumber, which is the point: it is whatgetFromBlockresumes from.unconfirmedBlocksis cut to the ones at or below it. They are ascending, so this is a prefix, and it has to be one.latestBlockis NOT truncated. It is the chain tip that was observed, not progress through it, and lowering it would widen the re-fetch window for no reason.contextis not touched: it is the identity the core validates, and it is the same identity whichever block of the stream this is.
It lives HERE, in the engine, because two layers need the identical rule and a second implementation is a divergence waiting to happen: the batch loop in promiseToFeed narrows a cursor per BATCH, and applyEventStream narrows one per BLOCK (@etherfold/processor-entities re-exports this as syncedThrough). The last unit of a stream needs no narrowing at all and gets the lastSync itself, so the common case costs nothing.
Type Parameters ​
ABI ​
ABI extends Abi
Parameters ​
lastSync ​
LastSync<ABI>
blockNumber ​
number
Returns ​
LastSync<ABI>