{"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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"}