namespace Leaf { export class Serializer { typeKey = "_$t" dataKey = "_$d" typeMap: { [type: string]: Serializer.ConvertConfig } = { "date": { Cons: Date, fromType: (date: Date) => { return date.toString() }, toType: (str: any) => { return new Date(str) } }, "reg": { Cons: RegExp, fromType: (reg: RegExp) => { return { s: reg.source, g: reg.global, i: reg.ignoreCase, m: reg.multiline } }, toType: (data: any) => { let mod = `${data.g && "g"}${data.m && "m"}${data.i && "i"}` return new RegExp(data.s, mod) } }, "error": { Cons: Error, fromType: (error: Error) => { let result = {} for (let key in error) { if (typeof error[key] !== "function") { if (error.hasOwnProperty(key)) { result[key] = error[key] } } } result["name"] = error.name result["message"] = error.message result["stack"] = error.stack return result }, toType: (data: any) => { if (!data) { return data } let error = new Error(data.message) for (let prop in data) { error[prop] = data[prop] } return error } } } private tokenize(data) { if (!data) return data if (Array.isArray(data)) { return data.map(item => this.tokenize(item)) } if (typeof data !== "object") return data for (let type in this.typeMap) { let info = this.typeMap[type] if (data instanceof info.Cons) { return { [this.typeKey]: type, [this.dataKey]: info.fromType(data) } } } let result = {} for (let k in data) { result[k] = this.tokenize(data[k]) } return result } private detokenize(v) { if (!v) return v if (Array.isArray(v)) { return v.map(item => this.detokenize(item)) } if (typeof v !== "object") return v if (!v[this.typeKey]) { let result = {} for (let k in v) { result[k] = this.detokenize(v[k]) } return result } let info = this.typeMap[v[this.typeKey]] if (!info) return v return info.toType(v[this.dataKey]) } serialize(data: any): string { return JSON.stringify(this.tokenize(data)) } deserialize(data: any) { return this.detokenize(JSON.parse(data)) } register(type: string, config: Serializer.ConvertConfig) { this.typeMap[type] = config } static defaultSerializer: Serializer static getDefault() { if (!this.defaultSerializer) this.defaultSerializer = new Serializer() return this.defaultSerializer } } export namespace Serializer { export type Constructor = new (...args: any[]) => T; export interface ConvertConfig { Cons: Constructor fromType: (T) => V toType: (V) => T } } }