mqtt-plus 1.4.0 → 1.4.2

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.
Files changed (73) hide show
  1. package/AGENTS.md +55 -44
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +4 -3
  4. package/doc/mqtt-plus-api.md +693 -680
  5. package/doc/mqtt-plus-architecture.d2 +139 -0
  6. package/doc/mqtt-plus-architecture.md +10 -0
  7. package/doc/mqtt-plus-architecture.svg +95 -0
  8. package/doc/mqtt-plus-broker-setup.md +9 -3
  9. package/doc/mqtt-plus-comm.md +73 -0
  10. package/doc/mqtt-plus-internals.md +3 -3
  11. package/dst-stage1/mqtt-plus-base.d.ts +3 -2
  12. package/dst-stage1/mqtt-plus-base.js +53 -22
  13. package/dst-stage1/mqtt-plus-event.d.ts +0 -2
  14. package/dst-stage1/mqtt-plus-event.js +6 -26
  15. package/dst-stage1/mqtt-plus-meta.d.ts +2 -2
  16. package/dst-stage1/mqtt-plus-meta.js +2 -2
  17. package/dst-stage1/mqtt-plus-msg.d.ts +2 -0
  18. package/dst-stage1/mqtt-plus-msg.js +17 -0
  19. package/dst-stage1/mqtt-plus-service.d.ts +0 -5
  20. package/dst-stage1/mqtt-plus-service.js +12 -48
  21. package/dst-stage1/mqtt-plus-sink.d.ts +0 -10
  22. package/dst-stage1/mqtt-plus-sink.js +25 -92
  23. package/dst-stage1/mqtt-plus-source.d.ts +0 -10
  24. package/dst-stage1/mqtt-plus-source.js +23 -88
  25. package/dst-stage1/mqtt-plus-subscription.d.ts +20 -0
  26. package/dst-stage1/mqtt-plus-subscription.js +126 -0
  27. package/dst-stage1/mqtt-plus-timer.d.ts +8 -0
  28. package/dst-stage1/mqtt-plus-timer.js +57 -0
  29. package/dst-stage1/mqtt-plus-topic.d.ts +20 -0
  30. package/dst-stage1/mqtt-plus-topic.js +112 -0
  31. package/dst-stage1/mqtt-plus-trace.js +2 -0
  32. package/dst-stage1/mqtt-plus-util.d.ts +0 -13
  33. package/dst-stage1/mqtt-plus-util.js +1 -77
  34. package/dst-stage1/mqtt-plus-version.d.ts +0 -1
  35. package/dst-stage1/mqtt-plus-version.js +0 -6
  36. package/dst-stage1/tsc.tsbuildinfo +1 -1
  37. package/dst-stage2/mqtt-plus.cjs.js +242 -292
  38. package/dst-stage2/mqtt-plus.esm.js +240 -290
  39. package/dst-stage2/mqtt-plus.umd.js +12 -12
  40. package/etc/knip.jsonc +1 -1
  41. package/etc/stx.conf +6 -4
  42. package/package.json +1 -1
  43. package/src/mqtt-plus-base.ts +56 -26
  44. package/src/mqtt-plus-event.ts +8 -24
  45. package/src/mqtt-plus-meta.ts +3 -3
  46. package/src/mqtt-plus-msg.ts +28 -0
  47. package/src/mqtt-plus-service.ts +12 -50
  48. package/src/mqtt-plus-sink.ts +32 -105
  49. package/src/mqtt-plus-source.ts +29 -99
  50. package/src/mqtt-plus-subscription.ts +141 -0
  51. package/src/mqtt-plus-timer.ts +61 -0
  52. package/src/mqtt-plus-trace.ts +4 -0
  53. package/src/mqtt-plus-util.ts +1 -81
  54. package/src/mqtt-plus-version.ts +0 -7
  55. package/tst/mqtt-plus-0-fixture.ts +2 -2
  56. package/tst/mqtt-plus-0-mosquitto.ts +5 -0
  57. package/tst/mqtt-plus-1-api.spec.ts +1 -1
  58. package/tst/mqtt-plus-2-event.spec.ts +0 -6
  59. package/tst/mqtt-plus-3-service.spec.ts +3 -7
  60. package/tst/mqtt-plus-4-sink.spec.ts +14 -9
  61. package/tst/mqtt-plus-5-source.spec.ts +11 -5
  62. package/tst/mqtt-plus-6-misc.spec.ts +23 -23
  63. package/tst/tsc.json +1 -1
  64. package/doc/mqtt-plus-communication.md +0 -68
  65. /package/doc/{mqtt-plus-1-event-emission.d2 → mqtt-plus-comm-event-emission.d2} +0 -0
  66. /package/doc/{mqtt-plus-1-event-emission.svg → mqtt-plus-comm-event-emission.svg} +0 -0
  67. /package/doc/{mqtt-plus-2-service-call.d2 → mqtt-plus-comm-service-call.d2} +0 -0
  68. /package/doc/{mqtt-plus-2-service-call.svg → mqtt-plus-comm-service-call.svg} +0 -0
  69. /package/doc/{mqtt-plus-3-sink-push.d2 → mqtt-plus-comm-sink-push.d2} +0 -0
  70. /package/doc/{mqtt-plus-3-sink-push.svg → mqtt-plus-comm-sink-push.svg} +0 -0
  71. /package/doc/{mqtt-plus-4-source-fetch.d2 → mqtt-plus-comm-source-fetch.d2} +0 -0
  72. /package/doc/{mqtt-plus-4-source-fetch.svg → mqtt-plus-comm-source-fetch.svg} +0 -0
  73. /package/{doc/theme.d2 → etc/d2.theme.d2} +0 -0
@@ -1,682 +1,695 @@
1
1
 
2
- Application Programming Interface
3
- ---------------------------------
4
-
5
- The **MQTT+** API provides the following functionalities:
6
-
7
- - **Construction**:<br/>
8
-
9
- /* (simplified TypeScript API method signature) */
10
- constructor<API extends Record<string,
11
- Event< (...args: any[]) => void | Promise<void>> |
12
- Service< (...args: any[]) => any | Promise<any> > |
13
- Source< (...args: any[]) => void | Promise<void>> |
14
- Sink< (...args: any[]) => void | Promise<void>>
15
- >>(
16
- mqtt: MqttClient | null,
17
- options?: {
18
- id: string
19
- codec: "cbor" | "json"
20
- timeout: number
21
- chunkSize: number
22
- chunkCredit: number
23
- topicMake: (name: string, operation: string, peerId?: string) => string
24
- topicMatch: (topic: string) => { name: string, operation: string, peerId?: string } | null
25
- }
26
- )
27
-
28
- The `API` is a TypeScript type,
29
- describing the available events, services, sources, and sinks.
30
-
31
- - The `mqtt` is the [MQTT.js](https://www.npmjs.com/package/mqtt) instance,
32
- which has to be established separately. A `null` MQTT instance can be
33
- used for performing dry-runs (see *Dry-Run Publishing for MQTT Last-Will* under
34
- **Event Emission** below).
35
-
36
- - The optional `options` object supports the following fields:
37
- - `id`: Custom MQTT peer identifier (default: auto-generated NanoID).
38
- - `codec`: Encoding format, either `cbor` or `json` (default: `cbor`).
39
- - `timeout`: Communication timeout in milliseconds (default: `10000`).
40
- - `chunkSize`: Chunk size in bytes for source/sink transfers (default: `16384`).
41
- - `chunkCredit`: Number of credit units for flow control in source/sink
42
- chunked transfers (default: `4`). Controls how many chunks can be
43
- in-flight before the receiver must grant additional credit.
44
- - `topicMake`: Custom topic generation function.
45
- The `operation` parameter is one of: `event-emission`,
46
- `service-call-request`, `service-call-response`,
47
- `source-fetch-request`, `source-fetch-response`,
48
- `source-fetch-chunk`, `source-fetch-credit`,
49
- `sink-push-request`, `sink-push-response`,
50
- `sink-push-chunk`, `sink-push-credit`. (default: `` (name, operation, peerId) =>
51
- `${name}/${operation}/${peerId ?? "any"}` ``)
52
- - `topicMatch`: Custom topic matching function.
53
- Returns `{ name: string, operation: string, peerId?: string }` or `null` if no match.
54
- The `peerId` is `undefined` for broadcast topics (those ending with `/any`).
55
- (default: `` (topic) => { const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/); return m ? { name: m[1], operation: m[2], peerId: m[3] === "any" ? undefined : m[3] } : null } ``)
56
-
57
- - **Destruction**:<br/>
58
-
59
- destroy(): void
60
-
61
- Clean up the MQTT+ instance by removing all event listeners.
62
- Call this method when the instance is no longer needed.
63
- The companion MQTT.js instance has to be destroyed separately.
64
-
65
- - **Event Handling**:<br/>
66
-
67
- /* listen for error or log events */
68
- on(event: "error", callback: (error: Error) => void): void
69
- on(event: "log", callback: (log: LogEvent) => void): void
70
-
71
- /* remove error or log event listener */
72
- off(event: "error", callback: (error: Error) => void): void
73
- off(event: "log", callback: (log: LogEvent) => void): void
74
-
75
- MQTT+ emits `error` and `log` events for monitoring and debugging.
76
-
77
- - The `on()` method registers an event listener.
78
- The `"error"` event is emitted when an error occurs during
79
- message processing, subscription, or publishing.
80
- The `"log"` event is emitted for informational and debug-level
81
- messages with a `LogEvent` object containing `timestamp`, `level`,
82
- `msg`, and optional `data` fields.
83
-
84
- - The `off()` method removes a previously registered event listener.
85
-
86
- - The `LogEvent` object provides `resolve()` for resolving lazy
87
- promise-based fields and `toString()` for rendering log entries
88
- as formatted strings.
89
-
90
- Example:
91
-
92
- mqttp.on("error", (err) => {
93
- console.error("MQTT+ error:", err.message)
94
- })
95
- mqttp.on("log", (log) => {
96
- console.log(log.toString())
97
- })
98
-
99
- - **Authentication**:<br/>
100
-
101
- /* store server-side secret credential */
102
- credential(credential: string): void
103
-
104
- /* issue client-side token on server-side */
105
- issue(payload: { roles: string[], id?: string }): Promise<string>
106
-
107
- /* add/remove client-side token (client-side) */
108
- authenticate(token: string): void
109
- authenticate(token: string, remove: boolean): void
110
-
111
- MQTT+ provides JWT-based authentication for securing events, services,
112
- sources, and sinks. Authentication works by issuing tokens on the
113
- server-side and validating them when messages are received.
114
-
115
- - The `credential()` method sets the secret key used for signing and
116
- verifying JWT tokens. This must be called before `issue()` can be
117
- used.
118
-
119
- - The `issue()` method creates a new JWT token with the specified `roles` array.
120
- The optional `id` field can bind the token to a specific client identifier.
121
-
122
- - The `authenticate()` method manages client-side tokens:
123
- called with a token, adds the token to the set of active tokens;
124
- called with a token and `true`, removes the token from the set.
125
-
126
- - When a client has tokens set via `authenticate()`, they are automatically
127
- included in outgoing `emit()`, `call()`, `push()`, and `fetch()` requests.
128
-
129
- Example:
130
-
131
- /* server: set credential and issue token */
132
- mqttp.credential("my-secret-key")
133
- const token = await mqttp.issue({ roles: [ "admin", "user" ] })
134
-
135
- /* client: add token for authentication */
136
- mqttp.authenticate(token)
137
-
138
- - **Meta Information**:<br/>
139
-
140
- /* set meta information by key */
141
- meta(key: string, value: any): void
142
-
143
- /* retrieve meta information by key */
144
- meta(key: string): any
145
-
146
- /* delete meta information by key */
147
- meta(key: string, value: null): void
148
-
149
- MQTT+ allows attaching persistent meta-data to an instance that is
150
- automatically included in all outgoing messages. This is useful for
151
- adding context information like client version, environment, or user
152
- identity to every request.
2
+ MQTT+ Application Programming Interface
3
+ =======================================
4
+
5
+ Construction
6
+ ------------
7
+
8
+ /* (simplified TypeScript API method signature) */
9
+ constructor<API extends Record<string,
10
+ Event< (...args: any[]) => void | Promise<void>> |
11
+ Service< (...args: any[]) => any | Promise<any> > |
12
+ Source< (...args: any[]) => void | Promise<void>> |
13
+ Sink< (...args: any[]) => void | Promise<void>>
14
+ >>(
15
+ mqtt: MqttClient | null,
16
+ options?: {
17
+ id: string
18
+ codec: "cbor" | "json"
19
+ timeout: number
20
+ chunkSize: number
21
+ chunkCredit: number
22
+ topicMake: (name: string, operation: string, peerId?: string) => string
23
+ topicMatch: (topic: string) => { name: string, operation: string, peerId?: string } | null
24
+ }
25
+ )
26
+
27
+ The `API` is a TypeScript type,
28
+ describing the available events, services, sources, and sinks.
29
+
30
+ - The `mqtt` is the [MQTT.js](https://www.npmjs.com/package/mqtt) instance,
31
+ which has to be established separately. A `null` MQTT instance can be
32
+ used for performing dry-runs (see *Dry-Run Publishing for MQTT Last-Will* under
33
+ **Event Emission** below).
34
+
35
+ - The optional `options` object supports the following fields:
36
+ - `id`: Custom MQTT peer identifier (default: auto-generated NanoID).
37
+ - `codec`: Encoding format, either `cbor` or `json` (default: `cbor`).
38
+ - `timeout`: Communication timeout in milliseconds (default: `10000`).
39
+ - `chunkSize`: Chunk size in bytes for source/sink transfers (default: `16384`).
40
+ - `chunkCredit`: Number of credit units for flow control in source/sink
41
+ chunked transfers (default: `4`). Controls how many chunks can be
42
+ in-flight before the receiver must grant additional credit.
43
+ - `topicMake`: Custom topic generation function.
44
+ The `operation` parameter is one of: `event-emission`,
45
+ `service-call-request`, `service-call-response`,
46
+ `source-fetch-request`, `source-fetch-response`,
47
+ `source-fetch-chunk`, `source-fetch-credit`,
48
+ `sink-push-request`, `sink-push-response`,
49
+ `sink-push-chunk`, `sink-push-credit`. (default: `` (name, operation, peerId) =>
50
+ `${name}/${operation}/${peerId ?? "any"}` ``)
51
+ - `topicMatch`: Custom topic matching function.
52
+ Returns `{ name: string, operation: string, peerId?: string }` or `null` if no match.
53
+ The `peerId` is `undefined` for broadcast topics (those ending with `/any`).
54
+ (default: `` (topic) => { const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/); return m ? { name: m[1], operation: m[2], peerId: m[3] === "any" ? undefined : m[3] } : null } ``)
55
+
56
+ Destruction
57
+ -----------
58
+
59
+ destroy(): void
60
+
61
+ Clean up the MQTT+ instance by removing all event listeners.
62
+ Call this method when the instance is no longer needed.
63
+ The companion MQTT.js instance has to be destroyed separately.
64
+
65
+ Event Handling
66
+ --------------
67
+
68
+ /* listen for error or log events */
69
+ on(event: "error", callback: (error: Error) => void): void
70
+ on(event: "log", callback: (log: LogEvent) => void): void
71
+
72
+ /* remove error or log event listener */
73
+ off(event: "error", callback: (error: Error) => void): void
74
+ off(event: "log", callback: (log: LogEvent) => void): void
75
+
76
+ MQTT+ emits `error` and `log` events for monitoring and debugging.
77
+
78
+ - The `on()` method registers an event listener.
79
+ The `"error"` event is emitted when an error occurs during
80
+ message processing, subscription, or publishing.
81
+ The `"log"` event is emitted for informational and debug-level
82
+ messages with a `LogEvent` object containing `timestamp`, `level`,
83
+ `msg`, and optional `data` fields.
84
+
85
+ - The `off()` method removes a previously registered event listener.
86
+
87
+ - The `LogEvent` object provides `resolve()` for resolving lazy
88
+ promise-based fields and `toString()` for rendering log entries
89
+ as formatted strings.
90
+
91
+ Example:
92
+
93
+ mqttp.on("error", (err) => {
94
+ console.error("MQTT+ error:", err.message)
95
+ })
96
+ mqttp.on("log", (log) => {
97
+ console.log(log.toString())
98
+ })
99
+
100
+ Authentication
101
+ --------------
102
+
103
+ /* store server-side secret credential */
104
+ credential(credential: string): void
105
+
106
+ /* issue client-side token on server-side */
107
+ issue(payload: { roles: string[], id?: string }): Promise<string>
108
+
109
+ /* add/remove client-side token (client-side) */
110
+ authenticate(token: string): void
111
+ authenticate(token: string, remove: boolean): void
112
+
113
+ MQTT+ provides JWT-based authentication for securing events, services,
114
+ sources, and sinks. Authentication works by issuing tokens on the
115
+ server-side and validating them when messages are received.
116
+
117
+ - The `credential()` method sets the secret key used for signing and
118
+ verifying JWT tokens. This must be called before `issue()` can be
119
+ used.
120
+
121
+ - The `issue()` method creates a new JWT token with the specified `roles` array.
122
+ The optional `id` field can bind the token to a specific client identifier.
123
+
124
+ - The `authenticate()` method manages client-side tokens:
125
+ called with a token, adds the token to the set of active tokens;
126
+ called with a token and `true`, removes the token from the set.
127
+
128
+ - When a client has tokens set via `authenticate()`, they are automatically
129
+ included in outgoing `emit()`, `call()`, `push()`, and `fetch()` requests.
130
+
131
+ Example:
132
+
133
+ /* server: set credential and issue token */
134
+ mqttp.credential("my-secret-key")
135
+ const token = await mqttp.issue({ roles: [ "admin", "user" ] })
136
+
137
+ /* client: add token for authentication */
138
+ mqttp.authenticate(token)
139
+
140
+ Meta Information
141
+ ----------------
142
+
143
+ /* set meta information by key */
144
+ meta(key: string, value: any): void
145
+
146
+ /* retrieve meta information by key */
147
+ meta(key: string): any
148
+
149
+ /* delete meta information by key */
150
+ meta(key: string, value: null): void
151
+
152
+ MQTT+ allows attaching persistent meta-data to an instance that is
153
+ automatically included in all outgoing messages. This is useful for
154
+ adding context information like client version, environment, or user
155
+ identity to every request.
156
+
157
+ - The `meta()` method manages instance-level meta-data:
158
+ called with a key only, retrieves the meta-data entry for that key;
159
+ called with a key and non-null value, sets the meta-data entry;
160
+ called with a key and `null`, deletes the meta-data entry.
161
+
162
+ - Instance-level meta-data set via `meta()` is merged with any per-request
163
+ `meta` option passed to `emit()`, `call()`, `push()`, or `fetch()`.
164
+ Per-request meta-data takes precedence over instance-level metadata.
165
+
166
+ - On the receiving side, meta-data is available via the `info.meta`
167
+ field in callbacks for `event()`, `service()`, `source()`, and `sink()`.
168
+ For `fetch()`, the returned `meta` promise resolves to the meta-data
169
+ sent by the source.
170
+
171
+ Example:
172
+
173
+ /* client: set instance-level metadata */
174
+ mqttp.meta("clientVersion", "1.0.0")
175
+ mqttp.meta("environment", "production")
176
+
177
+ /* client: retrieve a metadata entry */
178
+ const environment = mqttp.meta("environment")
179
+
180
+ /* client: delete a metadata entry */
181
+ mqttp.meta("environment", null)
182
+
183
+ /* client: per-request metadata (merged with instance-level) */
184
+ mqttp.call({ name: "example/hello", params: [ "world" ], meta: { requestId: "123" } })
185
+
186
+ /* server: access meta-data in callback */
187
+ await mqttp.service("example/hello", (arg, info) => {
188
+ console.log(info.meta?.clientVersion) /* "1.0.0" */
189
+ console.log(info.meta?.requestId) /* "123" */
190
+ return `hello ${arg}`
191
+ })
192
+
193
+ Event Registration
194
+ ------------------
195
+
196
+ /* (simplified TypeScript API method signature) */
197
+ event(
198
+ name: string,
199
+ callback: (
200
+ ...params: any[],
201
+ info: {
202
+ sender: string,
203
+ receiver?: string,
204
+ authenticated?: boolean,
205
+ meta?: Record<string, any>
206
+ }
207
+ ) => void | Promise<void>
208
+ ): Promise<Registration>
209
+ event({
210
+ name: string,
211
+ callback: (
212
+ ...params: any[],
213
+ info: {
214
+ sender: string,
215
+ receiver?: string,
216
+ authenticated?: boolean,
217
+ meta?: Record<string, any>
218
+ }
219
+ ) => void | Promise<void>,
220
+ options?: MQTT::IClientSubscribeOptions,
221
+ share?: string,
222
+ auth?: string | { mode: "require" | "optional", roles: string[] }
223
+ }): Promise<Registration>
224
+
225
+ Register for an event.
226
+
227
+ - The `name` has to be a valid MQTT topic name.
228
+
229
+ - The `callback` is called with the `params` passed to a remote `emit()`.
230
+ There is no return value of `callback`.
231
+
232
+ - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
233
+
234
+ - The optional `share` enables
235
+ [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
236
+ (MQTT 5.0) for load-balancing messages across multiple registrations
237
+ by specifying a group name. This internally prefixes the event with
238
+ `$share/<share>/`.
239
+
240
+ - The optional `auth` enables authentication validation on incoming events.
241
+ When set to a role name string (e.g., `"admin"`), authentication is required
242
+ and the token must include that role. When set to an object `{ mode, roles }`,
243
+ the mode can be `"require"` (reject unauthenticated) or `"optional"` (accept all
244
+ but reflect validation result in `info.authenticated`), and roles specifies
245
+ the required role names.
246
+
247
+ - Internally, on the MQTT broker, the topics generated by
248
+ `topicMake(name, "event-emission")` (default: `${name}/event-emission/any` and
249
+ `${name}/event-emission/${peerId}`) are subscribed.
250
+
251
+ - Returns a `Registration` object with a `destroy()` method.
252
+
253
+ Event Emission
254
+ --------------
255
+
256
+ /* (simplified TypeScript API method signature) */
257
+ emit(
258
+ event: string,
259
+ ...params: any[]
260
+ ): void
261
+ emit({
262
+ event: string,
263
+ params: any[],
264
+ receiver?: string,
265
+ options?: MQTT::IClientPublishOptions,
266
+ meta?: Record<string, any>
267
+ }): void
268
+ emit({
269
+ event: string,
270
+ params: any[],
271
+ receiver?: string,
272
+ options?: MQTT::IClientPublishOptions,
273
+ meta?: Record<string, any>,
274
+ dry: true
275
+ }): { topic: string, payload: string | Uint8Array, options: IClientPublishOptions }
276
+
277
+ Emit an event to all subscribers or a specific subscriber ("fire and forget").
278
+
279
+ - The optional `receiver` directs the event to a specific subscriber only.
280
+
281
+ - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
282
+
283
+ - The optional `meta` sends additional metadata alongside the event,
284
+ which is merged with instance-level metadata set via `meta()`.
285
+
286
+ - The optional `dry` flag, when set to `true`, returns the publish information
287
+ (`topic`, `payload`, `options`) instead of actually publishing to the MQTT broker.
288
+ This is useful for generating MQTT "last will" messages (see example below).
289
+
290
+ - The remote `event()` `callback` is called with `params` and its
291
+ return value is silently ignored.
292
+
293
+ - Internally, publishes to the MQTT topic by `topicMake(event, "event-emission", peerId)`
294
+ (default: `${event}/event-emission/any` or `${event}/event-emission/${peerId}`).
295
+
296
+ - *Dry-Run Publishing for MQTT Last-Will:*
297
+ When you need to set up an MQTT "last will" message (automatically published
298
+ by the broker when a client disconnects *unexpectedly*), you can use `dry: true`
299
+ together with a `null` MQTT client:
300
+
301
+ type API = {
302
+ "example/connection": Event<(state: "open" | "close") => void>
303
+ [...]
304
+ }
305
+ const mqttpDry = new MQTTp<API>(null, { id: "my-client" })
306
+ const will = mqttpDry.emit({
307
+ dry: true,
308
+ event: "example/connection",
309
+ params: [ "close" ],
310
+ [...]
311
+ })
312
+ mqttpDry.destroy()
313
+ const mqtt = MQTT.connect("[...]", {
314
+ will: {
315
+ topic: will.topic,
316
+ payload: will.payload,
317
+ qos: will.options.qos
318
+ },
319
+ [...]
320
+ })
321
+
322
+ Service Registration
323
+ --------------------
324
+
325
+ /* (simplified TypeScript API method signature) */
326
+ service(
327
+ name: string,
328
+ callback: (
329
+ ...params: any[],
330
+ info: {
331
+ sender: string,
332
+ receiver?: string,
333
+ authenticated?: boolean,
334
+ meta?: Record<string, any>
335
+ }
336
+ ) => any | Promise<any>
337
+ ): Promise<Registration>
338
+ service({
339
+ name: string,
340
+ callback: (
341
+ ...params: any[],
342
+ info: {
343
+ sender: string,
344
+ receiver?: string,
345
+ authenticated?: boolean,
346
+ meta?: Record<string, any>
347
+ }
348
+ ) => any | Promise<any>,
349
+ options?: MQTT::IClientSubscribeOptions,
350
+ share?: string,
351
+ auth?: string | { mode: "require" | "optional", roles: string[] }
352
+ }): Promise<Registration>
353
+
354
+ Register a service.
355
+
356
+ - The `name` has to be a valid MQTT topic name.
357
+
358
+ - The `callback` is called with the `params` passed to a remote `call()`.
359
+ The return value of `callback` will resolve the `Promise` returned by the remote `call()`.
360
+
361
+ - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
362
+
363
+ - The optional `share` enables
364
+ [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
365
+ (MQTT 5.0) for load-balancing service calls across multiple services
366
+ by specifying a group name. This internally prefixes the service
367
+ with `$share/<share>/`. By default a share named `default` is used.
368
+
369
+ - The optional `auth` enables authentication validation on incoming service calls.
370
+ When set to a role name string (e.g., `"admin"`), authentication is required
371
+ and the token must include that role. When set to an object `{ mode, roles }`,
372
+ the mode can be `"require"` (reject unauthenticated with error response) or
373
+ `"optional"` (accept all but reflect validation result in `info.authenticated`),
374
+ and roles specifies the required role names.
375
+
376
+ - Internally, on the MQTT broker, the topics generated by
377
+ `topicMake(name, "service-call-request")` (default: `${name}/service-call-request/any` and
378
+ `${name}/service-call-request/${peerId}`) are subscribed.
379
+
380
+ - Returns a `Registration` object with a `destroy()` method.
381
+
382
+ Service Call
383
+ ------------
384
+
385
+ /* (simplified TypeScript API method signature) */
386
+ call(
387
+ name: string,
388
+ ...params: any[]
389
+ ): Promise<any>
390
+ call({
391
+ name: string,
392
+ params: any[],
393
+ receiver?: string,
394
+ options?: MQTT::IClientPublishOptions,
395
+ meta?: Record<string, any>
396
+ }): Promise<any>
397
+
398
+ Call a service on all registrants or on a specific registrant ("request and response").
399
+
400
+ - The optional `receiver` directs the call to a specific registrant only.
401
+
402
+ - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
403
+
404
+ - The optional `meta` sends additional metadata alongside the service call,
405
+ which is merged with instance-level metadata set via `meta()`.
406
+
407
+ - The remote `service()` `callback` is called with `params` and its
408
+ return value resolves the returned `Promise`. If the remote `callback`
409
+ throws an exception, this rejects the returned `Promise`.
410
+
411
+ - Internally, on the MQTT broker, the topic by
412
+ `topicMake(service, "service-call-response", peerId)` (default:
413
+ `${service}/service-call-response/${peerId}`) is temporarily
414
+ subscribed for receiving the response.
415
+
416
+ Sink Registration
417
+ -----------------
418
+
419
+ /* (simplified TypeScript API method signature) */
420
+ sink(
421
+ name: string,
422
+ callback: (
423
+ ...params: any[],
424
+ info: {
425
+ sender: string,
426
+ receiver?: string,
427
+ authenticated?: boolean,
428
+ meta?: Record<string, any>,
429
+ stream?: Readable,
430
+ buffer?: Promise<Uint8Array>
431
+ }
432
+ ) => void | Promise<void>
433
+ ): Promise<Registration>
434
+ sink({
435
+ name: string,
436
+ callback: (
437
+ ...params: any[],
438
+ info: {
439
+ sender: string,
440
+ receiver?: string,
441
+ authenticated?: boolean,
442
+ meta?: Record<string, any>,
443
+ stream?: Readable,
444
+ buffer?: Promise<Uint8Array>
445
+ }
446
+ ) => void | Promise<void>,
447
+ options?: MQTT::IClientSubscribeOptions,
448
+ share?: string,
449
+ auth?: string | { mode: "require" | "optional", roles: string[] }
450
+ }): Promise<Registration>
451
+
452
+ Register a sink for receiving data.
453
+
454
+ - The `name` has to be a valid MQTT topic name.
455
+
456
+ - The `callback` is called with the `params` passed to a remote `push()`.
457
+ The `info.stream` provides a Node.js `Readable` stream for consuming the pushed data.
458
+ The `info.buffer` provides a lazy `Promise<Uint8Array>` that resolves to the complete data once the stream ends.
459
+ The `info.meta` contains optional metadata sent by the pusher via `push()`.
460
+
461
+ - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
462
+
463
+ - The optional `share` enables
464
+ [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
465
+ (MQTT 5.0) for load-balancing sink pushes across multiple sink
466
+ handlers by specifying a group name. This internally prefixes the
467
+ sink with `$share/<share>/`. By default a share named `default` is
468
+ used.
469
+
470
+ - The optional `auth` enables authentication validation on incoming sink pushes.
471
+ When set to a role name string (e.g., `"admin"`), authentication
472
+ is required and the token must include that role. When set to an
473
+ object `{ mode, roles }`, the mode can be `"require"` (reject
474
+ unauthenticated) or `"optional"` (accept all but reflect validation
475
+ result in `info.authenticated`), and roles specifies the required
476
+ role names.
477
+
478
+ - Internally, on the MQTT broker, the topics generated by
479
+ `topicMake(name, "sink-push-request")`
480
+ (default: `${name}/sink-push-request/any` and
481
+ `${name}/sink-push-request/${peerId}`) and
482
+ `topicMake(name, "sink-push-chunk", peerId)`
483
+ (default: `${name}/sink-push-chunk/${peerId}`) are subscribed.
484
+
485
+ - Returns a `Registration` object with a `destroy()` method.
486
+
487
+ Sink Push
488
+ ---------
489
+
490
+ /* (simplified TypeScript API method signature) */
491
+ push(
492
+ name: string,
493
+ data: Readable | Uint8Array,
494
+ ...params: any[]
495
+ ): Promise<void>
496
+ push({
497
+ name: string,
498
+ data: Readable | Uint8Array,
499
+ params: any[]
500
+ meta?: Record<string, any>,
501
+ receiver?: string,
502
+ options?: MQTT::IClientPublishOptions
503
+ }): Promise<void>
504
+
505
+ Pushes data to all established sinks or a specific sink handler.
506
+
507
+ - The `data` is either a Node.js `Readable` stream or a `Uint8Array` providing the data to push.
508
+
509
+ - The optional `meta` sends metadata alongside the data,
510
+ which becomes available on the sink handler side via `info.meta`.
511
+
512
+ - The optional `receiver` directs the push to a specific sink handler only.
513
+
514
+ - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
515
+
516
+ - The data is read from `data` in chunks (default: 16KB,
517
+ configurable via `chunkSize` option) and sent over MQTT until the
518
+ stream is closed or the buffer is fully transferred.
519
+ The returned `Promise` resolves when the entire data has been pushed.
520
+
521
+ - The remote `sink()` `callback` is called with `params` and an `info` object
522
+ containing `stream` (`Readable`) for consuming the pushed data,
523
+ `buffer` (lazy `Promise<Uint8Array>`) that resolves to the complete
524
+ data once the stream ends, and `meta` (`Record<string, any> |
525
+ undefined`) containing the metadata sent by the pusher.
526
+
527
+ - Internally, on the MQTT broker, the topic by
528
+ `topicMake(name, "sink-push-response", peerId)` (default:
529
+ `${name}/sink-push-response/${peerId}`) is temporarily
530
+ subscribed for receiving the ack/nak response,
531
+ then publishes to the MQTT topic by `topicMake(name, "sink-push-request", peerId)`
532
+ (default: `${name}/sink-push-request/any` or `${name}/sink-push-request/${peerId}`)
533
+ for the initial request, `topicMake(name, "sink-push-chunk", peerId)`
534
+ (default: `${name}/sink-push-chunk/${peerId}`) for the data chunks,
535
+ and optionally `topicMake(name, "sink-push-credit", peerId)`
536
+ (default: `${name}/sink-push-credit/${peerId}`) for credit-based flow control.
537
+
538
+ Source Registration
539
+ -------------------
540
+
541
+ /* (simplified TypeScript API method signature) */
542
+ source(
543
+ name: string,
544
+ callback: (
545
+ ...params: any[],
546
+ info: {
547
+ sender: string,
548
+ receiver?: string,
549
+ authenticated?: boolean,
550
+ meta?: Record<string, any>,
551
+ stream?: Readable,
552
+ buffer?: Promise<Uint8Array>
553
+ }
554
+ ) => void | Promise<void>
555
+ ): Promise<Registration>
556
+ source({
557
+ name: string,
558
+ callback: (
559
+ ...params: any[],
560
+ info: {
561
+ sender: string,
562
+ receiver?: string,
563
+ authenticated?: boolean,
564
+ meta?: Record<string, any>,
565
+ stream?: Readable,
566
+ buffer?: Promise<Uint8Array>
567
+ }
568
+ ) => void | Promise<void>,
569
+ options?: MQTT::IClientSubscribeOptions,
570
+ share?: string,
571
+ auth?: string | { mode: "require" | "optional", roles: string[] }
572
+ }): Promise<Registration>
573
+
574
+ Register a source for sending data.
575
+
576
+ - The `name` has to be a valid MQTT topic name.
577
+
578
+ - The `callback` is called with the `params` passed to a remote `fetch()`.
579
+ The `callback` should set `info.stream` to a `Readable` or
580
+ `info.buffer` to a `Promise<Uint8Array>` containing the data.
581
+ Optionally, the `callback` can set `info.meta` to a `Record<string,
582
+ any>` to send metadata back with the response.
583
+
584
+ - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
585
+
586
+ - The optional `share` enables
587
+ [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
588
+ (MQTT 5.0) for load-balancing source requests across multiple
589
+ sources by specifying a group name. This internally prefixes the
590
+ source with `$share/<share>/`. By default a share named `default` is
591
+ used.
592
+
593
+ - The optional `auth` enables authentication validation on incoming source fetches.
594
+ When set to a role name string (e.g., `"admin"`), authentication
595
+ is required and the token must include that role. When set to an
596
+ object `{ mode, roles }`, the mode can be `"require"` (reject
597
+ unauthenticated) or `"optional"` (accept all but reflect validation
598
+ result in `info.authenticated`), and roles specifies the required
599
+ role names.
600
+
601
+ - Internally, on the MQTT broker, the topics generated by
602
+ `topicMake(name, "source-fetch-request")`
603
+ (default: `${name}/source-fetch-request/any` and
604
+ `${name}/source-fetch-request/${peerId}`) and
605
+ `topicMake(name, "source-fetch-credit", peerId)`
606
+ (default: `${name}/source-fetch-credit/${peerId}`) are subscribed.
607
+
608
+ - Returns a `Registration` object with a `destroy()` method.
609
+
610
+ Source Fetch
611
+ ------------
612
+
613
+ /* (simplified TypeScript API method signature) */
614
+ fetch(
615
+ name: string,
616
+ ...params: any[]
617
+ ): Promise<{
618
+ stream: Readable,
619
+ buffer: Promise<Uint8Array>,
620
+ meta: Promise<Record<string, any> | undefined>
621
+ }>
622
+ fetch({
623
+ name: string,
624
+ params: any[],
625
+ receiver?: string,
626
+ options?: MQTT::IClientPublishOptions,
627
+ meta?: Record<string, any>
628
+ }): Promise<{
629
+ stream: Readable,
630
+ buffer: Promise<Uint8Array>,
631
+ meta: Promise<Record<string, any> | undefined>
632
+ }>
633
+
634
+ Fetches data from any source or from a specific source.
635
+
636
+ - The optional `receiver` directs the call to a specific source only.
637
+
638
+ - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
639
+
640
+ - The optional `meta` sends additional metadata alongside the fetch request,
641
+ which is merged with instance-level metadata set via `meta()`.
642
+
643
+ - Returns an object with a `stream` (`Readable`) for consuming the transferred data,
644
+ a lazy `buffer` (`Promise<Uint8Array>`) that resolves
645
+ to the complete data once the stream ends, and a `meta`
646
+ (`Promise<Record<string, any> | undefined>`) that resolves to
647
+ optional metadata sent by the source when the first chunk arrives.
648
+
649
+ - The remote `source()` `callback` is called with `params` and
650
+ should set `info.stream` to a `Readable` or `info.buffer` to
651
+ a `Promise<Uint8Array>` containing the data. Optionally, the
652
+ `callback` can set `info.meta` to send metadata back with the
653
+ response. If the remote `callback` throws an exception, this
654
+ destroys the stream with the error.
655
+
656
+ - Internally, on the MQTT broker, the topics by
657
+ `topicMake(name, "source-fetch-response", peerId)`
658
+ and `topicMake(name, "source-fetch-chunk", peerId)`
659
+ (default: `${name}/source-fetch-response/${peerId}` and
660
+ `${name}/source-fetch-chunk/${peerId}`) are temporarily subscribed
661
+ for receiving the response and data chunks.
662
+
663
+ Data Type Conversion Utilities
664
+ ------------------------------
665
+
666
+ /* convert character string to buffer */
667
+ str2buf(data: string): Uint8Array
668
+
669
+ /* convert buffer to character string */
670
+ buf2str(data: Uint8Array): string
671
+
672
+ /* convert byte-based typed array to buffer */
673
+ arr2buf(data: Buffer | Uint8Array | Int8Array): Uint8Array
674
+
675
+ /* convert buffer to byte-based typed array */
676
+ buf2arr(data: Uint8Array, type: typeof Buffer): Buffer
677
+ buf2arr(data: Uint8Array, type: typeof Uint8Array): Uint8Array
678
+ buf2arr(data: Uint8Array, type: typeof Int8Array): Int8Array
679
+
680
+ MQTT+ provides utility methods for converting between strings,
681
+ buffers, and typed arrays. These are useful when working with binary
682
+ data in source/sink transfers or when interfacing with API methods that
683
+ expect specific data types.
684
+
685
+ Example:
686
+
687
+ /* string to buffer conversion */
688
+ const buffer = mqttp.str2buf("Hello, World!")
689
+ const text = mqttp.buf2str(buffer)
690
+
691
+ /* typed array conversions */
692
+ const ui8a = mqttp.arr2buf(buffer)
693
+ const buffer = mqttp.buf2arr(ui8a, Buffer)
694
+ const i8a = mqttp.buf2arr(ui8a, Int8Array)
153
695
 
154
- - The `meta()` method manages instance-level meta-data:
155
- called with a key only, retrieves the meta-data entry for that key;
156
- called with a key and non-null value, sets the meta-data entry;
157
- called with a key and `null`, deletes the meta-data entry.
158
-
159
- - Instance-level meta-data set via `meta()` is merged with any per-request
160
- `meta` option passed to `emit()`, `call()`, `push()`, or `fetch()`.
161
- Per-request meta-data takes precedence over instance-level metadata.
162
-
163
- - On the receiving side, meta-data is available via the `info.meta`
164
- field in callbacks for `event()`, `service()`, `source()`, and `sink()`.
165
- For `fetch()`, the returned `meta` promise resolves to the meta-data
166
- sent by the source.
167
-
168
- Example:
169
-
170
- /* client: set instance-level metadata */
171
- mqttp.meta("clientVersion", "1.0.0")
172
- mqttp.meta("environment", "production")
173
-
174
- /* client: retrieve a metadata entry */
175
- const environment = mqttp.meta("environment")
176
-
177
- /* client: delete a metadata entry */
178
- mqttp.meta("environment", null)
179
-
180
- /* client: per-request metadata (merged with instance-level) */
181
- mqttp.call({ name: "example/hello", params: [ "world" ], meta: { requestId: "123" } })
182
-
183
- /* server: access meta-data in callback */
184
- await mqttp.service("example/hello", (arg, info) => {
185
- console.log(info.meta?.clientVersion) /* "1.0.0" */
186
- console.log(info.meta?.requestId) /* "123" */
187
- return `hello ${arg}`
188
- })
189
-
190
- - **Event Registration**:<br/>
191
-
192
- /* (simplified TypeScript API method signature) */
193
- event(
194
- name: string,
195
- callback: (
196
- ...params: any[],
197
- info: {
198
- sender: string,
199
- receiver?: string,
200
- authenticated?: boolean,
201
- meta?: Record<string, any>
202
- }
203
- ) => void | Promise<void>
204
- ): Promise<Registration>
205
- event({
206
- name: string,
207
- callback: (
208
- ...params: any[],
209
- info: {
210
- sender: string,
211
- receiver?: string,
212
- authenticated?: boolean,
213
- meta?: Record<string, any>
214
- }
215
- ) => void | Promise<void>,
216
- options?: MQTT::IClientSubscribeOptions,
217
- share?: string,
218
- auth?: string | { mode: "require" | "optional", roles: string[] }
219
- }): Promise<Registration>
220
-
221
- Register for an event.
222
-
223
- - The `name` has to be a valid MQTT topic name.
224
-
225
- - The `callback` is called with the `params` passed to a remote `emit()`.
226
- There is no return value of `callback`.
227
-
228
- - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
229
-
230
- - The optional `share` enables
231
- [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
232
- (MQTT 5.0) for load-balancing messages across multiple registrations
233
- by specifying a group name. This internally prefixes the event with
234
- `$share/<share>/`.
235
-
236
- - The optional `auth` enables authentication validation on incoming events.
237
- When set to a role name string (e.g., `"admin"`), authentication is required
238
- and the token must include that role. When set to an object `{ mode, roles }`,
239
- the mode can be `"require"` (reject unauthenticated) or `"optional"` (accept all
240
- but reflect validation result in `info.authenticated`), and roles specifies
241
- the required role names.
242
-
243
- - Internally, on the MQTT broker, the topics generated by
244
- `topicMake(name, "event-emission")` (default: `${name}/event-emission/any` and
245
- `${name}/event-emission/${peerId}`) are subscribed.
246
-
247
- - Returns a `Registration` object with a `destroy()` method.
248
-
249
- - **Service Registration**:<br/>
250
-
251
- /* (simplified TypeScript API method signature) */
252
- service(
253
- name: string,
254
- callback: (
255
- ...params: any[],
256
- info: {
257
- sender: string,
258
- receiver?: string,
259
- authenticated?: boolean,
260
- meta?: Record<string, any>
261
- }
262
- ) => any | Promise<any>
263
- ): Promise<Registration>
264
- service({
265
- name: string,
266
- callback: (
267
- ...params: any[],
268
- info: {
269
- sender: string,
270
- receiver?: string,
271
- authenticated?: boolean,
272
- meta?: Record<string, any>
273
- }
274
- ) => any | Promise<any>,
275
- options?: MQTT::IClientSubscribeOptions,
276
- share?: string,
277
- auth?: string | { mode: "require" | "optional", roles: string[] }
278
- }): Promise<Registration>
279
-
280
- Register a service.
281
-
282
- - The `name` has to be a valid MQTT topic name.
283
-
284
- - The `callback` is called with the `params` passed to a remote `call()`.
285
- The return value of `callback` will resolve the `Promise` returned by the remote `call()`.
286
-
287
- - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
288
-
289
- - The optional `share` enables
290
- [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
291
- (MQTT 5.0) for load-balancing service calls across multiple services
292
- by specifying a group name. This internally prefixes the service
293
- with `$share/<share>/`. By default a share named `default` is used.
294
-
295
- - The optional `auth` enables authentication validation on incoming service calls.
296
- When set to a role name string (e.g., `"admin"`), authentication is required
297
- and the token must include that role. When set to an object `{ mode, roles }`,
298
- the mode can be `"require"` (reject unauthenticated with error response) or
299
- `"optional"` (accept all but reflect validation result in `info.authenticated`),
300
- and roles specifies the required role names.
301
-
302
- - Internally, on the MQTT broker, the topics generated by
303
- `topicMake(name, "service-call-request")` (default: `${name}/service-call-request/any` and
304
- `${name}/service-call-request/${peerId}`) are subscribed
305
-
306
- - Returns a `Registration` object with a `destroy()` method.
307
-
308
- - **Source Registration**:<br/>
309
-
310
- /* (simplified TypeScript API method signature) */
311
- source(
312
- name: string,
313
- callback: (
314
- ...params: any[],
315
- info: {
316
- sender: string,
317
- receiver?: string,
318
- authenticated?: boolean,
319
- meta?: Record<string, any>,
320
- stream?: Readable,
321
- buffer?: Promise<Uint8Array>
322
- }
323
- ) => void | Promise<void>
324
- ): Promise<Registration>
325
- source({
326
- name: string,
327
- callback: (
328
- ...params: any[],
329
- info: {
330
- sender: string,
331
- receiver?: string,
332
- authenticated?: boolean,
333
- meta?: Record<string, any>,
334
- stream?: Readable,
335
- buffer?: Promise<Uint8Array>
336
- }
337
- ) => void | Promise<void>,
338
- options?: MQTT::IClientSubscribeOptions,
339
- share?: string,
340
- auth?: string | { mode: "require" | "optional", roles: string[] }
341
- }): Promise<Registration>
342
-
343
- Register a source for sending data.
344
-
345
- - The `name` has to be a valid MQTT topic name.
346
-
347
- - The `callback` is called with the `params` passed to a remote `fetch()`.
348
- The `callback` should set `info.stream` to a `Readable` or
349
- `info.buffer` to a `Promise<Uint8Array>` containing the data.
350
- Optionally, the `callback` can set `info.meta` to a `Record<string,
351
- any>` to send metadata back with the response.
352
-
353
- - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
354
-
355
- - The optional `share` enables
356
- [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
357
- (MQTT 5.0) for load-balancing source requests across multiple
358
- sources by specifying a group name. This internally prefixes the
359
- source with `$share/<share>/`. By default a share named `default` is
360
- used.
361
-
362
- - The optional `auth` enables authentication validation on incoming source fetches.
363
- When set to a role name string (e.g., `"admin"`), authentication
364
- is required and the token must include that role. When set to an
365
- object `{ mode, roles }`, the mode can be `"require"` (reject
366
- unauthenticated) or `"optional"` (accept all but reflect validation
367
- result in `info.authenticated`), and roles specifies the required
368
- role names.
369
-
370
- - Internally, on the MQTT broker, the topics by
371
- `topicMake(name, "source-fetch-request")`
372
- (default: `${name}/source-fetch-request/any` and
373
- `${name}/source-fetch-request/${peerId}`) and
374
- `topicMake(name, "source-fetch-credit", peerId)`
375
- (default: `${name}/source-fetch-credit/${peerId}`) are subscribed.
376
-
377
- - Returns a `Registration` object with a `destroy()` method.
378
-
379
- - **Sink Registration**:<br/>
380
-
381
- /* (simplified TypeScript API method signature) */
382
- sink(
383
- name: string,
384
- callback: (
385
- ...params: any[],
386
- info: {
387
- sender: string,
388
- receiver?: string,
389
- authenticated?: boolean,
390
- meta?: Record<string, any>,
391
- stream?: Readable,
392
- buffer?: Promise<Uint8Array>
393
- }
394
- ) => void | Promise<void>
395
- ): Promise<Registration>
396
- sink({
397
- name: string,
398
- callback: (
399
- ...params: any[],
400
- info: {
401
- sender: string,
402
- receiver?: string,
403
- authenticated?: boolean,
404
- meta?: Record<string, any>,
405
- stream?: Readable,
406
- buffer?: Promise<Uint8Array>
407
- }
408
- ) => void | Promise<void>,
409
- options?: MQTT::IClientSubscribeOptions,
410
- share?: string,
411
- auth?: string | { mode: "require" | "optional", roles: string[] }
412
- }): Promise<Registration>
413
-
414
- Register a sink for receiving data.
415
-
416
- - The `name` has to be a valid MQTT topic name.
417
-
418
- - The `callback` is called with the `params` passed to a remote `push()`.
419
- The `info.stream` provides a Node.js `Readable` stream for consuming the pushed data.
420
- The `info.buffer` provides a lazy `Promise<Uint8Array>` that resolves to the complete data once the stream ends.
421
- The `info.meta` contains optional metadata sent by the pusher via `push()`.
422
-
423
- - The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
424
-
425
- - The optional `share` enables
426
- [MQTT Shared Subscriptions](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250)
427
- (MQTT 5.0) for load-balancing sink pushes across multiple sink
428
- handlers by specifying a group name. This internally prefixes the
429
- sink with `$share/<share>/`. By default a share named `default` is
430
- used.
431
-
432
- - The optional `auth` enables authentication validation on incoming sink pushes.
433
- When set to a role name string (e.g., `"admin"`), authentication
434
- is required and the token must include that role. When set to an
435
- object `{ mode, roles }`, the mode can be `"require"` (reject
436
- unauthenticated) or `"optional"` (accept all but reflect validation
437
- result in `info.authenticated`), and roles specifies the required
438
- role names.
439
-
440
- - Internally, on the MQTT broker, the topics by
441
- `topicMake(name, "sink-push-request")`
442
- (default: `${name}/sink-push-request/any` and
443
- `${name}/sink-push-request/${peerId}`) and
444
- `topicMake(name, "sink-push-chunk", peerId)`
445
- (default: `${name}/sink-push-chunk/${peerId}`) are subscribed.
446
-
447
- - Returns a `Registration` object with a `destroy()` method.
448
-
449
- - **Event Emission**:<br/>
450
-
451
- /* (simplified TypeScript API method signature) */
452
- emit(
453
- event: string,
454
- ...params: any[]
455
- ): void
456
- emit({
457
- event: string,
458
- params: any[],
459
- receiver?: string,
460
- options?: MQTT::IClientPublishOptions,
461
- meta?: Record<string, any>
462
- }): void
463
- emit({
464
- event: string,
465
- params: any[],
466
- receiver?: string,
467
- options?: MQTT::IClientPublishOptions,
468
- meta?: Record<string, any>,
469
- dry: true
470
- }): { topic: string, payload: string | Uint8Array, options: IClientPublishOptions }
471
-
472
- Emit an event to all subscribers or a specific subscriber ("fire and forget").
473
-
474
- - The optional `receiver` directs the event to a specific subscriber only.
475
-
476
- - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
477
-
478
- - The optional `meta` sends additional metadata alongside the event,
479
- which is merged with instance-level metadata set via `meta()`.
480
-
481
- - The optional `dry` flag, when set to `true`, returns the publish information
482
- (`topic`, `payload`, `options`) instead of actually publishing to the MQTT broker.
483
- This is useful for generating MQTT "last will" messages (see example below).
484
-
485
- - The remote `event()` `callback` is called with `params` and its
486
- return value is silently ignored.
487
-
488
- - Internally, publishes to the MQTT topic by `topicMake(event, "event-emission", peerId)`
489
- (default: `${event}/event-emission/any` or `${event}/event-emission/${peerId}`).
490
-
491
- - *Dry-Run Publishing for MQTT Last-Will:*
492
- When you need to set up an MQTT "last will" message (automatically published
493
- by the broker when a client disconnects *unexpectedly*), you can use `dry: true`
494
- together with a `null` MQTT client:
495
-
496
- type API = {
497
- "example/connection": Event<(state: "open" | "close") => void>
498
- [...]
499
- }
500
- const mqttpDry = new MQTTp<API>(null, { id: "my-client" })
501
- const will = mqttpDry.emit({
502
- dry: true,
503
- event: "example/connection",
504
- params: [ "close" ],
505
- [...]
506
- })
507
- mqttpDry.destroy()
508
- const mqtt = MQTT.connect("[...]", {
509
- will: {
510
- topic: will.topic,
511
- payload: will.payload,
512
- qos: will.options.qos
513
- },
514
- [...]
515
- })
516
-
517
- - **Service Call**:<br/>
518
-
519
- /* (simplified TypeScript API method signature) */
520
- call(
521
- name: string,
522
- ...params: any[]
523
- ): Promise<any>
524
- call({
525
- name: string,
526
- params: any[],
527
- receiver?: string,
528
- options?: MQTT::IClientPublishOptions,
529
- meta?: Record<string, any>
530
- }): Promise<any>
531
-
532
- Call a service on all registrants or on a specific registrant ("request and response").
533
-
534
- - The optional `receiver` directs the call to a specific registrant only.
535
-
536
- - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
537
-
538
- - The optional `meta` sends additional metadata alongside the service call,
539
- which is merged with instance-level metadata set via `meta()`.
540
-
541
- - The remote `service()` `callback` is called with `params` and its
542
- return value resolves the returned `Promise`. If the remote `callback`
543
- throws an exception, this rejects the returned `Promise`.
544
-
545
- - Internally, on the MQTT broker, the topic by
546
- `topicMake(service, "service-call-response", peerId)` (default:
547
- `${service}/service-call-response/${peerId}`) is temporarily
548
- subscribed for receiving the response.
549
-
550
- - **Source Fetch**:<br/>
551
-
552
- /* (simplified TypeScript API method signature) */
553
- fetch(
554
- name: string,
555
- ...params: any[]
556
- ): Promise<{
557
- stream: Readable,
558
- buffer: Promise<Uint8Array>,
559
- meta: Promise<Record<string, any> | undefined>
560
- }>
561
- fetch({
562
- name: string,
563
- params: any[],
564
- receiver?: string,
565
- options?: MQTT::IClientPublishOptions,
566
- meta?: Record<string, any>
567
- }): Promise<{
568
- stream: Readable,
569
- buffer: Promise<Uint8Array>,
570
- meta: Promise<Record<string, any> | undefined>
571
- }>
572
-
573
- Fetches data from any source or from a specific source.
574
-
575
- - The optional `receiver` directs the call to a specific source only.
576
-
577
- - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
578
-
579
- - The optional `meta` sends additional metadata alongside the fetch request,
580
- which is merged with instance-level metadata set via `meta()`.
581
-
582
- - Returns an object with a `stream` (`Readable`) for consuming the transferred data,
583
- a lazy `buffer` (`Promise<Uint8Array>`) that resolves
584
- to the complete data once the stream ends, and a `meta`
585
- (`Promise<Record<string, any> | undefined>`) that resolves to
586
- optional metadata sent by the source when the first chunk arrives.
587
-
588
- - The remote `source()` `callback` is called with `params` and
589
- should set `info.stream` to a `Readable` or `info.buffer` to
590
- a `Promise<Uint8Array>` containing the data. Optionally, the
591
- `callback` can set `info.meta` to send metadata back with the
592
- response. If the remote `callback` throws an exception, this
593
- destroys the stream with the error.
594
-
595
- - Internally, on the MQTT broker, the topics by
596
- `topicMake(name, "source-fetch-response", peerId)`
597
- and `topicMake(name, "source-fetch-chunk", peerId)`
598
- (default: `${name}/source-fetch-response/${peerId}` and
599
- `${name}/source-fetch-chunk/${peerId}`) are temporarily subscribed
600
- for receiving the response and data chunks.
601
-
602
- - **Sink Push**:<br/>
603
-
604
- /* (simplified TypeScript API method signature) */
605
- push(
606
- name: string,
607
- data: Readable | Uint8Array,
608
- ...params: any[]
609
- ): Promise<void>
610
- push({
611
- name: string,
612
- data: Readable | Uint8Array,
613
- params: any[]
614
- meta?: Record<string, any>,
615
- receiver?: string,
616
- options?: MQTT::IClientPublishOptions
617
- }): Promise<void>
618
-
619
- Pushes data to all established sinks or a specific sink handler.
620
-
621
- - The `data` is either a Node.js `Readable` stream or a `Uint8Array` providing the data to push.
622
-
623
- - The optional `meta` sends metadata alongside the data,
624
- which becomes available on the sink handler side via `info.meta`.
625
-
626
- - The optional `receiver` directs the push to a specific sink handler only.
627
-
628
- - The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
629
-
630
- - The data is read from `data` in chunks (default: 16KB,
631
- configurable via `chunkSize` option) and sent over MQTT until the
632
- stream is closed or the buffer is fully transferred.
633
- The returned `Promise` resolves when the entire data has been pushed.
634
-
635
- - The remote `sink()` `callback` is called with `params` and an `info` object
636
- containing `stream` (`Readable`) for consuming the pushed data,
637
- `buffer` (lazy `Promise<Uint8Array>`) that resolves to the complete
638
- data once the stream ends, and `meta` (`Record<string, any> |
639
- undefined`) containing the metadata sent by the pusher.
640
-
641
- - Internally, on the MQTT broker, the topic by
642
- `topicMake(name, "sink-push-response", peerId)` (default:
643
- `${name}/sink-push-response/${peerId}`) is temporarily
644
- subscribed for receiving the ack/nak response,
645
- then publishes to the MQTT topic by `topicMake(name, "sink-push-request", peerId)`
646
- (default: `${name}/sink-push-request/any` or `${name}/sink-push-request/${peerId}`)
647
- for the initial request, `topicMake(name, "sink-push-chunk", peerId)`
648
- (default: `${name}/sink-push-chunk/${peerId}`) for the data chunks,
649
- and optionally `topicMake(name, "sink-push-credit", peerId)`
650
- (default: `${name}/sink-push-credit/${peerId}`) for credit-based flow control.
651
-
652
- - **Data Type Conversion Utilities**:<br/>
653
-
654
- /* convert character string to buffer */
655
- str2buf(data: string): Uint8Array
656
-
657
- /* convert buffer to character string */
658
- buf2str(data: Uint8Array): string
659
-
660
- /* convert byte-based typed array to buffer */
661
- arr2buf(data: Buffer | Uint8Array | Int8Array): Uint8Array
662
-
663
- /* convert buffer to byte-based typed array */
664
- buf2arr(data: Uint8Array, type: typeof Buffer): Buffer
665
- buf2arr(data: Uint8Array, type: typeof Uint8Array): Uint8Array
666
- buf2arr(data: Uint8Array, type: typeof Int8Array): Int8Array
667
-
668
- MQTT+ provides utility methods for converting between strings,
669
- buffers, and typed arrays. These are useful when working with binary
670
- data in source/sink transfers or when interfacing with API methods that
671
- expect specific data types.
672
-
673
- Example:
674
-
675
- /* string to buffer conversion */
676
- const buffer = mqttp.str2buf("Hello, World!")
677
- const text = mqttp.buf2str(buffer)
678
-
679
- /* typed array conversions */
680
- const ui8a = mqttp.arr2buf(buffer)
681
- const buffer = mqttp.buf2arr(ui8a, Buffer)
682
- const i8a = mqttp.buf2arr(ui8a, Int8Array)