Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 132x 132x 132x 132x 4x 4x 132x 132x 1x 11x 11x 11x 132x 132x 11x 1x 11x 11x 11x 11x 15x 15x 15x 15x 15x 15x 9x 9x 8x 8x 4x 4x 2x 2x | import * as Rx from 'rxjs';
import type {ObjValue} from '@jsonjoy.com/json-type';
import {RpcError} from '@jsonjoy.com/rpc-error';
import {RpcCallee, type RpcCallerOptions} from './RpcCallee';
import {printTree} from 'tree-dump/lib/printTree';
import {Procedure} from '../procedures';
import {Call} from './Call';
import {type t, type KeyType, type Type, Value, type Schema} from '@jsonjoy.com/json-type';
import {ValidatorCodegen} from '@jsonjoy.com/json-type/lib/codegen/validator/ValidatorCodegen';
import {type AbsType, FnRxType, FnType} from '@jsonjoy.com/json-type/lib/type/classes';
import type {UnObjType, UnObjValue} from '@jsonjoy.com/json-type/lib/value/ObjValue';
import type {Callee} from './types';
import type {Printable} from 'tree-dump';
import type {ProcedureReq, ProcedureRes, Procedures} from '../procedures';
type ObjectFieldToTuple<F> = F extends KeyType<infer K, infer V> ? [K, V] : never;
type ToObject<T> = T extends [string, unknown][] ? {[K in T[number] as K[0]]: K[1]} : never;
type ObjectFieldsToMap<F> = ToObject<{[K in keyof F]: ObjectFieldToTuple<F[K]>}>;
type ObjectValueToTypeMap<V> = ObjectFieldsToMap<UnObjType<UnObjValue<V>>>;
export type ObjectValueToRpcCallerProcedures<V extends ObjValue<any>, Ctx = unknown> = {
[K in keyof ObjectValueToTypeMap<V>]: ObjectValueToTypeMap<V>[K] extends FnType<infer Req, infer Res>
? Procedure<t.infer<Req>, Value<Res>, Ctx>
: never;
};
export type ObjectValueToProcedures<V extends ObjValue<any>, Ctx = unknown> = {
[K in keyof ObjectValueToRpcCallerProcedures<V, Ctx>]: ObjectValueToRpcCallerProcedures<V, Ctx>[K] extends Procedure<
infer Req,
Value<infer Res>,
infer Ctx
>
? Procedure<Req, Value<Res extends Type ? Res : never>, Ctx>
: never;
};
export interface ObjectValueCallerOptions<V extends ObjValue<any>, Ctx = unknown>
extends Omit<RpcCallerOptions<ObjectValueToRpcCallerProcedures<V, Ctx>>, 'procedures'> {
router: V;
}
const fnValueToProcedure = <V extends Value<any>>(fn: V) => {
const validator = ValidatorCodegen.get({type: fn.type.req, errors: 'object'});
const requestSchema = (fn.type.req as AbsType<Schema>).getSchema();
const isRequestVoid = requestSchema.kind === 'con' && requestSchema.value === undefined;
const validate = isRequestVoid
? () => {}
: (req: unknown) => {
const error: any = validator(req);
Iif (error) {
const message = error.message + (Array.isArray(error?.path) ? ' Path: /' + error.path.join('/') : '');
throw RpcError.validation(message, error);
}
};
const procedure =
fn.type instanceof FnRxType
? Procedure.rx(fn.data as any, validate)
: fn.type instanceof FnType
? Procedure.unary(fn.data as any, validate)
: Procedure.unary(async () => fn.data as any, validate);
return procedure;
};
const objectValueToProcedures = <V extends ObjValue<any>, Ctx = unknown>(router: V) => {
const procedures: Procedures = {};
const data = router.data as Record<string, unknown>;
for (const key in data) {
const field = router.get(key);
procedures[key] = fnValueToProcedure(field);
}
return procedures as ObjectValueToRpcCallerProcedures<V, Ctx>;
};
/**
* Converts a JSON Type ObjectValue (an object type with value methods) into
* a Reactive-RPC caller which can be used to call methods defined in the
* ObjectValue. Wraps all responses and errors into JSON Type {@link Value}
* objects.
*/
export class TypedCallee<
Ctx,
V extends ObjValue<any>,
P extends ObjectValueToProcedures<V, Ctx> = ObjectValueToProcedures<V, Ctx>,
> implements Callee<Ctx, P>, Printable
{
public readonly router: V;
public readonly rpc: RpcCallee;
constructor({router, ...rest}: ObjectValueCallerOptions<V, Ctx>) {
this.router = router;
Iif (!router.type.system) throw new Error('NO_MODULE');
const procedures = objectValueToProcedures(router);
this.rpc = new RpcCallee({...rest, procedures});
}
protected getResType<K extends keyof P>(name: K) {
Iif (typeof name !== 'string') throw RpcError.internal('Method name must be a string.');
const method = this.router.get(name);
Iif (!method) throw RpcError.badRequest(`Method ${String(name)} not found.`);
Iif (method instanceof FnType) return method.res;
Iif (method instanceof FnRxType) return method.res;
return method.type.res;
}
/** -------------------------------------------------------- {@link Callee} */
public info<K extends keyof P>(name: K): Pick<P[K], 'pretty' | 'rx'> | undefined {
return this.rpc.info(name as string);
}
public createCall<K extends keyof P>(name: K, ctx: Ctx): Call<ProcedureReq<P[K]>, ProcedureRes<P[K]>> {
const call = this.rpc.createCall(name as string, ctx);
const type = this.getResType(name);
const res$ = call.res$.pipe(Rx.map((data) => new Value(data, type))) as Rx.Observable<ProcedureRes<P[K]>>;
return new Call(call.req$, call.reqUnsubscribe$, call.stop$, res$);
}
public async call<K extends keyof P>(name: K, request: ProcedureReq<P[K]>, ctx: Ctx): Promise<ProcedureRes<P[K]>> {
const type = this.getResType(name as string);
const data = await this.rpc.call(name as any, request, ctx);
const value = new Value(data, type);
return value as ProcedureRes<P[K]>;
}
public call$<K extends keyof P>(
name: K,
request$: Rx.Observable<ProcedureReq<P[K]>>,
ctx: Ctx,
): Rx.Observable<ProcedureRes<P[K]>> {
return Rx.of(this.getResType(name as string) as Type).pipe(
Rx.switchMap((type) => this.rpc.call$(name as any, request$, ctx).pipe(Rx.map((data) => new Value(data, type)))),
) as any;
}
public notify<K extends keyof P>(name: K, request: ProcedureReq<P[K]>, ctx: Ctx): Promise<void> {
this.getResType(name as any);
return this.rpc.notify(name as any, request, ctx);
}
/** ----------------------------------------------------- {@link Printable} */
public toString(tab = ''): string {
return (
`${this.constructor.name}` +
printTree(tab, [(tab) => this.router.toString(tab), (tab) => this.router.system.toString(tab)])
);
}
}
|