-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9c78a77
Showing
18 changed files
with
1,062 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
examples | ||
tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<!-- | ||
Copyright (c) 2022 System233 | ||
This software is released under the MIT License. | ||
https://opensource.org/licenses/MIT | ||
--> | ||
|
||
# TSON-RPC | ||
Connect objects anywhere in pairs like quantum entanglement. | ||
|
||
🧪 Experimental Technology. | ||
❌ DO NOT USE IF YOU DON'T KNOW WHAT YOU'RE DOING. | ||
❗It may expose your system to danger or attack. | ||
|
||
## Example | ||
|
||
```ts | ||
|
||
|
||
class Test{ | ||
constructor(public name:string){} | ||
call(obj:Test,...args:any){ | ||
console.log(this.name,'call()',obj, args); | ||
obj.print(args) | ||
} | ||
|
||
print(args:[]){ | ||
console.log(this.name,'print', args); | ||
console.log(this.name,'print length', args.length); | ||
console.log(this.name,'print', args.map(x=>`${x}!`)); | ||
} | ||
push(array:any[]){ | ||
console.log(this.name,'push() before',array) | ||
array.push('pushed data'); | ||
console.log(this.name,'push() after',array) | ||
console.log(this.name,'set',array) | ||
console.log(this.name,'set length',array.length) | ||
console.log(this.name,'set iter..',[...array]) | ||
console.log(this.name,'set iter',array[Symbol.iterator]) | ||
} | ||
}; | ||
let client:WrappedHandler; | ||
|
||
const test=new Test("[Remote]"); | ||
|
||
const erase=(data:any)=>JSON.parse(JSON.stringify(data)); | ||
|
||
const server=new WrappedHandler(req=>client.handle(erase(req))); | ||
const id=server.new(test); | ||
|
||
|
||
|
||
client=new WrappedHandler(req=>server.handle(erase(req))); | ||
const remote=client.remote<Test>({id,type:typeOf(test)}); | ||
|
||
|
||
const localArray:string[]=[]; | ||
|
||
const local=new Test("[Local]"); | ||
remote.call(local,"local string"); | ||
remote.push(localArray); | ||
console.log('localArray',localArray) | ||
|
||
// [Remote] call() {} [ 'local string' ] | ||
// [Local] print [] | ||
// [Local] print length 1 | ||
// [Local] print [] | ||
// [Remote] push() before [] | ||
// [Remote] push() after [] <--You can't see the remote object. | ||
// [Remote] set [] | ||
// [Remote] set length 1 | ||
// [Remote] set iter.. [ 'pushed data' ] | ||
// [Remote] set iter [Function (anonymous)] | ||
// localArray [ 'pushed data' ] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export declare class BiMap<K, T> { | ||
map: Map<K, T>; | ||
imap: Map<T, K>; | ||
constructor(pairs?: [K, T][]); | ||
has(key: K): boolean; | ||
hasKey(key: K): boolean; | ||
hasValue(value: T): boolean; | ||
get(key: K): T; | ||
getByKey(key: K): T; | ||
getByValue(value: T): K; | ||
set(key: K, value: T): void; | ||
delete(key: K): boolean; | ||
deleteBoth(key: K, value: T): boolean; | ||
deleteKey(key: K): boolean; | ||
deleteValue(value: T): boolean; | ||
clear(): void; | ||
forEach(callbackfn: (value: T, key: K, map: Map<K, T>) => void, thisArg?: any): void; | ||
entries(): IterableIterator<[K, T]>; | ||
keys(): IterableIterator<K>; | ||
values(): IterableIterator<T>; | ||
get size(): number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
"use strict"; | ||
// Copyright (c) 2022 System233 | ||
// | ||
// This software is released under the MIT License. | ||
// https://opensource.org/licenses/MIT | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.BiMap = void 0; | ||
class BiMap { | ||
map = new Map; | ||
imap = new Map; | ||
constructor(pairs) { | ||
if (pairs != null) { | ||
pairs.forEach(([key, value]) => this.set(key, value)); | ||
} | ||
} | ||
has(key) { | ||
return this.hasKey(key); | ||
} | ||
hasKey(key) { | ||
return this.map.has(key); | ||
} | ||
hasValue(value) { | ||
return this.imap.has(value); | ||
} | ||
get(key) { | ||
return this.getByKey(key); | ||
} | ||
getByKey(key) { | ||
return this.map.get(key); | ||
} | ||
getByValue(value) { | ||
return this.imap.get(value); | ||
} | ||
set(key, value) { | ||
this.map.set(key, value); | ||
this.imap.set(value, key); | ||
} | ||
delete(key) { | ||
return this.deleteKey(key); | ||
} | ||
deleteBoth(key, value) { | ||
if (this.hasKey(key) && this.hasValue(value)) { | ||
return this.map.delete(key) && this.imap.delete(value); | ||
} | ||
return true; | ||
} | ||
deleteKey(key) { | ||
return this.deleteBoth(key, this.getByKey(key)); | ||
} | ||
deleteValue(value) { | ||
return this.deleteBoth(this.getByValue(value), value); | ||
} | ||
clear() { | ||
this.map.clear(); | ||
this.imap.clear(); | ||
} | ||
forEach(callbackfn, thisArg) { | ||
return this.map.forEach(callbackfn, thisArg); | ||
} | ||
entries() { | ||
return this.map.entries(); | ||
} | ||
keys() { | ||
return this.map.keys(); | ||
} | ||
values() { | ||
return this.map.values(); | ||
} | ||
get size() { | ||
return this.map.size; | ||
} | ||
} | ||
exports.BiMap = BiMap; | ||
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { TSON, TSONData } from "tson-serializer"; | ||
import { BiMap } from "./bimap"; | ||
export declare type TypeOf = "array" | "object" | "function" | null; | ||
export declare class Handler implements ProxyHandler<any> { | ||
apply(target: Function, thisArg: any, argArray: any[]): any; | ||
construct(target: any, argArray: any[], newTarget: Function): object; | ||
defineProperty(target: any, p: string | symbol, attributes: PropertyDescriptor): boolean; | ||
deleteProperty(target: any, p: string | symbol): boolean; | ||
get(target: any, p: string | symbol, receiver: any): any; | ||
getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor | undefined; | ||
getPrototypeOf(target: any): object | null; | ||
has(target: any, p: string | symbol): boolean; | ||
isExtensible(target: any): boolean; | ||
ownKeys(target: any): ArrayLike<string | symbol>; | ||
preventExtensions(target: any): boolean; | ||
set(target: any, p: string | symbol, value: any, receiver: any): boolean; | ||
setPrototypeOf(target: any, v: object | null): boolean; | ||
} | ||
export interface WrappedObjectData { | ||
id: string; | ||
type: TypeOf; | ||
} | ||
export declare type ParametersSkipFirst<T extends (first: any, ...args: any) => any> = T extends (first: any, ...args: infer P) => any ? P : never; | ||
export declare class WrappedObject<T extends object> implements ProxyHandler<T> { | ||
handler: WrappedHandler; | ||
data: WrappedObjectData; | ||
constructor(handler: WrappedHandler, data: WrappedObjectData); | ||
call<T extends keyof Handler>(method: T, args: ParametersSkipFirst<Handler[T]>): ReturnType<Handler[T]>; | ||
get(target: any, p: keyof Handler, receiver: any): any; | ||
has(target: any, p: string | symbol): boolean; | ||
} | ||
export interface WrappedRequest { | ||
target: WrappedObjectData; | ||
method: keyof Handler; | ||
data: TSONData[]; | ||
} | ||
export declare type RequestHandler = (request: WrappedRequest) => TSONData | TSONData[]; | ||
export declare const typeOf: (value: any) => TypeOf; | ||
export declare class WrappedHandler { | ||
request: RequestHandler; | ||
map: BiMap<string, object>; | ||
tson: TSON; | ||
handler: Handler; | ||
constructor(request: RequestHandler); | ||
id(value: object): string; | ||
dump(obj: object): WrappedObjectData; | ||
load<T extends object>(data: WrappedObjectData): T; | ||
new(value: object, id?: string): string; | ||
remote<T extends object>(data: WrappedObjectData): T; | ||
call<T extends keyof Handler>(target: WrappedObjectData, method: T, params: ParametersSkipFirst<Handler[T]>): ReturnType<Handler[T]>; | ||
handle<T extends keyof Handler>(request: WrappedRequest): TSONData<any, any>; | ||
delete(id: string): boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
"use strict"; | ||
// Copyright (c) 2022 System233 | ||
// | ||
// This software is released under the MIT License. | ||
// https://opensource.org/licenses/MIT | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WrappedHandler = exports.typeOf = exports.WrappedObject = exports.Handler = void 0; | ||
const tson_serializer_1 = require("tson-serializer"); | ||
const uuid_1 = require("uuid"); | ||
const bimap_1 = require("./bimap"); | ||
class Handler { | ||
apply(target, thisArg, argArray) { | ||
return target.apply(thisArg, argArray); | ||
} | ||
construct(target, argArray, newTarget) { | ||
return new target(...argArray); | ||
} | ||
defineProperty(target, p, attributes) { | ||
Object.defineProperty(target, p, attributes); | ||
return true; | ||
} | ||
deleteProperty(target, p) { | ||
return delete target[p]; | ||
} | ||
get(target, p, receiver) { | ||
return target[p]; | ||
} | ||
getOwnPropertyDescriptor(target, p) { | ||
return Object.getOwnPropertyDescriptor(target, p); | ||
} | ||
getPrototypeOf(target) { | ||
return Object.getPrototypeOf(target); | ||
} | ||
has(target, p) { | ||
return p in target; | ||
} | ||
isExtensible(target) { | ||
return Object.isExtensible(target); | ||
} | ||
ownKeys(target) { | ||
return Object.keys(target); | ||
} | ||
preventExtensions(target) { | ||
return Object.isFrozen(target); | ||
} | ||
set(target, p, value, receiver) { | ||
target[p] = value; | ||
return true; | ||
} | ||
setPrototypeOf(target, v) { | ||
Object.setPrototypeOf(target, v); | ||
return true; | ||
} | ||
} | ||
exports.Handler = Handler; | ||
; | ||
class WrappedObject { | ||
handler; | ||
data; | ||
constructor(handler, data) { | ||
this.handler = handler; | ||
this.data = data; | ||
const target = data.type == 'function' ? function () { } : data.type == 'array' ? [] : {}; | ||
return new Proxy(target, new Proxy({}, this)); | ||
} | ||
call(method, args) { | ||
return this.handler.call(this.data, method, args); | ||
} | ||
get(target, p, receiver) { | ||
if (p in target) { | ||
return target[p]; | ||
} | ||
const handler = (_, ...args) => this.call(p, args); | ||
target[p] = handler; | ||
return handler; | ||
} | ||
has(target, p) { | ||
return p in target; | ||
} | ||
} | ||
exports.WrappedObject = WrappedObject; | ||
const typeOf = (value) => value != null ? Array.isArray(value) ? 'array' : typeof value == 'object' ? 'object' : typeof value == 'function' ? 'function' : null : null; | ||
exports.typeOf = typeOf; | ||
class WrappedHandler { | ||
request; | ||
map = new bimap_1.BiMap; | ||
tson = new tson_serializer_1.TSON; | ||
handler = new Handler; | ||
constructor(request) { | ||
this.request = request; | ||
this.tson.register('Wrapped', (data) => this.load(data), (obj) => this.dump(obj), (value) => !!(0, exports.typeOf)(value)); | ||
} | ||
id(value) { | ||
if (this.map.hasValue(value)) { | ||
return this.map.getByValue(value); | ||
} | ||
return this.new(value); | ||
} | ||
dump(obj) { | ||
const type = (0, exports.typeOf)(obj); | ||
let id = this.map.getByValue(obj); | ||
if (id == null) { | ||
id = (0, uuid_1.v4)(); | ||
this.map.set(id, obj); | ||
} | ||
return { | ||
id, | ||
type | ||
}; | ||
} | ||
load(data) { | ||
if (this.map.has(data.id)) { | ||
return this.map.get(data.id); | ||
} | ||
return this.remote(data); | ||
} | ||
new(value, id) { | ||
id = id || (0, uuid_1.v4)(); | ||
this.map.set(id, value); | ||
return id; | ||
} | ||
remote(data) { | ||
const obj = new WrappedObject(this, data); | ||
this.new(obj, data.id); | ||
return obj; | ||
} | ||
call(target, method, params) { | ||
const data = params.map(x => this.tson.forward(x)); | ||
const result = this.request({ target, method, data }); | ||
return this.tson.backward(result); | ||
} | ||
handle(request) { | ||
const { target, method, data } = request; | ||
const args = this.tson.backward(data); | ||
const result = this.handler[method].call(this.handler, this.load(target), ...args); | ||
return this.tson.forward(result); | ||
} | ||
delete(id) { | ||
return this.map.delete(id); | ||
} | ||
} | ||
exports.WrappedHandler = WrappedHandler; |
Oops, something went wrong.