feat(rpc): scaffold the package and shared contract types

This commit is contained in:
2026-08-05 09:52:48 +05:30
parent 63e6148cdb
commit e1fca3eddf
6 changed files with 137 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
/**
* @wrnexus/rpc — typed request/response between workspace apps.
*
* A contract lives in the workspace's shared package and is imported by both
* sides: the callee `implement`s it, the caller gets a typed proxy. Types flow
* through a normal import, so there is no code generator and no generated file
* to go stale.
*/
export type {
AnyProcedures,
InferInput,
InputSchema,
InferProcedureInput,
InferProcedureOutput,
ProcedureDef,
ServiceContract,
ServiceResult,
} from "./types.ts";
+49
View File
@@ -0,0 +1,49 @@
import type { ObjectSchema } from "@wrnexus/validation";
/** Extract the validated value type from a `v.object(...)` schema. */
export type InferInput<S> = S extends ObjectSchema<infer T> ? T : never;
/**
* One callable procedure on a service. `input` is validated on the callee
* before the handler runs; `permission` is enforced there too.
*/
/** Structural shape of a validation schema, so ProcedureDef needs no generic. */
export interface InputSchema {
parse(value: Record<string, unknown>): {
ok: boolean;
value: unknown;
errors: Record<string, string>;
};
}
export interface ProcedureDef<Input = unknown, Output = unknown> {
input?: InputSchema;
/** Permission the callee checks before invoking the handler. */
permission?: string;
/** Only idempotent procedures are ever retried. */
idempotent?: boolean;
/** Type-only markers; never present at runtime. */
readonly __input?: Input;
readonly __output?: Output;
}
/**
* A procedure map with its element types erased. The `any` is deliberate and
* confined to this alias: the phantom `__input`/`__output` markers make
* ProcedureDef invariant, so no narrower erasure accepts a real contract.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AnyProcedures = Record<string, ProcedureDef<any, any>>;
export interface ServiceContract<Procedures extends AnyProcedures = AnyProcedures> {
/** Stable service id, used in the mounted path. */
name: string;
procedures: Procedures;
}
export type InferProcedureInput<P> = P extends ProcedureDef<infer I, unknown> ? I : never;
export type InferProcedureOutput<P> = P extends ProcedureDef<unknown, infer O> ? O : never;
/** What a transport returns: either a value or a structured failure. */
export type ServiceResult<T = unknown> =
{ ok: true; value: T } | { ok: false; code: string; message: string; retryable: boolean };