mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-25 23:15:18 +00:00
fix: keep httpapi instance reloads in layer store
This commit is contained in:
parent
f1470c1a88
commit
f0136f947b
8 changed files with 205 additions and 189 deletions
10
packages/opencode/src/project/instance-context.ts
Normal file
10
packages/opencode/src/project/instance-context.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { LocalContext } from "@/util/local-context"
|
||||
import type * as Project from "./project"
|
||||
|
||||
export interface InstanceContext {
|
||||
directory: string
|
||||
worktree: string
|
||||
project: Project.Info
|
||||
}
|
||||
|
||||
export const context = LocalContext.create<InstanceContext>("instance")
|
||||
159
packages/opencode/src/project/instance-store.ts
Normal file
159
packages/opencode/src/project/instance-store.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { GlobalBus } from "@/bus/global"
|
||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||
import { disposeInstance } from "@/effect/instance-registry"
|
||||
import { makeRuntime } from "@/effect/run-service"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { iife } from "@/util/iife"
|
||||
import { context, type InstanceContext } from "./instance-context"
|
||||
import * as Project from "./project"
|
||||
|
||||
export interface LoadInput {
|
||||
directory: string
|
||||
init?: () => Promise<unknown>
|
||||
worktree?: string
|
||||
project?: Project.Info
|
||||
}
|
||||
|
||||
export interface Interface {
|
||||
readonly load: (input: LoadInput) => Effect.Effect<InstanceContext>
|
||||
readonly reload: (input: LoadInput) => Effect.Effect<InstanceContext>
|
||||
readonly dispose: (ctx: InstanceContext) => Effect.Effect<void>
|
||||
readonly disposeAll: () => Effect.Effect<void>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/InstanceStore") {}
|
||||
|
||||
export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const project = yield* Project.Service
|
||||
const cache = new Map<string, Promise<InstanceContext>>()
|
||||
const disposal = {
|
||||
all: undefined as Promise<void> | undefined,
|
||||
}
|
||||
|
||||
const boot = Effect.fn("InstanceStore.boot")(function* (input: LoadInput & { directory: string }) {
|
||||
const ctx =
|
||||
input.project && input.worktree
|
||||
? {
|
||||
directory: input.directory,
|
||||
worktree: input.worktree,
|
||||
project: input.project,
|
||||
}
|
||||
: yield* project.fromDirectory(input.directory).pipe(
|
||||
Effect.map((result) => ({
|
||||
directory: input.directory,
|
||||
worktree: result.sandbox,
|
||||
project: result.project,
|
||||
})),
|
||||
)
|
||||
const init = input.init
|
||||
if (init) yield* Effect.promise(() => context.provide(ctx, init))
|
||||
return ctx
|
||||
})
|
||||
|
||||
function track(directory: string, next: Promise<InstanceContext>) {
|
||||
const task = next.catch((error) => {
|
||||
if (cache.get(directory) === task) cache.delete(directory)
|
||||
throw error
|
||||
})
|
||||
cache.set(directory, task)
|
||||
return task
|
||||
}
|
||||
|
||||
const load = Effect.fn("InstanceStore.load")(function* (input: LoadInput) {
|
||||
const directory = AppFileSystem.resolve(input.directory)
|
||||
const existing = cache.get(directory)
|
||||
if (existing) return yield* Effect.promise(() => existing)
|
||||
|
||||
Log.Default.info("creating instance", { directory })
|
||||
return yield* Effect.promise(() => track(directory, Effect.runPromise(boot({ ...input, directory }))))
|
||||
})
|
||||
|
||||
const reload = Effect.fn("InstanceStore.reload")(function* (input: LoadInput) {
|
||||
const directory = AppFileSystem.resolve(input.directory)
|
||||
Log.Default.info("reloading instance", { directory })
|
||||
yield* Effect.promise(() => disposeInstance(directory))
|
||||
cache.delete(directory)
|
||||
const next = track(directory, Effect.runPromise(boot({ ...input, directory })))
|
||||
|
||||
GlobalBus.emit("event", {
|
||||
directory,
|
||||
project: input.project?.id,
|
||||
workspace: WorkspaceContext.workspaceID,
|
||||
payload: {
|
||||
type: "server.instance.disposed",
|
||||
properties: {
|
||||
directory,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return yield* Effect.promise(() => next)
|
||||
})
|
||||
|
||||
const dispose = Effect.fn("InstanceStore.dispose")(function* (ctx: InstanceContext) {
|
||||
Log.Default.info("disposing instance", { directory: ctx.directory })
|
||||
yield* Effect.promise(() => disposeInstance(ctx.directory))
|
||||
cache.delete(ctx.directory)
|
||||
|
||||
GlobalBus.emit("event", {
|
||||
directory: ctx.directory,
|
||||
project: ctx.project.id,
|
||||
workspace: WorkspaceContext.workspaceID,
|
||||
payload: {
|
||||
type: "server.instance.disposed",
|
||||
properties: {
|
||||
directory: ctx.directory,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const disposeAll = Effect.fn("InstanceStore.disposeAll")(function* () {
|
||||
if (disposal.all) return yield* Effect.promise(() => disposal.all!)
|
||||
|
||||
disposal.all = iife(async () => {
|
||||
Log.Default.info("disposing all instances")
|
||||
const entries = [...cache.entries()]
|
||||
for (const [key, value] of entries) {
|
||||
if (cache.get(key) !== value) continue
|
||||
|
||||
const ctx = await value.catch((error) => {
|
||||
Log.Default.warn("instance dispose failed", { key, error })
|
||||
return undefined
|
||||
})
|
||||
|
||||
if (!ctx) {
|
||||
if (cache.get(key) === value) cache.delete(key)
|
||||
continue
|
||||
}
|
||||
|
||||
if (cache.get(key) !== value) continue
|
||||
await Effect.runPromise(dispose(ctx))
|
||||
}
|
||||
}).finally(() => {
|
||||
disposal.all = undefined
|
||||
})
|
||||
|
||||
return yield* Effect.promise(() => disposal.all!)
|
||||
})
|
||||
|
||||
yield* Effect.addFinalizer(() => disposeAll().pipe(Effect.ignore))
|
||||
|
||||
return Service.of({
|
||||
load,
|
||||
reload,
|
||||
dispose,
|
||||
disposeAll,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(Project.defaultLayer))
|
||||
|
||||
export const runtime = makeRuntime(Service, defaultLayer)
|
||||
|
||||
export * as InstanceStore from "./instance-store"
|
||||
|
|
@ -1,172 +1,14 @@
|
|||
import { GlobalBus } from "@/bus/global"
|
||||
import { disposeInstance } from "@/effect/instance-registry"
|
||||
import { makeRuntime } from "@/effect/run-service"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { iife } from "@/util/iife"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { LocalContext } from "@/util/local-context"
|
||||
import * as Project from "./project"
|
||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { context, type InstanceContext } from "./instance-context"
|
||||
import { InstanceStore } from "./instance-store"
|
||||
|
||||
export interface InstanceContext {
|
||||
directory: string
|
||||
worktree: string
|
||||
project: Project.Info
|
||||
}
|
||||
|
||||
const context = LocalContext.create<InstanceContext>("instance")
|
||||
|
||||
export interface LoadInput {
|
||||
directory: string
|
||||
init?: () => Promise<unknown>
|
||||
worktree?: string
|
||||
project?: Project.Info
|
||||
}
|
||||
|
||||
export interface Interface {
|
||||
readonly load: (input: LoadInput) => Effect.Effect<InstanceContext>
|
||||
readonly reload: (input: LoadInput) => Effect.Effect<InstanceContext>
|
||||
readonly dispose: (ctx: InstanceContext) => Effect.Effect<void>
|
||||
readonly disposeAll: () => Effect.Effect<void>
|
||||
}
|
||||
|
||||
export class InstanceStore extends Context.Service<InstanceStore, Interface>()("@opencode/InstanceStore") {}
|
||||
|
||||
export const instanceStoreLayer: Layer.Layer<InstanceStore, never, Project.Service> = Layer.effect(
|
||||
InstanceStore,
|
||||
Effect.gen(function* () {
|
||||
const project = yield* Project.Service
|
||||
const cache = new Map<string, Promise<InstanceContext>>()
|
||||
const disposal = {
|
||||
all: undefined as Promise<void> | undefined,
|
||||
}
|
||||
|
||||
const boot = Effect.fn("InstanceStore.boot")(function* (input: LoadInput & { directory: string }) {
|
||||
const ctx =
|
||||
input.project && input.worktree
|
||||
? {
|
||||
directory: input.directory,
|
||||
worktree: input.worktree,
|
||||
project: input.project,
|
||||
}
|
||||
: yield* project.fromDirectory(input.directory).pipe(
|
||||
Effect.map((result) => ({
|
||||
directory: input.directory,
|
||||
worktree: result.sandbox,
|
||||
project: result.project,
|
||||
})),
|
||||
)
|
||||
const init = input.init
|
||||
if (init) yield* Effect.promise(() => context.provide(ctx, init))
|
||||
return ctx
|
||||
})
|
||||
|
||||
function track(directory: string, next: Promise<InstanceContext>) {
|
||||
const task = next.catch((error) => {
|
||||
if (cache.get(directory) === task) cache.delete(directory)
|
||||
throw error
|
||||
})
|
||||
cache.set(directory, task)
|
||||
return task
|
||||
}
|
||||
|
||||
const load = Effect.fn("InstanceStore.load")(function* (input: LoadInput) {
|
||||
const directory = AppFileSystem.resolve(input.directory)
|
||||
const existing = cache.get(directory)
|
||||
if (existing) return yield* Effect.promise(() => existing)
|
||||
|
||||
Log.Default.info("creating instance", { directory })
|
||||
return yield* Effect.promise(() => track(directory, Effect.runPromise(boot({ ...input, directory }))))
|
||||
})
|
||||
|
||||
const reload = Effect.fn("InstanceStore.reload")(function* (input: LoadInput) {
|
||||
const directory = AppFileSystem.resolve(input.directory)
|
||||
Log.Default.info("reloading instance", { directory })
|
||||
yield* Effect.promise(() => disposeInstance(directory))
|
||||
cache.delete(directory)
|
||||
const next = track(directory, Effect.runPromise(boot({ ...input, directory })))
|
||||
|
||||
GlobalBus.emit("event", {
|
||||
directory,
|
||||
project: input.project?.id,
|
||||
workspace: WorkspaceContext.workspaceID,
|
||||
payload: {
|
||||
type: "server.instance.disposed",
|
||||
properties: {
|
||||
directory,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return yield* Effect.promise(() => next)
|
||||
})
|
||||
|
||||
const dispose = Effect.fn("InstanceStore.dispose")(function* (ctx: InstanceContext) {
|
||||
Log.Default.info("disposing instance", { directory: ctx.directory })
|
||||
yield* Effect.promise(() => disposeInstance(ctx.directory))
|
||||
cache.delete(ctx.directory)
|
||||
|
||||
GlobalBus.emit("event", {
|
||||
directory: ctx.directory,
|
||||
project: ctx.project.id,
|
||||
workspace: WorkspaceContext.workspaceID,
|
||||
payload: {
|
||||
type: "server.instance.disposed",
|
||||
properties: {
|
||||
directory: ctx.directory,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const disposeAll = Effect.fn("InstanceStore.disposeAll")(function* () {
|
||||
if (disposal.all) return yield* Effect.promise(() => disposal.all!)
|
||||
|
||||
disposal.all = iife(async () => {
|
||||
Log.Default.info("disposing all instances")
|
||||
const entries = [...cache.entries()]
|
||||
for (const [key, value] of entries) {
|
||||
if (cache.get(key) !== value) continue
|
||||
|
||||
const ctx = await value.catch((error) => {
|
||||
Log.Default.warn("instance dispose failed", { key, error })
|
||||
return undefined
|
||||
})
|
||||
|
||||
if (!ctx) {
|
||||
if (cache.get(key) === value) cache.delete(key)
|
||||
continue
|
||||
}
|
||||
|
||||
if (cache.get(key) !== value) continue
|
||||
await Effect.runPromise(dispose(ctx))
|
||||
}
|
||||
}).finally(() => {
|
||||
disposal.all = undefined
|
||||
})
|
||||
|
||||
return yield* Effect.promise(() => disposal.all!)
|
||||
})
|
||||
|
||||
yield* Effect.addFinalizer(() => disposeAll().pipe(Effect.ignore))
|
||||
|
||||
return InstanceStore.of({
|
||||
load,
|
||||
reload,
|
||||
dispose,
|
||||
disposeAll,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
export const instanceStoreDefaultLayer = instanceStoreLayer.pipe(Layer.provide(Project.defaultLayer))
|
||||
|
||||
const instanceStoreRuntime = makeRuntime(InstanceStore, instanceStoreDefaultLayer)
|
||||
export type { InstanceContext } from "./instance-context"
|
||||
export type { LoadInput } from "./instance-store"
|
||||
|
||||
export const Instance = {
|
||||
load(input: LoadInput): Promise<InstanceContext> {
|
||||
return instanceStoreRuntime.runPromise((store) => store.load(input))
|
||||
load(input: InstanceStore.LoadInput): Promise<InstanceContext> {
|
||||
return InstanceStore.runtime.runPromise((store) => store.load(input))
|
||||
},
|
||||
async provide<R>(input: { directory: string; init?: () => Promise<any>; fn: () => R }): Promise<R> {
|
||||
return context.provide(await Instance.load(input), async () => input.fn())
|
||||
|
|
@ -215,12 +57,12 @@ export const Instance = {
|
|||
return context.provide(ctx, fn)
|
||||
},
|
||||
async reload(input: { directory: string; init?: () => Promise<any>; project?: Project.Info; worktree?: string }) {
|
||||
return instanceStoreRuntime.runPromise((store) => store.reload(input))
|
||||
return InstanceStore.runtime.runPromise((store) => store.reload(input))
|
||||
},
|
||||
async dispose() {
|
||||
return instanceStoreRuntime.runPromise((store) => store.dispose(Instance.current))
|
||||
return InstanceStore.runtime.runPromise((store) => store.dispose(Instance.current))
|
||||
},
|
||||
async disposeAll() {
|
||||
return instanceStoreRuntime.runPromise((store) => store.disposeAll())
|
||||
return InstanceStore.runtime.runPromise((store) => store.disposeAll())
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import type { WorkspaceID } from "@/control-plane/schema"
|
|||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||
import { WorkspaceRef } from "@/effect/instance-ref"
|
||||
import { Instance, type InstanceContext } from "@/project/instance"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { Effect } from "effect"
|
||||
import { HttpEffect, HttpMiddleware, HttpServerRequest } from "effect/unstable/http"
|
||||
|
||||
type MarkedInstance = {
|
||||
ctx: InstanceContext
|
||||
store: InstanceStore.Interface
|
||||
workspaceID?: WorkspaceID
|
||||
}
|
||||
|
||||
|
|
@ -17,17 +19,17 @@ const disposeAfterResponse = new WeakMap<object, MarkedInstance>()
|
|||
|
||||
const mark = (ctx: InstanceContext) =>
|
||||
Effect.gen(function* () {
|
||||
return { ctx, workspaceID: yield* WorkspaceRef }
|
||||
return { ctx, store: yield* InstanceStore.Service, workspaceID: yield* WorkspaceRef }
|
||||
})
|
||||
|
||||
// Instance.dispose/reload still publish events through legacy ALS helpers.
|
||||
// InstanceStore lifecycle operations still publish events through legacy ALS helpers.
|
||||
// Effect request handlers carry these values in services, so bridge them back
|
||||
// into the legacy contexts only around the lifecycle operation.
|
||||
const restoreMarked = <A>(marked: MarkedInstance, fn: () => A) =>
|
||||
const restoreMarked = <A>(marked: MarkedInstance, effect: Effect.Effect<A>) =>
|
||||
Effect.promise(() =>
|
||||
WorkspaceContext.provide({
|
||||
workspaceID: marked.workspaceID,
|
||||
fn: () => Instance.restore(marked.ctx, fn),
|
||||
fn: () => Instance.restore(marked.ctx, () => Effect.runPromise(effect)),
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -43,11 +45,11 @@ export const markInstanceForDisposal = (ctx: InstanceContext) =>
|
|||
)
|
||||
})
|
||||
|
||||
export const markInstanceForReload = (ctx: InstanceContext, next: Parameters<typeof Instance.reload>[0]) =>
|
||||
export const markInstanceForReload = (ctx: InstanceContext, next: InstanceStore.LoadInput) =>
|
||||
Effect.gen(function* () {
|
||||
const marked = yield* mark(ctx)
|
||||
return yield* HttpEffect.appendPreResponseHandler((_request, response) =>
|
||||
Effect.as(Effect.uninterruptible(restoreMarked(marked, () => Instance.reload(next))), response),
|
||||
Effect.as(Effect.uninterruptible(restoreMarked(marked, marked.store.reload(next))), response),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -58,6 +60,6 @@ export const disposeMiddleware: HttpMiddleware.HttpMiddleware = (effect) =>
|
|||
const marked = disposeAfterResponse.get(request.source)
|
||||
if (!marked) return response
|
||||
disposeAfterResponse.delete(request.source)
|
||||
yield* Effect.uninterruptible(restoreMarked(marked, () => Instance.dispose()))
|
||||
yield* Effect.uninterruptible(restoreMarked(marked, marked.store.dispose(marked.ctx)))
|
||||
return response
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { InstanceBootstrap } from "@/project/bootstrap"
|
||||
import { InstanceStore, type InstanceContext } from "@/project/instance"
|
||||
import type { InstanceContext } from "@/project/instance"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
import { WorkspaceRouteContext } from "./workspace-routing"
|
||||
|
|
@ -24,7 +25,7 @@ function decode(input: string): string {
|
|||
}
|
||||
|
||||
function makeInstanceContext(
|
||||
store: Context.Service.Shape<typeof InstanceStore>,
|
||||
store: InstanceStore.Interface,
|
||||
directory: string,
|
||||
): Effect.Effect<InstanceContext> {
|
||||
return store.load({
|
||||
|
|
@ -35,7 +36,7 @@ function makeInstanceContext(
|
|||
|
||||
function provideInstanceContext<E>(
|
||||
effect: Effect.Effect<HttpServerResponse.HttpServerResponse, E>,
|
||||
store: Context.Service.Shape<typeof InstanceStore>,
|
||||
store: InstanceStore.Interface,
|
||||
): Effect.Effect<HttpServerResponse.HttpServerResponse, E, WorkspaceRouteContext> {
|
||||
return Effect.gen(function* () {
|
||||
const route = yield* WorkspaceRouteContext
|
||||
|
|
@ -50,14 +51,14 @@ function provideInstanceContext<E>(
|
|||
export const instanceContextLayer = Layer.effect(
|
||||
InstanceContextMiddleware,
|
||||
Effect.gen(function* () {
|
||||
const store = yield* InstanceStore
|
||||
const store = yield* InstanceStore.Service
|
||||
return InstanceContextMiddleware.of((effect) => provideInstanceContext(effect, store))
|
||||
}),
|
||||
)
|
||||
|
||||
export const instanceRouterMiddleware = HttpRouter.middleware()(
|
||||
Effect.gen(function* () {
|
||||
const store = yield* InstanceStore
|
||||
const store = yield* InstanceStore.Service
|
||||
return (effect) => provideInstanceContext(effect, store)
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { LSP } from "@/lsp/lsp"
|
|||
import { MCP } from "@/mcp"
|
||||
import { Permission } from "@/permission"
|
||||
import { Installation } from "@/installation"
|
||||
import { instanceStoreDefaultLayer } from "@/project/instance"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { Project } from "@/project/project"
|
||||
import { ProviderAuth } from "@/provider/auth"
|
||||
import { Provider } from "@/provider/provider"
|
||||
|
|
@ -146,7 +146,7 @@ export function createRoutes(corsOptions?: CorsOptions) {
|
|||
Format.defaultLayer,
|
||||
LSP.defaultLayer,
|
||||
Installation.defaultLayer,
|
||||
instanceStoreDefaultLayer,
|
||||
InstanceStore.defaultLayer,
|
||||
MCP.defaultLayer,
|
||||
Permission.defaultLayer,
|
||||
Project.defaultLayer,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Instance, InstanceStore, instanceStoreDefaultLayer } from "../../src/project/instance"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { tmpdirScoped } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(Layer.mergeAll(instanceStoreDefaultLayer, CrossSpawnSpawner.defaultLayer))
|
||||
const it = testEffect(Layer.mergeAll(InstanceStore.defaultLayer, CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
afterEach(async () => {
|
||||
await Instance.disposeAll()
|
||||
|
|
@ -15,7 +16,7 @@ describe("InstanceStore", () => {
|
|||
it.live("loads instance context without installing ALS for the caller", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const store = yield* InstanceStore
|
||||
const store = yield* InstanceStore.Service
|
||||
const ctx = yield* store.load({ directory: dir })
|
||||
|
||||
expect(ctx.directory).toBe(dir)
|
||||
|
|
@ -27,7 +28,7 @@ describe("InstanceStore", () => {
|
|||
it.live("runs load init inside the loaded legacy instance context", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const store = yield* InstanceStore
|
||||
const store = yield* InstanceStore.Service
|
||||
let initializedDirectory: string | undefined
|
||||
|
||||
yield* store.load({
|
||||
|
|
@ -45,7 +46,7 @@ describe("InstanceStore", () => {
|
|||
it.live("caches loaded instance context by directory", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const store = yield* InstanceStore
|
||||
const store = yield* InstanceStore.Service
|
||||
let initialized = 0
|
||||
|
||||
const first = yield* store.load({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import { registerAdapter } from "../../src/control-plane/adapters"
|
|||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
|
||||
import { Instance, instanceStoreDefaultLayer } from "../../src/project/instance"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
import { disposeMiddleware, markInstanceForDisposal } from "../../src/server/routes/instance/httpapi/lifecycle"
|
||||
import { instanceRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/instance-context"
|
||||
|
|
@ -40,7 +41,7 @@ const it = testEffect(
|
|||
testStateLayer,
|
||||
NodeHttpServer.layerTest,
|
||||
NodeServices.layer,
|
||||
instanceStoreDefaultLayer,
|
||||
InstanceStore.defaultLayer,
|
||||
Project.defaultLayer,
|
||||
Workspace.defaultLayer,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue