mqtt-plus 1.4.2 → 1.4.3
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/CHANGELOG.md +6 -0
- package/README.md +9 -3
- package/doc/mqtt-plus-internals.md +362 -42
- package/dst-stage1/mqtt-plus-auth.d.ts +1 -0
- package/package.json +1 -1
- package/src/mqtt-plus-auth.ts +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -117,10 +117,15 @@ mqtt.on("connect", async () => {
|
|
|
117
117
|
Documentation
|
|
118
118
|
-------------
|
|
119
119
|
|
|
120
|
+
Main documentation:
|
|
121
|
+
|
|
120
122
|
- [**Communication Patterns**](doc/mqtt-plus-comm.md)
|
|
121
123
|
- [**Application Programming Interface (API)**](doc/mqtt-plus-api.md)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
|
|
125
|
+
Additional auxilliary documentation:
|
|
126
|
+
|
|
127
|
+
- [Extra: Architecture Overview](doc/mqtt-plus-architecture.md)
|
|
128
|
+
- [Extra: Internal Protocol](doc/mqtt-plus-internals.md)
|
|
124
129
|
- [Extra: Broker Setup](doc/mqtt-plus-broker-setup.md)
|
|
125
130
|
|
|
126
131
|
Notice
|
|
@@ -139,7 +144,7 @@ Notice
|
|
|
139
144
|
License
|
|
140
145
|
-------
|
|
141
146
|
|
|
142
|
-
Copyright
|
|
147
|
+
Copyright © 2018-2026 Dr. Ralf S. Engelschall (http://engelschall.com/)
|
|
143
148
|
|
|
144
149
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
145
150
|
a copy of this software and associated documentation files (the
|
|
@@ -159,3 +164,4 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
159
164
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
160
165
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
161
166
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
167
|
+
|
|
@@ -1,59 +1,380 @@
|
|
|
1
1
|
|
|
2
2
|
MQTT+ Internals
|
|
3
|
-
|
|
3
|
+
===============
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Overview
|
|
6
|
+
--------
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
**MQTT+** implements a message-oriented protocol on top of standard MQTT.
|
|
9
|
+
Each MQTT+ instance is identified by a unique **peer ID** (a NanoID).
|
|
10
|
+
All messages are encoded as structured objects and transported as MQTT
|
|
11
|
+
payloads on well-defined MQTT topics. The protocol supports four
|
|
12
|
+
communication patterns: **Event Emission**, **Service Call**, **Sink
|
|
13
|
+
Push**, and **Source Fetch**.
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Message Encoding
|
|
16
|
+
----------------
|
|
17
|
+
|
|
18
|
+
Messages are encoded using one of two codecs, selected at instance creation:
|
|
19
|
+
|
|
20
|
+
- **CBOR** (default): Binary encoding via the `cbor2` library.
|
|
21
|
+
`Uint8Array` and `Buffer` values are encoded natively.
|
|
22
|
+
MQTT payloads are `Uint8Array`.
|
|
23
|
+
|
|
24
|
+
- **JSON**: Text encoding via a custom `JSONX` serializer.
|
|
25
|
+
`Uint8Array` values are encoded as `{ "__Uint8Array": "<base64>" }`.
|
|
26
|
+
MQTT payloads are UTF-8 strings.
|
|
27
|
+
|
|
28
|
+
Message Base Structure
|
|
29
|
+
----------------------
|
|
30
|
+
|
|
31
|
+
Every MQTT+ message shares a common base structure:
|
|
32
|
+
|
|
33
|
+
| Field | Type | Description |
|
|
34
|
+
|------------|-------------------|------------------------------------------------|
|
|
35
|
+
| `version` | `string` | Protocol version identifier, format `MQTT+/X.X` (e.g. `MQTT+/1.4`). Must match between peers. |
|
|
36
|
+
| `type` | `string` | One of the 11 message types (see below). |
|
|
37
|
+
| `id` | `string` | NanoID correlating requests with their responses. |
|
|
38
|
+
| `sender` | `string?` | NanoID of the sending peer. |
|
|
39
|
+
| `receiver` | `string?` | NanoID of the intended receiving peer. |
|
|
40
|
+
|
|
41
|
+
The `version` field is checked on every incoming message. A mismatch
|
|
42
|
+
causes the message to be rejected.
|
|
43
|
+
|
|
44
|
+
Message Types
|
|
45
|
+
-------------
|
|
46
|
+
|
|
47
|
+
The protocol defines 11 message types, grouped by communication pattern:
|
|
48
|
+
|
|
49
|
+
### Event Emission (1 message type)
|
|
50
|
+
|
|
51
|
+
| Type | Direction | Purpose |
|
|
52
|
+
|--------------------|-----------|-------------------------------|
|
|
53
|
+
| `event-emission` | one-way | Fire-and-forget event notification |
|
|
54
|
+
|
|
55
|
+
### Service Call (2 message types)
|
|
56
|
+
|
|
57
|
+
| Type | Direction | Purpose |
|
|
58
|
+
|--------------------------|-------------|--------------------------|
|
|
59
|
+
| `service-call-request` | caller -> callee | RPC request with parameters |
|
|
60
|
+
| `service-call-response` | callee -> caller | RPC result or error |
|
|
61
|
+
|
|
62
|
+
### Sink Push (4 message types)
|
|
63
|
+
|
|
64
|
+
| Type | Direction | Purpose |
|
|
65
|
+
|----------------------|------------------|-------------------------------|
|
|
66
|
+
| `sink-push-request` | pusher -> sink | Initiate data push |
|
|
67
|
+
| `sink-push-response` | sink -> pusher | Acknowledge (ack) or reject (nak) |
|
|
68
|
+
| `sink-push-chunk` | pusher -> sink | Transfer a data chunk |
|
|
69
|
+
| `sink-push-credit` | sink -> pusher | Replenish flow control credit |
|
|
70
|
+
|
|
71
|
+
### Source Fetch (4 message types)
|
|
72
|
+
|
|
73
|
+
| Type | Direction | Purpose |
|
|
74
|
+
|-------------------------|--------------------|-------------------------------|
|
|
75
|
+
| `source-fetch-request` | fetcher -> source | Initiate data fetch |
|
|
76
|
+
| `source-fetch-response` | source -> fetcher | Acknowledge (ack) or reject (nak) |
|
|
77
|
+
| `source-fetch-chunk` | source -> fetcher | Transfer a data chunk |
|
|
78
|
+
| `source-fetch-credit` | fetcher -> source | Replenish flow control credit |
|
|
79
|
+
|
|
80
|
+
Message Fields by Type
|
|
81
|
+
----------------------
|
|
82
|
+
|
|
83
|
+
### `event-emission`
|
|
84
|
+
|
|
85
|
+
| Field | Type | Required | Description |
|
|
86
|
+
|----------|------------------------|----------|-------------------------------|
|
|
87
|
+
| `name` | `string` | yes | Endpoint name |
|
|
88
|
+
| `params` | `any[]` | no | Event parameters (max 64) |
|
|
89
|
+
| `auth` | `string[]` | no | JWT tokens (max 8, each max 8192 chars) |
|
|
90
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata (non-array object) |
|
|
91
|
+
|
|
92
|
+
### `service-call-request`
|
|
93
|
+
|
|
94
|
+
| Field | Type | Required | Description |
|
|
95
|
+
|----------|------------------------|----------|-------------------------------|
|
|
96
|
+
| `name` | `string` | yes | Service endpoint name |
|
|
97
|
+
| `params` | `any[]` | no | Call parameters (max 64) |
|
|
98
|
+
| `auth` | `string[]` | no | JWT tokens (max 8) |
|
|
99
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata |
|
|
18
100
|
|
|
19
|
-
|
|
20
|
-
remote service named `example/hello` with parameters `"world"` and `42` via...
|
|
101
|
+
### `service-call-response`
|
|
21
102
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
103
|
+
| Field | Type | Required | Description |
|
|
104
|
+
|----------|-----------|----------|--------------------------------------|
|
|
105
|
+
| `result` | `any` | no | Return value on success |
|
|
106
|
+
| `error` | `string` | no | Error message on failure |
|
|
107
|
+
|
|
108
|
+
Exactly one of `result` or `error` is present.
|
|
109
|
+
|
|
110
|
+
### `sink-push-request`
|
|
111
|
+
|
|
112
|
+
| Field | Type | Required | Description |
|
|
113
|
+
|----------|------------------------|----------|-------------------------------|
|
|
114
|
+
| `name` | `string` | yes | Sink endpoint name |
|
|
115
|
+
| `params` | `any[]` | no | Push parameters (max 64) |
|
|
116
|
+
| `auth` | `string[]` | no | JWT tokens (max 8) |
|
|
117
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata |
|
|
118
|
+
|
|
119
|
+
### `sink-push-response`
|
|
120
|
+
|
|
121
|
+
| Field | Type | Required | Description |
|
|
122
|
+
|----------|------------------------|----------|-------------------------------|
|
|
123
|
+
| `name` | `string` | yes | Sink endpoint name |
|
|
124
|
+
| `error` | `string` | no | Error message (nak) or absent (ack) |
|
|
125
|
+
| `auth` | `string[]` | no | JWT tokens (max 8) |
|
|
126
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata |
|
|
127
|
+
| `credit` | `integer` | no | Initial flow control credit (min 1) |
|
|
128
|
+
|
|
129
|
+
### `sink-push-chunk`
|
|
130
|
+
|
|
131
|
+
| Field | Type | Required | Description |
|
|
132
|
+
|---------|--------------|----------|------------------------------------|
|
|
133
|
+
| `name` | `string` | yes | Sink endpoint name |
|
|
134
|
+
| `chunk` | `Uint8Array` | no | Data chunk payload |
|
|
135
|
+
| `error` | `string` | no | Error message (aborts the stream) |
|
|
136
|
+
| `final` | `boolean` | no | `true` on the last chunk |
|
|
137
|
+
|
|
138
|
+
### `sink-push-credit`
|
|
139
|
+
|
|
140
|
+
| Field | Type | Required | Description |
|
|
141
|
+
|----------|-----------|----------|-------------------------------------|
|
|
142
|
+
| `name` | `string` | yes | Sink endpoint name |
|
|
143
|
+
| `credit` | `integer` | yes | Number of additional credits (min 1)|
|
|
144
|
+
|
|
145
|
+
### `source-fetch-request`
|
|
146
|
+
|
|
147
|
+
| Field | Type | Required | Description |
|
|
148
|
+
|----------|------------------------|----------|-------------------------------|
|
|
149
|
+
| `name` | `string` | yes | Source endpoint name |
|
|
150
|
+
| `params` | `any[]` | no | Fetch parameters (max 64) |
|
|
151
|
+
| `auth` | `string[]` | no | JWT tokens (max 8) |
|
|
152
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata |
|
|
153
|
+
| `credit` | `integer` | no | Initial flow control credit (min 1) |
|
|
154
|
+
|
|
155
|
+
### `source-fetch-response`
|
|
156
|
+
|
|
157
|
+
| Field | Type | Required | Description |
|
|
158
|
+
|----------|------------------------|----------|-------------------------------|
|
|
159
|
+
| `name` | `string` | yes | Source endpoint name |
|
|
160
|
+
| `error` | `string` | no | Error message (nak) or absent (ack) |
|
|
161
|
+
| `auth` | `string[]` | no | JWT tokens (max 8) |
|
|
162
|
+
| `meta` | `Record<string, any>` | no | Arbitrary metadata |
|
|
163
|
+
|
|
164
|
+
### `source-fetch-chunk`
|
|
165
|
+
|
|
166
|
+
| Field | Type | Required | Description |
|
|
167
|
+
|---------|--------------|----------|------------------------------------|
|
|
168
|
+
| `name` | `string` | yes | Source endpoint name |
|
|
169
|
+
| `chunk` | `Uint8Array` | no | Data chunk payload |
|
|
170
|
+
| `error` | `string` | no | Error message (aborts the stream) |
|
|
171
|
+
| `final` | `boolean` | no | `true` on the last chunk |
|
|
172
|
+
|
|
173
|
+
### `source-fetch-credit`
|
|
174
|
+
|
|
175
|
+
| Field | Type | Required | Description |
|
|
176
|
+
|----------|-----------|----------|-------------------------------------|
|
|
177
|
+
| `name` | `string` | yes | Source endpoint name |
|
|
178
|
+
| `credit` | `integer` | yes | Number of additional credits (min 1)|
|
|
179
|
+
|
|
180
|
+
MQTT Topic Structure
|
|
181
|
+
--------------------
|
|
182
|
+
|
|
183
|
+
MQTT+ maps messages to MQTT topics using the pattern:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
{name}/{operation}/{peerId}
|
|
26
187
|
```
|
|
27
188
|
|
|
28
|
-
|
|
29
|
-
`
|
|
30
|
-
|
|
189
|
+
- **`name`**: The endpoint name (e.g. `example/hello`).
|
|
190
|
+
- **`operation`**: The message type (e.g. `service-call-request`).
|
|
191
|
+
- **`peerId`**: Either the target peer's NanoID (for directed messages) or
|
|
192
|
+
`any` (for broadcast messages).
|
|
193
|
+
|
|
194
|
+
### Broadcast Topics (Requests)
|
|
195
|
+
|
|
196
|
+
Request messages are published to broadcast topics when no specific
|
|
197
|
+
receiver is targeted:
|
|
198
|
+
|
|
199
|
+
| Pattern | Operation | Purpose |
|
|
200
|
+
|----------------------------|-------------------------|------------------------|
|
|
201
|
+
| `{name}/event-emission/any` | `event-emission` | Broadcast event |
|
|
202
|
+
| `{name}/event-emission/{peerId}` | `event-emission` | Directed event |
|
|
203
|
+
| `$share/{share}/{name}/service-call-request/any` | `service-call-request` | Shared service request |
|
|
204
|
+
| `$share/{share}/{name}/source-fetch-request/any` | `source-fetch-request` | Shared fetch request |
|
|
205
|
+
| `$share/{share}/{name}/sink-push-request/any` | `sink-push-request` | Shared push request |
|
|
206
|
+
|
|
207
|
+
Service, source, and sink requests use **MQTT shared subscriptions**
|
|
208
|
+
(`$share/{group}/...`) to distribute load across multiple handlers
|
|
209
|
+
(default group: `"default"`). Event emissions do *not* use shared
|
|
210
|
+
subscriptions by default (all registered handlers receive the event).
|
|
211
|
+
|
|
212
|
+
### Direct Topics (Responses and Chunks)
|
|
213
|
+
|
|
214
|
+
Response messages, chunks, and credits are sent to peer-specific topics:
|
|
215
|
+
|
|
216
|
+
| Pattern | Operation |
|
|
217
|
+
|--------------------------------------------------|---------------------------|
|
|
218
|
+
| `{name}/service-call-response/{clientId}` | `service-call-response` |
|
|
219
|
+
| `{name}/sink-push-response/{clientId}` | `sink-push-response` |
|
|
220
|
+
| `{name}/sink-push-chunk/{sinkId}` | `sink-push-chunk` |
|
|
221
|
+
| `{name}/sink-push-credit/{pusherId}` | `sink-push-credit` |
|
|
222
|
+
| `{name}/source-fetch-response/{clientId}` | `source-fetch-response` |
|
|
223
|
+
| `{name}/source-fetch-chunk/{clientId}` | `source-fetch-chunk` |
|
|
224
|
+
| `{name}/source-fetch-credit/{sourceId}` | `source-fetch-credit` |
|
|
225
|
+
|
|
226
|
+
The `{clientId}` is the `sender` field from the corresponding request
|
|
227
|
+
message, ensuring responses are routed back to the originating peer only.
|
|
228
|
+
|
|
229
|
+
### Topic Customization
|
|
230
|
+
|
|
231
|
+
The topic structure is fully customizable through the `topicMake` and
|
|
232
|
+
`topicMatch` options at instance creation time.
|
|
233
|
+
|
|
234
|
+
MQTT QoS Levels
|
|
235
|
+
---------------
|
|
236
|
+
|
|
237
|
+
| Communication Pattern | QoS | Rationale |
|
|
238
|
+
|-----------------------|-----|-----------------------------------|
|
|
239
|
+
| Event Emission | 0 | Best-effort, fire-and-forget |
|
|
240
|
+
| Service Call | 2 | Exactly-once for reliable RPC |
|
|
241
|
+
| Sink Push | 2 | Exactly-once for reliable data transfer |
|
|
242
|
+
| Source Fetch | 2 | Exactly-once for reliable data transfer |
|
|
243
|
+
|
|
244
|
+
Credit-Based Flow Control
|
|
245
|
+
-------------------------
|
|
246
|
+
|
|
247
|
+
Sink Push and Source Fetch patterns use a **credit-based flow control**
|
|
248
|
+
mechanism to prevent the data producer from overwhelming the consumer.
|
|
249
|
+
|
|
250
|
+
### How It Works
|
|
251
|
+
|
|
252
|
+
1. The **consumer** grants an initial number of credits (default: 4)
|
|
253
|
+
to the **producer** (via the response message or request message).
|
|
254
|
+
2. Each chunk sent by the producer **consumes one credit**.
|
|
255
|
+
3. When credits are exhausted, the producer **blocks** (waits).
|
|
256
|
+
4. As the consumer processes chunks, it sends **credit messages** to
|
|
257
|
+
replenish the producer's credit, unblocking it.
|
|
258
|
+
5. The chunk size is configurable (default: 16 KB).
|
|
259
|
+
|
|
260
|
+
### Configuration
|
|
261
|
+
|
|
262
|
+
| Option | Default | Description |
|
|
263
|
+
|---------------|-----------|------------------------------------------|
|
|
264
|
+
| `chunkSize` | `16384` | Maximum bytes per chunk (16 KB) |
|
|
265
|
+
| `chunkCredit` | `4` | Number of chunks allowed in-flight |
|
|
266
|
+
|
|
267
|
+
Setting `chunkCredit` to `0` disables flow control entirely.
|
|
268
|
+
|
|
269
|
+
### Direction of Credit
|
|
270
|
+
|
|
271
|
+
| Pattern | Credit Sender | Credit Receiver | Credit Message Type |
|
|
272
|
+
|--------------|---------------|-----------------|-------------------------|
|
|
273
|
+
| Sink Push | Sink | Pusher | `sink-push-credit` |
|
|
274
|
+
| Source Fetch | Fetcher | Source | `source-fetch-credit` |
|
|
275
|
+
|
|
276
|
+
Authentication
|
|
277
|
+
--------------
|
|
278
|
+
|
|
279
|
+
MQTT+ provides optional JWT-based authentication and role-based
|
|
280
|
+
authorization on any endpoint.
|
|
281
|
+
|
|
282
|
+
### Setup
|
|
283
|
+
|
|
284
|
+
1. The **server** sets a shared secret via `credential(secret)`.
|
|
285
|
+
The secret is derived into a 256-bit key using PBKDF2-SHA256
|
|
286
|
+
(600,000 iterations).
|
|
287
|
+
|
|
288
|
+
2. The server **issues JWT tokens** via `issue({ roles, id?, exp? })`,
|
|
289
|
+
signed with HS256.
|
|
290
|
+
|
|
291
|
+
3. The **client** stores tokens via `authenticate(token)`.
|
|
292
|
+
|
|
293
|
+
### Token Transmission
|
|
294
|
+
|
|
295
|
+
When a client sends a request (`event-emission`, `service-call-request`,
|
|
296
|
+
`sink-push-request`, or `source-fetch-request`), all stored JWT tokens
|
|
297
|
+
are included in the `auth` field (max 8 tokens, each max 8192 characters).
|
|
298
|
+
|
|
299
|
+
### Validation
|
|
300
|
+
|
|
301
|
+
On the server side, the handler validates the tokens against
|
|
302
|
+
the configured credential and required roles:
|
|
303
|
+
|
|
304
|
+
- The token must be a valid HS256-signed JWT.
|
|
305
|
+
- If the token payload contains an `id` field, it must match the `sender`
|
|
306
|
+
peer ID of the request.
|
|
307
|
+
- If the token payload contains an `exp` field, the token must not be
|
|
308
|
+
expired.
|
|
309
|
+
- The token's `roles` array must contain at least one of the
|
|
310
|
+
required roles.
|
|
311
|
+
|
|
312
|
+
### Authentication Modes
|
|
313
|
+
|
|
314
|
+
| Mode | Behavior |
|
|
315
|
+
|------------|----------------------------------------------------|
|
|
316
|
+
| `require` | Request is rejected if no valid token is found. |
|
|
317
|
+
| `optional` | Request passes even if no valid token is found. |
|
|
318
|
+
|
|
319
|
+
Message Dispatching
|
|
320
|
+
-------------------
|
|
321
|
+
|
|
322
|
+
### Request Dispatching
|
|
323
|
+
|
|
324
|
+
Incoming request messages are dispatched based on the combination of
|
|
325
|
+
their `type` and `name` fields. The dispatch key is
|
|
326
|
+
`{operation}:{name}` (e.g. `service-call-request:example/hello`).
|
|
327
|
+
|
|
328
|
+
### Response Dispatching
|
|
329
|
+
|
|
330
|
+
Incoming response messages are dispatched based on the combination
|
|
331
|
+
of their `type` and `id` fields. The dispatch key is
|
|
332
|
+
`{operation}:{requestId}` (e.g. `service-call-response:vwLzfQDu2uEeOdOfIlT42`).
|
|
333
|
+
This ensures responses are correlated to their originating requests.
|
|
334
|
+
|
|
335
|
+
Timeouts
|
|
336
|
+
--------
|
|
337
|
+
|
|
338
|
+
All bi-directional patterns (Service Call, Sink Push, Source Fetch) are
|
|
339
|
+
guarded by a configurable timeout (default: 10 seconds). If no response
|
|
340
|
+
or progress is received within the timeout, the operation is aborted
|
|
341
|
+
with a timeout error. For streaming patterns, each chunk or credit
|
|
342
|
+
message resets the timeout.
|
|
343
|
+
|
|
344
|
+
Error Handling
|
|
345
|
+
--------------
|
|
346
|
+
|
|
347
|
+
Errors are communicated in two ways, depending on timing:
|
|
348
|
+
|
|
349
|
+
1. **Before data transfer starts**: The response message carries an
|
|
350
|
+
`error` field (nak response).
|
|
351
|
+
|
|
352
|
+
2. **During data transfer**: A chunk message carries an `error` field
|
|
353
|
+
and `final: true`, terminating the stream.
|
|
354
|
+
|
|
355
|
+
Example Message Exchange
|
|
356
|
+
------------------------
|
|
357
|
+
|
|
358
|
+
A service call to `example/hello` with parameters `"world"` and `42`:
|
|
359
|
+
|
|
360
|
+
**Request** (published to `example/hello/service-call-request/any`):
|
|
31
361
|
|
|
32
362
|
```json
|
|
33
363
|
{
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
364
|
+
"version": "MQTT+/1.4",
|
|
365
|
+
"type": "service-call-request",
|
|
366
|
+
"id": "vwLzfQDu2uEeOdOfIlT42",
|
|
367
|
+
"name": "example/hello",
|
|
368
|
+
"params": [ "world", 42 ],
|
|
369
|
+
"sender": "2IBMSk0NPnrz1AeTERoea"
|
|
39
370
|
}
|
|
40
371
|
```
|
|
41
372
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
mqttp.service("example/hello", (a1, a2) => {
|
|
46
|
-
return `${a1}:${a2}`
|
|
47
|
-
})
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
...and then its result, in the above `mqttp.call()` example `"world:42"`, is then
|
|
51
|
-
sent back as the following success response
|
|
52
|
-
message to the temporary (client-specific) MQTT topic
|
|
53
|
-
`example/hello/service-call-response/2IBMSk0NPnrz1AeTERoea`:
|
|
373
|
+
**Response** (published to `example/hello/service-call-response/2IBMSk0NPnrz1AeTERoea`):
|
|
54
374
|
|
|
55
375
|
```json
|
|
56
376
|
{
|
|
377
|
+
"version": "MQTT+/1.4",
|
|
57
378
|
"type": "service-call-response",
|
|
58
379
|
"id": "vwLzfQDu2uEeOdOfIlT42",
|
|
59
380
|
"result": "world:42",
|
|
@@ -62,7 +383,6 @@ message to the temporary (client-specific) MQTT topic
|
|
|
62
383
|
}
|
|
63
384
|
```
|
|
64
385
|
|
|
65
|
-
The `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`id` is used for correlating the response to the request only.
|
|
386
|
+
The `id` field correlates the response to the request. The `sender`
|
|
387
|
+
field in the request is used as the peer-specific suffix in the response
|
|
388
|
+
topic, ensuring only the caller receives the response.
|
package/package.json
CHANGED
package/src/mqtt-plus-auth.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { MetaTrait } from "./mqtt-plus-meta"
|
|
|
36
36
|
export type AuthMode = "require" | "optional"
|
|
37
37
|
export type AuthRole = string
|
|
38
38
|
export type AuthOption = AuthRole | { mode: AuthMode, roles: AuthRole[] }
|
|
39
|
-
type TokenPayload = { roles: AuthRole[], id?: string }
|
|
39
|
+
type TokenPayload = { roles: AuthRole[], id?: string, exp?: number }
|
|
40
40
|
|
|
41
41
|
/* authentication trait */
|
|
42
42
|
export class AuthTrait<T extends APISchema = APISchema> extends MetaTrait<T> {
|