mqtt-plus 0.9.3 → 0.9.5

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 (56) hide show
  1. package/README.md +126 -75
  2. package/doc/mqtt-plus-1-event-emission.d2 +18 -0
  3. package/doc/mqtt-plus-1-event-emission.svg +104 -0
  4. package/doc/mqtt-plus-2-stream-transfer.d2 +22 -0
  5. package/doc/mqtt-plus-2-stream-transfer.svg +108 -0
  6. package/doc/mqtt-plus-3-service-call.d2 +21 -0
  7. package/doc/mqtt-plus-3-service-call.svg +107 -0
  8. package/doc/mqtt-plus-4-resource-transfer.d2 +25 -0
  9. package/doc/mqtt-plus-4-resource-transfer.svg +111 -0
  10. package/doc/theme.d2 +40 -0
  11. package/dst-stage1/mqtt-plus-api.d.ts +26 -0
  12. package/dst-stage1/mqtt-plus-api.js +24 -0
  13. package/dst-stage1/mqtt-plus-base.d.ts +19 -0
  14. package/dst-stage1/mqtt-plus-base.js +114 -0
  15. package/dst-stage1/mqtt-plus-codec.d.ts +6 -0
  16. package/dst-stage1/mqtt-plus-codec.js +11 -0
  17. package/dst-stage1/mqtt-plus-event.d.ts +18 -0
  18. package/dst-stage1/mqtt-plus-event.js +103 -0
  19. package/dst-stage1/mqtt-plus-info.d.ts +16 -0
  20. package/dst-stage1/mqtt-plus-info.js +24 -0
  21. package/dst-stage1/mqtt-plus-msg.d.ts +28 -14
  22. package/dst-stage1/mqtt-plus-msg.js +84 -46
  23. package/dst-stage1/mqtt-plus-options.d.ts +20 -0
  24. package/dst-stage1/mqtt-plus-options.js +46 -0
  25. package/dst-stage1/mqtt-plus-receiver.d.ts +12 -0
  26. package/dst-stage1/mqtt-plus-receiver.js +42 -0
  27. package/dst-stage1/mqtt-plus-resource.d.ts +19 -0
  28. package/dst-stage1/mqtt-plus-resource.js +196 -0
  29. package/dst-stage1/mqtt-plus-service.d.ts +22 -0
  30. package/dst-stage1/mqtt-plus-service.js +220 -0
  31. package/dst-stage1/mqtt-plus-stream.d.ts +20 -0
  32. package/dst-stage1/mqtt-plus-stream.js +152 -0
  33. package/dst-stage1/mqtt-plus.d.ts +5 -104
  34. package/dst-stage1/mqtt-plus.js +4 -508
  35. package/dst-stage2/mqtt-plus.cjs.js +462 -263
  36. package/dst-stage2/mqtt-plus.esm.js +460 -262
  37. package/dst-stage2/mqtt-plus.umd.js +14 -14
  38. package/etc/d2.mts +21 -0
  39. package/etc/eslint.mts +1 -1
  40. package/etc/stx.conf +13 -1
  41. package/etc/tsc.tsbuildinfo +1 -1
  42. package/package.json +19 -5
  43. package/src/mqtt-plus-api.ts +66 -0
  44. package/src/mqtt-plus-base.ts +156 -0
  45. package/src/mqtt-plus-codec.ts +20 -0
  46. package/src/mqtt-plus-event.ts +164 -0
  47. package/src/mqtt-plus-info.ts +43 -0
  48. package/src/mqtt-plus-msg.ts +116 -58
  49. package/src/mqtt-plus-options.ts +70 -0
  50. package/src/mqtt-plus-receiver.ts +52 -0
  51. package/src/mqtt-plus-resource.ts +276 -0
  52. package/src/mqtt-plus-service.ts +292 -0
  53. package/src/mqtt-plus-stream.ts +222 -0
  54. package/src/mqtt-plus.ts +6 -765
  55. package/tst/mqtt-plus.spec.ts +271 -0
  56. package/tst/tsc.json +30 -0
package/README.md CHANGED
@@ -22,10 +22,10 @@ $ npm install mqtt mqtt-plus
22
22
  About
23
23
  -----
24
24
 
25
- This is **MQTT+**, an companion addon API for the excellent
25
+ This is **MQTT+**, a companion addon API for the excellent
26
26
  [MQTT](http://mqtt.org/) client TypeScript/JavaScript API
27
- [MQTT.js](https://www.npmjs.com/package/mqtt), provoding additional
28
- communication patterns with optional type safety:
27
+ [MQTT.js](https://www.npmjs.com/package/mqtt), providing additional
28
+ communication patterns with type safety:
29
29
 
30
30
  - **Event Emission**:
31
31
 
@@ -38,7 +38,9 @@ communication patterns with optional type safety:
38
38
 
39
39
  In contrast to the regular MQTT message publish/subscribe, this
40
40
  pattern allows to direct the event to particular subscribers and
41
- provides optional information about the sender to subscribers.
41
+ provides optional information about the sender and receiver to subscribers.
42
+
43
+ ![Event Emission](doc/mqtt-plus-1-event-emission.svg)
42
44
 
43
45
  - **Stream Transfer**:
44
46
 
@@ -54,6 +56,8 @@ communication patterns with optional type safety:
54
56
  pattern allows to transfer arbitrary amounts of arbitrary data by
55
57
  chunking the data via a stream.
56
58
 
59
+ ![Stream Transfer](doc/mqtt-plus-2-stream-transfer.svg)
60
+
57
61
  - **Service Call**:
58
62
 
59
63
  Service Call is a *bi-directional* communication pattern.
@@ -69,14 +73,27 @@ communication patterns with optional type safety:
69
73
  Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call)
70
74
  (RPC) style communication.
71
75
 
76
+ ![Service Call](doc/mqtt-plus-3-service-call.svg)
77
+
78
+ - **Resource Transfer**:
79
+
80
+ Resource Transfer is a *bi-directional* communication pattern.
81
+ A Resource is the combination of a resource name and optionally zero or more arguments.
82
+ You *provision* for a resource transfer.
83
+ When a resource is *fetched* or *pushed*, a single particular provisioner (in case
84
+ of a directed resource transfer) or one arbitrary provisioner is called and
85
+ sends or receives the resource and its arguments.
86
+
87
+ ![Resource Transfer](doc/mqtt-plus-4-resource-transfer.svg)
88
+
72
89
  > [!Note]
73
90
  > **MQTT+** is similar to and derived from
74
91
  > [MQTT-JSON-RPC](https://github.com/rse/mqtt-json-rpc) of the same
75
92
  > author, but instead of just JSON, MQTT+ encodes packets as JSON
76
93
  > or CBOR (default), uses an own packet format (allowing sender and
77
94
  > receiver information), uses shorter NanoIDs instead of longer UUIDs
78
- > for identification of sender, receiver and requests, and has
79
- > no support for stream transfers.
95
+ > for identification of sender, receiver and requests, and additionally
96
+ > provides stream transfer and resource transfer support.
80
97
 
81
98
  Usage
82
99
  -----
@@ -91,9 +108,10 @@ pattern of each endpoint:
91
108
  import type * as MQTTpt from "mqtt-plus"
92
109
 
93
110
  export type API = {
94
- "example/sample": MQTTpt.Event<(a1: string, a2: boolean) => void> /* event */
95
- "example/upload": MQTTpt.Stream<(a1: string, a2: number) => void> /* stream */
96
- "example/hello": MQTTpt.Service<(a1: string, a2: number) => string> /* service */
111
+ "example/sample": MQTTpt.Event<(a1: string, a2: number) => void>
112
+ "example/upload": MQTTpt.Stream<(name: string) => void>
113
+ "example/hello": MQTTpt.Service<(a1: string, a2: number) => string>
114
+ "example/resource": MQTTpt.Resource<(filename: string) => void>
97
115
  }
98
116
  ```
99
117
 
@@ -113,11 +131,11 @@ const mqtt = MQTT.connect("wss://127.0.0.1:8883", { ... })
113
131
  const mqttp = new MQTTp<API>(mqtt)
114
132
 
115
133
  mqtt.on("connect", async () => {
116
- mqttp.subscribe("example/sample", (a1, a2) => {
117
- console.log("example/sample: ", a1, a2)
134
+ await mqttp.subscribe("example/sample", (a1, a2, info) => {
135
+ console.log("example/sample:", a1, a2, "from:", info.sender)
118
136
  })
119
- mqttp.register("example/hello", (a1, a2) => {
120
- console.log("example/hello: ", a1, a2)
137
+ await mqttp.register("example/hello", (a1, a2, info) => {
138
+ console.log("example/hello:", a1, a2, "from:", info.sender)
121
139
  return `${a1}:${a2}`
122
140
  })
123
141
  })
@@ -134,9 +152,9 @@ const mqtt = MQTT.connect("wss://127.0.0.1:8883", { ... })
134
152
  const mqttp = new MQTTp<API>(mqtt)
135
153
 
136
154
  mqtt.on("connect", () => {
137
- mqttp.emit("example/sample", "foo", true)
155
+ mqttp.emit("example/sample", "world", 42)
138
156
  mqttp.call("example/hello", "world", 42).then((response) => {
139
- console.log("example/hello response: ", response)
157
+ console.log("example/hello response:", response)
140
158
  mqtt.end()
141
159
  })
142
160
  })
@@ -152,18 +170,12 @@ The **MQTT+** API provides the following methods:
152
170
  constructor<API>(
153
171
  mqtt: MqttClient,
154
172
  options?: {
155
- id: string
156
- codec: "cbor" | "json"
157
- timeout: number
158
- chunkSize: number
159
- topicEventNoticeMake: (topic: string) => TopicMatching | null
160
- topicStreamChunkMake: (topic: string) => TopicMatching | null
161
- topicServiceRequestMake: (topic: string) => TopicMatching | null
162
- topicServiceResponseMake: (topic: string) => TopicMatching | null
163
- topicEventNoticeMatch: { name: string, peerId?: string }
164
- topicStreamChunkMatch: { name: string, peerId?: string }
165
- topicServiceRequestMatch: { name: string, peerId?: string }
166
- topicServiceResponseMatch: { name: string, peerId?: string }
173
+ id: string
174
+ codec: "cbor" | "json"
175
+ timeout: number
176
+ chunkSize: number
177
+ topicMake: (name: string, operation: string, peerId?: string) => string
178
+ topicMatch: (topic: string) => { name: string, operation: string, peerId?: string } | null
167
179
  }
168
180
  )
169
181
 
@@ -180,22 +192,12 @@ The **MQTT+** API provides the following methods:
180
192
  - `codec`: Encoding format (default: `cbor`).
181
193
  - `timeout`: Communication timeout in milliseconds (default: `10000`).
182
194
  - `chunkSize`: Chunk size in bytes for stream transfers (default: `16384`).
183
- - `topicEventNoticeMake`: Custom topic generation for event notices.
184
- (default: `` (name, peerId) => peerId ? `${name}/event-notice/${peerId}` : `${name}/event-notice` ``)
185
- - `topicStreamChunkMake`: Custom topic generation for stream chunks.
186
- (default: `` (name, peerId) => peerId ? `${name}/stream-chunk/${peerId}` : `${name}/stream-chunk` ``)
187
- - `topicServiceRequestMake`: Custom topic generation for service requests.
188
- (default: `` (name, peerId) => peerId ? `${name}/service-request/${peerId}` : `${name}/service-request` ``)
189
- - `topicServiceResponseMake`): Custom topic generation for service responses.
190
- (default: `` (name, peerId) => peerId ? `${name}/service-response/${peerId}` : `${name}/service-response` ``)
191
- - `topicEventNoticeMatch`: Custom topic matching for event notices.
192
- (default: `` (topic) => { const m = topic.match(/^(.+?)\/event-notice(?:\/(.+))?$/); return m ? { name: m[1], peerId: m[2] } : null } ``)
193
- - `topicStreamChunkMatch`: Custom topic matching for stream chunks.
194
- (default: `` (topic) => { const m = topic.match(/^(.+?)\/stream-chunk(?:\/(.+))?$/); return m ? { name: m[1], peerId: m[2] } : null } ``)
195
- - `topicServiceRequestMatch`: Custom topic matching for service requests.
196
- (default: `` (topic) => { const m = topic.match(/^(.+?)\/service-request(?:\/(.+))?$/); return m ? { name: m[1], peerId: m[2] } : null } ``)
197
- - `topicServiceResponseMatch`: Custom topic matching for service responses.
198
- (default: `` (topic) => { const m = topic.match(/^(.+?)\/service-response\/(.+)$/); return m ? { name: m[1], peerId: m[2] } : null } ``)
195
+ - `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`.
197
+ (default: `` (name, operation, peerId) => `${name}/${operation}` + (peerId ? `/${peerId}` : "/any") ``)
198
+ - `topicMatch`: Custom topic matching function.
199
+ Returns `{ name, operation, peerId? }` or `null` if no match. The `peerId` is `undefined` for broadcast topics (ending with `/any`).
200
+ (default: `` (topic) => { const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/); return m ? { name: m[1], operation: m[2], peerId: m[3] === "any" ? undefined : m[3] } : null } ``)
199
201
 
200
202
  - **Event Subscription**:<br/>
201
203
 
@@ -216,8 +218,8 @@ The **MQTT+** API provides the following methods:
216
218
  There is no return value of `callback`.
217
219
 
218
220
  Internally, on the MQTT broker, the topics generated by
219
- `topicEventNoticeMake()` (default: `${event}/event-notice` and
220
- `${event}/event-notice/${peerId}`) are subscribed. Returns a
221
+ `topicMake(event, "event-emission")` (default: `${event}/event-emission/any` and
222
+ `${event}/event-emission/${peerId}`) are subscribed. Returns a
221
223
  `Subscription` object with an `unsubscribe()` method.
222
224
 
223
225
  - **Stream Attachment**:<br/>
@@ -240,8 +242,8 @@ The **MQTT+** API provides the following methods:
240
242
  There is no return value of `callback`.
241
243
 
242
244
  Internally, on the MQTT broker, the topics generated by
243
- `topicStreamChunkMake()` (default: `${stream}/stream-chunk` and
244
- `${stream}/stream-chunk/${peerId}`) are subscribed. Returns an
245
+ `topicMake(stream, "stream-transfer")` (default: `${stream}/stream-transfer/any` and
246
+ `${stream}/stream-transfer/${peerId}`) are subscribed. Returns an
245
247
  `Attachment` object with an `unattach()` method.
246
248
 
247
249
  - **Service Registration**:<br/>
@@ -263,10 +265,33 @@ The **MQTT+** API provides the following methods:
263
265
  The return value of `callback` will resolve the `Promise` returned by the remote `call()`.
264
266
 
265
267
  Internally, on the MQTT broker, the topics by
266
- `topicServiceRequestMake()` (default: `${service}/service-request` and
267
- `${service}/service-request/${peerId}`) are subscribed. Returns a
268
+ `topicMake(service, "service-call-request")` (default: `${service}/service-call-request/any` and
269
+ `${service}/service-call-request/${peerId}`) are subscribed. Returns a
268
270
  `Registration` object with an `unregister()` method.
269
271
 
272
+ - **Resource Provisioning**:<br/>
273
+
274
+ /* (simplified TypeScript API method signature) */
275
+ provision(
276
+ resource: string,
277
+ options?: MQTT::IClientSubscribeOptions
278
+ callback: (
279
+ ...params: any[],
280
+ info: { sender: string, receiver?: string, resource: Buffer | null }
281
+ ) => void
282
+ ): Promise<Provisioning>
283
+
284
+ Provision a resource.
285
+ The `resource` has to be a valid MQTT topic name.
286
+ 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.
289
+
290
+ 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.
294
+
270
295
  - **Event Emission**:<br/>
271
296
 
272
297
  /* (simplified TypeScript API method signature) */
@@ -284,8 +309,8 @@ The **MQTT+** API provides the following methods:
284
309
  The remote `subscribe()` `callback` is called with `params` and its
285
310
  return value is silently ignored.
286
311
 
287
- Internally, publishes to the MQTT topic by `topicEventNoticeMake(event, peerId)`
288
- (default: `${event}/event-notice` or `${event}/event-notice/${peerId}`).
312
+ Internally, publishes to the MQTT topic by `topicMake(event, "event-emission", peerId)`
313
+ (default: `${event}/event-emission/any` or `${event}/event-emission/${peerId}`).
289
314
 
290
315
  - **Stream Transfer**:<br/>
291
316
 
@@ -311,8 +336,8 @@ The **MQTT+** API provides the following methods:
311
336
  The remote `attach()` `callback` is called with `params` and an `info` object
312
337
  containing a `stream.Readable` for consuming the transferred data.
313
338
 
314
- Internally, publishes to the MQTT topic by `topicStreamChunkMake(stream, peerId)`
315
- (default: `${stream}/stream-chunk` or `${stream}/stream-chunk/${peerId}`).
339
+ Internally, publishes to the MQTT topic by `topicMake(stream, "stream-transfer", peerId)`
340
+ (default: `${stream}/stream-transfer/any` or `${stream}/stream-transfer/${peerId}`).
316
341
 
317
342
  - **Service Call**:<br/>
318
343
 
@@ -332,8 +357,31 @@ The **MQTT+** API provides the following methods:
332
357
  return value resolves the returned `Promise`. If the remote `callback`
333
358
  throws an exception, this rejects the returned `Promise`.
334
359
 
335
- Internally, on the MQTT broker, the topic by `topicServiceResponseMake(service, peerId)`
336
- (default: `${service}/service-response/${peerId}`) is temporarily subscribed
360
+ Internally, on the MQTT broker, the topic by `topicMake(service, "service-call-response", peerId)`
361
+ (default: `${service}/service-call-response/${peerId}`) is temporarily subscribed
362
+ for receiving the response.
363
+
364
+ - **Resource Transfer**:<br/>
365
+
366
+ /* (simplified TypeScript API method signature) */
367
+ fetch(
368
+ blob: string,
369
+ receiver?: Receiver,
370
+ options?: MQTT::IClientSubscribeOptions,
371
+ ...params: any[]
372
+ ): Promise<Buffer>
373
+
374
+ Fetches a resource from any resource provisioner or from a specific provisioner.
375
+ The optional `receiver` directs the call to a specific provisioner only.
376
+ The optional `options` allows setting MQTT.js `publish()` options like `qos` or `retain`.
377
+
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`.
381
+
382
+ Internally, on the MQTT broker, the topic by
383
+ `topicMake(resource, "resource-transfer-response", peerId)` (default:
384
+ `${resource}/resource-transfer-response/${peerId}`) is temporarily subscribed
337
385
  for receiving the response.
338
386
 
339
387
  - **Receiver Wrapping**:<br/>
@@ -368,15 +416,16 @@ mqttp.call("example/hello", "world", 42).then((result) => {
368
416
  ```
369
417
 
370
418
  ...the following message is sent to the permanent MQTT topic
371
- `example/hello/service-request` (the shown NanoIDs are just pseudo
372
- ones):
419
+ `example/hello/service-call-request/any` (the shown NanoIDs are just
420
+ pseudo ones):
373
421
 
374
422
  ```json
375
423
  {
424
+ "type": "service-call-request",
376
425
  "id": "vwLzfQDu2uEeOdOfIlT42",
377
- "sender": "2IBMSk0NPnrz1AeTERoea",
378
- "method": "example/hello",
379
- "params": [ "world", 42 ]
426
+ "service": "example/hello",
427
+ "params": [ "world", 42 ],
428
+ "sender": "2IBMSk0NPnrz1AeTERoea"
380
429
  }
381
430
  ```
382
431
 
@@ -391,13 +440,15 @@ mqttp.register("example/hello", (a1, a2) => {
391
440
  ...and then its result, in the above `mqttp.call()` example `"world:42"`, is then
392
441
  sent back as the following success response
393
442
  message to the temporary (client-specific) MQTT topic
394
- `example/hello/service-response/2IBMSk0NPnrz1AeTERoea`:
443
+ `example/hello/service-call-response/2IBMSk0NPnrz1AeTERoea`:
395
444
 
396
445
  ```json
397
446
  {
398
- "id": "vwLzfQDu2uEeOdOfIlT42",
399
- "sender": "2IBMSk0NPnrz1AeTERoea",
400
- "result": "world:42"
447
+ "type": "service-call-response",
448
+ "id": "vwLzfQDu2uEeOdOfIlT42",
449
+ "result": "world:42",
450
+ "sender": "2IBMSk0NPnrz1AeTERoea",
451
+ "receiver": "2IBMSk0NPnrz1AeTERoea"
401
452
  }
402
453
  ```
403
454
 
@@ -503,18 +554,18 @@ mqtt.on("close", () => { console.log("CLOSE") })
503
554
  mqtt.on("reconnect", () => { console.log("RECONNECT") })
504
555
  mqtt.on("message", (topic, message) => { console.log("RECEIVED", topic, message.toString()) })
505
556
 
506
- mqtt.on("connect", () => {
557
+ mqtt.on("connect", async () => {
507
558
  console.log("CONNECT")
508
- mqttp.register("example/hello", (a1, a2) => {
509
- console.log("example/hello: request: ", a1, a2)
559
+ await mqttp.register("example/hello", (a1, a2, info) => {
560
+ console.log("example/hello: request:", a1, a2, "from:", info.sender)
510
561
  return `${a1}:${a2}`
511
562
  })
512
- mqttp.call("example/hello", "world", 42).then((result) => {
513
- console.log("example/hello success: ", result)
563
+ mqttp.call("example/hello", "world", 42).then(async (result) => {
564
+ console.log("example/hello success:", result)
514
565
  mqtt.end()
515
566
  await mosquitto.stop()
516
567
  }).catch((err) => {
517
- console.log("example/hello error: ", err)
568
+ console.log("example/hello error:", err)
518
569
  })
519
570
  })
520
571
  ```
@@ -524,17 +575,17 @@ The output will be:
524
575
  ```
525
576
  $ node sample.ts
526
577
  CONNECT
527
- RECEIVED example/hello/service-request {"id":"vwLzfQDu2uEeOdOfIlT42","sender":"2IBMSk0NPnrz1AeTERoea","service":"example/hello","params":["world",42]}
528
- example/hello: request: world 42 undefined
529
- RECEIVED example/hello/service-response/2IBMSk0NPnrz1AeTERoea {"id":"vwLzfQDu2uEeOdOfIlT42","sender":"2IBMSk0NPnrz1AeTERoea","receiver":"2IBMSk0NPnrz1AeTERoea","result":"world:42"}
530
- example/hello success: world:42
578
+ RECEIVED example/hello/service-call-request/any {"id":"vwLzfQDu2uEeOdOfIlT42","sender":"2IBMSk0NPnrz1AeTERoea","service":"example/hello","params":["world",42]}
579
+ example/hello: request: world 42 from: 2IBMSk0NPnrz1AeTERoea
580
+ RECEIVED example/hello/service-call-response/2IBMSk0NPnrz1AeTERoea {"id":"vwLzfQDu2uEeOdOfIlT42","sender":"2IBMSk0NPnrz1AeTERoea","receiver":"2IBMSk0NPnrz1AeTERoea","result":"world:42"}
581
+ example/hello success: world:42
531
582
  CLOSE
532
583
  ```
533
584
 
534
585
  License
535
586
  -------
536
587
 
537
- Copyright (c) 2018-2025 Dr. Ralf S. Engelschall (http://engelschall.com/)
588
+ Copyright (c) 2018-2026 Dr. Ralf S. Engelschall (http://engelschall.com/)
538
589
 
539
590
  Permission is hereby granted, free of charge, to any person obtaining
540
591
  a copy of this software and associated documentation files (the
@@ -0,0 +1,18 @@
1
+
2
+ shape: sequence_diagram
3
+
4
+ client: "Client"
5
+ broker: "Broker"
6
+ server: "Server"
7
+
8
+ broker.class: brown
9
+
10
+ "subscribe(\"foo/bar\")": {
11
+ server -> broker: "op: subscribe\ntopic: foo/bar/event-emission/any"
12
+ }
13
+
14
+ "emit(\"foo/bar\")": {
15
+ client -> broker: "op: publish\ntopic: foo/bar/event-emission/any\ndata: Event-Emission"
16
+ broker -> server: "op: publish\ntopic: foo/bar/event-emission/any\ndata: Event-Emission"
17
+ }
18
+
@@ -0,0 +1,104 @@
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 670 524"><svg class="d2-1243767131 d2-svg" width="670" height="524" viewBox="11 51 670 524"><rect x="11.000000" y="51.000000" width="670.000000" height="524.000000" rx="0.000000" fill="#ffffff" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
2
+ .d2-1243767131 .text {
3
+ font-family: "d2-1243767131-font-regular";
4
+ }
5
+ @font-face {
6
+ font-family: d2-1243767131-font-regular;
7
+ src: url("data:application/font-woff;base64,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");
8
+ }
9
+ .d2-1243767131 .text-italic {
10
+ font-family: "d2-1243767131-font-italic";
11
+ }
12
+ @font-face {
13
+ font-family: d2-1243767131-font-italic;
14
+ src: url("data:application/font-woff;base64,d09GRgABAAAAAA4IAAoAAAAAFeQAARhRAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgW1SVeGNtYXAAAAFUAAAAoAAAANQD2QQrZ2x5ZgAAAfQAAAdyAAAKWLLv+VRoZWFkAAAJaAAAADYAAAA2G7Ur2mhoZWEAAAmgAAAAJAAAACQLeAjIaG10eAAACcQAAACQAAAAkDmFBIVsb2NhAAAKVAAAAEoAAABKNxo0hm1heHAAAAqgAAAAIAAAACAAPAD2bmFtZQAACsAAAAMmAAAIMgntVzNwb3N0AAAN6AAAACAAAAAg/8YAMgADAeEBkAAFAAACigJY//EASwKKAlgARAFeADIBIwAAAgsFAwMEAwkCBCAAAHcAAAADAAAAAAAAAABBREJPAAEAIP//Au7/BgAAA9gBESAAAZMAAAAAAeYClAAAACAAA3icfM05LgcBAEfhb8zYxxj7PkRFIXolcQIn0AjRiELiNmK5gAtQOgYnoFGofmKi/r/2Kx4KpQK1yhs6rVJjy7Zde/YdOHLsxJkLV27cJvS+8++HvZ86d+n6z/ORn3znM195z2te8pynPOYh97nrb4MqrNnQWbdp1ZBSZdiIUWPGTZhUm9KY1poxa868BYuWLFvhFwAA//8BAAD//+DjJ+x4nHxWXWzb1tl+zyEt+kf+kSiRlmyZkiiRskxJNmmJtmVJlv/kHymJnTo1kshx+rVBviQrvHbZsqVBsxQIimHLMiAYkKJbihYFuvRiRXqzH6ADimHwOuRiQDZkCNZh6+oMyYo0hlCsRU0Oh3Jl2Re7oQ8snvd9n+c8z3MIDRACwM/ia0BBE7SDE9wAGhugKE3XRZ7SZFlkGF1mWSZ0Ca1feoWeOPxx5LXPFYEufPdn8/9efRtf2zqDLpZffNE48vIzzzz58KERRX9+CABAgQiA/fgqOMBL1hqrqZzbZbMxDGf9FSlNTSUHJXFnIb7082Nn+iZCSJsuvLBvZGXl8NTckdPPrTxbmv0GvjpXUCaVRtqeH5otK+ibBT2mbj2YKqoZ0g/BsFnBMfwqCAANQUlKDmaxpnI8I0lisA27XRynqSmdt9lQcP5kqv/wheLQQmeKTUkjx8ZDwbl0ZMIvhsr2iXP7Ste+VdCjvX458/S50XQ56e9ShZjVg2DqwFehxeKJCTAaIzIBRnwJnWo1Poo+bnukIakNX83/afyzcUBgNyvIwK9CFIAPSrJuzZAclGSZDJhK1Qa02dwujuerzNyfWIsM+5b00YVYuBhNJ4+m06uC5pmOh5O+gVAxMZg+YR8Z6etTJ4dCKhf3zurqojoYiff0Cv1dUoKLdRf0kSODgEE2K+g/aBNcZHI+WCNF0zVK1EWbTVZTul5j6N2xojK3oskZB81mj+caaXHZKe0PKW61OzSRFAbsR5amv31UiwQyhncmnBiLJ/4iBaOzZTVnnQEGwaygT/E6uImyCGKREVmNYTQLqtvVhmU1i8lZBy0dPJAzDsqVu1KSORx6Ima1T4Ymkj39vcEFMe7S7JFABq+/t+rrO3yItB6Lzpa1bCYavi8FAUHYrKBbaBO6d6HbYXT7xO/uf1opHU8qo1yMlXz9h1LDI/4UF/SW7CfKk88vJYKeft49uTYxPu11qK5wFYtsVrBch2WHu/9N3oiT6pBKV7fZ2xfey57sP/be1tBe+rCF5TdoE7wQru9HFMEEbDX1UlqKKIgg/Oeh/4/NH+3X8z32BuO3Tf6JqG+Y7/EtXDcx5ewVkyv2U8en1haV+AG1W2vLHQh7HJpbQOGWztbuAWEJEPQBoB/gO8ATPYs5XK9Ihoib6lvKteQ72vdlvFFnV3OXI9Db6HjK/n9L6K3hhoW5g60tOtOs9h3MGsvEHwIAuovXwWP5fefcGUpkydikLCVcKfV30L2LSjbZmC2O0vRM90x8Cq8/zIiJ/JAQMj5AiquzdT4aN94yTVITvsC3sAR+ALBBYKbqd9LrE7wOdqsXRfqxoswwwpXSKv58+f2z+8prXrxu+BD6g/HxJ8+dBwSKWYEv8Do4CcPJQZ3VKKKTbWq/lredL11AyEHZGNTM2XMODz699SOmiXIinKbpWl/8AG0ST5OeVYj8NlDbLqT1oI/nGFo6KI0MNCSWw5kUTWdLGZouuGeUKcLBNDfTN4U2ZkMDekTR8kOOHlc9DzurGva7aBM662fYSzPp2LsY38Wy1WEvyTW9o3toE9rBV6+/qmktzW2b6s7+FWVuRd1/TJlficYWtJRKHvaTR6aeX4pXn2Pja5PjhYm1yfFpUtv8zNTQp2iz6iWmbuI2LFopwbC7cqH5ezkbFV6KW5ZSpVEWO4U363PhNn53TIhtG0o4eQOh7WCQ/hUO7OjjBbQJHXUc8Yz0FTcttK8Y87i7OryhopBBG2Ul0zTZmEsbtwGZX5oVdAFtgrw3t/fGNkntami/MVD29PNjUjTTOxQfVmaV+Fx3nNUC0kDKnx3sX7QPRiQhEhe9suDN9vblw6GeiMsbE3okZ3BUiU2GycyjZgUt4zO1PEvpxJWa5cS6PPvl2CCNhgstxVC+67z9wjDVHWzztjg6EvZcrN3bipzDDZcvZ40HTmdPT3ODzrST2kNmBT1CG8SbX9XeUT+7HWlv15Q54ysoU0VyCUSesI/rDoFFKeMO6yGSQcuGd07UqjynAdDf0Qa0AhAXchyvpUhBdKlQDNE2mnaE2B+WjC20YdwX58XQbAh5DK+113zfTKCP0AZ4ARiLZzKLvqtKG7Y1+9s8Tmc473EeLEoNjRTtCDu/XzT+4UnP/JFhhpsyqojuG48CJVEsBpFj63GipACAaQJvVmAd/RXLEIQx9HWwQdD6/0V0E/0Y3wCJ3IvAgAhvbt/vH6Jm5AEKQNc1RrTfa/0wna7izJsH0JP4HrQD8FUZ6LzN+q7gv9MZ0E/OxU6daXK1vTP2xuLZ3/+67Lls/O2n8ROrEsF5xzwAD7b3yiknyW8iKqIbFDt1usnZrpIS73gvo8BPEieOSezY64tnP/gV2fsLcxW9jn9HZmKQhmbQrSGj9Bp14stXajqH22iD/E5yUDheegptWAQjKOB5uIVvke8V1jrzakidY3tE3uUT8TzPeQKdnMcPyOLqZThD3uXr3p3mPHI31xm2d3Nexcd5FFL3IroJj/ENaABgCVHMRb59no2im9ePHr3+XwAAAP//AQAA//9yvyB0AAAAAQAAAAEYUT3lfz9fDzz1AAED6AAAAADYXaDMAAAAAN1mLzf+vf7dCB0DyQACAAMAAgAAAAAAAAABAAAD2P7vAAAIQP69/bwIHQPoAML/0QAAAAAAAAAAAAAAJAJ0ACQAyAAAAkcAIwImADkB9wAjAfoADAIZACcCGAAfAbMAJQIXACcB4QAlARoAKwILAB8A7QAfAdwAHwD4ACwDHwAfAg0AHwIDACcCF//2AVYAHwGS//wBRQA8AhAAOAHAADsBwP/CAPIAFwGXAIABKwAjASMAQQEl/9QBVP+4AO0AHwAAAEcA8gAXAPIAgAAAAC4ALgBmAJgAsADwASgBYAGOAcYCAAIoAlICXgJ4ApoC3AMGAzQDbgOMA8gD9gQiBEAEcAR8BIgElgS0BNIE4gTwBQYFHAUsAAAAAQAAACQAjAAMAGYABwABAAAAAAAAAAAAAAAAAAQAA3icnJTbThtXFIY/B9tterqoUERu0L5MpWRMoxAl4cqUoIyKcOpxepCqSoM9PojxzMgzmJIn6HXfom+Rqz5Gn6LqdbV/L4MdRUEgBPx79jr8a61/bWCT/9igVr8L/N2cG66x3fzZ8B2+aB4Z3mC/+ZnhOg8b/xhuMGi8NdzkQaNr+BPe1f80/ClP6r8ZvstW/dDw5zyubxr+csPxr+GveMK7Ba7BM/4wXGOLwvAdNvnV8Ab3sJi1OvfYMdzga7YNN9kGekyoSJmQMcIxZMKIM2YklEQkzJgwJGGAI6RNSqWvGbGQY/TBrzERFTNiRRxT4UiJSIkpGVvEt/LKea2MQ51mdtemYkzMiTxOiclw5IzIyUg4VZyKioIXtGhR0hffgoqSgJIJKQE5M0a06HDIET3GTChxHCqSZxaRM6TinFj5nVn4zvRJyCiN1RkZA/F04pfIO+QIR4dCtquRj9YiPMTxo7w9t1y23xLo160wW8+7ZBMzVz9TdSXVzbkmONatz9vmB+GKF7hb9WedyfU9Guh/pcgnnGn+A00qE5MM57ZoE0lBkbuPY1/nkEgd+YmQHq/o8Iaezm26dGlzTI+Ql/Lt0MXxHR2OOZBHKLy4O5RijvkFx/eEsvGxE+vPYmIJv1OYuktxnKmOKYV67pkHqjVRhTefsN+hfE0dpXz62iNv6TS/THsWMzJVFGI4VS+X2iitfwNTxFS1+Nle3fttmNvuLbf4glw77NW64OQnt2B03VSD9zRzrp+AmAE5J7LokzOlRcWFeL8m5owUx4G690pbUtG+9PF5LqSShKkYhGSKM6PQ39h0Exn3/prunb0lA/l7pqeXVd0mi1Ovrmb0Rt1b3kXW5WRlAi2bar6ipr64Zqb9RDu1yj+Sb6nXLecRoeIudvtDr8AOz9llj7Gy9HUzv7zzr4S32FMHTklkNZSmfQ2PCdgl4Cm77PKcp+/1csnGGR+3xmc1f5sD9umwd201C9sO+7xci/bxzH+J7Y7qcTy6PD279TQf3EC132jfrt7NribnpzG3aFfbcUzM1HNxW6s1ufsE/wMAAP//AQAA//9yoVFAAAAAAwAA//UAAP/OADIAAAAAAAAAAAAAAAAAAAAAAAAAAA==");
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-1243767131 .fill-N1{fill:#303030;}
29
+ .d2-1243767131 .fill-N2{fill:#606060;}
30
+ .d2-1243767131 .fill-N3{fill:#909090;}
31
+ .d2-1243767131 .fill-N4{fill:#c0c0c0;}
32
+ .d2-1243767131 .fill-N5{fill:#e0e0e0;}
33
+ .d2-1243767131 .fill-N6{fill:#f0f0f0;}
34
+ .d2-1243767131 .fill-N7{fill:#ffffff;}
35
+ .d2-1243767131 .fill-B1{fill:#336699;}
36
+ .d2-1243767131 .fill-B2{fill:#6699cc;}
37
+ .d2-1243767131 .fill-B3{fill:#99ccff;}
38
+ .d2-1243767131 .fill-B4{fill:#c0d0ff;}
39
+ .d2-1243767131 .fill-B5{fill:#e0f0ff;}
40
+ .d2-1243767131 .fill-B6{fill:#f0f8ff;}
41
+ .d2-1243767131 .fill-AA2{fill:#cfb098;}
42
+ .d2-1243767131 .fill-AA4{fill:#efd0b8;}
43
+ .d2-1243767131 .fill-AA5{fill:#ffe0c8;}
44
+ .d2-1243767131 .fill-AB4{fill:#efd0b8;}
45
+ .d2-1243767131 .fill-AB5{fill:#ffe0c8;}
46
+ .d2-1243767131 .stroke-N1{stroke:#303030;}
47
+ .d2-1243767131 .stroke-N2{stroke:#606060;}
48
+ .d2-1243767131 .stroke-N3{stroke:#909090;}
49
+ .d2-1243767131 .stroke-N4{stroke:#c0c0c0;}
50
+ .d2-1243767131 .stroke-N5{stroke:#e0e0e0;}
51
+ .d2-1243767131 .stroke-N6{stroke:#f0f0f0;}
52
+ .d2-1243767131 .stroke-N7{stroke:#ffffff;}
53
+ .d2-1243767131 .stroke-B1{stroke:#336699;}
54
+ .d2-1243767131 .stroke-B2{stroke:#6699cc;}
55
+ .d2-1243767131 .stroke-B3{stroke:#99ccff;}
56
+ .d2-1243767131 .stroke-B4{stroke:#c0d0ff;}
57
+ .d2-1243767131 .stroke-B5{stroke:#e0f0ff;}
58
+ .d2-1243767131 .stroke-B6{stroke:#f0f8ff;}
59
+ .d2-1243767131 .stroke-AA2{stroke:#cfb098;}
60
+ .d2-1243767131 .stroke-AA4{stroke:#efd0b8;}
61
+ .d2-1243767131 .stroke-AA5{stroke:#ffe0c8;}
62
+ .d2-1243767131 .stroke-AB4{stroke:#efd0b8;}
63
+ .d2-1243767131 .stroke-AB5{stroke:#ffe0c8;}
64
+ .d2-1243767131 .background-color-N1{background-color:#303030;}
65
+ .d2-1243767131 .background-color-N2{background-color:#606060;}
66
+ .d2-1243767131 .background-color-N3{background-color:#909090;}
67
+ .d2-1243767131 .background-color-N4{background-color:#c0c0c0;}
68
+ .d2-1243767131 .background-color-N5{background-color:#e0e0e0;}
69
+ .d2-1243767131 .background-color-N6{background-color:#f0f0f0;}
70
+ .d2-1243767131 .background-color-N7{background-color:#ffffff;}
71
+ .d2-1243767131 .background-color-B1{background-color:#336699;}
72
+ .d2-1243767131 .background-color-B2{background-color:#6699cc;}
73
+ .d2-1243767131 .background-color-B3{background-color:#99ccff;}
74
+ .d2-1243767131 .background-color-B4{background-color:#c0d0ff;}
75
+ .d2-1243767131 .background-color-B5{background-color:#e0f0ff;}
76
+ .d2-1243767131 .background-color-B6{background-color:#f0f8ff;}
77
+ .d2-1243767131 .background-color-AA2{background-color:#cfb098;}
78
+ .d2-1243767131 .background-color-AA4{background-color:#efd0b8;}
79
+ .d2-1243767131 .background-color-AA5{background-color:#ffe0c8;}
80
+ .d2-1243767131 .background-color-AB4{background-color:#efd0b8;}
81
+ .d2-1243767131 .background-color-AB5{background-color:#ffe0c8;}
82
+ .d2-1243767131 .color-N1{color:#303030;}
83
+ .d2-1243767131 .color-N2{color:#606060;}
84
+ .d2-1243767131 .color-N3{color:#909090;}
85
+ .d2-1243767131 .color-N4{color:#c0c0c0;}
86
+ .d2-1243767131 .color-N5{color:#e0e0e0;}
87
+ .d2-1243767131 .color-N6{color:#f0f0f0;}
88
+ .d2-1243767131 .color-N7{color:#ffffff;}
89
+ .d2-1243767131 .color-B1{color:#336699;}
90
+ .d2-1243767131 .color-B2{color:#6699cc;}
91
+ .d2-1243767131 .color-B3{color:#99ccff;}
92
+ .d2-1243767131 .color-B4{color:#c0d0ff;}
93
+ .d2-1243767131 .color-B5{color:#e0f0ff;}
94
+ .d2-1243767131 .color-B6{color:#f0f8ff;}
95
+ .d2-1243767131 .color-AA2{color:#cfb098;}
96
+ .d2-1243767131 .color-AA4{color:#efd0b8;}
97
+ .d2-1243767131 .color-AA5{color:#ffe0c8;}
98
+ .d2-1243767131 .color-AB4{color:#efd0b8;}
99
+ .d2-1243767131 .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-1243767131);mix-blend-mode:overlay}.sketch-overlay-B2{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-B3{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-B4{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-B5{fill:url(#streaks-bright-d2-1243767131);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-1243767131);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-AA4{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-1243767131);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-1243767131);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-1243767131);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-1243767131);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-normal-d2-1243767131);mix-blend-mode:color-burn}.sketch-overlay-N6{fill:url(#streaks-bright-d2-1243767131);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-1243767131);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="296.000000" y="52.000000" width="100.000000" height="66.000000" stroke="#cfb098" fill="#ffe0c9" style="stroke-width:2;" /></g><text x="346.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="580.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="630.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 573.000000" stroke="#6699cc" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-1243767131)" /></g><g class="KGJyb2tlciAtLSApWzBd"><path d="M 346.000000 120.000000 L 346.000000 573.000000" stroke="#cfb098" fill="none" class="connection" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-1243767131)" /></g><g class="KHNlcnZlciAtLSApWzBd"><path d="M 630.000000 120.000000 L 630.000000 573.000000" stroke="#6699cc" fill="none" class="connection stroke-B2" style="stroke-width:2;stroke-dasharray:12.000000,11.838767;" mask="url(#d2-1243767131)" /></g><g class="JiMzOTtzdWJzY3JpYmUoJiMzNDtmb28vYmFyJiMzNDspJiMzOTs="><g class="shape blend" ><rect x="306.000000" y="175.000000" width="364.000000" height="92.000000" stroke="#336699" fill="#e0e0e0" class=" stroke-B1 fill-N5" style="stroke-width:0;" /></g><rect x="311.000000" y="180.000000" width="138.000000" height="21.000000" fill="#e0e0e0" class=" fill-N5" /><text x="380.000000" y="196.000000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">subscribe(&#34;foo/bar&#34;)</text></g><g class="JiMzOTtlbWl0KCYjMzQ7Zm9vL2JhciYjMzQ7KSYjMzk7"><g class="shape blend" ><rect x="22.000000" y="312.000000" width="648.000000" height="230.000000" stroke="#336699" fill="#e0e0e0" class=" stroke-B1 fill-N5" style="stroke-width:0;" /></g><rect x="27.000000" y="317.000000" width="104.000000" height="21.000000" fill="#e0e0e0" class=" fill-N5" /><text x="79.000000" y="333.000000" fill="#303030" class="text fill-N1" style="text-anchor:middle;font-size:16px">emit(&#34;foo/bar&#34;)</text></g><g class="KHNlcnZlciAtJmd0OyBicm9rZXIpWzBd"><marker id="mk-d2-1243767131-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 628.000000 237.000000 L 350.000000 237.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-1243767131-3488378134)" mask="url(#d2-1243767131)" /><text x="488.000000" y="235.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="488.000000" dy="0.000000">op: subscribe</tspan><tspan x="488.000000" dy="18.500000">topic: foo/bar/event-emission/any</tspan></text></g><g class="KGNsaWVudCAtJmd0OyBicm9rZXIpWzBd"><path d="M 64.000000 382.000000 L 342.000000 382.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-1243767131-3488378134)" mask="url(#d2-1243767131)" /><text x="204.000000" y="372.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="204.000000" dy="0.000000">op: publish</tspan><tspan x="204.000000" dy="17.666667">topic: foo/bar/event-emission/any</tspan><tspan x="204.000000" dy="17.666667">data: Event-Emission</tspan></text></g><g class="KGJyb2tlciAtJmd0OyBzZXJ2ZXIpWzBd"><path d="M 348.000000 504.000000 L 626.000000 504.000000" stroke="#336699" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-d2-1243767131-3488378134)" mask="url(#d2-1243767131)" /><text x="488.000000" y="494.000000" fill="#606060" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px"><tspan x="488.000000" dy="0.000000">op: publish</tspan><tspan x="488.000000" dy="17.666667">topic: foo/bar/event-emission/any</tspan><tspan x="488.000000" dy="17.666667">data: Event-Emission</tspan></text></g><mask id="d2-1243767131" maskUnits="userSpaceOnUse" x="11" y="51" width="670" height="524">
100
+ <rect x="11" y="51" width="670" height="524" fill="white"></rect>
101
+ <rect x="374.000000" y="219.000000" width="228" height="37" fill="black"></rect>
102
+ <rect x="90.000000" y="356.000000" width="228" height="53" fill="black"></rect>
103
+ <rect x="374.000000" y="478.000000" width="228" height="53" fill="black"></rect>
104
+ </mask></svg></svg>
@@ -0,0 +1,22 @@
1
+
2
+ shape: sequence_diagram
3
+
4
+ client: "Client"
5
+ broker: "Broker"
6
+ server: "Server"
7
+
8
+ broker.class: brown
9
+
10
+ attach("foo/bar") {
11
+ server -> broker: "op: subscribe\ntopic: foo/bar/stream-transfer"
12
+ }
13
+
14
+ fetch("foo/bar") {
15
+ client -> broker: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer #1"
16
+ broker -> server: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer #1"
17
+ client -> broker: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer #N"
18
+ broker -> server: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer #N"
19
+ client -> broker: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer EoS"
20
+ broker -> server: "op: publish\ntopic: foo/bar/stream-transfer/any\ndata: Stream-Transfer EoS"
21
+ }
22
+