UNPKG

every-plugin

Version:
128 lines (126 loc) 5.22 kB
import { PluginRuntimeError, toPluginRuntimeError } from "../errors.mjs"; import { ModuleFederationService } from "./module-federation.service.mjs"; import { validate } from "../validation.mjs"; import { SecretsService } from "./secrets.service.mjs"; import { Context, Effect, Scope } from "effect"; //#region src/runtime/services/plugin-loader.service.ts var PluginRegistryTag = class extends Context.Tag("PluginRegistry")() {}; var PluginMapTag = class extends Context.Tag("PluginMap")() {}; var RegistryService = class extends Effect.Service()("RegistryService", { effect: Effect.gen(function* () { const registry = yield* PluginRegistryTag; const pluginMap = yield* PluginMapTag; return { get: (pluginId) => Effect.gen(function* () { const entry = registry[pluginId]; if (!entry) return yield* Effect.fail(new PluginRuntimeError({ pluginId, operation: "validate-plugin-id", cause: /* @__PURE__ */ new Error(`Plugin ${pluginId} not found in registry`), retryable: false })); if ("module" in entry) return { constructor: entry.module, metadata: { remoteUrl: entry.remote || "", version: entry.version, description: entry.description } }; return { constructor: null, metadata: { remoteUrl: entry.remote, version: entry.version, description: entry.description } }; }), getModule: (pluginId) => Effect.succeed(pluginMap[pluginId] || null) }; }) }) {}; var PluginLoaderService = class extends Effect.Service()("PluginLoaderService", { effect: Effect.gen(function* () { const moduleFederationService = yield* ModuleFederationService; const secretsService = yield* SecretsService; const registryService = yield* RegistryService; const resolveUrl = (baseUrl, version) => version && version !== "latest" ? baseUrl.replace("@latest", `@${version}`) : baseUrl; return { loadPlugin: (pluginId) => Effect.gen(function* () { const entry = yield* registryService.get(pluginId); if (entry.constructor) { yield* Effect.logDebug("Loading plugin from direct module", { pluginId }); return { ctor: entry.constructor, metadata: entry.metadata }; } const url = entry.metadata.remoteUrl; if (!url) return yield* Effect.fail(new PluginRuntimeError({ pluginId, operation: "load-plugin", cause: /* @__PURE__ */ new Error(`Plugin ${pluginId} has no module or remote URL configured`), retryable: false })); const resolvedUrl = resolveUrl(url); yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "register-remote", true))); yield* Effect.logDebug("Loading plugin from remote", { pluginId, url: resolvedUrl }); return { ctor: yield* moduleFederationService.loadRemoteConstructor(pluginId, resolvedUrl).pipe(Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "load-remote", false))), metadata: entry.metadata }; }), instantiatePlugin: (pluginId, loadedPlugin) => Effect.gen(function* () { const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "instantiate-plugin", false))); instance.id = pluginId; return { plugin: instance, metadata: loadedPlugin.metadata }; }), initializePlugin: (pluginInstance, config, plugins) => Effect.gen(function* () { const { plugin } = pluginInstance; const validatedVariables = yield* validate(plugin.configSchema.variables, config.variables, plugin.id, "config").pipe(Effect.mapError((validationError) => new PluginRuntimeError({ pluginId: plugin.id, operation: "validate-config", cause: validationError.zodError, retryable: false }))); const validatedSecrets = yield* validate(plugin.configSchema.secrets, config.secrets, plugin.id, "config").pipe(Effect.mapError((validationError) => new PluginRuntimeError({ pluginId: plugin.id, operation: "validate-secrets", cause: validationError.zodError, retryable: false }))); const hydratedConfig = yield* secretsService.hydrateSecrets({ variables: validatedVariables, secrets: validatedSecrets }); const _variables = yield* validate(plugin.configSchema.variables, hydratedConfig.variables, plugin.id, "config").pipe(Effect.mapError((validationError) => new PluginRuntimeError({ pluginId: plugin.id, operation: "validate-hydrated-config", cause: validationError.zodError, retryable: false }))); const scope = yield* Scope.make(); const context = yield* plugin.initialize({ variables: _variables, secrets: hydratedConfig.secrets }, plugins ?? {}).pipe(Effect.provideService(Scope.Scope, scope), Effect.mapError((error) => toPluginRuntimeError(error, plugin.id, void 0, "initialize-plugin", false))); return { plugin, metadata: pluginInstance.metadata, config: { variables: _variables, secrets: hydratedConfig.secrets }, context, scope }; }) }; }) }) {}; //#endregion export { PluginLoaderService, PluginMapTag, PluginRegistryTag, RegistryService }; //# sourceMappingURL=plugin-loader.service.mjs.map