mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
refactor(sync): replace message reloads with incremental tail sync
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ListCodexSessionsRpcResponseSchema } from './apiTypes'
|
||||
import { ListCodexSessionsRpcResponseSchema, MessagesQuerySchema } from './apiTypes'
|
||||
|
||||
describe('ListCodexSessionsRpcResponseSchema', () => {
|
||||
it('preserves Codex session messages when parsing runner RPC responses', () => {
|
||||
@@ -29,3 +29,41 @@ describe('ListCodexSessionsRpcResponseSchema', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('MessagesQuerySchema', () => {
|
||||
it('parses a forward cursor with a bounded snapshot and epoch', () => {
|
||||
expect(MessagesQuerySchema.parse({
|
||||
afterAt: '1000',
|
||||
afterSeq: '10',
|
||||
untilAt: '2000',
|
||||
untilSeq: '20',
|
||||
epoch: '3',
|
||||
limit: '200'
|
||||
})).toEqual({
|
||||
afterAt: 1000,
|
||||
afterSeq: 10,
|
||||
untilAt: 2000,
|
||||
untilSeq: 20,
|
||||
epoch: 3,
|
||||
limit: 200
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects mixed before and after directions', () => {
|
||||
expect(MessagesQuerySchema.safeParse({
|
||||
beforeAt: 1000,
|
||||
beforeSeq: 10,
|
||||
afterAt: 2000,
|
||||
afterSeq: 20
|
||||
}).success).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects an unpaired or unscoped until cursor', () => {
|
||||
expect(MessagesQuerySchema.safeParse({ untilAt: 2000, untilSeq: 20 }).success).toBe(false)
|
||||
expect(MessagesQuerySchema.safeParse({ afterAt: 1000, afterSeq: 10, untilAt: 2000 }).success).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects epoch without a forward cursor', () => {
|
||||
expect(MessagesQuerySchema.safeParse({ epoch: 1 }).success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
+36
-3
@@ -78,9 +78,16 @@ export type SessionResponse = { session: Session }
|
||||
export type MessagesResponse = {
|
||||
messages: DecryptedMessage[]
|
||||
page: {
|
||||
direction: 'latest' | 'before' | 'after'
|
||||
limit: number
|
||||
epoch: number
|
||||
reset: boolean
|
||||
nextBeforeSeq: number | null
|
||||
nextBeforeAt: number | null
|
||||
nextAfterSeq: number | null
|
||||
nextAfterAt: number | null
|
||||
snapshotHeadSeq: number | null
|
||||
snapshotHeadAt: number | null
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
@@ -330,10 +337,36 @@ export const MessagesQuerySchema = z.object({
|
||||
limit: z.coerce.number().int().min(1).max(200).optional(),
|
||||
beforeSeq: z.coerce.number().int().min(1).optional(),
|
||||
beforeAt: z.coerce.number().int().min(0).optional(),
|
||||
}).refine((data) => (data.beforeAt === undefined) === (data.beforeSeq === undefined), {
|
||||
message: 'beforeAt and beforeSeq must be provided together',
|
||||
path: ['beforeAt'],
|
||||
afterSeq: z.coerce.number().int().min(1).optional(),
|
||||
afterAt: z.coerce.number().int().min(0).optional(),
|
||||
untilSeq: z.coerce.number().int().min(1).optional(),
|
||||
untilAt: z.coerce.number().int().min(0).optional(),
|
||||
epoch: z.coerce.number().int().min(0).optional(),
|
||||
})
|
||||
.refine((data) => (data.beforeAt === undefined) === (data.beforeSeq === undefined), {
|
||||
message: 'beforeAt and beforeSeq must be provided together',
|
||||
path: ['beforeAt'],
|
||||
})
|
||||
.refine((data) => (data.afterAt === undefined) === (data.afterSeq === undefined), {
|
||||
message: 'afterAt and afterSeq must be provided together',
|
||||
path: ['afterAt'],
|
||||
})
|
||||
.refine((data) => (data.untilAt === undefined) === (data.untilSeq === undefined), {
|
||||
message: 'untilAt and untilSeq must be provided together',
|
||||
path: ['untilAt'],
|
||||
})
|
||||
.refine((data) => data.beforeAt === undefined || data.afterAt === undefined, {
|
||||
message: 'before and after cursors are mutually exclusive',
|
||||
path: ['afterAt'],
|
||||
})
|
||||
.refine((data) => data.untilAt === undefined || data.afterAt !== undefined, {
|
||||
message: 'until cursor requires an after cursor',
|
||||
path: ['untilAt'],
|
||||
})
|
||||
.refine((data) => data.epoch === undefined || data.afterAt !== undefined, {
|
||||
message: 'epoch requires an after cursor',
|
||||
path: ['epoch'],
|
||||
})
|
||||
|
||||
export type MessagesQuery = z.infer<typeof MessagesQuerySchema>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user