UNPKG

every-plugin

Version:
1 lines 9.88 kB
{"version":3,"file":"plugin.cjs","names":["Context","Effect","extractFromFiberFailure","ORPCError","formatORPCError"],"sources":["../src/plugin.ts"],"sourcesContent":["import type { AnyContractRouter, AnySchema, InferSchemaOutput } from \"@orpc/contract\";\nimport { ORPCError } from \"@orpc/contract\";\nimport type { Implementer, Router } from \"@orpc/server\";\nimport { implement, onError } from \"@orpc/server\";\nimport { Context, Effect, type Context as EffectContext, type Layer, type Scope } from \"effect\";\nimport { extractFromFiberFailure, formatORPCError } from \"./runtime/errors\";\n\ntype ContextOutput<T> = T extends AnySchema ? InferSchemaOutput<T> : Record<string, never>;\n\nexport type PluginConfigFor<\n V extends AnySchema,\n S extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n> = {\n variables: V;\n secrets: S;\n context: TRequestContext;\n};\n\ntype PluginInitializeInput<V extends AnySchema, S extends AnySchema> = {\n variables: InferSchemaOutput<V>;\n secrets: InferSchemaOutput<S>;\n};\n\n/**\n * Tools provided to plugin initialize/services for building long-lived scoped resources.\n */\ntype ServiceOf<T> = T extends EffectContext.Tag<any, infer S> ? S : never;\n\nexport type PluginServicesTools = {\n buildService: <T>(tag: T, layer: Layer.Layer<any, any, any>) => Effect.Effect<ServiceOf<T>>;\n};\n\nexport class PluginIdTag extends Context.Tag(\"PluginId\")<PluginIdTag, string>() {}\n\ntype PluginDefinition<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any>,\n P extends Record<string, unknown>,\n> = {\n variables: V;\n secrets: S;\n contract: TContract;\n context?: TRequestContext;\n /**\n * Initialize the plugin and build dependencies.\n * A third argument `tools` is provided for building long-lived scoped resources\n * (e.g. database pools) via `tools.buildService(tag, layer)`.\n */\n initialize?: (\n config: PluginInitializeInput<V, S>,\n plugins: P,\n tools: PluginServicesTools,\n ) => Effect.Effect<TDeps, Error, Scope.Scope>;\n createRouter: (\n deps: TDeps,\n builder: Implementer<TContract, ContextOutput<TRequestContext>, ContextOutput<TRequestContext>>,\n ) => Router<TContract, any>;\n shutdown?: (deps: TDeps) => Effect.Effect<void, Error, never>;\n};\n\n/**\n * Loaded plugin with static binding property\n */\nexport interface LoadedPluginWithBinding<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n> {\n new (): Plugin<TContract, TVariables, TSecrets, TRequestContext, TDeps>;\n binding: {\n contract: TContract;\n variables: TVariables;\n secrets: TSecrets;\n context: TRequestContext;\n };\n}\n\n/**\n * Plugin interface\n */\nexport interface Plugin<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n> {\n readonly id: string;\n readonly contract: TContract;\n readonly configSchema: PluginConfigFor<TVariables, TSecrets, TRequestContext>;\n\n initialize(\n config: PluginInitializeInput<TVariables, TSecrets>,\n plugins: Record<string, unknown>,\n tools: PluginServicesTools,\n ): Effect.Effect<TDeps, unknown, Scope.Scope>;\n\n shutdown(): Effect.Effect<void, never>;\n\n /**\n * Creates the strongly-typed oRPC router for this plugin.\n * The router's procedure types are inferred directly from the contract.\n * @param deps The initialized plugin dependencies\n * @returns A router with procedures matching the plugin's contract\n */\n createRouter(deps: TDeps): Router<TContract, any>;\n}\n\nexport interface CreatePluginFn {\n <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n ): LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n\n withPlugins: <P extends Record<string, unknown>>() => CreatePluginWithPlugins<P>;\n}\n\nexport const createPlugin: CreatePluginFn = function createPlugin<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>) {\n const configSchema: PluginConfigFor<V, S, TRequestContext> = {\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n };\n\n class CreatedPlugin implements Plugin<TContract, V, S, TRequestContext, TDeps> {\n /** set during instantiation - registry key */\n id!: string;\n readonly contract = config.contract;\n readonly configSchema = configSchema;\n\n private _deps: TDeps | null = null;\n\n initialize(\n pluginConfig: PluginInitializeInput<V, S>,\n plugins: Record<string, unknown> = {},\n tools: PluginServicesTools,\n ): Effect.Effect<TDeps, unknown, Scope.Scope> {\n const init = config.initialize ?? (() => Effect.succeed({} as TDeps));\n\n return init(pluginConfig, plugins as P, tools).pipe(\n Effect.tap((deps) =>\n Effect.sync(() => {\n this._deps = deps;\n }),\n ),\n Effect.map(() => this._deps as TDeps),\n Effect.mapError((error) => error as unknown),\n );\n }\n\n shutdown(): Effect.Effect<void, never> {\n const self = this;\n return Effect.gen(function* () {\n if (config.shutdown && self._deps) {\n yield* config\n .shutdown(self._deps)\n .pipe(\n Effect.catchAll((error) =>\n Effect.logWarning(`Plugin shutdown hook failed for ${self.id}`, error),\n ),\n );\n }\n self._deps = null;\n });\n }\n\n createRouter(deps: TDeps): Router<TContract, any> {\n const base = implement(config.contract).$context<ContextOutput<TRequestContext>>();\n const errorMiddleware = onError((error: unknown) => {\n const unwrapped = extractFromFiberFailure(error);\n\n if (unwrapped !== error && unwrapped instanceof ORPCError) {\n throw unwrapped;\n }\n\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n throw error;\n }) as any;\n\n const builder = (base as any).use(errorMiddleware);\n const router = config.createRouter(deps, builder as any);\n return router as Router<TContract, any>;\n }\n }\n\n const PluginConstructor = CreatedPlugin as unknown as {\n new (): Plugin<TContract, V, S, TRequestContext, TDeps>;\n binding: {\n contract: TContract;\n variables: V;\n secrets: S;\n context: TRequestContext;\n };\n };\n\n PluginConstructor.binding = {\n contract: config.contract,\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n };\n\n return PluginConstructor as LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n};\n\nexport type CreatePluginWithPlugins<P extends Record<string, unknown>> = <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n>(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n) => LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n\nexport function withPlugins<P extends Record<string, unknown>>(): CreatePluginWithPlugins<P> {\n return <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n ) => createPlugin<V, S, TContract, TRequestContext, TDeps, P>(config as any);\n}\n\ncreatePlugin.withPlugins = withPlugins;\n"],"mappings":";;;;;;AAiCA,IAAa,cAAb,cAAiCA,eAAQ,IAAI,UAAU,CAAC,CAAsB,CAAC,CAAC,CAAC;AAgGjF,MAAa,eAA+B,SAAS,aAOnD,QAAsE;CACtE,MAAM,eAAuD;EAC3D,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;CAClB;CAEA,MAAM,cAAyE;;EAE7E;EACA,AAAS,WAAW,OAAO;EAC3B,AAAS,eAAe;EAExB,AAAQ,QAAsB;EAE9B,WACE,cACA,UAAmC,CAAC,GACpC,OAC4C;GAG5C,QAFa,OAAO,qBAAqBC,cAAO,QAAQ,CAAC,CAAU,GAExD,CAAC,cAAc,SAAc,KAAK,CAAC,CAAC,KAC7CA,cAAO,KAAK,SACVA,cAAO,WAAW;IAChB,KAAK,QAAQ;GACf,CAAC,CACH,GACAA,cAAO,UAAU,KAAK,KAAc,GACpCA,cAAO,UAAU,UAAU,KAAgB,CAC7C;EACF;EAEA,WAAuC;GACrC,MAAM,OAAO;GACb,OAAOA,cAAO,IAAI,aAAa;IAC7B,IAAI,OAAO,YAAY,KAAK,OAC1B,OAAO,OACJ,SAAS,KAAK,KAAK,CAAC,CACpB,KACCA,cAAO,UAAU,UACfA,cAAO,WAAW,mCAAmC,KAAK,MAAM,KAAK,CACvE,CACF;IAEJ,KAAK,QAAQ;GACf,CAAC;EACH;EAEA,aAAa,MAAqC;GAChD,MAAM,mCAAiB,OAAO,QAAQ,CAAC,CAAC,SAAyC;GACjF,MAAM,6CAA2B,UAAmB;IAClD,MAAM,YAAYC,uCAAwB,KAAK;IAE/C,IAAI,cAAc,SAAS,qBAAqBC,0BAC9C,MAAM;IAGR,MAAM,YAAYC,+BAAgB,KAAK;IACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACtC,MAAM;GACR,CAAC;GAED,MAAM,UAAW,KAAa,IAAI,eAAe;GAEjD,OADe,OAAO,aAAa,MAAM,OAC7B;EACd;CACF;CAEA,MAAM,oBAAoB;CAU1B,kBAAkB,UAAU;EAC1B,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;CAClB;CAEA,OAAO;AACT;AAYA,SAAgB,cAA6E;CAC3F,QAOE,WACG,aAAyD,MAAa;AAC7E;AAEA,aAAa,cAAc"}