feat(session): enforce SurfaceEvent type
This commit is contained in:
@@ -26,7 +26,7 @@ import { mkdir } from 'node:fs/promises'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { SessionPersistence } from '@deepseek-ai/dsh-session-persistence'
|
||||
import { isJsonValue, interruptedTurnClosers } from '@deepseek-ai/dsh-session'
|
||||
import type { Session, SessionEvent, SessionId, SessionMeta, SessionSummary } from '@deepseek-ai/dsh-session'
|
||||
import type { Session, SessionEvent, SurfaceEventType, SessionId, SessionMeta, SessionSummary } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
openDatabase, rowToMeta, scanRows, type EventRow, type SessionRow,
|
||||
} from './schema.ts'
|
||||
@@ -39,9 +39,10 @@ export { SCHEMA_VERSION } from './schema.ts'
|
||||
* events, events written before surface support).
|
||||
*/
|
||||
function surfaceBindings(event: SessionEvent): [string | null, string | null] {
|
||||
const se = event as SessionEvent<SurfaceEventType>
|
||||
return [
|
||||
event.sourceEventSeqs ? JSON.stringify(event.sourceEventSeqs) : null,
|
||||
event.surfaceOp !== undefined ? JSON.stringify(event.surfaceOp) : null,
|
||||
se.sourceEventSeqs ? JSON.stringify(se.sourceEventSeqs) : null,
|
||||
se.surfaceOp !== undefined ? JSON.stringify(se.surfaceOp) : null,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -126,19 +126,19 @@ export function rowToMeta(row: SessionRow): SessionMeta {
|
||||
|
||||
/** Reconstruct a {@link SessionEvent} from an `events` row (parses `data`). */
|
||||
export function rowToEvent(row: EventRow): SessionEvent {
|
||||
const event = {
|
||||
// Surface-metadata fields are conditional on the event type in the type
|
||||
// system; spread them so each variant gets only the fields it declares.
|
||||
const surfaceFields = {
|
||||
...row.source_event_seqs !== null ? { sourceEventSeqs: JSON.parse(row.source_event_seqs) as number[] } : {},
|
||||
...row.surface_op !== null ? { surfaceOp: JSON.parse(row.surface_op) as SurfaceOp } : {},
|
||||
}
|
||||
return {
|
||||
type: row.type as SessionEvent['type'],
|
||||
seq: row.seq,
|
||||
time: row.time,
|
||||
data: JSON.parse(row.data) as SessionEvent['data'],
|
||||
...surfaceFields,
|
||||
} as SessionEvent
|
||||
if (row.source_event_seqs !== null) {
|
||||
event.sourceEventSeqs = JSON.parse(row.source_event_seqs) as number[]
|
||||
}
|
||||
if (row.surface_op !== null) {
|
||||
event.surfaceOp = JSON.parse(row.surface_op) as SurfaceOp
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import { mkdtemp, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { Session, SessionEvent, SessionMeta } from '@deepseek-ai/dsh-session'
|
||||
import type { Session, SessionEvent, SurfaceEvent, SurfaceEventType, SessionMeta } from '@deepseek-ai/dsh-session'
|
||||
import SessionPersistenceSqlite, { SCHEMA_VERSION } from '@deepseek-ai/dsh-session-persistence-sqlite'
|
||||
import { openDatabase, rowToEvent, scanRows, type EventRow } from '../src/schema.ts'
|
||||
import { runPersistenceContract, meta, oneTurnLog } from '../../session-persistence/tests/contract.ts'
|
||||
@@ -800,8 +800,8 @@ describe('surface field round-trip', () => {
|
||||
surface_op: JSON.stringify('append'),
|
||||
}
|
||||
const event = rowToEvent(row)
|
||||
expect(event.sourceEventSeqs).toEqual([3, 5])
|
||||
expect(event.surfaceOp).toBe('append')
|
||||
expect((event as SurfaceEvent).sourceEventSeqs).toEqual([3, 5])
|
||||
expect((event as SurfaceEvent).surfaceOp).toBe('append')
|
||||
})
|
||||
|
||||
it('rowToEvent handles replace surfaceOp object', () => {
|
||||
@@ -812,8 +812,8 @@ describe('surface field round-trip', () => {
|
||||
surface_op: JSON.stringify({ op: 'replace', start: 0, end: 1 }),
|
||||
}
|
||||
const event = rowToEvent(row)
|
||||
expect(event.sourceEventSeqs).toEqual([0, 1])
|
||||
expect(event.surfaceOp).toEqual({ op: 'replace', start: 0, end: 1 })
|
||||
expect((event as SurfaceEvent).sourceEventSeqs).toEqual([0, 1])
|
||||
expect((event as SurfaceEvent).surfaceOp).toEqual({ op: 'replace', start: 0, end: 1 })
|
||||
})
|
||||
|
||||
it('scanRows with surface columns reconstructs events with surface fields', () => {
|
||||
@@ -827,9 +827,9 @@ describe('surface field round-trip', () => {
|
||||
]
|
||||
const { preserved } = scanRows(rows)
|
||||
expect(preserved).toHaveLength(2)
|
||||
expect(preserved[0]!.surfaceOp).toEqual({ op: 'replace', start: 0, end: 0 })
|
||||
expect(preserved[0]!.sourceEventSeqs).toBeUndefined()
|
||||
expect(preserved[1]!.surfaceOp).toBeUndefined()
|
||||
expect((preserved[0]! as SurfaceEvent).surfaceOp).toEqual({ op: 'replace', start: 0, end: 0 })
|
||||
expect((preserved[0]! as SurfaceEvent).sourceEventSeqs).toBeUndefined()
|
||||
expect((preserved[1] as SessionEvent<SurfaceEventType>).surfaceOp).toBeUndefined()
|
||||
})
|
||||
|
||||
it('append and load round-trips surface fields through SQLite', async () => {
|
||||
@@ -845,11 +845,11 @@ describe('surface field round-trip', () => {
|
||||
const loaded = await ctx.sessionPersistence.load(SessionId('roundtrip-surface'))
|
||||
expect(loaded.events).toHaveLength(4)
|
||||
const um = loaded.events[1]!
|
||||
expect(um.surfaceOp).toBe('append')
|
||||
expect(um.sourceEventSeqs).toBeUndefined()
|
||||
expect((um as SurfaceEvent).surfaceOp).toBe('append')
|
||||
expect((um as SurfaceEvent).sourceEventSeqs).toBeUndefined()
|
||||
const am = loaded.events[2]!
|
||||
expect(am.surfaceOp).toBe('append')
|
||||
expect(am.sourceEventSeqs).toEqual([0])
|
||||
expect((am as SurfaceEvent).surfaceOp).toBe('append')
|
||||
expect((am as SurfaceEvent).sourceEventSeqs).toEqual([0])
|
||||
await fiber.dispose()
|
||||
})
|
||||
|
||||
@@ -863,8 +863,8 @@ describe('surface field round-trip', () => {
|
||||
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
||||
await ctx.parallel('session/flush', session)
|
||||
const loaded = await ctx.sessionPersistence.load(SessionId('surface-noseq'))
|
||||
expect(loaded.events[1]!.surfaceOp).toBe('append')
|
||||
expect(loaded.events[1]!.sourceEventSeqs).toBeUndefined()
|
||||
expect((loaded.events[1]! as SurfaceEvent).surfaceOp).toBe('append')
|
||||
expect((loaded.events[1]! as SurfaceEvent).sourceEventSeqs).toBeUndefined()
|
||||
await fiber.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user