@orpc/server
Version:
<div align="center"> <image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" /> </div>
490 lines (480 loc) • 17.5 kB
JavaScript
import { mergeErrorMap, mergeMeta, mergeRoute, mergePrefix, mergeTags, isContractProcedure, getContractRouter, fallbackContractConfig } from '@orpc/contract';
export { ValidationError, eventIterator, type } from '@orpc/contract';
import { P as Procedure, b as addMiddleware, c as createProcedureClient, e as enhanceRouter, l as lazy, s as setHiddenRouterContract, u as unlazy, g as getRouter, i as isProcedure, d as isLazy, f as createAssertedLazyProcedure } from './shared/server.B_fj3X5m.mjs';
export { L as LAZY_SYMBOL, p as call, r as createAccessibleLazyRouter, a as createContractedProcedure, h as createORPCErrorConstructorMap, q as getHiddenRouterContract, j as getLazyMeta, n as isStartWithMiddlewares, m as mergeCurrentContext, o as mergeMiddlewares, k as middlewareOutputFn, w as resolveContractProcedures, t as traverseContractProcedures, x as unlazyRouter, v as validateORPCError } from './shared/server.B_fj3X5m.mjs';
import { toORPCError } from '@orpc/client';
export { ORPCError, isDefinedError, safe } from '@orpc/client';
import { resolveMaybeOptionalOptions } from '@orpc/shared';
export { AsyncIteratorClass, EventPublisher, asyncIteratorToStream as eventIteratorToStream, onError, onFinish, onStart, onSuccess, streamToAsyncIteratorClass as streamToEventIterator } from '@orpc/shared';
export { getEventMeta, withEventMeta } from '@orpc/standard-server';
const DEFAULT_CONFIG = {
initialInputValidationIndex: 0,
initialOutputValidationIndex: 0,
dedupeLeadingMiddlewares: true
};
function fallbackConfig(key, value) {
if (value === void 0) {
return DEFAULT_CONFIG[key];
}
return value;
}
function decorateMiddleware(middleware) {
const decorated = (...args) => middleware(...args);
decorated.mapInput = (mapInput) => {
const mapped = decorateMiddleware(
(options, input, ...rest) => middleware(options, mapInput(input), ...rest)
);
return mapped;
};
decorated.concat = (concatMiddleware, mapInput) => {
const mapped = mapInput ? decorateMiddleware(concatMiddleware).mapInput(mapInput) : concatMiddleware;
const concatted = decorateMiddleware((options, input, output, ...rest) => {
const merged = middleware({
...options,
next: (...[nextOptions1]) => mapped({
...options,
context: { ...options.context, ...nextOptions1?.context },
next: (...[nextOptions2]) => options.next({ context: { ...nextOptions1?.context, ...nextOptions2?.context } })
}, input, output, ...rest)
}, input, output, ...rest);
return merged;
});
return concatted;
};
return decorated;
}
function createActionableClient(client) {
const action = async (input) => {
try {
return [null, await client(input)];
} catch (error) {
if (error instanceof Error && "digest" in error && typeof error.digest === "string" && error.digest.startsWith("NEXT_")) {
throw error;
}
return [toORPCError(error).toJSON(), void 0];
}
};
return action;
}
class DecoratedProcedure extends Procedure {
/**
* Adds type-safe custom errors.
* The provided errors are spared-merged with any existing errors.
*
* @see {@link https://orpc.unnoq.com/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
*/
errors(errors) {
return new DecoratedProcedure({
...this["~orpc"],
errorMap: mergeErrorMap(this["~orpc"].errorMap, errors)
});
}
/**
* Sets or updates the metadata.
* The provided metadata is spared-merged with any existing metadata.
*
* @see {@link https://orpc.unnoq.com/docs/metadata Metadata Docs}
*/
meta(meta) {
return new DecoratedProcedure({
...this["~orpc"],
meta: mergeMeta(this["~orpc"].meta, meta)
});
}
/**
* Sets or updates the route definition.
* The provided route is spared-merged with any existing route.
* This option is typically relevant when integrating with OpenAPI.
*
* @see {@link https://orpc.unnoq.com/docs/openapi/routing OpenAPI Routing Docs}
* @see {@link https://orpc.unnoq.com/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
*/
route(route) {
return new DecoratedProcedure({
...this["~orpc"],
route: mergeRoute(this["~orpc"].route, route)
});
}
use(middleware, mapInput) {
const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
return new DecoratedProcedure({
...this["~orpc"],
middlewares: addMiddleware(this["~orpc"].middlewares, mapped)
});
}
/**
* Make this procedure callable (works like a function while still being a procedure).
*
* @see {@link https://orpc.unnoq.com/docs/client/server-side Server-side Client Docs}
*/
callable(...rest) {
const client = createProcedureClient(this, ...rest);
return new Proxy(client, {
get: (target, key) => {
return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
},
has: (target, key) => {
return Reflect.has(this, key) || Reflect.has(target, key);
}
});
}
/**
* Make this procedure compatible with server action.
*
* @see {@link https://orpc.unnoq.com/docs/server-action Server Action Docs}
*/
actionable(...rest) {
const action = createActionableClient(createProcedureClient(this, ...rest));
return new Proxy(action, {
get: (target, key) => {
return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
},
has: (target, key) => {
return Reflect.has(this, key) || Reflect.has(target, key);
}
});
}
}
class Builder {
/**
* This property holds the defined options.
*/
"~orpc";
constructor(def) {
this["~orpc"] = def;
}
/**
* Sets or overrides the config.
*
* @see {@link https://orpc.unnoq.com/docs/client/server-side#middlewares-order Middlewares Order Docs}
* @see {@link https://orpc.unnoq.com/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
*/
$config(config) {
const inputValidationCount = this["~orpc"].inputValidationIndex - fallbackConfig("initialInputValidationIndex", this["~orpc"].config.initialInputValidationIndex);
const outputValidationCount = this["~orpc"].outputValidationIndex - fallbackConfig("initialOutputValidationIndex", this["~orpc"].config.initialOutputValidationIndex);
return new Builder({
...this["~orpc"],
config,
dedupeLeadingMiddlewares: fallbackConfig("dedupeLeadingMiddlewares", config.dedupeLeadingMiddlewares),
inputValidationIndex: fallbackConfig("initialInputValidationIndex", config.initialInputValidationIndex) + inputValidationCount,
outputValidationIndex: fallbackConfig("initialOutputValidationIndex", config.initialOutputValidationIndex) + outputValidationCount
});
}
/**
* Set or override the initial context.
*
* @see {@link https://orpc.unnoq.com/docs/context Context Docs}
*/
$context() {
return new Builder({
...this["~orpc"],
middlewares: [],
inputValidationIndex: fallbackConfig("initialInputValidationIndex", this["~orpc"].config.initialInputValidationIndex),
outputValidationIndex: fallbackConfig("initialOutputValidationIndex", this["~orpc"].config.initialOutputValidationIndex)
});
}
/**
* Sets or overrides the initial meta.
*
* @see {@link https://orpc.unnoq.com/docs/metadata Metadata Docs}
*/
$meta(initialMeta) {
return new Builder({
...this["~orpc"],
meta: initialMeta
});
}
/**
* Sets or overrides the initial route.
* This option is typically relevant when integrating with OpenAPI.
*
* @see {@link https://orpc.unnoq.com/docs/openapi/routing OpenAPI Routing Docs}
* @see {@link https://orpc.unnoq.com/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
*/
$route(initialRoute) {
return new Builder({
...this["~orpc"],
route: initialRoute
});
}
/**
* Sets or overrides the initial input schema.
*
* @see {@link https://orpc.unnoq.com/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
*/
$input(initialInputSchema) {
return new Builder({
...this["~orpc"],
inputSchema: initialInputSchema
});
}
/**
* Creates a middleware.
*
* @see {@link https://orpc.unnoq.com/docs/middleware Middleware Docs}
*/
middleware(middleware) {
return decorateMiddleware(middleware);
}
/**
* Adds type-safe custom errors.
* The provided errors are spared-merged with any existing errors.
*
* @see {@link https://orpc.unnoq.com/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
*/
errors(errors) {
return new Builder({
...this["~orpc"],
errorMap: mergeErrorMap(this["~orpc"].errorMap, errors)
});
}
use(middleware, mapInput) {
const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
return new Builder({
...this["~orpc"],
middlewares: addMiddleware(this["~orpc"].middlewares, mapped)
});
}
/**
* Sets or updates the metadata.
* The provided metadata is spared-merged with any existing metadata.
*
* @see {@link https://orpc.unnoq.com/docs/metadata Metadata Docs}
*/
meta(meta) {
return new Builder({
...this["~orpc"],
meta: mergeMeta(this["~orpc"].meta, meta)
});
}
/**
* Sets or updates the route definition.
* The provided route is spared-merged with any existing route.
* This option is typically relevant when integrating with OpenAPI.
*
* @see {@link https://orpc.unnoq.com/docs/openapi/routing OpenAPI Routing Docs}
* @see {@link https://orpc.unnoq.com/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
*/
route(route) {
return new Builder({
...this["~orpc"],
route: mergeRoute(this["~orpc"].route, route)
});
}
/**
* Defines the input validation schema.
*
* @see {@link https://orpc.unnoq.com/docs/procedure#input-output-validation Input Validation Docs}
*/
input(schema) {
return new Builder({
...this["~orpc"],
inputSchema: schema,
inputValidationIndex: fallbackConfig("initialInputValidationIndex", this["~orpc"].config.initialInputValidationIndex) + this["~orpc"].middlewares.length
});
}
/**
* Defines the output validation schema.
*
* @see {@link https://orpc.unnoq.com/docs/procedure#input-output-validation Output Validation Docs}
*/
output(schema) {
return new Builder({
...this["~orpc"],
outputSchema: schema,
outputValidationIndex: fallbackConfig("initialOutputValidationIndex", this["~orpc"].config.initialOutputValidationIndex) + this["~orpc"].middlewares.length
});
}
/**
* Defines the handler of the procedure.
*
* @see {@link https://orpc.unnoq.com/docs/procedure Procedure Docs}
*/
handler(handler) {
return new DecoratedProcedure({
...this["~orpc"],
handler
});
}
/**
* Prefixes all procedures in the router.
* The provided prefix is post-appended to any existing router prefix.
*
* @note This option does not affect procedures that do not define a path in their route definition.
*
* @see {@link https://orpc.unnoq.com/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
*/
prefix(prefix) {
return new Builder({
...this["~orpc"],
prefix: mergePrefix(this["~orpc"].prefix, prefix)
});
}
/**
* Adds tags to all procedures in the router.
* This helpful when you want to group procedures together in the OpenAPI specification.
*
* @see {@link https://orpc.unnoq.com/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
*/
tag(...tags) {
return new Builder({
...this["~orpc"],
tags: mergeTags(this["~orpc"].tags, tags)
});
}
/**
* Applies all of the previously defined options to the specified router.
*
* @see {@link https://orpc.unnoq.com/docs/router#extending-router Extending Router Docs}
*/
router(router) {
return enhanceRouter(router, this["~orpc"]);
}
/**
* Create a lazy router
* And applies all of the previously defined options to the specified router.
*
* @see {@link https://orpc.unnoq.com/docs/router#extending-router Extending Router Docs}
*/
lazy(loader) {
return enhanceRouter(lazy(loader), this["~orpc"]);
}
}
const os = new Builder({
config: {},
route: {},
meta: {},
errorMap: {},
inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
middlewares: [],
dedupeLeadingMiddlewares: true
});
function implementerInternal(contract, config, middlewares) {
if (isContractProcedure(contract)) {
const impl2 = new Builder({
...contract["~orpc"],
config,
middlewares,
inputValidationIndex: fallbackConfig("initialInputValidationIndex", config?.initialInputValidationIndex) + middlewares.length,
outputValidationIndex: fallbackConfig("initialOutputValidationIndex", config?.initialOutputValidationIndex) + middlewares.length,
dedupeLeadingMiddlewares: fallbackConfig("dedupeLeadingMiddlewares", config.dedupeLeadingMiddlewares)
});
return impl2;
}
const impl = new Proxy(contract, {
get: (target, key) => {
if (typeof key !== "string") {
return Reflect.get(target, key);
}
let method;
if (key === "middleware") {
method = (mid) => decorateMiddleware(mid);
} else if (key === "use") {
method = (mid) => {
return implementerInternal(
contract,
config,
addMiddleware(middlewares, mid)
);
};
} else if (key === "router") {
method = (router) => {
const adapted = enhanceRouter(router, {
middlewares,
errorMap: {},
prefix: void 0,
tags: void 0,
dedupeLeadingMiddlewares: fallbackConfig("dedupeLeadingMiddlewares", config.dedupeLeadingMiddlewares)
});
return setHiddenRouterContract(adapted, contract);
};
} else if (key === "lazy") {
method = (loader) => {
const adapted = enhanceRouter(lazy(loader), {
middlewares,
errorMap: {},
prefix: void 0,
tags: void 0,
dedupeLeadingMiddlewares: fallbackConfig("dedupeLeadingMiddlewares", config.dedupeLeadingMiddlewares)
});
return setHiddenRouterContract(adapted, contract);
};
}
const next = getContractRouter(target, [key]);
if (!next) {
return method ?? next;
}
const nextImpl = implementerInternal(next, config, middlewares);
if (method) {
return new Proxy(method, {
get(_, key2) {
return Reflect.get(nextImpl, key2);
}
});
}
return nextImpl;
}
});
return impl;
}
function implement(contract, config = {}) {
const implInternal = implementerInternal(contract, config, []);
const impl = new Proxy(implInternal, {
get: (target, key) => {
let method;
if (key === "$context") {
method = () => impl;
} else if (key === "$config") {
method = (config2) => implement(contract, config2);
}
const next = Reflect.get(target, key);
if (!method || !next || typeof next !== "function" && typeof next !== "object") {
return method || next;
}
return new Proxy(method, {
get(_, key2) {
return Reflect.get(next, key2);
}
});
}
});
return impl;
}
function inferRPCMethodFromRouter(router) {
return async (_, path) => {
const { default: procedure } = await unlazy(getRouter(router, path));
if (!isProcedure(procedure)) {
throw new Error(
`[inferRPCMethodFromRouter] No valid procedure found at path "${path.join(".")}". This may happen when the router is not properly configured.`
);
}
const method = fallbackContractConfig("defaultMethod", procedure["~orpc"].route.method);
return method === "HEAD" ? "GET" : method;
};
}
function createRouterClient(router, ...rest) {
const options = resolveMaybeOptionalOptions(rest);
if (isProcedure(router)) {
const caller = createProcedureClient(router, options);
return caller;
}
const procedureCaller = isLazy(router) ? createProcedureClient(createAssertedLazyProcedure(router), options) : {};
const recursive = new Proxy(procedureCaller, {
get(target, key) {
if (typeof key !== "string") {
return Reflect.get(target, key);
}
const next = getRouter(router, [key]);
if (!next) {
return Reflect.get(target, key);
}
return createRouterClient(next, {
...rest[0],
path: [...rest[0]?.path ?? [], key]
});
}
});
return recursive;
}
export { Builder, DecoratedProcedure, Procedure, addMiddleware, createActionableClient, createAssertedLazyProcedure, createProcedureClient, createRouterClient, decorateMiddleware, enhanceRouter, fallbackConfig, getRouter, implement, implementerInternal, inferRPCMethodFromRouter, isLazy, isProcedure, lazy, os, setHiddenRouterContract, unlazy };