@timschrader/node-dbus
v1.0.0
Published
Modern, production-ready DBus library for Node.js with complete client and service support
Maintainers
Readme
@timschrader/node-dbus
Modern, production-ready DBus library for Node.js with TypeScript support.
Built on sd-bus (systemd) via C++ N-API — all DBus communication happens in native code for maximum performance and correctness.
Features
- Full Client Support — Call methods, read/write properties, subscribe to signals
- Service Export — Expose objects with methods, properties, and signals on the bus
- Introspection — Discover interfaces, methods, and properties; generate introspection XML
- Complete Type System — All DBus types (basic + containers), full validation in C++
- Name Management — Request/release well-known names with queue & replace support
- Signals — EventEmitter-based subscription with automatic match rule management
- Connection Management — Auto-reconnect, disconnect detection, multiple connections
- Variant Support — First-class
Variantclass with factories, JSON serialization, type guards - Retry & Timeouts — Per-call timeouts, AbortSignal cancellation,
withRetry()helper - Advanced Service — Fallback vtables for dynamic paths, ObjectManager, PropertiesChanged signals
- Property Caching — Optional sync property reads with auto-sync via PropertiesChanged
- Call Tracing — Built-in debug logging with custom logger support
- Service Templates — Pre-built MPRIS Player and Notification Daemon templates
- Code Generation — Auto-generate typed TypeScript proxies from DBus introspection
- Testing Utilities — MockBus for unit testing without a real DBus connection
- TypeScript — Full type definitions, type-safe API
Quick Start
Prerequisites
# Arch Linux
sudo pacman -Syu systemd pkgconf base-devel
# Ubuntu/Debian
sudo apt-get install libsystemd-dev pkg-config build-essential
# Fedora/RHEL
sudo dnf install systemd-devel pkg-config gcc-c++Install
npm install @timschrader/node-dbusThe native addon compiles automatically during install (requires the prerequisites above).
Install from Source
git clone https://github.com/Tim-Schrader/node-dbus.git
cd node-dbus
npm install
npm run build:allClient Usage
import { Bus, DBusError } from '@timschrader/node-dbus';
// Connect to the session bus
const bus = Bus.session();
// Get a proxy object and interface
const obj = bus.getObject('org.freedesktop.DBus', '/org/freedesktop/DBus');
const iface = obj.getInterface('org.freedesktop.DBus');
// Call a method
const names = await iface.call<string[]>('ListNames', '', []);
console.log('Bus names:', names);
// Read a property
const features = await bus.getProperty<string[]>(
'org.freedesktop.DBus', '/org/freedesktop/DBus',
'org.freedesktop.DBus', 'Features'
);
// Subscribe to signals
iface.on('NameOwnerChanged', (name, oldOwner, newOwner) => {
console.log(`${name}: ${oldOwner} → ${newOwner}`);
});
// Error handling
try {
await bus.call('org.nonexistent.Service', '/path', 'iface', 'Method');
} catch (err) {
if (DBusError.isDBusError(err)) {
console.log(`DBus error: ${err.dbusName}`);
}
}
bus.close();Service Usage
import { Bus, ServiceBuilder, NameFlags } from '@timschrader/node-dbus';
const bus = Bus.session();
const builder = new ServiceBuilder('com.example.Calculator')
.method('Add', 'ii', 'i', (a: number, b: number) => a + b)
.method('Echo', 's', 's', (s: string) => s)
.property('Version', 's', () => '1.0')
.property('Counter', 'i', () => counter, (v: number) => { counter = v; })
.signal('CounterChanged', 'i');
let counter = 0;
bus.exportObject('/com/example/Calculator', builder);
bus.requestName('com.example.Calculator', NameFlags.REPLACE_EXISTING);
// Emit signals
bus.emitSignal('/com/example/Calculator', 'com.example.Calculator',
'CounterChanged', 'i', [counter]);
// Emit PropertiesChanged
bus.emitPropertiesChanged('/com/example/Calculator',
'com.example.Calculator', ['Counter'], []);API Overview
Connection
const bus = Bus.session(); // Session bus
const bus = Bus.system(); // System bus
const bus = Bus.connect(address); // Custom address
bus.connected; // boolean
bus.uniqueName; // ":1.xxx"
bus.close(); // Disconnect
bus.reconnect(); // Reconnect
// Auto-reconnect with backoff
const bus = Bus.session({ autoReconnect: true, reconnectDelay: 1000 });
bus.on('disconnected', () => console.log('Lost connection'));
bus.on('reconnecting', ({ attempt }) => console.log(`Reconnecting #${attempt}`));
bus.on('connected', () => console.log('Connected'));Method Calls
// Via InterfaceProxy
const result = await iface.call<string>('MethodName', 'si', ['arg1', 42]);
// Via Bus directly
const result = await bus.call<string>(dest, path, iface, method, 'si', ['arg1', 42]);
// With timeout and abort
const result = await bus.call<string>(dest, path, iface, method, 's', ['arg'],
{ timeout: 5000, signal: abortController.signal });Properties
const val = await bus.getProperty<string>(dest, path, iface, 'PropName');
await bus.setProperty(dest, path, iface, 'PropName', 's', 'value');
const all = await bus.getAllProperties(dest, path, iface);Signals
iface.on('SignalName', (...args) => { /* handle signal */ });
iface.off('SignalName', handler);
iface.removeAllListeners('SignalName');Introspection
const data = await obj.introspect(); // Cached
data.interfaces; // IntrospectionInterface[]
data.nodes; // string[] (child paths)Code Generation
Generate typed TypeScript proxies from DBus introspection:
# CLI: Introspect a live service
npx dbus-codegen --service org.mpris.MediaPlayer2.vlc -p /org/mpris/MediaPlayer2 -o mpris.ts
# CLI: From XML file
npx dbus-codegen --xml introspection.xml -o types.ts// Programmatic API
import { parseIntrospectionXml, generateTypescript, signatureToTs } from '@timschrader/node-dbus';
signatureToTs('a{sv}'); // 'Record<string, Variant>'
const data = parseIntrospectionXml(xml);
const code = generateTypescript(data);
// Use generated proxy
import { MediaPlayer2Player } from './generated/mpris';
const proxy = new MediaPlayer2Player(iface);
await proxy.Play(); // Fully typed!See docs/guide/codegen.md for full documentation.
Name Management
import { NameFlags, NameResult } from '@timschrader/node-dbus';
const result = bus.requestName('com.example.MyService', NameFlags.REPLACE_EXISTING);
if (result === NameResult.PRIMARY_OWNER) { /* success */ }
bus.releaseName('com.example.MyService');Service Export
// Fixed object path
const builder = new ServiceBuilder('com.example.Iface')
.method('Method', 'ii', 'i', (a, b) => a + b)
.property('Prop', 's', getter, setter)
.signal('Signal', 'si');
bus.exportObject('/path', builder);
bus.unexportObject('/path');
// Dynamic paths (fallback)
const fb = new FallbackBuilder('com.example.Users')
.method('GetName', '', 's', (path) => path.split('/').pop()!);
bus.addFallback('/com/example/users', fb);
// ObjectManager (GetManagedObjects)
bus.addObjectManager('/com/example');Type System
All DBus types are supported:
| Signature | Type | JavaScript |
|-----------|------|------------|
| y | byte | number |
| b | boolean | boolean |
| n, q, i, u | int16/uint16/int32/uint32 | number |
| x, t | int64/uint64 | BigInt |
| d | double | number |
| s | string | string |
| o | object path | string |
| g | signature | string |
| v | variant | Variant |
| a... | array | array |
| (...) | struct | array |
| a{...} | dict | object |
import { Variant, VariantTypes, validateSignature } from '@timschrader/node-dbus';
// Create variants
const v = new Variant('s', 'hello');
const v2 = VariantTypes.int32(42);
// Validate
validateSignature('a{sv}'); // true
validateValue('i', 42); // true
validateObjectPath('/org/test'); // trueRetry & Timeouts
import { withRetry } from '@timschrader/node-dbus';
// Per-call timeout
bus.setDefaultTimeout(10000); // 10s default
// Retry with backoff
const result = await withRetry(
() => bus.call(dest, path, iface, method),
{ retries: 3, delayMs: 100 }
);Examples
See examples/ for runnable TypeScript examples:
npx tsx examples/client-basics.ts # Connect, call methods, properties
npx tsx examples/signals.ts # Signal subscription
npx tsx examples/service.ts # Create a DBus service
npx tsx examples/introspection.ts # Discover interfaces
npx tsx examples/types-and-variants.ts # Type system showcase
npx tsx examples/advanced.ts # All features combined
npx tsx examples/codegen.ts # Type code generationArchitecture
TypeScript (High-level API, Events, Timeouts)
↓ N-API
C++ (ALL DBus communication, Type serialization)
↓ sd-bus API
sd-bus Library (Wire protocol, Authentication)All DBus communication happens in C++ via sd-bus. TypeScript provides the high-level API, event management, and object lifecycle. Type validation happens in C++ before any data reaches the wire.
Development
npm run build # Build TypeScript
npm run build:native # Build C++ addon
npm run build:all # Both
npm test # Run tests (Vitest)
npm run dev # Watch modeProject Structure
src/ TypeScript source (Bus, InterfaceProxy, ServiceBuilder, ...)
native/src/ C++ N-API addon (bus, types, signals, methods, service)
test/ Vitest tests (unit + integration)
examples/ Runnable TypeScript examples
docs/ Documentation (user guide + dev reference)See docs/ for full documentation including the API reference and architecture guide.
Requirements
- OS: Linux (systemd-based distributions)
- Node.js: ≥18.0.0
- System: libsystemd-dev, pkg-config, build-essential
License
MIT © Tim Schrader
