>()
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/playgrounds/cloudflare-worker/worker/routers/message-v2.ts:
--------------------------------------------------------------------------------
1 | import { pub } from '../orpc'
2 | import * as z from 'zod'
3 |
4 | export const onMessageV2 = pub.handler(({ context, lastEventId, signal }) => {
5 | return context.messagePublisher.subscribe('some-room', { signal, lastEventId })
6 | })
7 |
8 | export const sendMessageV2 = pub
9 | .input(z.object({ message: z.string() }))
10 | .handler(async ({ context, input }) => {
11 | await context.messagePublisher.publish('some-room', input)
12 | })
13 |
--------------------------------------------------------------------------------
/playgrounds/svelte-kit/src/routes/orpc-stream.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
oRPC and Tanstack Query | Event Iterator example
15 |
16 | {JSON.stringify(streamed.data, null, 2)}
17 |
18 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/nuxt/app/components/orpc-stream.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
oRPC and Tanstack Query | Event Iterator example
14 |
15 | {{ JSON.stringify(query.data.value, null, 2) }}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/playgrounds/contract-first/src/routers/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/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/tanstack-start/src/components/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query'
2 | import { orpc } from '~/lib/orpc'
3 |
4 | export function EventIteratorQueries() {
5 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }))
6 |
7 | return (
8 |
9 |
oRPC and Tanstack Query | Event Iterator example
10 |
11 | {JSON.stringify(streamed.data, null, 2)}
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/playgrounds/cloudflare-worker/src/components/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query'
2 | import { orpc } from '../lib/orpc'
3 |
4 | export function EventIteratorQueries() {
5 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }))
6 |
7 | return (
8 |
9 |
oRPC and Tanstack Query | Event Iterator example
10 |
11 | {JSON.stringify(streamed.data, null, 2)}
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/playgrounds/next/src/app/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { orpc } from '@/lib/orpc'
4 | import { useQuery } from '@tanstack/react-query'
5 |
6 | export function EventIteratorQueries() {
7 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }))
8 |
9 | return (
10 |
11 |
oRPC and Tanstack Query | Event Iterator example
12 |
13 | {JSON.stringify(streamed.data, null, 2)}
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/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 | "strict": true,
14 | "noEmit": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------
/playgrounds/browser-extension/entrypoints/popup/components/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query'
2 | import { orpc } from '../lib/orpc'
3 |
4 | export function EventIteratorQueries() {
5 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }))
6 |
7 | return (
8 |
9 |
oRPC and Tanstack Query | Event Iterator example
10 |
11 | {JSON.stringify(streamed.data, null, 2)}
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/playgrounds/electron/src/renderer/src/components/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { orpc } from '@renderer/lib/orpc'
2 | import { useQuery } from '@tanstack/react-query'
3 |
4 | export function EventIteratorQueries() {
5 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }))
6 |
7 | return (
8 |
9 |
oRPC and Tanstack Query | Event Iterator example
10 |
11 | {JSON.stringify(streamed.data, null, 2)}
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/playgrounds/solid-start/src/routes/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/solid-query'
2 | import { orpc } from '~/lib/orpc'
3 |
4 | export function EventIteratorQueries() {
5 | const streamed = useQuery(
6 | () => orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }),
7 | )
8 |
9 | return (
10 |
11 |
oRPC and Tanstack Query | Event Iterator example
12 |
13 | {JSON.stringify(streamed.data, null, 2)}
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/playgrounds/browser-extension/entrypoints/popup/App.tsx:
--------------------------------------------------------------------------------
1 | import { CreatePlanetMutationForm } from './components/orpc-mutation'
2 | import { ListPlanetsQuery } from './components/orpc-query'
3 | import { EventIteratorQueries } from './components/orpc-stream'
4 |
5 | export default function App() {
6 | return (
7 |
8 |
ORPC Playground
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/standard-server-node/src/signal.ts:
--------------------------------------------------------------------------------
1 | import type Stream from 'node:stream'
2 | import { AbortError } from '@orpc/shared'
3 |
4 | export function toAbortSignal(stream: Stream.Writable): AbortSignal {
5 | const controller = new AbortController()
6 |
7 | stream.once('error', error => controller.abort(error))
8 |
9 | stream.once('close', () => {
10 | if (!stream.writableFinished) {
11 | controller.abort(new AbortError('Writable stream closed before it finished writing'))
12 | }
13 | })
14 |
15 | return controller.signal
16 | }
17 |
--------------------------------------------------------------------------------
/playgrounds/cloudflare-worker/worker/orpc.ts:
--------------------------------------------------------------------------------
1 | import { os } from '@orpc/server'
2 | import { dbProviderMiddleware } from './middlewares/db'
3 | import { requiredAuthMiddleware } from './middlewares/auth'
4 | import type { Publisher } from '@orpc/experimental-publisher'
5 |
6 | type ORPCContext = {
7 | env: Env
8 | messagePublisher: Publisher>
9 | }
10 |
11 | export const pub = os
12 | .$context()
13 | .use(dbProviderMiddleware)
14 |
15 | export const authed = pub
16 | .use(requiredAuthMiddleware)
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/shared/src/buffer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Converts Request/Response/Blob/File/.. to a buffer (ArrayBuffer or Uint8Array).
3 | *
4 | * Prefers the newer `.bytes` method when available as it more efficient but not widely supported yet.
5 | */
6 | export function readAsBuffer(source: Pick): Promise {
7 | if (typeof source.bytes === 'function') {
8 | // eslint-disable-next-line ban/ban
9 | return source.bytes()
10 | }
11 |
12 | return (source as Pick).arrayBuffer()
13 | }
14 |
--------------------------------------------------------------------------------
/packages/standard-server-node/src/headers.ts:
--------------------------------------------------------------------------------
1 | import type { StandardHeaders } from '@orpc/standard-server'
2 | import type { OutgoingHttpHeaders } from 'node:http'
3 |
4 | export function toNodeHttpHeaders(headers: StandardHeaders): OutgoingHttpHeaders {
5 | const nodeHttpHeaders: OutgoingHttpHeaders = {}
6 |
7 | for (const [key, value] of Object.entries(headers)) {
8 | // Node.js does not allow headers to be undefined
9 | if (value !== undefined) {
10 | nodeHttpHeaders[key] = value
11 | }
12 | }
13 |
14 | return nodeHttpHeaders
15 | }
16 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/playgrounds/nest/README.md:
--------------------------------------------------------------------------------
1 | # ORPC Playground
2 |
3 | This is a playground for [oRPC](https://orpc.dev) 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/electron/src/renderer/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { CreatePlanetMutationForm } from './components/orpc-mutation'
2 | import { ListPlanetsQuery } from './components/orpc-query'
3 | import { EventIteratorQueries } from './components/orpc-stream'
4 |
5 | function App(): React.JSX.Element {
6 | return (
7 |
8 |
ORPC Playground
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default App
20 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/playgrounds/astro/src/components/orpc-stream.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query'
2 | import { orpc } from '../lib/orpc'
3 | import { queryClient } from '../shared/query'
4 |
5 | export function EventIteratorQueries() {
6 | const streamed = useQuery(orpc.sse.experimental_streamedOptions({ queryFnOptions: { maxChunks: 3 } }), queryClient)
7 |
8 | return (
9 |
10 |
oRPC and Tanstack Query | Event Iterator example
11 |
12 | {JSON.stringify(streamed.data, null, 2)}
13 |
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/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": "../interop" },
10 | { "path": "../shared" }
11 | ],
12 | "include": ["src"],
13 | "exclude": [
14 | "**/*.bench.*",
15 | "**/*.test.*",
16 | "**/*.test-d.ts",
17 | "**/__tests__/**",
18 | "**/__mocks__/**",
19 | "**/__snapshots__/**"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/packages/publisher-durable-object/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.lib.json",
3 | "compilerOptions": {
4 | "types": ["node", "@cloudflare/workers-types"]
5 | },
6 | "references": [
7 | { "path": "../client" },
8 | { "path": "../shared" },
9 | { "path": "../publisher" },
10 | { "path": "../standard-server" }
11 | ],
12 | "include": ["src"],
13 | "exclude": [
14 | "**/*.bench.*",
15 | "**/*.test.*",
16 | "**/*.test-d.ts",
17 | "**/__tests__/**",
18 | "**/__mocks__/**",
19 | "**/__snapshots__/**"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/playgrounds/bun-websocket-otel/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies (bun install)
2 | node_modules
3 |
4 | # output
5 | out
6 | dist
7 | *.tgz
8 |
9 | # code coverage
10 | coverage
11 | *.lcov
12 |
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 |
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 |
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 |
30 | # IntelliJ based IDEs
31 | .idea
32 |
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 |
--------------------------------------------------------------------------------
/playgrounds/astro/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { CreatePlanetMutationForm } from "../components/orpc-mutation";
3 | import { ListPlanetsQuery } from "../components/orpc-query";
4 | import { EventIteratorQueries } from "../components/orpc-stream";
5 | ---
6 |
7 |
8 |
ORPC Playground
9 | You can visit the
10 |
Scalar API Reference
11 | page.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/packages/shared/src/id.ts:
--------------------------------------------------------------------------------
1 | export class SequentialIdGenerator {
2 | private index = BigInt(1)
3 |
4 | generate(): string {
5 | const id = this.index.toString(36)
6 | this.index++
7 | return id
8 | }
9 | }
10 |
11 | /**
12 | * Compares two sequential IDs.
13 | * Returns:
14 | * - negative if `a` < `b`
15 | * - positive if `a` > `b`
16 | * - 0 if equal
17 | */
18 | export function compareSequentialIds(a: string, b: string): number {
19 | if (a.length !== b.length) {
20 | return a.length - b.length
21 | }
22 |
23 | return a < b ? -1 : a > b ? 1 : 0
24 | }
25 |
--------------------------------------------------------------------------------
/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/cloudflare-worker/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "jsx": "react-jsx",
5 | "lib": ["ES2022", "DOM", "DOM.Iterable"],
6 | "moduleDetection": "force",
7 | "module": "ESNext",
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "types": ["node", "./worker-configuration.d.ts", "vite/client"],
12 |
13 | /* Linting */
14 | "strict": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["src", "worker", "worker-configuration.d.ts"]
19 | }
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/packages/standard-server-node/src/headers.test.ts:
--------------------------------------------------------------------------------
1 | import { toNodeHttpHeaders } from './headers'
2 |
3 | describe('toNodeHttpHeaders', () => {
4 | it('filters out undefined values', () => {
5 | const headers = toNodeHttpHeaders({
6 | 'x-custom': 'value',
7 | 'x-undefined': undefined,
8 | 'set-cookie': ['cookie1=value1', 'cookie2=value2'],
9 | })
10 |
11 | expect(headers).toEqual({
12 | 'x-custom': 'value',
13 | 'set-cookie': ['cookie1=value1', 'cookie2=value2'],
14 | })
15 | expect(headers).not.toHaveProperty('x-undefined')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/playgrounds/browser-extension/entrypoints/popup/lib/orpc.ts:
--------------------------------------------------------------------------------
1 | import type { router } from '../.../../../background/routers'
2 | import type { RouterClient } from '@orpc/server'
3 | import { createORPCClient } from '@orpc/client'
4 | import { 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/contract-first/src/routers/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/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/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/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 |
--------------------------------------------------------------------------------
/packages/shared/src/value.test.ts:
--------------------------------------------------------------------------------
1 | import { fallback, 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 |
15 | it('fallback', () => {
16 | expect(fallback(42, 0)).toBe(42)
17 | expect(fallback(undefined, 0)).toBe(0)
18 | })
19 |
--------------------------------------------------------------------------------
/playgrounds/bun-websocket-otel/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/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/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 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | We take security very seriously. If you believe you have found a security vulnerability, please report it to us as described below.
6 |
7 | ### Before Reporting
8 |
9 | Please ensure you are running the latest version of the software. The vulnerability may have already been addressed in a recent update.
10 |
11 | ### How to Report
12 |
13 | To report a security vulnerability, please email us at contact@unnoq.com.
14 |
15 | We appreciate your efforts to disclose your findings responsibly.
16 |
--------------------------------------------------------------------------------
/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/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/standard-server-node/src/url.ts:
--------------------------------------------------------------------------------
1 | import type { NodeHttpRequest } from './types'
2 | import { guard } from '@orpc/shared'
3 |
4 | export function toStandardUrl(req: NodeHttpRequest): URL {
5 | const protocol = ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:')
6 |
7 | // fallback for malformed host
8 | const origin = guard(() => new URL(`${protocol}//${req.headers.host ?? 'localhost'}`).origin) ?? `${protocol}//localhost`
9 |
10 | const path = req.originalUrl ?? req.url ?? '/'
11 |
12 | return new URL(`${origin}${path.startsWith('/') ? '' : '/'}${path}`)
13 | }
14 |
--------------------------------------------------------------------------------
/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/astro/src/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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/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/nuxt/server/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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 |
--------------------------------------------------------------------------------
/packages/react-swr/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { isObject } from '@orpc/shared'
2 |
3 | export function isSubsetOf(subsetKey: unknown, fullKey: unknown): boolean {
4 | return subsetKey === fullKey
5 | ? true
6 | : typeof subsetKey !== typeof fullKey
7 | ? false
8 | : isObject(subsetKey) && isObject(fullKey)
9 | ? Object.keys(subsetKey).every(key => subsetKey[key] === undefined || isSubsetOf(subsetKey[key], fullKey[key]))
10 | : Array.isArray(subsetKey) && Array.isArray(fullKey)
11 | ? subsetKey.every((value, index) => isSubsetOf(value, fullKey[index]))
12 | : false
13 | }
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/playgrounds/electron/src/main/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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 |
--------------------------------------------------------------------------------
/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 |
14 | /**
15 | * Returns the value if it is defined, otherwise returns the fallback
16 | */
17 | export function fallback(value: T | undefined, fallback: T): T {
18 | return value === undefined ? fallback : value
19 | }
20 |
--------------------------------------------------------------------------------
/packages/zod/src/schemas/url.ts:
--------------------------------------------------------------------------------
1 | import type { ZodType, ZodTypeDef } from 'zod/v3'
2 | import type { CustomParams } from './base'
3 | import { custom } from 'zod/v3'
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 |
--------------------------------------------------------------------------------
/playgrounds/cloudflare-worker/worker/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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 |
--------------------------------------------------------------------------------
/packages/ratelimit/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface RatelimiterLimitResult {
2 | /**
3 | * Whether the request may pass(true) or exceeded the limit(false)
4 | */
5 | success: boolean
6 | /**
7 | * Maximum number of requests allowed within a window.
8 | */
9 | limit?: number
10 | /**
11 | * How many requests the user has left within the current window.
12 | */
13 | remaining?: number
14 | /**
15 | * Unix timestamp in milliseconds when the limits are reset.
16 | */
17 | reset?: number
18 | }
19 |
20 | export interface Ratelimiter {
21 | limit(key: string): Promise
22 | }
23 |
--------------------------------------------------------------------------------
/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 | * Prefer parsed body if it is available.
13 | */
14 | body?: unknown
15 | }
16 |
17 | export type NodeHttpResponse = ServerResponse | Http2ServerResponse
18 |
--------------------------------------------------------------------------------
/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/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/shared/src/buffer.test.ts:
--------------------------------------------------------------------------------
1 | import { readAsBuffer } from './buffer'
2 |
3 | it('readAsBuffer', async () => {
4 | const blob = new Blob(['test'], { type: 'text/plain' })
5 |
6 | expect(new TextDecoder().decode(await readAsBuffer(blob))).toBe('test')
7 | expect(new TextDecoder().decode(await readAsBuffer(new Proxy(blob, {
8 | get: (target, prop) => {
9 | if (prop === 'bytes') {
10 | return undefined
11 | }
12 | return Reflect.get(target, prop)
13 | },
14 | })))).toBe('test')
15 |
16 | expect(new TextDecoder().decode(await readAsBuffer((new Response(blob) as any)))).toBe('test')
17 | })
18 |
--------------------------------------------------------------------------------
/packages/zod/src/schemas/blob.ts:
--------------------------------------------------------------------------------
1 | import type { ZodType, ZodTypeDef } from 'zod/v3'
2 | import type { CustomParams } from './base'
3 | import { custom } from 'zod/v3'
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/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 |
--------------------------------------------------------------------------------
/playgrounds/browser-extension/entrypoints/background/routers/sse.ts:
--------------------------------------------------------------------------------
1 | import { eventIterator, os } from '@orpc/server'
2 | import * as 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 |
--------------------------------------------------------------------------------
/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/zod/src/schemas/regexp.ts:
--------------------------------------------------------------------------------
1 | import type { ZodType, ZodTypeDef } from 'zod/v3'
2 | import type { CustomParams } from './base'
3 | import { custom } from 'zod/v3'
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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/packages/durable-iterator/src/object.ts:
--------------------------------------------------------------------------------
1 | import type { NestedClient } from '@orpc/client'
2 | import type { AsyncIteratorClass } from '@orpc/shared'
3 |
4 | export interface DurableIteratorObjectDef {
5 | '~eventPayloadType'?: { type: T }
6 | }
7 |
8 | export interface DurableIteratorObject {
9 | '~orpc'?: DurableIteratorObjectDef
10 | }
11 |
12 | export type InferDurableIteratorObjectRPC<
13 | T extends DurableIteratorObject,
14 | > = Exclude<{
15 | [K in keyof T]: T[K] extends ((...args: any[]) => NestedClient