@monitoro/herd
Version:
Automate your browser, build AI web tools and MCP servers with Monitoro Herd
122 lines (121 loc) • 3.85 kB
JavaScript
import { EventEmitter } from 'events';
import { Page } from './Page.js';
export class Device extends EventEmitter {
constructor(client, info) {
super();
this.pageMap = new Map(); // Changed to number key
this.client = client;
this.info = info;
}
get id() {
return this.info.id;
}
get deviceId() {
return this.info.deviceId;
}
get type() {
return this.info.type;
}
get name() {
return this.info.name;
}
get status() {
return this.info.status;
}
get lastActive() {
return this.info.lastActive ? new Date(this.info.lastActive) : null;
}
updateInfo(info) {
this.info = info;
this.emit('updated', info);
}
/**
* Create a new page in the device
*/
async newPage() {
const response = await this.client.sendCommand(this.deviceId, 'Tabs.createTab', {
url: 'about:blank',
active: false
});
// Unwrap the NATS response which contains the tab info in the result property
const tab = response?.result;
if (!tab) {
throw new Error('Failed to create new page: Invalid response format');
}
const tabId = typeof tab.id === 'string' ? parseInt(tab.id, 10) : tab.id;
if (isNaN(tabId)) {
throw new Error('Invalid tab ID: must be convertible to integer');
}
const page = new Page(this.client, this, tabId);
this.pageMap.set(tabId, page);
page.updateInfo(tab);
return page;
}
/**
* List all pages in the device
*/
async listPages() {
const response = await this.client.sendCommand(this.deviceId, 'Tabs.getTabs');
// Unwrap the NATS response
const tabs = response?.result;
if (!Array.isArray(tabs)) {
throw new Error('Failed to list pages: Invalid response format');
}
return tabs.map(tabInfo => {
const tabId = typeof tabInfo.id === 'string' ? parseInt(tabInfo.id, 10) : tabInfo.id;
if (isNaN(tabId)) {
throw new Error('Invalid tab ID: must be convertible to integer');
}
let page = this.pageMap.get(tabId);
if (!page) {
page = new Page(this.client, this, tabId);
this.pageMap.set(tabId, page);
}
page.updateInfo(tabInfo);
return page;
});
}
/**
* Get a specific page by ID
*/
async getPage(id) {
const pages = await this.listPages();
const page = pages.find(p => p.id === id);
if (!page) {
throw new Error(`Page ${id} not found`);
}
return page;
}
/**
* Subscribe to all events from this device
*/
onEvent(callback) {
return this.client.subscribeToDeviceEvents(this.deviceId, callback);
}
/**
* Subscribe to a specific event from this device
*/
on(eventName, callback) {
const unsubscribe = this.client.subscribeToDeviceEvent(this.deviceId, eventName, (event) => {
callback(event);
});
// Store the unsubscribe function for cleanup
const cleanups = this.listeners('cleanup');
cleanups.push(unsubscribe);
return this;
}
/**
* Close the device and cleanup resources
*/
async close() {
// Cleanup all pages
const pages = await this.listPages();
await Promise.all(pages.map(page => page.close()));
this.pageMap.clear();
// Execute cleanup functions
const cleanups = this.listeners('cleanup');
cleanups.forEach(cleanup => cleanup());
this.removeAllListeners();
await this.client.sendCommand(this.deviceId, 'Device.close');
}
}