refactor(state): replace ScopedState with InstanceState
Replace the generic ScopedState (keyed by caller-provided root) with InstanceState that hardcodes Instance.directory and integrates with the instance dispose/reload lifecycle via a global task registry. - Parallelize State + InstanceState disposal in reload/dispose - Use Effect's Record.map and Struct.pick in auth-service - Flatten nested Effect.gen in OAuth callback flow - Add docstrings to ProviderAuthService interface - Add State and InstanceState tests
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { Effect, ScopedCache, Scope } from "effect"
|
||||
|
||||
import { Instance } from "@/project/instance"
|
||||
|
||||
const TypeId = Symbol.for("@opencode/InstanceState")
|
||||
|
||||
type Task = (key: string) => Effect.Effect<void>
|
||||
|
||||
const tasks = new Set<Task>()
|
||||
|
||||
export interface InstanceState<A, E = never, R = never> {
|
||||
readonly [TypeId]: typeof TypeId
|
||||
readonly cache: ScopedCache.ScopedCache<string, A, E, R>
|
||||
}
|
||||
|
||||
export const make = <A, E = never, R = never>(input: {
|
||||
lookup: (key: string) => Effect.Effect<A, E, R>
|
||||
release?: (value: A, key: string) => Effect.Effect<void>
|
||||
}): Effect.Effect<InstanceState<A, E, R>, never, R | Scope.Scope> =>
|
||||
Effect.gen(function* () {
|
||||
const cache = yield* ScopedCache.make<string, A, E, R>({
|
||||
capacity: Number.POSITIVE_INFINITY,
|
||||
lookup: (key) =>
|
||||
Effect.acquireRelease(input.lookup(key), (value) => (input.release ? input.release(value, key) : Effect.void)),
|
||||
})
|
||||
|
||||
const task: Task = (key) => ScopedCache.invalidate(cache, key)
|
||||
tasks.add(task)
|
||||
yield* Effect.addFinalizer(() => Effect.sync(() => void tasks.delete(task)))
|
||||
|
||||
return {
|
||||
[TypeId]: TypeId,
|
||||
cache,
|
||||
}
|
||||
})
|
||||
|
||||
export const get = <A, E, R>(self: InstanceState<A, E, R>) => ScopedCache.get(self.cache, Instance.directory)
|
||||
|
||||
export const has = <A, E, R>(self: InstanceState<A, E, R>) => ScopedCache.has(self.cache, Instance.directory)
|
||||
|
||||
export const invalidate = <A, E, R>(self: InstanceState<A, E, R>) =>
|
||||
ScopedCache.invalidate(self.cache, Instance.directory)
|
||||
|
||||
export const dispose = (key: string) =>
|
||||
Effect.all(
|
||||
[...tasks].map((task) => task(key)),
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Effect, ScopedCache, Scope } from "effect"
|
||||
|
||||
const TypeId = Symbol.for("@opencode/ScopedState")
|
||||
|
||||
export interface ScopedState<A, E = never, R = never> {
|
||||
readonly [TypeId]: typeof TypeId
|
||||
readonly root: () => string
|
||||
readonly cache: ScopedCache.ScopedCache<string, A, E, R>
|
||||
}
|
||||
|
||||
export const make = <A, E = never, R = never>(input: {
|
||||
root: () => string
|
||||
lookup: (key: string) => Effect.Effect<A, E, R>
|
||||
release?: (value: A, key: string) => Effect.Effect<void>
|
||||
}): Effect.Effect<ScopedState<A, E, R>, never, Scope.Scope | R> =>
|
||||
ScopedCache.make<string, A, E, R>({
|
||||
capacity: Number.POSITIVE_INFINITY,
|
||||
lookup: (key) =>
|
||||
Effect.acquireRelease(input.lookup(key), (value) => (input.release ? input.release(value, key) : Effect.void)),
|
||||
}).pipe(
|
||||
Effect.map((cache) => ({
|
||||
[TypeId]: TypeId,
|
||||
root: input.root,
|
||||
cache,
|
||||
})),
|
||||
)
|
||||
|
||||
export const get = <A, E, R>(self: ScopedState<A, E, R>) => ScopedCache.get(self.cache, self.root())
|
||||
|
||||
export const getAt = <A, E, R>(self: ScopedState<A, E, R>, key: string) => ScopedCache.get(self.cache, key)
|
||||
|
||||
export const invalidate = <A, E, R>(self: ScopedState<A, E, R>) => ScopedCache.invalidate(self.cache, self.root())
|
||||
|
||||
export const invalidateAt = <A, E, R>(self: ScopedState<A, E, R>, key: string) =>
|
||||
ScopedCache.invalidate(self.cache, key)
|
||||
|
||||
export const has = <A, E, R>(self: ScopedState<A, E, R>) => ScopedCache.has(self.cache, self.root())
|
||||
|
||||
export const hasAt = <A, E, R>(self: ScopedState<A, E, R>, key: string) => ScopedCache.has(self.cache, key)
|
||||
Reference in New Issue
Block a user