UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

692 lines 31.7 kB
// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import * as Host from '../../core/host/host.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Platform from '../../core/platform/platform.js'; import * as ProtocolClient from '../../core/protocol_client/protocol_client.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as Bindings from '../../models/bindings/bindings.js'; import * as TextUtils from '../../models/text_utils/text_utils.js'; import * as DataGrid from '../../ui/components/data_grid/data_grid.js'; import * as IconButton from '../../ui/components/icon_button/icon_button.js'; import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js'; import * as UI from '../../ui/legacy/legacy.js'; import * as LitHtml from '../../ui/lit-html/lit-html.js'; import * as Components from './components/components.js'; import protocolMonitorStyles from './protocolMonitor.css.js'; const UIStrings = { /** *@description Text for one or a group of functions */ method: 'Method', /** * @description Text in Protocol Monitor. Title for a table column which shows in which direction * the particular protocol message was travelling. Values in this column will either be 'sent' or * 'received'. */ type: 'Type', /** * @description Text in Protocol Monitor of the Protocol Monitor tab. Noun relating to a network request. */ request: 'Request', /** *@description Title of a cell content in protocol monitor. A Network response refers to the act of acknowledging a network request. Should not be confused with answer. */ response: 'Response', /** *@description Text for timestamps of items */ timestamp: 'Timestamp', /** *@description Title of a cell content in protocol monitor. It describes the time between sending a request and receiving a response. */ elapsedTime: 'Elapsed time', /** *@description Text in Protocol Monitor of the Protocol Monitor tab */ target: 'Target', /** *@description Text to record a series of actions for analysis */ record: 'Record', /** *@description Text to clear everything */ clearAll: 'Clear all', /** *@description Text to filter result items */ filter: 'Filter', /** *@description Text for the documentation of something */ documentation: 'Documentation', /** *@description Text to open the CDP editor with the selected command */ editAndResend: 'Edit and resend', /** *@description Cell text content in Protocol Monitor of the Protocol Monitor tab *@example {30} PH1 */ sMs: '{PH1} ms', /** *@description Text in Protocol Monitor of the Protocol Monitor tab */ noMessageSelected: 'No message selected', /** *@description Text in Protocol Monitor for the save button */ save: 'Save', /** *@description Text in Protocol Monitor to describe the sessions column */ session: 'Session', /** *@description A placeholder for an input in Protocol Monitor. The input accepts commands that are sent to the backend on Enter. CDP stands for Chrome DevTools Protocol. */ sendRawCDPCommand: 'Send a raw `CDP` command', /** * @description A tooltip text for the input in the Protocol Monitor panel. The tooltip describes what format is expected. */ sendRawCDPCommandExplanation: 'Format: `\'Domain.commandName\'` for a command without parameters, or `\'{"command":"Domain.commandName", "parameters": {...}}\'` as a JSON object for a command with parameters. `\'cmd\'`/`\'method\'` and `\'args\'`/`\'params\'`/`\'arguments\'` are also supported as alternative keys for the `JSON` object.', /** * @description A label for a select input that allows selecting a CDP target to send the commands to. */ selectTarget: 'Select a target', /** * @description Tooltip for the the console sidebar toggle in the Console panel. Command to * open/show the sidebar. */ showCDPCommandEditor: 'Show CDP command editor', /** * @description Tooltip for the the console sidebar toggle in the Console panel. Command to * open/show the sidebar. */ hideCDPCommandEditor: 'Hide CDP command editor', /** * @description Screen reader announcement when the sidebar is shown in the Console panel. */ CDPCommandEditorShown: 'CDP command editor shown', /** * @description Screen reader announcement when the sidebar is hidden in the Console panel. */ CDPCommandEditorHidden: 'CDP command editor hidden', }; const str_ = i18n.i18n.registerUIStrings('panels/protocol_monitor/ProtocolMonitor.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); const timeRenderer = (value) => { return LitHtml.html `${i18nString(UIStrings.sMs, { PH1: String(value) })}`; }; export const buildProtocolMetadata = (domains) => { const metadataByCommand = new Map(); for (const domain of domains) { for (const command of Object.keys(domain.metadata)) { metadataByCommand.set(command, domain.metadata[command]); } } return metadataByCommand; }; const metadataByCommand = buildProtocolMetadata(ProtocolClient.InspectorBackend.inspectorBackend.agentPrototypes.values()); const typesByName = ProtocolClient.InspectorBackend.inspectorBackend.typeMap; const enumsByName = ProtocolClient.InspectorBackend.inspectorBackend.enumMap; export class ProtocolMonitorDataGrid extends Common.ObjectWrapper.eventMixin(UI.Widget.VBox) { started; startTime; requestTimeForId; dataGridRowForId; infoWidget; dataGridIntegrator; filterParser; suggestionBuilder; textFilterUI; messages = []; isRecording = false; #commandAutocompleteSuggestionProvider = new CommandAutocompleteSuggestionProvider(); #selectedTargetId; #commandInput; constructor(splitWidget) { super(true); this.started = false; this.startTime = 0; this.dataGridRowForId = new Map(); this.requestTimeForId = new Map(); const topToolbar = new UI.Toolbar.Toolbar('protocol-monitor-toolbar', this.contentElement); this.contentElement.classList.add('protocol-monitor'); const recordButton = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.record), 'record-start', 'record-stop'); recordButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => { recordButton.setToggled(!recordButton.toggled()); this.setRecording(recordButton.toggled()); }); recordButton.setToggleWithRedColor(true); topToolbar.appendToolbarItem(recordButton); recordButton.setToggled(true); const clearButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clearAll), 'clear'); clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => { this.messages = []; this.dataGridIntegrator.update({ ...this.dataGridIntegrator.data(), rows: [] }); this.infoWidget.render(null); }); topToolbar.appendToolbarItem(clearButton); const saveButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.save), 'download'); saveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => { void this.saveAsFile(); }); topToolbar.appendToolbarItem(saveButton); this.infoWidget = new InfoWidget(); const dataGridInitialData = { paddingRowsCount: 100, showScrollbar: true, columns: [ { id: 'type', title: i18nString(UIStrings.type), sortable: true, widthWeighting: 1, visible: true, hideable: true, styles: { 'text-align': 'center', }, }, { id: 'method', title: i18nString(UIStrings.method), sortable: false, widthWeighting: 5, visible: true, hideable: false, }, { id: 'request', title: i18nString(UIStrings.request), sortable: false, widthWeighting: 5, visible: true, hideable: true, }, { id: 'response', title: i18nString(UIStrings.response), sortable: false, widthWeighting: 5, visible: true, hideable: true, }, { id: 'elapsedTime', title: i18nString(UIStrings.elapsedTime), sortable: true, widthWeighting: 2, visible: true, hideable: true, }, { id: 'timestamp', title: i18nString(UIStrings.timestamp), sortable: true, widthWeighting: 5, visible: false, hideable: true, }, { id: 'target', title: i18nString(UIStrings.target), sortable: true, widthWeighting: 5, visible: false, hideable: true, }, { id: 'session', title: i18nString(UIStrings.session), sortable: true, widthWeighting: 5, visible: false, hideable: true, }, ], rows: [], contextMenus: { bodyRow: (menu, columns, row) => { const methodColumn = DataGrid.DataGridUtils.getRowEntryForColumnId(row, 'method'); const typeColumn = DataGrid.DataGridUtils.getRowEntryForColumnId(row, 'type'); /** * You can click the "Edit and resend" item in the context menu to be * taken to the CDP editor with the filled with the selected command. */ menu.editSection().appendItem(i18nString(UIStrings.editAndResend), () => { if (!methodColumn.value) { return; } const parameters = this.infoWidget.request; const targetId = this.infoWidget.targetId; const command = String(methodColumn.value); if (splitWidget.showMode() === UI.SplitWidget.ShowMode.OnlyMain) { splitWidget.toggleSidebar(); } this.dispatchEventToListeners(Events.CommandChange, { command, parameters, targetId }); }); /** * You can click the "Filter" item in the context menu to filter the * protocol monitor entries to those that match the method of the * current row. */ menu.editSection().appendItem(i18nString(UIStrings.filter), () => { const methodColumn = DataGrid.DataGridUtils.getRowEntryForColumnId(row, 'method'); this.textFilterUI.setValue(`method:${methodColumn.value}`, true); }); /** * You can click the "Documentation" item in the context menu to be * taken to the CDP Documentation site entry for the given method. */ menu.footerSection().appendItem(i18nString(UIStrings.documentation), () => { if (!methodColumn.value) { return; } const [domain, method] = String(methodColumn.value).split('.'); const type = typeColumn.value === 'sent' ? 'method' : 'event'; Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(`https://chromedevtools.github.io/devtools-protocol/tot/${domain}#${type}-${method}`); }); }, }, }; this.dataGridIntegrator = new DataGrid.DataGridControllerIntegrator.DataGridControllerIntegrator(dataGridInitialData); this.dataGridIntegrator.dataGrid.addEventListener('cellfocused', event => { const focusedRow = event.data.row; const infoWidgetData = { request: DataGrid.DataGridUtils.getRowEntryForColumnId(focusedRow, 'request'), response: DataGrid.DataGridUtils.getRowEntryForColumnId(focusedRow, 'response'), target: DataGrid.DataGridUtils.getRowEntryForColumnId(focusedRow, 'target'), type: DataGrid.DataGridUtils.getRowEntryForColumnId(focusedRow, 'type').title, }; this.infoWidget.render(infoWidgetData); }); this.dataGridIntegrator.dataGrid.addEventListener('newuserfiltertext', event => { this.textFilterUI.setValue(event.data.filterText, /* notify listeners */ true); }); const split = new UI.SplitWidget.SplitWidget(true, true, 'protocol-monitor-panel-split', 250); split.show(this.contentElement); split.setMainWidget(this.dataGridIntegrator); split.setSidebarWidget(this.infoWidget); const keys = ['method', 'request', 'response', 'type', 'target', 'session']; this.filterParser = new TextUtils.TextUtils.FilterParser(keys); this.suggestionBuilder = new UI.FilterSuggestionBuilder.FilterSuggestionBuilder(keys); this.textFilterUI = new UI.Toolbar.ToolbarInput(i18nString(UIStrings.filter), '', 1, .2, '', this.suggestionBuilder.completions.bind(this.suggestionBuilder), true); this.textFilterUI.addEventListener(UI.Toolbar.ToolbarInput.Event.TextChanged, event => { const query = event.data; const filters = this.filterParser.parse(query); this.dataGridIntegrator.update({ ...this.dataGridIntegrator.data(), filters }); }); const bottomToolbar = new UI.Toolbar.Toolbar('protocol-monitor-bottom-toolbar', this.contentElement); bottomToolbar.appendToolbarItem(splitWidget.createShowHideSidebarButton(i18nString(UIStrings.showCDPCommandEditor), i18nString(UIStrings.hideCDPCommandEditor), i18nString(UIStrings.CDPCommandEditorShown), i18nString(UIStrings.CDPCommandEditorHidden))); this.#commandInput = this.#createCommandInput(); bottomToolbar.appendToolbarItem(this.#commandInput); bottomToolbar.appendToolbarItem(this.#createTargetSelector()); const shadowRoot = bottomToolbar.element?.shadowRoot; const inputBar = shadowRoot?.querySelector('.toolbar-input'); const tabSelector = shadowRoot?.querySelector('.toolbar-select-container'); const populateToolbarInput = () => { const editorWidget = splitWidget.sidebarWidget(); if (!(editorWidget instanceof EditorWidget)) { return; } const commandJson = editorWidget.jsonEditor.getCommandJson(); if (commandJson) { this.#commandInput.setValue(commandJson); } }; splitWidget.addEventListener(UI.SplitWidget.Events.ShowModeChanged, (event => { if (event.data === 'OnlyMain') { populateToolbarInput(); inputBar?.setAttribute('style', 'display:flex; flex-grow: 1'); tabSelector?.setAttribute('style', 'display:flex'); } else { const { command, parameters } = parseCommandInput(this.#commandInput.value()); this.dispatchEventToListeners(Events.CommandChange, { command, parameters, targetId: this.#selectedTargetId }); inputBar?.setAttribute('style', 'display:none'); tabSelector?.setAttribute('style', 'display:none'); } })); topToolbar.appendToolbarItem(this.textFilterUI); } #createCommandInput() { const placeholder = i18nString(UIStrings.sendRawCDPCommand); const accessiblePlaceholder = placeholder; const growFactor = 1; const shrinkFactor = 0.2; const tooltip = i18nString(UIStrings.sendRawCDPCommandExplanation); const input = new UI.Toolbar.ToolbarInput(placeholder, accessiblePlaceholder, growFactor, shrinkFactor, tooltip, this.#commandAutocompleteSuggestionProvider.buildTextPromptCompletions, false); input.addEventListener(UI.Toolbar.ToolbarInput.Event.EnterPressed, () => { this.#commandAutocompleteSuggestionProvider.addEntry(input.value()); const { command, parameters } = parseCommandInput(input.value()); this.onCommandSend(command, parameters, this.#selectedTargetId); }); return input; } #createTargetSelector() { const selector = new UI.Toolbar.ToolbarComboBox(() => { this.#selectedTargetId = selector.selectedOption()?.value; }, i18nString(UIStrings.selectTarget)); selector.setMaxWidth(120); const targetManager = SDK.TargetManager.TargetManager.instance(); const syncTargets = () => { selector.removeOptions(); for (const target of targetManager.targets()) { selector.createOption(`${target.name()} (${target.inspectedURL()})`, target.id()); } }; targetManager.addEventListener(SDK.TargetManager.Events.AvailableTargetsChanged, syncTargets); syncTargets(); return selector; } onCommandSend(command, parameters, target) { const test = ProtocolClient.InspectorBackend.test; const targetManager = SDK.TargetManager.TargetManager.instance(); const selectedTarget = target ? targetManager.targetById(target) : null; const sessionId = selectedTarget ? selectedTarget.sessionId : ''; // TS thinks that properties are read-only because // in TS test is defined as a namespace. // @ts-ignore test.sendRawMessage(command, parameters, () => { }, sessionId); } static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!protocolMonitorImplInstance || forceNew) { protocolMonitorImplInstance = new ProtocolMonitorImpl(); } return protocolMonitorImplInstance; } wasShown() { if (this.started) { return; } this.registerCSSFiles([protocolMonitorStyles]); this.started = true; this.startTime = Date.now(); this.setRecording(true); } setRecording(recording) { this.isRecording = recording; const test = ProtocolClient.InspectorBackend.test; if (recording) { // TODO: TS thinks that properties are read-only because // in TS test is defined as a namespace. // @ts-ignore test.onMessageSent = this.messageSent.bind(this); // @ts-ignore test.onMessageReceived = this.messageReceived.bind(this); } else { // @ts-ignore test.onMessageSent = null; // @ts-ignore test.onMessageReceived = null; } } targetToString(target) { if (!target) { return ''; } return target.decorateLabel(`${target.name()} ${target === SDK.TargetManager.TargetManager.instance().rootTarget() ? '' : target.id()}`); } // eslint-disable messageReceived(message, target) { if (this.isRecording) { this.messages.push({ ...message, type: 'recv', domain: '-' }); } if ('id' in message && message.id) { const existingRow = this.dataGridRowForId.get(message.id); if (!existingRow) { return; } const allExistingRows = this.dataGridIntegrator.data().rows; const matchingExistingRowIndex = allExistingRows.findIndex(r => existingRow === r); const newRowWithUpdate = { ...existingRow, cells: existingRow.cells.map(cell => { if (cell.columnId === 'response') { return { ...cell, value: JSON.stringify(message.result || message.error), }; } if (cell.columnId === 'elapsedTime') { const requestTime = this.requestTimeForId.get(message.id); if (requestTime) { return { ...cell, value: Date.now() - requestTime, renderer: timeRenderer, }; } } return cell; }), }; const newRowsArray = [...this.dataGridIntegrator.data().rows]; newRowsArray[matchingExistingRowIndex] = newRowWithUpdate; // Now we've updated the message, it won't be updated again, so we can delete it from the tracking map. this.dataGridRowForId.delete(message.id); this.dataGridIntegrator.update({ ...this.dataGridIntegrator.data(), rows: newRowsArray, }); return; } const sdkTarget = target; const responseIcon = new IconButton.Icon.Icon(); responseIcon.data = { iconName: 'arrow-down', color: 'var(--icon-request)', width: '20px', height: '20px' }; const newRow = { cells: [ { columnId: 'method', value: message.method, title: message.method }, { columnId: 'request', value: '', renderer: DataGrid.DataGridRenderers.codeBlockRenderer }, { columnId: 'response', value: JSON.stringify(message.params), renderer: DataGrid.DataGridRenderers.codeBlockRenderer, }, { columnId: 'timestamp', value: Date.now() - this.startTime, renderer: timeRenderer, }, { columnId: 'elapsedTime', value: '' }, { columnId: 'type', value: responseIcon, title: 'received' }, { columnId: 'target', value: this.targetToString(sdkTarget) }, { columnId: 'session', value: message.sessionId || '' }, ], hidden: false, }; this.dataGridIntegrator.update({ ...this.dataGridIntegrator.data(), rows: this.dataGridIntegrator.data().rows.concat([newRow]), }); } messageSent(message, target) { if (this.isRecording) { this.messages.push({ ...message, type: 'send' }); } const sdkTarget = target; const requestResponseIcon = new IconButton.Icon.Icon(); requestResponseIcon .data = { iconName: 'arrow-up-down', color: 'var(--icon-request-response)', width: '20px', height: '20px' }; const newRow = { styles: { '--override-data-grid-row-background-color': 'var(--override-data-grid-sent-message-row-background-color)', }, cells: [ { columnId: 'method', value: message.method, title: message.method }, { columnId: 'request', value: JSON.stringify(message.params), renderer: DataGrid.DataGridRenderers.codeBlockRenderer, }, { columnId: 'response', value: '(pending)', renderer: DataGrid.DataGridRenderers.codeBlockRenderer }, { columnId: 'timestamp', value: Date.now() - this.startTime, renderer: timeRenderer, }, { columnId: 'elapsedTime', value: '(pending)' }, { columnId: 'type', value: requestResponseIcon, title: 'sent' }, { columnId: 'target', value: String(sdkTarget?.id()) }, { columnId: 'session', value: message.sessionId || '' }, ], hidden: false, }; this.requestTimeForId.set(message.id, Date.now()); this.dataGridRowForId.set(message.id, newRow); this.dataGridIntegrator.update({ ...this.dataGridIntegrator.data(), rows: this.dataGridIntegrator.data().rows.concat([newRow]), }); } async saveAsFile() { const now = new Date(); const fileName = 'ProtocolMonitor-' + Platform.DateUtilities.toISO8601Compact(now) + '.json'; const stream = new Bindings.FileUtils.FileOutputStream(); const accepted = await stream.open(fileName); if (!accepted) { return; } void stream.write(JSON.stringify(this.messages, null, ' ')); void stream.close(); } } let protocolMonitorImplInstance; export class ProtocolMonitorImpl extends UI.Widget.VBox { #split; #editorWidget = new EditorWidget(); #protocolMonitorDataGrid; // This width corresponds to the optimal width to use the editor properly // It is randomly chosen #sideBarMinWidth = 400; constructor() { super(true); this.#split = new UI.SplitWidget.SplitWidget(true, false, 'protocol-monitor-split-container', this.#sideBarMinWidth); this.#split.show(this.contentElement); this.#protocolMonitorDataGrid = new ProtocolMonitorDataGrid(this.#split); this.#protocolMonitorDataGrid.addEventListener(Events.CommandChange, event => { this.#editorWidget.jsonEditor.displayCommand(event.data.command, event.data.parameters, event.data.targetId); }); this.#editorWidget.element.style.overflow = 'hidden'; this.#split.setMainWidget(this.#protocolMonitorDataGrid); this.#split.setSidebarWidget(this.#editorWidget); this.#split.hideSidebar(true); this.#editorWidget.addEventListener(Events.CommandSent, event => { this.#protocolMonitorDataGrid.onCommandSend(event.data.command, event.data.parameters, event.data.targetId); }); } static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!protocolMonitorImplInstance || forceNew) { protocolMonitorImplInstance = new ProtocolMonitorImpl(); } return protocolMonitorImplInstance; } } export class CommandAutocompleteSuggestionProvider { #maxHistorySize = 200; #commandHistory = new Set(); constructor(maxHistorySize) { if (maxHistorySize !== undefined) { this.#maxHistorySize = maxHistorySize; } } buildTextPromptCompletions = async (expression, prefix, force) => { if (!prefix && !force && expression) { return []; } const newestToOldest = [...this.#commandHistory].reverse(); newestToOldest.push(...metadataByCommand.keys()); return newestToOldest.filter(cmd => cmd.startsWith(prefix)).map(text => ({ text, })); }; addEntry(value) { if (this.#commandHistory.has(value)) { this.#commandHistory.delete(value); } this.#commandHistory.add(value); if (this.#commandHistory.size > this.#maxHistorySize) { const earliestEntry = this.#commandHistory.values().next().value; this.#commandHistory.delete(earliestEntry); } } } export class InfoWidget extends UI.Widget.VBox { tabbedPane; request; targetId = ''; constructor() { super(); this.tabbedPane = new UI.TabbedPane.TabbedPane(); this.tabbedPane.appendTab('request', i18nString(UIStrings.request), new UI.Widget.Widget()); this.tabbedPane.appendTab('response', i18nString(UIStrings.response), new UI.Widget.Widget()); this.tabbedPane.show(this.contentElement); this.tabbedPane.selectTab('response'); this.request = {}; this.render(null); } render(data) { if (!data || !data.request || !data.response || !data.target) { this.tabbedPane.changeTabView('request', new UI.EmptyWidget.EmptyWidget(i18nString(UIStrings.noMessageSelected))); this.tabbedPane.changeTabView('response', new UI.EmptyWidget.EmptyWidget(i18nString(UIStrings.noMessageSelected))); return; } const requestEnabled = data && data.type && data.type === 'sent'; this.tabbedPane.setTabEnabled('request', Boolean(requestEnabled)); if (!requestEnabled) { this.tabbedPane.selectTab('response'); } const requestParsed = JSON.parse(String(data.request.value) || 'null'); this.request = requestParsed; this.targetId = String(data.target.value); this.tabbedPane.changeTabView('request', SourceFrame.JSONView.JSONView.createViewSync(requestParsed)); const responseParsed = data.response.value === '(pending)' ? null : JSON.parse(String(data.response.value) || 'null'); this.tabbedPane.changeTabView('response', SourceFrame.JSONView.JSONView.createViewSync(responseParsed)); } } // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var Events; (function (Events) { Events["CommandSent"] = "CommandSent"; Events["CommandChange"] = "CommandChange"; })(Events || (Events = {})); export class EditorWidget extends Common.ObjectWrapper.eventMixin(UI.Widget.VBox) { jsonEditor; constructor() { super(); this.jsonEditor = new Components.JSONEditor.JSONEditor(); this.jsonEditor.metadataByCommand = metadataByCommand; this.jsonEditor.typesByName = typesByName; this.jsonEditor.enumsByName = enumsByName; this.element.append(this.jsonEditor); this.jsonEditor.addEventListener(Components.JSONEditor.SubmitEditorEvent.eventName, (event) => { this.dispatchEventToListeners(Events.CommandSent, event.data); }); } } export function parseCommandInput(input) { // If input cannot be parsed as json, we assume it's the command name // for a command without parameters. Otherwise, we expect an object // with "command"/"method"/"cmd" and "parameters"/"params"/"args"/"arguments" attributes. let json = null; try { json = JSON.parse(input); } catch (err) { } const command = json ? json.command || json.method || json.cmd || '' : input; const parameters = json?.parameters || json?.params || json?.args || json?.arguments || {}; return { command, parameters }; } //# sourceMappingURL=ProtocolMonitor.js.map