feat(rpc): scaffold the package and shared contract types
This commit is contained in:
@@ -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";
|
||||
@@ -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 };
|
||||
Reference in New Issue
Block a user