Skip to content

Commit 2f405da

Browse files
authored
refactor: use Effect services instead of async facades in provider, auth, and file (#20480)
1 parent a9c85b7 commit 2f405da

File tree

6 files changed

+86
-89
lines changed

6 files changed

+86
-89
lines changed

bun.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"packages/slack"
2626
],
2727
"catalog": {
28-
"@effect/platform-node": "4.0.0-beta.42",
28+
"@effect/platform-node": "4.0.0-beta.43",
2929
"@types/bun": "1.3.11",
3030
"@octokit/rest": "22.0.0",
3131
"@hono/zod-validator": "0.4.2",
@@ -45,7 +45,7 @@
4545
"dompurify": "3.3.1",
4646
"drizzle-kit": "1.0.0-beta.19-d95b7a4",
4747
"drizzle-orm": "1.0.0-beta.19-d95b7a4",
48-
"effect": "4.0.0-beta.42",
48+
"effect": "4.0.0-beta.43",
4949
"ai": "6.0.138",
5050
"hono": "4.10.7",
5151
"hono-openapi": "1.1.2",

packages/opencode/src/effect/instance-state.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ export namespace InstanceState {
2424
return ((...args: any[]) => Instance.restore(ctx, () => fn(...args))) as F
2525
}
2626

27-
export const context = Effect.fnUntraced(function* () {
27+
export const context = Effect.gen(function* () {
2828
return (yield* InstanceRef) ?? Instance.current
29-
})()
29+
})
3030

3131
export const directory = Effect.map(context, (ctx) => ctx.directory)
3232

@@ -37,9 +37,9 @@ export namespace InstanceState {
3737
const cache = yield* ScopedCache.make<string, A, E, R>({
3838
capacity: Number.POSITIVE_INFINITY,
3939
lookup: () =>
40-
Effect.fnUntraced(function* () {
40+
Effect.gen(function* () {
4141
return yield* init(yield* context)
42-
})(),
42+
}),
4343
})
4444

4545
const off = registerDisposer((directory) => Effect.runPromise(ScopedCache.invalidate(cache, directory)))

packages/opencode/src/file/index.ts

Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { AppFileSystem } from "@/filesystem"
55
import { git } from "@/util/git"
66
import { Effect, Layer, ServiceMap } from "effect"
77
import { formatPatch, structuredPatch } from "diff"
8-
import fs from "fs"
98
import fuzzysort from "fuzzysort"
109
import ignore from "ignore"
1110
import path from "path"
@@ -359,49 +358,46 @@ export namespace File {
359358
const isGlobalHome = Instance.directory === Global.Path.home && Instance.project.id === "global"
360359
const next: Entry = { files: [], dirs: [] }
361360

362-
yield* Effect.promise(async () => {
363-
if (isGlobalHome) {
364-
const dirs = new Set<string>()
365-
const protectedNames = Protected.names()
366-
const ignoreNested = new Set(["node_modules", "dist", "build", "target", "vendor"])
367-
const shouldIgnoreName = (name: string) => name.startsWith(".") || protectedNames.has(name)
368-
const shouldIgnoreNested = (name: string) => name.startsWith(".") || ignoreNested.has(name)
369-
const top = await fs.promises
370-
.readdir(Instance.directory, { withFileTypes: true })
371-
.catch(() => [] as fs.Dirent[])
372-
373-
for (const entry of top) {
374-
if (!entry.isDirectory()) continue
375-
if (shouldIgnoreName(entry.name)) continue
376-
dirs.add(entry.name + "/")
377-
378-
const base = path.join(Instance.directory, entry.name)
379-
const children = await fs.promises.readdir(base, { withFileTypes: true }).catch(() => [] as fs.Dirent[])
380-
for (const child of children) {
381-
if (!child.isDirectory()) continue
382-
if (shouldIgnoreNested(child.name)) continue
383-
dirs.add(entry.name + "/" + child.name + "/")
384-
}
361+
if (isGlobalHome) {
362+
const dirs = new Set<string>()
363+
const protectedNames = Protected.names()
364+
const ignoreNested = new Set(["node_modules", "dist", "build", "target", "vendor"])
365+
const shouldIgnoreName = (name: string) => name.startsWith(".") || protectedNames.has(name)
366+
const shouldIgnoreNested = (name: string) => name.startsWith(".") || ignoreNested.has(name)
367+
const top = yield* appFs.readDirectoryEntries(Instance.directory).pipe(Effect.orElseSucceed(() => []))
368+
369+
for (const entry of top) {
370+
if (entry.type !== "directory") continue
371+
if (shouldIgnoreName(entry.name)) continue
372+
dirs.add(entry.name + "/")
373+
374+
const base = path.join(Instance.directory, entry.name)
375+
const children = yield* appFs.readDirectoryEntries(base).pipe(Effect.orElseSucceed(() => []))
376+
for (const child of children) {
377+
if (child.type !== "directory") continue
378+
if (shouldIgnoreNested(child.name)) continue
379+
dirs.add(entry.name + "/" + child.name + "/")
385380
}
381+
}
386382

387-
next.dirs = Array.from(dirs).toSorted()
388-
} else {
389-
const seen = new Set<string>()
390-
for await (const file of Ripgrep.files({ cwd: Instance.directory })) {
391-
next.files.push(file)
392-
let current = file
393-
while (true) {
394-
const dir = path.dirname(current)
395-
if (dir === ".") break
396-
if (dir === current) break
397-
current = dir
398-
if (seen.has(dir)) continue
399-
seen.add(dir)
400-
next.dirs.push(dir + "/")
401-
}
383+
next.dirs = Array.from(dirs).toSorted()
384+
} else {
385+
const files = yield* Effect.promise(() => Array.fromAsync(Ripgrep.files({ cwd: Instance.directory })))
386+
const seen = new Set<string>()
387+
for (const file of files) {
388+
next.files.push(file)
389+
let current = file
390+
while (true) {
391+
const dir = path.dirname(current)
392+
if (dir === ".") break
393+
if (dir === current) break
394+
current = dir
395+
if (seen.has(dir)) continue
396+
seen.add(dir)
397+
next.dirs.push(dir + "/")
402398
}
403399
}
404-
})
400+
}
405401

406402
const s = yield* InstanceState.get(state)
407403
s.cache = next
@@ -636,30 +632,27 @@ export namespace File {
636632
yield* ensure()
637633
const { cache } = yield* InstanceState.get(state)
638634

639-
return yield* Effect.promise(async () => {
640-
const query = input.query.trim()
641-
const limit = input.limit ?? 100
642-
const kind = input.type ?? (input.dirs === false ? "file" : "all")
643-
log.info("search", { query, kind })
635+
const query = input.query.trim()
636+
const limit = input.limit ?? 100
637+
const kind = input.type ?? (input.dirs === false ? "file" : "all")
638+
log.info("search", { query, kind })
644639

645-
const result = cache
646-
const preferHidden = query.startsWith(".") || query.includes("/.")
640+
const preferHidden = query.startsWith(".") || query.includes("/.")
647641

648-
if (!query) {
649-
if (kind === "file") return result.files.slice(0, limit)
650-
return sortHiddenLast(result.dirs.toSorted(), preferHidden).slice(0, limit)
651-
}
642+
if (!query) {
643+
if (kind === "file") return cache.files.slice(0, limit)
644+
return sortHiddenLast(cache.dirs.toSorted(), preferHidden).slice(0, limit)
645+
}
652646

653-
const items =
654-
kind === "file" ? result.files : kind === "directory" ? result.dirs : [...result.files, ...result.dirs]
647+
const items =
648+
kind === "file" ? cache.files : kind === "directory" ? cache.dirs : [...cache.files, ...cache.dirs]
655649

656-
const searchLimit = kind === "directory" && !preferHidden ? limit * 20 : limit
657-
const sorted = fuzzysort.go(query, items, { limit: searchLimit }).map((item) => item.target)
658-
const output = kind === "directory" ? sortHiddenLast(sorted, preferHidden).slice(0, limit) : sorted
650+
const searchLimit = kind === "directory" && !preferHidden ? limit * 20 : limit
651+
const sorted = fuzzysort.go(query, items, { limit: searchLimit }).map((item) => item.target)
652+
const output = kind === "directory" ? sortHiddenLast(sorted, preferHidden).slice(0, limit) : sorted
659653

660-
log.info("search", { query, kind, results: output.length })
661-
return output
662-
})
654+
log.info("search", { query, kind, results: output.length })
655+
return output
663656
})
664657

665658
log.info("init")

packages/opencode/src/provider/auth.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -111,26 +111,25 @@ export namespace ProviderAuth {
111111

112112
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/ProviderAuth") {}
113113

114-
export const layer = Layer.effect(
114+
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
115115
Service,
116116
Effect.gen(function* () {
117117
const auth = yield* Auth.Service
118+
const plugin = yield* Plugin.Service
118119
const state = yield* InstanceState.make<State>(
119-
Effect.fn("ProviderAuth.state")(() =>
120-
Effect.promise(async () => {
121-
const plugins = await Plugin.list()
122-
return {
123-
hooks: Record.fromEntries(
124-
Arr.filterMap(plugins, (x) =>
125-
x.auth?.provider !== undefined
126-
? Result.succeed([ProviderID.make(x.auth.provider), x.auth] as const)
127-
: Result.failVoid,
128-
),
120+
Effect.fn("ProviderAuth.state")(function* () {
121+
const plugins = yield* plugin.list()
122+
return {
123+
hooks: Record.fromEntries(
124+
Arr.filterMap(plugins, (x) =>
125+
x.auth?.provider !== undefined
126+
? Result.succeed([ProviderID.make(x.auth.provider), x.auth] as const)
127+
: Result.failVoid,
129128
),
130-
pending: new Map<ProviderID, AuthOAuthResult>(),
131-
}
132-
}),
133-
),
129+
),
130+
pending: new Map<ProviderID, AuthOAuthResult>(),
131+
}
132+
}),
134133
)
135134

136135
const methods = Effect.fn("ProviderAuth.methods")(function* () {
@@ -230,7 +229,9 @@ export namespace ProviderAuth {
230229
}),
231230
)
232231

233-
export const defaultLayer = layer.pipe(Layer.provide(Auth.defaultLayer))
232+
export const defaultLayer = Layer.suspend(() =>
233+
layer.pipe(Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer)),
234+
)
234235

235236
const { runPromise } = makeRuntime(Service, defaultLayer)
236237

packages/opencode/src/provider/provider.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -961,11 +961,12 @@ export namespace Provider {
961961
}
962962
}
963963

964-
const layer: Layer.Layer<Service, never, Config.Service | Auth.Service> = Layer.effect(
964+
const layer: Layer.Layer<Service, never, Config.Service | Auth.Service | Plugin.Service> = Layer.effect(
965965
Service,
966966
Effect.gen(function* () {
967967
const config = yield* Config.Service
968968
const auth = yield* Auth.Service
969+
const plugin = yield* Plugin.Service
969970

970971
const state = yield* InstanceState.make<State>(() =>
971972
Effect.gen(function* () {
@@ -1128,7 +1129,7 @@ export namespace Provider {
11281129
}
11291130
}
11301131

1131-
const plugins = yield* Effect.promise(() => Plugin.list())
1132+
const plugins = yield* plugin.list()
11321133
for (const plugin of plugins) {
11331134
if (!plugin.auth) continue
11341135
const providerID = ProviderID.make(plugin.auth.provider)
@@ -1541,7 +1542,9 @@ export namespace Provider {
15411542
}),
15421543
)
15431544

1544-
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Auth.defaultLayer))
1545+
export const defaultLayer = Layer.suspend(() =>
1546+
layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer)),
1547+
)
15451548

15461549
const { runPromise } = makeRuntime(Service, defaultLayer)
15471550

0 commit comments

Comments
 (0)