UNPKG

every-plugin

Version:
1 lines 8.71 kB
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import type { AnyContractRouter, AnySchema, InferSchemaOutput } from \"@orpc/contract\";\nimport { ORPCError } from \"@orpc/contract\";\nimport type { Context, Implementer, Router } from \"@orpc/server\";\nimport { implement, onError } from \"@orpc/server\";\nimport { Effect, 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\ntype PluginDefinition<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Context,\n P extends Record<string, unknown>,\n> = {\n variables: V;\n secrets: S;\n contract: TContract;\n context?: TRequestContext;\n initialize?: (\n config: PluginInitializeInput<V, S>,\n plugins: P,\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 Context = 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 Context = 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 ): 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 Context = 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 Context = 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 ): Effect.Effect<TDeps, unknown, Scope.Scope> {\n const init = config.initialize ?? (() => Effect.succeed({} as TDeps));\n\n return init(pluginConfig, plugins as P).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 formatORPCError(error);\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 Context = 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 Context = 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":";;;;;;AA+GA,MAAa,eAA+B,SAAS,aAOnD,QAAsE;CACtE,MAAM,eAAuD;EAC3D,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;EACjB;CAED,MAAM,cAAyE;;EAE7E;EACA,AAAS,WAAW,OAAO;EAC3B,AAAS,eAAe;EAExB,AAAQ,QAAsB;EAE9B,WACE,cACA,UAAmC,EAAE,EACO;AAG5C,WAFa,OAAO,qBAAqB,OAAO,QAAQ,EAAE,CAAU,GAExD,cAAc,QAAa,CAAC,KACtC,OAAO,KAAK,SACV,OAAO,WAAW;AAChB,SAAK,QAAQ;KACb,CACH,EACD,OAAO,UAAU,KAAK,MAAe,EACrC,OAAO,UAAU,UAAU,MAAiB,CAC7C;;EAGH,WAAuC;GACrC,MAAM,OAAO;AACb,UAAO,OAAO,IAAI,aAAa;AAC7B,QAAI,OAAO,YAAY,KAAK,MAC1B,QAAO,OACJ,SAAS,KAAK,MAAM,CACpB,KACC,OAAO,UAAU,UACf,OAAO,WAAW,mCAAmC,KAAK,MAAM,MAAM,CACvE,CACF;AAEL,SAAK,QAAQ;KACb;;EAGJ,aAAa,MAAqC;GAChD,MAAM,OAAO,UAAU,OAAO,SAAS,CAAC,UAA0C;GAClF,MAAM,kBAAkB,SAAS,UAAmB;IAClD,MAAM,YAAY,wBAAwB,MAAM;AAEhD,QAAI,cAAc,SAAS,qBAAqB,UAC9C,OAAM;AAGR,oBAAgB,MAAM;AACtB,UAAM;KACN;GAEF,MAAM,UAAW,KAAa,IAAI,gBAAgB;AAElD,UADe,OAAO,aAAa,MAAM,QAC5B;;;CAIjB,MAAM,oBAAoB;AAU1B,mBAAkB,UAAU;EAC1B,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;EACjB;AAED,QAAO;;AAaT,SAAgB,cAA6E;AAC3F,SAOE,WACG,aAAyD,OAAc;;AAG9E,aAAa,cAAc"}