refactor(schema): move session pagination cursors to schema
This commit is contained in:
parent
2cfe8883ea
commit
903acdf0d4
7 changed files with 225 additions and 166 deletions
|
|
@ -1,29 +1,10 @@
|
|||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { Session } from "@opencode-ai/core/session"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { SessionMessagesCursor } from "@opencode-ai/schema/session-messages-cursor"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import { Api } from "../api"
|
||||
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "@opencode-ai/protocol/errors"
|
||||
|
||||
const DefaultMessagesLimit = 50
|
||||
|
||||
const Cursor = Schema.Struct({
|
||||
id: SessionMessage.ID,
|
||||
order: Schema.Union([Schema.Literal("asc"), Schema.Literal("desc")]),
|
||||
direction: Schema.Union([Schema.Literal("previous"), Schema.Literal("next")]),
|
||||
})
|
||||
|
||||
const decodeCursor = Schema.decodeUnknownSync(Cursor)
|
||||
|
||||
const cursor = {
|
||||
encode(message: SessionMessage.Info, order: "asc" | "desc", direction: "previous" | "next") {
|
||||
return Buffer.from(JSON.stringify({ id: message.id, order, direction })).toString("base64url")
|
||||
},
|
||||
decode(input: string) {
|
||||
return decodeCursor(JSON.parse(Buffer.from(input, "base64url").toString("utf8")))
|
||||
},
|
||||
}
|
||||
|
||||
export const MessageHandler = HttpApiBuilder.group(Api, "server.message", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
|
|
@ -33,15 +14,16 @@ export const MessageHandler = HttpApiBuilder.group(Api, "server.message", (handl
|
|||
Effect.fn(function* (ctx) {
|
||||
if (ctx.query.cursor && ctx.query.order !== undefined)
|
||||
return yield* new InvalidCursorError({ message: "Cursor cannot be combined with order" })
|
||||
const decoded = yield* Effect.try({
|
||||
try: () => (ctx.query.cursor ? cursor.decode(ctx.query.cursor) : undefined),
|
||||
catch: () => new InvalidCursorError({ message: "Invalid cursor" }),
|
||||
})
|
||||
const decoded = ctx.query.cursor
|
||||
? yield* SessionMessagesCursor.parse(ctx.query.cursor).pipe(
|
||||
Effect.mapError(() => new InvalidCursorError({ message: "Invalid cursor" })),
|
||||
)
|
||||
: undefined
|
||||
const order = decoded?.order ?? ctx.query.order ?? "desc"
|
||||
const messages = yield* session
|
||||
.messages({
|
||||
sessionID: ctx.params.sessionID,
|
||||
limit: ctx.query.limit ?? DefaultMessagesLimit,
|
||||
limit: ctx.query.limit ?? SessionMessagesCursor.DefaultLimit,
|
||||
order,
|
||||
cursor: decoded ? { id: decoded.id, direction: decoded.direction } : undefined,
|
||||
})
|
||||
|
|
@ -66,14 +48,9 @@ export const MessageHandler = HttpApiBuilder.group(Api, "server.message", (handl
|
|||
)
|
||||
}),
|
||||
)
|
||||
const first = messages[0]
|
||||
const last = messages.at(-1)
|
||||
return {
|
||||
data: messages,
|
||||
cursor: {
|
||||
previous: first ? cursor.encode(first, order, "previous") : undefined,
|
||||
next: last ? cursor.encode(last, order, "next") : undefined,
|
||||
},
|
||||
cursor: SessionMessagesCursor.page(messages, order),
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { InstructionEntry } from "@opencode-ai/core/session/instruction-entry"
|
|||
import { DateTime, Effect, Stream } from "effect"
|
||||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||
import { Api } from "../api"
|
||||
import { SessionsCursor } from "@opencode-ai/protocol/groups/session"
|
||||
import { SessionsQuery } from "@opencode-ai/schema/sessions-query"
|
||||
import {
|
||||
ConflictError,
|
||||
CommandEvaluationError,
|
||||
|
|
@ -19,8 +19,6 @@ import {
|
|||
} from "@opencode-ai/protocol/errors"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
|
||||
const DefaultSessionsLimit = 50
|
||||
|
||||
export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
|
|
@ -31,42 +29,18 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
|
|||
Effect.fn(function* (ctx) {
|
||||
const query =
|
||||
ctx.query.cursor !== undefined
|
||||
? yield* SessionsCursor.parse(ctx.query.cursor).pipe(
|
||||
? yield* SessionsQuery.Cursor.parse(ctx.query.cursor).pipe(
|
||||
Effect.mapError(() => new InvalidCursorError({ message: "Invalid cursor" })),
|
||||
)
|
||||
: ctx.query
|
||||
const page = yield* session.list({
|
||||
...query,
|
||||
workspaceID: query.workspace,
|
||||
limit: ctx.query.limit ?? DefaultSessionsLimit,
|
||||
limit: ctx.query.limit ?? SessionsQuery.DefaultLimit,
|
||||
})
|
||||
const sessions = page.data
|
||||
const first = sessions[0]
|
||||
const last = sessions.at(-1)
|
||||
return {
|
||||
data: sessions,
|
||||
cursor: {
|
||||
previous: first
|
||||
? SessionsCursor.make({
|
||||
...query,
|
||||
anchor: {
|
||||
id: first.id,
|
||||
time: DateTime.toEpochMillis(first.time.updated),
|
||||
direction: "previous",
|
||||
},
|
||||
})
|
||||
: undefined,
|
||||
next: last
|
||||
? SessionsCursor.make({
|
||||
...query,
|
||||
anchor: {
|
||||
id: last.id,
|
||||
time: DateTime.toEpochMillis(last.time.updated),
|
||||
direction: "next",
|
||||
},
|
||||
})
|
||||
: undefined,
|
||||
},
|
||||
data: page.data,
|
||||
cursor: SessionsQuery.Cursor.page(query, page.data),
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue