every-plugin
Version:
82 lines (73 loc) • 2.78 kB
text/typescript
import { Effect, Exit, Ref, Scope } from "effect";
import type { AnyPlugin, InitializedPlugin } from "../../types";
import { toPluginRuntimeError } from "../errors";
export class PluginLifecycleService extends Effect.Service<PluginLifecycleService>()(
"PluginLifecycleService",
{
effect: Effect.gen(function* () {
const activePlugins = yield* Ref.make(new Set<InitializedPlugin<AnyPlugin>>());
return {
register: <T extends AnyPlugin>(plugin: InitializedPlugin<T>) =>
Ref.update(activePlugins, (plugins) =>
new Set(plugins).add(plugin as InitializedPlugin<AnyPlugin>),
),
unregister: (plugin: InitializedPlugin<AnyPlugin>) =>
Ref.update(activePlugins, (plugins) => {
const newSet = new Set(plugins);
newSet.delete(plugin);
return newSet;
}),
shutdown: (plugin: InitializedPlugin<AnyPlugin>) =>
Effect.gen(function* () {
// Remove from active plugins
yield* Ref.update(activePlugins, (plugins) => {
const newSet = new Set(plugins);
newSet.delete(plugin);
return newSet;
});
// Shutdown the plugin
yield* plugin.plugin
.shutdown()
.pipe(
Effect.mapError((error) =>
toPluginRuntimeError(
error,
plugin.plugin.id,
undefined,
"shutdown-plugin",
false,
),
),
);
}),
cleanup: () =>
Effect.gen(function* () {
const plugins = yield* Ref.get(activePlugins);
yield* Effect.forEach(
plugins,
(plugin) =>
Effect.gen(function* () {
yield* plugin.plugin
.shutdown()
.pipe(
Effect.catchAll((error) =>
Effect.logWarning(`Failed to shutdown plugin ${plugin.plugin.id}`, error),
),
);
yield* Scope.close(plugin.scope, Exit.succeed(undefined)).pipe(
Effect.catchAll((error) =>
Effect.logWarning(
`Failed to close scope for plugin ${plugin.plugin.id}`,
error,
),
),
);
}),
{ concurrency: "unbounded" },
);
yield* Ref.set(activePlugins, new Set());
}).pipe(Effect.catchAll((error) => Effect.logWarning("Plugin cleanup failed", error))),
};
}),
},
) {}