UNPKG

tson-rpc

Version:

Connect objects anywhere in pairs like quantum entanglement.

172 lines (166 loc) 6.04 kB
// Copyright (c) 2022 System233 // // This software is released under the MIT License. // https://opensource.org/licenses/MIT import { TSON, TSONData } from "tson-serializer"; import { v4 as uuidv4 } from 'uuid' import { BiMap } from "./bimap"; export type TypeOf = "array" | "object" | "function" | null; export class Handler implements ProxyHandler<any>{ apply(target: Function, thisArg: any, argArray: any[]): any { return target.apply(thisArg, argArray); } construct(target: any, argArray: any[], newTarget: Function): object { return new target(...argArray); } defineProperty(target: any, p: string | symbol, attributes: PropertyDescriptor): boolean { Object.defineProperty(target, p, attributes); return true; } deleteProperty(target: any, p: string | symbol): boolean { return delete target[p]; } get(target: any, p: string | symbol, receiver: any): any { return target[p]; } getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor | undefined { return Object.getOwnPropertyDescriptor(target, p); } getPrototypeOf(target: any): object | null { return Object.getPrototypeOf(target); } has(target: any, p: string | symbol): boolean { return p in target; } isExtensible(target: any): boolean { return Object.isExtensible(target); } ownKeys(target: any): ArrayLike<string | symbol> { return Object.keys(target); } preventExtensions(target: any): boolean { return Object.isFrozen(target); } set(target: any, p: string | symbol, value: any, receiver: any): boolean { target[p] = value; return true; } setPrototypeOf(target: any, v: object | null): boolean { Object.setPrototypeOf(target, v); return true; } }; export interface WrappedObjectData { id: string; type: TypeOf; } export type ParametersSkipFirst<T extends (first: any, ...args: any) => any> = T extends (first: any, ...args: infer P) => any ? P : never; export class WrappedObject<T extends object> implements ProxyHandler<T>{ constructor(public handler: WrappedHandler, public data: WrappedObjectData) { const target = data.type == 'function' ? function () { } : data.type == 'array' ? [] : {}; return new Proxy(target as any, new Proxy({}, this)); } call<T extends keyof Handler>(method: T, args: ParametersSkipFirst<Handler[T]>): ReturnType<Handler[T]> { return this.handler.call(this.data, method, args); } get(target: any, p: keyof Handler, receiver: any) { if (p in target) { return target[p]; } const handler = (_: any, ...args: any) => this.call(p, args); target[p] = handler; return handler; } has(target: any, p: string | symbol): boolean { return p in target; } } export interface WrappedRequest{ target: WrappedObjectData; method: keyof Handler; data: TSONData[] } export type RequestHandler = (request:WrappedRequest) => TSONData | TSONData[]; export const typeOf = (value: any): TypeOf => value != null&& !(value instanceof ArrayBuffer) ? Array.isArray(value) ? 'array' : typeof value == 'object' ? 'object' : typeof value == 'function' ? 'function' : null : null; export class WrappedHandler { map: BiMap<string, object> = new BiMap; tson: TSON = new TSON; handler: Handler = new Handler; constructor(public request: RequestHandler) { this.tson.register<object, WrappedObjectData>('Wrapped', (data) => this.load(data), (obj) => this.dump(obj), (value: any) => !!typeOf(value) ); } id(value: object) { if (this.map.hasValue(value)) { return this.map.getByValue(value); } return this.new(value); } dump(obj: object): WrappedObjectData { const type = typeOf(obj); let id = this.map.getByValue(obj); if (id == null) { id = uuidv4(); this.map.set(id, obj); } return { id, type }; } load<T extends object>(data: WrappedObjectData): T { if (this.map.has(data.id)) { return this.map.get(data.id) as T; } return this.remote<T>(data); } new(value: object, id?: string): string { id = id || uuidv4(); this.map.set(id, value); return id; } join(value: object, id?: string):WrappedObjectData{ const type=typeOf(value); id=this.new(value,id); return { id, type } } local(id:string):WrappedObjectData{ const value=this.get(id); if(value==null){ return null; } const type=typeOf(value); return { type, id } } remote<T extends object>(data: WrappedObjectData): T { const obj = new WrappedObject<T>(this, data) as T; this.new(obj, data.id); return obj; } call<T extends keyof Handler>(target: WrappedObjectData, method: T, params: ParametersSkipFirst<Handler[T]>): ReturnType<Handler[T]> { const data = params.map(x => this.tson.forward(x)); const result = this.request({target, method, data}); return this.tson.backward(result); } handle<T extends keyof Handler>(request:WrappedRequest) { const {target,method,data}=request; const args = this.tson.backward<ParametersSkipFirst<Handler[T]>>(data); const result = (this.handler[method] as any).call(this.handler, this.load(target), ...args); return this.tson.forward(result); } delete(id: string) { return this.map.delete(id); } get(id:string){ return this.map.getByKey(id); } }