refactor: unwrap Workspace namespace + self-reexport (#22934)
This commit is contained in:
@@ -26,27 +26,26 @@ import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { EventSequenceTable } from "@/sync/event.sql"
|
||||
import { waitEvent } from "./util"
|
||||
|
||||
export namespace Workspace {
|
||||
export const Info = WorkspaceInfo.meta({
|
||||
export const Info = WorkspaceInfo.meta({
|
||||
ref: "Workspace",
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
export const ConnectionStatus = z.object({
|
||||
export const ConnectionStatus = z.object({
|
||||
workspaceID: WorkspaceID.zod,
|
||||
status: z.enum(["connected", "connecting", "disconnected", "error"]),
|
||||
error: z.string().optional(),
|
||||
})
|
||||
export type ConnectionStatus = z.infer<typeof ConnectionStatus>
|
||||
})
|
||||
export type ConnectionStatus = z.infer<typeof ConnectionStatus>
|
||||
|
||||
const Restore = z.object({
|
||||
const Restore = z.object({
|
||||
workspaceID: WorkspaceID.zod,
|
||||
sessionID: SessionID.zod,
|
||||
total: z.number().int().min(0),
|
||||
step: z.number().int().min(0),
|
||||
})
|
||||
})
|
||||
|
||||
export const Event = {
|
||||
export const Event = {
|
||||
Ready: BusEvent.define(
|
||||
"workspace.ready",
|
||||
z.object({
|
||||
@@ -61,9 +60,9 @@ export namespace Workspace {
|
||||
),
|
||||
Restore: BusEvent.define("workspace.restore", Restore),
|
||||
Status: BusEvent.define("workspace.status", ConnectionStatus),
|
||||
}
|
||||
}
|
||||
|
||||
function fromRow(row: typeof WorkspaceTable.$inferSelect): Info {
|
||||
function fromRow(row: typeof WorkspaceTable.$inferSelect): Info {
|
||||
return {
|
||||
id: row.id,
|
||||
type: row.type,
|
||||
@@ -73,17 +72,17 @@ export namespace Workspace {
|
||||
extra: row.extra,
|
||||
projectID: row.project_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CreateInput = z.object({
|
||||
const CreateInput = z.object({
|
||||
id: WorkspaceID.zod.optional(),
|
||||
type: Info.shape.type,
|
||||
branch: Info.shape.branch,
|
||||
projectID: ProjectID.zod,
|
||||
extra: Info.shape.extra,
|
||||
})
|
||||
})
|
||||
|
||||
export const create = fn(CreateInput, async (input) => {
|
||||
export const create = fn(CreateInput, async (input) => {
|
||||
const id = WorkspaceID.ascending(input.id)
|
||||
const adaptor = await getAdaptor(input.projectID, input.type)
|
||||
|
||||
@@ -134,14 +133,14 @@ export namespace Workspace {
|
||||
})
|
||||
|
||||
return info
|
||||
})
|
||||
})
|
||||
|
||||
const SessionRestoreInput = z.object({
|
||||
const SessionRestoreInput = z.object({
|
||||
workspaceID: WorkspaceID.zod,
|
||||
sessionID: SessionID.zod,
|
||||
})
|
||||
})
|
||||
|
||||
export const sessionRestore = fn(SessionRestoreInput, async (input) => {
|
||||
export const sessionRestore = fn(SessionRestoreInput, async (input) => {
|
||||
log.info("session restore requested", {
|
||||
workspaceID: input.workspaceID,
|
||||
sessionID: input.sessionID,
|
||||
@@ -292,9 +291,9 @@ export namespace Workspace {
|
||||
})
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export function list(project: Project.Info) {
|
||||
export function list(project: Project.Info) {
|
||||
const rows = Database.use((db) =>
|
||||
db.select().from(WorkspaceTable).where(eq(WorkspaceTable.project_id, project.id)).all(),
|
||||
)
|
||||
@@ -302,22 +301,22 @@ export namespace Workspace {
|
||||
|
||||
for (const space of spaces) startSync(space)
|
||||
return spaces
|
||||
}
|
||||
}
|
||||
|
||||
function lookup(id: WorkspaceID) {
|
||||
function lookup(id: WorkspaceID) {
|
||||
const row = Database.use((db) => db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, id)).get())
|
||||
if (!row) return
|
||||
return fromRow(row)
|
||||
}
|
||||
}
|
||||
|
||||
export const get = fn(WorkspaceID.zod, async (id) => {
|
||||
export const get = fn(WorkspaceID.zod, async (id) => {
|
||||
const space = lookup(id)
|
||||
if (!space) return
|
||||
startSync(space)
|
||||
return space
|
||||
})
|
||||
})
|
||||
|
||||
export const remove = fn(WorkspaceID.zod, async (id) => {
|
||||
export const remove = fn(WorkspaceID.zod, async (id) => {
|
||||
const sessions = Database.use((db) =>
|
||||
db.select({ id: SessionTable.id }).from(SessionTable).where(eq(SessionTable.workspace_id, id)).all(),
|
||||
)
|
||||
@@ -340,13 +339,13 @@ export namespace Workspace {
|
||||
Database.use((db) => db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run())
|
||||
return info
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const connections = new Map<WorkspaceID, ConnectionStatus>()
|
||||
const aborts = new Map<WorkspaceID, AbortController>()
|
||||
const TIMEOUT = 5000
|
||||
const connections = new Map<WorkspaceID, ConnectionStatus>()
|
||||
const aborts = new Map<WorkspaceID, AbortController>()
|
||||
const TIMEOUT = 5000
|
||||
|
||||
function setStatus(id: WorkspaceID, status: ConnectionStatus["status"], error?: string) {
|
||||
function setStatus(id: WorkspaceID, status: ConnectionStatus["status"], error?: string) {
|
||||
const prev = connections.get(id)
|
||||
if (prev?.status === status && prev?.error === error) return
|
||||
const next = { workspaceID: id, status, error }
|
||||
@@ -364,13 +363,13 @@ export namespace Workspace {
|
||||
properties: next,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function status(): ConnectionStatus[] {
|
||||
export function status(): ConnectionStatus[] {
|
||||
return [...connections.values()]
|
||||
}
|
||||
}
|
||||
|
||||
function synced(state: Record<string, number>) {
|
||||
function synced(state: Record<string, number>) {
|
||||
const ids = Object.keys(state)
|
||||
if (ids.length === 0) return true
|
||||
|
||||
@@ -390,13 +389,13 @@ export namespace Workspace {
|
||||
return ids.every((id) => {
|
||||
return (done[id] ?? -1) >= state[id]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function isSyncing(workspaceID: WorkspaceID) {
|
||||
export async function isSyncing(workspaceID: WorkspaceID) {
|
||||
return aborts.has(workspaceID)
|
||||
}
|
||||
}
|
||||
|
||||
export async function waitForSync(workspaceID: WorkspaceID, state: Record<string, number>, signal?: AbortSignal) {
|
||||
export async function waitForSync(workspaceID: WorkspaceID, state: Record<string, number>, signal?: AbortSignal) {
|
||||
if (synced(state)) return
|
||||
|
||||
try {
|
||||
@@ -414,19 +413,19 @@ export namespace Workspace {
|
||||
if (signal?.aborted) throw signal.reason ?? new Error("Request aborted")
|
||||
throw new Error(`Timed out waiting for sync fence: ${JSON.stringify(state)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const log = Log.create({ service: "workspace-sync" })
|
||||
const log = Log.create({ service: "workspace-sync" })
|
||||
|
||||
function route(url: string | URL, path: string) {
|
||||
function route(url: string | URL, path: string) {
|
||||
const next = new URL(url)
|
||||
next.pathname = `${next.pathname.replace(/\/$/, "")}${path}`
|
||||
next.search = ""
|
||||
next.hash = ""
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
async function syncWorkspace(space: Info, signal: AbortSignal) {
|
||||
async function syncWorkspace(space: Info, signal: AbortSignal) {
|
||||
while (!signal.aborted) {
|
||||
log.info("connecting to global sync", { workspace: space.name })
|
||||
setStatus(space.id, "connecting")
|
||||
@@ -489,9 +488,9 @@ export namespace Workspace {
|
||||
// TODO: Implement exponential backoff
|
||||
await sleep(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function startSync(space: Info) {
|
||||
async function startSync(space: Info) {
|
||||
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
||||
|
||||
const adaptor = await getAdaptor(space.projectID, space.type)
|
||||
@@ -520,11 +519,12 @@ export namespace Workspace {
|
||||
error,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function stopSync(id: WorkspaceID) {
|
||||
function stopSync(id: WorkspaceID) {
|
||||
aborts.get(id)?.abort()
|
||||
aborts.delete(id)
|
||||
connections.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
export * as Workspace from "./workspace"
|
||||
|
||||
Reference in New Issue
Block a user