UNPKG

every-plugin

Version:
1 lines 11.8 kB
{"version":3,"file":"plugin-loader.service.mjs","names":[],"sources":["../../../src/runtime/services/plugin-loader.service.ts"],"sourcesContent":["import type { InferSchemaInput, InferSchemaOutput } from \"@orpc/contract\";\nimport { Context, Effect, Scope } from \"effect\";\nimport type { z } from \"zod\";\nimport type {\n AnyPlugin,\n AnyPluginConstructor,\n InitializedPlugin,\n LoadedPlugin,\n PluginInstance,\n PluginMetadata,\n PluginRegistry,\n} from \"../../types\";\nimport { PluginRuntimeError, toPluginRuntimeError } from \"../errors\";\nimport { validate } from \"../validation\";\nimport { ModuleFederationService } from \"./module-federation.service\";\nimport { SecretsService } from \"./secrets.service\";\n\nexport class PluginRegistryTag extends Context.Tag(\"PluginRegistry\")<\n PluginRegistryTag,\n PluginRegistry\n>() {}\n\nexport class PluginMapTag extends Context.Tag(\"PluginMap\")<\n PluginMapTag,\n Record<string, AnyPluginConstructor>\n>() {}\n\nexport class RegistryService extends Effect.Service<RegistryService>()(\"RegistryService\", {\n effect: Effect.gen(function* () {\n const registry = yield* PluginRegistryTag;\n const pluginMap = yield* PluginMapTag;\n\n return {\n get: (pluginId: string) =>\n Effect.gen(function* () {\n const entry = registry[pluginId];\n\n if (!entry) {\n return yield* Effect.fail(\n new PluginRuntimeError({\n pluginId,\n operation: \"validate-plugin-id\",\n cause: new Error(`Plugin ${pluginId} not found in registry`),\n retryable: false,\n }),\n );\n }\n\n if (\"module\" in entry) {\n return {\n constructor: entry.module,\n metadata: {\n remoteUrl: entry.remote || \"\",\n version: entry.version,\n description: entry.description,\n } as PluginMetadata,\n };\n }\n\n return {\n constructor: null,\n metadata: {\n remoteUrl: entry.remote,\n version: entry.version,\n description: entry.description,\n } as PluginMetadata,\n };\n }),\n\n getModule: (pluginId: string) => Effect.succeed(pluginMap[pluginId] || null),\n };\n }),\n}) {}\n\nexport class PluginLoaderService extends Effect.Service<PluginLoaderService>()(\n \"PluginLoaderService\",\n {\n effect: Effect.gen(function* () {\n const moduleFederationService = yield* ModuleFederationService;\n const secretsService = yield* SecretsService;\n const registryService = yield* RegistryService;\n\n const resolveUrl = (baseUrl: string, version?: string): string =>\n version && version !== \"latest\" ? baseUrl.replace(\"@latest\", `@${version}`) : baseUrl;\n\n return {\n loadPlugin: (pluginId: string) =>\n Effect.gen(function* () {\n const entry = yield* registryService.get(pluginId);\n\n if (entry.constructor) {\n yield* Effect.logDebug(\"Loading plugin from direct module\", { pluginId });\n\n return {\n ctor: entry.constructor,\n metadata: entry.metadata,\n } satisfies LoadedPlugin;\n }\n\n const url = entry.metadata.remoteUrl;\n if (!url) {\n return yield* Effect.fail(\n new PluginRuntimeError({\n pluginId,\n operation: \"load-plugin\",\n cause: new Error(`Plugin ${pluginId} has no module or remote URL configured`),\n retryable: false,\n }),\n );\n }\n\n const resolvedUrl = resolveUrl(url);\n\n yield* moduleFederationService\n .registerRemote(pluginId, resolvedUrl)\n .pipe(\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"register-remote\", true),\n ),\n );\n\n yield* Effect.logDebug(\"Loading plugin from remote\", { pluginId, url: resolvedUrl });\n\n const ctor = yield* moduleFederationService\n .loadRemoteConstructor(pluginId, resolvedUrl)\n .pipe(\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"load-remote\", false),\n ),\n );\n\n return {\n ctor,\n metadata: entry.metadata,\n } satisfies LoadedPlugin;\n }),\n\n instantiatePlugin: <T extends AnyPlugin>(pluginId: string, loadedPlugin: LoadedPlugin<T>) =>\n Effect.gen(function* () {\n const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"instantiate-plugin\", false),\n ),\n );\n\n (instance.id as string) = pluginId;\n\n return {\n plugin: instance,\n metadata: loadedPlugin.metadata,\n } satisfies PluginInstance<T>;\n }),\n\n initializePlugin: <T extends AnyPlugin>(\n pluginInstance: PluginInstance<T>,\n config: {\n variables: InferSchemaInput<T[\"configSchema\"][\"variables\"]>;\n secrets: InferSchemaInput<T[\"configSchema\"][\"secrets\"]>;\n },\n plugins?: Record<string, unknown>,\n ) =>\n Effect.gen(function* () {\n const { plugin } = pluginInstance;\n\n // Validate and hydrate config\n const validatedVariables = yield* validate(\n plugin.configSchema.variables as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"variables\"]>\n >,\n config.variables,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-config\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Validate secrets\n const validatedSecrets = yield* validate(\n plugin.configSchema.secrets as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"secrets\"]>\n >,\n config.secrets,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-secrets\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Hydrate secrets in variables\n const hydratedConfig = yield* secretsService.hydrateSecrets({\n variables: validatedVariables,\n secrets: validatedSecrets,\n });\n\n const _variables = yield* validate(\n plugin.configSchema.variables as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"variables\"]>\n >,\n hydratedConfig.variables,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-hydrated-config\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Create a long-lived scope for this plugin instance\n const scope = yield* Scope.make();\n\n // Initialize plugin within the scope\n const context = yield* plugin\n .initialize({ variables: _variables, secrets: hydratedConfig.secrets }, plugins ?? {})\n .pipe(\n Effect.provideService(Scope.Scope, scope),\n Effect.mapError((error) =>\n toPluginRuntimeError(error, plugin.id, undefined, \"initialize-plugin\", false),\n ),\n );\n\n return {\n plugin,\n metadata: pluginInstance.metadata,\n config: { variables: _variables, secrets: hydratedConfig.secrets },\n context,\n scope,\n } satisfies InitializedPlugin<T>;\n }),\n };\n }),\n },\n) {}\n"],"mappings":"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"}