UNPKG

universal-message

Version:

A universal message communication library for JavaScript/TypeScript that works across different environments including Worker threads, WebSocket, and other communication channels

718 lines (694 loc) 25 kB
"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 require('class-transformer'); var ECoderAbortController = { target: (t) => t instanceof AbortController, key: "AbortController", encode: (data, tool) => { if (!data.signal.aborted) { const abortHandler = async () => { tool.sendUpdate(data.signal.aborted); tool.remove(); data.signal.removeEventListener("abort", abortHandler); }; data.signal.addEventListener("abort", abortHandler); } return data.signal.aborted; }, decode: (body, tool) => { const data = new AbortController(); if (!!body.data) { data.abort(); } else { const abortHandler = async () => { tool.sendUpdate(data.signal.aborted); tool.remove(); data.signal.removeEventListener("abort", abortHandler); }; data.signal.addEventListener("abort", abortHandler); } return data; }, update: (newValue, target, tool) => { if (newValue) { tool.remove(); target.abort(); } } }; // src/plugs/MessageEcoders.ts var MessageEcoders = [ ECoderAbortController ]; // 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', _ => _.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) }; if (ecoder.update) { this.client.on(id, (value) => { ecoder.update && ecoder.update(value, target, tool, this.client); }); this.client.addParamListener(body.id, id); } 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, ndps)); } 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, ndps); } } 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); if (ecoder.update) { this.client.on(id, (value) => { ecoder.update && ecoder.update(value, target, tool, this.client); }); this.client.addParamListener(body.id, id); } 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, ndps)); } else if (data instanceof Object) { ndps.add(data); for (const key in data) { if (data.hasOwnProperty(key)) { data[key] = this.dataDecode(data[key], body, ndps); } } } 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', _2 => _2.__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 { // 消息ID -> 参数监听器ID集合 constructor(option) {;_class3.prototype.__init2.call(this);_class3.prototype.__init3.call(this);_class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this);_class3.prototype.__init6.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()} // 参数监听器管理 __init5() {this.paramListeners = /* @__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)); } // 添加参数监听器关联 addParamListener(messageId, paramListenerId) { if (!this.paramListeners.has(messageId)) { this.paramListeners.set(messageId, /* @__PURE__ */ new Set()); } this.paramListeners.get(messageId).add(paramListenerId); } // 清理消息相关的所有参数监听器 cleanupParamListeners(messageId) { const listeners = this.paramListeners.get(messageId); if (listeners) { listeners.forEach((listenerId) => { if (this.fns.has(listenerId)) { this.fns.delete(listenerId); console.log(`Cleaned up param listener: ${listenerId}`); } }); this.paramListeners.delete(messageId); } } 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; try { if (!isNull(data.error)) { if (call.reject) { call.reject(data.error); call.reject = void 0; } else { throw new Error(`Message error: ${data.error}`); } } else { if (call.resolve) { call.resolve(body); call.resolve = void 0; } } } finally { this.ons.delete(data.id); this.cleanupParamListeners(data.id); } 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) { let body = { id: msg.id, data: _fastcopy.copyStrict.call(void 0, result), key: msg.key, result: true, error }; body = await this.emitPlugin("onSend", body); body.data = Json2.stringify(body.data); this.option.send(body); } async send(key, data, id = rid(), other = {}) { return new Promise(async (resolve, reject) => { this.ons.set(id, { resolve, reject, once: true }); 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, id = rid()) { return this.send(key, data, id); } //---------- __init6() {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); } // 获取当前活跃连接数 getActiveConnectionsCount() { return this.ons.size; } // 手动清理所有资源 cleanup() { for (const [id, listener] of this.ons) { if (listener.reject) { listener.reject(new Error("Connection cleaned up")); } } this.ons.clear(); this.fns.clear(); this.paramListeners.clear(); console.log("MessageBase cleaned up"); } }, _class3); // src/MessageShared.ts var MessageShared = (_class4 = class extends MessageBase { __init7() {this.shareds = /* @__PURE__ */ new Map()} constructor(option) { super(option);_class4.prototype.__init7.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 await target(body.data); return await objectEval(target, "call", 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 }, 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:close", { id, className }, 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.__init8.call(this);; this.initClass(); } //---------代理类---------- __init8() {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.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;