* fix(session-projection): keep released units in checkpoints Since the Cordis rc.11 teardown order unloads the Web layer before the base layer, Session Controller and harness-dev-context release their projection units before their live Sessions detach. The projection cache is still alive at that point and wrote a checkpoint holding only the units that were still registered, replacing the complete record. A blank Session then lost sessionListMetadata and listed as an untitled conversation after every graceful restart. A released unit now stays in the registry as a predecessor: it leaves event drives, snapshots, restores, and change notifications, while checkpoint() keeps each Session's last cell of it without advancing it. Registering the same key again replaces the predecessor and refolds the log. The projection cache keeps replacing whole records. * test(session-projection): assert frozen released units across unload tests Update the remaining unload tests that still expected a released unit to vanish from checkpoint(): user-approval, agent-loop inbox and scope lifecycle, time-context, and the agent registry now assert that the same Session keeps its pre-unload row, that a later event does not advance it, and that a Session created after the unload has no row. The token-meter and goal unload tests also append an event after the unload. Document that a retained handle read still advances a released cell that checkpoint() serves, and that restore() folds only live units, so its refreshed rows omit released units. * docs(session-projection): sync the ProjectionHandle type-equiv block * docs(session-projection): record frozen released units and their upgrade Add the upgrade guide for checkpoint() keeping a released unit's last row, update the registration-handles Agent Note with the frozen-cell decision and the retained predecessor, list both consequences under the registry README's known limitations, note in the projection cache README that a stored row can be frozen at its unit's release, and say that restoreFloor() ignores released predecessors. Update the agent-loop resource-lifecycle unload test, which read checkpoints through an alias, to the frozen-row semantics. * docs(upgrade-guide): mark the released-checkpoint snippet ignore-check * docs(session-projection): state the retained-handle path and narrow a rejected alternative * docs(upgrade-guide): re-record the released-checkpoint guide pairing
30 KiB
Session Projections
English | 中文
The session-projection seam — a capability seam through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry (dsh-session-projection, ctx.sessionProjections), domain contributors (each registering one pure unit), and carriers (dsh-session-controller's history tail page and session/projection push frame). Host state readers require the registry during activation. The framework drives, the domain computes: the registry subscribes to session/event once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the session-projection RFC; drive/cache/feed contracts: the package README.
Source: packages/session/session-projection/src/index.ts
A ProjectionHandle retains its synchronous reader after unregistration. stateOf(session) folds newly committed events with the original unit; it does not rejoin global snapshots or notifications. Registration must still finish before the handle can be used. See the registration lifecycle.
/** An effect-owned registration with a reader retained independently of registration. */
interface ProjectionHandle<S> {
/**
* Read the current state, including after unregistering, by folding new
* Session events with this handle's original unit. Do not mutate the live value.
* After the final release, a read also advances the released cell that
* `checkpoint(session)` serves, until the key registers again.
* @param session - Session whose committed events are folded.
* @returns the projection state at the Session's current cursor.
*/
stateOf(session: Session): S
/**
* Create a reader for one exact Session without reading state or preparing it.
* The reader retains this unit after unregistration and owns no disposal.
* @param session - Session whose committed events the bound reader folds.
* @returns a synchronous callback with the same state and failures as `stateOf(session)`.
*/
readerFor(session: Session): () => S
/**
* Unregister from global drives and snapshots without invalidating this reader.
* @returns a promise that resolves when this registration is released.
*/
dispose(): Promise<void>
}
The compatibility rules define legacy disposal, keyed reads, and their once-per-key deprecation warnings.
ProjectionHandle.readerFor(session) returns a () => S callback for one exact Session. Calling it reads the original unit and preserves borrowed state, readiness requirements and synchronous failures. Owners can compose several readers in a typed object; domain services keep them private. See binding several states.
The unit
ProjectionDefinition<K, S> carries each unit's host state type locally; registration infers S from stateSchema and returns ProjectionHandle<S>. Only client-visible keys merge into SessionProjectionMap, which types their whole values. A wire block makes a unit client-visible, and rendering belongs to the slot system, never this layer:
/**
* One domain's state-driven computation unit: a pure synchronous fold plus
* declarations and an optional client view — never an opaque getter. The framework drives
* `apply` on every committed session event; the domain holds no
* subscriptions and owns only the computation. All functions MUST be
* synchronous (an async unit would tear the carriers' consistency cut), and
* `state` MUST be plain JSON (the persisted-cache precondition).
*/
interface ProjectionDefinition<K extends string, S> {
/** The projection key this unit owns. */
key: K
/** Validates persisted state before it seeds a fold. */
stateSchema: ZodType<S>
/**
* State for the empty log and its immutable Session metadata.
* @param header - immutable metadata for the Session being projected.
* @param inheritedEventCount - exact fork-inherited prefix length.
* @returns the initial state.
*/
init(header: SessionHeader, inheritedEventCount: SessionLogOffset): NoInfer<S>
/**
* Pure transition: previous state + one committed event → next state. A
* unit uninterested in an event MUST return the same state reference — an
* unchanged reference (`Object.is`) produces zero downstream work.
* @param state - the state covering all prior events.
* @param event - the next committed session event.
* @returns the next state (same reference when the event is not the unit's).
*/
apply(state: NoInfer<S>, event: SessionEvent): NoInfer<S>
/** Client view. Omit for host-only units. */
wire?: K extends keyof SessionProjectionMap ? {
/** Validates the wire payload before it leaves the host. */
viewSchema: ZodType<SessionProjectionViewValue<SessionProjectionMap[K]>>
/**
* State → wire payload (the read-side projection). The live drive keeps
* the two latest raw results and compares them with `Object.is`; an
* object-valued view must reuse its reference to suppress publication
* across internal-only state changes.
* @param state - the current state.
* @returns the whole current value for this unit's key.
*/
view(state: NoInfer<S>): SessionProjectionViewValue<SessionProjectionMap[K]>
/**
* Whether a Session summary carries this unit; omitted means `true`.
* Declare `false` on a key whose map entry carries the
* `SessionProjectionSummaryExcluded` mark: the runtime key set follows this
* field, while the client view type follows the mark.
* @default true
*/
summary?: boolean
} : never
/**
* Persisted-cache invalidation version: bump whenever the serialized state fields or the
* fold semantics change, so persisted `(sessionId, key, ver, seq, val)`
* rows from an older unit are discarded instead of being forward-applied
* into garbage. Non-negative integer.
*/
stateVersion: number
}
Every served projection value is a complete read model. A source event may carry a whole value or a domain-owned operation; the unit's deterministic apply owns replay, and checkpoint plus forward tail replay reconstructs the same state.
The snapshot and the change feed
/**
* One consistent read cut over every registered client-visible unit for one session.
* `asOfSeq` is the shared watermark — the seq of the last event every value
* reflects (`-1` for an empty log).
*/
interface ProjectionSnapshot {
/** Seq of the last event the values reflect; -1 for an empty log. */
asOfSeq: SessionSeqCursor
/** Whole current client value per registered key. */
values: Partial<SessionProjectionMap>
}
/**
* Change-feed listener: one unit's raw `view` result changed by `Object.is`
* for one session. `value` is the schema-validated output; `seq` is the
* unit's watermark at emission (the seq of the event that caused the change).
*/
type ProjectionChangeListener = (
session: Session,
key: Extract<keyof SessionProjectionMap, string>,
value: unknown,
seq: SessionSeq,
) => void
/**
* Event-level change-feed listener: one committed event changed the raw `view`
* result of at least one client-visible unit by `Object.is` for one session.
* `changes` holds one entry per such unit in registration order, each with the
* unit's `key` and its schema-validated view as `val`; `seq` is the seq of the
* causing event.
*/
type ProjectionEventChangeListener = (
session: Session,
changes: readonly { readonly key: Extract<keyof SessionProjectionMap, string>; readonly val: unknown }[],
seq: SessionSeq,
) => void
snapshot(session) is fully synchronous: a carrier reads it in the same tick as its page slice, so asOfSeq covers both reads at one sequence number. It returns only client views, and every value passes its unit's viewSchema before return. handle.stateOf(session) reads one live host state without computing unrelated views; callers must not mutate the borrowed reference. A state-reference change computes one cached raw view, and the change feed fires only when that result changes by Object.is; an object-valued view must preserve its reference to suppress publication across internal-only state changes. After every unit has been driven for one committed event, the event-level feed calls each onEventChanged listener once with every client-visible key whose view the event changed; an event that changes no client view makes no event-level call.
Summary reads
A Session summary carries every registered client view except the keys whose SessionProjectionMap entry carries the SessionProjectionSummaryExcluded<T> mark. The mark has no runtime representation: it decides the client view type, while wire.summary decides the runtime key set, with omission meaning true. A key whose entry carries the mark declares wire.summary: false so the two agree; the field is a plain boolean, so the type does not enforce it.
summaryKeys() derives the key set from the live registrations and keeps returning the same instance until a registration or its disposal changes the key table. A summary consumer passes it to the existing key-taking faces — snapshot(session, keys) for live Sessions and viewCheckpoint(rows, keys) for checkpoint rows, both using a set without copying — so there is no summary-specific read face and no second definition of the set.
A list row carries that summary projection, never the full projection: it promises the summary key set and nothing more. snapshot(session, keys) computes only the units it returns. Without a key subset, snapshot computes every client-visible unit and no Host-only unit, and viewCheckpoint serves every wire key, so an opened Session, its follow baseline, and the control baseline carry every projected value; the @ reference reads only its label keys through the same key-taking faces. The registry's cachedSnapshot(session, keys) is deprecated: it returns only values another read already computed and may omit keys.
Summary vocabulary
The declarations below state the summary rule. The mark, its stripping, the excluded-key test, and the summary value type are declared in the pure-type outlet; the key selection both read faces accept is declared in the service module.
/**
* One `SessionProjectionMap` entry excluded from Session summary payloads
* (the Session list row and any other summary read). The mark is type-only;
* `T` keeps its exact runtime value and wire representation. Wrap only the
* excluded contributor's own map entry, and place a nullable entry's `null`
* outside the wrapper (`SessionProjectionSummaryExcluded<Item[]> | null`) so
* the required symbol cannot absorb that member.
*/
type SessionProjectionSummaryExcluded<T> = T & {
readonly [sessionProjectionSummaryExcluded]: true
}
/**
* One map entry's client view value: the summary mark stripped. The
* conditional distributes over a nullable entry's union, so a `| null` member
* written outside the wrapper survives unchanged.
*/
type SessionProjectionViewValue<T> =
T extends SessionProjectionSummaryExcluded<infer Value> ? Value : T
/** Every client view value, keyed by projection key, with no summary mark. */
type SessionProjectionViewValues = {
[K in keyof SessionProjectionMap]: SessionProjectionViewValue<SessionProjectionMap[K]>
}
/**
* Keys a Session summary omits: map entries whose non-nullish value carries
* the summary mark. Nullish members are ignored, so a nullable entry is still
* recognized when its `null` lives outside the wrapper.
*/
type SessionProjectionSummaryExcludedKey = {
[K in keyof SessionProjectionMap]:
NonNullable<SessionProjectionMap[K]> extends SessionProjectionSummaryExcluded<unknown> ? K : never
}[keyof SessionProjectionMap]
/**
* The client values a Session summary carries: every client view value except
* {@link SessionProjectionSummaryExcludedKey}. A summary read returns these
* keys only; an opened Session's full snapshot keeps every view value.
*/
type SessionProjectionSummaryValues =
Omit<SessionProjectionViewValues, SessionProjectionSummaryExcludedKey>
/**
* One read's client-visible key selection: an explicit key list, or a derived
* set such as {@link SessionProjectionRegistry.summaryKeys}. A set is used as
* given; only a list is copied into one.
*/
type ProjectionKeySelection =
| readonly Extract<keyof SessionProjectionMap, string>[]
| ReadonlySet<Extract<keyof SessionProjectionMap, string>>
The registry: ctx.sessionProjections
SessionProjectionRegistry (signatures) owns the drive: one session/event subscription, eager apply over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds init over the in-memory log on first touch (event or read). Domains await register(definition) during activation and retain its handle. Registration belongs to the calling fiber: its last release removes the key from global drives and snapshots, checkpoints keep each Session's last state of it until the key registers again, and retained handles remain readable. Same-key, same-version registrants share one unit; incompatible versions reject registration.
Cordis API
Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — the language sides differ only in locale-specific paired document paths. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.sessionProjectionCache — SessionProjectionCache
The persisted projection cache service. Opens the session_projcache domain at init, checkpoints live sessions on a throttled write-behind (count/interval triggers from Config) plus three mandatory points — session creation, turn/end, and session disposal (the live-to-cold moment) — and serves the cached rows for a session header. Every durable write is fail-soft: failures log a warning and the cache self-heals on the next write.
/**
* The zero-I/O listing read: whole values viewed straight from the stored
* rows (version-matching keys only) of the record bound to the caller's
* lifecycle. The header is the only identity witness a listing holds, so
* this face matches the lifecycle identity (`formatVersion`, `createdAt`,
* `cwd`, `isSeeded`) and not the inherited cut: within one format
* generation the cut is fixed at fork time, so it distinguishes no
* lifecycle the other fields do not, and a viewed value never seeds a fold.
* The view is as stale as the last durable checkpoint but never wrong and
* never from an unrelated log. Its `asOfSeq` is the lowest watermark among
* the served rows: the stored record's own position, which the header
* cannot relate to the log the caller later opens. The Session list
* therefore labels the block as cached, and the client lets every value the
* connected Session produces supersede it whatever this number says.
* @param meta - the listed session's header (identity witness; no log read).
* @param keys - optional projection keys required by the caller's audience.
* @returns the viewed block, or `undefined` when no usable row exists for
* this lifecycle at the current Session format.
*/
cachedSnapshot( meta: SessionHeader, keys?: ProjectionKeySelection, ): ProjectionSnapshot | undefined
/**
* Read only a predecessor checkpoint's title as a zero-I/O listing hint.
*
* The authoritative Session header supplies the lifecycle identity. A cache
* checkpoint can lag that log but cannot lead it because writes flush the
* log first, so a matching predecessor title is a genuine (possibly stale)
* fact from this Session. The registry still requires the current title
* projection's row version and schema. No other predecessor projection is
* exposed: format normalization can change their current meaning, and the
* {@link cachedSnapshot} / hydration paths continue to reject them.
* @param meta - authoritative listed Session header.
* @returns a title-only block at the stored title row's watermark, or
* `undefined` when the record is current, newer, unrelated, missing, or
* incompatible with the title unit.
*/
cachedPredecessorTitle(meta: SessionHeader): ProjectionSnapshot | undefined
/**
* Hydrate projection cells for an already-prepared Session without another
* persistence read. The cache seeds matching rows; the supplied exact log
* advances every unit to the observation cut. No checkpoint is written
* because the logical observation may contain recovery events not yet durable.
* @param session - exact unpublished Session retained by persistence.
* @param events - exact logical event prefix represented by the observation.
* @returns all projection values at the event cut.
*/
hydratePrepared( session: Session, events: readonly SessionEvent[], ): ProjectionSnapshot
/**
* Durably checkpoint one live session NOW (all mandatory points call
* this; tests and carriers may too). The registry cut is snapshotted at
* this boundary (states are live references), then the session's record is
* replaced on the domain's write chain. NOT fail-soft — callers on the
* fail-soft paths contain it.
* @param session - the live session to checkpoint.
* @returns resolution after durability and event emission.
*/
async write(session: Session): Promise<void>
/**
* Cold-read one session's projections from its complete log. Each unit is
* seeded from the identity-checked cached rows — the registry skips `apply`
* for the already-folded prefix (events at or below the row's `seq`) — and
* the refreshed checkpoint is written back (fail-soft, fire-and-forget), so
* the first cold read creates the cache row and later ones seed from it.
* The caller supplies the complete log in seq order: this service never
* consults the persistence layer.
* @param meta - the stored session header (identity witness).
* @param inheritedEventCount - exact inherited prefix length for projection initialization and identity.
* @param events - the session's complete log, in seq order.
* @returns the projection cut at the log end.
*/
coldSnapshot( meta: SessionHeader, inheritedEventCount: SessionLogOffset, events: readonly SessionEvent[], ): ProjectionSnapshot
Types: Session · SessionEvent · SessionHeader · SessionLogOffset
Source: packages/session/session-projection-cache/src/index.ts
ctx.sessionProjections — SessionProjectionRegistry
ctx.sessionProjections: the projection unit table and its drive. The service subscribes to session/event once; every committed event passes every registered unit's apply (eager drive). A changed state reference computes the next client view; the change feed is notified only when its raw result changes by Object.is. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds init over the in-memory log on first touch (event or read). Consumers declare sessionProjections as a required injection, await registration during activation, and retain its ready handle. Registrants sharing a key share one unit and are counted. The last release removes the unit from live drives, restores, and client snapshots, while checkpoints keep each Session's last cell of it until the key registers again and the new unit refolds the log; retained handles still fold new events on demand using the original unit.
/**
* Register one domain's unit on the calling context's fiber. Fiber or handle
* disposal releases one reference; the final release removes live participation.
* Retained handles remain readable after release.
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
* @param definition - key, state schema, pure unit functions, and stateVersion.
* @returns an awaitable yielding a ready handle; the callable runtime value is deprecated.
*/
register<K extends keyof SessionProjectionMap, S>( definition: Omit<ProjectionDefinition<K, S>, 'wire'> & { wire: NonNullable<ProjectionDefinition<K, S>['wire']> }, ): Promise<ProjectionHandle<S>>
/**
* Register one host-only unit. Its state is omitted from client snapshots
* and always checkpointed like every other unit.
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
* @param definition - key, state schema, pure unit functions, and stateVersion.
* @returns an awaitable yielding a ready handle; the callable runtime value is deprecated.
*/
register<K extends string, S>( definition: ProjectionDefinition<K, S> & { key: Exclude<K, keyof SessionProjectionMap> }, ): Promise<ProjectionHandle<S>>
/**
* Subscribe to the change feed. The registration is an effect on the
* calling context's fiber.
* @param listener - called once per client-visible unit whose raw view changed by `Object.is`, per committed event.
* @returns the exact disposer that unsubscribes.
*/
onChanged(listener: ProjectionChangeListener): () => void
/**
* Subscribe to the event-level change feed. The registration is an effect on
* the calling context's fiber. The listener runs synchronously inside the
* committed event's `session/event` handling, once after every unit has been
* driven and after every {@link onChanged} call for that event. If a unit or
* an {@link onChanged} listener throws during that drive, the listener still
* receives the changes made before the throw.
* @param listener - called once per committed event that changed at least one client-visible unit's raw view by `Object.is`.
* @returns the exact disposer that unsubscribes.
*/
onEventChanged(listener: ProjectionEventChangeListener): () => void
/**
* One consistent cut over every registered client-visible unit for one session, read from
* the watermark cache (missing cells fold lazily over the in-memory log).
* Only the returned units are computed: other units, including Host-only
* units without `wire`, are neither folded nor advanced by this read.
* Fully synchronous — every value and `asOfSeq` reflect the same log
* position. Each value passes its unit's `viewSchema` before leaving.
* @param session - the session whose projection values are read.
* @param keys - optional client-visible outputs; only these units are computed.
* @returns the snapshot; `values` is empty when no selected client-visible unit is registered.
*/
snapshot( session: Session, keys?: ProjectionKeySelection, ): ProjectionSnapshot
/**
* The keys a Session summary carries: every registered client-visible key
* whose unit does not declare `summary: false`. The returned set is stable —
* the same instance on every call — until a registration or its disposal
* changes the key table, so a consumer may hold it across rows. Callers pass
* it to a key-taking read face such as `snapshot(session, summaryKeys())`
* or `viewCheckpoint(rows, summaryKeys())`; those faces use the set as-is.
* @returns the current summary key set; do not mutate it.
*/
summaryKeys(): ReadonlySet<Extract<keyof SessionProjectionMap, string>>
/**
* State-level checkpoint of every persisted unit for one session, read
* from the watermark cache (missing cells fold lazily over the in-memory
* log). This is the write side of the persisted projection cache: the
* returned rows are the `(key → {ver, seq, val})` part of the durable
* `(sessionId, key, ver, seq, val)`
* rows. Every `val` is a DETACHED structured clone — never the live
* cell reference: the watermark cache is this registry's authoritative
* mutable state, and a caller reaching the live reference could corrupt
* every subsequent snapshot and frame through it (plain JSON by the unit
* contract, so the clone is total).
* A released unit contributes the Session's last cell. The registry no
* longer advances that cell, while a retained handle read still advances it
* forward, so a writer running after a plugin unloads keeps that unit's
* latest state; a Session that has no cell of it gets no row. {@link restore}
* folds only live units, so its refreshed rows omit released units.
* @param session - the session whose unit states are checkpointed.
* @returns one row per registered key, plus one per released key with a cell for this Session.
*/
checkpoint(session: Session): ProjectionCheckpoint
/**
* The stored seq a {@link restore} tail read over `checkpoint` must start
* at: one event BELOW the lowest usable watermark (a row is usable when
* its `ver` matches the live unit's `stateVersion`; an absent or mismatched row
* pulls the floor to `0` — that key must refold the full log). The
* one-below anchor is load-bearing: the tail then proves how far the
* stored log still extends, so {@link restore} can detect a log that
* shrank below a row's watermark (crash-repair truncation) instead of
* serving the stale row as current — an empty tail read from the anchor
* yields an end below every watermark and the restore rejects for a full
* re-read.
* @param checkpoint - persisted rows for one session (possibly stale or empty).
* @returns the offset for the stored-log suffix read (`SessionHandle.read`),
* or `undefined` when no live unit is registered (released predecessors do not count; no read needed —
* {@link restore} would serve empty values regardless).
*/
restoreFloor(checkpoint: ProjectionCheckpoint): SessionLogOffset | undefined
/**
* View a checkpoint's rows without any log read: for every registered
* client-visible unit whose row's `ver` matches, serve the schema-validated
* `view` of the schema-validated stored state; mismatched, malformed, or absent rows leave their key
* absent (a cold or listing consumer treats it as not-yet-available and a
* fuller read path refolds it). The zero-I/O rung of the read ladder —
* values are as stale as their rows, never wrong.
* @param checkpoint - persisted rows for one session (possibly stale or empty).
* @param keys - optional wire keys to view.
* @returns whole values per key with a usable row; empty when none.
*/
viewCheckpoint( checkpoint: ProjectionCheckpoint, keys?: ProjectionKeySelection, ): Partial<SessionProjectionMap>
/**
* Cold read: fold every persisted unit over a stored log suffix, seeding
* each from its checkpoint row when usable — the one read recipe (cached
* state + forward tail replay + `view`) applied without a live `Session`.
* Call with the stored events at or past `restoreFloor(checkpoint)` (a
* `SessionHandle.read` slice) and that same floor as
* `baseSeq`; the floor's one-below anchor makes the supplied end honest,
* so a shrunk log is detected here. A row is usable iff its
* `ver` matches the live unit's `stateVersion`, it does not predate `baseSeq`
* (`seq >= baseSeq - 1`), and it does not claim events past the
* supplied end (`seq <= endSeq`); an unusable row is discarded
* and its key refolds from `init` — which is only sound over the full
* log, so a discarded row with `baseSeq > 0` throws (the caller re-reads
* from seq 0, e.g. after a crash-repair truncation shrank the log below
* a row's watermark).
* @param checkpoint - persisted rows for one session (possibly stale or empty).
* @param events - the stored events with `seq >= baseSeq`, in seq order.
* @param baseSeq - the seq `events` starts at (its first event's seq when non-empty).
* @param header - immutable metadata for the Session being restored.
* @param inheritedEventCount - exact fork-inherited prefix length supplied to unit initialization.
* @returns the snapshot cut at the supplied log end (`asOfSeq` is the last
* supplied event's seq, `baseSeq - 1` for an empty tail) plus the
* refreshed checkpoint rows at that cut, ready for a durable write-back.
* The rows cover live units only: a released unit has no cell for a log
* folded without a live `Session`, so a write-back of them omits it.
*/
restore( checkpoint: ProjectionCheckpoint, events: readonly SessionEvent[], baseSeq: SessionLogOffset, header: SessionHeader, inheritedEventCount: SessionLogOffset, ): { snapshot: ProjectionSnapshot; checkpoint: ProjectionCheckpoint }
/**
* Restore an exact cut and install its states on the supplied prepared Session.
* A later publication reuses these cells; ordinary live reads and event drive
* advance any constructor-owned suffix exactly once.
* @param session - exact prepared Session that owns the restored log prefix.
* @param checkpoint - persisted rows for this Session lifecycle.
* @param events - exact events at the observation cut.
* @param baseSeq - first supplied event sequence.
* @returns all projection values at the supplied cut.
*/
hydrate( session: Session, checkpoint: ProjectionCheckpoint, events: readonly SessionEvent[], baseSeq: SessionLogOffset, ): ProjectionSnapshot
Types: Session · SessionEvent · SessionHeader · SessionLogOffset