tson-rpc
Version:
Connect objects anywhere in pairs like quantum entanglement.
172 lines (166 loc) • 6.04 kB
text/typescript
// 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);
}
}