{"version":3,"file":"plugin-loader.service.cjs","names":["Context","Effect","PluginRuntimeError","ModuleFederationService","SecretsService","toPluginRuntimeError","validate","Scope","Layer","EffectContext","PluginIdTag","Exit"],"sources":["../../../src/runtime/services/plugin-loader.service.ts"],"sourcesContent":["import type { InferSchemaInput, InferSchemaOutput } from \"@orpc/contract\";\nimport { Context, Effect, Context as EffectContext, Exit, Layer, Scope } from \"effect\";\nimport type { z } from \"zod\";\nimport { PluginIdTag, type PluginServicesTools } from \"../../plugin\";\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.registerRemote(pluginId, resolvedUrl).pipe(\n              Effect.tapError((error) =>\n                Effect.logError(`Plugin ${pluginId} failed during register-remote: ${error}`),\n              ),\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.tapError((error) =>\n                  Effect.logError(`Plugin ${pluginId} failed during load-remote: ${error}`),\n                ),\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.tapError((error) =>\n                Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin: ${error}`),\n              ),\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            // Create a per-plugin MemoMap so tool-built layers are memoized\n            const memoMap = yield* Layer.makeMemoMap;\n\n            const tools: PluginServicesTools = {\n              buildService: (tag: any, layer: any) =>\n                (Layer.buildWithMemoMap(layer, memoMap, scope) as any).pipe(\n                  Effect.map((ctx: any) => EffectContext.get(ctx, tag)) as any,\n                ),\n            };\n\n            // Initialize plugin within the scope.\n            // If initialize fails, close the scope immediately so scoped\n            // resources (DB pools, caches) are released — otherwise every\n            // failed initialization leaks until the process exits.\n            const context = yield* plugin\n              .initialize(\n                { variables: _variables, secrets: hydratedConfig.secrets },\n                plugins ?? {},\n                tools,\n              )\n              .pipe(\n                Effect.provideService(PluginIdTag, plugin.id),\n                Effect.provideService(Scope.Scope, scope),\n                Effect.tapError(() =>\n                  Scope.close(scope, Exit.succeed(undefined)).pipe(\n                    Effect.catchAll((closeError) =>\n                      Effect.logWarning(\n                        `Failed to close scope for plugin ${plugin.id} after initialize error`,\n                        closeError,\n                      ),\n                    ),\n                  ),\n                ),\n                Effect.tapError((error) =>\n                  Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin: ${error}`),\n                ),\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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"}