research: delete Hono backend (do not merge) (#25667)

This commit is contained in:
Kit Langton
2026-05-09 09:10:42 -04:00
committed by GitHub
parent 32684e70e6
commit 28b03595bf
81 changed files with 224 additions and 7566 deletions

View File

@@ -22,23 +22,21 @@ import { disposeAllInstances, tmpdir } from "../fixture/fixture"
import { it } from "../lib/effect"
const original = {
OPENCODE_EXPERIMENTAL_HTTPAPI: Flag.OPENCODE_EXPERIMENTAL_HTTPAPI,
OPENCODE_SERVER_PASSWORD: Flag.OPENCODE_SERVER_PASSWORD,
OPENCODE_SERVER_USERNAME: Flag.OPENCODE_SERVER_USERNAME,
}
type Backend = "legacy" | "httpapi"
type ServerPath = "default" | "raw"
type Sdk = ReturnType<typeof createOpencodeClient>
type SdkResult = { response: Response; data?: unknown; error?: unknown }
type Captured = { status: number; data?: unknown; error?: unknown }
type ProjectFixture = { sdk: Sdk; directory: string }
type LlmProjectFixture = ProjectFixture & { llm: TestLLMServer["Service"] }
function app(backend: Backend, input?: { password?: string; username?: string }) {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = backend === "httpapi"
function app(serverPath: ServerPath, input?: { password?: string; username?: string }) {
Flag.OPENCODE_SERVER_PASSWORD = input?.password
Flag.OPENCODE_SERVER_USERNAME = input?.username
if (backend === "legacy") return Server.Legacy().app
if (serverPath === "default") return Server.Default().app
const handler = HttpRouter.toWebHandler(
ExperimentalHttpApiServer.routes.pipe(
@@ -62,7 +60,7 @@ function app(backend: Backend, input?: { password?: string; username?: string })
}
function client(
backend: Backend,
serverPath: ServerPath,
directory?: string,
input?: { password?: string; username?: string; headers?: Record<string, string> },
) {
@@ -70,12 +68,12 @@ function client(
baseUrl: "http://localhost",
directory,
headers: input?.headers,
fetch: serverFetch(backend, input),
fetch: serverFetch(serverPath, input),
})
}
function serverFetch(backend: Backend, input?: { password?: string; username?: string }) {
const serverApp = app(backend, input)
function serverFetch(serverPath: ServerPath, input?: { password?: string; username?: string }) {
const serverApp = app(serverPath, input)
return Object.assign(
async (request: RequestInfo | URL, init?: RequestInit) =>
await serverApp.fetch(request instanceof Request ? request : new Request(request, init)),
@@ -194,20 +192,20 @@ function httpapi<A, E>(name: string, effect: Effect.Effect<A, E, Scope.Scope>) {
it.live(name, effect)
}
function parity<A, E>(name: string, scenario: (backend: Backend) => Effect.Effect<A, E, Scope.Scope>) {
function serverPathParity<A, E>(name: string, scenario: (serverPath: ServerPath) => Effect.Effect<A, E, Scope.Scope>) {
it.live(
name,
Effect.gen(function* () {
const legacy = yield* scenario("legacy")
const standard = yield* scenario("default")
yield* resetState()
const httpapi = yield* scenario("httpapi")
expect(httpapi).toEqual(legacy)
const raw = yield* scenario("raw")
expect(raw).toEqual(standard)
}),
)
}
function withProject<A, E, R>(
backend: Backend,
serverPath: ServerPath,
options: { git?: boolean; config?: Partial<Config.Info>; setup?: (dir: string) => Effect.Effect<void> },
run: (input: ProjectFixture) => Effect.Effect<A, E, R>,
) {
@@ -216,30 +214,30 @@ function withProject<A, E, R>(
(tmp) => call(() => tmp[Symbol.asyncDispose]()).pipe(Effect.ignore),
).pipe(
Effect.tap((tmp) => options.setup?.(tmp.path) ?? Effect.void),
Effect.flatMap((tmp) => run({ sdk: client(backend, tmp.path), directory: tmp.path })),
Effect.flatMap((tmp) => run({ sdk: client(serverPath, tmp.path), directory: tmp.path })),
)
}
function withStandardProject<A, E, R>(backend: Backend, run: (input: ProjectFixture) => Effect.Effect<A, E, R>) {
return withProject(backend, { setup: writeStandardFiles }, run)
function withStandardProject<A, E, R>(serverPath: ServerPath, run: (input: ProjectFixture) => Effect.Effect<A, E, R>) {
return withProject(serverPath, { setup: writeStandardFiles }, run)
}
function withFakeLlm<A, E, R>(backend: Backend, run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>) {
function withFakeLlm<A, E, R>(serverPath: ServerPath, run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>) {
return Effect.gen(function* () {
const llm = yield* TestLLMServer
return yield* withProject(backend, { config: providerConfig(llm.url) }, (input) => run({ ...input, llm }))
return yield* withProject(serverPath, { config: providerConfig(llm.url) }, (input) => run({ ...input, llm }))
}).pipe(Effect.provide(TestLLMServer.layer))
}
function withFakeLlmProject<A, E, R>(
backend: Backend,
serverPath: ServerPath,
options: { setup?: (dir: string) => Effect.Effect<void> },
run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>,
) {
return Effect.gen(function* () {
const llm = yield* TestLLMServer
return yield* withProject(
backend,
serverPath,
{
config: providerConfig(llm.url),
setup: options.setup,
@@ -306,7 +304,6 @@ function seedMessage(directory: string, sessionID: string) {
}
afterEach(async () => {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original.OPENCODE_EXPERIMENTAL_HTTPAPI
Flag.OPENCODE_SERVER_PASSWORD = original.OPENCODE_SERVER_PASSWORD
Flag.OPENCODE_SERVER_USERNAME = original.OPENCODE_SERVER_USERNAME
await disposeAllInstances()
@@ -317,7 +314,7 @@ describe("HttpApi SDK", () => {
httpapi(
"uses the generated SDK for global and control routes",
Effect.gen(function* () {
const sdk = client("httpapi")
const sdk = client("raw")
const health = yield* call(() => sdk.global.health())
const log = yield* call(() => sdk.app.log({ service: "httpapi-sdk-test", level: "info", message: "hello" }))
@@ -334,7 +331,7 @@ describe("HttpApi SDK", () => {
httpapi(
"uses the generated SDK for safe instance routes",
withProject("httpapi", { git: false, setup: writeStandardFiles }, ({ sdk }) =>
withProject("raw", { git: false, setup: writeStandardFiles }, ({ sdk }) =>
Effect.gen(function* () {
const file = yield* call(() => sdk.file.read({ path: "hello.txt" }))
const session = yield* call(() => sdk.session.create({ title: "sdk" }))
@@ -357,9 +354,9 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK global and control behavior across backends", (backend) =>
serverPathParity("matches generated SDK global and control behavior", (serverPath) =>
Effect.gen(function* () {
const sdk = client(backend)
const sdk = client(serverPath)
const health = yield* capture(() => sdk.global.health())
const log = yield* capture(() => sdk.app.log({ service: "sdk-parity", level: "info", message: "hello" }))
const invalidAuth = yield* capture(() => sdk.auth.set({ providerID: "test" }))
@@ -372,22 +369,22 @@ describe("HttpApi SDK", () => {
}),
)
parity("matches generated SDK global event stream across backends", (backend) =>
firstEvent(() => client(backend).global.event({ signal: AbortSignal.timeout(1_000) })).pipe(
serverPathParity("matches generated SDK global event stream", (serverPath) =>
firstEvent(() => client(serverPath).global.event({ signal: AbortSignal.timeout(1_000) })).pipe(
Effect.map((event) => ({ type: record(record(event).payload).type })),
),
)
parity("matches generated SDK instance event stream across backends", (backend) =>
withStandardProject(backend, ({ sdk }) =>
serverPathParity("matches generated SDK instance event stream", (serverPath) =>
withStandardProject(serverPath, ({ sdk }) =>
firstEvent(() => sdk.event.subscribe(undefined, { signal: AbortSignal.timeout(1_000) })).pipe(
Effect.map((event) => ({ type: record(record(event).payload).type })),
),
),
)
parity("matches generated SDK missing session errors across backends", (backend) =>
withStandardProject(backend, ({ sdk }) =>
serverPathParity("matches generated SDK missing session errors", (serverPath) =>
withStandardProject(serverPath, ({ sdk }) =>
Effect.gen(function* () {
const sessionID = "ses_missing"
const expected = {
@@ -408,8 +405,8 @@ describe("HttpApi SDK", () => {
),
)
parity("formats missing session validation errors for -s", (backend) =>
withStandardProject(backend, ({ directory }) =>
serverPathParity("formats missing session validation errors for -s", (serverPath) =>
withStandardProject(serverPath, ({ directory }) =>
Effect.gen(function* () {
const sessionID = "ses_206f84f18ffeZ6hhD7pFYAiW5T"
const thrown = yield* captureThrown(() =>
@@ -417,7 +414,7 @@ describe("HttpApi SDK", () => {
url: "http://localhost",
directory,
sessionID,
fetch: serverFetch(backend),
fetch: serverFetch(serverPath),
}),
)
expect(errorMessage(thrown)).toBe(`Session not found: ${sessionID}`)
@@ -426,20 +423,21 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK basic auth behavior across backends", (backend) =>
withStandardProject(backend, ({ directory }) =>
httpapi(
"uses generated SDK basic auth behavior",
withStandardProject("raw", ({ directory }) =>
Effect.gen(function* () {
const missing = yield* capture(() =>
client(backend, directory, { password: "secret" }).file.read({ path: "hello.txt" }),
client("raw", directory, { password: "secret" }).file.read({ path: "hello.txt" }),
)
const bad = yield* capture(() =>
client(backend, directory, {
client("raw", directory, {
password: "secret",
headers: { authorization: authorization("opencode", "wrong") },
}).file.read({ path: "hello.txt" }),
)
const good = yield* capture(() =>
client(backend, directory, {
client("raw", directory, {
password: "secret",
headers: { authorization: authorization("opencode", "secret") },
}).file.read({ path: "hello.txt" }),
@@ -453,8 +451,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK instance read routes across backends", (backend) =>
withStandardProject(backend, ({ sdk, directory }) =>
serverPathParity("matches generated SDK instance read routes", (serverPath) =>
withStandardProject(serverPath, ({ sdk, directory }) =>
Effect.gen(function* () {
const project = yield* capture(() => sdk.project.current())
const projects = yield* capture(() => sdk.project.list())
@@ -504,8 +502,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK session lifecycle routes across backends", (backend) =>
withStandardProject(backend, ({ sdk }) =>
serverPathParity("matches generated SDK session lifecycle routes", (serverPath) =>
withStandardProject(serverPath, ({ sdk }) =>
Effect.gen(function* () {
const parent = yield* capture(() => sdk.session.create({ title: "parent" }))
const parentID = String(record(parent.data).id)
@@ -557,8 +555,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK session message and part routes across backends", (backend) =>
withStandardProject(backend, ({ sdk, directory }) =>
serverPathParity("matches generated SDK session message and part routes", (serverPath) =>
withStandardProject(serverPath, ({ sdk, directory }) =>
Effect.gen(function* () {
const session = yield* capture(() => sdk.session.create({ title: "messages" }))
const sessionID = String(record(session.data).id)
@@ -609,8 +607,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK prompt no-reply routes across backends", (backend) =>
withStandardProject(backend, ({ sdk }) =>
serverPathParity("matches generated SDK prompt no-reply routes", (serverPath) =>
withStandardProject(serverPath, ({ sdk }) =>
Effect.gen(function* () {
const session = yield* capture(() => sdk.session.create({ title: "prompt" }))
const sessionID = String(record(session.data).id)
@@ -646,8 +644,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK prompt streaming through fake LLM across backends", (backend) =>
withFakeLlm(backend, ({ sdk, llm }) =>
serverPathParity("matches generated SDK prompt streaming through fake LLM", (serverPath) =>
withFakeLlm(serverPath, ({ sdk, llm }) =>
Effect.gen(function* () {
yield* llm.text("fake world", { usage: { input: 11, output: 7 } })
const session = yield* capture(() =>
@@ -682,7 +680,7 @@ describe("HttpApi SDK", () => {
httpapi(
"includes project skills in REST API async prompt context",
withFakeLlmProject("httpapi", { setup: writeProjectSkill }, ({ sdk, llm }) =>
withFakeLlmProject("default", { setup: writeProjectSkill }, ({ sdk, llm }) =>
Effect.gen(function* () {
yield* llm.text("skill context ok", { usage: { input: 11, output: 7 } })
const session = yield* capture(() =>
@@ -710,8 +708,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK TUI validation and command routes across backends", (backend) =>
withStandardProject(backend, ({ sdk }) =>
serverPathParity("matches generated SDK TUI validation and command routes", (serverPath) =>
withStandardProject(serverPath, ({ sdk }) =>
Effect.gen(function* () {
const session = yield* capture(() => sdk.session.create({ title: "tui" }))
const sessionID = String(record(session.data).id)
@@ -761,8 +759,8 @@ describe("HttpApi SDK", () => {
),
)
parity("matches generated SDK project git initialization across backends", (backend) =>
withProject(backend, { git: false }, ({ sdk, directory }) =>
serverPathParity("matches generated SDK project git initialization", (serverPath) =>
withProject(serverPath, { git: false }, ({ sdk, directory }) =>
Effect.gen(function* () {
const before = yield* capture(() => sdk.project.current())
const init = yield* capture(() => sdk.project.initGit())