@lakutata/dbus 2.3.0 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +253 -25
- package/dist/cjs/adapters/DBusEntrypoint.d.ts +89 -0
- package/dist/cjs/adapters/DBusEntrypoint.js +280 -0
- package/dist/cjs/channel/DBusChannel.d.ts +36 -0
- package/dist/cjs/channel/DBusChannel.js +70 -0
- package/dist/cjs/components/DBusClientComponent.d.ts +494 -0
- package/dist/cjs/components/DBusClientComponent.js +272 -0
- package/dist/cjs/components/DBusObjectManager.d.ts +164 -0
- package/dist/cjs/components/DBusObjectManager.js +200 -0
- package/dist/cjs/context/DBusContext.d.ts +52 -0
- package/dist/cjs/context/DBusContext.js +68 -0
- package/dist/cjs/decorators/DBusInterface.d.ts +74 -0
- package/dist/cjs/decorators/DBusInterface.js +63 -0
- package/dist/cjs/decorators/DBusMethod.d.ts +115 -0
- package/dist/cjs/decorators/DBusMethod.js +20 -0
- package/dist/cjs/exports/DBus.d.ts +11 -0
- package/dist/cjs/exports/DBus.js +42 -0
- package/dist/cjs/lib/DBusClientOptions.d.ts +30 -0
- package/dist/cjs/lib/Declarations.d.ts +187 -0
- package/dist/cjs/lib/Declarations.js +113 -0
- package/dist/cjs/lib/Names.d.ts +35 -0
- package/dist/cjs/lib/Names.js +80 -0
- package/dist/cjs/lib/Signature.d.ts +14 -0
- package/dist/cjs/lib/Signature.js +53 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/DBusContextParams.d.ts +22 -0
- package/dist/cjs/types/DBusContextParams.js +2 -0
- package/dist/esm/adapters/DBusEntrypoint.js +274 -0
- package/dist/esm/channel/DBusChannel.js +67 -0
- package/dist/esm/components/DBusClientComponent.js +267 -0
- package/dist/esm/components/DBusObjectManager.js +197 -0
- package/dist/esm/context/DBusContext.js +64 -0
- package/dist/esm/decorators/DBusInterface.js +60 -0
- package/dist/esm/decorators/DBusMethod.js +17 -0
- package/dist/esm/exports/DBus.js +12 -0
- package/dist/esm/lib/DBusClientOptions.js +1 -0
- package/dist/esm/lib/Declarations.js +107 -0
- package/dist/esm/lib/Names.js +73 -0
- package/dist/esm/lib/Signature.js +49 -0
- package/dist/esm/types/DBusContextParams.js +1 -0
- package/dist/types/adapters/DBusEntrypoint.d.ts +89 -0
- package/dist/types/channel/DBusChannel.d.ts +36 -0
- package/dist/types/components/DBusClientComponent.d.ts +494 -0
- package/dist/types/components/DBusObjectManager.d.ts +164 -0
- package/dist/types/context/DBusContext.d.ts +52 -0
- package/dist/types/decorators/DBusInterface.d.ts +74 -0
- package/dist/types/decorators/DBusMethod.d.ts +115 -0
- package/dist/types/exports/DBus.d.ts +11 -0
- package/dist/types/lib/DBusClientOptions.d.ts +30 -0
- package/dist/types/lib/Declarations.d.ts +187 -0
- package/dist/types/lib/Names.d.ts +35 -0
- package/dist/types/lib/Signature.d.ts +14 -0
- package/dist/types/types/DBusContextParams.d.ts +22 -0
- package/package.json +38 -39
- package/CHANGELOG.md +0 -145
- package/dist/CommonExports.d.ts +0 -2
- package/dist/CommonExports.d.ts.map +0 -1
- package/dist/CommonExports.js +0 -44
- package/dist/ComponentExports.d.ts +0 -2
- package/dist/ComponentExports.d.ts.map +0 -1
- package/dist/ComponentExports.js +0 -5
- package/dist/ProviderExports.d.ts +0 -2
- package/dist/ProviderExports.d.ts.map +0 -1
- package/dist/ProviderExports.js +0 -5
- package/dist/components/DBusClientComponent.d.ts +0 -75
- package/dist/components/DBusClientComponent.d.ts.map +0 -1
- package/dist/components/DBusClientComponent.js +0 -125
- package/dist/interfaces/DBusClientOptions.d.ts +0 -7
- package/dist/interfaces/DBusClientOptions.d.ts.map +0 -1
- package/dist/providers/DBusClientProvider.d.ts +0 -75
- package/dist/providers/DBusClientProvider.d.ts.map +0 -1
- package/dist/providers/DBusClientProvider.js +0 -125
- package/dist/tests/DBusTest.spec.d.ts +0 -2
- package/dist/tests/DBusTest.spec.d.ts.map +0 -1
- package/dist/tests/DBusTest.spec.js +0 -26
- package/dist/tests/Test.d.ts +0 -9
- package/dist/tests/Test.d.ts.map +0 -1
- package/dist/tests/Test.js +0 -80
- package/tsconfig.json +0 -7
- /package/dist/{interfaces → cjs/lib}/DBusClientOptions.js +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present Lakutata
|
|
4
|
+
|
|
5
|
+
All rights reserved
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,38 +1,266 @@
|
|
|
1
|
-
|
|
1
|
+
# @lakutata/dbus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The D-Bus channel of the [lakutata](https://github.com/lakutata/lakutata) framework: the application owns a service
|
|
4
|
+
name on a bus (the system bus, the session bus or a private bus) and serves its controllers as the interfaces of D-Bus
|
|
5
|
+
objects: their methods, properties and signals. It also has a D-Bus client component, to call the other services of a
|
|
6
|
+
bus, read their properties and listen to their signals. The protocol is implemented by
|
|
7
|
+
[dbus-sdk](https://github.com/myq1991/node-dbus-sdk), whose classes this package re-exports. The umbrella package
|
|
8
|
+
`lakutata` does not re-export it: import it from `@lakutata/dbus`.
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Installation
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
```shell
|
|
13
|
+
npm install lakutata @lakutata/dbus
|
|
14
|
+
```
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-

|
|
16
|
+
- It needs a D-Bus daemon, as on Linux: the system bus (`/var/run/dbus/system_bus_socket`), the session bus of a
|
|
17
|
+
desktop session (`DBUS_SESSION_BUS_ADDRESS`), or a private `dbus-daemon`. Owning a name on the system bus needs a bus
|
|
18
|
+
policy allowing it (a file of `/etc/dbus-1/system.d/`).
|
|
19
|
+
- Node.js 22 or later; its peer dependencies `@lakutata/core` and `@lakutata/entrypoint` come with `lakutata`.
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
## Usage
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
### A D-Bus service
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
web application development; it aims to serve as a foundational framework for desktop applications, embedded systems
|
|
22
|
-
applications, and web applications. The framework primarily adopts an OOP (Object-Oriented Programming) approach and
|
|
23
|
-
encapsulates functionalities such as subprocesses, threads, permission management, and database ORM, enabling the
|
|
24
|
-
framework to be used out of the box.
|
|
25
|
+
`dbusEntrypoint` is the `dbus` adapter of the entrypoint. A controller declared with `@DBusInterface` serves an
|
|
26
|
+
interface at an object path; its `@DBusMethod` actions are the interface's methods.
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
```typescript
|
|
29
|
+
import {Application, DTO} from 'lakutata'
|
|
30
|
+
import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
|
|
31
|
+
import {Expect, Return} from 'lakutata/decorator/dto'
|
|
32
|
+
import {dbusEntrypoint, DBusInterface, DBusMethod} from '@lakutata/dbus'
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
class ConfigureDTO extends DTO {
|
|
35
|
+
@Expect(DTO.String().required())
|
|
36
|
+
public sn: string
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
@Expect(DTO.Number().integer().min(0).optional())
|
|
39
|
+
public retries?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@DBusInterface('com.example.Manager', {path: '/com/example/Manager'})
|
|
43
|
+
class ManagerController extends Controller {
|
|
44
|
+
//No argument, returns a string (s)
|
|
45
|
+
@DBusMethod('Ping', {returns: 's'})
|
|
46
|
+
public async ping(): Promise<string> {
|
|
47
|
+
return 'pong'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//One argument a{sv}, returns a dictionary a{sv} (the type of @Return)
|
|
51
|
+
@DBusMethod('Configure', ConfigureDTO)
|
|
52
|
+
@Return(ConfigureDTO)
|
|
53
|
+
public async configure(input: ConfigureDTO): Promise<ConfigureDTO> {
|
|
54
|
+
return input
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//busctl --user call com.example.DeviceService /com/example/Manager com.example.Manager Ping
|
|
59
|
+
Application.run(() => ({
|
|
60
|
+
id: 'devices.app',
|
|
61
|
+
name: 'Devices',
|
|
62
|
+
components: {
|
|
63
|
+
entrypoint: buildEntrypoints({
|
|
64
|
+
controllers: [ManagerController],
|
|
65
|
+
dbus: dbusEntrypoint({bus: 'session', service: 'com.example.DeviceService'})
|
|
66
|
+
})
|
|
67
|
+
},
|
|
68
|
+
bootstrap: ['entrypoint']
|
|
69
|
+
}))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **The input** of a method with a DTO is one argument, a dictionary of variants (`a{sv}`) holding the DTO's
|
|
73
|
+
properties; a method without a DTO takes no argument. The input goes through the middlewares, the validation and the
|
|
74
|
+
access control of the entrypoint, as in the other channels.
|
|
75
|
+
- **The result** has the type of the `returns` option (a DTO is `a{sv}`, or a signature such as `'s'`), or else the type
|
|
76
|
+
of the `@Return` schema: an object is `a{sv}`, a string `s`, a boolean `b`, an integer `x`, another number `d`, an
|
|
77
|
+
array `av`, the other values a variant `v`. Without them, the method has no result. The dates are sent as ISO
|
|
78
|
+
strings, the `null` and `undefined` values are left out (a variant has no null).
|
|
79
|
+
- **The errors** reach the callers as D-Bus errors named after their `errno`, prefixed by the service name
|
|
80
|
+
(`com.example.DeviceService.E_OFFLINE`), with their message.
|
|
81
|
+
- **The context** of the methods is a `DBusContext`: `context.path`, `context.interface` and `context.member`.
|
|
82
|
+
- **Several controllers** at the same path are the interfaces of the same objects. The objects implement
|
|
83
|
+
`org.freedesktop.DBus.Properties`, `Introspectable` and `Peer`, and the root object `/` implements `ObjectManager`.
|
|
84
|
+
- A method may also carry the decorators of the other channels (`@ServiceAction`, `@GET`…).
|
|
85
|
+
|
|
86
|
+
### The adapter
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
dbus: dbusEntrypoint({bus: 'system', service: 'com.example.DeviceService', replaceExisting: true})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `bus`: `'session'` (default, the address in `DBUS_SESSION_BUS_ADDRESS`), `'system'` (`DBUS_SYSTEM_BUS_ADDRESS`, else
|
|
93
|
+
`unix:path=/var/run/dbus/system_bus_socket`), the address of a bus (`unix:path=…`, `tcp:host=…,port=…`), or the path
|
|
94
|
+
of the socket of a private bus.
|
|
95
|
+
- `service`: the well-known name of the service. The adapter fails to start when another connection owns it
|
|
96
|
+
(`replaceExisting` takes it when its owner allows it, `allowReplacement` lets another service take it).
|
|
97
|
+
- `name` and `middlewares`: the adapter's name, and the middlewares of its calls (after the global ones).
|
|
98
|
+
- It stops gracefully with the entrypoint: the name is released, the calls in progress end, then the connection
|
|
99
|
+
closes.
|
|
100
|
+
|
|
101
|
+
### Objects, properties and signals
|
|
102
|
+
|
|
103
|
+
The path of an interface is either a path (`/com/example/Manager`), whose object is exported when the adapter starts
|
|
104
|
+
and stays exported while it runs (`DBusObjectManager.removeObject()` does not remove it), or a template
|
|
105
|
+
(`/com/example/Devices/{sn}`), whose objects are added and removed with `DBusObjectManager`. The
|
|
106
|
+
variables of the path are merged into the input of the methods, as the route parameters (they take precedence over the
|
|
107
|
+
client's input).
|
|
108
|
+
|
|
109
|
+
A property is a value of an object, kept by `DBusObjectManager`: the application sets it, the clients read it and are
|
|
110
|
+
notified of its changes (`PropertiesChanged`); until it is set, its value is its `initial` value. A `writable`
|
|
111
|
+
property is also written by the clients: the value is validated by the property's `schema` (the errors are given to the
|
|
112
|
+
client), kept, and `DBusObjectManager` emits `propertyWritten`. A write which must reach a device, or may fail after
|
|
113
|
+
the validation, is better a method: its errors are given to the caller. A signal's data is validated by its DTO when it
|
|
114
|
+
is emitted.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import {Component, DTO} from 'lakutata'
|
|
118
|
+
import {Controller} from 'lakutata/com/entrypoint'
|
|
119
|
+
import {Expect, Return} from 'lakutata/decorator/dto'
|
|
120
|
+
import {Inject} from 'lakutata/decorator/di'
|
|
121
|
+
import {
|
|
122
|
+
DBusInterface,
|
|
123
|
+
DBusMethod,
|
|
124
|
+
DBusObjectManager,
|
|
125
|
+
defineDBusProperty,
|
|
126
|
+
defineDBusSignal,
|
|
127
|
+
type DBusPropertyWrite
|
|
128
|
+
} from '@lakutata/dbus'
|
|
129
|
+
|
|
130
|
+
class DeviceSnDTO extends DTO {
|
|
131
|
+
@Expect(DTO.String().required())
|
|
132
|
+
public sn: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class StatusDTO extends DTO {
|
|
136
|
+
@Expect(DTO.String().required())
|
|
137
|
+
public sn: string
|
|
138
|
+
|
|
139
|
+
@Expect(DTO.Boolean().required())
|
|
140
|
+
public online: boolean
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const DeviceChanged = defineDBusSignal<StatusDTO>('Changed', StatusDTO)
|
|
144
|
+
export const DeviceState = defineDBusProperty<string>('State', 's', {initial: 'idle'})
|
|
145
|
+
export const DeviceLevel = defineDBusProperty<number>('Level', 'i', {writable: true, initial: 1, schema: DTO.Number().integer().min(0).max(10)})
|
|
146
|
+
|
|
147
|
+
@DBusInterface('com.example.Device', {path: '/com/example/Devices/{sn}', signals: [DeviceChanged], properties: [DeviceState, DeviceLevel]})
|
|
148
|
+
export class DeviceController extends Controller {
|
|
149
|
+
//The sn of the input is the one of the object path
|
|
150
|
+
@DBusMethod('Status', DeviceSnDTO)
|
|
151
|
+
@Return(StatusDTO)
|
|
152
|
+
public async status(input: DeviceSnDTO): Promise<StatusDTO> {
|
|
153
|
+
return {sn: input.sn, online: true} as StatusDTO
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//Registered in the components of the application
|
|
158
|
+
export class Devices extends Component {
|
|
159
|
+
@Inject(DBusObjectManager)
|
|
160
|
+
protected readonly manager: DBusObjectManager
|
|
161
|
+
|
|
162
|
+
protected async init(): Promise<void> {
|
|
163
|
+
this.manager.on('propertyWritten', (write: DBusPropertyWrite): void => {
|
|
164
|
+
if (write.property === DeviceLevel) console.log(`${write.path}: level ${write.value}`)
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public plug(sn: string): void {
|
|
169
|
+
const path: string = `/com/example/Devices/${sn}`
|
|
170
|
+
//Exported by the interfaces of the matching templates
|
|
171
|
+
this.manager.addObject(path)
|
|
172
|
+
this.manager.setPropertyValue(path, DeviceState, 'running')
|
|
173
|
+
this.manager.emitSignal(path, DeviceChanged, {sn: sn, online: true} as StatusDTO)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public unplug(sn: string): void {
|
|
177
|
+
this.manager.removeObject(`/com/example/Devices/${sn}`)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
34
180
|
```
|
|
35
181
|
|
|
36
|
-
|
|
182
|
+
`DBusObjectManager` is autoloaded, one per application: inject it in any object (`@Inject(DBusObjectManager)`) or get
|
|
183
|
+
it (`getObject(DBusObjectManager)`). The objects added before the adapter starts are exported when it starts. The
|
|
184
|
+
clients are told of the objects added and removed by the `ObjectManager` of the service (`InterfacesAdded`,
|
|
185
|
+
`InterfacesRemoved`).
|
|
186
|
+
|
|
187
|
+
### The client
|
|
188
|
+
|
|
189
|
+
`DBusClientComponent` connects to a bus when it is created, to call the services, read their objects and properties,
|
|
190
|
+
and listen to their signals; it disconnects when the application stops.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import {Application} from 'lakutata'
|
|
194
|
+
import {DBusClientComponent, type DBusRemoteInterface} from '@lakutata/dbus'
|
|
195
|
+
|
|
196
|
+
Application
|
|
197
|
+
.run(() => ({
|
|
198
|
+
id: 'client.app',
|
|
199
|
+
name: 'Client',
|
|
200
|
+
components: {
|
|
201
|
+
dbus: {class: DBusClientComponent, busAddress: 'unix:path=/var/run/dbus/system_bus_socket', timeout: 5000}
|
|
202
|
+
}
|
|
203
|
+
}))
|
|
204
|
+
.onLaunched(async (app: Application): Promise<void> => {
|
|
205
|
+
const dbus: DBusClientComponent = await app.getObject('dbus')
|
|
206
|
+
//A method call with an explicit signature: the output arguments, as an array
|
|
207
|
+
const [status] = await dbus.invoke({
|
|
208
|
+
service: 'com.example.DeviceService',
|
|
209
|
+
objectPath: '/com/example/Devices/A1',
|
|
210
|
+
interface: 'com.example.Device',
|
|
211
|
+
method: 'Status',
|
|
212
|
+
signature: 'a{sv}',
|
|
213
|
+
args: [{sn: 'A1'}]
|
|
214
|
+
})
|
|
215
|
+
console.log(status)
|
|
216
|
+
//An interface built from the introspection of its object
|
|
217
|
+
const device: DBusRemoteInterface = await dbus.interface('com.example.DeviceService', '/com/example/Devices/A1', 'com.example.Device')
|
|
218
|
+
console.log(await device.property.State.get())
|
|
219
|
+
device.signal.on('Changed', (data: unknown): void => console.log(data))
|
|
220
|
+
//The events of the connection
|
|
221
|
+
dbus.on('nameOwnerChanged', (name: string, oldOwner: string, newOwner: string): void => console.log(name, oldOwner, newOwner))
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`buildDBusClientOptions(options)` builds the same declaration from typed options:
|
|
226
|
+
`dbus: buildDBusClientOptions({busAddress: 'unix:path=/var/run/dbus/system_bus_socket'})`.
|
|
227
|
+
|
|
228
|
+
## API
|
|
229
|
+
|
|
230
|
+
| Export | What it is |
|
|
231
|
+
|---|---|
|
|
232
|
+
| `dbusEntrypoint`, `DBusEntrypointOptions` | The adapter of the entrypoint (its `dbus` option): the bus, the service name |
|
|
233
|
+
| `DBusInterface`, `DBusInterfaceOptions` | Class decorator: a controller served as a D-Bus interface at an object path or path template |
|
|
234
|
+
| `DBusMethod`, `DBusMethodOptions` | Method decorator: an action served as a method of the interface, the type of its result |
|
|
235
|
+
| `defineDBusSignal`, `DBusSignalDefinition` | Declares a signal, its DTO or signature |
|
|
236
|
+
| `defineDBusProperty`, `DBusPropertyDefinition`, `DBusPropertyOptions` | Declares a property: its signature, `writable`, `initial`, `schema` |
|
|
237
|
+
| `DBusInterfaceDeclaration` | The declaration `@DBusInterface` records |
|
|
238
|
+
| `DBusObjectManager` | The objects of the templates, the property values and the signals: `addObject`, `removeObject`, `hasObject`, `objects`, `setPropertyValue`, `getPropertyValue`, `emitSignal`; the `propertyWritten` event |
|
|
239
|
+
| `DBusPropertyWrite` | A property written by a client, the argument of `propertyWritten` |
|
|
240
|
+
| `DBusContext`, `DBusContextParams`, `DBUS_CONTEXT_TYPE` | The context of the method calls: `path`, `interface`, `member` |
|
|
241
|
+
| `DBusChannel` | The definition of the `dbus` channel, for the custom adapters |
|
|
242
|
+
| `DBusClientComponent` | The client: `invoke`, `interface`, `interfaceSignal`, `object`, `service`, `services`, `listNames`, `listServices`, `nameHasOwner`, `getNameOwner`, `addMatch`, the connection events |
|
|
243
|
+
| `buildDBusClientOptions`, `DBusClientOptions` | The client's declaration and options: `busAddress`, `timeout`, `callTimeout`, `decode` (`DecodeOpts` of `dbus-sdk`; the 64-bit integers are numbers by default) |
|
|
244
|
+
| `DBus`, `DBusRemoteInterface`, `DBusObject`, `DBusService`, `LocalService`, `LocalObject`, `LocalInterface`, `DBusSignedValue`, `DBusSignalEmitter`, `Signature`, `createDBusError`, `RequestNameFlags`, `RequestNameResultCode`, `BusNameBasicInfo`, `ConnectOpts`, `DecodeOpts`, `InvokeOpts` | Re-exported from dbus-sdk 2 (`DBusRemoteInterface` is its `DBusInterface`; see its MIGRATION.md for the changes from 1.x) |
|
|
245
|
+
|
|
246
|
+
The typings document each declaration with examples.
|
|
247
|
+
|
|
248
|
+
## Errors
|
|
249
|
+
|
|
250
|
+
| Error | When |
|
|
251
|
+
|---|---|
|
|
252
|
+
| `TypeError` | An invalid interface, member or service name, or object path, when the class is declared or `dbusEntrypoint()` is called; `DBusObjectManager.addObject()` with a template; the start of the adapter, when a `@DBusMethod` belongs to a controller without `@DBusInterface` |
|
|
253
|
+
| `Error` (`The DBus name … is owned by another service`) | The adapter's start, when another connection owns the service name; the entrypoint fails to start |
|
|
254
|
+
| The errors of dbus-sdk (`UnknownBusAddressError`, a connection error…) | The adapter's start or the client's creation, when the bus cannot be reached; the client's calls, when a method answers a D-Bus error |
|
|
255
|
+
| `InvalidValueException` (from `lakutata`) | `setPropertyValue()` or `emitSignal()` with a value its schema or DTO refuses; the client's options when it is created |
|
|
256
|
+
|
|
257
|
+
The errors thrown by the actions are given to the callers as D-Bus errors named after their `errno`
|
|
258
|
+
(`com.example.DeviceService.E_OFFLINE`), with their message; a client writing an invalid property value gets a D-Bus
|
|
259
|
+
error too.
|
|
260
|
+
|
|
261
|
+
## See also
|
|
37
262
|
|
|
38
|
-
|
|
263
|
+
- `@lakutata/entrypoint` (`lakutata/com/entrypoint`): the entrypoint, the controllers, the middlewares and the access
|
|
264
|
+
control (`doc/en/ActionPattern.md` of the package `lakutata`, Chinese in `doc/zh`).
|
|
265
|
+
- [dbus-sdk](https://github.com/myq1991/node-dbus-sdk): the D-Bus protocol, its client classes and its local services.
|
|
266
|
+
- `@lakutata/core` (`lakutata`): the components, `Application` and the dependency injection.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { EntrypointAdapter, MiddlewareDefinition } from '@lakutata/entrypoint';
|
|
2
|
+
import { DBusContext } from '../context/DBusContext.js';
|
|
3
|
+
/**
|
|
4
|
+
* The options of {@link dbusEntrypoint}: the bus to connect to, and the service name to own on it.
|
|
5
|
+
*/
|
|
6
|
+
export type DBusEntrypointOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* The adapter's name, which tells it apart from the entrypoint's other adapters.
|
|
9
|
+
* @default undefined
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The bus to connect to: `'session'` (the address in `DBUS_SESSION_BUS_ADDRESS`), `'system'` (the address in
|
|
14
|
+
* `DBUS_SYSTEM_BUS_ADDRESS`, else `unix:path=/var/run/dbus/system_bus_socket`), the address of a bus
|
|
15
|
+
* (`'unix:path=/run/my-bus.sock'`, `'tcp:host=127.0.0.1,port=5555'`), or the path of the socket of a private bus.
|
|
16
|
+
* The adapter fails to start when the bus cannot be reached (for `'session'`, also when `DBUS_SESSION_BUS_ADDRESS`
|
|
17
|
+
* is not set).
|
|
18
|
+
* @default 'session'
|
|
19
|
+
*/
|
|
20
|
+
bus?: 'session' | 'system' | string;
|
|
21
|
+
/**
|
|
22
|
+
* The well-known name the service owns on the bus (`'com.example.DeviceService'`): two or more elements separated by
|
|
23
|
+
* dots. The clients address the service by it, and it prefixes the names of the D-Bus errors. On the system bus,
|
|
24
|
+
* the bus policy (`/etc/dbus-1/system.d/`) must allow the process to own it.
|
|
25
|
+
*/
|
|
26
|
+
service: string;
|
|
27
|
+
/**
|
|
28
|
+
* Take the name from its current owner when that owner allowed it to be replaced (`allowReplacement`). Without it,
|
|
29
|
+
* the adapter fails to start when another connection owns the name; the request is never queued.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
replaceExisting?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Let another connection take the name with `replaceExisting`. The adapter is not stopped when the name is taken:
|
|
35
|
+
* the clients reach the new owner.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
allowReplacement?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The middlewares of this adapter's method calls, run after the entrypoint's global middlewares.
|
|
41
|
+
* @default []
|
|
42
|
+
*/
|
|
43
|
+
middlewares?: MiddlewareDefinition[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Build the D-Bus adapter of the entrypoint (its `dbus` option): the application owns a service name on a bus and
|
|
47
|
+
* serves the controllers declared with `@DBusInterface` as the interfaces of its objects, their `@DBusMethod` actions
|
|
48
|
+
* as the methods. The objects of the paths without variables are exported when the adapter starts, until it stops
|
|
49
|
+
* ({@link DBusObjectManager.removeObject} does not remove them); the objects of the path templates are exported when
|
|
50
|
+
* they are added with {@link DBusObjectManager.addObject}, before or after the start.
|
|
51
|
+
*
|
|
52
|
+
* Starting connects to the bus and requests the name. The adapter stops gracefully with the entrypoint: it releases the
|
|
53
|
+
* name (no new call reaches it), the calls in progress end, then the connection closes. It needs a D-Bus daemon, as on
|
|
54
|
+
* Linux: the system bus, a session bus, or a private `dbus-daemon`.
|
|
55
|
+
* @param options The bus, the service name and how it is owned, the adapter's middlewares.
|
|
56
|
+
* @returns The adapter, to give to the `dbus` option of `buildEntrypoints()`.
|
|
57
|
+
* @throws {TypeError} At once, when the service name is invalid; when the adapter starts, when an action declared with
|
|
58
|
+
* `@DBusMethod` belongs to a controller without `@DBusInterface`.
|
|
59
|
+
* @throws {Error} When the adapter starts, if the bus cannot be reached, or if another connection owns the name
|
|
60
|
+
* (`The DBus name … is owned by another service`): the entrypoint fails to start.
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import {Application} from 'lakutata'
|
|
64
|
+
* import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
|
|
65
|
+
* import {dbusEntrypoint, DBusInterface, DBusMethod} from '@lakutata/dbus'
|
|
66
|
+
*
|
|
67
|
+
* @DBusInterface('com.example.Manager', {path: '/com/example/Manager'})
|
|
68
|
+
* class ManagerController extends Controller {
|
|
69
|
+
* @DBusMethod('Ping', {returns: 's'})
|
|
70
|
+
* public async ping(): Promise<string> {
|
|
71
|
+
* return 'pong'
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* //busctl --system call com.example.DeviceService /com/example/Manager com.example.Manager Ping
|
|
76
|
+
* Application.run(() => ({
|
|
77
|
+
* id: 'devices.app',
|
|
78
|
+
* name: 'Devices',
|
|
79
|
+
* components: {
|
|
80
|
+
* entrypoint: buildEntrypoints({
|
|
81
|
+
* controllers: [ManagerController],
|
|
82
|
+
* dbus: dbusEntrypoint({bus: 'system', service: 'com.example.DeviceService'})
|
|
83
|
+
* })
|
|
84
|
+
* },
|
|
85
|
+
* bootstrap: ['entrypoint']
|
|
86
|
+
* }))
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function dbusEntrypoint(options: DBusEntrypointOptions): EntrypointAdapter<DBusContext>;
|