144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
/** @jsxImportSource @opentui/solid */
|
|
import { testRender } from "@opentui/solid"
|
|
import { onMount } from "solid-js"
|
|
import { ArgsProvider } from "../../../../src/cli/cmd/tui/context/args"
|
|
import { ExitProvider } from "../../../../src/cli/cmd/tui/context/exit"
|
|
import { KVProvider, useKV } from "../../../../src/cli/cmd/tui/context/kv"
|
|
import { ProjectProvider, useProject } from "../../../../src/cli/cmd/tui/context/project"
|
|
import { SDKProvider, type EventSource } from "../../../../src/cli/cmd/tui/context/sdk"
|
|
import { SyncProvider, useSync } from "../../../../src/cli/cmd/tui/context/sync"
|
|
import type { GlobalEvent } from "@opencode-ai/sdk/v2"
|
|
|
|
export const worktree = "/tmp/opencode"
|
|
export const directory = `${worktree}/packages/opencode`
|
|
|
|
export async function wait(fn: () => boolean, timeout = 2000) {
|
|
const start = Date.now()
|
|
while (!fn()) {
|
|
if (Date.now() - start > timeout) throw new Error("timed out waiting for condition")
|
|
await Bun.sleep(10)
|
|
}
|
|
}
|
|
|
|
export function json(data: unknown, init?: ResponseInit) {
|
|
return new Response(JSON.stringify(data), {
|
|
...init,
|
|
headers: { "content-type": "application/json", ...(init?.headers ?? {}) },
|
|
})
|
|
}
|
|
|
|
export function eventSource(): EventSource {
|
|
return { subscribe: async () => () => {} }
|
|
}
|
|
|
|
export function createEventSource() {
|
|
let fn: ((event: GlobalEvent) => void) | undefined
|
|
|
|
return {
|
|
source: {
|
|
subscribe: async (handler: (event: GlobalEvent) => void) => {
|
|
fn = handler
|
|
return () => {
|
|
if (fn === handler) fn = undefined
|
|
}
|
|
},
|
|
} satisfies EventSource,
|
|
emit(event: GlobalEvent) {
|
|
if (!fn) throw new Error("event source not ready")
|
|
fn(event)
|
|
},
|
|
}
|
|
}
|
|
|
|
type FetchHandler = (url: URL) => Response | Promise<Response> | undefined
|
|
|
|
export function createFetch(override?: FetchHandler) {
|
|
const session = [] as URL[]
|
|
const fetch = (async (input: RequestInfo | URL) => {
|
|
const url = new URL(input instanceof Request ? input.url : String(input))
|
|
if (url.pathname === "/session") session.push(url)
|
|
|
|
const overridden = await override?.(url)
|
|
if (overridden) return overridden
|
|
|
|
switch (url.pathname) {
|
|
case "/agent":
|
|
case "/command":
|
|
case "/experimental/workspace":
|
|
case "/experimental/workspace/status":
|
|
case "/formatter":
|
|
case "/lsp":
|
|
return json([])
|
|
case "/config":
|
|
case "/experimental/resource":
|
|
case "/mcp":
|
|
case "/provider/auth":
|
|
case "/session/status":
|
|
return json({})
|
|
case "/config/providers":
|
|
return json({ providers: {}, default: {} })
|
|
case "/experimental/console":
|
|
return json({ consoleManagedProviders: [], switchableOrgCount: 0 })
|
|
case "/path":
|
|
return json({ home: "", state: "", config: "", worktree, directory })
|
|
case "/project/current":
|
|
return json({ id: "proj_test" })
|
|
case "/provider":
|
|
return json({ all: [], default: {}, connected: [] })
|
|
case "/session":
|
|
return json([])
|
|
case "/vcs":
|
|
return json({ branch: "main" })
|
|
}
|
|
|
|
throw new Error(`unexpected request: ${url.pathname}`)
|
|
}) as typeof globalThis.fetch
|
|
|
|
return { fetch, session }
|
|
}
|
|
|
|
type Ctx = { kv: ReturnType<typeof useKV>; project: ReturnType<typeof useProject>; sync: ReturnType<typeof useSync> }
|
|
|
|
export async function mount(override?: FetchHandler) {
|
|
const calls = createFetch(override)
|
|
const events = createEventSource()
|
|
let sync!: ReturnType<typeof useSync>
|
|
let project!: ReturnType<typeof useProject>
|
|
let kv!: ReturnType<typeof useKV>
|
|
let done!: () => void
|
|
const ready = new Promise<void>((resolve) => {
|
|
done = resolve
|
|
})
|
|
|
|
function Probe() {
|
|
const ctx: Ctx = { kv: useKV(), project: useProject(), sync: useSync() }
|
|
onMount(() => {
|
|
sync = ctx.sync
|
|
project = ctx.project
|
|
kv = ctx.kv
|
|
done()
|
|
})
|
|
return <box />
|
|
}
|
|
|
|
const app = await testRender(() => (
|
|
<ArgsProvider>
|
|
<ExitProvider>
|
|
<KVProvider>
|
|
<SDKProvider url="http://test" directory={directory} fetch={calls.fetch} events={events.source}>
|
|
<ProjectProvider>
|
|
<SyncProvider>
|
|
<Probe />
|
|
</SyncProvider>
|
|
</ProjectProvider>
|
|
</SDKProvider>
|
|
</KVProvider>
|
|
</ExitProvider>
|
|
</ArgsProvider>
|
|
))
|
|
|
|
await ready
|
|
await wait(() => sync.status === "complete")
|
|
return { app, emit: events.emit, kv, project, sync, session: calls.session }
|
|
}
|