mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-22 03:45:23 +00:00
refactor(opencode): roll out serviceUse proxy across 14 services + tests (#28576)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Cache, Clock, Duration, Effect, Layer, Option, Schema, SchemaGetter, Context } from "effect"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import {
|
||||
FetchHttpClient,
|
||||
HttpClient,
|
||||
@@ -181,6 +182,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Account") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { eq } from "drizzle-orm"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Effect, Layer, Option, Schema, Context } from "effect"
|
||||
|
||||
import { Database } from "@/storage/db"
|
||||
@@ -38,6 +39,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/AccountRepo") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer: Layer.Layer<Service> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Config } from "@/config/config"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { ModelID, ProviderID } from "../provider/schema"
|
||||
import { generateObject, streamObject, type ModelMessage } from "ai"
|
||||
@@ -76,6 +77,8 @@ type State = Omit<Interface, "generate">
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import os from "os"
|
||||
@@ -319,6 +320,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Config") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
function globalConfigFile() {
|
||||
const candidates = ["opencode.jsonc", "opencode.json", "config.json"].map((file) =>
|
||||
path.join(Global.Path.config, file),
|
||||
|
||||
3
packages/opencode/src/env/index.ts
vendored
3
packages/opencode/src/env/index.ts
vendored
@@ -1,4 +1,5 @@
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
|
||||
type State = Record<string, string | undefined>
|
||||
@@ -12,6 +13,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Env") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BusEvent } from "@/bus/bus-event"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
@@ -326,6 +327,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/File") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from "path"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { Cause, Context, Effect, Fiber, Layer, Queue, Schema, Stream } from "effect"
|
||||
import type { PlatformError } from "effect/PlatformError"
|
||||
@@ -141,6 +142,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
function env() {
|
||||
const env = sanitizedProcessEnv()
|
||||
delete env.RIPGREP_CONFIG_PATH
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Effect, Layer, Context, Schema } from "effect"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { AppProcess } from "@opencode-ai/core/process"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
@@ -27,6 +28,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Format") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Effect, Layer, Schema, Context, Stream } from "effect"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { withTransientReadRetry } from "@/util/effect-http-client"
|
||||
import { errorMessage } from "@/util/error"
|
||||
@@ -89,6 +90,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Installation") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Service> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from "path"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Effect, Layer, Context, Option, Schema } from "effect"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
@@ -51,6 +52,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||
@@ -264,6 +265,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/MCP") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GlobalBus } from "@/bus/global"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { disposeInstance as runDisposers } from "@/effect/instance-registry"
|
||||
@@ -24,6 +25,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/InstanceStore") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
interface Entry {
|
||||
readonly deferred: Deferred.Deferred<InstanceContext>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Auth } from "@/auth"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||
@@ -102,6 +103,8 @@ interface State {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type * as SDK from "@opencode-ai/sdk/v2"
|
||||
import { serviceUse } from "@/effect/service-use"
|
||||
import { Effect, Exit, Layer, Option, Schema, Scope, Context, Stream } from "effect"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { Account } from "@/account/account"
|
||||
@@ -76,6 +77,8 @@ export interface Interface {
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ShareNext") {}
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
|
||||
Effect.sync(() => Database.use(fn))
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
|
||||
|
||||
it.live("list returns empty when no accounts exist", () =>
|
||||
Effect.gen(function* () {
|
||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
||||
const accounts = yield* AccountRepo.use.list()
|
||||
expect(accounts).toEqual([])
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("active returns none when no accounts exist", () =>
|
||||
Effect.gen(function* () {
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.isNone(active)).toBe(true)
|
||||
}),
|
||||
)
|
||||
@@ -45,13 +45,13 @@ it.live("persistAccount inserts and getRow retrieves", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
expect(Option.isSome(row)).toBe(true)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.id).toBe(AccountID.make("user-1"))
|
||||
expect(value.email).toBe("test@example.com")
|
||||
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-1"))
|
||||
}),
|
||||
)
|
||||
@@ -72,9 +72,9 @@ it.live("persistAccount normalizes trailing slashes in stored server URLs", () =
|
||||
}),
|
||||
)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const list = yield* AccountRepo.Service.use((r) => r.list())
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const active = yield* AccountRepo.use.active()
|
||||
const list = yield* AccountRepo.use.list()
|
||||
|
||||
expect(Option.getOrThrow(row).url).toBe("https://control.example.com")
|
||||
expect(Option.getOrThrow(active).url).toBe("https://control.example.com")
|
||||
@@ -112,7 +112,7 @@ it.live("persistAccount sets the active account and org", () =>
|
||||
)
|
||||
|
||||
// Last persisted account is active with its org
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.isSome(active)).toBe(true)
|
||||
expect(Option.getOrThrow(active).id).toBe(AccountID.make("user-2"))
|
||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
||||
@@ -148,7 +148,7 @@ it.live("list returns all accounts", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
||||
const accounts = yield* AccountRepo.use.list()
|
||||
expect(accounts.length).toBe(2)
|
||||
expect(accounts.map((a) => a.email).sort()).toEqual(["a@example.com", "b@example.com"])
|
||||
}),
|
||||
@@ -170,9 +170,9 @@ it.live("remove deletes an account", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
yield* AccountRepo.Service.use((r) => r.remove(id))
|
||||
yield* AccountRepo.use.remove(id)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
expect(Option.isNone(row)).toBe(true)
|
||||
}),
|
||||
)
|
||||
@@ -207,12 +207,12 @@ it.live("use stores the selected org and marks the account active", () =>
|
||||
)
|
||||
|
||||
yield* AccountRepo.Service.use((r) => r.use(id1, Option.some(OrgID.make("org-99"))))
|
||||
const active1 = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active1 = yield* AccountRepo.use.active()
|
||||
expect(Option.getOrThrow(active1).id).toBe(id1)
|
||||
expect(Option.getOrThrow(active1).active_org_id).toBe(OrgID.make("org-99"))
|
||||
|
||||
yield* AccountRepo.Service.use((r) => r.use(id1, Option.none()))
|
||||
const active2 = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active2 = yield* AccountRepo.use.active()
|
||||
expect(Option.getOrThrow(active2).active_org_id).toBeNull()
|
||||
}),
|
||||
)
|
||||
@@ -243,7 +243,7 @@ it.live("persistToken updates token fields", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("new_token"))
|
||||
expect(value.refresh_token).toBe(RefreshToken.make("new_refresh"))
|
||||
@@ -276,7 +276,7 @@ it.live("persistToken with no expiry sets token_expiry to null", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
expect(Option.getOrThrow(row).token_expiry).toBeNull()
|
||||
}),
|
||||
)
|
||||
@@ -309,14 +309,14 @@ it.live("persistAccount upserts on conflict", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
||||
const accounts = yield* AccountRepo.use.list()
|
||||
expect(accounts.length).toBe(1)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("at_v2"))
|
||||
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
||||
}),
|
||||
)
|
||||
@@ -337,9 +337,9 @@ it.live("remove clears active state when deleting the active account", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
yield* AccountRepo.Service.use((r) => r.remove(id))
|
||||
yield* AccountRepo.use.remove(id)
|
||||
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.isNone(active)).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -88,7 +88,7 @@ it.live("login normalizes trailing slashes in the provided server URL", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const result = yield* Account.Service.use((s) => s.login("https://one.example.com/")).pipe(
|
||||
const result = yield* Account.use.login("https://one.example.com/").pipe(
|
||||
Effect.provide(live(client)),
|
||||
)
|
||||
|
||||
@@ -109,7 +109,7 @@ it.live("login maps transport failures to account transport errors", () =>
|
||||
)
|
||||
|
||||
const error = yield* Effect.flip(
|
||||
Account.Service.use((s) => s.login("https://one.example.com")).pipe(Effect.provide(live(client))),
|
||||
Account.use.login("https://one.example.com").pipe(Effect.provide(live(client))),
|
||||
)
|
||||
|
||||
expect(error).toBeInstanceOf(AccountTransportError)
|
||||
@@ -163,7 +163,7 @@ it.live("orgsByAccount groups orgs per account", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
const rows = yield* Account.Service.use((s) => s.orgsByAccount()).pipe(Effect.provide(live(client)))
|
||||
const rows = yield* Account.use.orgsByAccount().pipe(Effect.provide(live(client)))
|
||||
|
||||
expect(rows.map((row) => [row.account.id, row.orgs.map((org) => org.id)]).map(([id, orgs]) => [id, orgs])).toEqual([
|
||||
[AccountID.make("user-1"), [OrgID.make("org-1")]],
|
||||
@@ -201,12 +201,12 @@ it.live("token refresh persists the new token", () =>
|
||||
),
|
||||
)
|
||||
|
||||
const token = yield* Account.Service.use((s) => s.token(id)).pipe(Effect.provide(live(client)))
|
||||
const token = yield* Account.use.token(id).pipe(Effect.provide(live(client)))
|
||||
|
||||
expect(Option.getOrThrow(token)).toBeDefined()
|
||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||
@@ -246,12 +246,12 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
|
||||
}),
|
||||
)
|
||||
|
||||
const token = yield* Account.Service.use((s) => s.token(id)).pipe(Effect.provide(live(client)))
|
||||
const token = yield* Account.use.token(id).pipe(Effect.provide(live(client)))
|
||||
|
||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||
expect(refreshCalls).toBe(1)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||
@@ -315,7 +315,7 @@ it.live("concurrent config and token requests coalesce token refresh", () =>
|
||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||
expect(refreshCalls).toBe(1)
|
||||
|
||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
||||
const row = yield* AccountRepo.use.getRow(id)
|
||||
const value = Option.getOrThrow(row)
|
||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||
@@ -388,7 +388,7 @@ it.live("poll stores the account and first org on success", () =>
|
||||
expect(res.email).toBe("user@example.com")
|
||||
}
|
||||
|
||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
||||
const active = yield* AccountRepo.use.active()
|
||||
expect(Option.getOrThrow(active)).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "user-1",
|
||||
|
||||
@@ -46,8 +46,8 @@ function testAgent(input: {
|
||||
|
||||
it.instance("[#26514] subagent spawned from plan mode inherits read-only restriction (edit denied)", () =>
|
||||
Effect.gen(function* () {
|
||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
||||
const generalAgent = yield* Agent.Service.use((svc) => svc.get("general"))
|
||||
const planAgent = yield* Agent.use.get("plan")
|
||||
const generalAgent = yield* Agent.use.get("general")
|
||||
|
||||
expect(planAgent).toBeDefined()
|
||||
expect(generalAgent).toBeDefined()
|
||||
@@ -83,8 +83,8 @@ it.instance("[#26514] explore subagent launched from plan mode also stays read-o
|
||||
// should propagate the parent **agent** permissions, not just deny edit
|
||||
// when the subagent happens to already deny it.
|
||||
Effect.gen(function* () {
|
||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
||||
const explore = yield* Agent.Service.use((svc) => svc.get("explore"))
|
||||
const planAgent = yield* Agent.use.get("plan")
|
||||
const explore = yield* Agent.use.get("explore")
|
||||
expect(planAgent).toBeDefined()
|
||||
expect(explore).toBeDefined()
|
||||
|
||||
@@ -108,8 +108,8 @@ it.instance(
|
||||
// be able to edit when the parent agent is `plan`.
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
||||
const my = yield* Agent.Service.use((svc) => svc.get("my_subagent"))
|
||||
const planAgent = yield* Agent.use.get("plan")
|
||||
const my = yield* Agent.use.get("my_subagent")
|
||||
expect(planAgent).toBeDefined()
|
||||
expect(my).toBeDefined()
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ it.instance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
yield* Plugin.Service.use((p) => p.init())
|
||||
const agents = yield* Agent.Service.use((svc) => svc.list())
|
||||
const agents = yield* Agent.use.list()
|
||||
const added = agents.find((agent) => agent.name === PLUGIN_AGENT.name)
|
||||
expect(added?.description).toBe(PLUGIN_AGENT.description)
|
||||
expect(added?.mode).toBe(PLUGIN_AGENT.mode)
|
||||
|
||||
@@ -11,7 +11,7 @@ it.instance(
|
||||
"agent color parsed from project config",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const cfg = yield* Config.Service.use((svc) => svc.get())
|
||||
const cfg = yield* Config.use.get()
|
||||
expect(cfg.agent?.["build"]?.color).toBe("#FFA500")
|
||||
expect(cfg.agent?.["plan"]?.color).toBe("primary")
|
||||
}),
|
||||
@@ -30,9 +30,9 @@ it.instance(
|
||||
"Agent.get includes color from config",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const plan = yield* AgentSvc.Service.use((svc) => svc.get("plan"))
|
||||
const plan = yield* AgentSvc.use.get("plan")
|
||||
expect(plan?.color).toBe("#A855F7")
|
||||
const build = yield* AgentSvc.Service.use((svc) => svc.get("build"))
|
||||
const build = yield* AgentSvc.use.get("build")
|
||||
expect(build?.color).toBe("accent")
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -70,14 +70,14 @@ const load = (ctx: InstanceContext) =>
|
||||
)
|
||||
const saveGlobal = (config: Config.Info) =>
|
||||
Effect.runPromise(
|
||||
Config.Service.use((svc) => svc.updateGlobal(config)).pipe(
|
||||
Config.use.updateGlobal(config).pipe(
|
||||
Effect.map((result) => result.info),
|
||||
Effect.scoped,
|
||||
Effect.provide(layer),
|
||||
),
|
||||
)
|
||||
const clear = async (wait = false) => {
|
||||
await Effect.runPromise(Config.Service.use((svc) => svc.invalidate()).pipe(Effect.scoped, Effect.provide(layer)))
|
||||
await Effect.runPromise(Config.use.invalidate().pipe(Effect.scoped, Effect.provide(layer)))
|
||||
if (wait) await InstanceRuntime.disposeAllInstances()
|
||||
}
|
||||
const listDirs = (ctx: InstanceContext) =>
|
||||
@@ -162,7 +162,7 @@ async function check(map: (dir: string) => string) {
|
||||
|
||||
it.instance("loads config with defaults when no files exist", () =>
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBeDefined()
|
||||
}),
|
||||
)
|
||||
@@ -218,7 +218,7 @@ test("does not create global config when OPENCODE_CONFIG_DIR is set", async () =
|
||||
it.instance(
|
||||
"loads JSON config file",
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("test/model")
|
||||
expect(config.username).toBe("testuser")
|
||||
}),
|
||||
@@ -228,7 +228,7 @@ it.instance(
|
||||
it.instance(
|
||||
"loads shell config field",
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.shell).toBe("bash")
|
||||
}),
|
||||
{ config: { shell: "bash" } },
|
||||
@@ -313,7 +313,7 @@ test("updates global config and omits empty shell key in jsonc", async () => {
|
||||
it.instance(
|
||||
"loads formatter boolean config",
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.formatter).toBe(true)
|
||||
}),
|
||||
{ config: { formatter: true } },
|
||||
@@ -322,7 +322,7 @@ it.instance(
|
||||
it.instance(
|
||||
"loads lsp boolean config",
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.lsp).toBe(true)
|
||||
}),
|
||||
{ config: { lsp: true } },
|
||||
@@ -355,7 +355,7 @@ it.instance("ignores legacy tui keys in opencode config", () =>
|
||||
tui: { scroll_speed: 4 },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("test/model")
|
||||
expect((config as Record<string, unknown>).theme).toBeUndefined()
|
||||
expect((config as Record<string, unknown>).tui).toBeUndefined()
|
||||
@@ -376,7 +376,7 @@ it.instance("loads JSONC config file", () =>
|
||||
}`,
|
||||
),
|
||||
)
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("test/model")
|
||||
expect(config.username).toBe("testuser")
|
||||
}),
|
||||
@@ -398,7 +398,7 @@ it.instance("jsonc overrides json in the same directory", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
model: "override",
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("base")
|
||||
expect(config.username).toBe("base")
|
||||
}),
|
||||
@@ -414,7 +414,7 @@ it.instance("handles environment variable substitution", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
username: "{env:TEST_VAR}",
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("test-user")
|
||||
}),
|
||||
),
|
||||
@@ -435,7 +435,7 @@ it.instance("preserves env variables when adding $schema to config", () =>
|
||||
}),
|
||||
),
|
||||
)
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("secret_value")
|
||||
|
||||
// Read the file to verify the env variable was preserved
|
||||
@@ -455,7 +455,7 @@ it.instance("handles file inclusion substitution", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
username: "{file:included.txt}",
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("test-user")
|
||||
}),
|
||||
)
|
||||
@@ -470,7 +470,7 @@ it.instance("handles file inclusion with replacement tokens", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
username: "{file:included.md}",
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("const out = await Bun.$`echo hi`")
|
||||
}),
|
||||
)
|
||||
@@ -547,7 +547,7 @@ it.instance("validates config schema and throws on invalid fields", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
invalid_field: "should cause error",
|
||||
})
|
||||
const exit = yield* Config.Service.use((svc) => svc.get()).pipe(Effect.exit)
|
||||
const exit = yield* Config.use.get().pipe(Effect.exit)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
}),
|
||||
)
|
||||
@@ -556,7 +556,7 @@ it.instance("throws error for invalid JSON", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
yield* Effect.promise(() => Filesystem.write(path.join(test.directory, "opencode.json"), "{ invalid json }"))
|
||||
const exit = yield* Config.Service.use((svc) => svc.get()).pipe(Effect.exit)
|
||||
const exit = yield* Config.use.get().pipe(Effect.exit)
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
}),
|
||||
)
|
||||
@@ -574,7 +574,7 @@ it.instance("handles agent configuration", () =>
|
||||
},
|
||||
},
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test_agent"]).toEqual(
|
||||
expect.objectContaining({
|
||||
model: "test/model",
|
||||
@@ -598,7 +598,7 @@ it.instance("treats agent variant as model-scoped setting (not provider option)"
|
||||
},
|
||||
},
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
const agent = config.agent?.["test_agent"]
|
||||
|
||||
expect(agent?.variant).toBe("xhigh")
|
||||
@@ -622,7 +622,7 @@ it.instance("handles command configuration", () =>
|
||||
},
|
||||
},
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.command?.["test_command"]).toEqual({
|
||||
template: "test template",
|
||||
description: "test command",
|
||||
@@ -638,7 +638,7 @@ it.instance("migrates autoshare to share field", () =>
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
autoshare: true,
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.share).toBe("auto")
|
||||
expect(config.autoshare).toBe(true)
|
||||
}),
|
||||
@@ -656,7 +656,7 @@ it.instance("migrates mode field to agent field", () =>
|
||||
},
|
||||
},
|
||||
})
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test_mode"]).toEqual({
|
||||
model: "test/model",
|
||||
temperature: 0.5,
|
||||
@@ -679,7 +679,7 @@ model: test/model
|
||||
Test agent prompt`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "test",
|
||||
@@ -705,7 +705,7 @@ permission:
|
||||
Ordered permissions`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(Object.keys(config.agent?.ordered?.permission ?? {})).toEqual(["bash", "*", "edit"])
|
||||
}),
|
||||
)
|
||||
@@ -732,7 +732,7 @@ mode: subagent
|
||||
Nested agent prompt`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
|
||||
expect(config.agent?.["helper"]).toMatchObject({
|
||||
name: "helper",
|
||||
@@ -770,7 +770,7 @@ description: Nested command
|
||||
Nested command template`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
|
||||
expect(config.command?.["hello"]).toEqual({
|
||||
description: "Test command",
|
||||
@@ -804,7 +804,7 @@ description: Nested command
|
||||
Nested command template`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
|
||||
expect(config.command?.["hello"]).toEqual({
|
||||
description: "Test command",
|
||||
@@ -834,7 +834,7 @@ it.instance("updates config and writes to file", () =>
|
||||
|
||||
it.instance("gets config directories", () =>
|
||||
Effect.gen(function* () {
|
||||
const dirs = yield* Config.Service.use((svc) => svc.directories())
|
||||
const dirs = yield* Config.use.directories()
|
||||
expect(dirs.length).toBeGreaterThanOrEqual(1)
|
||||
}),
|
||||
)
|
||||
@@ -950,7 +950,7 @@ it.instance("resolves scoped npm plugins in config", () =>
|
||||
yield* writeTextEffect(path.join(pluginDir, "index.js"), "export default {}\n")
|
||||
yield* writeConfigEffect(test.directory, { plugin: ["@scope/plugin"] })
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.plugin ?? []).toContain("@scope/plugin")
|
||||
}),
|
||||
)
|
||||
@@ -1014,7 +1014,7 @@ mode: subagent
|
||||
Helper subagent prompt`,
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["helper"]).toMatchObject({
|
||||
name: "helper",
|
||||
model: "test/model",
|
||||
@@ -1212,7 +1212,7 @@ it.instance("migrates legacy tools config to permissions - allow", () =>
|
||||
agent: { test: { tools: { bash: true, read: true } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({
|
||||
bash: "allow",
|
||||
read: "allow",
|
||||
@@ -1228,7 +1228,7 @@ it.instance("migrates legacy tools config to permissions - deny", () =>
|
||||
agent: { test: { tools: { bash: false, webfetch: false } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({
|
||||
bash: "deny",
|
||||
webfetch: "deny",
|
||||
@@ -1244,7 +1244,7 @@ it.instance("migrates legacy write tool to edit permission", () =>
|
||||
agent: { test: { tools: { write: true } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
||||
}),
|
||||
)
|
||||
@@ -1261,7 +1261,7 @@ it.instance(
|
||||
share: "disabled",
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("managed/model")
|
||||
expect(config.share).toBe("disabled")
|
||||
expect(config.username).toBe("testuser")
|
||||
@@ -1278,7 +1278,7 @@ it.instance(
|
||||
disabled_providers: ["openai"],
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.autoupdate).toBe(false)
|
||||
expect(config.disabled_providers).toEqual(["openai"])
|
||||
}),
|
||||
@@ -1288,7 +1288,7 @@ it.instance(
|
||||
it.instance(
|
||||
"missing managed settings file is not an error",
|
||||
Effect.gen(function* () {
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("user/model")
|
||||
}),
|
||||
{ config: { model: "user/model" } },
|
||||
@@ -1302,7 +1302,7 @@ it.instance("migrates legacy edit tool to edit permission", () =>
|
||||
agent: { test: { tools: { edit: false } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "deny" })
|
||||
}),
|
||||
)
|
||||
@@ -1315,7 +1315,7 @@ it.instance("migrates legacy patch tool to edit permission", () =>
|
||||
agent: { test: { tools: { patch: true } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
||||
}),
|
||||
)
|
||||
@@ -1328,7 +1328,7 @@ it.instance("migrates mixed legacy tools config", () =>
|
||||
agent: { test: { tools: { bash: true, write: true, read: false, webfetch: true } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({
|
||||
bash: "allow",
|
||||
edit: "allow",
|
||||
@@ -1346,7 +1346,7 @@ it.instance("merges legacy tools with existing permission config", () =>
|
||||
agent: { test: { permission: { glob: "allow" }, tools: { bash: true } } },
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.agent?.["test"]?.permission).toEqual({
|
||||
glob: "allow",
|
||||
bash: "allow",
|
||||
@@ -1375,7 +1375,7 @@ it.instance("permission config preserves user key order", () =>
|
||||
},
|
||||
})
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(Object.keys(config.permission!)).toEqual([
|
||||
"*",
|
||||
"edit",
|
||||
@@ -1451,7 +1451,7 @@ it.instance("project config can override MCP server enabled status", () =>
|
||||
"opencode.jsonc",
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.mcp?.jira).toEqual({
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
@@ -1496,7 +1496,7 @@ it.instance("MCP config deep merges preserving base config properties", () =>
|
||||
"opencode.jsonc",
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.mcp?.myserver).toEqual({
|
||||
type: "remote",
|
||||
url: "https://myserver.example.com/mcp",
|
||||
@@ -1537,7 +1537,7 @@ it.instance("local .opencode config can override MCP from project config", () =>
|
||||
"opencode.json",
|
||||
)
|
||||
|
||||
const config = yield* Config.Service.use((svc) => svc.get())
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.mcp?.docs?.enabled).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -93,7 +93,7 @@ it.instance("keeps server and tui plugin merge semantics aligned", () =>
|
||||
plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
|
||||
})
|
||||
|
||||
const server = yield* Config.Service.use((svc) => svc.get())
|
||||
const server = yield* Config.use.get()
|
||||
const tui = yield* getTuiConfig(test.directory)
|
||||
const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
||||
const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ describe("file fsmonitor", () => {
|
||||
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||
expect(before.exitCode).not.toBe(0)
|
||||
|
||||
yield* File.Service.use((svc) => svc.status())
|
||||
yield* File.use.status()
|
||||
|
||||
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||
expect(after.exitCode).not.toBe(0)
|
||||
@@ -63,7 +63,7 @@ describe("file fsmonitor", () => {
|
||||
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||
expect(before.exitCode).not.toBe(0)
|
||||
|
||||
yield* File.Service.use((svc) => svc.read("tracked.txt"))
|
||||
yield* File.use.read("tracked.txt")
|
||||
|
||||
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||
expect(after.exitCode).not.toBe(0)
|
||||
|
||||
@@ -10,8 +10,8 @@ import { TestInstance } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(File.defaultLayer)
|
||||
const read = (file: string) => File.Service.use((svc) => svc.read(file))
|
||||
const list = (dir?: string) => File.Service.use((svc) => svc.list(dir))
|
||||
const read = (file: string) => File.use.read(file)
|
||||
const list = (dir?: string) => File.use.list(dir)
|
||||
const expectAccessDenied = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
||||
Effect.gen(function* () {
|
||||
const exit = yield* effect.pipe(Effect.exit)
|
||||
|
||||
@@ -85,7 +85,7 @@ describe("file.ripgrep", () => {
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const value = 'other'\n"))
|
||||
|
||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" }))
|
||||
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle" })
|
||||
expect(result.partial).toBe(false)
|
||||
expect(result.items).toEqual([])
|
||||
}),
|
||||
@@ -100,7 +100,7 @@ describe("file.ripgrep", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" }))
|
||||
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle" })
|
||||
expect(result.partial).toBe(false)
|
||||
expect(result.items).toHaveLength(1)
|
||||
expect(result.items[0]?.path.text).toBe(path.join("src", "match.ts"))
|
||||
@@ -118,7 +118,7 @@ describe("file.ripgrep", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", glob: ["*.ts"] }))
|
||||
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle", glob: ["*.ts"] })
|
||||
expect(result.partial).toBe(false)
|
||||
expect(result.items).toHaveLength(1)
|
||||
expect(result.items[0]?.path.text).toContain("match.ts")
|
||||
@@ -136,7 +136,7 @@ describe("file.ripgrep", () => {
|
||||
)
|
||||
|
||||
const file = path.join(dir, "match.ts")
|
||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", file: [file] }))
|
||||
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle", file: [file] })
|
||||
expect(result.partial).toBe(false)
|
||||
expect(result.items).toHaveLength(1)
|
||||
expect(result.items[0]?.path.text).toBe(file)
|
||||
@@ -200,7 +200,7 @@ describe("file.ripgrep", () => {
|
||||
|
||||
const result = yield* withRipgrepConfig(
|
||||
path.join(dir, "missing-ripgreprc"),
|
||||
Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })),
|
||||
Ripgrep.use.search({ cwd: dir, pattern: "needle" }),
|
||||
)
|
||||
expect(result.items).toHaveLength(1)
|
||||
}),
|
||||
@@ -212,7 +212,7 @@ describe("file.ripgrep", () => {
|
||||
|
||||
const result = yield* withRipgrepConfig(
|
||||
path.join(dir, "missing-ripgreprc"),
|
||||
Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })),
|
||||
Ripgrep.use.search({ cwd: dir, pattern: "needle" }),
|
||||
)
|
||||
expect(result.items).toHaveLength(1)
|
||||
}),
|
||||
|
||||
@@ -133,7 +133,7 @@ describe("Format", () => {
|
||||
const file = `${dir}/test.txt`
|
||||
yield* Effect.promise(() => Bun.write(file, "x"))
|
||||
|
||||
const formatted = yield* Format.Service.use((fmt) => fmt.file(file))
|
||||
const formatted = yield* Format.use.file(file)
|
||||
expect(formatted).toBe(false)
|
||||
}),
|
||||
{
|
||||
@@ -146,10 +146,10 @@ describe("Format", () => {
|
||||
|
||||
it.live("status() initializes formatter state per directory", () =>
|
||||
Effect.gen(function* () {
|
||||
const a = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()), {
|
||||
const a = yield* provideTmpdirInstance(() => Format.use.status(), {
|
||||
config: { formatter: false },
|
||||
})
|
||||
const b = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()), {
|
||||
const b = yield* provideTmpdirInstance(() => Format.use.status(), {
|
||||
config: {
|
||||
formatter: true,
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ describe("installation", () => {
|
||||
"reads release version from GitHub releases",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("unknown"))
|
||||
const result = yield* Installation.use.latest("unknown")
|
||||
expect(result).toBe("1.2.3")
|
||||
}),
|
||||
)
|
||||
@@ -67,7 +67,7 @@ describe("installation", () => {
|
||||
"strips v prefix from GitHub release tag",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("curl"))
|
||||
const result = yield* Installation.use.latest("curl")
|
||||
expect(result).toBe("4.0.0-beta.1")
|
||||
}),
|
||||
)
|
||||
@@ -80,7 +80,7 @@ describe("installation", () => {
|
||||
}),
|
||||
).effect("reads npm versions via registry", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("npm"))
|
||||
const result = yield* Installation.use.latest("npm")
|
||||
expect(result).toBe("1.5.0")
|
||||
expect(npmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||
}),
|
||||
@@ -94,7 +94,7 @@ describe("installation", () => {
|
||||
}),
|
||||
).effect("reads bun versions via registry", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("bun"))
|
||||
const result = yield* Installation.use.latest("bun")
|
||||
expect(result).toBe("1.6.0")
|
||||
expect(bunCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||
}),
|
||||
@@ -108,7 +108,7 @@ describe("installation", () => {
|
||||
}),
|
||||
).effect("reads pnpm versions via registry", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("pnpm"))
|
||||
const result = yield* Installation.use.latest("pnpm")
|
||||
expect(result).toBe("1.7.0")
|
||||
expect(pnpmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||
}),
|
||||
@@ -116,7 +116,7 @@ describe("installation", () => {
|
||||
|
||||
testEffect(testLayer(() => jsonResponse({ version: "2.3.4" }))).effect("reads scoop manifest versions", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("scoop"))
|
||||
const result = yield* Installation.use.latest("scoop")
|
||||
expect(result).toBe("2.3.4")
|
||||
}),
|
||||
)
|
||||
@@ -125,7 +125,7 @@ describe("installation", () => {
|
||||
"reads chocolatey feed versions",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("choco"))
|
||||
const result = yield* Installation.use.latest("choco")
|
||||
expect(result).toBe("3.4.5")
|
||||
}),
|
||||
)
|
||||
@@ -142,7 +142,7 @@ describe("installation", () => {
|
||||
),
|
||||
).effect("reads brew formulae API versions", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("brew"))
|
||||
const result = yield* Installation.use.latest("brew")
|
||||
expect(result).toBe("2.0.0")
|
||||
}),
|
||||
)
|
||||
@@ -161,7 +161,7 @@ describe("installation", () => {
|
||||
),
|
||||
).effect("reads brew tap info JSON via CLI", () =>
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Installation.Service.use((svc) => svc.latest("brew"))
|
||||
const result = yield* Installation.use.latest("brew")
|
||||
expect(result).toBe("2.1.0")
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -175,7 +175,7 @@ mcpTest.instance("state() generates a new state when none is saved", () =>
|
||||
auth,
|
||||
)
|
||||
|
||||
const entryBefore = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen"))
|
||||
const entryBefore = yield* McpAuth.use.get("test-state-gen")
|
||||
expect(entryBefore?.oauthState).toBeUndefined()
|
||||
|
||||
// state() should generate and return a new state, not throw
|
||||
@@ -184,7 +184,7 @@ mcpTest.instance("state() generates a new state when none is saved", () =>
|
||||
expect(state.length).toBe(64) // 32 bytes as hex
|
||||
|
||||
// The generated state should be persisted
|
||||
const entryAfter = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen"))
|
||||
const entryAfter = yield* McpAuth.use.get("test-state-gen")
|
||||
expect(entryAfter?.oauthState).toBe(state)
|
||||
}),
|
||||
)
|
||||
@@ -202,7 +202,7 @@ mcpTest.instance("state() returns existing state when one is saved", () =>
|
||||
|
||||
// Pre-save a state
|
||||
const existingState = "pre-saved-state-value"
|
||||
yield* McpAuth.Service.use((auth) => auth.updateOAuthState("test-state-existing", existingState))
|
||||
yield* McpAuth.use.updateOAuthState("test-state-existing", existingState)
|
||||
|
||||
// state() should return the existing state
|
||||
const state = yield* Effect.promise(() => provider.state())
|
||||
|
||||
@@ -6,7 +6,7 @@ import { testEffect } from "./lib/effect"
|
||||
|
||||
const it = testEffect(Config.defaultLayer)
|
||||
|
||||
const load = Config.Service.use((svc) => svc.get())
|
||||
const load = Config.use.get()
|
||||
|
||||
describe("Permission.evaluate for permission.task", () => {
|
||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
|
||||
|
||||
@@ -72,10 +72,10 @@ describe("plugin.auth-override", () => {
|
||||
|
||||
const plain = yield* tmpdirScoped({ git: true })
|
||||
const plugin = pathToFileURL(path.join(pluginDir, "custom-copilot-auth.ts")).href
|
||||
const methods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe(
|
||||
const methods = yield* ProviderAuth.use.methods().pipe(
|
||||
Effect.provide(layer(tmp.directory, [plugin])),
|
||||
)
|
||||
const plainMethods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe(
|
||||
const plainMethods = yield* ProviderAuth.use.methods().pipe(
|
||||
Effect.provide(layer(plain, [])),
|
||||
provideInstance(plain),
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ const set = (k: string, v: string) =>
|
||||
Effect.gen(function* () {
|
||||
if (!originalEnv.has(k)) originalEnv.set(k, process.env[k])
|
||||
process.env[k] = v
|
||||
yield* Env.Service.use((svc) => svc.set(k, v))
|
||||
yield* Env.use.set(k, v)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -30,7 +30,7 @@ afterEach(async () => {
|
||||
await disposeAllInstances()
|
||||
})
|
||||
|
||||
const list = Provider.Service.use((svc) => svc.list())
|
||||
const list = Provider.use.list()
|
||||
|
||||
const withAuthJson = (contents: string) =>
|
||||
Effect.acquireRelease(
|
||||
|
||||
@@ -35,14 +35,14 @@ const set = (k: string, v: string) =>
|
||||
Effect.gen(function* () {
|
||||
rememberEnv(k)
|
||||
process.env[k] = v
|
||||
yield* Env.Service.use((svc) => svc.set(k, v))
|
||||
yield* Env.use.set(k, v)
|
||||
})
|
||||
|
||||
const remove = (k: string) =>
|
||||
Effect.gen(function* () {
|
||||
rememberEnv(k)
|
||||
delete process.env[k]
|
||||
yield* Env.Service.use((svc) => svc.remove(k))
|
||||
yield* Env.use.remove(k)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -332,7 +332,7 @@ test("parseModel handles model IDs with slashes", () => {
|
||||
it.instance("defaultModel returns first available model when no config set", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
||||
const model = yield* Provider.Service.use((provider) => provider.defaultModel())
|
||||
const model = yield* Provider.use.defaultModel()
|
||||
expect(model.providerID).toBeDefined()
|
||||
expect(model.modelID).toBeDefined()
|
||||
}),
|
||||
@@ -342,7 +342,7 @@ it.instance(
|
||||
"defaultModel respects config model setting",
|
||||
Effect.gen(function* () {
|
||||
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
||||
const model = yield* Provider.Service.use((provider) => provider.defaultModel())
|
||||
const model = yield* Provider.use.defaultModel()
|
||||
expect(String(model.providerID)).toBe("anthropic")
|
||||
expect(String(model.modelID)).toBe("claude-sonnet-4-20250514")
|
||||
}),
|
||||
@@ -1032,7 +1032,7 @@ it.instance("getProvider returns undefined for nonexistent provider", () =>
|
||||
it.instance("getProvider returns provider info", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||
const provider = yield* Provider.Service.use((svc) => svc.getProvider(ProviderID.anthropic))
|
||||
const provider = yield* Provider.use.getProvider(ProviderID.anthropic)
|
||||
expect(provider).toBeDefined()
|
||||
expect(String(provider?.id)).toBe("anthropic")
|
||||
}),
|
||||
@@ -1680,7 +1680,7 @@ it.effect("opencode loader keeps paid models when config apiKey is present", ()
|
||||
})
|
||||
|
||||
const listIn = (directory: string) =>
|
||||
Provider.Service.use((svc) => svc.list())
|
||||
Provider.use.list()
|
||||
.pipe(provideInstanceEffect(directory))
|
||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
@@ -1698,7 +1698,7 @@ it.effect("opencode loader keeps paid models when auth exists", () =>
|
||||
const keyedDir = yield* tmpdirScoped()
|
||||
|
||||
const listIn = (directory: string) =>
|
||||
Provider.Service.use((svc) => svc.list())
|
||||
Provider.use.list()
|
||||
.pipe(provideInstanceEffect(directory))
|
||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLaye
|
||||
|
||||
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||
Effect.acquireRelease(
|
||||
SessionNs.Service.use((session) => session.create(input)),
|
||||
SessionNs.use.create(input),
|
||||
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
||||
)
|
||||
|
||||
@@ -34,8 +34,8 @@ describe("session.listGlobal", () => {
|
||||
expect(ids).toContain(firstSession.id)
|
||||
expect(ids).toContain(secondSession.id)
|
||||
|
||||
const firstProject = yield* Project.Service.use((project) => project.get(firstSession.projectID))
|
||||
const secondProject = yield* Project.Service.use((project) => project.get(secondSession.projectID))
|
||||
const firstProject = yield* Project.use.get(firstSession.projectID)
|
||||
const secondProject = yield* Project.use.get(secondSession.projectID)
|
||||
|
||||
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
||||
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
||||
|
||||
@@ -31,7 +31,7 @@ function request(path: string, directory: string, init: RequestInit = {}) {
|
||||
}
|
||||
|
||||
function createSession(input?: Session.CreateInput) {
|
||||
return Session.Service.use((svc) => svc.create(input))
|
||||
return Session.use.create(input)
|
||||
}
|
||||
|
||||
function json<T>(response: Response) {
|
||||
|
||||
@@ -74,7 +74,7 @@ const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: stri
|
||||
projectID: input.projectID,
|
||||
})
|
||||
}),
|
||||
(info) => Workspace.Service.use((workspace) => workspace.remove(info.id)).pipe(Effect.ignore),
|
||||
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
const probeInstanceContext = Effect.gen(function* () {
|
||||
|
||||
@@ -52,7 +52,7 @@ function pathFor(path: string, params: Record<string, string>) {
|
||||
}
|
||||
|
||||
function createSession(input?: Session.CreateInput) {
|
||||
return Session.Service.use((svc) => svc.create(input))
|
||||
return Session.use.create(input)
|
||||
}
|
||||
|
||||
function createTextMessage(sessionID: SessionIDType, text: string) {
|
||||
@@ -101,7 +101,7 @@ const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: stri
|
||||
}),
|
||||
)
|
||||
}),
|
||||
(info) => Workspace.Service.use((svc) => svc.remove(info.id)).pipe(Effect.ignore),
|
||||
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
const insertLegacyAssistantMessage = (sessionID: SessionIDType, time = 1) =>
|
||||
|
||||
@@ -36,7 +36,7 @@ describe("sync HttpApi", () => {
|
||||
const tmp = yield* TestInstance
|
||||
const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
|
||||
const info = spyOn(Log.create({ service: "server.sync" }), "info")
|
||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "sync" }))
|
||||
const session = yield* Session.use.create({ title: "sync" })
|
||||
|
||||
const started = yield* Effect.promise(() =>
|
||||
Promise.resolve(app().request(SyncPaths.start, { method: "POST", headers })),
|
||||
|
||||
@@ -128,7 +128,7 @@ const createWorkspace = (input: { projectID: Project.Info["id"]; type: string; a
|
||||
projectID: input.projectID,
|
||||
})
|
||||
}),
|
||||
(info) => Workspace.Service.use((workspace) => workspace.remove(info.id)).pipe(Effect.ignore),
|
||||
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
const createRemoteWorkspace = (input: {
|
||||
|
||||
@@ -208,7 +208,7 @@ describe("workspace HttpApi", () => {
|
||||
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
||||
expect(workspace).toMatchObject({ type: "local-test", name: "local-test" })
|
||||
|
||||
const session = yield* Session.Service.use((svc) => svc.create({})).pipe(provideInstance(dir))
|
||||
const session = yield* Session.use.create({}).pipe(provideInstance(dir))
|
||||
const warped = yield* request(WorkspacePaths.warp, dir, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
@@ -424,7 +424,7 @@ describe("workspace HttpApi", () => {
|
||||
body: JSON.stringify({ type: "remote-session-target", branch: null }),
|
||||
})
|
||||
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
||||
const session = yield* Session.Service.use((svc) => svc.create()).pipe(
|
||||
const session = yield* Session.use.create().pipe(
|
||||
Effect.provideService(WorkspaceRef, workspace.id),
|
||||
provideInstance(dir),
|
||||
)
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("project.initGit endpoint", () => {
|
||||
worktree: tmp.directory,
|
||||
})
|
||||
|
||||
const ctx = yield* InstanceStore.Service.use((store) => store.reload({ directory: tmp.directory }))
|
||||
const ctx = yield* InstanceStore.use.reload({ directory: tmp.directory })
|
||||
const tracked = yield* Snapshot.Service.use((snapshot) => snapshot.track()).pipe(
|
||||
Effect.provideService(InstanceRef, ctx),
|
||||
)
|
||||
|
||||
@@ -22,8 +22,8 @@ describe("session action routes", () => {
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
const session = yield* Effect.acquireRelease(
|
||||
SessionNs.Service.use((svc) => svc.create({})),
|
||||
(created) => SessionNs.Service.use((svc) => svc.remove(created.id)).pipe(Effect.ignore),
|
||||
SessionNs.use.create({}),
|
||||
(created) => SessionNs.use.remove(created.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
const res = yield* Effect.promise(() =>
|
||||
|
||||
@@ -35,8 +35,8 @@ function pathFor(template: string, params: Record<string, string>) {
|
||||
|
||||
const withSession = (input?: Parameters<Session.Interface["create"]>[0]) =>
|
||||
Effect.acquireRelease(
|
||||
Session.Service.use((session) => session.create(input)),
|
||||
(created) => Session.Service.use((session) => session.remove(created.id)).pipe(Effect.ignore),
|
||||
Session.use.create(input),
|
||||
(created) => Session.use.remove(created.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
describe("session diff with missing patch (#26574)", () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ const it = testEffect(
|
||||
|
||||
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||
Effect.acquireRelease(
|
||||
SessionNs.Service.use((session) => session.create(input)),
|
||||
SessionNs.use.create(input),
|
||||
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ describe("session.list", () => {
|
||||
provideInstance(path.join(test.directory, "packages", "app")),
|
||||
)
|
||||
|
||||
const ids = (yield* SessionNs.Service.use((session) => session.list())).map((session) => session.id)
|
||||
const ids = (yield* SessionNs.use.list()).map((session) => session.id)
|
||||
expect(ids).toContain(root.id)
|
||||
expect(ids).toContain(parent.id)
|
||||
expect(ids).toContain(current.id)
|
||||
@@ -179,7 +179,7 @@ describe("session.list", () => {
|
||||
const root = yield* withSession({ title: "root-session" })
|
||||
const child = yield* withSession({ title: "child-session", parentID: root.id })
|
||||
|
||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ roots: true }))
|
||||
const sessions = yield* SessionNs.use.list({ roots: true })
|
||||
const ids = sessions.map((session) => session.id)
|
||||
|
||||
expect(ids).toContain(root.id)
|
||||
@@ -206,7 +206,7 @@ describe("session.list", () => {
|
||||
yield* withSession({ title: "unique-search-term-abc" })
|
||||
yield* withSession({ title: "other-session-xyz" })
|
||||
|
||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ search: "unique-search" }))
|
||||
const sessions = yield* SessionNs.use.list({ search: "unique-search" })
|
||||
const titles = sessions.map((session) => session.title)
|
||||
|
||||
expect(titles).toContain("unique-search-term-abc")
|
||||
@@ -223,7 +223,7 @@ describe("session.list", () => {
|
||||
yield* withSession({ title: "session-2" })
|
||||
yield* withSession({ title: "session-3" })
|
||||
|
||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ limit: 2 }))
|
||||
const sessions = yield* SessionNs.use.list({ limit: 2 })
|
||||
expect(sessions.length).toBe(2)
|
||||
}),
|
||||
{ git: true },
|
||||
|
||||
@@ -40,8 +40,8 @@ const withoutWatcher = <A, E, R>(effect: Effect.Effect<A, E, R>) => {
|
||||
}
|
||||
|
||||
const sessionScoped = Effect.acquireRelease(
|
||||
SessionNs.Service.use((svc) => svc.create({})),
|
||||
(session) => SessionNs.Service.use((svc) => svc.remove(session.id)).pipe(Effect.ignore),
|
||||
SessionNs.use.create({}),
|
||||
(session) => SessionNs.use.remove(session.id).pipe(Effect.ignore),
|
||||
)
|
||||
|
||||
const fill = Effect.fn("SessionMessagesTest.fill")(function* (
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("tui.selectSession endpoint", () => {
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const tmp = yield* TestInstance
|
||||
const session = yield* Session.Service.use((svc) => svc.create({}))
|
||||
const session = yield* Session.use.create({})
|
||||
|
||||
const app = Server.Default().app
|
||||
const response = yield* Effect.promise(() =>
|
||||
|
||||
@@ -292,7 +292,7 @@ function createSummaryCompaction(sessionID: SessionID) {
|
||||
}
|
||||
|
||||
function readCompactionPart(sessionID: SessionID) {
|
||||
return SessionNs.Service.use((ssn) => ssn.messages({ sessionID })).pipe(
|
||||
return SessionNs.use.messages({ sessionID }).pipe(
|
||||
Effect.map((messages) =>
|
||||
messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"),
|
||||
),
|
||||
|
||||
@@ -35,7 +35,7 @@ const awaitDeferred = <T>(deferred: Deferred.Deferred<T>, message: string) =>
|
||||
Effect.sleep("2 seconds").pipe(Effect.flatMap(() => Effect.fail(new Error(message)))),
|
||||
)
|
||||
|
||||
const remove = (id: SessionID) => SessionNs.Service.use((svc) => svc.remove(id))
|
||||
const remove = (id: SessionID) => SessionNs.use.remove(id)
|
||||
|
||||
const subscribeGlobal = (type: string, callback: (event: NonNullable<GlobalEvent["payload"]>) => void) => {
|
||||
const listener = (event: GlobalEvent) => {
|
||||
|
||||
@@ -128,7 +128,7 @@ describe("ShareNext", () => {
|
||||
Effect.gen(function* () {
|
||||
yield* seed("https://control.example.com", "org-1")
|
||||
|
||||
const req = yield* ShareNext.Service.use((svc) => svc.request()).pipe(Effect.provide(live(none)))
|
||||
const req = yield* ShareNext.use.request().pipe(Effect.provide(live(none)))
|
||||
|
||||
expect(req.api.create).toBe("/api/shares")
|
||||
expect(req.api.sync("shr_123")).toBe("/api/shares/shr_123/sync")
|
||||
@@ -147,7 +147,7 @@ describe("ShareNext", () => {
|
||||
provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
||||
const session = yield* Session.use.create({ title: "test" })
|
||||
const seen: HttpClientRequest.HttpClientRequest[] = []
|
||||
const client = HttpClient.make((req) => {
|
||||
seen.push(req)
|
||||
@@ -163,7 +163,7 @@ describe("ShareNext", () => {
|
||||
return Effect.succeed(json(req, { ok: true }))
|
||||
})
|
||||
|
||||
const result = yield* ShareNext.Service.use((svc) => svc.create(session.id)).pipe(
|
||||
const result = yield* ShareNext.use.create(session.id).pipe(
|
||||
Effect.provide(live(client)),
|
||||
)
|
||||
|
||||
@@ -188,7 +188,7 @@ describe("ShareNext", () => {
|
||||
provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
||||
const session = yield* Session.use.create({ title: "test" })
|
||||
const seen: HttpClientRequest.HttpClientRequest[] = []
|
||||
const client = HttpClient.make((req) => {
|
||||
seen.push(req)
|
||||
@@ -205,8 +205,8 @@ describe("ShareNext", () => {
|
||||
})
|
||||
|
||||
yield* Effect.gen(function* () {
|
||||
yield* ShareNext.Service.use((svc) => svc.create(session.id))
|
||||
yield* ShareNext.Service.use((svc) => svc.remove(session.id))
|
||||
yield* ShareNext.use.create(session.id)
|
||||
yield* ShareNext.use.remove(session.id)
|
||||
}).pipe(Effect.provide(live(client)))
|
||||
|
||||
expect(share(session.id)).toBeUndefined()
|
||||
@@ -222,7 +222,7 @@ describe("ShareNext", () => {
|
||||
it.live("create fails on a non-ok response and does not persist a share", () =>
|
||||
provideTmpdirInstance(() =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
||||
const session = yield* Session.use.create({ title: "test" })
|
||||
const client = HttpClient.make((req) => Effect.succeed(json(req, { error: "bad" }, 500)))
|
||||
|
||||
const exit = yield* ShareNext.Service.use((svc) => Effect.exit(svc.create(session.id))).pipe(
|
||||
|
||||
Reference in New Issue
Block a user