/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {MessageBus} from './message-bus'; import {Events, Topic} from './messages'; type ThrottleTopicDuration = { [method in Topic]?: number; }; type ThrottledTopics = { [method in Topic]?: boolean; }; type TopicsInProgress = { [method in Topic]?: boolean; }; const THROTTLE_METHODS: ThrottleTopicDuration = { getLatestComponentExplorerView: 100, }; type TopicBlockSequence = { [method in Topic]?: Topic[]; }; // We can't refresh the view until we've received // a response with the latest nested properties. const TOPIC_BLOCK_SEQUENCE: TopicBlockSequence = { getLatestComponentExplorerView: ['getNestedProperties'], }; type TopicSequence = { [method in Topic]?: Topic; }; const TOPIC_RESPONSE: TopicSequence = { getNestedProperties: 'nestedProperties', }; const TOPIC_REQUEST: TopicSequence = { nestedProperties: 'getNestedProperties', }; export class PriorityAwareMessageBus extends MessageBus { private _throttled: ThrottledTopics = {}; private _inProgress: TopicsInProgress = {}; constructor( private _bus: MessageBus, private _setTimeout: typeof setTimeout = setTimeout, ) { super(); } override on(topic: E, cb: Events[E]): void { return this._bus.on(topic, (...args: any) => { (cb as any)(...args); this._afterMessage(topic); }); } override once(topic: E, cb: Events[E]): void { return this._bus.once(topic, (...args: any) => { (cb as any)(...args); this._afterMessage(topic); }); } override emit(topic: E, args?: Parameters): boolean { if (this._throttled[topic]) { return false; } if (TOPIC_RESPONSE[topic]) { this._inProgress[topic] = true; } const blockedBy = TOPIC_BLOCK_SEQUENCE[topic]; if (blockedBy) { // The source code here is safe. // TypeScript type inference ignores the null check here. // tslint:disable-next-line: no-non-null-assertion for (const blocker of blockedBy!) { if (this._inProgress[blocker]) { return false; } } } if (THROTTLE_METHODS[topic]) { this._throttled[topic] = true; this._setTimeout(() => (this._throttled[topic] = false), THROTTLE_METHODS[topic]); } return this._bus.emit(topic, args); } override destroy(): void { this._bus.destroy(); } private _afterMessage(topic: Topic): void { const request = TOPIC_REQUEST[topic]; if (!request) { return; } if (this._inProgress[request]) { this._inProgress[request] = false; } } }