├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yaml │ ├── release-next.yaml │ └── release.yaml ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── apps └── content │ ├── .gitignore │ ├── .vitepress │ ├── .gitignore │ ├── assets │ │ └── nodejs-logo-icon.svg │ ├── config.ts │ └── theme │ │ ├── components │ │ ├── AsideSponsors.vue │ │ ├── Banner.vue │ │ └── FullSponsors.vue │ │ ├── composables │ │ └── sponsors.ts │ │ ├── custom.css │ │ └── index.ts │ ├── blog │ └── v1-announcement.md │ ├── docs │ ├── adapters │ │ ├── astro.md │ │ ├── browser.md │ │ ├── electron.md │ │ ├── elysia.md │ │ ├── express.md │ │ ├── fastify.md │ │ ├── hono.md │ │ ├── http.md │ │ ├── message-port.md │ │ ├── next.md │ │ ├── nuxt.md │ │ ├── react-native.md │ │ ├── remix.md │ │ ├── solid-start.md │ │ ├── svelte-kit.md │ │ ├── tanstack-start.md │ │ ├── websocket.md │ │ └── worker-threads.md │ ├── advanced │ │ ├── exceeds-the-maximum-length-problem.md │ │ ├── mocking.md │ │ ├── rpc-json-serializer.md │ │ ├── rpc-protocol.md │ │ ├── superjson.md │ │ └── validation-errors.md │ ├── best-practices │ │ ├── dedupe-middleware.md │ │ ├── no-throw-literal.md │ │ └── optimize-ssr.md │ ├── client │ │ ├── client-side.md │ │ ├── dynamic-link.md │ │ ├── error-handling.md │ │ ├── event-iterator.md │ │ ├── rpc-link.md │ │ └── server-side.md │ ├── comparison.md │ ├── context.md │ ├── contract-first │ │ ├── define-contract.md │ │ ├── implement-contract.md │ │ └── router-to-contract.md │ ├── ecosystem.md │ ├── error-handling.md │ ├── event-iterator.md │ ├── file-upload-download.md │ ├── getting-started.md │ ├── integrations │ │ ├── hey-api.md │ │ ├── pinia-colada.md │ │ ├── tanstack-query-old │ │ │ ├── basic.md │ │ │ ├── react.md │ │ │ ├── solid.md │ │ │ ├── svelte.md │ │ │ └── vue.md │ │ └── tanstack-query.md │ ├── lifecycle.md │ ├── metadata.md │ ├── middleware.md │ ├── openapi │ │ ├── advanced │ │ │ ├── openapi-json-serializer.md │ │ │ └── redirect-response.md │ │ ├── bracket-notation.md │ │ ├── client │ │ │ └── openapi-link.md │ │ ├── error-handling.md │ │ ├── getting-started.md │ │ ├── input-output-structure.md │ │ ├── integrations │ │ │ └── implement-contract-in-nest.md │ │ ├── openapi-handler.md │ │ ├── openapi-specification.md │ │ ├── plugins │ │ │ ├── openapi-reference.md │ │ │ └── zod-smart-coercion.md │ │ ├── routing.md │ │ └── scalar.md │ ├── playgrounds.md │ ├── plugins │ │ ├── batch-request-response.md │ │ ├── body-limit.md │ │ ├── client-retry.md │ │ ├── cors.md │ │ ├── response-headers.md │ │ ├── simple-csrf-protection.md │ │ └── strict-get-method.md │ ├── procedure.md │ ├── router.md │ ├── rpc-handler.md │ └── server-action.md │ ├── examples │ └── openai-streaming.md │ ├── index.md │ ├── package.json │ ├── public │ ├── _redirects │ ├── code-dark.png │ ├── code-light.png │ ├── icon.svg │ ├── images │ │ ├── optimized-ssr-diagram.svg │ │ └── standard-ssr-diagram.svg │ ├── logo.webp │ ├── og.jpg │ └── orpc-lifecycle.svg │ ├── shared │ └── planet.ts │ ├── tsconfig.json │ └── vercel.json ├── eslint.config.js ├── knip.json ├── package.json ├── packages ├── arktype │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── converter.test.ts │ │ ├── converter.ts │ │ └── index.ts │ └── tsconfig.json ├── client │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── adapters │ │ │ ├── fetch │ │ │ │ ├── index.ts │ │ │ │ ├── link-fetch-client.test.ts │ │ │ │ ├── link-fetch-client.ts │ │ │ │ ├── rpc-link.test.ts │ │ │ │ └── rpc-link.ts │ │ │ ├── message-port │ │ │ │ ├── index.ts │ │ │ │ ├── link-client.ts │ │ │ │ ├── message-port.test.ts │ │ │ │ ├── message-port.ts │ │ │ │ ├── rpc-link.test.ts │ │ │ │ └── rpc-link.ts │ │ │ ├── standard │ │ │ │ ├── index.ts │ │ │ │ ├── link.test.ts │ │ │ │ ├── link.ts │ │ │ │ ├── plugin.test.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── rpc-json-serializer.test.ts │ │ │ │ ├── rpc-json-serializer.ts │ │ │ │ ├── rpc-link-codec.test.ts │ │ │ │ ├── rpc-link-codec.ts │ │ │ │ ├── rpc-link.test.ts │ │ │ │ ├── rpc-link.ts │ │ │ │ ├── rpc-serializer.test.ts │ │ │ │ ├── rpc-serializer.ts │ │ │ │ ├── types.ts │ │ │ │ ├── utils.test.ts │ │ │ │ └── utils.ts │ │ │ └── websocket │ │ │ │ ├── index.ts │ │ │ │ ├── link-websocket-client.ts │ │ │ │ ├── rpc-link.test.ts │ │ │ │ └── rpc-link.ts │ │ ├── client.test-d.ts │ │ ├── client.test.ts │ │ ├── client.ts │ │ ├── dynamic-link.test-d.ts │ │ ├── dynamic-link.test.ts │ │ ├── dynamic-link.ts │ │ ├── error.test-d.ts │ │ ├── error.test.ts │ │ ├── error.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── index.ts │ │ ├── plugins │ │ │ ├── batch.test.ts │ │ │ ├── batch.ts │ │ │ ├── index.ts │ │ │ ├── retry.test.ts │ │ │ ├── retry.ts │ │ │ ├── simple-csrf-protection.test.ts │ │ │ └── simple-csrf-protection.ts │ │ ├── types.test-d.ts │ │ ├── types.ts │ │ ├── utils.test-d.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.ts │ │ ├── helpers.ts │ │ └── shared.ts │ └── tsconfig.json ├── contract │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── builder-variants.test-d.ts │ │ ├── builder-variants.ts │ │ ├── builder.test-d.ts │ │ ├── builder.test.ts │ │ ├── builder.ts │ │ ├── config.test.ts │ │ ├── config.ts │ │ ├── error.test-d.ts │ │ ├── error.test.ts │ │ ├── error.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── index.ts │ │ ├── link-utils.test-d.ts │ │ ├── link-utils.test.ts │ │ ├── link-utils.ts │ │ ├── meta.test.ts │ │ ├── meta.ts │ │ ├── procedure-client.test-d.ts │ │ ├── procedure-client.ts │ │ ├── procedure.test.ts │ │ ├── procedure.ts │ │ ├── route.test.ts │ │ ├── route.ts │ │ ├── router-client.test-d.ts │ │ ├── router-client.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ ├── router.test-d.ts │ │ ├── router.ts │ │ ├── schema-utils.test.ts │ │ ├── schema-utils.ts │ │ ├── schema.test-d.ts │ │ ├── schema.test.ts │ │ ├── schema.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── helpers.ts │ │ └── shared.ts │ └── tsconfig.json ├── hey-api │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── to-orpc-client.test-d.ts │ │ ├── to-orpc-client.test.ts │ │ └── to-orpc-client.ts │ ├── tests │ │ ├── client │ │ │ ├── client.gen.ts │ │ │ ├── index.ts │ │ │ ├── sdk.gen.ts │ │ │ └── types.gen.ts │ │ └── spec.json │ └── tsconfig.json ├── nest │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── implement.test-d.ts │ │ ├── implement.test.ts │ │ ├── implement.ts │ │ ├── index.ts │ │ ├── utils.test-d.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── openapi-client │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── adapters │ │ │ ├── fetch │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-link.test.ts │ │ │ │ └── openapi-link.ts │ │ │ └── standard │ │ │ │ ├── bracket-notation-utils.test.ts │ │ │ │ ├── bracket-notation-utils.ts │ │ │ │ ├── bracket-notation.test.ts │ │ │ │ ├── bracket-notation.ts │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-json-serializer.test.ts │ │ │ │ ├── openapi-json-serializer.ts │ │ │ │ ├── openapi-link-codec.test.ts │ │ │ │ ├── openapi-link-codec.ts │ │ │ │ ├── openapi-link.test.ts │ │ │ │ ├── openapi-link.ts │ │ │ │ ├── openapi-serializer.test.ts │ │ │ │ ├── openapi-serializer.ts │ │ │ │ ├── utils.test.ts │ │ │ │ └── utils.ts │ │ ├── index.ts │ │ ├── types.test-d.ts │ │ └── types.ts │ └── tsconfig.json ├── openapi │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── adapters │ │ │ ├── aws-lambda │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-handler.test.ts │ │ │ │ └── openapi-handler.ts │ │ │ ├── fetch │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-handler.test.ts │ │ │ │ └── openapi-handler.ts │ │ │ ├── node │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-handler.test.ts │ │ │ │ └── openapi-handler.ts │ │ │ └── standard │ │ │ │ ├── index.ts │ │ │ │ ├── openapi-codec.test.ts │ │ │ │ ├── openapi-codec.ts │ │ │ │ ├── openapi-handler.test.ts │ │ │ │ ├── openapi-handler.ts │ │ │ │ ├── openapi-matcher.test.ts │ │ │ │ ├── openapi-matcher.ts │ │ │ │ ├── utils.test.ts │ │ │ │ └── utils.ts │ │ ├── index.ts │ │ ├── openapi-custom.test.ts │ │ ├── openapi-custom.ts │ │ ├── openapi-generator.test.ts │ │ ├── openapi-generator.ts │ │ ├── openapi-utils.test.ts │ │ ├── openapi-utils.ts │ │ ├── plugins │ │ │ ├── index.ts │ │ │ ├── openapi-reference.test.ts │ │ │ └── openapi-reference.ts │ │ ├── router-client.test.ts │ │ ├── router-client.ts │ │ ├── schema-converter.test.ts │ │ ├── schema-converter.ts │ │ ├── schema-utils.test.ts │ │ ├── schema-utils.ts │ │ └── schema.ts │ └── tsconfig.json ├── react-query │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json ├── react │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── action-form.test.ts │ │ ├── action-form.ts │ │ ├── hooks │ │ │ ├── action-hooks.test-d.ts │ │ │ ├── action-hooks.test.tsx │ │ │ ├── action-hooks.ts │ │ │ └── index.ts │ │ └── index.ts │ └── tsconfig.json ├── server │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── adapters │ │ │ ├── aws-lambda │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── bun-ws │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── crossws │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── fetch │ │ │ │ ├── body-limit-plugin.test.ts │ │ │ │ ├── body-limit-plugin.ts │ │ │ │ ├── handler.test-d.ts │ │ │ │ ├── handler.test.ts │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── plugin.test-d.ts │ │ │ │ ├── plugin.test.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── message-port │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── node │ │ │ │ ├── body-limit-plugin.test.ts │ │ │ │ ├── body-limit-plugin.ts │ │ │ │ ├── handler.test-d.ts │ │ │ │ ├── handler.test.ts │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── plugin.test-d.ts │ │ │ │ ├── plugin.test.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ ├── standard │ │ │ │ ├── handler.test.ts │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── plugin.test.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── rpc-codec.test.ts │ │ │ │ ├── rpc-codec.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ ├── rpc-handler.ts │ │ │ │ ├── rpc-matcher.test.ts │ │ │ │ ├── rpc-matcher.ts │ │ │ │ ├── types.ts │ │ │ │ ├── utils.test-d.ts │ │ │ │ ├── utils.test.ts │ │ │ │ └── utils.ts │ │ │ ├── websocket │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ │ └── ws │ │ │ │ ├── handler.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rpc-handler.test.ts │ │ │ │ └── rpc-handler.ts │ │ ├── builder-variants.test-d.ts │ │ ├── builder-variants.ts │ │ ├── builder.test-d.ts │ │ ├── builder.test.ts │ │ ├── builder.ts │ │ ├── config.test.ts │ │ ├── config.ts │ │ ├── context.test-d.ts │ │ ├── context.test.ts │ │ ├── context.ts │ │ ├── error.test-d.ts │ │ ├── error.test.ts │ │ ├── error.ts │ │ ├── implementer-procedure.test-d.ts │ │ ├── implementer-procedure.ts │ │ ├── implementer-variants.test-d.ts │ │ ├── implementer-variants.ts │ │ ├── implementer.test-d.ts │ │ ├── implementer.test.ts │ │ ├── implementer.ts │ │ ├── index.ts │ │ ├── lazy.test.ts │ │ ├── lazy.ts │ │ ├── middleware-decorated.test-d.ts │ │ ├── middleware-decorated.test.ts │ │ ├── middleware-decorated.ts │ │ ├── middleware-utils.test.ts │ │ ├── middleware-utils.ts │ │ ├── middleware.test-d.ts │ │ ├── middleware.ts │ │ ├── plugins │ │ │ ├── batch.test.ts │ │ │ ├── batch.ts │ │ │ ├── cors.test.ts │ │ │ ├── cors.ts │ │ │ ├── index.ts │ │ │ ├── response-headers.test.ts │ │ │ ├── response-headers.ts │ │ │ ├── simple-csrf-protection.test.ts │ │ │ ├── simple-csrf-protection.ts │ │ │ ├── strict-get-method.test.ts │ │ │ └── strict-get-method.ts │ │ ├── procedure-action.test-d.ts │ │ ├── procedure-action.test.ts │ │ ├── procedure-action.ts │ │ ├── procedure-client.test-d.ts │ │ ├── procedure-client.test.ts │ │ ├── procedure-client.ts │ │ ├── procedure-decorated.test-d.ts │ │ ├── procedure-decorated.test.ts │ │ ├── procedure-decorated.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── procedure.test-d.ts │ │ ├── procedure.test.ts │ │ ├── procedure.ts │ │ ├── router-client.test-d.ts │ │ ├── router-client.test.ts │ │ ├── router-client.ts │ │ ├── router-hidden.test.ts │ │ ├── router-hidden.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ ├── router.test-d.ts │ │ └── router.ts │ ├── tests │ │ ├── message-port.test.ts │ │ └── shared.ts │ └── tsconfig.json ├── shared │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── args.test-d.ts │ │ ├── args.test.ts │ │ ├── args.ts │ │ ├── array.test.ts │ │ ├── array.ts │ │ ├── chain.ts │ │ ├── function.test.ts │ │ ├── function.ts │ │ ├── id.test.ts │ │ ├── id.ts │ │ ├── index.ts │ │ ├── interceptor.test-d.ts │ │ ├── interceptor.test.ts │ │ ├── interceptor.ts │ │ ├── iterator.test.ts │ │ ├── iterator.ts │ │ ├── json.test-d.ts │ │ ├── json.test.ts │ │ ├── json.ts │ │ ├── object.test.ts │ │ ├── object.ts │ │ ├── queue.test.ts │ │ ├── queue.ts │ │ ├── types.test-d.ts │ │ ├── types.ts │ │ ├── value.test-d.ts │ │ ├── value.test.ts │ │ └── value.ts │ └── tsconfig.json ├── solid-query │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json ├── standard-server-aws-lambda │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── body.test.ts │ │ ├── body.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── headers.test.ts │ │ ├── headers.ts │ │ ├── index.ts │ │ ├── request.test.ts │ │ ├── request.ts │ │ ├── response.test.ts │ │ ├── response.ts │ │ ├── types.test-d.ts │ │ ├── types.ts │ │ ├── url.test.ts │ │ └── url.ts │ └── tsconfig.json ├── standard-server-fetch │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── playground │ │ ├── event-source-client.ts │ │ ├── event-source.http │ │ └── event-source.ts │ ├── src │ │ ├── body.test.ts │ │ ├── body.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── headers.test.ts │ │ ├── headers.ts │ │ ├── index.ts │ │ ├── request.test.ts │ │ ├── request.ts │ │ ├── response.test.ts │ │ └── response.ts │ └── tsconfig.json ├── standard-server-node │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── playground │ │ └── event-source.ts │ ├── src │ │ ├── body.test.ts │ │ ├── body.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── index.ts │ │ ├── method.test.ts │ │ ├── method.ts │ │ ├── request.test.ts │ │ ├── request.ts │ │ ├── response.test.ts │ │ ├── response.ts │ │ ├── signal.test.ts │ │ ├── signal.ts │ │ ├── types.ts │ │ ├── url.test.ts │ │ └── url.ts │ └── tsconfig.json ├── standard-server-peer │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── client.test.ts │ │ ├── client.ts │ │ ├── codec.test.ts │ │ ├── codec.ts │ │ ├── event-iterator.test.ts │ │ ├── event-iterator.ts │ │ ├── index.ts │ │ ├── server.test.ts │ │ ├── server.ts │ │ └── types.ts │ ├── tests │ │ └── e2e.test.ts │ └── tsconfig.json ├── standard-server │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── batch │ │ │ ├── index.ts │ │ │ ├── request.test.ts │ │ │ ├── request.ts │ │ │ ├── response.test.ts │ │ │ ├── response.ts │ │ │ ├── signal.test.ts │ │ │ └── signal.ts │ │ ├── event-iterator │ │ │ ├── decoder.test.ts │ │ │ ├── decoder.ts │ │ │ ├── encoder.test.ts │ │ │ ├── encoder.ts │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ ├── meta.test.ts │ │ │ ├── meta.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ ├── types.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ └── tsconfig.json ├── svelte-query │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.tsx │ │ ├── procedure-utils.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.tsx │ │ ├── router-utils.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json ├── tanstack-query │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── key.test.ts │ │ ├── key.ts │ │ ├── procedure-utils.angular.test-d.ts │ │ ├── procedure-utils.react.test-d.ts │ │ ├── procedure-utils.solid.test-d.ts │ │ ├── procedure-utils.svelte.test-d.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── procedure-utils.vue.test-d.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json ├── valibot │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── converter.test.ts │ │ ├── converter.ts │ │ └── index.ts │ └── tsconfig.json ├── vue-colada │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── key.test.ts │ │ ├── key.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ └── types.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json ├── vue-query │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── general-utils.test-d.ts │ │ ├── general-utils.test.ts │ │ ├── general-utils.ts │ │ ├── index.ts │ │ ├── procedure-utils.test-d.ts │ │ ├── procedure-utils.test.ts │ │ ├── procedure-utils.ts │ │ ├── router-utils.test-d.ts │ │ ├── router-utils.test.ts │ │ ├── router-utils.ts │ │ ├── types.test-d.ts │ │ ├── types.ts │ │ ├── utils.test-d.ts │ │ ├── utils.test.ts │ │ └── utils.ts │ ├── tests │ │ ├── e2e.test-d.ts │ │ ├── e2e.test.tsx │ │ └── shared.tsx │ └── tsconfig.json └── zod │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ ├── coercer.test.ts │ ├── coercer.ts │ ├── converter.test.ts │ ├── converter.ts │ ├── custom-json-schema.test-d.ts │ ├── custom-json-schema.test.ts │ ├── custom-json-schema.ts │ ├── index.ts │ ├── schemas │ │ ├── base.ts │ │ ├── blob.test.ts │ │ ├── blob.ts │ │ ├── file.test.ts │ │ ├── file.ts │ │ ├── regexp.test.ts │ │ ├── regexp.ts │ │ ├── url.test.ts │ │ └── url.ts │ └── zod4 │ │ ├── coercer.combination.test.ts │ │ ├── coercer.native.test.ts │ │ ├── coercer.rest.test.ts │ │ ├── coercer.structure.test.ts │ │ ├── coercer.test.ts │ │ ├── coercer.ts │ │ ├── converter.combination.test.ts │ │ ├── converter.meta.test.ts │ │ ├── converter.native.test.ts │ │ ├── converter.number.test.ts │ │ ├── converter.processed.test.ts │ │ ├── converter.rest.test.ts │ │ ├── converter.string.test.ts │ │ ├── converter.structure.test.ts │ │ ├── converter.test.ts │ │ ├── converter.ts │ │ ├── index.ts │ │ └── registries.ts │ ├── tests │ └── shared.ts │ └── tsconfig.json ├── playgrounds ├── astro │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── astro.config.mjs │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── orpc-mutation.tsx │ │ │ └── orpc-query.tsx │ │ ├── lib │ │ │ └── orpc.ts │ │ ├── middlewares │ │ │ ├── auth.ts │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── pages │ │ │ ├── api │ │ │ │ └── [...rest].ts │ │ │ ├── index.astro │ │ │ └── rpc │ │ │ │ └── [...rest].ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── polyfill.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ │ └── shared │ │ │ └── query.ts │ └── tsconfig.json ├── browser-extension │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── entrypoints │ │ ├── background │ │ │ ├── index.ts │ │ │ ├── middlewares │ │ │ │ ├── auth.ts │ │ │ │ ├── db.ts │ │ │ │ └── retry.ts │ │ │ ├── orpc.ts │ │ │ ├── router │ │ │ │ ├── auth.ts │ │ │ │ ├── index.ts │ │ │ │ ├── planet.ts │ │ │ │ └── sse.ts │ │ │ └── schemas │ │ │ │ ├── auth.ts │ │ │ │ ├── planet.ts │ │ │ │ └── user.ts │ │ ├── content.ts │ │ └── popup │ │ │ ├── App.tsx │ │ │ ├── components │ │ │ ├── orpc-mutation.tsx │ │ │ └── orpc-query.tsx │ │ │ ├── index.html │ │ │ ├── lib │ │ │ └── orpc.ts │ │ │ ├── main.tsx │ │ │ ├── playground-client.ts │ │ │ └── playground-tanstac-query.ts │ ├── package.json │ ├── public │ │ ├── icon │ │ │ ├── 128.png │ │ │ ├── 16.png │ │ │ ├── 32.png │ │ │ ├── 48.png │ │ │ └── 96.png │ │ └── wxt.svg │ ├── tsconfig.json │ └── wxt.config.ts ├── contract-first │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── package.json │ ├── src │ │ ├── contract │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── lib │ │ │ └── orpc.ts │ │ ├── main.ts │ │ ├── middlewares │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── polyfill.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ └── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ └── tsconfig.json ├── electron │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── electron-builder.yml │ ├── electron.vite.config.ts │ ├── package.json │ ├── src │ │ ├── main │ │ │ ├── index.ts │ │ │ ├── middlewares │ │ │ │ ├── auth.ts │ │ │ │ ├── db.ts │ │ │ │ └── retry.ts │ │ │ ├── orpc.ts │ │ │ ├── router │ │ │ │ ├── auth.ts │ │ │ │ ├── index.ts │ │ │ │ ├── planet.ts │ │ │ │ └── sse.ts │ │ │ └── schemas │ │ │ │ ├── auth.ts │ │ │ │ ├── planet.ts │ │ │ │ └── user.ts │ │ ├── preload │ │ │ ├── index.d.ts │ │ │ └── index.ts │ │ └── renderer │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── App.tsx │ │ │ ├── components │ │ │ ├── orpc-mutation.tsx │ │ │ └── orpc-query.tsx │ │ │ ├── lib │ │ │ └── orpc.ts │ │ │ ├── main.tsx │ │ │ ├── playground-client.ts │ │ │ └── playground-tanstack-query.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── tsconfig.web.json ├── nest │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── nest-cli.json │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── auth │ │ │ └── auth.controller.ts │ │ ├── contract │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── lib │ │ │ └── orpc.ts │ │ ├── main.ts │ │ ├── other │ │ │ └── other.controller.ts │ │ ├── planet │ │ │ ├── planet.controller.ts │ │ │ └── planet.service.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── reference │ │ │ ├── reference.controller.ts │ │ │ └── reference.service.ts │ │ └── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── next │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── next.config.ts │ ├── package.json │ ├── public │ │ ├── file.svg │ │ ├── globe.svg │ │ ├── next.svg │ │ ├── vercel.svg │ │ └── window.svg │ ├── src │ │ ├── app │ │ │ ├── actions.ts │ │ │ ├── api │ │ │ │ └── [[...rest]] │ │ │ │ │ └── route.ts │ │ │ ├── error.tsx │ │ │ ├── favicon.ico │ │ │ ├── layout.tsx │ │ │ ├── loading.tsx │ │ │ ├── orpc-mutation.tsx │ │ │ ├── orpc-query.tsx │ │ │ ├── orpc-server-action.tsx │ │ │ ├── page.tsx │ │ │ ├── providers.tsx │ │ │ └── rpc │ │ │ │ └── [[...rest]] │ │ │ │ └── route.ts │ │ ├── lib │ │ │ ├── orpc.server.ts │ │ │ └── orpc.ts │ │ ├── middlewares │ │ │ ├── auth.ts │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── polyfill.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ └── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ └── tsconfig.json ├── nuxt │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── app.vue │ ├── components │ │ ├── orpc-mutation.vue │ │ └── orpc-query.vue │ ├── lib │ │ └── orpc.ts │ ├── nuxt.config.ts │ ├── package.json │ ├── plugins │ │ └── vue-query.ts │ ├── public │ │ ├── favicon.ico │ │ └── robots.txt │ ├── server │ │ ├── middlewares │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── plugins │ │ │ └── polyfills.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── routes │ │ │ ├── api │ │ │ │ ├── [...].ts │ │ │ │ └── index.ts │ │ │ └── rpc │ │ │ │ ├── [...].ts │ │ │ │ └── index.ts │ │ ├── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ │ └── tsconfig.json │ └── tsconfig.json ├── solid-start │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── app.config.ts │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── app.tsx │ │ ├── entry-client.tsx │ │ ├── entry-server.tsx │ │ ├── global.d.ts │ │ ├── lib │ │ │ ├── orpc.server.ts │ │ │ └── orpc.ts │ │ ├── middlewares │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── polyfill.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── routes │ │ │ ├── api │ │ │ │ ├── [...rest].ts │ │ │ │ └── index.ts │ │ │ ├── index.tsx │ │ │ ├── orpc-mutation.tsx │ │ │ ├── orpc-query.tsx │ │ │ └── rpc │ │ │ │ ├── [...rest].ts │ │ │ │ └── index.ts │ │ └── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ └── tsconfig.json ├── svelte-kit │ ├── .gitignore │ ├── .npmrc │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── package.json │ ├── src │ │ ├── app.d.ts │ │ ├── app.html │ │ ├── lib │ │ │ ├── index.ts │ │ │ └── orpc.ts │ │ ├── middlewares │ │ │ ├── db.ts │ │ │ └── retry.ts │ │ ├── orpc.ts │ │ ├── playground-client.ts │ │ ├── playground-tanstack-query.ts │ │ ├── polyfill.ts │ │ ├── router │ │ │ ├── auth.ts │ │ │ ├── index.ts │ │ │ ├── planet.ts │ │ │ └── sse.ts │ │ ├── routes │ │ │ ├── +page.svelte │ │ │ ├── api │ │ │ │ └── [...rest] │ │ │ │ │ └── +server.ts │ │ │ ├── orpc-mutation.svelte │ │ │ ├── orpc-query.svelte │ │ │ └── rpc │ │ │ │ └── [...rest] │ │ │ │ └── +server.ts │ │ └── schemas │ │ │ ├── auth.ts │ │ │ ├── planet.ts │ │ │ └── user.ts │ ├── static │ │ └── favicon.png │ ├── svelte.config.js │ ├── tsconfig.json │ └── vite.config.ts └── tanstack-start │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── README.md │ ├── app.config.ts │ ├── package.json │ ├── src │ ├── api.ts │ ├── client.tsx │ ├── components │ │ ├── orpc-mutation.tsx │ │ └── orpc-query.tsx │ ├── lib │ │ └── orpc.ts │ ├── middlewares │ │ ├── auth.ts │ │ ├── db.ts │ │ └── retry.ts │ ├── orpc.ts │ ├── playground-client.ts │ ├── playground-tanstack-query.ts │ ├── polyfill.ts │ ├── routeTree.gen.ts │ ├── router.tsx │ ├── router │ │ ├── auth.ts │ │ ├── index.ts │ │ ├── planet.ts │ │ └── sse.ts │ ├── routes │ │ ├── __root.tsx │ │ ├── api │ │ │ ├── $.ts │ │ │ ├── index.ts │ │ │ ├── rpc.$.ts │ │ │ └── rpc.ts │ │ └── index.tsx │ ├── schemas │ │ ├── auth.ts │ │ ├── planet.ts │ │ └── user.ts │ └── ssr.tsx │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.lib.json ├── vitest.jsdom.ts └── vitest.workspace.ts /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: npm 5 | directory: / 6 | schedule: 7 | interval: daily 8 | versioning-strategy: increase 9 | groups: 10 | dev-dependencies-minor-patch: 11 | dependency-type: development 12 | update-types: 13 | - minor 14 | - patch 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.npmrc 5 | !.*.example 6 | !.vscode/ 7 | !.github/ 8 | !.vitepress/ 9 | 10 | # Common generated folders 11 | logs/ 12 | node_modules/ 13 | out/ 14 | dist/ 15 | dist-ssr/ 16 | build/ 17 | coverage/ 18 | temp/ 19 | 20 | # Common generated files 21 | *.log 22 | *.log.* 23 | *.tsbuildinfo 24 | *.vitest-temp.json 25 | vite.config.ts.timestamp-* 26 | vitest.config.ts.timestamp-* 27 | 28 | # Common manual ignore files 29 | *.local 30 | *.pem -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | shamefully-hoist = true 3 | link-workspace-packages = true 4 | prefer-workspace-packages = true -------------------------------------------------------------------------------- /apps/content/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? -------------------------------------------------------------------------------- /apps/content/.vitepress/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | cache 3 | -------------------------------------------------------------------------------- /apps/content/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import type { EnhanceAppContext } from 'vitepress' 2 | import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client' 3 | import Theme from 'vitepress/theme' 4 | import { h } from 'vue' 5 | import AsideSponsors from './components/AsideSponsors.vue' 6 | import Banner from './components/Banner.vue' 7 | 8 | import 'virtual:group-icons.css' 9 | import '@shikijs/vitepress-twoslash/style.css' 10 | import './custom.css' 11 | 12 | export default { 13 | extends: Theme, 14 | enhanceApp({ app }: EnhanceAppContext) { 15 | app.use(TwoslashFloatingVue) 16 | }, 17 | Layout() { 18 | return h(Theme.Layout, null, { 19 | 'aside-outline-after': () => h(AsideSponsors), 20 | 'layout-top': () => h(Banner), 21 | }) 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /apps/content/docs/plugins/body-limit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Body Limit Plugin 3 | description: A plugin for oRPC to limit the request body size. 4 | --- 5 | 6 | # Body Limit Plugin 7 | 8 | The **Body Limit Plugin** restricts the size of the request body. 9 | 10 | ## Import 11 | 12 | Depending on your adapter, import the corresponding plugin: 13 | 14 | ```ts 15 | import { BodyLimitPlugin } from '@orpc/server/fetch' 16 | import { BodyLimitPlugin } from '@orpc/server/node' 17 | ``` 18 | 19 | ## Setup 20 | 21 | Configure the plugin with your desired maximum body size: 22 | 23 | ```ts 24 | const handler = new RPCHandler(router, { 25 | plugins: [ 26 | new BodyLimitPlugin({ 27 | maxBodySize: 1024 * 1024, // 1MB 28 | }), 29 | ], 30 | }) 31 | ``` 32 | -------------------------------------------------------------------------------- /apps/content/docs/plugins/cors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CORS Plugin 3 | description: CORS Plugin for oRPC 4 | --- 5 | 6 | # CORS Plugin 7 | 8 | `CORSPlugin` is a plugin for oRPC that allows you to configure CORS for your API. 9 | 10 | ## Basic 11 | 12 | ```ts 13 | import { CORSPlugin } from '@orpc/server/plugins' 14 | 15 | const handler = new RPCHandler(router, { 16 | plugins: [ 17 | new CORSPlugin({ 18 | origin: (origin, options) => origin, 19 | allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'], 20 | // ... 21 | }), 22 | ], 23 | }) 24 | ``` 25 | 26 | ::: info 27 | The `handler` can be any supported oRPC handler, such as [RPCHandler](/docs/rpc-handler), [OpenAPIHandler](/docs/openapi/openapi-handler), or another custom handler. 28 | ::: 29 | -------------------------------------------------------------------------------- /apps/content/public/_redirects: -------------------------------------------------------------------------------- 1 | /docs /docs/getting-started 301 2 | /docs/openapi /docs/openapi/getting-started 301 3 | /sponsor https://github.com/sponsors/unnoq 301 4 | /docs/integrations/nextjs /docs/integrations/next 301 -------------------------------------------------------------------------------- /apps/content/public/code-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/apps/content/public/code-dark.png -------------------------------------------------------------------------------- /apps/content/public/code-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/apps/content/public/code-light.png -------------------------------------------------------------------------------- /apps/content/public/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/content/public/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/apps/content/public/logo.webp -------------------------------------------------------------------------------- /apps/content/public/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/apps/content/public/og.jpg -------------------------------------------------------------------------------- /apps/content/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "types": ["node"] 5 | }, 6 | "exclude": [ 7 | "**/*.test.*", 8 | "**/*.test-d.ts", 9 | "**/__tests__/**", 10 | "**/__mocks__/**", 11 | "**/__snapshots__/**" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/content/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "cleanUrls": true, 3 | "headers": [ 4 | { 5 | "source": "/assets/(.*)", 6 | "headers": [ 7 | { 8 | "key": "Cache-Control", 9 | "value": "max-age=31536000, immutable" 10 | } 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /knip.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/knip@latest/schema.json", 3 | "ignore": ["*/*/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/arktype/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/arktype/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './converter' 2 | -------------------------------------------------------------------------------- /packages/arktype/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../openapi" }, 5 | { "path": "../contract" }, 6 | { "path": "../server" } 7 | ], 8 | "include": ["src"], 9 | "exclude": [ 10 | "**/*.test.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/client/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/client/src/adapters/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link-fetch-client' 2 | export * from './rpc-link' 3 | -------------------------------------------------------------------------------- /packages/client/src/adapters/message-port/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link-client' 2 | export * from './message-port' 3 | export * from './rpc-link' 4 | -------------------------------------------------------------------------------- /packages/client/src/adapters/standard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link' 2 | export * from './plugin' 3 | export * from './rpc-json-serializer' 4 | export * from './rpc-link' 5 | export * from './rpc-link-codec' 6 | export * from './rpc-serializer' 7 | export * from './types' 8 | export * from './utils' 9 | -------------------------------------------------------------------------------- /packages/client/src/adapters/standard/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { ClientContext } from '../../types' 2 | import type { StandardLinkOptions } from './link' 3 | 4 | export interface StandardLinkPlugin { 5 | order?: number 6 | init?(options: StandardLinkOptions): void 7 | } 8 | 9 | export class CompositeStandardLinkPlugin> implements StandardLinkPlugin { 10 | protected readonly plugins: TPlugin[] 11 | 12 | constructor(plugins: readonly TPlugin[] = []) { 13 | this.plugins = [...plugins].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) 14 | } 15 | 16 | init(options: StandardLinkOptions): void { 17 | for (const plugin of this.plugins) { 18 | plugin.init?.(options) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/client/src/adapters/standard/types.ts: -------------------------------------------------------------------------------- 1 | import type { StandardLazyResponse, StandardRequest } from '@orpc/standard-server' 2 | import type { ClientContext, ClientOptions } from '../../types' 3 | 4 | export interface StandardLinkCodec { 5 | encode(path: readonly string[], input: unknown, options: ClientOptions): Promise 6 | decode(response: StandardLazyResponse, options: ClientOptions, path: readonly string[], input: unknown): Promise 7 | } 8 | 9 | export interface StandardLinkClient { 10 | call(request: StandardRequest, options: ClientOptions, path: readonly string[], input: unknown): Promise 11 | } 12 | -------------------------------------------------------------------------------- /packages/client/src/adapters/standard/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { getMalformedResponseErrorCode, toHttpPath } from './utils' 2 | 3 | it('convertPathToHttpPath', () => { 4 | expect(toHttpPath(['ping'])).toEqual('/ping') 5 | expect(toHttpPath(['nested', 'ping'])).toEqual('/nested/ping') 6 | expect(toHttpPath(['nested/', 'ping'])).toEqual('/nested%2F/ping') 7 | }) 8 | 9 | it('getMalformedResponseErrorCode', () => { 10 | expect(getMalformedResponseErrorCode(400)).toEqual('BAD_REQUEST') 11 | expect(getMalformedResponseErrorCode(401)).toEqual('UNAUTHORIZED') 12 | expect(getMalformedResponseErrorCode(433)).toEqual('MALFORMED_ORPC_ERROR_RESPONSE') 13 | }) 14 | -------------------------------------------------------------------------------- /packages/client/src/adapters/standard/utils.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPPath } from '../../types' 2 | import { COMMON_ORPC_ERROR_DEFS } from '../../error' 3 | 4 | export function toHttpPath(path: readonly string[]): HTTPPath { 5 | return `/${path.map(encodeURIComponent).join('/')}` 6 | } 7 | 8 | export function getMalformedResponseErrorCode(status: number): string { 9 | return Object.entries(COMMON_ORPC_ERROR_DEFS).find(([, def]) => def.status === status)?.[0] ?? 'MALFORMED_ORPC_ERROR_RESPONSE' 10 | } 11 | -------------------------------------------------------------------------------- /packages/client/src/adapters/websocket/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link-websocket-client' 2 | export * from './rpc-link' 3 | -------------------------------------------------------------------------------- /packages/client/src/error.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { ORPCError } from './error' 2 | import { isDefinedError } from './error' 3 | 4 | it('isDefinedError', () => { 5 | const orpcError = {} as ORPCError<'CODE', { value: string }> | ORPCError<'BASE', { value: number }> 6 | const error = {} as Error | typeof orpcError 7 | 8 | if (isDefinedError(error)) { 9 | expectTypeOf(error).toEqualTypeOf(orpcError) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /packages/client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client' 2 | export * from './dynamic-link' 3 | export * from './error' 4 | export * from './event-iterator' 5 | export * from './types' 6 | export * from './utils' 7 | 8 | export { onError, onFinish, onStart, onSuccess } from '@orpc/shared' 9 | export type { Registry, ThrowableError } from '@orpc/shared' 10 | export { ErrorEvent } from '@orpc/standard-server' 11 | -------------------------------------------------------------------------------- /packages/client/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './batch' 2 | export * from './retry' 3 | export * from './simple-csrf-protection' 4 | -------------------------------------------------------------------------------- /packages/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../shared" }, 5 | { "path": "../standard-server" }, 6 | { "path": "../standard-server-fetch" }, 7 | { "path": "../standard-server-peer" } 8 | ], 9 | "include": ["src"], 10 | "exclude": [ 11 | "**/*.test.*", 12 | "**/*.bench.*", 13 | "**/*.test-d.ts", 14 | "**/__tests__/**", 15 | "**/__mocks__/**", 16 | "**/__snapshots__/**" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/contract/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/contract/src/config.test.ts: -------------------------------------------------------------------------------- 1 | import { fallbackContractConfig } from './config' 2 | 3 | it('fallbackConfig', () => { 4 | expect(fallbackContractConfig('defaultMethod', undefined)).toBe('POST') 5 | expect(fallbackContractConfig('defaultMethod', 'GET')).toBe('GET') 6 | }) 7 | -------------------------------------------------------------------------------- /packages/contract/src/error.test.ts: -------------------------------------------------------------------------------- 1 | import { baseErrorMap } from '../tests/shared' 2 | import { mergeErrorMap, ValidationError } from './error' 3 | 4 | it('validationError', () => { 5 | const error = new ValidationError({ message: 'message', issues: [{ message: 'message' }] }) 6 | expect(error).toBeInstanceOf(Error) 7 | expect(error.issues).toEqual([{ message: 'message' }]) 8 | }) 9 | 10 | it('mergeErrorMap', () => { 11 | expect(mergeErrorMap(baseErrorMap, baseErrorMap)).toEqual(baseErrorMap) 12 | expect(mergeErrorMap(baseErrorMap, { OVERRIDE: {}, INVALID: {} })).toEqual( 13 | { OVERRIDE: {}, INVALID: {}, BASE: baseErrorMap.BASE }, 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/contract/src/index.ts: -------------------------------------------------------------------------------- 1 | /** unnoq */ 2 | 3 | export * from './builder' 4 | export * from './builder-variants' 5 | export * from './config' 6 | export * from './error' 7 | export * from './event-iterator' 8 | export * from './link-utils' 9 | export * from './meta' 10 | export * from './procedure' 11 | export * from './procedure-client' 12 | export * from './route' 13 | export * from './router' 14 | export * from './router-client' 15 | export * from './router-utils' 16 | export * from './schema' 17 | export * from './schema-utils' 18 | export * from './types' 19 | 20 | export { ORPCError } from '@orpc/client' 21 | export type { HTTPMethod, HTTPPath } from '@orpc/client' 22 | export type { Registry, ThrowableError } from '@orpc/shared' 23 | -------------------------------------------------------------------------------- /packages/contract/src/link-utils.test-d.ts: -------------------------------------------------------------------------------- 1 | import { RPCLink } from '@orpc/client/fetch' 2 | import { router as contract } from '../tests/shared' 3 | import { inferRPCMethodFromContractRouter } from './link-utils' 4 | 5 | it('inferRPCMethodFromContractRouter', () => { 6 | const link = new RPCLink({ 7 | url: 'http://localhost:3000/rpc', 8 | method: inferRPCMethodFromContractRouter(contract), 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/contract/src/meta.test.ts: -------------------------------------------------------------------------------- 1 | import { mergeMeta } from './meta' 2 | 3 | it('mergeMeta', () => { 4 | expect(mergeMeta({}, { a: 2 })).toEqual({ a: 2 }) 5 | expect(mergeMeta({ a: 1, b: 1 }, { a: 2 })).toEqual({ a: 2, b: 1 }) 6 | }) 7 | -------------------------------------------------------------------------------- /packages/contract/src/meta.ts: -------------------------------------------------------------------------------- 1 | export type Meta = Record 2 | 3 | export function mergeMeta(meta1: T, meta2: T): T { 4 | return { ...meta1, ...meta2 } 5 | } 6 | -------------------------------------------------------------------------------- /packages/contract/src/procedure-client.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { Client, ORPCError } from '@orpc/client' 2 | import type { baseErrorMap, inputSchema, outputSchema } from '../tests/shared' 3 | import type { ContractProcedureClient } from './procedure-client' 4 | 5 | describe('ContractProcedureClient', () => { 6 | it('is a client', () => { 7 | expectTypeOf< 8 | ContractProcedureClient<{ cache?: boolean }, typeof inputSchema, typeof outputSchema, typeof baseErrorMap> 9 | >().toEqualTypeOf< 10 | Client< 11 | { cache?: boolean }, 12 | { input: number }, 13 | { output: string }, 14 | Error | ORPCError<'BASE', { output: string }> | ORPCError<'OVERRIDE', unknown> 15 | > 16 | >() 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/contract/src/procedure-client.ts: -------------------------------------------------------------------------------- 1 | import type { Client, ClientContext } from '@orpc/client' 2 | import type { ErrorFromErrorMap, ErrorMap } from './error' 3 | import type { AnySchema, InferSchemaInput, InferSchemaOutput } from './schema' 4 | 5 | export type ContractProcedureClient< 6 | TClientContext extends ClientContext, 7 | TInputSchema extends AnySchema, 8 | TOutputSchema extends AnySchema, 9 | TErrorMap extends ErrorMap, 10 | > = Client, InferSchemaOutput, ErrorFromErrorMap> 11 | -------------------------------------------------------------------------------- /packages/contract/src/router-client.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { ClientContext, NestedClient } from '@orpc/client' 2 | import type { ContractRouterClient } from './router-client' 3 | import { ping, pong } from '../tests/shared' 4 | 5 | const router = { 6 | ping, 7 | pong, 8 | nested: { 9 | ping, 10 | pong, 11 | }, 12 | } 13 | 14 | describe('ContractRouterClient', () => { 15 | it('is a NestedClient', () => { 16 | expectTypeOf>().toMatchTypeOf>() 17 | expectTypeOf>().not.toMatchTypeOf>() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/contract/src/router-client.ts: -------------------------------------------------------------------------------- 1 | import type { ClientContext } from '@orpc/client' 2 | import type { ContractProcedure } from './procedure' 3 | import type { ContractProcedureClient } from './procedure-client' 4 | import type { AnyContractRouter } from './router' 5 | 6 | export type ContractRouterClient> = 7 | TRouter extends ContractProcedure 8 | ? ContractProcedureClient 9 | : { 10 | [K in keyof TRouter]: TRouter[K] extends AnyContractRouter ? ContractRouterClient : never 11 | } 12 | -------------------------------------------------------------------------------- /packages/contract/src/schema-utils.ts: -------------------------------------------------------------------------------- 1 | import type { SchemaIssue } from './schema' 2 | import { isPropertyKey, isTypescriptObject } from '@orpc/shared' 3 | 4 | export function isSchemaIssue(issue: unknown): issue is SchemaIssue { 5 | if (!isTypescriptObject(issue) || typeof issue.message !== 'string') { 6 | return false 7 | } 8 | 9 | if (issue.path !== undefined) { 10 | if (!Array.isArray(issue.path)) { 11 | return false 12 | } 13 | 14 | if ( 15 | !issue.path.every(segment => isPropertyKey(segment) || (isTypescriptObject(segment) && isPropertyKey(segment.key))) 16 | ) { 17 | return false 18 | } 19 | } 20 | 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /packages/contract/src/schema.test.ts: -------------------------------------------------------------------------------- 1 | import { type } from './schema' 2 | 3 | describe('type', async () => { 4 | it('without map', async () => { 5 | const schema = type() 6 | const val = {} 7 | expect((await schema['~standard'].validate(val) as any).value).toBe(val) 8 | }) 9 | 10 | it('with map', async () => { 11 | const val = {} 12 | const check = vi.fn().mockReturnValueOnce('__mapped__') 13 | const schema = type(check) 14 | expect((await schema['~standard'].validate(val) as any).value).toBe('__mapped__') 15 | expect(check).toHaveBeenCalledWith(val) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/contract/src/types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-imports */ 2 | export type { OpenAPIV3_1 as OpenAPI } from 'openapi-types' 3 | -------------------------------------------------------------------------------- /packages/contract/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/__tests__/**", 12 | "**/__mocks__/**", 13 | "**/__snapshots__/**" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/hey-api/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/hey-api/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './to-orpc-client' 2 | -------------------------------------------------------------------------------- /packages/hey-api/tests/client/index.ts: -------------------------------------------------------------------------------- 1 | // This file is auto-generated by @hey-api/openapi-ts 2 | export * from './types.gen'; 3 | export * from './sdk.gen'; -------------------------------------------------------------------------------- /packages/hey-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/*.bench.*", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/nest/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/nest/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './implement' 2 | export { Implement as Impl } from './implement' 3 | export * from './utils' 4 | 5 | export { implement, ORPCError } from '@orpc/server' 6 | export type { 7 | ImplementedProcedure, 8 | Implementer, 9 | ImplementerInternal, 10 | ImplementerInternalWithMiddlewares, 11 | ProcedureImplementer, 12 | RouterImplementer, 13 | RouterImplementerWithMiddlewares, 14 | } from '@orpc/server' 15 | -------------------------------------------------------------------------------- /packages/nest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "experimentalDecorators": true 5 | }, 6 | "references": [ 7 | { "path": "../client" }, 8 | { "path": "../contract" }, 9 | { "path": "../openapi" }, 10 | { "path": "../openapi-client" }, 11 | { "path": "../server" }, 12 | { "path": "../shared" }, 13 | { "path": "../standard-server" }, 14 | { "path": "../standard-server-node" } 15 | ], 16 | "include": ["src"], 17 | "exclude": [ 18 | "**/*.test.*", 19 | "**/*.test-d.ts", 20 | "**/__tests__/**", 21 | "**/__mocks__/**", 22 | "**/__snapshots__/**" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/nest/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "../../tsconfig.base.json", 4 | "compilerOptions": { 5 | "experimentalDecorators": true, 6 | "types": ["node", "vitest/globals"] 7 | }, 8 | "references": [ 9 | { "path": "./tsconfig.json" } 10 | ], 11 | "include": [ 12 | "tests", 13 | "src/**/*.test.*", 14 | "src/**/*.test-d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/openapi-client/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/openapi-client/src/adapters/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-link' 2 | -------------------------------------------------------------------------------- /packages/openapi-client/src/adapters/standard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bracket-notation' 2 | export * from './bracket-notation-utils' 3 | export * from './openapi-json-serializer' 4 | export * from './openapi-link' 5 | export * from './openapi-link-codec' 6 | export * from './openapi-serializer' 7 | export * from './utils' 8 | -------------------------------------------------------------------------------- /packages/openapi-client/src/adapters/standard/utils.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPPath } from '@orpc/client' 2 | 3 | /** 4 | * @internal 5 | */ 6 | export function standardizeHTTPPath(path: HTTPPath): HTTPPath { 7 | return `/${path.replace(/\/{2,}/g, '/').replace(/^\/|\/$/g, '')}` 8 | } 9 | 10 | /** 11 | * @internal 12 | */ 13 | export function getDynamicParams(path: HTTPPath | undefined): { raw: string, name: string }[] | undefined { 14 | return path 15 | ? standardizeHTTPPath(path).match(/\/\{[^}]+\}/g)?.map(v => ({ 16 | raw: v, 17 | name: v.match(/\{\+?([^}]+)\}/)![1]!, 18 | })) 19 | : undefined 20 | } 21 | -------------------------------------------------------------------------------- /packages/openapi-client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types' 2 | -------------------------------------------------------------------------------- /packages/openapi-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../contract" }, 6 | { "path": "../shared" }, 7 | { "path": "../standard-server" } 8 | ], 9 | "include": ["src"], 10 | "exclude": [ 11 | "**/*.test.*", 12 | "**/*.bench.*", 13 | "**/*.test-d.ts", 14 | "**/__tests__/**", 15 | "**/__mocks__/**", 16 | "**/__snapshots__/**" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/openapi/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/openapi/src/adapters/aws-lambda/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-handler' 2 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-handler' 2 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/fetch/openapi-handler.test.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { OpenAPIHandler } from './openapi-handler' 3 | 4 | describe('openAPIHandler', () => { 5 | it('works', async () => { 6 | const handler = new OpenAPIHandler(os.route({ method: 'GET', path: '/ping' }).handler(({ input }) => ({ output: input }))) 7 | 8 | const { response } = await handler.handle(new Request('https://example.com/api/v1/ping?input=hello'), { 9 | prefix: '/api/v1', 10 | }) 11 | 12 | await expect(response?.text()).resolves.toContain('hello') 13 | expect(response?.status).toBe(200) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/node/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-handler' 2 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/node/openapi-handler.test.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage, ServerResponse } from 'node:http' 2 | import { os } from '@orpc/server' 3 | import request from 'supertest' 4 | import { OpenAPIHandler } from './openapi-handler' 5 | 6 | describe('openAPIHandler', () => { 7 | it('works', async () => { 8 | const handler = new OpenAPIHandler(os.route({ method: 'GET', path: '/ping' }).handler(({ input }) => ({ output: input }))) 9 | 10 | const res = await request(async (req: IncomingMessage, res: ServerResponse) => { 11 | await handler.handle(req, res, { prefix: '/prefix' }) 12 | }).get('/prefix/ping?input=hello') 13 | 14 | expect(res.text).toContain('hello') 15 | expect(res.status).toBe(200) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/standard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-codec' 2 | export * from './openapi-handler' 3 | export * from './openapi-matcher' 4 | export * from './utils' 5 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/standard/openapi-handler.test.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { StandardOpenAPIHandler } from './openapi-handler' 3 | 4 | describe('standardOpenAPIHandler', () => { 5 | const handler = new StandardOpenAPIHandler(os.route({ method: 'GET', path: '/ping' }).handler(({ input }) => ({ output: input })), { 6 | 7 | }) 8 | 9 | it('works', async () => { 10 | const { response } = await handler.handle({ 11 | url: new URL('https://example.com/api/v1/ping?input=hello'), 12 | body: () => Promise.resolve(undefined), 13 | headers: {}, 14 | method: 'GET', 15 | signal: undefined, 16 | }, { 17 | prefix: '/api/v1', 18 | context: {}, 19 | }) 20 | 21 | expect(response!.body).toEqual({ output: { input: 'hello' } }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/standard/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { decodeParams, toRou3Pattern } from './utils' 2 | 3 | it('toRou3Pattern', () => { 4 | expect(toRou3Pattern('/api/v1/users/{id}')).toBe('/api/v1/users/:id') 5 | expect(toRou3Pattern('/api/v1/users/{+id}')).toBe('/api/v1/users/**:id') 6 | expect(toRou3Pattern('/api/v1/users/name')).toBe('/api/v1/users/name') 7 | expect(toRou3Pattern('/api/v1/users/name{id}')).toBe('/api/v1/users/name{id}') 8 | }) 9 | 10 | it('decodeParams', () => { 11 | expect(decodeParams({ id: '1' })).toEqual({ id: '1' }) 12 | expect(decodeParams({ id: '1%2B1' })).toEqual({ id: '1+1' }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/openapi/src/adapters/standard/utils.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPPath } from '@orpc/client' 2 | import { standardizeHTTPPath } from '@orpc/openapi-client/standard' 3 | 4 | /** 5 | * {@link https://github.com/unjs/rou3} 6 | * 7 | * @internal 8 | */ 9 | export function toRou3Pattern(path: HTTPPath): string { 10 | return standardizeHTTPPath(path).replace(/\/\{\+([^}]+)\}/g, '/**:$1').replace(/\/\{([^}]+)\}/g, '/:$1') 11 | } 12 | 13 | /** 14 | * @internal 15 | */ 16 | export function decodeParams(params: Record): Record { 17 | return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, decodeURIComponent(value)])) 18 | } 19 | -------------------------------------------------------------------------------- /packages/openapi/src/index.ts: -------------------------------------------------------------------------------- 1 | import { customOpenAPIOperation } from './openapi-custom' 2 | 3 | export * from './openapi-custom' 4 | export * from './openapi-generator' 5 | export * from './openapi-utils' 6 | export * from './router-client' 7 | export * from './schema' 8 | export * from './schema-converter' 9 | export * from './schema-utils' 10 | 11 | export type { OpenAPI } from '@orpc/contract' 12 | 13 | export const oo = { 14 | spec: customOpenAPIOperation, 15 | } 16 | -------------------------------------------------------------------------------- /packages/openapi/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './openapi-reference' 2 | -------------------------------------------------------------------------------- /packages/openapi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../openapi-client" }, 6 | { "path": "../contract" }, 7 | { "path": "../server" }, 8 | { "path": "../standard-server" }, 9 | { "path": "../shared" } 10 | ], 11 | "include": ["src"], 12 | "exclude": [ 13 | "**/*.bench.*", 14 | "**/*.test.*", 15 | "**/*.test-d.ts", 16 | "**/__tests__/**", 17 | "**/__mocks__/**", 18 | "**/__snapshots__/**" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/react-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/react-query/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as TanstackQueryModule from '@orpc/tanstack-query' 2 | import { createGeneralUtils } from './general-utils' 3 | 4 | const generateOperationKeyOptions = vi.spyOn(TanstackQueryModule, 'generateOperationKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect(utils.key({ input: { search: '__search__' }, type: 'infinite' })).toEqual([['path'], { input: { search: '__search__' }, type: 'infinite' }]) 15 | expect(generateOperationKeyOptions).toHaveBeenCalledTimes(1) 16 | expect(generateOperationKeyOptions).toHaveBeenCalledWith(['path'], { input: { search: '__search__' }, type: 'infinite' }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/react-query/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouterUtils } from './router-utils' 2 | 3 | export * from './general-utils' 4 | export * from './procedure-utils' 5 | export * from './router-utils' 6 | export * from './types' 7 | 8 | export { 9 | createRouterUtils as createORPCReactQueryUtils, 10 | } 11 | 12 | export type { OperationKeyOptions as BuildKeyOptions, OperationType as KeyType } from '@orpc/tanstack-query' 13 | export { generateOperationKey as buildKey } from '@orpc/tanstack-query' 14 | -------------------------------------------------------------------------------- /packages/react-query/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query' 2 | import { orpc as client, streamedOrpc as streamedClient } from '../../client/tests/shared' 3 | import { createORPCReactQueryUtils } from '../src' 4 | 5 | export const orpc = createORPCReactQueryUtils(client) 6 | export const streamedOrpc = createORPCReactQueryUtils(streamedClient) 7 | 8 | export const queryClient = new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | retry: false, 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/react-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../tanstack-query" }, 6 | { "path": "../shared" } 7 | ], 8 | "include": ["src"], 9 | "exclude": [ 10 | "**/*.test.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/react/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './action-hooks' 2 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './action-form' 2 | export { getIssueMessage, parseFormData } from '@orpc/openapi-client/standard' 3 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../contract" }, 6 | { "path": "../openapi-client" }, 7 | { "path": "../server" }, 8 | { "path": "../shared" } 9 | ], 10 | "include": ["src"], 11 | "exclude": [ 12 | "**/*.test.*", 13 | "**/*.test-d.ts", 14 | "**/*.bench.*", 15 | "**/__tests__/**", 16 | "**/__mocks__/**", 17 | "**/__snapshots__/**" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/server/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/server/src/adapters/aws-lambda/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/bun-ws/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/bun-ws/rpc-handler.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { Router } from '../../router' 3 | import type { StandardRPCHandlerOptions } from '../standard' 4 | import { StandardRPCHandler } from '../standard' 5 | import { experimental_BunWsHandler as BunWsHandler } from './handler' 6 | 7 | /** 8 | * RPC Handler for Bun WS adapter 9 | * 10 | * @see {@link https://orpc.unnoq.com/docs/rpc-handler RPC Handler Docs} 11 | * @see {@link https://orpc.unnoq.com/docs/adapters/websocket Websocket Adapter Docs} 12 | */ 13 | export class experimental_RPCHandler extends BunWsHandler { 14 | constructor(router: Router, options: NoInfer> = {}) { 15 | super(new StandardRPCHandler(router, options)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/crossws/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/crossws/rpc-handler.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { Router } from '../../router' 3 | import type { StandardRPCHandlerOptions } from '../standard' 4 | import { StandardRPCHandler } from '../standard' 5 | import { experimental_CrosswsHandler as CrosswsHandler } from './handler' 6 | 7 | /** 8 | * RPC Handler for Crossws adapter 9 | * 10 | * @see {@link https://orpc.unnoq.com/docs/rpc-handler RPC Handler Docs} 11 | * @see {@link https://orpc.unnoq.com/docs/adapters/websocket Websocket Adapter Docs} 12 | */ 13 | export class experimental_RPCHandler extends CrosswsHandler { 14 | constructor(router: Router, options: NoInfer> = {}) { 15 | super(new StandardRPCHandler(router, options)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/fetch/handler.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { FetchHandler } from './handler' 2 | 3 | describe('FetchHandler', () => { 4 | it('optional context when all context is optional', () => { 5 | const handler = {} as FetchHandler<{ auth?: boolean }> 6 | 7 | handler.handle(new Request('https://example.com')) 8 | handler.handle(new Request('https://example.com'), { context: { auth: true } }) 9 | 10 | const handler2 = {} as FetchHandler<{ auth: boolean }> 11 | 12 | handler2.handle(new Request('https://example.com'), { context: { auth: true } }) 13 | // @ts-expect-error -- context is required 14 | handler2.handle(new Request('https://example.com')) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/server/src/adapters/fetch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body-limit-plugin' 2 | export * from './handler' 3 | export * from './plugin' 4 | export * from './rpc-handler' 5 | -------------------------------------------------------------------------------- /packages/server/src/adapters/fetch/plugin.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { StandardHandlerPlugin } from '../standard' 2 | import type { FetchHandlerPlugin } from './plugin' 3 | 4 | describe('FetchHandlerPlugin', () => { 5 | it('backward compatibility', () => { 6 | expectTypeOf>().toMatchTypeOf>() 7 | expectTypeOf>().toMatchTypeOf>() 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/server/src/adapters/fetch/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { StandardHandlerPlugin } from '../standard' 3 | import type { FetchHandlerOptions } from './handler' 4 | import { CompositeStandardHandlerPlugin } from '../standard' 5 | 6 | export interface FetchHandlerPlugin extends StandardHandlerPlugin { 7 | initRuntimeAdapter?(options: FetchHandlerOptions): void 8 | } 9 | 10 | export class CompositeFetchHandlerPlugin> 11 | extends CompositeStandardHandlerPlugin implements FetchHandlerPlugin { 12 | initRuntimeAdapter(options: FetchHandlerOptions): void { 13 | for (const plugin of this.plugins) { 14 | plugin.initRuntimeAdapter?.(options) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/message-port/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/node/handler.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage, ServerResponse } from 'node:http' 2 | import type { NodeHttpHandler } from './handler' 3 | 4 | describe('NodeHttpHandler', () => { 5 | it('optional context when all context is optional', () => { 6 | const handler = {} as NodeHttpHandler<{ auth?: boolean }> 7 | 8 | handler.handle({} as IncomingMessage, {} as ServerResponse) 9 | handler.handle({} as IncomingMessage, {} as ServerResponse, { context: { auth: true } }) 10 | 11 | const handler2 = {} as NodeHttpHandler<{ auth: boolean }> 12 | 13 | handler2.handle({} as IncomingMessage, {} as ServerResponse, { context: { auth: true } }) 14 | // @ts-expect-error -- context is required 15 | handler2.handle({} as IncomingMessage, {} as ServerResponse) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/node/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body-limit-plugin' 2 | export * from './handler' 3 | export * from './plugin' 4 | export * from './rpc-handler' 5 | -------------------------------------------------------------------------------- /packages/server/src/adapters/node/plugin.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { StandardHandlerPlugin } from '../standard' 2 | import type { NodeHttpHandlerPlugin } from './plugin' 3 | 4 | describe('NodeHttpHandlerPlugin', () => { 5 | it('backward compatibility', () => { 6 | expectTypeOf>().toMatchTypeOf>() 7 | expectTypeOf>().toMatchTypeOf>() 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/server/src/adapters/node/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { StandardHandlerPlugin } from '../standard' 3 | import type { NodeHttpHandlerOptions } from './handler' 4 | import { CompositeStandardHandlerPlugin } from '../standard' 5 | 6 | export interface NodeHttpHandlerPlugin extends StandardHandlerPlugin { 7 | initRuntimeAdapter?(options: NodeHttpHandlerOptions): void 8 | } 9 | 10 | export class CompositeNodeHttpHandlerPlugin> 11 | extends CompositeStandardHandlerPlugin implements NodeHttpHandlerPlugin { 12 | initRuntimeAdapter(options: NodeHttpHandlerOptions): void { 13 | for (const plugin of this.plugins) { 14 | plugin.initRuntimeAdapter?.(options) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/standard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './plugin' 3 | export * from './rpc-codec' 4 | export * from './rpc-handler' 5 | export * from './rpc-matcher' 6 | export * from './types' 7 | -------------------------------------------------------------------------------- /packages/server/src/adapters/standard/rpc-handler.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { os } from '../../builder' 3 | import { StandardRPCHandler } from './rpc-handler' 4 | 5 | describe('standardRPCHandler', () => { 6 | const handler = new StandardRPCHandler({ 7 | ping: os.handler(({ input }) => ({ output: input })), 8 | }, {}) 9 | 10 | it('works', async () => { 11 | const { response } = await handler.handle({ 12 | url: new URL('https://example.com/api/v1/ping'), 13 | body: () => Promise.resolve({ 14 | json: 'value', 15 | }), 16 | headers: {}, 17 | method: 'POST', 18 | signal: undefined, 19 | }, { 20 | prefix: '/api/v1', 21 | context: {}, 22 | }) 23 | 24 | expect(response?.body).toEqual({ json: { output: 'value' } }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/server/src/adapters/standard/utils.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { FriendlyStandardHandleOptions } from './utils' 2 | 3 | it('FriendlyStandardHandleOptions', () => { 4 | const _1: FriendlyStandardHandleOptions<{ a: string }> = { context: { a: '1' } } 5 | // @ts-expect-error - context is required 6 | const _2: FriendlyStandardHandleOptions<{ a: string }> = { } 7 | const _3: FriendlyStandardHandleOptions<{ a?: string }> = { context: { a: '1' } } 8 | const _4: FriendlyStandardHandleOptions<{ a?: string }> = { } 9 | // @ts-expect-error - context is invalid 10 | const _5: FriendlyStandardHandleOptions<{ a?: string }> = { context: { a: 1 } } 11 | }) 12 | -------------------------------------------------------------------------------- /packages/server/src/adapters/standard/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { resolveFriendlyStandardHandleOptions } from './utils' 2 | 3 | it('resolveFriendlyStandardHandleOptions', () => { 4 | expect(resolveFriendlyStandardHandleOptions({})).toEqual({ context: {} }) 5 | expect(resolveFriendlyStandardHandleOptions({ context: { a: 1 } })).toEqual({ context: { a: 1 } }) 6 | expect(resolveFriendlyStandardHandleOptions({ prefix: '/api/v1', context: { a: 1 } })).toEqual({ prefix: '/api/v1', context: { a: 1 } }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/server/src/adapters/standard/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { StandardHandleOptions } from './handler' 3 | 4 | export type FriendlyStandardHandleOptions = 5 | & Omit, 'context'> 6 | & (Record extends T ? { context?: T } : { context: T }) 7 | 8 | export function resolveFriendlyStandardHandleOptions(options: FriendlyStandardHandleOptions): StandardHandleOptions { 9 | return { 10 | ...options, 11 | context: options?.context ?? {} as T, // Context only optional if all fields are optional 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/adapters/websocket/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/websocket/rpc-handler.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { Router } from '../../router' 3 | import type { StandardRPCHandlerOptions } from '../standard' 4 | import { StandardRPCHandler } from '../standard' 5 | import { experimental_WebsocketHandler as WebsocketHandler } from './handler' 6 | 7 | /** 8 | * RPC Handler for Websocket adapter 9 | * 10 | * @see {@link https://orpc.unnoq.com/docs/rpc-handler RPC Handler Docs} 11 | * @see {@link https://orpc.unnoq.com/docs/adapters/websocket Websocket Adapter Docs} 12 | */ 13 | export class experimental_RPCHandler extends WebsocketHandler { 14 | constructor(router: Router, options: NoInfer> = {}) { 15 | super(new StandardRPCHandler(router, options)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/adapters/ws/index.ts: -------------------------------------------------------------------------------- 1 | export * from './handler' 2 | export * from './rpc-handler' 3 | -------------------------------------------------------------------------------- /packages/server/src/adapters/ws/rpc-handler.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '../../context' 2 | import type { Router } from '../../router' 3 | import type { StandardRPCHandlerOptions } from '../standard' 4 | import { StandardRPCHandler } from '../standard' 5 | import { experimental_WsHandler as WsHandler } from './handler' 6 | 7 | /** 8 | * RPC Handler for ws (node ws) adapter 9 | * 10 | * @see {@link https://orpc.unnoq.com/docs/rpc-handler RPC Handler Docs} 11 | * @see {@link https://orpc.unnoq.com/docs/adapters/websocket Websocket Adapter Docs} 12 | */ 13 | export class experimental_RPCHandler extends WsHandler { 14 | constructor(router: Router, options: NoInfer> = {}) { 15 | super(new StandardRPCHandler(router, options)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/config.test.ts: -------------------------------------------------------------------------------- 1 | import { fallbackConfig } from './config' 2 | 3 | it('fallbackConfig', () => { 4 | expect(fallbackConfig('initialInputValidationIndex', 1)).toBe(1) 5 | expect(fallbackConfig('initialInputValidationIndex', undefined)).toBe(0) 6 | }) 7 | -------------------------------------------------------------------------------- /packages/server/src/config.ts: -------------------------------------------------------------------------------- 1 | export interface Config { 2 | initialInputValidationIndex: number 3 | initialOutputValidationIndex: number 4 | dedupeLeadingMiddlewares: boolean 5 | } 6 | 7 | const DEFAULT_CONFIG: Config = { 8 | initialInputValidationIndex: 0, 9 | initialOutputValidationIndex: 0, 10 | dedupeLeadingMiddlewares: true, 11 | } 12 | 13 | export function fallbackConfig(key: T, value?: Config[T]): Config[T] { 14 | if (value === undefined) { 15 | return DEFAULT_CONFIG[key] 16 | } 17 | 18 | return value 19 | } 20 | -------------------------------------------------------------------------------- /packages/server/src/context.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { MergedCurrentContext, MergedInitialContext } from './context' 2 | 3 | it('MergedInitialContext', () => { 4 | expectTypeOf>().toMatchTypeOf<{ a: string, b: number }>() 5 | expectTypeOf>().toMatchTypeOf<{ a: never }>() 6 | expectTypeOf>().toMatchTypeOf<{ a: string }>() 7 | }) 8 | 9 | it('MergedCurrentContext', () => { 10 | expectTypeOf>().toMatchTypeOf<{ a: string, b: number }>() 11 | expectTypeOf>().toMatchTypeOf<{ a: number }>() 12 | }) 13 | -------------------------------------------------------------------------------- /packages/server/src/context.test.ts: -------------------------------------------------------------------------------- 1 | import { mergeCurrentContext } from './context' 2 | 3 | it('mergeCurrentContext', () => { 4 | expect(mergeCurrentContext({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 }) 5 | expect(mergeCurrentContext({ a: 1 }, { a: 2 })).toEqual({ a: 2 }) 6 | }) 7 | -------------------------------------------------------------------------------- /packages/server/src/context.ts: -------------------------------------------------------------------------------- 1 | export type Context = Record 2 | 3 | export type MergedInitialContext< 4 | TInitial extends Context, 5 | TAdditional extends Context, 6 | TCurrent extends Context, 7 | > = TInitial & Omit 8 | 9 | export type MergedCurrentContext = Omit & U 10 | 11 | export function mergeCurrentContext( 12 | context: T, 13 | other: U, 14 | ): MergedCurrentContext { 15 | return { ...context, ...other } 16 | } 17 | -------------------------------------------------------------------------------- /packages/server/src/error.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { ORPCError } from '@orpc/client' 2 | import type { baseErrorMap } from '../../contract/tests/shared' 3 | import type { ORPCErrorConstructorMap } from './error' 4 | 5 | it('ORPCErrorConstructorMap', () => { 6 | const constructors = {} as ORPCErrorConstructorMap 7 | 8 | const error = constructors.BASE({ data: { output: 123 } }) 9 | expectTypeOf(error).toEqualTypeOf>() 10 | 11 | // @ts-expect-error - invalid data 12 | constructors.BASE({ data: { output: '123' } }) 13 | // @ts-expect-error - missing data 14 | constructors.BASE() 15 | // can call without data if it is optional 16 | constructors.OVERRIDE() 17 | }) 18 | -------------------------------------------------------------------------------- /packages/server/src/lazy.test.ts: -------------------------------------------------------------------------------- 1 | import { ping } from '../tests/shared' 2 | import { getLazyMeta, isLazy, lazy, unlazy } from './lazy' 3 | 4 | it('lazy & isLazy & getLazyMeta & unlazy ', () => { 5 | const lazied = lazy(() => Promise.resolve({ default: ping }), { prefix: '/adapt' }) 6 | expect(lazied).toSatisfy(isLazy) 7 | expect(unlazy(lazied)).resolves.toEqual({ default: ping }) 8 | expect(getLazyMeta(lazied)).toEqual({ prefix: '/adapt' }) 9 | 10 | expect({}).not.toSatisfy(isLazy) 11 | expect(true).not.toSatisfy(isLazy) 12 | expect(unlazy(123)).resolves.toEqual({ default: 123 }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/server/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './batch' 2 | export * from './cors' 3 | export * from './response-headers' 4 | export * from './simple-csrf-protection' 5 | export * from './strict-get-method' 6 | -------------------------------------------------------------------------------- /packages/server/src/procedure.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { ContractProcedure } from '@orpc/contract' 2 | import type { baseErrorMap, BaseMeta, inputSchema, outputSchema } from '../../contract/tests/shared' 3 | import type { CurrentContext, InitialContext } from '../tests/shared' 4 | import type { Procedure } from './procedure' 5 | 6 | const procedure = {} as Procedure< 7 | InitialContext, 8 | CurrentContext, 9 | typeof inputSchema, 10 | typeof outputSchema, 11 | typeof baseErrorMap, 12 | BaseMeta 13 | > 14 | 15 | describe('Procedure', () => { 16 | it('is a contract procedure', () => { 17 | expectTypeOf(procedure).toMatchTypeOf< 18 | ContractProcedure< 19 | typeof inputSchema, 20 | typeof outputSchema, 21 | typeof baseErrorMap, 22 | BaseMeta 23 | > 24 | >() 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/server/src/procedure.test.ts: -------------------------------------------------------------------------------- 1 | import { isContractProcedure } from '@orpc/contract' 2 | import { ping } from '../tests/shared' 3 | import { isProcedure } from './procedure' 4 | 5 | describe('procedure', () => { 6 | it('also a contract procedure', () => { 7 | expect(ping).toSatisfy(isContractProcedure) 8 | }) 9 | }) 10 | 11 | it('isProcedure', () => { 12 | expect(ping).toSatisfy(isProcedure) 13 | expect(Object.assign({}, ping)).toSatisfy(isProcedure) 14 | 15 | expect({}).not.toSatisfy(isProcedure) 16 | expect(true).not.toSatisfy(isProcedure) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/server/src/router-hidden.test.ts: -------------------------------------------------------------------------------- 1 | import { contract, router } from '../tests/shared' 2 | import { getHiddenRouterContract, setHiddenRouterContract } from './router-hidden' 3 | 4 | it('setHiddenRouterContract & getHiddenRouterContract', () => { 5 | const applied = setHiddenRouterContract(router, contract) 6 | 7 | expect(applied).toEqual(router) 8 | expect(getHiddenRouterContract(applied)).toEqual(contract) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/server/src/router-hidden.ts: -------------------------------------------------------------------------------- 1 | import type { AnyContractRouter } from '@orpc/contract' 2 | import type { Lazyable } from './lazy' 3 | import type { AnyRouter } from './router' 4 | 5 | const HIDDEN_ROUTER_CONTRACT_SYMBOL = Symbol('ORPC_HIDDEN_ROUTER_CONTRACT') 6 | 7 | export function setHiddenRouterContract>(router: T, contract: AnyContractRouter): T { 8 | return new Proxy(router, { 9 | get(target, key) { 10 | if (key === HIDDEN_ROUTER_CONTRACT_SYMBOL) { 11 | return contract 12 | } 13 | 14 | return Reflect.get(target, key) 15 | }, 16 | }) 17 | } 18 | 19 | export function getHiddenRouterContract(router: Lazyable): AnyContractRouter | undefined { 20 | return (router as any)[HIDDEN_ROUTER_CONTRACT_SYMBOL] 21 | } 22 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../contract" }, 6 | { "path": "../standard-server" }, 7 | { "path": "../standard-server-fetch" }, 8 | { "path": "../standard-server-node" }, 9 | { "path": "../standard-server-aws-lambda" }, 10 | { "path": "../standard-server-peer" }, 11 | { "path": "../shared" } 12 | ], 13 | "include": ["src"], 14 | "exclude": [ 15 | "**/*.test.*", 16 | "**/*.bench.*", 17 | "**/*.test-d.ts", 18 | "**/__tests__/**", 19 | "**/__mocks__/**", 20 | "**/__snapshots__/**" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/shared/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/shared/src/args.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { MaybeOptionalOptions } from './args' 2 | 3 | it('MaybeOptionalOptions', () => { 4 | const a = (...[options]: MaybeOptionalOptions<{ a: number }>) => { 5 | expectTypeOf(options).toEqualTypeOf<{ a: number }>() 6 | } 7 | 8 | // @ts-expect-error - options is required 9 | a() 10 | // @ts-expect-error - options is invalid 11 | a({ a: '1' }) 12 | a({ a: 1 }) 13 | 14 | const b = (...[options]: MaybeOptionalOptions<{ b?: number }>) => { 15 | expectTypeOf(options).toEqualTypeOf<{ b?: number } | undefined>() 16 | } 17 | 18 | b() 19 | // @ts-expect-error - options is invalid 20 | b({ b: '1' }) 21 | b({ b: 1 }) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/shared/src/args.test.ts: -------------------------------------------------------------------------------- 1 | import { resolveMaybeOptionalOptions } from './args' 2 | 3 | it('resolveMaybeOptionalOptions', () => { 4 | expect(resolveMaybeOptionalOptions([{ a: 1 }])).toEqual({ a: 1 }) 5 | expect(resolveMaybeOptionalOptions([undefined])).toEqual({}) 6 | expect(resolveMaybeOptionalOptions([])).toEqual({}) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/shared/src/args.ts: -------------------------------------------------------------------------------- 1 | export type MaybeOptionalOptions = Record extends TOptions 2 | ? [options?: TOptions] 3 | : [options: TOptions] 4 | 5 | export function resolveMaybeOptionalOptions(rest: MaybeOptionalOptions): T { 6 | return rest[0] ?? {} as T // 0 only undefined when all fields are optional 7 | } 8 | -------------------------------------------------------------------------------- /packages/shared/src/array.test.ts: -------------------------------------------------------------------------------- 1 | import { splitInHalf, toArray } from './array' 2 | 3 | it('toArray', () => { 4 | expect(toArray(undefined)).toEqual([]) 5 | expect(toArray(null)).toEqual([]) 6 | expect(toArray(1)).toEqual([1]) 7 | expect(toArray([1])).toEqual([1]) 8 | }) 9 | 10 | it('splitInHalf', () => { 11 | expect(splitInHalf([1, 2, 3, 4, 5])).toEqual([[1, 2, 3], [4, 5]]) 12 | expect(splitInHalf([1, 2, 3, 4])).toEqual([[1, 2], [3, 4]]) 13 | expect(splitInHalf([1, 2, 3])).toEqual([[1, 2], [3]]) 14 | expect(splitInHalf([1, 2])).toEqual([[1], [2]]) 15 | expect(splitInHalf([1])).toEqual([[1], []]) 16 | expect(splitInHalf([])).toEqual([[], []]) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/shared/src/array.ts: -------------------------------------------------------------------------------- 1 | export function toArray(value: T): T extends readonly any[] ? T : Exclude[] { 2 | return (Array.isArray(value) ? value : value === undefined || value === null ? [] : [value]) as any 3 | } 4 | 5 | export function splitInHalf(arr: readonly T[]): [T[], T[]] { 6 | const half = Math.ceil(arr.length / 2) 7 | return [arr.slice(0, half), arr.slice(half)] 8 | } 9 | -------------------------------------------------------------------------------- /packages/shared/src/chain.ts: -------------------------------------------------------------------------------- 1 | import type { AnyFunction } from './function' 2 | 3 | export type OmitChainMethodDeep = { 4 | [P in keyof Omit]: T[P] extends AnyFunction 5 | ? ((...args: Parameters) => OmitChainMethodDeep, K>) 6 | : T[P] 7 | } 8 | -------------------------------------------------------------------------------- /packages/shared/src/function.ts: -------------------------------------------------------------------------------- 1 | export type AnyFunction = (...args: any[]) => any 2 | 3 | export function once any>(fn: T): () => ReturnType { 4 | let cached: { result: ReturnType } | undefined 5 | 6 | return (): ReturnType => { 7 | if (cached) { 8 | return cached.result 9 | } 10 | 11 | const result = fn() 12 | cached = { result } 13 | 14 | return result 15 | } 16 | } 17 | 18 | export function sequential( 19 | fn: (...args: A) => Promise, 20 | ): (...args: A) => Promise { 21 | let lastOperationPromise: Promise = Promise.resolve() 22 | 23 | return (...args: A): Promise => { 24 | return lastOperationPromise = lastOperationPromise.catch(() => { }).then(() => { 25 | return fn(...args) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/shared/src/id.test.ts: -------------------------------------------------------------------------------- 1 | import { SequentialIdGenerator } from './id' 2 | 3 | it('sequentialIdGenerator', () => { 4 | const idGenerator = new SequentialIdGenerator() 5 | 6 | expect(idGenerator.generate()).toBe(0) 7 | expect(idGenerator.generate()).toBe(1) 8 | expect(idGenerator.generate()).toBe(2) 9 | 10 | ;(idGenerator as any).nextId = Number.MAX_SAFE_INTEGER 11 | 12 | expect(idGenerator.generate()).toBe(Number.MAX_SAFE_INTEGER) 13 | expect(idGenerator.generate()).toBe(0) 14 | expect(idGenerator.generate()).toBe(1) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/shared/src/id.ts: -------------------------------------------------------------------------------- 1 | export class SequentialIdGenerator { 2 | private nextId = 0 3 | 4 | generate(): number { 5 | if (this.nextId === Number.MAX_SAFE_INTEGER) { 6 | this.nextId = 0 7 | return Number.MAX_SAFE_INTEGER 8 | } 9 | 10 | return this.nextId++ 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './args' 2 | export * from './array' 3 | export * from './chain' 4 | export * from './function' 5 | export * from './id' 6 | export * from './interceptor' 7 | export * from './iterator' 8 | export * from './json' 9 | export * from './object' 10 | export * from './queue' 11 | export * from './types' 12 | export * from './value' 13 | 14 | export { group, guard, mapEntries, mapValues, omit } from 'radash' 15 | export type { IsEqual, IsNever, PartialDeep, Promisable } from 'type-fest' 16 | -------------------------------------------------------------------------------- /packages/shared/src/json.test-d.ts: -------------------------------------------------------------------------------- 1 | import { stringifyJSON } from './json' 2 | 3 | it('stringifyJSON', () => { 4 | expectTypeOf(stringifyJSON(undefined)).toEqualTypeOf() 5 | expectTypeOf(stringifyJSON({})).toEqualTypeOf() 6 | }) 7 | -------------------------------------------------------------------------------- /packages/shared/src/json.test.ts: -------------------------------------------------------------------------------- 1 | import { parseEmptyableJSON, stringifyJSON } from './json' 2 | 3 | it('parseEmptyableJSON', () => { 4 | expect(parseEmptyableJSON(undefined)).toBeUndefined() 5 | expect(parseEmptyableJSON(null)).toBeUndefined() 6 | expect(parseEmptyableJSON('')).toBeUndefined() 7 | expect(parseEmptyableJSON('{}')).toEqual({}) 8 | expect(parseEmptyableJSON('{"foo":"bar"}')).toEqual({ foo: 'bar' }) 9 | }) 10 | 11 | it('stringifyJSON', () => { 12 | expect(stringifyJSON(undefined)).toBeUndefined() 13 | expect(stringifyJSON({})).toEqual('{}') 14 | expect(stringifyJSON({ foo: 'bar' })).toEqual('{"foo":"bar"}') 15 | }) 16 | -------------------------------------------------------------------------------- /packages/shared/src/json.ts: -------------------------------------------------------------------------------- 1 | export function parseEmptyableJSON(text: string | null | undefined): unknown { 2 | if (!text) { 3 | return undefined 4 | } 5 | 6 | return JSON.parse(text) 7 | } 8 | 9 | export function stringifyJSON(value: T): undefined extends T ? undefined | string : string { 10 | // eslint-disable-next-line ban/ban 11 | return JSON.stringify(value) 12 | } 13 | -------------------------------------------------------------------------------- /packages/shared/src/types.ts: -------------------------------------------------------------------------------- 1 | export type SetOptional = Omit & Partial> 2 | 3 | export type IntersectPick = Pick 4 | 5 | export type PromiseWithError = Promise & { __error?: { type: TError } } 6 | 7 | /** 8 | * The place where you can config the orpc types. 9 | * 10 | * - `throwableError` the error type that represent throwable errors should be `Error` or `null | undefined | {}` if you want more strict. 11 | */ 12 | export interface Registry { 13 | 14 | } 15 | 16 | export type ThrowableError = Registry extends { throwableError: infer T } ? T : Error 17 | -------------------------------------------------------------------------------- /packages/shared/src/value.test.ts: -------------------------------------------------------------------------------- 1 | import { value } from './value' 2 | 3 | it('value', async () => { 4 | expect(value(42)).toBe(42) 5 | expect(value(() => 42)).toBe(42) 6 | expect(await value(async () => 42)).toBe(42) 7 | 8 | expect(await value(async () => ({ 9 | then: (resolve: (value: PromiseLike) => void) => resolve(Promise.resolve(42)), 10 | }))).toBe(42) 11 | 12 | expect(value(() => ({ value: '42' }))).toEqual({ value: '42' }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/shared/src/value.ts: -------------------------------------------------------------------------------- 1 | export type Value = T | ((...args: TArgs) => T) 2 | 3 | export function value( 4 | value: Value, 5 | ...args: NoInfer 6 | ): T extends Value ? U : never { 7 | if (typeof value === 'function') { 8 | return (value as any)(...args) 9 | } 10 | 11 | return value as any 12 | } 13 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "lib": ["ES2022"] 5 | }, 6 | "include": ["src"], 7 | "exclude": [ 8 | "**/*.test.*", 9 | "**/*.test-d.ts", 10 | "**/__tests__/**", 11 | "**/__mocks__/**", 12 | "**/__snapshots__/**" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/solid-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/solid-query/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as keyModule from '@orpc/tanstack-query' 2 | import { createGeneralUtils } from './general-utils' 3 | 4 | const generateOperationKeySpy = vi.spyOn(keyModule, 'generateOperationKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect(utils.key({ input: { search: '__search__' }, type: 'infinite' })).toEqual([['path'], { input: { search: '__search__' }, type: 'infinite' }]) 15 | expect(generateOperationKeySpy).toHaveBeenCalledTimes(1) 16 | expect(generateOperationKeySpy).toHaveBeenCalledWith(['path'], { input: { search: '__search__' }, type: 'infinite' }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/solid-query/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouterUtils } from './router-utils' 2 | 3 | export * from './general-utils' 4 | export * from './procedure-utils' 5 | export * from './router-utils' 6 | export * from './types' 7 | 8 | export { 9 | createRouterUtils as createORPCSolidQueryUtils, 10 | } 11 | 12 | export type { OperationKeyOptions as BuildKeyOptions, OperationType as KeyType } from '@orpc/tanstack-query' 13 | export { generateOperationKey as buildKey } from '@orpc/tanstack-query' 14 | -------------------------------------------------------------------------------- /packages/solid-query/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/solid-query' 2 | import { orpc as client, streamedOrpc as streamedClient } from '../../client/tests/shared' 3 | import { createORPCSolidQueryUtils } from '../src' 4 | 5 | export const orpc = createORPCSolidQueryUtils(client) 6 | export const streamedOrpc = createORPCSolidQueryUtils(streamedClient) 7 | 8 | export const queryClient = new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | retry: false, 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/solid-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../tanstack-query" }, 6 | { "path": "../shared" } 7 | ], 8 | "include": ["src"], 9 | "exclude": [ 10 | "**/*.test.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/src/event-iterator.ts: -------------------------------------------------------------------------------- 1 | import { toEventIterator as baseToEventIterator } from '@orpc/standard-server-fetch' 2 | 3 | export function toEventIterator( 4 | body: string | undefined, 5 | ): AsyncIteratorObject & AsyncGenerator { 6 | if (body === undefined) { 7 | return baseToEventIterator(null) 8 | } 9 | 10 | return baseToEventIterator( 11 | new ReadableStream({ 12 | pull(controller) { 13 | controller.enqueue(body) 14 | controller.close() 15 | }, 16 | }).pipeThrough(new TextEncoderStream()), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/src/index.ts: -------------------------------------------------------------------------------- 1 | import type Stream from 'node:stream' 2 | 3 | export * from './body' 4 | export * from './event-iterator' 5 | export * from './headers' 6 | export * from './request' 7 | export * from './response' 8 | export * from './types' 9 | export * from './url' 10 | 11 | export { toAbortSignal, toEventStream, type ToEventStreamOptions } from '@orpc/standard-server-node' 12 | 13 | export type ResponseStream = Stream.Writable 14 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/src/types.test-d.ts: -------------------------------------------------------------------------------- 1 | import type * as awslambda from 'aws-lambda' 2 | import type { APIGatewayProxyEventV2 } from './types' 3 | 4 | it('APIGatewayProxyEvent', () => { 5 | expectTypeOf().toExtend() 6 | }) 7 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/src/url.test.ts: -------------------------------------------------------------------------------- 1 | import { toStandardUrl } from './url' 2 | 3 | it('toStandardUrl', () => { 4 | expect(toStandardUrl({ 5 | requestContext: { 6 | domainName: 'example.com', 7 | }, 8 | rawPath: '/api/planets', 9 | rawQueryString: 'key1=value1&key2=value2', 10 | } as any)).toEqual(new URL('https://example.com/api/planets?key1=value1&key2=value2')) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/src/url.ts: -------------------------------------------------------------------------------- 1 | import type { APIGatewayProxyEventV2 } from './types' 2 | 3 | export function toStandardUrl(event: APIGatewayProxyEventV2): URL { 4 | return new URL(`https://${event.requestContext.domainName}${event.rawPath}?${event.rawQueryString}`) 5 | } 6 | -------------------------------------------------------------------------------- /packages/standard-server-aws-lambda/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "lib": ["ES2022"], 5 | "types": ["node"] 6 | }, 7 | "references": [ 8 | { "path": "../shared" }, 9 | { "path": "../standard-server-node" } 10 | ], 11 | "include": ["src"], 12 | "exclude": [ 13 | "**/*.test.*", 14 | "**/*.test-d.ts", 15 | "**/*.bench.*", 16 | "**/__tests__/**", 17 | "**/__mocks__/**", 18 | "**/__snapshots__/**" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/standard-server-fetch/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/standard-server-fetch/playground/event-source-client.ts: -------------------------------------------------------------------------------- 1 | const eventSource = new EventSource('http://localhost:3000') 2 | 3 | eventSource.addEventListener('message', (event) => { 4 | console.log('message', event) 5 | }) 6 | 7 | eventSource.addEventListener('error', (event) => { 8 | console.log('error', event) 9 | }) 10 | 11 | eventSource.addEventListener('done', (event) => { 12 | console.log('done', event) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/standard-server-fetch/playground/event-source.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:3000 2 | Content-Type: text/event-stream 3 | 4 | event: message 5 | data: {"foo":"bar"} 6 | 7 | event: message 8 | data: {"foo":"bar"} 9 | 10 | event: done 11 | data: 123 12 | -------------------------------------------------------------------------------- /packages/standard-server-fetch/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body' 2 | export * from './event-iterator' 3 | export * from './headers' 4 | export * from './request' 5 | export * from './response' 6 | -------------------------------------------------------------------------------- /packages/standard-server-fetch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../standard-server" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/*.bench.*", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/standard-server-node/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/standard-server-node/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body' 2 | export * from './event-iterator' 3 | export * from './method' 4 | export * from './request' 5 | export * from './response' 6 | export * from './signal' 7 | export * from './types' 8 | export * from './url' 9 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/method.test.ts: -------------------------------------------------------------------------------- 1 | import { toStandardMethod } from './method' 2 | 3 | it('toStandardMethod', () => { 4 | expect(toStandardMethod('GET')).toEqual('GET') 5 | expect(toStandardMethod('POST')).toEqual('POST') 6 | expect(toStandardMethod(undefined)).toEqual('GET') 7 | }) 8 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/method.ts: -------------------------------------------------------------------------------- 1 | export function toStandardMethod(method: string | undefined): string { 2 | return method ?? 'GET' 3 | } 4 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/request.ts: -------------------------------------------------------------------------------- 1 | import type { StandardLazyRequest } from '@orpc/standard-server' 2 | import type { NodeHttpRequest, NodeHttpResponse } from './types' 3 | import { once } from '@orpc/shared' 4 | import { toStandardBody } from './body' 5 | import { toStandardMethod } from './method' 6 | import { toAbortSignal } from './signal' 7 | import { toStandardUrl } from './url' 8 | 9 | export function toStandardLazyRequest( 10 | req: NodeHttpRequest, 11 | res: NodeHttpResponse, 12 | ): StandardLazyRequest { 13 | return { 14 | method: toStandardMethod(req.method), 15 | url: toStandardUrl(req), 16 | headers: req.headers, 17 | body: once(() => toStandardBody(req)), 18 | signal: toAbortSignal(res), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/signal.ts: -------------------------------------------------------------------------------- 1 | import type Stream from 'node:stream' 2 | 3 | export function toAbortSignal(stream: Stream.Writable): AbortSignal { 4 | const controller = new AbortController() 5 | 6 | stream.once('error', error => controller.abort(error)) 7 | 8 | stream.once('close', () => { 9 | if (!stream.writableFinished) { 10 | controller.abort(new Error('Writable stream closed before it finished writing')) 11 | } 12 | }) 13 | 14 | return controller.signal 15 | } 16 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage, ServerResponse } from 'node:http' 2 | import type { Http2ServerRequest, Http2ServerResponse } from 'node:http2' 3 | 4 | export type NodeHttpRequest = (IncomingMessage | Http2ServerRequest) & { 5 | /** 6 | * Replace `req.url` with `req.originalUrl` when `req.originalUrl` is available. 7 | * This is useful for `express.js` middleware. 8 | */ 9 | originalUrl?: string 10 | } 11 | 12 | export type NodeHttpResponse = ServerResponse | Http2ServerResponse 13 | -------------------------------------------------------------------------------- /packages/standard-server-node/src/url.ts: -------------------------------------------------------------------------------- 1 | import type { NodeHttpRequest } from './types' 2 | 3 | export function toStandardUrl(req: NodeHttpRequest): URL { 4 | const protocol = ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:') 5 | const host = req.headers.host ?? 'localhost' 6 | const url = new URL(req.originalUrl ?? req.url ?? '/', `${protocol}//${host}`) 7 | 8 | return url 9 | } 10 | -------------------------------------------------------------------------------- /packages/standard-server-node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "lib": ["ES2022"], 5 | "types": ["node"] 6 | }, 7 | "references": [ 8 | { "path": "../shared" } 9 | ], 10 | "include": ["src"], 11 | "exclude": [ 12 | "**/*.test.*", 13 | "**/*.test-d.ts", 14 | "**/*.bench.*", 15 | "**/__tests__/**", 16 | "**/__mocks__/**", 17 | "**/__snapshots__/**" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/standard-server-peer/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/standard-server-peer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client' 2 | export * from './codec' 3 | export * from './event-iterator' 4 | export * from './server' 5 | export * from './types' 6 | -------------------------------------------------------------------------------- /packages/standard-server-peer/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { Promisable } from '@orpc/shared' 2 | 3 | export type EncodedMessage = string | ArrayBufferLike | Blob 4 | 5 | export interface EncodedMessageSendFn { 6 | (message: EncodedMessage): Promisable 7 | } 8 | -------------------------------------------------------------------------------- /packages/standard-server-peer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../standard-server" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/*.bench.*", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/standard-server/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/standard-server/src/batch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './request' 2 | export * from './response' 3 | export * from './signal' 4 | -------------------------------------------------------------------------------- /packages/standard-server/src/event-iterator/errors.ts: -------------------------------------------------------------------------------- 1 | export class EventEncoderError extends TypeError { } 2 | export class EventDecoderError extends TypeError { } 3 | 4 | export interface ErrorEventOptions extends ErrorOptions { 5 | message?: string 6 | data?: unknown 7 | } 8 | 9 | export class ErrorEvent extends Error { 10 | public data: unknown 11 | 12 | constructor(options?: ErrorEventOptions) { 13 | super(options?.message ?? 'An error event was received', options) 14 | 15 | this.data = options?.data 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/standard-server/src/event-iterator/index.ts: -------------------------------------------------------------------------------- 1 | export * from './decoder' 2 | export * from './encoder' 3 | export * from './errors' 4 | export * from './meta' 5 | export * from './types' 6 | -------------------------------------------------------------------------------- /packages/standard-server/src/event-iterator/types.ts: -------------------------------------------------------------------------------- 1 | export interface EventMessage { 2 | event: string | undefined 3 | id: string | undefined 4 | data: string | undefined 5 | 6 | /** 7 | * The number of milliseconds to wait before retrying the event iterator if error occurs. 8 | */ 9 | retry: number | undefined 10 | 11 | comments: string[] 12 | } 13 | -------------------------------------------------------------------------------- /packages/standard-server/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event-iterator' 2 | export * from './types' 3 | export * from './utils' 4 | -------------------------------------------------------------------------------- /packages/standard-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../shared" } 5 | ], 6 | "include": ["src"], 7 | "exclude": [ 8 | "**/*.test.*", 9 | "**/*.test-d.ts", 10 | "**/*.bench.*", 11 | "**/__tests__/**", 12 | "**/__mocks__/**", 13 | "**/__snapshots__/**" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/svelte-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/svelte-query/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as keyModule from '@orpc/tanstack-query' 2 | import { createGeneralUtils } from './general-utils' 3 | 4 | const generateOperationKeySpy = vi.spyOn(keyModule, 'generateOperationKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect(utils.key({ input: { search: '__search__' }, type: 'infinite' })).toEqual([['path'], { input: { search: '__search__' }, type: 'infinite' }]) 15 | expect(generateOperationKeySpy).toHaveBeenCalledTimes(1) 16 | expect(generateOperationKeySpy).toHaveBeenCalledWith(['path'], { input: { search: '__search__' }, type: 'infinite' }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/svelte-query/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouterUtils } from './router-utils' 2 | 3 | export * from './general-utils' 4 | export * from './procedure-utils' 5 | export * from './router-utils' 6 | export * from './types' 7 | 8 | export { 9 | createRouterUtils as createORPCSvelteQueryUtils, 10 | } 11 | 12 | export type { OperationKeyOptions as BuildKeyOptions, OperationType as KeyType } from '@orpc/tanstack-query' 13 | export { generateOperationKey as buildKey } from '@orpc/tanstack-query' 14 | -------------------------------------------------------------------------------- /packages/svelte-query/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/svelte-query' 2 | import { orpc as client, streamedOrpc as streamedClient } from '../../client/tests/shared' 3 | import { createORPCSvelteQueryUtils } from '../src' 4 | 5 | export const orpc = createORPCSvelteQueryUtils(client) 6 | export const streamedOrpc = createORPCSvelteQueryUtils(streamedClient) 7 | 8 | export const queryClient = new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | retry: false, 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/svelte-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/__tests__/**", 12 | "**/__mocks__/**", 13 | "**/__snapshots__/**" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/tanstack-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/tanstack-query/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import { createGeneralUtils } from './general-utils' 2 | import * as KeyModule from './key' 3 | 4 | const generateOperationKeySpy = vi.spyOn(KeyModule, 'generateOperationKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect( 15 | utils.key({ input: { search: '__search__' }, type: 'infinite' }), 16 | ).toBe(generateOperationKeySpy.mock.results[0]!.value) 17 | 18 | expect(generateOperationKeySpy).toHaveBeenCalledTimes(1) 19 | expect(generateOperationKeySpy).toHaveBeenCalledWith(['path'], { input: { search: '__search__' }, type: 'infinite' }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/tanstack-query/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './general-utils' 2 | export * from './key' 3 | export * from './procedure-utils' 4 | export * from './router-utils' 5 | export { createRouterUtils as createTanstackQueryUtils } from './router-utils' 6 | export * from './types' 7 | export { 8 | OPERATION_CONTEXT_SYMBOL as TANSTACK_QUERY_OPERATION_CONTEXT_SYMBOL, 9 | type OperationContext as TanstackQueryOperationContext, 10 | } from './types' 11 | -------------------------------------------------------------------------------- /packages/tanstack-query/src/key.test.ts: -------------------------------------------------------------------------------- 1 | import { generateOperationKey } from './key' 2 | 3 | it('generateOperationKey', () => { 4 | expect(generateOperationKey(['path'])).toEqual([['path'], {}]) 5 | expect(generateOperationKey(['planet', 'create'], { type: 'mutation' })) 6 | .toEqual([['planet', 'create'], { type: 'mutation' }]) 7 | expect(generateOperationKey(['planet', 'find'], { type: 'query', input: { id: 1 } })) 8 | .toEqual([['planet', 'find'], { type: 'query', input: { id: 1 } }]) 9 | expect(generateOperationKey(['planet', 'stream'], { type: 'streamed', input: { cursor: 0 }, fnOptions: { refetchMode: 'append' } })) 10 | .toEqual([['planet', 'stream'], { type: 'streamed', input: { cursor: 0 }, fnOptions: { refetchMode: 'append' } }]) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/tanstack-query/src/key.ts: -------------------------------------------------------------------------------- 1 | import type { OperationKey, OperationKeyOptions, OperationType } from './types' 2 | 3 | export function generateOperationKey( 4 | path: readonly string[], 5 | state: OperationKeyOptions = {}, 6 | ): OperationKey { 7 | return [path, { 8 | ...state.input !== undefined ? { input: state.input } : {}, 9 | ...state.type !== undefined ? { type: state.type } : {}, 10 | ...state.fnOptions !== undefined ? { fnOptions: state.fnOptions } : {}, 11 | } as any] 12 | } 13 | -------------------------------------------------------------------------------- /packages/tanstack-query/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query' 2 | import { orpc as client, streamedOrpc as streamedClient } from '../../client/tests/shared' 3 | import { createTanstackQueryUtils } from '../src' 4 | 5 | export const orpc = createTanstackQueryUtils(client) 6 | export const streamedOrpc = createTanstackQueryUtils(streamedClient) 7 | 8 | export const queryClient = new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | retry: false, 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/tanstack-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.test-d.ts", 11 | "**/*.bench.*", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/valibot/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/valibot/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './converter' 2 | -------------------------------------------------------------------------------- /packages/valibot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../openapi" }, 5 | { "path": "../contract" }, 6 | { "path": "../server" } 7 | ], 8 | "include": ["src"], 9 | "exclude": [ 10 | "**/*.test.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/vue-colada/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/vue-colada/src/general-utils.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { GeneralUtils } from './general-utils' 2 | import { ref } from 'vue' 3 | 4 | describe('GeneralUtils', () => { 5 | describe('.key', () => { 6 | const utils = {} as GeneralUtils<{ a: { b: { c: number } } }> 7 | 8 | it('infer correct input type & input', () => { 9 | utils.key() 10 | utils.key({}) 11 | utils.key({ input: { a: { b: { c: 1 } } } }) 12 | 13 | // @ts-expect-error invalid input 14 | utils.key({ input: 123 }) 15 | // @ts-expect-error invalid input 16 | utils.key({ input: { a: { b: { c: '1' } } } }) 17 | 18 | // @ts-expect-error not allow ref 19 | utils.key({ input: { a: { b: ref({ c: 1 }) } } }) 20 | 21 | // @ts-expect-error invalid input 22 | utils.key({ type: 'ddd' }) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/vue-colada/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import { createGeneralUtils } from './general-utils' 2 | import * as keyModule from './key' 3 | 4 | const buildKeySpy = vi.spyOn(keyModule, 'buildKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect( 15 | utils.key({ input: { search: '__search__' } }), 16 | ).toEqual( 17 | buildKeySpy.mock.results[0]!.value, 18 | ) 19 | 20 | expect(buildKeySpy).toHaveBeenCalledTimes(1) 21 | expect(buildKeySpy).toHaveBeenCalledWith(['path'], { input: { search: '__search__' } }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/vue-colada/src/general-utils.ts: -------------------------------------------------------------------------------- 1 | import type { EntryKey } from '@pinia/colada' 2 | import type { BuildKeyOptions } from './key' 3 | import { buildKey } from './key' 4 | 5 | export interface GeneralUtils { 6 | /** 7 | * Generate a query/mutation key for checking status, invalidate, set, get, etc. 8 | * 9 | * @see {@link https://orpc.unnoq.com/docs/integrations/pinia-colada#query-mutation-key Pinia Colada Query/Mutation Key Docs} 10 | */ 11 | key(options?: BuildKeyOptions): EntryKey 12 | } 13 | 14 | export function createGeneralUtils( 15 | path: string[], 16 | ): GeneralUtils { 17 | return { 18 | key(options) { 19 | return buildKey(path, options) 20 | }, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/vue-colada/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouterUtils } from './router-utils' 2 | 3 | export * from './general-utils' 4 | export * from './key' 5 | export * from './procedure-utils' 6 | export * from './router-utils' 7 | export * from './types' 8 | 9 | export { 10 | createRouterUtils as createORPCVueColadaUtils, 11 | } 12 | -------------------------------------------------------------------------------- /packages/vue-colada/src/key.ts: -------------------------------------------------------------------------------- 1 | import type { PartialDeep } from '@orpc/shared' 2 | import type { EntryKey } from '@pinia/colada' 3 | import { StandardRPCJsonSerializer } from '@orpc/client/standard' 4 | 5 | export interface BuildKeyOptions { 6 | type?: 'query' | 'mutation' 7 | input?: PartialDeep 8 | } 9 | 10 | export function buildKey( 11 | path: string[], 12 | options: BuildKeyOptions = {}, 13 | ): EntryKey { 14 | const [json] = new StandardRPCJsonSerializer().serialize(options.input) 15 | 16 | const withInput = json !== undefined ? { input: json } : {} 17 | const withType = options.type !== undefined ? { type: options.type } : {} 18 | 19 | return [ 20 | path, 21 | { 22 | ...withInput, 23 | ...withType as any, 24 | }, 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/vue-colada/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { PiniaColada } from '@pinia/colada' 2 | import { mount as baseMount } from '@vue/test-utils' 3 | import { createPinia } from 'pinia' 4 | import { orpc as client } from '../../client/tests/shared' 5 | import { createORPCVueColadaUtils } from '../src' 6 | 7 | export const orpc = createORPCVueColadaUtils(client) 8 | 9 | export const mount: typeof baseMount = (component, options) => { 10 | return baseMount(component, { 11 | global: { 12 | plugins: [ 13 | createPinia(), 14 | PiniaColada, 15 | ], 16 | }, 17 | ...options, 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /packages/vue-colada/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.bench.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/vue-query/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/vue-query/src/general-utils.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { GeneralUtils } from './general-utils' 2 | import { ref } from 'vue' 3 | 4 | describe('GeneralUtils', () => { 5 | describe('.key', () => { 6 | const utils = {} as GeneralUtils<{ a: { b: { c: number } } }> 7 | 8 | it('infer correct input type & input', () => { 9 | utils.key() 10 | utils.key({}) 11 | utils.key({ input: { a: { b: { c: 1 } } } }) 12 | 13 | // @ts-expect-error invalid input 14 | utils.key({ input: 123 }) 15 | // @ts-expect-error invalid input 16 | utils.key({ input: { a: { b: { c: '1' } } } }) 17 | 18 | // @ts-expect-error not allow ref 19 | utils.key({ input: { a: { b: ref({ c: 1 }) } } }) 20 | 21 | // @ts-expect-error invalid input 22 | utils.key({ type: 'ddd' }) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/vue-query/src/general-utils.test.ts: -------------------------------------------------------------------------------- 1 | import * as keyModule from '@orpc/tanstack-query' 2 | import { createGeneralUtils } from './general-utils' 3 | 4 | const generateOperationKeySpy = vi.spyOn(keyModule, 'generateOperationKey') 5 | 6 | beforeEach(() => { 7 | vi.clearAllMocks() 8 | }) 9 | 10 | describe('createGeneralUtils', () => { 11 | const utils = createGeneralUtils(['path']) 12 | 13 | it('.key', () => { 14 | expect( 15 | utils.key({ input: { search: '__search__' }, type: 'infinite' }), 16 | ).toEqual( 17 | generateOperationKeySpy.mock.results[0]!.value, 18 | ) 19 | 20 | expect(generateOperationKeySpy).toHaveBeenCalledTimes(1) 21 | expect(generateOperationKeySpy).toHaveBeenCalledWith(['path'], { input: { search: '__search__' }, type: 'infinite' }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/vue-query/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouterUtils } from './router-utils' 2 | 3 | export * from './general-utils' 4 | export * from './procedure-utils' 5 | export * from './router-utils' 6 | export * from './types' 7 | export * from './utils' 8 | 9 | export { 10 | createRouterUtils as createORPCVueQueryUtils, 11 | } 12 | 13 | export type { OperationKeyOptions as BuildKeyOptions, OperationType as KeyType } from '@orpc/tanstack-query' 14 | export { generateOperationKey as buildKey } from '@orpc/tanstack-query' 15 | -------------------------------------------------------------------------------- /packages/vue-query/tests/shared.tsx: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/vue-query' 2 | import { orpc as client, streamedOrpc as streamedClient } from '../../client/tests/shared' 3 | import { createORPCVueQueryUtils } from '../src' 4 | 5 | export const orpc = createORPCVueQueryUtils(client) 6 | export const streamedOrpc = createORPCVueQueryUtils(streamedClient) 7 | 8 | export const queryClient = new QueryClient({ 9 | defaultOptions: { 10 | queries: { 11 | retry: false, 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/vue-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../client" }, 5 | { "path": "../shared" } 6 | ], 7 | "include": ["src"], 8 | "exclude": [ 9 | "**/*.test.*", 10 | "**/*.bench.*", 11 | "**/*.test-d.ts", 12 | "**/__tests__/**", 13 | "**/__mocks__/**", 14 | "**/__snapshots__/**" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/zod/.gitignore: -------------------------------------------------------------------------------- 1 | # Hidden folders and files 2 | .* 3 | !.gitignore 4 | !.*.example 5 | 6 | # Common generated folders 7 | logs/ 8 | node_modules/ 9 | out/ 10 | dist/ 11 | dist-ssr/ 12 | build/ 13 | coverage/ 14 | temp/ 15 | 16 | # Common generated files 17 | *.log 18 | *.log.* 19 | *.tsbuildinfo 20 | *.vitest-temp.json 21 | vite.config.ts.timestamp-* 22 | vitest.config.ts.timestamp-* 23 | 24 | # Common manual ignore files 25 | *.local 26 | *.pem -------------------------------------------------------------------------------- /packages/zod/src/index.ts: -------------------------------------------------------------------------------- 1 | import { customJsonSchema } from './custom-json-schema' 2 | import { blob } from './schemas/blob' 3 | import { file } from './schemas/file' 4 | import { regexp } from './schemas/regexp' 5 | import { url } from './schemas/url' 6 | 7 | export * from './coercer' 8 | export * from './converter' 9 | export * from './custom-json-schema' 10 | export * from './schemas/base' 11 | export * from './schemas/blob' 12 | export * from './schemas/file' 13 | export * from './schemas/regexp' 14 | export * from './schemas/url' 15 | 16 | export const oz = { 17 | file, 18 | blob, 19 | url, 20 | regexp, 21 | openapi: customJsonSchema, 22 | } 23 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/blob.test.ts: -------------------------------------------------------------------------------- 1 | import { getCustomZodDef } from './base' 2 | import { blob } from './blob' 3 | 4 | it('blob', () => { 5 | expect(blob().parse(new Blob([]))).toBeInstanceOf(Blob) 6 | expect(blob().parse(new File([], ''))).toBeInstanceOf(File) 7 | expect(() => blob().parse({})).toThrow('Input is not a blob') 8 | expect(() => blob('__INVALID__').parse({})).toThrow('__INVALID__') 9 | 10 | expect(getCustomZodDef(blob()._def)).toEqual({ type: 'blob' }) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/blob.ts: -------------------------------------------------------------------------------- 1 | import type { ZodType, ZodTypeDef } from 'zod' 2 | import type { CustomParams } from './base' 3 | import { custom } from 'zod' 4 | import { composeParams, setCustomZodDef } from './base' 5 | 6 | export function blob( 7 | params?: string | CustomParams | ((input: unknown) => CustomParams), 8 | ): ZodType { 9 | const schema = custom( 10 | val => val instanceof Blob, 11 | composeParams( 12 | () => 'Input is not a blob', 13 | params, 14 | ), 15 | ) 16 | 17 | setCustomZodDef(schema._def, { type: 'blob' }) 18 | 19 | return schema 20 | } 21 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/regexp.test.ts: -------------------------------------------------------------------------------- 1 | import { getCustomZodDef } from './base' 2 | import { regexp } from './regexp' 3 | 4 | it('regexp', () => { 5 | expect(regexp().parse(/d/)).toBeInstanceOf(RegExp) 6 | expect(() => regexp().parse({})).toThrow('Input is not a regexp') 7 | expect(() => regexp(() => ({ message: '__INVALID__' })).parse({})).toThrow('__INVALID__') 8 | 9 | expect(getCustomZodDef(regexp()._def)).toEqual({ type: 'regexp' }) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/regexp.ts: -------------------------------------------------------------------------------- 1 | import type { ZodType, ZodTypeDef } from 'zod' 2 | import type { CustomParams } from './base' 3 | import { custom } from 'zod' 4 | import { composeParams, setCustomZodDef } from './base' 5 | 6 | export function regexp( 7 | params?: string | CustomParams | ((input: unknown) => CustomParams), 8 | ): ZodType { 9 | const schema = custom( 10 | val => val instanceof RegExp, 11 | composeParams( 12 | () => 'Input is not a regexp', 13 | params, 14 | ), 15 | ) 16 | 17 | setCustomZodDef(schema._def, { type: 'regexp' }) 18 | 19 | return schema 20 | } 21 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/url.test.ts: -------------------------------------------------------------------------------- 1 | import { getCustomZodDef } from './base' 2 | import { url } from './url' 3 | 4 | it('url', () => { 5 | expect(url().parse(new URL('https://example.com'))).toBeInstanceOf(URL) 6 | expect(() => url().parse({})).toThrow('Input is not a URL') 7 | expect(() => url('__INVALID__').parse({})).toThrow('__INVALID__') 8 | 9 | expect(getCustomZodDef(url()._def)).toEqual({ type: 'url' }) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/zod/src/schemas/url.ts: -------------------------------------------------------------------------------- 1 | import type { ZodType, ZodTypeDef } from 'zod' 2 | import type { CustomParams } from './base' 3 | import { custom } from 'zod' 4 | import { composeParams, setCustomZodDef } from './base' 5 | 6 | export function url( 7 | params?: string | CustomParams | ((input: unknown) => CustomParams), 8 | ): ZodType { 9 | const schema = custom( 10 | val => val instanceof URL, 11 | composeParams( 12 | () => 'Input is not a URL', 13 | params, 14 | ), 15 | ) 16 | 17 | setCustomZodDef(schema._def, { type: 'url' }) 18 | 19 | return schema 20 | } 21 | -------------------------------------------------------------------------------- /packages/zod/src/zod4/coercer.rest.test.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod/v4' 2 | import { testSchemaSmartCoercion } from '../../tests/shared' 3 | 4 | testSchemaSmartCoercion([ 5 | { 6 | name: 'number - 123', 7 | schema: z.number().or(z.string()), 8 | input: '123', 9 | }, 10 | { 11 | name: 'boolean - true', 12 | schema: z.boolean().or(z.string()), 13 | input: 'true', 14 | }, 15 | ]) 16 | -------------------------------------------------------------------------------- /packages/zod/src/zod4/converter.processed.test.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod/v4' 2 | import { testSchemaConverter } from '../../tests/shared' 3 | 4 | testSchemaConverter([ 5 | { 6 | name: 'lazy(() => z.object({ value: z.string() }))', 7 | schema: z.lazy(() => z.object({ value: z.string() })), 8 | input: [true, { type: 'object', properties: { value: { type: 'string' } }, required: ['value'] }], 9 | }, 10 | { 11 | name: 'lazy(() => z.object({ value: z.lazy(() => z.string()) }))', 12 | schema: z.lazy(() => z.object({ value: z.lazy(() => z.string()) })), 13 | input: [true, { type: 'object', properties: { value: { } } }], 14 | }, 15 | { 16 | name: 'string().transform(x => x)', 17 | schema: z.string().transform(x => x), 18 | input: [true, { type: 'string' }], 19 | output: [false, {}], 20 | }, 21 | ]) 22 | -------------------------------------------------------------------------------- /packages/zod/src/zod4/converter.rest.test.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod/v4' 2 | import { testSchemaConverter } from '../../tests/shared' 3 | 4 | testSchemaConverter([ 5 | { 6 | name: 'z.symbol()', 7 | schema: z.symbol(), 8 | input: [true, { not: {} }], 9 | }, 10 | { 11 | name: 'z.promise(z.string())', 12 | schema: z.promise(z.string()), 13 | input: [true, { not: {} }], 14 | }, 15 | { 16 | name: 'z.custom(() => false)', 17 | schema: z.custom(() => false), 18 | input: [true, { not: {} }], 19 | }, 20 | ]) 21 | -------------------------------------------------------------------------------- /packages/zod/src/zod4/index.ts: -------------------------------------------------------------------------------- 1 | export * from './coercer' 2 | export * from './converter' 3 | export * from './registries' 4 | -------------------------------------------------------------------------------- /packages/zod/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.lib.json", 3 | "references": [ 4 | { "path": "../openapi" }, 5 | { "path": "../contract" }, 6 | { "path": "../server" }, 7 | { "path": "../shared" } 8 | ], 9 | "include": ["src"], 10 | "exclude": [ 11 | "**/*.test.*", 12 | "**/*.test-d.ts", 13 | "**/__tests__/**", 14 | "**/__mocks__/**", 15 | "**/__snapshots__/**" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /playgrounds/astro/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # jetbrains setting folder 24 | .idea/ 25 | -------------------------------------------------------------------------------- /playgrounds/astro/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/astro/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Astro](https://astro.build). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/astro/astro.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from 'astro/config' 3 | 4 | import react from '@astrojs/react' 5 | 6 | // https://astro.build/config 7 | export default defineConfig({ 8 | integrations: [react()], 9 | }) 10 | -------------------------------------------------------------------------------- /playgrounds/astro/src/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { router } from '../router' 2 | import type { RouterClient } from '@orpc/server' 3 | import { createORPCClient } from '@orpc/client' 4 | import { RPCLink } from '@orpc/client/fetch' 5 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 6 | import { BatchLinkPlugin } from '@orpc/client/plugins' 7 | 8 | const link = new RPCLink({ 9 | url: `${typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000'}/rpc`, 10 | plugins: [ 11 | new BatchLinkPlugin({ 12 | groups: [{ 13 | condition: () => true, 14 | context: {}, 15 | }], 16 | }), 17 | ], 18 | }) 19 | 20 | export const client: RouterClient = createORPCClient(link) 21 | 22 | export const orpc = createTanstackQueryUtils(client) 23 | -------------------------------------------------------------------------------- /playgrounds/astro/src/middlewares/auth.ts: -------------------------------------------------------------------------------- 1 | import type { User } from '../schemas/user' 2 | import { os } from '@orpc/server' 3 | 4 | export const requiredAuthMiddleware = os 5 | .$context<{ session?: { user?: User } }>() 6 | .middleware(async ({ context, next }) => { 7 | if (!context.session || !context.session.user) { 8 | throw new Error('UNAUTHORIZED') 9 | } 10 | 11 | return next({ 12 | context: { user: context.session.user }, 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /playgrounds/astro/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { dbProviderMiddleware } from './middlewares/db' 3 | import { requiredAuthMiddleware } from './middlewares/auth' 4 | 5 | export const pub = os 6 | .use(dbProviderMiddleware) 7 | 8 | export const authed = pub 9 | .use(requiredAuthMiddleware) 10 | -------------------------------------------------------------------------------- /playgrounds/astro/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { CreatePlanetMutationForm } from "../components/orpc-mutation"; 3 | import { ListPlanetsQuery } from "../components/orpc-query"; 4 | --- 5 | 6 |
7 |

ORPC Playground

8 | You can visit the 9 | Scalar API Reference 10 | page. 11 |
12 | 13 |
14 | 15 |
-------------------------------------------------------------------------------- /playgrounds/astro/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/astro/src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { File } from 'node:buffer' 2 | 3 | /** 4 | * This file aims to polyfill missing APIs in Node.js 18 that oRPC depends on. 5 | * 6 | * Since Stackblitz runs on Node.js 18, these polyfills ensure oRPC works in that environment. 7 | * If you're running oRPC locally, please use Node.js 20 or later for full compatibility. 8 | */ 9 | 10 | /** 11 | * Note: Stackblitz provides an emulated Node.js environment with inherent limitations. 12 | * If you encounter issues, please test on a local setup with Node.js 20 or later before reporting them. 13 | */ 14 | 15 | /** 16 | * The `oz.file()` schema depends on the `File` API. 17 | * If you're not using `oz.file()`, you can safely remove this polyfill. 18 | */ 19 | if (typeof globalThis.File === 'undefined') { 20 | globalThis.File = File as any 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/astro/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/astro/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/astro/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/astro/src/shared/query.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query' 2 | 3 | export const queryClient = new QueryClient() 4 | -------------------------------------------------------------------------------- /playgrounds/astro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "react" 6 | }, 7 | "include": [ 8 | ".astro/types.d.ts", 9 | "**/*" 10 | ], 11 | "exclude": [ 12 | "dist" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .output 12 | stats.html 13 | stats-*.json 14 | .wxt 15 | web-ext.config.ts 16 | 17 | # Editor directories and files 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [WXT](https://wxt.dev/) 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | ## Sponsors 14 | 15 |

16 | 17 | 18 | 19 |

20 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/background/index.ts: -------------------------------------------------------------------------------- 1 | import { experimental_RPCHandler as RPCHandler } from '@orpc/server/message-port' 2 | import { router } from './router' 3 | 4 | const handler = new RPCHandler(router) 5 | 6 | export default defineBackground(() => { 7 | browser.runtime.onConnect.addListener((port) => { 8 | handler.upgrade(port) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/background/orpc.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { dbProviderMiddleware } from './middlewares/db' 3 | import { requiredAuthMiddleware } from './middlewares/auth' 4 | 5 | export const pub = os 6 | .use(dbProviderMiddleware) 7 | 8 | export const authed = pub 9 | .use(requiredAuthMiddleware) 10 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/background/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/background/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/background/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/content.ts: -------------------------------------------------------------------------------- 1 | export default defineContentScript({ 2 | matches: ['*://*.google.com/*'], 3 | main() { 4 | console.log('Hello content.') 5 | }, 6 | }) 7 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/popup/App.tsx: -------------------------------------------------------------------------------- 1 | import { CreatePlanetMutationForm } from './components/orpc-mutation' 2 | import { ListPlanetsQuery } from './components/orpc-query' 3 | 4 | export default function App() { 5 | return ( 6 |
7 |

ORPC Playground

8 |
9 | 10 |
11 | 12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Default Popup Title 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { router } from '../.../../../background/router' 2 | import type { RouterClient } from '@orpc/server' 3 | import { createORPCClient } from '@orpc/client' 4 | import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port' 5 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 6 | 7 | const port = browser.runtime.connect() 8 | 9 | const link = new RPCLink({ 10 | port, 11 | }) 12 | 13 | export const client: RouterClient = createORPCClient(link) 14 | 15 | export const orpc = createTanstackQueryUtils(client) 16 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/popup/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query' 5 | 6 | const queryClient = new QueryClient() 7 | 8 | ReactDOM.createRoot(document.getElementById('root')!).render( 9 | 10 | 11 | 12 | 13 | , 14 | ) 15 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/entrypoints/popup/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/public/icon/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/browser-extension/public/icon/128.png -------------------------------------------------------------------------------- /playgrounds/browser-extension/public/icon/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/browser-extension/public/icon/16.png -------------------------------------------------------------------------------- /playgrounds/browser-extension/public/icon/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/browser-extension/public/icon/32.png -------------------------------------------------------------------------------- /playgrounds/browser-extension/public/icon/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/browser-extension/public/icon/48.png -------------------------------------------------------------------------------- /playgrounds/browser-extension/public/icon/96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/browser-extension/public/icon/96.png -------------------------------------------------------------------------------- /playgrounds/browser-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.wxt/tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "allowImportingTsExtensions": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /playgrounds/browser-extension/wxt.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'wxt' 2 | 3 | // See https://wxt.dev/api/config.html 4 | export default defineConfig({ 5 | modules: ['@wxt-dev/module-react'], 6 | }) 7 | -------------------------------------------------------------------------------- /playgrounds/contract-first/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | pnpm-lock.yaml 3 | package-lock.json 4 | yarn.lock -------------------------------------------------------------------------------- /playgrounds/contract-first/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/contract-first/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) + [Contract-First](https://orpc.unnoq.com/docs/contract-first/define-contract) + [Node.js](https://nodejs.org). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/contract-first/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@orpc/contract-first-playground", 3 | "type": "module", 4 | "version": "1.3.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "tsx --watch src/main.ts", 8 | "start": "tsx src/main.ts", 9 | "type:check": "tsc --noEmit" 10 | }, 11 | "devDependencies": { 12 | "@orpc/client": "next", 13 | "@orpc/contract": "next", 14 | "@orpc/openapi": "next", 15 | "@orpc/server": "next", 16 | "@orpc/tanstack-query": "next", 17 | "@orpc/zod": "next", 18 | "@tanstack/react-query": "^5.77.2", 19 | "@types/node": "^22.15.18", 20 | "tsx": "^4.19.3", 21 | "typescript": "^5.8.3", 22 | "zod": "^3.25.11" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/contract/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const contract = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/contract/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, oc } from '@orpc/contract' 2 | import { z } from 'zod' 3 | 4 | export const sse = oc 5 | .route({ 6 | method: 'GET', 7 | path: '/sse', 8 | tags: ['SSE'], 9 | summary: 'Server-Sent Events', 10 | }) 11 | .output(eventIterator(z.object({ time: z.date() }))) 12 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import { createORPCClient } from '@orpc/client' 2 | import { RPCLink } from '@orpc/client/fetch' 3 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 4 | import type { ContractRouterClient } from '@orpc/contract' 5 | import type { contract } from '../contract' 6 | 7 | const rpcLink = new RPCLink({ 8 | url: 'http://localhost:3000/rpc', 9 | headers: () => ({ 10 | Authorization: 'Bearer default-token', 11 | }), 12 | }) 13 | 14 | export const orpcClient: ContractRouterClient = createORPCClient(rpcLink) 15 | 16 | export const orpc = createTanstackQueryUtils(orpcClient) 17 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | import type { UserSchema } from './schemas/user' 3 | import { implement, ORPCError } from '@orpc/server' 4 | import { dbProviderMiddleware } from './middlewares/db' 5 | import { contract } from './contract' 6 | 7 | export interface ORPCContext { 8 | user?: z.infer 9 | } 10 | 11 | export const pub = implement(contract) 12 | .$context() 13 | .use(dbProviderMiddleware) 14 | 15 | export const authed = pub.use(({ context, next }) => { 16 | if (!context.user) { 17 | throw new ORPCError('UNAUTHORIZED') 18 | } 19 | 20 | return next({ 21 | context: { 22 | user: context.user, 23 | }, 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { orpcClient as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/router/auth.ts: -------------------------------------------------------------------------------- 1 | import { authed, pub } from '../orpc' 2 | 3 | export const signup = pub.auth.signup 4 | .handler(async ({ input, context }) => { 5 | return { 6 | id: '28aa6286-48e9-4f23-adea-3486c86acd55', 7 | email: input.email, 8 | name: input.name, 9 | } 10 | }) 11 | 12 | export const signin = pub.auth.signin 13 | .handler(async ({ input, context }) => { 14 | return { token: 'token' } 15 | }) 16 | 17 | export const me = authed.auth.me 18 | .handler(async ({ input, context }) => { 19 | return context.user 20 | }) 21 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { pub } from '../orpc' 2 | import { me, signin, signup } from './auth' 3 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 4 | import { sse } from './sse' 5 | 6 | export const router = pub.router({ 7 | auth: { 8 | signup, 9 | signin, 10 | me, 11 | }, 12 | 13 | planet: { 14 | list: listPlanets, 15 | create: createPlanet, 16 | find: findPlanet, 17 | update: updatePlanet, 18 | }, 19 | 20 | sse, 21 | }) 22 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { pub } from '../orpc' 2 | 3 | const MAX_EVENTS = 5 4 | 5 | export const sse = pub.sse 6 | .handler(async function* () { 7 | let count = 0 8 | 9 | while (count < MAX_EVENTS) { 10 | count++ 11 | yield { time: new Date() } 12 | await new Promise(resolve => setTimeout(resolve, 1000)) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /playgrounds/contract-first/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/contract-first/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "jsx": "react-jsx", 6 | "lib": ["ES2022"], 7 | "moduleDetection": "force", 8 | "useDefineForClassFields": true, 9 | "module": "ES2022", 10 | 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "types": ["node"], 14 | 15 | "strict": true, 16 | "noImplicitOverride": true, 17 | "noEmit": true, 18 | "outDir": "${configDir}/dist", 19 | "noUncheckedSideEffectImports": true, 20 | "esModuleInterop": true, 21 | "isolatedModules": true, 22 | "skipLibCheck": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/electron/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | .DS_Store 5 | .eslintcache 6 | *.log* 7 | -------------------------------------------------------------------------------- /playgrounds/electron/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/electron/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Electron](https://www.electronjs.org). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | ## Sponsors 14 | 15 |

16 | 17 | 18 | 19 |

20 | -------------------------------------------------------------------------------- /playgrounds/electron/electron.vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | import { defineConfig, externalizeDepsPlugin } from 'electron-vite' 3 | import react from '@vitejs/plugin-react' 4 | 5 | export default defineConfig({ 6 | main: { 7 | plugins: [externalizeDepsPlugin()], 8 | }, 9 | preload: { 10 | plugins: [externalizeDepsPlugin()], 11 | }, 12 | renderer: { 13 | resolve: { 14 | alias: { 15 | '@renderer': resolve('src/renderer/src'), 16 | }, 17 | }, 18 | plugins: [react()], 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /playgrounds/electron/src/main/orpc.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { dbProviderMiddleware } from './middlewares/db' 3 | import { requiredAuthMiddleware } from './middlewares/auth' 4 | 5 | export const pub = os 6 | .use(dbProviderMiddleware) 7 | 8 | export const authed = pub 9 | .use(requiredAuthMiddleware) 10 | -------------------------------------------------------------------------------- /playgrounds/electron/src/main/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/electron/src/main/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/electron/src/main/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/electron/src/preload/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { ElectronAPI } from '@electron-toolkit/preload' 2 | 3 | declare global { 4 | interface Window { 5 | electron: ElectronAPI 6 | api: unknown 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /playgrounds/electron/src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electron x oRPC 6 | 7 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /playgrounds/electron/src/renderer/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { CreatePlanetMutationForm } from './components/orpc-mutation' 2 | import { ListPlanetsQuery } from './components/orpc-query' 3 | 4 | function App(): React.JSX.Element { 5 | return ( 6 |
7 |

ORPC Playground

8 |
9 | 10 |
11 | 12 |
13 | ) 14 | } 15 | 16 | export default App 17 | -------------------------------------------------------------------------------- /playgrounds/electron/src/renderer/src/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { router } from '../../../main/router' 2 | import type { RouterClient } from '@orpc/server' 3 | import { createORPCClient } from '@orpc/client' 4 | import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port' 5 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 6 | 7 | const { port1: clientPort, port2: serverPort } = new MessageChannel() 8 | 9 | window.postMessage('start-orpc-client', '*', [serverPort]) 10 | 11 | const link = new RPCLink({ 12 | port: clientPort, 13 | }) 14 | 15 | clientPort.start() 16 | 17 | export const client: RouterClient = createORPCClient(link) 18 | 19 | export const orpc = createTanstackQueryUtils(client) 20 | -------------------------------------------------------------------------------- /playgrounds/electron/src/renderer/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import App from './App' 4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query' 5 | 6 | const queryClient = new QueryClient() 7 | 8 | createRoot(document.getElementById('root')!).render( 9 | 10 | 11 | 12 | 13 | , 14 | ) 15 | -------------------------------------------------------------------------------- /playgrounds/electron/src/renderer/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/electron/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }], 3 | "files": [] 4 | } 5 | -------------------------------------------------------------------------------- /playgrounds/electron/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@electron-toolkit/tsconfig/tsconfig.node.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "moduleResolution": "bundler", 6 | "types": ["electron-vite/node"], 7 | 8 | "noImplicitReturns": false, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "emitDeclarationOnly": true, 12 | "outDir": "${configDir}/dist" 13 | }, 14 | "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /playgrounds/electron/tsconfig.web.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@electron-toolkit/tsconfig/tsconfig.web.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "jsx": "react-jsx", 6 | "baseUrl": ".", 7 | "moduleResolution": "bundler", 8 | "paths": { 9 | "@renderer/*": [ 10 | "src/renderer/src/*" 11 | ] 12 | }, 13 | "types": ["vite/client"], 14 | 15 | "noImplicitReturns": false, 16 | "noUnusedLocals": false, 17 | "noUnusedParameters": false, 18 | "emitDeclarationOnly": true, 19 | "outDir": "${configDir}/dist" 20 | }, 21 | "references": [{ "path": "./tsconfig.node.json" }], 22 | "include": [ 23 | "src/renderer/src/env.d.ts", 24 | "src/renderer/src/**/*", 25 | "src/renderer/src/**/*.tsx", 26 | "src/preload/*.d.ts" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /playgrounds/nest/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/nest/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [NestJS](https://nestjs.com). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run start:dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) to see the Scalar API Client. 14 | 15 | ## Sponsors 16 | 17 |

18 | 19 | 20 | 21 |

22 | -------------------------------------------------------------------------------- /playgrounds/nest/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true, 7 | "builder": "webpack" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /playgrounds/nest/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common' 2 | import { AuthController } from './auth/auth.controller' 3 | import { PlanetController } from './planet/planet.controller' 4 | import { OtherController } from './other/other.controller' 5 | import { PlanetService } from './planet/planet.service' 6 | import { ReferenceController } from './reference/reference.controller' 7 | import { ReferenceService } from './reference/reference.service' 8 | 9 | @Module({ 10 | imports: [], 11 | controllers: [AuthController, PlanetController, ReferenceController, OtherController], 12 | providers: [PlanetService, ReferenceService], 13 | }) 14 | export class AppModule {} 15 | -------------------------------------------------------------------------------- /playgrounds/nest/src/contract/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const contract = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/nest/src/contract/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, oc } from '@orpc/contract' 2 | import { z } from 'zod' 3 | 4 | export const sse = oc 5 | .route({ 6 | method: 'GET', 7 | path: '/sse', 8 | tags: ['SSE'], 9 | summary: 'Server-Sent Events', 10 | }) 11 | .output(eventIterator(z.object({ time: z.date() }))) 12 | -------------------------------------------------------------------------------- /playgrounds/nest/src/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import { createORPCClient } from '@orpc/client' 2 | import { OpenAPILink } from '@orpc/openapi-client/fetch' 3 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 4 | import type { ContractRouterClient } from '@orpc/contract' 5 | import { contract } from '../contract' 6 | import { JsonifiedClient } from '@orpc/openapi-client' 7 | 8 | const link = new OpenAPILink(contract, { 9 | url: 'http://localhost:3000', 10 | headers: () => ({ 11 | Authorization: 'Bearer default-token', 12 | }), 13 | }) 14 | 15 | export const client: JsonifiedClient> = createORPCClient(link) 16 | 17 | export const orpc = createTanstackQueryUtils(client) 18 | -------------------------------------------------------------------------------- /playgrounds/nest/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core' 2 | import { AppModule } from './app.module' 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule, { 6 | bodyParser: false, 7 | }) 8 | 9 | await app.listen(process.env.PORT ?? 3000) 10 | } 11 | bootstrap() 12 | -------------------------------------------------------------------------------- /playgrounds/nest/src/other/other.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common' 2 | import { Implement, implement } from '@orpc/nest' 3 | import { contract } from 'src/contract' 4 | 5 | const MAX_EVENTS = 5 6 | 7 | @Controller() 8 | export class OtherController { 9 | constructor() {} 10 | 11 | @Implement(contract.sse) 12 | list() { 13 | return implement(contract.sse).handler(async function* () { 14 | let count = 0 15 | 16 | while (count < MAX_EVENTS) { 17 | count++ 18 | yield { time: new Date() } 19 | await new Promise(resolve => setTimeout(resolve, 1000)) 20 | } 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /playgrounds/nest/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/nest/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/nest/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /playgrounds/nest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "ES2022", 5 | "lib": ["ES2022"], 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "baseUrl": "./", 9 | "module": "ES2022", 10 | "moduleResolution": "bundler", 11 | "types": ["node"], 12 | "strict": true, 13 | "strictBindCallApply": false, 14 | "strictNullChecks": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "noImplicitAny": false, 17 | "declaration": true, 18 | "outDir": "./dist", 19 | "removeComments": true, 20 | "sourceMap": true, 21 | "allowSyntheticDefaultImports": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "skipLibCheck": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /playgrounds/next/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for committing if needed) 33 | .env* 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | pnpm-lock.yaml 42 | package-lock.json 43 | yarn.lock -------------------------------------------------------------------------------- /playgrounds/next/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/next/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Next.js](https://nextjs.org). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/next/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next' 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | eslint: { 6 | ignoreDuringBuilds: true, 7 | }, 8 | typescript: { 9 | ignoreBuildErrors: true, 10 | }, 11 | experimental: { 12 | authInterrupts: true, 13 | }, 14 | } 15 | 16 | export default nextConfig 17 | -------------------------------------------------------------------------------- /playgrounds/next/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playgrounds/next/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playgrounds/next/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playgrounds/next/src/app/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | export default function ErrorPage({ error }: { error: Error }) { 4 | return ( 5 |
6 |

7 | Error: 8 | {error.message} 9 |

10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /playgrounds/next/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/next/src/app/favicon.ico -------------------------------------------------------------------------------- /playgrounds/next/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '../lib/orpc.server' 2 | 3 | import type { Metadata } from 'next' 4 | import { Providers } from './providers' 5 | 6 | export const metadata: Metadata = { 7 | title: 'ORPC Playground', 8 | description: 'End-to-end typesafe APIs builder, Developer-first simplicity', 9 | } 10 | 11 | export default function RootLayout({ 12 | children, 13 | }: Readonly<{ 14 | children: React.ReactNode 15 | }>) { 16 | return ( 17 | 18 | 19 | {children} 20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /playgrounds/next/src/app/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return
Loading...
3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/next/src/app/orpc-server-action.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useServerAction } from '@orpc/react/hooks' 4 | import { getting } from './actions' 5 | import { onSuccess } from '@orpc/client' 6 | import { getIssueMessage, parseFormData } from '@orpc/react' 7 | 8 | export function OrpcServerAction() { 9 | const state = useServerAction(getting, { 10 | interceptors: [ 11 | onSuccess((message) => { 12 | alert(message) 13 | }), 14 | ], 15 | }) 16 | 17 | return ( 18 |
{ state.execute(parseFormData(form)) }}> 19 | 20 |

{getIssueMessage(state.error, 'name')}

21 | 22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/next/src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query' 4 | import { useState } from 'react' 5 | 6 | export function Providers(props: { children: React.ReactNode }) { 7 | const [queryClient] = useState(() => new QueryClient()) 8 | 9 | return ( 10 | 11 | {props.children} 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /playgrounds/next/src/lib/orpc.server.ts: -------------------------------------------------------------------------------- 1 | 'server only' 2 | 3 | import { router } from '@/router' 4 | import { createRouterClient } from '@orpc/server' 5 | import { headers } from 'next/headers' 6 | 7 | /** 8 | * This is part of the Optimize SSR setup. 9 | * 10 | * @see {@link https://orpc.unnoq.com/docs/adapters/next#optimize-ssr} 11 | */ 12 | globalThis.$client = createRouterClient(router, { 13 | /** 14 | * Provide initial context if needed. 15 | * 16 | * Because this client instance is shared across all requests, 17 | * only include context that's safe to reuse globally. 18 | * For per-request context, use middleware context or pass a function as the initial context. 19 | */ 20 | context: async () => ({ 21 | headers: await headers(), 22 | }), 23 | }) 24 | -------------------------------------------------------------------------------- /playgrounds/next/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { dbProviderMiddleware } from './middlewares/db' 3 | import { requiredAuthMiddleware } from './middlewares/auth' 4 | 5 | export const pub = os 6 | .use(dbProviderMiddleware) 7 | 8 | export const authed = pub 9 | .use(requiredAuthMiddleware) 10 | -------------------------------------------------------------------------------- /playgrounds/next/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from '@/lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/next/src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { File } from 'node:buffer' 2 | 3 | /** 4 | * This file aims to polyfill missing APIs in Node.js 18 that oRPC depends on. 5 | * 6 | * Since Stackblitz runs on Node.js 18, these polyfills ensure oRPC works in that environment. 7 | * If you're running oRPC locally, please use Node.js 20 or later for full compatibility. 8 | */ 9 | 10 | /** 11 | * Note: Stackblitz provides an emulated Node.js environment with inherent limitations. 12 | * If you encounter issues, please test on a local setup with Node.js 20 or later before reporting them. 13 | */ 14 | 15 | /** 16 | * The `oz.file()` schema depends on the `File` API. 17 | * If you're not using `oz.file()`, you can safely remove this polyfill. 18 | */ 19 | if (typeof globalThis.File === 'undefined') { 20 | globalThis.File = File as any 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/next/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/next/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/next/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "ES2017", 5 | "jsx": "preserve", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "module": "esnext", 8 | "moduleResolution": "bundler", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | }, 12 | "resolveJsonModule": true, 13 | "allowJs": true, 14 | "strict": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "isolatedModules": true, 18 | "skipLibCheck": true, 19 | "plugins": [ 20 | { 21 | "name": "next" 22 | } 23 | ] 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /playgrounds/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | 26 | pnpm-lock.yaml 27 | package-lock.json 28 | yarn.lock -------------------------------------------------------------------------------- /playgrounds/nuxt/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/nuxt/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Nuxt.js](https://nuxt.com). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /playgrounds/nuxt/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { RouterClient } from '@orpc/server' 2 | import type { router } from '~/server/router' 3 | import { createORPCClient } from '@orpc/client' 4 | import { RPCLink } from '@orpc/client/fetch' 5 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 6 | 7 | const rpcLink = new RPCLink({ 8 | url: `${typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000'}/rpc`, 9 | headers: () => ({ 10 | Authorization: 'Bearer default-token', 11 | }), 12 | }) 13 | 14 | export const client: RouterClient = createORPCClient(rpcLink) 15 | 16 | export const orpc = createTanstackQueryUtils(client) 17 | -------------------------------------------------------------------------------- /playgrounds/nuxt/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | compatibilityDate: '2024-11-01', 4 | devtools: { enabled: true }, 5 | }) 6 | -------------------------------------------------------------------------------- /playgrounds/nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@orpc/nuxt-playground", 3 | "type": "module", 4 | "private": true, 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "devDependencies": { 13 | "@orpc/client": "next", 14 | "@orpc/openapi": "next", 15 | "@orpc/server": "next", 16 | "@orpc/tanstack-query": "next", 17 | "@orpc/zod": "next", 18 | "@tanstack/vue-query": "^5.77.2", 19 | "nuxt": "^3.16.2", 20 | "vue": "latest", 21 | "vue-router": "latest", 22 | "zod": "^3.25.11" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /playgrounds/nuxt/public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | import type { UserSchema } from './schemas/user' 3 | import { ORPCError, os } from '@orpc/server' 4 | import { dbProviderMiddleware } from './middlewares/db' 5 | 6 | export interface ORPCContext { 7 | user?: z.infer 8 | } 9 | 10 | export const pub = os 11 | .$context() 12 | .use(dbProviderMiddleware) 13 | 14 | export const authed = pub.use(({ context, next }) => { 15 | if (!context.user) { 16 | throw new ORPCError('UNAUTHORIZED') 17 | } 18 | 19 | return next({ 20 | context: { 21 | user: context.user, 22 | }, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from '@/lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/routes/api/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './[...]' 2 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/routes/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './[...]' 2 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/nuxt/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /playgrounds/solid-start/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .solid 3 | .output 4 | .vercel 5 | .netlify 6 | .vinxi 7 | app.config.timestamp_*.js 8 | 9 | # Environment 10 | .env 11 | .env*.local 12 | 13 | # dependencies 14 | /node_modules 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | *.launch 21 | .settings/ 22 | 23 | # Temp 24 | gitignore 25 | 26 | # System Files 27 | .DS_Store 28 | Thumbs.db 29 | -------------------------------------------------------------------------------- /playgrounds/solid-start/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/solid-start/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Solid Start](https://start.solidjs.com/). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/solid-start/app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@solidjs/start/config' 2 | import topLevelAwait from 'vite-plugin-top-level-await' 3 | 4 | export default defineConfig({ 5 | vite: { 6 | plugins: [ 7 | topLevelAwait(), 8 | ], 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/solid-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@orpc/solid-start-playground", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vinxi dev", 6 | "build": "vinxi build", 7 | "start": "vinxi start", 8 | "type:check": "tsc --noEmit" 9 | }, 10 | "devDependencies": { 11 | "@orpc/client": "next", 12 | "@orpc/openapi": "next", 13 | "@orpc/server": "next", 14 | "@orpc/tanstack-query": "next", 15 | "@orpc/zod": "next", 16 | "@solidjs/router": "^0.15.3", 17 | "@solidjs/start": "^1.1.0", 18 | "@tanstack/solid-query": "^5.77.2", 19 | "solid-js": "^1.9.5", 20 | "vinxi": "^0.5.6", 21 | "vite-plugin-top-level-await": "^1.5.0", 22 | "zod": "^3.25.11" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/solid-start/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/solid-start/public/favicon.ico -------------------------------------------------------------------------------- /playgrounds/solid-start/src/entry-client.tsx: -------------------------------------------------------------------------------- 1 | // @refresh reload 2 | import { mount, StartClient } from '@solidjs/start/client' 3 | 4 | mount(() => , document.getElementById('app')!) 5 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/entry-server.tsx: -------------------------------------------------------------------------------- 1 | // @refresh reload 2 | import { createHandler, StartServer } from '@solidjs/start/server' 3 | 4 | export default createHandler(() => ( 5 | ( 7 | 8 | 9 | 10 | 11 | 12 | {assets} 13 | 14 | 15 |
{children}
16 | {scripts} 17 | 18 | 19 | )} 20 | /> 21 | )) 22 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | import type { UserSchema } from './schemas/user' 3 | import { ORPCError, os } from '@orpc/server' 4 | import { dbProviderMiddleware } from './middlewares/db' 5 | 6 | export interface ORPCContext { 7 | user?: z.infer 8 | } 9 | 10 | export const pub = os 11 | .$context() 12 | .use(dbProviderMiddleware) 13 | 14 | export const authed = pub.use(({ context, next }) => { 15 | if (!context.user) { 16 | throw new ORPCError('UNAUTHORIZED') 17 | } 18 | 19 | return next({ 20 | context: { 21 | user: context.user, 22 | }, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from '~/lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { File } from 'node:buffer' 2 | 3 | /** 4 | * This file aims to polyfill missing APIs in Node.js 18 that oRPC depends on. 5 | * 6 | * Since Stackblitz runs on Node.js 18, these polyfills ensure oRPC works in that environment. 7 | * If you're running oRPC locally, please use Node.js 20 or later for full compatibility. 8 | */ 9 | 10 | /** 11 | * Note: Stackblitz provides an emulated Node.js environment with inherent limitations. 12 | * If you encounter issues, please test on a local setup with Node.js 20 or later before reporting them. 13 | */ 14 | 15 | /** 16 | * The `oz.file()` schema depends on the `File` API. 17 | * If you're not using `oz.file()`, you can safely remove this polyfill. 18 | */ 19 | if (typeof globalThis.File === 'undefined') { 20 | globalThis.File = File as any 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/routes/api/index.ts: -------------------------------------------------------------------------------- 1 | import { handle } from './[...rest]' 2 | 3 | export const HEAD = handle 4 | export const GET = handle 5 | export const POST = handle 6 | export const PUT = handle 7 | export const PATCH = handle 8 | export const DELETE = handle 9 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { CreatePlanetMutationForm } from './orpc-mutation' 2 | import { ListPlanetsQuery } from './orpc-query' 3 | 4 | export default function Home() { 5 | return ( 6 |
7 |

ORPC Playground

8 |

9 | You can visit the 10 | {' '} 11 | Scalar API Reference 12 | {' '} 13 | page. 14 |

15 |
16 | 17 |
18 | 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/routes/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './[...rest]' 2 | -------------------------------------------------------------------------------- /playgrounds/solid-start/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/solid-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "jsxImportSource": "solid-js", 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "paths": { 9 | "~/*": ["./src/*"] 10 | }, 11 | "types": ["vinxi/types/client"], 12 | "allowJs": true, 13 | "strict": true, 14 | "noEmit": true, 15 | "allowSyntheticDefaultImports": true, 16 | "esModuleInterop": true, 17 | "isolatedModules": true, 18 | "skipLibCheck": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | .netlify 7 | .wrangler 8 | /.svelte-kit 9 | /build 10 | 11 | # OS 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # Env 16 | .env 17 | .env.* 18 | !.env.example 19 | !.env.test 20 | 21 | # Vite 22 | vite.config.js.timestamp-* 23 | vite.config.ts.timestamp-* 24 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [SvelteKit](https://kit.svelte.dev/). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api](http://localhost:3000/api) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {} 14 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/lib/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { RouterClient } from '@orpc/server' 2 | import type { router } from '../router' 3 | import { createORPCClient } from '@orpc/client' 4 | import { RPCLink } from '@orpc/client/fetch' 5 | import { createTanstackQueryUtils } from '@orpc/tanstack-query' 6 | 7 | const rpcLink = new RPCLink({ 8 | url: `${typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000'}/rpc`, 9 | headers: () => ({ 10 | Authorization: 'Bearer default-token', 11 | }), 12 | }) 13 | 14 | export const client: RouterClient = createORPCClient(rpcLink) 15 | 16 | export const orpc = createTanstackQueryUtils(client) 17 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | import type { UserSchema } from './schemas/user' 3 | import { ORPCError, os } from '@orpc/server' 4 | import { dbProviderMiddleware } from './middlewares/db' 5 | 6 | export interface ORPCContext { 7 | user?: z.infer 8 | } 9 | 10 | export const pub = os 11 | .$context() 12 | .use(dbProviderMiddleware) 13 | 14 | export const authed = pub.use(({ context, next }) => { 15 | if (!context.user) { 16 | throw new ORPCError('UNAUTHORIZED') 17 | } 18 | 19 | return next({ 20 | context: { 21 | user: context.user, 22 | }, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from './lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 |
11 |

ORPC Playground

12 |

13 | You can visit the 14 | {' '} 15 | Scalar API Reference 16 | {' '} 17 | page. 18 |

19 |
20 | 21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unnoq/orpc/34e2f0a7e695c4cba67104540aeafe57e2ef9907/playgrounds/svelte-kit/static/favicon.png -------------------------------------------------------------------------------- /playgrounds/svelte-kit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto' 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://svelte.dev/docs/kit/integrations 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | 10 | kit: { 11 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 13 | // See https://svelte.dev/docs/kit/adapters for more information about adapters. 14 | adapter: adapter(), 15 | }, 16 | } 17 | 18 | export default config 19 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "bundler", 5 | "resolveJsonModule": true, 6 | "allowJs": true, 7 | "checkJs": true, 8 | "strict": true, 9 | "sourceMap": true, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "skipLibCheck": true 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 18 | // from the referenced tsconfig.json - TypeScript does not merge them in 19 | } 20 | -------------------------------------------------------------------------------- /playgrounds/svelte-kit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite' 2 | import { defineConfig } from 'vite' 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | ssr: { 7 | // Tell Vite not to externalize this package, so it will be processed by Vite. 8 | noExternal: [/^@orpc\/.+/], 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | yarn.lock 4 | 5 | .DS_Store 6 | .cache 7 | .env 8 | .vercel 9 | .output 10 | .vinxi 11 | 12 | /build/ 13 | /api/ 14 | /server/build 15 | /public/build 16 | .vinxi 17 | # Sentry Config File 18 | .env.sentry-build-plugin 19 | /test-results/ 20 | /playwright-report/ 21 | /blob-report/ 22 | /playwright/.cache/ 23 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/README.md: -------------------------------------------------------------------------------- 1 | # ORPC Playground 2 | 3 | This is a playground for [oRPC](https://orpc.unnoq.com) and [Tanstack Start](https://tanstack.com/start/latest). 4 | 5 | ## Getting Started 6 | 7 | First, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | Open [http://localhost:3000/api/docs](http://localhost:3000/api/docs) to see the Scalar API Client. 15 | 16 | ## Sponsors 17 | 18 |

19 | 20 | 21 | 22 |

23 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@tanstack/react-start/config' 2 | import tsConfigPaths from 'vite-tsconfig-paths' 3 | 4 | export default defineConfig({ 5 | tsr: { 6 | appDirectory: 'src', 7 | }, 8 | vite: { 9 | plugins: [ 10 | tsConfigPaths({ 11 | projects: ['./tsconfig.json'], 12 | }), 13 | ], 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/api.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createStartAPIHandler, 3 | defaultAPIFileRouteHandler, 4 | } from '@tanstack/react-start/api' 5 | 6 | export default createStartAPIHandler((ctx) => { 7 | if (import.meta.env.DEV) { 8 | console.log(ctx.request.url) 9 | } 10 | 11 | return defaultAPIFileRouteHandler(ctx) 12 | }) 13 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/client.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | import { hydrateRoot } from 'react-dom/client' 3 | import { StartClient } from '@tanstack/react-start' 4 | import { createRouter } from './router' 5 | 6 | const router = createRouter() 7 | 8 | hydrateRoot(document, ) 9 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/orpc.ts: -------------------------------------------------------------------------------- 1 | import { os } from '@orpc/server' 2 | import { dbProviderMiddleware } from './middlewares/db' 3 | import { requiredAuthMiddleware } from './middlewares/auth' 4 | 5 | export const pub = os 6 | .use(dbProviderMiddleware) 7 | 8 | export const authed = pub 9 | .use(requiredAuthMiddleware) 10 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/playground-client.ts: -------------------------------------------------------------------------------- 1 | import { client as orpc } from '~/lib/orpc' 2 | import { safe } from '@orpc/client' 3 | 4 | const token = await orpc.auth.signin({ 5 | email: 'john@doe.com', 6 | password: '123456', 7 | }) 8 | 9 | const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: 'Earth', description: 'The planet Earth' })) 10 | 11 | if (error) { 12 | if (isDefined) { 13 | const id = error.data.id 14 | // ^ type-safe 15 | } 16 | 17 | console.log('ERROR', error) 18 | } 19 | else { 20 | console.log('PLANET', planet) 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { me, signin, signup } from './auth' 2 | import { createPlanet, findPlanet, listPlanets, updatePlanet } from './planet' 3 | import { sse } from './sse' 4 | 5 | export const router = { 6 | auth: { 7 | signup, 8 | signin, 9 | me, 10 | }, 11 | 12 | planet: { 13 | list: listPlanets, 14 | create: createPlanet, 15 | find: findPlanet, 16 | update: updatePlanet, 17 | }, 18 | 19 | sse, 20 | } 21 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/router/sse.ts: -------------------------------------------------------------------------------- 1 | import { eventIterator, os } from '@orpc/server' 2 | import { z } from 'zod' 3 | 4 | const MAX_EVENTS = 5 5 | 6 | export const sse = os 7 | .route({ 8 | method: 'GET', 9 | path: '/sse', 10 | tags: ['SSE'], 11 | summary: 'Server-Sent Events', 12 | }) 13 | .output(eventIterator(z.object({ time: z.date() }))) 14 | .handler(async function* () { 15 | let count = 0 16 | 17 | while (count < MAX_EVENTS) { 18 | count++ 19 | yield { time: new Date() } 20 | await new Promise(resolve => setTimeout(resolve, 1000)) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/routes/api/index.ts: -------------------------------------------------------------------------------- 1 | import { createAPIFileRoute } from '@tanstack/react-start/api' 2 | import { APIRoute as BaseAPIRoute } from './$' 3 | 4 | export const APIRoute = createAPIFileRoute('/api')(BaseAPIRoute.methods) 5 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/routes/api/rpc.$.ts: -------------------------------------------------------------------------------- 1 | import '~/polyfill' 2 | 3 | import { RPCHandler } from '@orpc/server/fetch' 4 | import { createAPIFileRoute } from '@tanstack/react-start/api' 5 | import { router } from '~/router/index' 6 | 7 | const handler = new RPCHandler(router) 8 | 9 | async function handle({ request }: { request: Request }) { 10 | const { response } = await handler.handle(request, { 11 | prefix: '/api/rpc', 12 | context: {}, 13 | }) 14 | 15 | return response ?? new Response('Not Found', { status: 404 }) 16 | } 17 | 18 | export const APIRoute = createAPIFileRoute('/api/rpc/$')({ 19 | HEAD: handle, 20 | GET: handle, 21 | POST: handle, 22 | PUT: handle, 23 | PATCH: handle, 24 | DELETE: handle, 25 | }) 26 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/routes/api/rpc.ts: -------------------------------------------------------------------------------- 1 | import { createAPIFileRoute } from '@tanstack/react-start/api' 2 | import { APIRoute as BaseAPIRoute } from './rpc.$' 3 | 4 | export const APIRoute = createAPIFileRoute('/api/rpc')(BaseAPIRoute.methods) 5 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from '@tanstack/react-router' 2 | import { CreatePlanetMutationForm } from '~/components/orpc-mutation' 3 | import { ListPlanetsQuery } from '~/components/orpc-query' 4 | 5 | export const Route = createFileRoute('/')({ 6 | component: RouteComponent, 7 | }) 8 | 9 | function RouteComponent() { 10 | return ( 11 |
12 |

ORPC Playground

13 | You can visit the 14 | {' '} 15 | Redirect to Scalar API Reference 16 | {' '} 17 | page. 18 |
19 | 20 |
21 | 22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/schemas/auth.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CredentialSchema = z.object({ 4 | email: z.string().email(), 5 | password: z.string(), 6 | }) 7 | 8 | export const TokenSchema = z.object({ 9 | token: z.string(), 10 | }) 11 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/src/ssr.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { 4 | createStartHandler, 5 | defaultStreamHandler, 6 | } from '@tanstack/react-start/server' 7 | import { getRouterManifest } from '@tanstack/react-start/router-manifest' 8 | 9 | import { createRouter } from './router' 10 | 11 | export default createStartHandler({ 12 | createRouter, 13 | getRouterManifest, 14 | })((ctx) => { 15 | if (import.meta.env.DEV) { 16 | console.log(ctx.request.url) 17 | } 18 | 19 | return defaultStreamHandler(ctx) 20 | }) 21 | -------------------------------------------------------------------------------- /playgrounds/tanstack-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "jsx": "react-jsx", 5 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 6 | "baseUrl": ".", 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "paths": { 10 | "~/*": ["./src/*"] 11 | }, 12 | "resolveJsonModule": true, 13 | "allowJs": true, 14 | "strict": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "isolatedModules": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": ["**/*.ts", "**/*.tsx"] 22 | } 23 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/*' 3 | - 'packages/*' 4 | - 'playgrounds/*' 5 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "jsx": "react-jsx", 6 | "lib": ["ES2022"], 7 | "moduleDetection": "force", 8 | "useDefineForClassFields": true, 9 | "module": "ES2022", 10 | 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "types": [], 14 | 15 | "strict": true, 16 | "noImplicitOverride": true, 17 | "noUncheckedIndexedAccess": true, 18 | "noEmit": true, 19 | "outDir": "${configDir}/dist", 20 | "noUncheckedSideEffectImports": true, 21 | "esModuleInterop": true, 22 | "isolatedModules": true, 23 | "skipLibCheck": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.base.json", 4 | "compilerOptions": { 5 | "composite": true, 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 | "declaration": true, 8 | "declarationMap": true, 9 | "emitDeclarationOnly": true, 10 | "noEmit": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vitest.jsdom.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/dom' 2 | --------------------------------------------------------------------------------