mqtt-plus 0.9.5 → 0.9.7

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 (41) hide show
  1. package/README.md +93 -100
  2. package/doc/mqtt-plus-1-event-emission.d2 +2 -2
  3. package/doc/mqtt-plus-3-resource-transfer.d2 +32 -0
  4. package/doc/mqtt-plus-3-resource-transfer.svg +109 -0
  5. package/dst-stage1/mqtt-plus-api.d.ts +1 -6
  6. package/dst-stage1/mqtt-plus-base.js +1 -1
  7. package/dst-stage1/mqtt-plus-info.d.ts +4 -5
  8. package/dst-stage1/mqtt-plus-msg.d.ts +8 -12
  9. package/dst-stage1/mqtt-plus-msg.js +20 -35
  10. package/dst-stage1/mqtt-plus-resource.d.ts +29 -5
  11. package/dst-stage1/mqtt-plus-resource.js +243 -71
  12. package/dst-stage1/mqtt-plus-service.d.ts +2 -2
  13. package/dst-stage1/mqtt-plus-service.js +2 -2
  14. package/dst-stage1/mqtt-plus-stream.d.ts +4 -0
  15. package/dst-stage1/mqtt-plus-stream.js +54 -15
  16. package/dst-stage1/mqtt-plus-util.d.ts +2 -0
  17. package/dst-stage1/mqtt-plus-util.js +34 -0
  18. package/dst-stage1/mqtt-plus.d.ts +1 -0
  19. package/dst-stage1/mqtt-plus.js +1 -0
  20. package/dst-stage2/mqtt-plus.cjs.js +265 -198
  21. package/dst-stage2/mqtt-plus.esm.js +266 -199
  22. package/dst-stage2/mqtt-plus.umd.js +13 -13
  23. package/etc/eslint.mts +1 -1
  24. package/etc/tsc.tsbuildinfo +1 -1
  25. package/package.json +3 -2
  26. package/src/mqtt-plus-api.ts +1 -9
  27. package/src/mqtt-plus-base.ts +1 -3
  28. package/src/mqtt-plus-codec.ts +1 -1
  29. package/src/mqtt-plus-info.ts +8 -6
  30. package/src/mqtt-plus-msg.ts +28 -51
  31. package/src/mqtt-plus-resource.ts +326 -85
  32. package/src/mqtt-plus-service.ts +2 -2
  33. package/src/mqtt-plus.ts +1 -0
  34. package/tst/mqtt-plus.spec.ts +70 -62
  35. package/doc/mqtt-plus-2-stream-transfer.d2 +0 -22
  36. package/doc/mqtt-plus-2-stream-transfer.svg +0 -108
  37. package/doc/mqtt-plus-4-resource-transfer.d2 +0 -25
  38. package/doc/mqtt-plus-4-resource-transfer.svg +0 -111
  39. package/src/mqtt-plus-stream.ts +0 -222
  40. /package/doc/{mqtt-plus-3-service-call.d2 → mqtt-plus-2-service-call.d2} +0 -0
  41. /package/doc/{mqtt-plus-3-service-call.svg → mqtt-plus-2-service-call.svg} +0 -0
package/README.md CHANGED
@@ -42,22 +42,6 @@ communication patterns with type safety:
42
42
 
43
43
  ![Event Emission](doc/mqtt-plus-1-event-emission.svg)
44
44
 
45
- - **Stream Transfer**:
46
-
47
- Stream Transfer is a *uni-directional* communication pattern.
48
- A stream is the combination of a stream name, a `Readable` stream object and optionally zero or more arguments.
49
- You *attach* to a stream.
50
- When a stream is *transferred*, either a single particular attacher (in case of
51
- a directed stream transfer) or all attachers are called and receive the
52
- arguments as extra information. The `Readable` stream is available via
53
- `info.stream` in the attacher callback.
54
-
55
- In contrast to the regular MQTT message publish/subscribe, this
56
- pattern allows to transfer arbitrary amounts of arbitrary data by
57
- chunking the data via a stream.
58
-
59
- ![Stream Transfer](doc/mqtt-plus-2-stream-transfer.svg)
60
-
61
45
  - **Service Call**:
62
46
 
63
47
  Service Call is a *bi-directional* communication pattern.
@@ -73,27 +57,24 @@ communication patterns with type safety:
73
57
  Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call)
74
58
  (RPC) style communication.
75
59
 
76
- ![Service Call](doc/mqtt-plus-3-service-call.svg)
60
+ ![Service Call](doc/mqtt-plus-2-service-call.svg)
77
61
 
78
62
  - **Resource Transfer**:
79
63
 
80
64
  Resource Transfer is a *bi-directional* communication pattern.
81
65
  A Resource is the combination of a resource name and optionally zero or more arguments.
82
66
  You *provision* for a resource transfer.
83
- When a resource is *fetched* or *pushed*, a single particular provisioner (in case
67
+ When a resource is *fetched*, a single particular provisioner (in case
84
68
  of a directed resource transfer) or one arbitrary provisioner is called and
85
- sends or receives the resource and its arguments.
69
+ sends the resource and its arguments.
70
+ When a resource is *pushed*, the provisioner receives the resource data
71
+ as a stream with arguments.
86
72
 
87
- ![Resource Transfer](doc/mqtt-plus-4-resource-transfer.svg)
73
+ In contrast to the regular MQTT message publish/subscribe, this
74
+ pattern allows to transfer arbitrary amounts of arbitrary data by
75
+ chunking the data via a stream.
88
76
 
89
- > [!Note]
90
- > **MQTT+** is similar to and derived from
91
- > [MQTT-JSON-RPC](https://github.com/rse/mqtt-json-rpc) of the same
92
- > author, but instead of just JSON, MQTT+ encodes packets as JSON
93
- > or CBOR (default), uses an own packet format (allowing sender and
94
- > receiver information), uses shorter NanoIDs instead of longer UUIDs
95
- > for identification of sender, receiver and requests, and additionally
96
- > provides stream transfer and resource transfer support.
77
+ ![Resource Transfer](doc/mqtt-plus-3-resource-transfer.svg)
97
78
 
98
79
  Usage
99
80
  -----
@@ -101,7 +82,7 @@ Usage
101
82
  ### API:
102
83
 
103
84
  The API type defines the available endpoints. Use the marker types
104
- `Event<T>`, `Stream<T>`, and `Service<T>` to declare the communication
85
+ `Event<T>`, `Service<T>`, and `Resource<T>` to declare the communication
105
86
  pattern of each endpoint:
106
87
 
107
88
  ```ts
@@ -109,16 +90,15 @@ import type * as MQTTpt from "mqtt-plus"
109
90
 
110
91
  export type API = {
111
92
  "example/sample": MQTTpt.Event<(a1: string, a2: number) => void>
112
- "example/upload": MQTTpt.Stream<(name: string) => void>
113
93
  "example/hello": MQTTpt.Service<(a1: string, a2: number) => string>
114
94
  "example/resource": MQTTpt.Resource<(filename: string) => void>
115
95
  }
116
96
  ```
117
97
 
118
98
  The marker types ensure that `subscribe()` and `emit()` only accept
119
- `Event<T>` endpoints, `attach()` and `transfer()` only accept
120
- `Stream<T>` endpoints, and `register()` and `call()` only accept
121
- `Service<T>` endpoints.
99
+ `Event<T>` endpoints, `register()` and `call()` only accept
100
+ `Service<T>` endpoints, and `provision()`, `fetch()` and `push()` only
101
+ accept `Resource<T>` endpoints.
122
102
 
123
103
  ### Server:
124
104
 
@@ -187,18 +167,27 @@ The **MQTT+** API provides the following methods:
187
167
  The `mqtt` is the [MQTT.js](https://www.npmjs.com/package/mqtt) instance,
188
168
  which has to be established separately.
189
169
 
170
+ Use `destroy()` to clean up when the instance is no longer needed.
171
+
190
172
  The optional `options` object supports the following fields:
191
173
  - `id`: Custom MQTT peer identifier (default: auto-generated NanoID).
192
174
  - `codec`: Encoding format (default: `cbor`).
193
175
  - `timeout`: Communication timeout in milliseconds (default: `10000`).
194
- - `chunkSize`: Chunk size in bytes for stream transfers (default: `16384`).
176
+ - `chunkSize`: Chunk size in bytes for resource transfers (default: `16384`).
195
177
  - `topicMake`: Custom topic generation function.
196
- The `operation` parameter is one of: `event-emission`, `stream-transfer`, `service-call-request`, `service-call-response`, `resource-transfer-request`, `resource-transfer-response`.
178
+ The `operation` parameter is one of: `event-emission`, `service-call-request`, `service-call-response`, `resource-transfer-request`, `resource-transfer-response`.
197
179
  (default: `` (name, operation, peerId) => `${name}/${operation}` + (peerId ? `/${peerId}` : "/any") ``)
198
180
  - `topicMatch`: Custom topic matching function.
199
181
  Returns `{ name, operation, peerId? }` or `null` if no match. The `peerId` is `undefined` for broadcast topics (ending with `/any`).
200
182
  (default: `` (topic) => { const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/); return m ? { name: m[1], operation: m[2], peerId: m[3] === "any" ? undefined : m[3] } : null } ``)
201
183
 
184
+ - **Destruction**:<br/>
185
+
186
+ destroy(): void
187
+
188
+ Clean up the MQTT+ instance by removing all event listeners.
189
+ Call this method when the instance is no longer needed.
190
+
202
191
  - **Event Subscription**:<br/>
203
192
 
204
193
  /* (simplified TypeScript API method signature) */
@@ -222,30 +211,6 @@ The **MQTT+** API provides the following methods:
222
211
  `${event}/event-emission/${peerId}`) are subscribed. Returns a
223
212
  `Subscription` object with an `unsubscribe()` method.
224
213
 
225
- - **Stream Attachment**:<br/>
226
-
227
- /* (simplified TypeScript API method signature) */
228
- attach(
229
- stream: string,
230
- options?: MQTT::IClientSubscribeOptions
231
- callback: (
232
- ...params: any[],
233
- info: { sender: string, receiver?: string, stream: stream.Readable }
234
- ) => void
235
- ): Promise<Attachment>
236
-
237
- Attach to (observe) a stream.
238
- The `stream` has to be a valid MQTT topic name.
239
- The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
240
- The `callback` is called with the `params` passed to a remote `transfer()`.
241
- The `info.stream` provides a Node.js `Readable` stream for consuming the transferred data.
242
- There is no return value of `callback`.
243
-
244
- Internally, on the MQTT broker, the topics generated by
245
- `topicMake(stream, "stream-transfer")` (default: `${stream}/stream-transfer/any` and
246
- `${stream}/stream-transfer/${peerId}`) are subscribed. Returns an
247
- `Attachment` object with an `unattach()` method.
248
-
249
214
  - **Service Registration**:<br/>
250
215
 
251
216
  /* (simplified TypeScript API method signature) */
@@ -277,20 +242,32 @@ The **MQTT+** API provides the following methods:
277
242
  options?: MQTT::IClientSubscribeOptions
278
243
  callback: (
279
244
  ...params: any[],
280
- info: { sender: string, receiver?: string, resource: Buffer | null }
245
+ info: {
246
+ sender: string,
247
+ receiver?: string,
248
+ resource: Buffer | Readable | null,
249
+ stream?: Readable,
250
+ buffer?: Promise<Buffer>
251
+ }
281
252
  ) => void
282
253
  ): Promise<Provisioning>
283
254
 
284
- Provision a resource.
255
+ Provision a resource for both fetch requests and pushed data.
285
256
  The `resource` has to be a valid MQTT topic name.
286
257
  The optional `options` allows setting MQTT.js `subscribe()` options like `qos`.
287
- The `callback` is called with the `params` passed to a remote `fetch()`.
288
- The `callback` should set `info.resource` to a `Buffer` containing the resource data.
258
+
259
+ For **fetch requests**: The `callback` is called with the `params` passed to a remote `fetch()`.
260
+ The `callback` should set `info.resource` to a `Buffer` or `Readable` containing the resource data.
261
+
262
+ For **pushed data**: The `callback` is called with the `params` passed to a remote `push()`.
263
+ The `info.stream` provides a Node.js `Readable` stream for consuming the pushed data.
264
+ The `info.buffer` provides a lazy `Promise<Buffer>` that resolves to the complete data once the stream ends.
289
265
 
290
266
  Internally, on the MQTT broker, the topics by
291
- `topicMake(resource, "resource-transfer-request")` (default: `${resource}/resource-transfer-request/any` and
292
- `${resource}/resource-transfer-request/${peerId}`) are subscribed. Returns a
293
- `Provisioning` object with an `unprovision()` method.
267
+ `topicMake(resource, "resource-transfer-request")` and `topicMake(resource, "resource-transfer-response")`
268
+ (default: `${resource}/resource-transfer-request/any`, `${resource}/resource-transfer-request/${peerId}`,
269
+ `${resource}/resource-transfer-response/any`, and `${resource}/resource-transfer-response/${peerId}`)
270
+ are subscribed. Returns a `Provisioning` object with an `unprovision()` method.
294
271
 
295
272
  - **Event Emission**:<br/>
296
273
 
@@ -312,33 +289,6 @@ The **MQTT+** API provides the following methods:
312
289
  Internally, publishes to the MQTT topic by `topicMake(event, "event-emission", peerId)`
313
290
  (default: `${event}/event-emission/any` or `${event}/event-emission/${peerId}`).
314
291
 
315
- - **Stream Transfer**:<br/>
316
-
317
- /* (simplified TypeScript API method signature) */
318
- transfer(
319
- stream: string,
320
- readable: stream.Readable,
321
- receiver?: Receiver,
322
- options?: MQTT::IClientPublishOptions,
323
- ...params: any[]
324
- ): Promise<void>
325
-
326
- Transfer a stream to all attachers or a specific attacher.
327
- The `readable` is a Node.js `Readable` stream providing the data to transfer.
328
- The optional `receiver` directs the transfer to a specific attacher only.
329
- The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
330
-
331
- The data is read from `readable` in chunks (default: 16KB,
332
- configurable via `chunkSize` option) and sent over MQTT until the
333
- stream is closed. The returned `Promise` resolves when the entire
334
- stream has been transferred.
335
-
336
- The remote `attach()` `callback` is called with `params` and an `info` object
337
- containing a `stream.Readable` for consuming the transferred data.
338
-
339
- Internally, publishes to the MQTT topic by `topicMake(stream, "stream-transfer", peerId)`
340
- (default: `${stream}/stream-transfer/any` or `${stream}/stream-transfer/${peerId}`).
341
-
342
292
  - **Service Call**:<br/>
343
293
 
344
294
  /* (simplified TypeScript API method signature) */
@@ -361,36 +311,67 @@ The **MQTT+** API provides the following methods:
361
311
  (default: `${service}/service-call-response/${peerId}`) is temporarily subscribed
362
312
  for receiving the response.
363
313
 
364
- - **Resource Transfer**:<br/>
314
+ - **Resource Fetch**:<br/>
365
315
 
366
316
  /* (simplified TypeScript API method signature) */
367
317
  fetch(
368
- blob: string,
318
+ resource: string,
369
319
  receiver?: Receiver,
370
320
  options?: MQTT::IClientSubscribeOptions,
371
321
  ...params: any[]
372
- ): Promise<Buffer>
322
+ ): Promise<{ stream: Readable, buffer: Promise<Buffer> }>
373
323
 
374
324
  Fetches a resource from any resource provisioner or from a specific provisioner.
375
325
  The optional `receiver` directs the call to a specific provisioner only.
376
326
  The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
377
327
 
378
- The remote `provision()` `callback` is called with `params` and its
379
- return value resolves the returned `Promise`. If the remote `callback`
380
- throws an exception, this rejects the returned `Promise`.
328
+ Returns an object with a `stream` (`Readable`) for consuming the transferred data
329
+ and a lazy `buffer` (`Promise<Buffer>`) that resolves to the complete data once the stream ends.
330
+
331
+ The remote `provision()` `callback` is called with `params` and
332
+ should set `info.resource` to a `Buffer` or `Readable` containing the resource data.
333
+ If the remote `callback` throws an exception, this destroys the stream with the error.
381
334
 
382
335
  Internally, on the MQTT broker, the topic by
383
336
  `topicMake(resource, "resource-transfer-response", peerId)` (default:
384
337
  `${resource}/resource-transfer-response/${peerId}`) is temporarily subscribed
385
338
  for receiving the response.
386
339
 
340
+ - **Resource Push**:<br/>
341
+
342
+ /* (simplified TypeScript API method signature) */
343
+ push(
344
+ resource: string,
345
+ streamOrBuffer: Readable | Buffer,
346
+ receiver?: Receiver,
347
+ options?: MQTT::IClientPublishOptions,
348
+ ...params: any[]
349
+ ): Promise<void>
350
+
351
+ Pushes a resource to all provisioners or a specific provisioner.
352
+ The `streamOrBuffer` is either a Node.js `Readable` stream or a `Buffer` providing the data to push.
353
+ The optional `receiver` directs the push to a specific provisioner only.
354
+ The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
355
+
356
+ The data is read from `streamOrBuffer` in chunks (default: 16KB,
357
+ configurable via `chunkSize` option) and sent over MQTT until the
358
+ stream is closed or the buffer is fully transferred.
359
+ The returned `Promise` resolves when the entire data has been pushed.
360
+
361
+ The remote `provision()` `callback` is called with `params` and an `info` object
362
+ containing `stream` (`Readable`) for consuming the pushed data and
363
+ `buffer` (lazy `Promise<Buffer>`) that resolves to the complete data once the stream ends.
364
+
365
+ Internally, publishes to the MQTT topic by `topicMake(resource, "resource-transfer-response", peerId)`
366
+ (default: `${resource}/resource-transfer-response/any` or `${resource}/resource-transfer-response/${peerId}`).
367
+
387
368
  - **Receiver Wrapping**:<br/>
388
369
 
389
370
  receiver(
390
371
  id: string
391
372
  ): Receiver
392
373
 
393
- Wrap a receiver ID string for use with `emit()` or `call()` to direct the
374
+ Wrap a receiver ID string for use with `emit()`, `call()`, `fetch()` or `push()` to direct the
394
375
  message to a specific receiver. Returns a `Receiver` object.
395
376
 
396
377
  Internals
@@ -582,6 +563,18 @@ example/hello success: world:42
582
563
  CLOSE
583
564
  ```
584
565
 
566
+ Notice
567
+ ------
568
+
569
+ > [!Note]
570
+ > **MQTT+** is somewhat similar to and originally derived from
571
+ > [MQTT-JSON-RPC](https://github.com/rse/mqtt-json-rpc) of the same
572
+ > author, but instead of just JSON, MQTT+ encodes packets as JSON
573
+ > or CBOR (default), uses an own packet format (allowing sender and
574
+ > receiver information), uses shorter NanoIDs instead of longer UUIDs
575
+ > for identification of sender, receiver and requests, and additionally
576
+ > provides resource transfer support (with fetch and push capabilities).
577
+
585
578
  License
586
579
  -------
587
580
 
@@ -7,11 +7,11 @@ server: "Server"
7
7
 
8
8
  broker.class: brown
9
9
 
10
- "subscribe(\"foo/bar\")": {
10
+ subscribe("foo/bar"): {
11
11
  server -> broker: "op: subscribe\ntopic: foo/bar/event-emission/any"
12
12
  }
13
13
 
14
- "emit(\"foo/bar\")": {
14
+ emit("foo/bar"): {
15
15
  client -> broker: "op: publish\ntopic: foo/bar/event-emission/any\ndata: Event-Emission"
16
16
  broker -> server: "op: publish\ntopic: foo/bar/event-emission/any\ndata: Event-Emission"
17
17
  }
@@ -0,0 +1,32 @@
1
+
2
+ shape: sequence_diagram
3
+
4
+ client: "Client"
5
+ broker: "Broker"
6
+ server: "Server"
7
+
8
+ broker.class: brown
9
+
10
+ provision("foo/bar") {
11
+ server -> broker: "op: subscribe\ntopic: foo/bar/resource-transfer-request/any"
12
+ server -> broker: "op: subscribe\ntopic: foo/bar/resource-transfer-response/any"
13
+ }
14
+
15
+ push("foo/bar") {
16
+ client -> broker: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response #1"
17
+ broker -> server: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response #1"
18
+ client -> broker: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response #N"
19
+ broker -> server: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response #N"
20
+ client -> broker: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response EoS"
21
+ broker -> server: "op: publish\ntopic: foo/bar/resource-transfer-response/any\ndata: Resource-Transfer-Response EoS"
22
+ }
23
+
24
+ fetch("foo/bar") {
25
+ client -> broker: "op: subscribe\ntopic: foo/bar/resource-transfer-response/XXX"
26
+ client -> broker: "op: publish\ntopic: foo/bar/resource-transfer-request/any\ndata: Resource-Transfer-Request"
27
+ broker -> server: "op: publish\ntopic: foo/bar/resource-transfer-request/any\ndata: Resource-Transfer-Request"
28
+ broker <- server: "op: publish\ntopic: foo/bar/resource-transfer-response/XXX\ndata: Resource-Transfer-Response #1"
29
+ client <- broker: "op: publish\ntopic: foo/bar/resource-transfer-response/XXX\ndata: Resource-Transfer-Response #1"
30
+ broker <- server: "op: publish\ntopic: foo/bar/resource-transfer-response/XXX\ndata: Resource-Transfer-Response #N"
31
+ client <- broker: "op: publish\ntopic: foo/bar/resource-transfer-response/XXX\ndata: Resource-Transfer-Response #N"
32
+ }
@@ -0,0 +1,109 @@
1
+ <?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-d2-version="v0.7.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 714 1118"><svg class="d2-3961372893 d2-svg" width="714" height="1118" viewBox="11 51 714 1118"><rect x="11.000000" y="51.000000" width="714.000000" height="1118.000000" rx="0.000000" fill="#ffffff" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
2
+ .d2-3961372893 .text {
3
+ font-family: "d2-3961372893-font-regular";
4
+ }
5
+ @font-face {
6
+ font-family: d2-3961372893-font-regular;
7
+ src: url("data:application/font-woff;base64,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");
8
+ }
9
+ .d2-3961372893 .text-italic {
10
+ font-family: "d2-3961372893-font-italic";
11
+ }
12
+ @font-face {
13
+ font-family: d2-3961372893-font-italic;
14
+ src: url("data:application/font-woff;base64,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");
15
+ }]]></style><style type="text/css"><![CDATA[.shape {
16
+ shape-rendering: geometricPrecision;
17
+ stroke-linejoin: round;
18
+ }
19
+ .connection {
20
+ stroke-linecap: round;
21
+ stroke-linejoin: round;
22
+ }
23
+ .blend {
24
+ mix-blend-mode: multiply;
25
+ opacity: 0.5;
26
+ }
27
+
28
+ .d2-3961372893 .fill-N1{fill:#303030;}
29
+ .d2-3961372893 .fill-N2{fill:#606060;}
30
+ .d2-3961372893 .fill-N3{fill:#909090;}
31
+ .d2-3961372893 .fill-N4{fill:#c0c0c0;}
32
+ .d2-3961372893 .fill-N5{fill:#e0e0e0;}
33
+ .d2-3961372893 .fill-N6{fill:#f0f0f0;}
34
+ .d2-3961372893 .fill-N7{fill:#ffffff;}
35
+ .d2-3961372893 .fill-B1{fill:#336699;}
36
+ .d2-3961372893 .fill-B2{fill:#6699cc;}
37
+ .d2-3961372893 .fill-B3{fill:#99ccff;}
38
+ .d2-3961372893 .fill-B4{fill:#c0d0ff;}
39
+ .d2-3961372893 .fill-B5{fill:#e0f0ff;}
40
+ .d2-3961372893 .fill-B6{fill:#f0f8ff;}
41
+ .d2-3961372893 .fill-AA2{fill:#cfb098;}
42
+ .d2-3961372893 .fill-AA4{fill:#efd0b8;}
43
+ .d2-3961372893 .fill-AA5{fill:#ffe0c8;}
44
+ .d2-3961372893 .fill-AB4{fill:#efd0b8;}
45
+ .d2-3961372893 .fill-AB5{fill:#ffe0c8;}
46
+ .d2-3961372893 .stroke-N1{stroke:#303030;}
47
+ .d2-3961372893 .stroke-N2{stroke:#606060;}
48
+ .d2-3961372893 .stroke-N3{stroke:#909090;}
49
+ .d2-3961372893 .stroke-N4{stroke:#c0c0c0;}
50
+ .d2-3961372893 .stroke-N5{stroke:#e0e0e0;}
51
+ .d2-3961372893 .stroke-N6{stroke:#f0f0f0;}
52
+ .d2-3961372893 .stroke-N7{stroke:#ffffff;}
53
+ .d2-3961372893 .stroke-B1{stroke:#336699;}
54
+ .d2-3961372893 .stroke-B2{stroke:#6699cc;}
55
+ .d2-3961372893 .stroke-B3{stroke:#99ccff;}
56
+ .d2-3961372893 .stroke-B4{stroke:#c0d0ff;}
57
+ .d2-3961372893 .stroke-B5{stroke:#e0f0ff;}
58
+ .d2-3961372893 .stroke-B6{stroke:#f0f8ff;}
59
+ .d2-3961372893 .stroke-AA2{stroke:#cfb098;}
60
+ .d2-3961372893 .stroke-AA4{stroke:#efd0b8;}
61
+ .d2-3961372893 .stroke-AA5{stroke:#ffe0c8;}
62
+ .d2-3961372893 .stroke-AB4{stroke:#efd0b8;}
63
+ .d2-3961372893 .stroke-AB5{stroke:#ffe0c8;}
64
+ .d2-3961372893 .background-color-N1{background-color:#303030;}
65
+ .d2-3961372893 .background-color-N2{background-color:#606060;}
66
+ .d2-3961372893 .background-color-N3{background-color:#909090;}
67
+ .d2-3961372893 .background-color-N4{background-color:#c0c0c0;}
68
+ .d2-3961372893 .background-color-N5{background-color:#e0e0e0;}
69
+ .d2-3961372893 .background-color-N6{background-color:#f0f0f0;}
70
+ .d2-3961372893 .background-color-N7{background-color:#ffffff;}
71
+ .d2-3961372893 .background-color-B1{background-color:#336699;}
72
+ .d2-3961372893 .background-color-B2{background-color:#6699cc;}
73
+ .d2-3961372893 .background-color-B3{background-color:#99ccff;}
74
+ .d2-3961372893 .background-color-B4{background-color:#c0d0ff;}
75
+ .d2-3961372893 .background-color-B5{background-color:#e0f0ff;}
76
+ .d2-3961372893 .background-color-B6{background-color:#f0f8ff;}
77
+ .d2-3961372893 .background-color-AA2{background-color:#cfb098;}
78
+ .d2-3961372893 .background-color-AA4{background-color:#efd0b8;}
79
+ .d2-3961372893 .background-color-AA5{background-color:#ffe0c8;}
80
+ .d2-3961372893 .background-color-AB4{background-color:#efd0b8;}
81
+ .d2-3961372893 .background-color-AB5{background-color:#ffe0c8;}
82
+ .d2-3961372893 .color-N1{color:#303030;}
83
+ .d2-3961372893 .color-N2{color:#606060;}
84
+ .d2-3961372893 .color-N3{color:#909090;}
85
+ .d2-3961372893 .color-N4{color:#c0c0c0;}
86
+ .d2-3961372893 .color-N5{color:#e0e0e0;}
87
+ .d2-3961372893 .color-N6{color:#f0f0f0;}
88
+ .d2-3961372893 .color-N7{color:#ffffff;}
89
+ .d2-3961372893 .color-B1{color:#336699;}
90
+ .d2-3961372893 .color-B2{color:#6699cc;}
91
+ .d2-3961372893 .color-B3{color:#99ccff;}
92
+ .d2-3961372893 .color-B4{color:#c0d0ff;}
93
+ .d2-3961372893 .color-B5{color:#e0f0ff;}
94
+ .d2-3961372893 .color-B6{color:#f0f8ff;}
95
+ .d2-3961372893 .color-AA2{color:#cfb098;}
96
+ .d2-3961372893 .color-AA4{color:#efd0b8;}
97
+ .d2-3961372893 .color-AA5{color:#ffe0c8;}
98
+ .d2-3961372893 .color-AB4{color:#efd0b8;}
99
+ .d2-3961372893 .color-AB5{color:#ffe0c8;}.appendix text.text{fill:#303030}.md{--color-fg-default:#303030;--color-fg-muted:#606060;--color-fg-subtle:#909090;--color-canvas-default:#ffffff;--color-canvas-subtle:#f0f0f0;--color-border-default:#336699;--color-border-muted:#6699cc;--color-neutral-muted:#f0f0f0;--color-accent-fg:#6699cc;--color-accent-emphasis:#6699cc;--color-attention-subtle:#606060;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-dark-d2-3961372893);mix-blend-mode:overlay}.sketch-overlay-B2{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-B3{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-B4{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-B5{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-AA4{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-3961372893);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-3961372893);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-normal-d2-3961372893);mix-blend-mode:color-burn}.sketch-overlay-N6{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-3961372893);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g class="Y2xpZW50"><g class="shape" ><rect x="12.000000" y="52.000000" width="100.000000" height="66.000000" stroke="#336699" fill="#e0f0ff" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="62.000000" y="90.500000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">Client</text></g><g class="YnJva2Vy brown"><g class="shape" ><rect x="318.000000" y="52.000000" width="100.000000" height="66.000000" stroke="#cfb098" fill="#ffe0c9" style="stroke-width:2;" /></g><text x="368.000000" y="90.500000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">Broker</text></g><g class="c2VydmVy"><g class="shape" ><rect x="624.000000" y="52.000000" width="100.000000" height="66.000000" stroke="#336699" fill="#e0f0ff" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="674.000000" y="90.500000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">Server</text></g><g class="KGNsaWVudCAtLSApWzBd"><path d="M 62.000000 120.000000 L 62.000000 1167.000000" stroke="#6699cc" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-3961372893)" /></g><g class="KGJyb2tlciAtLSApWzBd"><path d="M 368.000000 120.000000 L 368.000000 1167.000000" stroke="#cfb098" fill="none" class="connection" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-3961372893)" /></g><g class="KHNlcnZlciAtLSApWzBd"><path d="M 674.000000 120.000000 L 674.000000 1167.000000" stroke="#6699cc" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-3961372893)" /></g><g class="JiMzOTtwcm92aXNpb24oJiMzNDtmb28vYmFyJiMzNDspJiMzOTs="><g class="shape blend" ><rect x="328.000000" y="175.000000" width="386.000000" height="92.000000" stroke="#336699" fill="#e0e0e0" class=" stroke-B1 fill-N5" style="stroke-width:0;" /></g><rect x="333.000000" y="180.000000" width="135.000000" height="21.000000" fill="#e0e0e0" class=" fill-N5" /><text x="400.500000" y="196.000000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">provision(&#34;foo/bar&#34;)</text></g><g class="JiMzOTt0cmFuc2ZlcigmIzM0O2Zvby9iYXImIzM0OykmIzM5Ow=="><g class="shape blend" ><rect x="22.000000" y="312.000000" width="692.000000" height="824.000000" stroke="#336699" fill="#e0e0e0" class=" stroke-B1 fill-N5" style="stroke-width:0;" /></g><rect x="27.000000" y="317.000000" width="126.000000" height="21.000000" fill="#e0e0e0" class=" fill-N5" /><text x="90.000000" y="333.000000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">transfer(&#34;foo/bar&#34;)</text></g><g class="KHNlcnZlciAtJmd0OyBicm9rZXIpWzBd"><marker id="mk-d2-3961372893-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" fill="#336699" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 672.000000 237.000000 L 372.000000 237.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-3961372893-3488378134)" mask="url(#d2-3961372893)" /><text x="521.000000" y="235.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="521.000000" dy="0.000000">op: subscribe</tspan><tspan x="521.000000" dy="18.500000">topic: foo/bar/resource-transfer/any</tspan></text></g><g class="KGNsaWVudCAtJmd0OyBicm9rZXIpWzBd"><path d="M 64.000000 374.000000 L 364.000000 374.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-3961372893-3488378134)" mask="url(#d2-3961372893)" /><text x="215.000000" y="372.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="215.000000" dy="0.000000">op: subscribe</tspan><tspan x="215.000000" dy="18.500000">topic: foo/bar/resource-transfer/XXX</tspan></text></g><g class="KGNsaWVudCAtJmd0OyBicm9rZXIpWzFd"><path d="M 64.000000 488.000000 L 364.000000 488.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-3961372893-3488378134)" mask="url(#d2-3961372893)" /><text x="215.000000" y="478.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="215.000000" dy="0.000000">op: publish</tspan><tspan x="215.000000" dy="17.666667">topic: foo/bar/resource-transfer/any</tspan><tspan x="215.000000" dy="17.666667">data: Resource-Transfer-Request</tspan></text></g><g class="KGJyb2tlciAtJmd0OyBzZXJ2ZXIpWzBd"><path d="M 370.000000 610.000000 L 670.000000 610.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-3961372893-3488378134)" mask="url(#d2-3961372893)" /><text x="521.000000" y="600.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="521.000000" dy="0.000000">op: publish</tspan><tspan x="521.000000" dy="17.666667">topic: foo/bar/resource-transfer/any</tspan><tspan x="521.000000" dy="17.666667">data: Resource-Transfer-Request</tspan></text></g><g class="KGJyb2tlciAmbHQ7LSBzZXJ2ZXIpWzBd"><marker id="mk-d2-3961372893-2451250203" markerWidth="10.000000" markerHeight="12.000000" refX="3.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="10.000000,0.000000 0.000000,6.000000 10.000000,12.000000" fill="#336699" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 372.000000 732.000000 L 672.000000 732.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-d2-3961372893-2451250203)" mask="url(#d2-3961372893)" /><text x="521.500000" y="722.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="521.500000" dy="0.000000">op: publish</tspan><tspan x="521.500000" dy="17.666667">topic: foo/bar/resource-response/XXX</tspan><tspan x="521.500000" dy="17.666667">data: Resource-Transfer-Response #1</tspan></text></g><g class="KGNsaWVudCAmbHQ7LSBicm9rZXIpWzBd"><path d="M 66.000000 854.000000 L 366.000000 854.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-d2-3961372893-2451250203)" mask="url(#d2-3961372893)" /><text x="215.500000" y="844.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="215.500000" dy="0.000000">op: publish</tspan><tspan x="215.500000" dy="17.666667">topic: foo/bar/resource-response/XXX</tspan><tspan x="215.500000" dy="17.666667">data: Resource-Transfer-Response #1</tspan></text></g><g class="KGJyb2tlciAmbHQ7LSBzZXJ2ZXIpWzFd"><path d="M 372.000000 976.000000 L 672.000000 976.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-d2-3961372893-2451250203)" mask="url(#d2-3961372893)" /><text x="521.000000" y="966.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="521.000000" dy="0.000000">op: publish</tspan><tspan x="521.000000" dy="17.666667">topic: foo/bar/resource-response/XXX</tspan><tspan x="521.000000" dy="17.666667">data: Resource-Transfer-Response #N</tspan></text></g><g class="KGNsaWVudCAmbHQ7LSBicm9rZXIpWzFd"><path d="M 66.000000 1098.000000 L 366.000000 1098.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-d2-3961372893-2451250203)" mask="url(#d2-3961372893)" /><text x="215.000000" y="1088.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="215.000000" dy="0.000000">op: publish</tspan><tspan x="215.000000" dy="17.666667">topic: foo/bar/resource-response/XXX</tspan><tspan x="215.000000" dy="17.666667">data: Resource-Transfer-Response #N</tspan></text></g><mask id="d2-3961372893" maskUnits="userSpaceOnUse" x="11" y="51" width="714" height="1118">
100
+ <rect x="11" y="51" width="714" height="1118" fill="white"></rect>
101
+ <rect x="400.000000" y="219.000000" width="242" height="37" fill="black"></rect>
102
+ <rect x="94.000000" y="356.000000" width="242" height="37" fill="black"></rect>
103
+ <rect x="94.000000" y="462.000000" width="242" height="53" fill="black"></rect>
104
+ <rect x="400.000000" y="584.000000" width="242" height="53" fill="black"></rect>
105
+ <rect x="397.000000" y="706.000000" width="249" height="53" fill="black"></rect>
106
+ <rect x="91.000000" y="828.000000" width="249" height="53" fill="black"></rect>
107
+ <rect x="396.000000" y="950.000000" width="250" height="53" fill="black"></rect>
108
+ <rect x="90.000000" y="1072.000000" width="250" height="53" fill="black"></rect>
109
+ </mask></svg></svg>
@@ -1,22 +1,17 @@
1
1
  type Brand<T> = T & {
2
2
  readonly __brand: unique symbol;
3
3
  };
4
- export type APIEndpoint = APIEndpointEvent | APIEndpointStream | APIEndpointService | APIEndpointResource;
4
+ export type APIEndpoint = APIEndpointEvent | APIEndpointService | APIEndpointResource;
5
5
  export type APIEndpointEvent = (...args: any[]) => void;
6
- export type APIEndpointStream = (...args: any[]) => void;
7
6
  export type APIEndpointService = (...args: any[]) => any;
8
7
  export type APIEndpointResource = (...args: any[]) => void;
9
8
  export type Event<T extends APIEndpointEvent> = Brand<T>;
10
- export type Stream<T extends APIEndpointStream> = Brand<T>;
11
9
  export type Service<T extends APIEndpointService> = Brand<T>;
12
10
  export type Resource<T extends APIEndpointResource> = Brand<T>;
13
11
  export type APISchema = Record<string, APIEndpoint>;
14
12
  export type EventKeys<T> = string extends keyof T ? string : {
15
13
  [K in keyof T]: T[K] extends Event<infer _F> ? K : never;
16
14
  }[keyof T];
17
- export type StreamKeys<T> = string extends keyof T ? string : {
18
- [K in keyof T]: T[K] extends Stream<infer _F> ? K : never;
19
- }[keyof T];
20
15
  export type ServiceKeys<T> = string extends keyof T ? string : {
21
16
  [K in keyof T]: T[K] extends Service<infer _F> ? K : never;
22
17
  }[keyof T];
@@ -109,6 +109,6 @@ export class BaseTrait extends ReceiverTrait {
109
109
  this._dispatchMessage(topic, parsed);
110
110
  }
111
111
  /* dispatch parsed message to appropriate handler
112
- (base implementation, to be overridden in super-traits) */
112
+ (base implementation, to be overridden in sub-traits) */
113
113
  _dispatchMessage(_topic, _parsed) { }
114
114
  }
@@ -1,16 +1,15 @@
1
- import stream from "stream";
1
+ import { Readable } from "stream";
2
2
  export interface InfoBase {
3
3
  sender: string;
4
4
  receiver?: string;
5
5
  }
6
6
  export interface InfoEvent extends InfoBase {
7
7
  }
8
- export interface InfoStream extends InfoBase {
9
- stream: stream.Readable;
10
- }
11
8
  export interface InfoService extends InfoBase {
12
9
  }
13
10
  export interface InfoResource extends InfoBase {
14
- resource: Buffer | null;
11
+ resource: Buffer | Readable | null;
12
+ stream?: Readable;
13
+ buffer?: Promise<Buffer>;
15
14
  }
16
15
  export type WithInfo<F, I extends InfoBase> = F extends (...args: infer P) => infer R ? (...args: [...P, info: I]) => R : never;
@@ -12,12 +12,6 @@ export declare class EventEmission extends Base {
12
12
  params?: any[] | undefined;
13
13
  constructor(id: string, event: string, params?: any[] | undefined, sender?: string, receiver?: string);
14
14
  }
15
- export declare class StreamTransfer extends Base {
16
- stream: string;
17
- chunk: Buffer | null;
18
- params?: any[] | undefined;
19
- constructor(id: string, stream: string, chunk: Buffer | null, params?: any[] | undefined, sender?: string, receiver?: string);
20
- }
21
15
  export declare class ServiceCallRequest extends Base {
22
16
  service: string;
23
17
  params?: any[] | undefined;
@@ -34,18 +28,20 @@ export declare class ResourceTransferRequest extends Base {
34
28
  constructor(id: string, resource: string, params?: any[] | undefined, sender?: string, receiver?: string);
35
29
  }
36
30
  export declare class ResourceTransferResponse extends Base {
37
- chunk: Buffer | null | undefined;
38
- error: string | undefined;
39
- constructor(id: string, chunk: Buffer | null | undefined, error: string | undefined, sender?: string, receiver?: string);
31
+ resource?: string | undefined;
32
+ params?: any[] | undefined;
33
+ chunk?: Buffer | undefined;
34
+ error?: string | undefined;
35
+ final?: boolean | undefined;
36
+ constructor(id: string, resource?: string | undefined, params?: any[] | undefined, chunk?: Buffer | undefined, error?: string | undefined, final?: boolean | undefined, sender?: string, receiver?: string);
40
37
  }
41
38
  export default class Msg {
42
39
  makeEventEmission(id: string, event: string, params?: any[], sender?: string, receiver?: string): EventEmission;
43
- makeStreamTransfer(id: string, stream: string, chunk: Buffer | null, params?: any[], sender?: string, receiver?: string): StreamTransfer;
44
40
  makeServiceCallRequest(id: string, service: string, params?: any[], sender?: string, receiver?: string): ServiceCallRequest;
45
41
  makeServiceCallResponse(id: string, result?: any, error?: string, sender?: string, receiver?: string): ServiceCallResponse;
46
42
  makeResourceTransferRequest(id: string, resource: string, params?: any[], sender?: string, receiver?: string): ResourceTransferRequest;
47
- makeResourceTransferResponse(id: string, chunk?: Buffer | null, error?: string, sender?: string, receiver?: string): ResourceTransferResponse;
48
- parse(obj: any): EventEmission | StreamTransfer | ServiceCallRequest | ServiceCallResponse | ResourceTransferRequest | ResourceTransferResponse;
43
+ makeResourceTransferResponse(id: string, resource?: string, params?: any[], chunk?: Buffer, error?: string, final?: boolean, sender?: string, receiver?: string): ResourceTransferResponse;
44
+ parse(obj: any): EventEmission | ServiceCallRequest | ServiceCallResponse | ResourceTransferRequest | ResourceTransferResponse;
49
45
  }
50
46
  export declare class MsgTrait<T extends APISchema = APISchema> extends CodecTrait<T> {
51
47
  protected msg: Msg;