[
  {
    "path": ".editorconfig",
    "content": "root = true\n[package.json]\nindent_style = space\nindent_size = 2\n\n[.prettierrc.json]\nprintWidth = 140\nindent_style = space\nindent_size =4 \n\n[*]\ninsert_final_newline = true\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"extends\": [\"airbnb-base\", \"airbnb-typescript/base\", \"prettier\"],\n    \"parserOptions\": {\n        \"project\": \"./tsconfig.json\"\n    },\n    \"rules\": {\n        \"max-len\": [\"error\", { \"code\": 140 }],\n        \"arrow-body-style\": \"off\",\n        \"import/prefer-default-export\": \"off\",\n        \"class-methods-use-this\": \"off\"\n    }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "yarn-error.log\nnode_modules\ndist\n.vscode\n"
  },
  {
    "path": ".prettierignore",
    "content": "# Ignore artifacts:\nbuild\ncoverage\ndist\nnode_modules\n"
  },
  {
    "path": ".prettierrc.js",
    "content": "module.exports = {\n    $schema: \"http://json.schemastore.org/prettierrc\",\n    trailingComma: \"all\",\n    tabWidth: 4,\n    singleQuote: false,\n    quoteProps: \"consistent\",\n    printWidth: 140,\n    semi: true,\n    overrides: [\n        {\n            files: \"src/**/*.ts\",\n            options: {\n                parser: \"typescript\",\n            },\n        },\n    ],\n};\n"
  },
  {
    "path": "README.MD",
    "content": "# tRPC - Nest.JS Adapter\n\n- Allows you to use Trpc with Nest.JS.\n- Allows you to use request scoped Nest.JS providers\n- Allows you to use Nest.JS's great module system\n\n\n## Feature support\nI don't use all of the features tRPC has :/ \n\nBoth __queries__ and __mutations__ work. \n\nI __haven't__ tested subscriptions yet.\n\nBatching __doesn't__ work. Currently it't not possible to create multiple requests from 1 single HTTP request in Nest.JS (or it's a skill issue :) )\n\n## How\n\nSee the `example` folder in this repo, but briefly \n\n```bash\nyarn add trpc-nestjs-adapter\n```\n\n`main.ts`\n``` ts \n// Standard  nest.js main.ts\nimport { NestFactory } from '@nestjs/core';\nimport { NestExpressApplication } from '@nestjs/platform-express';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create<NestExpressApplication>(AppModule);\n  await app.listen(3000);\n}\n\nbootstrap();\n```\n\n`app.module.ts`\n```ts\nimport { Module } from '@nestjs/common';\nimport { TrpcModule } from 'trpc-nestjs-adapter';\nimport { rootRouter } from './trpc/root-trpc.router.ts';\nimport { createContext }  from './trpc/create-context.ts';\n\n@Module({\n  imports: [\n    AModule,\n    TrpcModule.forRoot({\n      path: '/trpc',\n      router: rootRouter,\n      createContext,\n    }),\n  ],\n})\nexport class AppModule { }\n\n```\n\n\n### Inside of your procedures\n\n```ts\nexport const exampleMutation = trpc.procedure\n    .input()\n    .mutation(async ({ ctx })=>{\n        const nestService = await ctx.resolveNestDependency(SomeNestService);\n\n        await nestService.someServiceMethod()\n    })\n```\n\n\n#### Note\nThe package is marked `alpha` for a reason, but mostly It's not documented very well.\n\nIf/When/As the package gains traction I'll improve the example & related docs.\n"
  },
  {
    "path": "example/README.md",
    "content": "# Example\n\nThis folder is meant to be an example of how to ues `trpc-nestjs-adapter` in your codebase.\n\nNot that you can't just copy-paste this out of this repo and into yours. At the moment, use it  as more of an inspiration. \n\nIf/When/As the package gains traction I'll improve the example & related docs."
  },
  {
    "path": "example/app.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TrpcModule } from '../lib/trpc.module';\nimport { AModule } from './domain-b/a.module';\nimport { appRouter } from './init-trpc';\n\n@Module({\n  imports: [\n    AModule,\n    TrpcModule.forRoot({\n      path: '/trpc',\n      router: appRouter,\n      createContext: () => ({}),\n    }),\n  ],\n})\nexport class AppModule { }\n"
  },
  {
    "path": "example/domain-b/a.module.ts",
    "content": "import { Module } from '@nestjs/common';\n\nimport { AService } from './a.service';\n\n@Module({\n  providers: [\n    AService,\n  ],\n})\nexport class AModule { }\n"
  },
  {
    "path": "example/domain-b/a.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class AService {\n  smth() {\n    return {\n      a: true,\n    };\n  }\n}\n"
  },
  {
    "path": "example/index.ts",
    "content": "import { NestFactory } from '@nestjs/core';\nimport { NestExpressApplication } from '@nestjs/platform-express';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create<NestExpressApplication>(AppModule);\n  await app.listen(3000);\n}\n\nbootstrap();\n"
  },
  {
    "path": "example/init-trpc.ts",
    "content": "import { initTRPC } from '@trpc/server';\nimport { InferContextType } from '../lib/infer-context-type.type';\nimport { AService } from './domain-b/a.service';\n\n// You can use any variable name you like.\n// We use t to keep things simple.\ntype CtxType = InferContextType<typeof createContext>;\nconst createContext = () => ({\n  someValueOnContext: 'randomValue',\n});\nconst trpc = initTRPC.context<CtxType>().create({\n\n});\n\nconst { router } = trpc;\nconst publicProcedure = trpc.procedure;\n\nexport const appRouter = router({\n  something: publicProcedure.query(async ({ ctx }) => {\n    const service = await ctx.resolveNestDependency(AService);\n    console.log({ service });\n    return service.smth();\n  }),\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"trpc-nestjs-adapter\",\n    \"version\": \"1.0.0-alpha.11\",\n    \"description\": \"TRPC adapter for NestJS\",\n    \"keywords\": [\n        \"nestjs\",\n        \"nest\",\n        \"trpc\",\n        \"adapter\"\n    ],\n    \"author\": {\n        \"name\": \"Darko Stojkovski\",\n        \"email\": \"dar3.st@gmail.com\"\n    },\n    \"license\": \"MIT\",\n    \"main\": \"./dist/lib/index.js\",\n    \"types\": \"./dist/lib/index.d.ts\",\n    \"exports\": {\n        \".\": {\n            \"types\": \"./dist/lib/index.d.ts\",\n            \"import\": \"./dist/lib/index.js\",\n            \"require\": \"./dist/lib/index.js\"\n        },\n        \"./package.json\": \"./package.json\"\n    },\n    \"scripts\": {\n        \"build\": \"tsc\",\n        \"dev\": \"tsc -w\",\n        \"prelint\": \"prettier --check src/\",\n        \"lint\": \"eslint --ext .ts src/\",\n        \"lint:fix\": \"prettier --write src/; eslint --ext .ts src/ --fix\"\n    },\n    \"peerDependencies\": {\n        \"@nestjs/common\": \"^9.2.1\",\n        \"@nestjs/core\": \"^9.2.1\",\n        \"@nestjs/platform-express\": \"^9.2.1\",\n        \"@trpc/server\": \"~10.14.0\",\n        \"reflect-metadata\": \"^0.1.12\",\n        \"rxjs\": \"^7.1.0\"\n    },\n    \"dependencies\": {},\n    \"devDependencies\": {\n        \"@nestjs/common\": \"^9.2.1\",\n        \"@nestjs/core\": \"^9.2.1\",\n        \"@nestjs/platform-express\": \"^9.2.1\",\n        \"@trpc/server\": \"~10.14.0\",\n        \"@types/node\": \"^16.0.0\",\n        \"@typescript-eslint/eslint-plugin\": \"^5.48.2\",\n        \"@typescript-eslint/parser\": \"^5.48.2\",\n        \"eslint\": \"^8.2.0\",\n        \"eslint-config-airbnb-base\": \"15.0.0\",\n        \"eslint-config-airbnb-typescript\": \"^17.0.0\",\n        \"eslint-config-prettier\": \"^9.0.0\",\n        \"eslint-plugin-import\": \"^2.25.2\",\n        \"nodemon\": \"^2.0.20\",\n        \"prettier\": \"^3.0.3\",\n        \"reflect-metadata\": \"^0.1.12\",\n        \"rxjs\": \"^7.1.0\",\n        \"typescript\": \"^4.9.4\"\n    },\n    \"files\": [\n        \"dist\"\n    ]\n}\n"
  },
  {
    "path": "src/attach-trpc-to-express-app.ts",
    "content": "import { NestExpressApplication } from \"@nestjs/platform-express\";\nimport { TrpcModuleOptions } from \"./trpc-module-options.type\";\n\nimport { buildTrpcNestMiddleware, BuildTrpcNestMiddlewareOptions } from \"./build-trpc-nest-middleware\";\n\ninterface Options extends TrpcModuleOptions, BuildTrpcNestMiddlewareOptions {\n    expressApp: NestExpressApplication;\n}\n\n/**\n * Attaches a TRPC router to your nestExpressApp\n * @param options: Options\n */\nexport function attachTrpcToExpressApp({ router, moduleRef, createContext, path, expressApp }: Options): void {\n    const trpcNestMiddleware = buildTrpcNestMiddleware({\n        router,\n        moduleRef,\n        createContext,\n    });\n\n    expressApp.use(path, trpcNestMiddleware);\n}\n"
  },
  {
    "path": "src/build-nest-resolver.ts",
    "content": "/* eslint-disable no-underscore-dangle */\nimport { ContextIdFactory, ModuleRef } from \"@nestjs/core\";\n\nexport function buildNestResolver(req: any, moduleRef: ModuleRef) {\n    // Retrieve the contextId specific to this request\n    let contextId = ContextIdFactory.getByRequest(req);\n\n    // Effectively a provider for the `REQUEST` token\n    moduleRef.registerRequestByContextId(req, contextId);\n\n    const resolveNestDependency: ModuleRef[\"resolve\"] = (typeOrToken) => {\n        return moduleRef.resolve(typeOrToken, contextId, { strict: false });\n    };\n\n    // API Candidates\n    // const requestScopedService = await ctx.nestResolver.resolve(RequestScopedService);\n    // const requestScopedService = await ctx.nestResolver(RequestScopedService);\n    // const requestScopedService = await ctx.nestResolve(RequestScopedService);\n    // const requestScopedService = await ctx.resolveNestDependency(RequestScopedService);\n    // const requestScopedService = await ctx.resolveNest(RequestScopedService);\n    // const requestScopedService = await ctx.resolve(RequestScopedService);\n\n    return {\n        /**\n         * Resolves any NestJS dependency from your project.\n         *\n         * Return type of this function is automatically inferred.\n         *\n         * Returns a promise which resolves to the dependency.\n         */\n        resolveNestDependency,\n        /**\n         *\n         * \"resets\" the DI subtree from which dependencies will be resolved.\n         *\n         * All subsequent calls to `resolveNestDependency` will resolve dependencies from the new subtree\n         * `REQUEST` scoped dependencies will be re-created.\n         *\n         * use-case: testing\n         */\n        resetDiSubtree: () => {\n            contextId = ContextIdFactory.create();\n            moduleRef.registerRequestByContextId(req, contextId);\n        },\n        /**\n         * used to attach anything to the request object\n         *\n         * This is useful if you want to have something specific to the trpc request and later access it in a NestJS provider\n         *\n         * examples: req scoped prisma client, req scoped logger, etc...\n         */\n        attachToReqObject: (_trpc_nest_adapter_meta: Record<string, any>) => {\n            req._trpc_nest_adapter_meta = _trpc_nest_adapter_meta;\n        },\n    };\n}\n"
  },
  {
    "path": "src/build-trpc-nest-middleware.ts",
    "content": "import { createHTTPHandler } from \"@trpc/server/adapters/standalone\";\nimport { ModuleRef } from \"@nestjs/core\";\nimport { AnyRouter } from \"@trpc/server\";\nimport { buildNestResolver } from \"./build-nest-resolver\";\n\nexport interface BuildTrpcNestMiddlewareOptions {\n    /** Your TRPC Router */\n    router: AnyRouter;\n\n    /** The NestJS ModuleRef */\n    moduleRef: ModuleRef;\n\n    /** A function that returns the context object as used with TRPC */\n    createContext: () => any;\n}\n\n/**\n * Builds an Express middleware that handles all trpc requests.\n *\n * The middleware will add a `resolveNestDependency` property to the context\n *\n * `resolveNestDependency` is a function that can be used to resolve NestJS providers\n *\n * @param req Express request object\n * @param moduleRef The moduleRef from the NestJS app\n * @param createContext A function that returns the context object as used with TRPC\n * @returns Express middleware which is capable of handling trpc requests\n */\nexport function buildTrpcNestMiddleware({ moduleRef, router, createContext }: BuildTrpcNestMiddlewareOptions) {\n    return function trpcNestMiddleware(req: any, res: any) {\n        const { resolveNestDependency, attachToReqObject, resetDiSubtree } = buildNestResolver(req, moduleRef);\n\n        return createHTTPHandler({\n            router,\n            createContext: () => {\n                const userProvidedContext = createContext();\n\n                return {\n                    ...userProvidedContext,\n                    resolveNestDependency,\n                    attachToReqObject,\n                    resetDiSubtree,\n                };\n            },\n        })(req, res);\n    };\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export { TrpcModule } from \"./trpc.module\";\nexport type { InferContextType } from \"./infer-context-type.type\";\n"
  },
  {
    "path": "src/infer-context-type.type.ts",
    "content": "import { inferAsyncReturnType } from \"@trpc/server\";\nimport { NestResolver } from \"./nest-resolver.type\";\n\nexport type InferContextType<TContext> = TContext extends () => any ? inferAsyncReturnType<TContext> & NestResolver : NestResolver;\n"
  },
  {
    "path": "src/nest-resolver.type.ts",
    "content": "/* eslint-disable max-len */\ninterface Type<T = any> extends Function {\n    new (...args: any[]): T;\n}\n\nexport interface NestResolver {\n    /**\n     * Resolves any NestJS dependency from your project. a proxy to `moduleRef.get()`\n     *\n     * Return type of this function is automatically inferred.\n     *\n     * Returns a promise which resolves to the dependency.\n     */\n    resolveNestDependency: <TInput = any, TResult = TInput>(typeOrToken: Type<TInput> | Function | string | symbol) => Promise<TResult>;\n    attachToReqObject: (anything: Record<string, any>) => void;\n    resetDiSubtree: () => void;\n}\n"
  },
  {
    "path": "src/tokens.ts",
    "content": "export const TRPC_ROUTER_TOKEN = Symbol(\"TRPC_ROUTER_TOKEN\");\nexport const TRPC_PATH_TOKEN = Symbol(\"TRPC_PATH_TOKEN\");\nexport const TRPC_CREATE_CONTEXT_TOKEN = Symbol(\"TRPC_CREATE_CONTEXT_TOKEN\");\n"
  },
  {
    "path": "src/trpc-module-options.type.ts",
    "content": "import { AnyRouter } from \"@trpc/server\";\n\nexport interface TrpcModuleOptions<TRouter = AnyRouter> {\n    path: \"/trpc\" | string;\n    router: TRouter;\n    createContext: () => any;\n}\n"
  },
  {
    "path": "src/trpc.module.ts",
    "content": "import { DynamicModule, Inject, Module, OnModuleInit } from \"@nestjs/common\";\nimport { HttpAdapterHost, ModuleRef } from \"@nestjs/core\";\nimport { attachTrpcToExpressApp } from \"./attach-trpc-to-express-app\";\nimport { TRPC_CREATE_CONTEXT_TOKEN, TRPC_PATH_TOKEN, TRPC_ROUTER_TOKEN } from \"./tokens\";\nimport { TrpcModuleOptions } from \"./trpc-module-options.type\";\n\n@Module({})\nexport class TrpcModule implements OnModuleInit {\n    constructor(private moduleRef: ModuleRef) {}\n\n    @Inject()\n    private readonly httpAdapterHost!: HttpAdapterHost;\n\n    @Inject(TRPC_ROUTER_TOKEN)\n    private readonly router!: TrpcModuleOptions[\"router\"];\n\n    @Inject(TRPC_PATH_TOKEN)\n    private readonly path!: TrpcModuleOptions[\"path\"];\n\n    @Inject(TRPC_CREATE_CONTEXT_TOKEN)\n    private readonly createContext!: TrpcModuleOptions[\"createContext\"];\n\n    static forRoot(options: TrpcModuleOptions): DynamicModule {\n        if (!options.createContext || !options.path || !options.router) {\n            throw new Error(\"Please supply all of the required options to TrpcModule\");\n        }\n\n        return {\n            module: TrpcModule,\n            providers: [\n                { provide: TRPC_ROUTER_TOKEN, useValue: options.router },\n                { provide: TRPC_PATH_TOKEN, useValue: options.path },\n                { provide: TRPC_CREATE_CONTEXT_TOKEN, useValue: options.createContext },\n            ],\n        };\n    }\n\n    onModuleInit() {\n        attachTrpcToExpressApp({\n            moduleRef: this.moduleRef,\n            expressApp: this.httpAdapterHost.httpAdapter.getInstance(),\n            path: this.path,\n            createContext: this.createContext,\n            router: this.router,\n        });\n    }\n}\n"
  },
  {
    "path": "test/app.controller.ts",
    "content": "import { Controller, Get } from '@nestjs/common';\n\n@Controller()\nexport class AppController {\n  // eslint-disable-next-line class-methods-use-this\n  @Get('/status')\n  status() {\n    return { status: 'ok' };\n  }\n}\n"
  },
  {
    "path": "test/app.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { AppController } from './app.controller';\nimport { appRouter } from './init-trpc';\nimport { RequestScopedService } from './request-scoped.service';\nimport { TrpcModule } from '../lib/trpc.module';\nimport { AModule } from '../example/domain-b/a.module';\n\n@Module({\n  controllers: [AppController],\n  providers: [\n    RequestScopedService,\n  ],\n  imports: [\n    AModule,\n    TrpcModule.forRoot({\n      path: '/trpc',\n      router: appRouter,\n      createContext: () => {\n        'randomValue';\n      },\n    }),\n  ],\n})\nexport class AppModule { }\n"
  },
  {
    "path": "test/app.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class AppService {\n  // eslint-disable-next-line class-methods-use-this\n  doesSomething() {\n    return {\n      done: true,\n    };\n  }\n}\n"
  },
  {
    "path": "test/init-trpc.ts",
    "content": "import { initTRPC } from '@trpc/server';\nimport { RequestScopedService } from './request-scoped.service';\nimport { InferContextType } from '../lib/infer-context-type.type';\n\n// You can use any variable name you like.\n// We use t to keep things simple.\ntype CtxType = InferContextType<typeof createContext>;\nconst createContext = () => ({\n  someValueOnContext: 'randomValue',\n});\nconst trpc = initTRPC.context<CtxType>().create({\n\n});\n\nconst { router } = trpc;\nconst publicProcedure = trpc.procedure;\n\nexport const appRouter = router({\n  something: publicProcedure.query(async ({ ctx }) => {\n    const requestScopedService = await ctx.resolveNestDependency(RequestScopedService);\n\n    return requestScopedService.doesSomething();\n  }),\n});\n"
  },
  {
    "path": "test/main.ts",
    "content": "import { NestFactory } from '@nestjs/core';\nimport { NestExpressApplication } from '@nestjs/platform-express';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create<NestExpressApplication>(AppModule);\n  console.log({ app });\n  await app.listen(3000);\n}\n\nbootstrap();\n"
  },
  {
    "path": "test/request-scoped.service.ts",
    "content": "import { Inject, Injectable } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\n\n@Injectable()\nexport class RequestScopedService {\n  @Inject(REQUEST)\n  private readonly req: any;\n\n  doesSomething() {\n    return {\n      done: true,\n      isRequestScoped: true,\n      hostHeader: this.req.headers.host,\n    };\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2020\",\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"module\": \"commonjs\",\n    \"rootDir\": \"./src\",\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"outDir\": \"./dist\",\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"strict\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  }
]