{"version":3,"file":"index.mjs","names":[],"sources":["../../src/runtime/index.ts"],"sourcesContent":["import { createRouterClient } from \"@orpc/server\";\nimport { Cause, Effect, Exit, Hash, ManagedRuntime, Option } from \"effect\";\nimport type {\n  AnyPlugin,\n  AnyPluginConstructor,\n  InferRegistryFromEntries,\n  InitializedPlugin,\n  LoadedPlugin,\n  PluginConfigInput,\n  PluginInstance,\n  PluginRegistry,\n  PluginRegistryEntry,\n  PluginRouterType,\n  PluginRuntimeConfig,\n  RegisteredPlugin,\n  RegisteredPlugins,\n  UsePluginResult,\n} from \"../types\";\nimport { PluginRuntimeError } from \"./errors\";\nimport { PluginService } from \"./services/plugin.service\";\n\nexport class PluginRuntime<R = RegisteredPlugins> {\n  readonly __registryType?: R;\n\n  private pluginCache = new Map<\n    string,\n    Effect.Effect<InitializedPlugin<AnyPlugin>, PluginRuntimeError>\n  >();\n\n  constructor(\n    private runtime: ManagedRuntime.ManagedRuntime<PluginService, never>,\n    private registry: PluginRegistry,\n  ) {}\n\n  private generateCacheKey(pluginId: string, config: unknown): string {\n    const configHash = Hash.structure(config as object).toString();\n    return `${pluginId}:${configHash}`;\n  }\n\n  private validatePluginId(pluginId: string): Effect.Effect<string, PluginRuntimeError> {\n    if (!(pluginId in this.registry)) {\n      return Effect.fail(\n        new PluginRuntimeError({\n          pluginId: String(pluginId),\n          operation: \"validate-plugin-id\",\n          cause: new Error(`Plugin ID '${String(pluginId)}' not found in registry.`),\n          retryable: false,\n        }),\n      );\n    }\n    return Effect.succeed(String(pluginId));\n  }\n\n  private async runPromise<A, E>(effect: Effect.Effect<A, E, PluginService>): Promise<A> {\n    const exit = await this.runtime.runPromiseExit(effect);\n\n    if (Exit.isFailure(exit)) {\n      const error = Cause.failureOption(exit.cause);\n      if (Option.isSome(error)) {\n        throw error.value;\n      }\n      throw Cause.squash(exit.cause);\n    }\n\n    return exit.value;\n  }\n\n  async usePlugin<K extends keyof R & string>(\n    pluginId: K,\n    config: PluginConfigInput<R[K]>,\n    plugins?: Record<string, unknown>,\n  ): Promise<UsePluginResult<K, R>> {\n    const cacheKey = this.generateCacheKey(pluginId, { ...config, __plugins: plugins ?? {} });\n\n    let cachedPlugin = this.pluginCache.get(cacheKey);\n    if (!cachedPlugin) {\n      const operation = Effect.gen(this, function* () {\n        const pluginService = yield* PluginService;\n        const validatedId = yield* this.validatePluginId(pluginId);\n\n        // Load → Instantiate → Initialize\n        const ctor = yield* pluginService.loadPlugin(validatedId);\n        const instance = yield* pluginService.instantiatePlugin(pluginId, ctor);\n        const initialized = yield* pluginService.initializePlugin(instance, config, plugins);\n\n        return initialized;\n      }).pipe(Effect.provide(this.runtime));\n\n      cachedPlugin = Effect.cached(operation).pipe(Effect.flatten);\n      this.pluginCache.set(cacheKey, cachedPlugin);\n    }\n\n    const initialized = await this.runPromise(cachedPlugin);\n\n    // Create client factory that accepts request context\n    const createClient = (context?: any) => {\n      const router = initialized.plugin.createRouter(initialized.context);\n      return createRouterClient(router, { context: context ?? {} });\n    };\n\n    return {\n      createClient: createClient as any,\n      router: initialized.plugin.createRouter(initialized.context) as PluginRouterType<R[K]>,\n      metadata: initialized.metadata,\n      initialized: initialized as InitializedPlugin<RegisteredPlugin<K, R>>,\n    } as UsePluginResult<K, R>;\n  }\n\n  async loadPlugin<K extends keyof R & string>(\n    pluginId: K,\n  ): Promise<LoadedPlugin<RegisteredPlugin<K, R>>> {\n    const effect = Effect.gen(function* () {\n      const pluginService = yield* PluginService;\n      return yield* pluginService.loadPlugin(pluginId);\n    });\n    return this.runPromise(effect) as Promise<LoadedPlugin<RegisteredPlugin<K, R>>>;\n  }\n\n  async instantiatePlugin<K extends keyof R & string>(\n    pluginId: K,\n    loadedPlugin: LoadedPlugin<RegisteredPlugin<K, R>>,\n  ): Promise<PluginInstance<RegisteredPlugin<K, R>>> {\n    const effect = Effect.gen(function* () {\n      const pluginService = yield* PluginService;\n      return yield* pluginService.instantiatePlugin(pluginId, loadedPlugin);\n    });\n    return this.runPromise(effect) as Promise<PluginInstance<RegisteredPlugin<K, R>>>;\n  }\n\n  async initializePlugin<T extends AnyPlugin>(\n    instance: PluginInstance<T>,\n    config: any,\n    plugins?: Record<string, unknown>,\n  ): Promise<InitializedPlugin<T>> {\n    const effect = Effect.gen(function* () {\n      const pluginService = yield* PluginService;\n      return yield* pluginService.initializePlugin(instance, config, plugins);\n    });\n    return this.runPromise(effect);\n  }\n\n  async shutdown(): Promise<void> {\n    const effect = Effect.gen(function* () {\n      const pluginService = yield* PluginService;\n      yield* pluginService.cleanup();\n    });\n    await this.runPromise(effect);\n    await this.runtime.dispose();\n  }\n\n  async evictPlugin<K extends keyof R & string>(\n    pluginId: K,\n    config: PluginConfigInput<R[K]>,\n  ): Promise<void> {\n    const cacheKey = this.generateCacheKey(pluginId, config);\n\n    const effect = Effect.gen(this, function* () {\n      const pluginService = yield* PluginService;\n      const cachedPlugin = this.pluginCache.get(cacheKey);\n\n      if (cachedPlugin) {\n        this.pluginCache.delete(cacheKey);\n\n        const pluginResult = yield* cachedPlugin.pipe(Effect.catchAll(() => Effect.succeed(null)));\n\n        if (pluginResult) {\n          yield* pluginService\n            .shutdownPlugin(pluginResult)\n            .pipe(\n              Effect.catchAll((error) =>\n                Effect.logWarning(`Failed to shutdown evicted plugin ${pluginId}`, error),\n              ),\n            );\n        }\n      }\n    }).pipe(\n      Effect.catchAll((error) =>\n        Effect.logWarning(`Plugin eviction failed for ${pluginId}`, error),\n      ),\n    );\n\n    return this.runPromise(effect);\n  }\n}\n\n/**\n * Normalizes a remote URL to ensure it points to remoteEntry.js\n * If the URL doesn't end with a file extension, appends /remoteEntry.js\n */\nfunction normalizeRemoteUrl(url: string): string {\n  if (!url) return url;\n  if (url.endsWith(\".js\") || url.endsWith(\".json\")) return url;\n  return `${url.endsWith(\"/\") ? url.slice(0, -1) : url}/remoteEntry.js`;\n}\n\n/**\n * Extract plugin map (module constructors) from registry entries\n */\nfunction extractPluginMap(\n  registry: Record<string, PluginRegistryEntry>,\n): Record<string, AnyPluginConstructor> {\n  const pluginMap: Record<string, AnyPluginConstructor> = {};\n\n  for (const [pluginId, entry] of Object.entries(registry)) {\n    if (\"module\" in entry && entry.module) {\n      pluginMap[pluginId] = entry.module;\n    }\n  }\n\n  return pluginMap;\n}\n\n/**\n * Normalize registry entries - ensure remote URLs are properly formatted\n */\nfunction normalizeRegistry(registry: Record<string, PluginRegistryEntry>): PluginRegistry {\n  const normalized: Record<string, PluginRegistryEntry> = {};\n\n  for (const [pluginId, entry] of Object.entries(registry)) {\n    if (\"module\" in entry) {\n      normalized[pluginId] = {\n        ...entry,\n        remote: entry.remote ? normalizeRemoteUrl(entry.remote) : undefined,\n      };\n    } else {\n      normalized[pluginId] = {\n        ...entry,\n        remote: normalizeRemoteUrl(entry.remote),\n      };\n    }\n  }\n\n  return normalized as PluginRegistry;\n}\n\n/**\n * Creates a plugin runtime with support for both module and remote plugin entries.\n *\n * @example\n * ```typescript\n * // With module entries (types inferred automatically)\n * const runtime = createPluginRuntime({\n *   registry: {\n *     telegram: { module: TelegramPlugin },\n *     gopher: { remote: \"https://cdn.example.com/gopher/remoteEntry.js\" }\n *   },\n *   secrets: { API_KEY: \"...\" }\n * });\n *\n * // Types are automatically inferred from module entries!\n * const { router } = await runtime.usePlugin(\"telegram\", config);\n * ```\n */\nexport function createPluginRuntime<TRegistry extends Record<string, PluginRegistryEntry>>(\n  config: PluginRuntimeConfig<TRegistry>,\n): PluginRuntime<InferRegistryFromEntries<TRegistry>> {\n  const secrets = config.secrets || {};\n  const normalizedRegistry = normalizeRegistry(config.registry);\n  const pluginMap = extractPluginMap(config.registry);\n\n  const layer = PluginService.Live(normalizedRegistry, secrets, pluginMap);\n  const runtime = ManagedRuntime.make(layer);\n\n  return new PluginRuntime(runtime, normalizedRegistry) as PluginRuntime<\n    InferRegistryFromEntries<TRegistry>\n  >;\n}\n"],"mappings":"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"}