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
JavaScript
;
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;