UNPKG

every-plugin

Version:
1 lines 14.4 kB
{"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 { PluginIdTag, type PluginServicesTools } from \"../../plugin\";\nimport type {\n AnyPlugin,\n AnyPluginConstructor,\n InitializedPlugin,\n LoadedPlugin,\n PluginInstance,\n PluginMetadata,\n PluginRegistry,\n} from \"../../types\";\nimport { PluginRuntimeError, toPluginRuntimeError } from \"../errors\";\nimport { validate } from \"../validation\";\nimport { ModuleFederationService } from \"./module-federation.service\";\nimport { SecretsService } from \"./secrets.service\";\n\nexport class PluginRegistryTag extends Context.Tag(\"PluginRegistry\")<\n PluginRegistryTag,\n PluginRegistry\n>() {}\n\nexport class PluginMapTag extends Context.Tag(\"PluginMap\")<\n PluginMapTag,\n Record<string, AnyPluginConstructor>\n>() {}\n\nexport class RegistryService extends Effect.Service<RegistryService>()(\"RegistryService\", {\n effect: Effect.gen(function* () {\n const registry = yield* PluginRegistryTag;\n const pluginMap = yield* PluginMapTag;\n\n return {\n get: (pluginId: string) =>\n Effect.gen(function* () {\n const entry = registry[pluginId];\n\n if (!entry) {\n return yield* Effect.fail(\n new PluginRuntimeError({\n pluginId,\n operation: \"validate-plugin-id\",\n cause: new Error(`Plugin ${pluginId} not found in registry`),\n retryable: false,\n }),\n );\n }\n\n if (\"module\" in entry) {\n return {\n constructor: entry.module,\n metadata: {\n remoteUrl: entry.remote || \"\",\n version: entry.version,\n description: entry.description,\n } as PluginMetadata,\n };\n }\n\n return {\n constructor: null,\n metadata: {\n remoteUrl: entry.remote,\n version: entry.version,\n description: entry.description,\n } as PluginMetadata,\n };\n }),\n\n getModule: (pluginId: string) => Effect.succeed(pluginMap[pluginId] || null),\n };\n }),\n}) {}\n\nexport class PluginLoaderService extends Effect.Service<PluginLoaderService>()(\n \"PluginLoaderService\",\n {\n effect: Effect.gen(function* () {\n const moduleFederationService = yield* ModuleFederationService;\n const secretsService = yield* SecretsService;\n const registryService = yield* RegistryService;\n\n const resolveUrl = (baseUrl: string, version?: string): string =>\n version && version !== \"latest\" ? baseUrl.replace(\"@latest\", `@${version}`) : baseUrl;\n\n return {\n loadPlugin: (pluginId: string) =>\n Effect.gen(function* () {\n const entry = yield* registryService.get(pluginId);\n\n if (entry.constructor) {\n yield* Effect.logDebug(\"Loading plugin from direct module\", { pluginId });\n\n return {\n ctor: entry.constructor,\n metadata: entry.metadata,\n } satisfies LoadedPlugin;\n }\n\n const url = entry.metadata.remoteUrl;\n if (!url) {\n return yield* Effect.fail(\n new PluginRuntimeError({\n pluginId,\n operation: \"load-plugin\",\n cause: new Error(`Plugin ${pluginId} has no module or remote URL configured`),\n retryable: false,\n }),\n );\n }\n\n const resolvedUrl = resolveUrl(url);\n\n yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(\n Effect.tapError((error) =>\n Effect.logError(`Plugin ${pluginId} failed during register-remote: ${error}`),\n ),\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"register-remote\", true),\n ),\n );\n\n yield* Effect.logDebug(\"Loading plugin from remote\", { pluginId, url: resolvedUrl });\n\n const ctor = yield* moduleFederationService\n .loadRemoteConstructor(pluginId, resolvedUrl)\n .pipe(\n Effect.tapError((error) =>\n Effect.logError(`Plugin ${pluginId} failed during load-remote: ${error}`),\n ),\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"load-remote\", false),\n ),\n );\n\n return {\n ctor,\n metadata: entry.metadata,\n } satisfies LoadedPlugin;\n }),\n\n instantiatePlugin: <T extends AnyPlugin>(pluginId: string, loadedPlugin: LoadedPlugin<T>) =>\n Effect.gen(function* () {\n const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(\n Effect.tapError((error) =>\n Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin: ${error}`),\n ),\n Effect.mapError((error) =>\n toPluginRuntimeError(error, pluginId, undefined, \"instantiate-plugin\", false),\n ),\n );\n\n (instance.id as string) = pluginId;\n\n return {\n plugin: instance,\n metadata: loadedPlugin.metadata,\n } satisfies PluginInstance<T>;\n }),\n\n initializePlugin: <T extends AnyPlugin>(\n pluginInstance: PluginInstance<T>,\n config: {\n variables: InferSchemaInput<T[\"configSchema\"][\"variables\"]>;\n secrets: InferSchemaInput<T[\"configSchema\"][\"secrets\"]>;\n },\n plugins?: Record<string, unknown>,\n ) =>\n Effect.gen(function* () {\n const { plugin } = pluginInstance;\n\n // Validate and hydrate config\n const validatedVariables = yield* validate(\n plugin.configSchema.variables as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"variables\"]>\n >,\n config.variables,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-config\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Validate secrets\n const validatedSecrets = yield* validate(\n plugin.configSchema.secrets as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"secrets\"]>\n >,\n config.secrets,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-secrets\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Hydrate secrets in variables\n const hydratedConfig = yield* secretsService.hydrateSecrets({\n variables: validatedVariables,\n secrets: validatedSecrets,\n });\n\n const _variables = yield* validate(\n plugin.configSchema.variables as z.ZodSchema<\n InferSchemaOutput<T[\"configSchema\"][\"variables\"]>\n >,\n hydratedConfig.variables,\n plugin.id,\n \"config\",\n ).pipe(\n Effect.mapError(\n (validationError) =>\n new PluginRuntimeError({\n pluginId: plugin.id,\n operation: \"validate-hydrated-config\",\n cause: validationError.zodError,\n retryable: false,\n }),\n ),\n );\n\n // Create a long-lived scope for this plugin instance\n const scope = yield* Scope.make();\n\n // Create a per-plugin MemoMap so tool-built layers are memoized\n const memoMap = yield* Layer.makeMemoMap;\n\n const tools: PluginServicesTools = {\n buildService: (tag: any, layer: any) =>\n (Layer.buildWithMemoMap(layer, memoMap, scope) as any).pipe(\n Effect.map((ctx: any) => EffectContext.get(ctx, tag)) as any,\n ),\n };\n\n // Initialize plugin within the scope.\n // If initialize fails, close the scope immediately so scoped\n // resources (DB pools, caches) are released — otherwise every\n // failed initialization leaks until the process exits.\n const context = yield* plugin\n .initialize(\n { variables: _variables, secrets: hydratedConfig.secrets },\n plugins ?? {},\n tools,\n )\n .pipe(\n Effect.provideService(PluginIdTag, plugin.id),\n Effect.provideService(Scope.Scope, scope),\n Effect.tapError(() =>\n Scope.close(scope, Exit.succeed(undefined)).pipe(\n Effect.catchAll((closeError) =>\n Effect.logWarning(\n `Failed to close scope for plugin ${plugin.id} after initialize error`,\n closeError,\n ),\n ),\n ),\n ),\n Effect.tapError((error) =>\n Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin: ${error}`),\n ),\n Effect.mapError((error) =>\n toPluginRuntimeError(error, plugin.id, undefined, \"initialize-plugin\", false),\n ),\n );\n\n return {\n plugin,\n metadata: pluginInstance.metadata,\n config: { variables: _variables, secrets: hydratedConfig.secrets },\n context,\n scope,\n } satisfies InitializedPlugin<T>;\n }),\n };\n }),\n },\n) {}\n"],"mappings":";;;;;;;;AAkBA,IAAa,oBAAb,cAAuC,QAAQ,IAAI,gBAAgB,CAAC,CAGlE,CAAC,CAAC,CAAC;AAEL,IAAa,eAAb,cAAkC,QAAQ,IAAI,WAAW,CAAC,CAGxD,CAAC,CAAC,CAAC;AAEL,IAAa,kBAAb,cAAqC,OAAO,QAAyB,CAAC,CAAC,mBAAmB,EACxF,QAAQ,OAAO,IAAI,aAAa;CAC9B,MAAM,WAAW,OAAO;CACxB,MAAM,YAAY,OAAO;CAEzB,OAAO;EACL,MAAM,aACJ,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,SAAS;GAEvB,IAAI,CAAC,OACH,OAAO,OAAO,OAAO,KACnB,IAAI,mBAAmB;IACrB;IACA,WAAW;IACX,uBAAO,IAAI,MAAM,UAAU,SAAS,uBAAuB;IAC3D,WAAW;GACb,CAAC,CACH;GAGF,IAAI,YAAY,OACd,OAAO;IACL,aAAa,MAAM;IACnB,UAAU;KACR,WAAW,MAAM,UAAU;KAC3B,SAAS,MAAM;KACf,aAAa,MAAM;IACrB;GACF;GAGF,OAAO;IACL,aAAa;IACb,UAAU;KACR,WAAW,MAAM;KACjB,SAAS,MAAM;KACf,aAAa,MAAM;IACrB;GACF;EACF,CAAC;EAEH,YAAY,aAAqB,OAAO,QAAQ,UAAU,aAAa,IAAI;CAC7E;AACF,CAAC,EACH,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,sBAAb,cAAyC,OAAO,QAA6B,CAAC,CAC5E,uBACA,EACE,QAAQ,OAAO,IAAI,aAAa;CAC9B,MAAM,0BAA0B,OAAO;CACvC,MAAM,iBAAiB,OAAO;CAC9B,MAAM,kBAAkB,OAAO;CAE/B,MAAM,cAAc,SAAiB,YACnC,WAAW,YAAY,WAAW,QAAQ,QAAQ,WAAW,IAAI,SAAS,IAAI;CAEhF,OAAO;EACL,aAAa,aACX,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,OAAO,gBAAgB,IAAI,QAAQ;GAEjD,IAAI,MAAM,aAAa;IACrB,OAAO,OAAO,SAAS,qCAAqC,EAAE,SAAS,CAAC;IAExE,OAAO;KACL,MAAM,MAAM;KACZ,UAAU,MAAM;IAClB;GACF;GAEA,MAAM,MAAM,MAAM,SAAS;GAC3B,IAAI,CAAC,KACH,OAAO,OAAO,OAAO,KACnB,IAAI,mBAAmB;IACrB;IACA,WAAW;IACX,uBAAO,IAAI,MAAM,UAAU,SAAS,wCAAwC;IAC5E,WAAW;GACb,CAAC,CACH;GAGF,MAAM,cAAc,WAAW,GAAG;GAElC,OAAO,wBAAwB,eAAe,UAAU,WAAW,CAAC,CAAC,KACnE,OAAO,UAAU,UACf,OAAO,SAAS,UAAU,SAAS,kCAAkC,OAAO,CAC9E,GACA,OAAO,UAAU,UACf,qBAAqB,OAAO,UAAU,QAAW,mBAAmB,IAAI,CAC1E,CACF;GAEA,OAAO,OAAO,SAAS,8BAA8B;IAAE;IAAU,KAAK;GAAY,CAAC;GAanF,OAAO;IACL,aAZkB,wBACjB,sBAAsB,UAAU,WAAW,CAAC,CAC5C,KACC,OAAO,UAAU,UACf,OAAO,SAAS,UAAU,SAAS,8BAA8B,OAAO,CAC1E,GACA,OAAO,UAAU,UACf,qBAAqB,OAAO,UAAU,QAAW,eAAe,KAAK,CACvE,CACF;IAIA,UAAU,MAAM;GAClB;EACF,CAAC;EAEH,oBAAyC,UAAkB,iBACzD,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,OAAO,OAAO,UAAU,IAAI,aAAa,KAAK,CAAC,CAAC,CAAC,KAChE,OAAO,UAAU,UACf,OAAO,SAAS,UAAU,SAAS,qCAAqC,OAAO,CACjF,GACA,OAAO,UAAU,UACf,qBAAqB,OAAO,UAAU,QAAW,sBAAsB,KAAK,CAC9E,CACF;GAEA,AAAC,SAAS,KAAgB;GAE1B,OAAO;IACL,QAAQ;IACR,UAAU,aAAa;GACzB;EACF,CAAC;EAEH,mBACE,gBACA,QAIA,YAEA,OAAO,IAAI,aAAa;GACtB,MAAM,EAAE,WAAW;GAGnB,MAAM,qBAAqB,OAAO,SAChC,OAAO,aAAa,WAGpB,OAAO,WACP,OAAO,IACP,QACF,CAAC,CAAC,KACA,OAAO,UACJ,oBACC,IAAI,mBAAmB;IACrB,UAAU,OAAO;IACjB,WAAW;IACX,OAAO,gBAAgB;IACvB,WAAW;GACb,CAAC,CACL,CACF;GAGA,MAAM,mBAAmB,OAAO,SAC9B,OAAO,aAAa,SAGpB,OAAO,SACP,OAAO,IACP,QACF,CAAC,CAAC,KACA,OAAO,UACJ,oBACC,IAAI,mBAAmB;IACrB,UAAU,OAAO;IACjB,WAAW;IACX,OAAO,gBAAgB;IACvB,WAAW;GACb,CAAC,CACL,CACF;GAGA,MAAM,iBAAiB,OAAO,eAAe,eAAe;IAC1D,WAAW;IACX,SAAS;GACX,CAAC;GAED,MAAM,aAAa,OAAO,SACxB,OAAO,aAAa,WAGpB,eAAe,WACf,OAAO,IACP,QACF,CAAC,CAAC,KACA,OAAO,UACJ,oBACC,IAAI,mBAAmB;IACrB,UAAU,OAAO;IACjB,WAAW;IACX,OAAO,gBAAgB;IACvB,WAAW;GACb,CAAC,CACL,CACF;GAGA,MAAM,QAAQ,OAAO,MAAM,KAAK;GAGhC,MAAM,UAAU,OAAO,MAAM;GAa7B,MAAM,UAAU,OAAO,OACpB,WACC;IAAE,WAAW;IAAY,SAAS,eAAe;GAAQ,GACzD,WAAW,CAAC,GACZ,EAdF,eAAe,KAAU,UACtB,MAAM,iBAAiB,OAAO,SAAS,KAAK,CAAC,CAAS,KACrD,OAAO,KAAK,QAAaA,QAAc,IAAI,KAAK,GAAG,CAAC,CACtD,EAWI,CACN,CAAC,CACA,KACC,OAAO,eAAe,aAAa,OAAO,EAAE,GAC5C,OAAO,eAAe,MAAM,OAAO,KAAK,GACxC,OAAO,eACL,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAS,CAAC,CAAC,CAAC,KAC1C,OAAO,UAAU,eACf,OAAO,WACL,oCAAoC,OAAO,GAAG,0BAC9C,UACF,CACF,CACF,CACF,GACA,OAAO,UAAU,UACf,OAAO,SAAS,UAAU,OAAO,GAAG,oCAAoC,OAAO,CACjF,GACA,OAAO,UAAU,UACf,qBAAqB,OAAO,OAAO,IAAI,QAAW,qBAAqB,KAAK,CAC9E,CACF;GAEF,OAAO;IACL;IACA,UAAU,eAAe;IACzB,QAAQ;KAAE,WAAW;KAAY,SAAS,eAAe;IAAQ;IACjE;IACA;GACF;EACF,CAAC;CACL;AACF,CAAC,EACH,CACF,CAAC,CAAC,CAAC"}