every-plugin 2.9.3 → 2.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,13 +53,18 @@ var PluginRuntime = class {
53
53
  cachedPlugin = effect.Effect.cached(operation).pipe(effect.Effect.flatten);
54
54
  this.pluginCache.set(cacheKey, cachedPlugin);
55
55
  }
56
- const initialized = await this.runPromise(cachedPlugin);
57
- const createClient = (context) => {
58
- return (0, _orpc_server.createRouterClient)(initialized.plugin.createRouter(initialized.context), { context: context ?? {} });
59
- };
56
+ let initialized;
57
+ try {
58
+ initialized = await this.runPromise(cachedPlugin);
59
+ } catch (error) {
60
+ this.pluginCache.delete(cacheKey);
61
+ throw error;
62
+ }
63
+ const router = initialized.plugin.createRouter(initialized.context);
64
+ const createClient = (context) => (0, _orpc_server.createRouterClient)(router, { context: context ?? {} });
60
65
  return {
61
66
  createClient,
62
- router: initialized.plugin.createRouter(initialized.context),
67
+ router,
63
68
  metadata: initialized.metadata,
64
69
  initialized
65
70
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Hash","Effect","PluginRuntimeError","effect","Exit","Cause","Option","PluginService","ManagedRuntime"],"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 → Register\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 yield* pluginService.registerPlugin(initialized);\n\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: pluginId }), 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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 const initialized = yield* pluginService.initializePlugin(instance, config, plugins);\n yield* pluginService.registerPlugin(initialized);\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: instance.plugin.id }));\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 plugins?: Record<string, unknown>,\n ): Promise<void> {\n const cacheKey = this.generateCacheKey(pluginId, { ...config, __plugins: plugins ?? {} });\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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"}
1
+ {"version":3,"file":"index.cjs","names":["Hash","Effect","PluginRuntimeError","effect","Exit","Cause","Option","PluginService","ManagedRuntime"],"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 → Register\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 yield* pluginService.registerPlugin(initialized);\n\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: pluginId }), Effect.provide(this.runtime));\n\n cachedPlugin = Effect.cached(operation).pipe(Effect.flatten);\n this.pluginCache.set(cacheKey, cachedPlugin);\n }\n\n let initialized: InitializedPlugin<AnyPlugin>;\n try {\n initialized = await this.runPromise(cachedPlugin);\n } catch (error) {\n // Evict failed initializations so the next call retries instead\n // of returning the permanently-cached failure from Effect.cached.\n this.pluginCache.delete(cacheKey);\n throw error;\n }\n\n // Construct the router once per plugin instance, not per client/request.\n const router = initialized.plugin.createRouter(initialized.context) as PluginRouterType<R[K]>;\n\n // Create client factory that accepts request context\n const createClient = (context?: any) => createRouterClient(router, { context: context ?? {} });\n\n return {\n createClient: createClient as any,\n router,\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 const initialized = yield* pluginService.initializePlugin(instance, config, plugins);\n yield* pluginService.registerPlugin(initialized);\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: instance.plugin.id }));\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 plugins?: Record<string, unknown>,\n ): Promise<void> {\n const cacheKey = this.generateCacheKey(pluginId, { ...config, __plugins: plugins ?? {} });\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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/runtime/index.ts"],"mappings":";;;;;cAqBa,aAAA,KAAkB,iBAAA;EAAA,QASnB,OAAA;EAAA,QACA,QAAA;EAAA,SATD,cAAA,GAAiB,CAAA;EAAA,QAElB,WAAA;cAME,OAAA,EAAS,cAAA,CAAe,cAAA,CAAe,aAAA,UACvC,QAAA,EAAU,cAAA;EAAA,QAGZ,gBAAA;EAAA,QAKA,gBAAA;EAAA,QAcM,UAAA;EAcR,SAAA,iBAA0B,CAAA,UAAA,CAC9B,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,eAAA,CAAgB,CAAA,EAAG,CAAA;EAsCxB,UAAA,iBAA2B,CAAA,UAAA,CAC/B,QAAA,EAAU,CAAA,GACT,OAAA,CAAQ,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQtC,iBAAA,iBAAkC,CAAA,UAAA,CACtC,QAAA,EAAU,CAAA,EACV,YAAA,EAAc,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA,KAC9C,OAAA,CAAQ,cAAA,CAAe,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQxC,gBAAA,WAA2B,SAAA,CAAA,CAC/B,QAAA,EAAU,cAAA,CAAe,CAAA,GACzB,MAAA,OACA,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,iBAAA,CAAkB,CAAA;EAUvB,QAAA,CAAA,GAAY,OAAA;EASZ,WAAA,iBAA4B,CAAA,UAAA,CAChC,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAoGW,mBAAA,mBAAsC,MAAA,SAAe,mBAAA,EAAA,CACnE,MAAA,EAAQ,mBAAA,CAAoB,SAAA,IAC3B,aAAA,CAAc,wBAAA,CAAyB,SAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/runtime/index.ts"],"mappings":";;;;;cAqBa,aAAA,KAAkB,iBAAA;EAAA,QASnB,OAAA;EAAA,QACA,QAAA;EAAA,SATD,cAAA,GAAiB,CAAA;EAAA,QAElB,WAAA;cAME,OAAA,EAAS,cAAA,CAAe,cAAA,CAAe,aAAA,UACvC,QAAA,EAAU,cAAA;EAAA,QAGZ,gBAAA;EAAA,QAKA,gBAAA;EAAA,QAcM,UAAA;EAcR,SAAA,iBAA0B,CAAA,UAAA,CAC9B,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,eAAA,CAAgB,CAAA,EAAG,CAAA;EA8CxB,UAAA,iBAA2B,CAAA,UAAA,CAC/B,QAAA,EAAU,CAAA,GACT,OAAA,CAAQ,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQtC,iBAAA,iBAAkC,CAAA,UAAA,CACtC,QAAA,EAAU,CAAA,EACV,YAAA,EAAc,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA,KAC9C,OAAA,CAAQ,cAAA,CAAe,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQxC,gBAAA,WAA2B,SAAA,CAAA,CAC/B,QAAA,EAAU,cAAA,CAAe,CAAA,GACzB,MAAA,OACA,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,iBAAA,CAAkB,CAAA;EAUvB,QAAA,CAAA,GAAY,OAAA;EASZ,WAAA,iBAA4B,CAAA,UAAA,CAChC,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAoGW,mBAAA,mBAAsC,MAAA,SAAe,mBAAA,EAAA,CACnE,MAAA,EAAQ,mBAAA,CAAoB,SAAA,IAC3B,aAAA,CAAc,wBAAA,CAAyB,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/runtime/index.ts"],"mappings":";;;;;cAqBa,aAAA,KAAkB,iBAAA;EAAA,QASnB,OAAA;EAAA,QACA,QAAA;EAAA,SATD,cAAA,GAAiB,CAAA;EAAA,QAElB,WAAA;cAME,OAAA,EAAS,cAAA,CAAe,cAAA,CAAe,aAAA,UACvC,QAAA,EAAU,cAAA;EAAA,QAGZ,gBAAA;EAAA,QAKA,gBAAA;EAAA,QAcM,UAAA;EAcR,SAAA,iBAA0B,CAAA,UAAA,CAC9B,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,eAAA,CAAgB,CAAA,EAAG,CAAA;EAsCxB,UAAA,iBAA2B,CAAA,UAAA,CAC/B,QAAA,EAAU,CAAA,GACT,OAAA,CAAQ,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQtC,iBAAA,iBAAkC,CAAA,UAAA,CACtC,QAAA,EAAU,CAAA,EACV,YAAA,EAAc,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA,KAC9C,OAAA,CAAQ,cAAA,CAAe,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQxC,gBAAA,WAA2B,SAAA,CAAA,CAC/B,QAAA,EAAU,cAAA,CAAe,CAAA,GACzB,MAAA,OACA,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,iBAAA,CAAkB,CAAA;EAUvB,QAAA,CAAA,GAAY,OAAA;EASZ,WAAA,iBAA4B,CAAA,UAAA,CAChC,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAoGW,mBAAA,mBAAsC,MAAA,SAAe,mBAAA,EAAA,CACnE,MAAA,EAAQ,mBAAA,CAAoB,SAAA,IAC3B,aAAA,CAAc,wBAAA,CAAyB,SAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/runtime/index.ts"],"mappings":";;;;;cAqBa,aAAA,KAAkB,iBAAA;EAAA,QASnB,OAAA;EAAA,QACA,QAAA;EAAA,SATD,cAAA,GAAiB,CAAA;EAAA,QAElB,WAAA;cAME,OAAA,EAAS,cAAA,CAAe,cAAA,CAAe,aAAA,UACvC,QAAA,EAAU,cAAA;EAAA,QAGZ,gBAAA;EAAA,QAKA,gBAAA;EAAA,QAcM,UAAA;EAcR,SAAA,iBAA0B,CAAA,UAAA,CAC9B,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,eAAA,CAAgB,CAAA,EAAG,CAAA;EA8CxB,UAAA,iBAA2B,CAAA,UAAA,CAC/B,QAAA,EAAU,CAAA,GACT,OAAA,CAAQ,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQtC,iBAAA,iBAAkC,CAAA,UAAA,CACtC,QAAA,EAAU,CAAA,EACV,YAAA,EAAc,YAAA,CAAa,gBAAA,CAAiB,CAAA,EAAG,CAAA,KAC9C,OAAA,CAAQ,cAAA,CAAe,gBAAA,CAAiB,CAAA,EAAG,CAAA;EAQxC,gBAAA,WAA2B,SAAA,CAAA,CAC/B,QAAA,EAAU,cAAA,CAAe,CAAA,GACzB,MAAA,OACA,OAAA,GAAU,MAAA,oBACT,OAAA,CAAQ,iBAAA,CAAkB,CAAA;EAUvB,QAAA,CAAA,GAAY,OAAA;EASZ,WAAA,iBAA4B,CAAA,UAAA,CAChC,QAAA,EAAU,CAAA,EACV,MAAA,EAAQ,iBAAA,CAAkB,CAAA,CAAE,CAAA,IAC5B,OAAA,GAAU,MAAA,oBACT,OAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAoGW,mBAAA,mBAAsC,MAAA,SAAe,mBAAA,EAAA,CACnE,MAAA,EAAQ,mBAAA,CAAoB,SAAA,IAC3B,aAAA,CAAc,wBAAA,CAAyB,SAAA"}
@@ -51,13 +51,18 @@ var PluginRuntime = class {
51
51
  cachedPlugin = Effect.cached(operation).pipe(Effect.flatten);
52
52
  this.pluginCache.set(cacheKey, cachedPlugin);
53
53
  }
54
- const initialized = await this.runPromise(cachedPlugin);
55
- const createClient = (context) => {
56
- return createRouterClient(initialized.plugin.createRouter(initialized.context), { context: context ?? {} });
57
- };
54
+ let initialized;
55
+ try {
56
+ initialized = await this.runPromise(cachedPlugin);
57
+ } catch (error) {
58
+ this.pluginCache.delete(cacheKey);
59
+ throw error;
60
+ }
61
+ const router = initialized.plugin.createRouter(initialized.context);
62
+ const createClient = (context) => createRouterClient(router, { context: context ?? {} });
58
63
  return {
59
64
  createClient,
60
- router: initialized.plugin.createRouter(initialized.context),
65
+ router,
61
66
  metadata: initialized.metadata,
62
67
  initialized
63
68
  };
@@ -1 +1 @@
1
- {"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 → Register\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 yield* pluginService.registerPlugin(initialized);\n\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: pluginId }), 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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 const initialized = yield* pluginService.initializePlugin(instance, config, plugins);\n yield* pluginService.registerPlugin(initialized);\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: instance.plugin.id }));\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 plugins?: Record<string, unknown>,\n ): Promise<void> {\n const cacheKey = this.generateCacheKey(pluginId, { ...config, __plugins: plugins ?? {} });\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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"}
1
+ {"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 → Register\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 yield* pluginService.registerPlugin(initialized);\n\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: pluginId }), Effect.provide(this.runtime));\n\n cachedPlugin = Effect.cached(operation).pipe(Effect.flatten);\n this.pluginCache.set(cacheKey, cachedPlugin);\n }\n\n let initialized: InitializedPlugin<AnyPlugin>;\n try {\n initialized = await this.runPromise(cachedPlugin);\n } catch (error) {\n // Evict failed initializations so the next call retries instead\n // of returning the permanently-cached failure from Effect.cached.\n this.pluginCache.delete(cacheKey);\n throw error;\n }\n\n // Construct the router once per plugin instance, not per client/request.\n const router = initialized.plugin.createRouter(initialized.context) as PluginRouterType<R[K]>;\n\n // Create client factory that accepts request context\n const createClient = (context?: any) => createRouterClient(router, { context: context ?? {} });\n\n return {\n createClient: createClient as any,\n router,\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 }).pipe(Effect.annotateLogs({ plugin: pluginId }));\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 const initialized = yield* pluginService.initializePlugin(instance, config, plugins);\n yield* pluginService.registerPlugin(initialized);\n return initialized;\n }).pipe(Effect.annotateLogs({ plugin: instance.plugin.id }));\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 plugins?: Record<string, unknown>,\n ): Promise<void> {\n const cacheKey = this.generateCacheKey(pluginId, { ...config, __plugins: plugins ?? {} });\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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"}
@@ -21,7 +21,7 @@ function getInstalledPackageVersion(packageName, fallbackRange) {
21
21
  return match ? match[0] : fallbackRange.replace(/^[\^~>=<\s]+/, "");
22
22
  }
23
23
  }
24
- const PLUGIN_VERSION = "2.9.3";
24
+ const PLUGIN_VERSION = "2.9.5";
25
25
  const SHARE_CONFIG = {
26
26
  singleton: true,
27
27
  requiredVersion: false,
@@ -19,7 +19,7 @@ function getInstalledPackageVersion(packageName, fallbackRange) {
19
19
  return match ? match[0] : fallbackRange.replace(/^[\^~>=<\s]+/, "");
20
20
  }
21
21
  }
22
- const PLUGIN_VERSION = "2.9.3";
22
+ const PLUGIN_VERSION = "2.9.5";
23
23
  const SHARE_CONFIG = {
24
24
  singleton: true,
25
25
  requiredVersion: false,
@@ -63,18 +63,18 @@ var PluginLoaderService = class extends effect.Effect.Service()("PluginLoaderSer
63
63
  retryable: false
64
64
  }));
65
65
  const resolvedUrl = resolveUrl(url);
66
- yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(effect.Effect.tapError((_error) => effect.Effect.logError(`Plugin ${pluginId} failed during register-remote`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "register-remote", true)));
66
+ yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(effect.Effect.tapError((error) => effect.Effect.logError(`Plugin ${pluginId} failed during register-remote: ${error}`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "register-remote", true)));
67
67
  yield* effect.Effect.logDebug("Loading plugin from remote", {
68
68
  pluginId,
69
69
  url: resolvedUrl
70
70
  });
71
71
  return {
72
- ctor: yield* moduleFederationService.loadRemoteConstructor(pluginId, resolvedUrl).pipe(effect.Effect.tapError((_error) => effect.Effect.logError(`Plugin ${pluginId} failed during load-remote`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "load-remote", false))),
72
+ ctor: yield* moduleFederationService.loadRemoteConstructor(pluginId, resolvedUrl).pipe(effect.Effect.tapError((error) => effect.Effect.logError(`Plugin ${pluginId} failed during load-remote: ${error}`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "load-remote", false))),
73
73
  metadata: entry.metadata
74
74
  };
75
75
  }),
76
76
  instantiatePlugin: (pluginId, loadedPlugin) => effect.Effect.gen(function* () {
77
- const instance = yield* effect.Effect.try(() => new loadedPlugin.ctor()).pipe(effect.Effect.tapError((_error) => effect.Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "instantiate-plugin", false)));
77
+ const instance = yield* effect.Effect.try(() => new loadedPlugin.ctor()).pipe(effect.Effect.tapError((error) => effect.Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin: ${error}`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, pluginId, void 0, "instantiate-plugin", false)));
78
78
  instance.id = pluginId;
79
79
  return {
80
80
  plugin: instance,
@@ -110,7 +110,7 @@ var PluginLoaderService = class extends effect.Effect.Service()("PluginLoaderSer
110
110
  const context = yield* plugin.initialize({
111
111
  variables: _variables,
112
112
  secrets: hydratedConfig.secrets
113
- }, plugins ?? {}, { buildService: (tag, layer) => effect.Layer.buildWithMemoMap(layer, memoMap, scope).pipe(effect.Effect.map((ctx) => effect.Context.get(ctx, tag))) }).pipe(effect.Effect.provideService(effect.Scope.Scope, scope), effect.Effect.tapError((_error) => effect.Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, plugin.id, void 0, "initialize-plugin", false)));
113
+ }, plugins ?? {}, { buildService: (tag, layer) => effect.Layer.buildWithMemoMap(layer, memoMap, scope).pipe(effect.Effect.map((ctx) => effect.Context.get(ctx, tag))) }).pipe(effect.Effect.provideService(effect.Scope.Scope, scope), effect.Effect.tapError(() => effect.Scope.close(scope, effect.Exit.succeed(void 0)).pipe(effect.Effect.catchAll((closeError) => effect.Effect.logWarning(`Failed to close scope for plugin ${plugin.id} after initialize error`, closeError)))), effect.Effect.tapError((error) => effect.Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin: ${error}`)), effect.Effect.mapError((error) => require_errors.toPluginRuntimeError(error, plugin.id, void 0, "initialize-plugin", false)));
114
114
  return {
115
115
  plugin,
116
116
  metadata: pluginInstance.metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-loader.service.cjs","names":["Context","Effect","PluginRuntimeError","ModuleFederationService","SecretsService","toPluginRuntimeError","validate","Scope","Layer","EffectContext"],"sources":["../../../src/runtime/services/plugin-loader.service.ts"],"sourcesContent":["import type { InferSchemaInput, InferSchemaOutput } from \"@orpc/contract\";\nimport { Context, Effect, Context as EffectContext, Layer, Scope } from \"effect\";\nimport type { z } from \"zod\";\nimport 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`),\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`),\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`),\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 // The plugin's initialize method handles:\n // 1. Building services (DB pools, caches) via tools.buildService\n // 2. Shaping final deps via initialize\n // Any Layer.scoped resources built through tools.buildService are\n // bound to the plugin scope and persist until the plugin is shutdown.\n const context = yield* plugin\n .initialize(\n { variables: _variables, secrets: hydratedConfig.secrets },\n plugins ?? {},\n tools,\n )\n .pipe(\n Effect.provideService(Scope.Scope, scope),\n Effect.tapError((_error) =>\n Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`),\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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"}
1
+ {"version":3,"file":"plugin-loader.service.cjs","names":["Context","Effect","PluginRuntimeError","ModuleFederationService","SecretsService","toPluginRuntimeError","validate","Scope","Layer","EffectContext","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 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(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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"}
@@ -2,7 +2,7 @@ import { PluginRuntimeError, toPluginRuntimeError } from "../errors.mjs";
2
2
  import { ModuleFederationService } from "./module-federation.service.mjs";
3
3
  import { validate } from "../validation.mjs";
4
4
  import { SecretsService } from "./secrets.service.mjs";
5
- import { Context, Effect, Layer, Scope } from "effect";
5
+ import { Context, Effect, Exit, Layer, Scope } from "effect";
6
6
 
7
7
  //#region src/runtime/services/plugin-loader.service.ts
8
8
  var PluginRegistryTag = class extends Context.Tag("PluginRegistry")() {};
@@ -62,18 +62,18 @@ var PluginLoaderService = class extends Effect.Service()("PluginLoaderService",
62
62
  retryable: false
63
63
  }));
64
64
  const resolvedUrl = resolveUrl(url);
65
- yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(Effect.tapError((_error) => Effect.logError(`Plugin ${pluginId} failed during register-remote`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "register-remote", true)));
65
+ yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(Effect.tapError((error) => Effect.logError(`Plugin ${pluginId} failed during register-remote: ${error}`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "register-remote", true)));
66
66
  yield* Effect.logDebug("Loading plugin from remote", {
67
67
  pluginId,
68
68
  url: resolvedUrl
69
69
  });
70
70
  return {
71
- ctor: yield* moduleFederationService.loadRemoteConstructor(pluginId, resolvedUrl).pipe(Effect.tapError((_error) => Effect.logError(`Plugin ${pluginId} failed during load-remote`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "load-remote", false))),
71
+ ctor: yield* moduleFederationService.loadRemoteConstructor(pluginId, resolvedUrl).pipe(Effect.tapError((error) => Effect.logError(`Plugin ${pluginId} failed during load-remote: ${error}`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "load-remote", false))),
72
72
  metadata: entry.metadata
73
73
  };
74
74
  }),
75
75
  instantiatePlugin: (pluginId, loadedPlugin) => Effect.gen(function* () {
76
- const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(Effect.tapError((_error) => Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "instantiate-plugin", false)));
76
+ const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(Effect.tapError((error) => Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin: ${error}`)), Effect.mapError((error) => toPluginRuntimeError(error, pluginId, void 0, "instantiate-plugin", false)));
77
77
  instance.id = pluginId;
78
78
  return {
79
79
  plugin: instance,
@@ -109,7 +109,7 @@ var PluginLoaderService = class extends Effect.Service()("PluginLoaderService",
109
109
  const context = yield* plugin.initialize({
110
110
  variables: _variables,
111
111
  secrets: hydratedConfig.secrets
112
- }, plugins ?? {}, { buildService: (tag, layer) => Layer.buildWithMemoMap(layer, memoMap, scope).pipe(Effect.map((ctx) => Context.get(ctx, tag))) }).pipe(Effect.provideService(Scope.Scope, scope), Effect.tapError((_error) => Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`)), Effect.mapError((error) => toPluginRuntimeError(error, plugin.id, void 0, "initialize-plugin", false)));
112
+ }, plugins ?? {}, { buildService: (tag, layer) => Layer.buildWithMemoMap(layer, memoMap, scope).pipe(Effect.map((ctx) => Context.get(ctx, tag))) }).pipe(Effect.provideService(Scope.Scope, scope), Effect.tapError(() => Scope.close(scope, Exit.succeed(void 0)).pipe(Effect.catchAll((closeError) => Effect.logWarning(`Failed to close scope for plugin ${plugin.id} after initialize error`, closeError)))), Effect.tapError((error) => Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin: ${error}`)), Effect.mapError((error) => toPluginRuntimeError(error, plugin.id, void 0, "initialize-plugin", false)));
113
113
  return {
114
114
  plugin,
115
115
  metadata: pluginInstance.metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-loader.service.mjs","names":["EffectContext"],"sources":["../../../src/runtime/services/plugin-loader.service.ts"],"sourcesContent":["import type { InferSchemaInput, InferSchemaOutput } from \"@orpc/contract\";\nimport { Context, Effect, Context as EffectContext, Layer, Scope } from \"effect\";\nimport type { z } from \"zod\";\nimport 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`),\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`),\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`),\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 // The plugin's initialize method handles:\n // 1. Building services (DB pools, caches) via tools.buildService\n // 2. Shaping final deps via initialize\n // Any Layer.scoped resources built through tools.buildService are\n // bound to the plugin scope and persist until the plugin is shutdown.\n const context = yield* plugin\n .initialize(\n { variables: _variables, secrets: hydratedConfig.secrets },\n plugins ?? {},\n tools,\n )\n .pipe(\n Effect.provideService(Scope.Scope, scope),\n Effect.tapError((_error) =>\n Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`),\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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"}
1
+ {"version":3,"file":"plugin-loader.service.mjs","names":["EffectContext"],"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 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(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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "every-plugin",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -91,17 +91,25 @@ export class PluginRuntime<R = RegisteredPlugins> {
91
91
  this.pluginCache.set(cacheKey, cachedPlugin);
92
92
  }
93
93
 
94
- const initialized = await this.runPromise(cachedPlugin);
94
+ let initialized: InitializedPlugin<AnyPlugin>;
95
+ try {
96
+ initialized = await this.runPromise(cachedPlugin);
97
+ } catch (error) {
98
+ // Evict failed initializations so the next call retries instead
99
+ // of returning the permanently-cached failure from Effect.cached.
100
+ this.pluginCache.delete(cacheKey);
101
+ throw error;
102
+ }
103
+
104
+ // Construct the router once per plugin instance, not per client/request.
105
+ const router = initialized.plugin.createRouter(initialized.context) as PluginRouterType<R[K]>;
95
106
 
96
107
  // Create client factory that accepts request context
97
- const createClient = (context?: any) => {
98
- const router = initialized.plugin.createRouter(initialized.context);
99
- return createRouterClient(router, { context: context ?? {} });
100
- };
108
+ const createClient = (context?: any) => createRouterClient(router, { context: context ?? {} });
101
109
 
102
110
  return {
103
111
  createClient: createClient as any,
104
- router: initialized.plugin.createRouter(initialized.context) as PluginRouterType<R[K]>,
112
+ router,
105
113
  metadata: initialized.metadata,
106
114
  initialized: initialized as InitializedPlugin<RegisteredPlugin<K, R>>,
107
115
  } as UsePluginResult<K, R>;
@@ -1,5 +1,5 @@
1
1
  import type { InferSchemaInput, InferSchemaOutput } from "@orpc/contract";
2
- import { Context, Effect, Context as EffectContext, Layer, Scope } from "effect";
2
+ import { Context, Effect, Context as EffectContext, Exit, Layer, Scope } from "effect";
3
3
  import type { z } from "zod";
4
4
  import type { PluginServicesTools } from "../../plugin";
5
5
  import type {
@@ -113,8 +113,8 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
113
113
  const resolvedUrl = resolveUrl(url);
114
114
 
115
115
  yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(
116
- Effect.tapError((_error) =>
117
- Effect.logError(`Plugin ${pluginId} failed during register-remote`),
116
+ Effect.tapError((error) =>
117
+ Effect.logError(`Plugin ${pluginId} failed during register-remote: ${error}`),
118
118
  ),
119
119
  Effect.mapError((error) =>
120
120
  toPluginRuntimeError(error, pluginId, undefined, "register-remote", true),
@@ -126,8 +126,8 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
126
126
  const ctor = yield* moduleFederationService
127
127
  .loadRemoteConstructor(pluginId, resolvedUrl)
128
128
  .pipe(
129
- Effect.tapError((_error) =>
130
- Effect.logError(`Plugin ${pluginId} failed during load-remote`),
129
+ Effect.tapError((error) =>
130
+ Effect.logError(`Plugin ${pluginId} failed during load-remote: ${error}`),
131
131
  ),
132
132
  Effect.mapError((error) =>
133
133
  toPluginRuntimeError(error, pluginId, undefined, "load-remote", false),
@@ -143,8 +143,8 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
143
143
  instantiatePlugin: <T extends AnyPlugin>(pluginId: string, loadedPlugin: LoadedPlugin<T>) =>
144
144
  Effect.gen(function* () {
145
145
  const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(
146
- Effect.tapError((_error) =>
147
- Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin`),
146
+ Effect.tapError((error) =>
147
+ Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin: ${error}`),
148
148
  ),
149
149
  Effect.mapError((error) =>
150
150
  toPluginRuntimeError(error, pluginId, undefined, "instantiate-plugin", false),
@@ -248,12 +248,10 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
248
248
  ),
249
249
  };
250
250
 
251
- // Initialize plugin within the scope
252
- // The plugin's initialize method handles:
253
- // 1. Building services (DB pools, caches) via tools.buildService
254
- // 2. Shaping final deps via initialize
255
- // Any Layer.scoped resources built through tools.buildService are
256
- // bound to the plugin scope and persist until the plugin is shutdown.
251
+ // Initialize plugin within the scope.
252
+ // If initialize fails, close the scope immediately so scoped
253
+ // resources (DB pools, caches) are released — otherwise every
254
+ // failed initialization leaks until the process exits.
257
255
  const context = yield* plugin
258
256
  .initialize(
259
257
  { variables: _variables, secrets: hydratedConfig.secrets },
@@ -262,8 +260,18 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
262
260
  )
263
261
  .pipe(
264
262
  Effect.provideService(Scope.Scope, scope),
265
- Effect.tapError((_error) =>
266
- Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`),
263
+ Effect.tapError(() =>
264
+ Scope.close(scope, Exit.succeed(undefined)).pipe(
265
+ Effect.catchAll((closeError) =>
266
+ Effect.logWarning(
267
+ `Failed to close scope for plugin ${plugin.id} after initialize error`,
268
+ closeError,
269
+ ),
270
+ ),
271
+ ),
272
+ ),
273
+ Effect.tapError((error) =>
274
+ Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin: ${error}`),
267
275
  ),
268
276
  Effect.mapError((error) =>
269
277
  toPluginRuntimeError(error, plugin.id, undefined, "initialize-plugin", false),