debug-server-next
Version:
Dev server for hippy-core.
186 lines (185 loc) • 8.15 kB
JavaScript
;
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);
};