UNPKG

dbus-sdk

Version:

A Node.js SDK for interacting with DBus, enabling seamless service calling and exposure with TypeScript support

161 lines (160 loc) 8.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IntrospectableInterface = void 0; const LocalInterface_1 = require("../../LocalInterface"); const xml2js_1 = require("xml2js"); /** * A class representing the DBus Introspectable interface. * This interface provides introspection capabilities for DBus objects, allowing clients to query * the structure (methods, properties, signals) of a DBus object as XML data. * Implements the 'org.freedesktop.DBus.Introspectable' interface. */ class IntrospectableInterface extends LocalInterface_1.LocalInterface { /** * The DOCTYPE declaration prefix used in the introspection XML. * Used to construct the XML header for introspection data. */ #doctype = '!DOCTYPE'; /** * The full DOCTYPE header for the introspection XML. * Defines the DTD for DBus object introspection as per the freedesktop.org specification. */ #header = `<${this.#doctype} node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">`; /** * Constructor for the IntrospectableInterface. * Initializes the interface with the name 'org.freedesktop.DBus.Introspectable' * and defines the 'Introspect' method to return XML introspection data. */ constructor() { super('org.freedesktop.DBus.Introspectable'); this.defineMethod({ name: 'Introspect', outputArgs: [{ name: 'xml_data', type: 's' // String type for XML data }], method: () => this.getIntrospectXML(this.object?.name) }); } /** * Generates the full introspection XML for a given object path. * Combines the DOCTYPE header with the formatted XML content for the specified path. * * @param path - Optional object path to generate introspection XML for. * @param inputObjectPaths - Optional list of object paths to consider for introspection. * @returns The complete introspection XML string, including the DOCTYPE header. */ getIntrospectXML(path, inputObjectPaths) { return `${this.#header}\n${this.formatIntrospectXML(path, inputObjectPaths)}`; } /** * Formats the introspection data into XML for a given object path. * Builds an XML representation of the DBus object's structure, including interfaces, * methods, properties, signals, and sub-nodes, using the xml2js Builder. * * @param path - Optional object path to format introspection XML for. * @param inputObjectPaths - Optional list of object paths to consider for introspection. * @returns The formatted introspection XML string (without DOCTYPE header). */ formatIntrospectXML(path, inputObjectPaths) { const builder = new xml2js_1.Builder({ rootName: 'node', // Root element of the introspection XML headless: true // Exclude XML declaration (<?xml ... ?>) }); if (!path) return builder.buildObject({}); // Return empty XML if no path is provided const objectPaths = inputObjectPaths ? inputObjectPaths : this.object?.service?.listObjectPaths(); if (!objectPaths) return builder.buildObject({}); // Return empty XML if no object paths are available // Filter object paths that start with the given path to find relevant nodes const roughlyMatchedObjectPaths = objectPaths.filter((objectPath) => objectPath.startsWith(path)); const matchedObjectPath = roughlyMatchedObjectPaths.find((roughlyMatchedObjectPath) => roughlyMatchedObjectPath === path); const xmlObject = {}; // If an exact match for the path is found, include interface details if (matchedObjectPath) { const localObject = this.object.service.findObjectByPath(matchedObjectPath); const interfaces = localObject.introspectNode.interface.map((introspectableInterface) => { // Map methods of the interface to XML structure const methods = introspectableInterface.method.map((introspectMethod) => { const args = introspectMethod.arg.map((introspectMethodArgument) => { const xmlMethodArgObject = { direction: introspectMethodArgument.direction, // 'in' or 'out' type: introspectMethodArgument.type // DBus type signature (e.g., 's', 'i') }; if (introspectMethodArgument.name) xmlMethodArgObject.name = introspectMethodArgument.name; return { $: xmlMethodArgObject // XML attributes for the argument }; }); const xmlMethodObject = { $: { name: introspectMethod.name } // XML attribute for method name }; if (args.length) xmlMethodObject.arg = args; // Include arguments if any return xmlMethodObject; }); // Map properties of the interface to XML structure const properties = introspectableInterface.property.map((introspectProperty) => { const xmlPropertyObject = { $: { name: introspectProperty.name, // Property name type: introspectProperty.type, // DBus type signature access: introspectProperty.access // Access mode ('read', 'write', 'readwrite') } }; return xmlPropertyObject; }); // Map signals of the interface to XML structure const signals = introspectableInterface.signal.map((introspectSignal) => { const args = introspectSignal.arg.map((introspectSignalArgument) => { const xmlSignalArgObject = { type: introspectSignalArgument.type // DBus type signature }; if (introspectSignalArgument.name) xmlSignalArgObject.name = introspectSignalArgument.name; return { $: xmlSignalArgObject // XML attributes for the argument }; }); const xmlSignalObject = { $: { name: introspectSignal.name } // XML attribute for signal name }; if (args.length) xmlSignalObject.arg = args; // Include arguments if any return xmlSignalObject; }); // Construct the interface XML object const xmlInterfaceObject = { $: { name: introspectableInterface.name } // XML attribute for interface name }; if (methods.length) xmlInterfaceObject.method = methods; if (properties.length) xmlInterfaceObject.property = properties; if (signals.length) xmlInterfaceObject.signal = signals; return xmlInterfaceObject; }); if (interfaces.length) xmlObject.interface = interfaces; // Include interfaces if any } // Extract sub-nodes (child object paths) under the current path for hierarchical representation const nodes = roughlyMatchedObjectPaths.map((roughlyMatchedObjectPath) => { const subNode = roughlyMatchedObjectPath.replace(path, '').split('/').find((objectNode) => !!objectNode); return subNode ? subNode : ''; }).filter((subNode) => !!subNode) .map(nodeName => ({ $: { name: nodeName } })); // XML attribute for sub-node name const exists = []; if (nodes.length) xmlObject.node = nodes.filter((node) => { if (exists.includes(node.$.name)) return false; exists.push(node.$.name); return true; }); // Include sub-nodes if any return builder.buildObject(xmlObject); // Build and return the final XML string } } exports.IntrospectableInterface = IntrospectableInterface;