Server-side Client in Mini oRPC 
The server-side client in Mini oRPC transforms procedures into callable functions, enabling direct server-side invocation. This is the foundation of Mini oRPC client system - all other client functionality builds upon it.
INFO
The complete Mini oRPC implementation is available in our GitHub repository: Mini oRPC Repository
Implementation 
Here is the complete implementation of the server-side client functionality in Mini oRPC:
ts
import type { Client } from '@mini-orpc/client'
import type { MaybeOptionalOptions } from '@orpc/shared'
import type { AnyProcedure, Procedure, ProcedureHandlerOptions, } from './procedure'
import type { AnySchema, Context, InferSchemaInput, InferSchemaOutput, } from './types'
import { ORPCError } from '@mini-orpc/client'
import { resolveMaybeOptionalOptions } from '@orpc/shared'
import { ValidationError } from './error'
export type ProcedureClient<
  TInputSchema extends AnySchema,
  TOutputSchema extends AnySchema,
> = Client<InferSchemaInput<TInputSchema>, InferSchemaOutput<TOutputSchema>>
/**
 * context can be optional if `Record<never, never> extends TInitialContext`
 */
export type CreateProcedureClientOptions<TInitialContext extends Context> = {
  path?: readonly string[]
} & (Record<never, never> extends TInitialContext
  ? {
      context?: TInitialContext
    }
  : {
      context: TInitialContext
    })
/**
 * Turn a procedure into a callable function
 */
export function createProcedureClient<
  TInitialContext extends Context,
  TInputSchema extends AnySchema,
  TOutputSchema extends AnySchema,
>(
  procedure: Procedure<TInitialContext, any, TInputSchema, TOutputSchema>,
  ...rest: MaybeOptionalOptions<CreateProcedureClientOptions<TInitialContext>>
): ProcedureClient<TInputSchema, TOutputSchema> {
  const options = resolveMaybeOptionalOptions(rest)
  return (...[input, callerOptions]) => {
    return executeProcedureInternal(procedure, {
      context: options.context ?? {},
      input,
      path: options.path ?? [],
      procedure,
      signal: callerOptions?.signal,
    })
  }
}
async function validateInput(
  procedure: AnyProcedure,
  input: unknown
): Promise<any> {
  const schema = procedure['~orpc'].inputSchema
  if (!schema) {
    return input
  }
  const result = await schema['~standard'].validate(input)
  if (result.issues) {
    throw new ORPCError('BAD_REQUEST', {
      message: 'Input validation failed',
      data: {
        issues: result.issues,
      },
      cause: new ValidationError({
        message: 'Input validation failed',
        issues: result.issues,
      }),
    })
  }
  return result.value
}
async function validateOutput(
  procedure: AnyProcedure,
  output: unknown
): Promise<any> {
  const schema = procedure['~orpc'].outputSchema
  if (!schema) {
    return output
  }
  const result = await schema['~standard'].validate(output)
  if (result.issues) {
    throw new ORPCError('INTERNAL_SERVER_ERROR', {
      message: 'Output validation failed',
      cause: new ValidationError({
        message: 'Output validation failed',
        issues: result.issues,
      }),
    })
  }
  return result.value
}
function executeProcedureInternal(
  procedure: AnyProcedure,
  options: ProcedureHandlerOptions<any, any>
): Promise<any> {
  const middlewares = procedure['~orpc'].middlewares
  const inputValidationIndex = 0
  const outputValidationIndex = 0
  const next = async (
    index: number,
    context: Context,
    input: unknown
  ): Promise<unknown> => {
    let currentInput = input
    if (index === inputValidationIndex) {
      currentInput = await validateInput(procedure, currentInput)
    }
    const mid = middlewares[index]
    const output = mid
      ? (
          await mid({
            ...options,
            context,
            next: async (...[nextOptions]) => {
              const nextContext: Context = nextOptions?.context ?? {}
              return {
                output: await next(
                  index + 1,
                  { ...context, ...nextContext },
                  currentInput
                ),
                context: nextContext,
              }
            },
          })
        ).output
      : await procedure['~orpc'].handler({
          ...options,
          context,
          input: currentInput,
        })
    if (index === outputValidationIndex) {
      return await validateOutput(procedure, output)
    }
    return output
  }
  return next(0, options.context, options.input)
}ts
import type { MaybeOptionalOptions } from '@orpc/shared'
import { isObject, resolveMaybeOptionalOptions } from '@orpc/shared'
export type ORPCErrorOptions<TData> = ErrorOptions & {
  status?: number
  message?: string
} & (undefined extends TData ? { data?: TData } : { data: TData })
export class ORPCError<TCode extends string, TData> extends Error {
  readonly code: TCode
  readonly status: number
  readonly data: TData
  constructor(
    code: TCode,
    ...rest: MaybeOptionalOptions<ORPCErrorOptions<TData>>
  ) {
    const options = resolveMaybeOptionalOptions(rest)
    if (options?.status && !isORPCErrorStatus(options.status)) {
      throw new Error('[ORPCError] Invalid error status code.')
    }
    super(options.message, options)
    this.code = code
    this.status = options.status ?? 500 // Default to 500 if not provided
    this.data = options.data as TData // data only optional when TData is undefinable so can safely cast here
  }
  toJSON(): ORPCErrorJSON<TCode, TData> {
    return {
      code: this.code,
      status: this.status,
      message: this.message,
      data: this.data,
    }
  }
}
export type ORPCErrorJSON<TCode extends string, TData> = Pick<
  ORPCError<TCode, TData>,
  'code' | 'status' | 'message' | 'data'
>
export function isORPCErrorStatus(status: number): boolean {
  return status < 200 || status >= 400
}
export function isORPCErrorJson(
  json: unknown
): json is ORPCErrorJSON<string, unknown> {
  if (!isObject(json)) {
    return false
  }
  const validKeys = ['code', 'status', 'message', 'data']
  if (Object.keys(json).some(k => !validKeys.includes(k))) {
    return false
  }
  return (
    'code' in json
    && typeof json.code === 'string'
    && 'status' in json
    && typeof json.status === 'number'
    && isORPCErrorStatus(json.status)
    && 'message' in json
    && typeof json.message === 'string'
  )
}ts
export interface ClientOptions {
  signal?: AbortSignal
}
export type ClientRest<TInput> = undefined extends TInput
  ? [input?: TInput, options?: ClientOptions]
  : [input: TInput, options?: ClientOptions]
export interface Client<TInput, TOutput> {
  (...rest: ClientRest<TInput>): Promise<TOutput>
}
export type NestedClient = Client<any, any> | { [k: string]: NestedClient }Router Client 
Creating a client for each procedure individually can be tedious. Here is how to create a router client that handles multiple procedures:
ts
import type { MaybeOptionalOptions } from '@orpc/shared'
import type { Procedure } from './procedure'
import type { CreateProcedureClientOptions, ProcedureClient } from './procedure-client'
import type { AnyRouter, InferRouterInitialContexts } from './router'
import { get, resolveMaybeOptionalOptions, toArray } from '@orpc/shared'
import { isProcedure } from './procedure'
import { createProcedureClient } from './procedure-client'
export type RouterClient<TRouter extends AnyRouter> = TRouter extends Procedure<
  any,
  any,
  infer UInputSchema,
  infer UOutputSchema
>
  ? ProcedureClient<UInputSchema, UOutputSchema>
  : {
      [K in keyof TRouter]: TRouter[K] extends AnyRouter
        ? RouterClient<TRouter[K]>
        : never;
    }
/**
 * Turn a router into a chainable procedure client.
 */
export function createRouterClient<T extends AnyRouter>(
  router: T,
  ...rest: MaybeOptionalOptions<
    CreateProcedureClientOptions<InferRouterInitialContexts<T>>
  >
): RouterClient<T> {
  const options = resolveMaybeOptionalOptions(rest)
  if (isProcedure(router)) {
    const caller = createProcedureClient(router, options)
    return caller as RouterClient<T>
  }
  const recursive = new Proxy(router, {
    get(target, key) {
      if (typeof key !== 'string') {
        return Reflect.get(target, key)
      }
      const next = get(router, [key]) as AnyRouter | undefined
      if (!next) {
        return Reflect.get(target, key)
      }
      return createRouterClient(next, {
        ...options,
        path: [...toArray(options.path), key],
      })
    },
  })
  return recursive as unknown as RouterClient<T>
}Usage 
Transform any procedure or router into a callable client for server-side use:
ts
// Create a client for a single procedure
const procedureClient = createProcedureClient(myProcedure, {
  context: { userId: '123' },
})
const result = await procedureClient({ input: 'example' })
// Create a client for an entire router
const routerClient = createRouterClient(myRouter, {
  context: { userId: '123' },
})
const result = await routerClient.someProcedure({ input: 'example' })