feat(plugin): add namespaced hook API (#33416)

This commit is contained in:
Dax
2026-06-22 19:06:57 -04:00
committed by GitHub
parent dc468bdcfd
commit 909a1a6d78
150 changed files with 3286 additions and 3916 deletions
+15 -15
View File
@@ -3,7 +3,7 @@ export * as State from "./state"
import { Context, Effect, Scope, Semaphore } from "effect"
/**
* A replayable transform applied to a draft during rebuild.
* A replayable transform applied to a draft during reload.
*
* Domain drafts expose readable and writable state while preserving concise
* plugin/config code. Transforms may perform Effects before returning.
@@ -19,14 +19,14 @@ export type Transform<DraftApi> = (
transform: TransformCallback<DraftApi>,
) => Effect.Effect<Registration, never, Scope.Scope>
export type Rebuild = () => Effect.Effect<void>
export type Reload = () => Effect.Effect<void>
export interface Transformable<DraftApi> {
readonly transform: Transform<DraftApi>
readonly rebuild: Rebuild
readonly reload: Reload
}
const CurrentBatch = Context.Reference<Set<Rebuild> | undefined>("@opencode/State/CurrentBatch", {
const CurrentBatch = Context.Reference<Set<Reload> | undefined>("@opencode/State/CurrentBatch", {
defaultValue: () => undefined,
})
@@ -34,15 +34,15 @@ export function batch<A, E, R>(effect: Effect.Effect<A, E, R>) {
return Effect.gen(function* () {
const current = yield* CurrentBatch
if (current) return yield* effect
const rebuilds = new Set<Rebuild>()
const result = yield* effect.pipe(Effect.provideService(CurrentBatch, rebuilds))
yield* Effect.forEach(rebuilds, (rebuild) => rebuild(), { discard: true })
const reloads = new Set<Reload>()
const result = yield* effect.pipe(Effect.provideService(CurrentBatch, reloads))
yield* Effect.forEach(reloads, (reload) => reload(), { discard: true })
return result
})
}
export interface Options<State, DraftApi> {
/** Creates the base value for initial state and every scoped-transform rebuild. */
/** Creates the base value for initial state and every scoped-transform reload. */
readonly initial: () => State
/** Wraps mutable state in a domain-specific draft API. */
readonly draft: MakeDraft<State, DraftApi>
@@ -54,7 +54,7 @@ export interface Interface<State, DraftApi> extends Transformable<DraftApi> {
readonly get: () => State
/**
* Registers and applies a scoped transform. Closing the owning Scope removes
* the transform and rebuilds the materialized state.
* the transform and reloads the materialized state.
*/
}
@@ -78,11 +78,11 @@ export function create<State, DraftApi>(options: Options<State, DraftApi>): Inte
const materialize = Effect.fnUntraced(function* () {
const next = options.initial()
const api = options.draft(next)
for (const transform of transforms) yield* apply(transform.run, api).pipe(Effect.withSpan("State.rebuild.update"))
for (const transform of transforms) yield* apply(transform.run, api).pipe(Effect.withSpan("State.reload.update"))
yield* commit(next)
})
const rebuild = () => semaphore.withPermit(materialize())
const reload = () => semaphore.withPermit(materialize())
const result: Interface<State, DraftApi> = {
get: () => state,
@@ -101,7 +101,7 @@ export function create<State, DraftApi>(options: Options<State, DraftApi>): Inte
return Effect.gen(function* () {
const batch = yield* CurrentBatch
if (batch) {
batch.add(rebuild)
batch.add(reload)
return
}
yield* materialize()
@@ -116,13 +116,13 @@ export function create<State, DraftApi>(options: Options<State, DraftApi>): Inte
)
yield* Scope.addFinalizer(scope, dispose)
const batch = yield* CurrentBatch
if (batch) batch.add(rebuild)
else yield* rebuild()
if (batch) batch.add(reload)
else yield* reload()
return { dispose }
}),
)
}),
rebuild,
reload,
}
return result
}