refactor(session): use one surface manager
This commit is contained in:
@@ -16,13 +16,14 @@ import { SESSION_FORMAT_VERSION, SessionId } from './types.ts'
|
||||
import type { ContextEnvelope, CreateSessionOptions, EpochHeader, SessionEvent, SessionEventMap, SessionEventType, SessionHeader, SurfaceIntent, SurfaceEventType } from './types.ts'
|
||||
import { snapshotJsonValue } from './json.ts'
|
||||
import { SurfaceManager } from './surface.ts'
|
||||
import type { SessionSurface } from './surface.ts'
|
||||
import { foldRequestHeader } from './request-header.ts'
|
||||
|
||||
export * from './types.ts'
|
||||
export { isJsonValue, snapshotJsonValue } from './json.ts'
|
||||
export type { JsonValue } from './json.ts'
|
||||
export { interruptedTurnClosers } from './repair.ts'
|
||||
export type { SurfaceFoldReplacement, SurfaceFoldResult } from './surface.ts'
|
||||
export type { SessionSurface, SurfaceFoldReplacement, SurfaceFoldResult } from './surface.ts'
|
||||
export { foldSurface, isSurfaceEvent, isSurfaceEligibleType } from './surface.ts'
|
||||
export { canonicalHeader, foldRequestHeader, headerEquals } from './request-header.ts'
|
||||
|
||||
@@ -251,22 +252,12 @@ export function renderContextContent(
|
||||
*/
|
||||
export class Session {
|
||||
private log: SessionEvent[] = []
|
||||
/** Incremental acceptance state, kept separate from the public lazy view. */
|
||||
private readonly surfaceValidator = new SurfaceManager(this.log)
|
||||
|
||||
/**
|
||||
* Derived surface — a cached order of message-producing event sequences.
|
||||
* Lazily rebuilt from `surfaceOp` markers in the log; processes only new
|
||||
* events (delta) on each access — the log is append-only, so prior events
|
||||
* never change.
|
||||
* Undefined until first accessed (including after fork/seed).
|
||||
*/
|
||||
private _surface: SurfaceManager | undefined
|
||||
/** Single incremental owner of surface acceptance and projection state. */
|
||||
private readonly surfaceManager = new SurfaceManager(this.log)
|
||||
|
||||
/** The ordered surface over this session's event log. */
|
||||
get surface(): SurfaceManager {
|
||||
if (!this._surface) this._surface = new SurfaceManager(this.log)
|
||||
return this._surface
|
||||
get surface(): SessionSurface {
|
||||
return this.surfaceManager
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,7 +300,7 @@ export class Session {
|
||||
// live append and a full-log fold. The candidate is planned before it
|
||||
// enters `log`, so a failure cannot partially mutate the surface.
|
||||
try {
|
||||
this.surfaceValidator.validateNext(snapshot)
|
||||
this.surfaceManager.validateNext(snapshot)
|
||||
} catch (error: unknown) {
|
||||
throw new Error(`invalid seed event at index ${index}: ${error instanceof Error ? error.message : 'invalid surface metadata'}`)
|
||||
}
|
||||
@@ -402,7 +393,7 @@ export class Session {
|
||||
data: dataSnapshot,
|
||||
...(surfaceMetadataSnapshot as { surfaceOp?: unknown; sourceEventSeqs?: unknown }),
|
||||
} as unknown as SessionEvent<T>)
|
||||
this.surfaceValidator.validateNext(event as SessionEvent)
|
||||
this.surfaceManager.validateNext(event as SessionEvent)
|
||||
|
||||
if (entry !== undefined) entry.appending = true
|
||||
try {
|
||||
@@ -468,7 +459,7 @@ export class Session {
|
||||
*
|
||||
* CACHED: each surface node is projected exactly once, when first seen — a
|
||||
* call costs O(new nodes), and a surface rewrite (a `replace`;
|
||||
* {@link SurfaceManager.replaceGeneration}) rebuilds. The returned array is
|
||||
* {@link SessionSurface.replaceGeneration}) rebuilds. The returned array is
|
||||
* a fresh snapshot per call (later appends never grow an array a caller
|
||||
* already holds); the `Message` objects in it are SHARED and **deep-frozen**.
|
||||
* Their content reuses the already frozen durable event data, so the cache
|
||||
@@ -476,8 +467,9 @@ export class Session {
|
||||
* @returns a fresh array of the shared, frozen derived history.
|
||||
*/
|
||||
deriveMessages(): Message[] {
|
||||
const nodes = this.surface.nodes
|
||||
const generation = this.surface.replaceGeneration
|
||||
const surface = this.surface
|
||||
const nodes = surface.nodes
|
||||
const generation = surface.replaceGeneration
|
||||
if (generation !== this.derivedGeneration) {
|
||||
this.derived = []
|
||||
this.derivedNodes = 0
|
||||
|
||||
@@ -55,6 +55,14 @@ export interface SurfaceFoldResult {
|
||||
replacements: SurfaceFoldReplacement[]
|
||||
}
|
||||
|
||||
/** Readonly live projection of the message-producing session events. */
|
||||
export interface SessionSurface {
|
||||
/** Current surface event sequences in model-visible order. */
|
||||
readonly nodes: readonly number[]
|
||||
/** Monotonic count of committed positional replacements. */
|
||||
readonly replaceGeneration: number
|
||||
}
|
||||
|
||||
/** Mutable state shared by complete and incremental folds. */
|
||||
interface SurfaceFoldState {
|
||||
nodes: number[]
|
||||
@@ -244,7 +252,7 @@ export function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult
|
||||
}
|
||||
|
||||
/** Incremental ordered surface view and append-boundary validator. */
|
||||
export class SurfaceManager {
|
||||
export class SurfaceManager implements SessionSurface {
|
||||
/** Shared transition state; replacement history is not retained. */
|
||||
private _state = createFoldState()
|
||||
/** Last processed seq; -1 folds a seeded log on first access. */
|
||||
|
||||
Reference in New Issue
Block a user