refactor(workspace): centralize adapter invocation (#27768)
Some checks are pending
test / unit (linux) (push) Waiting to run
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404-arm platform_flag:--linux --arm64 target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run

This commit is contained in:
Shoubhit Dash 2026-05-15 23:59:01 +05:30 committed by GitHub
parent 2fdee50b3b
commit 2b0e72ab79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 71 additions and 68 deletions

View file

@ -0,0 +1,51 @@
import { Effect } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
import { getAdapter } from "./adapters"
import type { WorkspaceAdapter, WorkspaceInfo } from "./types"
const context = Effect.gen(function* () {
return {
instance: yield* InstanceRef,
workspaceID: yield* WorkspaceRef,
}
})
export const target = (info: WorkspaceInfo) =>
Effect.gen(function* () {
const adapter = getAdapter(info.projectID, info.type)
const ctx = yield* context
return yield* EffectBridge.fromPromise(() => adapter.target(info, ctx))
})
export const configure = (adapter: WorkspaceAdapter, info: WorkspaceInfo) =>
Effect.gen(function* () {
const ctx = yield* context
return yield* EffectBridge.fromPromise(() => adapter.configure(info, ctx))
})
export const create = (
adapter: WorkspaceAdapter,
info: WorkspaceInfo,
env: Record<string, string | undefined>,
from?: WorkspaceInfo,
) =>
Effect.gen(function* () {
const ctx = yield* context
return yield* EffectBridge.fromPromise(() => adapter.create(info, env, from, ctx))
})
export const list = (adapter: WorkspaceAdapter) =>
Effect.gen(function* () {
const ctx = yield* context
return yield* EffectBridge.fromPromise(() => Promise.resolve(adapter.list?.(ctx) ?? []))
})
export const remove = (info: WorkspaceInfo) =>
Effect.gen(function* () {
const adapter = getAdapter(info.projectID, info.type)
const ctx = yield* context
return yield* EffectBridge.fromPromise(() => adapter.remove(info, ctx))
})
export * as WorkspaceAdapterRuntime from "./workspace-adapter-runtime"

View file

@ -26,11 +26,11 @@ import { SessionID } from "@/session/schema"
import { NotFoundError } from "@/storage/storage"
import { errorData } from "@/util/error"
import { waitEvent } from "./util"
import { EffectBridge } from "@/effect/bridge"
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
import { WorkspaceRef } from "@/effect/instance-ref"
import { Vcs } from "@/project/vcs"
import { InstanceStore } from "@/project/instance-store"
import { InstanceBootstrap } from "@/project/bootstrap"
import { WorkspaceAdapterRuntime } from "./workspace-adapter-runtime"
export const Info = Schema.Struct({
...WorkspaceInfoSchema.fields,
@ -196,50 +196,6 @@ export const layer = Layer.effect(
})
}
const adapterContext = Effect.gen(function* () {
return {
instance: yield* InstanceRef,
workspaceID: yield* WorkspaceRef,
}
})
const adapterTarget = (workspace: Info) =>
Effect.gen(function* () {
const adapter = getAdapter(workspace.projectID, workspace.type)
const context = yield* adapterContext
return yield* EffectBridge.fromPromise(() => adapter.target(workspace, context))
})
const adapterConfigure = (adapter: ReturnType<typeof getAdapter>, info: WorkspaceInfo) =>
Effect.gen(function* () {
const context = yield* adapterContext
return yield* EffectBridge.fromPromise(() => adapter.configure(info, context))
})
const adapterCreate = (
adapter: ReturnType<typeof getAdapter>,
info: WorkspaceInfo,
env: Record<string, string | undefined>,
from?: WorkspaceInfo,
) =>
Effect.gen(function* () {
const context = yield* adapterContext
return yield* EffectBridge.fromPromise(() => adapter.create(info, env, from, context))
})
const adapterList = (adapter: ReturnType<typeof getAdapter>) =>
Effect.gen(function* () {
const context = yield* adapterContext
return yield* EffectBridge.fromPromise(() => Promise.resolve(adapter.list?.(context) ?? []))
})
const adapterRemove = (info: Info, type: string) =>
Effect.gen(function* () {
const adapter = getAdapter(info.projectID, type)
const context = yield* adapterContext
return yield* EffectBridge.fromPromise(() => adapter.remove(info, context))
})
const connectSSE = Effect.fn("Workspace.connectSSE")(function* (
url: URL | string,
headers: HeadersInit | undefined,
@ -325,7 +281,7 @@ export const layer = Layer.effect(
const workspace = yield* get(input.workspaceID)
if (!workspace) return input.fallback
const target = yield* adapterTarget(workspace)
const target = yield* WorkspaceAdapterRuntime.target(workspace)
if (target.type === "local") {
const store = yield* InstanceStore.Service
@ -438,7 +394,7 @@ export const layer = Layer.effect(
})
const syncWorkspaceLoop = Effect.fn("Workspace.syncWorkspaceLoop")(function* (space: Info) {
const target = yield* adapterTarget(space)
const target = yield* WorkspaceAdapterRuntime.target(space)
if (target.type === "local") return
@ -521,7 +477,7 @@ export const layer = Layer.effect(
const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) {
if (!flags.experimentalWorkspaces) return
const target = yield* adapterTarget(space).pipe(
const target = yield* WorkspaceAdapterRuntime.target(space).pipe(
Effect.catch((error) =>
Effect.sync(() => {
setStatus(space.id, "error")
@ -572,7 +528,7 @@ export const layer = Layer.effect(
const create = Effect.fn("Workspace.create")(function* (input: CreateInput) {
const id = WorkspaceID.ascending(input.id)
const adapter = getAdapter(input.projectID, input.type)
const config = yield* adapterConfigure(adapter, {
const config = yield* WorkspaceAdapterRuntime.configure(adapter, {
...input,
id,
name: Slug.create(),
@ -615,7 +571,7 @@ export const layer = Layer.effect(
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
}
yield* adapterCreate(adapter, config, env)
yield* WorkspaceAdapterRuntime.create(adapter, config, env)
yield* Effect.all(
[
waitEvent({
@ -654,7 +610,7 @@ export const layer = Layer.effect(
if (current?.workspaceID) {
const previous = yield* get(current.workspaceID)
if (previous) {
const target = yield* adapterTarget(previous)
const target = yield* WorkspaceAdapterRuntime.target(previous)
if (target.type === "remote") {
yield* syncHistory(previous, target.url, target.headers).pipe(
@ -732,7 +688,7 @@ export const layer = Layer.effect(
workspaceID,
})
const target = yield* adapterTarget(space)
const target = yield* WorkspaceAdapterRuntime.target(space)
if (target.type === "local") {
yield* sync.run(Session.Event.Updated, {
@ -885,16 +841,14 @@ export const layer = Layer.effect(
const discovered = yield* Effect.forEach(
registeredAdapters(project.id),
([type, adapter]) =>
adapter.list
? adapterList(adapter).pipe(
Effect.catchCause((error) =>
Effect.sync(() => {
log.warn("workspace adapter list failed", { type, error })
return []
}),
),
)
: Effect.succeed([]),
WorkspaceAdapterRuntime.list(adapter).pipe(
Effect.catchCause((error) =>
Effect.sync(() => {
log.warn("workspace adapter list failed", { type, error })
return []
}),
),
),
{ concurrency: "unbounded" },
).pipe(Effect.map((items) => items.flat()))
@ -967,7 +921,7 @@ export const layer = Layer.effect(
const info = fromRow(row)
yield* Effect.catchCause(
Effect.gen(function* () {
yield* adapterRemove(info, row.type)
yield* WorkspaceAdapterRuntime.remove(info)
}),
() =>
Effect.sync(() => {

View file

@ -1,8 +1,7 @@
import { getAdapter } from "@/control-plane/adapters"
import { WorkspaceID } from "@/control-plane/schema"
import type { Target } from "@/control-plane/types"
import { Workspace } from "@/control-plane/workspace"
import { EffectBridge } from "@/effect/bridge"
import { WorkspaceAdapterRuntime } from "@/control-plane/workspace-adapter-runtime"
import { Session } from "@/session/session"
import { HttpApiProxy } from "./proxy"
import * as Fence from "@/server/shared/fence"
@ -93,8 +92,7 @@ function missingWorkspaceResponse(id: WorkspaceID): HttpServerResponse.HttpServe
}
function resolveTarget(workspace: Workspace.Info): Effect.Effect<Target> {
const adapter = getAdapter(workspace.projectID, workspace.type)
return EffectBridge.fromPromise(() => adapter.target(workspace))
return WorkspaceAdapterRuntime.target(workspace)
}
function proxyRemote(