fix(cli): unify server resolution
This commit is contained in:
parent
6524dfc818
commit
cc686ab8f6
6 changed files with 59 additions and 28 deletions
|
|
@ -147,7 +147,7 @@ const appBindingCommands = [
|
|||
export type TuiInput = {
|
||||
server: {
|
||||
endpoint: Service.Endpoint
|
||||
discover?: () => Promise<Service.Endpoint>
|
||||
reconnect?: (attempt: number) => Promise<Service.Endpoint>
|
||||
reload?: () => Promise<void>
|
||||
}
|
||||
args: Args
|
||||
|
|
@ -200,10 +200,10 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
Effect.tryPromise(() => api.location.get()).pipe(Effect.map((response) => response.directory)),
|
||||
),
|
||||
)
|
||||
const discover = input.server.discover
|
||||
const reconnect = discover
|
||||
? async () => {
|
||||
const endpoint = await discover()
|
||||
const reconnectEndpoint = input.server.reconnect
|
||||
const reconnect = reconnectEndpoint
|
||||
? async (attempt: number) => {
|
||||
const endpoint = await reconnectEndpoint(attempt)
|
||||
const next = { baseUrl: endpoint.url, headers: Service.headers(endpoint) }
|
||||
return {
|
||||
client: createOpencodeClient({ ...next, directory }),
|
||||
|
|
@ -339,7 +339,7 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
<SDKProvider
|
||||
client={createOpencodeClient({ ...options, directory })}
|
||||
api={api}
|
||||
discover={reconnect}
|
||||
reconnect={reconnect}
|
||||
reload={input.server.reload}
|
||||
>
|
||||
<PermissionProvider>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
init: (props: {
|
||||
client: OpencodeClient
|
||||
api: OpenCodeClient
|
||||
discover?: () => Promise<{ client: OpencodeClient; api: OpenCodeClient }>
|
||||
reconnect?: (attempt: number) => Promise<{ client: OpencodeClient; api: OpenCodeClient }>
|
||||
// Stops and starts the managed service; present only in service mode.
|
||||
reload?: () => Promise<void>
|
||||
}) => {
|
||||
|
|
@ -122,8 +122,8 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
// Re-resolve the transport before retrying: the server may have
|
||||
// moved (service restarted on a new port) or need starting. Static
|
||||
// transports (--server, standalone) resolve to the same address.
|
||||
if (props.discover) {
|
||||
const next = await props.discover().catch(() => undefined)
|
||||
if (props.reconnect) {
|
||||
const next = await props.reconnect(attempt).catch(() => undefined)
|
||||
if (abort.signal.aborted || controller.signal.aborted) return
|
||||
if (next) {
|
||||
client = next.client
|
||||
|
|
@ -135,7 +135,7 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
attempt,
|
||||
error: message,
|
||||
})
|
||||
await wait(250, controller.signal)
|
||||
await wait(1_000, controller.signal)
|
||||
}
|
||||
})()
|
||||
return ready
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ function update(version: string): V2Event {
|
|||
}
|
||||
}
|
||||
|
||||
async function mount(discover?: () => Promise<{ client: OpencodeClient; api: OpenCodeClient }>, log?: LogSink) {
|
||||
async function mount(
|
||||
reconnect?: (attempt: number) => Promise<{ client: OpencodeClient; api: OpenCodeClient }>,
|
||||
log?: LogSink,
|
||||
) {
|
||||
const events = createEventStream()
|
||||
const calls = createFetch(undefined, events)
|
||||
const seen: V2Event[] = []
|
||||
|
|
@ -64,7 +67,7 @@ async function mount(discover?: () => Promise<{ client: OpencodeClient; api: Ope
|
|||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts log={log}>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)} discover={discover}>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)} reconnect={reconnect}>
|
||||
<ProjectProvider>
|
||||
<Probe
|
||||
onReady={async (ctx) => {
|
||||
|
|
@ -183,27 +186,28 @@ describe("useEvent", () => {
|
|||
}
|
||||
})
|
||||
|
||||
test("rediscovers the server after the event stream drops", async () => {
|
||||
let calls = 0
|
||||
test("reconnects to the server after the event stream drops", async () => {
|
||||
const attempts: number[] = []
|
||||
const replacementEvents = createEventStream()
|
||||
const replacementCalls = createFetch(undefined, replacementEvents)
|
||||
const replacement = { client: createClient(replacementCalls.fetch), api: createApi(replacementCalls.fetch) }
|
||||
const { app, events, sdk, seen } = await mount(async () => {
|
||||
calls += 1
|
||||
const { app, events, sdk, seen } = await mount(async (attempt) => {
|
||||
attempts.push(attempt)
|
||||
return replacement
|
||||
})
|
||||
|
||||
try {
|
||||
await wait(() => sdk.connection.status() === "connected")
|
||||
// Discovery only runs when the stream is down, never while connected.
|
||||
expect(calls).toBe(0)
|
||||
// Reconnection only runs when the stream is down, never while connected.
|
||||
expect(attempts).toEqual([])
|
||||
events.disconnect()
|
||||
await wait(() => sdk.connection.status() === "connected" && calls > 0)
|
||||
await wait(() => sdk.connection.status() === "connected" && attempts.length > 0)
|
||||
replacementEvents.emit(event(vcs("rediscovered"), { directory: "/tmp/rediscovered" }))
|
||||
await wait(() => seen.some((item) => item.type === "vcs.branch.updated" && item.data.branch === "rediscovered"))
|
||||
|
||||
expect(sdk.client).toBe(replacement.client)
|
||||
expect(sdk.api).toBe(replacement.api)
|
||||
expect(attempts).toEqual([1])
|
||||
const history = sdk.connection.internal.history()
|
||||
expect(history.map((event) => [event.data.status, event.data.attempt])).toEqual([
|
||||
["connecting", 0],
|
||||
|
|
@ -218,7 +222,7 @@ describe("useEvent", () => {
|
|||
}
|
||||
})
|
||||
|
||||
test("keeps the current client when discovery fails", async () => {
|
||||
test("keeps the current client when reconnection fails", async () => {
|
||||
let calls = 0
|
||||
const { app, events, sdk, seen } = await mount(async () => {
|
||||
calls += 1
|
||||
|
|
@ -229,7 +233,7 @@ describe("useEvent", () => {
|
|||
await wait(() => sdk.connection.status() === "connected")
|
||||
const original = sdk.client
|
||||
events.disconnect()
|
||||
// Discovery rejects; the loop retries against the last known transport,
|
||||
// Reconnection rejects; the loop retries against the last known transport,
|
||||
// which succeeds once the fixture accepts the reconnect.
|
||||
await wait(() => calls > 0 && sdk.connection.status() === "connected")
|
||||
events.emit(event(vcs("recovered"), { directory: "/tmp/recovered" }))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue