universal-message
Version:
A universal message communication library for JavaScript/TypeScript that works across different environments including Worker threads, WebSocket, and other communication channels
706 lines (680 loc) • 24.4 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;// src/plugs/MessageEcoders.ts
require('reflect-metadata');
// src/plugs/ECoders.ts
var _classtransformer = require('class-transformer');
function getClass(data, client) {
for (const [key, value] of client.$class.entries()) {
if (value && data instanceof value) return { value, key };
}
return null;
}
var ECoderAbortController = {
target: (t) => t instanceof AbortController,
key: "AbortController",
encode: (data, tool) => {
data.data;
if (!data.signal.aborted) {
data.signal.addEventListener("abort", async () => {
tool.sendUpdate(data.signal.aborted);
tool.remove();
});
}
return data.signal.aborted;
},
decode: (body, tool) => {
const data = new AbortController();
if (!!body.data) {
data.abort();
} else {
data.signal.addEventListener("abort", async () => {
tool.sendUpdate(data.signal.aborted);
tool.remove();
});
}
return data;
},
update: (newValue, target, tool) => {
if (newValue) {
tool.remove();
target.abort();
}
}
};
var ECoderClassTransformer = {
target: (e, client) => !!getClass(e, client),
key: "ClassTransformer",
encode: (data, tool, client) => {
const cls = getClass(data, client);
if (!cls) return data;
return {
class: cls.key,
data: _classtransformer.instanceToPlain.call(void 0, data)
};
},
decode: (body, tool, client) => {
if (!_optionalChain([body, 'optionalAccess', _ => _.data])) return body;
const { class: classKey, data } = body.data;
const cls = client.$class.get(classKey);
if (!cls) return body.data;
if (!data) return body.data;
const instance = _classtransformer.plainToInstance.call(void 0, cls, data);
return instance;
}
};
// src/plugs/MessageEcoders.ts
var MessageEcoders = [
ECoderAbortController,
ECoderClassTransformer
];
// src/tools.ts
var rid = () => Math.random().toString(36).substring(2, 15);
function isNull(data) {
return data === null || data === void 0;
}
async function objectEval(instance, type, prop, data) {
if (type === "get") {
return new Function("obj", `return obj.` + prop)(instance);
} else if (type === "set") {
return new Function("obj", "value", `obj.${prop}=value`)(instance, data);
} else if (type === "call") {
return await new Function("obj", "value", `return obj.${prop}(...value)`)(instance, data);
} else if (type === "delete") {
return await new Function("obj", `delete obj.${prop}`)(instance);
}
}
// src/plugs/MessageEcoderPlugin.ts
var MessageEcoderPlugin = (_class = class {
constructor(client) {;_class.prototype.__init.call(this);
this.client = client;
}
__init() {this.name = "MessageEcoderPlugin"}
async onSend(body) {
if (isNull(_optionalChain([body, 'optionalAccess', _2 => _2.data]))) return body;
body.data = this.dataEncode(body.data, body);
return body;
}
async onMessage(body) {
body.data = this.dataDecode(body.data, body);
return body;
}
dataEncode(data, body, ndps = /* @__PURE__ */ new Set()) {
if (ndps.has(data)) return data;
for (const ecoder of MessageEcoders) {
if (ecoder.target(data, this.client)) {
const target = data;
const id = "$args:" + ecoder.key + ":" + rid();
const tool = {
sendUpdate: (value) => {
if (!this.client.hasOn(id)) return;
this.client.emit(id, value);
},
remove: () => this.client.removeAll(id)
};
ecoder.update && this.client.on(id, (value) => {
ecoder.update && ecoder.update(value, target, tool, this.client);
});
const res = ecoder.encode(data, tool, this.client);
return {
id,
key: ecoder.key,
data: res
};
}
}
if (data instanceof Array) {
ndps.add(data);
return data.map((item) => this.dataEncode(item, body));
} else if (data instanceof Object && data !== null) {
ndps.add(data);
let ndata = {};
for (const key in data) {
if (data.hasOwnProperty(key)) {
ndata[key] = this.dataEncode(data[key], body);
}
}
return ndata;
}
return data;
}
dataDecode(data, body, ndps = /* @__PURE__ */ new Set()) {
if (isNull(data)) return data;
if (ndps.has(data)) return data;
for (const ecoder of MessageEcoders.toReversed()) {
if (ecoder.key === data.key) {
const id = data.id;
const tool = {
sendUpdate: (value) => {
if (!this.client.hasOn(id)) return;
this.client.emit(id, value);
},
remove: () => this.client.removeAll(id)
};
try {
const target = ecoder.decode(data, tool, this.client);
ecoder.update && this.client.on(id, (value) => {
ecoder.update && ecoder.update(value, target, tool, this.client);
});
return target;
} catch (e) {
console.error("Decoder Error:", ecoder.key, data, e);
return data;
}
}
}
if (data instanceof Array) {
ndps.add(data);
data = data.map((item) => this.dataDecode(item, body));
} else if (data instanceof Object) {
ndps.add(data);
for (const key in data) {
if (data.hasOwnProperty(key)) {
data[key] = this.dataDecode(data[key], body);
}
}
}
return data;
}
}, _class);
// src/MessageBase.ts
// src/utils/json2.ts
function deepObject(data, callback) {
for (const key in data) {
if (data.hasOwnProperty(key)) {
data[key] = callback(data[key]);
}
}
return data;
}
var Json2 = (_class2 = class {
static __initStatic() {this.key = "$$ref:"}
static stringify(data) {
data = this.dataToRefs(data);
data = this.encoder(data);
return JSON.stringify(data);
}
//处理对象中的嵌套引用
static dataToRefs(data) {
const refs = [data];
for (let index = 0; index < refs.length; index++) {
let data2 = refs[index];
if (data2 instanceof Array) {
data2 = data2.map((x) => this.addList(x, refs));
} else if (data2 instanceof Map) {
const newMap = /* @__PURE__ */ new Map();
for (const [key, value] of data2.entries()) {
newMap.set(key, this.addList(value, refs));
}
data2 = newMap;
} else if (data2 instanceof Set) {
const newSet = /* @__PURE__ */ new Set();
for (const value of data2.values()) {
newSet.add(this.addList(value, refs));
}
data2 = newSet;
} else if (typeof data2 === "object" && data2 !== null) {
Object.keys(data2).forEach((key) => {
data2[key] = this.addList(data2[key], refs);
});
}
refs[index] = data2;
}
return refs;
}
static addList(data, list) {
if (typeof data === "object" && data !== null) {
if (!list.find((x) => x === data)) {
list.push(data);
}
return this.key + list.indexOf(data);
}
return data;
}
static refsToData(refs) {
const dataDecoder = (data) => {
if (typeof data === "string" && data.startsWith(this.key)) {
const index = parseInt(data.substring(this.key.length));
return refs[index];
} else if (Array.isArray(data)) {
return data.map((item) => dataDecoder(item));
} else if (data instanceof Map) {
const newMap = /* @__PURE__ */ new Map();
for (const [key, value] of data.entries()) {
newMap.set(key, dataDecoder(value));
}
return newMap;
} else if (data instanceof Set) {
const newSet = /* @__PURE__ */ new Set();
for (const value of data.values()) {
newSet.add(dataDecoder(value));
}
return newSet;
} else if (data instanceof Object) {
const newObj = {};
for (const key in data) {
if (data.hasOwnProperty(key)) {
newObj[key] = dataDecoder(data[key]);
}
}
return newObj;
}
return data;
};
for (let index = refs.length - 1; index >= 0; index--) {
let data = refs[index];
if (data instanceof Array) {
refs[index] = data.map((x) => dataDecoder(x));
} else if (data instanceof Map) {
const newMap = /* @__PURE__ */ new Map();
for (const [key, value] of data.entries()) {
newMap.set(key, dataDecoder(value));
}
refs[index] = newMap;
} else if (data instanceof Set) {
const newSet = /* @__PURE__ */ new Set();
for (const value of data.values()) {
newSet.add(dataDecoder(value));
}
refs[index] = newSet;
} else if (typeof data === "object" && data !== null) {
const newObj = {};
Object.keys(data).forEach((key) => {
newObj[key] = dataDecoder(data[key]);
});
refs[index] = newObj;
} else {
refs[index] = dataDecoder(data);
}
}
return refs[0];
}
static encoder(data) {
if (Array.isArray(data)) {
return data.map((item) => this.encoder(item));
} else if (data instanceof Date) {
return { __e_type__: "date", value: data.getTime() };
} else if (data instanceof Map) {
let obj = Object.fromEntries(data.entries());
obj = deepObject(obj, (data2) => this.encoder(data2));
return { __e_type__: "map", value: obj };
} else if (data instanceof Set) {
return { __e_type__: "set", value: Array.from(data).map((x) => this.encoder(x)) };
} else if (data instanceof RegExp) {
return { __e_type__: "regexp", value: data.toString() };
} else if (data instanceof Error) {
return { __e_type__: "error", value: data.message, stack: data.stack };
} else if (data instanceof URL) {
return { __e_type__: "url", value: data.toString() };
} else if (typeof Blob !== "undefined" && data instanceof Blob) {
return { __e_type__: "blob", value: data };
} else if (data instanceof ArrayBuffer) {
return {
__e_type__: "arraybuffer",
value: Array.from(new Uint8Array(data))
};
} else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
return { __e_type__: "buffer", value: Array.from(data) };
} else if (data instanceof BigInt || typeof data === "bigint") {
return { __e_type__: "bigint", value: data.toString() };
} else if (data instanceof BigInt64Array) {
return { __e_type__: "bigint64array", value: Array.from(data) };
} else if (data instanceof BigUint64Array) {
return { __e_type__: "biguint64array", value: Array.from(data) };
}
if (typeof data === "object") {
return deepObject(data, (data2) => this.encoder(data2));
}
return data;
}
static parse(str) {
let start = Date.now();
let data = JSON.parse(str);
data = this.decoder(data);
data = this.refsToData(data);
return data;
}
static decoder(data) {
if (_optionalChain([data, 'optionalAccess', _3 => _3.__e_type__])) {
const type = data.__e_type__;
const value = data.value;
if (type === "date") {
return new Date(value);
} else if (type === "map") {
const map = /* @__PURE__ */ new Map();
for (const key in value) {
if (value.hasOwnProperty(key)) {
map.set(key, this.decoder(value[key]));
}
}
return map;
} else if (type === "set") {
return new Set(value.map((x) => this.decoder(x)));
} else if (type === "regexp") {
return new RegExp(value);
} else if (type === "error") {
const error = new Error(value);
if (data.stack) {
error.stack = data.stack;
}
return error;
} else if (type === "url") {
return new URL(value);
} else if (type === "blob") {
if (typeof Blob !== "undefined") return new Blob([value]);
return new Uint8Array(value);
} else if (type === "arraybuffer") {
return new Uint8Array(value).buffer;
} else if (type === "buffer") {
if (typeof Buffer !== "undefined") return Buffer.from(value);
return new ArrayBuffer(value);
} else if (type === "bigint") {
return BigInt(value);
} else if (type === "bigint64array") {
return BigInt64Array.from(value);
} else if (type === "biguint64array") {
return BigUint64Array.from(value);
}
}
if (data instanceof Array) {
return data.map((item) => this.decoder(item));
} else if (data instanceof Object) {
data = deepObject(data, (dta) => this.decoder(dta));
}
return data;
}
}, _class2.__initStatic(), _class2);
// src/MessageBase.ts
var _fastcopy = require('fast-copy');
var MessageBase = (_class3 = class {
constructor(option) {;_class3.prototype.__init2.call(this);_class3.prototype.__init3.call(this);_class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this);
this.option = option;
this.init();
}
__init2() {this.ons = /* @__PURE__ */ new Map()}
__init3() {this.fns = /* @__PURE__ */ new Map()}
__init4() {this.plugins = /* @__PURE__ */ new Map()}
init() {
this.option.on((data) => {
if (data instanceof MessageEvent) {
data = data.data;
}
this.listenCallback(data);
});
this.on("$message:base:listen:remove", ({ key }) => this.ons.delete(key));
this.addPlugin(new MessageEcoderPlugin(this));
}
addPlugin(plugin) {
this.plugins.set(plugin.name, plugin);
}
removePlugin(name) {
this.plugins.delete(name);
}
async emitPlugin(eventName, data) {
for (const [name, plugin] of this.plugins) {
if (plugin[eventName]) {
try {
data = await plugin[eventName](data);
} catch (e) {
console.error(`Plugin ${name} error on ${eventName}:`, e);
}
}
}
return data;
}
async listenCallback(data) {
if (!(data && data.id && data.key)) return;
data.data = Json2.parse(data.data || "null");
const result = (result2) => this.callback(data, result2);
const error = (err) => this.callback(data, null, err);
data = await this.emitPlugin("onMessage", data);
const body = data.data;
if (data.result) {
const call = this.ons.get(data.id);
if (!call) return;
if (!isNull(data.error)) {
this.ons.delete(data.id);
if (call.reject) {
call.reject(data.error);
call.reject = void 0;
} else {
throw new Error(`Worker error: ${data.error}`);
}
return;
}
if (call.resolve) {
call.resolve(body);
call.resolve = void 0;
if (!call.callback) {
this.ons.delete(data.id);
}
}
if (call.callback) {
call.callback(body);
}
return;
}
const fn = this.fns.get(data.key);
if (!fn) {
return error(new Error(`Worker function not found: ${data.key}`));
}
try {
result(await fn(body, data));
} catch (e) {
error(e);
} finally {
}
}
async callback(msg, result, error) {
return await this.send(msg.key, result, void 0, msg.id, { result: true, error });
}
async send(key, data, callback, id = rid(), other = {}) {
return new Promise(async (resolve, reject) => {
const back = (result) => {
if (callback) {
callback(result);
}
resolve(result);
};
this.ons.set(id, {
resolve,
reject,
callback: back,
once: !callback
});
let body = { id, data: _fastcopy.copyStrict.call(void 0, data), key, ...other };
body = await this.emitPlugin("onSend", body);
body.data = Json2.stringify(body.data);
this.option.send(body);
});
}
on(key, callback) {
this.fns.set(key, callback);
}
hasOn(key) {
return this.fns.has(key);
}
remove(key) {
this.fns.delete(key);
}
removeAll(key) {
if (!this.hasOn(key)) return;
this.remove(key);
this.send("$message:base:listen:remove", { key });
}
async emit(key, data, callback, id = rid()) {
return this.send(key, data, callback, id);
}
//----------
__init5() {this.$class = /* @__PURE__ */ new Map()}
//存储类
/**
* 添加代理类
* @param target 未初始化的类
*/
addClass(target) {
if (typeof target !== "function") throw new Error("Must provide a class constructor");
const name = target.name;
if (!name) throw new Error("Class must have a name");
this.$class.set(name, target);
}
}, _class3);
// src/MessageShared.ts
var MessageShared = (_class4 = class extends MessageBase {
__init6() {this.shareds = /* @__PURE__ */ new Map()}
constructor(option) {
super(option);_class4.prototype.__init6.call(this);;
this.on("$shared:type", async (body, msg) => {
const value = this.checkVar(body);
return typeof value;
});
this.on("$shared:get", async (body, msg) => {
const target = this.checkVar(body);
if (!body.attr) return target;
return await objectEval(target, "get", body.attr);
});
this.on("$shared:set", async (body, msg) => {
const target = this.checkVar(body);
if (!body.attr) {
this.shareds.set(body.key, body.data);
return;
}
return await objectEval(target, "set", body.attr);
});
this.on("$shared:call", async (body, msg) => {
const target = this.checkVar(body);
if (!body.attr) return target(body.data);
return await objectEval(target, "get", body.attr, body.data);
});
this.on("$shared:remove", async (body, msg) => {
const target = this.shareds.get(body.key);
if (!target) return;
if (!body.attr) {
this.shareds.delete(body.key);
return;
}
return await objectEval(target, "delete", body.attr);
});
}
checkVar(body) {
const target = this.shareds.get(body.key);
if (!target) throw new Error(`Variable ${body.key} not found`);
return target;
}
addShared(key, target) {
this.shareds.set(key, target);
}
delShared(key) {
this.shareds.delete(key);
}
loadShared(key) {
return {
value: async () => this.send("$shared:get", { key }, void 0),
del: async (attr) => this.send("$shared:remove", { key, attr }, void 0),
set: async (attr = "", data) => this.send("$shared:set", { key, data, attr }, void 0),
call: async (attr = "", ...data) => this.send("$shared:call", { key, data, attr }, void 0),
get: async (attr) => this.send("$shared:get", { key, attr }, void 0),
close: async () => this.send("$shared:remove", { key }, void 0)
};
}
}, _class4);
// src/MessageServer.ts
var MessageServer = class extends MessageShared {
constructor(option) {
super(option);
}
/**
* [发送] 初始化类
* @param target 对象或类名
* @param data
* @returns
*/
async newClass(target, ...data) {
const id = rid();
const className = typeof target === "string" ? target : target.name;
await this.send("$class:new", { className, data }, void 0, id);
return {
get: async (key) => await this.send("$class:get", { key, className, id }, void 0),
set: async (key, data2) => await this.send("$class:set", { key, id, className, data: data2 }, void 0),
call: async (key, ...data2) => await this.send("$class:call", { key, id, className, data: data2 }, void 0),
close: async () => await this.send("$class:$class:close", { id, className, data }, void 0),
static: this.staticClass(className)
};
}
/**
* [发送] 获取类静态方法
* @param target 对象或类名
* @returns
*/
staticClass(target) {
const className = typeof target === "string" ? target : target.name;
return {
get: async (key) => await this.send("$class:static:get", { key, className }, void 0),
set: async (key, data) => await this.send("$class:static:set", { key, className, data }, void 0),
call: async (key, ...data) => await this.send("$class:static:call", { key, className, data }, void 0),
close: async () => await this.send("$class:static:close", { className }, void 0)
};
}
};
// src/MessageClient.ts
var MessageClient = (_class5 = class extends MessageShared {
constructor(option) {
super(option);_class5.prototype.__init7.call(this);;
this.initClass();
}
//---------代理类----------
__init7() {this.clsNews = /* @__PURE__ */ new Map()}
//存储类实例
initClass() {
this.on("$class:new", async (body, msg) => {
if (!body.className) throw new Error("Class name is required");
const target = this.$class.get(body.className);
if (!target) throw new Error(`Class ${body.className} not found`);
const instance = new target(...body.data);
this.clsNews.set(msg.id, instance);
});
this.on("$class:get", async (body, msg) => {
if (!body.id) throw new Error(`Class instance id is required`);
const target = this.clsNews.get(body.id);
if (!target) throw new Error(`The ${body.className} instance does not exist. ` + body.id);
return await objectEval(target, "get", body.key);
});
this.on("$class:set", async (body, msg) => {
if (!body.id) throw new Error(`Class instance id is required`);
const target = this.clsNews.get(body.id);
if (!target) throw new Error(`The ${body.className} instance does not exist. ` + body.id);
return await objectEval(target, "set", body.key, body.data);
});
this.on("$class:call", async (body, msg) => {
if (!body.id) throw new Error(`Class instance id is required`);
const target = this.clsNews.get(body.id);
if (!target) throw new Error(`The ${body.className} instance does not exist. ` + body.id);
return await objectEval(target, "call", body.key, body.data);
});
this.on("$class:close", async (body, msg) => {
if (!body.id) throw new Error(`Class instance id is required`);
this.clsNews.delete(body.id);
});
this.on("$class:static:get", async (body, msg) => {
if (!body.className) throw new Error("Class name is required");
const target = this.$class.get(body.className);
if (!target) throw new Error(`Class ${body.className} not found`);
return await objectEval(target, "get", body.key);
});
this.on("$class:static:set", async (body, msg) => {
if (!body.className) throw new Error("Class name is required");
const target = this.$class.get(body.className);
if (!target) throw new Error(`Class ${body.className} not found`);
return await objectEval(target, "set", body.key, body.data);
});
this.on("$class:static:call", async (body, msg) => {
if (!body.className) throw new Error("Class name is required");
const target = this.$class.get(body.className);
if (!target) throw new Error(`Class ${body.className} not found`);
return await objectEval(target, "call", body.key, body.data);
});
this.on("$class:static:close", async (body, msg) => {
if (!body.className) throw new Error("Class name is required");
this.$class.delete(body.className);
});
}
}, _class5);
exports.ECoderAbortController = ECoderAbortController; exports.ECoderClassTransformer = ECoderClassTransformer; exports.Json2 = Json2; exports.MessageBase = MessageBase; exports.MessageClient = MessageClient; exports.MessageEcoderPlugin = MessageEcoderPlugin; exports.MessageEcoders = MessageEcoders; exports.MessageServer = MessageServer; exports.MessageShared = MessageShared; exports.isNull = isNull; exports.objectEval = objectEval; exports.rid = rid;