refactor(workspace): centralize adapter invocation (#27768)
This commit is contained in:
@@ -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"
|
||||||
@@ -26,11 +26,11 @@ import { SessionID } from "@/session/schema"
|
|||||||
import { NotFoundError } from "@/storage/storage"
|
import { NotFoundError } from "@/storage/storage"
|
||||||
import { errorData } from "@/util/error"
|
import { errorData } from "@/util/error"
|
||||||
import { waitEvent } from "./util"
|
import { waitEvent } from "./util"
|
||||||
import { EffectBridge } from "@/effect/bridge"
|
import { WorkspaceRef } from "@/effect/instance-ref"
|
||||||
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
|
|
||||||
import { Vcs } from "@/project/vcs"
|
import { Vcs } from "@/project/vcs"
|
||||||
import { InstanceStore } from "@/project/instance-store"
|
import { InstanceStore } from "@/project/instance-store"
|
||||||
import { InstanceBootstrap } from "@/project/bootstrap"
|
import { InstanceBootstrap } from "@/project/bootstrap"
|
||||||
|
import { WorkspaceAdapterRuntime } from "./workspace-adapter-runtime"
|
||||||
|
|
||||||
export const Info = Schema.Struct({
|
export const Info = Schema.Struct({
|
||||||
...WorkspaceInfoSchema.fields,
|
...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* (
|
const connectSSE = Effect.fn("Workspace.connectSSE")(function* (
|
||||||
url: URL | string,
|
url: URL | string,
|
||||||
headers: HeadersInit | undefined,
|
headers: HeadersInit | undefined,
|
||||||
@@ -325,7 +281,7 @@ export const layer = Layer.effect(
|
|||||||
const workspace = yield* get(input.workspaceID)
|
const workspace = yield* get(input.workspaceID)
|
||||||
if (!workspace) return input.fallback
|
if (!workspace) return input.fallback
|
||||||
|
|
||||||
const target = yield* adapterTarget(workspace)
|
const target = yield* WorkspaceAdapterRuntime.target(workspace)
|
||||||
|
|
||||||
if (target.type === "local") {
|
if (target.type === "local") {
|
||||||
const store = yield* InstanceStore.Service
|
const store = yield* InstanceStore.Service
|
||||||
@@ -438,7 +394,7 @@ export const layer = Layer.effect(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const syncWorkspaceLoop = Effect.fn("Workspace.syncWorkspaceLoop")(function* (space: Info) {
|
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
|
if (target.type === "local") return
|
||||||
|
|
||||||
@@ -521,7 +477,7 @@ export const layer = Layer.effect(
|
|||||||
const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) {
|
const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) {
|
||||||
if (!flags.experimentalWorkspaces) return
|
if (!flags.experimentalWorkspaces) return
|
||||||
|
|
||||||
const target = yield* adapterTarget(space).pipe(
|
const target = yield* WorkspaceAdapterRuntime.target(space).pipe(
|
||||||
Effect.catch((error) =>
|
Effect.catch((error) =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
setStatus(space.id, "error")
|
setStatus(space.id, "error")
|
||||||
@@ -572,7 +528,7 @@ export const layer = Layer.effect(
|
|||||||
const create = Effect.fn("Workspace.create")(function* (input: CreateInput) {
|
const create = Effect.fn("Workspace.create")(function* (input: CreateInput) {
|
||||||
const id = WorkspaceID.ascending(input.id)
|
const id = WorkspaceID.ascending(input.id)
|
||||||
const adapter = getAdapter(input.projectID, input.type)
|
const adapter = getAdapter(input.projectID, input.type)
|
||||||
const config = yield* adapterConfigure(adapter, {
|
const config = yield* WorkspaceAdapterRuntime.configure(adapter, {
|
||||||
...input,
|
...input,
|
||||||
id,
|
id,
|
||||||
name: Slug.create(),
|
name: Slug.create(),
|
||||||
@@ -615,7 +571,7 @@ export const layer = Layer.effect(
|
|||||||
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* adapterCreate(adapter, config, env)
|
yield* WorkspaceAdapterRuntime.create(adapter, config, env)
|
||||||
yield* Effect.all(
|
yield* Effect.all(
|
||||||
[
|
[
|
||||||
waitEvent({
|
waitEvent({
|
||||||
@@ -654,7 +610,7 @@ export const layer = Layer.effect(
|
|||||||
if (current?.workspaceID) {
|
if (current?.workspaceID) {
|
||||||
const previous = yield* get(current.workspaceID)
|
const previous = yield* get(current.workspaceID)
|
||||||
if (previous) {
|
if (previous) {
|
||||||
const target = yield* adapterTarget(previous)
|
const target = yield* WorkspaceAdapterRuntime.target(previous)
|
||||||
|
|
||||||
if (target.type === "remote") {
|
if (target.type === "remote") {
|
||||||
yield* syncHistory(previous, target.url, target.headers).pipe(
|
yield* syncHistory(previous, target.url, target.headers).pipe(
|
||||||
@@ -732,7 +688,7 @@ export const layer = Layer.effect(
|
|||||||
workspaceID,
|
workspaceID,
|
||||||
})
|
})
|
||||||
|
|
||||||
const target = yield* adapterTarget(space)
|
const target = yield* WorkspaceAdapterRuntime.target(space)
|
||||||
|
|
||||||
if (target.type === "local") {
|
if (target.type === "local") {
|
||||||
yield* sync.run(Session.Event.Updated, {
|
yield* sync.run(Session.Event.Updated, {
|
||||||
@@ -885,16 +841,14 @@ export const layer = Layer.effect(
|
|||||||
const discovered = yield* Effect.forEach(
|
const discovered = yield* Effect.forEach(
|
||||||
registeredAdapters(project.id),
|
registeredAdapters(project.id),
|
||||||
([type, adapter]) =>
|
([type, adapter]) =>
|
||||||
adapter.list
|
WorkspaceAdapterRuntime.list(adapter).pipe(
|
||||||
? adapterList(adapter).pipe(
|
Effect.catchCause((error) =>
|
||||||
Effect.catchCause((error) =>
|
Effect.sync(() => {
|
||||||
Effect.sync(() => {
|
log.warn("workspace adapter list failed", { type, error })
|
||||||
log.warn("workspace adapter list failed", { type, error })
|
return []
|
||||||
return []
|
}),
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
)
|
|
||||||
: Effect.succeed([]),
|
|
||||||
{ concurrency: "unbounded" },
|
{ concurrency: "unbounded" },
|
||||||
).pipe(Effect.map((items) => items.flat()))
|
).pipe(Effect.map((items) => items.flat()))
|
||||||
|
|
||||||
@@ -967,7 +921,7 @@ export const layer = Layer.effect(
|
|||||||
const info = fromRow(row)
|
const info = fromRow(row)
|
||||||
yield* Effect.catchCause(
|
yield* Effect.catchCause(
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* adapterRemove(info, row.type)
|
yield* WorkspaceAdapterRuntime.remove(info)
|
||||||
}),
|
}),
|
||||||
() =>
|
() =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
|
|||||||
+2
-4
@@ -1,8 +1,7 @@
|
|||||||
import { getAdapter } from "@/control-plane/adapters"
|
|
||||||
import { WorkspaceID } from "@/control-plane/schema"
|
import { WorkspaceID } from "@/control-plane/schema"
|
||||||
import type { Target } from "@/control-plane/types"
|
import type { Target } from "@/control-plane/types"
|
||||||
import { Workspace } from "@/control-plane/workspace"
|
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 { Session } from "@/session/session"
|
||||||
import { HttpApiProxy } from "./proxy"
|
import { HttpApiProxy } from "./proxy"
|
||||||
import * as Fence from "@/server/shared/fence"
|
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> {
|
function resolveTarget(workspace: Workspace.Info): Effect.Effect<Target> {
|
||||||
const adapter = getAdapter(workspace.projectID, workspace.type)
|
return WorkspaceAdapterRuntime.target(workspace)
|
||||||
return EffectBridge.fromPromise(() => adapter.target(workspace))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function proxyRemote(
|
function proxyRemote(
|
||||||
|
|||||||
Reference in New Issue
Block a user