UNPKG

debug-server-next

Version:

Dev server for hippy-core.

186 lines (185 loc) 8.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.subscribeByIWDP = exports.cleanAllDebugTargets = exports.cleanDebugTarget = exports.subscribeCommand = void 0; const tslib_1 = require("tslib"); const lodash_1 = require("lodash"); const enum_1 = require("@/@types/enum"); const db_1 = require("@/db"); const client_1 = require("@/client"); const middlewares_1 = require("@/middlewares"); const pub_sub_channel_1 = require("@/utils/pub-sub-channel"); const log_1 = require("@/utils/log"); const config_1 = require("@/config"); const log = new log_1.Logger('pub-sub-manager', enum_1.WinstonColor.Cyan); // 保存一个调试页面的缓存数据,key: clientId const channelMap = new Map(); /** * 订阅调试指令,触发时机: * 1. tunnel appConnect * 2. app ws connection * 3. get IWDP pages: 这种场景会触发的很频繁(前端定时器2s请求一次),但是已经订阅过的会直接 return */ const subscribeCommand = (debugTarget, ws) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const { clientId } = debugTarget; if (!channelMap.has(clientId)) addChannelItem(debugTarget); else return; const appClientList = createAppClientList(debugTarget, ws); const { downwardChannelSet, cmdIdChannelIdMap, publisherMap, upwardSubscriber } = channelMap.get(clientId); upwardSubscriber.pUnsubscribe(); upwardSubscriber.disconnect(); const newUpwardSubscriber = createUpwardSubscriber(clientId); channelMap.get(clientId).upwardSubscriber = newUpwardSubscriber; /** * 订阅上行消息。由于可能存在多个 devtools client(如多个插件,每个插件一个通道), * 所以这里应该用批量订阅 pSubscribe */ newUpwardSubscriber.pSubscribe((message, upwardChannelId) => { if (!upwardChannelId) return log.warn('pSubscribe without channelId'); let msgObj; try { msgObj = JSON.parse(message); } catch (e) { log.error('%s channel message are invalid JSON, %s', upwardChannelId, message); } log.info('on channel message, %s, %s, %s', msgObj.id, msgObj.method, upwardChannelId); const downwardChannelId = pub_sub_channel_1.upwardChannelToDownwardChannel(upwardChannelId); cmdIdChannelIdMap.set(msgObj.id, downwardChannelId); downwardChannelSet.add(downwardChannelId); appClientList.forEach((appClient) => { appClient.sendToApp(msgObj).catch((e) => { if (e !== 1 /* DomainFiltered */) { return log.error('%s app client send error: %j', appClient.type, e); } }); }); }); // 发布下行消息 appClientList.forEach((appClient) => { appClient.removeAllListeners("message" /* Message */); appClient.on("message" /* Message */, (msg) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const msgStr = JSON.stringify(msg); const { Publisher } = db_1.getDBOperator(); if ('id' in msg) { // 消息为 CommandRes,根据缓存的 cmdId 查找 downwardChannelId,只发布到该 channel const commandRes = msg; const downwardChannelId = cmdIdChannelIdMap.get(commandRes.id); if (!downwardChannelId) return; if (!publisherMap.has(downwardChannelId)) { const publisher = new Publisher(downwardChannelId); publisherMap.set(downwardChannelId, publisher); } const publisher = publisherMap.get(downwardChannelId); publisher.publish(msgStr); } else { // 消息类型为 EventRes,event 无法确定来源,广播到所有 channel downwardChannelSet.forEach((channelId) => { if (!publisherMap.has(channelId)) { const publisher = new Publisher(channelId); publisherMap.set(channelId, publisher); } const publisher = publisherMap.get(channelId); publisher.publish(msgStr); }); } })); }); }); exports.subscribeCommand = subscribeCommand; /** * 调试结束,清除缓存 * appDisconnect, app ws close 时调用 */ const cleanDebugTarget = (clientId) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const { model } = db_1.getDBOperator(); model.delete(config_1.config.redis.key, clientId); const channelInfo = channelMap.get(clientId); if (!channelInfo) return; const { publisherMap, upwardSubscriber, internalPublisher } = channelInfo; internalPublisher.publish(enum_1.InternalChannelEvent.WSClose); Array.from(publisherMap.values()).forEach((publisher) => publisher.disconnect()); // 稍作延迟,等处理完 InternalChannelEvent.WSClose 事件后再取消订阅 process.nextTick(() => { upwardSubscriber.pUnsubscribe(); upwardSubscriber.disconnect(); internalPublisher.disconnect(); channelMap.delete(clientId); }); }); exports.cleanDebugTarget = cleanDebugTarget; /** * 清除所有调试对象的缓存 */ const cleanAllDebugTargets = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { channelMap.forEach(({ debugTarget }) => { exports.cleanDebugTarget(debugTarget.clientId); }); }); exports.cleanAllDebugTargets = cleanAllDebugTargets; let oldIWDPDebugTargets = []; /** * 订阅 IWDP 获取到的 DebugTarget 上行消息,清理关闭的 IWDP DebugTarget。 * IWDP 检测到的已关闭的页面,清空调试对象缓存 */ const subscribeByIWDP = (debugTargets) => { const removedDebugTargets = lodash_1.differenceBy(oldIWDPDebugTargets, debugTargets, 'clientId'); oldIWDPDebugTargets = debugTargets; removedDebugTargets.forEach(({ clientId }) => { exports.cleanDebugTarget(clientId); }); debugTargets.forEach((debugTarget) => exports.subscribeCommand(debugTarget)); }; exports.subscribeByIWDP = subscribeByIWDP; const addChannelItem = (debugTarget) => { const { clientId } = debugTarget; const { Publisher } = db_1.getDBOperator(); const internalChannelId = pub_sub_channel_1.createInternalChannel(clientId, ''); const upwardSubscriber = createUpwardSubscriber(clientId); const internalPublisher = new Publisher(internalChannelId); channelMap.set(clientId, { downwardChannelSet: new Set(), cmdIdChannelIdMap: new Map(), publisherMap: new Map(), upwardSubscriber, internalPublisher, debugTarget, }); }; const createUpwardSubscriber = (clientId) => { const { Subscriber } = db_1.getDBOperator(); const upwardChannelId = pub_sub_channel_1.createUpwardChannel(clientId, '*'); log.info('subscribe to redis channel %s', upwardChannelId); return new Subscriber(upwardChannelId); }; /** * 根据调试对象创建匹配的调试通道(AppClient) */ const createAppClientList = (debugTarget, ws) => { const { clientId } = debugTarget; const options = client_1.appClientManager.getAppClientOptions(debugTarget.platform); return options .map((_a) => { var _b; var { Ctor: AppClientCtor } = _a, option = tslib_1.__rest(_a, ["Ctor"]); try { log.info(`create app client ${AppClientCtor.name}`); const urlParsedContext = middlewares_1.debugTargetToUrlParsedContext(debugTarget); const newOption = Object.assign(Object.assign({}, option), { urlParsedContext, iWDPWsUrl: debugTarget.iWDPWsUrl }); if (AppClientCtor.name === "WsAppClient" /* WS */) { newOption.ws = ws; } return new AppClientCtor(clientId, newOption); } catch (e) { log.error('create app client error: %s', (_b = e) === null || _b === void 0 ? void 0 : _b.stack); return null; } }) .filter((v) => v); };